Merge branch 'devel' into jpoirier-realtimeGCTest

This commit is contained in:
Simon Hafner 2015-05-13 12:06:05 -05:00
commit 9c4a74637d
250 changed files with 10952 additions and 8848 deletions

1
.gitignore vendored
View file

@ -41,4 +41,3 @@ xcuserdata/
/testresults.html /testresults.html
/testresults.json /testresults.json
testament.db testament.db
/csources/

3
.gitmodules vendored Normal file
View file

@ -0,0 +1,3 @@
[submodule "csources"]
path = csources
url = ../../nim-lang/csources.git

4
build.sh Normal file → Executable file
View file

@ -2,8 +2,8 @@
set -e set -e
set -x set -x
if [ ! -d "csources" ]; then if [ ! -e csources/.git ]; then
git clone --depth 1 https://github.com/nim-lang/csources.git git submodule update --init --depth 1
fi fi
cd "csources" cd "csources"

View file

@ -1,7 +1,7 @@
# #
# #
# The Nim Compiler # The Nim Compiler
# (c) Copyright 2013 Andreas Rumpf # (c) Copyright 2015 Andreas Rumpf
# #
# See the file "copying.txt", included in this # See the file "copying.txt", included in this
# distribution, for details about the copyright. # distribution, for details about the copyright.
@ -296,6 +296,7 @@ const
sfCompileToCpp* = sfInfixCall # compile the module as C++ code sfCompileToCpp* = sfInfixCall # compile the module as C++ code
sfCompileToObjc* = sfNamedParamCall # compile the module as Objective-C code sfCompileToObjc* = sfNamedParamCall # compile the module as Objective-C code
sfExperimental* = sfOverriden # module uses the .experimental switch sfExperimental* = sfOverriden # module uses the .experimental switch
sfGoto* = sfOverriden # var is used for 'goto' code generation
const const
# getting ready for the future expr/stmt merge # getting ready for the future expr/stmt merge
@ -472,7 +473,7 @@ type
# T and I here can bind to both typedesc and static types # T and I here can bind to both typedesc and static types
# before this is determined, we'll consider them to be a # before this is determined, we'll consider them to be a
# wildcard type. # wildcard type.
tfGuarded # guarded pointer tfHasAsgn # type has overloaded assignment operator
tfBorrowDot # distinct type borrows '.' tfBorrowDot # distinct type borrows '.'
TTypeFlags* = set[TTypeFlag] TTypeFlags* = set[TTypeFlag]
@ -529,19 +530,20 @@ type
TMagic* = enum # symbols that require compiler magic: TMagic* = enum # symbols that require compiler magic:
mNone, mNone,
mDefined, mDefinedInScope, mCompiles, mDefined, mDefinedInScope, mCompiles,
mLow, mHigh, mSizeOf, mTypeTrait, mIs, mOf, mAddr, mTypeOf, mRoof, mLow, mHigh, mSizeOf, mTypeTrait, mIs, mOf, mAddr, mTypeOf, mRoof, mPlugin,
mEcho, mShallowCopy, mSlurp, mStaticExec, mEcho, mShallowCopy, mSlurp, mStaticExec,
mParseExprToAst, mParseStmtToAst, mExpandToAst, mQuoteAst, mParseExprToAst, mParseStmtToAst, mExpandToAst, mQuoteAst,
mUnaryLt, mInc, mDec, mOrd, mNew, mNewFinalize, mNewSeq, mLengthOpenArray, mUnaryLt, mInc, mDec, mOrd, mNew, mNewFinalize, mNewSeq, mLengthOpenArray,
mLengthStr, mLengthArray, mLengthSeq, mIncl, mExcl, mCard, mChr, mGCref, mLengthStr, mLengthArray, mLengthSeq, mXLenStr, mXLenSeq,
mGCunref, mIncl, mExcl, mCard, mChr,
mGCref, mGCunref,
mAddI, mSubI, mMulI, mDivI, mModI, mAddI64, mSubI64, mMulI64, mAddI, mSubI, mMulI, mDivI, mModI, mAddI64, mSubI64, mMulI64,
mDivI64, mModI64, mSucc, mPred, mDivI64, mModI64, mSucc, mPred,
mAddF64, mSubF64, mMulF64, mDivF64, mAddF64, mSubF64, mMulF64, mDivF64,
mShrI, mShlI, mBitandI, mBitorI, mBitxorI, mMinI, mMaxI, mShrI, mShlI, mBitandI, mBitorI, mBitxorI, mMinI, mMaxI,
mShrI64, mShlI64, mBitandI64, mBitorI64, mBitxorI64, mMinI64, mMaxI64, mShrI64, mShlI64, mBitandI64, mBitorI64, mBitxorI64,
mMinF64, mMaxF64, mAddU, mSubU, mMulU, mMinF64, mMaxF64, mAddU, mSubU, mMulU,
mDivU, mModU, mEqI, mLeI, mDivU, mModU, mEqI, mLeI,
mLtI, mLtI,
@ -550,7 +552,7 @@ type
mEqEnum, mLeEnum, mLtEnum, mEqCh, mLeCh, mLtCh, mEqB, mLeB, mLtB, mEqRef, mEqEnum, mLeEnum, mLtEnum, mEqCh, mLeCh, mLtCh, mEqB, mLeB, mLtB, mEqRef,
mEqUntracedRef, mLePtr, mLtPtr, mEqCString, mXor, mEqProc, mUnaryMinusI, mEqUntracedRef, mLePtr, mLtPtr, mEqCString, mXor, mEqProc, mUnaryMinusI,
mUnaryMinusI64, mAbsI, mAbsI64, mNot, mUnaryMinusI64, mAbsI, mAbsI64, mNot,
mUnaryPlusI, mBitnotI, mUnaryPlusI64, mUnaryPlusI, mBitnotI,
mBitnotI64, mUnaryPlusF64, mUnaryMinusF64, mAbsF64, mZe8ToI, mZe8ToI64, mBitnotI64, mUnaryPlusF64, mUnaryMinusF64, mAbsF64, mZe8ToI, mZe8ToI64,
mZe16ToI, mZe16ToI64, mZe32ToI64, mZeIToI64, mToU8, mToU16, mToU32, mZe16ToI, mZe16ToI64, mZe32ToI64, mZeIToI64, mToU8, mToU16, mToU32,
mToFloat, mToBiggestFloat, mToInt, mToBiggestInt, mCharToStr, mBoolToStr, mToFloat, mToBiggestFloat, mToInt, mToBiggestInt, mCharToStr, mBoolToStr,
@ -589,11 +591,12 @@ type
const const
ctfeWhitelist* = {mNone, mUnaryLt, mSucc, ctfeWhitelist* = {mNone, mUnaryLt, mSucc,
mPred, mInc, mDec, mOrd, mLengthOpenArray, mPred, mInc, mDec, mOrd, mLengthOpenArray,
mLengthStr, mLengthArray, mLengthSeq, mIncl, mExcl, mCard, mChr, mLengthStr, mLengthArray, mLengthSeq, mXLenStr, mXLenSeq,
mIncl, mExcl, mCard, mChr,
mAddI, mSubI, mMulI, mDivI, mModI, mAddI64, mSubI64, mMulI64, mAddI, mSubI, mMulI, mDivI, mModI, mAddI64, mSubI64, mMulI64,
mDivI64, mModI64, mAddF64, mSubF64, mMulF64, mDivF64, mDivI64, mModI64, mAddF64, mSubF64, mMulF64, mDivF64,
mShrI, mShlI, mBitandI, mBitorI, mBitxorI, mMinI, mMaxI, mShrI, mShlI, mBitandI, mBitorI, mBitxorI, mMinI, mMaxI,
mShrI64, mShlI64, mBitandI64, mBitorI64, mBitxorI64, mMinI64, mMaxI64, mShrI64, mShlI64, mBitandI64, mBitorI64, mBitxorI64,
mMinF64, mMaxF64, mAddU, mSubU, mMulU, mMinF64, mMaxF64, mAddU, mSubU, mMulU,
mDivU, mModU, mEqI, mLeI, mDivU, mModU, mEqI, mLeI,
mLtI, mLtI,
@ -602,7 +605,7 @@ const
mEqEnum, mLeEnum, mLtEnum, mEqCh, mLeCh, mLtCh, mEqB, mLeB, mLtB, mEqRef, mEqEnum, mLeEnum, mLtEnum, mEqCh, mLeCh, mLtCh, mEqB, mLeB, mLtB, mEqRef,
mEqProc, mEqUntracedRef, mLePtr, mLtPtr, mEqCString, mXor, mUnaryMinusI, mEqProc, mEqUntracedRef, mLePtr, mLtPtr, mEqCString, mXor, mUnaryMinusI,
mUnaryMinusI64, mAbsI, mAbsI64, mNot, mUnaryMinusI64, mAbsI, mAbsI64, mNot,
mUnaryPlusI, mBitnotI, mUnaryPlusI64, mUnaryPlusI, mBitnotI,
mBitnotI64, mUnaryPlusF64, mUnaryMinusF64, mAbsF64, mZe8ToI, mZe8ToI64, mBitnotI64, mUnaryPlusF64, mUnaryMinusF64, mAbsF64, mZe8ToI, mZe8ToI64,
mZe16ToI, mZe16ToI64, mZe32ToI64, mZeIToI64, mToU8, mToU16, mToU32, mZe16ToI, mZe16ToI64, mZe32ToI64, mZeIToI64, mToU8, mToU16, mToU32,
mToFloat, mToBiggestFloat, mToInt, mToBiggestInt, mCharToStr, mBoolToStr, mToFloat, mToBiggestFloat, mToInt, mToBiggestInt, mCharToStr, mBoolToStr,
@ -685,8 +688,8 @@ type
s*: TStorageLoc s*: TStorageLoc
flags*: TLocFlags # location's flags flags*: TLocFlags # location's flags
t*: PType # type of location t*: PType # type of location
r*: PRope # rope value of location (code generators) r*: Rope # rope value of location (code generators)
heapRoot*: PRope # keeps track of the enclosing heap object that heapRoot*: Rope # keeps track of the enclosing heap object that
# owns this location (required by GC algorithms # owns this location (required by GC algorithms
# employing heap snapshots or sliding views) # employing heap snapshots or sliding views)
@ -698,7 +701,7 @@ type
kind*: TLibKind kind*: TLibKind
generated*: bool # needed for the backends: generated*: bool # needed for the backends:
isOverriden*: bool isOverriden*: bool
name*: PRope name*: Rope
path*: PNode # can be a string literal! path*: PNode # can be a string literal!
TInstantiation* = object TInstantiation* = object
@ -728,7 +731,8 @@ type
typScope*: PScope typScope*: PScope
of routineKinds: of routineKinds:
procInstCache*: seq[PInstantiation] 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: of skModule:
# modules keep track of the generic symbols they use from other modules. # modules keep track of the generic symbols they use from other modules.
# this is because in incremental compilation, when a module is about to # this is because in incremental compilation, when a module is about to
@ -794,8 +798,8 @@ type
# for enum types a list of symbols # for enum types a list of symbols
# for tyInt it can be the int literal # for tyInt it can be the int literal
# for procs and tyGenericBody, it's the # for procs and tyGenericBody, it's the
# the body of the user-defined type class
# formal param list # formal param list
# for concepts, the concept body
# else: unused # else: unused
owner*: PSym # the 'owner' of the type owner*: PSym # the 'owner' of the type
sym*: PSym # types have the sym associated with them sym*: PSym # types have the sym associated with them
@ -804,6 +808,7 @@ type
# mean that there is no destructor. # mean that there is no destructor.
# see instantiateDestructor in semdestruct.nim # see instantiateDestructor in semdestruct.nim
deepCopy*: PSym # overriden 'deepCopy' operation deepCopy*: PSym # overriden 'deepCopy' operation
assignment*: PSym # overriden '=' operator
size*: BiggestInt # the size of the type in bytes size*: BiggestInt # the size of the type in bytes
# -1 means that the size is unkwown # -1 means that the size is unkwown
align*: int16 # the type's alignment requirements align*: int16 # the type's alignment requirements
@ -1168,7 +1173,9 @@ proc newType*(kind: TTypeKind, owner: PSym): PType =
result.lockLevel = UnspecifiedLockLevel result.lockLevel = UnspecifiedLockLevel
when debugIds: when debugIds:
registerId(result) registerId(result)
#if result.id < 2000: #if result.id == 92231:
# echo "KNID ", kind
# writeStackTrace()
# messageOut(typeKindToStr[kind] & ' has id: ' & toString(result.id)) # messageOut(typeKindToStr[kind] & ' has id: ' & toString(result.id))
proc mergeLoc(a: var TLoc, b: TLoc) = proc mergeLoc(a: var TLoc, b: TLoc) =
@ -1218,6 +1225,7 @@ proc assignType*(dest, src: PType) =
dest.align = src.align dest.align = src.align
dest.destructor = src.destructor dest.destructor = src.destructor
dest.deepCopy = src.deepCopy dest.deepCopy = src.deepCopy
dest.assignment = src.assignment
dest.lockLevel = src.lockLevel dest.lockLevel = src.lockLevel
# this fixes 'type TLock = TSysLock': # this fixes 'type TLock = TSysLock':
if src.sym != nil: if src.sym != nil:
@ -1314,6 +1322,13 @@ proc skipTypes*(t: PType, kinds: TTypeKinds): PType =
result = t result = t
while result.kind in kinds: result = lastSon(result) 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.} = proc isGCedMem*(t: PType): bool {.inline.} =
result = t.kind in {tyString, tyRef, tySequence} or result = t.kind in {tyString, tyRef, tySequence} or
t.kind == tyProc and t.callConv == ccClosure t.kind == tyProc and t.callConv == ccClosure
@ -1334,6 +1349,13 @@ proc propagateToOwner*(owner, elem: PType) =
if elem.isMetaType: if elem.isMetaType:
owner.flags.incl tfHasMeta owner.flags.incl tfHasMeta
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, if owner.kind notin {tyProc, tyGenericInst, tyGenericBody,
tyGenericInvocation}: tyGenericInvocation}:
let elemB = elem.skipTypes({tyGenericInst}) let elemB = elem.skipTypes({tyGenericInst})

View file

@ -15,13 +15,13 @@ import
ast, hashes, intsets, strutils, options, msgs, ropes, idents, rodutils ast, hashes, intsets, strutils, options, msgs, ropes, idents, rodutils
proc hashNode*(p: RootRef): THash 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 # Convert a tree into its YAML representation; this is used by the
# YAML code generator and it is invaluable for debugging purposes. # YAML code generator and it is invaluable for debugging purposes.
# If maxRecDepht <> -1 then it won't print the whole graph. # If maxRecDepht <> -1 then it won't print the whole graph.
proc typeToYaml*(n: PType, indent: int = 0, maxRecDepth: int = - 1): PRope proc typeToYaml*(n: PType, indent: int = 0, maxRecDepth: int = - 1): Rope
proc symToYaml*(n: PSym, indent: int = 0, maxRecDepth: int = - 1): PRope proc symToYaml*(n: PSym, indent: int = 0, maxRecDepth: int = - 1): Rope
proc lineInfoToStr*(info: TLineInfo): PRope proc lineInfoToStr*(info: TLineInfo): Rope
# ----------------------- node sets: --------------------------------------- # ----------------------- node sets: ---------------------------------------
proc objectSetContains*(t: TObjectSet, obj: RootRef): bool proc objectSetContains*(t: TObjectSet, obj: RootRef): bool
@ -203,9 +203,9 @@ proc mustRehash(length, counter: int): bool =
assert(length > counter) assert(length > counter)
result = (length * 2 < counter * 3) or (length - counter < 4) result = (length * 2 < counter * 3) or (length - counter < 4)
proc rspaces(x: int): PRope = proc rspaces(x: int): Rope =
# returns x spaces # returns x spaces
result = toRope(spaces(x)) result = rope(spaces(x))
proc toYamlChar(c: char): string = proc toYamlChar(c: char): string =
case c case c
@ -213,7 +213,7 @@ proc toYamlChar(c: char): string =
of '\'', '\"', '\\': result = '\\' & c of '\'', '\"', '\\': result = '\\' & c
else: 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 # We have to split long strings into many ropes. Otherwise
# this could trigger InternalError(111). See the ropes module for # this could trigger InternalError(111). See the ropes module for
# further information. # further information.
@ -224,227 +224,227 @@ proc makeYamlString*(s: string): PRope =
if (i + 1) mod MaxLineLength == 0: if (i + 1) mod MaxLineLength == 0:
add(res, '\"') add(res, '\"')
add(res, "\n") add(res, "\n")
app(result, toRope(res)) add(result, rope(res))
res = "\"" # reset res = "\"" # reset
add(res, toYamlChar(s[i])) add(res, toYamlChar(s[i]))
add(res, '\"') 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 == {}: if flags == {}:
result = toRope("[]") result = rope("[]")
else: else:
result = nil result = nil
for x in items(flags): for x in items(flags):
if result != nil: app(result, ", ") if result != nil: add(result, ", ")
app(result, makeYamlString($x)) add(result, makeYamlString($x))
result = con("[", con(result, "]")) result = "[" & result & "]"
proc lineInfoToStr(info: TLineInfo): PRope = proc lineInfoToStr(info: TLineInfo): Rope =
result = ropef("[$1, $2, $3]", [makeYamlString(toFilename(info)), result = "[$1, $2, $3]" % [makeYamlString(toFilename(info)),
toRope(toLinenumber(info)), rope(toLinenumber(info)),
toRope(toColumn(info))]) rope(toColumn(info))]
proc treeToYamlAux(n: PNode, marker: var IntSet, proc treeToYamlAux(n: PNode, marker: var IntSet,
indent, maxRecDepth: int): PRope indent, maxRecDepth: int): Rope
proc symToYamlAux(n: PSym, marker: var IntSet, proc symToYamlAux(n: PSym, marker: var IntSet,
indent, maxRecDepth: int): PRope indent, maxRecDepth: int): Rope
proc typeToYamlAux(n: PType, marker: var IntSet, proc typeToYamlAux(n: PType, marker: var IntSet,
indent, maxRecDepth: int): PRope indent, maxRecDepth: int): Rope
proc strTableToYaml(n: TStrTable, marker: var IntSet, indent: int, proc strTableToYaml(n: TStrTable, marker: var IntSet, indent: int,
maxRecDepth: int): PRope = maxRecDepth: int): Rope =
var istr = rspaces(indent + 2) var istr = rspaces(indent + 2)
result = toRope("[") result = rope("[")
var mycount = 0 var mycount = 0
for i in countup(0, high(n.data)): for i in countup(0, high(n.data)):
if n.data[i] != nil: if n.data[i] != nil:
if mycount > 0: app(result, ",") if mycount > 0: add(result, ",")
appf(result, "$N$1$2", addf(result, "$N$1$2",
[istr, symToYamlAux(n.data[i], marker, indent + 2, maxRecDepth - 1)]) [istr, symToYamlAux(n.data[i], marker, indent + 2, maxRecDepth - 1)])
inc(mycount) inc(mycount)
if mycount > 0: appf(result, "$N$1", [rspaces(indent)]) if mycount > 0: addf(result, "$N$1", [rspaces(indent)])
app(result, "]") add(result, "]")
assert(mycount == n.counter) 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 # array of (name, value) pairs
var istr = rspaces(indent + 2) var istr = rspaces(indent + 2)
result = toRope("{") result = rope("{")
var i = 0 var i = 0
while i <= high(c): while i <= high(c):
if i > 0: app(result, ",") if i > 0: add(result, ",")
appf(result, "$N$1\"$2\": $3", [istr, c[i], c[i + 1]]) addf(result, "$N$1\"$2\": $3", [istr, c[i], c[i + 1]])
inc(i, 2) inc(i, 2)
appf(result, "$N$1}", [rspaces(indent)]) addf(result, "$N$1}", [rspaces(indent)])
proc symToYamlAux(n: PSym, marker: var IntSet, indent: int, proc symToYamlAux(n: PSym, marker: var IntSet, indent: int,
maxRecDepth: int): PRope = maxRecDepth: int): Rope =
if n == nil: if n == nil:
result = toRope("null") result = rope("null")
elif containsOrIncl(marker, n.id): elif containsOrIncl(marker, n.id):
result = ropef("\"$1 @$2\"", [toRope(n.name.s), toRope( result = "\"$1 @$2\"" % [rope(n.name.s), rope(
strutils.toHex(cast[ByteAddress](n), sizeof(n) * 2))]) strutils.toHex(cast[ByteAddress](n), sizeof(n) * 2))]
else: else:
var ast = treeToYamlAux(n.ast, marker, indent + 2, maxRecDepth - 1) var ast = treeToYamlAux(n.ast, marker, indent + 2, maxRecDepth - 1)
result = ropeConstr(indent, [toRope("kind"), result = ropeConstr(indent, [rope("kind"),
makeYamlString($n.kind), makeYamlString($n.kind),
toRope("name"), makeYamlString(n.name.s), rope("name"), makeYamlString(n.name.s),
toRope("typ"), typeToYamlAux(n.typ, marker, rope("typ"), typeToYamlAux(n.typ, marker,
indent + 2, maxRecDepth - 1), indent + 2, maxRecDepth - 1),
toRope("info"), lineInfoToStr(n.info), rope("info"), lineInfoToStr(n.info),
toRope("flags"), flagsToStr(n.flags), rope("flags"), flagsToStr(n.flags),
toRope("magic"), makeYamlString($n.magic), rope("magic"), makeYamlString($n.magic),
toRope("ast"), ast, toRope("options"), rope("ast"), ast, rope("options"),
flagsToStr(n.options), toRope("position"), flagsToStr(n.options), rope("position"),
toRope(n.position)]) rope(n.position)])
proc typeToYamlAux(n: PType, marker: var IntSet, indent: int, proc typeToYamlAux(n: PType, marker: var IntSet, indent: int,
maxRecDepth: int): PRope = maxRecDepth: int): Rope =
if n == nil: if n == nil:
result = toRope("null") result = rope("null")
elif containsOrIncl(marker, n.id): elif containsOrIncl(marker, n.id):
result = ropef("\"$1 @$2\"", [toRope($n.kind), toRope( result = "\"$1 @$2\"" % [rope($n.kind), rope(
strutils.toHex(cast[ByteAddress](n), sizeof(n) * 2))]) strutils.toHex(cast[ByteAddress](n), sizeof(n) * 2))]
else: else:
if sonsLen(n) > 0: if sonsLen(n) > 0:
result = toRope("[") result = rope("[")
for i in countup(0, sonsLen(n) - 1): for i in countup(0, sonsLen(n) - 1):
if i > 0: app(result, ",") if i > 0: add(result, ",")
appf(result, "$N$1$2", [rspaces(indent + 4), typeToYamlAux(n.sons[i], addf(result, "$N$1$2", [rspaces(indent + 4), typeToYamlAux(n.sons[i],
marker, indent + 4, maxRecDepth - 1)]) marker, indent + 4, maxRecDepth - 1)])
appf(result, "$N$1]", [rspaces(indent + 2)]) addf(result, "$N$1]", [rspaces(indent + 2)])
else: else:
result = toRope("null") result = rope("null")
result = ropeConstr(indent, [toRope("kind"), result = ropeConstr(indent, [rope("kind"),
makeYamlString($n.kind), makeYamlString($n.kind),
toRope("sym"), symToYamlAux(n.sym, marker, rope("sym"), symToYamlAux(n.sym, marker,
indent + 2, maxRecDepth - 1), toRope("n"), treeToYamlAux(n.n, marker, indent + 2, maxRecDepth - 1), rope("n"), treeToYamlAux(n.n, marker,
indent + 2, maxRecDepth - 1), toRope("flags"), flagsToStr(n.flags), indent + 2, maxRecDepth - 1), rope("flags"), flagsToStr(n.flags),
toRope("callconv"), rope("callconv"),
makeYamlString(CallingConvToStr[n.callConv]), makeYamlString(CallingConvToStr[n.callConv]),
toRope("size"), toRope(n.size), rope("size"), rope(n.size),
toRope("align"), toRope(n.align), rope("align"), rope(n.align),
toRope("sons"), result]) rope("sons"), result])
proc treeToYamlAux(n: PNode, marker: var IntSet, indent: int, proc treeToYamlAux(n: PNode, marker: var IntSet, indent: int,
maxRecDepth: int): PRope = maxRecDepth: int): Rope =
if n == nil: if n == nil:
result = toRope("null") result = rope("null")
else: else:
var istr = rspaces(indent + 2) var istr = rspaces(indent + 2)
result = ropef("{$N$1\"kind\": $2", [istr, makeYamlString($n.kind)]) result = "{$N$1\"kind\": $2" % [istr, makeYamlString($n.kind)]
if maxRecDepth != 0: 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 case n.kind
of nkCharLit..nkInt64Lit: 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: of nkFloatLit, nkFloat32Lit, nkFloat64Lit:
appf(result, ",$N$1\"floatVal\": $2", addf(result, ",$N$1\"floatVal\": $2",
[istr, toRope(n.floatVal.toStrMaxPrecision)]) [istr, rope(n.floatVal.toStrMaxPrecision)])
of nkStrLit..nkTripleStrLit: of nkStrLit..nkTripleStrLit:
if n.strVal.isNil: if n.strVal.isNil:
appf(result, ",$N$1\"strVal\": null", [istr]) addf(result, ",$N$1\"strVal\": null", [istr])
else: else:
appf(result, ",$N$1\"strVal\": $2", [istr, makeYamlString(n.strVal)]) addf(result, ",$N$1\"strVal\": $2", [istr, makeYamlString(n.strVal)])
of nkSym: of nkSym:
appf(result, ",$N$1\"sym\": $2", addf(result, ",$N$1\"sym\": $2",
[istr, symToYamlAux(n.sym, marker, indent + 2, maxRecDepth)]) [istr, symToYamlAux(n.sym, marker, indent + 2, maxRecDepth)])
of nkIdent: of nkIdent:
if n.ident != nil: 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: else:
appf(result, ",$N$1\"ident\": null", [istr]) addf(result, ",$N$1\"ident\": null", [istr])
else: else:
if sonsLen(n) > 0: if sonsLen(n) > 0:
appf(result, ",$N$1\"sons\": [", [istr]) addf(result, ",$N$1\"sons\": [", [istr])
for i in countup(0, sonsLen(n) - 1): for i in countup(0, sonsLen(n) - 1):
if i > 0: app(result, ",") if i > 0: add(result, ",")
appf(result, "$N$1$2", [rspaces(indent + 4), treeToYamlAux(n.sons[i], addf(result, "$N$1$2", [rspaces(indent + 4), treeToYamlAux(n.sons[i],
marker, indent + 4, maxRecDepth - 1)]) marker, indent + 4, maxRecDepth - 1)])
appf(result, "$N$1]", [istr]) addf(result, "$N$1]", [istr])
appf(result, ",$N$1\"typ\": $2", addf(result, ",$N$1\"typ\": $2",
[istr, typeToYamlAux(n.typ, marker, indent + 2, maxRecDepth)]) [istr, typeToYamlAux(n.typ, marker, indent + 2, maxRecDepth)])
appf(result, "$N$1}", [rspaces(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() var marker = initIntSet()
result = treeToYamlAux(n, marker, indent, maxRecDepth) 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() var marker = initIntSet()
result = typeToYamlAux(n, marker, indent, maxRecDepth) 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() var marker = initIntSet()
result = symToYamlAux(n, marker, indent, maxRecDepth) result = symToYamlAux(n, marker, indent, maxRecDepth)
proc debugTree(n: PNode, indent: int, maxRecDepth: int; renderType=false): PRope proc debugTree*(n: PNode, indent: int, maxRecDepth: int; renderType=false): Rope
proc debugType(n: PType, maxRecDepth=100): PRope = proc debugType(n: PType, maxRecDepth=100): Rope =
if n == nil: if n == nil:
result = toRope("null") result = rope("null")
else: else:
result = toRope($n.kind) result = rope($n.kind)
if n.sym != nil: if n.sym != nil:
app(result, " ") add(result, " ")
app(result, n.sym.name.s) add(result, n.sym.name.s)
if n.kind in IntegralTypes and n.n != nil: if n.kind in IntegralTypes and n.n != nil:
app(result, ", node: ") add(result, ", node: ")
app(result, debugTree(n.n, 2, maxRecDepth-1, renderType=true)) add(result, debugTree(n.n, 2, maxRecDepth-1, renderType=true))
if (n.kind != tyString) and (sonsLen(n) > 0) and maxRecDepth != 0: if (n.kind != tyString) and (sonsLen(n) > 0) and maxRecDepth != 0:
app(result, "(") add(result, "(")
for i in countup(0, sonsLen(n) - 1): for i in countup(0, sonsLen(n) - 1):
if i > 0: app(result, ", ") if i > 0: add(result, ", ")
if n.sons[i] == nil: if n.sons[i] == nil:
app(result, "null") add(result, "null")
else: 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: if n.kind == tyObject and n.n != nil:
app(result, ", node: ") add(result, ", node: ")
app(result, debugTree(n.n, 2, maxRecDepth-1, renderType=true)) add(result, debugTree(n.n, 2, maxRecDepth-1, renderType=true))
app(result, ")") add(result, ")")
proc debugTree(n: PNode, indent: int, maxRecDepth: int; proc debugTree(n: PNode, indent: int, maxRecDepth: int;
renderType=false): PRope = renderType=false): Rope =
if n == nil: if n == nil:
result = toRope("null") result = rope("null")
else: else:
var istr = rspaces(indent + 2) var istr = rspaces(indent + 2)
result = ropef("{$N$1\"kind\": $2", result = "{$N$1\"kind\": $2" %
[istr, makeYamlString($n.kind)]) [istr, makeYamlString($n.kind)]
if maxRecDepth != 0: if maxRecDepth != 0:
case n.kind case n.kind
of nkCharLit..nkUInt64Lit: 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: of nkFloatLit, nkFloat32Lit, nkFloat64Lit:
appf(result, ",$N$1\"floatVal\": $2", addf(result, ",$N$1\"floatVal\": $2",
[istr, toRope(n.floatVal.toStrMaxPrecision)]) [istr, rope(n.floatVal.toStrMaxPrecision)])
of nkStrLit..nkTripleStrLit: of nkStrLit..nkTripleStrLit:
if n.strVal.isNil: if n.strVal.isNil:
appf(result, ",$N$1\"strVal\": null", [istr]) addf(result, ",$N$1\"strVal\": null", [istr])
else: else:
appf(result, ",$N$1\"strVal\": $2", [istr, makeYamlString(n.strVal)]) addf(result, ",$N$1\"strVal\": $2", [istr, makeYamlString(n.strVal)])
of nkSym: of nkSym:
appf(result, ",$N$1\"sym\": $2_$3", addf(result, ",$N$1\"sym\": $2_$3",
[istr, toRope(n.sym.name.s), toRope(n.sym.id)]) [istr, rope(n.sym.name.s), rope(n.sym.id)])
# [istr, symToYaml(n.sym, indent, maxRecDepth), # [istr, symToYaml(n.sym, indent, maxRecDepth),
# toRope(n.sym.id)]) # rope(n.sym.id)])
if renderType and n.sym.typ != nil: 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: of nkIdent:
if n.ident != nil: 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: else:
appf(result, ",$N$1\"ident\": null", [istr]) addf(result, ",$N$1\"ident\": null", [istr])
else: else:
if sonsLen(n) > 0: if sonsLen(n) > 0:
appf(result, ",$N$1\"sons\": [", [istr]) addf(result, ",$N$1\"sons\": [", [istr])
for i in countup(0, sonsLen(n) - 1): for i in countup(0, sonsLen(n) - 1):
if i > 0: app(result, ",") if i > 0: add(result, ",")
appf(result, "$N$1$2", [rspaces(indent + 4), debugTree(n.sons[i], addf(result, "$N$1$2", [rspaces(indent + 4), debugTree(n.sons[i],
indent + 4, maxRecDepth - 1, renderType)]) indent + 4, maxRecDepth - 1, renderType)])
appf(result, "$N$1]", [istr]) addf(result, "$N$1]", [istr])
appf(result, ",$N$1\"info\": $2", [istr, lineInfoToStr(n.info)]) addf(result, ",$N$1\"info\": $2", [istr, lineInfoToStr(n.info)])
appf(result, "$N$1}", [rspaces(indent)]) addf(result, "$N$1}", [rspaces(indent)])
proc debug(n: PSym) = proc debug(n: PSym) =
if n == nil: if n == nil:
@ -452,17 +452,16 @@ proc debug(n: PSym) =
elif n.kind == skUnknown: elif n.kind == skUnknown:
msgWriteln("skUnknown") msgWriteln("skUnknown")
else: else:
#writeln(stdout, ropeToStr(symToYaml(n, 0, 1))) #writeln(stdout, $symToYaml(n, 0, 1))
msgWriteln("$1_$2: $3, $4, $5, $6" % [ msgWriteln("$1_$2: $3, $4, $5, $6" % [
n.name.s, $n.id, flagsToStr(n.flags).ropeToStr, n.name.s, $n.id, $flagsToStr(n.flags), $flagsToStr(n.loc.flags),
flagsToStr(n.loc.flags).ropeToStr, lineInfoToStr(n.info).ropeToStr, $lineInfoToStr(n.info), $n.kind])
$n.kind])
proc debug(n: PType) = proc debug(n: PType) =
msgWriteln(ropeToStr(debugType(n))) msgWriteln($debugType(n))
proc debug(n: PNode) = proc debug(n: PNode) =
msgWriteln(ropeToStr(debugTree(n, 0, 100))) msgWriteln($debugTree(n, 0, 100))
const const
EmptySeq = @[] EmptySeq = @[]

View file

@ -259,7 +259,7 @@ proc encodeLoc(w: PRodWriter, loc: TLoc, result: var string) =
pushType(w, loc.t) pushType(w, loc.t)
if loc.r != nil: if loc.r != nil:
add(result, '!') add(result, '!')
encodeStr(ropeToStr(loc.r), result) encodeStr($loc.r, result)
if loc.a != 0: if loc.a != 0:
add(result, '?') add(result, '?')
encodeVInt(loc.a, result) encodeVInt(loc.a, result)
@ -317,7 +317,7 @@ proc encodeLib(w: PRodWriter, lib: PLib, info: TLineInfo, result: var string) =
add(result, '|') add(result, '|')
encodeVInt(ord(lib.kind), result) encodeVInt(ord(lib.kind), result)
add(result, '|') add(result, '|')
encodeStr(ropeToStr(lib.name), result) encodeStr($lib.name, result)
add(result, '|') add(result, '|')
encodeNode(w, info, lib.path, result) encodeNode(w, info, lib.path, result)

View file

@ -19,13 +19,13 @@ proc hasNoInit(call: PNode): bool {.inline.} =
result = call.sons[0].kind == nkSym and sfNoInit in call.sons[0].sym.flags result = call.sons[0].kind == nkSym and sfNoInit in call.sons[0].sym.flags
proc fixupCall(p: BProc, le, ri: PNode, d: var TLoc, proc fixupCall(p: BProc, le, ri: PNode, d: var TLoc,
callee, params: PRope) = callee, params: Rope) =
var pl = con(callee, ~"(", params) var pl = callee & ~"(" & params
# getUniqueType() is too expensive here: # getUniqueType() is too expensive here:
var typ = skipTypes(ri.sons[0].typ, abstractInst) var typ = skipTypes(ri.sons[0].typ, abstractInst)
if typ.sons[0] != nil: if typ.sons[0] != nil:
if isInvalidReturnType(typ.sons[0]): 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: # beware of 'result = p(result)'. We may need to allocate a temporary:
if d.k in {locTemp, locNone} or not leftAppearsOnRightSide(le, ri): if d.k in {locTemp, locNone} or not leftAppearsOnRightSide(le, ri):
# Great, we can use 'd': # Great, we can use 'd':
@ -33,18 +33,18 @@ proc fixupCall(p: BProc, le, ri: PNode, d: var TLoc,
elif d.k notin {locExpr, locTemp} and not hasNoInit(ri): elif d.k notin {locExpr, locTemp} and not hasNoInit(ri):
# reset before pass as 'result' var: # reset before pass as 'result' var:
resetLoc(p, d) resetLoc(p, d)
app(pl, addrLoc(d)) add(pl, addrLoc(d))
app(pl, ~");$n") add(pl, ~");$n")
line(p, cpsStmts, pl) line(p, cpsStmts, pl)
else: else:
var tmp: TLoc var tmp: TLoc
getTemp(p, typ.sons[0], tmp, needsInit=true) getTemp(p, typ.sons[0], tmp, needsInit=true)
app(pl, addrLoc(tmp)) add(pl, addrLoc(tmp))
app(pl, ~");$n") add(pl, ~");$n")
line(p, cpsStmts, pl) line(p, cpsStmts, pl)
genAssignment(p, d, tmp, {}) # no need for deep copying genAssignment(p, d, tmp, {}) # no need for deep copying
else: else:
app(pl, ~")") add(pl, ~")")
if p.module.compileToCpp and lfSingleUse in d.flags: if p.module.compileToCpp and lfSingleUse in d.flags:
# do not generate spurious temporaries for C++! For C we're better off # do not generate spurious temporaries for C++! For C we're better off
# with them to prevent undefined behaviour and because the codegen # with them to prevent undefined behaviour and because the codegen
@ -60,7 +60,7 @@ proc fixupCall(p: BProc, le, ri: PNode, d: var TLoc,
list.r = pl list.r = pl
genAssignment(p, d, list, {}) # no need for deep copying genAssignment(p, d, list, {}) # no need for deep copying
else: else:
app(pl, ~");$n") add(pl, ~");$n")
line(p, cpsStmts, pl) line(p, cpsStmts, pl)
proc isInCurrentFrame(p: BProc, n: PNode): bool = proc isInCurrentFrame(p: BProc, n: PNode): bool =
@ -83,7 +83,7 @@ proc isInCurrentFrame(p: BProc, n: PNode): bool =
result = isInCurrentFrame(p, n.sons[0]) result = isInCurrentFrame(p, n.sons[0])
else: discard else: discard
proc openArrayLoc(p: BProc, n: PNode): PRope = proc openArrayLoc(p: BProc, n: PNode): Rope =
var a: TLoc var a: TLoc
let q = skipConv(n) let q = skipConv(n)
@ -104,28 +104,28 @@ proc openArrayLoc(p: BProc, n: PNode): PRope =
else: else:
"$1->data+($2), ($3)-($2)+1" "$1->data+($2), ($3)-($2)+1"
else: (internalError("openArrayLoc: " & typeToString(a.t)); "") else: (internalError("openArrayLoc: " & typeToString(a.t)); "")
result = ropef(fmt, [rdLoc(a), rdLoc(b), rdLoc(c)]) result = fmt % [rdLoc(a), rdLoc(b), rdLoc(c)]
else: else:
initLocExpr(p, n, a) initLocExpr(p, n, a)
case skipTypes(a.t, abstractVar).kind case skipTypes(a.t, abstractVar).kind
of tyOpenArray, tyVarargs: of tyOpenArray, tyVarargs:
result = ropef("$1, $1Len0", [rdLoc(a)]) result = "$1, $1Len0" % [rdLoc(a)]
of tyString, tySequence: of tyString, tySequence:
if skipTypes(n.typ, abstractInst).kind == tyVar and if skipTypes(n.typ, abstractInst).kind == tyVar and
not compileToCpp(p.module): not compileToCpp(p.module):
result = ropef("(*$1)->data, (*$1)->$2", [a.rdLoc, lenField(p)]) result = "(*$1)->data, (*$1)->$2" % [a.rdLoc, lenField(p)]
else: else:
result = ropef("$1->data, $1->$2", [a.rdLoc, lenField(p)]) result = "$1->data, $1->$2" % [a.rdLoc, lenField(p)]
of tyArray, tyArrayConstr: 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)) 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 var a: TLoc
initLocExpr(p, n.sons[0], a) 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; call: PNode): PRope = proc genArg(p: BProc, n: PNode, param: PSym; call: PNode): Rope =
var a: TLoc var a: TLoc
if n.kind == nkStringToCString: if n.kind == nkStringToCString:
result = genArgStringToCString(p, n) result = genArgStringToCString(p, n)
@ -151,7 +151,7 @@ proc genArg(p: BProc, n: PNode, param: PSym; call: PNode): PRope =
initLocExprSingleUse(p, n, a) initLocExprSingleUse(p, n, a)
result = rdLoc(a) result = rdLoc(a)
proc genArgNoParam(p: BProc, n: PNode): PRope = proc genArgNoParam(p: BProc, n: PNode): Rope =
var a: TLoc var a: TLoc
if n.kind == nkStringToCString: if n.kind == nkStringToCString:
result = genArgStringToCString(p, n) result = genArgStringToCString(p, n)
@ -163,7 +163,7 @@ proc genPrefixCall(p: BProc, le, ri: PNode, d: var TLoc) =
var op: TLoc var op: TLoc
# this is a hotspot in the compiler # this is a hotspot in the compiler
initLocExpr(p, ri.sons[0], op) initLocExpr(p, ri.sons[0], op)
var params: PRope var params: Rope
# getUniqueType() is too expensive here: # getUniqueType() is too expensive here:
var typ = skipTypes(ri.sons[0].typ, abstractInst) var typ = skipTypes(ri.sons[0].typ, abstractInst)
assert(typ.kind == tyProc) assert(typ.kind == tyProc)
@ -171,27 +171,27 @@ proc genPrefixCall(p: BProc, le, ri: PNode, d: var TLoc) =
var length = sonsLen(ri) var length = sonsLen(ri)
for i in countup(1, length - 1): for i in countup(1, length - 1):
if ri.sons[i].typ.isCompileTimeOnly: continue if ri.sons[i].typ.isCompileTimeOnly: continue
if params != nil: app(params, ~", ") if params != nil: add(params, ~", ")
if i < sonsLen(typ): if i < sonsLen(typ):
assert(typ.n.sons[i].kind == nkSym) assert(typ.n.sons[i].kind == nkSym)
app(params, genArg(p, ri.sons[i], typ.n.sons[i].sym, ri)) add(params, genArg(p, ri.sons[i], typ.n.sons[i].sym, ri))
else: else:
app(params, genArgNoParam(p, ri.sons[i])) add(params, genArgNoParam(p, ri.sons[i]))
fixupCall(p, le, ri, d, op.r, params) fixupCall(p, le, ri, d, op.r, params)
proc genClosureCall(p: BProc, le, ri: PNode, d: var TLoc) = 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) result = getClosureType(p.module, t, clHalf)
proc addComma(r: PRope): PRope = proc addComma(r: Rope): Rope =
result = if r == nil: r else: con(r, ~", ") result = if r == nil: r else: r & ~", "
const PatProc = "$1.ClEnv? $1.ClPrc($3$1.ClEnv):(($4)($1.ClPrc))($2)" 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 const PatIter = "$1.ClPrc($3$1.ClEnv)" # we know the env exists
var op: TLoc var op: TLoc
initLocExpr(p, ri.sons[0], op) initLocExpr(p, ri.sons[0], op)
var pl: PRope var pl: Rope
var typ = skipTypes(ri.sons[0].typ, abstractInst) var typ = skipTypes(ri.sons[0].typ, abstractInst)
assert(typ.kind == tyProc) assert(typ.kind == tyProc)
@ -201,19 +201,19 @@ proc genClosureCall(p: BProc, le, ri: PNode, d: var TLoc) =
if ri.sons[i].typ.isCompileTimeOnly: continue if ri.sons[i].typ.isCompileTimeOnly: continue
if i < sonsLen(typ): if i < sonsLen(typ):
assert(typ.n.sons[i].kind == nkSym) assert(typ.n.sons[i].kind == nkSym)
app(pl, genArg(p, ri.sons[i], typ.n.sons[i].sym, ri)) add(pl, genArg(p, ri.sons[i], typ.n.sons[i].sym, ri))
else: else:
app(pl, genArgNoParam(p, ri.sons[i])) add(pl, genArgNoParam(p, ri.sons[i]))
if i < length - 1: app(pl, ~", ") if i < length - 1: add(pl, ~", ")
template genCallPattern {.dirty.} = template genCallPattern {.dirty.} =
lineF(p, cpsStmts, callPattern & ";$n", op.r, pl, pl.addComma, rawProc) lineF(p, cpsStmts, callPattern & ";$n", [op.r, pl, pl.addComma, rawProc])
let rawProc = getRawProcType(p, typ) let rawProc = getRawProcType(p, typ)
let callPattern = if tfIterator in typ.flags: PatIter else: PatProc let callPattern = if tfIterator in typ.flags: PatIter else: PatProc
if typ.sons[0] != nil: if typ.sons[0] != nil:
if isInvalidReturnType(typ.sons[0]): 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: # beware of 'result = p(result)'. We may need to allocate a temporary:
if d.k in {locTemp, locNone} or not leftAppearsOnRightSide(le, ri): if d.k in {locTemp, locNone} or not leftAppearsOnRightSide(le, ri):
# Great, we can use 'd': # Great, we can use 'd':
@ -222,12 +222,12 @@ proc genClosureCall(p: BProc, le, ri: PNode, d: var TLoc) =
elif d.k notin {locExpr, locTemp} and not hasNoInit(ri): elif d.k notin {locExpr, locTemp} and not hasNoInit(ri):
# reset before pass as 'result' var: # reset before pass as 'result' var:
resetLoc(p, d) resetLoc(p, d)
app(pl, addrLoc(d)) add(pl, addrLoc(d))
genCallPattern() genCallPattern()
else: else:
var tmp: TLoc var tmp: TLoc
getTemp(p, typ.sons[0], tmp, needsInit=true) getTemp(p, typ.sons[0], tmp, needsInit=true)
app(pl, addrLoc(tmp)) add(pl, addrLoc(tmp))
genCallPattern() genCallPattern()
genAssignment(p, d, tmp, {}) # no need for deep copying genAssignment(p, d, tmp, {}) # no need for deep copying
else: else:
@ -235,12 +235,12 @@ proc genClosureCall(p: BProc, le, ri: PNode, d: var TLoc) =
assert(d.t != nil) # generate an assignment to d: assert(d.t != nil) # generate an assignment to d:
var list: TLoc var list: TLoc
initLoc(list, locCall, d.t, OnUnknown) 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 genAssignment(p, d, list, {}) # no need for deep copying
else: else:
genCallPattern() genCallPattern()
proc genOtherArg(p: BProc; ri: PNode; i: int; typ: PType): PRope = proc genOtherArg(p: BProc; ri: PNode; i: int; typ: PType): Rope =
if ri.sons[i].typ.isCompileTimeOnly: if ri.sons[i].typ.isCompileTimeOnly:
result = nil result = nil
elif i < sonsLen(typ): elif i < sonsLen(typ):
@ -291,7 +291,25 @@ y.v() --> y.v() is correct
""" """
proc genThisArg(p: BProc; ri: PNode; i: int; typ: PType): PRope = 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'. # 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 # However manual wrappers may also use 'ptr T'. In any case we support both
# for convenience. # for convenience.
@ -301,83 +319,84 @@ proc genThisArg(p: BProc; ri: PNode; i: int; typ: PType): PRope =
# skip the deref: # skip the deref:
var ri = ri[i] var ri = ri[i]
while ri.kind == nkObjDownConv: ri = ri[0] while ri.kind == nkObjDownConv: ri = ri[0]
if typ.sons[i].kind == tyVar: let t = typ.sons[i].skipTypes({tyGenericInst})
if t.kind == tyVar:
let x = if ri.kind == nkHiddenAddr: ri[0] else: ri let x = if ri.kind == nkHiddenAddr: ri[0] else: ri
if x.typ.kind == tyPtr: if x.typ.kind == tyPtr:
result = genArgNoParam(p, x) result = genArgNoParam(p, x)
result.app("->") result.add("->")
elif x.kind in {nkHiddenDeref, nkDerefExpr} and x[0].typ.kind == tyPtr: elif x.kind in {nkHiddenDeref, nkDerefExpr} and x[0].typ.kind == tyPtr:
result = genArgNoParam(p, x[0]) result = genArgNoParam(p, x[0])
result.app("->") result.add("->")
else: else:
result = genArgNoParam(p, x) result = genArgNoParam(p, x)
result.app(".") result.add(".")
elif typ.sons[i].kind == tyPtr: elif t.kind == tyPtr:
if ri.kind in {nkAddr, nkHiddenAddr}: if ri.kind in {nkAddr, nkHiddenAddr}:
result = genArgNoParam(p, ri[0]) result = genArgNoParam(p, ri[0])
result.app(".") result.add(".")
else: else:
result = genArgNoParam(p, ri) result = genArgNoParam(p, ri)
result.app("->") result.add("->")
else: else:
ri = skipAddrDeref(ri)
if ri.kind in {nkAddr, nkHiddenAddr}: ri = ri[0]
result = genArgNoParam(p, ri) #, typ.n.sons[i].sym) result = genArgNoParam(p, ri) #, typ.n.sons[i].sym)
result.app(".") result.add(".")
proc genPatternCall(p: BProc; ri: PNode; pat: string; typ: PType): PRope = proc genPatternCall(p: BProc; ri: PNode; pat: string; typ: PType): Rope =
var i = 0 var i = 0
var j = 1 var j = 1
while i < pat.len: while i < pat.len:
case pat[i] case pat[i]
of '@': of '@':
if j < ri.len: if j < ri.len:
result.app genOtherArg(p, ri, j, typ) result.add genOtherArg(p, ri, j, typ)
for k in j+1 .. < ri.len: for k in j+1 .. < ri.len:
result.app(~", ") result.add(~", ")
result.app genOtherArg(p, ri, k, typ) result.add genOtherArg(p, ri, k, typ)
inc i inc i
of '#': of '#':
if pat[i+1] in {'+', '@'}: if pat[i+1] in {'+', '@'}:
let ri = ri[j] let ri = ri[j]
if ri.kind in nkCallKinds: if ri.kind in nkCallKinds:
let typ = skipTypes(ri.sons[0].typ, abstractInst) let typ = skipTypes(ri.sons[0].typ, abstractInst)
if pat[i+1] == '+': result.app genArgNoParam(p, ri.sons[0]) if pat[i+1] == '+': result.add genArgNoParam(p, ri.sons[0])
result.app(~"(") result.add(~"(")
if 1 < ri.len: if 1 < ri.len:
result.app genOtherArg(p, ri, 1, typ) result.add genOtherArg(p, ri, 1, typ)
for k in j+1 .. < ri.len: for k in j+1 .. < ri.len:
result.app(~", ") result.add(~", ")
result.app genOtherArg(p, ri, k, typ) result.add genOtherArg(p, ri, k, typ)
result.app(~")") result.add(~")")
else: else:
localError(ri.info, "call expression expected for C++ pattern") localError(ri.info, "call expression expected for C++ pattern")
inc i inc i
elif pat[i+1] == '.': elif pat[i+1] == '.':
result.app genThisArg(p, ri, j, typ) result.add genThisArg(p, ri, j, typ)
inc i 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: else:
result.app genOtherArg(p, ri, j, typ) result.add genOtherArg(p, ri, j, typ)
inc j inc j
inc i inc i
of '\'': of '\'':
inc i var idx, stars: int
let stars = i if scanCppGenericSlot(pat, i, idx, stars):
while pat[i] == '*': inc i var t = resolveStarsInCppType(typ, idx, stars)
if pat[i] in Digits: if t == nil: result.add(~"void")
let j = pat[i].ord - '0'.ord else: result.add(getTypeDesc(p.module, t))
var t = typ.sons[j]
for k in 1..i-stars:
if t != nil and t.len > 0:
t = if t.kind == tyGenericInst: t.sons[1] else: t.elemType
if t == nil: result.app(~"void")
else: result.app(getTypeDesc(p.module, t))
inc i
else: else:
let start = i let start = i
while i < pat.len: while i < pat.len:
if pat[i] notin {'@', '#', '\''}: inc(i) if pat[i] notin {'@', '#', '\''}: inc(i)
else: break else: break
if i - 1 >= start: if i - 1 >= start:
app(result, substr(pat, start, i - 1)) add(result, substr(pat, start, i - 1))
proc genInfixCall(p: BProc, le, ri: PNode, d: var TLoc) = proc genInfixCall(p: BProc, le, ri: PNode, d: var TLoc) =
var op, a: TLoc var op, a: TLoc
@ -387,7 +406,7 @@ proc genInfixCall(p: BProc, le, ri: PNode, d: var TLoc) =
assert(typ.kind == tyProc) assert(typ.kind == tyProc)
var length = sonsLen(ri) var length = sonsLen(ri)
assert(sonsLen(typ) == sonsLen(typ.n)) assert(sonsLen(typ) == sonsLen(typ.n))
# don't call 'ropeToStr' here for efficiency: # don't call '$' here for efficiency:
let pat = ri.sons[0].sym.loc.r.data let pat = ri.sons[0].sym.loc.r.data
internalAssert pat != nil internalAssert pat != nil
if pat.contains({'#', '(', '@', '\''}): if pat.contains({'#', '(', '@', '\''}):
@ -410,19 +429,19 @@ proc genInfixCall(p: BProc, le, ri: PNode, d: var TLoc) =
list.r = pl list.r = pl
genAssignment(p, d, list, {}) # no need for deep copying genAssignment(p, d, list, {}) # no need for deep copying
else: else:
app(pl, ~";$n") add(pl, ~";$n")
line(p, cpsStmts, pl) line(p, cpsStmts, pl)
else: else:
var pl: PRope = nil var pl: Rope = nil
#var param = typ.n.sons[1].sym #var param = typ.n.sons[1].sym
if 1 < ri.len: if 1 < ri.len:
app(pl, genThisArg(p, ri, 1, typ)) add(pl, genThisArg(p, ri, 1, typ))
app(pl, op.r) add(pl, op.r)
var params: PRope var params: Rope
for i in countup(2, length - 1): for i in countup(2, length - 1):
if params != nil: params.app(~", ") if params != nil: params.add(~", ")
assert(sonsLen(typ) == sonsLen(typ.n)) assert(sonsLen(typ) == sonsLen(typ.n))
app(params, genOtherArg(p, ri, i, typ)) add(params, genOtherArg(p, ri, i, typ))
fixupCall(p, le, ri, d, pl, params) fixupCall(p, le, ri, d, pl, params)
proc genNamedParamCall(p: BProc, ri: PNode, d: var TLoc) = proc genNamedParamCall(p: BProc, ri: PNode, d: var TLoc) =
@ -436,55 +455,55 @@ proc genNamedParamCall(p: BProc, ri: PNode, d: var TLoc) =
var length = sonsLen(ri) var length = sonsLen(ri)
assert(sonsLen(typ) == sonsLen(typ.n)) assert(sonsLen(typ) == sonsLen(typ.n))
# don't call 'ropeToStr' here for efficiency: # don't call '$' here for efficiency:
let pat = ri.sons[0].sym.loc.r.data let pat = ri.sons[0].sym.loc.r.data
internalAssert pat != nil internalAssert pat != nil
var start = 3 var start = 3
if ' ' in pat: if ' ' in pat:
start = 1 start = 1
app(pl, op.r) add(pl, op.r)
if length > 1: if length > 1:
app(pl, ~": ") add(pl, ~": ")
app(pl, genArg(p, ri.sons[1], typ.n.sons[1].sym, ri)) add(pl, genArg(p, ri.sons[1], typ.n.sons[1].sym, ri))
start = 2 start = 2
else: else:
if length > 1: if length > 1:
app(pl, genArg(p, ri.sons[1], typ.n.sons[1].sym, ri)) add(pl, genArg(p, ri.sons[1], typ.n.sons[1].sym, ri))
app(pl, ~" ") add(pl, ~" ")
app(pl, op.r) add(pl, op.r)
if length > 2: if length > 2:
app(pl, ~": ") add(pl, ~": ")
app(pl, genArg(p, ri.sons[2], typ.n.sons[2].sym, ri)) add(pl, genArg(p, ri.sons[2], typ.n.sons[2].sym, ri))
for i in countup(start, length-1): for i in countup(start, length-1):
assert(sonsLen(typ) == sonsLen(typ.n)) assert(sonsLen(typ) == sonsLen(typ.n))
if i >= sonsLen(typ): if i >= sonsLen(typ):
internalError(ri.info, "varargs for objective C method?") internalError(ri.info, "varargs for objective C method?")
assert(typ.n.sons[i].kind == nkSym) assert(typ.n.sons[i].kind == nkSym)
var param = typ.n.sons[i].sym var param = typ.n.sons[i].sym
app(pl, ~" ") add(pl, ~" ")
app(pl, param.name.s) add(pl, param.name.s)
app(pl, ~": ") add(pl, ~": ")
app(pl, genArg(p, ri.sons[i], param, ri)) add(pl, genArg(p, ri.sons[i], param, ri))
if typ.sons[0] != nil: if typ.sons[0] != nil:
if isInvalidReturnType(typ.sons[0]): 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: # beware of 'result = p(result)'. We always allocate a temporary:
if d.k in {locTemp, locNone}: if d.k in {locTemp, locNone}:
# We already got a temp. Great, special case it: # We already got a temp. Great, special case it:
if d.k == locNone: getTemp(p, typ.sons[0], d, needsInit=true) if d.k == locNone: getTemp(p, typ.sons[0], d, needsInit=true)
app(pl, ~"Result: ") add(pl, ~"Result: ")
app(pl, addrLoc(d)) add(pl, addrLoc(d))
app(pl, ~"];$n") add(pl, ~"];$n")
line(p, cpsStmts, pl) line(p, cpsStmts, pl)
else: else:
var tmp: TLoc var tmp: TLoc
getTemp(p, typ.sons[0], tmp, needsInit=true) getTemp(p, typ.sons[0], tmp, needsInit=true)
app(pl, addrLoc(tmp)) add(pl, addrLoc(tmp))
app(pl, ~"];$n") add(pl, ~"];$n")
line(p, cpsStmts, pl) line(p, cpsStmts, pl)
genAssignment(p, d, tmp, {}) # no need for deep copying genAssignment(p, d, tmp, {}) # no need for deep copying
else: else:
app(pl, ~"]") add(pl, ~"]")
if d.k == locNone: getTemp(p, typ.sons[0], d) if d.k == locNone: getTemp(p, typ.sons[0], d)
assert(d.t != nil) # generate an assignment to d: assert(d.t != nil) # generate an assignment to d:
var list: TLoc var list: TLoc
@ -492,7 +511,7 @@ proc genNamedParamCall(p: BProc, ri: PNode, d: var TLoc) =
list.r = pl list.r = pl
genAssignment(p, d, list, {}) # no need for deep copying genAssignment(p, d, list, {}) # no need for deep copying
else: else:
app(pl, ~"];$n") add(pl, ~"];$n")
line(p, cpsStmts, pl) line(p, cpsStmts, pl)
proc genCall(p: BProc, e: PNode, d: var TLoc) = proc genCall(p: BProc, e: PNode, d: var TLoc) =

File diff suppressed because it is too large Load diff

View file

@ -45,29 +45,29 @@ const
] ]
NimMergeEndMark = "/*\tNIM_merge_END:*/" NimMergeEndMark = "/*\tNIM_merge_END:*/"
proc genSectionStart*(fs: TCFileSection): PRope = proc genSectionStart*(fs: TCFileSection): Rope =
if compilationCachePresent: if compilationCachePresent:
result = toRope(tnl) result = rope(tnl)
app(result, "/*\t") add(result, "/*\t")
app(result, CFileSectionNames[fs]) add(result, CFileSectionNames[fs])
app(result, ":*/") add(result, ":*/")
app(result, tnl) add(result, tnl)
proc genSectionEnd*(fs: TCFileSection): PRope = proc genSectionEnd*(fs: TCFileSection): Rope =
if compilationCachePresent: if compilationCachePresent:
result = toRope(NimMergeEndMark & tnl) result = rope(NimMergeEndMark & tnl)
proc genSectionStart*(ps: TCProcSection): PRope = proc genSectionStart*(ps: TCProcSection): Rope =
if compilationCachePresent: if compilationCachePresent:
result = toRope(tnl) result = rope(tnl)
app(result, "/*\t") add(result, "/*\t")
app(result, CProcSectionNames[ps]) add(result, CProcSectionNames[ps])
app(result, ":*/") add(result, ":*/")
app(result, tnl) add(result, tnl)
proc genSectionEnd*(ps: TCProcSection): PRope = proc genSectionEnd*(ps: TCProcSection): Rope =
if compilationCachePresent: if compilationCachePresent:
result = toRope(NimMergeEndMark & tnl) result = rope(NimMergeEndMark & tnl)
proc writeTypeCache(a: TIdTable, s: var string) = proc writeTypeCache(a: TIdTable, s: var string) =
var i = 0 var i = 0
@ -79,7 +79,7 @@ proc writeTypeCache(a: TIdTable, s: var string) =
s.add(' ') s.add(' ')
encodeVInt(id, s) encodeVInt(id, s)
s.add(':') s.add(':')
encodeStr(PRope(value).ropeToStr, s) encodeStr($Rope(value), s)
inc i inc i
s.add('}') s.add('}')
@ -95,7 +95,7 @@ proc writeIntSet(a: IntSet, s: var string) =
inc i inc i
s.add('}') s.add('}')
proc genMergeInfo*(m: BModule): PRope = proc genMergeInfo*(m: BModule): Rope =
if optSymbolFiles notin gGlobalOptions: return nil if optSymbolFiles notin gGlobalOptions: return nil
var s = "/*\tNIM_merge_INFO:" var s = "/*\tNIM_merge_INFO:"
s.add(tnl) s.add(tnl)
@ -111,7 +111,7 @@ proc genMergeInfo*(m: BModule): PRope =
encodeVInt(ord(m.frameDeclared), s) encodeVInt(ord(m.frameDeclared), s)
s.add(tnl) s.add(tnl)
s.add("*/") s.add("*/")
result = s.toRope result = s.rope
template `^`(pos: int): expr = L.buf[pos] template `^`(pos: int): expr = L.buf[pos]
@ -145,7 +145,7 @@ proc atEndMark(buf: cstring, pos: int): bool =
while s < NimMergeEndMark.len and buf[pos+s] == NimMergeEndMark[s]: inc s while s < NimMergeEndMark.len and buf[pos+s] == NimMergeEndMark[s]: inc s
result = s == NimMergeEndMark.len result = s == NimMergeEndMark.len
proc readVerbatimSection(L: var TBaseLexer): PRope = proc readVerbatimSection(L: var TBaseLexer): Rope =
var pos = L.bufpos var pos = L.bufpos
var buf = L.buf var buf = L.buf
var r = newStringOfCap(30_000) var r = newStringOfCap(30_000)
@ -169,7 +169,7 @@ proc readVerbatimSection(L: var TBaseLexer): PRope =
r.add(buf[pos]) r.add(buf[pos])
inc pos inc pos
L.bufpos = pos L.bufpos = pos
result = r.toRope result = r.rope
proc readKey(L: var TBaseLexer, result: var string) = proc readKey(L: var TBaseLexer, result: var string) =
var pos = L.bufpos 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 # 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 # better would be to adapt the data structure to not even store the
# object as key, but only the Id # object as key, but only the Id
idTablePut(result, newFakeType(key), value.toRope) idTablePut(result, newFakeType(key), value.rope)
inc L.bufpos inc L.bufpos
proc readIntSet(L: var TBaseLexer, result: var IntSet) = proc readIntSet(L: var TBaseLexer, result: var IntSet) =
@ -280,11 +280,11 @@ proc readMergeSections(cfilename: string, m: var TMergeSections) =
proc mergeRequired*(m: BModule): bool = proc mergeRequired*(m: BModule): bool =
for i in cfsHeaders..cfsProcs: for i in cfsHeaders..cfsProcs:
if m.s[i] != nil: if m.s[i] != nil:
#echo "not empty: ", i, " ", ropeToStr(m.s[i]) #echo "not empty: ", i, " ", m.s[i]
return true return true
for i in low(TCProcSection)..high(TCProcSection): for i in low(TCProcSection)..high(TCProcSection):
if m.initProc.s(i) != nil: 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 return true
proc mergeFiles*(cfilename: string, m: BModule) = proc mergeFiles*(cfilename: string, m: BModule) =
@ -293,6 +293,6 @@ proc mergeFiles*(cfilename: string, m: BModule) =
readMergeSections(cfilename, old) readMergeSections(cfilename, old)
# do the merge; old section before new section: # do the merge; old section before new section:
for i in low(TCFileSection)..high(TCFileSection): 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): 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)

View file

@ -61,11 +61,10 @@ proc genVarTuple(p: BProc, n: PNode) =
initLocalVar(p, v, immediateAsgn=isAssignedImmediately(n[L-1])) initLocalVar(p, v, immediateAsgn=isAssignedImmediately(n[L-1]))
initLoc(field, locExpr, t.sons[i], tup.s) initLoc(field, locExpr, t.sons[i], tup.s)
if t.kind == tyTuple: if t.kind == tyTuple:
field.r = ropef("$1.Field$2", [rdLoc(tup), toRope(i)]) field.r = "$1.Field$2" % [rdLoc(tup), rope(i)]
else: else:
if t.n.sons[i].kind != nkSym: internalError(n.info, "genVarTuple") if t.n.sons[i].kind != nkSym: internalError(n.info, "genVarTuple")
field.r = ropef("$1.$2", field.r = "$1.$2" % [rdLoc(tup), mangleRecFieldName(t.n.sons[i].sym, t)]
[rdLoc(tup), mangleRecFieldName(t.n.sons[i].sym, t)])
putLocIntoDest(p, v.loc, field) putLocIntoDest(p, v.loc, field)
proc genDeref(p: BProc, e: PNode, d: var TLoc; enforceDeref=false) 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: else:
expr(p, ri, a) expr(p, ri, a)
proc startBlock(p: BProc, start: TFormatStr = "{$n", proc startBlock(p: BProc, start: FormatStr = "{$n",
args: varargs[PRope]): int {.discardable.} = args: varargs[Rope]): int {.discardable.} =
lineCg(p, cpsStmts, start, args) lineCg(p, cpsStmts, start, args)
inc(p.labels) inc(p.labels)
result = len(p.blocks) 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].nestedTryStmts = p.nestedTryStmts.len.int16
p.blocks[result].nestedExceptStmts = p.inExceptBlock.int16 p.blocks[result].nestedExceptStmts = p.inExceptBlock.int16
proc assignLabel(b: var TBlock): PRope {.inline.} = proc assignLabel(b: var TBlock): Rope {.inline.} =
b.label = con("LA", b.id.toRope) b.label = "LA" & b.id.rope
result = b.label result = b.label
proc blockBody(b: var TBlock): PRope = proc blockBody(b: var TBlock): Rope =
result = b.sections[cpsLocals] result = b.sections[cpsLocals]
if b.frameLen > 0: if b.frameLen > 0:
result.appf("F.len+=$1;$n", b.frameLen.toRope) result.addf("F.len+=$1;$n", [b.frameLen.rope])
result.app(b.sections[cpsInit]) result.add(b.sections[cpsInit])
result.app(b.sections[cpsStmts]) result.add(b.sections[cpsStmts])
proc endBlock(p: BProc, blockEnd: PRope) = proc endBlock(p: BProc, blockEnd: Rope) =
let topBlock = p.blocks.len-1 let topBlock = p.blocks.len-1
# the block is merged into the parent block # 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) setLen(p.blocks, topBlock)
# this is done after the block is popped so $n is # this is done after the block is popped so $n is
# properly indented when pretty printing is enabled # properly indented when pretty printing is enabled
@ -124,7 +123,7 @@ proc endBlock(p: BProc) =
~"}$n" ~"}$n"
let frameLen = p.blocks[topBlock].frameLen let frameLen = p.blocks[topBlock].frameLen
if frameLen > 0: if frameLen > 0:
blockEnd.appf("F.len-=$1;$n", frameLen.toRope) blockEnd.addf("F.len-=$1;$n", [frameLen.rope])
endBlock(p, blockEnd) endBlock(p, blockEnd)
proc genSimpleBlock(p: BProc, stmts: PNode) {.inline.} = proc genSimpleBlock(p: BProc, stmts: PNode) {.inline.} =
@ -145,7 +144,7 @@ template preserveBreakIdx(body: stmt): stmt {.immediate.} =
proc genState(p: BProc, n: PNode) = proc genState(p: BProc, n: PNode) =
internalAssert n.len == 1 and n.sons[0].kind == nkIntLit internalAssert n.len == 1 and n.sons[0].kind == nkIntLit
let idx = n.sons[0].intVal 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) = proc genGotoState(p: BProc, n: PNode) =
# we resist the temptation to translate it into duff's device as it later # 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 p.beforeRetNeeded = true
lineF(p, cpsStmts, "case -1: goto BeforeRet;$n", []) lineF(p, cpsStmts, "case -1: goto BeforeRet;$n", [])
for i in 0 .. lastOrd(n.sons[0].typ): 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", []) lineF(p, cpsStmts, "}$n", [])
proc genBreakState(p: BProc, n: PNode) = proc genBreakState(p: BProc, n: PNode) =
@ -176,9 +175,18 @@ proc genBreakState(p: BProc, n: PNode) =
proc genVarPrototypeAux(m: BModule, sym: PSym) 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) = proc genSingleVar(p: BProc, a: PNode) =
var v = a.sons[0].sym 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 var targetProc = p
if sfGlobal in v.flags: if sfGlobal in v.flags:
if v.flags * {sfImportc, sfExportc} == {sfImportc} and if v.flags * {sfImportc, sfExportc} == {sfImportc} and
@ -214,17 +222,17 @@ proc genSingleVar(p: BProc, a: PNode) =
var tmp: TLoc var tmp: TLoc
if value.kind in nkCallKinds and value[0].kind == nkSym and if value.kind in nkCallKinds and value[0].kind == nkSym and
sfConstructor in value[0].sym.flags: sfConstructor in value[0].sym.flags:
var params: PRope var params: Rope
let typ = skipTypes(value.sons[0].typ, abstractInst) let typ = skipTypes(value.sons[0].typ, abstractInst)
assert(typ.kind == tyProc) assert(typ.kind == tyProc)
for i in 1.. <value.len: for i in 1.. <value.len:
if params != nil: params.app(~", ") if params != nil: params.add(~", ")
assert(sonsLen(typ) == sonsLen(typ.n)) assert(sonsLen(typ) == sonsLen(typ.n))
app(params, genOtherArg(p, value, i, typ)) add(params, genOtherArg(p, value, i, typ))
lineF(p, cpsStmts, "$#($#);$n", decl, params) lineF(p, cpsStmts, "$#($#);$n", [decl, params])
else: else:
initLocExprSingleUse(p, value, tmp) initLocExprSingleUse(p, value, tmp)
lineF(p, cpsStmts, "$# = $#;$n", decl, tmp.rdLoc) lineF(p, cpsStmts, "$# = $#;$n", [decl, tmp.rdLoc])
return return
assignLocalVar(p, v) assignLocalVar(p, v)
initLocalVar(p, v, imm) initLocalVar(p, v, imm)
@ -298,9 +306,9 @@ proc genIf(p: BProc, n: PNode, d: var TLoc) =
when not newScopeForIf: startBlock(p) when not newScopeForIf: startBlock(p)
if p.module.compileToCpp: if p.module.compileToCpp:
# avoid "jump to label crosses initialization" error: # avoid "jump to label crosses initialization" error:
app(p.s(cpsStmts), "{") add(p.s(cpsStmts), "{")
expr(p, it.sons[1], d) expr(p, it.sons[1], d)
app(p.s(cpsStmts), "}") add(p.s(cpsStmts), "}")
else: else:
expr(p, it.sons[1], d) expr(p, it.sons[1], d)
endBlock(p) endBlock(p)
@ -366,6 +374,19 @@ proc genReturnStmt(p: BProc, t: PNode) =
linefmt(p, cpsStmts, "if ($1.status != 0) #popCurrentException();$n", safePoint) linefmt(p, cpsStmts, "if ($1.status != 0) #popCurrentException();$n", safePoint)
lineF(p, cpsStmts, "goto 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) = proc genComputedGoto(p: BProc; n: PNode) =
# first pass: Generate array of computed labels: # first pass: Generate array of computed labels:
var casePos = -1 var casePos = -1
@ -389,11 +410,11 @@ proc genComputedGoto(p: BProc; n: PNode) =
localError(n.info, "no case statement found for computed goto"); return localError(n.info, "no case statement found for computed goto"); return
var id = p.labels+1 var id = p.labels+1
inc p.labels, arraySize+1 inc p.labels, arraySize+1
let tmp = ropef("TMP$1", id.toRope) let tmp = "TMP$1" % [id.rope]
var gotoArray = ropef("static void* $#[$#] = {", tmp, arraySize.toRope) var gotoArray = "static void* $#[$#] = {" % [tmp, arraySize.rope]
for i in 1..arraySize-1: for i in 1..arraySize-1:
gotoArray.appf("&&TMP$#, ", (id+i).toRope) gotoArray.addf("&&TMP$#, ", [(id+i).rope])
gotoArray.appf("&&TMP$#};$n", (id+arraySize).toRope) gotoArray.addf("&&TMP$#};$n", [(id+arraySize).rope])
line(p, cpsLocals, gotoArray) line(p, cpsLocals, gotoArray)
let topBlock = p.blocks.len-1 let topBlock = p.blocks.len-1
@ -407,13 +428,13 @@ proc genComputedGoto(p: BProc; n: PNode) =
for j in 0 .. casePos-1: genStmts(p, n.sons[j]) for j in 0 .. casePos-1: genStmts(p, n.sons[j])
let tailA = p.blocks[topBlock].sections[cpsStmts] 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] let caseStmt = n.sons[casePos]
var a: TLoc var a: TLoc
initLocExpr(p, caseStmt.sons[0], a) initLocExpr(p, caseStmt.sons[0], a)
# first goto: # first goto:
lineF(p, cpsStmts, "goto *$#[$#];$n", tmp, a.rdLoc) lineF(p, cpsStmts, "goto *$#[$#];$n", [tmp, a.rdLoc])
for i in 1 .. <caseStmt.len: for i in 1 .. <caseStmt.len:
startBlock(p) startBlock(p)
@ -423,16 +444,16 @@ proc genComputedGoto(p: BProc; n: PNode) =
localError(it.info, "range notation not available for computed goto") localError(it.info, "range notation not available for computed goto")
return return
let val = getOrdValue(it.sons[j]) 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) genStmts(p, it.lastSon)
#for j in casePos+1 .. <n.len: genStmts(p, n.sons[j]) # tailB #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 #for j in 0 .. casePos-1: genStmts(p, n.sons[j]) # tailA
app(p.s(cpsStmts), tailB) add(p.s(cpsStmts), tailB)
app(p.s(cpsStmts), tailA) add(p.s(cpsStmts), tailA)
var a: TLoc var a: TLoc
initLocExpr(p, caseStmt.sons[0], a) initLocExpr(p, caseStmt.sons[0], a)
lineF(p, cpsStmts, "goto *$#[$#];$n", tmp, a.rdLoc) lineF(p, cpsStmts, "goto *$#[$#];$n", [tmp, a.rdLoc])
endBlock(p) endBlock(p)
proc genWhileStmt(p: BProc, t: PNode) = proc genWhileStmt(p: BProc, t: PNode) =
@ -498,9 +519,9 @@ proc genParForStmt(p: BProc, t: PNode) =
lineF(p, cpsStmts, "#pragma omp parallel for $4$n" & lineF(p, cpsStmts, "#pragma omp parallel for $4$n" &
"for ($1 = $2; $1 <= $3; ++$1)", "for ($1 = $2; $1 <= $3; ++$1)",
forLoopVar.loc.rdLoc, [forLoopVar.loc.rdLoc,
rangeA.rdLoc, rangeB.rdLoc, rangeA.rdLoc, rangeB.rdLoc,
call.sons[3].getStr.toRope) call.sons[3].getStr.rope])
p.breakIdx = startBlock(p) p.breakIdx = startBlock(p)
p.blocks[p.breakIdx].isLoop = true p.blocks[p.breakIdx].isLoop = true
@ -530,12 +551,7 @@ proc genBreakStmt(p: BProc, t: PNode) =
lineF(p, cpsStmts, "goto $1;$n", [label]) lineF(p, cpsStmts, "goto $1;$n", [label])
proc getRaiseFrmt(p: BProc): string = 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" result = "#raiseException((#Exception*)$1, $2);$n"
else:
result = "#raiseException((#E_Base*)$1, $2);$n"
proc genRaiseStmt(p: BProc, t: PNode) = proc genRaiseStmt(p: BProc, t: PNode) =
if p.inExceptBlock > 0: if p.inExceptBlock > 0:
@ -560,7 +576,7 @@ proc genRaiseStmt(p: BProc, t: PNode) =
linefmt(p, cpsStmts, "#reraiseException();$n") linefmt(p, cpsStmts, "#reraiseException();$n")
proc genCaseGenericBranch(p: BProc, b: PNode, e: TLoc, proc genCaseGenericBranch(p: BProc, b: PNode, e: TLoc,
rangeFormat, eqFormat: TFormatStr, labl: TLabel) = rangeFormat, eqFormat: FormatStr, labl: TLabel) =
var var
x, y: TLoc x, y: TLoc
var length = sonsLen(b) var length = sonsLen(b)
@ -578,7 +594,7 @@ proc genCaseSecondPass(p: BProc, t: PNode, d: var TLoc,
labId, until: int): TLabel = labId, until: int): TLabel =
var lend = getLabel(p) var lend = getLabel(p)
for i in 1..until: 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: if t.sons[i].kind == nkOfBranch:
var length = sonsLen(t.sons[i]) var length = sonsLen(t.sons[i])
exprBlock(p, t.sons[i].sons[length - 1], d) exprBlock(p, t.sons[i].sons[length - 1], d)
@ -588,7 +604,7 @@ proc genCaseSecondPass(p: BProc, t: PNode, d: var TLoc,
result = lend result = lend
proc genIfForCaseUntil(p: BProc, t: PNode, d: var TLoc, proc genIfForCaseUntil(p: BProc, t: PNode, d: var TLoc,
rangeFormat, eqFormat: TFormatStr, rangeFormat, eqFormat: FormatStr,
until: int, a: TLoc): TLabel = until: int, a: TLoc): TLabel =
# generate a C-if statement for a Nim case statement # generate a C-if statement for a Nim case statement
var labId = p.labels var labId = p.labels
@ -596,27 +612,27 @@ proc genIfForCaseUntil(p: BProc, t: PNode, d: var TLoc,
inc(p.labels) inc(p.labels)
if t.sons[i].kind == nkOfBranch: # else statement if t.sons[i].kind == nkOfBranch: # else statement
genCaseGenericBranch(p, t.sons[i], a, rangeFormat, eqFormat, genCaseGenericBranch(p, t.sons[i], a, rangeFormat, eqFormat,
con("LA", toRope(p.labels))) "LA" & rope(p.labels))
else: 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: if until < t.len-1:
inc(p.labels) inc(p.labels)
var gotoTarget = 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) result = genCaseSecondPass(p, t, d, labId, until)
lineF(p, cpsStmts, "LA$1: ;$n", [toRope(gotoTarget)]) lineF(p, cpsStmts, "LA$1: ;$n", [rope(gotoTarget)])
else: else:
result = genCaseSecondPass(p, t, d, labId, until) result = genCaseSecondPass(p, t, d, labId, until)
proc genCaseGeneric(p: BProc, t: PNode, d: var TLoc, proc genCaseGeneric(p: BProc, t: PNode, d: var TLoc,
rangeFormat, eqFormat: TFormatStr) = rangeFormat, eqFormat: FormatStr) =
var a: TLoc var a: TLoc
initLocExpr(p, t.sons[0], a) initLocExpr(p, t.sons[0], a)
var lend = genIfForCaseUntil(p, t, d, rangeFormat, eqFormat, sonsLen(t)-1, a) var lend = genIfForCaseUntil(p, t, d, rangeFormat, eqFormat, sonsLen(t)-1, a)
fixLabel(p, lend) fixLabel(p, lend)
proc genCaseStringBranch(p: BProc, b: PNode, e: TLoc, labl: TLabel, proc genCaseStringBranch(p: BProc, b: PNode, e: TLoc, labl: TLabel,
branches: var openArray[PRope]) = branches: var openArray[Rope]) =
var x: TLoc var x: TLoc
var length = sonsLen(b) var length = sonsLen(b)
for i in countup(0, length - 2): for i in countup(0, length - 2):
@ -634,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 t.sons[i].kind == nkOfBranch: inc(strings, sonsLen(t.sons[i]) - 1)
if strings > stringCaseThreshold: if strings > stringCaseThreshold:
var bitMask = math.nextPowerOfTwo(strings) - 1 var bitMask = math.nextPowerOfTwo(strings) - 1
var branches: seq[PRope] var branches: seq[Rope]
newSeq(branches, bitMask + 1) newSeq(branches, bitMask + 1)
var a: TLoc var a: TLoc
initLocExpr(p, t.sons[0], a) # fist pass: gnerate ifs+goto: initLocExpr(p, t.sons[0], a) # fist pass: gnerate ifs+goto:
@ -642,21 +658,21 @@ proc genStringCase(p: BProc, t: PNode, d: var TLoc) =
for i in countup(1, sonsLen(t) - 1): for i in countup(1, sonsLen(t) - 1):
inc(p.labels) inc(p.labels)
if t.sons[i].kind == nkOfBranch: 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) branches)
else: else:
# else statement: nothing to do yet # else statement: nothing to do yet
# but we reserved a label, which we use later # but we reserved a label, which we use later
discard discard
linefmt(p, cpsStmts, "switch (#hashString($1) & $2) {$n", linefmt(p, cpsStmts, "switch (#hashString($1) & $2) {$n",
rdLoc(a), toRope(bitMask)) rdLoc(a), rope(bitMask))
for j in countup(0, high(branches)): for j in countup(0, high(branches)):
if branches[j] != nil: if branches[j] != nil:
lineF(p, cpsStmts, "case $1: $n$2break;$n", lineF(p, cpsStmts, "case $1: $n$2break;$n",
[intLiteral(j), branches[j]]) [intLiteral(j), branches[j]])
lineF(p, cpsStmts, "}$n") # else statement: lineF(p, cpsStmts, "}$n", []) # else statement:
if t.sons[sonsLen(t)-1].kind != nkOfBranch: 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 # third pass: generate statements
var lend = genCaseSecondPass(p, t, d, labId, sonsLen(t)-1) var lend = genCaseSecondPass(p, t, d, labId, sonsLen(t)-1)
fixLabel(p, lend) fixLabel(p, lend)
@ -718,13 +734,13 @@ proc genOrdinalCase(p: BProc, n: PNode, d: var TLoc) =
genCaseRange(p, branch) genCaseRange(p, branch)
else: else:
# else part of case statement: # else part of case statement:
lineF(p, cpsStmts, "default:$n") lineF(p, cpsStmts, "default:$n", [])
hasDefault = true hasDefault = true
exprBlock(p, branch.lastSon, d) exprBlock(p, branch.lastSon, d)
lineF(p, cpsStmts, "break;$n") lineF(p, cpsStmts, "break;$n", [])
if (hasAssume in CC[cCompiler].props) and not hasDefault: if (hasAssume in CC[cCompiler].props) and not hasDefault:
lineF(p, cpsStmts, "default: __assume(0);$n") lineF(p, cpsStmts, "default: __assume(0);$n", [])
lineF(p, cpsStmts, "}$n") lineF(p, cpsStmts, "}$n", [])
if lend != nil: fixLabel(p, lend) if lend != nil: fixLabel(p, lend)
proc genCase(p: BProc, t: PNode, d: var TLoc) = proc genCase(p: BProc, t: PNode, d: var TLoc) =
@ -737,6 +753,9 @@ proc genCase(p: BProc, t: PNode, d: var TLoc) =
of tyFloat..tyFloat128: of tyFloat..tyFloat128:
genCaseGeneric(p, t, d, "if ($1 >= $2 && $1 <= $3) goto $4;$n", genCaseGeneric(p, t, d, "if ($1 >= $2 && $1 <= $3) goto $4;$n",
"if ($1 == $2) goto $3;$n") "if ($1 == $2) goto $3;$n")
else:
if t.sons[0].kind == nkSym and sfGoto in t.sons[0].sym.flags:
genGotoForCase(p, t)
else: else:
genOrdinalCase(p, t, d) genOrdinalCase(p, t, d)
@ -773,11 +792,8 @@ proc genTryCpp(p: BProc, t: PNode, d: var TLoc) =
# finallyPart(); # finallyPart();
if not isEmptyType(t.typ) and d.k == locNone: if not isEmptyType(t.typ) and d.k == locNone:
getTemp(p, t.typ, d) getTemp(p, t.typ, d)
var
exc: PRope
i, length, blen: int
genLineDir(p, t) genLineDir(p, t)
exc = getTempName() let exc = getTempName()
if getCompilerProc("Exception") != nil: if getCompilerProc("Exception") != nil:
discard cgsym(p.module, "Exception") discard cgsym(p.module, "Exception")
else: else:
@ -785,36 +801,42 @@ proc genTryCpp(p: BProc, t: PNode, d: var TLoc) =
add(p.nestedTryStmts, t) add(p.nestedTryStmts, t)
startBlock(p, "try {$n") startBlock(p, "try {$n")
expr(p, t.sons[0], d) expr(p, t.sons[0], d)
length = sonsLen(t) let length = sonsLen(t)
endBlock(p, ropecg(p.module, "} catch (NimException& $1) {$n", [exc])) endBlock(p, ropecg(p.module, "} catch (NimException& $1) {$n", [exc]))
if optStackTrace in p.options: if optStackTrace in p.options:
linefmt(p, cpsStmts, "#setFrame((TFrame*)&F);$n") linefmt(p, cpsStmts, "#setFrame((TFrame*)&FR);$n")
inc p.inExceptBlock inc p.inExceptBlock
i = 1 var i = 1
var catchAllPresent = false var catchAllPresent = false
while (i < length) and (t.sons[i].kind == nkExceptBranch): while (i < length) and (t.sons[i].kind == nkExceptBranch):
blen = sonsLen(t.sons[i]) let blen = sonsLen(t.sons[i])
if i > 1: appf(p.s(cpsStmts), "else ") if i > 1: addf(p.s(cpsStmts), "else ", [])
if blen == 1: if blen == 1:
# general except section: # general except section:
catchAllPresent = true 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: else:
var orExpr: PRope = nil var orExpr: Rope = nil
for j in countup(0, blen - 2): for j in countup(0, blen - 2):
assert(t.sons[i].sons[j].kind == nkType) assert(t.sons[i].sons[j].kind == nkType)
if orExpr != nil: app(orExpr, "||") if orExpr != nil: add(orExpr, "||")
appcg(p.module, orExpr, appcg(p.module, orExpr,
"#isObj($1.exp->m_type, $2)", "#isObj($1.exp->m_type, $2)",
[exc, genTypeInfo(p.module, t.sons[i].sons[j].typ)]) [exc, genTypeInfo(p.module, t.sons[i].sons[j].typ)])
lineF(p, cpsStmts, "if ($1) ", [orExpr]) 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) inc(i)
# reraise the exception if there was no catch all # reraise the exception if there was no catch all
# and none of the handlers matched # and none of the handlers matched
if not catchAllPresent: if not catchAllPresent:
if i > 1: lineF(p, cpsStmts, "else ") if i > 1: lineF(p, cpsStmts, "else ", [])
startBlock(p) startBlock(p)
var finallyBlock = t.lastSon var finallyBlock = t.lastSon
if finallyBlock.kind == nkFinally: if finallyBlock.kind == nkFinally:
@ -824,7 +846,7 @@ proc genTryCpp(p: BProc, t: PNode, d: var TLoc) =
line(p, cpsStmts, ~"throw;$n") line(p, cpsStmts, ~"throw;$n")
endBlock(p) endBlock(p)
lineF(p, cpsStmts, "}$n") # end of catch block lineF(p, cpsStmts, "}$n", []) # end of catch block
dec p.inExceptBlock dec p.inExceptBlock
discard pop(p.nestedTryStmts) discard pop(p.nestedTryStmts)
@ -888,24 +910,24 @@ proc genTry(p: BProc, t: PNode, d: var TLoc) =
startBlock(p, "else {$n") startBlock(p, "else {$n")
linefmt(p, cpsStmts, "#popSafePoint();$n") linefmt(p, cpsStmts, "#popSafePoint();$n")
if optStackTrace in p.options: if optStackTrace in p.options:
linefmt(p, cpsStmts, "#setFrame((TFrame*)&F);$n") linefmt(p, cpsStmts, "#setFrame((TFrame*)&FR);$n")
inc p.inExceptBlock inc p.inExceptBlock
var i = 1 var i = 1
while (i < length) and (t.sons[i].kind == nkExceptBranch): while (i < length) and (t.sons[i].kind == nkExceptBranch):
var blen = sonsLen(t.sons[i]) var blen = sonsLen(t.sons[i])
if blen == 1: if blen == 1:
# general except section: # general except section:
if i > 1: lineF(p, cpsStmts, "else") if i > 1: lineF(p, cpsStmts, "else", [])
startBlock(p) startBlock(p)
linefmt(p, cpsStmts, "$1.status = 0;$n", safePoint) linefmt(p, cpsStmts, "$1.status = 0;$n", safePoint)
expr(p, t.sons[i].sons[0], d) expr(p, t.sons[i].sons[0], d)
linefmt(p, cpsStmts, "#popCurrentException();$n") linefmt(p, cpsStmts, "#popCurrentException();$n")
endBlock(p) endBlock(p)
else: else:
var orExpr: PRope = nil var orExpr: Rope = nil
for j in countup(0, blen - 2): for j in countup(0, blen - 2):
assert(t.sons[i].sons[j].kind == nkType) assert(t.sons[i].sons[j].kind == nkType)
if orExpr != nil: app(orExpr, "||") if orExpr != nil: add(orExpr, "||")
appcg(p.module, orExpr, appcg(p.module, orExpr,
"#isObj(#getCurrentException()->Sup.m_type, $1)", "#isObj(#getCurrentException()->Sup.m_type, $1)",
[genTypeInfo(p.module, t.sons[i].sons[j].typ)]) [genTypeInfo(p.module, t.sons[i].sons[j].typ)])
@ -925,7 +947,7 @@ proc genTry(p: BProc, t: PNode, d: var TLoc) =
discard pop(p.finallySafePoints) discard pop(p.finallySafePoints)
linefmt(p, cpsStmts, "if ($1.status != 0) #reraiseException();$n", safePoint) 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 = "" var res = ""
for i in countup(0, sonsLen(t) - 1): for i in countup(0, sonsLen(t) - 1):
case t.sons[i].kind case t.sons[i].kind
@ -936,7 +958,7 @@ proc genAsmOrEmitStmt(p: BProc, t: PNode, isAsmStmt=false): PRope =
if sym.kind in {skProc, skIterator, skClosureIterator, skMethod}: if sym.kind in {skProc, skIterator, skClosureIterator, skMethod}:
var a: TLoc var a: TLoc
initLocExpr(p, t.sons[i], a) initLocExpr(p, t.sons[i], a)
res.add(rdLoc(a).ropeToStr) res.add($rdLoc(a))
else: else:
var r = sym.loc.r var r = sym.loc.r
if r == nil: if r == nil:
@ -944,7 +966,7 @@ proc genAsmOrEmitStmt(p: BProc, t: PNode, isAsmStmt=false): PRope =
# it doesn't matter much: # it doesn't matter much:
r = mangleName(sym) r = mangleName(sym)
sym.loc.r = r # but be consequent! sym.loc.r = r # but be consequent!
res.add(r.ropeToStr) res.add($r)
else: internalError(t.sons[i].info, "genAsmOrEmitStmt()") else: internalError(t.sons[i].info, "genAsmOrEmitStmt()")
if isAsmStmt and hasGnuAsm in CC[cCompiler].props: if isAsmStmt and hasGnuAsm in CC[cCompiler].props:
@ -954,15 +976,15 @@ proc genAsmOrEmitStmt(p: BProc, t: PNode, isAsmStmt=false): PRope =
if x[j] in {'"', ':'}: if x[j] in {'"', ':'}:
# don't modify the line if already in quotes or # don't modify the line if already in quotes or
# some clobber register list: # some clobber register list:
app(result, x); app(result, tnl) add(result, x); add(result, tnl)
elif x[j] != '\0': elif x[j] != '\0':
# ignore empty lines # ignore empty lines
app(result, "\"") add(result, "\"")
app(result, x) add(result, x)
app(result, "\\n\"\n") add(result, "\\n\"\n")
else: else:
res.add(tnl) res.add(tnl)
result = res.toRope result = res.rope
proc genAsmStmt(p: BProc, t: PNode) = proc genAsmStmt(p: BProc, t: PNode) =
assert(t.kind == nkAsmStmt) assert(t.kind == nkAsmStmt)
@ -973,23 +995,30 @@ proc genAsmStmt(p: BProc, t: PNode) =
# work: # work:
if p.prc == nil: if p.prc == nil:
# top level asm statement? # top level asm statement?
appf(p.module.s[cfsProcHeaders], CC[cCompiler].asmStmtFrmt, [s]) addf(p.module.s[cfsProcHeaders], CC[cCompiler].asmStmtFrmt, [s])
else: else:
lineF(p, cpsStmts, CC[cCompiler].asmStmtFrmt, [s]) 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) = proc genEmit(p: BProc, t: PNode) =
var s = genAsmOrEmitStmt(p, t.sons[1]) var s = genAsmOrEmitStmt(p, t.sons[1])
if p.prc == nil: if p.prc == nil:
# top level emit pragma? # top level emit pragma?
genCLineDir(p.module.s[cfsProcHeaders], t.info) let section = determineSection(t[1])
app(p.module.s[cfsProcHeaders], s) genCLineDir(p.module.s[section], t.info)
add(p.module.s[section], s)
else: else:
genLineDir(p, t) genLineDir(p, t)
line(p, cpsStmts, s) line(p, cpsStmts, s)
var var
breakPointId: int = 0 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) = proc genBreakPoint(p: BProc, t: PNode) =
var name: string var name: string
@ -1003,7 +1032,7 @@ proc genBreakPoint(p: BProc, t: PNode) =
genLineDir(p, t) # BUGFIX genLineDir(p, t) # BUGFIX
appcg(p.module, gBreakpoints, appcg(p.module, gBreakpoints,
"#dbgRegisterBreakpoint($1, (NCSTRING)$2, (NCSTRING)$3);$n", [ "#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)]) makeCString(name)])
proc genWatchpoint(p: BProc, n: PNode) = proc genWatchpoint(p: BProc, n: PNode) =
@ -1066,7 +1095,9 @@ proc asgnFieldDiscriminant(p: BProc, e: PNode) =
proc genAsgn(p: BProc, e: PNode, fastAsgn: bool) = proc genAsgn(p: BProc, e: PNode, fastAsgn: bool) =
genLineDir(p, e) 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 var a: TLoc
initLocExpr(p, e.sons[0], a) initLocExpr(p, e.sons[0], a)
if fastAsgn: incl(a.flags, lfNoDeepCopy) if fastAsgn: incl(a.flags, lfNoDeepCopy)

View file

@ -19,12 +19,12 @@ proc accessThreadLocalVar(p: BProc, s: PSym) =
if emulatedThreadVars() and not p.threadVarAccessed: if emulatedThreadVars() and not p.threadVarAccessed:
p.threadVarAccessed = true p.threadVarAccessed = true
p.module.usesThreadVars = true p.module.usesThreadVars = true
appf(p.procSec(cpsLocals), "\tNimThreadVars* NimTV;$n") addf(p.procSec(cpsLocals), "\tNimThreadVars* NimTV;$n", [])
app(p.procSec(cpsInit), add(p.procSec(cpsInit),
ropecg(p.module, "\tNimTV = (NimThreadVars*) #GetThreadLocalVars();$n")) ropecg(p.module, "\tNimTV = (NimThreadVars*) #GetThreadLocalVars();$n"))
var 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 nimtvDeps: seq[PType] = @[] # type deps: every module needs whole struct
nimtvDeclared = initIntSet() # so that every var/field exists only once nimtvDeclared = initIntSet() # so that every var/field exists only once
# in the struct # in the struct
@ -43,23 +43,23 @@ proc declareThreadVar(m: BModule, s: PSym, isExtern: bool) =
# allocator for it :-( # allocator for it :-(
if not containsOrIncl(nimtvDeclared, s.id): if not containsOrIncl(nimtvDeclared, s.id):
nimtvDeps.add(s.loc.t) 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: else:
if isExtern: app(m.s[cfsVars], "extern ") if isExtern: add(m.s[cfsVars], "extern ")
if optThreads in gGlobalOptions: app(m.s[cfsVars], "NIM_THREADVAR ") if optThreads in gGlobalOptions: add(m.s[cfsVars], "NIM_THREADVAR ")
app(m.s[cfsVars], getTypeDesc(m, s.loc.t)) add(m.s[cfsVars], getTypeDesc(m, s.loc.t))
appf(m.s[cfsVars], " $1;$n", [s.loc.r]) addf(m.s[cfsVars], " $1;$n", [s.loc.r])
proc generateThreadLocalStorage(m: BModule) = proc generateThreadLocalStorage(m: BModule) =
if nimtv != nil and (m.usesThreadVars or sfMainModule in m.module.flags): if nimtv != nil and (m.usesThreadVars or sfMainModule in m.module.flags):
for t in items(nimtvDeps): discard getTypeDesc(m, t) 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) = proc generateThreadVarsSize(m: BModule) =
if nimtv != nil: if nimtv != nil:
let externc = if gCmd != cmdCompileToCpp and let externc = if gCmd != cmdCompileToCpp and
sfCompileToCpp in m.module.flags: "extern \"C\"" sfCompileToCpp in m.module.flags: "extern \"C\""
else: "" else: ""
appf(m.s[cfsProcs], addf(m.s[cfsProcs],
"$#NI NimThreadVarsSize(){return (NI)sizeof(NimThreadVars);}$n", "$#NI NimThreadVarsSize(){return (NI)sizeof(NimThreadVars);}$n",
[externc.toRope]) [externc.rope])

View file

@ -17,11 +17,11 @@ type
p: BProc p: BProc
visitorFrmt: string 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 genCaseRange(p: BProc, branch: PNode)
proc getTemp(p: BProc, t: PType, result: var TLoc; needsInit=false) 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 if n == nil: return
case n.kind case n.kind
of nkRecList: 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") if (n.sons[0].kind != nkSym): internalError(n.info, "genTraverseProc")
var p = c.p var p = c.p
let disc = n.sons[0].sym 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): for i in countup(1, sonsLen(n) - 1):
let branch = n.sons[i] let branch = n.sons[i]
assert branch.kind in {nkOfBranch, nkElse} assert branch.kind in {nkOfBranch, nkElse}
if branch.kind == nkOfBranch: if branch.kind == nkOfBranch:
genCaseRange(c.p, branch) genCaseRange(c.p, branch)
else: else:
lineF(p, cpsStmts, "default:$n") lineF(p, cpsStmts, "default:$n", [])
genTraverseProc(c, accessor, lastSon(branch)) genTraverseProc(c, accessor, lastSon(branch))
lineF(p, cpsStmts, "break;$n") lineF(p, cpsStmts, "break;$n", [])
lineF(p, cpsStmts, "} $n") lineF(p, cpsStmts, "} $n", [])
of nkSym: of nkSym:
let field = n.sym let field = n.sym
if field.loc.t == nil: if field.loc.t == nil:
internalError(n.info, "genTraverseProc()") 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()") 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: if not m.compileToCpp:
result = ropef("$1.Sup", accessor) result = "$1.Sup" % [accessor]
else: else:
result = accessor result = accessor
proc genTraverseProc(c: var TTraversalClosure, accessor: PRope, typ: PType) = proc genTraverseProc(c: var TTraversalClosure, accessor: Rope, typ: PType) =
if typ == nil: return if typ == nil: return
var p = c.p var p = c.p
case typ.kind case typ.kind
@ -66,9 +66,9 @@ proc genTraverseProc(c: var TTraversalClosure, accessor: PRope, typ: PType) =
var i: TLoc var i: TLoc
getTemp(p, getSysType(tyInt), i) getTemp(p, getSysType(tyInt), i)
linefmt(p, cpsStmts, "for ($1 = 0; $1 < $2; $1++) {$n", 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]) genTraverseProc(c, rfmt(nil, "$1[$2]", accessor, i.r), typ.sons[1])
lineF(p, cpsStmts, "}$n") lineF(p, cpsStmts, "}$n", [])
of tyObject: of tyObject:
for i in countup(0, sonsLen(typ) - 1): for i in countup(0, sonsLen(typ) - 1):
genTraverseProc(c, accessor.parentObj(c.p.module), typ.sons[i]) 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: of tyTuple:
let typ = getUniqueType(typ) let typ = getUniqueType(typ)
for i in countup(0, sonsLen(typ) - 1): 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: of tyRef, tyString, tySequence:
lineCg(p, cpsStmts, c.visitorFrmt, accessor) lineCg(p, cpsStmts, c.visitorFrmt, accessor)
of tyProc: of tyProc:
@ -85,17 +85,17 @@ proc genTraverseProc(c: var TTraversalClosure, accessor: PRope, typ: PType) =
else: else:
discard discard
proc genTraverseProcSeq(c: var TTraversalClosure, accessor: PRope, typ: PType) = proc genTraverseProcSeq(c: var TTraversalClosure, accessor: Rope, typ: PType) =
var p = c.p var p = c.p
assert typ.kind == tySequence assert typ.kind == tySequence
var i: TLoc var i: TLoc
getTemp(p, getSysType(tyInt), i) getTemp(p, getSysType(tyInt), i)
lineF(p, cpsStmts, "for ($1 = 0; $1 < $2->$3; $1++) {$n", lineF(p, cpsStmts, "for ($1 = 0; $1 < $2->$3; $1++) {$n",
i.r, accessor, toRope(if c.p.module.compileToCpp: "len" else: "Sup.len")) [i.r, accessor, rope(if c.p.module.compileToCpp: "len" else: "Sup.len")])
genTraverseProc(c, ropef("$1->data[$2]", accessor, i.r), typ.sons[0]) genTraverseProc(c, "$1->data[$2]" % [accessor, i.r], typ.sons[0])
lineF(p, cpsStmts, "}$n") 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 c: TTraversalClosure
var p = newProc(nil, m) var p = newProc(nil, m)
result = getGlobalTempName() result = getGlobalTempName()
@ -104,30 +104,30 @@ proc genTraverseProc(m: BModule, typ: PType, reason: TTypeInfoReason): PRope =
of tiNew: c.visitorFrmt = "#nimGCvisit((void*)$1, op);$n" of tiNew: c.visitorFrmt = "#nimGCvisit((void*)$1, op);$n"
else: assert false 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) let t = getTypeDesc(m, typ)
lineF(p, cpsLocals, "$1 a;$n", t) lineF(p, cpsLocals, "$1 a;$n", [t])
lineF(p, cpsInit, "a = ($1)p;$n", t) lineF(p, cpsInit, "a = ($1)p;$n", [t])
c.p = p c.p = p
assert typ.kind != tyTypeDesc assert typ.kind != tyTypeDesc
if typ.kind == tySequence: if typ.kind == tySequence:
genTraverseProcSeq(c, "a".toRope, typ) genTraverseProcSeq(c, "a".rope, typ)
else: else:
if skipTypes(typ.sons[0], typedescInst).kind in {tyArrayConstr, tyArray}: if skipTypes(typ.sons[0], typedescInst).kind in {tyArrayConstr, tyArray}:
# C's arrays are broken beyond repair: # C's arrays are broken beyond repair:
genTraverseProc(c, "a".toRope, typ.sons[0]) genTraverseProc(c, "a".rope, typ.sons[0])
else: else:
genTraverseProc(c, "(*a)".toRope, typ.sons[0]) genTraverseProc(c, "(*a)".rope, typ.sons[0])
let generatedProc = ropef("$1 {$n$2$3$4}$n", let generatedProc = "$1 {$n$2$3$4}$n" %
[header, p.s(cpsLocals), p.s(cpsInit), p.s(cpsStmts)]) [header, p.s(cpsLocals), p.s(cpsInit), p.s(cpsStmts)]
m.s[cfsProcHeaders].appf("$1;$n", header) m.s[cfsProcHeaders].addf("$1;$n", [header])
m.s[cfsProcs].app(generatedProc) 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) discard genTypeInfo(m, s.loc.t)
var c: TTraversalClosure var c: TTraversalClosure
@ -137,15 +137,15 @@ proc genTraverseProcForGlobal(m: BModule, s: PSym): PRope =
if sfThread in s.flags and emulatedThreadVars(): if sfThread in s.flags and emulatedThreadVars():
accessThreadLocalVar(p, s) accessThreadLocalVar(p, s)
sLoc = con("NimTV->", sLoc) sLoc = "NimTV->" & sLoc
c.visitorFrmt = "#nimGCvisit((void*)$1, 0);$n" c.visitorFrmt = "#nimGCvisit((void*)$1, 0);$n"
c.p = p c.p = p
let header = ropef("N_NIMCALL(void, $1)()", result) let header = "N_NIMCALL(void, $1)()" % [result]
genTraverseProc(c, sLoc, s.loc.t) genTraverseProc(c, sLoc, s.loc.t)
let generatedProc = ropef("$1 {$n$2$3$4}$n", let generatedProc = "$1 {$n$2$3$4}$n" %
[header, p.s(cpsLocals), p.s(cpsInit), p.s(cpsStmts)]) [header, p.s(cpsLocals), p.s(cpsInit), p.s(cpsStmts)]
m.s[cfsProcHeaders].appf("$1;$n", header) m.s[cfsProcHeaders].addf("$1;$n", [header])
m.s[cfsProcs].app(generatedProc) m.s[cfsProcs].add(generatedProc)

View file

@ -11,21 +11,22 @@
# ------------------------- Name Mangling -------------------------------- # ------------------------- Name Mangling --------------------------------
proc mangleField(name: string): string =
result = mangle(name)
result[0] = result[0].toUpper # Mangling makes everything lowercase,
# but some identifiers are C keywords
proc isKeyword(w: PIdent): bool = proc isKeyword(w: PIdent): bool =
# nimrod and C++ share some keywords # Nim and C++ share some keywords
# it's more efficient to test the whole nimrod keywords range # it's more efficient to test the whole Nim keywords range
case w.id case w.id
of ccgKeywordsLow..ccgKeywordsHigh, of ccgKeywordsLow..ccgKeywordsHigh,
nimKeywordsLow..nimKeywordsHigh, nimKeywordsLow..nimKeywordsHigh,
ord(wInline): return true ord(wInline): return true
else: return false else: return false
proc mangleName(s: PSym): PRope = proc mangleField(name: PIdent): string =
result = mangle(name.s)
if isKeyword(name):
result[0] = result[0].toUpper # Mangling makes everything lowercase,
# but some identifiers are C keywords
proc mangleName(s: PSym): Rope =
result = s.loc.r result = s.loc.r
if result == nil: if result == nil:
when oKeepVariableNames: when oKeepVariableNames:
@ -77,27 +78,27 @@ proc mangleName(s: PSym): PRope =
# These are not properly scoped now - we need to add blocks # These are not properly scoped now - we need to add blocks
# around for loops in transf # around for loops in transf
if keepOrigName: if keepOrigName:
result = s.name.s.mangle.newRope result = s.name.s.mangle.rope
else: else:
app(result, newRope(mangle(s.name.s))) add(result, rope(mangle(s.name.s)))
app(result, ~"_") add(result, ~"_")
app(result, toRope(s.id)) add(result, rope(s.id))
else: else:
app(result, newRope(mangle(s.name.s))) add(result, rope(mangle(s.name.s)))
app(result, ~"_") add(result, ~"_")
app(result, toRope(s.id)) add(result, rope(s.id))
s.loc.r = result s.loc.r = result
proc typeName(typ: PType): PRope = proc typeName(typ: PType): Rope =
result = if typ.sym != nil: typ.sym.name.s.mangle.toRope result = if typ.sym != nil: typ.sym.name.s.mangle.rope
else: ~"TY" else: ~"TY"
proc getTypeName(typ: PType): PRope = proc getTypeName(typ: PType): Rope =
if typ.sym != nil and {sfImportc, sfExportc} * typ.sym.flags != {}: if typ.sym != nil and {sfImportc, sfExportc} * typ.sym.flags != {}:
result = typ.sym.loc.r result = typ.sym.loc.r
else: else:
if typ.loc.r == nil: if typ.loc.r == nil:
typ.loc.r = con(typ.typeName, typ.id.toRope) typ.loc.r = typ.typeName & typ.id.rope
result = typ.loc.r result = typ.loc.r
if result == nil: internalError("getTypeName: " & $typ.kind) if result == nil: internalError("getTypeName: " & $typ.kind)
@ -110,7 +111,7 @@ proc mapSetType(typ: PType): TCTypeKind =
else: result = ctArray else: result = ctArray
proc mapType(typ: PType): TCTypeKind = proc mapType(typ: PType): TCTypeKind =
## Maps a nimrod type to a C type ## Maps a Nim type to a C type
case typ.kind case typ.kind
of tyNone, tyStmt: result = ctVoid of tyNone, tyStmt: result = ctVoid
of tyBool: result = ctBool of tyBool: result = ctBool
@ -156,7 +157,7 @@ proc isImportedType(t: PType): bool =
proc isImportedCppType(t: PType): bool = proc isImportedCppType(t: PType): bool =
result = t.sym != nil and sfInfixCall in t.sym.flags result = t.sym != nil and sfInfixCall in t.sym.flags
proc getTypeDescAux(m: BModule, typ: PType, check: var IntSet): PRope proc getTypeDescAux(m: BModule, typ: PType, check: var IntSet): Rope
proc needsComplexAssignment(typ: PType): bool = proc needsComplexAssignment(typ: PType): bool =
result = containsGarbageCollectedRef(typ) result = containsGarbageCollectedRef(typ)
@ -189,17 +190,17 @@ const
# but one can #define it to what one wants # but one can #define it to what one wants
"N_INLINE", "N_NOINLINE", "N_FASTCALL", "N_CLOSURE", "N_NOCONV"] "N_INLINE", "N_NOINLINE", "N_FASTCALL", "N_CLOSURE", "N_NOCONV"]
proc cacheGetType(tab: TIdTable, key: PType): PRope = proc cacheGetType(tab: TIdTable, key: PType): Rope =
# returns nil if we need to declare this type # returns nil if we need to declare this type
# since types are now unique via the ``getUniqueType`` mechanism, this slow # since types are now unique via the ``getUniqueType`` mechanism, this slow
# linear search is not necessary anymore: # linear search is not necessary anymore:
result = PRope(idTableGet(tab, key)) result = Rope(idTableGet(tab, key))
proc getTempName(): PRope = proc getTempName(): Rope =
result = rfmt(nil, "TMP$1", toRope(backendId())) result = rfmt(nil, "TMP$1", rope(backendId()))
proc getGlobalTempName(): PRope = proc getGlobalTempName(): Rope =
result = rfmt(nil, "TMP$1", toRope(backendId())) result = rfmt(nil, "TMP$1", rope(backendId()))
proc ccgIntroducedPtr(s: PSym): bool = proc ccgIntroducedPtr(s: PSym): bool =
var pt = skipTypes(s.typ, typedescInst) var pt = skipTypes(s.typ, typedescInst)
@ -226,13 +227,13 @@ proc fillResult(param: PSym) =
incl(param.loc.flags, lfIndirect) incl(param.loc.flags, lfIndirect)
param.loc.s = OnUnknown param.loc.s = OnUnknown
proc typeNameOrLiteral(t: PType, literal: string): PRope = proc typeNameOrLiteral(t: PType, literal: string): Rope =
if t.sym != nil and sfImportc in t.sym.flags and t.sym.magic == mNone: if t.sym != nil and sfImportc in t.sym.flags and t.sym.magic == mNone:
result = getTypeName(t) result = getTypeName(t)
else: else:
result = toRope(literal) result = rope(literal)
proc getSimpleTypeDesc(m: BModule, typ: PType): PRope = proc getSimpleTypeDesc(m: BModule, typ: PType): Rope =
const const
NumericalTypeToStr: array[tyInt..tyUInt64, string] = [ NumericalTypeToStr: array[tyInt..tyUInt64, string] = [
"NI", "NI8", "NI16", "NI32", "NI64", "NI", "NI8", "NI16", "NI32", "NI64",
@ -268,20 +269,20 @@ proc getSimpleTypeDesc(m: BModule, typ: PType): PRope =
proc pushType(m: BModule, typ: PType) = proc pushType(m: BModule, typ: PType) =
add(m.typeStack, typ) add(m.typeStack, typ)
proc getTypePre(m: BModule, typ: PType): PRope = proc getTypePre(m: BModule, typ: PType): Rope =
if typ == nil: result = toRope("void") if typ == nil: result = rope("void")
else: else:
result = getSimpleTypeDesc(m, typ) result = getSimpleTypeDesc(m, typ)
if result == nil: result = cacheGetType(m.typeCache, typ) if result == nil: result = cacheGetType(m.typeCache, typ)
proc structOrUnion(t: PType): PRope = proc structOrUnion(t: PType): Rope =
(if tfUnion in t.flags: toRope("union") else: toRope("struct")) (if tfUnion in t.flags: rope("union") else: rope("struct"))
proc getForwardStructFormat(m: BModule): string = proc getForwardStructFormat(m: BModule): string =
if m.compileToCpp: result = "$1 $2;$n" if m.compileToCpp: result = "$1 $2;$n"
else: result = "typedef $1 $2 $2;$n" else: result = "typedef $1 $2 $2;$n"
proc getTypeForward(m: BModule, typ: PType): PRope = proc getTypeForward(m: BModule, typ: PType): Rope =
result = cacheGetType(m.forwTypeCache, typ) result = cacheGetType(m.forwTypeCache, typ)
if result != nil: return if result != nil: return
result = getTypePre(m, typ) result = getTypePre(m, typ)
@ -290,12 +291,12 @@ proc getTypeForward(m: BModule, typ: PType): PRope =
of tySequence, tyTuple, tyObject: of tySequence, tyTuple, tyObject:
result = getTypeName(typ) result = getTypeName(typ)
if not isImportedType(typ): if not isImportedType(typ):
appf(m.s[cfsForwardTypes], getForwardStructFormat(m), addf(m.s[cfsForwardTypes], getForwardStructFormat(m),
[structOrUnion(typ), result]) [structOrUnion(typ), result])
idTablePut(m.forwTypeCache, typ, result) idTablePut(m.forwTypeCache, typ, result)
else: internalError("getTypeForward(" & $typ.kind & ')') else: internalError("getTypeForward(" & $typ.kind & ')')
proc getTypeDescWeak(m: BModule; t: PType; check: var IntSet): PRope = proc getTypeDescWeak(m: BModule; t: PType; check: var IntSet): Rope =
## like getTypeDescAux but creates only a *weak* dependency. In other words ## like getTypeDescAux but creates only a *weak* dependency. In other words
## we know we only need a pointer to it so we only generate a struct forward ## we know we only need a pointer to it so we only generate a struct forward
## declaration: ## declaration:
@ -310,7 +311,7 @@ proc getTypeDescWeak(m: BModule; t: PType; check: var IntSet): PRope =
pushType(m, x) pushType(m, x)
of tySequence: of tySequence:
let x = getUniqueType(etB) let x = getUniqueType(etB)
result = getTypeForward(m, x).con("*") result = getTypeForward(m, x) & "*"
pushType(m, x) pushType(m, x)
else: else:
result = getTypeDescAux(m, t, check) result = getTypeDescAux(m, t, check)
@ -321,7 +322,7 @@ proc paramStorageLoc(param: PSym): TStorageLoc =
else: else:
result = OnUnknown result = OnUnknown
proc genProcParams(m: BModule, t: PType, rettype, params: var PRope, proc genProcParams(m: BModule, t: PType, rettype, params: var Rope,
check: var IntSet, declareEnvironment=true) = check: var IntSet, declareEnvironment=true) =
params = nil params = nil
if (t.sons[0] == nil) or isInvalidReturnType(t.sons[0]): if (t.sons[0] == nil) or isInvalidReturnType(t.sons[0]):
@ -332,18 +333,18 @@ proc genProcParams(m: BModule, t: PType, rettype, params: var PRope,
if t.n.sons[i].kind != nkSym: internalError(t.n.info, "genProcParams") if t.n.sons[i].kind != nkSym: internalError(t.n.info, "genProcParams")
var param = t.n.sons[i].sym var param = t.n.sons[i].sym
if isCompileTimeOnly(param.typ): continue if isCompileTimeOnly(param.typ): continue
if params != nil: app(params, ~", ") if params != nil: add(params, ~", ")
fillLoc(param.loc, locParam, param.typ, mangleName(param), fillLoc(param.loc, locParam, param.typ, mangleName(param),
param.paramStorageLoc) param.paramStorageLoc)
if ccgIntroducedPtr(param): if ccgIntroducedPtr(param):
app(params, getTypeDescWeak(m, param.typ, check)) add(params, getTypeDescWeak(m, param.typ, check))
app(params, ~"*") add(params, ~"*")
incl(param.loc.flags, lfIndirect) incl(param.loc.flags, lfIndirect)
param.loc.s = OnUnknown param.loc.s = OnUnknown
else: else:
app(params, getTypeDescAux(m, param.typ, check)) add(params, getTypeDescAux(m, param.typ, check))
app(params, ~" ") add(params, ~" ")
app(params, param.loc.r) add(params, param.loc.r)
# declare the len field for open arrays: # declare the len field for open arrays:
var arr = param.typ var arr = param.typ
if arr.kind == tyVar: arr = arr.sons[0] if arr.kind == tyVar: arr = arr.sons[0]
@ -352,78 +353,78 @@ proc genProcParams(m: BModule, t: PType, rettype, params: var PRope,
# this fixes the 'sort' bug: # this fixes the 'sort' bug:
if param.typ.kind == tyVar: param.loc.s = OnUnknown if param.typ.kind == tyVar: param.loc.s = OnUnknown
# need to pass hidden parameter: # need to pass hidden parameter:
appf(params, ", NI $1Len$2", [param.loc.r, j.toRope]) addf(params, ", NI $1Len$2", [param.loc.r, j.rope])
inc(j) inc(j)
arr = arr.sons[0] arr = arr.sons[0]
if (t.sons[0] != nil) and isInvalidReturnType(t.sons[0]): if (t.sons[0] != nil) and isInvalidReturnType(t.sons[0]):
var arr = t.sons[0] var arr = t.sons[0]
if params != nil: app(params, ", ") if params != nil: add(params, ", ")
if (mapReturnType(t.sons[0]) != ctArray): if (mapReturnType(t.sons[0]) != ctArray):
app(params, getTypeDescWeak(m, arr, check)) add(params, getTypeDescWeak(m, arr, check))
app(params, "*") add(params, "*")
else: else:
app(params, getTypeDescAux(m, arr, check)) add(params, getTypeDescAux(m, arr, check))
appf(params, " Result", []) addf(params, " Result", [])
if t.callConv == ccClosure and declareEnvironment: if t.callConv == ccClosure and declareEnvironment:
if params != nil: app(params, ", ") if params != nil: add(params, ", ")
app(params, "void* ClEnv") add(params, "void* ClEnv")
if tfVarargs in t.flags: if tfVarargs in t.flags:
if params != nil: app(params, ", ") if params != nil: add(params, ", ")
app(params, "...") add(params, "...")
if params == nil: app(params, "void)") if params == nil: add(params, "void)")
else: app(params, ")") else: add(params, ")")
params = con("(", params) params = "(" & params
proc mangleRecFieldName(field: PSym, rectype: PType): PRope = proc mangleRecFieldName(field: PSym, rectype: PType): Rope =
if (rectype.sym != nil) and if (rectype.sym != nil) and
({sfImportc, sfExportc} * rectype.sym.flags != {}): ({sfImportc, sfExportc} * rectype.sym.flags != {}):
result = field.loc.r result = field.loc.r
else: else:
result = toRope(mangleField(field.name.s)) result = rope(mangleField(field.name))
if result == nil: internalError(field.info, "mangleRecFieldName") if result == nil: internalError(field.info, "mangleRecFieldName")
proc genRecordFieldsAux(m: BModule, n: PNode, proc genRecordFieldsAux(m: BModule, n: PNode,
accessExpr: PRope, rectype: PType, accessExpr: Rope, rectype: PType,
check: var IntSet): PRope = check: var IntSet): Rope =
var var
ae, uname, sname, a: PRope ae, uname, sname, a: Rope
k: PNode k: PNode
field: PSym field: PSym
result = nil result = nil
case n.kind case n.kind
of nkRecList: of nkRecList:
for i in countup(0, sonsLen(n) - 1): for i in countup(0, sonsLen(n) - 1):
app(result, genRecordFieldsAux(m, n.sons[i], accessExpr, rectype, check)) add(result, genRecordFieldsAux(m, n.sons[i], accessExpr, rectype, check))
of nkRecCase: of nkRecCase:
if n.sons[0].kind != nkSym: internalError(n.info, "genRecordFieldsAux") if n.sons[0].kind != nkSym: internalError(n.info, "genRecordFieldsAux")
app(result, genRecordFieldsAux(m, n.sons[0], accessExpr, rectype, check)) add(result, genRecordFieldsAux(m, n.sons[0], accessExpr, rectype, check))
uname = toRope(mangle(n.sons[0].sym.name.s) & 'U') uname = rope(mangle(n.sons[0].sym.name.s) & 'U')
if accessExpr != nil: ae = ropef("$1.$2", [accessExpr, uname]) if accessExpr != nil: ae = "$1.$2" % [accessExpr, uname]
else: ae = uname else: ae = uname
var unionBody: PRope = nil var unionBody: Rope = nil
for i in countup(1, sonsLen(n) - 1): for i in countup(1, sonsLen(n) - 1):
case n.sons[i].kind case n.sons[i].kind
of nkOfBranch, nkElse: of nkOfBranch, nkElse:
k = lastSon(n.sons[i]) k = lastSon(n.sons[i])
if k.kind != nkSym: if k.kind != nkSym:
sname = con("S", toRope(i)) sname = "S" & rope(i)
a = genRecordFieldsAux(m, k, ropef("$1.$2", [ae, sname]), rectype, a = genRecordFieldsAux(m, k, "$1.$2" % [ae, sname], rectype,
check) check)
if a != nil: if a != nil:
app(unionBody, "struct {") add(unionBody, "struct {")
app(unionBody, a) add(unionBody, a)
appf(unionBody, "} $1;$n", [sname]) addf(unionBody, "} $1;$n", [sname])
else: else:
app(unionBody, genRecordFieldsAux(m, k, ae, rectype, check)) add(unionBody, genRecordFieldsAux(m, k, ae, rectype, check))
else: internalError("genRecordFieldsAux(record case branch)") else: internalError("genRecordFieldsAux(record case branch)")
if unionBody != nil: if unionBody != nil:
appf(result, "union{$n$1} $2;$n", [unionBody, uname]) addf(result, "union{$n$1} $2;$n", [unionBody, uname])
of nkSym: of nkSym:
field = n.sym field = n.sym
if field.typ.kind == tyEmpty: return if field.typ.kind == tyEmpty: return
#assert(field.ast == nil) #assert(field.ast == nil)
sname = mangleRecFieldName(field, rectype) sname = mangleRecFieldName(field, rectype)
if accessExpr != nil: ae = ropef("$1.$2", [accessExpr, sname]) if accessExpr != nil: ae = "$1.$2" % [accessExpr, sname]
else: ae = sname else: ae = sname
fillLoc(field.loc, locField, field.typ, ae, OnUnknown) fillLoc(field.loc, locField, field.typ, ae, OnUnknown)
# for importcpp'ed objects, we only need to set field.loc, but don't # for importcpp'ed objects, we only need to set field.loc, but don't
@ -432,27 +433,27 @@ proc genRecordFieldsAux(m: BModule, n: PNode,
if not isImportedCppType(rectype): if not isImportedCppType(rectype):
let fieldType = field.loc.t.skipTypes(abstractInst) let fieldType = field.loc.t.skipTypes(abstractInst)
if fieldType.kind == tyArray and tfUncheckedArray in fieldType.flags: if fieldType.kind == tyArray and tfUncheckedArray in fieldType.flags:
appf(result, "$1 $2[SEQ_DECL_SIZE];$n", addf(result, "$1 $2[SEQ_DECL_SIZE];$n",
[getTypeDescAux(m, fieldType.elemType, check), sname]) [getTypeDescAux(m, fieldType.elemType, check), sname])
elif fieldType.kind == tySequence: elif fieldType.kind == tySequence:
# we need to use a weak dependency here for trecursive_table. # we need to use a weak dependency here for trecursive_table.
appf(result, "$1 $2;$n", [getTypeDescWeak(m, field.loc.t, check), sname]) addf(result, "$1 $2;$n", [getTypeDescWeak(m, field.loc.t, check), sname])
else: else:
# don't use fieldType here because we need the # don't use fieldType here because we need the
# tyGenericInst for C++ template support # tyGenericInst for C++ template support
appf(result, "$1 $2;$n", [getTypeDescAux(m, field.loc.t, check), sname]) addf(result, "$1 $2;$n", [getTypeDescAux(m, field.loc.t, check), sname])
else: internalError(n.info, "genRecordFieldsAux()") else: internalError(n.info, "genRecordFieldsAux()")
proc getRecordFields(m: BModule, typ: PType, check: var IntSet): PRope = proc getRecordFields(m: BModule, typ: PType, check: var IntSet): Rope =
result = genRecordFieldsAux(m, typ.n, nil, typ, check) result = genRecordFieldsAux(m, typ.n, nil, typ, check)
proc getRecordDesc(m: BModule, typ: PType, name: PRope, proc getRecordDesc(m: BModule, typ: PType, name: Rope,
check: var IntSet): PRope = check: var IntSet): Rope =
# declare the record: # declare the record:
var hasField = false var hasField = false
var attribute: PRope = var attribute: Rope =
if tfPacked in typ.flags: toRope(CC[cCompiler].packedPragma) if tfPacked in typ.flags: rope(CC[cCompiler].packedPragma)
else: nil else: nil
result = ropecg(m, CC[cCompiler].structStmtFmt, result = ropecg(m, CC[cCompiler].structStmtFmt,
@ -475,27 +476,54 @@ proc getRecordDesc(m: BModule, typ: PType, name: PRope,
[getTypeDescAux(m, typ.sons[0], check)]) [getTypeDescAux(m, typ.sons[0], check)])
hasField = true hasField = true
else: else:
appf(result, " {$n", [name]) addf(result, " {$n", [name])
var desc = getRecordFields(m, typ, check) var desc = getRecordFields(m, typ, check)
if desc == nil and not hasField: if desc == nil and not hasField:
appf(result, "char dummy;$n", []) addf(result, "char dummy;$n", [])
else: else:
app(result, desc) add(result, desc)
app(result, "};" & tnl) add(result, "};" & tnl)
proc getTupleDesc(m: BModule, typ: PType, name: PRope, proc getTupleDesc(m: BModule, typ: PType, name: Rope,
check: var IntSet): PRope = check: var IntSet): Rope =
result = ropef("$1 $2 {$n", [structOrUnion(typ), name]) result = "$1 $2 {$n" % [structOrUnion(typ), name]
var desc: PRope = nil var desc: Rope = nil
for i in countup(0, sonsLen(typ) - 1): for i in countup(0, sonsLen(typ) - 1):
appf(desc, "$1 Field$2;$n", addf(desc, "$1 Field$2;$n",
[getTypeDescAux(m, typ.sons[i], check), toRope(i)]) [getTypeDescAux(m, typ.sons[i], check), rope(i)])
if desc == nil: app(result, "char dummy;" & tnl) if desc == nil: add(result, "char dummy;" & tnl)
else: app(result, desc) else: add(result, desc)
app(result, "};" & tnl) add(result, "};" & tnl)
proc getTypeDescAux(m: BModule, typ: PType, check: var IntSet): PRope = proc scanCppGenericSlot(pat: string, cursor, outIdx, outStars: var int): bool =
# A helper proc for handling cppimport patterns, involving numeric
# placeholders for generic types (e.g. '0, '**2, etc).
# pre: the cursor must be placed at the ' symbol
# post: the cursor will be placed after the final digit
# false will returned if the input is not recognized as a placeholder
inc cursor
let begin = cursor
while pat[cursor] == '*': inc cursor
if pat[cursor] in Digits:
outIdx = pat[cursor].ord - '0'.ord
outStars = cursor - begin
inc cursor
return true
else:
return false
proc resolveStarsInCppType(typ: PType, idx, stars: int): PType =
# XXX: we should catch this earlier and report it as a semantic error
if idx >= typ.len: internalError "invalid apostrophe type parameter index"
result = typ.sons[idx]
for i in 1..stars:
if result != nil and result.len > 0:
result = if result.kind == tyGenericInst: result.sons[1]
else: result.elemType
proc getTypeDescAux(m: BModule, typ: PType, check: var IntSet): Rope =
# returns only the type's name # returns only the type's name
var t = getUniqueType(typ) var t = getUniqueType(typ)
if t == nil: internalError("getTypeDescAux: t == nil") if t == nil: internalError("getTypeDescAux: t == nil")
@ -523,39 +551,39 @@ proc getTypeDescAux(m: BModule, typ: PType, check: var IntSet): PRope =
case etB.kind case etB.kind
of tyObject, tyTuple: of tyObject, tyTuple:
if isImportedCppType(etB) and et.kind == tyGenericInst: if isImportedCppType(etB) and et.kind == tyGenericInst:
result = con(getTypeDescAux(m, et, check), star) result = getTypeDescAux(m, et, check) & star
else: else:
# no restriction! We have a forward declaration for structs # no restriction! We have a forward declaration for structs
let x = getUniqueType(etB) let x = getUniqueType(etB)
let name = getTypeForward(m, x) let name = getTypeForward(m, x)
result = con(name, star) result = name & star
idTablePut(m.typeCache, t, result) idTablePut(m.typeCache, t, result)
pushType(m, x) pushType(m, x)
of tySequence: of tySequence:
# no restriction! We have a forward declaration for structs # no restriction! We have a forward declaration for structs
let x = getUniqueType(etB) let x = getUniqueType(etB)
let name = getTypeForward(m, x) let name = getTypeForward(m, x)
result = con(name, "*" & star) result = name & "*" & star
idTablePut(m.typeCache, t, result) idTablePut(m.typeCache, t, result)
pushType(m, x) pushType(m, x)
else: else:
# else we have a strong dependency :-( # else we have a strong dependency :-(
result = con(getTypeDescAux(m, et, check), star) result = getTypeDescAux(m, et, check) & star
idTablePut(m.typeCache, t, result) idTablePut(m.typeCache, t, result)
of tyOpenArray, tyVarargs: of tyOpenArray, tyVarargs:
result = con(getTypeDescAux(m, t.sons[0], check), "*") result = getTypeDescAux(m, t.sons[0], check) & "*"
idTablePut(m.typeCache, t, result) idTablePut(m.typeCache, t, result)
of tyProc: of tyProc:
result = getTypeName(t) result = getTypeName(t)
idTablePut(m.typeCache, t, result) idTablePut(m.typeCache, t, result)
var rettype, desc: PRope var rettype, desc: Rope
genProcParams(m, t, rettype, desc, check) genProcParams(m, t, rettype, desc, check)
if not isImportedType(t): if not isImportedType(t):
if t.callConv != ccClosure: # procedure vars may need a closure! if t.callConv != ccClosure: # procedure vars may need a closure!
appf(m.s[cfsTypes], "typedef $1_PTR($2, $3) $4;$n", addf(m.s[cfsTypes], "typedef $1_PTR($2, $3) $4;$n",
[toRope(CallingConvToStr[t.callConv]), rettype, result, desc]) [rope(CallingConvToStr[t.callConv]), rettype, result, desc])
else: else:
appf(m.s[cfsTypes], "typedef struct {$n" & addf(m.s[cfsTypes], "typedef struct {$n" &
"N_NIMCALL_PTR($2, ClPrc) $3;$n" & "N_NIMCALL_PTR($2, ClPrc) $3;$n" &
"void* ClEnv;$n} $1;$n", "void* ClEnv;$n} $1;$n",
[result, rettype, desc]) [result, rettype, desc])
@ -566,11 +594,11 @@ proc getTypeDescAux(m: BModule, typ: PType, check: var IntSet): PRope =
if result == nil: if result == nil:
result = getTypeName(t) result = getTypeName(t)
if not isImportedType(t): if not isImportedType(t):
appf(m.s[cfsForwardTypes], getForwardStructFormat(m), addf(m.s[cfsForwardTypes], getForwardStructFormat(m),
[structOrUnion(t), result]) [structOrUnion(t), result])
idTablePut(m.forwTypeCache, t, result) idTablePut(m.forwTypeCache, t, result)
assert(cacheGetType(m.typeCache, t) == nil) assert(cacheGetType(m.typeCache, t) == nil)
idTablePut(m.typeCache, t, con(result, "*")) idTablePut(m.typeCache, t, result & "*")
if not isImportedType(t): if not isImportedType(t):
if skipTypes(t.sons[0], typedescInst).kind != tyEmpty: if skipTypes(t.sons[0], typedescInst).kind != tyEmpty:
const const
@ -582,8 +610,8 @@ proc getTypeDescAux(m: BModule, typ: PType, check: var IntSet): PRope =
" $1 data[SEQ_DECL_SIZE];$n" & " $1 data[SEQ_DECL_SIZE];$n" &
"};$n", [getTypeDescAux(m, t.sons[0], check), result]) "};$n", [getTypeDescAux(m, t.sons[0], check), result])
else: else:
result = toRope("TGenericSeq") result = rope("TGenericSeq")
app(result, "*") add(result, "*")
of tyArrayConstr, tyArray: of tyArrayConstr, tyArray:
var n: BiggestInt = lengthOrd(t) var n: BiggestInt = lengthOrd(t)
if n <= 0: n = 1 # make an array of at least one element if n <= 0: n = 1 # make an array of at least one element
@ -591,17 +619,39 @@ proc getTypeDescAux(m: BModule, typ: PType, check: var IntSet): PRope =
idTablePut(m.typeCache, t, result) idTablePut(m.typeCache, t, result)
if not isImportedType(t): if not isImportedType(t):
let foo = getTypeDescAux(m, t.sons[1], check) let foo = getTypeDescAux(m, t.sons[1], check)
appf(m.s[cfsTypes], "typedef $1 $2[$3];$n", addf(m.s[cfsTypes], "typedef $1 $2[$3];$n",
[foo, result, toRope(n)]) [foo, result, rope(n)])
of tyObject, tyTuple: of tyObject, tyTuple:
if isImportedCppType(t) and typ.kind == tyGenericInst: if isImportedCppType(t) and typ.kind == tyGenericInst:
# for instantiated templates we do not go through the type cache as the # for instantiated templates we do not go through the type cache as the
# the type cache is not aware of 'tyGenericInst'. # the type cache is not aware of 'tyGenericInst'.
result = getTypeName(t).con("<") let cppName = getTypeName(t)
var i = 0
var chunkStart = 0
while i < cppName.data.len:
if cppName.data[i] == '\'':
var chunkEnd = <i
var idx, stars: int
if scanCppGenericSlot(cppName.data, i, idx, stars):
result.add cppName.data.substr(chunkStart, chunkEnd)
chunkStart = i
let typeInSlot = resolveStarsInCppType(typ, idx + 1, stars)
if typeInSlot == nil or typeInSlot.kind == tyEmpty:
result.add(~"void")
else:
result.add getTypeDescAux(m, typeInSlot, check)
else:
inc i
if chunkStart != 0:
result.add cppName.data.substr(chunkStart)
else:
result = cppName & "<"
for i in 1 .. typ.len-2: for i in 1 .. typ.len-2:
if i > 1: result.app(", ") if i > 1: result.add(", ")
result.app(getTypeDescAux(m, typ.sons[i], check)) result.add(getTypeDescAux(m, typ.sons[i], check))
result.app("> ") result.add("> ")
# always call for sideeffects: # always call for sideeffects:
assert t.kind != tyTuple assert t.kind != tyTuple
discard getRecordDesc(m, t, result, check) discard getRecordDesc(m, t, result, check)
@ -610,25 +660,25 @@ proc getTypeDescAux(m: BModule, typ: PType, check: var IntSet): PRope =
if result == nil: if result == nil:
result = getTypeName(t) result = getTypeName(t)
if not isImportedType(t): if not isImportedType(t):
appf(m.s[cfsForwardTypes], getForwardStructFormat(m), addf(m.s[cfsForwardTypes], getForwardStructFormat(m),
[structOrUnion(t), result]) [structOrUnion(t), result])
idTablePut(m.forwTypeCache, t, result) idTablePut(m.forwTypeCache, t, result)
idTablePut(m.typeCache, t, result) # always call for sideeffects: idTablePut(m.typeCache, t, result) # always call for sideeffects:
let recdesc = if t.kind != tyTuple: getRecordDesc(m, t, result, check) let recdesc = if t.kind != tyTuple: getRecordDesc(m, t, result, check)
else: getTupleDesc(m, t, result, check) else: getTupleDesc(m, t, result, check)
if not isImportedType(t): app(m.s[cfsTypes], recdesc) if not isImportedType(t): add(m.s[cfsTypes], recdesc)
of tySet: of tySet:
case int(getSize(t)) case int(getSize(t))
of 1: result = toRope("NU8") of 1: result = rope("NU8")
of 2: result = toRope("NU16") of 2: result = rope("NU16")
of 4: result = toRope("NU32") of 4: result = rope("NU32")
of 8: result = toRope("NU64") of 8: result = rope("NU64")
else: else:
result = getTypeName(t) result = getTypeName(t)
idTablePut(m.typeCache, t, result) idTablePut(m.typeCache, t, result)
if not isImportedType(t): if not isImportedType(t):
appf(m.s[cfsTypes], "typedef NU8 $1[$2];$n", addf(m.s[cfsTypes], "typedef NU8 $1[$2];$n",
[result, toRope(getSize(t))]) [result, rope(getSize(t))])
of tyGenericInst, tyDistinct, tyOrdinal, tyConst, tyMutable, of tyGenericInst, tyDistinct, tyOrdinal, tyConst, tyMutable,
tyIter, tyTypeDesc: tyIter, tyTypeDesc:
result = getTypeDescAux(m, lastSon(t), check) result = getTypeDescAux(m, lastSon(t), check)
@ -638,7 +688,7 @@ proc getTypeDescAux(m: BModule, typ: PType, check: var IntSet): PRope =
# fixes bug #145: # fixes bug #145:
excl(check, t.id) excl(check, t.id)
proc getTypeDesc(m: BModule, typ: PType): PRope = proc getTypeDesc(m: BModule, typ: PType): Rope =
var check = initIntSet() var check = initIntSet()
result = getTypeDescAux(m, typ, check) result = getTypeDescAux(m, typ, check)
@ -646,23 +696,23 @@ type
TClosureTypeKind = enum TClosureTypeKind = enum
clHalf, clHalfWithEnv, clFull clHalf, clHalfWithEnv, clFull
proc getClosureType(m: BModule, t: PType, kind: TClosureTypeKind): PRope = proc getClosureType(m: BModule, t: PType, kind: TClosureTypeKind): Rope =
assert t.kind == tyProc assert t.kind == tyProc
var check = initIntSet() var check = initIntSet()
result = getTempName() result = getTempName()
var rettype, desc: PRope var rettype, desc: Rope
genProcParams(m, t, rettype, desc, check, declareEnvironment=kind != clHalf) genProcParams(m, t, rettype, desc, check, declareEnvironment=kind != clHalf)
if not isImportedType(t): if not isImportedType(t):
if t.callConv != ccClosure or kind != clFull: if t.callConv != ccClosure or kind != clFull:
appf(m.s[cfsTypes], "typedef $1_PTR($2, $3) $4;$n", addf(m.s[cfsTypes], "typedef $1_PTR($2, $3) $4;$n",
[toRope(CallingConvToStr[t.callConv]), rettype, result, desc]) [rope(CallingConvToStr[t.callConv]), rettype, result, desc])
else: else:
appf(m.s[cfsTypes], "typedef struct {$n" & addf(m.s[cfsTypes], "typedef struct {$n" &
"N_NIMCALL_PTR($2, ClPrc) $3;$n" & "N_NIMCALL_PTR($2, ClPrc) $3;$n" &
"void* ClEnv;$n} $1;$n", "void* ClEnv;$n} $1;$n",
[result, rettype, desc]) [result, rettype, desc])
proc getTypeDesc(m: BModule, magic: string): PRope = proc getTypeDesc(m: BModule, magic: string): Rope =
var sym = magicsys.getCompilerProc(magic) var sym = magicsys.getCompilerProc(magic)
if sym != nil: if sym != nil:
result = getTypeDesc(m, sym.typ) result = getTypeDesc(m, sym.typ)
@ -678,38 +728,38 @@ proc finishTypeDescriptions(m: BModule) =
template cgDeclFrmt*(s: PSym): string = s.constraint.strVal template cgDeclFrmt*(s: PSym): string = s.constraint.strVal
proc genProcHeader(m: BModule, prc: PSym): PRope = proc genProcHeader(m: BModule, prc: PSym): Rope =
var var
rettype, params: PRope rettype, params: Rope
genCLineDir(result, prc.info) genCLineDir(result, prc.info)
# using static is needed for inline procs # using static is needed for inline procs
if lfExportLib in prc.loc.flags: if lfExportLib in prc.loc.flags:
if m.isHeaderFile: if m.isHeaderFile:
result.app "N_LIB_IMPORT " result.add "N_LIB_IMPORT "
else: else:
result.app "N_LIB_EXPORT " result.add "N_LIB_EXPORT "
elif prc.typ.callConv == ccInline: elif prc.typ.callConv == ccInline:
result.app "static " result.add "static "
var check = initIntSet() var check = initIntSet()
fillLoc(prc.loc, locProc, prc.typ, mangleName(prc), OnUnknown) fillLoc(prc.loc, locProc, prc.typ, mangleName(prc), OnUnknown)
genProcParams(m, prc.typ, rettype, params, check) genProcParams(m, prc.typ, rettype, params, check)
# careful here! don't access ``prc.ast`` as that could reload large parts of # careful here! don't access ``prc.ast`` as that could reload large parts of
# the object graph! # the object graph!
if prc.constraint.isNil: if prc.constraint.isNil:
appf(result, "$1($2, $3)$4", addf(result, "$1($2, $3)$4",
[toRope(CallingConvToStr[prc.typ.callConv]), rettype, prc.loc.r, [rope(CallingConvToStr[prc.typ.callConv]), rettype, prc.loc.r,
params]) params])
else: else:
result = ropef(prc.cgDeclFrmt, [rettype, prc.loc.r, params]) result = prc.cgDeclFrmt % [rettype, prc.loc.r, params]
# ------------------ type info generation ------------------------------------- # ------------------ type info generation -------------------------------------
proc genTypeInfo(m: BModule, t: PType): PRope proc genTypeInfo(m: BModule, t: PType): Rope
proc getNimNode(m: BModule): PRope = proc getNimNode(m: BModule): Rope =
result = ropef("$1[$2]", [m.typeNodesName, toRope(m.typeNodes)]) result = "$1[$2]" % [m.typeNodesName, rope(m.typeNodes)]
inc(m.typeNodes) inc(m.typeNodes)
proc genTypeInfoAuxBase(m: BModule; typ, origType: PType; name, base: PRope) = proc genTypeInfoAuxBase(m: BModule; typ, origType: PType; name, base: Rope) =
var nimtypeKind: int var nimtypeKind: int
#allocMemTI(m, typ, name) #allocMemTI(m, typ, name)
if isObjLackingTypeField(typ): if isObjLackingTypeField(typ):
@ -717,48 +767,47 @@ proc genTypeInfoAuxBase(m: BModule; typ, origType: PType; name, base: PRope) =
else: else:
nimtypeKind = ord(typ.kind) nimtypeKind = ord(typ.kind)
var size: PRope var size: Rope
if tfIncompleteStruct in typ.flags: size = toRope"void*" if tfIncompleteStruct in typ.flags: size = rope"void*"
elif m.compileToCpp: size = getTypeDesc(m, origType) elif m.compileToCpp: size = getTypeDesc(m, origType)
else: size = getTypeDesc(m, typ) else: size = getTypeDesc(m, typ)
appf(m.s[cfsTypeInit3], addf(m.s[cfsTypeInit3],
"$1.size = sizeof($2);$n" & "$1.kind = $3;$n" & "$1.base = $4;$n", "$1.size = sizeof($2);$n" & "$1.kind = $3;$n" & "$1.base = $4;$n",
[name, size, toRope(nimtypeKind), base]) [name, size, rope(nimtypeKind), base])
# compute type flags for GC optimization # compute type flags for GC optimization
var flags = 0 var flags = 0
if not containsGarbageCollectedRef(typ): flags = flags or 1 if not containsGarbageCollectedRef(typ): flags = flags or 1
if not canFormAcycle(typ): flags = flags or 2 if not canFormAcycle(typ): flags = flags or 2
#else MessageOut("can contain a cycle: " & typeToString(typ)) #else MessageOut("can contain a cycle: " & typeToString(typ))
if flags != 0: if flags != 0:
appf(m.s[cfsTypeInit3], "$1.flags = $2;$n", [name, toRope(flags)]) addf(m.s[cfsTypeInit3], "$1.flags = $2;$n", [name, rope(flags)])
discard cgsym(m, "TNimType") discard cgsym(m, "TNimType")
appf(m.s[cfsVars], "TNimType $1; /* $2 */$n", addf(m.s[cfsVars], "TNimType $1; /* $2 */$n",
[name, toRope(typeToString(typ))]) [name, rope(typeToString(typ))])
proc genTypeInfoAux(m: BModule, typ, origType: PType, name: PRope) = proc genTypeInfoAux(m: BModule, typ, origType: PType, name: Rope) =
var base: PRope var base: Rope
if (sonsLen(typ) > 0) and (typ.sons[0] != nil): if (sonsLen(typ) > 0) and (typ.sons[0] != nil):
base = genTypeInfo(m, typ.sons[0]) base = genTypeInfo(m, typ.sons[0])
else: else:
base = toRope("0") base = rope("0")
genTypeInfoAuxBase(m, typ, origType, name, base) genTypeInfoAuxBase(m, typ, origType, name, base)
proc discriminatorTableName(m: BModule, objtype: PType, d: PSym): PRope = proc discriminatorTableName(m: BModule, objtype: PType, d: PSym): Rope =
# bugfix: we need to search the type that contains the discriminator: # bugfix: we need to search the type that contains the discriminator:
var objtype = objtype var objtype = objtype
while lookupInRecord(objtype.n, d.name) == nil: while lookupInRecord(objtype.n, d.name) == nil:
objtype = objtype.sons[0] objtype = objtype.sons[0]
if objtype.sym == nil: if objtype.sym == nil:
internalError(d.info, "anonymous obj with discriminator") internalError(d.info, "anonymous obj with discriminator")
result = ropef("NimDT_$1_$2", [ result = "NimDT_$1_$2" % [rope(objtype.id), rope(d.name.s.mangle)]
toRope(objtype.id), toRope(d.name.s.mangle)])
proc discriminatorTableDecl(m: BModule, objtype: PType, d: PSym): PRope = proc discriminatorTableDecl(m: BModule, objtype: PType, d: PSym): Rope =
discard cgsym(m, "TNimNode") discard cgsym(m, "TNimNode")
var tmp = discriminatorTableName(m, objtype, d) var tmp = discriminatorTableName(m, objtype, d)
result = ropef("TNimNode* $1[$2];$n", [tmp, toRope(lengthOrd(d.typ)+1)]) result = "TNimNode* $1[$2];$n" % [tmp, rope(lengthOrd(d.typ)+1)]
proc genObjectFields(m: BModule, typ: PType, n: PNode, expr: PRope) = proc genObjectFields(m: BModule, typ: PType, n: PNode, expr: Rope) =
case n.kind case n.kind
of nkRecList: of nkRecList:
var L = sonsLen(n) var L = sonsLen(n)
@ -766,29 +815,29 @@ proc genObjectFields(m: BModule, typ: PType, n: PNode, expr: PRope) =
genObjectFields(m, typ, n.sons[0], expr) genObjectFields(m, typ, n.sons[0], expr)
elif L > 0: elif L > 0:
var tmp = getTempName() var tmp = getTempName()
appf(m.s[cfsTypeInit1], "static TNimNode* $1[$2];$n", [tmp, toRope(L)]) addf(m.s[cfsTypeInit1], "static TNimNode* $1[$2];$n", [tmp, rope(L)])
for i in countup(0, L-1): for i in countup(0, L-1):
var tmp2 = getNimNode(m) var tmp2 = getNimNode(m)
appf(m.s[cfsTypeInit3], "$1[$2] = &$3;$n", [tmp, toRope(i), tmp2]) addf(m.s[cfsTypeInit3], "$1[$2] = &$3;$n", [tmp, rope(i), tmp2])
genObjectFields(m, typ, n.sons[i], tmp2) genObjectFields(m, typ, n.sons[i], tmp2)
appf(m.s[cfsTypeInit3], "$1.len = $2; $1.kind = 2; $1.sons = &$3[0];$n", addf(m.s[cfsTypeInit3], "$1.len = $2; $1.kind = 2; $1.sons = &$3[0];$n",
[expr, toRope(L), tmp]) [expr, rope(L), tmp])
else: else:
appf(m.s[cfsTypeInit3], "$1.len = $2; $1.kind = 2;$n", [expr, toRope(L)]) addf(m.s[cfsTypeInit3], "$1.len = $2; $1.kind = 2;$n", [expr, rope(L)])
of nkRecCase: of nkRecCase:
assert(n.sons[0].kind == nkSym) assert(n.sons[0].kind == nkSym)
var field = n.sons[0].sym var field = n.sons[0].sym
var tmp = discriminatorTableName(m, typ, field) var tmp = discriminatorTableName(m, typ, field)
var L = lengthOrd(field.typ) var L = lengthOrd(field.typ)
assert L > 0 assert L > 0
appf(m.s[cfsTypeInit3], "$1.kind = 3;$n" & addf(m.s[cfsTypeInit3], "$1.kind = 3;$n" &
"$1.offset = offsetof($2, $3);$n" & "$1.typ = $4;$n" & "$1.offset = offsetof($2, $3);$n" & "$1.typ = $4;$n" &
"$1.name = $5;$n" & "$1.sons = &$6[0];$n" & "$1.name = $5;$n" & "$1.sons = &$6[0];$n" &
"$1.len = $7;$n", [expr, getTypeDesc(m, typ), field.loc.r, "$1.len = $7;$n", [expr, getTypeDesc(m, typ), field.loc.r,
genTypeInfo(m, field.typ), genTypeInfo(m, field.typ),
makeCString(field.name.s), makeCString(field.name.s),
tmp, toRope(L)]) tmp, rope(L)])
appf(m.s[cfsData], "TNimNode* $1[$2];$n", [tmp, toRope(L+1)]) addf(m.s[cfsData], "TNimNode* $1[$2];$n", [tmp, rope(L+1)])
for i in countup(1, sonsLen(n)-1): for i in countup(1, sonsLen(n)-1):
var b = n.sons[i] # branch var b = n.sons[i] # branch
var tmp2 = getNimNode(m) var tmp2 = getNimNode(m)
@ -802,60 +851,60 @@ proc genObjectFields(m: BModule, typ: PType, n: PNode, expr: PRope) =
var x = int(getOrdValue(b.sons[j].sons[0])) var x = int(getOrdValue(b.sons[j].sons[0]))
var y = int(getOrdValue(b.sons[j].sons[1])) var y = int(getOrdValue(b.sons[j].sons[1]))
while x <= y: while x <= y:
appf(m.s[cfsTypeInit3], "$1[$2] = &$3;$n", [tmp, toRope(x), tmp2]) addf(m.s[cfsTypeInit3], "$1[$2] = &$3;$n", [tmp, rope(x), tmp2])
inc(x) inc(x)
else: else:
appf(m.s[cfsTypeInit3], "$1[$2] = &$3;$n", addf(m.s[cfsTypeInit3], "$1[$2] = &$3;$n",
[tmp, toRope(getOrdValue(b.sons[j])), tmp2]) [tmp, rope(getOrdValue(b.sons[j])), tmp2])
of nkElse: of nkElse:
appf(m.s[cfsTypeInit3], "$1[$2] = &$3;$n", addf(m.s[cfsTypeInit3], "$1[$2] = &$3;$n",
[tmp, toRope(L), tmp2]) [tmp, rope(L), tmp2])
else: internalError(n.info, "genObjectFields(nkRecCase)") else: internalError(n.info, "genObjectFields(nkRecCase)")
of nkSym: of nkSym:
var field = n.sym var field = n.sym
appf(m.s[cfsTypeInit3], "$1.kind = 1;$n" & addf(m.s[cfsTypeInit3], "$1.kind = 1;$n" &
"$1.offset = offsetof($2, $3);$n" & "$1.typ = $4;$n" & "$1.offset = offsetof($2, $3);$n" & "$1.typ = $4;$n" &
"$1.name = $5;$n", [expr, getTypeDesc(m, typ), "$1.name = $5;$n", [expr, getTypeDesc(m, typ),
field.loc.r, genTypeInfo(m, field.typ), makeCString(field.name.s)]) field.loc.r, genTypeInfo(m, field.typ), makeCString(field.name.s)])
else: internalError(n.info, "genObjectFields") else: internalError(n.info, "genObjectFields")
proc genObjectInfo(m: BModule, typ, origType: PType, name: PRope) = proc genObjectInfo(m: BModule, typ, origType: PType, name: Rope) =
if typ.kind == tyObject: genTypeInfoAux(m, typ, origType, name) if typ.kind == tyObject: genTypeInfoAux(m, typ, origType, name)
else: genTypeInfoAuxBase(m, typ, origType, name, toRope("0")) else: genTypeInfoAuxBase(m, typ, origType, name, rope("0"))
var tmp = getNimNode(m) var tmp = getNimNode(m)
if not isImportedCppType(typ): if not isImportedCppType(typ):
genObjectFields(m, typ, typ.n, tmp) genObjectFields(m, typ, typ.n, tmp)
appf(m.s[cfsTypeInit3], "$1.node = &$2;$n", [name, tmp]) addf(m.s[cfsTypeInit3], "$1.node = &$2;$n", [name, tmp])
var t = typ.sons[0] var t = typ.sons[0]
while t != nil: while t != nil:
t = t.skipTypes(abstractInst) t = t.skipTypes(abstractInst)
t.flags.incl tfObjHasKids t.flags.incl tfObjHasKids
t = t.sons[0] t = t.sons[0]
proc genTupleInfo(m: BModule, typ: PType, name: PRope) = proc genTupleInfo(m: BModule, typ: PType, name: Rope) =
genTypeInfoAuxBase(m, typ, typ, name, toRope("0")) genTypeInfoAuxBase(m, typ, typ, name, rope("0"))
var expr = getNimNode(m) var expr = getNimNode(m)
var length = sonsLen(typ) var length = sonsLen(typ)
if length > 0: if length > 0:
var tmp = getTempName() var tmp = getTempName()
appf(m.s[cfsTypeInit1], "static TNimNode* $1[$2];$n", [tmp, toRope(length)]) addf(m.s[cfsTypeInit1], "static TNimNode* $1[$2];$n", [tmp, rope(length)])
for i in countup(0, length - 1): for i in countup(0, length - 1):
var a = typ.sons[i] var a = typ.sons[i]
var tmp2 = getNimNode(m) var tmp2 = getNimNode(m)
appf(m.s[cfsTypeInit3], "$1[$2] = &$3;$n", [tmp, toRope(i), tmp2]) addf(m.s[cfsTypeInit3], "$1[$2] = &$3;$n", [tmp, rope(i), tmp2])
appf(m.s[cfsTypeInit3], "$1.kind = 1;$n" & addf(m.s[cfsTypeInit3], "$1.kind = 1;$n" &
"$1.offset = offsetof($2, Field$3);$n" & "$1.offset = offsetof($2, Field$3);$n" &
"$1.typ = $4;$n" & "$1.typ = $4;$n" &
"$1.name = \"Field$3\";$n", "$1.name = \"Field$3\";$n",
[tmp2, getTypeDesc(m, typ), toRope(i), genTypeInfo(m, a)]) [tmp2, getTypeDesc(m, typ), rope(i), genTypeInfo(m, a)])
appf(m.s[cfsTypeInit3], "$1.len = $2; $1.kind = 2; $1.sons = &$3[0];$n", addf(m.s[cfsTypeInit3], "$1.len = $2; $1.kind = 2; $1.sons = &$3[0];$n",
[expr, toRope(length), tmp]) [expr, rope(length), tmp])
else: else:
appf(m.s[cfsTypeInit3], "$1.len = $2; $1.kind = 2;$n", addf(m.s[cfsTypeInit3], "$1.len = $2; $1.kind = 2;$n",
[expr, toRope(length)]) [expr, rope(length)])
appf(m.s[cfsTypeInit3], "$1.node = &$2;$n", [name, expr]) addf(m.s[cfsTypeInit3], "$1.node = &$2;$n", [name, expr])
proc genEnumInfo(m: BModule, typ: PType, name: PRope) = proc genEnumInfo(m: BModule, typ: PType, name: Rope) =
# Type information for enumerations is quite heavy, so we do some # Type information for enumerations is quite heavy, so we do some
# optimizations here: The ``typ`` field is never set, as it is redundant # optimizations here: The ``typ`` field is never set, as it is redundant
# anyway. We generate a cstring array and a loop over it. Exceptional # anyway. We generate a cstring array and a loop over it. Exceptional
@ -863,9 +912,9 @@ proc genEnumInfo(m: BModule, typ: PType, name: PRope) =
genTypeInfoAux(m, typ, typ, name) genTypeInfoAux(m, typ, typ, name)
var nodePtrs = getTempName() var nodePtrs = getTempName()
var length = sonsLen(typ.n) var length = sonsLen(typ.n)
appf(m.s[cfsTypeInit1], "static TNimNode* $1[$2];$n", addf(m.s[cfsTypeInit1], "static TNimNode* $1[$2];$n",
[nodePtrs, toRope(length)]) [nodePtrs, rope(length)])
var enumNames, specialCases: PRope var enumNames, specialCases: Rope
var firstNimNode = m.typeNodes var firstNimNode = m.typeNodes
var hasHoles = false var hasHoles = false
for i in countup(0, length - 1): for i in countup(0, length - 1):
@ -874,38 +923,38 @@ proc genEnumInfo(m: BModule, typ: PType, name: PRope) =
var elemNode = getNimNode(m) var elemNode = getNimNode(m)
if field.ast == nil: if field.ast == nil:
# no explicit string literal for the enum field, so use field.name: # no explicit string literal for the enum field, so use field.name:
app(enumNames, makeCString(field.name.s)) add(enumNames, makeCString(field.name.s))
else: else:
app(enumNames, makeCString(field.ast.strVal)) add(enumNames, makeCString(field.ast.strVal))
if i < length - 1: app(enumNames, ", " & tnl) if i < length - 1: add(enumNames, ", " & tnl)
if field.position != i or tfEnumHasHoles in typ.flags: if field.position != i or tfEnumHasHoles in typ.flags:
appf(specialCases, "$1.offset = $2;$n", [elemNode, toRope(field.position)]) addf(specialCases, "$1.offset = $2;$n", [elemNode, rope(field.position)])
hasHoles = true hasHoles = true
var enumArray = getTempName() var enumArray = getTempName()
var counter = getTempName() var counter = getTempName()
appf(m.s[cfsTypeInit1], "NI $1;$n", [counter]) addf(m.s[cfsTypeInit1], "NI $1;$n", [counter])
appf(m.s[cfsTypeInit1], "static char* NIM_CONST $1[$2] = {$n$3};$n", addf(m.s[cfsTypeInit1], "static char* NIM_CONST $1[$2] = {$n$3};$n",
[enumArray, toRope(length), enumNames]) [enumArray, rope(length), enumNames])
appf(m.s[cfsTypeInit3], "for ($1 = 0; $1 < $2; $1++) {$n" & addf(m.s[cfsTypeInit3], "for ($1 = 0; $1 < $2; $1++) {$n" &
"$3[$1+$4].kind = 1;$n" & "$3[$1+$4].offset = $1;$n" & "$3[$1+$4].kind = 1;$n" & "$3[$1+$4].offset = $1;$n" &
"$3[$1+$4].name = $5[$1];$n" & "$6[$1] = &$3[$1+$4];$n" & "}$n", [counter, "$3[$1+$4].name = $5[$1];$n" & "$6[$1] = &$3[$1+$4];$n" & "}$n", [counter,
toRope(length), m.typeNodesName, toRope(firstNimNode), enumArray, nodePtrs]) rope(length), m.typeNodesName, rope(firstNimNode), enumArray, nodePtrs])
app(m.s[cfsTypeInit3], specialCases) add(m.s[cfsTypeInit3], specialCases)
appf(m.s[cfsTypeInit3], addf(m.s[cfsTypeInit3],
"$1.len = $2; $1.kind = 2; $1.sons = &$3[0];$n$4.node = &$1;$n", "$1.len = $2; $1.kind = 2; $1.sons = &$3[0];$n$4.node = &$1;$n",
[getNimNode(m), toRope(length), nodePtrs, name]) [getNimNode(m), rope(length), nodePtrs, name])
if hasHoles: if hasHoles:
# 1 << 2 is {ntfEnumHole} # 1 << 2 is {ntfEnumHole}
appf(m.s[cfsTypeInit3], "$1.flags = 1<<2;$n", [name]) addf(m.s[cfsTypeInit3], "$1.flags = 1<<2;$n", [name])
proc genSetInfo(m: BModule, typ: PType, name: PRope) = proc genSetInfo(m: BModule, typ: PType, name: Rope) =
assert(typ.sons[0] != nil) assert(typ.sons[0] != nil)
genTypeInfoAux(m, typ, typ, name) genTypeInfoAux(m, typ, typ, name)
var tmp = getNimNode(m) var tmp = getNimNode(m)
appf(m.s[cfsTypeInit3], "$1.len = $2; $1.kind = 0;$n" & "$3.node = &$1;$n", addf(m.s[cfsTypeInit3], "$1.len = $2; $1.kind = 0;$n" & "$3.node = &$1;$n",
[tmp, toRope(firstOrd(typ)), name]) [tmp, rope(firstOrd(typ)), name])
proc genArrayInfo(m: BModule, typ: PType, name: PRope) = proc genArrayInfo(m: BModule, typ: PType, name: Rope) =
genTypeInfoAuxBase(m, typ, typ, name, genTypeInfo(m, typ.sons[1])) genTypeInfoAuxBase(m, typ, typ, name, genTypeInfo(m, typ.sons[1]))
proc fakeClosureType(owner: PSym): PType = proc fakeClosureType(owner: PSym): PType =
@ -925,17 +974,17 @@ type
include ccgtrav include ccgtrav
proc genDeepCopyProc(m: BModule; s: PSym; result: PRope) = proc genDeepCopyProc(m: BModule; s: PSym; result: Rope) =
genProc(m, s) genProc(m, s)
appf(m.s[cfsTypeInit3], "$1.deepcopy =(void* (N_RAW_NIMCALL*)(void*))$2;$n", addf(m.s[cfsTypeInit3], "$1.deepcopy =(void* (N_RAW_NIMCALL*)(void*))$2;$n",
[result, s.loc.r]) [result, s.loc.r])
proc genTypeInfo(m: BModule, t: PType): PRope = proc genTypeInfo(m: BModule, t: PType): Rope =
let origType = t let origType = t
var t = getUniqueType(t) var t = getUniqueType(t)
result = ropef("NTI$1", [toRope(t.id)]) result = "NTI$1" % [rope(t.id)]
if containsOrIncl(m.typeInfoMarker, t.id): if containsOrIncl(m.typeInfoMarker, t.id):
return con("(&".toRope, result, ")".toRope) return "(&".rope & result & ")".rope
# getUniqueType doesn't skip tyDistinct when that has an overriden operation: # getUniqueType doesn't skip tyDistinct when that has an overriden operation:
while t.kind == tyDistinct: t = t.lastSon while t.kind == tyDistinct: t = t.lastSon
@ -946,23 +995,23 @@ proc genTypeInfo(m: BModule, t: PType): PRope =
# reference the type info as extern here # reference the type info as extern here
discard cgsym(m, "TNimType") discard cgsym(m, "TNimType")
discard cgsym(m, "TNimNode") discard cgsym(m, "TNimNode")
appf(m.s[cfsVars], "extern TNimType $1; /* $2 */$n", addf(m.s[cfsVars], "extern TNimType $1; /* $2 */$n",
[result, toRope(typeToString(t))]) [result, rope(typeToString(t))])
return con("(&".toRope, result, ")".toRope) return "(&".rope & result & ")".rope
case t.kind case t.kind
of tyEmpty: result = toRope"0" of tyEmpty: result = rope"0"
of tyPointer, tyBool, tyChar, tyCString, tyString, tyInt..tyUInt64, tyVar: of tyPointer, tyBool, tyChar, tyCString, tyString, tyInt..tyUInt64, tyVar:
genTypeInfoAuxBase(m, t, t, result, toRope"0") genTypeInfoAuxBase(m, t, t, result, rope"0")
of tyProc: of tyProc:
if t.callConv != ccClosure: if t.callConv != ccClosure:
genTypeInfoAuxBase(m, t, t, result, toRope"0") genTypeInfoAuxBase(m, t, t, result, rope"0")
else: else:
genTupleInfo(m, fakeClosureType(t.owner), result) genTupleInfo(m, fakeClosureType(t.owner), result)
of tySequence, tyRef: of tySequence, tyRef:
genTypeInfoAux(m, t, t, result) genTypeInfoAux(m, t, t, result)
if gSelectedGC >= gcMarkAndSweep: if gSelectedGC >= gcMarkAndSweep:
let markerProc = genTraverseProc(m, t, tiNew) let markerProc = genTraverseProc(m, t, tiNew)
appf(m.s[cfsTypeInit3], "$1.marker = $2;$n", [result, markerProc]) addf(m.s[cfsTypeInit3], "$1.marker = $2;$n", [result, markerProc])
of tyPtr, tyRange: genTypeInfoAux(m, t, t, result) of tyPtr, tyRange: genTypeInfoAux(m, t, t, result)
of tyArrayConstr, tyArray: genArrayInfo(m, t, result) of tyArrayConstr, tyArray: genArrayInfo(m, t, result)
of tySet: genSetInfo(m, t, result) of tySet: genSetInfo(m, t, result)
@ -979,7 +1028,7 @@ proc genTypeInfo(m: BModule, t: PType): PRope =
genDeepCopyProc(m, t.deepCopy, result) genDeepCopyProc(m, t.deepCopy, result)
elif origType.deepCopy != nil: elif origType.deepCopy != nil:
genDeepCopyProc(m, origType.deepCopy, result) genDeepCopyProc(m, origType.deepCopy, result)
result = con("(&".toRope, result, ")".toRope) result = "(&".rope & result & ")".rope
proc genTypeSection(m: BModule, n: PNode) = proc genTypeSection(m: BModule, n: PNode) =
discard discard

View file

@ -176,7 +176,7 @@ proc mangle*(name: string): string =
result = newStringOfCap(name.len) result = newStringOfCap(name.len)
case name[0] case name[0]
of Letters: of Letters:
result.add(name[0].toLower) result.add(name[0])
of Digits: of Digits:
result.add("N" & name[0]) result.add("N" & name[0])
else: else:
@ -193,13 +193,13 @@ proc mangle*(name: string): string =
else: else:
add(result, "HEX" & toHex(ord(c), 2)) add(result, "HEX" & toHex(ord(c), 2))
proc makeLLVMString*(s: string): PRope = proc makeLLVMString*(s: string): Rope =
const MaxLineLength = 64 const MaxLineLength = 64
result = nil result = nil
var res = "c\"" var res = "c\""
for i in countup(0, len(s) - 1): for i in countup(0, len(s) - 1):
if (i + 1) mod MaxLineLength == 0: if (i + 1) mod MaxLineLength == 0:
app(result, toRope(res)) add(result, rope(res))
setLen(res, 0) setLen(res, 0)
case s[i] case s[i]
of '\0'..'\x1F', '\x80'..'\xFF', '\"', '\\': of '\0'..'\x1F', '\x80'..'\xFF', '\"', '\\':
@ -207,6 +207,6 @@ proc makeLLVMString*(s: string): PRope =
add(res, toHex(ord(s[i]), 2)) add(res, toHex(ord(s[i]), 2))
else: add(res, s[i]) else: add(res, s[i])
add(res, "\\00\"") add(res, "\\00\"")
app(result, toRope(res)) add(result, rope(res))
initTypeTables() initTypeTables()

View file

@ -10,13 +10,15 @@
## This module implements the C code generator. ## This module implements the C code generator.
import import
ast, astalgo, strutils, hashes, trees, platform, magicsys, extccomp, ast, astalgo, hashes, trees, platform, magicsys, extccomp,
options, intsets, options, intsets,
nversion, nimsets, msgs, crc, bitsets, idents, lists, types, ccgutils, os, nversion, nimsets, msgs, crc, bitsets, idents, lists, types, ccgutils, os,
ropes, math, passes, rodread, wordrecg, treetab, cgmeth, condsyms, ropes, math, passes, rodread, wordrecg, treetab, cgmeth, condsyms,
rodutils, renderer, idgen, cgendata, ccgmerge, semfold, aliases, lowerings, rodutils, renderer, idgen, cgendata, ccgmerge, semfold, aliases, lowerings,
semparallel semparallel
import strutils except `%` # collides with ropes.`%`
when options.hasTinyCBackend: when options.hasTinyCBackend:
import tccgen import tccgen
@ -48,7 +50,7 @@ proc initLoc(result: var TLoc, k: TLocKind, typ: PType, s: TStorageLoc) =
result.r = nil result.r = nil
result.flags = {} result.flags = {}
proc fillLoc(a: var TLoc, k: TLocKind, typ: PType, r: PRope, s: TStorageLoc) = proc fillLoc(a: var TLoc, k: TLocKind, typ: PType, r: Rope, s: TStorageLoc) =
# fills the loc if it is not already initialized # fills the loc if it is not already initialized
if a.k == locNone: if a.k == locNone:
a.k = k a.k = k
@ -72,9 +74,9 @@ proc useHeader(m: BModule, sym: PSym) =
assert(sym.annex != nil) assert(sym.annex != nil)
discard lists.includeStr(m.headerFiles, getStr(sym.annex.path)) discard lists.includeStr(m.headerFiles, getStr(sym.annex.path))
proc cgsym(m: BModule, name: string): PRope proc cgsym(m: BModule, name: string): Rope
proc ropecg(m: BModule, frmt: TFormatStr, args: varargs[PRope]): PRope = proc ropecg(m: BModule, frmt: FormatStr, args: varargs[Rope]): Rope =
var i = 0 var i = 0
var length = len(frmt) var length = len(frmt)
result = nil result = nil
@ -84,11 +86,11 @@ proc ropecg(m: BModule, frmt: TFormatStr, args: varargs[PRope]): PRope =
inc(i) # skip '$' inc(i) # skip '$'
case frmt[i] case frmt[i]
of '$': of '$':
app(result, "$") add(result, "$")
inc(i) inc(i)
of '#': of '#':
inc(i) inc(i)
app(result, args[num]) add(result, args[num])
inc(num) inc(num)
of '0'..'9': of '0'..'9':
var j = 0 var j = 0
@ -99,12 +101,12 @@ proc ropecg(m: BModule, frmt: TFormatStr, args: varargs[PRope]): PRope =
num = j num = j
if j > high(args) + 1: if j > high(args) + 1:
internalError("ropes: invalid format string $" & $j) internalError("ropes: invalid format string $" & $j)
app(result, args[j-1]) add(result, args[j-1])
of 'n': of 'n':
if optLineDir notin gOptions: app(result, rnl) if optLineDir notin gOptions: add(result, rnl)
inc(i) inc(i)
of 'N': of 'N':
app(result, rnl) add(result, rnl)
inc(i) inc(i)
else: internalError("ropes: invalid format string $" & frmt[i]) else: internalError("ropes: invalid format string $" & frmt[i])
elif frmt[i] == '#' and frmt[i+1] in IdentStartChars: elif frmt[i] == '#' and frmt[i+1] in IdentStartChars:
@ -113,74 +115,74 @@ proc ropecg(m: BModule, frmt: TFormatStr, args: varargs[PRope]): PRope =
while frmt[j] in IdentChars: inc(j) while frmt[j] in IdentChars: inc(j)
var ident = substr(frmt, i, j-1) var ident = substr(frmt, i, j-1)
i = j i = j
app(result, cgsym(m, ident)) add(result, cgsym(m, ident))
elif frmt[i] == '#' and frmt[i+1] == '$': elif frmt[i] == '#' and frmt[i+1] == '$':
inc(i, 2) inc(i, 2)
var j = 0 var j = 0
while frmt[i] in Digits: while frmt[i] in Digits:
j = (j * 10) + ord(frmt[i]) - ord('0') j = (j * 10) + ord(frmt[i]) - ord('0')
inc(i) inc(i)
app(result, cgsym(m, args[j-1].ropeToStr)) add(result, cgsym(m, $args[j-1]))
var start = i var start = i
while i < length: while i < length:
if frmt[i] != '$' and frmt[i] != '#': inc(i) if frmt[i] != '$' and frmt[i] != '#': inc(i)
else: break else: break
if i - 1 >= start: if i - 1 >= start:
app(result, substr(frmt, start, i - 1)) add(result, substr(frmt, start, i - 1))
template rfmt(m: BModule, fmt: string, args: varargs[PRope]): expr = template rfmt(m: BModule, fmt: string, args: varargs[Rope]): expr =
ropecg(m, fmt, args) ropecg(m, fmt, args)
proc appcg(m: BModule, c: var PRope, frmt: TFormatStr, proc appcg(m: BModule, c: var Rope, frmt: FormatStr,
args: varargs[PRope]) = args: varargs[Rope]) =
app(c, ropecg(m, frmt, args)) add(c, ropecg(m, frmt, args))
proc appcg(m: BModule, s: TCFileSection, frmt: TFormatStr, proc appcg(m: BModule, s: TCFileSection, frmt: FormatStr,
args: varargs[PRope]) = args: varargs[Rope]) =
app(m.s[s], ropecg(m, frmt, args)) add(m.s[s], ropecg(m, frmt, args))
proc appcg(p: BProc, s: TCProcSection, frmt: TFormatStr, proc appcg(p: BProc, s: TCProcSection, frmt: FormatStr,
args: varargs[PRope]) = args: varargs[Rope]) =
app(p.s(s), ropecg(p.module, frmt, args)) add(p.s(s), ropecg(p.module, frmt, args))
var indent = "\t".toRope var indent = "\t".rope
proc indentLine(p: BProc, r: PRope): PRope = proc indentLine(p: BProc, r: Rope): Rope =
result = r result = r
for i in countup(0, p.blocks.len-1): prepend(result, indent) for i in countup(0, p.blocks.len-1): prepend(result, indent)
proc line(p: BProc, s: TCProcSection, r: PRope) = proc line(p: BProc, s: TCProcSection, r: Rope) =
app(p.s(s), indentLine(p, r)) add(p.s(s), indentLine(p, r))
proc line(p: BProc, s: TCProcSection, r: string) = proc line(p: BProc, s: TCProcSection, r: string) =
app(p.s(s), indentLine(p, r.toRope)) add(p.s(s), indentLine(p, r.rope))
proc lineF(p: BProc, s: TCProcSection, frmt: TFormatStr, proc lineF(p: BProc, s: TCProcSection, frmt: FormatStr,
args: varargs[PRope]) = args: openarray[Rope]) =
app(p.s(s), indentLine(p, ropef(frmt, args))) add(p.s(s), indentLine(p, frmt % args))
proc lineCg(p: BProc, s: TCProcSection, frmt: TFormatStr, proc lineCg(p: BProc, s: TCProcSection, frmt: FormatStr,
args: varargs[PRope]) = args: varargs[Rope]) =
app(p.s(s), indentLine(p, ropecg(p.module, frmt, args))) add(p.s(s), indentLine(p, ropecg(p.module, frmt, args)))
proc linefmt(p: BProc, s: TCProcSection, frmt: TFormatStr, proc linefmt(p: BProc, s: TCProcSection, frmt: FormatStr,
args: varargs[PRope]) = args: varargs[Rope]) =
app(p.s(s), indentLine(p, ropecg(p.module, frmt, args))) add(p.s(s), indentLine(p, ropecg(p.module, frmt, args)))
proc appLineCg(p: BProc, r: var PRope, frmt: TFormatStr, proc appLineCg(p: BProc, r: var Rope, frmt: FormatStr,
args: varargs[PRope]) = args: varargs[Rope]) =
app(r, indentLine(p, ropecg(p.module, frmt, args))) add(r, indentLine(p, ropecg(p.module, frmt, args)))
proc safeLineNm(info: TLineInfo): int = proc safeLineNm(info: TLineInfo): int =
result = toLinenumber(info) result = toLinenumber(info)
if result < 0: result = 0 # negative numbers are not allowed in #line if result < 0: result = 0 # negative numbers are not allowed in #line
proc genCLineDir(r: var PRope, filename: string, line: int) = proc genCLineDir(r: var Rope, filename: string, line: int) =
assert line >= 0 assert line >= 0
if optLineDir in gOptions: if optLineDir in gOptions:
appf(r, "$N#line $2 $1$N", addf(r, "$N#line $2 $1$N",
[toRope(makeSingleLineCString(filename)), toRope(line)]) [rope(makeSingleLineCString(filename)), rope(line)])
proc genCLineDir(r: var PRope, info: TLineInfo) = proc genCLineDir(r: var Rope, info: TLineInfo) =
genCLineDir(r, info.toFullPath, info.safeLineNm) genCLineDir(r, info.toFullPath, info.safeLineNm)
proc freshLineInfo(p: BProc; info: TLineInfo): bool = proc freshLineInfo(p: BProc; info: TLineInfo): bool =
@ -193,22 +195,22 @@ proc freshLineInfo(p: BProc; info: TLineInfo): bool =
proc genLineDir(p: BProc, t: PNode) = proc genLineDir(p: BProc, t: PNode) =
var line = t.info.safeLineNm var line = t.info.safeLineNm
if optEmbedOrigSrc in gGlobalOptions: if optEmbedOrigSrc in gGlobalOptions:
app(p.s(cpsStmts), con(~"//", t.info.sourceLine, rnl)) add(p.s(cpsStmts), ~"//" & t.info.sourceLine & rnl)
genCLineDir(p.s(cpsStmts), t.info.toFullPath, line) genCLineDir(p.s(cpsStmts), t.info.toFullPath, line)
if ({optStackTrace, optEndb} * p.options == {optStackTrace, optEndb}) and if ({optStackTrace, optEndb} * p.options == {optStackTrace, optEndb}) and
(p.prc == nil or sfPure notin p.prc.flags): (p.prc == nil or sfPure notin p.prc.flags):
if freshLineInfo(p, t.info): if freshLineInfo(p, t.info):
linefmt(p, cpsStmts, "#endb($1, $2);$n", linefmt(p, cpsStmts, "#endb($1, $2);$n",
line.toRope, makeCString(toFilename(t.info))) line.rope, makeCString(toFilename(t.info)))
elif ({optLineTrace, optStackTrace} * p.options == elif ({optLineTrace, optStackTrace} * p.options ==
{optLineTrace, optStackTrace}) and {optLineTrace, optStackTrace}) and
(p.prc == nil or sfPure notin p.prc.flags) and t.info.fileIndex >= 0: (p.prc == nil or sfPure notin p.prc.flags) and t.info.fileIndex >= 0:
if freshLineInfo(p, t.info): if freshLineInfo(p, t.info):
linefmt(p, cpsStmts, "nimln($1, $2);$n", linefmt(p, cpsStmts, "nimln($1, $2);$n",
line.toRope, t.info.quotedFilename) line.rope, t.info.quotedFilename)
proc postStmtActions(p: BProc) {.inline.} = proc postStmtActions(p: BProc) {.inline.} =
app(p.s(cpsStmts), p.module.injectStmt) add(p.s(cpsStmts), p.module.injectStmt)
proc accessThreadLocalVar(p: BProc, s: PSym) proc accessThreadLocalVar(p: BProc, s: PSym)
proc emulatedThreadVars(): bool {.inline.} proc emulatedThreadVars(): bool {.inline.}
@ -221,21 +223,21 @@ include "ccgtypes.nim"
# ------------------------------ Manager of temporaries ------------------ # ------------------------------ Manager of temporaries ------------------
proc rdLoc(a: TLoc): PRope = proc rdLoc(a: TLoc): Rope =
# 'read' location (deref if indirect) # 'read' location (deref if indirect)
result = a.r result = a.r
if lfIndirect in a.flags: result = ropef("(*$1)", [result]) if lfIndirect in a.flags: result = "(*$1)" % [result]
proc addrLoc(a: TLoc): PRope = proc addrLoc(a: TLoc): Rope =
result = a.r result = a.r
if lfIndirect notin a.flags and mapType(a.t) != ctArray: if lfIndirect notin a.flags and mapType(a.t) != ctArray:
result = con("(&", result).con(")") result = "(&" & result & ")"
proc rdCharLoc(a: TLoc): PRope = proc rdCharLoc(a: TLoc): Rope =
# read a location that may need a char-cast: # read a location that may need a char-cast:
result = rdLoc(a) result = rdLoc(a)
if skipTypes(a.t, abstractRange).kind == tyChar: if skipTypes(a.t, abstractRange).kind == tyChar:
result = ropef("((NU8)($1))", [result]) result = "((NU8)($1))" % [result]
proc genObjectInit(p: BProc, section: TCProcSection, t: PType, a: TLoc, proc genObjectInit(p: BProc, section: TCProcSection, t: PType, a: TLoc,
takeAddr: bool) = takeAddr: bool) =
@ -244,11 +246,11 @@ proc genObjectInit(p: BProc, section: TCProcSection, t: PType, a: TLoc,
discard discard
of frHeader: of frHeader:
var r = rdLoc(a) var r = rdLoc(a)
if not takeAddr: r = ropef("(*$1)", [r]) if not takeAddr: r = "(*$1)" % [r]
var s = skipTypes(t, abstractInst) var s = skipTypes(t, abstractInst)
if not p.module.compileToCpp: if not p.module.compileToCpp:
while (s.kind == tyObject) and (s.sons[0] != nil): while (s.kind == tyObject) and (s.sons[0] != nil):
app(r, ".Sup") add(r, ".Sup")
s = skipTypes(s.sons[0], abstractInst) s = skipTypes(s.sons[0], abstractInst)
linefmt(p, section, "$1.m_type = $2;$n", r, genTypeInfo(p.module, t)) linefmt(p, section, "$1.m_type = $2;$n", r, genTypeInfo(p.module, t))
of frEmbedded: of frEmbedded:
@ -276,7 +278,7 @@ proc resetLoc(p: BProc, loc: var TLoc) =
if containsGcRef: if containsGcRef:
var nilLoc: TLoc var nilLoc: TLoc
initLoc(nilLoc, locTemp, loc.t, OnStack) initLoc(nilLoc, locTemp, loc.t, OnStack)
nilLoc.r = toRope("NIM_NIL") nilLoc.r = rope("NIM_NIL")
genRefAssign(p, loc, nilLoc, {afSrcIsNil}) genRefAssign(p, loc, nilLoc, {afSrcIsNil})
else: else:
linefmt(p, cpsStmts, "$1 = 0;$n", rdLoc(loc)) linefmt(p, cpsStmts, "$1 = 0;$n", rdLoc(loc))
@ -325,7 +327,7 @@ proc initLocalVar(p: BProc, v: PSym, immediateAsgn: bool) =
proc getTemp(p: BProc, t: PType, result: var TLoc; needsInit=false) = proc getTemp(p: BProc, t: PType, result: var TLoc; needsInit=false) =
inc(p.labels) inc(p.labels)
result.r = con("LOC", toRope(p.labels)) result.r = "LOC" & rope(p.labels)
linefmt(p, cpsLocals, "$1 $2;$n", getTypeDesc(p.module, t), result.r) linefmt(p, cpsLocals, "$1 $2;$n", getTypeDesc(p.module, t), result.r)
result.k = locTemp result.k = locTemp
#result.a = - 1 #result.a = - 1
@ -340,9 +342,9 @@ proc keepAlive(p: BProc, toKeepAlive: TLoc) =
# of interior pointers instead # of interior pointers instead
if optRefcGC notin gGlobalOptions: return if optRefcGC notin gGlobalOptions: return
var result: TLoc var result: TLoc
var fid = toRope(p.gcFrameId) var fid = rope(p.gcFrameId)
result.r = con("GCFRAME.F", fid) result.r = "GCFRAME.F" & fid
appf(p.gcFrameType, " $1 F$2;$n", addf(p.gcFrameType, " $1 F$2;$n",
[getTypeDesc(p.module, toKeepAlive.t), fid]) [getTypeDesc(p.module, toKeepAlive.t), fid])
inc(p.gcFrameId) inc(p.gcFrameId)
result.k = locTemp result.k = locTemp
@ -359,10 +361,10 @@ proc keepAlive(p: BProc, toKeepAlive: TLoc) =
"memcpy((void*)$1, (NIM_CONST void*)$2, sizeof($3));$n", "memcpy((void*)$1, (NIM_CONST void*)$2, sizeof($3));$n",
addrLoc(result), addrLoc(toKeepAlive), rdLoc(result)) addrLoc(result), addrLoc(toKeepAlive), rdLoc(result))
proc initGCFrame(p: BProc): PRope = proc initGCFrame(p: BProc): Rope =
if p.gcFrameId > 0: result = ropef("struct {$1} GCFRAME;$n", p.gcFrameType) if p.gcFrameId > 0: result = "struct {$1} GCFRAME;$n" % [p.gcFrameType]
proc deinitGCFrame(p: BProc): PRope = proc deinitGCFrame(p: BProc): Rope =
if p.gcFrameId > 0: if p.gcFrameId > 0:
result = ropecg(p.module, result = ropecg(p.module,
"if (((NU)&GCFRAME) < 4096) #nimGCFrame(&GCFRAME);$n") "if (((NU)&GCFRAME) < 4096) #nimGCFrame(&GCFRAME);$n")
@ -371,42 +373,42 @@ proc localDebugInfo(p: BProc, s: PSym) =
if {optStackTrace, optEndb} * p.options != {optStackTrace, optEndb}: return if {optStackTrace, optEndb} * p.options != {optStackTrace, optEndb}: return
# XXX work around a bug: No type information for open arrays possible: # XXX work around a bug: No type information for open arrays possible:
if skipTypes(s.typ, abstractVar).kind in {tyOpenArray, tyVarargs}: return if skipTypes(s.typ, abstractVar).kind in {tyOpenArray, tyVarargs}: return
var a = con("&", s.loc.r) var a = "&" & s.loc.r
if s.kind == skParam and ccgIntroducedPtr(s): a = s.loc.r if s.kind == skParam and ccgIntroducedPtr(s): a = s.loc.r
lineF(p, cpsInit, lineF(p, cpsInit,
"F.s[$1].address = (void*)$3; F.s[$1].typ = $4; F.s[$1].name = $2;$n", "F.s[$1].address = (void*)$3; F.s[$1].typ = $4; F.s[$1].name = $2;$n",
[p.maxFrameLen.toRope, makeCString(normalize(s.name.s)), a, [p.maxFrameLen.rope, makeCString(normalize(s.name.s)), a,
genTypeInfo(p.module, s.loc.t)]) genTypeInfo(p.module, s.loc.t)])
inc(p.maxFrameLen) inc(p.maxFrameLen)
inc p.blocks[p.blocks.len-1].frameLen inc p.blocks[p.blocks.len-1].frameLen
proc localVarDecl(p: BProc; s: PSym): PRope = proc localVarDecl(p: BProc; s: PSym): Rope =
if s.loc.k == locNone: if s.loc.k == locNone:
fillLoc(s.loc, locLocalVar, s.typ, mangleName(s), OnStack) fillLoc(s.loc, locLocalVar, s.typ, mangleName(s), OnStack)
if s.kind == skLet: incl(s.loc.flags, lfNoDeepCopy) if s.kind == skLet: incl(s.loc.flags, lfNoDeepCopy)
result = getTypeDesc(p.module, s.loc.t) result = getTypeDesc(p.module, s.loc.t)
if s.constraint.isNil: if s.constraint.isNil:
if sfRegister in s.flags: app(result, " register") if sfRegister in s.flags: add(result, " register")
#elif skipTypes(s.typ, abstractInst).kind in GcTypeKinds: #elif skipTypes(s.typ, abstractInst).kind in GcTypeKinds:
# app(decl, " GC_GUARD") # add(decl, " GC_GUARD")
if sfVolatile in s.flags: app(result, " volatile") if sfVolatile in s.flags: add(result, " volatile")
app(result, " ") add(result, " ")
app(result, s.loc.r) add(result, s.loc.r)
else: else:
result = ropef(s.cgDeclFrmt, result, s.loc.r) result = s.cgDeclFrmt % [result, s.loc.r]
proc assignLocalVar(p: BProc, s: PSym) = proc assignLocalVar(p: BProc, s: PSym) =
#assert(s.loc.k == locNone) # not yet assigned #assert(s.loc.k == locNone) # not yet assigned
# this need not be fulfilled for inline procs; they are regenerated # this need not be fulfilled for inline procs; they are regenerated
# for each module that uses them! # for each module that uses them!
let decl = localVarDecl(p, s).con(";" & tnl) let decl = localVarDecl(p, s) & ";" & tnl
line(p, cpsLocals, decl) line(p, cpsLocals, decl)
localDebugInfo(p, s) localDebugInfo(p, s)
include ccgthreadvars include ccgthreadvars
proc varInDynamicLib(m: BModule, sym: PSym) proc varInDynamicLib(m: BModule, sym: PSym)
proc mangleDynLibProc(sym: PSym): PRope proc mangleDynLibProc(sym: PSym): Rope
proc assignGlobalVar(p: BProc, s: PSym) = proc assignGlobalVar(p: BProc, s: PSym) =
if s.loc.k == locNone: if s.loc.k == locNone:
@ -424,17 +426,17 @@ proc assignGlobalVar(p: BProc, s: PSym) =
if sfThread in s.flags: if sfThread in s.flags:
declareThreadVar(p.module, s, sfImportc in s.flags) declareThreadVar(p.module, s, sfImportc in s.flags)
else: else:
var decl: PRope = nil var decl: Rope = nil
var td = getTypeDesc(p.module, s.loc.t) var td = getTypeDesc(p.module, s.loc.t)
if s.constraint.isNil: if s.constraint.isNil:
if sfImportc in s.flags: app(decl, "extern ") if sfImportc in s.flags: add(decl, "extern ")
app(decl, td) add(decl, td)
if sfRegister in s.flags: app(decl, " register") if sfRegister in s.flags: add(decl, " register")
if sfVolatile in s.flags: app(decl, " volatile") if sfVolatile in s.flags: add(decl, " volatile")
appf(decl, " $1;$n", [s.loc.r]) addf(decl, " $1;$n", [s.loc.r])
else: else:
decl = ropef(s.cgDeclFrmt & ";$n", td, s.loc.r) decl = (s.cgDeclFrmt & ";$n") % [td, s.loc.r]
app(p.module.s[cfsVars], decl) add(p.module.s[cfsVars], decl)
if p.withinLoop > 0: if p.withinLoop > 0:
# fixes tests/run/tzeroarray: # fixes tests/run/tzeroarray:
resetLoc(p, s.loc) resetLoc(p, s.loc)
@ -455,7 +457,7 @@ proc fillProcLoc(sym: PSym) =
proc getLabel(p: BProc): TLabel = proc getLabel(p: BProc): TLabel =
inc(p.labels) inc(p.labels)
result = con("LA", toRope(p.labels)) result = "LA" & rope(p.labels)
proc fixLabel(p: BProc, labl: TLabel) = proc fixLabel(p: BProc, labl: TLabel) =
lineF(p, cpsStmts, "$1: ;$n", [labl]) lineF(p, cpsStmts, "$1: ;$n", [labl])
@ -467,9 +469,9 @@ proc expr(p: BProc, n: PNode, d: var TLoc)
proc genProcPrototype(m: BModule, sym: PSym) proc genProcPrototype(m: BModule, sym: PSym)
proc putLocIntoDest(p: BProc, d: var TLoc, s: TLoc) proc putLocIntoDest(p: BProc, d: var TLoc, s: TLoc)
proc genAssignment(p: BProc, dest, src: TLoc, flags: TAssignmentFlags) proc genAssignment(p: BProc, dest, src: TLoc, flags: TAssignmentFlags)
proc intLiteral(i: BiggestInt): PRope proc intLiteral(i: BiggestInt): Rope
proc genLiteral(p: BProc, n: PNode): PRope proc genLiteral(p: BProc, n: PNode): Rope
proc genOtherArg(p: BProc; ri: PNode; i: int; typ: PType): PRope proc genOtherArg(p: BProc; ri: PNode; i: int; typ: PType): Rope
proc initLocExpr(p: BProc, e: PNode, result: var TLoc) = proc initLocExpr(p: BProc, e: PNode, result: var TLoc) =
initLoc(result, locNone, e.typ, OnUnknown) initLoc(result, locNone, e.typ, OnUnknown)
@ -480,13 +482,13 @@ proc initLocExprSingleUse(p: BProc, e: PNode, result: var TLoc) =
result.flags.incl lfSingleUse result.flags.incl lfSingleUse
expr(p, e, result) expr(p, e, result)
proc lenField(p: BProc): PRope = proc lenField(p: BProc): Rope =
result = toRope(if p.module.compileToCpp: "len" else: "Sup.len") result = rope(if p.module.compileToCpp: "len" else: "Sup.len")
include ccgcalls, "ccgstmts.nim", "ccgexprs.nim" include ccgcalls, "ccgstmts.nim", "ccgexprs.nim"
# ----------------------------- dynamic library handling ----------------- # ----------------------------- dynamic library handling -----------------
# We don't finalize dynamic libs as this does the OS for us. # We don't finalize dynamic libs as the OS does this for us.
proc isGetProcAddr(lib: PLib): bool = proc isGetProcAddr(lib: PLib): bool =
let n = lib.path let n = lib.path
@ -500,16 +502,16 @@ proc loadDynamicLib(m: BModule, lib: PLib) =
var tmp = getGlobalTempName() var tmp = getGlobalTempName()
assert(lib.name == nil) assert(lib.name == nil)
lib.name = tmp # BUGFIX: cgsym has awful side-effects lib.name = tmp # BUGFIX: cgsym has awful side-effects
appf(m.s[cfsVars], "static void* $1;$n", [tmp]) addf(m.s[cfsVars], "static void* $1;$n", [tmp])
if lib.path.kind in {nkStrLit..nkTripleStrLit}: if lib.path.kind in {nkStrLit..nkTripleStrLit}:
var s: TStringSeq = @[] var s: TStringSeq = @[]
libCandidates(lib.path.strVal, s) libCandidates(lib.path.strVal, s)
if gVerbosity >= 2: if gVerbosity >= 2:
msgWriteln("Dependency: " & lib.path.strVal) msgWriteln("Dependency: " & lib.path.strVal)
var loadlib: PRope = nil var loadlib: Rope = nil
for i in countup(0, high(s)): for i in countup(0, high(s)):
inc(m.labels) inc(m.labels)
if i > 0: app(loadlib, "||") if i > 0: add(loadlib, "||")
appcg(m, loadlib, "($1 = #nimLoadLibrary((#NimStringDesc*) &$2))$n", appcg(m, loadlib, "($1 = #nimLoadLibrary((#NimStringDesc*) &$2))$n",
[tmp, getStrLit(m, s[i])]) [tmp, getStrLit(m, s[i])])
appcg(m, m.s[cfsDynLibInit], appcg(m, m.s[cfsDynLibInit],
@ -520,21 +522,21 @@ proc loadDynamicLib(m: BModule, lib: PLib) =
p.options = p.options - {optStackTrace, optEndb} p.options = p.options - {optStackTrace, optEndb}
var dest: TLoc var dest: TLoc
initLocExpr(p, lib.path, dest) initLocExpr(p, lib.path, dest)
app(m.s[cfsVars], p.s(cpsLocals)) add(m.s[cfsVars], p.s(cpsLocals))
app(m.s[cfsDynLibInit], p.s(cpsInit)) add(m.s[cfsDynLibInit], p.s(cpsInit))
app(m.s[cfsDynLibInit], p.s(cpsStmts)) add(m.s[cfsDynLibInit], p.s(cpsStmts))
appcg(m, m.s[cfsDynLibInit], appcg(m, m.s[cfsDynLibInit],
"if (!($1 = #nimLoadLibrary($2))) #nimLoadLibraryError($2);$n", "if (!($1 = #nimLoadLibrary($2))) #nimLoadLibraryError($2);$n",
[tmp, rdLoc(dest)]) [tmp, rdLoc(dest)])
if lib.name == nil: internalError("loadDynamicLib") if lib.name == nil: internalError("loadDynamicLib")
proc mangleDynLibProc(sym: PSym): PRope = proc mangleDynLibProc(sym: PSym): Rope =
if sfCompilerProc in sym.flags: if sfCompilerProc in sym.flags:
# NOTE: sym.loc.r is the external name! # NOTE: sym.loc.r is the external name!
result = toRope(sym.name.s) result = rope(sym.name.s)
else: else:
result = ropef("Dl_$1", [toRope(sym.id)]) result = "Dl_$1" % [rope(sym.id)]
proc symInDynamicLib(m: BModule, sym: PSym) = proc symInDynamicLib(m: BModule, sym: PSym) =
var lib = sym.annex var lib = sym.annex
@ -549,30 +551,28 @@ proc symInDynamicLib(m: BModule, sym: PSym) =
let n = lib.path let n = lib.path
var a: TLoc var a: TLoc
initLocExpr(m.initProc, n[0], a) initLocExpr(m.initProc, n[0], a)
var params = con(rdLoc(a), "(") var params = rdLoc(a) & "("
for i in 1 .. n.len-2: for i in 1 .. n.len-2:
initLocExpr(m.initProc, n[i], a) initLocExpr(m.initProc, n[i], a)
params.app(rdLoc(a)) params.add(rdLoc(a))
params.app(", ") params.add(", ")
let load = ropef("\t$1 = ($2) ($3$4));$n", let load = "\t$1 = ($2) ($3$4));$n" %
[tmp, getTypeDesc(m, sym.typ), [tmp, getTypeDesc(m, sym.typ), params, makeCString($extname)]
params, makeCString(ropeToStr(extname))])
var last = lastSon(n) var last = lastSon(n)
if last.kind == nkHiddenStdConv: last = last.sons[1] if last.kind == nkHiddenStdConv: last = last.sons[1]
internalAssert(last.kind == nkStrLit) internalAssert(last.kind == nkStrLit)
let idx = last.strVal let idx = last.strVal
if idx.len == 0: if idx.len == 0:
app(m.initProc.s(cpsStmts), load) add(m.initProc.s(cpsStmts), load)
elif idx.len == 1 and idx[0] in {'0'..'9'}: elif idx.len == 1 and idx[0] in {'0'..'9'}:
app(m.extensionLoaders[idx[0]], load) add(m.extensionLoaders[idx[0]], load)
else: else:
internalError(sym.info, "wrong index: " & idx) internalError(sym.info, "wrong index: " & idx)
else: else:
appcg(m, m.s[cfsDynLibInit], appcg(m, m.s[cfsDynLibInit],
"\t$1 = ($2) #nimGetProcAddr($3, $4);$n", "\t$1 = ($2) #nimGetProcAddr($3, $4);$n",
[tmp, getTypeDesc(m, sym.typ), [tmp, getTypeDesc(m, sym.typ), lib.name, makeCString($extname)])
lib.name, makeCString(ropeToStr(extname))]) addf(m.s[cfsVars], "$2 $1;$n", [sym.loc.r, getTypeDesc(m, sym.loc.t)])
appf(m.s[cfsVars], "$2 $1;$n", [sym.loc.r, getTypeDesc(m, sym.loc.t)])
proc varInDynamicLib(m: BModule, sym: PSym) = proc varInDynamicLib(m: BModule, sym: PSym) =
var lib = sym.annex var lib = sym.annex
@ -584,16 +584,15 @@ proc varInDynamicLib(m: BModule, sym: PSym) =
inc(m.labels, 2) inc(m.labels, 2)
appcg(m, m.s[cfsDynLibInit], appcg(m, m.s[cfsDynLibInit],
"$1 = ($2*) #nimGetProcAddr($3, $4);$n", "$1 = ($2*) #nimGetProcAddr($3, $4);$n",
[tmp, getTypeDesc(m, sym.typ), [tmp, getTypeDesc(m, sym.typ), lib.name, makeCString($extname)])
lib.name, makeCString(ropeToStr(extname))]) addf(m.s[cfsVars], "$2* $1;$n",
appf(m.s[cfsVars], "$2* $1;$n",
[sym.loc.r, getTypeDesc(m, sym.loc.t)]) [sym.loc.r, getTypeDesc(m, sym.loc.t)])
proc symInDynamicLibPartial(m: BModule, sym: PSym) = proc symInDynamicLibPartial(m: BModule, sym: PSym) =
sym.loc.r = mangleDynLibProc(sym) sym.loc.r = mangleDynLibProc(sym)
sym.typ.sym = nil # generate a new name sym.typ.sym = nil # generate a new name
proc cgsym(m: BModule, name: string): PRope = proc cgsym(m: BModule, name: string): Rope =
var sym = magicsys.getCompilerProc(name) var sym = magicsys.getCompilerProc(name)
if sym != nil: if sym != nil:
case sym.kind case sym.kind
@ -609,29 +608,29 @@ proc cgsym(m: BModule, name: string): PRope =
result = sym.loc.r result = sym.loc.r
proc generateHeaders(m: BModule) = proc generateHeaders(m: BModule) =
app(m.s[cfsHeaders], tnl & "#include \"nimbase.h\"" & tnl) add(m.s[cfsHeaders], tnl & "#include \"nimbase.h\"" & tnl)
var it = PStrEntry(m.headerFiles.head) var it = PStrEntry(m.headerFiles.head)
while it != nil: while it != nil:
if it.data[0] notin {'\"', '<'}: if it.data[0] notin {'\"', '<'}:
appf(m.s[cfsHeaders], "$N#include \"$1\"$N", [toRope(it.data)]) addf(m.s[cfsHeaders], "$N#include \"$1\"$N", [rope(it.data)])
else: else:
appf(m.s[cfsHeaders], "$N#include $1$N", [toRope(it.data)]) addf(m.s[cfsHeaders], "$N#include $1$N", [rope(it.data)])
it = PStrEntry(it.next) it = PStrEntry(it.next)
proc retIsNotVoid(s: PSym): bool = proc retIsNotVoid(s: PSym): bool =
result = (s.typ.sons[0] != nil) and not isInvalidReturnType(s.typ.sons[0]) result = (s.typ.sons[0] != nil) and not isInvalidReturnType(s.typ.sons[0])
proc initFrame(p: BProc, procname, filename: PRope): PRope = proc initFrame(p: BProc, procname, filename: Rope): Rope =
discard cgsym(p.module, "nimFrame") discard cgsym(p.module, "nimFrame")
if p.maxFrameLen > 0: if p.maxFrameLen > 0:
discard cgsym(p.module, "TVarSlot") discard cgsym(p.module, "TVarSlot")
result = rfmt(nil, "\tnimfrs($1, $2, $3, $4)$N", result = rfmt(nil, "\tnimfrs($1, $2, $3, $4)$N",
procname, filename, p.maxFrameLen.toRope, procname, filename, p.maxFrameLen.rope,
p.blocks[0].frameLen.toRope) p.blocks[0].frameLen.rope)
else: else:
result = rfmt(nil, "\tnimfr($1, $2)$N", procname, filename) result = rfmt(nil, "\tnimfr($1, $2)$N", procname, filename)
proc deinitFrame(p: BProc): PRope = proc deinitFrame(p: BProc): Rope =
result = rfmt(p.module, "\t#popFrame();$n") result = rfmt(p.module, "\t#popFrame();$n")
proc closureSetup(p: BProc, prc: PSym) = proc closureSetup(p: BProc, prc: PSym) =
@ -650,7 +649,7 @@ proc closureSetup(p: BProc, prc: PSym) =
proc genProcAux(m: BModule, prc: PSym) = proc genProcAux(m: BModule, prc: PSym) =
var p = newProc(prc, m) var p = newProc(prc, m)
var header = genProcHeader(m, prc) var header = genProcHeader(m, prc)
var returnStmt: PRope = nil var returnStmt: Rope = nil
assert(prc.ast != nil) assert(prc.ast != nil)
if sfPure notin prc.flags and prc.typ.sons[0] != nil: if sfPure notin prc.flags and prc.typ.sons[0] != nil:
if resultPos >= prc.ast.len: if resultPos >= prc.ast.len:
@ -676,33 +675,36 @@ proc genProcAux(m: BModule, prc: PSym) =
assignParam(p, param) assignParam(p, param)
closureSetup(p, prc) closureSetup(p, prc)
genStmts(p, prc.getBody) # modifies p.locals, p.init, etc. genStmts(p, prc.getBody) # modifies p.locals, p.init, etc.
var generatedProc: PRope var generatedProc: Rope
if sfNoReturn in prc.flags:
if hasDeclspec in extccomp.CC[extccomp.cCompiler].props:
header = "__declspec(noreturn) " & header
if sfPure in prc.flags: if sfPure in prc.flags:
if hasNakedDeclspec in extccomp.CC[extccomp.cCompiler].props: if hasDeclspec in extccomp.CC[extccomp.cCompiler].props:
header = con("__declspec(naked) ", header) header = "__declspec(naked) " & header
generatedProc = rfmt(nil, "$N$1 {$n$2$3$4}$N$N", generatedProc = rfmt(nil, "$N$1 {$n$2$3$4}$N$N",
header, p.s(cpsLocals), p.s(cpsInit), p.s(cpsStmts)) header, p.s(cpsLocals), p.s(cpsInit), p.s(cpsStmts))
else: else:
generatedProc = rfmt(nil, "$N$1 {$N", header) generatedProc = rfmt(nil, "$N$1 {$N", header)
app(generatedProc, initGCFrame(p)) add(generatedProc, initGCFrame(p))
if optStackTrace in prc.options: if optStackTrace in prc.options:
app(generatedProc, p.s(cpsLocals)) add(generatedProc, p.s(cpsLocals))
var procname = makeCString(prc.name.s) var procname = makeCString(prc.name.s)
app(generatedProc, initFrame(p, procname, prc.info.quotedFilename)) add(generatedProc, initFrame(p, procname, prc.info.quotedFilename))
else: else:
app(generatedProc, p.s(cpsLocals)) add(generatedProc, p.s(cpsLocals))
if optProfiler in prc.options: if optProfiler in prc.options:
# invoke at proc entry for recursion: # invoke at proc entry for recursion:
appcg(p, cpsInit, "\t#nimProfile();$n", []) appcg(p, cpsInit, "\t#nimProfile();$n", [])
if p.beforeRetNeeded: app(generatedProc, "{") if p.beforeRetNeeded: add(generatedProc, "{")
app(generatedProc, p.s(cpsInit)) add(generatedProc, p.s(cpsInit))
app(generatedProc, p.s(cpsStmts)) add(generatedProc, p.s(cpsStmts))
if p.beforeRetNeeded: app(generatedProc, ~"\t}BeforeRet: ;$n") if p.beforeRetNeeded: add(generatedProc, ~"\t}BeforeRet: ;$n")
app(generatedProc, deinitGCFrame(p)) add(generatedProc, deinitGCFrame(p))
if optStackTrace in prc.options: app(generatedProc, deinitFrame(p)) if optStackTrace in prc.options: add(generatedProc, deinitFrame(p))
app(generatedProc, returnStmt) add(generatedProc, returnStmt)
app(generatedProc, ~"}$N") add(generatedProc, ~"}$N")
app(m.s[cfsProcs], generatedProc) add(m.s[cfsProcs], generatedProc)
proc crossesCppBoundary(m: BModule; sym: PSym): bool {.inline.} = proc crossesCppBoundary(m: BModule; sym: PSym): bool {.inline.} =
result = sfCompileToCpp in m.module.flags and result = sfCompileToCpp in m.module.flags and
@ -715,15 +717,17 @@ proc genProcPrototype(m: BModule, sym: PSym) =
if lfDynamicLib in sym.loc.flags: if lfDynamicLib in sym.loc.flags:
if getModule(sym).id != m.module.id and if getModule(sym).id != m.module.id and
not containsOrIncl(m.declaredThings, sym.id): not containsOrIncl(m.declaredThings, sym.id):
app(m.s[cfsVars], rfmt(nil, "extern $1 $2;$n", add(m.s[cfsVars], rfmt(nil, "extern $1 $2;$n",
getTypeDesc(m, sym.loc.t), mangleDynLibProc(sym))) getTypeDesc(m, sym.loc.t), mangleDynLibProc(sym)))
elif not containsOrIncl(m.declaredProtos, sym.id): elif not containsOrIncl(m.declaredProtos, sym.id):
var header = genProcHeader(m, sym) var header = genProcHeader(m, sym)
if sym.typ.callConv != ccInline and crossesCppBoundary(m, sym): if sym.typ.callConv != ccInline and crossesCppBoundary(m, sym):
header = con("extern \"C\" ", header) header = "extern \"C\" " & header
if sfPure in sym.flags and hasNakedAttribute in CC[cCompiler].props: if sfPure in sym.flags and hasAttribute in CC[cCompiler].props:
header.app(" __attribute__((naked))") header.add(" __attribute__((naked))")
app(m.s[cfsProcHeaders], rfmt(nil, "$1;$n", header)) if sfNoReturn in sym.flags and hasAttribute in CC[cCompiler].props:
header.add(" __attribute__((noreturn))")
add(m.s[cfsProcHeaders], rfmt(nil, "$1;$n", header))
proc genProcNoForward(m: BModule, prc: PSym) = proc genProcNoForward(m: BModule, prc: PSym) =
fillProcLoc(prc) fillProcLoc(prc)
@ -760,16 +764,16 @@ proc requestConstImpl(p: BProc, sym: PSym) =
var q = findPendingModule(m, sym) var q = findPendingModule(m, sym)
if q != nil and not containsOrIncl(q.declaredThings, sym.id): if q != nil and not containsOrIncl(q.declaredThings, sym.id):
assert q.initProc.module == q assert q.initProc.module == q
appf(q.s[cfsData], "NIM_CONST $1 $2 = $3;$n", addf(q.s[cfsData], "NIM_CONST $1 $2 = $3;$n",
[getTypeDesc(q, sym.typ), sym.loc.r, genConstExpr(q.initProc, sym.ast)]) [getTypeDesc(q, sym.typ), sym.loc.r, genConstExpr(q.initProc, sym.ast)])
# declare header: # declare header:
if q != m and not containsOrIncl(m.declaredThings, sym.id): if q != m and not containsOrIncl(m.declaredThings, sym.id):
assert(sym.loc.r != nil) assert(sym.loc.r != nil)
let headerDecl = ropef("extern NIM_CONST $1 $2;$n", let headerDecl = "extern NIM_CONST $1 $2;$n" %
[getTypeDesc(m, sym.loc.t), sym.loc.r]) [getTypeDesc(m, sym.loc.t), sym.loc.r]
app(m.s[cfsData], headerDecl) add(m.s[cfsData], headerDecl)
if sfExportc in sym.flags and generatedHeader != nil: if sfExportc in sym.flags and generatedHeader != nil:
app(generatedHeader.s[cfsData], headerDecl) add(generatedHeader.s[cfsData], headerDecl)
proc isActivated(prc: PSym): bool = prc.typ != nil proc isActivated(prc: PSym): bool = prc.typ != nil
@ -798,47 +802,47 @@ proc genVarPrototypeAux(m: BModule, sym: PSym) =
if sfThread in sym.flags: if sfThread in sym.flags:
declareThreadVar(m, sym, true) declareThreadVar(m, sym, true)
else: else:
app(m.s[cfsVars], "extern ") add(m.s[cfsVars], "extern ")
app(m.s[cfsVars], getTypeDesc(m, sym.loc.t)) add(m.s[cfsVars], getTypeDesc(m, sym.loc.t))
if lfDynamicLib in sym.loc.flags: app(m.s[cfsVars], "*") if lfDynamicLib in sym.loc.flags: add(m.s[cfsVars], "*")
if sfRegister in sym.flags: app(m.s[cfsVars], " register") if sfRegister in sym.flags: add(m.s[cfsVars], " register")
if sfVolatile in sym.flags: app(m.s[cfsVars], " volatile") if sfVolatile in sym.flags: add(m.s[cfsVars], " volatile")
appf(m.s[cfsVars], " $1;$n", [sym.loc.r]) addf(m.s[cfsVars], " $1;$n", [sym.loc.r])
proc genVarPrototype(m: BModule, sym: PSym) = proc genVarPrototype(m: BModule, sym: PSym) =
genVarPrototypeAux(m, sym) genVarPrototypeAux(m, sym)
proc addIntTypes(result: var PRope) {.inline.} = proc addIntTypes(result: var Rope) {.inline.} =
appf(result, "#define NIM_INTBITS $1", [ addf(result, "#define NIM_INTBITS $1", [
platform.CPU[targetCPU].intSize.toRope]) platform.CPU[targetCPU].intSize.rope])
proc getCopyright(cfile: string): PRope = proc getCopyright(cfile: string): Rope =
if optCompileOnly in gGlobalOptions: if optCompileOnly in gGlobalOptions:
result = ropef("/* Generated by Nim Compiler v$1 */$N" & result = ("/* Generated by Nim Compiler v$1 */$N" &
"/* (c) 2015 Andreas Rumpf */$N" & "/* (c) 2015 Andreas Rumpf */$N" &
"/* The generated code is subject to the original license. */$N", "/* The generated code is subject to the original license. */$N") %
[toRope(VersionAsString)]) [rope(VersionAsString)]
else: else:
result = ropef("/* Generated by Nim Compiler v$1 */$N" & result = ("/* Generated by Nim Compiler v$1 */$N" &
"/* (c) 2015 Andreas Rumpf */$N" & "/* (c) 2015 Andreas Rumpf */$N" &
"/* The generated code is subject to the original license. */$N" & "/* The generated code is subject to the original license. */$N" &
"/* Compiled for: $2, $3, $4 */$N" & "/* Compiled for: $2, $3, $4 */$N" &
"/* Command for C compiler:$n $5 */$N", "/* Command for C compiler:$n $5 */$N") %
[toRope(VersionAsString), [rope(VersionAsString),
toRope(platform.OS[targetOS].name), rope(platform.OS[targetOS].name),
toRope(platform.CPU[targetCPU].name), rope(platform.CPU[targetCPU].name),
toRope(extccomp.CC[extccomp.cCompiler].name), rope(extccomp.CC[extccomp.cCompiler].name),
toRope(getCompileCFileCmd(cfile))]) rope(getCompileCFileCmd(cfile))]
proc getFileHeader(cfile: string): PRope = proc getFileHeader(cfile: string): Rope =
result = getCopyright(cfile) result = getCopyright(cfile)
addIntTypes(result) addIntTypes(result)
proc genFilenames(m: BModule): PRope = proc genFilenames(m: BModule): Rope =
discard cgsym(m, "dbgRegisterFilename") discard cgsym(m, "dbgRegisterFilename")
result = nil result = nil
for i in 0.. <fileInfos.len: for i in 0.. <fileInfos.len:
result.appf("dbgRegisterFilename($1);$N", fileInfos[i].projPath.makeCString) result.addf("dbgRegisterFilename($1);$N", [fileInfos[i].projPath.makeCString])
proc genMainProc(m: BModule) = proc genMainProc(m: BModule) =
const const
@ -920,7 +924,7 @@ proc genMainProc(m: BModule) =
MainProcs & MainProcs &
"}$N$N" "}$N$N"
var nimMain, otherMain: TFormatStr var nimMain, otherMain: FormatStr
if platform.targetOS == osWindows and if platform.targetOS == osWindows and
gGlobalOptions * {optGenGuiApp, optGenDynLib} != {}: gGlobalOptions * {optGenGuiApp, optGenDynLib} != {}:
if optGenGuiApp in gGlobalOptions: if optGenGuiApp in gGlobalOptions:
@ -941,10 +945,10 @@ proc genMainProc(m: BModule) =
otherMain = PosixCMain otherMain = PosixCMain
if gBreakpoints != nil: discard cgsym(m, "dbgRegisterBreakpoint") if gBreakpoints != nil: discard cgsym(m, "dbgRegisterBreakpoint")
if optEndb in gOptions: if optEndb in gOptions:
gBreakpoints.app(m.genFilenames) gBreakpoints.add(m.genFilenames)
let initStackBottomCall = let initStackBottomCall =
if platform.targetOS == osStandalone: "".toRope if platform.targetOS == osStandalone: "".rope
else: ropecg(m, "\t#initStackBottomWith((void *)&inner);$N") else: ropecg(m, "\t#initStackBottomWith((void *)&inner);$N")
inc(m.labels) inc(m.labels)
appcg(m, m.s[cfsProcs], PreMainBody, [ appcg(m, m.s[cfsProcs], PreMainBody, [
@ -952,56 +956,56 @@ proc genMainProc(m: BModule) =
if emulatedThreadVars() and platform.targetOS != osStandalone: if emulatedThreadVars() and platform.targetOS != osStandalone:
ropecg(m, "\t#initThreadVarsEmulation();$N") ropecg(m, "\t#initThreadVarsEmulation();$N")
else: else:
"".toRope, "".rope,
initStackBottomCall]) initStackBottomCall])
appcg(m, m.s[cfsProcs], nimMain, [mainModInit, initStackBottomCall, toRope(m.labels)]) appcg(m, m.s[cfsProcs], nimMain, [mainModInit, initStackBottomCall, rope(m.labels)])
if optNoMain notin gGlobalOptions: if optNoMain notin gGlobalOptions:
appcg(m, m.s[cfsProcs], otherMain, []) appcg(m, m.s[cfsProcs], otherMain, [])
proc getSomeInitName(m: PSym, suffix: string): PRope = proc getSomeInitName(m: PSym, suffix: string): Rope =
assert m.kind == skModule assert m.kind == skModule
assert m.owner.kind == skPackage assert m.owner.kind == skPackage
if {sfSystemModule, sfMainModule} * m.flags == {}: if {sfSystemModule, sfMainModule} * m.flags == {}:
result = m.owner.name.s.mangle.toRope result = m.owner.name.s.mangle.rope
result.app "_" result.add "_"
result.app m.name.s result.add m.name.s
result.app suffix result.add suffix
proc getInitName(m: PSym): PRope = getSomeInitName(m, "Init") proc getInitName(m: PSym): Rope = getSomeInitName(m, "Init")
proc getDatInitName(m: PSym): PRope = getSomeInitName(m, "DatInit") proc getDatInitName(m: PSym): Rope = getSomeInitName(m, "DatInit")
proc registerModuleToMain(m: PSym) = proc registerModuleToMain(m: PSym) =
var var
init = m.getInitName init = m.getInitName
datInit = m.getDatInitName datInit = m.getDatInitName
appf(mainModProcs, "NIM_EXTERNC N_NOINLINE(void, $1)(void);$N", [init]) addf(mainModProcs, "NIM_EXTERNC N_NOINLINE(void, $1)(void);$N", [init])
appf(mainModProcs, "NIM_EXTERNC N_NOINLINE(void, $1)(void);$N", [datInit]) addf(mainModProcs, "NIM_EXTERNC N_NOINLINE(void, $1)(void);$N", [datInit])
if sfSystemModule notin m.flags: if sfSystemModule notin m.flags:
appf(mainDatInit, "\t$1();$N", [datInit]) addf(mainDatInit, "\t$1();$N", [datInit])
let initCall = ropef("\t$1();$N", [init]) let initCall = "\t$1();$N" % [init]
if sfMainModule in m.flags: if sfMainModule in m.flags:
app(mainModInit, initCall) add(mainModInit, initCall)
else: else:
app(otherModsInit, initCall) add(otherModsInit, initCall)
proc genInitCode(m: BModule) = proc genInitCode(m: BModule) =
var initname = getInitName(m.module) var initname = getInitName(m.module)
var prc = ropef("NIM_EXTERNC N_NOINLINE(void, $1)(void) {$N", [initname]) var prc = "NIM_EXTERNC N_NOINLINE(void, $1)(void) {$N" % [initname]
if m.typeNodes > 0: if m.typeNodes > 0:
appcg(m, m.s[cfsTypeInit1], "static #TNimNode $1[$2];$n", appcg(m, m.s[cfsTypeInit1], "static #TNimNode $1[$2];$n",
[m.typeNodesName, toRope(m.typeNodes)]) [m.typeNodesName, rope(m.typeNodes)])
if m.nimTypes > 0: if m.nimTypes > 0:
appcg(m, m.s[cfsTypeInit1], "static #TNimType $1[$2];$n", appcg(m, m.s[cfsTypeInit1], "static #TNimType $1[$2];$n",
[m.nimTypesName, toRope(m.nimTypes)]) [m.nimTypesName, rope(m.nimTypes)])
app(prc, initGCFrame(m.initProc)) add(prc, initGCFrame(m.initProc))
app(prc, genSectionStart(cpsLocals)) add(prc, genSectionStart(cpsLocals))
app(prc, m.preInitProc.s(cpsLocals)) add(prc, m.preInitProc.s(cpsLocals))
app(prc, m.initProc.s(cpsLocals)) add(prc, m.initProc.s(cpsLocals))
app(prc, m.postInitProc.s(cpsLocals)) add(prc, m.postInitProc.s(cpsLocals))
app(prc, genSectionEnd(cpsLocals)) add(prc, genSectionEnd(cpsLocals))
if optStackTrace in m.initProc.options and not m.frameDeclared: if optStackTrace in m.initProc.options and not m.frameDeclared:
# BUT: the generated init code might depend on a current frame, so # BUT: the generated init code might depend on a current frame, so
@ -1009,58 +1013,58 @@ proc genInitCode(m: BModule) =
m.frameDeclared = true m.frameDeclared = true
if not m.preventStackTrace: if not m.preventStackTrace:
var procname = makeCString(m.module.name.s) var procname = makeCString(m.module.name.s)
app(prc, initFrame(m.initProc, procname, m.module.info.quotedFilename)) add(prc, initFrame(m.initProc, procname, m.module.info.quotedFilename))
else: else:
app(prc, ~"\tTFrame F; F.len = 0;$N") add(prc, ~"\tTFrame F; F.len = 0;$N")
app(prc, genSectionStart(cpsInit)) add(prc, genSectionStart(cpsInit))
app(prc, m.preInitProc.s(cpsInit)) add(prc, m.preInitProc.s(cpsInit))
app(prc, m.initProc.s(cpsInit)) add(prc, m.initProc.s(cpsInit))
app(prc, m.postInitProc.s(cpsInit)) add(prc, m.postInitProc.s(cpsInit))
app(prc, genSectionEnd(cpsInit)) add(prc, genSectionEnd(cpsInit))
app(prc, genSectionStart(cpsStmts)) add(prc, genSectionStart(cpsStmts))
app(prc, m.preInitProc.s(cpsStmts)) add(prc, m.preInitProc.s(cpsStmts))
app(prc, m.initProc.s(cpsStmts)) add(prc, m.initProc.s(cpsStmts))
app(prc, m.postInitProc.s(cpsStmts)) add(prc, m.postInitProc.s(cpsStmts))
app(prc, genSectionEnd(cpsStmts)) add(prc, genSectionEnd(cpsStmts))
if optStackTrace in m.initProc.options and not m.preventStackTrace: if optStackTrace in m.initProc.options and not m.preventStackTrace:
app(prc, deinitFrame(m.initProc)) add(prc, deinitFrame(m.initProc))
app(prc, deinitGCFrame(m.initProc)) add(prc, deinitGCFrame(m.initProc))
appf(prc, "}$N$N") addf(prc, "}$N$N", [])
prc.appf("NIM_EXTERNC N_NOINLINE(void, $1)(void) {$N", prc.addf("NIM_EXTERNC N_NOINLINE(void, $1)(void) {$N",
[getDatInitName(m.module)]) [getDatInitName(m.module)])
for i in cfsTypeInit1..cfsDynLibInit: for i in cfsTypeInit1..cfsDynLibInit:
app(prc, genSectionStart(i)) add(prc, genSectionStart(i))
app(prc, m.s[i]) add(prc, m.s[i])
app(prc, genSectionEnd(i)) add(prc, genSectionEnd(i))
appf(prc, "}$N$N") addf(prc, "}$N$N", [])
# we cannot simply add the init proc to ``m.s[cfsProcs]`` anymore because # we cannot simply add the init proc to ``m.s[cfsProcs]`` anymore because
# that would lead to a *nesting* of merge sections which the merger does # that would lead to a *nesting* of merge sections which the merger does
# not support. So we add it to another special section: ``cfsInitProc`` # not support. So we add it to another special section: ``cfsInitProc``
app(m.s[cfsInitProc], prc) add(m.s[cfsInitProc], prc)
for i, el in pairs(m.extensionLoaders): for i, el in pairs(m.extensionLoaders):
if el != nil: if el != nil:
let ex = ropef("N_NIMCALL(void, nimLoadProcs$1)(void) {$2}$N$N", let ex = "N_NIMCALL(void, nimLoadProcs$1)(void) {$2}$N$N" %
(i.ord - '0'.ord).toRope, el) [(i.ord - '0'.ord).rope, el]
app(m.s[cfsInitProc], ex) add(m.s[cfsInitProc], ex)
proc genModule(m: BModule, cfile: string): PRope = proc genModule(m: BModule, cfile: string): Rope =
result = getFileHeader(cfile) result = getFileHeader(cfile)
result.app(genMergeInfo(m)) result.add(genMergeInfo(m))
generateHeaders(m) generateHeaders(m)
generateThreadLocalStorage(m) generateThreadLocalStorage(m)
for i in countup(cfsHeaders, cfsProcs): for i in countup(cfsHeaders, cfsProcs):
app(result, genSectionStart(i)) add(result, genSectionStart(i))
app(result, m.s[i]) add(result, m.s[i])
app(result, genSectionEnd(i)) add(result, genSectionEnd(i))
app(result, m.s[cfsInitProc]) add(result, m.s[cfsInitProc])
proc newPreInitProc(m: BModule): BProc = proc newPreInitProc(m: BModule): BProc =
result = newProc(nil, m) result = newProc(nil, m)
@ -1172,22 +1176,22 @@ proc myOpen(module: PSym): PPassContext =
proc writeHeader(m: BModule) = proc writeHeader(m: BModule) =
var result = getCopyright(m.filename) var result = getCopyright(m.filename)
var guard = ropef("__$1__", m.filename.splitFile.name.toRope) var guard = "__$1__" % [m.filename.splitFile.name.rope]
result.appf("#ifndef $1$n#define $1$n", guard) result.addf("#ifndef $1$n#define $1$n", [guard])
addIntTypes(result) addIntTypes(result)
generateHeaders(m) generateHeaders(m)
generateThreadLocalStorage(m) generateThreadLocalStorage(m)
for i in countup(cfsHeaders, cfsProcs): for i in countup(cfsHeaders, cfsProcs):
app(result, genSectionStart(i)) add(result, genSectionStart(i))
app(result, m.s[i]) add(result, m.s[i])
app(result, genSectionEnd(i)) add(result, genSectionEnd(i))
app(result, m.s[cfsInitProc]) add(result, m.s[cfsInitProc])
if optGenDynLib in gGlobalOptions: if optGenDynLib in gGlobalOptions:
result.app("N_LIB_IMPORT ") result.add("N_LIB_IMPORT ")
result.appf("N_CDECL(void, NimMain)(void);$n") result.addf("N_CDECL(void, NimMain)(void);$n", [])
result.appf("#endif /* $1 */$n", guard) result.addf("#endif /* $1 */$n", [guard])
writeRope(result, m.filename) writeRope(result, m.filename)
proc getCFile(m: BModule): string = proc getCFile(m: BModule): string =
@ -1224,7 +1228,7 @@ proc finishModule(m: BModule) =
dec(gForwardedProcsCounter, i) dec(gForwardedProcsCounter, i)
setLen(m.forwardedProcs, 0) setLen(m.forwardedProcs, 0)
proc shouldRecompile(code: PRope, cfile: string): bool = proc shouldRecompile(code: Rope, cfile: string): bool =
result = true result = true
if optForceFullMake notin gGlobalOptions: if optForceFullMake notin gGlobalOptions:
var objFile = toObjFile(cfile) var objFile = toObjFile(cfile)
@ -1249,13 +1253,13 @@ proc writeModule(m: BModule, pending: bool) =
finishTypeDescriptions(m) finishTypeDescriptions(m)
if sfMainModule in m.module.flags: if sfMainModule in m.module.flags:
# generate main file: # generate main file:
app(m.s[cfsProcHeaders], mainModProcs) add(m.s[cfsProcHeaders], mainModProcs)
generateThreadVarsSize(m) generateThreadVarsSize(m)
var code = genModule(m, cfile) var code = genModule(m, cfile)
when hasTinyCBackend: when hasTinyCBackend:
if gCmd == cmdRun: if gCmd == cmdRun:
tccgen.compileCCode(ropeToStr(code)) tccgen.compileCCode($code)
return return
if shouldRecompile(code, cfile): if shouldRecompile(code, cfile):

View file

@ -15,7 +15,7 @@ import
from msgs import TLineInfo from msgs import TLineInfo
type 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 TCFileSection* = enum # the sections a generated C file consists of
cfsMergeInfo, # section containing merge information cfsMergeInfo, # section containing merge information
cfsHeaders, # section for C include file headers cfsHeaders, # section for C include file headers
@ -45,17 +45,17 @@ type
ctUInt, ctUInt8, ctUInt16, ctUInt32, ctUInt64, ctUInt, ctUInt8, ctUInt16, ctUInt32, ctUInt64,
ctArray, ctPtrToArray, ctStruct, ctPtr, ctNimStr, ctNimSeq, ctProc, ctArray, ctPtrToArray, ctStruct, ctPtr, ctNimStr, ctNimSeq, ctProc,
ctCString 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 TCProcSection* = enum # the sections a generated C proc consists of
cpsLocals, # section of local variables for C proc cpsLocals, # section of local variables for C proc
cpsInit, # section for init of variables for C proc cpsInit, # section for init of variables for C proc
cpsStmts # section of local statements 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 BModule* = ref TCGen
BProc* = ref TCProc BProc* = ref TCProc
TBlock*{.final.} = object TBlock*{.final.} = object
id*: int # the ID of the label; positive means that it 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 # nil if label is not used
sections*: TCProcSections # the code beloging sections*: TCProcSections # the code beloging
isLoop*: bool # whether block is a loop isLoop*: bool # whether block is a loop
@ -73,7 +73,7 @@ type
inExceptBlock*: int # are we currently inside an except block? inExceptBlock*: int # are we currently inside an except block?
# leaving such scopes by raise or by return must # leaving such scopes by raise or by return must
# execute any applicable finally blocks # 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 # using return in finally statements
labels*: Natural # for generating unique labels in the C proc labels*: Natural # for generating unique labels in the C proc
blocks*: seq[TBlock] # nested blocks blocks*: seq[TBlock] # nested blocks
@ -89,7 +89,7 @@ type
# requires 'T x = T()' to become 'T x; x = T()' # requires 'T x = T()' to become 'T x; x = T()'
# (yes, C++ is weird like that) # (yes, C++ is weird like that)
gcFrameId*: Natural # for the GC stack marking 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] TTypeSeq* = seq[PType]
TCGen = object of TPassContext # represents a C source file TCGen = object of TPassContext # represents a C source file
@ -118,24 +118,24 @@ type
dataCache*: TNodeTable dataCache*: TNodeTable
forwardedProcs*: TSymSeq # keep forwarded procs here forwardedProcs*: TSymSeq # keep forwarded procs here
typeNodes*, nimTypes*: int # used for type info generation 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 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 # OpenGL wrapper
injectStmt*: PRope injectStmt*: Rope
var var
mainModProcs*, mainModInit*, otherModsInit*, mainDatInit*: PRope mainModProcs*, mainModInit*, otherModsInit*, mainDatInit*: Rope
# varuious parts of the main module # 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 gModules*: seq[BModule] = @[] # list of all compiled modules
gForwardedProcsCounter*: int = 0 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 # section in the current block
result = p.blocks[p.blocks.len - 1].sections[s] 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 # top level proc sections
result = p.blocks[0].sections[s] result = p.blocks[0].sections[s]

View file

@ -80,9 +80,9 @@ proc writeVersionInfo(pass: TCmdLinePass) =
platform.OS[platform.hostOS].name, platform.OS[platform.hostOS].name,
CPU[platform.hostCPU].name])) CPU[platform.hostCPU].name]))
discard """const gitHash = gorge("git log -n 1 --format=%H") const gitHash = gorge("git log -n 1 --format=%H").strip
if gitHash.strip.len == 40: when gitHash.len == 40:
msgWriteln("git hash: " & gitHash)""" msgWriteln("git hash: " & gitHash)
msgWriteln("active boot switches:" & usedRelease & usedAvoidTimeMachine & msgWriteln("active boot switches:" & usedRelease & usedAvoidTimeMachine &
usedTinyC & usedGnuReadline & usedNativeStacktrace & usedNoCaas & usedTinyC & usedGnuReadline & usedNativeStacktrace & usedNoCaas &
@ -319,7 +319,7 @@ proc processSwitch(switch, arg: string, pass: TCmdLinePass, info: TLineInfo) =
undefSymbol(arg) undefSymbol(arg)
of "symbol": of "symbol":
expectArg(switch, arg, pass, info) expectArg(switch, arg, pass, info)
declareSymbol(arg) # deprecated, do nothing
of "compile": of "compile":
expectArg(switch, arg, pass, info) expectArg(switch, arg, pass, info)
if pass in {passCmd2, passPP}: processCompile(arg) if pass in {passCmd2, passPP}: processCompile(arg)
@ -407,7 +407,7 @@ proc processSwitch(switch, arg: string, pass: TCmdLinePass, info: TLineInfo) =
of "deadcodeelim": processOnOffSwitchG({optDeadCodeElim}, arg, pass, info) of "deadcodeelim": processOnOffSwitchG({optDeadCodeElim}, arg, pass, info)
of "threads": of "threads":
processOnOffSwitchG({optThreads}, arg, pass, info) 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 "tlsemulation": processOnOffSwitchG({optTlsEmulation}, arg, pass, info)
of "taintmode": processOnOffSwitchG({optTaintMode}, arg, pass, info) of "taintmode": processOnOffSwitchG({optTaintMode}, arg, pass, info)
of "implicitstatic": of "implicitstatic":
@ -488,7 +488,6 @@ proc processSwitch(switch, arg: string, pass: TCmdLinePass, info: TLineInfo) =
if theOS == osNone: localError(info, errUnknownOS, arg) if theOS == osNone: localError(info, errUnknownOS, arg)
elif theOS != platform.hostOS: elif theOS != platform.hostOS:
setTarget(theOS, targetCPU) setTarget(theOS, targetCPU)
condsyms.initDefines()
of "cpu": of "cpu":
expectArg(switch, arg, pass, info) expectArg(switch, arg, pass, info)
if pass in {passCmd1, passPP}: if pass in {passCmd1, passPP}:
@ -496,7 +495,6 @@ proc processSwitch(switch, arg: string, pass: TCmdLinePass, info: TLineInfo) =
if cpu == cpuNone: localError(info, errUnknownCPU, arg) if cpu == cpuNone: localError(info, errUnknownCPU, arg)
elif cpu != platform.hostCPU: elif cpu != platform.hostCPU:
setTarget(targetOS, cpu) setTarget(targetOS, cpu)
condsyms.initDefines()
of "run", "r": of "run", "r":
expectNoArg(switch, arg, pass, info) expectNoArg(switch, arg, pass, info)
incl(gGlobalOptions, optRun) incl(gGlobalOptions, optRun)

View file

@ -12,66 +12,64 @@
import import
strtabs, platform, strutils, idents 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. # to be style insensitive. Otherwise hell would break lose.
var gSymbols: StringTableRef var gSymbols: StringTableRef
const
catNone = "false"
proc defineSymbol*(symbol: string) = proc defineSymbol*(symbol: string) =
gSymbols[symbol] = "true" gSymbols[symbol] = "true"
proc declareSymbol*(symbol: string) =
gSymbols[symbol] = "unknown"
proc undefSymbol*(symbol: string) = proc undefSymbol*(symbol: string) =
gSymbols[symbol] = "false" gSymbols[symbol] = catNone
proc isDefined*(symbol: string): bool = proc isDefined*(symbol: string): bool =
if gSymbols.hasKey(symbol): 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 isDefined*(symbol: PIdent): bool = isDefined(symbol.s)
proc isDeclared*(symbol: PIdent): bool = gSymbols.hasKey(symbol.s)
iterator definedSymbolNames*: string = iterator definedSymbolNames*: string =
for key, val in pairs(gSymbols): for key, val in pairs(gSymbols):
if val == "true": yield key if val != catNone: yield key
proc countDefinedSymbols*(): int = proc countDefinedSymbols*(): int =
result = 0 result = 0
for key, val in pairs(gSymbols): for key, val in pairs(gSymbols):
if val == "true": inc(result) if val != catNone: 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
proc initDefines*() = proc initDefines*() =
gSymbols = newStringTable(modeStyleInsensitive) gSymbols = newStringTable(modeStyleInsensitive)
@ -90,58 +88,3 @@ proc initDefines*() =
defineSymbol("nimalias") defineSymbol("nimalias")
defineSymbol("nimlocks") defineSymbol("nimlocks")
defineSymbol("nimnode") defineSymbol("nimnode")
# 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, osVxWorks:
# 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

View file

@ -19,10 +19,10 @@ type
module*: PSym module*: PSym
PGen = ref TGen 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) = 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]"]; # s1 -> s2_4[label="[0-9]"];
proc addDotDependency(c: PPassContext, n: PNode): PNode = proc addDotDependency(c: PPassContext, n: PNode): PNode =
@ -42,8 +42,8 @@ proc addDotDependency(c: PPassContext, n: PNode): PNode =
discard discard
proc generateDot(project: string) = proc generateDot(project: string) =
writeRope(ropef("digraph $1 {$n$2}$n", [ writeRope("digraph $1 {$n$2}$n" % [
toRope(changeFileExt(extractFilename(project), "")), gDotGraph]), rope(changeFileExt(extractFilename(project), "")), gDotGraph],
changeFileExt(project, "dot")) changeFileExt(project, "dot"))
proc myOpen(module: PSym): PPassContext = proc myOpen(module: PSym): PPassContext =

View file

@ -17,9 +17,9 @@ import
importer, sempass2, json, xmltree, cgi, typesrenderer importer, sempass2, json, xmltree, cgi, typesrenderer
type type
TSections = array[TSymKind, PRope] TSections = array[TSymKind, Rope]
TDocumentor = object of rstgen.TRstGenerator TDocumentor = object of rstgen.TRstGenerator
modDesc: PRope # module description modDesc: Rope # module description
id: int # for generating IDs id: int # for generating IDs
toc, section: TSections toc, section: TSections
indexValFilename: string indexValFilename: string
@ -82,9 +82,9 @@ proc newDocumentor*(filename: string, config: StringTableRef): PDoc =
result.seenSymbols = newStringTable(modeCaseInsensitive) result.seenSymbols = newStringTable(modeCaseInsensitive)
result.id = 100 result.id = 100
proc dispA(dest: var PRope, xml, tex: string, args: openArray[PRope]) = proc dispA(dest: var Rope, xml, tex: string, args: openArray[Rope]) =
if gCmd != cmdRst2tex: appf(dest, xml, args) if gCmd != cmdRst2tex: addf(dest, xml, args)
else: appf(dest, tex, args) else: addf(dest, tex, args)
proc getVarIdx(varnames: openArray[string], id: string): int = proc getVarIdx(varnames: openArray[string], id: string): int =
for i in countup(0, high(varnames)): for i in countup(0, high(varnames)):
@ -92,8 +92,8 @@ proc getVarIdx(varnames: openArray[string], id: string): int =
return i return i
result = -1 result = -1
proc ropeFormatNamedVars(frmt: TFormatStr, varnames: openArray[string], proc ropeFormatNamedVars(frmt: FormatStr, varnames: openArray[string],
varvalues: openArray[PRope]): PRope = varvalues: openArray[Rope]): Rope =
var i = 0 var i = 0
var L = len(frmt) var L = len(frmt)
result = nil result = nil
@ -103,11 +103,11 @@ proc ropeFormatNamedVars(frmt: TFormatStr, varnames: openArray[string],
inc(i) # skip '$' inc(i) # skip '$'
case frmt[i] case frmt[i]
of '#': of '#':
app(result, varvalues[num]) add(result, varvalues[num])
inc(num) inc(num)
inc(i) inc(i)
of '$': of '$':
app(result, "$") add(result, "$")
inc(i) inc(i)
of '0'..'9': of '0'..'9':
var j = 0 var j = 0
@ -117,7 +117,7 @@ proc ropeFormatNamedVars(frmt: TFormatStr, varnames: openArray[string],
if (i > L + 0 - 1) or not (frmt[i] in {'0'..'9'}): break if (i > L + 0 - 1) or not (frmt[i] in {'0'..'9'}): break
if j > high(varvalues) + 1: internalError("ropeFormatNamedVars") if j > high(varvalues) + 1: internalError("ropeFormatNamedVars")
num = j num = j
app(result, varvalues[j - 1]) add(result, varvalues[j - 1])
of 'A'..'Z', 'a'..'z', '\x80'..'\xFF': of 'A'..'Z', 'a'..'z', '\x80'..'\xFF':
var id = "" var id = ""
while true: while true:
@ -125,7 +125,7 @@ proc ropeFormatNamedVars(frmt: TFormatStr, varnames: openArray[string],
inc(i) inc(i)
if not (frmt[i] in {'A'..'Z', '_', 'a'..'z', '\x80'..'\xFF'}): break if not (frmt[i] in {'A'..'Z', '_', 'a'..'z', '\x80'..'\xFF'}): break
var idx = getVarIdx(varnames, id) var idx = getVarIdx(varnames, id)
if idx >= 0: app(result, varvalues[idx]) if idx >= 0: add(result, varvalues[idx])
else: rawMessage(errUnknownSubstitionVar, id) else: rawMessage(errUnknownSubstitionVar, id)
of '{': of '{':
var id = "" var id = ""
@ -137,14 +137,14 @@ proc ropeFormatNamedVars(frmt: TFormatStr, varnames: openArray[string],
inc(i) # skip } inc(i) # skip }
# search for the variable: # search for the variable:
var idx = getVarIdx(varnames, id) var idx = getVarIdx(varnames, id)
if idx >= 0: app(result, varvalues[idx]) if idx >= 0: add(result, varvalues[idx])
else: rawMessage(errUnknownSubstitionVar, id) else: rawMessage(errUnknownSubstitionVar, id)
else: internalError("ropeFormatNamedVars") else: internalError("ropeFormatNamedVars")
var start = i var start = i
while i < L: while i < L:
if frmt[i] != '$': inc(i) if frmt[i] != '$': inc(i)
else: break 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 = proc genComment(d: PDoc, n: PNode): string =
result = "" result = ""
@ -154,9 +154,9 @@ proc genComment(d: PDoc, n: PNode): string =
toLinenumber(n.info), toColumn(n.info), toLinenumber(n.info), toColumn(n.info),
dummyHasToc, d.options + {roSkipPounds}), result) dummyHasToc, d.options + {roSkipPounds}), result)
proc genRecComment(d: PDoc, n: PNode): PRope = proc genRecComment(d: PDoc, n: PNode): Rope =
if n == nil: return nil if n == nil: return nil
result = genComment(d, n).toRope result = genComment(d, n).rope
if result == nil: if result == nil:
if n.kind notin {nkEmpty..nkNilLit}: if n.kind notin {nkEmpty..nkNilLit}:
for i in countup(0, len(n)-1): for i in countup(0, len(n)-1):
@ -331,9 +331,9 @@ proc genItem(d: PDoc, n, nameNode: PNode, k: TSymKind) =
if not isVisible(nameNode): return if not isVisible(nameNode): return
let let
name = getName(d, nameNode) name = getName(d, nameNode)
nameRope = name.toRope nameRope = name.rope
plainDocstring = getPlainDocstring(n) # call here before genRecComment! plainDocstring = getPlainDocstring(n) # call here before genRecComment!
var result: PRope = nil var result: Rope = nil
var literal, plainName = "" var literal, plainName = ""
var kind = tkEof var kind = tkEof
var comm = genRecComment(d, n) # call this here for the side-effect! var comm = genRecComment(d, n) # call this here for the side-effect!
@ -356,69 +356,69 @@ proc genItem(d: PDoc, n, nameNode: PNode, k: TSymKind) =
break break
of tkComment: of tkComment:
dispA(result, "<span class=\"Comment\">$1</span>", "\\spanComment{$1}", dispA(result, "<span class=\"Comment\">$1</span>", "\\spanComment{$1}",
[toRope(esc(d.target, literal))]) [rope(esc(d.target, literal))])
of tokKeywordLow..tokKeywordHigh: of tokKeywordLow..tokKeywordHigh:
dispA(result, "<span class=\"Keyword\">$1</span>", "\\spanKeyword{$1}", dispA(result, "<span class=\"Keyword\">$1</span>", "\\spanKeyword{$1}",
[toRope(literal)]) [rope(literal)])
of tkOpr: of tkOpr:
dispA(result, "<span class=\"Operator\">$1</span>", "\\spanOperator{$1}", dispA(result, "<span class=\"Operator\">$1</span>", "\\spanOperator{$1}",
[toRope(esc(d.target, literal))]) [rope(esc(d.target, literal))])
of tkStrLit..tkTripleStrLit: of tkStrLit..tkTripleStrLit:
dispA(result, "<span class=\"StringLit\">$1</span>", dispA(result, "<span class=\"StringLit\">$1</span>",
"\\spanStringLit{$1}", [toRope(esc(d.target, literal))]) "\\spanStringLit{$1}", [rope(esc(d.target, literal))])
of tkCharLit: of tkCharLit:
dispA(result, "<span class=\"CharLit\">$1</span>", "\\spanCharLit{$1}", dispA(result, "<span class=\"CharLit\">$1</span>", "\\spanCharLit{$1}",
[toRope(esc(d.target, literal))]) [rope(esc(d.target, literal))])
of tkIntLit..tkUInt64Lit: of tkIntLit..tkUInt64Lit:
dispA(result, "<span class=\"DecNumber\">$1</span>", dispA(result, "<span class=\"DecNumber\">$1</span>",
"\\spanDecNumber{$1}", [toRope(esc(d.target, literal))]) "\\spanDecNumber{$1}", [rope(esc(d.target, literal))])
of tkFloatLit..tkFloat128Lit: of tkFloatLit..tkFloat128Lit:
dispA(result, "<span class=\"FloatNumber\">$1</span>", dispA(result, "<span class=\"FloatNumber\">$1</span>",
"\\spanFloatNumber{$1}", [toRope(esc(d.target, literal))]) "\\spanFloatNumber{$1}", [rope(esc(d.target, literal))])
of tkSymbol: of tkSymbol:
dispA(result, "<span class=\"Identifier\">$1</span>", dispA(result, "<span class=\"Identifier\">$1</span>",
"\\spanIdentifier{$1}", [toRope(esc(d.target, literal))]) "\\spanIdentifier{$1}", [rope(esc(d.target, literal))])
of tkSpaces, tkInvalid: of tkSpaces, tkInvalid:
app(result, literal) add(result, literal)
of tkParLe, tkParRi, tkBracketLe, tkBracketRi, tkCurlyLe, tkCurlyRi, of tkParLe, tkParRi, tkBracketLe, tkBracketRi, tkCurlyLe, tkCurlyRi,
tkBracketDotLe, tkBracketDotRi, tkCurlyDotLe, tkCurlyDotRi, tkParDotLe, tkBracketDotLe, tkBracketDotRi, tkCurlyDotLe, tkCurlyDotRi, tkParDotLe,
tkParDotRi, tkComma, tkSemiColon, tkColon, tkEquals, tkDot, tkDotDot, tkParDotRi, tkComma, tkSemiColon, tkColon, tkEquals, tkDot, tkDotDot,
tkAccent, tkColonColon, tkAccent, tkColonColon,
tkGStrLit, tkGTripleStrLit, tkInfixOpr, tkPrefixOpr, tkPostfixOpr: tkGStrLit, tkGTripleStrLit, tkInfixOpr, tkPrefixOpr, tkPostfixOpr:
dispA(result, "<span class=\"Other\">$1</span>", "\\spanOther{$1}", dispA(result, "<span class=\"Other\">$1</span>", "\\spanOther{$1}",
[toRope(esc(d.target, literal))]) [rope(esc(d.target, literal))])
inc(d.id) inc(d.id)
let let
plainNameRope = toRope(xmltree.escape(plainName.strip)) plainNameRope = rope(xmltree.escape(plainName.strip))
cleanPlainSymbol = renderPlainSymbolName(nameNode) cleanPlainSymbol = renderPlainSymbolName(nameNode)
complexSymbol = complexName(k, n, cleanPlainSymbol) complexSymbol = complexName(k, n, cleanPlainSymbol)
plainSymbolRope = toRope(cleanPlainSymbol) plainSymbolRope = rope(cleanPlainSymbol)
plainSymbolEncRope = toRope(encodeUrl(cleanPlainSymbol)) plainSymbolEncRope = rope(encodeUrl(cleanPlainSymbol))
itemIDRope = toRope(d.id) itemIDRope = rope(d.id)
symbolOrId = d.newUniquePlainSymbol(complexSymbol) symbolOrId = d.newUniquePlainSymbol(complexSymbol)
symbolOrIdRope = symbolOrId.toRope symbolOrIdRope = symbolOrId.rope
symbolOrIdEncRope = encodeUrl(symbolOrId).toRope symbolOrIdEncRope = encodeUrl(symbolOrId).rope
var seeSrcRope: PRope = nil var seeSrcRope: Rope = nil
let docItemSeeSrc = getConfigVar("doc.item.seesrc") let docItemSeeSrc = getConfigVar("doc.item.seesrc")
if docItemSeeSrc.len > 0 and options.docSeeSrcUrl.len > 0: if docItemSeeSrc.len > 0 and options.docSeeSrcUrl.len > 0:
# XXX toFilename doesn't really work. We need to ensure that this keeps # XXX toFilename doesn't really work. We need to ensure that this keeps
# returning a relative path. # returning a relative path.
let urlRope = ropeFormatNamedVars(options.docSeeSrcUrl, 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, dispA(seeSrcRope, "$1", "", [ropeFormatNamedVars(docItemSeeSrc,
["path", "line", "url"], [n.info.toFilename.toRope, ["path", "line", "url"], [n.info.toFilename.rope,
toRope($n.info.line), urlRope])]) 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", ["name", "header", "desc", "itemID", "header_plain", "itemSym",
"itemSymOrID", "itemSymEnc", "itemSymOrIDEnc", "seeSrc"], "itemSymOrID", "itemSymEnc", "itemSymOrIDEnc", "seeSrc"],
[nameRope, result, comm, itemIDRope, plainNameRope, plainSymbolRope, [nameRope, result, comm, itemIDRope, plainNameRope, plainSymbolRope,
symbolOrIdRope, plainSymbolEncRope, symbolOrIdEncRope, seeSrcRope])) 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", ["name", "header", "desc", "itemID", "header_plain", "itemSym",
"itemSymOrID", "itemSymEnc", "itemSymOrIDEnc"], "itemSymOrID", "itemSymEnc", "itemSymOrIDEnc"],
[toRope(getName(d, nameNode, d.splitAfter)), result, comm, [rope(getName(d, nameNode, d.splitAfter)), result, comm,
itemIDRope, plainNameRope, plainSymbolRope, symbolOrIdRope, itemIDRope, plainNameRope, plainSymbolRope, symbolOrIdRope,
plainSymbolEncRope, symbolOrIdEncRope])) plainSymbolEncRope, symbolOrIdEncRope]))
@ -436,7 +436,7 @@ proc genJSONItem(d: PDoc, n, nameNode: PNode, k: TSymKind): JsonNode =
if not isVisible(nameNode): return if not isVisible(nameNode): return
var var
name = getName(d, nameNode) name = getName(d, nameNode)
comm = genRecComment(d, n).ropeToStr() comm = $genRecComment(d, n)
r: TSrcGen r: TSrcGen
initTokRender(r, n, {renderNoBody, renderNoComments, renderDocComments}) initTokRender(r, n, {renderNoBody, renderNoComments, renderDocComments})
@ -453,14 +453,14 @@ proc checkForFalse(n: PNode): bool =
proc traceDeps(d: PDoc, n: PNode) = proc traceDeps(d: PDoc, n: PNode) =
const k = skModule 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], dispA(d.section[k],
"<a class=\"reference external\" href=\"$1.html\">$1</a>", "<a class=\"reference external\" href=\"$1.html\">$1</a>",
"$1", [toRope(getModuleName(n))]) "$1", [rope(getModuleName(n))])
proc generateDoc*(d: PDoc, n: PNode) = proc generateDoc*(d: PDoc, n: PNode) =
case n.kind case n.kind
of nkCommentStmt: app(d.modDesc, genComment(d, n)) of nkCommentStmt: add(d.modDesc, genComment(d, n))
of nkProcDef: of nkProcDef:
when useEffectSystem: documentRaises(n) when useEffectSystem: documentRaises(n)
genItem(d, n, n.sons[namePos], skProc) genItem(d, n, n.sons[namePos], skProc)
@ -540,28 +540,28 @@ proc genSection(d: PDoc, kind: TSymKind) =
"Iterators", "Iterators", "Converters", "Macros", "Templates" "Iterators", "Iterators", "Converters", "Macros", "Templates"
] ]
if d.section[kind] == nil: return if d.section[kind] == nil: return
var title = sectionNames[kind].toRope var title = sectionNames[kind].rope
d.section[kind] = ropeFormatNamedVars(getConfigVar("doc.section"), [ d.section[kind] = ropeFormatNamedVars(getConfigVar("doc.section"), [
"sectionid", "sectionTitle", "sectionTitleID", "content"], [ "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"), [ d.toc[kind] = ropeFormatNamedVars(getConfigVar("doc.section.toc"), [
"sectionid", "sectionTitle", "sectionTitleID", "content"], [ "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 var
code, content: PRope code, content: Rope
title = "" title = ""
var j = 0 var j = 0
var tmp = "" var tmp = ""
renderTocEntries(d[], j, 1, tmp) renderTocEntries(d[], j, 1, tmp)
var toc = tmp.toRope var toc = tmp.rope
for i in countup(low(TSymKind), high(TSymKind)): for i in countup(low(TSymKind), high(TSymKind)):
genSection(d, i) genSection(d, i)
app(toc, d.toc[i]) add(toc, d.toc[i])
if toc != nil: if toc != nil:
toc = ropeFormatNamedVars(getConfigVar("doc.toc"), ["content"], [toc]) 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. # Extract the title. Non API modules generate an entry in the index table.
if d.meta[metaTitle].len != 0: if d.meta[metaTitle].len != 0:
@ -574,16 +574,16 @@ proc genOutFile(d: PDoc): PRope =
let bodyname = if d.hasToc: "doc.body_toc" else: "doc.body_no_toc" let bodyname = if d.hasToc: "doc.body_toc" else: "doc.body_no_toc"
content = ropeFormatNamedVars(getConfigVar(bodyname), ["title", content = ropeFormatNamedVars(getConfigVar(bodyname), ["title",
"tableofcontents", "moduledesc", "date", "time", "content"], "tableofcontents", "moduledesc", "date", "time", "content"],
[title.toRope, toc, d.modDesc, toRope(getDateStr()), [title.rope, toc, d.modDesc, rope(getDateStr()),
toRope(getClockStr()), code]) rope(getClockStr()), code])
if optCompileOnly notin gGlobalOptions: if optCompileOnly notin gGlobalOptions:
# XXX what is this hack doing here? 'optCompileOnly' means raw output!? # XXX what is this hack doing here? 'optCompileOnly' means raw output!?
code = ropeFormatNamedVars(getConfigVar("doc.file"), ["title", code = ropeFormatNamedVars(getConfigVar("doc.file"), ["title",
"tableofcontents", "moduledesc", "date", "time", "tableofcontents", "moduledesc", "date", "time",
"content", "author", "version", "analytics"], "content", "author", "version", "analytics"],
[title.toRope, toc, d.modDesc, toRope(getDateStr()), [title.rope, toc, d.modDesc, rope(getDateStr()),
toRope(getClockStr()), content, d.meta[metaAuthor].toRope, rope(getClockStr()), content, d.meta[metaAuthor].rope,
d.meta[metaVersion].toRope, d.analytics.toRope]) d.meta[metaVersion].rope, d.analytics.rope])
else: else:
code = content code = content
result = code result = code
@ -618,7 +618,7 @@ proc commandRstAux(filename, outExt: string) =
#d.modDesc = newMutableRope(30_000) #d.modDesc = newMutableRope(30_000)
renderRstToOut(d[], rst, modDesc) renderRstToOut(d[], rst, modDesc)
#freezeMutableRope(d.modDesc) #freezeMutableRope(d.modDesc)
d.modDesc = toRope(modDesc) d.modDesc = rope(modDesc)
writeOutput(d, filename, outExt) writeOutput(d, filename, outExt)
generateIndex(d) generateIndex(d)
@ -635,7 +635,7 @@ proc commandJSON*() =
var d = newDocumentor(gProjectFull, options.gConfigVars) var d = newDocumentor(gProjectFull, options.gConfigVars)
d.hasToc = true d.hasToc = true
var json = generateJson(d, ast) var json = generateJson(d, ast)
var content = newRope(pretty(json)) var content = rope(pretty(json))
if optStdout in gGlobalOptions: if optStdout in gGlobalOptions:
writeRope(stdout, content) writeRope(stdout, content)
@ -644,12 +644,12 @@ proc commandJSON*() =
writeRope(content, getOutFile(gProjectFull, JsonExt), useWarning = false) writeRope(content, getOutFile(gProjectFull, JsonExt), useWarning = false)
proc commandBuildIndex*() = proc commandBuildIndex*() =
var content = mergeIndexes(gProjectFull).toRope var content = mergeIndexes(gProjectFull).rope
let code = ropeFormatNamedVars(getConfigVar("doc.file"), ["title", let code = ropeFormatNamedVars(getConfigVar("doc.file"), ["title",
"tableofcontents", "moduledesc", "date", "time", "tableofcontents", "moduledesc", "date", "time",
"content", "author", "version", "analytics"], "content", "author", "version", "analytics"],
["Index".toRope, nil, nil, toRope(getDateStr()), ["Index".rope, nil, nil, rope(getDateStr()),
toRope(getClockStr()), content, nil, nil, nil]) rope(getClockStr()), content, nil, nil, nil])
# no analytics because context is not available # no analytics because context is not available
writeRope(code, getOutFile("theindex", HtmlExt)) writeRope(code, getOutFile("theindex", HtmlExt))

View file

@ -26,8 +26,8 @@ type
hasAssume, # CC has __assume (Visual C extension) hasAssume, # CC has __assume (Visual C extension)
hasGcGuard, # CC supports GC_GUARD to keep stack roots hasGcGuard, # CC supports GC_GUARD to keep stack roots
hasGnuAsm, # CC's asm uses the absurd GNU assembler syntax hasGnuAsm, # CC's asm uses the absurd GNU assembler syntax
hasNakedDeclspec, # CC has __declspec(naked) hasDeclspec, # CC has __declspec(X)
hasNakedAttribute # CC has __attribute__((naked)) hasAttribute, # CC has __attribute__((X))
TInfoCCProps* = set[TInfoCCProp] TInfoCCProps* = set[TInfoCCProp]
TInfoCC* = tuple[ TInfoCC* = tuple[
name: string, # the short name of the compiler name: string, # the short name of the compiler
@ -85,7 +85,7 @@ compiler gcc:
structStmtFmt: "$1 $3 $2 ", # struct|union [packed] $name structStmtFmt: "$1 $3 $2 ", # struct|union [packed] $name
packedPragma: "__attribute__((__packed__))", packedPragma: "__attribute__((__packed__))",
props: {hasSwitchRange, hasComputedGoto, hasCpp, hasGcGuard, hasGnuAsm, props: {hasSwitchRange, hasComputedGoto, hasCpp, hasGcGuard, hasGnuAsm,
hasNakedAttribute}) hasAttribute})
# LLVM Frontend for GCC/G++ # LLVM Frontend for GCC/G++
compiler llvmGcc: compiler llvmGcc:
@ -127,7 +127,7 @@ compiler vcc:
asmStmtFrmt: "__asm{$n$1$n}$n", asmStmtFrmt: "__asm{$n$1$n}$n",
structStmtFmt: "$3$n$1 $2", structStmtFmt: "$3$n$1 $2",
packedPragma: "#pragma pack(1)", packedPragma: "#pragma pack(1)",
props: {hasCpp, hasAssume, hasNakedDeclspec}) props: {hasCpp, hasAssume, hasDeclspec})
# Intel C/C++ Compiler # Intel C/C++ Compiler
compiler icl: compiler icl:
@ -449,7 +449,7 @@ proc execExternalProgram*(cmd: string, prettyCmd = "") =
if execWithEcho(cmd, prettyCmd) != 0: if execWithEcho(cmd, prettyCmd) != 0:
rawMessage(errExecutionOfProgramFailed, "") rawMessage(errExecutionOfProgramFailed, "")
proc generateScript(projectFile: string, script: PRope) = proc generateScript(projectFile: string, script: Rope) =
let (dir, name, ext) = splitFile(projectFile) let (dir, name, ext) = splitFile(projectFile)
writeRope(script, dir / addFileExt("compile_" & name, writeRope(script, dir / addFileExt("compile_" & name,
platform.OS[targetOS].scriptExt)) platform.OS[targetOS].scriptExt))
@ -604,7 +604,7 @@ proc addExternalFileToCompile*(filename: string) =
if optForceFullMake in gGlobalOptions or externalFileChanged(filename): if optForceFullMake in gGlobalOptions or externalFileChanged(filename):
appendStr(externalToCompile, 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) = prettyCmds: var TStringSeq, isExternal: bool) =
var it = PStrEntry(list.head) var it = PStrEntry(list.head)
while it != nil: while it != nil:
@ -615,8 +615,8 @@ proc compileCFile(list: TLinkedList, script: var PRope, cmds: var TStringSeq,
let (dir, name, ext) = splitFile(it.data) let (dir, name, ext) = splitFile(it.data)
add(prettyCmds, "CC: " & name) add(prettyCmds, "CC: " & name)
if optGenScript in gGlobalOptions: if optGenScript in gGlobalOptions:
app(script, compileCmd) add(script, compileCmd)
app(script, tnl) add(script, tnl)
it = PStrEntry(it.next) it = PStrEntry(it.next)
proc callCCompiler*(projectfile: string) = proc callCCompiler*(projectfile: string) =
@ -627,7 +627,7 @@ proc callCCompiler*(projectfile: string) =
# generated # generated
fileCounter = 0 fileCounter = 0
var c = cCompiler var c = cCompiler
var script: PRope = nil var script: Rope = nil
var cmds: TStringSeq = @[] var cmds: TStringSeq = @[]
var prettyCmds: TStringSeq = @[] var prettyCmds: TStringSeq = @[]
let prettyCb = proc (idx: int) = let prettyCb = proc (idx: int) =
@ -668,7 +668,8 @@ proc callCCompiler*(projectfile: string) =
it = PStrEntry(it.next) it = PStrEntry(it.next)
if optGenStaticLib in gGlobalOptions: if optGenStaticLib in gGlobalOptions:
linkCmd = CC[c].buildLib % ["libfile", (libNameTmpl() % gProjectName), let name = splitFile(gProjectName).name
linkCmd = CC[c].buildLib % ["libfile", (libNameTmpl() % name),
"objfiles", objfiles] "objfiles", objfiles]
else: else:
var linkerExe = getConfigVar(c, ".linkerexe") var linkerExe = getConfigVar(c, ".linkerexe")
@ -710,30 +711,30 @@ proc callCCompiler*(projectfile: string) =
else: else:
linkCmd = "" linkCmd = ""
if optGenScript in gGlobalOptions: if optGenScript in gGlobalOptions:
app(script, linkCmd) add(script, linkCmd)
app(script, tnl) add(script, tnl)
generateScript(projectfile, script) generateScript(projectfile, script)
proc genMappingFiles(list: TLinkedList): PRope = proc genMappingFiles(list: TLinkedList): Rope =
var it = PStrEntry(list.head) var it = PStrEntry(list.head)
while it != nil: 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) it = PStrEntry(it.next)
proc writeMapping*(gSymbolMapping: PRope) = proc writeMapping*(gSymbolMapping: Rope) =
if optGenMapping notin gGlobalOptions: return if optGenMapping notin gGlobalOptions: return
var code = toRope("[C_Files]\n") var code = rope("[C_Files]\n")
app(code, genMappingFiles(toCompile)) add(code, genMappingFiles(toCompile))
app(code, genMappingFiles(externalToCompile)) add(code, genMappingFiles(externalToCompile))
app(code, "\n[C_Compiler]\nFlags=") add(code, "\n[C_Compiler]\nFlags=")
app(code, strutils.escape(getCompileOptions())) add(code, strutils.escape(getCompileOptions()))
app(code, "\n[Linker]\nFlags=") add(code, "\n[Linker]\nFlags=")
app(code, strutils.escape(getLinkOptions() & " " & add(code, strutils.escape(getLinkOptions() & " " &
getConfigVar(cCompiler, ".options.linker"))) getConfigVar(cCompiler, ".options.linker")))
app(code, "\n[Environment]\nlibpath=") add(code, "\n[Environment]\nlibpath=")
app(code, strutils.escape(libpath)) 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")) writeRope(code, joinPath(gProjectPath, "mapping.txt"))

View file

@ -22,7 +22,8 @@ const
someLt = {mLtI, mLtI64, mLtF64, mLtU, mLtU64, mLtEnum, someLt = {mLtI, mLtI64, mLtF64, mLtU, mLtU64, mLtEnum,
mLtCh, mLtB, mLtPtr, mLtStr} mLtCh, mLtB, mLtPtr, mLtStr}
someLen = {mLengthOpenArray, mLengthStr, mLengthArray, mLengthSeq} someLen = {mLengthOpenArray, mLengthStr, mLengthArray, mLengthSeq,
mXLenStr, mXLenSeq}
someIn = {mInRange, mInSet} someIn = {mInRange, mInSet}
@ -34,8 +35,8 @@ const
someMul = {mMulI, mMulI64, mMulF64} someMul = {mMulI, mMulI64, mMulF64}
someDiv = {mDivI, mDivI64, mDivF64} someDiv = {mDivI, mDivI64, mDivF64}
someMod = {mModI, mModI64} someMod = {mModI, mModI64}
someMax = {mMaxI, mMaxI64, mMaxF64} someMax = {mMaxI, mMaxF64}
someMin = {mMinI, mMinI64, mMinF64} someMin = {mMinI, mMinF64}
proc isValue(n: PNode): bool = n.kind in {nkCharLit..nkNilLit} proc isValue(n: PNode): bool = n.kind in {nkCharLit..nkNilLit}
proc isLocation(n: PNode): bool = not n.isValue proc isLocation(n: PNode): bool = not n.isValue
@ -122,7 +123,7 @@ proc neg(n: PNode): PNode =
let eAsNode = newIntNode(nkIntLit, e.sym.position) let eAsNode = newIntNode(nkIntLit, e.sym.position)
if not inSet(n.sons[1], eAsNode): s.add eAsNode if not inSet(n.sons[1], eAsNode): s.add eAsNode
result.sons[1] = s 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]) result.sons[1] = complement(n.sons[1])
else: else:
# not ({2, 3, 4}.contains(x)) x != 2 and x != 3 and x != 4 # not ({2, 3, 4}.contains(x)) x != 2 and x != 3 and x != 4
@ -907,5 +908,5 @@ proc buildProperFieldCheck(access, check: PNode): PNode =
proc checkFieldAccess*(m: TModel, n: PNode) = proc checkFieldAccess*(m: TModel, n: PNode) =
for i in 1..n.len-1: for i in 1..n.len-1:
let check = buildProperFieldCheck(n.sons[0], n.sons[i]) 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 message(n.info, warnProveField, renderTree(n.sons[0])); break

View file

@ -47,7 +47,7 @@ Start: "doc/overview.html"
[Other] [Other]
Files: "readme.txt;install.txt;contributors.txt;copying.txt" Files: "readme.txt;install.txt;contributors.txt;copying.txt"
Files: "configure;makefile" Files: "makefile"
Files: "*.ini" Files: "*.ini"
Files: "koch.nim" Files: "koch.nim"
@ -62,7 +62,7 @@ Files: "icons/koch_icon.o"
Files: "compiler/readme.txt" Files: "compiler/readme.txt"
Files: "compiler/installer.ini" Files: "compiler/installer.ini"
Files: "compiler/nim.nimrod.cfg" Files: "compiler/nim.nim.cfg"
Files: "compiler/*.nim" Files: "compiler/*.nim"
Files: "doc/*.txt" Files: "doc/*.txt"
Files: "doc/manual/*.txt" Files: "doc/manual/*.txt"
@ -70,6 +70,10 @@ Files: "doc/*.nim"
Files: "doc/*.cfg" Files: "doc/*.cfg"
Files: "compiler/nimfix/*.nim" Files: "compiler/nimfix/*.nim"
Files: "compiler/nimfix/*.cfg" Files: "compiler/nimfix/*.cfg"
Files: "compiler/nimsuggest/*.nim"
Files: "compiler/nimsuggest/*.cfg"
Files: "compiler/plugins/locals/*.nim"
Files: "compiler/plugins/active.nim"
Files: "tools/*.nim" Files: "tools/*.nim"
Files: "tools/*.cfg" Files: "tools/*.cfg"
Files: "tools/*.tmpl" Files: "tools/*.tmpl"
@ -97,13 +101,8 @@ Files: "lib/pure/concurrency/*.cfg"
Files: "lib/impure/*.nim" Files: "lib/impure/*.nim"
Files: "lib/wrappers/*.nim" Files: "lib/wrappers/*.nim"
Files: "lib/wrappers/cairo/*.nim"
Files: "lib/wrappers/gtk/*.nim"
Files: "lib/wrappers/lua/*.nim"
Files: "lib/wrappers/opengl/*.nim"
Files: "lib/wrappers/readline/*.nim" Files: "lib/wrappers/readline/*.nim"
Files: "lib/wrappers/sdl/*.nim" Files: "lib/wrappers/sdl/*.nim"
Files: "lib/wrappers/x11/*.nim"
Files: "lib/wrappers/zip/*.nim" Files: "lib/wrappers/zip/*.nim"
Files: "lib/wrappers/zip/libzip_all.c" Files: "lib/wrappers/zip/libzip_all.c"
@ -115,8 +114,6 @@ Files: "lib/packages/docutils/*.nim"
[Other] [Other]
Files: "examples/*.nim" Files: "examples/*.nim"
Files: "examples/gtk/*.nim"
Files: "examples/0mq/*.nim"
Files: "examples/c++iface/*.nim" Files: "examples/c++iface/*.nim"
Files: "examples/objciface/*.nim" Files: "examples/objciface/*.nim"
Files: "examples/cross_calculator/" Files: "examples/cross_calculator/"
@ -126,12 +123,109 @@ Files: "examples/*.txt"
Files: "examples/*.cfg" Files: "examples/*.cfg"
Files: "examples/*.tmpl" 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] [Windows]
Files: "bin/nim.exe" Files: "bin/nim.exe"
Files: "bin/nim_debug.exe"
Files: "bin/c2nim.exe" Files: "bin/c2nim.exe"
Files: "bin/nimgrep.exe" Files: "bin/nimgrep.exe"
Files: "bin/nimsuggest.exe"
Files: "bin/nimble.exe"
Files: "bin/*.dll"
Files: "dist/*.dll" Files: "dist/*.dll"
Files: "koch.exe" Files: "koch.exe"
@ -142,7 +236,7 @@ BinPath: r"bin;dist\mingw\bin;dist"
; Section | dir | zipFile | size hint (in KB) | url | exe start menu entry ; 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|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"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-0.3.0\bin\aporia.exe"
; for now only NSIS supports optional downloads ; for now only NSIS supports optional downloads
[UnixBin] [UnixBin]

File diff suppressed because it is too large Load diff

View file

@ -9,10 +9,10 @@
## Type info generation for the JS backend. ## Type info generation for the JS backend.
proc genTypeInfo(p: PProc, typ: PType): PRope proc genTypeInfo(p: PProc, typ: PType): Rope
proc genObjectFields(p: PProc, typ: PType, n: PNode): PRope = proc genObjectFields(p: PProc, typ: PType, n: PNode): Rope =
var var
s, u: PRope s, u: Rope
length: int length: int
field: PSym field: PSym
b: PNode b: PNode
@ -25,16 +25,16 @@ proc genObjectFields(p: PProc, typ: PType, n: PNode): PRope =
else: else:
s = nil s = nil
for i in countup(0, length - 1): for i in countup(0, length - 1):
if i > 0: app(s, ", " & tnl) if i > 0: add(s, ", " & tnl)
app(s, genObjectFields(p, typ, n.sons[i])) add(s, genObjectFields(p, typ, n.sons[i]))
result = ropef("{kind: 2, len: $1, offset: 0, " & result = ("{kind: 2, len: $1, offset: 0, " &
"typ: null, name: null, sons: [$2]}", [toRope(length), s]) "typ: null, name: null, sons: [$2]}") % [rope(length), s]
of nkSym: of nkSym:
field = n.sym field = n.sym
s = genTypeInfo(p, field.typ) s = genTypeInfo(p, field.typ)
result = ropef("{kind: 1, offset: \"$1\", len: 0, " & result = ("{kind: 1, offset: \"$1\", len: 0, " &
"typ: $2, name: $3, sons: null}", "typ: $2, name: $3, sons: null}") %
[mangleName(field), s, makeJSString(field.name.s)]) [mangleName(field), s, makeJSString(field.name.s)]
of nkRecCase: of nkRecCase:
length = sonsLen(n) length = sonsLen(n)
if (n.sons[0].kind != nkSym): internalError(n.info, "genObjectFields") 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: if sonsLen(b) < 2:
internalError(b.info, "genObjectFields; nkOfBranch broken") internalError(b.info, "genObjectFields; nkOfBranch broken")
for j in countup(0, sonsLen(b) - 2): for j in countup(0, sonsLen(b) - 2):
if u != nil: app(u, ", ") if u != nil: add(u, ", ")
if b.sons[j].kind == nkRange: if b.sons[j].kind == nkRange:
appf(u, "[$1, $2]", [toRope(getOrdValue(b.sons[j].sons[0])), addf(u, "[$1, $2]", [rope(getOrdValue(b.sons[j].sons[0])),
toRope(getOrdValue(b.sons[j].sons[1]))]) rope(getOrdValue(b.sons[j].sons[1]))])
else: else:
app(u, toRope(getOrdValue(b.sons[j]))) add(u, rope(getOrdValue(b.sons[j])))
of nkElse: of nkElse:
u = toRope(lengthOrd(field.typ)) u = rope(lengthOrd(field.typ))
else: internalError(n.info, "genObjectFields(nkRecCase)") else: internalError(n.info, "genObjectFields(nkRecCase)")
if result != nil: app(result, ", " & tnl) if result != nil: add(result, ", " & tnl)
appf(result, "[SetConstr($1), $2]", addf(result, "[SetConstr($1), $2]",
[u, genObjectFields(p, typ, lastSon(b))]) [u, genObjectFields(p, typ, lastSon(b))])
result = ropef("{kind: 3, offset: \"$1\", len: $3, " & result = ("{kind: 3, offset: \"$1\", len: $3, " &
"typ: $2, name: $4, sons: [$5]}", [mangleName(field), s, "typ: $2, name: $4, sons: [$5]}") % [mangleName(field), s,
toRope(lengthOrd(field.typ)), makeJSString(field.name.s), result]) rope(lengthOrd(field.typ)), makeJSString(field.name.s), result]
else: internalError(n.info, "genObjectFields") else: internalError(n.info, "genObjectFields")
proc genObjectInfo(p: PProc, typ: PType, name: PRope) = proc genObjectInfo(p: PProc, typ: PType, name: Rope) =
var s = ropef("var $1 = {size: 0, kind: $2, base: null, node: null, " & var s = ("var $1 = {size: 0, kind: $2, base: null, node: null, " &
"finalizer: null};$n", [name, toRope(ord(typ.kind))]) "finalizer: null};$n") % [name, rope(ord(typ.kind))]
prepend(p.g.typeInfo, s) prepend(p.g.typeInfo, s)
appf(p.g.typeInfo, "var NNI$1 = $2;$n", addf(p.g.typeInfo, "var NNI$1 = $2;$n",
[toRope(typ.id), genObjectFields(p, typ, typ.n)]) [rope(typ.id), genObjectFields(p, typ, typ.n)])
appf(p.g.typeInfo, "$1.node = NNI$2;$n", [name, toRope(typ.id)]) addf(p.g.typeInfo, "$1.node = NNI$2;$n", [name, rope(typ.id)])
if (typ.kind == tyObject) and (typ.sons[0] != nil): 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])]) [name, genTypeInfo(p, typ.sons[0])])
proc genTupleFields(p: PProc, typ: PType): PRope = proc genTupleFields(p: PProc, typ: PType): Rope =
var s: PRope = nil var s: Rope = nil
for i in 0 .. <typ.len: for i in 0 .. <typ.len:
if i > 0: app(s, ", " & tnl) if i > 0: add(s, ", " & tnl)
s.appf("{kind: 1, offset: \"Field$1\", len: 0, " & s.addf("{kind: 1, offset: \"Field$1\", len: 0, " &
"typ: $2, name: \"Field$1\", sons: null}", "typ: $2, name: \"Field$1\", sons: null}",
[i.toRope, genTypeInfo(p, typ.sons[i])]) [i.rope, genTypeInfo(p, typ.sons[i])])
result = ropef("{kind: 2, len: $1, offset: 0, " & result = ("{kind: 2, len: $1, offset: 0, " &
"typ: null, name: null, sons: [$2]}", [toRope(typ.len), s]) "typ: null, name: null, sons: [$2]}") % [rope(typ.len), s]
proc genTupleInfo(p: PProc, typ: PType, name: PRope) = proc genTupleInfo(p: PProc, typ: PType, name: Rope) =
var s = ropef("var $1 = {size: 0, kind: $2, base: null, node: null, " & var s = ("var $1 = {size: 0, kind: $2, base: null, node: null, " &
"finalizer: null};$n", [name, toRope(ord(typ.kind))]) "finalizer: null};$n") % [name, rope(ord(typ.kind))]
prepend(p.g.typeInfo, s) prepend(p.g.typeInfo, s)
appf(p.g.typeInfo, "var NNI$1 = $2;$n", addf(p.g.typeInfo, "var NNI$1 = $2;$n",
[toRope(typ.id), genTupleFields(p, typ)]) [rope(typ.id), genTupleFields(p, typ)])
appf(p.g.typeInfo, "$1.node = NNI$2;$n", [name, toRope(typ.id)]) 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) let length = sonsLen(typ.n)
var s: PRope = nil var s: Rope = nil
for i in countup(0, length - 1): for i in countup(0, length - 1):
if (typ.n.sons[i].kind != nkSym): internalError(typ.n.info, "genEnumInfo") if (typ.n.sons[i].kind != nkSym): internalError(typ.n.info, "genEnumInfo")
let field = typ.n.sons[i].sym 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 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}", addf(s, "{kind: 1, offset: $1, typ: $2, name: $3, len: 0, sons: null}",
[toRope(field.position), name, makeJSString(extName)]) [rope(field.position), name, makeJSString(extName)])
var n = ropef("var NNI$1 = {kind: 2, offset: 0, typ: null, " & var n = ("var NNI$1 = {kind: 2, offset: 0, typ: null, " &
"name: null, len: $2, sons: [$3]};$n", [toRope(typ.id), toRope(length), s]) "name: null, len: $2, sons: [$3]};$n") % [rope(typ.id), rope(length), s]
s = ropef("var $1 = {size: 0, kind: $2, base: null, node: null, " & s = ("var $1 = {size: 0, kind: $2, base: null, node: null, " &
"finalizer: null};$n", [name, toRope(ord(typ.kind))]) "finalizer: null};$n") % [name, rope(ord(typ.kind))]
prepend(p.g.typeInfo, s) prepend(p.g.typeInfo, s)
app(p.g.typeInfo, n) add(p.g.typeInfo, n)
appf(p.g.typeInfo, "$1.node = NNI$2;$n", [name, toRope(typ.id)]) addf(p.g.typeInfo, "$1.node = NNI$2;$n", [name, rope(typ.id)])
if typ.sons[0] != nil: 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])]) [name, genTypeInfo(p, typ.sons[0])])
proc genTypeInfo(p: PProc, typ: PType): PRope = proc genTypeInfo(p: PProc, typ: PType): Rope =
var t = typ var t = typ
if t.kind == tyGenericInst: t = lastSon(t) 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 if containsOrIncl(p.g.typeInfoGenerated, t.id): return
case t.kind case t.kind
of tyDistinct: of tyDistinct:
result = genTypeInfo(p, typ.sons[0]) result = genTypeInfo(p, typ.sons[0])
of tyPointer, tyProc, tyBool, tyChar, tyCString, tyString, tyInt..tyUInt64: of tyPointer, tyProc, tyBool, tyChar, tyCString, tyString, tyInt..tyUInt64:
var s = ropef( var s =
"var $1 = {size: 0,kind: $2,base: null,node: null,finalizer: null};$n", "var $1 = {size: 0,kind: $2,base: null,node: null,finalizer: null};$n" %
[result, toRope(ord(t.kind))]) [result, rope(ord(t.kind))]
prepend(p.g.typeInfo, s) prepend(p.g.typeInfo, s)
of tyVar, tyRef, tyPtr, tySequence, tyRange, tySet: of tyVar, tyRef, tyPtr, tySequence, tyRange, tySet:
var s = ropef( var s =
"var $1 = {size: 0,kind: $2,base: null,node: null,finalizer: null};$n", "var $1 = {size: 0,kind: $2,base: null,node: null,finalizer: null};$n" %
[result, toRope(ord(t.kind))]) [result, rope(ord(t.kind))]
prepend(p.g.typeInfo, s) 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)]) [result, genTypeInfo(p, typ.lastSon)])
of tyArrayConstr, tyArray: of tyArrayConstr, tyArray:
var s = ropef( var s =
"var $1 = {size: 0,kind: $2,base: null,node: null,finalizer: null};$n", "var $1 = {size: 0,kind: $2,base: null,node: null,finalizer: null};$n" %
[result, toRope(ord(t.kind))]) [result, rope(ord(t.kind))]
prepend(p.g.typeInfo, s) 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])]) [result, genTypeInfo(p, typ.sons[1])])
of tyEnum: genEnumInfo(p, t, result) of tyEnum: genEnumInfo(p, t, result)
of tyObject: genObjectInfo(p, t, result) of tyObject: genObjectInfo(p, t, result)

View file

@ -61,7 +61,7 @@ type
tkComma, tkSemiColon, tkComma, tkSemiColon,
tkColon, tkColonColon, tkEquals, tkDot, tkDotDot, tkColon, tkColonColon, tkEquals, tkDot, tkDotDot,
tkOpr, tkComment, tkAccent, tkOpr, tkComment, tkAccent,
tkSpaces, tkInfixOpr, tkPrefixOpr, tkPostfixOpr, tkSpaces, tkInfixOpr, tkPrefixOpr, tkPostfixOpr
TTokTypes* = set[TTokType] TTokTypes* = set[TTokType]
@ -131,24 +131,10 @@ type
var gLinesCompiled*: int # all lines that have been compiled 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.} = proc getLineInfo*(L: TLexer, tok: TToken): TLineInfo {.inline.} =
newLineInfo(L.fileIdx, tok.line, tok.col) newLineInfo(L.fileIdx, tok.line, tok.col)
proc closeLexer*(lex: var TLexer) proc isKeyword*(kind: TTokType): bool =
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 =
result = (kind >= tokKeywordLow) and (kind <= tokKeywordHigh) result = (kind >= tokKeywordLow) and (kind <= tokKeywordHigh)
proc isNimIdentifier*(s: string): bool = proc isNimIdentifier*(s: string): bool =
@ -206,14 +192,17 @@ proc fillToken(L: var TToken) =
L.base = base10 L.base = base10
L.ident = dummyIdent L.ident = dummyIdent
proc openLexer(lex: var TLexer, fileIdx: int32, inputstream: PLLStream) = proc openLexer*(lex: var TLexer, fileIdx: int32, inputstream: PLLStream) =
openBaseLexer(lex, inputstream) openBaseLexer(lex, inputstream)
lex.fileIdx = fileidx lex.fileIdx = fileidx
lex.indentAhead = - 1 lex.indentAhead = - 1
lex.currLineIndent = 0 lex.currLineIndent = 0
inc(lex.lineNumber, inputstream.lineOffset) 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) inc(gLinesCompiled, lex.lineNumber)
closeBaseLexer(lex) closeBaseLexer(lex)
@ -229,9 +218,13 @@ proc dispMessage(L: TLexer; info: TLineInfo; msg: TMsgKind; arg: string) =
else: else:
L.errorHandler(info, msg, arg) 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) 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 = "") = proc lexMessagePos(L: var TLexer, msg: TMsgKind, pos: int, arg = "") =
var info = newLineInfo(L.fileIdx, L.lineNumber, pos - L.lineStart) var info = newLineInfo(L.fileIdx, L.lineNumber, pos - L.lineStart)
L.dispMessage(info, msg, arg) L.dispMessage(info, msg, arg)
@ -785,7 +778,7 @@ proc skip(L: var TLexer, tok: var TToken) =
break # EndOfFile also leaves the loop break # EndOfFile also leaves the loop
L.bufpos = pos L.bufpos = pos
proc rawGetTok(L: var TLexer, tok: var TToken) = proc rawGetTok*(L: var TLexer, tok: var TToken) =
fillToken(tok) fillToken(tok)
if L.indentAhead >= 0: if L.indentAhead >= 0:
tok.indent = L.indentAhead tok.indent = L.indentAhead
@ -874,6 +867,15 @@ proc rawGetTok(L: var TLexer, tok: var TToken) =
of '`': of '`':
tok.tokType = tkAccent tok.tokType = tkAccent
inc(L.bufpos) 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 '\"': of '\"':
# check for extended raw string literal: # check for extended raw string literal:
var rawMode = L.bufpos > 0 and L.buf[L.bufpos-1] in SymChars var rawMode = L.bufpos > 0 and L.buf[L.bufpos-1] in SymChars

View file

@ -15,6 +15,10 @@ const
import ast, astalgo, types, idents, magicsys, msgs, options import ast, astalgo, types, idents, magicsys, msgs, options
from trees import getMagic 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 = proc newTupleAccess*(tup: PNode, i: int): PNode =
result = newNodeIT(nkBracketExpr, tup.info, tup.typ.skipTypes( result = newNodeIT(nkBracketExpr, tup.info, tup.typ.skipTypes(
abstractInst).sons[i]) abstractInst).sons[i])
@ -382,11 +386,11 @@ proc getRoot*(n: PNode): PSym =
if getMagic(n) == mSlice: result = getRoot(n.sons[1]) if getMagic(n) == mSlice: result = getRoot(n.sons[1])
else: discard else: discard
proc newIntLit(value: BiggestInt): PNode = proc newIntLit*(value: BiggestInt): PNode =
result = nkIntLit.newIntNode(value) result = nkIntLit.newIntNode(value)
result.typ = getSysType(tyInt) result.typ = getSysType(tyInt)
proc genHigh(n: PNode): PNode = proc genHigh*(n: PNode): PNode =
if skipTypes(n.typ, abstractVar).kind in {tyArrayConstr, tyArray}: if skipTypes(n.typ, abstractVar).kind in {tyArrayConstr, tyArray}:
result = newIntLit(lastOrd(skipTypes(n.typ, abstractVar))) result = newIntLit(lastOrd(skipTypes(n.typ, abstractVar)))
else: else:

View file

@ -54,6 +54,7 @@ proc commandDoc2 =
finishDoc2Pass(gProjectName) finishDoc2Pass(gProjectName)
proc commandCompileToC = proc commandCompileToC =
extccomp.initVars()
semanticPasses() semanticPasses()
registerPass(cgenPass) registerPass(cgenPass)
rodPass() rodPass()

View file

@ -387,7 +387,7 @@ const
warnProveField: "cannot prove that field '$1' is accessible [ProveField]", warnProveField: "cannot prove that field '$1' is accessible [ProveField]",
warnProveIndex: "cannot prove index '$1' is valid [ProveIndex]", warnProveIndex: "cannot prove index '$1' is valid [ProveIndex]",
warnGcUnsafe: "not GC-safe: '$1' [GcUnsafe]", 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]", warnUninit: "'$1' might not have been initialized [Uninit]",
warnGcMem: "'$1' uses GC'ed memory [GcMem]", warnGcMem: "'$1' uses GC'ed memory [GcMem]",
warnDestructor: "usage of a type with a destructor in a non destructible context. This will become a compile time error in the future. [Destructor]", warnDestructor: "usage of a type with a destructor in a non destructible context. This will become a compile time error in the future. [Destructor]",
@ -449,10 +449,10 @@ type
fullPath: string # This is a canonical full filesystem path fullPath: string # This is a canonical full filesystem path
projPath*: string # This is relative to the project's root projPath*: string # This is relative to the project's root
shortName*: string # short name of the module shortName*: string # short name of the module
quotedName*: PRope # cached quoted short name for codegen quotedName*: Rope # cached quoted short name for codegen
# purposes # 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 # used for better error messages and
# embedding the original source in the # embedding the original source in the
# generated code # generated code
@ -493,24 +493,20 @@ proc toCChar*(c: char): string =
of '\'', '\"', '\\': result = '\\' & c of '\'', '\"', '\\': result = '\\' & c
else: result = $(c) else: result = $(c)
proc makeCString*(s: string): PRope = proc makeCString*(s: string): Rope =
# BUGFIX: We have to split long strings into many ropes. Otherwise
# this could trigger an internalError(). See the ropes module for
# further information.
const const
MaxLineLength = 64 MaxLineLength = 64
result = nil result = nil
var res = "\"" var res = newStringOfCap(int(s.len.toFloat * 1.1) + 1)
add(res, "\"")
for i in countup(0, len(s) - 1): for i in countup(0, len(s) - 1):
if (i + 1) mod MaxLineLength == 0: if (i + 1) mod MaxLineLength == 0:
add(res, '\"') add(res, '\"')
add(res, tnl) add(res, tnl)
app(result, toRope(res)) # reset: add(res, '\"')
setLen(res, 1)
res[0] = '\"'
add(res, toCChar(s[i])) add(res, toCChar(s[i]))
add(res, '\"') add(res, '\"')
app(result, toRope(res)) add(result, rope(res))
proc newFileInfo(fullPath, projPath: string): TFileInfo = proc newFileInfo(fullPath, projPath: string): TFileInfo =
@ -568,7 +564,7 @@ var gCodegenLineInfo* = newLineInfo(int32(1), 1, 1)
proc raiseRecoverableError*(msg: string) {.noinline, noreturn.} = proc raiseRecoverableError*(msg: string) {.noinline, noreturn.} =
raise newException(ERecoverableError, msg) raise newException(ERecoverableError, msg)
proc sourceLine*(i: TLineInfo): PRope proc sourceLine*(i: TLineInfo): Rope
var var
gNotes*: TNoteKinds = {low(TNoteKind)..high(TNoteKind)} - gNotes*: TNoteKinds = {low(TNoteKind)..high(TNoteKind)} -
@ -742,7 +738,7 @@ proc writeContext(lastinfo: TLineInfo) =
if msgContext[i] != lastinfo and msgContext[i] != info: if msgContext[i] != lastinfo and msgContext[i] != info:
msgWriteln(PosContextFormat % [toMsgFilename(msgContext[i]), msgWriteln(PosContextFormat % [toMsgFilename(msgContext[i]),
coordToStr(msgContext[i].line), coordToStr(msgContext[i].line),
coordToStr(msgContext[i].col), coordToStr(msgContext[i].col+1),
getMessageStr(errInstantiationFrom, "")]) getMessageStr(errInstantiationFrom, "")])
info = msgContext[i] info = msgContext[i]
@ -776,7 +772,7 @@ proc rawMessage*(msg: TMsgKind, arg: string) =
proc writeSurroundingSrc(info: TLineInfo) = proc writeSurroundingSrc(info: TLineInfo) =
const indent = " " const indent = " "
msgWriteln(indent & info.sourceLine.ropeToStr) msgWriteln(indent & $info.sourceLine)
msgWriteln(indent & spaces(info.col) & '^') msgWriteln(indent & spaces(info.col) & '^')
proc formatMsg*(info: TLineInfo, msg: TMsgKind, arg: string): string = proc formatMsg*(info: TLineInfo, msg: TMsgKind, arg: string): string =
@ -785,7 +781,7 @@ proc formatMsg*(info: TLineInfo, msg: TMsgKind, arg: string): string =
of hintMin..hintMax: PosHintFormat of hintMin..hintMax: PosHintFormat
else: PosErrorFormat else: PosErrorFormat
result = frmt % [toMsgFilename(info), coordToStr(info.line), 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, proc liMessage(info: TLineInfo, msg: TMsgKind, arg: string,
eh: TErrorHandling) = eh: TErrorHandling) =
@ -808,8 +804,11 @@ proc liMessage(info: TLineInfo, msg: TMsgKind, arg: string,
ignoreMsg = optHints notin gOptions or msg notin gNotes ignoreMsg = optHints notin gOptions or msg notin gNotes
frmt = PosHintFormat frmt = PosHintFormat
inc(gHintCounter) 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), let s = frmt % [toMsgFilename(info), coordToStr(info.line),
coordToStr(info.col), getMessageStr(msg, arg)] coordToStr(info.col+1), getMessageStr(msg, arg)]
if not ignoreMsg and not ignoreMsgBecauseOfIdeTools(msg): if not ignoreMsg and not ignoreMsgBecauseOfIdeTools(msg):
msgWriteln(s) msgWriteln(s)
if optPrintSurroundingSrc and msg in errMin..errMax: if optPrintSurroundingSrc and msg in errMin..errMax:
@ -831,6 +830,9 @@ proc localError*(info: TLineInfo, msg: TMsgKind, arg = "") =
proc localError*(info: TLineInfo, arg: string) = proc localError*(info: TLineInfo, arg: string) =
liMessage(info, errGenerated, arg, doNothing) 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 = "") = proc message*(info: TLineInfo, msg: TMsgKind, arg = "") =
liMessage(info, msg, arg, doNothing) liMessage(info, msg, arg, doNothing)
@ -852,9 +854,9 @@ template internalAssert*(e: bool): stmt =
if not e: internalError($instantiationInfo()) if not e: internalError($instantiationInfo())
proc addSourceLine*(fileIdx: int32, line: string) = 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 i.fileIndex < 0: return nil
if not optPreserveOrigSource and fileInfos[i.fileIndex].lines.len == 0: if not optPreserveOrigSource and fileInfos[i.fileIndex].lines.len == 0:
@ -869,16 +871,14 @@ proc sourceLine*(i: TLineInfo): PRope =
result = fileInfos[i.fileIndex].lines[i.line-1] result = fileInfos[i.fileIndex].lines[i.line-1]
proc quotedFilename*(i: TLineInfo): PRope = proc quotedFilename*(i: TLineInfo): Rope =
internalAssert i.fileIndex >= 0 internalAssert i.fileIndex >= 0
result = fileInfos[i.fileIndex].quotedName 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 case err
of rInvalidFormatStr: of rInvalidFormatStr:
internalError("ropes: invalid format string: " & msg) internalError("ropes: invalid format string: " & msg)
of rTokenTooLong:
internalError("ropes: token too long: " & msg)
of rCannotOpenFile: of rCannotOpenFile:
rawMessage(if useWarning: warnCannotOpenFile else: errCannotOpenFile, msg) rawMessage(if useWarning: warnCannotOpenFile else: errCannotOpenFile, msg)

View file

@ -5,7 +5,7 @@ hint[XDeclaredButNotUsed]:off
path:"$projectPath/.." path:"$projectPath/.."
path:"$lib/packages/docutils" path:"$lib/packages/docutils"
path:"$nim/compiler" path:"../../compiler"
define:useStdoutAsStdmsg define:useStdoutAsStdmsg
symbol:nimfix symbol:nimfix

View file

@ -82,7 +82,8 @@ proc action(cmd: string) =
if cmd[i] == ';': if cmd[i] == ';':
i = parseQuoted(cmd, dirtyfile, i+1) i = parseQuoted(cmd, dirtyfile, i+1)
i += skipWhile(cmd, seps, i) i += skipWhile(cmd, seps, i)
var line, col = -1 var line = -1
var col = 0
i += parseInt(cmd, line, i) i += parseInt(cmd, line, i)
i += skipWhile(cmd, seps, i) i += skipWhile(cmd, seps, i)
i += parseInt(cmd, col, i) i += parseInt(cmd, col, i)
@ -97,7 +98,7 @@ proc action(cmd: string) =
resetModule dirtyIdx resetModule dirtyIdx
if dirtyIdx != gProjectMainIdx: if dirtyIdx != gProjectMainIdx:
resetModule gProjectMainIdx resetModule gProjectMainIdx
gTrackPos = newLineInfo(dirtyIdx, line, col) gTrackPos = newLineInfo(dirtyIdx, line, col-1)
#echo dirtyfile, gDirtyBufferIdx, " project ", gProjectMainIdx #echo dirtyfile, gDirtyBufferIdx, " project ", gProjectMainIdx
gErrorCounter = 0 gErrorCounter = 0
if not isKnownFile: if not isKnownFile:

View file

@ -6,7 +6,7 @@ hint[XDeclaredButNotUsed]:off
path:"$projectPath/../.." path:"$projectPath/../.."
path:"$lib/packages/docutils" path:"$lib/packages/docutils"
path:"$nim/compiler" path:"../../compiler"
define:useStdoutAsStdmsg define:useStdoutAsStdmsg
define:nimsuggest define:nimsuggest

View file

@ -1,7 +1,7 @@
# #
# #
# The Nim Compiler # The Nim Compiler
# (c) Copyright 2013 Andreas Rumpf # (c) Copyright 2015 Andreas Rumpf
# #
# See the file "copying.txt", included in this # See the file "copying.txt", included in this
# distribution, for details about the copyright. # distribution, for details about the copyright.

View file

@ -190,6 +190,8 @@ proc isAssignable*(owner: PSym, n: PNode): TAssignableResult =
result = arLocalLValue result = arLocalLValue
else: else:
result = arLValue result = arLValue
elif n.sym.kind == skParam and n.sym.typ.kind == tyVar:
result = arLValue
elif n.sym.kind == skType: elif n.sym.kind == skType:
let t = n.sym.typ.skipTypes({tyTypeDesc}) let t = n.sym.typ.skipTypes({tyTypeDesc})
if t.kind == tyVar: result = arStrange if t.kind == tyVar: result = arStrange

View file

@ -91,7 +91,7 @@ proc closeParser(p: var TParser) =
proc parMessage(p: TParser, msg: TMsgKind, arg = "") = proc parMessage(p: TParser, msg: TMsgKind, arg = "") =
## Produce and emit the parser message `arg` to output. ## 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) = proc parMessage(p: TParser, msg: TMsgKind, tok: TToken) =
## Produce and emit a parser message to output about the token `tok` ## Produce and emit a parser message to output about the token `tok`
@ -154,7 +154,7 @@ proc eat(p: var TParser, tokType: TTokType) =
if p.tok.tokType == tokType: if p.tok.tokType == tokType:
getTok(p) getTok(p)
else: else:
lexMessage(p.lex, errTokenExpected, TokTypeToStr[tokType]) lexMessageTok(p.lex, errTokenExpected, p.tok, TokTypeToStr[tokType])
proc parLineInfo(p: TParser): TLineInfo = proc parLineInfo(p: TParser): TLineInfo =
## Retrieve the line information associated with the parser's current state. ## Retrieve the line information associated with the parser's current state.
@ -212,27 +212,29 @@ proc getPrecedence(tok: TToken, strongSpaces: bool): int =
let relevantChar = tok.ident.s[0] let relevantChar = tok.ident.s[0]
# arrow like? # 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) = 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 case relevantChar
of '$', '^': considerAsgn(10) of '$', '^': considerAsgn(10)
of '*', '%', '/', '\\': considerAsgn(9) of '*', '%', '/', '\\': considerAsgn(9)
of '~': result = considerStrongSpaces(8) of '~': result = 8
of '+', '-', '|': considerAsgn(8) of '+', '-', '|': considerAsgn(8)
of '&': considerAsgn(7) of '&': considerAsgn(7)
of '=', '<', '>', '!': result = considerStrongSpaces(5) of '=', '<', '>', '!': result = 5
of '.': considerAsgn(6) of '.': considerAsgn(6)
of '?': result = considerStrongSpaces(2) of '?': result = 2
else: considerAsgn(2) else: considerAsgn(2)
of tkDiv, tkMod, tkShl, tkShr: result = 9 of tkDiv, tkMod, tkShl, tkShr: result = 9
of tkIn, tkNotin, tkIs, tkIsnot, tkNot, tkOf, tkAs: result = 5 of tkIn, tkNotin, tkIs, tkIsnot, tkNot, tkOf, tkAs: result = 5
of tkDotDot: result = considerStrongSpaces(6) of tkDotDot: result = 6
of tkAnd: result = 4 of tkAnd: result = 4
of tkOr, tkXor, tkPtr, tkRef: result = 3 of tkOr, tkXor, tkPtr, tkRef: result = 3
else: result = -10 else: return -10
result = considerStrongSpaces(result)
proc isOperator(tok: TToken): bool = proc isOperator(tok: TToken): bool =
## Determines if the given token is an operator type token. ## Determines if the given token is an operator type token.
@ -387,7 +389,6 @@ proc exprList(p: var TParser, endTok: TTokType, result: PNode) =
if p.tok.tokType != tkComma: break if p.tok.tokType != tkComma: break
getTok(p) getTok(p)
optInd(p, a) optInd(p, a)
eat(p, endTok)
proc dotExpr(p: var TParser, a: PNode): PNode = proc dotExpr(p: var TParser, a: PNode): PNode =
#| dotExpr = expr '.' optInd symbol #| dotExpr = expr '.' optInd symbol
@ -943,8 +944,7 @@ proc parseDoBlock(p: var TParser): PNode =
getTok(p) getTok(p)
let params = parseParamList(p, retColon=false) let params = parseParamList(p, retColon=false)
let pragmas = optPragmas(p) let pragmas = optPragmas(p)
eat(p, tkColon) colcom(p, result)
skipComment(p, result)
result = newProcNode(nkDo, info, parseStmt(p), result = newProcNode(nkDo, info, parseStmt(p),
params = params, params = params,
pragmas = pragmas) pragmas = pragmas)
@ -1138,9 +1138,11 @@ proc parseMacroColon(p: var TParser, x: PNode): PNode =
result = makeCall(result) result = makeCall(result)
getTok(p) getTok(p)
skipComment(p, result) skipComment(p, result)
let stmtList = newNodeP(nkStmtList, p)
if p.tok.tokType notin {tkOf, tkElif, tkElse, tkExcept}: if p.tok.tokType notin {tkOf, tkElif, tkElse, tkExcept}:
let body = parseStmt(p) let body = parseStmt(p)
addSon(result, makeStmtList(body)) stmtList.add body
#addSon(result, makeStmtList(body))
while sameInd(p): while sameInd(p):
var b: PNode var b: PNode
case p.tok.tokType case p.tok.tokType
@ -1152,19 +1154,22 @@ proc parseMacroColon(p: var TParser, x: PNode): PNode =
getTok(p) getTok(p)
optInd(p, b) optInd(p, b)
addSon(b, parseExpr(p)) addSon(b, parseExpr(p))
eat(p, tkColon)
of tkExcept: of tkExcept:
b = newNodeP(nkExceptBranch, p) b = newNodeP(nkExceptBranch, p)
exprList(p, tkColon, b) exprList(p, tkColon, b)
skipComment(p, b)
of tkElse: of tkElse:
b = newNodeP(nkElse, p) b = newNodeP(nkElse, p)
getTok(p) getTok(p)
eat(p, tkColon)
else: break else: break
eat(p, tkColon)
addSon(b, parseStmt(p)) addSon(b, parseStmt(p))
addSon(result, b) addSon(stmtList, b)
if b.kind == nkElse: break 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 = proc parseExprStmt(p: var TParser): PNode =
#| exprStmt = simpleExpr #| exprStmt = simpleExpr
@ -1309,8 +1314,7 @@ proc parseIfOrWhen(p: var TParser, kind: TNodeKind): PNode =
var branch = newNodeP(nkElifBranch, p) var branch = newNodeP(nkElifBranch, p)
optInd(p, branch) optInd(p, branch)
addSon(branch, parseExpr(p)) addSon(branch, parseExpr(p))
eat(p, tkColon) colcom(p, branch)
skipComment(p, branch)
addSon(branch, parseStmt(p)) addSon(branch, parseStmt(p))
skipComment(p, branch) skipComment(p, branch)
addSon(result, branch) addSon(result, branch)
@ -1318,8 +1322,7 @@ proc parseIfOrWhen(p: var TParser, kind: TNodeKind): PNode =
if p.tok.tokType == tkElse and sameOrNoInd(p): if p.tok.tokType == tkElse and sameOrNoInd(p):
var branch = newNodeP(nkElse, p) var branch = newNodeP(nkElse, p)
eat(p, tkElse) eat(p, tkElse)
eat(p, tkColon) colcom(p, branch)
skipComment(p, branch)
addSon(branch, parseStmt(p)) addSon(branch, parseStmt(p))
addSon(result, branch) addSon(result, branch)
@ -1367,13 +1370,11 @@ proc parseCase(p: var TParser): PNode =
getTok(p) getTok(p)
optInd(p, b) optInd(p, b)
addSon(b, parseExpr(p)) addSon(b, parseExpr(p))
eat(p, tkColon)
of tkElse: of tkElse:
b = newNodeP(nkElse, p) b = newNodeP(nkElse, p)
getTok(p) getTok(p)
eat(p, tkColon)
else: break else: break
skipComment(p, b) colcom(p, b)
addSon(b, parseStmt(p)) addSon(b, parseStmt(p))
addSon(result, b) addSon(result, b)
if b.kind == nkElse: break if b.kind == nkElse: break
@ -1390,8 +1391,7 @@ proc parseTry(p: var TParser; isExpr: bool): PNode =
#| (optInd 'finally' colcom stmt)? #| (optInd 'finally' colcom stmt)?
result = newNodeP(nkTryStmt, p) result = newNodeP(nkTryStmt, p)
getTok(p) getTok(p)
eat(p, tkColon) colcom(p, result)
skipComment(p, result)
addSon(result, parseStmt(p)) addSon(result, parseStmt(p))
var b: PNode = nil var b: PNode = nil
while sameOrNoInd(p) or isExpr: while sameOrNoInd(p) or isExpr:
@ -1401,10 +1401,9 @@ proc parseTry(p: var TParser; isExpr: bool): PNode =
exprList(p, tkColon, b) exprList(p, tkColon, b)
of tkFinally: of tkFinally:
b = newNodeP(nkFinally, p) b = newNodeP(nkFinally, p)
getTokNoInd(p) getTok(p)
eat(p, tkColon)
else: break else: break
skipComment(p, b) colcom(p, b)
addSon(b, parseStmt(p)) addSon(b, parseStmt(p))
addSon(result, b) addSon(result, b)
if b.kind == nkFinally: break if b.kind == nkFinally: break
@ -1413,7 +1412,7 @@ proc parseTry(p: var TParser; isExpr: bool): PNode =
proc parseExceptBlock(p: var TParser, kind: TNodeKind): PNode = proc parseExceptBlock(p: var TParser, kind: TNodeKind): PNode =
#| exceptBlock = 'except' colcom stmt #| exceptBlock = 'except' colcom stmt
result = newNodeP(kind, p) result = newNodeP(kind, p)
getTokNoInd(p) getTok(p)
colcom(p, result) colcom(p, result)
addSon(result, parseStmt(p)) addSon(result, parseStmt(p))
@ -1446,7 +1445,7 @@ proc parseStaticOrDefer(p: var TParser; k: TNodeKind): PNode =
#| staticStmt = 'static' colcom stmt #| staticStmt = 'static' colcom stmt
#| deferStmt = 'defer' colcom stmt #| deferStmt = 'defer' colcom stmt
result = newNodeP(k, p) result = newNodeP(k, p)
getTokNoInd(p) getTok(p)
colcom(p, result) colcom(p, result)
addSon(result, parseStmt(p)) addSon(result, parseStmt(p))
@ -1627,7 +1626,7 @@ proc parseEnum(p: var TParser): PNode =
p.tok.tokType == tkEof: p.tok.tokType == tkEof:
break break
if result.len <= 1: 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 parseObjectPart(p: var TParser): PNode
proc parseObjectWhen(p: var TParser): PNode = proc parseObjectWhen(p: var TParser): PNode =
@ -1685,9 +1684,8 @@ proc parseObjectCase(p: var TParser): PNode =
of tkElse: of tkElse:
b = newNodeP(nkElse, p) b = newNodeP(nkElse, p)
getTok(p) getTok(p)
eat(p, tkColon)
else: break else: break
skipComment(p, b) colcom(p, b)
var fields = parseObjectPart(p) var fields = parseObjectPart(p)
if fields.kind == nkEmpty: if fields.kind == nkEmpty:
parMessage(p, errIdentifierExpected, p.tok) parMessage(p, errIdentifierExpected, p.tok)
@ -1883,7 +1881,7 @@ proc simpleStmt(p: var TParser): PNode =
proc complexOrSimpleStmt(p: var TParser): PNode = proc complexOrSimpleStmt(p: var TParser): PNode =
#| complexOrSimpleStmt = (ifStmt | whenStmt | whileStmt #| complexOrSimpleStmt = (ifStmt | whenStmt | whileStmt
#| | tryStmt | finallyStmt | exceptStmt | forStmt #| | tryStmt | forStmt
#| | blockStmt | staticStmt | deferStmt | asmStmt #| | blockStmt | staticStmt | deferStmt | asmStmt
#| | 'proc' routine #| | 'proc' routine
#| | 'method' routine #| | 'method' routine
@ -2030,8 +2028,8 @@ proc parseString*(s: string; filename: string = ""; line: int = 0;
var parser: TParser var parser: TParser
# XXX for now the builtin 'parseStmt/Expr' functions do not know about strong # XXX for now the builtin 'parseStmt/Expr' functions do not know about strong
# spaces... # spaces...
openParser(parser, filename, stream, false)
parser.lex.errorHandler = errorHandler parser.lex.errorHandler = errorHandler
openParser(parser, filename, stream, false)
result = parser.parseAll result = parser.parseAll
closeParser(parser) closeParser(parser)

43
compiler/plugins.nim Normal file
View 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)

View 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

View 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)

View file

@ -60,7 +60,7 @@ const
varPragmas* = {wImportc, wExportc, wVolatile, wRegister, wThreadVar, wNodecl, varPragmas* = {wImportc, wExportc, wVolatile, wRegister, wThreadVar, wNodecl,
wMagic, wHeader, wDeprecated, wCompilerproc, wDynlib, wExtern, wMagic, wHeader, wDeprecated, wCompilerproc, wDynlib, wExtern,
wImportCpp, wImportObjC, wError, wNoInit, wCompileTime, wGlobal, wImportCpp, wImportObjC, wError, wNoInit, wCompileTime, wGlobal,
wGensym, wInject, wCodegenDecl, wGuard} wGensym, wInject, wCodegenDecl, wGuard, wGoto}
constPragmas* = {wImportc, wExportc, wHeader, wDeprecated, wMagic, wNodecl, constPragmas* = {wImportc, wExportc, wHeader, wDeprecated, wMagic, wNodecl,
wExtern, wImportCpp, wImportObjC, wError, wGensym, wInject} wExtern, wImportCpp, wImportObjC, wError, wGensym, wInject}
letPragmas* = varPragmas letPragmas* = varPragmas
@ -89,7 +89,7 @@ proc pragmaAsm*(c: PContext, n: PNode): char =
invalidPragma(it) invalidPragma(it)
proc setExternName(s: PSym, extname: string) = 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: if gCmd == cmdPretty and '$' notin extname:
# note that '{.importc.}' is transformed into '{.importc: "$1".}' # note that '{.importc.}' is transformed into '{.importc: "$1".}'
s.loc.flags.incl(lfFullExternalName) s.loc.flags.incl(lfFullExternalName)
@ -105,7 +105,7 @@ proc validateExternCName(s: PSym, info: TLineInfo) =
## Valid identifiers are those alphanumeric including the underscore not ## Valid identifiers are those alphanumeric including the underscore not
## starting with a number. If the check fails, a generic error will be ## starting with a number. If the check fails, a generic error will be
## displayed to the user. ## 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 if target.len < 1 or target[0] notin IdentStartChars or
not target.allCharsInSet(IdentChars): not target.allCharsInSet(IdentChars):
localError(info, errGenerated, "invalid exported symbol") localError(info, errGenerated, "invalid exported symbol")
@ -677,7 +677,7 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: int,
incl(sym.loc.flags, lfHeader) incl(sym.loc.flags, lfHeader)
incl(sym.loc.flags, lfNoDecl) incl(sym.loc.flags, lfNoDecl)
# implies nodecl, because otherwise header would not make sense # 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: of wDestructor:
sym.flags.incl sfOverriden sym.flags.incl sfOverriden
if sym.name.s.normalize != "destroy": if sym.name.s.normalize != "destroy":
@ -843,6 +843,11 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: int,
invalidPragma(it) invalidPragma(it)
else: else:
sym.guard = pragmaGuard(c, it, sym.kind) 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: of wInjectStmt:
if it.kind != nkExprColonExpr: if it.kind != nkExprColonExpr:
localError(it.info, errExprExpected) localError(it.info, errExprExpected)
@ -865,9 +870,11 @@ proc implicitPragmas*(c: PContext, sym: PSym, n: PNode,
while it != nil: while it != nil:
let o = it.otherPragmas let o = it.otherPragmas
if not o.isNil: if not o.isNil:
pushInfoContext(n.info)
for i in countup(0, sonsLen(o) - 1): for i in countup(0, sonsLen(o) - 1):
if singlePragma(c, sym, o, i, validPragmas): if singlePragma(c, sym, o, i, validPragmas):
internalError(n.info, "implicitPragmas") internalError(n.info, "implicitPragmas")
popInfoContext()
it = it.next.POptionEntry it = it.next.POptionEntry
if lfExportLib in sym.loc.flags and sfExportc notin sym.flags: if lfExportLib in sym.loc.flags and sfExportc notin sym.flags:
@ -877,7 +884,7 @@ proc implicitPragmas*(c: PContext, sym: PSym, n: PNode,
sfImportc in sym.flags and lib != nil: sfImportc in sym.flags and lib != nil:
incl(sym.loc.flags, lfDynamicLib) incl(sym.loc.flags, lfDynamicLib)
addToLib(lib, sym) 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 = proc hasPragma*(n: PNode, pragma: TSpecialWord): bool =
if n == nil or n.sons == nil: if n == nil or n.sons == nil:

View file

@ -503,6 +503,7 @@ proc gsub(g: var TSrcGen, n: PNode) =
proc hasCom(n: PNode): bool = proc hasCom(n: PNode): bool =
result = false result = false
if n.isNil: return false
if n.comment != nil: return true if n.comment != nil: return true
case n.kind case n.kind
of nkEmpty..nkNilLit: discard of nkEmpty..nkNilLit: discard

View file

@ -277,7 +277,7 @@ proc decodeLoc(r: PRodReader, loc: var TLoc, info: TLineInfo) =
loc.t = nil loc.t = nil
if r.s[r.pos] == '!': if r.s[r.pos] == '!':
inc(r.pos) inc(r.pos)
loc.r = toRope(decodeStr(r.s, r.pos)) loc.r = rope(decodeStr(r.s, r.pos))
else: else:
loc.r = nil loc.r = nil
if r.s[r.pos] == '>': inc(r.pos) 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)) result.kind = TLibKind(decodeVInt(r.s, r.pos))
if r.s[r.pos] != '|': internalError("decodeLib: 1") if r.s[r.pos] != '|': internalError("decodeLib: 1")
inc(r.pos) 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") if r.s[r.pos] != '|': internalError("decodeLib: 2")
inc(r.pos) inc(r.pos)
result.path = decodeNode(r, info) result.path = decodeNode(r, info)

View file

@ -186,7 +186,7 @@ proc encodeLoc(w: PRodWriter, loc: TLoc, result: var string) =
pushType(w, loc.t) pushType(w, loc.t)
if loc.r != nil: if loc.r != nil:
add(result, '!') add(result, '!')
encodeStr(ropeToStr(loc.r), result) encodeStr($loc.r, result)
if oldLen + 1 == result.len: if oldLen + 1 == result.len:
# no data was necessary, so remove the '<' again: # no data was necessary, so remove the '<' again:
setLen(result, oldLen) setLen(result, oldLen)
@ -241,7 +241,7 @@ proc encodeLib(w: PRodWriter, lib: PLib, info: TLineInfo, result: var string) =
add(result, '|') add(result, '|')
encodeVInt(ord(lib.kind), result) encodeVInt(ord(lib.kind), result)
add(result, '|') add(result, '|')
encodeStr(ropeToStr(lib.name), result) encodeStr($lib.name, result)
add(result, '|') add(result, '|')
encodeNode(w, info, lib.path, result) encodeNode(w, info, lib.path, result)

View file

@ -56,77 +56,59 @@
# To cache them they are inserted in a `cache` array. # To cache them they are inserted in a `cache` array.
import import
strutils, platform, hashes, crc, options platform, hashes
type 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 # performance of the code generator (assignments
# copy the format strings # copy the format strings
# though it is not necessary) # though it is not necessary)
PRope* = ref TRope Rope* = ref RopeObj
TRope*{.acyclic.} = object of RootObj # the empty rope is represented RopeObj*{.acyclic.} = object of RootObj # the empty rope is represented
# by nil to safe space # by nil to safe space
left*, right*: PRope left*, right*: Rope
length*: int length*: int
data*: string # != nil if a leaf data*: string # != nil if a leaf
TRopeSeq* = seq[PRope] RopeSeq* = seq[Rope]
TRopesError* = enum RopesError* = enum
rCannotOpenFile rCannotOpenFile
rInvalidFormatStr 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 # implementation
var errorHandler*: proc(err: TRopesError, msg: string, useWarning = false) var errorHandler*: proc(err: RopesError, msg: string, useWarning = false)
# avoid dependency on msgs.nim # avoid dependency on msgs.nim
proc ropeLen(a: PRope): int = proc len*(a: Rope): int =
## the rope's length
if a == nil: result = 0 if a == nil: result = 0
else: result = a.length else: result = a.length
proc newRope*(data: string = nil): PRope = proc newRope(data: string = nil): Rope =
new(result) new(result)
if data != nil: if data != nil:
result.length = len(data) result.length = len(data)
result.data = 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 ## creates a new rope that supports direct modifications of the rope's
## 'data' and 'length' fields. ## 'data' and 'length' fields.
new(result) new(result)
result.data = newStringOfCap(capacity) result.data = newStringOfCap(capacity)
proc freezeMutableRope*(r: PRope) {.inline.} = proc freezeMutableRope*(r: Rope) {.inline.} =
r.length = r.data.len r.length = r.data.len
var var
cache: array[0..2048*2 - 1, PRope] cache: array[0..2048*2 - 1, Rope]
proc resetRopeCache* = proc resetRopeCache* =
for i in low(cache)..high(cache): for i in low(cache)..high(cache):
cache[i] = nil cache[i] = nil
proc ropeInvariant(r: PRope): bool = proc ropeInvariant(r: Rope): bool =
if r == nil: if r == nil:
result = true result = true
else: else:
@ -143,7 +125,7 @@ var gCacheTries* = 0
var gCacheMisses* = 0 var gCacheMisses* = 0
var gCacheIntTries* = 0 var gCacheIntTries* = 0
proc insertInCache(s: string): PRope = proc insertInCache(s: string): Rope =
inc gCacheTries inc gCacheTries
var h = hash(s) and high(cache) var h = hash(s) and high(cache)
result = cache[h] result = cache[h]
@ -152,82 +134,77 @@ proc insertInCache(s: string): PRope =
result = newRope(s) result = newRope(s)
cache[h] = result cache[h] = result
proc toRope(s: string): PRope = proc rope*(s: string): Rope =
## Converts a string to a rope.
if s.len == 0: if s.len == 0:
result = nil result = nil
else: else:
result = insertInCache(s) result = insertInCache(s)
assert(ropeInvariant(result)) assert(ropeInvariant(result))
proc ropeSeqInsert(rs: var TRopeSeq, r: PRope, at: Natural) = proc rope*(i: BiggestInt): Rope =
var length = len(rs) ## Converts an int to a rope.
if at > length: inc gCacheIntTries
setLen(rs, at + 1) result = rope($i)
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 newRecRopeToStr(result: var string, resultLen: var int, r: PRope) = proc rope*(f: BiggestFloat): Rope =
var stack = @[r] ## Converts a float to a rope.
while len(stack) > 0: result = rope($f)
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 ropeToStr(p: PRope): string = proc `&`*(a, b: Rope): Rope =
if p == nil: if a == nil:
result = "" result = b
else: elif b == nil:
result = newString(p.length) result = a
var resultLen = 0
newRecRopeToStr(result, resultLen, p)
proc con(a, b: PRope): PRope =
if a == nil: result = b
elif b == nil: result = a
else: else:
result = newRope() result = newRope()
result.length = a.length + b.length result.length = a.length + b.length
result.left = a result.left = a
result.right = b result.right = b
proc con(a: PRope, b: string): PRope = result = con(a, toRope(b)) proc `&`*(a: Rope, b: string): Rope =
proc con(a: string, b: PRope): PRope = result = con(toRope(a), b) ## the concatenation operator for ropes.
result = a & rope(b)
proc con(a: varargs[PRope]): PRope = proc `&`*(a: string, b: Rope): Rope =
for i in countup(0, high(a)): result = con(result, a[i]) ## the concatenation operator for ropes.
result = rope(a) & b
proc ropeConcat*(a: varargs[PRope]): PRope = proc `&`*(a: openArray[Rope]): Rope =
# not overloaded version of concat to speed-up `rfmt` a little bit ## the concatenation operator for an openarray of ropes.
for i in countup(0, high(a)): result = con(result, a[i]) for i in countup(0, high(a)): result = result & a[i]
proc toRope(i: BiggestInt): PRope = proc add*(a: var Rope, b: Rope) =
inc gCacheIntTries ## adds `b` to the rope `a`.
result = toRope($i) a = a & b
proc app(a: var PRope, b: PRope) = a = con(a, b) proc add*(a: var Rope, b: string) =
proc app(a: var PRope, b: string) = a = con(a, b) ## adds `b` to the rope `a`.
proc prepend(a: var PRope, b: PRope) = a = con(b, a) a = a & b
proc writeRope*(f: File, c: PRope) = iterator leaves*(r: Rope): string =
var stack = @[c] ## iterates over any leaf string in the rope `r`.
while len(stack) > 0: if r != nil:
var it = pop(stack) var stack = @[r]
while it.data == nil: while stack.len > 0:
add(stack, it.right) var it = stack.pop
while isNil(it.data):
stack.add(it.right)
it = it.left it = it.left
assert(it != nil) assert(it != nil)
assert(it.data != nil) assert(it.data != nil)
write(f, it.data) 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 var f: File
if open(f, filename, fmWrite): if open(f, filename, fmWrite):
if head != nil: writeRope(f, head) if head != nil: writeRope(f, head)
@ -235,42 +212,69 @@ proc writeRope*(head: PRope, filename: string, useWarning = false) =
else: else:
errorHandler(rCannotOpenFile, filename, useWarning) 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 var
rnl* = tnl.newRope rnl* = tnl.newRope
softRnl* = tnl.newRope softRnl* = tnl.newRope
proc ropef(frmt: TFormatStr, args: varargs[PRope]): PRope = proc `%`*(frmt: FormatStr, args: openArray[Rope]): Rope =
var i = 0 var i = 0
var length = len(frmt) var length = len(frmt)
result = nil result = nil
var num = 0 var num = 0
while i <= length - 1: while i < length:
if frmt[i] == '$': if frmt[i] == '$':
inc(i) # skip '$' inc(i) # skip '$'
case frmt[i] case frmt[i]
of '$': of '$':
app(result, "$") add(result, "$")
inc(i) inc(i)
of '#': of '#':
inc(i) inc(i)
app(result, args[num]) add(result, args[num])
inc(num) inc(num)
of '0'..'9': of '0'..'9':
var j = 0 var j = 0
while true: while true:
j = (j * 10) + ord(frmt[i]) - ord('0') j = j * 10 + ord(frmt[i]) - ord('0')
inc(i) inc(i)
if (i > length + 0 - 1) or not (frmt[i] in {'0'..'9'}): break if frmt[i] notin {'0'..'9'}: break
num = j num = j
if j > high(args) + 1: if j > high(args) + 1:
errorHandler(rInvalidFormatStr, $(j)) errorHandler(rInvalidFormatStr, $(j))
else: 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': of 'n':
app(result, softRnl) add(result, softRnl)
inc i inc(i)
of 'N': of 'N':
app(result, rnl) add(result, rnl)
inc(i) inc(i)
else: else:
errorHandler(rInvalidFormatStr, $(frmt[i])) errorHandler(rInvalidFormatStr, $(frmt[i]))
@ -279,83 +283,67 @@ proc ropef(frmt: TFormatStr, args: varargs[PRope]): PRope =
if frmt[i] != '$': inc(i) if frmt[i] != '$': inc(i)
else: break else: break
if i - 1 >= start: if i - 1 >= start:
app(result, substr(frmt, start, i - 1)) add(result, substr(frmt, start, i - 1))
assert(ropeInvariant(result)) 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: when true:
template `~`*(r: string): PRope = r.ropef template `~`*(r: string): Rope = r % []
else: else:
{.push stack_trace: off, line_trace: off.} {.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 # this is the new optimized "to rope" operator
# the mnemonic is that `~` looks a bit like a rope :) # the mnemonic is that `~` looks a bit like a rope :)
var r {.global.} = r.ropef var r {.global.} = r % []
return r return r
{.pop.} {.pop.}
proc appf(c: var PRope, frmt: TFormatStr, args: varargs[PRope]) =
app(c, ropef(frmt, args))
const const
bufSize = 1024 # 1 KB is reasonable bufSize = 1024 # 1 KB is reasonable
proc auxRopeEqualsFile(r: PRope, bin: var File, buf: pointer): bool = proc equalsFile*(r: Rope, f: File): bool =
if r.data != nil: ## returns true if the contents of the file `f` equal `r`.
if r.length > bufSize: var
errorHandler(rTokenTooLong, r.data) buf: array[bufSize, char]
bpos = buf.len
blen = buf.len
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 return
var readBytes = readBuffer(bin, buf, r.length) let n = min(blen - bpos, slen - spos)
result = readBytes == r.length and # TODO There's gotta be a better way of comparing here...
equalMem(buf, addr(r.data[0]), r.length) # BUGFIX if not equalMem(addr(buf[bpos]), cast[pointer](cast[int](cstring(s))+spos), n):
else: result = false
result = auxRopeEqualsFile(r.left, bin, buf) return
if result: result = auxRopeEqualsFile(r.right, bin, buf) spos += n
bpos += n
proc ropeEqualsFile(r: PRope, f: string): bool = result = readBuffer(f, addr(buf[0]), 1) == 0 # check that we've read all
var bin: File
result = open(bin, f) proc equalsFile*(r: Rope, filename: string): bool =
if not result: ## returns true if the contents of the file `f` equal `r`. If `f` does not
return # not equal if file does not exist ## exist, false is returned.
var buf = alloc(bufSize) var f: File
result = auxRopeEqualsFile(r, bin, buf) result = open(f, filename)
if result: if result:
result = readBuffer(bin, buf, bufSize) == 0 # really at the end of file? result = equalsFile(r, f)
dealloc(buf) close(f)
close(bin)
proc crcFromRopeAux(r: PRope, startVal: TCrc32): TCrc32 = proc writeRopeIfNotEqual*(r: Rope, filename: string): bool =
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 =
# returns true if overwritten # returns true if overwritten
var c: TCrc32 if not equalsFile(r, filename):
c = crcFromFile(filename)
if c != crcFromRope(r):
writeRope(r, filename) writeRope(r, filename)
result = true result = true
else: else:

View file

@ -16,7 +16,7 @@ import
procfind, lookups, rodread, pragmas, passes, semdata, semtypinst, sigmatch, procfind, lookups, rodread, pragmas, passes, semdata, semtypinst, sigmatch,
intsets, transf, vmdef, vm, idgen, aliases, cgmeth, lambdalifting, intsets, transf, vmdef, vm, idgen, aliases, cgmeth, lambdalifting,
evaltempl, patterns, parampatterns, sempass2, nimfix.pretty, semmacrosanity, evaltempl, patterns, parampatterns, sempass2, nimfix.pretty, semmacrosanity,
semparallel, lowerings semparallel, lowerings, plugins, plugins.active
when defined(nimfix): when defined(nimfix):
import nimfix.prettybase import nimfix.prettybase
@ -89,6 +89,10 @@ proc fitNode(c: PContext, formal: PType, arg: PNode): PNode =
let x = result.skipConv let x = result.skipConv
if x.kind == nkPar and formal.kind != tyExpr: if x.kind == nkPar and formal.kind != tyExpr:
changeType(x, formal, check=true) 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, proc inferWithMetatype(c: PContext, formal: PType,
arg: PNode, coerceDistincts = false): PNode arg: PNode, coerceDistincts = false): PNode
@ -398,7 +402,7 @@ proc myOpen(module: PSym): PPassContext =
c.semInferredLambda = semInferredLambda c.semInferredLambda = semInferredLambda
c.semGenerateInstance = generateInstance c.semGenerateInstance = generateInstance
c.semTypeNode = semTypeNode c.semTypeNode = semTypeNode
c.instDeepCopy = sigmatch.instDeepCopy c.instTypeBoundOp = sigmatch.instTypeBoundOp
pushProcCon(c, module) pushProcCon(c, module)
pushOwner(c.module) pushOwner(c.module)

View file

@ -7,111 +7,84 @@
# distribution, for details about the copyright. # 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 # included from sem.nim
type type
TTypeAttachedOp = enum
attachedDestructor,
attachedAsgn,
attachedDeepCopy
TLiftCtx = object TLiftCtx = object
c: PContext c: PContext
info: TLineInfo # for construction info: TLineInfo # for construction
result: PNode
kind: TTypeAttachedOp kind: TTypeAttachedOp
fn: PSym
asgnForType: PType
recurse: bool
type proc liftBodyAux(c: var TLiftCtx; t: PType; body, x, y: PNode)
TFieldInstCtx = object # either 'tup[i]' or 'field' is valid proc liftBody(c: PContext; typ: PType; info: TLineInfo): PSym
tupleType: PType # if != nil we're traversing a tuple
tupleIndex: int
field: PSym
replaceByFieldName: bool
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 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: of nkSym:
var fc: TFieldInstCtx # either 'tup[i]' or 'field' is valid let f = n.sym
fc.field = typ.sym liftBodyAux(c, f.typ, body, x.dotField(f), y.dotField(f))
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)
of nkNilLit: discard of nkNilLit: discard
of nkRecCase: of nkRecCase:
let L = forLoop.len # copy the selector:
let call = forLoop.sons[L-2] liftBodyObj(c, n[0], body, x, y)
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)
# we need to generate a case statement: # we need to generate a case statement:
var caseStmt = newNodeI(nkCaseStmt, c.info) var caseStmt = newNodeI(nkCaseStmt, c.info)
# XXX generate 'if' that checks same branches
# generate selector: # generate selector:
var access = newNodeI(nkDotExpr, forLoop.info, 2) var access = dotField(x, n[0].sym)
access.sons[0] = call.sons[1] caseStmt.add(access)
access.sons[1] = newSymNode(typ.sons[0].sym, forLoop.info)
caseStmt.add(semExprWithType(c.c, access))
# copy the branches over, but replace the fields with the for loop body: # copy the branches over, but replace the fields with the for loop body:
for i in 1 .. <typ.len: for i in 1 .. <n.len:
var branch = copyTree(typ[i]) var branch = copyTree(n[i])
let L = branch.len let L = branch.len
branch.sons[L-1] = newNodeI(nkStmtList, forLoop.info) branch.sons[L-1] = newNodeI(nkStmtList, c.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:
illFormedAstLocal(typ)
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 = newNodeI(nkCall, x.info)
result.add(newSymNode(op)) result.add newSymNode(op)
result.add x result.add genAddr(c, x)
result.add y result.add y
proc newAsgnStmt(le, ri: PNode): PNode = proc newAsgnStmt(le, ri: PNode): PNode =
@ -127,63 +100,124 @@ proc newDestructorCall(op: PSym; x: PNode): PNode =
proc newDeepCopyCall(op: PSym; x, y: PNode): PNode = proc newDeepCopyCall(op: PSym; x, y: PNode): PNode =
result = newAsgnStmt(x, newDestructorCall(op, y)) result = newAsgnStmt(x, newDestructorCall(op, y))
proc considerOverloadedOp(c: TLiftCtx; t: PType; x, y: PNode): bool = proc considerOverloadedOp(c: var TLiftCtx; t: PType; body, x, y: PNode): bool =
let op = t.attachedOps[c.kind] case c.kind
of attachedDestructor:
let op = t.destructor
if op != nil: if op != nil:
markUsed(c.info, op) markUsed(c.info, op)
styleCheckUse(c.info, op) styleCheckUse(c.info, op)
case c.kind body.add newDestructorCall(op, x)
of attachedDestructor: result = true
c.result.add newDestructorCall(op, x)
of attachedAsgn: of attachedAsgn:
c.result.add newAsgnCall(op, x, y) 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: of attachedDeepCopy:
c.result.add newDeepCopyCall(op, x, y) let op = t.deepCopy
if op != nil:
markUsed(c.info, op)
styleCheckUse(c.info, op)
body.add newDeepCopyCall(op, x, y)
result = true 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: if c.kind != attachedDestructor:
c.result.add newAsgnStmt(x, y) body.add newAsgnStmt(x, y)
proc liftBodyAux(c: TLiftCtx; t: PType; x, y: PNode) = proc addVar(father, v, value: PNode) =
const hasAttachedOp: array[TTypeAttachedOp, TTypeIter] = [ var vpart = newNodeI(nkIdentDefs, v.info, 3)
(proc (t: PType, closure: PObject): bool = vpart.sons[0] = v
t.attachedOp[attachedDestructor] != nil), vpart.sons[1] = ast.emptyNode
(proc (t: PType, closure: PObject): bool = vpart.sons[2] = value
t.attachedOp[attachedAsgn] != nil), addSon(father, vpart)
(proc (t: PType, closure: PObject): bool =
t.attachedOp[attachedDeepCopy] != nil)] 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 case t.kind
of tyNone, tyEmpty: discard of tyNone, tyEmpty: discard
of tyPointer, tySet, tyBool, tyChar, tyEnum, tyInt..tyUInt64, tyCString: of tyPointer, tySet, tyBool, tyChar, tyEnum, tyInt..tyUInt64, tyCString,
defaultOp(c, t, x, y) tyPtr, tyString, tyRef:
of tyPtr, tyString: defaultOp(c, t, body, x, y)
if not considerOverloadedOp(c, t, x, y):
defaultOp(c, t, x, y)
of tyArrayConstr, tyArray, tySequence: of tyArrayConstr, tyArray, tySequence:
if iterOverType(lastSon(t), hasAttachedOp[c.kind], nil): if tfHasAsgn in t.flags:
# generate loop and call the attached Op: 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: else:
defaultOp(c, t, x, y) defaultOp(c, t, body, x, y)
of tyObject: of tyObject, tyDistinct:
liftBodyObj(c, t.n, x, y) 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: of tyTuple:
liftBodyTup(c, t, x, y) liftBodyTup(c, t, body, x, y)
of tyRef:
# we MUST NOT check for acyclic here as a DAG might still share nodes:
of tyProc: of tyProc:
if t.callConv != ccClosure or c.kind != attachedDeepCopy: if t.callConv != ccClosure or c.kind != attachedDeepCopy:
defaultOp(c, t, x, y) defaultOp(c, t, body, x, y)
else: else:
# a big problem is that we don't know the enviroment's type here, so we # 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: # 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.typ = t
call.sons[0] = newSymNode(createMagic("deepCopy", mDeepCopy)) call.sons[0] = newSymNode(createMagic("deepCopy", mDeepCopy))
call.sons[1] = y call.sons[1] = y
c.result.add newAsgnStmt(x, call) body.add newAsgnStmt(x, call)
of tyVarargs, tyOpenArray: of tyVarargs, tyOpenArray:
localError(c.info, errGenerated, "cannot copy openArray") localError(c.info, errGenerated, "cannot copy openArray")
of tyFromExpr, tyIter, tyProxy, tyBuiltInTypeClass, tyUserTypeClass, of tyFromExpr, tyIter, tyProxy, tyBuiltInTypeClass, tyUserTypeClass,
@ -191,13 +225,60 @@ proc liftBodyAux(c: TLiftCtx; t: PType; x, y: PNode) =
tyMutable, tyGenericParam, tyGenericBody, tyNil, tyExpr, tyStmt, tyMutable, tyGenericParam, tyGenericBody, tyNil, tyExpr, tyStmt,
tyTypeDesc, tyGenericInvocation, tyBigNum, tyConst, tyForward: tyTypeDesc, tyGenericInvocation, tyBigNum, tyConst, tyForward:
internalError(c.info, "assignment requested for type: " & typeToString(t)) internalError(c.info, "assignment requested for type: " & typeToString(t))
of tyDistinct, tyOrdinal, tyRange, of tyOrdinal, tyRange,
tyGenericInst, tyFieldAccessor, tyStatic, tyVar: 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 var a: TLiftCtx
a.info = info a.info = info
a.result = newNodeI(nkStmtList, info) let body = newNodeI(nkStmtList, info)
liftBodyAux(a, typ) result = newSym(skProc, getIdent":lifted=", typ.owner, info)
result = a.result 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)

View file

@ -43,10 +43,16 @@ type
inst*: PInstantiation inst*: PInstantiation
TExprFlag* = enum TExprFlag* = enum
efLValue, efWantIterator, efInTypeof, efWantStmt, efDetermineType, efLValue, efWantIterator, efInTypeof,
efWantStmt, efAllowStmt, efDetermineType,
efAllowDestructor, efWantValue, efOperand, efNoSemCheck efAllowDestructor, efWantValue, efOperand, efNoSemCheck
TExprFlags* = set[TExprFlag] TExprFlags* = set[TExprFlag]
TTypeAttachedOp* = enum
attachedAsgn,
attachedDeepCopy,
attachedDestructor
PContext* = ref TContext PContext* = ref TContext
TContext* = object of TPassContext # a context represents a module TContext* = object of TPassContext # a context represents a module
module*: PSym # the module sym belonging to the context module*: PSym # the module sym belonging to the context
@ -93,8 +99,8 @@ type
lastGenericIdx*: int # used for the generics stack lastGenericIdx*: int # used for the generics stack
hloLoopDetector*: int # used to prevent endless loops in the HLO hloLoopDetector*: int # used to prevent endless loops in the HLO
inParallelStmt*: int inParallelStmt*: int
instDeepCopy*: proc (c: PContext; dc: PSym; t: PType; instTypeBoundOp*: proc (c: PContext; dc: PSym; t: PType; info: TLineInfo;
info: TLineInfo): PSym {.nimcall.} op: TTypeAttachedOp): PSym {.nimcall.}
proc makeInstPair*(s: PSym, inst: PInstantiation): TInstantiationPair = proc makeInstPair*(s: PSym, inst: PInstantiation): TInstantiationPair =
@ -112,7 +118,6 @@ proc newOptionEntry*(): POptionEntry
proc newLib*(kind: TLibKind): PLib proc newLib*(kind: TLibKind): PLib
proc addToLib*(lib: PLib, sym: PSym) proc addToLib*(lib: PLib, sym: PSym)
proc makePtrType*(c: PContext, baseType: PType): PType proc makePtrType*(c: PContext, baseType: PType): PType
proc makeVarType*(c: PContext, baseType: PType): PType
proc newTypeS*(kind: TTypeKind, c: PContext): PType proc newTypeS*(kind: TTypeKind, c: PContext): PType
proc fillTypeS*(dest: PType, kind: TTypeKind, c: PContext) proc fillTypeS*(dest: PType, kind: TTypeKind, c: PContext)
@ -207,7 +212,10 @@ proc makePtrType(c: PContext, baseType: PType): PType =
result = newTypeS(tyPtr, c) result = newTypeS(tyPtr, c)
addSonSkipIntLit(result, baseType.assertNotNil) addSonSkipIntLit(result, baseType.assertNotNil)
proc makeVarType(c: PContext, baseType: PType): PType = proc makeVarType*(c: PContext, baseType: PType): PType =
if baseType.kind == tyVar:
result = baseType
else:
result = newTypeS(tyVar, c) result = newTypeS(tyVar, c)
addSonSkipIntLit(result, baseType.assertNotNil) addSonSkipIntLit(result, baseType.assertNotNil)
@ -241,6 +249,7 @@ proc makeAndType*(c: PContext, t1, t2: PType): PType =
propagateToOwner(result, t1) propagateToOwner(result, t1)
propagateToOwner(result, t2) propagateToOwner(result, t2)
result.flags.incl((t1.flags + t2.flags) * {tfHasStatic}) result.flags.incl((t1.flags + t2.flags) * {tfHasStatic})
result.flags.incl tfHasMeta
proc makeOrType*(c: PContext, t1, t2: PType): PType = proc makeOrType*(c: PContext, t1, t2: PType): PType =
result = newTypeS(tyOr, c) result = newTypeS(tyOr, c)
@ -248,12 +257,14 @@ proc makeOrType*(c: PContext, t1, t2: PType): PType =
propagateToOwner(result, t1) propagateToOwner(result, t1)
propagateToOwner(result, t2) propagateToOwner(result, t2)
result.flags.incl((t1.flags + t2.flags) * {tfHasStatic}) result.flags.incl((t1.flags + t2.flags) * {tfHasStatic})
result.flags.incl tfHasMeta
proc makeNotType*(c: PContext, t1: PType): PType = proc makeNotType*(c: PContext, t1: PType): PType =
result = newTypeS(tyNot, c) result = newTypeS(tyNot, c)
result.sons = @[t1] result.sons = @[t1]
propagateToOwner(result, t1) propagateToOwner(result, t1)
result.flags.incl(t1.flags * {tfHasStatic}) result.flags.incl(t1.flags * {tfHasStatic})
result.flags.incl tfHasMeta
proc nMinusOne*(n: PNode): PNode = proc nMinusOne*(n: PNode): PNode =
result = newNode(nkCall, n.info, @[ result = newNode(nkCall, n.info, @[

View file

@ -24,14 +24,14 @@ proc semFieldAccess(c: PContext, n: PNode, flags: TExprFlags = {}): PNode
proc semOperand(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 # same as 'semExprWithType' but doesn't check for proc vars
result = semExpr(c, n, flags + {efOperand}) 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: # do not produce another redundant error message:
#raiseRecoverableError("") #raiseRecoverableError("")
result = errorNode(c, n) result = errorNode(c, n)
if result.typ != nil: if result.typ != nil:
# XXX tyGenericInst here? # XXX tyGenericInst here?
if result.typ.kind == tyVar: result = newDeref(result) if result.typ.kind == tyVar: result = newDeref(result)
elif efWantStmt in flags: elif {efWantStmt, efAllowStmt} * flags != {}:
result.typ = newTypeS(tyEmpty, c) result.typ = newTypeS(tyEmpty, c)
else: else:
localError(n.info, errExprXHasNoType, localError(n.info, errExprXHasNoType,
@ -389,7 +389,7 @@ proc isOpImpl(c: PContext, n: PNode): PNode =
maybeLiftType(t2, c, n.info) maybeLiftType(t2, c, n.info)
var m: TCandidate var m: TCandidate
initCandidate(c, m, t2) 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 = newIntNode(nkIntLit, ord(match))
result.typ = n.typ result.typ = n.typ
@ -447,6 +447,7 @@ proc changeType(n: PNode, newType: PType, check: bool) =
of nkPar: of nkPar:
let tup = newType.skipTypes({tyGenericInst}) let tup = newType.skipTypes({tyGenericInst})
if tup.kind != tyTuple: if tup.kind != tyTuple:
if tup.kind == tyObject: return
internalError(n.info, "changeType: no tuple type for constructor") internalError(n.info, "changeType: no tuple type for constructor")
elif sonsLen(n) > 0 and n.sons[0].kind == nkExprColonExpr: elif sonsLen(n) > 0 and n.sons[0].kind == nkExprColonExpr:
# named tuple? # named tuple?
@ -535,6 +536,18 @@ proc semArrayConstr(c: PContext, n: PNode, flags: TExprFlags): PNode =
result.typ.sons[0] = makeRangeType(c, 0, sonsLen(result) - 1, n.info) result.typ.sons[0] = makeRangeType(c, 0, sonsLen(result) - 1, n.info)
proc fixAbstractType(c: PContext, n: PNode) = proc fixAbstractType(c: PContext, n: PNode) =
for i in 1 .. < n.len:
let it = n.sons[i]
# do not get rid of nkHiddenSubConv for OpenArrays, the codegen needs it:
if it.kind == nkHiddenSubConv and
skipTypes(it.typ, abstractVar).kind notin {tyOpenArray, tyVarargs}:
if 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]
when false:
# XXX finally rewrite that crap! # XXX finally rewrite that crap!
for i in countup(1, sonsLen(n) - 1): for i in countup(1, sonsLen(n) - 1):
var it = n.sons[i] var it = n.sons[i]
@ -1298,6 +1311,9 @@ proc semAsgn(c: PContext, n: PNode): PNode =
typeMismatch(n, lhs.typ, rhs.typ) typeMismatch(n, lhs.typ, rhs.typ)
n.sons[1] = fitNode(c, le, rhs) 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) fixAbstractType(c, n)
asgnToResultVar(c, n, n.sons[0], n.sons[1]) asgnToResultVar(c, n, n.sons[0], n.sons[1])
result = n result = n
@ -2037,7 +2053,7 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode =
of nkEmpty, nkNone, nkCommentStmt: of nkEmpty, nkNone, nkCommentStmt:
discard discard
of nkNilLit: of nkNilLit:
result.typ = getSysType(tyNil) if result.typ == nil: result.typ = getSysType(tyNil)
of nkIntLit: of nkIntLit:
if result.typ == nil: setIntLitType(result) if result.typ == nil: setIntLitType(result)
of nkInt8Lit: of nkInt8Lit:

View file

@ -118,6 +118,9 @@ proc makeRange(typ: PType, first, last: BiggestInt): PType =
let lowerNode = newIntNode(nkIntLit, minA) let lowerNode = newIntNode(nkIntLit, minA)
if typ.kind == tyInt and minA == maxA: if typ.kind == tyInt and minA == maxA:
result = getIntLitType(lowerNode) 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: else:
var n = newNode(nkRange) var n = newNode(nkRange)
addSon(n, lowerNode) addSon(n, lowerNode)
@ -135,8 +138,9 @@ proc makeRangeF(typ: PType, first, last: BiggestFloat): PType =
addSonSkipIntLit(result, skipTypes(typ, {tyRange})) addSonSkipIntLit(result, skipTypes(typ, {tyRange}))
proc getIntervalType*(m: TMagic, n: PNode): PType = 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. # work but the feature is very nice for reducing explicit conversions.
const ordIntLit = {nkIntLit..nkUInt64Lit}
result = n.typ result = n.typ
template commutativeOp(opr: expr) {.immediate.} = template commutativeOp(opr: expr) {.immediate.} =
@ -170,11 +174,17 @@ proc getIntervalType*(m: TMagic, n: PNode): PType =
let a = n.sons[1].typ let a = n.sons[1].typ
if isFloatRange(a): if isFloatRange(a):
# abs(-5.. 1) == (1..5) # abs(-5.. 1) == (1..5)
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])), result = makeRangeF(a, abs(getFloat(a.n.sons[1])),
abs(getFloat(a.n.sons[0]))) abs(getFloat(a.n.sons[0])))
of mAbsI, mAbsI64: of mAbsI, mAbsI64:
let a = n.sons[1].typ let a = n.sons[1].typ
if isIntRange(a): if isIntRange(a):
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])), result = makeRange(a, `|abs|`(getInt(a.n.sons[1])),
`|abs|`(getInt(a.n.sons[0]))) `|abs|`(getInt(a.n.sons[0])))
of mSucc: of mSucc:
@ -202,15 +212,15 @@ proc getIntervalType*(m: TMagic, n: PNode): PType =
var a = n.sons[1] var a = n.sons[1]
var b = n.sons[2] var b = n.sons[2]
# symmetrical: # symmetrical:
if b.kind notin {nkIntLit..nkUInt32Lit}: swap(a, b) if b.kind notin ordIntLit: swap(a, b)
if b.kind in {nkIntLit..nkUInt32Lit}: if b.kind in ordIntLit:
let x = b.intVal|+|1 let x = b.intVal|+|1
if (x and -x) == x and x >= 0: if (x and -x) == x and x >= 0:
result = makeRange(a.typ, 0, b.intVal) result = makeRange(a.typ, 0, b.intVal)
of mModU: of mModU:
let a = n.sons[1] let a = n.sons[1]
let b = n.sons[2] let b = n.sons[2]
if b.kind in {nkIntLit..nkUInt32Lit}: if a.kind in ordIntLit:
if b.intVal >= 0: if b.intVal >= 0:
result = makeRange(a.typ, 0, b.intVal-1) result = makeRange(a.typ, 0, b.intVal-1)
else: else:
@ -226,9 +236,9 @@ proc getIntervalType*(m: TMagic, n: PNode): PType =
result = makeRange(a.typ, b.intVal+1, -(b.intVal+1)) result = makeRange(a.typ, b.intVal+1, -(b.intVal+1))
of mDivI, mDivI64, mDivU: of mDivI, mDivI64, mDivU:
binaryOp(`|div|`) binaryOp(`|div|`)
of mMinI, mMinI64: of mMinI:
commutativeOp(min) commutativeOp(min)
of mMaxI, mMaxI64: of mMaxI:
commutativeOp(max) commutativeOp(max)
else: discard else: discard
@ -276,10 +286,14 @@ proc evalOp(m: TMagic, n, a, b, c: PNode): PNode =
of mNot: result = newIntNodeT(1 - getInt(a), n) of mNot: result = newIntNodeT(1 - getInt(a), n)
of mCard: result = newIntNodeT(nimsets.cardSet(a), n) of mCard: result = newIntNodeT(nimsets.cardSet(a), n)
of mBitnotI, mBitnotI64: result = newIntNodeT(not getInt(a), n) of mBitnotI, mBitnotI64: result = newIntNodeT(not getInt(a), n)
of mLengthStr: result = newIntNodeT(len(getStr(a)), n) of mLengthStr, mXLenStr:
if a.kind == nkNilLit: result = newIntNodeT(0, n)
else: result = newIntNodeT(len(getStr(a)), n)
of mLengthArray: result = newIntNodeT(lengthOrd(a.typ), n) of mLengthArray: result = newIntNodeT(lengthOrd(a.typ), n)
of mLengthSeq, mLengthOpenArray: result = newIntNodeT(sonsLen(a), n) # BUGFIX of mLengthSeq, mLengthOpenArray, mXLenSeq:
of mUnaryPlusI, mUnaryPlusI64, mUnaryPlusF64: result = a # throw `+` away if a.kind == nkNilLit: result = newIntNodeT(0, n)
else: result = newIntNodeT(sonsLen(a), n) # BUGFIX
of mUnaryPlusI, mUnaryPlusF64: result = a # throw `+` away
of mToFloat, mToBiggestFloat: of mToFloat, mToBiggestFloat:
result = newFloatNodeT(toFloat(int(getInt(a))), n) result = newFloatNodeT(toFloat(int(getInt(a))), n)
of mToInt, mToBiggestInt: result = newIntNodeT(system.toInt(getFloat(a)), n) of mToInt, mToBiggestInt: result = newIntNodeT(system.toInt(getFloat(a)), n)
@ -299,10 +313,10 @@ proc evalOp(m: TMagic, n, a, b, c: PNode): PNode =
of mAddI, mAddI64: result = newIntNodeT(getInt(a) + getInt(b), n) of mAddI, mAddI64: result = newIntNodeT(getInt(a) + getInt(b), n)
of mSubI, mSubI64: 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 mMulI, mMulI64: result = newIntNodeT(getInt(a) * getInt(b), n)
of mMinI, mMinI64: of mMinI:
if getInt(a) > getInt(b): result = newIntNodeT(getInt(b), n) if getInt(a) > getInt(b): result = newIntNodeT(getInt(b), n)
else: result = newIntNodeT(getInt(a), n) else: result = newIntNodeT(getInt(a), n)
of mMaxI, mMaxI64: of mMaxI:
if getInt(a) > getInt(b): result = newIntNodeT(getInt(a), n) if getInt(a) > getInt(b): result = newIntNodeT(getInt(a), n)
else: result = newIntNodeT(getInt(b), n) else: result = newIntNodeT(getInt(b), n)
of mShlI, mShlI64: of mShlI, mShlI64:
@ -420,7 +434,8 @@ proc evalOp(m: TMagic, n, a, b, c: PNode): PNode =
mExit, mInc, ast.mDec, mEcho, mSwap, mAppendStrCh, mExit, mInc, ast.mDec, mEcho, mSwap, mAppendStrCh,
mAppendStrStr, mAppendSeqElem, mSetLengthStr, mSetLengthSeq, mAppendStrStr, mAppendSeqElem, mSetLengthStr, mSetLengthSeq,
mParseExprToAst, mParseStmtToAst, mExpandToAst, mTypeTrait, mDotDot, mParseExprToAst, mParseStmtToAst, mExpandToAst, mTypeTrait, mDotDot,
mNLen..mNError, mEqRef, mSlurp, mStaticExec, mNGenSym, mSpawn, mParallel: mNLen..mNError, mEqRef, mSlurp, mStaticExec, mNGenSym, mSpawn,
mParallel, mPlugin:
discard discard
else: internalError(a.info, "evalOp(" & $m & ')') else: internalError(a.info, "evalOp(" & $m & ')')
@ -513,7 +528,7 @@ proc rangeCheck(n: PNode, value: BiggestInt) =
proc foldConv*(n, a: PNode; check = false): PNode = proc foldConv*(n, a: PNode; check = false): PNode =
# XXX range checks? # XXX range checks?
case skipTypes(n.typ, abstractRange).kind case skipTypes(n.typ, abstractRange).kind
of tyInt..tyInt64: of tyInt..tyInt64, tyUInt..tyUInt64:
case skipTypes(a.typ, abstractRange).kind case skipTypes(a.typ, abstractRange).kind
of tyFloat..tyFloat64: of tyFloat..tyFloat64:
result = newIntNodeT(int(getFloat(a)), n) result = newIntNodeT(int(getFloat(a)), n)
@ -640,7 +655,7 @@ proc getConstExpr(m: PSym, n: PNode): PNode =
result = copyNode(n) result = copyNode(n)
of nkIfExpr: of nkIfExpr:
result = getConstIfExpr(m, n) result = getConstIfExpr(m, n)
of nkCall, nkCommand, nkCallStrLit, nkPrefix, nkInfix: of nkCallKinds:
if n.sons[0].kind != nkSym: return if n.sons[0].kind != nkSym: return
var s = n.sons[0].sym var s = n.sons[0].sym
if s.kind != skProc: return if s.kind != skProc: return

View file

@ -176,7 +176,9 @@ proc instantiateProcType(c: PContext, pt: TIdTable,
for i in 1 .. <result.len: for i in 1 .. <result.len:
# twrong_field_caching requires these 'resetIdTable' calls: # twrong_field_caching requires these 'resetIdTable' calls:
if i > 1: resetIdTable(cl.symMap) if i > 1:
resetIdTable(cl.symMap)
resetIdTable(cl.localCache)
result.sons[i] = replaceTypeVarsT(cl, result.sons[i]) result.sons[i] = replaceTypeVarsT(cl, result.sons[i])
propagateToOwner(result, result.sons[i]) propagateToOwner(result, result.sons[i])
internalAssert originalParams[i].kind == nkSym internalAssert originalParams[i].kind == nkSym
@ -185,7 +187,9 @@ proc instantiateProcType(c: PContext, pt: TIdTable,
let param = copySym(oldParam) let param = copySym(oldParam)
param.owner = prc param.owner = prc
param.typ = result.sons[i] param.typ = result.sons[i]
param.ast = oldParam.ast.copyTree if oldParam.ast != nil:
param.ast = fitNode(c, param.typ, oldParam.ast)
# don't be lazy here and call replaceTypeVarsN(cl, originalParams[i])! # don't be lazy here and call replaceTypeVarsN(cl, originalParams[i])!
result.n.sons[i] = newSymNode(param) result.n.sons[i] = newSymNode(param)
addDecl(c, param) addDecl(c, param)
@ -196,6 +200,7 @@ proc instantiateProcType(c: PContext, pt: TIdTable,
addDecl(c, result.n.sons[i].sym) addDecl(c, result.n.sons[i].sym)
resetIdTable(cl.symMap) resetIdTable(cl.symMap)
resetIdTable(cl.localCache)
result.sons[0] = replaceTypeVarsT(cl, result.sons[0]) result.sons[0] = replaceTypeVarsT(cl, result.sons[0])
result.n.sons[0] = originalParams[0].copyTree result.n.sons[0] = originalParams[0].copyTree

View file

@ -34,7 +34,7 @@ proc ithField(n: PNode, field: int): PSym =
else: discard else: discard
proc annotateType*(n: PNode, t: PType) = 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' # Note: x can be unequal to t and we need to be careful to use 't'
# to not to skip tyGenericInst # to not to skip tyGenericInst
case n.kind case n.kind

View file

@ -54,7 +54,7 @@ proc evalTypeTrait(trait: PNode, operand: PType, context: PSym): PNode =
result.typ = newType(tyString, context) result.typ = newType(tyString, context)
result.info = trait.info result.info = trait.info
of "arity": of "arity":
result = newIntNode(nkIntLit, typ.n.len-1) result = newIntNode(nkIntLit, typ.len - ord(typ.kind==tyProc))
result.typ = newType(tyInt, context) result.typ = newType(tyInt, context)
result.info = trait.info result.info = trait.info
else: else:
@ -101,34 +101,6 @@ proc semBindSym(c: PContext, n: PNode): PNode =
else: else:
localError(n.sons[1].info, errUndeclaredIdentifier, sl.strVal) 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 semShallowCopy(c: PContext, n: PNode, flags: TExprFlags): PNode
proc isStrangeArray(t: PType): bool = proc isStrangeArray(t: PType): bool =
@ -161,7 +133,6 @@ proc magicsAfterOverloadResolution(c: PContext, n: PNode,
of mHigh, mLow: result = semLowHigh(c, n, n[0].sym.magic) of mHigh, mLow: result = semLowHigh(c, n, n[0].sym.magic)
of mShallowCopy: result = semShallowCopy(c, n, flags) of mShallowCopy: result = semShallowCopy(c, n, flags)
of mNBindSym: result = semBindSym(c, n) of mNBindSym: result = semBindSym(c, n)
of mLocals: result = semLocals(c, n)
of mProcCall: of mProcCall:
result = n result = n
result.typ = n[1].typ result.typ = n[1].typ
@ -196,4 +167,11 @@ proc magicsAfterOverloadResolution(c: PContext, n: PNode,
result.add newSymNode(createMagic("-", mSubI), n.info) result.add newSymNode(createMagic("-", mSubI), n.info)
result.add lenExprB result.add lenExprB
result.add n.sons[1] 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 else: result = n

View file

@ -194,8 +194,38 @@ proc warnAboutGcUnsafe(n: PNode) =
#assert false #assert false
message(n.info, warnGcUnsafe, renderTree(n)) message(n.info, warnGcUnsafe, renderTree(n))
template markGcUnsafe(a: PEffects) = proc markGcUnsafe(a: PEffects; reason: PSym) =
a.gcUnsafe = true 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) = proc useVar(a: PEffects, n: PNode) =
let s = n.sym let s = n.sym
@ -210,8 +240,8 @@ proc useVar(a: PEffects, n: PNode) =
if {sfGlobal, sfThread} * s.flags == {sfGlobal} and s.kind in {skVar, skLet}: if {sfGlobal, sfThread} * s.flags == {sfGlobal} and s.kind in {skVar, skLet}:
if s.guard != nil: guardGlobal(a, n, s.guard) if s.guard != nil: guardGlobal(a, n, s.guard)
if (tfHasGCedMem in s.typ.flags or s.typ.isGCedMem): if (tfHasGCedMem in s.typ.flags or s.typ.isGCedMem):
if warnGcUnsafe in gNotes: warnAboutGcUnsafe(n) #if warnGcUnsafe in gNotes: warnAboutGcUnsafe(n)
markGcUnsafe(a) markGcUnsafe(a, s)
type type
TIntersection = seq[tuple[id, count: int]] # a simple count table TIntersection = seq[tuple[id, count: int]] # a simple count table
@ -450,7 +480,7 @@ proc propagateEffects(tracked: PEffects, n: PNode, s: PSym) =
if notGcSafe(s.typ) and sfImportc notin s.flags: if notGcSafe(s.typ) and sfImportc notin s.flags:
if warnGcUnsafe in gNotes: warnAboutGcUnsafe(n) if warnGcUnsafe in gNotes: warnAboutGcUnsafe(n)
markGcUnsafe(tracked) markGcUnsafe(tracked, s)
mergeLockLevels(tracked, n, s.getLockLevel) mergeLockLevels(tracked, n, s.getLockLevel)
proc notNilCheck(tracked: PEffects, n: PNode, paramType: PType) = proc notNilCheck(tracked: PEffects, n: PNode, paramType: PType) =
@ -504,13 +534,13 @@ proc trackOperand(tracked: PEffects, n: PNode, paramType: PType) =
# assume GcUnsafe unless in its type; 'forward' does not matter: # assume GcUnsafe unless in its type; 'forward' does not matter:
if notGcSafe(op) and not isOwnedProcVar(a, tracked.owner): if notGcSafe(op) and not isOwnedProcVar(a, tracked.owner):
if warnGcUnsafe in gNotes: warnAboutGcUnsafe(n) if warnGcUnsafe in gNotes: warnAboutGcUnsafe(n)
markGcUnsafe(tracked) markGcUnsafe(tracked, a)
else: else:
mergeEffects(tracked, effectList.sons[exceptionEffects], n) mergeEffects(tracked, effectList.sons[exceptionEffects], n)
mergeTags(tracked, effectList.sons[tagEffects], n) mergeTags(tracked, effectList.sons[tagEffects], n)
if notGcSafe(op): if notGcSafe(op):
if warnGcUnsafe in gNotes: warnAboutGcUnsafe(n) if warnGcUnsafe in gNotes: warnAboutGcUnsafe(n)
markGcUnsafe(tracked) markGcUnsafe(tracked, a)
notNilCheck(tracked, n, paramType) notNilCheck(tracked, n, paramType)
proc breaksBlock(n: PNode): bool = proc breaksBlock(n: PNode): bool =
@ -530,7 +560,10 @@ proc trackCase(tracked: PEffects, n: PNode) =
track(tracked, n.sons[0]) track(tracked, n.sons[0])
let oldState = tracked.init.len let oldState = tracked.init.len
let oldFacts = tracked.guards.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 inter: TIntersection = @[]
var toCover = 0 var toCover = 0
for i in 1.. <n.len: for i in 1.. <n.len:
@ -545,13 +578,8 @@ proc trackCase(tracked: PEffects, n: PNode) =
for i in oldState.. <tracked.init.len: for i in oldState.. <tracked.init.len:
addToIntersection(inter, tracked.init[i]) 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) setLen(tracked.init, oldState)
if exh: if not stringCase or lastSon(n).kind == nkElse:
for id, count in items(inter): for id, count in items(inter):
if count >= toCover: tracked.init.add id if count >= toCover: tracked.init.add id
# else we can't merge # else we can't merge
@ -658,7 +686,7 @@ proc track(tracked: PEffects, n: PNode) =
# and it's not a recursive call: # and it's not a recursive call:
if not (a.kind == nkSym and a.sym == tracked.owner): if not (a.kind == nkSym and a.sym == tracked.owner):
warnAboutGcUnsafe(n) warnAboutGcUnsafe(n)
markGcUnsafe(tracked) markGcUnsafe(tracked, a)
for i in 1 .. <len(n): trackOperand(tracked, n.sons[i], paramType(op, i)) 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}: if a.kind == nkSym and a.sym.magic in {mNew, mNewFinalize, mNewSeq}:
# may not look like an assignment, but it is: # may not look like an assignment, but it is:
@ -684,8 +712,8 @@ proc track(tracked: PEffects, n: PNode) =
of nkVarSection, nkLetSection: of nkVarSection, nkLetSection:
for child in n: for child in n:
let last = lastSon(child) let last = lastSon(child)
if last.kind != nkEmpty: track(tracked, last)
if child.kind == nkIdentDefs and last.kind != nkEmpty: if child.kind == nkIdentDefs and last.kind != nkEmpty:
track(tracked, last)
for i in 0 .. child.len-3: for i in 0 .. child.len-3:
initVar(tracked, child.sons[i], volatileCheck=false) initVar(tracked, child.sons[i], volatileCheck=false)
addAsgnFact(tracked.guards, child.sons[i], last) addAsgnFact(tracked.guards, child.sons[i], last)
@ -734,7 +762,7 @@ proc track(tracked: PEffects, n: PNode) =
setLen(tracked.locked, oldLocked) setLen(tracked.locked, oldLocked)
tracked.currLockLevel = oldLockLevel tracked.currLockLevel = oldLockLevel
of nkTypeSection, nkProcDef, nkConverterDef, nkMethodDef, nkIteratorDef, of nkTypeSection, nkProcDef, nkConverterDef, nkMethodDef, nkIteratorDef,
nkMacroDef, nkTemplateDef: nkMacroDef, nkTemplateDef, nkLambda, nkDo:
discard discard
else: else:
for i in 0 .. <safeLen(n): track(tracked, n.sons[i]) for i in 0 .. <safeLen(n): track(tracked, n.sons[i])
@ -853,9 +881,11 @@ proc trackProc*(s: PSym, body: PNode) =
if sfThread in s.flags and t.gcUnsafe: if sfThread in s.flags and t.gcUnsafe:
if optThreads in gGlobalOptions and optThreadAnalysis in gGlobalOptions: if optThreads in gGlobalOptions and optThreadAnalysis in gGlobalOptions:
localError(s.info, "'$1' is not GC-safe" % s.name.s) #localError(s.info, "'$1' is not GC-safe" % s.name.s)
listGcUnsafety(s, onlyWarning=false)
else: else:
localError(s.info, warnGcUnsafe2, s.name.s) listGcUnsafety(s, onlyWarning=true)
#localError(s.info, warnGcUnsafe2, s.name.s)
if not t.gcUnsafe: if not t.gcUnsafe:
s.typ.flags.incl tfGcSafe s.typ.flags.incl tfGcSafe
if s.typ.lockLevel == UnspecifiedLockLevel: if s.typ.lockLevel == UnspecifiedLockLevel:

View file

@ -102,10 +102,6 @@ proc semDestructorCheck(c: PContext, n: PNode, flags: TExprFlags) {.inline.} =
c.p.owner.kind notin {skTemplate, skMacro}: c.p.owner.kind notin {skTemplate, skMacro}:
localError(n.info, errGenerated, "value expected, but got a type") localError(n.info, errGenerated, "value expected, but got a type")
proc newDeref(n: PNode): PNode {.inline.} =
result = newNodeIT(nkHiddenDeref, n.info, n.typ.sons[0])
addSon(result, n)
proc semExprBranch(c: PContext, n: PNode): PNode = proc semExprBranch(c: PContext, n: PNode): PNode =
result = semExpr(c, n) result = semExpr(c, n)
if result.typ != nil: 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: elif tfNotNil in v.typ.flags and tfNotNil notin v.ast.typ.flags:
message(v.info, warnProveInit, v.name.s) message(v.info, warnProveInit, v.name.s)
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 = proc semVarOrLet(c: PContext, n: PNode, symkind: TSymKind): PNode =
var b: PNode var b: PNode
result = copyNode(n) result = copyNode(n)
@ -396,14 +430,15 @@ proc semVarOrLet(c: PContext, n: PNode, symkind: TSymKind): PNode =
newSons(b, length) newSons(b, length)
b.sons[length-2] = a.sons[length-2] # keep type desc for doc generator b.sons[length-2] = a.sons[length-2] # keep type desc for doc generator
b.sons[length-1] = def b.sons[length-1] = def
addSon(result, b) addToVarSection(c, result, n, b)
elif tup.kind == tyTuple and def.kind == nkPar and elif tup.kind == tyTuple and def.kind == nkPar and
a.kind == nkIdentDefs and a.len > 3: a.kind == nkIdentDefs and a.len > 3:
message(a.info, warnEachIdentIsTuple) message(a.info, warnEachIdentIsTuple)
for j in countup(0, length-3): for j in countup(0, length-3):
var v = semIdentDef(c, a.sons[j], symkind) var v = semIdentDef(c, a.sons[j], symkind)
if sfGenSym notin v.flags: addInterfaceDecl(c, v) if sfGenSym notin v.flags and not isDiscardUnderscore(v):
addInterfaceDecl(c, v)
when oKeepVariableNames: when oKeepVariableNames:
if c.inUnrolledContext > 0: v.flags.incl(sfShadowed) if c.inUnrolledContext > 0: v.flags.incl(sfShadowed)
else: else:
@ -429,7 +464,7 @@ proc semVarOrLet(c: PContext, n: PNode, symkind: TSymKind): PNode =
addSon(b, newSymNode(v)) addSon(b, newSymNode(v))
addSon(b, a.sons[length-2]) # keep type desc for doc generator addSon(b, a.sons[length-2]) # keep type desc for doc generator
addSon(b, copyTree(def)) addSon(b, copyTree(def))
addSon(result, b) addToVarSection(c, result, n, b)
else: else:
if def.kind == nkPar: v.ast = def[j] if def.kind == nkPar: v.ast = def[j]
v.typ = tup.sons[j] v.typ = tup.sons[j]
@ -518,7 +553,8 @@ proc semForVars(c: PContext, n: PNode): PNode =
if getCurrOwner().kind == skModule: incl(v.flags, sfGlobal) if getCurrOwner().kind == skModule: incl(v.flags, sfGlobal)
v.typ = iter.sons[i] v.typ = iter.sons[i]
n.sons[i] = newSymNode(v) 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) inc(c.p.nestedLoopCounter)
n.sons[length-1] = semStmt(c, n.sons[length-1]) n.sons[length-1] = semStmt(c, n.sons[length-1])
dec(c.p.nestedLoopCounter) dec(c.p.nestedLoopCounter)
@ -654,6 +690,16 @@ proc typeSectionRightSidePass(c: PContext, n: PNode) =
#debug s.typ #debug s.typ
s.ast = a s.ast = a
popOwner() 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) = proc checkForMetaFields(n: PNode) =
template checkMeta(t) = template checkMeta(t) =
@ -697,16 +743,6 @@ proc typeSectionFinalPass(c: PContext, n: PNode) =
checkConstructedType(s.info, s.typ) checkConstructedType(s.info, s.typ)
if s.typ.kind in {tyObject, tyTuple} and not s.typ.n.isNil: if s.typ.kind in {tyObject, tyTuple} and not s.typ.n.isNil:
checkForMetaFields(s.typ.n) checkForMetaFields(s.typ.n)
let aa = a.sons[2]
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 = proc semTypeSection(c: PContext, n: PNode): PNode =
## Processes a type section. This must be done in separate passes, in order ## Processes a type section. This must be done in separate passes, in order
@ -909,11 +945,12 @@ proc maybeAddResult(c: PContext, s: PSym, n: PNode) =
proc semOverride(c: PContext, s: PSym, n: PNode) = proc semOverride(c: PContext, s: PSym, n: PNode) =
case s.name.s.normalize case s.name.s.normalize
of "destroy": of "destroy", "=destroy":
doDestructorStuff(c, s, n) doDestructorStuff(c, s, n)
if not experimentalMode(c): if not experimentalMode(c):
localError n.info, "use the {.experimental.} pragma to enable destructors" 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 if s.typ.len == 2 and
s.typ.sons[1].skipTypes(abstractInst).kind in {tyRef, tyPtr} and s.typ.sons[1].skipTypes(abstractInst).kind in {tyRef, tyPtr} and
sameType(s.typ.sons[1], s.typ.sons[0]): sameType(s.typ.sons[1], s.typ.sons[0]):
@ -935,10 +972,35 @@ proc semOverride(c: PContext, s: PSym, n: PNode) =
else: else:
localError(n.info, errGenerated, localError(n.info, errGenerated,
"signature for 'deepCopy' must be proc[T: ptr|ref](x: T): T") "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 type
TProcCompilationSteps = enum TProcCompilationSteps = enum
@ -970,7 +1032,7 @@ proc semProcAux(c: PContext, n: PNode, kind: TSymKind,
s = semIdentDef(c, n.sons[0], kind) s = semIdentDef(c, n.sons[0], kind)
n.sons[namePos] = newSymNode(s) n.sons[namePos] = newSymNode(s)
s.ast = n s.ast = n
s.scope = c.currentScope #s.scope = c.currentScope
if sfNoForward in c.module.flags and if sfNoForward in c.module.flags and
sfSystemModule notin c.module.flags: sfSystemModule notin c.module.flags:
@ -982,14 +1044,14 @@ proc semProcAux(c: PContext, n: PNode, kind: TSymKind,
s.owner = getCurrOwner() s.owner = getCurrOwner()
typeIsDetermined = s.typ == nil typeIsDetermined = s.typ == nil
s.ast = n s.ast = n
s.scope = c.currentScope #s.scope = c.currentScope
# if typeIsDetermined: assert phase == stepCompileBody # if typeIsDetermined: assert phase == stepCompileBody
# else: assert phase == stepDetermineType # else: assert phase == stepDetermineType
# before compiling the proc body, set as current the scope # before compiling the proc body, set as current the scope
# where the proc was declared # where the proc was declared
let oldScope = c.currentScope let oldScope = c.currentScope
c.currentScope = s.scope #c.currentScope = s.scope
pushOwner(s) pushOwner(s)
openScope(c) openScope(c)
var gp: PNode var gp: PNode
@ -1014,7 +1076,7 @@ proc semProcAux(c: PContext, n: PNode, kind: TSymKind,
if s.kind in skIterators: if s.kind in skIterators:
s.typ.flags.incl(tfIterator) s.typ.flags.incl(tfIterator)
var proto = searchForProc(c, s.scope, s) var proto = searchForProc(c, oldScope, s)
if proto == nil: if proto == nil:
if s.kind == skClosureIterator: s.typ.callConv = ccClosure if s.kind == skClosureIterator: s.typ.callConv = ccClosure
else: s.typ.callConv = lastOptionEntry(c).defaultCC else: s.typ.callConv = lastOptionEntry(c).defaultCC
@ -1022,10 +1084,10 @@ proc semProcAux(c: PContext, n: PNode, kind: TSymKind,
if sfGenSym in s.flags: discard if sfGenSym in s.flags: discard
elif kind in OverloadableSyms: elif kind in OverloadableSyms:
if not typeIsDetermined: if not typeIsDetermined:
addInterfaceOverloadableSymAt(c, s.scope, s) addInterfaceOverloadableSymAt(c, oldScope, s)
else: else:
if not typeIsDetermined: if not typeIsDetermined:
addInterfaceDeclAt(c, s.scope, s) addInterfaceDeclAt(c, oldScope, s)
if n.sons[pragmasPos].kind != nkEmpty: if n.sons[pragmasPos].kind != nkEmpty:
pragma(c, s, n.sons[pragmasPos], validPragmas) pragma(c, s, n.sons[pragmasPos], validPragmas)
else: else:
@ -1055,7 +1117,7 @@ proc semProcAux(c: PContext, n: PNode, kind: TSymKind,
popOwner() popOwner()
pushOwner(s) pushOwner(s)
s.options = gOptions 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: if n.sons[bodyPos].kind != nkEmpty:
# for DLL generation it is annoying to check for sfImportc! # for DLL generation it is annoying to check for sfImportc!
if sfBorrow in s.flags: if sfBorrow in s.flags:
@ -1093,7 +1155,7 @@ proc semProcAux(c: PContext, n: PNode, kind: TSymKind,
elif sfBorrow in s.flags: semBorrow(c, n, s) elif sfBorrow in s.flags: semBorrow(c, n, s)
sideEffectsCheck(c, s) sideEffectsCheck(c, s)
closeScope(c) # close scope for parameters closeScope(c) # close scope for parameters
c.currentScope = oldScope # c.currentScope = oldScope
popOwner() popOwner()
if n.sons[patternPos].kind != nkEmpty: if n.sons[patternPos].kind != nkEmpty:
c.patterns.add(s) c.patterns.add(s)

View file

@ -482,7 +482,7 @@ proc semTemplateDef(c: PContext, n: PNode): PNode =
s = semIdentVis(c, skTemplate, n.sons[0], {}) s = semIdentVis(c, skTemplate, n.sons[0], {})
styleCheckDef(s) styleCheckDef(s)
# check parameter list: # check parameter list:
s.scope = c.currentScope #s.scope = c.currentScope
pushOwner(s) pushOwner(s)
openScope(c) openScope(c)
n.sons[namePos] = newSymNode(s, n.sons[namePos].info) n.sons[namePos] = newSymNode(s, n.sons[namePos].info)

View file

@ -261,7 +261,8 @@ proc semArray(c: PContext, n: PNode, prev: PType): PType =
if not isOrdinalType(indx): if not isOrdinalType(indx):
localError(n.sons[1].info, errOrdinalTypeExpected) localError(n.sons[1].info, errOrdinalTypeExpected)
elif enumHasHoles(indx): elif enumHasHoles(indx):
localError(n.sons[1].info, errEnumXHasHoles, indx.sym.name.s) localError(n.sons[1].info, errEnumXHasHoles,
typeToString(indx.skipTypes({tyRange})))
base = semTypeNode(c, n.sons[2], nil) base = semTypeNode(c, n.sons[2], nil)
addSonSkipIntLit(result, base) addSonSkipIntLit(result, base)
else: else:
@ -593,7 +594,7 @@ proc semRecordNodeAux(c: PContext, n: PNode, check: var IntSet, pos: var int,
f.position = pos f.position = pos
if (rec != nil) and ({sfImportc, sfExportc} * rec.flags != {}) and if (rec != nil) and ({sfImportc, sfExportc} * rec.flags != {}) and
(f.loc.r == nil): (f.loc.r == nil):
f.loc.r = toRope(f.name.s) f.loc.r = rope(f.name.s)
f.flags = f.flags + ({sfImportc, sfExportc} * rec.flags) f.flags = f.flags + ({sfImportc, sfExportc} * rec.flags)
inc(pos) inc(pos)
if containsOrIncl(check, f.name.id): if containsOrIncl(check, f.name.id):
@ -628,7 +629,7 @@ proc skipGenericInvocation(t: PType): PType {.inline.} =
result = t result = t
if result.kind == tyGenericInvocation: if result.kind == tyGenericInvocation:
result = result.sons[0] result = result.sons[0]
if result.kind == tyGenericBody: while result.kind in {tyGenericInst, tyGenericBody}:
result = lastSon(result) result = lastSon(result)
proc addInheritedFields(c: PContext, check: var IntSet, pos: var int, proc addInheritedFields(c: PContext, check: var IntSet, pos: var int,
@ -646,7 +647,10 @@ proc semObjectNode(c: PContext, n: PNode, prev: PType): PType =
# n.sons[0] contains the pragmas (if any). We process these later... # n.sons[0] contains the pragmas (if any). We process these later...
checkSonsLen(n, 3) checkSonsLen(n, 3)
if n.sons[1].kind != nkEmpty: if n.sons[1].kind != nkEmpty:
base = skipTypes(semTypeNode(c, n.sons[1].sons[0], nil), skipPtrs) base = skipTypesOrNil(semTypeNode(c, n.sons[1].sons[0], nil), skipPtrs)
if base.isNil:
localError(n.info, errIllegalRecursionInTypeX, "object")
else:
var concreteBase = skipGenericInvocation(base).skipTypes(skipPtrs) var concreteBase = skipGenericInvocation(base).skipTypes(skipPtrs)
if concreteBase.kind == tyObject and tfFinal notin concreteBase.flags: if concreteBase.kind == tyObject and tfFinal notin concreteBase.flags:
addInheritedFields(c, check, pos, concreteBase) addInheritedFields(c, check, pos, concreteBase)
@ -786,7 +790,7 @@ proc liftParamType(c: PContext, procKind: TSymKind, genericParams: PNode,
@[newTypeS(paramType.kind, c)]) @[newTypeS(paramType.kind, c)])
result = addImplicitGeneric(typ) result = addImplicitGeneric(typ)
else: else:
for i in 0 .. <paramType.sons.len: for i in 0 .. <paramType.len:
if paramType.sons[i] == paramType: if paramType.sons[i] == paramType:
globalError(info, errIllegalRecursionInTypeX, typeToString(paramType)) globalError(info, errIllegalRecursionInTypeX, typeToString(paramType))
var lifted = liftingWalk(paramType.sons[i]) var lifted = liftingWalk(paramType.sons[i])
@ -831,7 +835,7 @@ proc liftParamType(c: PContext, procKind: TSymKind, genericParams: PNode,
cp.kind = tyUserTypeClassInst cp.kind = tyUserTypeClassInst
return addImplicitGeneric(cp) return addImplicitGeneric(cp)
for i in 1 .. (paramType.sons.len - 2): for i in 1 .. paramType.len-2:
var lifted = liftingWalk(paramType.sons[i]) var lifted = liftingWalk(paramType.sons[i])
if lifted != nil: if lifted != nil:
paramType.sons[i] = lifted paramType.sons[i] = lifted
@ -844,7 +848,7 @@ proc liftParamType(c: PContext, procKind: TSymKind, genericParams: PNode,
result.shouldHaveMeta result.shouldHaveMeta
of tyGenericInvocation: of tyGenericInvocation:
for i in 1 .. <paramType.sonsLen: for i in 1 .. <paramType.len:
let lifted = liftingWalk(paramType.sons[i]) let lifted = liftingWalk(paramType.sons[i])
if lifted != nil: paramType.sons[i] = lifted if lifted != nil: paramType.sons[i] = lifted
when false: when false:

View file

@ -16,9 +16,9 @@ const
proc sharedPtrCheck(info: TLineInfo, t: PType) = proc sharedPtrCheck(info: TLineInfo, t: PType) =
if t.kind == tyPtr and t.len > 1: 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) 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: if tfHasGCedMem in t.flags or t.isGCedMem:
localError(info, errGenerated, localError(info, errGenerated,
"shared memory may not refer to GC'ed thread local memory") "shared memory may not refer to GC'ed thread local memory")
@ -233,6 +233,8 @@ proc instCopyType*(cl: var TReplTypeVars, t: PType): PType =
# XXX: relying on allowMetaTypes is a kludge # XXX: relying on allowMetaTypes is a kludge
result = copyType(t, t.owner, cl.allowMetaTypes) result = copyType(t, t.owner, cl.allowMetaTypes)
result.flags.incl tfFromGeneric result.flags.incl tfFromGeneric
if not (t.kind in tyMetaTypes or
(t.kind == tyStatic and t.n == nil)):
result.flags.excl tfInstClearedFlags result.flags.excl tfInstClearedFlags
proc handleGenericInvocation(cl: var TReplTypeVars, t: PType): PType = proc handleGenericInvocation(cl: var TReplTypeVars, t: PType): PType =
@ -307,7 +309,13 @@ proc handleGenericInvocation(cl: var TReplTypeVars, t: PType): PType =
if dc != nil and sfFromGeneric notin newbody.deepCopy.flags: if dc != nil and sfFromGeneric notin newbody.deepCopy.flags:
# 'deepCopy' needs to be instantiated for # 'deepCopy' needs to be instantiated for
# generics *when the type is constructed*: # 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) = proc eraseVoidParams*(t: PType) =
# transform '(): void' into '()' because old parts of the compiler really # transform '(): void' into '()' because old parts of the compiler really
@ -412,15 +420,23 @@ proc replaceTypeVarsTAux(cl: var TReplTypeVars, t: PType): PType =
result = t result = t
of tyGenericInst: of tyGenericInst:
result = PType(idTableGet(cl.localCache, t))
if result != nil: return result
result = instCopyType(cl, t) result = instCopyType(cl, t)
idTablePut(cl.localCache, t, result)
for i in 1 .. <result.sonsLen: for i in 1 .. <result.sonsLen:
result.sons[i] = replaceTypeVarsT(cl, result.sons[i]) result.sons[i] = replaceTypeVarsT(cl, result.sons[i])
propagateToOwner(result, result.lastSon) propagateToOwner(result, result.lastSon)
else: else:
if containsGenericType(t): if containsGenericType(t):
#if not cl.allowMetaTypes:
result = PType(idTableGet(cl.localCache, t))
if result != nil: return result
result = instCopyType(cl, t) result = instCopyType(cl, t)
result.size = -1 # needs to be recomputed result.size = -1 # needs to be recomputed
#if not cl.allowMetaTypes:
idTablePut(cl.localCache, t, result)
for i in countup(0, sonsLen(result) - 1): for i in countup(0, sonsLen(result) - 1):
if result.sons[i] != nil: if result.sons[i] != nil:

View file

@ -99,9 +99,12 @@ proc initCandidate*(ctx: PContext, c: var TCandidate, callee: PSym,
c.calleeSym = callee c.calleeSym = callee
if callee.kind in skProcKinds and calleeScope == -1: if callee.kind in skProcKinds and calleeScope == -1:
if callee.originatingModule == ctx.module: if callee.originatingModule == ctx.module:
let rootSym = if sfFromGeneric notin callee.flags: callee c.calleeScope = 2
else: callee.owner var owner = callee
c.calleeScope = rootSym.scope.depthLevel while true:
owner = owner.skipGenericOwner
if owner.kind == skModule: break
inc c.calleeScope
else: else:
c.calleeScope = 1 c.calleeScope = 1
else: else:
@ -144,6 +147,7 @@ proc copyCandidate(a: var TCandidate, b: TCandidate) =
proc sumGeneric(t: PType): int = proc sumGeneric(t: PType): int =
var t = t var t = t
var isvar = 1
while true: while true:
case t.kind case t.kind
of tyGenericInst, tyArray, tyRef, tyPtr, tyDistinct, tyArrayConstr, of tyGenericInst, tyArray, tyRef, tyPtr, tyDistinct, tyArrayConstr,
@ -151,18 +155,20 @@ proc sumGeneric(t: PType): int =
t = t.lastSon t = t.lastSon
inc result inc result
of tyVar: of tyVar:
# but do not make 'var T' more specific than 'T'!
t = t.sons[0] t = t.sons[0]
inc result
inc isvar
of tyGenericInvocation, tyTuple: of tyGenericInvocation, tyTuple:
result = ord(t.kind == tyGenericInvocation) result += ord(t.kind == tyGenericInvocation)
for i in 0 .. <t.len: result += t.sons[i].sumGeneric for i in 0 .. <t.len: result += t.sons[i].sumGeneric
break break
of tyGenericParam, tyExpr, tyStatic, tyStmt, tyTypeDesc: break of tyGenericParam, tyExpr, tyStatic, tyStmt, tyTypeDesc: break
of tyBool, tyChar, tyEnum, tyObject, tyProc, tyPointer, of tyBool, tyChar, tyEnum, tyObject, tyProc, tyPointer,
tyString, tyCString, tyInt..tyInt64, tyFloat..tyFloat128, tyString, tyCString, tyInt..tyInt64, tyFloat..tyFloat128,
tyUInt..tyUInt64: tyUInt..tyUInt64:
return 1 return isvar
else: return 0 else:
return 0
#var ggDebug: bool #var ggDebug: bool
@ -916,6 +922,7 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation =
of tyAnd: of tyAnd:
considerPreviousT: considerPreviousT:
result = isEqual
for branch in f.sons: for branch in f.sons:
let x = typeRel(c, branch, aOrig) let x = typeRel(c, branch, aOrig)
if x < isSubtype: return isNone if x < isSubtype: return isNone
@ -1105,8 +1112,10 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation =
localError(f.n.info, errTypeExpected) localError(f.n.info, errTypeExpected)
result = isNone result = isNone
of tyNone:
if a.kind == tyNone: result = isEqual
else: else:
internalAssert false internalError " unknown type kind " & $f.kind
proc cmpTypes*(c: PContext, f, a: PType): TTypeRelation = proc cmpTypes*(c: PContext, f, a: PType): TTypeRelation =
var m: TCandidate var m: TCandidate
@ -1191,15 +1200,6 @@ proc isInlineIterator*(t: PType): bool =
result = t.kind == tyIter or result = t.kind == tyIter or
(t.kind == tyBuiltInTypeClass and t.base.kind == tyIter) (t.kind == tyBuiltInTypeClass and t.base.kind == tyIter)
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 incMatches(m: var TCandidate; r: TTypeRelation; convMatch = 1) = proc incMatches(m: var TCandidate; r: TTypeRelation; convMatch = 1) =
case r case r
of isConvertible, isIntConv: inc(m.convMatches, convMatch) of isConvertible, isIntConv: inc(m.convMatches, convMatch)
@ -1304,7 +1304,7 @@ proc paramTypesMatchAux(m: var TCandidate, f, argType: PType,
if arg.typ == nil: if arg.typ == nil:
result = arg result = arg
elif skipTypes(arg.typ, abstractVar-{tyTypeDesc}).kind == tyTuple: elif skipTypes(arg.typ, abstractVar-{tyTypeDesc}).kind == tyTuple:
result = implicitConv(nkHiddenStdConv, f, copyTree(arg), m, c) result = implicitConv(nkHiddenSubConv, f, arg, m, c)
elif arg.typ.isEmptyContainer: elif arg.typ.isEmptyContainer:
result = arg.copyTree result = arg.copyTree
result.typ = getInstantiatedType(c, arg, m, f) result.typ = getInstantiatedType(c, arg, m, f)
@ -1319,7 +1319,7 @@ proc paramTypesMatchAux(m: var TCandidate, f, argType: PType,
inc(m.exactMatches) inc(m.exactMatches)
result = arg result = arg
if skipTypes(f, abstractVar-{tyTypeDesc}).kind in {tyTuple}: if skipTypes(f, abstractVar-{tyTypeDesc}).kind in {tyTuple}:
result = implicitConv(nkHiddenStdConv, f, arg, m, c) result = implicitConv(nkHiddenSubConv, f, arg, m, c)
of isNone: of isNone:
# do not do this in ``typeRel`` as it then can't infere T in ``ref T``: # do not do this in ``typeRel`` as it then can't infere T in ``ref T``:
if a.kind in {tyProxy, tyUnknown}: if a.kind in {tyProxy, tyUnknown}:
@ -1419,9 +1419,12 @@ proc prepareOperand(c: PContext; formal: PType; a: PNode): PNode =
# a.typ == nil is valid # a.typ == nil is valid
result = a result = a
elif a.typ.isNil: 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} let flags = if formal.kind == tyIter: {efDetermineType, efWantIterator}
elif formal.kind == tyStmt: {efDetermineType, efWantStmt} else: {efDetermineType, efAllowStmt}
else: {efDetermineType} #elif formal.kind == tyStmt: {efDetermineType, efWantStmt}
#else: {efDetermineType}
result = c.semOperand(c, a, flags) result = c.semOperand(c, a, flags)
else: else:
result = a result = a
@ -1462,9 +1465,7 @@ proc matchesAux(c: PContext, n, nOrig: PNode,
m.state = csNoMatch m.state = csNoMatch
return return
if formal.typ.kind == tyVar: if formal.typ.kind == tyVar:
if n.isLValue: if not n.isLValue:
inc(m.genericMatches, 100)
else:
m.state = csNoMatch m.state = csNoMatch
return return
@ -1570,6 +1571,8 @@ proc matchesAux(c: PContext, n, nOrig: PNode,
#assert(container == nil) #assert(container == nil)
if container.isNil: if container.isNil:
container = newNodeIT(nkBracket, n.sons[a].info, arrayConstr(c, arg)) container = newNodeIT(nkBracket, n.sons[a].info, arrayConstr(c, arg))
else:
incrIndexType(container.typ)
addSon(container, arg) addSon(container, arg)
setSon(m.call, formal.position + 1, setSon(m.call, formal.position + 1,
implicitConv(nkHiddenStdConv, formal.typ, container, m, c)) implicitConv(nkHiddenStdConv, formal.typ, container, m, c))
@ -1627,12 +1630,15 @@ proc argtypeMatches*(c: PContext, f, a: PType): bool =
# instantiate generic converters for that # instantiate generic converters for that
result = res != nil result = res != nil
proc instDeepCopy*(c: PContext; dc: PSym; t: PType; info: TLineInfo): PSym {. proc instTypeBoundOp*(c: PContext; dc: PSym; t: PType; info: TLineInfo;
procvar.} = op: TTypeAttachedOp): PSym {.procvar.} =
var m: TCandidate var m: TCandidate
initCandidate(c, m, dc.typ) initCandidate(c, m, dc.typ)
var f = dc.typ.sons[1] var f = dc.typ.sons[1]
if op == attachedDeepCopy:
if f.kind in {tyRef, tyPtr}: f = f.lastSon if f.kind in {tyRef, tyPtr}: f = f.lastSon
else:
if f.kind == tyVar: f = f.lastSon
if typeRel(m, f, t) == isNone: if typeRel(m, f, t) == isNone:
localError(info, errGenerated, "cannot instantiate 'deepCopy'") localError(info, errGenerated, "cannot instantiate 'deepCopy'")
else: else:

View file

@ -379,6 +379,9 @@ proc transformConv(c: PTransf, n: PNode): PTransNode =
result = transformSons(c, n) result = transformSons(c, n)
of tyOpenArray, tyVarargs: of tyOpenArray, tyVarargs:
result = transform(c, n.sons[1]) result = transform(c, n.sons[1])
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: of tyCString:
if source.kind == tyString: if source.kind == tyString:
result = newTransNode(nkStringToCString, n, 1) result = newTransNode(nkStringToCString, n, 1)
@ -713,8 +716,7 @@ proc transform(c: PTransf, n: PNode): PTransNode =
add(result, PTransNode(newSymNode(labl))) add(result, PTransNode(newSymNode(labl)))
of nkBreakStmt: result = transformBreak(c, n) of nkBreakStmt: result = transformBreak(c, n)
of nkWhileStmt: result = transformWhile(c, n) of nkWhileStmt: result = transformWhile(c, n)
of nkCall, nkHiddenCallConv, nkCommand, nkInfix, nkPrefix, nkPostfix, of nkCallKinds:
nkCallStrLit:
result = transformCall(c, n) result = transformCall(c, n)
of nkAddr, nkHiddenAddr: of nkAddr, nkHiddenAddr:
result = transformAddrDeref(c, n, nkDerefExpr, nkHiddenDeref) result = transformAddrDeref(c, n, nkDerefExpr, nkHiddenDeref)

View file

@ -142,7 +142,7 @@ proc swapOperands*(op: PNode) =
op.sons[2] = tmp op.sons[2] = tmp
proc isRange*(n: PNode): bool {.inline.} = 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 if n[0].kind == nkIdent and n[0].ident.id == ord(wDotDot) or
n[0].kind in {nkClosedSymChoice, nkOpenSymChoice} and n[0].kind in {nkClosedSymChoice, nkOpenSymChoice} and
n[0][1].sym.name.id == ord(wDotDot): n[0][1].sym.name.id == ord(wDotDot):

View file

@ -541,6 +541,9 @@ proc typeToString(typ: PType, prefer: TPreferedDesc = preferName): string =
of tyProc: of tyProc:
result = if tfIterator in t.flags: "iterator (" else: "proc (" result = if tfIterator in t.flags: "iterator (" else: "proc ("
for i in countup(1, sonsLen(t) - 1): for i in countup(1, sonsLen(t) - 1):
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])) add(result, typeToString(t.sons[i]))
if i < sonsLen(t) - 1: add(result, ", ") if i < sonsLen(t) - 1: add(result, ", ")
add(result, ')') add(result, ')')
@ -1436,3 +1439,45 @@ proc skipConv*(n: PNode): PNode =
proc skipConvTakeType*(n: PNode): PNode = proc skipConvTakeType*(n: PNode): PNode =
result = n.skipConv result = n.skipConv
result.typ = n.typ 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

View file

@ -16,7 +16,8 @@ import ast except getstr
import import
strutils, astalgo, msgs, vmdef, vmgen, nimsets, types, passes, unsigned, 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 semfold import leValueConv, ordinalValToString
from evaltempl import evalTemplate from evaltempl import evalTemplate
@ -371,11 +372,6 @@ template handleJmpBack() {.dirty.} =
globalError(c.debug[pc], errTooManyIterations) globalError(c.debug[pc], errTooManyIterations)
dec(c.loopIterations) 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 = proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
var pc = start var pc = start
var tos = tos var tos = tos
@ -1043,7 +1039,14 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
decodeB(rkNode) decodeB(rkNode)
let newLen = regs[rb].intVal.int let newLen = regs[rb].intVal.int
if regs[ra].node.isNil: stackTrace(c, tos, pc, errNilAccess) 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: of opcSwap:
let rb = instr.regB let rb = instr.regB
if regs[ra].kind == regs[rb].kind: if regs[ra].kind == regs[rb].kind:
@ -1362,6 +1365,19 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
while typ.kind == tyTypeDesc and typ.len > 0: typ = typ.sons[0] while typ.kind == tyTypeDesc and typ.len > 0: typ = typ.sons[0]
createStr regs[ra] createStr regs[ra]
regs[ra].node.strVal = typ.typeToString(preferExported) 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 inc pc
proc execute(c: PCtx, start: int): PNode = proc execute(c: PCtx, start: int): PNode =

View file

@ -16,7 +16,7 @@ const
byteExcess* = 128 # we use excess-K for immediates byteExcess* = 128 # we use excess-K for immediates
wordExcess* = 32768 wordExcess* = 32768
MaxLoopIterations* = 500_000 # max iterations of all loops MaxLoopIterations* = 1500_000 # max iterations of all loops
type type
@ -66,7 +66,8 @@ type
opcMulSet, opcPlusSet, opcMinusSet, opcSymdiffSet, opcConcatStr, opcMulSet, opcPlusSet, opcMinusSet, opcSymdiffSet, opcConcatStr,
opcContainsSet, opcRepr, opcSetLenStr, opcSetLenSeq, opcContainsSet, opcRepr, opcSetLenStr, opcSetLenSeq,
opcSwap, opcIsNil, opcOf, opcIs, opcSwap, opcIsNil, opcOf, opcIs,
opcSubStr, opcParseFloat, opcConv, opcCast, opcQuit, opcReset, opcSubStr, opcParseFloat, opcConv, opcCast,
opcQuit, opcReset,
opcNarrowS, opcNarrowU, opcNarrowS, opcNarrowU,
opcAddStrCh, opcAddStrCh,
@ -132,7 +133,8 @@ type
opcLdImmInt, # dest = immediate value opcLdImmInt, # dest = immediate value
opcNBindSym, opcNBindSym,
opcSetType, # dest.typ = types[Bx] opcSetType, # dest.typ = types[Bx]
opcTypeTrait opcTypeTrait,
opcMarshalLoad, opcMarshalStore
TBlock* = object TBlock* = object
label*: PSym label*: PSym
@ -221,7 +223,8 @@ proc registerCallback*(c: PCtx; name: string; callback: VmCallback) =
const const
firstABxInstr* = opcTJmp firstABxInstr* = opcTJmp
largeInstrs* = { # instructions which use 2 int32s instead of 1: largeInstrs* = { # instructions which use 2 int32s instead of 1:
opcSubStr, opcConv, opcCast, opcNewSeq, opcOf} opcSubStr, opcConv, opcCast, opcNewSeq, opcOf,
opcMarshalLoad, opcMarshalStore}
slotSomeTemp* = slotTempUnknown slotSomeTemp* = slotTempUnknown
relativeJumps* = {opcTJmp, opcFJmp, opcJmp, opcJmpBack} relativeJumps* = {opcTJmp, opcFJmp, opcJmp, opcJmpBack}

View file

@ -144,7 +144,9 @@ proc mapTypeToAst(t: PType, info: TLineInfo; allowRecursion=false): PNode =
of tyIter: result = mapTypeToBracket("iter", t, info) of tyIter: result = mapTypeToBracket("iter", t, info)
of tyProxy: result = atomicType"error" of tyProxy: result = atomicType"error"
of tyBuiltInTypeClass: result = mapTypeToBracket("builtinTypeClass", t, info) of tyBuiltInTypeClass: result = mapTypeToBracket("builtinTypeClass", t, info)
of tyUserTypeClass: result = mapTypeToBracket("userTypeClass", t, info) of tyUserTypeClass:
result = mapTypeToBracket("concept", t, info)
result.add t.n.copyTree
of tyCompositeTypeClass: result = mapTypeToBracket("compositeTypeClass", t, info) of tyCompositeTypeClass: result = mapTypeToBracket("compositeTypeClass", t, info)
of tyAnd: result = mapTypeToBracket("and", t, info) of tyAnd: result = mapTypeToBracket("and", t, info)
of tyOr: result = mapTypeToBracket("or", t, info) of tyOr: result = mapTypeToBracket("or", t, info)

View file

@ -76,6 +76,11 @@ proc codeListing(c: PCtx, result: var string, start=0; last = -1) =
elif opc in {opcLdConst, opcAsgnConst}: elif opc in {opcLdConst, opcAsgnConst}:
result.addf("\t$#\tr$#, $#", ($opc).substr(3), x.regA, result.addf("\t$#\tr$#, $#", ($opc).substr(3), x.regA,
c.constants[x.regBx-wordExcess].renderTree) 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: else:
result.addf("\t$#\tr$#, $#", ($opc).substr(3), x.regA, x.regBx-wordExcess) result.addf("\t$#\tr$#, $#", ($opc).substr(3), x.regA, x.regBx-wordExcess)
result.add("\t#") result.add("\t#")
@ -368,7 +373,7 @@ proc sameConstant*(a, b: PNode): bool =
case a.kind case a.kind
of nkSym: result = a.sym == b.sym of nkSym: result = a.sym == b.sym
of nkIdent: result = a.ident.id == b.ident.id 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 nkFloatLit..nkFloat64Lit: result = a.floatVal == b.floatVal
of nkStrLit..nkTripleStrLit: result = a.strVal == b.strVal of nkStrLit..nkTripleStrLit: result = a.strVal == b.strVal
of nkType, nkNilLit: result = a.typ == b.typ of nkType, nkNilLit: result = a.typ == b.typ
@ -696,8 +701,7 @@ proc genCard(c: PCtx; n: PNode; dest: var TDest) =
c.gABC(n, opcCard, dest, tmp) c.gABC(n, opcCard, dest, tmp)
c.freeTemp(tmp) c.freeTemp(tmp)
proc genMagic(c: PCtx; n: PNode; dest: var TDest) = proc genMagic(c: PCtx; n: PNode; dest: var TDest; m: TMagic) =
let m = n.sons[0].sym.magic
case m case m
of mAnd: c.genAndOr(n, opcFJmp, dest) of mAnd: c.genAndOr(n, opcFJmp, dest)
of mOr: c.genAndOr(n, opcTJmp, dest) of mOr: c.genAndOr(n, opcTJmp, dest)
@ -742,9 +746,9 @@ proc genMagic(c: PCtx; n: PNode; dest: var TDest) =
c.gABC(n, opcNewStr, dest, tmp) c.gABC(n, opcNewStr, dest, tmp)
c.freeTemp(tmp) c.freeTemp(tmp)
# XXX buggy # XXX buggy
of mLengthOpenArray, mLengthArray, mLengthSeq: of mLengthOpenArray, mLengthArray, mLengthSeq, mXLenSeq:
genUnaryABI(c, n, dest, opcLenSeq) genUnaryABI(c, n, dest, opcLenSeq)
of mLengthStr: of mLengthStr, mXLenStr:
genUnaryABI(c, n, dest, opcLenStr) genUnaryABI(c, n, dest, opcLenStr)
of mIncl, mExcl: of mIncl, mExcl:
unused(n, dest) unused(n, dest)
@ -791,7 +795,7 @@ proc genMagic(c: PCtx; n: PNode; dest: var TDest) =
genUnaryABC(c, n, dest, opcUnaryMinusInt) genUnaryABC(c, n, dest, opcUnaryMinusInt)
genNarrow(c, n, dest) genNarrow(c, n, dest)
of mUnaryMinusF64: genUnaryABC(c, n, dest, opcUnaryMinusFloat) 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: of mBitnotI, mBitnotI64:
genUnaryABC(c, n, dest, opcBitnotInt) genUnaryABC(c, n, dest, opcBitnotInt)
genNarrowU(c, n, dest) genNarrowU(c, n, dest)
@ -1008,7 +1012,7 @@ proc genMagic(c: PCtx; n: PNode; dest: var TDest) =
if dest < 0: dest = c.getTemp(n.typ) if dest < 0: dest = c.getTemp(n.typ)
c.gABC(n, opcCallSite, dest) c.gABC(n, opcCallSite, dest)
of mNGenSym: genBinaryABC(c, n, dest, opcGenSym) of mNGenSym: genBinaryABC(c, n, dest, opcGenSym)
of mMinI, mMaxI, mMinI64, mMaxI64, mAbsF64, mMinF64, mMaxF64, mAbsI, of mMinI, mMaxI, mAbsF64, mMinF64, mMaxF64, mAbsI,
mAbsI64, mDotDot: mAbsI64, mDotDot:
c.genCall(n, dest) c.genCall(n, dest)
of mExpandToAst: of mExpandToAst:
@ -1028,6 +1032,22 @@ proc genMagic(c: PCtx; n: PNode; dest: var TDest) =
# mGCref, mGCunref, # mGCref, mGCunref,
internalError(n.info, "cannot generate code for: " & $m) 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 const
atomicTypes = {tyBool, tyChar, atomicTypes = {tyBool, tyChar,
tyExpr, tyStmt, tyTypeDesc, tyStatic, tyExpr, tyStmt, tyTypeDesc, tyStatic,
@ -1364,7 +1384,7 @@ proc getNullValue(typ: PType, info: TLineInfo): PNode =
of tyCString, tyString: of tyCString, tyString:
result = newNodeIT(nkStrLit, info, t) result = newNodeIT(nkStrLit, info, t)
of tyVar, tyPointer, tyPtr, tySequence, tyExpr, of tyVar, tyPointer, tyPtr, tySequence, tyExpr,
tyStmt, tyTypeDesc, tyStatic, tyRef: tyStmt, tyTypeDesc, tyStatic, tyRef, tyNil:
result = newNodeIT(nkNilLit, info, t) result = newNodeIT(nkNilLit, info, t)
of tyProc: of tyProc:
if t.callConv != ccClosure: if t.callConv != ccClosure:
@ -1391,7 +1411,7 @@ proc getNullValue(typ: PType, info: TLineInfo): PNode =
addSon(result, getNullValue(t.sons[i], info)) addSon(result, getNullValue(t.sons[i], info))
of tySet: of tySet:
result = newNodeIT(nkCurly, info, t) result = newNodeIT(nkCurly, info, t)
else: internalError("getNullValue: " & $t.kind) else: internalError(info, "getNullValue: " & $t.kind)
proc ldNullOpcode(t: PType): TOpcode = proc ldNullOpcode(t: PType): TOpcode =
if fitsRegister(t): opcLdNullReg else: opcLdNull if fitsRegister(t): opcLdNullReg else: opcLdNull
@ -1533,6 +1553,15 @@ proc matches(s: PSym; x: string): bool =
dec L dec L
result = true 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 = proc procIsCallback(c: PCtx; s: PSym): bool =
if s.offset < -1: return true if s.offset < -1: return true
var i = -2 var i = -2
@ -1570,8 +1599,17 @@ proc gen(c: PCtx; n: PNode; dest: var TDest; flags: TGenFlags = {}) =
else: else:
internalError(n.info, "cannot generate code for: " & s.name.s) internalError(n.info, "cannot generate code for: " & s.name.s)
of nkCallKinds: of nkCallKinds:
if n.sons[0].kind == nkSym and n.sons[0].sym.magic != mNone: if n.sons[0].kind == nkSym:
genMagic(c, n, dest) 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: else:
genCall(c, n, dest) genCall(c, n, dest)
clearDest(c, n, dest) clearDest(c, n, dest)
@ -1610,7 +1648,8 @@ proc gen(c: PCtx; n: PNode; dest: var TDest; flags: TGenFlags = {}) =
genBreak(c, n) genBreak(c, n)
of nkTryStmt: genTry(c, n, dest) of nkTryStmt: genTry(c, n, dest)
of nkStmtList: of nkStmtList:
unused(n, dest) #unused(n, dest)
# XXX Fix this bug properly, lexim triggers it
for x in n: gen(c, x) for x in n: gen(c, x)
of nkStmtListExpr: of nkStmtListExpr:
let L = n.len-1 let L = n.len-1

283
compiler/vmmarshal.nim Normal file
View 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)

View file

@ -96,7 +96,7 @@ path="$lib/pure/unidecode"
# Configuration for the GNU C/C++ compiler: # Configuration for the GNU C/C++ compiler:
@if windows: @if windows:
#gcc.path = r"$nimrod\dist\mingw\bin" #gcc.path = r"$nim\dist\mingw\bin"
@if gcc: @if gcc:
tlsEmulation:on tlsEmulation:on
@end @end

1
csources Submodule

@ -0,0 +1 @@
Subproject commit 15724e2e1f3e7749d508dfcd995e84fea2850802

View file

@ -179,7 +179,7 @@ simpleStmt = ((returnStmt | raiseStmt | yieldStmt | discardStmt | breakStmt
| continueStmt | pragmaStmt | importStmt | exportStmt | fromStmt | continueStmt | pragmaStmt | importStmt | exportStmt | fromStmt
| includeStmt | commentStmt) / exprStmt) COMMENT? | includeStmt | commentStmt) / exprStmt) COMMENT?
complexOrSimpleStmt = (ifStmt | whenStmt | whileStmt complexOrSimpleStmt = (ifStmt | whenStmt | whileStmt
| tryStmt | finallyStmt | exceptStmt | forStmt | tryStmt | forStmt
| blockStmt | staticStmt | deferStmt | asmStmt | blockStmt | staticStmt | deferStmt | asmStmt
| 'proc' routine | 'proc' routine
| 'method' routine | 'method' routine

View file

@ -453,10 +453,6 @@ Other
* `zipfiles <zipfiles.html>`_ * `zipfiles <zipfiles.html>`_
This module implements a zip archive creator/reader/modifier. This module implements a zip archive creator/reader/modifier.
* `web <web.html>`_
This module contains simple high-level procedures for dealing with the
Web like loading the contents of a Web page from an URL.
* `ssl <ssl.html>`_ * `ssl <ssl.html>`_
This module provides an easy to use sockets-style This module provides an easy to use sockets-style
Nim interface to the OpenSSL library. Nim interface to the OpenSSL library.
@ -513,25 +509,6 @@ Regular expressions
Wrapper for the TRE library. Wrapper for the TRE library.
Graphics libraries
------------------
* `sdl <sdl.html>`_
Part of the wrapper for SDL.
* `sdl_gfx <sdl_gfx.html>`_
Part of the wrapper for SDL.
* `sdl_image <sdl_image.html>`_
Part of the wrapper for SDL.
* `sdl_mixer <sdl_mixer.html>`_
Part of the wrapper for SDL.
* `sdl_net <sdl_net.html>`_
Part of the wrapper for SDL.
* `sdl_ttf <sdl_ttf.html>`_
Part of the wrapper for SDL.
* `smpeg <smpeg.html>`_
Part of the wrapper for SDL.
GUI libraries GUI libraries
------------- -------------

View file

@ -219,7 +219,7 @@ Concepts are written in the following form:
(x < y) is bool (x < y) is bool
Container[T] = concept c Container[T] = concept c
c.len is ordinal c.len is Ordinal
items(c) is iterator items(c) is iterator
for value in c: for value in c:
type(value) is T type(value) is T

View file

@ -289,7 +289,7 @@ Numerical constants are of a single type and have the form::
INT32_LIT = INT_LIT ['\''] ('i' | 'I') '32' INT32_LIT = INT_LIT ['\''] ('i' | 'I') '32'
INT64_LIT = INT_LIT ['\''] ('i' | 'I') '64' INT64_LIT = INT_LIT ['\''] ('i' | 'I') '64'
UINT8_LIT = INT_LIT ['\''] ('u' | 'U') UINT_LIT = INT_LIT ['\''] ('u' | 'U')
UINT8_LIT = INT_LIT ['\''] ('u' | 'U') '8' UINT8_LIT = INT_LIT ['\''] ('u' | 'U') '8'
UINT16_LIT = INT_LIT ['\''] ('u' | 'U') '16' UINT16_LIT = INT_LIT ['\''] ('u' | 'U') '16'
UINT32_LIT = INT_LIT ['\''] ('u' | 'U') '32' UINT32_LIT = INT_LIT ['\''] ('u' | 'U') '32'

View file

@ -14,7 +14,7 @@ deprecated pragma
The deprecated pragma is used to mark a symbol as deprecated: The deprecated pragma is used to mark a symbol as deprecated:
.. code-block:: nimrod .. code-block:: nim
proc p() {.deprecated.} proc p() {.deprecated.}
var x {.deprecated.}: char var x {.deprecated.}: char
@ -22,7 +22,7 @@ It can also be used as a statement. Then it takes a list of *renamings*. The
upcoming ``nimfix`` tool can automatically update the code and perform these upcoming ``nimfix`` tool can automatically update the code and perform these
renamings: renamings:
.. code-block:: nimrod .. code-block:: nim
type type
File = object File = object
Stream = ref object Stream = ref object
@ -71,10 +71,9 @@ procedural variable.
compileTime pragma compileTime pragma
------------------ ------------------
The ``compileTime`` pragma is used to mark a proc to be used at compile The ``compileTime`` pragma is used to mark a proc or variable to be used at
time only. No code will be generated for it. Compile time procs are useful compile time only. No code will be generated for it. Compile time procs are
as helpers for macros. useful as helpers for macros.
noReturn pragma noReturn pragma
--------------- ---------------

View file

@ -129,6 +129,7 @@ initialized and does not rely on syntactic properties:
x = a() x = a()
use x use x
let statement let statement
------------- -------------
@ -141,6 +142,19 @@ nor can their address be taken. They cannot be assigned new values.
For let variables the same pragmas are available as for ordinary variables. For let variables the same pragmas are available as for ordinary variables.
Tuple unpacking
---------------
In a ``var`` or ``let`` statement tuple unpacking can be performed. The special
identifier ``_`` can be used to ignore some parts of the tuple:
.. code-block:: nim
proc returnsTuple(): (int, int, int) = (4, 2, 3)
let (x, _, z) = returnsTuple()
Const section Const section
------------- -------------

View file

@ -15,8 +15,6 @@ Associativity
Binary operators whose first character is ``^`` are right-associative, all Binary operators whose first character is ``^`` are right-associative, all
other binary operators are left-associative. other binary operators are left-associative.
Operators ending in ``>`` but longer than a single character are
called `arrow like`:idx:.
Precedence Precedence
@ -33,9 +31,12 @@ as ``(@x).abc`` whereas ``$x.abc`` is parsed as ``$(x.abc)``.
For binary operators that are not keywords the precedence is determined by the For binary operators that are not keywords the precedence is determined by the
following rules: following rules:
Operators ending in either ``->``, ``~>`` or ``=>`` are called
`arrow like`:idx:, and have the lowest precedence of all operators.
If the operator ends with ``=`` and its first character is none of If the operator ends with ``=`` and its first character is none of
``<``, ``>``, ``!``, ``=``, ``~``, ``?``, it is an *assignment operator* which ``<``, ``>``, ``!``, ``=``, ``~``, ``?``, it is an *assignment operator* which
has the lowest precedence. has the second lowest precedence.
Otherwise precedence is determined by the first character. Otherwise precedence is determined by the first character.
@ -44,7 +45,7 @@ Precedence level Operators First charact
================ =============================================== ================== =============== ================ =============================================== ================== ===============
10 (highest) ``$ ^`` OP10 10 (highest) ``$ ^`` OP10
9 ``* / div mod shl shr %`` ``* % \ /`` OP9 9 ``* / div mod shl shr %`` ``* % \ /`` OP9
8 ``+ -`` ``+ ~ |`` OP8 8 ``+ -`` ``+ - ~ |`` OP8
7 ``&`` ``&`` OP7 7 ``&`` ``&`` OP7
6 ``..`` ``.`` OP6 6 ``..`` ``.`` OP6
5 ``== <= < >= > != in notin is isnot not of`` ``= < > !`` OP5 5 ``== <= < >= > != in notin is isnot not of`` ``= < > !`` OP5

View file

@ -45,10 +45,9 @@ contain a ``ref`` or ``closure`` type. This enforces
the *no heap sharing restriction*. the *no heap sharing restriction*.
Routines that are imported from C are always assumed to be ``gcsafe``. Routines that are imported from C are always assumed to be ``gcsafe``.
To enable the GC-safety checking the ``--threadAnalysis:on`` command line To disable the GC-safety checking the ``--threadAnalysis:off`` command line
switch must be used. This is a temporary workaround to ease the porting effort switch can be used. This is a temporary workaround to ease the porting effort
from old code to the new threading model. In the future the thread analysis from old code to the new threading model.
will always be performed.
Future directions: Future directions:
@ -206,5 +205,4 @@ restrictions / changes:
* Every array access has to be provably within bounds. This is called * Every array access has to be provably within bounds. This is called
the *bounds check*. the *bounds check*.
* Slices are optimized so that no copy is performed. This optimization is not * Slices are optimized so that no copy is performed. This optimization is not
yet performed for ordinary slices outside of a ``parallel`` section. Slices yet performed for ordinary slices outside of a ``parallel`` section.
are also special in that they currently do not support negative indexes!

View file

@ -22,21 +22,43 @@ operator `=`
------------ ------------
This operator is the assignment operator. Note that in the contexts This operator is the assignment operator. Note that in the contexts
like ``let v = expr``, ``var v = expr``, ``parameter = defaultValue`` or for ``result = expr``, ``parameter = defaultValue`` or for
parameter passing no assignment is performed. The ``override`` pragma is parameter passing no assignment is performed. For a type ``T`` that has an
optional for overriding ``=``. overloaded assignment operator ``var v = T()`` is rewritten
to ``var v: T; v = T()``; in other words ``var`` and ``let`` contexts do count
as assignments.
The assignment operator needs to be attached to an object or distinct
type ``T``. Its signature has to be ``(var T, T)``. Example:
.. code-block:: nim
type
Concrete = object
a, b: string
proc `=`(d: var Concrete; src: Concrete) =
shallowCopy(d.a, src.a)
shallowCopy(d.b, src.b)
echo "Concrete '=' called"
var x, y: array[0..2, Concrete]
var cA, cB: Concrete
var cATup, cBTup: tuple[x: int, ha: Concrete]
x = y
cA = cB
cATup = cBTup
**Note**: Overriding of operator ``=`` is not yet implemented.
destructors destructors
----------- -----------
A destructor must have a single parameter with a concrete type (the name of a A destructor must have a single parameter with a concrete type (the name of a
generic type is allowed too). The name of the destructor has to be ``destroy`` generic type is allowed too). The name of the destructor has to be ``=destroy``.
and it need to be annotated with the ``override`` pragma.
``destroy(v)`` will be automatically invoked for every local stack ``=destroy(v)`` will be automatically invoked for every local stack
variable ``v`` that goes out of scope. variable ``v`` that goes out of scope.
If a structured type features a field with destructable type and If a structured type features a field with destructable type and
@ -53,7 +75,7 @@ can then only be used in *destructible contexts* and as parameters:
x, y: int x, y: int
p: pointer p: pointer
proc destroy(o: var MyObj) {.override.} = proc `=destroy`(o: var MyObj) =
if o.p != nil: dealloc o.p if o.p != nil: dealloc o.p
proc open: MyObj = proc open: MyObj =
@ -85,7 +107,7 @@ be destructed at its scope exit. Later versions of the language will improve
the support of destructors. the support of destructors.
Be aware that destructors are not called for objects allocated with ``new``. Be aware that destructors are not called for objects allocated with ``new``.
This may change in future versions of language, but for now the ``finalizer`` This may change in future versions of language, but for now the `finalizer`:idx:
parameter to ``new`` has to be used. parameter to ``new`` has to be used.
**Note**: Destructors are still experimental and the spec might change **Note**: Destructors are still experimental and the spec might change
@ -95,7 +117,7 @@ significantly in order to incorporate an escape analysis.
deepCopy deepCopy
-------- --------
``deepCopy`` is a builtin that is invoked whenever data is passed to ``=deepCopy`` is a builtin that is invoked whenever data is passed to
a ``spawn``'ed proc to ensure memory safety. The programmer can override its a ``spawn``'ed proc to ensure memory safety. The programmer can override its
behaviour for a specific ``ref`` or ``ptr`` type ``T``. (Later versions of the behaviour for a specific ``ref`` or ``ptr`` type ``T``. (Later versions of the
language may weaken this restriction.) language may weaken this restriction.)
@ -103,10 +125,10 @@ language may weaken this restriction.)
The signature has to be: The signature has to be:
.. code-block:: nim .. code-block:: nim
proc deepCopy(x: T): T {.override.} proc `=deepCopy`(x: T): T
This mechanism is used by most data structures that support shared memory like This mechanism will be used by most data structures that support shared memory
channels to implement thread safe automatic memory management. like channels to implement thread safe automatic memory management.
The builtin ``deepCopy`` can even clone closures and their environments. See The builtin ``deepCopy`` can even clone closures and their environments. See
the documentation of `spawn`_ for details. the documentation of `spawn`_ for details.

View file

@ -864,7 +864,9 @@ Future directions:
* Builtin regions like ``private``, ``global`` and ``local`` will * Builtin regions like ``private``, ``global`` and ``local`` will
prove very useful for the upcoming OpenCL target. prove very useful for the upcoming OpenCL target.
* Builtin "regions" can model ``lent`` and ``unique`` pointers. * Builtin "regions" can model ``lent`` and ``unique`` pointers.
* An assignment operator can be attached to a region so that proper write
barriers can be generated. This would imply that the GC can be implemented
completely in user-space.
Procedural type Procedural type

View file

@ -386,6 +386,25 @@ Example:
As can be seen from the example, to Nim symbols can be referred via backticks. As can be seen from the example, to Nim symbols can be referred via backticks.
Use two backticks to produce a single verbatim backtick. Use two backticks to produce a single verbatim backtick.
For a toplevel emit statement the section where in the generated C/C++ file
the code should be emitted can be influenced via the
prefixes ``/*TYPESECTION*/`` or ``/*VARSECTION*/``:
.. code-block:: Nim
{.emit: """/*TYPESECTION*/
struct Vector3 {
public:
Vector3(): x(5) {}
Vector3(float x_): x(x_) {}
float x;
};
""".}
type Vector3 {.importcpp: "Vector3", nodecl} = object
x: cfloat
proc constructVector3(a: cfloat): Vector3 {.importcpp: "Vector3(@)", nodecl}
ImportCpp pragma ImportCpp pragma
---------------- ----------------
@ -506,7 +525,7 @@ For example:
.. code-block:: nim .. code-block:: nim
type Input {.importcpp: "System::Input".} = object type Input {.importcpp: "System::Input".} = object
proc getSubsystem*[T](): ptr T {.importcpp: "SystemManager::getSubsystem<'*0>()".} proc getSubsystem*[T](): ptr T {.importcpp: "SystemManager::getSubsystem<'*0>()", nodecl.}
let x: ptr Input = getSubsystem[Input]() let x: ptr Input = getSubsystem[Input]()
@ -596,6 +615,25 @@ Produces:
x[6] = 91.4; x[6] = 91.4;
- If more precise control is needed, the apostrophe ``'`` can be used in the
supplied pattern to denote the concrete type parameters of the generic type.
See the usage of the apostrophe operator in proc patterns for more details.
.. code-block:: nim
type
VectorIterator {.importcpp: "std::vector<'0>::iterator".} [T] = object
var x: VectorIterator[cint]
Produces:
.. code-block:: C
std::vector<int>::iterator x;
ImportObjC pragma ImportObjC pragma
----------------- -----------------
Similar to the `importc pragma for C <manual.html#importc-pragma>`_, the Similar to the `importc pragma for C <manual.html#importc-pragma>`_, the

174
doc/nimsuggest.txt Normal file
View file

@ -0,0 +1,174 @@
================================
Nim IDE Integration Guide
================================
:Author: Unknown
:Version: |nimversion|
.. contents::
Nim differs from many other compilers in that it is really fast,
and being so fast makes it suited to provide external queries for
text editors about the source code being written. Through the
``nimsuggest`` tool, any IDE
can query a ``.nim`` source file and obtain useful information like
definition of symbols or suggestions for completion.
This document will guide you through the available options. If you
want to look at practical examples of nimsuggest support you can look
at the
`various editor integrations <https://github.com/Araq/Nim/wiki/Editor-Support>`_
already available.
Installation
============
Nimsuggest is available as a Nimble package but currently does not install
properly via Nimble. As nimsuggest is part of the compiler it also doesn't make
too much sense as a Nimble package. Instead we will do the building manually::
cd compiler/nimsuggest
nim c -d:release nimsuggest
cp nimsuggest ../../bin
# OR: copy the nimsuggest binary to where your 'nim' binary is
cd ../..
Nimsuggest invocation
=====================
Run it via ``nimsuggest --stdin myproject.nim``. Nimsuggest is a server that
takes queries that are related to ``myproject``. There is some support so that
you can throw random ``.nim`` files which are not part of ``myproject`` at
Nimsuggest too, but usually the query refer to modules/files that are part of
``myproject``.
``--stdin`` means that Nimsuggest reads the query from ``stdin``. This is great
for testing things out and playing with it but for an editor communication
via sockets is more reasonable so that is the default. It listens to port 6000
by default.
Specifying the location of the query
------------------------------------
Nimsuggest than waits for queries to process. A query consists of a
cryptic 3 letter "command" ``def`` or ``con`` or ``sug`` or ``use`` followed by
a location. A query location consists of:
``file.nim``
This is the name of the module or include file the query refers to.
``dirtyfile.nim``
This is optional.
The ``file`` paramater is enough for static analysis, but IDEs
tend to have *unsaved buffers* where the user may still be in
the middle of typing a line. In such situations the IDE can
save the current contents to a temporary file and then use the
``dirtyfile.nim`` option to tell Nimsuggest that ``foobar.nim`` should
be taken from ``temporary/foobar.nim``.
``line``
An integer with the line you are going to query. For the compiler
lines start at **1**.
``col``
An integer with the column you are going to query. For the
compiler columns start at **1**.
Definitions
-----------
The ``def`` Nimsuggest command performs a query about the definition
of a specific symbol. If available, Nimsuggest will answer with the
type, source file, line/column information and other accessory data
if available like a docstring. With this information an IDE can
provide the typical *Jump to definition* where a user puts the
cursor on a symbol or uses the mouse to select it and is redirected
to the place where the symbol is located.
Since Nim is implemented in Nim, one of the nice things of
this feature is that any user with an IDE supporting it can quickly
jump around the standard library implementation and see exactly
what a proc does, learning about the language and seeing real life
examples of how to write/implement specific features.
Nimsuggest will always answer with a single definition or none if it
can't find any valid symbol matching the position of the query.
Suggestions
-----------
The ``sug`` Nimsuggest command performs a query about possible
completion symbols at some point in the file.
The typical usage scenario for this option is to call it after the
user has typed the dot character for `the object oriented call
syntax <tut2.html#method-call-syntax>`_. Nimsuggest will try to return
the suggestions sorted first by scope (from innermost to outermost)
and then by item name.
Invocation context
------------------
The ``con`` Nimsuggest command is very similar to the suggestions
command, but instead of being used after the user has typed a dot
character, this one is meant to be used after the user has typed
an opening brace to start typing parameters.
Symbol usages
-------------
The ``use`` Nimsuggest command lists all usages of the symbol at
a position. IDEs can use this to find all the places in the file
where the symbol is used and offer the user to rename it in all
places at the same time.
For this kind of query the IDE will most likely ignore all the
type/signature info provided by Nimsuggest and concentrate on the
filename, line and column position of the multiple returned answers.
Parsing nimsuggest output
=========================
Nimsuggest output is always returned on single lines separated by
tab characters (``\t``). The values of each column are:
1. Three characters indicating the type of returned answer (e.g.
``def`` for definition, ``sug`` for suggestion, etc).
2. Type of the symbol. This can be ``skProc``, ``skLet``, and just
about any of the enums defined in the module ``compiler/ast.nim``.
3. Full qualitifed path of the symbol. If you are querying a symbol
defined in the ``proj.nim`` file, this would have the form
``proj.symbolName``.
4. Type/signature. For variables and enums this will contain the
type of the symbol, for procs, methods and templates this will
contain the full unique signature (e.g. ``proc (File)``).
5. Full path to the file containing the symbol.
6. Line where the symbol is located in the file. Lines start to
count at **1**.
7. Column where the symbol is located in the file. Columns start
to count at **1**.
8. Docstring for the symbol if available or the empty string. To
differentiate the docstring from end of answer,
the docstring is always provided enclosed in double quotes, and
if the docstring spans multiple lines, all following lines of the
docstring will start with a blank space to align visually with
the starting quote.
Also, you won't find raw ``\n`` characters breaking the one
answer per line format. Instead you will need to parse sequences
in the form ``\xHH``, where *HH* is a hexadecimal value (e.g.
newlines generate the sequence ``\x0A``).

View file

@ -4,6 +4,11 @@ Tools available with Nim
The standard distribution ships with the following tools: The standard distribution ships with the following tools:
- | `Nimsuggest for IDE support <nimsuggest.html>`_
| Through the ``nimsuggest`` tool, any IDE can query a ``.nim`` source file
and obtain useful information like definition of symbols or suggestions for
completion.
- | `Nim Installation Generator <niminst.html>`_ - | `Nim Installation Generator <niminst.html>`_
| How to generate a nice installer for your Nim program. | How to generate a nice installer for your Nim program.

View file

@ -1451,7 +1451,7 @@ operators perform implicit dereferencing operations for reference types:
type type
Node = ref NodeObj Node = ref NodeObj
NodeObj = object NodeObj = object
le, ri: PNode le, ri: Node
data: int data: int
var var
n: Node n: Node

View file

@ -106,6 +106,12 @@ proc zip(args: string) =
exec("$# --var:version=$# --var:mingw=none --main:compiler/nim.nim zip compiler/installer.ini" % exec("$# --var:version=$# --var:mingw=none --main:compiler/nim.nim zip compiler/installer.ini" %
["tools/niminst/niminst".exe, VersionAsString]) ["tools/niminst/niminst".exe, VersionAsString])
proc targz(args: string) =
exec("$3 cc -r $2 --var:version=$1 --var:mingw=none --main:compiler/nim.nim scripts compiler/installer.ini" %
[VersionAsString, compileNimInst, findNim()])
exec("$# --var:version=$# --var:mingw=none --main:compiler/nim.nim targz compiler/installer.ini" %
["tools" / "niminst" / "niminst".exe, VersionAsString])
proc buildTool(toolname, args: string) = proc buildTool(toolname, args: string) =
exec("$# cc $# $#" % [findNim(), args, toolname]) exec("$# cc $# $#" % [findNim(), args, toolname])
copyFile(dest="bin"/ splitFile(toolname).name.exe, source=toolname.exe) copyFile(dest="bin"/ splitFile(toolname).name.exe, source=toolname.exe)
@ -113,12 +119,12 @@ proc buildTool(toolname, args: string) =
proc nsis(args: string) = proc nsis(args: string) =
# make sure we have generated the niminst executables: # make sure we have generated the niminst executables:
buildTool("tools/niminst/niminst", args) buildTool("tools/niminst/niminst", args)
buildTool("tools/nimgrep", args) #buildTool("tools/nimgrep", args)
# produce 'nimrod_debug.exe': # produce 'nim_debug.exe':
exec "nim c compiler" / "nim.nim" #exec "nim c compiler" / "nim.nim"
copyExe("compiler/nim".exe, "bin/nim_debug".exe) #copyExe("compiler/nim".exe, "bin/nim_debug".exe)
exec(("tools" / "niminst" / "niminst --var:version=$# --var:mingw=mingw$#" & exec(("tools" / "niminst" / "niminst --var:version=$# --var:mingw=mingw$#" &
" nsis compiler/nim") % [VersionAsString, $(sizeof(pointer)*8)]) " nsis compiler/installer.ini") % [VersionAsString, $(sizeof(pointer)*8)])
proc install(args: string) = proc install(args: string) =
exec("$# cc -r $# --var:version=$# --var:mingw=none --main:compiler/nim.nim scripts compiler/installer.ini" % exec("$# cc -r $# --var:version=$# --var:mingw=none --main:compiler/nim.nim scripts compiler/installer.ini" %
@ -143,8 +149,6 @@ proc findStartNim: string =
# we try several things before giving up: # we try several things before giving up:
# * bin/nim # * bin/nim
# * $PATH/nim # * $PATH/nim
# * bin/nimrod
# * $PATH/nimrod
# If these fail, we try to build nim with the "build.(sh|bat)" script. # If these fail, we try to build nim with the "build.(sh|bat)" script.
var nim = "nim".exe var nim = "nim".exe
result = "bin" / nim result = "bin" / nim
@ -152,13 +156,6 @@ proc findStartNim: string =
for dir in split(getEnv("PATH"), PathSep): for dir in split(getEnv("PATH"), PathSep):
if existsFile(dir / nim): return dir / nim if existsFile(dir / nim): return dir / nim
# try the old "nimrod.exe":
var nimrod = "nimrod".exe
result = "bin" / nimrod
if existsFile(result): return
for dir in split(getEnv("PATH"), PathSep):
if existsFile(dir / nim): return dir / nimrod
when defined(Posix): when defined(Posix):
const buildScript = "build.sh" const buildScript = "build.sh"
if existsFile(buildScript): if existsFile(buildScript):
@ -317,12 +314,14 @@ proc winRelease() =
#buildTool("tools/niminst/niminst", " -d:release") #buildTool("tools/niminst/niminst", " -d:release")
buildTool("tools/nimgrep", " -d:release") buildTool("tools/nimgrep", " -d:release")
buildTool("compiler/nimfix/nimfix", " -d:release") buildTool("compiler/nimfix/nimfix", " -d:release")
buildTool("compiler/nimsuggest/nimsuggest", " -d:release")
#run7z("win32", "bin/nim.exe", "bin/c2nim.exe", "bin/nimgrep.exe",
# "bin/nimfix.exe",
# "bin/nimble.exe", "bin/*.dll",
# "config", "dist/*.dll", "examples", "lib",
# "readme.txt", "contributors.txt", "copying.txt")
run7z("win32", "bin/nim.exe", "bin/c2nim.exe", "bin/nimgrep.exe",
"bin/nimfix.exe",
"bin/nimble.exe", "bin/*.dll",
"config", "dist/*.dll", "examples", "lib",
"readme.txt", "contributors.txt", "copying.txt")
# second step: XXX build 64 bit version # second step: XXX build 64 bit version
# -------------- tests -------------------------------------------------------- # -------------- tests --------------------------------------------------------
@ -366,6 +365,7 @@ of cmdArgument:
of "pdf": pdf() of "pdf": pdf()
of "csource", "csources": csource(op.cmdLineRest) of "csource", "csources": csource(op.cmdLineRest)
of "zip": zip(op.cmdLineRest) of "zip": zip(op.cmdLineRest)
of "targz": targz(op.cmdLineRest)
of "nsis": nsis(op.cmdLineRest) of "nsis": nsis(op.cmdLineRest)
of "install": install(op.cmdLineRest) of "install": install(op.cmdLineRest)
of "test", "tests": tests(op.cmdLineRest) of "test", "tests": tests(op.cmdLineRest)

View file

@ -88,7 +88,9 @@ type
ntyBigNum, ntyBigNum,
ntyConst, ntyMutable, ntyVarargs, ntyConst, ntyMutable, ntyVarargs,
ntyIter, ntyIter,
ntyError ntyError,
ntyBuiltinTypeClass, ntyConcept, ntyConceptInst, ntyComposite,
ntyAnd, ntyOr, ntyNot
TNimTypeKinds* {.deprecated.} = set[NimTypeKind] TNimTypeKinds* {.deprecated.} = set[NimTypeKind]
NimSymKind* = enum NimSymKind* = enum
@ -162,6 +164,7 @@ proc kind*(n: NimNode): NimNodeKind {.magic: "NKind", noSideEffect.}
## returns the `kind` of the node `n`. ## returns the `kind` of the node `n`.
proc intVal*(n: NimNode): BiggestInt {.magic: "NIntVal", noSideEffect.} proc intVal*(n: NimNode): BiggestInt {.magic: "NIntVal", noSideEffect.}
proc boolVal*(n: NimNode): bool {.compileTime, noSideEffect.} = n.intVal != 0
proc floatVal*(n: NimNode): BiggestFloat {.magic: "NFloatVal", noSideEffect.} proc floatVal*(n: NimNode): BiggestFloat {.magic: "NFloatVal", noSideEffect.}
proc symbol*(n: NimNode): NimSym {.magic: "NSymbol", noSideEffect.} proc symbol*(n: NimNode): NimSym {.magic: "NSymbol", noSideEffect.}
proc ident*(n: NimNode): NimIdent {.magic: "NIdent", noSideEffect.} proc ident*(n: NimNode): NimIdent {.magic: "NIdent", noSideEffect.}
@ -355,6 +358,12 @@ proc expectLen*(n: NimNode, len: int) {.compileTime.} =
## macros that check its number of arguments. ## macros that check its number of arguments.
if n.len != len: error("macro expects a node with " & $len & " children") if n.len != len: error("macro expects a node with " & $len & " children")
proc newTree*(kind: NimNodeKind,
children: varargs[NimNode]): NimNode {.compileTime.} =
## produces a new node with children.
result = newNimNode(kind)
result.add(children)
proc newCall*(theProc: NimNode, proc newCall*(theProc: NimNode,
args: varargs[NimNode]): NimNode {.compileTime.} = args: varargs[NimNode]): NimNode {.compileTime.} =
## produces a new call node. `theProc` is the proc that is called with ## produces a new call node. `theProc` is the proc that is called with
@ -389,6 +398,11 @@ proc newLit*(i: BiggestInt): NimNode {.compileTime.} =
result = newNimNode(nnkIntLit) result = newNimNode(nnkIntLit)
result.intVal = i result.intVal = i
proc newLit*(b: bool): NimNode {.compileTime.} =
## produces a new boolean literal node.
result = newNimNode(nnkIntLit)
result.intVal = ord(b)
proc newLit*(f: BiggestFloat): NimNode {.compileTime.} = proc newLit*(f: BiggestFloat): NimNode {.compileTime.} =
## produces a new float literal node. ## produces a new float literal node.
result = newNimNode(nnkFloatLit) result = newNimNode(nnkFloatLit)

View file

@ -205,7 +205,7 @@ proc setEncoding*(connection: TDbConn, encoding: string): bool {.
exec(connection, sql"PRAGMA encoding = ?", [encoding]) exec(connection, sql"PRAGMA encoding = ?", [encoding])
result = connection.getValue(sql"PRAGMA encoding") == encoding result = connection.getValue(sql"PRAGMA encoding") == encoding
when isMainModule: when not defined(testing) and isMainModule:
var db = open("db.sql", "", "", "") var db = open("db.sql", "", "", "")
exec(db, sql"create table tbl1(one varchar(10), two smallint)", []) exec(db, sql"create table tbl1(one varchar(10), two smallint)", [])
exec(db, sql"insert into tbl1 values('hello!',10)", []) exec(db, sql"insert into tbl1 values('hello!',10)", [])

View file

@ -499,7 +499,7 @@ template withEvents*(surf: PSurface, event: expr, actions: stmt): stmt {.
if sdl.init(sdl.INIT_VIDEO) < 0: raiseEGraphics() if sdl.init(sdl.INIT_VIDEO) < 0: raiseEGraphics()
if sdl_ttf.init() < 0: raiseEGraphics() if sdl_ttf.init() < 0: raiseEGraphics()
when isMainModule: when not defined(testing) and isMainModule:
var surf = newScreenSurface(800, 600) var surf = newScreenSurface(800, 600)
surf.fillSurface(colWhite) surf.fillSurface(colWhite)

View file

@ -135,7 +135,7 @@ else:
var cur, old: Termios var cur, old: Termios
discard fd.tcgetattr(cur.addr) discard fd.tcgetattr(cur.addr)
old = cur old = cur
cur.lflag = cur.lflag and not Tcflag(ECHO) cur.c_lflag = cur.c_lflag and not Tcflag(ECHO)
discard fd.tcsetattr(TCSADRAIN, cur.addr) discard fd.tcsetattr(TCSADRAIN, cur.addr)
stdout.write prompt stdout.write prompt
result = stdin.readLine(password) result = stdin.readLine(password)

View file

@ -7,8 +7,11 @@
# distribution, for details about the copyright. # distribution, for details about the copyright.
# #
## Regular expression support for Nim. Consider using the pegs module ## Regular expression support for Nim. Consider using the pegs module instead.
## instead. ##
## There is an alternative regular expressions library with a more unified API:
## `nre <https://github.com/flaviut/nre>`_. It may be added to the standard
## library in the future, instead of `re`.
## ##
## **Note:** The 're' proc defaults to the **extended regular expression ## **Note:** The 're' proc defaults to the **extended regular expression
## syntax** which lets you use whitespace freely to make your regexes readable. ## syntax** which lets you use whitespace freely to make your regexes readable.
@ -43,8 +46,8 @@ type
## expression will be used only once) ## expression will be used only once)
RegexDesc = object RegexDesc = object
h: PPcre h: ptr Pcre
e: ptr TExtra e: ptr ExtraData
Regex* = ref RegexDesc ## a compiled regular expression Regex* = ref RegexDesc ## a compiled regular expression
@ -60,7 +63,7 @@ proc raiseInvalidRegex(msg: string) {.noinline, noreturn.} =
e.msg = msg e.msg = msg
raise e raise e
proc rawCompile(pattern: string, flags: cint): PPcre = proc rawCompile(pattern: string, flags: cint): ptr Pcre =
var var
msg: cstring msg: cstring
offset: cint offset: cint
@ -84,7 +87,7 @@ proc re*(s: string, flags = {reExtended, reStudy}): Regex =
result.h = rawCompile(s, cast[cint](flags - {reStudy})) result.h = rawCompile(s, cast[cint](flags - {reStudy}))
if reStudy in flags: if reStudy in flags:
var msg: cstring var msg: cstring
result.e = pcre.study(result.h, 0, msg) result.e = pcre.study(result.h, 0, addr msg)
if not isNil(msg): raiseInvalidRegex($msg) if not isNil(msg): raiseInvalidRegex($msg)
proc matchOrFind(s: string, pattern: Regex, matches: var openArray[string], proc matchOrFind(s: string, pattern: Regex, matches: var openArray[string],
@ -143,8 +146,8 @@ proc findBounds*(s: string, pattern: Regex,
proc findBounds*(s: string, pattern: Regex, proc findBounds*(s: string, pattern: Regex,
start = 0): tuple[first, last: int] = start = 0): tuple[first, last: int] =
## returns the starting position of `pattern` in `s`. If it does not ## returns the starting position and end position of ``pattern`` in ``s``.
## match, ``(-1,0)`` is returned. ## If it does not match, ``(-1,0)`` is returned.
var var
rtarray = initRtArray[cint](3) rtarray = initRtArray[cint](3)
rawMatches = rtarray.getRawData rawMatches = rtarray.getRawData
@ -231,6 +234,7 @@ iterator findAll*(s: string, pattern: Regex, start = 0): string =
if res < 0'i32: break if res < 0'i32: break
let a = rawMatches[0] let a = rawMatches[0]
let b = rawMatches[1] let b = rawMatches[1]
if a == b and a == i: break
yield substr(s, int(a), int(b)-1) yield substr(s, int(a), int(b)-1)
i = b i = b
@ -260,9 +264,9 @@ template `=~` *(s: string, pattern: Regex): expr =
## else: ## else:
## echo("syntax error") ## echo("syntax error")
## ##
bind MaxSubPatterns bind MaxSubpatterns
when not declaredInScope(matches): when not declaredInScope(matches):
var matches {.inject.}: array[0..MaxSubpatterns-1, string] var matches {.inject.}: array[MaxSubpatterns, string]
match(s, pattern, matches) match(s, pattern, matches)
# ------------------------- more string handling ------------------------------ # ------------------------- more string handling ------------------------------
@ -320,7 +324,7 @@ proc replacef*(s: string, sub: Regex, by: string): string =
## ##
## "var1<-keykey; val2<-key2key2" ## "var1<-keykey; val2<-key2key2"
result = "" result = ""
var caps: array[0..MaxSubpatterns-1, string] var caps: array[MaxSubpatterns, string]
var prev = 0 var prev = 0
while true: while true:
var match = findBounds(s, sub, caps, prev) var match = findBounds(s, sub, caps, prev)
@ -338,7 +342,7 @@ proc parallelReplace*(s: string, subs: openArray[
## applied in parallel. ## applied in parallel.
result = "" result = ""
var i = 0 var i = 0
var caps: array[0..MaxSubpatterns-1, string] var caps: array[MaxSubpatterns, string]
while i < s.len: while i < s.len:
block searchSubs: block searchSubs:
for j in 0..high(subs): for j in 0..high(subs):
@ -373,23 +377,26 @@ iterator split*(s: string, sep: Regex): string =
## Results in: ## Results in:
## ##
## .. code-block:: nim ## .. code-block:: nim
## ""
## "this" ## "this"
## "is" ## "is"
## "an" ## "an"
## "example" ## "example"
## ""
## ##
var var
first = 0 first = -1
last = 0 last = -1
while last < len(s): while last < len(s):
var x = matchLen(s, sep, last) var x = matchLen(s, sep, last)
if x > 0: inc(last, x) if x > 0: inc(last, x)
first = last first = last
if x == 0: inc(last)
while last < len(s): while last < len(s):
inc(last)
x = matchLen(s, sep, last) x = matchLen(s, sep, last)
if x > 0: break if x >= 0: break
if first < last: inc(last)
if first <= last:
yield substr(s, first, last-1) yield substr(s, first, last-1)
proc split*(s: string, sep: Regex): seq[string] = proc split*(s: string, sep: Regex): seq[string] =
@ -409,22 +416,28 @@ proc escapeRe*(s: string): string =
result.add(toHex(ord(c), 2)) result.add(toHex(ord(c), 2))
const ## common regular expressions const ## common regular expressions
reIdentifier* = r"\b[a-zA-Z_]+[a-zA-Z_0-9]*\b" ## describes an identifier reIdentifier* {.deprecated.} = r"\b[a-zA-Z_]+[a-zA-Z_0-9]*\b"
reNatural* = r"\b\d+\b" ## describes a natural number ## describes an identifier
reInteger* = r"\b[-+]?\d+\b" ## describes an integer reNatural* {.deprecated.} = r"\b\d+\b"
reHex* = r"\b0[xX][0-9a-fA-F]+\b" ## describes a hexadecimal number ## describes a natural number
reBinary* = r"\b0[bB][01]+\b" ## describes a binary number (example: 0b11101) reInteger* {.deprecated.} = r"\b[-+]?\d+\b"
reOctal* = r"\b0[oO][0-7]+\b" ## describes an octal number (example: 0o777) ## describes an integer
reFloat* = r"\b[-+]?[0-9]*\.?[0-9]+([eE][-+]?[0-9]+)?\b" reHex* {.deprecated.} = r"\b0[xX][0-9a-fA-F]+\b"
## describes a hexadecimal number
reBinary* {.deprecated.} = r"\b0[bB][01]+\b"
## describes a binary number (example: 0b11101)
reOctal* {.deprecated.} = r"\b0[oO][0-7]+\b"
## describes an octal number (example: 0o777)
reFloat* {.deprecated.} = r"\b[-+]?[0-9]*\.?[0-9]+([eE][-+]?[0-9]+)?\b"
## describes a floating point number ## describes a floating point number
reEmail* = r"\b[a-zA-Z0-9!#$%&'*+/=?^_`{|}~\-]+(?:\. &" & reEmail* {.deprecated.} = r"\b[a-zA-Z0-9!#$%&'*+/=?^_`{|}~\-]+(?:\. &" &
r"[a-zA-Z0-9!#$%&'*+/=?^_`{|}~-]+)" & r"[a-zA-Z0-9!#$%&'*+/=?^_`{|}~-]+)*@" &
r"*@(?:[a-zA-Z0-9](?:[a-zA-Z0-9-]*[a-zA-Z0-9])?\.)+" & r"(?:[a-zA-Z0-9](?:[a-zA-Z0-9-]*[a-zA-Z0-9])?\.)+" &
r"(?:[a-zA-Z]{2}|com|org|" & r"(?:[a-zA-Z]{2}|com|org|net|gov|mil|biz|" &
r"net|gov|mil|biz|info|mobi|name|aero|jobs|museum)\b" r"info|mobi|name|aero|jobs|museum)\b"
## describes a common email address ## describes a common email address
reURL* = r"\b(http(s)?|ftp|gopher|telnet|file|notes|ms\-help):" & reURL* {.deprecated.} = r"\b(http(s)?|ftp|gopher|telnet|file|notes|ms-help)" &
r"((//)|(\\\\))+[\w\d:#@%/;$()~_?\+\-\=\\\.\&]*\b" r":((//)|(\\\\))+[\w\d:#@%/;$()~_?\+\-\=\\\.\&]*\b"
## describes an URL ## describes an URL
when isMainModule: when isMainModule:
@ -442,7 +455,7 @@ when isMainModule:
assert find("_____abc_______", re"abc") == 5 assert find("_____abc_______", re"abc") == 5
var matches: array[0..5, string] var matches: array[6, string]
if match("abcdefg", re"c(d)ef(g)", matches, 2): if match("abcdefg", re"c(d)ef(g)", matches, 2):
assert matches[0] == "d" assert matches[0] == "d"
assert matches[1] == "g" assert matches[1] == "g"
@ -471,7 +484,12 @@ when isMainModule:
var accum: seq[string] = @[] var accum: seq[string] = @[]
for word in split("00232this02939is39an22example111", re"\d+"): for word in split("00232this02939is39an22example111", re"\d+"):
accum.add(word) accum.add(word)
assert(accum == @["this", "is", "an", "example"]) assert(accum == @["", "this", "is", "an", "example", ""])
accum = @[]
for word in split("AAA : : BBB", re"\s*:\s*"):
accum.add(word)
assert(accum == @["AAA", "", "BBB"])
for x in findAll("abcdef", re"^{.}", 3): for x in findAll("abcdef", re"^{.}", 3):
assert x == "d" assert x == "d"
@ -484,7 +502,7 @@ when isMainModule:
assert("XYZ".match(re"^\d*") == true) assert("XYZ".match(re"^\d*") == true)
block: block:
var matches: array[0..15, string] var matches: array[16, string]
if match("abcdefghijklmnop", re"(a)(b)(c)(d)(e)(f)(g)(h)(i)(j)(k)(l)(m)(n)(o)(p)", matches): if match("abcdefghijklmnop", re"(a)(b)(c)(d)(e)(f)(g)(h)(i)(j)(k)(l)(m)(n)(o)(p)", matches):
for i in 0..matches.high: for i in 0..matches.high:
assert matches[i] == $chr(i + 'a'.ord) assert matches[i] == $chr(i + 'a'.ord)

View file

@ -82,7 +82,7 @@ proc close*(sock: TSecureSocket) =
ERR_print_errors_fp(stderr) ERR_print_errors_fp(stderr)
raiseOSError(osLastError()) raiseOSError(osLastError())
when isMainModule: when not defined(testing) and isMainModule:
var s: TSecureSocket var s: TSecureSocket
echo connect(s, "smtp.gmail.com", 465) echo connect(s, "smtp.gmail.com", 465)

View file

@ -10,7 +10,7 @@
## This module implements a zip archive creator/reader/modifier. ## This module implements a zip archive creator/reader/modifier.
import import
streams, libzip, times, os streams, libzip, times, os, strutils
type type
TZipArchive* = object of RootObj ## represents a zip archive TZipArchive* = object of RootObj ## represents a zip archive
@ -111,19 +111,25 @@ proc addFile*(z: var TZipArchive, dest: string, src: Stream) =
type type
TZipFileStream = object of StreamObj TZipFileStream = object of StreamObj
f: PZipFile f: PZipFile
atEnd: bool
PZipFileStream* = PZipFileStream* =
ref TZipFileStream ## a reader stream of a file within a zip archive ref TZipFileStream ## a reader stream of a file within a zip archive
proc fsClose(s: Stream) = zip_fclose(PZipFileStream(s).f) proc fsClose(s: Stream) = zip_fclose(PZipFileStream(s).f)
proc fsAtEnd(s: Stream): bool = PZipFileStream(s).atEnd
proc fsReadData(s: Stream, buffer: pointer, bufLen: int): int = proc fsReadData(s: Stream, buffer: pointer, bufLen: int): int =
result = zip_fread(PZipFileStream(s).f, buffer, bufLen) result = zip_fread(PZipFileStream(s).f, buffer, bufLen)
if result == 0:
PZipFileStream(s).atEnd = true
proc newZipFileStream(f: PZipFile): PZipFileStream = proc newZipFileStream(f: PZipFile): PZipFileStream =
new(result) new(result)
result.f = f result.f = f
result.atEnd = false
result.closeImpl = fsClose result.closeImpl = fsClose
result.readDataImpl = fsReadData result.readDataImpl = fsReadData
result.atEndImpl = fsAtEnd
# other methods are nil! # other methods are nil!
# ---------------------------------------------------------------------------- # ----------------------------------------------------------------------------
@ -158,12 +164,20 @@ proc extractFile*(z: var TZipArchive, srcFile: string, dest: Stream) =
proc extractFile*(z: var TZipArchive, srcFile: string, dest: string) = proc extractFile*(z: var TZipArchive, srcFile: string, dest: string) =
## extracts a file from the zip archive `z` to the destination filename. ## extracts a file from the zip archive `z` to the destination filename.
var file = newFileStream(dest, fmReadWrite) var file = newFileStream(dest, fmWrite)
extractFile(z, srcFile, file) extractFile(z, srcFile, file)
file.close() file.close()
proc extractAll*(z: var TZipArchive, dest: string) = proc extractAll*(z: var TZipArchive, dest: string) =
## extracts all files from archive `z` to the destination directory. ## extracts all files from archive `z` to the destination directory.
for file in walkFiles(z): for file in walkFiles(z):
extractFile(z, file, dest / extractFilename(file)) if file.endsWith("/"):
createDir(dest / file)
else:
extractFile(z, file, dest / file)
when not defined(testing) and isMainModule:
var zip: TZipArchive
if not zip.open("nim-0.11.0.zip"):
raise newException(IOError, "opening zip failed")
zip.extractAll("test")

View file

@ -36,8 +36,11 @@ type
onsubmit*: proc (event: ref TEvent) {.nimcall.} onsubmit*: proc (event: ref TEvent) {.nimcall.}
onunload*: proc (event: ref TEvent) {.nimcall.} onunload*: proc (event: ref TEvent) {.nimcall.}
TWindow* {.importc.} = object of TEventHandlers addEventListener*: proc(ev: cstring, cb: proc(ev: ref TEvent), useCapture: bool = false) {.nimcall.}
document*: ref TDocument
Window* = ref WindowObj
WindowObj {.importc.} = object of TEventHandlers
document*: Document
event*: ref TEvent event*: ref TEvent
history*: ref THistory history*: ref THistory
location*: ref TLocation location*: ref TLocation
@ -55,7 +58,6 @@ type
status*: cstring status*: cstring
toolbar*: ref TToolBar toolbar*: ref TToolBar
addEventListener*: proc(ev: cstring, cb: proc(ev: ref TEvent) ) {.nimcall.}
alert*: proc (msg: cstring) {.nimcall.} alert*: proc (msg: cstring) {.nimcall.}
back*: proc () {.nimcall.} back*: proc () {.nimcall.}
blur*: proc () {.nimcall.} blur*: proc () {.nimcall.}
@ -75,7 +77,7 @@ type
moveBy*: proc (x, y: int) {.nimcall.} moveBy*: proc (x, y: int) {.nimcall.}
moveTo*: proc (x, y: int) {.nimcall.} moveTo*: proc (x, y: int) {.nimcall.}
open*: proc (uri, windowname: cstring, open*: proc (uri, windowname: cstring,
properties: cstring = nil): ref TWindow {.nimcall.} properties: cstring = nil): Window {.nimcall.}
print*: proc () {.nimcall.} print*: proc () {.nimcall.}
prompt*: proc (text, default: cstring): cstring {.nimcall.} prompt*: proc (text, default: cstring): cstring {.nimcall.}
releaseEvents*: proc (eventMask: int) {.nimcall.} releaseEvents*: proc (eventMask: int) {.nimcall.}
@ -89,115 +91,8 @@ type
stop*: proc () {.nimcall.} stop*: proc () {.nimcall.}
frames*: seq[TFrame] frames*: seq[TFrame]
TFrame* {.importc.} = object of TWindow Frame* = ref FrameObj
FrameObj {.importc.} = object of WindowObj
TDocument* {.importc.} = object of TEventHandlers
addEventListener*: proc(ev: cstring, cb: proc(ev: ref TEvent) ) {.nimcall.}
alinkColor*: cstring
bgColor*: cstring
charset*: cstring
cookie*: cstring
defaultCharset*: cstring
fgColor*: cstring
lastModified*: cstring
linkColor*: cstring
referrer*: cstring
title*: cstring
URL*: cstring
vlinkColor*: cstring
captureEvents*: proc (eventMask: int) {.nimcall.}
createAttribute*: proc (identifier: cstring): ref TNode {.nimcall.}
createElement*: proc (identifier: cstring): ref TNode {.nimcall.}
createTextNode*: proc (identifier: cstring): ref TNode {.nimcall.}
getElementById*: proc (id: cstring): ref TNode {.nimcall.}
getElementsByName*: proc (name: cstring): seq[ref TNode] {.nimcall.}
getElementsByTagName*: proc (name: cstring): seq[ref TNode] {.nimcall.}
getElementsByClassName*: proc (name: cstring): seq[ref TNode] {.nimcall.}
getSelection*: proc (): cstring {.nimcall.}
handleEvent*: proc (event: ref TEvent) {.nimcall.}
open*: proc () {.nimcall.}
releaseEvents*: proc (eventMask: int) {.nimcall.}
routeEvent*: proc (event: ref TEvent) {.nimcall.}
write*: proc (text: cstring) {.nimcall.}
writeln*: proc (text: cstring) {.nimcall.}
anchors*: seq[ref TAnchor]
forms*: seq[ref TForm]
images*: seq[ref TImage]
applets*: seq[ref TApplet]
embeds*: seq[ref TEmbed]
links*: seq[ref TLink]
TLink* {.importc.} = object of RootObj
name*: cstring
target*: cstring
text*: cstring
x*: int
y*: int
TEmbed* {.importc.} = object of RootObj
height*: int
hspace*: int
name*: cstring
src*: cstring
width*: int
`type`*: cstring
vspace*: int
play*: proc () {.nimcall.}
stop*: proc () {.nimcall.}
TAnchor* {.importc.} = object of RootObj
name*: cstring
text*: cstring
x*, y*: int
TApplet* {.importc.} = object of RootObj
TElement* {.importc.} = object of TEventHandlers
checked*: bool
defaultChecked*: bool
defaultValue*: cstring
disabled*: bool
form*: ref TForm
name*: cstring
readOnly*: bool
`type`*: cstring
value*: cstring
blur*: proc () {.nimcall.}
click*: proc () {.nimcall.}
focus*: proc () {.nimcall.}
handleEvent*: proc (event: ref TEvent) {.nimcall.}
select*: proc () {.nimcall.}
options*: seq[ref TOption]
TOption* {.importc.} = object of RootObj
defaultSelected*: bool
selected*: bool
selectedIndex*: int
text*: cstring
value*: cstring
TForm* {.importc.} = object of TEventHandlers
action*: cstring
encoding*: cstring
`method`*: cstring
name*: cstring
target*: cstring
handleEvent*: proc (event: ref TEvent) {.nimcall.}
reset*: proc () {.nimcall.}
submit*: proc () {.nimcall.}
elements*: seq[ref TElement]
TImage* {.importc.} = object of TEventHandlers
border*: int
complete*: bool
height*: int
hspace*: int
lowsrc*: cstring
name*: cstring
src*: cstring
vspace*: int
width*: int
handleEvent*: proc (event: ref TEvent) {.nimcall.}
ClassList* {.importc.} = object of RootObj ClassList* {.importc.} = object of RootObj
add*: proc (class: cstring) {.nimcall.} add*: proc (class: cstring) {.nimcall.}
@ -218,43 +113,153 @@ type
DocumentTypeNode, DocumentTypeNode,
DocumentFragmentNode, DocumentFragmentNode,
NotationNode NotationNode
TNode* {.importc.} = object of RootObj
attributes*: seq[ref TNode] Node* = ref NodeObj
childNodes*: seq[ref TNode] NodeObj {.importc.} = object of TEventHandlers
children*: seq[ref TNode] attributes*: seq[Node]
classList*: ref Classlist childNodes*: seq[Node]
children*: seq[Node]
data*: cstring data*: cstring
firstChild*: ref TNode firstChild*: Node
lastChild*: ref TNode lastChild*: Node
nextSibling*: ref TNode nextSibling*: Node
nodeName*: cstring nodeName*: cstring
nodeType*: TNodeType nodeType*: TNodeType
nodeValue*: cstring nodeValue*: cstring
parentNode*: ref TNode parentNode*: Node
previousSibling*: ref TNode previousSibling*: Node
appendChild*: proc (child: ref TNode) {.nimcall.} appendChild*: proc (child: Node) {.nimcall.}
appendData*: proc (data: cstring) {.nimcall.} appendData*: proc (data: cstring) {.nimcall.}
cloneNode*: proc (copyContent: bool): ref TNode {.nimcall.} cloneNode*: proc (copyContent: bool): Node {.nimcall.}
deleteData*: proc (start, len: int) {.nimcall.} deleteData*: proc (start, len: int) {.nimcall.}
getAttribute*: proc (attr: cstring): cstring {.nimcall.} getAttribute*: proc (attr: cstring): cstring {.nimcall.}
getAttributeNode*: proc (attr: cstring): ref TNode {.nimcall.} getAttributeNode*: proc (attr: cstring): Node {.nimcall.}
getElementsByTagName*: proc (name: cstring): seq[ref TNode] {.nimcall.}
getElementsByClassName*: proc (name: cstring): seq[ref TNode] {.nimcall.}
hasChildNodes*: proc (): bool {.nimcall.} hasChildNodes*: proc (): bool {.nimcall.}
innerHTML*: cstring innerHTML*: cstring
insertBefore*: proc (newNode, before: ref TNode) {.nimcall.} insertBefore*: proc (newNode, before: Node) {.nimcall.}
insertData*: proc (position: int, data: cstring) {.nimcall.} insertData*: proc (position: int, data: cstring) {.nimcall.}
addEventListener*: proc(ev: cstring, cb: proc(ev: ref TEvent)) {.nimcall.}
removeAttribute*: proc (attr: cstring) {.nimcall.} removeAttribute*: proc (attr: cstring) {.nimcall.}
removeAttributeNode*: proc (attr: ref TNode) {.nimcall.} removeAttributeNode*: proc (attr: Node) {.nimcall.}
removeChild*: proc (child: ref TNode) {.nimcall.} removeChild*: proc (child: Node) {.nimcall.}
replaceChild*: proc (newNode, oldNode: ref TNode) {.nimcall.} replaceChild*: proc (newNode, oldNode: Node) {.nimcall.}
replaceData*: proc (start, len: int, text: cstring) {.nimcall.} replaceData*: proc (start, len: int, text: cstring) {.nimcall.}
scrollIntoView*: proc () {.nimcall.} scrollIntoView*: proc () {.nimcall.}
setAttribute*: proc (name, value: cstring) {.nimcall.} setAttribute*: proc (name, value: cstring) {.nimcall.}
setAttributeNode*: proc (attr: ref TNode) {.nimcall.} setAttributeNode*: proc (attr: Node) {.nimcall.}
style*: ref TStyle style*: ref TStyle
Document* = ref DocumentObj
DocumentObj {.importc.} = object of NodeObj
alinkColor*: cstring
bgColor*: cstring
body*: Element
charset*: cstring
cookie*: cstring
defaultCharset*: cstring
fgColor*: cstring
head*: Element
lastModified*: cstring
linkColor*: cstring
referrer*: cstring
title*: cstring
URL*: cstring
vlinkColor*: cstring
captureEvents*: proc (eventMask: int) {.nimcall.}
createAttribute*: proc (identifier: cstring): Node {.nimcall.}
createElement*: proc (identifier: cstring): Element {.nimcall.}
createTextNode*: proc (identifier: cstring): Node {.nimcall.}
getElementById*: proc (id: cstring): Element {.nimcall.}
getElementsByName*: proc (name: cstring): seq[Element] {.nimcall.}
getElementsByTagName*: proc (name: cstring): seq[Element] {.nimcall.}
getElementsByClassName*: proc (name: cstring): seq[Element] {.nimcall.}
getSelection*: proc (): cstring {.nimcall.}
handleEvent*: proc (event: ref TEvent) {.nimcall.}
open*: proc () {.nimcall.}
releaseEvents*: proc (eventMask: int) {.nimcall.}
routeEvent*: proc (event: ref TEvent) {.nimcall.}
write*: proc (text: cstring) {.nimcall.}
writeln*: proc (text: cstring) {.nimcall.}
anchors*: seq[AnchorElement]
forms*: seq[FormElement]
images*: seq[ImageElement]
applets*: seq[ref TApplet]
embeds*: seq[EmbedElement]
links*: seq[LinkElement]
Element* = ref ElementObj
ElementObj {.importc.} = object of NodeObj
classList*: ref Classlist
checked*: bool
defaultChecked*: bool
defaultValue*: cstring
disabled*: bool
form*: FormElement
name*: cstring
readOnly*: bool
blur*: proc () {.nimcall.}
click*: proc () {.nimcall.}
focus*: proc () {.nimcall.}
handleEvent*: proc (event: ref TEvent) {.nimcall.}
select*: proc () {.nimcall.}
options*: seq[OptionElement]
getElementsByTagName*: proc (name: cstring): seq[Element] {.nimcall.}
getElementsByClassName*: proc (name: cstring): seq[Element] {.nimcall.}
LinkElement* = ref LinkObj
LinkObj {.importc.} = object of ElementObj
target*: cstring
text*: cstring
x*: int
y*: int
EmbedElement* = ref EmbedObj
EmbedObj {.importc.} = object of ElementObj
height*: int
hspace*: int
src*: cstring
width*: int
`type`*: cstring
vspace*: int
play*: proc () {.nimcall.}
stop*: proc () {.nimcall.}
AnchorElement* = ref AnchorObj
AnchorObj {.importc.} = object of ElementObj
text*: cstring
x*, y*: int
TApplet* {.importc.} = object of RootObj
OptionElement* = ref OptionObj
OptionObj {.importc.} = object of ElementObj
defaultSelected*: bool
selected*: bool
selectedIndex*: int
text*: cstring
value*: cstring
FormElement* = ref FormObj
FormObj {.importc.} = object of ElementObj
action*: cstring
encoding*: cstring
`method`*: cstring
target*: cstring
reset*: proc () {.nimcall.}
submit*: proc () {.nimcall.}
elements*: seq[Element]
ImageElement* = ref ImageObj
ImageObj {.importc.} = object of ElementObj
border*: int
complete*: bool
height*: int
hspace*: int
lowsrc*: cstring
src*: cstring
vspace*: int
width*: int
TStyle* {.importc.} = object of RootObj TStyle* {.importc.} = object of RootObj
background*: cstring background*: cstring
backgroundAttachment*: cstring backgroundAttachment*: cstring
@ -350,7 +355,7 @@ type
setAttribute*: proc (attr, value: cstring, caseSensitive=false) {.nimcall.} setAttribute*: proc (attr, value: cstring, caseSensitive=false) {.nimcall.}
TEvent* {.importc.} = object of RootObj TEvent* {.importc.} = object of RootObj
target*: ref TNode target*: Node
altKey*, ctrlKey*, shiftKey*: bool altKey*, ctrlKey*, shiftKey*: bool
button*: int button*: int
clientX*, clientY*: int clientX*, clientY*: int
@ -448,8 +453,8 @@ type
TInterval* {.importc.} = object of RootObj TInterval* {.importc.} = object of RootObj
var var
window* {.importc, nodecl.}: ref TWindow window* {.importc, nodecl.}: Window
document* {.importc, nodecl.}: ref TDocument document* {.importc, nodecl.}: Document
navigator* {.importc, nodecl.}: ref TNavigator navigator* {.importc, nodecl.}: ref TNavigator
screen* {.importc, nodecl.}: ref TScreen screen* {.importc, nodecl.}: ref TScreen
@ -466,3 +471,17 @@ proc isNaN*(x: BiggestFloat): bool {.importc, nodecl.}
proc parseFloat*(s: cstring): BiggestFloat {.importc, nodecl.} proc parseFloat*(s: cstring): BiggestFloat {.importc, nodecl.}
proc parseInt*(s: cstring): int {.importc, nodecl.} proc parseInt*(s: cstring): int {.importc, nodecl.}
proc parseInt*(s: cstring, radix: int):int {.importc, nodecl.} proc parseInt*(s: cstring, radix: int):int {.importc, nodecl.}
type
TWindow* {.deprecated.} = WindowObj
TFrame* {.deprecated.} = FrameObj
TNode* {.deprecated.} = NodeObj
TDocument* {.deprecated.} = DocumentObj
TElement* {.deprecated.} = ElementObj
TLink* {.deprecated.} = LinkObj
TEmbed* {.deprecated.} = EmbedObj
TAnchor* {.deprecated.} = AnchorObj
TOption* {.deprecated.} = OptionObj
TForm* {.deprecated.} = FormObj
TImage* {.deprecated.} = ImageObj

View file

@ -343,15 +343,15 @@ struct TFrame {
}; };
#define nimfr(proc, file) \ #define nimfr(proc, file) \
TFrame F; \ TFrame FR; \
F.procname = proc; F.filename = file; F.line = 0; F.len = 0; nimFrame(&F); FR.procname = proc; FR.filename = file; FR.line = 0; FR.len = 0; nimFrame(&FR);
#define nimfrs(proc, file, slots, length) \ #define nimfrs(proc, file, slots, length) \
struct {TFrame* prev;NCSTRING procname;NI line;NCSTRING filename; NI len; TVarSlot s[slots];} F; \ struct {TFrame* prev;NCSTRING procname;NI line;NCSTRING filename; NI len; TVarSlot s[slots];} FR; \
F.procname = proc; F.filename = file; F.line = 0; F.len = length; nimFrame((TFrame*)&F); FR.procname = proc; FR.filename = file; FR.line = 0; FR.len = length; nimFrame((TFrame*)&FR);
#define nimln(n, file) \ #define nimln(n, file) \
F.line = n; F.filename = file; FR.line = n; FR.filename = file;
#define NIM_POSIX_INIT __attribute__((constructor)) #define NIM_POSIX_INIT __attribute__((constructor))

View file

@ -564,7 +564,7 @@ proc fixupEmbeddedRef(n, a, b: PRstNode) =
proc parsePostfix(p: var TRstParser, n: PRstNode): PRstNode = proc parsePostfix(p: var TRstParser, n: PRstNode): PRstNode =
result = n result = n
if isInlineMarkupEnd(p, "_"): if isInlineMarkupEnd(p, "_") or isInlineMarkupEnd(p, "__"):
inc(p.idx) inc(p.idx)
if p.tok[p.idx-2].symbol == "`" and p.tok[p.idx-3].symbol == ">": if p.tok[p.idx-2].symbol == "`" and p.tok[p.idx-3].symbol == ">":
var a = newRstNode(rnInner) var a = newRstNode(rnInner)
@ -877,7 +877,7 @@ proc parseUntilNewline(p: var TRstParser, father: PRstNode) =
of tkWhite, tkWord, tkAdornment, tkOther, tkPunct: parseInline(p, father) of tkWhite, tkWord, tkAdornment, tkOther, tkPunct: parseInline(p, father)
of tkEof, tkIndent: break of tkEof, tkIndent: break
proc parseSection(p: var TRstParser, result: PRstNode) proc parseSection(p: var TRstParser, result: PRstNode) {.gcsafe.}
proc parseField(p: var TRstParser): PRstNode = proc parseField(p: var TRstParser): PRstNode =
## Returns a parsed rnField node. ## Returns a parsed rnField node.
## ##
@ -947,7 +947,7 @@ proc getArgument(n: PRstNode): string =
if n.sons[0] == nil: result = "" if n.sons[0] == nil: result = ""
else: result = addNodes(n.sons[0]) else: result = addNodes(n.sons[0])
proc parseDotDot(p: var TRstParser): PRstNode proc parseDotDot(p: var TRstParser): PRstNode {.gcsafe.}
proc parseLiteralBlock(p: var TRstParser): PRstNode = proc parseLiteralBlock(p: var TRstParser): PRstNode =
result = newRstNode(rnLiteralBlock) result = newRstNode(rnLiteralBlock)
var n = newRstNode(rnLeaf, "") var n = newRstNode(rnLeaf, "")
@ -1133,7 +1133,7 @@ proc getColumns(p: var TRstParser, cols: var TIntSeq) =
# last column has no limit: # last column has no limit:
cols[L - 1] = 32000 cols[L - 1] = 32000
proc parseDoc(p: var TRstParser): PRstNode proc parseDoc(p: var TRstParser): PRstNode {.gcsafe.}
proc parseSimpleTable(p: var TRstParser): PRstNode = proc parseSimpleTable(p: var TRstParser): PRstNode =
var var

View file

@ -98,7 +98,8 @@ type
indent: int indent: int
verbatim: int verbatim: int
proc renderRstToRst(d: var TRenderContext, n: PRstNode, result: var string) proc renderRstToRst(d: var TRenderContext, n: PRstNode,
result: var string) {.gcsafe.}
proc renderRstSons(d: var TRenderContext, n: PRstNode, result: var string) = proc renderRstSons(d: var TRenderContext, n: PRstNode, result: var string) =
for i in countup(0, len(n) - 1): for i in countup(0, len(n) - 1):

View file

@ -1251,5 +1251,6 @@ proc rstToHtml*(s: string, options: TRstParseOptions,
when isMainModule: when isMainModule:
echo rstToHtml("*Hello* **world**!", {}, assert rstToHtml("*Hello* **world**!", {},
newStringTable(modeStyleInsensitive)) newStringTable(modeStyleInsensitive)) ==
"<em>Hello</em> <strong>world</strong>!"

View file

@ -95,10 +95,11 @@ type
header: "<dirent.h>", final, pure.} = object ## dirent_t struct header: "<dirent.h>", final, pure.} = object ## dirent_t struct
d_ino*: Tino ## File serial number. d_ino*: Tino ## File serial number.
when defined(linux) or defined(macosx) or defined(bsd): when defined(linux) or defined(macosx) or defined(bsd):
d_off*: TOff ## Not an offset. Value that ``telldir()`` would return.
d_reclen*: cshort ## Length of this record. (not POSIX) d_reclen*: cshort ## Length of this record. (not POSIX)
d_type*: int8 ## Type of file; not supported by all filesystem types. d_type*: int8 ## Type of file; not supported by all filesystem types.
## (not POSIX) ## (not POSIX)
when defined(linux) or defined(bsd):
d_off*: TOff ## Not an offset. Value that ``telldir()`` would return.
d_name*: array [0..255, char] ## Name of entry. d_name*: array [0..255, char] ## Name of entry.
Tflock* {.importc: "struct flock", final, pure, Tflock* {.importc: "struct flock", final, pure,
@ -1579,8 +1580,11 @@ else:
when defined(macosx): when defined(macosx):
# We can't use the NOSIGNAL flag in the ``send`` function, it has no effect # We can't use the NOSIGNAL flag in the ``send`` function, it has no effect
var # Instead we should use SO_NOSIGPIPE in setsockopt
const
MSG_NOSIGNAL* = 0'i32 MSG_NOSIGNAL* = 0'i32
var
SO_NOSIGPIPE* {.importc, header: "<sys/socket.h>".}: cint
else: else:
var var
MSG_NOSIGNAL* {.importc, header: "<sys/socket.h>".}: cint MSG_NOSIGNAL* {.importc, header: "<sys/socket.h>".}: cint

View file

@ -18,16 +18,13 @@ const
NCCS* = 32 NCCS* = 32
type type
Termios* = object {.importc: "struct termios", header: "<termios.h>", final, pure.} Termios* {.importc: "struct termios", header: "<termios.h>".} = object
iflag*: Tcflag # input mode flags c_iflag*: Tcflag # input mode flags
oflag*: Tcflag # output mode flags c_oflag*: Tcflag # output mode flags
cflag*: Tcflag # control mode flags c_cflag*: Tcflag # control mode flags
lflag*: Tcflag # local mode flags c_lflag*: Tcflag # local mode flags
line*: cuchar # line discipline c_line*: cuchar # line discipline
cc*: array[NCCS, cuchar] # control characters c_cc*: array[NCCS, cuchar] # control characters
ispeed*: Speed # input speed
ospeed*: Speed # output speed
# cc characters # cc characters

View file

@ -221,7 +221,7 @@ proc spawn*[TIn](p: var TActorPool[TIn, void], input: TIn,
setupTask() setupTask()
schedule() schedule()
when isMainModule: when not defined(testing) and isMainModule:
var var
a: TActorPool[int, void] a: TActorPool[int, void]
createActorPool(a) createActorPool(a)

View file

@ -24,7 +24,7 @@ proc `*`*(x: int, order: SortOrder): int {.inline.} =
var y = order.ord - 1 var y = order.ord - 1
result = (x xor y) - y result = (x xor y) - y
proc reverse*[T](a: var openArray[T], first, last: int) = proc reverse*[T](a: var openArray[T], first, last: Natural) =
## reverses the array ``a[first..last]``. ## reverses the array ``a[first..last]``.
var x = first var x = first
var y = last var y = last
@ -37,11 +37,11 @@ proc reverse*[T](a: var openArray[T]) =
## reverses the array `a`. ## reverses the array `a`.
reverse(a, 0, a.high) reverse(a, 0, a.high)
proc reversed*[T](a: openArray[T], first, last: int): seq[T] = proc reversed*[T](a: openArray[T], first, last: Natural): seq[T] =
## returns the reverse of the array `a[first..last]`. ## returns the reverse of the array `a[first..last]`.
result = newSeq[T](last - first + 1) result = newSeq[T](last - first + 1)
var x = first var x = first.int
var y = last var y = last.int
while x <= last: while x <= last:
result[x] = a[y] result[x] = a[y]
dec(y) dec(y)

View file

@ -634,6 +634,93 @@ when defined(windows) or defined(nimdoc):
# free ``ol``. # free ``ol``.
return retFuture return retFuture
proc recvInto*(socket: TAsyncFD, buf: cstring, size: int,
flags = {SocketFlag.SafeDisconn}): Future[int] =
## Reads **up to** ``size`` bytes from ``socket`` into ``buf``, which must
## at least be of that size. Returned future will complete once all the
## data requested is read, a part of the data has been read, or the socket
## has disconnected in which case the future will complete with a value of
## ``0``.
##
## **Warning**: The ``Peek`` socket flag is not supported on Windows.
# Things to note:
# * When WSARecv completes immediately then ``bytesReceived`` is very
# unreliable.
# * Still need to implement message-oriented socket disconnection,
# '\0' in the message currently signifies a socket disconnect. Who
# knows what will happen when someone sends that to our socket.
verifyPresence(socket)
assert SocketFlag.Peek notin flags, "Peek not supported on Windows."
var retFuture = newFuture[int]("recvInto")
#buf[] = '\0'
var dataBuf: TWSABuf
dataBuf.buf = buf
dataBuf.len = size
var bytesReceived: Dword
var flagsio = flags.toOSFlags().Dword
var ol = PCustomOverlapped()
GC_ref(ol)
ol.data = TCompletionData(fd: socket, cb:
proc (fd: TAsyncFD, bytesCount: Dword, errcode: OSErrorCode) =
if not retFuture.finished:
if errcode == OSErrorCode(-1):
if bytesCount == 0 and dataBuf.buf[0] == '\0':
retFuture.complete(0)
else:
retFuture.complete(bytesCount)
else:
if flags.isDisconnectionError(errcode):
retFuture.complete(0)
else:
retFuture.fail(newException(OSError, osErrorMsg(errcode)))
if dataBuf.buf != nil:
dataBuf.buf = nil
)
let ret = WSARecv(socket.SocketHandle, addr dataBuf, 1, addr bytesReceived,
addr flagsio, cast[POVERLAPPED](ol), nil)
if ret == -1:
let err = osLastError()
if err.int32 != ERROR_IO_PENDING:
if dataBuf.buf != nil:
dataBuf.buf = nil
GC_unref(ol)
if flags.isDisconnectionError(err):
retFuture.complete(0)
else:
retFuture.fail(newException(OSError, osErrorMsg(err)))
elif ret == 0 and bytesReceived == 0 and dataBuf.buf[0] == '\0':
# We have to ensure that the buffer is empty because WSARecv will tell
# us immediately when it was disconnected, even when there is still
# data in the buffer.
# We want to give the user as much data as we can. So we only return
# the empty string (which signals a disconnection) when there is
# nothing left to read.
retFuture.complete(0)
# 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.
# From my tests bytesReceived isn't reliable.
let realSize =
if bytesReceived == 0:
size
else:
bytesReceived
assert realSize <= size
retFuture.complete(realSize)
# We don't deallocate ``ol`` here because even though this completed
# immediately poll will still be notified about its completion and it will
# free ``ol``.
return retFuture
proc send*(socket: TAsyncFD, data: string, proc send*(socket: TAsyncFD, data: string,
flags = {SocketFlag.SafeDisconn}): Future[void] = flags = {SocketFlag.SafeDisconn}): Future[void] =
## Sends ``data`` to ``socket``. The returned future will complete once all ## Sends ``data`` to ``socket``. The returned future will complete once all
@ -841,6 +928,8 @@ else:
proc newAsyncRawSocket*(domain: cint, typ: cint, protocol: cint): TAsyncFD = proc newAsyncRawSocket*(domain: cint, typ: cint, protocol: cint): TAsyncFD =
result = newRawSocket(domain, typ, protocol).TAsyncFD result = newRawSocket(domain, typ, protocol).TAsyncFD
result.SocketHandle.setBlocking(false) result.SocketHandle.setBlocking(false)
when defined(macosx):
result.SocketHandle.setSockOptInt(SOL_SOCKET, SO_NOSIGPIPE, 1)
register(result) register(result)
proc newAsyncRawSocket*(domain: Domain = AF_INET, proc newAsyncRawSocket*(domain: Domain = AF_INET,
@ -848,6 +937,8 @@ else:
protocol: Protocol = IPPROTO_TCP): TAsyncFD = protocol: Protocol = IPPROTO_TCP): TAsyncFD =
result = newRawSocket(domain, typ, protocol).TAsyncFD result = newRawSocket(domain, typ, protocol).TAsyncFD
result.SocketHandle.setBlocking(false) result.SocketHandle.setBlocking(false)
when defined(macosx):
result.SocketHandle.setSockOptInt(SOL_SOCKET, SO_NOSIGPIPE, 1)
register(result) register(result)
proc closeSocket*(sock: TAsyncFD) = proc closeSocket*(sock: TAsyncFD) =
@ -959,7 +1050,6 @@ else:
result = true result = true
let res = recv(sock.SocketHandle, addr readBuffer[0], size.cint, let res = recv(sock.SocketHandle, addr readBuffer[0], size.cint,
flags.toOSFlags()) flags.toOSFlags())
#echo("recv cb res: ", res)
if res < 0: if res < 0:
let lastError = osLastError() let lastError = osLastError()
if lastError.int32 notin {EINTR, EWOULDBLOCK, EAGAIN}: if lastError.int32 notin {EINTR, EWOULDBLOCK, EAGAIN}:
@ -980,6 +1070,30 @@ else:
addRead(socket, cb) addRead(socket, cb)
return retFuture return retFuture
proc recvInto*(socket: TAsyncFD, buf: cstring, size: int,
flags = {SocketFlag.SafeDisconn}): Future[int] =
var retFuture = newFuture[int]("recvInto")
proc cb(sock: TAsyncFD): bool =
result = true
let res = recv(sock.SocketHandle, buf, size.cint,
flags.toOSFlags())
if res < 0:
let lastError = osLastError()
if lastError.int32 notin {EINTR, EWOULDBLOCK, EAGAIN}:
if flags.isDisconnectionError(lastError):
retFuture.complete(0)
else:
retFuture.fail(newException(OSError, osErrorMsg(lastError)))
else:
result = false # We still want this callback to be called.
else:
retFuture.complete(res)
# TODO: The following causes a massive slowdown.
#if not cb(socket):
addRead(socket, cb)
return retFuture
proc send*(socket: TAsyncFD, data: string, proc send*(socket: TAsyncFD, data: string,
flags = {SocketFlag.SafeDisconn}): Future[void] = flags = {SocketFlag.SafeDisconn}): Future[void] =
var retFuture = newFuture[void]("send") var retFuture = newFuture[void]("send")
@ -1273,7 +1387,7 @@ proc processBody(node, retFutureSym: NimNode,
else: discard else: discard
for i in 0 .. <result.len: for i in 0 .. <result.len:
result[i] = processBody(result[i], retFutureSym, subTypeIsVoid, tryStmt) result[i] = processBody(result[i], retFutureSym, subTypeIsVoid, nil)
proc getName(node: NimNode): string {.compileTime.} = proc getName(node: NimNode): string {.compileTime.} =
case node.kind case node.kind
@ -1378,8 +1492,8 @@ macro async*(prc: stmt): stmt {.immediate.} =
result[6] = outerProcBody result[6] = outerProcBody
#echo(treeRepr(result)) #echo(treeRepr(result))
if prc[0].getName == "test3": #if prc[0].getName == "test":
echo(toStrLit(result)) # echo(toStrLit(result))
proc recvLine*(socket: TAsyncFD): Future[string] {.async.} = proc recvLine*(socket: TAsyncFD): Future[string] {.async.} =
## Reads a line of data from ``socket``. Returned future will complete once ## Reads a line of data from ``socket``. Returned future will complete once

View file

@ -300,7 +300,7 @@ proc newAsyncFtpClient*(address: string, port = Port(21),
result.dsockConnected = false result.dsockConnected = false
result.csock = newAsyncSocket() result.csock = newAsyncSocket()
when isMainModule: when not defined(testing) and isMainModule:
var ftp = newAsyncFtpClient("example.com", user = "test", pass = "test") var ftp = newAsyncFtpClient("example.com", user = "test", pass = "test")
proc main(ftp: AsyncFtpClient) {.async.} = proc main(ftp: AsyncFtpClient) {.async.} =
await ftp.connect() await ftp.connect()

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