Merge branch 'devel'
This commit is contained in:
commit
0be654efe1
669 changed files with 28587 additions and 20697 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
|
@ -41,3 +41,4 @@ xcuserdata/
|
|||
/testresults.html
|
||||
/testresults.json
|
||||
testament.db
|
||||
/csources/
|
||||
|
|
|
|||
11
compiler.nimble
Normal file
11
compiler.nimble
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
[Package]
|
||||
name = "compiler"
|
||||
version = "0.10.3"
|
||||
author = "Andreas Rumpf"
|
||||
description = "Compiler package providing the compiler sources as a library."
|
||||
license = "MIT"
|
||||
|
||||
InstallDirs = "doc, compiler"
|
||||
|
||||
[Deps]
|
||||
Requires: "nim >= 0.10.3"
|
||||
418
compiler/ast.nim
418
compiler/ast.nim
|
|
@ -1,7 +1,7 @@
|
|||
#
|
||||
#
|
||||
# The Nim Compiler
|
||||
# (c) Copyright 2013 Andreas Rumpf
|
||||
# (c) Copyright 2015 Andreas Rumpf
|
||||
#
|
||||
# See the file "copying.txt", included in this
|
||||
# distribution, for details about the copyright.
|
||||
|
|
@ -196,6 +196,7 @@ type
|
|||
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
|
||||
|
|
@ -256,7 +257,7 @@ type
|
|||
sfThread, # proc will run as a thread
|
||||
# variable is a thread variable
|
||||
sfCompileTime, # proc can be evaluated at compile time
|
||||
sfMerge, # proc can be merged with itself
|
||||
sfConstructor, # proc is a C++ constructor
|
||||
sfDeadCodeElim, # dead code elimination for the module is turned on
|
||||
sfBorrow, # proc is borrowed
|
||||
sfInfixCall, # symbol needs infix call syntax in target language;
|
||||
|
|
@ -295,6 +296,7 @@ const
|
|||
sfCompileToCpp* = sfInfixCall # compile the module as C++ code
|
||||
sfCompileToObjc* = sfNamedParamCall # compile the module as Objective-C code
|
||||
sfExperimental* = sfOverriden # module uses the .experimental switch
|
||||
sfGoto* = sfOverriden # var is used for 'goto' code generation
|
||||
|
||||
const
|
||||
# getting ready for the future expr/stmt merge
|
||||
|
|
@ -314,7 +316,7 @@ type
|
|||
# XXX put this into an include file to avoid this issue!
|
||||
tyNone, tyBool, tyChar,
|
||||
tyEmpty, tyArrayConstr, tyNil, tyExpr, tyStmt, tyTypeDesc,
|
||||
tyGenericInvokation, # ``T[a, b]`` for types to invoke
|
||||
tyGenericInvocation, # ``T[a, b]`` for types to invoke
|
||||
tyGenericBody, # ``T[a, b, body]`` last parameter is the body
|
||||
tyGenericInst, # ``T[a, b, realInstance]`` instantiated generic type
|
||||
# realInstance will be a concrete type like tyObject
|
||||
|
|
@ -377,7 +379,7 @@ type
|
|||
|
||||
tyFromExpr #\
|
||||
# This is a type representing an expression that depends
|
||||
# on generic parameters (the exprsesion is stored in t.n)
|
||||
# on generic parameters (the expression is stored in t.n)
|
||||
# It will be converted to a real type only during generic
|
||||
# instantiation and prior to this it has the potential to
|
||||
# be any type.
|
||||
|
|
@ -399,6 +401,7 @@ const
|
|||
tyPureObject* = tyTuple
|
||||
GcTypeKinds* = {tyRef, tySequence, tyString}
|
||||
tyError* = tyProxy # as an errornous node should match everything
|
||||
tyUnknown* = tyFromExpr
|
||||
|
||||
tyUnknownTypes* = {tyError, tyFromExpr}
|
||||
|
||||
|
|
@ -427,6 +430,7 @@ type
|
|||
nfExplicitCall # x.y() was used instead of x.y
|
||||
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)
|
||||
|
|
@ -456,8 +460,8 @@ type
|
|||
tfNotNil, # type cannot be 'nil'
|
||||
|
||||
tfNeedsInit, # type constains a "not nil" constraint somewhere or some
|
||||
# other type so that it requires inititalization
|
||||
tfHasShared, # type constains a "shared" constraint modifier somewhere
|
||||
# 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
|
||||
tfHasGCedMem, # type contains GC'ed memory
|
||||
|
|
@ -469,7 +473,7 @@ type
|
|||
# T and I here can bind to both typedesc and static types
|
||||
# before this is determined, we'll consider them to be a
|
||||
# wildcard type.
|
||||
tfGuarded # guarded pointer
|
||||
tfHasAsgn # type has overloaded assignment operator
|
||||
tfBorrowDot # distinct type borrows '.'
|
||||
|
||||
TTypeFlags* = set[TTypeFlag]
|
||||
|
|
@ -520,23 +524,26 @@ const
|
|||
skError* = skUnknown
|
||||
|
||||
# type flags that are essential for type equality:
|
||||
eqTypeFlags* = {tfIterator, tfShared, tfNotNil}
|
||||
eqTypeFlags* = {tfIterator, tfShared, tfNotNil, tfVarIsPtr}
|
||||
|
||||
type
|
||||
TMagic* = enum # symbols that require compiler magic:
|
||||
mNone,
|
||||
mDefined, mDefinedInScope, mCompiles,
|
||||
mLow, mHigh, mSizeOf, mTypeTrait, mIs, mOf,
|
||||
mLow, mHigh, mSizeOf, mTypeTrait, mIs, mOf, mAddr, mTypeOf, mRoof, mPlugin,
|
||||
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,
|
||||
mDivI64, mModI64,
|
||||
mUnaryLt, mInc, mDec, mOrd, mNew, mNewFinalize, mNewSeq, mLengthOpenArray,
|
||||
mLengthStr, mLengthArray, mLengthSeq, mXLenStr, mXLenSeq,
|
||||
mIncl, mExcl, mCard, mChr,
|
||||
mGCref, mGCunref,
|
||||
|
||||
mAddI, mSubI, mMulI, mDivI, mModI, mAddI64, mSubI64, mMulI64,
|
||||
mDivI64, mModI64, mSucc, mPred,
|
||||
mAddF64, mSubF64, mMulF64, mDivF64,
|
||||
|
||||
mShrI, mShlI, mBitandI, mBitorI, mBitxorI, mMinI, mMaxI,
|
||||
mShrI64, mShlI64, mBitandI64, mBitorI64, mBitxorI64, mMinI64, mMaxI64,
|
||||
mShrI64, mShlI64, mBitandI64, mBitorI64, mBitxorI64,
|
||||
mMinF64, mMaxF64, mAddU, mSubU, mMulU,
|
||||
mDivU, mModU, mEqI, mLeI,
|
||||
mLtI,
|
||||
|
|
@ -545,14 +552,14 @@ type
|
|||
mEqEnum, mLeEnum, mLtEnum, mEqCh, mLeCh, mLtCh, mEqB, mLeB, mLtB, mEqRef,
|
||||
mEqUntracedRef, mLePtr, mLtPtr, mEqCString, mXor, mEqProc, mUnaryMinusI,
|
||||
mUnaryMinusI64, mAbsI, mAbsI64, mNot,
|
||||
mUnaryPlusI, mBitnotI, mUnaryPlusI64,
|
||||
mUnaryPlusI, mBitnotI,
|
||||
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,
|
||||
mPlusSet, mMinusSet, mSymDiffSet, mConStrStr, mSlice,
|
||||
mDotDot, # this one is only necessary to give nice compile time warnings
|
||||
mFields, mFieldPairs, mOmpParFor,
|
||||
mAppendStrCh, mAppendStrStr, mAppendSeqElem,
|
||||
mInRange, mInSet, mRepr, mExit, mSetLengthStr, mSetLengthSeq,
|
||||
|
|
@ -584,11 +591,12 @@ type
|
|||
const
|
||||
ctfeWhitelist* = {mNone, mUnaryLt, mSucc,
|
||||
mPred, mInc, mDec, mOrd, mLengthOpenArray,
|
||||
mLengthStr, mLengthArray, mLengthSeq, mIncl, mExcl, mCard, mChr,
|
||||
mLengthStr, mLengthArray, mLengthSeq, mXLenStr, mXLenSeq,
|
||||
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,
|
||||
mShrI64, mShlI64, mBitandI64, mBitorI64, mBitxorI64, mMinI64, mMaxI64,
|
||||
mShrI64, mShlI64, mBitandI64, mBitorI64, mBitxorI64,
|
||||
mMinF64, mMaxF64, mAddU, mSubU, mMulU,
|
||||
mDivU, mModU, mEqI, mLeI,
|
||||
mLtI,
|
||||
|
|
@ -597,14 +605,13 @@ const
|
|||
mEqEnum, mLeEnum, mLtEnum, mEqCh, mLeCh, mLtCh, mEqB, mLeB, mLtB, mEqRef,
|
||||
mEqProc, mEqUntracedRef, mLePtr, mLtPtr, mEqCString, mXor, mUnaryMinusI,
|
||||
mUnaryMinusI64, mAbsI, mAbsI64, mNot,
|
||||
mUnaryPlusI, mBitnotI, mUnaryPlusI64,
|
||||
mUnaryPlusI, mBitnotI,
|
||||
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,
|
||||
mPlusSet, mMinusSet, mSymDiffSet, mConStrStr,
|
||||
mAppendStrCh, mAppendStrStr, mAppendSeqElem,
|
||||
mInRange, mInSet, mRepr,
|
||||
mCopyStr, mCopyStrLast}
|
||||
|
|
@ -653,7 +660,6 @@ type
|
|||
locGlobalVar, # location is a global variable
|
||||
locParam, # location is a parameter
|
||||
locField, # location is a record field
|
||||
locArrayElem, # location is an array element
|
||||
locExpr, # "location" is really an expression
|
||||
locProc, # location is a proc (an address of a procedure)
|
||||
locData, # location is a constant
|
||||
|
|
@ -661,26 +667,29 @@ type
|
|||
locOther # location is something other
|
||||
TLocFlag* = enum
|
||||
lfIndirect, # backend introduced a pointer
|
||||
lfParamCopy, # backend introduced a parameter copy (LLVM)
|
||||
lfFullExternalName, # only used when 'gCmd == cmdPretty': Indicates
|
||||
# that the symbol has been imported via 'importc: "fullname"' and
|
||||
# no format string.
|
||||
lfNoDeepCopy, # no need for a deep copy
|
||||
lfNoDecl, # do not declare it in C
|
||||
lfDynamicLib, # link symbol to dynamic library
|
||||
lfExportLib, # export symbol for dynamic library generation
|
||||
lfHeader, # include header file for symbol
|
||||
lfImportCompilerProc # ``importc`` of a compilerproc
|
||||
lfImportCompilerProc, # ``importc`` of a compilerproc
|
||||
lfSingleUse # no location yet and will only be used once
|
||||
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*{.final.} = object
|
||||
TLoc* = object
|
||||
k*: TLocKind # kind of location
|
||||
s*: TStorageLoc
|
||||
flags*: TLocFlags # location's flags
|
||||
t*: PType # type of location
|
||||
r*: PRope # rope value of location (code generators)
|
||||
heapRoot*: PRope # keeps track of the enclosing heap object that
|
||||
r*: Rope # rope value of location (code generators)
|
||||
heapRoot*: Rope # keeps track of the enclosing heap object that
|
||||
# owns this location (required by GC algorithms
|
||||
# employing heap snapshots or sliding views)
|
||||
|
||||
|
|
@ -692,7 +701,7 @@ type
|
|||
kind*: TLibKind
|
||||
generated*: bool # needed for the backends:
|
||||
isOverriden*: bool
|
||||
name*: PRope
|
||||
name*: Rope
|
||||
path*: PNode # can be a string literal!
|
||||
|
||||
TInstantiation* = object
|
||||
|
|
@ -722,7 +731,8 @@ type
|
|||
typScope*: PScope
|
||||
of routineKinds:
|
||||
procInstCache*: seq[PInstantiation]
|
||||
scope*: PScope # the scope where the proc was defined
|
||||
gcUnsafetyReason*: PSym # for better error messages wrt gcsafe
|
||||
#scope*: PScope # the scope where the proc was defined
|
||||
of skModule:
|
||||
# modules keep track of the generic symbols they use from other modules.
|
||||
# this is because in incremental compilation, when a module is about to
|
||||
|
|
@ -788,8 +798,8 @@ type
|
|||
# for enum types a list of symbols
|
||||
# for tyInt it can be the int literal
|
||||
# for procs and tyGenericBody, it's the
|
||||
# the body of the user-defined type class
|
||||
# formal param list
|
||||
# for concepts, the concept body
|
||||
# else: unused
|
||||
owner*: PSym # the 'owner' of the type
|
||||
sym*: PSym # types have the sym associated with them
|
||||
|
|
@ -798,51 +808,52 @@ type
|
|||
# mean that there is no destructor.
|
||||
# see instantiateDestructor in semdestruct.nim
|
||||
deepCopy*: PSym # overriden 'deepCopy' operation
|
||||
assignment*: PSym # overriden '=' operator
|
||||
size*: BiggestInt # the size of the type in bytes
|
||||
# -1 means that the size is unkwown
|
||||
align*: int16 # the type's alignment requirements
|
||||
lockLevel*: TLockLevel # lock level as required for deadlock checking
|
||||
loc*: TLoc
|
||||
|
||||
TPair*{.final.} = object
|
||||
TPair* = object
|
||||
key*, val*: RootRef
|
||||
|
||||
TPairSeq* = seq[TPair]
|
||||
TTable*{.final.} = object # the same as table[PObject] of PObject
|
||||
TTable* = object # the same as table[PObject] of PObject
|
||||
counter*: int
|
||||
data*: TPairSeq
|
||||
|
||||
TIdPair*{.final.} = object
|
||||
TIdPair* = object
|
||||
key*: PIdObj
|
||||
val*: RootRef
|
||||
|
||||
TIdPairSeq* = seq[TIdPair]
|
||||
TIdTable*{.final.} = object # the same as table[PIdent] of PObject
|
||||
TIdTable* = object # the same as table[PIdent] of PObject
|
||||
counter*: int
|
||||
data*: TIdPairSeq
|
||||
|
||||
TIdNodePair*{.final.} = object
|
||||
TIdNodePair* = object
|
||||
key*: PIdObj
|
||||
val*: PNode
|
||||
|
||||
TIdNodePairSeq* = seq[TIdNodePair]
|
||||
TIdNodeTable*{.final.} = object # the same as table[PIdObj] of PNode
|
||||
TIdNodeTable* = object # the same as table[PIdObj] of PNode
|
||||
counter*: int
|
||||
data*: TIdNodePairSeq
|
||||
|
||||
TNodePair*{.final.} = object
|
||||
TNodePair* = object
|
||||
h*: THash # because it is expensive to compute!
|
||||
key*: PNode
|
||||
val*: int
|
||||
|
||||
TNodePairSeq* = seq[TNodePair]
|
||||
TNodeTable*{.final.} = object # the same as table[PNode] of int;
|
||||
TNodeTable* = object # the same as table[PNode] of int;
|
||||
# nodes are compared by structure!
|
||||
counter*: int
|
||||
data*: TNodePairSeq
|
||||
|
||||
TObjectSeq* = seq[RootRef]
|
||||
TObjectSet*{.final.} = object
|
||||
TObjectSet* = object
|
||||
counter*: int
|
||||
data*: TObjectSeq
|
||||
|
||||
|
|
@ -857,7 +868,7 @@ const
|
|||
OverloadableSyms* = {skProc, skMethod, skIterator, skClosureIterator,
|
||||
skConverter, skModule, skTemplate, skMacro}
|
||||
|
||||
GenericTypes*: TTypeKinds = {tyGenericInvokation, tyGenericBody,
|
||||
GenericTypes*: TTypeKinds = {tyGenericInvocation, tyGenericBody,
|
||||
tyGenericParam}
|
||||
|
||||
StructuralEquivTypes*: TTypeKinds = {tyArrayConstr, tyNil, tyTuple, tyArray,
|
||||
|
|
@ -882,7 +893,7 @@ const
|
|||
skMacro, skTemplate, skConverter, skEnumField, skLet, skStub, skAlias}
|
||||
PersistentNodeFlags*: TNodeFlags = {nfBase2, nfBase8, nfBase16,
|
||||
nfDotSetter, nfDotField,
|
||||
nfIsRef}
|
||||
nfIsRef, nfIsCursor}
|
||||
namePos* = 0
|
||||
patternPos* = 1 # empty except for term rewriting macros
|
||||
genericParamsPos* = 2
|
||||
|
|
@ -912,53 +923,7 @@ const
|
|||
|
||||
skIterators* = {skIterator, skClosureIterator}
|
||||
|
||||
lfFullExternalName* = lfParamCopy # \
|
||||
# only used when 'gCmd == cmdPretty': Indicates that the symbol has been
|
||||
# imported via 'importc: "fullname"' and no format string.
|
||||
|
||||
# creator procs:
|
||||
proc newSym*(symKind: TSymKind, name: PIdent, owner: PSym,
|
||||
info: TLineInfo): PSym
|
||||
proc newType*(kind: TTypeKind, owner: PSym): PType
|
||||
proc newNode*(kind: TNodeKind): PNode
|
||||
proc newIntNode*(kind: TNodeKind, intVal: BiggestInt): PNode
|
||||
proc newIntTypeNode*(kind: TNodeKind, intVal: BiggestInt, typ: PType): PNode
|
||||
proc newFloatNode*(kind: TNodeKind, floatVal: BiggestFloat): PNode
|
||||
proc newStrNode*(kind: TNodeKind, strVal: string): PNode
|
||||
proc newIdentNode*(ident: PIdent, info: TLineInfo): PNode
|
||||
proc newSymNode*(sym: PSym): PNode
|
||||
proc newNodeI*(kind: TNodeKind, info: TLineInfo): PNode
|
||||
proc newNodeIT*(kind: TNodeKind, info: TLineInfo, typ: PType): PNode
|
||||
proc initStrTable*(x: var TStrTable)
|
||||
proc initTable*(x: var TTable)
|
||||
proc initIdTable*(x: var TIdTable)
|
||||
proc initObjectSet*(x: var TObjectSet)
|
||||
proc initIdNodeTable*(x: var TIdNodeTable)
|
||||
proc initNodeTable*(x: var TNodeTable)
|
||||
|
||||
# copy procs:
|
||||
proc copyType*(t: PType, owner: PSym, keepId: bool): PType
|
||||
proc copySym*(s: PSym, keepId: bool = false): PSym
|
||||
proc assignType*(dest, src: PType)
|
||||
proc copyStrTable*(dest: var TStrTable, src: TStrTable)
|
||||
proc copyTable*(dest: var TTable, src: TTable)
|
||||
proc copyObjectSet*(dest: var TObjectSet, src: TObjectSet)
|
||||
proc copyIdTable*(dest: var TIdTable, src: TIdTable)
|
||||
proc sonsLen*(n: PNode): int {.inline.}
|
||||
proc sonsLen*(n: PType): int {.inline.}
|
||||
proc lastSon*(n: PNode): PNode {.inline.}
|
||||
proc lastSon*(n: PType): PType {.inline.}
|
||||
proc newSons*(father: PNode, length: int)
|
||||
proc newSons*(father: PType, length: int)
|
||||
proc addSon*(father, son: PNode)
|
||||
proc delSon*(father: PNode, idx: int)
|
||||
proc hasSonWith*(n: PNode, kind: TNodeKind): bool
|
||||
proc hasSubnodeWith*(n: PNode, kind: TNodeKind): bool
|
||||
proc replaceSons*(n: PNode, oldKind, newKind: TNodeKind)
|
||||
proc copyNode*(src: PNode): PNode
|
||||
# does not copy its sons!
|
||||
proc copyTree*(src: PNode): PNode
|
||||
# does copy its sons!
|
||||
var ggDebug* {.deprecated.}: bool ## convenience switch for trying out things
|
||||
|
||||
proc isCallExpr*(n: PNode): bool =
|
||||
result = n.kind in nkCallKinds
|
||||
|
|
@ -987,6 +952,57 @@ template `{}`*(n: PNode, i: int): expr = n[i -| n]
|
|||
template `{}=`*(n: PNode, i: int, s: PNode): stmt =
|
||||
n.sons[i -| n] = s
|
||||
|
||||
when defined(useNodeIds):
|
||||
const nodeIdToDebug* = -1 # 299750 # 300761 #300863 # 300879
|
||||
var gNodeId: int
|
||||
|
||||
proc newNode*(kind: TNodeKind): PNode =
|
||||
new(result)
|
||||
result.kind = kind
|
||||
#result.info = UnknownLineInfo() inlined:
|
||||
result.info.fileIndex = int32(- 1)
|
||||
result.info.col = int16(- 1)
|
||||
result.info.line = int16(- 1)
|
||||
when defined(useNodeIds):
|
||||
result.id = gNodeId
|
||||
if result.id == nodeIdToDebug:
|
||||
echo "KIND ", result.kind
|
||||
writeStackTrace()
|
||||
inc gNodeId
|
||||
|
||||
proc newIntNode*(kind: TNodeKind, intVal: BiggestInt): PNode =
|
||||
result = newNode(kind)
|
||||
result.intVal = intVal
|
||||
|
||||
proc newIntTypeNode*(kind: TNodeKind, intVal: BiggestInt, typ: PType): PNode =
|
||||
result = newIntNode(kind, intVal)
|
||||
result.typ = typ
|
||||
|
||||
proc newFloatNode*(kind: TNodeKind, floatVal: BiggestFloat): PNode =
|
||||
result = newNode(kind)
|
||||
result.floatVal = floatVal
|
||||
|
||||
proc newStrNode*(kind: TNodeKind, strVal: string): PNode =
|
||||
result = newNode(kind)
|
||||
result.strVal = strVal
|
||||
|
||||
proc newSym*(symKind: TSymKind, name: PIdent, owner: PSym,
|
||||
info: TLineInfo): PSym =
|
||||
# generates a symbol and initializes the hash field too
|
||||
new(result)
|
||||
result.name = name
|
||||
result.kind = symKind
|
||||
result.flags = {}
|
||||
result.info = info
|
||||
result.options = gOptions
|
||||
result.owner = owner
|
||||
result.offset = - 1
|
||||
result.id = getID()
|
||||
when debugIds:
|
||||
registerId(result)
|
||||
#if result.id < 2000:
|
||||
# MessageOut(name.s & " has id: " & toString(result.id))
|
||||
|
||||
var emptyNode* = newNode(nkEmpty)
|
||||
# There is a single empty node that is shared! Do not overwrite it!
|
||||
|
||||
|
|
@ -1029,80 +1045,43 @@ 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
|
||||
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
|
||||
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
|
||||
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
|
||||
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
|
||||
|
||||
when defined(useNodeIds):
|
||||
const nodeIdToDebug* = -1 # 884953 # 612794
|
||||
#612840 # 612905 # 614635 # 614637 # 614641
|
||||
# 423408
|
||||
#429107 # 430443 # 441048 # 441090 # 441153
|
||||
var gNodeId: int
|
||||
|
||||
proc newNode(kind: TNodeKind): PNode =
|
||||
new(result)
|
||||
result.kind = kind
|
||||
#result.info = UnknownLineInfo() inlined:
|
||||
result.info.fileIndex = int32(- 1)
|
||||
result.info.col = int16(- 1)
|
||||
result.info.line = int16(- 1)
|
||||
when defined(useNodeIds):
|
||||
result.id = gNodeId
|
||||
if result.id == nodeIdToDebug:
|
||||
echo "KIND ", result.kind
|
||||
writeStackTrace()
|
||||
inc gNodeId
|
||||
|
||||
proc newIntNode(kind: TNodeKind, intVal: BiggestInt): PNode =
|
||||
result = newNode(kind)
|
||||
result.intVal = intVal
|
||||
|
||||
proc newIntTypeNode(kind: TNodeKind, intVal: BiggestInt, typ: PType): PNode =
|
||||
result = newIntNode(kind, intVal)
|
||||
result.typ = typ
|
||||
|
||||
proc newFloatNode(kind: TNodeKind, floatVal: BiggestFloat): PNode =
|
||||
result = newNode(kind)
|
||||
result.floatVal = floatVal
|
||||
|
||||
proc newStrNode(kind: TNodeKind, strVal: string): PNode =
|
||||
result = newNode(kind)
|
||||
result.strVal = strVal
|
||||
|
||||
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
|
||||
|
|
@ -1114,7 +1093,7 @@ proc newSymNode*(sym: PSym, info: TLineInfo): PNode =
|
|||
result.typ = sym.typ
|
||||
result.info = info
|
||||
|
||||
proc newNodeI(kind: TNodeKind, info: TLineInfo): PNode =
|
||||
proc newNodeI*(kind: TNodeKind, info: TLineInfo): PNode =
|
||||
new(result)
|
||||
result.kind = kind
|
||||
result.info = info
|
||||
|
|
@ -1153,11 +1132,16 @@ 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) =
|
||||
assert son != nil
|
||||
if isNil(father.sons): father.sons = @[]
|
||||
add(father.sons, son)
|
||||
|
||||
var emptyParams = newNode(nkFormalParams)
|
||||
emptyParams.addSon(emptyNode)
|
||||
|
||||
|
|
@ -1179,7 +1163,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
|
||||
|
|
@ -1189,7 +1173,9 @@ proc newType(kind: TTypeKind, owner: PSym): PType =
|
|||
result.lockLevel = UnspecifiedLockLevel
|
||||
when debugIds:
|
||||
registerId(result)
|
||||
#if result.id < 2000:
|
||||
#if result.id == 92231:
|
||||
# echo "KNID ", kind
|
||||
# writeStackTrace()
|
||||
# messageOut(typeKindToStr[kind] & ' has id: ' & toString(result.id))
|
||||
|
||||
proc mergeLoc(a: var TLoc, b: TLoc) =
|
||||
|
|
@ -1200,7 +1186,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 assignType(dest, src: PType) =
|
||||
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):
|
||||
newSeq(father.sons, length)
|
||||
else:
|
||||
setLen(father.sons, length)
|
||||
|
||||
proc sonsLen*(n: PType): int =
|
||||
if isNil(n.sons): result = 0
|
||||
else: result = len(n.sons)
|
||||
|
||||
proc len*(n: PType): int =
|
||||
if isNil(n.sons): result = 0
|
||||
else: result = len(n.sons)
|
||||
|
||||
proc sonsLen*(n: PNode): int =
|
||||
if isNil(n.sons): result = 0
|
||||
else: result = len(n.sons)
|
||||
|
||||
proc lastSon*(n: PNode): PNode =
|
||||
result = n.sons[sonsLen(n) - 1]
|
||||
|
||||
proc lastSon*(n: PType): PType =
|
||||
result = n.sons[sonsLen(n) - 1]
|
||||
|
||||
proc assignType*(dest, src: PType) =
|
||||
dest.kind = src.kind
|
||||
dest.flags = src.flags
|
||||
dest.callConv = src.callConv
|
||||
|
|
@ -1209,6 +1225,7 @@ proc assignType(dest, src: PType) =
|
|||
dest.align = src.align
|
||||
dest.destructor = src.destructor
|
||||
dest.deepCopy = src.deepCopy
|
||||
dest.assignment = src.assignment
|
||||
dest.lockLevel = src.lockLevel
|
||||
# this fixes 'type TLock = TSysLock':
|
||||
if src.sym != nil:
|
||||
|
|
@ -1221,7 +1238,7 @@ proc assignType(dest, src: PType) =
|
|||
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:
|
||||
|
|
@ -1230,7 +1247,7 @@ proc copyType(t: PType, owner: PSym, keepId: bool): PType =
|
|||
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
|
||||
|
|
@ -1264,24 +1281,7 @@ proc createModuleAlias*(s: PSym, newIdent: PIdent, info: TLineInfo): PSym =
|
|||
# XXX once usedGenerics is used, ensure module aliases keep working!
|
||||
assert s.usedGenerics == nil
|
||||
|
||||
proc newSym(symKind: TSymKind, name: PIdent, owner: PSym,
|
||||
info: TLineInfo): PSym =
|
||||
# generates a symbol and initializes the hash field too
|
||||
new(result)
|
||||
result.name = name
|
||||
result.kind = symKind
|
||||
result.flags = {}
|
||||
result.info = info
|
||||
result.options = gOptions
|
||||
result.owner = owner
|
||||
result.offset = - 1
|
||||
result.id = getID()
|
||||
when debugIds:
|
||||
registerId(result)
|
||||
#if result.id < 2000:
|
||||
# MessageOut(name.s & " has id: " & toString(result.id))
|
||||
|
||||
proc initStrTable(x: var TStrTable) =
|
||||
proc initStrTable*(x: var TStrTable) =
|
||||
x.counter = 0
|
||||
newSeq(x.data, StartSize)
|
||||
|
||||
|
|
@ -1292,46 +1292,28 @@ 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)
|
||||
|
||||
proc initObjectSet(x: var TObjectSet) =
|
||||
proc resetIdTable*(x: var TIdTable) =
|
||||
x.counter = 0
|
||||
# clear and set to old initial size:
|
||||
setLen(x.data, 0)
|
||||
setLen(x.data, StartSize)
|
||||
|
||||
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)
|
||||
|
||||
proc sonsLen(n: PType): int =
|
||||
if isNil(n.sons): result = 0
|
||||
else: result = len(n.sons)
|
||||
|
||||
proc len*(n: PType): int =
|
||||
if isNil(n.sons): result = 0
|
||||
else: result = len(n.sons)
|
||||
|
||||
proc newSons(father: PType, length: int) =
|
||||
if isNil(father.sons):
|
||||
newSeq(father.sons, length)
|
||||
else:
|
||||
setLen(father.sons, length)
|
||||
|
||||
proc sonsLen(n: PNode): int =
|
||||
if isNil(n.sons): result = 0
|
||||
else: result = len(n.sons)
|
||||
|
||||
proc newSons(father: PNode, length: int) =
|
||||
if isNil(father.sons):
|
||||
newSeq(father.sons, length)
|
||||
else:
|
||||
setLen(father.sons, length)
|
||||
|
||||
proc skipTypes*(t: PType, kinds: TTypeKinds): PType =
|
||||
## Used throughout the compiler code to test whether a type tree contains or
|
||||
## doesn't contain a specific type/types - it is often the case that only the
|
||||
|
|
@ -1340,15 +1322,22 @@ proc skipTypes*(t: PType, kinds: TTypeKinds): PType =
|
|||
result = t
|
||||
while result.kind in kinds: result = lastSon(result)
|
||||
|
||||
proc skipTypesOrNil*(t: PType, kinds: TTypeKinds): PType =
|
||||
## same as skipTypes but handles 'nil'
|
||||
result = t
|
||||
while result != nil and result.kind in kinds:
|
||||
if result.len == 0: return nil
|
||||
result = lastSon(result)
|
||||
|
||||
proc isGCedMem*(t: PType): bool {.inline.} =
|
||||
result = t.kind in {tyString, tyRef, tySequence} or
|
||||
t.kind == tyProc and t.callConv == ccClosure
|
||||
|
||||
proc propagateToOwner*(owner, elem: PType) =
|
||||
const HaveTheirOwnEmpty = {tySequence, tySet}
|
||||
owner.flags = owner.flags + (elem.flags * {tfHasShared, tfHasMeta})
|
||||
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, tyGenericInvokation}:
|
||||
if owner.kind in {tyGenericInst, tyGenericBody, tyGenericInvocation}:
|
||||
owner.flags.incl tfNotNil
|
||||
elif owner.kind notin HaveTheirOwnEmpty:
|
||||
owner.flags.incl tfNeedsInit
|
||||
|
|
@ -1357,14 +1346,22 @@ proc propagateToOwner*(owner, elem: PType) =
|
|||
if owner.kind in HaveTheirOwnEmpty: discard
|
||||
else: owner.flags.incl tfNeedsInit
|
||||
|
||||
if tfShared in elem.flags:
|
||||
owner.flags.incl tfHasShared
|
||||
|
||||
if elem.isMetaType:
|
||||
owner.flags.incl tfHasMeta
|
||||
|
||||
if owner.kind != tyProc:
|
||||
if elem.isGCedMem or tfHasGCedMem in elem.flags:
|
||||
if tfHasAsgn in elem.flags:
|
||||
let o2 = elem.skipTypes({tyGenericInst})
|
||||
if o2.kind in {tyTuple, tyObject, tyArray, tyArrayConstr,
|
||||
tySequence, tySet, tyDistinct}:
|
||||
o2.flags.incl tfHasAsgn
|
||||
owner.flags.incl tfHasAsgn
|
||||
|
||||
if owner.kind notin {tyProc, tyGenericInst, tyGenericBody,
|
||||
tyGenericInvocation}:
|
||||
let elemB = elem.skipTypes({tyGenericInst})
|
||||
if elemB.isGCedMem or tfHasGCedMem in elemB.flags:
|
||||
# for simplicity, we propagate this flag even to generics. We then
|
||||
# ensure this doesn't bite us in sempass2.
|
||||
owner.flags.incl tfHasGCedMem
|
||||
|
||||
proc rawAddSon*(father, son: PType) =
|
||||
|
|
@ -1372,22 +1369,17 @@ proc rawAddSon*(father, son: PType) =
|
|||
add(father.sons, son)
|
||||
if not son.isNil: propagateToOwner(father, son)
|
||||
|
||||
proc addSon(father, son: PNode) =
|
||||
assert son != nil
|
||||
if isNil(father.sons): father.sons = @[]
|
||||
add(father.sons, son)
|
||||
|
||||
proc addSonNilAllowed*(father, son: PNode) =
|
||||
if isNil(father.sons): father.sons = @[]
|
||||
add(father.sons, son)
|
||||
|
||||
proc delSon(father: PNode, idx: int) =
|
||||
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:
|
||||
return nil
|
||||
|
|
@ -1424,7 +1416,7 @@ 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:
|
||||
return nil
|
||||
|
|
@ -1446,13 +1438,7 @@ proc copyTree(src: PNode): PNode =
|
|||
for i in countup(0, sonsLen(src) - 1):
|
||||
result.sons[i] = copyTree(src.sons[i])
|
||||
|
||||
proc lastSon(n: PNode): PNode =
|
||||
result = n.sons[sonsLen(n) - 1]
|
||||
|
||||
proc lastSon(n: PType): PType =
|
||||
result = n.sons[sonsLen(n) - 1]
|
||||
|
||||
proc hasSonWith(n: PNode, kind: TNodeKind): bool =
|
||||
proc hasSonWith*(n: PNode, kind: TNodeKind): bool =
|
||||
for i in countup(0, sonsLen(n) - 1):
|
||||
if n.sons[i].kind == kind:
|
||||
return true
|
||||
|
|
@ -1474,7 +1460,7 @@ 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:
|
||||
|
|
@ -1517,7 +1503,7 @@ proc getStr*(a: PNode): string =
|
|||
proc getStrOrChar*(a: PNode): string =
|
||||
case a.kind
|
||||
of nkStrLit..nkTripleStrLit: result = a.strVal
|
||||
of nkCharLit: result = $chr(int(a.intVal))
|
||||
of nkCharLit..nkUInt64Lit: result = $chr(int(a.intVal))
|
||||
else:
|
||||
internalError(a.info, "getStrOrChar")
|
||||
result = ""
|
||||
|
|
@ -1566,3 +1552,11 @@ proc makeStmtList*(n: PNode): PNode =
|
|||
else:
|
||||
result = newNodeI(nkStmtList, n.info)
|
||||
result.add n
|
||||
|
||||
proc createMagic*(name: string, m: TMagic): PSym =
|
||||
result = newSym(skProc, getIdent(name), nil, unknownLineInfo())
|
||||
result.magic = m
|
||||
|
||||
let
|
||||
opNot* = createMagic("not", mNot)
|
||||
opContains* = createMagic("contains", mInSet)
|
||||
|
|
|
|||
|
|
@ -15,13 +15,13 @@ import
|
|||
ast, hashes, intsets, strutils, options, msgs, ropes, idents, rodutils
|
||||
|
||||
proc hashNode*(p: RootRef): THash
|
||||
proc treeToYaml*(n: PNode, indent: int = 0, maxRecDepth: int = - 1): PRope
|
||||
proc treeToYaml*(n: PNode, indent: int = 0, maxRecDepth: int = - 1): Rope
|
||||
# Convert a tree into its YAML representation; this is used by the
|
||||
# YAML code generator and it is invaluable for debugging purposes.
|
||||
# If maxRecDepht <> -1 then it won't print the whole graph.
|
||||
proc typeToYaml*(n: PType, indent: int = 0, maxRecDepth: int = - 1): PRope
|
||||
proc symToYaml*(n: PSym, indent: int = 0, maxRecDepth: int = - 1): PRope
|
||||
proc lineInfoToStr*(info: TLineInfo): PRope
|
||||
proc typeToYaml*(n: PType, indent: int = 0, maxRecDepth: int = - 1): Rope
|
||||
proc symToYaml*(n: PSym, indent: int = 0, maxRecDepth: int = - 1): Rope
|
||||
proc lineInfoToStr*(info: TLineInfo): Rope
|
||||
|
||||
# ----------------------- node sets: ---------------------------------------
|
||||
proc objectSetContains*(t: TObjectSet, obj: RootRef): bool
|
||||
|
|
@ -130,8 +130,8 @@ proc skipConvAndClosure*(n: PNode): PNode =
|
|||
proc sameValue*(a, b: PNode): bool =
|
||||
result = false
|
||||
case a.kind
|
||||
of nkCharLit..nkInt64Lit:
|
||||
if b.kind in {nkCharLit..nkInt64Lit}: result = a.intVal == b.intVal
|
||||
of nkCharLit..nkUInt64Lit:
|
||||
if b.kind in {nkCharLit..nkUInt64Lit}: result = a.intVal == b.intVal
|
||||
of nkFloatLit..nkFloat64Lit:
|
||||
if b.kind in {nkFloatLit..nkFloat64Lit}: result = a.floatVal == b.floatVal
|
||||
of nkStrLit..nkTripleStrLit:
|
||||
|
|
@ -145,8 +145,8 @@ proc leValue*(a, b: PNode): bool =
|
|||
# a <= b?
|
||||
result = false
|
||||
case a.kind
|
||||
of nkCharLit..nkInt64Lit:
|
||||
if b.kind in {nkCharLit..nkInt64Lit}: result = a.intVal <= b.intVal
|
||||
of nkCharLit..nkUInt32Lit:
|
||||
if b.kind in {nkCharLit..nkUInt32Lit}: result = a.intVal <= b.intVal
|
||||
of nkFloatLit..nkFloat64Lit:
|
||||
if b.kind in {nkFloatLit..nkFloat64Lit}: result = a.floatVal <= b.floatVal
|
||||
of nkStrLit..nkTripleStrLit:
|
||||
|
|
@ -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): Rope =
|
||||
# returns x spaces
|
||||
result = toRope(repeatChar(x))
|
||||
result = rope(spaces(x))
|
||||
|
||||
proc toYamlChar(c: char): string =
|
||||
case c
|
||||
|
|
@ -213,7 +213,7 @@ proc toYamlChar(c: char): string =
|
|||
of '\'', '\"', '\\': result = '\\' & c
|
||||
else: result = $c
|
||||
|
||||
proc makeYamlString*(s: string): PRope =
|
||||
proc makeYamlString*(s: string): Rope =
|
||||
# We have to split long strings into many ropes. Otherwise
|
||||
# this could trigger InternalError(111). See the ropes module for
|
||||
# further information.
|
||||
|
|
@ -224,242 +224,244 @@ proc makeYamlString*(s: string): PRope =
|
|||
if (i + 1) mod MaxLineLength == 0:
|
||||
add(res, '\"')
|
||||
add(res, "\n")
|
||||
app(result, toRope(res))
|
||||
add(result, rope(res))
|
||||
res = "\"" # reset
|
||||
add(res, toYamlChar(s[i]))
|
||||
add(res, '\"')
|
||||
app(result, toRope(res))
|
||||
add(result, rope(res))
|
||||
|
||||
proc flagsToStr[T](flags: set[T]): PRope =
|
||||
proc flagsToStr[T](flags: set[T]): Rope =
|
||||
if flags == {}:
|
||||
result = toRope("[]")
|
||||
result = rope("[]")
|
||||
else:
|
||||
result = nil
|
||||
for x in items(flags):
|
||||
if result != nil: app(result, ", ")
|
||||
app(result, makeYamlString($x))
|
||||
result = con("[", con(result, "]"))
|
||||
if result != nil: add(result, ", ")
|
||||
add(result, makeYamlString($x))
|
||||
result = "[" & result & "]"
|
||||
|
||||
proc lineInfoToStr(info: TLineInfo): PRope =
|
||||
result = ropef("[$1, $2, $3]", [makeYamlString(toFilename(info)),
|
||||
toRope(toLinenumber(info)),
|
||||
toRope(toColumn(info))])
|
||||
proc lineInfoToStr(info: TLineInfo): Rope =
|
||||
result = "[$1, $2, $3]" % [makeYamlString(toFilename(info)),
|
||||
rope(toLinenumber(info)),
|
||||
rope(toColumn(info))]
|
||||
|
||||
proc treeToYamlAux(n: PNode, marker: var IntSet,
|
||||
indent, maxRecDepth: int): PRope
|
||||
indent, maxRecDepth: int): Rope
|
||||
proc symToYamlAux(n: PSym, marker: var IntSet,
|
||||
indent, maxRecDepth: int): PRope
|
||||
indent, maxRecDepth: int): Rope
|
||||
proc typeToYamlAux(n: PType, marker: var IntSet,
|
||||
indent, maxRecDepth: int): PRope
|
||||
indent, maxRecDepth: int): Rope
|
||||
proc strTableToYaml(n: TStrTable, marker: var IntSet, indent: int,
|
||||
maxRecDepth: int): PRope =
|
||||
var istr = spaces(indent + 2)
|
||||
result = toRope("[")
|
||||
maxRecDepth: int): Rope =
|
||||
var istr = rspaces(indent + 2)
|
||||
result = rope("[")
|
||||
var mycount = 0
|
||||
for i in countup(0, high(n.data)):
|
||||
if n.data[i] != nil:
|
||||
if mycount > 0: app(result, ",")
|
||||
appf(result, "$N$1$2",
|
||||
if mycount > 0: add(result, ",")
|
||||
addf(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)])
|
||||
app(result, "]")
|
||||
if mycount > 0: addf(result, "$N$1", [rspaces(indent)])
|
||||
add(result, "]")
|
||||
assert(mycount == n.counter)
|
||||
|
||||
proc ropeConstr(indent: int, c: openArray[PRope]): PRope =
|
||||
proc ropeConstr(indent: int, c: openArray[Rope]): Rope =
|
||||
# array of (name, value) pairs
|
||||
var istr = spaces(indent + 2)
|
||||
result = toRope("{")
|
||||
var istr = rspaces(indent + 2)
|
||||
result = rope("{")
|
||||
var i = 0
|
||||
while i <= high(c):
|
||||
if i > 0: app(result, ",")
|
||||
appf(result, "$N$1\"$2\": $3", [istr, c[i], c[i + 1]])
|
||||
if i > 0: add(result, ",")
|
||||
addf(result, "$N$1\"$2\": $3", [istr, c[i], c[i + 1]])
|
||||
inc(i, 2)
|
||||
appf(result, "$N$1}", [spaces(indent)])
|
||||
addf(result, "$N$1}", [rspaces(indent)])
|
||||
|
||||
proc symToYamlAux(n: PSym, marker: var IntSet, indent: int,
|
||||
maxRecDepth: int): PRope =
|
||||
maxRecDepth: int): Rope =
|
||||
if n == nil:
|
||||
result = toRope("null")
|
||||
result = rope("null")
|
||||
elif containsOrIncl(marker, n.id):
|
||||
result = ropef("\"$1 @$2\"", [toRope(n.name.s), toRope(
|
||||
strutils.toHex(cast[ByteAddress](n), sizeof(n) * 2))])
|
||||
result = "\"$1 @$2\"" % [rope(n.name.s), rope(
|
||||
strutils.toHex(cast[ByteAddress](n), sizeof(n) * 2))]
|
||||
else:
|
||||
var ast = treeToYamlAux(n.ast, marker, indent + 2, maxRecDepth - 1)
|
||||
result = ropeConstr(indent, [toRope("kind"),
|
||||
result = ropeConstr(indent, [rope("kind"),
|
||||
makeYamlString($n.kind),
|
||||
toRope("name"), makeYamlString(n.name.s),
|
||||
toRope("typ"), typeToYamlAux(n.typ, marker,
|
||||
rope("name"), makeYamlString(n.name.s),
|
||||
rope("typ"), typeToYamlAux(n.typ, marker,
|
||||
indent + 2, maxRecDepth - 1),
|
||||
toRope("info"), lineInfoToStr(n.info),
|
||||
toRope("flags"), flagsToStr(n.flags),
|
||||
toRope("magic"), makeYamlString($n.magic),
|
||||
toRope("ast"), ast, toRope("options"),
|
||||
flagsToStr(n.options), toRope("position"),
|
||||
toRope(n.position)])
|
||||
rope("info"), lineInfoToStr(n.info),
|
||||
rope("flags"), flagsToStr(n.flags),
|
||||
rope("magic"), makeYamlString($n.magic),
|
||||
rope("ast"), ast, rope("options"),
|
||||
flagsToStr(n.options), rope("position"),
|
||||
rope(n.position)])
|
||||
|
||||
proc typeToYamlAux(n: PType, marker: var IntSet, indent: int,
|
||||
maxRecDepth: int): PRope =
|
||||
maxRecDepth: int): Rope =
|
||||
if n == nil:
|
||||
result = toRope("null")
|
||||
result = rope("null")
|
||||
elif containsOrIncl(marker, n.id):
|
||||
result = ropef("\"$1 @$2\"", [toRope($n.kind), toRope(
|
||||
strutils.toHex(cast[ByteAddress](n), sizeof(n) * 2))])
|
||||
result = "\"$1 @$2\"" % [rope($n.kind), rope(
|
||||
strutils.toHex(cast[ByteAddress](n), sizeof(n) * 2))]
|
||||
else:
|
||||
if sonsLen(n) > 0:
|
||||
result = toRope("[")
|
||||
result = rope("[")
|
||||
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],
|
||||
if i > 0: add(result, ",")
|
||||
addf(result, "$N$1$2", [rspaces(indent + 4), typeToYamlAux(n.sons[i],
|
||||
marker, indent + 4, maxRecDepth - 1)])
|
||||
appf(result, "$N$1]", [spaces(indent + 2)])
|
||||
addf(result, "$N$1]", [rspaces(indent + 2)])
|
||||
else:
|
||||
result = toRope("null")
|
||||
result = ropeConstr(indent, [toRope("kind"),
|
||||
result = rope("null")
|
||||
result = ropeConstr(indent, [rope("kind"),
|
||||
makeYamlString($n.kind),
|
||||
toRope("sym"), symToYamlAux(n.sym, marker,
|
||||
indent + 2, maxRecDepth - 1), toRope("n"), treeToYamlAux(n.n, marker,
|
||||
indent + 2, maxRecDepth - 1), toRope("flags"), flagsToStr(n.flags),
|
||||
toRope("callconv"),
|
||||
rope("sym"), symToYamlAux(n.sym, marker,
|
||||
indent + 2, maxRecDepth - 1), rope("n"), treeToYamlAux(n.n, marker,
|
||||
indent + 2, maxRecDepth - 1), rope("flags"), flagsToStr(n.flags),
|
||||
rope("callconv"),
|
||||
makeYamlString(CallingConvToStr[n.callConv]),
|
||||
toRope("size"), toRope(n.size),
|
||||
toRope("align"), toRope(n.align),
|
||||
toRope("sons"), result])
|
||||
rope("size"), rope(n.size),
|
||||
rope("align"), rope(n.align),
|
||||
rope("sons"), result])
|
||||
|
||||
proc treeToYamlAux(n: PNode, marker: var IntSet, indent: int,
|
||||
maxRecDepth: int): PRope =
|
||||
maxRecDepth: int): Rope =
|
||||
if n == nil:
|
||||
result = toRope("null")
|
||||
result = rope("null")
|
||||
else:
|
||||
var istr = spaces(indent + 2)
|
||||
result = ropef("{$N$1\"kind\": $2", [istr, makeYamlString($n.kind)])
|
||||
var istr = rspaces(indent + 2)
|
||||
result = "{$N$1\"kind\": $2" % [istr, makeYamlString($n.kind)]
|
||||
if maxRecDepth != 0:
|
||||
appf(result, ",$N$1\"info\": $2", [istr, lineInfoToStr(n.info)])
|
||||
addf(result, ",$N$1\"info\": $2", [istr, lineInfoToStr(n.info)])
|
||||
case n.kind
|
||||
of nkCharLit..nkInt64Lit:
|
||||
appf(result, ",$N$1\"intVal\": $2", [istr, toRope(n.intVal)])
|
||||
addf(result, ",$N$1\"intVal\": $2", [istr, rope(n.intVal)])
|
||||
of nkFloatLit, nkFloat32Lit, nkFloat64Lit:
|
||||
appf(result, ",$N$1\"floatVal\": $2",
|
||||
[istr, toRope(n.floatVal.toStrMaxPrecision)])
|
||||
addf(result, ",$N$1\"floatVal\": $2",
|
||||
[istr, rope(n.floatVal.toStrMaxPrecision)])
|
||||
of nkStrLit..nkTripleStrLit:
|
||||
if n.strVal.isNil:
|
||||
appf(result, ",$N$1\"strVal\": null", [istr])
|
||||
addf(result, ",$N$1\"strVal\": null", [istr])
|
||||
else:
|
||||
appf(result, ",$N$1\"strVal\": $2", [istr, makeYamlString(n.strVal)])
|
||||
addf(result, ",$N$1\"strVal\": $2", [istr, makeYamlString(n.strVal)])
|
||||
of nkSym:
|
||||
appf(result, ",$N$1\"sym\": $2",
|
||||
addf(result, ",$N$1\"sym\": $2",
|
||||
[istr, symToYamlAux(n.sym, marker, indent + 2, maxRecDepth)])
|
||||
of nkIdent:
|
||||
if n.ident != nil:
|
||||
appf(result, ",$N$1\"ident\": $2", [istr, makeYamlString(n.ident.s)])
|
||||
addf(result, ",$N$1\"ident\": $2", [istr, makeYamlString(n.ident.s)])
|
||||
else:
|
||||
appf(result, ",$N$1\"ident\": null", [istr])
|
||||
addf(result, ",$N$1\"ident\": null", [istr])
|
||||
else:
|
||||
if sonsLen(n) > 0:
|
||||
appf(result, ",$N$1\"sons\": [", [istr])
|
||||
addf(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],
|
||||
if i > 0: add(result, ",")
|
||||
addf(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",
|
||||
addf(result, "$N$1]", [istr])
|
||||
addf(result, ",$N$1\"typ\": $2",
|
||||
[istr, typeToYamlAux(n.typ, marker, indent + 2, maxRecDepth)])
|
||||
appf(result, "$N$1}", [spaces(indent)])
|
||||
addf(result, "$N$1}", [rspaces(indent)])
|
||||
|
||||
proc treeToYaml(n: PNode, indent: int = 0, maxRecDepth: int = - 1): PRope =
|
||||
proc treeToYaml(n: PNode, indent: int = 0, maxRecDepth: int = - 1): Rope =
|
||||
var marker = initIntSet()
|
||||
result = treeToYamlAux(n, marker, indent, maxRecDepth)
|
||||
|
||||
proc typeToYaml(n: PType, indent: int = 0, maxRecDepth: int = - 1): PRope =
|
||||
proc typeToYaml(n: PType, indent: int = 0, maxRecDepth: int = - 1): Rope =
|
||||
var marker = initIntSet()
|
||||
result = typeToYamlAux(n, marker, indent, maxRecDepth)
|
||||
|
||||
proc symToYaml(n: PSym, indent: int = 0, maxRecDepth: int = - 1): PRope =
|
||||
proc symToYaml(n: PSym, indent: int = 0, maxRecDepth: int = - 1): Rope =
|
||||
var marker = initIntSet()
|
||||
result = symToYamlAux(n, marker, indent, maxRecDepth)
|
||||
|
||||
proc debugTree(n: PNode, indent: int, maxRecDepth: int; renderType=false): PRope
|
||||
proc debugType(n: PType, maxRecDepth=100): PRope =
|
||||
proc debugTree*(n: PNode, indent: int, maxRecDepth: int; renderType=false): Rope
|
||||
proc debugType(n: PType, maxRecDepth=100): Rope =
|
||||
if n == nil:
|
||||
result = toRope("null")
|
||||
result = rope("null")
|
||||
else:
|
||||
result = toRope($n.kind)
|
||||
result = rope($n.kind)
|
||||
if n.sym != nil:
|
||||
app(result, " ")
|
||||
app(result, n.sym.name.s)
|
||||
add(result, " ")
|
||||
add(result, n.sym.name.s)
|
||||
if n.kind in IntegralTypes and n.n != nil:
|
||||
add(result, ", node: ")
|
||||
add(result, debugTree(n.n, 2, maxRecDepth-1, renderType=true))
|
||||
if (n.kind != tyString) and (sonsLen(n) > 0) and maxRecDepth != 0:
|
||||
app(result, "(")
|
||||
add(result, "(")
|
||||
for i in countup(0, sonsLen(n) - 1):
|
||||
if i > 0: app(result, ", ")
|
||||
if i > 0: add(result, ", ")
|
||||
if n.sons[i] == nil:
|
||||
app(result, "null")
|
||||
add(result, "null")
|
||||
else:
|
||||
app(result, debugType(n.sons[i], maxRecDepth-1))
|
||||
add(result, debugType(n.sons[i], maxRecDepth-1))
|
||||
if n.kind == tyObject and n.n != nil:
|
||||
app(result, ", node: ")
|
||||
app(result, debugTree(n.n, 2, maxRecDepth-1, renderType=true))
|
||||
app(result, ")")
|
||||
add(result, ", node: ")
|
||||
add(result, debugTree(n.n, 2, maxRecDepth-1, renderType=true))
|
||||
add(result, ")")
|
||||
|
||||
proc debugTree(n: PNode, indent: int, maxRecDepth: int;
|
||||
renderType=false): PRope =
|
||||
renderType=false): Rope =
|
||||
if n == nil:
|
||||
result = toRope("null")
|
||||
result = rope("null")
|
||||
else:
|
||||
var istr = spaces(indent + 2)
|
||||
result = ropef("{$N$1\"kind\": $2",
|
||||
[istr, makeYamlString($n.kind)])
|
||||
var istr = rspaces(indent + 2)
|
||||
result = "{$N$1\"kind\": $2" %
|
||||
[istr, makeYamlString($n.kind)]
|
||||
if maxRecDepth != 0:
|
||||
case n.kind
|
||||
of nkCharLit..nkUInt64Lit:
|
||||
appf(result, ",$N$1\"intVal\": $2", [istr, toRope(n.intVal)])
|
||||
addf(result, ",$N$1\"intVal\": $2", [istr, rope(n.intVal)])
|
||||
of nkFloatLit, nkFloat32Lit, nkFloat64Lit:
|
||||
appf(result, ",$N$1\"floatVal\": $2",
|
||||
[istr, toRope(n.floatVal.toStrMaxPrecision)])
|
||||
addf(result, ",$N$1\"floatVal\": $2",
|
||||
[istr, rope(n.floatVal.toStrMaxPrecision)])
|
||||
of nkStrLit..nkTripleStrLit:
|
||||
if n.strVal.isNil:
|
||||
appf(result, ",$N$1\"strVal\": null", [istr])
|
||||
addf(result, ",$N$1\"strVal\": null", [istr])
|
||||
else:
|
||||
appf(result, ",$N$1\"strVal\": $2", [istr, makeYamlString(n.strVal)])
|
||||
addf(result, ",$N$1\"strVal\": $2", [istr, makeYamlString(n.strVal)])
|
||||
of nkSym:
|
||||
appf(result, ",$N$1\"sym\": $2_$3",
|
||||
[istr, toRope(n.sym.name.s), toRope(n.sym.id)])
|
||||
addf(result, ",$N$1\"sym\": $2_$3",
|
||||
[istr, rope(n.sym.name.s), rope(n.sym.id)])
|
||||
# [istr, symToYaml(n.sym, indent, maxRecDepth),
|
||||
# toRope(n.sym.id)])
|
||||
# rope(n.sym.id)])
|
||||
if renderType and n.sym.typ != nil:
|
||||
appf(result, ",$N$1\"typ\": $2", [istr, debugType(n.sym.typ, 2)])
|
||||
addf(result, ",$N$1\"typ\": $2", [istr, debugType(n.sym.typ, 2)])
|
||||
of nkIdent:
|
||||
if n.ident != nil:
|
||||
appf(result, ",$N$1\"ident\": $2", [istr, makeYamlString(n.ident.s)])
|
||||
addf(result, ",$N$1\"ident\": $2", [istr, makeYamlString(n.ident.s)])
|
||||
else:
|
||||
appf(result, ",$N$1\"ident\": null", [istr])
|
||||
addf(result, ",$N$1\"ident\": null", [istr])
|
||||
else:
|
||||
if sonsLen(n) > 0:
|
||||
appf(result, ",$N$1\"sons\": [", [istr])
|
||||
addf(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],
|
||||
if i > 0: add(result, ",")
|
||||
addf(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)])
|
||||
addf(result, "$N$1]", [istr])
|
||||
addf(result, ",$N$1\"info\": $2", [istr, lineInfoToStr(n.info)])
|
||||
addf(result, "$N$1}", [rspaces(indent)])
|
||||
|
||||
proc debug(n: PSym) =
|
||||
if n == nil:
|
||||
writeln(stdout, "null")
|
||||
msgWriteln("null")
|
||||
elif n.kind == skUnknown:
|
||||
writeln(stdout, "skUnknown")
|
||||
msgWriteln("skUnknown")
|
||||
else:
|
||||
#writeln(stdout, ropeToStr(symToYaml(n, 0, 1)))
|
||||
writeln(stdout, "$1_$2: $3, $4, $5, $6" % [
|
||||
n.name.s, $n.id, flagsToStr(n.flags).ropeToStr,
|
||||
flagsToStr(n.loc.flags).ropeToStr, lineInfoToStr(n.info).ropeToStr,
|
||||
$n.kind])
|
||||
#writeln(stdout, $symToYaml(n, 0, 1))
|
||||
msgWriteln("$1_$2: $3, $4, $5, $6" % [
|
||||
n.name.s, $n.id, $flagsToStr(n.flags), $flagsToStr(n.loc.flags),
|
||||
$lineInfoToStr(n.info), $n.kind])
|
||||
|
||||
proc debug(n: PType) =
|
||||
writeln(stdout, ropeToStr(debugType(n)))
|
||||
msgWriteln($debugType(n))
|
||||
|
||||
proc debug(n: PNode) =
|
||||
writeln(stdout, ropeToStr(debugTree(n, 0, 100)))
|
||||
msgWriteln($debugTree(n, 0, 100))
|
||||
|
||||
const
|
||||
EmptySeq = @[]
|
||||
|
|
@ -678,9 +680,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
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
#
|
||||
#
|
||||
# The Nim Compiler
|
||||
# (c) Copyright 2014 Andreas Rumpf
|
||||
# (c) Copyright 2015 Andreas Rumpf
|
||||
#
|
||||
# See the file "copying.txt", included in this
|
||||
# distribution, for details about the copyright.
|
||||
|
|
@ -119,8 +119,8 @@ proc hashType(c: var MD5Context, t: PType) =
|
|||
c.hashSym(t.sym)
|
||||
|
||||
case t.kind
|
||||
of tyGenericBody, tyGenericInst, tyGenericInvokation:
|
||||
for i in countup(0, sonsLen(t) -1 -ord(t.kind != tyGenericInvokation)):
|
||||
of tyGenericBody, tyGenericInst, tyGenericInvocation:
|
||||
for i in countup(0, sonsLen(t) -1 -ord(t.kind != tyGenericInvocation)):
|
||||
c.hashType t.sons[i]
|
||||
of tyUserTypeClass:
|
||||
internalAssert t.sym != nil and t.sym.owner != nil
|
||||
|
|
@ -259,7 +259,7 @@ proc encodeLoc(w: PRodWriter, loc: TLoc, result: var string) =
|
|||
pushType(w, loc.t)
|
||||
if loc.r != nil:
|
||||
add(result, '!')
|
||||
encodeStr(ropeToStr(loc.r), result)
|
||||
encodeStr($loc.r, result)
|
||||
if loc.a != 0:
|
||||
add(result, '?')
|
||||
encodeVInt(loc.a, result)
|
||||
|
|
@ -317,7 +317,7 @@ proc encodeLib(w: PRodWriter, lib: PLib, info: TLineInfo, result: var string) =
|
|||
add(result, '|')
|
||||
encodeVInt(ord(lib.kind), result)
|
||||
add(result, '|')
|
||||
encodeStr(ropeToStr(lib.name), result)
|
||||
encodeStr($lib.name, result)
|
||||
add(result, '|')
|
||||
encodeNode(w, info, lib.path, result)
|
||||
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
#
|
||||
#
|
||||
# The Nim Compiler
|
||||
# (c) Copyright 2013 Andreas Rumpf
|
||||
# (c) Copyright 2015 Andreas Rumpf
|
||||
#
|
||||
# See the file "copying.txt", included in this
|
||||
# distribution, for details about the copyright.
|
||||
|
|
@ -19,13 +19,13 @@ proc hasNoInit(call: PNode): bool {.inline.} =
|
|||
result = call.sons[0].kind == nkSym and sfNoInit in call.sons[0].sym.flags
|
||||
|
||||
proc fixupCall(p: BProc, le, ri: PNode, d: var TLoc,
|
||||
callee, params: PRope) =
|
||||
var pl = con(callee, ~"(", params)
|
||||
callee, params: Rope) =
|
||||
var pl = callee & ~"(" & params
|
||||
# getUniqueType() is too expensive here:
|
||||
var typ = skipTypes(ri.sons[0].typ, abstractInst)
|
||||
if typ.sons[0] != nil:
|
||||
if isInvalidReturnType(typ.sons[0]):
|
||||
if params != nil: pl.app(~", ")
|
||||
if params != nil: pl.add(~", ")
|
||||
# beware of 'result = p(result)'. We may need to allocate a temporary:
|
||||
if d.k in {locTemp, locNone} or not leftAppearsOnRightSide(le, ri):
|
||||
# Great, we can use 'd':
|
||||
|
|
@ -33,26 +33,34 @@ proc fixupCall(p: BProc, le, ri: PNode, d: var TLoc,
|
|||
elif d.k notin {locExpr, locTemp} and not hasNoInit(ri):
|
||||
# reset before pass as 'result' var:
|
||||
resetLoc(p, d)
|
||||
app(pl, addrLoc(d))
|
||||
app(pl, ~");$n")
|
||||
add(pl, addrLoc(d))
|
||||
add(pl, ~");$n")
|
||||
line(p, cpsStmts, pl)
|
||||
else:
|
||||
var tmp: TLoc
|
||||
getTemp(p, typ.sons[0], tmp, needsInit=true)
|
||||
app(pl, addrLoc(tmp))
|
||||
app(pl, ~");$n")
|
||||
add(pl, addrLoc(tmp))
|
||||
add(pl, ~");$n")
|
||||
line(p, cpsStmts, pl)
|
||||
genAssignment(p, d, tmp, {}) # no need for deep copying
|
||||
else:
|
||||
app(pl, ~")")
|
||||
if d.k == locNone: getTemp(p, typ.sons[0], d)
|
||||
assert(d.t != nil) # generate an assignment to d:
|
||||
var list: TLoc
|
||||
initLoc(list, locCall, d.t, OnUnknown)
|
||||
list.r = pl
|
||||
genAssignment(p, d, list, {}) # no need for deep copying
|
||||
add(pl, ~")")
|
||||
if p.module.compileToCpp and lfSingleUse in d.flags:
|
||||
# do not generate spurious temporaries for C++! For C we're better off
|
||||
# with them to prevent undefined behaviour and because the codegen
|
||||
# is free to emit expressions multiple times!
|
||||
d.k = locCall
|
||||
d.r = pl
|
||||
excl d.flags, lfSingleUse
|
||||
else:
|
||||
if d.k == locNone: getTemp(p, typ.sons[0], d)
|
||||
assert(d.t != nil) # generate an assignment to d:
|
||||
var list: TLoc
|
||||
initLoc(list, locCall, d.t, OnUnknown)
|
||||
list.r = pl
|
||||
genAssignment(p, d, list, {}) # no need for deep copying
|
||||
else:
|
||||
app(pl, ~");$n")
|
||||
add(pl, ~");$n")
|
||||
line(p, cpsStmts, pl)
|
||||
|
||||
proc isInCurrentFrame(p: BProc, n: PNode): bool =
|
||||
|
|
@ -75,7 +83,7 @@ proc isInCurrentFrame(p: BProc, n: PNode): bool =
|
|||
result = isInCurrentFrame(p, n.sons[0])
|
||||
else: discard
|
||||
|
||||
proc openArrayLoc(p: BProc, n: PNode): PRope =
|
||||
proc openArrayLoc(p: BProc, n: PNode): Rope =
|
||||
var a: TLoc
|
||||
|
||||
let q = skipConv(n)
|
||||
|
|
@ -90,33 +98,34 @@ proc openArrayLoc(p: BProc, n: PNode): PRope =
|
|||
of tyOpenArray, tyVarargs, tyArray, tyArrayConstr:
|
||||
"($1)+($2), ($3)-($2)+1"
|
||||
of tyString, tySequence:
|
||||
if skipTypes(n.typ, abstractInst).kind == tyVar:
|
||||
if skipTypes(n.typ, abstractInst).kind == tyVar and
|
||||
not compileToCpp(p.module):
|
||||
"(*$1)->data+($2), ($3)-($2)+1"
|
||||
else:
|
||||
"$1->data+($2), ($3)-($2)+1"
|
||||
else: (internalError("openArrayLoc: " & typeToString(a.t)); "")
|
||||
result = ropef(fmt, [rdLoc(a), rdLoc(b), rdLoc(c)])
|
||||
result = fmt % [rdLoc(a), rdLoc(b), rdLoc(c)]
|
||||
else:
|
||||
initLocExpr(p, n, a)
|
||||
case skipTypes(a.t, abstractVar).kind
|
||||
of tyOpenArray, tyVarargs:
|
||||
result = ropef("$1, $1Len0", [rdLoc(a)])
|
||||
result = "$1, $1Len0" % [rdLoc(a)]
|
||||
of tyString, tySequence:
|
||||
if skipTypes(n.typ, abstractInst).kind == tyVar:
|
||||
result = ropef("(*$1)->data, (*$1)->$2", [a.rdLoc, lenField(p)])
|
||||
if skipTypes(n.typ, abstractInst).kind == tyVar and
|
||||
not compileToCpp(p.module):
|
||||
result = "(*$1)->data, (*$1)->$2" % [a.rdLoc, lenField(p)]
|
||||
else:
|
||||
result = ropef("$1->data, $1->$2", [a.rdLoc, lenField(p)])
|
||||
result = "$1->data, $1->$2" % [a.rdLoc, lenField(p)]
|
||||
of tyArray, tyArrayConstr:
|
||||
result = ropef("$1, $2", [rdLoc(a), toRope(lengthOrd(a.t))])
|
||||
result = "$1, $2" % [rdLoc(a), rope(lengthOrd(a.t))]
|
||||
else: internalError("openArrayLoc: " & typeToString(a.t))
|
||||
|
||||
proc genArgStringToCString(p: BProc,
|
||||
n: PNode): PRope {.inline.} =
|
||||
proc genArgStringToCString(p: BProc, n: PNode): Rope {.inline.} =
|
||||
var a: TLoc
|
||||
initLocExpr(p, n.sons[0], a)
|
||||
result = ropef("$1->data", [a.rdLoc])
|
||||
result = "$1->data" % [a.rdLoc]
|
||||
|
||||
proc genArg(p: BProc, n: PNode, param: PSym): PRope =
|
||||
proc genArg(p: BProc, n: PNode, param: PSym; call: PNode): Rope =
|
||||
var a: TLoc
|
||||
if n.kind == nkStringToCString:
|
||||
result = genArgStringToCString(p, n)
|
||||
|
|
@ -126,23 +135,35 @@ proc genArg(p: BProc, n: PNode, param: PSym): PRope =
|
|||
elif ccgIntroducedPtr(param):
|
||||
initLocExpr(p, n, a)
|
||||
result = addrLoc(a)
|
||||
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
|
||||
{lfHeader, lfNoDecl} * callee.sym.loc.flags != {}:
|
||||
result = addrLoc(a)
|
||||
else:
|
||||
result = rdLoc(a)
|
||||
else:
|
||||
initLocExpr(p, n, a)
|
||||
initLocExprSingleUse(p, n, a)
|
||||
result = rdLoc(a)
|
||||
|
||||
proc genArgNoParam(p: BProc, n: PNode): PRope =
|
||||
proc genArgNoParam(p: BProc, n: PNode): Rope =
|
||||
var a: TLoc
|
||||
if n.kind == nkStringToCString:
|
||||
result = genArgStringToCString(p, n)
|
||||
else:
|
||||
initLocExpr(p, n, a)
|
||||
initLocExprSingleUse(p, n, a)
|
||||
result = rdLoc(a)
|
||||
|
||||
proc genPrefixCall(p: BProc, le, ri: PNode, d: var TLoc) =
|
||||
var op: TLoc
|
||||
# this is a hotspot in the compiler
|
||||
initLocExpr(p, ri.sons[0], op)
|
||||
var params: PRope
|
||||
var params: Rope
|
||||
# getUniqueType() is too expensive here:
|
||||
var typ = skipTypes(ri.sons[0].typ, abstractInst)
|
||||
assert(typ.kind == tyProc)
|
||||
|
|
@ -150,48 +171,49 @@ proc genPrefixCall(p: BProc, le, ri: PNode, d: var TLoc) =
|
|||
var length = sonsLen(ri)
|
||||
for i in countup(1, length - 1):
|
||||
if ri.sons[i].typ.isCompileTimeOnly: continue
|
||||
if params != nil: app(params, ~", ")
|
||||
if params != nil: add(params, ~", ")
|
||||
if i < sonsLen(typ):
|
||||
assert(typ.n.sons[i].kind == nkSym)
|
||||
app(params, genArg(p, ri.sons[i], typ.n.sons[i].sym))
|
||||
add(params, genArg(p, ri.sons[i], typ.n.sons[i].sym, ri))
|
||||
else:
|
||||
app(params, genArgNoParam(p, ri.sons[i]))
|
||||
add(params, genArgNoParam(p, ri.sons[i]))
|
||||
fixupCall(p, le, ri, d, op.r, params)
|
||||
|
||||
proc genClosureCall(p: BProc, le, ri: PNode, d: var TLoc) =
|
||||
|
||||
proc getRawProcType(p: BProc, t: PType): PRope =
|
||||
proc getRawProcType(p: BProc, t: PType): Rope =
|
||||
result = getClosureType(p.module, t, clHalf)
|
||||
|
||||
proc addComma(r: PRope): PRope =
|
||||
result = if r == nil: r else: con(r, ~", ")
|
||||
proc addComma(r: Rope): Rope =
|
||||
result = if r == nil: r else: r & ~", "
|
||||
|
||||
const PatProc = "$1.ClEnv? $1.ClPrc($3$1.ClEnv):(($4)($1.ClPrc))($2)"
|
||||
const PatIter = "$1.ClPrc($3$1.ClEnv)" # we know the env exists
|
||||
var op: TLoc
|
||||
initLocExpr(p, ri.sons[0], op)
|
||||
var pl: PRope
|
||||
var pl: Rope
|
||||
|
||||
var typ = skipTypes(ri.sons[0].typ, abstractInst)
|
||||
assert(typ.kind == tyProc)
|
||||
var length = sonsLen(ri)
|
||||
for i in countup(1, length - 1):
|
||||
assert(sonsLen(typ) == sonsLen(typ.n))
|
||||
if ri.sons[i].typ.isCompileTimeOnly: continue
|
||||
if i < sonsLen(typ):
|
||||
assert(typ.n.sons[i].kind == nkSym)
|
||||
app(pl, genArg(p, ri.sons[i], typ.n.sons[i].sym))
|
||||
add(pl, genArg(p, ri.sons[i], typ.n.sons[i].sym, ri))
|
||||
else:
|
||||
app(pl, genArgNoParam(p, ri.sons[i]))
|
||||
if i < length - 1: app(pl, ~", ")
|
||||
add(pl, genArgNoParam(p, ri.sons[i]))
|
||||
if i < length - 1: add(pl, ~", ")
|
||||
|
||||
template genCallPattern {.dirty.} =
|
||||
lineF(p, cpsStmts, callPattern & ";$n", op.r, pl, pl.addComma, rawProc)
|
||||
lineF(p, cpsStmts, callPattern & ";$n", [op.r, pl, pl.addComma, rawProc])
|
||||
|
||||
let rawProc = getRawProcType(p, typ)
|
||||
let callPattern = if tfIterator in typ.flags: PatIter else: PatProc
|
||||
if typ.sons[0] != nil:
|
||||
if isInvalidReturnType(typ.sons[0]):
|
||||
if sonsLen(ri) > 1: app(pl, ~", ")
|
||||
if sonsLen(ri) > 1: add(pl, ~", ")
|
||||
# beware of 'result = p(result)'. We may need to allocate a temporary:
|
||||
if d.k in {locTemp, locNone} or not leftAppearsOnRightSide(le, ri):
|
||||
# Great, we can use 'd':
|
||||
|
|
@ -200,12 +222,12 @@ proc genClosureCall(p: BProc, le, ri: PNode, d: var TLoc) =
|
|||
elif d.k notin {locExpr, locTemp} and not hasNoInit(ri):
|
||||
# reset before pass as 'result' var:
|
||||
resetLoc(p, d)
|
||||
app(pl, addrLoc(d))
|
||||
add(pl, addrLoc(d))
|
||||
genCallPattern()
|
||||
else:
|
||||
var tmp: TLoc
|
||||
getTemp(p, typ.sons[0], tmp, needsInit=true)
|
||||
app(pl, addrLoc(tmp))
|
||||
add(pl, addrLoc(tmp))
|
||||
genCallPattern()
|
||||
genAssignment(p, d, tmp, {}) # no need for deep copying
|
||||
else:
|
||||
|
|
@ -213,37 +235,214 @@ proc genClosureCall(p: BProc, le, ri: PNode, d: var TLoc) =
|
|||
assert(d.t != nil) # generate an assignment to d:
|
||||
var list: TLoc
|
||||
initLoc(list, locCall, d.t, OnUnknown)
|
||||
list.r = ropef(callPattern, op.r, pl, pl.addComma, rawProc)
|
||||
list.r = callPattern % [op.r, pl, pl.addComma, rawProc]
|
||||
genAssignment(p, d, list, {}) # no need for deep copying
|
||||
else:
|
||||
genCallPattern()
|
||||
|
||||
proc genOtherArg(p: BProc; ri: PNode; i: int; typ: PType): Rope =
|
||||
if ri.sons[i].typ.isCompileTimeOnly:
|
||||
result = nil
|
||||
elif i < sonsLen(typ):
|
||||
# 'var T' is 'T&' in C++. This means we ignore the request of
|
||||
# any nkHiddenAddr when it's a 'var T'.
|
||||
assert(typ.n.sons[i].kind == nkSym)
|
||||
if typ.sons[i].kind == tyVar and ri.sons[i].kind == nkHiddenAddr:
|
||||
result = genArgNoParam(p, ri.sons[i][0])
|
||||
else:
|
||||
result = genArgNoParam(p, ri.sons[i]) #, typ.n.sons[i].sym)
|
||||
else:
|
||||
result = genArgNoParam(p, ri.sons[i])
|
||||
|
||||
discard """
|
||||
Dot call syntax in C++
|
||||
======================
|
||||
|
||||
so c2nim translates 'this' sometimes to 'T' and sometimes to 'var T'
|
||||
both of which are wrong, but often more convenient to use.
|
||||
For manual wrappers it can also be 'ptr T'
|
||||
|
||||
Fortunately we know which parameter is the 'this' parameter and so can fix this
|
||||
mess in the codegen.
|
||||
now ... if the *argument* is a 'ptr' the codegen shall emit -> and otherwise .
|
||||
but this only depends on the argument and not on how the 'this' was declared
|
||||
however how the 'this' was declared affects whether we end up with
|
||||
wrong 'addr' and '[]' ops...
|
||||
|
||||
Since I'm tired I'll enumerate all the cases here:
|
||||
|
||||
var
|
||||
x: ptr T
|
||||
y: T
|
||||
|
||||
proc t(x: T)
|
||||
x[].t() --> (*x).t() is correct.
|
||||
y.t() --> y.t() is correct
|
||||
|
||||
proc u(x: ptr T)
|
||||
x.u() --> needs to become x->u()
|
||||
(addr y).u() --> needs to become y.u()
|
||||
|
||||
proc v(x: var T)
|
||||
--> first skip the implicit 'nkAddr' node
|
||||
x[].v() --> (*x).v() is correct, but might have been eliminated due
|
||||
to the nkAddr node! So for this case we need to generate '->'
|
||||
y.v() --> y.v() is correct
|
||||
|
||||
"""
|
||||
|
||||
proc skipAddrDeref(node: PNode): PNode =
|
||||
var n = node
|
||||
var isAddr = false
|
||||
case n.kind
|
||||
of nkAddr, nkHiddenAddr:
|
||||
n = n.sons[0]
|
||||
isAddr = true
|
||||
of nkDerefExpr, nkHiddenDeref:
|
||||
n = n.sons[0]
|
||||
else: return n
|
||||
if n.kind == nkObjDownConv: n = n.sons[0]
|
||||
if isAddr and n.kind in {nkDerefExpr, nkHiddenDeref}:
|
||||
result = n.sons[0]
|
||||
elif n.kind in {nkAddr, nkHiddenAddr}:
|
||||
result = n.sons[0]
|
||||
else:
|
||||
result = node
|
||||
|
||||
proc genThisArg(p: BProc; ri: PNode; i: int; typ: PType): Rope =
|
||||
# for better or worse c2nim translates the 'this' argument to a 'var T'.
|
||||
# However manual wrappers may also use 'ptr T'. In any case we support both
|
||||
# for convenience.
|
||||
internalAssert i < sonsLen(typ)
|
||||
assert(typ.n.sons[i].kind == nkSym)
|
||||
# if the parameter is lying (tyVar) and thus we required an additional deref,
|
||||
# skip the deref:
|
||||
var ri = ri[i]
|
||||
while ri.kind == nkObjDownConv: ri = ri[0]
|
||||
let t = typ.sons[i].skipTypes({tyGenericInst})
|
||||
if t.kind == tyVar:
|
||||
let x = if ri.kind == nkHiddenAddr: ri[0] else: ri
|
||||
if x.typ.kind == tyPtr:
|
||||
result = genArgNoParam(p, x)
|
||||
result.add("->")
|
||||
elif x.kind in {nkHiddenDeref, nkDerefExpr} and x[0].typ.kind == tyPtr:
|
||||
result = genArgNoParam(p, x[0])
|
||||
result.add("->")
|
||||
else:
|
||||
result = genArgNoParam(p, x)
|
||||
result.add(".")
|
||||
elif t.kind == tyPtr:
|
||||
if ri.kind in {nkAddr, nkHiddenAddr}:
|
||||
result = genArgNoParam(p, ri[0])
|
||||
result.add(".")
|
||||
else:
|
||||
result = genArgNoParam(p, ri)
|
||||
result.add("->")
|
||||
else:
|
||||
ri = skipAddrDeref(ri)
|
||||
if ri.kind in {nkAddr, nkHiddenAddr}: ri = ri[0]
|
||||
result = genArgNoParam(p, ri) #, typ.n.sons[i].sym)
|
||||
result.add(".")
|
||||
|
||||
proc genPatternCall(p: BProc; ri: PNode; pat: string; typ: PType): Rope =
|
||||
var i = 0
|
||||
var j = 1
|
||||
while i < pat.len:
|
||||
case pat[i]
|
||||
of '@':
|
||||
if j < ri.len:
|
||||
result.add genOtherArg(p, ri, j, typ)
|
||||
for k in j+1 .. < ri.len:
|
||||
result.add(~", ")
|
||||
result.add genOtherArg(p, ri, k, typ)
|
||||
inc i
|
||||
of '#':
|
||||
if pat[i+1] in {'+', '@'}:
|
||||
let ri = ri[j]
|
||||
if ri.kind in nkCallKinds:
|
||||
let typ = skipTypes(ri.sons[0].typ, abstractInst)
|
||||
if pat[i+1] == '+': result.add genArgNoParam(p, ri.sons[0])
|
||||
result.add(~"(")
|
||||
if 1 < ri.len:
|
||||
result.add genOtherArg(p, ri, 1, typ)
|
||||
for k in j+1 .. < ri.len:
|
||||
result.add(~", ")
|
||||
result.add genOtherArg(p, ri, k, typ)
|
||||
result.add(~")")
|
||||
else:
|
||||
localError(ri.info, "call expression expected for C++ pattern")
|
||||
inc i
|
||||
elif pat[i+1] == '.':
|
||||
result.add genThisArg(p, ri, j, typ)
|
||||
inc i
|
||||
elif pat[i+1] == '[':
|
||||
var arg = ri.sons[j].skipAddrDeref
|
||||
while arg.kind in {nkAddr, nkHiddenAddr, nkObjDownConv}: arg = arg[0]
|
||||
result.add genArgNoParam(p, arg)
|
||||
#result.add debugTree(arg, 0, 10)
|
||||
else:
|
||||
result.add genOtherArg(p, ri, j, typ)
|
||||
inc j
|
||||
inc i
|
||||
of '\'':
|
||||
var idx, stars: int
|
||||
if scanCppGenericSlot(pat, i, idx, stars):
|
||||
var t = resolveStarsInCppType(typ, idx, stars)
|
||||
if t == nil: result.add(~"void")
|
||||
else: result.add(getTypeDesc(p.module, t))
|
||||
else:
|
||||
let start = i
|
||||
while i < pat.len:
|
||||
if pat[i] notin {'@', '#', '\''}: inc(i)
|
||||
else: break
|
||||
if i - 1 >= start:
|
||||
add(result, substr(pat, start, i - 1))
|
||||
|
||||
proc genInfixCall(p: BProc, le, ri: PNode, d: var TLoc) =
|
||||
var op, a: TLoc
|
||||
initLocExpr(p, ri.sons[0], op)
|
||||
var pl: PRope = nil
|
||||
# getUniqueType() is too expensive here:
|
||||
var typ = skipTypes(ri.sons[0].typ, abstractInst)
|
||||
assert(typ.kind == tyProc)
|
||||
var length = sonsLen(ri)
|
||||
assert(sonsLen(typ) == sonsLen(typ.n))
|
||||
|
||||
var param = typ.n.sons[1].sym
|
||||
app(pl, genArg(p, ri.sons[1], param))
|
||||
|
||||
if skipTypes(param.typ, {tyGenericInst}).kind == tyPtr: app(pl, ~"->")
|
||||
else: app(pl, ~".")
|
||||
app(pl, op.r)
|
||||
var params: PRope
|
||||
for i in countup(2, length - 1):
|
||||
if params != nil: params.app(~", ")
|
||||
assert(sonsLen(typ) == sonsLen(typ.n))
|
||||
if i < sonsLen(typ):
|
||||
assert(typ.n.sons[i].kind == nkSym)
|
||||
app(params, genArg(p, ri.sons[i], typ.n.sons[i].sym))
|
||||
# don't call '$' here for efficiency:
|
||||
let pat = ri.sons[0].sym.loc.r.data
|
||||
internalAssert pat != nil
|
||||
if pat.contains({'#', '(', '@', '\''}):
|
||||
var pl = genPatternCall(p, ri, pat, typ)
|
||||
# simpler version of 'fixupCall' that works with the pl+params combination:
|
||||
var typ = skipTypes(ri.sons[0].typ, abstractInst)
|
||||
if typ.sons[0] != nil:
|
||||
if p.module.compileToCpp and lfSingleUse in d.flags:
|
||||
# do not generate spurious temporaries for C++! For C we're better off
|
||||
# with them to prevent undefined behaviour and because the codegen
|
||||
# is free to emit expressions multiple times!
|
||||
d.k = locCall
|
||||
d.r = pl
|
||||
excl d.flags, lfSingleUse
|
||||
else:
|
||||
if d.k == locNone: getTemp(p, typ.sons[0], d)
|
||||
assert(d.t != nil) # generate an assignment to d:
|
||||
var list: TLoc
|
||||
initLoc(list, locCall, d.t, OnUnknown)
|
||||
list.r = pl
|
||||
genAssignment(p, d, list, {}) # no need for deep copying
|
||||
else:
|
||||
app(params, genArgNoParam(p, ri.sons[i]))
|
||||
fixupCall(p, le, ri, d, pl, params)
|
||||
add(pl, ~";$n")
|
||||
line(p, cpsStmts, pl)
|
||||
else:
|
||||
var pl: Rope = nil
|
||||
#var param = typ.n.sons[1].sym
|
||||
if 1 < ri.len:
|
||||
add(pl, genThisArg(p, ri, 1, typ))
|
||||
add(pl, op.r)
|
||||
var params: Rope
|
||||
for i in countup(2, length - 1):
|
||||
if params != nil: params.add(~", ")
|
||||
assert(sonsLen(typ) == sonsLen(typ.n))
|
||||
add(params, genOtherArg(p, ri, i, typ))
|
||||
fixupCall(p, le, ri, d, pl, params)
|
||||
|
||||
proc genNamedParamCall(p: BProc, ri: PNode, d: var TLoc) =
|
||||
# generates a crappy ObjC call
|
||||
|
|
@ -256,43 +455,55 @@ proc genNamedParamCall(p: BProc, ri: PNode, d: var TLoc) =
|
|||
var length = sonsLen(ri)
|
||||
assert(sonsLen(typ) == sonsLen(typ.n))
|
||||
|
||||
if length > 1:
|
||||
app(pl, genArg(p, ri.sons[1], typ.n.sons[1].sym))
|
||||
app(pl, ~" ")
|
||||
app(pl, op.r)
|
||||
if length > 2:
|
||||
app(pl, ~": ")
|
||||
app(pl, genArg(p, ri.sons[2], typ.n.sons[2].sym))
|
||||
for i in countup(3, length-1):
|
||||
# don't call '$' here for efficiency:
|
||||
let pat = ri.sons[0].sym.loc.r.data
|
||||
internalAssert pat != nil
|
||||
var start = 3
|
||||
if ' ' in pat:
|
||||
start = 1
|
||||
add(pl, op.r)
|
||||
if length > 1:
|
||||
add(pl, ~": ")
|
||||
add(pl, genArg(p, ri.sons[1], typ.n.sons[1].sym, ri))
|
||||
start = 2
|
||||
else:
|
||||
if length > 1:
|
||||
add(pl, genArg(p, ri.sons[1], typ.n.sons[1].sym, ri))
|
||||
add(pl, ~" ")
|
||||
add(pl, op.r)
|
||||
if length > 2:
|
||||
add(pl, ~": ")
|
||||
add(pl, genArg(p, ri.sons[2], typ.n.sons[2].sym, ri))
|
||||
for i in countup(start, length-1):
|
||||
assert(sonsLen(typ) == sonsLen(typ.n))
|
||||
if i >= sonsLen(typ):
|
||||
internalError(ri.info, "varargs for objective C method?")
|
||||
assert(typ.n.sons[i].kind == nkSym)
|
||||
var param = typ.n.sons[i].sym
|
||||
app(pl, ~" ")
|
||||
app(pl, param.name.s)
|
||||
app(pl, ~": ")
|
||||
app(pl, genArg(p, ri.sons[i], param))
|
||||
add(pl, ~" ")
|
||||
add(pl, param.name.s)
|
||||
add(pl, ~": ")
|
||||
add(pl, genArg(p, ri.sons[i], param, ri))
|
||||
if typ.sons[0] != nil:
|
||||
if isInvalidReturnType(typ.sons[0]):
|
||||
if sonsLen(ri) > 1: app(pl, ~" ")
|
||||
if sonsLen(ri) > 1: add(pl, ~" ")
|
||||
# beware of 'result = p(result)'. We always allocate a temporary:
|
||||
if d.k in {locTemp, locNone}:
|
||||
# We already got a temp. Great, special case it:
|
||||
if d.k == locNone: getTemp(p, typ.sons[0], d, needsInit=true)
|
||||
app(pl, ~"Result: ")
|
||||
app(pl, addrLoc(d))
|
||||
app(pl, ~"];$n")
|
||||
add(pl, ~"Result: ")
|
||||
add(pl, addrLoc(d))
|
||||
add(pl, ~"];$n")
|
||||
line(p, cpsStmts, pl)
|
||||
else:
|
||||
var tmp: TLoc
|
||||
getTemp(p, typ.sons[0], tmp, needsInit=true)
|
||||
app(pl, addrLoc(tmp))
|
||||
app(pl, ~"];$n")
|
||||
add(pl, addrLoc(tmp))
|
||||
add(pl, ~"];$n")
|
||||
line(p, cpsStmts, pl)
|
||||
genAssignment(p, d, tmp, {}) # no need for deep copying
|
||||
else:
|
||||
app(pl, ~"]")
|
||||
add(pl, ~"]")
|
||||
if d.k == locNone: getTemp(p, typ.sons[0], d)
|
||||
assert(d.t != nil) # generate an assignment to d:
|
||||
var list: TLoc
|
||||
|
|
@ -300,14 +511,13 @@ proc genNamedParamCall(p: BProc, ri: PNode, d: var TLoc) =
|
|||
list.r = pl
|
||||
genAssignment(p, d, list, {}) # no need for deep copying
|
||||
else:
|
||||
app(pl, ~"];$n")
|
||||
add(pl, ~"];$n")
|
||||
line(p, cpsStmts, pl)
|
||||
|
||||
proc genCall(p: BProc, e: PNode, d: var TLoc) =
|
||||
if e.sons[0].typ.callConv == ccClosure:
|
||||
genClosureCall(p, nil, e, d)
|
||||
elif e.sons[0].kind == nkSym and sfInfixCall in e.sons[0].sym.flags and
|
||||
e.len >= 2:
|
||||
elif e.sons[0].kind == nkSym and sfInfixCall in e.sons[0].sym.flags:
|
||||
genInfixCall(p, nil, e, d)
|
||||
elif e.sons[0].kind == nkSym and sfNamedParamCall in e.sons[0].sym.flags:
|
||||
genNamedParamCall(p, e, d)
|
||||
|
|
@ -320,8 +530,7 @@ proc genCall(p: BProc, e: PNode, d: var TLoc) =
|
|||
proc genAsgnCall(p: BProc, le, ri: PNode, d: var TLoc) =
|
||||
if ri.sons[0].typ.callConv == ccClosure:
|
||||
genClosureCall(p, le, ri, d)
|
||||
elif ri.sons[0].kind == nkSym and sfInfixCall in ri.sons[0].sym.flags and
|
||||
ri.len >= 2:
|
||||
elif ri.sons[0].kind == nkSym and sfInfixCall in ri.sons[0].sym.flags:
|
||||
genInfixCall(p, le, ri, d)
|
||||
elif ri.sons[0].kind == nkSym and sfNamedParamCall in ri.sons[0].sym.flags:
|
||||
genNamedParamCall(p, ri, d)
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -45,29 +45,29 @@ const
|
|||
]
|
||||
NimMergeEndMark = "/*\tNIM_merge_END:*/"
|
||||
|
||||
proc genSectionStart*(fs: TCFileSection): PRope =
|
||||
proc genSectionStart*(fs: TCFileSection): Rope =
|
||||
if compilationCachePresent:
|
||||
result = toRope(tnl)
|
||||
app(result, "/*\t")
|
||||
app(result, CFileSectionNames[fs])
|
||||
app(result, ":*/")
|
||||
app(result, tnl)
|
||||
result = rope(tnl)
|
||||
add(result, "/*\t")
|
||||
add(result, CFileSectionNames[fs])
|
||||
add(result, ":*/")
|
||||
add(result, tnl)
|
||||
|
||||
proc genSectionEnd*(fs: TCFileSection): PRope =
|
||||
proc genSectionEnd*(fs: TCFileSection): Rope =
|
||||
if compilationCachePresent:
|
||||
result = toRope(NimMergeEndMark & tnl)
|
||||
result = rope(NimMergeEndMark & tnl)
|
||||
|
||||
proc genSectionStart*(ps: TCProcSection): PRope =
|
||||
proc genSectionStart*(ps: TCProcSection): Rope =
|
||||
if compilationCachePresent:
|
||||
result = toRope(tnl)
|
||||
app(result, "/*\t")
|
||||
app(result, CProcSectionNames[ps])
|
||||
app(result, ":*/")
|
||||
app(result, tnl)
|
||||
result = rope(tnl)
|
||||
add(result, "/*\t")
|
||||
add(result, CProcSectionNames[ps])
|
||||
add(result, ":*/")
|
||||
add(result, tnl)
|
||||
|
||||
proc genSectionEnd*(ps: TCProcSection): PRope =
|
||||
proc genSectionEnd*(ps: TCProcSection): Rope =
|
||||
if compilationCachePresent:
|
||||
result = toRope(NimMergeEndMark & tnl)
|
||||
result = rope(NimMergeEndMark & tnl)
|
||||
|
||||
proc writeTypeCache(a: TIdTable, s: var string) =
|
||||
var i = 0
|
||||
|
|
@ -79,7 +79,7 @@ proc writeTypeCache(a: TIdTable, s: var string) =
|
|||
s.add(' ')
|
||||
encodeVInt(id, s)
|
||||
s.add(':')
|
||||
encodeStr(PRope(value).ropeToStr, s)
|
||||
encodeStr($Rope(value), s)
|
||||
inc i
|
||||
s.add('}')
|
||||
|
||||
|
|
@ -95,7 +95,7 @@ proc writeIntSet(a: IntSet, s: var string) =
|
|||
inc i
|
||||
s.add('}')
|
||||
|
||||
proc genMergeInfo*(m: BModule): PRope =
|
||||
proc genMergeInfo*(m: BModule): Rope =
|
||||
if optSymbolFiles notin gGlobalOptions: return nil
|
||||
var s = "/*\tNIM_merge_INFO:"
|
||||
s.add(tnl)
|
||||
|
|
@ -111,9 +111,9 @@ proc genMergeInfo*(m: BModule): PRope =
|
|||
encodeVInt(ord(m.frameDeclared), s)
|
||||
s.add(tnl)
|
||||
s.add("*/")
|
||||
result = s.toRope
|
||||
result = s.rope
|
||||
|
||||
template `^`(pos: expr): expr = L.buf[pos]
|
||||
template `^`(pos: int): expr = L.buf[pos]
|
||||
|
||||
proc skipWhite(L: var TBaseLexer) =
|
||||
var pos = L.bufpos
|
||||
|
|
@ -145,7 +145,7 @@ proc atEndMark(buf: cstring, pos: int): bool =
|
|||
while s < NimMergeEndMark.len and buf[pos+s] == NimMergeEndMark[s]: inc s
|
||||
result = s == NimMergeEndMark.len
|
||||
|
||||
proc readVerbatimSection(L: var TBaseLexer): PRope =
|
||||
proc readVerbatimSection(L: var TBaseLexer): Rope =
|
||||
var pos = L.bufpos
|
||||
var buf = L.buf
|
||||
var r = newStringOfCap(30_000)
|
||||
|
|
@ -169,7 +169,7 @@ proc readVerbatimSection(L: var TBaseLexer): PRope =
|
|||
r.add(buf[pos])
|
||||
inc pos
|
||||
L.bufpos = pos
|
||||
result = r.toRope
|
||||
result = r.rope
|
||||
|
||||
proc readKey(L: var TBaseLexer, result: var string) =
|
||||
var pos = L.bufpos
|
||||
|
|
@ -197,7 +197,7 @@ proc readTypeCache(L: var TBaseLexer, result: var TIdTable) =
|
|||
# XXX little hack: we create a "fake" type object with the correct Id
|
||||
# better would be to adapt the data structure to not even store the
|
||||
# object as key, but only the Id
|
||||
idTablePut(result, newFakeType(key), value.toRope)
|
||||
idTablePut(result, newFakeType(key), value.rope)
|
||||
inc L.bufpos
|
||||
|
||||
proc readIntSet(L: var TBaseLexer, result: var IntSet) =
|
||||
|
|
@ -223,7 +223,7 @@ proc processMergeInfo(L: var TBaseLexer, m: BModule) =
|
|||
of "typeInfo": readIntSet(L, m.typeInfoMarker)
|
||||
of "labels": m.labels = decodeVInt(L.buf, L.bufpos)
|
||||
of "hasframe": m.frameDeclared = decodeVInt(L.buf, L.bufpos) != 0
|
||||
else: internalError("ccgmerge: unkown key: " & k)
|
||||
else: internalError("ccgmerge: unknown key: " & k)
|
||||
|
||||
when not defined(nimhygiene):
|
||||
{.pragma: inject.}
|
||||
|
|
@ -280,11 +280,11 @@ proc readMergeSections(cfilename: string, m: var TMergeSections) =
|
|||
proc mergeRequired*(m: BModule): bool =
|
||||
for i in cfsHeaders..cfsProcs:
|
||||
if m.s[i] != nil:
|
||||
#echo "not empty: ", i, " ", ropeToStr(m.s[i])
|
||||
#echo "not empty: ", i, " ", m.s[i]
|
||||
return true
|
||||
for i in low(TCProcSection)..high(TCProcSection):
|
||||
if m.initProc.s(i) != nil:
|
||||
#echo "not empty: ", i, " ", ropeToStr(m.initProc.s[i])
|
||||
#echo "not empty: ", i, " ", m.initProc.s[i]
|
||||
return true
|
||||
|
||||
proc mergeFiles*(cfilename: string, m: BModule) =
|
||||
|
|
@ -293,6 +293,6 @@ proc mergeFiles*(cfilename: string, m: BModule) =
|
|||
readMergeSections(cfilename, old)
|
||||
# do the merge; old section before new section:
|
||||
for i in low(TCFileSection)..high(TCFileSection):
|
||||
m.s[i] = con(old.f[i], m.s[i])
|
||||
m.s[i] = old.f[i] & m.s[i]
|
||||
for i in low(TCProcSection)..high(TCProcSection):
|
||||
m.initProc.s(i) = con(old.p[i], m.initProc.s(i))
|
||||
m.initProc.s(i) = old.p[i] & m.initProc.s(i)
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
#
|
||||
#
|
||||
# The Nim Compiler
|
||||
# (c) Copyright 2014 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 @@ proc genVarTuple(p: BProc, n: PNode) =
|
|||
return
|
||||
genLineDir(p, n)
|
||||
initLocExpr(p, n.sons[L-1], tup)
|
||||
var t = tup.t
|
||||
var t = tup.t.getUniqueType
|
||||
for i in countup(0, L-3):
|
||||
var v = n.sons[i].sym
|
||||
if sfCompileTime in v.flags: continue
|
||||
|
|
@ -61,11 +61,10 @@ proc genVarTuple(p: BProc, n: PNode) =
|
|||
initLocalVar(p, v, immediateAsgn=isAssignedImmediately(n[L-1]))
|
||||
initLoc(field, locExpr, t.sons[i], tup.s)
|
||||
if t.kind == tyTuple:
|
||||
field.r = ropef("$1.Field$2", [rdLoc(tup), toRope(i)])
|
||||
field.r = "$1.Field$2" % [rdLoc(tup), rope(i)]
|
||||
else:
|
||||
if t.n.sons[i].kind != nkSym: internalError(n.info, "genVarTuple")
|
||||
field.r = ropef("$1.$2",
|
||||
[rdLoc(tup), mangleRecFieldName(t.n.sons[i].sym, t)])
|
||||
field.r = "$1.$2" % [rdLoc(tup), mangleRecFieldName(t.n.sons[i].sym, t)]
|
||||
putLocIntoDest(p, v.loc, field)
|
||||
|
||||
proc genDeref(p: BProc, e: PNode, d: var TLoc; enforceDeref=false)
|
||||
|
|
@ -86,8 +85,8 @@ proc loadInto(p: BProc, le, ri: PNode, a: var TLoc) {.inline.} =
|
|||
else:
|
||||
expr(p, ri, a)
|
||||
|
||||
proc startBlock(p: BProc, start: TFormatStr = "{$n",
|
||||
args: varargs[PRope]): int {.discardable.} =
|
||||
proc startBlock(p: BProc, start: FormatStr = "{$n",
|
||||
args: varargs[Rope]): int {.discardable.} =
|
||||
lineCg(p, cpsStmts, start, args)
|
||||
inc(p.labels)
|
||||
result = len(p.blocks)
|
||||
|
|
@ -96,21 +95,21 @@ proc startBlock(p: BProc, start: TFormatStr = "{$n",
|
|||
p.blocks[result].nestedTryStmts = p.nestedTryStmts.len.int16
|
||||
p.blocks[result].nestedExceptStmts = p.inExceptBlock.int16
|
||||
|
||||
proc assignLabel(b: var TBlock): PRope {.inline.} =
|
||||
b.label = con("LA", b.id.toRope)
|
||||
proc assignLabel(b: var TBlock): Rope {.inline.} =
|
||||
b.label = "LA" & b.id.rope
|
||||
result = b.label
|
||||
|
||||
proc blockBody(b: var TBlock): PRope =
|
||||
proc blockBody(b: var TBlock): Rope =
|
||||
result = b.sections[cpsLocals]
|
||||
if b.frameLen > 0:
|
||||
result.appf("F.len+=$1;$n", b.frameLen.toRope)
|
||||
result.app(b.sections[cpsInit])
|
||||
result.app(b.sections[cpsStmts])
|
||||
result.addf("F.len+=$1;$n", [b.frameLen.rope])
|
||||
result.add(b.sections[cpsInit])
|
||||
result.add(b.sections[cpsStmts])
|
||||
|
||||
proc endBlock(p: BProc, blockEnd: PRope) =
|
||||
proc endBlock(p: BProc, blockEnd: Rope) =
|
||||
let topBlock = p.blocks.len-1
|
||||
# the block is merged into the parent block
|
||||
app(p.blocks[topBlock-1].sections[cpsStmts], p.blocks[topBlock].blockBody)
|
||||
add(p.blocks[topBlock-1].sections[cpsStmts], p.blocks[topBlock].blockBody)
|
||||
setLen(p.blocks, topBlock)
|
||||
# this is done after the block is popped so $n is
|
||||
# properly indented when pretty printing is enabled
|
||||
|
|
@ -124,7 +123,7 @@ proc endBlock(p: BProc) =
|
|||
~"}$n"
|
||||
let frameLen = p.blocks[topBlock].frameLen
|
||||
if frameLen > 0:
|
||||
blockEnd.appf("F.len-=$1;$n", frameLen.toRope)
|
||||
blockEnd.addf("F.len-=$1;$n", [frameLen.rope])
|
||||
endBlock(p, blockEnd)
|
||||
|
||||
proc genSimpleBlock(p: BProc, stmts: PNode) {.inline.} =
|
||||
|
|
@ -145,7 +144,7 @@ template preserveBreakIdx(body: stmt): stmt {.immediate.} =
|
|||
proc genState(p: BProc, n: PNode) =
|
||||
internalAssert n.len == 1 and n.sons[0].kind == nkIntLit
|
||||
let idx = n.sons[0].intVal
|
||||
linefmt(p, cpsStmts, "STATE$1: ;$n", idx.toRope)
|
||||
linefmt(p, cpsStmts, "STATE$1: ;$n", idx.rope)
|
||||
|
||||
proc genGotoState(p: BProc, n: PNode) =
|
||||
# we resist the temptation to translate it into duff's device as it later
|
||||
|
|
@ -159,7 +158,7 @@ proc genGotoState(p: BProc, n: PNode) =
|
|||
p.beforeRetNeeded = true
|
||||
lineF(p, cpsStmts, "case -1: goto BeforeRet;$n", [])
|
||||
for i in 0 .. lastOrd(n.sons[0].typ):
|
||||
lineF(p, cpsStmts, "case $1: goto STATE$1;$n", [toRope(i)])
|
||||
lineF(p, cpsStmts, "case $1: goto STATE$1;$n", [rope(i)])
|
||||
lineF(p, cpsStmts, "}$n", [])
|
||||
|
||||
proc genBreakState(p: BProc, n: PNode) =
|
||||
|
|
@ -176,9 +175,18 @@ proc genBreakState(p: BProc, n: PNode) =
|
|||
|
||||
proc genVarPrototypeAux(m: BModule, sym: PSym)
|
||||
|
||||
proc genGotoVar(p: BProc; value: PNode) =
|
||||
if value.kind notin {nkCharLit..nkUInt64Lit}:
|
||||
localError(value.info, "'goto' target must be a literal value")
|
||||
else:
|
||||
lineF(p, cpsStmts, "goto NIMSTATE_$#;$n", [value.intVal.rope])
|
||||
|
||||
proc genSingleVar(p: BProc, a: PNode) =
|
||||
var v = a.sons[0].sym
|
||||
if sfCompileTime in v.flags: return
|
||||
if {sfCompileTime, sfGoto} * v.flags != {}:
|
||||
# translate 'var state {.goto.} = X' into 'goto LX':
|
||||
if sfGoto in v.flags: genGotoVar(p, a.sons[2])
|
||||
return
|
||||
var targetProc = p
|
||||
if sfGlobal in v.flags:
|
||||
if v.flags * {sfImportc, sfExportc} == {sfImportc} and
|
||||
|
|
@ -202,8 +210,32 @@ proc genSingleVar(p: BProc, a: PNode) =
|
|||
genVarPrototypeAux(generatedHeader, v)
|
||||
registerGcRoot(p, v)
|
||||
else:
|
||||
let value = a.sons[2]
|
||||
let imm = isAssignedImmediately(value)
|
||||
if imm and p.module.compileToCpp and p.splitDecls == 0 and
|
||||
not containsHiddenPointer(v.typ):
|
||||
# C++ really doesn't like things like 'Foo f; f = x' as that invokes a
|
||||
# parameterless constructor followed by an assignment operator. So we
|
||||
# generate better code here:
|
||||
genLineDir(p, a)
|
||||
let decl = localVarDecl(p, v)
|
||||
var tmp: TLoc
|
||||
if value.kind in nkCallKinds and value[0].kind == nkSym and
|
||||
sfConstructor in value[0].sym.flags:
|
||||
var params: Rope
|
||||
let typ = skipTypes(value.sons[0].typ, abstractInst)
|
||||
assert(typ.kind == tyProc)
|
||||
for i in 1.. <value.len:
|
||||
if params != nil: params.add(~", ")
|
||||
assert(sonsLen(typ) == sonsLen(typ.n))
|
||||
add(params, genOtherArg(p, value, i, typ))
|
||||
lineF(p, cpsStmts, "$#($#);$n", [decl, params])
|
||||
else:
|
||||
initLocExprSingleUse(p, value, tmp)
|
||||
lineF(p, cpsStmts, "$# = $#;$n", [decl, tmp.rdLoc])
|
||||
return
|
||||
assignLocalVar(p, v)
|
||||
initLocalVar(p, v, isAssignedImmediately(a.sons[2]))
|
||||
initLocalVar(p, v, imm)
|
||||
|
||||
if a.sons[2].kind != nkEmpty:
|
||||
genLineDir(targetProc, a)
|
||||
|
|
@ -242,15 +274,6 @@ proc genConstStmt(p: BProc, t: PNode) =
|
|||
elif c.typ.kind in ConstantDataTypes and lfNoDecl notin c.loc.flags and
|
||||
c.ast.len != 0:
|
||||
if not emitLazily(c): requestConstImpl(p, c)
|
||||
when false:
|
||||
# generate the data:
|
||||
fillLoc(c.loc, locData, c.typ, mangleName(c), OnUnknown)
|
||||
if sfImportc in c.flags:
|
||||
appf(p.module.s[cfsData], "extern NIM_CONST $1 $2;$n",
|
||||
[getTypeDesc(p.module, c.typ), c.loc.r])
|
||||
else:
|
||||
appf(p.module.s[cfsData], "NIM_CONST $1 $2 = $3;$n",
|
||||
[getTypeDesc(p.module, c.typ), c.loc.r, genConstExpr(p, c.ast)])
|
||||
|
||||
proc genIf(p: BProc, n: PNode, d: var TLoc) =
|
||||
#
|
||||
|
|
@ -275,17 +298,22 @@ proc genIf(p: BProc, n: PNode, d: var TLoc) =
|
|||
let it = n.sons[i]
|
||||
if it.len == 2:
|
||||
when newScopeForIf: startBlock(p)
|
||||
initLocExpr(p, it.sons[0], a)
|
||||
initLocExprSingleUse(p, it.sons[0], a)
|
||||
lelse = getLabel(p)
|
||||
inc(p.labels)
|
||||
lineFF(p, cpsStmts, "if (!$1) goto $2;$n",
|
||||
"br i1 $1, label %LOC$3, label %$2$nLOC$3: $n",
|
||||
[rdLoc(a), lelse, toRope(p.labels)])
|
||||
lineF(p, cpsStmts, "if (!$1) goto $2;$n",
|
||||
[rdLoc(a), lelse])
|
||||
when not newScopeForIf: startBlock(p)
|
||||
expr(p, it.sons[1], d)
|
||||
if p.module.compileToCpp:
|
||||
# avoid "jump to label crosses initialization" error:
|
||||
add(p.s(cpsStmts), "{")
|
||||
expr(p, it.sons[1], d)
|
||||
add(p.s(cpsStmts), "}")
|
||||
else:
|
||||
expr(p, it.sons[1], d)
|
||||
endBlock(p)
|
||||
if sonsLen(n) > 1:
|
||||
lineFF(p, cpsStmts, "goto $1;$n", "br label %$1$n", [lend])
|
||||
lineF(p, cpsStmts, "goto $1;$n", [lend])
|
||||
fixLabel(p, lelse)
|
||||
elif it.len == 1:
|
||||
startBlock(p)
|
||||
|
|
@ -344,7 +372,20 @@ proc genReturnStmt(p: BProc, t: PNode) =
|
|||
# consume it before we return.
|
||||
var safePoint = p.finallySafePoints[p.finallySafePoints.len-1]
|
||||
linefmt(p, cpsStmts, "if ($1.status != 0) #popCurrentException();$n", safePoint)
|
||||
lineFF(p, cpsStmts, "goto BeforeRet;$n", "br label %BeforeRet$n", [])
|
||||
lineF(p, cpsStmts, "goto BeforeRet;$n", [])
|
||||
|
||||
proc genGotoForCase(p: BProc; caseStmt: PNode) =
|
||||
for i in 1 .. <caseStmt.len:
|
||||
startBlock(p)
|
||||
let it = caseStmt.sons[i]
|
||||
for j in 0 .. it.len-2:
|
||||
if it.sons[j].kind == nkRange:
|
||||
localError(it.info, "range notation not available for computed goto")
|
||||
return
|
||||
let val = getOrdValue(it.sons[j])
|
||||
lineF(p, cpsStmts, "NIMSTATE_$#:$n", [val.rope])
|
||||
genStmts(p, it.lastSon)
|
||||
endBlock(p)
|
||||
|
||||
proc genComputedGoto(p: BProc; n: PNode) =
|
||||
# first pass: Generate array of computed labels:
|
||||
|
|
@ -369,11 +410,11 @@ proc genComputedGoto(p: BProc; n: PNode) =
|
|||
localError(n.info, "no case statement found for computed goto"); return
|
||||
var id = p.labels+1
|
||||
inc p.labels, arraySize+1
|
||||
let tmp = ropef("TMP$1", id.toRope)
|
||||
var gotoArray = ropef("static void* $#[$#] = {", tmp, arraySize.toRope)
|
||||
let tmp = "TMP$1" % [id.rope]
|
||||
var gotoArray = "static void* $#[$#] = {" % [tmp, arraySize.rope]
|
||||
for i in 1..arraySize-1:
|
||||
gotoArray.appf("&&TMP$#, ", (id+i).toRope)
|
||||
gotoArray.appf("&&TMP$#};$n", (id+arraySize).toRope)
|
||||
gotoArray.addf("&&TMP$#, ", [(id+i).rope])
|
||||
gotoArray.addf("&&TMP$#};$n", [(id+arraySize).rope])
|
||||
line(p, cpsLocals, gotoArray)
|
||||
|
||||
let topBlock = p.blocks.len-1
|
||||
|
|
@ -387,13 +428,13 @@ proc genComputedGoto(p: BProc; n: PNode) =
|
|||
for j in 0 .. casePos-1: genStmts(p, n.sons[j])
|
||||
let tailA = p.blocks[topBlock].sections[cpsStmts]
|
||||
|
||||
p.blocks[topBlock].sections[cpsStmts] = oldBody.con(tailA)
|
||||
p.blocks[topBlock].sections[cpsStmts] = oldBody & tailA
|
||||
|
||||
let caseStmt = n.sons[casePos]
|
||||
var a: TLoc
|
||||
initLocExpr(p, caseStmt.sons[0], a)
|
||||
# first goto:
|
||||
lineF(p, cpsStmts, "goto *$#[$#];$n", tmp, a.rdLoc)
|
||||
lineF(p, cpsStmts, "goto *$#[$#];$n", [tmp, a.rdLoc])
|
||||
|
||||
for i in 1 .. <caseStmt.len:
|
||||
startBlock(p)
|
||||
|
|
@ -403,16 +444,16 @@ proc genComputedGoto(p: BProc; n: PNode) =
|
|||
localError(it.info, "range notation not available for computed goto")
|
||||
return
|
||||
let val = getOrdValue(it.sons[j])
|
||||
lineF(p, cpsStmts, "TMP$#:$n", intLiteral(val+id+1))
|
||||
lineF(p, cpsStmts, "TMP$#:$n", [intLiteral(val+id+1)])
|
||||
genStmts(p, it.lastSon)
|
||||
#for j in casePos+1 .. <n.len: genStmts(p, n.sons[j]) # tailB
|
||||
#for j in 0 .. casePos-1: genStmts(p, n.sons[j]) # tailA
|
||||
app(p.s(cpsStmts), tailB)
|
||||
app(p.s(cpsStmts), tailA)
|
||||
add(p.s(cpsStmts), tailB)
|
||||
add(p.s(cpsStmts), tailA)
|
||||
|
||||
var a: TLoc
|
||||
initLocExpr(p, caseStmt.sons[0], a)
|
||||
lineF(p, cpsStmts, "goto *$#[$#];$n", tmp, a.rdLoc)
|
||||
lineF(p, cpsStmts, "goto *$#[$#];$n", [tmp, a.rdLoc])
|
||||
endBlock(p)
|
||||
|
||||
proc genWhileStmt(p: BProc, t: PNode) =
|
||||
|
|
@ -478,9 +519,9 @@ proc genParForStmt(p: BProc, t: PNode) =
|
|||
|
||||
lineF(p, cpsStmts, "#pragma omp parallel for $4$n" &
|
||||
"for ($1 = $2; $1 <= $3; ++$1)",
|
||||
forLoopVar.loc.rdLoc,
|
||||
[forLoopVar.loc.rdLoc,
|
||||
rangeA.rdLoc, rangeB.rdLoc,
|
||||
call.sons[3].getStr.toRope)
|
||||
call.sons[3].getStr.rope])
|
||||
|
||||
p.breakIdx = startBlock(p)
|
||||
p.blocks[p.breakIdx].isLoop = true
|
||||
|
|
@ -510,12 +551,7 @@ proc genBreakStmt(p: BProc, t: PNode) =
|
|||
lineF(p, cpsStmts, "goto $1;$n", [label])
|
||||
|
||||
proc getRaiseFrmt(p: BProc): string =
|
||||
if p.module.compileToCpp:
|
||||
result = "throw NimException($1, $2);$n"
|
||||
elif getCompilerProc("Exception") != nil:
|
||||
result = "#raiseException((#Exception*)$1, $2);$n"
|
||||
else:
|
||||
result = "#raiseException((#E_Base*)$1, $2);$n"
|
||||
result = "#raiseException((#Exception*)$1, $2);$n"
|
||||
|
||||
proc genRaiseStmt(p: BProc, t: PNode) =
|
||||
if p.inExceptBlock > 0:
|
||||
|
|
@ -540,7 +576,7 @@ proc genRaiseStmt(p: BProc, t: PNode) =
|
|||
linefmt(p, cpsStmts, "#reraiseException();$n")
|
||||
|
||||
proc genCaseGenericBranch(p: BProc, b: PNode, e: TLoc,
|
||||
rangeFormat, eqFormat: TFormatStr, labl: TLabel) =
|
||||
rangeFormat, eqFormat: FormatStr, labl: TLabel) =
|
||||
var
|
||||
x, y: TLoc
|
||||
var length = sonsLen(b)
|
||||
|
|
@ -558,7 +594,7 @@ proc genCaseSecondPass(p: BProc, t: PNode, d: var TLoc,
|
|||
labId, until: int): TLabel =
|
||||
var lend = getLabel(p)
|
||||
for i in 1..until:
|
||||
lineF(p, cpsStmts, "LA$1: ;$n", [toRope(labId + i)])
|
||||
lineF(p, cpsStmts, "LA$1: ;$n", [rope(labId + i)])
|
||||
if t.sons[i].kind == nkOfBranch:
|
||||
var length = sonsLen(t.sons[i])
|
||||
exprBlock(p, t.sons[i].sons[length - 1], d)
|
||||
|
|
@ -568,7 +604,7 @@ proc genCaseSecondPass(p: BProc, t: PNode, d: var TLoc,
|
|||
result = lend
|
||||
|
||||
proc genIfForCaseUntil(p: BProc, t: PNode, d: var TLoc,
|
||||
rangeFormat, eqFormat: TFormatStr,
|
||||
rangeFormat, eqFormat: FormatStr,
|
||||
until: int, a: TLoc): TLabel =
|
||||
# generate a C-if statement for a Nim case statement
|
||||
var labId = p.labels
|
||||
|
|
@ -576,27 +612,27 @@ proc genIfForCaseUntil(p: BProc, t: PNode, d: var TLoc,
|
|||
inc(p.labels)
|
||||
if t.sons[i].kind == nkOfBranch: # else statement
|
||||
genCaseGenericBranch(p, t.sons[i], a, rangeFormat, eqFormat,
|
||||
con("LA", toRope(p.labels)))
|
||||
"LA" & rope(p.labels))
|
||||
else:
|
||||
lineF(p, cpsStmts, "goto LA$1;$n", [toRope(p.labels)])
|
||||
lineF(p, cpsStmts, "goto LA$1;$n", [rope(p.labels)])
|
||||
if until < t.len-1:
|
||||
inc(p.labels)
|
||||
var gotoTarget = p.labels
|
||||
lineF(p, cpsStmts, "goto LA$1;$n", [toRope(gotoTarget)])
|
||||
lineF(p, cpsStmts, "goto LA$1;$n", [rope(gotoTarget)])
|
||||
result = genCaseSecondPass(p, t, d, labId, until)
|
||||
lineF(p, cpsStmts, "LA$1: ;$n", [toRope(gotoTarget)])
|
||||
lineF(p, cpsStmts, "LA$1: ;$n", [rope(gotoTarget)])
|
||||
else:
|
||||
result = genCaseSecondPass(p, t, d, labId, until)
|
||||
|
||||
proc genCaseGeneric(p: BProc, t: PNode, d: var TLoc,
|
||||
rangeFormat, eqFormat: TFormatStr) =
|
||||
rangeFormat, eqFormat: FormatStr) =
|
||||
var a: TLoc
|
||||
initLocExpr(p, t.sons[0], a)
|
||||
var lend = genIfForCaseUntil(p, t, d, rangeFormat, eqFormat, sonsLen(t)-1, a)
|
||||
fixLabel(p, lend)
|
||||
|
||||
proc genCaseStringBranch(p: BProc, b: PNode, e: TLoc, labl: TLabel,
|
||||
branches: var openArray[PRope]) =
|
||||
branches: var openArray[Rope]) =
|
||||
var x: TLoc
|
||||
var length = sonsLen(b)
|
||||
for i in countup(0, length - 2):
|
||||
|
|
@ -614,7 +650,7 @@ proc genStringCase(p: BProc, t: PNode, d: var TLoc) =
|
|||
if t.sons[i].kind == nkOfBranch: inc(strings, sonsLen(t.sons[i]) - 1)
|
||||
if strings > stringCaseThreshold:
|
||||
var bitMask = math.nextPowerOfTwo(strings) - 1
|
||||
var branches: seq[PRope]
|
||||
var branches: seq[Rope]
|
||||
newSeq(branches, bitMask + 1)
|
||||
var a: TLoc
|
||||
initLocExpr(p, t.sons[0], a) # fist pass: gnerate ifs+goto:
|
||||
|
|
@ -622,21 +658,21 @@ proc genStringCase(p: BProc, t: PNode, d: var TLoc) =
|
|||
for i in countup(1, sonsLen(t) - 1):
|
||||
inc(p.labels)
|
||||
if t.sons[i].kind == nkOfBranch:
|
||||
genCaseStringBranch(p, t.sons[i], a, con("LA", toRope(p.labels)),
|
||||
genCaseStringBranch(p, t.sons[i], a, "LA" & rope(p.labels),
|
||||
branches)
|
||||
else:
|
||||
# else statement: nothing to do yet
|
||||
# but we reserved a label, which we use later
|
||||
discard
|
||||
linefmt(p, cpsStmts, "switch (#hashString($1) & $2) {$n",
|
||||
rdLoc(a), toRope(bitMask))
|
||||
rdLoc(a), rope(bitMask))
|
||||
for j in countup(0, high(branches)):
|
||||
if branches[j] != nil:
|
||||
lineF(p, cpsStmts, "case $1: $n$2break;$n",
|
||||
[intLiteral(j), branches[j]])
|
||||
lineF(p, cpsStmts, "}$n") # else statement:
|
||||
lineF(p, cpsStmts, "}$n", []) # else statement:
|
||||
if t.sons[sonsLen(t)-1].kind != nkOfBranch:
|
||||
lineF(p, cpsStmts, "goto LA$1;$n", [toRope(p.labels)])
|
||||
lineF(p, cpsStmts, "goto LA$1;$n", [rope(p.labels)])
|
||||
# third pass: generate statements
|
||||
var lend = genCaseSecondPass(p, t, d, labId, sonsLen(t)-1)
|
||||
fixLabel(p, lend)
|
||||
|
|
@ -698,13 +734,13 @@ proc genOrdinalCase(p: BProc, n: PNode, d: var TLoc) =
|
|||
genCaseRange(p, branch)
|
||||
else:
|
||||
# else part of case statement:
|
||||
lineF(p, cpsStmts, "default:$n")
|
||||
lineF(p, cpsStmts, "default:$n", [])
|
||||
hasDefault = true
|
||||
exprBlock(p, branch.lastSon, d)
|
||||
lineF(p, cpsStmts, "break;$n")
|
||||
lineF(p, cpsStmts, "break;$n", [])
|
||||
if (hasAssume in CC[cCompiler].props) and not hasDefault:
|
||||
lineF(p, cpsStmts, "default: __assume(0);$n")
|
||||
lineF(p, cpsStmts, "}$n")
|
||||
lineF(p, cpsStmts, "default: __assume(0);$n", [])
|
||||
lineF(p, cpsStmts, "}$n", [])
|
||||
if lend != nil: fixLabel(p, lend)
|
||||
|
||||
proc genCase(p: BProc, t: PNode, d: var TLoc) =
|
||||
|
|
@ -718,7 +754,10 @@ proc genCase(p: BProc, t: PNode, d: var TLoc) =
|
|||
genCaseGeneric(p, t, d, "if ($1 >= $2 && $1 <= $3) goto $4;$n",
|
||||
"if ($1 == $2) goto $3;$n")
|
||||
else:
|
||||
genOrdinalCase(p, t, d)
|
||||
if t.sons[0].kind == nkSym and sfGoto in t.sons[0].sym.flags:
|
||||
genGotoForCase(p, t)
|
||||
else:
|
||||
genOrdinalCase(p, t, d)
|
||||
|
||||
proc hasGeneralExceptSection(t: PNode): bool =
|
||||
var length = sonsLen(t)
|
||||
|
|
@ -753,11 +792,8 @@ proc genTryCpp(p: BProc, t: PNode, d: var TLoc) =
|
|||
# finallyPart();
|
||||
if not isEmptyType(t.typ) and d.k == locNone:
|
||||
getTemp(p, t.typ, d)
|
||||
var
|
||||
exc: PRope
|
||||
i, length, blen: int
|
||||
genLineDir(p, t)
|
||||
exc = getTempName()
|
||||
let exc = getTempName()
|
||||
if getCompilerProc("Exception") != nil:
|
||||
discard cgsym(p.module, "Exception")
|
||||
else:
|
||||
|
|
@ -765,36 +801,42 @@ proc genTryCpp(p: BProc, t: PNode, d: var TLoc) =
|
|||
add(p.nestedTryStmts, t)
|
||||
startBlock(p, "try {$n")
|
||||
expr(p, t.sons[0], d)
|
||||
length = sonsLen(t)
|
||||
let length = sonsLen(t)
|
||||
endBlock(p, ropecg(p.module, "} catch (NimException& $1) {$n", [exc]))
|
||||
if optStackTrace in p.options:
|
||||
linefmt(p, cpsStmts, "#setFrame((TFrame*)&F);$n")
|
||||
linefmt(p, cpsStmts, "#setFrame((TFrame*)&FR);$n")
|
||||
inc p.inExceptBlock
|
||||
i = 1
|
||||
var i = 1
|
||||
var catchAllPresent = false
|
||||
while (i < length) and (t.sons[i].kind == nkExceptBranch):
|
||||
blen = sonsLen(t.sons[i])
|
||||
if i > 1: appf(p.s(cpsStmts), "else ")
|
||||
let blen = sonsLen(t.sons[i])
|
||||
if i > 1: addf(p.s(cpsStmts), "else ", [])
|
||||
if blen == 1:
|
||||
# general except section:
|
||||
catchAllPresent = true
|
||||
exprBlock(p, t.sons[i].sons[0], d)
|
||||
startBlock(p)
|
||||
expr(p, t.sons[i].sons[0], d)
|
||||
linefmt(p, cpsStmts, "#popCurrentException();$n")
|
||||
endBlock(p)
|
||||
else:
|
||||
var orExpr: PRope = nil
|
||||
var orExpr: Rope = nil
|
||||
for j in countup(0, blen - 2):
|
||||
assert(t.sons[i].sons[j].kind == nkType)
|
||||
if orExpr != nil: app(orExpr, "||")
|
||||
if orExpr != nil: add(orExpr, "||")
|
||||
appcg(p.module, orExpr,
|
||||
"#isObj($1.exp->m_type, $2)",
|
||||
[exc, genTypeInfo(p.module, t.sons[i].sons[j].typ)])
|
||||
lineF(p, cpsStmts, "if ($1) ", [orExpr])
|
||||
exprBlock(p, t.sons[i].sons[blen-1], d)
|
||||
startBlock(p)
|
||||
expr(p, t.sons[i].sons[blen-1], d)
|
||||
linefmt(p, cpsStmts, "#popCurrentException();$n")
|
||||
endBlock(p)
|
||||
inc(i)
|
||||
|
||||
# reraise the exception if there was no catch all
|
||||
# and none of the handlers matched
|
||||
if not catchAllPresent:
|
||||
if i > 1: lineF(p, cpsStmts, "else ")
|
||||
if i > 1: lineF(p, cpsStmts, "else ", [])
|
||||
startBlock(p)
|
||||
var finallyBlock = t.lastSon
|
||||
if finallyBlock.kind == nkFinally:
|
||||
|
|
@ -804,7 +846,7 @@ proc genTryCpp(p: BProc, t: PNode, d: var TLoc) =
|
|||
line(p, cpsStmts, ~"throw;$n")
|
||||
endBlock(p)
|
||||
|
||||
lineF(p, cpsStmts, "}$n") # end of catch block
|
||||
lineF(p, cpsStmts, "}$n", []) # end of catch block
|
||||
dec p.inExceptBlock
|
||||
|
||||
discard pop(p.nestedTryStmts)
|
||||
|
|
@ -868,24 +910,24 @@ proc genTry(p: BProc, t: PNode, d: var TLoc) =
|
|||
startBlock(p, "else {$n")
|
||||
linefmt(p, cpsStmts, "#popSafePoint();$n")
|
||||
if optStackTrace in p.options:
|
||||
linefmt(p, cpsStmts, "#setFrame((TFrame*)&F);$n")
|
||||
linefmt(p, cpsStmts, "#setFrame((TFrame*)&FR);$n")
|
||||
inc p.inExceptBlock
|
||||
var i = 1
|
||||
while (i < length) and (t.sons[i].kind == nkExceptBranch):
|
||||
var blen = sonsLen(t.sons[i])
|
||||
if blen == 1:
|
||||
# general except section:
|
||||
if i > 1: lineF(p, cpsStmts, "else")
|
||||
if i > 1: lineF(p, cpsStmts, "else", [])
|
||||
startBlock(p)
|
||||
linefmt(p, cpsStmts, "$1.status = 0;$n", safePoint)
|
||||
expr(p, t.sons[i].sons[0], d)
|
||||
linefmt(p, cpsStmts, "#popCurrentException();$n")
|
||||
endBlock(p)
|
||||
else:
|
||||
var orExpr: PRope = nil
|
||||
var orExpr: Rope = nil
|
||||
for j in countup(0, blen - 2):
|
||||
assert(t.sons[i].sons[j].kind == nkType)
|
||||
if orExpr != nil: app(orExpr, "||")
|
||||
if orExpr != nil: add(orExpr, "||")
|
||||
appcg(p.module, orExpr,
|
||||
"#isObj(#getCurrentException()->Sup.m_type, $1)",
|
||||
[genTypeInfo(p.module, t.sons[i].sons[j].typ)])
|
||||
|
|
@ -905,7 +947,7 @@ proc genTry(p: BProc, t: PNode, d: var TLoc) =
|
|||
discard pop(p.finallySafePoints)
|
||||
linefmt(p, cpsStmts, "if ($1.status != 0) #reraiseException();$n", safePoint)
|
||||
|
||||
proc genAsmOrEmitStmt(p: BProc, t: PNode, isAsmStmt=false): PRope =
|
||||
proc genAsmOrEmitStmt(p: BProc, t: PNode, isAsmStmt=false): Rope =
|
||||
var res = ""
|
||||
for i in countup(0, sonsLen(t) - 1):
|
||||
case t.sons[i].kind
|
||||
|
|
@ -916,7 +958,7 @@ proc genAsmOrEmitStmt(p: BProc, t: PNode, isAsmStmt=false): PRope =
|
|||
if sym.kind in {skProc, skIterator, skClosureIterator, skMethod}:
|
||||
var a: TLoc
|
||||
initLocExpr(p, t.sons[i], a)
|
||||
res.add(rdLoc(a).ropeToStr)
|
||||
res.add($rdLoc(a))
|
||||
else:
|
||||
var r = sym.loc.r
|
||||
if r == nil:
|
||||
|
|
@ -924,47 +966,59 @@ proc genAsmOrEmitStmt(p: BProc, t: PNode, isAsmStmt=false): PRope =
|
|||
# it doesn't matter much:
|
||||
r = mangleName(sym)
|
||||
sym.loc.r = r # but be consequent!
|
||||
res.add(r.ropeToStr)
|
||||
res.add($r)
|
||||
else: internalError(t.sons[i].info, "genAsmOrEmitStmt()")
|
||||
|
||||
if isAsmStmt and hasGnuAsm in CC[cCompiler].props:
|
||||
for x in splitLines(res):
|
||||
var j = 0
|
||||
while x[j] in {' ', '\t'}: inc(j)
|
||||
if x[j] == ':' and x[j+1] == '"' or x[j] == '"':
|
||||
if x[j] in {'"', ':'}:
|
||||
# don't modify the line if already in quotes or
|
||||
# some clobber register list:
|
||||
app(result, x); app(result, tnl)
|
||||
add(result, x); add(result, tnl)
|
||||
elif x[j] != '\0':
|
||||
# ignore empty lines
|
||||
app(result, "\"")
|
||||
app(result, x)
|
||||
app(result, "\\n\"\n")
|
||||
add(result, "\"")
|
||||
add(result, x)
|
||||
add(result, "\\n\"\n")
|
||||
else:
|
||||
res.add(tnl)
|
||||
result = res.toRope
|
||||
result = res.rope
|
||||
|
||||
proc genAsmStmt(p: BProc, t: PNode) =
|
||||
assert(t.kind == nkAsmStmt)
|
||||
genLineDir(p, t)
|
||||
var s = genAsmOrEmitStmt(p, t, isAsmStmt=true)
|
||||
# see bug #2362, "top level asm statements" seem to be a mis-feature
|
||||
# but even if we don't do this, the example in #2362 cannot possibly
|
||||
# work:
|
||||
if p.prc == nil:
|
||||
# top level asm statement?
|
||||
appf(p.module.s[cfsProcHeaders], CC[cCompiler].asmStmtFrmt, [s])
|
||||
addf(p.module.s[cfsProcHeaders], CC[cCompiler].asmStmtFrmt, [s])
|
||||
else:
|
||||
lineF(p, cpsStmts, CC[cCompiler].asmStmtFrmt, [s])
|
||||
|
||||
proc determineSection(n: PNode): TCFileSection =
|
||||
result = cfsProcHeaders
|
||||
if n.len >= 1 and n.sons[0].kind in {nkStrLit..nkTripleStrLit}:
|
||||
if n.sons[0].strVal.startsWith("/*TYPESECTION*/"): result = cfsTypes
|
||||
elif n.sons[0].strVal.startsWith("/*VARSECTION*/"): result = cfsVars
|
||||
|
||||
proc genEmit(p: BProc, t: PNode) =
|
||||
genLineDir(p, t)
|
||||
var s = genAsmOrEmitStmt(p, t.sons[1])
|
||||
if p.prc == nil:
|
||||
# top level emit pragma?
|
||||
app(p.module.s[cfsProcHeaders], s)
|
||||
let section = determineSection(t[1])
|
||||
genCLineDir(p.module.s[section], t.info)
|
||||
add(p.module.s[section], s)
|
||||
else:
|
||||
genLineDir(p, t)
|
||||
line(p, cpsStmts, s)
|
||||
|
||||
var
|
||||
breakPointId: int = 0
|
||||
gBreakpoints: PRope # later the breakpoints are inserted into the main proc
|
||||
gBreakpoints: Rope # later the breakpoints are inserted into the main proc
|
||||
|
||||
proc genBreakPoint(p: BProc, t: PNode) =
|
||||
var name: string
|
||||
|
|
@ -978,7 +1032,7 @@ proc genBreakPoint(p: BProc, t: PNode) =
|
|||
genLineDir(p, t) # BUGFIX
|
||||
appcg(p.module, gBreakpoints,
|
||||
"#dbgRegisterBreakpoint($1, (NCSTRING)$2, (NCSTRING)$3);$n", [
|
||||
toRope(toLinenumber(t.info)), makeCString(toFilename(t.info)),
|
||||
rope(toLinenumber(t.info)), makeCString(toFilename(t.info)),
|
||||
makeCString(name)])
|
||||
|
||||
proc genWatchpoint(p: BProc, n: PNode) =
|
||||
|
|
@ -1041,7 +1095,9 @@ proc asgnFieldDiscriminant(p: BProc, e: PNode) =
|
|||
|
||||
proc genAsgn(p: BProc, e: PNode, fastAsgn: bool) =
|
||||
genLineDir(p, e)
|
||||
if not fieldDiscriminantCheckNeeded(p, e):
|
||||
if e.sons[0].kind == nkSym and sfGoto in e.sons[0].sym.flags:
|
||||
genGotoVar(p, e.sons[1])
|
||||
elif not fieldDiscriminantCheckNeeded(p, e):
|
||||
var a: TLoc
|
||||
initLocExpr(p, e.sons[0], a)
|
||||
if fastAsgn: incl(a.flags, lfNoDeepCopy)
|
||||
|
|
|
|||
|
|
@ -19,12 +19,12 @@ proc accessThreadLocalVar(p: BProc, s: PSym) =
|
|||
if emulatedThreadVars() and not p.threadVarAccessed:
|
||||
p.threadVarAccessed = true
|
||||
p.module.usesThreadVars = true
|
||||
appf(p.procSec(cpsLocals), "\tNimThreadVars* NimTV;$n")
|
||||
app(p.procSec(cpsInit),
|
||||
addf(p.procSec(cpsLocals), "\tNimThreadVars* NimTV;$n", [])
|
||||
add(p.procSec(cpsInit),
|
||||
ropecg(p.module, "\tNimTV = (NimThreadVars*) #GetThreadLocalVars();$n"))
|
||||
|
||||
var
|
||||
nimtv: PRope # nimrod thread vars; the struct body
|
||||
nimtv: Rope # nimrod thread vars; the struct body
|
||||
nimtvDeps: seq[PType] = @[] # type deps: every module needs whole struct
|
||||
nimtvDeclared = initIntSet() # so that every var/field exists only once
|
||||
# in the struct
|
||||
|
|
@ -43,23 +43,23 @@ proc declareThreadVar(m: BModule, s: PSym, isExtern: bool) =
|
|||
# allocator for it :-(
|
||||
if not containsOrIncl(nimtvDeclared, s.id):
|
||||
nimtvDeps.add(s.loc.t)
|
||||
appf(nimtv, "$1 $2;$n", [getTypeDesc(m, s.loc.t), s.loc.r])
|
||||
addf(nimtv, "$1 $2;$n", [getTypeDesc(m, s.loc.t), s.loc.r])
|
||||
else:
|
||||
if isExtern: app(m.s[cfsVars], "extern ")
|
||||
if optThreads in gGlobalOptions: app(m.s[cfsVars], "NIM_THREADVAR ")
|
||||
app(m.s[cfsVars], getTypeDesc(m, s.loc.t))
|
||||
appf(m.s[cfsVars], " $1;$n", [s.loc.r])
|
||||
if isExtern: add(m.s[cfsVars], "extern ")
|
||||
if optThreads in gGlobalOptions: add(m.s[cfsVars], "NIM_THREADVAR ")
|
||||
add(m.s[cfsVars], getTypeDesc(m, s.loc.t))
|
||||
addf(m.s[cfsVars], " $1;$n", [s.loc.r])
|
||||
|
||||
proc generateThreadLocalStorage(m: BModule) =
|
||||
if nimtv != nil and (m.usesThreadVars or sfMainModule in m.module.flags):
|
||||
for t in items(nimtvDeps): discard getTypeDesc(m, t)
|
||||
appf(m.s[cfsSeqTypes], "typedef struct {$1} NimThreadVars;$n", [nimtv])
|
||||
addf(m.s[cfsSeqTypes], "typedef struct {$1} NimThreadVars;$n", [nimtv])
|
||||
|
||||
proc generateThreadVarsSize(m: BModule) =
|
||||
if nimtv != nil:
|
||||
let externc = if gCmd != cmdCompileToCpp and
|
||||
sfCompileToCpp in m.module.flags: "extern \"C\""
|
||||
else: ""
|
||||
appf(m.s[cfsProcs],
|
||||
addf(m.s[cfsProcs],
|
||||
"$#NI NimThreadVarsSize(){return (NI)sizeof(NimThreadVars);}$n",
|
||||
[externc.toRope])
|
||||
[externc.rope])
|
||||
|
|
|
|||
|
|
@ -17,11 +17,11 @@ type
|
|||
p: BProc
|
||||
visitorFrmt: string
|
||||
|
||||
proc genTraverseProc(c: var TTraversalClosure, accessor: PRope, typ: PType)
|
||||
proc genTraverseProc(c: var TTraversalClosure, accessor: Rope, typ: PType)
|
||||
proc genCaseRange(p: BProc, branch: PNode)
|
||||
proc getTemp(p: BProc, t: PType, result: var TLoc; needsInit=false)
|
||||
|
||||
proc genTraverseProc(c: var TTraversalClosure, accessor: PRope, n: PNode) =
|
||||
proc genTraverseProc(c: var TTraversalClosure, accessor: Rope, n: PNode) =
|
||||
if n == nil: return
|
||||
case n.kind
|
||||
of nkRecList:
|
||||
|
|
@ -31,31 +31,31 @@ proc genTraverseProc(c: var TTraversalClosure, accessor: PRope, n: PNode) =
|
|||
if (n.sons[0].kind != nkSym): internalError(n.info, "genTraverseProc")
|
||||
var p = c.p
|
||||
let disc = n.sons[0].sym
|
||||
lineF(p, cpsStmts, "switch ($1.$2) {$n", accessor, disc.loc.r)
|
||||
lineF(p, cpsStmts, "switch ($1.$2) {$n", [accessor, disc.loc.r])
|
||||
for i in countup(1, sonsLen(n) - 1):
|
||||
let branch = n.sons[i]
|
||||
assert branch.kind in {nkOfBranch, nkElse}
|
||||
if branch.kind == nkOfBranch:
|
||||
genCaseRange(c.p, branch)
|
||||
else:
|
||||
lineF(p, cpsStmts, "default:$n")
|
||||
lineF(p, cpsStmts, "default:$n", [])
|
||||
genTraverseProc(c, accessor, lastSon(branch))
|
||||
lineF(p, cpsStmts, "break;$n")
|
||||
lineF(p, cpsStmts, "} $n")
|
||||
lineF(p, cpsStmts, "break;$n", [])
|
||||
lineF(p, cpsStmts, "} $n", [])
|
||||
of nkSym:
|
||||
let field = n.sym
|
||||
if field.loc.t == nil:
|
||||
internalError(n.info, "genTraverseProc()")
|
||||
genTraverseProc(c, ropef("$1.$2", accessor, field.loc.r), field.loc.t)
|
||||
genTraverseProc(c, "$1.$2" % [accessor, field.loc.r], field.loc.t)
|
||||
else: internalError(n.info, "genTraverseProc()")
|
||||
|
||||
proc parentObj(accessor: PRope; m: BModule): PRope {.inline.} =
|
||||
proc parentObj(accessor: Rope; m: BModule): Rope {.inline.} =
|
||||
if not m.compileToCpp:
|
||||
result = ropef("$1.Sup", accessor)
|
||||
result = "$1.Sup" % [accessor]
|
||||
else:
|
||||
result = accessor
|
||||
|
||||
proc genTraverseProc(c: var TTraversalClosure, accessor: PRope, typ: PType) =
|
||||
proc genTraverseProc(c: var TTraversalClosure, accessor: Rope, typ: PType) =
|
||||
if typ == nil: return
|
||||
var p = c.p
|
||||
case typ.kind
|
||||
|
|
@ -66,9 +66,9 @@ proc genTraverseProc(c: var TTraversalClosure, accessor: PRope, typ: PType) =
|
|||
var i: TLoc
|
||||
getTemp(p, getSysType(tyInt), i)
|
||||
linefmt(p, cpsStmts, "for ($1 = 0; $1 < $2; $1++) {$n",
|
||||
i.r, arraySize.toRope)
|
||||
i.r, arraySize.rope)
|
||||
genTraverseProc(c, rfmt(nil, "$1[$2]", accessor, i.r), typ.sons[1])
|
||||
lineF(p, cpsStmts, "}$n")
|
||||
lineF(p, cpsStmts, "}$n", [])
|
||||
of tyObject:
|
||||
for i in countup(0, sonsLen(typ) - 1):
|
||||
genTraverseProc(c, accessor.parentObj(c.p.module), typ.sons[i])
|
||||
|
|
@ -76,7 +76,7 @@ proc genTraverseProc(c: var TTraversalClosure, accessor: PRope, typ: PType) =
|
|||
of tyTuple:
|
||||
let typ = getUniqueType(typ)
|
||||
for i in countup(0, sonsLen(typ) - 1):
|
||||
genTraverseProc(c, rfmt(nil, "$1.Field$2", accessor, i.toRope), typ.sons[i])
|
||||
genTraverseProc(c, rfmt(nil, "$1.Field$2", accessor, i.rope), typ.sons[i])
|
||||
of tyRef, tyString, tySequence:
|
||||
lineCg(p, cpsStmts, c.visitorFrmt, accessor)
|
||||
of tyProc:
|
||||
|
|
@ -85,17 +85,17 @@ proc genTraverseProc(c: var TTraversalClosure, accessor: PRope, typ: PType) =
|
|||
else:
|
||||
discard
|
||||
|
||||
proc genTraverseProcSeq(c: var TTraversalClosure, accessor: PRope, typ: PType) =
|
||||
proc genTraverseProcSeq(c: var TTraversalClosure, accessor: Rope, typ: PType) =
|
||||
var p = c.p
|
||||
assert typ.kind == tySequence
|
||||
var i: TLoc
|
||||
getTemp(p, getSysType(tyInt), i)
|
||||
lineF(p, cpsStmts, "for ($1 = 0; $1 < $2->$3; $1++) {$n",
|
||||
i.r, accessor, toRope(if c.p.module.compileToCpp: "len" else: "Sup.len"))
|
||||
genTraverseProc(c, ropef("$1->data[$2]", accessor, i.r), typ.sons[0])
|
||||
lineF(p, cpsStmts, "}$n")
|
||||
[i.r, accessor, rope(if c.p.module.compileToCpp: "len" else: "Sup.len")])
|
||||
genTraverseProc(c, "$1->data[$2]" % [accessor, i.r], typ.sons[0])
|
||||
lineF(p, cpsStmts, "}$n", [])
|
||||
|
||||
proc genTraverseProc(m: BModule, typ: PType, reason: TTypeInfoReason): PRope =
|
||||
proc genTraverseProc(m: BModule, typ: PType, reason: TTypeInfoReason): Rope =
|
||||
var c: TTraversalClosure
|
||||
var p = newProc(nil, m)
|
||||
result = getGlobalTempName()
|
||||
|
|
@ -104,30 +104,30 @@ proc genTraverseProc(m: BModule, typ: PType, reason: TTypeInfoReason): PRope =
|
|||
of tiNew: c.visitorFrmt = "#nimGCvisit((void*)$1, op);$n"
|
||||
else: assert false
|
||||
|
||||
let header = ropef("N_NIMCALL(void, $1)(void* p, NI op)", result)
|
||||
let header = "N_NIMCALL(void, $1)(void* p, NI op)" % [result]
|
||||
|
||||
let t = getTypeDesc(m, typ)
|
||||
lineF(p, cpsLocals, "$1 a;$n", t)
|
||||
lineF(p, cpsInit, "a = ($1)p;$n", t)
|
||||
lineF(p, cpsLocals, "$1 a;$n", [t])
|
||||
lineF(p, cpsInit, "a = ($1)p;$n", [t])
|
||||
|
||||
c.p = p
|
||||
assert typ.kind != tyTypeDesc
|
||||
if typ.kind == tySequence:
|
||||
genTraverseProcSeq(c, "a".toRope, typ)
|
||||
genTraverseProcSeq(c, "a".rope, typ)
|
||||
else:
|
||||
if skipTypes(typ.sons[0], typedescInst).kind in {tyArrayConstr, tyArray}:
|
||||
# C's arrays are broken beyond repair:
|
||||
genTraverseProc(c, "a".toRope, typ.sons[0])
|
||||
genTraverseProc(c, "a".rope, typ.sons[0])
|
||||
else:
|
||||
genTraverseProc(c, "(*a)".toRope, typ.sons[0])
|
||||
genTraverseProc(c, "(*a)".rope, typ.sons[0])
|
||||
|
||||
let generatedProc = ropef("$1 {$n$2$3$4}$n",
|
||||
[header, p.s(cpsLocals), p.s(cpsInit), p.s(cpsStmts)])
|
||||
let generatedProc = "$1 {$n$2$3$4}$n" %
|
||||
[header, p.s(cpsLocals), p.s(cpsInit), p.s(cpsStmts)]
|
||||
|
||||
m.s[cfsProcHeaders].appf("$1;$n", header)
|
||||
m.s[cfsProcs].app(generatedProc)
|
||||
m.s[cfsProcHeaders].addf("$1;$n", [header])
|
||||
m.s[cfsProcs].add(generatedProc)
|
||||
|
||||
proc genTraverseProcForGlobal(m: BModule, s: PSym): PRope =
|
||||
proc genTraverseProcForGlobal(m: BModule, s: PSym): Rope =
|
||||
discard genTypeInfo(m, s.loc.t)
|
||||
|
||||
var c: TTraversalClosure
|
||||
|
|
@ -137,15 +137,15 @@ proc genTraverseProcForGlobal(m: BModule, s: PSym): PRope =
|
|||
|
||||
if sfThread in s.flags and emulatedThreadVars():
|
||||
accessThreadLocalVar(p, s)
|
||||
sLoc = con("NimTV->", sLoc)
|
||||
sLoc = "NimTV->" & sLoc
|
||||
|
||||
c.visitorFrmt = "#nimGCvisit((void*)$1, 0);$n"
|
||||
c.p = p
|
||||
let header = ropef("N_NIMCALL(void, $1)()", result)
|
||||
let header = "N_NIMCALL(void, $1)()" % [result]
|
||||
genTraverseProc(c, sLoc, s.loc.t)
|
||||
|
||||
let generatedProc = ropef("$1 {$n$2$3$4}$n",
|
||||
[header, p.s(cpsLocals), p.s(cpsInit), p.s(cpsStmts)])
|
||||
let generatedProc = "$1 {$n$2$3$4}$n" %
|
||||
[header, p.s(cpsLocals), p.s(cpsInit), p.s(cpsStmts)]
|
||||
|
||||
m.s[cfsProcHeaders].appf("$1;$n", header)
|
||||
m.s[cfsProcs].app(generatedProc)
|
||||
m.s[cfsProcHeaders].addf("$1;$n", [header])
|
||||
m.s[cfsProcs].add(generatedProc)
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -70,13 +70,25 @@ when false:
|
|||
for i in countup(low(TTypeKind), high(TTypeKind)):
|
||||
echo i, " ", gTypeTable[i].counter
|
||||
|
||||
proc slowSearch(key: PType; k: TTypeKind): PType =
|
||||
# tuples are quite horrible as C does not support them directly and
|
||||
# tuple[string, string] is a (strange) subtype of
|
||||
# tuple[nameA, nameB: string]. This bites us here, so we
|
||||
# use 'sameBackendType' instead of 'sameType'.
|
||||
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):
|
||||
return t
|
||||
idTablePut(gTypeTable[k], key, key)
|
||||
result = key
|
||||
|
||||
proc getUniqueType*(key: PType): PType =
|
||||
# this is a hotspot in the compiler!
|
||||
if key == nil: return
|
||||
var k = key.kind
|
||||
case k
|
||||
of tyBool, tyChar,
|
||||
tyInt..tyUInt64:
|
||||
of tyBool, tyChar, tyInt..tyUInt64:
|
||||
# no canonicalization for integral types, so that e.g. ``pid_t`` is
|
||||
# produced instead of ``NI``.
|
||||
result = key
|
||||
|
|
@ -86,9 +98,8 @@ proc getUniqueType*(key: PType): PType =
|
|||
if result == nil:
|
||||
gCanonicalTypes[k] = key
|
||||
result = key
|
||||
of tyTypeDesc, tyTypeClasses, tyGenericParam,
|
||||
tyFromExpr, tyFieldAccessor:
|
||||
internalError("GetUniqueType")
|
||||
of tyTypeDesc, tyTypeClasses, tyGenericParam, tyFromExpr, tyFieldAccessor:
|
||||
internalError("getUniqueType")
|
||||
of tyDistinct:
|
||||
if key.deepCopy != nil: result = key
|
||||
else: result = getUniqueType(lastSon(key))
|
||||
|
|
@ -98,23 +109,20 @@ proc getUniqueType*(key: PType): PType =
|
|||
#if obj.sym != nil and obj.sym.name.s == "TOption":
|
||||
# echo "for ", typeToString(key), " I returned "
|
||||
# debug result
|
||||
of tyArrayConstr, tyGenericInvokation, tyGenericBody,
|
||||
of tyPtr, tyRef, tyVar:
|
||||
let elemType = lastSon(key)
|
||||
if elemType.kind in {tyBool, tyChar, tyInt..tyUInt64}:
|
||||
# no canonicalization for integral types, so that e.g. ``ptr pid_t`` is
|
||||
# produced instead of ``ptr NI``.
|
||||
result = key
|
||||
else:
|
||||
result = slowSearch(key, k)
|
||||
of tyArrayConstr, tyGenericInvocation, tyGenericBody,
|
||||
tyOpenArray, tyArray, tySet, tyRange, tyTuple,
|
||||
tyPtr, tyRef, tySequence, tyForward, tyVarargs, tyProxy, tyVar:
|
||||
# tuples are quite horrible as C does not support them directly and
|
||||
# tuple[string, string] is a (strange) subtype of
|
||||
# tuple[nameA, nameB: string]. This bites us here, so we
|
||||
# use 'sameBackendType' instead of 'sameType'.
|
||||
|
||||
tySequence, tyForward, tyVarargs, tyProxy:
|
||||
# we have to do a slow linear search because types may need
|
||||
# to be compared by their structure:
|
||||
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):
|
||||
return t
|
||||
idTablePut(gTypeTable[k], key, key)
|
||||
result = key
|
||||
result = slowSearch(key, k)
|
||||
of tyObject:
|
||||
if tfFromGeneric notin key.flags:
|
||||
# fast case; lookup per id suffices:
|
||||
|
|
@ -127,7 +135,7 @@ proc getUniqueType*(key: PType): 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 sameType(t, key):
|
||||
if t != nil and sameBackendType(t, key):
|
||||
return t
|
||||
idTablePut(gTypeTable[k], key, key)
|
||||
result = key
|
||||
|
|
@ -141,13 +149,7 @@ proc getUniqueType*(key: PType): PType =
|
|||
result = key
|
||||
else:
|
||||
# ugh, we need the canon here:
|
||||
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):
|
||||
return t
|
||||
idTablePut(gTypeTable[k], key, key)
|
||||
result = key
|
||||
result = slowSearch(key, k)
|
||||
|
||||
proc tableGetType*(tab: TIdTable, key: PType): RootRef =
|
||||
# returns nil if we need to declare this type
|
||||
|
|
@ -174,7 +176,7 @@ proc mangle*(name: string): string =
|
|||
result = newStringOfCap(name.len)
|
||||
case name[0]
|
||||
of Letters:
|
||||
result.add(name[0].toLower)
|
||||
result.add(name[0])
|
||||
of Digits:
|
||||
result.add("N" & name[0])
|
||||
else:
|
||||
|
|
@ -191,13 +193,13 @@ proc mangle*(name: string): string =
|
|||
else:
|
||||
add(result, "HEX" & toHex(ord(c), 2))
|
||||
|
||||
proc makeLLVMString*(s: string): PRope =
|
||||
proc makeLLVMString*(s: string): Rope =
|
||||
const MaxLineLength = 64
|
||||
result = nil
|
||||
var res = "c\""
|
||||
for i in countup(0, len(s) - 1):
|
||||
if (i + 1) mod MaxLineLength == 0:
|
||||
app(result, toRope(res))
|
||||
add(result, rope(res))
|
||||
setLen(res, 0)
|
||||
case s[i]
|
||||
of '\0'..'\x1F', '\x80'..'\xFF', '\"', '\\':
|
||||
|
|
@ -205,6 +207,6 @@ proc makeLLVMString*(s: string): PRope =
|
|||
add(res, toHex(ord(s[i]), 2))
|
||||
else: add(res, s[i])
|
||||
add(res, "\\00\"")
|
||||
app(result, toRope(res))
|
||||
add(result, rope(res))
|
||||
|
||||
initTypeTables()
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -12,8 +12,10 @@
|
|||
import
|
||||
ast, astalgo, ropes, passes, options, intsets, lists, platform
|
||||
|
||||
from msgs import TLineInfo
|
||||
|
||||
type
|
||||
TLabel* = PRope # for the C generator a label is just a rope
|
||||
TLabel* = Rope # for the C generator a label is just a rope
|
||||
TCFileSection* = enum # the sections a generated C file consists of
|
||||
cfsMergeInfo, # section containing merge information
|
||||
cfsHeaders, # section for C include file headers
|
||||
|
|
@ -43,17 +45,17 @@ type
|
|||
ctUInt, ctUInt8, ctUInt16, ctUInt32, ctUInt64,
|
||||
ctArray, ctPtrToArray, ctStruct, ctPtr, ctNimStr, ctNimSeq, ctProc,
|
||||
ctCString
|
||||
TCFileSections* = array[TCFileSection, PRope] # represents a generated C file
|
||||
TCFileSections* = array[TCFileSection, Rope] # represents a generated C file
|
||||
TCProcSection* = enum # the sections a generated C proc consists of
|
||||
cpsLocals, # section of local variables for C proc
|
||||
cpsInit, # section for init of variables for C proc
|
||||
cpsStmts # section of local statements for C proc
|
||||
TCProcSections* = array[TCProcSection, PRope] # represents a generated C proc
|
||||
TCProcSections* = array[TCProcSection, Rope] # represents a generated C proc
|
||||
BModule* = ref TCGen
|
||||
BProc* = ref TCProc
|
||||
TBlock*{.final.} = object
|
||||
id*: int # the ID of the label; positive means that it
|
||||
label*: PRope # generated text for the label
|
||||
label*: Rope # generated text for the label
|
||||
# nil if label is not used
|
||||
sections*: TCProcSections # the code beloging
|
||||
isLoop*: bool # whether block is a loop
|
||||
|
|
@ -65,12 +67,13 @@ type
|
|||
prc*: PSym # the Nim proc that this C proc belongs to
|
||||
beforeRetNeeded*: bool # true iff 'BeforeRet' label for proc is needed
|
||||
threadVarAccessed*: bool # true if the proc already accessed some threadvar
|
||||
lastLineInfo*: TLineInfo # to avoid generating excessive 'nimln' statements
|
||||
nestedTryStmts*: seq[PNode] # in how many nested try statements we are
|
||||
# (the vars must be volatile then)
|
||||
inExceptBlock*: int # are we currently inside an except block?
|
||||
# leaving such scopes by raise or by return must
|
||||
# execute any applicable finally blocks
|
||||
finallySafePoints*: seq[PRope] # For correctly cleaning up exceptions when
|
||||
finallySafePoints*: seq[Rope] # For correctly cleaning up exceptions when
|
||||
# using return in finally statements
|
||||
labels*: Natural # for generating unique labels in the C proc
|
||||
blocks*: seq[TBlock] # nested blocks
|
||||
|
|
@ -82,8 +85,11 @@ type
|
|||
maxFrameLen*: int # max length of frame descriptor
|
||||
module*: BModule # used to prevent excessive parameter passing
|
||||
withinLoop*: int # > 0 if we are within a loop
|
||||
splitDecls*: int # > 0 if we are in some context for C++ that
|
||||
# requires 'T x = T()' to become 'T x; x = T()'
|
||||
# (yes, C++ is weird like that)
|
||||
gcFrameId*: Natural # for the GC stack marking
|
||||
gcFrameType*: PRope # the struct {} we put the GC markers into
|
||||
gcFrameType*: Rope # the struct {} we put the GC markers into
|
||||
|
||||
TTypeSeq* = seq[PType]
|
||||
TCGen = object of TPassContext # represents a C source file
|
||||
|
|
@ -112,24 +118,24 @@ type
|
|||
dataCache*: TNodeTable
|
||||
forwardedProcs*: TSymSeq # keep forwarded procs here
|
||||
typeNodes*, nimTypes*: int # used for type info generation
|
||||
typeNodesName*, nimTypesName*: PRope # used for type info generation
|
||||
typeNodesName*, nimTypesName*: Rope # used for type info generation
|
||||
labels*: Natural # for generating unique module-scope names
|
||||
extensionLoaders*: array['0'..'9', PRope] # special procs for the
|
||||
extensionLoaders*: array['0'..'9', Rope] # special procs for the
|
||||
# OpenGL wrapper
|
||||
injectStmt*: PRope
|
||||
injectStmt*: Rope
|
||||
|
||||
var
|
||||
mainModProcs*, mainModInit*, otherModsInit*, mainDatInit*: PRope
|
||||
mainModProcs*, mainModInit*, otherModsInit*, mainDatInit*: Rope
|
||||
# varuious parts of the main module
|
||||
gMapping*: PRope # the generated mapping file (if requested)
|
||||
gMapping*: Rope # the generated mapping file (if requested)
|
||||
gModules*: seq[BModule] = @[] # list of all compiled modules
|
||||
gForwardedProcsCounter*: int = 0
|
||||
|
||||
proc s*(p: BProc, s: TCProcSection): var PRope {.inline.} =
|
||||
proc s*(p: BProc, s: TCProcSection): var Rope {.inline.} =
|
||||
# section in the current block
|
||||
result = p.blocks[p.blocks.len - 1].sections[s]
|
||||
|
||||
proc procSec*(p: BProc, s: TCProcSection): var PRope {.inline.} =
|
||||
proc procSec*(p: BProc, s: TCProcSection): var Rope {.inline.} =
|
||||
# top level proc sections
|
||||
result = p.blocks[0].sections[s]
|
||||
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
#
|
||||
#
|
||||
# The Nim Compiler
|
||||
# (c) Copyright 2014 Andreas Rumpf
|
||||
# (c) Copyright 2015 Andreas Rumpf
|
||||
#
|
||||
# See the file "copying.txt", included in this
|
||||
# distribution, for details about the copyright.
|
||||
|
|
@ -52,7 +52,7 @@ proc processSwitch*(switch, arg: string, pass: TCmdLinePass, info: TLineInfo)
|
|||
|
||||
const
|
||||
HelpMessage = "Nim Compiler Version $1 (" & CompileDate & ") [$2: $3]\n" &
|
||||
"Copyright (c) 2006-2014 by Andreas Rumpf\n"
|
||||
"Copyright (c) 2006-2015 by Andreas Rumpf\n"
|
||||
|
||||
const
|
||||
Usage = slurp"doc/basicopt.txt".replace("//", "")
|
||||
|
|
@ -65,14 +65,14 @@ proc getCommandLineDesc(): string =
|
|||
proc helpOnError(pass: TCmdLinePass) =
|
||||
if pass == passCmd1:
|
||||
msgWriteln(getCommandLineDesc())
|
||||
quit(0)
|
||||
msgQuit(0)
|
||||
|
||||
proc writeAdvancedUsage(pass: TCmdLinePass) =
|
||||
if pass == passCmd1:
|
||||
msgWriteln(`%`(HelpMessage, [VersionAsString,
|
||||
platform.OS[platform.hostOS].name,
|
||||
CPU[platform.hostCPU].name]) & AdvancedUsage)
|
||||
quit(0)
|
||||
msgQuit(0)
|
||||
|
||||
proc writeVersionInfo(pass: TCmdLinePass) =
|
||||
if pass == passCmd1:
|
||||
|
|
@ -80,14 +80,14 @@ proc writeVersionInfo(pass: TCmdLinePass) =
|
|||
platform.OS[platform.hostOS].name,
|
||||
CPU[platform.hostCPU].name]))
|
||||
|
||||
discard """const gitHash = gorge("git log -n 1 --format=%H")
|
||||
if gitHash.strip.len == 40:
|
||||
msgWriteln("git hash: " & gitHash)"""
|
||||
const gitHash = gorge("git log -n 1 --format=%H").strip
|
||||
when gitHash.len == 40:
|
||||
msgWriteln("git hash: " & gitHash)
|
||||
|
||||
msgWriteln("active boot switches:" & usedRelease & usedAvoidTimeMachine &
|
||||
usedTinyC & usedGnuReadline & usedNativeStacktrace & usedNoCaas &
|
||||
usedFFI & usedBoehm & usedMarkAndSweep & usedGenerational & usedNoGC)
|
||||
quit(0)
|
||||
msgQuit(0)
|
||||
|
||||
var
|
||||
helpWritten: bool
|
||||
|
|
@ -141,7 +141,7 @@ proc expectNoArg(switch, arg: string, pass: TCmdLinePass, info: TLineInfo) =
|
|||
if arg != "": localError(info, errCmdLineNoArgExpected, addPrefix(switch))
|
||||
|
||||
proc processSpecificNote(arg: string, state: TSpecialWord, pass: TCmdLinePass,
|
||||
info: TLineInfo) =
|
||||
info: TLineInfo; orig: string) =
|
||||
var id = "" # arg = "X]:on|off"
|
||||
var i = 0
|
||||
var n = hintMin
|
||||
|
|
@ -149,17 +149,17 @@ proc processSpecificNote(arg: string, state: TSpecialWord, pass: TCmdLinePass,
|
|||
add(id, arg[i])
|
||||
inc(i)
|
||||
if i < len(arg) and (arg[i] == ']'): inc(i)
|
||||
else: invalidCmdLineOption(pass, arg, info)
|
||||
else: invalidCmdLineOption(pass, orig, info)
|
||||
if i < len(arg) and (arg[i] in {':', '='}): inc(i)
|
||||
else: invalidCmdLineOption(pass, arg, info)
|
||||
else: invalidCmdLineOption(pass, orig, info)
|
||||
if state == wHint:
|
||||
var x = findStr(msgs.HintsToStr, id)
|
||||
if x >= 0: n = TNoteKind(x + ord(hintMin))
|
||||
else: invalidCmdLineOption(pass, arg, info)
|
||||
else: localError(info, "unknown hint: " & id)
|
||||
else:
|
||||
var x = findStr(msgs.WarningsToStr, id)
|
||||
if x >= 0: n = TNoteKind(x + ord(warnMin))
|
||||
else: invalidCmdLineOption(pass, arg, info)
|
||||
else: localError(info, "unknown warning: " & id)
|
||||
case whichKeyword(substr(arg, i))
|
||||
of wOn: incl(gNotes, n)
|
||||
of wOff: excl(gNotes, n)
|
||||
|
|
@ -252,11 +252,11 @@ proc trackDirty(arg: string, info: TLineInfo) =
|
|||
if parseUtils.parseInt(a[3], column) <= 0:
|
||||
localError(info, errInvalidNumber, a[2])
|
||||
|
||||
gDirtyBufferIdx = a[0].fileInfoIdx
|
||||
gDirtyOriginalIdx = a[1].fileInfoIdx
|
||||
let dirtyOriginalIdx = a[1].fileInfoIdx
|
||||
if dirtyOriginalIdx >= 0:
|
||||
msgs.setDirtyFile(dirtyOriginalIdx, a[0])
|
||||
|
||||
optTrackPos = newLineInfo(gDirtyBufferIdx, line, column)
|
||||
msgs.addCheckpoint(optTrackPos)
|
||||
gTrackPos = newLineInfo(dirtyOriginalIdx, line, column)
|
||||
|
||||
proc track(arg: string, info: TLineInfo) =
|
||||
var a = arg.split(',')
|
||||
|
|
@ -266,8 +266,7 @@ proc track(arg: string, info: TLineInfo) =
|
|||
localError(info, errInvalidNumber, a[1])
|
||||
if parseUtils.parseInt(a[2], column) <= 0:
|
||||
localError(info, errInvalidNumber, a[2])
|
||||
optTrackPos = newLineInfo(a[0], line, column)
|
||||
msgs.addCheckpoint(optTrackPos)
|
||||
gTrackPos = newLineInfo(a[0], line, column)
|
||||
|
||||
proc dynlibOverride(switch, arg: string, pass: TCmdLinePass, info: TLineInfo) =
|
||||
if pass in {passCmd2, passPP}:
|
||||
|
|
@ -320,7 +319,7 @@ proc processSwitch(switch, arg: string, pass: TCmdLinePass, info: TLineInfo) =
|
|||
undefSymbol(arg)
|
||||
of "symbol":
|
||||
expectArg(switch, arg, pass, info)
|
||||
declareSymbol(arg)
|
||||
# deprecated, do nothing
|
||||
of "compile":
|
||||
expectArg(switch, arg, pass, info)
|
||||
if pass in {passCmd2, passPP}: processCompile(arg)
|
||||
|
|
@ -369,16 +368,26 @@ proc processSwitch(switch, arg: string, pass: TCmdLinePass, info: TLineInfo) =
|
|||
defineSymbol("nogc")
|
||||
else: localError(info, errNoneBoehmRefcExpectedButXFound, arg)
|
||||
of "warnings", "w": processOnOffSwitch({optWarns}, arg, pass, info)
|
||||
of "warning": processSpecificNote(arg, wWarning, pass, info)
|
||||
of "hint": processSpecificNote(arg, wHint, pass, info)
|
||||
of "warning": processSpecificNote(arg, wWarning, pass, info, switch)
|
||||
of "hint": processSpecificNote(arg, wHint, pass, info, switch)
|
||||
of "hints": processOnOffSwitch({optHints}, arg, pass, info)
|
||||
of "threadanalysis": processOnOffSwitchG({optThreadAnalysis}, arg, pass, info)
|
||||
of "stacktrace": processOnOffSwitch({optStackTrace}, arg, pass, info)
|
||||
of "linetrace": processOnOffSwitch({optLineTrace}, arg, pass, info)
|
||||
of "debugger":
|
||||
processOnOffSwitch({optEndb}, arg, pass, info)
|
||||
if optEndb in gOptions: defineSymbol("endb")
|
||||
else: undefSymbol("endb")
|
||||
case arg.normalize
|
||||
of "on", "endb":
|
||||
gOptions.incl optEndb
|
||||
defineSymbol("endb")
|
||||
of "off":
|
||||
gOptions.excl optEndb
|
||||
undefSymbol("endb")
|
||||
of "native", "gdb":
|
||||
incl(gGlobalOptions, optCDebug)
|
||||
gOptions = gOptions + {optLineDir} - {optEndb}
|
||||
undefSymbol("endb")
|
||||
else:
|
||||
localError(info, "expected endb|gdb but found " & arg)
|
||||
of "profiler":
|
||||
processOnOffSwitch({optProfiler}, arg, pass, info)
|
||||
if optProfiler in gOptions: defineSymbol("profiler")
|
||||
|
|
@ -398,7 +407,7 @@ proc processSwitch(switch, arg: string, pass: TCmdLinePass, info: TLineInfo) =
|
|||
of "deadcodeelim": processOnOffSwitchG({optDeadCodeElim}, arg, pass, info)
|
||||
of "threads":
|
||||
processOnOffSwitchG({optThreads}, arg, pass, info)
|
||||
if optThreads in gGlobalOptions: incl(gNotes, warnGcUnsafe)
|
||||
#if optThreads in gGlobalOptions: incl(gNotes, warnGcUnsafe)
|
||||
of "tlsemulation": processOnOffSwitchG({optTlsEmulation}, arg, pass, info)
|
||||
of "taintmode": processOnOffSwitchG({optTaintMode}, arg, pass, info)
|
||||
of "implicitstatic":
|
||||
|
|
@ -479,7 +488,6 @@ proc processSwitch(switch, arg: string, pass: TCmdLinePass, info: TLineInfo) =
|
|||
if theOS == osNone: localError(info, errUnknownOS, arg)
|
||||
elif theOS != platform.hostOS:
|
||||
setTarget(theOS, targetCPU)
|
||||
condsyms.initDefines()
|
||||
of "cpu":
|
||||
expectArg(switch, arg, pass, info)
|
||||
if pass in {passCmd1, passPP}:
|
||||
|
|
@ -487,7 +495,6 @@ proc processSwitch(switch, arg: string, pass: TCmdLinePass, info: TLineInfo) =
|
|||
if cpu == cpuNone: localError(info, errUnknownCPU, arg)
|
||||
elif cpu != platform.hostCPU:
|
||||
setTarget(targetOS, cpu)
|
||||
condsyms.initDefines()
|
||||
of "run", "r":
|
||||
expectNoArg(switch, arg, pass, info)
|
||||
incl(gGlobalOptions, optRun)
|
||||
|
|
@ -541,19 +548,19 @@ proc processSwitch(switch, arg: string, pass: TCmdLinePass, info: TLineInfo) =
|
|||
trackDirty(arg, info)
|
||||
of "suggest":
|
||||
expectNoArg(switch, arg, pass, info)
|
||||
incl(gGlobalOptions, optSuggest)
|
||||
gIdeCmd = ideSug
|
||||
of "def":
|
||||
expectNoArg(switch, arg, pass, info)
|
||||
incl(gGlobalOptions, optDef)
|
||||
gIdeCmd = ideDef
|
||||
of "eval":
|
||||
expectArg(switch, arg, pass, info)
|
||||
gEvalExpr = arg
|
||||
of "context":
|
||||
expectNoArg(switch, arg, pass, info)
|
||||
incl(gGlobalOptions, optContext)
|
||||
gIdeCmd = ideCon
|
||||
of "usages":
|
||||
expectNoArg(switch, arg, pass, info)
|
||||
incl(gGlobalOptions, optUsages)
|
||||
gIdeCmd = ideUse
|
||||
of "stdout":
|
||||
expectNoArg(switch, arg, pass, info)
|
||||
incl(gGlobalOptions, optStdout)
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
#
|
||||
#
|
||||
# The Nim Compiler
|
||||
# (c) Copyright 2014 Andreas Rumpf
|
||||
# (c) Copyright 2015 Andreas Rumpf
|
||||
#
|
||||
# See the file "copying.txt", included in this
|
||||
# distribution, for details about the copyright.
|
||||
|
|
@ -12,66 +12,64 @@
|
|||
import
|
||||
strtabs, platform, strutils, idents
|
||||
|
||||
# We need to use a PStringTable here as defined symbols are always guaranteed
|
||||
# We need to use a StringTableRef here as defined symbols are always guaranteed
|
||||
# to be style insensitive. Otherwise hell would break lose.
|
||||
var gSymbols: StringTableRef
|
||||
|
||||
const
|
||||
catNone = "false"
|
||||
|
||||
proc defineSymbol*(symbol: string) =
|
||||
gSymbols[symbol] = "true"
|
||||
|
||||
proc declareSymbol*(symbol: string) =
|
||||
gSymbols[symbol] = "unknown"
|
||||
|
||||
proc undefSymbol*(symbol: string) =
|
||||
gSymbols[symbol] = "false"
|
||||
gSymbols[symbol] = catNone
|
||||
|
||||
proc isDefined*(symbol: string): bool =
|
||||
if gSymbols.hasKey(symbol):
|
||||
result = gSymbols[symbol] == "true"
|
||||
result = gSymbols[symbol] != catNone
|
||||
elif cmpIgnoreStyle(symbol, CPU[targetCPU].name) == 0:
|
||||
result = true
|
||||
elif cmpIgnoreStyle(symbol, platform.OS[targetOS].name) == 0:
|
||||
result = true
|
||||
else:
|
||||
case symbol.normalize
|
||||
of "x86": result = targetCPU == cpuI386
|
||||
of "itanium": result = targetCPU == cpuIa64
|
||||
of "x8664": result = targetCPU == cpuAmd64
|
||||
of "posix", "unix":
|
||||
result = targetOS in {osLinux, osMorphos, osSkyos, osIrix, osPalmos,
|
||||
osQnx, osAtari, osAix,
|
||||
osHaiku, osVxWorks, osSolaris, osNetbsd,
|
||||
osFreebsd, osOpenbsd, osMacosx}
|
||||
of "bsd":
|
||||
result = targetOS in {osNetbsd, osFreebsd, osOpenbsd}
|
||||
of "emulatedthreadvars":
|
||||
result = platform.OS[targetOS].props.contains(ospLacksThreadVars)
|
||||
of "msdos": result = targetOS == osDos
|
||||
of "mswindows", "win32": result = targetOS == osWindows
|
||||
of "macintosh": result = targetOS in {osMacos, osMacosx}
|
||||
of "sunos": result = targetOS == osSolaris
|
||||
of "littleendian": result = CPU[targetCPU].endian == platform.littleEndian
|
||||
of "bigendian": result = CPU[targetCPU].endian == platform.bigEndian
|
||||
of "cpu8": result = CPU[targetCPU].bit == 8
|
||||
of "cpu16": result = CPU[targetCPU].bit == 16
|
||||
of "cpu32": result = CPU[targetCPU].bit == 32
|
||||
of "cpu64": result = CPU[targetCPU].bit == 64
|
||||
of "nimrawsetjmp":
|
||||
result = targetOS in {osSolaris, osNetbsd, osFreebsd, osOpenbsd, osMacosx}
|
||||
else: discard
|
||||
|
||||
proc isDefined*(symbol: PIdent): bool = isDefined(symbol.s)
|
||||
proc isDeclared*(symbol: PIdent): bool = gSymbols.hasKey(symbol.s)
|
||||
|
||||
iterator definedSymbolNames*: string =
|
||||
for key, val in pairs(gSymbols):
|
||||
if val == "true": yield key
|
||||
if val != catNone: yield key
|
||||
|
||||
proc countDefinedSymbols*(): int =
|
||||
result = 0
|
||||
for key, val in pairs(gSymbols):
|
||||
if val == "true": inc(result)
|
||||
|
||||
# For ease of bootstrapping, we keep them here and not in the global config
|
||||
# file for now:
|
||||
const
|
||||
additionalSymbols = """
|
||||
x86 itanium x8664
|
||||
msdos mswindows win32 unix posix sunos bsd macintosh RISCOS hpux
|
||||
mac
|
||||
|
||||
hppa hp9000 hp9000s300 hp9000s700 hp9000s800 hp9000s820 ELATE sparcv9
|
||||
|
||||
ecmascript js nimrodvm nimffi nimdoc cpp objc
|
||||
gcc llvmgcc clang lcc bcc dmc wcc vcc tcc pcc ucc icl
|
||||
boehmgc gcmarkandsweep gcgenerational nogc gcUseBitvectors
|
||||
endb profiler
|
||||
executable guiapp consoleapp library dll staticlib
|
||||
|
||||
quick
|
||||
release debug
|
||||
useWinAnsi useFork useNimRtl useMalloc useRealtimeGC ssl memProfiler
|
||||
nodejs kwin nimfix
|
||||
|
||||
usesysassert usegcassert tinyC useFFI
|
||||
useStdoutAsStdmsg createNimRtl
|
||||
booting fulldebug corruption nimsuperops noSignalHandler useGnuReadline
|
||||
noCaas noDocGen noBusyWaiting nativeStackTrace useNodeIds selftest
|
||||
reportMissedDeadlines avoidTimeMachine useClone ignoreAllocationSize
|
||||
debugExecProcesses pcreDll useLipzipSrc
|
||||
preventDeadlocks UNICODE winUnicode trackGcHeaders posixRealtime
|
||||
|
||||
nimStdSetjmp nimRawSetjmp nimSigSetjmp
|
||||
""".split
|
||||
if val != catNone: inc(result)
|
||||
|
||||
proc initDefines*() =
|
||||
gSymbols = newStringTable(modeStyleInsensitive)
|
||||
|
|
@ -89,58 +87,4 @@ proc initDefines*() =
|
|||
defineSymbol("nimparsebiggestfloatmagic")
|
||||
defineSymbol("nimalias")
|
||||
defineSymbol("nimlocks")
|
||||
|
||||
# add platform specific symbols:
|
||||
for c in low(CPU)..high(CPU):
|
||||
declareSymbol("cpu" & $CPU[c].bit)
|
||||
declareSymbol(normalize(EndianToStr[CPU[c].endian]))
|
||||
declareSymbol(CPU[c].name)
|
||||
for o in low(platform.OS)..high(platform.OS):
|
||||
declareSymbol(platform.OS[o].name)
|
||||
|
||||
for a in additionalSymbols:
|
||||
declareSymbol(a)
|
||||
|
||||
# -----------------------------------------------------------
|
||||
case targetCPU
|
||||
of cpuI386: defineSymbol("x86")
|
||||
of cpuIa64: defineSymbol("itanium")
|
||||
of cpuAmd64: defineSymbol("x8664")
|
||||
else: discard
|
||||
case targetOS
|
||||
of osDos:
|
||||
defineSymbol("msdos")
|
||||
of osWindows:
|
||||
defineSymbol("mswindows")
|
||||
defineSymbol("win32")
|
||||
of osLinux, osMorphos, osSkyos, osIrix, osPalmos, osQnx, osAtari, osAix,
|
||||
osHaiku:
|
||||
# these are all 'unix-like'
|
||||
defineSymbol("unix")
|
||||
defineSymbol("posix")
|
||||
of osSolaris:
|
||||
defineSymbol("sunos")
|
||||
defineSymbol("unix")
|
||||
defineSymbol("posix")
|
||||
of osNetbsd, osFreebsd, osOpenbsd:
|
||||
defineSymbol("unix")
|
||||
defineSymbol("bsd")
|
||||
defineSymbol("posix")
|
||||
of osMacos:
|
||||
defineSymbol("macintosh")
|
||||
of osMacosx:
|
||||
defineSymbol("macintosh")
|
||||
defineSymbol("unix")
|
||||
defineSymbol("posix")
|
||||
else: discard
|
||||
defineSymbol("cpu" & $CPU[targetCPU].bit)
|
||||
defineSymbol(normalize(EndianToStr[CPU[targetCPU].endian]))
|
||||
defineSymbol(CPU[targetCPU].name)
|
||||
defineSymbol(platform.OS[targetOS].name)
|
||||
declareSymbol("emulatedthreadvars")
|
||||
if platform.OS[targetOS].props.contains(ospLacksThreadVars):
|
||||
defineSymbol("emulatedthreadvars")
|
||||
case targetOS
|
||||
of osSolaris, osNetbsd, osFreebsd, osOpenbsd, osMacosx:
|
||||
defineSymbol("nimRawSetjmp")
|
||||
else: discard
|
||||
defineSymbol("nimnode")
|
||||
|
|
|
|||
|
|
@ -19,10 +19,10 @@ type
|
|||
module*: PSym
|
||||
PGen = ref TGen
|
||||
|
||||
var gDotGraph: PRope # the generated DOT file; we need a global variable
|
||||
var gDotGraph: Rope # the generated DOT file; we need a global variable
|
||||
|
||||
proc addDependencyAux(importing, imported: string) =
|
||||
appf(gDotGraph, "$1 -> $2;$n", [toRope(importing), toRope(imported)])
|
||||
addf(gDotGraph, "$1 -> $2;$n", [rope(importing), rope(imported)])
|
||||
# s1 -> s2_4[label="[0-9]"];
|
||||
|
||||
proc addDotDependency(c: PPassContext, n: PNode): PNode =
|
||||
|
|
@ -42,8 +42,8 @@ proc addDotDependency(c: PPassContext, n: PNode): PNode =
|
|||
discard
|
||||
|
||||
proc generateDot(project: string) =
|
||||
writeRope(ropef("digraph $1 {$n$2}$n", [
|
||||
toRope(changeFileExt(extractFilename(project), "")), gDotGraph]),
|
||||
writeRope("digraph $1 {$n$2}$n" % [
|
||||
rope(changeFileExt(extractFilename(project), "")), gDotGraph],
|
||||
changeFileExt(project, "dot"))
|
||||
|
||||
proc myOpen(module: PSym): PPassContext =
|
||||
|
|
|
|||
|
|
@ -17,12 +17,13 @@ import
|
|||
importer, sempass2, json, xmltree, cgi, typesrenderer
|
||||
|
||||
type
|
||||
TSections = array[TSymKind, PRope]
|
||||
TSections = array[TSymKind, Rope]
|
||||
TDocumentor = object of rstgen.TRstGenerator
|
||||
modDesc: PRope # module description
|
||||
modDesc: Rope # module description
|
||||
id: int # for generating IDs
|
||||
toc, section: TSections
|
||||
indexValFilename: string
|
||||
analytics: string # Google Analytics javascript, "" if doesn't exist
|
||||
seenSymbols: StringTableRef # avoids duplicate symbol generation for HTML.
|
||||
|
||||
PDoc* = ref TDocumentor ## Alias to type less.
|
||||
|
|
@ -61,12 +62,29 @@ proc newDocumentor*(filename: string, config: StringTableRef): PDoc =
|
|||
initRstGenerator(result[], (if gCmd != cmdRst2tex: outHtml else: outLatex),
|
||||
options.gConfigVars, filename, {roSupportRawDirective},
|
||||
docgenFindFile, compilerMsgHandler)
|
||||
|
||||
if config.hasKey("doc.googleAnalytics"):
|
||||
result.analytics = """
|
||||
<script>
|
||||
(function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
|
||||
(i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
|
||||
m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
|
||||
})(window,document,'script','//www.google-analytics.com/analytics.js','ga');
|
||||
|
||||
ga('create', '$1', 'auto');
|
||||
ga('send', 'pageview');
|
||||
|
||||
</script>
|
||||
""" % [config["doc.googleAnalytics"]]
|
||||
else:
|
||||
result.analytics = ""
|
||||
|
||||
result.seenSymbols = newStringTable(modeCaseInsensitive)
|
||||
result.id = 100
|
||||
|
||||
proc dispA(dest: var PRope, xml, tex: string, args: openArray[PRope]) =
|
||||
if gCmd != cmdRst2tex: appf(dest, xml, args)
|
||||
else: appf(dest, tex, args)
|
||||
proc dispA(dest: var Rope, xml, tex: string, args: openArray[Rope]) =
|
||||
if gCmd != cmdRst2tex: addf(dest, xml, args)
|
||||
else: addf(dest, tex, args)
|
||||
|
||||
proc getVarIdx(varnames: openArray[string], id: string): int =
|
||||
for i in countup(0, high(varnames)):
|
||||
|
|
@ -74,8 +92,8 @@ proc getVarIdx(varnames: openArray[string], id: string): int =
|
|||
return i
|
||||
result = -1
|
||||
|
||||
proc ropeFormatNamedVars(frmt: TFormatStr, varnames: openArray[string],
|
||||
varvalues: openArray[PRope]): PRope =
|
||||
proc ropeFormatNamedVars(frmt: FormatStr, varnames: openArray[string],
|
||||
varvalues: openArray[Rope]): Rope =
|
||||
var i = 0
|
||||
var L = len(frmt)
|
||||
result = nil
|
||||
|
|
@ -85,11 +103,11 @@ proc ropeFormatNamedVars(frmt: TFormatStr, varnames: openArray[string],
|
|||
inc(i) # skip '$'
|
||||
case frmt[i]
|
||||
of '#':
|
||||
app(result, varvalues[num])
|
||||
add(result, varvalues[num])
|
||||
inc(num)
|
||||
inc(i)
|
||||
of '$':
|
||||
app(result, "$")
|
||||
add(result, "$")
|
||||
inc(i)
|
||||
of '0'..'9':
|
||||
var j = 0
|
||||
|
|
@ -99,7 +117,7 @@ proc ropeFormatNamedVars(frmt: TFormatStr, varnames: openArray[string],
|
|||
if (i > L + 0 - 1) or not (frmt[i] in {'0'..'9'}): break
|
||||
if j > high(varvalues) + 1: internalError("ropeFormatNamedVars")
|
||||
num = j
|
||||
app(result, varvalues[j - 1])
|
||||
add(result, varvalues[j - 1])
|
||||
of 'A'..'Z', 'a'..'z', '\x80'..'\xFF':
|
||||
var id = ""
|
||||
while true:
|
||||
|
|
@ -107,8 +125,8 @@ proc ropeFormatNamedVars(frmt: TFormatStr, varnames: openArray[string],
|
|||
inc(i)
|
||||
if not (frmt[i] in {'A'..'Z', '_', 'a'..'z', '\x80'..'\xFF'}): break
|
||||
var idx = getVarIdx(varnames, id)
|
||||
if idx >= 0: app(result, varvalues[idx])
|
||||
else: rawMessage(errUnkownSubstitionVar, id)
|
||||
if idx >= 0: add(result, varvalues[idx])
|
||||
else: rawMessage(errUnknownSubstitionVar, id)
|
||||
of '{':
|
||||
var id = ""
|
||||
inc(i)
|
||||
|
|
@ -119,14 +137,14 @@ proc ropeFormatNamedVars(frmt: TFormatStr, varnames: openArray[string],
|
|||
inc(i) # skip }
|
||||
# search for the variable:
|
||||
var idx = getVarIdx(varnames, id)
|
||||
if idx >= 0: app(result, varvalues[idx])
|
||||
else: rawMessage(errUnkownSubstitionVar, id)
|
||||
if idx >= 0: add(result, varvalues[idx])
|
||||
else: rawMessage(errUnknownSubstitionVar, id)
|
||||
else: internalError("ropeFormatNamedVars")
|
||||
var start = i
|
||||
while i < L:
|
||||
if frmt[i] != '$': inc(i)
|
||||
else: break
|
||||
if i - 1 >= start: app(result, substr(frmt, start, i - 1))
|
||||
if i - 1 >= start: add(result, substr(frmt, start, i - 1))
|
||||
|
||||
proc genComment(d: PDoc, n: PNode): string =
|
||||
result = ""
|
||||
|
|
@ -136,9 +154,9 @@ proc genComment(d: PDoc, n: PNode): string =
|
|||
toLinenumber(n.info), toColumn(n.info),
|
||||
dummyHasToc, d.options + {roSkipPounds}), result)
|
||||
|
||||
proc genRecComment(d: PDoc, n: PNode): PRope =
|
||||
proc genRecComment(d: PDoc, n: PNode): Rope =
|
||||
if n == nil: return nil
|
||||
result = genComment(d, n).toRope
|
||||
result = genComment(d, n).rope
|
||||
if result == nil:
|
||||
if n.kind notin {nkEmpty..nkNilLit}:
|
||||
for i in countup(0, len(n)-1):
|
||||
|
|
@ -254,7 +272,7 @@ proc complexName(k: TSymKind, n: PNode, baseName: string): string =
|
|||
## type)?(,param type)*``. The callable type part will be added only if the
|
||||
## node is not a proc, as those are the common ones. The suffix will be a dot
|
||||
## and a single letter representing the type of the callable. The parameter
|
||||
## types will be added with a preceeding dash. Return types won't be added.
|
||||
## types will be added with a preceding dash. Return types won't be added.
|
||||
##
|
||||
## If you modify the output of this proc, please update the anchor generation
|
||||
## section of ``doc/docgen.txt``.
|
||||
|
|
@ -313,9 +331,9 @@ proc genItem(d: PDoc, n, nameNode: PNode, k: TSymKind) =
|
|||
if not isVisible(nameNode): return
|
||||
let
|
||||
name = getName(d, nameNode)
|
||||
nameRope = name.toRope
|
||||
nameRope = name.rope
|
||||
plainDocstring = getPlainDocstring(n) # call here before genRecComment!
|
||||
var result: PRope = nil
|
||||
var result: Rope = nil
|
||||
var literal, plainName = ""
|
||||
var kind = tkEof
|
||||
var comm = genRecComment(d, n) # call this here for the side-effect!
|
||||
|
|
@ -338,69 +356,69 @@ proc genItem(d: PDoc, n, nameNode: PNode, k: TSymKind) =
|
|||
break
|
||||
of tkComment:
|
||||
dispA(result, "<span class=\"Comment\">$1</span>", "\\spanComment{$1}",
|
||||
[toRope(esc(d.target, literal))])
|
||||
[rope(esc(d.target, literal))])
|
||||
of tokKeywordLow..tokKeywordHigh:
|
||||
dispA(result, "<span class=\"Keyword\">$1</span>", "\\spanKeyword{$1}",
|
||||
[toRope(literal)])
|
||||
[rope(literal)])
|
||||
of tkOpr:
|
||||
dispA(result, "<span class=\"Operator\">$1</span>", "\\spanOperator{$1}",
|
||||
[toRope(esc(d.target, literal))])
|
||||
[rope(esc(d.target, literal))])
|
||||
of tkStrLit..tkTripleStrLit:
|
||||
dispA(result, "<span class=\"StringLit\">$1</span>",
|
||||
"\\spanStringLit{$1}", [toRope(esc(d.target, literal))])
|
||||
"\\spanStringLit{$1}", [rope(esc(d.target, literal))])
|
||||
of tkCharLit:
|
||||
dispA(result, "<span class=\"CharLit\">$1</span>", "\\spanCharLit{$1}",
|
||||
[toRope(esc(d.target, literal))])
|
||||
[rope(esc(d.target, literal))])
|
||||
of tkIntLit..tkUInt64Lit:
|
||||
dispA(result, "<span class=\"DecNumber\">$1</span>",
|
||||
"\\spanDecNumber{$1}", [toRope(esc(d.target, literal))])
|
||||
"\\spanDecNumber{$1}", [rope(esc(d.target, literal))])
|
||||
of tkFloatLit..tkFloat128Lit:
|
||||
dispA(result, "<span class=\"FloatNumber\">$1</span>",
|
||||
"\\spanFloatNumber{$1}", [toRope(esc(d.target, literal))])
|
||||
"\\spanFloatNumber{$1}", [rope(esc(d.target, literal))])
|
||||
of tkSymbol:
|
||||
dispA(result, "<span class=\"Identifier\">$1</span>",
|
||||
"\\spanIdentifier{$1}", [toRope(esc(d.target, literal))])
|
||||
"\\spanIdentifier{$1}", [rope(esc(d.target, literal))])
|
||||
of tkSpaces, tkInvalid:
|
||||
app(result, literal)
|
||||
add(result, literal)
|
||||
of tkParLe, tkParRi, tkBracketLe, tkBracketRi, tkCurlyLe, tkCurlyRi,
|
||||
tkBracketDotLe, tkBracketDotRi, tkCurlyDotLe, tkCurlyDotRi, tkParDotLe,
|
||||
tkParDotRi, tkComma, tkSemiColon, tkColon, tkEquals, tkDot, tkDotDot,
|
||||
tkAccent, tkColonColon,
|
||||
tkGStrLit, tkGTripleStrLit, tkInfixOpr, tkPrefixOpr, tkPostfixOpr:
|
||||
dispA(result, "<span class=\"Other\">$1</span>", "\\spanOther{$1}",
|
||||
[toRope(esc(d.target, literal))])
|
||||
[rope(esc(d.target, literal))])
|
||||
inc(d.id)
|
||||
let
|
||||
plainNameRope = toRope(xmltree.escape(plainName.strip))
|
||||
plainNameRope = rope(xmltree.escape(plainName.strip))
|
||||
cleanPlainSymbol = renderPlainSymbolName(nameNode)
|
||||
complexSymbol = complexName(k, n, cleanPlainSymbol)
|
||||
plainSymbolRope = toRope(cleanPlainSymbol)
|
||||
plainSymbolEncRope = toRope(encodeUrl(cleanPlainSymbol))
|
||||
itemIDRope = toRope(d.id)
|
||||
plainSymbolRope = rope(cleanPlainSymbol)
|
||||
plainSymbolEncRope = rope(encodeUrl(cleanPlainSymbol))
|
||||
itemIDRope = rope(d.id)
|
||||
symbolOrId = d.newUniquePlainSymbol(complexSymbol)
|
||||
symbolOrIdRope = symbolOrId.toRope
|
||||
symbolOrIdEncRope = encodeUrl(symbolOrId).toRope
|
||||
symbolOrIdRope = symbolOrId.rope
|
||||
symbolOrIdEncRope = encodeUrl(symbolOrId).rope
|
||||
|
||||
var seeSrcRope: PRope = nil
|
||||
var seeSrcRope: Rope = nil
|
||||
let docItemSeeSrc = getConfigVar("doc.item.seesrc")
|
||||
if docItemSeeSrc.len > 0 and options.docSeeSrcUrl.len > 0:
|
||||
# XXX toFilename doesn't really work. We need to ensure that this keeps
|
||||
# returning a relative path.
|
||||
let urlRope = ropeFormatNamedVars(options.docSeeSrcUrl,
|
||||
["path", "line"], [n.info.toFilename.toRope, toRope($n.info.line)])
|
||||
["path", "line"], [n.info.toFilename.rope, rope($n.info.line)])
|
||||
dispA(seeSrcRope, "$1", "", [ropeFormatNamedVars(docItemSeeSrc,
|
||||
["path", "line", "url"], [n.info.toFilename.toRope,
|
||||
toRope($n.info.line), urlRope])])
|
||||
["path", "line", "url"], [n.info.toFilename.rope,
|
||||
rope($n.info.line), urlRope])])
|
||||
|
||||
app(d.section[k], ropeFormatNamedVars(getConfigVar("doc.item"),
|
||||
add(d.section[k], ropeFormatNamedVars(getConfigVar("doc.item"),
|
||||
["name", "header", "desc", "itemID", "header_plain", "itemSym",
|
||||
"itemSymOrID", "itemSymEnc", "itemSymOrIDEnc", "seeSrc"],
|
||||
[nameRope, result, comm, itemIDRope, plainNameRope, plainSymbolRope,
|
||||
symbolOrIdRope, plainSymbolEncRope, symbolOrIdEncRope, seeSrcRope]))
|
||||
app(d.toc[k], ropeFormatNamedVars(getConfigVar("doc.item.toc"),
|
||||
add(d.toc[k], ropeFormatNamedVars(getConfigVar("doc.item.toc"),
|
||||
["name", "header", "desc", "itemID", "header_plain", "itemSym",
|
||||
"itemSymOrID", "itemSymEnc", "itemSymOrIDEnc"],
|
||||
[toRope(getName(d, nameNode, d.splitAfter)), result, comm,
|
||||
[rope(getName(d, nameNode, d.splitAfter)), result, comm,
|
||||
itemIDRope, plainNameRope, plainSymbolRope, symbolOrIdRope,
|
||||
plainSymbolEncRope, symbolOrIdEncRope]))
|
||||
|
||||
|
|
@ -418,7 +436,7 @@ proc genJSONItem(d: PDoc, n, nameNode: PNode, k: TSymKind): JsonNode =
|
|||
if not isVisible(nameNode): return
|
||||
var
|
||||
name = getName(d, nameNode)
|
||||
comm = genRecComment(d, n).ropeToStr()
|
||||
comm = $genRecComment(d, n)
|
||||
r: TSrcGen
|
||||
|
||||
initTokRender(r, n, {renderNoBody, renderNoComments, renderDocComments})
|
||||
|
|
@ -435,14 +453,14 @@ proc checkForFalse(n: PNode): bool =
|
|||
|
||||
proc traceDeps(d: PDoc, n: PNode) =
|
||||
const k = skModule
|
||||
if d.section[k] != nil: app(d.section[k], ", ")
|
||||
if d.section[k] != nil: add(d.section[k], ", ")
|
||||
dispA(d.section[k],
|
||||
"<a class=\"reference external\" href=\"$1.html\">$1</a>",
|
||||
"$1", [toRope(getModuleName(n))])
|
||||
"$1", [rope(getModuleName(n))])
|
||||
|
||||
proc generateDoc*(d: PDoc, n: PNode) =
|
||||
case n.kind
|
||||
of nkCommentStmt: app(d.modDesc, genComment(d, n))
|
||||
of nkCommentStmt: add(d.modDesc, genComment(d, n))
|
||||
of nkProcDef:
|
||||
when useEffectSystem: documentRaises(n)
|
||||
genItem(d, n, n.sons[namePos], skProc)
|
||||
|
|
@ -503,13 +521,13 @@ proc generateJson(d: PDoc, n: PNode, jArray: JsonNode = nil): JsonNode =
|
|||
result = genJSONItem(d, n.sons[i], n.sons[i].sons[0],
|
||||
succ(skType, ord(n.kind)-ord(nkTypeSection)))
|
||||
of nkStmtList:
|
||||
var elem = jArray
|
||||
if elem == nil: elem = newJArray()
|
||||
result = if jArray != nil: jArray else: newJArray()
|
||||
|
||||
for i in countup(0, sonsLen(n) - 1):
|
||||
var r = generateJson(d, n.sons[i], elem)
|
||||
var r = generateJson(d, n.sons[i], result)
|
||||
if r != nil:
|
||||
elem.add(r)
|
||||
if result == nil: result = elem
|
||||
result.add(r)
|
||||
|
||||
of nkWhenStmt:
|
||||
# generate documentation for the first branch only:
|
||||
if not checkForFalse(n.sons[0].sons[0]) and jArray != nil:
|
||||
|
|
@ -522,28 +540,28 @@ proc genSection(d: PDoc, kind: TSymKind) =
|
|||
"Iterators", "Iterators", "Converters", "Macros", "Templates"
|
||||
]
|
||||
if d.section[kind] == nil: return
|
||||
var title = sectionNames[kind].toRope
|
||||
var title = sectionNames[kind].rope
|
||||
d.section[kind] = ropeFormatNamedVars(getConfigVar("doc.section"), [
|
||||
"sectionid", "sectionTitle", "sectionTitleID", "content"], [
|
||||
ord(kind).toRope, title, toRope(ord(kind) + 50), d.section[kind]])
|
||||
ord(kind).rope, title, rope(ord(kind) + 50), d.section[kind]])
|
||||
d.toc[kind] = ropeFormatNamedVars(getConfigVar("doc.section.toc"), [
|
||||
"sectionid", "sectionTitle", "sectionTitleID", "content"], [
|
||||
ord(kind).toRope, title, toRope(ord(kind) + 50), d.toc[kind]])
|
||||
ord(kind).rope, title, rope(ord(kind) + 50), d.toc[kind]])
|
||||
|
||||
proc genOutFile(d: PDoc): PRope =
|
||||
proc genOutFile(d: PDoc): Rope =
|
||||
var
|
||||
code, content: PRope
|
||||
code, content: Rope
|
||||
title = ""
|
||||
var j = 0
|
||||
var tmp = ""
|
||||
renderTocEntries(d[], j, 1, tmp)
|
||||
var toc = tmp.toRope
|
||||
var toc = tmp.rope
|
||||
for i in countup(low(TSymKind), high(TSymKind)):
|
||||
genSection(d, i)
|
||||
app(toc, d.toc[i])
|
||||
add(toc, d.toc[i])
|
||||
if toc != nil:
|
||||
toc = ropeFormatNamedVars(getConfigVar("doc.toc"), ["content"], [toc])
|
||||
for i in countup(low(TSymKind), high(TSymKind)): app(code, d.section[i])
|
||||
for i in countup(low(TSymKind), high(TSymKind)): add(code, d.section[i])
|
||||
|
||||
# Extract the title. Non API modules generate an entry in the index table.
|
||||
if d.meta[metaTitle].len != 0:
|
||||
|
|
@ -556,16 +574,16 @@ proc genOutFile(d: PDoc): PRope =
|
|||
let bodyname = if d.hasToc: "doc.body_toc" else: "doc.body_no_toc"
|
||||
content = ropeFormatNamedVars(getConfigVar(bodyname), ["title",
|
||||
"tableofcontents", "moduledesc", "date", "time", "content"],
|
||||
[title.toRope, toc, d.modDesc, toRope(getDateStr()),
|
||||
toRope(getClockStr()), code])
|
||||
[title.rope, toc, d.modDesc, rope(getDateStr()),
|
||||
rope(getClockStr()), code])
|
||||
if optCompileOnly notin gGlobalOptions:
|
||||
# XXX what is this hack doing here? 'optCompileOnly' means raw output!?
|
||||
code = ropeFormatNamedVars(getConfigVar("doc.file"), ["title",
|
||||
"tableofcontents", "moduledesc", "date", "time",
|
||||
"content", "author", "version"],
|
||||
[title.toRope, toc, d.modDesc, toRope(getDateStr()),
|
||||
toRope(getClockStr()), content, d.meta[metaAuthor].toRope,
|
||||
d.meta[metaVersion].toRope])
|
||||
"content", "author", "version", "analytics"],
|
||||
[title.rope, toc, d.modDesc, rope(getDateStr()),
|
||||
rope(getClockStr()), content, d.meta[metaAuthor].rope,
|
||||
d.meta[metaVersion].rope, d.analytics.rope])
|
||||
else:
|
||||
code = content
|
||||
result = code
|
||||
|
|
@ -600,7 +618,7 @@ proc commandRstAux(filename, outExt: string) =
|
|||
#d.modDesc = newMutableRope(30_000)
|
||||
renderRstToOut(d[], rst, modDesc)
|
||||
#freezeMutableRope(d.modDesc)
|
||||
d.modDesc = toRope(modDesc)
|
||||
d.modDesc = rope(modDesc)
|
||||
writeOutput(d, filename, outExt)
|
||||
generateIndex(d)
|
||||
|
||||
|
|
@ -617,7 +635,7 @@ proc commandJSON*() =
|
|||
var d = newDocumentor(gProjectFull, options.gConfigVars)
|
||||
d.hasToc = true
|
||||
var json = generateJson(d, ast)
|
||||
var content = newRope(pretty(json))
|
||||
var content = rope(pretty(json))
|
||||
|
||||
if optStdout in gGlobalOptions:
|
||||
writeRope(stdout, content)
|
||||
|
|
@ -626,11 +644,12 @@ proc commandJSON*() =
|
|||
writeRope(content, getOutFile(gProjectFull, JsonExt), useWarning = false)
|
||||
|
||||
proc commandBuildIndex*() =
|
||||
var content = mergeIndexes(gProjectFull).toRope
|
||||
var content = mergeIndexes(gProjectFull).rope
|
||||
|
||||
let code = ropeFormatNamedVars(getConfigVar("doc.file"), ["title",
|
||||
"tableofcontents", "moduledesc", "date", "time",
|
||||
"content", "author", "version"],
|
||||
["Index".toRope, nil, nil, toRope(getDateStr()),
|
||||
toRope(getClockStr()), content, nil, nil])
|
||||
"content", "author", "version", "analytics"],
|
||||
["Index".rope, nil, nil, rope(getDateStr()),
|
||||
rope(getClockStr()), content, nil, nil, nil])
|
||||
# no analytics because context is not available
|
||||
writeRope(code, getOutFile("theindex", HtmlExt))
|
||||
|
|
|
|||
|
|
@ -1,495 +0,0 @@
|
|||
#
|
||||
#
|
||||
# The Nim Compiler
|
||||
# (c) Copyright 2014 Andreas Rumpf
|
||||
#
|
||||
# See the file "copying.txt", included in this
|
||||
# distribution, for details about the copyright.
|
||||
#
|
||||
|
||||
## This file implements the FFI part of the evaluator for Nim code.
|
||||
|
||||
import ast, astalgo, ropes, types, options, tables, dynlib, libffi, msgs, os
|
||||
|
||||
when defined(windows):
|
||||
const libcDll = "msvcrt.dll"
|
||||
else:
|
||||
const libcDll = "libc.so(.6|.5|)"
|
||||
|
||||
type
|
||||
TDllCache = tables.TTable[string, TLibHandle]
|
||||
var
|
||||
gDllCache = initTable[string, TLibHandle]()
|
||||
|
||||
when defined(windows):
|
||||
var gExeHandle = loadLib(os.getAppFilename())
|
||||
else:
|
||||
var gExeHandle = loadLib()
|
||||
|
||||
proc getDll(cache: var TDllCache; dll: string; info: TLineInfo): pointer =
|
||||
result = cache[dll]
|
||||
if result.isNil:
|
||||
var libs: seq[string] = @[]
|
||||
libCandidates(dll, libs)
|
||||
for c in libs:
|
||||
result = loadLib(c)
|
||||
if not result.isNil: break
|
||||
if result.isNil:
|
||||
globalError(info, "cannot load: " & dll)
|
||||
cache[dll] = result
|
||||
|
||||
const
|
||||
nkPtrLit = nkIntLit # hopefully we can get rid of this hack soon
|
||||
|
||||
var myerrno {.importc: "errno", header: "<errno.h>".}: cint ## error variable
|
||||
|
||||
proc importcSymbol*(sym: PSym): PNode =
|
||||
let name = ropeToStr(sym.loc.r)
|
||||
|
||||
# the AST does not support untyped pointers directly, so we use an nkIntLit
|
||||
# that contains the address instead:
|
||||
result = newNodeIT(nkPtrLit, sym.info, sym.typ)
|
||||
case name
|
||||
of "stdin": result.intVal = cast[TAddress](system.stdin)
|
||||
of "stdout": result.intVal = cast[TAddress](system.stdout)
|
||||
of "stderr": result.intVal = cast[TAddress](system.stderr)
|
||||
of "vmErrnoWrapper": result.intVal = cast[TAddress](myerrno)
|
||||
else:
|
||||
let lib = sym.annex
|
||||
if lib != nil and lib.path.kind notin {nkStrLit..nkTripleStrLit}:
|
||||
globalError(sym.info, "dynlib needs to be a string lit for the REPL")
|
||||
var theAddr: pointer
|
||||
if lib.isNil and not gExehandle.isNil:
|
||||
# first try this exe itself:
|
||||
theAddr = gExehandle.symAddr(name)
|
||||
# then try libc:
|
||||
if theAddr.isNil:
|
||||
let dllhandle = gDllCache.getDll(libcDll, sym.info)
|
||||
theAddr = dllhandle.symAddr(name)
|
||||
elif not lib.isNil:
|
||||
let dllhandle = gDllCache.getDll(if lib.kind == libHeader: libcDll
|
||||
else: lib.path.strVal, sym.info)
|
||||
theAddr = dllhandle.symAddr(name)
|
||||
if theAddr.isNil: globalError(sym.info, "cannot import: " & sym.name.s)
|
||||
result.intVal = cast[TAddress](theAddr)
|
||||
|
||||
proc mapType(t: ast.PType): ptr libffi.TType =
|
||||
if t == nil: return addr libffi.type_void
|
||||
|
||||
case t.kind
|
||||
of tyBool, tyEnum, tyChar, tyInt..tyInt64, tyUInt..tyUInt64, tySet:
|
||||
case t.getSize
|
||||
of 1: result = addr libffi.type_uint8
|
||||
of 2: result = addr libffi.type_sint16
|
||||
of 4: result = addr libffi.type_sint32
|
||||
of 8: result = addr libffi.type_sint64
|
||||
else: result = nil
|
||||
of tyFloat, tyFloat64: result = addr libffi.type_double
|
||||
of tyFloat32: result = addr libffi.type_float
|
||||
of tyVar, tyPointer, tyPtr, tyRef, tyCString, tySequence, tyString, tyExpr,
|
||||
tyStmt, tyTypeDesc, tyProc, tyArray, tyArrayConstr, tyStatic, tyNil:
|
||||
result = addr libffi.type_pointer
|
||||
of tyDistinct:
|
||||
result = mapType(t.sons[0])
|
||||
else:
|
||||
result = nil
|
||||
# too risky:
|
||||
#of tyFloat128: result = addr libffi.type_longdouble
|
||||
|
||||
proc mapCallConv(cc: TCallingConvention, info: TLineInfo): TABI =
|
||||
case cc
|
||||
of ccDefault: result = DEFAULT_ABI
|
||||
of ccStdCall: result = when defined(windows): STDCALL else: DEFAULT_ABI
|
||||
of ccCDecl: result = DEFAULT_ABI
|
||||
else:
|
||||
globalError(info, "cannot map calling convention to FFI")
|
||||
|
||||
template rd(T, p: expr): expr {.immediate.} = (cast[ptr T](p))[]
|
||||
template wr(T, p, v: expr) {.immediate.} = (cast[ptr T](p))[] = v
|
||||
template `+!`(x, y: expr): expr {.immediate.} =
|
||||
cast[pointer](cast[TAddress](x) + y)
|
||||
|
||||
proc packSize(v: PNode, typ: PType): int =
|
||||
## computes the size of the blob
|
||||
case typ.kind
|
||||
of tyPtr, tyRef, tyVar:
|
||||
if v.kind in {nkNilLit, nkPtrLit}:
|
||||
result = sizeof(pointer)
|
||||
else:
|
||||
result = sizeof(pointer) + packSize(v.sons[0], typ.lastSon)
|
||||
of tyDistinct, tyGenericInst:
|
||||
result = packSize(v, typ.sons[0])
|
||||
of tyArray, tyArrayConstr:
|
||||
# consider: ptr array[0..1000_000, int] which is common for interfacing;
|
||||
# we use the real length here instead
|
||||
if v.kind in {nkNilLit, nkPtrLit}:
|
||||
result = sizeof(pointer)
|
||||
elif v.len != 0:
|
||||
result = v.len * packSize(v.sons[0], typ.sons[1])
|
||||
else:
|
||||
result = typ.getSize.int
|
||||
|
||||
proc pack(v: PNode, typ: PType, res: pointer)
|
||||
|
||||
proc getField(n: PNode; position: int): PSym =
|
||||
case n.kind
|
||||
of nkRecList:
|
||||
for i in countup(0, sonsLen(n) - 1):
|
||||
result = getField(n.sons[i], position)
|
||||
if result != nil: return
|
||||
of nkRecCase:
|
||||
result = getField(n.sons[0], position)
|
||||
if result != nil: return
|
||||
for i in countup(1, sonsLen(n) - 1):
|
||||
case n.sons[i].kind
|
||||
of nkOfBranch, nkElse:
|
||||
result = getField(lastSon(n.sons[i]), position)
|
||||
if result != nil: return
|
||||
else: internalError(n.info, "getField(record case branch)")
|
||||
of nkSym:
|
||||
if n.sym.position == position: result = n.sym
|
||||
else: discard
|
||||
|
||||
proc packObject(x: PNode, typ: PType, res: pointer) =
|
||||
internalAssert x.kind in {nkObjConstr, nkPar}
|
||||
# compute the field's offsets:
|
||||
discard typ.getSize
|
||||
for i in countup(ord(x.kind == nkObjConstr), sonsLen(x) - 1):
|
||||
var it = x.sons[i]
|
||||
if it.kind == nkExprColonExpr:
|
||||
internalAssert it.sons[0].kind == nkSym
|
||||
let field = it.sons[0].sym
|
||||
pack(it.sons[1], field.typ, res +! field.offset)
|
||||
elif typ.n != nil:
|
||||
let field = getField(typ.n, i)
|
||||
pack(it, field.typ, res +! field.offset)
|
||||
else:
|
||||
globalError(x.info, "cannot pack unnamed tuple")
|
||||
|
||||
const maxPackDepth = 20
|
||||
var packRecCheck = 0
|
||||
|
||||
proc pack(v: PNode, typ: PType, res: pointer) =
|
||||
template awr(T, v: expr) {.immediate, dirty.} =
|
||||
wr(T, res, v)
|
||||
|
||||
case typ.kind
|
||||
of tyBool: awr(bool, v.intVal != 0)
|
||||
of tyChar: awr(char, v.intVal.chr)
|
||||
of tyInt: awr(int, v.intVal.int)
|
||||
of tyInt8: awr(int8, v.intVal.int8)
|
||||
of tyInt16: awr(int16, v.intVal.int16)
|
||||
of tyInt32: awr(int32, v.intVal.int32)
|
||||
of tyInt64: awr(int64, v.intVal.int64)
|
||||
of tyUInt: awr(uint, v.intVal.uint)
|
||||
of tyUInt8: awr(uint8, v.intVal.uint8)
|
||||
of tyUInt16: awr(uint16, v.intVal.uint16)
|
||||
of tyUInt32: awr(uint32, v.intVal.uint32)
|
||||
of tyUInt64: awr(uint64, v.intVal.uint64)
|
||||
of tyEnum, tySet:
|
||||
case v.typ.getSize
|
||||
of 1: awr(uint8, v.intVal.uint8)
|
||||
of 2: awr(uint16, v.intVal.uint16)
|
||||
of 4: awr(int32, v.intVal.int32)
|
||||
of 8: awr(int64, v.intVal.int64)
|
||||
else:
|
||||
globalError(v.info, "cannot map value to FFI (tyEnum, tySet)")
|
||||
of tyFloat: awr(float, v.floatVal)
|
||||
of tyFloat32: awr(float32, v.floatVal)
|
||||
of tyFloat64: awr(float64, v.floatVal)
|
||||
|
||||
of tyPointer, tyProc, tyCString, tyString:
|
||||
if v.kind == nkNilLit:
|
||||
# nothing to do since the memory is 0 initialized anyway
|
||||
discard
|
||||
elif v.kind == nkPtrLit:
|
||||
awr(pointer, cast[pointer](v.intVal))
|
||||
elif v.kind in {nkStrLit..nkTripleStrLit}:
|
||||
awr(cstring, cstring(v.strVal))
|
||||
else:
|
||||
globalError(v.info, "cannot map pointer/proc value to FFI")
|
||||
of tyPtr, tyRef, tyVar:
|
||||
if v.kind == nkNilLit:
|
||||
# nothing to do since the memory is 0 initialized anyway
|
||||
discard
|
||||
elif v.kind == nkPtrLit:
|
||||
awr(pointer, cast[pointer](v.intVal))
|
||||
else:
|
||||
if packRecCheck > maxPackDepth:
|
||||
packRecCheck = 0
|
||||
globalError(v.info, "cannot map value to FFI " & typeToString(v.typ))
|
||||
inc packRecCheck
|
||||
pack(v.sons[0], typ.lastSon, res +! sizeof(pointer))
|
||||
dec packRecCheck
|
||||
awr(pointer, res +! sizeof(pointer))
|
||||
of tyArray, tyArrayConstr:
|
||||
let baseSize = typ.sons[1].getSize
|
||||
for i in 0 .. <v.len:
|
||||
pack(v.sons[i], typ.sons[1], res +! i * baseSize)
|
||||
of tyObject, tyTuple:
|
||||
packObject(v, typ, res)
|
||||
of tyNil:
|
||||
discard
|
||||
of tyDistinct, tyGenericInst:
|
||||
pack(v, typ.sons[0], res)
|
||||
else:
|
||||
globalError(v.info, "cannot map value to FFI " & typeToString(v.typ))
|
||||
|
||||
proc unpack(x: pointer, typ: PType, n: PNode): PNode
|
||||
|
||||
proc unpackObjectAdd(x: pointer, n, result: PNode) =
|
||||
case n.kind
|
||||
of nkRecList:
|
||||
for i in countup(0, sonsLen(n) - 1):
|
||||
unpackObjectAdd(x, n.sons[i], result)
|
||||
of nkRecCase:
|
||||
globalError(result.info, "case objects cannot be unpacked")
|
||||
of nkSym:
|
||||
var pair = newNodeI(nkExprColonExpr, result.info, 2)
|
||||
pair.sons[0] = n
|
||||
pair.sons[1] = unpack(x +! n.sym.offset, n.sym.typ, nil)
|
||||
#echo "offset: ", n.sym.name.s, " ", n.sym.offset
|
||||
result.add pair
|
||||
else: discard
|
||||
|
||||
proc unpackObject(x: pointer, typ: PType, n: PNode): PNode =
|
||||
# compute the field's offsets:
|
||||
discard typ.getSize
|
||||
|
||||
# iterate over any actual field of 'n' ... if n is nil we need to create
|
||||
# the nkPar node:
|
||||
if n.isNil:
|
||||
result = newNode(nkPar)
|
||||
result.typ = typ
|
||||
if typ.n.isNil:
|
||||
internalError("cannot unpack unnamed tuple")
|
||||
unpackObjectAdd(x, typ.n, result)
|
||||
else:
|
||||
result = n
|
||||
if result.kind notin {nkObjConstr, nkPar}:
|
||||
globalError(n.info, "cannot map value from FFI")
|
||||
if typ.n.isNil:
|
||||
globalError(n.info, "cannot unpack unnamed tuple")
|
||||
for i in countup(ord(n.kind == nkObjConstr), sonsLen(n) - 1):
|
||||
var it = n.sons[i]
|
||||
if it.kind == nkExprColonExpr:
|
||||
internalAssert it.sons[0].kind == nkSym
|
||||
let field = it.sons[0].sym
|
||||
it.sons[1] = unpack(x +! field.offset, field.typ, it.sons[1])
|
||||
else:
|
||||
let field = getField(typ.n, i)
|
||||
n.sons[i] = unpack(x +! field.offset, field.typ, it)
|
||||
|
||||
proc unpackArray(x: pointer, typ: PType, n: PNode): PNode =
|
||||
if n.isNil:
|
||||
result = newNode(nkBracket)
|
||||
result.typ = typ
|
||||
newSeq(result.sons, lengthOrd(typ).int)
|
||||
else:
|
||||
result = n
|
||||
if result.kind != nkBracket:
|
||||
globalError(n.info, "cannot map value from FFI")
|
||||
let baseSize = typ.sons[1].getSize
|
||||
for i in 0 .. < result.len:
|
||||
result.sons[i] = unpack(x +! i * baseSize, typ.sons[1], result.sons[i])
|
||||
|
||||
proc canonNodeKind(k: TNodeKind): TNodeKind =
|
||||
case k
|
||||
of nkCharLit..nkUInt64Lit: result = nkIntLit
|
||||
of nkFloatLit..nkFloat128Lit: result = nkFloatLit
|
||||
of nkStrLit..nkTripleStrLit: result = nkStrLit
|
||||
else: result = k
|
||||
|
||||
proc unpack(x: pointer, typ: PType, n: PNode): PNode =
|
||||
template aw(k, v, field: expr) {.immediate, dirty.} =
|
||||
if n.isNil:
|
||||
result = newNode(k)
|
||||
result.typ = typ
|
||||
else:
|
||||
# check we have the right field:
|
||||
result = n
|
||||
if result.kind.canonNodeKind != k.canonNodeKind:
|
||||
#echo "expected ", k, " but got ", result.kind
|
||||
#debug result
|
||||
return newNodeI(nkExceptBranch, n.info)
|
||||
#globalError(n.info, "cannot map value from FFI")
|
||||
result.field = v
|
||||
|
||||
template setNil() =
|
||||
if n.isNil:
|
||||
result = newNode(nkNilLit)
|
||||
result.typ = typ
|
||||
else:
|
||||
reset n[]
|
||||
result = n
|
||||
result.kind = nkNilLit
|
||||
result.typ = typ
|
||||
|
||||
template awi(kind, v: expr) {.immediate, dirty.} = aw(kind, v, intVal)
|
||||
template awf(kind, v: expr) {.immediate, dirty.} = aw(kind, v, floatVal)
|
||||
template aws(kind, v: expr) {.immediate, dirty.} = aw(kind, v, strVal)
|
||||
|
||||
case typ.kind
|
||||
of tyBool: awi(nkIntLit, rd(bool, x).ord)
|
||||
of tyChar: awi(nkCharLit, rd(char, x).ord)
|
||||
of tyInt: awi(nkIntLit, rd(int, x))
|
||||
of tyInt8: awi(nkInt8Lit, rd(int8, x))
|
||||
of tyInt16: awi(nkInt16Lit, rd(int16, x))
|
||||
of tyInt32: awi(nkInt32Lit, rd(int32, x))
|
||||
of tyInt64: awi(nkInt64Lit, rd(int64, x))
|
||||
of tyUInt: awi(nkUIntLit, rd(uint, x).BiggestInt)
|
||||
of tyUInt8: awi(nkUInt8Lit, rd(uint8, x).BiggestInt)
|
||||
of tyUInt16: awi(nkUInt16Lit, rd(uint16, x).BiggestInt)
|
||||
of tyUInt32: awi(nkUInt32Lit, rd(uint32, x).BiggestInt)
|
||||
of tyUInt64: awi(nkUInt64Lit, rd(uint64, x).BiggestInt)
|
||||
of tyEnum:
|
||||
case typ.getSize
|
||||
of 1: awi(nkIntLit, rd(uint8, x).BiggestInt)
|
||||
of 2: awi(nkIntLit, rd(uint16, x).BiggestInt)
|
||||
of 4: awi(nkIntLit, rd(int32, x).BiggestInt)
|
||||
of 8: awi(nkIntLit, rd(int64, x).BiggestInt)
|
||||
else:
|
||||
globalError(n.info, "cannot map value from FFI (tyEnum, tySet)")
|
||||
of tyFloat: awf(nkFloatLit, rd(float, x))
|
||||
of tyFloat32: awf(nkFloat32Lit, rd(float32, x))
|
||||
of tyFloat64: awf(nkFloat64Lit, rd(float64, x))
|
||||
of tyPointer, tyProc:
|
||||
let p = rd(pointer, x)
|
||||
if p.isNil:
|
||||
setNil()
|
||||
elif n != nil and n.kind == nkStrLit:
|
||||
# we passed a string literal as a pointer; however strings are already
|
||||
# in their unboxed representation so nothing it to be unpacked:
|
||||
result = n
|
||||
else:
|
||||
awi(nkPtrLit, cast[TAddress](p))
|
||||
of tyPtr, tyRef, tyVar:
|
||||
let p = rd(pointer, x)
|
||||
if p.isNil:
|
||||
setNil()
|
||||
elif n == nil or n.kind == nkPtrLit:
|
||||
awi(nkPtrLit, cast[TAddress](p))
|
||||
elif n != nil and n.len == 1:
|
||||
internalAssert n.kind == nkRefTy
|
||||
n.sons[0] = unpack(p, typ.lastSon, n.sons[0])
|
||||
result = n
|
||||
else:
|
||||
globalError(n.info, "cannot map value from FFI " & typeToString(typ))
|
||||
of tyObject, tyTuple:
|
||||
result = unpackObject(x, typ, n)
|
||||
of tyArray, tyArrayConstr:
|
||||
result = unpackArray(x, typ, n)
|
||||
of tyCString, tyString:
|
||||
let p = rd(cstring, x)
|
||||
if p.isNil:
|
||||
setNil()
|
||||
else:
|
||||
aws(nkStrLit, $p)
|
||||
of tyNil:
|
||||
setNil()
|
||||
of tyDistinct, tyGenericInst:
|
||||
result = unpack(x, typ.sons[0], n)
|
||||
else:
|
||||
# XXX what to do with 'array' here?
|
||||
globalError(n.info, "cannot map value from FFI " & typeToString(typ))
|
||||
|
||||
proc fficast*(x: PNode, destTyp: PType): PNode =
|
||||
if x.kind == nkPtrLit and x.typ.kind in {tyPtr, tyRef, tyVar, tyPointer,
|
||||
tyProc, tyCString, tyString,
|
||||
tySequence}:
|
||||
result = newNodeIT(x.kind, x.info, destTyp)
|
||||
result.intVal = x.intVal
|
||||
elif x.kind == nkNilLit:
|
||||
result = newNodeIT(x.kind, x.info, destTyp)
|
||||
else:
|
||||
# we play safe here and allocate the max possible size:
|
||||
let size = max(packSize(x, x.typ), packSize(x, destTyp))
|
||||
var a = alloc0(size)
|
||||
pack(x, x.typ, a)
|
||||
# cast through a pointer needs a new inner object:
|
||||
let y = if x.kind == nkRefTy: newNodeI(nkRefTy, x.info, 1)
|
||||
else: x.copyTree
|
||||
y.typ = x.typ
|
||||
result = unpack(a, destTyp, y)
|
||||
dealloc a
|
||||
|
||||
proc callForeignFunction*(call: PNode): PNode =
|
||||
internalAssert call.sons[0].kind == nkPtrLit
|
||||
|
||||
var cif: TCif
|
||||
var sig: TParamList
|
||||
# use the arguments' types for varargs support:
|
||||
for i in 1..call.len-1:
|
||||
sig[i-1] = mapType(call.sons[i].typ)
|
||||
if sig[i-1].isNil:
|
||||
globalError(call.info, "cannot map FFI type")
|
||||
|
||||
let typ = call.sons[0].typ
|
||||
if prep_cif(cif, mapCallConv(typ.callConv, call.info), cuint(call.len-1),
|
||||
mapType(typ.sons[0]), sig) != OK:
|
||||
globalError(call.info, "error in FFI call")
|
||||
|
||||
var args: TArgList
|
||||
let fn = cast[pointer](call.sons[0].intVal)
|
||||
for i in 1 .. call.len-1:
|
||||
var t = call.sons[i].typ
|
||||
args[i-1] = alloc0(packSize(call.sons[i], t))
|
||||
pack(call.sons[i], t, args[i-1])
|
||||
let retVal = if isEmptyType(typ.sons[0]): pointer(nil)
|
||||
else: alloc(typ.sons[0].getSize.int)
|
||||
|
||||
libffi.call(cif, fn, retVal, args)
|
||||
|
||||
if retVal.isNil:
|
||||
result = emptyNode
|
||||
else:
|
||||
result = unpack(retVal, typ.sons[0], nil)
|
||||
result.info = call.info
|
||||
|
||||
if retVal != nil: dealloc retVal
|
||||
for i in 1 .. call.len-1:
|
||||
call.sons[i] = unpack(args[i-1], typ.sons[i], call[i])
|
||||
dealloc args[i-1]
|
||||
|
||||
proc callForeignFunction*(fn: PNode, fntyp: PType,
|
||||
args: var TNodeSeq, start, len: int,
|
||||
info: TLineInfo): PNode =
|
||||
internalAssert fn.kind == nkPtrLit
|
||||
|
||||
var cif: TCif
|
||||
var sig: TParamList
|
||||
for i in 0..len-1:
|
||||
var aTyp = args[i+start].typ
|
||||
if aTyp.isNil:
|
||||
internalAssert i+1 < fntyp.len
|
||||
aTyp = fntyp.sons[i+1]
|
||||
args[i+start].typ = aTyp
|
||||
sig[i] = mapType(aTyp)
|
||||
if sig[i].isNil: globalError(info, "cannot map FFI type")
|
||||
|
||||
if prep_cif(cif, mapCallConv(fntyp.callConv, info), cuint(len),
|
||||
mapType(fntyp.sons[0]), sig) != OK:
|
||||
globalError(info, "error in FFI call")
|
||||
|
||||
var cargs: TArgList
|
||||
let fn = cast[pointer](fn.intVal)
|
||||
for i in 0 .. len-1:
|
||||
let t = args[i+start].typ
|
||||
cargs[i] = alloc0(packSize(args[i+start], t))
|
||||
pack(args[i+start], t, cargs[i])
|
||||
let retVal = if isEmptyType(fntyp.sons[0]): pointer(nil)
|
||||
else: alloc(fntyp.sons[0].getSize.int)
|
||||
|
||||
libffi.call(cif, fn, retVal, cargs)
|
||||
|
||||
if retVal.isNil:
|
||||
result = emptyNode
|
||||
else:
|
||||
result = unpack(retVal, fntyp.sons[0], nil)
|
||||
result.info = info
|
||||
|
||||
if retVal != nil: dealloc retVal
|
||||
for i in 0 .. len-1:
|
||||
let t = args[i+start].typ
|
||||
args[i+start] = unpack(cargs[i], t, args[i+start])
|
||||
dealloc cargs[i]
|
||||
|
|
@ -29,7 +29,7 @@ proc evalTemplateAux(templ, actual: PNode, c: var TemplCtx, result: PNode) =
|
|||
of nkSym:
|
||||
var s = templ.sym
|
||||
if s.owner.id == c.owner.id:
|
||||
if s.kind == skParam:
|
||||
if s.kind == skParam and sfGenSym notin s.flags:
|
||||
let x = actual.sons[s.position]
|
||||
if x.kind == nkArgList:
|
||||
for y in items(x): result.add(y)
|
||||
|
|
@ -93,7 +93,7 @@ proc evalTemplate*(n: PNode, tmpl, genSymOwner: PSym): PNode =
|
|||
evalTemplateAux(body, args, ctx, result)
|
||||
if result.len == 1: result = result.sons[0]
|
||||
else:
|
||||
globalError(result.info, errIllFormedAstX,
|
||||
localError(result.info, errIllFormedAstX,
|
||||
renderTree(result, {renderNoComments}))
|
||||
else:
|
||||
result = copyNode(body)
|
||||
|
|
|
|||
|
|
@ -332,7 +332,6 @@ const
|
|||
ucc(),
|
||||
icl()]
|
||||
|
||||
const
|
||||
hExt* = ".h"
|
||||
|
||||
var
|
||||
|
|
@ -364,7 +363,16 @@ proc nameToCC*(name: string): TSystemCC =
|
|||
proc getConfigVar(c: TSystemCC, suffix: string): string =
|
||||
# use ``cpu.os.cc`` for cross compilation, unless ``--compileOnly`` is given
|
||||
# for niminst support
|
||||
let fullSuffix = (if gCmd == cmdCompileToCpp: ".cpp" & suffix else: suffix)
|
||||
let fullSuffix =
|
||||
if gCmd == cmdCompileToCpp:
|
||||
".cpp" & suffix
|
||||
elif gCmd == cmdCompileToOC:
|
||||
".objc" & suffix
|
||||
elif gCmd == cmdCompileToJS:
|
||||
".js" & suffix
|
||||
else:
|
||||
suffix
|
||||
|
||||
if (platform.hostOS != targetOS or platform.hostCPU != targetCPU) and
|
||||
optCompileOnly notin gGlobalOptions:
|
||||
let fullCCname = platform.CPU[targetCPU].name & '.' &
|
||||
|
|
@ -381,7 +389,7 @@ proc setCC*(ccname: string) =
|
|||
cCompiler = nameToCC(ccname)
|
||||
if cCompiler == ccNone: rawMessage(errUnknownCcompiler, ccname)
|
||||
compileOptions = getConfigVar(cCompiler, ".options.always")
|
||||
linkOptions = getConfigVar(cCompiler, ".options.linker")
|
||||
linkOptions = ""
|
||||
ccompilerpath = getConfigVar(cCompiler, ".path")
|
||||
for i in countup(low(CC), high(CC)): undefSymbol(CC[i].name)
|
||||
defineSymbol(CC[cCompiler].name)
|
||||
|
|
@ -391,7 +399,7 @@ proc addOpt(dest: var string, src: string) =
|
|||
add(dest, src)
|
||||
|
||||
proc addLinkOption*(option: string) =
|
||||
if find(linkOptions, option, 0) < 0: addOpt(linkOptions, option)
|
||||
addOpt(linkOptions, option)
|
||||
|
||||
proc addCompileOption*(option: string) =
|
||||
if strutils.find(compileOptions, option, 0) < 0:
|
||||
|
|
@ -402,7 +410,7 @@ proc initVars*() =
|
|||
for i in countup(low(CC), high(CC)): undefSymbol(CC[i].name)
|
||||
defineSymbol(CC[cCompiler].name)
|
||||
addCompileOption(getConfigVar(cCompiler, ".options.always"))
|
||||
addLinkOption(getConfigVar(cCompiler, ".options.linker"))
|
||||
#addLinkOption(getConfigVar(cCompiler, ".options.linker"))
|
||||
if len(ccompilerpath) == 0:
|
||||
ccompilerpath = getConfigVar(cCompiler, ".path")
|
||||
|
||||
|
|
@ -429,15 +437,19 @@ proc addFileToLink*(filename: string) =
|
|||
prependStr(toLink, filename)
|
||||
# BUGFIX: was ``appendStr``
|
||||
|
||||
proc execExternalProgram*(cmd: string, prettyCmd = "") =
|
||||
proc execWithEcho(cmd: string, prettyCmd = ""): int =
|
||||
if optListCmd in gGlobalOptions or gVerbosity > 0:
|
||||
if prettyCmd != "":
|
||||
msgWriteln(prettyCmd)
|
||||
else:
|
||||
msgWriteln(cmd)
|
||||
if execCmd(cmd) != 0: rawMessage(errExecutionOfProgramFailed, "")
|
||||
result = execCmd(cmd)
|
||||
|
||||
proc generateScript(projectFile: string, script: PRope) =
|
||||
proc execExternalProgram*(cmd: string, prettyCmd = "") =
|
||||
if execWithEcho(cmd, prettyCmd) != 0:
|
||||
rawMessage(errExecutionOfProgramFailed, "")
|
||||
|
||||
proc generateScript(projectFile: string, script: Rope) =
|
||||
let (dir, name, ext) = splitFile(projectFile)
|
||||
writeRope(script, dir / addFileExt("compile_" & name,
|
||||
platform.OS[targetOS].scriptExt))
|
||||
|
|
@ -547,15 +559,16 @@ proc getCompileCFileCmd*(cfilename: string, isExternal = false): string =
|
|||
else:
|
||||
completeCFilePath(toObjFile(cfile))
|
||||
objfile = quoteShell(objfile)
|
||||
cfile = quoteShell(cfile)
|
||||
result = quoteShell(compilePattern % [
|
||||
"file", cfile, "objfile", objfile, "options", options,
|
||||
"include", includeCmd, "nimrod", getPrefixDir(),
|
||||
"include", includeCmd, "nim", getPrefixDir(),
|
||||
"nim", getPrefixDir(), "lib", libpath])
|
||||
add(result, ' ')
|
||||
addf(result, CC[c].compileTmpl, [
|
||||
"file", cfile, "objfile", objfile,
|
||||
"options", options, "include", includeCmd,
|
||||
"nimrod", quoteShell(getPrefixDir()),
|
||||
"nim", quoteShell(getPrefixDir()),
|
||||
"nim", quoteShell(getPrefixDir()),
|
||||
"lib", quoteShell(libpath)])
|
||||
|
||||
|
|
@ -568,6 +581,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
|
||||
|
|
@ -588,7 +604,7 @@ proc addExternalFileToCompile*(filename: string) =
|
|||
if optForceFullMake in gGlobalOptions or externalFileChanged(filename):
|
||||
appendStr(externalToCompile, filename)
|
||||
|
||||
proc compileCFile(list: TLinkedList, script: var PRope, cmds: var TStringSeq,
|
||||
proc compileCFile(list: TLinkedList, script: var Rope, cmds: var TStringSeq,
|
||||
prettyCmds: var TStringSeq, isExternal: bool) =
|
||||
var it = PStrEntry(list.head)
|
||||
while it != nil:
|
||||
|
|
@ -599,8 +615,8 @@ proc compileCFile(list: TLinkedList, script: var PRope, cmds: var TStringSeq,
|
|||
let (dir, name, ext) = splitFile(it.data)
|
||||
add(prettyCmds, "CC: " & name)
|
||||
if optGenScript in gGlobalOptions:
|
||||
app(script, compileCmd)
|
||||
app(script, tnl)
|
||||
add(script, compileCmd)
|
||||
add(script, tnl)
|
||||
it = PStrEntry(it.next)
|
||||
|
||||
proc callCCompiler*(projectfile: string) =
|
||||
|
|
@ -611,7 +627,7 @@ proc callCCompiler*(projectfile: string) =
|
|||
# generated
|
||||
fileCounter = 0
|
||||
var c = cCompiler
|
||||
var script: PRope = nil
|
||||
var script: Rope = nil
|
||||
var cmds: TStringSeq = @[]
|
||||
var prettyCmds: TStringSeq = @[]
|
||||
let prettyCb = proc (idx: int) =
|
||||
|
|
@ -622,15 +638,17 @@ proc callCCompiler*(projectfile: string) =
|
|||
if gNumberOfProcessors == 0: gNumberOfProcessors = countProcessors()
|
||||
var res = 0
|
||||
if gNumberOfProcessors <= 1:
|
||||
for i in countup(0, high(cmds)): res = max(execCmd(cmds[i]), res)
|
||||
for i in countup(0, high(cmds)):
|
||||
res = execWithEcho(cmds[i])
|
||||
if res != 0: rawMessage(errExecutionOfProgramFailed, [])
|
||||
elif optListCmd in gGlobalOptions or gVerbosity > 1:
|
||||
res = execProcesses(cmds, {poEchoCmd, poUseShell, poParentStreams},
|
||||
res = execProcesses(cmds, {poEchoCmd, poUsePath, poParentStreams},
|
||||
gNumberOfProcessors)
|
||||
elif gVerbosity == 1:
|
||||
res = execProcesses(cmds, {poUseShell, poParentStreams},
|
||||
res = execProcesses(cmds, {poUsePath, poParentStreams},
|
||||
gNumberOfProcessors, prettyCb)
|
||||
else:
|
||||
res = execProcesses(cmds, {poUseShell, poParentStreams},
|
||||
res = execProcesses(cmds, {poUsePath, poParentStreams},
|
||||
gNumberOfProcessors)
|
||||
if res != 0:
|
||||
if gNumberOfProcessors <= 1:
|
||||
|
|
@ -673,15 +691,16 @@ proc callCCompiler*(projectfile: string) =
|
|||
if not exefile.isAbsolute():
|
||||
exefile = joinPath(splitFile(projectfile).dir, exefile)
|
||||
exefile = quoteShell(exefile)
|
||||
let linkOptions = getLinkOptions()
|
||||
let linkOptions = getLinkOptions() & " " &
|
||||
getConfigVar(cCompiler, ".options.linker")
|
||||
linkCmd = quoteShell(linkCmd % ["builddll", builddll,
|
||||
"buildgui", buildgui, "options", linkOptions, "objfiles", objfiles,
|
||||
"exefile", exefile, "nimrod", getPrefixDir(), "lib", libpath])
|
||||
"exefile", exefile, "nim", getPrefixDir(), "lib", libpath])
|
||||
linkCmd.add ' '
|
||||
addf(linkCmd, CC[c].linkTmpl, ["builddll", builddll,
|
||||
"buildgui", buildgui, "options", linkOptions,
|
||||
"objfiles", objfiles, "exefile", exefile,
|
||||
"nimrod", quoteShell(getPrefixDir()),
|
||||
"nim", quoteShell(getPrefixDir()),
|
||||
"lib", quoteShell(libpath)])
|
||||
if optCompileOnly notin gGlobalOptions:
|
||||
if gVerbosity == 1:
|
||||
|
|
@ -691,30 +710,30 @@ proc callCCompiler*(projectfile: string) =
|
|||
else:
|
||||
linkCmd = ""
|
||||
if optGenScript in gGlobalOptions:
|
||||
app(script, linkCmd)
|
||||
app(script, tnl)
|
||||
add(script, linkCmd)
|
||||
add(script, tnl)
|
||||
generateScript(projectfile, script)
|
||||
|
||||
proc genMappingFiles(list: TLinkedList): PRope =
|
||||
proc genMappingFiles(list: TLinkedList): Rope =
|
||||
var it = PStrEntry(list.head)
|
||||
while it != nil:
|
||||
appf(result, "--file:r\"$1\"$N", [toRope(it.data)])
|
||||
addf(result, "--file:r\"$1\"$N", [rope(it.data)])
|
||||
it = PStrEntry(it.next)
|
||||
|
||||
proc writeMapping*(gSymbolMapping: PRope) =
|
||||
proc writeMapping*(gSymbolMapping: Rope) =
|
||||
if optGenMapping notin gGlobalOptions: return
|
||||
var code = toRope("[C_Files]\n")
|
||||
app(code, genMappingFiles(toCompile))
|
||||
app(code, genMappingFiles(externalToCompile))
|
||||
app(code, "\n[C_Compiler]\nFlags=")
|
||||
app(code, strutils.escape(getCompileOptions()))
|
||||
var code = rope("[C_Files]\n")
|
||||
add(code, genMappingFiles(toCompile))
|
||||
add(code, genMappingFiles(externalToCompile))
|
||||
add(code, "\n[C_Compiler]\nFlags=")
|
||||
add(code, strutils.escape(getCompileOptions()))
|
||||
|
||||
app(code, "\n[Linker]\nFlags=")
|
||||
app(code, strutils.escape(getLinkOptions()))
|
||||
add(code, "\n[Linker]\nFlags=")
|
||||
add(code, strutils.escape(getLinkOptions() & " " &
|
||||
getConfigVar(cCompiler, ".options.linker")))
|
||||
|
||||
app(code, "\n[Environment]\nlibpath=")
|
||||
app(code, strutils.escape(libpath))
|
||||
add(code, "\n[Environment]\nlibpath=")
|
||||
add(code, strutils.escape(libpath))
|
||||
|
||||
appf(code, "\n[Symbols]$n$1", [gSymbolMapping])
|
||||
addf(code, "\n[Symbols]$n$1", [gSymbolMapping])
|
||||
writeRope(code, joinPath(gProjectPath, "mapping.txt"))
|
||||
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
89
compiler/forloops.nim
Normal file
89
compiler/forloops.nim
Normal file
|
|
@ -0,0 +1,89 @@
|
|||
#
|
||||
#
|
||||
# The Nim Compiler
|
||||
# (c) Copyright 2015 Andreas Rumpf
|
||||
#
|
||||
# See the file "copying.txt", included in this
|
||||
# distribution, for details about the copyright.
|
||||
#
|
||||
|
||||
## This module implements for loop detection for better C code generation.
|
||||
|
||||
import ast, astalgo
|
||||
|
||||
const
|
||||
someCmp = {mEqI, mEqI64, mEqF64, mEqEnum, mEqCh, mEqB, mEqRef, mEqProc,
|
||||
mEqUntracedRef, mLeI, mLeI64, mLeF64, mLeU, mLeU64, mLeEnum,
|
||||
mLeCh, mLeB, mLePtr, mLtI, mLtI64, mLtF64, mLtU, mLtU64, mLtEnum,
|
||||
mLtCh, mLtB, mLtPtr}
|
||||
|
||||
proc isCounter(s: PSym): bool {.inline.} =
|
||||
s.kind in {skResult, skVar, skLet, skTemp} and
|
||||
{sfGlobal, sfAddrTaken} * s.flags == {}
|
||||
|
||||
proc isCall(n: PNode): bool {.inline.} =
|
||||
n.kind in nkCallKinds and n[0].kind == nkSym
|
||||
|
||||
proc fromSystem(op: PSym): bool = sfSystemModule in getModule(op).flags
|
||||
|
||||
proc getCounter(lastStmt: PNode): PSym =
|
||||
if lastStmt.isCall:
|
||||
let op = lastStmt.sym
|
||||
if op.magic in {mDec, mInc} or
|
||||
((op.name.s == "+=" or op.name.s == "-=") and op.fromSystem):
|
||||
if op[1].kind == nkSym and isCounter(op[1].sym):
|
||||
result = op[1].sym
|
||||
|
||||
proc counterInTree(n, loop: PNode; counter: PSym): bool =
|
||||
# prune the search tree: within the loop the counter may be used:
|
||||
if n == loop: return
|
||||
case n.kind
|
||||
of nkSym:
|
||||
if n.sym == counter: return true
|
||||
of nkVarSection, nkLetSection:
|
||||
# definitions are fine!
|
||||
for it in n:
|
||||
if counterInTree(it.lastSon): return true
|
||||
else:
|
||||
for i in 0 .. <safeLen(n):
|
||||
if counterInTree(n[i], loop, counter): return true
|
||||
|
||||
proc copyExcept(n: PNode, x, dest: PNode) =
|
||||
if x == n: return
|
||||
if n.kind in {nkStmtList, nkStmtListExpr}:
|
||||
for i in 0 .. <n.len: copyExcept(n[i], x, dest)
|
||||
else:
|
||||
dest.add n
|
||||
|
||||
type
|
||||
ForLoop* = object
|
||||
counter*: PSym
|
||||
init*, cond*, increment*, body*: PNode
|
||||
|
||||
proc extractForLoop*(loop, fullTree: PNode): ForLoop =
|
||||
## returns 'counter == nil' if the while loop 'n' is not a for loop:
|
||||
assert loop.kind == nkWhileStmt
|
||||
let cond == loop[0]
|
||||
|
||||
if not cond.isCall: return
|
||||
if cond[0].sym.magic notin someCmp: return
|
||||
|
||||
var lastStmt = loop[1]
|
||||
while lastStmt.kind in {nkStmtList, nkStmtListExpr}:
|
||||
lastStmt = lastStmt.lastSon
|
||||
|
||||
let counter = getCounter(lastStmt)
|
||||
if counter.isNil or counter.ast.isNil: return
|
||||
|
||||
template `=~`(a, b): expr = a.kind == nkSym and a.sym == b
|
||||
|
||||
if cond[1] =~ counter or cond[2] =~ counter:
|
||||
# ok, now check 'counter' is not used *after* the loop
|
||||
if counterInTree(fullTree, loop, counter): return
|
||||
# ok, success, fill in the fields:
|
||||
result.counter = counter
|
||||
result.init = counter.ast
|
||||
result.cond = cond
|
||||
result.increment = lastStmt
|
||||
result.body = newNodeI(nkStmtList, loop[1].info)
|
||||
copyExcept(loop[1], lastStmt, result.body)
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
#
|
||||
#
|
||||
# The Nim Compiler
|
||||
# (c) Copyright 2014 Andreas Rumpf
|
||||
# (c) Copyright 2015 Andreas Rumpf
|
||||
#
|
||||
# See the file "copying.txt", included in this
|
||||
# distribution, for details about the copyright.
|
||||
|
|
@ -22,7 +22,8 @@ const
|
|||
someLt = {mLtI, mLtI64, mLtF64, mLtU, mLtU64, mLtEnum,
|
||||
mLtCh, mLtB, mLtPtr, mLtStr}
|
||||
|
||||
someLen = {mLengthOpenArray, mLengthStr, mLengthArray, mLengthSeq}
|
||||
someLen = {mLengthOpenArray, mLengthStr, mLengthArray, mLengthSeq,
|
||||
mXLenStr, mXLenSeq}
|
||||
|
||||
someIn = {mInRange, mInSet}
|
||||
|
||||
|
|
@ -34,8 +35,8 @@ const
|
|||
someMul = {mMulI, mMulI64, mMulF64}
|
||||
someDiv = {mDivI, mDivI64, mDivF64}
|
||||
someMod = {mModI, mModI64}
|
||||
someMax = {mMaxI, mMaxI64, mMaxF64}
|
||||
someMin = {mMinI, mMinI64, mMinF64}
|
||||
someMax = {mMaxI, mMaxF64}
|
||||
someMin = {mMinI, mMinF64}
|
||||
|
||||
proc isValue(n: PNode): bool = n.kind in {nkCharLit..nkNilLit}
|
||||
proc isLocation(n: PNode): bool = not n.isValue
|
||||
|
|
@ -81,18 +82,12 @@ proc isLetLocation(m: PNode, isApprox: bool): bool =
|
|||
|
||||
proc interestingCaseExpr*(m: PNode): bool = isLetLocation(m, true)
|
||||
|
||||
proc createMagic*(name: string, m: TMagic): PSym =
|
||||
result = newSym(skProc, getIdent(name), nil, unknownLineInfo())
|
||||
result.magic = m
|
||||
|
||||
let
|
||||
opLe = createMagic("<=", mLeI)
|
||||
opLt = createMagic("<", mLtI)
|
||||
opAnd = createMagic("and", mAnd)
|
||||
opOr = createMagic("or", mOr)
|
||||
opNot = createMagic("not", mNot)
|
||||
opIsNil = createMagic("isnil", mIsNil)
|
||||
opContains = createMagic("contains", mInSet)
|
||||
opEq = createMagic("==", mEqI)
|
||||
opAdd = createMagic("+", mAddI)
|
||||
opSub = createMagic("-", mSubI)
|
||||
|
|
@ -128,7 +123,7 @@ proc neg(n: PNode): PNode =
|
|||
let eAsNode = newIntNode(nkIntLit, e.sym.position)
|
||||
if not inSet(n.sons[1], eAsNode): s.add eAsNode
|
||||
result.sons[1] = s
|
||||
elif lengthOrd(t) < 1000:
|
||||
elif t.kind notin {tyString, tySequence} and lengthOrd(t) < 1000:
|
||||
result.sons[1] = complement(n.sons[1])
|
||||
else:
|
||||
# not ({2, 3, 4}.contains(x)) x != 2 and x != 3 and x != 4
|
||||
|
|
@ -200,8 +195,12 @@ proc lowBound*(x: PNode): PNode =
|
|||
result.info = x.info
|
||||
|
||||
proc highBound*(x: PNode): PNode =
|
||||
result = if x.typ.skipTypes(abstractInst).kind == tyArray:
|
||||
nkIntLit.newIntNode(lastOrd(x.typ))
|
||||
let typ = x.typ.skipTypes(abstractInst)
|
||||
result = if typ.kind in {tyArrayConstr, tyArray}:
|
||||
nkIntLit.newIntNode(lastOrd(typ))
|
||||
elif typ.kind == tySequence and x.kind == nkSym and
|
||||
x.sym.kind == skConst:
|
||||
nkIntLit.newIntNode(x.sym.ast.len-1)
|
||||
else:
|
||||
opAdd.buildCall(opLen.buildCall(x), minusOne())
|
||||
result.info = x.info
|
||||
|
|
@ -220,12 +219,23 @@ proc reassociation(n: PNode): PNode =
|
|||
result = opAdd.buildCall(result[1][1], result[1][2] |*| result[2])
|
||||
else: discard
|
||||
|
||||
proc pred(n: PNode): PNode =
|
||||
if n.kind in {nkCharLit..nkUInt64Lit} and n.intVal != low(BiggestInt):
|
||||
result = copyNode(n)
|
||||
dec result.intVal
|
||||
else:
|
||||
result = n
|
||||
|
||||
proc canon*(n: PNode): PNode =
|
||||
# XXX for now only the new code in 'semparallel' uses this
|
||||
if n.safeLen >= 1:
|
||||
result = shallowCopy(n)
|
||||
for i in 0 .. < n.len:
|
||||
result.sons[i] = canon(n.sons[i])
|
||||
elif n.kind == nkSym and n.sym.kind == skLet and
|
||||
n.sym.ast.getMagic in (someEq + someAdd + someMul + someMin +
|
||||
someMax + someHigh + {mUnaryLt} + someSub + someLen):
|
||||
result = n.sym.ast.copyTree
|
||||
else:
|
||||
result = n
|
||||
case result.getMagic
|
||||
|
|
@ -237,20 +247,26 @@ proc canon*(n: PNode): PNode =
|
|||
of someHigh:
|
||||
# high == len+(-1)
|
||||
result = opAdd.buildCall(opLen.buildCall(result[1]), minusOne())
|
||||
of mUnaryMinusI, mUnaryMinusI64:
|
||||
of mUnaryLt:
|
||||
result = buildCall(opAdd, result[1], newIntNode(nkIntLit, -1))
|
||||
of someSub:
|
||||
# x - 4 --> x + (-4)
|
||||
result = negate(result[1], result[2], result)
|
||||
of someLen:
|
||||
result.sons[0] = opLen.newSymNode
|
||||
of someLt:
|
||||
# x < y same as x <= y-1:
|
||||
let y = n[2].canon
|
||||
let p = pred(y)
|
||||
let minus = if p != y: p else: opAdd.buildCall(y, minusOne()).canon
|
||||
result = opLe.buildCall(n[1].canon, minus)
|
||||
else: discard
|
||||
|
||||
result = skipConv(result)
|
||||
result = reassociation(result)
|
||||
# most important rule: (x-4) < a.len --> x < a.len+4
|
||||
# most important rule: (x-4) <= a.len --> x <= a.len+4
|
||||
case result.getMagic
|
||||
of someLe, someLt:
|
||||
of someLe:
|
||||
let x = result[1]
|
||||
let y = result[2]
|
||||
if x.kind in nkCallKinds and x.len == 3 and x[2].isValue and
|
||||
|
|
@ -358,13 +374,28 @@ proc addFactNeg*(m: var TModel, n: PNode) =
|
|||
let n = n.neg
|
||||
if n != nil: addFact(m, n)
|
||||
|
||||
proc canonOpr(opr: PSym): PSym =
|
||||
case opr.magic
|
||||
of someEq: result = opEq
|
||||
of someLe: result = opLe
|
||||
of someLt: result = opLt
|
||||
of someLen: result = opLen
|
||||
of someAdd: result = opAdd
|
||||
of someSub: result = opSub
|
||||
of someMul: result = opMul
|
||||
of someDiv: result = opDiv
|
||||
else: result = opr
|
||||
|
||||
proc sameTree*(a, b: PNode): bool =
|
||||
result = false
|
||||
if a == b:
|
||||
result = true
|
||||
elif (a != nil) and (b != nil) and (a.kind == b.kind):
|
||||
elif a != nil and b != nil and a.kind == b.kind:
|
||||
case a.kind
|
||||
of nkSym: result = a.sym == b.sym
|
||||
of nkSym:
|
||||
result = a.sym == b.sym
|
||||
if not result and a.sym.magic != mNone:
|
||||
result = a.sym.magic == b.sym.magic or canonOpr(a.sym) == canonOpr(b.sym)
|
||||
of nkIdent: result = a.ident.id == b.ident.id
|
||||
of nkCharLit..nkInt64Lit: result = a.intVal == b.intVal
|
||||
of nkFloatLit..nkFloat64Lit: result = a.floatVal == b.floatVal
|
||||
|
|
@ -408,13 +439,6 @@ proc valuesUnequal(a, b: PNode): bool =
|
|||
if a.isValue and b.isValue:
|
||||
result = not sameValue(a, b)
|
||||
|
||||
proc pred(n: PNode): PNode =
|
||||
if n.kind in {nkCharLit..nkUInt64Lit} and n.intVal != low(BiggestInt):
|
||||
result = copyNode(n)
|
||||
dec result.intVal
|
||||
else:
|
||||
result = n
|
||||
|
||||
proc impliesEq(fact, eq: PNode): TImplication =
|
||||
let (loc, val) = if isLocation(eq.sons[1]): (1, 2) else: (2, 1)
|
||||
|
||||
|
|
@ -635,7 +659,7 @@ proc factImplies(fact, prop: PNode): TImplication =
|
|||
if a == b: return ~a
|
||||
return impUnknown
|
||||
else:
|
||||
internalError(fact.info, "invalid fact")
|
||||
return impUnknown
|
||||
of mAnd:
|
||||
result = factImplies(fact.sons[1], prop)
|
||||
if result != impUnknown: return result
|
||||
|
|
@ -649,7 +673,7 @@ proc factImplies(fact, prop: PNode): TImplication =
|
|||
of someLe: result = impliesLe(fact, prop.sons[1], prop.sons[2])
|
||||
of someLt: result = impliesLt(fact, prop.sons[1], prop.sons[2])
|
||||
of mInSet: result = impliesIn(fact, prop.sons[2], prop.sons[1])
|
||||
else: internalError(prop.info, "invalid proposition")
|
||||
else: result = impUnknown
|
||||
|
||||
proc doesImply*(facts: TModel, prop: PNode): TImplication =
|
||||
assert prop.kind in nkCallKinds
|
||||
|
|
@ -677,6 +701,7 @@ proc pleViaModel(model: TModel; aa, bb: PNode): TImplication
|
|||
|
||||
proc ple(m: TModel; a, b: PNode): TImplication =
|
||||
template `<=?`(a,b): expr = ple(m,a,b) == impYes
|
||||
|
||||
# 0 <= 3
|
||||
if a.isValue and b.isValue:
|
||||
return if leValue(a, b): impYes else: impNo
|
||||
|
|
@ -750,16 +775,21 @@ proc pleViaModelRec(m: var TModel; a, b: PNode): TImplication =
|
|||
# mark as used:
|
||||
m[i] = nil
|
||||
if ple(m, a, x) == impYes:
|
||||
if ple(m, y, b) == impYes: return impYes
|
||||
if ple(m, y, b) == impYes:
|
||||
return impYes
|
||||
#if pleViaModelRec(m, y, b): return impYes
|
||||
# fact: 16 <= i
|
||||
# x y
|
||||
# question: i <= 15? no!
|
||||
result = impliesLe(fact, a, b)
|
||||
if result != impUnknown: return result
|
||||
if sameTree(y, a):
|
||||
result = ple(m, b, x)
|
||||
if result != impUnknown: return result
|
||||
if result != impUnknown:
|
||||
return result
|
||||
when false:
|
||||
# given: x <= y; y==a; x <= a this means: a <= b if x <= b
|
||||
if sameTree(y, a):
|
||||
result = ple(m, b, x)
|
||||
if result != impUnknown:
|
||||
return result
|
||||
|
||||
proc pleViaModel(model: TModel; aa, bb: PNode): TImplication =
|
||||
# compute replacements:
|
||||
|
|
@ -878,5 +908,5 @@ proc buildProperFieldCheck(access, check: PNode): PNode =
|
|||
proc checkFieldAccess*(m: TModel, n: PNode) =
|
||||
for i in 1..n.len-1:
|
||||
let check = buildProperFieldCheck(n.sons[0], n.sons[i])
|
||||
if m.doesImply(check) != impYes:
|
||||
if check != nil and m.doesImply(check) != impYes:
|
||||
message(n.info, warnProveField, renderTree(n.sons[0])); break
|
||||
|
|
|
|||
|
|
@ -25,7 +25,7 @@ type
|
|||
next*: PIdent # for hash-table chaining
|
||||
h*: THash # hash value of s
|
||||
|
||||
var firstCharIsCS*: bool
|
||||
var firstCharIsCS*: bool = true
|
||||
var buckets*: array[0..4096 * 2 - 1, PIdent]
|
||||
|
||||
proc cmpIgnoreStyle(a, b: cstring, blen: int): int =
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -143,7 +143,7 @@ proc importForwarded(c: PContext, n: PNode, exceptSet: IntSet) =
|
|||
of nkExportExceptStmt:
|
||||
localError(n.info, errGenerated, "'export except' not implemented")
|
||||
else:
|
||||
for i in 0 ..safeLen(n)-1:
|
||||
for i in 0..safeLen(n)-1:
|
||||
importForwarded(c, n.sons[i], exceptSet)
|
||||
|
||||
proc importModuleAs(n: PNode, realModule: PSym): PSym =
|
||||
|
|
|
|||
|
|
@ -51,23 +51,35 @@ Files: "configure;makefile"
|
|||
Files: "*.ini"
|
||||
Files: "koch.nim"
|
||||
|
||||
Files: "icons/nimrod.ico"
|
||||
Files: "icons/nimrod.rc"
|
||||
Files: "icons/nimrod.res"
|
||||
Files: "icons/nimrod_icon.o"
|
||||
Files: "icons/nim.ico"
|
||||
Files: "icons/nim.rc"
|
||||
Files: "icons/nim.res"
|
||||
Files: "icons/nim_icon.o"
|
||||
Files: "icons/koch.ico"
|
||||
Files: "icons/koch.rc"
|
||||
Files: "icons/koch.res"
|
||||
Files: "icons/koch_icon.o"
|
||||
|
||||
Files: "compiler/readme.txt"
|
||||
Files: "compiler/nim.ini"
|
||||
Files: "compiler/nim.nimrod.cfg"
|
||||
Files: "compiler/installer.ini"
|
||||
Files: "compiler/nim.nim.cfg"
|
||||
Files: "compiler/*.nim"
|
||||
Files: "doc/*.txt"
|
||||
Files: "doc/manual/*.txt"
|
||||
Files: "doc/*.nim"
|
||||
Files: "doc/*.cfg"
|
||||
Files: "compiler/nimfix/*.nim"
|
||||
Files: "compiler/nimfix/*.cfg"
|
||||
|
||||
Files: "tools/*.nim"
|
||||
Files: "tools/*.cfg"
|
||||
Files: "tools/*.tmpl"
|
||||
Files: "tools/niminst/*.nim"
|
||||
Files: "tools/niminst/*.cfg"
|
||||
Files: "tools/niminst/*.tmpl"
|
||||
Files: "tools/niminst/*.nsh"
|
||||
Files: "web/website.ini"
|
||||
Files: "web/*.nim"
|
||||
Files: "web/*.txt"
|
||||
|
||||
[Lib]
|
||||
Files: "lib/nimbase.h"
|
||||
|
|
@ -77,8 +89,11 @@ Files: "lib/*.cfg"
|
|||
Files: "lib/system/*.nim"
|
||||
Files: "lib/core/*.nim"
|
||||
Files: "lib/pure/*.nim"
|
||||
Files: "lib/pure/*.cfg"
|
||||
Files: "lib/pure/collections/*.nim"
|
||||
Files: "lib/pure/concurrency/*.nim"
|
||||
Files: "lib/pure/unidecode/*.nim"
|
||||
Files: "lib/pure/concurrency/*.cfg"
|
||||
Files: "lib/impure/*.nim"
|
||||
Files: "lib/wrappers/*.nim"
|
||||
|
||||
|
|
@ -111,12 +126,109 @@ Files: "examples/*.txt"
|
|||
Files: "examples/*.cfg"
|
||||
Files: "examples/*.tmpl"
|
||||
|
||||
Files: "tests/actiontable/*.nim"
|
||||
Files: "tests/alias/*.nim"
|
||||
Files: "tests/ambsym/*.nim"
|
||||
Files: "tests/array/*.nim"
|
||||
Files: "tests/assign/*.nim"
|
||||
Files: "tests/astoverload/*.nim"
|
||||
Files: "tests/async/*.nim"
|
||||
Files: "tests/benchmarks/*.nim"
|
||||
Files: "tests/bind/*.nim"
|
||||
Files: "tests/borrow/*.nim"
|
||||
Files: "tests/casestmt/*.nim"
|
||||
Files: "tests/ccgbugs/*.nim"
|
||||
Files: "tests/clearmsg/*.nim"
|
||||
Files: "tests/closure/*.nim"
|
||||
Files: "tests/cnstseq/*.nim"
|
||||
Files: "tests/collections/*.nim"
|
||||
Files: "tests/compiles/*.nim"
|
||||
Files: "tests/concat/*.nim"
|
||||
Files: "tests/concepts/*.nim"
|
||||
Files: "tests/constr/*.nim"
|
||||
Files: "tests/constraints/*.nim"
|
||||
Files: "tests/controlflow/*.nim"
|
||||
Files: "tests/converter/*.nim"
|
||||
Files: "tests/cpp/*.nim"
|
||||
Files: "tests/defaultprocparam/*.nim"
|
||||
Files: "tests/deprecated/*.nim"
|
||||
Files: "tests/destructor/*.nim"
|
||||
Files: "tests/dir with space/*.nim"
|
||||
Files: "tests/discard/*.nim"
|
||||
Files: "tests/distinct/*.nim"
|
||||
Files: "tests/dll/*.nim"
|
||||
Files: "tests/effects/*.nim"
|
||||
Files: "tests/enum/*.nim"
|
||||
Files: "tests/exception/*.nim"
|
||||
Files: "tests/exprs/*.nim"
|
||||
Files: "tests/fields/*.nim"
|
||||
Files: "tests/float/*.nim"
|
||||
Files: "tests/friends/*.nim"
|
||||
Files: "tests/gc/*.nim"
|
||||
Files: "tests/generics/*.nim"
|
||||
Files: "tests/gensym/*.nim"
|
||||
Files: "tests/global/*.nim"
|
||||
Files: "tests/implicit/*.nim"
|
||||
Files: "tests/init/*.nim"
|
||||
Files: "tests/iter/*.nim"
|
||||
Files: "tests/js/*.nim"
|
||||
Files: "tests/js/*.cfg"
|
||||
Files: "tests/let/*.nim"
|
||||
Files: "tests/lexer/*.nim"
|
||||
Files: "tests/lookups/*.nim"
|
||||
Files: "tests/macros/*.nim"
|
||||
Files: "tests/magics/*.nim"
|
||||
Files: "tests/metatype/*.nim"
|
||||
Files: "tests/method/*.nim"
|
||||
Files: "tests/misc/*.nim"
|
||||
Files: "tests/modules/*.nim"
|
||||
Files: "tests/namedparams/*.nim"
|
||||
Files: "tests/notnil/*.nim"
|
||||
Files: "tests/objects/*.nim"
|
||||
Files: "tests/objvariant/*.nim"
|
||||
Files: "tests/openarray/*.nim"
|
||||
Files: "tests/osproc/*.nim"
|
||||
Files: "tests/overflw/*.nim"
|
||||
Files: "tests/overload/*.nim"
|
||||
Files: "tests/parallel/*.nim"
|
||||
Files: "tests/parallel/*.cfg"
|
||||
Files: "tests/parser/*.nim"
|
||||
Files: "tests/pragmas/*.nim"
|
||||
Files: "tests/proc/*.nim"
|
||||
Files: "tests/procvar/*.nim"
|
||||
Files: "tests/range/*.nim"
|
||||
Files: "tests/rodfiles/*.nim"
|
||||
Files: "tests/seq/*.nim"
|
||||
Files: "tests/sets/*.nim"
|
||||
Files: "tests/showoff/*.nim"
|
||||
Files: "tests/specialops/*.nim"
|
||||
Files: "tests/stdlib/*.nim"
|
||||
Files: "tests/system/*.nim"
|
||||
Files: "tests/template/*.nim"
|
||||
Files: "tests/testament/*.nim"
|
||||
Files: "tests/testdata/*.nim"
|
||||
Files: "tests/threads/*.nim"
|
||||
Files: "tests/threads/*.cfg"
|
||||
Files: "tests/trmacros/*.nim"
|
||||
Files: "tests/tuples/*.nim"
|
||||
Files: "tests/typerel/*.nim"
|
||||
Files: "tests/types/*.nim"
|
||||
Files: "tests/usingstmt/*.nim"
|
||||
Files: "tests/varres/*.nim"
|
||||
Files: "tests/varstmt/*.nim"
|
||||
Files: "tests/vm/*.nim"
|
||||
Files: "tests/readme.txt"
|
||||
Files: "tests/testament/css/*.css"
|
||||
Files: "tests/testament/*.cfg"
|
||||
Files: "lib/pure/unidecode/unidecode.dat"
|
||||
|
||||
[Windows]
|
||||
Files: "bin/nim.exe"
|
||||
Files: "bin/nim_debug.exe"
|
||||
Files: "bin/c2nim.exe"
|
||||
Files: "bin/nimgrep.exe"
|
||||
Files: "bin/nimsuggest.exe"
|
||||
Files: "bin/nimble.exe"
|
||||
Files: "bin/*.dll"
|
||||
|
||||
Files: "dist/*.dll"
|
||||
Files: "koch.exe"
|
||||
|
|
@ -125,9 +237,9 @@ Files: "start.bat"
|
|||
BinPath: r"bin;dist\mingw\bin;dist"
|
||||
|
||||
; Section | dir | zipFile | size hint (in KB) | url | exe start menu entry
|
||||
Download: r"Documentation|doc|docs.zip|13824|http://nim-lang.org/download/docs-${version}.zip|doc\overview.html"
|
||||
Download: r"Documentation|doc|docs.zip|13824|http://nim-lang.org/download/docs-${version}.zip|overview.html"
|
||||
Download: r"C Compiler (MingW)|dist|mingw.zip|82944|http://nim-lang.org/download/${mingw}.zip"
|
||||
Download: r"Aporia IDE|dist|aporia.zip|97997|http://nim-lang.org/download/aporia-0.1.3.zip|aporia\bin\aporia.exe"
|
||||
Download: r"Aporia IDE|dist|aporia.zip|97997|http://nim-lang.org/download/aporia-0.3.0.zip|aporia\bin\aporia.exe"
|
||||
; for now only NSIS supports optional downloads
|
||||
|
||||
[UnixBin]
|
||||
File diff suppressed because it is too large
Load diff
|
|
@ -9,10 +9,10 @@
|
|||
|
||||
## Type info generation for the JS backend.
|
||||
|
||||
proc genTypeInfo(p: PProc, typ: PType): PRope
|
||||
proc genObjectFields(p: PProc, typ: PType, n: PNode): PRope =
|
||||
proc genTypeInfo(p: PProc, typ: PType): Rope
|
||||
proc genObjectFields(p: PProc, typ: PType, n: PNode): Rope =
|
||||
var
|
||||
s, u: PRope
|
||||
s, u: Rope
|
||||
length: int
|
||||
field: PSym
|
||||
b: PNode
|
||||
|
|
@ -25,16 +25,16 @@ proc genObjectFields(p: PProc, typ: PType, n: PNode): PRope =
|
|||
else:
|
||||
s = nil
|
||||
for i in countup(0, length - 1):
|
||||
if i > 0: app(s, ", " & tnl)
|
||||
app(s, genObjectFields(p, typ, n.sons[i]))
|
||||
result = ropef("{kind: 2, len: $1, offset: 0, " &
|
||||
"typ: null, name: null, sons: [$2]}", [toRope(length), s])
|
||||
if i > 0: add(s, ", " & tnl)
|
||||
add(s, genObjectFields(p, typ, n.sons[i]))
|
||||
result = ("{kind: 2, len: $1, offset: 0, " &
|
||||
"typ: null, name: null, sons: [$2]}") % [rope(length), s]
|
||||
of nkSym:
|
||||
field = n.sym
|
||||
s = genTypeInfo(p, field.typ)
|
||||
result = ropef("{kind: 1, offset: \"$1\", len: 0, " &
|
||||
"typ: $2, name: $3, sons: null}",
|
||||
[mangleName(field), s, makeJSString(field.name.s)])
|
||||
result = ("{kind: 1, offset: \"$1\", len: 0, " &
|
||||
"typ: $2, name: $3, sons: null}") %
|
||||
[mangleName(field), s, makeJSString(field.name.s)]
|
||||
of nkRecCase:
|
||||
length = sonsLen(n)
|
||||
if (n.sons[0].kind != nkSym): internalError(n.info, "genObjectFields")
|
||||
|
|
@ -48,99 +48,99 @@ proc genObjectFields(p: PProc, typ: PType, n: PNode): PRope =
|
|||
if sonsLen(b) < 2:
|
||||
internalError(b.info, "genObjectFields; nkOfBranch broken")
|
||||
for j in countup(0, sonsLen(b) - 2):
|
||||
if u != nil: app(u, ", ")
|
||||
if u != nil: add(u, ", ")
|
||||
if b.sons[j].kind == nkRange:
|
||||
appf(u, "[$1, $2]", [toRope(getOrdValue(b.sons[j].sons[0])),
|
||||
toRope(getOrdValue(b.sons[j].sons[1]))])
|
||||
addf(u, "[$1, $2]", [rope(getOrdValue(b.sons[j].sons[0])),
|
||||
rope(getOrdValue(b.sons[j].sons[1]))])
|
||||
else:
|
||||
app(u, toRope(getOrdValue(b.sons[j])))
|
||||
add(u, rope(getOrdValue(b.sons[j])))
|
||||
of nkElse:
|
||||
u = toRope(lengthOrd(field.typ))
|
||||
u = rope(lengthOrd(field.typ))
|
||||
else: internalError(n.info, "genObjectFields(nkRecCase)")
|
||||
if result != nil: app(result, ", " & tnl)
|
||||
appf(result, "[SetConstr($1), $2]",
|
||||
if result != nil: add(result, ", " & tnl)
|
||||
addf(result, "[SetConstr($1), $2]",
|
||||
[u, genObjectFields(p, typ, lastSon(b))])
|
||||
result = ropef("{kind: 3, offset: \"$1\", len: $3, " &
|
||||
"typ: $2, name: $4, sons: [$5]}", [mangleName(field), s,
|
||||
toRope(lengthOrd(field.typ)), makeJSString(field.name.s), result])
|
||||
result = ("{kind: 3, offset: \"$1\", len: $3, " &
|
||||
"typ: $2, name: $4, sons: [$5]}") % [mangleName(field), s,
|
||||
rope(lengthOrd(field.typ)), makeJSString(field.name.s), result]
|
||||
else: internalError(n.info, "genObjectFields")
|
||||
|
||||
proc genObjectInfo(p: PProc, typ: PType, name: PRope) =
|
||||
var s = ropef("var $1 = {size: 0, kind: $2, base: null, node: null, " &
|
||||
"finalizer: null};$n", [name, toRope(ord(typ.kind))])
|
||||
proc genObjectInfo(p: PProc, typ: PType, name: Rope) =
|
||||
var s = ("var $1 = {size: 0, kind: $2, base: null, node: null, " &
|
||||
"finalizer: null};$n") % [name, rope(ord(typ.kind))]
|
||||
prepend(p.g.typeInfo, s)
|
||||
appf(p.g.typeInfo, "var NNI$1 = $2;$n",
|
||||
[toRope(typ.id), genObjectFields(p, typ, typ.n)])
|
||||
appf(p.g.typeInfo, "$1.node = NNI$2;$n", [name, toRope(typ.id)])
|
||||
addf(p.g.typeInfo, "var NNI$1 = $2;$n",
|
||||
[rope(typ.id), genObjectFields(p, typ, typ.n)])
|
||||
addf(p.g.typeInfo, "$1.node = NNI$2;$n", [name, rope(typ.id)])
|
||||
if (typ.kind == tyObject) and (typ.sons[0] != nil):
|
||||
appf(p.g.typeInfo, "$1.base = $2;$n",
|
||||
addf(p.g.typeInfo, "$1.base = $2;$n",
|
||||
[name, genTypeInfo(p, typ.sons[0])])
|
||||
|
||||
proc genTupleFields(p: PProc, typ: PType): PRope =
|
||||
var s: PRope = nil
|
||||
proc genTupleFields(p: PProc, typ: PType): Rope =
|
||||
var s: Rope = nil
|
||||
for i in 0 .. <typ.len:
|
||||
if i > 0: app(s, ", " & tnl)
|
||||
s.appf("{kind: 1, offset: \"Field$1\", len: 0, " &
|
||||
if i > 0: add(s, ", " & tnl)
|
||||
s.addf("{kind: 1, offset: \"Field$1\", len: 0, " &
|
||||
"typ: $2, name: \"Field$1\", sons: null}",
|
||||
[i.toRope, genTypeInfo(p, typ.sons[i])])
|
||||
result = ropef("{kind: 2, len: $1, offset: 0, " &
|
||||
"typ: null, name: null, sons: [$2]}", [toRope(typ.len), s])
|
||||
[i.rope, genTypeInfo(p, typ.sons[i])])
|
||||
result = ("{kind: 2, len: $1, offset: 0, " &
|
||||
"typ: null, name: null, sons: [$2]}") % [rope(typ.len), s]
|
||||
|
||||
proc genTupleInfo(p: PProc, typ: PType, name: PRope) =
|
||||
var s = ropef("var $1 = {size: 0, kind: $2, base: null, node: null, " &
|
||||
"finalizer: null};$n", [name, toRope(ord(typ.kind))])
|
||||
proc genTupleInfo(p: PProc, typ: PType, name: Rope) =
|
||||
var s = ("var $1 = {size: 0, kind: $2, base: null, node: null, " &
|
||||
"finalizer: null};$n") % [name, rope(ord(typ.kind))]
|
||||
prepend(p.g.typeInfo, s)
|
||||
appf(p.g.typeInfo, "var NNI$1 = $2;$n",
|
||||
[toRope(typ.id), genTupleFields(p, typ)])
|
||||
appf(p.g.typeInfo, "$1.node = NNI$2;$n", [name, toRope(typ.id)])
|
||||
addf(p.g.typeInfo, "var NNI$1 = $2;$n",
|
||||
[rope(typ.id), genTupleFields(p, typ)])
|
||||
addf(p.g.typeInfo, "$1.node = NNI$2;$n", [name, rope(typ.id)])
|
||||
|
||||
proc genEnumInfo(p: PProc, typ: PType, name: PRope) =
|
||||
proc genEnumInfo(p: PProc, typ: PType, name: Rope) =
|
||||
let length = sonsLen(typ.n)
|
||||
var s: PRope = nil
|
||||
var s: Rope = nil
|
||||
for i in countup(0, length - 1):
|
||||
if (typ.n.sons[i].kind != nkSym): internalError(typ.n.info, "genEnumInfo")
|
||||
let field = typ.n.sons[i].sym
|
||||
if i > 0: app(s, ", " & tnl)
|
||||
if i > 0: add(s, ", " & tnl)
|
||||
let extName = if field.ast == nil: field.name.s else: field.ast.strVal
|
||||
appf(s, "{kind: 1, offset: $1, typ: $2, name: $3, len: 0, sons: null}",
|
||||
[toRope(field.position), name, makeJSString(extName)])
|
||||
var n = ropef("var NNI$1 = {kind: 2, offset: 0, typ: null, " &
|
||||
"name: null, len: $2, sons: [$3]};$n", [toRope(typ.id), toRope(length), s])
|
||||
s = ropef("var $1 = {size: 0, kind: $2, base: null, node: null, " &
|
||||
"finalizer: null};$n", [name, toRope(ord(typ.kind))])
|
||||
addf(s, "{kind: 1, offset: $1, typ: $2, name: $3, len: 0, sons: null}",
|
||||
[rope(field.position), name, makeJSString(extName)])
|
||||
var n = ("var NNI$1 = {kind: 2, offset: 0, typ: null, " &
|
||||
"name: null, len: $2, sons: [$3]};$n") % [rope(typ.id), rope(length), s]
|
||||
s = ("var $1 = {size: 0, kind: $2, base: null, node: null, " &
|
||||
"finalizer: null};$n") % [name, rope(ord(typ.kind))]
|
||||
prepend(p.g.typeInfo, s)
|
||||
app(p.g.typeInfo, n)
|
||||
appf(p.g.typeInfo, "$1.node = NNI$2;$n", [name, toRope(typ.id)])
|
||||
add(p.g.typeInfo, n)
|
||||
addf(p.g.typeInfo, "$1.node = NNI$2;$n", [name, rope(typ.id)])
|
||||
if typ.sons[0] != nil:
|
||||
appf(p.g.typeInfo, "$1.base = $2;$n",
|
||||
addf(p.g.typeInfo, "$1.base = $2;$n",
|
||||
[name, genTypeInfo(p, typ.sons[0])])
|
||||
|
||||
proc genTypeInfo(p: PProc, typ: PType): PRope =
|
||||
proc genTypeInfo(p: PProc, typ: PType): Rope =
|
||||
var t = typ
|
||||
if t.kind == tyGenericInst: t = lastSon(t)
|
||||
result = ropef("NTI$1", [toRope(t.id)])
|
||||
result = "NTI$1" % [rope(t.id)]
|
||||
if containsOrIncl(p.g.typeInfoGenerated, t.id): return
|
||||
case t.kind
|
||||
of tyDistinct:
|
||||
result = genTypeInfo(p, typ.sons[0])
|
||||
of tyPointer, tyProc, tyBool, tyChar, tyCString, tyString, tyInt..tyFloat128:
|
||||
var s = ropef(
|
||||
"var $1 = {size: 0,kind: $2,base: null,node: null,finalizer: null};$n",
|
||||
[result, toRope(ord(t.kind))])
|
||||
of tyPointer, tyProc, tyBool, tyChar, tyCString, tyString, tyInt..tyUInt64:
|
||||
var s =
|
||||
"var $1 = {size: 0,kind: $2,base: null,node: null,finalizer: null};$n" %
|
||||
[result, rope(ord(t.kind))]
|
||||
prepend(p.g.typeInfo, s)
|
||||
of tyVar, tyRef, tyPtr, tySequence, tyRange, tySet:
|
||||
var s = ropef(
|
||||
"var $1 = {size: 0,kind: $2,base: null,node: null,finalizer: null};$n",
|
||||
[result, toRope(ord(t.kind))])
|
||||
var s =
|
||||
"var $1 = {size: 0,kind: $2,base: null,node: null,finalizer: null};$n" %
|
||||
[result, rope(ord(t.kind))]
|
||||
prepend(p.g.typeInfo, s)
|
||||
appf(p.g.typeInfo, "$1.base = $2;$n",
|
||||
addf(p.g.typeInfo, "$1.base = $2;$n",
|
||||
[result, genTypeInfo(p, typ.lastSon)])
|
||||
of tyArrayConstr, tyArray:
|
||||
var s = ropef(
|
||||
"var $1 = {size: 0,kind: $2,base: null,node: null,finalizer: null};$n",
|
||||
[result, toRope(ord(t.kind))])
|
||||
var s =
|
||||
"var $1 = {size: 0,kind: $2,base: null,node: null,finalizer: null};$n" %
|
||||
[result, rope(ord(t.kind))]
|
||||
prepend(p.g.typeInfo, s)
|
||||
appf(p.g.typeInfo, "$1.base = $2;$n",
|
||||
addf(p.g.typeInfo, "$1.base = $2;$n",
|
||||
[result, genTypeInfo(p, typ.sons[1])])
|
||||
of tyEnum: genEnumInfo(p, t, result)
|
||||
of tyObject: genObjectInfo(p, t, result)
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
#
|
||||
#
|
||||
# The Nim Compiler
|
||||
# (c) Copyright 2014 Andreas Rumpf
|
||||
# (c) Copyright 2015 Andreas Rumpf
|
||||
#
|
||||
# See the file "copying.txt", included in this
|
||||
# distribution, for details about the copyright.
|
||||
|
|
@ -15,7 +15,7 @@ import
|
|||
|
||||
discard """
|
||||
The basic approach is that captured vars need to be put on the heap and
|
||||
that the calling chain needs to be explicitely modelled. Things to consider:
|
||||
that the calling chain needs to be explicitly modelled. Things to consider:
|
||||
|
||||
proc a =
|
||||
var v = 0
|
||||
|
|
@ -187,6 +187,7 @@ proc addHiddenParam(routine: PSym, param: PSym) =
|
|||
param.position = params.len-1
|
||||
addSon(params, newSymNode(param))
|
||||
incl(routine.typ.flags, tfCapturesEnv)
|
||||
assert sfFromGeneric in param.flags
|
||||
#echo "produced environment: ", param.id, " for ", routine.name.s
|
||||
|
||||
proc getHiddenParam(routine: PSym): PSym =
|
||||
|
|
@ -194,12 +195,14 @@ proc getHiddenParam(routine: PSym): PSym =
|
|||
let hidden = lastSon(params)
|
||||
internalAssert hidden.kind == nkSym and hidden.sym.kind == skParam
|
||||
result = hidden.sym
|
||||
assert sfFromGeneric in result.flags
|
||||
|
||||
proc getEnvParam(routine: PSym): PSym =
|
||||
let params = routine.ast.sons[paramsPos]
|
||||
let hidden = lastSon(params)
|
||||
if hidden.kind == nkSym and hidden.sym.name.s == paramName:
|
||||
result = hidden.sym
|
||||
assert sfFromGeneric in result.flags
|
||||
|
||||
proc initIter(iter: PSym): TIter =
|
||||
result.fn = iter
|
||||
|
|
@ -580,7 +583,7 @@ proc searchForInnerProcs(o: POuterContext, n: PNode, env: PEnv) =
|
|||
elif it.kind == nkIdentDefs:
|
||||
var L = sonsLen(it)
|
||||
if it.sons[0].kind == nkSym:
|
||||
# this can be false for recursive invokations that already
|
||||
# this can be false for recursive invocations that already
|
||||
# transformed it into 'env.varName':
|
||||
env.vars.incl(it.sons[0].sym.id)
|
||||
searchForInnerProcs(o, it.sons[L-1], env)
|
||||
|
|
@ -716,15 +719,17 @@ proc outerProcSons(o: POuterContext, n: PNode, it: TIter) =
|
|||
let x = transformOuterProc(o, n.sons[i], it)
|
||||
if x != nil: n.sons[i] = x
|
||||
|
||||
proc liftIterSym*(n: PNode): PNode =
|
||||
proc liftIterSym(n: PNode; owner: PSym): PNode =
|
||||
# transforms (iter) to (let env = newClosure[iter](); (iter, env))
|
||||
let iter = n.sym
|
||||
assert iter.kind == skClosureIterator
|
||||
|
||||
result = newNodeIT(nkStmtListExpr, n.info, n.typ)
|
||||
|
||||
var env = copySym(getHiddenParam(iter))
|
||||
env.kind = skLet
|
||||
let hp = getHiddenParam(iter)
|
||||
let env = newSym(skLet, iter.name, owner, n.info)
|
||||
env.typ = hp.typ
|
||||
env.flags = hp.flags
|
||||
var v = newNodeI(nkVarSection, n.info)
|
||||
addVar(v, newSymNode(env))
|
||||
result.add(v)
|
||||
|
|
@ -853,7 +858,6 @@ proc transformOuterProc(o: POuterContext, n: PNode; it: TIter): PNode =
|
|||
addUniqueField(it.obj, local)
|
||||
return indirectAccess(newSymNode(it.closureParam), local, n.info)
|
||||
|
||||
var closure = PEnv(idTableGet(o.lambdasToEnv, local))
|
||||
if local.kind == skClosureIterator:
|
||||
# consider: [i1, i2, i1] Since we merged the iterator's closure
|
||||
# with the captured owning variables, we need to generate the
|
||||
|
|
@ -861,13 +865,25 @@ proc transformOuterProc(o: POuterContext, n: PNode; it: TIter): PNode =
|
|||
if local == o.fn or local == it.fn:
|
||||
message(n.info, errRecursiveDependencyX, local.name.s)
|
||||
# XXX why doesn't this work?
|
||||
var closure = PEnv(idTableGet(o.lambdasToEnv, local))
|
||||
if closure.isNil:
|
||||
return liftIterSym(n)
|
||||
return liftIterSym(n, o.fn)
|
||||
else:
|
||||
let createdVar = generateIterClosureCreation(o, closure,
|
||||
closure.attachedNode)
|
||||
let lpt = getHiddenParam(local).typ
|
||||
if lpt != createdVar.typ:
|
||||
assert lpt.kind == tyRef and createdVar.typ.kind == tyRef
|
||||
# fix bug 'tshallowcopy_closures' but report if this gets any weirder:
|
||||
if createdVar.typ.sons[0].len == 1 and lpt.sons[0].len >= 1:
|
||||
createdVar.typ = lpt
|
||||
if createdVar.kind == nkSym: createdVar.sym.typ = lpt
|
||||
closure.obj = lpt.sons[0]
|
||||
else:
|
||||
internalError(n.info, "environment computation failed")
|
||||
return makeClosure(local, createdVar, n.info)
|
||||
|
||||
var closure = PEnv(idTableGet(o.lambdasToEnv, local))
|
||||
if closure != nil:
|
||||
# we need to replace the lambda with '(lambda, env)':
|
||||
let a = closure.createdVar
|
||||
|
|
@ -935,7 +951,7 @@ proc liftLambdas*(fn: PSym, body: PNode): PNode =
|
|||
# ignore forward declaration:
|
||||
result = body
|
||||
else:
|
||||
#if fn.name.s == "cbOuter":
|
||||
#if fn.name.s == "sort":
|
||||
# echo rendertree(fn.ast, {renderIds})
|
||||
var o = newOuterContext(fn)
|
||||
let ex = closureCreationPoint(body)
|
||||
|
|
@ -983,7 +999,7 @@ proc liftForLoop*(body: PNode): PNode =
|
|||
# proc invoke(iter: iterator(): int) =
|
||||
# for x in iter(): echo x
|
||||
#
|
||||
# --> When to create the closure? --> for the (count) occurence!
|
||||
# --> When to create the closure? --> for the (count) occurrence!
|
||||
discard """
|
||||
for i in foo(): ...
|
||||
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
#
|
||||
#
|
||||
# The Nim Compiler
|
||||
# (c) Copyright 2014 Andreas Rumpf
|
||||
# (c) Copyright 2015 Andreas Rumpf
|
||||
#
|
||||
# See the file "copying.txt", included in this
|
||||
# distribution, for details about the copyright.
|
||||
|
|
@ -35,7 +35,7 @@ type
|
|||
tkSymbol, # keywords:
|
||||
tkAddr, tkAnd, tkAs, tkAsm, tkAtomic,
|
||||
tkBind, tkBlock, tkBreak, tkCase, tkCast,
|
||||
tkConst, tkContinue, tkConverter,
|
||||
tkConcept, tkConst, tkContinue, tkConverter,
|
||||
tkDefer, tkDiscard, tkDistinct, tkDiv, tkDo,
|
||||
tkElif, tkElse, tkEnd, tkEnum, tkExcept, tkExport,
|
||||
tkFinally, tkFor, tkFrom, tkFunc,
|
||||
|
|
@ -61,7 +61,7 @@ type
|
|||
tkComma, tkSemiColon,
|
||||
tkColon, tkColonColon, tkEquals, tkDot, tkDotDot,
|
||||
tkOpr, tkComment, tkAccent,
|
||||
tkSpaces, tkInfixOpr, tkPrefixOpr, tkPostfixOpr,
|
||||
tkSpaces, tkInfixOpr, tkPrefixOpr, tkPostfixOpr
|
||||
|
||||
TTokTypes* = set[TTokType]
|
||||
|
||||
|
|
@ -72,7 +72,7 @@ const
|
|||
"tkSymbol",
|
||||
"addr", "and", "as", "asm", "atomic",
|
||||
"bind", "block", "break", "case", "cast",
|
||||
"const", "continue", "converter",
|
||||
"concept", "const", "continue", "converter",
|
||||
"defer", "discard", "distinct", "div", "do",
|
||||
"elif", "else", "end", "enum", "except", "export",
|
||||
"finally", "for", "from", "func", "generic", "if",
|
||||
|
|
@ -107,7 +107,7 @@ type
|
|||
TToken* = object # a Nim token
|
||||
tokType*: TTokType # the type of the token
|
||||
indent*: int # the indentation; != -1 if the token has been
|
||||
# preceeded with indentation
|
||||
# preceded with indentation
|
||||
ident*: PIdent # the parsed identifier
|
||||
iNumber*: BiggestInt # the parsed integer literal
|
||||
fNumber*: BiggestFloat # the parsed floating point literal
|
||||
|
|
@ -131,24 +131,10 @@ type
|
|||
|
||||
var gLinesCompiled*: int # all lines that have been compiled
|
||||
|
||||
proc isKeyword*(kind: TTokType): bool
|
||||
proc openLexer*(lex: var TLexer, fileidx: int32, inputstream: PLLStream)
|
||||
proc rawGetTok*(L: var TLexer, tok: var TToken)
|
||||
# reads in the next token into tok and skips it
|
||||
|
||||
proc getLineInfo*(L: TLexer, tok: TToken): TLineInfo {.inline.} =
|
||||
newLineInfo(L.fileIdx, tok.line, tok.col)
|
||||
|
||||
proc closeLexer*(lex: var TLexer)
|
||||
proc printTok*(tok: TToken)
|
||||
proc tokToStr*(tok: TToken): string
|
||||
|
||||
proc openLexer*(lex: var TLexer, filename: string, inputstream: PLLStream) =
|
||||
openLexer(lex, filename.fileInfoIdx, inputstream)
|
||||
|
||||
proc lexMessage*(L: TLexer, msg: TMsgKind, arg = "")
|
||||
|
||||
proc isKeyword(kind: TTokType): bool =
|
||||
proc isKeyword*(kind: TTokType): bool =
|
||||
result = (kind >= tokKeywordLow) and (kind <= tokKeywordHigh)
|
||||
|
||||
proc isNimIdentifier*(s: string): bool =
|
||||
|
|
@ -181,10 +167,8 @@ proc prettyTok*(tok: TToken): string =
|
|||
else: result = tokToStr(tok)
|
||||
|
||||
proc printTok*(tok: TToken) =
|
||||
write(stdout, tok.line, ":", tok.col, "\t")
|
||||
write(stdout, TokTypeToStr[tok.tokType])
|
||||
write(stdout, " ")
|
||||
writeln(stdout, tokToStr(tok))
|
||||
msgWriteln($tok.line & ":" & $tok.col & "\t" &
|
||||
TokTypeToStr[tok.tokType] & " " & tokToStr(tok))
|
||||
|
||||
var dummyIdent: PIdent
|
||||
|
||||
|
|
@ -208,14 +192,17 @@ proc fillToken(L: var TToken) =
|
|||
L.base = base10
|
||||
L.ident = dummyIdent
|
||||
|
||||
proc openLexer(lex: var TLexer, fileIdx: int32, inputstream: PLLStream) =
|
||||
proc openLexer*(lex: var TLexer, fileIdx: int32, inputstream: PLLStream) =
|
||||
openBaseLexer(lex, inputstream)
|
||||
lex.fileIdx = fileidx
|
||||
lex.indentAhead = - 1
|
||||
lex.currLineIndent = 0
|
||||
inc(lex.lineNumber, inputstream.lineOffset)
|
||||
|
||||
proc closeLexer(lex: var TLexer) =
|
||||
proc openLexer*(lex: var TLexer, filename: string, inputstream: PLLStream) =
|
||||
openLexer(lex, filename.fileInfoIdx, inputstream)
|
||||
|
||||
proc closeLexer*(lex: var TLexer) =
|
||||
inc(gLinesCompiled, lex.lineNumber)
|
||||
closeBaseLexer(lex)
|
||||
|
||||
|
|
@ -231,9 +218,13 @@ proc dispMessage(L: TLexer; info: TLineInfo; msg: TMsgKind; arg: string) =
|
|||
else:
|
||||
L.errorHandler(info, msg, arg)
|
||||
|
||||
proc lexMessage(L: TLexer, msg: TMsgKind, arg = "") =
|
||||
proc lexMessage*(L: TLexer, msg: TMsgKind, arg = "") =
|
||||
L.dispMessage(getLineInfo(L), msg, arg)
|
||||
|
||||
proc lexMessageTok*(L: TLexer, msg: TMsgKind, tok: TToken, arg = "") =
|
||||
var info = newLineInfo(L.fileIdx, tok.line, tok.col)
|
||||
L.dispMessage(info, msg, arg)
|
||||
|
||||
proc lexMessagePos(L: var TLexer, msg: TMsgKind, pos: int, arg = "") =
|
||||
var info = newLineInfo(L.fileIdx, L.lineNumber, pos - L.lineStart)
|
||||
L.dispMessage(info, msg, arg)
|
||||
|
|
@ -264,6 +255,19 @@ proc isFloatLiteral(s: string): bool =
|
|||
return true
|
||||
result = false
|
||||
|
||||
{.push overflowChecks: off.}
|
||||
# We need to parse the largest uint literal without overflow checks
|
||||
proc unsafeParseUInt(s: string, b: var BiggestInt, start = 0): int =
|
||||
var i = start
|
||||
if s[i] in {'0'..'9'}:
|
||||
b = 0
|
||||
while s[i] in {'0'..'9'}:
|
||||
b = b * 10 + (ord(s[i]) - ord('0'))
|
||||
inc(i)
|
||||
while s[i] == '_': inc(i) # underscores are allowed and ignored
|
||||
result = i - start
|
||||
{.pop.} # overflowChecks
|
||||
|
||||
proc getNumber(L: var TLexer): TToken =
|
||||
var
|
||||
pos, endpos: int
|
||||
|
|
@ -425,6 +429,12 @@ proc getNumber(L: var TLexer): TToken =
|
|||
(result.tokType == tkFloat64Lit):
|
||||
result.fNumber = parseFloat(result.literal)
|
||||
if result.tokType == tkIntLit: result.tokType = tkFloatLit
|
||||
elif result.tokType == tkUint64Lit:
|
||||
xi = 0
|
||||
let len = unsafeParseUInt(result.literal, xi)
|
||||
if len != result.literal.len or len == 0:
|
||||
raise newException(ValueError, "invalid integer: " & $xi)
|
||||
result.iNumber = xi
|
||||
else:
|
||||
result.iNumber = parseBiggestInt(result.literal)
|
||||
if (result.iNumber < low(int32)) or (result.iNumber > high(int32)):
|
||||
|
|
@ -660,7 +670,7 @@ proc getOperator(L: var TLexer, tok: var TToken) =
|
|||
inc(pos)
|
||||
endOperator(L, tok, pos, h)
|
||||
# advance pos but don't store it in L.bufpos so the next token (which might
|
||||
# be an operator too) gets the preceeding spaces:
|
||||
# be an operator too) gets the preceding spaces:
|
||||
tok.strongSpaceB = 0
|
||||
while buf[pos] == ' ':
|
||||
inc pos
|
||||
|
|
@ -674,7 +684,15 @@ proc scanComment(L: var TLexer, tok: var TToken) =
|
|||
when not defined(nimfix):
|
||||
assert buf[pos+1] == '#'
|
||||
if buf[pos+2] == '[':
|
||||
lexMessagePos(L, warnDeprecated, pos, "use '## [' instead; '##['")
|
||||
if buf[pos+3] == ']':
|
||||
# ##[] is the (rather complex) "cursor token" for idetools
|
||||
tok.tokType = tkComment
|
||||
tok.literal = "[]"
|
||||
inc(L.bufpos, 4)
|
||||
return
|
||||
else:
|
||||
lexMessagePos(L, warnDeprecated, pos, "use '## [' instead; '##['")
|
||||
|
||||
tok.tokType = tkComment
|
||||
# iNumber contains the number of '\n' in the token
|
||||
tok.iNumber = 0
|
||||
|
|
@ -760,7 +778,7 @@ proc skip(L: var TLexer, tok: var TToken) =
|
|||
break # EndOfFile also leaves the loop
|
||||
L.bufpos = pos
|
||||
|
||||
proc rawGetTok(L: var TLexer, tok: var TToken) =
|
||||
proc rawGetTok*(L: var TLexer, tok: var TToken) =
|
||||
fillToken(tok)
|
||||
if L.indentAhead >= 0:
|
||||
tok.indent = L.indentAhead
|
||||
|
|
@ -849,6 +867,15 @@ proc rawGetTok(L: var TLexer, tok: var TToken) =
|
|||
of '`':
|
||||
tok.tokType = tkAccent
|
||||
inc(L.bufpos)
|
||||
of '_':
|
||||
inc(L.bufpos)
|
||||
if L.buf[L.bufpos] notin SymChars:
|
||||
tok.tokType = tkSymbol
|
||||
tok.ident = getIdent("_")
|
||||
else:
|
||||
tok.literal = $c
|
||||
tok.tokType = tkInvalid
|
||||
lexMessage(L, errInvalidToken, c & " (\\" & $(ord(c)) & ')')
|
||||
of '\"':
|
||||
# check for extended raw string literal:
|
||||
var rawMode = L.bufpos > 0 and L.buf[L.bufpos-1] in SymChars
|
||||
|
|
|
|||
|
|
@ -30,47 +30,32 @@ type
|
|||
|
||||
PLLStream* = ref TLLStream
|
||||
|
||||
proc llStreamOpen*(data: string): PLLStream
|
||||
proc llStreamOpen*(f: var File): PLLStream
|
||||
proc llStreamOpen*(filename: string, mode: FileMode): PLLStream
|
||||
proc llStreamOpen*(): PLLStream
|
||||
proc llStreamOpenStdIn*(): PLLStream
|
||||
proc llStreamClose*(s: PLLStream)
|
||||
proc llStreamRead*(s: PLLStream, buf: pointer, bufLen: int): int
|
||||
proc llStreamReadLine*(s: PLLStream, line: var string): bool
|
||||
proc llStreamReadAll*(s: PLLStream): string
|
||||
proc llStreamWrite*(s: PLLStream, data: string)
|
||||
proc llStreamWrite*(s: PLLStream, data: char)
|
||||
proc llStreamWrite*(s: PLLStream, buf: pointer, buflen: int)
|
||||
proc llStreamWriteln*(s: PLLStream, data: string)
|
||||
# implementation
|
||||
|
||||
proc llStreamOpen(data: string): PLLStream =
|
||||
proc llStreamOpen*(data: string): PLLStream =
|
||||
new(result)
|
||||
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
|
||||
|
||||
proc llStreamOpen(filename: string, mode: FileMode): PLLStream =
|
||||
proc llStreamOpen*(filename: string, mode: FileMode): PLLStream =
|
||||
new(result)
|
||||
result.kind = llsFile
|
||||
if not open(result.f, filename, mode): result = nil
|
||||
|
||||
proc llStreamOpen(): PLLStream =
|
||||
proc llStreamOpen*(): PLLStream =
|
||||
new(result)
|
||||
result.kind = llsNone
|
||||
|
||||
proc llStreamOpenStdIn(): PLLStream =
|
||||
proc llStreamOpenStdIn*(): PLLStream =
|
||||
new(result)
|
||||
result.kind = llsStdIn
|
||||
result.s = ""
|
||||
result.lineOffset = -1
|
||||
|
||||
proc llStreamClose(s: PLLStream) =
|
||||
proc llStreamClose*(s: PLLStream) =
|
||||
case s.kind
|
||||
of llsNone, llsString, llsStdIn:
|
||||
discard
|
||||
|
|
@ -130,7 +115,7 @@ proc llReadFromStdin(s: PLLStream, buf: pointer, bufLen: int): int =
|
|||
copyMem(buf, addr(s.s[s.rd]), result)
|
||||
inc(s.rd, result)
|
||||
|
||||
proc llStreamRead(s: PLLStream, buf: pointer, bufLen: int): int =
|
||||
proc llStreamRead*(s: PLLStream, buf: pointer, bufLen: int): int =
|
||||
case s.kind
|
||||
of llsNone:
|
||||
result = 0
|
||||
|
|
@ -144,7 +129,7 @@ proc llStreamRead(s: PLLStream, buf: pointer, bufLen: int): int =
|
|||
of llsStdIn:
|
||||
result = llReadFromStdin(s, buf, bufLen)
|
||||
|
||||
proc llStreamReadLine(s: PLLStream, line: var string): bool =
|
||||
proc llStreamReadLine*(s: PLLStream, line: var string): bool =
|
||||
setLen(line, 0)
|
||||
case s.kind
|
||||
of llsNone:
|
||||
|
|
@ -168,7 +153,7 @@ proc llStreamReadLine(s: PLLStream, line: var string): bool =
|
|||
of llsStdIn:
|
||||
result = readLine(stdin, line)
|
||||
|
||||
proc llStreamWrite(s: PLLStream, data: string) =
|
||||
proc llStreamWrite*(s: PLLStream, data: string) =
|
||||
case s.kind
|
||||
of llsNone, llsStdIn:
|
||||
discard
|
||||
|
|
@ -178,11 +163,11 @@ proc llStreamWrite(s: PLLStream, data: string) =
|
|||
of llsFile:
|
||||
write(s.f, data)
|
||||
|
||||
proc llStreamWriteln(s: PLLStream, data: string) =
|
||||
proc llStreamWriteln*(s: PLLStream, data: string) =
|
||||
llStreamWrite(s, data)
|
||||
llStreamWrite(s, "\n")
|
||||
|
||||
proc llStreamWrite(s: PLLStream, data: char) =
|
||||
proc llStreamWrite*(s: PLLStream, data: char) =
|
||||
var c: char
|
||||
case s.kind
|
||||
of llsNone, llsStdIn:
|
||||
|
|
@ -194,7 +179,7 @@ proc llStreamWrite(s: PLLStream, data: char) =
|
|||
c = data
|
||||
discard writeBuffer(s.f, addr(c), sizeof(c))
|
||||
|
||||
proc llStreamWrite(s: PLLStream, buf: pointer, buflen: int) =
|
||||
proc llStreamWrite*(s: PLLStream, buf: pointer, buflen: int) =
|
||||
case s.kind
|
||||
of llsNone, llsStdIn:
|
||||
discard
|
||||
|
|
@ -206,7 +191,7 @@ proc llStreamWrite(s: PLLStream, buf: pointer, buflen: int) =
|
|||
of llsFile:
|
||||
discard writeBuffer(s.f, buf, buflen)
|
||||
|
||||
proc llStreamReadAll(s: PLLStream): string =
|
||||
proc llStreamReadAll*(s: PLLStream): string =
|
||||
const
|
||||
bufSize = 2048
|
||||
case s.kind
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
#
|
||||
#
|
||||
# The Nim Compiler
|
||||
# (c) Copyright 2012 Andreas Rumpf
|
||||
# (c) Copyright 2015 Andreas Rumpf
|
||||
#
|
||||
# See the file "copying.txt", included in this
|
||||
# distribution, for details about the copyright.
|
||||
|
|
@ -22,7 +22,9 @@ proc considerQuotedIdent*(n: PNode): PIdent =
|
|||
of nkSym: result = n.sym.name
|
||||
of nkAccQuoted:
|
||||
case n.len
|
||||
of 0: globalError(n.info, errIdentifierExpected, renderTree(n))
|
||||
of 0:
|
||||
localError(n.info, errIdentifierExpected, renderTree(n))
|
||||
result = getIdent"<Error>"
|
||||
of 1: result = considerQuotedIdent(n.sons[0])
|
||||
else:
|
||||
var id = ""
|
||||
|
|
@ -31,11 +33,14 @@ proc considerQuotedIdent*(n: PNode): PIdent =
|
|||
case x.kind
|
||||
of nkIdent: id.add(x.ident.s)
|
||||
of nkSym: id.add(x.sym.name.s)
|
||||
else: globalError(n.info, errIdentifierExpected, renderTree(n))
|
||||
else:
|
||||
localError(n.info, errIdentifierExpected, renderTree(n))
|
||||
return getIdent"<Error>"
|
||||
result = getIdent(id)
|
||||
of nkOpenSymChoice, nkClosedSymChoice: result = n.sons[0].sym.name
|
||||
else:
|
||||
globalError(n.info, errIdentifierExpected, renderTree(n))
|
||||
localError(n.info, errIdentifierExpected, renderTree(n))
|
||||
result = getIdent"<Error>"
|
||||
|
||||
template addSym*(scope: PScope, s: PSym) =
|
||||
strTableAdd(scope.symbols, s)
|
||||
|
|
@ -82,6 +87,16 @@ proc searchInScopes*(c: PContext, s: PIdent): PSym =
|
|||
if result != nil: return
|
||||
result = nil
|
||||
|
||||
proc debugScopes*(c: PContext; limit=0) {.deprecated.} =
|
||||
var i = 0
|
||||
for scope in walkScopes(c.currentScope):
|
||||
echo "scope ", i
|
||||
for h in 0 .. high(scope.symbols.data):
|
||||
if scope.symbols.data[h] != nil:
|
||||
echo scope.symbols.data[h].name.s
|
||||
if i == limit: break
|
||||
inc i
|
||||
|
||||
proc searchInScopes*(c: PContext, s: PIdent, filter: TSymKinds): PSym =
|
||||
for scope in walkScopes(c.currentScope):
|
||||
result = strTableGet(scope.symbols, s)
|
||||
|
|
@ -161,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)
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
#
|
||||
#
|
||||
# The Nim Compiler
|
||||
# (c) Copyright 2014 Andreas Rumpf
|
||||
# (c) Copyright 2015 Andreas Rumpf
|
||||
#
|
||||
# See the file "copying.txt", included in this
|
||||
# distribution, for details about the copyright.
|
||||
|
|
@ -13,9 +13,12 @@ const
|
|||
genPrefix* = ":tmp" # prefix for generated names
|
||||
|
||||
import ast, astalgo, types, idents, magicsys, msgs, options
|
||||
from guards import createMagic
|
||||
from trees import getMagic
|
||||
|
||||
proc newDeref*(n: PNode): PNode {.inline.} =
|
||||
result = newNodeIT(nkHiddenDeref, n.info, n.typ.sons[0])
|
||||
addSon(result, n)
|
||||
|
||||
proc newTupleAccess*(tup: PNode, i: int): PNode =
|
||||
result = newNodeIT(nkBracketExpr, tup.info, tup.typ.skipTypes(
|
||||
abstractInst).sons[i])
|
||||
|
|
@ -204,6 +207,17 @@ proc flowVarKind(t: PType): TFlowVarKind =
|
|||
elif containsGarbageCollectedRef(t): fvInvalid
|
||||
else: fvBlob
|
||||
|
||||
proc typeNeedsNoDeepCopy(t: PType): bool =
|
||||
var t = t.skipTypes(abstractInst)
|
||||
# for the tconvexhull example (and others) we're a bit lax here and pretend
|
||||
# seqs and strings are *by value* only and 'shallow' doesn't exist!
|
||||
if t.kind == tyString: return true
|
||||
# note that seq[T] is fine, but 'var seq[T]' is not, so we need to skip 'var'
|
||||
# for the stricter check and likewise we can skip 'seq' for a less
|
||||
# strict check:
|
||||
if t.kind in {tyVar, tySequence}: t = t.sons[0]
|
||||
result = not containsGarbageCollectedRef(t)
|
||||
|
||||
proc addLocalVar(varSection, varInit: PNode; owner: PSym; typ: PType;
|
||||
v: PNode; useShallowCopy=false): PSym =
|
||||
result = newSym(skTemp, getIdent(genPrefix), owner, varSection.info)
|
||||
|
|
@ -216,7 +230,7 @@ proc addLocalVar(varSection, varInit: PNode; owner: PSym; typ: PType;
|
|||
vpart.sons[2] = if varInit.isNil: v else: ast.emptyNode
|
||||
varSection.add vpart
|
||||
if varInit != nil:
|
||||
if useShallowCopy:
|
||||
if useShallowCopy and typeNeedsNoDeepCopy(typ):
|
||||
varInit.add newFastAsgnStmt(newSymNode(result), v)
|
||||
else:
|
||||
let deepCopyCall = newNodeI(nkCall, varInit.info, 3)
|
||||
|
|
@ -372,11 +386,11 @@ proc getRoot*(n: PNode): PSym =
|
|||
if getMagic(n) == mSlice: result = getRoot(n.sons[1])
|
||||
else: discard
|
||||
|
||||
proc newIntLit(value: BiggestInt): PNode =
|
||||
proc newIntLit*(value: BiggestInt): PNode =
|
||||
result = nkIntLit.newIntNode(value)
|
||||
result.typ = getSysType(tyInt)
|
||||
|
||||
proc genHigh(n: PNode): PNode =
|
||||
proc genHigh*(n: PNode): PNode =
|
||||
if skipTypes(n.typ, abstractVar).kind in {tyArrayConstr, tyArray}:
|
||||
result = newIntLit(lastOrd(skipTypes(n.typ, abstractVar)))
|
||||
else:
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
#
|
||||
#
|
||||
# The Nim Compiler
|
||||
# (c) Copyright 2014 Andreas Rumpf
|
||||
# (c) Copyright 2015 Andreas Rumpf
|
||||
#
|
||||
# See the file "copying.txt", included in this
|
||||
# distribution, for details about the copyright.
|
||||
|
|
@ -19,12 +19,6 @@ import
|
|||
|
||||
from magicsys import systemModule, resetSysTypes
|
||||
|
||||
const
|
||||
hasLLVM_Backend = false
|
||||
|
||||
when hasLLVM_Backend:
|
||||
import llvmgen
|
||||
|
||||
proc rodPass =
|
||||
if optSymbolFiles in gGlobalOptions:
|
||||
registerPass(rodwritePass)
|
||||
|
|
@ -60,20 +54,16 @@ proc commandDoc2 =
|
|||
finishDoc2Pass(gProjectName)
|
||||
|
||||
proc commandCompileToC =
|
||||
extccomp.initVars()
|
||||
semanticPasses()
|
||||
registerPass(cgenPass)
|
||||
rodPass()
|
||||
#registerPass(cleanupPass())
|
||||
if optCaasEnabled in gGlobalOptions:
|
||||
# echo "BEFORE CHECK DEP"
|
||||
# discard checkDepMem(gProjectMainIdx)
|
||||
# echo "CHECK DEP COMPLETE"
|
||||
discard
|
||||
|
||||
compileProject()
|
||||
cgenWriteModules()
|
||||
if gCmd != cmdRun:
|
||||
extccomp.callCCompiler(changeFileExt(gProjectFull, ""))
|
||||
extccomp.callCCompiler(if gProjectName == "-": "stdinfile" else: changeFileExt(gProjectFull, ""))
|
||||
|
||||
if isServing:
|
||||
# caas will keep track only of the compilation commands
|
||||
|
|
@ -111,14 +101,6 @@ proc commandCompileToC =
|
|||
ccgutils.resetCaches()
|
||||
GC_fullCollect()
|
||||
|
||||
when hasLLVM_Backend:
|
||||
proc commandCompileToLLVM =
|
||||
semanticPasses()
|
||||
registerPass(llvmgen.llvmgenPass())
|
||||
rodPass()
|
||||
#registerPass(cleanupPass())
|
||||
compileProject()
|
||||
|
||||
proc commandCompileToJS =
|
||||
#incl(gGlobalOptions, optSafeCode)
|
||||
setTarget(osJS, cpuJS)
|
||||
|
|
@ -188,20 +170,18 @@ proc commandSuggest =
|
|||
# cache in a state where "no recompilation is necessary", but the
|
||||
# cgen pass was never executed at all.
|
||||
commandCompileToC()
|
||||
if gDirtyBufferIdx != 0:
|
||||
discard compileModule(gDirtyBufferIdx, {sfDirty})
|
||||
resetModule(gDirtyBufferIdx)
|
||||
if optDef in gGlobalOptions:
|
||||
defFromSourceMap(optTrackPos)
|
||||
let gDirtyBufferIdx = gTrackPos.fileIndex
|
||||
discard compileModule(gDirtyBufferIdx, {sfDirty})
|
||||
resetModule(gDirtyBufferIdx)
|
||||
else:
|
||||
msgs.gErrorMax = high(int) # do not stop after first error
|
||||
semanticPasses()
|
||||
rodPass()
|
||||
# XXX: this handles the case when the dirty buffer is the main file,
|
||||
# but doesn't handle the case when it's imported module
|
||||
var projFile = if gProjectMainIdx == gDirtyOriginalIdx: gDirtyBufferIdx
|
||||
else: gProjectMainIdx
|
||||
compileProject(projFile)
|
||||
#var projFile = if gProjectMainIdx == gDirtyOriginalIdx: gDirtyBufferIdx
|
||||
# else: gProjectMainIdx
|
||||
compileProject() #(projFile)
|
||||
|
||||
proc resetMemory =
|
||||
resetCompilationLists()
|
||||
|
|
@ -232,7 +212,6 @@ proc resetMemory =
|
|||
# rodread.gMods
|
||||
|
||||
# !! ropes.cache
|
||||
# semthreads.computed?
|
||||
#
|
||||
# suggest.usageSym
|
||||
#
|
||||
|
|
@ -273,7 +252,6 @@ proc mainCommand* =
|
|||
commandCompileToC()
|
||||
of "cpp", "compiletocpp":
|
||||
gCmd = cmdCompileToCpp
|
||||
if cCompiler == ccGcc: setCC("gcc")
|
||||
defineSymbol("cpp")
|
||||
commandCompileToC()
|
||||
of "objc", "compiletooc":
|
||||
|
|
@ -290,12 +268,6 @@ proc mainCommand* =
|
|||
of "js", "compiletojs":
|
||||
gCmd = cmdCompileToJS
|
||||
commandCompileToJS()
|
||||
of "compiletollvm":
|
||||
gCmd = cmdCompileToLLVM
|
||||
when hasLLVM_Backend:
|
||||
CommandCompileToLLVM()
|
||||
else:
|
||||
rawMessage(errInvalidCommandX, command)
|
||||
of "doc":
|
||||
wantMainModule()
|
||||
gCmd = cmdDoc
|
||||
|
|
@ -392,7 +364,9 @@ proc mainCommand* =
|
|||
gVerbosity > 0):
|
||||
rawMessage(hintSuccessX, [$gLinesCompiled,
|
||||
formatFloat(epochTime() - gLastCmdTime, ffDecimal, 3),
|
||||
formatSize(getTotalMem())])
|
||||
formatSize(getTotalMem()),
|
||||
if condSyms.isDefined("release"): "Release Build"
|
||||
else: "Debug Build"])
|
||||
|
||||
when PrintRopeCacheStats:
|
||||
echo "rope cache stats: "
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
#
|
||||
#
|
||||
# The Nim Compiler
|
||||
# (c) Copyright 2014 Andreas Rumpf
|
||||
# (c) Copyright 2015 Andreas Rumpf
|
||||
#
|
||||
# See the file "copying.txt", included in this
|
||||
# distribution, for details about the copyright.
|
||||
|
|
@ -34,9 +34,6 @@ proc getModule(fileIdx: int32): PSym =
|
|||
if fileIdx >= 0 and fileIdx < gCompiledModules.len:
|
||||
result = gCompiledModules[fileIdx]
|
||||
|
||||
template compiledAt(x: PSym): expr =
|
||||
gMemCacheData[x.position].compiledAt
|
||||
|
||||
template crc(x: PSym): expr =
|
||||
gMemCacheData[x.position].crc
|
||||
|
||||
|
|
@ -74,10 +71,12 @@ proc addDep(x: PSym, dep: int32) =
|
|||
|
||||
proc resetModule*(fileIdx: int32) =
|
||||
# echo "HARD RESETTING ", fileIdx.toFilename
|
||||
gMemCacheData[fileIdx].needsRecompile = Yes
|
||||
gCompiledModules[fileIdx] = nil
|
||||
cgendata.gModules[fileIdx] = nil
|
||||
resetSourceMap(fileIdx)
|
||||
if fileIdx <% gMemCacheData.len:
|
||||
gMemCacheData[fileIdx].needsRecompile = Yes
|
||||
if fileIdx <% gCompiledModules.len:
|
||||
gCompiledModules[fileIdx] = nil
|
||||
if fileIdx <% cgendata.gModules.len:
|
||||
cgendata.gModules[fileIdx] = nil
|
||||
|
||||
proc resetAllModules* =
|
||||
for i in 0..gCompiledModules.high:
|
||||
|
|
@ -117,7 +116,7 @@ proc newModule(fileIdx: int32): PSym =
|
|||
result.kind = skModule
|
||||
let filename = fileIdx.toFullPath
|
||||
result.name = getIdent(splitFile(filename).name)
|
||||
if not isNimIdentifier(result.name.s):
|
||||
if result.name.s != "-" and not isNimIdentifier(result.name.s):
|
||||
rawMessage(errInvalidModuleName, result.name.s)
|
||||
|
||||
result.info = newLineInfo(fileIdx, 1, 1)
|
||||
|
|
|
|||
|
|
@ -10,9 +10,6 @@
|
|||
import
|
||||
options, strutils, os, tables, ropes, platform
|
||||
|
||||
when useCaas:
|
||||
import sockets
|
||||
|
||||
type
|
||||
TMsgKind* = enum
|
||||
errUnknown, errIllFormedAstX, errInternal, errCannotOpenFile, errGenerated,
|
||||
|
|
@ -72,7 +69,7 @@ type
|
|||
errInvalidOrderInArrayConstructor,
|
||||
errInvalidOrderInEnumX, errEnumXHasHoles, errExceptExpected, errInvalidTry,
|
||||
errOptionExpected, errXisNoLabel, errNotAllCasesCovered,
|
||||
errUnkownSubstitionVar, errComplexStmtRequiresInd, errXisNotCallable,
|
||||
errUnknownSubstitionVar, errComplexStmtRequiresInd, errXisNotCallable,
|
||||
errNoPragmasAllowedForX, errNoGenericParamsAllowedForX,
|
||||
errInvalidParamKindX, errDefaultArgumentInvalid, errNamedParamHasToBeIdent,
|
||||
errNoReturnTypeForX, errConvNeedsOneArg, errInvalidPragmaX,
|
||||
|
|
@ -92,7 +89,7 @@ type
|
|||
errTIsNotAConcreteType,
|
||||
errInvalidSectionStart, errGridTableNotImplemented, errGeneralParseError,
|
||||
errNewSectionExpected, errWhitespaceExpected, errXisNoValidIndexFile,
|
||||
errCannotRenderX, errVarVarTypeNotAllowed, errInstantiateXExplicitely,
|
||||
errCannotRenderX, errVarVarTypeNotAllowed, errInstantiateXExplicitly,
|
||||
errOnlyACallOpCanBeDelegator, errUsingNoSymbol,
|
||||
errMacroBodyDependsOnGenericTypes,
|
||||
errDestructorNotGenericEnough,
|
||||
|
|
@ -116,11 +113,12 @@ type
|
|||
warnSmallLshouldNotBeUsed, warnUnknownMagic, warnRedefinitionOfLabel,
|
||||
warnUnknownSubstitutionX, warnLanguageXNotSupported,
|
||||
warnFieldXNotSupported, warnCommentXIgnored,
|
||||
warnNilStatement, warnAnalysisLoophole,
|
||||
warnNilStatement, warnTypelessParam,
|
||||
warnDifferentHeaps, warnWriteToForeignHeap, warnUnsafeCode,
|
||||
warnEachIdentIsTuple, warnShadowIdent,
|
||||
warnProveInit, warnProveField, warnProveIndex, warnGcUnsafe, warnGcUnsafe2,
|
||||
warnUninit, warnGcMem, warnLockLevel, warnUser,
|
||||
warnUninit, warnGcMem, warnDestructor, warnLockLevel, warnResultShadowed,
|
||||
warnUser,
|
||||
hintSuccess, hintSuccessX,
|
||||
hintLineTooLong, hintXDeclaredButNotUsed, hintConvToBaseNotNeeded,
|
||||
hintConvFromXtoItselfNotNeeded, hintExprAlwaysX, hintQuitCalled,
|
||||
|
|
@ -282,7 +280,7 @@ const
|
|||
errOptionExpected: "option expected, but found \'$1\'",
|
||||
errXisNoLabel: "\'$1\' is not a label",
|
||||
errNotAllCasesCovered: "not all cases are covered",
|
||||
errUnkownSubstitionVar: "unknown substitution variable: \'$1\'",
|
||||
errUnknownSubstitionVar: "unknown substitution variable: \'$1\'",
|
||||
errComplexStmtRequiresInd: "complex statement requires indentation",
|
||||
errXisNotCallable: "\'$1\' is not callable",
|
||||
errNoPragmasAllowedForX: "no pragmas allowed for $1",
|
||||
|
|
@ -314,7 +312,7 @@ const
|
|||
errXOnlyAtModuleScope: "\'$1\' is only allowed at top level",
|
||||
errXNeedsParamObjectType: "'$1' needs a parameter that has an object type",
|
||||
errTemplateInstantiationTooNested: "template/macro instantiation too nested",
|
||||
errInstantiationFrom: "instantiation from here",
|
||||
errInstantiationFrom: "template/generic instantiation from here",
|
||||
errInvalidIndexValueForTuple: "invalid index value for tuple subscript",
|
||||
errCommandExpectsFilename: "command expects a filename argument",
|
||||
errMainModuleMustBeSpecified: "please, specify a main module in the project configuration file",
|
||||
|
|
@ -328,12 +326,12 @@ const
|
|||
errXisNoValidIndexFile: "\'$1\' is no valid index file",
|
||||
errCannotRenderX: "cannot render reStructuredText element \'$1\'",
|
||||
errVarVarTypeNotAllowed: "type \'var var\' is not allowed",
|
||||
errInstantiateXExplicitely: "instantiate '$1' explicitely",
|
||||
errInstantiateXExplicitly: "instantiate '$1' explicitly",
|
||||
errOnlyACallOpCanBeDelegator: "only a call operator can be a delegator",
|
||||
errUsingNoSymbol: "'$1' is not a variable, constant or a proc name",
|
||||
errMacroBodyDependsOnGenericTypes: "the macro body cannot be compiled, " &
|
||||
"because the parameter '$1' has a generic type",
|
||||
errDestructorNotGenericEnough: "Destructor signarue is too specific. " &
|
||||
errDestructorNotGenericEnough: "Destructor signature is too specific. " &
|
||||
"A destructor must be associated will all instantiations of a generic type",
|
||||
errInlineIteratorsAsProcParams: "inline iterators can be used as parameters only for " &
|
||||
"templates, macros and other inline iterators",
|
||||
|
|
@ -362,7 +360,7 @@ const
|
|||
errCannotInferReturnType: "cannot infer the return type of the proc",
|
||||
errGenericLambdaNotAllowed: "A nested proc can have generic parameters only when " &
|
||||
"it is used as an operand to another routine and the types " &
|
||||
"of the generic paramers can be infered from the expected signature.",
|
||||
"of the generic paramers can be inferred from the expected signature.",
|
||||
errCompilerDoesntSupportTarget: "The current compiler \'$1\' doesn't support the requested compilation target",
|
||||
errUser: "$1",
|
||||
warnCannotOpenFile: "cannot open \'$1\' [CannotOpenFile]",
|
||||
|
|
@ -379,7 +377,7 @@ const
|
|||
warnFieldXNotSupported: "field \'$1\' not supported [FieldXNotSupported]",
|
||||
warnCommentXIgnored: "comment \'$1\' ignored [CommentXIgnored]",
|
||||
warnNilStatement: "'nil' statement is deprecated; use an empty 'discard' statement instead [NilStmt]",
|
||||
warnAnalysisLoophole: "thread analysis incomplete due to unknown call '$1' [AnalysisLoophole]",
|
||||
warnTypelessParam: "'$1' has no type. Typeless parameters are deprecated; only allowed for 'template' [TypelessParam]",
|
||||
warnDifferentHeaps: "possible inconsistency of thread local heaps [DifferentHeaps]",
|
||||
warnWriteToForeignHeap: "write to foreign heap [WriteToForeignHeap]",
|
||||
warnUnsafeCode: "unsafe code: '$1' [UnsafeCode]",
|
||||
|
|
@ -389,13 +387,15 @@ const
|
|||
warnProveField: "cannot prove that field '$1' is accessible [ProveField]",
|
||||
warnProveIndex: "cannot prove index '$1' is valid [ProveIndex]",
|
||||
warnGcUnsafe: "not GC-safe: '$1' [GcUnsafe]",
|
||||
warnGcUnsafe2: "cannot prove '$1' is GC-safe. Does not compile with --threads:on.",
|
||||
warnGcUnsafe2: "$1",
|
||||
warnUninit: "'$1' might not have been initialized [Uninit]",
|
||||
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]",
|
||||
hintSuccessX: "operation successful ($# lines compiled; $# sec total; $#; $#) [SuccessX]",
|
||||
hintLineTooLong: "line too long [LineTooLong]",
|
||||
hintXDeclaredButNotUsed: "\'$1\' is declared but not used [XDeclaredButNotUsed]",
|
||||
hintConvToBaseNotNeeded: "conversion to base object is not needed [ConvToBaseNotNeeded]",
|
||||
|
|
@ -413,17 +413,17 @@ const
|
|||
hintUser: "$1 [User]"]
|
||||
|
||||
const
|
||||
WarningsToStr*: array[0..28, string] = ["CannotOpenFile", "OctalEscape",
|
||||
WarningsToStr*: array[0..30, string] = ["CannotOpenFile", "OctalEscape",
|
||||
"XIsNeverRead", "XmightNotBeenInit",
|
||||
"Deprecated", "ConfigDeprecated",
|
||||
"SmallLshouldNotBeUsed", "UnknownMagic",
|
||||
"RedefinitionOfLabel", "UnknownSubstitutionX",
|
||||
"LanguageXNotSupported", "FieldXNotSupported",
|
||||
"CommentXIgnored", "NilStmt",
|
||||
"AnalysisLoophole", "DifferentHeaps", "WriteToForeignHeap",
|
||||
"TypelessParam", "DifferentHeaps", "WriteToForeignHeap",
|
||||
"UnsafeCode", "EachIdentIsTuple", "ShadowIdent",
|
||||
"ProveInit", "ProveField", "ProveIndex", "GcUnsafe", "GcUnsafe2", "Uninit",
|
||||
"GcMem", "LockLevel", "User"]
|
||||
"GcMem", "Destructor", "LockLevel", "ResultShadowed", "User"]
|
||||
|
||||
HintsToStr*: array[0..16, string] = ["Success", "SuccessX", "LineTooLong",
|
||||
"XDeclaredButNotUsed", "ConvToBaseNotNeeded", "ConvFromXtoItselfNotNeeded",
|
||||
|
|
@ -445,21 +445,24 @@ type
|
|||
TNoteKind* = range[warnMin..hintMax] # "notes" are warnings or hints
|
||||
TNoteKinds* = set[TNoteKind]
|
||||
|
||||
TFileInfo*{.final.} = object
|
||||
TFileInfo* = object
|
||||
fullPath: string # This is a canonical full filesystem path
|
||||
projPath*: string # This is relative to the project's root
|
||||
shortName*: string # short name of the module
|
||||
quotedName*: PRope # cached quoted short name for codegen
|
||||
quotedName*: Rope # cached quoted short name for codegen
|
||||
# purposes
|
||||
|
||||
lines*: seq[PRope] # the source code of the module
|
||||
lines*: seq[Rope] # the source code of the module
|
||||
# used for better error messages and
|
||||
# embedding the original source in the
|
||||
# generated code
|
||||
dirtyfile: string # the file that is actually read into memory
|
||||
# and parsed; usually 'nil' but is used
|
||||
# for 'nimsuggest'
|
||||
|
||||
TLineInfo*{.final.} = object # This is designed to be as small as possible,
|
||||
TLineInfo* = object # This is designed to be as small as possible,
|
||||
# because it is used
|
||||
# in syntax nodes. We safe space here by using
|
||||
# in syntax nodes. We save space here by using
|
||||
# two int16 and an int32.
|
||||
# On 64 bit and on 32 bit systems this is
|
||||
# only 8 bytes.
|
||||
|
|
@ -490,24 +493,20 @@ proc toCChar*(c: char): string =
|
|||
of '\'', '\"', '\\': result = '\\' & c
|
||||
else: result = $(c)
|
||||
|
||||
proc makeCString*(s: string): PRope =
|
||||
# BUGFIX: We have to split long strings into many ropes. Otherwise
|
||||
# this could trigger an InternalError(). See the ropes module for
|
||||
# further information.
|
||||
proc makeCString*(s: string): Rope =
|
||||
const
|
||||
MaxLineLength = 64
|
||||
result = nil
|
||||
var res = "\""
|
||||
var res = newStringOfCap(int(s.len.toFloat * 1.1) + 1)
|
||||
add(res, "\"")
|
||||
for i in countup(0, len(s) - 1):
|
||||
if (i + 1) mod MaxLineLength == 0:
|
||||
add(res, '\"')
|
||||
add(res, tnl)
|
||||
app(result, toRope(res)) # reset:
|
||||
setLen(res, 1)
|
||||
res[0] = '\"'
|
||||
add(res, '\"')
|
||||
add(res, toCChar(s[i]))
|
||||
add(res, '\"')
|
||||
app(result, toRope(res))
|
||||
add(result, rope(res))
|
||||
|
||||
|
||||
proc newFileInfo(fullPath, projPath: string): TFileInfo =
|
||||
|
|
@ -521,7 +520,7 @@ proc newFileInfo(fullPath, projPath: string): TFileInfo =
|
|||
if optEmbedOrigSrc in gGlobalOptions or true:
|
||||
result.lines = @[]
|
||||
|
||||
proc fileInfoIdx*(filename: string): int32 =
|
||||
proc fileInfoIdx*(filename: string; isKnownFile: var bool): int32 =
|
||||
var
|
||||
canon: string
|
||||
pseudoPath = false
|
||||
|
|
@ -538,11 +537,16 @@ proc fileInfoIdx*(filename: string): int32 =
|
|||
if filenameToIndexTbl.hasKey(canon):
|
||||
result = filenameToIndexTbl[canon]
|
||||
else:
|
||||
isKnownFile = false
|
||||
result = fileInfos.len.int32
|
||||
fileInfos.add(newFileInfo(canon, if pseudoPath: filename
|
||||
else: canon.shortenDir))
|
||||
filenameToIndexTbl[canon] = result
|
||||
|
||||
proc fileInfoIdx*(filename: string): int32 =
|
||||
var dummy: bool
|
||||
result = fileInfoIdx(filename, dummy)
|
||||
|
||||
proc newLineInfo*(fileInfoIdx: int32, line, col: int): TLineInfo =
|
||||
result.fileIndex = fileInfoIdx
|
||||
result.line = int16(line)
|
||||
|
|
@ -560,7 +564,7 @@ var gCodegenLineInfo* = newLineInfo(int32(1), 1, 1)
|
|||
proc raiseRecoverableError*(msg: string) {.noinline, noreturn.} =
|
||||
raise newException(ERecoverableError, msg)
|
||||
|
||||
proc sourceLine*(i: TLineInfo): PRope
|
||||
proc sourceLine*(i: TLineInfo): Rope
|
||||
|
||||
var
|
||||
gNotes*: TNoteKinds = {low(TNoteKind)..high(TNoteKind)} -
|
||||
|
|
@ -571,9 +575,6 @@ var
|
|||
gWarnCounter*: int = 0
|
||||
gErrorMax*: int = 1 # stop after gErrorMax errors
|
||||
|
||||
when useCaas:
|
||||
var stdoutSocket*: Socket
|
||||
|
||||
proc unknownLineInfo*(): TLineInfo =
|
||||
result.line = int16(-1)
|
||||
result.col = int16(-1)
|
||||
|
|
@ -582,35 +583,20 @@ proc unknownLineInfo*(): TLineInfo =
|
|||
var
|
||||
msgContext: seq[TLineInfo] = @[]
|
||||
lastError = unknownLineInfo()
|
||||
bufferedMsgs*: seq[string]
|
||||
|
||||
errorOutputs* = {eStdOut, eStdErr}
|
||||
|
||||
proc clearBufferedMsgs* =
|
||||
bufferedMsgs = nil
|
||||
writelnHook*: proc (output: string) {.closure.}
|
||||
|
||||
proc suggestWriteln*(s: string) =
|
||||
if eStdOut in errorOutputs:
|
||||
when useCaas:
|
||||
if isNil(stdoutSocket): writeln(stdout, s)
|
||||
else:
|
||||
writeln(stdout, s)
|
||||
stdoutSocket.send(s & "\c\L")
|
||||
else:
|
||||
writeln(stdout, s)
|
||||
if isNil(writelnHook): writeln(stdout, s)
|
||||
else: writelnHook(s)
|
||||
|
||||
if eInMemory in errorOutputs:
|
||||
bufferedMsgs.safeAdd(s)
|
||||
proc msgQuit*(x: int8) = quit x
|
||||
proc msgQuit*(x: string) = quit x
|
||||
|
||||
proc suggestQuit*() =
|
||||
if not isServing:
|
||||
quit(0)
|
||||
elif isWorkingWithDirtyBuffer:
|
||||
# No need to compile the rest if we are working with a
|
||||
# throw-away buffer. Incomplete dot expressions frequently
|
||||
# found in dirty buffers will result in errors few steps
|
||||
# from now anyway.
|
||||
raise newException(ESuggestDone, "suggest done")
|
||||
raise newException(ESuggestDone, "suggest done")
|
||||
|
||||
# this format is understood by many text editors: it is the same that
|
||||
# Borland and Freepascal use
|
||||
|
|
@ -646,6 +632,18 @@ proc toFullPath*(fileIdx: int32): string =
|
|||
if fileIdx < 0: result = "???"
|
||||
else: result = fileInfos[fileIdx].fullPath
|
||||
|
||||
proc setDirtyFile*(fileIdx: int32; filename: string) =
|
||||
assert fileIdx >= 0
|
||||
fileInfos[fileIdx].dirtyFile = filename
|
||||
|
||||
proc toFullPathConsiderDirty*(fileIdx: int32): string =
|
||||
if fileIdx < 0:
|
||||
result = "???"
|
||||
elif not fileInfos[fileIdx].dirtyFile.isNil:
|
||||
result = fileInfos[fileIdx].dirtyFile
|
||||
else:
|
||||
result = fileInfos[fileIdx].fullPath
|
||||
|
||||
template toFilename*(info: TLineInfo): string =
|
||||
info.fileIndex.toFilename
|
||||
|
||||
|
|
@ -653,12 +651,12 @@ template toFullPath*(info: TLineInfo): string =
|
|||
info.fileIndex.toFullPath
|
||||
|
||||
proc toMsgFilename*(info: TLineInfo): string =
|
||||
if info.fileIndex < 0: result = "???"
|
||||
if info.fileIndex < 0:
|
||||
result = "???"
|
||||
elif gListFullPaths:
|
||||
result = fileInfos[info.fileIndex].fullPath
|
||||
else:
|
||||
if gListFullPaths:
|
||||
result = fileInfos[info.fileIndex].fullPath
|
||||
else:
|
||||
result = fileInfos[info.fileIndex].projPath
|
||||
result = fileInfos[info.fileIndex].projPath
|
||||
|
||||
proc toLinenumber*(info: TLineInfo): int {.inline.} =
|
||||
result = info.line
|
||||
|
|
@ -672,20 +670,13 @@ proc toFileLine*(info: TLineInfo): string {.inline.} =
|
|||
proc toFileLineCol*(info: TLineInfo): string {.inline.} =
|
||||
result = info.toFilename & "(" & $info.line & "," & $info.col & ")"
|
||||
|
||||
template `$`*(info: TLineInfo): expr = toFileLineCol(info)
|
||||
proc `$`*(info: TLineInfo): string = toFileLineCol(info)
|
||||
|
||||
proc `??`* (info: TLineInfo, filename: string): bool =
|
||||
# only for debugging purposes
|
||||
result = filename in info.toFilename
|
||||
|
||||
var checkPoints*: seq[TLineInfo] = @[]
|
||||
var optTrackPos*: TLineInfo
|
||||
|
||||
proc addCheckpoint*(info: TLineInfo) =
|
||||
checkPoints.add(info)
|
||||
|
||||
proc addCheckpoint*(filename: string, line: int) =
|
||||
addCheckpoint(newLineInfo(filename, line, - 1))
|
||||
var gTrackPos*: TLineInfo
|
||||
|
||||
proc outWriteln*(s: string) =
|
||||
## Writes to stdout. Always.
|
||||
|
|
@ -693,15 +684,16 @@ proc outWriteln*(s: string) =
|
|||
|
||||
proc msgWriteln*(s: string) =
|
||||
## Writes to stdout. If --stdout option is given, writes to stderr instead.
|
||||
if gCmd == cmdIdeTools and optCDebug notin gGlobalOptions: return
|
||||
|
||||
if optStdout in gGlobalOptions:
|
||||
#if gCmd == cmdIdeTools and optCDebug notin gGlobalOptions: return
|
||||
|
||||
if not isNil(writelnHook):
|
||||
writelnHook(s)
|
||||
elif optStdout in gGlobalOptions:
|
||||
if eStdErr in errorOutputs: writeln(stderr, s)
|
||||
else:
|
||||
if eStdOut in errorOutputs: writeln(stdout, s)
|
||||
|
||||
if eInMemory in errorOutputs: bufferedMsgs.safeAdd(s)
|
||||
|
||||
proc coordToStr(coord: int): string =
|
||||
if coord == -1: result = "???"
|
||||
else: result = $coord
|
||||
|
|
@ -713,26 +705,16 @@ proc msgKindToString*(kind: TMsgKind): string =
|
|||
proc getMessageStr(msg: TMsgKind, arg: string): string =
|
||||
result = msgKindToString(msg) % [arg]
|
||||
|
||||
type
|
||||
TCheckPointResult* = enum
|
||||
cpNone, cpFuzzy, cpExact
|
||||
|
||||
proc inCheckpoint*(current: TLineInfo): TCheckPointResult =
|
||||
for i in countup(0, high(checkPoints)):
|
||||
if current.fileIndex == checkPoints[i].fileIndex:
|
||||
if current.line == checkPoints[i].line and
|
||||
abs(current.col-checkPoints[i].col) < 4:
|
||||
return cpExact
|
||||
if current.line >= checkPoints[i].line:
|
||||
return cpFuzzy
|
||||
|
||||
type
|
||||
TErrorHandling = enum doNothing, doAbort, doRaise
|
||||
|
||||
proc handleError(msg: TMsgKind, eh: TErrorHandling, s: string) =
|
||||
template quit =
|
||||
if defined(debug) or gVerbosity >= 3 or msg == errInternal:
|
||||
writeStackTrace()
|
||||
if stackTraceAvailable() and isNil(writelnHook):
|
||||
writeStackTrace()
|
||||
else:
|
||||
msgWriteln("No stack traceback available\nTo create a stacktrace, rerun compilation with ./koch temp " & options.command & " <file>")
|
||||
quit 1
|
||||
|
||||
if msg >= fatalMin and msg <= fatalMax:
|
||||
|
|
@ -756,10 +738,13 @@ proc writeContext(lastinfo: TLineInfo) =
|
|||
if msgContext[i] != lastinfo and msgContext[i] != info:
|
||||
msgWriteln(PosContextFormat % [toMsgFilename(msgContext[i]),
|
||||
coordToStr(msgContext[i].line),
|
||||
coordToStr(msgContext[i].col),
|
||||
coordToStr(msgContext[i].col+1),
|
||||
getMessageStr(errInstantiationFrom, "")])
|
||||
info = msgContext[i]
|
||||
|
||||
proc ignoreMsgBecauseOfIdeTools(msg: TMsgKind): bool =
|
||||
msg >= errGenerated and gCmd == cmdIdeTools and optIdeDebug notin gGlobalOptions
|
||||
|
||||
proc rawMessage*(msg: TMsgKind, args: openArray[string]) =
|
||||
var frmt: string
|
||||
case msg
|
||||
|
|
@ -778,7 +763,8 @@ proc rawMessage*(msg: TMsgKind, args: openArray[string]) =
|
|||
frmt = RawHintFormat
|
||||
inc(gHintCounter)
|
||||
let s = `%`(frmt, `%`(msgKindToString(msg), args))
|
||||
msgWriteln(s)
|
||||
if not ignoreMsgBecauseOfIdeTools(msg):
|
||||
msgWriteln(s)
|
||||
handleError(msg, doAbort, s)
|
||||
|
||||
proc rawMessage*(msg: TMsgKind, arg: string) =
|
||||
|
|
@ -786,8 +772,8 @@ proc rawMessage*(msg: TMsgKind, arg: string) =
|
|||
|
||||
proc writeSurroundingSrc(info: TLineInfo) =
|
||||
const indent = " "
|
||||
msgWriteln(indent & info.sourceLine.ropeToStr)
|
||||
msgWriteln(indent & repeatChar(info.col, ' ') & '^')
|
||||
msgWriteln(indent & $info.sourceLine)
|
||||
msgWriteln(indent & spaces(info.col) & '^')
|
||||
|
||||
proc formatMsg*(info: TLineInfo, msg: TMsgKind, arg: string): string =
|
||||
let frmt = case msg
|
||||
|
|
@ -795,7 +781,7 @@ proc formatMsg*(info: TLineInfo, msg: TMsgKind, arg: string): string =
|
|||
of hintMin..hintMax: PosHintFormat
|
||||
else: PosErrorFormat
|
||||
result = frmt % [toMsgFilename(info), coordToStr(info.line),
|
||||
coordToStr(info.col), getMessageStr(msg, arg)]
|
||||
coordToStr(info.col+1), getMessageStr(msg, arg)]
|
||||
|
||||
proc liMessage(info: TLineInfo, msg: TMsgKind, arg: string,
|
||||
eh: TErrorHandling) =
|
||||
|
|
@ -818,9 +804,12 @@ proc liMessage(info: TLineInfo, msg: TMsgKind, arg: string,
|
|||
ignoreMsg = optHints notin gOptions or msg notin gNotes
|
||||
frmt = PosHintFormat
|
||||
inc(gHintCounter)
|
||||
# NOTE: currently line info line numbers start with 1,
|
||||
# but column numbers start with 0, however most editors expect
|
||||
# first column to be 1, so we need to +1 here
|
||||
let s = frmt % [toMsgFilename(info), coordToStr(info.line),
|
||||
coordToStr(info.col), getMessageStr(msg, arg)]
|
||||
if not ignoreMsg:
|
||||
coordToStr(info.col+1), getMessageStr(msg, arg)]
|
||||
if not ignoreMsg and not ignoreMsgBecauseOfIdeTools(msg):
|
||||
msgWriteln(s)
|
||||
if optPrintSurroundingSrc and msg in errMin..errMax:
|
||||
info.writeSurroundingSrc
|
||||
|
|
@ -841,6 +830,9 @@ proc localError*(info: TLineInfo, msg: TMsgKind, arg = "") =
|
|||
proc localError*(info: TLineInfo, arg: string) =
|
||||
liMessage(info, errGenerated, arg, doNothing)
|
||||
|
||||
proc localError*(info: TLineInfo, format: string, params: openarray[string]) =
|
||||
localError(info, format % params)
|
||||
|
||||
proc message*(info: TLineInfo, msg: TMsgKind, arg = "") =
|
||||
liMessage(info, msg, arg, doNothing)
|
||||
|
||||
|
|
@ -862,9 +854,9 @@ template internalAssert*(e: bool): stmt =
|
|||
if not e: internalError($instantiationInfo())
|
||||
|
||||
proc addSourceLine*(fileIdx: int32, line: string) =
|
||||
fileInfos[fileIdx].lines.add line.toRope
|
||||
fileInfos[fileIdx].lines.add line.rope
|
||||
|
||||
proc sourceLine*(i: TLineInfo): PRope =
|
||||
proc sourceLine*(i: TLineInfo): Rope =
|
||||
if i.fileIndex < 0: return nil
|
||||
|
||||
if not optPreserveOrigSource and fileInfos[i.fileIndex].lines.len == 0:
|
||||
|
|
@ -879,16 +871,14 @@ proc sourceLine*(i: TLineInfo): PRope =
|
|||
|
||||
result = fileInfos[i.fileIndex].lines[i.line-1]
|
||||
|
||||
proc quotedFilename*(i: TLineInfo): PRope =
|
||||
proc quotedFilename*(i: TLineInfo): Rope =
|
||||
internalAssert i.fileIndex >= 0
|
||||
result = fileInfos[i.fileIndex].quotedName
|
||||
|
||||
ropes.errorHandler = proc (err: TRopesError, msg: string, useWarning: bool) =
|
||||
ropes.errorHandler = proc (err: RopesError, msg: string, useWarning: bool) =
|
||||
case err
|
||||
of rInvalidFormatStr:
|
||||
internalError("ropes: invalid format string: " & msg)
|
||||
of rTokenTooLong:
|
||||
internalError("ropes: token too long: " & msg)
|
||||
of rCannotOpenFile:
|
||||
rawMessage(if useWarning: warnCannotOpenFile else: errCannotOpenFile, msg)
|
||||
|
||||
|
|
|
|||
|
|
@ -9,13 +9,14 @@
|
|||
|
||||
when defined(gcc) and defined(windows):
|
||||
when defined(x86):
|
||||
{.link: "icons/nimrod.res".}
|
||||
{.link: "icons/nim.res".}
|
||||
else:
|
||||
{.link: "icons/nimrod_icon.o".}
|
||||
{.link: "icons/nim_icon.o".}
|
||||
|
||||
import
|
||||
commands, lexer, condsyms, options, msgs, nversion, nimconf, ropes,
|
||||
extccomp, strutils, os, osproc, platform, main, parseopt, service
|
||||
extccomp, strutils, os, osproc, platform, main, parseopt, service,
|
||||
nodejs
|
||||
|
||||
when hasTinyCBackend:
|
||||
import tccgen
|
||||
|
|
@ -60,6 +61,8 @@ proc handleCmdLine() =
|
|||
if gCmd == cmdRun:
|
||||
tccgen.run(commands.arguments)
|
||||
if optRun in gGlobalOptions:
|
||||
if gProjectName == "-":
|
||||
gProjectFull = "stdinfile"
|
||||
if gCmd == cmdCompileToJS:
|
||||
var ex: string
|
||||
if options.outFile.len > 0:
|
||||
|
|
@ -67,7 +70,7 @@ proc handleCmdLine() =
|
|||
else:
|
||||
ex = quoteShell(
|
||||
completeCFilePath(changeFileExt(gProjectFull, "js").prependCurDir))
|
||||
execExternalProgram("node " & ex & ' ' & commands.arguments)
|
||||
execExternalProgram(findNodeJs() & " " & ex & ' ' & commands.arguments)
|
||||
else:
|
||||
var binPath: string
|
||||
if options.outFile.len > 0:
|
||||
|
|
@ -89,4 +92,4 @@ condsyms.initDefines()
|
|||
|
||||
when not defined(selftest):
|
||||
handleCmdLine()
|
||||
quit(int8(msgs.gErrorCounter > 0))
|
||||
msgQuit(int8(msgs.gErrorCounter > 0))
|
||||
|
|
|
|||
|
|
@ -1,7 +1,5 @@
|
|||
# Special configuration file for the Nim project
|
||||
|
||||
# gc:markAndSweep
|
||||
|
||||
hint[XDeclaredButNotUsed]:off
|
||||
path:"llvm"
|
||||
path:"$projectPath/.."
|
||||
|
|
@ -20,3 +18,4 @@ define:useStdoutAsStdmsg
|
|||
cs:partial
|
||||
#define:useNodeIds
|
||||
symbol:nimfix
|
||||
#gc:markAndSweep
|
||||
|
|
@ -33,7 +33,7 @@ proc `<.`(a, b: string): bool =
|
|||
while true:
|
||||
let ii = parseInt(a, verA, i)
|
||||
let jj = parseInt(b, verB, j)
|
||||
# if A has no number left, but B has, B is prefered: 0.8 vs 0.8.3
|
||||
# if A has no number left, but B has, B is preferred: 0.8 vs 0.8.3
|
||||
if ii <= 0 or jj <= 0: return jj > 0
|
||||
if verA < verB: return true
|
||||
elif verA > verB: return false
|
||||
|
|
|
|||
|
|
@ -11,10 +11,10 @@
|
|||
|
||||
import
|
||||
llstream, nversion, commands, os, strutils, msgs, platform, condsyms, lexer,
|
||||
options, idents, wordrecg
|
||||
options, idents, wordrecg, strtabs
|
||||
|
||||
# ---------------- configuration file parser -----------------------------
|
||||
# we use Nim's scanner here to safe space and work
|
||||
# we use Nim's scanner here to save space and work
|
||||
|
||||
proc ppGetTok(L: var TLexer, tok: var TToken) =
|
||||
# simple filter
|
||||
|
|
@ -82,17 +82,17 @@ proc doElif(L: var TLexer, tok: var TToken) =
|
|||
proc jumpToDirective(L: var TLexer, tok: var TToken, dest: TJumpDest) =
|
||||
var nestedIfs = 0
|
||||
while true:
|
||||
if (tok.ident != nil) and (tok.ident.s == "@"):
|
||||
if tok.ident != nil and tok.ident.s == "@":
|
||||
ppGetTok(L, tok)
|
||||
case whichKeyword(tok.ident)
|
||||
of wIf:
|
||||
inc(nestedIfs)
|
||||
of wElse:
|
||||
if (dest == jdElseEndif) and (nestedIfs == 0):
|
||||
if dest == jdElseEndif and nestedIfs == 0:
|
||||
doElse(L, tok)
|
||||
break
|
||||
of wElif:
|
||||
if (dest == jdElseEndif) and (nestedIfs == 0):
|
||||
if dest == jdElseEndif and nestedIfs == 0:
|
||||
doElif(L, tok)
|
||||
break
|
||||
of wEnd:
|
||||
|
|
@ -121,7 +121,8 @@ proc parseDirective(L: var TLexer, tok: var TToken) =
|
|||
of wEnd: doEnd(L, tok)
|
||||
of wWrite:
|
||||
ppGetTok(L, tok)
|
||||
msgs.msgWriteln(tokToStr(tok))
|
||||
msgs.msgWriteln(strtabs.`%`(tokToStr(tok), options.gConfigVars,
|
||||
{useEnvironment, useKey}))
|
||||
ppGetTok(L, tok)
|
||||
else:
|
||||
case tok.ident.s.normalize
|
||||
|
|
@ -157,7 +158,7 @@ proc checkSymbol(L: TLexer, tok: TToken) =
|
|||
proc parseAssignment(L: var TLexer, tok: var TToken) =
|
||||
if tok.ident.id == getIdent("-").id or tok.ident.id == getIdent("--").id:
|
||||
confTok(L, tok) # skip unnecessary prefix
|
||||
var info = getLineInfo(L, tok) # safe for later in case of an error
|
||||
var info = getLineInfo(L, tok) # save for later in case of an error
|
||||
checkSymbol(L, tok)
|
||||
var s = tokToStr(tok)
|
||||
confTok(L, tok) # skip symbol
|
||||
|
|
@ -178,9 +179,10 @@ proc parseAssignment(L: var TLexer, tok: var TToken) =
|
|||
if tok.tokType == tkBracketRi: confTok(L, tok)
|
||||
else: lexMessage(L, errTokenExpected, "']'")
|
||||
add(val, ']')
|
||||
if tok.tokType in {tkColon, tkEquals}:
|
||||
let percent = tok.ident.id == getIdent("%=").id
|
||||
if tok.tokType in {tkColon, tkEquals} or percent:
|
||||
if len(val) > 0: add(val, ':')
|
||||
confTok(L, tok) # skip ':' or '='
|
||||
confTok(L, tok) # skip ':' or '=' or '%'
|
||||
checkSymbol(L, tok)
|
||||
add(val, tokToStr(tok))
|
||||
confTok(L, tok) # skip symbol
|
||||
|
|
@ -189,7 +191,11 @@ proc parseAssignment(L: var TLexer, tok: var TToken) =
|
|||
checkSymbol(L, tok)
|
||||
add(val, tokToStr(tok))
|
||||
confTok(L, tok)
|
||||
processSwitch(s, val, passPP, info)
|
||||
if percent:
|
||||
processSwitch(s, strtabs.`%`(val, options.gConfigVars,
|
||||
{useEnvironment, useEmpty}), passPP, info)
|
||||
else:
|
||||
processSwitch(s, val, passPP, info)
|
||||
|
||||
proc readConfigFile(filename: string) =
|
||||
var
|
||||
|
|
@ -246,6 +252,11 @@ proc loadConfigs*(cfg: string) =
|
|||
|
||||
if gProjectName.len != 0:
|
||||
# new project wide config file:
|
||||
let projectConfig = changeFileExt(gProjectFull, "nim.cfg")
|
||||
if fileExists(projectConfig): readConfigFile(projectConfig)
|
||||
else: readConfigFile(changeFileExt(gProjectFull, "nimrod.cfg"))
|
||||
var projectConfig = changeFileExt(gProjectFull, "nimcfg")
|
||||
if not fileExists(projectConfig):
|
||||
projectConfig = changeFileExt(gProjectFull, "nim.cfg")
|
||||
if not fileExists(projectConfig):
|
||||
projectConfig = changeFileExt(gProjectFull, "nimrod.cfg")
|
||||
if fileExists(projectConfig):
|
||||
rawMessage(warnDeprecated, projectConfig)
|
||||
readConfigFile(projectConfig)
|
||||
|
|
|
|||
|
|
@ -24,7 +24,7 @@ proc execute*(program: string) =
|
|||
when hasFFI: defineSymbol("nimffi")
|
||||
registerPass(verbosePass)
|
||||
registerPass(semPass)
|
||||
registerPass(vmPass)
|
||||
registerPass(evalPass)
|
||||
|
||||
appendStr(searchPaths, options.libpath)
|
||||
compileSystemModule()
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
#
|
||||
#
|
||||
# The Nim Compiler
|
||||
# (c) Copyright 2014 Andreas Rumpf
|
||||
# (c) Copyright 2015 Andreas Rumpf
|
||||
#
|
||||
# See the file "copying.txt", included in this
|
||||
# distribution, for details about the copyright.
|
||||
|
|
|
|||
|
|
@ -2,10 +2,10 @@
|
|||
# gc:markAndSweep
|
||||
|
||||
hint[XDeclaredButNotUsed]:off
|
||||
path:"$projectPath/../.."
|
||||
path:"$projectPath/.."
|
||||
|
||||
path:"$lib/packages/docutils"
|
||||
path:"$nim/compiler"
|
||||
path:"../../compiler"
|
||||
|
||||
define:useStdoutAsStdmsg
|
||||
symbol:nimfix
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
#
|
||||
#
|
||||
# The Nim Compiler
|
||||
# (c) Copyright 2014 Andreas Rumpf
|
||||
# (c) Copyright 2015 Andreas Rumpf
|
||||
#
|
||||
# See the file "copying.txt", included in this
|
||||
# distribution, for details about the copyright.
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
#
|
||||
#
|
||||
# The Nim Compiler
|
||||
# (c) Copyright 2014 Andreas Rumpf
|
||||
# (c) Copyright 2015 Andreas Rumpf
|
||||
#
|
||||
# See the file "copying.txt", included in this
|
||||
# distribution, for details about the copyright.
|
||||
|
|
|
|||
|
|
@ -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")
|
||||
|
|
|
|||
198
compiler/nimsuggest/nimsuggest.nim
Normal file
198
compiler/nimsuggest/nimsuggest.nim
Normal file
|
|
@ -0,0 +1,198 @@
|
|||
#
|
||||
#
|
||||
# The Nim Compiler
|
||||
# (c) Copyright 2015 Andreas Rumpf
|
||||
#
|
||||
# See the file "copying.txt", included in this
|
||||
# distribution, for details about the copyright.
|
||||
#
|
||||
|
||||
## Nimsuggest is a tool that helps to give editors IDE like capabilities.
|
||||
|
||||
import strutils, os, parseopt, parseUtils
|
||||
import options, commands, modules, sem, passes, passaux, msgs, nimconf,
|
||||
extccomp, condsyms, lists, net, rdstdin
|
||||
|
||||
const Usage = """
|
||||
Nimsuggest - Tool to give every editor IDE like capabilities for Nim
|
||||
Usage:
|
||||
nimsuggest [options] projectfile.nim
|
||||
|
||||
Options:
|
||||
--port:PORT port, by default 6000
|
||||
--address:HOST binds to that address, by default ""
|
||||
--stdin read commands from stdin and write results to
|
||||
stdout instead of using sockets
|
||||
|
||||
The server then listens to the connection and takes line-based commands.
|
||||
|
||||
In addition, all command line options of Nim that do not affect code generation
|
||||
are supported.
|
||||
"""
|
||||
|
||||
var
|
||||
gPort = 6000.Port
|
||||
gAddress = ""
|
||||
gUseStdin: bool
|
||||
|
||||
const
|
||||
seps = {':', ';', ' ', '\t'}
|
||||
Help = "usage: sug|con|def|use file.nim[;dirtyfile.nim]:line:col\n"&
|
||||
"type 'quit' to quit\n" &
|
||||
"type 'debug' to toggle debug mode on/off\n" &
|
||||
"type 'terse' to toggle terse mode on/off"
|
||||
|
||||
proc parseQuoted(cmd: string; outp: var string; start: int): int =
|
||||
var i = start
|
||||
i += skipWhitespace(cmd, i)
|
||||
if cmd[i] == '"':
|
||||
i += parseUntil(cmd, outp, '"', i+1)+2
|
||||
else:
|
||||
i += parseUntil(cmd, outp, seps, i)
|
||||
result = i
|
||||
|
||||
proc action(cmd: string) =
|
||||
template toggle(sw) =
|
||||
if sw in gGlobalOptions:
|
||||
excl(gGlobalOptions, sw)
|
||||
else:
|
||||
incl(gGlobalOptions, sw)
|
||||
return
|
||||
|
||||
template err() =
|
||||
echo Help
|
||||
return
|
||||
|
||||
var opc = ""
|
||||
var i = parseIdent(cmd, opc, 0)
|
||||
case opc.normalize
|
||||
of "sug": gIdeCmd = ideSug
|
||||
of "con": gIdeCmd = ideCon
|
||||
of "def": gIdeCmd = ideDef
|
||||
of "use":
|
||||
modules.resetAllModules()
|
||||
gIdeCmd = ideUse
|
||||
of "quit": quit()
|
||||
of "debug": toggle optIdeDebug
|
||||
of "terse": toggle optIdeTerse
|
||||
else: err()
|
||||
var dirtyfile = ""
|
||||
var orig = ""
|
||||
i = parseQuoted(cmd, orig, i)
|
||||
if cmd[i] == ';':
|
||||
i = parseQuoted(cmd, dirtyfile, i+1)
|
||||
i += skipWhile(cmd, seps, i)
|
||||
var line = -1
|
||||
var col = 0
|
||||
i += parseInt(cmd, line, i)
|
||||
i += skipWhile(cmd, seps, i)
|
||||
i += parseInt(cmd, col, i)
|
||||
|
||||
var isKnownFile = true
|
||||
if orig.len == 0: err()
|
||||
let dirtyIdx = orig.fileInfoIdx(isKnownFile)
|
||||
|
||||
if dirtyfile.len != 0: msgs.setDirtyFile(dirtyIdx, dirtyfile)
|
||||
else: msgs.setDirtyFile(dirtyIdx, nil)
|
||||
|
||||
resetModule dirtyIdx
|
||||
if dirtyIdx != gProjectMainIdx:
|
||||
resetModule gProjectMainIdx
|
||||
gTrackPos = newLineInfo(dirtyIdx, line, col-1)
|
||||
#echo dirtyfile, gDirtyBufferIdx, " project ", gProjectMainIdx
|
||||
gErrorCounter = 0
|
||||
if not isKnownFile:
|
||||
compileProject(dirtyIdx)
|
||||
else:
|
||||
compileProject()
|
||||
|
||||
proc serve() =
|
||||
# do not stop after the first error:
|
||||
msgs.gErrorMax = high(int)
|
||||
if gUseStdin:
|
||||
echo Help
|
||||
var line = ""
|
||||
while readLineFromStdin("> ", line):
|
||||
action line
|
||||
echo ""
|
||||
flushFile(stdout)
|
||||
else:
|
||||
var server = newSocket()
|
||||
server.bindAddr(gPort, gAddress)
|
||||
var inp = "".TaintedString
|
||||
server.listen()
|
||||
|
||||
while true:
|
||||
var stdoutSocket = newSocket()
|
||||
msgs.writelnHook = proc (line: string) =
|
||||
stdoutSocket.send(line & "\c\L")
|
||||
|
||||
accept(server, stdoutSocket)
|
||||
|
||||
stdoutSocket.readLine(inp)
|
||||
action inp.string
|
||||
|
||||
stdoutSocket.send("\c\L")
|
||||
stdoutSocket.close()
|
||||
|
||||
proc mainCommand =
|
||||
registerPass verbosePass
|
||||
registerPass semPass
|
||||
gCmd = cmdIdeTools
|
||||
incl gGlobalOptions, optCaasEnabled
|
||||
isServing = true
|
||||
wantMainModule()
|
||||
appendStr(searchPaths, options.libpath)
|
||||
if gProjectFull.len != 0:
|
||||
# current path is always looked first for modules
|
||||
prependStr(searchPaths, gProjectPath)
|
||||
|
||||
serve()
|
||||
|
||||
proc processCmdLine*(pass: TCmdLinePass, cmd: string) =
|
||||
var p = parseopt.initOptParser(cmd)
|
||||
while true:
|
||||
parseopt.next(p)
|
||||
case p.kind
|
||||
of cmdEnd: break
|
||||
of cmdLongoption, cmdShortOption:
|
||||
case p.key.normalize
|
||||
of "port": gPort = parseInt(p.val).Port
|
||||
of "address": gAddress = p.val
|
||||
of "stdin": gUseStdin = true
|
||||
else: processSwitch(pass, p)
|
||||
of cmdArgument:
|
||||
options.gProjectName = unixToNativePath(p.key)
|
||||
# if processArgument(pass, p, argsCount): break
|
||||
|
||||
proc handleCmdLine() =
|
||||
if paramCount() == 0:
|
||||
stdout.writeln(Usage)
|
||||
else:
|
||||
processCmdLine(passCmd1, "")
|
||||
if gProjectName != "":
|
||||
try:
|
||||
gProjectFull = canonicalizePath(gProjectName)
|
||||
except OSError:
|
||||
gProjectFull = gProjectName
|
||||
var p = splitFile(gProjectFull)
|
||||
gProjectPath = p.dir
|
||||
gProjectName = p.name
|
||||
else:
|
||||
gProjectPath = getCurrentDir()
|
||||
loadConfigs(DefaultConfig) # load all config files
|
||||
# now process command line arguments again, because some options in the
|
||||
# command line can overwite the config file's settings
|
||||
extccomp.initVars()
|
||||
processCmdLine(passCmd2, "")
|
||||
mainCommand()
|
||||
|
||||
when false:
|
||||
proc quitCalled() {.noconv.} =
|
||||
writeStackTrace()
|
||||
|
||||
addQuitProc(quitCalled)
|
||||
|
||||
condsyms.initDefines()
|
||||
defineSymbol "nimsuggest"
|
||||
handleCmdline()
|
||||
17
compiler/nimsuggest/nimsuggest.nim.cfg
Normal file
17
compiler/nimsuggest/nimsuggest.nim.cfg
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
# Special configuration file for the Nim project
|
||||
|
||||
gc:markAndSweep
|
||||
|
||||
hint[XDeclaredButNotUsed]:off
|
||||
path:"$projectPath/../.."
|
||||
|
||||
path:"$lib/packages/docutils"
|
||||
path:"../../compiler"
|
||||
|
||||
define:useStdoutAsStdmsg
|
||||
define:nimsuggest
|
||||
|
||||
cs:partial
|
||||
#define:useNodeIds
|
||||
define:booting
|
||||
#define:noDocgen
|
||||
6
compiler/nodejs.nim
Normal file
6
compiler/nodejs.nim
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
import os
|
||||
|
||||
proc findNodeJs*(): string =
|
||||
result = findExe("nodejs")
|
||||
if result == "":
|
||||
result = findExe("node")
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
#
|
||||
#
|
||||
# The Nim Compiler
|
||||
# (c) Copyright 2014 Andreas Rumpf
|
||||
# (c) Copyright 2015 Andreas Rumpf
|
||||
#
|
||||
# See the file "copying.txt", included in this
|
||||
# distribution, for details about the copyright.
|
||||
|
|
|
|||
|
|
@ -56,17 +56,14 @@ type # please make sure we have under 32 options
|
|||
optNoMain, # do not generate a "main" proc
|
||||
optThreads, # support for multi-threading
|
||||
optStdout, # output to stdout
|
||||
optSuggest, # ideTools: 'suggest'
|
||||
optContext, # ideTools: 'context'
|
||||
optDef, # ideTools: 'def'
|
||||
optUsages, # ideTools: 'usages'
|
||||
optThreadAnalysis, # thread analysis pass
|
||||
optTaintMode, # taint mode turned on
|
||||
optTlsEmulation, # thread var emulation turned on
|
||||
optGenIndex # generate index file for documentation;
|
||||
optEmbedOrigSrc # embed the original source in the generated code
|
||||
# also: generate header file
|
||||
|
||||
optIdeDebug # idetools: debug mode
|
||||
optIdeTerse # idetools: use terse descriptions
|
||||
TGlobalOptions* = set[TGlobalOption]
|
||||
TCommands* = enum # Nim's commands
|
||||
# **keep binary compatible**
|
||||
|
|
@ -86,6 +83,12 @@ type # please make sure we have under 32 options
|
|||
TGCMode* = enum # the selected GC
|
||||
gcNone, gcBoehm, gcMarkAndSweep, gcRefc, gcV2, gcGenerational
|
||||
|
||||
TIdeCmd* = enum
|
||||
ideNone, ideSug, ideCon, ideDef, ideUse
|
||||
|
||||
var
|
||||
gIdeCmd*: TIdeCmd
|
||||
|
||||
const
|
||||
ChecksOptions* = {optObjCheck, optFieldCheck, optRangeCheck, optNilCheck,
|
||||
optOverflowCheck, optBoundsCheck, optAssert, optNaNCheck, optInfCheck}
|
||||
|
|
@ -111,18 +114,12 @@ var
|
|||
gLastCmdTime*: float # when caas is enabled, we measure each command
|
||||
gListFullPaths*: bool
|
||||
isServing*: bool = false
|
||||
gDirtyBufferIdx* = 0'i32 # indicates the fileIdx of the dirty version of
|
||||
# the tracked source X, saved by the CAAS client.
|
||||
gDirtyOriginalIdx* = 0'i32 # the original source file of the dirtified buffer.
|
||||
gNoNimblePath* = false
|
||||
gExperimentalMode*: bool
|
||||
|
||||
proc importantComments*(): bool {.inline.} = gCmd in {cmdDoc, cmdIdeTools}
|
||||
proc usesNativeGC*(): bool {.inline.} = gSelectedGC >= gcRefc
|
||||
|
||||
template isWorkingWithDirtyBuffer*: expr =
|
||||
gDirtyBufferIdx != 0
|
||||
|
||||
template compilationCachePresent*: expr =
|
||||
{optCaasEnabled, optSymbolFiles} * gGlobalOptions != {}
|
||||
|
||||
|
|
@ -286,7 +283,7 @@ when noTimeMachine:
|
|||
var p = startProcess("/usr/bin/tmutil", args = ["addexclusion", dir])
|
||||
discard p.waitForExit
|
||||
p.close
|
||||
except E_Base, EOS:
|
||||
except Exception:
|
||||
discard
|
||||
|
||||
proc completeGeneratedFilePath*(f: string, createSubDir: bool = true): string =
|
||||
|
|
|
|||
|
|
@ -131,11 +131,10 @@ proc semNodeKindConstraints*(p: PNode): PNode =
|
|||
result.strVal.add(ppEof)
|
||||
|
||||
type
|
||||
TSideEffectAnalysis = enum
|
||||
TSideEffectAnalysis* = enum
|
||||
seUnknown, seSideEffect, seNoSideEffect
|
||||
|
||||
proc checkForSideEffects(n: PNode): TSideEffectAnalysis =
|
||||
# XXX is 'raise' a side effect?
|
||||
proc checkForSideEffects*(n: PNode): TSideEffectAnalysis =
|
||||
case n.kind
|
||||
of nkCallKinds:
|
||||
# only calls can produce side effects:
|
||||
|
|
@ -162,6 +161,8 @@ proc checkForSideEffects(n: PNode): TSideEffectAnalysis =
|
|||
# an atom cannot produce a side effect:
|
||||
result = seNoSideEffect
|
||||
else:
|
||||
# assume no side effect:
|
||||
result = seNoSideEffect
|
||||
for i in 0 .. <n.len:
|
||||
let ret = checkForSideEffects(n.sons[i])
|
||||
if ret == seSideEffect: return ret
|
||||
|
|
@ -174,7 +175,8 @@ type
|
|||
arLValue, # is an l-value
|
||||
arLocalLValue, # is an l-value, but local var; must not escape
|
||||
# its stack frame!
|
||||
arDiscriminant # is a discriminant
|
||||
arDiscriminant, # is a discriminant
|
||||
arStrange # it is a strange beast like 'typedesc[var T]'
|
||||
|
||||
proc isAssignable*(owner: PSym, n: PNode): TAssignableResult =
|
||||
## 'owner' can be nil!
|
||||
|
|
@ -188,6 +190,11 @@ proc isAssignable*(owner: PSym, n: PNode): TAssignableResult =
|
|||
result = arLocalLValue
|
||||
else:
|
||||
result = arLValue
|
||||
elif n.sym.kind == skParam and n.sym.typ.kind == tyVar:
|
||||
result = arLValue
|
||||
elif n.sym.kind == skType:
|
||||
let t = n.sym.typ.skipTypes({tyTypeDesc})
|
||||
if t.kind == tyVar: result = arStrange
|
||||
of nkDotExpr:
|
||||
if skipTypes(n.sons[0].typ, abstractInst-{tyTypeDesc}).kind in
|
||||
{tyVar, tyPtr, tyRef}:
|
||||
|
|
@ -211,7 +218,7 @@ proc isAssignable*(owner: PSym, n: PNode): TAssignableResult =
|
|||
elif compareTypes(n.typ, n.sons[1].typ, dcEqIgnoreDistinct):
|
||||
# types that are equal modulo distinction preserve l-value:
|
||||
result = isAssignable(owner, n.sons[1])
|
||||
of nkHiddenDeref, nkDerefExpr:
|
||||
of nkHiddenDeref, nkDerefExpr, nkHiddenAddr:
|
||||
result = arLValue
|
||||
of nkObjUpConv, nkObjDownConv, nkCheckedFieldExpr:
|
||||
result = isAssignable(owner, n.sons[0])
|
||||
|
|
@ -221,6 +228,9 @@ proc isAssignable*(owner: PSym, n: PNode): TAssignableResult =
|
|||
else:
|
||||
discard
|
||||
|
||||
proc isLValue*(n: PNode): bool =
|
||||
isAssignable(nil, n) in {arLValue, arLocalLValue, arStrange}
|
||||
|
||||
proc matchNodeKinds*(p, n: PNode): bool =
|
||||
# matches the parameter constraint 'p' against the concrete AST 'n'.
|
||||
# Efficiency matters here.
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
#
|
||||
#
|
||||
# The Nim Compiler
|
||||
# (c) Copyright 2014 Andreas Rumpf
|
||||
# (c) Copyright 2015 Andreas Rumpf
|
||||
#
|
||||
# See the file "copying.txt", included in this
|
||||
# distribution, for details about the copyright.
|
||||
|
|
@ -91,7 +91,7 @@ proc closeParser(p: var TParser) =
|
|||
|
||||
proc parMessage(p: TParser, msg: TMsgKind, arg = "") =
|
||||
## Produce and emit the parser message `arg` to output.
|
||||
lexMessage(p.lex, msg, arg)
|
||||
lexMessageTok(p.lex, msg, p.tok, arg)
|
||||
|
||||
proc parMessage(p: TParser, msg: TMsgKind, tok: TToken) =
|
||||
## Produce and emit a parser message to output about the token `tok`
|
||||
|
|
@ -111,7 +111,12 @@ proc rawSkipComment(p: var TParser, node: PNode) =
|
|||
if p.tok.tokType == tkComment:
|
||||
if node != nil:
|
||||
if node.comment == nil: node.comment = ""
|
||||
add(node.comment, p.tok.literal)
|
||||
if p.tok.literal == "[]":
|
||||
node.flags.incl nfIsCursor
|
||||
#echo "parser: "
|
||||
#debug node
|
||||
else:
|
||||
add(node.comment, p.tok.literal)
|
||||
else:
|
||||
parMessage(p, errInternal, "skipComment")
|
||||
getTok(p)
|
||||
|
|
@ -149,7 +154,7 @@ proc eat(p: var TParser, tokType: TTokType) =
|
|||
if p.tok.tokType == tokType:
|
||||
getTok(p)
|
||||
else:
|
||||
lexMessage(p.lex, errTokenExpected, TokTypeToStr[tokType])
|
||||
lexMessageTok(p.lex, errTokenExpected, p.tok, TokTypeToStr[tokType])
|
||||
|
||||
proc parLineInfo(p: TParser): TLineInfo =
|
||||
## Retrieve the line information associated with the parser's current state.
|
||||
|
|
@ -193,8 +198,8 @@ proc isSigilLike(tok: TToken): bool {.inline.} =
|
|||
|
||||
proc isRightAssociative(tok: TToken): bool {.inline.} =
|
||||
## Determines whether the token is right assocative.
|
||||
result = tok.tokType == tkOpr and (tok.ident.s[0] == '^' or
|
||||
(let L = tok.ident.s.len; L > 1 and tok.ident.s[L-1] == '>'))
|
||||
result = tok.tokType == tkOpr and tok.ident.s[0] == '^'
|
||||
# or (let L = tok.ident.s.len; L > 1 and tok.ident.s[L-1] == '>'))
|
||||
|
||||
proc getPrecedence(tok: TToken, strongSpaces: bool): int =
|
||||
## Calculates the precedence of the given token.
|
||||
|
|
@ -207,27 +212,29 @@ proc getPrecedence(tok: TToken, strongSpaces: bool): int =
|
|||
let relevantChar = tok.ident.s[0]
|
||||
|
||||
# arrow like?
|
||||
if L > 1 and tok.ident.s[L-1] == '>': return considerStrongSpaces(1)
|
||||
if L > 1 and tok.ident.s[L-1] == '>' and
|
||||
tok.ident.s[L-2] in {'-', '~', '='}: return considerStrongSpaces(1)
|
||||
|
||||
template considerAsgn(value: expr) =
|
||||
result = if tok.ident.s[L-1] == '=': 1 else: considerStrongSpaces(value)
|
||||
result = if tok.ident.s[L-1] == '=': 1 else: value
|
||||
|
||||
case relevantChar
|
||||
of '$', '^': considerAsgn(10)
|
||||
of '*', '%', '/', '\\': considerAsgn(9)
|
||||
of '~': result = considerStrongSpaces(8)
|
||||
of '~': result = 8
|
||||
of '+', '-', '|': considerAsgn(8)
|
||||
of '&': considerAsgn(7)
|
||||
of '=', '<', '>', '!': result = considerStrongSpaces(5)
|
||||
of '=', '<', '>', '!': result = 5
|
||||
of '.': considerAsgn(6)
|
||||
of '?': result = considerStrongSpaces(2)
|
||||
of '?': result = 2
|
||||
else: considerAsgn(2)
|
||||
of tkDiv, tkMod, tkShl, tkShr: result = 9
|
||||
of tkIn, tkNotin, tkIs, tkIsnot, tkNot, tkOf, tkAs: result = 5
|
||||
of tkDotDot: result = considerStrongSpaces(6)
|
||||
of tkDotDot: result = 6
|
||||
of tkAnd: result = 4
|
||||
of tkOr, tkXor, tkPtr, tkRef: result = 3
|
||||
else: result = -10
|
||||
else: return -10
|
||||
result = considerStrongSpaces(result)
|
||||
|
||||
proc isOperator(tok: TToken): bool =
|
||||
## Determines if the given token is an operator type token.
|
||||
|
|
@ -236,9 +243,15 @@ proc isOperator(tok: TToken): bool =
|
|||
|
||||
proc isUnary(p: TParser): bool =
|
||||
## Check if the current parser token is a unary operator
|
||||
p.strongSpaces and p.tok.tokType in {tkOpr, tkDotDot} and
|
||||
p.tok.strongSpaceB == 0 and
|
||||
p.tok.strongSpaceA > 0
|
||||
if p.tok.tokType in {tkOpr, tkDotDot} and
|
||||
p.tok.strongSpaceB == 0 and
|
||||
p.tok.strongSpaceA > 0:
|
||||
# XXX change this after 0.10.4 is out
|
||||
if p.strongSpaces:
|
||||
result = true
|
||||
else:
|
||||
parMessage(p, warnDeprecated,
|
||||
"will be parsed as unary operator; inconsistent spacing")
|
||||
|
||||
proc checkBinary(p: TParser) {.inline.} =
|
||||
## Check if the current parser token is a binary operator.
|
||||
|
|
@ -261,7 +274,7 @@ proc checkBinary(p: TParser) {.inline.} =
|
|||
#| operator = OP0 | OP1 | OP2 | OP3 | OP4 | OP5 | OP6 | OP7 | OP8 | OP9
|
||||
#| | 'or' | 'xor' | 'and'
|
||||
#| | 'is' | 'isnot' | 'in' | 'notin' | 'of'
|
||||
#| | 'div' | 'mod' | 'shl' | 'shr' | 'not' | 'addr' | 'static' | '..'
|
||||
#| | 'div' | 'mod' | 'shl' | 'shr' | 'not' | 'static' | '..'
|
||||
#|
|
||||
#| prefixOperator = operator
|
||||
#|
|
||||
|
|
@ -286,9 +299,9 @@ proc colcom(p: var TParser, n: PNode) =
|
|||
|
||||
proc parseSymbol(p: var TParser, allowNil = false): PNode =
|
||||
#| symbol = '`' (KEYW|IDENT|literal|(operator|'('|')'|'['|']'|'{'|'}'|'=')+)+ '`'
|
||||
#| | IDENT
|
||||
#| | IDENT | 'addr' | 'type'
|
||||
case p.tok.tokType
|
||||
of tkSymbol:
|
||||
of tkSymbol, tkAddr, tkType:
|
||||
result = newIdentNodeP(p.tok.ident, p)
|
||||
getTok(p)
|
||||
of tkAccent:
|
||||
|
|
@ -319,7 +332,10 @@ proc parseSymbol(p: var TParser, allowNil = false): PNode =
|
|||
getTok(p)
|
||||
else:
|
||||
parMessage(p, errIdentifierExpected, p.tok)
|
||||
getTok(p) # BUGFIX: We must consume a token here to prevent endless loops!
|
||||
# BUGFIX: We must consume a token here to prevent endless loops!
|
||||
# But: this really sucks for idetools and keywords, so we don't do it
|
||||
# if it is a keyword:
|
||||
if not isKeyword(p.tok.tokType): getTok(p)
|
||||
result = ast.emptyNode
|
||||
|
||||
proc indexExpr(p: var TParser): PNode =
|
||||
|
|
@ -373,29 +389,18 @@ proc exprList(p: var TParser, endTok: TTokType, result: PNode) =
|
|||
if p.tok.tokType != tkComma: break
|
||||
getTok(p)
|
||||
optInd(p, a)
|
||||
eat(p, endTok)
|
||||
|
||||
proc dotExpr(p: var TParser, a: PNode): PNode =
|
||||
#| dotExpr = expr '.' optInd ('type' | 'addr' | symbol)
|
||||
#| dotExpr = expr '.' optInd symbol
|
||||
var info = p.parLineInfo
|
||||
getTok(p)
|
||||
optInd(p, a)
|
||||
case p.tok.tokType
|
||||
of tkType:
|
||||
result = newNodeP(nkTypeOfExpr, p)
|
||||
getTok(p)
|
||||
addSon(result, a)
|
||||
of tkAddr:
|
||||
result = newNodeP(nkAddr, p)
|
||||
getTok(p)
|
||||
addSon(result, a)
|
||||
else:
|
||||
result = newNodeI(nkDotExpr, info)
|
||||
addSon(result, a)
|
||||
addSon(result, parseSymbol(p))
|
||||
result = newNodeI(nkDotExpr, info)
|
||||
optInd(p, result)
|
||||
addSon(result, a)
|
||||
addSon(result, parseSymbol(p))
|
||||
|
||||
proc qualifiedIdent(p: var TParser): PNode =
|
||||
#| qualifiedIdent = symbol ('.' optInd ('type' | 'addr' | symbol))?
|
||||
#| qualifiedIdent = symbol ('.' optInd symbol)?
|
||||
result = parseSymbol(p)
|
||||
if p.tok.tokType == tkDot: result = dotExpr(p, result)
|
||||
|
||||
|
|
@ -562,7 +567,7 @@ proc identOrLiteral(p: var TParser, mode: TPrimaryMode): PNode =
|
|||
#| tupleConstr = '(' optInd (exprColonEqExpr comma?)* optPar ')'
|
||||
#| arrayConstr = '[' optInd (exprColonEqExpr comma?)* optPar ']'
|
||||
case p.tok.tokType
|
||||
of tkSymbol:
|
||||
of tkSymbol, tkType, tkAddr:
|
||||
result = newIdentNodeP(p.tok.ident, p)
|
||||
getTok(p)
|
||||
result = parseGStrLit(p, result)
|
||||
|
|
@ -668,11 +673,11 @@ proc namedParams(p: var TParser, callee: PNode,
|
|||
proc parseMacroColon(p: var TParser, x: PNode): PNode
|
||||
proc primarySuffix(p: var TParser, r: PNode, baseIndent: int): PNode =
|
||||
#| primarySuffix = '(' (exprColonEqExpr comma?)* ')' doBlocks?
|
||||
#| | doBlocks
|
||||
#| | '.' optInd ('type' | 'addr' | symbol) generalizedLit?
|
||||
#| | '[' optInd indexExprList optPar ']'
|
||||
#| | '{' optInd indexExprList optPar '}'
|
||||
#| | &( '`'|IDENT|literal|'cast') expr # command syntax
|
||||
#| | doBlocks
|
||||
#| | '.' optInd symbol generalizedLit?
|
||||
#| | '[' optInd indexExprList optPar ']'
|
||||
#| | '{' optInd indexExprList optPar '}'
|
||||
#| | &( '`'|IDENT|literal|'cast'|'addr'|'type') expr # command syntax
|
||||
result = r
|
||||
while p.tok.indent < 0 or
|
||||
(p.tok.tokType == tkDot and p.tok.indent >= baseIndent):
|
||||
|
|
@ -698,21 +703,22 @@ proc primarySuffix(p: var TParser, r: PNode, baseIndent: int): PNode =
|
|||
of tkCurlyLe:
|
||||
if p.strongSpaces and p.tok.strongSpaceA > 0: break
|
||||
result = namedParams(p, result, nkCurlyExpr, tkCurlyRi)
|
||||
of tkSymbol, tkAccent, tkIntLit..tkCharLit, tkNil, tkCast:
|
||||
of tkSymbol, tkAccent, tkIntLit..tkCharLit, tkNil, tkCast, tkAddr, tkType:
|
||||
if p.inPragma == 0:
|
||||
# actually parsing {.push hints:off.} as {.push(hints:off).} is a sweet
|
||||
# solution, but pragmas.nim can't handle that
|
||||
let a = result
|
||||
result = newNodeP(nkCommand, p)
|
||||
addSon(result, a)
|
||||
addSon result, parseExpr(p)
|
||||
when false:
|
||||
when true:
|
||||
addSon result, parseExpr(p)
|
||||
else:
|
||||
while p.tok.tokType != tkEof:
|
||||
let a = parseExpr(p)
|
||||
addSon(result, a)
|
||||
let x = parseExpr(p)
|
||||
addSon(result, x)
|
||||
if p.tok.tokType != tkComma: break
|
||||
getTok(p)
|
||||
optInd(p, a)
|
||||
optInd(p, x)
|
||||
if p.tok.tokType == tkDo:
|
||||
parseDoBlocks(p, result)
|
||||
else:
|
||||
|
|
@ -737,7 +743,7 @@ proc parseOperators(p: var TParser, headNode: PNode,
|
|||
var a = newNodeP(nkInfix, p)
|
||||
var opNode = newIdentNodeP(p.tok.ident, p) # skip operator:
|
||||
getTok(p)
|
||||
optInd(p, opNode)
|
||||
optInd(p, a)
|
||||
# read sub-expression with higher priority:
|
||||
var b = simpleExprAux(p, opPrec + leftAssoc, modeB)
|
||||
addSon(a, opNode)
|
||||
|
|
@ -857,6 +863,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:
|
||||
|
|
@ -886,6 +893,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) ')'
|
||||
|
|
@ -935,8 +944,7 @@ proc parseDoBlock(p: var TParser): PNode =
|
|||
getTok(p)
|
||||
let params = parseParamList(p, retColon=false)
|
||||
let pragmas = optPragmas(p)
|
||||
eat(p, tkColon)
|
||||
skipComment(p, result)
|
||||
colcom(p, result)
|
||||
result = newProcNode(nkDo, info, parseStmt(p),
|
||||
params = params,
|
||||
pragmas = pragmas)
|
||||
|
|
@ -1020,11 +1028,10 @@ proc parseObject(p: var TParser): PNode
|
|||
proc parseTypeClass(p: var TParser): PNode
|
||||
|
||||
proc primary(p: var TParser, mode: TPrimaryMode): PNode =
|
||||
#| typeKeyw = 'var' | 'ref' | 'ptr' | 'shared' | 'type' | 'tuple'
|
||||
#| typeKeyw = 'var' | 'ref' | 'ptr' | 'shared' | 'tuple'
|
||||
#| | 'proc' | 'iterator' | 'distinct' | 'object' | 'enum'
|
||||
#| primary = typeKeyw typeDescK
|
||||
#| / prefixOperator* identOrLiteral primarySuffix*
|
||||
#| / 'addr' primary
|
||||
#| / 'static' primary
|
||||
#| / 'bind' primary
|
||||
if isOperator(p.tok):
|
||||
|
|
@ -1044,11 +1051,6 @@ proc primary(p: var TParser, mode: TPrimaryMode): PNode =
|
|||
return
|
||||
|
||||
case p.tok.tokType:
|
||||
of tkVar: result = parseTypeDescKAux(p, nkVarTy, mode)
|
||||
of tkRef: result = parseTypeDescKAux(p, nkRefTy, mode)
|
||||
of tkPtr: result = parseTypeDescKAux(p, nkPtrTy, mode)
|
||||
of tkDistinct: result = parseTypeDescKAux(p, nkDistinctTy, mode)
|
||||
of tkType: result = parseTypeDescKAux(p, nkTypeOfExpr, mode)
|
||||
of tkTuple: result = parseTuple(p, mode == pmTypeDef)
|
||||
of tkProc: result = parseProcExpr(p, mode notin {pmTypeDesc, pmTypeDef})
|
||||
of tkIterator:
|
||||
|
|
@ -1077,15 +1079,15 @@ proc primary(p: var TParser, mode: TPrimaryMode): PNode =
|
|||
else:
|
||||
result = newNodeP(nkObjectTy, p)
|
||||
getTok(p)
|
||||
of tkGeneric:
|
||||
of tkGeneric, tkConcept:
|
||||
if mode == pmTypeDef:
|
||||
let wasGeneric = p.tok.tokType == tkGeneric
|
||||
result = parseTypeClass(p)
|
||||
# hack so that it's remembered and can be marked as deprecated in
|
||||
# sem'check:
|
||||
if wasGeneric: result.flags.incl nfBase2
|
||||
else:
|
||||
parMessage(p, errInvalidToken, p.tok)
|
||||
of tkAddr:
|
||||
result = newNodeP(nkAddr, p)
|
||||
getTokNoInd(p)
|
||||
addSon(result, primary(p, pmNormal))
|
||||
of tkStatic:
|
||||
let info = parLineInfo(p)
|
||||
getTokNoInd(p)
|
||||
|
|
@ -1099,6 +1101,10 @@ proc primary(p: var TParser, mode: TPrimaryMode): PNode =
|
|||
getTok(p)
|
||||
optInd(p, result)
|
||||
addSon(result, primary(p, pmNormal))
|
||||
of tkVar: result = parseTypeDescKAux(p, nkVarTy, mode)
|
||||
of tkRef: result = parseTypeDescKAux(p, nkRefTy, mode)
|
||||
of tkPtr: result = parseTypeDescKAux(p, nkPtrTy, mode)
|
||||
of tkDistinct: result = parseTypeDescKAux(p, nkDistinctTy, mode)
|
||||
else:
|
||||
let baseInd = p.lex.currLineIndent
|
||||
result = identOrLiteral(p, mode)
|
||||
|
|
@ -1111,7 +1117,7 @@ proc parseTypeDesc(p: var TParser): PNode =
|
|||
|
||||
proc parseTypeDefAux(p: var TParser): PNode =
|
||||
#| typeDefAux = simpleExpr
|
||||
#| | 'generic' typeClass
|
||||
#| | 'concept' typeClass
|
||||
result = simpleExpr(p, pmTypeDef)
|
||||
|
||||
proc makeCall(n: PNode): PNode =
|
||||
|
|
@ -1132,9 +1138,11 @@ proc parseMacroColon(p: var TParser, x: PNode): PNode =
|
|||
result = makeCall(result)
|
||||
getTok(p)
|
||||
skipComment(p, result)
|
||||
let stmtList = newNodeP(nkStmtList, p)
|
||||
if p.tok.tokType notin {tkOf, tkElif, tkElse, tkExcept}:
|
||||
let body = parseStmt(p)
|
||||
addSon(result, makeStmtList(body))
|
||||
stmtList.add body
|
||||
#addSon(result, makeStmtList(body))
|
||||
while sameInd(p):
|
||||
var b: PNode
|
||||
case p.tok.tokType
|
||||
|
|
@ -1146,19 +1154,22 @@ proc parseMacroColon(p: var TParser, x: PNode): PNode =
|
|||
getTok(p)
|
||||
optInd(p, b)
|
||||
addSon(b, parseExpr(p))
|
||||
eat(p, tkColon)
|
||||
of tkExcept:
|
||||
b = newNodeP(nkExceptBranch, p)
|
||||
exprList(p, tkColon, b)
|
||||
skipComment(p, b)
|
||||
of tkElse:
|
||||
b = newNodeP(nkElse, p)
|
||||
getTok(p)
|
||||
eat(p, tkColon)
|
||||
else: break
|
||||
eat(p, tkColon)
|
||||
addSon(b, parseStmt(p))
|
||||
addSon(result, b)
|
||||
addSon(stmtList, b)
|
||||
if b.kind == nkElse: break
|
||||
if stmtList.len == 1 and stmtList[0].kind == nkStmtList:
|
||||
# to keep backwards compatibility (see tests/vm/tstringnil)
|
||||
result.add stmtList[0]
|
||||
else:
|
||||
result.add stmtList
|
||||
|
||||
proc parseExprStmt(p: var TParser): PNode =
|
||||
#| exprStmt = simpleExpr
|
||||
|
|
@ -1167,9 +1178,7 @@ proc parseExprStmt(p: var TParser): PNode =
|
|||
#| doBlocks
|
||||
#| / macroColon
|
||||
#| ))?
|
||||
inc p.inPragma
|
||||
var a = simpleExpr(p)
|
||||
dec p.inPragma
|
||||
if p.tok.tokType == tkEquals:
|
||||
getTok(p)
|
||||
optInd(p, result)
|
||||
|
|
@ -1178,8 +1187,20 @@ proc parseExprStmt(p: var TParser): PNode =
|
|||
addSon(result, a)
|
||||
addSon(result, b)
|
||||
else:
|
||||
if p.tok.indent < 0 and isExprStart(p):
|
||||
result = newNode(nkCommand, a.info, @[a])
|
||||
# simpleExpr parsed 'p a' from 'p a, b'?
|
||||
if p.tok.indent < 0 and p.tok.tokType == tkComma and a.kind == nkCommand:
|
||||
result = a
|
||||
while true:
|
||||
getTok(p)
|
||||
optInd(p, result)
|
||||
var e = parseExpr(p)
|
||||
addSon(result, e)
|
||||
if p.tok.tokType != tkComma: break
|
||||
elif p.tok.indent < 0 and isExprStart(p):
|
||||
if a.kind == nkCommand:
|
||||
result = a
|
||||
else:
|
||||
result = newNode(nkCommand, a.info, @[a])
|
||||
while true:
|
||||
var e = parseExpr(p)
|
||||
addSon(result, e)
|
||||
|
|
@ -1293,8 +1314,7 @@ proc parseIfOrWhen(p: var TParser, kind: TNodeKind): PNode =
|
|||
var branch = newNodeP(nkElifBranch, p)
|
||||
optInd(p, branch)
|
||||
addSon(branch, parseExpr(p))
|
||||
eat(p, tkColon)
|
||||
skipComment(p, branch)
|
||||
colcom(p, branch)
|
||||
addSon(branch, parseStmt(p))
|
||||
skipComment(p, branch)
|
||||
addSon(result, branch)
|
||||
|
|
@ -1302,8 +1322,7 @@ proc parseIfOrWhen(p: var TParser, kind: TNodeKind): PNode =
|
|||
if p.tok.tokType == tkElse and sameOrNoInd(p):
|
||||
var branch = newNodeP(nkElse, p)
|
||||
eat(p, tkElse)
|
||||
eat(p, tkColon)
|
||||
skipComment(p, branch)
|
||||
colcom(p, branch)
|
||||
addSon(branch, parseStmt(p))
|
||||
addSon(result, branch)
|
||||
|
||||
|
|
@ -1351,13 +1370,11 @@ proc parseCase(p: var TParser): PNode =
|
|||
getTok(p)
|
||||
optInd(p, b)
|
||||
addSon(b, parseExpr(p))
|
||||
eat(p, tkColon)
|
||||
of tkElse:
|
||||
b = newNodeP(nkElse, p)
|
||||
getTok(p)
|
||||
eat(p, tkColon)
|
||||
else: break
|
||||
skipComment(p, b)
|
||||
colcom(p, b)
|
||||
addSon(b, parseStmt(p))
|
||||
addSon(result, b)
|
||||
if b.kind == nkElse: break
|
||||
|
|
@ -1374,8 +1391,7 @@ proc parseTry(p: var TParser; isExpr: bool): PNode =
|
|||
#| (optInd 'finally' colcom stmt)?
|
||||
result = newNodeP(nkTryStmt, p)
|
||||
getTok(p)
|
||||
eat(p, tkColon)
|
||||
skipComment(p, result)
|
||||
colcom(p, result)
|
||||
addSon(result, parseStmt(p))
|
||||
var b: PNode = nil
|
||||
while sameOrNoInd(p) or isExpr:
|
||||
|
|
@ -1385,10 +1401,9 @@ proc parseTry(p: var TParser; isExpr: bool): PNode =
|
|||
exprList(p, tkColon, b)
|
||||
of tkFinally:
|
||||
b = newNodeP(nkFinally, p)
|
||||
getTokNoInd(p)
|
||||
eat(p, tkColon)
|
||||
getTok(p)
|
||||
else: break
|
||||
skipComment(p, b)
|
||||
colcom(p, b)
|
||||
addSon(b, parseStmt(p))
|
||||
addSon(result, b)
|
||||
if b.kind == nkFinally: break
|
||||
|
|
@ -1397,7 +1412,7 @@ proc parseTry(p: var TParser; isExpr: bool): PNode =
|
|||
proc parseExceptBlock(p: var TParser, kind: TNodeKind): PNode =
|
||||
#| exceptBlock = 'except' colcom stmt
|
||||
result = newNodeP(kind, p)
|
||||
getTokNoInd(p)
|
||||
getTok(p)
|
||||
colcom(p, result)
|
||||
addSon(result, parseStmt(p))
|
||||
|
||||
|
|
@ -1430,7 +1445,7 @@ proc parseStaticOrDefer(p: var TParser; k: TNodeKind): PNode =
|
|||
#| staticStmt = 'static' colcom stmt
|
||||
#| deferStmt = 'defer' colcom stmt
|
||||
result = newNodeP(k, p)
|
||||
getTokNoInd(p)
|
||||
getTok(p)
|
||||
colcom(p, result)
|
||||
addSon(result, parseStmt(p))
|
||||
|
||||
|
|
@ -1589,6 +1604,7 @@ proc parseEnum(p: var TParser): PNode =
|
|||
optInd(p, result)
|
||||
while true:
|
||||
var a = parseSymbol(p)
|
||||
if a.kind == nkEmpty: return
|
||||
if p.tok.indent >= 0 and p.tok.indent <= p.currInd:
|
||||
add(result, a)
|
||||
break
|
||||
|
|
@ -1610,7 +1626,7 @@ proc parseEnum(p: var TParser): PNode =
|
|||
p.tok.tokType == tkEof:
|
||||
break
|
||||
if result.len <= 1:
|
||||
lexMessage(p.lex, errIdentifierExpected, prettyTok(p.tok))
|
||||
lexMessageTok(p.lex, errIdentifierExpected, p.tok, prettyTok(p.tok))
|
||||
|
||||
proc parseObjectPart(p: var TParser): PNode
|
||||
proc parseObjectWhen(p: var TParser): PNode =
|
||||
|
|
@ -1668,9 +1684,8 @@ proc parseObjectCase(p: var TParser): PNode =
|
|||
of tkElse:
|
||||
b = newNodeP(nkElse, p)
|
||||
getTok(p)
|
||||
eat(p, tkColon)
|
||||
else: break
|
||||
skipComment(p, b)
|
||||
colcom(p, b)
|
||||
var fields = parseObjectPart(p)
|
||||
if fields.kind == nkEmpty:
|
||||
parMessage(p, errIdentifierExpected, p.tok)
|
||||
|
|
@ -1683,14 +1698,14 @@ proc parseObjectCase(p: var TParser): PNode =
|
|||
|
||||
proc parseObjectPart(p: var TParser): PNode =
|
||||
#| objectPart = IND{>} objectPart^+IND{=} DED
|
||||
#| / objectWhen / objectCase / 'nil' / declColonEquals
|
||||
#| / objectWhen / objectCase / 'nil' / 'discard' / declColonEquals
|
||||
if realInd(p):
|
||||
result = newNodeP(nkRecList, p)
|
||||
withInd(p):
|
||||
rawSkipComment(p, result)
|
||||
while sameInd(p):
|
||||
case p.tok.tokType
|
||||
of tkCase, tkWhen, tkSymbol, tkAccent, tkNil:
|
||||
of tkCase, tkWhen, tkSymbol, tkAccent, tkNil, tkDiscard:
|
||||
addSon(result, parseObjectPart(p))
|
||||
else:
|
||||
parMessage(p, errIdentifierExpected, p.tok)
|
||||
|
|
@ -1704,7 +1719,7 @@ proc parseObjectPart(p: var TParser): PNode =
|
|||
of tkSymbol, tkAccent:
|
||||
result = parseIdentColonEquals(p, {withPragma})
|
||||
skipComment(p, result)
|
||||
of tkNil:
|
||||
of tkNil, tkDiscard:
|
||||
result = newNodeP(nkNilLit, p)
|
||||
getTok(p)
|
||||
else:
|
||||
|
|
@ -1735,8 +1750,8 @@ proc parseObject(p: var TParser): PNode =
|
|||
|
||||
proc parseTypeClassParam(p: var TParser): PNode =
|
||||
if p.tok.tokType == tkVar:
|
||||
result = newNodeP(nkVarTy, p)
|
||||
getTok(p)
|
||||
result = newNode(nkVarTy)
|
||||
result.addSon(p.parseSymbol)
|
||||
else:
|
||||
result = p.parseSymbol
|
||||
|
|
@ -1747,7 +1762,7 @@ proc parseTypeClass(p: var TParser): PNode =
|
|||
#| &IND{>} stmt
|
||||
result = newNodeP(nkTypeClassTy, p)
|
||||
getTok(p)
|
||||
var args = newNode(nkArgList)
|
||||
var args = newNodeP(nkArgList, p)
|
||||
addSon(result, args)
|
||||
addSon(args, p.parseTypeClassParam)
|
||||
while p.tok.tokType == tkComma:
|
||||
|
|
@ -1866,7 +1881,7 @@ proc simpleStmt(p: var TParser): PNode =
|
|||
|
||||
proc complexOrSimpleStmt(p: var TParser): PNode =
|
||||
#| complexOrSimpleStmt = (ifStmt | whenStmt | whileStmt
|
||||
#| | tryStmt | finallyStmt | exceptStmt | forStmt
|
||||
#| | tryStmt | forStmt
|
||||
#| | blockStmt | staticStmt | deferStmt | asmStmt
|
||||
#| | 'proc' routine
|
||||
#| | 'method' routine
|
||||
|
|
@ -2013,8 +2028,8 @@ proc parseString*(s: string; filename: string = ""; line: int = 0;
|
|||
var parser: TParser
|
||||
# XXX for now the builtin 'parseStmt/Expr' functions do not know about strong
|
||||
# spaces...
|
||||
openParser(parser, filename, stream, false)
|
||||
parser.lex.errorHandler = errorHandler
|
||||
openParser(parser, filename, stream, false)
|
||||
|
||||
result = parser.parseAll
|
||||
closeParser(parser)
|
||||
|
|
|
|||
|
|
@ -169,8 +169,12 @@ proc processModule(module: PSym, stream: PLLStream, rd: PRodReader) =
|
|||
if rd == nil:
|
||||
openPasses(a, module)
|
||||
if stream == nil:
|
||||
let filename = fileIdx.toFullPath
|
||||
s = llStreamOpen(filename, fmRead)
|
||||
let filename = fileIdx.toFullPathConsiderDirty
|
||||
if module.name.s == "-":
|
||||
module.name.s = "stdinfile"
|
||||
s = llStreamOpen(stdin)
|
||||
else:
|
||||
s = llStreamOpen(filename, fmRead)
|
||||
if s == nil:
|
||||
rawMessage(errCannotOpenFile, filename)
|
||||
return
|
||||
|
|
|
|||
|
|
@ -275,7 +275,7 @@ proc applyRule*(c: PContext, s: PSym, n: PNode): PNode =
|
|||
if arg != rs and aliases.isPartOf(rs, arg) == arYes:
|
||||
ok = true
|
||||
break
|
||||
# constraint not fullfilled:
|
||||
# constraint not fulfilled:
|
||||
if not ok: return nil
|
||||
of aqNoAlias:
|
||||
# it MUST not alias with any other param:
|
||||
|
|
@ -284,7 +284,7 @@ proc applyRule*(c: PContext, s: PSym, n: PNode): PNode =
|
|||
if arg != rs and aliases.isPartOf(rs, arg) != arNo:
|
||||
ok = false
|
||||
break
|
||||
# constraint not fullfilled:
|
||||
# constraint not fulfilled:
|
||||
if not ok: return nil
|
||||
|
||||
markUsed(n.info, s)
|
||||
|
|
|
|||
|
|
@ -21,8 +21,8 @@ type
|
|||
# conditionals to condsyms (end of module).
|
||||
osNone, osDos, osWindows, osOs2, osLinux, osMorphos, osSkyos, osSolaris,
|
||||
osIrix, osNetbsd, osFreebsd, osOpenbsd, osAix, osPalmos, osQnx, osAmiga,
|
||||
osAtari, osNetware, osMacos, osMacosx, osHaiku, osJS, osNimrodVM,
|
||||
osStandalone
|
||||
osAtari, osNetware, osMacos, osMacosx, osHaiku, osVxworks,
|
||||
osJS, osNimrodVM, osStandalone
|
||||
|
||||
type
|
||||
TInfoOSProp* = enum
|
||||
|
|
@ -136,6 +136,10 @@ const
|
|||
objExt: ".o", newLine: "\x0A", pathSep: ":", dirSep: "/",
|
||||
scriptExt: ".sh", curDir: ".", exeExt: "", extSep: ".",
|
||||
props: {ospNeedsPIC, ospPosix, ospLacksThreadVars}),
|
||||
(name: "VxWorks", parDir: "..", dllFrmt: "lib$1.so", altDirSep: "/",
|
||||
objExt: ".o", newLine: "\x0A", pathSep: ";", dirSep: "\\",
|
||||
scriptExt: ".sh", curDir: ".", exeExt: ".vxe", extSep: ".",
|
||||
props: {ospNeedsPIC, ospPosix, ospLacksThreadVars}),
|
||||
(name: "JS", parDir: "..",
|
||||
dllFrmt: "lib$1.so", altDirSep: "/",
|
||||
objExt: ".o", newLine: "\x0A",
|
||||
|
|
|
|||
43
compiler/plugins.nim
Normal file
43
compiler/plugins.nim
Normal file
|
|
@ -0,0 +1,43 @@
|
|||
#
|
||||
#
|
||||
# The Nim Compiler
|
||||
# (c) Copyright 2015 Andreas Rumpf
|
||||
#
|
||||
# See the file "copying.txt", included in this
|
||||
# distribution, for details about the copyright.
|
||||
#
|
||||
|
||||
## Plugin support for the Nim compiler. Right now there are no plugins and they
|
||||
## need to be build with the compiler, no DLL support.
|
||||
|
||||
import ast, semdata, idents
|
||||
|
||||
type
|
||||
Transformation* = proc (c: PContext; n: PNode): PNode {.nimcall.}
|
||||
Plugin = ref object
|
||||
fn, module, package: PIdent
|
||||
t: Transformation
|
||||
next: Plugin
|
||||
|
||||
proc pluginMatches(p: Plugin; s: PSym): bool =
|
||||
if s.name.id != p.fn.id: return false
|
||||
let module = s.owner
|
||||
if module == nil or module.kind != skModule or
|
||||
module.name.id != p.module.id: return false
|
||||
let package = module.owner
|
||||
if package == nil or package.kind != skPackage or
|
||||
package.name.id != p.package.id: return false
|
||||
return true
|
||||
|
||||
var head: Plugin
|
||||
|
||||
proc getPlugin*(fn: PSym): Transformation =
|
||||
var it = head
|
||||
while it != nil:
|
||||
if pluginMatches(it, fn): return it.t
|
||||
it = it.next
|
||||
|
||||
proc registerPlugin*(package, module, fn: string; t: Transformation) =
|
||||
let oldHead = head
|
||||
head = Plugin(fn: getIdent(fn), module: getIdent(module),
|
||||
package: getIdent(package), t: t, next: oldHead)
|
||||
13
compiler/plugins/active.nim
Normal file
13
compiler/plugins/active.nim
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
#
|
||||
#
|
||||
# The Nim Compiler
|
||||
# (c) Copyright 2015 Andreas Rumpf
|
||||
#
|
||||
# See the file "copying.txt", included in this
|
||||
# distribution, for details about the copyright.
|
||||
#
|
||||
|
||||
## Include file that imports all plugins that are active.
|
||||
|
||||
import
|
||||
locals.locals
|
||||
42
compiler/plugins/locals/locals.nim
Normal file
42
compiler/plugins/locals/locals.nim
Normal file
|
|
@ -0,0 +1,42 @@
|
|||
#
|
||||
#
|
||||
# The Nim Compiler
|
||||
# (c) Copyright 2015 Andreas Rumpf
|
||||
#
|
||||
# See the file "copying.txt", included in this
|
||||
# distribution, for details about the copyright.
|
||||
#
|
||||
|
||||
## The builtin 'system.locals' implemented as a plugin.
|
||||
|
||||
import plugins, ast, astalgo, magicsys, lookups, semdata, lowerings
|
||||
|
||||
proc semLocals(c: PContext, n: PNode): PNode =
|
||||
var counter = 0
|
||||
var tupleType = newTypeS(tyTuple, c)
|
||||
result = newNodeIT(nkPar, n.info, tupleType)
|
||||
tupleType.n = newNodeI(nkRecList, n.info)
|
||||
# for now we skip openarrays ...
|
||||
for scope in walkScopes(c.currentScope):
|
||||
if scope == c.topLevelScope: break
|
||||
for it in items(scope.symbols):
|
||||
# XXX parameters' owners are wrong for generics; this caused some pain
|
||||
# for closures too; we should finally fix it.
|
||||
#if it.owner != c.p.owner: return result
|
||||
if it.kind in skLocalVars and
|
||||
it.typ.skipTypes({tyGenericInst, tyVar}).kind notin
|
||||
{tyVarargs, tyOpenArray, tyTypeDesc, tyStatic, tyExpr, tyStmt, tyEmpty}:
|
||||
|
||||
var field = newSym(skField, it.name, getCurrOwner(), n.info)
|
||||
field.typ = it.typ.skipTypes({tyGenericInst, tyVar})
|
||||
field.position = counter
|
||||
inc(counter)
|
||||
|
||||
addSon(tupleType.n, newSymNode(field))
|
||||
addSonSkipIntLit(tupleType, field.typ)
|
||||
|
||||
var a = newSymNode(it, result.info)
|
||||
if it.typ.skipTypes({tyGenericInst}).kind == tyVar: a = newDeref(a)
|
||||
result.add(a)
|
||||
|
||||
registerPlugin("stdlib", "system", "locals", semLocals)
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
#
|
||||
#
|
||||
# The Nim Compiler
|
||||
# (c) Copyright 2014 Andreas Rumpf
|
||||
# (c) Copyright 2015 Andreas Rumpf
|
||||
#
|
||||
# See the file "copying.txt", included in this
|
||||
# distribution, for details about the copyright.
|
||||
|
|
@ -25,7 +25,7 @@ const
|
|||
wBorrow, wExtern, wImportCompilerProc, wThread, wImportCpp, wImportObjC,
|
||||
wAsmNoStackFrame, wError, wDiscardable, wNoInit, wDestructor, wCodegenDecl,
|
||||
wGensym, wInject, wRaises, wTags, wLocks, wDelegator, wGcSafe,
|
||||
wOverride}
|
||||
wOverride, wConstructor}
|
||||
converterPragmas* = procPragmas
|
||||
methodPragmas* = procPragmas
|
||||
templatePragmas* = {wImmediate, wDeprecated, wError, wGensym, wInject, wDirty,
|
||||
|
|
@ -60,7 +60,7 @@ const
|
|||
varPragmas* = {wImportc, wExportc, wVolatile, wRegister, wThreadVar, wNodecl,
|
||||
wMagic, wHeader, wDeprecated, wCompilerproc, wDynlib, wExtern,
|
||||
wImportCpp, wImportObjC, wError, wNoInit, wCompileTime, wGlobal,
|
||||
wGensym, wInject, wCodegenDecl, wGuard}
|
||||
wGensym, wInject, wCodegenDecl, wGuard, wGoto}
|
||||
constPragmas* = {wImportc, wExportc, wHeader, wDeprecated, wMagic, wNodecl,
|
||||
wExtern, wImportCpp, wImportObjC, wError, wGensym, wInject}
|
||||
letPragmas* = varPragmas
|
||||
|
|
@ -89,7 +89,7 @@ proc pragmaAsm*(c: PContext, n: PNode): char =
|
|||
invalidPragma(it)
|
||||
|
||||
proc setExternName(s: PSym, extname: string) =
|
||||
s.loc.r = toRope(extname % s.name.s)
|
||||
s.loc.r = rope(extname % s.name.s)
|
||||
if gCmd == cmdPretty and '$' notin extname:
|
||||
# note that '{.importc.}' is transformed into '{.importc: "$1".}'
|
||||
s.loc.flags.incl(lfFullExternalName)
|
||||
|
|
@ -105,7 +105,7 @@ proc validateExternCName(s: PSym, info: TLineInfo) =
|
|||
## Valid identifiers are those alphanumeric including the underscore not
|
||||
## starting with a number. If the check fails, a generic error will be
|
||||
## displayed to the user.
|
||||
let target = ropeToStr(s.loc.r)
|
||||
let target = $s.loc.r
|
||||
if target.len < 1 or target[0] notin IdentStartChars or
|
||||
not target.allCharsInSet(IdentChars):
|
||||
localError(info, errGenerated, "invalid exported symbol")
|
||||
|
|
@ -411,12 +411,6 @@ proc processCommonLink(c: PContext, n: PNode, feature: TLinkFeature) =
|
|||
proc pragmaBreakpoint(c: PContext, n: PNode) =
|
||||
discard getOptionalStr(c, n, "")
|
||||
|
||||
proc pragmaCheckpoint(c: PContext, n: PNode) =
|
||||
# checkpoints can be used to debug the compiler; they are not documented
|
||||
var info = n.info
|
||||
inc(info.line) # next line is affected!
|
||||
msgs.addCheckpoint(info)
|
||||
|
||||
proc pragmaWatchpoint(c: PContext, n: PNode) =
|
||||
if n.kind == nkExprColonExpr:
|
||||
n.sons[1] = c.semExpr(c, n.sons[1])
|
||||
|
|
@ -453,7 +447,9 @@ proc semAsmOrEmit*(con: PContext, n: PNode, marker: char): PNode =
|
|||
addSon(result, newStrNode(nkStrLit, $marker))
|
||||
if c < 0: break
|
||||
a = c + 1
|
||||
else: illFormedAst(n)
|
||||
else:
|
||||
illFormedAstLocal(n)
|
||||
result = newNode(nkAsmStmt, n.info)
|
||||
|
||||
proc pragmaEmit(c: PContext, n: PNode) =
|
||||
discard getStrLitNode(c, n)
|
||||
|
|
@ -669,8 +665,11 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: int,
|
|||
incl(sym.flags, sfGlobal)
|
||||
incl(sym.flags, sfPure)
|
||||
of wMerge:
|
||||
# only supported for backwards compat, doesn't do anything anymore
|
||||
noVal(it)
|
||||
incl(sym.flags, sfMerge)
|
||||
of wConstructor:
|
||||
noVal(it)
|
||||
incl(sym.flags, sfConstructor)
|
||||
of wHeader:
|
||||
var lib = getLib(c, libHeader, getStrLitNode(c, it))
|
||||
addToLib(lib, sym)
|
||||
|
|
@ -678,7 +677,7 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: int,
|
|||
incl(sym.loc.flags, lfHeader)
|
||||
incl(sym.loc.flags, lfNoDecl)
|
||||
# implies nodecl, because otherwise header would not make sense
|
||||
if sym.loc.r == nil: sym.loc.r = toRope(sym.name.s)
|
||||
if sym.loc.r == nil: sym.loc.r = rope(sym.name.s)
|
||||
of wDestructor:
|
||||
sym.flags.incl sfOverriden
|
||||
if sym.name.s.normalize != "destroy":
|
||||
|
|
@ -739,11 +738,10 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: int,
|
|||
incl(sym.flags, sfProcvar)
|
||||
if sym.typ != nil: incl(sym.typ.flags, tfThread)
|
||||
of wGcSafe:
|
||||
if optThreadAnalysis in gGlobalOptions:
|
||||
noVal(it)
|
||||
if sym.kind != skType: incl(sym.flags, sfThread)
|
||||
if sym.typ != nil: incl(sym.typ.flags, tfGcSafe)
|
||||
else: invalidPragma(it)
|
||||
noVal(it)
|
||||
if sym.kind != skType: incl(sym.flags, sfThread)
|
||||
if sym.typ != nil: incl(sym.typ.flags, tfGcSafe)
|
||||
else: invalidPragma(it)
|
||||
of wPacked:
|
||||
noVal(it)
|
||||
if sym.typ == nil: invalidPragma(it)
|
||||
|
|
@ -845,6 +843,11 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: int,
|
|||
invalidPragma(it)
|
||||
else:
|
||||
sym.guard = pragmaGuard(c, it, sym.kind)
|
||||
of wGoto:
|
||||
if sym == nil or sym.kind notin {skVar, skLet}:
|
||||
invalidPragma(it)
|
||||
else:
|
||||
sym.flags.incl sfGoto
|
||||
of wInjectStmt:
|
||||
if it.kind != nkExprColonExpr:
|
||||
localError(it.info, errExprExpected)
|
||||
|
|
@ -867,9 +870,11 @@ proc implicitPragmas*(c: PContext, sym: PSym, n: PNode,
|
|||
while it != nil:
|
||||
let o = it.otherPragmas
|
||||
if not o.isNil:
|
||||
pushInfoContext(n.info)
|
||||
for i in countup(0, sonsLen(o) - 1):
|
||||
if singlePragma(c, sym, o, i, validPragmas):
|
||||
internalError(n.info, "implicitPragmas")
|
||||
popInfoContext()
|
||||
it = it.next.POptionEntry
|
||||
|
||||
if lfExportLib in sym.loc.flags and sfExportc notin sym.flags:
|
||||
|
|
@ -879,7 +884,7 @@ proc implicitPragmas*(c: PContext, sym: PSym, n: PNode,
|
|||
sfImportc in sym.flags and lib != nil:
|
||||
incl(sym.loc.flags, lfDynamicLib)
|
||||
addToLib(lib, sym)
|
||||
if sym.loc.r == nil: sym.loc.r = toRope(sym.name.s)
|
||||
if sym.loc.r == nil: sym.loc.r = rope(sym.name.s)
|
||||
|
||||
proc hasPragma*(n: PNode, pragma: TSpecialWord): bool =
|
||||
if n == nil or n.sons == nil:
|
||||
|
|
|
|||
|
|
@ -41,17 +41,20 @@ proc renderModule*(n: PNode, filename: string, renderFlags: TRenderFlags = {})
|
|||
proc renderTree*(n: PNode, renderFlags: TRenderFlags = {}): string
|
||||
proc initTokRender*(r: var TSrcGen, n: PNode, renderFlags: TRenderFlags = {})
|
||||
proc getNextTok*(r: var TSrcGen, kind: var TTokType, literal: var string)
|
||||
|
||||
proc `$`*(n: PNode): string = n.renderTree
|
||||
# implementation
|
||||
# We render the source code in a two phases: The first
|
||||
# determines how long the subtree will likely be, the second
|
||||
# phase appends to a buffer that will be the output.
|
||||
|
||||
proc isKeyword*(s: string): bool =
|
||||
var i = getIdent(s)
|
||||
proc isKeyword*(i: PIdent): bool =
|
||||
if (i.id >= ord(tokKeywordLow) - ord(tkSymbol)) and
|
||||
(i.id <= ord(tokKeywordHigh) - ord(tkSymbol)):
|
||||
result = true
|
||||
|
||||
proc isKeyword*(s: string): bool = isKeyword(getIdent(s))
|
||||
|
||||
proc renderDefinitionName*(s: PSym, noQuotes = false): string =
|
||||
## Returns the definition name of the symbol.
|
||||
##
|
||||
|
|
@ -59,7 +62,7 @@ proc renderDefinitionName*(s: PSym, noQuotes = false): string =
|
|||
## happen if the name happens to be a keyword or the first character is not
|
||||
## part of the SymStartChars set.
|
||||
let x = s.name.s
|
||||
if noQuotes or (x[0] in SymStartChars and not renderer.isKeyword(x)):
|
||||
if noQuotes or (x[0] in SymStartChars and not renderer.isKeyword(s.name)):
|
||||
result = x
|
||||
else:
|
||||
result = '`' & x & '`'
|
||||
|
|
@ -91,7 +94,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
|
||||
|
||||
|
|
@ -189,7 +192,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)
|
||||
|
|
@ -279,7 +282,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) =
|
||||
|
|
@ -382,7 +385,7 @@ proc lsub(n: PNode): int =
|
|||
result = lsub(n.sons[0]) + lcomma(n, 1) + 2
|
||||
of nkHiddenStdConv, nkHiddenSubConv, nkHiddenCallConv: result = lsub(n[1])
|
||||
of nkCast: result = lsub(n.sons[0]) + lsub(n.sons[1]) + len("cast[]()")
|
||||
of nkAddr: result = lsub(n.sons[0]) + len("addr()")
|
||||
of nkAddr: result = (if n.len>0: lsub(n.sons[0]) + len("addr()") else: 4)
|
||||
of nkStaticExpr: result = lsub(n.sons[0]) + len("static_")
|
||||
of nkHiddenAddr, nkHiddenDeref: result = lsub(n.sons[0])
|
||||
of nkCommand: result = lsub(n.sons[0]) + lcomma(n, 1) + 1
|
||||
|
|
@ -394,6 +397,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_")
|
||||
|
|
@ -429,7 +433,7 @@ proc lsub(n: PNode): int =
|
|||
len("if_:_")
|
||||
of nkElifExpr: result = lsons(n) + len("_elif_:_")
|
||||
of nkElseExpr: result = lsub(n.sons[0]) + len("_else:_") # type descriptions
|
||||
of nkTypeOfExpr: result = lsub(n.sons[0]) + len("type_")
|
||||
of nkTypeOfExpr: result = (if n.len > 0: lsub(n.sons[0]) else: 0)+len("type_")
|
||||
of nkRefTy: result = (if n.len > 0: lsub(n.sons[0])+1 else: 0) + len("ref")
|
||||
of nkPtrTy: result = (if n.len > 0: lsub(n.sons[0])+1 else: 0) + len("ptr")
|
||||
of nkVarTy: result = (if n.len > 0: lsub(n.sons[0])+1 else: 0) + len("var")
|
||||
|
|
@ -499,6 +503,7 @@ proc gsub(g: var TSrcGen, n: PNode) =
|
|||
|
||||
proc hasCom(n: PNode): bool =
|
||||
result = false
|
||||
if n.isNil: return false
|
||||
if n.comment != nil: return true
|
||||
case n.kind
|
||||
of nkEmpty..nkNilLit: discard
|
||||
|
|
@ -721,7 +726,7 @@ proc gproc(g: var TSrcGen, n: PNode) =
|
|||
proc gTypeClassTy(g: var TSrcGen, n: PNode) =
|
||||
var c: TContext
|
||||
initContext(c)
|
||||
putWithSpace(g, tkGeneric, "generic")
|
||||
putWithSpace(g, tkConcept, "concept")
|
||||
gsons(g, n[0], c) # arglist
|
||||
gsub(g, n[1]) # pragmas
|
||||
gsub(g, n[2]) # of
|
||||
|
|
@ -842,9 +847,10 @@ proc gsub(g: var TSrcGen, n: PNode, c: TContext) =
|
|||
put(g, tkParRi, ")")
|
||||
of nkAddr:
|
||||
put(g, tkAddr, "addr")
|
||||
put(g, tkParLe, "(")
|
||||
gsub(g, n.sons[0])
|
||||
put(g, tkParRi, ")")
|
||||
if n.len > 0:
|
||||
put(g, tkParLe, "(")
|
||||
gsub(g, n.sons[0])
|
||||
put(g, tkParRi, ")")
|
||||
of nkStaticExpr:
|
||||
put(g, tkStatic, "static")
|
||||
put(g, tkSpaces, Space)
|
||||
|
|
@ -1265,9 +1271,12 @@ proc gsub(g: var TSrcGen, n: PNode, c: TContext) =
|
|||
putWithSpace(g, tkColon, ":")
|
||||
gcoms(g)
|
||||
gstmts(g, n.sons[0], c)
|
||||
of nkFinally:
|
||||
of nkFinally, nkDefer:
|
||||
optNL(g)
|
||||
put(g, tkFinally, "finally")
|
||||
if n.kind == nkFinally:
|
||||
put(g, tkFinally, "finally")
|
||||
else:
|
||||
put(g, tkDefer, "defer")
|
||||
putWithSpace(g, tkColon, ":")
|
||||
gcoms(g)
|
||||
gstmts(g, n.sons[0], c)
|
||||
|
|
@ -1291,10 +1300,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])
|
||||
|
|
|
|||
|
|
@ -277,7 +277,7 @@ proc decodeLoc(r: PRodReader, loc: var TLoc, info: TLineInfo) =
|
|||
loc.t = nil
|
||||
if r.s[r.pos] == '!':
|
||||
inc(r.pos)
|
||||
loc.r = toRope(decodeStr(r.s, r.pos))
|
||||
loc.r = rope(decodeStr(r.s, r.pos))
|
||||
else:
|
||||
loc.r = nil
|
||||
if r.s[r.pos] == '>': inc(r.pos)
|
||||
|
|
@ -344,7 +344,7 @@ proc decodeLib(r: PRodReader, info: TLineInfo): PLib =
|
|||
result.kind = TLibKind(decodeVInt(r.s, r.pos))
|
||||
if r.s[r.pos] != '|': internalError("decodeLib: 1")
|
||||
inc(r.pos)
|
||||
result.name = toRope(decodeStr(r.s, r.pos))
|
||||
result.name = rope(decodeStr(r.s, r.pos))
|
||||
if r.s[r.pos] != '|': internalError("decodeLib: 2")
|
||||
inc(r.pos)
|
||||
result.path = decodeNode(r, info)
|
||||
|
|
@ -666,7 +666,7 @@ proc newRodReader(modfilename: string, crc: TCrc32,
|
|||
r.readerIndex = readerIndex
|
||||
r.filename = modfilename
|
||||
initIdTable(r.syms)
|
||||
# we terminate the file explicitely with ``\0``, so the cast to `cstring`
|
||||
# we terminate the file explicitly with ``\0``, so the cast to `cstring`
|
||||
# is safe:
|
||||
r.s = cast[cstring](r.memfile.mem)
|
||||
if startsWith(r.s, "NIM:"):
|
||||
|
|
|
|||
|
|
@ -186,7 +186,7 @@ proc encodeLoc(w: PRodWriter, loc: TLoc, result: var string) =
|
|||
pushType(w, loc.t)
|
||||
if loc.r != nil:
|
||||
add(result, '!')
|
||||
encodeStr(ropeToStr(loc.r), result)
|
||||
encodeStr($loc.r, result)
|
||||
if oldLen + 1 == result.len:
|
||||
# no data was necessary, so remove the '<' again:
|
||||
setLen(result, oldLen)
|
||||
|
|
@ -200,7 +200,7 @@ proc encodeType(w: PRodWriter, t: PType, result: var string) =
|
|||
return
|
||||
# we need no surrounding [] here because the type is in a line of its own
|
||||
if t.kind == tyForward: internalError("encodeType: tyForward")
|
||||
# for the new rodfile viewer we use a preceeding [ so that the data section
|
||||
# for the new rodfile viewer we use a preceding [ so that the data section
|
||||
# can easily be disambiguated:
|
||||
add(result, '[')
|
||||
encodeVInt(ord(t.kind), result)
|
||||
|
|
@ -241,7 +241,7 @@ proc encodeLib(w: PRodWriter, lib: PLib, info: TLineInfo, result: var string) =
|
|||
add(result, '|')
|
||||
encodeVInt(ord(lib.kind), result)
|
||||
add(result, '|')
|
||||
encodeStr(ropeToStr(lib.name), result)
|
||||
encodeStr($lib.name, result)
|
||||
add(result, '|')
|
||||
encodeNode(w, info, lib.path, result)
|
||||
|
||||
|
|
|
|||
|
|
@ -52,81 +52,63 @@
|
|||
# Note that the left and right pointers are not needed for leaves.
|
||||
# Leaves have relatively high memory overhead (~30 bytes on a 32
|
||||
# bit machines) and we produce many of them. This is why we cache and
|
||||
# share leaves accross different rope trees.
|
||||
# share leaves across different rope trees.
|
||||
# To cache them they are inserted in a `cache` array.
|
||||
|
||||
import
|
||||
strutils, platform, hashes, crc, options
|
||||
platform, hashes
|
||||
|
||||
type
|
||||
TFormatStr* = string # later we may change it to CString for better
|
||||
FormatStr* = string # later we may change it to CString for better
|
||||
# performance of the code generator (assignments
|
||||
# copy the format strings
|
||||
# though it is not necessary)
|
||||
PRope* = ref TRope
|
||||
TRope*{.acyclic.} = object of RootObj # the empty rope is represented
|
||||
# by nil to safe space
|
||||
left*, right*: PRope
|
||||
Rope* = ref RopeObj
|
||||
RopeObj*{.acyclic.} = object of RootObj # the empty rope is represented
|
||||
# by nil to safe space
|
||||
left*, right*: Rope
|
||||
length*: int
|
||||
data*: string # != nil if a leaf
|
||||
|
||||
TRopeSeq* = seq[PRope]
|
||||
RopeSeq* = seq[Rope]
|
||||
|
||||
TRopesError* = enum
|
||||
RopesError* = enum
|
||||
rCannotOpenFile
|
||||
rInvalidFormatStr
|
||||
rTokenTooLong
|
||||
|
||||
proc con*(a, b: PRope): PRope
|
||||
proc con*(a: PRope, b: string): PRope
|
||||
proc con*(a: string, b: PRope): PRope
|
||||
proc con*(a: varargs[PRope]): PRope
|
||||
proc app*(a: var PRope, b: PRope)
|
||||
proc app*(a: var PRope, b: string)
|
||||
proc prepend*(a: var PRope, b: PRope)
|
||||
proc toRope*(s: string): PRope
|
||||
proc toRope*(i: BiggestInt): PRope
|
||||
proc ropeLen*(a: PRope): int
|
||||
proc writeRopeIfNotEqual*(r: PRope, filename: string): bool
|
||||
proc ropeToStr*(p: PRope): string
|
||||
proc ropef*(frmt: TFormatStr, args: varargs[PRope]): PRope
|
||||
proc appf*(c: var PRope, frmt: TFormatStr, args: varargs[PRope])
|
||||
proc ropeEqualsFile*(r: PRope, f: string): bool
|
||||
# returns true if the rope r is the same as the contents of file f
|
||||
proc ropeInvariant*(r: PRope): bool
|
||||
# exported for debugging
|
||||
# implementation
|
||||
|
||||
var errorHandler*: proc(err: TRopesError, msg: string, useWarning = false)
|
||||
var errorHandler*: proc(err: RopesError, msg: string, useWarning = false)
|
||||
# avoid dependency on msgs.nim
|
||||
|
||||
proc ropeLen(a: PRope): int =
|
||||
proc len*(a: Rope): int =
|
||||
## the rope's length
|
||||
if a == nil: result = 0
|
||||
else: result = a.length
|
||||
|
||||
proc newRope*(data: string = nil): PRope =
|
||||
proc newRope(data: string = nil): Rope =
|
||||
new(result)
|
||||
if data != nil:
|
||||
result.length = len(data)
|
||||
result.data = data
|
||||
|
||||
proc newMutableRope*(capacity = 30): PRope =
|
||||
proc newMutableRope*(capacity = 30): Rope =
|
||||
## creates a new rope that supports direct modifications of the rope's
|
||||
## 'data' and 'length' fields.
|
||||
new(result)
|
||||
result.data = newStringOfCap(capacity)
|
||||
|
||||
proc freezeMutableRope*(r: PRope) {.inline.} =
|
||||
proc freezeMutableRope*(r: Rope) {.inline.} =
|
||||
r.length = r.data.len
|
||||
|
||||
var
|
||||
cache: array[0..2048*2 -1, PRope]
|
||||
cache: array[0..2048*2 - 1, Rope]
|
||||
|
||||
proc resetRopeCache* =
|
||||
for i in low(cache)..high(cache):
|
||||
cache[i] = nil
|
||||
|
||||
proc ropeInvariant(r: PRope): bool =
|
||||
proc ropeInvariant(r: Rope): bool =
|
||||
if r == nil:
|
||||
result = true
|
||||
else:
|
||||
|
|
@ -143,7 +125,7 @@ var gCacheTries* = 0
|
|||
var gCacheMisses* = 0
|
||||
var gCacheIntTries* = 0
|
||||
|
||||
proc insertInCache(s: string): PRope =
|
||||
proc insertInCache(s: string): Rope =
|
||||
inc gCacheTries
|
||||
var h = hash(s) and high(cache)
|
||||
result = cache[h]
|
||||
|
|
@ -152,82 +134,77 @@ proc insertInCache(s: string): PRope =
|
|||
result = newRope(s)
|
||||
cache[h] = result
|
||||
|
||||
proc toRope(s: string): PRope =
|
||||
proc rope*(s: string): Rope =
|
||||
## Converts a string to a rope.
|
||||
if s.len == 0:
|
||||
result = nil
|
||||
else:
|
||||
result = insertInCache(s)
|
||||
assert(ropeInvariant(result))
|
||||
|
||||
proc ropeSeqInsert(rs: var TRopeSeq, r: PRope, at: Natural) =
|
||||
var length = len(rs)
|
||||
if at > length:
|
||||
setLen(rs, at + 1)
|
||||
else:
|
||||
setLen(rs, length + 1) # move old rope elements:
|
||||
for i in countdown(length, at + 1):
|
||||
rs[i] = rs[i - 1] # this is correct, I used pen and paper to validate it
|
||||
rs[at] = r
|
||||
proc rope*(i: BiggestInt): Rope =
|
||||
## Converts an int to a rope.
|
||||
inc gCacheIntTries
|
||||
result = rope($i)
|
||||
|
||||
proc newRecRopeToStr(result: var string, resultLen: var int, r: PRope) =
|
||||
var stack = @[r]
|
||||
while len(stack) > 0:
|
||||
var it = pop(stack)
|
||||
while it.data == nil:
|
||||
add(stack, it.right)
|
||||
it = it.left
|
||||
assert(it.data != nil)
|
||||
copyMem(addr(result[resultLen]), addr(it.data[0]), it.length)
|
||||
inc(resultLen, it.length)
|
||||
assert(resultLen <= len(result))
|
||||
proc rope*(f: BiggestFloat): Rope =
|
||||
## Converts a float to a rope.
|
||||
result = rope($f)
|
||||
|
||||
proc ropeToStr(p: PRope): string =
|
||||
if p == nil:
|
||||
result = ""
|
||||
else:
|
||||
result = newString(p.length)
|
||||
var resultLen = 0
|
||||
newRecRopeToStr(result, resultLen, p)
|
||||
|
||||
proc con(a, b: PRope): PRope =
|
||||
if a == nil: result = b
|
||||
elif b == nil: result = a
|
||||
proc `&`*(a, b: Rope): Rope =
|
||||
if a == nil:
|
||||
result = b
|
||||
elif b == nil:
|
||||
result = a
|
||||
else:
|
||||
result = newRope()
|
||||
result.length = a.length + b.length
|
||||
result.left = a
|
||||
result.right = b
|
||||
|
||||
proc con(a: PRope, b: string): PRope = result = con(a, toRope(b))
|
||||
proc con(a: string, b: PRope): PRope = result = con(toRope(a), b)
|
||||
proc `&`*(a: Rope, b: string): Rope =
|
||||
## the concatenation operator for ropes.
|
||||
result = a & rope(b)
|
||||
|
||||
proc con(a: varargs[PRope]): PRope =
|
||||
for i in countup(0, high(a)): result = con(result, a[i])
|
||||
proc `&`*(a: string, b: Rope): Rope =
|
||||
## the concatenation operator for ropes.
|
||||
result = rope(a) & b
|
||||
|
||||
proc ropeConcat*(a: varargs[PRope]): PRope =
|
||||
# not overloaded version of concat to speed-up `rfmt` a little bit
|
||||
for i in countup(0, high(a)): result = con(result, a[i])
|
||||
proc `&`*(a: openArray[Rope]): Rope =
|
||||
## the concatenation operator for an openarray of ropes.
|
||||
for i in countup(0, high(a)): result = result & a[i]
|
||||
|
||||
proc toRope(i: BiggestInt): PRope =
|
||||
inc gCacheIntTries
|
||||
result = toRope($i)
|
||||
proc add*(a: var Rope, b: Rope) =
|
||||
## adds `b` to the rope `a`.
|
||||
a = a & b
|
||||
|
||||
proc app(a: var PRope, b: PRope) = a = con(a, b)
|
||||
proc app(a: var PRope, b: string) = a = con(a, b)
|
||||
proc prepend(a: var PRope, b: PRope) = a = con(b, a)
|
||||
proc add*(a: var Rope, b: string) =
|
||||
## adds `b` to the rope `a`.
|
||||
a = a & b
|
||||
|
||||
proc writeRope*(f: File, c: PRope) =
|
||||
var stack = @[c]
|
||||
while len(stack) > 0:
|
||||
var it = pop(stack)
|
||||
while it.data == nil:
|
||||
add(stack, it.right)
|
||||
it = it.left
|
||||
assert(it != nil)
|
||||
assert(it.data != nil)
|
||||
write(f, it.data)
|
||||
iterator leaves*(r: Rope): string =
|
||||
## iterates over any leaf string in the rope `r`.
|
||||
if r != nil:
|
||||
var stack = @[r]
|
||||
while stack.len > 0:
|
||||
var it = stack.pop
|
||||
while isNil(it.data):
|
||||
stack.add(it.right)
|
||||
it = it.left
|
||||
assert(it != nil)
|
||||
assert(it.data != nil)
|
||||
yield it.data
|
||||
|
||||
proc writeRope*(head: PRope, filename: string, useWarning = false) =
|
||||
iterator items*(r: Rope): char =
|
||||
## iterates over any character in the rope `r`.
|
||||
for s in leaves(r):
|
||||
for c in items(s): yield c
|
||||
|
||||
proc writeRope*(f: File, r: Rope) =
|
||||
## writes a rope to a file.
|
||||
for s in leaves(r): write(f, s)
|
||||
|
||||
proc writeRope*(head: Rope, filename: string, useWarning = false) =
|
||||
var f: File
|
||||
if open(f, filename, fmWrite):
|
||||
if head != nil: writeRope(f, head)
|
||||
|
|
@ -235,42 +212,69 @@ proc writeRope*(head: PRope, filename: string, useWarning = false) =
|
|||
else:
|
||||
errorHandler(rCannotOpenFile, filename, useWarning)
|
||||
|
||||
proc `$`*(r: Rope): string =
|
||||
## converts a rope back to a string.
|
||||
result = newString(r.len)
|
||||
setLen(result, 0)
|
||||
for s in leaves(r): add(result, s)
|
||||
|
||||
proc ropeConcat*(a: varargs[Rope]): Rope =
|
||||
# not overloaded version of concat to speed-up `rfmt` a little bit
|
||||
for i in countup(0, high(a)): result = result & a[i]
|
||||
|
||||
proc prepend*(a: var Rope, b: Rope) = a = b & a
|
||||
proc prepend*(a: var Rope, b: string) = a = b & a
|
||||
|
||||
var
|
||||
rnl* = tnl.newRope
|
||||
softRnl* = tnl.newRope
|
||||
|
||||
proc ropef(frmt: TFormatStr, args: varargs[PRope]): PRope =
|
||||
proc `%`*(frmt: FormatStr, args: openArray[Rope]): Rope =
|
||||
var i = 0
|
||||
var length = len(frmt)
|
||||
result = nil
|
||||
var num = 0
|
||||
while i <= length - 1:
|
||||
while i < length:
|
||||
if frmt[i] == '$':
|
||||
inc(i) # skip '$'
|
||||
case frmt[i]
|
||||
of '$':
|
||||
app(result, "$")
|
||||
add(result, "$")
|
||||
inc(i)
|
||||
of '#':
|
||||
inc(i)
|
||||
app(result, args[num])
|
||||
add(result, args[num])
|
||||
inc(num)
|
||||
of '0'..'9':
|
||||
var j = 0
|
||||
while true:
|
||||
j = (j * 10) + ord(frmt[i]) - ord('0')
|
||||
j = j * 10 + ord(frmt[i]) - ord('0')
|
||||
inc(i)
|
||||
if (i > length + 0 - 1) or not (frmt[i] in {'0'..'9'}): break
|
||||
if frmt[i] notin {'0'..'9'}: break
|
||||
num = j
|
||||
if j > high(args) + 1:
|
||||
errorHandler(rInvalidFormatStr, $(j))
|
||||
else:
|
||||
app(result, args[j - 1])
|
||||
add(result, args[j-1])
|
||||
of '{':
|
||||
inc(i)
|
||||
var j = 0
|
||||
while frmt[i] in {'0'..'9'}:
|
||||
j = j * 10 + ord(frmt[i]) - ord('0')
|
||||
inc(i)
|
||||
num = j
|
||||
if frmt[i] == '}': inc(i)
|
||||
else: errorHandler(rInvalidFormatStr, $(frmt[i]))
|
||||
|
||||
if j > high(args) + 1:
|
||||
errorHandler(rInvalidFormatStr, $(j))
|
||||
else:
|
||||
add(result, args[j-1])
|
||||
of 'n':
|
||||
app(result, softRnl)
|
||||
inc i
|
||||
add(result, softRnl)
|
||||
inc(i)
|
||||
of 'N':
|
||||
app(result, rnl)
|
||||
add(result, rnl)
|
||||
inc(i)
|
||||
else:
|
||||
errorHandler(rInvalidFormatStr, $(frmt[i]))
|
||||
|
|
@ -279,83 +283,67 @@ proc ropef(frmt: TFormatStr, args: varargs[PRope]): PRope =
|
|||
if frmt[i] != '$': inc(i)
|
||||
else: break
|
||||
if i - 1 >= start:
|
||||
app(result, substr(frmt, start, i - 1))
|
||||
add(result, substr(frmt, start, i - 1))
|
||||
assert(ropeInvariant(result))
|
||||
|
||||
proc addf*(c: var Rope, frmt: FormatStr, args: openArray[Rope]) =
|
||||
## shortcut for ``add(c, frmt % args)``.
|
||||
add(c, frmt % args)
|
||||
|
||||
when true:
|
||||
template `~`*(r: string): PRope = r.ropef
|
||||
template `~`*(r: string): Rope = r % []
|
||||
else:
|
||||
{.push stack_trace: off, line_trace: off.}
|
||||
proc `~`*(r: static[string]): PRope =
|
||||
proc `~`*(r: static[string]): Rope =
|
||||
# this is the new optimized "to rope" operator
|
||||
# the mnemonic is that `~` looks a bit like a rope :)
|
||||
var r {.global.} = r.ropef
|
||||
var r {.global.} = r % []
|
||||
return r
|
||||
{.pop.}
|
||||
|
||||
proc appf(c: var PRope, frmt: TFormatStr, args: varargs[PRope]) =
|
||||
app(c, ropef(frmt, args))
|
||||
|
||||
const
|
||||
bufSize = 1024 # 1 KB is reasonable
|
||||
|
||||
proc auxRopeEqualsFile(r: PRope, bin: var File, buf: pointer): bool =
|
||||
if r.data != nil:
|
||||
if r.length > bufSize:
|
||||
errorHandler(rTokenTooLong, r.data)
|
||||
return
|
||||
var readBytes = readBuffer(bin, buf, r.length)
|
||||
result = readBytes == r.length and
|
||||
equalMem(buf, addr(r.data[0]), r.length) # BUGFIX
|
||||
else:
|
||||
result = auxRopeEqualsFile(r.left, bin, buf)
|
||||
if result: result = auxRopeEqualsFile(r.right, bin, buf)
|
||||
proc equalsFile*(r: Rope, f: File): bool =
|
||||
## returns true if the contents of the file `f` equal `r`.
|
||||
var
|
||||
buf: array[bufSize, char]
|
||||
bpos = buf.len
|
||||
blen = buf.len
|
||||
|
||||
proc ropeEqualsFile(r: PRope, f: string): bool =
|
||||
var bin: File
|
||||
result = open(bin, f)
|
||||
if not result:
|
||||
return # not equal if file does not exist
|
||||
var buf = alloc(bufSize)
|
||||
result = auxRopeEqualsFile(r, bin, buf)
|
||||
for s in leaves(r):
|
||||
var spos = 0
|
||||
let slen = s.len
|
||||
while spos < slen:
|
||||
if bpos == blen:
|
||||
# Read more data
|
||||
bpos = 0
|
||||
blen = readBuffer(f, addr(buf[0]), buf.len)
|
||||
if blen == 0: # no more data in file
|
||||
result = false
|
||||
return
|
||||
let n = min(blen - bpos, slen - spos)
|
||||
# TODO There's gotta be a better way of comparing here...
|
||||
if not equalMem(addr(buf[bpos]), cast[pointer](cast[int](cstring(s))+spos), n):
|
||||
result = false
|
||||
return
|
||||
spos += n
|
||||
bpos += n
|
||||
|
||||
result = readBuffer(f, addr(buf[0]), 1) == 0 # check that we've read all
|
||||
|
||||
proc equalsFile*(r: Rope, filename: string): bool =
|
||||
## returns true if the contents of the file `f` equal `r`. If `f` does not
|
||||
## exist, false is returned.
|
||||
var f: File
|
||||
result = open(f, filename)
|
||||
if result:
|
||||
result = readBuffer(bin, buf, bufSize) == 0 # really at the end of file?
|
||||
dealloc(buf)
|
||||
close(bin)
|
||||
result = equalsFile(r, f)
|
||||
close(f)
|
||||
|
||||
proc crcFromRopeAux(r: PRope, startVal: TCrc32): TCrc32 =
|
||||
if r.data != nil:
|
||||
result = startVal
|
||||
for i in countup(0, len(r.data) - 1):
|
||||
result = updateCrc32(r.data[i], result)
|
||||
else:
|
||||
result = crcFromRopeAux(r.left, startVal)
|
||||
result = crcFromRopeAux(r.right, result)
|
||||
|
||||
proc newCrcFromRopeAux(r: PRope, startVal: TCrc32): TCrc32 =
|
||||
# XXX profiling shows this is actually expensive
|
||||
var stack: TRopeSeq = @[r]
|
||||
result = startVal
|
||||
while len(stack) > 0:
|
||||
var it = pop(stack)
|
||||
while it.data == nil:
|
||||
add(stack, it.right)
|
||||
it = it.left
|
||||
assert(it.data != nil)
|
||||
var i = 0
|
||||
var L = len(it.data)
|
||||
while i < L:
|
||||
result = updateCrc32(it.data[i], result)
|
||||
inc(i)
|
||||
|
||||
proc crcFromRope(r: PRope): TCrc32 =
|
||||
result = newCrcFromRopeAux(r, InitCrc32)
|
||||
|
||||
proc writeRopeIfNotEqual(r: PRope, filename: string): bool =
|
||||
proc writeRopeIfNotEqual*(r: Rope, filename: string): bool =
|
||||
# returns true if overwritten
|
||||
var c: TCrc32
|
||||
c = crcFromFile(filename)
|
||||
if c != crcFromRope(r):
|
||||
if not equalsFile(r, filename):
|
||||
writeRope(r, filename)
|
||||
result = true
|
||||
else:
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@ import
|
|||
procfind, lookups, rodread, pragmas, passes, semdata, semtypinst, sigmatch,
|
||||
intsets, transf, vmdef, vm, idgen, aliases, cgmeth, lambdalifting,
|
||||
evaltempl, patterns, parampatterns, sempass2, nimfix.pretty, semmacrosanity,
|
||||
semparallel, lowerings
|
||||
semparallel, lowerings, plugins, plugins.active
|
||||
|
||||
when defined(nimfix):
|
||||
import nimfix.prettybase
|
||||
|
|
@ -47,6 +47,24 @@ proc finishMethod(c: PContext, s: PSym)
|
|||
|
||||
proc indexTypesMatch(c: PContext, f, a: PType, arg: PNode): PNode
|
||||
|
||||
template semIdeForTemplateOrGenericCheck(n, requiresCheck) =
|
||||
# we check quickly if the node is where the cursor is
|
||||
when defined(nimsuggest):
|
||||
if n.info.fileIndex == gTrackPos.fileIndex and n.info.line == gTrackPos.line:
|
||||
requiresCheck = true
|
||||
|
||||
template semIdeForTemplateOrGeneric(c: PContext; n: PNode;
|
||||
requiresCheck: bool) =
|
||||
# use only for idetools support; this is pretty slow so generics and
|
||||
# templates perform some quick check whether the cursor is actually in
|
||||
# the generic or template.
|
||||
when defined(nimsuggest):
|
||||
assert gCmd == cmdIdeTools
|
||||
if requiresCheck:
|
||||
if optIdeDebug in gGlobalOptions:
|
||||
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:
|
||||
localError(n.info, errGenerated, msgKindToString(errTypeMismatch) &
|
||||
|
|
@ -71,21 +89,16 @@ proc fitNode(c: PContext, formal: PType, arg: PNode): PNode =
|
|||
let x = result.skipConv
|
||||
if x.kind == nkPar and formal.kind != tyExpr:
|
||||
changeType(x, formal, check=true)
|
||||
else:
|
||||
result = skipHiddenSubConv(result)
|
||||
#result.typ = takeType(formal, arg.typ)
|
||||
#echo arg.info, " picked ", result.typ.typeToString
|
||||
|
||||
proc inferWithMetatype(c: PContext, formal: PType,
|
||||
arg: PNode, coerceDistincts = false): PNode
|
||||
|
||||
var commonTypeBegin = PType(kind: tyExpr)
|
||||
|
||||
proc isEmptyContainer(t: PType): bool =
|
||||
case t.kind
|
||||
of tyExpr, tyNil: result = true
|
||||
of tyArray, tyArrayConstr: result = t.sons[1].kind == tyEmpty
|
||||
of tySet, tySequence, tyOpenArray, tyVarargs:
|
||||
result = t.sons[0].kind == tyEmpty
|
||||
of tyGenericInst: result = isEmptyContainer(t.lastSon)
|
||||
else: result = false
|
||||
|
||||
proc commonType*(x, y: PType): PType =
|
||||
# new type relation that is used for array constructors,
|
||||
# if expressions, etc.:
|
||||
|
|
@ -112,9 +125,11 @@ proc commonType*(x, y: PType): PType =
|
|||
elif a.kind == tyTuple and b.kind == tyTuple and a.len == b.len:
|
||||
var nt: PType
|
||||
for i in 0.. <a.len:
|
||||
if isEmptyContainer(a.sons[i]) and not isEmptyContainer(b.sons[i]):
|
||||
let aEmpty = isEmptyContainer(a.sons[i])
|
||||
let bEmpty = isEmptyContainer(b.sons[i])
|
||||
if aEmpty != bEmpty:
|
||||
if nt.isNil: nt = copyType(a, a.owner, false)
|
||||
nt.sons[i] = b.sons[i]
|
||||
nt.sons[i] = if aEmpty: b.sons[i] else: a.sons[i]
|
||||
if not nt.isNil: result = nt
|
||||
#elif b.sons[idx].kind == tyEmpty: return x
|
||||
elif a.kind == tyRange and b.kind == tyRange:
|
||||
|
|
@ -175,9 +190,15 @@ proc semIdentWithPragma(c: PContext, kind: TSymKind, n: PNode,
|
|||
allowed: TSymFlags): PSym
|
||||
proc semStmtScope(c: PContext, n: PNode): PNode
|
||||
|
||||
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) &
|
||||
"' in this context: '" & typeToString(typ) & "'")
|
||||
|
||||
proc paramsTypeCheck(c: PContext, typ: PType) {.inline.} =
|
||||
if not typeAllowed(typ, skConst):
|
||||
localError(typ.n.info, errXisNoType, typeToString(typ))
|
||||
typeAllowedCheck(typ.n.info, typ, skConst)
|
||||
|
||||
proc expectMacroOrTemplateCall(c: PContext, n: PNode): PSym
|
||||
proc semDirectOp(c: PContext, n: PNode, flags: TExprFlags): PNode
|
||||
|
|
@ -270,6 +291,7 @@ proc semConstExpr(c: PContext, n: PNode): PNode =
|
|||
return n
|
||||
result = getConstExpr(c.module, e)
|
||||
if result == nil:
|
||||
#if e.kind == nkEmpty: globalError(n.info, errConstExprExpected)
|
||||
result = evalConstExpr(c.module, e)
|
||||
if result == nil or result.kind == nkEmpty:
|
||||
if e.info != n.info:
|
||||
|
|
@ -353,13 +375,7 @@ proc semConstBoolExpr(c: PContext, n: PNode): PNode =
|
|||
localError(n.info, errConstExprExpected)
|
||||
result = nn
|
||||
|
||||
type
|
||||
TSemGenericFlag = enum
|
||||
withinBind, withinTypeDesc, withinMixin
|
||||
TSemGenericFlags = set[TSemGenericFlag]
|
||||
|
||||
proc semGenericStmt(c: PContext, n: PNode, flags: TSemGenericFlags,
|
||||
ctx: var IntSet): PNode
|
||||
proc semGenericStmt(c: PContext, n: PNode): PNode
|
||||
|
||||
include semtypes, semtempl, semgnrc, semstmts, semexprs
|
||||
|
||||
|
|
@ -386,7 +402,7 @@ proc myOpen(module: PSym): PPassContext =
|
|||
c.semInferredLambda = semInferredLambda
|
||||
c.semGenerateInstance = generateInstance
|
||||
c.semTypeNode = semTypeNode
|
||||
c.instDeepCopy = sigmatch.instDeepCopy
|
||||
c.instTypeBoundOp = sigmatch.instTypeBoundOp
|
||||
|
||||
pushProcCon(c, module)
|
||||
pushOwner(c.module)
|
||||
|
|
|
|||
|
|
@ -1,13 +1,14 @@
|
|||
#
|
||||
#
|
||||
# The Nim Compiler
|
||||
# (c) Copyright 2014 Andreas Rumpf
|
||||
# (c) Copyright 2015 Andreas Rumpf
|
||||
#
|
||||
# See the file "copying.txt", included in this
|
||||
# distribution, for details about the copyright.
|
||||
#
|
||||
|
||||
## This module implements lifting for assignments and ``deepCopy``.
|
||||
## This module implements lifting for assignments. Later versions of this code
|
||||
## will be able to also lift ``=deepCopy`` and ``=destroy``.
|
||||
|
||||
# included from sem.nim
|
||||
|
||||
|
|
@ -15,98 +16,75 @@ type
|
|||
TLiftCtx = object
|
||||
c: PContext
|
||||
info: TLineInfo # for construction
|
||||
result: PNode
|
||||
kind: TTypeAttachedOp
|
||||
fn: PSym
|
||||
asgnForType: PType
|
||||
recurse: bool
|
||||
|
||||
type
|
||||
TFieldInstCtx = object # either 'tup[i]' or 'field' is valid
|
||||
tupleType: PType # if != nil we're traversing a tuple
|
||||
tupleIndex: int
|
||||
field: PSym
|
||||
replaceByFieldName: bool
|
||||
proc liftBodyAux(c: var TLiftCtx; t: PType; body, x, y: PNode)
|
||||
proc liftBody(c: PContext; typ: PType; info: TLineInfo): PSym
|
||||
|
||||
proc instFieldLoopBody(c: TFieldInstCtx, n: PNode, forLoop: PNode): PNode =
|
||||
proc at(a, i: PNode, elemType: PType): PNode =
|
||||
result = newNodeI(nkBracketExpr, a.info, 2)
|
||||
result.sons[0] = a
|
||||
result.sons[1] = i
|
||||
result.typ = elemType
|
||||
|
||||
proc liftBodyTup(c: var TLiftCtx; t: PType; body, x, y: PNode) =
|
||||
for i in 0 .. <t.len:
|
||||
let lit = lowerings.newIntLit(i)
|
||||
liftBodyAux(c, t.sons[i], body, x.at(lit, t.sons[i]), y.at(lit, t.sons[i]))
|
||||
|
||||
proc dotField(x: PNode, f: PSym): PNode =
|
||||
result = newNodeI(nkDotExpr, x.info, 2)
|
||||
result.sons[0] = x
|
||||
result.sons[1] = newSymNode(f, x.info)
|
||||
result.typ = f.typ
|
||||
|
||||
proc liftBodyObj(c: var TLiftCtx; n, body, x, y: PNode) =
|
||||
case n.kind
|
||||
of nkEmpty..pred(nkIdent), succ(nkIdent)..nkNilLit: result = n
|
||||
of nkIdent:
|
||||
result = n
|
||||
var L = sonsLen(forLoop)
|
||||
if c.replaceByFieldName:
|
||||
if n.ident.id == forLoop[0].ident.id:
|
||||
let fieldName = if c.tupleType.isNil: c.field.name.s
|
||||
elif c.tupleType.n.isNil: "Field" & $c.tupleIndex
|
||||
else: c.tupleType.n.sons[c.tupleIndex].sym.name.s
|
||||
result = newStrNode(nkStrLit, fieldName)
|
||||
return
|
||||
# other fields:
|
||||
for i in ord(c.replaceByFieldName)..L-3:
|
||||
if n.ident.id == forLoop[i].ident.id:
|
||||
var call = forLoop.sons[L-2]
|
||||
var tupl = call.sons[i+1-ord(c.replaceByFieldName)]
|
||||
if c.field.isNil:
|
||||
result = newNodeI(nkBracketExpr, n.info)
|
||||
result.add(tupl)
|
||||
result.add(newIntNode(nkIntLit, c.tupleIndex))
|
||||
else:
|
||||
result = newNodeI(nkDotExpr, n.info)
|
||||
result.add(tupl)
|
||||
result.add(newSymNode(c.field, n.info))
|
||||
break
|
||||
else:
|
||||
if n.kind == nkContinueStmt:
|
||||
localError(n.info, errGenerated,
|
||||
"'continue' not supported in a 'fields' loop")
|
||||
result = copyNode(n)
|
||||
newSons(result, sonsLen(n))
|
||||
for i in countup(0, sonsLen(n)-1):
|
||||
result.sons[i] = instFieldLoopBody(c, n.sons[i], forLoop)
|
||||
|
||||
proc liftBodyObj(c: TLiftCtx; typ, x, y: PNode) =
|
||||
case typ.kind
|
||||
of nkSym:
|
||||
var fc: TFieldInstCtx # either 'tup[i]' or 'field' is valid
|
||||
fc.field = typ.sym
|
||||
fc.replaceByFieldName = c.m == mFieldPairs
|
||||
openScope(c.c)
|
||||
inc c.c.inUnrolledContext
|
||||
let body = instFieldLoopBody(fc, lastSon(forLoop), forLoop)
|
||||
father.add(semStmt(c.c, body))
|
||||
dec c.c.inUnrolledContext
|
||||
closeScope(c.c)
|
||||
let f = n.sym
|
||||
liftBodyAux(c, f.typ, body, x.dotField(f), y.dotField(f))
|
||||
of nkNilLit: discard
|
||||
of nkRecCase:
|
||||
let L = forLoop.len
|
||||
let call = forLoop.sons[L-2]
|
||||
if call.len > 2:
|
||||
localError(forLoop.info, errGenerated,
|
||||
"parallel 'fields' iterator does not work for 'case' objects")
|
||||
return
|
||||
# iterate over the selector:
|
||||
asgnForObjectFields(c, typ[0], forLoop, father)
|
||||
# copy the selector:
|
||||
liftBodyObj(c, n[0], body, x, y)
|
||||
# we need to generate a case statement:
|
||||
var caseStmt = newNodeI(nkCaseStmt, c.info)
|
||||
# XXX generate 'if' that checks same branches
|
||||
# generate selector:
|
||||
var access = newNodeI(nkDotExpr, forLoop.info, 2)
|
||||
access.sons[0] = call.sons[1]
|
||||
access.sons[1] = newSymNode(typ.sons[0].sym, forLoop.info)
|
||||
caseStmt.add(semExprWithType(c.c, access))
|
||||
var access = dotField(x, n[0].sym)
|
||||
caseStmt.add(access)
|
||||
# copy the branches over, but replace the fields with the for loop body:
|
||||
for i in 1 .. <typ.len:
|
||||
var branch = copyTree(typ[i])
|
||||
for i in 1 .. <n.len:
|
||||
var branch = copyTree(n[i])
|
||||
let L = branch.len
|
||||
branch.sons[L-1] = newNodeI(nkStmtList, forLoop.info)
|
||||
semForObjectFields(c, typ[i].lastSon, forLoop, branch[L-1])
|
||||
caseStmt.add(branch)
|
||||
father.add(caseStmt)
|
||||
of nkRecList:
|
||||
for t in items(typ): liftBodyObj(c, t, x, y)
|
||||
else:
|
||||
illFormedAst(typ)
|
||||
branch.sons[L-1] = newNodeI(nkStmtList, c.info)
|
||||
|
||||
proc newAsgnCall(op: PSym; x, y: PNode): PNode =
|
||||
liftBodyObj(c, n[i].lastSon, branch.sons[L-1], x, y)
|
||||
caseStmt.add(branch)
|
||||
body.add(caseStmt)
|
||||
localError(c.info, "cannot lift assignment operator to 'case' object")
|
||||
of nkRecList:
|
||||
for t in items(n): liftBodyObj(c, t, body, x, y)
|
||||
else:
|
||||
illFormedAstLocal(n)
|
||||
|
||||
proc genAddr(c: PContext; x: PNode): PNode =
|
||||
if x.kind == nkHiddenDeref:
|
||||
checkSonsLen(x, 1)
|
||||
result = x.sons[0]
|
||||
else:
|
||||
result = newNodeIT(nkHiddenAddr, x.info, makeVarType(c, x.typ))
|
||||
addSon(result, x)
|
||||
|
||||
proc newAsgnCall(c: PContext; op: PSym; x, y: PNode): PNode =
|
||||
if sfError in op.flags:
|
||||
localError(x.info, errWrongSymbolX, op.name.s)
|
||||
result = newNodeI(nkCall, x.info)
|
||||
result.add(newSymNode(op))
|
||||
result.add x
|
||||
result.add newSymNode(op)
|
||||
result.add genAddr(c, x)
|
||||
result.add y
|
||||
|
||||
proc newAsgnStmt(le, ri: PNode): PNode =
|
||||
|
|
@ -122,76 +100,185 @@ proc newDestructorCall(op: PSym; x: PNode): PNode =
|
|||
proc newDeepCopyCall(op: PSym; x, y: PNode): PNode =
|
||||
result = newAsgnStmt(x, newDestructorCall(op, y))
|
||||
|
||||
proc considerOverloadedOp(c: TLiftCtx; t: PType; x, y: PNode): bool =
|
||||
let op = t.attachedOps[c.kind]
|
||||
if op != nil:
|
||||
markUsed(c.info, op)
|
||||
styleCheckUse(c.info, op)
|
||||
case c.kind
|
||||
of attachedDestructor:
|
||||
c.result.add newDestructorCall(op, x)
|
||||
of attachedAsgn:
|
||||
c.result.add newAsgnCall(op, x, y)
|
||||
of attachedDeepCopy:
|
||||
c.result.add newDeepCopyCall(op, x, y)
|
||||
result = true
|
||||
proc considerOverloadedOp(c: var TLiftCtx; t: PType; body, x, y: PNode): bool =
|
||||
case c.kind
|
||||
of attachedDestructor:
|
||||
let op = t.destructor
|
||||
if op != nil:
|
||||
markUsed(c.info, op)
|
||||
styleCheckUse(c.info, op)
|
||||
body.add newDestructorCall(op, x)
|
||||
result = true
|
||||
of attachedAsgn:
|
||||
if tfHasAsgn in t.flags:
|
||||
var op: PSym
|
||||
if sameType(t, c.asgnForType):
|
||||
# generate recursive call:
|
||||
if c.recurse:
|
||||
op = c.fn
|
||||
else:
|
||||
c.recurse = true
|
||||
return false
|
||||
else:
|
||||
op = t.assignment
|
||||
if op == nil:
|
||||
op = liftBody(c.c, t, c.info)
|
||||
markUsed(c.info, op)
|
||||
styleCheckUse(c.info, op)
|
||||
body.add newAsgnCall(c.c, op, x, y)
|
||||
result = true
|
||||
of attachedDeepCopy:
|
||||
let op = t.deepCopy
|
||||
if op != nil:
|
||||
markUsed(c.info, op)
|
||||
styleCheckUse(c.info, op)
|
||||
body.add newDeepCopyCall(op, x, y)
|
||||
result = true
|
||||
|
||||
proc defaultOp(c: TLiftCtx; t: PType; x, y: PNode) =
|
||||
proc defaultOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
|
||||
if c.kind != attachedDestructor:
|
||||
c.result.add newAsgnStmt(x, y)
|
||||
body.add newAsgnStmt(x, y)
|
||||
|
||||
proc liftBodyAux(c: TLiftCtx; t: PType; x, y: PNode) =
|
||||
const hasAttachedOp: array[TTypeAttachedOp, TTypeIter] = [
|
||||
(proc (t: PType, closure: PObject): bool =
|
||||
t.attachedOp[attachedDestructor] != nil),
|
||||
(proc (t: PType, closure: PObject): bool =
|
||||
t.attachedOp[attachedAsgn] != nil),
|
||||
(proc (t: PType, closure: PObject): bool =
|
||||
t.attachedOp[attachedDeepCopy] != nil)]
|
||||
proc addVar(father, v, value: PNode) =
|
||||
var vpart = newNodeI(nkIdentDefs, v.info, 3)
|
||||
vpart.sons[0] = v
|
||||
vpart.sons[1] = ast.emptyNode
|
||||
vpart.sons[2] = value
|
||||
addSon(father, vpart)
|
||||
|
||||
proc declareCounter(c: var TLiftCtx; body: PNode; first: BiggestInt): PNode =
|
||||
var temp = newSym(skTemp, getIdent(lowerings.genPrefix), c.fn, c.info)
|
||||
temp.typ = getSysType(tyInt)
|
||||
incl(temp.flags, sfFromGeneric)
|
||||
|
||||
var v = newNodeI(nkVarSection, c.info)
|
||||
result = newSymNode(temp)
|
||||
v.addVar(result, lowerings.newIntLit(first))
|
||||
body.add v
|
||||
|
||||
proc genBuiltin(magic: TMagic; name: string; i: PNode): PNode =
|
||||
result = newNodeI(nkCall, i.info)
|
||||
result.add createMagic(name, magic).newSymNode
|
||||
result.add i
|
||||
|
||||
proc genWhileLoop(c: var TLiftCtx; i, dest: PNode): PNode =
|
||||
result = newNodeI(nkWhileStmt, c.info, 2)
|
||||
let cmp = genBuiltin(mLeI, "<=", i)
|
||||
cmp.add genHigh(dest)
|
||||
cmp.typ = getSysType(tyBool)
|
||||
result.sons[0] = cmp
|
||||
result.sons[1] = newNodeI(nkStmtList, c.info)
|
||||
|
||||
proc addIncStmt(body, i: PNode) =
|
||||
let incCall = genBuiltin(mInc, "inc", i)
|
||||
incCall.add lowerings.newIntLit(1)
|
||||
body.add incCall
|
||||
|
||||
proc newSeqCall(c: PContext; x, y: PNode): PNode =
|
||||
# don't call genAddr(c, x) here:
|
||||
result = genBuiltin(mNewSeq, "newSeq", x)
|
||||
let lenCall = genBuiltin(mLengthSeq, "len", y)
|
||||
lenCall.typ = getSysType(tyInt)
|
||||
result.add lenCall
|
||||
|
||||
proc liftBodyAux(c: var TLiftCtx; t: PType; body, x, y: PNode) =
|
||||
case t.kind
|
||||
of tyNone, tyEmpty: discard
|
||||
of tyPointer, tySet, tyBool, tyChar, tyEnum, tyInt..tyUInt64, tyCString:
|
||||
defaultOp(c, t, x, y)
|
||||
of tyPtr, tyString:
|
||||
if not considerOverloadedOp(c, t, x, y):
|
||||
defaultOp(c, t, x, y)
|
||||
of tyPointer, tySet, tyBool, tyChar, tyEnum, tyInt..tyUInt64, tyCString,
|
||||
tyPtr, tyString, tyRef:
|
||||
defaultOp(c, t, body, x, y)
|
||||
of tyArrayConstr, tyArray, tySequence:
|
||||
if iterOverType(lastSon(t), hasAttachedOp[c.kind], nil):
|
||||
# generate loop and call the attached Op:
|
||||
|
||||
if tfHasAsgn in t.flags:
|
||||
if t.kind == tySequence:
|
||||
# XXX add 'nil' handling here
|
||||
body.add newSeqCall(c.c, x, y)
|
||||
let i = declareCounter(c, body, firstOrd(t))
|
||||
let whileLoop = genWhileLoop(c, i, x)
|
||||
let elemType = t.lastSon
|
||||
liftBodyAux(c, elemType, whileLoop.sons[1], x.at(i, elemType),
|
||||
y.at(i, elemType))
|
||||
addIncStmt(whileLoop.sons[1], i)
|
||||
body.add whileLoop
|
||||
else:
|
||||
defaultOp(c, t, x, y)
|
||||
of tyObject:
|
||||
liftBodyObj(c, t.n, x, y)
|
||||
defaultOp(c, t, body, x, y)
|
||||
of tyObject, tyDistinct:
|
||||
if not considerOverloadedOp(c, t, body, x, y):
|
||||
if t.sons[0] != nil: liftBodyAux(c, t.sons[0], body, x, y)
|
||||
if t.kind == tyObject: liftBodyObj(c, t.n, body, x, y)
|
||||
of tyTuple:
|
||||
liftBodyTup(c, t, x, y)
|
||||
of tyRef:
|
||||
# we MUST not check for acyclic here as a DAG might still share nodes:
|
||||
|
||||
liftBodyTup(c, t, body, x, y)
|
||||
of tyProc:
|
||||
if t.callConv != ccClosure or c.kind != attachedDeepCopy:
|
||||
defaultOp(c, t, x, y)
|
||||
defaultOp(c, t, body, x, y)
|
||||
else:
|
||||
# a big problem is that we don't know the enviroment's type here, so we
|
||||
# have to go through some indirection; we delegate this to the codegen:
|
||||
call = newNodeI(nkCall, n.info, 2)
|
||||
let call = newNodeI(nkCall, c.info, 2)
|
||||
call.typ = t
|
||||
call.sons[0] = newSymNode(createMagic("deepCopy", mDeepCopy))
|
||||
call.sons[1] = y
|
||||
c.result.add newAsgnStmt(x, call)
|
||||
body.add newAsgnStmt(x, call)
|
||||
of tyVarargs, tyOpenArray:
|
||||
localError(c.info, errGenerated, "cannot copy openArray")
|
||||
of tyFromExpr, tyIter, tyProxy, tyBuiltInTypeClass, tyUserTypeClass,
|
||||
tyUserTypeClassInst, tyCompositeTypeClass, tyAnd, tyOr, tyNot, tyAnything,
|
||||
tyMutable, tyGenericParam, tyGenericBody, tyNil, tyExpr, tyStmt,
|
||||
tyTypeDesc, tyGenericInvokation, tyBigNum, tyConst, tyForward:
|
||||
tyTypeDesc, tyGenericInvocation, tyBigNum, tyConst, tyForward:
|
||||
internalError(c.info, "assignment requested for type: " & typeToString(t))
|
||||
of tyDistinct, tyOrdinal, tyRange,
|
||||
of tyOrdinal, tyRange,
|
||||
tyGenericInst, tyFieldAccessor, tyStatic, tyVar:
|
||||
liftBodyAux(c, lastSon(t))
|
||||
liftBodyAux(c, lastSon(t), body, x, y)
|
||||
|
||||
proc liftBody(c: PContext; typ: PType; info: TLineInfo): PNode =
|
||||
proc newProcType(info: TLineInfo; owner: PSym): PType =
|
||||
result = newType(tyProc, owner)
|
||||
result.n = newNodeI(nkFormalParams, info)
|
||||
rawAddSon(result, nil) # return type
|
||||
# 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))
|
||||
|
||||
proc addParam(procType: PType; param: PSym) =
|
||||
param.position = procType.len-1
|
||||
addSon(procType.n, newSymNode(param))
|
||||
rawAddSon(procType, param.typ)
|
||||
|
||||
proc liftBody(c: PContext; typ: PType; info: TLineInfo): PSym =
|
||||
var a: TLiftCtx
|
||||
a.info = info
|
||||
a.result = newNodeI(nkStmtList, info)
|
||||
liftBodyAux(a, typ)
|
||||
let body = newNodeI(nkStmtList, info)
|
||||
result = newSym(skProc, getIdent":lifted=", typ.owner, info)
|
||||
a.fn = result
|
||||
a.asgnForType = typ
|
||||
|
||||
let dest = newSym(skParam, getIdent"dest", result, info)
|
||||
let src = newSym(skParam, getIdent"src", result, info)
|
||||
dest.typ = makeVarType(c, typ)
|
||||
src.typ = typ
|
||||
|
||||
result.typ = newProcType(info, typ.owner)
|
||||
result.typ.addParam dest
|
||||
result.typ.addParam src
|
||||
|
||||
liftBodyAux(a, typ, body, newSymNode(dest).newDeref, newSymNode(src))
|
||||
|
||||
var n = newNodeI(nkProcDef, info, bodyPos+1)
|
||||
for i in 0 .. < n.len: n.sons[i] = emptyNode
|
||||
n.sons[namePos] = newSymNode(result)
|
||||
n.sons[paramsPos] = result.typ.n
|
||||
n.sons[bodyPos] = body
|
||||
result.ast = n
|
||||
|
||||
# register late as recursion is handled differently
|
||||
typ.assignment = result
|
||||
#echo "Produced this ", n
|
||||
|
||||
proc getAsgnOrLiftBody(c: PContext; typ: PType; info: TLineInfo): PSym =
|
||||
let t = typ.skipTypes({tyGenericInst, tyVar})
|
||||
result = t.assignment
|
||||
if result.isNil:
|
||||
result = liftBody(c, t, info)
|
||||
|
||||
proc overloadedAsgn(c: PContext; dest, src: PNode): PNode =
|
||||
let a = getAsgnOrLiftBody(c, dest.typ, dest.info)
|
||||
result = newAsgnCall(c, a, dest, src)
|
||||
|
|
|
|||
|
|
@ -41,38 +41,55 @@ 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
|
||||
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
|
||||
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,
|
||||
|
|
@ -81,8 +98,9 @@ proc notFoundError*(c: PContext, n: PNode, errors: CandidateErrors) =
|
|||
if c.inCompilesContext > 0:
|
||||
# fail fast:
|
||||
globalError(n.info, errTypeMismatch, "")
|
||||
if errors.len == 0:
|
||||
if errors.isNil or errors.len == 0:
|
||||
localError(n.info, errExprXCannotBeCalled, n[0].renderTree)
|
||||
return
|
||||
|
||||
# to avoid confusing errors like:
|
||||
# got (SslPtr, SocketHandle)
|
||||
|
|
@ -123,7 +141,8 @@ proc gatherUsedSyms(c: PContext, usedSyms: var seq[PNode]) =
|
|||
for s in scope.usingSyms: usedSyms.safeAdd(s)
|
||||
|
||||
proc resolveOverloads(c: PContext, n, orig: PNode,
|
||||
filter: TSymKinds): TCandidate =
|
||||
filter: TSymKinds;
|
||||
errors: var CandidateErrors): TCandidate =
|
||||
var initialBinding: PNode
|
||||
var alt: TCandidate
|
||||
var f = n.sons[0]
|
||||
|
|
@ -134,7 +153,6 @@ proc resolveOverloads(c: PContext, n, orig: PNode,
|
|||
else:
|
||||
initialBinding = nil
|
||||
|
||||
var errors: CandidateErrors
|
||||
var usedSyms: seq[PNode]
|
||||
|
||||
template pickBest(headSymbol: expr) =
|
||||
|
|
@ -183,7 +201,8 @@ proc resolveOverloads(c: PContext, n, orig: PNode,
|
|||
|
||||
elif nfDotSetter in n.flags:
|
||||
internalAssert f.kind == nkIdent and n.sonsLen == 3
|
||||
let calleeName = newStrNode(nkStrLit, f.ident.s[0.. -2]).withInfo(n.info)
|
||||
let calleeName = newStrNode(nkStrLit,
|
||||
f.ident.s[0..f.ident.s.len-2]).withInfo(n.info)
|
||||
let callOp = newIdentNode(getIdent".=", n.info)
|
||||
n.sons[0..1] = [callOp, n[1], calleeName]
|
||||
orig.sons[0..1] = [callOp, orig[1], calleeName]
|
||||
|
|
@ -204,7 +223,7 @@ proc resolveOverloads(c: PContext, n, orig: PNode,
|
|||
|
||||
errors = @[]
|
||||
pickBest(f)
|
||||
notFoundError(c, n, errors)
|
||||
#notFoundError(c, n, errors)
|
||||
|
||||
return
|
||||
|
||||
|
|
@ -260,7 +279,7 @@ proc inferWithMetatype(c: PContext, formal: PType,
|
|||
instGenericConvertersArg(c, result, m)
|
||||
if result != nil:
|
||||
# This almost exactly replicates the steps taken by the compiler during
|
||||
# param matching. It performs an embarassing ammount of back-and-forth
|
||||
# param matching. It performs an embarrassing amount of back-and-forth
|
||||
# type jugling, but it's the price to pay for consistency and correctness
|
||||
result.typ = generateTypeInstance(c, m.bindings, arg.info,
|
||||
formal.skipTypes({tyCompositeTypeClass}))
|
||||
|
|
@ -277,25 +296,45 @@ proc semResolvedCall(c: PContext, n: PNode, x: TCandidate): PNode =
|
|||
styleCheckUse(n.sons[0].info, finalCallee)
|
||||
if finalCallee.ast == nil:
|
||||
internalError(n.info, "calleeSym.ast is nil") # XXX: remove this check!
|
||||
if x.hasFauxMatch:
|
||||
result = x.call
|
||||
result.sons[0] = newSymNode(finalCallee, result.sons[0].info)
|
||||
if containsGenericType(result.typ) or x.fauxMatch == tyUnknown:
|
||||
result.typ = newTypeS(x.fauxMatch, c)
|
||||
return
|
||||
if finalCallee.ast.sons[genericParamsPos].kind != nkEmpty:
|
||||
# a generic proc!
|
||||
if not x.proxyMatch:
|
||||
finalCallee = generateInstance(c, x.calleeSym, x.bindings, n.info)
|
||||
else:
|
||||
result = x.call
|
||||
result.sons[0] = newSymNode(finalCallee, result.sons[0].info)
|
||||
result.typ = finalCallee.typ.sons[0]
|
||||
if containsGenericType(result.typ): result.typ = errorType(c)
|
||||
return
|
||||
finalCallee = generateInstance(c, x.calleeSym, x.bindings, n.info)
|
||||
result = x.call
|
||||
instGenericConvertersSons(c, result, x)
|
||||
result.sons[0] = newSymNode(finalCallee, result.sons[0].info)
|
||||
result.typ = finalCallee.typ.sons[0]
|
||||
|
||||
proc canDeref(n: PNode): bool {.inline.} =
|
||||
result = n.len >= 2 and (let t = n[1].typ;
|
||||
t != nil and t.skipTypes({tyGenericInst}).kind in {tyPtr, tyRef})
|
||||
|
||||
proc tryDeref(n: PNode): PNode =
|
||||
result = newNodeI(nkHiddenDeref, n.info)
|
||||
result.typ = n.typ.skipTypes(abstractInst).sons[0]
|
||||
result.addSon(n)
|
||||
|
||||
proc semOverloadedCall(c: PContext, n, nOrig: PNode,
|
||||
filter: TSymKinds): PNode =
|
||||
var r = resolveOverloads(c, n, nOrig, filter)
|
||||
var errors: CandidateErrors
|
||||
|
||||
var r = resolveOverloads(c, n, nOrig, filter, errors)
|
||||
if r.state == csMatch: result = semResolvedCall(c, n, r)
|
||||
elif experimentalMode(c) and canDeref(n):
|
||||
# try to deref the first argument and then try overloading resolution again:
|
||||
n.sons[1] = n.sons[1].tryDeref
|
||||
var r = resolveOverloads(c, n, nOrig, filter, errors)
|
||||
if r.state == csMatch: result = semResolvedCall(c, n, r)
|
||||
else:
|
||||
# get rid of the deref again for a better error message:
|
||||
n.sons[1] = n.sons[1].sons[0]
|
||||
notFoundError(c, n, errors)
|
||||
else:
|
||||
notFoundError(c, n, errors)
|
||||
# else: result = errorNode(c, n)
|
||||
|
||||
proc explicitGenericInstError(n: PNode): PNode =
|
||||
|
|
|
|||
|
|
@ -26,7 +26,7 @@ type
|
|||
|
||||
POptionEntry* = ref TOptionEntry
|
||||
PProcCon* = ref TProcCon
|
||||
TProcCon*{.final.} = object # procedure context; also used for top-level
|
||||
TProcCon* = object # procedure context; also used for top-level
|
||||
# statements
|
||||
owner*: PSym # the symbol this context belongs to
|
||||
resultSym*: PSym # the result symbol (if we are in a proc)
|
||||
|
|
@ -35,16 +35,24 @@ type
|
|||
inTryStmt*: int # whether we are in a try statement; works also
|
||||
# in standalone ``except`` and ``finally``
|
||||
next*: PProcCon # used for stacking procedure contexts
|
||||
wasForwarded*: bool # whether the current proc has a separate header
|
||||
bracketExpr*: PNode # current bracket expression (for ^ support)
|
||||
|
||||
TInstantiationPair* = object
|
||||
genericSym*: PSym
|
||||
inst*: PInstantiation
|
||||
|
||||
TExprFlag* = enum
|
||||
efLValue, efWantIterator, efInTypeof, efWantStmt, efDetermineType,
|
||||
efLValue, efWantIterator, efInTypeof,
|
||||
efWantStmt, efAllowStmt, efDetermineType,
|
||||
efAllowDestructor, efWantValue, efOperand, efNoSemCheck
|
||||
TExprFlags* = set[TExprFlag]
|
||||
|
||||
TTypeAttachedOp* = enum
|
||||
attachedAsgn,
|
||||
attachedDeepCopy,
|
||||
attachedDestructor
|
||||
|
||||
PContext* = ref TContext
|
||||
TContext* = object of TPassContext # a context represents a module
|
||||
module*: PSym # the module sym belonging to the context
|
||||
|
|
@ -91,8 +99,8 @@ type
|
|||
lastGenericIdx*: int # used for the generics stack
|
||||
hloLoopDetector*: int # used to prevent endless loops in the HLO
|
||||
inParallelStmt*: int
|
||||
instDeepCopy*: proc (c: PContext; dc: PSym; t: PType;
|
||||
info: TLineInfo): PSym {.nimcall.}
|
||||
instTypeBoundOp*: proc (c: PContext; dc: PSym; t: PType; info: TLineInfo;
|
||||
op: TTypeAttachedOp): PSym {.nimcall.}
|
||||
|
||||
|
||||
proc makeInstPair*(s: PSym, inst: PInstantiation): TInstantiationPair =
|
||||
|
|
@ -110,7 +118,6 @@ proc newOptionEntry*(): POptionEntry
|
|||
proc newLib*(kind: TLibKind): PLib
|
||||
proc addToLib*(lib: PLib, sym: PSym)
|
||||
proc makePtrType*(c: PContext, baseType: PType): PType
|
||||
proc makeVarType*(c: PContext, baseType: PType): PType
|
||||
proc newTypeS*(kind: TTypeKind, c: PContext): PType
|
||||
proc fillTypeS*(dest: PType, kind: TTypeKind, c: PContext)
|
||||
|
||||
|
|
@ -205,9 +212,12 @@ proc makePtrType(c: PContext, baseType: PType): PType =
|
|||
result = newTypeS(tyPtr, c)
|
||||
addSonSkipIntLit(result, baseType.assertNotNil)
|
||||
|
||||
proc makeVarType(c: PContext, baseType: PType): PType =
|
||||
result = newTypeS(tyVar, c)
|
||||
addSonSkipIntLit(result, baseType.assertNotNil)
|
||||
proc makeVarType*(c: PContext, baseType: PType): PType =
|
||||
if baseType.kind == tyVar:
|
||||
result = baseType
|
||||
else:
|
||||
result = newTypeS(tyVar, c)
|
||||
addSonSkipIntLit(result, baseType.assertNotNil)
|
||||
|
||||
proc makeTypeDesc*(c: PContext, typ: PType): PType =
|
||||
result = newTypeS(tyTypeDesc, c)
|
||||
|
|
@ -220,6 +230,7 @@ proc makeTypeSymNode*(c: PContext, typ: PType, info: TLineInfo): PNode =
|
|||
|
||||
proc makeTypeFromExpr*(c: PContext, n: PNode): PType =
|
||||
result = newTypeS(tyFromExpr, c)
|
||||
assert n != nil
|
||||
result.n = n
|
||||
|
||||
proc newTypeWithSons*(c: PContext, kind: TTypeKind,
|
||||
|
|
@ -238,6 +249,7 @@ proc makeAndType*(c: PContext, t1, t2: PType): PType =
|
|||
propagateToOwner(result, t1)
|
||||
propagateToOwner(result, t2)
|
||||
result.flags.incl((t1.flags + t2.flags) * {tfHasStatic})
|
||||
result.flags.incl tfHasMeta
|
||||
|
||||
proc makeOrType*(c: PContext, t1, t2: PType): PType =
|
||||
result = newTypeS(tyOr, c)
|
||||
|
|
@ -245,12 +257,14 @@ proc makeOrType*(c: PContext, t1, t2: PType): PType =
|
|||
propagateToOwner(result, t1)
|
||||
propagateToOwner(result, t2)
|
||||
result.flags.incl((t1.flags + t2.flags) * {tfHasStatic})
|
||||
result.flags.incl tfHasMeta
|
||||
|
||||
proc makeNotType*(c: PContext, t1: PType): PType =
|
||||
result = newTypeS(tyNot, c)
|
||||
result.sons = @[t1]
|
||||
propagateToOwner(result, t1)
|
||||
result.flags.incl(t1.flags * {tfHasStatic})
|
||||
result.flags.incl tfHasMeta
|
||||
|
||||
proc nMinusOne*(n: PNode): PNode =
|
||||
result = newNode(nkCall, n.info, @[
|
||||
|
|
@ -268,7 +282,7 @@ proc makeRangeWithStaticExpr*(c: PContext, n: PNode): PType =
|
|||
|
||||
template rangeHasStaticIf*(t: PType): bool =
|
||||
# this accepts the ranges's node
|
||||
t.n[1].kind == nkStaticExpr
|
||||
t.n != nil and t.n.len > 1 and t.n[1].kind == nkStaticExpr
|
||||
|
||||
template getStaticTypeFromRange*(t: PType): PType =
|
||||
t.n[1][0][1].typ
|
||||
|
|
@ -306,6 +320,9 @@ proc markIndirect*(c: PContext, s: PSym) {.inline.} =
|
|||
proc illFormedAst*(n: PNode) =
|
||||
globalError(n.info, errIllFormedAstX, renderTree(n, {renderNoComments}))
|
||||
|
||||
proc illFormedAstLocal*(n: PNode) =
|
||||
localError(n.info, errIllFormedAstX, renderTree(n, {renderNoComments}))
|
||||
|
||||
proc checkSonsLen*(n: PNode, length: int) =
|
||||
if sonsLen(n) != length: illFormedAst(n)
|
||||
|
||||
|
|
|
|||
|
|
@ -30,7 +30,7 @@ proc instantiateDestructor(c: PContext, typ: PType): PType
|
|||
|
||||
proc doDestructorStuff(c: PContext, s: PSym, n: PNode) =
|
||||
var t = s.typ.sons[1].skipTypes({tyVar})
|
||||
if t.kind == tyGenericInvokation:
|
||||
if t.kind == tyGenericInvocation:
|
||||
for i in 1 .. <t.sonsLen:
|
||||
if t.sons[i].kind != tyGenericParam:
|
||||
localError(n.info, errDestructorNotGenericEnough)
|
||||
|
|
@ -71,11 +71,12 @@ proc destroyCase(c: PContext, n: PNode, holder: PNode): PNode =
|
|||
result.addSon(newNode(nkDotExpr, n.info, @[holder, n.sons[0]]))
|
||||
for i in countup(1, n.len - 1):
|
||||
# of A, B:
|
||||
var caseBranch = newNode(n[i].kind, n[i].info, n[i].sons[0 .. -2])
|
||||
let ni = n[i]
|
||||
var caseBranch = newNode(ni.kind, ni.info, ni.sons[0..ni.len-2])
|
||||
|
||||
let stmt = destroyFieldOrFields(c, n[i].lastSon, holder)
|
||||
let stmt = destroyFieldOrFields(c, ni.lastSon, holder)
|
||||
if stmt == nil:
|
||||
caseBranch.addSon(newNode(nkStmtList, n[i].info, @[]))
|
||||
caseBranch.addSon(newNode(nkStmtList, ni.info, @[]))
|
||||
else:
|
||||
caseBranch.addSon(stmt)
|
||||
nonTrivialFields += stmt.len
|
||||
|
|
@ -209,7 +210,7 @@ proc insertDestructors(c: PContext,
|
|||
|
||||
if j < totalVars - 1:
|
||||
var remainingVars = newNodeI(varSection.kind, info)
|
||||
remainingVars.sons = varSection.sons[(j+1)..(-1)]
|
||||
remainingVars.sons = varSection.sons[(j+1)..varSection.len-1]
|
||||
let (outer, inner) = insertDestructors(c, remainingVars)
|
||||
if outer != nil:
|
||||
tryStmt.addSon(outer)
|
||||
|
|
|
|||
|
|
@ -24,14 +24,14 @@ proc semFieldAccess(c: PContext, n: PNode, flags: TExprFlags = {}): PNode
|
|||
proc semOperand(c: PContext, n: PNode, flags: TExprFlags = {}): PNode =
|
||||
# same as 'semExprWithType' but doesn't check for proc vars
|
||||
result = semExpr(c, n, flags + {efOperand})
|
||||
if result.kind == nkEmpty:
|
||||
if result.kind == nkEmpty and result.typ.isNil:
|
||||
# do not produce another redundant error message:
|
||||
#raiseRecoverableError("")
|
||||
result = errorNode(c, n)
|
||||
if result.typ != nil:
|
||||
# XXX tyGenericInst here?
|
||||
if result.typ.kind == tyVar: result = newDeref(result)
|
||||
elif efWantStmt in flags:
|
||||
elif {efWantStmt, efAllowStmt} * flags != {}:
|
||||
result.typ = newTypeS(tyEmpty, c)
|
||||
else:
|
||||
localError(n.info, errExprXHasNoType,
|
||||
|
|
@ -103,15 +103,31 @@ proc semSym(c: PContext, n: PNode, s: PSym, flags: TExprFlags): PNode =
|
|||
result = newSymNode(s, n.info)
|
||||
of skMacro: result = semMacroExpr(c, n, n, s, flags)
|
||||
of skTemplate: result = semTemplateExpr(c, n, s, flags)
|
||||
of skVar, skLet, skResult, skParam, skForVar:
|
||||
of skParam:
|
||||
markUsed(n.info, s)
|
||||
styleCheckUse(n.info, s)
|
||||
if s.typ.kind == tyStatic and s.typ.n != nil:
|
||||
# XXX see the hack in sigmatch.nim ...
|
||||
return s.typ.n
|
||||
elif sfGenSym in s.flags:
|
||||
if c.p.wasForwarded:
|
||||
# gensym'ed parameters that nevertheless have been forward declared
|
||||
# need a special fixup:
|
||||
let realParam = c.p.owner.typ.n[s.position+1]
|
||||
internalAssert realParam.kind == nkSym and realParam.sym.kind == skParam
|
||||
return newSymNode(c.p.owner.typ.n[s.position+1].sym, n.info)
|
||||
elif c.p.owner.kind == skMacro:
|
||||
# gensym'ed macro parameters need a similar hack (see bug #1944):
|
||||
var u = searchInScopes(c, s.name)
|
||||
internalAssert u != nil and u.kind == skParam and u.owner == s.owner
|
||||
return newSymNode(u, n.info)
|
||||
result = newSymNode(s, n.info)
|
||||
of skVar, skLet, skResult, skForVar:
|
||||
markUsed(n.info, s)
|
||||
styleCheckUse(n.info, s)
|
||||
# if a proc accesses a global variable, it is not side effect free:
|
||||
if sfGlobal in s.flags:
|
||||
incl(c.p.owner.flags, sfSideEffect)
|
||||
elif s.kind == skParam and s.typ.kind == tyStatic and s.typ.n != nil:
|
||||
# XXX see the hack in sigmatch.nim ...
|
||||
return s.typ.n
|
||||
result = newSymNode(s, n.info)
|
||||
# We cannot check for access to outer vars for example because it's still
|
||||
# not sure the symbol really ends up being used:
|
||||
|
|
@ -201,7 +217,7 @@ proc isCastable(dst, src: PType): bool =
|
|||
result = false
|
||||
elif srcSize < 0:
|
||||
result = false
|
||||
elif not typeAllowed(dst, skParam):
|
||||
elif typeAllowed(dst, skParam) != nil:
|
||||
result = false
|
||||
else:
|
||||
result = (dstSize >= srcSize) or
|
||||
|
|
@ -219,7 +235,7 @@ proc maybeLiftType(t: var PType, c: PContext, info: TLineInfo) =
|
|||
# gnrc. params, then it won't be necessary to open a new scope here
|
||||
openScope(c)
|
||||
var lifted = liftParamType(c, skType, newNodeI(nkArgList, info),
|
||||
t, ":anon", info)
|
||||
t, ":anon", info)
|
||||
closeScope(c)
|
||||
if lifted != nil: t = lifted
|
||||
|
||||
|
|
@ -229,7 +245,7 @@ proc semConv(c: PContext, n: PNode): PNode =
|
|||
return n
|
||||
|
||||
result = newNodeI(nkConv, n.info)
|
||||
var targetType = semTypeNode(c, n.sons[0], nil)
|
||||
var targetType = semTypeNode(c, n.sons[0], nil).skipTypes({tyTypeDesc})
|
||||
maybeLiftType(targetType, c, n[0].info)
|
||||
result.addSon copyTree(n.sons[0])
|
||||
var op = semExprWithType(c, n.sons[1])
|
||||
|
|
@ -293,7 +309,7 @@ proc semLowHigh(c: PContext, n: PNode, m: TMagic): PNode =
|
|||
var typ = skipTypes(n.sons[1].typ, abstractVarRange +
|
||||
{tyTypeDesc, tyFieldAccessor})
|
||||
case typ.kind
|
||||
of tySequence, tyString, tyOpenArray, tyVarargs:
|
||||
of tySequence, tyString, tyCString, tyOpenArray, tyVarargs:
|
||||
n.typ = getSysType(tyInt)
|
||||
of tyArrayConstr, tyArray:
|
||||
n.typ = typ.sons[0] # indextype
|
||||
|
|
@ -373,7 +389,7 @@ proc isOpImpl(c: PContext, n: PNode): PNode =
|
|||
maybeLiftType(t2, c, n.info)
|
||||
var m: TCandidate
|
||||
initCandidate(c, m, t2)
|
||||
let match = typeRel(m, t2, t1) != isNone
|
||||
let match = typeRel(m, t2, t1) >= isSubtype # isNone
|
||||
result = newIntNode(nkIntLit, ord(match))
|
||||
|
||||
result.typ = n.typ
|
||||
|
|
@ -431,14 +447,32 @@ proc changeType(n: PNode, newType: PType, check: bool) =
|
|||
of nkPar:
|
||||
let tup = newType.skipTypes({tyGenericInst})
|
||||
if tup.kind != tyTuple:
|
||||
if tup.kind == tyObject: return
|
||||
internalError(n.info, "changeType: no tuple type for constructor")
|
||||
elif sonsLen(n) > 0 and n.sons[0].kind == nkExprColonExpr:
|
||||
# named tuple?
|
||||
for i in countup(0, sonsLen(n) - 1):
|
||||
var m = n.sons[i].sons[0]
|
||||
if m.kind != nkSym:
|
||||
internalError(m.info, "changeType(): invalid tuple constr")
|
||||
return
|
||||
if tup.n != nil:
|
||||
var f = getSymFromList(tup.n, m.sym.name)
|
||||
if f == nil:
|
||||
internalError(m.info, "changeType(): invalid identifier")
|
||||
return
|
||||
changeType(n.sons[i].sons[1], f.typ, check)
|
||||
else:
|
||||
changeType(n.sons[i].sons[1], tup.sons[i], check)
|
||||
else:
|
||||
for i in countup(0, sonsLen(n) - 1):
|
||||
var m = n.sons[i]
|
||||
if m.kind == nkExprColonExpr:
|
||||
m = m.sons[1]
|
||||
n.sons[i] = m
|
||||
changeType(m, tup.sons[i], check)
|
||||
changeType(n.sons[i], tup.sons[i], check)
|
||||
when false:
|
||||
var m = n.sons[i]
|
||||
var a = newNodeIT(nkExprColonExpr, m.info, newType.sons[i])
|
||||
addSon(a, newSymNode(newType.n.sons[i].sym))
|
||||
addSon(a, m)
|
||||
changeType(m, tup.sons[i], check)
|
||||
of nkCharLit..nkUInt64Lit:
|
||||
if check:
|
||||
let value = n.intVal
|
||||
|
|
@ -501,44 +535,45 @@ proc semArrayConstr(c: PContext, n: PNode, flags: TExprFlags): PNode =
|
|||
result.sons[i] = fitNode(c, typ, result.sons[i])
|
||||
result.typ.sons[0] = makeRangeType(c, 0, sonsLen(result) - 1, n.info)
|
||||
|
||||
proc fixAbstractType(c: PContext, n: PNode) =
|
||||
# XXX finally rewrite that crap!
|
||||
for i in countup(1, sonsLen(n) - 1):
|
||||
var it = n.sons[i]
|
||||
case it.kind
|
||||
of nkHiddenStdConv, nkHiddenSubConv:
|
||||
if it.sons[1].kind == nkBracket:
|
||||
it.sons[1].typ = arrayConstrType(c, it.sons[1])
|
||||
#it.sons[1] = semArrayConstr(c, it.sons[1])
|
||||
if skipTypes(it.typ, abstractVar).kind in {tyOpenArray, tyVarargs}:
|
||||
#if n.sons[0].kind == nkSym and IdentEq(n.sons[0].sym.name, "[]="):
|
||||
# debug(n)
|
||||
template fixAbstractType(c: PContext, n: PNode) =
|
||||
when false:
|
||||
# XXX finally rewrite that crap!
|
||||
for i in countup(1, sonsLen(n) - 1):
|
||||
var it = n.sons[i]
|
||||
case it.kind
|
||||
of nkHiddenStdConv, nkHiddenSubConv:
|
||||
if it.sons[1].kind == nkBracket:
|
||||
it.sons[1].typ = arrayConstrType(c, it.sons[1])
|
||||
#it.sons[1] = semArrayConstr(c, it.sons[1])
|
||||
if skipTypes(it.typ, abstractVar).kind in {tyOpenArray, tyVarargs}:
|
||||
#if n.sons[0].kind == nkSym and IdentEq(n.sons[0].sym.name, "[]="):
|
||||
# debug(n)
|
||||
|
||||
var s = skipTypes(it.sons[1].typ, abstractVar)
|
||||
if s.kind == tyArrayConstr and s.sons[1].kind == tyEmpty:
|
||||
s = copyType(s, getCurrOwner(), false)
|
||||
skipTypes(s, abstractVar).sons[1] = elemType(
|
||||
skipTypes(it.typ, abstractVar))
|
||||
it.sons[1].typ = s
|
||||
elif s.kind == tySequence and s.sons[0].kind == tyEmpty:
|
||||
s = copyType(s, getCurrOwner(), false)
|
||||
skipTypes(s, abstractVar).sons[0] = elemType(
|
||||
skipTypes(it.typ, abstractVar))
|
||||
it.sons[1].typ = s
|
||||
var s = skipTypes(it.sons[1].typ, abstractVar)
|
||||
if s.kind == tyArrayConstr and s.sons[1].kind == tyEmpty:
|
||||
s = copyType(s, getCurrOwner(), false)
|
||||
skipTypes(s, abstractVar).sons[1] = elemType(
|
||||
skipTypes(it.typ, abstractVar))
|
||||
it.sons[1].typ = s
|
||||
elif s.kind == tySequence and s.sons[0].kind == tyEmpty:
|
||||
s = copyType(s, getCurrOwner(), false)
|
||||
skipTypes(s, abstractVar).sons[0] = elemType(
|
||||
skipTypes(it.typ, abstractVar))
|
||||
it.sons[1].typ = s
|
||||
|
||||
elif skipTypes(it.sons[1].typ, abstractVar).kind in
|
||||
{tyNil, tyArrayConstr, tyTuple, tySet}:
|
||||
var s = skipTypes(it.typ, abstractVar)
|
||||
if s.kind != tyExpr:
|
||||
changeType(it.sons[1], s, check=true)
|
||||
n.sons[i] = it.sons[1]
|
||||
of nkBracket:
|
||||
# an implicitly constructed array (passed to an open array):
|
||||
n.sons[i] = semArrayConstr(c, it, {})
|
||||
else:
|
||||
discard
|
||||
#if (it.typ == nil):
|
||||
# InternalError(it.info, "fixAbstractType: " & renderTree(it))
|
||||
elif skipTypes(it.sons[1].typ, abstractVar).kind in
|
||||
{tyNil, tyArrayConstr, tyTuple, tySet}:
|
||||
var s = skipTypes(it.typ, abstractVar)
|
||||
if s.kind != tyExpr:
|
||||
changeType(it.sons[1], s, check=true)
|
||||
n.sons[i] = it.sons[1]
|
||||
of nkBracket:
|
||||
# an implicitly constructed array (passed to an open array):
|
||||
n.sons[i] = semArrayConstr(c, it, {})
|
||||
else:
|
||||
discard
|
||||
#if (it.typ == nil):
|
||||
# InternalError(it.info, "fixAbstractType: " & renderTree(it))
|
||||
|
||||
proc skipObjConv(n: PNode): PNode =
|
||||
case n.kind
|
||||
|
|
@ -554,7 +589,8 @@ proc isAssignable(c: PContext, n: PNode): TAssignableResult =
|
|||
result = parampatterns.isAssignable(c.p.owner, n)
|
||||
|
||||
proc newHiddenAddrTaken(c: PContext, n: PNode): PNode =
|
||||
if n.kind == nkHiddenDeref:
|
||||
if n.kind == nkHiddenDeref and not (gCmd == cmdCompileToCpp or
|
||||
sfCompileToCpp in c.module.flags):
|
||||
checkSonsLen(n, 1)
|
||||
result = n.sons[0]
|
||||
else:
|
||||
|
|
@ -652,7 +688,9 @@ proc evalAtCompileTime(c: PContext, n: PNode): PNode =
|
|||
# implicit statics.
|
||||
if n.len > 1:
|
||||
for i in 1 .. <n.len:
|
||||
if n[i].typ.kind != tyStatic or tfUnresolved notin n[i].typ.flags:
|
||||
# see bug #2113, it's possible that n[i].typ for errornous code:
|
||||
if n[i].typ.isNil or n[i].typ.kind != tyStatic or
|
||||
tfUnresolved notin n[i].typ.flags:
|
||||
break maybeLabelAsStatic
|
||||
n.typ = newTypeWithSons(c, tyStatic, @[n.typ])
|
||||
n.typ.flags.incl tfUnresolved
|
||||
|
|
@ -667,7 +705,7 @@ proc evalAtCompileTime(c: PContext, n: PNode): PNode =
|
|||
if callee.kind notin {skProc, skConverter} or callee.isGenericRoutine:
|
||||
return
|
||||
|
||||
if n.typ != nil and not typeAllowed(n.typ, skConst): return
|
||||
if n.typ != nil and typeAllowed(n.typ, skConst) != nil: return
|
||||
|
||||
var call = newNodeIT(nkCall, n.info, n.typ)
|
||||
call.add(n.sons[0])
|
||||
|
|
@ -720,11 +758,26 @@ proc semOverloadedCallAnalyseEffects(c: PContext, n: PNode, nOrig: PNode,
|
|||
else:
|
||||
if callee.kind in skIterators and callee.id == c.p.owner.id:
|
||||
localError(n.info, errRecursiveDependencyX, callee.name.s)
|
||||
# error correction, prevents endless for loop elimination in transf.
|
||||
# See bug #2051:
|
||||
result.sons[0] = newSymNode(errorSym(c, n))
|
||||
if sfNoSideEffect notin callee.flags:
|
||||
if {sfImportc, sfSideEffect} * callee.flags != {}:
|
||||
incl(c.p.owner.flags, sfSideEffect)
|
||||
|
||||
proc semObjConstr(c: PContext, n: PNode, flags: TExprFlags): PNode
|
||||
|
||||
proc resolveIndirectCall(c: PContext; n, nOrig: PNode;
|
||||
t: PType): TCandidate =
|
||||
initCandidate(c, result, t)
|
||||
matches(c, n, nOrig, result)
|
||||
if result.state != csMatch:
|
||||
# try to deref the first argument:
|
||||
if experimentalMode(c) and canDeref(n):
|
||||
n.sons[1] = n.sons[1].tryDeref
|
||||
initCandidate(c, result, t)
|
||||
matches(c, n, nOrig, result)
|
||||
|
||||
proc semIndirectOp(c: PContext, n: PNode, flags: TExprFlags): PNode =
|
||||
result = nil
|
||||
checkMinSonsLen(n, 1)
|
||||
|
|
@ -748,9 +801,7 @@ proc semIndirectOp(c: PContext, n: PNode, flags: TExprFlags): PNode =
|
|||
t = skipTypes(n.sons[0].typ, abstractInst-{tyTypeDesc})
|
||||
if t != nil and t.kind == tyProc:
|
||||
# This is a proc variable, apply normal overload resolution
|
||||
var m: TCandidate
|
||||
initCandidate(c, m, t)
|
||||
matches(c, n, nOrig, m)
|
||||
let m = resolveIndirectCall(c, n, nOrig, t)
|
||||
if m.state != csMatch:
|
||||
if c.inCompilesContext > 0:
|
||||
# speed up error generation:
|
||||
|
|
@ -780,7 +831,6 @@ proc semIndirectOp(c: PContext, n: PNode, flags: TExprFlags): PNode =
|
|||
if tfNoSideEffect notin t.flags: incl(c.p.owner.flags, sfSideEffect)
|
||||
elif t != nil and t.kind == tyTypeDesc:
|
||||
if n.len == 1: return semObjConstr(c, n, flags)
|
||||
let destType = t.skipTypes({tyTypeDesc, tyGenericInst})
|
||||
return semConv(c, n)
|
||||
else:
|
||||
result = overloadedCallOpr(c, n)
|
||||
|
|
@ -851,6 +901,11 @@ proc isTypeExpr(n: PNode): bool =
|
|||
of nkSym: result = n.sym.kind == skType
|
||||
else: result = false
|
||||
|
||||
proc createSetType(c: PContext; baseType: PType): PType =
|
||||
assert baseType != nil
|
||||
result = newTypeS(tySet, c)
|
||||
rawAddSon(result, baseType)
|
||||
|
||||
proc lookupInRecordAndBuildCheck(c: PContext, n, r: PNode, field: PIdent,
|
||||
check: var PNode): PSym =
|
||||
# transform in a node that contains the runtime check for the
|
||||
|
|
@ -866,7 +921,8 @@ proc lookupInRecordAndBuildCheck(c: PContext, n, r: PNode, field: PIdent,
|
|||
if (r.sons[0].kind != nkSym): illFormedAst(r)
|
||||
result = lookupInRecordAndBuildCheck(c, n, r.sons[0], field, check)
|
||||
if result != nil: return
|
||||
var s = newNodeI(nkCurly, r.info)
|
||||
let setType = createSetType(c, r.sons[0].typ)
|
||||
var s = newNodeIT(nkCurly, r.info, setType)
|
||||
for i in countup(1, sonsLen(r) - 1):
|
||||
var it = r.sons[i]
|
||||
case it.kind
|
||||
|
|
@ -878,13 +934,14 @@ proc lookupInRecordAndBuildCheck(c: PContext, n, r: PNode, field: PIdent,
|
|||
if check == nil:
|
||||
check = newNodeI(nkCheckedFieldExpr, n.info)
|
||||
addSon(check, ast.emptyNode) # make space for access node
|
||||
s = newNodeI(nkCurly, n.info)
|
||||
s = newNodeIT(nkCurly, n.info, setType)
|
||||
for j in countup(0, sonsLen(it) - 2): addSon(s, copyTree(it.sons[j]))
|
||||
var inExpr = newNodeI(nkCall, n.info)
|
||||
addSon(inExpr, newIdentNode(getIdent("in"), n.info))
|
||||
var inExpr = newNodeIT(nkCall, n.info, getSysType(tyBool))
|
||||
addSon(inExpr, newSymNode(ast.opContains, n.info))
|
||||
addSon(inExpr, s)
|
||||
addSon(inExpr, copyTree(r.sons[0]))
|
||||
addSon(inExpr, s) #writeln(output, renderTree(inExpr));
|
||||
addSon(check, semExpr(c, inExpr))
|
||||
addSon(check, inExpr)
|
||||
#addSon(check, semExpr(c, inExpr))
|
||||
return
|
||||
of nkElse:
|
||||
result = lookupInRecordAndBuildCheck(c, n, lastSon(it), field, check)
|
||||
|
|
@ -892,14 +949,14 @@ proc lookupInRecordAndBuildCheck(c: PContext, n, r: PNode, field: PIdent,
|
|||
if check == nil:
|
||||
check = newNodeI(nkCheckedFieldExpr, n.info)
|
||||
addSon(check, ast.emptyNode) # make space for access node
|
||||
var inExpr = newNodeI(nkCall, n.info)
|
||||
addSon(inExpr, newIdentNode(getIdent("in"), n.info))
|
||||
addSon(inExpr, copyTree(r.sons[0]))
|
||||
var inExpr = newNodeIT(nkCall, n.info, getSysType(tyBool))
|
||||
addSon(inExpr, newSymNode(ast.opContains, n.info))
|
||||
addSon(inExpr, s)
|
||||
var notExpr = newNodeI(nkCall, n.info)
|
||||
addSon(notExpr, newIdentNode(getIdent("not"), n.info))
|
||||
addSon(inExpr, copyTree(r.sons[0]))
|
||||
var notExpr = newNodeIT(nkCall, n.info, getSysType(tyBool))
|
||||
addSon(notExpr, newSymNode(ast.opNot, n.info))
|
||||
addSon(notExpr, inExpr)
|
||||
addSon(check, semExpr(c, notExpr))
|
||||
addSon(check, notExpr)
|
||||
return
|
||||
else: illFormedAst(it)
|
||||
of nkSym:
|
||||
|
|
@ -915,9 +972,10 @@ proc makeDeref(n: PNode): PNode =
|
|||
t = skipTypes(t.sons[0], {tyGenericInst})
|
||||
while t.kind in {tyPtr, tyRef}:
|
||||
var a = result
|
||||
result = newNodeIT(nkHiddenDeref, n.info, t.sons[0])
|
||||
let baseTyp = t.lastSon
|
||||
result = newNodeIT(nkHiddenDeref, n.info, baseTyp)
|
||||
addSon(result, a)
|
||||
t = skipTypes(t.lastSon, {tyGenericInst})
|
||||
t = skipTypes(baseTyp, {tyGenericInst})
|
||||
|
||||
const
|
||||
tyTypeParamsHolders = {tyGenericInst, tyCompositeTypeClass}
|
||||
|
|
@ -926,8 +984,8 @@ const
|
|||
proc readTypeParameter(c: PContext, typ: PType,
|
||||
paramName: PIdent, info: TLineInfo): PNode =
|
||||
let ty = if typ.kind == tyGenericInst: typ.skipGenericAlias
|
||||
else: (internalAssert(typ.kind == tyCompositeTypeClass); typ.sons[1])
|
||||
#debug ty
|
||||
else: (internalAssert(typ.kind == tyCompositeTypeClass);
|
||||
typ.sons[1].skipGenericAlias)
|
||||
let tbody = ty.sons[0]
|
||||
for s in countup(0, tbody.len-2):
|
||||
let tParam = tbody.sons[s]
|
||||
|
|
@ -985,7 +1043,7 @@ proc builtinFieldAccess(c: PContext, n: PNode, flags: TExprFlags): PNode =
|
|||
of tyTypeParamsHolders:
|
||||
return readTypeParameter(c, ty, i, n.info)
|
||||
of tyObject, tyTuple:
|
||||
if ty.n.kind == nkRecList:
|
||||
if ty.n != nil and ty.n.kind == nkRecList:
|
||||
for field in ty.n:
|
||||
if field.sym.name == i:
|
||||
n.typ = newTypeWithSons(c, tyFieldAccessor, @[ty, field.sym.typ])
|
||||
|
|
@ -1053,7 +1111,7 @@ proc dotTransformation(c: PContext, n: PNode): PNode =
|
|||
|
||||
proc semFieldAccess(c: PContext, n: PNode, flags: TExprFlags): PNode =
|
||||
# this is difficult, because the '.' is used in many different contexts
|
||||
# in Nimrod. We first allow types in the semantic checking.
|
||||
# in Nim. We first allow types in the semantic checking.
|
||||
result = builtinFieldAccess(c, n, flags)
|
||||
if result == nil:
|
||||
result = dotTransformation(c, n)
|
||||
|
|
@ -1077,19 +1135,20 @@ proc semSubscript(c: PContext, n: PNode, flags: TExprFlags): PNode =
|
|||
## returns nil if not a built-in subscript operator; also called for the
|
||||
## checking of assignments
|
||||
if sonsLen(n) == 1:
|
||||
var x = semDeref(c, n)
|
||||
let x = semDeref(c, n)
|
||||
if x == nil: return nil
|
||||
result = newNodeIT(nkDerefExpr, x.info, x.typ)
|
||||
result.add(x[0])
|
||||
return
|
||||
checkMinSonsLen(n, 2)
|
||||
n.sons[0] = semExprWithType(c, n.sons[0])
|
||||
var arr = skipTypes(n.sons[0].typ, {tyGenericInst, tyVar, tyPtr, tyRef})
|
||||
let arr = skipTypes(n.sons[0].typ, {tyGenericInst, tyVar, tyPtr, tyRef})
|
||||
case arr.kind
|
||||
of tyArray, tyOpenArray, tyVarargs, tyArrayConstr, tySequence, tyString,
|
||||
tyCString:
|
||||
if n.len != 2: return nil
|
||||
n.sons[0] = makeDeref(n.sons[0])
|
||||
c.p.bracketExpr = n.sons[0]
|
||||
for i in countup(1, sonsLen(n) - 1):
|
||||
n.sons[i] = semExprWithType(c, n.sons[i],
|
||||
flags*{efInTypeof, efDetermineType})
|
||||
|
|
@ -1110,6 +1169,7 @@ proc semSubscript(c: PContext, n: PNode, flags: TExprFlags): PNode =
|
|||
of tyTuple:
|
||||
checkSonsLen(n, 2)
|
||||
n.sons[0] = makeDeref(n.sons[0])
|
||||
c.p.bracketExpr = n.sons[0]
|
||||
# [] operator for tuples requires constant expression:
|
||||
n.sons[1] = semConstExpr(c, n.sons[1])
|
||||
if skipTypes(n.sons[1].typ, {tyGenericInst, tyRange, tyOrdinal}).kind in
|
||||
|
|
@ -1120,13 +1180,16 @@ proc semSubscript(c: PContext, n: PNode, flags: TExprFlags): PNode =
|
|||
else:
|
||||
localError(n.info, errIndexTypesDoNotMatch)
|
||||
result = n
|
||||
else: discard
|
||||
else:
|
||||
c.p.bracketExpr = n.sons[0]
|
||||
|
||||
proc semArrayAccess(c: PContext, n: PNode, flags: TExprFlags): PNode =
|
||||
let oldBracketExpr = c.p.bracketExpr
|
||||
result = semSubscript(c, n, flags)
|
||||
if result == nil:
|
||||
# overloaded [] operator:
|
||||
result = semExpr(c, buildOverloadedSubscripts(n, getIdent"[]"))
|
||||
c.p.bracketExpr = oldBracketExpr
|
||||
|
||||
proc propertyWriteAccess(c: PContext, n, nOrig, a: PNode): PNode =
|
||||
var id = considerQuotedIdent(a[1])
|
||||
|
|
@ -1167,6 +1230,7 @@ proc asgnToResultVar(c: PContext, n, le, ri: PNode) {.inline.} =
|
|||
if x.typ.kind == tyVar and x.kind == nkSym and x.sym.kind == skResult:
|
||||
n.sons[0] = x # 'result[]' --> 'result'
|
||||
n.sons[1] = takeImplicitAddr(c, ri)
|
||||
x.typ.flags.incl tfVarIsPtr
|
||||
|
||||
template resultTypeIsInferrable(typ: PType): expr =
|
||||
typ.isMetaType and typ.kind != tyTypeDesc
|
||||
|
|
@ -1192,11 +1256,15 @@ proc semAsgn(c: PContext, n: PNode): PNode =
|
|||
of nkBracketExpr:
|
||||
# a[i] = x
|
||||
# --> `[]=`(a, i, x)
|
||||
let oldBracketExpr = c.p.bracketExpr
|
||||
a = semSubscript(c, a, {efLValue})
|
||||
if a == nil:
|
||||
result = buildOverloadedSubscripts(n.sons[0], getIdent"[]=")
|
||||
add(result, n[1])
|
||||
return semExprNoType(c, result)
|
||||
result = semExprNoType(c, result)
|
||||
c.p.bracketExpr = oldBracketExpr
|
||||
return result
|
||||
c.p.bracketExpr = oldBracketExpr
|
||||
of nkCurlyExpr:
|
||||
# a{i} = x --> `{}=`(a, i, x)
|
||||
result = buildOverloadedSubscripts(n.sons[0], getIdent"{}=")
|
||||
|
|
@ -1232,6 +1300,9 @@ proc semAsgn(c: PContext, n: PNode): PNode =
|
|||
typeMismatch(n, lhs.typ, rhs.typ)
|
||||
|
||||
n.sons[1] = fitNode(c, le, rhs)
|
||||
if tfHasAsgn in lhs.typ.flags and not lhsIsResult:
|
||||
return overloadedAsgn(c, lhs, n.sons[1])
|
||||
|
||||
fixAbstractType(c, n)
|
||||
asgnToResultVar(c, n, n.sons[0], n.sons[1])
|
||||
result = n
|
||||
|
|
@ -1285,7 +1356,12 @@ proc semProcBody(c: PContext, n: PNode): PNode =
|
|||
|
||||
if c.p.owner.kind notin {skMacro, skTemplate} and
|
||||
c.p.resultSym != nil and c.p.resultSym.typ.isMetaType:
|
||||
localError(c.p.resultSym.info, errCannotInferReturnType)
|
||||
if isEmptyType(result.typ):
|
||||
# we inferred a 'void' return type:
|
||||
c.p.resultSym.typ = nil
|
||||
c.p.owner.typ.sons[0] = nil
|
||||
else:
|
||||
localError(c.p.resultSym.info, errCannotInferReturnType)
|
||||
|
||||
closeScope(c)
|
||||
|
||||
|
|
@ -1318,17 +1394,18 @@ proc semYield(c: PContext, n: PNode): PNode =
|
|||
elif n.sons[0].kind != nkEmpty:
|
||||
n.sons[0] = semExprWithType(c, n.sons[0]) # check for type compatibility:
|
||||
var iterType = c.p.owner.typ
|
||||
var restype = iterType.sons[0]
|
||||
let restype = iterType.sons[0]
|
||||
if restype != nil:
|
||||
let adjustedRes = if c.p.owner.kind == skIterator: restype.base
|
||||
let adjustedRes = if restype.kind == tyIter: restype.base
|
||||
else: restype
|
||||
n.sons[0] = fitNode(c, adjustedRes, n.sons[0])
|
||||
if adjustedRes.kind != tyExpr:
|
||||
n.sons[0] = fitNode(c, adjustedRes, n.sons[0])
|
||||
if n.sons[0].typ == nil: internalError(n.info, "semYield")
|
||||
|
||||
if resultTypeIsInferrable(adjustedRes):
|
||||
let inferred = n.sons[0].typ
|
||||
if c.p.owner.kind == skIterator:
|
||||
iterType.sons[0].sons[0] = inferred
|
||||
if restype.kind == tyIter:
|
||||
restype.sons[0] = inferred
|
||||
else:
|
||||
iterType.sons[0] = inferred
|
||||
|
||||
|
|
@ -1378,10 +1455,6 @@ proc semDefined(c: PContext, n: PNode, onlyCurrentScope: bool): PNode =
|
|||
localError(n.info, "obsolete usage of 'defined', use 'declared' instead")
|
||||
elif condsyms.isDefined(n.sons[1].ident):
|
||||
result.intVal = 1
|
||||
elif not condsyms.isDeclared(n.sons[1].ident):
|
||||
message(n.info, warnUser,
|
||||
"undeclared conditional symbol; use --symbol to declare it: " &
|
||||
n[1].ident.s)
|
||||
elif lookUpForDefined(c, n.sons[1], onlyCurrentScope) != nil:
|
||||
result.intVal = 1
|
||||
result.info = n.info
|
||||
|
|
@ -1453,7 +1526,9 @@ proc semExpandToAst(c: PContext, n: PNode): PNode =
|
|||
|
||||
# Preserve the magic symbol in order to be handled in evals.nim
|
||||
internalAssert n.sons[0].sym.magic == mExpandToAst
|
||||
n.typ = getSysSym("PNimrodNode").typ # expandedSym.getReturnType
|
||||
#n.typ = getSysSym("PNimrodNode").typ # expandedSym.getReturnType
|
||||
n.typ = if getCompilerProc("NimNode") != nil: sysTypeFromName"NimNode"
|
||||
else: sysTypeFromName"PNimrodNode"
|
||||
result = n
|
||||
|
||||
proc semExpandToAst(c: PContext, n: PNode, magicSym: PSym,
|
||||
|
|
@ -1581,7 +1656,7 @@ proc semShallowCopy(c: PContext, n: PNode, flags: TExprFlags): PNode =
|
|||
result = semDirectOp(c, n, flags)
|
||||
|
||||
proc createFlowVar(c: PContext; t: PType; info: TLineInfo): PType =
|
||||
result = newType(tyGenericInvokation, c.module)
|
||||
result = newType(tyGenericInvocation, c.module)
|
||||
addSonSkipIntLit(result, magicsys.getCompilerProc("FlowVar").typ)
|
||||
addSonSkipIntLit(result, t)
|
||||
result = instGenericContainer(c, info, result, allowMetaTypes = false)
|
||||
|
|
@ -1611,6 +1686,12 @@ proc semMagic(c: PContext, n: PNode, s: PSym, flags: TExprFlags): PNode =
|
|||
# DON'T forget to update ast.SpecialSemMagics if you add a magic here!
|
||||
result = n
|
||||
case s.magic # magics that need special treatment
|
||||
of mAddr:
|
||||
checkSonsLen(n, 2)
|
||||
result = semAddr(c, n.sons[1])
|
||||
of mTypeOf:
|
||||
checkSonsLen(n, 2)
|
||||
result = semTypeOf(c, n.sons[1])
|
||||
of mDefined: result = semDefined(c, setMs(n, s), false)
|
||||
of mDefinedInScope: result = semDefined(c, setMs(n, s), true)
|
||||
of mCompiles: result = semCompiles(c, setMs(n, s), flags)
|
||||
|
|
@ -1805,6 +1886,14 @@ proc semTuplePositionsConstr(c: PContext, n: PNode, flags: TExprFlags): PNode =
|
|||
addSonSkipIntLit(typ, n.sons[i].typ)
|
||||
result.typ = typ
|
||||
|
||||
proc isTupleType(n: PNode): bool =
|
||||
if n.len == 0:
|
||||
return false # don't interpret () as type
|
||||
for i in countup(0, n.len - 1):
|
||||
if n[i].typ == nil or n[i].typ.kind != tyTypeDesc:
|
||||
return false
|
||||
return true
|
||||
|
||||
proc checkInitialized(n: PNode, ids: IntSet, info: TLineInfo) =
|
||||
case n.kind
|
||||
of nkRecList:
|
||||
|
|
@ -1820,7 +1909,8 @@ proc checkInitialized(n: PNode, ids: IntSet, info: TLineInfo) =
|
|||
of nkOfBranch, nkElse: checkInitialized(lastSon(n.sons[i]), ids, info)
|
||||
else: internalError(info, "checkInitialized")
|
||||
of nkSym:
|
||||
if tfNeedsInit in n.sym.typ.flags and n.sym.name.id notin ids:
|
||||
if {tfNotNil, tfNeedsInit} * n.sym.typ.flags != {} and
|
||||
n.sym.name.id notin ids:
|
||||
message(info, errGenerated, "field not initialized: " & n.sym.name.s)
|
||||
else: internalError(info, "checkInitialized")
|
||||
|
||||
|
|
@ -1838,12 +1928,11 @@ proc semObjConstr(c: PContext, n: PNode, flags: TExprFlags): PNode =
|
|||
var ids = initIntSet()
|
||||
for i in 1.. <n.len:
|
||||
let it = n.sons[i]
|
||||
if it.kind != nkExprColonExpr or it.sons[0].kind notin {nkSym, nkIdent}:
|
||||
if it.kind != nkExprColonExpr:
|
||||
localError(n.info, errNamedExprExpected)
|
||||
break
|
||||
var id: PIdent
|
||||
if it.sons[0].kind == nkIdent: id = it.sons[0].ident
|
||||
else: id = it.sons[0].sym.name
|
||||
let id = considerQuotedIdent(it.sons[0])
|
||||
|
||||
if containsOrIncl(ids, id.id):
|
||||
localError(it.info, errFieldInitTwice, id.s)
|
||||
var e = semExprWithType(c, it.sons[1], flags*{efAllowDestructor})
|
||||
|
|
@ -1861,7 +1950,7 @@ proc semObjConstr(c: PContext, n: PNode, flags: TExprFlags): PNode =
|
|||
it.sons[0] = newSymNode(f)
|
||||
e = fitNode(c, f.typ, e)
|
||||
# small hack here in a nkObjConstr the ``nkExprColonExpr`` node can have
|
||||
# 3 childen the last being the field check
|
||||
# 3 children the last being the field check
|
||||
if check != nil:
|
||||
check.sons[0] = it.sons[0]
|
||||
it.add(check)
|
||||
|
|
@ -1937,7 +2026,7 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode =
|
|||
case n.kind
|
||||
of nkIdent, nkAccQuoted:
|
||||
var s = lookUp(c, n)
|
||||
semCaptureSym(s, c.p.owner)
|
||||
if c.inTypeClass == 0: semCaptureSym(s, c.p.owner)
|
||||
result = semSym(c, n, s, flags)
|
||||
if s.kind in {skProc, skMethod, skConverter}+skIterators:
|
||||
#performProcvarCheck(c, n, s)
|
||||
|
|
@ -1945,7 +2034,7 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode =
|
|||
if result.kind == nkSym:
|
||||
markIndirect(c, result.sym)
|
||||
# if isGenericRoutine(result.sym):
|
||||
# localError(n.info, errInstantiateXExplicitely, s.name.s)
|
||||
# localError(n.info, errInstantiateXExplicitly, s.name.s)
|
||||
of nkSym:
|
||||
# because of the changed symbol binding, this does not mean that we
|
||||
# don't have to check the symbol for semantics here again!
|
||||
|
|
@ -1994,7 +2083,7 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode =
|
|||
of nkBind:
|
||||
message(n.info, warnDeprecated, "bind")
|
||||
result = semExpr(c, n.sons[0], flags)
|
||||
of nkTypeOfExpr, nkTupleTy, nkRefTy..nkEnumTy, nkStaticTy:
|
||||
of nkTypeOfExpr, nkTupleTy, nkTupleClassTy, nkRefTy..nkEnumTy, nkStaticTy:
|
||||
var typ = semTypeNode(c, n, nil).skipTypes({tyTypeDesc, tyIter})
|
||||
result.typ = makeTypeDesc(c, typ)
|
||||
#result = symNodeFromType(c, typ, n.info)
|
||||
|
|
@ -2068,21 +2157,26 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode =
|
|||
of nkPar:
|
||||
case checkPar(n)
|
||||
of paNone: result = errorNode(c, n)
|
||||
of paTuplePositions: result = semTuplePositionsConstr(c, n, flags)
|
||||
of paTuplePositions:
|
||||
var tupexp = semTuplePositionsConstr(c, n, flags)
|
||||
if isTupleType(tupexp):
|
||||
# reinterpret as type
|
||||
var typ = semTypeNode(c, n, nil).skipTypes({tyTypeDesc, tyIter})
|
||||
result.typ = makeTypeDesc(c, typ)
|
||||
else:
|
||||
result = tupexp
|
||||
of paTupleFields: result = semTupleFieldsConstr(c, n, flags)
|
||||
of paSingle: result = semExpr(c, n.sons[0], flags)
|
||||
of nkCurly: result = semSetConstr(c, n)
|
||||
of nkBracket: result = semArrayConstr(c, n, flags)
|
||||
of nkObjConstr: result = semObjConstr(c, n, flags)
|
||||
of nkLambdaKinds: result = semLambda(c, n, flags)
|
||||
of nkLambda: result = semLambda(c, n, flags)
|
||||
of nkDo: result = semDo(c, n, flags)
|
||||
of nkDerefExpr: result = semDeref(c, n)
|
||||
of nkAddr:
|
||||
result = n
|
||||
checkSonsLen(n, 1)
|
||||
n.sons[0] = semExprWithType(c, n.sons[0])
|
||||
if isAssignable(c, n.sons[0]) notin {arLValue, arLocalLValue}:
|
||||
localError(n.info, errExprHasNoAddress)
|
||||
n.typ = makePtrType(c, n.sons[0].typ)
|
||||
result = semAddr(c, n.sons[0])
|
||||
of nkHiddenAddr, nkHiddenDeref:
|
||||
checkSonsLen(n, 1)
|
||||
n.sons[0] = semExpr(c, n.sons[0], flags)
|
||||
|
|
@ -2148,6 +2242,8 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode =
|
|||
result = semPragmaBlock(c, n)
|
||||
of nkStaticStmt:
|
||||
result = semStaticStmt(c, n)
|
||||
of nkDefer:
|
||||
localError(n.info, errGenerated, "'defer' not allowed in this context")
|
||||
else:
|
||||
localError(n.info, errInvalidExpressionX,
|
||||
renderTree(n, {renderNoComments}))
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
#
|
||||
#
|
||||
# The Nim Compiler
|
||||
# (c) Copyright 2014 Andreas Rumpf
|
||||
# (c) Copyright 2015 Andreas Rumpf
|
||||
#
|
||||
# See the file "copying.txt", included in this
|
||||
# distribution, for details about the copyright.
|
||||
|
|
@ -19,12 +19,13 @@ type
|
|||
|
||||
proc instFieldLoopBody(c: TFieldInstCtx, n: PNode, forLoop: PNode): PNode =
|
||||
case n.kind
|
||||
of nkEmpty..pred(nkIdent), succ(nkIdent)..nkNilLit: result = n
|
||||
of nkIdent:
|
||||
of nkEmpty..pred(nkIdent), succ(nkSym)..nkNilLit: result = n
|
||||
of nkIdent, nkSym:
|
||||
result = n
|
||||
let ident = considerQuotedIdent(n)
|
||||
var L = sonsLen(forLoop)
|
||||
if c.replaceByFieldName:
|
||||
if n.ident.id == forLoop[0].ident.id:
|
||||
if ident.id == considerQuotedIdent(forLoop[0]).id:
|
||||
let fieldName = if c.tupleType.isNil: c.field.name.s
|
||||
elif c.tupleType.n.isNil: "Field" & $c.tupleIndex
|
||||
else: c.tupleType.n.sons[c.tupleIndex].sym.name.s
|
||||
|
|
@ -32,7 +33,7 @@ proc instFieldLoopBody(c: TFieldInstCtx, n: PNode, forLoop: PNode): PNode =
|
|||
return
|
||||
# other fields:
|
||||
for i in ord(c.replaceByFieldName)..L-3:
|
||||
if n.ident.id == forLoop[i].ident.id:
|
||||
if ident.id == considerQuotedIdent(forLoop[i]).id:
|
||||
var call = forLoop.sons[L-2]
|
||||
var tupl = call.sons[i+1-ord(c.replaceByFieldName)]
|
||||
if c.field.isNil:
|
||||
|
|
@ -98,7 +99,7 @@ proc semForObjectFields(c: TFieldsCtx, typ, forLoop, father: PNode) =
|
|||
of nkRecList:
|
||||
for t in items(typ): semForObjectFields(c, t, forLoop, father)
|
||||
else:
|
||||
illFormedAst(typ)
|
||||
illFormedAstLocal(typ)
|
||||
|
||||
proc semForFields(c: PContext, n: PNode, m: TMagic): PNode =
|
||||
# so that 'break' etc. work as expected, we produce
|
||||
|
|
@ -155,7 +156,7 @@ proc semForFields(c: PContext, n: PNode, m: TMagic): PNode =
|
|||
dec(c.p.nestedLoopCounter)
|
||||
# for TR macros this 'while true: ...; break' loop is pretty bad, so
|
||||
# we avoid it now if we can:
|
||||
if hasSonWith(stmts, nkBreakStmt):
|
||||
if containsNode(stmts, {nkBreakStmt}):
|
||||
var b = newNodeI(nkBreakStmt, n.info)
|
||||
b.add(ast.emptyNode)
|
||||
stmts.add(b)
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
#
|
||||
#
|
||||
# The Nim Compiler
|
||||
# (c) Copyright 2014 Andreas Rumpf
|
||||
# (c) Copyright 2015 Andreas Rumpf
|
||||
#
|
||||
# See the file "copying.txt", included in this
|
||||
# distribution, for details about the copyright.
|
||||
|
|
@ -118,6 +118,9 @@ proc makeRange(typ: PType, first, last: BiggestInt): PType =
|
|||
let lowerNode = newIntNode(nkIntLit, minA)
|
||||
if typ.kind == tyInt and minA == maxA:
|
||||
result = getIntLitType(lowerNode)
|
||||
elif typ.kind in {tyUint, tyUInt64}:
|
||||
# these are not ordinal types, so you get no subrange type for these:
|
||||
result = typ
|
||||
else:
|
||||
var n = newNode(nkRange)
|
||||
addSon(n, lowerNode)
|
||||
|
|
@ -135,8 +138,9 @@ proc makeRangeF(typ: PType, first, last: BiggestFloat): PType =
|
|||
addSonSkipIntLit(result, skipTypes(typ, {tyRange}))
|
||||
|
||||
proc getIntervalType*(m: TMagic, n: PNode): PType =
|
||||
# Nimrod requires interval arithmetic for ``range`` types. Lots of tedious
|
||||
# Nim requires interval arithmetic for ``range`` types. Lots of tedious
|
||||
# work but the feature is very nice for reducing explicit conversions.
|
||||
const ordIntLit = {nkIntLit..nkUInt64Lit}
|
||||
result = n.typ
|
||||
|
||||
template commutativeOp(opr: expr) {.immediate.} =
|
||||
|
|
@ -170,13 +174,19 @@ proc getIntervalType*(m: TMagic, n: PNode): PType =
|
|||
let a = n.sons[1].typ
|
||||
if isFloatRange(a):
|
||||
# abs(-5.. 1) == (1..5)
|
||||
result = makeRangeF(a, abs(getFloat(a.n.sons[1])),
|
||||
abs(getFloat(a.n.sons[0])))
|
||||
if a.n[0].floatVal <= 0.0:
|
||||
result = makeRangeF(a, 0.0, abs(getFloat(a.n.sons[0])))
|
||||
else:
|
||||
result = makeRangeF(a, abs(getFloat(a.n.sons[1])),
|
||||
abs(getFloat(a.n.sons[0])))
|
||||
of mAbsI, mAbsI64:
|
||||
let a = n.sons[1].typ
|
||||
if isIntRange(a):
|
||||
result = makeRange(a, `|abs|`(getInt(a.n.sons[1])),
|
||||
`|abs|`(getInt(a.n.sons[0])))
|
||||
if a.n[0].intVal <= 0:
|
||||
result = makeRange(a, 0, `|abs|`(getInt(a.n.sons[0])))
|
||||
else:
|
||||
result = makeRange(a, `|abs|`(getInt(a.n.sons[1])),
|
||||
`|abs|`(getInt(a.n.sons[0])))
|
||||
of mSucc:
|
||||
let a = n.sons[1].typ
|
||||
let b = n.sons[2].typ
|
||||
|
|
@ -202,15 +212,15 @@ proc getIntervalType*(m: TMagic, n: PNode): PType =
|
|||
var a = n.sons[1]
|
||||
var b = n.sons[2]
|
||||
# symmetrical:
|
||||
if b.kind notin {nkIntLit..nkUInt32Lit}: swap(a, b)
|
||||
if b.kind in {nkIntLit..nkUInt32Lit}:
|
||||
if b.kind notin ordIntLit: swap(a, b)
|
||||
if b.kind in ordIntLit:
|
||||
let x = b.intVal|+|1
|
||||
if (x and -x) == x and x >= 0:
|
||||
result = makeRange(a.typ, 0, b.intVal)
|
||||
of mModU:
|
||||
let a = n.sons[1]
|
||||
let b = n.sons[2]
|
||||
if b.kind in {nkIntLit..nkUInt32Lit}:
|
||||
if a.kind in ordIntLit:
|
||||
if b.intVal >= 0:
|
||||
result = makeRange(a.typ, 0, b.intVal-1)
|
||||
else:
|
||||
|
|
@ -226,9 +236,9 @@ proc getIntervalType*(m: TMagic, n: PNode): PType =
|
|||
result = makeRange(a.typ, b.intVal+1, -(b.intVal+1))
|
||||
of mDivI, mDivI64, mDivU:
|
||||
binaryOp(`|div|`)
|
||||
of mMinI, mMinI64:
|
||||
of mMinI:
|
||||
commutativeOp(min)
|
||||
of mMaxI, mMaxI64:
|
||||
of mMaxI:
|
||||
commutativeOp(max)
|
||||
else: discard
|
||||
|
||||
|
|
@ -276,10 +286,11 @@ proc evalOp(m: TMagic, n, a, b, c: PNode): PNode =
|
|||
of mNot: result = newIntNodeT(1 - getInt(a), n)
|
||||
of mCard: result = newIntNodeT(nimsets.cardSet(a), n)
|
||||
of mBitnotI, mBitnotI64: result = newIntNodeT(not getInt(a), n)
|
||||
of mLengthStr: result = newIntNodeT(len(getStr(a)), n)
|
||||
of mLengthStr, mXLenStr: result = newIntNodeT(len(getStr(a)), n)
|
||||
of mLengthArray: result = newIntNodeT(lengthOrd(a.typ), n)
|
||||
of mLengthSeq, mLengthOpenArray: result = newIntNodeT(sonsLen(a), n) # BUGFIX
|
||||
of mUnaryPlusI, mUnaryPlusI64, mUnaryPlusF64: result = a # throw `+` away
|
||||
of mLengthSeq, mLengthOpenArray, mXLenSeq:
|
||||
result = newIntNodeT(sonsLen(a), n) # BUGFIX
|
||||
of mUnaryPlusI, mUnaryPlusF64: result = a # throw `+` away
|
||||
of mToFloat, mToBiggestFloat:
|
||||
result = newFloatNodeT(toFloat(int(getInt(a))), n)
|
||||
of mToInt, mToBiggestInt: result = newIntNodeT(system.toInt(getFloat(a)), n)
|
||||
|
|
@ -299,10 +310,10 @@ proc evalOp(m: TMagic, n, a, b, c: PNode): PNode =
|
|||
of mAddI, mAddI64: result = newIntNodeT(getInt(a) + getInt(b), n)
|
||||
of mSubI, mSubI64: result = newIntNodeT(getInt(a) - getInt(b), n)
|
||||
of mMulI, mMulI64: result = newIntNodeT(getInt(a) * getInt(b), n)
|
||||
of mMinI, mMinI64:
|
||||
of mMinI:
|
||||
if getInt(a) > getInt(b): result = newIntNodeT(getInt(b), n)
|
||||
else: result = newIntNodeT(getInt(a), n)
|
||||
of mMaxI, mMaxI64:
|
||||
of mMaxI:
|
||||
if getInt(a) > getInt(b): result = newIntNodeT(getInt(a), n)
|
||||
else: result = newIntNodeT(getInt(b), n)
|
||||
of mShlI, mShlI64:
|
||||
|
|
@ -419,8 +430,9 @@ proc evalOp(m: TMagic, n, a, b, c: PNode): PNode =
|
|||
of mNewString, mNewStringOfCap,
|
||||
mExit, mInc, ast.mDec, mEcho, mSwap, mAppendStrCh,
|
||||
mAppendStrStr, mAppendSeqElem, mSetLengthStr, mSetLengthSeq,
|
||||
mParseExprToAst, mParseStmtToAst, mExpandToAst, mTypeTrait,
|
||||
mNLen..mNError, mEqRef, mSlurp, mStaticExec, mNGenSym, mSpawn, mParallel:
|
||||
mParseExprToAst, mParseStmtToAst, mExpandToAst, mTypeTrait, mDotDot,
|
||||
mNLen..mNError, mEqRef, mSlurp, mStaticExec, mNGenSym, mSpawn,
|
||||
mParallel, mPlugin:
|
||||
discard
|
||||
else: internalError(a.info, "evalOp(" & $m & ')')
|
||||
|
||||
|
|
@ -513,7 +525,7 @@ proc rangeCheck(n: PNode, value: BiggestInt) =
|
|||
proc foldConv*(n, a: PNode; check = false): PNode =
|
||||
# XXX range checks?
|
||||
case skipTypes(n.typ, abstractRange).kind
|
||||
of tyInt..tyInt64:
|
||||
of tyInt..tyInt64, tyUInt..tyUInt64:
|
||||
case skipTypes(a.typ, abstractRange).kind
|
||||
of tyFloat..tyFloat64:
|
||||
result = newIntNodeT(int(getFloat(a)), n)
|
||||
|
|
@ -533,7 +545,7 @@ proc foldConv*(n, a: PNode; check = false): PNode =
|
|||
discard
|
||||
else:
|
||||
result = a
|
||||
result.typ = n.typ
|
||||
result.typ = takeType(n.typ, a.typ)
|
||||
|
||||
proc getArrayConstr(m: PSym, n: PNode): PNode =
|
||||
if n.kind == nkBracket:
|
||||
|
|
@ -665,8 +677,8 @@ proc getConstExpr(m: PSym, n: PNode): PNode =
|
|||
of mLow:
|
||||
result = newIntNodeT(firstOrd(n.sons[1].typ), n)
|
||||
of mHigh:
|
||||
if skipTypes(n.sons[1].typ, abstractVar).kind notin
|
||||
{tyOpenArray, tyVarargs, tySequence, tyString}:
|
||||
if skipTypes(n.sons[1].typ, abstractVar).kind notin
|
||||
{tySequence, tyString, tyCString, tyOpenArray, tyVarargs}:
|
||||
result = newIntNodeT(lastOrd(skipTypes(n[1].typ, abstractVar)), n)
|
||||
else:
|
||||
var a = getArrayConstr(m, n.sons[1])
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
#
|
||||
#
|
||||
# The Nim Compiler
|
||||
# (c) Copyright 2014 Andreas Rumpf
|
||||
# (c) Copyright 2015 Andreas Rumpf
|
||||
#
|
||||
# See the file "copying.txt", included in this
|
||||
# distribution, for details about the copyright.
|
||||
|
|
@ -26,9 +26,22 @@ proc getIdentNode(n: PNode): PNode =
|
|||
illFormedAst(n)
|
||||
result = n
|
||||
|
||||
type
|
||||
GenericCtx = object
|
||||
toMixin: IntSet
|
||||
cursorInBody: bool # only for nimsuggest
|
||||
|
||||
type
|
||||
TSemGenericFlag = enum
|
||||
withinBind, withinTypeDesc, withinMixin
|
||||
TSemGenericFlags = set[TSemGenericFlag]
|
||||
|
||||
proc semGenericStmt(c: PContext, n: PNode,
|
||||
flags: TSemGenericFlags, ctx: var GenericCtx): PNode
|
||||
|
||||
proc semGenericStmtScope(c: PContext, n: PNode,
|
||||
flags: TSemGenericFlags,
|
||||
ctx: var IntSet): PNode =
|
||||
ctx: var GenericCtx): PNode =
|
||||
openScope(c)
|
||||
result = semGenericStmt(c, n, flags, ctx)
|
||||
closeScope(c)
|
||||
|
|
@ -37,7 +50,8 @@ template macroToExpand(s: expr): expr =
|
|||
s.kind in {skMacro, skTemplate} and (s.typ.len == 1 or sfImmediate in s.flags)
|
||||
|
||||
proc semGenericStmtSymbol(c: PContext, n: PNode, s: PSym,
|
||||
ctx: var IntSet): PNode =
|
||||
ctx: var GenericCtx): PNode =
|
||||
semIdeForTemplateOrGenericCheck(n, ctx.cursorInBody)
|
||||
incl(s.flags, sfUsed)
|
||||
case s.kind
|
||||
of skUnknown:
|
||||
|
|
@ -60,13 +74,20 @@ proc semGenericStmtSymbol(c: PContext, n: PNode, s: PSym,
|
|||
else:
|
||||
result = symChoice(c, n, s, scOpen)
|
||||
of skGenericParam:
|
||||
result = newSymNodeTypeDesc(s, n.info)
|
||||
if s.typ != nil and s.typ.kind == tyStatic:
|
||||
if s.typ.n != nil:
|
||||
result = s.typ.n
|
||||
else:
|
||||
result = n
|
||||
else:
|
||||
result = newSymNodeTypeDesc(s, n.info)
|
||||
styleCheckUse(n.info, s)
|
||||
of skParam:
|
||||
result = n
|
||||
styleCheckUse(n.info, s)
|
||||
of skType:
|
||||
if (s.typ != nil) and (s.typ.kind != tyGenericParam):
|
||||
if (s.typ != nil) and
|
||||
(s.typ.flags * {tfGenericTypeParam, tfImplicitTypeParam} == {}):
|
||||
result = newSymNodeTypeDesc(s, n.info)
|
||||
else:
|
||||
result = n
|
||||
|
|
@ -76,17 +97,17 @@ proc semGenericStmtSymbol(c: PContext, n: PNode, s: PSym,
|
|||
styleCheckUse(n.info, s)
|
||||
|
||||
proc lookup(c: PContext, n: PNode, flags: TSemGenericFlags,
|
||||
ctx: var IntSet): PNode =
|
||||
ctx: var GenericCtx): PNode =
|
||||
result = n
|
||||
let ident = considerQuotedIdent(n)
|
||||
var s = searchInScopes(c, ident).skipAlias(n)
|
||||
if s == nil:
|
||||
if ident.id notin ctx and withinMixin notin flags:
|
||||
if ident.id notin ctx.toMixin and withinMixin notin flags:
|
||||
localError(n.info, errUndeclaredIdentifier, ident.s)
|
||||
else:
|
||||
if withinBind in flags:
|
||||
result = symChoice(c, n, s, scClosed)
|
||||
elif s.name.id in ctx:
|
||||
elif s.name.id in ctx.toMixin:
|
||||
result = symChoice(c, n, s, scForceOpen)
|
||||
else:
|
||||
result = semGenericStmtSymbol(c, n, s, ctx)
|
||||
|
|
@ -98,8 +119,10 @@ proc newDot(n, b: PNode): PNode =
|
|||
result.add(b)
|
||||
|
||||
proc fuzzyLookup(c: PContext, n: PNode, flags: TSemGenericFlags,
|
||||
ctx: var IntSet; isMacro: var bool): PNode =
|
||||
ctx: var GenericCtx; isMacro: var bool): PNode =
|
||||
assert n.kind == nkDotExpr
|
||||
semIdeForTemplateOrGenericCheck(n, ctx.cursorInBody)
|
||||
|
||||
let luf = if withinMixin notin flags: {checkUndeclared} else: {}
|
||||
|
||||
var s = qualifiedLookUp(c, n, luf)
|
||||
|
|
@ -115,7 +138,7 @@ proc fuzzyLookup(c: PContext, n: PNode, flags: TSemGenericFlags,
|
|||
isMacro = s.kind in {skTemplate, skMacro}
|
||||
if withinBind in flags:
|
||||
result = newDot(result, symChoice(c, n, s, scClosed))
|
||||
elif s.name.id in ctx:
|
||||
elif s.name.id in ctx.toMixin:
|
||||
result = newDot(result, symChoice(c, n, s, scForceOpen))
|
||||
else:
|
||||
let sym = semGenericStmtSymbol(c, n, s, ctx)
|
||||
|
|
@ -130,9 +153,11 @@ proc addTempDecl(c: PContext; n: PNode; kind: TSymKind) =
|
|||
styleCheckDef(n.info, s, kind)
|
||||
|
||||
proc semGenericStmt(c: PContext, n: PNode,
|
||||
flags: TSemGenericFlags, ctx: var IntSet): PNode =
|
||||
flags: TSemGenericFlags, ctx: var GenericCtx): PNode =
|
||||
result = n
|
||||
if gCmd == cmdIdeTools: suggestStmt(c, n)
|
||||
#if gCmd == cmdIdeTools: suggestStmt(c, n)
|
||||
semIdeForTemplateOrGenericCheck(n, ctx.cursorInBody)
|
||||
|
||||
case n.kind
|
||||
of nkIdent, nkAccQuoted:
|
||||
result = lookup(c, n, flags, ctx)
|
||||
|
|
@ -155,14 +180,15 @@ proc semGenericStmt(c: PContext, n: PNode,
|
|||
of nkBind:
|
||||
result = semGenericStmt(c, n.sons[0], flags+{withinBind}, ctx)
|
||||
of nkMixinStmt:
|
||||
result = semMixinStmt(c, n, ctx)
|
||||
result = semMixinStmt(c, n, ctx.toMixin)
|
||||
of nkCall, nkHiddenCallConv, nkInfix, nkPrefix, nkCommand, nkCallStrLit:
|
||||
# check if it is an expression macro:
|
||||
checkMinSonsLen(n, 1)
|
||||
let fn = n.sons[0]
|
||||
var s = qualifiedLookUp(c, fn, {})
|
||||
if s == nil and withinMixin notin flags and
|
||||
fn.kind in {nkIdent, nkAccQuoted} and considerQuotedIdent(fn).id notin ctx:
|
||||
fn.kind in {nkIdent, nkAccQuoted} and
|
||||
considerQuotedIdent(fn).id notin ctx.toMixin:
|
||||
localError(n.info, errUndeclaredIdentifier, fn.renderTree)
|
||||
|
||||
var first = 0
|
||||
|
|
@ -170,7 +196,7 @@ proc semGenericStmt(c: PContext, n: PNode,
|
|||
if s != nil:
|
||||
incl(s.flags, sfUsed)
|
||||
mixinContext = s.magic in {mDefined, mDefinedInScope, mCompiles}
|
||||
let scOption = if s.name.id in ctx: scForceOpen else: scOpen
|
||||
let scOption = if s.name.id in ctx.toMixin: scForceOpen else: scOpen
|
||||
case s.kind
|
||||
of skMacro:
|
||||
if macroToExpand(s):
|
||||
|
|
@ -217,7 +243,7 @@ proc semGenericStmt(c: PContext, n: PNode,
|
|||
elif fn.kind == nkDotExpr:
|
||||
result.sons[0] = fuzzyLookup(c, fn, flags, ctx, mixinContext)
|
||||
first = 1
|
||||
# Consider 'when defined(globalsSlot): ThreadVarSetValue(globalsSlot, ...)'
|
||||
# Consider 'when declared(globalsSlot): ThreadVarSetValue(globalsSlot, ...)'
|
||||
# in threads.nim: the subtle preprocessing here binds 'globalsSlot' which
|
||||
# is not exported and yet the generic 'threadProcWrapper' works correctly.
|
||||
let flags = if mixinContext: flags+{withinMixin} else: flags
|
||||
|
|
@ -330,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)
|
||||
|
|
@ -371,3 +397,8 @@ proc semGenericStmt(c: PContext, n: PNode,
|
|||
for i in countup(0, sonsLen(n) - 1):
|
||||
result.sons[i] = semGenericStmt(c, n.sons[i], flags, ctx)
|
||||
|
||||
proc semGenericStmt(c: PContext, n: PNode): PNode =
|
||||
var ctx: GenericCtx
|
||||
ctx.toMixin = initIntset()
|
||||
result = semGenericStmt(c, n, {}, ctx)
|
||||
semIdeForTemplateOrGeneric(c, result, ctx.cursorInBody)
|
||||
|
|
|
|||
|
|
@ -21,7 +21,8 @@ proc instantiateGenericParamList(c: PContext, n: PNode, pt: TIdTable,
|
|||
var q = a.sym
|
||||
if q.typ.kind notin {tyTypeDesc, tyGenericParam, tyStatic, tyIter}+tyTypeClasses:
|
||||
continue
|
||||
var s = newSym(skType, q.name, getCurrOwner(), q.info)
|
||||
let symKind = if q.typ.kind == tyStatic: skConst else: skType
|
||||
var s = newSym(symKind, q.name, getCurrOwner(), q.info)
|
||||
s.flags = s.flags + {sfUsed, sfFromGeneric}
|
||||
var t = PType(idTableGet(pt, q.typ))
|
||||
if t == nil:
|
||||
|
|
@ -35,11 +36,12 @@ proc instantiateGenericParamList(c: PContext, n: PNode, pt: TIdTable,
|
|||
elif t.kind == tyGenericParam:
|
||||
localError(a.info, errCannotInstantiateX, q.name.s)
|
||||
t = errorType(c)
|
||||
elif t.kind == tyGenericInvokation:
|
||||
elif t.kind == tyGenericInvocation:
|
||||
#t = instGenericContainer(c, a, t)
|
||||
t = generateTypeInstance(c, pt, a, t)
|
||||
#t = ReplaceTypeVarsT(cl, t)
|
||||
s.typ = t
|
||||
if t.kind == tyStatic: s.ast = t.n
|
||||
addDecl(c, s)
|
||||
entry.concreteTypes[i] = t
|
||||
|
||||
|
|
@ -75,11 +77,12 @@ proc removeDefaultParamValues(n: PNode) =
|
|||
proc freshGenSyms(n: PNode, owner: PSym, symMap: var TIdTable) =
|
||||
# we need to create a fresh set of gensym'ed symbols:
|
||||
if n.kind == nkSym and sfGenSym in n.sym.flags:
|
||||
var x = PSym(idTableGet(symMap, n.sym))
|
||||
let s = n.sym
|
||||
var x = PSym(idTableGet(symMap, s))
|
||||
if x == nil:
|
||||
x = copySym(n.sym, false)
|
||||
x = copySym(s, false)
|
||||
x.owner = owner
|
||||
idTablePut(symMap, n.sym, x)
|
||||
idTablePut(symMap, s, x)
|
||||
n.sym = x
|
||||
else:
|
||||
for i in 0 .. <safeLen(n): freshGenSyms(n.sons[i], owner, symMap)
|
||||
|
|
@ -97,13 +100,18 @@ proc addProcDecls(c: PContext, fn: PSym) =
|
|||
|
||||
maybeAddResult(c, fn, fn.ast)
|
||||
|
||||
proc instantiateBody(c: PContext, n: PNode, result: PSym) =
|
||||
proc instantiateBody(c: PContext, n, params: PNode, result: PSym) =
|
||||
if n.sons[bodyPos].kind != nkEmpty:
|
||||
inc c.inGenericInst
|
||||
# add it here, so that recursive generic procs are possible:
|
||||
var b = n.sons[bodyPos]
|
||||
var symMap: TIdTable
|
||||
initIdTable symMap
|
||||
if params != nil:
|
||||
for i in 1 .. <params.len:
|
||||
let param = params[i].sym
|
||||
if sfGenSym in param.flags:
|
||||
idTablePut(symMap, params[i].sym, result.typ.n[param.position+1].sym)
|
||||
freshGenSyms(b, result, symMap)
|
||||
b = semProcBody(c, b)
|
||||
b = hloBody(c, b)
|
||||
|
|
@ -120,7 +128,7 @@ proc fixupInstantiatedSymbols(c: PContext, s: PSym) =
|
|||
openScope(c)
|
||||
var n = oldPrc.ast
|
||||
n.sons[bodyPos] = copyTree(s.getBody)
|
||||
instantiateBody(c, n, oldPrc)
|
||||
instantiateBody(c, n, nil, oldPrc)
|
||||
closeScope(c)
|
||||
popInfoContext()
|
||||
|
||||
|
|
@ -167,17 +175,32 @@ proc instantiateProcType(c: PContext, pt: TIdTable,
|
|||
result.n = originalParams.shallowCopy
|
||||
|
||||
for i in 1 .. <result.len:
|
||||
# twrong_field_caching requires these 'resetIdTable' calls:
|
||||
if i > 1:
|
||||
resetIdTable(cl.symMap)
|
||||
resetIdTable(cl.localCache)
|
||||
result.sons[i] = replaceTypeVarsT(cl, result.sons[i])
|
||||
propagateToOwner(result, result.sons[i])
|
||||
let param = replaceTypeVarsN(cl, originalParams[i])
|
||||
result.n.sons[i] = param
|
||||
if param.kind == nkSym:
|
||||
# XXX: this won't be true for void params
|
||||
# implement pass-through of void params and
|
||||
# the "sort by distance to point" container
|
||||
param.sym.owner = prc
|
||||
addDecl(c, param.sym)
|
||||
internalAssert originalParams[i].kind == nkSym
|
||||
when true:
|
||||
let oldParam = originalParams[i].sym
|
||||
let param = copySym(oldParam)
|
||||
param.owner = prc
|
||||
param.typ = result.sons[i]
|
||||
if oldParam.ast != nil:
|
||||
param.ast = fitNode(c, param.typ, oldParam.ast)
|
||||
|
||||
# don't be lazy here and call replaceTypeVarsN(cl, originalParams[i])!
|
||||
result.n.sons[i] = newSymNode(param)
|
||||
addDecl(c, param)
|
||||
else:
|
||||
let param = replaceTypeVarsN(cl, originalParams[i])
|
||||
result.n.sons[i] = param
|
||||
param.sym.owner = prc
|
||||
addDecl(c, result.n.sons[i].sym)
|
||||
|
||||
resetIdTable(cl.symMap)
|
||||
resetIdTable(cl.localCache)
|
||||
result.sons[0] = replaceTypeVarsT(cl, result.sons[0])
|
||||
result.n.sons[0] = originalParams[0].copyTree
|
||||
|
||||
|
|
@ -232,7 +255,7 @@ proc generateInstance(c: PContext, fn: PSym, pt: TIdTable,
|
|||
pragma(c, result, n.sons[pragmasPos], allRoutinePragmas)
|
||||
if isNil(n.sons[bodyPos]):
|
||||
n.sons[bodyPos] = copyTree(fn.getBody)
|
||||
instantiateBody(c, n, result)
|
||||
instantiateBody(c, n, fn.typ.n, result)
|
||||
sideEffectsCheck(c, result)
|
||||
paramsTypeCheck(c, result.typ)
|
||||
else:
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
#
|
||||
#
|
||||
# The Nim Compiler
|
||||
# (c) Copyright 2014 Andreas Rumpf
|
||||
# (c) Copyright 2015 Andreas Rumpf
|
||||
#
|
||||
# See the file "copying.txt", included in this
|
||||
# distribution, for details about the copyright.
|
||||
|
|
@ -34,7 +34,7 @@ proc ithField(n: PNode, field: int): PSym =
|
|||
else: discard
|
||||
|
||||
proc annotateType*(n: PNode, t: PType) =
|
||||
let x = t.skipTypes(abstractInst)
|
||||
let x = t.skipTypes(abstractInst+{tyRange})
|
||||
# Note: x can be unequal to t and we need to be careful to use 't'
|
||||
# to not to skip tyGenericInst
|
||||
case n.kind
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
#
|
||||
#
|
||||
# The Nim Compiler
|
||||
# (c) Copyright 2014 Andreas Rumpf
|
||||
# (c) Copyright 2015 Andreas Rumpf
|
||||
#
|
||||
# See the file "copying.txt", included in this
|
||||
# distribution, for details about the copyright.
|
||||
|
|
@ -10,6 +10,20 @@
|
|||
# This include file implements the semantic checking for magics.
|
||||
# included from sem.nim
|
||||
|
||||
proc semAddr(c: PContext; n: PNode): PNode =
|
||||
result = newNodeI(nkAddr, n.info)
|
||||
let x = semExprWithType(c, n)
|
||||
if isAssignable(c, x) notin {arLValue, arLocalLValue}:
|
||||
localError(n.info, errExprHasNoAddress)
|
||||
result.add x
|
||||
result.typ = makePtrType(c, x.typ)
|
||||
|
||||
proc semTypeOf(c: PContext; n: PNode): PNode =
|
||||
result = newNodeI(nkTypeOfExpr, n.info)
|
||||
let typExpr = semExprWithType(c, n, {efInTypeof})
|
||||
result.add typExpr
|
||||
result.typ = makeTypeDesc(c, typExpr.typ.skipTypes({tyTypeDesc, tyIter}))
|
||||
|
||||
proc semIsPartOf(c: PContext, n: PNode, flags: TExprFlags): PNode =
|
||||
var r = isPartOf(n[1], n[2])
|
||||
result = newIntNodeT(ord(r), n)
|
||||
|
|
@ -40,7 +54,7 @@ proc evalTypeTrait(trait: PNode, operand: PType, context: PSym): PNode =
|
|||
result.typ = newType(tyString, context)
|
||||
result.info = trait.info
|
||||
of "arity":
|
||||
result = newIntNode(nkIntLit, typ.n.len-1)
|
||||
result = newIntNode(nkIntLit, typ.len - ord(typ.kind==tyProc))
|
||||
result.typ = newType(tyInt, context)
|
||||
result.info = trait.info
|
||||
else:
|
||||
|
|
@ -87,38 +101,28 @@ proc semBindSym(c: PContext, n: PNode): PNode =
|
|||
else:
|
||||
localError(n.sons[1].info, errUndeclaredIdentifier, sl.strVal)
|
||||
|
||||
proc semLocals(c: PContext, n: PNode): PNode =
|
||||
var counter = 0
|
||||
var tupleType = newTypeS(tyTuple, c)
|
||||
result = newNodeIT(nkPar, n.info, tupleType)
|
||||
tupleType.n = newNodeI(nkRecList, n.info)
|
||||
# for now we skip openarrays ...
|
||||
for scope in walkScopes(c.currentScope):
|
||||
if scope == c.topLevelScope: break
|
||||
for it in items(scope.symbols):
|
||||
# XXX parameters' owners are wrong for generics; this caused some pain
|
||||
# for closures too; we should finally fix it.
|
||||
#if it.owner != c.p.owner: return result
|
||||
if it.kind in skLocalVars and
|
||||
it.typ.skipTypes({tyGenericInst, tyVar}).kind notin
|
||||
{tyVarargs, tyOpenArray, tyTypeDesc, tyStatic, tyExpr, tyStmt, tyEmpty}:
|
||||
|
||||
var field = newSym(skField, it.name, getCurrOwner(), n.info)
|
||||
field.typ = it.typ.skipTypes({tyGenericInst, tyVar})
|
||||
field.position = counter
|
||||
inc(counter)
|
||||
|
||||
addSon(tupleType.n, newSymNode(field))
|
||||
addSonSkipIntLit(tupleType, field.typ)
|
||||
|
||||
var a = newSymNode(it, result.info)
|
||||
if it.typ.skipTypes({tyGenericInst}).kind == tyVar: a = newDeref(a)
|
||||
result.add(a)
|
||||
|
||||
proc semShallowCopy(c: PContext, n: PNode, flags: TExprFlags): PNode
|
||||
|
||||
proc isStrangeArray(t: PType): bool =
|
||||
let t = t.skipTypes(abstractInst)
|
||||
result = t.kind == tyArray and t.firstOrd != 0
|
||||
|
||||
proc isNegative(n: PNode): bool =
|
||||
let n = n.skipConv
|
||||
if n.kind in {nkCharLit..nkUInt64Lit}:
|
||||
result = n.intVal < 0
|
||||
elif n.kind in nkCallKinds and n.sons[0].kind == nkSym:
|
||||
result = n.sons[0].sym.magic in {mUnaryMinusI, mUnaryMinusI64}
|
||||
|
||||
proc magicsAfterOverloadResolution(c: PContext, n: PNode,
|
||||
flags: TExprFlags): PNode =
|
||||
case n[0].sym.magic
|
||||
of mAddr:
|
||||
checkSonsLen(n, 2)
|
||||
result = semAddr(c, n.sons[1])
|
||||
of mTypeOf:
|
||||
checkSonsLen(n, 2)
|
||||
result = semTypeOf(c, n.sons[1])
|
||||
of mIsPartOf: result = semIsPartOf(c, n, flags)
|
||||
of mTypeTrait: result = semTypeTraits(c, n)
|
||||
of mAstToStr:
|
||||
|
|
@ -129,8 +133,45 @@ proc magicsAfterOverloadResolution(c: PContext, n: PNode,
|
|||
of mHigh, mLow: result = semLowHigh(c, n, n[0].sym.magic)
|
||||
of mShallowCopy: result = semShallowCopy(c, n, flags)
|
||||
of mNBindSym: result = semBindSym(c, n)
|
||||
of mLocals: result = semLocals(c, n)
|
||||
of mProcCall:
|
||||
result = n
|
||||
result.typ = n[1].typ
|
||||
of mDotDot:
|
||||
result = n
|
||||
# disallow negative indexing for now:
|
||||
if not c.p.bracketExpr.isNil:
|
||||
if isNegative(n.sons[1]) or (n.len > 2 and isNegative(n.sons[2])):
|
||||
localError(n.info, "use '^' instead of '-'; negative indexing is obsolete")
|
||||
of mRoof:
|
||||
# error correction:
|
||||
result = n.sons[1]
|
||||
if c.p.bracketExpr.isNil:
|
||||
localError(n.info, "no surrounding array access context for '^'")
|
||||
elif c.p.bracketExpr.checkForSideEffects != seNoSideEffect:
|
||||
localError(n.info, "invalid context for '^' as '$#' has side effects" %
|
||||
renderTree(c.p.bracketExpr))
|
||||
elif c.p.bracketExpr.typ.isStrangeArray:
|
||||
localError(n.info, "invalid context for '^' as len!=high+1 for '$#'" %
|
||||
renderTree(c.p.bracketExpr))
|
||||
else:
|
||||
# ^x is rewritten to: len(a)-x
|
||||
let lenExpr = newNodeI(nkCall, n.info)
|
||||
lenExpr.add newIdentNode(getIdent"len", n.info)
|
||||
lenExpr.add c.p.bracketExpr
|
||||
let lenExprB = semExprWithType(c, lenExpr)
|
||||
if lenExprB.typ.isNil or not isOrdinalType(lenExprB.typ):
|
||||
localError(n.info, "'$#' has to be of an ordinal type for '^'" %
|
||||
renderTree(lenExpr))
|
||||
else:
|
||||
result = newNodeIT(nkCall, n.info, getSysType(tyInt))
|
||||
result.add newSymNode(createMagic("-", mSubI), n.info)
|
||||
result.add lenExprB
|
||||
result.add n.sons[1]
|
||||
of mPlugin:
|
||||
let plugin = getPlugin(n[0].sym)
|
||||
if plugin.isNil:
|
||||
localError(n.info, "cannot find plugin " & n[0].sym.name.s)
|
||||
result = n
|
||||
else:
|
||||
result = plugin(c, n)
|
||||
else: result = n
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
#
|
||||
#
|
||||
# The Nim Compiler
|
||||
# (c) Copyright 2014 Andreas Rumpf
|
||||
# (c) Copyright 2015 Andreas Rumpf
|
||||
#
|
||||
# See the file "copying.txt", included in this
|
||||
# distribution, for details about the copyright.
|
||||
|
|
@ -317,8 +317,9 @@ proc analyseIf(c: var AnalysisCtx; n: PNode) =
|
|||
proc analyse(c: var AnalysisCtx; n: PNode) =
|
||||
case n.kind
|
||||
of nkAsgn, nkFastAsgn:
|
||||
if n[0].isSingleAssignable and n[1].isLocal:
|
||||
let slot = c.getSlot(n[1].sym)
|
||||
let y = n[1].skipConv
|
||||
if n[0].isSingleAssignable and y.isLocal:
|
||||
let slot = c.getSlot(y.sym)
|
||||
slot.alias = n[0].sym
|
||||
elif n[0].isLocal:
|
||||
# since we already ensure sfAddrTaken is not in s.flags, we only need to
|
||||
|
|
@ -334,7 +335,7 @@ proc analyse(c: var AnalysisCtx; n: PNode) =
|
|||
analyse(c, n[0])
|
||||
else:
|
||||
analyseSons(c, n)
|
||||
addAsgnFact(c.guards, n[0], n[1])
|
||||
addAsgnFact(c.guards, n[0], y)
|
||||
of nkCallKinds:
|
||||
# direct call:
|
||||
if n[0].kind == nkSym: analyseCall(c, n, n[0].sym)
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
#
|
||||
#
|
||||
# The Nim Compiler
|
||||
# (c) Copyright 2014 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 @@ import
|
|||
# Predefined effects:
|
||||
# io, time (time dependent), gc (performs GC'ed allocation), exceptions,
|
||||
# side effect (accesses global), store (stores into *type*),
|
||||
# store_unkown (performs some store) --> store(any)|store(x)
|
||||
# store_unknown (performs some store) --> store(any)|store(x)
|
||||
# load (loads from *type*), recursive (recursive call), unsafe,
|
||||
# endless (has endless loops), --> user effects are defined over *patterns*
|
||||
# --> a TR macro can annotate the proc with user defined annotations
|
||||
|
|
@ -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:
|
||||
|
|
@ -194,6 +194,39 @@ proc warnAboutGcUnsafe(n: PNode) =
|
|||
#assert false
|
||||
message(n.info, warnGcUnsafe, renderTree(n))
|
||||
|
||||
proc markGcUnsafe(a: PEffects; reason: PSym) =
|
||||
a.gcUnsafe = true
|
||||
if a.owner.kind in routineKinds: a.owner.gcUnsafetyReason = reason
|
||||
|
||||
proc markGcUnsafe(a: PEffects; reason: PNode) =
|
||||
a.gcUnsafe = true
|
||||
if a.owner.kind in routineKinds:
|
||||
if reason.kind == nkSym:
|
||||
a.owner.gcUnsafetyReason = reason.sym
|
||||
else:
|
||||
a.owner.gcUnsafetyReason = newSym(skUnknown, getIdent("<unknown>"),
|
||||
a.owner, reason.info)
|
||||
|
||||
proc listGcUnsafety(s: PSym; onlyWarning: bool) =
|
||||
let u = s.gcUnsafetyReason
|
||||
if u != nil:
|
||||
let msgKind = if onlyWarning: warnGcUnsafe2 else: errGenerated
|
||||
if u.kind in {skLet, skVar}:
|
||||
message(s.info, msgKind,
|
||||
("'$#' is not GC-safe as it accesses '$#'" &
|
||||
" which is a global using GC'ed memory") % [s.name.s, u.name.s])
|
||||
elif u.kind in routineKinds:
|
||||
# recursive call *always* produces only a warning so the full error
|
||||
# message is printed:
|
||||
listGcUnsafety(u, true)
|
||||
message(s.info, msgKind,
|
||||
"'$#' is not GC-safe as it calls '$#'" %
|
||||
[s.name.s, u.name.s])
|
||||
else:
|
||||
internalAssert u.kind == skUnknown
|
||||
message(u.info, msgKind,
|
||||
"'$#' is not GC-safe as it performs an indirect call here" % s.name.s)
|
||||
|
||||
proc useVar(a: PEffects, n: PNode) =
|
||||
let s = n.sym
|
||||
if isLocalVar(a, s):
|
||||
|
|
@ -206,10 +239,9 @@ proc useVar(a: PEffects, n: PNode) =
|
|||
a.init.add s.id
|
||||
if {sfGlobal, sfThread} * s.flags == {sfGlobal} and s.kind in {skVar, skLet}:
|
||||
if s.guard != nil: guardGlobal(a, n, s.guard)
|
||||
if (tfHasGCedMem in s.typ.flags or s.typ.isGCedMem) and
|
||||
tfGcSafe notin s.typ.flags:
|
||||
if warnGcUnsafe in gNotes: warnAboutGcUnsafe(n)
|
||||
a.gcUnsafe = true
|
||||
if (tfHasGCedMem in s.typ.flags or s.typ.isGCedMem):
|
||||
#if warnGcUnsafe in gNotes: warnAboutGcUnsafe(n)
|
||||
markGcUnsafe(a, s)
|
||||
|
||||
type
|
||||
TIntersection = seq[tuple[id, count: int]] # a simple count table
|
||||
|
|
@ -230,7 +262,7 @@ proc getEbase(): PType =
|
|||
|
||||
proc excType(n: PNode): PType =
|
||||
# reraise is like raising E_Base:
|
||||
let t = if n.kind == nkEmpty: getEbase() else: n.typ
|
||||
let t = if n.kind == nkEmpty or n.typ.isNil: getEbase() else: n.typ
|
||||
result = skipTypes(t, skipPtrs)
|
||||
|
||||
proc createRaise(n: PNode): PNode =
|
||||
|
|
@ -448,7 +480,7 @@ proc propagateEffects(tracked: PEffects, n: PNode, s: PSym) =
|
|||
|
||||
if notGcSafe(s.typ) and sfImportc notin s.flags:
|
||||
if warnGcUnsafe in gNotes: warnAboutGcUnsafe(n)
|
||||
tracked.gcUnsafe = true
|
||||
markGcUnsafe(tracked, s)
|
||||
mergeLockLevels(tracked, n, s.getLockLevel)
|
||||
|
||||
proc notNilCheck(tracked: PEffects, n: PNode, paramType: PType) =
|
||||
|
|
@ -458,7 +490,7 @@ proc notNilCheck(tracked: PEffects, n: PNode, paramType: PType) =
|
|||
if n.kind == nkAddr:
|
||||
# addr(x[]) can't be proven, but addr(x) can:
|
||||
if not containsNode(n, {nkDerefExpr, nkHiddenDeref}): return
|
||||
elif n.kind == nkSym and n.sym.kind in routineKinds:
|
||||
elif (n.kind == nkSym and n.sym.kind in routineKinds) or n.kind in procDefs:
|
||||
# 'p' is not nil obviously:
|
||||
return
|
||||
case impliesNotNil(tracked.guards, n)
|
||||
|
|
@ -502,13 +534,13 @@ proc trackOperand(tracked: PEffects, n: PNode, paramType: PType) =
|
|||
# assume GcUnsafe unless in its type; 'forward' does not matter:
|
||||
if notGcSafe(op) and not isOwnedProcVar(a, tracked.owner):
|
||||
if warnGcUnsafe in gNotes: warnAboutGcUnsafe(n)
|
||||
tracked.gcUnsafe = true
|
||||
markGcUnsafe(tracked, a)
|
||||
else:
|
||||
mergeEffects(tracked, effectList.sons[exceptionEffects], n)
|
||||
mergeTags(tracked, effectList.sons[tagEffects], n)
|
||||
if notGcSafe(op):
|
||||
if warnGcUnsafe in gNotes: warnAboutGcUnsafe(n)
|
||||
tracked.gcUnsafe = true
|
||||
markGcUnsafe(tracked, a)
|
||||
notNilCheck(tracked, n, paramType)
|
||||
|
||||
proc breaksBlock(n: PNode): bool =
|
||||
|
|
@ -528,7 +560,10 @@ proc trackCase(tracked: PEffects, n: PNode) =
|
|||
track(tracked, n.sons[0])
|
||||
let oldState = tracked.init.len
|
||||
let oldFacts = tracked.guards.len
|
||||
let interesting = interestingCaseExpr(n.sons[0]) and warnProveField in gNotes
|
||||
let stringCase = skipTypes(n.sons[0].typ,
|
||||
abstractVarRange-{tyTypeDesc}).kind in {tyFloat..tyFloat128, tyString}
|
||||
let interesting = not stringCase and interestingCaseExpr(n.sons[0]) and
|
||||
warnProveField in gNotes
|
||||
var inter: TIntersection = @[]
|
||||
var toCover = 0
|
||||
for i in 1.. <n.len:
|
||||
|
|
@ -543,13 +578,8 @@ proc trackCase(tracked: PEffects, n: PNode) =
|
|||
for i in oldState.. <tracked.init.len:
|
||||
addToIntersection(inter, tracked.init[i])
|
||||
|
||||
let exh = case skipTypes(n.sons[0].typ, abstractVarRange-{tyTypeDesc}).kind
|
||||
of tyFloat..tyFloat128, tyString:
|
||||
lastSon(n).kind == nkElse
|
||||
else:
|
||||
true
|
||||
setLen(tracked.init, oldState)
|
||||
if exh:
|
||||
if not stringCase or lastSon(n).kind == nkElse:
|
||||
for id, count in items(inter):
|
||||
if count >= toCover: tracked.init.add id
|
||||
# else we can't merge
|
||||
|
|
@ -656,7 +686,7 @@ proc track(tracked: PEffects, n: PNode) =
|
|||
# and it's not a recursive call:
|
||||
if not (a.kind == nkSym and a.sym == tracked.owner):
|
||||
warnAboutGcUnsafe(n)
|
||||
tracked.gcUnsafe = true
|
||||
markGcUnsafe(tracked, a)
|
||||
for i in 1 .. <len(n): trackOperand(tracked, n.sons[i], paramType(op, i))
|
||||
if a.kind == nkSym and a.sym.magic in {mNew, mNewFinalize, mNewSeq}:
|
||||
# may not look like an assignment, but it is:
|
||||
|
|
@ -682,8 +712,8 @@ proc track(tracked: PEffects, n: PNode) =
|
|||
of nkVarSection, nkLetSection:
|
||||
for child in n:
|
||||
let last = lastSon(child)
|
||||
if last.kind != nkEmpty: track(tracked, last)
|
||||
if child.kind == nkIdentDefs and last.kind != nkEmpty:
|
||||
track(tracked, last)
|
||||
for i in 0 .. child.len-3:
|
||||
initVar(tracked, child.sons[i], volatileCheck=false)
|
||||
addAsgnFact(tracked.guards, child.sons[i], last)
|
||||
|
|
@ -732,7 +762,7 @@ proc track(tracked: PEffects, n: PNode) =
|
|||
setLen(tracked.locked, oldLocked)
|
||||
tracked.currLockLevel = oldLockLevel
|
||||
of nkTypeSection, nkProcDef, nkConverterDef, nkMethodDef, nkIteratorDef,
|
||||
nkMacroDef, nkTemplateDef:
|
||||
nkMacroDef, nkTemplateDef, nkLambda, nkDo:
|
||||
discard
|
||||
else:
|
||||
for i in 0 .. <safeLen(n): track(tracked, n.sons[i])
|
||||
|
|
@ -849,19 +879,22 @@ proc trackProc*(s: PSym, body: PNode) =
|
|||
# after the check, use the formal spec:
|
||||
effects.sons[tagEffects] = tagsSpec
|
||||
|
||||
if optThreadAnalysis in gGlobalOptions:
|
||||
if sfThread in s.flags and t.gcUnsafe:
|
||||
if optThreads in gGlobalOptions:
|
||||
localError(s.info, "'$1' is not GC-safe" % s.name.s)
|
||||
else:
|
||||
localError(s.info, warnGcUnsafe2, s.name.s)
|
||||
if not t.gcUnsafe: s.typ.flags.incl tfGcSafe
|
||||
if s.typ.lockLevel == UnspecifiedLockLevel:
|
||||
s.typ.lockLevel = t.maxLockLevel
|
||||
elif t.maxLockLevel > s.typ.lockLevel:
|
||||
localError(s.info,
|
||||
"declared lock level is $1, but real lock level is $2" %
|
||||
[$s.typ.lockLevel, $t.maxLockLevel])
|
||||
if sfThread in s.flags and t.gcUnsafe:
|
||||
if optThreads in gGlobalOptions and optThreadAnalysis in gGlobalOptions:
|
||||
#localError(s.info, "'$1' is not GC-safe" % s.name.s)
|
||||
listGcUnsafety(s, onlyWarning=false)
|
||||
else:
|
||||
listGcUnsafety(s, onlyWarning=true)
|
||||
#localError(s.info, warnGcUnsafe2, s.name.s)
|
||||
if not t.gcUnsafe:
|
||||
s.typ.flags.incl tfGcSafe
|
||||
if s.typ.lockLevel == UnspecifiedLockLevel:
|
||||
s.typ.lockLevel = t.maxLockLevel
|
||||
elif t.maxLockLevel > s.typ.lockLevel:
|
||||
#localError(s.info,
|
||||
message(s.info, warnLockLevel,
|
||||
"declared lock level is $1, but real lock level is $2" %
|
||||
[$s.typ.lockLevel, $t.maxLockLevel])
|
||||
|
||||
proc trackTopLevelStmt*(module: PSym; n: PNode) =
|
||||
if n.kind in {nkPragma, nkMacroDef, nkTemplateDef, nkProcDef,
|
||||
|
|
|
|||
|
|
@ -92,20 +92,16 @@ proc semProc(c: PContext, n: PNode): PNode
|
|||
include semdestruct
|
||||
|
||||
proc semDestructorCheck(c: PContext, n: PNode, flags: TExprFlags) {.inline.} =
|
||||
if efAllowDestructor notin flags and n.kind in nkCallKinds+{nkObjConstr}:
|
||||
if efAllowDestructor notin flags and
|
||||
n.kind in nkCallKinds+{nkObjConstr,nkBracket}:
|
||||
if instantiateDestructor(c, n.typ) != nil:
|
||||
localError(n.info, errGenerated,
|
||||
"usage of a type with a destructor in a non destructible context")
|
||||
localError(n.info, warnDestructor)
|
||||
# This still breaks too many things:
|
||||
when false:
|
||||
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.} =
|
||||
result = newNodeIT(nkHiddenDeref, n.info, n.typ.sons[0])
|
||||
addSon(result, n)
|
||||
|
||||
proc semExprBranch(c: PContext, n: PNode): PNode =
|
||||
result = semExpr(c, n)
|
||||
if result.typ != nil:
|
||||
|
|
@ -340,6 +336,44 @@ proc checkNilable(v: PSym) =
|
|||
elif tfNotNil in v.typ.flags and tfNotNil notin v.ast.typ.flags:
|
||||
message(v.info, warnProveInit, v.name.s)
|
||||
|
||||
include semasgn
|
||||
|
||||
proc addToVarSection(c: PContext; result: var PNode; orig, identDefs: PNode) =
|
||||
# consider this:
|
||||
# var
|
||||
# x = 0
|
||||
# withOverloadedAssignment = foo()
|
||||
# y = use(withOverloadedAssignment)
|
||||
# We need to split this into a statement list with multiple 'var' sections
|
||||
# in order for this transformation to be correct.
|
||||
let L = identDefs.len
|
||||
let value = identDefs[L-1]
|
||||
if value.typ != nil and tfHasAsgn in value.typ.flags:
|
||||
# the spec says we need to rewrite 'var x = T()' to 'var x: T; x = T()':
|
||||
identDefs.sons[L-1] = emptyNode
|
||||
if result.kind != nkStmtList:
|
||||
let oldResult = result
|
||||
oldResult.add identDefs
|
||||
result = newNodeI(nkStmtList, result.info)
|
||||
result.add oldResult
|
||||
else:
|
||||
let o = copyNode(orig)
|
||||
o.add identDefs
|
||||
result.add o
|
||||
for i in 0 .. L-3:
|
||||
result.add overloadedAsgn(c, identDefs[i], value)
|
||||
elif result.kind == nkStmtList:
|
||||
let o = copyNode(orig)
|
||||
o.add identDefs
|
||||
result.add o
|
||||
else:
|
||||
result.add identDefs
|
||||
|
||||
proc isDiscardUnderscore(v: PSym): bool =
|
||||
if v.name.s == "_":
|
||||
v.flags.incl(sfGenSym)
|
||||
result = true
|
||||
|
||||
proc semVarOrLet(c: PContext, n: PNode, symkind: TSymKind): PNode =
|
||||
var b: PNode
|
||||
result = copyNode(n)
|
||||
|
|
@ -359,13 +393,17 @@ proc semVarOrLet(c: PContext, n: PNode, symkind: TSymKind): PNode =
|
|||
var def: PNode
|
||||
if a.sons[length-1].kind != nkEmpty:
|
||||
def = semExprWithType(c, a.sons[length-1], {efAllowDestructor})
|
||||
if def.typ.kind == tyTypeDesc and c.p.owner.kind != skMacro:
|
||||
# prevent the all too common 'var x = int' bug:
|
||||
localError(def.info, "'typedesc' metatype is not valid here; typed '=' instead of ':'?")
|
||||
def.typ = errorType(c)
|
||||
if typ != nil:
|
||||
if typ.isMetaType:
|
||||
def = inferWithMetatype(c, typ, def)
|
||||
typ = def.typ
|
||||
else:
|
||||
# BUGFIX: ``fitNode`` is needed here!
|
||||
# check type compability 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:
|
||||
|
|
@ -380,8 +418,7 @@ proc semVarOrLet(c: PContext, n: PNode, symkind: TSymKind): PNode =
|
|||
|
||||
# this can only happen for errornous var statements:
|
||||
if typ == nil: continue
|
||||
if not typeAllowed(typ, symkind):
|
||||
localError(a.info, errXisNoType, typeToString(typ))
|
||||
typeAllowedCheck(a.info, typ, symkind)
|
||||
var tup = skipTypes(typ, {tyGenericInst})
|
||||
if a.kind == nkVarTuple:
|
||||
if tup.kind != tyTuple:
|
||||
|
|
@ -393,19 +430,23 @@ proc semVarOrLet(c: PContext, n: PNode, symkind: TSymKind): PNode =
|
|||
newSons(b, length)
|
||||
b.sons[length-2] = a.sons[length-2] # keep type desc for doc generator
|
||||
b.sons[length-1] = def
|
||||
addSon(result, b)
|
||||
addToVarSection(c, result, n, b)
|
||||
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)
|
||||
if sfGenSym notin v.flags and not isDiscardUnderscore(v):
|
||||
addInterfaceDecl(c, v)
|
||||
when oKeepVariableNames:
|
||||
if c.inUnrolledContext > 0: v.flags.incl(sfShadowed)
|
||||
else:
|
||||
let shadowed = findShadowedVar(c, v)
|
||||
if shadowed != nil:
|
||||
shadowed.flags.incl(sfShadowed)
|
||||
if shadowed.kind == skResult and sfGenSym notin v.flags:
|
||||
message(a.info, warnResultShadowed)
|
||||
# a shadowed variable is an error unless it appears on the right
|
||||
# side of the '=':
|
||||
if warnShadowIdent in gNotes and not identWithin(def, v.name):
|
||||
|
|
@ -423,7 +464,7 @@ proc semVarOrLet(c: PContext, n: PNode, symkind: TSymKind): PNode =
|
|||
addSon(b, newSymNode(v))
|
||||
addSon(b, a.sons[length-2]) # keep type desc for doc generator
|
||||
addSon(b, copyTree(def))
|
||||
addSon(result, b)
|
||||
addToVarSection(c, result, n, b)
|
||||
else:
|
||||
if def.kind == nkPar: v.ast = def[j]
|
||||
v.typ = tup.sons[j]
|
||||
|
|
@ -456,7 +497,7 @@ proc semConst(c: PContext, n: PNode): PNode =
|
|||
if typ == nil:
|
||||
localError(a.sons[2].info, errConstExprExpected)
|
||||
continue
|
||||
if not typeAllowed(typ, skConst) and def.kind != nkNilLit:
|
||||
if typeAllowed(typ, skConst) != nil and def.kind != nkNilLit:
|
||||
localError(a.info, errXisNoType, typeToString(typ))
|
||||
continue
|
||||
v.typ = typ
|
||||
|
|
@ -512,7 +553,8 @@ proc semForVars(c: PContext, n: PNode): PNode =
|
|||
if getCurrOwner().kind == skModule: incl(v.flags, sfGlobal)
|
||||
v.typ = iter.sons[i]
|
||||
n.sons[i] = newSymNode(v)
|
||||
if sfGenSym notin v.flags: addForVarDecl(c, v)
|
||||
if sfGenSym notin v.flags and not isDiscardUnderscore(v):
|
||||
addForVarDecl(c, v)
|
||||
inc(c.p.nestedLoopCounter)
|
||||
n.sons[length-1] = semStmt(c, n.sons[length-1])
|
||||
dec(c.p.nestedLoopCounter)
|
||||
|
|
@ -648,12 +690,23 @@ proc typeSectionRightSidePass(c: PContext, n: PNode) =
|
|||
#debug s.typ
|
||||
s.ast = a
|
||||
popOwner()
|
||||
let aa = a.sons[2]
|
||||
if aa.kind in {nkRefTy, nkPtrTy} and aa.len == 1 and
|
||||
aa.sons[0].kind == nkObjectTy:
|
||||
# give anonymous object a dummy symbol:
|
||||
var st = s.typ
|
||||
if st.kind == tyGenericBody: st = st.lastSon
|
||||
internalAssert st.kind in {tyPtr, tyRef}
|
||||
internalAssert st.lastSon.sym == nil
|
||||
st.lastSon.sym = newSym(skType, getIdent(s.name.s & ":ObjectType"),
|
||||
getCurrOwner(), s.info)
|
||||
|
||||
proc checkForMetaFields(n: PNode) =
|
||||
template checkMeta(t) =
|
||||
if t != nil and t.isMetaType and tfGenericTypeParam notin t.flags:
|
||||
localError(n.info, errTIsNotAConcreteType, t.typeToString)
|
||||
|
||||
if n.isNil: return
|
||||
case n.kind
|
||||
of nkRecList, nkRecCase:
|
||||
for s in n: checkForMetaFields(s)
|
||||
|
|
@ -663,8 +716,8 @@ proc checkForMetaFields(n: PNode) =
|
|||
let t = n.sym.typ
|
||||
case t.kind
|
||||
of tySequence, tySet, tyArray, tyOpenArray, tyVar, tyPtr, tyRef,
|
||||
tyProc, tyGenericInvokation, tyGenericInst:
|
||||
let start = ord(t.kind in {tyGenericInvokation, tyGenericInst})
|
||||
tyProc, tyGenericInvocation, tyGenericInst:
|
||||
let start = ord(t.kind in {tyGenericInvocation, tyGenericInst})
|
||||
for i in start .. <t.sons.len:
|
||||
checkMeta(t.sons[i])
|
||||
else:
|
||||
|
|
@ -688,18 +741,8 @@ proc typeSectionFinalPass(c: PContext, n: PNode) =
|
|||
assignType(s.typ, t)
|
||||
s.typ.id = t.id # same id
|
||||
checkConstructedType(s.info, s.typ)
|
||||
if s.typ.kind in {tyObject, tyTuple}:
|
||||
if s.typ.kind in {tyObject, tyTuple} and not s.typ.n.isNil:
|
||||
checkForMetaFields(s.typ.n)
|
||||
let aa = a.sons[2]
|
||||
if aa.kind in {nkRefTy, nkPtrTy} and aa.len == 1 and
|
||||
aa.sons[0].kind == nkObjectTy:
|
||||
# give anonymous object a dummy symbol:
|
||||
var st = s.typ
|
||||
if st.kind == tyGenericBody: st = st.lastSon
|
||||
internalAssert st.kind in {tyPtr, tyRef}
|
||||
internalAssert st.lastSon.sym == nil
|
||||
st.lastSon.sym = newSym(skType, getIdent(s.name.s & ":ObjectType"),
|
||||
getCurrOwner(), s.info)
|
||||
|
||||
proc semTypeSection(c: PContext, n: PNode): PNode =
|
||||
## Processes a type section. This must be done in separate passes, in order
|
||||
|
|
@ -753,7 +796,8 @@ proc lookupMacro(c: PContext, n: PNode): PSym =
|
|||
else:
|
||||
result = searchInScopes(c, considerQuotedIdent(n), {skMacro, skTemplate})
|
||||
|
||||
proc semProcAnnotation(c: PContext, prc: PNode): PNode =
|
||||
proc semProcAnnotation(c: PContext, prc: PNode;
|
||||
validPragmas: TSpecialWords): PNode =
|
||||
var n = prc.sons[pragmasPos]
|
||||
if n == nil or n.kind == nkEmpty: return
|
||||
for i in countup(0, <n.len):
|
||||
|
|
@ -778,12 +822,18 @@ proc semProcAnnotation(c: PContext, prc: PNode): PNode =
|
|||
x.add(it.sons[1])
|
||||
x.add(prc)
|
||||
# recursion assures that this works for multiple macro annotations too:
|
||||
return semStmt(c, x)
|
||||
result = semStmt(c, x)
|
||||
# since a proc annotation can set pragmas, we process these here again.
|
||||
# This is required for SqueakNim-like export pragmas.
|
||||
if result.kind in procDefs and result[namePos].kind == nkSym and
|
||||
result[pragmasPos].kind != nkEmpty:
|
||||
pragma(c, result[namePos].sym, result[pragmasPos], validPragmas)
|
||||
return
|
||||
|
||||
proc semLambda(c: PContext, n: PNode, flags: TExprFlags): PNode =
|
||||
# XXX semProcAux should be good enough for this now, we will eventually
|
||||
# remove semLambda
|
||||
result = semProcAnnotation(c, n)
|
||||
result = semProcAnnotation(c, n, lambdaPragmas)
|
||||
if result != nil: return result
|
||||
result = n
|
||||
checkSonsLen(n, bodyPos + 1)
|
||||
|
|
@ -813,8 +863,7 @@ proc semLambda(c: PContext, n: PNode, flags: TExprFlags): PNode =
|
|||
# we have a list of implicit type parameters:
|
||||
n.sons[genericParamsPos] = gp
|
||||
else:
|
||||
s.typ = newTypeS(tyProc, c)
|
||||
rawAddSon(s.typ, nil)
|
||||
s.typ = newProcType(c, n.info)
|
||||
if n.sons[pragmasPos].kind != nkEmpty:
|
||||
pragma(c, s, n.sons[pragmasPos], lambdaPragmas)
|
||||
s.options = gOptions
|
||||
|
|
@ -826,9 +875,9 @@ proc semLambda(c: PContext, n: PNode, flags: TExprFlags): PNode =
|
|||
if gp.len == 0 or (gp.len == 1 and tfRetType in gp[0].typ.flags):
|
||||
pushProcCon(c, s)
|
||||
addResult(c, s.typ.sons[0], n.info, skProc)
|
||||
addResultNode(c, n)
|
||||
let semBody = hloBody(c, semProcBody(c, n.sons[bodyPos]))
|
||||
n.sons[bodyPos] = transformBody(c.module, semBody, s)
|
||||
addResultNode(c, n)
|
||||
popProcCon(c)
|
||||
elif efOperand notin flags:
|
||||
localError(n.info, errGenericLambdaNotAllowed)
|
||||
|
|
@ -839,6 +888,13 @@ proc semLambda(c: PContext, n: PNode, flags: TExprFlags): PNode =
|
|||
popOwner()
|
||||
result.typ = s.typ
|
||||
|
||||
proc semDo(c: PContext, n: PNode, flags: TExprFlags): PNode =
|
||||
# 'do' without params produces a stmt:
|
||||
if n[genericParamsPos].kind == nkEmpty and n[paramsPos].kind == nkEmpty:
|
||||
result = semStmt(c, n[bodyPos])
|
||||
else:
|
||||
result = semLambda(c, n, flags)
|
||||
|
||||
proc semInferredLambda(c: PContext, pt: TIdTable, n: PNode): PNode =
|
||||
var n = n
|
||||
|
||||
|
|
@ -850,13 +906,15 @@ proc semInferredLambda(c: PContext, pt: TIdTable, n: PNode): PNode =
|
|||
|
||||
openScope(c)
|
||||
var s = n.sons[namePos].sym
|
||||
pushOwner(s)
|
||||
addParams(c, n.typ.n, skProc)
|
||||
pushProcCon(c, s)
|
||||
addResult(c, n.typ.sons[0], n.info, skProc)
|
||||
addResultNode(c, n)
|
||||
let semBody = hloBody(c, semProcBody(c, n.sons[bodyPos]))
|
||||
n.sons[bodyPos] = transformBody(c.module, semBody, n.sons[namePos].sym)
|
||||
addResultNode(c, n)
|
||||
popProcCon(c)
|
||||
popOwner()
|
||||
closeScope(c)
|
||||
|
||||
s.ast = result
|
||||
|
|
@ -887,11 +945,12 @@ proc maybeAddResult(c: PContext, s: PSym, n: PNode) =
|
|||
|
||||
proc semOverride(c: PContext, s: PSym, n: PNode) =
|
||||
case s.name.s.normalize
|
||||
of "destroy":
|
||||
of "destroy", "=destroy":
|
||||
doDestructorStuff(c, s, n)
|
||||
if not experimentalMode(c):
|
||||
localError n.info, "use the {.experimental.} pragma to enable destructors"
|
||||
of "deepcopy":
|
||||
incl(s.flags, sfUsed)
|
||||
of "deepcopy", "=deepcopy":
|
||||
if s.typ.len == 2 and
|
||||
s.typ.sons[1].skipTypes(abstractInst).kind in {tyRef, tyPtr} and
|
||||
sameType(s.typ.sons[1], s.typ.sons[0]):
|
||||
|
|
@ -900,7 +959,7 @@ proc semOverride(c: PContext, s: PSym, n: PNode) =
|
|||
var t = s.typ.sons[1].skipTypes(abstractInst).lastSon.skipTypes(abstractInst)
|
||||
while true:
|
||||
if t.kind == tyGenericBody: t = t.lastSon
|
||||
elif t.kind == tyGenericInvokation: t = t.sons[0]
|
||||
elif t.kind == tyGenericInvocation: t = t.sons[0]
|
||||
else: break
|
||||
if t.kind in {tyObject, tyDistinct, tyEnum}:
|
||||
if t.deepCopy.isNil: t.deepCopy = s
|
||||
|
|
@ -913,10 +972,35 @@ proc semOverride(c: PContext, s: PSym, n: PNode) =
|
|||
else:
|
||||
localError(n.info, errGenerated,
|
||||
"signature for 'deepCopy' must be proc[T: ptr|ref](x: T): T")
|
||||
of "=": discard
|
||||
else: localError(n.info, errGenerated,
|
||||
"'destroy' or 'deepCopy' expected for 'override'")
|
||||
incl(s.flags, sfUsed)
|
||||
incl(s.flags, sfUsed)
|
||||
of "=":
|
||||
incl(s.flags, sfUsed)
|
||||
let t = s.typ
|
||||
if t.len == 3 and t.sons[0] == nil and t.sons[1].kind == tyVar:
|
||||
var obj = t.sons[1].sons[0]
|
||||
while true:
|
||||
incl(obj.flags, tfHasAsgn)
|
||||
if obj.kind == tyGenericBody: obj = obj.lastSon
|
||||
elif obj.kind == tyGenericInvocation: obj = obj.sons[0]
|
||||
else: break
|
||||
var objB = t.sons[2]
|
||||
while true:
|
||||
if objB.kind == tyGenericBody: objB = objB.lastSon
|
||||
elif objB.kind == tyGenericInvocation: objB = objB.sons[0]
|
||||
else: break
|
||||
if obj.kind in {tyObject, tyDistinct} and sameType(obj, objB):
|
||||
if obj.assignment.isNil:
|
||||
obj.assignment = s
|
||||
else:
|
||||
localError(n.info, errGenerated,
|
||||
"cannot bind another '=' to: " & typeToString(obj))
|
||||
return
|
||||
localError(n.info, errGenerated,
|
||||
"signature for '=' must be proc[T: object](x: var T; y: T)")
|
||||
else:
|
||||
if sfOverriden in s.flags:
|
||||
localError(n.info, errGenerated,
|
||||
"'destroy' or 'deepCopy' expected for 'override'")
|
||||
|
||||
type
|
||||
TProcCompilationSteps = enum
|
||||
|
|
@ -931,7 +1015,7 @@ proc isForwardDecl(s: PSym): bool =
|
|||
proc semProcAux(c: PContext, n: PNode, kind: TSymKind,
|
||||
validPragmas: TSpecialWords,
|
||||
phase = stepRegisterSymbol): PNode =
|
||||
result = semProcAnnotation(c, n)
|
||||
result = semProcAnnotation(c, n, validPragmas)
|
||||
if result != nil: return result
|
||||
result = n
|
||||
checkSonsLen(n, bodyPos + 1)
|
||||
|
|
@ -948,7 +1032,7 @@ proc semProcAux(c: PContext, n: PNode, kind: TSymKind,
|
|||
s = semIdentDef(c, n.sons[0], kind)
|
||||
n.sons[namePos] = newSymNode(s)
|
||||
s.ast = n
|
||||
s.scope = c.currentScope
|
||||
#s.scope = c.currentScope
|
||||
|
||||
if sfNoForward in c.module.flags and
|
||||
sfSystemModule notin c.module.flags:
|
||||
|
|
@ -960,14 +1044,14 @@ proc semProcAux(c: PContext, n: PNode, kind: TSymKind,
|
|||
s.owner = getCurrOwner()
|
||||
typeIsDetermined = s.typ == nil
|
||||
s.ast = n
|
||||
s.scope = c.currentScope
|
||||
#s.scope = c.currentScope
|
||||
|
||||
# if typeIsDetermined: assert phase == stepCompileBody
|
||||
# else: assert phase == stepDetermineType
|
||||
# before compiling the proc body, set as current the scope
|
||||
# where the proc was declared
|
||||
let oldScope = c.currentScope
|
||||
c.currentScope = s.scope
|
||||
#c.currentScope = s.scope
|
||||
pushOwner(s)
|
||||
openScope(c)
|
||||
var gp: PNode
|
||||
|
|
@ -986,14 +1070,13 @@ proc semProcAux(c: PContext, n: PNode, kind: TSymKind,
|
|||
# check for semantics again:
|
||||
# semParamList(c, n.sons[ParamsPos], nil, s)
|
||||
else:
|
||||
s.typ = newTypeS(tyProc, c)
|
||||
rawAddSon(s.typ, nil)
|
||||
s.typ = newProcType(c, n.info)
|
||||
if n.sons[patternPos].kind != nkEmpty:
|
||||
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)
|
||||
var proto = searchForProc(c, oldScope, s)
|
||||
if proto == nil:
|
||||
if s.kind == skClosureIterator: s.typ.callConv = ccClosure
|
||||
else: s.typ.callConv = lastOptionEntry(c).defaultCC
|
||||
|
|
@ -1001,10 +1084,10 @@ proc semProcAux(c: PContext, n: PNode, kind: TSymKind,
|
|||
if sfGenSym in s.flags: discard
|
||||
elif kind in OverloadableSyms:
|
||||
if not typeIsDetermined:
|
||||
addInterfaceOverloadableSymAt(c, s.scope, s)
|
||||
addInterfaceOverloadableSymAt(c, oldScope, s)
|
||||
else:
|
||||
if not typeIsDetermined:
|
||||
addInterfaceDeclAt(c, s.scope, s)
|
||||
addInterfaceDeclAt(c, oldScope, s)
|
||||
if n.sons[pragmasPos].kind != nkEmpty:
|
||||
pragma(c, s, n.sons[pragmasPos], validPragmas)
|
||||
else:
|
||||
|
|
@ -1034,7 +1117,7 @@ proc semProcAux(c: PContext, n: PNode, kind: TSymKind,
|
|||
popOwner()
|
||||
pushOwner(s)
|
||||
s.options = gOptions
|
||||
if sfOverriden in s.flags: semOverride(c, s, n)
|
||||
if sfOverriden in s.flags or s.name.s[0] == '=': semOverride(c, s, n)
|
||||
if n.sons[bodyPos].kind != nkEmpty:
|
||||
# for DLL generation it is annoying to check for sfImportc!
|
||||
if sfBorrow in s.flags:
|
||||
|
|
@ -1046,6 +1129,7 @@ proc semProcAux(c: PContext, n: PNode, kind: TSymKind,
|
|||
if n.sons[genericParamsPos].kind == nkEmpty or usePseudoGenerics:
|
||||
if not usePseudoGenerics: paramsTypeCheck(c, s.typ)
|
||||
pushProcCon(c, s)
|
||||
c.p.wasForwarded = proto != nil
|
||||
maybeAddResult(c, s, n)
|
||||
if sfImportc notin s.flags:
|
||||
# no semantic checking for importc:
|
||||
|
|
@ -1057,8 +1141,9 @@ proc semProcAux(c: PContext, n: PNode, kind: TSymKind,
|
|||
else:
|
||||
if s.typ.sons[0] != nil and kind notin skIterators:
|
||||
addDecl(c, newSym(skUnknown, getIdent"result", nil, n.info))
|
||||
var toBind = initIntSet()
|
||||
n.sons[bodyPos] = semGenericStmtScope(c, n.sons[bodyPos], {}, toBind)
|
||||
openScope(c)
|
||||
n.sons[bodyPos] = semGenericStmt(c, n.sons[bodyPos])
|
||||
closeScope(c)
|
||||
fixupInstantiatedSymbols(c, s)
|
||||
if sfImportc in s.flags:
|
||||
# so we just ignore the body after semantic checking for importc:
|
||||
|
|
@ -1070,7 +1155,7 @@ proc semProcAux(c: PContext, n: PNode, kind: TSymKind,
|
|||
elif sfBorrow in s.flags: semBorrow(c, n, s)
|
||||
sideEffectsCheck(c, s)
|
||||
closeScope(c) # close scope for parameters
|
||||
c.currentScope = oldScope
|
||||
# c.currentScope = oldScope
|
||||
popOwner()
|
||||
if n.sons[patternPos].kind != nkEmpty:
|
||||
c.patterns.add(s)
|
||||
|
|
@ -1128,7 +1213,10 @@ proc semMethod(c: PContext, n: PNode): PNode =
|
|||
result = semProcAux(c, n, skMethod, methodPragmas)
|
||||
|
||||
var s = result.sons[namePos].sym
|
||||
if not isGenericRoutine(s) and result.sons[bodyPos].kind != nkEmpty:
|
||||
if not isGenericRoutine(s):
|
||||
# why check for the body? bug #2400 has none. Checking for sfForward makes
|
||||
# no sense either.
|
||||
# and result.sons[bodyPos].kind != nkEmpty:
|
||||
if hasObjParam(s):
|
||||
methodDef(s, fromCache=false)
|
||||
else:
|
||||
|
|
@ -1183,6 +1271,8 @@ proc semPragmaBlock(c: PContext, n: PNode): PNode =
|
|||
else: discard
|
||||
|
||||
proc semStaticStmt(c: PContext, n: PNode): PNode =
|
||||
#echo "semStaticStmt"
|
||||
#writeStackTrace()
|
||||
let a = semStmt(c, n.sons[0])
|
||||
n.sons[0] = a
|
||||
evalStaticStmt(c.module, a, c.p.owner)
|
||||
|
|
@ -1200,7 +1290,8 @@ proc semStaticStmt(c: PContext, n: PNode): PNode =
|
|||
proc usesResult(n: PNode): bool =
|
||||
# nkStmtList(expr) properly propagates the void context,
|
||||
# so we don't need to process that all over again:
|
||||
if n.kind notin {nkStmtList, nkStmtListExpr} + procDefs:
|
||||
if n.kind notin {nkStmtList, nkStmtListExpr,
|
||||
nkMacroDef, nkTemplateDef} + procDefs:
|
||||
if isAtom(n):
|
||||
result = n.kind == nkSym and n.sym.kind == skResult
|
||||
elif n.kind == nkReturnStmt:
|
||||
|
|
@ -1252,7 +1343,7 @@ proc semStmtList(c: PContext, n: PNode, flags: TExprFlags): PNode =
|
|||
var tryStmt = newNodeI(nkTryStmt, n.sons[i].info)
|
||||
var body = newNodeI(nkStmtList, n.sons[i].info)
|
||||
if i < n.sonsLen - 1:
|
||||
body.sons = n.sons[(i+1)..(-1)]
|
||||
body.sons = n.sons[(i+1)..n.len-1]
|
||||
tryStmt.addSon(body)
|
||||
tryStmt.addSon(deferPart)
|
||||
n.sons[i] = semTry(c, tryStmt)
|
||||
|
|
@ -1261,10 +1352,14 @@ proc semStmtList(c: PContext, n: PNode, flags: TExprFlags): PNode =
|
|||
return
|
||||
else:
|
||||
n.sons[i] = semExpr(c, n.sons[i])
|
||||
if c.inTypeClass > 0 and n[i].typ != nil and n[i].typ.kind == tyBool:
|
||||
let verdict = semConstExpr(c, n[i])
|
||||
if verdict.intVal == 0:
|
||||
localError(result.info, "type class predicate failed")
|
||||
if c.inTypeClass > 0 and n[i].typ != nil:
|
||||
case n[i].typ.kind
|
||||
of tyBool:
|
||||
let verdict = semConstExpr(c, n[i])
|
||||
if verdict.intVal == 0:
|
||||
localError(result.info, "type class predicate failed")
|
||||
of tyUnknown: continue
|
||||
else: discard
|
||||
if n.sons[i].typ == enforceVoidContext or usesResult(n.sons[i]):
|
||||
voidContext = true
|
||||
n.typ = enforceVoidContext
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
#
|
||||
#
|
||||
# The Nim Compiler
|
||||
# (c) Copyright 2014 Andreas Rumpf
|
||||
# (c) Copyright 2015 Andreas Rumpf
|
||||
#
|
||||
# See the file "copying.txt", included in this
|
||||
# distribution, for details about the copyright.
|
||||
|
|
@ -105,10 +105,11 @@ proc replaceIdentBySym(n: var PNode, s: PNode) =
|
|||
else: illFormedAst(n)
|
||||
|
||||
type
|
||||
TemplCtx {.pure, final.} = object
|
||||
TemplCtx = object
|
||||
c: PContext
|
||||
toBind, toMixin, toInject: IntSet
|
||||
owner: PSym
|
||||
cursorInBody: bool # only for nimsuggest
|
||||
|
||||
proc getIdentNode(c: var TemplCtx, n: PNode): PNode =
|
||||
case n.kind
|
||||
|
|
@ -127,7 +128,7 @@ proc getIdentNode(c: var TemplCtx, n: PNode): PNode =
|
|||
|
||||
proc isTemplParam(c: TemplCtx, n: PNode): bool {.inline.} =
|
||||
result = n.kind == nkSym and n.sym.kind == skParam and
|
||||
n.sym.owner == c.owner
|
||||
n.sym.owner == c.owner and sfGenSym notin n.sym.flags
|
||||
|
||||
proc semTemplBody(c: var TemplCtx, n: PNode): PNode
|
||||
|
||||
|
|
@ -246,8 +247,8 @@ proc semRoutineInTemplBody(c: var TemplCtx, n: PNode, k: TSymKind): PNode =
|
|||
n.sons[i] = semTemplBody(c, n.sons[i])
|
||||
closeScope(c)
|
||||
|
||||
proc semTemplSomeDecl(c: var TemplCtx, n: PNode, symKind: TSymKind) =
|
||||
for i in countup(0, sonsLen(n) - 1):
|
||||
proc semTemplSomeDecl(c: var TemplCtx, n: PNode, symKind: TSymKind; start=0) =
|
||||
for i in countup(start, sonsLen(n) - 1):
|
||||
var a = n.sons[i]
|
||||
if a.kind == nkCommentStmt: continue
|
||||
if (a.kind != nkIdentDefs) and (a.kind != nkVarTuple): illFormedAst(a)
|
||||
|
|
@ -261,6 +262,7 @@ proc semTemplSomeDecl(c: var TemplCtx, n: PNode, symKind: TSymKind) =
|
|||
proc semPattern(c: PContext, n: PNode): PNode
|
||||
proc semTemplBody(c: var TemplCtx, n: PNode): PNode =
|
||||
result = n
|
||||
semIdeForTemplateOrGenericCheck(n, c.cursorInBody)
|
||||
case n.kind
|
||||
of nkIdent:
|
||||
if n.ident.id in c.toInject: return n
|
||||
|
|
@ -348,6 +350,10 @@ proc semTemplBody(c: var TemplCtx, n: PNode): PNode =
|
|||
a.sons[L-1] = semTemplBodyScope(c, a.sons[L-1])
|
||||
of nkVarSection: semTemplSomeDecl(c, n, skVar)
|
||||
of nkLetSection: semTemplSomeDecl(c, n, skLet)
|
||||
of nkFormalParams:
|
||||
checkMinSonsLen(n, 1)
|
||||
n.sons[0] = semTemplBody(c, n.sons[0])
|
||||
semTemplSomeDecl(c, n, skParam, 1)
|
||||
of nkConstSection:
|
||||
for i in countup(0, sonsLen(n) - 1):
|
||||
var a = n.sons[i]
|
||||
|
|
@ -402,7 +408,13 @@ proc semTemplBody(c: var TemplCtx, n: PNode): PNode =
|
|||
if n.kind == nkDotExpr or n.kind == nkAccQuoted:
|
||||
let s = qualifiedLookUp(c.c, n, {})
|
||||
if s != nil:
|
||||
if contains(c.toBind, s.id):
|
||||
# do not symchoice a quoted template parameter (bug #2390):
|
||||
if s.owner == c.owner and s.kind == skParam and
|
||||
n.kind == nkAccQuoted and n.len == 1:
|
||||
incl(s.flags, sfUsed)
|
||||
styleCheckUse(n.info, s)
|
||||
return newSymNode(s, n.info)
|
||||
elif contains(c.toBind, s.id):
|
||||
return symChoice(c.c, n, s, scClosed)
|
||||
elif contains(c.toMixin, s.name.id):
|
||||
return symChoice(c.c, n, s, scForceOpen)
|
||||
|
|
@ -414,6 +426,7 @@ proc semTemplBody(c: var TemplCtx, n: PNode): PNode =
|
|||
|
||||
proc semTemplBodyDirty(c: var TemplCtx, n: PNode): PNode =
|
||||
result = n
|
||||
semIdeForTemplateOrGenericCheck(n, c.cursorInBody)
|
||||
case n.kind
|
||||
of nkIdent:
|
||||
let s = qualifiedLookUp(c.c, n, {})
|
||||
|
|
@ -429,7 +442,7 @@ proc semTemplBodyDirty(c: var TemplCtx, n: PNode): PNode =
|
|||
of nkEmpty, nkSym..nkNilLit:
|
||||
discard
|
||||
else:
|
||||
# dotExpr is ambiguous: note that we explicitely allow 'x.TemplateParam',
|
||||
# dotExpr is ambiguous: note that we explicitly allow 'x.TemplateParam',
|
||||
# so we use the generic code for nkDotExpr too
|
||||
if n.kind == nkDotExpr or n.kind == nkAccQuoted:
|
||||
let s = qualifiedLookUp(c.c, n, {})
|
||||
|
|
@ -469,7 +482,7 @@ proc semTemplateDef(c: PContext, n: PNode): PNode =
|
|||
s = semIdentVis(c, skTemplate, n.sons[0], {})
|
||||
styleCheckDef(s)
|
||||
# check parameter list:
|
||||
s.scope = c.currentScope
|
||||
#s.scope = c.currentScope
|
||||
pushOwner(s)
|
||||
openScope(c)
|
||||
n.sons[namePos] = newSymNode(s, n.sons[namePos].info)
|
||||
|
|
@ -485,6 +498,11 @@ proc semTemplateDef(c: PContext, n: PNode): PNode =
|
|||
# process parameters:
|
||||
if n.sons[paramsPos].kind != nkEmpty:
|
||||
semParamList(c, n.sons[paramsPos], gp, s)
|
||||
# a template's parameters are not gensym'ed even if that was originally the
|
||||
# case as we determine whether it's a template parameter in the template
|
||||
# body by the absence of the sfGenSym flag:
|
||||
for i in 1 .. s.typ.n.len-1:
|
||||
s.typ.n.sons[i].sym.flags.excl sfGenSym
|
||||
if sonsLen(gp) > 0:
|
||||
if n.sons[genericParamsPos].kind == nkEmpty:
|
||||
# we have a list of implicit type parameters:
|
||||
|
|
@ -515,6 +533,7 @@ proc semTemplateDef(c: PContext, n: PNode): PNode =
|
|||
if s.typ.sons[0].kind notin {tyStmt, tyTypeDesc}:
|
||||
n.sons[bodyPos] = transformToExpr(n.sons[bodyPos])
|
||||
# only parameters are resolved, no type checking is performed
|
||||
semIdeForTemplateOrGeneric(c, n.sons[bodyPos], ctx.cursorInBody)
|
||||
closeScope(c)
|
||||
popOwner()
|
||||
s.ast = n
|
||||
|
|
@ -627,7 +646,7 @@ proc semPatternBody(c: var TemplCtx, n: PNode): PNode =
|
|||
for i in countup(0, sonsLen(n) - 1):
|
||||
result.sons[i] = semPatternBody(c, n.sons[i])
|
||||
else:
|
||||
# dotExpr is ambiguous: note that we explicitely allow 'x.TemplateParam',
|
||||
# dotExpr is ambiguous: note that we explicitly allow 'x.TemplateParam',
|
||||
# so we use the generic code for nkDotExpr too
|
||||
case n.kind
|
||||
of nkDotExpr, nkAccQuoted:
|
||||
|
|
|
|||
|
|
@ -214,51 +214,55 @@ proc semRange(c: PContext, n: PNode, prev: PType): PType =
|
|||
localError(n.info, errXExpectsOneTypeParam, "range")
|
||||
result = newOrPrevType(tyError, prev, c)
|
||||
|
||||
proc semArrayIndex(c: PContext, n: PNode): PType =
|
||||
if isRange(n): result = semRangeAux(c, n, nil)
|
||||
else:
|
||||
let e = semExprWithType(c, n, {efDetermineType})
|
||||
if e.typ.kind == tyFromExpr:
|
||||
result = makeRangeWithStaticExpr(c, e.typ.n)
|
||||
elif e.kind in {nkIntLit..nkUInt64Lit}:
|
||||
result = makeRangeType(c, 0, e.intVal-1, n.info, e.typ)
|
||||
elif e.kind == nkSym and e.typ.kind == tyStatic:
|
||||
if e.sym.ast != nil:
|
||||
return semArrayIndex(c, e.sym.ast)
|
||||
if not isOrdinalType(e.typ.lastSon):
|
||||
localError(n[1].info, errOrdinalTypeExpected)
|
||||
result = makeRangeWithStaticExpr(c, e)
|
||||
if c.inGenericContext > 0: result.flags.incl tfUnresolved
|
||||
elif e.kind in nkCallKinds and hasGenericArguments(e):
|
||||
if not isOrdinalType(e.typ):
|
||||
localError(n[1].info, errOrdinalTypeExpected)
|
||||
# This is an int returning call, depending on an
|
||||
# yet unknown generic param (see tgenericshardcases).
|
||||
# We are going to construct a range type that will be
|
||||
# properly filled-out in semtypinst (see how tyStaticExpr
|
||||
# is handled there).
|
||||
result = makeRangeWithStaticExpr(c, e)
|
||||
elif e.kind == nkIdent:
|
||||
result = e.typ.skipTypes({tyTypeDesc})
|
||||
else:
|
||||
let x = semConstExpr(c, e)
|
||||
if x.kind in {nkIntLit..nkUInt64Lit}:
|
||||
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 =
|
||||
var indx, base: PType
|
||||
var base: PType
|
||||
result = newOrPrevType(tyArray, prev, c)
|
||||
if sonsLen(n) == 3:
|
||||
# 3 = length(array indx base)
|
||||
if isRange(n[1]): indx = semRangeAux(c, n[1], nil)
|
||||
else:
|
||||
let e = semExprWithType(c, n.sons[1], {efDetermineType})
|
||||
if e.typ.kind == tyFromExpr:
|
||||
indx = makeRangeWithStaticExpr(c, e.typ.n)
|
||||
elif e.kind in {nkIntLit..nkUInt64Lit}:
|
||||
indx = makeRangeType(c, 0, e.intVal-1, n.info, e.typ)
|
||||
elif e.kind == nkSym and e.typ.kind == tyStatic:
|
||||
if e.sym.ast != nil: return semArray(c, e.sym.ast, nil)
|
||||
internalAssert c.inGenericContext > 0
|
||||
if not isOrdinalType(e.typ.lastSon):
|
||||
localError(n[1].info, errOrdinalTypeExpected)
|
||||
indx = makeRangeWithStaticExpr(c, e)
|
||||
indx.flags.incl tfUnresolved
|
||||
elif e.kind in nkCallKinds and hasGenericArguments(e):
|
||||
if not isOrdinalType(e.typ):
|
||||
localError(n[1].info, errOrdinalTypeExpected)
|
||||
# This is an int returning call, depending on an
|
||||
# yet unknown generic param (see tgenericshardcases).
|
||||
# We are going to construct a range type that will be
|
||||
# properly filled-out in semtypinst (see how tyStaticExpr
|
||||
# is handled there).
|
||||
indx = makeRangeWithStaticExpr(c, e)
|
||||
elif e.kind == nkIdent:
|
||||
indx = e.typ.skipTypes({tyTypeDesc})
|
||||
else:
|
||||
let x = semConstExpr(c, e)
|
||||
if x.kind in {nkIntLit..nkUInt64Lit}:
|
||||
indx = makeRangeType(c, 0, x.intVal-1, n.info,
|
||||
x.typ.skipTypes({tyTypeDesc}))
|
||||
else:
|
||||
indx = x.typ.skipTypes({tyTypeDesc})
|
||||
#localError(n[1].info, errConstExprExpected)
|
||||
var indx = semArrayIndex(c, n[1])
|
||||
addSonSkipIntLit(result, indx)
|
||||
if indx.kind == tyGenericInst: indx = lastSon(indx)
|
||||
if indx.kind notin {tyGenericParam, tyStatic, tyFromExpr}:
|
||||
if not isOrdinalType(indx):
|
||||
localError(n.sons[1].info, errOrdinalTypeExpected)
|
||||
elif enumHasHoles(indx):
|
||||
localError(n.sons[1].info, errEnumXHasHoles, indx.sym.name.s)
|
||||
localError(n.sons[1].info, errEnumXHasHoles,
|
||||
typeToString(indx.skipTypes({tyRange})))
|
||||
base = semTypeNode(c, n.sons[2], nil)
|
||||
addSonSkipIntLit(result, base)
|
||||
else:
|
||||
|
|
@ -329,16 +333,27 @@ proc semTypeIdent(c: PContext, n: PNode): PSym =
|
|||
if result.typ.kind != tyGenericParam:
|
||||
# XXX get rid of this hack!
|
||||
var oldInfo = n.info
|
||||
when defined(useNodeIds):
|
||||
let oldId = n.id
|
||||
reset(n[])
|
||||
when defined(useNodeIds):
|
||||
n.id = oldId
|
||||
n.kind = nkSym
|
||||
n.sym = result
|
||||
n.info = oldInfo
|
||||
n.typ = result.typ
|
||||
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)
|
||||
|
|
@ -444,6 +459,7 @@ proc semCaseBranchSetElem(c: PContext, t, b: PNode,
|
|||
|
||||
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:
|
||||
|
|
@ -453,8 +469,11 @@ proc semCaseBranch(c: PContext, t, branch: PNode, branchIndex: int,
|
|||
else:
|
||||
# constant sets and arrays are allowed:
|
||||
var r = semConstExpr(c, b)
|
||||
# for ``{}`` we want to trigger the type mismatch in ``fitNode``:
|
||||
if r.kind notin {nkCurly, nkBracket} or len(r) == 0:
|
||||
if r.kind in {nkCurly, nkBracket} and len(r) == 0 and sonsLen(branch)==2:
|
||||
# discarding ``{}`` and ``[]`` branches silently
|
||||
delSon(branch, 0)
|
||||
return
|
||||
elif r.kind notin {nkCurly, nkBracket} or len(r) == 0:
|
||||
checkMinSonsLen(t, 1)
|
||||
branch.sons[i] = skipConv(fitNode(c, t.sons[0].typ, r))
|
||||
inc(covered)
|
||||
|
|
@ -575,7 +594,7 @@ proc semRecordNodeAux(c: PContext, n: PNode, check: var IntSet, pos: var int,
|
|||
f.position = pos
|
||||
if (rec != nil) and ({sfImportc, sfExportc} * rec.flags != {}) and
|
||||
(f.loc.r == nil):
|
||||
f.loc.r = toRope(f.name.s)
|
||||
f.loc.r = rope(f.name.s)
|
||||
f.flags = f.flags + ({sfImportc, sfExportc} * rec.flags)
|
||||
inc(pos)
|
||||
if containsOrIncl(check, f.name.id):
|
||||
|
|
@ -606,19 +625,20 @@ proc addInheritedFieldsAux(c: PContext, check: var IntSet, pos: var int,
|
|||
inc(pos)
|
||||
else: internalError(n.info, "addInheritedFieldsAux()")
|
||||
|
||||
proc skipGenericInvocation(t: PType): PType {.inline.} =
|
||||
result = t
|
||||
if result.kind == tyGenericInvocation:
|
||||
result = result.sons[0]
|
||||
while result.kind in {tyGenericInst, tyGenericBody}:
|
||||
result = lastSon(result)
|
||||
|
||||
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):
|
||||
addInheritedFields(c, check, pos, obj.sons[0])
|
||||
addInheritedFields(c, check, pos, obj.sons[0].skipGenericInvocation)
|
||||
addInheritedFieldsAux(c, check, pos, obj.n)
|
||||
|
||||
proc skipGenericInvokation(t: PType): PType {.inline.} =
|
||||
result = t
|
||||
if result.kind == tyGenericInvokation:
|
||||
result = result.sons[0]
|
||||
if result.kind == tyGenericBody:
|
||||
result = lastSon(result)
|
||||
|
||||
proc semObjectNode(c: PContext, n: PNode, prev: PType): PType =
|
||||
if n.sonsLen == 0: return newConstraint(c, tyObject)
|
||||
var check = initIntSet()
|
||||
|
|
@ -627,14 +647,17 @@ proc semObjectNode(c: PContext, n: PNode, prev: PType): PType =
|
|||
# n.sons[0] contains the pragmas (if any). We process these later...
|
||||
checkSonsLen(n, 3)
|
||||
if n.sons[1].kind != nkEmpty:
|
||||
base = skipTypes(semTypeNode(c, n.sons[1].sons[0], nil), skipPtrs)
|
||||
var concreteBase = skipGenericInvokation(base).skipTypes(skipPtrs)
|
||||
if concreteBase.kind == tyObject and tfFinal notin concreteBase.flags:
|
||||
addInheritedFields(c, check, pos, concreteBase)
|
||||
base = skipTypesOrNil(semTypeNode(c, n.sons[1].sons[0], nil), skipPtrs)
|
||||
if base.isNil:
|
||||
localError(n.info, errIllegalRecursionInTypeX, "object")
|
||||
else:
|
||||
if concreteBase.kind != tyError:
|
||||
localError(n.sons[1].info, errInheritanceOnlyWithNonFinalObjects)
|
||||
base = nil
|
||||
var concreteBase = skipGenericInvocation(base).skipTypes(skipPtrs)
|
||||
if concreteBase.kind == tyObject and tfFinal notin concreteBase.flags:
|
||||
addInheritedFields(c, check, pos, concreteBase)
|
||||
else:
|
||||
if concreteBase.kind != tyError:
|
||||
localError(n.sons[1].info, errInheritanceOnlyWithNonFinalObjects)
|
||||
base = nil
|
||||
if n.kind != nkObjectTy: internalError(n.info, "semObjectNode")
|
||||
result = newOrPrevType(tyObject, prev, c)
|
||||
rawAddSon(result, base)
|
||||
|
|
@ -659,25 +682,23 @@ proc findEnforcedStaticType(t: PType): PType =
|
|||
if t != nil: return t
|
||||
|
||||
proc addParamOrResult(c: PContext, param: PSym, kind: TSymKind) =
|
||||
template addDecl(x) =
|
||||
if sfGenSym notin x.flags: addDecl(c, x)
|
||||
|
||||
if kind == skMacro:
|
||||
let staticType = findEnforcedStaticType(param.typ)
|
||||
if staticType != nil:
|
||||
var a = copySym(param)
|
||||
a.typ = staticType.base
|
||||
addDecl(a)
|
||||
addDecl(c, a)
|
||||
elif param.typ.kind == tyTypeDesc:
|
||||
addDecl(param)
|
||||
addDecl(c, param)
|
||||
else:
|
||||
# within a macro, every param has the type PNimrodNode!
|
||||
let nn = getSysSym"PNimrodNode"
|
||||
# within a macro, every param has the type NimNode!
|
||||
let nn = if getCompilerProc("NimNode") != nil: getSysSym"NimNode"
|
||||
else: getSysSym"PNimrodNode"
|
||||
var a = copySym(param)
|
||||
a.typ = nn.typ
|
||||
addDecl(a)
|
||||
addDecl(c, a)
|
||||
else:
|
||||
addDecl(param)
|
||||
if sfGenSym notin param.flags: addDecl(c, param)
|
||||
|
||||
let typedescId = getIdent"typedesc"
|
||||
|
||||
|
|
@ -769,18 +790,25 @@ proc liftParamType(c: PContext, procKind: TSymKind, genericParams: PNode,
|
|||
@[newTypeS(paramType.kind, c)])
|
||||
result = addImplicitGeneric(typ)
|
||||
else:
|
||||
for i in 0 .. <paramType.sons.len:
|
||||
for i in 0 .. <paramType.len:
|
||||
if paramType.sons[i] == paramType:
|
||||
globalError(info, errIllegalRecursionInTypeX, typeToString(paramType))
|
||||
var lifted = liftingWalk(paramType.sons[i])
|
||||
if lifted != nil:
|
||||
paramType.sons[i] = lifted
|
||||
result = paramType
|
||||
|
||||
of tyGenericBody:
|
||||
result = newTypeS(tyGenericInvokation, c)
|
||||
result = newTypeS(tyGenericInvocation, c)
|
||||
result.rawAddSon(paramType)
|
||||
|
||||
for i in 0 .. paramType.sonsLen - 2:
|
||||
result.rawAddSon newTypeS(tyAnything, c)
|
||||
# result.rawAddSon(copyType(paramType.sons[i], getCurrOwner(), true))
|
||||
if paramType.sons[i].kind == tyStatic:
|
||||
var x = copyNode(ast.emptyNode)
|
||||
x.typ = paramType.sons[i]
|
||||
result.rawAddSon makeTypeFromExpr(c, x) # aka 'tyUnknown'
|
||||
else:
|
||||
result.rawAddSon newTypeS(tyAnything, c)
|
||||
|
||||
if paramType.lastSon.kind == tyUserTypeClass:
|
||||
result.kind = tyUserTypeClassInst
|
||||
|
|
@ -807,7 +835,7 @@ proc liftParamType(c: PContext, procKind: TSymKind, genericParams: PNode,
|
|||
cp.kind = tyUserTypeClassInst
|
||||
return addImplicitGeneric(cp)
|
||||
|
||||
for i in 1 .. (paramType.sons.len - 2):
|
||||
for i in 1 .. paramType.len-2:
|
||||
var lifted = liftingWalk(paramType.sons[i])
|
||||
if lifted != nil:
|
||||
paramType.sons[i] = lifted
|
||||
|
|
@ -819,8 +847,8 @@ proc liftParamType(c: PContext, procKind: TSymKind, genericParams: PNode,
|
|||
result = liftBody
|
||||
result.shouldHaveMeta
|
||||
|
||||
of tyGenericInvokation:
|
||||
for i in 1 .. <paramType.sonsLen:
|
||||
of tyGenericInvocation:
|
||||
for i in 1 .. <paramType.len:
|
||||
let lifted = liftingWalk(paramType.sons[i])
|
||||
if lifted != nil: paramType.sons[i] = lifted
|
||||
when false:
|
||||
|
|
@ -853,25 +881,25 @@ proc semParamType(c: PContext, n: PNode, constraint: var PNode): PType =
|
|||
else:
|
||||
result = semTypeNode(c, n, nil)
|
||||
|
||||
proc semProcTypeNode(c: PContext, n, genericParams: PNode,
|
||||
prev: PType, kind: TSymKind; isType=false): PType =
|
||||
# for historical reasons (code grows) this is invoked for parameter
|
||||
# lists too and then 'isType' is false.
|
||||
var
|
||||
res: PNode
|
||||
cl: IntSet
|
||||
checkMinSonsLen(n, 1)
|
||||
proc newProcType(c: PContext; info: TLineInfo; prev: PType = nil): PType =
|
||||
result = newOrPrevType(tyProc, prev, c)
|
||||
result.callConv = lastOptionEntry(c).defaultCC
|
||||
result.n = newNodeI(nkFormalParams, n.info)
|
||||
if genericParams != nil and sonsLen(genericParams) == 0:
|
||||
cl = initIntSet()
|
||||
result.n = newNodeI(nkFormalParams, info)
|
||||
rawAddSon(result, nil) # return type
|
||||
# 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:
|
||||
res = newNodeI(nkEffectList, n.info)
|
||||
addSon(result.n, res)
|
||||
addSon(result.n, newNodeI(nkEffectList, info))
|
||||
|
||||
proc semProcTypeNode(c: PContext, n, genericParams: PNode,
|
||||
prev: PType, kind: TSymKind; isType=false): PType =
|
||||
# for historical reasons (code grows) this is invoked for parameter
|
||||
# lists too and then 'isType' is false.
|
||||
var cl: IntSet
|
||||
checkMinSonsLen(n, 1)
|
||||
result = newProcType(c, n.info, prev)
|
||||
if genericParams != nil and sonsLen(genericParams) == 0:
|
||||
cl = initIntSet()
|
||||
var check = initIntSet()
|
||||
var counter = 0
|
||||
for i in countup(1, n.len - 1):
|
||||
|
|
@ -896,7 +924,7 @@ proc semProcTypeNode(c: PContext, n, genericParams: PNode,
|
|||
|
||||
if hasDefault:
|
||||
def = semExprWithType(c, a.sons[length-1])
|
||||
# check type compability between def.typ and typ:
|
||||
# check type compatibility between def.typ and typ:
|
||||
if typ == nil:
|
||||
typ = def.typ
|
||||
elif def != nil:
|
||||
|
|
@ -908,6 +936,8 @@ proc semProcTypeNode(c: PContext, n, genericParams: PNode,
|
|||
if not hasType and not hasDefault:
|
||||
if isType: localError(a.info, "':' expected")
|
||||
let tdef = if kind in {skTemplate, skMacro}: tyExpr else: tyAnything
|
||||
if tdef == tyAnything:
|
||||
message(a.info, warnTypelessParam, renderTree(n))
|
||||
typ = newTypeS(tdef, c)
|
||||
|
||||
if skipTypes(typ, {tyGenericInst}).kind == tyEmpty: continue
|
||||
|
|
@ -933,7 +963,7 @@ proc semProcTypeNode(c: PContext, n, genericParams: PNode,
|
|||
r = semTypeNode(c, n.sons[0], nil)
|
||||
elif kind == skIterator:
|
||||
# XXX This is special magic we should likely get rid of
|
||||
r = newTypeS(tyAnything, c)
|
||||
r = newTypeS(tyExpr, c)
|
||||
|
||||
if r != nil:
|
||||
# turn explicit 'void' return type into 'nil' because the rest of the
|
||||
|
|
@ -953,7 +983,7 @@ proc semProcTypeNode(c: PContext, n, genericParams: PNode,
|
|||
# we don't need to change the return type to iter[T]
|
||||
if not r.isInlineIterator: r = newTypeWithSons(c, tyIter, @[r])
|
||||
result.sons[0] = r
|
||||
res.typ = r
|
||||
result.n.typ = r
|
||||
|
||||
if genericParams != nil:
|
||||
for n in genericParams:
|
||||
|
|
@ -985,7 +1015,7 @@ proc semBlockType(c: PContext, n: PNode, prev: PType): PType =
|
|||
closeScope(c)
|
||||
dec(c.p.nestedBlockCounter)
|
||||
|
||||
proc semGenericParamInInvokation(c: PContext, n: PNode): PType =
|
||||
proc semGenericParamInInvocation(c: PContext, n: PNode): PType =
|
||||
result = semTypeNode(c, n, nil)
|
||||
|
||||
proc semGeneric(c: PContext, n: PNode, s: PSym, prev: PType): PType =
|
||||
|
|
@ -998,7 +1028,7 @@ proc semGeneric(c: PContext, n: PNode, s: PSym, prev: PType): PType =
|
|||
if t.kind == tyCompositeTypeClass and t.base.kind == tyGenericBody:
|
||||
t = t.base
|
||||
|
||||
result = newOrPrevType(tyGenericInvokation, prev, c)
|
||||
result = newOrPrevType(tyGenericInvocation, prev, c)
|
||||
addSonSkipIntLit(result, t)
|
||||
|
||||
template addToResult(typ) =
|
||||
|
|
@ -1009,20 +1039,20 @@ proc semGeneric(c: PContext, n: PNode, s: PSym, prev: PType): PType =
|
|||
|
||||
if t.kind == tyForward:
|
||||
for i in countup(1, sonsLen(n)-1):
|
||||
var elem = semGenericParamInInvokation(c, n.sons[i])
|
||||
var elem = semGenericParamInInvocation(c, n.sons[i])
|
||||
addToResult(elem)
|
||||
return
|
||||
elif t.kind != tyGenericBody:
|
||||
#we likely got code of the form TypeA[TypeB] where TypeA is
|
||||
#not generic.
|
||||
# we likely got code of the form TypeA[TypeB] where TypeA is
|
||||
# not generic.
|
||||
localError(n.info, errNoGenericParamsAllowedForX, s.name.s)
|
||||
return newOrPrevType(tyError, prev, c)
|
||||
else:
|
||||
var m = newCandidate(c, t)
|
||||
matches(c, n, copyTree(n), m)
|
||||
|
||||
if m.state != csMatch:
|
||||
var err = "cannot instantiate " & typeToString(t) & "\n" &
|
||||
if m.state != csMatch and not m.typedescMatched:
|
||||
let err = "cannot instantiate " & typeToString(t) & "\n" &
|
||||
"got: (" & describeArgs(c, n) & ")\n" &
|
||||
"but expected: (" & describeArgs(c, t.n, 0) & ")"
|
||||
localError(n.info, errGenerated, err)
|
||||
|
|
@ -1031,9 +1061,14 @@ proc semGeneric(c: PContext, n: PNode, s: PSym, prev: PType): PType =
|
|||
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)
|
||||
var typ = m.call[i].typ
|
||||
if typ.kind == tyTypeDesc and typ.sons[0].kind == tyNone:
|
||||
isConcrete = false
|
||||
addToResult(typ)
|
||||
else:
|
||||
typ = typ.skipTypes({tyTypeDesc})
|
||||
if containsGenericType(typ): isConcrete = false
|
||||
addToResult(typ)
|
||||
|
||||
if isConcrete:
|
||||
if s.ast == nil and s.typ.kind != tyCompositeTypeClass:
|
||||
|
|
@ -1059,6 +1094,8 @@ proc freshType(res, prev: PType): PType {.inline.} =
|
|||
|
||||
proc semTypeClass(c: PContext, n: PNode, prev: PType): PType =
|
||||
# if n.sonsLen == 0: return newConstraint(c, tyTypeClass)
|
||||
if nfBase2 in n.flags:
|
||||
message(n.info, warnDeprecated, "use 'concept' instead; 'generic'")
|
||||
result = newOrPrevType(tyUserTypeClass, prev, c)
|
||||
result.n = n
|
||||
|
||||
|
|
@ -1101,9 +1138,7 @@ proc semTypeNode(c: PContext, n: PNode, prev: PType): PType =
|
|||
of nkPar:
|
||||
if sonsLen(n) == 1: result = semTypeNode(c, n.sons[0], prev)
|
||||
else:
|
||||
# XXX support anon tuple here
|
||||
localError(n.info, errTypeExpected)
|
||||
result = newOrPrevType(tyError, prev, c)
|
||||
result = semAnonTuple(c, n, prev)
|
||||
of nkCallKinds:
|
||||
if isRange(n):
|
||||
result = semRangeAux(c, n, prev)
|
||||
|
|
@ -1129,7 +1164,8 @@ proc semTypeNode(c: PContext, n: PNode, prev: PType): PType =
|
|||
case n.len
|
||||
of 3:
|
||||
result = semTypeNode(c, n.sons[1], prev)
|
||||
if result.kind in NilableTypes and n.sons[2].kind == nkNilLit:
|
||||
if result.skipTypes({tyGenericInst}).kind in NilableTypes+GenericTypes and
|
||||
n.sons[2].kind == nkNilLit:
|
||||
result = freshType(result, prev)
|
||||
result.flags.incl(tfNotNil)
|
||||
else:
|
||||
|
|
@ -1143,6 +1179,10 @@ proc semTypeNode(c: PContext, n: PNode, prev: PType): PType =
|
|||
result = semAnyRef(c, n, tyPtr, prev)
|
||||
elif op.id == ord(wRef):
|
||||
result = semAnyRef(c, n, tyRef, prev)
|
||||
elif op.id == ord(wType):
|
||||
checkSonsLen(n, 2)
|
||||
let typExpr = semExprWithType(c, n.sons[1], {efInTypeof})
|
||||
result = typExpr.typ.skipTypes({tyIter})
|
||||
else:
|
||||
result = semTypeExpr(c, n)
|
||||
of nkWhenStmt:
|
||||
|
|
@ -1172,12 +1212,12 @@ proc semTypeNode(c: PContext, n: PNode, prev: PType): PType =
|
|||
var typeExpr = semExpr(c, n)
|
||||
if typeExpr.typ.kind != tyTypeDesc:
|
||||
localError(n.info, errTypeExpected)
|
||||
return errorType(c)
|
||||
result = typeExpr.typ.base
|
||||
if result.isMetaType:
|
||||
var toBind = initIntSet()
|
||||
var preprocessed = semGenericStmt(c, n, {}, toBind)
|
||||
return makeTypeFromExpr(c, preprocessed)
|
||||
result = errorType(c)
|
||||
else:
|
||||
result = typeExpr.typ.base
|
||||
if result.isMetaType:
|
||||
var preprocessed = semGenericStmt(c, n)
|
||||
result = makeTypeFromExpr(c, preprocessed.copyTree)
|
||||
of nkIdent, nkAccQuoted:
|
||||
var s = semTypeIdent(c, n)
|
||||
if s.typ == nil:
|
||||
|
|
@ -1210,6 +1250,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)
|
||||
|
|
@ -1248,6 +1289,7 @@ proc semTypeNode(c: PContext, n: PNode, prev: PType): PType =
|
|||
else:
|
||||
localError(n.info, errTypeExpected)
|
||||
result = newOrPrevType(tyError, prev, c)
|
||||
n.typ = result
|
||||
|
||||
proc setMagicType(m: PSym, kind: TTypeKind, size: int) =
|
||||
m.typ.kind = kind
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
#
|
||||
#
|
||||
# The Nim Compiler
|
||||
# (c) Copyright 2014 Andreas Rumpf
|
||||
# (c) Copyright 2015 Andreas Rumpf
|
||||
#
|
||||
# See the file "copying.txt", included in this
|
||||
# distribution, for details about the copyright.
|
||||
|
|
@ -16,9 +16,9 @@ const
|
|||
|
||||
proc sharedPtrCheck(info: TLineInfo, t: PType) =
|
||||
if t.kind == tyPtr and t.len > 1:
|
||||
if t.sons[0].sym.magic in {mShared, mGuarded}:
|
||||
if t.sons[0].sym.magic == mShared:
|
||||
incl(t.flags, tfShared)
|
||||
if t.sons[0].sym.magic == mGuarded: incl(t.flags, tfGuarded)
|
||||
#if t.sons[0].sym.magic == mGuarded: incl(t.flags, tfGuarded)
|
||||
if tfHasGCedMem in t.flags or t.isGCedMem:
|
||||
localError(info, errGenerated,
|
||||
"shared memory may not refer to GC'ed thread local memory")
|
||||
|
|
@ -61,7 +61,7 @@ proc searchInstTypes*(key: PType): PType =
|
|||
if inst.sons.len < key.sons.len:
|
||||
# XXX: This happens for prematurely cached
|
||||
# types such as TChannel[empty]. Why?
|
||||
# See the notes for PActor in handleGenericInvokation
|
||||
# See the notes for PActor in handleGenericInvocation
|
||||
return
|
||||
block matchType:
|
||||
for j in 1 .. high(key.sons):
|
||||
|
|
@ -89,6 +89,7 @@ type
|
|||
info*: TLineInfo
|
||||
allowMetaTypes*: bool # allow types such as seq[Number]
|
||||
# i.e. the result contains unresolved generics
|
||||
skipTypedesc*: bool # wether we should skip typeDescs
|
||||
|
||||
proc replaceTypeVarsTAux(cl: var TReplTypeVars, t: PType): PType
|
||||
proc replaceTypeVarsS(cl: var TReplTypeVars, s: PSym): PSym
|
||||
|
|
@ -101,7 +102,6 @@ template checkMetaInvariants(cl: TReplTypeVars, t: PType) =
|
|||
echo "UNEXPECTED META ", t.id, " ", instantiationInfo(-1)
|
||||
debug t
|
||||
writeStackTrace()
|
||||
quit 1
|
||||
|
||||
proc replaceTypeVarsT*(cl: var TReplTypeVars, t: PType): PType =
|
||||
result = replaceTypeVarsTAux(cl, t)
|
||||
|
|
@ -224,7 +224,7 @@ proc lookupTypeVar(cl: var TReplTypeVars, t: PType): PType =
|
|||
result = errorType(cl.c)
|
||||
# In order to prevent endless recursions, we must remember
|
||||
# this bad lookup and replace it with errorType everywhere.
|
||||
# These code paths are only active in nimrod check
|
||||
# These code paths are only active in "nim check"
|
||||
idTablePut(cl.typeMap, t, result)
|
||||
elif result.kind == tyGenericParam and not cl.allowMetaTypes:
|
||||
internalError(cl.info, "substitution with generic parameter")
|
||||
|
|
@ -233,10 +233,12 @@ proc instCopyType*(cl: var TReplTypeVars, t: PType): PType =
|
|||
# XXX: relying on allowMetaTypes is a kludge
|
||||
result = copyType(t, t.owner, cl.allowMetaTypes)
|
||||
result.flags.incl tfFromGeneric
|
||||
result.flags.excl tfInstClearedFlags
|
||||
if not (t.kind in tyMetaTypes or
|
||||
(t.kind == tyStatic and t.n == nil)):
|
||||
result.flags.excl tfInstClearedFlags
|
||||
|
||||
proc handleGenericInvokation(cl: var TReplTypeVars, t: PType): PType =
|
||||
# tyGenericInvokation[A, tyGenericInvokation[A, B]]
|
||||
proc handleGenericInvocation(cl: var TReplTypeVars, t: PType): PType =
|
||||
# tyGenericInvocation[A, tyGenericInvocation[A, B]]
|
||||
# is difficult to handle:
|
||||
var body = t.sons[0]
|
||||
if body.kind != tyGenericBody: internalError(cl.info, "no generic body")
|
||||
|
|
@ -246,7 +248,7 @@ proc handleGenericInvokation(cl: var TReplTypeVars, t: PType): PType =
|
|||
result = PType(idTableGet(cl.localCache, t))
|
||||
else:
|
||||
result = searchInstTypes(t)
|
||||
if result != nil: return
|
||||
if result != nil and eqTypeFlags*result.flags == eqTypeFlags*t.flags: return
|
||||
for i in countup(1, sonsLen(t) - 1):
|
||||
var x = t.sons[i]
|
||||
if x.kind == tyGenericParam:
|
||||
|
|
@ -261,7 +263,7 @@ proc handleGenericInvokation(cl: var TReplTypeVars, t: PType): PType =
|
|||
if header != t:
|
||||
# search again after first pass:
|
||||
result = searchInstTypes(header)
|
||||
if result != nil: return
|
||||
if result != nil and eqTypeFlags*result.flags == eqTypeFlags*t.flags: return
|
||||
else:
|
||||
header = instCopyType(cl, t)
|
||||
|
||||
|
|
@ -277,9 +279,11 @@ proc handleGenericInvokation(cl: var TReplTypeVars, t: PType): PType =
|
|||
else:
|
||||
idTablePut(cl.localCache, t, result)
|
||||
|
||||
let oldSkipTypedesc = cl.skipTypedesc
|
||||
cl.skipTypedesc = true
|
||||
for i in countup(1, sonsLen(t) - 1):
|
||||
var x = replaceTypeVarsT(cl, t.sons[i])
|
||||
assert x.kind != tyGenericInvokation
|
||||
assert x.kind != tyGenericInvocation
|
||||
header.sons[i] = x
|
||||
propagateToOwner(header, x)
|
||||
idTablePut(cl.typeMap, body.sons[i-1], x)
|
||||
|
|
@ -290,13 +294,14 @@ proc handleGenericInvokation(cl: var TReplTypeVars, t: PType): PType =
|
|||
rawAddSon(result, header.sons[i])
|
||||
|
||||
var newbody = replaceTypeVarsT(cl, lastSon(body))
|
||||
cl.skipTypedesc = oldSkipTypedesc
|
||||
newbody.flags = newbody.flags + (t.flags + body.flags - tfInstClearedFlags)
|
||||
result.flags = result.flags + newbody.flags - tfInstClearedFlags
|
||||
# This is actually wrong: tgeneric_closure fails with this line:
|
||||
#newbody.callConv = body.callConv
|
||||
# This type may be a generic alias and we want to resolve it here.
|
||||
# One step is enough, because the recursive nature of
|
||||
# handleGenericInvokation will handle the alias-to-alias-to-alias case
|
||||
# handleGenericInvocation will handle the alias-to-alias-to-alias case
|
||||
if newbody.isGenericAlias: newbody = newbody.skipGenericAlias
|
||||
rawAddSon(result, newbody)
|
||||
checkPartialConstructedType(cl.info, newbody)
|
||||
|
|
@ -304,7 +309,13 @@ proc handleGenericInvokation(cl: var TReplTypeVars, t: PType): PType =
|
|||
if dc != nil and sfFromGeneric notin newbody.deepCopy.flags:
|
||||
# 'deepCopy' needs to be instantiated for
|
||||
# generics *when the type is constructed*:
|
||||
newbody.deepCopy = cl.c.instDeepCopy(cl.c, dc, result, cl.info)
|
||||
newbody.deepCopy = cl.c.instTypeBoundOp(cl.c, dc, result, cl.info,
|
||||
attachedDeepCopy)
|
||||
let asgn = newbody.assignment
|
||||
if asgn != nil and sfFromGeneric notin asgn.flags:
|
||||
# '=' needs to be instantiated for generics when the type is constructed:
|
||||
newbody.assignment = cl.c.instTypeBoundOp(cl.c, asgn, result, cl.info,
|
||||
attachedAsgn)
|
||||
|
||||
proc eraseVoidParams*(t: PType) =
|
||||
# transform '(): void' into '()' because old parts of the compiler really
|
||||
|
|
@ -342,6 +353,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:
|
||||
|
|
@ -360,8 +373,8 @@ proc replaceTypeVarsTAux(cl: var TReplTypeVars, t: PType): PType =
|
|||
if lookup != nil: return lookup
|
||||
|
||||
case t.kind
|
||||
of tyGenericInvokation:
|
||||
result = handleGenericInvokation(cl, t)
|
||||
of tyGenericInvocation:
|
||||
result = handleGenericInvocation(cl, t)
|
||||
|
||||
of tyGenericBody:
|
||||
localError(cl.info, errCannotInstantiateX, typeToString(t))
|
||||
|
|
@ -370,8 +383,10 @@ proc replaceTypeVarsTAux(cl: var TReplTypeVars, t: PType): PType =
|
|||
|
||||
of tyFromExpr:
|
||||
if cl.allowMetaTypes: return
|
||||
assert t.n.typ != t
|
||||
var n = prepareNode(cl, t.n)
|
||||
n = cl.c.semConstExpr(cl.c, n)
|
||||
if n.kind != nkEmpty:
|
||||
n = cl.c.semConstExpr(cl.c, n)
|
||||
if n.typ.kind == tyTypeDesc:
|
||||
# XXX: sometimes, chained typedescs enter here.
|
||||
# It may be worth investigating why this is happening,
|
||||
|
|
@ -390,15 +405,14 @@ proc replaceTypeVarsTAux(cl: var TReplTypeVars, t: PType): PType =
|
|||
else:
|
||||
result = n.typ
|
||||
|
||||
of tyInt:
|
||||
of tyInt, tyFloat:
|
||||
result = skipIntLit(t)
|
||||
# XXX now there are also float literals
|
||||
|
||||
of tyTypeDesc:
|
||||
let lookup = PType(idTableGet(cl.typeMap, t)) # lookupTypeVar(cl, t)
|
||||
if lookup != nil:
|
||||
result = lookup
|
||||
if tfUnresolved in t.flags: result = result.base
|
||||
if tfUnresolved in t.flags or cl.skipTypedesc: result = result.base
|
||||
elif t.sons[0].kind != tyNone:
|
||||
result = makeTypeDesc(cl.c, replaceTypeVarsT(cl, t.sons[0]))
|
||||
|
||||
|
|
@ -406,15 +420,23 @@ proc replaceTypeVarsTAux(cl: var TReplTypeVars, t: PType): PType =
|
|||
result = t
|
||||
|
||||
of tyGenericInst:
|
||||
result = PType(idTableGet(cl.localCache, t))
|
||||
if result != nil: return result
|
||||
result = instCopyType(cl, t)
|
||||
idTablePut(cl.localCache, t, result)
|
||||
for i in 1 .. <result.sonsLen:
|
||||
result.sons[i] = replaceTypeVarsT(cl, result.sons[i])
|
||||
propagateToOwner(result, result.lastSon)
|
||||
|
||||
else:
|
||||
if containsGenericType(t):
|
||||
#if not cl.allowMetaTypes:
|
||||
result = PType(idTableGet(cl.localCache, t))
|
||||
if result != nil: return result
|
||||
result = instCopyType(cl, t)
|
||||
result.size = -1 # needs to be recomputed
|
||||
#if not cl.allowMetaTypes:
|
||||
idTablePut(cl.localCache, t, result)
|
||||
|
||||
for i in countup(0, sonsLen(result) - 1):
|
||||
if result.sons[i] != nil:
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
#
|
||||
#
|
||||
# The Nim Compiler
|
||||
# (c) Copyright 2012 Andreas Rumpf
|
||||
# (c) Copyright 2015 Andreas Rumpf
|
||||
#
|
||||
# See the file "copying.txt", included in this
|
||||
# distribution, for details about the copyright.
|
||||
|
|
@ -14,7 +14,7 @@ import
|
|||
extccomp, strutils, os, platform, parseopt
|
||||
|
||||
when useCaas:
|
||||
import sockets
|
||||
import net
|
||||
|
||||
# We cache modules and the dependency graph. However, we don't check for
|
||||
# file changes but expect the client to tell us about them, otherwise the
|
||||
|
|
@ -33,7 +33,12 @@ proc processCmdLine*(pass: TCmdLinePass, cmd: string) =
|
|||
parseopt.next(p)
|
||||
case p.kind
|
||||
of cmdEnd: break
|
||||
of cmdLongoption, cmdShortOption: processSwitch(pass, p)
|
||||
of cmdLongoption, cmdShortOption:
|
||||
if p.key == " ":
|
||||
p.key = "-"
|
||||
if processArgument(pass, p, argsCount): break
|
||||
else:
|
||||
processSwitch(pass, p)
|
||||
of cmdArgument:
|
||||
if processArgument(pass, p, argsCount): break
|
||||
if pass == passCmd2:
|
||||
|
|
@ -45,8 +50,6 @@ proc serve*(action: proc (){.nimcall.}) =
|
|||
curCaasCmd = cmd
|
||||
processCmdLine(passCmd2, cmd)
|
||||
action()
|
||||
gDirtyBufferIdx = 0
|
||||
gDirtyOriginalIdx = 0
|
||||
gErrorCounter = 0
|
||||
|
||||
let typ = getConfigVar("server.type")
|
||||
|
|
@ -61,14 +64,16 @@ proc serve*(action: proc (){.nimcall.}) =
|
|||
|
||||
of "tcp", "":
|
||||
when useCaas:
|
||||
var server = socket()
|
||||
if server == invalidSocket: raiseOSError(osLastError())
|
||||
var server = newSocket()
|
||||
let p = getConfigVar("server.port")
|
||||
let port = if p.len > 0: parseInt(p).Port else: 6000.Port
|
||||
server.bindAddr(port, getConfigVar("server.address"))
|
||||
var inp = "".TaintedString
|
||||
server.listen()
|
||||
new(stdoutSocket)
|
||||
var stdoutSocket = newSocket()
|
||||
msgs.writelnHook = proc (line: string) =
|
||||
stdoutSocket.send(line & "\c\L")
|
||||
|
||||
while true:
|
||||
accept(server, stdoutSocket)
|
||||
stdoutSocket.readLine(inp)
|
||||
|
|
@ -76,7 +81,7 @@ proc serve*(action: proc (){.nimcall.}) =
|
|||
stdoutSocket.send("\c\L")
|
||||
stdoutSocket.close()
|
||||
else:
|
||||
quit "server.type not supported; compiler built without caas support"
|
||||
msgQuit "server.type not supported; compiler built without caas support"
|
||||
else:
|
||||
echo "Invalid server.type:", typ
|
||||
quit 1
|
||||
msgQuit 1
|
||||
|
|
|
|||
|
|
@ -39,12 +39,14 @@ type
|
|||
bindings*: TIdTable # maps types to types
|
||||
baseTypeMatch: bool # needed for conversions from T to openarray[T]
|
||||
# for example
|
||||
proxyMatch*: bool # to prevent instantiations
|
||||
fauxMatch*: TTypeKind # the match was successful only due to the use
|
||||
# of error or wildcard (unknown) types.
|
||||
# this is used to prevent instantiations.
|
||||
genericConverter*: bool # true if a generic converter needs to
|
||||
# be instantiated
|
||||
coerceDistincts*: bool # this is an explicit coercion that can strip away
|
||||
# a distrinct type
|
||||
typedescMatched: bool
|
||||
typedescMatched*: bool
|
||||
inheritancePenalty: int # to prefer closest father object type
|
||||
errors*: CandidateErrors # additional clarifications to be displayed to the
|
||||
# user if overload resolution fails
|
||||
|
|
@ -66,6 +68,8 @@ const
|
|||
|
||||
proc markUsed*(info: TLineInfo, s: PSym)
|
||||
|
||||
template hasFauxMatch*(c: TCandidate): bool = c.fauxMatch != tyNone
|
||||
|
||||
proc initCandidateAux(ctx: PContext,
|
||||
c: var TCandidate, callee: PType) {.inline.} =
|
||||
c.c = ctx
|
||||
|
|
@ -87,7 +91,7 @@ proc initCandidate*(ctx: PContext, c: var TCandidate, callee: PType) =
|
|||
initIdTable(c.bindings)
|
||||
|
||||
proc put(t: var TIdTable, key, val: PType) {.inline.} =
|
||||
idTablePut(t, key, val)
|
||||
idTablePut(t, key, val.skipIntLit)
|
||||
|
||||
proc initCandidate*(ctx: PContext, c: var TCandidate, callee: PSym,
|
||||
binding: PNode, calleeScope = -1) =
|
||||
|
|
@ -95,9 +99,12 @@ proc initCandidate*(ctx: PContext, c: var TCandidate, callee: PSym,
|
|||
c.calleeSym = callee
|
||||
if callee.kind in skProcKinds and calleeScope == -1:
|
||||
if callee.originatingModule == ctx.module:
|
||||
let rootSym = if sfFromGeneric notin callee.flags: callee
|
||||
else: callee.owner
|
||||
c.calleeScope = rootSym.scope.depthLevel
|
||||
c.calleeScope = 2
|
||||
var owner = callee
|
||||
while true:
|
||||
owner = owner.skipGenericOwner
|
||||
if owner.kind == skModule: break
|
||||
inc c.calleeScope
|
||||
else:
|
||||
c.calleeScope = 1
|
||||
else:
|
||||
|
|
@ -109,9 +116,12 @@ proc initCandidate*(ctx: PContext, c: var TCandidate, callee: PSym,
|
|||
for i in 1..min(sonsLen(typeParams), sonsLen(binding)-1):
|
||||
var formalTypeParam = typeParams.sons[i-1].typ
|
||||
var bound = binding[i].typ
|
||||
if bound != nil and formalTypeParam.kind != tyTypeDesc:
|
||||
internalAssert bound != nil
|
||||
if formalTypeParam.kind == tyTypeDesc:
|
||||
if bound.kind != tyTypeDesc:
|
||||
bound = makeTypeDesc(ctx, bound)
|
||||
else:
|
||||
bound = bound.skipTypes({tyTypeDesc})
|
||||
assert bound != nil
|
||||
put(c.bindings, formalTypeParam, bound)
|
||||
|
||||
proc newCandidate*(ctx: PContext, callee: PSym,
|
||||
|
|
@ -137,6 +147,7 @@ proc copyCandidate(a: var TCandidate, b: TCandidate) =
|
|||
|
||||
proc sumGeneric(t: PType): int =
|
||||
var t = t
|
||||
var isvar = 1
|
||||
while true:
|
||||
case t.kind
|
||||
of tyGenericInst, tyArray, tyRef, tyPtr, tyDistinct, tyArrayConstr,
|
||||
|
|
@ -144,38 +155,48 @@ proc sumGeneric(t: PType): int =
|
|||
t = t.lastSon
|
||||
inc result
|
||||
of tyVar:
|
||||
# but do not make 'var T' more specific than 'T'!
|
||||
t = t.sons[0]
|
||||
of tyGenericInvokation, tyTuple:
|
||||
result = ord(t.kind == tyGenericInvokation)
|
||||
inc result
|
||||
inc isvar
|
||||
of tyGenericInvocation, tyTuple:
|
||||
result += ord(t.kind == tyGenericInvocation)
|
||||
for i in 0 .. <t.len: result += t.sons[i].sumGeneric
|
||||
break
|
||||
of tyProc:
|
||||
# proc matche proc better than 'stmt' to disambiguate 'spawn'
|
||||
return 1
|
||||
of tyGenericParam, tyExpr, tyStatic, tyStmt, tyTypeDesc: break
|
||||
else: return 0
|
||||
of tyBool, tyChar, tyEnum, tyObject, tyProc, tyPointer,
|
||||
tyString, tyCString, tyInt..tyInt64, tyFloat..tyFloat128,
|
||||
tyUInt..tyUInt64:
|
||||
return isvar
|
||||
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:
|
||||
proc betterThan(a, b: PType): bool {.inline.} = a.sumGeneric > b.sumGeneric
|
||||
|
||||
if a.len > 1 and b.len > 1:
|
||||
let aa = a.sons[1].sumGeneric
|
||||
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):
|
||||
if not a.sons[i].betterThan(b.sons[i]): return 0
|
||||
# a must be longer or of the same length as b:
|
||||
result = a.len - b.len
|
||||
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
|
||||
|
||||
proc cmpCandidates*(a, b: TCandidate): int =
|
||||
result = a.exactMatches - b.exactMatches
|
||||
|
|
@ -198,10 +219,11 @@ proc cmpCandidates*(a, b: TCandidate): int =
|
|||
|
||||
proc writeMatches*(c: TCandidate) =
|
||||
writeln(stdout, "exact matches: " & $c.exactMatches)
|
||||
writeln(stdout, "subtype matches: " & $c.subtypeMatches)
|
||||
writeln(stdout, "conv matches: " & $c.convMatches)
|
||||
writeln(stdout, "intconv matches: " & $c.intConvMatches)
|
||||
writeln(stdout, "generic matches: " & $c.genericMatches)
|
||||
writeln(stdout, "subtype matches: " & $c.subtypeMatches)
|
||||
writeln(stdout, "intconv matches: " & $c.intConvMatches)
|
||||
writeln(stdout, "conv matches: " & $c.convMatches)
|
||||
writeln(stdout, "inheritance: " & $c.inheritancePenalty)
|
||||
|
||||
proc argTypeToString(arg: PNode; prefer: TPreferedDesc): string =
|
||||
if arg.kind in nkSymChoices:
|
||||
|
|
@ -254,7 +276,7 @@ proc concreteType(c: TCandidate, t: PType): PType =
|
|||
# example code that triggers it:
|
||||
# proc sort[T](cmp: proc(a, b: T): int = cmp)
|
||||
if result.kind != tyGenericParam: break
|
||||
of tyGenericInvokation:
|
||||
of tyGenericInvocation:
|
||||
internalError("cannot resolve type: " & typeToString(t))
|
||||
result = t
|
||||
else:
|
||||
|
|
@ -330,7 +352,8 @@ proc minRel(a, b: TTypeRelation): TTypeRelation =
|
|||
|
||||
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
|
||||
|
|
@ -349,7 +372,7 @@ proc recordRel(c: var TCandidate, f, a: PType): TTypeRelation =
|
|||
var y = a.n.sons[i].sym
|
||||
if f.kind == tyObject and typeRel(c, x.typ, y.typ) < isSubtype:
|
||||
return isNone
|
||||
if x.name.id != y.name.id and f.kind != tyTuple: return isNone
|
||||
if x.name.id != y.name.id: return isNone
|
||||
|
||||
proc allowsNil(f: PType): TTypeRelation {.inline.} =
|
||||
result = if tfNotNil notin f.flags: isSubtype else: isNone
|
||||
|
|
@ -372,17 +395,17 @@ proc procParamTypeRel(c: var TCandidate, f, a: PType): TTypeRelation =
|
|||
# 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
|
||||
|
|
@ -410,7 +433,8 @@ proc procTypeRel(c: var TCandidate, f, a: PType): TTypeRelation =
|
|||
|
||||
if tfNoSideEffect in f.flags and tfNoSideEffect notin a.flags:
|
||||
return isNone
|
||||
elif tfThread in f.flags and a.flags * {tfThread, tfNoSideEffect} == {}:
|
||||
elif tfThread in f.flags and a.flags * {tfThread, tfNoSideEffect} == {} and
|
||||
optThreadAnalysis in gGlobalOptions:
|
||||
# noSideEffect implies ``tfThread``!
|
||||
return isNone
|
||||
elif f.flags * {tfIterator} != a.flags * {tfIterator}:
|
||||
|
|
@ -424,6 +448,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:
|
||||
|
|
@ -453,7 +478,7 @@ proc matchUserTypeClass*(c: PContext, m: var TCandidate,
|
|||
openScope(c)
|
||||
inc c.inTypeClass
|
||||
|
||||
finally:
|
||||
defer:
|
||||
dec c.inTypeClass
|
||||
closeScope(c)
|
||||
|
||||
|
|
@ -462,10 +487,25 @@ proc matchUserTypeClass*(c: PContext, m: var TCandidate,
|
|||
var
|
||||
typeParamName = ff.base.sons[i-1].sym.name
|
||||
typ = ff.sons[i]
|
||||
param = newSym(skType, typeParamName, body.sym, body.sym.info)
|
||||
param: PSym
|
||||
|
||||
template paramSym(kind): expr =
|
||||
newSym(kind, typeParamName, body.sym, body.sym.info)
|
||||
|
||||
case typ.kind
|
||||
of tyStatic:
|
||||
param = paramSym skConst
|
||||
param.typ = typ.base
|
||||
param.ast = typ.n
|
||||
of tyUnknown:
|
||||
param = paramSym skVar
|
||||
param.typ = typ
|
||||
else:
|
||||
param = paramSym skType
|
||||
param.typ = makeTypeDesc(c, typ)
|
||||
|
||||
param.typ = makeTypeDesc(c, typ)
|
||||
addDecl(c, param)
|
||||
#echo "A ", param.name.s, " ", typeToString(param.typ), " ", param.kind
|
||||
|
||||
for param in body.n[0]:
|
||||
var
|
||||
|
|
@ -474,30 +514,19 @@ proc matchUserTypeClass*(c: PContext, m: var TCandidate,
|
|||
|
||||
if param.kind == nkVarTy:
|
||||
dummyName = param[0]
|
||||
dummyType = if a.kind != tyVar: makeVarType(c, a)
|
||||
else: a
|
||||
dummyType = if a.kind != tyVar: makeVarType(c, a) else: a
|
||||
else:
|
||||
dummyName = param
|
||||
dummyType = a
|
||||
|
||||
internalAssert dummyName.kind == nkIdent
|
||||
var dummyParam = newSym(skType, dummyName.ident, body.sym, body.sym.info)
|
||||
var dummyParam = newSym(skVar, dummyName.ident, body.sym, body.sym.info)
|
||||
dummyParam.typ = dummyType
|
||||
addDecl(c, dummyParam)
|
||||
#echo "B ", dummyName.ident.s, " ", typeToString(dummyType), " ", dummyparam.kind
|
||||
|
||||
var checkedBody = c.semTryExpr(c, body.n[3].copyTree)
|
||||
#m.errors = bufferedMsgs
|
||||
clearBufferedMsgs()
|
||||
if checkedBody == nil: return isNone
|
||||
|
||||
if checkedBody.kind == nkStmtList:
|
||||
for stmt in checkedBody:
|
||||
case stmt.kind
|
||||
of nkReturnStmt: discard
|
||||
of nkTypeSection: discard
|
||||
of nkConstDef: discard
|
||||
else: discard
|
||||
|
||||
return isGeneric
|
||||
|
||||
proc shouldSkipDistinct(rules: PNode, callIdent: PIdent): bool =
|
||||
|
|
@ -517,6 +546,16 @@ proc maybeSkipDistinct(t: PType, callee: PSym): PType =
|
|||
else:
|
||||
result = t
|
||||
|
||||
proc tryResolvingStaticExpr(c: var TCandidate, n: PNode): PNode =
|
||||
# Consider this example:
|
||||
# type Value[N: static[int]] = object
|
||||
# proc foo[N](a: Value[N], r: range[0..(N-1)])
|
||||
# Here, N-1 will be initially nkStaticExpr that can be evaluated only after
|
||||
# N is bound to a concrete value during the matching of the first param.
|
||||
# This proc is used to evaluate such static expressions.
|
||||
let instantiated = replaceTypesInBody(c.c, c.bindings, n)
|
||||
result = c.c.semExpr(c.c, instantiated)
|
||||
|
||||
proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation =
|
||||
# typeRel can be used to establish various relationships between types:
|
||||
#
|
||||
|
|
@ -538,7 +577,7 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation =
|
|||
assert(f != nil)
|
||||
|
||||
if f.kind == tyExpr:
|
||||
put(c.bindings, f, aOrig)
|
||||
if aOrig != nil: put(c.bindings, f, aOrig)
|
||||
return isGeneric
|
||||
|
||||
assert(aOrig != nil)
|
||||
|
|
@ -552,15 +591,14 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation =
|
|||
|
||||
if a.kind == tyGenericInst and
|
||||
skipTypes(f, {tyVar}).kind notin {
|
||||
tyGenericBody, tyGenericInvokation,
|
||||
tyGenericBody, tyGenericInvocation,
|
||||
tyGenericInst, tyGenericParam} + tyTypeClasses:
|
||||
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.} =
|
||||
|
|
@ -572,20 +610,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
|
||||
|
|
@ -616,10 +655,16 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation =
|
|||
elif skipTypes(a, {tyRange}).kind == f.kind: result = isSubtype
|
||||
of tyRange:
|
||||
if a.kind == f.kind:
|
||||
if f.base.kind == tyNone: return isGeneric
|
||||
result = typeRel(c, base(f), base(a))
|
||||
# bugfix: accept integer conversions here
|
||||
#if result < isGeneric: result = isNone
|
||||
if result notin {isNone, isGeneric}:
|
||||
# resolve any late-bound static expressions
|
||||
# that may appear in the range:
|
||||
for i in 0..1:
|
||||
if f.n[i].kind == nkStaticExpr:
|
||||
f.n.sons[i] = tryResolvingStaticExpr(c, f.n[i])
|
||||
result = typeRangeRel(f, a)
|
||||
else:
|
||||
if skipTypes(f, {tyRange}).kind == a.kind:
|
||||
|
|
@ -657,22 +702,27 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation =
|
|||
else:
|
||||
fRange = prev
|
||||
result = typeRel(c, f.sons[1], a.sons[1])
|
||||
if result < isGeneric:
|
||||
result = isNone
|
||||
elif tfUnresolved in fRange.flags and
|
||||
rangeHasStaticIf(fRange):
|
||||
# This is a range from an array instantiated with a generic
|
||||
# static param. We must extract the static param here and bind
|
||||
# it to the size of the currently supplied array.
|
||||
var
|
||||
rangeStaticT = fRange.getStaticTypeFromRange
|
||||
replacementT = newTypeWithSons(c.c, tyStatic, @[tyInt.getSysType])
|
||||
inputUpperBound = a.sons[0].n[1].intVal
|
||||
# we must correct for the off-by-one discrepancy between
|
||||
# ranges and static params:
|
||||
replacementT.n = newIntNode(nkIntLit, inputUpperBound + 1)
|
||||
put(c.bindings, rangeStaticT, replacementT)
|
||||
result = isGeneric
|
||||
if result < isGeneric: return isNone
|
||||
if rangeHasStaticIf(fRange):
|
||||
if tfUnresolved in fRange.flags:
|
||||
# This is a range from an array instantiated with a generic
|
||||
# static param. We must extract the static param here and bind
|
||||
# it to the size of the currently supplied array.
|
||||
var
|
||||
rangeStaticT = fRange.getStaticTypeFromRange
|
||||
replacementT = newTypeWithSons(c.c, tyStatic, @[tyInt.getSysType])
|
||||
inputUpperBound = a.sons[0].n[1].intVal
|
||||
# we must correct for the off-by-one discrepancy between
|
||||
# ranges and static params:
|
||||
replacementT.n = newIntNode(nkIntLit, inputUpperBound + 1)
|
||||
put(c.bindings, rangeStaticT, replacementT)
|
||||
return isGeneric
|
||||
|
||||
let len = tryResolvingStaticExpr(c, fRange.n[1])
|
||||
if len.kind == nkIntLit and len.intVal+1 == lengthOrd(a):
|
||||
return # if we get this far, the result is already good
|
||||
else:
|
||||
return isNone
|
||||
elif lengthOrd(fRange) != lengthOrd(a):
|
||||
result = isNone
|
||||
else: discard
|
||||
|
|
@ -737,11 +787,11 @@ 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):
|
||||
if f.sons[0].kind != tyGenericParam and a.sons[0].kind == tyEmpty:
|
||||
result = isSubtype
|
||||
else:
|
||||
result = typeRel(c, f.sons[0], a.sons[0])
|
||||
|
|
@ -821,8 +871,8 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation =
|
|||
result = typeRel(c, ff, aa)
|
||||
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)
|
||||
|
||||
|
|
@ -833,10 +883,10 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation =
|
|||
let ff = lastSon(f)
|
||||
if ff != nil: result = typeRel(c, ff, a)
|
||||
|
||||
of tyGenericInvokation:
|
||||
of tyGenericInvocation:
|
||||
var x = a.skipGenericAlias
|
||||
if x.kind == tyGenericInvokation or f.sons[0].kind != tyGenericBody:
|
||||
#InternalError("typeRel: tyGenericInvokation -> tyGenericInvokation")
|
||||
if x.kind == tyGenericInvocation or f.sons[0].kind != tyGenericBody:
|
||||
#InternalError("typeRel: tyGenericInvocation -> tyGenericInvocation")
|
||||
# simply no match for now:
|
||||
discard
|
||||
elif x.kind == tyGenericInst and
|
||||
|
|
@ -848,30 +898,49 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation =
|
|||
elif typeRel(c, f.sons[i], x.sons[i]) <= isSubtype: return
|
||||
result = isGeneric
|
||||
else:
|
||||
result = typeRel(c, f.sons[0], x)
|
||||
let genericBody = f.sons[0]
|
||||
result = typeRel(c, genericBody, x)
|
||||
if result != isNone:
|
||||
# see tests/generics/tgeneric3.nim for an example that triggers this
|
||||
# piece of code:
|
||||
#
|
||||
# proc internalFind[T,D](n: PNode[T,D], key: T): ref TItem[T,D]
|
||||
# proc internalPut[T,D](ANode: ref TNode[T,D], Akey: T, Avalue: D,
|
||||
# Oldvalue: var D): ref TNode[T,D]
|
||||
# var root = internalPut[int, int](nil, 312, 312, oldvalue)
|
||||
# var it1 = internalFind(root, 312) # cannot instantiate: 'D'
|
||||
#
|
||||
# we steal the generic parameters from the tyGenericBody:
|
||||
for i in countup(1, sonsLen(f) - 1):
|
||||
var x = PType(idTableGet(c.bindings, f.sons[0].sons[i - 1]))
|
||||
if x == nil or x.kind in {tyGenericInvokation, tyGenericParam}:
|
||||
var x = PType(idTableGet(c.bindings, genericBody.sons[i-1]))
|
||||
if x == nil:
|
||||
discard "maybe fine (for eg. a==tyNil)"
|
||||
elif x.kind in {tyGenericInvocation, tyGenericParam}:
|
||||
internalError("wrong instantiated type!")
|
||||
put(c.bindings, f.sons[i], x)
|
||||
else:
|
||||
put(c.bindings, f.sons[i], x)
|
||||
|
||||
of tyAnd:
|
||||
considerPreviousT:
|
||||
result = isEqual
|
||||
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:
|
||||
|
|
@ -930,7 +999,12 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation =
|
|||
else:
|
||||
internalAssert a.sons != nil and a.sons.len > 0
|
||||
c.typedescMatched = true
|
||||
result = typeRel(c, f.base, a.base)
|
||||
var aa = a
|
||||
while aa.kind in {tyTypeDesc, tyGenericParam} and
|
||||
aa.len > 0:
|
||||
aa = lastSon(aa)
|
||||
result = typeRel(c, f.base, aa)
|
||||
if result > isGeneric: result = isGeneric
|
||||
else:
|
||||
result = isNone
|
||||
else:
|
||||
|
|
@ -954,20 +1028,38 @@ 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:
|
||||
if aOrig.kind == tyStatic:
|
||||
result = typeRel(c, f.lastSon, a)
|
||||
if result != isNone: put(c.bindings, f, aOrig)
|
||||
let prev = PType(idTableGet(c.bindings, f))
|
||||
if prev == nil:
|
||||
if aOrig.kind == tyStatic:
|
||||
result = typeRel(c, f.lastSon, a)
|
||||
if result != isNone and f.n != nil:
|
||||
if not exprStructuralEquivalent(f.n, aOrig.n):
|
||||
result = isNone
|
||||
if result != isNone: put(c.bindings, f, aOrig)
|
||||
else:
|
||||
result = isNone
|
||||
elif prev.kind == tyStatic:
|
||||
if aOrig.kind == tyStatic:
|
||||
result = typeRel(c, prev.lastSon, a)
|
||||
if result != isNone and prev.n != nil:
|
||||
if not exprStructuralEquivalent(prev.n, aOrig.n):
|
||||
result = isNone
|
||||
else: result = isNone
|
||||
else:
|
||||
# XXX endless recursion?
|
||||
#result = typeRel(c, prev, aOrig)
|
||||
result = isNone
|
||||
|
||||
of tyTypeDesc:
|
||||
var prev = PType(idTableGet(c.bindings, f))
|
||||
if prev == nil:
|
||||
|
|
@ -1006,22 +1098,24 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation =
|
|||
|
||||
of tyFromExpr:
|
||||
# fix the expression, so it contains the already instantiated types
|
||||
let instantiated = replaceTypesInBody(c.c, c.bindings, f.n)
|
||||
let reevaluted = c.c.semExpr(c.c, instantiated)
|
||||
case reevaluted.typ.kind
|
||||
if f.n == nil or f.n.kind == nkEmpty: return isGeneric
|
||||
let reevaluated = tryResolvingStaticExpr(c, f.n)
|
||||
case reevaluated.typ.kind
|
||||
of tyTypeDesc:
|
||||
result = typeRel(c, a, reevaluted.typ.base)
|
||||
result = typeRel(c, a, reevaluated.typ.base)
|
||||
of tyStatic:
|
||||
result = typeRel(c, a, reevaluted.typ.base)
|
||||
if result != isNone and reevaluted.typ.n != nil:
|
||||
if not exprStructuralEquivalent(aOrig.n, reevaluted.typ.n):
|
||||
result = typeRel(c, a, reevaluated.typ.base)
|
||||
if result != isNone and reevaluated.typ.n != nil:
|
||||
if not exprStructuralEquivalent(aOrig.n, reevaluated.typ.n):
|
||||
result = isNone
|
||||
else:
|
||||
localError(f.n.info, errTypeExpected)
|
||||
result = isNone
|
||||
|
||||
of tyNone:
|
||||
if a.kind == tyNone: result = isEqual
|
||||
else:
|
||||
internalAssert false
|
||||
internalError " unknown type kind " & $f.kind
|
||||
|
||||
proc cmpTypes*(c: PContext, f, a: PType): TTypeRelation =
|
||||
var m: TCandidate
|
||||
|
|
@ -1041,7 +1135,7 @@ proc implicitConv(kind: TNodeKind, f: PType, arg: PNode, m: TCandidate,
|
|||
c: PContext): PNode =
|
||||
result = newNodeI(kind, arg.info)
|
||||
if containsGenericType(f):
|
||||
if not m.proxyMatch:
|
||||
if not m.hasFauxMatch:
|
||||
result.typ = getInstantiatedType(c, arg, m, f)
|
||||
else:
|
||||
result.typ = errorType(c)
|
||||
|
|
@ -1106,6 +1200,17 @@ proc isInlineIterator*(t: PType): bool =
|
|||
result = t.kind == tyIter or
|
||||
(t.kind == tyBuiltInTypeClass and t.base.kind == tyIter)
|
||||
|
||||
proc incMatches(m: var TCandidate; r: TTypeRelation; convMatch = 1) =
|
||||
case r
|
||||
of isConvertible, isIntConv: inc(m.convMatches, convMatch)
|
||||
of isSubtype, isSubrange: inc(m.subtypeMatches)
|
||||
of isGeneric, isInferred: inc(m.genericMatches)
|
||||
of isFromIntLit: inc(m.intConvMatches, 256)
|
||||
of isInferredConvertible:
|
||||
inc(m.convMatches)
|
||||
of isEqual: inc(m.exactMatches)
|
||||
of isNone: discard
|
||||
|
||||
proc paramTypesMatchAux(m: var TCandidate, f, argType: PType,
|
||||
argSemantized, argOrig: PNode): PNode =
|
||||
var
|
||||
|
|
@ -1127,9 +1232,8 @@ proc paramTypesMatchAux(m: var TCandidate, f, argType: PType,
|
|||
return argSemantized
|
||||
|
||||
if argType.kind == tyStatic:
|
||||
if m.callee.kind == tyGenericBody:
|
||||
result = newNodeI(nkType, argOrig.info)
|
||||
result.typ = makeTypeFromExpr(c, arg)
|
||||
if m.callee.kind == tyGenericBody and tfGenericTypeParam notin argType.flags:
|
||||
result = newNodeIT(nkType, argOrig.info, makeTypeFromExpr(c, arg))
|
||||
return
|
||||
else:
|
||||
var evaluated = c.semTryConstExpr(c, arg)
|
||||
|
|
@ -1147,17 +1251,9 @@ proc paramTypesMatchAux(m: var TCandidate, f, argType: PType,
|
|||
|
||||
if r != isNone and m.calleeSym != nil and
|
||||
m.calleeSym.kind in {skMacro, skTemplate}:
|
||||
# XXX: duplicating this is ugly, maybe we should move this
|
||||
# XXX: duplicating this is ugly, but we cannot (!) move this
|
||||
# directly into typeRel using return-like templates
|
||||
case r
|
||||
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 isEqual: inc(m.exactMatches)
|
||||
of isNone: discard
|
||||
|
||||
incMatches(m, r)
|
||||
if f.kind == tyStmt:
|
||||
return arg
|
||||
elif f.kind == tyTypeDesc:
|
||||
|
|
@ -1165,7 +1261,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)
|
||||
|
|
@ -1177,21 +1273,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)
|
||||
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:
|
||||
|
|
@ -1200,30 +1297,35 @@ 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)
|
||||
result = copyTree(arg)
|
||||
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
|
||||
if arg.typ == nil:
|
||||
result = arg
|
||||
elif skipTypes(arg.typ, abstractVar-{tyTypeDesc}).kind == tyTuple:
|
||||
result = implicitConv(nkHiddenSubConv, f, arg, m, c)
|
||||
elif arg.typ.isEmptyContainer:
|
||||
result = arg.copyTree
|
||||
result.typ = getInstantiatedType(c, arg, m, f)
|
||||
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)
|
||||
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(nkHiddenSubConv, 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 == tyProxy:
|
||||
if a.kind in {tyProxy, tyUnknown}:
|
||||
inc(m.genericMatches)
|
||||
m.proxyMatch = true
|
||||
return copyTree(arg)
|
||||
m.fauxMatch = a.kind
|
||||
return arg
|
||||
result = userConvMatch(c, m, f, a, arg)
|
||||
# check for a base type match, which supports varargs[T] without []
|
||||
# constructor in a call:
|
||||
|
|
@ -1250,7 +1352,6 @@ proc paramTypesMatch*(m: var TCandidate, f, a: PType,
|
|||
# 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
|
||||
# roll back the side effects of the unification algorithm.
|
||||
|
||||
let c = m.c
|
||||
var x, y, z: TCandidate
|
||||
initCandidate(c, x, m.callee)
|
||||
|
|
@ -1263,15 +1364,27 @@ proc paramTypesMatch*(m: var TCandidate, f, a: PType,
|
|||
for i in countup(0, sonsLen(arg) - 1):
|
||||
if arg.sons[i].sym.kind in {skProc, skMethod, skConverter}+skIterators:
|
||||
copyCandidate(z, m)
|
||||
var r = typeRel(z, f, arg.sons[i].typ)
|
||||
z.callee = arg.sons[i].typ
|
||||
z.calleeSym = arg.sons[i].sym
|
||||
#if arg.sons[i].sym.name.s == "cmp":
|
||||
# ggDebug = true
|
||||
# echo "CALLLEEEEEEEE A ", typeToString(z.callee)
|
||||
# XXX this is still all wrong: (T, T) should be 2 generic matches
|
||||
# and (int, int) 2 exact matches, etc. Essentially you cannot call
|
||||
# typeRel here and expect things to work!
|
||||
let r = typeRel(z, f, arg.sons[i].typ)
|
||||
incMatches(z, r, 2)
|
||||
#if arg.sons[i].sym.name.s == "cmp": # and arg.info.line == 606:
|
||||
# echo "M ", r, " ", arg.info, " ", typeToString(arg.sons[i].sym.typ)
|
||||
# writeMatches(z)
|
||||
if r != isNone:
|
||||
z.state = csMatch
|
||||
case x.state
|
||||
of csEmpty, csNoMatch:
|
||||
x = z
|
||||
best = i
|
||||
x.state = csMatch
|
||||
of csMatch:
|
||||
var cmp = cmpCandidates(x, z)
|
||||
let cmp = cmpCandidates(x, z)
|
||||
if cmp < 0:
|
||||
best = i
|
||||
x = z
|
||||
|
|
@ -1279,11 +1392,14 @@ proc paramTypesMatch*(m: var TCandidate, f, a: PType,
|
|||
y = z # z is as good as x
|
||||
if x.state == csEmpty:
|
||||
result = nil
|
||||
elif (y.state == csMatch) and (cmpCandidates(x, y) == 0):
|
||||
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
|
||||
result = nil
|
||||
# ambiguous: more than one symbol fits!
|
||||
# See tsymchoice_for_expr as an example. 'f.kind == tyExpr' should match
|
||||
# anyway:
|
||||
if f.kind == tyExpr: result = arg
|
||||
else: result = nil
|
||||
else:
|
||||
# only one valid interpretation found:
|
||||
markUsed(arg.info, arg.sons[best].sym)
|
||||
|
|
@ -1291,6 +1407,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) =
|
||||
if sonsLen(father) <= at: setLen(father.sons, at + 1)
|
||||
father.sons[at] = son
|
||||
|
|
@ -1302,9 +1419,12 @@ proc prepareOperand(c: PContext; formal: PType; a: PNode): PNode =
|
|||
# a.typ == nil is valid
|
||||
result = a
|
||||
elif a.typ.isNil:
|
||||
# XXX This is unsound! 'formal' can differ from overloaded routine to
|
||||
# overloaded routine!
|
||||
let flags = if formal.kind == tyIter: {efDetermineType, efWantIterator}
|
||||
elif formal.kind == tyStmt: {efDetermineType, efWantStmt}
|
||||
else: {efDetermineType}
|
||||
else: {efDetermineType, efAllowStmt}
|
||||
#elif formal.kind == tyStmt: {efDetermineType, efWantStmt}
|
||||
#else: {efDetermineType}
|
||||
result = c.semOperand(c, a, flags)
|
||||
else:
|
||||
result = a
|
||||
|
|
@ -1344,6 +1464,10 @@ proc matchesAux(c: PContext, n, nOrig: PNode,
|
|||
else:
|
||||
m.state = csNoMatch
|
||||
return
|
||||
if formal.typ.kind == tyVar:
|
||||
if not n.isLValue:
|
||||
m.state = csNoMatch
|
||||
return
|
||||
|
||||
var
|
||||
# iterates over formal parameters
|
||||
|
|
@ -1389,13 +1513,14 @@ proc matchesAux(c: PContext, n, nOrig: PNode,
|
|||
return
|
||||
checkConstraint(n.sons[a].sons[1])
|
||||
if m.baseTypeMatch:
|
||||
assert(container == nil)
|
||||
#assert(container == nil)
|
||||
container = newNodeIT(nkBracket, n.sons[a].info, arrayConstr(c, arg))
|
||||
addSon(container, arg)
|
||||
setSon(m.call, formal.position + 1, container)
|
||||
if f != formalLen - 1: container = nil
|
||||
else:
|
||||
setSon(m.call, formal.position + 1, arg)
|
||||
inc f
|
||||
else:
|
||||
# unnamed param
|
||||
if f >= formalLen:
|
||||
|
|
@ -1409,12 +1534,14 @@ proc matchesAux(c: PContext, n, nOrig: PNode,
|
|||
copyTree(n.sons[a]), m, c))
|
||||
else:
|
||||
addSon(m.call, copyTree(n.sons[a]))
|
||||
elif formal != nil:
|
||||
elif formal != nil and formal.typ.kind == tyVarargs:
|
||||
# beware of the side-effects in 'prepareOperand'! So only do it for
|
||||
# varags matching. See tests/metatype/tstatic_overloading.
|
||||
m.baseTypeMatch = false
|
||||
n.sons[a] = prepareOperand(c, formal.typ, n.sons[a])
|
||||
var arg = paramTypesMatch(m, formal.typ, n.sons[a].typ,
|
||||
n.sons[a], nOrig.sons[a])
|
||||
if (arg != nil) and m.baseTypeMatch and (container != nil):
|
||||
if arg != nil and m.baseTypeMatch and container != nil:
|
||||
addSon(container, arg)
|
||||
incrIndexType(container.typ)
|
||||
else:
|
||||
|
|
@ -1428,7 +1555,7 @@ proc matchesAux(c: PContext, n, nOrig: PNode,
|
|||
internalError(n.sons[a].info, "matches")
|
||||
return
|
||||
formal = m.callee.n.sons[f].sym
|
||||
if containsOrIncl(marker, formal.position):
|
||||
if containsOrIncl(marker, formal.position) and container.isNil:
|
||||
# already in namedParams:
|
||||
localError(n.sons[a].info, errCannotBindXTwice, formal.name.s)
|
||||
m.state = csNoMatch
|
||||
|
|
@ -1441,17 +1568,24 @@ proc matchesAux(c: PContext, n, nOrig: PNode,
|
|||
m.state = csNoMatch
|
||||
return
|
||||
if m.baseTypeMatch:
|
||||
assert(container == nil)
|
||||
container = newNodeIT(nkBracket, n.sons[a].info, arrayConstr(c, arg))
|
||||
#assert(container == nil)
|
||||
if container.isNil:
|
||||
container = newNodeIT(nkBracket, n.sons[a].info, arrayConstr(c, arg))
|
||||
else:
|
||||
incrIndexType(container.typ)
|
||||
addSon(container, arg)
|
||||
setSon(m.call, formal.position + 1,
|
||||
implicitConv(nkHiddenStdConv, formal.typ, container, m, c))
|
||||
if f != formalLen - 1: container = nil
|
||||
#if f != formalLen - 1: container = nil
|
||||
|
||||
# pick the formal from the end, so that 'x, y, varargs, z' works:
|
||||
f = max(f, formalLen - n.len + a + 1)
|
||||
else:
|
||||
setSon(m.call, formal.position + 1, arg)
|
||||
inc(f)
|
||||
container = nil
|
||||
checkConstraint(n.sons[a])
|
||||
inc(a)
|
||||
inc(f)
|
||||
|
||||
proc semFinishOperands*(c: PContext, n: PNode) =
|
||||
# this needs to be called to ensure that after overloading resolution every
|
||||
|
|
@ -1496,12 +1630,15 @@ proc argtypeMatches*(c: PContext, f, a: PType): bool =
|
|||
# instantiate generic converters for that
|
||||
result = res != nil
|
||||
|
||||
proc instDeepCopy*(c: PContext; dc: PSym; t: PType; info: TLineInfo): PSym {.
|
||||
procvar.} =
|
||||
proc instTypeBoundOp*(c: PContext; dc: PSym; t: PType; info: TLineInfo;
|
||||
op: TTypeAttachedOp): PSym {.procvar.} =
|
||||
var m: TCandidate
|
||||
initCandidate(c, m, dc.typ)
|
||||
var f = dc.typ.sons[1]
|
||||
if f.kind in {tyRef, tyPtr}: f = f.lastSon
|
||||
if op == attachedDeepCopy:
|
||||
if f.kind in {tyRef, tyPtr}: f = f.lastSon
|
||||
else:
|
||||
if f.kind == tyVar: f = f.lastSon
|
||||
if typeRel(m, f, t) == isNone:
|
||||
localError(info, errGenerated, "cannot instantiate 'deepCopy'")
|
||||
else:
|
||||
|
|
@ -1567,7 +1704,7 @@ tests:
|
|||
|
||||
setup:
|
||||
var c: TCandidate
|
||||
InitCandidate(nil, c, nil)
|
||||
initCandidate(nil, c, nil)
|
||||
|
||||
template yes(x, y) =
|
||||
test astToStr(x) & " is " & astToStr(y):
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
#
|
||||
#
|
||||
# The Nim Compiler
|
||||
# (c) Copyright 2013 Andreas Rumpf
|
||||
# (c) Copyright 2015 Andreas Rumpf
|
||||
#
|
||||
# See the file "copying.txt", included in this
|
||||
# distribution, for details about the copyright.
|
||||
|
|
@ -22,44 +22,57 @@ const
|
|||
|
||||
#template sectionSuggest(): expr = "##begin\n" & getStackTrace() & "##end\n"
|
||||
|
||||
proc origModuleName(m: PSym): string =
|
||||
result = if m.position == gDirtyBufferIdx:
|
||||
fileInfos[gDirtyOriginalIdx].shortName
|
||||
else:
|
||||
m.name.s
|
||||
template origModuleName(m: PSym): string = m.name.s
|
||||
|
||||
proc symToStr(s: PSym, isLocal: bool, section: string, li: TLineInfo): string =
|
||||
result = section
|
||||
result.add(sep)
|
||||
result.add($s.kind)
|
||||
result.add(sep)
|
||||
if not isLocal and s.kind != skModule:
|
||||
let ow = s.owner
|
||||
if ow.kind != skModule and ow.owner != nil:
|
||||
let ow2 = ow.owner
|
||||
result.add(ow2.origModuleName)
|
||||
if optIdeTerse in gGlobalOptions:
|
||||
if s.kind in routineKinds:
|
||||
result.add renderTree(s.ast, {renderNoBody, renderNoComments,
|
||||
renderDocComments, renderNoPragmas})
|
||||
else:
|
||||
result.add s.name.s
|
||||
result.add(sep)
|
||||
result.add(toFullPath(li))
|
||||
result.add(sep)
|
||||
result.add($toLinenumber(li))
|
||||
result.add(sep)
|
||||
result.add($toColumn(li))
|
||||
else:
|
||||
result.add($s.kind)
|
||||
result.add(sep)
|
||||
if not isLocal and s.kind != skModule:
|
||||
let ow = s.owner
|
||||
if ow.kind != skModule and ow.owner != nil:
|
||||
let ow2 = ow.owner
|
||||
result.add(ow2.origModuleName)
|
||||
result.add('.')
|
||||
result.add(ow.origModuleName)
|
||||
result.add('.')
|
||||
result.add(ow.origModuleName)
|
||||
result.add('.')
|
||||
result.add(s.name.s)
|
||||
result.add(sep)
|
||||
if s.typ != nil:
|
||||
result.add(typeToString(s.typ))
|
||||
result.add(sep)
|
||||
result.add(toFullPath(li))
|
||||
result.add(sep)
|
||||
result.add($toLinenumber(li))
|
||||
result.add(sep)
|
||||
result.add($toColumn(li))
|
||||
result.add(sep)
|
||||
when not defined(noDocgen):
|
||||
result.add(s.extractDocComment.escape)
|
||||
result.add(s.name.s)
|
||||
result.add(sep)
|
||||
if s.typ != nil:
|
||||
result.add(typeToString(s.typ))
|
||||
result.add(sep)
|
||||
result.add(toFullPath(li))
|
||||
result.add(sep)
|
||||
result.add($toLinenumber(li))
|
||||
result.add(sep)
|
||||
result.add($toColumn(li))
|
||||
result.add(sep)
|
||||
when not defined(noDocgen):
|
||||
result.add(s.extractDocComment.escape)
|
||||
|
||||
proc symToStr(s: PSym, isLocal: bool, section: string): string =
|
||||
result = symToStr(s, isLocal, section, s.info)
|
||||
|
||||
proc filterSym(s: PSym): bool {.inline.} =
|
||||
result = s.name.s[0] in lexer.SymChars and s.kind != skModule
|
||||
result = s.kind != skModule
|
||||
|
||||
proc filterSymNoOpr(s: PSym): bool {.inline.} =
|
||||
result = s.kind != skModule and s.name.s[0] in lexer.SymChars and
|
||||
not isKeyword(s.name)
|
||||
|
||||
proc fieldVisible*(c: PContext, f: PSym): bool {.inline.} =
|
||||
let fmoduleId = getModule(f).id
|
||||
|
|
@ -74,9 +87,6 @@ proc suggestField(c: PContext, s: PSym, outputs: var int) =
|
|||
suggestWriteln(symToStr(s, isLocal=true, sectionSuggest))
|
||||
inc outputs
|
||||
|
||||
when not defined(nimhygiene):
|
||||
{.pragma: inject.}
|
||||
|
||||
template wholeSymTab(cond, section: expr) {.immediate.} =
|
||||
var isLocal = true
|
||||
for scope in walkScopes(c.currentScope):
|
||||
|
|
@ -134,11 +144,20 @@ proc suggestCall(c: PContext, n, nOrig: PNode, outputs: var int) =
|
|||
|
||||
proc typeFits(c: PContext, s: PSym, firstArg: PType): bool {.inline.} =
|
||||
if s.typ != nil and sonsLen(s.typ) > 1 and s.typ.sons[1] != nil:
|
||||
# special rule: if system and some weird generic match via 'tyExpr'
|
||||
# or 'tyGenericParam' we won't list it either to reduce the noise (nobody
|
||||
# wants 'system.`-|` as suggestion
|
||||
let m = s.getModule()
|
||||
if m != nil and sfSystemModule in m.flags:
|
||||
if s.kind == skType: return
|
||||
var exp = s.typ.sons[1].skipTypes({tyGenericInst, tyVar})
|
||||
if exp.kind == tyVarargs: exp = elemType(exp)
|
||||
if exp.kind in {tyExpr, tyStmt, tyGenericParam, tyAnything}: return
|
||||
result = sigmatch.argtypeMatches(c, s.typ.sons[1], firstArg)
|
||||
|
||||
proc suggestOperations(c: PContext, n: PNode, typ: PType, outputs: var int) =
|
||||
assert typ != nil
|
||||
wholeSymTab(filterSym(it) and typeFits(c, it, typ), sectionSuggest)
|
||||
wholeSymTab(filterSymNoOpr(it) and typeFits(c, it, typ), sectionSuggest)
|
||||
|
||||
proc suggestEverything(c: PContext, n: PNode, outputs: var int) =
|
||||
# do not produce too many symbols:
|
||||
|
|
@ -194,19 +213,28 @@ proc suggestFieldAccess(c: PContext, n: PNode, outputs: var int) =
|
|||
else:
|
||||
suggestOperations(c, n, typ, outputs)
|
||||
|
||||
type
|
||||
TCheckPointResult = enum
|
||||
cpNone, cpFuzzy, cpExact
|
||||
|
||||
proc inCheckpoint(current: TLineInfo): TCheckPointResult =
|
||||
if current.fileIndex == gTrackPos.fileIndex:
|
||||
if current.line == gTrackPos.line and
|
||||
abs(current.col-gTrackPos.col) < 4:
|
||||
return cpExact
|
||||
if current.line >= gTrackPos.line:
|
||||
return cpFuzzy
|
||||
|
||||
proc findClosestDot(n: PNode): PNode =
|
||||
if n.kind == nkDotExpr and msgs.inCheckpoint(n.info) == cpExact:
|
||||
if n.kind == nkDotExpr and inCheckpoint(n.info) == cpExact:
|
||||
result = n
|
||||
else:
|
||||
for i in 0.. <safeLen(n):
|
||||
result = findClosestDot(n.sons[i])
|
||||
if result != nil: return
|
||||
|
||||
const
|
||||
CallNodes = {nkCall, nkInfix, nkPrefix, nkPostfix, nkCommand, nkCallStrLit}
|
||||
|
||||
proc findClosestCall(n: PNode): PNode =
|
||||
if n.kind in CallNodes and msgs.inCheckpoint(n.info) == cpExact:
|
||||
if n.kind in nkCallKinds and inCheckpoint(n.info) == cpExact:
|
||||
result = n
|
||||
else:
|
||||
for i in 0.. <safeLen(n):
|
||||
|
|
@ -214,34 +242,20 @@ proc findClosestCall(n: PNode): PNode =
|
|||
if result != nil: return
|
||||
|
||||
proc isTracked(current: TLineInfo, tokenLen: int): bool =
|
||||
for i in countup(0, high(checkPoints)):
|
||||
if current.fileIndex == checkPoints[i].fileIndex:
|
||||
if current.line == checkPoints[i].line:
|
||||
let col = checkPoints[i].col
|
||||
if col >= current.col and col <= current.col+tokenLen-1:
|
||||
return true
|
||||
if current.fileIndex == gTrackPos.fileIndex:
|
||||
if current.line == gTrackPos.line:
|
||||
let col = gTrackPos.col
|
||||
if col >= current.col and col <= current.col+tokenLen-1:
|
||||
return true
|
||||
|
||||
proc findClosestSym(n: PNode): PNode =
|
||||
if n.kind == nkSym and msgs.inCheckpoint(n.info) == cpExact:
|
||||
if n.kind == nkSym and inCheckpoint(n.info) == cpExact:
|
||||
result = n
|
||||
elif n.kind notin {nkNone..nkNilLit}:
|
||||
for i in 0.. <sonsLen(n):
|
||||
result = findClosestSym(n.sons[i])
|
||||
if result != nil: return
|
||||
|
||||
proc safeSemExpr(c: PContext, n: PNode): PNode =
|
||||
try:
|
||||
result = c.semExpr(c, n)
|
||||
except ERecoverableError:
|
||||
result = ast.emptyNode
|
||||
|
||||
proc fuzzySemCheck(c: PContext, n: PNode): PNode =
|
||||
result = safeSemExpr(c, n)
|
||||
if result == nil or result.kind == nkEmpty:
|
||||
result = newNodeI(n.kind, n.info)
|
||||
if n.kind notin {nkNone..nkNilLit}:
|
||||
for i in 0 .. < sonsLen(n): result.addSon(fuzzySemCheck(c, n.sons[i]))
|
||||
|
||||
var
|
||||
usageSym*: PSym
|
||||
lastLineInfo: TLineInfo
|
||||
|
|
@ -261,69 +275,18 @@ proc findDefinition(info: TLineInfo; s: PSym) =
|
|||
suggestWriteln(symToStr(s, isLocal=false, sectionDef))
|
||||
suggestQuit()
|
||||
|
||||
type
|
||||
TSourceMap = object
|
||||
lines: seq[TLineMap]
|
||||
|
||||
TEntry = object
|
||||
pos: int
|
||||
sym: PSym
|
||||
|
||||
TLineMap = object
|
||||
entries: seq[TEntry]
|
||||
|
||||
var
|
||||
gSourceMaps: seq[TSourceMap] = @[]
|
||||
|
||||
proc ensureIdx[T](x: var T, y: int) =
|
||||
if x.len <= y: x.setLen(y+1)
|
||||
|
||||
proc ensureSeq[T](x: var seq[T]) =
|
||||
if x == nil: newSeq(x, 0)
|
||||
|
||||
proc resetSourceMap*(fileIdx: int32) =
|
||||
ensureIdx(gSourceMaps, fileIdx)
|
||||
gSourceMaps[fileIdx].lines = @[]
|
||||
|
||||
proc addToSourceMap(sym: PSym, info: TLineInfo) =
|
||||
ensureIdx(gSourceMaps, info.fileIndex)
|
||||
ensureSeq(gSourceMaps[info.fileIndex].lines)
|
||||
ensureIdx(gSourceMaps[info.fileIndex].lines, info.line)
|
||||
ensureSeq(gSourceMaps[info.fileIndex].lines[info.line].entries)
|
||||
gSourceMaps[info.fileIndex].lines[info.line].entries.add(TEntry(pos: info.col, sym: sym))
|
||||
|
||||
proc defFromLine(entries: var seq[TEntry], col: int32) =
|
||||
if entries == nil: return
|
||||
# The sorting is done lazily here on purpose.
|
||||
# No need to pay the price for it unless the user requests
|
||||
# "goto definition" on a particular line
|
||||
sort(entries) do (a,b: TEntry) -> int:
|
||||
return cmp(a.pos, b.pos)
|
||||
|
||||
for e in entries:
|
||||
# currently, the line-infos for most expressions point to
|
||||
# one position past the end of the expression. This means
|
||||
# that the first expr that ends after the cursor column is
|
||||
# the one we are looking for.
|
||||
if e.pos >= col:
|
||||
suggestWriteln(symToStr(e.sym, isLocal=false, sectionDef))
|
||||
return
|
||||
|
||||
proc defFromSourceMap*(i: TLineInfo) =
|
||||
if not ((i.fileIndex < gSourceMaps.len) and
|
||||
(gSourceMaps[i.fileIndex].lines != nil) and
|
||||
(i.line < gSourceMaps[i.fileIndex].lines.len)): return
|
||||
|
||||
defFromLine(gSourceMaps[i.fileIndex].lines[i.line].entries, i.col)
|
||||
|
||||
proc suggestSym*(info: TLineInfo; s: PSym) {.inline.} =
|
||||
## misnamed: should be 'symDeclared'
|
||||
if optUsages in gGlobalOptions:
|
||||
if gIdeCmd == ideUse:
|
||||
findUsages(info, s)
|
||||
if optDef in gGlobalOptions:
|
||||
elif gIdeCmd == ideDef:
|
||||
findDefinition(info, s)
|
||||
if isServing:
|
||||
addToSourceMap(s, info)
|
||||
|
||||
proc markUsed(info: TLineInfo; s: PSym) =
|
||||
incl(s.flags, sfUsed)
|
||||
|
|
@ -336,31 +299,39 @@ proc useSym*(sym: PSym): PNode =
|
|||
result = newSymNode(sym)
|
||||
markUsed(result.info, sym)
|
||||
|
||||
proc safeSemExpr*(c: PContext, n: PNode): PNode =
|
||||
# use only for idetools support!
|
||||
try:
|
||||
result = c.semExpr(c, n)
|
||||
except ERecoverableError:
|
||||
result = ast.emptyNode
|
||||
|
||||
proc suggestExpr*(c: PContext, node: PNode) =
|
||||
var cp = msgs.inCheckpoint(node.info)
|
||||
if cp == cpNone: return
|
||||
if nfIsCursor notin node.flags:
|
||||
if gTrackPos.line < 0: return
|
||||
var cp = inCheckpoint(node.info)
|
||||
if cp == cpNone: return
|
||||
var outputs = 0
|
||||
# This keeps semExpr() from coming here recursively:
|
||||
if c.inCompilesContext > 0: return
|
||||
inc(c.inCompilesContext)
|
||||
|
||||
if optSuggest in gGlobalOptions:
|
||||
var n = findClosestDot(node)
|
||||
if gIdeCmd == ideSug:
|
||||
var n = if nfIsCursor in node.flags: node else: findClosestDot(node)
|
||||
if n == nil: n = node
|
||||
else: cp = cpExact
|
||||
if n.kind == nkDotExpr and cp == cpExact:
|
||||
if n.kind == nkDotExpr:
|
||||
var obj = safeSemExpr(c, n.sons[0])
|
||||
suggestFieldAccess(c, obj, outputs)
|
||||
if optIdeDebug in gGlobalOptions:
|
||||
echo "expression ", renderTree(obj), " has type ", typeToString(obj.typ)
|
||||
#writeStackTrace()
|
||||
else:
|
||||
#debug n
|
||||
suggestEverything(c, n, outputs)
|
||||
|
||||
if optContext in gGlobalOptions:
|
||||
var n = findClosestCall(node)
|
||||
elif gIdeCmd == ideCon:
|
||||
var n = if nfIsCursor in node.flags: node else: findClosestCall(node)
|
||||
if n == nil: n = node
|
||||
else: cp = cpExact
|
||||
|
||||
if n.kind in CallNodes:
|
||||
if n.kind in nkCallKinds:
|
||||
var a = copyNode(n)
|
||||
var x = safeSemExpr(c, n.sons[0])
|
||||
if x.kind == nkEmpty or x.typ == nil: x = n.sons[0]
|
||||
|
|
@ -373,13 +344,7 @@ proc suggestExpr*(c: PContext, node: PNode) =
|
|||
suggestCall(c, a, n, outputs)
|
||||
|
||||
dec(c.inCompilesContext)
|
||||
if outputs > 0 and optUsages notin gGlobalOptions: suggestQuit()
|
||||
if outputs > 0 and gIdeCmd != ideUse: suggestQuit()
|
||||
|
||||
proc suggestStmt*(c: PContext, n: PNode) =
|
||||
suggestExpr(c, n)
|
||||
|
||||
proc findSuggest*(c: PContext, n: PNode) =
|
||||
if n == nil: return
|
||||
suggestExpr(c, n)
|
||||
for i in 0.. <safeLen(n):
|
||||
findSuggest(c, n.sons[i])
|
||||
|
|
|
|||
|
|
@ -45,7 +45,7 @@ proc parseFile(fileIdx: int32): PNode =
|
|||
var
|
||||
p: TParsers
|
||||
f: File
|
||||
let filename = fileIdx.toFullPath
|
||||
let filename = fileIdx.toFullPathConsiderDirty
|
||||
if not open(f, filename):
|
||||
rawMessage(errCannotOpenFile, filename)
|
||||
return
|
||||
|
|
@ -163,7 +163,7 @@ proc evalPipe(p: var TParsers, n: PNode, filename: string,
|
|||
proc openParsers(p: var TParsers, fileIdx: int32, inputstream: PLLStream) =
|
||||
var s: PLLStream
|
||||
p.skin = skinStandard
|
||||
let filename = fileIdx.toFullPath
|
||||
let filename = fileIdx.toFullPathConsiderDirty
|
||||
var pipe = parsePipe(filename, inputstream)
|
||||
if pipe != nil: s = evalPipe(p, pipe, filename, inputstream)
|
||||
else: s = inputstream
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
#
|
||||
#
|
||||
# The Nim Compiler
|
||||
# (c) Copyright 2014 Andreas Rumpf
|
||||
# (c) Copyright 2015 Andreas Rumpf
|
||||
#
|
||||
# See the file "copying.txt", included in this
|
||||
# distribution, for details about the copyright.
|
||||
|
|
@ -152,6 +152,7 @@ proc transformVarSection(c: PTransf, v: PNode): PTransNode =
|
|||
defs[0] = newSymNode(newVar).PTransNode
|
||||
defs[1] = it.sons[1].PTransNode
|
||||
defs[2] = transform(c, it.sons[2])
|
||||
newVar.ast = defs[2].PNode
|
||||
result[i] = defs
|
||||
else:
|
||||
if it.kind != nkVarTuple:
|
||||
|
|
@ -321,6 +322,7 @@ proc transformYield(c: PTransf, n: PNode): PTransNode =
|
|||
|
||||
proc transformAddrDeref(c: PTransf, n: PNode, a, b: TNodeKind): PTransNode =
|
||||
result = transformSons(c, n)
|
||||
if gCmd == cmdCompileToCpp or sfCompileToCpp in c.module.flags: return
|
||||
var n = result.PNode
|
||||
case n.sons[0].kind
|
||||
of nkObjUpConv, nkObjDownConv, nkChckRange, nkChckRangeF, nkChckRange64:
|
||||
|
|
@ -377,6 +379,9 @@ proc transformConv(c: PTransf, n: PNode): PTransNode =
|
|||
result = transformSons(c, n)
|
||||
of tyOpenArray, tyVarargs:
|
||||
result = transform(c, n.sons[1])
|
||||
PNode(result).typ = takeType(n.typ, n.sons[1].typ)
|
||||
#echo n.info, " came here and produced ", typeToString(PNode(result).typ),
|
||||
# " from ", typeToString(n.typ), " and ", typeToString(n.sons[1].typ)
|
||||
of tyCString:
|
||||
if source.kind == tyString:
|
||||
result = newTransNode(nkStringToCString, n, 1)
|
||||
|
|
@ -397,7 +402,7 @@ proc transformConv(c: PTransf, n: PNode): PTransNode =
|
|||
if diff < 0:
|
||||
result = newTransNode(nkObjUpConv, n, 1)
|
||||
result[0] = transform(c, n.sons[1])
|
||||
elif diff > 0:
|
||||
elif diff > 0 and diff != high(int):
|
||||
result = newTransNode(nkObjDownConv, n, 1)
|
||||
result[0] = transform(c, n.sons[1])
|
||||
else:
|
||||
|
|
@ -409,7 +414,7 @@ proc transformConv(c: PTransf, n: PNode): PTransNode =
|
|||
if diff < 0:
|
||||
result = newTransNode(nkObjUpConv, n, 1)
|
||||
result[0] = transform(c, n.sons[1])
|
||||
elif diff > 0:
|
||||
elif diff > 0 and diff != high(int):
|
||||
result = newTransNode(nkObjDownConv, n, 1)
|
||||
result[0] = transform(c, n.sons[1])
|
||||
else:
|
||||
|
|
@ -491,7 +496,8 @@ proc transformFor(c: PTransf, n: PNode): PTransNode =
|
|||
var newC = newTransCon(getCurrOwner(c))
|
||||
newC.forStmt = n
|
||||
newC.forLoopBody = loopBody
|
||||
internalAssert iter.kind == skIterator
|
||||
# this can fail for 'nimsuggest' and 'check':
|
||||
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):
|
||||
|
|
@ -576,8 +582,7 @@ proc getMergeOp(n: PNode): PSym =
|
|||
case n.kind
|
||||
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):
|
||||
if n.sons[0].kind == nkSym and n.sons[0].sym.magic == mConStrStr:
|
||||
result = n.sons[0].sym
|
||||
else: discard
|
||||
|
||||
|
|
|
|||
|
|
@ -142,7 +142,7 @@ proc swapOperands*(op: PNode) =
|
|||
op.sons[2] = tmp
|
||||
|
||||
proc isRange*(n: PNode): bool {.inline.} =
|
||||
if n.kind == nkInfix:
|
||||
if n.kind in nkCallKinds:
|
||||
if n[0].kind == nkIdent and n[0].ident.id == ord(wDotDot) or
|
||||
n[0].kind in {nkClosedSymChoice, nkOpenSymChoice} and
|
||||
n[0][1].sym.name.id == ord(wDotDot):
|
||||
|
|
|
|||
|
|
@ -29,7 +29,8 @@ proc hashTree(n: PNode): THash =
|
|||
if (n.floatVal >= - 1000000.0) and (n.floatVal <= 1000000.0):
|
||||
result = result !& toInt(n.floatVal)
|
||||
of nkStrLit..nkTripleStrLit:
|
||||
result = result !& hash(n.strVal)
|
||||
if not n.strVal.isNil:
|
||||
result = result !& hash(n.strVal)
|
||||
else:
|
||||
for i in countup(0, sonsLen(n) - 1):
|
||||
result = result !& hashTree(n.sons[i])
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@ proc lastOrd*(t: PType): BiggestInt
|
|||
proc lengthOrd*(t: PType): BiggestInt
|
||||
type
|
||||
TPreferedDesc* = enum
|
||||
preferName, preferDesc, preferExported, preferModuleInfo
|
||||
preferName, preferDesc, preferExported, preferModuleInfo, preferGenericArg
|
||||
|
||||
proc typeToString*(typ: PType; prefer: TPreferedDesc = preferName): string
|
||||
proc base*(t: PType): PType
|
||||
|
|
@ -85,8 +85,6 @@ proc analyseObjectWithTypeField*(t: PType): TTypeFieldResult
|
|||
# this does a complex analysis whether a call to ``objectInit`` needs to be
|
||||
# made or intializing of the type field suffices or if there is no type field
|
||||
# at all in this type.
|
||||
proc typeAllowed*(t: PType, kind: TSymKind): bool
|
||||
# implementation
|
||||
|
||||
proc invalidGenericInst(f: PType): bool =
|
||||
result = f.kind == tyGenericInst and lastSon(f) == nil
|
||||
|
|
@ -141,7 +139,7 @@ proc elemType*(t: PType): PType =
|
|||
case t.kind
|
||||
of tyGenericInst, tyDistinct: result = elemType(lastSon(t))
|
||||
of tyArray, tyArrayConstr: result = t.sons[1]
|
||||
else: result = t.sons[0]
|
||||
else: result = t.lastSon
|
||||
assert(result != nil)
|
||||
|
||||
proc skipGeneric(t: PType): PType =
|
||||
|
|
@ -252,7 +250,7 @@ proc containsObject(t: PType): bool =
|
|||
|
||||
proc isObjectWithTypeFieldPredicate(t: PType): bool =
|
||||
result = t.kind == tyObject and t.sons[0] == nil and
|
||||
not (t.sym != nil and sfPure in t.sym.flags) and
|
||||
not (t.sym != nil and {sfPure, sfInfixCall} * t.sym.flags != {}) and
|
||||
tfFinal notin t.flags
|
||||
|
||||
proc analyseObjectWithTypeFieldAux(t: PType,
|
||||
|
|
@ -398,7 +396,7 @@ proc rangeToStr(n: PNode): string =
|
|||
const
|
||||
typeToStr: array[TTypeKind, string] = ["None", "bool", "Char", "empty",
|
||||
"Array Constructor [$1]", "nil", "expr", "stmt", "typeDesc",
|
||||
"GenericInvokation", "GenericBody", "GenericInst", "GenericParam",
|
||||
"GenericInvocation", "GenericBody", "GenericInst", "GenericParam",
|
||||
"distinct $1", "enum", "ordinal[$1]", "array[$1, $2]", "object", "tuple",
|
||||
"set[$1]", "range[$1]", "ptr ", "ref ", "var ", "seq[$1]", "proc",
|
||||
"pointer", "OpenArray[$1]", "string", "CString", "Forward",
|
||||
|
|
@ -411,11 +409,13 @@ const
|
|||
"UserTypeClassInst", "CompositeTypeClass",
|
||||
"and", "or", "not", "any", "static", "TypeFromExpr", "FieldAccessor"]
|
||||
|
||||
const preferToResolveSymbols = {preferName, preferModuleInfo, preferGenericArg}
|
||||
|
||||
proc typeToString(typ: PType, prefer: TPreferedDesc = preferName): string =
|
||||
var t = typ
|
||||
result = ""
|
||||
if t == nil: return
|
||||
if prefer in {preferName, preferModuleInfo} and t.sym != nil and
|
||||
if prefer in preferToResolveSymbols and t.sym != nil and
|
||||
sfAnon notin t.sym.flags:
|
||||
if t.kind == tyInt and isIntLit(t):
|
||||
return t.sym.name.s & " literal(" & $t.n.intVal & ")"
|
||||
|
|
@ -428,20 +428,26 @@ proc typeToString(typ: PType, prefer: TPreferedDesc = preferName): string =
|
|||
if not isIntLit(t) or prefer == preferExported:
|
||||
result = typeToStr[t.kind]
|
||||
else:
|
||||
result = "int literal(" & $t.n.intVal & ")"
|
||||
of tyGenericBody, tyGenericInst, tyGenericInvokation:
|
||||
if prefer == preferGenericArg:
|
||||
result = $t.n.intVal
|
||||
else:
|
||||
result = "int literal(" & $t.n.intVal & ")"
|
||||
of tyGenericBody, tyGenericInst, tyGenericInvocation:
|
||||
result = typeToString(t.sons[0]) & '['
|
||||
for i in countup(1, sonsLen(t) -1 -ord(t.kind != tyGenericInvokation)):
|
||||
for i in countup(1, sonsLen(t)-1-ord(t.kind != tyGenericInvocation)):
|
||||
if i > 1: add(result, ", ")
|
||||
add(result, typeToString(t.sons[i]))
|
||||
add(result, typeToString(t.sons[i], preferGenericArg))
|
||||
add(result, ']')
|
||||
of tyTypeDesc:
|
||||
if t.base.kind == tyNone: result = "typedesc"
|
||||
else: result = "typedesc[" & typeToString(t.base) & "]"
|
||||
of tyStatic:
|
||||
internalAssert t.len > 0
|
||||
result = "static[" & typeToString(t.sons[0]) & "]"
|
||||
if t.n != nil: result.add "(" & renderTree(t.n) & ")"
|
||||
if prefer == preferGenericArg and t.n != nil:
|
||||
result = t.n.renderTree
|
||||
else:
|
||||
result = "static[" & typeToString(t.sons[0]) & "]"
|
||||
if t.n != nil: result.add "(" & renderTree(t.n) & ")"
|
||||
of tyUserTypeClass:
|
||||
internalAssert t.sym != nil and t.sym.owner != nil
|
||||
return t.sym.owner.name.s
|
||||
|
|
@ -501,18 +507,22 @@ proc typeToString(typ: PType, prefer: TPreferedDesc = preferName): string =
|
|||
if prefer == preferModuleInfo: preferModuleInfo else: preferName)
|
||||
of tyTuple:
|
||||
# we iterate over t.sons here, because t.n may be nil
|
||||
result = "tuple["
|
||||
if t.n != nil:
|
||||
result = "tuple["
|
||||
assert(sonsLen(t.n) == sonsLen(t))
|
||||
for i in countup(0, sonsLen(t.n) - 1):
|
||||
assert(t.n.sons[i].kind == nkSym)
|
||||
add(result, t.n.sons[i].sym.name.s & ": " & typeToString(t.sons[i]))
|
||||
if i < sonsLen(t.n) - 1: add(result, ", ")
|
||||
add(result, ']')
|
||||
elif sonsLen(t) == 0:
|
||||
result = "tuple[]"
|
||||
else:
|
||||
result = "("
|
||||
for i in countup(0, sonsLen(t) - 1):
|
||||
add(result, typeToString(t.sons[i]))
|
||||
if i < sonsLen(t) - 1: add(result, ", ")
|
||||
add(result, ']')
|
||||
add(result, ')')
|
||||
of tyPtr, tyRef, tyVar, tyMutable, tyConst:
|
||||
result = typeToStr[t.kind]
|
||||
if t.len >= 2:
|
||||
|
|
@ -531,6 +541,9 @@ proc typeToString(typ: PType, prefer: TPreferedDesc = preferName): string =
|
|||
of tyProc:
|
||||
result = if tfIterator in t.flags: "iterator (" else: "proc ("
|
||||
for i in countup(1, sonsLen(t) - 1):
|
||||
if t.n != nil and i < t.n.len and t.n[i].kind == nkSym:
|
||||
add(result, t.n[i].sym.name.s)
|
||||
add(result, ": ")
|
||||
add(result, typeToString(t.sons[i]))
|
||||
if i < sonsLen(t) - 1: add(result, ", ")
|
||||
add(result, ')')
|
||||
|
|
@ -580,7 +593,7 @@ proc firstOrd(t: PType): BiggestInt =
|
|||
of tyUInt..tyUInt64: result = 0
|
||||
of tyEnum:
|
||||
# if basetype <> nil then return firstOrd of basetype
|
||||
if (sonsLen(t) > 0) and (t.sons[0] != nil):
|
||||
if sonsLen(t) > 0 and t.sons[0] != nil:
|
||||
result = firstOrd(t.sons[0])
|
||||
else:
|
||||
assert(t.n.sons[0].kind == nkSym)
|
||||
|
|
@ -643,12 +656,12 @@ proc lengthOrd(t: PType): BiggestInt =
|
|||
type
|
||||
TDistinctCompare* = enum ## how distinct types are to be compared
|
||||
dcEq, ## a and b should be the same type
|
||||
dcEqIgnoreDistinct, ## compare symetrically: (distinct a) == b, a == b
|
||||
dcEqIgnoreDistinct, ## compare symmetrically: (distinct a) == b, a == b
|
||||
## or a == (distinct b)
|
||||
dcEqOrDistinctOf ## a equals b or a is distinct of b
|
||||
|
||||
TTypeCmpFlag* = enum
|
||||
IgnoreTupleFields
|
||||
IgnoreTupleFields ## NOTE: Only set this flag for backends!
|
||||
IgnoreCC
|
||||
ExactTypeDescValues
|
||||
ExactGenericParams
|
||||
|
|
@ -768,11 +781,20 @@ proc sameTuple(a, b: PType, c: var TSameTypeClosure): bool =
|
|||
var x = a.sons[i]
|
||||
var y = b.sons[i]
|
||||
if IgnoreTupleFields in c.flags:
|
||||
x = skipTypes(x, {tyRange})
|
||||
y = skipTypes(y, {tyRange})
|
||||
x = skipTypes(x, {tyRange, tyGenericInst})
|
||||
y = skipTypes(y, {tyRange, tyGenericInst})
|
||||
|
||||
result = sameTypeAux(x, y, c)
|
||||
if not result: return
|
||||
if a.n != nil and b.n != nil and IgnoreTupleFields notin c.flags:
|
||||
for i in countup(0, sonsLen(a.n) - 1):
|
||||
# check field names:
|
||||
if a.n.sons[i].kind == nkSym and b.n.sons[i].kind == nkSym:
|
||||
var x = a.n.sons[i].sym
|
||||
var y = b.n.sons[i].sym
|
||||
result = x.name.id == y.name.id
|
||||
if not result: break
|
||||
else: internalError(a.n.info, "sameTuple")
|
||||
|
||||
template ifFastObjectTypeCheckFailed(a, b: PType, body: stmt) {.immediate.} =
|
||||
if tfFromGeneric notin a.flags + b.flags:
|
||||
|
|
@ -781,7 +803,7 @@ template ifFastObjectTypeCheckFailed(a, b: PType, body: stmt) {.immediate.} =
|
|||
else:
|
||||
# expensive structural equality test; however due to the way generic and
|
||||
# objects work, if one of the types does **not** contain tfFromGeneric,
|
||||
# they cannot be equal. The check ``a.sym.Id == b.sym.Id`` checks
|
||||
# they cannot be equal. The check ``a.sym.id == b.sym.id`` checks
|
||||
# for the same origin and is essential because we don't want "pure"
|
||||
# structural type equivalence:
|
||||
#
|
||||
|
|
@ -808,8 +830,13 @@ proc sameEnumTypes*(a, b: PType): bool {.inline.} =
|
|||
proc sameObjectTree(a, b: PNode, c: var TSameTypeClosure): bool =
|
||||
if a == b:
|
||||
result = true
|
||||
elif (a != nil) and (b != nil) and (a.kind == b.kind):
|
||||
if sameTypeOrNilAux(a.typ, b.typ, c):
|
||||
elif a != nil and b != nil and a.kind == b.kind:
|
||||
var x = a.typ
|
||||
var y = b.typ
|
||||
if IgnoreTupleFields in c.flags:
|
||||
if x != nil: x = skipTypes(x, {tyRange, tyGenericInst})
|
||||
if y != nil: y = skipTypes(y, {tyRange, tyGenericInst})
|
||||
if sameTypeOrNilAux(x, y, c):
|
||||
case a.kind
|
||||
of nkSym:
|
||||
# same symbol as string is enough:
|
||||
|
|
@ -895,6 +922,9 @@ proc sameTypeAux(x, y: PType, c: var TSameTypeClosure): bool =
|
|||
result = sameFlags(a, b)
|
||||
of tyStatic, tyFromExpr:
|
||||
result = exprStructuralEquivalent(a.n, b.n) and sameFlags(a, b)
|
||||
if result and a.len == b.len and a.len == 1:
|
||||
cycleCheck()
|
||||
result = sameTypeAux(a.sons[0], b.sons[0], c)
|
||||
of tyObject:
|
||||
ifFastObjectTypeCheckFailed(a, b):
|
||||
cycleCheck()
|
||||
|
|
@ -926,7 +956,7 @@ proc sameTypeAux(x, y: PType, c: var TSameTypeClosure): bool =
|
|||
result = sameChildrenAux(a, b, c) and sameFlags(a, b)
|
||||
if result and ExactGenericParams in c.flags:
|
||||
result = a.sym.position == b.sym.position
|
||||
of tyGenericInvokation, tyGenericBody, tySequence,
|
||||
of tyGenericInvocation, tyGenericBody, tySequence,
|
||||
tyOpenArray, tySet, tyRef, tyPtr, tyVar, tyArrayConstr,
|
||||
tyArray, tyProc, tyConst, tyMutable, tyVarargs, tyIter,
|
||||
tyOrdinal, tyTypeClasses, tyFieldAccessor:
|
||||
|
|
@ -1010,22 +1040,23 @@ type
|
|||
TTypeAllowedFlags = set[TTypeAllowedFlag]
|
||||
|
||||
proc typeAllowedAux(marker: var IntSet, typ: PType, kind: TSymKind,
|
||||
flags: TTypeAllowedFlags = {}): bool
|
||||
flags: TTypeAllowedFlags = {}): PType
|
||||
|
||||
proc typeAllowedNode(marker: var IntSet, n: PNode, kind: TSymKind,
|
||||
flags: TTypeAllowedFlags = {}): bool =
|
||||
result = true
|
||||
flags: TTypeAllowedFlags = {}): PType =
|
||||
if n != nil:
|
||||
result = typeAllowedAux(marker, n.typ, kind, flags)
|
||||
#if not result: debug(n.typ)
|
||||
if result:
|
||||
if result == nil:
|
||||
case n.kind
|
||||
of nkNone..nkNilLit:
|
||||
discard
|
||||
else:
|
||||
for i in countup(0, sonsLen(n) - 1):
|
||||
result = typeAllowedNode(marker, n.sons[i], kind, flags)
|
||||
if not result: break
|
||||
let it = n.sons[i]
|
||||
if it.kind == nkRecCase and kind == skConst: return n.typ
|
||||
result = typeAllowedNode(marker, it, kind, flags)
|
||||
if result != nil: break
|
||||
|
||||
proc matchType*(a: PType, pattern: openArray[tuple[k:TTypeKind, i:int]],
|
||||
last: TTypeKind): bool =
|
||||
|
|
@ -1037,84 +1068,90 @@ proc matchType*(a: PType, pattern: openArray[tuple[k:TTypeKind, i:int]],
|
|||
result = a.kind == last
|
||||
|
||||
proc typeAllowedAux(marker: var IntSet, typ: PType, kind: TSymKind,
|
||||
flags: TTypeAllowedFlags = {}): bool =
|
||||
flags: TTypeAllowedFlags = {}): PType =
|
||||
assert(kind in {skVar, skLet, skConst, skParam, skResult})
|
||||
# if we have already checked the type, return true, because we stop the
|
||||
# evaluation if something is wrong:
|
||||
result = true
|
||||
result = nil
|
||||
if typ == nil: return
|
||||
if containsOrIncl(marker, typ.id): return
|
||||
var t = skipTypes(typ, abstractInst-{tyTypeDesc})
|
||||
case t.kind
|
||||
of tyVar:
|
||||
if kind == skConst: return false
|
||||
if kind == skConst: return t
|
||||
var t2 = skipTypes(t.sons[0], abstractInst-{tyTypeDesc})
|
||||
case t2.kind
|
||||
of tyVar:
|
||||
result = taHeap in flags # ``var var`` is illegal on the heap:
|
||||
if taHeap notin flags: result = t2 # ``var var`` is illegal on the heap
|
||||
of tyOpenArray:
|
||||
result = kind == skParam and typeAllowedAux(marker, t2, kind, flags)
|
||||
if kind != skParam: result = t
|
||||
else: result = typeAllowedAux(marker, t2, kind, flags)
|
||||
else:
|
||||
result = kind in {skParam, skResult} and
|
||||
typeAllowedAux(marker, t2, kind, flags)
|
||||
if kind notin {skParam, skResult}: result = t
|
||||
else: result = typeAllowedAux(marker, t2, kind, flags)
|
||||
of tyProc:
|
||||
for i in countup(1, sonsLen(t) - 1):
|
||||
result = typeAllowedAux(marker, t.sons[i], skParam, flags)
|
||||
if not result: break
|
||||
if result and t.sons[0] != nil:
|
||||
if result != nil: break
|
||||
if result.isNil and t.sons[0] != nil:
|
||||
result = typeAllowedAux(marker, t.sons[0], skResult, flags)
|
||||
of tyExpr, tyStmt, tyTypeDesc, tyStatic:
|
||||
result = true
|
||||
# XXX er ... no? these should not be allowed!
|
||||
of tyTypeDesc:
|
||||
# XXX: This is still a horrible idea...
|
||||
result = nil
|
||||
of tyExpr, tyStmt, tyStatic:
|
||||
if kind notin {skParam, skResult}: result = t
|
||||
of tyEmpty:
|
||||
result = taField in flags
|
||||
if taField notin flags: result = t
|
||||
of tyTypeClasses:
|
||||
result = tfGenericTypeParam in t.flags or
|
||||
taField notin flags
|
||||
of tyGenericBody, tyGenericParam, tyGenericInvokation,
|
||||
if not (tfGenericTypeParam in t.flags or taField notin flags): result = t
|
||||
of tyGenericBody, tyGenericParam, tyGenericInvocation,
|
||||
tyNone, tyForward, tyFromExpr, tyFieldAccessor:
|
||||
result = false
|
||||
result = t
|
||||
of tyNil:
|
||||
result = kind == skConst
|
||||
if kind != skConst: result = t
|
||||
of tyString, tyBool, tyChar, tyEnum, tyInt..tyBigNum, tyCString, tyPointer:
|
||||
result = true
|
||||
result = nil
|
||||
of tyOrdinal:
|
||||
result = kind == skParam
|
||||
if kind != skParam: result = t
|
||||
of tyGenericInst, tyDistinct:
|
||||
result = typeAllowedAux(marker, lastSon(t), kind, flags)
|
||||
of tyRange:
|
||||
result = skipTypes(t.sons[0], abstractInst-{tyTypeDesc}).kind in
|
||||
{tyChar, tyEnum, tyInt..tyFloat128}
|
||||
if skipTypes(t.sons[0], abstractInst-{tyTypeDesc}).kind notin
|
||||
{tyChar, tyEnum, tyInt..tyFloat128, tyUInt8..tyUInt32}: result = t
|
||||
of tyOpenArray, tyVarargs:
|
||||
result = (kind == skParam) and typeAllowedAux(marker, t.sons[0], skVar, flags)
|
||||
if kind != skParam: result = t
|
||||
else: result = typeAllowedAux(marker, t.sons[0], skVar, flags)
|
||||
of tySequence:
|
||||
result = t.sons[0].kind == tyEmpty or
|
||||
typeAllowedAux(marker, t.sons[0], skVar, flags+{taHeap})
|
||||
if t.sons[0].kind != tyEmpty:
|
||||
result = typeAllowedAux(marker, t.sons[0], skVar, flags+{taHeap})
|
||||
of tyArray:
|
||||
result = t.sons[1].kind == tyEmpty or
|
||||
typeAllowedAux(marker, t.sons[1], skVar, flags)
|
||||
if t.sons[1].kind != tyEmpty:
|
||||
result = typeAllowedAux(marker, t.sons[1], skVar, flags)
|
||||
of tyRef:
|
||||
if kind == skConst: return false
|
||||
result = typeAllowedAux(marker, t.lastSon, skVar, flags+{taHeap})
|
||||
if kind == skConst: result = t
|
||||
else: result = typeAllowedAux(marker, t.lastSon, skVar, flags+{taHeap})
|
||||
of tyPtr:
|
||||
result = typeAllowedAux(marker, t.lastSon, skVar, flags+{taHeap})
|
||||
of tyArrayConstr, tySet, tyConst, tyMutable, tyIter:
|
||||
for i in countup(0, sonsLen(t) - 1):
|
||||
result = typeAllowedAux(marker, t.sons[i], kind, flags)
|
||||
if not result: break
|
||||
if result != nil: break
|
||||
of tyObject, tyTuple:
|
||||
if kind == skConst and t.kind == tyObject: return false
|
||||
if kind == skConst and t.kind == tyObject and t.sons[0] != nil: return t
|
||||
let flags = flags+{taField}
|
||||
for i in countup(0, sonsLen(t) - 1):
|
||||
result = typeAllowedAux(marker, t.sons[i], kind, flags)
|
||||
if not result: break
|
||||
if result and t.n != nil: result = typeAllowedNode(marker, t.n, kind, flags)
|
||||
if result != nil: break
|
||||
if result.isNil and t.n != nil:
|
||||
result = typeAllowedNode(marker, t.n, kind, flags)
|
||||
of tyProxy:
|
||||
# for now same as error node; we say it's a valid type as it should
|
||||
# prevent cascading errors:
|
||||
result = true
|
||||
result = nil
|
||||
|
||||
proc typeAllowed(t: PType, kind: TSymKind): bool =
|
||||
proc typeAllowed*(t: PType, kind: TSymKind): PType =
|
||||
# returns 'nil' on success and otherwise the part of the type that is
|
||||
# wrong!
|
||||
var marker = initIntSet()
|
||||
result = typeAllowedAux(marker, t, kind, {})
|
||||
|
||||
|
|
@ -1402,3 +1439,45 @@ proc skipConv*(n: PNode): PNode =
|
|||
proc skipConvTakeType*(n: PNode): PNode =
|
||||
result = n.skipConv
|
||||
result.typ = n.typ
|
||||
|
||||
proc isEmptyContainer*(t: PType): bool =
|
||||
case t.kind
|
||||
of tyExpr, tyNil: result = true
|
||||
of tyArray, tyArrayConstr: result = t.sons[1].kind == tyEmpty
|
||||
of tySet, tySequence, tyOpenArray, tyVarargs:
|
||||
result = t.sons[0].kind == tyEmpty
|
||||
of tyGenericInst: result = isEmptyContainer(t.lastSon)
|
||||
else: result = false
|
||||
|
||||
proc takeType*(formal, arg: PType): PType =
|
||||
# param: openArray[string] = []
|
||||
# [] is an array constructor of length 0 of type string!
|
||||
if arg.kind == tyNil:
|
||||
# and not (formal.kind == tyProc and formal.callConv == ccClosure):
|
||||
result = formal
|
||||
elif formal.kind in {tyOpenArray, tyVarargs, tySequence} and
|
||||
arg.isEmptyContainer:
|
||||
let a = copyType(arg.skipTypes({tyGenericInst}), arg.owner, keepId=false)
|
||||
a.sons[ord(arg.kind in {tyArray, tyArrayConstr})] = formal.sons[0]
|
||||
result = a
|
||||
elif formal.kind in {tyTuple, tySet} and arg.kind == formal.kind:
|
||||
result = formal
|
||||
else:
|
||||
result = arg
|
||||
|
||||
proc skipHiddenSubConv*(n: PNode): PNode =
|
||||
if n.kind == nkHiddenSubConv:
|
||||
# param: openArray[string] = []
|
||||
# [] is an array constructor of length 0 of type string!
|
||||
let formal = n.typ
|
||||
result = n.sons[1]
|
||||
let arg = result.typ
|
||||
let dest = takeType(formal, arg)
|
||||
if dest == arg and formal.kind != tyExpr:
|
||||
#echo n.info, " came here for ", formal.typeToString
|
||||
result = n
|
||||
else:
|
||||
result = copyTree(result)
|
||||
result.typ = dest
|
||||
else:
|
||||
result = n
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
#
|
||||
#
|
||||
# The Nim Compiler
|
||||
# (c) Copyright 2014 Andreas Rumpf
|
||||
# (c) Copyright 2015 Andreas Rumpf
|
||||
#
|
||||
# See the file "copying.txt", included in this
|
||||
# distribution, for details about the copyright.
|
||||
|
|
@ -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)] = ']'
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
#
|
||||
#
|
||||
# The Nim Compiler
|
||||
# (c) Copyright 2014 Andreas Rumpf
|
||||
# (c) Copyright 2015 Andreas Rumpf
|
||||
#
|
||||
# See the file "copying.txt", included in this
|
||||
# distribution, for details about the copyright.
|
||||
|
|
@ -16,7 +16,8 @@ import ast except getstr
|
|||
|
||||
import
|
||||
strutils, astalgo, msgs, vmdef, vmgen, nimsets, types, passes, unsigned,
|
||||
parser, vmdeps, idents, trees, renderer, options, transf, parseutils
|
||||
parser, vmdeps, idents, trees, renderer, options, transf, parseutils,
|
||||
vmmarshal
|
||||
|
||||
from semfold import leValueConv, ordinalValToString
|
||||
from evaltempl import evalTemplate
|
||||
|
|
@ -123,8 +124,12 @@ proc createStrKeepNode(x: var TFullReg) =
|
|||
if x.node.isNil:
|
||||
x.node = newNode(nkStrLit)
|
||||
elif x.node.kind == nkNilLit:
|
||||
when defined(useNodeIds):
|
||||
let id = x.node.id
|
||||
system.reset(x.node[])
|
||||
x.node.kind = nkStrLit
|
||||
when defined(useNodeIds):
|
||||
x.node.id = id
|
||||
elif x.node.kind notin {nkStrLit..nkTripleStrLit} or
|
||||
nfAllConst in x.node.flags:
|
||||
# XXX this is hacky; tests/txmlgen triggers it:
|
||||
|
|
@ -154,7 +159,7 @@ proc moveConst(x: var TFullReg, y: TFullReg) =
|
|||
of rkNodeAddr: x.nodeAddr = y.nodeAddr
|
||||
|
||||
# this seems to be the best way to model the reference semantics
|
||||
# of PNimrodNode:
|
||||
# of system.NimNode:
|
||||
template asgnRef(x, y: expr) = moveConst(x, y)
|
||||
|
||||
proc copyValue(src: PNode): PNode =
|
||||
|
|
@ -367,11 +372,6 @@ template handleJmpBack() {.dirty.} =
|
|||
globalError(c.debug[pc], errTooManyIterations)
|
||||
dec(c.loopIterations)
|
||||
|
||||
proc skipColon(n: PNode): PNode =
|
||||
result = n
|
||||
if n.kind == nkExprColonExpr:
|
||||
result = n.sons[1]
|
||||
|
||||
proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
|
||||
var pc = start
|
||||
var tos = tos
|
||||
|
|
@ -772,10 +772,14 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
|
|||
stackTrace(c, tos, pc, errNilAccess)
|
||||
of opcEcho:
|
||||
let rb = instr.regB
|
||||
for i in ra..ra+rb-1:
|
||||
#if regs[i].kind != rkNode: debug regs[i]
|
||||
write(stdout, regs[i].node.strVal)
|
||||
writeln(stdout, "")
|
||||
if rb == 1:
|
||||
msgWriteln(regs[ra].node.strVal)
|
||||
else:
|
||||
var outp = ""
|
||||
for i in ra..ra+rb-1:
|
||||
#if regs[i].kind != rkNode: debug regs[i]
|
||||
outp.add(regs[i].node.strVal)
|
||||
msgWriteln(outp)
|
||||
of opcContainsSet:
|
||||
decodeBC(rkInt)
|
||||
regs[ra].intVal = ord(inSet(regs[rb].node, regs[rc].regToNode))
|
||||
|
|
@ -806,7 +810,7 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
|
|||
leValueConv(regs[ra].regToNode, regs[rc].regToNode)):
|
||||
stackTrace(c, tos, pc, errGenerated,
|
||||
msgKindToString(errIllegalConvFromXtoY) % [
|
||||
"unknown type" , "unknown type"])
|
||||
$regs[ra].regToNode, "[" & $regs[rb].regToNode & ".." & $regs[rc].regToNode & "]"])
|
||||
of opcIndCall, opcIndCallAsgn:
|
||||
# dest = call regStart, n; where regStart = fn, arg1, ...
|
||||
let rb = instr.regB
|
||||
|
|
@ -984,6 +988,7 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
|
|||
let rb = instr.regBx - wordExcess
|
||||
let cnst = c.constants.sons[rb]
|
||||
if fitsRegister(cnst.typ):
|
||||
myreset(regs[ra])
|
||||
putIntoReg(regs[ra], cnst)
|
||||
else:
|
||||
ensureKind(rkNode)
|
||||
|
|
@ -1011,7 +1016,7 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
|
|||
of opcQuit:
|
||||
if c.mode in {emRepl, emStaticExpr, emStaticStmt}:
|
||||
message(c.debug[pc], hintQuitCalled)
|
||||
quit(int(getOrdValue(regs[ra].regToNode)))
|
||||
msgQuit(int8(getOrdValue(regs[ra].regToNode)))
|
||||
else:
|
||||
return TFullReg(kind: rkNone)
|
||||
of opcSetLenStr:
|
||||
|
|
@ -1034,7 +1039,14 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
|
|||
decodeB(rkNode)
|
||||
let newLen = regs[rb].intVal.int
|
||||
if regs[ra].node.isNil: stackTrace(c, tos, pc, errNilAccess)
|
||||
else: setLen(regs[ra].node.sons, newLen)
|
||||
else:
|
||||
let oldLen = regs[ra].node.len
|
||||
setLen(regs[ra].node.sons, newLen)
|
||||
if oldLen < newLen:
|
||||
# XXX This is still not entirely correct
|
||||
# set to default value:
|
||||
for i in oldLen .. <newLen:
|
||||
regs[ra].node.sons[i] = newNodeI(nkEmpty, c.debug[pc])
|
||||
of opcSwap:
|
||||
let rb = instr.regB
|
||||
if regs[ra].kind == regs[rb].kind:
|
||||
|
|
@ -1086,14 +1098,20 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
|
|||
of opcNAdd:
|
||||
decodeBC(rkNode)
|
||||
var u = regs[rb].node
|
||||
u.add(regs[rc].node)
|
||||
if u.kind notin {nkEmpty..nkNilLit}:
|
||||
u.add(regs[rc].node)
|
||||
else:
|
||||
stackTrace(c, tos, pc, errGenerated, "cannot add to node kind: " & $u.kind)
|
||||
regs[ra].node = u
|
||||
of opcNAddMultiple:
|
||||
decodeBC(rkNode)
|
||||
let x = regs[rc].node
|
||||
var u = regs[rb].node
|
||||
# XXX can be optimized:
|
||||
for i in 0.. <x.len: u.add(x.sons[i])
|
||||
if u.kind notin {nkEmpty..nkNilLit}:
|
||||
# XXX can be optimized:
|
||||
for i in 0.. <x.len: u.add(x.sons[i])
|
||||
else:
|
||||
stackTrace(c, tos, pc, errGenerated, "cannot add to node kind: " & $u.kind)
|
||||
regs[ra].node = u
|
||||
of opcNKind:
|
||||
decodeB(rkInt)
|
||||
|
|
@ -1126,7 +1144,21 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
|
|||
else:
|
||||
stackTrace(c, tos, pc, errFieldXNotFound, "ident")
|
||||
of opcNGetType:
|
||||
internalError(c.debug[pc], "unknown opcode " & $instr.opcode)
|
||||
let rb = instr.regB
|
||||
let rc = instr.regC
|
||||
if rc == 0:
|
||||
ensureKind(rkNode)
|
||||
if regs[rb].kind == rkNode and regs[rb].node.typ != nil:
|
||||
regs[ra].node = opMapTypeToAst(regs[rb].node.typ, c.debug[pc])
|
||||
else:
|
||||
stackTrace(c, tos, pc, errGenerated, "node has no type")
|
||||
else:
|
||||
# typeKind opcode:
|
||||
ensureKind(rkInt)
|
||||
if regs[rb].kind == rkNode and regs[rb].node.typ != nil:
|
||||
regs[ra].intVal = ord(regs[rb].node.typ.kind)
|
||||
#else:
|
||||
# stackTrace(c, tos, pc, errGenerated, "node has no type")
|
||||
of opcNStrVal:
|
||||
decodeB(rkNode)
|
||||
createStr regs[ra]
|
||||
|
|
@ -1333,6 +1365,19 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
|
|||
while typ.kind == tyTypeDesc and typ.len > 0: typ = typ.sons[0]
|
||||
createStr regs[ra]
|
||||
regs[ra].node.strVal = typ.typeToString(preferExported)
|
||||
of opcMarshalLoad:
|
||||
let ra = instr.regA
|
||||
let rb = instr.regB
|
||||
inc pc
|
||||
let typ = c.types[c.code[pc].regBx - wordExcess]
|
||||
putIntoReg(regs[ra], loadAny(regs[rb].node.strVal, typ))
|
||||
of opcMarshalStore:
|
||||
decodeB(rkNode)
|
||||
inc pc
|
||||
let typ = c.types[c.code[pc].regBx - wordExcess]
|
||||
createStrKeepNode(regs[ra])
|
||||
if regs[ra].node.strVal.isNil: regs[ra].node.strVal = newStringOfCap(1000)
|
||||
storeAny(regs[ra].node.strVal, typ, regs[rb].regToNode)
|
||||
inc pc
|
||||
|
||||
proc execute(c: PCtx, start: int): PNode =
|
||||
|
|
@ -1363,9 +1408,11 @@ var
|
|||
globalCtx: PCtx
|
||||
|
||||
proc setupGlobalCtx(module: PSym) =
|
||||
if globalCtx.isNil: globalCtx = newCtx(module)
|
||||
else: refresh(globalCtx, module)
|
||||
registerAdditionalOps(globalCtx)
|
||||
if globalCtx.isNil:
|
||||
globalCtx = newCtx(module)
|
||||
registerAdditionalOps(globalCtx)
|
||||
else:
|
||||
refresh(globalCtx, module)
|
||||
|
||||
proc myOpen(module: PSym): PPassContext =
|
||||
#var c = newEvalContext(module, emRepl)
|
||||
|
|
@ -1404,6 +1451,7 @@ 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 =
|
||||
result = evalConstExprAux(module, nil, e, emConst)
|
||||
|
|
@ -1435,7 +1483,9 @@ proc evalMacroCall*(module: PSym, n, nOrig: PNode, sym: PSym): PNode =
|
|||
# immediate macros can bypass any type and arity checking so we check the
|
||||
# arity here too:
|
||||
if sym.typ.len > n.safeLen and sym.typ.len > 1:
|
||||
globalError(n.info, "got $#, but expected $# argument(s)" % [$ <n.safeLen, $ <sym.typ.len])
|
||||
globalError(n.info, "in call '$#' got $#, but expected $# argument(s)" % [
|
||||
n.renderTree,
|
||||
$ <n.safeLen, $ <sym.typ.len])
|
||||
|
||||
setupGlobalCtx(module)
|
||||
var c = globalCtx
|
||||
|
|
@ -1463,6 +1513,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
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@ const
|
|||
byteExcess* = 128 # we use excess-K for immediates
|
||||
wordExcess* = 32768
|
||||
|
||||
MaxLoopIterations* = 500_000 # max iterations of all loops
|
||||
MaxLoopIterations* = 1500_000 # max iterations of all loops
|
||||
|
||||
|
||||
type
|
||||
|
|
@ -66,7 +66,8 @@ type
|
|||
opcMulSet, opcPlusSet, opcMinusSet, opcSymdiffSet, opcConcatStr,
|
||||
opcContainsSet, opcRepr, opcSetLenStr, opcSetLenSeq,
|
||||
opcSwap, opcIsNil, opcOf, opcIs,
|
||||
opcSubStr, opcParseFloat, opcConv, opcCast, opcQuit, opcReset,
|
||||
opcSubStr, opcParseFloat, opcConv, opcCast,
|
||||
opcQuit, opcReset,
|
||||
opcNarrowS, opcNarrowU,
|
||||
|
||||
opcAddStrCh,
|
||||
|
|
@ -132,7 +133,8 @@ type
|
|||
opcLdImmInt, # dest = immediate value
|
||||
opcNBindSym,
|
||||
opcSetType, # dest.typ = types[Bx]
|
||||
opcTypeTrait
|
||||
opcTypeTrait,
|
||||
opcMarshalLoad, opcMarshalStore
|
||||
|
||||
TBlock* = object
|
||||
label*: PSym
|
||||
|
|
@ -213,6 +215,7 @@ proc newCtx*(module: PSym): PCtx =
|
|||
proc refresh*(c: PCtx, module: PSym) =
|
||||
c.module = module
|
||||
c.prc = PProc(blocks: @[])
|
||||
c.loopIterations = MaxLoopIterations
|
||||
|
||||
proc registerCallback*(c: PCtx; name: string; callback: VmCallback) =
|
||||
c.callbacks.add((name, callback))
|
||||
|
|
@ -220,7 +223,8 @@ proc registerCallback*(c: PCtx; name: string; callback: VmCallback) =
|
|||
const
|
||||
firstABxInstr* = opcTJmp
|
||||
largeInstrs* = { # instructions which use 2 int32s instead of 1:
|
||||
opcSubStr, opcConv, opcCast, opcNewSeq, opcOf}
|
||||
opcSubStr, opcConv, opcCast, opcNewSeq, opcOf,
|
||||
opcMarshalLoad, opcMarshalStore}
|
||||
slotSomeTemp* = slotTempUnknown
|
||||
relativeJumps* = {opcTJmp, opcFJmp, opcJmp, opcJmpBack}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,13 +1,13 @@
|
|||
#
|
||||
#
|
||||
# The Nim Compiler
|
||||
# (c) Copyright 2013 Andreas Rumpf
|
||||
# (c) Copyright 2015 Andreas Rumpf
|
||||
#
|
||||
# See the file "copying.txt", included in this
|
||||
# distribution, for details about the copyright.
|
||||
#
|
||||
|
||||
import ast, types, msgs, osproc, streams, options
|
||||
import ast, types, msgs, osproc, streams, options, idents
|
||||
|
||||
proc readOutput(p: Process): string =
|
||||
result = ""
|
||||
|
|
@ -19,11 +19,14 @@ proc readOutput(p: Process): string =
|
|||
discard p.waitForExit
|
||||
|
||||
proc opGorge*(cmd, input: string): string =
|
||||
var p = startCmd(cmd)
|
||||
if input.len != 0:
|
||||
p.inputStream.write(input)
|
||||
p.inputStream.close()
|
||||
result = p.readOutput
|
||||
try:
|
||||
var p = startProcess(cmd, options={poEvalCommand})
|
||||
if input.len != 0:
|
||||
p.inputStream.write(input)
|
||||
p.inputStream.close()
|
||||
result = p.readOutput
|
||||
except IOError, OSError:
|
||||
result = ""
|
||||
|
||||
proc opSlurp*(file: string, info: TLineInfo, module: PSym): string =
|
||||
try:
|
||||
|
|
@ -36,3 +39,124 @@ proc opSlurp*(file: string, info: TLineInfo, module: PSym): string =
|
|||
except IOError:
|
||||
localError(info, errCannotOpenFile, file)
|
||||
result = ""
|
||||
|
||||
proc atomicTypeX(name: string; t: PType; info: TLineInfo): PNode =
|
||||
let sym = newSym(skType, getIdent(name), t.owner, info)
|
||||
sym.typ = t
|
||||
result = newSymNode(sym)
|
||||
result.typ = t
|
||||
|
||||
proc mapTypeToAst(t: PType, info: TLineInfo; allowRecursion=false): PNode
|
||||
|
||||
proc mapTypeToBracket(name: string; t: PType; info: TLineInfo): PNode =
|
||||
result = newNodeIT(nkBracketExpr, info, t)
|
||||
result.add atomicTypeX(name, t, info)
|
||||
for i in 0 .. < t.len:
|
||||
if t.sons[i] == nil:
|
||||
let void = atomicTypeX("void", t, info)
|
||||
void.typ = newType(tyEmpty, t.owner)
|
||||
result.add void
|
||||
else:
|
||||
result.add mapTypeToAst(t.sons[i], info)
|
||||
|
||||
proc mapTypeToAst(t: PType, info: TLineInfo; allowRecursion=false): PNode =
|
||||
template atomicType(name): expr = atomicTypeX(name, t, info)
|
||||
|
||||
case t.kind
|
||||
of tyNone: result = atomicType("none")
|
||||
of tyBool: result = atomicType("bool")
|
||||
of tyChar: result = atomicType("char")
|
||||
of tyNil: result = atomicType("nil")
|
||||
of tyExpr: result = atomicType("expr")
|
||||
of tyStmt: result = atomicType("stmt")
|
||||
of tyEmpty: result = atomicType"void"
|
||||
of tyArrayConstr, tyArray:
|
||||
result = newNodeIT(nkBracketExpr, info, t)
|
||||
result.add atomicType("array")
|
||||
result.add mapTypeToAst(t.sons[0], info)
|
||||
result.add mapTypeToAst(t.sons[1], info)
|
||||
of tyTypeDesc:
|
||||
if t.base != nil:
|
||||
result = newNodeIT(nkBracketExpr, info, t)
|
||||
result.add atomicType("typeDesc")
|
||||
result.add mapTypeToAst(t.base, info)
|
||||
else:
|
||||
result = atomicType"typeDesc"
|
||||
of tyGenericInvocation:
|
||||
result = newNodeIT(nkBracketExpr, info, t)
|
||||
for i in 0 .. < t.len:
|
||||
result.add mapTypeToAst(t.sons[i], info)
|
||||
of tyGenericInst, tyGenericBody, tyOrdinal, tyUserTypeClassInst:
|
||||
result = mapTypeToAst(t.lastSon, info)
|
||||
of tyDistinct:
|
||||
if allowRecursion:
|
||||
result = mapTypeToBracket("distinct", t, info)
|
||||
else:
|
||||
result = atomicType(t.sym.name.s)
|
||||
of tyGenericParam, tyForward: result = atomicType(t.sym.name.s)
|
||||
of tyObject:
|
||||
if allowRecursion:
|
||||
result = newNodeIT(nkObjectTy, info, t)
|
||||
if t.sons[0] == nil:
|
||||
result.add ast.emptyNode
|
||||
else:
|
||||
result.add mapTypeToAst(t.sons[0], info)
|
||||
result.add copyTree(t.n)
|
||||
else:
|
||||
result = atomicType(t.sym.name.s)
|
||||
of tyEnum:
|
||||
result = newNodeIT(nkEnumTy, info, t)
|
||||
result.add copyTree(t.n)
|
||||
of tyTuple: result = mapTypeToBracket("tuple", t, info)
|
||||
of tySet: result = mapTypeToBracket("set", t, info)
|
||||
of tyPtr: result = mapTypeToBracket("ptr", t, info)
|
||||
of tyRef: result = mapTypeToBracket("ref", t, info)
|
||||
of tyVar: result = mapTypeToBracket("var", t, info)
|
||||
of tySequence: result = mapTypeToBracket("seq", t, info)
|
||||
of tyProc: result = mapTypeToBracket("proc", t, info)
|
||||
of tyOpenArray: result = mapTypeToBracket("openArray", t, info)
|
||||
of tyRange:
|
||||
result = newNodeIT(nkBracketExpr, info, t)
|
||||
result.add atomicType("range")
|
||||
result.add t.n.sons[0].copyTree
|
||||
result.add t.n.sons[1].copyTree
|
||||
of tyPointer: result = atomicType"pointer"
|
||||
of tyString: result = atomicType"string"
|
||||
of tyCString: result = atomicType"cstring"
|
||||
of tyInt: result = atomicType"int"
|
||||
of tyInt8: result = atomicType"int8"
|
||||
of tyInt16: result = atomicType"int16"
|
||||
of tyInt32: result = atomicType"int32"
|
||||
of tyInt64: result = atomicType"int64"
|
||||
of tyFloat: result = atomicType"float"
|
||||
of tyFloat32: result = atomicType"float32"
|
||||
of tyFloat64: result = atomicType"float64"
|
||||
of tyFloat128: result = atomicType"float128"
|
||||
of tyUInt: result = atomicType"uint"
|
||||
of tyUInt8: result = atomicType"uint8"
|
||||
of tyUInt16: result = atomicType"uint16"
|
||||
of tyUInt32: result = atomicType"uint32"
|
||||
of tyUInt64: result = atomicType"uint64"
|
||||
of tyBigNum: result = atomicType"bignum"
|
||||
of tyConst: result = mapTypeToBracket("const", t, info)
|
||||
of tyMutable: result = mapTypeToBracket("mutable", t, info)
|
||||
of tyVarargs: result = mapTypeToBracket("varargs", t, info)
|
||||
of tyIter: result = mapTypeToBracket("iter", t, info)
|
||||
of tyProxy: result = atomicType"error"
|
||||
of tyBuiltInTypeClass: result = mapTypeToBracket("builtinTypeClass", t, info)
|
||||
of tyUserTypeClass:
|
||||
result = mapTypeToBracket("concept", t, info)
|
||||
result.add t.n.copyTree
|
||||
of tyCompositeTypeClass: result = mapTypeToBracket("compositeTypeClass", t, info)
|
||||
of tyAnd: result = mapTypeToBracket("and", t, info)
|
||||
of tyOr: result = mapTypeToBracket("or", t, info)
|
||||
of tyNot: result = mapTypeToBracket("not", t, info)
|
||||
of tyAnything: result = atomicType"anything"
|
||||
of tyStatic, tyFromExpr, tyFieldAccessor:
|
||||
result = newNodeIT(nkBracketExpr, info, t)
|
||||
result.add atomicType("static")
|
||||
if t.n != nil:
|
||||
result.add t.n.copyTree
|
||||
|
||||
proc opMapTypeToAst*(t: PType; info: TLineInfo): PNode =
|
||||
result = mapTypeToAst(t, info, true)
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
#
|
||||
#
|
||||
# The Nim Compiler
|
||||
# (c) Copyright 2014 Andreas Rumpf
|
||||
# (c) Copyright 2015 Andreas Rumpf
|
||||
#
|
||||
# See the file "copying.txt", included in this
|
||||
# distribution, for details about the copyright.
|
||||
|
|
@ -76,6 +76,11 @@ proc codeListing(c: PCtx, result: var string, start=0; last = -1) =
|
|||
elif opc in {opcLdConst, opcAsgnConst}:
|
||||
result.addf("\t$#\tr$#, $#", ($opc).substr(3), x.regA,
|
||||
c.constants[x.regBx-wordExcess].renderTree)
|
||||
elif opc in {opcMarshalLoad, opcMarshalStore}:
|
||||
let y = c.code[i+1]
|
||||
result.addf("\t$#\tr$#, r$#, $#", ($opc).substr(3), x.regA, x.regB,
|
||||
c.types[y.regBx-wordExcess].typeToString)
|
||||
inc i
|
||||
else:
|
||||
result.addf("\t$#\tr$#, $#", ($opc).substr(3), x.regA, x.regBx-wordExcess)
|
||||
result.add("\t#")
|
||||
|
|
@ -368,7 +373,7 @@ proc sameConstant*(a, b: PNode): bool =
|
|||
case a.kind
|
||||
of nkSym: result = a.sym == b.sym
|
||||
of nkIdent: result = a.ident.id == b.ident.id
|
||||
of nkCharLit..nkInt64Lit: result = a.intVal == b.intVal
|
||||
of nkCharLit..nkUInt64Lit: result = a.intVal == b.intVal
|
||||
of nkFloatLit..nkFloat64Lit: result = a.floatVal == b.floatVal
|
||||
of nkStrLit..nkTripleStrLit: result = a.strVal == b.strVal
|
||||
of nkType, nkNilLit: result = a.typ == b.typ
|
||||
|
|
@ -604,7 +609,8 @@ proc genNarrowU(c: PCtx; n: PNode; dest: TDest) =
|
|||
let t = skipTypes(n.typ, abstractVar-{tyTypeDesc})
|
||||
# uint is uint64 in the VM, we we only need to mask the result for
|
||||
# other unsigned types:
|
||||
if t.kind in {tyUInt8..tyUInt32, tyInt8..tyInt32}:
|
||||
if t.kind in {tyUInt8..tyUInt32, tyInt8..tyInt32} or
|
||||
(t.kind == tyInt and t.size == 4):
|
||||
c.gABC(n, opcNarrowU, dest, TRegister(t.size*8))
|
||||
|
||||
proc genBinaryABCnarrow(c: PCtx; n: PNode; dest: var TDest; opc: TOpcode) =
|
||||
|
|
@ -695,8 +701,7 @@ proc genCard(c: PCtx; n: PNode; dest: var TDest) =
|
|||
c.gABC(n, opcCard, dest, tmp)
|
||||
c.freeTemp(tmp)
|
||||
|
||||
proc genMagic(c: PCtx; n: PNode; dest: var TDest) =
|
||||
let m = n.sons[0].sym.magic
|
||||
proc genMagic(c: PCtx; n: PNode; dest: var TDest; m: TMagic) =
|
||||
case m
|
||||
of mAnd: c.genAndOr(n, opcFJmp, dest)
|
||||
of mOr: c.genAndOr(n, opcTJmp, dest)
|
||||
|
|
@ -741,9 +746,9 @@ proc genMagic(c: PCtx; n: PNode; dest: var TDest) =
|
|||
c.gABC(n, opcNewStr, dest, tmp)
|
||||
c.freeTemp(tmp)
|
||||
# XXX buggy
|
||||
of mLengthOpenArray, mLengthArray, mLengthSeq:
|
||||
of mLengthOpenArray, mLengthArray, mLengthSeq, mXLenSeq:
|
||||
genUnaryABI(c, n, dest, opcLenSeq)
|
||||
of mLengthStr:
|
||||
of mLengthStr, mXLenStr:
|
||||
genUnaryABI(c, n, dest, opcLenStr)
|
||||
of mIncl, mExcl:
|
||||
unused(n, dest)
|
||||
|
|
@ -790,7 +795,7 @@ proc genMagic(c: PCtx; n: PNode; dest: var TDest) =
|
|||
genUnaryABC(c, n, dest, opcUnaryMinusInt)
|
||||
genNarrow(c, n, dest)
|
||||
of mUnaryMinusF64: genUnaryABC(c, n, dest, opcUnaryMinusFloat)
|
||||
of mUnaryPlusI, mUnaryPlusI64, mUnaryPlusF64: gen(c, n.sons[1], dest)
|
||||
of mUnaryPlusI, mUnaryPlusF64: gen(c, n.sons[1], dest)
|
||||
of mBitnotI, mBitnotI64:
|
||||
genUnaryABC(c, n, dest, opcBitnotInt)
|
||||
genNarrowU(c, n, dest)
|
||||
|
|
@ -893,7 +898,8 @@ proc genMagic(c: PCtx; n: PNode; dest: var TDest) =
|
|||
of mHigh:
|
||||
if dest < 0: dest = c.getTemp(n.typ)
|
||||
let tmp = c.genx(n.sons[1])
|
||||
if n.sons[1].typ.skipTypes(abstractVar-{tyTypeDesc}).kind == tyString:
|
||||
case n.sons[1].typ.skipTypes(abstractVar-{tyTypeDesc}).kind:
|
||||
of tyString, tyCString:
|
||||
c.gABI(n, opcLenStr, dest, tmp, 1)
|
||||
else:
|
||||
c.gABI(n, opcLenSeq, dest, tmp, 1)
|
||||
|
|
@ -948,7 +954,12 @@ proc genMagic(c: PCtx; n: PNode; dest: var TDest) =
|
|||
of mNFloatVal: genUnaryABC(c, n, dest, opcNFloatVal)
|
||||
of mNSymbol: genUnaryABC(c, n, dest, opcNSymbol)
|
||||
of mNIdent: genUnaryABC(c, n, dest, opcNIdent)
|
||||
of mNGetType: genUnaryABC(c, n, dest, opcNGetType)
|
||||
of mNGetType:
|
||||
let tmp = c.genx(n.sons[1])
|
||||
if dest < 0: dest = c.getTemp(n.typ)
|
||||
c.gABC(n, opcNGetType, dest, tmp, if n[0].sym.name.s == "typeKind": 1 else: 0)
|
||||
c.freeTemp(tmp)
|
||||
#genUnaryABC(c, n, dest, opcNGetType)
|
||||
of mNStrVal: genUnaryABC(c, n, dest, opcNStrVal)
|
||||
of mNSetIntVal:
|
||||
unused(n, dest)
|
||||
|
|
@ -1001,7 +1012,8 @@ proc genMagic(c: PCtx; n: PNode; dest: var TDest) =
|
|||
if dest < 0: dest = c.getTemp(n.typ)
|
||||
c.gABC(n, opcCallSite, dest)
|
||||
of mNGenSym: genBinaryABC(c, n, dest, opcGenSym)
|
||||
of mMinI, mMaxI, mMinI64, mMaxI64, mAbsF64, mMinF64, mMaxF64, mAbsI, mAbsI64:
|
||||
of mMinI, mMaxI, mAbsF64, mMinF64, mMaxF64, mAbsI,
|
||||
mAbsI64, mDotDot:
|
||||
c.genCall(n, dest)
|
||||
of mExpandToAst:
|
||||
if n.len != 2:
|
||||
|
|
@ -1020,6 +1032,22 @@ proc genMagic(c: PCtx; n: PNode; dest: var TDest) =
|
|||
# mGCref, mGCunref,
|
||||
internalError(n.info, "cannot generate code for: " & $m)
|
||||
|
||||
proc genMarshalLoad(c: PCtx, n: PNode, dest: var TDest) =
|
||||
## Signature: proc to*[T](data: string): T
|
||||
if dest < 0: dest = c.getTemp(n.typ)
|
||||
var tmp = c.genx(n.sons[1])
|
||||
c.gABC(n, opcMarshalLoad, dest, tmp)
|
||||
c.gABx(n, opcMarshalLoad, 0, c.genType(n.typ))
|
||||
c.freeTemp(tmp)
|
||||
|
||||
proc genMarshalStore(c: PCtx, n: PNode, dest: var TDest) =
|
||||
## Signature: proc `$$`*[T](x: T): string
|
||||
if dest < 0: dest = c.getTemp(n.typ)
|
||||
var tmp = c.genx(n.sons[1])
|
||||
c.gABC(n, opcMarshalStore, dest, tmp)
|
||||
c.gABx(n, opcMarshalStore, 0, c.genType(n.sons[1].typ))
|
||||
c.freeTemp(tmp)
|
||||
|
||||
const
|
||||
atomicTypes = {tyBool, tyChar,
|
||||
tyExpr, tyStmt, tyTypeDesc, tyStatic,
|
||||
|
|
@ -1244,8 +1272,8 @@ proc genGlobalInit(c: PCtx; n: PNode; s: PSym) =
|
|||
c.globals.add(getNullValue(s.typ, n.info))
|
||||
s.position = c.globals.len
|
||||
# This is rather hard to support, due to the laziness of the VM code
|
||||
# generator. See tests/compile/tmacro2 for why this is necesary:
|
||||
# var decls{.compileTime.}: seq[PNimrodNode] = @[]
|
||||
# generator. See tests/compile/tmacro2 for why this is necessary:
|
||||
# var decls{.compileTime.}: seq[NimNode] = @[]
|
||||
let dest = c.getTemp(s.typ)
|
||||
c.gABx(n, opcLdGlobal, dest, s.position)
|
||||
let tmp = c.genx(s.ast)
|
||||
|
|
@ -1356,7 +1384,7 @@ proc getNullValue(typ: PType, info: TLineInfo): PNode =
|
|||
of tyCString, tyString:
|
||||
result = newNodeIT(nkStrLit, info, t)
|
||||
of tyVar, tyPointer, tyPtr, tySequence, tyExpr,
|
||||
tyStmt, tyTypeDesc, tyStatic, tyRef:
|
||||
tyStmt, tyTypeDesc, tyStatic, tyRef, tyNil:
|
||||
result = newNodeIT(nkNilLit, info, t)
|
||||
of tyProc:
|
||||
if t.callConv != ccClosure:
|
||||
|
|
@ -1383,7 +1411,7 @@ proc getNullValue(typ: PType, info: TLineInfo): PNode =
|
|||
addSon(result, getNullValue(t.sons[i], info))
|
||||
of tySet:
|
||||
result = newNodeIT(nkCurly, info, t)
|
||||
else: internalError("getNullValue: " & $t.kind)
|
||||
else: internalError(info, "getNullValue: " & $t.kind)
|
||||
|
||||
proc ldNullOpcode(t: PType): TOpcode =
|
||||
if fitsRegister(t): opcLdNullReg else: opcLdNull
|
||||
|
|
@ -1525,6 +1553,15 @@ proc matches(s: PSym; x: string): bool =
|
|||
dec L
|
||||
result = true
|
||||
|
||||
proc matches(s: PSym; y: varargs[string]): bool =
|
||||
var s = s
|
||||
var L = y.len-1
|
||||
while L >= 0:
|
||||
if s == nil or y[L].cmpIgnoreStyle(s.name.s) != 0: return false
|
||||
s = if sfFromGeneric in s.flags: s.owner.owner else: s.owner
|
||||
dec L
|
||||
result = true
|
||||
|
||||
proc procIsCallback(c: PCtx; s: PSym): bool =
|
||||
if s.offset < -1: return true
|
||||
var i = -2
|
||||
|
|
@ -1562,8 +1599,17 @@ proc gen(c: PCtx; n: PNode; dest: var TDest; flags: TGenFlags = {}) =
|
|||
else:
|
||||
internalError(n.info, "cannot generate code for: " & s.name.s)
|
||||
of nkCallKinds:
|
||||
if n.sons[0].kind == nkSym and n.sons[0].sym.magic != mNone:
|
||||
genMagic(c, n, dest)
|
||||
if n.sons[0].kind == nkSym:
|
||||
let s = n.sons[0].sym
|
||||
if s.magic != mNone:
|
||||
genMagic(c, n, dest, s.magic)
|
||||
elif matches(s, "stdlib", "marshal", "to"):
|
||||
genMarshalLoad(c, n, dest)
|
||||
elif matches(s, "stdlib", "marshal", "$$"):
|
||||
genMarshalStore(c, n, dest)
|
||||
else:
|
||||
genCall(c, n, dest)
|
||||
clearDest(c, n, dest)
|
||||
else:
|
||||
genCall(c, n, dest)
|
||||
clearDest(c, n, dest)
|
||||
|
|
@ -1602,7 +1648,8 @@ proc gen(c: PCtx; n: PNode; dest: var TDest; flags: TGenFlags = {}) =
|
|||
genBreak(c, n)
|
||||
of nkTryStmt: genTry(c, n, dest)
|
||||
of nkStmtList:
|
||||
unused(n, dest)
|
||||
#unused(n, dest)
|
||||
# XXX Fix this bug properly, lexim triggers it
|
||||
for x in n: gen(c, x)
|
||||
of nkStmtListExpr:
|
||||
let L = n.len-1
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
#
|
||||
#
|
||||
# The Nim Compiler
|
||||
# (c) Copyright 2014 Andreas Rumpf
|
||||
# (c) Copyright 2015 Andreas Rumpf
|
||||
#
|
||||
# See the file "copying.txt", included in this
|
||||
# distribution, for details about the copyright.
|
||||
|
|
|
|||
283
compiler/vmmarshal.nim
Normal file
283
compiler/vmmarshal.nim
Normal file
|
|
@ -0,0 +1,283 @@
|
|||
#
|
||||
#
|
||||
# The Nim Compiler
|
||||
# (c) Copyright 2015 Andreas Rumpf
|
||||
#
|
||||
# See the file "copying.txt", included in this
|
||||
# distribution, for details about the copyright.
|
||||
#
|
||||
|
||||
## Implements marshaling for the VM.
|
||||
|
||||
import streams, json, intsets, tables, ast, astalgo, idents, types, msgs
|
||||
|
||||
proc ptrToInt(x: PNode): int {.inline.} =
|
||||
result = cast[int](x) # don't skip alignment
|
||||
|
||||
proc getField(n: PNode; position: int): PSym =
|
||||
case n.kind
|
||||
of nkRecList:
|
||||
for i in countup(0, sonsLen(n) - 1):
|
||||
result = getField(n.sons[i], position)
|
||||
if result != nil: return
|
||||
of nkRecCase:
|
||||
result = getField(n.sons[0], position)
|
||||
if result != nil: return
|
||||
for i in countup(1, sonsLen(n) - 1):
|
||||
case n.sons[i].kind
|
||||
of nkOfBranch, nkElse:
|
||||
result = getField(lastSon(n.sons[i]), position)
|
||||
if result != nil: return
|
||||
else: internalError(n.info, "getField(record case branch)")
|
||||
of nkSym:
|
||||
if n.sym.position == position: result = n.sym
|
||||
else: discard
|
||||
|
||||
proc storeAny(s: var string; t: PType; a: PNode; stored: var IntSet)
|
||||
|
||||
proc storeObj(s: var string; typ: PType; x: PNode; stored: var IntSet) =
|
||||
internalAssert x.kind in {nkObjConstr, nkPar}
|
||||
let start = ord(x.kind == nkObjConstr)
|
||||
for i in countup(start, sonsLen(x) - 1):
|
||||
if i > start: s.add(", ")
|
||||
var it = x.sons[i]
|
||||
if it.kind == nkExprColonExpr:
|
||||
internalAssert it.sons[0].kind == nkSym
|
||||
let field = it.sons[0].sym
|
||||
s.add(escapeJson(field.name.s))
|
||||
s.add(": ")
|
||||
storeAny(s, field.typ, it.sons[1], stored)
|
||||
elif typ.n != nil:
|
||||
let field = getField(typ.n, i)
|
||||
s.add(escapeJson(field.name.s))
|
||||
s.add(": ")
|
||||
storeAny(s, field.typ, it, stored)
|
||||
|
||||
proc skipColon*(n: PNode): PNode =
|
||||
result = n
|
||||
if n.kind == nkExprColonExpr:
|
||||
result = n.sons[1]
|
||||
|
||||
proc storeAny(s: var string; t: PType; a: PNode; stored: var IntSet) =
|
||||
case t.kind
|
||||
of tyNone: assert false
|
||||
of tyBool: s.add($(a.intVal != 0))
|
||||
of tyChar:
|
||||
let ch = char(a.intVal)
|
||||
if ch < '\128':
|
||||
s.add(escapeJson($ch))
|
||||
else:
|
||||
s.add($int(ch))
|
||||
of tyArray, tySequence:
|
||||
if t.kind == tySequence and a.kind == nkNilLit: s.add("null")
|
||||
else:
|
||||
s.add("[")
|
||||
for i in 0 .. a.len-1:
|
||||
if i > 0: s.add(", ")
|
||||
storeAny(s, t.elemType, a[i], stored)
|
||||
s.add("]")
|
||||
of tyTuple:
|
||||
s.add("{")
|
||||
for i in 0.. <t.len:
|
||||
if i > 0: s.add(", ")
|
||||
s.add("\"Field" & $i)
|
||||
s.add("\": ")
|
||||
storeAny(s, t.sons[i], a[i].skipColon, stored)
|
||||
s.add("}")
|
||||
of tyObject:
|
||||
s.add("{")
|
||||
storeObj(s, t, a, stored)
|
||||
s.add("}")
|
||||
of tySet:
|
||||
s.add("[")
|
||||
for i in 0.. <a.len:
|
||||
if i > 0: s.add(", ")
|
||||
if a[i].kind == nkRange:
|
||||
var x = copyNode(a[i][0])
|
||||
storeAny(s, t.lastSon, x, stored)
|
||||
while x.intVal+1 <= a[i][1].intVal:
|
||||
s.add(", ")
|
||||
storeAny(s, t.lastSon, x, stored)
|
||||
inc x.intVal
|
||||
else:
|
||||
storeAny(s, t.lastSon, a[i], stored)
|
||||
s.add("]")
|
||||
of tyRange, tyGenericInst: storeAny(s, t.lastSon, a, stored)
|
||||
of tyEnum:
|
||||
# we need a slow linear search because of enums with holes:
|
||||
for e in items(t.n):
|
||||
if e.sym.position == a.intVal:
|
||||
s.add e.sym.name.s.escapeJson
|
||||
break
|
||||
of tyPtr, tyRef:
|
||||
var x = a
|
||||
if isNil(x) or x.kind == nkNilLit: s.add("null")
|
||||
elif stored.containsOrIncl(x.ptrToInt):
|
||||
# already stored, so we simply write out the pointer as an int:
|
||||
s.add($x.ptrToInt)
|
||||
else:
|
||||
# else as a [value, key] pair:
|
||||
# (reversed order for convenient x[0] access!)
|
||||
s.add("[")
|
||||
s.add($x.ptrToInt)
|
||||
s.add(", ")
|
||||
storeAny(s, t.lastSon, a, stored)
|
||||
s.add("]")
|
||||
of tyString, tyCString:
|
||||
if a.kind == nkNilLit or a.strVal.isNil: s.add("null")
|
||||
else: s.add(escapeJson(a.strVal))
|
||||
of tyInt..tyInt64, tyUInt..tyUInt64: s.add($a.intVal)
|
||||
of tyFloat..tyFloat128: s.add($a.floatVal)
|
||||
else:
|
||||
internalError a.info, "cannot marshal at compile-time " & t.typeToString
|
||||
|
||||
proc storeAny*(s: var string; t: PType; a: PNode) =
|
||||
var stored = initIntSet()
|
||||
storeAny(s, t, a, stored)
|
||||
|
||||
proc loadAny(p: var JsonParser, t: PType,
|
||||
tab: var Table[BiggestInt, PNode]): PNode =
|
||||
case t.kind
|
||||
of tyNone: assert false
|
||||
of tyBool:
|
||||
case p.kind
|
||||
of jsonFalse: result = newIntNode(nkIntLit, 0)
|
||||
of jsonTrue: result = newIntNode(nkIntLit, 1)
|
||||
else: raiseParseErr(p, "'true' or 'false' expected for a bool")
|
||||
next(p)
|
||||
of tyChar:
|
||||
if p.kind == jsonString:
|
||||
var x = p.str
|
||||
if x.len == 1:
|
||||
result = newIntNode(nkIntLit, ord(x[0]))
|
||||
next(p)
|
||||
return
|
||||
elif p.kind == jsonInt:
|
||||
result = newIntNode(nkIntLit, getInt(p))
|
||||
next(p)
|
||||
return
|
||||
raiseParseErr(p, "string of length 1 expected for a char")
|
||||
of tyEnum:
|
||||
if p.kind == jsonString:
|
||||
for e in items(t.n):
|
||||
if e.sym.name.s == p.str:
|
||||
result = newIntNode(nkIntLit, e.sym.position)
|
||||
next(p)
|
||||
return
|
||||
raiseParseErr(p, "string expected for an enum")
|
||||
of tyArray:
|
||||
if p.kind != jsonArrayStart: raiseParseErr(p, "'[' expected for an array")
|
||||
next(p)
|
||||
result = newNode(nkBracket)
|
||||
while p.kind != jsonArrayEnd and p.kind != jsonEof:
|
||||
result.add loadAny(p, t.elemType, tab)
|
||||
if p.kind == jsonArrayEnd: next(p)
|
||||
else: raiseParseErr(p, "']' end of array expected")
|
||||
of tySequence:
|
||||
case p.kind
|
||||
of jsonNull:
|
||||
result = newNode(nkNilLit)
|
||||
next(p)
|
||||
of jsonArrayStart:
|
||||
next(p)
|
||||
result = newNode(nkBracket)
|
||||
while p.kind != jsonArrayEnd and p.kind != jsonEof:
|
||||
result.add loadAny(p, t.elemType, tab)
|
||||
if p.kind == jsonArrayEnd: next(p)
|
||||
else: raiseParseErr(p, "")
|
||||
else:
|
||||
raiseParseErr(p, "'[' expected for a seq")
|
||||
of tyTuple:
|
||||
if p.kind != jsonObjectStart: raiseParseErr(p, "'{' expected for an object")
|
||||
next(p)
|
||||
result = newNode(nkPar)
|
||||
var i = 0
|
||||
while p.kind != jsonObjectEnd and p.kind != jsonEof:
|
||||
if p.kind != jsonString:
|
||||
raiseParseErr(p, "string expected for a field name")
|
||||
next(p)
|
||||
if i >= t.len:
|
||||
raiseParseErr(p, "too many fields to tuple type " & typeToString(t))
|
||||
result.add loadAny(p, t.sons[i], tab)
|
||||
inc i
|
||||
if p.kind == jsonObjectEnd: next(p)
|
||||
else: raiseParseErr(p, "'}' end of object expected")
|
||||
of tyObject:
|
||||
if p.kind != jsonObjectStart: raiseParseErr(p, "'{' expected for an object")
|
||||
next(p)
|
||||
result = newNode(nkPar)
|
||||
result.sons = @[]
|
||||
while p.kind != jsonObjectEnd and p.kind != jsonEof:
|
||||
if p.kind != jsonString:
|
||||
raiseParseErr(p, "string expected for a field name")
|
||||
let field = lookupInRecord(t.n, getIdent(p.str))
|
||||
if field.isNil:
|
||||
raiseParseErr(p, "unknown field for object of type " & typeToString(t))
|
||||
next(p)
|
||||
if field.position >= result.sons.len:
|
||||
setLen(result.sons, field.position+1)
|
||||
result.sons[field.position] = loadAny(p, field.typ, tab)
|
||||
if p.kind == jsonObjectEnd: next(p)
|
||||
else: raiseParseErr(p, "'}' end of object expected")
|
||||
of tySet:
|
||||
if p.kind != jsonArrayStart: raiseParseErr(p, "'[' expected for a set")
|
||||
next(p)
|
||||
result = newNode(nkCurly)
|
||||
while p.kind != jsonArrayEnd and p.kind != jsonEof:
|
||||
result.add loadAny(p, t.lastSon, tab)
|
||||
next(p)
|
||||
if p.kind == jsonArrayEnd: next(p)
|
||||
else: raiseParseErr(p, "']' end of array expected")
|
||||
of tyPtr, tyRef:
|
||||
case p.kind
|
||||
of jsonNull:
|
||||
result = newNode(nkNilLit)
|
||||
next(p)
|
||||
of jsonInt:
|
||||
result = tab[p.getInt]
|
||||
if result.isNil:
|
||||
raiseParseErr(p, "cannot load object with address " & $p.getInt)
|
||||
next(p)
|
||||
of jsonArrayStart:
|
||||
next(p)
|
||||
if p.kind == jsonInt:
|
||||
let idx = p.getInt
|
||||
next(p)
|
||||
result = loadAny(p, t.lastSon, tab)
|
||||
tab[idx] = result
|
||||
else: raiseParseErr(p, "index for ref type expected")
|
||||
if p.kind == jsonArrayEnd: next(p)
|
||||
else: raiseParseErr(p, "']' end of ref-address pair expected")
|
||||
else: raiseParseErr(p, "int for pointer type expected")
|
||||
of tyString, tyCString:
|
||||
case p.kind
|
||||
of jsonNull:
|
||||
result = newNode(nkNilLit)
|
||||
next(p)
|
||||
of jsonString:
|
||||
result = newStrNode(nkStrLit, p.str)
|
||||
next(p)
|
||||
else: raiseParseErr(p, "string expected")
|
||||
of tyInt..tyInt64, tyUInt..tyUInt64:
|
||||
if p.kind == jsonInt:
|
||||
result = newIntNode(nkIntLit, getInt(p))
|
||||
next(p)
|
||||
return
|
||||
raiseParseErr(p, "int expected")
|
||||
of tyFloat..tyFloat128:
|
||||
if p.kind == jsonFloat:
|
||||
result = newFloatNode(nkFloatLit, getFloat(p))
|
||||
next(p)
|
||||
return
|
||||
raiseParseErr(p, "float expected")
|
||||
of tyRange, tyGenericInst: result = loadAny(p, t.lastSon, tab)
|
||||
else:
|
||||
internalError "cannot marshal at compile-time " & t.typeToString
|
||||
|
||||
proc loadAny*(s: string; t: PType): PNode =
|
||||
var tab = initTable[BiggestInt, PNode]()
|
||||
var p: JsonParser
|
||||
open(p, newStringStream(s), "unknown file")
|
||||
next(p)
|
||||
result = loadAny(p, t, tab)
|
||||
close(p)
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
#
|
||||
#
|
||||
# The Nim Compiler
|
||||
# (c) Copyright 2014 Andreas Rumpf
|
||||
# (c) Copyright 2015 Andreas Rumpf
|
||||
#
|
||||
# See the file "copying.txt", included in this
|
||||
# distribution, for details about the copyright.
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue