fixes #1940; code breakage! stricter template evaluation

This commit is contained in:
Araq
2015-03-07 02:30:51 +01:00
parent c914532c26
commit d58212ccc5
6 changed files with 479 additions and 435 deletions

View File

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

View File

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

File diff suppressed because it is too large Load Diff

View File

@@ -1232,7 +1232,7 @@ proc paramTypesMatchAux(m: var TCandidate, f, argType: PType,
elif f.kind == tyStatic:
return arg.typ.n
else:
return argOrig
return argSemantized # argOrig
if r != isNone and f.isInlineIterator:
var inlined = newTypeS(tyStatic, c)

View File

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

View File

@@ -0,0 +1,47 @@
# bug #1940
discard """
nimout: '''===
merge (A) with (B)
merge (A B) with (C)
merge (A B C) with (D)
merge (A B C D) with (E)
merge (A B C D E) with (F)
==='''
"""
type SqlStmt = tuple
sql: string
parts: int
proc sql(q: string): SqlStmt =
result.sql = q
result.parts = 1
template `&%%`(x, y: SqlStmt): SqlStmt =
const a = x
const b = y
static:
#echo "some merge"
echo "merge (", a.sql, ") with (", b.sql, ")"
const newSql = a.sql & " " & b.sql
const newParts = a.parts + b.parts
SqlStmt((sql: newSql, parts: newParts))
static:
echo "==="
let c =(sql("A") &%%
sql("B")) &%%
sql("C") &%%
sql("D") &%%
sql("E") &%%
sql("F")
echo c.sql
static:
echo "==="