diff options
author | Simon Hafner <hafnersimon@gmail.com> | 2015-05-13 12:06:05 -0500 |
---|---|---|
committer | Simon Hafner <hafnersimon@gmail.com> | 2015-05-13 12:06:05 -0500 |
commit | 9c4a74637db266c3cfcffcfb1e65bae982c6e4bf (patch) | |
tree | 495f50a989d7ab696ddb0b7b0e815c572ab27f3c | |
parent | c55f884b5c0ebc0b637138a8de446ba1fd05acdf (diff) | |
parent | 0b184f2584221543a7dec9c8ae4a700533919e0c (diff) | |
download | Nim-9c4a74637db266c3cfcffcfb1e65bae982c6e4bf.tar.gz |
Merge branch 'devel' into jpoirier-realtimeGCTest
250 files changed, 10959 insertions, 8856 deletions
diff --git a/.gitignore b/.gitignore index d804fb8f5..462df4efc 100644 --- a/.gitignore +++ b/.gitignore @@ -41,4 +41,3 @@ xcuserdata/ /testresults.html /testresults.json testament.db -/csources/ diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 000000000..26f35d82c --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "csources"] + path = csources + url = ../../nim-lang/csources.git diff --git a/build.sh b/build.sh index 139c28359..91e169241 100644..100755 --- a/build.sh +++ b/build.sh @@ -2,8 +2,8 @@ set -e set -x -if [ ! -d "csources" ]; then - git clone --depth 1 https://github.com/nim-lang/csources.git +if [ ! -e csources/.git ]; then + git submodule update --init --depth 1 fi cd "csources" diff --git a/compiler/ast.nim b/compiler/ast.nim index 10f2a71da..3798410e8 100644 --- a/compiler/ast.nim +++ b/compiler/ast.nim @@ -1,7 +1,7 @@ # # # The Nim Compiler -# (c) Copyright 2013 Andreas Rumpf +# (c) Copyright 2015 Andreas Rumpf # # See the file "copying.txt", included in this # distribution, for details about the copyright. @@ -296,6 +296,7 @@ const sfCompileToCpp* = sfInfixCall # compile the module as C++ code sfCompileToObjc* = sfNamedParamCall # compile the module as Objective-C code sfExperimental* = sfOverriden # module uses the .experimental switch + sfGoto* = sfOverriden # var is used for 'goto' code generation const # getting ready for the future expr/stmt merge @@ -472,7 +473,7 @@ type # T and I here can bind to both typedesc and static types # before this is determined, we'll consider them to be a # wildcard type. - tfGuarded # guarded pointer + tfHasAsgn # type has overloaded assignment operator tfBorrowDot # distinct type borrows '.' TTypeFlags* = set[TTypeFlag] @@ -529,19 +530,20 @@ type TMagic* = enum # symbols that require compiler magic: mNone, 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, mParseExprToAst, mParseStmtToAst, mExpandToAst, mQuoteAst, mUnaryLt, mInc, mDec, mOrd, mNew, mNewFinalize, mNewSeq, mLengthOpenArray, - mLengthStr, mLengthArray, mLengthSeq, mIncl, mExcl, mCard, mChr, mGCref, - mGCunref, + mLengthStr, mLengthArray, mLengthSeq, mXLenStr, mXLenSeq, + mIncl, mExcl, mCard, mChr, + mGCref, mGCunref, mAddI, mSubI, mMulI, mDivI, mModI, mAddI64, mSubI64, mMulI64, mDivI64, mModI64, mSucc, mPred, mAddF64, mSubF64, mMulF64, mDivF64, mShrI, mShlI, mBitandI, mBitorI, mBitxorI, mMinI, mMaxI, - mShrI64, mShlI64, mBitandI64, mBitorI64, mBitxorI64, mMinI64, mMaxI64, + mShrI64, mShlI64, mBitandI64, mBitorI64, mBitxorI64, mMinF64, mMaxF64, mAddU, mSubU, mMulU, mDivU, mModU, mEqI, mLeI, mLtI, @@ -550,7 +552,7 @@ type mEqEnum, mLeEnum, mLtEnum, mEqCh, mLeCh, mLtCh, mEqB, mLeB, mLtB, mEqRef, mEqUntracedRef, mLePtr, mLtPtr, mEqCString, mXor, mEqProc, mUnaryMinusI, mUnaryMinusI64, mAbsI, mAbsI64, mNot, - mUnaryPlusI, mBitnotI, mUnaryPlusI64, + mUnaryPlusI, mBitnotI, mBitnotI64, mUnaryPlusF64, mUnaryMinusF64, mAbsF64, mZe8ToI, mZe8ToI64, mZe16ToI, mZe16ToI64, mZe32ToI64, mZeIToI64, mToU8, mToU16, mToU32, mToFloat, mToBiggestFloat, mToInt, mToBiggestInt, mCharToStr, mBoolToStr, @@ -589,11 +591,12 @@ type const ctfeWhitelist* = {mNone, mUnaryLt, mSucc, mPred, mInc, mDec, mOrd, mLengthOpenArray, - mLengthStr, mLengthArray, mLengthSeq, mIncl, mExcl, mCard, mChr, + mLengthStr, mLengthArray, mLengthSeq, mXLenStr, mXLenSeq, + mIncl, mExcl, mCard, mChr, mAddI, mSubI, mMulI, mDivI, mModI, mAddI64, mSubI64, mMulI64, mDivI64, mModI64, mAddF64, mSubF64, mMulF64, mDivF64, mShrI, mShlI, mBitandI, mBitorI, mBitxorI, mMinI, mMaxI, - mShrI64, mShlI64, mBitandI64, mBitorI64, mBitxorI64, mMinI64, mMaxI64, + mShrI64, mShlI64, mBitandI64, mBitorI64, mBitxorI64, mMinF64, mMaxF64, mAddU, mSubU, mMulU, mDivU, mModU, mEqI, mLeI, mLtI, @@ -602,7 +605,7 @@ const mEqEnum, mLeEnum, mLtEnum, mEqCh, mLeCh, mLtCh, mEqB, mLeB, mLtB, mEqRef, mEqProc, mEqUntracedRef, mLePtr, mLtPtr, mEqCString, mXor, mUnaryMinusI, mUnaryMinusI64, mAbsI, mAbsI64, mNot, - mUnaryPlusI, mBitnotI, mUnaryPlusI64, + mUnaryPlusI, mBitnotI, mBitnotI64, mUnaryPlusF64, mUnaryMinusF64, mAbsF64, mZe8ToI, mZe8ToI64, mZe16ToI, mZe16ToI64, mZe32ToI64, mZeIToI64, mToU8, mToU16, mToU32, mToFloat, mToBiggestFloat, mToInt, mToBiggestInt, mCharToStr, mBoolToStr, @@ -685,8 +688,8 @@ type s*: TStorageLoc flags*: TLocFlags # location's flags t*: PType # type of location - r*: PRope # rope value of location (code generators) - heapRoot*: PRope # keeps track of the enclosing heap object that + r*: Rope # rope value of location (code generators) + heapRoot*: Rope # keeps track of the enclosing heap object that # owns this location (required by GC algorithms # employing heap snapshots or sliding views) @@ -698,7 +701,7 @@ type kind*: TLibKind generated*: bool # needed for the backends: isOverriden*: bool - name*: PRope + name*: Rope path*: PNode # can be a string literal! TInstantiation* = object @@ -728,7 +731,8 @@ type typScope*: PScope of routineKinds: procInstCache*: seq[PInstantiation] - scope*: PScope # the scope where the proc was defined + gcUnsafetyReason*: PSym # for better error messages wrt gcsafe + #scope*: PScope # the scope where the proc was defined of skModule: # modules keep track of the generic symbols they use from other modules. # this is because in incremental compilation, when a module is about to @@ -794,8 +798,8 @@ type # for enum types a list of symbols # for tyInt it can be the int literal # for procs and tyGenericBody, it's the - # the body of the user-defined type class # formal param list + # for concepts, the concept body # else: unused owner*: PSym # the 'owner' of the type sym*: PSym # types have the sym associated with them @@ -804,6 +808,7 @@ type # mean that there is no destructor. # see instantiateDestructor in semdestruct.nim deepCopy*: PSym # overriden 'deepCopy' operation + assignment*: PSym # overriden '=' operator size*: BiggestInt # the size of the type in bytes # -1 means that the size is unkwown align*: int16 # the type's alignment requirements @@ -1168,7 +1173,9 @@ proc newType*(kind: TTypeKind, owner: PSym): PType = result.lockLevel = UnspecifiedLockLevel when debugIds: registerId(result) - #if result.id < 2000: + #if result.id == 92231: + # echo "KNID ", kind + # writeStackTrace() # messageOut(typeKindToStr[kind] & ' has id: ' & toString(result.id)) proc mergeLoc(a: var TLoc, b: TLoc) = @@ -1218,6 +1225,7 @@ proc assignType*(dest, src: PType) = dest.align = src.align dest.destructor = src.destructor dest.deepCopy = src.deepCopy + dest.assignment = src.assignment dest.lockLevel = src.lockLevel # this fixes 'type TLock = TSysLock': if src.sym != nil: @@ -1314,6 +1322,13 @@ proc skipTypes*(t: PType, kinds: TTypeKinds): PType = result = t while result.kind in kinds: result = lastSon(result) +proc skipTypesOrNil*(t: PType, kinds: TTypeKinds): PType = + ## same as skipTypes but handles 'nil' + result = t + while result != nil and result.kind in kinds: + if result.len == 0: return nil + result = lastSon(result) + proc isGCedMem*(t: PType): bool {.inline.} = result = t.kind in {tyString, tyRef, tySequence} or t.kind == tyProc and t.callConv == ccClosure @@ -1334,6 +1349,13 @@ proc propagateToOwner*(owner, elem: PType) = if elem.isMetaType: 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, tyGenericInvocation}: let elemB = elem.skipTypes({tyGenericInst}) diff --git a/compiler/astalgo.nim b/compiler/astalgo.nim index c53e53b88..1707718d7 100644 --- a/compiler/astalgo.nim +++ b/compiler/astalgo.nim @@ -11,18 +11,18 @@ # and sets of nodes are supported. Efficiency is important as # the data structures here are used in various places of the compiler. -import +import ast, hashes, intsets, strutils, options, msgs, ropes, idents, rodutils proc hashNode*(p: RootRef): THash -proc treeToYaml*(n: PNode, indent: int = 0, maxRecDepth: int = - 1): PRope +proc treeToYaml*(n: PNode, indent: int = 0, maxRecDepth: int = - 1): Rope # Convert a tree into its YAML representation; this is used by the # YAML code generator and it is invaluable for debugging purposes. # If maxRecDepht <> -1 then it won't print the whole graph. -proc typeToYaml*(n: PType, indent: int = 0, maxRecDepth: int = - 1): PRope -proc symToYaml*(n: PSym, indent: int = 0, maxRecDepth: int = - 1): PRope -proc lineInfoToStr*(info: TLineInfo): PRope - +proc typeToYaml*(n: PType, indent: int = 0, maxRecDepth: int = - 1): Rope +proc symToYaml*(n: PSym, indent: int = 0, maxRecDepth: int = - 1): Rope +proc lineInfoToStr*(info: TLineInfo): Rope + # ----------------------- node sets: --------------------------------------- proc objectSetContains*(t: TObjectSet, obj: RootRef): bool # returns true whether n is in t @@ -34,10 +34,10 @@ proc objectSetContainsOrIncl*(t: var TObjectSet, obj: RootRef): bool # ----------------------- (key, val)-Hashtables ---------------------------- proc tablePut*(t: var TTable, key, val: RootRef) proc tableGet*(t: TTable, key: RootRef): RootRef -type +type TCmpProc* = proc (key, closure: RootRef): bool {.nimcall.} # true if found -proc tableSearch*(t: TTable, key, closure: RootRef, +proc tableSearch*(t: TTable, key, closure: RootRef, comparator: TCmpProc): RootRef # return val as soon as comparator returns true; if this never happens, # nil is returned @@ -45,16 +45,16 @@ proc tableSearch*(t: TTable, key, closure: RootRef, # ----------------------- str table ----------------------------------------- proc strTableContains*(t: TStrTable, n: PSym): bool proc strTableAdd*(t: var TStrTable, n: PSym) -proc strTableGet*(t: TStrTable, name: PIdent): PSym - -type +proc strTableGet*(t: TStrTable, name: PIdent): PSym + +type TTabIter*{.final.} = object # consider all fields here private h*: THash # current hash proc initTabIter*(ti: var TTabIter, tab: TStrTable): PSym proc nextIter*(ti: var TTabIter, tab: TStrTable): PSym # usage: - # var + # var # i: TTabIter # s: PSym # s = InitTabIter(i, table) @@ -63,7 +63,7 @@ proc nextIter*(ti: var TTabIter, tab: TStrTable): PSym # s = NextIter(i, table) # -type +type TIdentIter*{.final.} = object # iterator over all syms with same identifier h*: THash # current hash name*: PIdent @@ -97,11 +97,11 @@ proc mustRehash*(length, counter: int): bool proc nextTry*(h, maxHash: THash): THash {.inline.} # ------------- table[int, int] --------------------------------------------- -const +const InvalidKey* = low(int) -type - TIIPair*{.final.} = object +type + TIIPair*{.final.} = object key*, val*: int TIIPairSeq* = seq[TIIPair] @@ -127,21 +127,21 @@ proc skipConvAndClosure*(n: PNode): PNode = result = result.sons[1] else: break -proc sameValue*(a, b: PNode): bool = +proc sameValue*(a, b: PNode): bool = result = false case a.kind - of nkCharLit..nkUInt64Lit: + of nkCharLit..nkUInt64Lit: if b.kind in {nkCharLit..nkUInt64Lit}: result = a.intVal == b.intVal - of nkFloatLit..nkFloat64Lit: + of nkFloatLit..nkFloat64Lit: if b.kind in {nkFloatLit..nkFloat64Lit}: result = a.floatVal == b.floatVal - of nkStrLit..nkTripleStrLit: + of nkStrLit..nkTripleStrLit: if b.kind in {nkStrLit..nkTripleStrLit}: result = a.strVal == b.strVal else: # don't raise an internal error for 'nimrod check': #InternalError(a.info, "SameValue") discard -proc leValue*(a, b: PNode): bool = +proc leValue*(a, b: PNode): bool = # a <= b? result = false case a.kind @@ -162,289 +162,289 @@ proc weakLeValue*(a, b: PNode): TImplication = else: result = if leValue(a, b): impYes else: impNo -proc lookupInRecord(n: PNode, field: PIdent): PSym = +proc lookupInRecord(n: PNode, field: PIdent): PSym = result = nil case n.kind - of nkRecList: - for i in countup(0, sonsLen(n) - 1): + of nkRecList: + for i in countup(0, sonsLen(n) - 1): result = lookupInRecord(n.sons[i], field) - if result != nil: return - of nkRecCase: + if result != nil: return + of nkRecCase: if (n.sons[0].kind != nkSym): internalError(n.info, "lookupInRecord") result = lookupInRecord(n.sons[0], field) - if result != nil: return - for i in countup(1, sonsLen(n) - 1): + if result != nil: return + for i in countup(1, sonsLen(n) - 1): case n.sons[i].kind - of nkOfBranch, nkElse: + of nkOfBranch, nkElse: result = lookupInRecord(lastSon(n.sons[i]), field) - if result != nil: return + if result != nil: return else: internalError(n.info, "lookupInRecord(record case branch)") - of nkSym: + of nkSym: if n.sym.name.id == field.id: result = n.sym else: internalError(n.info, "lookupInRecord()") - -proc getModule(s: PSym): PSym = + +proc getModule(s: PSym): PSym = result = s assert((result.kind == skModule) or (result.owner != result)) while result != nil and result.kind != skModule: result = result.owner - -proc getSymFromList(list: PNode, ident: PIdent, start: int = 0): PSym = - for i in countup(start, sonsLen(list) - 1): + +proc getSymFromList(list: PNode, ident: PIdent, start: int = 0): PSym = + for i in countup(start, sonsLen(list) - 1): if list.sons[i].kind == nkSym: result = list.sons[i].sym - if result.name.id == ident.id: return + if result.name.id == ident.id: return else: internalError(list.info, "getSymFromList") result = nil -proc hashNode(p: RootRef): THash = +proc hashNode(p: RootRef): THash = result = hash(cast[pointer](p)) -proc mustRehash(length, counter: int): bool = +proc mustRehash(length, counter: int): bool = assert(length > counter) result = (length * 2 < counter * 3) or (length - counter < 4) -proc rspaces(x: int): PRope = +proc rspaces(x: int): Rope = # returns x spaces - result = toRope(spaces(x)) + result = rope(spaces(x)) -proc toYamlChar(c: char): string = +proc toYamlChar(c: char): string = case c of '\0'..'\x1F', '\x80'..'\xFF': result = "\\u" & strutils.toHex(ord(c), 4) of '\'', '\"', '\\': result = '\\' & c else: result = $c - -proc makeYamlString*(s: string): PRope = + +proc makeYamlString*(s: string): Rope = # We have to split long strings into many ropes. Otherwise # this could trigger InternalError(111). See the ropes module for # further information. const MaxLineLength = 64 result = nil var res = "\"" - for i in countup(0, if s.isNil: -1 else: (len(s)-1)): - if (i + 1) mod MaxLineLength == 0: + for i in countup(0, if s.isNil: -1 else: (len(s)-1)): + if (i + 1) mod MaxLineLength == 0: add(res, '\"') add(res, "\n") - app(result, toRope(res)) + add(result, rope(res)) res = "\"" # reset add(res, toYamlChar(s[i])) add(res, '\"') - app(result, toRope(res)) + add(result, rope(res)) -proc flagsToStr[T](flags: set[T]): PRope = - if flags == {}: - result = toRope("[]") - else: +proc flagsToStr[T](flags: set[T]): Rope = + if flags == {}: + result = rope("[]") + else: result = nil - for x in items(flags): - if result != nil: app(result, ", ") - app(result, makeYamlString($x)) - result = con("[", con(result, "]")) - -proc lineInfoToStr(info: TLineInfo): PRope = - result = ropef("[$1, $2, $3]", [makeYamlString(toFilename(info)), - toRope(toLinenumber(info)), - toRope(toColumn(info))]) - -proc treeToYamlAux(n: PNode, marker: var IntSet, - indent, maxRecDepth: int): PRope -proc symToYamlAux(n: PSym, marker: var IntSet, - indent, maxRecDepth: int): PRope -proc typeToYamlAux(n: PType, marker: var IntSet, - indent, maxRecDepth: int): PRope -proc strTableToYaml(n: TStrTable, marker: var IntSet, indent: int, - maxRecDepth: int): PRope = + for x in items(flags): + if result != nil: add(result, ", ") + add(result, makeYamlString($x)) + result = "[" & result & "]" + +proc lineInfoToStr(info: TLineInfo): Rope = + result = "[$1, $2, $3]" % [makeYamlString(toFilename(info)), + rope(toLinenumber(info)), + rope(toColumn(info))] + +proc treeToYamlAux(n: PNode, marker: var IntSet, + indent, maxRecDepth: int): Rope +proc symToYamlAux(n: PSym, marker: var IntSet, + indent, maxRecDepth: int): Rope +proc typeToYamlAux(n: PType, marker: var IntSet, + indent, maxRecDepth: int): Rope +proc strTableToYaml(n: TStrTable, marker: var IntSet, indent: int, + maxRecDepth: int): Rope = var istr = rspaces(indent + 2) - result = toRope("[") + result = rope("[") var mycount = 0 - for i in countup(0, high(n.data)): - if n.data[i] != nil: - if mycount > 0: app(result, ",") - appf(result, "$N$1$2", + for i in countup(0, high(n.data)): + if n.data[i] != nil: + if mycount > 0: add(result, ",") + addf(result, "$N$1$2", [istr, symToYamlAux(n.data[i], marker, indent + 2, maxRecDepth - 1)]) inc(mycount) - if mycount > 0: appf(result, "$N$1", [rspaces(indent)]) - app(result, "]") + if mycount > 0: addf(result, "$N$1", [rspaces(indent)]) + add(result, "]") assert(mycount == n.counter) -proc ropeConstr(indent: int, c: openArray[PRope]): PRope = +proc ropeConstr(indent: int, c: openArray[Rope]): Rope = # array of (name, value) pairs var istr = rspaces(indent + 2) - result = toRope("{") + result = rope("{") var i = 0 - while i <= high(c): - if i > 0: app(result, ",") - appf(result, "$N$1\"$2\": $3", [istr, c[i], c[i + 1]]) + while i <= high(c): + if i > 0: add(result, ",") + addf(result, "$N$1\"$2\": $3", [istr, c[i], c[i + 1]]) inc(i, 2) - appf(result, "$N$1}", [rspaces(indent)]) - -proc symToYamlAux(n: PSym, marker: var IntSet, indent: int, - maxRecDepth: int): PRope = - if n == nil: - result = toRope("null") - elif containsOrIncl(marker, n.id): - result = ropef("\"$1 @$2\"", [toRope(n.name.s), toRope( - strutils.toHex(cast[ByteAddress](n), sizeof(n) * 2))]) - else: + addf(result, "$N$1}", [rspaces(indent)]) + +proc symToYamlAux(n: PSym, marker: var IntSet, indent: int, + maxRecDepth: int): Rope = + if n == nil: + result = rope("null") + elif containsOrIncl(marker, n.id): + result = "\"$1 @$2\"" % [rope(n.name.s), rope( + strutils.toHex(cast[ByteAddress](n), sizeof(n) * 2))] + else: var ast = treeToYamlAux(n.ast, marker, indent + 2, maxRecDepth - 1) - result = ropeConstr(indent, [toRope("kind"), - makeYamlString($n.kind), - toRope("name"), makeYamlString(n.name.s), - toRope("typ"), typeToYamlAux(n.typ, marker, - indent + 2, maxRecDepth - 1), - toRope("info"), lineInfoToStr(n.info), - toRope("flags"), flagsToStr(n.flags), - toRope("magic"), makeYamlString($n.magic), - toRope("ast"), ast, toRope("options"), - flagsToStr(n.options), toRope("position"), - toRope(n.position)]) - -proc typeToYamlAux(n: PType, marker: var IntSet, indent: int, - maxRecDepth: int): PRope = - if n == nil: - result = toRope("null") - elif containsOrIncl(marker, n.id): - result = ropef("\"$1 @$2\"", [toRope($n.kind), toRope( - strutils.toHex(cast[ByteAddress](n), sizeof(n) * 2))]) - else: - if sonsLen(n) > 0: - result = toRope("[") - for i in countup(0, sonsLen(n) - 1): - if i > 0: app(result, ",") - appf(result, "$N$1$2", [rspaces(indent + 4), typeToYamlAux(n.sons[i], + result = ropeConstr(indent, [rope("kind"), + makeYamlString($n.kind), + rope("name"), makeYamlString(n.name.s), + rope("typ"), typeToYamlAux(n.typ, marker, + indent + 2, maxRecDepth - 1), + rope("info"), lineInfoToStr(n.info), + rope("flags"), flagsToStr(n.flags), + rope("magic"), makeYamlString($n.magic), + rope("ast"), ast, rope("options"), + flagsToStr(n.options), rope("position"), + rope(n.position)]) + +proc typeToYamlAux(n: PType, marker: var IntSet, indent: int, + maxRecDepth: int): Rope = + if n == nil: + result = rope("null") + elif containsOrIncl(marker, n.id): + result = "\"$1 @$2\"" % [rope($n.kind), rope( + strutils.toHex(cast[ByteAddress](n), sizeof(n) * 2))] + else: + if sonsLen(n) > 0: + result = rope("[") + for i in countup(0, sonsLen(n) - 1): + if i > 0: add(result, ",") + addf(result, "$N$1$2", [rspaces(indent + 4), typeToYamlAux(n.sons[i], marker, indent + 4, maxRecDepth - 1)]) - appf(result, "$N$1]", [rspaces(indent + 2)]) - else: - result = toRope("null") - result = ropeConstr(indent, [toRope("kind"), - makeYamlString($n.kind), - toRope("sym"), symToYamlAux(n.sym, marker, - indent + 2, maxRecDepth - 1), toRope("n"), treeToYamlAux(n.n, marker, - indent + 2, maxRecDepth - 1), toRope("flags"), flagsToStr(n.flags), - toRope("callconv"), - makeYamlString(CallingConvToStr[n.callConv]), - toRope("size"), toRope(n.size), - toRope("align"), toRope(n.align), - toRope("sons"), result]) - -proc treeToYamlAux(n: PNode, marker: var IntSet, indent: int, - maxRecDepth: int): PRope = - if n == nil: - result = toRope("null") - else: + addf(result, "$N$1]", [rspaces(indent + 2)]) + else: + result = rope("null") + result = ropeConstr(indent, [rope("kind"), + makeYamlString($n.kind), + rope("sym"), symToYamlAux(n.sym, marker, + indent + 2, maxRecDepth - 1), rope("n"), treeToYamlAux(n.n, marker, + indent + 2, maxRecDepth - 1), rope("flags"), flagsToStr(n.flags), + rope("callconv"), + makeYamlString(CallingConvToStr[n.callConv]), + rope("size"), rope(n.size), + rope("align"), rope(n.align), + rope("sons"), result]) + +proc treeToYamlAux(n: PNode, marker: var IntSet, indent: int, + maxRecDepth: int): Rope = + if n == nil: + result = rope("null") + else: var istr = rspaces(indent + 2) - result = ropef("{$N$1\"kind\": $2", [istr, makeYamlString($n.kind)]) - if maxRecDepth != 0: - appf(result, ",$N$1\"info\": $2", [istr, lineInfoToStr(n.info)]) + result = "{$N$1\"kind\": $2" % [istr, makeYamlString($n.kind)] + if maxRecDepth != 0: + addf(result, ",$N$1\"info\": $2", [istr, lineInfoToStr(n.info)]) case n.kind - of nkCharLit..nkInt64Lit: - appf(result, ",$N$1\"intVal\": $2", [istr, toRope(n.intVal)]) - of nkFloatLit, nkFloat32Lit, nkFloat64Lit: - appf(result, ",$N$1\"floatVal\": $2", - [istr, toRope(n.floatVal.toStrMaxPrecision)]) - of nkStrLit..nkTripleStrLit: + of nkCharLit..nkInt64Lit: + addf(result, ",$N$1\"intVal\": $2", [istr, rope(n.intVal)]) + of nkFloatLit, nkFloat32Lit, nkFloat64Lit: + addf(result, ",$N$1\"floatVal\": $2", + [istr, rope(n.floatVal.toStrMaxPrecision)]) + of nkStrLit..nkTripleStrLit: if n.strVal.isNil: - appf(result, ",$N$1\"strVal\": null", [istr]) + addf(result, ",$N$1\"strVal\": null", [istr]) else: - appf(result, ",$N$1\"strVal\": $2", [istr, makeYamlString(n.strVal)]) - of nkSym: - appf(result, ",$N$1\"sym\": $2", + addf(result, ",$N$1\"strVal\": $2", [istr, makeYamlString(n.strVal)]) + of nkSym: + addf(result, ",$N$1\"sym\": $2", [istr, symToYamlAux(n.sym, marker, indent + 2, maxRecDepth)]) - of nkIdent: - if n.ident != nil: - appf(result, ",$N$1\"ident\": $2", [istr, makeYamlString(n.ident.s)]) - else: - appf(result, ",$N$1\"ident\": null", [istr]) - else: - if sonsLen(n) > 0: - appf(result, ",$N$1\"sons\": [", [istr]) - for i in countup(0, sonsLen(n) - 1): - if i > 0: app(result, ",") - appf(result, "$N$1$2", [rspaces(indent + 4), treeToYamlAux(n.sons[i], + of nkIdent: + if n.ident != nil: + addf(result, ",$N$1\"ident\": $2", [istr, makeYamlString(n.ident.s)]) + else: + addf(result, ",$N$1\"ident\": null", [istr]) + else: + if sonsLen(n) > 0: + addf(result, ",$N$1\"sons\": [", [istr]) + for i in countup(0, sonsLen(n) - 1): + if i > 0: add(result, ",") + addf(result, "$N$1$2", [rspaces(indent + 4), treeToYamlAux(n.sons[i], marker, indent + 4, maxRecDepth - 1)]) - appf(result, "$N$1]", [istr]) - appf(result, ",$N$1\"typ\": $2", + addf(result, "$N$1]", [istr]) + addf(result, ",$N$1\"typ\": $2", [istr, typeToYamlAux(n.typ, marker, indent + 2, maxRecDepth)]) - appf(result, "$N$1}", [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() result = treeToYamlAux(n, marker, indent, maxRecDepth) -proc typeToYaml(n: PType, indent: int = 0, maxRecDepth: int = - 1): PRope = +proc typeToYaml(n: PType, indent: int = 0, maxRecDepth: int = - 1): Rope = var marker = initIntSet() result = typeToYamlAux(n, marker, indent, maxRecDepth) -proc symToYaml(n: PSym, indent: int = 0, maxRecDepth: int = - 1): PRope = +proc symToYaml(n: PSym, indent: int = 0, maxRecDepth: int = - 1): Rope = var marker = initIntSet() result = symToYamlAux(n, marker, indent, maxRecDepth) -proc debugTree(n: PNode, indent: int, maxRecDepth: int; renderType=false): PRope -proc debugType(n: PType, maxRecDepth=100): PRope = - if n == nil: - result = toRope("null") +proc debugTree*(n: PNode, indent: int, maxRecDepth: int; renderType=false): Rope +proc debugType(n: PType, maxRecDepth=100): Rope = + if n == nil: + result = rope("null") else: - result = toRope($n.kind) - if n.sym != nil: - app(result, " ") - app(result, n.sym.name.s) + result = rope($n.kind) + if n.sym != nil: + add(result, " ") + add(result, n.sym.name.s) if n.kind in IntegralTypes and n.n != nil: - app(result, ", node: ") - app(result, debugTree(n.n, 2, maxRecDepth-1, renderType=true)) + add(result, ", node: ") + add(result, debugTree(n.n, 2, maxRecDepth-1, renderType=true)) if (n.kind != tyString) and (sonsLen(n) > 0) and maxRecDepth != 0: - app(result, "(") + add(result, "(") for i in countup(0, sonsLen(n) - 1): - if i > 0: app(result, ", ") + if i > 0: add(result, ", ") if n.sons[i] == nil: - app(result, "null") + add(result, "null") else: - app(result, debugType(n.sons[i], maxRecDepth-1)) + add(result, debugType(n.sons[i], maxRecDepth-1)) if n.kind == tyObject and n.n != nil: - app(result, ", node: ") - app(result, debugTree(n.n, 2, maxRecDepth-1, renderType=true)) - app(result, ")") + add(result, ", node: ") + add(result, debugTree(n.n, 2, maxRecDepth-1, renderType=true)) + add(result, ")") proc debugTree(n: PNode, indent: int, maxRecDepth: int; - renderType=false): PRope = - if n == nil: - result = toRope("null") - else: + renderType=false): Rope = + if n == nil: + result = rope("null") + else: var istr = rspaces(indent + 2) - result = ropef("{$N$1\"kind\": $2", - [istr, makeYamlString($n.kind)]) - if maxRecDepth != 0: + result = "{$N$1\"kind\": $2" % + [istr, makeYamlString($n.kind)] + if maxRecDepth != 0: case n.kind of nkCharLit..nkUInt64Lit: - appf(result, ",$N$1\"intVal\": $2", [istr, toRope(n.intVal)]) - of nkFloatLit, nkFloat32Lit, nkFloat64Lit: - appf(result, ",$N$1\"floatVal\": $2", - [istr, toRope(n.floatVal.toStrMaxPrecision)]) - of nkStrLit..nkTripleStrLit: + addf(result, ",$N$1\"intVal\": $2", [istr, rope(n.intVal)]) + of nkFloatLit, nkFloat32Lit, nkFloat64Lit: + addf(result, ",$N$1\"floatVal\": $2", + [istr, rope(n.floatVal.toStrMaxPrecision)]) + of nkStrLit..nkTripleStrLit: if n.strVal.isNil: - appf(result, ",$N$1\"strVal\": null", [istr]) + addf(result, ",$N$1\"strVal\": null", [istr]) else: - appf(result, ",$N$1\"strVal\": $2", [istr, makeYamlString(n.strVal)]) - of nkSym: - appf(result, ",$N$1\"sym\": $2_$3", - [istr, toRope(n.sym.name.s), toRope(n.sym.id)]) - # [istr, symToYaml(n.sym, indent, maxRecDepth), - # toRope(n.sym.id)]) + addf(result, ",$N$1\"strVal\": $2", [istr, makeYamlString(n.strVal)]) + of nkSym: + addf(result, ",$N$1\"sym\": $2_$3", + [istr, rope(n.sym.name.s), rope(n.sym.id)]) + # [istr, symToYaml(n.sym, indent, maxRecDepth), + # rope(n.sym.id)]) if renderType and n.sym.typ != nil: - appf(result, ",$N$1\"typ\": $2", [istr, debugType(n.sym.typ, 2)]) - of nkIdent: - if n.ident != nil: - appf(result, ",$N$1\"ident\": $2", [istr, makeYamlString(n.ident.s)]) - else: - appf(result, ",$N$1\"ident\": null", [istr]) - else: - if sonsLen(n) > 0: - appf(result, ",$N$1\"sons\": [", [istr]) - for i in countup(0, sonsLen(n) - 1): - if i > 0: app(result, ",") - appf(result, "$N$1$2", [rspaces(indent + 4), debugTree(n.sons[i], + addf(result, ",$N$1\"typ\": $2", [istr, debugType(n.sym.typ, 2)]) + of nkIdent: + if n.ident != nil: + addf(result, ",$N$1\"ident\": $2", [istr, makeYamlString(n.ident.s)]) + else: + addf(result, ",$N$1\"ident\": null", [istr]) + else: + if sonsLen(n) > 0: + addf(result, ",$N$1\"sons\": [", [istr]) + for i in countup(0, sonsLen(n) - 1): + if i > 0: add(result, ",") + addf(result, "$N$1$2", [rspaces(indent + 4), debugTree(n.sons[i], indent + 4, maxRecDepth - 1, renderType)]) - appf(result, "$N$1]", [istr]) - appf(result, ",$N$1\"info\": $2", [istr, lineInfoToStr(n.info)]) - appf(result, "$N$1}", [rspaces(indent)]) + addf(result, "$N$1]", [istr]) + addf(result, ",$N$1\"info\": $2", [istr, lineInfoToStr(n.info)]) + addf(result, "$N$1}", [rspaces(indent)]) proc debug(n: PSym) = if n == nil: @@ -452,27 +452,26 @@ proc debug(n: PSym) = elif n.kind == skUnknown: msgWriteln("skUnknown") else: - #writeln(stdout, ropeToStr(symToYaml(n, 0, 1))) + #writeln(stdout, $symToYaml(n, 0, 1)) msgWriteln("$1_$2: $3, $4, $5, $6" % [ - n.name.s, $n.id, flagsToStr(n.flags).ropeToStr, - flagsToStr(n.loc.flags).ropeToStr, lineInfoToStr(n.info).ropeToStr, - $n.kind]) + n.name.s, $n.id, $flagsToStr(n.flags), $flagsToStr(n.loc.flags), + $lineInfoToStr(n.info), $n.kind]) -proc debug(n: PType) = - msgWriteln(ropeToStr(debugType(n))) +proc debug(n: PType) = + msgWriteln($debugType(n)) -proc debug(n: PNode) = - msgWriteln(ropeToStr(debugTree(n, 0, 100))) +proc debug(n: PNode) = + msgWriteln($debugTree(n, 0, 100)) -const +const EmptySeq = @[] -proc nextTry(h, maxHash: THash): THash = - result = ((5 * h) + 1) and maxHash +proc nextTry(h, maxHash: THash): THash = + result = ((5 * h) + 1) and maxHash # For any initial h in range(maxHash), repeating that maxHash times # generates each int in range(maxHash) exactly once (see any text on # random-number generation for proof). - + proc objectSetContains(t: TObjectSet, obj: RootRef): bool = # returns true whether n is in t var h: THash = hashNode(obj) and high(t.data) # start with real hash value @@ -490,94 +489,94 @@ proc objectSetRawInsert(data: var TObjectSeq, obj: RootRef) = assert(data[h] == nil) data[h] = obj -proc objectSetEnlarge(t: var TObjectSet) = +proc objectSetEnlarge(t: var TObjectSet) = var n: TObjectSeq newSeq(n, len(t.data) * GrowthFactor) for i in countup(0, high(t.data)): if t.data[i] != nil: objectSetRawInsert(n, t.data[i]) swap(t.data, n) -proc objectSetIncl(t: var TObjectSet, obj: RootRef) = +proc objectSetIncl(t: var TObjectSet, obj: RootRef) = if mustRehash(len(t.data), t.counter): objectSetEnlarge(t) objectSetRawInsert(t.data, obj) inc(t.counter) -proc objectSetContainsOrIncl(t: var TObjectSet, obj: RootRef): bool = +proc objectSetContainsOrIncl(t: var TObjectSet, obj: RootRef): bool = # returns true if obj is already in the string table: var h: THash = hashNode(obj) and high(t.data) - while true: + while true: var it = t.data[h] - if it == nil: break - if it == obj: + if it == nil: break + if it == obj: return true # found it h = nextTry(h, high(t.data)) - if mustRehash(len(t.data), t.counter): + if mustRehash(len(t.data), t.counter): objectSetEnlarge(t) objectSetRawInsert(t.data, obj) - else: + else: assert(t.data[h] == nil) t.data[h] = obj inc(t.counter) result = false -proc tableRawGet(t: TTable, key: RootRef): int = +proc tableRawGet(t: TTable, key: RootRef): int = var h: THash = hashNode(key) and high(t.data) # start with real hash value - while t.data[h].key != nil: - if t.data[h].key == key: + while t.data[h].key != nil: + if t.data[h].key == key: return h h = nextTry(h, high(t.data)) result = -1 -proc tableSearch(t: TTable, key, closure: RootRef, - comparator: TCmpProc): RootRef = +proc tableSearch(t: TTable, key, closure: RootRef, + comparator: TCmpProc): RootRef = var h: THash = hashNode(key) and high(t.data) # start with real hash value - while t.data[h].key != nil: - if t.data[h].key == key: - if comparator(t.data[h].val, closure): + while t.data[h].key != nil: + if t.data[h].key == key: + if comparator(t.data[h].val, closure): # BUGFIX 1 return t.data[h].val h = nextTry(h, high(t.data)) result = nil -proc tableGet(t: TTable, key: RootRef): RootRef = +proc tableGet(t: TTable, key: RootRef): RootRef = var index = tableRawGet(t, key) if index >= 0: result = t.data[index].val else: result = nil - -proc tableRawInsert(data: var TPairSeq, key, val: RootRef) = + +proc tableRawInsert(data: var TPairSeq, key, val: RootRef) = var h: THash = hashNode(key) and high(data) - while data[h].key != nil: + while data[h].key != nil: assert(data[h].key != key) h = nextTry(h, high(data)) assert(data[h].key == nil) data[h].key = key data[h].val = val -proc tableEnlarge(t: var TTable) = +proc tableEnlarge(t: var TTable) = var n: TPairSeq newSeq(n, len(t.data) * GrowthFactor) - for i in countup(0, high(t.data)): + for i in countup(0, high(t.data)): if t.data[i].key != nil: tableRawInsert(n, t.data[i].key, t.data[i].val) swap(t.data, n) -proc tablePut(t: var TTable, key, val: RootRef) = +proc tablePut(t: var TTable, key, val: RootRef) = var index = tableRawGet(t, key) - if index >= 0: + if index >= 0: t.data[index].val = val - else: + else: if mustRehash(len(t.data), t.counter): tableEnlarge(t) tableRawInsert(t.data, key, val) inc(t.counter) -proc strTableContains(t: TStrTable, n: PSym): bool = +proc strTableContains(t: TStrTable, n: PSym): bool = var h: THash = n.name.h and high(t.data) # start with real hash value - while t.data[h] != nil: - if (t.data[h] == n): + while t.data[h] != nil: + if (t.data[h] == n): return true h = nextTry(h, high(t.data)) result = false -proc strTableRawInsert(data: var TSymSeq, n: PSym) = +proc strTableRawInsert(data: var TSymSeq, n: PSym) = var h: THash = n.name.h and high(data) if sfImmediate notin n.flags: # fast path: @@ -614,18 +613,18 @@ proc symTabReplaceRaw(data: var TSymSeq, prevSym: PSym, newSym: PSym) = return h = nextTry(h, high(data)) assert false - + proc symTabReplace*(t: var TStrTable, prevSym: PSym, newSym: PSym) = symTabReplaceRaw(t.data, prevSym, newSym) -proc strTableEnlarge(t: var TStrTable) = +proc strTableEnlarge(t: var TStrTable) = var n: TSymSeq newSeq(n, len(t.data) * GrowthFactor) - for i in countup(0, high(t.data)): + for i in countup(0, high(t.data)): if t.data[i] != nil: strTableRawInsert(n, t.data[i]) swap(t.data, n) -proc strTableAdd(t: var TStrTable, n: PSym) = +proc strTableAdd(t: var TStrTable, n: PSym) = if mustRehash(len(t.data), t.counter): strTableEnlarge(t) strTableRawInsert(t.data, n) inc(t.counter) @@ -666,103 +665,103 @@ proc strTableIncl*(t: var TStrTable, n: PSym): bool {.discardable.} = inc(t.counter) result = false -proc strTableGet(t: TStrTable, name: PIdent): PSym = +proc strTableGet(t: TStrTable, name: PIdent): PSym = var h: THash = name.h and high(t.data) - while true: + while true: result = t.data[h] - if result == nil: break - if result.name.id == name.id: break + if result == nil: break + if result.name.id == name.id: break h = nextTry(h, high(t.data)) -proc initIdentIter(ti: var TIdentIter, tab: TStrTable, s: PIdent): PSym = +proc initIdentIter(ti: var TIdentIter, tab: TStrTable, s: PIdent): PSym = ti.h = s.h ti.name = s if tab.counter == 0: result = nil else: result = nextIdentIter(ti, tab) - -proc nextIdentIter(ti: var TIdentIter, tab: TStrTable): PSym = + +proc nextIdentIter(ti: var TIdentIter, tab: TStrTable): PSym = var h = ti.h and high(tab.data) var start = h result = tab.data[h] - while result != nil: - if result.name.id == ti.name.id: break + while result != nil: + if result.name.id == ti.name.id: break h = nextTry(h, high(tab.data)) - if h == start: + if h == start: result = nil - break + break result = tab.data[h] ti.h = nextTry(h, high(tab.data)) - -proc nextIdentExcluding*(ti: var TIdentIter, tab: TStrTable, + +proc nextIdentExcluding*(ti: var TIdentIter, tab: TStrTable, excluding: IntSet): PSym = var h: THash = ti.h and high(tab.data) var start = h result = tab.data[h] - while result != nil: - if result.name.id == ti.name.id and not contains(excluding, result.id): + while result != nil: + if result.name.id == ti.name.id and not contains(excluding, result.id): break h = nextTry(h, high(tab.data)) - if h == start: + if h == start: result = nil - break + break result = tab.data[h] ti.h = nextTry(h, high(tab.data)) if result != nil and contains(excluding, result.id): result = nil proc firstIdentExcluding*(ti: var TIdentIter, tab: TStrTable, s: PIdent, - excluding: IntSet): PSym = + excluding: IntSet): PSym = ti.h = s.h ti.name = s if tab.counter == 0: result = nil else: result = nextIdentExcluding(ti, tab, excluding) -proc initTabIter(ti: var TTabIter, tab: TStrTable): PSym = +proc initTabIter(ti: var TTabIter, tab: TStrTable): PSym = ti.h = 0 # we start by zero ... - if tab.counter == 0: + if tab.counter == 0: result = nil # FIX 1: removed endless loop - else: + else: result = nextIter(ti, tab) - -proc nextIter(ti: var TTabIter, tab: TStrTable): PSym = + +proc nextIter(ti: var TTabIter, tab: TStrTable): PSym = result = nil - while (ti.h <= high(tab.data)): + while (ti.h <= high(tab.data)): result = tab.data[ti.h] inc(ti.h) # ... and increment by one always - if result != nil: break + if result != nil: break -iterator items*(tab: TStrTable): PSym = +iterator items*(tab: TStrTable): PSym = var it: TTabIter var s = initTabIter(it, tab) - while s != nil: + while s != nil: yield s s = nextIter(it, tab) -proc hasEmptySlot(data: TIdPairSeq): bool = - for h in countup(0, high(data)): - if data[h].key == nil: +proc hasEmptySlot(data: TIdPairSeq): bool = + for h in countup(0, high(data)): + if data[h].key == nil: return true result = false -proc idTableRawGet(t: TIdTable, key: int): int = +proc idTableRawGet(t: TIdTable, key: int): int = var h: THash h = key and high(t.data) # start with real hash value - while t.data[h].key != nil: + while t.data[h].key != nil: if t.data[h].key.id == key: return h h = nextTry(h, high(t.data)) result = - 1 -proc idTableHasObjectAsKey(t: TIdTable, key: PIdObj): bool = +proc idTableHasObjectAsKey(t: TIdTable, key: PIdObj): bool = var index = idTableRawGet(t, key.id) if index >= 0: result = t.data[index].key == key else: result = false - -proc idTableGet(t: TIdTable, key: PIdObj): RootRef = + +proc idTableGet(t: TIdTable, key: PIdObj): RootRef = var index = idTableRawGet(t, key.id) if index >= 0: result = t.data[index].val else: result = nil - -proc idTableGet(t: TIdTable, key: int): RootRef = + +proc idTableGet(t: TIdTable, key: int): RootRef = var index = idTableRawGet(t, key) if index >= 0: result = t.data[index].val else: result = nil @@ -771,30 +770,30 @@ iterator pairs*(t: TIdTable): tuple[key: int, value: RootRef] = for i in 0..high(t.data): if t.data[i].key != nil: yield (t.data[i].key.id, t.data[i].val) - -proc idTableRawInsert(data: var TIdPairSeq, key: PIdObj, val: RootRef) = + +proc idTableRawInsert(data: var TIdPairSeq, key: PIdObj, val: RootRef) = var h: THash h = key.id and high(data) - while data[h].key != nil: + while data[h].key != nil: assert(data[h].key.id != key.id) h = nextTry(h, high(data)) assert(data[h].key == nil) data[h].key = key data[h].val = val -proc idTablePut(t: var TIdTable, key: PIdObj, val: RootRef) = - var +proc idTablePut(t: var TIdTable, key: PIdObj, val: RootRef) = + var index: int n: TIdPairSeq index = idTableRawGet(t, key.id) - if index >= 0: + if index >= 0: assert(t.data[index].key != nil) t.data[index].val = val - else: - if mustRehash(len(t.data), t.counter): + else: + if mustRehash(len(t.data), t.counter): newSeq(n, len(t.data) * GrowthFactor) - for i in countup(0, high(t.data)): - if t.data[i].key != nil: + for i in countup(0, high(t.data)): + if t.data[i].key != nil: idTableRawInsert(n, t.data[i].key, t.data[i].val) assert(hasEmptySlot(n)) swap(t.data, n) @@ -805,7 +804,7 @@ iterator idTablePairs*(t: TIdTable): tuple[key: PIdObj, val: RootRef] = for i in 0 .. high(t.data): if not isNil(t.data[i].key): yield (t.data[i].key, t.data[i].val) -proc idNodeTableRawGet(t: TIdNodeTable, key: PIdObj): int = +proc idNodeTableRawGet(t: TIdNodeTable, key: PIdObj): int = var h: THash h = key.id and high(t.data) # start with real hash value while t.data[h].key != nil: @@ -814,7 +813,7 @@ proc idNodeTableRawGet(t: TIdNodeTable, key: PIdObj): int = h = nextTry(h, high(t.data)) result = - 1 -proc idNodeTableGet(t: TIdNodeTable, key: PIdObj): PNode = +proc idNodeTableGet(t: TIdNodeTable, key: PIdObj): PNode = var index: int index = idNodeTableRawGet(t, key) if index >= 0: result = t.data[index].val @@ -823,28 +822,28 @@ proc idNodeTableGet(t: TIdNodeTable, key: PIdObj): PNode = proc idNodeTableGetLazy*(t: TIdNodeTable, key: PIdObj): PNode = if not isNil(t.data): result = idNodeTableGet(t, key) - -proc idNodeTableRawInsert(data: var TIdNodePairSeq, key: PIdObj, val: PNode) = + +proc idNodeTableRawInsert(data: var TIdNodePairSeq, key: PIdObj, val: PNode) = var h: THash h = key.id and high(data) - while data[h].key != nil: + while data[h].key != nil: assert(data[h].key.id != key.id) h = nextTry(h, high(data)) assert(data[h].key == nil) data[h].key = key data[h].val = val -proc idNodeTablePut(t: var TIdNodeTable, key: PIdObj, val: PNode) = +proc idNodeTablePut(t: var TIdNodeTable, key: PIdObj, val: PNode) = var index = idNodeTableRawGet(t, key) - if index >= 0: + if index >= 0: assert(t.data[index].key != nil) t.data[index].val = val - else: - if mustRehash(len(t.data), t.counter): + else: + if mustRehash(len(t.data), t.counter): var n: TIdNodePairSeq newSeq(n, len(t.data) * GrowthFactor) - for i in countup(0, high(t.data)): - if t.data[i].key != nil: + for i in countup(0, high(t.data)): + if t.data[i].key != nil: idNodeTableRawInsert(n, t.data[i].key, t.data[i].val) swap(t.data, n) idNodeTableRawInsert(t.data, key, val) @@ -858,46 +857,46 @@ iterator pairs*(t: TIdNodeTable): tuple[key: PIdObj, val: PNode] = for i in 0 .. high(t.data): if not isNil(t.data[i].key): yield (t.data[i].key, t.data[i].val) -proc initIITable(x: var TIITable) = +proc initIITable(x: var TIITable) = x.counter = 0 newSeq(x.data, StartSize) for i in countup(0, StartSize - 1): x.data[i].key = InvalidKey - -proc iiTableRawGet(t: TIITable, key: int): int = + +proc iiTableRawGet(t: TIITable, key: int): int = var h: THash h = key and high(t.data) # start with real hash value - while t.data[h].key != InvalidKey: + while t.data[h].key != InvalidKey: if t.data[h].key == key: return h h = nextTry(h, high(t.data)) result = -1 -proc iiTableGet(t: TIITable, key: int): int = +proc iiTableGet(t: TIITable, key: int): int = var index = iiTableRawGet(t, key) if index >= 0: result = t.data[index].val else: result = InvalidKey - -proc iiTableRawInsert(data: var TIIPairSeq, key, val: int) = + +proc iiTableRawInsert(data: var TIIPairSeq, key, val: int) = var h: THash h = key and high(data) - while data[h].key != InvalidKey: + while data[h].key != InvalidKey: assert(data[h].key != key) h = nextTry(h, high(data)) assert(data[h].key == InvalidKey) data[h].key = key data[h].val = val -proc iiTablePut(t: var TIITable, key, val: int) = +proc iiTablePut(t: var TIITable, key, val: int) = var index = iiTableRawGet(t, key) - if index >= 0: + if index >= 0: assert(t.data[index].key != InvalidKey) t.data[index].val = val - else: - if mustRehash(len(t.data), t.counter): + else: + if mustRehash(len(t.data), t.counter): var n: TIIPairSeq newSeq(n, len(t.data) * GrowthFactor) for i in countup(0, high(n)): n[i].key = InvalidKey - for i in countup(0, high(t.data)): - if t.data[i].key != InvalidKey: + for i in countup(0, high(t.data)): + if t.data[i].key != InvalidKey: iiTableRawInsert(n, t.data[i].key, t.data[i].val) swap(t.data, n) iiTableRawInsert(t.data, key, val) diff --git a/compiler/canonicalizer.nim b/compiler/canonicalizer.nim index 50d3fd017..6fcc57a91 100644 --- a/compiler/canonicalizer.nim +++ b/compiler/canonicalizer.nim @@ -243,24 +243,24 @@ proc encodeNode(w: PRodWriter, fInfo: TLineInfo, n: PNode, encodeNode(w, n.info, n.sons[i], result) add(result, ')') -proc encodeLoc(w: PRodWriter, loc: TLoc, result: var string) = +proc encodeLoc(w: PRodWriter, loc: TLoc, result: var string) = var oldLen = result.len result.add('<') if loc.k != low(loc.k): encodeVInt(ord(loc.k), result) - if loc.s != low(loc.s): + if loc.s != low(loc.s): add(result, '*') encodeVInt(ord(loc.s), result) - if loc.flags != {}: + if loc.flags != {}: add(result, '$') encodeVInt(cast[int32](loc.flags), result) if loc.t != nil: add(result, '^') encodeVInt(cast[int32](loc.t.id), result) pushType(w, loc.t) - if loc.r != nil: + if loc.r != nil: add(result, '!') - encodeStr(ropeToStr(loc.r), result) - if loc.a != 0: + encodeStr($loc.r, result) + if loc.a != 0: add(result, '?') encodeVInt(loc.a, result) if oldLen + 1 == result.len: @@ -317,7 +317,7 @@ proc encodeLib(w: PRodWriter, lib: PLib, info: TLineInfo, result: var string) = add(result, '|') encodeVInt(ord(lib.kind), result) add(result, '|') - encodeStr(ropeToStr(lib.name), result) + encodeStr($lib.name, result) add(result, '|') encodeNode(w, info, lib.path, result) diff --git a/compiler/ccgcalls.nim b/compiler/ccgcalls.nim index 5c4767aed..2dacc25e9 100644 --- a/compiler/ccgcalls.nim +++ b/compiler/ccgcalls.nim @@ -19,13 +19,13 @@ proc hasNoInit(call: PNode): bool {.inline.} = result = call.sons[0].kind == nkSym and sfNoInit in call.sons[0].sym.flags proc fixupCall(p: BProc, le, ri: PNode, d: var TLoc, - callee, params: PRope) = - var pl = con(callee, ~"(", params) + callee, params: Rope) = + var pl = callee & ~"(" & params # getUniqueType() is too expensive here: var typ = skipTypes(ri.sons[0].typ, abstractInst) if typ.sons[0] != nil: if isInvalidReturnType(typ.sons[0]): - if params != nil: pl.app(~", ") + if params != nil: pl.add(~", ") # beware of 'result = p(result)'. We may need to allocate a temporary: if d.k in {locTemp, locNone} or not leftAppearsOnRightSide(le, ri): # Great, we can use 'd': @@ -33,18 +33,18 @@ proc fixupCall(p: BProc, le, ri: PNode, d: var TLoc, elif d.k notin {locExpr, locTemp} and not hasNoInit(ri): # reset before pass as 'result' var: resetLoc(p, d) - app(pl, addrLoc(d)) - app(pl, ~");$n") + add(pl, addrLoc(d)) + add(pl, ~");$n") line(p, cpsStmts, pl) else: var tmp: TLoc getTemp(p, typ.sons[0], tmp, needsInit=true) - app(pl, addrLoc(tmp)) - app(pl, ~");$n") + add(pl, addrLoc(tmp)) + add(pl, ~");$n") line(p, cpsStmts, pl) genAssignment(p, d, tmp, {}) # no need for deep copying else: - app(pl, ~")") + add(pl, ~")") if p.module.compileToCpp and lfSingleUse in d.flags: # do not generate spurious temporaries for C++! For C we're better off # with them to prevent undefined behaviour and because the codegen @@ -60,7 +60,7 @@ proc fixupCall(p: BProc, le, ri: PNode, d: var TLoc, list.r = pl genAssignment(p, d, list, {}) # no need for deep copying else: - app(pl, ~");$n") + add(pl, ~");$n") line(p, cpsStmts, pl) proc isInCurrentFrame(p: BProc, n: PNode): bool = @@ -83,7 +83,7 @@ proc isInCurrentFrame(p: BProc, n: PNode): bool = result = isInCurrentFrame(p, n.sons[0]) else: discard -proc openArrayLoc(p: BProc, n: PNode): PRope = +proc openArrayLoc(p: BProc, n: PNode): Rope = var a: TLoc let q = skipConv(n) @@ -104,28 +104,28 @@ proc openArrayLoc(p: BProc, n: PNode): PRope = else: "$1->data+($2), ($3)-($2)+1" else: (internalError("openArrayLoc: " & typeToString(a.t)); "") - result = ropef(fmt, [rdLoc(a), rdLoc(b), rdLoc(c)]) + result = fmt % [rdLoc(a), rdLoc(b), rdLoc(c)] else: initLocExpr(p, n, a) case skipTypes(a.t, abstractVar).kind of tyOpenArray, tyVarargs: - result = ropef("$1, $1Len0", [rdLoc(a)]) + result = "$1, $1Len0" % [rdLoc(a)] of tyString, tySequence: if skipTypes(n.typ, abstractInst).kind == tyVar and not compileToCpp(p.module): - result = ropef("(*$1)->data, (*$1)->$2", [a.rdLoc, lenField(p)]) + result = "(*$1)->data, (*$1)->$2" % [a.rdLoc, lenField(p)] else: - result = ropef("$1->data, $1->$2", [a.rdLoc, lenField(p)]) + result = "$1->data, $1->$2" % [a.rdLoc, lenField(p)] of tyArray, tyArrayConstr: - result = ropef("$1, $2", [rdLoc(a), toRope(lengthOrd(a.t))]) + result = "$1, $2" % [rdLoc(a), rope(lengthOrd(a.t))] else: internalError("openArrayLoc: " & typeToString(a.t)) -proc genArgStringToCString(p: BProc, n: PNode): PRope {.inline.} = +proc genArgStringToCString(p: BProc, n: PNode): Rope {.inline.} = var a: TLoc initLocExpr(p, n.sons[0], a) - result = ropef("$1->data", [a.rdLoc]) + result = "$1->data" % [a.rdLoc] -proc genArg(p: BProc, n: PNode, param: PSym; call: PNode): PRope = +proc genArg(p: BProc, n: PNode, param: PSym; call: PNode): Rope = var a: TLoc if n.kind == nkStringToCString: result = genArgStringToCString(p, n) @@ -151,7 +151,7 @@ proc genArg(p: BProc, n: PNode, param: PSym; call: PNode): PRope = initLocExprSingleUse(p, n, a) result = rdLoc(a) -proc genArgNoParam(p: BProc, n: PNode): PRope = +proc genArgNoParam(p: BProc, n: PNode): Rope = var a: TLoc if n.kind == nkStringToCString: result = genArgStringToCString(p, n) @@ -163,7 +163,7 @@ proc genPrefixCall(p: BProc, le, ri: PNode, d: var TLoc) = var op: TLoc # this is a hotspot in the compiler initLocExpr(p, ri.sons[0], op) - var params: PRope + var params: Rope # getUniqueType() is too expensive here: var typ = skipTypes(ri.sons[0].typ, abstractInst) assert(typ.kind == tyProc) @@ -171,27 +171,27 @@ proc genPrefixCall(p: BProc, le, ri: PNode, d: var TLoc) = var length = sonsLen(ri) for i in countup(1, length - 1): if ri.sons[i].typ.isCompileTimeOnly: continue - if params != nil: app(params, ~", ") + if params != nil: add(params, ~", ") if i < sonsLen(typ): assert(typ.n.sons[i].kind == nkSym) - app(params, genArg(p, ri.sons[i], typ.n.sons[i].sym, ri)) + add(params, genArg(p, ri.sons[i], typ.n.sons[i].sym, ri)) else: - app(params, genArgNoParam(p, ri.sons[i])) + add(params, genArgNoParam(p, ri.sons[i])) fixupCall(p, le, ri, d, op.r, params) proc genClosureCall(p: BProc, le, ri: PNode, d: var TLoc) = - proc getRawProcType(p: BProc, t: PType): PRope = + proc getRawProcType(p: BProc, t: PType): Rope = result = getClosureType(p.module, t, clHalf) - proc addComma(r: PRope): PRope = - result = if r == nil: r else: con(r, ~", ") + proc addComma(r: Rope): Rope = + result = if r == nil: r else: r & ~", " const PatProc = "$1.ClEnv? $1.ClPrc($3$1.ClEnv):(($4)($1.ClPrc))($2)" const PatIter = "$1.ClPrc($3$1.ClEnv)" # we know the env exists var op: TLoc initLocExpr(p, ri.sons[0], op) - var pl: PRope + var pl: Rope var typ = skipTypes(ri.sons[0].typ, abstractInst) assert(typ.kind == tyProc) @@ -201,19 +201,19 @@ proc genClosureCall(p: BProc, le, ri: PNode, d: var TLoc) = if ri.sons[i].typ.isCompileTimeOnly: continue if i < sonsLen(typ): assert(typ.n.sons[i].kind == nkSym) - app(pl, genArg(p, ri.sons[i], typ.n.sons[i].sym, ri)) + add(pl, genArg(p, ri.sons[i], typ.n.sons[i].sym, ri)) else: - app(pl, genArgNoParam(p, ri.sons[i])) - if i < length - 1: app(pl, ~", ") + add(pl, genArgNoParam(p, ri.sons[i])) + if i < length - 1: add(pl, ~", ") template genCallPattern {.dirty.} = - lineF(p, cpsStmts, callPattern & ";$n", op.r, pl, pl.addComma, rawProc) + lineF(p, cpsStmts, callPattern & ";$n", [op.r, pl, pl.addComma, rawProc]) let rawProc = getRawProcType(p, typ) let callPattern = if tfIterator in typ.flags: PatIter else: PatProc if typ.sons[0] != nil: if isInvalidReturnType(typ.sons[0]): - if sonsLen(ri) > 1: app(pl, ~", ") + if sonsLen(ri) > 1: add(pl, ~", ") # beware of 'result = p(result)'. We may need to allocate a temporary: if d.k in {locTemp, locNone} or not leftAppearsOnRightSide(le, ri): # Great, we can use 'd': @@ -222,12 +222,12 @@ proc genClosureCall(p: BProc, le, ri: PNode, d: var TLoc) = elif d.k notin {locExpr, locTemp} and not hasNoInit(ri): # reset before pass as 'result' var: resetLoc(p, d) - app(pl, addrLoc(d)) + add(pl, addrLoc(d)) genCallPattern() else: var tmp: TLoc getTemp(p, typ.sons[0], tmp, needsInit=true) - app(pl, addrLoc(tmp)) + add(pl, addrLoc(tmp)) genCallPattern() genAssignment(p, d, tmp, {}) # no need for deep copying else: @@ -235,12 +235,12 @@ proc genClosureCall(p: BProc, le, ri: PNode, d: var TLoc) = assert(d.t != nil) # generate an assignment to d: var list: TLoc initLoc(list, locCall, d.t, OnUnknown) - list.r = ropef(callPattern, op.r, pl, pl.addComma, rawProc) + list.r = callPattern % [op.r, pl, pl.addComma, rawProc] genAssignment(p, d, list, {}) # no need for deep copying else: genCallPattern() -proc genOtherArg(p: BProc; ri: PNode; i: int; typ: PType): PRope = +proc genOtherArg(p: BProc; ri: PNode; i: int; typ: PType): Rope = if ri.sons[i].typ.isCompileTimeOnly: result = nil 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'. # However manual wrappers may also use 'ptr T'. In any case we support both # for convenience. @@ -301,83 +319,84 @@ proc genThisArg(p: BProc; ri: PNode; i: int; typ: PType): PRope = # skip the deref: var ri = ri[i] 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 if x.typ.kind == tyPtr: result = genArgNoParam(p, x) - result.app("->") + result.add("->") elif x.kind in {nkHiddenDeref, nkDerefExpr} and x[0].typ.kind == tyPtr: result = genArgNoParam(p, x[0]) - result.app("->") + result.add("->") else: result = genArgNoParam(p, x) - result.app(".") - elif typ.sons[i].kind == tyPtr: + result.add(".") + elif t.kind == tyPtr: if ri.kind in {nkAddr, nkHiddenAddr}: result = genArgNoParam(p, ri[0]) - result.app(".") + result.add(".") else: result = genArgNoParam(p, ri) - result.app("->") + result.add("->") else: + ri = skipAddrDeref(ri) + if ri.kind in {nkAddr, nkHiddenAddr}: ri = ri[0] result = genArgNoParam(p, ri) #, typ.n.sons[i].sym) - result.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 j = 1 while i < pat.len: case pat[i] of '@': 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: - result.app(~", ") - result.app genOtherArg(p, ri, k, typ) + result.add(~", ") + result.add genOtherArg(p, ri, k, typ) inc i of '#': if pat[i+1] in {'+', '@'}: let ri = ri[j] if ri.kind in nkCallKinds: let typ = skipTypes(ri.sons[0].typ, abstractInst) - if pat[i+1] == '+': result.app genArgNoParam(p, ri.sons[0]) - result.app(~"(") + if pat[i+1] == '+': result.add genArgNoParam(p, ri.sons[0]) + result.add(~"(") 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: - result.app(~", ") - result.app genOtherArg(p, ri, k, typ) - result.app(~")") + result.add(~", ") + result.add genOtherArg(p, ri, k, typ) + result.add(~")") else: localError(ri.info, "call expression expected for C++ pattern") inc i elif pat[i+1] == '.': - result.app genThisArg(p, ri, j, typ) + result.add genThisArg(p, ri, j, typ) inc i + elif pat[i+1] == '[': + var arg = ri.sons[j].skipAddrDeref + while arg.kind in {nkAddr, nkHiddenAddr, nkObjDownConv}: arg = arg[0] + result.add genArgNoParam(p, arg) + #result.add debugTree(arg, 0, 10) else: - result.app genOtherArg(p, ri, j, typ) + result.add genOtherArg(p, ri, j, typ) inc j inc i of '\'': - inc i - let stars = i - while pat[i] == '*': inc i - if pat[i] in Digits: - let j = pat[i].ord - '0'.ord - 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 + var idx, stars: int + if scanCppGenericSlot(pat, i, idx, stars): + var t = resolveStarsInCppType(typ, idx, stars) + if t == nil: result.add(~"void") + else: result.add(getTypeDesc(p.module, t)) else: let start = i while i < pat.len: if pat[i] notin {'@', '#', '\''}: inc(i) else: break if i - 1 >= start: - app(result, substr(pat, start, i - 1)) + add(result, substr(pat, start, i - 1)) proc genInfixCall(p: BProc, le, ri: PNode, d: var TLoc) = var op, a: TLoc @@ -387,7 +406,7 @@ proc genInfixCall(p: BProc, le, ri: PNode, d: var TLoc) = assert(typ.kind == tyProc) var length = sonsLen(ri) 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 internalAssert pat != nil if pat.contains({'#', '(', '@', '\''}): @@ -410,19 +429,19 @@ proc genInfixCall(p: BProc, le, ri: PNode, d: var TLoc) = list.r = pl genAssignment(p, d, list, {}) # no need for deep copying else: - app(pl, ~";$n") + add(pl, ~";$n") line(p, cpsStmts, pl) else: - var pl: PRope = nil + var pl: Rope = nil #var param = typ.n.sons[1].sym if 1 < ri.len: - app(pl, genThisArg(p, ri, 1, typ)) - app(pl, op.r) - var params: PRope + add(pl, genThisArg(p, ri, 1, typ)) + add(pl, op.r) + var params: Rope for i in countup(2, length - 1): - if params != nil: params.app(~", ") + if params != nil: params.add(~", ") 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) 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) 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 internalAssert pat != nil var start = 3 if ' ' in pat: start = 1 - app(pl, op.r) + add(pl, op.r) if length > 1: - app(pl, ~": ") - app(pl, genArg(p, ri.sons[1], typ.n.sons[1].sym, ri)) + add(pl, ~": ") + add(pl, genArg(p, ri.sons[1], typ.n.sons[1].sym, ri)) start = 2 else: if length > 1: - app(pl, genArg(p, ri.sons[1], typ.n.sons[1].sym, ri)) - app(pl, ~" ") - app(pl, op.r) + add(pl, genArg(p, ri.sons[1], typ.n.sons[1].sym, ri)) + add(pl, ~" ") + add(pl, op.r) if length > 2: - app(pl, ~": ") - app(pl, genArg(p, ri.sons[2], typ.n.sons[2].sym, ri)) + add(pl, ~": ") + add(pl, genArg(p, ri.sons[2], typ.n.sons[2].sym, ri)) for i in countup(start, length-1): assert(sonsLen(typ) == sonsLen(typ.n)) if i >= sonsLen(typ): internalError(ri.info, "varargs for objective C method?") assert(typ.n.sons[i].kind == nkSym) var param = typ.n.sons[i].sym - app(pl, ~" ") - app(pl, param.name.s) - app(pl, ~": ") - app(pl, genArg(p, ri.sons[i], param, ri)) + add(pl, ~" ") + add(pl, param.name.s) + add(pl, ~": ") + add(pl, genArg(p, ri.sons[i], param, ri)) if typ.sons[0] != nil: if isInvalidReturnType(typ.sons[0]): - if sonsLen(ri) > 1: app(pl, ~" ") + if sonsLen(ri) > 1: add(pl, ~" ") # beware of 'result = p(result)'. We always allocate a temporary: if d.k in {locTemp, locNone}: # We already got a temp. Great, special case it: if d.k == locNone: getTemp(p, typ.sons[0], d, needsInit=true) - app(pl, ~"Result: ") - app(pl, addrLoc(d)) - app(pl, ~"];$n") + add(pl, ~"Result: ") + add(pl, addrLoc(d)) + add(pl, ~"];$n") line(p, cpsStmts, pl) else: var tmp: TLoc getTemp(p, typ.sons[0], tmp, needsInit=true) - app(pl, addrLoc(tmp)) - app(pl, ~"];$n") + add(pl, addrLoc(tmp)) + add(pl, ~"];$n") line(p, cpsStmts, pl) genAssignment(p, d, tmp, {}) # no need for deep copying else: - app(pl, ~"]") + add(pl, ~"]") if d.k == locNone: getTemp(p, typ.sons[0], d) assert(d.t != nil) # generate an assignment to d: var list: TLoc @@ -492,7 +511,7 @@ proc genNamedParamCall(p: BProc, ri: PNode, d: var TLoc) = list.r = pl genAssignment(p, d, list, {}) # no need for deep copying else: - app(pl, ~"];$n") + add(pl, ~"];$n") line(p, cpsStmts, pl) proc genCall(p: BProc, e: PNode, d: var TLoc) = diff --git a/compiler/ccgexprs.nim b/compiler/ccgexprs.nim index aa1bf31be..05a3602d1 100644 --- a/compiler/ccgexprs.nim +++ b/compiler/ccgexprs.nim @@ -11,45 +11,45 @@ # -------------------------- constant expressions ------------------------ -proc int64Literal(i: BiggestInt): PRope = +proc int64Literal(i: BiggestInt): Rope = if i > low(int64): - result = rfmt(nil, "IL64($1)", toRope(i)) + result = rfmt(nil, "IL64($1)", rope(i)) else: result = ~"(IL64(-9223372036854775807) - IL64(1))" -proc uint64Literal(i: uint64): PRope = toRope($i & "ULL") +proc uint64Literal(i: uint64): Rope = rope($i & "ULL") -proc intLiteral(i: BiggestInt): PRope = +proc intLiteral(i: BiggestInt): Rope = if i > low(int32) and i <= high(int32): - result = toRope(i) + result = rope(i) elif i == low(int32): # Nim has the same bug for the same reasons :-) result = ~"(-2147483647 -1)" elif i > low(int64): - result = rfmt(nil, "IL64($1)", toRope(i)) + result = rfmt(nil, "IL64($1)", rope(i)) else: result = ~"(IL64(-9223372036854775807) - IL64(1))" -proc int32Literal(i: int): PRope = +proc int32Literal(i: int): Rope = if i == int(low(int32)): result = ~"(-2147483647 -1)" else: - result = toRope(i) + result = rope(i) -proc genHexLiteral(v: PNode): PRope = +proc genHexLiteral(v: PNode): Rope = # hex literals are unsigned in C # so we don't generate hex literals any longer. if v.kind notin {nkIntLit..nkUInt64Lit}: internalError(v.info, "genHexLiteral") result = intLiteral(v.intVal) -proc getStrLit(m: BModule, s: string): PRope = +proc getStrLit(m: BModule, s: string): Rope = discard cgsym(m, "TGenericSeq") - result = con("TMP", toRope(backendId())) - appf(m.s[cfsData], "STRING_LITERAL($1, $2, $3);$n", - [result, makeCString(s), toRope(len(s))]) + result = "TMP" & rope(backendId()) + addf(m.s[cfsData], "STRING_LITERAL($1, $2, $3);$n", + [result, makeCString(s), rope(len(s))]) -proc genLiteral(p: BProc, n: PNode, ty: PType): PRope = +proc genLiteral(p: BProc, n: PNode, ty: PType): Rope = if ty == nil: internalError(n.info, "genLiteral: ty is nil") case n.kind of nkCharLit..nkUInt64Lit: @@ -62,21 +62,21 @@ proc genLiteral(p: BProc, n: PNode, ty: PType): PRope = of tyInt64: result = int64Literal(n.intVal) of tyUInt64: result = uint64Literal(uint64(n.intVal)) else: - result = ropef("(($1) $2)", [getTypeDesc(p.module, - skipTypes(ty, abstractVarRange)), intLiteral(n.intVal)]) + result = "(($1) $2)" % [getTypeDesc(p.module, + skipTypes(ty, abstractVarRange)), intLiteral(n.intVal)] of nkNilLit: let t = skipTypes(ty, abstractVarRange) if t.kind == tyProc and t.callConv == ccClosure: var id = nodeTableTestOrSet(p.module.dataCache, n, gBackendId) - result = con("TMP", toRope(id)) + result = "TMP" & rope(id) if id == gBackendId: # not found in cache: inc(gBackendId) - appf(p.module.s[cfsData], + addf(p.module.s[cfsData], "static NIM_CONST $1 $2 = {NIM_NIL,NIM_NIL};$n", [getTypeDesc(p.module, t), result]) else: - result = toRope("NIM_NIL") + result = rope("NIM_NIL") of nkStrLit..nkTripleStrLit: if n.strVal.isNil: result = ropecg(p.module, "((#NimStringDesc*) NIM_NIL)", []) @@ -87,16 +87,16 @@ proc genLiteral(p: BProc, n: PNode, ty: PType): PRope = result = ropecg(p.module, "((#NimStringDesc*) &$1)", [getStrLit(p.module, n.strVal)]) else: - result = ropecg(p.module, "((#NimStringDesc*) &TMP$1)", [toRope(id)]) + result = ropecg(p.module, "((#NimStringDesc*) &TMP$1)", [rope(id)]) else: result = makeCString(n.strVal) of nkFloatLit..nkFloat64Lit: - result = toRope(n.floatVal.toStrMaxPrecision) + result = rope(n.floatVal.toStrMaxPrecision) else: internalError(n.info, "genLiteral(" & $n.kind & ')') result = nil -proc genLiteral(p: BProc, n: PNode): PRope = +proc genLiteral(p: BProc, n: PNode): Rope = result = genLiteral(p, n, n.typ) proc bitSetToWord(s: TBitSet, size: int): BiggestInt = @@ -113,10 +113,10 @@ proc bitSetToWord(s: TBitSet, size: int): BiggestInt = for j in countup(0, size - 1): if j < len(s): result = result or `shl`(Ze64(s[j]), (Size - 1 - j) * 8) -proc genRawSetData(cs: TBitSet, size: int): PRope = - var frmt: TFormatStr +proc genRawSetData(cs: TBitSet, size: int): Rope = + var frmt: FormatStr if size > 8: - result = ropef("{$n") + result = "{$n" % [] for i in countup(0, size - 1): if i < size - 1: # not last iteration? @@ -124,22 +124,22 @@ proc genRawSetData(cs: TBitSet, size: int): PRope = else: frmt = "0x$1, " else: frmt = "0x$1}$n" - appf(result, frmt, [toRope(toHex(ze64(cs[i]), 2))]) + addf(result, frmt, [rope(toHex(ze64(cs[i]), 2))]) else: result = intLiteral(bitSetToWord(cs, size)) - # result := toRope('0x' + ToHex(bitSetToWord(cs, size), size * 2)) + # result := rope('0x' + ToHex(bitSetToWord(cs, size), size * 2)) -proc genSetNode(p: BProc, n: PNode): PRope = +proc genSetNode(p: BProc, n: PNode): Rope = var cs: TBitSet var size = int(getSize(n.typ)) toBitSet(n, cs) if size > 8: var id = nodeTableTestOrSet(p.module.dataCache, n, gBackendId) - result = con("TMP", toRope(id)) + result = "TMP" & rope(id) if id == gBackendId: # not found in cache: inc(gBackendId) - appf(p.module.s[cfsData], "static NIM_CONST $1 $2 = $3;$n", + addf(p.module.s[cfsData], "static NIM_CONST $1 $2 = $3;$n", [getTypeDesc(p.module, n.typ), result, genRawSetData(cs, size)]) else: result = genRawSetData(cs, size) @@ -211,12 +211,12 @@ proc asgnComplexity(n: PNode): int = result += asgnComplexity(t) else: discard -proc optAsgnLoc(a: TLoc, t: PType, field: PRope): TLoc = +proc optAsgnLoc(a: TLoc, t: PType, field: Rope): TLoc = assert field != nil result.k = locField result.s = a.s result.t = t - result.r = rdLoc(a).con(".").con(field) + result.r = rdLoc(a) & "." & field result.heapRoot = a.heapRoot proc genOptAsgnTuple(p: BProc, dest, src: TLoc, flags: TAssignmentFlags) = @@ -230,7 +230,7 @@ proc genOptAsgnTuple(p: BProc, dest, src: TLoc, flags: TAssignmentFlags) = let t = skipTypes(dest.t, abstractInst).getUniqueType() for i in 0 .. <t.len: let t = t.sons[i] - let field = ropef("Field$1", i.toRope) + let field = "Field$1" % [i.rope] genAssignment(p, optAsgnLoc(dest, t, field), optAsgnLoc(src, t, field), newflags) @@ -313,8 +313,8 @@ proc genAssignment(p: BProc, dest, src: TLoc, flags: TAssignmentFlags) = of tyProc: if needsComplexAssignment(dest.t): # optimize closure assignment: - let a = optAsgnLoc(dest, dest.t, "ClEnv".toRope) - let b = optAsgnLoc(src, dest.t, "ClEnv".toRope) + let a = optAsgnLoc(dest, dest.t, "ClEnv".rope) + let b = optAsgnLoc(src, dest.t, "ClEnv".rope) genRefAssign(p, a, b, flags) linefmt(p, cpsStmts, "$1.ClPrc = $2.ClPrc;$n", rdLoc(dest), rdLoc(src)) else: @@ -365,7 +365,7 @@ proc genAssignment(p: BProc, dest, src: TLoc, flags: TAssignmentFlags) = if mapType(ty) == ctArray: useStringh(p.module) linefmt(p, cpsStmts, "memcpy((void*)$1, (NIM_CONST void*)$2, $3);$n", - rdLoc(dest), rdLoc(src), toRope(getSize(dest.t))) + rdLoc(dest), rdLoc(src), rope(getSize(dest.t))) else: linefmt(p, cpsStmts, "$1 = $2;$n", rdLoc(dest), rdLoc(src)) of tyPtr, tyPointer, tyChar, tyBool, tyEnum, tyCString, @@ -391,7 +391,7 @@ proc genDeepCopy(p: BProc; dest, src: TLoc) = if mapType(ty) == ctArray: useStringh(p.module) linefmt(p, cpsStmts, "memcpy((void*)$1, (NIM_CONST void*)$2, $3);$n", - rdLoc(dest), rdLoc(src), toRope(getSize(dest.t))) + rdLoc(dest), rdLoc(src), rope(getSize(dest.t))) else: linefmt(p, cpsStmts, "$1 = $2;$n", rdLoc(dest), rdLoc(src)) of tyPointer, tyChar, tyBool, tyEnum, tyCString, @@ -409,7 +409,7 @@ proc putLocIntoDest(p: BProc, d: var TLoc, s: TLoc) = else: d = s # ``d`` is free, so fill it with ``s`` -proc putDataIntoDest(p: BProc, d: var TLoc, t: PType, r: PRope) = +proc putDataIntoDest(p: BProc, d: var TLoc, t: PType, r: Rope) = var a: TLoc if d.k != locNone: # need to generate an assignment here @@ -424,7 +424,7 @@ proc putDataIntoDest(p: BProc, d: var TLoc, t: PType, r: PRope) = d.t = t d.r = r -proc putIntoDest(p: BProc, d: var TLoc, t: PType, r: PRope) = +proc putIntoDest(p: BProc, d: var TLoc, t: PType, r: Rope) = var a: TLoc if d.k != locNone: # need to generate an assignment here @@ -486,13 +486,13 @@ proc unaryExprChar(p: BProc, e: PNode, d: var TLoc, frmt: string) = putIntoDest(p, d, e.typ, ropecg(p.module, frmt, [rdCharLoc(a)])) proc binaryArithOverflowRaw(p: BProc, t: PType, a, b: TLoc; - frmt: string): PRope = + frmt: string): Rope = var size = getSize(t) - let storage = if size < platform.intSize: toRope("NI") + let storage = if size < platform.intSize: rope("NI") else: getTypeDesc(p.module, t) result = getTempName() linefmt(p, cpsLocals, "$1 $2;$n", storage, result) - lineCg(p, cpsStmts, frmt, result, rdLoc(a), rdLoc(b)) + lineCg(p, cpsStmts, frmt, result, rdCharLoc(a), rdCharLoc(b)) if size < platform.intSize or t.kind in {tyRange, tyEnum}: linefmt(p, cpsStmts, "if ($1 < $2 || $1 > $3) #raiseOverflow();$n", result, intLiteral(firstOrd(t)), intLiteral(lastOrd(t))) @@ -522,11 +522,11 @@ proc binaryArithOverflow(p: BProc, e: PNode, d: var TLoc, m: TMagic) = # later via 'chckRange' let t = e.typ.skipTypes(abstractRange) if optOverflowCheck notin p.options: - let res = ropef(opr[m], [getTypeDesc(p.module, t), rdLoc(a), rdLoc(b)]) + let res = opr[m] % [getTypeDesc(p.module, t), rdLoc(a), rdLoc(b)] putIntoDest(p, d, e.typ, res) else: let res = binaryArithOverflowRaw(p, t, a, b, prc[m]) - putIntoDest(p, d, e.typ, ropef("($#)($#)", [getTypeDesc(p.module, t), res])) + putIntoDest(p, d, e.typ, "($#)($#)" % [getTypeDesc(p.module, t), res]) proc unaryArithOverflow(p: BProc, e: PNode, d: var TLoc, m: TMagic) = const @@ -544,7 +544,7 @@ proc unaryArithOverflow(p: BProc, e: PNode, d: var TLoc, m: TMagic) = if optOverflowCheck in p.options: linefmt(p, cpsStmts, "if ($1 == $2) #raiseOverflow();$n", rdLoc(a), intLiteral(firstOrd(t))) - putIntoDest(p, d, e.typ, ropef(opr[m], [rdLoc(a), toRope(getSize(t) * 8)])) + putIntoDest(p, d, e.typ, opr[m] % [rdLoc(a), rope(getSize(t) * 8)]) proc binaryArith(p: BProc, e: PNode, d: var TLoc, op: TMagic) = const @@ -566,8 +566,6 @@ proc binaryArith(p: BProc, e: PNode, d: var TLoc, op: TMagic) = "($4)($1 & $2)", # BitandI64 "($4)($1 | $2)", # BitorI64 "($4)($1 ^ $2)", # BitxorI64 - "(($1 <= $2) ? $1 : $2)", # MinI64 - "(($1 >= $2) ? $1 : $2)", # MaxI64 "(($1 <= $2) ? $1 : $2)", # MinF64 "(($1 >= $2) ? $1 : $2)", # MaxF64 "($4)((NU$3)($1) + (NU$3)($2))", # AddU @@ -613,8 +611,8 @@ proc binaryArith(p: BProc, e: PNode, d: var TLoc, op: TMagic) = # BUGFIX: cannot use result-type here, as it may be a boolean s = max(getSize(a.t), getSize(b.t)) * 8 putIntoDest(p, d, e.typ, - ropef(binArithTab[op], [rdLoc(a), rdLoc(b), toRope(s), - getSimpleTypeDesc(p.module, e.typ)])) + binArithTab[op] % [rdLoc(a), rdLoc(b), rope(s), + getSimpleTypeDesc(p.module, e.typ)]) proc genEqProc(p: BProc, e: PNode, d: var TLoc) = var a, b: TLoc @@ -624,10 +622,9 @@ proc genEqProc(p: BProc, e: PNode, d: var TLoc) = initLocExpr(p, e.sons[2], b) if a.t.callConv == ccClosure: putIntoDest(p, d, e.typ, - ropef("($1.ClPrc == $2.ClPrc && $1.ClEnv == $2.ClEnv)", [ - rdLoc(a), rdLoc(b)])) + "($1.ClPrc == $2.ClPrc && $1.ClEnv == $2.ClEnv)" % [rdLoc(a), rdLoc(b)]) else: - putIntoDest(p, d, e.typ, ropef("($1 == $2)", [rdLoc(a), rdLoc(b)])) + putIntoDest(p, d, e.typ, "($1 == $2)" % [rdLoc(a), rdLoc(b)]) proc genIsNil(p: BProc, e: PNode, d: var TLoc) = let t = skipTypes(e.sons[1].typ, abstractRange) @@ -641,7 +638,6 @@ proc unaryArith(p: BProc, e: PNode, d: var TLoc, op: TMagic) = unArithTab: array[mNot..mToBiggestInt, string] = ["!($1)", # Not "$1", # UnaryPlusI "($3)((NU$2) ~($1))", # BitnotI - "$1", # UnaryPlusI64 "($3)((NU$2) ~($1))", # BitnotI64 "$1", # UnaryPlusF64 "-($1)", # UnaryMinusF64 @@ -667,8 +663,8 @@ proc unaryArith(p: BProc, e: PNode, d: var TLoc, op: TMagic) = initLocExpr(p, e.sons[1], a) t = skipTypes(e.typ, abstractRange) putIntoDest(p, d, e.typ, - ropef(unArithTab[op], [rdLoc(a), toRope(getSize(t) * 8), - getSimpleTypeDesc(p.module, e.typ)])) + unArithTab[op] % [rdLoc(a), rope(getSize(t) * 8), + getSimpleTypeDesc(p.module, e.typ)]) proc isCppRef(p: BProc; typ: PType): bool {.inline.} = result = p.module.compileToCpp and @@ -677,7 +673,7 @@ proc isCppRef(p: BProc; typ: PType): bool {.inline.} = proc genDeref(p: BProc, e: PNode, d: var TLoc; enforceDeref=false) = let mt = mapType(e.sons[0].typ) - if (mt in {ctArray, ctPtrToArray} and not enforceDeref): + if mt in {ctArray, ctPtrToArray} and not enforceDeref: # XXX the amount of hacks for C's arrays is incredible, maybe we should # simply wrap them in a struct? --> Losing auto vectorization then? #if e[0].kind != nkBracketExpr: @@ -686,19 +682,29 @@ proc genDeref(p: BProc, e: PNode, d: var TLoc; enforceDeref=false) = else: var a: TLoc initLocExprSingleUse(p, e.sons[0], a) - let typ = skipTypes(a.t, abstractInst) - case typ.kind - of tyRef: - d.s = OnHeap - of tyVar: - d.s = OnUnknown - if tfVarIsPtr notin typ.flags and p.module.compileToCpp and - e.kind == nkHiddenDeref: + if d.k == locNone: + let typ = skipTypes(a.t, abstractInst) + # dest = *a; <-- We do not know that 'dest' is on the heap! + # It is completely wrong to set 'd.s' here, unless it's not yet + # been assigned to. + case typ.kind + of tyRef: + d.s = OnHeap + of tyVar: + d.s = OnUnknown + if tfVarIsPtr notin typ.flags and p.module.compileToCpp and + e.kind == nkHiddenDeref: + putIntoDest(p, d, e.typ, rdLoc(a)) + return + of tyPtr: + d.s = OnUnknown # BUGFIX! + else: internalError(e.info, "genDeref " & $a.t.kind) + elif p.module.compileToCpp: + let typ = skipTypes(a.t, abstractInst) + if typ.kind == tyVar and tfVarIsPtr notin typ.flags and + e.kind == nkHiddenDeref: putIntoDest(p, d, e.typ, rdLoc(a)) return - of tyPtr: - d.s = OnUnknown # BUGFIX! - else: internalError(e.info, "genDeref " & $a.t.kind) if enforceDeref and mt == ctPtrToArray: # we lie about the type for better C interop: 'ptr array[3,T]' is # translated to 'ptr T', but for deref'ing this produces wrong code. @@ -707,14 +713,14 @@ proc genDeref(p: BProc, e: PNode, d: var TLoc; enforceDeref=false) = # so the '&' and '*' cancel out: putIntoDest(p, d, a.t.sons[0], rdLoc(a)) else: - putIntoDest(p, d, e.typ, ropef("(*$1)", [rdLoc(a)])) + putIntoDest(p, d, e.typ, "(*$1)" % [rdLoc(a)]) proc genAddr(p: BProc, e: PNode, d: var TLoc) = # careful 'addr(myptrToArray)' needs to get the ampersand: if e.sons[0].typ.skipTypes(abstractInst).kind in {tyRef, tyPtr}: var a: TLoc initLocExpr(p, e.sons[0], a) - putIntoDest(p, d, e.typ, con("&", a.r)) + putIntoDest(p, d, e.typ, "&" & a.r) #Message(e.info, warnUser, "HERE NEW &") elif mapType(e.sons[0].typ) == ctArray or isCppRef(p, e.sons[0].typ): expr(p, e.sons[0], d) @@ -747,7 +753,7 @@ proc genTupleElem(p: BProc, e: PNode, d: var TLoc) = case e.sons[1].kind of nkIntLit..nkUInt64Lit: i = int(e.sons[1].intVal) else: internalError(e.info, "genTupleElem") - appf(r, ".Field$1", [toRope(i)]) + addf(r, ".Field$1", [rope(i)]) putIntoDest(p, d, ty.sons[i], r) proc genRecordField(p: BProc, e: PNode, d: var TLoc) = @@ -758,7 +764,7 @@ proc genRecordField(p: BProc, e: PNode, d: var TLoc) = if ty.kind == tyTuple: # we found a unique tuple type which lacks field information # so we use Field$i - appf(r, ".Field$1", [toRope(f.position)]) + addf(r, ".Field$1", [rope(f.position)]) putIntoDest(p, d, f.typ, r) else: var field: PSym = nil @@ -767,17 +773,17 @@ proc genRecordField(p: BProc, e: PNode, d: var TLoc) = internalError(e.info, "genRecordField") field = lookupInRecord(ty.n, f.name) if field != nil: break - if not p.module.compileToCpp: app(r, ".Sup") + if not p.module.compileToCpp: add(r, ".Sup") ty = getUniqueType(ty.sons[0]) if field == nil: internalError(e.info, "genRecordField 2 ") if field.loc.r == nil: internalError(e.info, "genRecordField 3") - appf(r, ".$1", [field.loc.r]) + addf(r, ".$1", [field.loc.r]) putIntoDest(p, d, field.typ, r) #d.s = a.s proc genInExprAux(p: BProc, e: PNode, a, b, d: var TLoc) -proc genFieldCheck(p: BProc, e: PNode, obj: PRope, field: PSym) = +proc genFieldCheck(p: BProc, e: PNode, obj: Rope, field: PSym) = var test, u, v: TLoc for i in countup(1, sonsLen(e) - 1): var it = e.sons[i] @@ -790,12 +796,12 @@ proc genFieldCheck(p: BProc, e: PNode, obj: PRope, field: PSym) = initLoc(test, locNone, it.typ, OnStack) initLocExpr(p, it.sons[1], u) initLoc(v, locExpr, disc.typ, OnUnknown) - v.r = ropef("$1.$2", [obj, disc.sym.loc.r]) + v.r = "$1.$2" % [obj, disc.sym.loc.r] genInExprAux(p, it, u, v, test) let id = nodeTableTestOrSet(p.module.dataCache, newStrNode(nkStrLit, field.name.s), gBackendId) let strLit = if id == gBackendId: getStrLit(p.module, field.name.s) - else: con("TMP", toRope(id)) + else: "TMP" & rope(id) if op.magic == mNot: linefmt(p, cpsStmts, "if ($1) #raiseFieldError(((#NimStringDesc*) &$2));$n", @@ -811,7 +817,7 @@ proc genCheckedRecordField(p: BProc, e: PNode, d: var TLoc) = a: TLoc f, field: PSym ty: PType - r: PRope + r: Rope ty = genRecordFieldAux(p, e.sons[0], d, a) r = rdLoc(a) f = e.sons[0].sons[1].sym @@ -820,13 +826,13 @@ proc genCheckedRecordField(p: BProc, e: PNode, d: var TLoc) = assert(ty.kind in {tyTuple, tyObject}) field = lookupInRecord(ty.n, f.name) if field != nil: break - if not p.module.compileToCpp: app(r, ".Sup") + if not p.module.compileToCpp: add(r, ".Sup") ty = getUniqueType(ty.sons[0]) if field == nil: internalError(e.info, "genCheckedRecordField") if field.loc.r == nil: internalError(e.info, "genCheckedRecordField") # generate the checks: genFieldCheck(p, e, r, field) - app(r, rfmt(nil, ".$1", field.loc.r)) + add(r, rfmt(nil, ".$1", field.loc.r)) putIntoDest(p, d, field.typ, r) else: genRecordField(p, e.sons[0], d) @@ -955,11 +961,14 @@ proc genEcho(p: BProc, n: PNode) = # is threadsafe. internalAssert n.kind == nkBracket discard lists.includeStr(p.module.headerFiles, "<stdio.h>") - var args: PRope = nil + var args: Rope = nil var a: TLoc for i in countup(0, n.len-1): - initLocExpr(p, n.sons[i], a) - appf(args, ", $1? ($1)->data:\"nil\"", [rdLoc(a)]) + if n.sons[i].skipConv.kind == nkNilLit: + add(args, ", \"nil\"") + else: + initLocExpr(p, n.sons[i], a) + addf(args, ", $1? ($1)->data:\"nil\"", [rdLoc(a)]) linefmt(p, cpsStmts, "printf($1$2);$n", makeCString(repeat("%s", n.len) & tnl), args) @@ -986,22 +995,22 @@ proc genStrConcat(p: BProc, e: PNode, d: var TLoc) = var a, tmp: TLoc getTemp(p, e.typ, tmp) var L = 0 - var appends: PRope = nil - var lens: PRope = nil + var appends: Rope = nil + var lens: Rope = nil for i in countup(0, sonsLen(e) - 2): # compute the length expression: initLocExpr(p, e.sons[i + 1], a) if skipTypes(e.sons[i + 1].typ, abstractVarRange).kind == tyChar: inc(L) - app(appends, rfmt(p.module, "#appendChar($1, $2);$n", tmp.r, rdLoc(a))) + add(appends, rfmt(p.module, "#appendChar($1, $2);$n", tmp.r, rdLoc(a))) else: if e.sons[i + 1].kind in {nkStrLit..nkTripleStrLit}: inc(L, len(e.sons[i + 1].strVal)) else: - appf(lens, "$1->$2 + ", [rdLoc(a), lenField(p)]) - app(appends, rfmt(p.module, "#appendString($1, $2);$n", tmp.r, rdLoc(a))) - linefmt(p, cpsStmts, "$1 = #rawNewString($2$3);$n", tmp.r, lens, toRope(L)) - app(p.s(cpsStmts), appends) + addf(lens, "$1->$2 + ", [rdLoc(a), lenField(p)]) + add(appends, rfmt(p.module, "#appendString($1, $2);$n", tmp.r, rdLoc(a))) + linefmt(p, cpsStmts, "$1 = #rawNewString($2$3);$n", tmp.r, lens, rope(L)) + add(p.s(cpsStmts), appends) if d.k == locNone: d = tmp keepAlive(p, tmp) @@ -1023,7 +1032,7 @@ proc genStrAppend(p: BProc, e: PNode, d: var TLoc) = # } var a, dest: TLoc - appends, lens: PRope + appends, lens: Rope assert(d.k == locNone) var L = 0 initLocExpr(p, e.sons[1], dest) @@ -1032,19 +1041,19 @@ proc genStrAppend(p: BProc, e: PNode, d: var TLoc) = initLocExpr(p, e.sons[i + 2], a) if skipTypes(e.sons[i + 2].typ, abstractVarRange).kind == tyChar: inc(L) - app(appends, rfmt(p.module, "#appendChar($1, $2);$n", + add(appends, rfmt(p.module, "#appendChar($1, $2);$n", rdLoc(dest), rdLoc(a))) else: if e.sons[i + 2].kind in {nkStrLit..nkTripleStrLit}: inc(L, len(e.sons[i + 2].strVal)) else: - appf(lens, "$1->$2 + ", [rdLoc(a), lenField(p)]) - app(appends, rfmt(p.module, "#appendString($1, $2);$n", + addf(lens, "$1->$2 + ", [rdLoc(a), lenField(p)]) + add(appends, rfmt(p.module, "#appendString($1, $2);$n", rdLoc(dest), rdLoc(a))) linefmt(p, cpsStmts, "$1 = #resizeString($1, $2$3);$n", - rdLoc(dest), lens, toRope(L)) + rdLoc(dest), lens, rope(L)) keepAlive(p, dest) - app(p.s(cpsStmts), appends) + add(p.s(cpsStmts), appends) gcUsage(e) proc genSeqElemAppend(p: BProc, e: PNode, d: var TLoc) = @@ -1074,14 +1083,14 @@ proc genReset(p: BProc, n: PNode) = linefmt(p, cpsStmts, "#genericReset((void*)$1, $2);$n", addrLoc(a), genTypeInfo(p.module, skipTypes(a.t, abstractVarRange))) -proc rawGenNew(p: BProc, a: TLoc, sizeExpr: PRope) = +proc rawGenNew(p: BProc, a: TLoc, sizeExpr: Rope) = var sizeExpr = sizeExpr let refType = skipTypes(a.t, abstractVarRange) var b: TLoc initLoc(b, locExpr, a.t, OnHeap) if sizeExpr.isNil: - sizeExpr = ropef("sizeof($1)", - getTypeDesc(p.module, skipTypes(refType.sons[0], abstractRange))) + sizeExpr = "sizeof($1)" % + [getTypeDesc(p.module, skipTypes(refType.sons[0], abstractRange))] let args = [getTypeDesc(p.module, refType), genTypeInfo(p.module, refType), sizeExpr] @@ -1111,7 +1120,7 @@ proc genNew(p: BProc, e: PNode) = rawGenNew(p, a, nil) gcUsage(e) -proc genNewSeqAux(p: BProc, dest: TLoc, length: PRope) = +proc genNewSeqAux(p: BProc, dest: TLoc, length: Rope) = let seqtype = skipTypes(dest.t, abstractVarRange) let args = [getTypeDesc(p.module, seqtype), genTypeInfo(p.module, seqtype), length] @@ -1144,7 +1153,7 @@ proc genObjConstr(p: BProc, e: PNode, d: var TLoc) = if isRef: rawGenNew(p, tmp, nil) t = t.lastSon.skipTypes(abstractInst) - r = ropef("(*$1)", r) + r = "(*$1)" % [r] gcUsage(e) else: constructLoc(p, tmp) @@ -1158,13 +1167,13 @@ proc genObjConstr(p: BProc, e: PNode, d: var TLoc) = while ty != nil: field = lookupInRecord(ty.n, it.sons[0].sym.name) if field != nil: break - if not p.module.compileToCpp: app(tmp2.r, ".Sup") + if not p.module.compileToCpp: add(tmp2.r, ".Sup") ty = getUniqueType(ty.sons[0]) if field == nil or field.loc.r == nil: internalError(e.info, "genObjConstr") if it.len == 3 and optFieldCheck in p.options: genFieldCheck(p, it.sons[2], tmp2.r, field) - app(tmp2.r, ".") - app(tmp2.r, field.loc.r) + add(tmp2.r, ".") + add(tmp2.r, field.loc.r) tmp2.k = locTemp tmp2.t = field.loc.t tmp2.s = if isRef: OnHeap else: OnStack @@ -1214,14 +1223,14 @@ proc genNewFinalize(p: BProc, e: PNode) = var a, b, f: TLoc refType, bt: PType - ti: PRope + ti: Rope oldModule: BModule refType = skipTypes(e.sons[1].typ, abstractVarRange) initLocExpr(p, e.sons[1], a) initLocExpr(p, e.sons[2], f) initLoc(b, locExpr, a.t, OnHeap) ti = genTypeInfo(p.module, refType) - appf(p.module.s[cfsTypeInit3], "$1->finalizer = (void*)$2;$n", [ti, rdLoc(f)]) + addf(p.module.s[cfsTypeInit3], "$1->finalizer = (void*)$2;$n", [ti, rdLoc(f)]) b.r = ropecg(p.module, "($1) #newObj($2, sizeof($3))", [ getTypeDesc(p.module, refType), ti, getTypeDesc(p.module, skipTypes(refType.lastSon, abstractRange))]) @@ -1230,18 +1239,18 @@ proc genNewFinalize(p: BProc, e: PNode) = genObjectInit(p, cpsStmts, bt, a, false) gcUsage(e) -proc genOfHelper(p: BProc; dest: PType; a: PRope): PRope = +proc genOfHelper(p: BProc; dest: PType; a: Rope): Rope = # unfortunately 'genTypeInfo' sets tfObjHasKids as a side effect, so we # have to call it here first: let ti = genTypeInfo(p.module, dest) if tfFinal in dest.flags or (p.module.objHasKidsValid and tfObjHasKids notin dest.flags): - result = ropef("$1.m_type == $2", a, ti) + result = "$1.m_type == $2" % [a, ti] else: discard cgsym(p.module, "TNimType") inc p.module.labels - let cache = con("Nim_OfCheck_CACHE", p.module.labels.toRope) - appf(p.module.s[cfsVars], "static TNimType* $#[2];$n", cache) + let cache = "Nim_OfCheck_CACHE" & p.module.labels.rope + addf(p.module.s[cfsVars], "static TNimType* $#[2];$n", [cache]) result = rfmt(p.module, "#isObjWithCache($#.m_type, $#, $#)", a, ti, cache) when false: # former version: @@ -1253,7 +1262,7 @@ proc genOf(p: BProc, x: PNode, typ: PType, d: var TLoc) = initLocExpr(p, x, a) var dest = skipTypes(typ, typedescPtrs) var r = rdLoc(a) - var nilCheck: PRope = nil + var nilCheck: Rope = nil var t = skipTypes(a.t, abstractInst) while t.kind in {tyVar, tyPtr, tyRef}: if t.kind != tyVar: nilCheck = r @@ -1262,7 +1271,7 @@ proc genOf(p: BProc, x: PNode, typ: PType, d: var TLoc) = t = skipTypes(t.lastSon, typedescInst) if not p.module.compileToCpp: while t.kind == tyObject and t.sons[0] != nil: - app(r, ~".Sup") + add(r, ~".Sup") t = skipTypes(t.sons[0], typedescInst) if isObjLackingTypeField(t): globalError(x.info, errGenerated, @@ -1303,13 +1312,13 @@ proc genRepr(p: BProc, e: PNode, d: var TLoc) = var b: TLoc case a.t.kind of tyOpenArray, tyVarargs: - putIntoDest(p, b, e.typ, ropef("$1, $1Len0", [rdLoc(a)])) + putIntoDest(p, b, e.typ, "$1, $1Len0" % [rdLoc(a)]) of tyString, tySequence: putIntoDest(p, b, e.typ, - ropef("$1->data, $1->$2", [rdLoc(a), lenField(p)])) + "$1->data, $1->$2" % [rdLoc(a), lenField(p)]) of tyArray, tyArrayConstr: putIntoDest(p, b, e.typ, - ropef("$1, $2", [rdLoc(a), toRope(lengthOrd(a.t))])) + "$1, $2" % [rdLoc(a), rope(lengthOrd(a.t))]) else: internalError(e.sons[0].info, "genRepr()") putIntoDest(p, d, e.typ, ropecg(p.module, "#reprOpenArray($1, $2)", [rdLoc(b), @@ -1346,19 +1355,19 @@ proc genArrayLen(p: BProc, e: PNode, d: var TLoc, op: TMagic) = else: unaryExpr(p, e, d, "$1Len0") of tyCString: useStringh(p.module) - if op == mHigh: unaryExpr(p, e, d, "(strlen($1)-1)") - else: unaryExpr(p, e, d, "strlen($1)") + if op == mHigh: unaryExpr(p, e, d, "($1 ? (strlen($1)-1) : -1)") + else: unaryExpr(p, e, d, "($1 ? strlen($1) : 0)") of tyString, tySequence: if not p.module.compileToCpp: - if op == mHigh: unaryExpr(p, e, d, "($1->Sup.len-1)") - else: unaryExpr(p, e, d, "$1->Sup.len") + if op == mHigh: unaryExpr(p, e, d, "($1 ? ($1->Sup.len-1) : -1)") + else: unaryExpr(p, e, d, "($1 ? $1->Sup.len : 0)") else: - if op == mHigh: unaryExpr(p, e, d, "($1->len-1)") - else: unaryExpr(p, e, d, "$1->len") + if op == mHigh: unaryExpr(p, e, d, "($1 ? ($1->len-1) : -1)") + else: unaryExpr(p, e, d, "($1 ? $1->len : 0)") of tyArray, tyArrayConstr: # YYY: length(sideeffect) is optimized away incorrectly? - if op == mHigh: putIntoDest(p, d, e.typ, toRope(lastOrd(typ))) - else: putIntoDest(p, d, e.typ, toRope(lengthOrd(typ))) + if op == mHigh: putIntoDest(p, d, e.typ, rope(lastOrd(typ))) + else: putIntoDest(p, d, e.typ, rope(lengthOrd(typ))) else: internalError(e.info, "genArrayLen()") proc genSetLengthSeq(p: BProc, e: PNode, d: var TLoc) = @@ -1396,13 +1405,13 @@ proc genSwap(p: BProc, e: PNode, d: var TLoc) = genAssignment(p, a, b, {}) genAssignment(p, b, tmp, {}) -proc rdSetElemLoc(a: TLoc, setType: PType): PRope = +proc rdSetElemLoc(a: TLoc, setType: PType): Rope = # read a location of an set element; it may need a subtraction operation # before the set operation result = rdCharLoc(a) assert(setType.kind == tySet) if firstOrd(setType) != 0: - result = ropef("($1- $2)", [result, toRope(firstOrd(setType))]) + result = "($1- $2)" % [result, rope(firstOrd(setType))] proc fewCmps(s: PNode): bool = # this function estimates whether it is better to emit code @@ -1416,7 +1425,7 @@ proc fewCmps(s: PNode): bool = result = sonsLen(s) <= 8 # 8 seems to be a good value proc binaryExprIn(p: BProc, e: PNode, a, b, d: var TLoc, frmt: string) = - putIntoDest(p, d, e.typ, ropef(frmt, [rdLoc(a), rdSetElemLoc(b, a.t)])) + putIntoDest(p, d, e.typ, frmt % [rdLoc(a), rdSetElemLoc(b, a.t)]) proc genInExprAux(p: BProc, e: PNode, a, b, d: var TLoc) = case int(getSize(skipTypes(e.sons[1].typ, abstractVar))) @@ -1446,19 +1455,19 @@ proc genInOp(p: BProc, e: PNode, d: var TLoc) = e.sons[2] initLocExpr(p, ea, a) initLoc(b, locExpr, e.typ, OnUnknown) - b.r = toRope("(") + b.r = rope("(") var length = sonsLen(e.sons[1]) for i in countup(0, length - 1): if e.sons[1].sons[i].kind == nkRange: initLocExpr(p, e.sons[1].sons[i].sons[0], x) initLocExpr(p, e.sons[1].sons[i].sons[1], y) - appf(b.r, "$1 >= $2 && $1 <= $3", + addf(b.r, "$1 >= $2 && $1 <= $3", [rdCharLoc(a), rdCharLoc(x), rdCharLoc(y)]) else: initLocExpr(p, e.sons[1].sons[i], x) - appf(b.r, "$1 == $2", [rdCharLoc(a), rdCharLoc(x)]) - if i < length - 1: app(b.r, " || ") - app(b.r, ")") + addf(b.r, "$1 == $2", [rdCharLoc(a), rdCharLoc(x)]) + if i < length - 1: add(b.r, " || ") + add(b.r, ")") putIntoDest(p, d, e.typ, b.r) else: assert(e.sons[1].typ != nil) @@ -1514,7 +1523,7 @@ proc genSetOp(p: BProc, e: PNode, d: var TLoc, op: TMagic) = initLocExpr(p, e.sons[2], b) if d.k == locNone: getTemp(p, getSysType(tyBool), d) lineF(p, cpsStmts, lookupOpr[op], - [rdLoc(i), toRope(size), rdLoc(d), rdLoc(a), rdLoc(b)]) + [rdLoc(i), rope(size), rdLoc(d), rdLoc(a), rdLoc(b)]) of mEqSet: useStringh(p.module) binaryExprChar(p, e, d, "(memcmp($1, $2, " & $(size) & ")==0)") @@ -1527,8 +1536,8 @@ proc genSetOp(p: BProc, e: PNode, d: var TLoc, op: TMagic) = lineF(p, cpsStmts, "for ($1 = 0; $1 < $2; $1++) $n" & " $3[$1] = $4[$1] $6 $5[$1];$n", [ - rdLoc(i), toRope(size), rdLoc(d), rdLoc(a), rdLoc(b), - toRope(lookupOpr[op])]) + rdLoc(i), rope(size), rdLoc(d), rdLoc(a), rdLoc(b), + rope(lookupOpr[op])]) of mInSet: genInOp(p, e, d) else: internalError(e.info, "genSetOp") @@ -1545,14 +1554,14 @@ proc genSomeCast(p: BProc, e: PNode, d: var TLoc) = initLocExpr(p, e.sons[1], a) let etyp = skipTypes(e.typ, abstractRange) if etyp.kind in ValueTypes and lfIndirect notin a.flags: - putIntoDest(p, d, e.typ, ropef("(*($1*) ($2))", - [getTypeDesc(p.module, e.typ), addrLoc(a)])) + putIntoDest(p, d, e.typ, "(*($1*) ($2))" % + [getTypeDesc(p.module, e.typ), addrLoc(a)]) elif etyp.kind == tyProc and etyp.callConv == ccClosure: - putIntoDest(p, d, e.typ, ropef("(($1) ($2))", - [getClosureType(p.module, etyp, clHalfWithEnv), rdCharLoc(a)])) + putIntoDest(p, d, e.typ, "(($1) ($2))" % + [getClosureType(p.module, etyp, clHalfWithEnv), rdCharLoc(a)]) else: - putIntoDest(p, d, e.typ, ropef("(($1) ($2))", - [getTypeDesc(p.module, e.typ), rdCharLoc(a)])) + putIntoDest(p, d, e.typ, "(($1) ($2))" % + [getTypeDesc(p.module, e.typ), rdCharLoc(a)]) proc genCast(p: BProc, e: PNode, d: var TLoc) = const floatTypes = {tyFloat..tyFloat128} @@ -1562,9 +1571,9 @@ proc genCast(p: BProc, e: PNode, d: var TLoc) = if destt.kind in floatTypes or srct.kind in floatTypes: # 'cast' and some float type involved? --> use a union. inc(p.labels) - var lbl = p.labels.toRope + var lbl = p.labels.rope var tmp: TLoc - tmp.r = ropef("LOC$1.source", lbl) + tmp.r = "LOC$1.source" % [lbl] linefmt(p, cpsLocals, "union { $1 source; $2 dest; } LOC$3;$n", getTypeDesc(p.module, srct), getTypeDesc(p.module, destt), lbl) tmp.k = locExpr @@ -1572,7 +1581,7 @@ proc genCast(p: BProc, e: PNode, d: var TLoc) = tmp.s = OnStack tmp.flags = {} expr(p, e.sons[1], tmp) - putIntoDest(p, d, e.typ, ropef("LOC$#.dest", lbl)) + putIntoDest(p, d, e.typ, "LOC$#.dest" % [lbl]) else: # I prefer the shorter cast version for pointer types -> generate less # C code; plus it's the right thing to do for closures: @@ -1584,16 +1593,14 @@ proc genRangeChck(p: BProc, n: PNode, d: var TLoc, magic: string) = # range checks for unsigned turned out to be buggy and annoying: if optRangeCheck notin p.options or dest.kind in {tyUInt..tyUInt64}: initLocExpr(p, n.sons[0], a) - putIntoDest(p, d, n.typ, ropef("(($1) ($2))", - [getTypeDesc(p.module, dest), rdCharLoc(a)])) + putIntoDest(p, d, n.typ, "(($1) ($2))" % + [getTypeDesc(p.module, dest), rdCharLoc(a)]) else: initLocExpr(p, n.sons[0], a) - if leValue(n.sons[2], n.sons[1]): - internalError(n.info, "range check will always fail; empty range") putIntoDest(p, d, dest, ropecg(p.module, "(($1)#$5($2, $3, $4))", [ getTypeDesc(p.module, dest), rdCharLoc(a), genLiteral(p, n.sons[1], dest), genLiteral(p, n.sons[2], dest), - toRope(magic)])) + rope(magic)])) proc genConv(p: BProc, e: PNode, d: var TLoc) = let destType = e.typ.skipTypes({tyVar, tyGenericInst}) @@ -1605,8 +1612,7 @@ proc genConv(p: BProc, e: PNode, d: var TLoc) = proc convStrToCStr(p: BProc, n: PNode, d: var TLoc) = var a: TLoc initLocExpr(p, n.sons[0], a) - putIntoDest(p, d, skipTypes(n.typ, abstractVar), ropef("$1->data", - [rdLoc(a)])) + putIntoDest(p, d, skipTypes(n.typ, abstractVar), "$1->data" % [rdLoc(a)]) proc convCStrToStr(p: BProc, n: PNode, d: var TLoc) = var a: TLoc @@ -1641,7 +1647,7 @@ proc binaryFloatArith(p: BProc, e: PNode, d: var TLoc, m: TMagic) = initLocExpr(p, e.sons[1], a) initLocExpr(p, e.sons[2], b) putIntoDest(p, d, e.typ, rfmt(nil, "(($4)($2) $1 ($4)($3))", - toRope(opr[m]), rdLoc(a), rdLoc(b), + rope(opr[m]), rdLoc(a), rdLoc(b), getSimpleTypeDesc(p.module, e[1].typ))) if optNaNCheck in p.options: linefmt(p, cpsStmts, "#nanCheck($1);$n", rdLoc(d)) @@ -1651,7 +1657,7 @@ proc binaryFloatArith(p: BProc, e: PNode, d: var TLoc, m: TMagic) = binaryArith(p, e, d, m) proc genMagicExpr(p: BProc, e: PNode, d: var TLoc, op: TMagic) = - var line, filen: PRope + var line, filen: Rope case op of mOr, mAnd: genAndOr(p, e, d, op) of mNot..mToBiggestInt: unaryArith(p, e, d, op) @@ -1672,7 +1678,8 @@ proc genMagicExpr(p: BProc, e: PNode, d: var TLoc, op: TMagic) = "$# = #subInt64($#, $#);$n"] const fun: array [mInc..mDec, string] = ["$# = #addInt($#, $#);$n", "$# = #subInt($#, $#);$n"] - if optOverflowCheck notin p.options: + let underlying = skipTypes(e.sons[1].typ, {tyGenericInst, tyVar, tyRange}) + if optOverflowCheck notin p.options or underlying.kind in {tyUInt..tyUInt64}: binaryStmt(p, e, d, opr[op]) else: var a, b: TLoc @@ -1681,12 +1688,11 @@ proc genMagicExpr(p: BProc, e: PNode, d: var TLoc, op: TMagic) = initLocExpr(p, e.sons[1], a) initLocExpr(p, e.sons[2], b) - let underlying = skipTypes(e.sons[1].typ, {tyGenericInst, tyVar, tyRange}) let ranged = skipTypes(e.sons[1].typ, {tyGenericInst, tyVar}) let res = binaryArithOverflowRaw(p, ranged, a, b, if underlying.kind == tyInt64: fun64[op] else: fun[op]) - putIntoDest(p, a, ranged, ropef("($#)($#)", [ - getTypeDesc(p.module, ranged), res])) + putIntoDest(p, a, ranged, "($#)($#)" % [ + getTypeDesc(p.module, ranged), res]) of mConStrStr: genStrConcat(p, e, d) of mAppendStrCh: @@ -1713,12 +1719,16 @@ proc genMagicExpr(p: BProc, e: PNode, d: var TLoc, op: TMagic) = of mNewSeq: genNewSeq(p, e) of mSizeOf: let t = e.sons[1].typ.skipTypes({tyTypeDesc}) - putIntoDest(p, d, e.typ, ropef("((NI)sizeof($1))", - [getTypeDesc(p.module, t)])) + putIntoDest(p, d, e.typ, "((NI)sizeof($1))" % [getTypeDesc(p.module, t)]) of mChr: genSomeCast(p, e, d) of mOrd: genOrd(p, e, d) of mLengthArray, mHigh, mLengthStr, mLengthSeq, mLengthOpenArray: genArrayLen(p, e, d, op) + of mXLenStr, mXLenSeq: + if not p.module.compileToCpp: + unaryExpr(p, e, d, "($1->Sup.len-1)") + else: + unaryExpr(p, e, d, "$1->len") of mGCref: unaryStmt(p, e, d, "#nimGCref($1);$n") of mGCunref: unaryStmt(p, e, d, "#nimGCunref($1);$n") of mSetLengthStr: genSetLengthStr(p, e, d) @@ -1730,7 +1740,7 @@ proc genMagicExpr(p: BProc, e: PNode, d: var TLoc, op: TMagic) = mParseBiggestFloat: var opr = e.sons[0].sym if lfNoDecl notin opr.loc.flags: - discard cgsym(p.module, opr.loc.r.ropeToStr) + discard cgsym(p.module, $opr.loc.r) genCall(p, e, d) of mReset: genReset(p, e) of mEcho: genEcho(p, e[1].skipConv) @@ -1752,17 +1762,17 @@ proc genMagicExpr(p: BProc, e: PNode, d: var TLoc, op: TMagic) = of mDotDot: genCall(p, e, d) else: internalError(e.info, "genMagicExpr: " & $op) -proc genConstExpr(p: BProc, n: PNode): PRope +proc genConstExpr(p: BProc, n: PNode): Rope proc handleConstExpr(p: BProc, n: PNode, d: var TLoc): bool = if nfAllConst in n.flags and d.k == locNone and n.len > 0 and n.isDeepConstExpr: var t = getUniqueType(n.typ) discard getTypeDesc(p.module, t) # so that any fields are initialized var id = nodeTableTestOrSet(p.module.dataCache, n, gBackendId) - fillLoc(d, locData, t, con("TMP", toRope(id)), OnHeap) + fillLoc(d, locData, t, "TMP" & rope(id), OnHeap) if id == gBackendId: # expression not found in the cache: inc(gBackendId) - appf(p.module.s[cfsData], "NIM_CONST $1 $2 = $3;$n", + addf(p.module.s[cfsData], "NIM_CONST $1 $2 = $3;$n", [getTypeDesc(p.module, t), d.r, genConstExpr(p, n)]) result = true else: @@ -1824,13 +1834,12 @@ proc genTupleConstr(p: BProc, n: PNode, d: var TLoc) = var it = n.sons[i] if it.kind == nkExprColonExpr: it = it.sons[1] initLoc(rec, locExpr, it.typ, d.s) - rec.r = ropef("$1.Field$2", [rdLoc(d), toRope(i)]) + rec.r = "$1.Field$2" % [rdLoc(d), rope(i)] expr(p, it, rec) when false: initLoc(rec, locExpr, it.typ, d.s) if (t.n.sons[i].kind != nkSym): InternalError(n.info, "genTupleConstr") - rec.r = ropef("$1.$2", - [rdLoc(d), mangleRecFieldName(t.n.sons[i].sym, t)]) + rec.r = "$1.$2" % [rdLoc(d), mangleRecFieldName(t.n.sons[i].sym, t)] expr(p, it, rec) proc isConstClosure(n: PNode): bool {.inline.} = @@ -1842,8 +1851,8 @@ proc genClosure(p: BProc, n: PNode, d: var TLoc) = if isConstClosure(n): inc(p.labels) - var tmp = con("LOC", toRope(p.labels)) - appf(p.module.s[cfsData], "NIM_CONST $1 $2 = $3;$n", + var tmp = "LOC" & rope(p.labels) + addf(p.module.s[cfsData], "NIM_CONST $1 $2 = $3;$n", [getTypeDesc(p.module, n.typ), tmp, genConstExpr(p, n)]) putIntoDest(p, d, n.typ, tmp) else: @@ -1861,7 +1870,7 @@ proc genArrayConstr(p: BProc, n: PNode, d: var TLoc) = if d.k == locNone: getTemp(p, n.typ, d) for i in countup(0, sonsLen(n) - 1): initLoc(arr, locExpr, elemType(skipTypes(n.typ, abstractInst)), d.s) - arr.r = ropef("$1[$2]", [rdLoc(d), intLiteral(i)]) + arr.r = "$1[$2]" % [rdLoc(d), intLiteral(i)] expr(p, n.sons[i], arr) proc genComplexConst(p: BProc, sym: PSym, d: var TLoc) = @@ -1880,16 +1889,16 @@ proc upConv(p: BProc, n: PNode, d: var TLoc) = var dest = skipTypes(n.typ, abstractPtrs) if optObjCheck in p.options and not isObjLackingTypeField(dest): var r = rdLoc(a) - var nilCheck: PRope = nil + var nilCheck: Rope = nil var t = skipTypes(a.t, abstractInst) while t.kind in {tyVar, tyPtr, tyRef}: if t.kind != tyVar: nilCheck = r if t.kind != tyVar or not p.module.compileToCpp: - r = ropef("(*$1)", [r]) + r = "(*$1)" % [r] t = skipTypes(t.lastSon, abstractInst) if not p.module.compileToCpp: while t.kind == tyObject and t.sons[0] != nil: - app(r, ".Sup") + add(r, ".Sup") t = skipTypes(t.sons[0], abstractInst) if nilCheck != nil: linefmt(p, cpsStmts, "if ($1) #chckObj($2.m_type, $3);$n", @@ -1899,10 +1908,10 @@ proc upConv(p: BProc, n: PNode, d: var TLoc) = r, genTypeInfo(p.module, dest)) if n.sons[0].typ.kind != tyObject: putIntoDest(p, d, n.typ, - ropef("(($1) ($2))", [getTypeDesc(p.module, n.typ), rdLoc(a)])) + "(($1) ($2))" % [getTypeDesc(p.module, n.typ), rdLoc(a)]) else: - putIntoDest(p, d, n.typ, ropef("(*($1*) ($2))", - [getTypeDesc(p.module, dest), addrLoc(a)])) + putIntoDest(p, d, n.typ, "(*($1*) ($2))" % + [getTypeDesc(p.module, dest), addrLoc(a)]) proc downConv(p: BProc, n: PNode, d: var TLoc) = if p.module.compileToCpp: @@ -1919,10 +1928,10 @@ proc downConv(p: BProc, n: PNode, d: var TLoc) = var r = rdLoc(a) let isRef = skipTypes(arg.typ, abstractInst).kind in {tyRef, tyPtr, tyVar} if isRef: - app(r, "->Sup") + add(r, "->Sup") else: - app(r, ".Sup") - for i in countup(2, abs(inheritanceDiff(dest, src))): app(r, ".Sup") + add(r, ".Sup") + for i in countup(2, abs(inheritanceDiff(dest, src))): add(r, ".Sup") if isRef: # it can happen that we end up generating '&&x->Sup' here, so we pack # the '&x->Sup' into a temporary and then those address is taken @@ -1933,7 +1942,7 @@ proc downConv(p: BProc, n: PNode, d: var TLoc) = getTemp(p, n.typ, d) linefmt(p, cpsStmts, "$1 = &$2;$n", rdLoc(d), r) else: - r = con("&", r) + r = "&" & r putIntoDest(p, d, n.typ, r) else: putIntoDest(p, d, n.typ, r) @@ -1942,12 +1951,12 @@ proc exprComplexConst(p: BProc, n: PNode, d: var TLoc) = var t = getUniqueType(n.typ) discard getTypeDesc(p.module, t) # so that any fields are initialized var id = nodeTableTestOrSet(p.module.dataCache, n, gBackendId) - var tmp = con("TMP", toRope(id)) + var tmp = "TMP" & rope(id) if id == gBackendId: # expression not found in the cache: inc(gBackendId) - appf(p.module.s[cfsData], "NIM_CONST $1 $2 = $3;$n", + addf(p.module.s[cfsData], "NIM_CONST $1 $2 = $3;$n", [getTypeDesc(p.module, t), tmp, genConstExpr(p, n)]) if d.k == locNone: @@ -1982,7 +1991,7 @@ proc expr(p: BProc, n: PNode, d: var TLoc) = else: genComplexConst(p, sym, d) of skEnumField: - putIntoDest(p, d, n.typ, toRope(sym.position)) + putIntoDest(p, d, n.typ, rope(sym.position)) of skVar, skForVar, skResult, skLet: if sfGlobal in sym.flags: genVarPrototype(p.module, sym) if sym.loc.r == nil or sym.loc.t == nil: @@ -1991,7 +2000,7 @@ proc expr(p: BProc, n: PNode, d: var TLoc) = if sfThread in sym.flags: accessThreadLocalVar(p, sym) if emulatedThreadVars(): - putIntoDest(p, d, sym.loc.t, con("NimTV->", sym.loc.r)) + putIntoDest(p, d, sym.loc.t, "NimTV->" & sym.loc.r) else: putLocIntoDest(p, d, sym.loc) else: @@ -2134,42 +2143,42 @@ proc expr(p: BProc, n: PNode, d: var TLoc) = of nkBreakState: genBreakState(p, n) else: internalError(n.info, "expr(" & $n.kind & "); unknown node kind") -proc genNamedConstExpr(p: BProc, n: PNode): PRope = +proc genNamedConstExpr(p: BProc, n: PNode): Rope = if n.kind == nkExprColonExpr: result = genConstExpr(p, n.sons[1]) else: result = genConstExpr(p, n) -proc genConstSimpleList(p: BProc, n: PNode): PRope = +proc genConstSimpleList(p: BProc, n: PNode): Rope = var length = sonsLen(n) - result = toRope("{") + result = rope("{") for i in countup(0, length - 2): - appf(result, "$1,$n", [genNamedConstExpr(p, n.sons[i])]) - if length > 0: app(result, genNamedConstExpr(p, n.sons[length - 1])) - appf(result, "}$n") + addf(result, "$1,$n", [genNamedConstExpr(p, n.sons[i])]) + if length > 0: add(result, genNamedConstExpr(p, n.sons[length - 1])) + addf(result, "}$n", []) -proc genConstSeq(p: BProc, n: PNode, t: PType): PRope = - var data = ropef("{{$1, $1}", n.len.toRope) +proc genConstSeq(p: BProc, n: PNode, t: PType): Rope = + var data = "{{$1, $1}" % [n.len.rope] if n.len > 0: # array part needs extra curlies: - data.app(", {") + data.add(", {") for i in countup(0, n.len - 1): - if i > 0: data.appf(",$n") - data.app genConstExpr(p, n.sons[i]) - data.app("}") - data.app("}") + if i > 0: data.addf(",$n", []) + data.add genConstExpr(p, n.sons[i]) + data.add("}") + data.add("}") inc(gBackendId) - result = con("CNSTSEQ", gBackendId.toRope) + result = "CNSTSEQ" & gBackendId.rope appcg(p.module, cfsData, "NIM_CONST struct {$n" & " #TGenericSeq Sup;$n" & " $1 data[$2];$n" & "} $3 = $4;$n", [ - getTypeDesc(p.module, t.sons[0]), n.len.toRope, result, data]) + getTypeDesc(p.module, t.sons[0]), n.len.rope, result, data]) - result = ropef("(($1)&$2)", [getTypeDesc(p.module, t), result]) + result = "(($1)&$2)" % [getTypeDesc(p.module, t), result] -proc genConstExpr(p: BProc, n: PNode): PRope = +proc genConstExpr(p: BProc, n: PNode): Rope = case n.kind of nkHiddenStdConv, nkHiddenSubConv: result = genConstExpr(p, n.sons[1]) diff --git a/compiler/ccgmerge.nim b/compiler/ccgmerge.nim index 5057b9ff1..2a37257b6 100644 --- a/compiler/ccgmerge.nim +++ b/compiler/ccgmerge.nim @@ -45,29 +45,29 @@ const ] NimMergeEndMark = "/*\tNIM_merge_END:*/" -proc genSectionStart*(fs: TCFileSection): PRope = +proc genSectionStart*(fs: TCFileSection): Rope = if compilationCachePresent: - result = toRope(tnl) - app(result, "/*\t") - app(result, CFileSectionNames[fs]) - app(result, ":*/") - app(result, tnl) + result = rope(tnl) + add(result, "/*\t") + add(result, CFileSectionNames[fs]) + add(result, ":*/") + add(result, tnl) -proc genSectionEnd*(fs: TCFileSection): PRope = +proc genSectionEnd*(fs: TCFileSection): Rope = if compilationCachePresent: - result = toRope(NimMergeEndMark & tnl) + result = rope(NimMergeEndMark & tnl) -proc genSectionStart*(ps: TCProcSection): PRope = +proc genSectionStart*(ps: TCProcSection): Rope = if compilationCachePresent: - result = toRope(tnl) - app(result, "/*\t") - app(result, CProcSectionNames[ps]) - app(result, ":*/") - app(result, tnl) + result = rope(tnl) + add(result, "/*\t") + add(result, CProcSectionNames[ps]) + add(result, ":*/") + add(result, tnl) -proc genSectionEnd*(ps: TCProcSection): PRope = +proc genSectionEnd*(ps: TCProcSection): Rope = if compilationCachePresent: - result = toRope(NimMergeEndMark & tnl) + result = rope(NimMergeEndMark & tnl) proc writeTypeCache(a: TIdTable, s: var string) = var i = 0 @@ -79,7 +79,7 @@ proc writeTypeCache(a: TIdTable, s: var string) = s.add(' ') encodeVInt(id, s) s.add(':') - encodeStr(PRope(value).ropeToStr, s) + encodeStr($Rope(value), s) inc i s.add('}') @@ -95,7 +95,7 @@ proc writeIntSet(a: IntSet, s: var string) = inc i s.add('}') -proc genMergeInfo*(m: BModule): PRope = +proc genMergeInfo*(m: BModule): Rope = if optSymbolFiles notin gGlobalOptions: return nil var s = "/*\tNIM_merge_INFO:" s.add(tnl) @@ -111,7 +111,7 @@ proc genMergeInfo*(m: BModule): PRope = encodeVInt(ord(m.frameDeclared), s) s.add(tnl) s.add("*/") - result = s.toRope + result = s.rope 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 result = s == NimMergeEndMark.len -proc readVerbatimSection(L: var TBaseLexer): PRope = +proc readVerbatimSection(L: var TBaseLexer): Rope = var pos = L.bufpos var buf = L.buf var r = newStringOfCap(30_000) @@ -169,7 +169,7 @@ proc readVerbatimSection(L: var TBaseLexer): PRope = r.add(buf[pos]) inc pos L.bufpos = pos - result = r.toRope + result = r.rope proc readKey(L: var TBaseLexer, result: var string) = var pos = L.bufpos @@ -197,7 +197,7 @@ proc readTypeCache(L: var TBaseLexer, result: var TIdTable) = # XXX little hack: we create a "fake" type object with the correct Id # better would be to adapt the data structure to not even store the # object as key, but only the Id - idTablePut(result, newFakeType(key), value.toRope) + idTablePut(result, newFakeType(key), value.rope) inc L.bufpos proc readIntSet(L: var TBaseLexer, result: var IntSet) = @@ -280,11 +280,11 @@ proc readMergeSections(cfilename: string, m: var TMergeSections) = proc mergeRequired*(m: BModule): bool = for i in cfsHeaders..cfsProcs: if m.s[i] != nil: - #echo "not empty: ", i, " ", ropeToStr(m.s[i]) + #echo "not empty: ", i, " ", m.s[i] return true for i in low(TCProcSection)..high(TCProcSection): if m.initProc.s(i) != nil: - #echo "not empty: ", i, " ", ropeToStr(m.initProc.s[i]) + #echo "not empty: ", i, " ", m.initProc.s[i] return true proc mergeFiles*(cfilename: string, m: BModule) = @@ -293,6 +293,6 @@ proc mergeFiles*(cfilename: string, m: BModule) = readMergeSections(cfilename, old) # do the merge; old section before new section: for i in low(TCFileSection)..high(TCFileSection): - m.s[i] = con(old.f[i], m.s[i]) + m.s[i] = old.f[i] & m.s[i] for i in low(TCProcSection)..high(TCProcSection): - m.initProc.s(i) = con(old.p[i], m.initProc.s(i)) + m.initProc.s(i) = old.p[i] & m.initProc.s(i) diff --git a/compiler/ccgstmts.nim b/compiler/ccgstmts.nim index 0cf452985..6d29b1684 100644 --- a/compiler/ccgstmts.nim +++ b/compiler/ccgstmts.nim @@ -61,11 +61,10 @@ proc genVarTuple(p: BProc, n: PNode) = initLocalVar(p, v, immediateAsgn=isAssignedImmediately(n[L-1])) initLoc(field, locExpr, t.sons[i], tup.s) if t.kind == tyTuple: - field.r = ropef("$1.Field$2", [rdLoc(tup), toRope(i)]) + field.r = "$1.Field$2" % [rdLoc(tup), rope(i)] else: if t.n.sons[i].kind != nkSym: internalError(n.info, "genVarTuple") - field.r = ropef("$1.$2", - [rdLoc(tup), mangleRecFieldName(t.n.sons[i].sym, t)]) + field.r = "$1.$2" % [rdLoc(tup), mangleRecFieldName(t.n.sons[i].sym, t)] putLocIntoDest(p, v.loc, field) proc genDeref(p: BProc, e: PNode, d: var TLoc; enforceDeref=false) @@ -86,8 +85,8 @@ proc loadInto(p: BProc, le, ri: PNode, a: var TLoc) {.inline.} = else: expr(p, ri, a) -proc startBlock(p: BProc, start: TFormatStr = "{$n", - args: varargs[PRope]): int {.discardable.} = +proc startBlock(p: BProc, start: FormatStr = "{$n", + args: varargs[Rope]): int {.discardable.} = lineCg(p, cpsStmts, start, args) inc(p.labels) result = len(p.blocks) @@ -96,21 +95,21 @@ proc startBlock(p: BProc, start: TFormatStr = "{$n", p.blocks[result].nestedTryStmts = p.nestedTryStmts.len.int16 p.blocks[result].nestedExceptStmts = p.inExceptBlock.int16 -proc assignLabel(b: var TBlock): PRope {.inline.} = - b.label = con("LA", b.id.toRope) +proc assignLabel(b: var TBlock): Rope {.inline.} = + b.label = "LA" & b.id.rope result = b.label -proc blockBody(b: var TBlock): PRope = +proc blockBody(b: var TBlock): Rope = result = b.sections[cpsLocals] if b.frameLen > 0: - result.appf("F.len+=$1;$n", b.frameLen.toRope) - result.app(b.sections[cpsInit]) - result.app(b.sections[cpsStmts]) + result.addf("F.len+=$1;$n", [b.frameLen.rope]) + result.add(b.sections[cpsInit]) + result.add(b.sections[cpsStmts]) -proc endBlock(p: BProc, blockEnd: PRope) = +proc endBlock(p: BProc, blockEnd: Rope) = let topBlock = p.blocks.len-1 # the block is merged into the parent block - app(p.blocks[topBlock-1].sections[cpsStmts], p.blocks[topBlock].blockBody) + add(p.blocks[topBlock-1].sections[cpsStmts], p.blocks[topBlock].blockBody) setLen(p.blocks, topBlock) # this is done after the block is popped so $n is # properly indented when pretty printing is enabled @@ -124,7 +123,7 @@ proc endBlock(p: BProc) = ~"}$n" let frameLen = p.blocks[topBlock].frameLen if frameLen > 0: - blockEnd.appf("F.len-=$1;$n", frameLen.toRope) + blockEnd.addf("F.len-=$1;$n", [frameLen.rope]) endBlock(p, blockEnd) proc genSimpleBlock(p: BProc, stmts: PNode) {.inline.} = @@ -145,7 +144,7 @@ template preserveBreakIdx(body: stmt): stmt {.immediate.} = proc genState(p: BProc, n: PNode) = internalAssert n.len == 1 and n.sons[0].kind == nkIntLit let idx = n.sons[0].intVal - linefmt(p, cpsStmts, "STATE$1: ;$n", idx.toRope) + linefmt(p, cpsStmts, "STATE$1: ;$n", idx.rope) proc genGotoState(p: BProc, n: PNode) = # we resist the temptation to translate it into duff's device as it later @@ -159,7 +158,7 @@ proc genGotoState(p: BProc, n: PNode) = p.beforeRetNeeded = true lineF(p, cpsStmts, "case -1: goto BeforeRet;$n", []) for i in 0 .. lastOrd(n.sons[0].typ): - lineF(p, cpsStmts, "case $1: goto STATE$1;$n", [toRope(i)]) + lineF(p, cpsStmts, "case $1: goto STATE$1;$n", [rope(i)]) lineF(p, cpsStmts, "}$n", []) proc genBreakState(p: BProc, n: PNode) = @@ -176,9 +175,18 @@ proc genBreakState(p: BProc, n: PNode) = proc genVarPrototypeAux(m: BModule, sym: PSym) +proc genGotoVar(p: BProc; value: PNode) = + if value.kind notin {nkCharLit..nkUInt64Lit}: + localError(value.info, "'goto' target must be a literal value") + else: + lineF(p, cpsStmts, "goto NIMSTATE_$#;$n", [value.intVal.rope]) + proc genSingleVar(p: BProc, a: PNode) = var v = a.sons[0].sym - if sfCompileTime in v.flags: return + if {sfCompileTime, sfGoto} * v.flags != {}: + # translate 'var state {.goto.} = X' into 'goto LX': + if sfGoto in v.flags: genGotoVar(p, a.sons[2]) + return var targetProc = p if sfGlobal in v.flags: if v.flags * {sfImportc, sfExportc} == {sfImportc} and @@ -214,17 +222,17 @@ proc genSingleVar(p: BProc, a: PNode) = var tmp: TLoc if value.kind in nkCallKinds and value[0].kind == nkSym and sfConstructor in value[0].sym.flags: - var params: PRope + var params: Rope let typ = skipTypes(value.sons[0].typ, abstractInst) assert(typ.kind == tyProc) for i in 1.. <value.len: - if params != nil: params.app(~", ") + if params != nil: params.add(~", ") assert(sonsLen(typ) == sonsLen(typ.n)) - app(params, genOtherArg(p, value, i, typ)) - lineF(p, cpsStmts, "$#($#);$n", decl, params) + add(params, genOtherArg(p, value, i, typ)) + lineF(p, cpsStmts, "$#($#);$n", [decl, params]) else: initLocExprSingleUse(p, value, tmp) - lineF(p, cpsStmts, "$# = $#;$n", decl, tmp.rdLoc) + lineF(p, cpsStmts, "$# = $#;$n", [decl, tmp.rdLoc]) return assignLocalVar(p, v) initLocalVar(p, v, imm) @@ -298,9 +306,9 @@ proc genIf(p: BProc, n: PNode, d: var TLoc) = when not newScopeForIf: startBlock(p) if p.module.compileToCpp: # avoid "jump to label crosses initialization" error: - app(p.s(cpsStmts), "{") + add(p.s(cpsStmts), "{") expr(p, it.sons[1], d) - app(p.s(cpsStmts), "}") + add(p.s(cpsStmts), "}") else: expr(p, it.sons[1], d) endBlock(p) @@ -366,6 +374,19 @@ proc genReturnStmt(p: BProc, t: PNode) = linefmt(p, cpsStmts, "if ($1.status != 0) #popCurrentException();$n", safePoint) lineF(p, cpsStmts, "goto BeforeRet;$n", []) +proc genGotoForCase(p: BProc; caseStmt: PNode) = + for i in 1 .. <caseStmt.len: + startBlock(p) + let it = caseStmt.sons[i] + for j in 0 .. it.len-2: + if it.sons[j].kind == nkRange: + localError(it.info, "range notation not available for computed goto") + return + let val = getOrdValue(it.sons[j]) + lineF(p, cpsStmts, "NIMSTATE_$#:$n", [val.rope]) + genStmts(p, it.lastSon) + endBlock(p) + proc genComputedGoto(p: BProc; n: PNode) = # first pass: Generate array of computed labels: var casePos = -1 @@ -389,11 +410,11 @@ proc genComputedGoto(p: BProc; n: PNode) = localError(n.info, "no case statement found for computed goto"); return var id = p.labels+1 inc p.labels, arraySize+1 - let tmp = ropef("TMP$1", id.toRope) - var gotoArray = ropef("static void* $#[$#] = {", tmp, arraySize.toRope) + let tmp = "TMP$1" % [id.rope] + var gotoArray = "static void* $#[$#] = {" % [tmp, arraySize.rope] for i in 1..arraySize-1: - gotoArray.appf("&&TMP$#, ", (id+i).toRope) - gotoArray.appf("&&TMP$#};$n", (id+arraySize).toRope) + gotoArray.addf("&&TMP$#, ", [(id+i).rope]) + gotoArray.addf("&&TMP$#};$n", [(id+arraySize).rope]) line(p, cpsLocals, gotoArray) let topBlock = p.blocks.len-1 @@ -407,13 +428,13 @@ proc genComputedGoto(p: BProc; n: PNode) = for j in 0 .. casePos-1: genStmts(p, n.sons[j]) let tailA = p.blocks[topBlock].sections[cpsStmts] - p.blocks[topBlock].sections[cpsStmts] = oldBody.con(tailA) + p.blocks[topBlock].sections[cpsStmts] = oldBody & tailA let caseStmt = n.sons[casePos] var a: TLoc initLocExpr(p, caseStmt.sons[0], a) # first goto: - lineF(p, cpsStmts, "goto *$#[$#];$n", tmp, a.rdLoc) + lineF(p, cpsStmts, "goto *$#[$#];$n", [tmp, a.rdLoc]) for i in 1 .. <caseStmt.len: startBlock(p) @@ -423,16 +444,16 @@ proc genComputedGoto(p: BProc; n: PNode) = localError(it.info, "range notation not available for computed goto") return let val = getOrdValue(it.sons[j]) - lineF(p, cpsStmts, "TMP$#:$n", intLiteral(val+id+1)) + lineF(p, cpsStmts, "TMP$#:$n", [intLiteral(val+id+1)]) genStmts(p, it.lastSon) #for j in casePos+1 .. <n.len: genStmts(p, n.sons[j]) # tailB #for j in 0 .. casePos-1: genStmts(p, n.sons[j]) # tailA - app(p.s(cpsStmts), tailB) - app(p.s(cpsStmts), tailA) + add(p.s(cpsStmts), tailB) + add(p.s(cpsStmts), tailA) var a: TLoc initLocExpr(p, caseStmt.sons[0], a) - lineF(p, cpsStmts, "goto *$#[$#];$n", tmp, a.rdLoc) + lineF(p, cpsStmts, "goto *$#[$#];$n", [tmp, a.rdLoc]) endBlock(p) proc genWhileStmt(p: BProc, t: PNode) = @@ -498,9 +519,9 @@ proc genParForStmt(p: BProc, t: PNode) = lineF(p, cpsStmts, "#pragma omp parallel for $4$n" & "for ($1 = $2; $1 <= $3; ++$1)", - forLoopVar.loc.rdLoc, + [forLoopVar.loc.rdLoc, rangeA.rdLoc, rangeB.rdLoc, - call.sons[3].getStr.toRope) + call.sons[3].getStr.rope]) p.breakIdx = startBlock(p) p.blocks[p.breakIdx].isLoop = true @@ -530,12 +551,7 @@ proc genBreakStmt(p: BProc, t: PNode) = lineF(p, cpsStmts, "goto $1;$n", [label]) proc getRaiseFrmt(p: BProc): string = - if p.module.compileToCpp: - result = "throw NimException($1, $2);$n" - elif getCompilerProc("Exception") != nil: - result = "#raiseException((#Exception*)$1, $2);$n" - else: - result = "#raiseException((#E_Base*)$1, $2);$n" + result = "#raiseException((#Exception*)$1, $2);$n" proc genRaiseStmt(p: BProc, t: PNode) = if p.inExceptBlock > 0: @@ -560,7 +576,7 @@ proc genRaiseStmt(p: BProc, t: PNode) = linefmt(p, cpsStmts, "#reraiseException();$n") proc genCaseGenericBranch(p: BProc, b: PNode, e: TLoc, - rangeFormat, eqFormat: TFormatStr, labl: TLabel) = + rangeFormat, eqFormat: FormatStr, labl: TLabel) = var x, y: TLoc var length = sonsLen(b) @@ -578,7 +594,7 @@ proc genCaseSecondPass(p: BProc, t: PNode, d: var TLoc, labId, until: int): TLabel = var lend = getLabel(p) for i in 1..until: - lineF(p, cpsStmts, "LA$1: ;$n", [toRope(labId + i)]) + lineF(p, cpsStmts, "LA$1: ;$n", [rope(labId + i)]) if t.sons[i].kind == nkOfBranch: var length = sonsLen(t.sons[i]) exprBlock(p, t.sons[i].sons[length - 1], d) @@ -588,7 +604,7 @@ proc genCaseSecondPass(p: BProc, t: PNode, d: var TLoc, result = lend proc genIfForCaseUntil(p: BProc, t: PNode, d: var TLoc, - rangeFormat, eqFormat: TFormatStr, + rangeFormat, eqFormat: FormatStr, until: int, a: TLoc): TLabel = # generate a C-if statement for a Nim case statement var labId = p.labels @@ -596,27 +612,27 @@ proc genIfForCaseUntil(p: BProc, t: PNode, d: var TLoc, inc(p.labels) if t.sons[i].kind == nkOfBranch: # else statement genCaseGenericBranch(p, t.sons[i], a, rangeFormat, eqFormat, - con("LA", toRope(p.labels))) + "LA" & rope(p.labels)) else: - lineF(p, cpsStmts, "goto LA$1;$n", [toRope(p.labels)]) + lineF(p, cpsStmts, "goto LA$1;$n", [rope(p.labels)]) if until < t.len-1: inc(p.labels) var gotoTarget = p.labels - lineF(p, cpsStmts, "goto LA$1;$n", [toRope(gotoTarget)]) + lineF(p, cpsStmts, "goto LA$1;$n", [rope(gotoTarget)]) result = genCaseSecondPass(p, t, d, labId, until) - lineF(p, cpsStmts, "LA$1: ;$n", [toRope(gotoTarget)]) + lineF(p, cpsStmts, "LA$1: ;$n", [rope(gotoTarget)]) else: result = genCaseSecondPass(p, t, d, labId, until) proc genCaseGeneric(p: BProc, t: PNode, d: var TLoc, - rangeFormat, eqFormat: TFormatStr) = + rangeFormat, eqFormat: FormatStr) = var a: TLoc initLocExpr(p, t.sons[0], a) var lend = genIfForCaseUntil(p, t, d, rangeFormat, eqFormat, sonsLen(t)-1, a) fixLabel(p, lend) proc genCaseStringBranch(p: BProc, b: PNode, e: TLoc, labl: TLabel, - branches: var openArray[PRope]) = + branches: var openArray[Rope]) = var x: TLoc var length = sonsLen(b) for i in countup(0, length - 2): @@ -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 strings > stringCaseThreshold: var bitMask = math.nextPowerOfTwo(strings) - 1 - var branches: seq[PRope] + var branches: seq[Rope] newSeq(branches, bitMask + 1) var a: TLoc initLocExpr(p, t.sons[0], a) # fist pass: gnerate ifs+goto: @@ -642,21 +658,21 @@ proc genStringCase(p: BProc, t: PNode, d: var TLoc) = for i in countup(1, sonsLen(t) - 1): inc(p.labels) if t.sons[i].kind == nkOfBranch: - genCaseStringBranch(p, t.sons[i], a, con("LA", toRope(p.labels)), + genCaseStringBranch(p, t.sons[i], a, "LA" & rope(p.labels), branches) else: # else statement: nothing to do yet # but we reserved a label, which we use later discard linefmt(p, cpsStmts, "switch (#hashString($1) & $2) {$n", - rdLoc(a), toRope(bitMask)) + rdLoc(a), rope(bitMask)) for j in countup(0, high(branches)): if branches[j] != nil: lineF(p, cpsStmts, "case $1: $n$2break;$n", [intLiteral(j), branches[j]]) - lineF(p, cpsStmts, "}$n") # else statement: + lineF(p, cpsStmts, "}$n", []) # else statement: if t.sons[sonsLen(t)-1].kind != nkOfBranch: - lineF(p, cpsStmts, "goto LA$1;$n", [toRope(p.labels)]) + lineF(p, cpsStmts, "goto LA$1;$n", [rope(p.labels)]) # third pass: generate statements var lend = genCaseSecondPass(p, t, d, labId, sonsLen(t)-1) fixLabel(p, lend) @@ -718,13 +734,13 @@ proc genOrdinalCase(p: BProc, n: PNode, d: var TLoc) = genCaseRange(p, branch) else: # else part of case statement: - lineF(p, cpsStmts, "default:$n") + lineF(p, cpsStmts, "default:$n", []) hasDefault = true exprBlock(p, branch.lastSon, d) - lineF(p, cpsStmts, "break;$n") + lineF(p, cpsStmts, "break;$n", []) if (hasAssume in CC[cCompiler].props) and not hasDefault: - lineF(p, cpsStmts, "default: __assume(0);$n") - lineF(p, cpsStmts, "}$n") + lineF(p, cpsStmts, "default: __assume(0);$n", []) + lineF(p, cpsStmts, "}$n", []) if lend != nil: fixLabel(p, lend) proc genCase(p: BProc, t: PNode, d: var TLoc) = @@ -738,7 +754,10 @@ proc genCase(p: BProc, t: PNode, d: var TLoc) = genCaseGeneric(p, t, d, "if ($1 >= $2 && $1 <= $3) goto $4;$n", "if ($1 == $2) goto $3;$n") else: - genOrdinalCase(p, t, d) + if t.sons[0].kind == nkSym and sfGoto in t.sons[0].sym.flags: + genGotoForCase(p, t) + else: + genOrdinalCase(p, t, d) proc hasGeneralExceptSection(t: PNode): bool = var length = sonsLen(t) @@ -773,11 +792,8 @@ proc genTryCpp(p: BProc, t: PNode, d: var TLoc) = # finallyPart(); if not isEmptyType(t.typ) and d.k == locNone: getTemp(p, t.typ, d) - var - exc: PRope - i, length, blen: int genLineDir(p, t) - exc = getTempName() + let exc = getTempName() if getCompilerProc("Exception") != nil: discard cgsym(p.module, "Exception") else: @@ -785,36 +801,42 @@ proc genTryCpp(p: BProc, t: PNode, d: var TLoc) = add(p.nestedTryStmts, t) startBlock(p, "try {$n") expr(p, t.sons[0], d) - length = sonsLen(t) + let length = sonsLen(t) endBlock(p, ropecg(p.module, "} catch (NimException& $1) {$n", [exc])) if optStackTrace in p.options: - linefmt(p, cpsStmts, "#setFrame((TFrame*)&F);$n") + linefmt(p, cpsStmts, "#setFrame((TFrame*)&FR);$n") inc p.inExceptBlock - i = 1 + var i = 1 var catchAllPresent = false while (i < length) and (t.sons[i].kind == nkExceptBranch): - blen = sonsLen(t.sons[i]) - if i > 1: appf(p.s(cpsStmts), "else ") + let blen = sonsLen(t.sons[i]) + if i > 1: addf(p.s(cpsStmts), "else ", []) if blen == 1: # general except section: catchAllPresent = true - exprBlock(p, t.sons[i].sons[0], d) + startBlock(p) + expr(p, t.sons[i].sons[0], d) + linefmt(p, cpsStmts, "#popCurrentException();$n") + endBlock(p) else: - var orExpr: PRope = nil + var orExpr: Rope = nil for j in countup(0, blen - 2): assert(t.sons[i].sons[j].kind == nkType) - if orExpr != nil: app(orExpr, "||") + if orExpr != nil: add(orExpr, "||") appcg(p.module, orExpr, "#isObj($1.exp->m_type, $2)", [exc, genTypeInfo(p.module, t.sons[i].sons[j].typ)]) lineF(p, cpsStmts, "if ($1) ", [orExpr]) - exprBlock(p, t.sons[i].sons[blen-1], d) + startBlock(p) + expr(p, t.sons[i].sons[blen-1], d) + linefmt(p, cpsStmts, "#popCurrentException();$n") + endBlock(p) inc(i) # reraise the exception if there was no catch all # and none of the handlers matched if not catchAllPresent: - if i > 1: lineF(p, cpsStmts, "else ") + if i > 1: lineF(p, cpsStmts, "else ", []) startBlock(p) var finallyBlock = t.lastSon if finallyBlock.kind == nkFinally: @@ -824,7 +846,7 @@ proc genTryCpp(p: BProc, t: PNode, d: var TLoc) = line(p, cpsStmts, ~"throw;$n") endBlock(p) - lineF(p, cpsStmts, "}$n") # end of catch block + lineF(p, cpsStmts, "}$n", []) # end of catch block dec p.inExceptBlock discard pop(p.nestedTryStmts) @@ -888,24 +910,24 @@ proc genTry(p: BProc, t: PNode, d: var TLoc) = startBlock(p, "else {$n") linefmt(p, cpsStmts, "#popSafePoint();$n") if optStackTrace in p.options: - linefmt(p, cpsStmts, "#setFrame((TFrame*)&F);$n") + linefmt(p, cpsStmts, "#setFrame((TFrame*)&FR);$n") inc p.inExceptBlock var i = 1 while (i < length) and (t.sons[i].kind == nkExceptBranch): var blen = sonsLen(t.sons[i]) if blen == 1: # general except section: - if i > 1: lineF(p, cpsStmts, "else") + if i > 1: lineF(p, cpsStmts, "else", []) startBlock(p) linefmt(p, cpsStmts, "$1.status = 0;$n", safePoint) expr(p, t.sons[i].sons[0], d) linefmt(p, cpsStmts, "#popCurrentException();$n") endBlock(p) else: - var orExpr: PRope = nil + var orExpr: Rope = nil for j in countup(0, blen - 2): assert(t.sons[i].sons[j].kind == nkType) - if orExpr != nil: app(orExpr, "||") + if orExpr != nil: add(orExpr, "||") appcg(p.module, orExpr, "#isObj(#getCurrentException()->Sup.m_type, $1)", [genTypeInfo(p.module, t.sons[i].sons[j].typ)]) @@ -925,7 +947,7 @@ proc genTry(p: BProc, t: PNode, d: var TLoc) = discard pop(p.finallySafePoints) linefmt(p, cpsStmts, "if ($1.status != 0) #reraiseException();$n", safePoint) -proc genAsmOrEmitStmt(p: BProc, t: PNode, isAsmStmt=false): PRope = +proc genAsmOrEmitStmt(p: BProc, t: PNode, isAsmStmt=false): Rope = var res = "" for i in countup(0, sonsLen(t) - 1): case t.sons[i].kind @@ -936,7 +958,7 @@ proc genAsmOrEmitStmt(p: BProc, t: PNode, isAsmStmt=false): PRope = if sym.kind in {skProc, skIterator, skClosureIterator, skMethod}: var a: TLoc initLocExpr(p, t.sons[i], a) - res.add(rdLoc(a).ropeToStr) + res.add($rdLoc(a)) else: var r = sym.loc.r if r == nil: @@ -944,7 +966,7 @@ proc genAsmOrEmitStmt(p: BProc, t: PNode, isAsmStmt=false): PRope = # it doesn't matter much: r = mangleName(sym) sym.loc.r = r # but be consequent! - res.add(r.ropeToStr) + res.add($r) else: internalError(t.sons[i].info, "genAsmOrEmitStmt()") if isAsmStmt and hasGnuAsm in CC[cCompiler].props: @@ -954,15 +976,15 @@ proc genAsmOrEmitStmt(p: BProc, t: PNode, isAsmStmt=false): PRope = if x[j] in {'"', ':'}: # don't modify the line if already in quotes or # some clobber register list: - app(result, x); app(result, tnl) + add(result, x); add(result, tnl) elif x[j] != '\0': # ignore empty lines - app(result, "\"") - app(result, x) - app(result, "\\n\"\n") + add(result, "\"") + add(result, x) + add(result, "\\n\"\n") else: res.add(tnl) - result = res.toRope + result = res.rope proc genAsmStmt(p: BProc, t: PNode) = assert(t.kind == nkAsmStmt) @@ -973,23 +995,30 @@ proc genAsmStmt(p: BProc, t: PNode) = # work: if p.prc == nil: # top level asm statement? - appf(p.module.s[cfsProcHeaders], CC[cCompiler].asmStmtFrmt, [s]) + addf(p.module.s[cfsProcHeaders], CC[cCompiler].asmStmtFrmt, [s]) else: lineF(p, cpsStmts, CC[cCompiler].asmStmtFrmt, [s]) +proc determineSection(n: PNode): TCFileSection = + result = cfsProcHeaders + if n.len >= 1 and n.sons[0].kind in {nkStrLit..nkTripleStrLit}: + if n.sons[0].strVal.startsWith("/*TYPESECTION*/"): result = cfsTypes + elif n.sons[0].strVal.startsWith("/*VARSECTION*/"): result = cfsVars + proc genEmit(p: BProc, t: PNode) = var s = genAsmOrEmitStmt(p, t.sons[1]) if p.prc == nil: # top level emit pragma? - genCLineDir(p.module.s[cfsProcHeaders], t.info) - app(p.module.s[cfsProcHeaders], s) + let section = determineSection(t[1]) + genCLineDir(p.module.s[section], t.info) + add(p.module.s[section], s) else: genLineDir(p, t) line(p, cpsStmts, s) var breakPointId: int = 0 - gBreakpoints: PRope # later the breakpoints are inserted into the main proc + gBreakpoints: Rope # later the breakpoints are inserted into the main proc proc genBreakPoint(p: BProc, t: PNode) = var name: string @@ -1003,7 +1032,7 @@ proc genBreakPoint(p: BProc, t: PNode) = genLineDir(p, t) # BUGFIX appcg(p.module, gBreakpoints, "#dbgRegisterBreakpoint($1, (NCSTRING)$2, (NCSTRING)$3);$n", [ - toRope(toLinenumber(t.info)), makeCString(toFilename(t.info)), + rope(toLinenumber(t.info)), makeCString(toFilename(t.info)), makeCString(name)]) proc genWatchpoint(p: BProc, n: PNode) = @@ -1066,7 +1095,9 @@ proc asgnFieldDiscriminant(p: BProc, e: PNode) = proc genAsgn(p: BProc, e: PNode, fastAsgn: bool) = genLineDir(p, e) - if not fieldDiscriminantCheckNeeded(p, e): + if e.sons[0].kind == nkSym and sfGoto in e.sons[0].sym.flags: + genGotoVar(p, e.sons[1]) + elif not fieldDiscriminantCheckNeeded(p, e): var a: TLoc initLocExpr(p, e.sons[0], a) if fastAsgn: incl(a.flags, lfNoDeepCopy) diff --git a/compiler/ccgthreadvars.nim b/compiler/ccgthreadvars.nim index c24dd5c41..d741c47a9 100644 --- a/compiler/ccgthreadvars.nim +++ b/compiler/ccgthreadvars.nim @@ -7,7 +7,7 @@ # distribution, for details about the copyright. # -## Thread var support for crappy architectures that lack native support for +## Thread var support for crappy architectures that lack native support for ## thread local storage. (**Thank you Mac OS X!**) # included from cgen.nim @@ -19,12 +19,12 @@ proc accessThreadLocalVar(p: BProc, s: PSym) = if emulatedThreadVars() and not p.threadVarAccessed: p.threadVarAccessed = true p.module.usesThreadVars = true - appf(p.procSec(cpsLocals), "\tNimThreadVars* NimTV;$n") - app(p.procSec(cpsInit), + addf(p.procSec(cpsLocals), "\tNimThreadVars* NimTV;$n", []) + add(p.procSec(cpsInit), ropecg(p.module, "\tNimTV = (NimThreadVars*) #GetThreadLocalVars();$n")) - + var - nimtv: PRope # nimrod thread vars; the struct body + nimtv: Rope # nimrod thread vars; the struct body nimtvDeps: seq[PType] = @[] # type deps: every module needs whole struct nimtvDeclared = initIntSet() # so that every var/field exists only once # in the struct @@ -43,23 +43,23 @@ proc declareThreadVar(m: BModule, s: PSym, isExtern: bool) = # allocator for it :-( if not containsOrIncl(nimtvDeclared, s.id): nimtvDeps.add(s.loc.t) - appf(nimtv, "$1 $2;$n", [getTypeDesc(m, s.loc.t), s.loc.r]) + addf(nimtv, "$1 $2;$n", [getTypeDesc(m, s.loc.t), s.loc.r]) else: - if isExtern: app(m.s[cfsVars], "extern ") - if optThreads in gGlobalOptions: app(m.s[cfsVars], "NIM_THREADVAR ") - app(m.s[cfsVars], getTypeDesc(m, s.loc.t)) - appf(m.s[cfsVars], " $1;$n", [s.loc.r]) - + if isExtern: add(m.s[cfsVars], "extern ") + if optThreads in gGlobalOptions: add(m.s[cfsVars], "NIM_THREADVAR ") + add(m.s[cfsVars], getTypeDesc(m, s.loc.t)) + addf(m.s[cfsVars], " $1;$n", [s.loc.r]) + proc generateThreadLocalStorage(m: BModule) = if nimtv != nil and (m.usesThreadVars or sfMainModule in m.module.flags): for t in items(nimtvDeps): discard getTypeDesc(m, t) - appf(m.s[cfsSeqTypes], "typedef struct {$1} NimThreadVars;$n", [nimtv]) + addf(m.s[cfsSeqTypes], "typedef struct {$1} NimThreadVars;$n", [nimtv]) proc generateThreadVarsSize(m: BModule) = if nimtv != nil: let externc = if gCmd != cmdCompileToCpp and sfCompileToCpp in m.module.flags: "extern \"C\"" else: "" - appf(m.s[cfsProcs], + addf(m.s[cfsProcs], "$#NI NimThreadVarsSize(){return (NI)sizeof(NimThreadVars);}$n", - [externc.toRope]) + [externc.rope]) diff --git a/compiler/ccgtrav.nim b/compiler/ccgtrav.nim index 8bb820283..5f59702e5 100644 --- a/compiler/ccgtrav.nim +++ b/compiler/ccgtrav.nim @@ -17,11 +17,11 @@ type p: BProc visitorFrmt: string -proc genTraverseProc(c: var TTraversalClosure, accessor: PRope, typ: PType) +proc genTraverseProc(c: var TTraversalClosure, accessor: Rope, typ: PType) proc genCaseRange(p: BProc, branch: PNode) proc getTemp(p: BProc, t: PType, result: var TLoc; needsInit=false) -proc genTraverseProc(c: var TTraversalClosure, accessor: PRope, n: PNode) = +proc genTraverseProc(c: var TTraversalClosure, accessor: Rope, n: PNode) = if n == nil: return case n.kind of nkRecList: @@ -31,31 +31,31 @@ proc genTraverseProc(c: var TTraversalClosure, accessor: PRope, n: PNode) = if (n.sons[0].kind != nkSym): internalError(n.info, "genTraverseProc") var p = c.p let disc = n.sons[0].sym - lineF(p, cpsStmts, "switch ($1.$2) {$n", accessor, disc.loc.r) + lineF(p, cpsStmts, "switch ($1.$2) {$n", [accessor, disc.loc.r]) for i in countup(1, sonsLen(n) - 1): let branch = n.sons[i] assert branch.kind in {nkOfBranch, nkElse} if branch.kind == nkOfBranch: genCaseRange(c.p, branch) else: - lineF(p, cpsStmts, "default:$n") + lineF(p, cpsStmts, "default:$n", []) genTraverseProc(c, accessor, lastSon(branch)) - lineF(p, cpsStmts, "break;$n") - lineF(p, cpsStmts, "} $n") + lineF(p, cpsStmts, "break;$n", []) + lineF(p, cpsStmts, "} $n", []) of nkSym: let field = n.sym if field.loc.t == nil: internalError(n.info, "genTraverseProc()") - genTraverseProc(c, ropef("$1.$2", accessor, field.loc.r), field.loc.t) + genTraverseProc(c, "$1.$2" % [accessor, field.loc.r], field.loc.t) else: internalError(n.info, "genTraverseProc()") -proc parentObj(accessor: PRope; m: BModule): PRope {.inline.} = +proc parentObj(accessor: Rope; m: BModule): Rope {.inline.} = if not m.compileToCpp: - result = ropef("$1.Sup", accessor) + result = "$1.Sup" % [accessor] else: result = accessor -proc genTraverseProc(c: var TTraversalClosure, accessor: PRope, typ: PType) = +proc genTraverseProc(c: var TTraversalClosure, accessor: Rope, typ: PType) = if typ == nil: return var p = c.p case typ.kind @@ -66,9 +66,9 @@ proc genTraverseProc(c: var TTraversalClosure, accessor: PRope, typ: PType) = var i: TLoc getTemp(p, getSysType(tyInt), i) linefmt(p, cpsStmts, "for ($1 = 0; $1 < $2; $1++) {$n", - i.r, arraySize.toRope) + i.r, arraySize.rope) genTraverseProc(c, rfmt(nil, "$1[$2]", accessor, i.r), typ.sons[1]) - lineF(p, cpsStmts, "}$n") + lineF(p, cpsStmts, "}$n", []) of tyObject: for i in countup(0, sonsLen(typ) - 1): genTraverseProc(c, accessor.parentObj(c.p.module), typ.sons[i]) @@ -76,7 +76,7 @@ proc genTraverseProc(c: var TTraversalClosure, accessor: PRope, typ: PType) = of tyTuple: let typ = getUniqueType(typ) for i in countup(0, sonsLen(typ) - 1): - genTraverseProc(c, rfmt(nil, "$1.Field$2", accessor, i.toRope), typ.sons[i]) + genTraverseProc(c, rfmt(nil, "$1.Field$2", accessor, i.rope), typ.sons[i]) of tyRef, tyString, tySequence: lineCg(p, cpsStmts, c.visitorFrmt, accessor) of tyProc: @@ -85,67 +85,67 @@ proc genTraverseProc(c: var TTraversalClosure, accessor: PRope, typ: PType) = else: discard -proc genTraverseProcSeq(c: var TTraversalClosure, accessor: PRope, typ: PType) = +proc genTraverseProcSeq(c: var TTraversalClosure, accessor: Rope, typ: PType) = var p = c.p - assert typ.kind == tySequence + assert typ.kind == tySequence var i: TLoc getTemp(p, getSysType(tyInt), i) lineF(p, cpsStmts, "for ($1 = 0; $1 < $2->$3; $1++) {$n", - i.r, accessor, toRope(if c.p.module.compileToCpp: "len" else: "Sup.len")) - genTraverseProc(c, ropef("$1->data[$2]", accessor, i.r), typ.sons[0]) - lineF(p, cpsStmts, "}$n") - -proc genTraverseProc(m: BModule, typ: PType, reason: TTypeInfoReason): PRope = + [i.r, accessor, rope(if c.p.module.compileToCpp: "len" else: "Sup.len")]) + genTraverseProc(c, "$1->data[$2]" % [accessor, i.r], typ.sons[0]) + lineF(p, cpsStmts, "}$n", []) + +proc genTraverseProc(m: BModule, typ: PType, reason: TTypeInfoReason): Rope = var c: TTraversalClosure var p = newProc(nil, m) result = getGlobalTempName() - + case reason of tiNew: c.visitorFrmt = "#nimGCvisit((void*)$1, op);$n" else: assert false - - let header = ropef("N_NIMCALL(void, $1)(void* p, NI op)", result) - + + let header = "N_NIMCALL(void, $1)(void* p, NI op)" % [result] + let t = getTypeDesc(m, typ) - lineF(p, cpsLocals, "$1 a;$n", t) - lineF(p, cpsInit, "a = ($1)p;$n", t) - + lineF(p, cpsLocals, "$1 a;$n", [t]) + lineF(p, cpsInit, "a = ($1)p;$n", [t]) + c.p = p assert typ.kind != tyTypeDesc if typ.kind == tySequence: - genTraverseProcSeq(c, "a".toRope, typ) + genTraverseProcSeq(c, "a".rope, typ) else: if skipTypes(typ.sons[0], typedescInst).kind in {tyArrayConstr, tyArray}: # C's arrays are broken beyond repair: - genTraverseProc(c, "a".toRope, typ.sons[0]) + genTraverseProc(c, "a".rope, typ.sons[0]) else: - genTraverseProc(c, "(*a)".toRope, typ.sons[0]) - - let generatedProc = ropef("$1 {$n$2$3$4}$n", - [header, p.s(cpsLocals), p.s(cpsInit), p.s(cpsStmts)]) - - m.s[cfsProcHeaders].appf("$1;$n", header) - m.s[cfsProcs].app(generatedProc) - -proc genTraverseProcForGlobal(m: BModule, s: PSym): PRope = + genTraverseProc(c, "(*a)".rope, typ.sons[0]) + + let generatedProc = "$1 {$n$2$3$4}$n" % + [header, p.s(cpsLocals), p.s(cpsInit), p.s(cpsStmts)] + + m.s[cfsProcHeaders].addf("$1;$n", [header]) + m.s[cfsProcs].add(generatedProc) + +proc genTraverseProcForGlobal(m: BModule, s: PSym): Rope = discard genTypeInfo(m, s.loc.t) - + var c: TTraversalClosure var p = newProc(nil, m) var sLoc = s.loc.r result = getGlobalTempName() - + if sfThread in s.flags and emulatedThreadVars(): accessThreadLocalVar(p, s) - sLoc = con("NimTV->", sLoc) - + sLoc = "NimTV->" & sLoc + c.visitorFrmt = "#nimGCvisit((void*)$1, 0);$n" c.p = p - let header = ropef("N_NIMCALL(void, $1)()", result) + let header = "N_NIMCALL(void, $1)()" % [result] genTraverseProc(c, sLoc, s.loc.t) - - let generatedProc = ropef("$1 {$n$2$3$4}$n", - [header, p.s(cpsLocals), p.s(cpsInit), p.s(cpsStmts)]) - - m.s[cfsProcHeaders].appf("$1;$n", header) - m.s[cfsProcs].app(generatedProc) + + let generatedProc = "$1 {$n$2$3$4}$n" % + [header, p.s(cpsLocals), p.s(cpsInit), p.s(cpsStmts)] + + m.s[cfsProcHeaders].addf("$1;$n", [header]) + m.s[cfsProcs].add(generatedProc) diff --git a/compiler/ccgtypes.nim b/compiler/ccgtypes.nim index 086aeb966..3742fd2fd 100644 --- a/compiler/ccgtypes.nim +++ b/compiler/ccgtypes.nim @@ -11,21 +11,22 @@ # ------------------------- 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 = - # nimrod and C++ share some keywords - # it's more efficient to test the whole nimrod keywords range + # Nim and C++ share some keywords + # it's more efficient to test the whole Nim keywords range case w.id of ccgKeywordsLow..ccgKeywordsHigh, nimKeywordsLow..nimKeywordsHigh, ord(wInline): return true 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 if result == nil: when oKeepVariableNames: @@ -77,27 +78,27 @@ proc mangleName(s: PSym): PRope = # These are not properly scoped now - we need to add blocks # around for loops in transf if keepOrigName: - result = s.name.s.mangle.newRope + result = s.name.s.mangle.rope else: - app(result, newRope(mangle(s.name.s))) - app(result, ~"_") - app(result, toRope(s.id)) + add(result, rope(mangle(s.name.s))) + add(result, ~"_") + add(result, rope(s.id)) else: - app(result, newRope(mangle(s.name.s))) - app(result, ~"_") - app(result, toRope(s.id)) + add(result, rope(mangle(s.name.s))) + add(result, ~"_") + add(result, rope(s.id)) s.loc.r = result -proc typeName(typ: PType): PRope = - result = if typ.sym != nil: typ.sym.name.s.mangle.toRope +proc typeName(typ: PType): Rope = + result = if typ.sym != nil: typ.sym.name.s.mangle.rope else: ~"TY" -proc getTypeName(typ: PType): PRope = +proc getTypeName(typ: PType): Rope = if typ.sym != nil and {sfImportc, sfExportc} * typ.sym.flags != {}: result = typ.sym.loc.r else: 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 if result == nil: internalError("getTypeName: " & $typ.kind) @@ -110,7 +111,7 @@ proc mapSetType(typ: PType): TCTypeKind = else: result = ctArray proc mapType(typ: PType): TCTypeKind = - ## Maps a nimrod type to a C type + ## Maps a Nim type to a C type case typ.kind of tyNone, tyStmt: result = ctVoid of tyBool: result = ctBool @@ -156,7 +157,7 @@ proc isImportedType(t: PType): bool = proc isImportedCppType(t: PType): bool = 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 = result = containsGarbageCollectedRef(typ) @@ -189,17 +190,17 @@ const # but one can #define it to what one wants "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 # since types are now unique via the ``getUniqueType`` mechanism, this slow # linear search is not necessary anymore: - result = PRope(idTableGet(tab, key)) + result = Rope(idTableGet(tab, key)) -proc getTempName(): PRope = - result = rfmt(nil, "TMP$1", toRope(backendId())) +proc getTempName(): Rope = + result = rfmt(nil, "TMP$1", rope(backendId())) -proc getGlobalTempName(): PRope = - result = rfmt(nil, "TMP$1", toRope(backendId())) +proc getGlobalTempName(): Rope = + result = rfmt(nil, "TMP$1", rope(backendId())) proc ccgIntroducedPtr(s: PSym): bool = var pt = skipTypes(s.typ, typedescInst) @@ -226,13 +227,13 @@ proc fillResult(param: PSym) = incl(param.loc.flags, lfIndirect) 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: result = getTypeName(t) else: - result = toRope(literal) + result = rope(literal) -proc getSimpleTypeDesc(m: BModule, typ: PType): PRope = +proc getSimpleTypeDesc(m: BModule, typ: PType): Rope = const NumericalTypeToStr: array[tyInt..tyUInt64, string] = [ "NI", "NI8", "NI16", "NI32", "NI64", @@ -268,20 +269,20 @@ proc getSimpleTypeDesc(m: BModule, typ: PType): PRope = proc pushType(m: BModule, typ: PType) = add(m.typeStack, typ) -proc getTypePre(m: BModule, typ: PType): PRope = - if typ == nil: result = toRope("void") +proc getTypePre(m: BModule, typ: PType): Rope = + if typ == nil: result = rope("void") else: result = getSimpleTypeDesc(m, typ) if result == nil: result = cacheGetType(m.typeCache, typ) -proc structOrUnion(t: PType): PRope = - (if tfUnion in t.flags: toRope("union") else: toRope("struct")) +proc structOrUnion(t: PType): Rope = + (if tfUnion in t.flags: rope("union") else: rope("struct")) proc getForwardStructFormat(m: BModule): string = if m.compileToCpp: result = "$1 $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) if result != nil: return result = getTypePre(m, typ) @@ -290,12 +291,12 @@ proc getTypeForward(m: BModule, typ: PType): PRope = of tySequence, tyTuple, tyObject: result = getTypeName(typ) if not isImportedType(typ): - appf(m.s[cfsForwardTypes], getForwardStructFormat(m), + addf(m.s[cfsForwardTypes], getForwardStructFormat(m), [structOrUnion(typ), result]) idTablePut(m.forwTypeCache, typ, result) 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 ## we know we only need a pointer to it so we only generate a struct forward ## declaration: @@ -310,7 +311,7 @@ proc getTypeDescWeak(m: BModule; t: PType; check: var IntSet): PRope = pushType(m, x) of tySequence: let x = getUniqueType(etB) - result = getTypeForward(m, x).con("*") + result = getTypeForward(m, x) & "*" pushType(m, x) else: result = getTypeDescAux(m, t, check) @@ -321,7 +322,7 @@ proc paramStorageLoc(param: PSym): TStorageLoc = else: 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) = params = nil 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") var param = t.n.sons[i].sym if isCompileTimeOnly(param.typ): continue - if params != nil: app(params, ~", ") + if params != nil: add(params, ~", ") fillLoc(param.loc, locParam, param.typ, mangleName(param), param.paramStorageLoc) if ccgIntroducedPtr(param): - app(params, getTypeDescWeak(m, param.typ, check)) - app(params, ~"*") + add(params, getTypeDescWeak(m, param.typ, check)) + add(params, ~"*") incl(param.loc.flags, lfIndirect) param.loc.s = OnUnknown else: - app(params, getTypeDescAux(m, param.typ, check)) - app(params, ~" ") - app(params, param.loc.r) + add(params, getTypeDescAux(m, param.typ, check)) + add(params, ~" ") + add(params, param.loc.r) # declare the len field for open arrays: var arr = param.typ 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: if param.typ.kind == tyVar: param.loc.s = OnUnknown # 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) arr = arr.sons[0] if (t.sons[0] != nil) and isInvalidReturnType(t.sons[0]): var arr = t.sons[0] - if params != nil: app(params, ", ") + if params != nil: add(params, ", ") if (mapReturnType(t.sons[0]) != ctArray): - app(params, getTypeDescWeak(m, arr, check)) - app(params, "*") + add(params, getTypeDescWeak(m, arr, check)) + add(params, "*") else: - app(params, getTypeDescAux(m, arr, check)) - appf(params, " Result", []) + add(params, getTypeDescAux(m, arr, check)) + addf(params, " Result", []) if t.callConv == ccClosure and declareEnvironment: - if params != nil: app(params, ", ") - app(params, "void* ClEnv") + if params != nil: add(params, ", ") + add(params, "void* ClEnv") if tfVarargs in t.flags: - if params != nil: app(params, ", ") - app(params, "...") - if params == nil: app(params, "void)") - else: app(params, ")") - params = con("(", params) + if params != nil: add(params, ", ") + add(params, "...") + if params == nil: add(params, "void)") + else: add(params, ")") + params = "(" & params -proc mangleRecFieldName(field: PSym, rectype: PType): PRope = +proc mangleRecFieldName(field: PSym, rectype: PType): Rope = if (rectype.sym != nil) and ({sfImportc, sfExportc} * rectype.sym.flags != {}): result = field.loc.r else: - result = toRope(mangleField(field.name.s)) + result = rope(mangleField(field.name)) if result == nil: internalError(field.info, "mangleRecFieldName") proc genRecordFieldsAux(m: BModule, n: PNode, - accessExpr: PRope, rectype: PType, - check: var IntSet): PRope = + accessExpr: Rope, rectype: PType, + check: var IntSet): Rope = var - ae, uname, sname, a: PRope + ae, uname, sname, a: Rope k: PNode field: PSym result = nil case n.kind of nkRecList: 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: if n.sons[0].kind != nkSym: internalError(n.info, "genRecordFieldsAux") - app(result, genRecordFieldsAux(m, n.sons[0], accessExpr, rectype, check)) - uname = toRope(mangle(n.sons[0].sym.name.s) & 'U') - if accessExpr != nil: ae = ropef("$1.$2", [accessExpr, uname]) + add(result, genRecordFieldsAux(m, n.sons[0], accessExpr, rectype, check)) + uname = rope(mangle(n.sons[0].sym.name.s) & 'U') + if accessExpr != nil: ae = "$1.$2" % [accessExpr, uname] else: ae = uname - var unionBody: PRope = nil + var unionBody: Rope = nil for i in countup(1, sonsLen(n) - 1): case n.sons[i].kind of nkOfBranch, nkElse: k = lastSon(n.sons[i]) if k.kind != nkSym: - sname = con("S", toRope(i)) - a = genRecordFieldsAux(m, k, ropef("$1.$2", [ae, sname]), rectype, + sname = "S" & rope(i) + a = genRecordFieldsAux(m, k, "$1.$2" % [ae, sname], rectype, check) if a != nil: - app(unionBody, "struct {") - app(unionBody, a) - appf(unionBody, "} $1;$n", [sname]) + add(unionBody, "struct {") + add(unionBody, a) + addf(unionBody, "} $1;$n", [sname]) else: - app(unionBody, genRecordFieldsAux(m, k, ae, rectype, check)) + add(unionBody, genRecordFieldsAux(m, k, ae, rectype, check)) else: internalError("genRecordFieldsAux(record case branch)") if unionBody != nil: - appf(result, "union{$n$1} $2;$n", [unionBody, uname]) + addf(result, "union{$n$1} $2;$n", [unionBody, uname]) of nkSym: field = n.sym if field.typ.kind == tyEmpty: return #assert(field.ast == nil) sname = mangleRecFieldName(field, rectype) - if accessExpr != nil: ae = ropef("$1.$2", [accessExpr, sname]) + if accessExpr != nil: ae = "$1.$2" % [accessExpr, sname] else: ae = sname fillLoc(field.loc, locField, field.typ, ae, OnUnknown) # 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): let fieldType = field.loc.t.skipTypes(abstractInst) 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]) elif fieldType.kind == tySequence: # 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: # don't use fieldType here because we need the # 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()") -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) -proc getRecordDesc(m: BModule, typ: PType, name: PRope, - check: var IntSet): PRope = +proc getRecordDesc(m: BModule, typ: PType, name: Rope, + check: var IntSet): Rope = # declare the record: var hasField = false - var attribute: PRope = - if tfPacked in typ.flags: toRope(CC[cCompiler].packedPragma) + var attribute: Rope = + if tfPacked in typ.flags: rope(CC[cCompiler].packedPragma) else: nil result = ropecg(m, CC[cCompiler].structStmtFmt, @@ -475,27 +476,54 @@ proc getRecordDesc(m: BModule, typ: PType, name: PRope, [getTypeDescAux(m, typ.sons[0], check)]) hasField = true else: - appf(result, " {$n", [name]) + addf(result, " {$n", [name]) var desc = getRecordFields(m, typ, check) if desc == nil and not hasField: - appf(result, "char dummy;$n", []) + addf(result, "char dummy;$n", []) else: - app(result, desc) - app(result, "};" & tnl) + add(result, desc) + add(result, "};" & tnl) -proc getTupleDesc(m: BModule, typ: PType, name: PRope, - check: var IntSet): PRope = - result = ropef("$1 $2 {$n", [structOrUnion(typ), name]) - var desc: PRope = nil +proc getTupleDesc(m: BModule, typ: PType, name: Rope, + check: var IntSet): Rope = + result = "$1 $2 {$n" % [structOrUnion(typ), name] + var desc: Rope = nil for i in countup(0, sonsLen(typ) - 1): - appf(desc, "$1 Field$2;$n", - [getTypeDescAux(m, typ.sons[i], check), toRope(i)]) - if desc == nil: app(result, "char dummy;" & tnl) - else: app(result, desc) - app(result, "};" & tnl) + addf(desc, "$1 Field$2;$n", + [getTypeDescAux(m, typ.sons[i], check), rope(i)]) + if desc == nil: add(result, "char dummy;" & tnl) + else: add(result, desc) + add(result, "};" & tnl) + +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" -proc getTypeDescAux(m: BModule, typ: PType, check: var IntSet): PRope = + 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 var t = getUniqueType(typ) if t == nil: internalError("getTypeDescAux: t == nil") @@ -523,39 +551,39 @@ proc getTypeDescAux(m: BModule, typ: PType, check: var IntSet): PRope = case etB.kind of tyObject, tyTuple: if isImportedCppType(etB) and et.kind == tyGenericInst: - result = con(getTypeDescAux(m, et, check), star) + result = getTypeDescAux(m, et, check) & star else: # no restriction! We have a forward declaration for structs let x = getUniqueType(etB) let name = getTypeForward(m, x) - result = con(name, star) + result = name & star idTablePut(m.typeCache, t, result) pushType(m, x) of tySequence: # no restriction! We have a forward declaration for structs let x = getUniqueType(etB) let name = getTypeForward(m, x) - result = con(name, "*" & star) + result = name & "*" & star idTablePut(m.typeCache, t, result) pushType(m, x) else: # else we have a strong dependency :-( - result = con(getTypeDescAux(m, et, check), star) + result = getTypeDescAux(m, et, check) & star idTablePut(m.typeCache, t, result) of tyOpenArray, tyVarargs: - result = con(getTypeDescAux(m, t.sons[0], check), "*") + result = getTypeDescAux(m, t.sons[0], check) & "*" idTablePut(m.typeCache, t, result) of tyProc: result = getTypeName(t) idTablePut(m.typeCache, t, result) - var rettype, desc: PRope + var rettype, desc: Rope genProcParams(m, t, rettype, desc, check) if not isImportedType(t): if t.callConv != ccClosure: # procedure vars may need a closure! - appf(m.s[cfsTypes], "typedef $1_PTR($2, $3) $4;$n", - [toRope(CallingConvToStr[t.callConv]), rettype, result, desc]) + addf(m.s[cfsTypes], "typedef $1_PTR($2, $3) $4;$n", + [rope(CallingConvToStr[t.callConv]), rettype, result, desc]) else: - appf(m.s[cfsTypes], "typedef struct {$n" & + addf(m.s[cfsTypes], "typedef struct {$n" & "N_NIMCALL_PTR($2, ClPrc) $3;$n" & "void* ClEnv;$n} $1;$n", [result, rettype, desc]) @@ -566,11 +594,11 @@ proc getTypeDescAux(m: BModule, typ: PType, check: var IntSet): PRope = if result == nil: result = getTypeName(t) if not isImportedType(t): - appf(m.s[cfsForwardTypes], getForwardStructFormat(m), + addf(m.s[cfsForwardTypes], getForwardStructFormat(m), [structOrUnion(t), result]) idTablePut(m.forwTypeCache, t, result) assert(cacheGetType(m.typeCache, t) == nil) - idTablePut(m.typeCache, t, con(result, "*")) + idTablePut(m.typeCache, t, result & "*") if not isImportedType(t): if skipTypes(t.sons[0], typedescInst).kind != tyEmpty: const @@ -582,8 +610,8 @@ proc getTypeDescAux(m: BModule, typ: PType, check: var IntSet): PRope = " $1 data[SEQ_DECL_SIZE];$n" & "};$n", [getTypeDescAux(m, t.sons[0], check), result]) else: - result = toRope("TGenericSeq") - app(result, "*") + result = rope("TGenericSeq") + add(result, "*") of tyArrayConstr, tyArray: var n: BiggestInt = lengthOrd(t) 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) if not isImportedType(t): let foo = getTypeDescAux(m, t.sons[1], check) - appf(m.s[cfsTypes], "typedef $1 $2[$3];$n", - [foo, result, toRope(n)]) + addf(m.s[cfsTypes], "typedef $1 $2[$3];$n", + [foo, result, rope(n)]) of tyObject, tyTuple: if isImportedCppType(t) and typ.kind == tyGenericInst: # for instantiated templates we do not go through the type cache as the # the type cache is not aware of 'tyGenericInst'. - result = getTypeName(t).con("<") - for i in 1 .. typ.len-2: - if i > 1: result.app(", ") - result.app(getTypeDescAux(m, typ.sons[i], check)) - result.app("> ") + 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: + if i > 1: result.add(", ") + result.add(getTypeDescAux(m, typ.sons[i], check)) + result.add("> ") # always call for sideeffects: assert t.kind != tyTuple discard getRecordDesc(m, t, result, check) @@ -610,25 +660,25 @@ proc getTypeDescAux(m: BModule, typ: PType, check: var IntSet): PRope = if result == nil: result = getTypeName(t) if not isImportedType(t): - appf(m.s[cfsForwardTypes], getForwardStructFormat(m), + addf(m.s[cfsForwardTypes], getForwardStructFormat(m), [structOrUnion(t), result]) idTablePut(m.forwTypeCache, t, result) idTablePut(m.typeCache, t, result) # always call for sideeffects: let recdesc = if t.kind != tyTuple: getRecordDesc(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: case int(getSize(t)) - of 1: result = toRope("NU8") - of 2: result = toRope("NU16") - of 4: result = toRope("NU32") - of 8: result = toRope("NU64") + of 1: result = rope("NU8") + of 2: result = rope("NU16") + of 4: result = rope("NU32") + of 8: result = rope("NU64") else: result = getTypeName(t) idTablePut(m.typeCache, t, result) if not isImportedType(t): - appf(m.s[cfsTypes], "typedef NU8 $1[$2];$n", - [result, toRope(getSize(t))]) + addf(m.s[cfsTypes], "typedef NU8 $1[$2];$n", + [result, rope(getSize(t))]) of tyGenericInst, tyDistinct, tyOrdinal, tyConst, tyMutable, tyIter, tyTypeDesc: result = getTypeDescAux(m, lastSon(t), check) @@ -638,7 +688,7 @@ proc getTypeDescAux(m: BModule, typ: PType, check: var IntSet): PRope = # fixes bug #145: excl(check, t.id) -proc getTypeDesc(m: BModule, typ: PType): PRope = +proc getTypeDesc(m: BModule, typ: PType): Rope = var check = initIntSet() result = getTypeDescAux(m, typ, check) @@ -646,23 +696,23 @@ type TClosureTypeKind = enum 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 var check = initIntSet() result = getTempName() - var rettype, desc: PRope + var rettype, desc: Rope genProcParams(m, t, rettype, desc, check, declareEnvironment=kind != clHalf) if not isImportedType(t): if t.callConv != ccClosure or kind != clFull: - appf(m.s[cfsTypes], "typedef $1_PTR($2, $3) $4;$n", - [toRope(CallingConvToStr[t.callConv]), rettype, result, desc]) + addf(m.s[cfsTypes], "typedef $1_PTR($2, $3) $4;$n", + [rope(CallingConvToStr[t.callConv]), rettype, result, desc]) else: - appf(m.s[cfsTypes], "typedef struct {$n" & + addf(m.s[cfsTypes], "typedef struct {$n" & "N_NIMCALL_PTR($2, ClPrc) $3;$n" & "void* ClEnv;$n} $1;$n", [result, rettype, desc]) -proc getTypeDesc(m: BModule, magic: string): PRope = +proc getTypeDesc(m: BModule, magic: string): Rope = var sym = magicsys.getCompilerProc(magic) if sym != nil: result = getTypeDesc(m, sym.typ) @@ -678,38 +728,38 @@ proc finishTypeDescriptions(m: BModule) = template cgDeclFrmt*(s: PSym): string = s.constraint.strVal -proc genProcHeader(m: BModule, prc: PSym): PRope = +proc genProcHeader(m: BModule, prc: PSym): Rope = var - rettype, params: PRope + rettype, params: Rope genCLineDir(result, prc.info) # using static is needed for inline procs if lfExportLib in prc.loc.flags: if m.isHeaderFile: - result.app "N_LIB_IMPORT " + result.add "N_LIB_IMPORT " else: - result.app "N_LIB_EXPORT " + result.add "N_LIB_EXPORT " elif prc.typ.callConv == ccInline: - result.app "static " + result.add "static " var check = initIntSet() fillLoc(prc.loc, locProc, prc.typ, mangleName(prc), OnUnknown) genProcParams(m, prc.typ, rettype, params, check) # careful here! don't access ``prc.ast`` as that could reload large parts of # the object graph! if prc.constraint.isNil: - appf(result, "$1($2, $3)$4", - [toRope(CallingConvToStr[prc.typ.callConv]), rettype, prc.loc.r, + addf(result, "$1($2, $3)$4", + [rope(CallingConvToStr[prc.typ.callConv]), rettype, prc.loc.r, params]) else: - result = ropef(prc.cgDeclFrmt, [rettype, prc.loc.r, params]) + result = prc.cgDeclFrmt % [rettype, prc.loc.r, params] # ------------------ type info generation ------------------------------------- -proc genTypeInfo(m: BModule, t: PType): PRope -proc getNimNode(m: BModule): PRope = - result = ropef("$1[$2]", [m.typeNodesName, toRope(m.typeNodes)]) +proc genTypeInfo(m: BModule, t: PType): Rope +proc getNimNode(m: BModule): Rope = + result = "$1[$2]" % [m.typeNodesName, rope(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 #allocMemTI(m, typ, name) if isObjLackingTypeField(typ): @@ -717,48 +767,47 @@ proc genTypeInfoAuxBase(m: BModule; typ, origType: PType; name, base: PRope) = else: nimtypeKind = ord(typ.kind) - var size: PRope - if tfIncompleteStruct in typ.flags: size = toRope"void*" + var size: Rope + if tfIncompleteStruct in typ.flags: size = rope"void*" elif m.compileToCpp: size = getTypeDesc(m, origType) 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", - [name, size, toRope(nimtypeKind), base]) + [name, size, rope(nimtypeKind), base]) # compute type flags for GC optimization var flags = 0 if not containsGarbageCollectedRef(typ): flags = flags or 1 if not canFormAcycle(typ): flags = flags or 2 #else MessageOut("can contain a cycle: " & typeToString(typ)) 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") - appf(m.s[cfsVars], "TNimType $1; /* $2 */$n", - [name, toRope(typeToString(typ))]) + addf(m.s[cfsVars], "TNimType $1; /* $2 */$n", + [name, rope(typeToString(typ))]) -proc genTypeInfoAux(m: BModule, typ, origType: PType, name: PRope) = - var base: PRope +proc genTypeInfoAux(m: BModule, typ, origType: PType, name: Rope) = + var base: Rope if (sonsLen(typ) > 0) and (typ.sons[0] != nil): base = genTypeInfo(m, typ.sons[0]) else: - base = toRope("0") + base = rope("0") 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: var objtype = objtype while lookupInRecord(objtype.n, d.name) == nil: objtype = objtype.sons[0] if objtype.sym == nil: internalError(d.info, "anonymous obj with discriminator") - result = ropef("NimDT_$1_$2", [ - toRope(objtype.id), toRope(d.name.s.mangle)]) + result = "NimDT_$1_$2" % [rope(objtype.id), rope(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") 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 of nkRecList: 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) elif L > 0: 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): 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) - appf(m.s[cfsTypeInit3], "$1.len = $2; $1.kind = 2; $1.sons = &$3[0];$n", - [expr, toRope(L), tmp]) + addf(m.s[cfsTypeInit3], "$1.len = $2; $1.kind = 2; $1.sons = &$3[0];$n", + [expr, rope(L), tmp]) 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: assert(n.sons[0].kind == nkSym) var field = n.sons[0].sym var tmp = discriminatorTableName(m, typ, field) var L = lengthOrd(field.typ) 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.name = $5;$n" & "$1.sons = &$6[0];$n" & "$1.len = $7;$n", [expr, getTypeDesc(m, typ), field.loc.r, genTypeInfo(m, field.typ), makeCString(field.name.s), - tmp, toRope(L)]) - appf(m.s[cfsData], "TNimNode* $1[$2];$n", [tmp, toRope(L+1)]) + tmp, rope(L)]) + addf(m.s[cfsData], "TNimNode* $1[$2];$n", [tmp, rope(L+1)]) for i in countup(1, sonsLen(n)-1): var b = n.sons[i] # branch 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 y = int(getOrdValue(b.sons[j].sons[1])) 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) else: - appf(m.s[cfsTypeInit3], "$1[$2] = &$3;$n", - [tmp, toRope(getOrdValue(b.sons[j])), tmp2]) + addf(m.s[cfsTypeInit3], "$1[$2] = &$3;$n", + [tmp, rope(getOrdValue(b.sons[j])), tmp2]) of nkElse: - appf(m.s[cfsTypeInit3], "$1[$2] = &$3;$n", - [tmp, toRope(L), tmp2]) + addf(m.s[cfsTypeInit3], "$1[$2] = &$3;$n", + [tmp, rope(L), tmp2]) else: internalError(n.info, "genObjectFields(nkRecCase)") of nkSym: 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.name = $5;$n", [expr, getTypeDesc(m, typ), field.loc.r, genTypeInfo(m, field.typ), makeCString(field.name.s)]) 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) - else: genTypeInfoAuxBase(m, typ, origType, name, toRope("0")) + else: genTypeInfoAuxBase(m, typ, origType, name, rope("0")) var tmp = getNimNode(m) if not isImportedCppType(typ): 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] while t != nil: t = t.skipTypes(abstractInst) t.flags.incl tfObjHasKids t = t.sons[0] -proc genTupleInfo(m: BModule, typ: PType, name: PRope) = - genTypeInfoAuxBase(m, typ, typ, name, toRope("0")) +proc genTupleInfo(m: BModule, typ: PType, name: Rope) = + genTypeInfoAuxBase(m, typ, typ, name, rope("0")) var expr = getNimNode(m) var length = sonsLen(typ) if length > 0: 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): var a = typ.sons[i] var tmp2 = getNimNode(m) - appf(m.s[cfsTypeInit3], "$1[$2] = &$3;$n", [tmp, toRope(i), tmp2]) - appf(m.s[cfsTypeInit3], "$1.kind = 1;$n" & + addf(m.s[cfsTypeInit3], "$1[$2] = &$3;$n", [tmp, rope(i), tmp2]) + addf(m.s[cfsTypeInit3], "$1.kind = 1;$n" & "$1.offset = offsetof($2, Field$3);$n" & "$1.typ = $4;$n" & "$1.name = \"Field$3\";$n", - [tmp2, getTypeDesc(m, typ), toRope(i), genTypeInfo(m, a)]) - appf(m.s[cfsTypeInit3], "$1.len = $2; $1.kind = 2; $1.sons = &$3[0];$n", - [expr, toRope(length), tmp]) + [tmp2, getTypeDesc(m, typ), rope(i), genTypeInfo(m, a)]) + addf(m.s[cfsTypeInit3], "$1.len = $2; $1.kind = 2; $1.sons = &$3[0];$n", + [expr, rope(length), tmp]) else: - appf(m.s[cfsTypeInit3], "$1.len = $2; $1.kind = 2;$n", - [expr, toRope(length)]) - appf(m.s[cfsTypeInit3], "$1.node = &$2;$n", [name, expr]) + addf(m.s[cfsTypeInit3], "$1.len = $2; $1.kind = 2;$n", + [expr, rope(length)]) + 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 # optimizations here: The ``typ`` field is never set, as it is redundant # 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) var nodePtrs = getTempName() var length = sonsLen(typ.n) - appf(m.s[cfsTypeInit1], "static TNimNode* $1[$2];$n", - [nodePtrs, toRope(length)]) - var enumNames, specialCases: PRope + addf(m.s[cfsTypeInit1], "static TNimNode* $1[$2];$n", + [nodePtrs, rope(length)]) + var enumNames, specialCases: Rope var firstNimNode = m.typeNodes var hasHoles = false for i in countup(0, length - 1): @@ -874,38 +923,38 @@ proc genEnumInfo(m: BModule, typ: PType, name: PRope) = var elemNode = getNimNode(m) if field.ast == nil: # 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: - app(enumNames, makeCString(field.ast.strVal)) - if i < length - 1: app(enumNames, ", " & tnl) + add(enumNames, makeCString(field.ast.strVal)) + if i < length - 1: add(enumNames, ", " & tnl) 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 var enumArray = getTempName() var counter = getTempName() - appf(m.s[cfsTypeInit1], "NI $1;$n", [counter]) - appf(m.s[cfsTypeInit1], "static char* NIM_CONST $1[$2] = {$n$3};$n", - [enumArray, toRope(length), enumNames]) - appf(m.s[cfsTypeInit3], "for ($1 = 0; $1 < $2; $1++) {$n" & + addf(m.s[cfsTypeInit1], "NI $1;$n", [counter]) + addf(m.s[cfsTypeInit1], "static char* NIM_CONST $1[$2] = {$n$3};$n", + [enumArray, rope(length), enumNames]) + 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].name = $5[$1];$n" & "$6[$1] = &$3[$1+$4];$n" & "}$n", [counter, - toRope(length), m.typeNodesName, toRope(firstNimNode), enumArray, nodePtrs]) - app(m.s[cfsTypeInit3], specialCases) - appf(m.s[cfsTypeInit3], + rope(length), m.typeNodesName, rope(firstNimNode), enumArray, nodePtrs]) + add(m.s[cfsTypeInit3], specialCases) + addf(m.s[cfsTypeInit3], "$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: # 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) genTypeInfoAux(m, typ, typ, name) var tmp = getNimNode(m) - appf(m.s[cfsTypeInit3], "$1.len = $2; $1.kind = 0;$n" & "$3.node = &$1;$n", - [tmp, toRope(firstOrd(typ)), name]) + addf(m.s[cfsTypeInit3], "$1.len = $2; $1.kind = 0;$n" & "$3.node = &$1;$n", + [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])) proc fakeClosureType(owner: PSym): PType = @@ -925,17 +974,17 @@ type include ccgtrav -proc genDeepCopyProc(m: BModule; s: PSym; result: PRope) = +proc genDeepCopyProc(m: BModule; s: PSym; result: Rope) = 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]) -proc genTypeInfo(m: BModule, t: PType): PRope = +proc genTypeInfo(m: BModule, t: PType): Rope = let origType = t var t = getUniqueType(t) - result = ropef("NTI$1", [toRope(t.id)]) + result = "NTI$1" % [rope(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: 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 discard cgsym(m, "TNimType") discard cgsym(m, "TNimNode") - appf(m.s[cfsVars], "extern TNimType $1; /* $2 */$n", - [result, toRope(typeToString(t))]) - return con("(&".toRope, result, ")".toRope) + addf(m.s[cfsVars], "extern TNimType $1; /* $2 */$n", + [result, rope(typeToString(t))]) + return "(&".rope & result & ")".rope case t.kind - of tyEmpty: result = toRope"0" + of tyEmpty: result = rope"0" 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: if t.callConv != ccClosure: - genTypeInfoAuxBase(m, t, t, result, toRope"0") + genTypeInfoAuxBase(m, t, t, result, rope"0") else: genTupleInfo(m, fakeClosureType(t.owner), result) of tySequence, tyRef: genTypeInfoAux(m, t, t, result) if gSelectedGC >= gcMarkAndSweep: 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 tyArrayConstr, tyArray: genArrayInfo(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) elif origType.deepCopy != nil: genDeepCopyProc(m, origType.deepCopy, result) - result = con("(&".toRope, result, ")".toRope) + result = "(&".rope & result & ")".rope proc genTypeSection(m: BModule, n: PNode) = discard diff --git a/compiler/ccgutils.nim b/compiler/ccgutils.nim index 25c1a12e5..4ba6643ec 100644 --- a/compiler/ccgutils.nim +++ b/compiler/ccgutils.nim @@ -176,7 +176,7 @@ proc mangle*(name: string): string = result = newStringOfCap(name.len) case name[0] of Letters: - result.add(name[0].toLower) + result.add(name[0]) of Digits: result.add("N" & name[0]) else: @@ -193,13 +193,13 @@ proc mangle*(name: string): string = else: add(result, "HEX" & toHex(ord(c), 2)) -proc makeLLVMString*(s: string): PRope = +proc makeLLVMString*(s: string): Rope = const MaxLineLength = 64 result = nil var res = "c\"" for i in countup(0, len(s) - 1): if (i + 1) mod MaxLineLength == 0: - app(result, toRope(res)) + add(result, rope(res)) setLen(res, 0) case s[i] of '\0'..'\x1F', '\x80'..'\xFF', '\"', '\\': @@ -207,6 +207,6 @@ proc makeLLVMString*(s: string): PRope = add(res, toHex(ord(s[i]), 2)) else: add(res, s[i]) add(res, "\\00\"") - app(result, toRope(res)) + add(result, rope(res)) initTypeTables() diff --git a/compiler/cgen.nim b/compiler/cgen.nim index 01db97e73..4b0bac28a 100644 --- a/compiler/cgen.nim +++ b/compiler/cgen.nim @@ -10,13 +10,15 @@ ## This module implements the C code generator. import - ast, astalgo, strutils, hashes, trees, platform, magicsys, extccomp, + ast, astalgo, hashes, trees, platform, magicsys, extccomp, options, intsets, nversion, nimsets, msgs, crc, bitsets, idents, lists, types, ccgutils, os, ropes, math, passes, rodread, wordrecg, treetab, cgmeth, condsyms, rodutils, renderer, idgen, cgendata, ccgmerge, semfold, aliases, lowerings, semparallel +import strutils except `%` # collides with ropes.`%` + when options.hasTinyCBackend: import tccgen @@ -48,7 +50,7 @@ proc initLoc(result: var TLoc, k: TLocKind, typ: PType, s: TStorageLoc) = result.r = nil 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 if a.k == locNone: a.k = k @@ -72,9 +74,9 @@ proc useHeader(m: BModule, sym: PSym) = assert(sym.annex != nil) 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 length = len(frmt) result = nil @@ -84,11 +86,11 @@ proc ropecg(m: BModule, frmt: TFormatStr, args: varargs[PRope]): PRope = inc(i) # skip '$' case frmt[i] of '$': - app(result, "$") + add(result, "$") inc(i) of '#': inc(i) - app(result, args[num]) + add(result, args[num]) inc(num) of '0'..'9': var j = 0 @@ -99,12 +101,12 @@ proc ropecg(m: BModule, frmt: TFormatStr, args: varargs[PRope]): PRope = num = j if j > high(args) + 1: internalError("ropes: invalid format string $" & $j) - app(result, args[j-1]) + add(result, args[j-1]) of 'n': - if optLineDir notin gOptions: app(result, rnl) + if optLineDir notin gOptions: add(result, rnl) inc(i) of 'N': - app(result, rnl) + add(result, rnl) inc(i) else: internalError("ropes: invalid format string $" & frmt[i]) 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) var ident = substr(frmt, i, j-1) i = j - app(result, cgsym(m, ident)) + add(result, cgsym(m, ident)) elif frmt[i] == '#' and frmt[i+1] == '$': inc(i, 2) var j = 0 while frmt[i] in Digits: j = (j * 10) + ord(frmt[i]) - ord('0') inc(i) - app(result, cgsym(m, args[j-1].ropeToStr)) + add(result, cgsym(m, $args[j-1])) var start = i while i < length: if frmt[i] != '$' and frmt[i] != '#': inc(i) else: break 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) -proc appcg(m: BModule, c: var PRope, frmt: TFormatStr, - args: varargs[PRope]) = - app(c, ropecg(m, frmt, args)) +proc appcg(m: BModule, c: var Rope, frmt: FormatStr, + args: varargs[Rope]) = + add(c, ropecg(m, frmt, args)) -proc appcg(m: BModule, s: TCFileSection, frmt: TFormatStr, - args: varargs[PRope]) = - app(m.s[s], ropecg(m, frmt, args)) +proc appcg(m: BModule, s: TCFileSection, frmt: FormatStr, + args: varargs[Rope]) = + add(m.s[s], ropecg(m, frmt, args)) -proc appcg(p: BProc, s: TCProcSection, frmt: TFormatStr, - args: varargs[PRope]) = - app(p.s(s), ropecg(p.module, frmt, args)) +proc appcg(p: BProc, s: TCProcSection, frmt: FormatStr, + args: varargs[Rope]) = + add(p.s(s), ropecg(p.module, frmt, args)) -var indent = "\t".toRope -proc indentLine(p: BProc, r: PRope): PRope = +var indent = "\t".rope +proc indentLine(p: BProc, r: Rope): Rope = result = r for i in countup(0, p.blocks.len-1): prepend(result, indent) -proc line(p: BProc, s: TCProcSection, r: PRope) = - app(p.s(s), indentLine(p, r)) +proc line(p: BProc, s: TCProcSection, r: Rope) = + add(p.s(s), indentLine(p, r)) 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, - args: varargs[PRope]) = - app(p.s(s), indentLine(p, ropef(frmt, args))) +proc lineF(p: BProc, s: TCProcSection, frmt: FormatStr, + args: openarray[Rope]) = + add(p.s(s), indentLine(p, frmt % args)) -proc lineCg(p: BProc, s: TCProcSection, frmt: TFormatStr, - args: varargs[PRope]) = - app(p.s(s), indentLine(p, ropecg(p.module, frmt, args))) +proc lineCg(p: BProc, s: TCProcSection, frmt: FormatStr, + args: varargs[Rope]) = + add(p.s(s), indentLine(p, ropecg(p.module, frmt, args))) -proc linefmt(p: BProc, s: TCProcSection, frmt: TFormatStr, - args: varargs[PRope]) = - app(p.s(s), indentLine(p, ropecg(p.module, frmt, args))) +proc linefmt(p: BProc, s: TCProcSection, frmt: FormatStr, + args: varargs[Rope]) = + add(p.s(s), indentLine(p, ropecg(p.module, frmt, args))) -proc appLineCg(p: BProc, r: var PRope, frmt: TFormatStr, - args: varargs[PRope]) = - app(r, indentLine(p, ropecg(p.module, frmt, args))) +proc appLineCg(p: BProc, r: var Rope, frmt: FormatStr, + args: varargs[Rope]) = + add(r, indentLine(p, ropecg(p.module, frmt, args))) proc safeLineNm(info: TLineInfo): int = result = toLinenumber(info) 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 if optLineDir in gOptions: - appf(r, "$N#line $2 $1$N", - [toRope(makeSingleLineCString(filename)), toRope(line)]) + addf(r, "$N#line $2 $1$N", + [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) proc freshLineInfo(p: BProc; info: TLineInfo): bool = @@ -193,22 +195,22 @@ proc freshLineInfo(p: BProc; info: TLineInfo): bool = proc genLineDir(p: BProc, t: PNode) = var line = t.info.safeLineNm 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) if ({optStackTrace, optEndb} * p.options == {optStackTrace, optEndb}) and (p.prc == nil or sfPure notin p.prc.flags): if freshLineInfo(p, t.info): linefmt(p, cpsStmts, "#endb($1, $2);$n", - line.toRope, makeCString(toFilename(t.info))) + line.rope, makeCString(toFilename(t.info))) elif ({optLineTrace, optStackTrace} * p.options == {optLineTrace, optStackTrace}) and (p.prc == nil or sfPure notin p.prc.flags) and t.info.fileIndex >= 0: if freshLineInfo(p, t.info): linefmt(p, cpsStmts, "nimln($1, $2);$n", - line.toRope, t.info.quotedFilename) + line.rope, t.info.quotedFilename) 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 emulatedThreadVars(): bool {.inline.} @@ -221,21 +223,21 @@ include "ccgtypes.nim" # ------------------------------ Manager of temporaries ------------------ -proc rdLoc(a: TLoc): PRope = +proc rdLoc(a: TLoc): Rope = # 'read' location (deref if indirect) 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 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: result = rdLoc(a) 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, takeAddr: bool) = @@ -244,11 +246,11 @@ proc genObjectInit(p: BProc, section: TCProcSection, t: PType, a: TLoc, discard of frHeader: var r = rdLoc(a) - if not takeAddr: r = ropef("(*$1)", [r]) + if not takeAddr: r = "(*$1)" % [r] var s = skipTypes(t, abstractInst) if not p.module.compileToCpp: while (s.kind == tyObject) and (s.sons[0] != nil): - app(r, ".Sup") + add(r, ".Sup") s = skipTypes(s.sons[0], abstractInst) linefmt(p, section, "$1.m_type = $2;$n", r, genTypeInfo(p.module, t)) of frEmbedded: @@ -276,7 +278,7 @@ proc resetLoc(p: BProc, loc: var TLoc) = if containsGcRef: var nilLoc: TLoc initLoc(nilLoc, locTemp, loc.t, OnStack) - nilLoc.r = toRope("NIM_NIL") + nilLoc.r = rope("NIM_NIL") genRefAssign(p, loc, nilLoc, {afSrcIsNil}) else: 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) = 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) result.k = locTemp #result.a = - 1 @@ -340,9 +342,9 @@ proc keepAlive(p: BProc, toKeepAlive: TLoc) = # of interior pointers instead if optRefcGC notin gGlobalOptions: return var result: TLoc - var fid = toRope(p.gcFrameId) - result.r = con("GCFRAME.F", fid) - appf(p.gcFrameType, " $1 F$2;$n", + var fid = rope(p.gcFrameId) + result.r = "GCFRAME.F" & fid + addf(p.gcFrameType, " $1 F$2;$n", [getTypeDesc(p.module, toKeepAlive.t), fid]) inc(p.gcFrameId) result.k = locTemp @@ -359,10 +361,10 @@ proc keepAlive(p: BProc, toKeepAlive: TLoc) = "memcpy((void*)$1, (NIM_CONST void*)$2, sizeof($3));$n", addrLoc(result), addrLoc(toKeepAlive), rdLoc(result)) -proc initGCFrame(p: BProc): PRope = - if p.gcFrameId > 0: result = ropef("struct {$1} GCFRAME;$n", p.gcFrameType) +proc initGCFrame(p: BProc): Rope = + 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: result = ropecg(p.module, "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 # XXX work around a bug: No type information for open arrays possible: 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 lineF(p, cpsInit, "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)]) inc(p.maxFrameLen) 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: fillLoc(s.loc, locLocalVar, s.typ, mangleName(s), OnStack) if s.kind == skLet: incl(s.loc.flags, lfNoDeepCopy) result = getTypeDesc(p.module, s.loc.t) 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: - # app(decl, " GC_GUARD") - if sfVolatile in s.flags: app(result, " volatile") - app(result, " ") - app(result, s.loc.r) + # add(decl, " GC_GUARD") + if sfVolatile in s.flags: add(result, " volatile") + add(result, " ") + add(result, s.loc.r) else: - result = ropef(s.cgDeclFrmt, result, s.loc.r) + result = s.cgDeclFrmt % [result, s.loc.r] proc assignLocalVar(p: BProc, s: PSym) = #assert(s.loc.k == locNone) # not yet assigned # this need not be fulfilled for inline procs; they are regenerated # for each module that uses them! - let decl = localVarDecl(p, s).con(";" & tnl) + let decl = localVarDecl(p, s) & ";" & tnl line(p, cpsLocals, decl) localDebugInfo(p, s) include ccgthreadvars proc varInDynamicLib(m: BModule, sym: PSym) -proc mangleDynLibProc(sym: PSym): PRope +proc mangleDynLibProc(sym: PSym): Rope proc assignGlobalVar(p: BProc, s: PSym) = if s.loc.k == locNone: @@ -424,17 +426,17 @@ proc assignGlobalVar(p: BProc, s: PSym) = if sfThread in s.flags: declareThreadVar(p.module, s, sfImportc in s.flags) else: - var decl: PRope = nil + var decl: Rope = nil var td = getTypeDesc(p.module, s.loc.t) if s.constraint.isNil: - if sfImportc in s.flags: app(decl, "extern ") - app(decl, td) - if sfRegister in s.flags: app(decl, " register") - if sfVolatile in s.flags: app(decl, " volatile") - appf(decl, " $1;$n", [s.loc.r]) + if sfImportc in s.flags: add(decl, "extern ") + add(decl, td) + if sfRegister in s.flags: add(decl, " register") + if sfVolatile in s.flags: add(decl, " volatile") + addf(decl, " $1;$n", [s.loc.r]) else: - decl = ropef(s.cgDeclFrmt & ";$n", td, s.loc.r) - app(p.module.s[cfsVars], decl) + decl = (s.cgDeclFrmt & ";$n") % [td, s.loc.r] + add(p.module.s[cfsVars], decl) if p.withinLoop > 0: # fixes tests/run/tzeroarray: resetLoc(p, s.loc) @@ -455,7 +457,7 @@ proc fillProcLoc(sym: PSym) = proc getLabel(p: BProc): TLabel = inc(p.labels) - result = con("LA", toRope(p.labels)) + result = "LA" & rope(p.labels) proc fixLabel(p: BProc, labl: TLabel) = 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 putLocIntoDest(p: BProc, d: var TLoc, s: TLoc) proc genAssignment(p: BProc, dest, src: TLoc, flags: TAssignmentFlags) -proc intLiteral(i: BiggestInt): PRope -proc genLiteral(p: BProc, n: PNode): PRope -proc genOtherArg(p: BProc; ri: PNode; i: int; typ: PType): PRope +proc intLiteral(i: BiggestInt): Rope +proc genLiteral(p: BProc, n: PNode): Rope +proc genOtherArg(p: BProc; ri: PNode; i: int; typ: PType): Rope proc initLocExpr(p: BProc, e: PNode, result: var TLoc) = initLoc(result, locNone, e.typ, OnUnknown) @@ -480,13 +482,13 @@ proc initLocExprSingleUse(p: BProc, e: PNode, result: var TLoc) = result.flags.incl lfSingleUse expr(p, e, result) -proc lenField(p: BProc): PRope = - result = toRope(if p.module.compileToCpp: "len" else: "Sup.len") +proc lenField(p: BProc): Rope = + result = rope(if p.module.compileToCpp: "len" else: "Sup.len") include ccgcalls, "ccgstmts.nim", "ccgexprs.nim" # ----------------------------- 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 = let n = lib.path @@ -500,16 +502,16 @@ proc loadDynamicLib(m: BModule, lib: PLib) = var tmp = getGlobalTempName() assert(lib.name == nil) 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}: var s: TStringSeq = @[] libCandidates(lib.path.strVal, s) if gVerbosity >= 2: msgWriteln("Dependency: " & lib.path.strVal) - var loadlib: PRope = nil + var loadlib: Rope = nil for i in countup(0, high(s)): inc(m.labels) - if i > 0: app(loadlib, "||") + if i > 0: add(loadlib, "||") appcg(m, loadlib, "($1 = #nimLoadLibrary((#NimStringDesc*) &$2))$n", [tmp, getStrLit(m, s[i])]) appcg(m, m.s[cfsDynLibInit], @@ -520,21 +522,21 @@ proc loadDynamicLib(m: BModule, lib: PLib) = p.options = p.options - {optStackTrace, optEndb} var dest: TLoc initLocExpr(p, lib.path, dest) - app(m.s[cfsVars], p.s(cpsLocals)) - app(m.s[cfsDynLibInit], p.s(cpsInit)) - app(m.s[cfsDynLibInit], p.s(cpsStmts)) + add(m.s[cfsVars], p.s(cpsLocals)) + add(m.s[cfsDynLibInit], p.s(cpsInit)) + add(m.s[cfsDynLibInit], p.s(cpsStmts)) appcg(m, m.s[cfsDynLibInit], "if (!($1 = #nimLoadLibrary($2))) #nimLoadLibraryError($2);$n", [tmp, rdLoc(dest)]) if lib.name == nil: internalError("loadDynamicLib") -proc mangleDynLibProc(sym: PSym): PRope = +proc mangleDynLibProc(sym: PSym): Rope = if sfCompilerProc in sym.flags: # NOTE: sym.loc.r is the external name! - result = toRope(sym.name.s) + result = rope(sym.name.s) else: - result = ropef("Dl_$1", [toRope(sym.id)]) + result = "Dl_$1" % [rope(sym.id)] proc symInDynamicLib(m: BModule, sym: PSym) = var lib = sym.annex @@ -549,30 +551,28 @@ proc symInDynamicLib(m: BModule, sym: PSym) = let n = lib.path var a: TLoc initLocExpr(m.initProc, n[0], a) - var params = con(rdLoc(a), "(") + var params = rdLoc(a) & "(" for i in 1 .. n.len-2: initLocExpr(m.initProc, n[i], a) - params.app(rdLoc(a)) - params.app(", ") - let load = ropef("\t$1 = ($2) ($3$4));$n", - [tmp, getTypeDesc(m, sym.typ), - params, makeCString(ropeToStr(extname))]) + params.add(rdLoc(a)) + params.add(", ") + let load = "\t$1 = ($2) ($3$4));$n" % + [tmp, getTypeDesc(m, sym.typ), params, makeCString($extname)] var last = lastSon(n) if last.kind == nkHiddenStdConv: last = last.sons[1] internalAssert(last.kind == nkStrLit) let idx = last.strVal 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'}: - app(m.extensionLoaders[idx[0]], load) + add(m.extensionLoaders[idx[0]], load) else: internalError(sym.info, "wrong index: " & idx) else: appcg(m, m.s[cfsDynLibInit], "\t$1 = ($2) #nimGetProcAddr($3, $4);$n", - [tmp, getTypeDesc(m, sym.typ), - lib.name, makeCString(ropeToStr(extname))]) - appf(m.s[cfsVars], "$2 $1;$n", [sym.loc.r, getTypeDesc(m, sym.loc.t)]) + [tmp, getTypeDesc(m, sym.typ), lib.name, makeCString($extname)]) + addf(m.s[cfsVars], "$2 $1;$n", [sym.loc.r, getTypeDesc(m, sym.loc.t)]) proc varInDynamicLib(m: BModule, sym: PSym) = var lib = sym.annex @@ -584,16 +584,15 @@ proc varInDynamicLib(m: BModule, sym: PSym) = inc(m.labels, 2) appcg(m, m.s[cfsDynLibInit], "$1 = ($2*) #nimGetProcAddr($3, $4);$n", - [tmp, getTypeDesc(m, sym.typ), - lib.name, makeCString(ropeToStr(extname))]) - appf(m.s[cfsVars], "$2* $1;$n", + [tmp, getTypeDesc(m, sym.typ), lib.name, makeCString($extname)]) + addf(m.s[cfsVars], "$2* $1;$n", [sym.loc.r, getTypeDesc(m, sym.loc.t)]) proc symInDynamicLibPartial(m: BModule, sym: PSym) = sym.loc.r = mangleDynLibProc(sym) 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) if sym != nil: case sym.kind @@ -609,29 +608,29 @@ proc cgsym(m: BModule, name: string): PRope = result = sym.loc.r 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) while it != nil: 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: - 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) proc retIsNotVoid(s: PSym): bool = 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") if p.maxFrameLen > 0: discard cgsym(p.module, "TVarSlot") result = rfmt(nil, "\tnimfrs($1, $2, $3, $4)$N", - procname, filename, p.maxFrameLen.toRope, - p.blocks[0].frameLen.toRope) + procname, filename, p.maxFrameLen.rope, + p.blocks[0].frameLen.rope) else: 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") proc closureSetup(p: BProc, prc: PSym) = @@ -650,7 +649,7 @@ proc closureSetup(p: BProc, prc: PSym) = proc genProcAux(m: BModule, prc: PSym) = var p = newProc(prc, m) var header = genProcHeader(m, prc) - var returnStmt: PRope = nil + var returnStmt: Rope = nil assert(prc.ast != nil) if sfPure notin prc.flags and prc.typ.sons[0] != nil: if resultPos >= prc.ast.len: @@ -676,33 +675,36 @@ proc genProcAux(m: BModule, prc: PSym) = assignParam(p, param) closureSetup(p, prc) 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 hasNakedDeclspec in extccomp.CC[extccomp.cCompiler].props: - header = con("__declspec(naked) ", header) + if hasDeclspec in extccomp.CC[extccomp.cCompiler].props: + header = "__declspec(naked) " & header generatedProc = rfmt(nil, "$N$1 {$n$2$3$4}$N$N", header, p.s(cpsLocals), p.s(cpsInit), p.s(cpsStmts)) else: generatedProc = rfmt(nil, "$N$1 {$N", header) - app(generatedProc, initGCFrame(p)) + add(generatedProc, initGCFrame(p)) if optStackTrace in prc.options: - app(generatedProc, p.s(cpsLocals)) + add(generatedProc, p.s(cpsLocals)) var procname = makeCString(prc.name.s) - app(generatedProc, initFrame(p, procname, prc.info.quotedFilename)) + add(generatedProc, initFrame(p, procname, prc.info.quotedFilename)) else: - app(generatedProc, p.s(cpsLocals)) + add(generatedProc, p.s(cpsLocals)) if optProfiler in prc.options: # invoke at proc entry for recursion: appcg(p, cpsInit, "\t#nimProfile();$n", []) - if p.beforeRetNeeded: app(generatedProc, "{") - app(generatedProc, p.s(cpsInit)) - app(generatedProc, p.s(cpsStmts)) - if p.beforeRetNeeded: app(generatedProc, ~"\t}BeforeRet: ;$n") - app(generatedProc, deinitGCFrame(p)) - if optStackTrace in prc.options: app(generatedProc, deinitFrame(p)) - app(generatedProc, returnStmt) - app(generatedProc, ~"}$N") - app(m.s[cfsProcs], generatedProc) + if p.beforeRetNeeded: add(generatedProc, "{") + add(generatedProc, p.s(cpsInit)) + add(generatedProc, p.s(cpsStmts)) + if p.beforeRetNeeded: add(generatedProc, ~"\t}BeforeRet: ;$n") + add(generatedProc, deinitGCFrame(p)) + if optStackTrace in prc.options: add(generatedProc, deinitFrame(p)) + add(generatedProc, returnStmt) + add(generatedProc, ~"}$N") + add(m.s[cfsProcs], generatedProc) proc crossesCppBoundary(m: BModule; sym: PSym): bool {.inline.} = result = sfCompileToCpp in m.module.flags and @@ -715,15 +717,17 @@ proc genProcPrototype(m: BModule, sym: PSym) = if lfDynamicLib in sym.loc.flags: if getModule(sym).id != m.module.id and 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))) elif not containsOrIncl(m.declaredProtos, sym.id): var header = genProcHeader(m, sym) if sym.typ.callConv != ccInline and crossesCppBoundary(m, sym): - header = con("extern \"C\" ", header) - if sfPure in sym.flags and hasNakedAttribute in CC[cCompiler].props: - header.app(" __attribute__((naked))") - app(m.s[cfsProcHeaders], rfmt(nil, "$1;$n", header)) + header = "extern \"C\" " & header + if sfPure in sym.flags and hasAttribute in CC[cCompiler].props: + header.add(" __attribute__((naked))") + 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) = fillProcLoc(prc) @@ -760,16 +764,16 @@ proc requestConstImpl(p: BProc, sym: PSym) = var q = findPendingModule(m, sym) if q != nil and not containsOrIncl(q.declaredThings, sym.id): 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)]) # declare header: if q != m and not containsOrIncl(m.declaredThings, sym.id): assert(sym.loc.r != nil) - let headerDecl = ropef("extern NIM_CONST $1 $2;$n", - [getTypeDesc(m, sym.loc.t), sym.loc.r]) - app(m.s[cfsData], headerDecl) + let headerDecl = "extern NIM_CONST $1 $2;$n" % + [getTypeDesc(m, sym.loc.t), sym.loc.r] + add(m.s[cfsData], headerDecl) 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 @@ -798,47 +802,47 @@ proc genVarPrototypeAux(m: BModule, sym: PSym) = if sfThread in sym.flags: declareThreadVar(m, sym, true) else: - app(m.s[cfsVars], "extern ") - app(m.s[cfsVars], getTypeDesc(m, sym.loc.t)) - if lfDynamicLib in sym.loc.flags: app(m.s[cfsVars], "*") - if sfRegister in sym.flags: app(m.s[cfsVars], " register") - if sfVolatile in sym.flags: app(m.s[cfsVars], " volatile") - appf(m.s[cfsVars], " $1;$n", [sym.loc.r]) + add(m.s[cfsVars], "extern ") + add(m.s[cfsVars], getTypeDesc(m, sym.loc.t)) + if lfDynamicLib in sym.loc.flags: add(m.s[cfsVars], "*") + if sfRegister in sym.flags: add(m.s[cfsVars], " register") + if sfVolatile in sym.flags: add(m.s[cfsVars], " volatile") + addf(m.s[cfsVars], " $1;$n", [sym.loc.r]) proc genVarPrototype(m: BModule, sym: PSym) = genVarPrototypeAux(m, sym) -proc addIntTypes(result: var PRope) {.inline.} = - appf(result, "#define NIM_INTBITS $1", [ - platform.CPU[targetCPU].intSize.toRope]) +proc addIntTypes(result: var Rope) {.inline.} = + addf(result, "#define NIM_INTBITS $1", [ + platform.CPU[targetCPU].intSize.rope]) -proc getCopyright(cfile: string): PRope = +proc getCopyright(cfile: string): Rope = 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" & - "/* The generated code is subject to the original license. */$N", - [toRope(VersionAsString)]) + "/* The generated code is subject to the original license. */$N") % + [rope(VersionAsString)] else: - result = ropef("/* Generated by Nim Compiler v$1 */$N" & + result = ("/* Generated by Nim Compiler v$1 */$N" & "/* (c) 2015 Andreas Rumpf */$N" & "/* The generated code is subject to the original license. */$N" & "/* Compiled for: $2, $3, $4 */$N" & - "/* Command for C compiler:$n $5 */$N", - [toRope(VersionAsString), - toRope(platform.OS[targetOS].name), - toRope(platform.CPU[targetCPU].name), - toRope(extccomp.CC[extccomp.cCompiler].name), - toRope(getCompileCFileCmd(cfile))]) - -proc getFileHeader(cfile: string): PRope = + "/* Command for C compiler:$n $5 */$N") % + [rope(VersionAsString), + rope(platform.OS[targetOS].name), + rope(platform.CPU[targetCPU].name), + rope(extccomp.CC[extccomp.cCompiler].name), + rope(getCompileCFileCmd(cfile))] + +proc getFileHeader(cfile: string): Rope = result = getCopyright(cfile) addIntTypes(result) -proc genFilenames(m: BModule): PRope = +proc genFilenames(m: BModule): Rope = discard cgsym(m, "dbgRegisterFilename") result = nil 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) = const @@ -920,7 +924,7 @@ proc genMainProc(m: BModule) = MainProcs & "}$N$N" - var nimMain, otherMain: TFormatStr + var nimMain, otherMain: FormatStr if platform.targetOS == osWindows and gGlobalOptions * {optGenGuiApp, optGenDynLib} != {}: if optGenGuiApp in gGlobalOptions: @@ -941,10 +945,10 @@ proc genMainProc(m: BModule) = otherMain = PosixCMain if gBreakpoints != nil: discard cgsym(m, "dbgRegisterBreakpoint") if optEndb in gOptions: - gBreakpoints.app(m.genFilenames) + gBreakpoints.add(m.genFilenames) let initStackBottomCall = - if platform.targetOS == osStandalone: "".toRope + if platform.targetOS == osStandalone: "".rope else: ropecg(m, "\t#initStackBottomWith((void *)&inner);$N") inc(m.labels) appcg(m, m.s[cfsProcs], PreMainBody, [ @@ -952,56 +956,56 @@ proc genMainProc(m: BModule) = if emulatedThreadVars() and platform.targetOS != osStandalone: ropecg(m, "\t#initThreadVarsEmulation();$N") else: - "".toRope, + "".rope, 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: 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.owner.kind == skPackage if {sfSystemModule, sfMainModule} * m.flags == {}: - result = m.owner.name.s.mangle.toRope - result.app "_" - result.app m.name.s - result.app suffix + result = m.owner.name.s.mangle.rope + result.add "_" + result.add m.name.s + result.add suffix -proc getInitName(m: PSym): PRope = getSomeInitName(m, "Init") -proc getDatInitName(m: PSym): PRope = getSomeInitName(m, "DatInit") +proc getInitName(m: PSym): Rope = getSomeInitName(m, "Init") +proc getDatInitName(m: PSym): Rope = getSomeInitName(m, "DatInit") proc registerModuleToMain(m: PSym) = var init = m.getInitName datInit = m.getDatInitName - appf(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", [init]) + addf(mainModProcs, "NIM_EXTERNC N_NOINLINE(void, $1)(void);$N", [datInit]) if sfSystemModule notin m.flags: - appf(mainDatInit, "\t$1();$N", [datInit]) - let initCall = ropef("\t$1();$N", [init]) + addf(mainDatInit, "\t$1();$N", [datInit]) + let initCall = "\t$1();$N" % [init] if sfMainModule in m.flags: - app(mainModInit, initCall) + add(mainModInit, initCall) else: - app(otherModsInit, initCall) + add(otherModsInit, initCall) proc genInitCode(m: BModule) = 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: appcg(m, m.s[cfsTypeInit1], "static #TNimNode $1[$2];$n", - [m.typeNodesName, toRope(m.typeNodes)]) + [m.typeNodesName, rope(m.typeNodes)]) if m.nimTypes > 0: 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)) - app(prc, m.preInitProc.s(cpsLocals)) - app(prc, m.initProc.s(cpsLocals)) - app(prc, m.postInitProc.s(cpsLocals)) - app(prc, genSectionEnd(cpsLocals)) + add(prc, genSectionStart(cpsLocals)) + add(prc, m.preInitProc.s(cpsLocals)) + add(prc, m.initProc.s(cpsLocals)) + add(prc, m.postInitProc.s(cpsLocals)) + add(prc, genSectionEnd(cpsLocals)) if optStackTrace in m.initProc.options and not m.frameDeclared: # BUT: the generated init code might depend on a current frame, so @@ -1009,58 +1013,58 @@ proc genInitCode(m: BModule) = m.frameDeclared = true if not m.preventStackTrace: 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: - app(prc, ~"\tTFrame F; F.len = 0;$N") - - app(prc, genSectionStart(cpsInit)) - app(prc, m.preInitProc.s(cpsInit)) - app(prc, m.initProc.s(cpsInit)) - app(prc, m.postInitProc.s(cpsInit)) - app(prc, genSectionEnd(cpsInit)) - - app(prc, genSectionStart(cpsStmts)) - app(prc, m.preInitProc.s(cpsStmts)) - app(prc, m.initProc.s(cpsStmts)) - app(prc, m.postInitProc.s(cpsStmts)) - app(prc, genSectionEnd(cpsStmts)) + add(prc, ~"\tTFrame F; F.len = 0;$N") + + add(prc, genSectionStart(cpsInit)) + add(prc, m.preInitProc.s(cpsInit)) + add(prc, m.initProc.s(cpsInit)) + add(prc, m.postInitProc.s(cpsInit)) + add(prc, genSectionEnd(cpsInit)) + + add(prc, genSectionStart(cpsStmts)) + add(prc, m.preInitProc.s(cpsStmts)) + add(prc, m.initProc.s(cpsStmts)) + add(prc, m.postInitProc.s(cpsStmts)) + add(prc, genSectionEnd(cpsStmts)) if optStackTrace in m.initProc.options and not m.preventStackTrace: - app(prc, deinitFrame(m.initProc)) - app(prc, deinitGCFrame(m.initProc)) - appf(prc, "}$N$N") + add(prc, deinitFrame(m.initProc)) + add(prc, deinitGCFrame(m.initProc)) + 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)]) for i in cfsTypeInit1..cfsDynLibInit: - app(prc, genSectionStart(i)) - app(prc, m.s[i]) - app(prc, genSectionEnd(i)) + add(prc, genSectionStart(i)) + add(prc, m.s[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 # that would lead to a *nesting* of merge sections which the merger does # 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): if el != nil: - let ex = ropef("N_NIMCALL(void, nimLoadProcs$1)(void) {$2}$N$N", - (i.ord - '0'.ord).toRope, el) - app(m.s[cfsInitProc], ex) + let ex = "N_NIMCALL(void, nimLoadProcs$1)(void) {$2}$N$N" % + [(i.ord - '0'.ord).rope, el] + add(m.s[cfsInitProc], ex) -proc genModule(m: BModule, cfile: string): PRope = +proc genModule(m: BModule, cfile: string): Rope = result = getFileHeader(cfile) - result.app(genMergeInfo(m)) + result.add(genMergeInfo(m)) generateHeaders(m) generateThreadLocalStorage(m) for i in countup(cfsHeaders, cfsProcs): - app(result, genSectionStart(i)) - app(result, m.s[i]) - app(result, genSectionEnd(i)) - app(result, m.s[cfsInitProc]) + add(result, genSectionStart(i)) + add(result, m.s[i]) + add(result, genSectionEnd(i)) + add(result, m.s[cfsInitProc]) proc newPreInitProc(m: BModule): BProc = result = newProc(nil, m) @@ -1172,22 +1176,22 @@ proc myOpen(module: PSym): PPassContext = proc writeHeader(m: BModule) = var result = getCopyright(m.filename) - var guard = ropef("__$1__", m.filename.splitFile.name.toRope) - result.appf("#ifndef $1$n#define $1$n", guard) + var guard = "__$1__" % [m.filename.splitFile.name.rope] + result.addf("#ifndef $1$n#define $1$n", [guard]) addIntTypes(result) generateHeaders(m) generateThreadLocalStorage(m) for i in countup(cfsHeaders, cfsProcs): - app(result, genSectionStart(i)) - app(result, m.s[i]) - app(result, genSectionEnd(i)) - app(result, m.s[cfsInitProc]) + add(result, genSectionStart(i)) + add(result, m.s[i]) + add(result, genSectionEnd(i)) + add(result, m.s[cfsInitProc]) if optGenDynLib in gGlobalOptions: - result.app("N_LIB_IMPORT ") - result.appf("N_CDECL(void, NimMain)(void);$n") - result.appf("#endif /* $1 */$n", guard) + result.add("N_LIB_IMPORT ") + result.addf("N_CDECL(void, NimMain)(void);$n", []) + result.addf("#endif /* $1 */$n", [guard]) writeRope(result, m.filename) proc getCFile(m: BModule): string = @@ -1224,7 +1228,7 @@ proc finishModule(m: BModule) = dec(gForwardedProcsCounter, i) setLen(m.forwardedProcs, 0) -proc shouldRecompile(code: PRope, cfile: string): bool = +proc shouldRecompile(code: Rope, cfile: string): bool = result = true if optForceFullMake notin gGlobalOptions: var objFile = toObjFile(cfile) @@ -1249,13 +1253,13 @@ proc writeModule(m: BModule, pending: bool) = finishTypeDescriptions(m) if sfMainModule in m.module.flags: # generate main file: - app(m.s[cfsProcHeaders], mainModProcs) + add(m.s[cfsProcHeaders], mainModProcs) generateThreadVarsSize(m) var code = genModule(m, cfile) when hasTinyCBackend: if gCmd == cmdRun: - tccgen.compileCCode(ropeToStr(code)) + tccgen.compileCCode($code) return if shouldRecompile(code, cfile): diff --git a/compiler/cgendata.nim b/compiler/cgendata.nim index 9e9640f59..187186373 100644 --- a/compiler/cgendata.nim +++ b/compiler/cgendata.nim @@ -15,7 +15,7 @@ import from msgs import TLineInfo type - TLabel* = PRope # for the C generator a label is just a rope + TLabel* = Rope # for the C generator a label is just a rope TCFileSection* = enum # the sections a generated C file consists of cfsMergeInfo, # section containing merge information cfsHeaders, # section for C include file headers @@ -45,17 +45,17 @@ type ctUInt, ctUInt8, ctUInt16, ctUInt32, ctUInt64, ctArray, ctPtrToArray, ctStruct, ctPtr, ctNimStr, ctNimSeq, ctProc, ctCString - TCFileSections* = array[TCFileSection, PRope] # represents a generated C file + TCFileSections* = array[TCFileSection, Rope] # represents a generated C file TCProcSection* = enum # the sections a generated C proc consists of cpsLocals, # section of local variables for C proc cpsInit, # section for init of variables for C proc cpsStmts # section of local statements for C proc - TCProcSections* = array[TCProcSection, PRope] # represents a generated C proc + TCProcSections* = array[TCProcSection, Rope] # represents a generated C proc BModule* = ref TCGen BProc* = ref TCProc TBlock*{.final.} = object id*: int # the ID of the label; positive means that it - label*: PRope # generated text for the label + label*: Rope # generated text for the label # nil if label is not used sections*: TCProcSections # the code beloging isLoop*: bool # whether block is a loop @@ -73,7 +73,7 @@ type inExceptBlock*: int # are we currently inside an except block? # leaving such scopes by raise or by return must # execute any applicable finally blocks - finallySafePoints*: seq[PRope] # For correctly cleaning up exceptions when + finallySafePoints*: seq[Rope] # For correctly cleaning up exceptions when # using return in finally statements labels*: Natural # for generating unique labels in the C proc blocks*: seq[TBlock] # nested blocks @@ -89,7 +89,7 @@ type # requires 'T x = T()' to become 'T x; x = T()' # (yes, C++ is weird like that) gcFrameId*: Natural # for the GC stack marking - gcFrameType*: PRope # the struct {} we put the GC markers into + gcFrameType*: Rope # the struct {} we put the GC markers into TTypeSeq* = seq[PType] TCGen = object of TPassContext # represents a C source file @@ -118,24 +118,24 @@ type dataCache*: TNodeTable forwardedProcs*: TSymSeq # keep forwarded procs here typeNodes*, nimTypes*: int # used for type info generation - typeNodesName*, nimTypesName*: PRope # used for type info generation + typeNodesName*, nimTypesName*: Rope # used for type info generation labels*: Natural # for generating unique module-scope names - extensionLoaders*: array['0'..'9', PRope] # special procs for the + extensionLoaders*: array['0'..'9', Rope] # special procs for the # OpenGL wrapper - injectStmt*: PRope + injectStmt*: Rope var - mainModProcs*, mainModInit*, otherModsInit*, mainDatInit*: PRope + mainModProcs*, mainModInit*, otherModsInit*, mainDatInit*: Rope # varuious parts of the main module - gMapping*: PRope # the generated mapping file (if requested) + gMapping*: Rope # the generated mapping file (if requested) gModules*: seq[BModule] = @[] # list of all compiled modules gForwardedProcsCounter*: int = 0 -proc s*(p: BProc, s: TCProcSection): var PRope {.inline.} = +proc s*(p: BProc, s: TCProcSection): var Rope {.inline.} = # section in the current block result = p.blocks[p.blocks.len - 1].sections[s] -proc procSec*(p: BProc, s: TCProcSection): var PRope {.inline.} = +proc procSec*(p: BProc, s: TCProcSection): var Rope {.inline.} = # top level proc sections result = p.blocks[0].sections[s] diff --git a/compiler/commands.nim b/compiler/commands.nim index a2d02e469..b6ebb6bcb 100644 --- a/compiler/commands.nim +++ b/compiler/commands.nim @@ -24,8 +24,8 @@ bootSwitch(usedMarkAndSweep, defined(gcmarkandsweep), "--gc:markAndSweep") bootSwitch(usedGenerational, defined(gcgenerational), "--gc:generational") bootSwitch(usedNoGC, defined(nogc), "--gc:none") -import - os, msgs, options, nversion, condsyms, strutils, extccomp, platform, lists, +import + os, msgs, options, nversion, condsyms, strutils, extccomp, platform, lists, wordrecg, parseutils, nimblecmd, idents, parseopt # but some have deps to imported modules. Yay. @@ -39,8 +39,8 @@ bootSwitch(usedFFI, hasFFI, "-d:useFFI") proc writeCommandLineUsage*() -type - TCmdLinePass* = enum +type + TCmdLinePass* = enum passCmd1, # first pass over the command line passCmd2, # second pass over the command line passPP # preprocessor called processCommand() @@ -54,35 +54,35 @@ const HelpMessage = "Nim Compiler Version $1 (" & CompileDate & ") [$2: $3]\n" & "Copyright (c) 2006-2015 by Andreas Rumpf\n" -const +const Usage = slurp"doc/basicopt.txt".replace("//", "") AdvancedUsage = slurp"doc/advopt.txt".replace("//", "") -proc getCommandLineDesc(): string = - result = (HelpMessage % [VersionAsString, platform.OS[platform.hostOS].name, +proc getCommandLineDesc(): string = + result = (HelpMessage % [VersionAsString, platform.OS[platform.hostOS].name, CPU[platform.hostCPU].name]) & Usage -proc helpOnError(pass: TCmdLinePass) = +proc helpOnError(pass: TCmdLinePass) = if pass == passCmd1: msgWriteln(getCommandLineDesc()) msgQuit(0) -proc writeAdvancedUsage(pass: TCmdLinePass) = +proc writeAdvancedUsage(pass: TCmdLinePass) = if pass == passCmd1: - msgWriteln(`%`(HelpMessage, [VersionAsString, - platform.OS[platform.hostOS].name, + msgWriteln(`%`(HelpMessage, [VersionAsString, + platform.OS[platform.hostOS].name, CPU[platform.hostCPU].name]) & AdvancedUsage) msgQuit(0) -proc writeVersionInfo(pass: TCmdLinePass) = +proc writeVersionInfo(pass: TCmdLinePass) = if pass == passCmd1: - msgWriteln(`%`(HelpMessage, [VersionAsString, - platform.OS[platform.hostOS].name, + msgWriteln(`%`(HelpMessage, [VersionAsString, + platform.OS[platform.hostOS].name, CPU[platform.hostCPU].name])) - discard """const gitHash = gorge("git log -n 1 --format=%H") - if gitHash.strip.len == 40: - msgWriteln("git hash: " & gitHash)""" + const gitHash = gorge("git log -n 1 --format=%H").strip + when gitHash.len == 40: + msgWriteln("git hash: " & gitHash) msgWriteln("active boot switches:" & usedRelease & usedAvoidTimeMachine & usedTinyC & usedGnuReadline & usedNativeStacktrace & usedNoCaas & @@ -92,8 +92,8 @@ proc writeVersionInfo(pass: TCmdLinePass) = var helpWritten: bool -proc writeCommandLineUsage() = - if not helpWritten: +proc writeCommandLineUsage() = + if not helpWritten: msgWriteln(getCommandLineDesc()) helpWritten = true @@ -101,51 +101,51 @@ proc addPrefix(switch: string): string = if len(switch) == 1: result = "-" & switch else: result = "--" & switch -proc invalidCmdLineOption(pass: TCmdLinePass, switch: string, info: TLineInfo) = +proc invalidCmdLineOption(pass: TCmdLinePass, switch: string, info: TLineInfo) = if switch == " ": localError(info, errInvalidCmdLineOption, "-") else: localError(info, errInvalidCmdLineOption, addPrefix(switch)) -proc splitSwitch(switch: string, cmd, arg: var string, pass: TCmdLinePass, - info: TLineInfo) = +proc splitSwitch(switch: string, cmd, arg: var string, pass: TCmdLinePass, + info: TLineInfo) = cmd = "" var i = 0 if i < len(switch) and switch[i] == '-': inc(i) if i < len(switch) and switch[i] == '-': inc(i) - while i < len(switch): + while i < len(switch): case switch[i] of 'a'..'z', 'A'..'Z', '0'..'9', '_', '.': add(cmd, switch[i]) - else: break + else: break inc(i) if i >= len(switch): arg = "" elif switch[i] in {':', '=', '['}: arg = substr(switch, i + 1) else: invalidCmdLineOption(pass, switch, info) - -proc processOnOffSwitch(op: TOptions, arg: string, pass: TCmdLinePass, - info: TLineInfo) = + +proc processOnOffSwitch(op: TOptions, arg: string, pass: TCmdLinePass, + info: TLineInfo) = case whichKeyword(arg) of wOn: gOptions = gOptions + op of wOff: gOptions = gOptions - op else: localError(info, errOnOrOffExpectedButXFound, arg) - -proc processOnOffSwitchG(op: TGlobalOptions, arg: string, pass: TCmdLinePass, - info: TLineInfo) = + +proc processOnOffSwitchG(op: TGlobalOptions, arg: string, pass: TCmdLinePass, + info: TLineInfo) = case whichKeyword(arg) of wOn: gGlobalOptions = gGlobalOptions + op of wOff: gGlobalOptions = gGlobalOptions - op else: localError(info, errOnOrOffExpectedButXFound, arg) - -proc expectArg(switch, arg: string, pass: TCmdLinePass, info: TLineInfo) = + +proc expectArg(switch, arg: string, pass: TCmdLinePass, info: TLineInfo) = if arg == "": localError(info, errCmdLineArgExpected, addPrefix(switch)) - -proc expectNoArg(switch, arg: string, pass: TCmdLinePass, info: TLineInfo) = + +proc expectNoArg(switch, arg: string, pass: TCmdLinePass, info: TLineInfo) = if arg != "": localError(info, errCmdLineNoArgExpected, addPrefix(switch)) - -proc processSpecificNote(arg: string, state: TSpecialWord, pass: TCmdLinePass, - info: TLineInfo; orig: string) = + +proc processSpecificNote(arg: string, state: TSpecialWord, pass: TCmdLinePass, + info: TLineInfo; orig: string) = var id = "" # arg = "X]:on|off" var i = 0 var n = hintMin - while i < len(arg) and (arg[i] != ']'): + while i < len(arg) and (arg[i] != ']'): add(id, arg[i]) inc(i) if i < len(arg) and (arg[i] == ']'): inc(i) @@ -165,7 +165,7 @@ proc processSpecificNote(arg: string, state: TSpecialWord, pass: TCmdLinePass, of wOff: excl(gNotes, n) else: localError(info, errOnOrOffExpectedButXFound, arg) -proc processCompile(filename: string) = +proc processCompile(filename: string) = var found = findFile(filename) if found == "": found = filename var trunc = changeFileExt(found, "") @@ -191,7 +191,7 @@ proc testCompileOptionArg*(switch, arg: string, info: TLineInfo): bool = else: localError(info, errNoneSpeedOrSizeExpectedButXFound, arg) else: invalidCmdLineOption(passCmd1, switch, info) -proc testCompileOption*(switch: string, info: TLineInfo): bool = +proc testCompileOption*(switch: string, info: TLineInfo): bool = case switch.normalize of "debuginfo": result = contains(gGlobalOptions, optCDebug) of "compileonly", "c": result = contains(gGlobalOptions, optCompileOnly) @@ -228,11 +228,11 @@ proc testCompileOption*(switch: string, info: TLineInfo): bool = of "patterns": result = contains(gOptions, optPatterns) of "experimental": result = gExperimentalMode else: invalidCmdLineOption(passCmd1, switch, info) - + proc processPath(path: string, notRelativeToProj = false): string = let p = if notRelativeToProj or os.isAbsolute(path) or - '$' in path or path[0] == '.': - path + '$' in path or path[0] == '.': + path else: options.gProjectPath / path result = unixToNativePath(p % ["nimrod", getPrefixDir(), @@ -251,14 +251,14 @@ proc trackDirty(arg: string, info: TLineInfo) = localError(info, errInvalidNumber, a[1]) if parseUtils.parseInt(a[3], column) <= 0: localError(info, errInvalidNumber, a[2]) - + let dirtyOriginalIdx = a[1].fileInfoIdx if dirtyOriginalIdx >= 0: msgs.setDirtyFile(dirtyOriginalIdx, a[0]) gTrackPos = newLineInfo(dirtyOriginalIdx, line, column) -proc track(arg: string, info: TLineInfo) = +proc track(arg: string, info: TLineInfo) = var a = arg.split(',') if a.len != 3: localError(info, errTokenExpected, "FILE,LINE,COLUMN") var line, column: int @@ -273,13 +273,13 @@ proc dynlibOverride(switch, arg: string, pass: TCmdLinePass, info: TLineInfo) = expectArg(switch, arg, pass, info) options.inclDynlibOverride(arg) -proc processSwitch(switch, arg: string, pass: TCmdLinePass, info: TLineInfo) = - var +proc processSwitch(switch, arg: string, pass: TCmdLinePass, info: TLineInfo) = + var theOS: TSystemOS cpu: TSystemCPU key, val: string case switch.normalize - of "path", "p": + of "path", "p": expectArg(switch, arg, pass, info) addPath(processPath(arg), info) of "nimblepath", "babelpath": @@ -303,7 +303,7 @@ proc processSwitch(switch, arg: string, pass: TCmdLinePass, info: TLineInfo) = of "nimcache": expectArg(switch, arg, pass, info) options.nimcacheDir = processPath(arg) - of "out", "o": + of "out", "o": expectArg(switch, arg, pass, info) options.outFile = arg of "docseesrcurl": @@ -311,19 +311,19 @@ proc processSwitch(switch, arg: string, pass: TCmdLinePass, info: TLineInfo) = options.docSeeSrcUrl = arg of "mainmodule", "m": discard "allow for backwards compatibility, but don't do anything" - of "define", "d": + of "define", "d": expectArg(switch, arg, pass, info) defineSymbol(arg) - of "undef", "u": + of "undef", "u": expectArg(switch, arg, pass, info) undefSymbol(arg) of "symbol": expectArg(switch, arg, pass, info) - declareSymbol(arg) - of "compile": + # deprecated, do nothing + of "compile": expectArg(switch, arg, pass, info) if pass in {passCmd2, passPP}: processCompile(arg) - of "link": + of "link": expectArg(switch, arg, pass, info) if pass in {passCmd2, passPP}: addFileToLink(arg) of "debuginfo": @@ -332,25 +332,25 @@ proc processSwitch(switch, arg: string, pass: TCmdLinePass, info: TLineInfo) = of "embedsrc": expectNoArg(switch, arg, pass, info) incl(gGlobalOptions, optEmbedOrigSrc) - of "compileonly", "c": + of "compileonly", "c": expectNoArg(switch, arg, pass, info) incl(gGlobalOptions, optCompileOnly) - of "nolinking": + of "nolinking": expectNoArg(switch, arg, pass, info) incl(gGlobalOptions, optNoLinking) - of "nomain": + of "nomain": expectNoArg(switch, arg, pass, info) incl(gGlobalOptions, optNoMain) - of "forcebuild", "f": + of "forcebuild", "f": expectNoArg(switch, arg, pass, info) incl(gGlobalOptions, optForceFullMake) of "project": expectNoArg(switch, arg, pass, info) gWholeProject = true - of "gc": + of "gc": expectArg(switch, arg, pass, info) case arg.normalize - of "boehm": + of "boehm": gSelectedGC = gcBoehm defineSymbol("boehmgc") of "refc": @@ -388,7 +388,7 @@ proc processSwitch(switch, arg: string, pass: TCmdLinePass, info: TLineInfo) = undefSymbol("endb") else: localError(info, "expected endb|gdb but found " & arg) - of "profiler": + of "profiler": processOnOffSwitch({optProfiler}, arg, pass, info) if optProfiler in gOptions: defineSymbol("profiler") else: undefSymbol("profiler") @@ -407,7 +407,7 @@ proc processSwitch(switch, arg: string, pass: TCmdLinePass, info: TLineInfo) = of "deadcodeelim": processOnOffSwitchG({optDeadCodeElim}, arg, pass, info) of "threads": processOnOffSwitchG({optThreads}, arg, pass, info) - if optThreads in gGlobalOptions: incl(gNotes, warnGcUnsafe) + #if optThreads in gGlobalOptions: incl(gNotes, warnGcUnsafe) of "tlsemulation": processOnOffSwitchG({optTlsEmulation}, arg, pass, info) of "taintmode": processOnOffSwitchG({optTaintMode}, arg, pass, info) of "implicitstatic": @@ -417,17 +417,17 @@ proc processSwitch(switch, arg: string, pass: TCmdLinePass, info: TLineInfo) = of "opt": expectArg(switch, arg, pass, info) case arg.normalize - of "speed": + of "speed": incl(gOptions, optOptimizeSpeed) excl(gOptions, optOptimizeSize) - of "size": + of "size": excl(gOptions, optOptimizeSpeed) incl(gOptions, optOptimizeSize) of "none": excl(gOptions, optOptimizeSpeed) excl(gOptions, optOptimizeSize) else: localError(info, errNoneSpeedOrSizeExpectedButXFound, arg) - of "app": + of "app": expectArg(switch, arg, pass, info) case arg.normalize of "gui": @@ -449,10 +449,10 @@ proc processSwitch(switch, arg: string, pass: TCmdLinePass, info: TLineInfo) = defineSymbol("library") defineSymbol("staticlib") else: localError(info, errGuiConsoleOrLibExpectedButXFound, arg) - of "passc", "t": + of "passc", "t": expectArg(switch, arg, pass, info) if pass in {passCmd2, passPP}: extccomp.addCompileOption(arg) - of "passl", "l": + of "passl", "l": expectArg(switch, arg, pass, info) if pass in {passCmd2, passPP}: extccomp.addLinkOption(arg) of "cincludes": @@ -475,52 +475,50 @@ proc processSwitch(switch, arg: string, pass: TCmdLinePass, info: TLineInfo) = of "include": expectArg(switch, arg, pass, info) if pass in {passCmd2, passPP}: implicitIncludes.add arg - of "listcmd": + of "listcmd": expectNoArg(switch, arg, pass, info) incl(gGlobalOptions, optListCmd) - of "genmapping": + of "genmapping": expectNoArg(switch, arg, pass, info) incl(gGlobalOptions, optGenMapping) - of "os": + of "os": expectArg(switch, arg, pass, info) - if pass in {passCmd1, passPP}: + if pass in {passCmd1, passPP}: theOS = platform.nameToOS(arg) if theOS == osNone: localError(info, errUnknownOS, arg) - elif theOS != platform.hostOS: + elif theOS != platform.hostOS: setTarget(theOS, targetCPU) - condsyms.initDefines() - of "cpu": + of "cpu": expectArg(switch, arg, pass, info) - if pass in {passCmd1, passPP}: + if pass in {passCmd1, passPP}: cpu = platform.nameToCPU(arg) if cpu == cpuNone: localError(info, errUnknownCPU, arg) - elif cpu != platform.hostCPU: + elif cpu != platform.hostCPU: setTarget(targetOS, cpu) - condsyms.initDefines() - of "run", "r": + of "run", "r": expectNoArg(switch, arg, pass, info) incl(gGlobalOptions, optRun) - of "verbosity": + of "verbosity": expectArg(switch, arg, pass, info) gVerbosity = parseInt(arg) - of "parallelbuild": + of "parallelbuild": expectArg(switch, arg, pass, info) gNumberOfProcessors = parseInt(arg) - of "version", "v": + of "version", "v": expectNoArg(switch, arg, pass, info) writeVersionInfo(pass) - of "advanced": + of "advanced": expectNoArg(switch, arg, pass, info) writeAdvancedUsage(pass) - of "help", "h": + of "help", "h": expectNoArg(switch, arg, pass, info) helpOnError(pass) - of "symbolfiles": + of "symbolfiles": processOnOffSwitchG({optSymbolFiles}, arg, pass, info) - of "skipcfg": + of "skipcfg": expectNoArg(switch, arg, pass, info) incl(gGlobalOptions, optSkipConfigFile) - of "skipprojcfg": + of "skipprojcfg": expectNoArg(switch, arg, pass, info) incl(gGlobalOptions, optSkipProjConfigFile) of "skipusercfg": @@ -529,17 +527,17 @@ proc processSwitch(switch, arg: string, pass: TCmdLinePass, info: TLineInfo) = of "skipparentcfg": expectNoArg(switch, arg, pass, info) incl(gGlobalOptions, optSkipParentConfigFiles) - of "genscript": + of "genscript": expectNoArg(switch, arg, pass, info) incl(gGlobalOptions, optGenScript) of "lib": expectArg(switch, arg, pass, info) libpath = processPath(arg, notRelativeToProj=true) - of "putenv": + of "putenv": expectArg(switch, arg, pass, info) splitSwitch(arg, key, val, pass, info) os.putEnv(key, val) - of "cc": + of "cc": expectArg(switch, arg, pass, info) setCC(arg) of "track": @@ -548,7 +546,7 @@ proc processSwitch(switch, arg: string, pass: TCmdLinePass, info: TLineInfo) = of "trackdirty": expectArg(switch, arg, pass, info) trackDirty(arg, info) - of "suggest": + of "suggest": expectNoArg(switch, arg, pass, info) gIdeCmd = ideSug of "def": @@ -584,7 +582,7 @@ proc processSwitch(switch, arg: string, pass: TCmdLinePass, info: TLineInfo) = else: if strutils.find(switch, '.') >= 0: options.setConfigVar(switch, arg) else: invalidCmdLineOption(pass, switch, info) - + proc processCommand(switch: string, pass: TCmdLinePass) = var cmd, arg: string splitSwitch(switch, cmd, arg, pass, gCmdLineInfo) @@ -600,14 +598,14 @@ proc processSwitch*(pass: TCmdLinePass; p: OptParser) = # hint[X]:off is parsed as (p.key = "hint[X]", p.val = "off") # we fix this here var bracketLe = strutils.find(p.key, '[') - if bracketLe >= 0: + if bracketLe >= 0: var key = substr(p.key, 0, bracketLe - 1) var val = substr(p.key, bracketLe + 1) & ':' & p.val processSwitch(key, val, pass, gCmdLineInfo) - else: + else: processSwitch(p.key, p.val, pass, gCmdLineInfo) -proc processArgument*(pass: TCmdLinePass; p: OptParser; +proc processArgument*(pass: TCmdLinePass; p: OptParser; argsCount: var int): bool = if argsCount == 0: options.command = p.key diff --git a/compiler/condsyms.nim b/compiler/condsyms.nim index 7ddf44d4a..ad7d80c85 100644 --- a/compiler/condsyms.nim +++ b/compiler/condsyms.nim @@ -9,71 +9,69 @@ # This module handles the conditional symbols. -import +import strtabs, platform, strutils, idents -# We need to use a PStringTable here as defined symbols are always guaranteed +# We need to use a StringTableRef here as defined symbols are always guaranteed # to be style insensitive. Otherwise hell would break lose. var gSymbols: StringTableRef -proc defineSymbol*(symbol: string) = - gSymbols[symbol] = "true" +const + catNone = "false" -proc declareSymbol*(symbol: string) = - gSymbols[symbol] = "unknown" +proc defineSymbol*(symbol: string) = + gSymbols[symbol] = "true" -proc undefSymbol*(symbol: string) = - gSymbols[symbol] = "false" +proc undefSymbol*(symbol: string) = + gSymbols[symbol] = catNone -proc isDefined*(symbol: string): bool = +proc isDefined*(symbol: string): bool = if gSymbols.hasKey(symbol): - result = gSymbols[symbol] == "true" - + result = gSymbols[symbol] != catNone + elif cmpIgnoreStyle(symbol, CPU[targetCPU].name) == 0: + result = true + elif cmpIgnoreStyle(symbol, platform.OS[targetOS].name) == 0: + result = true + else: + case symbol.normalize + of "x86": result = targetCPU == cpuI386 + of "itanium": result = targetCPU == cpuIa64 + of "x8664": result = targetCPU == cpuAmd64 + of "posix", "unix": + result = targetOS in {osLinux, osMorphos, osSkyos, osIrix, osPalmos, + osQnx, osAtari, osAix, + osHaiku, osVxWorks, osSolaris, osNetbsd, + osFreebsd, osOpenbsd, osMacosx} + of "bsd": + result = targetOS in {osNetbsd, osFreebsd, osOpenbsd} + of "emulatedthreadvars": + result = platform.OS[targetOS].props.contains(ospLacksThreadVars) + of "msdos": result = targetOS == osDos + of "mswindows", "win32": result = targetOS == osWindows + of "macintosh": result = targetOS in {osMacos, osMacosx} + of "sunos": result = targetOS == osSolaris + of "littleendian": result = CPU[targetCPU].endian == platform.littleEndian + of "bigendian": result = CPU[targetCPU].endian == platform.bigEndian + of "cpu8": result = CPU[targetCPU].bit == 8 + of "cpu16": result = CPU[targetCPU].bit == 16 + of "cpu32": result = CPU[targetCPU].bit == 32 + of "cpu64": result = CPU[targetCPU].bit == 64 + of "nimrawsetjmp": + result = targetOS in {osSolaris, osNetbsd, osFreebsd, osOpenbsd, osMacosx} + else: discard + proc isDefined*(symbol: PIdent): bool = isDefined(symbol.s) -proc isDeclared*(symbol: PIdent): bool = gSymbols.hasKey(symbol.s) iterator definedSymbolNames*: string = for key, val in pairs(gSymbols): - if val == "true": yield key + if val != catNone: yield key -proc countDefinedSymbols*(): int = +proc countDefinedSymbols*(): int = result = 0 for key, val in pairs(gSymbols): - if val == "true": inc(result) - -# For ease of bootstrapping, we keep them here and not in the global config -# file for now: -const - additionalSymbols = """ - x86 itanium x8664 - msdos mswindows win32 unix posix sunos bsd macintosh RISCOS hpux - mac - - hppa hp9000 hp9000s300 hp9000s700 hp9000s800 hp9000s820 ELATE sparcv9 + if val != catNone: inc(result) - 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) defineSymbol("nimrod") # 'nimrod' is always defined # for bootstrapping purposes and old code: @@ -90,58 +88,3 @@ proc initDefines*() = defineSymbol("nimalias") defineSymbol("nimlocks") 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 diff --git a/compiler/depends.nim b/compiler/depends.nim index 115a98f84..1ccb134f2 100644 --- a/compiler/depends.nim +++ b/compiler/depends.nim @@ -9,41 +9,41 @@ # This module implements a dependency file generator. -import +import os, options, ast, astalgo, msgs, ropes, idents, passes, importer proc generateDot*(project: string) -type +type TGen = object of TPassContext module*: PSym PGen = ref TGen -var gDotGraph: PRope # the generated DOT file; we need a global variable +var gDotGraph: Rope # the generated DOT file; we need a global variable -proc addDependencyAux(importing, imported: string) = - appf(gDotGraph, "$1 -> $2;$n", [toRope(importing), toRope(imported)]) +proc addDependencyAux(importing, imported: string) = + addf(gDotGraph, "$1 -> $2;$n", [rope(importing), rope(imported)]) # s1 -> s2_4[label="[0-9]"]; - -proc addDotDependency(c: PPassContext, n: PNode): PNode = + +proc addDotDependency(c: PPassContext, n: PNode): PNode = result = n var g = PGen(c) case n.kind - of nkImportStmt: - for i in countup(0, sonsLen(n) - 1): + of nkImportStmt: + for i in countup(0, sonsLen(n) - 1): var imported = getModuleName(n.sons[i]) addDependencyAux(g.module.name.s, imported) - of nkFromStmt, nkImportExceptStmt: + of nkFromStmt, nkImportExceptStmt: var imported = getModuleName(n.sons[0]) addDependencyAux(g.module.name.s, imported) - of nkStmtList, nkBlockStmt, nkStmtListExpr, nkBlockExpr: + of nkStmtList, nkBlockStmt, nkStmtListExpr, nkBlockExpr: for i in countup(0, sonsLen(n) - 1): discard addDotDependency(c, n.sons[i]) - else: + else: discard -proc generateDot(project: string) = - writeRope(ropef("digraph $1 {$n$2}$n", [ - toRope(changeFileExt(extractFilename(project), "")), gDotGraph]), +proc generateDot(project: string) = + writeRope("digraph $1 {$n$2}$n" % [ + rope(changeFileExt(extractFilename(project), "")), gDotGraph], changeFileExt(project, "dot")) proc myOpen(module: PSym): PPassContext = diff --git a/compiler/docgen.nim b/compiler/docgen.nim index 4af69745b..f8489d825 100644 --- a/compiler/docgen.nim +++ b/compiler/docgen.nim @@ -17,9 +17,9 @@ import importer, sempass2, json, xmltree, cgi, typesrenderer type - TSections = array[TSymKind, PRope] + TSections = array[TSymKind, Rope] TDocumentor = object of rstgen.TRstGenerator - modDesc: PRope # module description + modDesc: Rope # module description id: int # for generating IDs toc, section: TSections indexValFilename: string @@ -82,9 +82,9 @@ proc newDocumentor*(filename: string, config: StringTableRef): PDoc = result.seenSymbols = newStringTable(modeCaseInsensitive) result.id = 100 -proc dispA(dest: var PRope, xml, tex: string, args: openArray[PRope]) = - if gCmd != cmdRst2tex: appf(dest, xml, args) - else: appf(dest, tex, args) +proc dispA(dest: var Rope, xml, tex: string, args: openArray[Rope]) = + if gCmd != cmdRst2tex: addf(dest, xml, args) + else: addf(dest, tex, args) proc getVarIdx(varnames: openArray[string], id: string): int = for i in countup(0, high(varnames)): @@ -92,8 +92,8 @@ proc getVarIdx(varnames: openArray[string], id: string): int = return i result = -1 -proc ropeFormatNamedVars(frmt: TFormatStr, varnames: openArray[string], - varvalues: openArray[PRope]): PRope = +proc ropeFormatNamedVars(frmt: FormatStr, varnames: openArray[string], + varvalues: openArray[Rope]): Rope = var i = 0 var L = len(frmt) result = nil @@ -103,11 +103,11 @@ proc ropeFormatNamedVars(frmt: TFormatStr, varnames: openArray[string], inc(i) # skip '$' case frmt[i] of '#': - app(result, varvalues[num]) + add(result, varvalues[num]) inc(num) inc(i) of '$': - app(result, "$") + add(result, "$") inc(i) of '0'..'9': var j = 0 @@ -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 j > high(varvalues) + 1: internalError("ropeFormatNamedVars") num = j - app(result, varvalues[j - 1]) + add(result, varvalues[j - 1]) of 'A'..'Z', 'a'..'z', '\x80'..'\xFF': var id = "" while true: @@ -125,7 +125,7 @@ proc ropeFormatNamedVars(frmt: TFormatStr, varnames: openArray[string], inc(i) if not (frmt[i] in {'A'..'Z', '_', 'a'..'z', '\x80'..'\xFF'}): break var idx = getVarIdx(varnames, id) - if idx >= 0: app(result, varvalues[idx]) + if idx >= 0: add(result, varvalues[idx]) else: rawMessage(errUnknownSubstitionVar, id) of '{': var id = "" @@ -137,14 +137,14 @@ proc ropeFormatNamedVars(frmt: TFormatStr, varnames: openArray[string], inc(i) # skip } # search for the variable: var idx = getVarIdx(varnames, id) - if idx >= 0: app(result, varvalues[idx]) + if idx >= 0: add(result, varvalues[idx]) else: rawMessage(errUnknownSubstitionVar, id) else: internalError("ropeFormatNamedVars") var start = i while i < L: if frmt[i] != '$': inc(i) else: break - if i - 1 >= start: app(result, substr(frmt, start, i - 1)) + if i - 1 >= start: add(result, substr(frmt, start, i - 1)) proc genComment(d: PDoc, n: PNode): string = result = "" @@ -154,9 +154,9 @@ proc genComment(d: PDoc, n: PNode): string = toLinenumber(n.info), toColumn(n.info), dummyHasToc, d.options + {roSkipPounds}), result) -proc genRecComment(d: PDoc, n: PNode): PRope = +proc genRecComment(d: PDoc, n: PNode): Rope = if n == nil: return nil - result = genComment(d, n).toRope + result = genComment(d, n).rope if result == nil: if n.kind notin {nkEmpty..nkNilLit}: for i in countup(0, len(n)-1): @@ -331,9 +331,9 @@ proc genItem(d: PDoc, n, nameNode: PNode, k: TSymKind) = if not isVisible(nameNode): return let name = getName(d, nameNode) - nameRope = name.toRope + nameRope = name.rope plainDocstring = getPlainDocstring(n) # call here before genRecComment! - var result: PRope = nil + var result: Rope = nil var literal, plainName = "" var kind = tkEof var comm = genRecComment(d, n) # call this here for the side-effect! @@ -356,69 +356,69 @@ proc genItem(d: PDoc, n, nameNode: PNode, k: TSymKind) = break of tkComment: dispA(result, "<span class=\"Comment\">$1</span>", "\\spanComment{$1}", - [toRope(esc(d.target, literal))]) + [rope(esc(d.target, literal))]) of tokKeywordLow..tokKeywordHigh: dispA(result, "<span class=\"Keyword\">$1</span>", "\\spanKeyword{$1}", - [toRope(literal)]) + [rope(literal)]) of tkOpr: dispA(result, "<span class=\"Operator\">$1</span>", "\\spanOperator{$1}", - [toRope(esc(d.target, literal))]) + [rope(esc(d.target, literal))]) of tkStrLit..tkTripleStrLit: dispA(result, "<span class=\"StringLit\">$1</span>", - "\\spanStringLit{$1}", [toRope(esc(d.target, literal))]) + "\\spanStringLit{$1}", [rope(esc(d.target, literal))]) of tkCharLit: dispA(result, "<span class=\"CharLit\">$1</span>", "\\spanCharLit{$1}", - [toRope(esc(d.target, literal))]) + [rope(esc(d.target, literal))]) of tkIntLit..tkUInt64Lit: dispA(result, "<span class=\"DecNumber\">$1</span>", - "\\spanDecNumber{$1}", [toRope(esc(d.target, literal))]) + "\\spanDecNumber{$1}", [rope(esc(d.target, literal))]) of tkFloatLit..tkFloat128Lit: dispA(result, "<span class=\"FloatNumber\">$1</span>", - "\\spanFloatNumber{$1}", [toRope(esc(d.target, literal))]) + "\\spanFloatNumber{$1}", [rope(esc(d.target, literal))]) of tkSymbol: dispA(result, "<span class=\"Identifier\">$1</span>", - "\\spanIdentifier{$1}", [toRope(esc(d.target, literal))]) + "\\spanIdentifier{$1}", [rope(esc(d.target, literal))]) of tkSpaces, tkInvalid: - app(result, literal) + add(result, literal) of tkParLe, tkParRi, tkBracketLe, tkBracketRi, tkCurlyLe, tkCurlyRi, tkBracketDotLe, tkBracketDotRi, tkCurlyDotLe, tkCurlyDotRi, tkParDotLe, tkParDotRi, tkComma, tkSemiColon, tkColon, tkEquals, tkDot, tkDotDot, tkAccent, tkColonColon, tkGStrLit, tkGTripleStrLit, tkInfixOpr, tkPrefixOpr, tkPostfixOpr: dispA(result, "<span class=\"Other\">$1</span>", "\\spanOther{$1}", - [toRope(esc(d.target, literal))]) + [rope(esc(d.target, literal))]) inc(d.id) let - plainNameRope = toRope(xmltree.escape(plainName.strip)) + plainNameRope = rope(xmltree.escape(plainName.strip)) cleanPlainSymbol = renderPlainSymbolName(nameNode) complexSymbol = complexName(k, n, cleanPlainSymbol) - plainSymbolRope = toRope(cleanPlainSymbol) - plainSymbolEncRope = toRope(encodeUrl(cleanPlainSymbol)) - itemIDRope = toRope(d.id) + plainSymbolRope = rope(cleanPlainSymbol) + plainSymbolEncRope = rope(encodeUrl(cleanPlainSymbol)) + itemIDRope = rope(d.id) symbolOrId = d.newUniquePlainSymbol(complexSymbol) - symbolOrIdRope = symbolOrId.toRope - symbolOrIdEncRope = encodeUrl(symbolOrId).toRope + symbolOrIdRope = symbolOrId.rope + symbolOrIdEncRope = encodeUrl(symbolOrId).rope - var seeSrcRope: PRope = nil + var seeSrcRope: Rope = nil let docItemSeeSrc = getConfigVar("doc.item.seesrc") if docItemSeeSrc.len > 0 and options.docSeeSrcUrl.len > 0: # XXX toFilename doesn't really work. We need to ensure that this keeps # returning a relative path. let urlRope = ropeFormatNamedVars(options.docSeeSrcUrl, - ["path", "line"], [n.info.toFilename.toRope, toRope($n.info.line)]) + ["path", "line"], [n.info.toFilename.rope, rope($n.info.line)]) dispA(seeSrcRope, "$1", "", [ropeFormatNamedVars(docItemSeeSrc, - ["path", "line", "url"], [n.info.toFilename.toRope, - toRope($n.info.line), urlRope])]) + ["path", "line", "url"], [n.info.toFilename.rope, + rope($n.info.line), urlRope])]) - app(d.section[k], ropeFormatNamedVars(getConfigVar("doc.item"), + add(d.section[k], ropeFormatNamedVars(getConfigVar("doc.item"), ["name", "header", "desc", "itemID", "header_plain", "itemSym", "itemSymOrID", "itemSymEnc", "itemSymOrIDEnc", "seeSrc"], [nameRope, result, comm, itemIDRope, plainNameRope, plainSymbolRope, symbolOrIdRope, plainSymbolEncRope, symbolOrIdEncRope, seeSrcRope])) - app(d.toc[k], ropeFormatNamedVars(getConfigVar("doc.item.toc"), + add(d.toc[k], ropeFormatNamedVars(getConfigVar("doc.item.toc"), ["name", "header", "desc", "itemID", "header_plain", "itemSym", "itemSymOrID", "itemSymEnc", "itemSymOrIDEnc"], - [toRope(getName(d, nameNode, d.splitAfter)), result, comm, + [rope(getName(d, nameNode, d.splitAfter)), result, comm, itemIDRope, plainNameRope, plainSymbolRope, symbolOrIdRope, plainSymbolEncRope, symbolOrIdEncRope])) @@ -436,7 +436,7 @@ proc genJSONItem(d: PDoc, n, nameNode: PNode, k: TSymKind): JsonNode = if not isVisible(nameNode): return var name = getName(d, nameNode) - comm = genRecComment(d, n).ropeToStr() + comm = $genRecComment(d, n) r: TSrcGen initTokRender(r, n, {renderNoBody, renderNoComments, renderDocComments}) @@ -453,14 +453,14 @@ proc checkForFalse(n: PNode): bool = proc traceDeps(d: PDoc, n: PNode) = const k = skModule - if d.section[k] != nil: app(d.section[k], ", ") + if d.section[k] != nil: add(d.section[k], ", ") dispA(d.section[k], "<a class=\"reference external\" href=\"$1.html\">$1</a>", - "$1", [toRope(getModuleName(n))]) + "$1", [rope(getModuleName(n))]) proc generateDoc*(d: PDoc, n: PNode) = case n.kind - of nkCommentStmt: app(d.modDesc, genComment(d, n)) + of nkCommentStmt: add(d.modDesc, genComment(d, n)) of nkProcDef: when useEffectSystem: documentRaises(n) genItem(d, n, n.sons[namePos], skProc) @@ -540,28 +540,28 @@ proc genSection(d: PDoc, kind: TSymKind) = "Iterators", "Iterators", "Converters", "Macros", "Templates" ] if d.section[kind] == nil: return - var title = sectionNames[kind].toRope + var title = sectionNames[kind].rope d.section[kind] = ropeFormatNamedVars(getConfigVar("doc.section"), [ "sectionid", "sectionTitle", "sectionTitleID", "content"], [ - ord(kind).toRope, title, toRope(ord(kind) + 50), d.section[kind]]) + ord(kind).rope, title, rope(ord(kind) + 50), d.section[kind]]) d.toc[kind] = ropeFormatNamedVars(getConfigVar("doc.section.toc"), [ "sectionid", "sectionTitle", "sectionTitleID", "content"], [ - ord(kind).toRope, title, toRope(ord(kind) + 50), d.toc[kind]]) + ord(kind).rope, title, rope(ord(kind) + 50), d.toc[kind]]) -proc genOutFile(d: PDoc): PRope = +proc genOutFile(d: PDoc): Rope = var - code, content: PRope + code, content: Rope title = "" var j = 0 var tmp = "" renderTocEntries(d[], j, 1, tmp) - var toc = tmp.toRope + var toc = tmp.rope for i in countup(low(TSymKind), high(TSymKind)): genSection(d, i) - app(toc, d.toc[i]) + add(toc, d.toc[i]) if toc != nil: toc = ropeFormatNamedVars(getConfigVar("doc.toc"), ["content"], [toc]) - for i in countup(low(TSymKind), high(TSymKind)): app(code, d.section[i]) + for i in countup(low(TSymKind), high(TSymKind)): add(code, d.section[i]) # Extract the title. Non API modules generate an entry in the index table. if d.meta[metaTitle].len != 0: @@ -574,16 +574,16 @@ proc genOutFile(d: PDoc): PRope = let bodyname = if d.hasToc: "doc.body_toc" else: "doc.body_no_toc" content = ropeFormatNamedVars(getConfigVar(bodyname), ["title", "tableofcontents", "moduledesc", "date", "time", "content"], - [title.toRope, toc, d.modDesc, toRope(getDateStr()), - toRope(getClockStr()), code]) + [title.rope, toc, d.modDesc, rope(getDateStr()), + rope(getClockStr()), code]) if optCompileOnly notin gGlobalOptions: # XXX what is this hack doing here? 'optCompileOnly' means raw output!? code = ropeFormatNamedVars(getConfigVar("doc.file"), ["title", "tableofcontents", "moduledesc", "date", "time", "content", "author", "version", "analytics"], - [title.toRope, toc, d.modDesc, toRope(getDateStr()), - toRope(getClockStr()), content, d.meta[metaAuthor].toRope, - d.meta[metaVersion].toRope, d.analytics.toRope]) + [title.rope, toc, d.modDesc, rope(getDateStr()), + rope(getClockStr()), content, d.meta[metaAuthor].rope, + d.meta[metaVersion].rope, d.analytics.rope]) else: code = content result = code @@ -618,7 +618,7 @@ proc commandRstAux(filename, outExt: string) = #d.modDesc = newMutableRope(30_000) renderRstToOut(d[], rst, modDesc) #freezeMutableRope(d.modDesc) - d.modDesc = toRope(modDesc) + d.modDesc = rope(modDesc) writeOutput(d, filename, outExt) generateIndex(d) @@ -635,7 +635,7 @@ proc commandJSON*() = var d = newDocumentor(gProjectFull, options.gConfigVars) d.hasToc = true var json = generateJson(d, ast) - var content = newRope(pretty(json)) + var content = rope(pretty(json)) if optStdout in gGlobalOptions: writeRope(stdout, content) @@ -644,12 +644,12 @@ proc commandJSON*() = writeRope(content, getOutFile(gProjectFull, JsonExt), useWarning = false) proc commandBuildIndex*() = - var content = mergeIndexes(gProjectFull).toRope + var content = mergeIndexes(gProjectFull).rope let code = ropeFormatNamedVars(getConfigVar("doc.file"), ["title", "tableofcontents", "moduledesc", "date", "time", "content", "author", "version", "analytics"], - ["Index".toRope, nil, nil, toRope(getDateStr()), - toRope(getClockStr()), content, nil, nil, nil]) + ["Index".rope, nil, nil, rope(getDateStr()), + rope(getClockStr()), content, nil, nil, nil]) # no analytics because context is not available writeRope(code, getOutFile("theindex", HtmlExt)) diff --git a/compiler/extccomp.nim b/compiler/extccomp.nim index a68e7f734..186a3884d 100644 --- a/compiler/extccomp.nim +++ b/compiler/extccomp.nim @@ -15,9 +15,9 @@ import lists, ropes, os, strutils, osproc, platform, condsyms, options, msgs, crc -type - TSystemCC* = enum - ccNone, ccGcc, ccLLVM_Gcc, ccCLang, ccLcc, ccBcc, ccDmc, ccWcc, ccVcc, +type + TSystemCC* = enum + ccNone, ccGcc, ccLLVM_Gcc, ccCLang, ccLcc, ccBcc, ccDmc, ccWcc, ccVcc, ccTcc, ccPcc, ccUcc, ccIcl TInfoCCProp* = enum # properties of the C compiler: hasSwitchRange, # CC allows ranges in switch statements (GNU C) @@ -26,8 +26,8 @@ type hasAssume, # CC has __assume (Visual C extension) hasGcGuard, # CC supports GC_GUARD to keep stack roots hasGnuAsm, # CC's asm uses the absurd GNU assembler syntax - hasNakedDeclspec, # CC has __declspec(naked) - hasNakedAttribute # CC has __attribute__((naked)) + hasDeclspec, # CC has __declspec(X) + hasAttribute, # CC has __attribute__((X)) TInfoCCProps* = set[TInfoCCProp] TInfoCC* = tuple[ name: string, # the short name of the compiler @@ -54,7 +54,7 @@ type props: TInfoCCProps] # properties of the C compiler -# Configuration settings for various compilers. +# Configuration settings for various compilers. # When adding new compilers, the cmake sources could be a good reference: # http://cmake.org/gitweb?p=cmake.git;a=tree;f=Modules/Platform; @@ -85,7 +85,7 @@ compiler gcc: structStmtFmt: "$1 $3 $2 ", # struct|union [packed] $name packedPragma: "__attribute__((__packed__))", props: {hasSwitchRange, hasComputedGoto, hasCpp, hasGcGuard, hasGnuAsm, - hasNakedAttribute}) + hasAttribute}) # LLVM Frontend for GCC/G++ compiler llvmGcc: @@ -127,7 +127,7 @@ compiler vcc: asmStmtFrmt: "__asm{$n$1$n}$n", structStmtFmt: "$3$n$1 $2", packedPragma: "#pragma pack(1)", - props: {hasCpp, hasAssume, hasNakedDeclspec}) + props: {hasCpp, hasAssume, hasDeclspec}) # Intel C/C++ Compiler compiler icl: @@ -136,7 +136,7 @@ compiler icl: result = vcc() else: result = gcc() - + result.name = "icl" result.compilerExe = "icl" result.linkerExe = "icl" @@ -317,7 +317,7 @@ compiler ucc: packedPragma: "", # XXX: not supported yet props: {}) -const +const CC*: array[succ(low(TSystemCC))..high(TSystemCC), TInfoCC] = [ gcc(), llvmGcc(), @@ -346,7 +346,7 @@ var proc libNameTmpl(): string {.inline.} = result = if targetOS == osWindows: "$1.lib" else: "lib$1.a" -var +var toLink, toCompile, externalToCompile: TLinkedList linkOptions: string = "" compileOptions: string = "" @@ -355,8 +355,8 @@ var proc nameToCC*(name: string): TSystemCC = ## Returns the kind of compiler referred to by `name`, or ccNone ## if the name doesn't refer to any known compiler. - for i in countup(succ(ccNone), high(TSystemCC)): - if cmpIgnoreStyle(name, CC[i].name) == 0: + for i in countup(succ(ccNone), high(TSystemCC)): + if cmpIgnoreStyle(name, CC[i].name) == 0: return i result = ccNone @@ -375,8 +375,8 @@ proc getConfigVar(c: TSystemCC, suffix: string): string = if (platform.hostOS != targetOS or platform.hostCPU != targetCPU) and optCompileOnly notin gGlobalOptions: - let fullCCname = platform.CPU[targetCPU].name & '.' & - platform.OS[targetOS].name & '.' & + let fullCCname = platform.CPU[targetCPU].name & '.' & + platform.OS[targetOS].name & '.' & CC[c].name & fullSuffix result = getConfigVar(fullCCname) if result.len == 0: @@ -385,7 +385,7 @@ proc getConfigVar(c: TSystemCC, suffix: string): string = else: result = getConfigVar(CC[c].name & fullSuffix) -proc setCC*(ccname: string) = +proc setCC*(ccname: string) = cCompiler = nameToCC(ccname) if cCompiler == ccNone: rawMessage(errUnknownCcompiler, ccname) compileOptions = getConfigVar(cCompiler, ".options.always") @@ -394,18 +394,18 @@ proc setCC*(ccname: string) = for i in countup(low(CC), high(CC)): undefSymbol(CC[i].name) defineSymbol(CC[cCompiler].name) -proc addOpt(dest: var string, src: string) = +proc addOpt(dest: var string, src: string) = if len(dest) == 0 or dest[len(dest)-1] != ' ': add(dest, " ") add(dest, src) proc addLinkOption*(option: string) = addOpt(linkOptions, option) -proc addCompileOption*(option: string) = - if strutils.find(compileOptions, option, 0) < 0: +proc addCompileOption*(option: string) = + if strutils.find(compileOptions, option, 0) < 0: addOpt(compileOptions, option) -proc initVars*() = +proc initVars*() = # we need to define the symbol here, because ``CC`` may have never been set! for i in countup(low(CC), high(CC)): undefSymbol(CC[i].name) defineSymbol(CC[cCompiler].name) @@ -414,10 +414,10 @@ proc initVars*() = if len(ccompilerpath) == 0: ccompilerpath = getConfigVar(cCompiler, ".path") -proc completeCFilePath*(cfile: string, createSubDir: bool = true): string = +proc completeCFilePath*(cfile: string, createSubDir: bool = true): string = result = completeGeneratedFilePath(cfile, createSubDir) -proc toObjFile*(filename: string): string = +proc toObjFile*(filename: string): string = # Object file for compilation result = changeFileExt(filename, CC[cCompiler].objExt) @@ -449,22 +449,22 @@ proc execExternalProgram*(cmd: string, prettyCmd = "") = if execWithEcho(cmd, prettyCmd) != 0: rawMessage(errExecutionOfProgramFailed, "") -proc generateScript(projectFile: string, script: PRope) = +proc generateScript(projectFile: string, script: Rope) = let (dir, name, ext) = splitFile(projectFile) - writeRope(script, dir / addFileExt("compile_" & name, + writeRope(script, dir / addFileExt("compile_" & name, platform.OS[targetOS].scriptExt)) -proc getOptSpeed(c: TSystemCC): string = +proc getOptSpeed(c: TSystemCC): string = result = getConfigVar(c, ".options.speed") if result == "": result = CC[c].optSpeed # use default settings from this file -proc getDebug(c: TSystemCC): string = +proc getDebug(c: TSystemCC): string = result = getConfigVar(c, ".options.debug") if result == "": result = CC[c].debug # use default settings from this file -proc getOptSize(c: TSystemCC): string = +proc getOptSize(c: TSystemCC): string = result = getConfigVar(c, ".options.size") if result == "": result = CC[c].optSize # use default settings from this file @@ -476,7 +476,7 @@ proc noAbsolutePaths: bool {.inline.} = # `optGenMapping` is included here for niminst. result = gGlobalOptions * {optGenScript, optGenMapping} != {} -const +const specialFileA = 42 specialFileB = 42 @@ -488,7 +488,7 @@ proc add(s: var string, many: openArray[string]) = proc cFileSpecificOptions(cfilename: string): string = result = compileOptions var trunk = splitFile(cfilename).name - if optCDebug in gGlobalOptions: + if optCDebug in gGlobalOptions: var key = trunk & ".debug" if existsConfigVar(key): addOpt(result, getConfigVar(key)) else: addOpt(result, getDebug(cCompiler)) @@ -528,17 +528,17 @@ proc getLinkerExe(compiler: TSystemCC): string = elif gMixedMode and gCmd != cmdCompileToCpp: CC[compiler].cppCompiler else: compiler.getCompilerExe -proc getCompileCFileCmd*(cfilename: string, isExternal = false): string = +proc getCompileCFileCmd*(cfilename: string, isExternal = false): string = var c = cCompiler var options = cFileSpecificOptions(cfilename) var exe = getConfigVar(c, ".exe") if exe.len == 0: exe = c.getCompilerExe - + if needsExeExt(): exe = addFileExt(exe, "exe") if optGenDynLib in gGlobalOptions and ospNeedsPIC in platform.OS[targetOS].props: add(options, ' ' & CC[c].pic) - + var includeCmd, compilePattern: string if not noAbsolutePaths(): # compute include paths: @@ -551,7 +551,7 @@ proc getCompileCFileCmd*(cfilename: string, isExternal = false): string = else: includeCmd = "" compilePattern = c.getCompilerExe - + var cfile = if noAbsolutePaths(): extractFilename(cfilename) else: cfilename var objfile = if not isExternal or noAbsolutePaths(): @@ -580,14 +580,14 @@ proc footprint(filename: string): TCrc32 = extccomp.CC[extccomp.cCompiler].name >< getCompileCFileCmd(filename, true) -proc externalFileChanged(filename: string): bool = +proc externalFileChanged(filename: string): bool = if gCmd notin {cmdCompileToC, cmdCompileToCpp, cmdCompileToOC, cmdCompileToLLVM}: return false var crcFile = toGeneratedFile(filename.withPackageName, "crc") var currentCrc = int(footprint(filename)) var f: File - if open(f, crcFile, fmRead): + if open(f, crcFile, fmRead): var line = newStringOfCap(40) if not f.readLine(line): line = "0" close(f) @@ -595,7 +595,7 @@ proc externalFileChanged(filename: string): bool = result = oldCrc != currentCrc else: result = true - if result: + if result: if open(f, crcFile, fmWrite): f.writeln($currentCrc) close(f) @@ -604,41 +604,41 @@ proc addExternalFileToCompile*(filename: string) = if optForceFullMake in gGlobalOptions or externalFileChanged(filename): appendStr(externalToCompile, filename) -proc compileCFile(list: TLinkedList, script: var PRope, cmds: var TStringSeq, +proc compileCFile(list: TLinkedList, script: var Rope, cmds: var TStringSeq, prettyCmds: var TStringSeq, isExternal: bool) = var it = PStrEntry(list.head) - while it != nil: + while it != nil: inc(fileCounter) # call the C compiler for the .c file: var compileCmd = getCompileCFileCmd(it.data, isExternal) - if optCompileOnly notin gGlobalOptions: + if optCompileOnly notin gGlobalOptions: add(cmds, compileCmd) let (dir, name, ext) = splitFile(it.data) add(prettyCmds, "CC: " & name) - if optGenScript in gGlobalOptions: - app(script, compileCmd) - app(script, tnl) + if optGenScript in gGlobalOptions: + add(script, compileCmd) + add(script, tnl) it = PStrEntry(it.next) proc callCCompiler*(projectfile: string) = - var + var linkCmd, buildgui, builddll: string - if gGlobalOptions * {optCompileOnly, optGenScript} == {optCompileOnly}: + if gGlobalOptions * {optCompileOnly, optGenScript} == {optCompileOnly}: return # speed up that call if only compiling and no script shall be # generated fileCounter = 0 var c = cCompiler - var script: PRope = nil + var script: Rope = nil var cmds: TStringSeq = @[] var prettyCmds: TStringSeq = @[] let prettyCb = proc (idx: int) = echo prettyCmds[idx] compileCFile(toCompile, script, cmds, prettyCmds, false) compileCFile(externalToCompile, script, cmds, prettyCmds, true) - if optCompileOnly notin gGlobalOptions: + if optCompileOnly notin gGlobalOptions: if gNumberOfProcessors == 0: gNumberOfProcessors = countProcessors() var res = 0 - if gNumberOfProcessors <= 1: - for i in countup(0, high(cmds)): + if gNumberOfProcessors <= 1: + for i in countup(0, high(cmds)): res = execWithEcho(cmds[i]) if res != 0: rawMessage(errExecutionOfProgramFailed, []) elif optListCmd in gGlobalOptions or gVerbosity > 1: @@ -668,7 +668,8 @@ proc callCCompiler*(projectfile: string) = it = PStrEntry(it.next) 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] else: var linkerExe = getConfigVar(c, ".linkerexe") @@ -685,13 +686,13 @@ proc callCCompiler*(projectfile: string) = else: exefile = splitFile(projectfile).name & platform.OS[targetOS].exeExt builddll = "" - if options.outFile.len > 0: + if options.outFile.len > 0: exefile = options.outFile.expandTilde if not noAbsolutePaths(): if not exefile.isAbsolute(): exefile = joinPath(splitFile(projectfile).dir, exefile) exefile = quoteShell(exefile) - let linkOptions = getLinkOptions() & " " & + let linkOptions = getLinkOptions() & " " & getConfigVar(cCompiler, ".options.linker") linkCmd = quoteShell(linkCmd % ["builddll", builddll, "buildgui", buildgui, "options", linkOptions, "objfiles", objfiles, @@ -710,30 +711,30 @@ proc callCCompiler*(projectfile: string) = else: linkCmd = "" if optGenScript in gGlobalOptions: - app(script, linkCmd) - app(script, tnl) + add(script, linkCmd) + add(script, tnl) generateScript(projectfile, script) -proc genMappingFiles(list: TLinkedList): PRope = +proc genMappingFiles(list: TLinkedList): Rope = var it = PStrEntry(list.head) - while it != nil: - appf(result, "--file:r\"$1\"$N", [toRope(it.data)]) + while it != nil: + addf(result, "--file:r\"$1\"$N", [rope(it.data)]) it = PStrEntry(it.next) -proc writeMapping*(gSymbolMapping: PRope) = - if optGenMapping notin gGlobalOptions: return - var code = toRope("[C_Files]\n") - app(code, genMappingFiles(toCompile)) - app(code, genMappingFiles(externalToCompile)) - app(code, "\n[C_Compiler]\nFlags=") - app(code, strutils.escape(getCompileOptions())) - - app(code, "\n[Linker]\nFlags=") - app(code, strutils.escape(getLinkOptions() & " " & +proc writeMapping*(gSymbolMapping: Rope) = + if optGenMapping notin gGlobalOptions: return + var code = rope("[C_Files]\n") + add(code, genMappingFiles(toCompile)) + add(code, genMappingFiles(externalToCompile)) + add(code, "\n[C_Compiler]\nFlags=") + add(code, strutils.escape(getCompileOptions())) + + add(code, "\n[Linker]\nFlags=") + add(code, strutils.escape(getLinkOptions() & " " & getConfigVar(cCompiler, ".options.linker"))) - app(code, "\n[Environment]\nlibpath=") - app(code, strutils.escape(libpath)) - - appf(code, "\n[Symbols]$n$1", [gSymbolMapping]) + add(code, "\n[Environment]\nlibpath=") + add(code, strutils.escape(libpath)) + + addf(code, "\n[Symbols]$n$1", [gSymbolMapping]) writeRope(code, joinPath(gProjectPath, "mapping.txt")) diff --git a/compiler/guards.nim b/compiler/guards.nim index cedd2be2b..df2c1dd75 100644 --- a/compiler/guards.nim +++ b/compiler/guards.nim @@ -22,7 +22,8 @@ const someLt = {mLtI, mLtI64, mLtF64, mLtU, mLtU64, mLtEnum, mLtCh, mLtB, mLtPtr, mLtStr} - someLen = {mLengthOpenArray, mLengthStr, mLengthArray, mLengthSeq} + someLen = {mLengthOpenArray, mLengthStr, mLengthArray, mLengthSeq, + mXLenStr, mXLenSeq} someIn = {mInRange, mInSet} @@ -34,8 +35,8 @@ const someMul = {mMulI, mMulI64, mMulF64} someDiv = {mDivI, mDivI64, mDivF64} someMod = {mModI, mModI64} - someMax = {mMaxI, mMaxI64, mMaxF64} - someMin = {mMinI, mMinI64, mMinF64} + someMax = {mMaxI, mMaxF64} + someMin = {mMinI, mMinF64} proc isValue(n: PNode): bool = n.kind in {nkCharLit..nkNilLit} proc isLocation(n: PNode): bool = not n.isValue @@ -122,7 +123,7 @@ proc neg(n: PNode): PNode = let eAsNode = newIntNode(nkIntLit, e.sym.position) if not inSet(n.sons[1], eAsNode): s.add eAsNode result.sons[1] = s - elif lengthOrd(t) < 1000: + elif t.kind notin {tyString, tySequence} and lengthOrd(t) < 1000: result.sons[1] = complement(n.sons[1]) else: # not ({2, 3, 4}.contains(x)) x != 2 and x != 3 and x != 4 @@ -907,5 +908,5 @@ proc buildProperFieldCheck(access, check: PNode): PNode = proc checkFieldAccess*(m: TModel, n: PNode) = for i in 1..n.len-1: let check = buildProperFieldCheck(n.sons[0], n.sons[i]) - if m.doesImply(check) != impYes: + if check != nil and m.doesImply(check) != impYes: message(n.info, warnProveField, renderTree(n.sons[0])); break diff --git a/compiler/installer.ini b/compiler/installer.ini index b4160cab3..fff82cb5b 100644 --- a/compiler/installer.ini +++ b/compiler/installer.ini @@ -47,7 +47,7 @@ Start: "doc/overview.html" [Other] Files: "readme.txt;install.txt;contributors.txt;copying.txt" -Files: "configure;makefile" +Files: "makefile" Files: "*.ini" Files: "koch.nim" @@ -62,7 +62,7 @@ Files: "icons/koch_icon.o" Files: "compiler/readme.txt" Files: "compiler/installer.ini" -Files: "compiler/nim.nimrod.cfg" +Files: "compiler/nim.nim.cfg" Files: "compiler/*.nim" Files: "doc/*.txt" Files: "doc/manual/*.txt" @@ -70,6 +70,10 @@ Files: "doc/*.nim" Files: "doc/*.cfg" Files: "compiler/nimfix/*.nim" 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/*.cfg" Files: "tools/*.tmpl" @@ -97,13 +101,8 @@ Files: "lib/pure/concurrency/*.cfg" Files: "lib/impure/*.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/sdl/*.nim" -Files: "lib/wrappers/x11/*.nim" Files: "lib/wrappers/zip/*.nim" Files: "lib/wrappers/zip/libzip_all.c" @@ -115,8 +114,6 @@ Files: "lib/packages/docutils/*.nim" [Other] Files: "examples/*.nim" -Files: "examples/gtk/*.nim" -Files: "examples/0mq/*.nim" Files: "examples/c++iface/*.nim" Files: "examples/objciface/*.nim" Files: "examples/cross_calculator/" @@ -126,12 +123,109 @@ Files: "examples/*.txt" Files: "examples/*.cfg" Files: "examples/*.tmpl" +Files: "tests/actiontable/*.nim" +Files: "tests/alias/*.nim" +Files: "tests/ambsym/*.nim" +Files: "tests/array/*.nim" +Files: "tests/assign/*.nim" +Files: "tests/astoverload/*.nim" +Files: "tests/async/*.nim" +Files: "tests/benchmarks/*.nim" +Files: "tests/bind/*.nim" +Files: "tests/borrow/*.nim" +Files: "tests/casestmt/*.nim" +Files: "tests/ccgbugs/*.nim" +Files: "tests/clearmsg/*.nim" +Files: "tests/closure/*.nim" +Files: "tests/cnstseq/*.nim" +Files: "tests/collections/*.nim" +Files: "tests/compiles/*.nim" +Files: "tests/concat/*.nim" +Files: "tests/concepts/*.nim" +Files: "tests/constr/*.nim" +Files: "tests/constraints/*.nim" +Files: "tests/controlflow/*.nim" +Files: "tests/converter/*.nim" +Files: "tests/cpp/*.nim" +Files: "tests/defaultprocparam/*.nim" +Files: "tests/deprecated/*.nim" +Files: "tests/destructor/*.nim" +Files: "tests/dir with space/*.nim" +Files: "tests/discard/*.nim" +Files: "tests/distinct/*.nim" +Files: "tests/dll/*.nim" +Files: "tests/effects/*.nim" +Files: "tests/enum/*.nim" +Files: "tests/exception/*.nim" +Files: "tests/exprs/*.nim" +Files: "tests/fields/*.nim" +Files: "tests/float/*.nim" +Files: "tests/friends/*.nim" +Files: "tests/gc/*.nim" +Files: "tests/generics/*.nim" +Files: "tests/gensym/*.nim" +Files: "tests/global/*.nim" +Files: "tests/implicit/*.nim" +Files: "tests/init/*.nim" +Files: "tests/iter/*.nim" +Files: "tests/js/*.nim" +Files: "tests/js/*.cfg" +Files: "tests/let/*.nim" +Files: "tests/lexer/*.nim" +Files: "tests/lookups/*.nim" +Files: "tests/macros/*.nim" +Files: "tests/magics/*.nim" +Files: "tests/metatype/*.nim" +Files: "tests/method/*.nim" +Files: "tests/misc/*.nim" +Files: "tests/modules/*.nim" +Files: "tests/namedparams/*.nim" +Files: "tests/notnil/*.nim" +Files: "tests/objects/*.nim" +Files: "tests/objvariant/*.nim" +Files: "tests/openarray/*.nim" +Files: "tests/osproc/*.nim" +Files: "tests/overflw/*.nim" +Files: "tests/overload/*.nim" +Files: "tests/parallel/*.nim" +Files: "tests/parallel/*.cfg" +Files: "tests/parser/*.nim" +Files: "tests/pragmas/*.nim" +Files: "tests/proc/*.nim" +Files: "tests/procvar/*.nim" +Files: "tests/range/*.nim" +Files: "tests/rodfiles/*.nim" +Files: "tests/seq/*.nim" +Files: "tests/sets/*.nim" +Files: "tests/showoff/*.nim" +Files: "tests/specialops/*.nim" +Files: "tests/stdlib/*.nim" +Files: "tests/system/*.nim" +Files: "tests/template/*.nim" +Files: "tests/testament/*.nim" +Files: "tests/testdata/*.nim" +Files: "tests/threads/*.nim" +Files: "tests/threads/*.cfg" +Files: "tests/trmacros/*.nim" +Files: "tests/tuples/*.nim" +Files: "tests/typerel/*.nim" +Files: "tests/types/*.nim" +Files: "tests/usingstmt/*.nim" +Files: "tests/varres/*.nim" +Files: "tests/varstmt/*.nim" +Files: "tests/vm/*.nim" +Files: "tests/readme.txt" +Files: "tests/testament/css/*.css" +Files: "tests/testament/*.cfg" +Files: "lib/pure/unidecode/unidecode.dat" [Windows] Files: "bin/nim.exe" -Files: "bin/nim_debug.exe" Files: "bin/c2nim.exe" Files: "bin/nimgrep.exe" +Files: "bin/nimsuggest.exe" +Files: "bin/nimble.exe" +Files: "bin/*.dll" Files: "dist/*.dll" Files: "koch.exe" @@ -142,7 +236,7 @@ BinPath: r"bin;dist\mingw\bin;dist" ; Section | dir | zipFile | size hint (in KB) | url | exe start menu entry Download: r"Documentation|doc|docs.zip|13824|http://nim-lang.org/download/docs-${version}.zip|overview.html" Download: r"C Compiler (MingW)|dist|mingw.zip|82944|http://nim-lang.org/download/${mingw}.zip" -Download: r"Aporia IDE|dist|aporia.zip|97997|http://nim-lang.org/download/aporia-0.1.3.zip|aporia\bin\aporia.exe" +Download: r"Aporia IDE|dist|aporia.zip|97997|http://nim-lang.org/download/aporia-0.3.0.zip|aporia-0.3.0\bin\aporia.exe" ; for now only NSIS supports optional downloads [UnixBin] diff --git a/compiler/jsgen.nim b/compiler/jsgen.nim index 563f0c866..704713243 100644 --- a/compiler/jsgen.nim +++ b/compiler/jsgen.nim @@ -59,9 +59,9 @@ type TCompRes = object kind: TResKind typ: TJSTypeKind - res: PRope # result part; index if this is an + res: Rope # result part; index if this is an # (address, index)-tuple - address: PRope # address of an (address, index)-tuple + address: Rope # address of an (address, index)-tuple TBlock = object id: int # the ID of the label; positive means that it @@ -69,7 +69,7 @@ type isLoop: bool # whether it's a 'block' or 'while' TGlobals = object - typeInfo, code: PRope + typeInfo, code: Rope forwarded: seq[PSym] generatedSyms: IntSet typeInfoGenerated: IntSet @@ -79,7 +79,7 @@ type TProc = object procDef: PNode prc: PSym - locals, body: PRope + locals, body: Rope options: TOptions module: BModule g: PGlobals @@ -104,13 +104,13 @@ proc initCompRes(r: var TCompRes) = r.typ = etyNone r.kind = resNone -proc rdLoc(a: TCompRes): PRope {.inline.} = +proc rdLoc(a: TCompRes): Rope {.inline.} = result = a.res when false: if a.typ != etyBaseIndex: result = a.res else: - result = ropef("$1[$2]", a.address, a.res) + result = "$1[$2]" % [a.address, a.res] proc newProc(globals: PGlobals, module: BModule, procDef: PNode, options: TOptions): PProc = @@ -124,7 +124,7 @@ proc newProc(globals: PGlobals, module: BModule, procDef: PNode, const MappedToObject = {tyObject, tyArray, tyArrayConstr, tyTuple, tyOpenArray, - tySet, tyVar, tyRef, tyPtr, tyBigNum, tyVarargs} + tySet, tyBigNum, tyVarargs} proc mapType(typ: PType): TJSTypeKind = let t = skipTypes(typ, abstractInst) @@ -155,21 +155,22 @@ proc mapType(typ: PType): TJSTypeKind = of tyProc: result = etyProc of tyCString: result = etyString -proc mangleName(s: PSym): PRope = +proc mangleName(s: PSym): Rope = result = s.loc.r if result == nil: - result = toRope(mangle(s.name.s)) - app(result, "_") - app(result, toRope(s.id)) + result = rope(mangle(s.name.s)) + add(result, "_") + add(result, rope(s.id)) s.loc.r = result -proc makeJSString(s: string): PRope = strutils.escape(s).toRope +proc makeJSString(s: string): Rope = + (if s.isNil: "null".rope else: strutils.escape(s).rope) include jstypes proc gen(p: PProc, n: PNode, r: var TCompRes) proc genStmt(p: PProc, n: PNode) -proc genProc(oldProc: PProc, prc: PSym): PRope +proc genProc(oldProc: PProc, prc: PSym): Rope proc genConstant(p: PProc, c: PSym) proc useMagic(p: PProc, name: string) = @@ -178,7 +179,7 @@ proc useMagic(p: PProc, name: string) = if s != nil: internalAssert s.kind in {skProc, skMethod, skConverter} if not p.g.generatedSyms.containsOrIncl(s.id): - app(p.g.code, genProc(p, s)) + add(p.g.code, genProc(p, s)) else: # we used to exclude the system module from this check, but for DLL # generation support this sloppyness leads to hard to detect bugs, so @@ -196,10 +197,10 @@ proc isSimpleExpr(n: PNode): bool = elif n.isAtom: result = true -proc getTemp(p: PProc): PRope = +proc getTemp(p: PProc): Rope = inc(p.unique) - result = ropef("Tmp$1", [toRope(p.unique)]) - appf(p.locals, "var $1;$n" | "local $1;$n", [result]) + result = "Tmp$1" % [rope(p.unique)] + addf(p.locals, "var $1;$n" | "local $1;$n", [result]) proc genAnd(p: PProc, a, b: PNode, r: var TCompRes) = assert r.kind == resNone @@ -208,7 +209,7 @@ proc genAnd(p: PProc, a, b: PNode, r: var TCompRes) = gen(p, a, x) gen(p, b, y) r.kind = resExpr - r.res = ropef("($1 && $2)" | "($1 and $2)", [x.rdLoc, y.rdLoc]) + r.res = ("($1 && $2)" | "($1 and $2)") % [x.rdLoc, y.rdLoc] else: r.res = p.getTemp r.kind = resVal @@ -222,11 +223,11 @@ proc genAnd(p: PProc, a, b: PNode, r: var TCompRes) = # tmp = b # tmp gen(p, a, x) - p.body.appf("if (!$1) $2 = false; else {" | - "if not $1 then $2 = false; else", x.rdLoc, r.rdLoc) + p.body.addf("if (!$1) $2 = false; else {" | + "if not $1 then $2 = false; else", [x.rdLoc, r.rdLoc]) gen(p, b, y) - p.body.appf("$2 = $1; }" | - "$2 = $1 end", y.rdLoc, r.rdLoc) + p.body.addf("$2 = $1; }" | + "$2 = $1 end", [y.rdLoc, r.rdLoc]) proc genOr(p: PProc, a, b: PNode, r: var TCompRes) = assert r.kind == resNone @@ -235,16 +236,16 @@ proc genOr(p: PProc, a, b: PNode, r: var TCompRes) = gen(p, a, x) gen(p, b, y) r.kind = resExpr - r.res = ropef("($1 || $2)" | "($1 or $2)", [x.rdLoc, y.rdLoc]) + r.res = ("($1 || $2)" | "($1 or $2)") % [x.rdLoc, y.rdLoc] else: r.res = p.getTemp r.kind = resVal gen(p, a, x) - p.body.appf("if ($1) $2 = true; else {" | - "if $1 then $2 = true; else", x.rdLoc, r.rdLoc) + p.body.addf("if ($1) $2 = true; else {" | + "if $1 then $2 = true; else", [x.rdLoc, r.rdLoc]) gen(p, b, y) - p.body.appf("$2 = $1; }" | - "$2 = $1 end", y.rdLoc, r.rdLoc) + p.body.addf("$2 = $1; }" | + "$2 = $1 end", [y.rdLoc, r.rdLoc]) type TMagicFrmt = array[0..3, string] @@ -280,8 +281,6 @@ const # magic checked op; magic unchecked op; checked op; unchecked op ["", "", "($1 & $2)", "($1 & $2)"], # BitandI64 ["", "", "($1 | $2)", "($1 | $2)"], # BitorI64 ["", "", "($1 ^ $2)", "($1 ^ $2)"], # BitxorI64 - ["nimMin", "nimMin", "nimMin($1, $2)", "nimMin($1, $2)"], # MinI64 - ["nimMax", "nimMax", "nimMax($1, $2)", "nimMax($1, $2)"], # MaxI64 ["nimMin", "nimMin", "nimMin($1, $2)", "nimMin($1, $2)"], # MinF64 ["nimMax", "nimMax", "nimMax($1, $2)", "nimMax($1, $2)"], # MaxF64 ["addU", "addU", "addU($1, $2)", "addU($1, $2)"], # addU @@ -325,7 +324,6 @@ const # magic checked op; magic unchecked op; checked op; unchecked op ["", "", "!($1)", "!($1)"], # Not ["", "", "+($1)", "+($1)"], # UnaryPlusI ["", "", "~($1)", "~($1)"], # BitnotI - ["", "", "+($1)", "+($1)"], # UnaryPlusI64 ["", "", "~($1)", "~($1)"], # BitnotI64 ["", "", "+($1)", "+($1)"], # UnaryPlusF64 ["", "", "-($1)", "-($1)"], # UnaryMinusF64 @@ -382,8 +380,6 @@ const # magic checked op; magic unchecked op; checked op; unchecked op ["", "", "($1 & $2)", "($1 & $2)"], # BitandI64 ["", "", "($1 | $2)", "($1 | $2)"], # BitorI64 ["", "", "($1 ^ $2)", "($1 ^ $2)"], # BitxorI64 - ["nimMin", "nimMin", "nimMin($1, $2)", "nimMin($1, $2)"], # MinI64 - ["nimMax", "nimMax", "nimMax($1, $2)", "nimMax($1, $2)"], # MaxI64 ["nimMin", "nimMin", "nimMin($1, $2)", "nimMin($1, $2)"], # MinF64 ["nimMax", "nimMax", "nimMax($1, $2)", "nimMax($1, $2)"], # MaxF64 ["addU", "addU", "addU($1, $2)", "addU($1, $2)"], # addU @@ -427,7 +423,6 @@ const # magic checked op; magic unchecked op; checked op; unchecked op ["", "", "not ($1)", "not ($1)"], # Not ["", "", "+($1)", "+($1)"], # UnaryPlusI ["", "", "~($1)", "~($1)"], # BitnotI - ["", "", "+($1)", "+($1)"], # UnaryPlusI64 ["", "", "~($1)", "~($1)"], # BitnotI64 ["", "", "+($1)", "+($1)"], # UnaryPlusF64 ["", "", "-($1)", "-($1)"], # UnaryMinusF64 @@ -460,7 +455,7 @@ proc binaryExpr(p: PProc, n: PNode, r: var TCompRes, magic, frmt: string) = useMagic(p, magic) gen(p, n.sons[1], x) gen(p, n.sons[2], y) - r.res = ropef(frmt, [x.rdLoc, y.rdLoc]) + r.res = frmt % [x.rdLoc, y.rdLoc] r.kind = resExpr proc ternaryExpr(p: PProc, n: PNode, r: var TCompRes, magic, frmt: string) = @@ -469,13 +464,13 @@ proc ternaryExpr(p: PProc, n: PNode, r: var TCompRes, magic, frmt: string) = gen(p, n.sons[1], x) gen(p, n.sons[2], y) gen(p, n.sons[3], z) - r.res = ropef(frmt, [x.rdLoc, y.rdLoc, z.rdLoc]) + r.res = frmt % [x.rdLoc, y.rdLoc, z.rdLoc] r.kind = resExpr proc unaryExpr(p: PProc, n: PNode, r: var TCompRes, magic, frmt: string) = useMagic(p, magic) gen(p, n.sons[1], r) - r.res = ropef(frmt, [r.rdLoc]) + r.res = frmt % [r.rdLoc] r.kind = resExpr proc arithAux(p: PProc, n: PNode, r: var TCompRes, op: TMagic, ops: TMagicOps) = @@ -486,10 +481,10 @@ proc arithAux(p: PProc, n: PNode, r: var TCompRes, op: TMagic, ops: TMagicOps) = if sonsLen(n) > 2: gen(p, n.sons[1], x) gen(p, n.sons[2], y) - r.res = ropef(ops[op][i + 2], [x.rdLoc, y.rdLoc]) + r.res = ops[op][i + 2] % [x.rdLoc, y.rdLoc] else: gen(p, n.sons[1], r) - r.res = ropef(ops[op][i + 2], [r.rdLoc]) + r.res = ops[op][i + 2] % [r.rdLoc] r.kind = resExpr proc arith(p: PProc, n: PNode, r: var TCompRes, op: TMagic) = @@ -498,16 +493,16 @@ proc arith(p: PProc, n: PNode, r: var TCompRes, op: TMagic) = proc genLineDir(p: PProc, n: PNode) = let line = toLinenumber(n.info) if optLineDir in p.options: - appf(p.body, "// line $2 \"$1\"$n" | "-- line $2 \"$1\"$n", - [toRope(toFilename(n.info)), toRope(line)]) + addf(p.body, "// line $2 \"$1\"$n" | "-- line $2 \"$1\"$n", + [rope(toFilename(n.info)), rope(line)]) if {optStackTrace, optEndb} * p.options == {optStackTrace, optEndb} and ((p.prc == nil) or sfPure notin p.prc.flags): useMagic(p, "endb") - appf(p.body, "endb($1);$n", [toRope(line)]) + addf(p.body, "endb($1);$n", [rope(line)]) elif ({optLineTrace, optStackTrace} * p.options == {optLineTrace, optStackTrace}) and ((p.prc == nil) or not (sfPure in p.prc.flags)): - appf(p.body, "F.line = $1;$n", [toRope(line)]) + addf(p.body, "F.line = $1;$n", [rope(line)]) proc genWhileStmt(p: PProc, n: PNode) = var @@ -519,21 +514,21 @@ proc genWhileStmt(p: PProc, n: PNode) = setLen(p.blocks, length + 1) p.blocks[length].id = -p.unique p.blocks[length].isLoop = true - let labl = p.unique.toRope - appf(p.body, "L$1: while (true) {$n" | "while true do$n", labl) + let labl = p.unique.rope + addf(p.body, "L$1: while (true) {$n" | "while true do$n", [labl]) gen(p, n.sons[0], cond) - appf(p.body, "if (!$1) break L$2;$n" | "if not $1 then goto ::L$2:: end;$n", + addf(p.body, "if (!$1) break L$2;$n" | "if not $1 then goto ::L$2:: end;$n", [cond.res, labl]) genStmt(p, n.sons[1]) - appf(p.body, "}$n" | "end ::L$#::$n", [labl]) + addf(p.body, "}$n" | "end ::L$#::$n", [labl]) setLen(p.blocks, length) proc moveInto(p: PProc, src: var TCompRes, dest: TCompRes) = if src.kind != resNone: if dest.kind != resNone: - p.body.appf("$1 = $2;$n", dest.rdLoc, src.rdLoc) + p.body.addf("$1 = $2;$n", [dest.rdLoc, src.rdLoc]) else: - p.body.appf("$1;$n", src.rdLoc) + p.body.addf("$1;$n", [src.rdLoc]) src.kind = resNone src.res = nil @@ -562,52 +557,52 @@ proc genTry(p: PProc, n: PNode, r: var TCompRes) = r.kind = resVal r.res = getTemp(p) inc(p.unique) - var safePoint = ropef("Tmp$1", [toRope(p.unique)]) - appf(p.body, + var safePoint = "Tmp$1" % [rope(p.unique)] + addf(p.body, "var $1 = {prev: excHandler, exc: null};$nexcHandler = $1;$n" | "local $1 = pcall(", [safePoint]) - if optStackTrace in p.options: app(p.body, "framePtr = F;" & tnl) - appf(p.body, "try {$n" | "function()$n") + if optStackTrace in p.options: add(p.body, "framePtr = F;" & tnl) + addf(p.body, "try {$n" | "function()$n", []) var length = sonsLen(n) var a: TCompRes gen(p, n.sons[0], a) moveInto(p, a, r) var i = 1 if p.target == targetJS and length > 1 and n.sons[i].kind == nkExceptBranch: - appf(p.body, "} catch (EXC) {$n lastJSError = EXC;$n") + addf(p.body, "} catch (EXC) {$n lastJSError = EXC;$n", []) elif p.target == targetLua: - appf(p.body, "end)$n") + addf(p.body, "end)$n", []) while i < length and n.sons[i].kind == nkExceptBranch: let blen = sonsLen(n.sons[i]) if blen == 1: # general except section: - if i > 1: appf(p.body, "else {$n" | "else$n") + if i > 1: addf(p.body, "else {$n" | "else$n", []) gen(p, n.sons[i].sons[0], a) moveInto(p, a, r) - if i > 1: appf(p.body, "}$n" | "end$n") + if i > 1: addf(p.body, "}$n" | "end$n", []) else: - var orExpr: PRope = nil + var orExpr: Rope = nil useMagic(p, "isObj") for j in countup(0, blen - 2): if n.sons[i].sons[j].kind != nkType: internalError(n.info, "genTryStmt") - if orExpr != nil: app(orExpr, "||" | " or ") - appf(orExpr, "isObj($1.exc.m_type, $2)", + if orExpr != nil: add(orExpr, "||" | " or ") + addf(orExpr, "isObj($1.exc.m_type, $2)", [safePoint, genTypeInfo(p, n.sons[i].sons[j].typ)]) - if i > 1: app(p.body, "else ") - appf(p.body, "if ($1.exc && ($2)) {$n" | "if $1.exc and ($2) then$n", + if i > 1: add(p.body, "else ") + addf(p.body, "if ($1.exc && ($2)) {$n" | "if $1.exc and ($2) then$n", [safePoint, orExpr]) gen(p, n.sons[i].sons[blen - 1], a) moveInto(p, a, r) - appf(p.body, "}$n" | "end$n") + addf(p.body, "}$n" | "end$n", []) inc(i) if p.target == targetJS: - app(p.body, "} finally {" & tnl & "excHandler = excHandler.prev;" & tnl) + add(p.body, "} finally {" & tnl & "excHandler = excHandler.prev;" & tnl) if i < length and n.sons[i].kind == nkFinally: genStmt(p, n.sons[i].sons[0]) if p.target == targetJS: - app(p.body, "}" & tnl) + add(p.body, "}" & tnl) if p.target == targetLua: # we need to repeat the finally block for Lua ... if i < length and n.sons[i].kind == nkFinally: @@ -620,11 +615,11 @@ proc genRaiseStmt(p: PProc, n: PNode) = gen(p, n.sons[0], a) let typ = skipTypes(n.sons[0].typ, abstractPtrs) useMagic(p, "raiseException") - appf(p.body, "raiseException($1, $2);$n", + addf(p.body, "raiseException($1, $2);$n", [a.rdLoc, makeJSString(typ.sym.name.s)]) else: useMagic(p, "reraiseException") - app(p.body, "reraiseException();" & tnl) + add(p.body, "reraiseException();" & tnl) proc genCaseJS(p: PProc, n: PNode, r: var TCompRes) = var @@ -634,9 +629,9 @@ proc genCaseJS(p: PProc, n: PNode, r: var TCompRes) = let stringSwitch = skipTypes(n.sons[0].typ, abstractVar).kind == tyString if stringSwitch: useMagic(p, "toJSStr") - appf(p.body, "switch (toJSStr($1)) {$n", [cond.rdLoc]) + addf(p.body, "switch (toJSStr($1)) {$n", [cond.rdLoc]) else: - appf(p.body, "switch ($1) {$n", [cond.rdLoc]) + addf(p.body, "switch ($1) {$n", [cond.rdLoc]) if not isEmptyType(n.typ): r.kind = resVal r.res = getTemp(p) @@ -650,27 +645,27 @@ proc genCaseJS(p: PProc, n: PNode, r: var TCompRes) = var v = copyNode(e.sons[0]) while v.intVal <= e.sons[1].intVal: gen(p, v, cond) - appf(p.body, "case $1: ", [cond.rdLoc]) + addf(p.body, "case $1: ", [cond.rdLoc]) inc(v.intVal) else: if stringSwitch: case e.kind - of nkStrLit..nkTripleStrLit: appf(p.body, "case $1: ", + of nkStrLit..nkTripleStrLit: addf(p.body, "case $1: ", [makeJSString(e.strVal)]) else: internalError(e.info, "jsgen.genCaseStmt: 2") else: gen(p, e, cond) - appf(p.body, "case $1: ", [cond.rdLoc]) + addf(p.body, "case $1: ", [cond.rdLoc]) gen(p, lastSon(it), stmt) moveInto(p, stmt, r) - appf(p.body, "$nbreak;$n") + addf(p.body, "$nbreak;$n", []) of nkElse: - appf(p.body, "default: $n") + addf(p.body, "default: $n", []) gen(p, it.sons[0], stmt) moveInto(p, stmt, r) - appf(p.body, "break;$n") + addf(p.body, "break;$n", []) else: internalError(it.info, "jsgen.genCaseStmt") - appf(p.body, "}$n") + addf(p.body, "}$n", []) proc genCaseLua(p: PProc, n: PNode, r: var TCompRes) = var @@ -681,7 +676,7 @@ proc genCaseLua(p: PProc, n: PNode, r: var TCompRes) = if stringSwitch: useMagic(p, "eqStr") let tmp = getTemp(p) - appf(p.body, "$1 = $2;$n", [tmp, cond.rdLoc]) + addf(p.body, "$1 = $2;$n", [tmp, cond.rdLoc]) if not isEmptyType(n.typ): r.kind = resVal r.res = getTemp(p) @@ -689,34 +684,34 @@ proc genCaseLua(p: PProc, n: PNode, r: var TCompRes) = let it = n.sons[i] case it.kind of nkOfBranch: - if i != 1: appf(p.body, "$nelsif ") - else: appf(p.body, "if ") + if i != 1: addf(p.body, "$nelsif ", []) + else: addf(p.body, "if ", []) for j in countup(0, sonsLen(it) - 2): - if j != 0: app(p.body, " or ") + if j != 0: add(p.body, " or ") let e = it.sons[j] if e.kind == nkRange: var ia, ib: TCompRes gen(p, e.sons[0], ia) gen(p, e.sons[1], ib) - appf(p.body, "$1 >= $2 and $1 <= $3", [tmp, ia.rdLoc, ib.rdLoc]) + addf(p.body, "$1 >= $2 and $1 <= $3", [tmp, ia.rdLoc, ib.rdLoc]) else: if stringSwitch: case e.kind - of nkStrLit..nkTripleStrLit: appf(p.body, "eqStr($1, $2)", + of nkStrLit..nkTripleStrLit: addf(p.body, "eqStr($1, $2)", [tmp, makeJSString(e.strVal)]) else: internalError(e.info, "jsgen.genCaseStmt: 2") else: gen(p, e, cond) - appf(p.body, "$1 == $2", [tmp, cond.rdLoc]) - appf(p.body, " then$n") + addf(p.body, "$1 == $2", [tmp, cond.rdLoc]) + addf(p.body, " then$n", []) gen(p, lastSon(it), stmt) moveInto(p, stmt, r) of nkElse: - appf(p.body, "else$n") + addf(p.body, "else$n", []) gen(p, it.sons[0], stmt) moveInto(p, stmt, r) else: internalError(it.info, "jsgen.genCaseStmt") - appf(p.body, "$nend$n") + addf(p.body, "$nend$n", []) proc genBlock(p: PProc, n: PNode, r: var TCompRes) = inc(p.unique) @@ -730,9 +725,9 @@ proc genBlock(p: PProc, n: PNode, r: var TCompRes) = setLen(p.blocks, idx + 1) p.blocks[idx].id = - p.unique # negative because it isn't used yet let labl = p.unique - appf(p.body, "L$1: do {$n" | "", labl.toRope) + addf(p.body, "L$1: do {$n" | "", [labl.rope]) gen(p, n.sons[1], r) - appf(p.body, "} while(false);$n" | "$n::L$#::$n", labl.toRope) + addf(p.body, "} while(false);$n" | "$n::L$#::$n", [labl.rope]) setLen(p.blocks, idx) proc genBreakStmt(p: PProc, n: PNode) = @@ -751,15 +746,15 @@ proc genBreakStmt(p: PProc, n: PNode) = if idx < 0 or not p.blocks[idx].isLoop: internalError(n.info, "no loop to break") p.blocks[idx].id = abs(p.blocks[idx].id) # label is used - appf(p.body, "break L$1;$n" | "goto ::L$1::;$n", [toRope(p.blocks[idx].id)]) + addf(p.body, "break L$1;$n" | "goto ::L$1::;$n", [rope(p.blocks[idx].id)]) proc genAsmStmt(p: PProc, n: PNode) = genLineDir(p, n) assert(n.kind == nkAsmStmt) for i in countup(0, sonsLen(n) - 1): case n.sons[i].kind - of nkStrLit..nkTripleStrLit: app(p.body, n.sons[i].strVal) - of nkSym: app(p.body, mangleName(n.sons[i].sym)) + of nkStrLit..nkTripleStrLit: add(p.body, n.sons[i].strVal) + of nkSym: add(p.body, mangleName(n.sons[i].sym)) else: internalError(n.sons[i].info, "jsgen: genAsmStmt()") proc genIf(p: PProc, n: PNode, r: var TCompRes) = @@ -772,35 +767,35 @@ proc genIf(p: PProc, n: PNode, r: var TCompRes) = let it = n.sons[i] if sonsLen(it) != 1: if i > 0: - appf(p.body, "else {$n" | "else$n", []) + addf(p.body, "else {$n" | "else$n", []) inc(toClose) gen(p, it.sons[0], cond) - appf(p.body, "if ($1) {$n" | "if $# then$n", cond.rdLoc) + addf(p.body, "if ($1) {$n" | "if $# then$n", [cond.rdLoc]) gen(p, it.sons[1], stmt) else: # else part: - appf(p.body, "else {$n" | "else$n") + addf(p.body, "else {$n" | "else$n", []) gen(p, it.sons[0], stmt) moveInto(p, stmt, r) - appf(p.body, "}$n" | "end$n") + addf(p.body, "}$n" | "end$n", []) if p.target == targetJS: - app(p.body, repeat('}', toClose) & tnl) + add(p.body, repeat('}', toClose) & tnl) else: - for i in 1..toClose: appf(p.body, "end$n") + for i in 1..toClose: addf(p.body, "end$n", []) -proc generateHeader(p: PProc, typ: PType): PRope = +proc generateHeader(p: PProc, typ: PType): Rope = result = nil for i in countup(1, sonsLen(typ.n) - 1): - if result != nil: app(result, ", ") assert(typ.n.sons[i].kind == nkSym) var param = typ.n.sons[i].sym if isCompileTimeOnly(param.typ): continue + if result != nil: add(result, ", ") var name = mangleName(param) - app(result, name) + add(result, name) if mapType(param.typ) == etyBaseIndex: - app(result, ", ") - app(result, name) - app(result, "_Idx") + add(result, ", ") + add(result, name) + add(result, "_Idx") const nodeKindsNeedNoCopy = {nkCharLit..nkInt64Lit, nkStrLit..nkTripleStrLit, @@ -819,17 +814,17 @@ proc genAsgnAux(p: PProc, x, y: PNode, noCopyNeeded: bool) = case mapType(x.typ) of etyObject: if needsNoCopy(y) or noCopyNeeded: - appf(p.body, "$1 = $2;$n", [a.rdLoc, b.rdLoc]) + addf(p.body, "$1 = $2;$n", [a.rdLoc, b.rdLoc]) else: useMagic(p, "nimCopy") - appf(p.body, "$1 = nimCopy($2, $3);$n", + addf(p.body, "$1 = nimCopy($2, $3);$n", [a.res, b.res, genTypeInfo(p, y.typ)]) of etyBaseIndex: if a.typ != etyBaseIndex or b.typ != etyBaseIndex: internalError(x.info, "genAsgn") - appf(p.body, "$1 = $2; $3 = $4;$n", [a.address, b.address, a.res, b.res]) + addf(p.body, "$1 = $2; $3 = $4;$n", [a.address, b.address, a.res, b.res]) else: - appf(p.body, "$1 = $2;$n", [a.res, b.res]) + addf(p.body, "$1 = $2;$n", [a.res, b.res]) proc genAsgn(p: PProc, n: PNode) = genLineDir(p, n) @@ -844,17 +839,17 @@ proc genSwap(p: PProc, n: PNode) = gen(p, n.sons[1], a) gen(p, n.sons[2], b) inc(p.unique) - var tmp = ropef("Tmp$1", [toRope(p.unique)]) + var tmp = "Tmp$1" % [rope(p.unique)] if mapType(skipTypes(n.sons[1].typ, abstractVar)) == etyBaseIndex: inc(p.unique) - let tmp2 = ropef("Tmp$1", [toRope(p.unique)]) + let tmp2 = "Tmp$1" % [rope(p.unique)] if a.typ != etyBaseIndex or b.typ != etyBaseIndex: internalError(n.info, "genSwap") - appf(p.body, "var $1 = $2; $2 = $3; $3 = $1;$n" | + addf(p.body, "var $1 = $2; $2 = $3; $3 = $1;$n" | "local $1 = $2; $2 = $3; $3 = $1;$n", [ tmp, a.address, b.address]) tmp = tmp2 - appf(p.body, "var $1 = $2; $2 = $3; $3 = $1;" | + addf(p.body, "var $1 = $2; $2 = $3; $3 = $1;" | "local $1 = $2; $2 = $3; $3 = $1;", [tmp, a.res, b.res]) proc getFieldPosition(f: PNode): int = @@ -874,7 +869,7 @@ proc genFieldAddr(p: PProc, n: PNode, r: var TCompRes) = if b.sons[1].kind != nkSym: internalError(b.sons[1].info, "genFieldAddr") var f = b.sons[1].sym if f.loc.r == nil: f.loc.r = mangleName(f) - r.res = makeJSString(ropeToStr(f.loc.r)) + r.res = makeJSString($f.loc.r) internalAssert a.typ != etyBaseIndex r.address = a.res r.kind = resExpr @@ -883,12 +878,12 @@ proc genFieldAccess(p: PProc, n: PNode, r: var TCompRes) = r.typ = etyNone gen(p, n.sons[0], r) if skipTypes(n.sons[0].typ, abstractVarRange).kind == tyTuple: - r.res = ropef("$1.Field$2", [r.res, getFieldPosition(n.sons[1]).toRope]) + r.res = "$1.Field$2" % [r.res, getFieldPosition(n.sons[1]).rope] else: if n.sons[1].kind != nkSym: internalError(n.sons[1].info, "genFieldAddr") var f = n.sons[1].sym if f.loc.r == nil: f.loc.r = mangleName(f) - r.res = ropef("$1.$2", [r.res, f.loc.r]) + r.res = "$1.$2" % [r.res, f.loc.r] r.kind = resExpr proc genCheckedFieldAddr(p: PProc, n: PNode, r: var TCompRes) = @@ -914,10 +909,9 @@ proc genArrayAddr(p: PProc, n: PNode, r: var TCompRes) = else: first = 0 if optBoundsCheck in p.options and not isConstExpr(m.sons[1]): useMagic(p, "chckIndx") - r.res = ropef("chckIndx($1, $2, $3.length)-$2", - [b.res, toRope(first), a.res]) + r.res = "chckIndx($1, $2, $3.length)-$2" % [b.res, rope(first), a.res] elif first != 0: - r.res = ropef("($1)-$2", [b.res, toRope(first)]) + r.res = "($1)-$2" % [b.res, rope(first)] else: r.res = b.res r.kind = resExpr @@ -934,10 +928,17 @@ proc genArrayAccess(p: PProc, n: PNode, r: var TCompRes) = else: internalError(n.info, "expr(nkBracketExpr, " & $ty.kind & ')') r.typ = etyNone if r.res == nil: internalError(n.info, "genArrayAccess") - r.res = ropef("$1[$2]", [r.address, r.res]) + r.res = "$1[$2]" % [r.address, r.res] r.address = nil r.kind = resExpr +proc isIndirect(v: PSym): bool = + result = {sfAddrTaken, sfGlobal} * v.flags != {} and + #(mapType(v.typ) != etyObject) and + {sfImportc, sfVolatile, sfExportc} * v.flags == {} and + v.kind notin {skProc, skConverter, skMethod, skIterator, skClosureIterator, + skConst, skTemp, skLet} + proc genAddr(p: PProc, n: PNode, r: var TCompRes) = case n.sons[0].kind of nkSym: @@ -946,17 +947,21 @@ proc genAddr(p: PProc, n: PNode, r: var TCompRes) = case s.kind of skVar, skLet, skResult: r.kind = resExpr - if mapType(n.sons[0].typ) == etyObject: + let jsType = mapType(n.typ) + if jsType == etyObject: # make addr() a no-op: r.typ = etyNone - r.res = s.loc.r + if isIndirect(s): + r.res = s.loc.r & "[0]" + else: + r.res = s.loc.r r.address = nil - elif {sfGlobal, sfAddrTaken} * s.flags != {}: + elif {sfGlobal, sfAddrTaken} * s.flags != {} or jsType == etyBaseIndex: # for ease of code generation, we do not distinguish between # sfAddrTaken and sfGlobal. r.typ = etyBaseIndex r.address = s.loc.r - r.res = toRope("0") + r.res = rope("0") else: # 'var openArray' for instance produces an 'addr' but this is harmless: gen(p, n.sons[0], r) @@ -988,13 +993,13 @@ proc genSym(p: PProc, n: PNode, r: var TCompRes) = if k == etyBaseIndex: r.typ = etyBaseIndex if {sfAddrTaken, sfGlobal} * s.flags != {}: - r.address = ropef("$1[0]", [s.loc.r]) - r.res = ropef("$1[1]", [s.loc.r]) + r.address = "$1[0]" % [s.loc.r] + r.res = "$1[1]" % [s.loc.r] else: r.address = s.loc.r - r.res = con(s.loc.r, "_Idx") - elif k != etyObject and {sfAddrTaken, sfGlobal} * s.flags != {}: - r.res = ropef("$1[0]", [s.loc.r]) + r.res = s.loc.r & "_Idx" + elif isIndirect(s): + r.res = "$1[0]" % [s.loc.r] else: r.res = s.loc.r of skConst: @@ -1018,8 +1023,8 @@ proc genSym(p: PProc, n: PNode, r: var TCompRes) = var owner = p while owner != nil and owner.prc != s.owner: owner = owner.up - if owner != nil: app(owner.locals, newp) - else: app(p.g.code, newp) + if owner != nil: add(owner.locals, newp) + else: add(p.g.code, newp) else: if s.loc.r == nil: internalError(n.info, "symbol has no generated name: " & s.name.s) @@ -1033,26 +1038,26 @@ proc genDeref(p: PProc, n: PNode, r: var TCompRes) = var a: TCompRes gen(p, n.sons[0], a) if a.typ != etyBaseIndex: internalError(n.info, "genDeref") - r.res = ropef("$1[$2]", [a.address, a.res]) + r.res = "$1[$2]" % [a.address, a.res] proc genArg(p: PProc, n: PNode, r: var TCompRes) = var a: TCompRes gen(p, n, a) if a.typ == etyBaseIndex: - app(r.res, a.address) - app(r.res, ", ") - app(r.res, a.res) + add(r.res, a.address) + add(r.res, ", ") + add(r.res, a.res) else: - app(r.res, a.res) + add(r.res, a.res) proc genArgs(p: PProc, n: PNode, r: var TCompRes) = - app(r.res, "(") + add(r.res, "(") for i in countup(1, sonsLen(n) - 1): let it = n.sons[i] if it.typ.isCompileTimeOnly: continue - if i > 1: app(r.res, ", ") + if i > 1: add(r.res, ", ") genArg(p, it, r) - app(r.res, ")") + add(r.res, ")") r.kind = resExpr proc genCall(p: PProc, n: PNode, r: var TCompRes) = @@ -1064,58 +1069,58 @@ proc genInfixCall(p: PProc, n: PNode, r: var TCompRes) = if r.typ == etyBaseIndex: if r.address == nil: globalError(n.info, "cannot invoke with infix syntax") - r.res = ropef("$1[$2]", [r.address, r.res]) + r.res = "$1[$2]" % [r.address, r.res] r.address = nil r.typ = etyNone - app(r.res, ".") + add(r.res, ".") var op: TCompRes gen(p, n.sons[0], op) - app(r.res, op.res) + add(r.res, op.res) - app(r.res, "(") + add(r.res, "(") for i in countup(2, sonsLen(n) - 1): - if i > 2: app(r.res, ", ") + if i > 2: add(r.res, ", ") genArg(p, n.sons[i], r) - app(r.res, ")") + add(r.res, ")") r.kind = resExpr proc genEcho(p: PProc, n: PNode, r: var TCompRes) = useMagic(p, "rawEcho") - app(r.res, "rawEcho(") + add(r.res, "rawEcho(") let n = n[1].skipConv internalAssert n.kind == nkBracket for i in countup(0, sonsLen(n) - 1): let it = n.sons[i] if it.typ.isCompileTimeOnly: continue - if i > 0: app(r.res, ", ") + if i > 0: add(r.res, ", ") genArg(p, it, r) - app(r.res, ")") + add(r.res, ")") r.kind = resExpr -proc putToSeq(s: string, indirect: bool): PRope = - result = toRope(s) - if indirect: result = ropef("[$1]", [result]) +proc putToSeq(s: string, indirect: bool): Rope = + result = rope(s) + if indirect: result = "[$1]" % [result] -proc createVar(p: PProc, typ: PType, indirect: bool): PRope -proc createRecordVarAux(p: PProc, rec: PNode, c: var int): PRope = +proc createVar(p: PProc, typ: PType, indirect: bool): Rope +proc createRecordVarAux(p: PProc, rec: PNode, c: var int): Rope = result = nil case rec.kind of nkRecList: for i in countup(0, sonsLen(rec) - 1): - app(result, createRecordVarAux(p, rec.sons[i], c)) + add(result, createRecordVarAux(p, rec.sons[i], c)) of nkRecCase: - app(result, createRecordVarAux(p, rec.sons[0], c)) + add(result, createRecordVarAux(p, rec.sons[0], c)) for i in countup(1, sonsLen(rec) - 1): - app(result, createRecordVarAux(p, lastSon(rec.sons[i]), c)) + add(result, createRecordVarAux(p, lastSon(rec.sons[i]), c)) of nkSym: - if c > 0: app(result, ", ") - app(result, mangleName(rec.sym)) - app(result, ": ") - app(result, createVar(p, rec.sym.typ, false)) + if c > 0: add(result, ", ") + add(result, mangleName(rec.sym)) + add(result, ": ") + add(result, createVar(p, rec.sym.typ, false)) inc(c) else: internalError(rec.info, "createRecordVarAux") -proc createVar(p: PProc, typ: PType, indirect: bool): PRope = +proc createVar(p: PProc, typ: PType, indirect: bool): Rope = var t = skipTypes(typ, abstractInst) case t.kind of tyInt..tyInt64, tyUInt..tyUInt64, tyEnum, tyChar: @@ -1125,7 +1130,7 @@ proc createVar(p: PProc, typ: PType, indirect: bool): PRope = of tyRange, tyGenericInst: result = createVar(p, lastSon(typ), indirect) of tySet: - result = toRope("{}") + result = putToSeq("{}", indirect) of tyBool: result = putToSeq("false", indirect) of tyArray, tyArrayConstr: @@ -1135,33 +1140,36 @@ proc createVar(p: PProc, typ: PType, indirect: bool): PRope = useMagic(p, "arrayConstr") # XXX: arrayConstr depends on nimCopy. This line shouldn't be necessary. useMagic(p, "nimCopy") - result = ropef("arrayConstr($1, $2, $3)", [toRope(length), - createVar(p, e, false), genTypeInfo(p, e)]) + result = "arrayConstr($1, $2, $3)" % [rope(length), + createVar(p, e, false), genTypeInfo(p, e)] else: - result = toRope("[") + result = rope("[") var i = 0 while i < length: - if i > 0: app(result, ", ") - app(result, createVar(p, e, false)) + if i > 0: add(result, ", ") + add(result, createVar(p, e, false)) inc(i) - app(result, "]") + add(result, "]") + if indirect: result = "[$1]" % [result] of tyTuple: - result = toRope("{") + result = rope("{") for i in 0.. <t.sonsLen: - if i > 0: app(result, ", ") - appf(result, "Field$1: $2" | "Field$# = $#", i.toRope, - createVar(p, t.sons[i], false)) - app(result, "}") + if i > 0: add(result, ", ") + addf(result, "Field$1: $2" | "Field$# = $#", [i.rope, + createVar(p, t.sons[i], false)]) + add(result, "}") + if indirect: result = "[$1]" % [result] of tyObject: - result = toRope("{") + result = rope("{") var c = 0 if tfFinal notin t.flags or t.sons[0] != nil: inc(c) - appf(result, "m_type: $1" | "m_type = $#", [genTypeInfo(p, t)]) + addf(result, "m_type: $1" | "m_type = $#", [genTypeInfo(p, t)]) while t != nil: - app(result, createRecordVarAux(p, t.n, c)) + add(result, createRecordVarAux(p, t.n, c)) t = t.sons[0] - app(result, "}") + add(result, "}") + if indirect: result = "[$1]" % [result] of tyVar, tyPtr, tyRef: if mapType(t) == etyBaseIndex: result = putToSeq("[null, 0]" | "{nil, 0}", indirect) @@ -1173,17 +1181,12 @@ proc createVar(p: PProc, typ: PType, indirect: bool): PRope = internalError("createVar: " & $t.kind) result = nil -proc isIndirect(v: PSym): bool = - result = {sfAddrTaken, sfGlobal} * v.flags != {} and - (mapType(v.typ) != etyObject) and - v.kind notin {skProc, skConverter, skMethod, skIterator, skClosureIterator} - proc genVarInit(p: PProc, v: PSym, n: PNode) = var a: TCompRes - s: PRope + s: Rope if n.kind == nkEmpty: - appf(p.body, "var $1 = $2;$n" | "local $1 = $2;$n", + addf(p.body, "var $1 = $2;$n" | "local $1 = $2;$n", [mangleName(v), createVar(p, v.typ, isIndirect(v))]) else: discard mangleName(v) @@ -1194,23 +1197,23 @@ proc genVarInit(p: PProc, v: PSym, n: PNode) = s = a.res else: useMagic(p, "nimCopy") - s = ropef("nimCopy($1, $2)", [a.res, genTypeInfo(p, n.typ)]) + s = "nimCopy($1, $2)" % [a.res, genTypeInfo(p, n.typ)] of etyBaseIndex: if (a.typ != etyBaseIndex): internalError(n.info, "genVarInit") if {sfAddrTaken, sfGlobal} * v.flags != {}: - appf(p.body, "var $1 = [$2, $3];$n" | "local $1 = {$2, $3};$n", + addf(p.body, "var $1 = [$2, $3];$n" | "local $1 = {$2, $3};$n", [v.loc.r, a.address, a.res]) else: - appf(p.body, "var $1 = $2; var $1_Idx = $3;$n" | + addf(p.body, "var $1 = $2; var $1_Idx = $3;$n" | "local $1 = $2; local $1_Idx = $3;$n", [ v.loc.r, a.address, a.res]) return else: s = a.res if isIndirect(v): - appf(p.body, "var $1 = [$2];$n" | "local $1 = {$2};$n", [v.loc.r, s]) + addf(p.body, "var $1 = /**/[$2];$n" | "local $1 = {$2};$n", [v.loc.r, s]) else: - appf(p.body, "var $1 = $2;$n" | "local $1 = $2;$n", [v.loc.r, s]) + addf(p.body, "var $1 = $2;$n" | "local $1 = $2;$n", [v.loc.r, s]) proc genVarStmt(p: PProc, n: PNode) = for i in countup(0, sonsLen(n) - 1): @@ -1233,21 +1236,21 @@ proc genConstant(p: PProc, c: PSym) = p.body = nil #genLineDir(p, c.ast) genVarInit(p, c, c.ast) - app(p.g.code, p.body) + add(p.g.code, p.body) p.body = oldBody proc genNew(p: PProc, n: PNode) = var a: TCompRes gen(p, n.sons[1], a) var t = skipTypes(n.sons[1].typ, abstractVar).sons[0] - appf(p.body, "$1 = $2;$n", [a.res, createVar(p, t, true)]) + addf(p.body, "$1 = $2;$n", [a.res, createVar(p, t, false)]) proc genNewSeq(p: PProc, n: PNode) = var x, y: TCompRes gen(p, n.sons[1], x) gen(p, n.sons[2], y) let t = skipTypes(n.sons[1].typ, abstractVar).sons[0] - appf(p.body, "$1 = new Array($2); for (var i=0;i<$2;++i) {$1[i]=$3;}", [ + addf(p.body, "$1 = new Array($2); for (var i=0;i<$2;++i) {$1[i]=$3;}", [ x.rdLoc, y.rdLoc, createVar(p, t, false)]) proc genOrd(p: PProc, n: PNode, r: var TCompRes) = @@ -1262,22 +1265,22 @@ proc genConStrStr(p: PProc, n: PNode, r: var TCompRes) = gen(p, n.sons[1], a) r.kind = resExpr if skipTypes(n.sons[1].typ, abstractVarRange).kind == tyChar: - r.res.app(ropef("[$1].concat(", [a.res])) + r.res.add("[$1].concat(" % [a.res]) else: - r.res.app(ropef("($1.slice(0,-1)).concat(", [a.res])) + r.res.add("($1.slice(0,-1)).concat(" % [a.res]) for i in countup(2, sonsLen(n) - 2): gen(p, n.sons[i], a) if skipTypes(n.sons[i].typ, abstractVarRange).kind == tyChar: - r.res.app(ropef("[$1],", [a.res])) + r.res.add("[$1]," % [a.res]) else: - r.res.app(ropef("$1.slice(0,-1),", [a.res])) + r.res.add("$1.slice(0,-1)," % [a.res]) gen(p, n.sons[sonsLen(n) - 1], a) if skipTypes(n.sons[sonsLen(n) - 1].typ, abstractVarRange).kind == tyChar: - r.res.app(ropef("[$1, 0])", [a.res])) + r.res.add("[$1, 0])" % [a.res]) else: - r.res.app(ropef("$1)", [a.res])) + r.res.add("$1)" % [a.res]) proc genRepr(p: PProc, n: PNode, r: var TCompRes) = var t = skipTypes(n.sons[1].typ, abstractVarRange) @@ -1288,8 +1291,7 @@ proc genRepr(p: PProc, n: PNode, r: var TCompRes) = gen(p, n.sons[1], r) useMagic(p, "cstrToNimstr") r.kind = resExpr - r.res = ropef("cstrToNimstr($1.node.sons[$2].name)", - [genTypeInfo(p, t), r.res]) + r.res = "cstrToNimstr($1.node.sons[$2].name)" % [genTypeInfo(p, t), r.res] else: # XXX: internalError(n.info, "genRepr: Not implemented") @@ -1299,23 +1301,23 @@ proc genOf(p: PProc, n: PNode, r: var TCompRes) = let t = skipTypes(n.sons[2].typ, abstractVarRange+{tyRef, tyPtr, tyTypeDesc}) gen(p, n.sons[1], x) if tfFinal in t.flags: - r.res = ropef("($1.m_type == $2)", [x.res, genTypeInfo(p, t)]) + r.res = "($1.m_type == $2)" % [x.res, genTypeInfo(p, t)] else: useMagic(p, "isObj") - r.res = ropef("isObj($1.m_type, $2)", [x.res, genTypeInfo(p, t)]) + r.res = "isObj($1.m_type, $2)" % [x.res, genTypeInfo(p, t)] r.kind = resExpr proc genReset(p: PProc, n: PNode) = var x: TCompRes useMagic(p, "genericReset") gen(p, n.sons[1], x) - appf(p.body, "$1 = genericReset($1, $2);$n", [x.res, + addf(p.body, "$1 = genericReset($1, $2);$n", [x.res, genTypeInfo(p, n.sons[1].typ)]) proc genMagic(p: PProc, n: PNode, r: var TCompRes) = var a: TCompRes - line, filen: PRope + line, filen: Rope var op = n.sons[0].sym.magic case op of mOr: genOr(p, n.sons[1], n.sons[2], r) @@ -1327,14 +1329,17 @@ proc genMagic(p: PProc, n: PNode, r: var TCompRes) = # XXX: range checking? if not (optOverflowCheck in p.options): unaryExpr(p, n, r, "", "$1 - 1") else: unaryExpr(p, n, r, "subInt", "subInt($1, 1)") - of mAppendStrCh: binaryExpr(p, n, r, "addChar", "addChar($1, $2)") + of mAppendStrCh: binaryExpr(p, n, r, "addChar", + "if ($1 != null) { addChar($1, $2); } else { $1 = [$2, 0]; }") of mAppendStrStr: if skipTypes(n.sons[1].typ, abstractVarRange).kind == tyCString: - binaryExpr(p, n, r, "", "$1 += $2") + binaryExpr(p, n, r, "", "if ($1 != null) { $1 += $2; } else { $1 = $2; }") else: - binaryExpr(p, n, r, "", "$1 = ($1.slice(0, -1)).concat($2)") + binaryExpr(p, n, r, "", + "if ($1 != null) { $1 = ($1.slice(0, -1)).concat($2); } else { $1 = $2;}") # XXX: make a copy of $2, because of Javascript's sucking semantics - of mAppendSeqElem: binaryExpr(p, n, r, "", "$1.push($2)") + of mAppendSeqElem: binaryExpr(p, n, r, "", + "if ($1 != null) { $1.push($2); } else { $1 = [$2]; }") of mConStrStr: genConStrStr(p, n, r) of mEqStr: binaryExpr(p, n, r, "eqStrings", "eqStrings($1, $2)") of mLeStr: binaryExpr(p, n, r, "cmpStrings", "(cmpStrings($1, $2) <= 0)") @@ -1342,17 +1347,20 @@ proc genMagic(p: PProc, n: PNode, r: var TCompRes) = of mIsNil: unaryExpr(p, n, r, "", "$1 == null") of mEnumToStr: genRepr(p, n, r) of mNew, mNewFinalize: genNew(p, n) - of mSizeOf: r.res = toRope(getSize(n.sons[1].typ)) + of mSizeOf: r.res = rope(getSize(n.sons[1].typ)) of mChr, mArrToSeq: gen(p, n.sons[1], r) # nothing to do of mOrd: genOrd(p, n, r) - of mLengthStr: unaryExpr(p, n, r, "", "($1.length-1)") + of mLengthStr: unaryExpr(p, n, r, "", "($1 != null ? $1.length-1 : 0)") + of mXLenStr: unaryExpr(p, n, r, "", "$1.length-1") of mLengthSeq, mLengthOpenArray, mLengthArray: + unaryExpr(p, n, r, "", "($1 != null ? $1.length : 0)") + of mXLenSeq: unaryExpr(p, n, r, "", "$1.length") of mHigh: if skipTypes(n.sons[1].typ, abstractVar).kind == tyString: - unaryExpr(p, n, r, "", "($1.length-2)") + unaryExpr(p, n, r, "", "($1 != null ? ($1.length-2) : -1)") else: - unaryExpr(p, n, r, "", "($1.length-1)") + unaryExpr(p, n, r, "", "($1 != null ? ($1.length-1) : -1)") of mInc: if optOverflowCheck notin p.options: binaryExpr(p, n, r, "", "$1 += $2") else: binaryExpr(p, n, r, "addInt", "$1 = addInt($1, $2)") @@ -1389,56 +1397,56 @@ proc genSetConstr(p: PProc, n: PNode, r: var TCompRes) = var a, b: TCompRes useMagic(p, "SetConstr") - r.res = toRope("SetConstr(") + r.res = rope("SetConstr(") r.kind = resExpr for i in countup(0, sonsLen(n) - 1): - if i > 0: app(r.res, ", ") + if i > 0: add(r.res, ", ") var it = n.sons[i] if it.kind == nkRange: gen(p, it.sons[0], a) gen(p, it.sons[1], b) - appf(r.res, "[$1, $2]", [a.res, b.res]) + addf(r.res, "[$1, $2]", [a.res, b.res]) else: gen(p, it, a) - app(r.res, a.res) - app(r.res, ")") + add(r.res, a.res) + add(r.res, ")") proc genArrayConstr(p: PProc, n: PNode, r: var TCompRes) = var a: TCompRes - r.res = toRope("[") + r.res = rope("[") r.kind = resExpr for i in countup(0, sonsLen(n) - 1): - if i > 0: app(r.res, ", ") + if i > 0: add(r.res, ", ") gen(p, n.sons[i], a) - app(r.res, a.res) - app(r.res, "]") + add(r.res, a.res) + add(r.res, "]") proc genTupleConstr(p: PProc, n: PNode, r: var TCompRes) = var a: TCompRes - r.res = toRope("{") + r.res = rope("{") r.kind = resExpr for i in countup(0, sonsLen(n) - 1): - if i > 0: app(r.res, ", ") + if i > 0: add(r.res, ", ") var it = n.sons[i] if it.kind == nkExprColonExpr: it = it.sons[1] gen(p, it, a) - appf(r.res, "Field$#: $#" | "Field$# = $#", [i.toRope, a.res]) - r.res.app("}") + addf(r.res, "Field$#: $#" | "Field$# = $#", [i.rope, a.res]) + r.res.add("}") proc genObjConstr(p: PProc, n: PNode, r: var TCompRes) = # XXX inheritance? var a: TCompRes - r.res = toRope("{") + r.res = rope("{") r.kind = resExpr for i in countup(1, sonsLen(n) - 1): - if i > 1: app(r.res, ", ") + if i > 1: add(r.res, ", ") var it = n.sons[i] internalAssert it.kind == nkExprColonExpr gen(p, it.sons[1], a) var f = it.sons[0].sym if f.loc.r == nil: f.loc.r = mangleName(f) - appf(r.res, "$#: $#" | "$# = $#" , [f.loc.r, a.res]) - r.res.app("}") + addf(r.res, "$#: $#" | "$# = $#" , [f.loc.r, a.res]) + r.res.add("}") proc genConv(p: PProc, n: PNode, r: var TCompRes) = var dest = skipTypes(n.typ, abstractVarRange) @@ -1449,10 +1457,10 @@ proc genConv(p: PProc, n: PNode, r: var TCompRes) = return case dest.kind: of tyBool: - r.res = ropef("(($1)? 1:0)" | "toBool($#)", [r.res]) + r.res = ("(($1)? 1:0)" | "toBool($#)") % [r.res] r.kind = resExpr of tyInt: - r.res = ropef("($1|0)", [r.res]) + r.res = "($1|0)" % [r.res] else: # TODO: What types must we handle here? discard @@ -1467,7 +1475,7 @@ proc genRangeChck(p: PProc, n: PNode, r: var TCompRes, magic: string) = gen(p, n.sons[1], a) gen(p, n.sons[2], b) useMagic(p, "chckRange") - r.res = ropef("chckRange($1, $2, $3)", [r.res, a.res, b.res]) + r.res = "chckRange($1, $2, $3)" % [r.res, a.res, b.res] r.kind = resExpr proc convStrToCStr(p: PProc, n: PNode, r: var TCompRes) = @@ -1479,7 +1487,7 @@ proc convStrToCStr(p: PProc, n: PNode, r: var TCompRes) = gen(p, n.sons[0], r) if r.res == nil: internalError(n.info, "convStrToCStr") useMagic(p, "toJSStr") - r.res = ropef("toJSStr($1)", [r.res]) + r.res = "toJSStr($1)" % [r.res] r.kind = resExpr proc convCStrToStr(p: PProc, n: PNode, r: var TCompRes) = @@ -1491,7 +1499,7 @@ proc convCStrToStr(p: PProc, n: PNode, r: var TCompRes) = gen(p, n.sons[0], r) if r.res == nil: internalError(n.info, "convCStrToStr") useMagic(p, "cstrToNimstr") - r.res = ropef("cstrToNimstr($1)", [r.res]) + r.res = "cstrToNimstr($1)" % [r.res] r.kind = resExpr proc genReturnStmt(p: PProc, n: PNode) = @@ -1501,32 +1509,32 @@ proc genReturnStmt(p: PProc, n: PNode) = genStmt(p, n.sons[0]) else: genLineDir(p, n) - appf(p.body, "break BeforeRet;$n" | "goto ::BeforeRet::;$n") + addf(p.body, "break BeforeRet;$n" | "goto ::BeforeRet::;$n", []) -proc genProcBody(p: PProc, prc: PSym): PRope = +proc genProcBody(p: PProc, prc: PSym): Rope = if optStackTrace in prc.options: - result = ropef(("var F={procname:$1,prev:framePtr,filename:$2,line:0};$n" | - "local F={procname=$#,prev=framePtr,filename=$#,line=0};$n") & - "framePtr = F;$n", [ - makeJSString(prc.owner.name.s & '.' & prc.name.s), - makeJSString(toFilename(prc.info))]) + result = (("var F={procname:$1,prev:framePtr,filename:$2,line:0};$n" | + "local F={procname=$#,prev=framePtr,filename=$#,line=0};$n") & + "framePtr = F;$n") % [ + makeJSString(prc.owner.name.s & '.' & prc.name.s), + makeJSString(toFilename(prc.info))] else: result = nil if p.beforeRetNeeded: - appf(result, "BeforeRet: do {$n$1} while (false); $n" | + addf(result, "BeforeRet: do {$n$1} while (false); $n" | "$#;::BeforeRet::$n", [p.body]) else: - app(result, p.body) + add(result, p.body) if prc.typ.callConv == ccSysCall and p.target == targetJS: - result = ropef("try {$n$1} catch (e) {$n" & - " alert(\"Unhandled exception:\\n\" + e.message + \"\\n\"$n}", [result]) + result = ("try {$n$1} catch (e) {$n" & + " alert(\"Unhandled exception:\\n\" + e.message + \"\\n\"$n}") % [result] if optStackTrace in prc.options: - app(result, "framePtr = framePtr.prev;" & tnl) + add(result, "framePtr = framePtr.prev;" & tnl) -proc genProc(oldProc: PProc, prc: PSym): PRope = +proc genProc(oldProc: PProc, prc: PSym): Rope = var resultSym: PSym - name, returnStmt, resultAsgn, header: PRope + name, returnStmt, resultAsgn, header: Rope a: TCompRes #if gVerbosity >= 3: # echo "BEGIN generating code for: " & prc.name.s @@ -1539,23 +1547,23 @@ proc genProc(oldProc: PProc, prc: PSym): PRope = header = generateHeader(p, prc.typ) if prc.typ.sons[0] != nil and sfPure notin prc.flags: resultSym = prc.ast.sons[resultPos].sym - resultAsgn = ropef("var $# = $#;$n" | "local $# = $#;$n", [ + resultAsgn = ("var $# = $#;$n" | "local $# = $#;$n") % [ mangleName(resultSym), - createVar(p, resultSym.typ, isIndirect(resultSym))]) + createVar(p, resultSym.typ, isIndirect(resultSym))] gen(p, prc.ast.sons[resultPos], a) - returnStmt = ropef("return $#;$n", [a.res]) + returnStmt = "return $#;$n" % [a.res] genStmt(p, prc.getBody) - result = ropef("function $#($#) {$n$#$#$#$#}$n" | - "function $#($#) $n$#$#$#$#$nend$n", - [name, header, p.locals, resultAsgn, - genProcBody(p, prc), returnStmt]) + result = ("function $#($#) {$n$#$#$#$#}$n" | + "function $#($#) $n$#$#$#$#$nend$n") % + [name, header, p.locals, resultAsgn, + genProcBody(p, prc), returnStmt] #if gVerbosity >= 3: # echo "END generated code for: " & prc.name.s proc genStmt(p: PProc, n: PNode) = var r: TCompRes gen(p, n, r) - if r.res != nil: appf(p.body, "$#;$n", r.res) + if r.res != nil: addf(p.body, "$#;$n", [r.res]) proc gen(p: PProc, n: PNode, r: var TCompRes) = r.typ = etyNone @@ -1566,34 +1574,34 @@ proc gen(p: PProc, n: PNode, r: var TCompRes) = of nkSym: genSym(p, n, r) of nkCharLit..nkInt64Lit: - r.res = toRope(n.intVal) + r.res = rope(n.intVal) r.kind = resExpr of nkNilLit: if isEmptyType(n.typ): discard elif mapType(n.typ) == etyBaseIndex: r.typ = etyBaseIndex - r.address = toRope"null" | toRope"nil" - r.res = toRope"0" + r.address = rope"null" | rope"nil" + r.res = rope"0" r.kind = resExpr else: - r.res = toRope"null" | toRope"nil" + r.res = rope"null" | rope"nil" r.kind = resExpr of nkStrLit..nkTripleStrLit: if skipTypes(n.typ, abstractVarRange).kind == tyString: useMagic(p, "cstrToNimstr") - r.res = ropef("cstrToNimstr($1)", [makeJSString(n.strVal)]) + r.res = "cstrToNimstr($1)" % [makeJSString(n.strVal)] else: r.res = makeJSString(n.strVal) r.kind = resExpr of nkFloatLit..nkFloat64Lit: let f = n.floatVal - if f != f: r.res = toRope"NaN" - elif f == 0.0: r.res = toRope"0.0" + if f != f: r.res = rope"NaN" + elif f == 0.0: r.res = rope"0.0" elif f == 0.5 * f: - if f > 0.0: r.res = toRope"Infinity" - else: r.res = toRope"-Infinity" - else: r.res = toRope(f.toStrMaxPrecision) + if f > 0.0: r.res = rope"Infinity" + else: r.res = rope"-Infinity" + else: r.res = rope(f.toStrMaxPrecision) r.kind = resExpr of nkCallKinds: if (n.sons[0].kind == nkSym) and (n.sons[0].sym.magic != mNone): @@ -1628,7 +1636,7 @@ proc gen(p: PProc, n: PNode, r: var TCompRes) = r.res = s.loc.r if lfNoDecl in s.loc.flags or s.magic != mNone: discard elif not p.g.generatedSyms.containsOrIncl(s.id): - app(p.locals, genProc(p, s)) + add(p.locals, genProc(p, s)) of nkType: r.res = genTypeInfo(p, n.typ) of nkStmtList, nkStmtListExpr: # this shows the distinction is nice for backends and should be kept @@ -1679,23 +1687,23 @@ proc newModule(module: PSym): BModule = result.module = module if globals == nil: globals = newGlobals() -proc genHeader(): PRope = - result = ropef("/* Generated by the Nim Compiler v$1 */$n" & - "/* (c) 2015 Andreas Rumpf */$n$n" & - "var framePtr = null;$n" & - "var excHandler = null;$n" & - "var lastJSError = null;$n", - [toRope(VersionAsString)]) +proc genHeader(): Rope = + result = ("/* Generated by the Nim Compiler v$1 */$n" & + "/* (c) 2015 Andreas Rumpf */$n$n" & + "var framePtr = null;$n" & + "var excHandler = null;$n" & + "var lastJSError = null;$n") % + [rope(VersionAsString)] proc genModule(p: PProc, n: PNode) = if optStackTrace in p.options: - appf(p.body, "var F = {procname:$1,prev:framePtr,filename:$2,line:0};$n" & + addf(p.body, "var F = {procname:$1,prev:framePtr,filename:$2,line:0};$n" & "framePtr = F;$n", [ makeJSString("module " & p.module.module.name.s), makeJSString(toFilename(p.module.module.info))]) genStmt(p, n) if optStackTrace in p.options: - appf(p.body, "framePtr = framePtr.prev;$n") + addf(p.body, "framePtr = framePtr.prev;$n", []) proc myProcess(b: PPassContext, n: PNode): PNode = if passes.skipCodegen(n): return n @@ -1704,23 +1712,23 @@ proc myProcess(b: PPassContext, n: PNode): PNode = if m.module == nil: internalError(n.info, "myProcess") var p = newProc(globals, m, nil, m.module.options) genModule(p, n) - app(p.g.code, p.locals) - app(p.g.code, p.body) + add(p.g.code, p.locals) + add(p.g.code, p.body) -proc wholeCode*(m: BModule): PRope = +proc wholeCode*(m: BModule): Rope = for prc in globals.forwarded: if not globals.generatedSyms.containsOrIncl(prc.id): var p = newProc(globals, m, nil, m.module.options) - app(p.g.code, genProc(p, prc)) + add(p.g.code, genProc(p, prc)) var disp = generateMethodDispatchers() for i in 0..sonsLen(disp)-1: let prc = disp.sons[i].sym if not globals.generatedSyms.containsOrIncl(prc.id): var p = newProc(globals, m, nil, m.module.options) - app(p.g.code, genProc(p, prc)) + add(p.g.code, genProc(p, prc)) - result = con(globals.typeInfo, globals.code) + result = globals.typeInfo & globals.code proc myClose(b: PPassContext, n: PNode): PNode = if passes.skipCodegen(n): return n @@ -1734,7 +1742,7 @@ proc myClose(b: PPassContext, n: PNode): PNode = else: getCurrentDir() / options.outFile else: changeFileExt(completeCFilePath(m.module.filename), "js") - discard writeRopeIfNotEqual(con(genHeader(), code), outfile) + discard writeRopeIfNotEqual(genHeader() & code, outfile) proc myOpenCached(s: PSym, rd: PRodReader): PPassContext = internalError("symbol files are not possible with the JS code generator") diff --git a/compiler/jstypes.nim b/compiler/jstypes.nim index 1288c854d..851938327 100644 --- a/compiler/jstypes.nim +++ b/compiler/jstypes.nim @@ -9,138 +9,138 @@ ## Type info generation for the JS backend. -proc genTypeInfo(p: PProc, typ: PType): PRope -proc genObjectFields(p: PProc, typ: PType, n: PNode): PRope = - var - s, u: PRope +proc genTypeInfo(p: PProc, typ: PType): Rope +proc genObjectFields(p: PProc, typ: PType, n: PNode): Rope = + var + s, u: Rope length: int field: PSym b: PNode result = nil case n.kind - of nkRecList: + of nkRecList: length = sonsLen(n) - if length == 1: + if length == 1: result = genObjectFields(p, typ, n.sons[0]) - else: + else: s = nil - for i in countup(0, length - 1): - if i > 0: app(s, ", " & tnl) - app(s, genObjectFields(p, typ, n.sons[i])) - result = ropef("{kind: 2, len: $1, offset: 0, " & - "typ: null, name: null, sons: [$2]}", [toRope(length), s]) - of nkSym: + for i in countup(0, length - 1): + if i > 0: add(s, ", " & tnl) + add(s, genObjectFields(p, typ, n.sons[i])) + result = ("{kind: 2, len: $1, offset: 0, " & + "typ: null, name: null, sons: [$2]}") % [rope(length), s] + of nkSym: field = n.sym s = genTypeInfo(p, field.typ) - result = ropef("{kind: 1, offset: \"$1\", len: 0, " & - "typ: $2, name: $3, sons: null}", - [mangleName(field), s, makeJSString(field.name.s)]) - of nkRecCase: + result = ("{kind: 1, offset: \"$1\", len: 0, " & + "typ: $2, name: $3, sons: null}") % + [mangleName(field), s, makeJSString(field.name.s)] + of nkRecCase: length = sonsLen(n) if (n.sons[0].kind != nkSym): internalError(n.info, "genObjectFields") field = n.sons[0].sym s = genTypeInfo(p, field.typ) - for i in countup(1, length - 1): + for i in countup(1, length - 1): b = n.sons[i] # branch u = nil case b.kind - of nkOfBranch: - if sonsLen(b) < 2: + of nkOfBranch: + if sonsLen(b) < 2: internalError(b.info, "genObjectFields; nkOfBranch broken") - for j in countup(0, sonsLen(b) - 2): - if u != nil: app(u, ", ") - if b.sons[j].kind == nkRange: - appf(u, "[$1, $2]", [toRope(getOrdValue(b.sons[j].sons[0])), - toRope(getOrdValue(b.sons[j].sons[1]))]) - else: - app(u, toRope(getOrdValue(b.sons[j]))) - of nkElse: - u = toRope(lengthOrd(field.typ)) + for j in countup(0, sonsLen(b) - 2): + if u != nil: add(u, ", ") + if b.sons[j].kind == nkRange: + addf(u, "[$1, $2]", [rope(getOrdValue(b.sons[j].sons[0])), + rope(getOrdValue(b.sons[j].sons[1]))]) + else: + add(u, rope(getOrdValue(b.sons[j]))) + of nkElse: + u = rope(lengthOrd(field.typ)) else: internalError(n.info, "genObjectFields(nkRecCase)") - if result != nil: app(result, ", " & tnl) - appf(result, "[SetConstr($1), $2]", + if result != nil: add(result, ", " & tnl) + addf(result, "[SetConstr($1), $2]", [u, genObjectFields(p, typ, lastSon(b))]) - result = ropef("{kind: 3, offset: \"$1\", len: $3, " & - "typ: $2, name: $4, sons: [$5]}", [mangleName(field), s, - toRope(lengthOrd(field.typ)), makeJSString(field.name.s), result]) + result = ("{kind: 3, offset: \"$1\", len: $3, " & + "typ: $2, name: $4, sons: [$5]}") % [mangleName(field), s, + rope(lengthOrd(field.typ)), makeJSString(field.name.s), result] else: internalError(n.info, "genObjectFields") - -proc genObjectInfo(p: PProc, typ: PType, name: PRope) = - var s = ropef("var $1 = {size: 0, kind: $2, base: null, node: null, " & - "finalizer: null};$n", [name, toRope(ord(typ.kind))]) + +proc genObjectInfo(p: PProc, typ: PType, name: Rope) = + var s = ("var $1 = {size: 0, kind: $2, base: null, node: null, " & + "finalizer: null};$n") % [name, rope(ord(typ.kind))] prepend(p.g.typeInfo, s) - appf(p.g.typeInfo, "var NNI$1 = $2;$n", - [toRope(typ.id), genObjectFields(p, typ, typ.n)]) - appf(p.g.typeInfo, "$1.node = NNI$2;$n", [name, toRope(typ.id)]) - if (typ.kind == tyObject) and (typ.sons[0] != nil): - appf(p.g.typeInfo, "$1.base = $2;$n", + addf(p.g.typeInfo, "var NNI$1 = $2;$n", + [rope(typ.id), genObjectFields(p, typ, typ.n)]) + addf(p.g.typeInfo, "$1.node = NNI$2;$n", [name, rope(typ.id)]) + if (typ.kind == tyObject) and (typ.sons[0] != nil): + addf(p.g.typeInfo, "$1.base = $2;$n", [name, genTypeInfo(p, typ.sons[0])]) -proc genTupleFields(p: PProc, typ: PType): PRope = - var s: PRope = nil +proc genTupleFields(p: PProc, typ: PType): Rope = + var s: Rope = nil for i in 0 .. <typ.len: - if i > 0: app(s, ", " & tnl) - s.appf("{kind: 1, offset: \"Field$1\", len: 0, " & + if i > 0: add(s, ", " & tnl) + s.addf("{kind: 1, offset: \"Field$1\", len: 0, " & "typ: $2, name: \"Field$1\", sons: null}", - [i.toRope, genTypeInfo(p, typ.sons[i])]) - result = ropef("{kind: 2, len: $1, offset: 0, " & - "typ: null, name: null, sons: [$2]}", [toRope(typ.len), s]) + [i.rope, genTypeInfo(p, typ.sons[i])]) + result = ("{kind: 2, len: $1, offset: 0, " & + "typ: null, name: null, sons: [$2]}") % [rope(typ.len), s] -proc genTupleInfo(p: PProc, typ: PType, name: PRope) = - var s = ropef("var $1 = {size: 0, kind: $2, base: null, node: null, " & - "finalizer: null};$n", [name, toRope(ord(typ.kind))]) +proc genTupleInfo(p: PProc, typ: PType, name: Rope) = + var s = ("var $1 = {size: 0, kind: $2, base: null, node: null, " & + "finalizer: null};$n") % [name, rope(ord(typ.kind))] prepend(p.g.typeInfo, s) - appf(p.g.typeInfo, "var NNI$1 = $2;$n", - [toRope(typ.id), genTupleFields(p, typ)]) - appf(p.g.typeInfo, "$1.node = NNI$2;$n", [name, toRope(typ.id)]) + addf(p.g.typeInfo, "var NNI$1 = $2;$n", + [rope(typ.id), genTupleFields(p, typ)]) + addf(p.g.typeInfo, "$1.node = NNI$2;$n", [name, rope(typ.id)]) -proc genEnumInfo(p: PProc, typ: PType, name: PRope) = +proc genEnumInfo(p: PProc, typ: PType, name: Rope) = let length = sonsLen(typ.n) - var s: PRope = nil - for i in countup(0, length - 1): + var s: Rope = nil + for i in countup(0, length - 1): if (typ.n.sons[i].kind != nkSym): internalError(typ.n.info, "genEnumInfo") let field = typ.n.sons[i].sym - if i > 0: app(s, ", " & tnl) + if i > 0: add(s, ", " & tnl) let extName = if field.ast == nil: field.name.s else: field.ast.strVal - appf(s, "{kind: 1, offset: $1, typ: $2, name: $3, len: 0, sons: null}", - [toRope(field.position), name, makeJSString(extName)]) - var n = ropef("var NNI$1 = {kind: 2, offset: 0, typ: null, " & - "name: null, len: $2, sons: [$3]};$n", [toRope(typ.id), toRope(length), s]) - s = ropef("var $1 = {size: 0, kind: $2, base: null, node: null, " & - "finalizer: null};$n", [name, toRope(ord(typ.kind))]) + addf(s, "{kind: 1, offset: $1, typ: $2, name: $3, len: 0, sons: null}", + [rope(field.position), name, makeJSString(extName)]) + var n = ("var NNI$1 = {kind: 2, offset: 0, typ: null, " & + "name: null, len: $2, sons: [$3]};$n") % [rope(typ.id), rope(length), s] + s = ("var $1 = {size: 0, kind: $2, base: null, node: null, " & + "finalizer: null};$n") % [name, rope(ord(typ.kind))] prepend(p.g.typeInfo, s) - app(p.g.typeInfo, n) - appf(p.g.typeInfo, "$1.node = NNI$2;$n", [name, toRope(typ.id)]) + add(p.g.typeInfo, n) + addf(p.g.typeInfo, "$1.node = NNI$2;$n", [name, rope(typ.id)]) if typ.sons[0] != nil: - appf(p.g.typeInfo, "$1.base = $2;$n", + addf(p.g.typeInfo, "$1.base = $2;$n", [name, genTypeInfo(p, typ.sons[0])]) -proc genTypeInfo(p: PProc, typ: PType): PRope = +proc genTypeInfo(p: PProc, typ: PType): Rope = var t = typ if t.kind == tyGenericInst: t = lastSon(t) - result = ropef("NTI$1", [toRope(t.id)]) - if containsOrIncl(p.g.typeInfoGenerated, t.id): return + result = "NTI$1" % [rope(t.id)] + if containsOrIncl(p.g.typeInfoGenerated, t.id): return case t.kind - of tyDistinct: + of tyDistinct: result = genTypeInfo(p, typ.sons[0]) of tyPointer, tyProc, tyBool, tyChar, tyCString, tyString, tyInt..tyUInt64: - var s = ropef( - "var $1 = {size: 0,kind: $2,base: null,node: null,finalizer: null};$n", - [result, toRope(ord(t.kind))]) + var s = + "var $1 = {size: 0,kind: $2,base: null,node: null,finalizer: null};$n" % + [result, rope(ord(t.kind))] prepend(p.g.typeInfo, s) - of tyVar, tyRef, tyPtr, tySequence, tyRange, tySet: - var s = ropef( - "var $1 = {size: 0,kind: $2,base: null,node: null,finalizer: null};$n", - [result, toRope(ord(t.kind))]) + of tyVar, tyRef, tyPtr, tySequence, tyRange, tySet: + var s = + "var $1 = {size: 0,kind: $2,base: null,node: null,finalizer: null};$n" % + [result, rope(ord(t.kind))] prepend(p.g.typeInfo, s) - appf(p.g.typeInfo, "$1.base = $2;$n", + addf(p.g.typeInfo, "$1.base = $2;$n", [result, genTypeInfo(p, typ.lastSon)]) - of tyArrayConstr, tyArray: - var s = ropef( - "var $1 = {size: 0,kind: $2,base: null,node: null,finalizer: null};$n", - [result, toRope(ord(t.kind))]) + of tyArrayConstr, tyArray: + var s = + "var $1 = {size: 0,kind: $2,base: null,node: null,finalizer: null};$n" % + [result, rope(ord(t.kind))] prepend(p.g.typeInfo, s) - appf(p.g.typeInfo, "$1.base = $2;$n", + addf(p.g.typeInfo, "$1.base = $2;$n", [result, genTypeInfo(p, typ.sons[1])]) of tyEnum: genEnumInfo(p, t, result) of tyObject: genObjectInfo(p, t, result) diff --git a/compiler/lambdalifting.nim b/compiler/lambdalifting.nim index 123445e1f..c68bc352c 100644 --- a/compiler/lambdalifting.nim +++ b/compiler/lambdalifting.nim @@ -9,92 +9,92 @@ # This include file implements lambda lifting for the transformator. -import - intsets, strutils, lists, options, ast, astalgo, trees, treetab, msgs, os, +import + intsets, strutils, lists, options, ast, astalgo, trees, treetab, msgs, os, idents, renderer, types, magicsys, rodread, lowerings discard """ The basic approach is that captured vars need to be put on the heap and that the calling chain needs to be explicitly modelled. Things to consider: - + proc a = var v = 0 proc b = var w = 2 - + for x in 0..3: proc c = capture v, w, x c() b() - + for x in 0..4: proc d = capture x d() - + Needs to be translated into: - + proc a = var cl: * new cl cl.v = 0 - + proc b(cl) = var bcl: * new bcl bcl.w = 2 bcl.up = cl - + for x in 0..3: var bcl2: * new bcl2 bcl2.up = bcl bcl2.up2 = cl bcl2.x = x - + proc c(cl) = capture cl.up2.v, cl.up.w, cl.x c(bcl2) - + c(bcl) - + b(cl) - + for x in 0..4: var acl2: * new acl2 acl2.x = x proc d(cl) = capture cl.x d(acl2) - + Closures as interfaces: - + proc outer: T = var captureMe: TObject # value type required for efficiency proc getter(): int = result = captureMe.x proc setter(x: int) = captureMe.x = x - + result = (getter, setter) - + Is translated to: - + proc outer: T = var cl: * new cl - + proc getter(cl): int = result = cl.captureMe.x proc setter(cl: *, x: int) = cl.captureMe.x = x - + result = ((cl, getter), (cl, setter)) - - + + For 'byref' capture, the outer proc needs to access the captured var through the indirection too. For 'bycopy' capture, the outer proc accesses the var not through the indirection. - - Possible optimizations: - + + Possible optimizations: + 1) If the closure contains a single 'ref' and this reference is not re-assigned (check ``sfAddrTaken`` flag) make this the - closure. This is an important optimization if closures are used as + closure. This is an important optimization if closures are used as interfaces. 2) If the closure does not escape, put it onto the stack, not on the heap. 3) Dataflow analysis would help to eliminate the 'up' indirections. @@ -126,7 +126,7 @@ type fn, closureParam, state, resultSym: PSym # most are only valid if # fn.kind == skClosureIterator obj: PType - + PEnv = ref TEnv TEnv {.final.} = object of RootObj attachedNode, replacementNode: PNode @@ -141,7 +141,7 @@ type # if up.fn != fn then we cross function boundaries. # This is an important case to consider. vars: IntSet # variables belonging to this environment - + TOuterContext = object fn: PSym # may also be a module! head: PEnv @@ -284,7 +284,7 @@ proc addClosureParam(fn: PSym; e: PEnv) = #assert e.obj.kind == tyObject proc illegalCapture(s: PSym): bool {.inline.} = - result = skipTypes(s.typ, abstractInst).kind in + result = skipTypes(s.typ, abstractInst).kind in {tyVar, tyOpenArray, tyVarargs} or s.kind == skResult @@ -344,7 +344,7 @@ proc createUpField(obj, fieldType: PType): PSym = #rawAddField(obj, result) addField(obj, result) -proc captureVar(o: POuterContext; top: PEnv; local: PSym; +proc captureVar(o: POuterContext; top: PEnv; local: PSym; info: TLineInfo): bool = # first check if we should be concerned at all: var it = top @@ -408,7 +408,7 @@ proc gatherVars(o: POuterContext; e: PEnv; n: PNode): int = var s = n.sym if interestingVar(s) and e.fn != s.owner: if captureVar(o, e, s, n.info): result = 1 - of nkEmpty..pred(nkSym), succ(nkSym)..nkNilLit, nkClosure, nkProcDef, + of nkEmpty..pred(nkSym), succ(nkSym)..nkNilLit, nkClosure, nkProcDef, nkMethodDef, nkConverterDef, nkMacroDef, nkTemplateDef, nkTypeSection: discard else: @@ -418,7 +418,7 @@ proc gatherVars(o: POuterContext; e: PEnv; n: PNode): int = proc generateThunk(prc: PNode, dest: PType): PNode = ## Converts 'prc' into '(thunk, nil)' so that it's compatible with ## a closure. - + # we cannot generate a proper thunk here for GC-safety reasons (see internal # documentation): if gCmd == cmdCompileToJS: return prc @@ -515,7 +515,7 @@ proc closureCreationPoint(n: PNode): PNode = proc addParamsToEnv(fn: PSym; env: PEnv) = let params = fn.typ.n - for i in 1.. <params.len: + for i in 1.. <params.len: if params.sons[i].kind != nkSym: internalError(params.info, "liftLambdas: strange params") let param = params.sons[i].sym @@ -541,7 +541,7 @@ proc searchForInnerProcs(o: POuterContext, n: PNode, env: PEnv) = addParamsToEnv(fn, envB) searchForInnerProcs(o, body, envB) fn.ast.sons[bodyPos] = ex - + let capturedCounter = gatherVars(o, envB, body) # dummy closure param needed? if capturedCounter == 0 and fn.typ.callConv == ccClosure: @@ -560,7 +560,7 @@ proc searchForInnerProcs(o: POuterContext, n: PNode, env: PEnv) = of nkWhileStmt, nkForStmt, nkParForStmt, nkBlockStmt: # some nodes open a new scope, so they are candidates for the insertion # of closure creation; however for simplicity we merge closures between - # branches, in fact, only loop bodies are of interest here as only they + # branches, in fact, only loop bodies are of interest here as only they # yield observable changes in semantics. For Zahary we also # include ``nkBlock``. We don't do this for closure iterators because # 'yield' can produce wrong code otherwise (XXX show example): @@ -598,7 +598,7 @@ proc searchForInnerProcs(o: POuterContext, n: PNode, env: PEnv) = internalError(it.info, "searchForInnerProcs") of nkClosure: searchForInnerProcs(o, n.sons[0], env) - of nkProcDef, nkMethodDef, nkConverterDef, nkMacroDef, nkTemplateDef, + of nkProcDef, nkMethodDef, nkConverterDef, nkMacroDef, nkTemplateDef, nkTypeSection: # don't recurse here: discard @@ -606,7 +606,7 @@ proc searchForInnerProcs(o: POuterContext, n: PNode, env: PEnv) = for i in countup(0, sonsLen(n) - 1): searchForInnerProcs(o, n.sons[i], env) -proc newAsgnStmt(le, ri: PNode, info: TLineInfo): PNode = +proc newAsgnStmt(le, ri: PNode, info: TLineInfo): PNode = # Bugfix: unfortunately we cannot use 'nkFastAsgn' here as that would # mean to be able to capture string literals which have no GC header. # However this can only happen if the capture happens through a parameter, @@ -624,7 +624,7 @@ proc rawClosureCreation(o: POuterContext, scope: PEnv; env: PNode): PNode = result.add(v) # add 'new' statement: result.add(newCall(getSysSym"internalNew", env)) - + # add assignment statements: for local in scope.capturedVars: let fieldAccess = indirectAccess(env, local, env.info) @@ -696,10 +696,10 @@ proc transformYield(c: POuterContext, n: PNode, it: TIter): PNode = retStmt.add(a) else: retStmt.add(emptyNode) - + var stateLabelStmt = newNodeI(nkState, n.info) stateLabelStmt.add(newIntTypeNode(nkIntLit, stateNo, getSysType(tyInt))) - + result = newNodeI(nkStmtList, n.info) result.add(stateAsgnStmt) result.add(retStmt) @@ -725,7 +725,7 @@ proc liftIterSym(n: PNode; owner: PSym): PNode = assert iter.kind == skClosureIterator result = newNodeIT(nkStmtListExpr, n.info, n.typ) - + let hp = getHiddenParam(iter) let env = newSym(skLet, iter.name, owner, n.info) env.typ = hp.typ @@ -800,7 +800,7 @@ proc transformOuterProcBody(o: POuterContext, n: PNode; it: TIter): PNode = # with some rather primitive check for now: if n.kind == nkStmtList and n.len > 0: if n.sons[0].kind == nkGotoState: return nil - if n.len > 1 and n[1].kind == nkStmtList and n[1].len > 0 and + if n.len > 1 and n[1].kind == nkStmtList and n[1].len > 0 and n[1][0].kind == nkGotoState: return nil result = newNodeI(nkStmtList, it.fn.info) @@ -812,7 +812,7 @@ proc transformOuterProcBody(o: POuterContext, n: PNode; it: TIter): PNode = var state0 = newNodeI(nkState, it.fn.info) state0.add(newIntNode(nkIntLit, 0)) result.add(state0) - + let newBody = transformOuterProc(o, n, it) if newBody != nil: result.add(newBody) @@ -899,7 +899,7 @@ proc transformOuterProc(o: POuterContext, n: PNode; it: TIter): PNode = let x = closure.createdVar assert x != nil return makeClosure(local, x, n.info) - + if not contains(o.capturedVars, local.id): return # change 'local' to 'closure.local', unless it's a 'byCopy' variable: # if sfByCopy notin local.flags: @@ -946,7 +946,7 @@ proc transformOuterProc(o: POuterContext, n: PNode; it: TIter): PNode = proc liftLambdas*(fn: PSym, body: PNode): PNode = # XXX gCmd == cmdCompileToJS does not suffice! The compiletime stuff needs # the transformation even when compiling to JS ... - if body.kind == nkEmpty or gCmd == cmdCompileToJS or + if body.kind == nkEmpty or gCmd == cmdCompileToJS or fn.skipGenericOwner.kind != skModule: # ignore forward declaration: result = body @@ -985,17 +985,17 @@ proc liftLambdasForTopLevel*(module: PSym, body: PNode): PNode = proc liftForLoop*(body: PNode): PNode = # problem ahead: the iterator could be invoked indirectly, but then - # we don't know what environment to create here: - # + # we don't know what environment to create here: + # # iterator count(): int = # yield 0 - # + # # iterator count2(): int = # var x = 3 # yield x # inc x # yield x - # + # # proc invoke(iter: iterator(): int) = # for x in iter(): echo x # @@ -1004,7 +1004,7 @@ proc liftForLoop*(body: PNode): PNode = for i in foo(): ... Is transformed to: - + cl = createClosure() while true: let i = foo(cl) @@ -1016,7 +1016,7 @@ proc liftForLoop*(body: PNode): PNode = var call = body[L-2] result = newNodeI(nkStmtList, body.info) - + # static binding? var env: PSym if call[0].kind == nkSym and call[0].sym.kind == skClosureIterator: @@ -1030,18 +1030,18 @@ proc liftForLoop*(body: PNode): PNode = result.add(v) # add 'new' statement: result.add(newCall(getSysSym"internalNew", env.newSymNode)) - + var loopBody = newNodeI(nkStmtList, body.info, 3) var whileLoop = newNodeI(nkWhileStmt, body.info, 2) whileLoop.sons[0] = newIntTypeNode(nkIntLit, 1, getSysType(tyBool)) whileLoop.sons[1] = loopBody result.add whileLoop - + # setup loopBody: # gather vars in a tuple: var v2 = newNodeI(nkLetSection, body.info) var vpart = newNodeI(if L == 3: nkIdentDefs else: nkVarTuple, body.info) - for i in 0 .. L-3: + for i in 0 .. L-3: assert body[i].kind == nkSym body[i].sym.kind = skLet addSon(vpart, body[i]) diff --git a/compiler/lexer.nim b/compiler/lexer.nim index 78266ef4d..8080e0e8c 100644 --- a/compiler/lexer.nim +++ b/compiler/lexer.nim @@ -61,7 +61,7 @@ type tkComma, tkSemiColon, tkColon, tkColonColon, tkEquals, tkDot, tkDotDot, tkOpr, tkComment, tkAccent, - tkSpaces, tkInfixOpr, tkPrefixOpr, tkPostfixOpr, + tkSpaces, tkInfixOpr, tkPrefixOpr, tkPostfixOpr TTokTypes* = set[TTokType] @@ -131,24 +131,10 @@ type var gLinesCompiled*: int # all lines that have been compiled -proc isKeyword*(kind: TTokType): bool -proc openLexer*(lex: var TLexer, fileidx: int32, inputstream: PLLStream) -proc rawGetTok*(L: var TLexer, tok: var TToken) - # reads in the next token into tok and skips it - proc getLineInfo*(L: TLexer, tok: TToken): TLineInfo {.inline.} = newLineInfo(L.fileIdx, tok.line, tok.col) -proc closeLexer*(lex: var TLexer) -proc printTok*(tok: TToken) -proc tokToStr*(tok: TToken): string - -proc openLexer*(lex: var TLexer, filename: string, inputstream: PLLStream) = - openLexer(lex, filename.fileInfoIdx, inputstream) - -proc lexMessage*(L: TLexer, msg: TMsgKind, arg = "") - -proc isKeyword(kind: TTokType): bool = +proc isKeyword*(kind: TTokType): bool = result = (kind >= tokKeywordLow) and (kind <= tokKeywordHigh) proc isNimIdentifier*(s: string): bool = @@ -206,14 +192,17 @@ proc fillToken(L: var TToken) = L.base = base10 L.ident = dummyIdent -proc openLexer(lex: var TLexer, fileIdx: int32, inputstream: PLLStream) = +proc openLexer*(lex: var TLexer, fileIdx: int32, inputstream: PLLStream) = openBaseLexer(lex, inputstream) lex.fileIdx = fileidx lex.indentAhead = - 1 lex.currLineIndent = 0 inc(lex.lineNumber, inputstream.lineOffset) -proc closeLexer(lex: var TLexer) = +proc openLexer*(lex: var TLexer, filename: string, inputstream: PLLStream) = + openLexer(lex, filename.fileInfoIdx, inputstream) + +proc closeLexer*(lex: var TLexer) = inc(gLinesCompiled, lex.lineNumber) closeBaseLexer(lex) @@ -229,9 +218,13 @@ proc dispMessage(L: TLexer; info: TLineInfo; msg: TMsgKind; arg: string) = else: L.errorHandler(info, msg, arg) -proc lexMessage(L: TLexer, msg: TMsgKind, arg = "") = +proc lexMessage*(L: TLexer, msg: TMsgKind, arg = "") = L.dispMessage(getLineInfo(L), msg, arg) +proc lexMessageTok*(L: TLexer, msg: TMsgKind, tok: TToken, arg = "") = + var info = newLineInfo(L.fileIdx, tok.line, tok.col) + L.dispMessage(info, msg, arg) + proc lexMessagePos(L: var TLexer, msg: TMsgKind, pos: int, arg = "") = var info = newLineInfo(L.fileIdx, L.lineNumber, pos - L.lineStart) L.dispMessage(info, msg, arg) @@ -785,7 +778,7 @@ proc skip(L: var TLexer, tok: var TToken) = break # EndOfFile also leaves the loop L.bufpos = pos -proc rawGetTok(L: var TLexer, tok: var TToken) = +proc rawGetTok*(L: var TLexer, tok: var TToken) = fillToken(tok) if L.indentAhead >= 0: tok.indent = L.indentAhead @@ -874,6 +867,15 @@ proc rawGetTok(L: var TLexer, tok: var TToken) = of '`': tok.tokType = tkAccent inc(L.bufpos) + of '_': + inc(L.bufpos) + if L.buf[L.bufpos] notin SymChars: + tok.tokType = tkSymbol + tok.ident = getIdent("_") + else: + tok.literal = $c + tok.tokType = tkInvalid + lexMessage(L, errInvalidToken, c & " (\\" & $(ord(c)) & ')') of '\"': # check for extended raw string literal: var rawMode = L.bufpos > 0 and L.buf[L.bufpos-1] in SymChars diff --git a/compiler/lowerings.nim b/compiler/lowerings.nim index a51ca9ed6..b6b01d558 100644 --- a/compiler/lowerings.nim +++ b/compiler/lowerings.nim @@ -15,6 +15,10 @@ const import ast, astalgo, types, idents, magicsys, msgs, options from trees import getMagic +proc newDeref*(n: PNode): PNode {.inline.} = + result = newNodeIT(nkHiddenDeref, n.info, n.typ.sons[0]) + addSon(result, n) + proc newTupleAccess*(tup: PNode, i: int): PNode = result = newNodeIT(nkBracketExpr, tup.info, tup.typ.skipTypes( abstractInst).sons[i]) @@ -382,11 +386,11 @@ proc getRoot*(n: PNode): PSym = if getMagic(n) == mSlice: result = getRoot(n.sons[1]) else: discard -proc newIntLit(value: BiggestInt): PNode = +proc newIntLit*(value: BiggestInt): PNode = result = nkIntLit.newIntNode(value) result.typ = getSysType(tyInt) -proc genHigh(n: PNode): PNode = +proc genHigh*(n: PNode): PNode = if skipTypes(n.typ, abstractVar).kind in {tyArrayConstr, tyArray}: result = newIntLit(lastOrd(skipTypes(n.typ, abstractVar))) else: diff --git a/compiler/main.nim b/compiler/main.nim index 363327e40..0c80c19b7 100644 --- a/compiler/main.nim +++ b/compiler/main.nim @@ -54,6 +54,7 @@ proc commandDoc2 = finishDoc2Pass(gProjectName) proc commandCompileToC = + extccomp.initVars() semanticPasses() registerPass(cgenPass) rodPass() diff --git a/compiler/msgs.nim b/compiler/msgs.nim index 22ef9dc30..041a181be 100644 --- a/compiler/msgs.nim +++ b/compiler/msgs.nim @@ -387,7 +387,7 @@ const warnProveField: "cannot prove that field '$1' is accessible [ProveField]", warnProveIndex: "cannot prove index '$1' is valid [ProveIndex]", warnGcUnsafe: "not GC-safe: '$1' [GcUnsafe]", - warnGcUnsafe2: "cannot prove '$1' is GC-safe. Does not compile with --threads:on.", + warnGcUnsafe2: "$1", warnUninit: "'$1' might not have been initialized [Uninit]", warnGcMem: "'$1' uses GC'ed memory [GcMem]", warnDestructor: "usage of a type with a destructor in a non destructible context. This will become a compile time error in the future. [Destructor]", @@ -449,10 +449,10 @@ type fullPath: string # This is a canonical full filesystem path projPath*: string # This is relative to the project's root shortName*: string # short name of the module - quotedName*: PRope # cached quoted short name for codegen + quotedName*: Rope # cached quoted short name for codegen # purposes - lines*: seq[PRope] # the source code of the module + lines*: seq[Rope] # the source code of the module # used for better error messages and # embedding the original source in the # generated code @@ -493,24 +493,20 @@ proc toCChar*(c: char): string = of '\'', '\"', '\\': result = '\\' & c else: result = $(c) -proc makeCString*(s: string): PRope = - # BUGFIX: We have to split long strings into many ropes. Otherwise - # this could trigger an internalError(). See the ropes module for - # further information. +proc makeCString*(s: string): Rope = const MaxLineLength = 64 result = nil - var res = "\"" + var res = newStringOfCap(int(s.len.toFloat * 1.1) + 1) + add(res, "\"") for i in countup(0, len(s) - 1): if (i + 1) mod MaxLineLength == 0: add(res, '\"') add(res, tnl) - app(result, toRope(res)) # reset: - setLen(res, 1) - res[0] = '\"' + add(res, '\"') add(res, toCChar(s[i])) add(res, '\"') - app(result, toRope(res)) + add(result, rope(res)) proc newFileInfo(fullPath, projPath: string): TFileInfo = @@ -568,7 +564,7 @@ var gCodegenLineInfo* = newLineInfo(int32(1), 1, 1) proc raiseRecoverableError*(msg: string) {.noinline, noreturn.} = raise newException(ERecoverableError, msg) -proc sourceLine*(i: TLineInfo): PRope +proc sourceLine*(i: TLineInfo): Rope var gNotes*: TNoteKinds = {low(TNoteKind)..high(TNoteKind)} - @@ -742,7 +738,7 @@ proc writeContext(lastinfo: TLineInfo) = if msgContext[i] != lastinfo and msgContext[i] != info: msgWriteln(PosContextFormat % [toMsgFilename(msgContext[i]), coordToStr(msgContext[i].line), - coordToStr(msgContext[i].col), + coordToStr(msgContext[i].col+1), getMessageStr(errInstantiationFrom, "")]) info = msgContext[i] @@ -776,7 +772,7 @@ proc rawMessage*(msg: TMsgKind, arg: string) = proc writeSurroundingSrc(info: TLineInfo) = const indent = " " - msgWriteln(indent & info.sourceLine.ropeToStr) + msgWriteln(indent & $info.sourceLine) msgWriteln(indent & spaces(info.col) & '^') 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 else: PosErrorFormat result = frmt % [toMsgFilename(info), coordToStr(info.line), - coordToStr(info.col), getMessageStr(msg, arg)] + coordToStr(info.col+1), getMessageStr(msg, arg)] proc liMessage(info: TLineInfo, msg: TMsgKind, arg: string, eh: TErrorHandling) = @@ -808,8 +804,11 @@ proc liMessage(info: TLineInfo, msg: TMsgKind, arg: string, ignoreMsg = optHints notin gOptions or msg notin gNotes frmt = PosHintFormat inc(gHintCounter) + # NOTE: currently line info line numbers start with 1, + # but column numbers start with 0, however most editors expect + # first column to be 1, so we need to +1 here let s = frmt % [toMsgFilename(info), coordToStr(info.line), - coordToStr(info.col), getMessageStr(msg, arg)] + coordToStr(info.col+1), getMessageStr(msg, arg)] if not ignoreMsg and not ignoreMsgBecauseOfIdeTools(msg): msgWriteln(s) if optPrintSurroundingSrc and msg in errMin..errMax: @@ -831,6 +830,9 @@ proc localError*(info: TLineInfo, msg: TMsgKind, arg = "") = proc localError*(info: TLineInfo, arg: string) = liMessage(info, errGenerated, arg, doNothing) +proc localError*(info: TLineInfo, format: string, params: openarray[string]) = + localError(info, format % params) + proc message*(info: TLineInfo, msg: TMsgKind, arg = "") = liMessage(info, msg, arg, doNothing) @@ -852,9 +854,9 @@ template internalAssert*(e: bool): stmt = if not e: internalError($instantiationInfo()) proc addSourceLine*(fileIdx: int32, line: string) = - fileInfos[fileIdx].lines.add line.toRope + fileInfos[fileIdx].lines.add line.rope -proc sourceLine*(i: TLineInfo): PRope = +proc sourceLine*(i: TLineInfo): Rope = if i.fileIndex < 0: return nil if not optPreserveOrigSource and fileInfos[i.fileIndex].lines.len == 0: @@ -869,16 +871,14 @@ proc sourceLine*(i: TLineInfo): PRope = result = fileInfos[i.fileIndex].lines[i.line-1] -proc quotedFilename*(i: TLineInfo): PRope = +proc quotedFilename*(i: TLineInfo): Rope = internalAssert i.fileIndex >= 0 result = fileInfos[i.fileIndex].quotedName -ropes.errorHandler = proc (err: TRopesError, msg: string, useWarning: bool) = +ropes.errorHandler = proc (err: RopesError, msg: string, useWarning: bool) = case err of rInvalidFormatStr: internalError("ropes: invalid format string: " & msg) - of rTokenTooLong: - internalError("ropes: token too long: " & msg) of rCannotOpenFile: rawMessage(if useWarning: warnCannotOpenFile else: errCannotOpenFile, msg) diff --git a/compiler/nimfix/nimfix.nim.cfg b/compiler/nimfix/nimfix.nim.cfg index 533563a98..73219d6f8 100644 --- a/compiler/nimfix/nimfix.nim.cfg +++ b/compiler/nimfix/nimfix.nim.cfg @@ -5,7 +5,7 @@ hint[XDeclaredButNotUsed]:off path:"$projectPath/.." path:"$lib/packages/docutils" -path:"$nim/compiler" +path:"../../compiler" define:useStdoutAsStdmsg symbol:nimfix diff --git a/compiler/nimsuggest/nimsuggest.nim b/compiler/nimsuggest/nimsuggest.nim index b45ca475c..8285d81d9 100644 --- a/compiler/nimsuggest/nimsuggest.nim +++ b/compiler/nimsuggest/nimsuggest.nim @@ -82,7 +82,8 @@ proc action(cmd: string) = if cmd[i] == ';': i = parseQuoted(cmd, dirtyfile, i+1) i += skipWhile(cmd, seps, i) - var line, col = -1 + var line = -1 + var col = 0 i += parseInt(cmd, line, i) i += skipWhile(cmd, seps, i) i += parseInt(cmd, col, i) @@ -97,7 +98,7 @@ proc action(cmd: string) = resetModule dirtyIdx if dirtyIdx != gProjectMainIdx: resetModule gProjectMainIdx - gTrackPos = newLineInfo(dirtyIdx, line, col) + gTrackPos = newLineInfo(dirtyIdx, line, col-1) #echo dirtyfile, gDirtyBufferIdx, " project ", gProjectMainIdx gErrorCounter = 0 if not isKnownFile: @@ -150,11 +151,11 @@ proc mainCommand = proc processCmdLine*(pass: TCmdLinePass, cmd: string) = var p = parseopt.initOptParser(cmd) - while true: + while true: parseopt.next(p) case p.kind - of cmdEnd: break - of cmdLongoption, cmdShortOption: + of cmdEnd: break + of cmdLongoption, cmdShortOption: case p.key.normalize of "port": gPort = parseInt(p.val).Port of "address": gAddress = p.val diff --git a/compiler/nimsuggest/nimsuggest.nim.cfg b/compiler/nimsuggest/nimsuggest.nim.cfg index 062092f16..acca17396 100644 --- a/compiler/nimsuggest/nimsuggest.nim.cfg +++ b/compiler/nimsuggest/nimsuggest.nim.cfg @@ -6,7 +6,7 @@ hint[XDeclaredButNotUsed]:off path:"$projectPath/../.." path:"$lib/packages/docutils" -path:"$nim/compiler" +path:"../../compiler" define:useStdoutAsStdmsg define:nimsuggest diff --git a/compiler/options.nim b/compiler/options.nim index 1b4a624ab..998ab7781 100644 --- a/compiler/options.nim +++ b/compiler/options.nim @@ -1,7 +1,7 @@ # # # The Nim Compiler -# (c) Copyright 2013 Andreas Rumpf +# (c) Copyright 2015 Andreas Rumpf # # See the file "copying.txt", included in this # distribution, for details about the copyright. @@ -9,7 +9,7 @@ import os, lists, strutils, strtabs, osproc, sets - + const hasTinyCBackend* = defined(tinyc) useEffectSystem* = true @@ -21,10 +21,10 @@ const type # please make sure we have under 32 options # (improves code efficiency a lot!) TOption* = enum # **keep binary compatible** - optNone, optObjCheck, optFieldCheck, optRangeCheck, optBoundsCheck, + optNone, optObjCheck, optFieldCheck, optRangeCheck, optBoundsCheck, optOverflowCheck, optNilCheck, optNaNCheck, optInfCheck, - optAssert, optLineDir, optWarns, optHints, + optAssert, optLineDir, optWarns, optHints, optOptimizeSpeed, optOptimizeSize, optStackTrace, # stack tracing support optLineTrace, # line tracing support (includes stack tracing) optEndb, # embedded debugger @@ -37,8 +37,8 @@ type # please make sure we have under 32 options TOptions* = set[TOption] TGlobalOption* = enum # **keep binary compatible** - gloptNone, optForceFullMake, optDeadCodeElim, - optListCmd, optCompileOnly, optNoLinking, + gloptNone, optForceFullMake, optDeadCodeElim, + optListCmd, optCompileOnly, optNoLinking, optSafeCode, # only allow safe code optCDebug, # turn on debugging information optGenDynLib, # generate a dynamic library @@ -67,9 +67,9 @@ type # please make sure we have under 32 options TGlobalOptions* = set[TGlobalOption] TCommands* = enum # Nim's commands # **keep binary compatible** - cmdNone, cmdCompileToC, cmdCompileToCpp, cmdCompileToOC, - cmdCompileToJS, cmdCompileToLLVM, cmdInterpret, cmdPretty, cmdDoc, - cmdGenDepend, cmdDump, + cmdNone, cmdCompileToC, cmdCompileToCpp, cmdCompileToOC, + cmdCompileToJS, cmdCompileToLLVM, cmdInterpret, cmdPretty, cmdDoc, + cmdGenDepend, cmdDump, cmdCheck, # semantic checking for whole project cmdParse, # parse a single file (for debugging) cmdScan, # scan a single file (for debugging) @@ -90,12 +90,12 @@ var gIdeCmd*: TIdeCmd const - ChecksOptions* = {optObjCheck, optFieldCheck, optRangeCheck, optNilCheck, + ChecksOptions* = {optObjCheck, optFieldCheck, optRangeCheck, optNilCheck, optOverflowCheck, optBoundsCheck, optAssert, optNaNCheck, optInfCheck} -var - gOptions*: TOptions = {optObjCheck, optFieldCheck, optRangeCheck, - optBoundsCheck, optOverflowCheck, optAssert, optWarns, +var + gOptions*: TOptions = {optObjCheck, optFieldCheck, optRangeCheck, + optBoundsCheck, optOverflowCheck, optAssert, optWarns, optHints, optStackTrace, optLineTrace, optPatterns, optNilCheck} gGlobalOptions*: TGlobalOptions = {optThreadAnalysis} @@ -129,7 +129,7 @@ template optPreserveOrigSource*: expr = template optPrintSurroundingSrc*: expr = gVerbosity >= 2 -const +const genSubDir* = "nimcache" NimExt* = "nim" RodExt* = "rod" @@ -168,20 +168,20 @@ proc mainCommandArg*: string = else: result = gProjectName -proc existsConfigVar*(key: string): bool = +proc existsConfigVar*(key: string): bool = result = hasKey(gConfigVars, key) -proc getConfigVar*(key: string): string = +proc getConfigVar*(key: string): string = result = gConfigVars[key] -proc setConfigVar*(key, val: string) = +proc setConfigVar*(key, val: string) = gConfigVars[key] = val -proc getOutFile*(filename, ext: string): string = +proc getOutFile*(filename, ext: string): string = if options.outFile != "": result = options.outFile else: result = changeFileExt(filename, ext) - -proc getPrefixDir*(): string = + +proc getPrefixDir*(): string = ## gets the application directory result = splitPath(getAppDir()).head @@ -189,22 +189,22 @@ proc canonicalizePath*(path: string): string = when not FileSystemCaseSensitive: result = path.expandFilename.toLower else: result = path.expandFilename -proc shortenDir*(dir: string): string = +proc shortenDir*(dir: string): string = ## returns the interesting part of a dir var prefix = getPrefixDir() & DirSep - if startsWith(dir, prefix): + if startsWith(dir, prefix): return substr(dir, len(prefix)) prefix = gProjectPath & DirSep if startsWith(dir, prefix): return substr(dir, len(prefix)) result = dir -proc removeTrailingDirSep*(path: string): string = - if (len(path) > 0) and (path[len(path) - 1] == DirSep): +proc removeTrailingDirSep*(path: string): string = + if (len(path) > 0) and (path[len(path) - 1] == DirSep): result = substr(path, 0, len(path) - 2) - else: + else: result = path - + proc getGeneratedPath: string = result = if nimcacheDir.len > 0: nimcacheDir else: gProjectPath.shortenDir / genSubDir @@ -257,7 +257,7 @@ proc withPackageName*(path: string): string = let (p, file, ext) = path.splitFile result = (p / (x & '_' & file)) & ext -proc toGeneratedFile*(path, ext: string): string = +proc toGeneratedFile*(path, ext: string): string = ## converts "/home/a/mymodule.nim", "rod" to "/home/a/nimcache/mymodule.rod" var (head, tail) = splitPath(path) #if len(head) > 0: head = shortenDir(head & dirSep) @@ -301,7 +301,7 @@ proc completeGeneratedFilePath*(f: string, createSubDir: bool = true): string = result = joinPath(subdir, tail) #echo "completeGeneratedFilePath(", f, ") = ", result -iterator iterSearchPath*(searchPaths: TLinkedList): string = +iterator iterSearchPath*(searchPaths: TLinkedList): string = var it = PStrEntry(searchPaths.head) while it != nil: yield it.data @@ -324,7 +324,7 @@ proc rawFindFile2(f: string): string = it = PStrEntry(it.next) result = "" -proc findFile*(f: string): string {.procvar.} = +proc findFile*(f: string): string {.procvar.} = result = f.rawFindFile if result.len == 0: result = f.toLower.rawFindFile @@ -351,7 +351,7 @@ proc findModule*(modulename, currentModule: string): string = if not existsFile(result): result = findFile(m) -proc libCandidates*(s: string, dest: var seq[string]) = +proc libCandidates*(s: string, dest: var seq[string]) = var le = strutils.find(s, '(') var ri = strutils.find(s, ')', le+1) if le >= 0 and ri > le: @@ -359,7 +359,7 @@ proc libCandidates*(s: string, dest: var seq[string]) = var suffix = substr(s, ri + 1) for middle in split(substr(s, le + 1, ri - 1), '|'): libCandidates(prefix & middle & suffix, dest) - else: + else: add(dest, s) proc canonDynlibName(s: string): string = @@ -376,17 +376,17 @@ proc inclDynlibOverride*(lib: string) = proc isDynlibOverride*(lib: string): bool = result = gDllOverrides.hasKey(lib.canonDynlibName) -proc binaryStrSearch*(x: openArray[string], y: string): int = +proc binaryStrSearch*(x: openArray[string], y: string): int = var a = 0 var b = len(x) - 1 - while a <= b: + while a <= b: var mid = (a + b) div 2 var c = cmpIgnoreCase(x[mid], y) - if c < 0: + if c < 0: a = mid + 1 - elif c > 0: + elif c > 0: b = mid - 1 - else: + else: return mid result = - 1 diff --git a/compiler/parampatterns.nim b/compiler/parampatterns.nim index 3f67005b9..b7fe269df 100644 --- a/compiler/parampatterns.nim +++ b/compiler/parampatterns.nim @@ -190,6 +190,8 @@ proc isAssignable*(owner: PSym, n: PNode): TAssignableResult = result = arLocalLValue else: result = arLValue + elif n.sym.kind == skParam and n.sym.typ.kind == tyVar: + result = arLValue elif n.sym.kind == skType: let t = n.sym.typ.skipTypes({tyTypeDesc}) if t.kind == tyVar: result = arStrange diff --git a/compiler/parser.nim b/compiler/parser.nim index dcd5401e8..0d2ba7cfc 100644 --- a/compiler/parser.nim +++ b/compiler/parser.nim @@ -91,7 +91,7 @@ proc closeParser(p: var TParser) = proc parMessage(p: TParser, msg: TMsgKind, arg = "") = ## Produce and emit the parser message `arg` to output. - lexMessage(p.lex, msg, arg) + lexMessageTok(p.lex, msg, p.tok, arg) proc parMessage(p: TParser, msg: TMsgKind, tok: TToken) = ## Produce and emit a parser message to output about the token `tok` @@ -154,7 +154,7 @@ proc eat(p: var TParser, tokType: TTokType) = if p.tok.tokType == tokType: getTok(p) else: - lexMessage(p.lex, errTokenExpected, TokTypeToStr[tokType]) + lexMessageTok(p.lex, errTokenExpected, p.tok, TokTypeToStr[tokType]) proc parLineInfo(p: TParser): TLineInfo = ## Retrieve the line information associated with the parser's current state. @@ -212,27 +212,29 @@ proc getPrecedence(tok: TToken, strongSpaces: bool): int = let relevantChar = tok.ident.s[0] # arrow like? - if L > 1 and tok.ident.s[L-1] == '>': return considerStrongSpaces(1) + if L > 1 and tok.ident.s[L-1] == '>' and + tok.ident.s[L-2] in {'-', '~', '='}: return considerStrongSpaces(1) template considerAsgn(value: expr) = - result = if tok.ident.s[L-1] == '=': 1 else: considerStrongSpaces(value) + result = if tok.ident.s[L-1] == '=': 1 else: value case relevantChar of '$', '^': considerAsgn(10) of '*', '%', '/', '\\': considerAsgn(9) - of '~': result = considerStrongSpaces(8) + of '~': result = 8 of '+', '-', '|': considerAsgn(8) of '&': considerAsgn(7) - of '=', '<', '>', '!': result = considerStrongSpaces(5) + of '=', '<', '>', '!': result = 5 of '.': considerAsgn(6) - of '?': result = considerStrongSpaces(2) + of '?': result = 2 else: considerAsgn(2) of tkDiv, tkMod, tkShl, tkShr: result = 9 of tkIn, tkNotin, tkIs, tkIsnot, tkNot, tkOf, tkAs: result = 5 - of tkDotDot: result = considerStrongSpaces(6) + of tkDotDot: result = 6 of tkAnd: result = 4 of tkOr, tkXor, tkPtr, tkRef: result = 3 - else: result = -10 + else: return -10 + result = considerStrongSpaces(result) proc isOperator(tok: TToken): bool = ## Determines if the given token is an operator type token. @@ -387,7 +389,6 @@ proc exprList(p: var TParser, endTok: TTokType, result: PNode) = if p.tok.tokType != tkComma: break getTok(p) optInd(p, a) - eat(p, endTok) proc dotExpr(p: var TParser, a: PNode): PNode = #| dotExpr = expr '.' optInd symbol @@ -943,8 +944,7 @@ proc parseDoBlock(p: var TParser): PNode = getTok(p) let params = parseParamList(p, retColon=false) let pragmas = optPragmas(p) - eat(p, tkColon) - skipComment(p, result) + colcom(p, result) result = newProcNode(nkDo, info, parseStmt(p), params = params, pragmas = pragmas) @@ -1138,9 +1138,11 @@ proc parseMacroColon(p: var TParser, x: PNode): PNode = result = makeCall(result) getTok(p) skipComment(p, result) + let stmtList = newNodeP(nkStmtList, p) if p.tok.tokType notin {tkOf, tkElif, tkElse, tkExcept}: let body = parseStmt(p) - addSon(result, makeStmtList(body)) + stmtList.add body + #addSon(result, makeStmtList(body)) while sameInd(p): var b: PNode case p.tok.tokType @@ -1152,19 +1154,22 @@ proc parseMacroColon(p: var TParser, x: PNode): PNode = getTok(p) optInd(p, b) addSon(b, parseExpr(p)) - eat(p, tkColon) of tkExcept: b = newNodeP(nkExceptBranch, p) exprList(p, tkColon, b) - skipComment(p, b) of tkElse: b = newNodeP(nkElse, p) getTok(p) - eat(p, tkColon) else: break + eat(p, tkColon) addSon(b, parseStmt(p)) - addSon(result, b) + addSon(stmtList, b) if b.kind == nkElse: break + if stmtList.len == 1 and stmtList[0].kind == nkStmtList: + # to keep backwards compatibility (see tests/vm/tstringnil) + result.add stmtList[0] + else: + result.add stmtList proc parseExprStmt(p: var TParser): PNode = #| exprStmt = simpleExpr @@ -1309,8 +1314,7 @@ proc parseIfOrWhen(p: var TParser, kind: TNodeKind): PNode = var branch = newNodeP(nkElifBranch, p) optInd(p, branch) addSon(branch, parseExpr(p)) - eat(p, tkColon) - skipComment(p, branch) + colcom(p, branch) addSon(branch, parseStmt(p)) skipComment(p, branch) addSon(result, branch) @@ -1318,8 +1322,7 @@ proc parseIfOrWhen(p: var TParser, kind: TNodeKind): PNode = if p.tok.tokType == tkElse and sameOrNoInd(p): var branch = newNodeP(nkElse, p) eat(p, tkElse) - eat(p, tkColon) - skipComment(p, branch) + colcom(p, branch) addSon(branch, parseStmt(p)) addSon(result, branch) @@ -1367,13 +1370,11 @@ proc parseCase(p: var TParser): PNode = getTok(p) optInd(p, b) addSon(b, parseExpr(p)) - eat(p, tkColon) of tkElse: b = newNodeP(nkElse, p) getTok(p) - eat(p, tkColon) else: break - skipComment(p, b) + colcom(p, b) addSon(b, parseStmt(p)) addSon(result, b) if b.kind == nkElse: break @@ -1390,8 +1391,7 @@ proc parseTry(p: var TParser; isExpr: bool): PNode = #| (optInd 'finally' colcom stmt)? result = newNodeP(nkTryStmt, p) getTok(p) - eat(p, tkColon) - skipComment(p, result) + colcom(p, result) addSon(result, parseStmt(p)) var b: PNode = nil while sameOrNoInd(p) or isExpr: @@ -1401,10 +1401,9 @@ proc parseTry(p: var TParser; isExpr: bool): PNode = exprList(p, tkColon, b) of tkFinally: b = newNodeP(nkFinally, p) - getTokNoInd(p) - eat(p, tkColon) + getTok(p) else: break - skipComment(p, b) + colcom(p, b) addSon(b, parseStmt(p)) addSon(result, b) if b.kind == nkFinally: break @@ -1413,7 +1412,7 @@ proc parseTry(p: var TParser; isExpr: bool): PNode = proc parseExceptBlock(p: var TParser, kind: TNodeKind): PNode = #| exceptBlock = 'except' colcom stmt result = newNodeP(kind, p) - getTokNoInd(p) + getTok(p) colcom(p, result) addSon(result, parseStmt(p)) @@ -1446,7 +1445,7 @@ proc parseStaticOrDefer(p: var TParser; k: TNodeKind): PNode = #| staticStmt = 'static' colcom stmt #| deferStmt = 'defer' colcom stmt result = newNodeP(k, p) - getTokNoInd(p) + getTok(p) colcom(p, result) addSon(result, parseStmt(p)) @@ -1627,7 +1626,7 @@ proc parseEnum(p: var TParser): PNode = p.tok.tokType == tkEof: break if result.len <= 1: - lexMessage(p.lex, errIdentifierExpected, prettyTok(p.tok)) + lexMessageTok(p.lex, errIdentifierExpected, p.tok, prettyTok(p.tok)) proc parseObjectPart(p: var TParser): PNode proc parseObjectWhen(p: var TParser): PNode = @@ -1685,9 +1684,8 @@ proc parseObjectCase(p: var TParser): PNode = of tkElse: b = newNodeP(nkElse, p) getTok(p) - eat(p, tkColon) else: break - skipComment(p, b) + colcom(p, b) var fields = parseObjectPart(p) if fields.kind == nkEmpty: parMessage(p, errIdentifierExpected, p.tok) @@ -1883,7 +1881,7 @@ proc simpleStmt(p: var TParser): PNode = proc complexOrSimpleStmt(p: var TParser): PNode = #| complexOrSimpleStmt = (ifStmt | whenStmt | whileStmt - #| | tryStmt | finallyStmt | exceptStmt | forStmt + #| | tryStmt | forStmt #| | blockStmt | staticStmt | deferStmt | asmStmt #| | 'proc' routine #| | 'method' routine @@ -2030,8 +2028,8 @@ proc parseString*(s: string; filename: string = ""; line: int = 0; var parser: TParser # XXX for now the builtin 'parseStmt/Expr' functions do not know about strong # spaces... - openParser(parser, filename, stream, false) parser.lex.errorHandler = errorHandler + openParser(parser, filename, stream, false) result = parser.parseAll closeParser(parser) diff --git a/compiler/plugins.nim b/compiler/plugins.nim new file mode 100644 index 000000000..1c9b7b77b --- /dev/null +++ b/compiler/plugins.nim @@ -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) diff --git a/compiler/plugins/active.nim b/compiler/plugins/active.nim new file mode 100644 index 000000000..e9c11c2ea --- /dev/null +++ b/compiler/plugins/active.nim @@ -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 diff --git a/compiler/plugins/locals/locals.nim b/compiler/plugins/locals/locals.nim new file mode 100644 index 000000000..d89149f33 --- /dev/null +++ b/compiler/plugins/locals/locals.nim @@ -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) diff --git a/compiler/pragmas.nim b/compiler/pragmas.nim index 056e4f4c0..c048d78e9 100644 --- a/compiler/pragmas.nim +++ b/compiler/pragmas.nim @@ -60,7 +60,7 @@ const varPragmas* = {wImportc, wExportc, wVolatile, wRegister, wThreadVar, wNodecl, wMagic, wHeader, wDeprecated, wCompilerproc, wDynlib, wExtern, wImportCpp, wImportObjC, wError, wNoInit, wCompileTime, wGlobal, - wGensym, wInject, wCodegenDecl, wGuard} + wGensym, wInject, wCodegenDecl, wGuard, wGoto} constPragmas* = {wImportc, wExportc, wHeader, wDeprecated, wMagic, wNodecl, wExtern, wImportCpp, wImportObjC, wError, wGensym, wInject} letPragmas* = varPragmas @@ -89,7 +89,7 @@ proc pragmaAsm*(c: PContext, n: PNode): char = invalidPragma(it) proc setExternName(s: PSym, extname: string) = - s.loc.r = toRope(extname % s.name.s) + s.loc.r = rope(extname % s.name.s) if gCmd == cmdPretty and '$' notin extname: # note that '{.importc.}' is transformed into '{.importc: "$1".}' s.loc.flags.incl(lfFullExternalName) @@ -105,7 +105,7 @@ proc validateExternCName(s: PSym, info: TLineInfo) = ## Valid identifiers are those alphanumeric including the underscore not ## starting with a number. If the check fails, a generic error will be ## displayed to the user. - let target = ropeToStr(s.loc.r) + let target = $s.loc.r if target.len < 1 or target[0] notin IdentStartChars or not target.allCharsInSet(IdentChars): localError(info, errGenerated, "invalid exported symbol") @@ -677,7 +677,7 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: int, incl(sym.loc.flags, lfHeader) incl(sym.loc.flags, lfNoDecl) # implies nodecl, because otherwise header would not make sense - if sym.loc.r == nil: sym.loc.r = toRope(sym.name.s) + if sym.loc.r == nil: sym.loc.r = rope(sym.name.s) of wDestructor: sym.flags.incl sfOverriden if sym.name.s.normalize != "destroy": @@ -843,6 +843,11 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: int, invalidPragma(it) else: sym.guard = pragmaGuard(c, it, sym.kind) + of wGoto: + if sym == nil or sym.kind notin {skVar, skLet}: + invalidPragma(it) + else: + sym.flags.incl sfGoto of wInjectStmt: if it.kind != nkExprColonExpr: localError(it.info, errExprExpected) @@ -865,9 +870,11 @@ proc implicitPragmas*(c: PContext, sym: PSym, n: PNode, while it != nil: let o = it.otherPragmas if not o.isNil: + pushInfoContext(n.info) for i in countup(0, sonsLen(o) - 1): if singlePragma(c, sym, o, i, validPragmas): internalError(n.info, "implicitPragmas") + popInfoContext() it = it.next.POptionEntry if lfExportLib in sym.loc.flags and sfExportc notin sym.flags: @@ -877,7 +884,7 @@ proc implicitPragmas*(c: PContext, sym: PSym, n: PNode, sfImportc in sym.flags and lib != nil: incl(sym.loc.flags, lfDynamicLib) addToLib(lib, sym) - if sym.loc.r == nil: sym.loc.r = toRope(sym.name.s) + if sym.loc.r == nil: sym.loc.r = rope(sym.name.s) proc hasPragma*(n: PNode, pragma: TSpecialWord): bool = if n == nil or n.sons == nil: diff --git a/compiler/renderer.nim b/compiler/renderer.nim index ce818e3cd..ffdb60696 100644 --- a/compiler/renderer.nim +++ b/compiler/renderer.nim @@ -503,6 +503,7 @@ proc gsub(g: var TSrcGen, n: PNode) = proc hasCom(n: PNode): bool = result = false + if n.isNil: return false if n.comment != nil: return true case n.kind of nkEmpty..nkNilLit: discard diff --git a/compiler/rodread.nim b/compiler/rodread.nim index 545a8dda9..e92f7ecfa 100644 --- a/compiler/rodread.nim +++ b/compiler/rodread.nim @@ -277,7 +277,7 @@ proc decodeLoc(r: PRodReader, loc: var TLoc, info: TLineInfo) = loc.t = nil if r.s[r.pos] == '!': inc(r.pos) - loc.r = toRope(decodeStr(r.s, r.pos)) + loc.r = rope(decodeStr(r.s, r.pos)) else: loc.r = nil if r.s[r.pos] == '>': inc(r.pos) @@ -344,7 +344,7 @@ proc decodeLib(r: PRodReader, info: TLineInfo): PLib = result.kind = TLibKind(decodeVInt(r.s, r.pos)) if r.s[r.pos] != '|': internalError("decodeLib: 1") inc(r.pos) - result.name = toRope(decodeStr(r.s, r.pos)) + result.name = rope(decodeStr(r.s, r.pos)) if r.s[r.pos] != '|': internalError("decodeLib: 2") inc(r.pos) result.path = decodeNode(r, info) diff --git a/compiler/rodwrite.nim b/compiler/rodwrite.nim index 0f211b4ba..e178b7ce6 100644 --- a/compiler/rodwrite.nim +++ b/compiler/rodwrite.nim @@ -186,7 +186,7 @@ proc encodeLoc(w: PRodWriter, loc: TLoc, result: var string) = pushType(w, loc.t) if loc.r != nil: add(result, '!') - encodeStr(ropeToStr(loc.r), result) + encodeStr($loc.r, result) if oldLen + 1 == result.len: # no data was necessary, so remove the '<' again: setLen(result, oldLen) @@ -241,7 +241,7 @@ proc encodeLib(w: PRodWriter, lib: PLib, info: TLineInfo, result: var string) = add(result, '|') encodeVInt(ord(lib.kind), result) add(result, '|') - encodeStr(ropeToStr(lib.name), result) + encodeStr($lib.name, result) add(result, '|') encodeNode(w, info, lib.path, result) diff --git a/compiler/ropes.nim b/compiler/ropes.nim index ae71234c4..edac8e9d0 100644 --- a/compiler/ropes.nim +++ b/compiler/ropes.nim @@ -56,77 +56,59 @@ # To cache them they are inserted in a `cache` array. import - strutils, platform, hashes, crc, options + platform, hashes type - TFormatStr* = string # later we may change it to CString for better + FormatStr* = string # later we may change it to CString for better # performance of the code generator (assignments # copy the format strings # though it is not necessary) - PRope* = ref TRope - TRope*{.acyclic.} = object of RootObj # the empty rope is represented - # by nil to safe space - left*, right*: PRope + Rope* = ref RopeObj + RopeObj*{.acyclic.} = object of RootObj # the empty rope is represented + # by nil to safe space + left*, right*: Rope length*: int data*: string # != nil if a leaf - TRopeSeq* = seq[PRope] + RopeSeq* = seq[Rope] - TRopesError* = enum + RopesError* = enum rCannotOpenFile rInvalidFormatStr - rTokenTooLong - -proc con*(a, b: PRope): PRope -proc con*(a: PRope, b: string): PRope -proc con*(a: string, b: PRope): PRope -proc con*(a: varargs[PRope]): PRope -proc app*(a: var PRope, b: PRope) -proc app*(a: var PRope, b: string) -proc prepend*(a: var PRope, b: PRope) -proc toRope*(s: string): PRope -proc toRope*(i: BiggestInt): PRope -proc ropeLen*(a: PRope): int -proc writeRopeIfNotEqual*(r: PRope, filename: string): bool -proc ropeToStr*(p: PRope): string -proc ropef*(frmt: TFormatStr, args: varargs[PRope]): PRope -proc appf*(c: var PRope, frmt: TFormatStr, args: varargs[PRope]) -proc ropeEqualsFile*(r: PRope, f: string): bool - # returns true if the rope r is the same as the contents of file f -proc ropeInvariant*(r: PRope): bool - # exported for debugging + # implementation -var errorHandler*: proc(err: TRopesError, msg: string, useWarning = false) +var errorHandler*: proc(err: RopesError, msg: string, useWarning = false) # avoid dependency on msgs.nim -proc ropeLen(a: PRope): int = +proc len*(a: Rope): int = + ## the rope's length if a == nil: result = 0 else: result = a.length -proc newRope*(data: string = nil): PRope = +proc newRope(data: string = nil): Rope = new(result) if data != nil: result.length = len(data) result.data = data -proc newMutableRope*(capacity = 30): PRope = +proc newMutableRope*(capacity = 30): Rope = ## creates a new rope that supports direct modifications of the rope's ## 'data' and 'length' fields. new(result) result.data = newStringOfCap(capacity) -proc freezeMutableRope*(r: PRope) {.inline.} = +proc freezeMutableRope*(r: Rope) {.inline.} = r.length = r.data.len var - cache: array[0..2048*2 - 1, PRope] + cache: array[0..2048*2 - 1, Rope] proc resetRopeCache* = for i in low(cache)..high(cache): cache[i] = nil -proc ropeInvariant(r: PRope): bool = +proc ropeInvariant(r: Rope): bool = if r == nil: result = true else: @@ -143,7 +125,7 @@ var gCacheTries* = 0 var gCacheMisses* = 0 var gCacheIntTries* = 0 -proc insertInCache(s: string): PRope = +proc insertInCache(s: string): Rope = inc gCacheTries var h = hash(s) and high(cache) result = cache[h] @@ -152,82 +134,77 @@ proc insertInCache(s: string): PRope = result = newRope(s) cache[h] = result -proc toRope(s: string): PRope = +proc rope*(s: string): Rope = + ## Converts a string to a rope. if s.len == 0: result = nil else: result = insertInCache(s) assert(ropeInvariant(result)) -proc ropeSeqInsert(rs: var TRopeSeq, r: PRope, at: Natural) = - var length = len(rs) - if at > length: - setLen(rs, at + 1) - else: - setLen(rs, length + 1) # move old rope elements: - for i in countdown(length, at + 1): - rs[i] = rs[i - 1] # this is correct, I used pen and paper to validate it - rs[at] = r - -proc newRecRopeToStr(result: var string, resultLen: var int, r: PRope) = - var stack = @[r] - while len(stack) > 0: - var it = pop(stack) - while it.data == nil: - add(stack, it.right) - it = it.left - assert(it.data != nil) - copyMem(addr(result[resultLen]), addr(it.data[0]), it.length) - inc(resultLen, it.length) - assert(resultLen <= len(result)) - -proc ropeToStr(p: PRope): string = - if p == nil: - result = "" - else: - result = newString(p.length) - var resultLen = 0 - newRecRopeToStr(result, resultLen, p) +proc rope*(i: BiggestInt): Rope = + ## Converts an int to a rope. + inc gCacheIntTries + result = rope($i) -proc con(a, b: PRope): PRope = - if a == nil: result = b - elif b == nil: result = a +proc rope*(f: BiggestFloat): Rope = + ## Converts a float to a rope. + result = rope($f) + +proc `&`*(a, b: Rope): Rope = + if a == nil: + result = b + elif b == nil: + result = a else: result = newRope() result.length = a.length + b.length result.left = a result.right = b -proc con(a: PRope, b: string): PRope = result = con(a, toRope(b)) -proc con(a: string, b: PRope): PRope = result = con(toRope(a), b) - -proc con(a: varargs[PRope]): PRope = - for i in countup(0, high(a)): result = con(result, a[i]) - -proc ropeConcat*(a: varargs[PRope]): PRope = - # not overloaded version of concat to speed-up `rfmt` a little bit - for i in countup(0, high(a)): result = con(result, a[i]) - -proc toRope(i: BiggestInt): PRope = - inc gCacheIntTries - result = toRope($i) - -proc app(a: var PRope, b: PRope) = a = con(a, b) -proc app(a: var PRope, b: string) = a = con(a, b) -proc prepend(a: var PRope, b: PRope) = a = con(b, a) - -proc writeRope*(f: File, c: PRope) = - var stack = @[c] - while len(stack) > 0: - var it = pop(stack) - while it.data == nil: - add(stack, it.right) - it = it.left - assert(it != nil) - assert(it.data != nil) - write(f, it.data) - -proc writeRope*(head: PRope, filename: string, useWarning = false) = +proc `&`*(a: Rope, b: string): Rope = + ## the concatenation operator for ropes. + result = a & rope(b) + +proc `&`*(a: string, b: Rope): Rope = + ## the concatenation operator for ropes. + result = rope(a) & b + +proc `&`*(a: openArray[Rope]): Rope = + ## the concatenation operator for an openarray of ropes. + for i in countup(0, high(a)): result = result & a[i] + +proc add*(a: var Rope, b: Rope) = + ## adds `b` to the rope `a`. + a = a & b + +proc add*(a: var Rope, b: string) = + ## adds `b` to the rope `a`. + a = a & b + +iterator leaves*(r: Rope): string = + ## iterates over any leaf string in the rope `r`. + if r != nil: + var stack = @[r] + while stack.len > 0: + var it = stack.pop + while isNil(it.data): + stack.add(it.right) + it = it.left + assert(it != nil) + assert(it.data != nil) + yield it.data + +iterator items*(r: Rope): char = + ## iterates over any character in the rope `r`. + for s in leaves(r): + for c in items(s): yield c + +proc writeRope*(f: File, r: Rope) = + ## writes a rope to a file. + for s in leaves(r): write(f, s) + +proc writeRope*(head: Rope, filename: string, useWarning = false) = var f: File if open(f, filename, fmWrite): if head != nil: writeRope(f, head) @@ -235,42 +212,69 @@ proc writeRope*(head: PRope, filename: string, useWarning = false) = else: errorHandler(rCannotOpenFile, filename, useWarning) +proc `$`*(r: Rope): string = + ## converts a rope back to a string. + result = newString(r.len) + setLen(result, 0) + for s in leaves(r): add(result, s) + +proc ropeConcat*(a: varargs[Rope]): Rope = + # not overloaded version of concat to speed-up `rfmt` a little bit + for i in countup(0, high(a)): result = result & a[i] + +proc prepend*(a: var Rope, b: Rope) = a = b & a +proc prepend*(a: var Rope, b: string) = a = b & a + var rnl* = tnl.newRope softRnl* = tnl.newRope -proc ropef(frmt: TFormatStr, args: varargs[PRope]): PRope = +proc `%`*(frmt: FormatStr, args: openArray[Rope]): Rope = var i = 0 var length = len(frmt) result = nil var num = 0 - while i <= length - 1: + while i < length: if frmt[i] == '$': inc(i) # skip '$' case frmt[i] of '$': - app(result, "$") + add(result, "$") inc(i) of '#': inc(i) - app(result, args[num]) + add(result, args[num]) inc(num) of '0'..'9': var j = 0 while true: - j = (j * 10) + ord(frmt[i]) - ord('0') + j = j * 10 + ord(frmt[i]) - ord('0') + inc(i) + if frmt[i] notin {'0'..'9'}: break + num = j + if j > high(args) + 1: + errorHandler(rInvalidFormatStr, $(j)) + else: + 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) - if (i > length + 0 - 1) or not (frmt[i] in {'0'..'9'}): break num = j + if frmt[i] == '}': inc(i) + else: errorHandler(rInvalidFormatStr, $(frmt[i])) + if j > high(args) + 1: errorHandler(rInvalidFormatStr, $(j)) else: - app(result, args[j - 1]) + add(result, args[j-1]) of 'n': - app(result, softRnl) - inc i + add(result, softRnl) + inc(i) of 'N': - app(result, rnl) + add(result, rnl) inc(i) else: errorHandler(rInvalidFormatStr, $(frmt[i])) @@ -279,83 +283,67 @@ proc ropef(frmt: TFormatStr, args: varargs[PRope]): PRope = if frmt[i] != '$': inc(i) else: break if i - 1 >= start: - app(result, substr(frmt, start, i - 1)) + add(result, substr(frmt, start, i - 1)) assert(ropeInvariant(result)) +proc addf*(c: var Rope, frmt: FormatStr, args: openArray[Rope]) = + ## shortcut for ``add(c, frmt % args)``. + add(c, frmt % args) + when true: - template `~`*(r: string): PRope = r.ropef + template `~`*(r: string): Rope = r % [] else: {.push stack_trace: off, line_trace: off.} - proc `~`*(r: static[string]): PRope = + proc `~`*(r: static[string]): Rope = # this is the new optimized "to rope" operator # the mnemonic is that `~` looks a bit like a rope :) - var r {.global.} = r.ropef + var r {.global.} = r % [] return r {.pop.} -proc appf(c: var PRope, frmt: TFormatStr, args: varargs[PRope]) = - app(c, ropef(frmt, args)) - const bufSize = 1024 # 1 KB is reasonable -proc auxRopeEqualsFile(r: PRope, bin: var File, buf: pointer): bool = - if r.data != nil: - if r.length > bufSize: - errorHandler(rTokenTooLong, r.data) - return - var readBytes = readBuffer(bin, buf, r.length) - result = readBytes == r.length and - equalMem(buf, addr(r.data[0]), r.length) # BUGFIX - else: - result = auxRopeEqualsFile(r.left, bin, buf) - if result: result = auxRopeEqualsFile(r.right, bin, buf) - -proc ropeEqualsFile(r: PRope, f: string): bool = - var bin: File - result = open(bin, f) - if not result: - return # not equal if file does not exist - var buf = alloc(bufSize) - result = auxRopeEqualsFile(r, bin, buf) +proc equalsFile*(r: Rope, f: File): bool = + ## returns true if the contents of the file `f` equal `r`. + var + buf: array[bufSize, char] + bpos = buf.len + blen = buf.len + + for s in leaves(r): + var spos = 0 + let slen = s.len + while spos < slen: + if bpos == blen: + # Read more data + bpos = 0 + blen = readBuffer(f, addr(buf[0]), buf.len) + if blen == 0: # no more data in file + result = false + return + let n = min(blen - bpos, slen - spos) + # TODO There's gotta be a better way of comparing here... + if not equalMem(addr(buf[bpos]), cast[pointer](cast[int](cstring(s))+spos), n): + result = false + return + spos += n + bpos += n + + result = readBuffer(f, addr(buf[0]), 1) == 0 # check that we've read all + +proc equalsFile*(r: Rope, filename: string): bool = + ## returns true if the contents of the file `f` equal `r`. If `f` does not + ## exist, false is returned. + var f: File + result = open(f, filename) if result: - result = readBuffer(bin, buf, bufSize) == 0 # really at the end of file? - dealloc(buf) - close(bin) - -proc crcFromRopeAux(r: PRope, startVal: TCrc32): TCrc32 = - if r.data != nil: - result = startVal - for i in countup(0, len(r.data) - 1): - result = updateCrc32(r.data[i], result) - else: - result = crcFromRopeAux(r.left, startVal) - result = crcFromRopeAux(r.right, result) - -proc newCrcFromRopeAux(r: PRope, startVal: TCrc32): TCrc32 = - # XXX profiling shows this is actually expensive - var stack: TRopeSeq = @[r] - result = startVal - while len(stack) > 0: - var it = pop(stack) - while it.data == nil: - add(stack, it.right) - it = it.left - assert(it.data != nil) - var i = 0 - var L = len(it.data) - while i < L: - result = updateCrc32(it.data[i], result) - inc(i) - -proc crcFromRope(r: PRope): TCrc32 = - result = newCrcFromRopeAux(r, InitCrc32) - -proc writeRopeIfNotEqual(r: PRope, filename: string): bool = + result = equalsFile(r, f) + close(f) + +proc writeRopeIfNotEqual*(r: Rope, filename: string): bool = # returns true if overwritten - var c: TCrc32 - c = crcFromFile(filename) - if c != crcFromRope(r): + if not equalsFile(r, filename): writeRope(r, filename) result = true else: diff --git a/compiler/sem.nim b/compiler/sem.nim index 2d2f15fab..346a17df1 100644 --- a/compiler/sem.nim +++ b/compiler/sem.nim @@ -16,7 +16,7 @@ import procfind, lookups, rodread, pragmas, passes, semdata, semtypinst, sigmatch, intsets, transf, vmdef, vm, idgen, aliases, cgmeth, lambdalifting, evaltempl, patterns, parampatterns, sempass2, nimfix.pretty, semmacrosanity, - semparallel, lowerings + semparallel, lowerings, plugins, plugins.active when defined(nimfix): import nimfix.prettybase @@ -89,6 +89,10 @@ proc fitNode(c: PContext, formal: PType, arg: PNode): PNode = let x = result.skipConv if x.kind == nkPar and formal.kind != tyExpr: changeType(x, formal, check=true) + else: + result = skipHiddenSubConv(result) + #result.typ = takeType(formal, arg.typ) + #echo arg.info, " picked ", result.typ.typeToString proc inferWithMetatype(c: PContext, formal: PType, arg: PNode, coerceDistincts = false): PNode @@ -398,7 +402,7 @@ proc myOpen(module: PSym): PPassContext = c.semInferredLambda = semInferredLambda c.semGenerateInstance = generateInstance c.semTypeNode = semTypeNode - c.instDeepCopy = sigmatch.instDeepCopy + c.instTypeBoundOp = sigmatch.instTypeBoundOp pushProcCon(c, module) pushOwner(c.module) diff --git a/compiler/semasgn.nim b/compiler/semasgn.nim index 208c4ce1a..a1e209263 100644 --- a/compiler/semasgn.nim +++ b/compiler/semasgn.nim @@ -7,111 +7,84 @@ # 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 type - TTypeAttachedOp = enum - attachedDestructor, - attachedAsgn, - attachedDeepCopy - TLiftCtx = object c: PContext info: TLineInfo # for construction - result: PNode kind: TTypeAttachedOp + fn: PSym + asgnForType: PType + recurse: bool -type - TFieldInstCtx = object # either 'tup[i]' or 'field' is valid - tupleType: PType # if != nil we're traversing a tuple - tupleIndex: int - field: PSym - replaceByFieldName: bool +proc liftBodyAux(c: var TLiftCtx; t: PType; body, x, y: PNode) +proc liftBody(c: PContext; typ: PType; info: TLineInfo): PSym + +proc 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 instFieldLoopBody(c: TFieldInstCtx, n: PNode, forLoop: PNode): PNode = +proc liftBodyObj(c: var TLiftCtx; n, body, x, y: PNode) = case n.kind - of nkEmpty..pred(nkIdent), succ(nkIdent)..nkNilLit: result = n - of nkIdent: - result = n - var L = sonsLen(forLoop) - if c.replaceByFieldName: - if n.ident.id == forLoop[0].ident.id: - let fieldName = if c.tupleType.isNil: c.field.name.s - elif c.tupleType.n.isNil: "Field" & $c.tupleIndex - else: c.tupleType.n.sons[c.tupleIndex].sym.name.s - result = newStrNode(nkStrLit, fieldName) - return - # other fields: - for i in ord(c.replaceByFieldName)..L-3: - if n.ident.id == forLoop[i].ident.id: - var call = forLoop.sons[L-2] - var tupl = call.sons[i+1-ord(c.replaceByFieldName)] - if c.field.isNil: - result = newNodeI(nkBracketExpr, n.info) - result.add(tupl) - result.add(newIntNode(nkIntLit, c.tupleIndex)) - else: - result = newNodeI(nkDotExpr, n.info) - result.add(tupl) - result.add(newSymNode(c.field, n.info)) - break - else: - if n.kind == nkContinueStmt: - localError(n.info, errGenerated, - "'continue' not supported in a 'fields' loop") - result = copyNode(n) - newSons(result, sonsLen(n)) - for i in countup(0, sonsLen(n)-1): - result.sons[i] = instFieldLoopBody(c, n.sons[i], forLoop) - -proc liftBodyObj(c: TLiftCtx; typ, x, y: PNode) = - case typ.kind of nkSym: - var fc: TFieldInstCtx # either 'tup[i]' or 'field' is valid - fc.field = typ.sym - fc.replaceByFieldName = c.m == mFieldPairs - openScope(c.c) - inc c.c.inUnrolledContext - let body = instFieldLoopBody(fc, lastSon(forLoop), forLoop) - father.add(semStmt(c.c, body)) - dec c.c.inUnrolledContext - closeScope(c.c) + let f = n.sym + liftBodyAux(c, f.typ, body, x.dotField(f), y.dotField(f)) of nkNilLit: discard of nkRecCase: - let L = forLoop.len - let call = forLoop.sons[L-2] - if call.len > 2: - localError(forLoop.info, errGenerated, - "parallel 'fields' iterator does not work for 'case' objects") - return - # iterate over the selector: - asgnForObjectFields(c, typ[0], forLoop, father) + # copy the selector: + liftBodyObj(c, n[0], body, x, y) # we need to generate a case statement: var caseStmt = newNodeI(nkCaseStmt, c.info) + # XXX generate 'if' that checks same branches # generate selector: - var access = newNodeI(nkDotExpr, forLoop.info, 2) - access.sons[0] = call.sons[1] - access.sons[1] = newSymNode(typ.sons[0].sym, forLoop.info) - caseStmt.add(semExprWithType(c.c, access)) + var access = dotField(x, n[0].sym) + caseStmt.add(access) # copy the branches over, but replace the fields with the for loop body: - for i in 1 .. <typ.len: - var branch = copyTree(typ[i]) + for i in 1 .. <n.len: + var branch = copyTree(n[i]) let L = branch.len - branch.sons[L-1] = newNodeI(nkStmtList, forLoop.info) - semForObjectFields(c, typ[i].lastSon, forLoop, branch[L-1]) + branch.sons[L-1] = newNodeI(nkStmtList, c.info) + + liftBodyObj(c, n[i].lastSon, branch.sons[L-1], x, y) caseStmt.add(branch) - father.add(caseStmt) + body.add(caseStmt) + localError(c.info, "cannot lift assignment operator to 'case' object") of nkRecList: - for t in items(typ): liftBodyObj(c, t, x, y) + for t in items(n): liftBodyObj(c, t, body, x, y) else: - illFormedAstLocal(typ) + illFormedAstLocal(n) -proc newAsgnCall(op: PSym; x, y: PNode): PNode = +proc genAddr(c: PContext; x: PNode): PNode = + if x.kind == nkHiddenDeref: + checkSonsLen(x, 1) + result = x.sons[0] + else: + result = newNodeIT(nkHiddenAddr, x.info, makeVarType(c, x.typ)) + addSon(result, x) + +proc newAsgnCall(c: PContext; op: PSym; x, y: PNode): PNode = + if sfError in op.flags: + localError(x.info, errWrongSymbolX, op.name.s) result = newNodeI(nkCall, x.info) - result.add(newSymNode(op)) - result.add x + result.add newSymNode(op) + result.add genAddr(c, x) result.add y proc newAsgnStmt(le, ri: PNode): PNode = @@ -127,63 +100,124 @@ proc newDestructorCall(op: PSym; x: PNode): PNode = proc newDeepCopyCall(op: PSym; x, y: PNode): PNode = result = newAsgnStmt(x, newDestructorCall(op, y)) -proc considerOverloadedOp(c: TLiftCtx; t: PType; x, y: PNode): bool = - let op = t.attachedOps[c.kind] - if op != nil: - markUsed(c.info, op) - styleCheckUse(c.info, op) - case c.kind - of attachedDestructor: - c.result.add newDestructorCall(op, x) - of attachedAsgn: - c.result.add newAsgnCall(op, x, y) - of attachedDeepCopy: - c.result.add newDeepCopyCall(op, x, y) - result = true - -proc defaultOp(c: TLiftCtx; t: PType; x, y: PNode) = +proc considerOverloadedOp(c: var TLiftCtx; t: PType; body, x, y: PNode): bool = + case c.kind + of attachedDestructor: + let op = t.destructor + if op != nil: + markUsed(c.info, op) + styleCheckUse(c.info, op) + body.add newDestructorCall(op, x) + result = true + of attachedAsgn: + if tfHasAsgn in t.flags: + var op: PSym + if sameType(t, c.asgnForType): + # generate recursive call: + if c.recurse: + op = c.fn + else: + c.recurse = true + return false + else: + op = t.assignment + if op == nil: + op = liftBody(c.c, t, c.info) + markUsed(c.info, op) + styleCheckUse(c.info, op) + body.add newAsgnCall(c.c, op, x, y) + result = true + of attachedDeepCopy: + let op = t.deepCopy + if op != nil: + markUsed(c.info, op) + styleCheckUse(c.info, op) + body.add newDeepCopyCall(op, x, y) + result = true + +proc defaultOp(c: var TLiftCtx; t: PType; body, x, y: PNode) = if c.kind != attachedDestructor: - c.result.add newAsgnStmt(x, y) - -proc liftBodyAux(c: TLiftCtx; t: PType; x, y: PNode) = - const hasAttachedOp: array[TTypeAttachedOp, TTypeIter] = [ - (proc (t: PType, closure: PObject): bool = - t.attachedOp[attachedDestructor] != nil), - (proc (t: PType, closure: PObject): bool = - t.attachedOp[attachedAsgn] != nil), - (proc (t: PType, closure: PObject): bool = - t.attachedOp[attachedDeepCopy] != nil)] + body.add newAsgnStmt(x, y) + +proc addVar(father, v, value: PNode) = + var vpart = newNodeI(nkIdentDefs, v.info, 3) + vpart.sons[0] = v + vpart.sons[1] = ast.emptyNode + vpart.sons[2] = value + addSon(father, vpart) + +proc declareCounter(c: var TLiftCtx; body: PNode; first: BiggestInt): PNode = + var temp = newSym(skTemp, getIdent(lowerings.genPrefix), c.fn, c.info) + temp.typ = getSysType(tyInt) + incl(temp.flags, sfFromGeneric) + + var v = newNodeI(nkVarSection, c.info) + result = newSymNode(temp) + v.addVar(result, lowerings.newIntLit(first)) + body.add v + +proc genBuiltin(magic: TMagic; name: string; i: PNode): PNode = + result = newNodeI(nkCall, i.info) + result.add createMagic(name, magic).newSymNode + result.add i + +proc genWhileLoop(c: var TLiftCtx; i, dest: PNode): PNode = + result = newNodeI(nkWhileStmt, c.info, 2) + let cmp = genBuiltin(mLeI, "<=", i) + cmp.add genHigh(dest) + cmp.typ = getSysType(tyBool) + result.sons[0] = cmp + result.sons[1] = newNodeI(nkStmtList, c.info) + +proc addIncStmt(body, i: PNode) = + let incCall = genBuiltin(mInc, "inc", i) + incCall.add lowerings.newIntLit(1) + body.add incCall + +proc newSeqCall(c: PContext; x, y: PNode): PNode = + # don't call genAddr(c, x) here: + result = genBuiltin(mNewSeq, "newSeq", x) + let lenCall = genBuiltin(mLengthSeq, "len", y) + lenCall.typ = getSysType(tyInt) + result.add lenCall + +proc liftBodyAux(c: var TLiftCtx; t: PType; body, x, y: PNode) = case t.kind of tyNone, tyEmpty: discard - of tyPointer, tySet, tyBool, tyChar, tyEnum, tyInt..tyUInt64, tyCString: - defaultOp(c, t, x, y) - of tyPtr, tyString: - if not considerOverloadedOp(c, t, x, y): - defaultOp(c, t, x, y) + of tyPointer, tySet, tyBool, tyChar, tyEnum, tyInt..tyUInt64, tyCString, + tyPtr, tyString, tyRef: + defaultOp(c, t, body, x, y) of tyArrayConstr, tyArray, tySequence: - if iterOverType(lastSon(t), hasAttachedOp[c.kind], nil): - # generate loop and call the attached Op: - + if tfHasAsgn in t.flags: + if t.kind == tySequence: + # XXX add 'nil' handling here + body.add newSeqCall(c.c, x, y) + let i = declareCounter(c, body, firstOrd(t)) + let whileLoop = genWhileLoop(c, i, x) + let elemType = t.lastSon + liftBodyAux(c, elemType, whileLoop.sons[1], x.at(i, elemType), + y.at(i, elemType)) + addIncStmt(whileLoop.sons[1], i) + body.add whileLoop else: - defaultOp(c, t, x, y) - of tyObject: - liftBodyObj(c, t.n, x, y) + defaultOp(c, t, body, x, y) + of tyObject, tyDistinct: + if not considerOverloadedOp(c, t, body, x, y): + if t.sons[0] != nil: liftBodyAux(c, t.sons[0], body, x, y) + if t.kind == tyObject: liftBodyObj(c, t.n, body, x, y) of tyTuple: - liftBodyTup(c, t, x, y) - of tyRef: - # we MUST NOT check for acyclic here as a DAG might still share nodes: - + liftBodyTup(c, t, body, x, y) of tyProc: if t.callConv != ccClosure or c.kind != attachedDeepCopy: - defaultOp(c, t, x, y) + defaultOp(c, t, body, x, y) else: # a big problem is that we don't know the enviroment's type here, so we # have to go through some indirection; we delegate this to the codegen: - call = newNodeI(nkCall, n.info, 2) + let call = newNodeI(nkCall, c.info, 2) call.typ = t call.sons[0] = newSymNode(createMagic("deepCopy", mDeepCopy)) call.sons[1] = y - c.result.add newAsgnStmt(x, call) + body.add newAsgnStmt(x, call) of tyVarargs, tyOpenArray: localError(c.info, errGenerated, "cannot copy openArray") of tyFromExpr, tyIter, tyProxy, tyBuiltInTypeClass, tyUserTypeClass, @@ -191,13 +225,60 @@ proc liftBodyAux(c: TLiftCtx; t: PType; x, y: PNode) = tyMutable, tyGenericParam, tyGenericBody, tyNil, tyExpr, tyStmt, tyTypeDesc, tyGenericInvocation, tyBigNum, tyConst, tyForward: internalError(c.info, "assignment requested for type: " & typeToString(t)) - of tyDistinct, tyOrdinal, tyRange, + of tyOrdinal, tyRange, tyGenericInst, tyFieldAccessor, tyStatic, tyVar: - liftBodyAux(c, lastSon(t)) + liftBodyAux(c, lastSon(t), body, x, y) + +proc 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 liftBody(c: PContext; typ: PType; info: TLineInfo): PNode = +proc addParam(procType: PType; param: PSym) = + param.position = procType.len-1 + addSon(procType.n, newSymNode(param)) + rawAddSon(procType, param.typ) + +proc liftBody(c: PContext; typ: PType; info: TLineInfo): PSym = var a: TLiftCtx a.info = info - a.result = newNodeI(nkStmtList, info) - liftBodyAux(a, typ) - result = a.result + let body = newNodeI(nkStmtList, info) + result = newSym(skProc, getIdent":lifted=", typ.owner, info) + a.fn = result + a.asgnForType = typ + + let dest = newSym(skParam, getIdent"dest", result, info) + let src = newSym(skParam, getIdent"src", result, info) + dest.typ = makeVarType(c, typ) + src.typ = typ + + result.typ = newProcType(info, typ.owner) + result.typ.addParam dest + result.typ.addParam src + + liftBodyAux(a, typ, body, newSymNode(dest).newDeref, newSymNode(src)) + + var n = newNodeI(nkProcDef, info, bodyPos+1) + for i in 0 .. < n.len: n.sons[i] = emptyNode + n.sons[namePos] = newSymNode(result) + n.sons[paramsPos] = result.typ.n + n.sons[bodyPos] = body + result.ast = n + + # register late as recursion is handled differently + typ.assignment = result + #echo "Produced this ", n + +proc getAsgnOrLiftBody(c: PContext; typ: PType; info: TLineInfo): PSym = + let t = typ.skipTypes({tyGenericInst, tyVar}) + result = t.assignment + if result.isNil: + result = liftBody(c, t, info) + +proc overloadedAsgn(c: PContext; dest, src: PNode): PNode = + let a = getAsgnOrLiftBody(c, dest.typ, dest.info) + result = newAsgnCall(c, a, dest, src) diff --git a/compiler/semdata.nim b/compiler/semdata.nim index 9b5d788af..345a8c0d1 100644 --- a/compiler/semdata.nim +++ b/compiler/semdata.nim @@ -43,10 +43,16 @@ type inst*: PInstantiation TExprFlag* = enum - efLValue, efWantIterator, efInTypeof, efWantStmt, efDetermineType, + efLValue, efWantIterator, efInTypeof, + efWantStmt, efAllowStmt, efDetermineType, efAllowDestructor, efWantValue, efOperand, efNoSemCheck TExprFlags* = set[TExprFlag] + TTypeAttachedOp* = enum + attachedAsgn, + attachedDeepCopy, + attachedDestructor + PContext* = ref TContext TContext* = object of TPassContext # a context represents a module module*: PSym # the module sym belonging to the context @@ -93,8 +99,8 @@ type lastGenericIdx*: int # used for the generics stack hloLoopDetector*: int # used to prevent endless loops in the HLO inParallelStmt*: int - instDeepCopy*: proc (c: PContext; dc: PSym; t: PType; - info: TLineInfo): PSym {.nimcall.} + instTypeBoundOp*: proc (c: PContext; dc: PSym; t: PType; info: TLineInfo; + op: TTypeAttachedOp): PSym {.nimcall.} proc makeInstPair*(s: PSym, inst: PInstantiation): TInstantiationPair = @@ -112,7 +118,6 @@ proc newOptionEntry*(): POptionEntry proc newLib*(kind: TLibKind): PLib proc addToLib*(lib: PLib, sym: PSym) proc makePtrType*(c: PContext, baseType: PType): PType -proc makeVarType*(c: PContext, baseType: PType): PType proc newTypeS*(kind: TTypeKind, c: PContext): PType proc fillTypeS*(dest: PType, kind: TTypeKind, c: PContext) @@ -207,9 +212,12 @@ proc makePtrType(c: PContext, baseType: PType): PType = result = newTypeS(tyPtr, c) addSonSkipIntLit(result, baseType.assertNotNil) -proc makeVarType(c: PContext, baseType: PType): PType = - result = newTypeS(tyVar, c) - addSonSkipIntLit(result, baseType.assertNotNil) +proc makeVarType*(c: PContext, baseType: PType): PType = + if baseType.kind == tyVar: + result = baseType + else: + result = newTypeS(tyVar, c) + addSonSkipIntLit(result, baseType.assertNotNil) proc makeTypeDesc*(c: PContext, typ: PType): PType = result = newTypeS(tyTypeDesc, c) @@ -241,6 +249,7 @@ proc makeAndType*(c: PContext, t1, t2: PType): PType = propagateToOwner(result, t1) propagateToOwner(result, t2) result.flags.incl((t1.flags + t2.flags) * {tfHasStatic}) + result.flags.incl tfHasMeta proc makeOrType*(c: PContext, t1, t2: PType): PType = result = newTypeS(tyOr, c) @@ -248,12 +257,14 @@ proc makeOrType*(c: PContext, t1, t2: PType): PType = propagateToOwner(result, t1) propagateToOwner(result, t2) result.flags.incl((t1.flags + t2.flags) * {tfHasStatic}) + result.flags.incl tfHasMeta proc makeNotType*(c: PContext, t1: PType): PType = result = newTypeS(tyNot, c) result.sons = @[t1] propagateToOwner(result, t1) result.flags.incl(t1.flags * {tfHasStatic}) + result.flags.incl tfHasMeta proc nMinusOne*(n: PNode): PNode = result = newNode(nkCall, n.info, @[ diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim index 28373e3c6..cd6ba3753 100644 --- a/compiler/semexprs.nim +++ b/compiler/semexprs.nim @@ -24,14 +24,14 @@ proc semFieldAccess(c: PContext, n: PNode, flags: TExprFlags = {}): PNode proc semOperand(c: PContext, n: PNode, flags: TExprFlags = {}): PNode = # same as 'semExprWithType' but doesn't check for proc vars result = semExpr(c, n, flags + {efOperand}) - if result.kind == nkEmpty: + if result.kind == nkEmpty and result.typ.isNil: # do not produce another redundant error message: #raiseRecoverableError("") result = errorNode(c, n) if result.typ != nil: # XXX tyGenericInst here? if result.typ.kind == tyVar: result = newDeref(result) - elif efWantStmt in flags: + elif {efWantStmt, efAllowStmt} * flags != {}: result.typ = newTypeS(tyEmpty, c) else: localError(n.info, errExprXHasNoType, @@ -389,7 +389,7 @@ proc isOpImpl(c: PContext, n: PNode): PNode = maybeLiftType(t2, c, n.info) var m: TCandidate initCandidate(c, m, t2) - let match = typeRel(m, t2, t1) != isNone + let match = typeRel(m, t2, t1) >= isSubtype # isNone result = newIntNode(nkIntLit, ord(match)) result.typ = n.typ @@ -447,6 +447,7 @@ proc changeType(n: PNode, newType: PType, check: bool) = of nkPar: let tup = newType.skipTypes({tyGenericInst}) if tup.kind != tyTuple: + if tup.kind == tyObject: return internalError(n.info, "changeType: no tuple type for constructor") elif sonsLen(n) > 0 and n.sons[0].kind == nkExprColonExpr: # named tuple? @@ -535,43 +536,55 @@ proc semArrayConstr(c: PContext, n: PNode, flags: TExprFlags): PNode = result.typ.sons[0] = makeRangeType(c, 0, sonsLen(result) - 1, n.info) proc fixAbstractType(c: PContext, n: PNode) = - # XXX finally rewrite that crap! - for i in countup(1, sonsLen(n) - 1): - var it = n.sons[i] - case it.kind - of nkHiddenStdConv, nkHiddenSubConv: - if it.sons[1].kind == nkBracket: - it.sons[1].typ = arrayConstrType(c, it.sons[1]) - #it.sons[1] = semArrayConstr(c, it.sons[1]) - if skipTypes(it.typ, abstractVar).kind in {tyOpenArray, tyVarargs}: - #if n.sons[0].kind == nkSym and IdentEq(n.sons[0].sym.name, "[]="): - # debug(n) - - var s = skipTypes(it.sons[1].typ, abstractVar) - if s.kind == tyArrayConstr and s.sons[1].kind == tyEmpty: - s = copyType(s, getCurrOwner(), false) - skipTypes(s, abstractVar).sons[1] = elemType( - skipTypes(it.typ, abstractVar)) - it.sons[1].typ = s - elif s.kind == tySequence and s.sons[0].kind == tyEmpty: - s = copyType(s, getCurrOwner(), false) - skipTypes(s, abstractVar).sons[0] = elemType( - skipTypes(it.typ, abstractVar)) - it.sons[1].typ = s - - elif skipTypes(it.sons[1].typ, abstractVar).kind in - {tyNil, tyArrayConstr, tyTuple, tySet}: + 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] - of nkBracket: - # an implicitly constructed array (passed to an open array): - n.sons[i] = semArrayConstr(c, it, {}) - else: - discard - #if (it.typ == nil): - # InternalError(it.info, "fixAbstractType: " & renderTree(it)) + when false: + # XXX finally rewrite that crap! + for i in countup(1, sonsLen(n) - 1): + var it = n.sons[i] + case it.kind + of nkHiddenStdConv, nkHiddenSubConv: + if it.sons[1].kind == nkBracket: + it.sons[1].typ = arrayConstrType(c, it.sons[1]) + #it.sons[1] = semArrayConstr(c, it.sons[1]) + if skipTypes(it.typ, abstractVar).kind in {tyOpenArray, tyVarargs}: + #if n.sons[0].kind == nkSym and IdentEq(n.sons[0].sym.name, "[]="): + # debug(n) + + var s = skipTypes(it.sons[1].typ, abstractVar) + if s.kind == tyArrayConstr and s.sons[1].kind == tyEmpty: + s = copyType(s, getCurrOwner(), false) + skipTypes(s, abstractVar).sons[1] = elemType( + skipTypes(it.typ, abstractVar)) + it.sons[1].typ = s + elif s.kind == tySequence and s.sons[0].kind == tyEmpty: + s = copyType(s, getCurrOwner(), false) + skipTypes(s, abstractVar).sons[0] = elemType( + skipTypes(it.typ, abstractVar)) + it.sons[1].typ = s + + elif skipTypes(it.sons[1].typ, abstractVar).kind in + {tyNil, tyArrayConstr, tyTuple, tySet}: + var s = skipTypes(it.typ, abstractVar) + if s.kind != tyExpr: + changeType(it.sons[1], s, check=true) + n.sons[i] = it.sons[1] + of nkBracket: + # an implicitly constructed array (passed to an open array): + n.sons[i] = semArrayConstr(c, it, {}) + else: + discard + #if (it.typ == nil): + # InternalError(it.info, "fixAbstractType: " & renderTree(it)) proc skipObjConv(n: PNode): PNode = case n.kind @@ -1298,6 +1311,9 @@ proc semAsgn(c: PContext, n: PNode): PNode = typeMismatch(n, lhs.typ, rhs.typ) n.sons[1] = fitNode(c, le, rhs) + if tfHasAsgn in lhs.typ.flags and not lhsIsResult: + return overloadedAsgn(c, lhs, n.sons[1]) + fixAbstractType(c, n) asgnToResultVar(c, n, n.sons[0], n.sons[1]) result = n @@ -2037,7 +2053,7 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode = of nkEmpty, nkNone, nkCommentStmt: discard of nkNilLit: - result.typ = getSysType(tyNil) + if result.typ == nil: result.typ = getSysType(tyNil) of nkIntLit: if result.typ == nil: setIntLitType(result) of nkInt8Lit: diff --git a/compiler/semfold.nim b/compiler/semfold.nim index 2e7179673..da24005c2 100644 --- a/compiler/semfold.nim +++ b/compiler/semfold.nim @@ -118,6 +118,9 @@ proc makeRange(typ: PType, first, last: BiggestInt): PType = let lowerNode = newIntNode(nkIntLit, minA) if typ.kind == tyInt and minA == maxA: result = getIntLitType(lowerNode) + elif typ.kind in {tyUint, tyUInt64}: + # these are not ordinal types, so you get no subrange type for these: + result = typ else: var n = newNode(nkRange) addSon(n, lowerNode) @@ -135,8 +138,9 @@ proc makeRangeF(typ: PType, first, last: BiggestFloat): PType = addSonSkipIntLit(result, skipTypes(typ, {tyRange})) proc getIntervalType*(m: TMagic, n: PNode): PType = - # Nimrod requires interval arithmetic for ``range`` types. Lots of tedious + # Nim requires interval arithmetic for ``range`` types. Lots of tedious # work but the feature is very nice for reducing explicit conversions. + const ordIntLit = {nkIntLit..nkUInt64Lit} result = n.typ template commutativeOp(opr: expr) {.immediate.} = @@ -170,13 +174,19 @@ proc getIntervalType*(m: TMagic, n: PNode): PType = let a = n.sons[1].typ if isFloatRange(a): # abs(-5.. 1) == (1..5) - result = makeRangeF(a, abs(getFloat(a.n.sons[1])), - abs(getFloat(a.n.sons[0]))) + if a.n[0].floatVal <= 0.0: + result = makeRangeF(a, 0.0, abs(getFloat(a.n.sons[0]))) + else: + result = makeRangeF(a, abs(getFloat(a.n.sons[1])), + abs(getFloat(a.n.sons[0]))) of mAbsI, mAbsI64: let a = n.sons[1].typ if isIntRange(a): - result = makeRange(a, `|abs|`(getInt(a.n.sons[1])), - `|abs|`(getInt(a.n.sons[0]))) + if a.n[0].intVal <= 0: + result = makeRange(a, 0, `|abs|`(getInt(a.n.sons[0]))) + else: + result = makeRange(a, `|abs|`(getInt(a.n.sons[1])), + `|abs|`(getInt(a.n.sons[0]))) of mSucc: let a = n.sons[1].typ let b = n.sons[2].typ @@ -202,15 +212,15 @@ proc getIntervalType*(m: TMagic, n: PNode): PType = var a = n.sons[1] var b = n.sons[2] # symmetrical: - if b.kind notin {nkIntLit..nkUInt32Lit}: swap(a, b) - if b.kind in {nkIntLit..nkUInt32Lit}: + if b.kind notin ordIntLit: swap(a, b) + if b.kind in ordIntLit: let x = b.intVal|+|1 if (x and -x) == x and x >= 0: result = makeRange(a.typ, 0, b.intVal) of mModU: let a = n.sons[1] let b = n.sons[2] - if b.kind in {nkIntLit..nkUInt32Lit}: + if a.kind in ordIntLit: if b.intVal >= 0: result = makeRange(a.typ, 0, b.intVal-1) else: @@ -226,9 +236,9 @@ proc getIntervalType*(m: TMagic, n: PNode): PType = result = makeRange(a.typ, b.intVal+1, -(b.intVal+1)) of mDivI, mDivI64, mDivU: binaryOp(`|div|`) - of mMinI, mMinI64: + of mMinI: commutativeOp(min) - of mMaxI, mMaxI64: + of mMaxI: commutativeOp(max) else: discard @@ -276,10 +286,14 @@ proc evalOp(m: TMagic, n, a, b, c: PNode): PNode = of mNot: result = newIntNodeT(1 - getInt(a), n) of mCard: result = newIntNodeT(nimsets.cardSet(a), n) of mBitnotI, mBitnotI64: result = newIntNodeT(not getInt(a), n) - of mLengthStr: result = newIntNodeT(len(getStr(a)), n) + of mLengthStr, mXLenStr: + if a.kind == nkNilLit: result = newIntNodeT(0, n) + else: result = newIntNodeT(len(getStr(a)), n) of mLengthArray: result = newIntNodeT(lengthOrd(a.typ), n) - of mLengthSeq, mLengthOpenArray: result = newIntNodeT(sonsLen(a), n) # BUGFIX - of mUnaryPlusI, mUnaryPlusI64, mUnaryPlusF64: result = a # throw `+` away + of mLengthSeq, mLengthOpenArray, mXLenSeq: + 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: result = newFloatNodeT(toFloat(int(getInt(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 mSubI, mSubI64: result = newIntNodeT(getInt(a) - getInt(b), n) of mMulI, mMulI64: result = newIntNodeT(getInt(a) * getInt(b), n) - of mMinI, mMinI64: + of mMinI: if getInt(a) > getInt(b): result = newIntNodeT(getInt(b), n) else: result = newIntNodeT(getInt(a), n) - of mMaxI, mMaxI64: + of mMaxI: if getInt(a) > getInt(b): result = newIntNodeT(getInt(a), n) else: result = newIntNodeT(getInt(b), n) of mShlI, mShlI64: @@ -420,7 +434,8 @@ proc evalOp(m: TMagic, n, a, b, c: PNode): PNode = mExit, mInc, ast.mDec, mEcho, mSwap, mAppendStrCh, mAppendStrStr, mAppendSeqElem, mSetLengthStr, mSetLengthSeq, mParseExprToAst, mParseStmtToAst, mExpandToAst, mTypeTrait, mDotDot, - mNLen..mNError, mEqRef, mSlurp, mStaticExec, mNGenSym, mSpawn, mParallel: + mNLen..mNError, mEqRef, mSlurp, mStaticExec, mNGenSym, mSpawn, + mParallel, mPlugin: discard else: internalError(a.info, "evalOp(" & $m & ')') @@ -513,7 +528,7 @@ proc rangeCheck(n: PNode, value: BiggestInt) = proc foldConv*(n, a: PNode; check = false): PNode = # XXX range checks? case skipTypes(n.typ, abstractRange).kind - of tyInt..tyInt64: + of tyInt..tyInt64, tyUInt..tyUInt64: case skipTypes(a.typ, abstractRange).kind of tyFloat..tyFloat64: result = newIntNodeT(int(getFloat(a)), n) @@ -640,7 +655,7 @@ proc getConstExpr(m: PSym, n: PNode): PNode = result = copyNode(n) of nkIfExpr: result = getConstIfExpr(m, n) - of nkCall, nkCommand, nkCallStrLit, nkPrefix, nkInfix: + of nkCallKinds: if n.sons[0].kind != nkSym: return var s = n.sons[0].sym if s.kind != skProc: return diff --git a/compiler/seminst.nim b/compiler/seminst.nim index dc36ecf34..b2aef63a8 100644 --- a/compiler/seminst.nim +++ b/compiler/seminst.nim @@ -176,7 +176,9 @@ proc instantiateProcType(c: PContext, pt: TIdTable, for i in 1 .. <result.len: # 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]) propagateToOwner(result, result.sons[i]) internalAssert originalParams[i].kind == nkSym @@ -185,7 +187,9 @@ proc instantiateProcType(c: PContext, pt: TIdTable, let param = copySym(oldParam) param.owner = prc 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])! result.n.sons[i] = newSymNode(param) addDecl(c, param) @@ -196,6 +200,7 @@ proc instantiateProcType(c: PContext, pt: TIdTable, addDecl(c, result.n.sons[i].sym) resetIdTable(cl.symMap) + resetIdTable(cl.localCache) result.sons[0] = replaceTypeVarsT(cl, result.sons[0]) result.n.sons[0] = originalParams[0].copyTree diff --git a/compiler/semmacrosanity.nim b/compiler/semmacrosanity.nim index 2ef7a54e7..bb9814a16 100644 --- a/compiler/semmacrosanity.nim +++ b/compiler/semmacrosanity.nim @@ -16,9 +16,9 @@ proc ithField(n: PNode, field: int): PSym = result = nil case n.kind of nkRecList: - for i in countup(0, sonsLen(n) - 1): + for i in countup(0, sonsLen(n) - 1): result = ithField(n.sons[i], field-i) - if result != nil: return + if result != nil: return of nkRecCase: if n.sons[0].kind != nkSym: internalError(n.info, "ithField") result = ithField(n.sons[0], field-1) @@ -34,7 +34,7 @@ proc ithField(n: PNode, field: int): PSym = else: discard proc annotateType*(n: PNode, t: PType) = - let x = t.skipTypes(abstractInst) + let x = t.skipTypes(abstractInst+{tyRange}) # Note: x can be unequal to t and we need to be careful to use 't' # to not to skip tyGenericInst case n.kind @@ -80,7 +80,7 @@ proc annotateType*(n: PNode, t: PType) = if x.kind in {tyString, tyCString}: n.typ = t else: - globalError(n.info, "string literal must be of some string type") + globalError(n.info, "string literal must be of some string type") of nkNilLit: if x.kind in NilableTypes: n.typ = t diff --git a/compiler/semmagic.nim b/compiler/semmagic.nim index de7700be6..0a7846f1d 100644 --- a/compiler/semmagic.nim +++ b/compiler/semmagic.nim @@ -54,7 +54,7 @@ proc evalTypeTrait(trait: PNode, operand: PType, context: PSym): PNode = result.typ = newType(tyString, context) result.info = trait.info of "arity": - result = newIntNode(nkIntLit, typ.n.len-1) + result = newIntNode(nkIntLit, typ.len - ord(typ.kind==tyProc)) result.typ = newType(tyInt, context) result.info = trait.info else: @@ -101,34 +101,6 @@ proc semBindSym(c: PContext, n: PNode): PNode = else: localError(n.sons[1].info, errUndeclaredIdentifier, sl.strVal) -proc semLocals(c: PContext, n: PNode): PNode = - var counter = 0 - var tupleType = newTypeS(tyTuple, c) - result = newNodeIT(nkPar, n.info, tupleType) - tupleType.n = newNodeI(nkRecList, n.info) - # for now we skip openarrays ... - for scope in walkScopes(c.currentScope): - if scope == c.topLevelScope: break - for it in items(scope.symbols): - # XXX parameters' owners are wrong for generics; this caused some pain - # for closures too; we should finally fix it. - #if it.owner != c.p.owner: return result - if it.kind in skLocalVars and - it.typ.skipTypes({tyGenericInst, tyVar}).kind notin - {tyVarargs, tyOpenArray, tyTypeDesc, tyStatic, tyExpr, tyStmt, tyEmpty}: - - var field = newSym(skField, it.name, getCurrOwner(), n.info) - field.typ = it.typ.skipTypes({tyGenericInst, tyVar}) - field.position = counter - inc(counter) - - addSon(tupleType.n, newSymNode(field)) - addSonSkipIntLit(tupleType, field.typ) - - var a = newSymNode(it, result.info) - if it.typ.skipTypes({tyGenericInst}).kind == tyVar: a = newDeref(a) - result.add(a) - proc semShallowCopy(c: PContext, n: PNode, flags: TExprFlags): PNode proc isStrangeArray(t: PType): bool = @@ -161,7 +133,6 @@ proc magicsAfterOverloadResolution(c: PContext, n: PNode, of mHigh, mLow: result = semLowHigh(c, n, n[0].sym.magic) of mShallowCopy: result = semShallowCopy(c, n, flags) of mNBindSym: result = semBindSym(c, n) - of mLocals: result = semLocals(c, n) of mProcCall: result = n result.typ = n[1].typ @@ -196,4 +167,11 @@ proc magicsAfterOverloadResolution(c: PContext, n: PNode, result.add newSymNode(createMagic("-", mSubI), n.info) result.add lenExprB result.add n.sons[1] + of mPlugin: + let plugin = getPlugin(n[0].sym) + if plugin.isNil: + localError(n.info, "cannot find plugin " & n[0].sym.name.s) + result = n + else: + result = plugin(c, n) else: result = n diff --git a/compiler/sempass2.nim b/compiler/sempass2.nim index 48f54fa6c..adf03be64 100644 --- a/compiler/sempass2.nim +++ b/compiler/sempass2.nim @@ -194,8 +194,38 @@ proc warnAboutGcUnsafe(n: PNode) = #assert false message(n.info, warnGcUnsafe, renderTree(n)) -template markGcUnsafe(a: PEffects) = +proc markGcUnsafe(a: PEffects; reason: PSym) = a.gcUnsafe = true + if a.owner.kind in routineKinds: a.owner.gcUnsafetyReason = reason + +proc markGcUnsafe(a: PEffects; reason: PNode) = + a.gcUnsafe = true + if a.owner.kind in routineKinds: + if reason.kind == nkSym: + a.owner.gcUnsafetyReason = reason.sym + else: + a.owner.gcUnsafetyReason = newSym(skUnknown, getIdent("<unknown>"), + a.owner, reason.info) + +proc listGcUnsafety(s: PSym; onlyWarning: bool) = + let u = s.gcUnsafetyReason + if u != nil: + let msgKind = if onlyWarning: warnGcUnsafe2 else: errGenerated + if u.kind in {skLet, skVar}: + message(s.info, msgKind, + ("'$#' is not GC-safe as it accesses '$#'" & + " which is a global using GC'ed memory") % [s.name.s, u.name.s]) + elif u.kind in routineKinds: + # recursive call *always* produces only a warning so the full error + # message is printed: + listGcUnsafety(u, true) + message(s.info, msgKind, + "'$#' is not GC-safe as it calls '$#'" % + [s.name.s, u.name.s]) + else: + internalAssert u.kind == skUnknown + message(u.info, msgKind, + "'$#' is not GC-safe as it performs an indirect call here" % s.name.s) proc useVar(a: PEffects, n: PNode) = let s = n.sym @@ -210,8 +240,8 @@ proc useVar(a: PEffects, n: PNode) = if {sfGlobal, sfThread} * s.flags == {sfGlobal} and s.kind in {skVar, skLet}: if s.guard != nil: guardGlobal(a, n, s.guard) if (tfHasGCedMem in s.typ.flags or s.typ.isGCedMem): - if warnGcUnsafe in gNotes: warnAboutGcUnsafe(n) - markGcUnsafe(a) + #if warnGcUnsafe in gNotes: warnAboutGcUnsafe(n) + markGcUnsafe(a, s) type 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 warnGcUnsafe in gNotes: warnAboutGcUnsafe(n) - markGcUnsafe(tracked) + markGcUnsafe(tracked, s) mergeLockLevels(tracked, n, s.getLockLevel) 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: if notGcSafe(op) and not isOwnedProcVar(a, tracked.owner): if warnGcUnsafe in gNotes: warnAboutGcUnsafe(n) - markGcUnsafe(tracked) + markGcUnsafe(tracked, a) else: mergeEffects(tracked, effectList.sons[exceptionEffects], n) mergeTags(tracked, effectList.sons[tagEffects], n) if notGcSafe(op): if warnGcUnsafe in gNotes: warnAboutGcUnsafe(n) - markGcUnsafe(tracked) + markGcUnsafe(tracked, a) notNilCheck(tracked, n, paramType) proc breaksBlock(n: PNode): bool = @@ -530,7 +560,10 @@ proc trackCase(tracked: PEffects, n: PNode) = track(tracked, n.sons[0]) let oldState = tracked.init.len let oldFacts = tracked.guards.len - let interesting = interestingCaseExpr(n.sons[0]) and warnProveField in gNotes + let stringCase = skipTypes(n.sons[0].typ, + abstractVarRange-{tyTypeDesc}).kind in {tyFloat..tyFloat128, tyString} + let interesting = not stringCase and interestingCaseExpr(n.sons[0]) and + warnProveField in gNotes var inter: TIntersection = @[] var toCover = 0 for i in 1.. <n.len: @@ -545,13 +578,8 @@ proc trackCase(tracked: PEffects, n: PNode) = for i in oldState.. <tracked.init.len: addToIntersection(inter, tracked.init[i]) - let exh = case skipTypes(n.sons[0].typ, abstractVarRange-{tyTypeDesc}).kind - of tyFloat..tyFloat128, tyString: - lastSon(n).kind == nkElse - else: - true setLen(tracked.init, oldState) - if exh: + if not stringCase or lastSon(n).kind == nkElse: for id, count in items(inter): if count >= toCover: tracked.init.add id # else we can't merge @@ -658,7 +686,7 @@ proc track(tracked: PEffects, n: PNode) = # and it's not a recursive call: if not (a.kind == nkSym and a.sym == tracked.owner): warnAboutGcUnsafe(n) - markGcUnsafe(tracked) + markGcUnsafe(tracked, a) for i in 1 .. <len(n): trackOperand(tracked, n.sons[i], paramType(op, i)) if a.kind == nkSym and a.sym.magic in {mNew, mNewFinalize, mNewSeq}: # may not look like an assignment, but it is: @@ -684,8 +712,8 @@ proc track(tracked: PEffects, n: PNode) = of nkVarSection, nkLetSection: for child in n: let last = lastSon(child) + if last.kind != nkEmpty: track(tracked, last) if child.kind == nkIdentDefs and last.kind != nkEmpty: - track(tracked, last) for i in 0 .. child.len-3: initVar(tracked, child.sons[i], volatileCheck=false) addAsgnFact(tracked.guards, child.sons[i], last) @@ -734,7 +762,7 @@ proc track(tracked: PEffects, n: PNode) = setLen(tracked.locked, oldLocked) tracked.currLockLevel = oldLockLevel of nkTypeSection, nkProcDef, nkConverterDef, nkMethodDef, nkIteratorDef, - nkMacroDef, nkTemplateDef: + nkMacroDef, nkTemplateDef, nkLambda, nkDo: discard else: for i in 0 .. <safeLen(n): track(tracked, n.sons[i]) @@ -853,9 +881,11 @@ proc trackProc*(s: PSym, body: PNode) = if sfThread in s.flags and t.gcUnsafe: if optThreads in gGlobalOptions and optThreadAnalysis in gGlobalOptions: - localError(s.info, "'$1' is not GC-safe" % s.name.s) + #localError(s.info, "'$1' is not GC-safe" % s.name.s) + listGcUnsafety(s, onlyWarning=false) else: - localError(s.info, warnGcUnsafe2, s.name.s) + listGcUnsafety(s, onlyWarning=true) + #localError(s.info, warnGcUnsafe2, s.name.s) if not t.gcUnsafe: s.typ.flags.incl tfGcSafe if s.typ.lockLevel == UnspecifiedLockLevel: diff --git a/compiler/semstmts.nim b/compiler/semstmts.nim index 7263b21b9..c355a5bf1 100644 --- a/compiler/semstmts.nim +++ b/compiler/semstmts.nim @@ -102,10 +102,6 @@ proc semDestructorCheck(c: PContext, n: PNode, flags: TExprFlags) {.inline.} = c.p.owner.kind notin {skTemplate, skMacro}: localError(n.info, errGenerated, "value expected, but got a type") -proc newDeref(n: PNode): PNode {.inline.} = - result = newNodeIT(nkHiddenDeref, n.info, n.typ.sons[0]) - addSon(result, n) - proc semExprBranch(c: PContext, n: PNode): PNode = result = semExpr(c, n) if result.typ != nil: @@ -340,6 +336,44 @@ proc checkNilable(v: PSym) = elif tfNotNil in v.typ.flags and tfNotNil notin v.ast.typ.flags: message(v.info, warnProveInit, v.name.s) +include semasgn + +proc addToVarSection(c: PContext; result: var PNode; orig, identDefs: PNode) = + # consider this: + # var + # x = 0 + # withOverloadedAssignment = foo() + # y = use(withOverloadedAssignment) + # We need to split this into a statement list with multiple 'var' sections + # in order for this transformation to be correct. + let L = identDefs.len + let value = identDefs[L-1] + if value.typ != nil and tfHasAsgn in value.typ.flags: + # the spec says we need to rewrite 'var x = T()' to 'var x: T; x = T()': + identDefs.sons[L-1] = emptyNode + if result.kind != nkStmtList: + let oldResult = result + oldResult.add identDefs + result = newNodeI(nkStmtList, result.info) + result.add oldResult + else: + let o = copyNode(orig) + o.add identDefs + result.add o + for i in 0 .. L-3: + result.add overloadedAsgn(c, identDefs[i], value) + elif result.kind == nkStmtList: + let o = copyNode(orig) + o.add identDefs + result.add o + else: + result.add identDefs + +proc isDiscardUnderscore(v: PSym): bool = + if v.name.s == "_": + v.flags.incl(sfGenSym) + result = true + proc semVarOrLet(c: PContext, n: PNode, symkind: TSymKind): PNode = var b: PNode result = copyNode(n) @@ -396,14 +430,15 @@ proc semVarOrLet(c: PContext, n: PNode, symkind: TSymKind): PNode = newSons(b, length) b.sons[length-2] = a.sons[length-2] # keep type desc for doc generator b.sons[length-1] = def - addSon(result, b) + addToVarSection(c, result, n, b) elif tup.kind == tyTuple and def.kind == nkPar and a.kind == nkIdentDefs and a.len > 3: message(a.info, warnEachIdentIsTuple) for j in countup(0, length-3): var v = semIdentDef(c, a.sons[j], symkind) - if sfGenSym notin v.flags: addInterfaceDecl(c, v) + if sfGenSym notin v.flags and not isDiscardUnderscore(v): + addInterfaceDecl(c, v) when oKeepVariableNames: if c.inUnrolledContext > 0: v.flags.incl(sfShadowed) else: @@ -429,7 +464,7 @@ proc semVarOrLet(c: PContext, n: PNode, symkind: TSymKind): PNode = addSon(b, newSymNode(v)) addSon(b, a.sons[length-2]) # keep type desc for doc generator addSon(b, copyTree(def)) - addSon(result, b) + addToVarSection(c, result, n, b) else: if def.kind == nkPar: v.ast = def[j] v.typ = tup.sons[j] @@ -518,7 +553,8 @@ proc semForVars(c: PContext, n: PNode): PNode = if getCurrOwner().kind == skModule: incl(v.flags, sfGlobal) v.typ = iter.sons[i] n.sons[i] = newSymNode(v) - if sfGenSym notin v.flags: addForVarDecl(c, v) + if sfGenSym notin v.flags and not isDiscardUnderscore(v): + addForVarDecl(c, v) inc(c.p.nestedLoopCounter) n.sons[length-1] = semStmt(c, n.sons[length-1]) dec(c.p.nestedLoopCounter) @@ -654,6 +690,16 @@ proc typeSectionRightSidePass(c: PContext, n: PNode) = #debug s.typ s.ast = a popOwner() + let aa = a.sons[2] + if aa.kind in {nkRefTy, nkPtrTy} and aa.len == 1 and + aa.sons[0].kind == nkObjectTy: + # give anonymous object a dummy symbol: + var st = s.typ + if st.kind == tyGenericBody: st = st.lastSon + internalAssert st.kind in {tyPtr, tyRef} + internalAssert st.lastSon.sym == nil + st.lastSon.sym = newSym(skType, getIdent(s.name.s & ":ObjectType"), + getCurrOwner(), s.info) proc checkForMetaFields(n: PNode) = template checkMeta(t) = @@ -697,16 +743,6 @@ proc typeSectionFinalPass(c: PContext, n: PNode) = checkConstructedType(s.info, s.typ) if s.typ.kind in {tyObject, tyTuple} and not s.typ.n.isNil: checkForMetaFields(s.typ.n) - let aa = a.sons[2] - if aa.kind in {nkRefTy, nkPtrTy} and aa.len == 1 and - aa.sons[0].kind == nkObjectTy: - # give anonymous object a dummy symbol: - var st = s.typ - if st.kind == tyGenericBody: st = st.lastSon - internalAssert st.kind in {tyPtr, tyRef} - internalAssert st.lastSon.sym == nil - st.lastSon.sym = newSym(skType, getIdent(s.name.s & ":ObjectType"), - getCurrOwner(), s.info) proc semTypeSection(c: PContext, n: PNode): PNode = ## Processes a type section. This must be done in separate passes, in order @@ -909,11 +945,12 @@ proc maybeAddResult(c: PContext, s: PSym, n: PNode) = proc semOverride(c: PContext, s: PSym, n: PNode) = case s.name.s.normalize - of "destroy": + of "destroy", "=destroy": doDestructorStuff(c, s, n) if not experimentalMode(c): localError n.info, "use the {.experimental.} pragma to enable destructors" - of "deepcopy": + incl(s.flags, sfUsed) + of "deepcopy", "=deepcopy": if s.typ.len == 2 and s.typ.sons[1].skipTypes(abstractInst).kind in {tyRef, tyPtr} and sameType(s.typ.sons[1], s.typ.sons[0]): @@ -935,10 +972,35 @@ proc semOverride(c: PContext, s: PSym, n: PNode) = else: localError(n.info, errGenerated, "signature for 'deepCopy' must be proc[T: ptr|ref](x: T): T") - of "=": discard - else: localError(n.info, errGenerated, - "'destroy' or 'deepCopy' expected for 'override'") - incl(s.flags, sfUsed) + incl(s.flags, sfUsed) + of "=": + incl(s.flags, sfUsed) + let t = s.typ + if t.len == 3 and t.sons[0] == nil and t.sons[1].kind == tyVar: + var obj = t.sons[1].sons[0] + while true: + incl(obj.flags, tfHasAsgn) + if obj.kind == tyGenericBody: obj = obj.lastSon + elif obj.kind == tyGenericInvocation: obj = obj.sons[0] + else: break + var objB = t.sons[2] + while true: + if objB.kind == tyGenericBody: objB = objB.lastSon + elif objB.kind == tyGenericInvocation: objB = objB.sons[0] + else: break + if obj.kind in {tyObject, tyDistinct} and sameType(obj, objB): + if obj.assignment.isNil: + obj.assignment = s + else: + localError(n.info, errGenerated, + "cannot bind another '=' to: " & typeToString(obj)) + return + localError(n.info, errGenerated, + "signature for '=' must be proc[T: object](x: var T; y: T)") + else: + if sfOverriden in s.flags: + localError(n.info, errGenerated, + "'destroy' or 'deepCopy' expected for 'override'") type TProcCompilationSteps = enum @@ -970,7 +1032,7 @@ proc semProcAux(c: PContext, n: PNode, kind: TSymKind, s = semIdentDef(c, n.sons[0], kind) n.sons[namePos] = newSymNode(s) s.ast = n - s.scope = c.currentScope + #s.scope = c.currentScope if sfNoForward in c.module.flags and sfSystemModule notin c.module.flags: @@ -982,14 +1044,14 @@ proc semProcAux(c: PContext, n: PNode, kind: TSymKind, s.owner = getCurrOwner() typeIsDetermined = s.typ == nil s.ast = n - s.scope = c.currentScope + #s.scope = c.currentScope # if typeIsDetermined: assert phase == stepCompileBody # else: assert phase == stepDetermineType # before compiling the proc body, set as current the scope # where the proc was declared let oldScope = c.currentScope - c.currentScope = s.scope + #c.currentScope = s.scope pushOwner(s) openScope(c) var gp: PNode @@ -1014,7 +1076,7 @@ proc semProcAux(c: PContext, n: PNode, kind: TSymKind, if s.kind in skIterators: s.typ.flags.incl(tfIterator) - var proto = searchForProc(c, s.scope, s) + var proto = searchForProc(c, oldScope, s) if proto == nil: if s.kind == skClosureIterator: s.typ.callConv = ccClosure else: s.typ.callConv = lastOptionEntry(c).defaultCC @@ -1022,10 +1084,10 @@ proc semProcAux(c: PContext, n: PNode, kind: TSymKind, if sfGenSym in s.flags: discard elif kind in OverloadableSyms: if not typeIsDetermined: - addInterfaceOverloadableSymAt(c, s.scope, s) + addInterfaceOverloadableSymAt(c, oldScope, s) else: if not typeIsDetermined: - addInterfaceDeclAt(c, s.scope, s) + addInterfaceDeclAt(c, oldScope, s) if n.sons[pragmasPos].kind != nkEmpty: pragma(c, s, n.sons[pragmasPos], validPragmas) else: @@ -1055,7 +1117,7 @@ proc semProcAux(c: PContext, n: PNode, kind: TSymKind, popOwner() pushOwner(s) s.options = gOptions - if sfOverriden in s.flags: semOverride(c, s, n) + if sfOverriden in s.flags or s.name.s[0] == '=': semOverride(c, s, n) if n.sons[bodyPos].kind != nkEmpty: # for DLL generation it is annoying to check for sfImportc! if sfBorrow in s.flags: @@ -1093,7 +1155,7 @@ proc semProcAux(c: PContext, n: PNode, kind: TSymKind, elif sfBorrow in s.flags: semBorrow(c, n, s) sideEffectsCheck(c, s) closeScope(c) # close scope for parameters - c.currentScope = oldScope + # c.currentScope = oldScope popOwner() if n.sons[patternPos].kind != nkEmpty: c.patterns.add(s) diff --git a/compiler/semtempl.nim b/compiler/semtempl.nim index b6efa5119..161d22fc1 100644 --- a/compiler/semtempl.nim +++ b/compiler/semtempl.nim @@ -482,7 +482,7 @@ proc semTemplateDef(c: PContext, n: PNode): PNode = s = semIdentVis(c, skTemplate, n.sons[0], {}) styleCheckDef(s) # check parameter list: - s.scope = c.currentScope + #s.scope = c.currentScope pushOwner(s) openScope(c) n.sons[namePos] = newSymNode(s, n.sons[namePos].info) diff --git a/compiler/semtypes.nim b/compiler/semtypes.nim index 1da4d7352..8c7bd7243 100644 --- a/compiler/semtypes.nim +++ b/compiler/semtypes.nim @@ -261,7 +261,8 @@ proc semArray(c: PContext, n: PNode, prev: PType): PType = if not isOrdinalType(indx): localError(n.sons[1].info, errOrdinalTypeExpected) elif enumHasHoles(indx): - localError(n.sons[1].info, errEnumXHasHoles, indx.sym.name.s) + localError(n.sons[1].info, errEnumXHasHoles, + typeToString(indx.skipTypes({tyRange}))) base = semTypeNode(c, n.sons[2], nil) addSonSkipIntLit(result, base) else: @@ -593,7 +594,7 @@ proc semRecordNodeAux(c: PContext, n: PNode, check: var IntSet, pos: var int, f.position = pos if (rec != nil) and ({sfImportc, sfExportc} * rec.flags != {}) and (f.loc.r == nil): - f.loc.r = toRope(f.name.s) + f.loc.r = rope(f.name.s) f.flags = f.flags + ({sfImportc, sfExportc} * rec.flags) inc(pos) if containsOrIncl(check, f.name.id): @@ -628,7 +629,7 @@ proc skipGenericInvocation(t: PType): PType {.inline.} = result = t if result.kind == tyGenericInvocation: result = result.sons[0] - if result.kind == tyGenericBody: + while result.kind in {tyGenericInst, tyGenericBody}: result = lastSon(result) proc addInheritedFields(c: PContext, check: var IntSet, pos: var int, @@ -646,14 +647,17 @@ proc semObjectNode(c: PContext, n: PNode, prev: PType): PType = # n.sons[0] contains the pragmas (if any). We process these later... checkSonsLen(n, 3) if n.sons[1].kind != nkEmpty: - base = skipTypes(semTypeNode(c, n.sons[1].sons[0], nil), skipPtrs) - var concreteBase = skipGenericInvocation(base).skipTypes(skipPtrs) - if concreteBase.kind == tyObject and tfFinal notin concreteBase.flags: - addInheritedFields(c, check, pos, concreteBase) + base = skipTypesOrNil(semTypeNode(c, n.sons[1].sons[0], nil), skipPtrs) + if base.isNil: + localError(n.info, errIllegalRecursionInTypeX, "object") else: - if concreteBase.kind != tyError: - localError(n.sons[1].info, errInheritanceOnlyWithNonFinalObjects) - base = nil + var concreteBase = skipGenericInvocation(base).skipTypes(skipPtrs) + if concreteBase.kind == tyObject and tfFinal notin concreteBase.flags: + addInheritedFields(c, check, pos, concreteBase) + else: + if concreteBase.kind != tyError: + localError(n.sons[1].info, errInheritanceOnlyWithNonFinalObjects) + base = nil if n.kind != nkObjectTy: internalError(n.info, "semObjectNode") result = newOrPrevType(tyObject, prev, c) rawAddSon(result, base) @@ -786,7 +790,7 @@ proc liftParamType(c: PContext, procKind: TSymKind, genericParams: PNode, @[newTypeS(paramType.kind, c)]) result = addImplicitGeneric(typ) else: - for i in 0 .. <paramType.sons.len: + for i in 0 .. <paramType.len: if paramType.sons[i] == paramType: globalError(info, errIllegalRecursionInTypeX, typeToString(paramType)) var lifted = liftingWalk(paramType.sons[i]) @@ -831,7 +835,7 @@ proc liftParamType(c: PContext, procKind: TSymKind, genericParams: PNode, cp.kind = tyUserTypeClassInst return addImplicitGeneric(cp) - for i in 1 .. (paramType.sons.len - 2): + for i in 1 .. paramType.len-2: var lifted = liftingWalk(paramType.sons[i]) if lifted != nil: paramType.sons[i] = lifted @@ -844,7 +848,7 @@ proc liftParamType(c: PContext, procKind: TSymKind, genericParams: PNode, result.shouldHaveMeta of tyGenericInvocation: - for i in 1 .. <paramType.sonsLen: + for i in 1 .. <paramType.len: let lifted = liftingWalk(paramType.sons[i]) if lifted != nil: paramType.sons[i] = lifted when false: diff --git a/compiler/semtypinst.nim b/compiler/semtypinst.nim index 5779c3c4b..c5caf8b92 100644 --- a/compiler/semtypinst.nim +++ b/compiler/semtypinst.nim @@ -16,9 +16,9 @@ const proc sharedPtrCheck(info: TLineInfo, t: PType) = if t.kind == tyPtr and t.len > 1: - if t.sons[0].sym.magic in {mShared, mGuarded}: + if t.sons[0].sym.magic == mShared: incl(t.flags, tfShared) - if t.sons[0].sym.magic == mGuarded: incl(t.flags, tfGuarded) + #if t.sons[0].sym.magic == mGuarded: incl(t.flags, tfGuarded) if tfHasGCedMem in t.flags or t.isGCedMem: localError(info, errGenerated, "shared memory may not refer to GC'ed thread local memory") @@ -233,7 +233,9 @@ proc instCopyType*(cl: var TReplTypeVars, t: PType): PType = # XXX: relying on allowMetaTypes is a kludge result = copyType(t, t.owner, cl.allowMetaTypes) result.flags.incl tfFromGeneric - result.flags.excl tfInstClearedFlags + if not (t.kind in tyMetaTypes or + (t.kind == tyStatic and t.n == nil)): + result.flags.excl tfInstClearedFlags proc handleGenericInvocation(cl: var TReplTypeVars, t: PType): PType = # tyGenericInvocation[A, tyGenericInvocation[A, B]] @@ -307,7 +309,13 @@ proc handleGenericInvocation(cl: var TReplTypeVars, t: PType): PType = if dc != nil and sfFromGeneric notin newbody.deepCopy.flags: # 'deepCopy' needs to be instantiated for # generics *when the type is constructed*: - newbody.deepCopy = cl.c.instDeepCopy(cl.c, dc, result, cl.info) + newbody.deepCopy = cl.c.instTypeBoundOp(cl.c, dc, result, cl.info, + attachedDeepCopy) + let asgn = newbody.assignment + if asgn != nil and sfFromGeneric notin asgn.flags: + # '=' needs to be instantiated for generics when the type is constructed: + newbody.assignment = cl.c.instTypeBoundOp(cl.c, asgn, result, cl.info, + attachedAsgn) proc eraseVoidParams*(t: PType) = # transform '(): void' into '()' because old parts of the compiler really @@ -412,15 +420,23 @@ proc replaceTypeVarsTAux(cl: var TReplTypeVars, t: PType): PType = result = t of tyGenericInst: + result = PType(idTableGet(cl.localCache, t)) + if result != nil: return result result = instCopyType(cl, t) + idTablePut(cl.localCache, t, result) for i in 1 .. <result.sonsLen: result.sons[i] = replaceTypeVarsT(cl, result.sons[i]) propagateToOwner(result, result.lastSon) else: if containsGenericType(t): + #if not cl.allowMetaTypes: + result = PType(idTableGet(cl.localCache, t)) + if result != nil: return result result = instCopyType(cl, t) result.size = -1 # needs to be recomputed + #if not cl.allowMetaTypes: + idTablePut(cl.localCache, t, result) for i in countup(0, sonsLen(result) - 1): if result.sons[i] != nil: diff --git a/compiler/sigmatch.nim b/compiler/sigmatch.nim index 1fce99e50..2a9d15b5a 100644 --- a/compiler/sigmatch.nim +++ b/compiler/sigmatch.nim @@ -99,9 +99,12 @@ proc initCandidate*(ctx: PContext, c: var TCandidate, callee: PSym, c.calleeSym = callee if callee.kind in skProcKinds and calleeScope == -1: if callee.originatingModule == ctx.module: - let rootSym = if sfFromGeneric notin callee.flags: callee - else: callee.owner - c.calleeScope = rootSym.scope.depthLevel + c.calleeScope = 2 + var owner = callee + while true: + owner = owner.skipGenericOwner + if owner.kind == skModule: break + inc c.calleeScope else: c.calleeScope = 1 else: @@ -144,6 +147,7 @@ proc copyCandidate(a: var TCandidate, b: TCandidate) = proc sumGeneric(t: PType): int = var t = t + var isvar = 1 while true: case t.kind of tyGenericInst, tyArray, tyRef, tyPtr, tyDistinct, tyArrayConstr, @@ -151,18 +155,20 @@ proc sumGeneric(t: PType): int = t = t.lastSon inc result of tyVar: - # but do not make 'var T' more specific than 'T'! t = t.sons[0] + inc result + inc isvar of tyGenericInvocation, tyTuple: - result = ord(t.kind == tyGenericInvocation) + result += ord(t.kind == tyGenericInvocation) for i in 0 .. <t.len: result += t.sons[i].sumGeneric break of tyGenericParam, tyExpr, tyStatic, tyStmt, tyTypeDesc: break of tyBool, tyChar, tyEnum, tyObject, tyProc, tyPointer, tyString, tyCString, tyInt..tyInt64, tyFloat..tyFloat128, tyUInt..tyUInt64: - return 1 - else: return 0 + return isvar + else: + return 0 #var ggDebug: bool @@ -916,6 +922,7 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation = of tyAnd: considerPreviousT: + result = isEqual for branch in f.sons: let x = typeRel(c, branch, aOrig) 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) result = isNone + of tyNone: + if a.kind == tyNone: result = isEqual else: - internalAssert false + internalError " unknown type kind " & $f.kind proc cmpTypes*(c: PContext, f, a: PType): TTypeRelation = var m: TCandidate @@ -1191,15 +1200,6 @@ proc isInlineIterator*(t: PType): bool = result = t.kind == tyIter or (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) = case r of isConvertible, isIntConv: inc(m.convMatches, convMatch) @@ -1304,7 +1304,7 @@ proc paramTypesMatchAux(m: var TCandidate, f, argType: PType, if arg.typ == nil: result = arg 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: result = arg.copyTree result.typ = getInstantiatedType(c, arg, m, f) @@ -1319,7 +1319,7 @@ proc paramTypesMatchAux(m: var TCandidate, f, argType: PType, inc(m.exactMatches) result = arg if skipTypes(f, abstractVar-{tyTypeDesc}).kind in {tyTuple}: - result = implicitConv(nkHiddenStdConv, f, arg, m, c) + result = implicitConv(nkHiddenSubConv, f, arg, m, c) of isNone: # do not do this in ``typeRel`` as it then can't infere T in ``ref T``: if a.kind in {tyProxy, tyUnknown}: @@ -1419,9 +1419,12 @@ proc prepareOperand(c: PContext; formal: PType; a: PNode): PNode = # a.typ == nil is valid result = a elif a.typ.isNil: + # XXX This is unsound! 'formal' can differ from overloaded routine to + # overloaded routine! let flags = if formal.kind == tyIter: {efDetermineType, efWantIterator} - elif formal.kind == tyStmt: {efDetermineType, efWantStmt} - else: {efDetermineType} + else: {efDetermineType, efAllowStmt} + #elif formal.kind == tyStmt: {efDetermineType, efWantStmt} + #else: {efDetermineType} result = c.semOperand(c, a, flags) else: result = a @@ -1462,9 +1465,7 @@ proc matchesAux(c: PContext, n, nOrig: PNode, m.state = csNoMatch return if formal.typ.kind == tyVar: - if n.isLValue: - inc(m.genericMatches, 100) - else: + if not n.isLValue: m.state = csNoMatch return @@ -1570,6 +1571,8 @@ proc matchesAux(c: PContext, n, nOrig: PNode, #assert(container == nil) if container.isNil: container = newNodeIT(nkBracket, n.sons[a].info, arrayConstr(c, arg)) + else: + incrIndexType(container.typ) addSon(container, arg) setSon(m.call, formal.position + 1, implicitConv(nkHiddenStdConv, formal.typ, container, m, c)) @@ -1627,12 +1630,15 @@ proc argtypeMatches*(c: PContext, f, a: PType): bool = # instantiate generic converters for that result = res != nil -proc instDeepCopy*(c: PContext; dc: PSym; t: PType; info: TLineInfo): PSym {. - procvar.} = +proc instTypeBoundOp*(c: PContext; dc: PSym; t: PType; info: TLineInfo; + op: TTypeAttachedOp): PSym {.procvar.} = var m: TCandidate initCandidate(c, m, dc.typ) var f = dc.typ.sons[1] - if f.kind in {tyRef, tyPtr}: f = f.lastSon + if op == attachedDeepCopy: + if f.kind in {tyRef, tyPtr}: f = f.lastSon + else: + if f.kind == tyVar: f = f.lastSon if typeRel(m, f, t) == isNone: localError(info, errGenerated, "cannot instantiate 'deepCopy'") else: diff --git a/compiler/transf.nim b/compiler/transf.nim index 2143b6bec..57547b682 100644 --- a/compiler/transf.nim +++ b/compiler/transf.nim @@ -379,6 +379,9 @@ proc transformConv(c: PTransf, n: PNode): PTransNode = result = transformSons(c, n) of tyOpenArray, tyVarargs: result = transform(c, n.sons[1]) + PNode(result).typ = takeType(n.typ, n.sons[1].typ) + #echo n.info, " came here and produced ", typeToString(PNode(result).typ), + # " from ", typeToString(n.typ), " and ", typeToString(n.sons[1].typ) of tyCString: if source.kind == tyString: result = newTransNode(nkStringToCString, n, 1) @@ -713,8 +716,7 @@ proc transform(c: PTransf, n: PNode): PTransNode = add(result, PTransNode(newSymNode(labl))) of nkBreakStmt: result = transformBreak(c, n) of nkWhileStmt: result = transformWhile(c, n) - of nkCall, nkHiddenCallConv, nkCommand, nkInfix, nkPrefix, nkPostfix, - nkCallStrLit: + of nkCallKinds: result = transformCall(c, n) of nkAddr, nkHiddenAddr: result = transformAddrDeref(c, n, nkDerefExpr, nkHiddenDeref) diff --git a/compiler/trees.nim b/compiler/trees.nim index 86a1139a0..2c631af99 100644 --- a/compiler/trees.nim +++ b/compiler/trees.nim @@ -9,40 +9,40 @@ # tree helper routines -import +import ast, astalgo, lexer, msgs, strutils, wordrecg -proc hasSon(father, son: PNode): bool = - for i in countup(0, sonsLen(father) - 1): - if father.sons[i] == son: +proc hasSon(father, son: PNode): bool = + for i in countup(0, sonsLen(father) - 1): + if father.sons[i] == son: return true result = false -proc cyclicTreeAux(n, s: PNode): bool = - if n == nil: +proc cyclicTreeAux(n, s: PNode): bool = + if n == nil: return false - if hasSon(s, n): + if hasSon(s, n): return true var m = sonsLen(s) addSon(s, n) - if not (n.kind in {nkEmpty..nkNilLit}): - for i in countup(0, sonsLen(n) - 1): - if cyclicTreeAux(n.sons[i], s): + if not (n.kind in {nkEmpty..nkNilLit}): + for i in countup(0, sonsLen(n) - 1): + if cyclicTreeAux(n.sons[i], s): return true result = false delSon(s, m) -proc cyclicTree*(n: PNode): bool = +proc cyclicTree*(n: PNode): bool = var s = newNodeI(nkEmpty, n.info) result = cyclicTreeAux(n, s) -proc exprStructuralEquivalent*(a, b: PNode): bool = +proc exprStructuralEquivalent*(a, b: PNode): bool = result = false - if a == b: + if a == b: result = true - elif (a != nil) and (b != nil) and (a.kind == b.kind): + elif (a != nil) and (b != nil) and (a.kind == b.kind): case a.kind - of nkSym: + of nkSym: # don't go nuts here: same symbol as string is enough: result = a.sym.name.id == b.sym.name.id of nkIdent: result = a.ident.id == b.ident.id @@ -50,12 +50,12 @@ proc exprStructuralEquivalent*(a, b: PNode): bool = of nkFloatLit..nkFloat64Lit: result = a.floatVal == b.floatVal of nkStrLit..nkTripleStrLit: result = a.strVal == b.strVal of nkEmpty, nkNilLit, nkType: result = true - else: - if sonsLen(a) == sonsLen(b): - for i in countup(0, sonsLen(a) - 1): - if not exprStructuralEquivalent(a.sons[i], b.sons[i]): return + else: + if sonsLen(a) == sonsLen(b): + for i in countup(0, sonsLen(a) - 1): + if not exprStructuralEquivalent(a.sons[i], b.sons[i]): return result = true - + proc sameTree*(a, b: PNode): bool = result = false if a == b: @@ -66,7 +66,7 @@ proc sameTree*(a, b: PNode): bool = if a.info.col != b.info.col: return #if a.info.fileIndex <> b.info.fileIndex then exit; case a.kind - of nkSym: + of nkSym: # don't go nuts here: same symbol as string is enough: result = a.sym.name.id == b.sym.name.id of nkIdent: result = a.ident.id == b.ident.id @@ -75,15 +75,15 @@ proc sameTree*(a, b: PNode): bool = of nkStrLit..nkTripleStrLit: result = a.strVal == b.strVal of nkEmpty, nkNilLit, nkType: result = true else: - if sonsLen(a) == sonsLen(b): - for i in countup(0, sonsLen(a) - 1): - if not sameTree(a.sons[i], b.sons[i]): return + if sonsLen(a) == sonsLen(b): + for i in countup(0, sonsLen(a) - 1): + if not sameTree(a.sons[i], b.sons[i]): return result = true - -proc getProcSym*(call: PNode): PSym = + +proc getProcSym*(call: PNode): PSym = result = call.sons[0].sym -proc getOpSym*(op: PNode): PSym = +proc getOpSym*(op: PNode): PSym = if op.kind notin {nkCall, nkHiddenCallConv, nkCommand, nkCallStrLit}: result = nil else: @@ -91,25 +91,25 @@ proc getOpSym*(op: PNode): PSym = elif op.sons[0].kind == nkSym: result = op.sons[0].sym else: result = nil -proc getMagic*(op: PNode): TMagic = +proc getMagic*(op: PNode): TMagic = case op.kind of nkCallKinds: case op.sons[0].kind of nkSym: result = op.sons[0].sym.magic else: result = mNone else: result = mNone - -proc treeToSym*(t: PNode): PSym = + +proc treeToSym*(t: PNode): PSym = result = t.sym -proc isConstExpr*(n: PNode): bool = +proc isConstExpr*(n: PNode): bool = result = (n.kind in - {nkCharLit..nkInt64Lit, nkStrLit..nkTripleStrLit, + {nkCharLit..nkInt64Lit, nkStrLit..nkTripleStrLit, nkFloatLit..nkFloat64Lit, nkNilLit}) or (nfAllConst in n.flags) proc isDeepConstExpr*(n: PNode): bool = case n.kind - of nkCharLit..nkInt64Lit, nkStrLit..nkTripleStrLit, + of nkCharLit..nkInt64Lit, nkStrLit..nkTripleStrLit, nkFloatLit..nkFloat64Lit, nkNilLit: result = true of nkExprEqExpr, nkExprColonExpr, nkHiddenStdConv, nkHiddenSubConv: @@ -122,33 +122,33 @@ proc isDeepConstExpr*(n: PNode): bool = result = n.typ.isNil or n.typ.skipTypes({tyGenericInst, tyDistinct}).kind != tyObject else: discard -proc flattenTreeAux(d, a: PNode, op: TMagic) = +proc flattenTreeAux(d, a: PNode, op: TMagic) = if (getMagic(a) == op): # a is a "leaf", so add it: for i in countup(1, sonsLen(a) - 1): # BUGFIX flattenTreeAux(d, a.sons[i], op) - else: + else: addSon(d, copyTree(a)) - -proc flattenTree*(root: PNode, op: TMagic): PNode = + +proc flattenTree*(root: PNode, op: TMagic): PNode = result = copyNode(root) if getMagic(root) == op: # BUGFIX: forget to copy prc addSon(result, copyNode(root.sons[0])) flattenTreeAux(result, root, op) -proc swapOperands*(op: PNode) = +proc swapOperands*(op: PNode) = var tmp = op.sons[1] op.sons[1] = op.sons[2] op.sons[2] = tmp -proc isRange*(n: PNode): bool {.inline.} = - if n.kind == nkInfix: +proc isRange*(n: PNode): bool {.inline.} = + if n.kind in nkCallKinds: if n[0].kind == nkIdent and n[0].ident.id == ord(wDotDot) or - n[0].kind in {nkClosedSymChoice, nkOpenSymChoice} and + n[0].kind in {nkClosedSymChoice, nkOpenSymChoice} and n[0][1].sym.name.id == ord(wDotDot): result = true -proc whichPragma*(n: PNode): TSpecialWord = +proc whichPragma*(n: PNode): TSpecialWord = let key = if n.kind == nkExprColonExpr: n.sons[0] else: n if key.kind == nkIdent: result = whichKeyword(key.ident) diff --git a/compiler/types.nim b/compiler/types.nim index 153c26a42..e205f5722 100644 --- a/compiler/types.nim +++ b/compiler/types.nim @@ -541,6 +541,9 @@ proc typeToString(typ: PType, prefer: TPreferedDesc = preferName): string = of tyProc: result = if tfIterator in t.flags: "iterator (" else: "proc (" for i in countup(1, sonsLen(t) - 1): + if t.n != nil and i < t.n.len and t.n[i].kind == nkSym: + add(result, t.n[i].sym.name.s) + add(result, ": ") add(result, typeToString(t.sons[i])) if i < sonsLen(t) - 1: add(result, ", ") add(result, ')') @@ -1436,3 +1439,45 @@ proc skipConv*(n: PNode): PNode = proc skipConvTakeType*(n: PNode): PNode = result = n.skipConv result.typ = n.typ + +proc isEmptyContainer*(t: PType): bool = + case t.kind + of tyExpr, tyNil: result = true + of tyArray, tyArrayConstr: result = t.sons[1].kind == tyEmpty + of tySet, tySequence, tyOpenArray, tyVarargs: + result = t.sons[0].kind == tyEmpty + of tyGenericInst: result = isEmptyContainer(t.lastSon) + else: result = false + +proc takeType*(formal, arg: PType): PType = + # param: openArray[string] = [] + # [] is an array constructor of length 0 of type string! + if arg.kind == tyNil: + # and not (formal.kind == tyProc and formal.callConv == ccClosure): + result = formal + elif formal.kind in {tyOpenArray, tyVarargs, tySequence} and + arg.isEmptyContainer: + let a = copyType(arg.skipTypes({tyGenericInst}), arg.owner, keepId=false) + a.sons[ord(arg.kind in {tyArray, tyArrayConstr})] = formal.sons[0] + result = a + elif formal.kind in {tyTuple, tySet} and arg.kind == formal.kind: + result = formal + else: + result = arg + +proc skipHiddenSubConv*(n: PNode): PNode = + if n.kind == nkHiddenSubConv: + # param: openArray[string] = [] + # [] is an array constructor of length 0 of type string! + let formal = n.typ + result = n.sons[1] + let arg = result.typ + let dest = takeType(formal, arg) + if dest == arg and formal.kind != tyExpr: + #echo n.info, " came here for ", formal.typeToString + result = n + else: + result = copyTree(result) + result.typ = dest + else: + result = n diff --git a/compiler/vm.nim b/compiler/vm.nim index 3b5c8e7f3..1c6c9a30b 100644 --- a/compiler/vm.nim +++ b/compiler/vm.nim @@ -16,7 +16,8 @@ import ast except getstr import strutils, astalgo, msgs, vmdef, vmgen, nimsets, types, passes, unsigned, - parser, vmdeps, idents, trees, renderer, options, transf, parseutils + parser, vmdeps, idents, trees, renderer, options, transf, parseutils, + vmmarshal from semfold import leValueConv, ordinalValToString from evaltempl import evalTemplate @@ -371,11 +372,6 @@ template handleJmpBack() {.dirty.} = globalError(c.debug[pc], errTooManyIterations) dec(c.loopIterations) -proc skipColon(n: PNode): PNode = - result = n - if n.kind == nkExprColonExpr: - result = n.sons[1] - proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg = var pc = start var tos = tos @@ -1043,7 +1039,14 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg = decodeB(rkNode) let newLen = regs[rb].intVal.int if regs[ra].node.isNil: stackTrace(c, tos, pc, errNilAccess) - else: setLen(regs[ra].node.sons, newLen) + else: + let oldLen = regs[ra].node.len + setLen(regs[ra].node.sons, newLen) + if oldLen < newLen: + # XXX This is still not entirely correct + # set to default value: + for i in oldLen .. <newLen: + regs[ra].node.sons[i] = newNodeI(nkEmpty, c.debug[pc]) of opcSwap: let rb = instr.regB if regs[ra].kind == regs[rb].kind: @@ -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] createStr regs[ra] regs[ra].node.strVal = typ.typeToString(preferExported) + of opcMarshalLoad: + let ra = instr.regA + let rb = instr.regB + inc pc + let typ = c.types[c.code[pc].regBx - wordExcess] + putIntoReg(regs[ra], loadAny(regs[rb].node.strVal, typ)) + of opcMarshalStore: + decodeB(rkNode) + inc pc + let typ = c.types[c.code[pc].regBx - wordExcess] + createStrKeepNode(regs[ra]) + if regs[ra].node.strVal.isNil: regs[ra].node.strVal = newStringOfCap(1000) + storeAny(regs[ra].node.strVal, typ, regs[rb].regToNode) inc pc proc execute(c: PCtx, start: int): PNode = diff --git a/compiler/vmdef.nim b/compiler/vmdef.nim index 90b9f2517..047009f01 100644 --- a/compiler/vmdef.nim +++ b/compiler/vmdef.nim @@ -16,7 +16,7 @@ const byteExcess* = 128 # we use excess-K for immediates wordExcess* = 32768 - MaxLoopIterations* = 500_000 # max iterations of all loops + MaxLoopIterations* = 1500_000 # max iterations of all loops type @@ -29,7 +29,7 @@ type opcRet, # return opcYldYoid, # yield with no value opcYldVal, # yield with a value - + opcAsgnInt, opcAsgnStr, opcAsgnFloat, @@ -48,8 +48,8 @@ type opcWrDeref, opcWrStrIdx, opcLdStrIdx, # a = b[c] - - opcAddInt, + + opcAddInt, opcAddImmInt, opcSubInt, opcSubImmInt, @@ -58,36 +58,37 @@ type opcIncl, opcInclRange, opcExcl, opcCard, opcMulInt, opcDivInt, opcModInt, opcAddFloat, opcSubFloat, opcMulFloat, opcDivFloat, opcShrInt, opcShlInt, - opcBitandInt, opcBitorInt, opcBitxorInt, opcAddu, opcSubu, opcMulu, - opcDivu, opcModu, opcEqInt, opcLeInt, opcLtInt, opcEqFloat, - opcLeFloat, opcLtFloat, opcLeu, opcLtu, opcEqRef, opcEqNimrodNode, opcXor, - opcNot, opcUnaryMinusInt, opcUnaryMinusFloat, opcBitnotInt, + opcBitandInt, opcBitorInt, opcBitxorInt, opcAddu, opcSubu, opcMulu, + opcDivu, opcModu, opcEqInt, opcLeInt, opcLtInt, opcEqFloat, + opcLeFloat, opcLtFloat, opcLeu, opcLtu, opcEqRef, opcEqNimrodNode, opcXor, + opcNot, opcUnaryMinusInt, opcUnaryMinusFloat, opcBitnotInt, opcEqStr, opcLeStr, opcLtStr, opcEqSet, opcLeSet, opcLtSet, opcMulSet, opcPlusSet, opcMinusSet, opcSymdiffSet, opcConcatStr, opcContainsSet, opcRepr, opcSetLenStr, opcSetLenSeq, opcSwap, opcIsNil, opcOf, opcIs, - opcSubStr, opcParseFloat, opcConv, opcCast, opcQuit, opcReset, + opcSubStr, opcParseFloat, opcConv, opcCast, + opcQuit, opcReset, opcNarrowS, opcNarrowU, - + opcAddStrCh, opcAddStrStr, opcAddSeqElem, opcRangeChck, - + opcNAdd, opcNAddMultiple, - opcNKind, - opcNIntVal, - opcNFloatVal, - opcNSymbol, + opcNKind, + opcNIntVal, + opcNFloatVal, + opcNSymbol, opcNIdent, opcNGetType, opcNStrVal, - + opcNSetIntVal, opcNSetFloatVal, opcNSetSymbol, opcNSetIdent, opcNSetType, opcNSetStrVal, opcNNewNimNode, opcNCopyNimNode, opcNCopyNimTree, opcNDel, opcGenSym, - + opcSlurp, opcGorge, opcParseExprToAst, @@ -100,7 +101,7 @@ type opcEqIdent, opcStrToIdent, opcIdentToStr, - + opcEcho, opcIndCall, # dest = call regStart, n; where regStart = fn, arg1, ... opcIndCallAsgn, # dest = call regStart, n; where regStart = fn, arg1, ... @@ -110,7 +111,7 @@ type opcNSetChild, opcCallSite, opcNewStr, - + opcTJmp, # jump Bx if A != 0 opcFJmp, # jump Bx if A == 0 opcJmp, # jump Bx @@ -132,7 +133,8 @@ type opcLdImmInt, # dest = immediate value opcNBindSym, opcSetType, # dest.typ = types[Bx] - opcTypeTrait + opcTypeTrait, + opcMarshalLoad, opcMarshalStore TBlock* = object label*: PSym @@ -178,13 +180,13 @@ type slots*: pointer currentException*: PNode VmCallback* = proc (args: VmArgs) {.closure.} - + PCtx* = ref TCtx TCtx* = object of passes.TPassContext # code gen context code*: seq[TInstr] debug*: seq[TLineInfo] # line info for every instruction; kept separate # to not slow down interpretation - globals*: PNode # + globals*: PNode # constants*: PNode # constant data types*: seq[PType] # some instructions reference types (e.g. 'except') currentExceptionA*, currentExceptionB*: PNode @@ -203,7 +205,7 @@ type TPosition* = distinct int PEvalContext* = PCtx - + proc newCtx*(module: PSym): PCtx = PCtx(code: @[], debug: @[], globals: newNode(nkStmtListExpr), constants: newNode(nkStmtList), types: @[], @@ -221,7 +223,8 @@ proc registerCallback*(c: PCtx; name: string; callback: VmCallback) = const firstABxInstr* = opcTJmp largeInstrs* = { # instructions which use 2 int32s instead of 1: - opcSubStr, opcConv, opcCast, opcNewSeq, opcOf} + opcSubStr, opcConv, opcCast, opcNewSeq, opcOf, + opcMarshalLoad, opcMarshalStore} slotSomeTemp* = slotTempUnknown relativeJumps* = {opcTJmp, opcFJmp, opcJmp, opcJmpBack} diff --git a/compiler/vmdeps.nim b/compiler/vmdeps.nim index 6148ed319..21ee4967b 100644 --- a/compiler/vmdeps.nim +++ b/compiler/vmdeps.nim @@ -144,7 +144,9 @@ proc mapTypeToAst(t: PType, info: TLineInfo; allowRecursion=false): PNode = of tyIter: result = mapTypeToBracket("iter", t, info) of tyProxy: result = atomicType"error" 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 tyAnd: result = mapTypeToBracket("and", t, info) of tyOr: result = mapTypeToBracket("or", t, info) diff --git a/compiler/vmgen.nim b/compiler/vmgen.nim index 3178bee60..0743a4502 100644 --- a/compiler/vmgen.nim +++ b/compiler/vmgen.nim @@ -76,6 +76,11 @@ proc codeListing(c: PCtx, result: var string, start=0; last = -1) = elif opc in {opcLdConst, opcAsgnConst}: result.addf("\t$#\tr$#, $#", ($opc).substr(3), x.regA, c.constants[x.regBx-wordExcess].renderTree) + elif opc in {opcMarshalLoad, opcMarshalStore}: + let y = c.code[i+1] + result.addf("\t$#\tr$#, r$#, $#", ($opc).substr(3), x.regA, x.regB, + c.types[y.regBx-wordExcess].typeToString) + inc i else: result.addf("\t$#\tr$#, $#", ($opc).substr(3), x.regA, x.regBx-wordExcess) result.add("\t#") @@ -368,7 +373,7 @@ proc sameConstant*(a, b: PNode): bool = case a.kind of nkSym: result = a.sym == b.sym of nkIdent: result = a.ident.id == b.ident.id - of nkCharLit..nkInt64Lit: result = a.intVal == b.intVal + of nkCharLit..nkUInt64Lit: result = a.intVal == b.intVal of nkFloatLit..nkFloat64Lit: result = a.floatVal == b.floatVal of nkStrLit..nkTripleStrLit: result = a.strVal == b.strVal of nkType, nkNilLit: result = a.typ == b.typ @@ -696,8 +701,7 @@ proc genCard(c: PCtx; n: PNode; dest: var TDest) = c.gABC(n, opcCard, dest, tmp) c.freeTemp(tmp) -proc genMagic(c: PCtx; n: PNode; dest: var TDest) = - let m = n.sons[0].sym.magic +proc genMagic(c: PCtx; n: PNode; dest: var TDest; m: TMagic) = case m of mAnd: c.genAndOr(n, opcFJmp, dest) of mOr: c.genAndOr(n, opcTJmp, dest) @@ -742,9 +746,9 @@ proc genMagic(c: PCtx; n: PNode; dest: var TDest) = c.gABC(n, opcNewStr, dest, tmp) c.freeTemp(tmp) # XXX buggy - of mLengthOpenArray, mLengthArray, mLengthSeq: + of mLengthOpenArray, mLengthArray, mLengthSeq, mXLenSeq: genUnaryABI(c, n, dest, opcLenSeq) - of mLengthStr: + of mLengthStr, mXLenStr: genUnaryABI(c, n, dest, opcLenStr) of mIncl, mExcl: unused(n, dest) @@ -791,7 +795,7 @@ proc genMagic(c: PCtx; n: PNode; dest: var TDest) = genUnaryABC(c, n, dest, opcUnaryMinusInt) genNarrow(c, n, dest) of mUnaryMinusF64: genUnaryABC(c, n, dest, opcUnaryMinusFloat) - of mUnaryPlusI, mUnaryPlusI64, mUnaryPlusF64: gen(c, n.sons[1], dest) + of mUnaryPlusI, mUnaryPlusF64: gen(c, n.sons[1], dest) of mBitnotI, mBitnotI64: genUnaryABC(c, n, dest, opcBitnotInt) genNarrowU(c, n, dest) @@ -1008,7 +1012,7 @@ proc genMagic(c: PCtx; n: PNode; dest: var TDest) = if dest < 0: dest = c.getTemp(n.typ) c.gABC(n, opcCallSite, dest) of mNGenSym: genBinaryABC(c, n, dest, opcGenSym) - of mMinI, mMaxI, mMinI64, mMaxI64, mAbsF64, mMinF64, mMaxF64, mAbsI, + of mMinI, mMaxI, mAbsF64, mMinF64, mMaxF64, mAbsI, mAbsI64, mDotDot: c.genCall(n, dest) of mExpandToAst: @@ -1028,6 +1032,22 @@ proc genMagic(c: PCtx; n: PNode; dest: var TDest) = # mGCref, mGCunref, internalError(n.info, "cannot generate code for: " & $m) +proc genMarshalLoad(c: PCtx, n: PNode, dest: var TDest) = + ## Signature: proc to*[T](data: string): T + if dest < 0: dest = c.getTemp(n.typ) + var tmp = c.genx(n.sons[1]) + c.gABC(n, opcMarshalLoad, dest, tmp) + c.gABx(n, opcMarshalLoad, 0, c.genType(n.typ)) + c.freeTemp(tmp) + +proc genMarshalStore(c: PCtx, n: PNode, dest: var TDest) = + ## Signature: proc `$$`*[T](x: T): string + if dest < 0: dest = c.getTemp(n.typ) + var tmp = c.genx(n.sons[1]) + c.gABC(n, opcMarshalStore, dest, tmp) + c.gABx(n, opcMarshalStore, 0, c.genType(n.sons[1].typ)) + c.freeTemp(tmp) + const atomicTypes = {tyBool, tyChar, tyExpr, tyStmt, tyTypeDesc, tyStatic, @@ -1364,7 +1384,7 @@ proc getNullValue(typ: PType, info: TLineInfo): PNode = of tyCString, tyString: result = newNodeIT(nkStrLit, info, t) of tyVar, tyPointer, tyPtr, tySequence, tyExpr, - tyStmt, tyTypeDesc, tyStatic, tyRef: + tyStmt, tyTypeDesc, tyStatic, tyRef, tyNil: result = newNodeIT(nkNilLit, info, t) of tyProc: if t.callConv != ccClosure: @@ -1391,7 +1411,7 @@ proc getNullValue(typ: PType, info: TLineInfo): PNode = addSon(result, getNullValue(t.sons[i], info)) of tySet: result = newNodeIT(nkCurly, info, t) - else: internalError("getNullValue: " & $t.kind) + else: internalError(info, "getNullValue: " & $t.kind) proc ldNullOpcode(t: PType): TOpcode = if fitsRegister(t): opcLdNullReg else: opcLdNull @@ -1533,6 +1553,15 @@ proc matches(s: PSym; x: string): bool = dec L result = true +proc matches(s: PSym; y: varargs[string]): bool = + var s = s + var L = y.len-1 + while L >= 0: + if s == nil or y[L].cmpIgnoreStyle(s.name.s) != 0: return false + s = if sfFromGeneric in s.flags: s.owner.owner else: s.owner + dec L + result = true + proc procIsCallback(c: PCtx; s: PSym): bool = if s.offset < -1: return true var i = -2 @@ -1570,8 +1599,17 @@ proc gen(c: PCtx; n: PNode; dest: var TDest; flags: TGenFlags = {}) = else: internalError(n.info, "cannot generate code for: " & s.name.s) of nkCallKinds: - if n.sons[0].kind == nkSym and n.sons[0].sym.magic != mNone: - genMagic(c, n, dest) + if n.sons[0].kind == nkSym: + let s = n.sons[0].sym + if s.magic != mNone: + genMagic(c, n, dest, s.magic) + elif matches(s, "stdlib", "marshal", "to"): + genMarshalLoad(c, n, dest) + elif matches(s, "stdlib", "marshal", "$$"): + genMarshalStore(c, n, dest) + else: + genCall(c, n, dest) + clearDest(c, n, dest) else: genCall(c, n, dest) clearDest(c, n, dest) @@ -1610,7 +1648,8 @@ proc gen(c: PCtx; n: PNode; dest: var TDest; flags: TGenFlags = {}) = genBreak(c, n) of nkTryStmt: genTry(c, n, dest) of nkStmtList: - unused(n, dest) + #unused(n, dest) + # XXX Fix this bug properly, lexim triggers it for x in n: gen(c, x) of nkStmtListExpr: let L = n.len-1 diff --git a/compiler/vmmarshal.nim b/compiler/vmmarshal.nim new file mode 100644 index 000000000..293d0d949 --- /dev/null +++ b/compiler/vmmarshal.nim @@ -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) diff --git a/compiler/vmops.nim b/compiler/vmops.nim index 502ad8ecc..1023d4783 100644 --- a/compiler/vmops.nim +++ b/compiler/vmops.nim @@ -10,7 +10,7 @@ # Unforunately this cannot be a module yet: #import vmdeps, vm from math import sqrt, ln, log10, log2, exp, round, arccos, arcsin, - arctan, arctan2, cos, cosh, hypot, sinh, sin, tan, tanh, pow, trunc, + arctan, arctan2, cos, cosh, hypot, sinh, sin, tan, tanh, pow, trunc, floor, ceil, fmod from os import getEnv, existsEnv, dirExists, fileExists diff --git a/config/nim.cfg b/config/nim.cfg index ccb9977db..fef7df79e 100644 --- a/config/nim.cfg +++ b/config/nim.cfg @@ -96,7 +96,7 @@ path="$lib/pure/unidecode" # Configuration for the GNU C/C++ compiler: @if windows: - #gcc.path = r"$nimrod\dist\mingw\bin" + #gcc.path = r"$nim\dist\mingw\bin" @if gcc: tlsEmulation:on @end diff --git a/csources b/csources new file mode 160000 +Subproject 15724e2e1f3e7749d508dfcd995e84fea285080 diff --git a/doc/grammar.txt b/doc/grammar.txt index b53515495..72dc6c974 100644 --- a/doc/grammar.txt +++ b/doc/grammar.txt @@ -179,7 +179,7 @@ simpleStmt = ((returnStmt | raiseStmt | yieldStmt | discardStmt | breakStmt | continueStmt | pragmaStmt | importStmt | exportStmt | fromStmt | includeStmt | commentStmt) / exprStmt) COMMENT? complexOrSimpleStmt = (ifStmt | whenStmt | whileStmt - | tryStmt | finallyStmt | exceptStmt | forStmt + | tryStmt | forStmt | blockStmt | staticStmt | deferStmt | asmStmt | 'proc' routine | 'method' routine diff --git a/doc/lib.txt b/doc/lib.txt index 385e7a91a..1c0278068 100644 --- a/doc/lib.txt +++ b/doc/lib.txt @@ -37,7 +37,7 @@ Core * `unsigned <unsigned.html>`_ This module implements basic arithmetic operators for unsigned integers. - To discourage users from using unsigned integers, it's not part + To discourage users from using unsigned integers, it's not part of ``system``, but an extra import. * `threads <threads.html>`_ @@ -45,7 +45,7 @@ Core import it explicitly. * `channels <channels.html>`_ - Nim message passing support for threads. **Note**: This is part of the + Nim message passing support for threads. **Note**: This is part of the system module. Do not import it explicitly. * `locks <locks.html>`_ @@ -55,7 +55,7 @@ Core Contains the AST API and documentation of Nim for writing macros. * `typeinfo <typeinfo.html>`_ - Provides (unsafe) access to Nim's run time type information. + Provides (unsafe) access to Nim's run time type information. * `typetraits <typetraits.html>`_ This module defines compile-time reflection procs for working with types. @@ -110,9 +110,9 @@ String handling * `unicode <unicode.html>`_ This module provides support to handle the Unicode UTF-8 encoding. - + * `encodings <encodings.html>`_ - Converts between different character encodings. On UNIX, this uses + Converts between different character encodings. On UNIX, this uses the ``iconv`` library, on Windows the Windows API. * `pegs <pegs.html>`_ @@ -159,7 +159,7 @@ Generic Operating System Services may provide other implementations for this standard stream interface. * `marshal <marshal.html>`_ - Contains procs for serialization and deseralization of arbitrary Nim + Contains procs for serialization and deseralization of arbitrary Nim data structures. * `terminal <terminal.html>`_ @@ -168,7 +168,7 @@ Generic Operating System Services sequences and does not depend on any other module. * `memfiles <memfiles.html>`_ - This module provides support for memory mapped files (Posix's ``mmap``) + This module provides support for memory mapped files (Posix's ``mmap``) on the different operating systems. * `fsmonitor <fsmonitor.html>`_ @@ -228,7 +228,7 @@ Internet Protocols and Support This module implements a simple HTTP client. * `smtp <smtp.html>`_ - This module implement a simple SMTP client. + This module implement a simple SMTP client. * `ftpclient <ftpclient.html>`_ This module implements an FTP client. @@ -346,7 +346,7 @@ XML Processing This module parses an HTML document and creates its XML tree representation. * `htmlgen <htmlgen.html>`_ - This module implements a simple XML and HTML code + This module implements a simple XML and HTML code generator. Each commonly used HTML tag has a corresponding macro that generates a string with its HTML representation. @@ -381,7 +381,7 @@ Miscellaneous * `oids <oids.html>`_ An OID is a global ID that consists of a timestamp, - a unique counter and a random value. This combination should suffice to + a unique counter and a random value. This combination should suffice to produce a globally distributed unique ID. This implementation was extracted from the Mongodb interface and it thus binary compatible with a Mongo OID. @@ -453,12 +453,8 @@ Other * `zipfiles <zipfiles.html>`_ 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>`_ - This module provides an easy to use sockets-style + This module provides an easy to use sockets-style Nim interface to the OpenSSL library. * `rdstdin <rdstdin.html>`_ @@ -513,25 +509,6 @@ Regular expressions 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 ------------- @@ -591,7 +568,7 @@ Data Compression and Archiving Scientific computing -------------------- -* `libsvm <libsvm.html>`_ +* `libsvm <libsvm.html>`_ Low level wrapper for `lib svm <http://www.csie.ntu.edu.tw/~cjlin/libsvm/>`_. Nimble diff --git a/doc/manual/generics.txt b/doc/manual/generics.txt index cce3a8830..2736f88fb 100644 --- a/doc/manual/generics.txt +++ b/doc/manual/generics.txt @@ -219,7 +219,7 @@ Concepts are written in the following form: (x < y) is bool Container[T] = concept c - c.len is ordinal + c.len is Ordinal items(c) is iterator for value in c: type(value) is T diff --git a/doc/manual/lexing.txt b/doc/manual/lexing.txt index e2f006f04..df6d85636 100644 --- a/doc/manual/lexing.txt +++ b/doc/manual/lexing.txt @@ -289,7 +289,7 @@ Numerical constants are of a single type and have the form:: INT32_LIT = INT_LIT ['\''] ('i' | 'I') '32' 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' UINT16_LIT = INT_LIT ['\''] ('u' | 'U') '16' UINT32_LIT = INT_LIT ['\''] ('u' | 'U') '32' diff --git a/doc/manual/pragmas.txt b/doc/manual/pragmas.txt index 34428233f..39aebd826 100644 --- a/doc/manual/pragmas.txt +++ b/doc/manual/pragmas.txt @@ -14,7 +14,7 @@ deprecated pragma The deprecated pragma is used to mark a symbol as deprecated: -.. code-block:: nimrod +.. code-block:: nim proc p() {.deprecated.} 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 renamings: -.. code-block:: nimrod +.. code-block:: nim type File = object Stream = ref object @@ -71,14 +71,13 @@ procedural variable. compileTime pragma ------------------ -The ``compileTime`` pragma is used to mark a proc to be used at compile -time only. No code will be generated for it. Compile time procs are useful -as helpers for macros. - +The ``compileTime`` pragma is used to mark a proc or variable to be used at +compile time only. No code will be generated for it. Compile time procs are +useful as helpers for macros. noReturn pragma --------------- -The ``noreturn`` pragma is used to mark a proc that never returns. +The ``noreturn`` pragma is used to mark a proc that never returns. acyclic pragma @@ -122,25 +121,25 @@ shallow pragma -------------- The ``shallow`` pragma affects the semantics of a type: The compiler is allowed to make a shallow copy. This can cause serious semantic issues and -break memory safety! However, it can speed up assignments considerably, -because the semantics of Nim require deep copying of sequences and strings. +break memory safety! However, it can speed up assignments considerably, +because the semantics of Nim require deep copying of sequences and strings. This can be expensive, especially if sequences are used to build a tree -structure: +structure: .. code-block:: nim type NodeKind = enum nkLeaf, nkInner Node {.final, shallow.} = object case kind: NodeKind - of nkLeaf: + of nkLeaf: strVal: string - of nkInner: + of nkInner: children: seq[Node] pure pragma ----------- -An object type can be marked with the ``pure`` pragma so that its type +An object type can be marked with the ``pure`` pragma so that its type field which is used for runtime type identification is omitted. This used to be necessary for binary compatibility with other compiled languages. @@ -163,12 +162,12 @@ error pragma ------------ The ``error`` pragma is used to make the compiler output an error message with the given content. Compilation does not necessarily abort after an error -though. +though. The ``error`` pragma can also be used to annotate a symbol (like an iterator or proc). The *usage* of the symbol then triggers a compile-time error. This is especially useful to rule out that some -operation is valid due to overloading and type conversions: +operation is valid due to overloading and type conversions: .. code-block:: nim ## check that underlying int values are compared and not the pointers: @@ -201,7 +200,7 @@ The ``line`` pragma can be used to affect line information of the annotated statement as seen in stack backtraces: .. code-block:: nim - + template myassert*(cond: expr, msg = "") = if not cond: # change run-time line information of the 'raise' statement: @@ -215,26 +214,26 @@ If the ``line`` pragma is used with a parameter, the parameter needs be a linearScanEnd pragma -------------------- -The ``linearScanEnd`` pragma can be used to tell the compiler how to +The ``linearScanEnd`` pragma can be used to tell the compiler how to compile a Nim `case`:idx: statement. Syntactically it has to be used as a statement: .. code-block:: nim case myInt - of 0: + of 0: echo "most common case" - of 1: + of 1: {.linearScanEnd.} echo "second most common case" of 2: echo "unlikely: use branch table" else: echo "unlikely too: use branch table for ", myInt -In the example, the case branches ``0`` and ``1`` are much more common than -the other cases. Therefore the generated assembler code should test for these -values first, so that the CPU's branch predictor has a good chance to succeed +In the example, the case branches ``0`` and ``1`` are much more common than +the other cases. Therefore the generated assembler code should test for these +values first, so that the CPU's branch predictor has a good chance to succeed (avoiding an expensive CPU pipeline stall). The other cases might be put into a jump table for O(1) overhead, but at the cost of a (very likely) pipeline -stall. +stall. The ``linearScanEnd`` pragma should be put into the last branch that should be tested against via linear scanning. If put into the last branch of the @@ -243,8 +242,8 @@ whole ``case`` statement, the whole ``case`` statement uses linear scanning. computedGoto pragma ------------------- -The ``computedGoto`` pragma can be used to tell the compiler how to -compile a Nim `case`:idx: in a ``while true`` statement. +The ``computedGoto`` pragma can be used to tell the compiler how to +compile a Nim `case`:idx: in a ``while true`` statement. Syntactically it has to be used as a statement inside the loop: .. code-block:: nim @@ -278,21 +277,21 @@ Syntactically it has to be used as a statement inside the loop: of enumE: break inc(pc) - + vm() As the example shows ``computedGoto`` is mostly useful for interpreters. If -the underlying backend (C compiler) does not support the computed goto +the underlying backend (C compiler) does not support the computed goto extension the pragma is simply ignored. unroll pragma ------------- The ``unroll`` pragma can be used to tell the compiler that it should unroll -a `for`:idx: or `while`:idx: loop for runtime efficiency: +a `for`:idx: or `while`:idx: loop for runtime efficiency: .. code-block:: nim - proc searchChar(s: string, c: char): int = + proc searchChar(s: string, c: char): int = for i in 0 .. s.high: {.unroll: 4.} if s[i] == c: return i @@ -440,7 +439,7 @@ Example: Please note that if a callable symbol is never used in this scenario, its body will never be compiled. This is the default behavior leading to best compilation - times, but if exhaustive compilation of all definitions is required, using + times, but if exhaustive compilation of all definitions is required, using ``nim check`` provides this option as well. Example: @@ -461,9 +460,9 @@ Example: pragma pragma ------------- -The ``pragma`` pragma can be used to declare user defined pragmas. This is -useful because Nim's templates and macros do not affect pragmas. User -defined pragmas are in a different module-wide scope than all other symbols. +The ``pragma`` pragma can be used to declare user defined pragmas. This is +useful because Nim's templates and macros do not affect pragmas. User +defined pragmas are in a different module-wide scope than all other symbols. They cannot be imported from a module. Example: @@ -473,8 +472,8 @@ Example: {.pragma: rtl, exportc, dynlib, cdecl.} else: {.pragma: rtl, importc, dynlib: "client.dll", cdecl.} - - proc p*(a, b: int): int {.rtl.} = + + proc p*(a, b: int): int {.rtl.} = result = a+b In the example a new pragma named ``rtl`` is introduced that either imports diff --git a/doc/manual/stmts.txt b/doc/manual/stmts.txt index 5b1284872..5e47110e9 100644 --- a/doc/manual/stmts.txt +++ b/doc/manual/stmts.txt @@ -2,7 +2,7 @@ Statements and expressions ========================== Nim uses the common statement/expression paradigm: Statements do not -produce a value in contrast to expressions. However, some expressions are +produce a value in contrast to expressions. However, some expressions are statements. Statements are separated into `simple statements`:idx: and @@ -16,9 +16,9 @@ statements always have to be intended. The details can be found in the grammar. Statement list expression ------------------------- -Statements can also occur in an expression context that looks +Statements can also occur in an expression context that looks like ``(stmt1; stmt2; ...; ex)``. This is called -an statement list expression or ``(;)``. The type +an statement list expression or ``(;)``. The type of ``(stmt1; stmt2; ...; ex)`` is the type of ``ex``. All the other statements must be of type ``void``. (One can use ``discard`` to produce a ``void`` type.) ``(;)`` does not introduce a new scope. @@ -30,24 +30,24 @@ Discard statement Example: .. code-block:: nim - proc p(x, y: int): int = + proc p(x, y: int): int = result = x + y discard p(3, 4) # discard the return value of `p` The ``discard`` statement evaluates its expression for side-effects and -throws the expression's resulting value away. +throws the expression's resulting value away. Ignoring the return value of a procedure without using a discard statement is a static error. The return value can be ignored implicitly if the called proc/iterator has -been declared with the `discardable`:idx: pragma: +been declared with the `discardable`:idx: pragma: .. code-block:: nim - proc p(x, y: int): int {.discardable.} = + proc p(x, y: int): int {.discardable.} = result = x + y - + p(3, 4) # now valid An empty ``discard`` statement is often used as a null statement: @@ -98,11 +98,11 @@ T = enum cast[T](0); this may be an invalid value The implicit initialization can be avoided for optimization reasons with the -`noinit`:idx: pragma: +`noinit`:idx: pragma: .. code-block:: nim var - a {.noInit.}: array [0..1023, char] + a {.noInit.}: array [0..1023, char] If a proc is annotated with the ``noinit`` pragma this refers to its implicit ``result`` variable: @@ -113,13 +113,13 @@ If a proc is annotated with the ``noinit`` pragma this refers to its implicit The implicit initialization can be also prevented by the `requiresInit`:idx: type pragma. The compiler requires an explicit initialization then. However -it does a `control flow analysis`:idx: to prove the variable has been +it does a `control flow analysis`:idx: to prove the variable has been initialized and does not rely on syntactic properties: .. code-block:: nim type MyObject = object {.requiresInit.} - + proc p() = # the following is valid: var x: MyObject @@ -129,11 +129,12 @@ initialized and does not rely on syntactic properties: x = a() use x + let statement ------------- A ``let`` statement declares new local and global `single assignment`:idx: -variables and binds a value to them. The syntax is the same as that of the ``var`` +variables and binds a value to them. The syntax is the same as that of the ``var`` statement, except that the keyword ``var`` is replaced by the keyword ``let``. Let variables are not l-values and can thus not be passed to ``var`` parameters nor can their address be taken. They cannot be assigned new values. @@ -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. +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 ------------- @@ -157,33 +171,33 @@ have no side-effect can be used in constant expressions too: constEval = contains("abc", 'b') # computed at compile time! -The rules for compile-time computability are: +The rules for compile-time computability are: 1. Literals are compile-time computable. 2. Type conversions are compile-time computable. 3. Procedure calls of the form ``p(X)`` are compile-time computable if - ``p`` is a proc without side-effects (see the `noSideEffect pragma`_ - for details) and if ``X`` is a (possibly empty) list of compile-time + ``p`` is a proc without side-effects (see the `noSideEffect pragma`_ + for details) and if ``X`` is a (possibly empty) list of compile-time computable arguments. -Constants cannot be of type ``ptr``, ``ref``, ``var`` or ``object``, nor can +Constants cannot be of type ``ptr``, ``ref``, ``var`` or ``object``, nor can they contain such a type. Static statement/expression --------------------------- -A static statement/expression can be used to enforce compile +A static statement/expression can be used to enforce compile time evaluation explicitly. Enforced compile time evaluation can even evaluate -code that has side effects: +code that has side effects: .. code-block:: static: echo "echo at compile time" -It's a static error if the compiler cannot perform the evaluation at compile +It's a static error if the compiler cannot perform the evaluation at compile time. The current implementation poses some restrictions for compile time @@ -217,7 +231,7 @@ the ``:`` are executed. This goes on until the last ``elif``. If all conditions fail, the ``else`` part is executed. If there is no ``else`` part, execution continues with the statement after the ``if`` statement. -The scoping for an ``if`` statement is slightly subtle to support an important +The scoping for an ``if`` statement is slightly subtle to support an important use case. A new scope starts for the ``if``/``elif`` condition and ends after the corresponding *then* block: @@ -229,7 +243,7 @@ the corresponding *then* block: else: # 'm' not declared here -In the example the scopes have been enclosed in ``{| |}``. +In the example the scopes have been enclosed in ``{| |}``. Case statement @@ -244,7 +258,7 @@ Example: echo("permission denied") of "go-for-a-walk": echo("please yourself") else: echo("unknown command") - + # indentation of the branches is also allowed; and so is an optional colon # after the selecting expression: case readline(stdin): @@ -252,15 +266,15 @@ Example: echo("permission denied") of "go-for-a-walk": echo("please yourself") else: echo("unknown command") - + The ``case`` statement is similar to the if statement, but it represents a multi-branch selection. The expression after the keyword ``case`` is evaluated and if its value is in a *slicelist* the corresponding statements (after the ``of`` keyword) are executed. If the value is not in any given *slicelist* the ``else`` part is executed. If there is no ``else`` -part and not all possible values that ``expr`` can hold occur in a -``slicelist``, a static error occurs. This holds only for expressions of +part and not all possible values that ``expr`` can hold occur in a +``slicelist``, a static error occurs. This holds only for expressions of ordinal types. "All possible values" of ``expr`` are determined by ``expr``'s type. To suppress the static error an ``else`` part with an empty ``discard`` statement should be used. @@ -281,7 +295,7 @@ expanded into a list of its elements: of SymChars, '_': echo "an identifier" of '0'..'9': echo "a number" else: echo "other" - + # is equivalent to: proc classify(s: string) = case s[0] @@ -580,14 +594,14 @@ A table constructor is syntactic sugar for an array constructor: .. code-block:: nim {"key1": "value1", "key2", "key3": "value2"} - + # is the same as: [("key1", "value1"), ("key2", "value2"), ("key3", "value2")] -The empty table can be written ``{:}`` (in contrast to the empty set +The empty table can be written ``{:}`` (in contrast to the empty set which is ``{}``) which is thus another way to write as the empty array -constructor ``[]``. This slightly unusal way of supporting tables +constructor ``[]``. This slightly unusal way of supporting tables has lots of advantages: * The order of the (key,value)-pairs is preserved, thus it is easy to diff --git a/doc/manual/syntax.txt b/doc/manual/syntax.txt index cf44eb588..24644bce2 100644 --- a/doc/manual/syntax.txt +++ b/doc/manual/syntax.txt @@ -15,8 +15,6 @@ Associativity Binary operators whose first character is ``^`` are right-associative, all other binary operators are left-associative. -Operators ending in ``>`` but longer than a single character are -called `arrow like`:idx:. 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 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 ``<``, ``>``, ``!``, ``=``, ``~``, ``?``, it is an *assignment operator* which -has the lowest precedence. +has the second lowest precedence. Otherwise precedence is determined by the first character. @@ -43,14 +44,14 @@ Otherwise precedence is determined by the first character. Precedence level Operators First character Terminal symbol ================ =============================================== ================== =============== 10 (highest) ``$ ^`` OP10 - 9 ``* / div mod shl shr %`` ``* % \ /`` OP9 - 8 ``+ -`` ``+ ~ |`` OP8 + 9 ``* / div mod shl shr %`` ``* % \ /`` OP9 + 8 ``+ -`` ``+ - ~ |`` OP8 7 ``&`` ``&`` OP7 6 ``..`` ``.`` OP6 - 5 ``== <= < >= > != in notin is isnot not of`` ``= < > !`` OP5 + 5 ``== <= < >= > != in notin is isnot not of`` ``= < > !`` OP5 4 ``and`` OP4 3 ``or xor`` OP3 - 2 ``@ : ?`` OP2 + 2 ``@ : ?`` OP2 1 *assignment operator* (like ``+=``, ``*=``) OP1 0 (lowest) *arrow like operator* (like ``->``, ``=>``) OP0 ================ =============================================== ================== =============== @@ -67,7 +68,7 @@ is still parsed as ``1 + (3 * 4)``, but ``1+3 * 4`` is parsed as ``(1+3) * 4``: .. code-block:: nim #! strongSpaces - if foo+4 * 4 == 8 and b&c | 9 ++ + if foo+4 * 4 == 8 and b&c | 9 ++ bar: echo "" # is parsed as diff --git a/doc/manual/threads.txt b/doc/manual/threads.txt index 245545f6f..fc3040c87 100644 --- a/doc/manual/threads.txt +++ b/doc/manual/threads.txt @@ -1,16 +1,16 @@ Threads ======= -To enable thread support the ``--threads:on`` command line switch needs to -be used. The ``system`` module then contains several threading primitives. -See the `threads <threads.html>`_ and `channels <channels.html>`_ modules +To enable thread support the ``--threads:on`` command line switch needs to +be used. The ``system`` module then contains several threading primitives. +See the `threads <threads.html>`_ and `channels <channels.html>`_ modules for the low level thread API. There are also high level parallelism constructs available. See `spawn`_ for further details. Nim's memory model for threads is quite different than that of other common -programming languages (C, Pascal, Java): Each thread has its own (garbage -collected) heap and sharing of memory is restricted to global variables. This -helps to prevent race conditions. GC efficiency is improved quite a lot, +programming languages (C, Pascal, Java): Each thread has its own (garbage +collected) heap and sharing of memory is restricted to global variables. This +helps to prevent race conditions. GC efficiency is improved quite a lot, because the GC never has to stop other threads and see what they reference. Memory allocation requires no lock at all! This design easily scales to massive multicore processors that are becoming the norm. @@ -22,10 +22,10 @@ Thread pragma A proc that is executed as a new thread of execution should be marked by the ``thread`` pragma for reasons of readability. The compiler checks for violations of the `no heap sharing restriction`:idx:\: This restriction implies -that it is invalid to construct a data structure that consists of memory +that it is invalid to construct a data structure that consists of memory allocated from different (thread local) heaps. -A thread proc is passed to ``createThread`` or ``spawn`` and invoked +A thread proc is passed to ``createThread`` or ``spawn`` and invoked indirectly; so the ``thread`` pragma implies ``procvar``. @@ -34,7 +34,7 @@ GC safety We call a proc ``p`` `GC safe`:idx: when it doesn't access any global variable that contains GC'ed memory (``string``, ``seq``, ``ref`` or a closure) either -directly or indirectly through a call to a GC unsafe proc. +directly or indirectly through a call to a GC unsafe proc. The `gcsafe`:idx: annotation can be used to mark a proc to be gcsafe, otherwise this property is inferred by the compiler. Note that ``noSideEfect`` @@ -45,10 +45,9 @@ contain a ``ref`` or ``closure`` type. This enforces the *no heap sharing restriction*. Routines that are imported from C are always assumed to be ``gcsafe``. -To enable the GC-safety checking the ``--threadAnalysis:on`` command line -switch must 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 -will always be performed. +To disable the GC-safety checking the ``--threadAnalysis:off`` command line +switch can be used. This is a temporary workaround to ease the porting effort +from old code to the new threading model. Future directions: @@ -59,13 +58,13 @@ Future directions: Threadvar pragma ---------------- -A global variable can be marked with the ``threadvar`` pragma; it is +A global variable can be marked with the ``threadvar`` pragma; it is a `thread-local`:idx: variable then: .. code-block:: nim var checkpoints* {.threadvar.}: seq[string] -Due to implementation restrictions thread local variables cannot be +Due to implementation restrictions thread local variables cannot be initialized within the ``var`` section. (Every thread local variable needs to be replicated at thread creation.) @@ -73,7 +72,7 @@ be replicated at thread creation.) Threads and exceptions ---------------------- -The interaction between threads and exceptions is simple: A *handled* exception +The interaction between threads and exceptions is simple: A *handled* exception in one thread cannot affect any other thread. However, an *unhandled* exception in one thread terminates the whole *process*! @@ -82,7 +81,7 @@ in one thread terminates the whole *process*! Parallel & Spawn ================ -Nim has two flavors of parallelism: +Nim has two flavors of parallelism: 1) `Structured`:idx: parallelism via the ``parallel`` statement. 2) `Unstructured`:idx: parallelism via the standalone ``spawn`` statement. @@ -115,7 +114,7 @@ Spawn statement proc processLine(line: string) = discard "do some heavy lifting here" - + for x in lines("myinput.txt"): spawn processLine(x) sync() @@ -144,7 +143,7 @@ wait on multiple flow variables at the same time: .. code-block:: nim import threadpool, ... - + # wait until 2 out of 3 servers received the update: proc main = var responses = newSeq[RawFlowVar](3) @@ -203,8 +202,7 @@ restrictions / changes: the ``parallel`` section. This is called the *immutability check*. Currently it is not specified what exactly "complex location" means. We need to make this an optimization! -* 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*. * Slices are optimized so that no copy is performed. This optimization is not - yet performed for ordinary slices outside of a ``parallel`` section. Slices - are also special in that they currently do not support negative indexes! + yet performed for ordinary slices outside of a ``parallel`` section. diff --git a/doc/manual/type_bound_ops.txt b/doc/manual/type_bound_ops.txt index 59d55a6cd..c707979fe 100644 --- a/doc/manual/type_bound_ops.txt +++ b/doc/manual/type_bound_ops.txt @@ -21,27 +21,49 @@ helper distinct or object type has to be used for one pointer type. operator `=` ------------ -This operator is the assignment operator. Note that in the contexts -like ``let v = expr``, ``var v = expr``, ``parameter = defaultValue`` or for -parameter passing no assignment is performed. The ``override`` pragma is -optional for overriding ``=``. +This operator is the assignment operator. Note that in the contexts +``result = expr``, ``parameter = defaultValue`` or for +parameter passing no assignment is performed. For a type ``T`` that has an +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 ----------- 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`` -and it need to be annotated with the ``override`` pragma. +generic type is allowed too). The name of the destructor has to be ``=destroy``. -``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. -If a structured type features a field with destructable type and +If a structured type features a field with destructable type and the user has not provided an explicit implementation, a destructor for the -structured type will be automatically generated. Calls to any base class +structured type will be automatically generated. Calls to any base class destructors in both user-defined and generated destructors will be inserted. A destructor is attached to the type it destructs; expressions of this type @@ -52,13 +74,13 @@ can then only be used in *destructible contexts* and as parameters: MyObj = object x, y: int p: pointer - - proc destroy(o: var MyObj) {.override.} = + + proc `=destroy`(o: var MyObj) = if o.p != nil: dealloc o.p - + proc open: MyObj = result = MyObj(x: 1, y: 2, p: alloc(3)) - + proc work(o: MyObj) = echo o.x # No destructor invoked here for 'o' as 'o' is a parameter. @@ -68,7 +90,7 @@ can then only be used in *destructible contexts* and as parameters: var x = open() # valid: pass 'x' to some other proc: work(x) - + # Error: usage of a type with a destructor in a non destructible context echo open() @@ -85,7 +107,7 @@ be destructed at its scope exit. Later versions of the language will improve the support of destructors. 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. **Note**: Destructors are still experimental and the spec might change @@ -95,7 +117,7 @@ significantly in order to incorporate an escape analysis. 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 behaviour for a specific ``ref`` or ``ptr`` type ``T``. (Later versions of the language may weaken this restriction.) @@ -103,10 +125,10 @@ language may weaken this restriction.) The signature has to be: .. 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 -channels to implement thread safe automatic memory management. +This mechanism will be used by most data structures that support shared memory +like channels to implement thread safe automatic memory management. The builtin ``deepCopy`` can even clone closures and their environments. See the documentation of `spawn`_ for details. diff --git a/doc/manual/types.txt b/doc/manual/types.txt index e9d33045b..bdf51941d 100644 --- a/doc/manual/types.txt +++ b/doc/manual/types.txt @@ -864,7 +864,9 @@ Future directions: * Builtin regions like ``private``, ``global`` and ``local`` will prove very useful for the upcoming OpenCL target. * 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 diff --git a/doc/nimc.txt b/doc/nimc.txt index 831fce567..fb1873539 100644 --- a/doc/nimc.txt +++ b/doc/nimc.txt @@ -386,6 +386,25 @@ Example: As can be seen from the example, to Nim symbols can be referred via backticks. 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 ---------------- @@ -506,7 +525,7 @@ For example: .. code-block:: nim 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]() @@ -596,6 +615,25 @@ Produces: 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 ----------------- Similar to the `importc pragma for C <manual.html#importc-pragma>`_, the diff --git a/doc/nimsuggest.txt b/doc/nimsuggest.txt new file mode 100644 index 000000000..2b52196b9 --- /dev/null +++ b/doc/nimsuggest.txt @@ -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``). diff --git a/doc/tools.txt b/doc/tools.txt index 7f2830879..b0a45c575 100644 --- a/doc/tools.txt +++ b/doc/tools.txt @@ -4,6 +4,11 @@ Tools available with Nim 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>`_ | How to generate a nice installer for your Nim program. diff --git a/doc/tut1.txt b/doc/tut1.txt index cb5a0c8dd..58ace1dbe 100644 --- a/doc/tut1.txt +++ b/doc/tut1.txt @@ -16,7 +16,7 @@ Introduction </p></blockquote> -This document is a tutorial for the programming language *Nim*. +This document is a tutorial for the programming language *Nim*. This tutorial assumes that you are familiar with basic programming concepts like variables, types or statements but is kept very basic. The `manual <manual.html>`_ contains many more examples of the advanced language features. @@ -50,7 +50,7 @@ Commonly used commands and switches have abbreviations, so you can also use:: nim c -r greetings.nim To compile a release version use:: - + nim c -d:release greetings.nim By default the Nim compiler generates a large amount of runtime checks @@ -116,7 +116,7 @@ hash character ``#``. Documentation comments start with ``##``: .. code-block:: nim # A comment. - + var myVariable: int ## a documentation comment @@ -200,7 +200,7 @@ constant declaration at compile time: .. code-block:: nim const x = "abc" # the constant x contains the string "abc" - + Indentation can be used after the ``const`` keyword to list a whole section of constants: @@ -214,7 +214,7 @@ constants: The let statement ================= -The ``let`` statement works like the ``var`` statement but the declared +The ``let`` statement works like the ``var`` statement but the declared symbols are *single assignment* variables: After the initialization their value cannot change: @@ -228,7 +228,7 @@ and put it into a data section": .. code-block:: const input = readLine(stdin) # Error: constant expression expected - + .. code-block:: let input = readLine(stdin) # works @@ -310,8 +310,8 @@ the compiler that for every other value nothing should be done: else: discard The empty `discard statement`_ is a *do nothing* statement. The compiler knows -that a case statement with an else part cannot fail and thus the error -disappears. Note that it is impossible to cover all possible string values: +that a case statement with an else part cannot fail and thus the error +disappears. Note that it is impossible to cover all possible string values: that is why string cases always need an ``else`` branch. In general the case statement is used for subrange types or enumerations where @@ -406,7 +406,7 @@ The block's *label* (``myblock`` in the example) is optional. Break statement --------------- A block can be left prematurely with a ``break`` statement. The break statement -can leave a ``while``, ``for``, or a ``block`` statement. It leaves the +can leave a ``while``, ``for``, or a ``block`` statement. It leaves the innermost construct, unless a label of a block is given: .. code-block:: nim @@ -461,7 +461,7 @@ differences: * The statements within a branch do not open a new scope. * The compiler checks the semantics and produces code *only* for the statements that belong to the first condition that evaluates to ``true``. - + The ``when`` statement is useful for writing platform specific code, similar to the ``#ifdef`` construct in the C programming language. @@ -486,14 +486,14 @@ to be indented, but single simple statements do not: .. code-block:: nim # no indentation needed for single assignment statement: if x: x = false - + # indentation needed for nested if statement: if x: if y: y = false else: y = true - + # indentation needed, because two statements follow the condition: if x: x = false @@ -514,7 +514,7 @@ contain indentation at certain places for better readability: As a rule of thumb, indentation within expressions is allowed after operators, an open parenthesis and after commas. -With parenthesis and semicolons ``(;)`` you can use statements where only +With parenthesis and semicolons ``(;)`` you can use statements where only an expression is allowed: .. code-block:: nim @@ -560,45 +560,45 @@ Some terminology: in the example ``question`` is called a (formal) *parameter*, Result variable --------------- -A procedure that returns a value has an implicit ``result`` variable declared +A procedure that returns a value has an implicit ``result`` variable declared that represents the return value. A ``return`` statement with no expression is a -shorthand for ``return result``. The ``result`` value is always returned +shorthand for ``return result``. The ``result`` value is always returned automatically at the end a procedure if there is no ``return`` statement at the exit. .. code-block:: nim - proc sumTillNegative(x: varargs[int]): int = + proc sumTillNegative(x: varargs[int]): int = for i in x: if i < 0: return - result = result + i - + result = result + i + echo sumTillNegative() # echos 0 echo sumTillNegative(3, 4, 5) # echos 12 echo sumTillNegative(3, 4 , -1 , 6) # echos 7 -The ``result`` variable is already implicitly declared at the start of the +The ``result`` variable is already implicitly declared at the start of the function, so declaring it again with 'var result', for example, would shadow it with a normal variable of the same name. The result variable is also already initialised with the type's default value. Note that referential data types will be ``nil`` at the start of the procedure, and thus may require manual initialisation. - + Parameters ---------- Parameters are constant in the procedure body. By default, their value cannot be -changed because this allows the compiler to implement parameter passing in the +changed because this allows the compiler to implement parameter passing in the most efficient way. If a mutable variable is needed inside the procedure, it has to be declared with ``var`` in the procedure body. Shadowing the parameter name -is possible, and actually an idiom: +is possible, and actually an idiom: .. code-block:: nim proc printSeq(s: seq, nprinted: int = -1) = var nprinted = if nprinted == -1: s.len else: min(nprinted, s.len) for i in 0 .. <nprinted: echo s[i] - + If the procedure needs to modify the argument for the caller, a ``var`` parameter can be used: @@ -630,12 +630,12 @@ allow to silently throw away a return value: The return value can be ignored implicitly if the called proc/iterator has -been declared with the ``discardable`` pragma: +been declared with the ``discardable`` pragma: .. code-block:: nim - proc p(x, y: int): int {.discardable.} = + proc p(x, y: int): int {.discardable.} = return x + y - + p(3, 4) # now valid The ``discard`` statement can also be used to create block comments as @@ -899,7 +899,7 @@ object on the heap, so there is a trade-off to be made here. Integers -------- -Nim has these integer types built-in: +Nim has these integer types built-in: ``int int8 int16 int32 int64 uint uint8 uint16 uint32 uint64``. The default integer type is ``int``. Integer literals can have a *type suffix* @@ -1114,7 +1114,7 @@ Arrays An array is a simple fixed length container. Each element in the array has the same type. The array's index type can be any ordinal type. -Arrays can be constructed via ``[]``: +Arrays can be constructed via ``[]``: .. code-block:: nim @@ -1370,12 +1370,12 @@ integer. var building: tuple[street: string, number: int] building = ("Rue del Percebe", 13) echo(building.street) - + # The following line does not compile, they are different tuples! #person = building # --> Error: type mismatch: got (tuple[street: string, number: int]) # but expected 'Person' - + # The following works because the field names and types are the same. var teacher: tuple[name: string, age: int] = ("Mark", 42) person = teacher @@ -1450,13 +1450,13 @@ operators perform implicit dereferencing operations for reference types: type Node = ref NodeObj - NodeObj = object - le, ri: PNode + NodeObj = object + le, ri: Node data: int var n: Node new(n) - n.data = 9 + n.data = 9 # no need to write n[].data; in fact n[].data is highly discouraged! To allocate a new traced object, the built-in procedure ``new`` has to be used. @@ -1559,9 +1559,9 @@ This is best illustrated by an example: A symbol of a module *can* be *qualified* with the ``module.symbol`` syntax. If -the symbol is ambiguous, it even *has* to be qualified. A symbol is ambiguous -if it is defined in two (or more) different modules and both modules are -imported by a third one: +the symbol is ambiguous, it even *has* to be qualified. A symbol is ambiguous +if it is defined in two (or more) different modules and both modules are +imported by a third one: .. code-block:: nim # Module A diff --git a/koch.nim b/koch.nim index 508d7e007..55019b544 100644 --- a/koch.nim +++ b/koch.nim @@ -81,7 +81,7 @@ proc exec(cmd: string, errorcode: int = QuitFailure) = echo(cmd) if execShellCmd(cmd) != 0: quit("FAILURE", errorcode) -proc tryExec(cmd: string): bool = +proc tryExec(cmd: string): bool = echo(cmd) result = execShellCmd(cmd) == 0 @@ -96,7 +96,7 @@ proc copyExe(source, dest: string) = const compileNimInst = "-d:useLibzipSrc tools/niminst/niminst" -proc csource(args: string) = +proc csource(args: string) = exec("$4 cc $1 -r $3 --var:version=$2 --var:mingw=none csource --main:compiler/nim.nim compiler/installer.ini $1" % [args, VersionAsString, compileNimInst, findNim()]) @@ -106,6 +106,12 @@ proc zip(args: string) = exec("$# --var:version=$# --var:mingw=none --main:compiler/nim.nim zip compiler/installer.ini" % ["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) = exec("$# cc $# $#" % [findNim(), args, toolname]) copyFile(dest="bin"/ splitFile(toolname).name.exe, source=toolname.exe) @@ -113,14 +119,14 @@ proc buildTool(toolname, args: string) = proc nsis(args: string) = # make sure we have generated the niminst executables: buildTool("tools/niminst/niminst", args) - buildTool("tools/nimgrep", args) - # produce 'nimrod_debug.exe': - exec "nim c compiler" / "nim.nim" - copyExe("compiler/nim".exe, "bin/nim_debug".exe) + #buildTool("tools/nimgrep", args) + # produce 'nim_debug.exe': + #exec "nim c compiler" / "nim.nim" + #copyExe("compiler/nim".exe, "bin/nim_debug".exe) 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" % [findNim(), compileNimInst, VersionAsString]) exec("sh ./install.sh $#" % args) @@ -139,12 +145,10 @@ proc pdf(args="") = # -------------- boot --------------------------------------------------------- -proc findStartNim: string = +proc findStartNim: string = # we try several things before giving up: # * bin/nim # * $PATH/nim - # * bin/nimrod - # * $PATH/nimrod # If these fail, we try to build nim with the "build.(sh|bat)" script. var nim = "nim".exe result = "bin" / nim @@ -152,34 +156,27 @@ proc findStartNim: string = for dir in split(getEnv("PATH"), PathSep): 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): const buildScript = "build.sh" - if existsFile(buildScript): + if existsFile(buildScript): if tryExec("./" & buildScript): return "bin" / nim else: const buildScript = "build.bat" - if existsFile(buildScript): + if existsFile(buildScript): if tryExec(buildScript): return "bin" / nim echo("Found no nim compiler and every attempt to build one failed!") quit("FAILURE") -proc thVersion(i: int): string = +proc thVersion(i: int): string = result = ("compiler" / "nim" & $i).exe - + proc boot(args: string) = var output = "compiler" / "nim".exe var finalDest = "bin" / "nim".exe # default to use the 'c' command: let bootOptions = if args.len == 0 or args.startsWith("-"): "c" else: "" - + copyExe(findStartNim(), 0.thVersion) for i in 0..2: echo "iteration: ", i+1 @@ -204,7 +201,7 @@ const ".bzrignore", "nim", "nim.exe", "koch", "koch.exe", ".gitignore" ] -proc cleanAux(dir: string) = +proc cleanAux(dir: string) = for kind, path in walkDir(dir): case kind of pcFile: @@ -215,25 +212,25 @@ proc cleanAux(dir: string) = removeFile(path) of pcDir: case splitPath(path).tail - of "nimcache": + of "nimcache": echo "removing dir: ", path removeDir(path) of "dist", ".git", "icons": discard else: cleanAux(path) else: discard -proc removePattern(pattern: string) = - for f in walkFiles(pattern): +proc removePattern(pattern: string) = + for f in walkFiles(pattern): echo "removing: ", f removeFile(f) -proc clean(args: string) = +proc clean(args: string) = if existsFile("koch.dat"): removeFile("koch.dat") removePattern("web/*.html") removePattern("doc/*.html") cleanAux(getCurrentDir()) for kind, path in walkDir(getCurrentDir() / "build"): - if kind == pcDir: + if kind == pcDir: echo "removing dir: ", path removeDir(path) @@ -276,7 +273,7 @@ when defined(withUpdate): "Local branch must be ahead of it. Exiting...") else: quit("An error has occurred.") - + else: echo("No repo or executable found!") when defined(haveZipLib): @@ -293,7 +290,7 @@ when defined(withUpdate): quit("Error reading archive.") else: quit("No failback available. Exiting...") - + echo("Starting update...") boot(args) echo("Update complete!") @@ -317,12 +314,14 @@ proc winRelease() = #buildTool("tools/niminst/niminst", " -d:release") buildTool("tools/nimgrep", " -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 # -------------- tests -------------------------------------------------------- @@ -346,8 +345,8 @@ proc temp(args: string) = copyExe(output, finalDest) if args.len > 0: exec(finalDest & " " & args) -proc showHelp() = - quit(HelpText % [VersionAsString & spaces(44-len(VersionAsString)), +proc showHelp() = + quit(HelpText % [VersionAsString & spaces(44-len(VersionAsString)), CompileDate, CompileTime], QuitSuccess) var op = initOptParser() @@ -366,6 +365,7 @@ of cmdArgument: of "pdf": pdf() of "csource", "csources": csource(op.cmdLineRest) of "zip": zip(op.cmdLineRest) + of "targz": targz(op.cmdLineRest) of "nsis": nsis(op.cmdLineRest) of "install": install(op.cmdLineRest) of "test", "tests": tests(op.cmdLineRest) diff --git a/lib/core/macros.nim b/lib/core/macros.nim index 5583748e0..35f0f61c1 100644 --- a/lib/core/macros.nim +++ b/lib/core/macros.nim @@ -88,7 +88,9 @@ type ntyBigNum, ntyConst, ntyMutable, ntyVarargs, ntyIter, - ntyError + ntyError, + ntyBuiltinTypeClass, ntyConcept, ntyConceptInst, ntyComposite, + ntyAnd, ntyOr, ntyNot TNimTypeKinds* {.deprecated.} = set[NimTypeKind] NimSymKind* = enum @@ -162,6 +164,7 @@ proc kind*(n: NimNode): NimNodeKind {.magic: "NKind", noSideEffect.} ## returns the `kind` of the node `n`. 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 symbol*(n: NimNode): NimSym {.magic: "NSymbol", 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. 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, args: varargs[NimNode]): NimNode {.compileTime.} = ## 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.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.} = ## produces a new float literal node. result = newNimNode(nnkFloatLit) diff --git a/lib/impure/db_sqlite.nim b/lib/impure/db_sqlite.nim index 4be692f39..8536ab6f2 100644 --- a/lib/impure/db_sqlite.nim +++ b/lib/impure/db_sqlite.nim @@ -205,7 +205,7 @@ proc setEncoding*(connection: TDbConn, encoding: string): bool {. exec(connection, sql"PRAGMA encoding = ?", [encoding]) result = connection.getValue(sql"PRAGMA encoding") == encoding -when isMainModule: +when not defined(testing) and isMainModule: var db = open("db.sql", "", "", "") exec(db, sql"create table tbl1(one varchar(10), two smallint)", []) exec(db, sql"insert into tbl1 values('hello!',10)", []) diff --git a/lib/impure/graphics.nim b/lib/impure/graphics.nim index dfadb46ee..814c0ebe1 100644 --- a/lib/impure/graphics.nim +++ b/lib/impure/graphics.nim @@ -499,7 +499,7 @@ template withEvents*(surf: PSurface, event: expr, actions: stmt): stmt {. if sdl.init(sdl.INIT_VIDEO) < 0: raiseEGraphics() if sdl_ttf.init() < 0: raiseEGraphics() -when isMainModule: +when not defined(testing) and isMainModule: var surf = newScreenSurface(800, 600) surf.fillSurface(colWhite) diff --git a/lib/impure/rdstdin.nim b/lib/impure/rdstdin.nim index 55f8c5d32..f4d00979c 100644 --- a/lib/impure/rdstdin.nim +++ b/lib/impure/rdstdin.nim @@ -135,7 +135,7 @@ else: var cur, old: Termios discard fd.tcgetattr(cur.addr) 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) stdout.write prompt result = stdin.readLine(password) diff --git a/lib/impure/re.nim b/lib/impure/re.nim index c24734f89..279f8aadd 100644 --- a/lib/impure/re.nim +++ b/lib/impure/re.nim @@ -7,8 +7,11 @@ # distribution, for details about the copyright. # -## Regular expression support for Nim. Consider using the pegs module -## instead. +## Regular expression support for Nim. Consider using the pegs module 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 ## syntax** which lets you use whitespace freely to make your regexes readable. @@ -36,31 +39,31 @@ const type RegexFlag* = enum ## options for regular expressions reIgnoreCase = 0, ## do caseless matching - reMultiLine = 1, ## ``^`` and ``$`` match newlines within data + reMultiLine = 1, ## ``^`` and ``$`` match newlines within data reDotAll = 2, ## ``.`` matches anything including NL reExtended = 3, ## ignore whitespace and ``#`` comments reStudy = 4 ## study the expression (may be omitted if the ## expression will be used only once) - + RegexDesc = object - h: PPcre - e: ptr TExtra - + h: ptr Pcre + e: ptr ExtraData + Regex* = ref RegexDesc ## a compiled regular expression - + RegexError* = object of ValueError ## is raised if the pattern is no valid regular expression. {.deprecated: [TRegexFlag: RegexFlag, TRegexDesc: RegexDesc, TRegex: Regex, EInvalidRegEx: RegexError].} -proc raiseInvalidRegex(msg: string) {.noinline, noreturn.} = +proc raiseInvalidRegex(msg: string) {.noinline, noreturn.} = var e: ref RegexError new(e) e.msg = msg raise e -proc rawCompile(pattern: string, flags: cint): PPcre = +proc rawCompile(pattern: string, flags: cint): ptr Pcre = var msg: cstring offset: cint @@ -68,10 +71,10 @@ proc rawCompile(pattern: string, flags: cint): PPcre = if result == nil: raiseInvalidRegex($msg & "\n" & pattern & "\n" & spaces(offset) & "^\n") -proc finalizeRegEx(x: Regex) = +proc finalizeRegEx(x: Regex) = # XXX This is a hack, but PCRE does not export its "free" function properly. # Sigh. The hack relies on PCRE's implementation (see ``pcre_get.c``). - # Fortunately the implementation is unlikely to change. + # Fortunately the implementation is unlikely to change. pcre.free_substring(cast[cstring](x.h)) if not isNil(x.e): pcre.free_substring(cast[cstring](x.e)) @@ -84,7 +87,7 @@ proc re*(s: string, flags = {reExtended, reStudy}): Regex = result.h = rawCompile(s, cast[cint](flags - {reStudy})) if reStudy in flags: 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) proc matchOrFind(s: string, pattern: Regex, matches: var openArray[string], @@ -101,10 +104,10 @@ proc matchOrFind(s: string, pattern: Regex, matches: var openArray[string], if a >= 0'i32: matches[i-1] = substr(s, int(a), int(b)-1) else: matches[i-1] = nil return rawMatches[1] - rawMatches[0] - + proc findBounds*(s: string, pattern: Regex, matches: var openArray[string], start = 0): tuple[first, last: int] = - ## returns the starting position and end position of `pattern` in `s` + ## returns the starting position and end position of `pattern` in `s` ## and the captured ## substrings in the array `matches`. If it does not match, nothing ## is written into `matches` and ``(-1,0)`` is returned. @@ -120,12 +123,12 @@ proc findBounds*(s: string, pattern: Regex, matches: var openArray[string], if a >= 0'i32: matches[i-1] = substr(s, int(a), int(b)-1) else: matches[i-1] = nil return (rawMatches[0].int, rawMatches[1].int - 1) - -proc findBounds*(s: string, pattern: Regex, + +proc findBounds*(s: string, pattern: Regex, matches: var openArray[tuple[first, last: int]], start = 0): tuple[first, last: int] = - ## returns the starting position and end position of ``pattern`` in ``s`` - ## and the captured substrings in the array `matches`. + ## returns the starting position and end position of ``pattern`` in ``s`` + ## and the captured substrings in the array `matches`. ## If it does not match, nothing is written into `matches` and ## ``(-1,0)`` is returned. var @@ -141,10 +144,10 @@ proc findBounds*(s: string, pattern: Regex, else: matches[i-1] = (-1,0) return (rawMatches[0].int, rawMatches[1].int - 1) -proc findBounds*(s: string, pattern: Regex, +proc findBounds*(s: string, pattern: Regex, start = 0): tuple[first, last: int] = - ## returns the starting position of `pattern` in `s`. If it does not - ## match, ``(-1,0)`` is returned. + ## returns the starting position and end position of ``pattern`` in ``s``. + ## If it does not match, ``(-1,0)`` is returned. var rtarray = initRtArray[cint](3) rawMatches = rtarray.getRawData @@ -152,7 +155,7 @@ proc findBounds*(s: string, pattern: Regex, cast[ptr cint](rawMatches), 3) if res < 0'i32: return (int(res), 0) return (int(rawMatches[0]), int(rawMatches[1]-1)) - + proc matchOrFind(s: string, pattern: Regex, start, flags: cint): cint = var rtarray = initRtArray[cint](3) @@ -172,7 +175,7 @@ proc matchLen*(s: string, pattern: Regex, matches: var openArray[string], proc matchLen*(s: string, pattern: Regex, start = 0): int = ## the same as ``match``, but it returns the length of the match, ## if there is no match, -1 is returned. Note that a match length - ## of zero can happen. + ## of zero can happen. return matchOrFind(s, pattern, start.cint, pcre.ANCHORED) proc match*(s: string, pattern: Regex, start = 0): bool = @@ -216,7 +219,7 @@ proc find*(s: string, pattern: Regex, start = 0): int = if res < 0'i32: return res return rawMatches[0] -iterator findAll*(s: string, pattern: Regex, start = 0): string = +iterator findAll*(s: string, pattern: Regex, start = 0): string = ## Yields all matching *substrings* of `s` that match `pattern`. ## ## Note that since this is an iterator you should not modify the string you @@ -231,10 +234,11 @@ iterator findAll*(s: string, pattern: Regex, start = 0): string = if res < 0'i32: break let a = rawMatches[0] let b = rawMatches[1] + if a == b and a == i: break yield substr(s, int(a), int(b)-1) i = b -proc findAll*(s: string, pattern: Regex, start = 0): seq[string] = +proc findAll*(s: string, pattern: Regex, start = 0): seq[string] = ## returns all matching *substrings* of `s` that match `pattern`. ## If it does not match, @[] is returned. accumulateResult(findAll(s, pattern, start)) @@ -242,13 +246,13 @@ proc findAll*(s: string, pattern: Regex, start = 0): seq[string] = when not defined(nimhygiene): {.pragma: inject.} -template `=~` *(s: string, pattern: Regex): expr = - ## This calls ``match`` with an implicit declared ``matches`` array that - ## can be used in the scope of the ``=~`` call: - ## +template `=~` *(s: string, pattern: Regex): expr = + ## This calls ``match`` with an implicit declared ``matches`` array that + ## can be used in the scope of the ``=~`` call: + ## ## .. code-block:: nim ## - ## if line =~ re"\s*(\w+)\s*\=\s*(\w+)": + ## if line =~ re"\s*(\w+)\s*\=\s*(\w+)": ## # matches a key=value pair: ## echo("Key: ", matches[0]) ## echo("Value: ", matches[1]) @@ -260,9 +264,9 @@ template `=~` *(s: string, pattern: Regex): expr = ## else: ## echo("syntax error") ## - bind MaxSubPatterns + bind MaxSubpatterns when not declaredInScope(matches): - var matches {.inject.}: array[0..MaxSubpatterns-1, string] + var matches {.inject.}: array[MaxSubpatterns, string] match(s, pattern, matches) # ------------------------- more string handling ------------------------------ @@ -286,7 +290,7 @@ proc endsWith*(s: string, suffix: Regex): bool = if matchLen(s, suffix, i) == s.len - i: return true proc replace*(s: string, sub: Regex, by = ""): string = - ## Replaces `sub` in `s` by the string `by`. Captures cannot be + ## Replaces `sub` in `s` by the string `by`. Captures cannot be ## accessed in `by`. Examples: ## ## .. code-block:: nim @@ -306,7 +310,7 @@ proc replace*(s: string, sub: Regex, by = ""): string = add(result, by) prev = match.last + 1 add(result, substr(s, prev)) - + proc replacef*(s: string, sub: Regex, by: string): string = ## Replaces `sub` in `s` by the string `by`. Captures can be accessed in `by` ## with the notation ``$i`` and ``$#`` (see strutils.`%`). Examples: @@ -320,7 +324,7 @@ proc replacef*(s: string, sub: Regex, by: string): string = ## ## "var1<-keykey; val2<-key2key2" result = "" - var caps: array[0..MaxSubpatterns-1, string] + var caps: array[MaxSubpatterns, string] var prev = 0 while true: var match = findBounds(s, sub, caps, prev) @@ -338,7 +342,7 @@ proc parallelReplace*(s: string, subs: openArray[ ## applied in parallel. result = "" var i = 0 - var caps: array[0..MaxSubpatterns-1, string] + var caps: array[MaxSubpatterns, string] while i < s.len: block searchSubs: for j in 0..high(subs): @@ -359,7 +363,7 @@ proc transformFile*(infile, outfile: string, ## error occurs. This is supposed to be used for quick scripting. var x = readFile(infile).string writeFile(outfile, x.parallelReplace(subs)) - + iterator split*(s: string, sep: Regex): string = ## Splits the string `s` into substrings. ## @@ -373,64 +377,73 @@ iterator split*(s: string, sep: Regex): string = ## Results in: ## ## .. code-block:: nim + ## "" ## "this" ## "is" ## "an" ## "example" + ## "" ## var - first = 0 - last = 0 + first = -1 + last = -1 while last < len(s): var x = matchLen(s, sep, last) if x > 0: inc(last, x) first = last + if x == 0: inc(last) while last < len(s): - inc(last) x = matchLen(s, sep, last) - if x > 0: break - if first < last: + if x >= 0: break + inc(last) + if first <= last: yield substr(s, first, last-1) proc split*(s: string, sep: Regex): seq[string] = ## Splits the string `s` into substrings. accumulateResult(split(s, sep)) - -proc escapeRe*(s: string): string = - ## escapes `s` so that it is matched verbatim when used as a regular + +proc escapeRe*(s: string): string = + ## escapes `s` so that it is matched verbatim when used as a regular ## expression. result = "" for c in items(s): case c of 'a'..'z', 'A'..'Z', '0'..'9', '_': result.add(c) - else: + else: result.add("\\x") result.add(toHex(ord(c), 2)) - + const ## common regular expressions - reIdentifier* = r"\b[a-zA-Z_]+[a-zA-Z_0-9]*\b" ## describes an identifier - reNatural* = r"\b\d+\b" ## describes a natural number - reInteger* = r"\b[-+]?\d+\b" ## describes an integer - reHex* = r"\b0[xX][0-9a-fA-F]+\b" ## describes a hexadecimal number - reBinary* = r"\b0[bB][01]+\b" ## describes a binary number (example: 0b11101) - reOctal* = r"\b0[oO][0-7]+\b" ## describes an octal number (example: 0o777) - reFloat* = r"\b[-+]?[0-9]*\.?[0-9]+([eE][-+]?[0-9]+)?\b" + reIdentifier* {.deprecated.} = r"\b[a-zA-Z_]+[a-zA-Z_0-9]*\b" + ## describes an identifier + reNatural* {.deprecated.} = r"\b\d+\b" + ## describes a natural number + reInteger* {.deprecated.} = r"\b[-+]?\d+\b" + ## describes an integer + 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 - reEmail* = r"\b[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-Z]{2}|com|org|" & - r"net|gov|mil|biz|info|mobi|name|aero|jobs|museum)\b" + reEmail* {.deprecated.} = r"\b[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-Z]{2}|com|org|net|gov|mil|biz|" & + r"info|mobi|name|aero|jobs|museum)\b" ## describes a common email address - reURL* = r"\b(http(s)?|ftp|gopher|telnet|file|notes|ms\-help):" & - r"((//)|(\\\\))+[\w\d:#@%/;$()~_?\+\-\=\\\.\&]*\b" + reURL* {.deprecated.} = r"\b(http(s)?|ftp|gopher|telnet|file|notes|ms-help)" & + r":((//)|(\\\\))+[\w\d:#@%/;$()~_?\+\-\=\\\.\&]*\b" ## describes an URL when isMainModule: assert match("(a b c)", re"\( .* \)") assert match("WHiLe", re("while", {reIgnoreCase})) - + assert "0158787".match(re"\d+") assert "ABC 0232".match(re"\w+\s+\d+") assert "ABC".match(re"\d+ | \w+") @@ -439,21 +452,21 @@ when isMainModule: var pattern = re"[a-z0-9]+\s*=\s*[a-z0-9]+" assert matchLen("key1= cal9", pattern) == 11 - + assert find("_____abc_______", re"abc") == 5 - - var matches: array[0..5, string] - if match("abcdefg", re"c(d)ef(g)", matches, 2): + + var matches: array[6, string] + if match("abcdefg", re"c(d)ef(g)", matches, 2): assert matches[0] == "d" assert matches[1] == "g" else: assert false - + if "abc" =~ re"(a)bcxyz|(\w+)": assert matches[1] == "abc" else: assert false - + if "abc" =~ re"(cba)?.*": assert matches[0] == nil else: assert false @@ -461,7 +474,7 @@ when isMainModule: if "abc" =~ re"().*": assert matches[0] == "" else: assert false - + assert "var1=key; var2=key2".endsWith(re"\w+=\w+") assert("var1=key; var2=key2".replacef(re"(\w+)=(\w+)", "$1<-$2$2") == "var1<-keykey; var2<-key2key2") @@ -471,7 +484,12 @@ when isMainModule: var accum: seq[string] = @[] for word in split("00232this02939is39an22example111", re"\d+"): 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): assert x == "d" @@ -484,7 +502,7 @@ when isMainModule: assert("XYZ".match(re"^\d*") == true) 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): for i in 0..matches.high: assert matches[i] == $chr(i + 'a'.ord) diff --git a/lib/impure/ssl.nim b/lib/impure/ssl.nim index bb7cfc0d3..d318a1979 100644 --- a/lib/impure/ssl.nim +++ b/lib/impure/ssl.nim @@ -82,7 +82,7 @@ proc close*(sock: TSecureSocket) = ERR_print_errors_fp(stderr) raiseOSError(osLastError()) -when isMainModule: +when not defined(testing) and isMainModule: var s: TSecureSocket echo connect(s, "smtp.gmail.com", 465) diff --git a/lib/impure/zipfiles.nim b/lib/impure/zipfiles.nim index c22294061..b41ca1e4b 100644 --- a/lib/impure/zipfiles.nim +++ b/lib/impure/zipfiles.nim @@ -9,8 +9,8 @@ ## This module implements a zip archive creator/reader/modifier. -import - streams, libzip, times, os +import + streams, libzip, times, os, strutils type TZipArchive* = object of RootObj ## represents a zip archive @@ -18,14 +18,14 @@ type w: PZip -proc zipError(z: var TZipArchive) = +proc zipError(z: var TZipArchive) = var e: ref IOError new(e) e.msg = $zip_strerror(z.w) raise e - + proc open*(z: var TZipArchive, filename: string, mode: FileMode = fmRead): bool = - ## Opens a zip file for reading, writing or appending. All file modes are + ## Opens a zip file for reading, writing or appending. All file modes are ## supported. Returns true iff successful, false otherwise. var err, flags: int32 case mode @@ -41,21 +41,21 @@ proc open*(z: var TZipArchive, filename: string, mode: FileMode = fmRead): bool proc close*(z: var TZipArchive) = ## Closes a zip file. zip_close(z.w) - -proc createDir*(z: var TZipArchive, dir: string) = + +proc createDir*(z: var TZipArchive, dir: string) = ## Creates a directory within the `z` archive. This does not fail if the - ## directory already exists. Note that for adding a file like + ## directory already exists. Note that for adding a file like ## ``"path1/path2/filename"`` it is not necessary - ## to create the ``"path/path2"`` subdirectories - it will be done - ## automatically by ``addFile``. - assert(z.mode != fmRead) + ## to create the ``"path/path2"`` subdirectories - it will be done + ## automatically by ``addFile``. + assert(z.mode != fmRead) discard zip_add_dir(z.w, dir) zip_error_clear(z.w) -proc addFile*(z: var TZipArchive, dest, src: string) = +proc addFile*(z: var TZipArchive, dest, src: string) = ## Adds the file `src` to the archive `z` with the name `dest`. `dest` - ## may contain a path that will be created. - assert(z.mode != fmRead) + ## may contain a path that will be created. + assert(z.mode != fmRead) if not fileExists(src): raise newException(IOError, "File '" & src & "' does not exist") var zipsrc = zip_source_file(z.w, src, 0, -1) @@ -67,21 +67,21 @@ proc addFile*(z: var TZipArchive, dest, src: string) = zip_source_free(zipsrc) zipError(z) -proc addFile*(z: var TZipArchive, file: string) = +proc addFile*(z: var TZipArchive, file: string) = ## A shortcut for ``addFile(z, file, file)``, i.e. the name of the source is ## the name of the destination. addFile(z, file, file) - -proc mySourceCallback(state, data: pointer, len: int, - cmd: TZipSourceCmd): int {.cdecl.} = + +proc mySourceCallback(state, data: pointer, len: int, + cmd: TZipSourceCmd): int {.cdecl.} = var src = cast[Stream](state) case cmd - of ZIP_SOURCE_OPEN: + of ZIP_SOURCE_OPEN: if src.setPositionImpl != nil: setPosition(src, 0) # reset of ZIP_SOURCE_READ: result = readData(src, data, len) of ZIP_SOURCE_CLOSE: close(src) - of ZIP_SOURCE_STAT: + of ZIP_SOURCE_STAT: var stat = cast[PZipStat](data) zip_stat_init(stat) stat.size = high(int32)-1 # we don't know the size @@ -94,8 +94,8 @@ proc mySourceCallback(state, data: pointer, len: int, result = 2*sizeof(cint) of constZIP_SOURCE_FREE: GC_unref(src) else: assert(false) - -proc addFile*(z: var TZipArchive, dest: string, src: Stream) = + +proc addFile*(z: var TZipArchive, dest: string, src: Stream) = ## Adds a file named with `dest` to the archive `z`. `dest` ## may contain a path. The file's content is read from the `src` stream. assert(z.mode != fmRead) @@ -105,39 +105,45 @@ proc addFile*(z: var TZipArchive, dest: string, src: Stream) = if zip_add(z.w, dest, zipsrc) < 0'i32: zip_source_free(zipsrc) zipError(z) - + # -------------- zip file stream --------------------------------------------- type TZipFileStream = object of StreamObj f: PZipFile + atEnd: bool - PZipFileStream* = - ref TZipFileStream ## a reader stream of a file within a zip archive + PZipFileStream* = + ref TZipFileStream ## a reader stream of a file within a zip archive proc fsClose(s: Stream) = zip_fclose(PZipFileStream(s).f) -proc fsReadData(s: Stream, buffer: pointer, bufLen: int): int = +proc fsAtEnd(s: Stream): bool = PZipFileStream(s).atEnd +proc fsReadData(s: Stream, buffer: pointer, bufLen: int): int = 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) result.f = f + result.atEnd = false result.closeImpl = fsClose result.readDataImpl = fsReadData + result.atEndImpl = fsAtEnd # other methods are nil! # ---------------------------------------------------------------------------- - -proc getStream*(z: var TZipArchive, filename: string): PZipFileStream = + +proc getStream*(z: var TZipArchive, filename: string): PZipFileStream = ## returns a stream that can be used to read the file named `filename` ## from the archive `z`. Returns nil in case of an error. - ## The returned stream does not support the `setPosition`, `getPosition`, + ## The returned stream does not support the `setPosition`, `getPosition`, ## `writeData` or `atEnd` methods. var x = zip_fopen(z.w, filename, 0'i32) if x != nil: result = newZipFileStream(x) - -iterator walkFiles*(z: var TZipArchive): string = - ## walks over all files in the archive `z` and returns the filename + +iterator walkFiles*(z: var TZipArchive): string = + ## walks over all files in the archive `z` and returns the filename ## (including the path). var i = 0'i32 var num = zip_get_num_files(z.w) @@ -158,12 +164,20 @@ proc extractFile*(z: var TZipArchive, srcFile: string, dest: Stream) = proc extractFile*(z: var TZipArchive, srcFile: string, dest: string) = ## 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) file.close() proc extractAll*(z: var TZipArchive, dest: string) = ## extracts all files from archive `z` to the destination directory. 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") diff --git a/lib/js/dom.nim b/lib/js/dom.nim index 870213db3..b063fa838 100644 --- a/lib/js/dom.nim +++ b/lib/js/dom.nim @@ -36,8 +36,11 @@ type onsubmit*: proc (event: ref TEvent) {.nimcall.} onunload*: proc (event: ref TEvent) {.nimcall.} - TWindow* {.importc.} = object of TEventHandlers - document*: ref TDocument + addEventListener*: proc(ev: cstring, cb: proc(ev: ref TEvent), useCapture: bool = false) {.nimcall.} + + Window* = ref WindowObj + WindowObj {.importc.} = object of TEventHandlers + document*: Document event*: ref TEvent history*: ref THistory location*: ref TLocation @@ -55,7 +58,6 @@ type status*: cstring toolbar*: ref TToolBar - addEventListener*: proc(ev: cstring, cb: proc(ev: ref TEvent) ) {.nimcall.} alert*: proc (msg: cstring) {.nimcall.} back*: proc () {.nimcall.} blur*: proc () {.nimcall.} @@ -66,7 +68,7 @@ type confirm*: proc (msg: cstring): bool {.nimcall.} disableExternalCapture*: proc () {.nimcall.} enableExternalCapture*: proc () {.nimcall.} - find*: proc (text: cstring, caseSensitive = false, + find*: proc (text: cstring, caseSensitive = false, backwards = false) {.nimcall.} focus*: proc () {.nimcall.} forward*: proc () {.nimcall.} @@ -75,7 +77,7 @@ type moveBy*: proc (x, y: int) {.nimcall.} moveTo*: proc (x, y: int) {.nimcall.} open*: proc (uri, windowname: cstring, - properties: cstring = nil): ref TWindow {.nimcall.} + properties: cstring = nil): Window {.nimcall.} print*: proc () {.nimcall.} prompt*: proc (text, default: cstring): cstring {.nimcall.} releaseEvents*: proc (eventMask: int) {.nimcall.} @@ -89,16 +91,73 @@ type stop*: proc () {.nimcall.} frames*: seq[TFrame] - TFrame* {.importc.} = object of TWindow + Frame* = ref FrameObj + FrameObj {.importc.} = object of WindowObj + + ClassList* {.importc.} = object of RootObj + add*: proc (class: cstring) {.nimcall.} + remove*: proc (class: cstring) {.nimcall.} + contains*: proc (class: cstring):bool {.nimcall.} + toggle*: proc (class: cstring) {.nimcall.} + + TNodeType* = enum + ElementNode = 1, + AttributeNode, + TextNode, + CDATANode, + EntityRefNode, + EntityNode, + ProcessingInstructionNode, + CommentNode, + DocumentNode, + DocumentTypeNode, + DocumentFragmentNode, + NotationNode + + Node* = ref NodeObj + NodeObj {.importc.} = object of TEventHandlers + attributes*: seq[Node] + childNodes*: seq[Node] + children*: seq[Node] + data*: cstring + firstChild*: Node + lastChild*: Node + nextSibling*: Node + nodeName*: cstring + nodeType*: TNodeType + nodeValue*: cstring + parentNode*: Node + previousSibling*: Node + appendChild*: proc (child: Node) {.nimcall.} + appendData*: proc (data: cstring) {.nimcall.} + cloneNode*: proc (copyContent: bool): Node {.nimcall.} + deleteData*: proc (start, len: int) {.nimcall.} + getAttribute*: proc (attr: cstring): cstring {.nimcall.} + getAttributeNode*: proc (attr: cstring): Node {.nimcall.} + hasChildNodes*: proc (): bool {.nimcall.} + innerHTML*: cstring + insertBefore*: proc (newNode, before: Node) {.nimcall.} + insertData*: proc (position: int, data: cstring) {.nimcall.} + removeAttribute*: proc (attr: cstring) {.nimcall.} + removeAttributeNode*: proc (attr: Node) {.nimcall.} + removeChild*: proc (child: Node) {.nimcall.} + replaceChild*: proc (newNode, oldNode: Node) {.nimcall.} + replaceData*: proc (start, len: int, text: cstring) {.nimcall.} + scrollIntoView*: proc () {.nimcall.} + setAttribute*: proc (name, value: cstring) {.nimcall.} + setAttributeNode*: proc (attr: Node) {.nimcall.} + style*: ref TStyle - TDocument* {.importc.} = object of TEventHandlers - addEventListener*: proc(ev: cstring, cb: proc(ev: ref TEvent) ) {.nimcall.} + 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 @@ -106,13 +165,13 @@ type 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.} + 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.} @@ -120,24 +179,43 @@ type 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] + anchors*: seq[AnchorElement] + forms*: seq[FormElement] + images*: seq[ImageElement] applets*: seq[ref TApplet] - embeds*: seq[ref TEmbed] - links*: seq[ref TLink] + embeds*: seq[EmbedElement] + links*: seq[LinkElement] - TLink* {.importc.} = object of RootObj + 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 - TEmbed* {.importc.} = object of RootObj + EmbedElement* = ref EmbedObj + EmbedObj {.importc.} = object of ElementObj height*: int hspace*: int - name*: cstring src*: cstring width*: int `type`*: cstring @@ -145,115 +223,42 @@ type play*: proc () {.nimcall.} stop*: proc () {.nimcall.} - TAnchor* {.importc.} = object of RootObj - name*: cstring + AnchorElement* = ref AnchorObj + AnchorObj {.importc.} = object of ElementObj 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 + OptionElement* = ref OptionObj + OptionObj {.importc.} = object of ElementObj defaultSelected*: bool selected*: bool selectedIndex*: int text*: cstring value*: cstring - TForm* {.importc.} = object of TEventHandlers + FormElement* = ref FormObj + FormObj {.importc.} = object of ElementObj 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] + elements*: seq[Element] - TImage* {.importc.} = object of TEventHandlers + ImageElement* = ref ImageObj + ImageObj {.importc.} = object of ElementObj 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 - add*: proc (class: cstring) {.nimcall.} - remove*: proc (class: cstring) {.nimcall.} - contains*: proc (class: cstring):bool {.nimcall.} - toggle*: proc (class: cstring) {.nimcall.} - - TNodeType* = enum - ElementNode = 1, - AttributeNode, - TextNode, - CDATANode, - EntityRefNode, - EntityNode, - ProcessingInstructionNode, - CommentNode, - DocumentNode, - DocumentTypeNode, - DocumentFragmentNode, - NotationNode - TNode* {.importc.} = object of RootObj - attributes*: seq[ref TNode] - childNodes*: seq[ref TNode] - children*: seq[ref TNode] - classList*: ref Classlist - data*: cstring - firstChild*: ref TNode - lastChild*: ref TNode - nextSibling*: ref TNode - nodeName*: cstring - nodeType*: TNodeType - nodeValue*: cstring - parentNode*: ref TNode - previousSibling*: ref TNode - appendChild*: proc (child: ref TNode) {.nimcall.} - appendData*: proc (data: cstring) {.nimcall.} - cloneNode*: proc (copyContent: bool): ref TNode {.nimcall.} - deleteData*: proc (start, len: int) {.nimcall.} - getAttribute*: proc (attr: cstring): cstring {.nimcall.} - getAttributeNode*: proc (attr: cstring): ref TNode {.nimcall.} - getElementsByTagName*: proc (name: cstring): seq[ref TNode] {.nimcall.} - getElementsByClassName*: proc (name: cstring): seq[ref TNode] {.nimcall.} - hasChildNodes*: proc (): bool {.nimcall.} - innerHTML*: cstring - insertBefore*: proc (newNode, before: ref TNode) {.nimcall.} - insertData*: proc (position: int, data: cstring) {.nimcall.} - addEventListener*: proc(ev: cstring, cb: proc(ev: ref TEvent)) {.nimcall.} - removeAttribute*: proc (attr: cstring) {.nimcall.} - removeAttributeNode*: proc (attr: ref TNode) {.nimcall.} - removeChild*: proc (child: ref TNode) {.nimcall.} - replaceChild*: proc (newNode, oldNode: ref TNode) {.nimcall.} - replaceData*: proc (start, len: int, text: cstring) {.nimcall.} - scrollIntoView*: proc () {.nimcall.} - setAttribute*: proc (name, value: cstring) {.nimcall.} - setAttributeNode*: proc (attr: ref TNode) {.nimcall.} - style*: ref TStyle TStyle* {.importc.} = object of RootObj background*: cstring @@ -350,7 +355,7 @@ type setAttribute*: proc (attr, value: cstring, caseSensitive=false) {.nimcall.} TEvent* {.importc.} = object of RootObj - target*: ref TNode + target*: Node altKey*, ctrlKey*, shiftKey*: bool button*: int clientX*, clientY*: int @@ -448,8 +453,8 @@ type TInterval* {.importc.} = object of RootObj var - window* {.importc, nodecl.}: ref TWindow - document* {.importc, nodecl.}: ref TDocument + window* {.importc, nodecl.}: Window + document* {.importc, nodecl.}: Document navigator* {.importc, nodecl.}: ref TNavigator screen* {.importc, nodecl.}: ref TScreen @@ -466,3 +471,17 @@ proc isNaN*(x: BiggestFloat): bool {.importc, nodecl.} proc parseFloat*(s: cstring): BiggestFloat {.importc, nodecl.} proc parseInt*(s: cstring): 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 diff --git a/lib/nimbase.h b/lib/nimbase.h index e9dad0bb7..eea618bac 100644 --- a/lib/nimbase.h +++ b/lib/nimbase.h @@ -343,15 +343,15 @@ struct TFrame { }; #define nimfr(proc, file) \ - TFrame F; \ - F.procname = proc; F.filename = file; F.line = 0; F.len = 0; nimFrame(&F); + TFrame FR; \ + FR.procname = proc; FR.filename = file; FR.line = 0; FR.len = 0; nimFrame(&FR); #define nimfrs(proc, file, slots, length) \ - struct {TFrame* prev;NCSTRING procname;NI line;NCSTRING filename; NI len; TVarSlot s[slots];} F; \ - F.procname = proc; F.filename = file; F.line = 0; F.len = length; nimFrame((TFrame*)&F); + struct {TFrame* prev;NCSTRING procname;NI line;NCSTRING filename; NI len; TVarSlot s[slots];} FR; \ + FR.procname = proc; FR.filename = file; FR.line = 0; FR.len = length; nimFrame((TFrame*)&FR); #define nimln(n, file) \ - F.line = n; F.filename = file; + FR.line = n; FR.filename = file; #define NIM_POSIX_INIT __attribute__((constructor)) diff --git a/lib/packages/docutils/rst.nim b/lib/packages/docutils/rst.nim index 97784898e..2ee94ba13 100644 --- a/lib/packages/docutils/rst.nim +++ b/lib/packages/docutils/rst.nim @@ -11,25 +11,25 @@ ## subset is implemented. Some features of the `markdown`:idx: wiki syntax are ## also supported. -import +import os, strutils, rstast type - TRstParseOption* = enum ## options for the RST parser + TRstParseOption* = enum ## options for the RST parser roSkipPounds, ## skip ``#`` at line beginning (documentation ## embedded in Nim comments) roSupportSmilies, ## make the RST parser support smilies like ``:)`` roSupportRawDirective, ## support the ``raw`` directive (don't support ## it for sandboxing) roSupportMarkdown ## support additional features of markdown - + TRstParseOptions* = set[TRstParseOption] - + TMsgClass* = enum - mcHint = "Hint", - mcWarning = "Warning", + mcHint = "Hint", + mcWarning = "Warning", mcError = "Error" - + TMsgKind* = enum ## the possible messages meCannotOpenFile, meExpected, @@ -41,20 +41,20 @@ type mwUnknownSubstitution, mwUnsupportedLanguage, mwUnsupportedField - + TMsgHandler* = proc (filename: string, line, col: int, msgKind: TMsgKind, arg: string) {.nimcall.} ## what to do in case of an error TFindFileHandler* = proc (filename: string): string {.nimcall.} const messages: array [TMsgKind, string] = [ - meCannotOpenFile: "cannot open '$1'", + meCannotOpenFile: "cannot open '$1'", meExpected: "'$1' expected", meGridTableNotImplemented: "grid table is not implemented", - meNewSectionExpected: "new section expected", + meNewSectionExpected: "new section expected", meGeneralParseError: "general parse error", meInvalidDirective: "invalid directive: '$1'", - mwRedefinitionOfLabel: "redefinition of label '$1'", + mwRedefinitionOfLabel: "redefinition of label '$1'", mwUnknownSubstitution: "unknown substitution '$1'", mwUnsupportedLanguage: "language '$1' not supported", mwUnsupportedField: "field '$1' not supported" @@ -67,7 +67,7 @@ proc getArgument*(n: PRstNode): string # ----------------------------- scanner part -------------------------------- -const +const SymChars: set[char] = {'a'..'z', 'A'..'Z', '0'..'9', '\x80'..'\xFF'} SmileyStartChars: set[char] = {':', ';', '8'} Smilies = { @@ -111,14 +111,14 @@ const } type - TTokType = enum + TTokType = enum tkEof, tkIndent, tkWhite, tkWord, tkAdornment, tkPunct, tkOther TToken = object # a RST token kind*: TTokType # the type of the token ival*: int # the indentation or parsed integer value symbol*: string # the parsed symbol as string line*, col*: int # line and column of the token - + TTokenSeq = seq[TToken] TLexer = object of RootObj buf*: cstring @@ -127,61 +127,61 @@ type skipPounds*: bool -proc getThing(L: var TLexer, tok: var TToken, s: set[char]) = +proc getThing(L: var TLexer, tok: var TToken, s: set[char]) = tok.kind = tkWord tok.line = L.line tok.col = L.col var pos = L.bufpos - while true: + while true: add(tok.symbol, L.buf[pos]) inc(pos) - if L.buf[pos] notin s: break + if L.buf[pos] notin s: break inc(L.col, pos - L.bufpos) L.bufpos = pos -proc getAdornment(L: var TLexer, tok: var TToken) = +proc getAdornment(L: var TLexer, tok: var TToken) = tok.kind = tkAdornment tok.line = L.line tok.col = L.col var pos = L.bufpos var c = L.buf[pos] - while true: + while true: add(tok.symbol, L.buf[pos]) inc(pos) - if L.buf[pos] != c: break + if L.buf[pos] != c: break inc(L.col, pos - L.bufpos) L.bufpos = pos -proc getIndentAux(L: var TLexer, start: int): int = +proc getIndentAux(L: var TLexer, start: int): int = var pos = start - var buf = L.buf + var buf = L.buf # skip the newline (but include it in the token!) - if buf[pos] == '\x0D': + if buf[pos] == '\x0D': if buf[pos + 1] == '\x0A': inc(pos, 2) else: inc(pos) - elif buf[pos] == '\x0A': + elif buf[pos] == '\x0A': inc(pos) - if L.skipPounds: + if L.skipPounds: if buf[pos] == '#': inc(pos) if buf[pos] == '#': inc(pos) - while true: + while true: case buf[pos] - of ' ', '\x0B', '\x0C': + of ' ', '\x0B', '\x0C': inc(pos) inc(result) - of '\x09': + of '\x09': inc(pos) result = result - (result mod 8) + 8 - else: + else: break # EndOfFile also leaves the loop - if buf[pos] == '\0': + if buf[pos] == '\0': result = 0 - elif (buf[pos] == '\x0A') or (buf[pos] == '\x0D'): + elif (buf[pos] == '\x0A') or (buf[pos] == '\x0D'): # look at the next line for proper indentation: result = getIndentAux(L, pos) L.bufpos = pos # no need to set back buf - -proc getIndent(L: var TLexer, tok: var TToken) = + +proc getIndent(L: var TLexer, tok: var TToken) = tok.col = 0 tok.kind = tkIndent # skip the newline (but include it in the token!) tok.ival = getIndentAux(L, L.bufpos) @@ -191,85 +191,85 @@ proc getIndent(L: var TLexer, tok: var TToken) = tok.ival = max(tok.ival - L.baseIndent, 0) tok.symbol = "\n" & spaces(tok.ival) -proc rawGetTok(L: var TLexer, tok: var TToken) = +proc rawGetTok(L: var TLexer, tok: var TToken) = tok.symbol = "" tok.ival = 0 var c = L.buf[L.bufpos] case c - of 'a'..'z', 'A'..'Z', '\x80'..'\xFF', '0'..'9': + of 'a'..'z', 'A'..'Z', '\x80'..'\xFF', '0'..'9': getThing(L, tok, SymChars) - of ' ', '\x09', '\x0B', '\x0C': + of ' ', '\x09', '\x0B', '\x0C': getThing(L, tok, {' ', '\x09'}) tok.kind = tkWhite - if L.buf[L.bufpos] in {'\x0D', '\x0A'}: + if L.buf[L.bufpos] in {'\x0D', '\x0A'}: rawGetTok(L, tok) # ignore spaces before \n - of '\x0D', '\x0A': + of '\x0D', '\x0A': getIndent(L, tok) - of '!', '\"', '#', '$', '%', '&', '\'', '(', ')', '*', '+', ',', '-', '.', + of '!', '\"', '#', '$', '%', '&', '\'', '(', ')', '*', '+', ',', '-', '.', '/', ':', ';', '<', '=', '>', '?', '@', '[', '\\', ']', '^', '_', '`', '{', - '|', '}', '~': + '|', '}', '~': getAdornment(L, tok) if len(tok.symbol) <= 3: tok.kind = tkPunct - else: + else: tok.line = L.line tok.col = L.col - if c == '\0': + if c == '\0': tok.kind = tkEof - else: + else: tok.kind = tkOther add(tok.symbol, c) inc(L.bufpos) inc(L.col) tok.col = max(tok.col - L.baseIndent, 0) -proc getTokens(buffer: string, skipPounds: bool, tokens: var TTokenSeq): int = +proc getTokens(buffer: string, skipPounds: bool, tokens: var TTokenSeq): int = var L: TLexer var length = len(tokens) L.buf = cstring(buffer) L.line = 0 # skip UTF-8 BOM - if (L.buf[0] == '\xEF') and (L.buf[1] == '\xBB') and (L.buf[2] == '\xBF'): + if (L.buf[0] == '\xEF') and (L.buf[1] == '\xBB') and (L.buf[2] == '\xBF'): inc(L.bufpos, 3) L.skipPounds = skipPounds - if skipPounds: - if L.buf[L.bufpos] == '#': + if skipPounds: + if L.buf[L.bufpos] == '#': inc(L.bufpos) inc(result) - if L.buf[L.bufpos] == '#': + if L.buf[L.bufpos] == '#': inc(L.bufpos) inc(result) L.baseIndent = 0 - while L.buf[L.bufpos] == ' ': + while L.buf[L.bufpos] == ' ': inc(L.bufpos) inc(L.baseIndent) inc(result) - while true: + while true: inc(length) setLen(tokens, length) rawGetTok(L, tokens[length - 1]) - if tokens[length - 1].kind == tkEof: break - if tokens[0].kind == tkWhite: + if tokens[length - 1].kind == tkEof: break + if tokens[0].kind == tkWhite: # BUGFIX tokens[0].ival = len(tokens[0].symbol) tokens[0].kind = tkIndent type TLevelMap = array[char, int] - TSubstitution = object + TSubstitution = object key*: string value*: PRstNode - TSharedState = object + TSharedState = object options: TRstParseOptions # parsing options uLevel, oLevel: int # counters for the section levels subs: seq[TSubstitution] # substitutions refs: seq[TSubstitution] # references underlineToLevel: TLevelMap # Saves for each possible title adornment # character its level in the - # current document. + # current document. # This is for single underline adornments. - overlineToLevel: TLevelMap # Saves for each possible title adornment + overlineToLevel: TLevelMap # Saves for each possible title adornment # character its level in the current - # document. + # document. # This is for over-underline adornments. msgHandler: TMsgHandler # How to handle errors. findFile: TFindFileHandler # How to find files. @@ -293,7 +293,7 @@ proc whichMsgClass*(k: TMsgKind): TMsgClass = of 'w', 'W': result = mcWarning of 'h', 'H': result = mcHint else: assert false, "msgkind does not fit naming scheme" - + proc defaultMsgHandler*(filename: string, line, col: int, msgkind: TMsgKind, arg: string) {.procvar.} = let mc = msgkind.whichMsgClass @@ -302,31 +302,31 @@ proc defaultMsgHandler*(filename: string, line, col: int, msgkind: TMsgKind, if mc == mcError: raise newException(EParseError, message) else: writeln(stdout, message) -proc defaultFindFile*(filename: string): string {.procvar.} = +proc defaultFindFile*(filename: string): string {.procvar.} = if existsFile(filename): result = filename else: result = "" proc newSharedState(options: TRstParseOptions, findFile: TFindFileHandler, - msgHandler: TMsgHandler): PSharedState = + msgHandler: TMsgHandler): PSharedState = new(result) result.subs = @[] result.refs = @[] result.options = options result.msgHandler = if not isNil(msgHandler): msgHandler else: defaultMsgHandler result.findFile = if not isNil(findFile): findFile else: defaultFindFile - -proc rstMessage(p: TRstParser, msgKind: TMsgKind, arg: string) = - p.s.msgHandler(p.filename, p.line + p.tok[p.idx].line, + +proc rstMessage(p: TRstParser, msgKind: TMsgKind, arg: string) = + p.s.msgHandler(p.filename, p.line + p.tok[p.idx].line, p.col + p.tok[p.idx].col, msgKind, arg) -proc rstMessage(p: TRstParser, msgKind: TMsgKind, arg: string, line, col: int) = +proc rstMessage(p: TRstParser, msgKind: TMsgKind, arg: string, line, col: int) = p.s.msgHandler(p.filename, p.line + line, p.col + col, msgKind, arg) -proc rstMessage(p: TRstParser, msgKind: TMsgKind) = - p.s.msgHandler(p.filename, p.line + p.tok[p.idx].line, - p.col + p.tok[p.idx].col, msgKind, +proc rstMessage(p: TRstParser, msgKind: TMsgKind) = + p.s.msgHandler(p.filename, p.line + p.tok[p.idx].line, + p.col + p.tok[p.idx].col, msgKind, p.tok[p.idx].symbol) when false: @@ -334,16 +334,16 @@ when false: assert p.indentStack[0] == 0 for i in 1 .. high(p.indentStack): assert p.indentStack[i] < 1_000 -proc currInd(p: TRstParser): int = +proc currInd(p: TRstParser): int = result = p.indentStack[high(p.indentStack)] -proc pushInd(p: var TRstParser, ind: int) = +proc pushInd(p: var TRstParser, ind: int) = add(p.indentStack, ind) proc popInd(p: var TRstParser) = if len(p.indentStack) > 1: setLen(p.indentStack, len(p.indentStack) - 1) - -proc initParser(p: var TRstParser, sharedState: PSharedState) = + +proc initParser(p: var TRstParser, sharedState: PSharedState) = p.indentStack = @[0] p.tok = @[] p.idx = 0 @@ -353,150 +353,150 @@ proc initParser(p: var TRstParser, sharedState: PSharedState) = p.line = 1 p.s = sharedState -proc addNodesAux(n: PRstNode, result: var string) = - if n.kind == rnLeaf: +proc addNodesAux(n: PRstNode, result: var string) = + if n.kind == rnLeaf: add(result, n.text) - else: + else: for i in countup(0, len(n) - 1): addNodesAux(n.sons[i], result) - -proc addNodes(n: PRstNode): string = + +proc addNodes(n: PRstNode): string = result = "" addNodesAux(n, result) -proc rstnodeToRefnameAux(n: PRstNode, r: var string, b: var bool) = - if n.kind == rnLeaf: - for i in countup(0, len(n.text) - 1): +proc rstnodeToRefnameAux(n: PRstNode, r: var string, b: var bool) = + if n.kind == rnLeaf: + for i in countup(0, len(n.text) - 1): case n.text[i] - of '0'..'9': - if b: + of '0'..'9': + if b: add(r, '-') b = false if len(r) == 0: add(r, 'Z') add(r, n.text[i]) - of 'a'..'z': - if b: + of 'a'..'z': + if b: add(r, '-') b = false add(r, n.text[i]) - of 'A'..'Z': - if b: + of 'A'..'Z': + if b: add(r, '-') b = false add(r, chr(ord(n.text[i]) - ord('A') + ord('a'))) - else: + else: if (len(r) > 0): b = true - else: + else: for i in countup(0, len(n) - 1): rstnodeToRefnameAux(n.sons[i], r, b) - -proc rstnodeToRefname(n: PRstNode): string = + +proc rstnodeToRefname(n: PRstNode): string = result = "" var b = false rstnodeToRefnameAux(n, result, b) -proc findSub(p: var TRstParser, n: PRstNode): int = - var key = addNodes(n) +proc findSub(p: var TRstParser, n: PRstNode): int = + var key = addNodes(n) # the spec says: if no exact match, try one without case distinction: - for i in countup(0, high(p.s.subs)): - if key == p.s.subs[i].key: + for i in countup(0, high(p.s.subs)): + if key == p.s.subs[i].key: return i - for i in countup(0, high(p.s.subs)): - if cmpIgnoreStyle(key, p.s.subs[i].key) == 0: + for i in countup(0, high(p.s.subs)): + if cmpIgnoreStyle(key, p.s.subs[i].key) == 0: return i result = -1 -proc setSub(p: var TRstParser, key: string, value: PRstNode) = +proc setSub(p: var TRstParser, key: string, value: PRstNode) = var length = len(p.s.subs) - for i in countup(0, length - 1): - if key == p.s.subs[i].key: + for i in countup(0, length - 1): + if key == p.s.subs[i].key: p.s.subs[i].value = value - return + return setLen(p.s.subs, length + 1) p.s.subs[length].key = key p.s.subs[length].value = value -proc setRef(p: var TRstParser, key: string, value: PRstNode) = +proc setRef(p: var TRstParser, key: string, value: PRstNode) = var length = len(p.s.refs) - for i in countup(0, length - 1): + for i in countup(0, length - 1): if key == p.s.refs[i].key: if p.s.refs[i].value.addNodes != value.addNodes: rstMessage(p, mwRedefinitionOfLabel, key) p.s.refs[i].value = value - return + return setLen(p.s.refs, length + 1) p.s.refs[length].key = key p.s.refs[length].value = value -proc findRef(p: var TRstParser, key: string): PRstNode = - for i in countup(0, high(p.s.refs)): - if key == p.s.refs[i].key: +proc findRef(p: var TRstParser, key: string): PRstNode = + for i in countup(0, high(p.s.refs)): + if key == p.s.refs[i].key: return p.s.refs[i].value -proc newLeaf(p: var TRstParser): PRstNode = +proc newLeaf(p: var TRstParser): PRstNode = result = newRstNode(rnLeaf, p.tok[p.idx].symbol) -proc getReferenceName(p: var TRstParser, endStr: string): PRstNode = +proc getReferenceName(p: var TRstParser, endStr: string): PRstNode = var res = newRstNode(rnInner) - while true: + while true: case p.tok[p.idx].kind - of tkWord, tkOther, tkWhite: + of tkWord, tkOther, tkWhite: add(res, newLeaf(p)) - of tkPunct: - if p.tok[p.idx].symbol == endStr: + of tkPunct: + if p.tok[p.idx].symbol == endStr: inc(p.idx) - break - else: + break + else: add(res, newLeaf(p)) - else: + else: rstMessage(p, meExpected, endStr) - break + break inc(p.idx) result = res -proc untilEol(p: var TRstParser): PRstNode = +proc untilEol(p: var TRstParser): PRstNode = result = newRstNode(rnInner) - while not (p.tok[p.idx].kind in {tkIndent, tkEof}): + while not (p.tok[p.idx].kind in {tkIndent, tkEof}): add(result, newLeaf(p)) inc(p.idx) -proc expect(p: var TRstParser, tok: string) = +proc expect(p: var TRstParser, tok: string) = if p.tok[p.idx].symbol == tok: inc(p.idx) else: rstMessage(p, meExpected, tok) - -proc isInlineMarkupEnd(p: TRstParser, markup: string): bool = + +proc isInlineMarkupEnd(p: TRstParser, markup: string): bool = result = p.tok[p.idx].symbol == markup - if not result: + if not result: return # Rule 3: result = not (p.tok[p.idx - 1].kind in {tkIndent, tkWhite}) - if not result: + if not result: return # Rule 4: result = (p.tok[p.idx + 1].kind in {tkIndent, tkWhite, tkEof}) or (p.tok[p.idx + 1].symbol[0] in - {'\'', '\"', ')', ']', '}', '>', '-', '/', '\\', ':', '.', ',', ';', '!', + {'\'', '\"', ')', ']', '}', '>', '-', '/', '\\', ':', '.', ',', ';', '!', '?', '_'}) - if not result: + if not result: return # Rule 7: - if p.idx > 0: - if (markup != "``") and (p.tok[p.idx - 1].symbol == "\\"): + if p.idx > 0: + if (markup != "``") and (p.tok[p.idx - 1].symbol == "\\"): result = false -proc isInlineMarkupStart(p: TRstParser, markup: string): bool = +proc isInlineMarkupStart(p: TRstParser, markup: string): bool = var d: char result = p.tok[p.idx].symbol == markup - if not result: + if not result: return # Rule 1: result = (p.idx == 0) or (p.tok[p.idx - 1].kind in {tkIndent, tkWhite}) or (p.tok[p.idx - 1].symbol[0] in {'\'', '\"', '(', '[', '{', '<', '-', '/', ':', '_'}) - if not result: + if not result: return # Rule 2: result = not (p.tok[p.idx + 1].kind in {tkIndent, tkWhite, tkEof}) - if not result: + if not result: return # Rule 5 & 7: - if p.idx > 0: - if p.tok[p.idx - 1].symbol == "\\": + if p.idx > 0: + if p.tok[p.idx - 1].symbol == "\\": result = false - else: + else: var c = p.tok[p.idx - 1].symbol[0] case c of '\'', '\"': d = c @@ -507,7 +507,7 @@ proc isInlineMarkupStart(p: TRstParser, markup: string): bool = else: d = '\0' if d != '\0': result = p.tok[p.idx + 1].symbol[0] != d -proc match(p: TRstParser, start: int, expr: string): bool = +proc match(p: TRstParser, start: int, expr: string): bool = # regular expressions are: # special char exact match # 'w' tkWord @@ -521,7 +521,7 @@ proc match(p: TRstParser, start: int, expr: string): bool = var i = 0 var j = start var last = len(expr) - 1 - while i <= last: + while i <= last: case expr[i] of 'w': result = p.tok[j].kind == tkWord of ' ': result = p.tok[j].kind == tkWhite @@ -531,75 +531,75 @@ proc match(p: TRstParser, start: int, expr: string): bool = of 'o': result = p.tok[j].kind == tkOther of 'T': result = true of 'E': result = p.tok[j].kind in {tkEof, tkWhite, tkIndent} - of 'e': + of 'e': result = (p.tok[j].kind == tkWord) or (p.tok[j].symbol == "#") - if result: + if result: case p.tok[j].symbol[0] of 'a'..'z', 'A'..'Z': result = len(p.tok[j].symbol) == 1 of '0'..'9': result = allCharsInSet(p.tok[j].symbol, {'0'..'9'}) else: discard - else: + else: var c = expr[i] var length = 0 - while (i <= last) and (expr[i] == c): + while (i <= last) and (expr[i] == c): inc(i) inc(length) dec(i) result = (p.tok[j].kind in {tkPunct, tkAdornment}) and (len(p.tok[j].symbol) == length) and (p.tok[j].symbol[0] == c) - if not result: return + if not result: return inc(j) inc(i) result = true - -proc fixupEmbeddedRef(n, a, b: PRstNode) = + +proc fixupEmbeddedRef(n, a, b: PRstNode) = var sep = - 1 - for i in countdown(len(n) - 2, 0): - if n.sons[i].text == "<": + for i in countdown(len(n) - 2, 0): + if n.sons[i].text == "<": sep = i - break + break var incr = if (sep > 0) and (n.sons[sep - 1].text[0] == ' '): 2 else: 1 for i in countup(0, sep - incr): add(a, n.sons[i]) for i in countup(sep + 1, len(n) - 2): add(b, n.sons[i]) - -proc parsePostfix(p: var TRstParser, n: PRstNode): PRstNode = + +proc parsePostfix(p: var TRstParser, n: PRstNode): PRstNode = result = n - if isInlineMarkupEnd(p, "_"): + if isInlineMarkupEnd(p, "_") or isInlineMarkupEnd(p, "__"): inc(p.idx) if p.tok[p.idx-2].symbol == "`" and p.tok[p.idx-3].symbol == ">": var a = newRstNode(rnInner) var b = newRstNode(rnInner) fixupEmbeddedRef(n, a, b) - if len(a) == 0: + if len(a) == 0: result = newRstNode(rnStandaloneHyperlink) add(result, b) - else: + else: result = newRstNode(rnHyperlink) add(result, a) add(result, b) setRef(p, rstnodeToRefname(a), b) - elif n.kind == rnInterpretedText: + elif n.kind == rnInterpretedText: n.kind = rnRef - else: + else: result = newRstNode(rnRef) add(result, n) - elif match(p, p.idx, ":w:"): + elif match(p, p.idx, ":w:"): # a role: - if p.tok[p.idx + 1].symbol == "idx": + if p.tok[p.idx + 1].symbol == "idx": n.kind = rnIdx - elif p.tok[p.idx + 1].symbol == "literal": + elif p.tok[p.idx + 1].symbol == "literal": n.kind = rnInlineLiteral - elif p.tok[p.idx + 1].symbol == "strong": + elif p.tok[p.idx + 1].symbol == "strong": n.kind = rnStrongEmphasis - elif p.tok[p.idx + 1].symbol == "emphasis": + elif p.tok[p.idx + 1].symbol == "emphasis": n.kind = rnEmphasis elif (p.tok[p.idx + 1].symbol == "sub") or - (p.tok[p.idx + 1].symbol == "subscript"): + (p.tok[p.idx + 1].symbol == "subscript"): n.kind = rnSub elif (p.tok[p.idx + 1].symbol == "sup") or - (p.tok[p.idx + 1].symbol == "supscript"): + (p.tok[p.idx + 1].symbol == "supscript"): n.kind = rnSup - else: + else: result = newRstNode(rnGeneralRole) n.kind = rnInner add(result, n) @@ -614,7 +614,7 @@ proc matchVerbatim(p: TRstParser, start: int, expr: string): int = inc j, p.tok[result].symbol.len inc result if j < expr.len: result = 0 - + proc parseSmiley(p: var TRstParser): PRstNode = if p.tok[p.idx].symbol[0] notin SmileyStartChars: return for key, val in items(Smilies): @@ -636,17 +636,17 @@ proc isUrl(p: TRstParser, i: int): bool = (p.tok[i+3].kind == tkWord) and (p.tok[i].symbol in ["http", "https", "ftp", "telnet", "file"]) -proc parseUrl(p: var TRstParser, father: PRstNode) = +proc parseUrl(p: var TRstParser, father: PRstNode) = #if p.tok[p.idx].symbol[strStart] == '<': if isUrl(p, p.idx): var n = newRstNode(rnStandaloneHyperlink) - while true: + while true: case p.tok[p.idx].kind of tkWord, tkAdornment, tkOther: discard - of tkPunct: + of tkPunct: if p.tok[p.idx+1].kind notin {tkWord, tkAdornment, tkOther, tkPunct}: break - else: break + else: break add(n, newLeaf(p)) inc(p.idx) add(father, n) @@ -655,13 +655,13 @@ proc parseUrl(p: var TRstParser, father: PRstNode) = inc(p.idx) if p.tok[p.idx].symbol == "_": n = parsePostfix(p, n) add(father, n) - -proc parseBackslash(p: var TRstParser, father: PRstNode) = + +proc parseBackslash(p: var TRstParser, father: PRstNode) = assert(p.tok[p.idx].kind == tkPunct) - if p.tok[p.idx].symbol == "\\\\": + if p.tok[p.idx].symbol == "\\\\": add(father, newRstNode(rnLeaf, "\\")) inc(p.idx) - elif p.tok[p.idx].symbol == "\\": + elif p.tok[p.idx].symbol == "\\": # XXX: Unicode? inc(p.idx) if p.tok[p.idx].kind != tkWhite: add(father, newLeaf(p)) @@ -674,13 +674,13 @@ when false: proc parseAdhoc(p: var TRstParser, father: PRstNode, verbatim: bool) = if not verbatim and isURL(p, p.idx): var n = newRstNode(rnStandaloneHyperlink) - while true: + while true: case p.tok[p.idx].kind of tkWord, tkAdornment, tkOther: nil - of tkPunct: + of tkPunct: if p.tok[p.idx+1].kind notin {tkWord, tkAdornment, tkOther, tkPunct}: break - else: break + else: break add(n, newLeaf(p)) inc(p.idx) add(father, n) @@ -694,33 +694,33 @@ when false: if p.tok[p.idx].symbol == "_": n = parsePostfix(p, n) add(father, n) -proc parseUntil(p: var TRstParser, father: PRstNode, postfix: string, - interpretBackslash: bool) = +proc parseUntil(p: var TRstParser, father: PRstNode, postfix: string, + interpretBackslash: bool) = let line = p.tok[p.idx].line col = p.tok[p.idx].col inc p.idx - while true: + while true: case p.tok[p.idx].kind - of tkPunct: - if isInlineMarkupEnd(p, postfix): + of tkPunct: + if isInlineMarkupEnd(p, postfix): inc(p.idx) - break - elif interpretBackslash: + break + elif interpretBackslash: parseBackslash(p, father) - else: + else: add(father, newLeaf(p)) inc(p.idx) - of tkAdornment, tkWord, tkOther: + of tkAdornment, tkWord, tkOther: add(father, newLeaf(p)) inc(p.idx) - of tkIndent: + of tkIndent: add(father, newRstNode(rnLeaf, " ")) inc(p.idx) - if p.tok[p.idx].kind == tkIndent: + if p.tok[p.idx].kind == tkIndent: rstMessage(p, meExpected, postfix, line, col) - break - of tkWhite: + break + of tkWhite: add(father, newRstNode(rnLeaf, " ")) inc(p.idx) else: rstMessage(p, meExpected, postfix, line, col) @@ -753,20 +753,20 @@ proc parseMarkdownCodeblock(p: var TRstParser): PRstNode = result = newRstNode(rnCodeBlock) add(result, args) add(result, nil) - add(result, lb) - -proc parseInline(p: var TRstParser, father: PRstNode) = + add(result, lb) + +proc parseInline(p: var TRstParser, father: PRstNode) = case p.tok[p.idx].kind - of tkPunct: + of tkPunct: if isInlineMarkupStart(p, "***"): var n = newRstNode(rnTripleEmphasis) parseUntil(p, n, "***", true) add(father, n) - elif isInlineMarkupStart(p, "**"): + elif isInlineMarkupStart(p, "**"): var n = newRstNode(rnStrongEmphasis) parseUntil(p, n, "**", true) add(father, n) - elif isInlineMarkupStart(p, "*"): + elif isInlineMarkupStart(p, "*"): var n = newRstNode(rnEmphasis) parseUntil(p, n, "*", true) add(father, n) @@ -777,12 +777,12 @@ proc parseInline(p: var TRstParser, father: PRstNode) = var n = newRstNode(rnInlineLiteral) parseUntil(p, n, "``", false) add(father, n) - elif isInlineMarkupStart(p, "`"): + elif isInlineMarkupStart(p, "`"): var n = newRstNode(rnInterpretedText) parseUntil(p, n, "`", true) n = parsePostfix(p, n) add(father, n) - elif isInlineMarkupStart(p, "|"): + elif isInlineMarkupStart(p, "|"): var n = newRstNode(rnSubstitutionReferences) parseUntil(p, n, "|", false) add(father, n) @@ -800,7 +800,7 @@ proc parseInline(p: var TRstParser, father: PRstNode) = add(father, n) return parseUrl(p, father) - of tkAdornment, tkOther, tkWhite: + of tkAdornment, tkOther, tkWhite: if roSupportSmilies in p.s.options: let n = parseSmiley(p) if n != nil: @@ -809,75 +809,75 @@ proc parseInline(p: var TRstParser, father: PRstNode) = add(father, newLeaf(p)) inc(p.idx) else: discard - -proc getDirective(p: var TRstParser): string = - if p.tok[p.idx].kind == tkWhite and p.tok[p.idx+1].kind == tkWord: + +proc getDirective(p: var TRstParser): string = + if p.tok[p.idx].kind == tkWhite and p.tok[p.idx+1].kind == tkWord: var j = p.idx inc(p.idx) result = p.tok[p.idx].symbol inc(p.idx) - while p.tok[p.idx].kind in {tkWord, tkPunct, tkAdornment, tkOther}: - if p.tok[p.idx].symbol == "::": break + while p.tok[p.idx].kind in {tkWord, tkPunct, tkAdornment, tkOther}: + if p.tok[p.idx].symbol == "::": break add(result, p.tok[p.idx].symbol) inc(p.idx) if p.tok[p.idx].kind == tkWhite: inc(p.idx) - if p.tok[p.idx].symbol == "::": + if p.tok[p.idx].symbol == "::": inc(p.idx) if (p.tok[p.idx].kind == tkWhite): inc(p.idx) - else: + else: p.idx = j # set back result = "" # error - else: + else: result = "" - -proc parseComment(p: var TRstParser): PRstNode = + +proc parseComment(p: var TRstParser): PRstNode = case p.tok[p.idx].kind - of tkIndent, tkEof: - if p.tok[p.idx].kind != tkEof and p.tok[p.idx + 1].kind == tkIndent: + of tkIndent, tkEof: + if p.tok[p.idx].kind != tkEof and p.tok[p.idx + 1].kind == tkIndent: inc(p.idx) # empty comment - else: + else: var indent = p.tok[p.idx].ival - while true: + while true: case p.tok[p.idx].kind - of tkEof: - break - of tkIndent: - if (p.tok[p.idx].ival < indent): break - else: + of tkEof: + break + of tkIndent: + if (p.tok[p.idx].ival < indent): break + else: discard inc(p.idx) else: while p.tok[p.idx].kind notin {tkIndent, tkEof}: inc(p.idx) result = nil -type +type TDirKind = enum # must be ordered alphabetically! dkNone, dkAuthor, dkAuthors, dkCode, dkCodeBlock, dkContainer, dkContents, dkFigure, dkImage, dkInclude, dkIndex, dkRaw, dkTitle -const +const DirIds: array[0..12, string] = ["", "author", "authors", "code", "code-block", "container", "contents", "figure", "image", "include", "index", "raw", "title"] -proc getDirKind(s: string): TDirKind = +proc getDirKind(s: string): TDirKind = let i = find(DirIds, s) if i >= 0: result = TDirKind(i) else: result = dkNone - -proc parseLine(p: var TRstParser, father: PRstNode) = - while true: + +proc parseLine(p: var TRstParser, father: PRstNode) = + while true: case p.tok[p.idx].kind of tkWhite, tkWord, tkOther, tkPunct: parseInline(p, father) - else: break + else: break -proc parseUntilNewline(p: var TRstParser, father: PRstNode) = - while true: +proc parseUntilNewline(p: var TRstParser, father: PRstNode) = + while true: case p.tok[p.idx].kind of tkWhite, tkWord, tkAdornment, tkOther, tkPunct: parseInline(p, father) 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 = ## Returns a parsed rnField node. ## @@ -888,9 +888,9 @@ proc parseField(p: var TRstParser): PRstNode = parseUntil(p, fieldname, ":", false) var fieldbody = newRstNode(rnFieldBody) if p.tok[p.idx].kind != tkIndent: parseLine(p, fieldbody) - if p.tok[p.idx].kind == tkIndent: + if p.tok[p.idx].kind == tkIndent: var indent = p.tok[p.idx].ival - if indent > col: + if indent > col: pushInd(p, indent) parseSection(p, fieldbody) popInd(p) @@ -909,14 +909,14 @@ proc parseFields(p: var TRstParser): PRstNode = var col = if atStart: p.tok[p.idx].col else: p.tok[p.idx].ival result = newRstNode(rnFieldList) if not atStart: inc(p.idx) - while true: + while true: add(result, parseField(p)) if (p.tok[p.idx].kind == tkIndent) and (p.tok[p.idx].ival == col) and - (p.tok[p.idx + 1].symbol == ":"): + (p.tok[p.idx + 1].symbol == ":"): inc(p.idx) - else: - break - + else: + break + proc getFieldValue*(n: PRstNode): string = ## Returns the value of a specific ``rnField`` node. ## @@ -929,122 +929,122 @@ proc getFieldValue*(n: PRstNode): string = assert n.sons[1].kind == rnFieldBody result = addNodes(n.sons[1]).strip -proc getFieldValue(n: PRstNode, fieldname: string): string = +proc getFieldValue(n: PRstNode, fieldname: string): string = result = "" - if n.sons[1] == nil: return - if (n.sons[1].kind != rnFieldList): + if n.sons[1] == nil: return + if (n.sons[1].kind != rnFieldList): #InternalError("getFieldValue (2): " & $n.sons[1].kind) # We don't like internal errors here anymore as that would break the forum! return - for i in countup(0, len(n.sons[1]) - 1): + for i in countup(0, len(n.sons[1]) - 1): var f = n.sons[1].sons[i] - if cmpIgnoreStyle(addNodes(f.sons[0]), fieldname) == 0: + if cmpIgnoreStyle(addNodes(f.sons[0]), fieldname) == 0: result = addNodes(f.sons[1]) if result == "": result = "\x01\x01" # indicates that the field exists - return + return -proc getArgument(n: PRstNode): string = +proc getArgument(n: PRstNode): string = if n.sons[0] == nil: result = "" else: result = addNodes(n.sons[0]) - -proc parseDotDot(p: var TRstParser): PRstNode -proc parseLiteralBlock(p: var TRstParser): PRstNode = + +proc parseDotDot(p: var TRstParser): PRstNode {.gcsafe.} +proc parseLiteralBlock(p: var TRstParser): PRstNode = result = newRstNode(rnLiteralBlock) var n = newRstNode(rnLeaf, "") - if p.tok[p.idx].kind == tkIndent: + if p.tok[p.idx].kind == tkIndent: var indent = p.tok[p.idx].ival inc(p.idx) - while true: + while true: case p.tok[p.idx].kind - of tkEof: - break - of tkIndent: - if (p.tok[p.idx].ival < indent): - break - else: + of tkEof: + break + of tkIndent: + if (p.tok[p.idx].ival < indent): + break + else: add(n.text, "\n") add(n.text, spaces(p.tok[p.idx].ival - indent)) inc(p.idx) - else: + else: add(n.text, p.tok[p.idx].symbol) inc(p.idx) - else: - while not (p.tok[p.idx].kind in {tkIndent, tkEof}): + else: + while not (p.tok[p.idx].kind in {tkIndent, tkEof}): add(n.text, p.tok[p.idx].symbol) inc(p.idx) add(result, n) -proc getLevel(map: var TLevelMap, lvl: var int, c: char): int = - if map[c] == 0: +proc getLevel(map: var TLevelMap, lvl: var int, c: char): int = + if map[c] == 0: inc(lvl) map[c] = lvl result = map[c] -proc tokenAfterNewline(p: TRstParser): int = +proc tokenAfterNewline(p: TRstParser): int = result = p.idx - while true: + while true: case p.tok[result].kind - of tkEof: - break - of tkIndent: + of tkEof: + break + of tkIndent: inc(result) - break + break else: inc(result) - -proc isLineBlock(p: TRstParser): bool = + +proc isLineBlock(p: TRstParser): bool = var j = tokenAfterNewline(p) result = (p.tok[p.idx].col == p.tok[j].col) and (p.tok[j].symbol == "|") or (p.tok[j].col > p.tok[p.idx].col) -proc predNL(p: TRstParser): bool = +proc predNL(p: TRstParser): bool = result = true if p.idx > 0: result = p.tok[p.idx-1].kind == tkIndent and p.tok[p.idx-1].ival == currInd(p) - -proc isDefList(p: TRstParser): bool = + +proc isDefList(p: TRstParser): bool = var j = tokenAfterNewline(p) result = (p.tok[p.idx].col < p.tok[j].col) and (p.tok[j].kind in {tkWord, tkOther, tkPunct}) and (p.tok[j - 2].symbol != "::") -proc isOptionList(p: TRstParser): bool = +proc isOptionList(p: TRstParser): bool = result = match(p, p.idx, "-w") or match(p, p.idx, "--w") or match(p, p.idx, "/w") or match(p, p.idx, "//w") -proc whichSection(p: TRstParser): TRstNodeKind = +proc whichSection(p: TRstParser): TRstNodeKind = case p.tok[p.idx].kind - of tkAdornment: + of tkAdornment: if match(p, p.idx + 1, "ii"): result = rnTransition elif match(p, p.idx + 1, " a"): result = rnTable elif match(p, p.idx + 1, "i"): result = rnOverline else: result = rnLeaf - of tkPunct: - if match(p, tokenAfterNewline(p), "ai"): + of tkPunct: + if match(p, tokenAfterNewline(p), "ai"): result = rnHeadline - elif p.tok[p.idx].symbol == "::": + elif p.tok[p.idx].symbol == "::": result = rnLiteralBlock elif predNL(p) and ((p.tok[p.idx].symbol == "+") or (p.tok[p.idx].symbol == "*") or - (p.tok[p.idx].symbol == "-")) and (p.tok[p.idx + 1].kind == tkWhite): + (p.tok[p.idx].symbol == "-")) and (p.tok[p.idx + 1].kind == tkWhite): result = rnBulletList - elif (p.tok[p.idx].symbol == "|") and isLineBlock(p): + elif (p.tok[p.idx].symbol == "|") and isLineBlock(p): result = rnLineBlock - elif (p.tok[p.idx].symbol == "..") and predNL(p): + elif (p.tok[p.idx].symbol == "..") and predNL(p): result = rnDirective elif match(p, p.idx, ":w:") and predNL(p): # (p.tok[p.idx].symbol == ":") result = rnFieldList - elif match(p, p.idx, "(e) "): + elif match(p, p.idx, "(e) "): result = rnEnumList - elif match(p, p.idx, "+a+"): + elif match(p, p.idx, "+a+"): result = rnGridTable rstMessage(p, meGridTableNotImplemented) - elif isDefList(p): + elif isDefList(p): result = rnDefList - elif isOptionList(p): + elif isOptionList(p): result = rnOptionList - else: + else: result = rnParagraph of tkWord, tkOther, tkWhite: if match(p, tokenAfterNewline(p), "ai"): result = rnHeadline @@ -1052,58 +1052,58 @@ proc whichSection(p: TRstParser): TRstNodeKind = elif isDefList(p): result = rnDefList else: result = rnParagraph else: result = rnLeaf - -proc parseLineBlock(p: var TRstParser): PRstNode = + +proc parseLineBlock(p: var TRstParser): PRstNode = result = nil - if p.tok[p.idx + 1].kind == tkWhite: + if p.tok[p.idx + 1].kind == tkWhite: var col = p.tok[p.idx].col result = newRstNode(rnLineBlock) pushInd(p, p.tok[p.idx + 2].col) inc(p.idx, 2) - while true: + while true: var item = newRstNode(rnLineBlockItem) parseSection(p, item) add(result, item) if (p.tok[p.idx].kind == tkIndent) and (p.tok[p.idx].ival == col) and (p.tok[p.idx + 1].symbol == "|") and - (p.tok[p.idx + 2].kind == tkWhite): + (p.tok[p.idx + 2].kind == tkWhite): inc(p.idx, 3) - else: - break + else: + break popInd(p) -proc parseParagraph(p: var TRstParser, result: PRstNode) = - while true: +proc parseParagraph(p: var TRstParser, result: PRstNode) = + while true: case p.tok[p.idx].kind - of tkIndent: - if p.tok[p.idx + 1].kind == tkIndent: + of tkIndent: + if p.tok[p.idx + 1].kind == tkIndent: inc(p.idx) - break - elif (p.tok[p.idx].ival == currInd(p)): + break + elif (p.tok[p.idx].ival == currInd(p)): inc(p.idx) case whichSection(p) - of rnParagraph, rnLeaf, rnHeadline, rnOverline, rnDirective: + of rnParagraph, rnLeaf, rnHeadline, rnOverline, rnDirective: add(result, newRstNode(rnLeaf, " ")) - of rnLineBlock: + of rnLineBlock: addIfNotNil(result, parseLineBlock(p)) - else: break - else: - break - of tkPunct: + else: break + else: + break + of tkPunct: if (p.tok[p.idx].symbol == "::") and (p.tok[p.idx + 1].kind == tkIndent) and - (currInd(p) < p.tok[p.idx + 1].ival): + (currInd(p) < p.tok[p.idx + 1].ival): add(result, newRstNode(rnLeaf, ":")) inc(p.idx) # skip '::' add(result, parseLiteralBlock(p)) - break - else: + break + else: parseInline(p, result) - of tkWhite, tkWord, tkAdornment, tkOther: + of tkWhite, tkWord, tkAdornment, tkOther: parseInline(p, result) else: break -proc parseHeadline(p: var TRstParser): PRstNode = +proc parseHeadline(p: var TRstParser): PRstNode = result = newRstNode(rnHeadline) parseUntilNewline(p, result) assert(p.tok[p.idx].kind == tkIndent) @@ -1112,31 +1112,31 @@ proc parseHeadline(p: var TRstParser): PRstNode = inc(p.idx, 2) result.level = getLevel(p.s.underlineToLevel, p.s.uLevel, c) -type +type TIntSeq = seq[int] -proc tokEnd(p: TRstParser): int = +proc tokEnd(p: TRstParser): int = result = p.tok[p.idx].col + len(p.tok[p.idx].symbol) - 1 -proc getColumns(p: var TRstParser, cols: var TIntSeq) = +proc getColumns(p: var TRstParser, cols: var TIntSeq) = var L = 0 - while true: + while true: inc(L) setLen(cols, L) cols[L - 1] = tokEnd(p) assert(p.tok[p.idx].kind == tkAdornment) inc(p.idx) - if p.tok[p.idx].kind != tkWhite: break + if p.tok[p.idx].kind != tkWhite: break inc(p.idx) - if p.tok[p.idx].kind != tkAdornment: break - if p.tok[p.idx].kind == tkIndent: inc(p.idx) + if p.tok[p.idx].kind != tkAdornment: break + if p.tok[p.idx].kind == tkIndent: inc(p.idx) # last column has no limit: cols[L - 1] = 32000 -proc parseDoc(p: var TRstParser): PRstNode +proc parseDoc(p: var TRstParser): PRstNode {.gcsafe.} -proc parseSimpleTable(p: var TRstParser): PRstNode = - var +proc parseSimpleTable(p: var TRstParser): PRstNode = + var cols: TIntSeq row: seq[string] i, last, line: int @@ -1148,36 +1148,36 @@ proc parseSimpleTable(p: var TRstParser): PRstNode = row = @[] a = nil c = p.tok[p.idx].symbol[0] - while true: - if p.tok[p.idx].kind == tkAdornment: + while true: + if p.tok[p.idx].kind == tkAdornment: last = tokenAfterNewline(p) - if p.tok[last].kind in {tkEof, tkIndent}: + if p.tok[last].kind in {tkEof, tkIndent}: # skip last adornment line: p.idx = last - break + break getColumns(p, cols) setLen(row, len(cols)) - if a != nil: + if a != nil: for j in 0..len(a)-1: a.sons[j].kind = rnTableHeaderCell - if p.tok[p.idx].kind == tkEof: break + if p.tok[p.idx].kind == tkEof: break for j in countup(0, high(row)): row[j] = "" # the following while loop iterates over the lines a single cell may span: line = p.tok[p.idx].line - while true: + while true: i = 0 - while not (p.tok[p.idx].kind in {tkIndent, tkEof}): - if (tokEnd(p) <= cols[i]): + while not (p.tok[p.idx].kind in {tkIndent, tkEof}): + if (tokEnd(p) <= cols[i]): add(row[i], p.tok[p.idx].symbol) inc(p.idx) - else: + else: if p.tok[p.idx].kind == tkWhite: inc(p.idx) inc(i) if p.tok[p.idx].kind == tkIndent: inc(p.idx) - if tokEnd(p) <= cols[0]: break - if p.tok[p.idx].kind in {tkEof, tkAdornment}: break + if tokEnd(p) <= cols[0]: break + if p.tok[p.idx].kind in {tkEof, tkAdornment}: break for j in countup(1, high(row)): add(row[j], '\x0A') a = newRstNode(rnTableRow) - for j in countup(0, high(row)): + for j in countup(0, high(row)): initParser(q, p.s) q.col = cols[j] q.line = line - 1 @@ -1188,95 +1188,95 @@ proc parseSimpleTable(p: var TRstParser): PRstNode = add(a, b) add(result, a) -proc parseTransition(p: var TRstParser): PRstNode = +proc parseTransition(p: var TRstParser): PRstNode = result = newRstNode(rnTransition) inc(p.idx) if p.tok[p.idx].kind == tkIndent: inc(p.idx) if p.tok[p.idx].kind == tkIndent: inc(p.idx) - -proc parseOverline(p: var TRstParser): PRstNode = + +proc parseOverline(p: var TRstParser): PRstNode = var c = p.tok[p.idx].symbol[0] inc(p.idx, 2) result = newRstNode(rnOverline) - while true: + while true: parseUntilNewline(p, result) - if p.tok[p.idx].kind == tkIndent: + if p.tok[p.idx].kind == tkIndent: inc(p.idx) - if p.tok[p.idx - 1].ival > currInd(p): + if p.tok[p.idx - 1].ival > currInd(p): add(result, newRstNode(rnLeaf, " ")) - else: - break - else: - break + else: + break + else: + break result.level = getLevel(p.s.overlineToLevel, p.s.oLevel, c) - if p.tok[p.idx].kind == tkAdornment: + if p.tok[p.idx].kind == tkAdornment: inc(p.idx) # XXX: check? if p.tok[p.idx].kind == tkIndent: inc(p.idx) - -proc parseBulletList(p: var TRstParser): PRstNode = + +proc parseBulletList(p: var TRstParser): PRstNode = result = nil - if p.tok[p.idx + 1].kind == tkWhite: + if p.tok[p.idx + 1].kind == tkWhite: var bullet = p.tok[p.idx].symbol var col = p.tok[p.idx].col result = newRstNode(rnBulletList) pushInd(p, p.tok[p.idx + 2].col) inc(p.idx, 2) - while true: + while true: var item = newRstNode(rnBulletItem) parseSection(p, item) add(result, item) if (p.tok[p.idx].kind == tkIndent) and (p.tok[p.idx].ival == col) and (p.tok[p.idx + 1].symbol == bullet) and - (p.tok[p.idx + 2].kind == tkWhite): + (p.tok[p.idx + 2].kind == tkWhite): inc(p.idx, 3) - else: - break + else: + break popInd(p) -proc parseOptionList(p: var TRstParser): PRstNode = +proc parseOptionList(p: var TRstParser): PRstNode = result = newRstNode(rnOptionList) - while true: + while true: if isOptionList(p): var a = newRstNode(rnOptionGroup) var b = newRstNode(rnDescription) var c = newRstNode(rnOptionListItem) if match(p, p.idx, "//w"): inc(p.idx) - while not (p.tok[p.idx].kind in {tkIndent, tkEof}): - if (p.tok[p.idx].kind == tkWhite) and (len(p.tok[p.idx].symbol) > 1): + while not (p.tok[p.idx].kind in {tkIndent, tkEof}): + if (p.tok[p.idx].kind == tkWhite) and (len(p.tok[p.idx].symbol) > 1): inc(p.idx) - break + break add(a, newLeaf(p)) inc(p.idx) var j = tokenAfterNewline(p) if (j > 0) and (p.tok[j - 1].kind == tkIndent) and - (p.tok[j - 1].ival > currInd(p)): + (p.tok[j - 1].ival > currInd(p)): pushInd(p, p.tok[j - 1].ival) parseSection(p, b) popInd(p) - else: + else: parseLine(p, b) if (p.tok[p.idx].kind == tkIndent): inc(p.idx) add(c, a) add(c, b) add(result, c) - else: - break - -proc parseDefinitionList(p: var TRstParser): PRstNode = + else: + break + +proc parseDefinitionList(p: var TRstParser): PRstNode = result = nil var j = tokenAfterNewline(p) - 1 if (j >= 1) and (p.tok[j].kind == tkIndent) and - (p.tok[j].ival > currInd(p)) and (p.tok[j - 1].symbol != "::"): + (p.tok[j].ival > currInd(p)) and (p.tok[j - 1].symbol != "::"): var col = p.tok[p.idx].col result = newRstNode(rnDefList) - while true: + while true: j = p.idx var a = newRstNode(rnDefName) parseLine(p, a) if (p.tok[p.idx].kind == tkIndent) and (p.tok[p.idx].ival > currInd(p)) and (p.tok[p.idx + 1].symbol != "::") and - not (p.tok[p.idx + 1].kind in {tkIndent, tkEof}): + not (p.tok[p.idx + 1].kind in {tkIndent, tkEof}): pushInd(p, p.tok[p.idx].ival) var b = newRstNode(rnDefBody) parseSection(p, b) @@ -1285,74 +1285,74 @@ proc parseDefinitionList(p: var TRstParser): PRstNode = add(c, b) add(result, c) popInd(p) - else: + else: p.idx = j - break - if (p.tok[p.idx].kind == tkIndent) and (p.tok[p.idx].ival == col): + break + if (p.tok[p.idx].kind == tkIndent) and (p.tok[p.idx].ival == col): inc(p.idx) j = tokenAfterNewline(p) - 1 if j >= 1 and p.tok[j].kind == tkIndent and p.tok[j].ival > col and - p.tok[j-1].symbol != "::" and p.tok[j+1].kind != tkIndent: + p.tok[j-1].symbol != "::" and p.tok[j+1].kind != tkIndent: discard - else: - break + else: + break if len(result) == 0: result = nil - -proc parseEnumList(p: var TRstParser): PRstNode = - const + +proc parseEnumList(p: var TRstParser): PRstNode = + const wildcards: array[0..2, string] = ["(e) ", "e) ", "e. "] wildpos: array[0..2, int] = [1, 0, 0] result = nil var w = 0 - while w <= 2: - if match(p, p.idx, wildcards[w]): break + while w <= 2: + if match(p, p.idx, wildcards[w]): break inc(w) - if w <= 2: + if w <= 2: var col = p.tok[p.idx].col result = newRstNode(rnEnumList) inc(p.idx, wildpos[w] + 3) var j = tokenAfterNewline(p) - if (p.tok[j].col == p.tok[p.idx].col) or match(p, j, wildcards[w]): + if (p.tok[j].col == p.tok[p.idx].col) or match(p, j, wildcards[w]): pushInd(p, p.tok[p.idx].col) - while true: + while true: var item = newRstNode(rnEnumItem) parseSection(p, item) add(result, item) if (p.tok[p.idx].kind == tkIndent) and (p.tok[p.idx].ival == col) and - match(p, p.idx + 1, wildcards[w]): + match(p, p.idx + 1, wildcards[w]): inc(p.idx, wildpos[w] + 4) - else: - break + else: + break popInd(p) - else: + else: dec(p.idx, wildpos[w] + 3) result = nil -proc sonKind(father: PRstNode, i: int): TRstNodeKind = +proc sonKind(father: PRstNode, i: int): TRstNodeKind = result = rnLeaf if i < len(father): result = father.sons[i].kind - -proc parseSection(p: var TRstParser, result: PRstNode) = - while true: + +proc parseSection(p: var TRstParser, result: PRstNode) = + while true: var leave = false assert(p.idx >= 0) - while p.tok[p.idx].kind == tkIndent: - if currInd(p) == p.tok[p.idx].ival: + while p.tok[p.idx].kind == tkIndent: + if currInd(p) == p.tok[p.idx].ival: inc(p.idx) - elif p.tok[p.idx].ival > currInd(p): + elif p.tok[p.idx].ival > currInd(p): pushInd(p, p.tok[p.idx].ival) var a = newRstNode(rnBlockQuote) parseSection(p, a) add(result, a) popInd(p) - else: + else: leave = true break if leave or p.tok[p.idx].kind == tkEof: break var a: PRstNode = nil var k = whichSection(p) case k - of rnLiteralBlock: + of rnLiteralBlock: inc(p.idx) # skip '::' a = parseLiteralBlock(p) of rnBulletList: a = parseBulletList(p) @@ -1362,7 +1362,7 @@ proc parseSection(p: var TRstParser, result: PRstNode) = of rnLeaf: rstMessage(p, meNewSectionExpected) of rnParagraph: discard of rnDefList: a = parseDefinitionList(p) - of rnFieldList: + of rnFieldList: if p.idx > 0: dec(p.idx) a = parseFields(p) of rnTransition: a = parseTransition(p) @@ -1373,35 +1373,35 @@ proc parseSection(p: var TRstParser, result: PRstNode) = else: #InternalError("rst.parseSection()") discard - if a == nil and k != rnDirective: + if a == nil and k != rnDirective: a = newRstNode(rnParagraph) parseParagraph(p, a) addIfNotNil(result, a) - if sonKind(result, 0) == rnParagraph and sonKind(result, 1) != rnParagraph: + if sonKind(result, 0) == rnParagraph and sonKind(result, 1) != rnParagraph: result.sons[0].kind = rnInner - -proc parseSectionWrapper(p: var TRstParser): PRstNode = + +proc parseSectionWrapper(p: var TRstParser): PRstNode = result = newRstNode(rnInner) parseSection(p, result) - while (result.kind == rnInner) and (len(result) == 1): + while (result.kind == rnInner) and (len(result) == 1): result = result.sons[0] - + proc `$`(t: TToken): string = result = $t.kind & ' ' & (if isNil(t.symbol): "NIL" else: t.symbol) -proc parseDoc(p: var TRstParser): PRstNode = +proc parseDoc(p: var TRstParser): PRstNode = result = parseSectionWrapper(p) - if p.tok[p.idx].kind != tkEof: + if p.tok[p.idx].kind != tkEof: when false: assert isAllocatedPtr(cast[pointer](p.tok)) for i in 0 .. high(p.tok): - assert isNil(p.tok[i].symbol) or + assert isNil(p.tok[i].symbol) or isAllocatedPtr(cast[pointer](p.tok[i].symbol)) echo "index: ", p.idx, " length: ", high(p.tok), "##", p.tok[p.idx-1], p.tok[p.idx], p.tok[p.idx+1] #assert isAllocatedPtr(cast[pointer](p.indentStack)) rstMessage(p, meGeneralParseError) - + type TDirFlag = enum hasArg, hasOptions, argIsFile, argIsWord @@ -1421,55 +1421,55 @@ proc parseDirective(p: var TRstParser, flags: TDirFlags): PRstNode = result = newRstNode(rnDirective) var args: PRstNode = nil var options: PRstNode = nil - if hasArg in flags: + if hasArg in flags: args = newRstNode(rnDirArg) - if argIsFile in flags: - while true: + if argIsFile in flags: + while true: case p.tok[p.idx].kind - of tkWord, tkOther, tkPunct, tkAdornment: + of tkWord, tkOther, tkPunct, tkAdornment: add(args, newLeaf(p)) inc(p.idx) - else: break + else: break elif argIsWord in flags: while p.tok[p.idx].kind == tkWhite: inc(p.idx) - if p.tok[p.idx].kind == tkWord: + if p.tok[p.idx].kind == tkWord: add(args, newLeaf(p)) inc(p.idx) else: args = nil - else: + else: parseLine(p, args) add(result, args) - if hasOptions in flags: + if hasOptions in flags: if (p.tok[p.idx].kind == tkIndent) and (p.tok[p.idx].ival >= 3) and - (p.tok[p.idx + 1].symbol == ":"): + (p.tok[p.idx + 1].symbol == ":"): options = parseFields(p) add(result, options) - -proc indFollows(p: TRstParser): bool = + +proc indFollows(p: TRstParser): bool = result = p.tok[p.idx].kind == tkIndent and p.tok[p.idx].ival > currInd(p) - -proc parseDirective(p: var TRstParser, flags: TDirFlags, - contentParser: TSectionParser): PRstNode = + +proc parseDirective(p: var TRstParser, flags: TDirFlags, + contentParser: TSectionParser): PRstNode = ## Returns a generic rnDirective tree. ## ## The children are rnDirArg, rnFieldList and rnLineBlock. Any might be nil. result = parseDirective(p, flags) - if not isNil(contentParser) and indFollows(p): + if not isNil(contentParser) and indFollows(p): pushInd(p, p.tok[p.idx].ival) var content = contentParser(p) popInd(p) add(result, content) - else: + else: add(result, nil) -proc parseDirBody(p: var TRstParser, contentParser: TSectionParser): PRstNode = - if indFollows(p): +proc parseDirBody(p: var TRstParser, contentParser: TSectionParser): PRstNode = + if indFollows(p): pushInd(p, p.tok[p.idx].ival) result = contentParser(p) popInd(p) - -proc dirInclude(p: var TRstParser): PRstNode = + +proc dirInclude(p: var TRstParser): PRstNode = # #The following options are recognized: # @@ -1490,18 +1490,18 @@ proc dirInclude(p: var TRstParser): PRstNode = var n = parseDirective(p, {hasArg, argIsFile, hasOptions}, nil) var filename = strip(addNodes(n.sons[0])) var path = p.s.findFile(filename) - if path == "": + if path == "": rstMessage(p, meCannotOpenFile, filename) - else: + else: # XXX: error handling; recursive file inclusion! - if getFieldValue(n, "literal") != "": + if getFieldValue(n, "literal") != "": result = newRstNode(rnLiteralBlock) add(result, newRstNode(rnLeaf, readFile(path))) else: var q: TRstParser initParser(q, p.s) q.filename = filename - q.col += getTokens(readFile(path), false, q.tok) + q.col += getTokens(readFile(path), false, q.tok) # workaround a GCC bug; more like the interior pointer bug? #if find(q.tok[high(q.tok)].symbol, "\0\x01\x02") > 0: # InternalError("Too many binary zeros in include file") @@ -1526,7 +1526,7 @@ proc dirCodeBlock(p: var TRstParser, nimrodExtension = false): PRstNode = ## file. result = parseDirective(p, {hasArg, hasOptions}, parseLiteralBlock) var filename = strip(getFieldValue(result, "file")) - if filename != "": + if filename != "": var path = p.s.findFile(filename) if path == "": rstMessage(p, meCannotOpenFile, filename) var n = newRstNode(rnLiteralBlock) @@ -1548,49 +1548,49 @@ proc dirCodeBlock(p: var TRstParser, nimrodExtension = false): PRstNode = result.kind = rnCodeBlock -proc dirContainer(p: var TRstParser): PRstNode = +proc dirContainer(p: var TRstParser): PRstNode = result = parseDirective(p, {hasArg}, parseSectionWrapper) assert(result.kind == rnDirective) assert(len(result) == 3) result.kind = rnContainer -proc dirImage(p: var TRstParser): PRstNode = +proc dirImage(p: var TRstParser): PRstNode = result = parseDirective(p, {hasOptions, hasArg, argIsFile}, nil) result.kind = rnImage -proc dirFigure(p: var TRstParser): PRstNode = - result = parseDirective(p, {hasOptions, hasArg, argIsFile}, +proc dirFigure(p: var TRstParser): PRstNode = + result = parseDirective(p, {hasOptions, hasArg, argIsFile}, parseSectionWrapper) result.kind = rnFigure -proc dirTitle(p: var TRstParser): PRstNode = +proc dirTitle(p: var TRstParser): PRstNode = result = parseDirective(p, {hasArg}, nil) result.kind = rnTitle -proc dirContents(p: var TRstParser): PRstNode = +proc dirContents(p: var TRstParser): PRstNode = result = parseDirective(p, {hasArg}, nil) result.kind = rnContents -proc dirIndex(p: var TRstParser): PRstNode = +proc dirIndex(p: var TRstParser): PRstNode = result = parseDirective(p, {}, parseSectionWrapper) result.kind = rnIndex proc dirRawAux(p: var TRstParser, result: var PRstNode, kind: TRstNodeKind, - contentParser: TSectionParser) = + contentParser: TSectionParser) = var filename = getFieldValue(result, "file") - if filename.len > 0: + if filename.len > 0: var path = p.s.findFile(filename) - if path.len == 0: + if path.len == 0: rstMessage(p, meCannotOpenFile, filename) - else: + else: var f = readFile(path) result = newRstNode(kind) add(result, newRstNode(rnLeaf, f)) - else: + else: result.kind = kind add(result, parseDirBody(p, contentParser)) -proc dirRaw(p: var TRstParser): PRstNode = +proc dirRaw(p: var TRstParser): PRstNode = # #The following options are recognized: # @@ -1603,19 +1603,19 @@ proc dirRaw(p: var TRstParser): PRstNode = if result.sons[0] != nil: if cmpIgnoreCase(result.sons[0].sons[0].text, "html") == 0: dirRawAux(p, result, rnRawHtml, parseLiteralBlock) - elif cmpIgnoreCase(result.sons[0].sons[0].text, "latex") == 0: + elif cmpIgnoreCase(result.sons[0].sons[0].text, "latex") == 0: dirRawAux(p, result, rnRawLatex, parseLiteralBlock) else: rstMessage(p, meInvalidDirective, result.sons[0].sons[0].text) else: dirRawAux(p, result, rnRaw, parseSectionWrapper) -proc parseDotDot(p: var TRstParser): PRstNode = +proc parseDotDot(p: var TRstParser): PRstNode = result = nil var col = p.tok[p.idx].col inc(p.idx) var d = getDirective(p) - if d != "": + if d != "": pushInd(p, col) case getDirKind(d) of dkInclude: result = dirInclude(p) @@ -1634,66 +1634,66 @@ proc parseDotDot(p: var TRstParser): PRstNode = of dkIndex: result = dirIndex(p) else: rstMessage(p, meInvalidDirective, d) popInd(p) - elif match(p, p.idx, " _"): + elif match(p, p.idx, " _"): # hyperlink target: inc(p.idx, 2) var a = getReferenceName(p, ":") if p.tok[p.idx].kind == tkWhite: inc(p.idx) var b = untilEol(p) setRef(p, rstnodeToRefname(a), b) - elif match(p, p.idx, " |"): + elif match(p, p.idx, " |"): # substitution definitions: inc(p.idx, 2) var a = getReferenceName(p, "|") var b: PRstNode if p.tok[p.idx].kind == tkWhite: inc(p.idx) - if cmpIgnoreStyle(p.tok[p.idx].symbol, "replace") == 0: + if cmpIgnoreStyle(p.tok[p.idx].symbol, "replace") == 0: inc(p.idx) expect(p, "::") b = untilEol(p) - elif cmpIgnoreStyle(p.tok[p.idx].symbol, "image") == 0: + elif cmpIgnoreStyle(p.tok[p.idx].symbol, "image") == 0: inc(p.idx) b = dirImage(p) - else: + else: rstMessage(p, meInvalidDirective, p.tok[p.idx].symbol) setSub(p, addNodes(a), b) - elif match(p, p.idx, " ["): + elif match(p, p.idx, " ["): # footnotes, citations inc(p.idx, 2) var a = getReferenceName(p, "]") if p.tok[p.idx].kind == tkWhite: inc(p.idx) var b = untilEol(p) setRef(p, rstnodeToRefname(a), b) - else: + else: result = parseComment(p) - -proc resolveSubs(p: var TRstParser, n: PRstNode): PRstNode = + +proc resolveSubs(p: var TRstParser, n: PRstNode): PRstNode = result = n - if n == nil: return + if n == nil: return case n.kind - of rnSubstitutionReferences: + of rnSubstitutionReferences: var x = findSub(p, n) - if x >= 0: + if x >= 0: result = p.s.subs[x].value - else: + else: var key = addNodes(n) var e = getEnv(key) if e != "": result = newRstNode(rnLeaf, e) else: rstMessage(p, mwUnknownSubstitution, key) - of rnRef: + of rnRef: var y = findRef(p, rstnodeToRefname(n)) - if y != nil: + if y != nil: result = newRstNode(rnHyperlink) n.kind = rnInner add(result, n) add(result, y) - of rnLeaf: + of rnLeaf: discard - of rnContents: + of rnContents: p.hasToc = true - else: + else: for i in countup(0, len(n) - 1): n.sons[i] = resolveSubs(p, n.sons[i]) - + proc rstParse*(text, filename: string, line, column: int, hasToc: var bool, options: TRstParseOptions, diff --git a/lib/packages/docutils/rstast.nim b/lib/packages/docutils/rstast.nim index 614001d76..c3956ab8b 100644 --- a/lib/packages/docutils/rstast.nim +++ b/lib/packages/docutils/rstast.nim @@ -30,7 +30,7 @@ type rnField, # a field item rnFieldName, # consisting of a field name ... rnFieldBody, # ... and a field body - rnOptionList, rnOptionListItem, rnOptionGroup, rnOption, rnOptionString, + rnOptionList, rnOptionListItem, rnOptionGroup, rnOption, rnOptionString, rnOptionArgument, rnDescription, rnLiteralBlock, rnQuotedLiteralBlock, rnLineBlock, # the | thingie rnLineBlockItem, # sons of the | thing @@ -50,7 +50,7 @@ type # * `file#id <file#id>'_ rnSubstitutionDef, # a definition of a substitution rnGeneralRole, # Inline markup: - rnSub, rnSup, rnIdx, + rnSub, rnSup, rnIdx, rnEmphasis, # "*" rnStrongEmphasis, # "**" rnTripleEmphasis, # "***" @@ -71,25 +71,25 @@ type level*: int ## valid for some node kinds sons*: TRstNodeSeq ## the node's sons -proc len*(n: PRstNode): int = +proc len*(n: PRstNode): int = result = len(n.sons) -proc newRstNode*(kind: TRstNodeKind): PRstNode = +proc newRstNode*(kind: TRstNodeKind): PRstNode = new(result) result.sons = @[] result.kind = kind -proc newRstNode*(kind: TRstNodeKind, s: string): PRstNode = +proc newRstNode*(kind: TRstNodeKind, s: string): PRstNode = result = newRstNode(kind) result.text = s -proc lastSon*(n: PRstNode): PRstNode = +proc lastSon*(n: PRstNode): PRstNode = result = n.sons[len(n.sons)-1] proc add*(father, son: PRstNode) = add(father.sons, son) -proc addIfNotNil*(father, son: PRstNode) = +proc addIfNotNil*(father, son: PRstNode) = if son != nil: add(father, son) @@ -98,26 +98,27 @@ type indent: 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) = - for i in countup(0, len(n) - 1): +proc renderRstSons(d: var TRenderContext, n: PRstNode, result: var string) = + for i in countup(0, len(n) - 1): renderRstToRst(d, n.sons[i], result) - + proc renderRstToRst(d: var TRenderContext, n: PRstNode, result: var string) = # this is needed for the index generation; it may also be useful for # debugging, but most code is already debugged... - const + const lvlToChar: array[0..8, char] = ['!', '=', '-', '~', '`', '<', '*', '|', '+'] if n == nil: return var ind = spaces(d.indent) case n.kind - of rnInner: + of rnInner: renderRstSons(d, n, result) of rnHeadline: result.add("\n") result.add(ind) - + let oldLen = result.len renderRstSons(d, n, result) let headlineLen = result.len - oldLen @@ -131,16 +132,16 @@ proc renderRstToRst(d: var TRenderContext, n: PRstNode, result: var string) = var headline = "" renderRstSons(d, n, headline) - + let lvl = repeat(lvlToChar[n.level], headline.len - d.indent) result.add(lvl) result.add("\n") result.add(headline) - + result.add("\n") result.add(ind) result.add(lvl) - of rnTransition: + of rnTransition: result.add("\n\n") result.add(ind) result.add repeat('-', 78-d.indent) @@ -149,11 +150,11 @@ proc renderRstToRst(d: var TRenderContext, n: PRstNode, result: var string) = result.add("\n\n") result.add(ind) renderRstSons(d, n, result) - of rnBulletItem: + of rnBulletItem: inc(d.indent, 2) var tmp = "" renderRstSons(d, n, tmp) - if tmp.len > 0: + if tmp.len > 0: result.add("\n") result.add(ind) result.add("* ") @@ -163,22 +164,22 @@ proc renderRstToRst(d: var TRenderContext, n: PRstNode, result: var string) = inc(d.indent, 4) var tmp = "" renderRstSons(d, n, tmp) - if tmp.len > 0: + if tmp.len > 0: result.add("\n") result.add(ind) result.add("(#) ") result.add(tmp) dec(d.indent, 4) - of rnOptionList, rnFieldList, rnDefList, rnDefItem, rnLineBlock, rnFieldName, - rnFieldBody, rnStandaloneHyperlink, rnBulletList, rnEnumList: + of rnOptionList, rnFieldList, rnDefList, rnDefItem, rnLineBlock, rnFieldName, + rnFieldBody, rnStandaloneHyperlink, rnBulletList, rnEnumList: renderRstSons(d, n, result) - of rnDefName: + of rnDefName: result.add("\n\n") result.add(ind) renderRstSons(d, n, result) of rnDefBody: inc(d.indent, 2) - if n.sons[0].kind != rnBulletList: + if n.sons[0].kind != rnBulletList: result.add("\n") result.add(ind) result.add(" ") @@ -187,10 +188,10 @@ proc renderRstToRst(d: var TRenderContext, n: PRstNode, result: var string) = of rnField: var tmp = "" renderRstToRst(d, n.sons[0], tmp) - + var L = max(tmp.len + 3, 30) inc(d.indent, L) - + result.add "\n" result.add ind result.add ':' @@ -198,9 +199,9 @@ proc renderRstToRst(d: var TRenderContext, n: PRstNode, result: var string) = result.add ':' result.add spaces(L - tmp.len - 2) renderRstToRst(d, n.sons[1], result) - + dec(d.indent, L) - of rnLineBlockItem: + of rnLineBlockItem: result.add("\n") result.add(ind) result.add("| ") @@ -209,11 +210,11 @@ proc renderRstToRst(d: var TRenderContext, n: PRstNode, result: var string) = inc(d.indent, 2) renderRstSons(d, n, result) dec(d.indent, 2) - of rnRef: + of rnRef: result.add("`") renderRstSons(d, n, result) result.add("`_") - of rnHyperlink: + of rnHyperlink: result.add('`') renderRstToRst(d, n.sons[0], result) result.add(" <") @@ -225,23 +226,23 @@ proc renderRstToRst(d: var TRenderContext, n: PRstNode, result: var string) = result.add("`:") renderRstToRst(d, n.sons[1],result) result.add(':') - of rnSub: + of rnSub: result.add('`') renderRstSons(d, n, result) result.add("`:sub:") - of rnSup: + of rnSup: result.add('`') renderRstSons(d, n, result) result.add("`:sup:") - of rnIdx: + of rnIdx: result.add('`') renderRstSons(d, n, result) result.add("`:idx:") - of rnEmphasis: + of rnEmphasis: result.add("*") renderRstSons(d, n, result) result.add("*") - of rnStrongEmphasis: + of rnStrongEmphasis: result.add("**") renderRstSons(d, n, result) result.add("**") @@ -249,11 +250,11 @@ proc renderRstToRst(d: var TRenderContext, n: PRstNode, result: var string) = result.add("***") renderRstSons(d, n, result) result.add("***") - of rnInterpretedText: + of rnInterpretedText: result.add('`') renderRstSons(d, n, result) result.add('`') - of rnInlineLiteral: + of rnInlineLiteral: inc(d.verbatim) result.add("``") renderRstSons(d, n, result) @@ -266,11 +267,11 @@ proc renderRstToRst(d: var TRenderContext, n: PRstNode, result: var string) = result.add("\\\\") # XXX: escape more special characters! else: result.add(n.text) - of rnIndex: + of rnIndex: result.add("\n\n") result.add(ind) result.add(".. index::\n") - + inc(d.indent, 3) if n.sons[2] != nil: renderRstSons(d, n.sons[2], result) dec(d.indent, 3) @@ -280,7 +281,7 @@ proc renderRstToRst(d: var TRenderContext, n: PRstNode, result: var string) = result.add(".. contents::") else: result.add("Error: cannot render: " & $n.kind) - + proc renderRstToRst*(n: PRstNode, result: var string) = ## renders `n` into its string representation and appends to `result`. var d: TRenderContext @@ -302,7 +303,7 @@ proc renderRstToJsonNode(node: PRstNode): JsonNode = proc renderRstToJson*(node: PRstNode): string = ## Writes the given RST node as JSON that is in the form - ## :: + ## :: ## { ## "kind":string node.kind, ## "text":optional string node.text, diff --git a/lib/packages/docutils/rstgen.nim b/lib/packages/docutils/rstgen.nim index da05be9bf..9e96d8a63 100644 --- a/lib/packages/docutils/rstgen.nim +++ b/lib/packages/docutils/rstgen.nim @@ -34,14 +34,14 @@ type TOutputTarget* = enum ## which document type to generate outHtml, # output is HTML outLatex # output is Latex - - TTocEntry = object + + TTocEntry = object n*: PRstNode refname*, header*: string - TMetaEnum* = enum + TMetaEnum* = enum metaNone, metaTitle, metaSubtitle, metaAuthor, metaVersion - + TRstGenerator* = object of RootObj target*: TOutputTarget config*: StringTableRef @@ -60,7 +60,7 @@ type seenIndexTerms: Table[string, int] ## \ ## Keeps count of same text index terms to generate different identifiers ## for hyperlinks. See renderIndexTerm proc for details. - + PDoc = var TRstGenerator ## Alias to type less. CodeBlockParams = object ## Stores code block params. @@ -136,7 +136,7 @@ proc initRstGenerator*(g: var TRstGenerator, target: TOutputTarget, g.currentSection = "Module " & fileParts.name g.seenIndexTerms = initTable[string, int]() g.msgHandler = msgHandler - + let s = config["split.item.toc"] if s != "": g.splitAfter = parseInt(s) for i in low(g.meta)..high(g.meta): g.meta[i] = "" @@ -147,23 +147,23 @@ proc writeIndexFile*(g: var TRstGenerator, outfile: string) = ## You previously need to add entries to the index with the `setIndexTerm() ## <#setIndexTerm>`_ proc. If the index is empty the file won't be created. if g.theIndex.len > 0: writeFile(outfile, g.theIndex) - -proc addXmlChar(dest: var string, c: char) = + +proc addXmlChar(dest: var string, c: char) = case c of '&': add(dest, "&") of '<': add(dest, "<") of '>': add(dest, ">") of '\"': add(dest, """) else: add(dest, c) - -proc addRtfChar(dest: var string, c: char) = + +proc addRtfChar(dest: var string, c: char) = case c of '{': add(dest, "\\{") of '}': add(dest, "\\}") of '\\': add(dest, "\\\\") else: add(dest, c) - -proc addTexChar(dest: var string, c: char) = + +proc addTexChar(dest: var string, c: char) = case c of '_': add(dest, "\\_") of '{': add(dest, "\\symbol{123}") @@ -183,54 +183,54 @@ proc addTexChar(dest: var string, c: char) = var splitter*: string = "<wbr />" -proc escChar*(target: TOutputTarget, dest: var string, c: char) {.inline.} = +proc escChar*(target: TOutputTarget, dest: var string, c: char) {.inline.} = case target of outHtml: addXmlChar(dest, c) of outLatex: addTexChar(dest, c) - -proc nextSplitPoint*(s: string, start: int): int = + +proc nextSplitPoint*(s: string, start: int): int = result = start - while result < len(s) + 0: + while result < len(s) + 0: case s[result] - of '_': return - of 'a'..'z': - if result + 1 < len(s) + 0: - if s[result + 1] in {'A'..'Z'}: return + of '_': return + of 'a'..'z': + if result + 1 < len(s) + 0: + if s[result + 1] in {'A'..'Z'}: return else: discard inc(result) dec(result) # last valid index - -proc esc*(target: TOutputTarget, s: string, splitAfter = -1): string = + +proc esc*(target: TOutputTarget, s: string, splitAfter = -1): string = result = "" - if splitAfter >= 0: + if splitAfter >= 0: var partLen = 0 var j = 0 - while j < len(s): + while j < len(s): var k = nextSplitPoint(s, j) - if (splitter != " ") or (partLen + k - j + 1 > splitAfter): + if (splitter != " ") or (partLen + k - j + 1 > splitAfter): partLen = 0 add(result, splitter) for i in countup(j, k): escChar(target, result, s[i]) inc(partLen, k - j + 1) j = k + 1 - else: + else: for i in countup(0, len(s) - 1): escChar(target, result, s[i]) proc disp(target: TOutputTarget, xml, tex: string): string = - if target != outLatex: result = xml + if target != outLatex: result = xml else: result = tex - -proc dispF(target: TOutputTarget, xml, tex: string, - args: varargs[string]): string = - if target != outLatex: result = xml % args + +proc dispF(target: TOutputTarget, xml, tex: string, + args: varargs[string]): string = + if target != outLatex: result = xml % args else: result = tex % args - -proc dispA(target: TOutputTarget, dest: var string, + +proc dispA(target: TOutputTarget, dest: var string, xml, tex: string, args: varargs[string]) = if target != outLatex: addf(dest, xml, args) else: addf(dest, tex, args) - + proc `or`(x, y: string): string {.inline.} = result = if x.isNil: y else: x @@ -248,7 +248,7 @@ proc renderRstToOut*(d: var TRstGenerator, n: PRstNode, result: var string) ## renderRstToOut(gen, rst, generatedHTML) ## echo generatedHTML -proc renderAux(d: PDoc, n: PRstNode, result: var string) = +proc renderAux(d: PDoc, n: PRstNode, result: var string) = for i in countup(0, len(n)-1): renderRstToOut(d, n.sons[i], result) proc renderAux(d: PDoc, n: PRstNode, frmtA, frmtB: string, result: var string) = @@ -347,7 +347,7 @@ proc renderIndexTerm*(d: PDoc, n: PRstNode, result: var string) = var term = "" renderAux(d, n, term) setIndexTerm(d, id, term, d.currentSection) - dispA(d.target, result, "<span id=\"$1\">$2</span>", "$2\\label{$1}", + dispA(d.target, result, "<span id=\"$1\">$2</span>", "$2\\label{$1}", [id, term]) type @@ -656,7 +656,7 @@ proc mergeIndexes*(dir: string): string = result.add("<h2>API symbols</h2>\n") result.add(generateSymbolIndex(symbols)) - + # ---------------------------------------------------------------------------- proc stripTOCHTML(s: string): string = @@ -677,7 +677,7 @@ proc stripTOCHTML(s: string): string = result.delete(first, last) first = result.find('<', first) -proc renderHeadline(d: PDoc, n: PRstNode, result: var string) = +proc renderHeadline(d: PDoc, n: PRstNode, result: var string) = var tmp = "" for i in countup(0, len(n) - 1): renderRstToOut(d, n.sons[i], tmp) d.currentSection = tmp @@ -700,9 +700,9 @@ proc renderHeadline(d: PDoc, n: PRstNode, result: var string) = "id=\"$2\" href=\"#$2\">$3</a></h$1>", "\\rsth$4{$3}\\label{$2}\n", [$n.level, d.tocPart[length].refname, tmp, $chr(n.level - 1 + ord('A'))]) else: - dispA(d.target, result, "\n<h$1 id=\"$2\">$3</h$1>", + dispA(d.target, result, "\n<h$1 id=\"$2\">$3</h$1>", "\\rsth$4{$3}\\label{$2}\n", [ - $n.level, refname, tmp, + $n.level, refname, tmp, $chr(n.level - 1 + ord('A'))]) # Generate index entry using spaces to indicate TOC level for the output HTML. @@ -710,7 +710,7 @@ proc renderHeadline(d: PDoc, n: PRstNode, result: var string) = setIndexTerm(d, refname, tmp.stripTOCHTML, spaces(max(0, n.level)) & tmp) -proc renderOverline(d: PDoc, n: PRstNode, result: var string) = +proc renderOverline(d: PDoc, n: PRstNode, result: var string) = if d.meta[metaTitle].len == 0: for i in countup(0, len(n)-1): renderRstToOut(d, n.sons[i], d.meta[metaTitle]) @@ -723,14 +723,14 @@ proc renderOverline(d: PDoc, n: PRstNode, result: var string) = var tmp = "" for i in countup(0, len(n) - 1): renderRstToOut(d, n.sons[i], tmp) d.currentSection = tmp - dispA(d.target, result, "<h$1 id=\"$2\"><center>$3</center></h$1>", + dispA(d.target, result, "<h$1 id=\"$2\"><center>$3</center></h$1>", "\\rstov$4{$3}\\label{$2}\n", [$n.level, rstnodeToRefname(n), tmp, $chr(n.level - 1 + ord('A'))]) - -proc renderTocEntry(d: PDoc, e: TTocEntry, result: var string) = + +proc renderTocEntry(d: PDoc, e: TTocEntry, result: var string) = dispA(d.target, result, - "<li><a class=\"reference\" id=\"$1_toc\" href=\"#$1\">$2</a></li>\n", + "<li><a class=\"reference\" id=\"$1_toc\" href=\"#$1\">$2</a></li>\n", "\\item\\label{$1_toc} $2\\ref{$1}\n", [e.refname, e.header]) proc renderTocEntries*(d: var TRstGenerator, j: var int, lvl: int, @@ -759,33 +759,33 @@ proc renderImage(d: PDoc, n: PRstNode, result: var string) = var options = "" var s = getFieldValue(n, "scale") if s.valid: dispA(d.target, options, " scale=\"$1\"", " scale=$1", [strip(s)]) - + s = getFieldValue(n, "height") if s.valid: dispA(d.target, options, " height=\"$1\"", " height=$1", [strip(s)]) - + s = getFieldValue(n, "width") if s.valid: dispA(d.target, options, " width=\"$1\"", " width=$1", [strip(s)]) - + s = getFieldValue(n, "alt") if s.valid: dispA(d.target, options, " alt=\"$1\"", "", [strip(s)]) - + s = getFieldValue(n, "align") if s.valid: dispA(d.target, options, " align=\"$1\"", "", [strip(s)]) - + if options.len > 0: options = dispF(d.target, "$1", "[$1]", [options]) let arg = getArgument(n) if arg.valid: - dispA(d.target, result, "<img src=\"$1\"$2 />", "\\includegraphics$2{$1}", + dispA(d.target, result, "<img src=\"$1\"$2 />", "\\includegraphics$2{$1}", [arg, options]) if len(n) >= 3: renderRstToOut(d, n.sons[2], result) - + proc renderSmiley(d: PDoc, n: PRstNode, result: var string) = dispA(d.target, result, - """<img src="$1" width="15" + """<img src="$1" width="15" height="17" hspace="2" vspace="2" class="smiley" />""", "\\includegraphics{$1}", [d.config["doc.smiley_format"] % n.text]) - + proc parseCodeBlockField(d: PDoc, n: PRstNode, params: var CodeBlockParams) = ## Parses useful fields which can appear before a code block. ## @@ -880,11 +880,11 @@ proc renderCodeBlock(d: PDoc, n: PRstNode, result: var string) = else: var g: TGeneralTokenizer initGeneralTokenizer(g, m.text) - while true: + while true: getNextToken(g, params.lang) case g.kind - of gtEof: break - of gtNone, gtWhitespace: + of gtEof: break + of gtNone, gtWhitespace: add(result, substr(m.text, g.start, g.length + g.start - 1)) else: dispA(d.target, result, "<span class=\"$2\">$1</span>", "\\span$2{$1}", [ @@ -893,36 +893,36 @@ proc renderCodeBlock(d: PDoc, n: PRstNode, result: var string) = deinitGeneralTokenizer(g) dispA(d.target, result, blockEnd, "\n\\end{rstpre}\n") -proc renderContainer(d: PDoc, n: PRstNode, result: var string) = +proc renderContainer(d: PDoc, n: PRstNode, result: var string) = var tmp = "" renderRstToOut(d, n.sons[2], tmp) var arg = strip(getArgument(n)) - if arg == "": + if arg == "": dispA(d.target, result, "<div>$1</div>", "$1", [tmp]) else: dispA(d.target, result, "<div class=\"$1\">$2</div>", "$2", [arg, tmp]) - -proc texColumns(n: PRstNode): string = + +proc texColumns(n: PRstNode): string = result = "" for i in countup(1, len(n)): add(result, "|X") - -proc renderField(d: PDoc, n: PRstNode, result: var string) = + +proc renderField(d: PDoc, n: PRstNode, result: var string) = var b = false - if d.target == outLatex: + if d.target == outLatex: var fieldname = addNodes(n.sons[0]) var fieldval = esc(d.target, strip(addNodes(n.sons[1]))) - if cmpIgnoreStyle(fieldname, "author") == 0 or + if cmpIgnoreStyle(fieldname, "author") == 0 or cmpIgnoreStyle(fieldname, "authors") == 0: if d.meta[metaAuthor].len == 0: d.meta[metaAuthor] = fieldval b = true - elif cmpIgnoreStyle(fieldname, "version") == 0: + elif cmpIgnoreStyle(fieldname, "version") == 0: if d.meta[metaVersion].len == 0: d.meta[metaVersion] = fieldval b = true if not b: renderAux(d, n, "<tr>$1</tr>\n", "$1", result) - + proc renderRstToOut(d: PDoc, n: PRstNode, result: var string) = if n == nil: return case n.kind @@ -947,54 +947,54 @@ proc renderRstToOut(d: PDoc, n: PRstNode, result: var string) = of rnDefBody: renderAux(d, n, "<dd>$1</dd>\n", "$1\n", result) of rnFieldList: var tmp = "" - for i in countup(0, len(n) - 1): + for i in countup(0, len(n) - 1): renderRstToOut(d, n.sons[i], tmp) - if tmp.len != 0: + if tmp.len != 0: dispA(d.target, result, "<table class=\"docinfo\" frame=\"void\" rules=\"none\">" & "<col class=\"docinfo-name\" />" & - "<col class=\"docinfo-content\" />" & + "<col class=\"docinfo-content\" />" & "<tbody valign=\"top\">$1" & - "</tbody></table>", - "\\begin{description}$1\\end{description}\n", + "</tbody></table>", + "\\begin{description}$1\\end{description}\n", [tmp]) of rnField: renderField(d, n, result) - of rnFieldName: + of rnFieldName: renderAux(d, n, "<th class=\"docinfo-name\">$1:</th>", "\\item[$1:]", result) - of rnFieldBody: + of rnFieldBody: renderAux(d, n, "<td>$1</td>", " $1\n", result) - of rnIndex: + of rnIndex: renderRstToOut(d, n.sons[2], result) - of rnOptionList: - renderAux(d, n, "<table frame=\"void\">$1</table>", + of rnOptionList: + renderAux(d, n, "<table frame=\"void\">$1</table>", "\\begin{description}\n$1\\end{description}\n", result) - of rnOptionListItem: + of rnOptionListItem: renderAux(d, n, "<tr>$1</tr>\n", "$1", result) - of rnOptionGroup: + of rnOptionGroup: renderAux(d, n, "<th align=\"left\">$1</th>", "\\item[$1]", result) - of rnDescription: + of rnDescription: renderAux(d, n, "<td align=\"left\">$1</td>\n", " $1\n", result) - of rnOption, rnOptionString, rnOptionArgument: + of rnOption, rnOptionString, rnOptionArgument: doAssert false, "renderRstToOut" of rnLiteralBlock: - renderAux(d, n, "<pre>$1</pre>\n", + renderAux(d, n, "<pre>$1</pre>\n", "\\begin{rstpre}\n$1\n\\end{rstpre}\n", result) - of rnQuotedLiteralBlock: + of rnQuotedLiteralBlock: doAssert false, "renderRstToOut" - of rnLineBlock: + of rnLineBlock: renderAux(d, n, "<p>$1</p>", "$1\n\n", result) - of rnLineBlockItem: + of rnLineBlockItem: renderAux(d, n, "$1<br />", "$1\\\\\n", result) - of rnBlockQuote: - renderAux(d, n, "<blockquote><p>$1</p></blockquote>\n", + of rnBlockQuote: + renderAux(d, n, "<blockquote><p>$1</p></blockquote>\n", "\\begin{quote}$1\\end{quote}\n", result) - of rnTable, rnGridTable: - renderAux(d, n, - "<table border=\"1\" class=\"docutils\">$1</table>", + of rnTable, rnGridTable: + renderAux(d, n, + "<table border=\"1\" class=\"docutils\">$1</table>", "\\begin{table}\\begin{rsttab}{" & texColumns(n) & "|}\n\\hline\n$1\\end{rsttab}\\end{table}", result) - of rnTableRow: + of rnTableRow: if len(n) >= 1: if d.target == outLatex: #var tmp = "" @@ -1007,25 +1007,25 @@ proc renderRstToOut(d: PDoc, n: PRstNode, result: var string) = result.add("<tr>") renderAux(d, n, result) result.add("</tr>\n") - of rnTableDataCell: + of rnTableDataCell: renderAux(d, n, "<td>$1</td>", "$1", result) - of rnTableHeaderCell: + of rnTableHeaderCell: renderAux(d, n, "<th>$1</th>", "\\textbf{$1}", result) - of rnLabel: + of rnLabel: doAssert false, "renderRstToOut" # used for footnotes and other - of rnFootnote: + of rnFootnote: doAssert false, "renderRstToOut" # a footnote - of rnCitation: + of rnCitation: doAssert false, "renderRstToOut" # similar to footnote - of rnRef: + of rnRef: var tmp = "" renderAux(d, n, tmp) dispA(d.target, result, "<a class=\"reference external\" href=\"#$2\">$1</a>", "$1\\ref{$2}", [tmp, rstnodeToRefname(n)]) - of rnStandaloneHyperlink: - renderAux(d, n, - "<a class=\"reference external\" href=\"$1\">$1</a>", + of rnStandaloneHyperlink: + renderAux(d, n, + "<a class=\"reference external\" href=\"$1\">$1</a>", "\\href{$1}{$1}", result) of rnHyperlink: var tmp0 = "" @@ -1042,11 +1042,11 @@ proc renderRstToOut(d: PDoc, n: PRstNode, result: var string) = of rnRawLatex: if d.target == outLatex: result.add addNodes(lastSon(n)) - + of rnImage, rnFigure: renderImage(d, n, result) of rnCodeBlock: renderCodeBlock(d, n, result) of rnContainer: renderContainer(d, n, result) - of rnSubstitutionReferences, rnSubstitutionDef: + of rnSubstitutionReferences, rnSubstitutionDef: renderAux(d, n, "|$1|", "|$1|", result) of rnDirective: renderAux(d, n, "", "", result) @@ -1063,15 +1063,15 @@ proc renderRstToOut(d: PDoc, n: PRstNode, result: var string) = of rnStrongEmphasis: renderAux(d, n, "<strong>$1</strong>", "\\textbf{$1}", result) of rnTripleEmphasis: - renderAux(d, n, "<strong><em>$1</em></strong>", + renderAux(d, n, "<strong><em>$1</em></strong>", "\\textbf{emph{$1}}", result) of rnInterpretedText: renderAux(d, n, "<cite>$1</cite>", "\\emph{$1}", result) of rnIdx: renderIndexTerm(d, n, result) - of rnInlineLiteral: - renderAux(d, n, - "<tt class=\"docutils literal\"><span class=\"pre\">$1</span></tt>", + of rnInlineLiteral: + renderAux(d, n, + "<tt class=\"docutils literal\"><span class=\"pre\">$1</span></tt>", "\\texttt{$1}", result) of rnSmiley: renderSmiley(d, n, result) of rnLeaf: result.add(esc(d.target, n.text)) @@ -1082,55 +1082,55 @@ proc renderRstToOut(d: PDoc, n: PRstNode, result: var string) = # ----------------------------------------------------------------------------- -proc getVarIdx(varnames: openArray[string], id: string): int = - for i in countup(0, high(varnames)): - if cmpIgnoreStyle(varnames[i], id) == 0: +proc getVarIdx(varnames: openArray[string], id: string): int = + for i in countup(0, high(varnames)): + if cmpIgnoreStyle(varnames[i], id) == 0: return i result = -1 -proc formatNamedVars*(frmt: string, varnames: openArray[string], - varvalues: openArray[string]): string = +proc formatNamedVars*(frmt: string, varnames: openArray[string], + varvalues: openArray[string]): string = var i = 0 var L = len(frmt) result = "" var num = 0 - while i < L: - if frmt[i] == '$': + while i < L: + if frmt[i] == '$': inc(i) # skip '$' case frmt[i] - of '#': + of '#': add(result, varvalues[num]) inc(num) inc(i) - of '$': + of '$': add(result, "$") inc(i) - of '0'..'9': + of '0'..'9': var j = 0 - while true: + while true: j = (j * 10) + ord(frmt[i]) - ord('0') inc(i) - if i > L-1 or frmt[i] notin {'0'..'9'}: break + if i > L-1 or frmt[i] notin {'0'..'9'}: break if j > high(varvalues) + 1: raise newException(ValueError, "invalid index: " & $j) num = j add(result, varvalues[j - 1]) - of 'A'..'Z', 'a'..'z', '\x80'..'\xFF': + of 'A'..'Z', 'a'..'z', '\x80'..'\xFF': var id = "" - while true: + while true: add(id, frmt[i]) inc(i) - if frmt[i] notin {'A'..'Z', '_', 'a'..'z', '\x80'..'\xFF'}: break + if frmt[i] notin {'A'..'Z', '_', 'a'..'z', '\x80'..'\xFF'}: break var idx = getVarIdx(varnames, id) - if idx >= 0: + if idx >= 0: add(result, varvalues[idx]) else: raise newException(ValueError, "unknown substitution var: " & id) - of '{': + of '{': var id = "" inc(i) - while frmt[i] != '}': - if frmt[i] == '\0': + while frmt[i] != '}': + if frmt[i] == '\0': raise newException(ValueError, "'}' expected") add(id, frmt[i]) inc(i) @@ -1138,12 +1138,12 @@ proc formatNamedVars*(frmt: string, varnames: openArray[string], # search for the variable: var idx = getVarIdx(varnames, id) if idx >= 0: add(result, varvalues[idx]) - else: + else: raise newException(ValueError, "unknown substitution var: " & id) else: raise newException(ValueError, "unknown substitution: $" & $frmt[i]) var start = i - while i < L: + while i < L: if frmt[i] != '$': inc(i) else: break if i-1 >= start: add(result, substr(frmt, start, i - 1)) @@ -1163,10 +1163,10 @@ proc defaultConfig*(): StringTableRef = ## pages, while this proc returns just the content for procs like ## ``rstToHtml`` to generate the bare minimum HTML. result = newStringTable(modeStyleInsensitive) - + template setConfigVar(key, val: expr) = result[key] = val - + # If you need to modify these values, it might be worth updating the template # file in config/nimdoc.cfg. setConfigVar("split.item.toc", "20") @@ -1214,7 +1214,7 @@ $content # ---------- forum --------------------------------------------------------- -proc rstToHtml*(s: string, options: TRstParseOptions, +proc rstToHtml*(s: string, options: TRstParseOptions, config: StringTableRef): string = ## Converts an input rst string into embeddable HTML. ## @@ -1236,13 +1236,13 @@ proc rstToHtml*(s: string, options: TRstParseOptions, ## output you have to create your own ``TRstGenerator`` with ## ``initRstGenerator`` and related procs. - proc myFindFile(filename: string): string = + proc myFindFile(filename: string): string = # we don't find any files in online mode: result = "" const filen = "input" var d: TRstGenerator - initRstGenerator(d, outHtml, config, filen, options, myFindFile, + initRstGenerator(d, outHtml, config, filen, options, myFindFile, rst.defaultMsgHandler) var dummyHasToc = false var rst = rstParse(s, filen, 0, 1, dummyHasToc, options) @@ -1251,5 +1251,6 @@ proc rstToHtml*(s: string, options: TRstParseOptions, when isMainModule: - echo rstToHtml("*Hello* **world**!", {}, - newStringTable(modeStyleInsensitive)) + assert rstToHtml("*Hello* **world**!", {}, + newStringTable(modeStyleInsensitive)) == + "<em>Hello</em> <strong>world</strong>!" diff --git a/lib/posix/posix.nim b/lib/posix/posix.nim index 00b655a10..0c7b84090 100644 --- a/lib/posix/posix.nim +++ b/lib/posix/posix.nim @@ -95,10 +95,11 @@ type header: "<dirent.h>", final, pure.} = object ## dirent_t struct d_ino*: Tino ## File serial number. 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_type*: int8 ## Type of file; not supported by all filesystem types. ## (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. Tflock* {.importc: "struct flock", final, pure, @@ -1579,8 +1580,11 @@ else: when defined(macosx): # 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 + var + SO_NOSIGPIPE* {.importc, header: "<sys/socket.h>".}: cint else: var MSG_NOSIGNAL* {.importc, header: "<sys/socket.h>".}: cint diff --git a/lib/posix/termios.nim b/lib/posix/termios.nim index 830e8a207..710b2fa6b 100644 --- a/lib/posix/termios.nim +++ b/lib/posix/termios.nim @@ -10,28 +10,25 @@ {.deadCodeElim: on.} import posix -type +type Speed* = cuint Tcflag* = cuint -const +const NCCS* = 32 -type - Termios* = object {.importc: "struct termios", header: "<termios.h>", final, pure.} - iflag*: Tcflag # input mode flags - oflag*: Tcflag # output mode flags - cflag*: Tcflag # control mode flags - lflag*: Tcflag # local mode flags - line*: cuchar # line discipline - cc*: array[NCCS, cuchar] # control characters - ispeed*: Speed # input speed - ospeed*: Speed # output speed - - -# cc characters - -const +type + Termios* {.importc: "struct termios", header: "<termios.h>".} = object + c_iflag*: Tcflag # input mode flags + c_oflag*: Tcflag # output mode flags + c_cflag*: Tcflag # control mode flags + c_lflag*: Tcflag # local mode flags + c_line*: cuchar # line discipline + c_cc*: array[NCCS, cuchar] # control characters + +# cc characters + +const VINTR* = 0 VQUIT* = 1 VERASE* = 2 @@ -50,9 +47,9 @@ const VLNEXT* = 15 VEOL2* = 16 -# iflag bits +# iflag bits -const +const IGNBRK* = 1 BRKINT* = 2 IGNPAR* = 4 @@ -69,9 +66,9 @@ const IMAXBEL* = 20000 IUTF8* = 40000 -# oflag bits +# oflag bits -const +const OPOST* = 1 OLCUC* = 2 ONLCR* = 4 @@ -104,9 +101,9 @@ const VT1* = 40000 XTABS* = 14000 -# cflag bit meaning +# cflag bit meaning -const +const CBAUD* = 10017 B0* = 0 B50* = 1 @@ -158,9 +155,9 @@ const CMSPAR* = 0o010000000000 CRTSCTS* = 0o020000000000 -# lflag bits +# lflag bits -const +const ISIG* = 1 ICANON* = 2 XCASE* = 4 @@ -178,87 +175,87 @@ const IEXTEN* = 0o000000100000 EXTPROC* = 0o000000200000 -# tcflow() and TCXONC use these +# tcflow() and TCXONC use these -const +const TCOOFF* = 0 TCOON* = 1 TCIOFF* = 2 TCION* = 3 -# tcflush() and TCFLSH use these +# tcflush() and TCFLSH use these -const +const TCIFLUSH* = 0 TCOFLUSH* = 1 TCIOFLUSH* = 2 -# tcsetattr uses these +# tcsetattr uses these -const +const TCSANOW* = 0 TCSADRAIN* = 1 TCSAFLUSH* = 2 # Compare a character C to a value VAL from the `cc' array in a -# `struct termios'. If VAL is _POSIX_VDISABLE, no character can match it. +# `struct termios'. If VAL is _POSIX_VDISABLE, no character can match it. -template cceq*(val, c: expr): expr = +template cceq*(val, c: expr): expr = c == val and val != POSIX_VDISABLE -# Return the output baud rate stored in *TERMIOS_P. +# Return the output baud rate stored in *TERMIOS_P. -proc cfGetOspeed*(termios: ptr Termios): Speed {.importc: "cfgetospeed", +proc cfGetOspeed*(termios: ptr Termios): Speed {.importc: "cfgetospeed", header: "<termios.h>".} -# Return the input baud rate stored in *TERMIOS_P. +# Return the input baud rate stored in *TERMIOS_P. -proc cfGetIspeed*(termios: ptr Termios): Speed {.importc: "cfgetispeed", +proc cfGetIspeed*(termios: ptr Termios): Speed {.importc: "cfgetispeed", header: "<termios.h>".} -# Set the output baud rate stored in *TERMIOS_P to SPEED. +# Set the output baud rate stored in *TERMIOS_P to SPEED. proc cfSetOspeed*(termios: ptr Termios; speed: Speed): cint {. importc: "cfsetospeed", header: "<termios.h>".} -# Set the input baud rate stored in *TERMIOS_P to SPEED. +# Set the input baud rate stored in *TERMIOS_P to SPEED. proc cfSetIspeed*(termios: ptr Termios; speed: Speed): cint {. importc: "cfsetispeed", header: "<termios.h>".} -# Set both the input and output baud rates in *TERMIOS_OP to SPEED. +# Set both the input and output baud rates in *TERMIOS_OP to SPEED. proc cfSetSpeed*(termios: ptr Termios; speed: Speed): cint {. importc: "cfsetspeed", header: "<termios.h>".} -# Put the state of FD into *TERMIOS_P. +# Put the state of FD into *TERMIOS_P. proc tcGetAttr*(fd: cint; termios: ptr Termios): cint {. importc: "tcgetattr", header: "<termios.h>".} # Set the state of FD to *TERMIOS_P. -# Values for OPTIONAL_ACTIONS (TCSA*) are in <bits/termios.h>. +# Values for OPTIONAL_ACTIONS (TCSA*) are in <bits/termios.h>. proc tcSetAttr*(fd: cint; optional_actions: cint; termios: ptr Termios): cint {. importc: "tcsetattr", header: "<termios.h>".} -# Set *TERMIOS_P to indicate raw mode. +# Set *TERMIOS_P to indicate raw mode. -proc cfMakeRaw*(termios: ptr Termios) {.importc: "cfmakeraw", +proc cfMakeRaw*(termios: ptr Termios) {.importc: "cfmakeraw", header: "<termios.h>".} -# Send zero bits on FD. +# Send zero bits on FD. -proc tcSendBreak*(fd: cint; duration: cint): cint {.importc: "tcsendbreak", +proc tcSendBreak*(fd: cint; duration: cint): cint {.importc: "tcsendbreak", header: "<termios.h>".} # Wait for pending output to be written on FD. # # This function is a cancellation point and therefore not marked with -# . +# . proc tcDrain*(fd: cint): cint {.importc: "tcdrain", header: "<termios.h>".} # Flush pending data on FD. -# Values for QUEUE_SELECTOR (TC{I,O,IO}FLUSH) are in <bits/termios.h>. +# Values for QUEUE_SELECTOR (TC{I,O,IO}FLUSH) are in <bits/termios.h>. -proc tcFlush*(fd: cint; queue_selector: cint): cint {.importc: "tcflush", +proc tcFlush*(fd: cint; queue_selector: cint): cint {.importc: "tcflush", header: "<termios.h>".} # Suspend or restart transmission on FD. -# Values for ACTION (TC[IO]{OFF,ON}) are in <bits/termios.h>. +# Values for ACTION (TC[IO]{OFF,ON}) are in <bits/termios.h>. -proc tcFlow*(fd: cint; action: cint): cint {.importc: "tcflow", +proc tcFlow*(fd: cint; action: cint): cint {.importc: "tcflow", header: "<termios.h>".} -# Get process group ID for session leader for controlling terminal FD. +# Get process group ID for session leader for controlling terminal FD. proc tcGetSid*(fd: cint): TPid {.importc: "tcgetsid", header: "<termios.h>".} diff --git a/lib/pure/actors.nim b/lib/pure/actors.nim index 8c61ce7df..294c24741 100644 --- a/lib/pure/actors.nim +++ b/lib/pure/actors.nim @@ -221,7 +221,7 @@ proc spawn*[TIn](p: var TActorPool[TIn, void], input: TIn, setupTask() schedule() -when isMainModule: +when not defined(testing) and isMainModule: var a: TActorPool[int, void] createActorPool(a) diff --git a/lib/pure/algorithm.nim b/lib/pure/algorithm.nim index a1ab7be13..f7ccb9234 100644 --- a/lib/pure/algorithm.nim +++ b/lib/pure/algorithm.nim @@ -24,7 +24,7 @@ proc `*`*(x: int, order: SortOrder): int {.inline.} = var y = order.ord - 1 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]``. var x = first var y = last @@ -37,11 +37,11 @@ proc reverse*[T](a: var openArray[T]) = ## reverses the array `a`. 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]`. result = newSeq[T](last - first + 1) - var x = first - var y = last + var x = first.int + var y = last.int while x <= last: result[x] = a[y] dec(y) diff --git a/lib/pure/asyncdispatch.nim b/lib/pure/asyncdispatch.nim index 8e0ac8d21..a4d7a1632 100644 --- a/lib/pure/asyncdispatch.nim +++ b/lib/pure/asyncdispatch.nim @@ -634,6 +634,93 @@ when defined(windows) or defined(nimdoc): # free ``ol``. 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, flags = {SocketFlag.SafeDisconn}): Future[void] = ## 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 = result = newRawSocket(domain, typ, protocol).TAsyncFD result.SocketHandle.setBlocking(false) + when defined(macosx): + result.SocketHandle.setSockOptInt(SOL_SOCKET, SO_NOSIGPIPE, 1) register(result) proc newAsyncRawSocket*(domain: Domain = AF_INET, @@ -848,6 +937,8 @@ else: protocol: Protocol = IPPROTO_TCP): TAsyncFD = result = newRawSocket(domain, typ, protocol).TAsyncFD result.SocketHandle.setBlocking(false) + when defined(macosx): + result.SocketHandle.setSockOptInt(SOL_SOCKET, SO_NOSIGPIPE, 1) register(result) proc closeSocket*(sock: TAsyncFD) = @@ -959,7 +1050,6 @@ else: result = true let res = recv(sock.SocketHandle, addr readBuffer[0], size.cint, flags.toOSFlags()) - #echo("recv cb res: ", res) if res < 0: let lastError = osLastError() if lastError.int32 notin {EINTR, EWOULDBLOCK, EAGAIN}: @@ -980,6 +1070,30 @@ else: addRead(socket, cb) 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, flags = {SocketFlag.SafeDisconn}): Future[void] = var retFuture = newFuture[void]("send") @@ -1273,7 +1387,7 @@ proc processBody(node, retFutureSym: NimNode, else: discard 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.} = case node.kind @@ -1378,8 +1492,8 @@ macro async*(prc: stmt): stmt {.immediate.} = result[6] = outerProcBody #echo(treeRepr(result)) - if prc[0].getName == "test3": - echo(toStrLit(result)) + #if prc[0].getName == "test": + # echo(toStrLit(result)) proc recvLine*(socket: TAsyncFD): Future[string] {.async.} = ## Reads a line of data from ``socket``. Returned future will complete once diff --git a/lib/pure/asyncftpclient.nim b/lib/pure/asyncftpclient.nim index 96f54b49e..daf69d59f 100644 --- a/lib/pure/asyncftpclient.nim +++ b/lib/pure/asyncftpclient.nim @@ -300,7 +300,7 @@ proc newAsyncFtpClient*(address: string, port = Port(21), result.dsockConnected = false result.csock = newAsyncSocket() -when isMainModule: +when not defined(testing) and isMainModule: var ftp = newAsyncFtpClient("example.com", user = "test", pass = "test") proc main(ftp: AsyncFtpClient) {.async.} = await ftp.connect() diff --git a/lib/pure/asynchttpserver.nim b/lib/pure/asynchttpserver.nim index 64242234c..279cedb5d 100644 --- a/lib/pure/asynchttpserver.nim +++ b/lib/pure/asynchttpserver.nim @@ -23,8 +23,7 @@ ## proc cb(req: Request) {.async.} = ## await req.respond(Http200, "Hello World") ## -## asyncCheck server.serve(Port(8080), cb) -## runForever() +## waitFor server.serve(Port(8080), cb) import strtabs, asyncnet, asyncdispatch, parseutils, uri, strutils type @@ -109,22 +108,19 @@ proc sendHeaders*(req: Request, headers: StringTableRef): Future[void] = addHeaders(msg, headers) return req.client.send(msg) -proc respond*(req: Request, code: HttpCode, - content: string, headers = newStringTable()) {.async.} = +proc respond*(req: Request, code: HttpCode, content: string, + headers: StringTableRef = nil): Future[void] = ## Responds to the request with the specified ``HttpCode``, headers and ## content. ## ## This procedure will **not** close the client socket. - var customHeaders = headers - customHeaders["Content-Length"] = $content.len var msg = "HTTP/1.1 " & $code & "\c\L" - msg.addHeaders(customHeaders) - await req.client.send(msg & "\c\L" & content) -proc newRequest(): Request = - result.headers = newStringTable(modeCaseInsensitive) - result.hostname = "" - result.body = "" + if headers != nil: + msg.addHeaders(headers) + msg.add("Content-Length: " & $content.len & "\c\L\c\L") + msg.add(content) + result = req.client.send(msg) proc parseHeader(line: string): tuple[key, value: string] = var i = 0 @@ -149,59 +145,65 @@ proc sendStatus(client: AsyncSocket, status: string): Future[void] = proc processClient(client: AsyncSocket, address: string, callback: proc (request: Request): Future[void] {.closure, gcsafe.}) {.async.} = + var request: Request + request.url = initUri() + request.headers = newStringTable(modeCaseInsensitive) + var line = newStringOfCap(80) + var key, value = "" + while not client.isClosed: # GET /path HTTP/1.1 # Header: val # \n - var request = newRequest() - request.hostname = address + request.headers.clear(modeCaseInsensitive) + request.hostname.shallowCopy(address) assert client != nil request.client = client # First line - GET /path HTTP/1.1 - let line = await client.recvLine() # TODO: Timeouts. + line.setLen(0) + await client.recvLineInto(addr line) # TODO: Timeouts. if line == "": client.close() return - let lineParts = line.split(' ') - if lineParts.len != 3: - await request.respond(Http400, "Invalid request. Got: " & line) - continue - let reqMethod = lineParts[0] - let path = lineParts[1] - let protocol = lineParts[2] + var i = 0 + for linePart in line.split(' '): + case i + of 0: request.reqMethod.shallowCopy(linePart.normalize) + of 1: parseUri(linePart, request.url) + of 2: + try: + request.protocol = parseProtocol(linePart) + except ValueError: + asyncCheck request.respond(Http400, + "Invalid request protocol. Got: " & linePart) + continue + else: + await request.respond(Http400, "Invalid request. Got: " & line) + continue + inc i # Headers - var i = 0 while true: i = 0 - let headerLine = await client.recvLine() - if headerLine == "": - client.close(); return - if headerLine == "\c\L": break - # TODO: Compiler crash - #let (key, value) = parseHeader(headerLine) - let kv = parseHeader(headerLine) - request.headers[kv.key] = kv.value + line.setLen(0) + await client.recvLineInto(addr line) - request.reqMethod = reqMethod - request.url = parseUri(path) - try: - request.protocol = protocol.parseProtocol() - except ValueError: - asyncCheck request.respond(Http400, "Invalid request protocol. Got: " & - protocol) - continue + if line == "": + client.close(); return + if line == "\c\L": break + let (key, value) = parseHeader(line) + request.headers[key] = value - if reqMethod.normalize == "post": + if request.reqMethod == "post": # Check for Expect header if request.headers.hasKey("Expect"): if request.headers["Expect"].toLower == "100-continue": await client.sendStatus("100 Continue") else: await client.sendStatus("417 Expectation Failed") - + # Read the body # - Check for Content-length header if request.headers.hasKey("Content-Length"): @@ -215,11 +217,11 @@ proc processClient(client: AsyncSocket, address: string, await request.respond(Http400, "Bad Request. No Content-Length.") continue - case reqMethod.normalize + case request.reqMethod of "get", "post", "head", "put", "delete", "trace", "options", "connect", "patch": await callback(request) else: - await request.respond(Http400, "Invalid request method. Got: " & reqMethod) + await request.respond(Http400, "Invalid request method. Got: " & request.reqMethod) # Persistent connections if (request.protocol == HttpVer11 and @@ -247,7 +249,7 @@ proc serve*(server: AsyncHttpServer, port: Port, server.socket.setSockOpt(OptReuseAddr, true) server.socket.bindAddr(port, address) server.socket.listen() - + while true: # TODO: Causes compiler crash. #var (address, client) = await server.socket.acceptAddr() @@ -260,7 +262,7 @@ proc close*(server: AsyncHttpServer) = ## Terminates the async http server instance. server.socket.close() -when isMainModule: +when not defined(testing) and isMainModule: proc main = var server = newAsyncHttpServer() proc cb(req: Request) {.async.} = diff --git a/lib/pure/asyncio.nim b/lib/pure/asyncio.nim index f58bb4302..6ae2c608b 100644 --- a/lib/pure/asyncio.nim +++ b/lib/pure/asyncio.nim @@ -660,7 +660,7 @@ proc len*(disp: Dispatcher): int = ## Retrieves the amount of delegates in ``disp``. return disp.delegates.len -when isMainModule: +when not defined(testing) and isMainModule: proc testConnect(s: AsyncSocket, no: int) = echo("Connected! " & $no) diff --git a/lib/pure/asyncnet.nim b/lib/pure/asyncnet.nim index e7325e0d7..aadbde824 100644 --- a/lib/pure/asyncnet.nim +++ b/lib/pure/asyncnet.nim @@ -24,7 +24,7 @@ ## ## Chat server ## ^^^^^^^^^^^ -## +## ## The following example demonstrates a simple chat server. ## ## .. code-block::nim @@ -182,26 +182,30 @@ proc connect*(socket: AsyncSocket, address: string, port: Port, sslSetConnectState(socket.sslHandle) sslLoop(socket, flags, sslDoHandshake(socket.sslHandle)) -proc readInto(buf: cstring, size: int, socket: AsyncSocket, - flags: set[SocketFlag]): Future[int] {.async.} = +template readInto(buf: cstring, size: int, socket: AsyncSocket, + flags: set[SocketFlag]): int = + ## Reads **up to** ``size`` bytes from ``socket`` into ``buf``. Note that + ## this is a template and not a proc. + var res = 0 if socket.isSsl: when defined(ssl): # SSL mode. sslLoop(socket, flags, sslRead(socket.sslHandle, buf, size.cint)) - result = opResult + res = opResult else: - var data = await recv(socket.fd.TAsyncFD, size, flags) - if data.len != 0: - copyMem(buf, addr data[0], data.len) + var recvIntoFut = recvInto(socket.fd.TAsyncFD, buf, size, flags) + yield recvIntoFut # Not in SSL mode. - result = data.len + res = recvIntoFut.read() + res -proc readIntoBuf(socket: AsyncSocket, - flags: set[SocketFlag]): Future[int] {.async.} = - result = await readInto(addr socket.buffer[0], BufferSize, socket, flags) +template readIntoBuf(socket: AsyncSocket, + flags: set[SocketFlag]): int = + var size = readInto(addr socket.buffer[0], BufferSize, socket, flags) socket.currPos = 0 - socket.bufLen = result + socket.bufLen = size + size proc recv*(socket: AsyncSocket, size: int, flags = {SocketFlag.SafeDisconn}): Future[string] {.async.} = @@ -222,10 +226,11 @@ proc recv*(socket: AsyncSocket, size: int, ## to be read then the future will complete with a value of ``""``. if socket.isBuffered: result = newString(size) + shallow(result) let originalBufPos = socket.currPos if socket.bufLen == 0: - let res = await socket.readIntoBuf(flags - {SocketFlag.Peek}) + let res = socket.readIntoBuf(flags - {SocketFlag.Peek}) if res == 0: result.setLen(0) return @@ -236,7 +241,7 @@ proc recv*(socket: AsyncSocket, size: int, if SocketFlag.Peek in flags: # We don't want to get another buffer if we're peeking. break - let res = await socket.readIntoBuf(flags - {SocketFlag.Peek}) + let res = socket.readIntoBuf(flags - {SocketFlag.Peek}) if res == 0: break @@ -251,7 +256,7 @@ proc recv*(socket: AsyncSocket, size: int, result.setLen(read) else: result = newString(size) - let read = await readInto(addr result[0], size, socket, flags) + let read = readInto(addr result[0], size, socket, flags) result.setLen(read) proc send*(socket: AsyncSocket, data: string, @@ -302,15 +307,14 @@ proc accept*(socket: AsyncSocket, retFut.complete(future.read.client) return retFut -proc recvLine*(socket: AsyncSocket, - flags = {SocketFlag.SafeDisconn}): Future[string] {.async.} = - ## Reads a line of data from ``socket``. Returned future will complete once - ## a full line is read or an error occurs. +proc recvLineInto*(socket: AsyncSocket, resString: ptr string, + flags = {SocketFlag.SafeDisconn}) {.async.} = + ## Reads a line of data from ``socket`` into ``resString``. ## ## If a full line is read ``\r\L`` is not ## added to ``line``, however if solely ``\r\L`` is read then ``line`` ## will be set to it. - ## + ## ## If the socket is disconnected, ``line`` will be set to ``""``. ## ## If the socket is disconnected in the middle of a line (before ``\r\L`` @@ -318,27 +322,32 @@ proc recvLine*(socket: AsyncSocket, ## The partial line **will be lost**. ## ## **Warning**: The ``Peek`` flag is not yet implemented. - ## - ## **Warning**: ``recvLine`` on unbuffered sockets assumes that the protocol - ## uses ``\r\L`` to delimit a new line. - template addNLIfEmpty(): stmt = - if result.len == 0: - result.add("\c\L") + ## + ## **Warning**: ``recvLineInto`` on unbuffered sockets assumes that the + ## protocol uses ``\r\L`` to delimit a new line. + ## + ## **Warning**: ``recvLineInto`` currently uses a raw pointer to a string for + ## performance reasons. This will likely change soon to use FutureVars. assert SocketFlag.Peek notin flags ## TODO: + result = newFuture[void]("asyncnet.recvLineInto") + + template addNLIfEmpty(): stmt = + if resString[].len == 0: + resString[].add("\c\L") + if socket.isBuffered: - result = "" if socket.bufLen == 0: - let res = await socket.readIntoBuf(flags) + let res = socket.readIntoBuf(flags) if res == 0: return var lastR = false while true: if socket.currPos >= socket.bufLen: - let res = await socket.readIntoBuf(flags) + let res = socket.readIntoBuf(flags) if res == 0: - result = "" - break + resString[].setLen(0) + return case socket.buffer[socket.currPos] of '\r': @@ -353,24 +362,53 @@ proc recvLine*(socket: AsyncSocket, socket.currPos.inc() return else: - result.add socket.buffer[socket.currPos] + resString[].add socket.buffer[socket.currPos] socket.currPos.inc() else: - result = "" var c = "" while true: - c = await recv(socket, 1, flags) + let recvFut = recv(socket, 1, flags) + c = recvFut.read() if c.len == 0: - return "" + resString[].setLen(0) + return if c == "\r": - c = await recv(socket, 1, flags) # Skip \L + let recvFut = recv(socket, 1, flags) # Skip \L + c = recvFut.read() assert c == "\L" addNLIfEmpty() return elif c == "\L": addNLIfEmpty() return - add(result.string, c) + resString[].add c + +proc recvLine*(socket: AsyncSocket, + flags = {SocketFlag.SafeDisconn}): Future[string] {.async.} = + ## Reads a line of data from ``socket``. Returned future will complete once + ## a full line is read or an error occurs. + ## + ## If a full line is read ``\r\L`` is not + ## added to ``line``, however if solely ``\r\L`` is read then ``line`` + ## will be set to it. + ## + ## If the socket is disconnected, ``line`` will be set to ``""``. + ## + ## If the socket is disconnected in the middle of a line (before ``\r\L`` + ## is read) then line will be set to ``""``. + ## The partial line **will be lost**. + ## + ## **Warning**: The ``Peek`` flag is not yet implemented. + ## + ## **Warning**: ``recvLine`` on unbuffered sockets assumes that the protocol + ## uses ``\r\L`` to delimit a new line. + template addNLIfEmpty(): stmt = + if result.len == 0: + result.add("\c\L") + assert SocketFlag.Peek notin flags ## TODO: + + result = "" + await socket.recvLineInto(addr result, flags) proc listen*(socket: AsyncSocket, backlog = SOMAXCONN) {.tags: [ReadIOEffect].} = ## Marks ``socket`` as accepting connections. @@ -458,7 +496,7 @@ proc isClosed*(socket: AsyncSocket): bool = ## Determines whether the socket has been closed. return socket.closed -when isMainModule: +when not defined(testing) and isMainModule: type TestCases = enum HighClient, LowClient, LowServer @@ -500,11 +538,11 @@ when isMainModule: proc (future: Future[void]) = echo("Send") client.close() - + var f = accept(sock) f.callback = onAccept - + var f = accept(sock) f.callback = onAccept runForever() - + diff --git a/lib/pure/basic3d.nim b/lib/pure/basic3d.nim index 18ebed67b..5a943dd05 100644 --- a/lib/pure/basic3d.nim +++ b/lib/pure/basic3d.nim @@ -16,33 +16,33 @@ import times ## Vectors are implemented as direction vectors, ie. when transformed with a matrix ## the translation part of matrix is ignored. The coordinate system used is ## right handed, because its compatible with 2d coordinate system (rotation around -## zaxis equals 2d rotation). +## zaxis equals 2d rotation). ## Operators `+` , `-` , `*` , `/` , `+=` , `-=` , `*=` and `/=` are implemented ## for vectors and scalars. ## ## ## Quick start example: -## +## ## # Create a matrix which first rotates, then scales and at last translates -## +## ## var m:TMatrix3d=rotate(PI,vector3d(1,1,2.5)) & scale(2.0) & move(100.0,200.0,300.0) -## +## ## # Create a 3d point at (100,150,200) and a vector (5,2,3) -## -## var pt:TPoint3d=point3d(100.0,150.0,200.0) -## +## +## var pt:TPoint3d=point3d(100.0,150.0,200.0) +## ## var vec:TVector3d=vector3d(5.0,2.0,3.0) -## -## +## +## ## pt &= m # transforms pt in place -## +## ## var pt2:TPoint3d=pt & m #concatenates pt with m and returns a new point -## +## ## var vec2:TVector3d=vec & m #concatenates vec with m and returns a new vector -type +type TMatrix3d* =object ## Implements a row major 3d matrix, which means ## transformations are applied the order they are concatenated. @@ -53,12 +53,12 @@ type ## [ tx ty tz tw ] ax*,ay*,az*,aw*, bx*,by*,bz*,bw*, cx*,cy*,cz*,cw*, tx*,ty*,tz*,tw*:float TPoint3d* = object - ## Implements a non-homegeneous 2d point stored as + ## Implements a non-homegeneous 2d point stored as ## an `x` , `y` and `z` coordinate. x*,y*,z*:float - TVector3d* = object - ## Implements a 3d **direction vector** stored as - ## an `x` , `y` and `z` coordinate. Direction vector means, + TVector3d* = object + ## Implements a 3d **direction vector** stored as + ## an `x` , `y` and `z` coordinate. Direction vector means, ## that when transforming a vector with a matrix, the translational ## part of the matrix is ignored. x*,y*,z*:float @@ -67,7 +67,7 @@ type # Some forward declarations proc matrix3d*(ax,ay,az,aw,bx,by,bz,bw,cx,cy,cz,cw,tx,ty,tz,tw:float):TMatrix3d {.noInit.} - ## Creates a new 4x4 3d transformation matrix. + ## Creates a new 4x4 3d transformation matrix. ## `ax` , `ay` , `az` is the local x axis. ## `bx` , `by` , `bz` is the local y axis. ## `cx` , `cy` , `cz` is the local z axis. @@ -76,7 +76,7 @@ proc vector3d*(x,y,z:float):TVector3d {.noInit,inline.} ## Returns a new 3d vector (`x`,`y`,`z`) proc point3d*(x,y,z:float):TPoint3d {.noInit,inline.} ## Returns a new 4d point (`x`,`y`,`z`) -proc tryNormalize*(v:var TVector3d):bool +proc tryNormalize*(v:var TVector3d):bool ## Modifies `v` to have a length of 1.0, keeping its angle. ## If `v` has zero length (and thus no angle), it is left unmodified and false is ## returned, otherwise true is returned. @@ -85,7 +85,7 @@ proc tryNormalize*(v:var TVector3d):bool let IDMATRIX*:TMatrix3d=matrix3d( - 1.0,0.0,0.0,0.0, + 1.0,0.0,0.0,0.0, 0.0,1.0,0.0,0.0, 0.0,0.0,1.0,0.0, 0.0,0.0,0.0,1.0) @@ -114,20 +114,20 @@ proc safeArccos(v:float):float= ## due to rounding issues return arccos(clamp(v,-1.0,1.0)) -template makeBinOpVector(s:expr)= +template makeBinOpVector(s:expr)= ## implements binary operators + , - , * and / for vectors - proc s*(a,b:TVector3d):TVector3d {.inline,noInit.} = + proc s*(a,b:TVector3d):TVector3d {.inline,noInit.} = vector3d(s(a.x,b.x),s(a.y,b.y),s(a.z,b.z)) - proc s*(a:TVector3d,b:float):TVector3d {.inline,noInit.} = + proc s*(a:TVector3d,b:float):TVector3d {.inline,noInit.} = vector3d(s(a.x,b),s(a.y,b),s(a.z,b)) - proc s*(a:float,b:TVector3d):TVector3d {.inline,noInit.} = + proc s*(a:float,b:TVector3d):TVector3d {.inline,noInit.} = vector3d(s(a,b.x),s(a,b.y),s(a,b.z)) - -template makeBinOpAssignVector(s:expr)= + +template makeBinOpAssignVector(s:expr)= ## implements inplace binary operators += , -= , /= and *= for vectors - proc s*(a:var TVector3d,b:TVector3d) {.inline.} = + proc s*(a:var TVector3d,b:TVector3d) {.inline.} = s(a.x,b.x) ; s(a.y,b.y) ; s(a.z,b.z) - proc s*(a:var TVector3d,b:float) {.inline.} = + proc s*(a:var TVector3d,b:float) {.inline.} = s(a.x,b) ; s(a.y,b) ; s(a.z,b) @@ -188,20 +188,20 @@ proc scale*(s:float):TMatrix3d {.noInit.} = proc scale*(s:float,org:TPoint3d):TMatrix3d {.noInit.} = ## Returns a new scaling matrix using, `org` as scale origin. - result.setElements(s,0,0,0, 0,s,0,0, 0,0,s,0, + result.setElements(s,0,0,0, 0,s,0,0, 0,0,s,0, org.x-s*org.x,org.y-s*org.y,org.z-s*org.z,1.0) proc stretch*(sx,sy,sz:float):TMatrix3d {.noInit.} = ## Returns new a stretch matrix, which is a ## scale matrix with non uniform scale in x,y and z. result.setElements(sx,0,0,0, 0,sy,0,0, 0,0,sz,0, 0,0,0,1) - + proc stretch*(sx,sy,sz:float,org:TPoint3d):TMatrix3d {.noInit.} = ## Returns a new stretch matrix, which is a ## scale matrix with non uniform scale in x,y and z. ## `org` is used as stretch origin. result.setElements(sx,0,0,0, 0,sy,0,0, 0,0,sz,0, org.x-sx*org.x,org.y-sy*org.y,org.z-sz*org.z,1) - + proc move*(dx,dy,dz:float):TMatrix3d {.noInit.} = ## Returns a new translation matrix. result.setElements(1,0,0,0, 0,1,0,0, 0,0,1,0, dx,dy,dz,1) @@ -235,7 +235,7 @@ proc rotate*(angle:float,axis:TVector3d):TMatrix3d {.noInit.}= uvomc=normax.x*normax.y*omc uwomc=normax.x*normax.z*omc vwomc=normax.y*normax.z*omc - + result.setElements( u2+(1.0-u2)*cs, uvomc+wsi, uwomc-vsi, 0.0, uvomc-wsi, v2+(1.0-v2)*cs, vwomc+usi, 0.0, @@ -248,11 +248,11 @@ proc rotate*(angle:float,org:TPoint3d,axis:TVector3d):TMatrix3d {.noInit.}= # see PDF document http://inside.mines.edu/~gmurray/ArbitraryAxisRotation/ArbitraryAxisRotation.pdf # for how this is computed - + var normax=axis if not normax.tryNormalize: #simplifies matrix computation below a lot raise newException(DivByZeroError,"Cannot rotate around zero length axis") - + let u=normax.x v=normax.y @@ -272,7 +272,7 @@ proc rotate*(angle:float,org:TPoint3d,axis:TVector3d):TMatrix3d {.noInit.}= uvomc=normax.x*normax.y*omc uwomc=normax.x*normax.z*omc vwomc=normax.y*normax.z*omc - + result.setElements( u2+(v2+w2)*cs, uvomc+wsi, uwomc-vsi, 0.0, uvomc-wsi, v2+(u2+w2)*cs, vwomc+usi, 0.0, @@ -305,7 +305,7 @@ proc rotateY*(angle:float):TMatrix3d {.noInit.}= 0,1,0,0, s,0,c,0, 0,0,0,1) - + proc rotateZ*(angle:float):TMatrix3d {.noInit.}= ## Creates a matrix that rotates around the z-axis with `angle` radians, ## which is also called a 'yaw' matrix. @@ -317,19 +317,19 @@ proc rotateZ*(angle:float):TMatrix3d {.noInit.}= -s,c,0,0, 0,0,1,0, 0,0,0,1) - + proc isUniform*(m:TMatrix3d,tol=1.0e-6):bool= - ## Checks if the transform is uniform, that is + ## Checks if the transform is uniform, that is ## perpendicular axes of equal length, which means (for example) ## it cannot transform a sphere into an ellipsoid. - ## `tol` is used as tolerance for both equal length comparison + ## `tol` is used as tolerance for both equal length comparison ## and perpendicular comparison. - + #dot product=0 means perpendicular coord. system, check xaxis vs yaxis and xaxis vs zaxis if abs(m.ax*m.bx+m.ay*m.by+m.az*m.bz)<=tol and # x vs y abs(m.ax*m.cx+m.ay*m.cy+m.az*m.cz)<=tol and #x vs z abs(m.bx*m.cx+m.by*m.cy+m.bz*m.cz)<=tol: #y vs z - + #subtract squared lengths of axes to check if uniform scaling: let sqxlen=(m.ax*m.ax+m.ay*m.ay+m.az*m.az) @@ -340,16 +340,16 @@ proc isUniform*(m:TMatrix3d,tol=1.0e-6):bool= return false - + proc mirror*(planeperp:TVector3d):TMatrix3d {.noInit.}= ## Creates a matrix that mirrors over the plane that has `planeperp` as normal, ## and passes through origo. `planeperp` does not need to be normalized. - + # https://en.wikipedia.org/wiki/Transformation_matrix var n=planeperp if not n.tryNormalize: raise newException(DivByZeroError,"Cannot mirror over a plane with a zero length normal") - + let a=n.x b=n.y @@ -357,7 +357,7 @@ proc mirror*(planeperp:TVector3d):TMatrix3d {.noInit.}= ab=a*b ac=a*c bc=b*c - + result.setElements( 1-2*a*a , -2*ab,-2*ac,0, -2*ab , 1-2*b*b, -2*bc, 0, @@ -376,7 +376,7 @@ proc mirror*(org:TPoint3d,planeperp:TVector3d):TMatrix3d {.noInit.}= var n=planeperp if not n.tryNormalize: raise newException(DivByZeroError,"Cannot mirror over a plane with a zero length normal") - + let a=n.x b=n.y @@ -390,7 +390,7 @@ proc mirror*(org:TPoint3d,planeperp:TVector3d):TMatrix3d {.noInit.}= tx=org.x ty=org.y tz=org.z - + result.setElements( 1-2*aa , -2*ab,-2*ac,0, -2*ab , 1-2*bb, -2*bc, 0, @@ -402,8 +402,8 @@ proc mirror*(org:TPoint3d,planeperp:TVector3d):TMatrix3d {.noInit.}= proc determinant*(m:TMatrix3d):float= ## Computes the determinant of matrix `m`. - - # This computation is gotten from ratsimp(optimize(determinant(m))) + + # This computation is gotten from ratsimp(optimize(determinant(m))) # in maxima CAS let O1=m.cx*m.tw-m.cw*m.tx @@ -423,10 +423,10 @@ proc inverse*(m:TMatrix3d):TMatrix3d {.noInit.}= ## Computes the inverse of matrix `m`. If the matrix ## determinant is zero, thus not invertible, a EDivByZero ## will be raised. - + # this computation comes from optimize(invert(m)) in maxima CAS - - let + + let det=m.determinant O2=m.cy*m.tw-m.cw*m.ty O3=m.cz*m.tw-m.cw*m.tz @@ -464,7 +464,7 @@ proc inverse*(m:TMatrix3d):TMatrix3d {.noInit.}= proc equals*(m1:TMatrix3d,m2:TMatrix3d,tol=1.0e-6):bool= ## Checks if all elements of `m1`and `m2` is equal within ## a given tolerance `tol`. - return + return abs(m1.ax-m2.ax)<=tol and abs(m1.ay-m2.ay)<=tol and abs(m1.az-m2.az)<=tol and @@ -486,11 +486,11 @@ proc `=~`*(m1,m2:TMatrix3d):bool= ## Checks if `m1` and `m2` is approximately equal, using a ## tolerance of 1e-6. equals(m1,m2) - + proc transpose*(m:TMatrix3d):TMatrix3d {.noInit.}= ## Returns the transpose of `m` result.setElements(m.ax,m.bx,m.cx,m.tx,m.ay,m.by,m.cy,m.ty,m.az,m.bz,m.cz,m.tz,m.aw,m.bw,m.cw,m.tw) - + proc getXAxis*(m:TMatrix3d):TVector3d {.noInit.}= ## Gets the local x axis of `m` result.x=m.ax @@ -509,26 +509,26 @@ proc getZAxis*(m:TMatrix3d):TVector3d {.noInit.}= result.y=m.cy result.z=m.cz - + proc `$`*(m:TMatrix3d):string= ## String representation of `m` - return rtos(m.ax) & "," & rtos(m.ay) & "," &rtos(m.az) & "," & rtos(m.aw) & - "\n" & rtos(m.bx) & "," & rtos(m.by) & "," &rtos(m.bz) & "," & rtos(m.bw) & - "\n" & rtos(m.cx) & "," & rtos(m.cy) & "," &rtos(m.cz) & "," & rtos(m.cw) & - "\n" & rtos(m.tx) & "," & rtos(m.ty) & "," &rtos(m.tz) & "," & rtos(m.tw) - + return rtos(m.ax) & "," & rtos(m.ay) & "," & rtos(m.az) & "," & rtos(m.aw) & + "\n" & rtos(m.bx) & "," & rtos(m.by) & "," & rtos(m.bz) & "," & rtos(m.bw) & + "\n" & rtos(m.cx) & "," & rtos(m.cy) & "," & rtos(m.cz) & "," & rtos(m.cw) & + "\n" & rtos(m.tx) & "," & rtos(m.ty) & "," & rtos(m.tz) & "," & rtos(m.tw) + proc apply*(m:TMatrix3d, x,y,z:var float, translate=false)= ## Applies transformation `m` onto `x` , `y` , `z` , optionally ## using the translation part of the matrix. - let + let oldx=x oldy=y oldz=z - + x=m.cx*oldz+m.bx*oldy+m.ax*oldx y=m.cy*oldz+m.by*oldy+m.ay*oldx z=m.cz*oldz+m.bz*oldy+m.az*oldx - + if translate: x+=m.tx y+=m.ty @@ -552,13 +552,13 @@ proc `len=`*(v:var TVector3d,newlen:float) {.noInit.} = ## an arbitrary vector of the requested length is returned. let fac=newlen/v.len - + if newlen==0.0: v.x=0.0 v.y=0.0 v.z=0.0 return - + if fac==Inf or fac==NegInf: #to short for float accuracy #do as good as possible: @@ -588,7 +588,7 @@ proc `&` *(v:TVector3d,m:TMatrix3d):TVector3d {.noInit.} = ## Concatenate vector `v` with a transformation matrix. ## Transforming a vector ignores the translational part ## of the matrix. - + # | AX AY AZ AW | # | X Y Z 1 | * | BX BY BZ BW | # | CX CY CZ CW | @@ -605,12 +605,12 @@ proc `&=` *(v:var TVector3d,m:TMatrix3d) {.noInit.} = ## Applies transformation `m` onto `v` in place. ## Transforming a vector ignores the translational part ## of the matrix. - + # | AX AY AZ AW | # | X Y Z 1 | * | BX BY BZ BW | # | CX CY CZ CW | # | 0 0 0 1 | - + let newx=m.cx*v.z+m.bx*v.y+m.ax*v.x newy=m.cy*v.z+m.by*v.y+m.ay*v.x @@ -620,38 +620,38 @@ proc `&=` *(v:var TVector3d,m:TMatrix3d) {.noInit.} = proc transformNorm*(v:var TVector3d,m:TMatrix3d)= ## Applies a normal direction transformation `m` onto `v` in place. - ## The resulting vector is *not* normalized. Transforming a vector ignores the - ## translational part of the matrix. If the matrix is not invertible + ## The resulting vector is *not* normalized. Transforming a vector ignores the + ## translational part of the matrix. If the matrix is not invertible ## (determinant=0), an EDivByZero will be raised. # transforming a normal is done by transforming # by the transpose of the inverse of the original matrix - + # Major reason this simple function is here is that this function can be optimized in the future, # (possibly by hardware) as well as having a consistent API with the 2d version. v&=transpose(inverse(m)) - + proc transformInv*(v:var TVector3d,m:TMatrix3d)= - ## Applies the inverse of `m` on vector `v`. Transforming a vector ignores - ## the translational part of the matrix. Transforming a vector ignores the + ## Applies the inverse of `m` on vector `v`. Transforming a vector ignores + ## the translational part of the matrix. Transforming a vector ignores the ## translational part of the matrix. ## If the matrix is not invertible (determinant=0), an EDivByZero ## will be raised. - + # Major reason this simple function is here is that this function can be optimized in the future, # (possibly by hardware) as well as having a consistent API with the 2d version. v&=m.inverse - + proc transformNormInv*(vec:var TVector3d,m:TMatrix3d)= ## Applies an inverse normal direction transformation `m` onto `v` in place. - ## This is faster than creating an inverse - ## matrix and transformNorm(...) it. Transforming a vector ignores the + ## This is faster than creating an inverse + ## matrix and transformNorm(...) it. Transforming a vector ignores the ## translational part of the matrix. - + # see vector2d:s equivalent for a deeper look how/why this works vec&=m.transpose -proc tryNormalize*(v:var TVector3d):bool= +proc tryNormalize*(v:var TVector3d):bool= ## Modifies `v` to have a length of 1.0, keeping its angle. ## If `v` has zero length (and thus no angle), it is left unmodified and false is ## returned, otherwise true is returned. @@ -663,26 +663,26 @@ proc tryNormalize*(v:var TVector3d):bool= v.x/=mag v.y/=mag v.z/=mag - + return true -proc normalize*(v:var TVector3d) {.inline.}= +proc normalize*(v:var TVector3d) {.inline.}= ## Modifies `v` to have a length of 1.0, keeping its angle. ## If `v` has zero length, an EDivByZero will be raised. if not tryNormalize(v): raise newException(DivByZeroError,"Cannot normalize zero length vector") proc rotate*(vec:var TVector3d,angle:float,axis:TVector3d)= - ## Rotates `vec` in place, with `angle` radians over `axis`, which passes + ## Rotates `vec` in place, with `angle` radians over `axis`, which passes ## through origo. # see PDF document http://inside.mines.edu/~gmurray/ArbitraryAxisRotation/ArbitraryAxisRotation.pdf # for how this is computed - + var normax=axis if not normax.tryNormalize: raise newException(DivByZeroError,"Cannot rotate around zero length axis") - + let cs=cos(angle) si=sin(angle) @@ -694,11 +694,11 @@ proc rotate*(vec:var TVector3d,angle:float,axis:TVector3d)= y=vec.y z=vec.z uxyzomc=(u*x+v*y+w*z)*omc - + vec.x=u*uxyzomc+x*cs+(v*z-w*y)*si vec.y=v*uxyzomc+y*cs+(w*x-u*z)*si vec.z=w*uxyzomc+z*cs+(u*y-v*x)*si - + proc scale*(v:var TVector3d,s:float)= ## Scales the vector in place with factor `s` v.x*=s @@ -713,12 +713,12 @@ proc stretch*(v:var TVector3d,sx,sy,sz:float)= proc mirror*(v:var TVector3d,planeperp:TVector3d)= ## Computes the mirrored vector of `v` over the plane - ## that has `planeperp` as normal direction. + ## that has `planeperp` as normal direction. ## `planeperp` does not need to be normalized. - + var n=planeperp n.normalize - + let x=v.x y=v.y @@ -729,7 +729,7 @@ proc mirror*(v:var TVector3d,planeperp:TVector3d)= ac=a*c ab=a*b bc=b*c - + v.x= -2*(ac*z+ab*y+a*a*x)+x v.y= -2*(bc*z+b*b*y+ab*x)+y v.z= -2*(c*c*z+bc*y+ac*x)+z @@ -740,7 +740,7 @@ proc `-` *(v:TVector3d):TVector3d= result.x= -v.x result.y= -v.y result.z= -v.z - + # declare templated binary operators makeBinOpVector(`+`) makeBinOpVector(`-`) @@ -752,7 +752,7 @@ makeBinOpAssignVector(`*=`) makeBinOpAssignVector(`/=`) proc dot*(v1,v2:TVector3d):float {.inline.}= - ## Computes the dot product of two vectors. + ## Computes the dot product of two vectors. ## Returns 0.0 if the vectors are perpendicular. return v1.x*v2.x+v1.y*v2.y+v1.z*v2.z @@ -769,12 +769,12 @@ proc cross*(v1,v2:TVector3d):TVector3d {.inline.}= proc equals*(v1,v2:TVector3d,tol=1.0e-6):bool= ## Checks if two vectors approximately equals with a tolerance. return abs(v2.x-v1.x)<=tol and abs(v2.y-v1.y)<=tol and abs(v2.z-v1.z)<=tol - + proc `=~` *(v1,v2:TVector3d):bool= - ## Checks if two vectors approximately equals with a + ## Checks if two vectors approximately equals with a ## hardcoded tolerance 1e-6 equals(v1,v2) - + proc angleTo*(v1,v2:TVector3d):float= ## Returns the smallest angle between v1 and v2, ## which is in range 0-PI @@ -801,7 +801,7 @@ proc arbitraryAxis*(norm:TVector3d):TMatrix3d {.noInit.}= ay=cross(norm,ax) ay.normalize() az=cross(ax,ay) - + result.setElements( ax.x,ax.y,ax.z,0.0, ay.x,ay.y,ay.z,0.0, @@ -811,20 +811,20 @@ proc arbitraryAxis*(norm:TVector3d):TMatrix3d {.noInit.}= proc bisect*(v1,v2:TVector3d):TVector3d {.noInit.}= ## Computes the bisector between v1 and v2 as a normalized vector. ## If one of the input vectors has zero length, a normalized version - ## of the other is returned. If both input vectors has zero length, + ## of the other is returned. If both input vectors has zero length, ## an arbitrary normalized vector `v1` is returned. var vmag1=v1.len vmag2=v2.len - - # zero length vector equals arbitrary vector, just change + + # zero length vector equals arbitrary vector, just change # magnitude to one to avoid zero division - if vmag1==0.0: + if vmag1==0.0: if vmag2==0: #both are zero length return any normalized vector return XAXIS vmag1=1.0 - if vmag2==0.0: vmag2=1.0 - + if vmag2==0.0: vmag2=1.0 + let x1=v1.x/vmag1 y1=v1.y/vmag1 @@ -832,14 +832,14 @@ proc bisect*(v1,v2:TVector3d):TVector3d {.noInit.}= x2=v2.x/vmag2 y2=v2.y/vmag2 z2=v2.z/vmag2 - + result.x=(x1 + x2) * 0.5 result.y=(y1 + y2) * 0.5 result.z=(z1 + z2) * 0.5 - + if not result.tryNormalize(): # This can happen if vectors are colinear. In this special case - # there are actually inifinitely many bisectors, we select just + # there are actually inifinitely many bisectors, we select just # one of them. result=v1.cross(XAXIS) if result.sqrLen<1.0e-9: @@ -857,14 +857,14 @@ proc point3d*(x,y,z:float):TPoint3d= result.x=x result.y=y result.z=z - + proc sqrDist*(a,b:TPoint3d):float= ## Computes the squared distance between `a`and `b` let dx=b.x-a.x let dy=b.y-a.y let dz=b.z-a.z result=dx*dx+dy*dy+dz*dz - + proc dist*(a,b:TPoint3d):float {.inline.}= ## Computes the absolute distance between `a`and `b` result=sqrt(sqrDist(a,b)) @@ -876,7 +876,7 @@ proc `$` *(p:TPoint3d):string= result.add(rtos(p.y)) result.add(",") result.add(rtos(p.z)) - + proc `&`*(p:TPoint3d,m:TMatrix3d):TPoint3d= ## Concatenates a point `p` with a transform `m`, ## resulting in a new, transformed point. @@ -893,18 +893,18 @@ proc `&=` *(p:var TPoint3d,m:TMatrix3d)= p.x=m.cx*z+m.bx*y+m.ax*x+m.tx p.y=m.cy*z+m.by*y+m.ay*x+m.ty p.z=m.cz*z+m.bz*y+m.az*x+m.tz - + proc transformInv*(p:var TPoint3d,m:TMatrix3d)= ## Applies the inverse of transformation `m` onto `p` in place. ## If the matrix is not invertable (determinant=0) , EDivByZero will ## be raised. - + # can possibly be more optimized in the future so use this function when possible p&=inverse(m) proc `+`*(p:TPoint3d,v:TVector3d):TPoint3d {.noInit,inline.} = - ## Adds a vector `v` to a point `p`, resulting + ## Adds a vector `v` to a point `p`, resulting ## in a new point. result.x=p.x+v.x result.y=p.y+v.y @@ -917,7 +917,7 @@ proc `+=`*(p:var TPoint3d,v:TVector3d) {.noInit,inline.} = p.z+=v.z proc `-`*(p:TPoint3d,v:TVector3d):TPoint3d {.noInit,inline.} = - ## Subtracts a vector `v` from a point `p`, resulting + ## Subtracts a vector `v` from a point `p`, resulting ## in a new point. result.x=p.x-v.x result.y=p.y-v.y @@ -933,37 +933,37 @@ proc `-=`*(p:var TPoint3d,v:TVector3d) {.noInit,inline.} = ## Subtracts a vector `v` from a point `p` in place. p.x-=v.x p.y-=v.y - p.z-=v.z + p.z-=v.z proc equals(p1,p2:TPoint3d,tol=1.0e-6):bool {.inline.}= ## Checks if two points approximately equals with a tolerance. return abs(p2.x-p1.x)<=tol and abs(p2.y-p1.y)<=tol and abs(p2.z-p1.z)<=tol proc `=~`*(p1,p2:TPoint3d):bool {.inline.}= - ## Checks if two vectors approximately equals with a + ## Checks if two vectors approximately equals with a ## hardcoded tolerance 1e-6 equals(p1,p2) proc rotate*(p:var TPoint3d,rad:float,axis:TVector3d)= - ## Rotates point `p` in place `rad` radians about an axis + ## Rotates point `p` in place `rad` radians about an axis ## passing through origo. - + var v=vector3d(p.x,p.y,p.z) v.rotate(rad,axis) # reuse this code here since doing the same thing and quite complicated p.x=v.x p.y=v.y p.z=v.z - + proc rotate*(p:var TPoint3d,angle:float,org:TPoint3d,axis:TVector3d)= - ## Rotates point `p` in place `rad` radians about an axis + ## Rotates point `p` in place `rad` radians about an axis ## passing through `org` - + # see PDF document http://inside.mines.edu/~gmurray/ArbitraryAxisRotation/ArbitraryAxisRotation.pdf # for how this is computed - + var normax=axis normax.normalize - + let cs=cos(angle) omc=1.0-cs @@ -987,17 +987,17 @@ proc rotate*(p:var TPoint3d,angle:float,org:TPoint3d,axis:TVector3d)= bv=b*v cw=c*w uxmvymwz=ux-vy-wz - + p.x=(a*(vv+ww)-u*(bv+cw-uxmvymwz))*omc + x*cs + (b*w+v*z-c*v-w*y)*si p.y=(b*(uu+ww)-v*(au+cw-uxmvymwz))*omc + y*cs + (c*u-a*w+w*x-u*z)*si p.z=(c*(uu+vv)-w*(au+bv-uxmvymwz))*omc + z*cs + (a*v+u*y-b*u-v*x)*si - + proc scale*(p:var TPoint3d,fac:float) {.inline.}= ## Scales a point in place `fac` times with world origo as origin. p.x*=fac p.y*=fac p.z*=fac - + proc scale*(p:var TPoint3d,fac:float,org:TPoint3d){.inline.}= ## Scales the point in place `fac` times with `org` as origin. p.x=(p.x - org.x) * fac + org.x @@ -1005,7 +1005,7 @@ proc scale*(p:var TPoint3d,fac:float,org:TPoint3d){.inline.}= p.z=(p.z - org.z) * fac + org.z proc stretch*(p:var TPoint3d,facx,facy,facz:float){.inline.}= - ## Scales a point in place non uniformly `facx` , `facy` , `facz` times + ## Scales a point in place non uniformly `facx` , `facy` , `facz` times ## with world origo as origin. p.x*=facx p.y*=facy @@ -1017,7 +1017,7 @@ proc stretch*(p:var TPoint3d,facx,facy,facz:float,org:TPoint3d){.inline.}= p.x=(p.x - org.x) * facx + org.x p.y=(p.y - org.y) * facy + org.y p.z=(p.z - org.z) * facz + org.z - + proc move*(p:var TPoint3d,dx,dy,dz:float){.inline.}= ## Translates a point `dx` , `dy` , `dz` in place. @@ -1033,7 +1033,7 @@ proc move*(p:var TPoint3d,v:TVector3d){.inline.}= proc area*(a,b,c:TPoint3d):float {.inline.}= ## Computes the area of the triangle thru points `a` , `b` and `c` - + # The area of a planar 3d quadliteral is the magnitude of the cross # product of two edge vectors. Taking this time 0.5 gives the triangle area. return cross(b-a,c-a).len*0.5 diff --git a/lib/pure/collections/LockFreeHash.nim b/lib/pure/collections/LockFreeHash.nim index c3954468a..0df97c685 100644 --- a/lib/pure/collections/LockFreeHash.nim +++ b/lib/pure/collections/LockFreeHash.nim @@ -525,7 +525,7 @@ proc get*[K,V](table: var PConcTable[K,V], key: var K): V = #Tests ---------------------------- -when isMainModule: +when not defined(testing) and isMainModule: import locks, times, mersenne const diff --git a/lib/pure/collections/critbits.nim b/lib/pure/collections/critbits.nim index 3d10e39aa..7e3f23851 100644 --- a/lib/pure/collections/critbits.nim +++ b/lib/pure/collections/critbits.nim @@ -286,18 +286,19 @@ proc `$`*[T](c: CritBitTree[T]): string = result.add("}") when isMainModule: + import sequtils + var r: CritBitTree[void] r.incl "abc" r.incl "xyz" r.incl "def" r.incl "definition" r.incl "prefix" + doAssert r.contains"def" - #r.del "def" - for w in r.items: - echo w - - for w in r.itemsWithPrefix("de"): - echo w + r.excl "def" + + assert toSeq(r.items) == @["abc", "definition", "prefix", "xyz"] + assert toSeq(r.itemsWithPrefix("de")) == @["definition"] diff --git a/lib/pure/collections/intsets.nim b/lib/pure/collections/intsets.nim index 7520e6e46..25f6616a6 100644 --- a/lib/pure/collections/intsets.nim +++ b/lib/pure/collections/intsets.nim @@ -14,12 +14,12 @@ ## copy; use ``assign`` to get a deep copy. import - os, hashes, math + hashes, math type BitScalar = int -const +const InitIntSetSize = 8 # must be a power of two! TrunkShift = 9 BitsPerTrunk = 1 shl TrunkShift # needs to be a power of 2 and @@ -31,11 +31,11 @@ const type PTrunk = ref TTrunk - TTrunk {.final.} = object + TTrunk {.final.} = object next: PTrunk # all nodes are connected with this pointer key: int # start address at bit 0 bits: array[0..IntsPerTrunk - 1, BitScalar] # a bit vector - + TTrunkSeq = seq[PTrunk] IntSet* = object ## an efficient set of 'int' implemented as a sparse bit set counter, max: int @@ -44,42 +44,42 @@ type {.deprecated: [TIntSet: IntSet].} -proc mustRehash(length, counter: int): bool {.inline.} = +proc mustRehash(length, counter: int): bool {.inline.} = assert(length > counter) result = (length * 2 < counter * 3) or (length - counter < 4) -proc nextTry(h, maxHash: THash): THash {.inline.} = - result = ((5 * h) + 1) and maxHash +proc nextTry(h, maxHash: THash): THash {.inline.} = + result = ((5 * h) + 1) and maxHash -proc intSetGet(t: IntSet, key: int): PTrunk = +proc intSetGet(t: IntSet, key: int): PTrunk = var h = key and t.max - while t.data[h] != nil: - if t.data[h].key == key: + while t.data[h] != nil: + if t.data[h].key == key: return t.data[h] h = nextTry(h, t.max) result = nil -proc intSetRawInsert(t: IntSet, data: var TTrunkSeq, desc: PTrunk) = +proc intSetRawInsert(t: IntSet, data: var TTrunkSeq, desc: PTrunk) = var h = desc.key and t.max - while data[h] != nil: + while data[h] != nil: assert(data[h] != desc) h = nextTry(h, t.max) assert(data[h] == nil) data[h] = desc -proc intSetEnlarge(t: var IntSet) = +proc intSetEnlarge(t: var IntSet) = var n: TTrunkSeq var oldMax = t.max t.max = ((t.max + 1) * 2) - 1 newSeq(n, t.max + 1) - for i in countup(0, oldMax): + for i in countup(0, oldMax): if t.data[i] != nil: intSetRawInsert(t, n, t.data[i]) swap(t.data, n) -proc intSetPut(t: var IntSet, key: int): PTrunk = +proc intSetPut(t: var IntSet, key: int): PTrunk = var h = key and t.max - while t.data[h] != nil: - if t.data[h].key == key: + while t.data[h] != nil: + if t.data[h].key == key: return t.data[h] h = nextTry(h, t.max) if mustRehash(t.max + 1, t.counter): intSetEnlarge(t) @@ -94,43 +94,43 @@ proc intSetPut(t: var IntSet, key: int): PTrunk = t.data[h] = result proc contains*(s: IntSet, key: int): bool = - ## returns true iff `key` is in `s`. + ## returns true iff `key` is in `s`. var t = intSetGet(s, `shr`(key, TrunkShift)) - if t != nil: + if t != nil: var u = key and TrunkMask result = (t.bits[`shr`(u, IntShift)] and `shl`(1, u and IntMask)) != 0 - else: + else: result = false - -proc incl*(s: var IntSet, key: int) = + +proc incl*(s: var IntSet, key: int) = ## includes an element `key` in `s`. var t = intSetPut(s, `shr`(key, TrunkShift)) var u = key and TrunkMask t.bits[`shr`(u, IntShift)] = t.bits[`shr`(u, IntShift)] or `shl`(1, u and IntMask) -proc excl*(s: var IntSet, key: int) = +proc excl*(s: var IntSet, key: int) = ## excludes `key` from the set `s`. var t = intSetGet(s, `shr`(key, TrunkShift)) - if t != nil: + if t != nil: var u = key and TrunkMask t.bits[`shr`(u, IntShift)] = t.bits[`shr`(u, IntShift)] and not `shl`(1, u and IntMask) -proc containsOrIncl*(s: var IntSet, key: int): bool = +proc containsOrIncl*(s: var IntSet, key: int): bool = ## returns true if `s` contains `key`, otherwise `key` is included in `s` ## and false is returned. var t = intSetGet(s, `shr`(key, TrunkShift)) - if t != nil: + if t != nil: var u = key and TrunkMask result = (t.bits[`shr`(u, IntShift)] and `shl`(1, u and IntMask)) != 0 - if not result: + if not result: t.bits[`shr`(u, IntShift)] = t.bits[`shr`(u, IntShift)] or `shl`(1, u and IntMask) - else: + else: incl(s, key) result = false - + proc initIntSet*: IntSet = ## creates a new int set that is empty. newSeq(result.data, InitIntSetSize) @@ -140,14 +140,14 @@ proc initIntSet*: IntSet = proc assign*(dest: var IntSet, src: IntSet) = ## copies `src` to `dest`. `dest` does not need to be initialized by - ## `initIntSet`. + ## `initIntSet`. dest.counter = src.counter dest.max = src.max newSeq(dest.data, src.data.len) - + var it = src.head while it != nil: - + var h = it.key and dest.max while dest.data[h] != nil: h = nextTry(h, dest.max) assert(dest.data[h] == nil) @@ -168,7 +168,7 @@ iterator items*(s: IntSet): int {.inline.} = while r != nil: var i = 0 while i <= high(r.bits): - var w = r.bits[i] + var w = r.bits[i] # taking a copy of r.bits[i] here is correct, because # modifying operations are not allowed during traversation var j = 0 @@ -198,14 +198,21 @@ proc empty*(s: IntSet): bool {.inline, deprecated.} = result = s.counter == 0 when isMainModule: + import sequtils, algorithm + var x = initIntSet() x.incl(1) x.incl(2) x.incl(7) x.incl(1056) - for e in items(x): echo e - var y: TIntSet + var xs = toSeq(items(x)) + xs.sort(cmp[int]) + assert xs == @[1, 2, 7, 1056] + + var y: IntSet assign(y, x) - for e in items(y): echo e + var ys = toSeq(items(y)) + ys.sort(cmp[int]) + assert ys == @[1, 2, 7, 1056] diff --git a/lib/pure/collections/sequtils.nim b/lib/pure/collections/sequtils.nim index 3f37d1ef0..e9cd2cb3c 100644 --- a/lib/pure/collections/sequtils.nim +++ b/lib/pure/collections/sequtils.nim @@ -492,9 +492,8 @@ when isMainModule: block: # filter iterator test let numbers = @[1, 4, 5, 8, 9, 7, 4] - for n in filter(numbers, proc (x: int): bool = x mod 2 == 0): - echo($n) - # echoes 4, 8, 4 in separate lines + assert toSeq(filter(numbers, proc (x: int): bool = x mod 2 == 0)) == + @[4, 8, 4] block: # keepIf test var floats = @[13.0, 12.5, 5.8, 2.0, 6.1, 9.9, 10.1] @@ -616,4 +615,5 @@ when isMainModule: #doAssert a.repeat(-1) == @[] # will not compile! doAssert b.repeat(3) == @[] - echo "Finished doc tests" + when not defined(testing): + echo "Finished doc tests" diff --git a/lib/pure/collections/sets.nim b/lib/pure/collections/sets.nim index 4a20d00a4..280e0eeba 100644 --- a/lib/pure/collections/sets.nim +++ b/lib/pure/collections/sets.nim @@ -114,7 +114,7 @@ proc mustRehash(length, counter: int): bool {.inline.} = assert(length > counter) result = (length * 2 < counter * 3) or (length - counter < 4) -proc rightSize*(count: int): int {.inline.} = +proc rightSize*(count: Natural): int {.inline.} = ## Return the value of `initialSize` to support `count` items. ## ## If more items are expected to be added, simply add that @@ -798,177 +798,179 @@ proc `==`*[A](s, t: OrderedSet[A]): bool = g = nxg result = compared == s.counter -proc testModule() = - ## Internal micro test to validate docstrings and such. - block isValidTest: - var options: HashSet[string] - proc savePreferences(options: HashSet[string]) = - assert options.isValid, "Pass an initialized set!" - options = initSet[string]() - options.savePreferences - - block lenTest: - var values: HashSet[int] - assert(not values.isValid) - assert values.len == 0 - assert values.card == 0 - - block setIterator: - type pair = tuple[a, b: int] - var a, b = initSet[pair]() - a.incl((2, 3)) - a.incl((3, 2)) - a.incl((2, 3)) - for x, y in a.items: - b.incl((x - 2, y + 1)) - assert a.len == b.card - assert a.len == 2 - #echo b - - block setContains: - var values = initSet[int]() - assert(not values.contains(2)) - values.incl(2) - assert values.contains(2) - values.excl(2) - assert(not values.contains(2)) - - values.incl(4) - var others = toSet([6, 7]) - values.incl(others) - assert values.len == 3 - - values.init - assert values.containsOrIncl(2) == false - assert values.containsOrIncl(2) == true - var - a = toSet([1, 2]) - b = toSet([1]) - b.incl(2) - assert a == b - - block exclusions: - var s = toSet([2, 3, 6, 7]) - s.excl(2) - s.excl(2) - assert s.len == 3 - - var - numbers = toSet([1, 2, 3, 4, 5]) - even = toSet([2, 4, 6, 8]) - numbers.excl(even) - #echo numbers - # --> {1, 3, 5} - - block toSeqAndString: - var a = toSet([2, 4, 5]) - var b = initSet[int]() - for x in [2, 4, 5]: b.incl(x) - assert($a == $b) - #echo a - #echo toSet(["no", "esc'aping", "is \" provided"]) - - #block orderedToSeqAndString: - # echo toOrderedSet([2, 4, 5]) - # echo toOrderedSet(["no", "esc'aping", "is \" provided"]) - - block setOperations: - var - a = toSet(["a", "b"]) - b = toSet(["b", "c"]) - c = union(a, b) - assert c == toSet(["a", "b", "c"]) - var d = intersection(a, b) - assert d == toSet(["b"]) - var e = difference(a, b) - assert e == toSet(["a"]) - var f = symmetricDifference(a, b) - assert f == toSet(["a", "c"]) - assert d < a and d < b - assert((a < a) == false) - assert d <= a and d <= b - assert((a <= a)) - # Alias test. - assert a + b == toSet(["a", "b", "c"]) - assert a * b == toSet(["b"]) - assert a - b == toSet(["a"]) - assert a -+- b == toSet(["a", "c"]) - assert disjoint(a, b) == false - assert disjoint(a, b - a) == true - - block mapSet: - var a = toSet([1, 2, 3]) - var b = a.map(proc (x: int): string = $x) - assert b == toSet(["1", "2", "3"]) - - block isValidTest: - var cards: OrderedSet[string] - proc saveTarotCards(cards: OrderedSet[string]) = - assert cards.isValid, "Pass an initialized set!" - cards = initOrderedSet[string]() - cards.saveTarotCards - - block lenTest: - var values: OrderedSet[int] - assert(not values.isValid) - assert values.len == 0 - assert values.card == 0 - - block setIterator: - type pair = tuple[a, b: int] - var a, b = initOrderedSet[pair]() - a.incl((2, 3)) - a.incl((3, 2)) - a.incl((2, 3)) - for x, y in a.items: - b.incl((x - 2, y + 1)) - assert a.len == b.card - assert a.len == 2 - - #block orderedSetIterator: - # var a = initOrderedSet[int]() - # for value in [9, 2, 1, 5, 1, 8, 4, 2]: - # a.incl(value) - # for value in a.items: - # echo "Got ", value - - block setContains: - var values = initOrderedSet[int]() - assert(not values.contains(2)) - values.incl(2) - assert values.contains(2) - - block toSeqAndString: - var a = toOrderedSet([2, 4, 5]) - var b = initOrderedSet[int]() - for x in [2, 4, 5]: b.incl(x) - assert($a == $b) - assert(a == b) # https://github.com/Araq/Nimrod/issues/1413 - - block initBlocks: - var a: OrderedSet[int] - a.init(4) - a.incl(2) - a.init - assert a.len == 0 and a.isValid - a = initOrderedSet[int](4) - a.incl(2) - assert a.len == 1 - - var b: HashSet[int] - b.init(4) - b.incl(2) - b.init - assert b.len == 0 and b.isValid - b = initSet[int](4) - b.incl(2) - assert b.len == 1 - - for i in 0 .. 32: - var s = rightSize(i) - if s <= i or mustRehash(s, i): - echo "performance issue: rightSize() will not elide enlarge() at ", i - - echo "Micro tests run successfully." - -when isMainModule and not defined(release): testModule() +when isMainModule and not defined(release): + proc testModule() = + ## Internal micro test to validate docstrings and such. + block isValidTest: + var options: HashSet[string] + proc savePreferences(options: HashSet[string]) = + assert options.isValid, "Pass an initialized set!" + options = initSet[string]() + options.savePreferences + + block lenTest: + var values: HashSet[int] + assert(not values.isValid) + assert values.len == 0 + assert values.card == 0 + + block setIterator: + type pair = tuple[a, b: int] + var a, b = initSet[pair]() + a.incl((2, 3)) + a.incl((3, 2)) + a.incl((2, 3)) + for x, y in a.items: + b.incl((x - 2, y + 1)) + assert a.len == b.card + assert a.len == 2 + #echo b + + block setContains: + var values = initSet[int]() + assert(not values.contains(2)) + values.incl(2) + assert values.contains(2) + values.excl(2) + assert(not values.contains(2)) + + values.incl(4) + var others = toSet([6, 7]) + values.incl(others) + assert values.len == 3 + + values.init + assert values.containsOrIncl(2) == false + assert values.containsOrIncl(2) == true + var + a = toSet([1, 2]) + b = toSet([1]) + b.incl(2) + assert a == b + + block exclusions: + var s = toSet([2, 3, 6, 7]) + s.excl(2) + s.excl(2) + assert s.len == 3 + + var + numbers = toSet([1, 2, 3, 4, 5]) + even = toSet([2, 4, 6, 8]) + numbers.excl(even) + #echo numbers + # --> {1, 3, 5} + + block toSeqAndString: + var a = toSet([2, 4, 5]) + var b = initSet[int]() + for x in [2, 4, 5]: b.incl(x) + assert($a == $b) + #echo a + #echo toSet(["no", "esc'aping", "is \" provided"]) + + #block orderedToSeqAndString: + # echo toOrderedSet([2, 4, 5]) + # echo toOrderedSet(["no", "esc'aping", "is \" provided"]) + + block setOperations: + var + a = toSet(["a", "b"]) + b = toSet(["b", "c"]) + c = union(a, b) + assert c == toSet(["a", "b", "c"]) + var d = intersection(a, b) + assert d == toSet(["b"]) + var e = difference(a, b) + assert e == toSet(["a"]) + var f = symmetricDifference(a, b) + assert f == toSet(["a", "c"]) + assert d < a and d < b + assert((a < a) == false) + assert d <= a and d <= b + assert((a <= a)) + # Alias test. + assert a + b == toSet(["a", "b", "c"]) + assert a * b == toSet(["b"]) + assert a - b == toSet(["a"]) + assert a -+- b == toSet(["a", "c"]) + assert disjoint(a, b) == false + assert disjoint(a, b - a) == true + + block mapSet: + var a = toSet([1, 2, 3]) + var b = a.map(proc (x: int): string = $x) + assert b == toSet(["1", "2", "3"]) + + block isValidTest: + var cards: OrderedSet[string] + proc saveTarotCards(cards: OrderedSet[string]) = + assert cards.isValid, "Pass an initialized set!" + cards = initOrderedSet[string]() + cards.saveTarotCards + + block lenTest: + var values: OrderedSet[int] + assert(not values.isValid) + assert values.len == 0 + assert values.card == 0 + + block setIterator: + type pair = tuple[a, b: int] + var a, b = initOrderedSet[pair]() + a.incl((2, 3)) + a.incl((3, 2)) + a.incl((2, 3)) + for x, y in a.items: + b.incl((x - 2, y + 1)) + assert a.len == b.card + assert a.len == 2 + + #block orderedSetIterator: + # var a = initOrderedSet[int]() + # for value in [9, 2, 1, 5, 1, 8, 4, 2]: + # a.incl(value) + # for value in a.items: + # echo "Got ", value + + block setContains: + var values = initOrderedSet[int]() + assert(not values.contains(2)) + values.incl(2) + assert values.contains(2) + + block toSeqAndString: + var a = toOrderedSet([2, 4, 5]) + var b = initOrderedSet[int]() + for x in [2, 4, 5]: b.incl(x) + assert($a == $b) + assert(a == b) # https://github.com/Araq/Nimrod/issues/1413 + + block initBlocks: + var a: OrderedSet[int] + a.init(4) + a.incl(2) + a.init + assert a.len == 0 and a.isValid + a = initOrderedSet[int](4) + a.incl(2) + assert a.len == 1 + + var b: HashSet[int] + b.init(4) + b.incl(2) + b.init + assert b.len == 0 and b.isValid + b = initSet[int](4) + b.incl(2) + assert b.len == 1 + + for i in 0 .. 32: + var s = rightSize(i) + if s <= i or mustRehash(s, i): + echo "performance issue: rightSize() will not elide enlarge() at ", i + + when not defined(testing): + echo "Micro tests run successfully." + + testModule() diff --git a/lib/pure/collections/tables.nim b/lib/pure/collections/tables.nim index f85acef22..9496fa2fe 100644 --- a/lib/pure/collections/tables.nim +++ b/lib/pure/collections/tables.nim @@ -128,7 +128,7 @@ proc mustRehash(length, counter: int): bool {.inline.} = assert(length > counter) result = (length * 2 < counter * 3) or (length - counter < 4) -proc rightSize*(count: int): int {.inline.} = +proc rightSize*(count: Natural): int {.inline.} = ## Return the value of `initialSize` to support `count` items. ## ## If more items are expected to be added, simply add that @@ -215,6 +215,10 @@ proc hasKey*[A, B](t: Table[A, B], key: A): bool = var hc: THash result = rawGet(t, key, hc) >= 0 +proc contains*[A, B](t: Table[A, B], key: A): bool = + ## alias of `hasKey` for use with the `in` operator. + return hasKey[A, B](t, key) + proc rawInsert[A, B](t: var Table[A, B], data: var KeyValuePairSeq[A, B], key: A, val: B, hc: THash, h: THash) = rawInsertImpl() @@ -411,6 +415,10 @@ proc hasKey*[A, B](t: TableRef[A, B], key: A): bool = ## returns true iff `key` is in the table `t`. result = t[].hasKey(key) +proc contains*[A, B](t: TableRef[A, B], key: A): bool = + ## alias of `hasKey` for use with the `in` operator. + return hasKey[A, B](t, key) + proc `[]=`*[A, B](t: TableRef[A, B], key: A, val: B) = ## puts a (key, value)-pair into `t`. t[][key] = val @@ -532,6 +540,10 @@ proc hasKey*[A, B](t: OrderedTable[A, B], key: A): bool = var hc: THash result = rawGet(t, key, hc) >= 0 +proc contains*[A, B](t: OrderedTable[A, B], key: A): bool = + ## alias of `hasKey` for use with the `in` operator. + return hasKey[A, B](t, key) + proc rawInsert[A, B](t: var OrderedTable[A, B], data: var OrderedKeyValuePairSeq[A, B], key: A, val: B, hc: THash, h: THash) = @@ -704,6 +716,10 @@ proc hasKey*[A, B](t: OrderedTableRef[A, B], key: A): bool = ## returns true iff `key` is in the table `t`. result = t[].hasKey(key) +proc contains*[A, B](t: OrderedTableRef[A, B], key: A): bool = + ## alias of `hasKey` for use with the `in` operator. + return hasKey[A, B](t, key) + proc `[]=`*[A, B](t: OrderedTableRef[A, B], key: A, val: B) = ## puts a (key, value)-pair into `t`. t[][key] = val @@ -804,6 +820,10 @@ proc hasKey*[A](t: CountTable[A], key: A): bool = ## returns true iff `key` is in the table `t`. result = rawGet(t, key) >= 0 +proc contains*[A](t: CountTable[A], key: A): bool = + ## alias of `hasKey` for use with the `in` operator. + return hasKey[A](t, key) + proc rawInsert[A](t: CountTable[A], data: var seq[tuple[key: A, val: int]], key: A, val: int) = var h: THash = hash(key) and high(data) @@ -819,15 +839,18 @@ proc enlarge[A](t: var CountTable[A]) = swap(t.data, n) proc `[]=`*[A](t: var CountTable[A], key: A, val: int) = - ## puts a (key, value)-pair into `t`. `val` has to be positive. + ## puts a (key, value)-pair into `t`. assert val > 0 var h = rawGet(t, key) if h >= 0: t.data[h].val = val else: - h = -1 - h - t.data[h].key = key - t.data[h].val = val + if mustRehash(len(t.data), t.counter): enlarge(t) + rawInsert(t, t.data, key, val) + inc(t.counter) + #h = -1 - h + #t.data[h].key = key + #t.data[h].val = val proc initCountTable*[A](initialSize=64): CountTable[A] = ## creates a new count table that is empty. @@ -942,6 +965,10 @@ proc hasKey*[A](t: CountTableRef[A], key: A): bool = ## returns true iff `key` is in the table `t`. result = t[].hasKey(key) +proc contains*[A](t: CountTableRef[A], key: A): bool = + ## alias of `hasKey` for use with the `in` operator. + return hasKey[A](t, key) + proc `[]=`*[A](t: CountTableRef[A], key: A, val: int) = ## puts a (key, value)-pair into `t`. `val` has to be positive. assert val > 0 @@ -984,6 +1011,22 @@ proc sort*[A](t: CountTableRef[A]) = ## `t` in the sorted order. t[].sort +proc merge*[A](s: var CountTable[A], t: CountTable[A]) = + ## merges the second table into the first one + for key, value in t: + s.inc(key, value) + +proc merge*[A](s, t: CountTable[A]): CountTable[A] = + ## merges the two tables into a new one + result = initCountTable[A](nextPowerOfTwo(max(s.len, t.len))) + for table in @[s, t]: + for key, value in table: + result.inc(key, value) + +proc merge*[A](s, t: CountTableRef[A]) = + ## merges the second table into the first one + s[].merge(t[]) + when isMainModule: type Person = object @@ -1012,3 +1055,48 @@ when isMainModule: s2[p2] = 45_000 s3[p1] = 30_000 s3[p2] = 45_000 + + var + t1 = initCountTable[string]() + t2 = initCountTable[string]() + t1.inc("foo") + t1.inc("bar", 2) + t1.inc("baz", 3) + t2.inc("foo", 4) + t2.inc("bar") + t2.inc("baz", 11) + merge(t1, t2) + assert(t1["foo"] == 5) + assert(t1["bar"] == 3) + assert(t1["baz"] == 14) + + let + t1r = newCountTable[string]() + t2r = newCountTable[string]() + t1r.inc("foo") + t1r.inc("bar", 2) + t1r.inc("baz", 3) + t2r.inc("foo", 4) + t2r.inc("bar") + t2r.inc("baz", 11) + merge(t1r, t2r) + assert(t1r["foo"] == 5) + assert(t1r["bar"] == 3) + assert(t1r["baz"] == 14) + + var + t1l = initCountTable[string]() + t2l = initCountTable[string]() + t1l.inc("foo") + t1l.inc("bar", 2) + t1l.inc("baz", 3) + t2l.inc("foo", 4) + t2l.inc("bar") + t2l.inc("baz", 11) + let + t1merging = t1l + t2merging = t2l + let merged = merge(t1merging, t2merging) + assert(merged["foo"] == 5) + assert(merged["bar"] == 3) + assert(merged["baz"] == 14) diff --git a/lib/pure/concurrency/cpuload.nim b/lib/pure/concurrency/cpuload.nim index c1796089a..7ce5e01b7 100644 --- a/lib/pure/concurrency/cpuload.nim +++ b/lib/pure/concurrency/cpuload.nim @@ -78,7 +78,7 @@ proc advice*(s: var ThreadPoolState): ThreadPoolAdvice = result = doNothing inc s.calls -when isMainModule: +when not defined(testing) and isMainModule: proc busyLoop() = while true: discard random(80) diff --git a/lib/pure/concurrency/threadpool.nim b/lib/pure/concurrency/threadpool.nim index 9f1e53fb8..a431691ad 100644 --- a/lib/pure/concurrency/threadpool.nim +++ b/lib/pure/concurrency/threadpool.nim @@ -300,7 +300,7 @@ proc setMinPoolSize*(size: range[1..MaxThreadPoolSize]) = minPoolSize = size proc setMaxPoolSize*(size: range[1..MaxThreadPoolSize]) = - ## sets the minimal thread pool size. The default value of this + ## sets the maximal thread pool size. The default value of this ## is ``MaxThreadPoolSize``. maxPoolSize = size if currentPoolSize > maxPoolSize: diff --git a/lib/pure/cookies.nim b/lib/pure/cookies.nim index 6247efed2..9983c4a04 100644 --- a/lib/pure/cookies.nim +++ b/lib/pure/cookies.nim @@ -56,6 +56,12 @@ proc setCookie*(key, value: string, expires: TimeInfo, when isMainModule: var tim = Time(int(getTime()) + 76 * (60 * 60 * 24)) - echo(setCookie("test", "value", tim.getGMTime())) + let cookie = setCookie("test", "value", tim.getGMTime()) + when not defined(testing): + echo cookie + let start = "Set-Cookie: test=value; Expires=" + assert cookie[0..start.high] == start - echo parseCookies("uid=1; kp=2") + let table = parseCookies("uid=1; kp=2") + assert table["uid"] == "1" + assert table["kp"] == "2" diff --git a/lib/pure/encodings.nim b/lib/pure/encodings.nim index 25c7ad9ef..2a6134615 100644 --- a/lib/pure/encodings.nim +++ b/lib/pure/encodings.nim @@ -451,7 +451,7 @@ proc convert*(s: string, destEncoding = "UTF-8", finally: close(c) -when isMainModule: +when not defined(testing) and isMainModule: let orig = "öäüß" cp1252 = convert(orig, "CP1252", "UTF-8") diff --git a/lib/pure/fsmonitor.nim b/lib/pure/fsmonitor.nim index e6919b661..83779eb9c 100644 --- a/lib/pure/fsmonitor.nim +++ b/lib/pure/fsmonitor.nim @@ -198,7 +198,7 @@ proc register*(d: Dispatcher, monitor: FSMonitor, var deleg = toDelegate(monitor) d.register(deleg) -when isMainModule: +when not defined(testing) and isMainModule: proc main = var disp = newDispatcher() var monitor = newMonitor() diff --git a/lib/pure/ftpclient.nim b/lib/pure/ftpclient.nim index dc387b79c..dd141eb01 100644 --- a/lib/pure/ftpclient.nim +++ b/lib/pure/ftpclient.nim @@ -593,7 +593,7 @@ proc register*(d: Dispatcher, ftp: AsyncFTPClient): Delegate {.discardable.} = ftp.disp = d return ftp.disp.register(ftp.csock) -when isMainModule: +when not defined(testing) and isMainModule: proc main = var d = newDispatcher() let hev = @@ -629,7 +629,7 @@ when isMainModule: if not d.poll(): break main() -when isMainModule and false: +when not defined(testing) and isMainModule: var ftp = ftpClient("example.com", user = "foo", pass = "bar") ftp.connect() echo ftp.pwd() diff --git a/lib/pure/gentabs.nim b/lib/pure/gentabs.nim index a6128efc9..84d0a44de 100644 --- a/lib/pure/gentabs.nim +++ b/lib/pure/gentabs.nim @@ -9,7 +9,7 @@ ## The ``gentabs`` module implements an efficient hash table that is a ## key-value mapping. The keys are required to be strings, but the values -## may be any Nim or user defined type. This module supports matching +## may be any Nim or user defined type. This module supports matching ## of keys in case-sensitive, case-insensitive and style-insensitive modes. {.deprecated.} @@ -22,7 +22,7 @@ type modeCaseSensitive, ## case sensitive matching of keys modeCaseInsensitive, ## case insensitive matching of keys modeStyleInsensitive ## style sensitive matching of keys - + TGenKeyValuePair[T] = tuple[key: string, val: T] TGenKeyValuePairSeq[T] = seq[TGenKeyValuePair[T]] TGenTable*[T] = object of RootObj @@ -83,7 +83,7 @@ proc rawGet[T](tbl: PGenTable[T], key: string): int = h = nextTry(h, high(tbl.data)) result = - 1 -proc rawInsert[T](tbl: PGenTable[T], data: var TGenKeyValuePairSeq[T], +proc rawInsert[T](tbl: PGenTable[T], data: var TGenKeyValuePairSeq[T], key: string, val: T) = var h: THash h = myhash(tbl, key) and high(data) @@ -96,7 +96,7 @@ proc enlarge[T](tbl: PGenTable[T]) = var n: TGenKeyValuePairSeq[T] newSeq(n, len(tbl.data) * growthFactor) for i in countup(0, high(tbl.data)): - if not isNil(tbl.data[i].key): + if not isNil(tbl.data[i].key): rawInsert[T](tbl, n, tbl.data[i].key, tbl.data[i].val) swap(tbl.data, n) @@ -141,20 +141,20 @@ when isMainModule: assert(not x.hasKey("NOPE")) # ...but key "NOPE" is not in the table. for k,v in pairs(x): # make sure the 'pairs' iterator works assert(x[k]==v) - + # # Verify a table of user-defined types # type TMyType = tuple[first, second: string] # a pair of strings - + var y = newGenTable[TMyType](modeCaseInsensitive) # hash table where each # value is TMyType tuple - + #var junk: TMyType = ("OK", "Here") - + #echo junk.first, " ", junk.second - + y["Hello"] = ("Hello", "World") y["Goodbye"] = ("Goodbye", "Everyone") #y["Hello"] = TMyType( ("Hello", "World") ) @@ -163,31 +163,44 @@ when isMainModule: assert( not isNil(y["Hello"].first) ) assert( y["Hello"].first == "Hello" ) assert( y["Hello"].second == "World" ) - + # # Verify table of tables # - var z: PGenTable[ PGenTable[int] ] # hash table where each value is + var z: PGenTable[ PGenTable[int] ] # hash table where each value is # a hash table of ints - + z = newGenTable[PGenTable[int]](modeCaseInsensitive) z["first"] = newGenTable[int](modeCaseInsensitive) z["first"]["one"] = 1 z["first"]["two"] = 2 z["first"]["three"] = 3 - + z["second"] = newGenTable[int](modeCaseInsensitive) z["second"]["red"] = 10 z["second"]["blue"] = 20 - + assert(len(z) == 2) # length of outer table assert(len(z["first"]) == 3) # length of "first" table assert(len(z["second"]) == 2) # length of "second" table assert( z["first"]["one"] == 1) # retrieve from first inner table assert( z["second"]["red"] == 10) # retrieve from second inner table - - for k,v in pairs(z): - echo( "$# ($#) ->" % [k,$len(v)] ) - #for k2,v2 in pairs(v): - # echo( " $# <-> $#" % [k2,$v2] ) - echo() + + when false: + # disabled: depends on hash order: + var output = "" + for k, v in pairs(z): + output.add( "$# ($#) ->\L" % [k,$len(v)] ) + for k2,v2 in pairs(v): + output.add( " $# <-> $#\L" % [k2,$v2] ) + + let expected = unindent """ + first (3) -> + two <-> 2 + three <-> 3 + one <-> 1 + second (2) -> + red <-> 10 + blue <-> 20 + """ + assert output == expected diff --git a/lib/pure/hashes.nim b/lib/pure/hashes.nim index a16342d44..2ce8ac796 100644 --- a/lib/pure/hashes.nim +++ b/lib/pure/hashes.nim @@ -37,29 +37,29 @@ ## h = h !& hash(x.bar) ## result = !$h -import +import strutils -type - THash* = int ## a hash value; hash tables using these values should +type + THash* = int ## a hash value; hash tables using these values should ## always have a size of a power of two and can use the ``and`` ## operator instead of ``mod`` for truncation of the hash value. -proc `!&`*(h: THash, val: int): THash {.inline.} = +proc `!&`*(h: THash, val: int): THash {.inline.} = ## mixes a hash value `h` with `val` to produce a new hash value. This is ## only needed if you need to implement a hash proc for a new datatype. result = h +% val result = result +% result shl 10 result = result xor (result shr 6) -proc `!$`*(h: THash): THash {.inline.} = +proc `!$`*(h: THash): THash {.inline.} = ## finishes the computation of the hash value. This is ## only needed if you need to implement a hash proc for a new datatype. result = h +% h shl 3 result = result xor (result shr 11) result = result +% result shl 15 -proc hashData*(data: pointer, size: int): THash = +proc hashData*(data: pointer, size: int): THash = ## hashes an array of bytes of size `size` var h: THash = 0 when defined(js): @@ -69,7 +69,7 @@ proc hashData*(data: pointer, size: int): THash = var p = cast[cstring](data) var i = 0 var s = size - while s > 0: + while s > 0: h = h !& ord(p[i]) inc(i) dec(s) @@ -78,7 +78,7 @@ proc hashData*(data: pointer, size: int): THash = when defined(js): var objectID = 0 -proc hash*(x: pointer): THash {.inline.} = +proc hash*(x: pointer): THash {.inline.} = ## efficient hashing of pointers when defined(js): asm """ @@ -93,7 +93,7 @@ proc hash*(x: pointer): THash {.inline.} = """ else: result = (cast[THash](x)) shr 3 # skip the alignment - + when not defined(booting): proc hash*[T: proc](x: T): THash {.inline.} = ## efficient hashing of proc vars; closures are supported too. @@ -101,58 +101,65 @@ when not defined(booting): result = hash(rawProc(x)) !& hash(rawEnv(x)) else: result = hash(pointer(x)) - -proc hash*(x: int): THash {.inline.} = + +proc hash*(x: int): THash {.inline.} = ## efficient hashing of integers result = x -proc hash*(x: int64): THash {.inline.} = +proc hash*(x: int64): THash {.inline.} = ## efficient hashing of integers result = toU32(x) -proc hash*(x: char): THash {.inline.} = +proc hash*(x: char): THash {.inline.} = ## efficient hashing of characters result = ord(x) -proc hash*(x: string): THash = +proc hash*(x: string): THash = ## efficient hashing of strings var h: THash = 0 - for i in 0..x.len-1: + for i in 0..x.len-1: h = h !& ord(x[i]) result = !$h - -proc hashIgnoreStyle*(x: string): THash = + +proc hashIgnoreStyle*(x: string): THash = ## efficient hashing of strings; style is ignored var h: THash = 0 - for i in 0..x.len-1: + for i in 0..x.len-1: var c = x[i] - if c == '_': + if c == '_': continue # skip _ - if c in {'A'..'Z'}: + if c in {'A'..'Z'}: c = chr(ord(c) + (ord('a') - ord('A'))) # toLower() h = h !& ord(c) result = !$h -proc hashIgnoreCase*(x: string): THash = +proc hashIgnoreCase*(x: string): THash = ## efficient hashing of strings; case is ignored var h: THash = 0 - for i in 0..x.len-1: + for i in 0..x.len-1: var c = x[i] - if c in {'A'..'Z'}: + if c in {'A'..'Z'}: c = chr(ord(c) + (ord('a') - ord('A'))) # toLower() h = h !& ord(c) result = !$h - -proc hash*[T: tuple](x: T): THash = - ## efficient hashing of tuples. - for f in fields(x): - result = result !& hash(f) - result = !$result proc hash*(x: float): THash {.inline.} = var y = x + 1.0 result = cast[ptr THash](addr(y))[] + +# Forward declarations before methods that hash containers. This allows +# containers to contain other containers +proc hash*[A](x: openArray[A]): THash +proc hash*[A](x: set[A]): THash + + +proc hash*[T: tuple](x: T): THash = + ## efficient hashing of tuples. + for f in fields(x): + result = result !& hash(f) + result = !$result + proc hash*[A](x: openArray[A]): THash = for it in items(x): result = result !& hash(it) result = !$result @@ -160,3 +167,4 @@ proc hash*[A](x: openArray[A]): THash = proc hash*[A](x: set[A]): THash = for it in items(x): result = result !& hash(it) result = !$result + diff --git a/lib/pure/htmlgen.nim b/lib/pure/htmlgen.nim index d712e53f3..e6c15371e 100644 --- a/lib/pure/htmlgen.nim +++ b/lib/pure/htmlgen.nim @@ -483,7 +483,8 @@ macro `var`*(e: expr): expr {.immediate.} = result = xmlCheckedTag(e, "var", commonAttr) when isMainModule: - var nim = "Nim" - echo h1(a(href="http://nim-lang.org", nim)) - echo form(action="test", `accept-charset` = "Content-Type") - + let nim = "Nim" + assert h1(a(href="http://nim-lang.org", nim)) == + """<h1><a href="http://nim-lang.org">Nim</a></h1>""" + assert form(action="test", `accept-charset` = "Content-Type") == + """<form action="test" accept-charset="Content-Type"></form>""" diff --git a/lib/pure/htmlparser.nim b/lib/pure/htmlparser.nim index 5e4eba4e5..9719181b8 100644 --- a/lib/pure/htmlparser.nim +++ b/lib/pure/htmlparser.nim @@ -593,7 +593,7 @@ proc loadHtml*(path: string): XmlNode = var errors: seq[string] = @[] result = loadHtml(path, errors) -when isMainModule: +when not defined(testing) and isMainModule: import os var errors: seq[string] = @[] diff --git a/lib/pure/httpclient.nim b/lib/pure/httpclient.nim index 4c2580da0..9c27ecdab 100644 --- a/lib/pure/httpclient.nim +++ b/lib/pure/httpclient.nim @@ -819,7 +819,7 @@ proc get*(client: AsyncHttpClient, url: string): Future[Response] {.async.} = result = await client.request(redirectTo, httpGET) lastUrl = redirectTo -when isMainModule: +when not defined(testing) and isMainModule: when true: # Async proc main() {.async.} = diff --git a/lib/pure/httpserver.nim b/lib/pure/httpserver.nim index 5efdbe297..dc76c9228 100644 --- a/lib/pure/httpserver.nim +++ b/lib/pure/httpserver.nim @@ -514,7 +514,7 @@ proc close*(h: PAsyncHTTPServer) = ## Closes the ``PAsyncHTTPServer``. h.asyncSocket.close() -when isMainModule: +when not defined(testing) and isMainModule: var counter = 0 var s: TServer diff --git a/lib/pure/json.nim b/lib/pure/json.nim index c3db5bdf8..5d824d6f8 100644 --- a/lib/pure/json.nim +++ b/lib/pure/json.nim @@ -605,6 +605,49 @@ proc newJArray*(): JsonNode = result.kind = JArray result.elems = @[] +proc getStr*(n: JsonNode, default: string = ""): string = + ## Retrieves the string value of a `JString JsonNode`. + ## + ## Returns ``default`` if ``n`` is not a ``JString``. + if n.kind != JString: return default + else: return n.str + +proc getNum*(n: JsonNode, default: BiggestInt = 0): BiggestInt = + ## Retrieves the int value of a `JInt JsonNode`. + ## + ## Returns ``default`` if ``n`` is not a ``JInt``. + if n.kind != JInt: return default + else: return n.num + +proc getFNum*(n: JsonNode, default: float = 0.0): float = + ## Retrieves the float value of a `JFloat JsonNode`. + ## + ## Returns ``default`` if ``n`` is not a ``JFloat``. + if n.kind != JFloat: return default + else: return n.fnum + +proc getBVal*(n: JsonNode, default: bool = false): bool = + ## Retrieves the bool value of a `JBool JsonNode`. + ## + ## Returns ``default`` if ``n`` is not a ``JBool``. + if n.kind != JBool: return default + else: return n.bval + +proc getFields*(n: JsonNode, + default: seq[tuple[key: string, val: JsonNode]] = @[]): + seq[tuple[key: string, val: JsonNode]] = + ## Retrieves the key, value pairs of a `JObject JsonNode`. + ## + ## Returns ``default`` if ``n`` is not a ``JObject``. + if n.kind != JObject: return default + else: return n.fields + +proc getElems*(n: JsonNode, default: seq[JsonNode] = @[]): seq[JsonNode] = + ## Retrieves the int value of a `JArray JsonNode`. + ## + ## Returns ``default`` if ``n`` is not a ``JArray``. + if n.kind != JArray: return default + else: return n.elems proc `%`*(s: string): JsonNode = ## Generic constructor for JSON data. Creates a new `JString JsonNode`. @@ -765,22 +808,25 @@ proc `[]=`*(obj: JsonNode, key: string, val: JsonNode) = return obj.fields.add((key, val)) -proc `{}`*(node: JsonNode, key: string): JsonNode = - ## Transverses the node and gets the given value. If any of the - ## names does not exist, returns nil +proc `{}`*(node: JsonNode, keys: varargs[string]): JsonNode = + ## Traverses the node and gets the given value. If any of the + ## keys do not exist, returns nil. Also returns nil if one of the + ## intermediate data structures is not an object result = node - if isNil(node): return nil - result = result[key] - -proc `{}=`*(node: JsonNode, names: varargs[string], value: JsonNode) = - ## Transverses the node and tries to set the value at the given location - ## to `value` If any of the names are missing, they are added + for key in keys: + if isNil(result) or result.kind!=JObject: + return nil + result=result[key] + +proc `{}=`*(node: JsonNode, keys: varargs[string], value: JsonNode) = + ## Traverses the node and tries to set the value at the given location + ## to `value` If any of the keys are missing, they are added var node = node - for i in 0..(names.len-2): - if isNil(node[names[i]]): - node[names[i]] = newJObject() - node = node[names[i]] - node[names[names.len-1]] = value + for i in 0..(keys.len-2): + if isNil(node[keys[i]]): + node[keys[i]] = newJObject() + node = node[keys[i]] + node[keys[keys.len-1]] = value proc delete*(obj: JsonNode, key: string) = ## Deletes ``obj[key]`` preserving the order of the other (key, value)-pairs. @@ -1107,19 +1153,22 @@ when false: when isMainModule: #var node = parse("{ \"test\": null }") #echo(node.existsKey("test56")) + var parsed = parseFile("tests/testdata/jsontest.json") var parsed2 = parseFile("tests/testdata/jsontest2.json") - echo(parsed) - echo() - echo(pretty(parsed, 2)) - echo() - echo(parsed["keyÄÖöoßß"]) - echo() - echo(pretty(parsed2)) - try: - echo(parsed["key2"][12123]) - raise newException(ValueError, "That line was expected to fail") - except IndexError: echo() + + when not defined(testing): + echo(parsed) + echo() + echo(pretty(parsed, 2)) + echo() + echo(parsed["keyÄÖöoßß"]) + echo() + echo(pretty(parsed2)) + try: + echo(parsed["key2"][12123]) + raise newException(ValueError, "That line was expected to fail") + except IndexError: echo() let testJson = parseJson"""{ "a": [1, 2, 3, 4], "b": "asd" }""" # nil passthrough @@ -1143,9 +1192,17 @@ when isMainModule: except: assert(false, "EInvalidIndex thrown for valid index") + assert(testJson{"b"}.str=="asd", "Couldn't fetch a singly nested key with {}") + assert(isNil(testJson{"nonexistent"}), "Non-existent keys should return nil") + assert(parsed2{"repository", "description"}.str=="IRC Library for Haskell", "Couldn't fetch via multiply nested key using {}") + assert(isNil(testJson{"a", "b"}), "Indexing through a list should return nil") + assert(isNil(testJson{"a", "b"}), "Indexing through a list should return nil") + assert(testJson{"a"}==parseJson"[1, 2, 3, 4]", "Didn't return a non-JObject when there was one to be found") + assert(isNil(parseJson("[1, 2, 3]"){"foo"}), "Indexing directly into a list should return nil") + # Generator: var j = %* [{"name": "John", "age": 30}, {"name": "Susan", "age": 31}] - assert j == %[%{"name": %"John", "age": %30}, %{"name": %"Susan", "age": %31}] + assert j == %[%{"name": %"John", "age": %30}, %{"name": %"Susan", "age": %31}] var j2 = %* [ @@ -1173,12 +1230,13 @@ when isMainModule: } ] assert j3 == %[%{"name": %"John", "age": %30}, %{"name": %"Susan", "age": %31}] - - discard """ - while true: - var json = stdin.readLine() - var node = parse(json) - echo(node) - echo() - echo() - """ + + when not defined(testing): + discard """ + while true: + var json = stdin.readLine() + var node = parse(json) + echo(node) + echo() + echo() + """ diff --git a/lib/pure/logging.nim b/lib/pure/logging.nim index ca674af4b..a2ea53472 100644 --- a/lib/pure/logging.nim +++ b/lib/pure/logging.nim @@ -278,7 +278,7 @@ proc getLogFilter*(): Level = # -------------- -when isMainModule: +when not defined(testing) and isMainModule: var L = newConsoleLogger() var fL = newFileLogger("test.log", fmtStr = verboseFmtStr) var rL = newRollingFileLogger("rolling.log", fmtStr = verboseFmtStr) diff --git a/lib/pure/marshal.nim b/lib/pure/marshal.nim index b63c334ff..e0092f314 100644 --- a/lib/pure/marshal.nim +++ b/lib/pure/marshal.nim @@ -15,8 +15,8 @@ ## type than its compiletime type: ## ## .. code-block:: nim -## -## type +## +## type ## TA = object ## TB = object of TA ## f: int @@ -28,6 +28,8 @@ ## new(b) ## a = b ## echo($$a[]) # produces "{}", not "{f: 0}" +## +## **Note**: The ``to`` and ``$$`` operations are available at compile-time! import streams, typeinfo, json, intsets, tables @@ -38,7 +40,12 @@ proc storeAny(s: Stream, a: TAny, stored: var IntSet) = case a.kind of akNone: assert false of akBool: s.write($getBool(a)) - of akChar: s.write(escapeJson($getChar(a))) + of akChar: + let ch = getChar(a) + if ch < '\128': + s.write(escapeJson($ch)) + else: + s.write($int(ch)) of akArray, akSequence: if a.kind == akSequence and isNil(a): s.write("null") else: @@ -92,7 +99,7 @@ proc storeAny(s: Stream, a: TAny, stored: var IntSet) = proc loadAny(p: var JsonParser, a: TAny, t: var Table[BiggestInt, pointer]) = case a.kind of akNone: assert false - of akBool: + of akBool: case p.kind of jsonFalse: setBiggestInt(a, 0) of jsonTrue: setBiggestInt(a, 1) @@ -105,8 +112,12 @@ proc loadAny(p: var JsonParser, a: TAny, t: var Table[BiggestInt, pointer]) = setBiggestInt(a, ord(x[0])) next(p) return + elif p.kind == jsonInt: + setBiggestInt(a, getInt(p)) + next(p) + return raiseParseErr(p, "string of length 1 expected for a char") - of akEnum: + of akEnum: if p.kind == jsonString: setBiggestInt(a, getEnumOrdinal(a, p.str)) next(p) @@ -122,7 +133,7 @@ proc loadAny(p: var JsonParser, a: TAny, t: var Table[BiggestInt, pointer]) = if p.kind == jsonArrayEnd: next(p) else: raiseParseErr(p, "']' end of array expected") of akSequence: - case p.kind + case p.kind of jsonNull: setPointer(a, nil) next(p) @@ -143,7 +154,7 @@ proc loadAny(p: var JsonParser, a: TAny, t: var Table[BiggestInt, pointer]) = if p.kind != jsonObjectStart: raiseParseErr(p, "'{' expected for an object") next(p) while p.kind != jsonObjectEnd and p.kind != jsonEof: - if p.kind != jsonString: + if p.kind != jsonString: raiseParseErr(p, "string expected for a field name") var fieldName = p.str next(p) @@ -160,7 +171,7 @@ proc loadAny(p: var JsonParser, a: TAny, t: var Table[BiggestInt, pointer]) = if p.kind == jsonArrayEnd: next(p) else: raiseParseErr(p, "']' end of array expected") of akPtr, akRef: - case p.kind + case p.kind of jsonNull: setPointer(a, nil) next(p) @@ -170,7 +181,7 @@ proc loadAny(p: var JsonParser, a: TAny, t: var Table[BiggestInt, pointer]) = of jsonArrayStart: next(p) if a.kind == akRef: invokeNew(a) - else: setPointer(a, alloc0(a.baseTypeSize)) + else: setPointer(a, alloc0(a.baseTypeSize)) if p.kind == jsonInt: t[p.getInt] = getPointer(a) next(p) @@ -179,8 +190,8 @@ proc loadAny(p: var JsonParser, a: TAny, t: var Table[BiggestInt, pointer]) = if p.kind == jsonArrayEnd: next(p) else: raiseParseErr(p, "']' end of ref-address pair expected") else: raiseParseErr(p, "int for pointer type expected") - of akProc, akPointer, akCString: - case p.kind + of akProc, akPointer, akCString: + case p.kind of jsonNull: setPointer(a, nil) next(p) @@ -189,7 +200,7 @@ proc loadAny(p: var JsonParser, a: TAny, t: var Table[BiggestInt, pointer]) = next(p) else: raiseParseErr(p, "int for pointer type expected") of akString: - case p.kind + case p.kind of jsonNull: setPointer(a, nil) next(p) @@ -197,7 +208,7 @@ proc loadAny(p: var JsonParser, a: TAny, t: var Table[BiggestInt, pointer]) = setString(a, p.str) next(p) else: raiseParseErr(p, "string expected") - of akInt..akInt64, akUInt..akUInt64: + of akInt..akInt64, akUInt..akUInt64: if p.kind == jsonInt: setBiggestInt(a, getInt(p)) next(p) @@ -243,22 +254,22 @@ proc to*[T](data: string): T = ## reads data and transforms it to a ``T``. var tab = initTable[BiggestInt, pointer]() loadAny(newStringStream(data), toAny(result), tab) - -when isMainModule: + +when not defined(testing) and isMainModule: template testit(x: expr) = echo($$to[type(x)]($$x)) var x: array[0..4, array[0..4, string]] = [ - ["test", "1", "2", "3", "4"], ["test", "1", "2", "3", "4"], - ["test", "1", "2", "3", "4"], ["test", "1", "2", "3", "4"], + ["test", "1", "2", "3", "4"], ["test", "1", "2", "3", "4"], + ["test", "1", "2", "3", "4"], ["test", "1", "2", "3", "4"], ["test", "1", "2", "3", "4"]] testit(x) var test2: tuple[name: string, s: uint] = ("tuple test", 56u) testit(test2) - + type TE = enum blah, blah2 - + TestObj = object test, asd: int case test2: TE @@ -266,7 +277,7 @@ when isMainModule: help: string else: nil - + PNode = ref TNode TNode = object next, prev: PNode @@ -294,7 +305,7 @@ when isMainModule: test4.a = "ref string test: A" test4.b = "ref string test: B" testit(test4) - + var test5 = @[(0,1),(2,3),(4,5)] testit(test5) @@ -305,7 +316,7 @@ when isMainModule: echo($$test7) testit(test7) - type + type TA {.inheritable.} = object TB = object of TA f: int diff --git a/lib/pure/math.nim b/lib/pure/math.nim index c902af381..cb58ea39b 100644 --- a/lib/pure/math.nim +++ b/lib/pure/math.nim @@ -85,7 +85,7 @@ proc fac*(n: int): int {.noSideEffect.} = proc isPowerOfTwo*(x: int): bool {.noSideEffect.} = ## returns true, if `x` is a power of two, false otherwise. ## Zero and negative numbers are not a power of two. - return (x != 0) and ((x and (x - 1)) == 0) + return (x > 0) and ((x and (x - 1)) == 0) proc nextPowerOfTwo*(x: int): int {.noSideEffect.} = ## returns `x` rounded up to the nearest power of two. @@ -114,18 +114,23 @@ proc sum*[T](x: openArray[T]): T {.noSideEffect.} = ## If `x` is empty, 0 is returned. for i in items(x): result = result + i -proc mean*(x: openArray[float]): float {.noSideEffect.} = - ## computes the mean of the elements in `x`. +template toFloat(f: float): float = f + +proc mean*[T](x: openArray[T]): float {.noSideEffect.} = + ## computes the mean of the elements in `x`, which are first converted to floats. ## If `x` is empty, NaN is returned. - result = sum(x) / toFloat(len(x)) + ## ``toFloat(x: T): float`` must be defined. + for i in items(x): result = result + toFloat(i) + result = result / toFloat(len(x)) -proc variance*(x: openArray[float]): float {.noSideEffect.} = +proc variance*[T](x: openArray[T]): float {.noSideEffect.} = ## computes the variance of the elements in `x`. ## If `x` is empty, NaN is returned. + ## ``toFloat(x: T): float`` must be defined. result = 0.0 var m = mean(x) - for i in 0 .. high(x): - var diff = x[i] - m + for i in items(x): + var diff = toFloat(i) - m result = result + diff*diff result = result / toFloat(len(x)) @@ -152,6 +157,7 @@ proc randomize*(seed: int) {.benign.} ## Note: Does nothing for the JavaScript target, ## as JavaScript does not support this. +{.push noSideEffect.} when not defined(JS): proc sqrt*(x: float): float {.importc: "sqrt", header: "<math.h>".} ## computes the square root of `x`. @@ -273,6 +279,8 @@ else: var y = exp(2.0*x) return (y-1.0)/(y+1.0) +{.pop.} + proc `mod`*(x, y: float): float = result = if y == 0.0: x else: x - y * (x/y).floor @@ -369,4 +377,10 @@ when isMainModule and not defined(JS): randomize(seed) for i in 0..SIZE-1: assert buf[i] == random(high(int)), "non deterministic random seeding" - echo "random values equal after reseeding" + + when not defined(testing): + echo "random values equal after reseeding" + + # Check for no side effect annotation + proc mySqrt(num: float): float {.noSideEffect.} = + return sqrt(num) diff --git a/lib/pure/mersenne.nim b/lib/pure/mersenne.nim index a6a781cb8..74112e304 100644 --- a/lib/pure/mersenne.nim +++ b/lib/pure/mersenne.nim @@ -32,7 +32,7 @@ proc getNum*(m: var MersenneTwister): int = return int(y) # Test -when isMainModule: +when not defined(testing) and isMainModule: var mt = newMersenneTwister(2525) for i in 0..99: diff --git a/lib/pure/mimetypes.nim b/lib/pure/mimetypes.nim index a52ba4ebe..642419e64 100644 --- a/lib/pure/mimetypes.nim +++ b/lib/pure/mimetypes.nim @@ -518,5 +518,5 @@ proc register*(mimedb: var MimeDB, ext: string, mimetype: string) = when isMainModule: var m = newMimetypes() - echo m.getMimetype("mp4") - echo m.getExt("text/html") + assert m.getMimetype("mp4") == "video/mp4" + assert m.getExt("text/html") == "html" diff --git a/lib/pure/oids.nim b/lib/pure/oids.nim index 0dc8e3c15..ac90dd16b 100644 --- a/lib/pure/oids.nim +++ b/lib/pure/oids.nim @@ -88,6 +88,6 @@ proc generatedTime*(oid: Oid): Time = bigEndian32(addr(tmp), addr(dummy)) result = Time(tmp) -when isMainModule: +when not defined(testing) and isMainModule: let xo = genOid() echo xo.generatedTime diff --git a/lib/pure/osproc.nim b/lib/pure/osproc.nim index cd3700019..dce0673ba 100644 --- a/lib/pure/osproc.nim +++ b/lib/pure/osproc.nim @@ -137,7 +137,7 @@ proc startProcess*(command: string, ## `env` is the environment that will be passed to the process. ## If ``env == nil`` the environment is inherited of ## the parent process. `options` are additional flags that may be passed - ## to `startProcess`. See the documentation of ``TProcessOption`` for the + ## to `startProcess`. See the documentation of ``ProcessOption`` for the ## meaning of these flags. You need to `close` the process when done. ## ## Note that you can't pass any `args` if you use the option @@ -174,7 +174,7 @@ proc terminate*(p: Process) {.rtl, extern: "nosp$1", tags: [].} proc kill*(p: Process) {.rtl, extern: "nosp$1", tags: [].} ## Kill the process `p`. On Posix OSes the procedure sends ``SIGKILL`` to ## the process. On Windows ``kill()`` is simply an alias for ``terminate()``. - + proc running*(p: Process): bool {.rtl, extern: "nosp$1", tags: [].} ## Returns true iff the process `p` is still running. Returns immediately. @@ -666,7 +666,7 @@ elif not defined(useNimRtl): data.workingDir = workingDir - when declared(posix_spawn) and not defined(useFork) and + when declared(posix_spawn) and not defined(useFork) and not defined(useClone) and not defined(linux): pid = startProcessAuxSpawn(data) else: @@ -823,7 +823,7 @@ elif not defined(useNimRtl): discard execvp(data.sysCommand, data.sysArgs) else: when defined(uClibc): - # uClibc environment (OpenWrt included) doesn't have the full execvpe + # uClibc environment (OpenWrt included) doesn't have the full execvpe discard execve(data.sysCommand, data.sysArgs, data.sysEnv) else: discard execvpe(data.sysCommand, data.sysArgs, data.sysEnv) @@ -864,9 +864,9 @@ elif not defined(useNimRtl): raiseOsError(osLastError()) proc kill(p: Process) = - if kill(p.id, SIGKILL) != 0'i32: + if kill(p.id, SIGKILL) != 0'i32: raiseOsError(osLastError()) - + proc waitForExit(p: Process, timeout: int = -1): int = #if waitPid(p.id, p.exitCode, 0) == int(p.id): # ``waitPid`` fails if the process is not running anymore. But then @@ -883,7 +883,7 @@ elif not defined(useNimRtl): var ret = waitpid(p.id, p.exitCode, WNOHANG) var b = ret == int(p.id) if b: result = -1 - if p.exitCode == -3: result = -1 + if not WIFEXITED(p.exitCode): result = -1 else: result = p.exitCode.int shr 8 proc createStream(stream: var Stream, handle: var FileHandle, @@ -907,7 +907,7 @@ elif not defined(useNimRtl): createStream(p.errStream, p.errHandle, fmRead) return p.errStream - proc csystem(cmd: cstring): cint {.nodecl, importc: "system", + proc csystem(cmd: cstring): cint {.nodecl, importc: "system", header: "<stdlib.h>".} proc execCmd(command: string): int = diff --git a/lib/pure/parsecsv.nim b/lib/pure/parsecsv.nim index f4943ed89..117d75cfa 100644 --- a/lib/pure/parsecsv.nim +++ b/lib/pure/parsecsv.nim @@ -166,7 +166,7 @@ proc close*(my: var CsvParser) {.inline.} = ## closes the parser `my` and its associated input stream. lexbase.close(my) -when isMainModule: +when not defined(testing) and isMainModule: import os var s = newFileStream(paramStr(1), fmRead) if s == nil: quit("cannot open the file" & paramStr(1)) diff --git a/lib/pure/parsesql.nim b/lib/pure/parsesql.nim index bb4ede779..91917b1c5 100644 --- a/lib/pure/parsesql.nim +++ b/lib/pure/parsesql.nim @@ -1330,7 +1330,7 @@ proc renderSQL*(n: SqlNode): string = result = "" ra(n, result, 0) -when isMainModule: +when not defined(testing) and isMainModule: echo(renderSQL(parseSQL(newStringStream(""" CREATE TYPE happiness AS ENUM ('happy', 'very happy', 'ecstatic'); CREATE TABLE holidays ( diff --git a/lib/pure/parseutils.nim b/lib/pure/parseutils.nim index eb649a878..c07b713de 100644 --- a/lib/pure/parseutils.nim +++ b/lib/pure/parseutils.nim @@ -323,8 +323,12 @@ iterator interpolatedFragments*(s: string): tuple[kind: InterpolatedKind, i = j when isMainModule: - for k, v in interpolatedFragments("$test{} $this is ${an{ example}} "): - echo "(", k, ", \"", v, "\")" + import sequtils + let input = "$test{} $this is ${an{ example}} " + let expected = @[(ikVar, "test"), (ikStr, "{} "), (ikVar, "this"), + (ikStr, " is "), (ikExpr, "an{ example}"), (ikStr, " ")] + assert toSeq(interpolatedFragments(input)) == expected + var value = 0 discard parseHex("0x38", value) assert value == 56 diff --git a/lib/pure/parsexml.nim b/lib/pure/parsexml.nim index 2663c5b2f..eb792f086 100644 --- a/lib/pure/parsexml.nim +++ b/lib/pure/parsexml.nim @@ -628,7 +628,7 @@ proc next*(my: var XmlParser) = my.kind = xmlError my.state = stateNormal -when isMainModule: +when not defined(testing) and isMainModule: import os var s = newFileStream(paramStr(1), fmRead) if s == nil: quit("cannot open the file" & paramStr(1)) diff --git a/lib/pure/pegs.nimfix b/lib/pure/pegs.nimfix deleted file mode 100644 index 15bc95351..000000000 --- a/lib/pure/pegs.nimfix +++ /dev/null @@ -1,1770 +0,0 @@ -# -# -# Nim's Runtime Library -# (c) Copyright 2012 Andreas Rumpf -# -# See the file "copying.txt", included in this -# distribution, for details about the copyright. -# - -## Simple PEG (Parsing expression grammar) matching. Uses no memorization, but -## uses superoperators and symbol inlining to improve performance. Note: -## Matching performance is hopefully competitive with optimized regular -## expression engines. -## -## .. include:: ../doc/pegdocs.txt -## - -include "system/inclrtl" - -const - useUnicode = true ## change this to deactivate proper UTF-8 support - -import - strutils - -when useUnicode: - import unicode - -const - InlineThreshold = 5 ## number of leaves; -1 to disable inlining - MaxSubpatterns* = 10 ## defines the maximum number of subpatterns that - ## can be captured. More subpatterns cannot be captured! - -type - PegKind = enum - pkEmpty, - pkAny, ## any character (.) - pkAnyRune, ## any Unicode character (_) - pkNewLine, ## CR-LF, LF, CR - pkLetter, ## Unicode letter - pkLower, ## Unicode lower case letter - pkUpper, ## Unicode upper case letter - pkTitle, ## Unicode title character - pkWhitespace, ## Unicode whitespace character - pkTerminal, - pkTerminalIgnoreCase, - pkTerminalIgnoreStyle, - pkChar, ## single character to match - pkCharChoice, - pkNonTerminal, - pkSequence, ## a b c ... --> Internal DSL: peg(a, b, c) - pkOrderedChoice, ## a / b / ... --> Internal DSL: a / b or /[a, b, c] - pkGreedyRep, ## a* --> Internal DSL: *a - ## a+ --> (a a*) - pkGreedyRepChar, ## x* where x is a single character (superop) - pkGreedyRepSet, ## [set]* (superop) - pkGreedyAny, ## .* or _* (superop) - pkOption, ## a? --> Internal DSL: ?a - pkAndPredicate, ## &a --> Internal DSL: &a - pkNotPredicate, ## !a --> Internal DSL: !a - pkCapture, ## {a} --> Internal DSL: capture(a) - pkBackRef, ## $i --> Internal DSL: backref(i) - pkBackRefIgnoreCase, - pkBackRefIgnoreStyle, - pkSearch, ## @a --> Internal DSL: !*a - pkCapturedSearch, ## {@} a --> Internal DSL: !*\a - pkRule, ## a <- b - pkList, ## a, b - pkStartAnchor ## ^ --> Internal DSL: startAnchor() - NonTerminalFlag = enum - ntDeclared, ntUsed - NonTerminalObj = object ## represents a non terminal symbol - name: string ## the name of the symbol - line: int ## line the symbol has been declared/used in - col: int ## column the symbol has been declared/used in - flags: set[NonTerminalFlag] ## the nonterminal's flags - rule: TNode ## the rule that the symbol refers to - TNode {.shallow.} = object - case kind: PegKind - of pkEmpty..pkWhitespace: nil - of pkTerminal, pkTerminalIgnoreCase, pkTerminalIgnoreStyle: term: string - of pkChar, pkGreedyRepChar: ch: char - of pkCharChoice, pkGreedyRepSet: charChoice: ref set[char] - of pkNonTerminal: nt: PNonTerminal - of pkBackRef..pkBackRefIgnoreStyle: index: range[0..MaxSubpatterns] - else: sons: seq[TNode] - PNonTerminal* = ref NonTerminalObj - TPeg* = TNode - -block: - type - Peg = TNode - NonTerminal = PNonTerminal - {.deprecated: [TPeg: Peg, PNonTerminal: NonTerminal].} - -proc term*(t: string): TPeg {.nosideEffect, rtl, extern: "npegs$1Str".} = - ## constructs a PEG from a terminal string - if t.len != 1: - result.kind = pkTerminal - result.term = t - else: - result.kind = pkChar - result.ch = t[0] - -proc termIgnoreCase*(t: string): TPeg {.nosideEffect, rtl, extern: "npegs$1".} = - ## constructs a PEG from a terminal string; ignore case for matching - result.kind = pkTerminalIgnoreCase - result.term = t - -proc termIgnoreStyle*(t: string): TPeg {.nosideEffect, rtl, extern: "npegs$1".} = - ## constructs a PEG from a terminal string; ignore style for matching - result.kind = pkTerminalIgnoreStyle - result.term = t - -proc term*(t: char): TPeg {.nosideEffect, rtl, extern: "npegs$1Char".} = - ## constructs a PEG from a terminal char - assert t != '\0' - result.kind = pkChar - result.ch = t - -proc charSet*(s: set[char]): TPeg {.nosideEffect, rtl, extern: "npegs$1".} = - ## constructs a PEG from a character set `s` - assert '\0' notin s - result.kind = pkCharChoice - new(result.charChoice) - result.charChoice[] = s - -proc len(a: TPeg): int {.inline.} = return a.sons.len -proc add(d: var TPeg, s: TPeg) {.inline.} = add(d.sons, s) - -proc addChoice(dest: var TPeg, elem: TPeg) = - var L = dest.len-1 - if L >= 0 and dest.sons[L].kind == pkCharChoice: - # caution! Do not introduce false aliasing here! - case elem.kind - of pkCharChoice: - dest.sons[L] = charSet(dest.sons[L].charChoice[] + elem.charChoice[]) - of pkChar: - dest.sons[L] = charSet(dest.sons[L].charChoice[] + {elem.ch}) - else: add(dest, elem) - else: add(dest, elem) - -template multipleOp(k: PegKind, localOpt: expr) = - result.kind = k - result.sons = @[] - for x in items(a): - if x.kind == k: - for y in items(x.sons): - localOpt(result, y) - else: - localOpt(result, x) - if result.len == 1: - result = result.sons[0] - -proc `/`*(a: varargs[TPeg]): TPeg {. - nosideEffect, rtl, extern: "npegsOrderedChoice".} = - ## constructs an ordered choice with the PEGs in `a` - multipleOp(pkOrderedChoice, addChoice) - -proc addSequence(dest: var TPeg, elem: TPeg) = - var L = dest.len-1 - if L >= 0 and dest.sons[L].kind == pkTerminal: - # caution! Do not introduce false aliasing here! - case elem.kind - of pkTerminal: - dest.sons[L] = term(dest.sons[L].term & elem.term) - of pkChar: - dest.sons[L] = term(dest.sons[L].term & elem.ch) - else: add(dest, elem) - else: add(dest, elem) - -proc sequence*(a: varargs[TPeg]): TPeg {. - nosideEffect, rtl, extern: "npegs$1".} = - ## constructs a sequence with all the PEGs from `a` - multipleOp(pkSequence, addSequence) - -proc `?`*(a: TPeg): TPeg {.nosideEffect, rtl, extern: "npegsOptional".} = - ## constructs an optional for the PEG `a` - if a.kind in {pkOption, pkGreedyRep, pkGreedyAny, pkGreedyRepChar, - pkGreedyRepSet}: - # a* ? --> a* - # a? ? --> a? - result = a - else: - result.kind = pkOption - result.sons = @[a] - -proc `*`*(a: TPeg): TPeg {.nosideEffect, rtl, extern: "npegsGreedyRep".} = - ## constructs a "greedy repetition" for the PEG `a` - case a.kind - of pkGreedyRep, pkGreedyRepChar, pkGreedyRepSet, pkGreedyAny, pkOption: - assert false - # produces endless loop! - of pkChar: - result.kind = pkGreedyRepChar - result.ch = a.ch - of pkCharChoice: - result.kind = pkGreedyRepSet - result.charChoice = a.charChoice # copying a reference suffices! - of pkAny, pkAnyRune: - result.kind = pkGreedyAny - else: - result.kind = pkGreedyRep - result.sons = @[a] - -proc `!*`*(a: TPeg): TPeg {.nosideEffect, rtl, extern: "npegsSearch".} = - ## constructs a "search" for the PEG `a` - result.kind = pkSearch - result.sons = @[a] - -proc `!*\`*(a: TPeg): TPeg {.noSideEffect, rtl, - extern: "npgegsCapturedSearch".} = - ## constructs a "captured search" for the PEG `a` - result.kind = pkCapturedSearch - result.sons = @[a] - -proc `+`*(a: TPeg): TPeg {.nosideEffect, rtl, extern: "npegsGreedyPosRep".} = - ## constructs a "greedy positive repetition" with the PEG `a` - return sequence(a, *a) - -proc `&`*(a: TPeg): TPeg {.nosideEffect, rtl, extern: "npegsAndPredicate".} = - ## constructs an "and predicate" with the PEG `a` - result.kind = pkAndPredicate - result.sons = @[a] - -proc `!`*(a: TPeg): TPeg {.nosideEffect, rtl, extern: "npegsNotPredicate".} = - ## constructs a "not predicate" with the PEG `a` - result.kind = pkNotPredicate - result.sons = @[a] - -proc any*: TPeg {.inline.} = - ## constructs the PEG `any character`:idx: (``.``) - result.kind = pkAny - -proc anyRune*: TPeg {.inline.} = - ## constructs the PEG `any rune`:idx: (``_``) - result.kind = pkAnyRune - -proc newLine*: TPeg {.inline.} = - ## constructs the PEG `newline`:idx: (``\n``) - result.kind = pkNewLine - -proc unicodeLetter*: TPeg {.inline.} = - ## constructs the PEG ``\letter`` which matches any Unicode letter. - result.kind = pkLetter - -proc unicodeLower*: TPeg {.inline.} = - ## constructs the PEG ``\lower`` which matches any Unicode lowercase letter. - result.kind = pkLower - -proc unicodeUpper*: TPeg {.inline.} = - ## constructs the PEG ``\upper`` which matches any Unicode uppercase letter. - result.kind = pkUpper - -proc unicodeTitle*: TPeg {.inline.} = - ## constructs the PEG ``\title`` which matches any Unicode title letter. - result.kind = pkTitle - -proc unicodeWhitespace*: TPeg {.inline.} = - ## constructs the PEG ``\white`` which matches any Unicode - ## whitespace character. - result.kind = pkWhitespace - -proc startAnchor*: TPeg {.inline.} = - ## constructs the PEG ``^`` which matches the start of the input. - result.kind = pkStartAnchor - -proc endAnchor*: TPeg {.inline.} = - ## constructs the PEG ``$`` which matches the end of the input. - result = !any() - -proc capture*(a: TPeg): TPeg {.nosideEffect, rtl, extern: "npegsCapture".} = - ## constructs a capture with the PEG `a` - result.kind = pkCapture - result.sons = @[a] - -proc backref*(index: range[1..MaxSubpatterns]): TPeg {. - nosideEffect, rtl, extern: "npegs$1".} = - ## constructs a back reference of the given `index`. `index` starts counting - ## from 1. - result.kind = pkBackRef - result.index = index-1 - -proc backrefIgnoreCase*(index: range[1..MaxSubpatterns]): TPeg {. - nosideEffect, rtl, extern: "npegs$1".} = - ## constructs a back reference of the given `index`. `index` starts counting - ## from 1. Ignores case for matching. - result.kind = pkBackRefIgnoreCase - result.index = index-1 - -proc backrefIgnoreStyle*(index: range[1..MaxSubpatterns]): TPeg {. - nosideEffect, rtl, extern: "npegs$1".}= - ## constructs a back reference of the given `index`. `index` starts counting - ## from 1. Ignores style for matching. - result.kind = pkBackRefIgnoreStyle - result.index = index-1 - -proc spaceCost(n: TPeg): int = - case n.kind - of pkEmpty: discard - of pkTerminal, pkTerminalIgnoreCase, pkTerminalIgnoreStyle, pkChar, - pkGreedyRepChar, pkCharChoice, pkGreedyRepSet, - pkAny..pkWhitespace, pkGreedyAny: - result = 1 - of pkNonTerminal: - # we cannot inline a rule with a non-terminal - result = InlineThreshold+1 - else: - for i in 0..n.len-1: - inc(result, spaceCost(n.sons[i])) - if result >= InlineThreshold: break - -proc nonterminal*(n: PNonTerminal): TPeg {. - nosideEffect, rtl, extern: "npegs$1".} = - ## constructs a PEG that consists of the nonterminal symbol - assert n != nil - if ntDeclared in n.flags and spaceCost(n.rule) < InlineThreshold: - when false: echo "inlining symbol: ", n.name - result = n.rule # inlining of rule enables better optimizations - else: - result.kind = pkNonTerminal - result.nt = n - -proc newNonTerminal*(name: string, line, column: int): PNonTerminal {. - nosideEffect, rtl, extern: "npegs$1".} = - ## constructs a nonterminal symbol - new(result) - result.name = name - result.line = line - result.col = column - -template letters*: expr = - ## expands to ``charset({'A'..'Z', 'a'..'z'})`` - charSet({'A'..'Z', 'a'..'z'}) - -template digits*: expr = - ## expands to ``charset({'0'..'9'})`` - charSet({'0'..'9'}) - -template whitespace*: expr = - ## expands to ``charset({' ', '\9'..'\13'})`` - charSet({' ', '\9'..'\13'}) - -template identChars*: expr = - ## expands to ``charset({'a'..'z', 'A'..'Z', '0'..'9', '_'})`` - charSet({'a'..'z', 'A'..'Z', '0'..'9', '_'}) - -template identStartChars*: expr = - ## expands to ``charset({'A'..'Z', 'a'..'z', '_'})`` - charSet({'a'..'z', 'A'..'Z', '_'}) - -template ident*: expr = - ## same as ``[a-zA-Z_][a-zA-z_0-9]*``; standard identifier - sequence(charSet({'a'..'z', 'A'..'Z', '_'}), - *charSet({'a'..'z', 'A'..'Z', '0'..'9', '_'})) - -template natural*: expr = - ## same as ``\d+`` - +digits - -# ------------------------- debugging ----------------------------------------- - -proc esc(c: char, reserved = {'\0'..'\255'}): string = - case c - of '\b': result = "\\b" - of '\t': result = "\\t" - of '\c': result = "\\c" - of '\L': result = "\\l" - of '\v': result = "\\v" - of '\f': result = "\\f" - of '\e': result = "\\e" - of '\a': result = "\\a" - of '\\': result = "\\\\" - of 'a'..'z', 'A'..'Z', '0'..'9', '_': result = $c - elif c < ' ' or c >= '\128': result = '\\' & $ord(c) - elif c in reserved: result = '\\' & c - else: result = $c - -proc singleQuoteEsc(c: char): string = return "'" & esc(c, {'\''}) & "'" - -proc singleQuoteEsc(str: string): string = - result = "'" - for c in items(str): add result, esc(c, {'\''}) - add result, '\'' - -proc charSetEscAux(cc: set[char]): string = - const reserved = {'^', '-', ']'} - result = "" - var c1 = 0 - while c1 <= 0xff: - if chr(c1) in cc: - var c2 = c1 - while c2 < 0xff and chr(succ(c2)) in cc: inc(c2) - if c1 == c2: - add result, esc(chr(c1), reserved) - elif c2 == succ(c1): - add result, esc(chr(c1), reserved) & esc(chr(c2), reserved) - else: - add result, esc(chr(c1), reserved) & '-' & esc(chr(c2), reserved) - c1 = c2 - inc(c1) - -proc charSetEsc(cc: set[char]): string = - if card(cc) >= 128+64: - result = "[^" & charSetEscAux({'\1'..'\xFF'} - cc) & ']' - else: - result = '[' & charSetEscAux(cc) & ']' - -proc toStrAux(r: TPeg, res: var string) = - case r.kind - of pkEmpty: add(res, "()") - of pkAny: add(res, '.') - of pkAnyRune: add(res, '_') - of pkLetter: add(res, "\\letter") - of pkLower: add(res, "\\lower") - of pkUpper: add(res, "\\upper") - of pkTitle: add(res, "\\title") - of pkWhitespace: add(res, "\\white") - - of pkNewLine: add(res, "\\n") - of pkTerminal: add(res, singleQuoteEsc(r.term)) - of pkTerminalIgnoreCase: - add(res, 'i') - add(res, singleQuoteEsc(r.term)) - of pkTerminalIgnoreStyle: - add(res, 'y') - add(res, singleQuoteEsc(r.term)) - of pkChar: add(res, singleQuoteEsc(r.ch)) - of pkCharChoice: add(res, charSetEsc(r.charChoice[])) - of pkNonTerminal: add(res, r.nt.name) - of pkSequence: - add(res, '(') - toStrAux(r.sons[0], res) - for i in 1 .. high(r.sons): - add(res, ' ') - toStrAux(r.sons[i], res) - add(res, ')') - of pkOrderedChoice: - add(res, '(') - toStrAux(r.sons[0], res) - for i in 1 .. high(r.sons): - add(res, " / ") - toStrAux(r.sons[i], res) - add(res, ')') - of pkGreedyRep: - toStrAux(r.sons[0], res) - add(res, '*') - of pkGreedyRepChar: - add(res, singleQuoteEsc(r.ch)) - add(res, '*') - of pkGreedyRepSet: - add(res, charSetEsc(r.charChoice[])) - add(res, '*') - of pkGreedyAny: - add(res, ".*") - of pkOption: - toStrAux(r.sons[0], res) - add(res, '?') - of pkAndPredicate: - add(res, '&') - toStrAux(r.sons[0], res) - of pkNotPredicate: - add(res, '!') - toStrAux(r.sons[0], res) - of pkSearch: - add(res, '@') - toStrAux(r.sons[0], res) - of pkCapturedSearch: - add(res, "{@}") - toStrAux(r.sons[0], res) - of pkCapture: - add(res, '{') - toStrAux(r.sons[0], res) - add(res, '}') - of pkBackRef: - add(res, '$') - add(res, $r.index) - of pkBackRefIgnoreCase: - add(res, "i$") - add(res, $r.index) - of pkBackRefIgnoreStyle: - add(res, "y$") - add(res, $r.index) - of pkRule: - toStrAux(r.sons[0], res) - add(res, " <- ") - toStrAux(r.sons[1], res) - of pkList: - for i in 0 .. high(r.sons): - toStrAux(r.sons[i], res) - add(res, "\n") - of pkStartAnchor: - add(res, '^') - -proc `$` *(r: TPeg): string {.nosideEffect, rtl, extern: "npegsToString".} = - ## converts a PEG to its string representation - result = "" - toStrAux(r, result) - -# --------------------- core engine ------------------------------------------- - -type - Captures* = object ## contains the captured substrings. - matches: array[0..MaxSubpatterns-1, tuple[first, last: int]] - ml: int - origStart: int - -{.deprecated: [TCaptures: Captures].} - -proc bounds*(c: Captures, - i: range[0..MaxSubpatterns-1]): tuple[first, last: int] = - ## returns the bounds ``[first..last]`` of the `i`'th capture. - result = c.matches[i] - -when not useUnicode: - type - Rune = char - template fastRuneAt(s, i, ch: expr) = - ch = s[i] - inc(i) - template runeLenAt(s, i: expr): expr = 1 - - proc isAlpha(a: char): bool {.inline.} = return a in {'a'..'z','A'..'Z'} - proc isUpper(a: char): bool {.inline.} = return a in {'A'..'Z'} - proc isLower(a: char): bool {.inline.} = return a in {'a'..'z'} - proc isTitle(a: char): bool {.inline.} = return false - proc isWhiteSpace(a: char): bool {.inline.} = return a in {' ', '\9'..'\13'} - -proc rawMatch*(s: string, p: TPeg, start: int, c: var Captures): int {. - nosideEffect, rtl, extern: "npegs$1".} = - ## low-level matching proc that implements the PEG interpreter. Use this - ## for maximum efficiency (every other PEG operation ends up calling this - ## proc). - ## Returns -1 if it does not match, else the length of the match - case p.kind - of pkEmpty: result = 0 # match of length 0 - of pkAny: - if s[start] != '\0': result = 1 - else: result = -1 - of pkAnyRune: - if s[start] != '\0': - result = runeLenAt(s, start) - else: - result = -1 - of pkLetter: - if s[start] != '\0': - var a: Rune - result = start - fastRuneAt(s, result, a) - if isAlpha(a): dec(result, start) - else: result = -1 - else: - result = -1 - of pkLower: - if s[start] != '\0': - var a: Rune - result = start - fastRuneAt(s, result, a) - if isLower(a): dec(result, start) - else: result = -1 - else: - result = -1 - of pkUpper: - if s[start] != '\0': - var a: Rune - result = start - fastRuneAt(s, result, a) - if isUpper(a): dec(result, start) - else: result = -1 - else: - result = -1 - of pkTitle: - if s[start] != '\0': - var a: Rune - result = start - fastRuneAt(s, result, a) - if isTitle(a): dec(result, start) - else: result = -1 - else: - result = -1 - of pkWhitespace: - if s[start] != '\0': - var a: Rune - result = start - fastRuneAt(s, result, a) - if isWhiteSpace(a): dec(result, start) - else: result = -1 - else: - result = -1 - of pkGreedyAny: - result = len(s) - start - of pkNewLine: - if s[start] == '\L': result = 1 - elif s[start] == '\C': - if s[start+1] == '\L': result = 2 - else: result = 1 - else: result = -1 - of pkTerminal: - result = len(p.term) - for i in 0..result-1: - if p.term[i] != s[start+i]: - result = -1 - break - of pkTerminalIgnoreCase: - var - i = 0 - a, b: Rune - result = start - while i < len(p.term): - fastRuneAt(p.term, i, a) - fastRuneAt(s, result, b) - if toLower(a) != toLower(b): - result = -1 - break - dec(result, start) - of pkTerminalIgnoreStyle: - var - i = 0 - a, b: Rune - result = start - while i < len(p.term): - while true: - fastRuneAt(p.term, i, a) - if a != Rune('_'): break - while true: - fastRuneAt(s, result, b) - if b != Rune('_'): break - if toLower(a) != toLower(b): - result = -1 - break - dec(result, start) - of pkChar: - if p.ch == s[start]: result = 1 - else: result = -1 - of pkCharChoice: - if contains(p.charChoice[], s[start]): result = 1 - else: result = -1 - of pkNonTerminal: - var oldMl = c.ml - when false: echo "enter: ", p.nt.name - result = rawMatch(s, p.nt.rule, start, c) - when false: echo "leave: ", p.nt.name - if result < 0: c.ml = oldMl - of pkSequence: - var oldMl = c.ml - result = 0 - for i in 0..high(p.sons): - var x = rawMatch(s, p.sons[i], start+result, c) - if x < 0: - c.ml = oldMl - result = -1 - break - else: inc(result, x) - of pkOrderedChoice: - var oldMl = c.ml - for i in 0..high(p.sons): - result = rawMatch(s, p.sons[i], start, c) - if result >= 0: break - c.ml = oldMl - of pkSearch: - var oldMl = c.ml - result = 0 - while start+result < s.len: - var x = rawMatch(s, p.sons[0], start+result, c) - if x >= 0: - inc(result, x) - return - inc(result) - result = -1 - c.ml = oldMl - of pkCapturedSearch: - var idx = c.ml # reserve a slot for the subpattern - inc(c.ml) - result = 0 - while start+result < s.len: - var x = rawMatch(s, p.sons[0], start+result, c) - if x >= 0: - if idx < MaxSubpatterns: - c.matches[idx] = (start, start+result-1) - #else: silently ignore the capture - inc(result, x) - return - inc(result) - result = -1 - c.ml = idx - of pkGreedyRep: - result = 0 - while true: - var x = rawMatch(s, p.sons[0], start+result, c) - # if x == 0, we have an endless loop; so the correct behaviour would be - # not to break. But endless loops can be easily introduced: - # ``(comment / \w*)*`` is such an example. Breaking for x == 0 does the - # expected thing in this case. - if x <= 0: break - inc(result, x) - of pkGreedyRepChar: - result = 0 - var ch = p.ch - while ch == s[start+result]: inc(result) - of pkGreedyRepSet: - result = 0 - while contains(p.charChoice[], s[start+result]): inc(result) - of pkOption: - result = max(0, rawMatch(s, p.sons[0], start, c)) - of pkAndPredicate: - var oldMl = c.ml - result = rawMatch(s, p.sons[0], start, c) - if result >= 0: result = 0 # do not consume anything - else: c.ml = oldMl - of pkNotPredicate: - var oldMl = c.ml - result = rawMatch(s, p.sons[0], start, c) - if result < 0: result = 0 - else: - c.ml = oldMl - result = -1 - of pkCapture: - var idx = c.ml # reserve a slot for the subpattern - inc(c.ml) - result = rawMatch(s, p.sons[0], start, c) - if result >= 0: - if idx < MaxSubpatterns: - c.matches[idx] = (start, start+result-1) - #else: silently ignore the capture - else: - c.ml = idx - of pkBackRef..pkBackRefIgnoreStyle: - if p.index >= c.ml: return -1 - var (a, b) = c.matches[p.index] - var n: TPeg - n.kind = succ(pkTerminal, ord(p.kind)-ord(pkBackRef)) - n.term = s.substr(a, b) - result = rawMatch(s, n, start, c) - of pkStartAnchor: - if c.origStart == start: result = 0 - else: result = -1 - of pkRule, pkList: assert false - -template fillMatches(s, caps, c: expr) = - for k in 0..c.ml-1: - caps[k] = substr(s, c.matches[k][0], c.matches[k][1]) - -proc match*(s: string, pattern: TPeg, matches: var openArray[string], - start = 0): bool {.nosideEffect, rtl, extern: "npegs$1Capture".} = - ## returns ``true`` if ``s[start..]`` matches the ``pattern`` and - ## the captured substrings in the array ``matches``. If it does not - ## match, nothing is written into ``matches`` and ``false`` is - ## returned. - var c: Captures - c.origStart = start - result = rawMatch(s, pattern, start, c) == len(s) - start - if result: fillMatches(s, matches, c) - -proc match*(s: string, pattern: TPeg, - start = 0): bool {.nosideEffect, rtl, extern: "npegs$1".} = - ## returns ``true`` if ``s`` matches the ``pattern`` beginning from ``start``. - var c: Captures - c.origStart = start - result = rawMatch(s, pattern, start, c) == len(s)-start - -proc matchLen*(s: string, pattern: TPeg, matches: var openArray[string], - start = 0): int {.nosideEffect, rtl, extern: "npegs$1Capture".} = - ## the same as ``match``, but it returns the length of the match, - ## if there is no match, -1 is returned. Note that a match length - ## of zero can happen. It's possible that a suffix of `s` remains - ## that does not belong to the match. - var c: Captures - c.origStart = start - result = rawMatch(s, pattern, start, c) - if result >= 0: fillMatches(s, matches, c) - -proc matchLen*(s: string, pattern: TPeg, - start = 0): int {.nosideEffect, rtl, extern: "npegs$1".} = - ## the same as ``match``, but it returns the length of the match, - ## if there is no match, -1 is returned. Note that a match length - ## of zero can happen. It's possible that a suffix of `s` remains - ## that does not belong to the match. - var c: Captures - c.origStart = start - result = rawMatch(s, pattern, start, c) - -proc find*(s: string, pattern: TPeg, matches: var openArray[string], - start = 0): int {.nosideEffect, rtl, extern: "npegs$1Capture".} = - ## returns the starting position of ``pattern`` in ``s`` and the captured - ## substrings in the array ``matches``. If it does not match, nothing - ## is written into ``matches`` and -1 is returned. - var c: Captures - c.origStart = start - for i in start .. s.len-1: - c.ml = 0 - if rawMatch(s, pattern, i, c) >= 0: - fillMatches(s, matches, c) - return i - return -1 - # could also use the pattern here: (!P .)* P - -proc findBounds*(s: string, pattern: TPeg, matches: var openArray[string], - start = 0): tuple[first, last: int] {. - nosideEffect, rtl, extern: "npegs$1Capture".} = - ## returns the starting position and end position of ``pattern`` in ``s`` - ## and the captured - ## substrings in the array ``matches``. If it does not match, nothing - ## is written into ``matches`` and (-1,0) is returned. - var c: Captures - c.origStart = start - for i in start .. s.len-1: - c.ml = 0 - var L = rawMatch(s, pattern, i, c) - if L >= 0: - fillMatches(s, matches, c) - return (i, i+L-1) - return (-1, 0) - -proc find*(s: string, pattern: TPeg, - start = 0): int {.nosideEffect, rtl, extern: "npegs$1".} = - ## returns the starting position of ``pattern`` in ``s``. If it does not - ## match, -1 is returned. - var c: Captures - c.origStart = start - for i in start .. s.len-1: - if rawMatch(s, pattern, i, c) >= 0: return i - return -1 - -iterator findAll*(s: string, pattern: TPeg, start = 0): string = - ## yields all matching *substrings* of `s` that match `pattern`. - var c: Captures - c.origStart = start - var i = start - while i < s.len: - c.ml = 0 - var L = rawMatch(s, pattern, i, c) - if L < 0: - inc(i, 1) - else: - yield substr(s, i, i+L-1) - inc(i, L) - -proc findAll*(s: string, pattern: TPeg, start = 0): seq[string] {. - nosideEffect, rtl, extern: "npegs$1".} = - ## returns all matching *substrings* of `s` that match `pattern`. - ## If it does not match, @[] is returned. - accumulateResult(findAll(s, pattern, start)) - -when not defined(nimhygiene): - {.pragma: inject.} - -template `=~`*(s: string, pattern: TPeg): bool = - ## This calls ``match`` with an implicit declared ``matches`` array that - ## can be used in the scope of the ``=~`` call: - ## - ## .. code-block:: nim - ## - ## if line =~ peg"\s* {\w+} \s* '=' \s* {\w+}": - ## # matches a key=value pair: - ## echo("Key: ", matches[0]) - ## echo("Value: ", matches[1]) - ## elif line =~ peg"\s*{'#'.*}": - ## # matches a comment - ## # note that the implicit ``matches`` array is different from the - ## # ``matches`` array of the first branch - ## echo("comment: ", matches[0]) - ## else: - ## echo("syntax error") - ## - bind MaxSubpatterns - when not declaredInScope(matches): - var matches {.inject.}: array[0..MaxSubpatterns-1, string] - match(s, pattern, matches) - -# ------------------------- more string handling ------------------------------ - -proc contains*(s: string, pattern: TPeg, start = 0): bool {. - nosideEffect, rtl, extern: "npegs$1".} = - ## same as ``find(s, pattern, start) >= 0`` - return find(s, pattern, start) >= 0 - -proc contains*(s: string, pattern: TPeg, matches: var openArray[string], - start = 0): bool {.nosideEffect, rtl, extern: "npegs$1Capture".} = - ## same as ``find(s, pattern, matches, start) >= 0`` - return find(s, pattern, matches, start) >= 0 - -proc startsWith*(s: string, prefix: TPeg, start = 0): bool {. - nosideEffect, rtl, extern: "npegs$1".} = - ## returns true if `s` starts with the pattern `prefix` - result = matchLen(s, prefix, start) >= 0 - -proc endsWith*(s: string, suffix: TPeg, start = 0): bool {. - nosideEffect, rtl, extern: "npegs$1".} = - ## returns true if `s` ends with the pattern `prefix` - var c: Captures - c.origStart = start - for i in start .. s.len-1: - if rawMatch(s, suffix, i, c) == s.len - i: return true - -proc replacef*(s: string, sub: TPeg, by: string): string {. - nosideEffect, rtl, extern: "npegs$1".} = - ## Replaces `sub` in `s` by the string `by`. Captures can be accessed in `by` - ## with the notation ``$i`` and ``$#`` (see strutils.`%`). Examples: - ## - ## .. code-block:: nim - ## "var1=key; var2=key2".replace(peg"{\ident}'='{\ident}", "$1<-$2$2") - ## - ## Results in: - ## - ## .. code-block:: nim - ## - ## "var1<-keykey; val2<-key2key2" - result = "" - var i = 0 - var caps: array[0..MaxSubpatterns-1, string] - var c: Captures - while i < s.len: - c.ml = 0 - var x = rawMatch(s, sub, i, c) - if x <= 0: - add(result, s[i]) - inc(i) - else: - fillMatches(s, caps, c) - addf(result, by, caps) - inc(i, x) - add(result, substr(s, i)) - -proc replace*(s: string, sub: TPeg, by = ""): string {. - nosideEffect, rtl, extern: "npegs$1".} = - ## Replaces `sub` in `s` by the string `by`. Captures cannot be accessed - ## in `by`. - result = "" - var i = 0 - var c: Captures - while i < s.len: - var x = rawMatch(s, sub, i, c) - if x <= 0: - add(result, s[i]) - inc(i) - else: - add(result, by) - inc(i, x) - add(result, substr(s, i)) - -proc parallelReplace*(s: string, subs: varargs[ - tuple[pattern: TPeg, repl: string]]): string {. - nosideEffect, rtl, extern: "npegs$1".} = - ## Returns a modified copy of `s` with the substitutions in `subs` - ## applied in parallel. - result = "" - var i = 0 - var c: Captures - var caps: array[0..MaxSubpatterns-1, string] - while i < s.len: - block searchSubs: - for j in 0..high(subs): - c.ml = 0 - var x = rawMatch(s, subs[j][0], i, c) - if x > 0: - fillMatches(s, caps, c) - addf(result, subs[j][1], caps) - inc(i, x) - break searchSubs - add(result, s[i]) - inc(i) - # copy the rest: - add(result, substr(s, i)) - -proc transformFile*(infile, outfile: string, - subs: varargs[tuple[pattern: TPeg, repl: string]]) {. - rtl, extern: "npegs$1".} = - ## reads in the file `infile`, performs a parallel replacement (calls - ## `parallelReplace`) and writes back to `outfile`. Raises ``EIO`` if an - ## error occurs. This is supposed to be used for quick scripting. - var x = readFile(infile).string - writeFile(outfile, x.parallelReplace(subs)) - -iterator split*(s: string, sep: TPeg): string = - ## Splits the string `s` into substrings. - ## - ## Substrings are separated by the PEG `sep`. - ## Examples: - ## - ## .. code-block:: nim - ## for word in split("00232this02939is39an22example111", peg"\d+"): - ## writeln(stdout, word) - ## - ## Results in: - ## - ## .. code-block:: nim - ## "this" - ## "is" - ## "an" - ## "example" - ## - var c: Captures - var - first = 0 - last = 0 - while last < len(s): - c.ml = 0 - var x = rawMatch(s, sep, last, c) - if x > 0: inc(last, x) - first = last - while last < len(s): - inc(last) - c.ml = 0 - x = rawMatch(s, sep, last, c) - if x > 0: break - if first < last: - yield substr(s, first, last-1) - -proc split*(s: string, sep: TPeg): seq[string] {. - nosideEffect, rtl, extern: "npegs$1".} = - ## Splits the string `s` into substrings. - accumulateResult(split(s, sep)) - -# ------------------- scanner ------------------------------------------------- - -type - TModifier = enum - modNone, - modVerbatim, - modIgnoreCase, - modIgnoreStyle - TTokKind = enum ## enumeration of all tokens - tkInvalid, ## invalid token - tkEof, ## end of file reached - tkAny, ## . - tkAnyRune, ## _ - tkIdentifier, ## abc - tkStringLit, ## "abc" or 'abc' - tkCharSet, ## [^A-Z] - tkParLe, ## '(' - tkParRi, ## ')' - tkCurlyLe, ## '{' - tkCurlyRi, ## '}' - tkCurlyAt, ## '{@}' - tkArrow, ## '<-' - tkBar, ## '/' - tkStar, ## '*' - tkPlus, ## '+' - tkAmp, ## '&' - tkNot, ## '!' - tkOption, ## '?' - tkAt, ## '@' - tkBuiltin, ## \identifier - tkEscaped, ## \\ - tkBackref, ## '$' - tkDollar, ## '$' - tkHat ## '^' - - TToken {.final.} = object ## a token - kind: TTokKind ## the type of the token - modifier: TModifier - literal: string ## the parsed (string) literal - charset: set[char] ## if kind == tkCharSet - index: int ## if kind == tkBackref - - PegLexer {.inheritable.} = object ## the lexer object. - bufpos: int ## the current position within the buffer - buf: cstring ## the buffer itself - lineNumber: int ## the current line number - lineStart: int ## index of last line start in buffer - colOffset: int ## column to add - filename: string - -const - tokKindToStr: array[TTokKind, string] = [ - "invalid", "[EOF]", ".", "_", "identifier", "string literal", - "character set", "(", ")", "{", "}", "{@}", - "<-", "/", "*", "+", "&", "!", "?", - "@", "built-in", "escaped", "$", "$", "^" - ] - -proc handleCR(L: var PegLexer, pos: int): int = - assert(L.buf[pos] == '\c') - inc(L.lineNumber) - result = pos+1 - if L.buf[result] == '\L': inc(result) - L.lineStart = result - -proc handleLF(L: var PegLexer, pos: int): int = - assert(L.buf[pos] == '\L') - inc(L.lineNumber) - result = pos+1 - L.lineStart = result - -proc init(L: var PegLexer, input, filename: string, line = 1, col = 0) = - L.buf = input - L.bufpos = 0 - L.lineNumber = line - L.colOffset = col - L.lineStart = 0 - L.filename = filename - -proc getColumn(L: PegLexer): int {.inline.} = - result = abs(L.bufpos - L.lineStart) + L.colOffset - -proc getLine(L: PegLexer): int {.inline.} = - result = L.lineNumber - -proc errorStr(L: PegLexer, msg: string, line = -1, col = -1): string = - var line = if line < 0: getLine(L) else: line - var col = if col < 0: getColumn(L) else: col - result = "$1($2, $3) Error: $4" % [L.filename, $line, $col, msg] - -proc handleHexChar(c: var PegLexer, xi: var int) = - case c.buf[c.bufpos] - of '0'..'9': - xi = (xi shl 4) or (ord(c.buf[c.bufpos]) - ord('0')) - inc(c.bufpos) - of 'a'..'f': - xi = (xi shl 4) or (ord(c.buf[c.bufpos]) - ord('a') + 10) - inc(c.bufpos) - of 'A'..'F': - xi = (xi shl 4) or (ord(c.buf[c.bufpos]) - ord('A') + 10) - inc(c.bufpos) - else: discard - -proc getEscapedChar(c: var PegLexer, tok: var TToken) = - inc(c.bufpos) - case c.buf[c.bufpos] - of 'r', 'R', 'c', 'C': - add(tok.literal, '\c') - inc(c.bufpos) - of 'l', 'L': - add(tok.literal, '\L') - inc(c.bufpos) - of 'f', 'F': - add(tok.literal, '\f') - inc(c.bufpos) - of 'e', 'E': - add(tok.literal, '\e') - inc(c.bufpos) - of 'a', 'A': - add(tok.literal, '\a') - inc(c.bufpos) - of 'b', 'B': - add(tok.literal, '\b') - inc(c.bufpos) - of 'v', 'V': - add(tok.literal, '\v') - inc(c.bufpos) - of 't', 'T': - add(tok.literal, '\t') - inc(c.bufpos) - of 'x', 'X': - inc(c.bufpos) - var xi = 0 - handleHexChar(c, xi) - handleHexChar(c, xi) - if xi == 0: tok.kind = tkInvalid - else: add(tok.literal, chr(xi)) - of '0'..'9': - var val = ord(c.buf[c.bufpos]) - ord('0') - inc(c.bufpos) - var i = 1 - while (i <= 3) and (c.buf[c.bufpos] in {'0'..'9'}): - val = val * 10 + ord(c.buf[c.bufpos]) - ord('0') - inc(c.bufpos) - inc(i) - if val > 0 and val <= 255: add(tok.literal, chr(val)) - else: tok.kind = tkInvalid - of '\0'..'\31': - tok.kind = tkInvalid - elif c.buf[c.bufpos] in strutils.Letters: - tok.kind = tkInvalid - else: - add(tok.literal, c.buf[c.bufpos]) - inc(c.bufpos) - -proc skip(c: var PegLexer) = - var pos = c.bufpos - var buf = c.buf - while true: - case buf[pos] - of ' ', '\t': - inc(pos) - of '#': - while not (buf[pos] in {'\c', '\L', '\0'}): inc(pos) - of '\c': - pos = handleCR(c, pos) - buf = c.buf - of '\L': - pos = handleLF(c, pos) - buf = c.buf - else: - break # EndOfFile also leaves the loop - c.bufpos = pos - -proc getString(c: var PegLexer, tok: var TToken) = - tok.kind = tkStringLit - var pos = c.bufpos + 1 - var buf = c.buf - var quote = buf[pos-1] - while true: - case buf[pos] - of '\\': - c.bufpos = pos - getEscapedChar(c, tok) - pos = c.bufpos - of '\c', '\L', '\0': - tok.kind = tkInvalid - break - elif buf[pos] == quote: - inc(pos) - break - else: - add(tok.literal, buf[pos]) - inc(pos) - c.bufpos = pos - -proc getDollar(c: var PegLexer, tok: var TToken) = - var pos = c.bufpos + 1 - var buf = c.buf - if buf[pos] in {'0'..'9'}: - tok.kind = tkBackref - tok.index = 0 - while buf[pos] in {'0'..'9'}: - tok.index = tok.index * 10 + ord(buf[pos]) - ord('0') - inc(pos) - else: - tok.kind = tkDollar - c.bufpos = pos - -proc getCharSet(c: var PegLexer, tok: var TToken) = - tok.kind = tkCharSet - tok.charset = {} - var pos = c.bufpos + 1 - var buf = c.buf - var caret = false - if buf[pos] == '^': - inc(pos) - caret = true - while true: - var ch: char - case buf[pos] - of ']': - inc(pos) - break - of '\\': - c.bufpos = pos - getEscapedChar(c, tok) - pos = c.bufpos - ch = tok.literal[tok.literal.len-1] - of '\C', '\L', '\0': - tok.kind = tkInvalid - break - else: - ch = buf[pos] - inc(pos) - incl(tok.charset, ch) - if buf[pos] == '-': - if buf[pos+1] == ']': - incl(tok.charset, '-') - inc(pos) - else: - inc(pos) - var ch2: char - case buf[pos] - of '\\': - c.bufpos = pos - getEscapedChar(c, tok) - pos = c.bufpos - ch2 = tok.literal[tok.literal.len-1] - of '\C', '\L', '\0': - tok.kind = tkInvalid - break - else: - ch2 = buf[pos] - inc(pos) - for i in ord(ch)+1 .. ord(ch2): - incl(tok.charset, chr(i)) - c.bufpos = pos - if caret: tok.charset = {'\1'..'\xFF'} - tok.charset - -proc getSymbol(c: var PegLexer, tok: var TToken) = - var pos = c.bufpos - var buf = c.buf - while true: - add(tok.literal, buf[pos]) - inc(pos) - if buf[pos] notin strutils.IdentChars: break - c.bufpos = pos - tok.kind = tkIdentifier - -proc getBuiltin(c: var PegLexer, tok: var TToken) = - if c.buf[c.bufpos+1] in strutils.Letters: - inc(c.bufpos) - getSymbol(c, tok) - tok.kind = tkBuiltin - else: - tok.kind = tkEscaped - getEscapedChar(c, tok) # may set tok.kind to tkInvalid - -proc getTok(c: var PegLexer, tok: var TToken) = - tok.kind = tkInvalid - tok.modifier = modNone - setLen(tok.literal, 0) - skip(c) - case c.buf[c.bufpos] - of '{': - inc(c.bufpos) - if c.buf[c.bufpos] == '@' and c.buf[c.bufpos+1] == '}': - tok.kind = tkCurlyAt - inc(c.bufpos, 2) - add(tok.literal, "{@}") - else: - tok.kind = tkCurlyLe - add(tok.literal, '{') - of '}': - tok.kind = tkCurlyRi - inc(c.bufpos) - add(tok.literal, '}') - of '[': - getCharSet(c, tok) - of '(': - tok.kind = tkParLe - inc(c.bufpos) - add(tok.literal, '(') - of ')': - tok.kind = tkParRi - inc(c.bufpos) - add(tok.literal, ')') - of '.': - tok.kind = tkAny - inc(c.bufpos) - add(tok.literal, '.') - of '_': - tok.kind = tkAnyRune - inc(c.bufpos) - add(tok.literal, '_') - of '\\': - getBuiltin(c, tok) - of '\'', '"': getString(c, tok) - of '$': getDollar(c, tok) - of '\0': - tok.kind = tkEof - tok.literal = "[EOF]" - of 'a'..'z', 'A'..'Z', '\128'..'\255': - getSymbol(c, tok) - if c.buf[c.bufpos] in {'\'', '"'} or - c.buf[c.bufpos] == '$' and c.buf[c.bufpos+1] in {'0'..'9'}: - case tok.literal - of "i": tok.modifier = modIgnoreCase - of "y": tok.modifier = modIgnoreStyle - of "v": tok.modifier = modVerbatim - else: discard - setLen(tok.literal, 0) - if c.buf[c.bufpos] == '$': - getDollar(c, tok) - else: - getString(c, tok) - if tok.modifier == modNone: tok.kind = tkInvalid - of '+': - tok.kind = tkPlus - inc(c.bufpos) - add(tok.literal, '+') - of '*': - tok.kind = tkStar - inc(c.bufpos) - add(tok.literal, '+') - of '<': - if c.buf[c.bufpos+1] == '-': - inc(c.bufpos, 2) - tok.kind = tkArrow - add(tok.literal, "<-") - else: - add(tok.literal, '<') - of '/': - tok.kind = tkBar - inc(c.bufpos) - add(tok.literal, '/') - of '?': - tok.kind = tkOption - inc(c.bufpos) - add(tok.literal, '?') - of '!': - tok.kind = tkNot - inc(c.bufpos) - add(tok.literal, '!') - of '&': - tok.kind = tkAmp - inc(c.bufpos) - add(tok.literal, '!') - of '@': - tok.kind = tkAt - inc(c.bufpos) - add(tok.literal, '@') - if c.buf[c.bufpos] == '@': - tok.kind = tkCurlyAt - inc(c.bufpos) - add(tok.literal, '@') - of '^': - tok.kind = tkHat - inc(c.bufpos) - add(tok.literal, '^') - else: - add(tok.literal, c.buf[c.bufpos]) - inc(c.bufpos) - -proc arrowIsNextTok(c: PegLexer): bool = - # the only look ahead we need - var pos = c.bufpos - while c.buf[pos] in {'\t', ' '}: inc(pos) - result = c.buf[pos] == '<' and c.buf[pos+1] == '-' - -# ----------------------------- parser ---------------------------------------- - -type - EInvalidPeg* = object of ValueError ## raised if an invalid - ## PEG has been detected - PegParser = object of PegLexer ## the PEG parser object - tok: TToken - nonterms: seq[PNonTerminal] - modifier: TModifier - captures: int - identIsVerbatim: bool - skip: TPeg - -proc pegError(p: PegParser, msg: string, line = -1, col = -1) = - var e: ref EInvalidPeg - new(e) - e.msg = errorStr(p, msg, line, col) - raise e - -proc getTok(p: var PegParser) = - getTok(p, p.tok) - if p.tok.kind == tkInvalid: pegError(p, "invalid token") - -proc eat(p: var PegParser, kind: TTokKind) = - if p.tok.kind == kind: getTok(p) - else: pegError(p, tokKindToStr[kind] & " expected") - -proc parseExpr(p: var PegParser): TPeg - -proc getNonTerminal(p: var PegParser, name: string): PNonTerminal = - for i in 0..high(p.nonterms): - result = p.nonterms[i] - if cmpIgnoreStyle(result.name, name) == 0: return - # forward reference: - result = newNonTerminal(name, getLine(p), getColumn(p)) - add(p.nonterms, result) - -proc modifiedTerm(s: string, m: TModifier): TPeg = - case m - of modNone, modVerbatim: result = term(s) - of modIgnoreCase: result = termIgnoreCase(s) - of modIgnoreStyle: result = termIgnoreStyle(s) - -proc modifiedBackref(s: int, m: TModifier): TPeg = - case m - of modNone, modVerbatim: result = backref(s) - of modIgnoreCase: result = backrefIgnoreCase(s) - of modIgnoreStyle: result = backrefIgnoreStyle(s) - -proc builtin(p: var PegParser): TPeg = - # do not use "y", "skip" or "i" as these would be ambiguous - case p.tok.literal - of "n": result = newLine() - of "d": result = charSet({'0'..'9'}) - of "D": result = charSet({'\1'..'\xff'} - {'0'..'9'}) - of "s": result = charSet({' ', '\9'..'\13'}) - of "S": result = charSet({'\1'..'\xff'} - {' ', '\9'..'\13'}) - of "w": result = charSet({'a'..'z', 'A'..'Z', '_', '0'..'9'}) - of "W": result = charSet({'\1'..'\xff'} - {'a'..'z','A'..'Z','_','0'..'9'}) - of "a": result = charSet({'a'..'z', 'A'..'Z'}) - of "A": result = charSet({'\1'..'\xff'} - {'a'..'z', 'A'..'Z'}) - of "ident": result = pegs.ident - of "letter": result = unicodeLetter() - of "upper": result = unicodeUpper() - of "lower": result = unicodeLower() - of "title": result = unicodeTitle() - of "white": result = unicodeWhitespace() - else: pegError(p, "unknown built-in: " & p.tok.literal) - -proc token(terminal: TPeg, p: PegParser): TPeg = - if p.skip.kind == pkEmpty: result = terminal - else: result = sequence(p.skip, terminal) - -proc primary(p: var PegParser): TPeg = - case p.tok.kind - of tkAmp: - getTok(p) - return &primary(p) - of tkNot: - getTok(p) - return !primary(p) - of tkAt: - getTok(p) - return !*primary(p) - of tkCurlyAt: - getTok(p) - return !*\primary(p).token(p) - else: discard - case p.tok.kind - of tkIdentifier: - if p.identIsVerbatim: - var m = p.tok.modifier - if m == modNone: m = p.modifier - result = modifiedTerm(p.tok.literal, m).token(p) - getTok(p) - elif not arrowIsNextTok(p): - var nt = getNonTerminal(p, p.tok.literal) - incl(nt.flags, ntUsed) - result = nonterminal(nt).token(p) - getTok(p) - else: - pegError(p, "expression expected, but found: " & p.tok.literal) - of tkStringLit: - var m = p.tok.modifier - if m == modNone: m = p.modifier - result = modifiedTerm(p.tok.literal, m).token(p) - getTok(p) - of tkCharSet: - if '\0' in p.tok.charset: - pegError(p, "binary zero ('\\0') not allowed in character class") - result = charSet(p.tok.charset).token(p) - getTok(p) - of tkParLe: - getTok(p) - result = parseExpr(p) - eat(p, tkParRi) - of tkCurlyLe: - getTok(p) - result = capture(parseExpr(p)).token(p) - eat(p, tkCurlyRi) - inc(p.captures) - of tkAny: - result = any().token(p) - getTok(p) - of tkAnyRune: - result = anyRune().token(p) - getTok(p) - of tkBuiltin: - result = builtin(p).token(p) - getTok(p) - of tkEscaped: - result = term(p.tok.literal[0]).token(p) - getTok(p) - of tkDollar: - result = endAnchor() - getTok(p) - of tkHat: - result = startAnchor() - getTok(p) - of tkBackref: - var m = p.tok.modifier - if m == modNone: m = p.modifier - result = modifiedBackref(p.tok.index, m).token(p) - if p.tok.index < 0 or p.tok.index > p.captures: - pegError(p, "invalid back reference index: " & $p.tok.index) - getTok(p) - else: - pegError(p, "expression expected, but found: " & p.tok.literal) - getTok(p) # we must consume a token here to prevent endless loops! - while true: - case p.tok.kind - of tkOption: - result = ?result - getTok(p) - of tkStar: - result = *result - getTok(p) - of tkPlus: - result = +result - getTok(p) - else: break - -proc seqExpr(p: var PegParser): TPeg = - result = primary(p) - while true: - case p.tok.kind - of tkAmp, tkNot, tkAt, tkStringLit, tkCharSet, tkParLe, tkCurlyLe, - tkAny, tkAnyRune, tkBuiltin, tkEscaped, tkDollar, tkBackref, - tkHat, tkCurlyAt: - result = sequence(result, primary(p)) - of tkIdentifier: - if not arrowIsNextTok(p): - result = sequence(result, primary(p)) - else: break - else: break - -proc parseExpr(p: var PegParser): TPeg = - result = seqExpr(p) - while p.tok.kind == tkBar: - getTok(p) - result = result / seqExpr(p) - -proc parseRule(p: var PegParser): PNonTerminal = - if p.tok.kind == tkIdentifier and arrowIsNextTok(p): - result = getNonTerminal(p, p.tok.literal) - if ntDeclared in result.flags: - pegError(p, "attempt to redefine: " & result.name) - result.line = getLine(p) - result.col = getColumn(p) - getTok(p) - eat(p, tkArrow) - result.rule = parseExpr(p) - incl(result.flags, ntDeclared) # NOW inlining may be attempted - else: - pegError(p, "rule expected, but found: " & p.tok.literal) - -proc rawParse(p: var PegParser): TPeg = - ## parses a rule or a PEG expression - while p.tok.kind == tkBuiltin: - case p.tok.literal - of "i": - p.modifier = modIgnoreCase - getTok(p) - of "y": - p.modifier = modIgnoreStyle - getTok(p) - of "skip": - getTok(p) - p.skip = ?primary(p) - else: break - if p.tok.kind == tkIdentifier and arrowIsNextTok(p): - result = parseRule(p).rule - while p.tok.kind != tkEof: - discard parseRule(p) - else: - p.identIsVerbatim = true - result = parseExpr(p) - if p.tok.kind != tkEof: - pegError(p, "EOF expected, but found: " & p.tok.literal) - for i in 0..high(p.nonterms): - var nt = p.nonterms[i] - if ntDeclared notin nt.flags: - pegError(p, "undeclared identifier: " & nt.name, nt.line, nt.col) - elif ntUsed notin nt.flags and i > 0: - pegError(p, "unused rule: " & nt.name, nt.line, nt.col) - -proc parsePeg*(pattern: string, filename = "pattern", line = 1, col = 0): TPeg = - ## constructs a Peg object from `pattern`. `filename`, `line`, `col` are - ## used for error messages, but they only provide start offsets. `parsePeg` - ## keeps track of line and column numbers within `pattern`. - var p: PegParser - init(PegLexer(p), pattern, filename, line, col) - p.tok.kind = tkInvalid - p.tok.modifier = modNone - p.tok.literal = "" - p.tok.charset = {} - p.nonterms = @[] - p.identIsVerbatim = false - getTok(p) - result = rawParse(p) - -proc peg*(pattern: string): TPeg = - ## constructs a Peg object from the `pattern`. The short name has been - ## chosen to encourage its use as a raw string modifier:: - ## - ## peg"{\ident} \s* '=' \s* {.*}" - result = parsePeg(pattern, "pattern") - -proc escapePeg*(s: string): string = - ## escapes `s` so that it is matched verbatim when used as a peg. - result = "" - var inQuote = false - for c in items(s): - case c - of '\0'..'\31', '\'', '"', '\\': - if inQuote: - result.add('\'') - inQuote = false - result.add("\\x") - result.add(toHex(ord(c), 2)) - else: - if not inQuote: - result.add('\'') - inQuote = true - result.add(c) - if inQuote: result.add('\'') - -when isMainModule: - assert escapePeg("abc''def'") == r"'abc'\x27\x27'def'\x27" - assert match("(a b c)", peg"'(' @ ')'") - assert match("W_HI_Le", peg"\y 'while'") - assert(not match("W_HI_L", peg"\y 'while'")) - assert(not match("W_HI_Le", peg"\y v'while'")) - assert match("W_HI_Le", peg"y'while'") - - assert($ +digits == $peg"\d+") - assert "0158787".match(peg"\d+") - assert "ABC 0232".match(peg"\w+\s+\d+") - assert "ABC".match(peg"\d+ / \w+") - - for word in split("00232this02939is39an22example111", peg"\d+"): - writeln(stdout, word) - - assert matchLen("key", ident) == 3 - - var pattern = sequence(ident, *whitespace, term('='), *whitespace, ident) - assert matchLen("key1= cal9", pattern) == 11 - - var ws = newNonTerminal("ws", 1, 1) - ws.rule = *whitespace - - var expr = newNonTerminal("expr", 1, 1) - expr.rule = sequence(capture(ident), *sequence( - nonterminal(ws), term('+'), nonterminal(ws), nonterminal(expr))) - - var c: Captures - var s = "a+b + c +d+e+f" - assert rawMatch(s, expr.rule, 0, c) == len(s) - var a = "" - for i in 0..c.ml-1: - a.add(substr(s, c.matches[i][0], c.matches[i][1])) - assert a == "abcdef" - #echo expr.rule - - #const filename = "lib/devel/peg/grammar.txt" - #var grammar = parsePeg(newFileStream(filename, fmRead), filename) - #echo "a <- [abc]*?".match(grammar) - assert find("_____abc_______", term("abc"), 2) == 5 - assert match("_______ana", peg"A <- 'ana' / . A") - assert match("abcs%%%", peg"A <- ..A / .A / '%'") - - var matches: array[0..MaxSubpatterns-1, string] - if "abc" =~ peg"{'a'}'bc' 'xyz' / {\ident}": - assert matches[0] == "abc" - else: - assert false - - var g2 = peg"""S <- A B / C D - A <- 'a'+ - B <- 'b'+ - C <- 'c'+ - D <- 'd'+ - """ - assert($g2 == "((A B) / (C D))") - assert match("cccccdddddd", g2) - assert("var1=key; var2=key2".replacef(peg"{\ident}'='{\ident}", "$1<-$2$2") == - "var1<-keykey; var2<-key2key2") - assert("var1=key; var2=key2".replace(peg"{\ident}'='{\ident}", "$1<-$2$2") == - "$1<-$2$2; $1<-$2$2") - assert "var1=key; var2=key2".endsWith(peg"{\ident}'='{\ident}") - - if "aaaaaa" =~ peg"'aa' !. / ({'a'})+": - assert matches[0] == "a" - else: - assert false - - if match("abcdefg", peg"c {d} ef {g}", matches, 2): - assert matches[0] == "d" - assert matches[1] == "g" - else: - assert false - - for x in findAll("abcdef", peg".", 3): - echo x - - for x in findAll("abcdef", peg"^{.}", 3): - assert x == "d" - - if "f(a, b)" =~ peg"{[0-9]+} / ({\ident} '(' {@} ')')": - assert matches[0] == "f" - assert matches[1] == "a, b" - else: - assert false - - assert match("eine übersicht und außerdem", peg"(\letter \white*)+") - # ß is not a lower cased letter?! - assert match("eine übersicht und auerdem", peg"(\lower \white*)+") - assert match("EINE ÜBERSICHT UND AUSSERDEM", peg"(\upper \white*)+") - assert(not match("456678", peg"(\letter)+")) - - assert("var1 = key; var2 = key2".replacef( - peg"\skip(\s*) {\ident}'='{\ident}", "$1<-$2$2") == - "var1<-keykey;var2<-key2key2") - - assert match("prefix/start", peg"^start$", 7) - diff --git a/lib/pure/rationals.nim b/lib/pure/rationals.nim index 04aa8316a..3b68a2381 100644 --- a/lib/pure/rationals.nim +++ b/lib/pure/rationals.nim @@ -12,6 +12,7 @@ ## a denominator `den`, both of type int. The denominator can not be 0. import math +import hashes type Rational*[T] = object ## a rational number, consisting of a numerator and denominator @@ -32,7 +33,7 @@ proc `$`*[T](x: Rational[T]): string = ## Turn a rational number into a string. result = $x.num & "/" & $x.den -proc toRational*[T](x: SomeInteger): Rational[T] = +proc toRational*[T](x: T): Rational[T] = ## Convert some integer `x` to a rational number. result.num = x result.den = 1 @@ -188,7 +189,7 @@ proc `/=`*[T](x: var Rational[T], y: T) = x.den *= y reduce(x) -proc cmp*(x, y: Rational): int = +proc cmp*(x, y: Rational): int {.procvar.} = ## Compares two rationals. (x - y).num @@ -205,6 +206,17 @@ proc abs*[T](x: Rational[T]): Rational[T] = result.num = abs x.num result.den = abs x.den +proc hash*[T](x: Rational[T]): THash = + ## Computes hash for rational `x` + # reduce first so that hash(x) == hash(y) for x == y + var copy = x + reduce(copy) + + var h: THash = 0 + h = h !& hash(copy.num) + h = h !& hash(copy.den) + result = !$h + when isMainModule: var z = Rational[int](num: 0, den: 1) @@ -242,11 +254,13 @@ when isMainModule: assert( not(o > o) ) assert( cmp(o, o) == 0 ) assert( cmp(z, z) == 0 ) + assert( hash(o) == hash(o) ) assert( a == b ) assert( a >= b ) assert( not(b > a) ) assert( cmp(a, b) == 0 ) + assert( hash(a) == hash(b) ) var x = 1//3 @@ -270,6 +284,6 @@ when isMainModule: y /= 9 assert( y == 13//27 ) - assert toRational[int, int](5) == 5//1 + assert toRational(5) == 5//1 assert abs(toFloat(y) - 0.4814814814814815) < 1.0e-7 assert toInt(z) == 0 diff --git a/lib/pure/rawsockets.nim b/lib/pure/rawsockets.nim index ac348eb1b..a30c23ada 100644 --- a/lib/pure/rawsockets.nim +++ b/lib/pure/rawsockets.nim @@ -39,6 +39,9 @@ export SO_KEEPALIVE, SO_OOBINLINE, SO_REUSEADDR, MSG_PEEK +when defined(macosx): + export SO_NOSIGPIPE + type Port* = distinct uint16 ## port type @@ -428,10 +431,6 @@ proc selectWrite*(writefds: var seq[SocketHandle], pruneSocketSet(writefds, (wr)) -# We ignore signal SIGPIPE on Darwin -when defined(macosx): - signal(SIGPIPE, SIG_IGN) - when defined(Windows): var wsa: WSAData if wsaStartup(0x0101'i16, addr wsa) != 0: raiseOSError(osLastError()) diff --git a/lib/pure/redis.nim b/lib/pure/redis.nim index 9177ddee5..aa2e0f9bd 100644 --- a/lib/pure/redis.nim +++ b/lib/pure/redis.nim @@ -1080,7 +1080,7 @@ proc assertListsIdentical(listA, listB: seq[string]) = assert(item == listB[i]) i = i + 1 -when isMainModule: +when not defined(testing) and isMainModule: when false: var r = open() diff --git a/lib/pure/romans.nim b/lib/pure/romans.nim index 79fb75526..0c182843a 100644 --- a/lib/pure/romans.nim +++ b/lib/pure/romans.nim @@ -44,16 +44,13 @@ proc decimalToRoman*(number: range[1..3_999]): string = ("XC", 90), ("L", 50), ("XL", 40), ("X", 10), ("IX", 9), ("V", 5), ("IV", 4), ("I", 1)] result = "" - var decVal = number + var decVal: int = number for key, val in items(romanComposites): while decVal >= val: dec(decVal, val) result.add(key) when isMainModule: - import math - randomize() - for i in 1 .. 100: - var rnd = 1 + random(3990) - assert rnd == rnd.decimalToRoman.romanToDecimal + for i in 1 .. 3_999: + assert i == i.decimalToRoman.romanToDecimal diff --git a/lib/pure/ropes.nim b/lib/pure/ropes.nim index 4cc64a154..5c7fedfe3 100644 --- a/lib/pure/ropes.nim +++ b/lib/pure/ropes.nim @@ -52,9 +52,9 @@ proc len*(a: Rope): int {.rtl, extern: "nro$1".} = ## the rope's length if a == nil: result = 0 else: result = a.length - + proc newRope(): Rope = new(result) -proc newRope(data: string): Rope = +proc newRope(data: string): Rope = new(result) result.length = len(data) result.data = data @@ -65,18 +65,18 @@ var when countCacheMisses: var misses, hits: int - -proc splay(s: string, tree: Rope, cmpres: var int): Rope = + +proc splay(s: string, tree: Rope, cmpres: var int): Rope = var c: int var t = tree N.left = nil N.right = nil # reset to nil var le = N var r = N - while true: + while true: c = cmp(s, t.data) - if c < 0: - if (t.left != nil) and (s < t.left.data): + if c < 0: + if (t.left != nil) and (s < t.left.data): var y = t.left t.left = y.right y.right = t @@ -85,8 +85,8 @@ proc splay(s: string, tree: Rope, cmpres: var int): Rope = r.left = t r = t t = t.left - elif c > 0: - if (t.right != nil) and (s > t.right.data): + elif c > 0: + if (t.right != nil) and (s > t.right.data): var y = t.right t.right = y.left y.left = t @@ -95,8 +95,8 @@ proc splay(s: string, tree: Rope, cmpres: var int): Rope = le.right = t le = t t = t.right - else: - break + else: + break cmpres = c le.right = t.left r.left = t.right @@ -104,50 +104,50 @@ proc splay(s: string, tree: Rope, cmpres: var int): Rope = t.right = N.left result = t -proc insertInCache(s: string, tree: Rope): Rope = +proc insertInCache(s: string, tree: Rope): Rope = var t = tree - if t == nil: + if t == nil: result = newRope(s) when countCacheMisses: inc(misses) return var cmp: int t = splay(s, t, cmp) - if cmp == 0: + if cmp == 0: # We get here if it's already in the Tree # Don't add it again result = t when countCacheMisses: inc(hits) - else: + else: when countCacheMisses: inc(misses) result = newRope(s) - if cmp < 0: + if cmp < 0: result.left = t.left result.right = t t.left = nil - else: + else: # i > t.item: result.right = t.right result.left = t t.right = nil proc rope*(s: string): Rope {.rtl, extern: "nro$1Str".} = - ## Converts a string to a rope. - if s.len == 0: + ## Converts a string to a rope. + if s.len == 0: result = nil - elif cacheEnabled: + elif cacheEnabled: result = insertInCache(s, cache) cache = result - else: + else: result = newRope(s) - -proc rope*(i: BiggestInt): Rope {.rtl, extern: "nro$1BiggestInt".} = - ## Converts an int to a rope. + +proc rope*(i: BiggestInt): Rope {.rtl, extern: "nro$1BiggestInt".} = + ## Converts an int to a rope. result = rope($i) proc rope*(f: BiggestFloat): Rope {.rtl, extern: "nro$1BiggestFloat".} = - ## Converts a float to a rope. + ## Converts a float to a rope. result = rope($f) - + proc enableCache*() {.rtl, extern: "nro$1".} = ## Enables the caching of leaves. This reduces the memory footprint at ## the cost of runtime efficiency. @@ -160,9 +160,9 @@ proc disableCache*() {.rtl, extern: "nro$1".} = proc `&`*(a, b: Rope): Rope {.rtl, extern: "nroConcRopeRope".} = ## the concatenation operator for ropes. - if a == nil: + if a == nil: result = b - elif b == nil: + elif b == nil: result = a else: result = newRope() @@ -177,16 +177,16 @@ proc `&`*(a, b: Rope): Rope {.rtl, extern: "nroConcRopeRope".} = else: result.left = a result.right = b - -proc `&`*(a: Rope, b: string): Rope {.rtl, extern: "nroConcRopeStr".} = + +proc `&`*(a: Rope, b: string): Rope {.rtl, extern: "nroConcRopeStr".} = ## the concatenation operator for ropes. result = a & rope(b) - -proc `&`*(a: string, b: Rope): Rope {.rtl, extern: "nroConcStrRope".} = + +proc `&`*(a: string, b: Rope): Rope {.rtl, extern: "nroConcStrRope".} = ## the concatenation operator for ropes. result = rope(a) & b - -proc `&`*(a: openArray[Rope]): Rope {.rtl, extern: "nroConcOpenArray".} = + +proc `&`*(a: openArray[Rope]): Rope {.rtl, extern: "nroConcOpenArray".} = ## the concatenation operator for an openarray of ropes. for i in countup(0, high(a)): result = result & a[i] @@ -219,7 +219,7 @@ iterator leaves*(r: Rope): string = ## iterates over any leaf string in the rope `r`. if r != nil: var stack = @[r] - while stack.len > 0: + while stack.len > 0: var it = stack.pop while isConc(it): stack.add(it.right) @@ -227,7 +227,7 @@ iterator leaves*(r: Rope): string = assert(it != nil) assert(it.data != nil) yield it.data - + iterator items*(r: Rope): char = ## iterates over any character in the rope `r`. for s in leaves(r): @@ -237,7 +237,7 @@ proc write*(f: File, r: Rope) {.rtl, extern: "nro$1".} = ## writes a rope to a file. for s in leaves(r): write(f, s) -proc `$`*(r: Rope): string {.rtl, extern: "nroToString".}= +proc `$`*(r: Rope): string {.rtl, extern: "nroToString".}= ## converts a rope back to a string. result = newString(r.len) setLen(result, 0) @@ -251,25 +251,25 @@ when false: new(result) result.length = -idx - proc compileFrmt(frmt: string): Rope = + proc compileFrmt(frmt: string): Rope = var i = 0 var length = len(frmt) result = nil var num = 0 - while i < length: - if frmt[i] == '$': + while i < length: + if frmt[i] == '$': inc(i) case frmt[i] - of '$': + of '$': add(result, "$") inc(i) - of '#': + of '#': inc(i) add(result, compiledArg(num+1)) inc(num) - of '0'..'9': + of '0'..'9': var j = 0 - while true: + while true: j = j * 10 + ord(frmt[i]) - ord('0') inc(i) if frmt[i] notin {'0'..'9'}: break @@ -285,13 +285,13 @@ when false: add(s, compiledArg(j)) else: raise newException(EInvalidValue, "invalid format string") var start = i - while i < length: + while i < length: if frmt[i] != '$': inc(i) - else: break - if i - 1 >= start: + else: break + if i - 1 >= start: add(result, substr(frmt, start, i-1)) - -proc `%`*(frmt: string, args: openArray[Rope]): Rope {. + +proc `%`*(frmt: string, args: openArray[Rope]): Rope {. rtl, extern: "nroFormat".} = ## `%` substitution operator for ropes. Does not support the ``$identifier`` ## nor ``${identifier}`` notations. @@ -299,23 +299,23 @@ proc `%`*(frmt: string, args: openArray[Rope]): Rope {. var length = len(frmt) result = nil var num = 0 - while i < length: - if frmt[i] == '$': + while i < length: + if frmt[i] == '$': inc(i) case frmt[i] - of '$': + of '$': add(result, "$") inc(i) - of '#': + of '#': inc(i) add(result, args[num]) inc(num) - of '0'..'9': + of '0'..'9': var j = 0 - while true: + while true: j = j * 10 + ord(frmt[i]) - ord('0') inc(i) - if frmt[i] notin {'0'..'9'}: break + if frmt[i] notin {'0'..'9'}: break add(result, args[j-1]) of '{': inc(i) @@ -325,13 +325,14 @@ proc `%`*(frmt: string, args: openArray[Rope]): Rope {. inc(i) if frmt[i] == '}': inc(i) else: raise newException(ValueError, "invalid format string") + add(result, args[j-1]) else: raise newException(ValueError, "invalid format string") var start = i - while i < length: + while i < length: if frmt[i] != '$': inc(i) - else: break - if i - 1 >= start: + else: break + if i - 1 >= start: add(result, substr(frmt, start, i - 1)) proc addf*(c: var Rope, frmt: string, args: openArray[Rope]) {. @@ -339,29 +340,46 @@ proc addf*(c: var Rope, frmt: string, args: openArray[Rope]) {. ## shortcut for ``add(c, frmt % args)``. add(c, frmt % args) +const + bufSize = 1024 # 1 KB is reasonable + proc equalsFile*(r: Rope, f: File): bool {.rtl, extern: "nro$1File".} = ## returns true if the contents of the file `f` equal `r`. - var bufSize = 1024 # reasonable start value - var buf = alloc(bufSize) + var + buf: array[bufSize, char] + bpos = buf.len + blen = buf.len + for s in leaves(r): - if s.len > bufSize: - bufSize = max(bufSize * 2, s.len) - buf = realloc(buf, bufSize) - var readBytes = readBuffer(f, buf, s.len) - result = readBytes == s.len and equalMem(buf, cstring(s), s.len) - if not result: break - if result: - result = readBuffer(f, buf, 1) == 0 # really at the end of file? - dealloc(buf) + var spos = 0 + let slen = s.len + while spos < slen: + if bpos == blen: + # Read more data + bpos = 0 + blen = readBuffer(f, addr(buf[0]), buf.len) + if blen == 0: # no more data in file + result = false + return + let n = min(blen - bpos, slen - spos) + # TODO There's gotta be a better way of comparing here... + if not equalMem(addr(buf[bpos]), + cast[pointer](cast[int](cstring(s))+spos), n): + result = false + return + spos += n + bpos += n + + result = readBuffer(f, addr(buf[0]), 1) == 0 # check that we've read all -proc equalsFile*(r: Rope, f: string): bool {.rtl, extern: "nro$1Str".} = +proc equalsFile*(r: Rope, filename: string): bool {.rtl, extern: "nro$1Str".} = ## returns true if the contents of the file `f` equal `r`. If `f` does not ## exist, false is returned. - var bin: File - result = open(bin, f) + var f: File + result = open(f, filename) if result: - result = equalsFile(r, bin) - close(bin) + result = equalsFile(r, f) + close(f) new(N) # init dummy node for splay algorithm diff --git a/lib/pure/selectors.nim b/lib/pure/selectors.nim index b6bc9dd3a..6901ecf58 100644 --- a/lib/pure/selectors.nim +++ b/lib/pure/selectors.nim @@ -296,7 +296,7 @@ proc contains*(s: Selector, key: SelectorKey): bool = TReadyInfo: ReadyInfo, PSelector: Selector].} -when isMainModule and not defined(nimdoc): +when not defined(testing) and isMainModule and not defined(nimdoc): # Select() import sockets type diff --git a/lib/pure/smtp.nim b/lib/pure/smtp.nim index 81198f9e1..c1bc259a5 100644 --- a/lib/pure/smtp.nim +++ b/lib/pure/smtp.nim @@ -253,7 +253,7 @@ proc close*(smtp: AsyncSmtp) {.async.} = await smtp.sock.send("QUIT\c\L") smtp.sock.close() -when isMainModule: +when not defined(testing) and isMainModule: #var msg = createMessage("Test subject!", # "Hello, my name is dom96.\n What\'s yours?", @["dominik@localhost"]) #echo(msg) diff --git a/lib/pure/strtabs.nim b/lib/pure/strtabs.nim index 727d5a386..7fdd994f2 100644 --- a/lib/pure/strtabs.nim +++ b/lib/pure/strtabs.nim @@ -168,6 +168,12 @@ proc newStringTable*(mode: StringTableMode): StringTableRef {. result.counter = 0 newSeq(result.data, startSize) +proc clear*(s: StringTableRef, mode: StringTableMode) = + ## resets a string table to be empty again. + s.mode = mode + s.counter = 0 + s.data.setLen(startSize) + proc newStringTable*(keyValuePairs: varargs[string], mode: StringTableMode): StringTableRef {. rtl, extern: "nst$1WithPairs".} = @@ -227,7 +233,7 @@ proc `$`*(t: StringTableRef): string {.rtl, extern: "nstDollar".} = result = "{:}" else: result = "{" - for key, val in pairs(t): + for key, val in pairs(t): if result.len > 1: result.add(", ") result.add(key) result.add(": ") diff --git a/lib/pure/strutils.nim b/lib/pure/strutils.nim index 655203cda..eb4be719a 100644 --- a/lib/pure/strutils.nim +++ b/lib/pure/strutils.nim @@ -169,14 +169,12 @@ proc cmpIgnoreStyle*(a, b: string): int {.noSideEffect, {.pop.} -proc strip*(s: string, leading = true, trailing = true): string {.noSideEffect, - rtl, extern: "nsuStrip".} = - ## Strips whitespace from `s` and returns the resulting string. +proc strip*(s: string, leading = true, trailing = true, chars: set[char] = Whitespace): string + {.noSideEffect, rtl, extern: "nsuStrip".} = + ## Strips `chars` from `s` and returns the resulting string. ## - ## If `leading` is true, leading whitespace is stripped. - ## If `trailing` is true, trailing whitespace is stripped. - const - chars: set[char] = Whitespace + ## If `leading` is true, leading `chars` are stripped. + ## If `trailing` is true, trailing `chars` are stripped. var first = 0 last = len(s)-1 @@ -386,7 +384,7 @@ proc split*(s: string, sep: string): seq[string] {.noSideEffect, ## `split iterator <#split.i,string,string>`_. accumulateResult(split(s, sep)) -proc toHex*(x: BiggestInt, len: int): string {.noSideEffect, +proc toHex*(x: BiggestInt, len: Positive): string {.noSideEffect, rtl, extern: "nsuToHex".} = ## Converts `x` to its hexadecimal representation. ## @@ -403,7 +401,7 @@ proc toHex*(x: BiggestInt, len: int): string {.noSideEffect, # handle negative overflow if n == 0 and x < 0: n = -1 -proc intToStr*(x: int, minchars: int = 1): string {.noSideEffect, +proc intToStr*(x: int, minchars: Positive = 1): string {.noSideEffect, rtl, extern: "nsuIntToStr".} = ## Converts `x` to its decimal representation. ## @@ -499,7 +497,7 @@ proc parseEnum*[T: enum](s: string, default: T): T = return e result = default -proc repeat*(c: char, count: int): string {.noSideEffect, +proc repeat*(c: char, count: Natural): string {.noSideEffect, rtl, extern: "nsuRepeatChar".} = ## Returns a string of length `count` consisting only of ## the character `c`. You can use this proc to left align strings. Example: @@ -514,7 +512,7 @@ proc repeat*(c: char, count: int): string {.noSideEffect, result = newString(count) for i in 0..count-1: result[i] = c -proc repeat*(s: string, n: int): string {.noSideEffect, +proc repeat*(s: string, n: Natural): string {.noSideEffect, rtl, extern: "nsuRepeatStr".} = ## Returns String `s` concatenated `n` times. Example: ## @@ -523,7 +521,7 @@ proc repeat*(s: string, n: int): string {.noSideEffect, result = newStringOfCap(n * s.len) for i in 1..n: result.add(s) -template spaces*(n: int): string = repeat(' ',n) +template spaces*(n: Natural): string = repeat(' ',n) ## Returns a String with `n` space characters. You can use this proc ## to left align strings. Example: ## @@ -535,15 +533,15 @@ template spaces*(n: int): string = repeat(' ',n) ## echo text1 & spaces(max(0, width - text1.len)) & "|" ## echo text2 & spaces(max(0, width - text2.len)) & "|" -proc repeatChar*(count: int, c: char = ' '): string {.deprecated.} = repeat(c, count) +proc repeatChar*(count: Natural, c: char = ' '): string {.deprecated.} = repeat(c, count) ## deprecated: use repeat() or spaces() -proc repeatStr*(count: int, s: string): string {.deprecated.} = repeat(s, count) +proc repeatStr*(count: Natural, s: string): string {.deprecated.} = repeat(s, count) ## deprecated: use repeat(string, count) or string.repeat(count) -proc align*(s: string, count: int, padding = ' '): string {. +proc align*(s: string, count: Natural, padding = ' '): string {. noSideEffect, rtl, extern: "nsuAlignString".} = - ## Aligns a string `s` with `padding`, so that is of length `count`. + ## Aligns a string `s` with `padding`, so that it is of length `count`. ## ## `padding` characters (by default spaces) are added before `s` resulting in ## right alignment. If ``s.len >= count``, no spaces are added and `s` is @@ -682,7 +680,7 @@ proc endsWith*(s, suffix: string): bool {.noSideEffect, inc(i) if suffix[i] == '\0': return true -proc continuesWith*(s, substr: string, start: int): bool {.noSideEffect, +proc continuesWith*(s, substr: string, start: Natural): bool {.noSideEffect, rtl, extern: "nsuContinuesWith".} = ## Returns true iff ``s`` continues with ``substr`` at position ``start``. ## @@ -693,8 +691,8 @@ proc continuesWith*(s, substr: string, start: int): bool {.noSideEffect, if s[i+start] != substr[i]: return false inc(i) -proc addSep*(dest: var string, sep = ", ", startLen = 0) {.noSideEffect, - inline.} = +proc addSep*(dest: var string, sep = ", ", startLen: Natural = 0) + {.noSideEffect, inline.} = ## Adds a separator to `dest` only if its length is bigger than `startLen`. ## ## A shorthand for: @@ -784,7 +782,7 @@ proc findAux(s, sub: string, start: int, a: SkipTable): int = inc(j, a[s[j+m]]) return -1 -proc find*(s, sub: string, start: int = 0): int {.noSideEffect, +proc find*(s, sub: string, start: Natural = 0): int {.noSideEffect, rtl, extern: "nsuFindStr".} = ## Searches for `sub` in `s` starting at position `start`. ## @@ -793,7 +791,7 @@ proc find*(s, sub: string, start: int = 0): int {.noSideEffect, preprocessSub(sub, a) result = findAux(s, sub, start, a) -proc find*(s: string, sub: char, start: int = 0): int {.noSideEffect, +proc find*(s: string, sub: char, start: Natural = 0): int {.noSideEffect, rtl, extern: "nsuFindChar".} = ## Searches for `sub` in `s` starting at position `start`. ## @@ -802,7 +800,7 @@ proc find*(s: string, sub: char, start: int = 0): int {.noSideEffect, if sub == s[i]: return i return -1 -proc find*(s: string, chars: set[char], start: int = 0): int {.noSideEffect, +proc find*(s: string, chars: set[char], start: Natural = 0): int {.noSideEffect, rtl, extern: "nsuFindCharSet".} = ## Searches for `chars` in `s` starting at position `start`. ## @@ -933,7 +931,7 @@ proc replaceWord*(s, sub: string, by = ""): string {.noSideEffect, var j = findAux(s, sub, i, a) if j < 0: break # word boundary? - if (j == 0 or s[j-1] notin wordChars) and + if (j == 0 or s[j-1] notin wordChars) and (j+sub.len >= s.len or s[j+sub.len] notin wordChars): add result, substr(s, i, j - 1) add result, by @@ -976,7 +974,7 @@ proc parseOctInt*(s: string): int {.noSideEffect, of '\0': break else: raise newException(ValueError, "invalid integer: " & s) -proc toOct*(x: BiggestInt, len: int): string {.noSideEffect, +proc toOct*(x: BiggestInt, len: Positive): string {.noSideEffect, rtl, extern: "nsuToOct".} = ## Converts `x` into its octal representation. ## @@ -992,7 +990,7 @@ proc toOct*(x: BiggestInt, len: int): string {.noSideEffect, shift = shift + 3 mask = mask shl 3 -proc toBin*(x: BiggestInt, len: int): string {.noSideEffect, +proc toBin*(x: BiggestInt, len: Positive): string {.noSideEffect, rtl, extern: "nsuToBin".} = ## Converts `x` into its binary representation. ## @@ -1221,7 +1219,7 @@ proc formatBiggestFloat*(f: BiggestFloat, format: FloatFormatMode = ffDefault, ## of significant digits to be printed. ## `precision`'s default value is the maximum number of meaningful digits ## after the decimal point for Nim's ``biggestFloat`` type. - ## + ## ## If ``precision == 0``, it tries to format it nicely. const floatFormatToChar: array[FloatFormatMode, char] = ['g', 'f', 'e'] var @@ -1286,7 +1284,7 @@ proc findNormalized(x: string, inArray: openArray[string]): int = return -1 proc invalidFormatString() {.noinline.} = - raise newException(ValueError, "invalid format string") + raise newException(ValueError, "invalid format string") proc addf*(s: var string, formatstr: string, a: varargs[string, `$`]) {. noSideEffect, rtl, extern: "nsuAddf".} = @@ -1402,21 +1400,28 @@ when isMainModule: doAssert align("a", 0) == "a" doAssert align("1232", 6) == " 1232" doAssert align("1232", 6, '#') == "##1232" - echo wordWrap(""" this is a long text -- muchlongerthan10chars and here - it goes""", 10, false) + + let + inp = """ this is a long text -- muchlongerthan10chars and here + it goes""" + outp = " this is a\nlong text\n--\nmuchlongerthan10chars\nand here\nit goes" + doAssert wordWrap(inp, 10, false) == outp + doAssert formatBiggestFloat(0.00000000001, ffDecimal, 11) == "0.00000000001" - doAssert formatBiggestFloat(0.00000000001, ffScientific, 1) == "1.0e-11" + doAssert formatBiggestFloat(0.00000000001, ffScientific, 1) in + ["1.0e-11", "1.0e-011"] doAssert "$# $3 $# $#" % ["a", "b", "c"] == "a c b c" - echo formatSize(1'i64 shl 31 + 300'i64) # == "4,GB" - echo formatSize(1'i64 shl 31) + when not defined(testing): + echo formatSize(1'i64 shl 31 + 300'i64) # == "4,GB" + echo formatSize(1'i64 shl 31) doAssert "$animal eats $food." % ["animal", "The cat", "food", "fish"] == "The cat eats fish." doAssert "-ld a-ldz -ld".replaceWord("-ld") == " a-ldz " doAssert "-lda-ldz -ld abc".replaceWord("-ld") == "-lda-ldz abc" - + type MyEnum = enum enA, enB, enC, enuD, enE doAssert parseEnum[MyEnum]("enu_D") == enuD @@ -1426,3 +1431,11 @@ when isMainModule: doAssert count("foofoofoo", "foofoo", overlapping = true) == 2 doAssert count("foofoofoo", 'f') == 3 doAssert count("foofoofoobar", {'f','b'}) == 4 + + doAssert strip(" foofoofoo ") == "foofoofoo" + doAssert strip("sfoofoofoos", chars = {'s'}) == "foofoofoo" + doAssert strip("barfoofoofoobar", chars = {'b', 'a', 'r'}) == "foofoofoo" + doAssert strip("stripme but don't strip this stripme", + chars = {'s', 't', 'r', 'i', 'p', 'm', 'e'}) == " but don't strip this " + doAssert strip("sfoofoofoos", leading = false, chars = {'s'}) == "sfoofoofoo" + doAssert strip("sfoofoofoos", trailing = false, chars = {'s'}) == "foofoofoos" diff --git a/lib/pure/subexes.nim b/lib/pure/subexes.nim index d701b85b1..d213c99e6 100644 --- a/lib/pure/subexes.nim +++ b/lib/pure/subexes.nim @@ -386,8 +386,13 @@ when isMainModule: longishA, longish)""" - echo "type TMyEnum* = enum\n $', '2i'\n '{..}" % ["fieldA", - "fieldB", "FiledClkad", "fieldD", "fieldE", "longishFieldName"] + assert "type TMyEnum* = enum\n $', '2i'\n '{..}" % ["fieldA", + "fieldB", "FiledClkad", "fieldD", "fieldE", "longishFieldName"] == + strutils.unindent """ + type TMyEnum* = enum + fieldA, fieldB, + FiledClkad, fieldD, + fieldE, longishFieldName""" doAssert subex"$1($', '{2..})" % ["f", "a", "b", "c"] == "f(a, b, c)" @@ -395,7 +400,12 @@ when isMainModule: doAssert subex"$['''|'|''''|']']#" % "0" == "'|" - echo subex("type\n TEnum = enum\n $', '40c'\n '{..}") % [ - "fieldNameA", "fieldNameB", "fieldNameC", "fieldNameD"] + assert subex("type\n TEnum = enum\n $', '40c'\n '{..}") % [ + "fieldNameA", "fieldNameB", "fieldNameC", "fieldNameD"] == + strutils.unindent """ + type + TEnum = enum + fieldNameA, fieldNameB, fieldNameC, + fieldNameD""" diff --git a/lib/pure/terminal.nim b/lib/pure/terminal.nim index df637dcb6..29f700db5 100644 --- a/lib/pure/terminal.nim +++ b/lib/pure/terminal.nim @@ -51,12 +51,13 @@ else: proc setRaw(fd: FileHandle, time: cint = TCSAFLUSH) = var mode: Termios discard fd.tcgetattr(addr mode) - mode.iflag = mode.iflag and not Tcflag(BRKINT or ICRNL or INPCK or ISTRIP or IXON) - mode.oflag = mode.oflag and not Tcflag(OPOST) - mode.cflag = (mode.cflag and not Tcflag(CSIZE or PARENB)) or CS8 - mode.lflag = mode.lflag and not Tcflag(ECHO or ICANON or IEXTEN or ISIG) - mode.cc[VMIN] = 1.cuchar - mode.cc[VTIME] = 0.cuchar + mode.c_iflag = mode.c_iflag and not Tcflag(BRKINT or ICRNL or INPCK or + ISTRIP or IXON) + mode.c_oflag = mode.c_oflag and not Tcflag(OPOST) + mode.c_cflag = (mode.c_cflag and not Tcflag(CSIZE or PARENB)) or CS8 + mode.c_lflag = mode.c_lflag and not Tcflag(ECHO or ICANON or IEXTEN or ISIG) + mode.c_cc[VMIN] = 1.cuchar + mode.c_cc[VTIME] = 0.cuchar discard fd.tcsetattr(time, addr mode) proc setCursorPos*(x, y: int) = @@ -375,7 +376,7 @@ when not defined(windows): result = stdin.readChar() discard fd.tcsetattr(TCSADRAIN, addr oldMode) -when isMainModule: +when not defined(testing) and isMainModule: system.addQuitProc(resetAttributes) write(stdout, "never mind") eraseLine() diff --git a/lib/pure/times.nim b/lib/pure/times.nim index c16cae04b..1b9fa4599 100644 --- a/lib/pure/times.nim +++ b/lib/pure/times.nim @@ -119,7 +119,7 @@ type ## in the range 0 to 23. monthday*: range[1..31] ## The day of the month, in the range 1 to 31. month*: Month ## The current month. - year*: range[-10_000..10_000] ## The current year. + year*: int ## The current year. weekday*: WeekDay ## The current day of the week. yearday*: range[0..365] ## The number of days since January 1, ## in the range 0 to 365. @@ -379,7 +379,7 @@ when not defined(JS): result.hour = t.hour result.monthday = t.monthday result.month = ord(t.month) - result.year = t.year - 1900 + result.year = cint(t.year - 1900) result.weekday = weekDays[t.weekday] result.yearday = t.yearday result.isdst = if t.isDST: 1 else: 0 @@ -541,6 +541,8 @@ elif defined(JS): proc getTimezone(): int = result = newDate().getTimezoneOffset() + proc epochTime*(): float {.tags: [TimeEffect].} = newDate().toSeconds() + proc getDateStr*(): string {.rtl, extern: "nt$1", tags: [TimeEffect].} = ## gets the current date as a string of the format ``YYYY-MM-DD``. var ti = getLocalTime(getTime()) @@ -762,7 +764,7 @@ proc parseToken(info: var TimeInfo; token, value: string; j: var int) = info.monthday = value[j..j+1].parseInt() j += 2 of "ddd": - case value[j..j+2].toLower(): + case value[j..j+2].toLower() of "sun": info.weekday = dSun of "mon": @@ -1034,8 +1036,6 @@ when isMainModule: # Tue 19 Jan 03:14:07 GMT 2038 var t = getGMTime(fromSeconds(2147483647)) - echo t.format("ddd dd MMM hh:mm:ss ZZZ yyyy") - echo t.format("ddd ddMMMhhmmssZZZyyyy") assert t.format("ddd dd MMM hh:mm:ss ZZZ yyyy") == "Tue 19 Jan 03:14:07 UTC 2038" assert t.format("ddd ddMMMhh:mm:ssZZZyyyy") == "Tue 19Jan03:14:07UTC2038" @@ -1068,46 +1068,48 @@ when isMainModule: var f = "dddd at hh:mmtt on MMM d, yyyy" assert($s.parse(f) == "Tue Dec 15 09:04:00 2015") # ANSIC = "Mon Jan _2 15:04:05 2006" - s = "Mon Jan 2 15:04:05 2006" - f = "ddd MMM d HH:mm:ss yyyy" - assert($s.parse(f) == "Mon Jan 2 15:04:05 2006") + s = "Thu Jan 12 15:04:05 2006" + f = "ddd MMM dd HH:mm:ss yyyy" + assert($s.parse(f) == "Thu Jan 12 15:04:05 2006") # UnixDate = "Mon Jan _2 15:04:05 MST 2006" - s = "Mon Jan 2 15:04:05 MST 2006" - f = "ddd MMM d HH:mm:ss ZZZ yyyy" - assert($s.parse(f) == "Mon Jan 2 15:04:05 2006") + s = "Thu Jan 12 15:04:05 MST 2006" + f = "ddd MMM dd HH:mm:ss ZZZ yyyy" + assert($s.parse(f) == "Thu Jan 12 15:04:05 2006") # RubyDate = "Mon Jan 02 15:04:05 -0700 2006" - s = "Mon Jan 02 15:04:05 -07:00 2006" + s = "Thu Jan 12 15:04:05 -07:00 2006" f = "ddd MMM dd HH:mm:ss zzz yyyy" - assert($s.parse(f) == "Mon Jan 2 15:04:05 2006") + assert($s.parse(f) == "Thu Jan 12 15:04:05 2006") # RFC822 = "02 Jan 06 15:04 MST" - s = "02 Jan 06 15:04 MST" + s = "12 Jan 16 15:04 MST" f = "dd MMM yy HH:mm ZZZ" - assert($s.parse(f) == "Mon Jan 2 15:04:00 2006") + assert($s.parse(f) == "Tue Jan 12 15:04:00 2016") # RFC822Z = "02 Jan 06 15:04 -0700" # RFC822 with numeric zone - s = "02 Jan 06 15:04 -07:00" + s = "12 Jan 16 15:04 -07:00" f = "dd MMM yy HH:mm zzz" - assert($s.parse(f) == "Mon Jan 2 15:04:00 2006") + assert($s.parse(f) == "Tue Jan 12 15:04:00 2016") # RFC850 = "Monday, 02-Jan-06 15:04:05 MST" - s = "Monday, 02-Jan-06 15:04:05 MST" + s = "Monday, 12-Jan-06 15:04:05 MST" f = "dddd, dd-MMM-yy HH:mm:ss ZZZ" - assert($s.parse(f) == "Mon Jan 2 15:04:05 2006") + assert($s.parse(f) == "Thu Jan 12 15:04:05 2006") # RFC1123 = "Mon, 02 Jan 2006 15:04:05 MST" - s = "Mon, 02 Jan 2006 15:04:05 MST" + s = "Thu, 12 Jan 2006 15:04:05 MST" f = "ddd, dd MMM yyyy HH:mm:ss ZZZ" - assert($s.parse(f) == "Mon Jan 2 15:04:05 2006") + assert($s.parse(f) == "Thu Jan 12 15:04:05 2006") # RFC1123Z = "Mon, 02 Jan 2006 15:04:05 -0700" # RFC1123 with numeric zone - s = "Mon, 02 Jan 2006 15:04:05 -07:00" + s = "Thu, 12 Jan 2006 15:04:05 -07:00" f = "ddd, dd MMM yyyy HH:mm:ss zzz" - assert($s.parse(f) == "Mon Jan 2 15:04:05 2006") + assert($s.parse(f) == "Thu Jan 12 15:04:05 2006") # RFC3339 = "2006-01-02T15:04:05Z07:00" - s = "2006-01-02T15:04:05Z-07:00" + s = "2006-01-12T15:04:05Z-07:00" f = "yyyy-MM-ddTHH:mm:ssZzzz" - assert($s.parse(f) == "Mon Jan 2 15:04:05 2006") + assert($s.parse(f) == "Thu Jan 12 15:04:05 2006") # RFC3339Nano = "2006-01-02T15:04:05.999999999Z07:00" - s = "2006-01-02T15:04:05.999999999Z-07:00" + s = "2006-01-12T15:04:05.999999999Z-07:00" f = "yyyy-MM-ddTHH:mm:ss.999999999Zzzz" - assert($s.parse(f) == "Mon Jan 2 15:04:05 2006") + assert($s.parse(f) == "Thu Jan 12 15:04:05 2006") # Kitchen = "3:04PM" s = "3:04PM" f = "h:mmtt" - echo "Kitchen: " & $s.parse(f) + assert "15:04:00" in $s.parse(f) + when not defined(testing): + echo "Kitchen: " & $s.parse(f) diff --git a/lib/pure/unicode.nim b/lib/pure/unicode.nim index a6f8f916b..5fd3c2418 100644 --- a/lib/pure/unicode.nim +++ b/lib/pure/unicode.nim @@ -19,7 +19,7 @@ type Rune16* = distinct int16 ## 16 bit Unicode character {.deprecated: [TRune: Rune, TRune16: Rune16].} - + proc `<=%`*(a, b: Rune): bool = return int(a) <=% int(b) proc `<%`*(a, b: Rune): bool = return int(a) <% int(b) proc `==`*(a, b: Rune): bool = return int(a) == int(b) @@ -39,7 +39,7 @@ proc runeLen*(s: string): int {.rtl, extern: "nuc$1".} = else: inc i inc(result) -proc runeLenAt*(s: string, i: int): int = +proc runeLenAt*(s: string, i: Natural): int = ## returns the number of bytes the rune starting at ``s[i]`` takes. if ord(s[i]) <=% 127: result = 1 elif ord(s[i]) shr 5 == 0b110: result = 2 @@ -58,7 +58,7 @@ template fastRuneAt*(s: string, i: int, result: expr, doInc = true) = when doInc: inc(i) elif ord(s[i]) shr 5 == 0b110: # assert(ord(s[i+1]) shr 6 == 0b10) - result = Rune((ord(s[i]) and (ones(5))) shl 6 or + result = Rune((ord(s[i]) and (ones(5))) shl 6 or (ord(s[i+1]) and ones(6))) when doInc: inc(i, 2) elif ord(s[i]) shr 4 == 0b1110: @@ -77,7 +77,7 @@ template fastRuneAt*(s: string, i: int, result: expr, doInc = true) = (ord(s[i+2]) and ones(6)) shl 6 or (ord(s[i+3]) and ones(6))) when doInc: inc(i, 4) - elif ord(s[i]) shr 2 == 0b111110: + elif ord(s[i]) shr 2 == 0b111110: # assert(ord(s[i+1]) shr 6 == 0b10) # assert(ord(s[i+2]) shr 6 == 0b10) # assert(ord(s[i+3]) shr 6 == 0b10) @@ -88,7 +88,7 @@ template fastRuneAt*(s: string, i: int, result: expr, doInc = true) = (ord(s[i+3]) and ones(6)) shl 6 or (ord(s[i+4]) and ones(6))) when doInc: inc(i, 5) - elif ord(s[i]) shr 1 == 0b1111110: + elif ord(s[i]) shr 1 == 0b1111110: # assert(ord(s[i+1]) shr 6 == 0b10) # assert(ord(s[i+2]) shr 6 == 0b10) # assert(ord(s[i+3]) shr 6 == 0b10) @@ -105,11 +105,36 @@ template fastRuneAt*(s: string, i: int, result: expr, doInc = true) = result = Rune(ord(s[i])) when doInc: inc(i) -proc runeAt*(s: string, i: int): Rune = +proc validateUtf8*(s: string): int = + ## returns the position of the invalid byte in ``s`` if the string ``s`` does + ## not hold valid UTF-8 data. Otherwise -1 is returned. + var i = 0 + let L = s.len + while i < L: + if ord(s[i]) <=% 127: + inc(i) + elif ord(s[i]) shr 5 == 0b110: + if i+1 < L and ord(s[i+1]) shr 6 == 0b10: inc(i, 2) + else: return i + elif ord(s[i]) shr 4 == 0b1110: + if i+2 < L and ord(s[i+1]) shr 6 == 0b10 and ord(s[i+2]) shr 6 == 0b10: + inc i, 3 + else: return i + elif ord(s[i]) shr 3 == 0b11110: + if i+3 < L and ord(s[i+1]) shr 6 == 0b10 and + ord(s[i+2]) shr 6 == 0b10 and + ord(s[i+3]) shr 6 == 0b10: + inc i, 4 + else: return i + else: + return i + return -1 + +proc runeAt*(s: string, i: Natural): Rune = ## returns the unicode character in `s` at byte index `i` fastRuneAt(s, i, result, false) -proc toUTF8*(c: Rune): string {.rtl, extern: "nuc$1".} = +proc toUTF8*(c: Rune): string {.rtl, extern: "nuc$1".} = ## converts a rune into its UTF8 representation var i = RuneImpl(c) if i <=% 127: @@ -159,967 +184,967 @@ proc `$`*(runes: seq[Rune]): string = const alphaRanges = [ - 0x00d8, 0x00f6, # - - 0x00f8, 0x01f5, # - - 0x0250, 0x02a8, # - - 0x038e, 0x03a1, # - - 0x03a3, 0x03ce, # - - 0x03d0, 0x03d6, # - - 0x03e2, 0x03f3, # - - 0x0490, 0x04c4, # - - 0x0561, 0x0587, # - - 0x05d0, 0x05ea, # - - 0x05f0, 0x05f2, # - - 0x0621, 0x063a, # - - 0x0640, 0x064a, # - - 0x0671, 0x06b7, # - - 0x06ba, 0x06be, # - - 0x06c0, 0x06ce, # - - 0x06d0, 0x06d3, # - - 0x0905, 0x0939, # - - 0x0958, 0x0961, # - - 0x0985, 0x098c, # - - 0x098f, 0x0990, # - - 0x0993, 0x09a8, # - - 0x09aa, 0x09b0, # - - 0x09b6, 0x09b9, # - - 0x09dc, 0x09dd, # - - 0x09df, 0x09e1, # - - 0x09f0, 0x09f1, # - - 0x0a05, 0x0a0a, # - - 0x0a0f, 0x0a10, # - - 0x0a13, 0x0a28, # - - 0x0a2a, 0x0a30, # - - 0x0a32, 0x0a33, # - - 0x0a35, 0x0a36, # - - 0x0a38, 0x0a39, # - - 0x0a59, 0x0a5c, # - - 0x0a85, 0x0a8b, # - - 0x0a8f, 0x0a91, # - - 0x0a93, 0x0aa8, # - - 0x0aaa, 0x0ab0, # - - 0x0ab2, 0x0ab3, # - - 0x0ab5, 0x0ab9, # - - 0x0b05, 0x0b0c, # - - 0x0b0f, 0x0b10, # - - 0x0b13, 0x0b28, # - - 0x0b2a, 0x0b30, # - - 0x0b32, 0x0b33, # - - 0x0b36, 0x0b39, # - - 0x0b5c, 0x0b5d, # - - 0x0b5f, 0x0b61, # - - 0x0b85, 0x0b8a, # - - 0x0b8e, 0x0b90, # - - 0x0b92, 0x0b95, # - - 0x0b99, 0x0b9a, # - - 0x0b9e, 0x0b9f, # - - 0x0ba3, 0x0ba4, # - - 0x0ba8, 0x0baa, # - - 0x0bae, 0x0bb5, # - - 0x0bb7, 0x0bb9, # - - 0x0c05, 0x0c0c, # - - 0x0c0e, 0x0c10, # - - 0x0c12, 0x0c28, # - - 0x0c2a, 0x0c33, # - - 0x0c35, 0x0c39, # - - 0x0c60, 0x0c61, # - - 0x0c85, 0x0c8c, # - - 0x0c8e, 0x0c90, # - - 0x0c92, 0x0ca8, # - - 0x0caa, 0x0cb3, # - - 0x0cb5, 0x0cb9, # - - 0x0ce0, 0x0ce1, # - - 0x0d05, 0x0d0c, # - - 0x0d0e, 0x0d10, # - - 0x0d12, 0x0d28, # - - 0x0d2a, 0x0d39, # - - 0x0d60, 0x0d61, # - - 0x0e01, 0x0e30, # - - 0x0e32, 0x0e33, # - - 0x0e40, 0x0e46, # - - 0x0e5a, 0x0e5b, # - - 0x0e81, 0x0e82, # - - 0x0e87, 0x0e88, # - - 0x0e94, 0x0e97, # - - 0x0e99, 0x0e9f, # - - 0x0ea1, 0x0ea3, # - - 0x0eaa, 0x0eab, # - - 0x0ead, 0x0eae, # - - 0x0eb2, 0x0eb3, # - - 0x0ec0, 0x0ec4, # - - 0x0edc, 0x0edd, # - - 0x0f18, 0x0f19, # - - 0x0f40, 0x0f47, # - - 0x0f49, 0x0f69, # - - 0x10d0, 0x10f6, # - - 0x1100, 0x1159, # - - 0x115f, 0x11a2, # - - 0x11a8, 0x11f9, # - - 0x1e00, 0x1e9b, # - - 0x1f50, 0x1f57, # - - 0x1f80, 0x1fb4, # - - 0x1fb6, 0x1fbc, # - - 0x1fc2, 0x1fc4, # - - 0x1fc6, 0x1fcc, # - - 0x1fd0, 0x1fd3, # - - 0x1fd6, 0x1fdb, # - - 0x1fe0, 0x1fec, # - - 0x1ff2, 0x1ff4, # - - 0x1ff6, 0x1ffc, # - - 0x210a, 0x2113, # - - 0x2115, 0x211d, # - - 0x2120, 0x2122, # - - 0x212a, 0x2131, # - - 0x2133, 0x2138, # - - 0x3041, 0x3094, # - - 0x30a1, 0x30fa, # - - 0x3105, 0x312c, # - - 0x3131, 0x318e, # - - 0x3192, 0x319f, # - - 0x3260, 0x327b, # - - 0x328a, 0x32b0, # - - 0x32d0, 0x32fe, # - - 0x3300, 0x3357, # - - 0x3371, 0x3376, # - - 0x337b, 0x3394, # - - 0x3399, 0x339e, # - - 0x33a9, 0x33ad, # - - 0x33b0, 0x33c1, # - - 0x33c3, 0x33c5, # - - 0x33c7, 0x33d7, # - - 0x33d9, 0x33dd, # - - 0x4e00, 0x9fff, # - - 0xac00, 0xd7a3, # - - 0xf900, 0xfb06, # - - 0xfb13, 0xfb17, # - - 0xfb1f, 0xfb28, # - - 0xfb2a, 0xfb36, # - - 0xfb38, 0xfb3c, # - - 0xfb40, 0xfb41, # - - 0xfb43, 0xfb44, # - - 0xfb46, 0xfbb1, # - - 0xfbd3, 0xfd3d, # - - 0xfd50, 0xfd8f, # - - 0xfd92, 0xfdc7, # - - 0xfdf0, 0xfdf9, # - - 0xfe70, 0xfe72, # - - 0xfe76, 0xfefc, # - - 0xff66, 0xff6f, # - - 0xff71, 0xff9d, # - - 0xffa0, 0xffbe, # - - 0xffc2, 0xffc7, # - - 0xffca, 0xffcf, # - - 0xffd2, 0xffd7, # - - 0xffda, 0xffdc] # - + 0x00d8, 0x00f6, # - + 0x00f8, 0x01f5, # - + 0x0250, 0x02a8, # - + 0x038e, 0x03a1, # - + 0x03a3, 0x03ce, # - + 0x03d0, 0x03d6, # - + 0x03e2, 0x03f3, # - + 0x0490, 0x04c4, # - + 0x0561, 0x0587, # - + 0x05d0, 0x05ea, # - + 0x05f0, 0x05f2, # - + 0x0621, 0x063a, # - + 0x0640, 0x064a, # - + 0x0671, 0x06b7, # - + 0x06ba, 0x06be, # - + 0x06c0, 0x06ce, # - + 0x06d0, 0x06d3, # - + 0x0905, 0x0939, # - + 0x0958, 0x0961, # - + 0x0985, 0x098c, # - + 0x098f, 0x0990, # - + 0x0993, 0x09a8, # - + 0x09aa, 0x09b0, # - + 0x09b6, 0x09b9, # - + 0x09dc, 0x09dd, # - + 0x09df, 0x09e1, # - + 0x09f0, 0x09f1, # - + 0x0a05, 0x0a0a, # - + 0x0a0f, 0x0a10, # - + 0x0a13, 0x0a28, # - + 0x0a2a, 0x0a30, # - + 0x0a32, 0x0a33, # - + 0x0a35, 0x0a36, # - + 0x0a38, 0x0a39, # - + 0x0a59, 0x0a5c, # - + 0x0a85, 0x0a8b, # - + 0x0a8f, 0x0a91, # - + 0x0a93, 0x0aa8, # - + 0x0aaa, 0x0ab0, # - + 0x0ab2, 0x0ab3, # - + 0x0ab5, 0x0ab9, # - + 0x0b05, 0x0b0c, # - + 0x0b0f, 0x0b10, # - + 0x0b13, 0x0b28, # - + 0x0b2a, 0x0b30, # - + 0x0b32, 0x0b33, # - + 0x0b36, 0x0b39, # - + 0x0b5c, 0x0b5d, # - + 0x0b5f, 0x0b61, # - + 0x0b85, 0x0b8a, # - + 0x0b8e, 0x0b90, # - + 0x0b92, 0x0b95, # - + 0x0b99, 0x0b9a, # - + 0x0b9e, 0x0b9f, # - + 0x0ba3, 0x0ba4, # - + 0x0ba8, 0x0baa, # - + 0x0bae, 0x0bb5, # - + 0x0bb7, 0x0bb9, # - + 0x0c05, 0x0c0c, # - + 0x0c0e, 0x0c10, # - + 0x0c12, 0x0c28, # - + 0x0c2a, 0x0c33, # - + 0x0c35, 0x0c39, # - + 0x0c60, 0x0c61, # - + 0x0c85, 0x0c8c, # - + 0x0c8e, 0x0c90, # - + 0x0c92, 0x0ca8, # - + 0x0caa, 0x0cb3, # - + 0x0cb5, 0x0cb9, # - + 0x0ce0, 0x0ce1, # - + 0x0d05, 0x0d0c, # - + 0x0d0e, 0x0d10, # - + 0x0d12, 0x0d28, # - + 0x0d2a, 0x0d39, # - + 0x0d60, 0x0d61, # - + 0x0e01, 0x0e30, # - + 0x0e32, 0x0e33, # - + 0x0e40, 0x0e46, # - + 0x0e5a, 0x0e5b, # - + 0x0e81, 0x0e82, # - + 0x0e87, 0x0e88, # - + 0x0e94, 0x0e97, # - + 0x0e99, 0x0e9f, # - + 0x0ea1, 0x0ea3, # - + 0x0eaa, 0x0eab, # - + 0x0ead, 0x0eae, # - + 0x0eb2, 0x0eb3, # - + 0x0ec0, 0x0ec4, # - + 0x0edc, 0x0edd, # - + 0x0f18, 0x0f19, # - + 0x0f40, 0x0f47, # - + 0x0f49, 0x0f69, # - + 0x10d0, 0x10f6, # - + 0x1100, 0x1159, # - + 0x115f, 0x11a2, # - + 0x11a8, 0x11f9, # - + 0x1e00, 0x1e9b, # - + 0x1f50, 0x1f57, # - + 0x1f80, 0x1fb4, # - + 0x1fb6, 0x1fbc, # - + 0x1fc2, 0x1fc4, # - + 0x1fc6, 0x1fcc, # - + 0x1fd0, 0x1fd3, # - + 0x1fd6, 0x1fdb, # - + 0x1fe0, 0x1fec, # - + 0x1ff2, 0x1ff4, # - + 0x1ff6, 0x1ffc, # - + 0x210a, 0x2113, # - + 0x2115, 0x211d, # - + 0x2120, 0x2122, # - + 0x212a, 0x2131, # - + 0x2133, 0x2138, # - + 0x3041, 0x3094, # - + 0x30a1, 0x30fa, # - + 0x3105, 0x312c, # - + 0x3131, 0x318e, # - + 0x3192, 0x319f, # - + 0x3260, 0x327b, # - + 0x328a, 0x32b0, # - + 0x32d0, 0x32fe, # - + 0x3300, 0x3357, # - + 0x3371, 0x3376, # - + 0x337b, 0x3394, # - + 0x3399, 0x339e, # - + 0x33a9, 0x33ad, # - + 0x33b0, 0x33c1, # - + 0x33c3, 0x33c5, # - + 0x33c7, 0x33d7, # - + 0x33d9, 0x33dd, # - + 0x4e00, 0x9fff, # - + 0xac00, 0xd7a3, # - + 0xf900, 0xfb06, # - + 0xfb13, 0xfb17, # - + 0xfb1f, 0xfb28, # - + 0xfb2a, 0xfb36, # - + 0xfb38, 0xfb3c, # - + 0xfb40, 0xfb41, # - + 0xfb43, 0xfb44, # - + 0xfb46, 0xfbb1, # - + 0xfbd3, 0xfd3d, # - + 0xfd50, 0xfd8f, # - + 0xfd92, 0xfdc7, # - + 0xfdf0, 0xfdf9, # - + 0xfe70, 0xfe72, # - + 0xfe76, 0xfefc, # - + 0xff66, 0xff6f, # - + 0xff71, 0xff9d, # - + 0xffa0, 0xffbe, # - + 0xffc2, 0xffc7, # - + 0xffca, 0xffcf, # - + 0xffd2, 0xffd7, # - + 0xffda, 0xffdc] # - alphaSinglets = [ - 0x00aa, # - 0x00b5, # - 0x00ba, # - 0x03da, # - 0x03dc, # - 0x03de, # - 0x03e0, # - 0x06d5, # - 0x09b2, # - 0x0a5e, # - 0x0a8d, # - 0x0ae0, # - 0x0b9c, # - 0x0cde, # - 0x0e4f, # - 0x0e84, # - 0x0e8a, # - 0x0e8d, # - 0x0ea5, # - 0x0ea7, # - 0x0eb0, # - 0x0ebd, # - 0x1fbe, # - 0x207f, # - 0x20a8, # - 0x2102, # - 0x2107, # - 0x2124, # - 0x2126, # - 0x2128, # - 0xfb3e, # - 0xfe74] # + 0x00aa, # + 0x00b5, # + 0x00ba, # + 0x03da, # + 0x03dc, # + 0x03de, # + 0x03e0, # + 0x06d5, # + 0x09b2, # + 0x0a5e, # + 0x0a8d, # + 0x0ae0, # + 0x0b9c, # + 0x0cde, # + 0x0e4f, # + 0x0e84, # + 0x0e8a, # + 0x0e8d, # + 0x0ea5, # + 0x0ea7, # + 0x0eb0, # + 0x0ebd, # + 0x1fbe, # + 0x207f, # + 0x20a8, # + 0x2102, # + 0x2107, # + 0x2124, # + 0x2126, # + 0x2128, # + 0xfb3e, # + 0xfe74] # spaceRanges = [ - 0x0009, 0x000a, # tab and newline - 0x0020, 0x0020, # space - 0x00a0, 0x00a0, # - 0x2000, 0x200b, # - - 0x2028, 0x2029, # - 0x3000, 0x3000, # - 0xfeff, 0xfeff] # + 0x0009, 0x000a, # tab and newline + 0x0020, 0x0020, # space + 0x00a0, 0x00a0, # + 0x2000, 0x200b, # - + 0x2028, 0x2029, # - 0x3000, 0x3000, # + 0xfeff, 0xfeff] # toupperRanges = [ - 0x0061, 0x007a, 468, # a-z A-Z - 0x00e0, 0x00f6, 468, # - - - 0x00f8, 0x00fe, 468, # - - - 0x0256, 0x0257, 295, # - - - 0x0258, 0x0259, 298, # - - - 0x028a, 0x028b, 283, # - - - 0x03ad, 0x03af, 463, # - - - 0x03b1, 0x03c1, 468, # - - - 0x03c3, 0x03cb, 468, # - - - 0x03cd, 0x03ce, 437, # - - - 0x0430, 0x044f, 468, # - - - 0x0451, 0x045c, 420, # - - - 0x045e, 0x045f, 420, # - - - 0x0561, 0x0586, 452, # - - - 0x1f00, 0x1f07, 508, # - - - 0x1f10, 0x1f15, 508, # - - - 0x1f20, 0x1f27, 508, # - - - 0x1f30, 0x1f37, 508, # - - - 0x1f40, 0x1f45, 508, # - - - 0x1f60, 0x1f67, 508, # - - - 0x1f70, 0x1f71, 574, # - - - 0x1f72, 0x1f75, 586, # - - - 0x1f76, 0x1f77, 600, # - - - 0x1f78, 0x1f79, 628, # - - - 0x1f7a, 0x1f7b, 612, # - - - 0x1f7c, 0x1f7d, 626, # - - - 0x1f80, 0x1f87, 508, # - - - 0x1f90, 0x1f97, 508, # - - - 0x1fa0, 0x1fa7, 508, # - - - 0x1fb0, 0x1fb1, 508, # - - - 0x1fd0, 0x1fd1, 508, # - - - 0x1fe0, 0x1fe1, 508, # - - - 0x2170, 0x217f, 484, # - - - 0x24d0, 0x24e9, 474, # - - - 0xff41, 0xff5a, 468] # - - + 0x0061, 0x007a, 468, # a-z A-Z + 0x00e0, 0x00f6, 468, # - - + 0x00f8, 0x00fe, 468, # - - + 0x0256, 0x0257, 295, # - - + 0x0258, 0x0259, 298, # - - + 0x028a, 0x028b, 283, # - - + 0x03ad, 0x03af, 463, # - - + 0x03b1, 0x03c1, 468, # - - + 0x03c3, 0x03cb, 468, # - - + 0x03cd, 0x03ce, 437, # - - + 0x0430, 0x044f, 468, # - - + 0x0451, 0x045c, 420, # - - + 0x045e, 0x045f, 420, # - - + 0x0561, 0x0586, 452, # - - + 0x1f00, 0x1f07, 508, # - - + 0x1f10, 0x1f15, 508, # - - + 0x1f20, 0x1f27, 508, # - - + 0x1f30, 0x1f37, 508, # - - + 0x1f40, 0x1f45, 508, # - - + 0x1f60, 0x1f67, 508, # - - + 0x1f70, 0x1f71, 574, # - - + 0x1f72, 0x1f75, 586, # - - + 0x1f76, 0x1f77, 600, # - - + 0x1f78, 0x1f79, 628, # - - + 0x1f7a, 0x1f7b, 612, # - - + 0x1f7c, 0x1f7d, 626, # - - + 0x1f80, 0x1f87, 508, # - - + 0x1f90, 0x1f97, 508, # - - + 0x1fa0, 0x1fa7, 508, # - - + 0x1fb0, 0x1fb1, 508, # - - + 0x1fd0, 0x1fd1, 508, # - - + 0x1fe0, 0x1fe1, 508, # - - + 0x2170, 0x217f, 484, # - - + 0x24d0, 0x24e9, 474, # - - + 0xff41, 0xff5a, 468] # - - toupperSinglets = [ - 0x00ff, 621, # - 0x0101, 499, # - 0x0103, 499, # - 0x0105, 499, # - 0x0107, 499, # - 0x0109, 499, # - 0x010b, 499, # - 0x010d, 499, # - 0x010f, 499, # - 0x0111, 499, # - 0x0113, 499, # - 0x0115, 499, # - 0x0117, 499, # - 0x0119, 499, # - 0x011b, 499, # - 0x011d, 499, # - 0x011f, 499, # - 0x0121, 499, # - 0x0123, 499, # - 0x0125, 499, # - 0x0127, 499, # - 0x0129, 499, # - 0x012b, 499, # - 0x012d, 499, # - 0x012f, 499, # - 0x0131, 268, # I - 0x0133, 499, # - 0x0135, 499, # - 0x0137, 499, # - 0x013a, 499, # - 0x013c, 499, # - 0x013e, 499, # - 0x0140, 499, # - 0x0142, 499, # - 0x0144, 499, # - 0x0146, 499, # - 0x0148, 499, # - 0x014b, 499, # - 0x014d, 499, # - 0x014f, 499, # - 0x0151, 499, # - 0x0153, 499, # - 0x0155, 499, # - 0x0157, 499, # - 0x0159, 499, # - 0x015b, 499, # - 0x015d, 499, # - 0x015f, 499, # - 0x0161, 499, # - 0x0163, 499, # - 0x0165, 499, # - 0x0167, 499, # - 0x0169, 499, # - 0x016b, 499, # - 0x016d, 499, # - 0x016f, 499, # - 0x0171, 499, # - 0x0173, 499, # - 0x0175, 499, # - 0x0177, 499, # - 0x017a, 499, # - 0x017c, 499, # - 0x017e, 499, # - 0x017f, 200, # S - 0x0183, 499, # - 0x0185, 499, # - 0x0188, 499, # - 0x018c, 499, # - 0x0192, 499, # - 0x0199, 499, # - 0x01a1, 499, # - 0x01a3, 499, # - 0x01a5, 499, # - 0x01a8, 499, # - 0x01ad, 499, # - 0x01b0, 499, # - 0x01b4, 499, # - 0x01b6, 499, # - 0x01b9, 499, # - 0x01bd, 499, # - 0x01c5, 499, # - 0x01c6, 498, # - 0x01c8, 499, # - 0x01c9, 498, # - 0x01cb, 499, # - 0x01cc, 498, # - 0x01ce, 499, # - 0x01d0, 499, # - 0x01d2, 499, # - 0x01d4, 499, # - 0x01d6, 499, # - 0x01d8, 499, # - 0x01da, 499, # - 0x01dc, 499, # - 0x01df, 499, # - 0x01e1, 499, # - 0x01e3, 499, # - 0x01e5, 499, # - 0x01e7, 499, # - 0x01e9, 499, # - 0x01eb, 499, # - 0x01ed, 499, # - 0x01ef, 499, # - 0x01f2, 499, # - 0x01f3, 498, # - 0x01f5, 499, # - 0x01fb, 499, # - 0x01fd, 499, # - 0x01ff, 499, # - 0x0201, 499, # - 0x0203, 499, # - 0x0205, 499, # - 0x0207, 499, # - 0x0209, 499, # - 0x020b, 499, # - 0x020d, 499, # - 0x020f, 499, # - 0x0211, 499, # - 0x0213, 499, # - 0x0215, 499, # - 0x0217, 499, # - 0x0253, 290, # - 0x0254, 294, # - 0x025b, 297, # - 0x0260, 295, # - 0x0263, 293, # - 0x0268, 291, # - 0x0269, 289, # - 0x026f, 289, # - 0x0272, 287, # - 0x0283, 282, # - 0x0288, 282, # - 0x0292, 281, # - 0x03ac, 462, # - 0x03cc, 436, # - 0x03d0, 438, # - 0x03d1, 443, # - 0x03d5, 453, # - 0x03d6, 446, # - 0x03e3, 499, # - 0x03e5, 499, # - 0x03e7, 499, # - 0x03e9, 499, # - 0x03eb, 499, # - 0x03ed, 499, # - 0x03ef, 499, # - 0x03f0, 414, # - 0x03f1, 420, # - 0x0461, 499, # - 0x0463, 499, # - 0x0465, 499, # - 0x0467, 499, # - 0x0469, 499, # - 0x046b, 499, # - 0x046d, 499, # - 0x046f, 499, # - 0x0471, 499, # - 0x0473, 499, # - 0x0475, 499, # - 0x0477, 499, # - 0x0479, 499, # - 0x047b, 499, # - 0x047d, 499, # - 0x047f, 499, # - 0x0481, 499, # - 0x0491, 499, # - 0x0493, 499, # - 0x0495, 499, # - 0x0497, 499, # - 0x0499, 499, # - 0x049b, 499, # - 0x049d, 499, # - 0x049f, 499, # - 0x04a1, 499, # - 0x04a3, 499, # - 0x04a5, 499, # - 0x04a7, 499, # - 0x04a9, 499, # - 0x04ab, 499, # - 0x04ad, 499, # - 0x04af, 499, # - 0x04b1, 499, # - 0x04b3, 499, # - 0x04b5, 499, # - 0x04b7, 499, # - 0x04b9, 499, # - 0x04bb, 499, # - 0x04bd, 499, # - 0x04bf, 499, # - 0x04c2, 499, # - 0x04c4, 499, # - 0x04c8, 499, # - 0x04cc, 499, # - 0x04d1, 499, # - 0x04d3, 499, # - 0x04d5, 499, # - 0x04d7, 499, # - 0x04d9, 499, # - 0x04db, 499, # - 0x04dd, 499, # - 0x04df, 499, # - 0x04e1, 499, # - 0x04e3, 499, # - 0x04e5, 499, # - 0x04e7, 499, # - 0x04e9, 499, # - 0x04eb, 499, # - 0x04ef, 499, # - 0x04f1, 499, # - 0x04f3, 499, # - 0x04f5, 499, # - 0x04f9, 499, # - 0x1e01, 499, # - 0x1e03, 499, # - 0x1e05, 499, # - 0x1e07, 499, # - 0x1e09, 499, # - 0x1e0b, 499, # - 0x1e0d, 499, # - 0x1e0f, 499, # - 0x1e11, 499, # - 0x1e13, 499, # - 0x1e15, 499, # - 0x1e17, 499, # - 0x1e19, 499, # - 0x1e1b, 499, # - 0x1e1d, 499, # - 0x1e1f, 499, # - 0x1e21, 499, # - 0x1e23, 499, # - 0x1e25, 499, # - 0x1e27, 499, # - 0x1e29, 499, # - 0x1e2b, 499, # - 0x1e2d, 499, # - 0x1e2f, 499, # - 0x1e31, 499, # - 0x1e33, 499, # - 0x1e35, 499, # - 0x1e37, 499, # - 0x1e39, 499, # - 0x1e3b, 499, # - 0x1e3d, 499, # - 0x1e3f, 499, # - 0x1e41, 499, # - 0x1e43, 499, # - 0x1e45, 499, # - 0x1e47, 499, # - 0x1e49, 499, # - 0x1e4b, 499, # - 0x1e4d, 499, # - 0x1e4f, 499, # - 0x1e51, 499, # - 0x1e53, 499, # - 0x1e55, 499, # - 0x1e57, 499, # - 0x1e59, 499, # - 0x1e5b, 499, # - 0x1e5d, 499, # - 0x1e5f, 499, # - 0x1e61, 499, # - 0x1e63, 499, # - 0x1e65, 499, # - 0x1e67, 499, # - 0x1e69, 499, # - 0x1e6b, 499, # - 0x1e6d, 499, # - 0x1e6f, 499, # - 0x1e71, 499, # - 0x1e73, 499, # - 0x1e75, 499, # - 0x1e77, 499, # - 0x1e79, 499, # - 0x1e7b, 499, # - 0x1e7d, 499, # - 0x1e7f, 499, # - 0x1e81, 499, # - 0x1e83, 499, # - 0x1e85, 499, # - 0x1e87, 499, # - 0x1e89, 499, # - 0x1e8b, 499, # - 0x1e8d, 499, # - 0x1e8f, 499, # - 0x1e91, 499, # - 0x1e93, 499, # - 0x1e95, 499, # - 0x1ea1, 499, # - 0x1ea3, 499, # - 0x1ea5, 499, # - 0x1ea7, 499, # - 0x1ea9, 499, # - 0x1eab, 499, # - 0x1ead, 499, # - 0x1eaf, 499, # - 0x1eb1, 499, # - 0x1eb3, 499, # - 0x1eb5, 499, # - 0x1eb7, 499, # - 0x1eb9, 499, # - 0x1ebb, 499, # - 0x1ebd, 499, # - 0x1ebf, 499, # - 0x1ec1, 499, # - 0x1ec3, 499, # - 0x1ec5, 499, # - 0x1ec7, 499, # - 0x1ec9, 499, # - 0x1ecb, 499, # - 0x1ecd, 499, # - 0x1ecf, 499, # - 0x1ed1, 499, # - 0x1ed3, 499, # - 0x1ed5, 499, # - 0x1ed7, 499, # - 0x1ed9, 499, # - 0x1edb, 499, # - 0x1edd, 499, # - 0x1edf, 499, # - 0x1ee1, 499, # - 0x1ee3, 499, # - 0x1ee5, 499, # - 0x1ee7, 499, # - 0x1ee9, 499, # - 0x1eeb, 499, # - 0x1eed, 499, # - 0x1eef, 499, # - 0x1ef1, 499, # - 0x1ef3, 499, # - 0x1ef5, 499, # - 0x1ef7, 499, # - 0x1ef9, 499, # - 0x1f51, 508, # - 0x1f53, 508, # - 0x1f55, 508, # - 0x1f57, 508, # - 0x1fb3, 509, # - 0x1fc3, 509, # - 0x1fe5, 507, # - 0x1ff3, 509] # + 0x00ff, 621, # + 0x0101, 499, # + 0x0103, 499, # + 0x0105, 499, # + 0x0107, 499, # + 0x0109, 499, # + 0x010b, 499, # + 0x010d, 499, # + 0x010f, 499, # + 0x0111, 499, # + 0x0113, 499, # + 0x0115, 499, # + 0x0117, 499, # + 0x0119, 499, # + 0x011b, 499, # + 0x011d, 499, # + 0x011f, 499, # + 0x0121, 499, # + 0x0123, 499, # + 0x0125, 499, # + 0x0127, 499, # + 0x0129, 499, # + 0x012b, 499, # + 0x012d, 499, # + 0x012f, 499, # + 0x0131, 268, # I + 0x0133, 499, # + 0x0135, 499, # + 0x0137, 499, # + 0x013a, 499, # + 0x013c, 499, # + 0x013e, 499, # + 0x0140, 499, # + 0x0142, 499, # + 0x0144, 499, # + 0x0146, 499, # + 0x0148, 499, # + 0x014b, 499, # + 0x014d, 499, # + 0x014f, 499, # + 0x0151, 499, # + 0x0153, 499, # + 0x0155, 499, # + 0x0157, 499, # + 0x0159, 499, # + 0x015b, 499, # + 0x015d, 499, # + 0x015f, 499, # + 0x0161, 499, # + 0x0163, 499, # + 0x0165, 499, # + 0x0167, 499, # + 0x0169, 499, # + 0x016b, 499, # + 0x016d, 499, # + 0x016f, 499, # + 0x0171, 499, # + 0x0173, 499, # + 0x0175, 499, # + 0x0177, 499, # + 0x017a, 499, # + 0x017c, 499, # + 0x017e, 499, # + 0x017f, 200, # S + 0x0183, 499, # + 0x0185, 499, # + 0x0188, 499, # + 0x018c, 499, # + 0x0192, 499, # + 0x0199, 499, # + 0x01a1, 499, # + 0x01a3, 499, # + 0x01a5, 499, # + 0x01a8, 499, # + 0x01ad, 499, # + 0x01b0, 499, # + 0x01b4, 499, # + 0x01b6, 499, # + 0x01b9, 499, # + 0x01bd, 499, # + 0x01c5, 499, # + 0x01c6, 498, # + 0x01c8, 499, # + 0x01c9, 498, # + 0x01cb, 499, # + 0x01cc, 498, # + 0x01ce, 499, # + 0x01d0, 499, # + 0x01d2, 499, # + 0x01d4, 499, # + 0x01d6, 499, # + 0x01d8, 499, # + 0x01da, 499, # + 0x01dc, 499, # + 0x01df, 499, # + 0x01e1, 499, # + 0x01e3, 499, # + 0x01e5, 499, # + 0x01e7, 499, # + 0x01e9, 499, # + 0x01eb, 499, # + 0x01ed, 499, # + 0x01ef, 499, # + 0x01f2, 499, # + 0x01f3, 498, # + 0x01f5, 499, # + 0x01fb, 499, # + 0x01fd, 499, # + 0x01ff, 499, # + 0x0201, 499, # + 0x0203, 499, # + 0x0205, 499, # + 0x0207, 499, # + 0x0209, 499, # + 0x020b, 499, # + 0x020d, 499, # + 0x020f, 499, # + 0x0211, 499, # + 0x0213, 499, # + 0x0215, 499, # + 0x0217, 499, # + 0x0253, 290, # + 0x0254, 294, # + 0x025b, 297, # + 0x0260, 295, # + 0x0263, 293, # + 0x0268, 291, # + 0x0269, 289, # + 0x026f, 289, # + 0x0272, 287, # + 0x0283, 282, # + 0x0288, 282, # + 0x0292, 281, # + 0x03ac, 462, # + 0x03cc, 436, # + 0x03d0, 438, # + 0x03d1, 443, # + 0x03d5, 453, # + 0x03d6, 446, # + 0x03e3, 499, # + 0x03e5, 499, # + 0x03e7, 499, # + 0x03e9, 499, # + 0x03eb, 499, # + 0x03ed, 499, # + 0x03ef, 499, # + 0x03f0, 414, # + 0x03f1, 420, # + 0x0461, 499, # + 0x0463, 499, # + 0x0465, 499, # + 0x0467, 499, # + 0x0469, 499, # + 0x046b, 499, # + 0x046d, 499, # + 0x046f, 499, # + 0x0471, 499, # + 0x0473, 499, # + 0x0475, 499, # + 0x0477, 499, # + 0x0479, 499, # + 0x047b, 499, # + 0x047d, 499, # + 0x047f, 499, # + 0x0481, 499, # + 0x0491, 499, # + 0x0493, 499, # + 0x0495, 499, # + 0x0497, 499, # + 0x0499, 499, # + 0x049b, 499, # + 0x049d, 499, # + 0x049f, 499, # + 0x04a1, 499, # + 0x04a3, 499, # + 0x04a5, 499, # + 0x04a7, 499, # + 0x04a9, 499, # + 0x04ab, 499, # + 0x04ad, 499, # + 0x04af, 499, # + 0x04b1, 499, # + 0x04b3, 499, # + 0x04b5, 499, # + 0x04b7, 499, # + 0x04b9, 499, # + 0x04bb, 499, # + 0x04bd, 499, # + 0x04bf, 499, # + 0x04c2, 499, # + 0x04c4, 499, # + 0x04c8, 499, # + 0x04cc, 499, # + 0x04d1, 499, # + 0x04d3, 499, # + 0x04d5, 499, # + 0x04d7, 499, # + 0x04d9, 499, # + 0x04db, 499, # + 0x04dd, 499, # + 0x04df, 499, # + 0x04e1, 499, # + 0x04e3, 499, # + 0x04e5, 499, # + 0x04e7, 499, # + 0x04e9, 499, # + 0x04eb, 499, # + 0x04ef, 499, # + 0x04f1, 499, # + 0x04f3, 499, # + 0x04f5, 499, # + 0x04f9, 499, # + 0x1e01, 499, # + 0x1e03, 499, # + 0x1e05, 499, # + 0x1e07, 499, # + 0x1e09, 499, # + 0x1e0b, 499, # + 0x1e0d, 499, # + 0x1e0f, 499, # + 0x1e11, 499, # + 0x1e13, 499, # + 0x1e15, 499, # + 0x1e17, 499, # + 0x1e19, 499, # + 0x1e1b, 499, # + 0x1e1d, 499, # + 0x1e1f, 499, # + 0x1e21, 499, # + 0x1e23, 499, # + 0x1e25, 499, # + 0x1e27, 499, # + 0x1e29, 499, # + 0x1e2b, 499, # + 0x1e2d, 499, # + 0x1e2f, 499, # + 0x1e31, 499, # + 0x1e33, 499, # + 0x1e35, 499, # + 0x1e37, 499, # + 0x1e39, 499, # + 0x1e3b, 499, # + 0x1e3d, 499, # + 0x1e3f, 499, # + 0x1e41, 499, # + 0x1e43, 499, # + 0x1e45, 499, # + 0x1e47, 499, # + 0x1e49, 499, # + 0x1e4b, 499, # + 0x1e4d, 499, # + 0x1e4f, 499, # + 0x1e51, 499, # + 0x1e53, 499, # + 0x1e55, 499, # + 0x1e57, 499, # + 0x1e59, 499, # + 0x1e5b, 499, # + 0x1e5d, 499, # + 0x1e5f, 499, # + 0x1e61, 499, # + 0x1e63, 499, # + 0x1e65, 499, # + 0x1e67, 499, # + 0x1e69, 499, # + 0x1e6b, 499, # + 0x1e6d, 499, # + 0x1e6f, 499, # + 0x1e71, 499, # + 0x1e73, 499, # + 0x1e75, 499, # + 0x1e77, 499, # + 0x1e79, 499, # + 0x1e7b, 499, # + 0x1e7d, 499, # + 0x1e7f, 499, # + 0x1e81, 499, # + 0x1e83, 499, # + 0x1e85, 499, # + 0x1e87, 499, # + 0x1e89, 499, # + 0x1e8b, 499, # + 0x1e8d, 499, # + 0x1e8f, 499, # + 0x1e91, 499, # + 0x1e93, 499, # + 0x1e95, 499, # + 0x1ea1, 499, # + 0x1ea3, 499, # + 0x1ea5, 499, # + 0x1ea7, 499, # + 0x1ea9, 499, # + 0x1eab, 499, # + 0x1ead, 499, # + 0x1eaf, 499, # + 0x1eb1, 499, # + 0x1eb3, 499, # + 0x1eb5, 499, # + 0x1eb7, 499, # + 0x1eb9, 499, # + 0x1ebb, 499, # + 0x1ebd, 499, # + 0x1ebf, 499, # + 0x1ec1, 499, # + 0x1ec3, 499, # + 0x1ec5, 499, # + 0x1ec7, 499, # + 0x1ec9, 499, # + 0x1ecb, 499, # + 0x1ecd, 499, # + 0x1ecf, 499, # + 0x1ed1, 499, # + 0x1ed3, 499, # + 0x1ed5, 499, # + 0x1ed7, 499, # + 0x1ed9, 499, # + 0x1edb, 499, # + 0x1edd, 499, # + 0x1edf, 499, # + 0x1ee1, 499, # + 0x1ee3, 499, # + 0x1ee5, 499, # + 0x1ee7, 499, # + 0x1ee9, 499, # + 0x1eeb, 499, # + 0x1eed, 499, # + 0x1eef, 499, # + 0x1ef1, 499, # + 0x1ef3, 499, # + 0x1ef5, 499, # + 0x1ef7, 499, # + 0x1ef9, 499, # + 0x1f51, 508, # + 0x1f53, 508, # + 0x1f55, 508, # + 0x1f57, 508, # + 0x1fb3, 509, # + 0x1fc3, 509, # + 0x1fe5, 507, # + 0x1ff3, 509] # tolowerRanges = [ 0x0041, 0x005a, 532, # A-Z a-z - 0x00c0, 0x00d6, 532, # - - - 0x00d8, 0x00de, 532, # - - - 0x0189, 0x018a, 705, # - - - 0x018e, 0x018f, 702, # - - - 0x01b1, 0x01b2, 717, # - - - 0x0388, 0x038a, 537, # - - - 0x038e, 0x038f, 563, # - - - 0x0391, 0x03a1, 532, # - - - 0x03a3, 0x03ab, 532, # - - - 0x0401, 0x040c, 580, # - - - 0x040e, 0x040f, 580, # - - - 0x0410, 0x042f, 532, # - - - 0x0531, 0x0556, 548, # - - - 0x10a0, 0x10c5, 548, # - - - 0x1f08, 0x1f0f, 492, # - - - 0x1f18, 0x1f1d, 492, # - - - 0x1f28, 0x1f2f, 492, # - - - 0x1f38, 0x1f3f, 492, # - - - 0x1f48, 0x1f4d, 492, # - - - 0x1f68, 0x1f6f, 492, # - - - 0x1f88, 0x1f8f, 492, # - - - 0x1f98, 0x1f9f, 492, # - - - 0x1fa8, 0x1faf, 492, # - - - 0x1fb8, 0x1fb9, 492, # - - - 0x1fba, 0x1fbb, 426, # - - - 0x1fc8, 0x1fcb, 414, # - - - 0x1fd8, 0x1fd9, 492, # - - - 0x1fda, 0x1fdb, 400, # - - - 0x1fe8, 0x1fe9, 492, # - - - 0x1fea, 0x1feb, 388, # - - - 0x1ff8, 0x1ff9, 372, # - - - 0x1ffa, 0x1ffb, 374, # - - - 0x2160, 0x216f, 516, # - - - 0x24b6, 0x24cf, 526, # - - - 0xff21, 0xff3a, 532] # - - + 0x00c0, 0x00d6, 532, # - - + 0x00d8, 0x00de, 532, # - - + 0x0189, 0x018a, 705, # - - + 0x018e, 0x018f, 702, # - - + 0x01b1, 0x01b2, 717, # - - + 0x0388, 0x038a, 537, # - - + 0x038e, 0x038f, 563, # - - + 0x0391, 0x03a1, 532, # - - + 0x03a3, 0x03ab, 532, # - - + 0x0401, 0x040c, 580, # - - + 0x040e, 0x040f, 580, # - - + 0x0410, 0x042f, 532, # - - + 0x0531, 0x0556, 548, # - - + 0x10a0, 0x10c5, 548, # - - + 0x1f08, 0x1f0f, 492, # - - + 0x1f18, 0x1f1d, 492, # - - + 0x1f28, 0x1f2f, 492, # - - + 0x1f38, 0x1f3f, 492, # - - + 0x1f48, 0x1f4d, 492, # - - + 0x1f68, 0x1f6f, 492, # - - + 0x1f88, 0x1f8f, 492, # - - + 0x1f98, 0x1f9f, 492, # - - + 0x1fa8, 0x1faf, 492, # - - + 0x1fb8, 0x1fb9, 492, # - - + 0x1fba, 0x1fbb, 426, # - - + 0x1fc8, 0x1fcb, 414, # - - + 0x1fd8, 0x1fd9, 492, # - - + 0x1fda, 0x1fdb, 400, # - - + 0x1fe8, 0x1fe9, 492, # - - + 0x1fea, 0x1feb, 388, # - - + 0x1ff8, 0x1ff9, 372, # - - + 0x1ffa, 0x1ffb, 374, # - - + 0x2160, 0x216f, 516, # - - + 0x24b6, 0x24cf, 526, # - - + 0xff21, 0xff3a, 532] # - - tolowerSinglets = [ - 0x0100, 501, # - 0x0102, 501, # - 0x0104, 501, # - 0x0106, 501, # - 0x0108, 501, # - 0x010a, 501, # - 0x010c, 501, # - 0x010e, 501, # - 0x0110, 501, # - 0x0112, 501, # - 0x0114, 501, # - 0x0116, 501, # - 0x0118, 501, # - 0x011a, 501, # - 0x011c, 501, # - 0x011e, 501, # - 0x0120, 501, # - 0x0122, 501, # - 0x0124, 501, # - 0x0126, 501, # - 0x0128, 501, # - 0x012a, 501, # - 0x012c, 501, # - 0x012e, 501, # - 0x0130, 301, # i - 0x0132, 501, # - 0x0134, 501, # - 0x0136, 501, # - 0x0139, 501, # - 0x013b, 501, # - 0x013d, 501, # - 0x013f, 501, # - 0x0141, 501, # - 0x0143, 501, # - 0x0145, 501, # - 0x0147, 501, # - 0x014a, 501, # - 0x014c, 501, # - 0x014e, 501, # - 0x0150, 501, # - 0x0152, 501, # - 0x0154, 501, # - 0x0156, 501, # - 0x0158, 501, # - 0x015a, 501, # - 0x015c, 501, # - 0x015e, 501, # - 0x0160, 501, # - 0x0162, 501, # - 0x0164, 501, # - 0x0166, 501, # - 0x0168, 501, # - 0x016a, 501, # - 0x016c, 501, # - 0x016e, 501, # - 0x0170, 501, # - 0x0172, 501, # - 0x0174, 501, # - 0x0176, 501, # - 0x0178, 379, # - 0x0179, 501, # - 0x017b, 501, # - 0x017d, 501, # - 0x0181, 710, # - 0x0182, 501, # - 0x0184, 501, # - 0x0186, 706, # - 0x0187, 501, # - 0x018b, 501, # - 0x0190, 703, # - 0x0191, 501, # - 0x0193, 705, # - 0x0194, 707, # - 0x0196, 711, # - 0x0197, 709, # - 0x0198, 501, # - 0x019c, 711, # - 0x019d, 713, # - 0x01a0, 501, # - 0x01a2, 501, # - 0x01a4, 501, # - 0x01a7, 501, # - 0x01a9, 718, # - 0x01ac, 501, # - 0x01ae, 718, # - 0x01af, 501, # - 0x01b3, 501, # - 0x01b5, 501, # - 0x01b7, 719, # - 0x01b8, 501, # - 0x01bc, 501, # - 0x01c4, 502, # - 0x01c5, 501, # - 0x01c7, 502, # - 0x01c8, 501, # - 0x01ca, 502, # - 0x01cb, 501, # - 0x01cd, 501, # - 0x01cf, 501, # - 0x01d1, 501, # - 0x01d3, 501, # - 0x01d5, 501, # - 0x01d7, 501, # - 0x01d9, 501, # - 0x01db, 501, # - 0x01de, 501, # - 0x01e0, 501, # - 0x01e2, 501, # - 0x01e4, 501, # - 0x01e6, 501, # - 0x01e8, 501, # - 0x01ea, 501, # - 0x01ec, 501, # - 0x01ee, 501, # - 0x01f1, 502, # - 0x01f2, 501, # - 0x01f4, 501, # - 0x01fa, 501, # - 0x01fc, 501, # - 0x01fe, 501, # - 0x0200, 501, # - 0x0202, 501, # - 0x0204, 501, # - 0x0206, 501, # - 0x0208, 501, # - 0x020a, 501, # - 0x020c, 501, # - 0x020e, 501, # - 0x0210, 501, # - 0x0212, 501, # - 0x0214, 501, # - 0x0216, 501, # - 0x0386, 538, # - 0x038c, 564, # - 0x03e2, 501, # - 0x03e4, 501, # - 0x03e6, 501, # - 0x03e8, 501, # - 0x03ea, 501, # - 0x03ec, 501, # - 0x03ee, 501, # - 0x0460, 501, # - 0x0462, 501, # - 0x0464, 501, # - 0x0466, 501, # - 0x0468, 501, # - 0x046a, 501, # - 0x046c, 501, # - 0x046e, 501, # - 0x0470, 501, # - 0x0472, 501, # - 0x0474, 501, # - 0x0476, 501, # - 0x0478, 501, # - 0x047a, 501, # - 0x047c, 501, # - 0x047e, 501, # - 0x0480, 501, # - 0x0490, 501, # - 0x0492, 501, # - 0x0494, 501, # - 0x0496, 501, # - 0x0498, 501, # - 0x049a, 501, # - 0x049c, 501, # - 0x049e, 501, # - 0x04a0, 501, # - 0x04a2, 501, # - 0x04a4, 501, # - 0x04a6, 501, # - 0x04a8, 501, # - 0x04aa, 501, # - 0x04ac, 501, # - 0x04ae, 501, # - 0x04b0, 501, # - 0x04b2, 501, # - 0x04b4, 501, # - 0x04b6, 501, # - 0x04b8, 501, # - 0x04ba, 501, # - 0x04bc, 501, # - 0x04be, 501, # - 0x04c1, 501, # - 0x04c3, 501, # - 0x04c7, 501, # - 0x04cb, 501, # - 0x04d0, 501, # - 0x04d2, 501, # - 0x04d4, 501, # - 0x04d6, 501, # - 0x04d8, 501, # - 0x04da, 501, # - 0x04dc, 501, # - 0x04de, 501, # - 0x04e0, 501, # - 0x04e2, 501, # - 0x04e4, 501, # - 0x04e6, 501, # - 0x04e8, 501, # - 0x04ea, 501, # - 0x04ee, 501, # - 0x04f0, 501, # - 0x04f2, 501, # - 0x04f4, 501, # - 0x04f8, 501, # - 0x1e00, 501, # - 0x1e02, 501, # - 0x1e04, 501, # - 0x1e06, 501, # - 0x1e08, 501, # - 0x1e0a, 501, # - 0x1e0c, 501, # - 0x1e0e, 501, # - 0x1e10, 501, # - 0x1e12, 501, # - 0x1e14, 501, # - 0x1e16, 501, # - 0x1e18, 501, # - 0x1e1a, 501, # - 0x1e1c, 501, # - 0x1e1e, 501, # - 0x1e20, 501, # - 0x1e22, 501, # - 0x1e24, 501, # - 0x1e26, 501, # - 0x1e28, 501, # - 0x1e2a, 501, # - 0x1e2c, 501, # - 0x1e2e, 501, # - 0x1e30, 501, # - 0x1e32, 501, # - 0x1e34, 501, # - 0x1e36, 501, # - 0x1e38, 501, # - 0x1e3a, 501, # - 0x1e3c, 501, # - 0x1e3e, 501, # - 0x1e40, 501, # - 0x1e42, 501, # - 0x1e44, 501, # - 0x1e46, 501, # - 0x1e48, 501, # - 0x1e4a, 501, # - 0x1e4c, 501, # - 0x1e4e, 501, # - 0x1e50, 501, # - 0x1e52, 501, # - 0x1e54, 501, # - 0x1e56, 501, # - 0x1e58, 501, # - 0x1e5a, 501, # - 0x1e5c, 501, # - 0x1e5e, 501, # - 0x1e60, 501, # - 0x1e62, 501, # - 0x1e64, 501, # - 0x1e66, 501, # - 0x1e68, 501, # - 0x1e6a, 501, # - 0x1e6c, 501, # - 0x1e6e, 501, # - 0x1e70, 501, # - 0x1e72, 501, # - 0x1e74, 501, # - 0x1e76, 501, # - 0x1e78, 501, # - 0x1e7a, 501, # - 0x1e7c, 501, # - 0x1e7e, 501, # - 0x1e80, 501, # - 0x1e82, 501, # - 0x1e84, 501, # - 0x1e86, 501, # - 0x1e88, 501, # - 0x1e8a, 501, # - 0x1e8c, 501, # - 0x1e8e, 501, # - 0x1e90, 501, # - 0x1e92, 501, # - 0x1e94, 501, # - 0x1ea0, 501, # - 0x1ea2, 501, # - 0x1ea4, 501, # - 0x1ea6, 501, # - 0x1ea8, 501, # - 0x1eaa, 501, # - 0x1eac, 501, # - 0x1eae, 501, # - 0x1eb0, 501, # - 0x1eb2, 501, # - 0x1eb4, 501, # - 0x1eb6, 501, # - 0x1eb8, 501, # - 0x1eba, 501, # - 0x1ebc, 501, # - 0x1ebe, 501, # - 0x1ec0, 501, # - 0x1ec2, 501, # - 0x1ec4, 501, # - 0x1ec6, 501, # - 0x1ec8, 501, # - 0x1eca, 501, # - 0x1ecc, 501, # - 0x1ece, 501, # - 0x1ed0, 501, # - 0x1ed2, 501, # - 0x1ed4, 501, # - 0x1ed6, 501, # - 0x1ed8, 501, # - 0x1eda, 501, # - 0x1edc, 501, # - 0x1ede, 501, # - 0x1ee0, 501, # - 0x1ee2, 501, # - 0x1ee4, 501, # - 0x1ee6, 501, # - 0x1ee8, 501, # - 0x1eea, 501, # - 0x1eec, 501, # - 0x1eee, 501, # - 0x1ef0, 501, # - 0x1ef2, 501, # - 0x1ef4, 501, # - 0x1ef6, 501, # - 0x1ef8, 501, # - 0x1f59, 492, # - 0x1f5b, 492, # - 0x1f5d, 492, # - 0x1f5f, 492, # - 0x1fbc, 491, # - 0x1fcc, 491, # - 0x1fec, 493, # - 0x1ffc, 491] # + 0x0100, 501, # + 0x0102, 501, # + 0x0104, 501, # + 0x0106, 501, # + 0x0108, 501, # + 0x010a, 501, # + 0x010c, 501, # + 0x010e, 501, # + 0x0110, 501, # + 0x0112, 501, # + 0x0114, 501, # + 0x0116, 501, # + 0x0118, 501, # + 0x011a, 501, # + 0x011c, 501, # + 0x011e, 501, # + 0x0120, 501, # + 0x0122, 501, # + 0x0124, 501, # + 0x0126, 501, # + 0x0128, 501, # + 0x012a, 501, # + 0x012c, 501, # + 0x012e, 501, # + 0x0130, 301, # i + 0x0132, 501, # + 0x0134, 501, # + 0x0136, 501, # + 0x0139, 501, # + 0x013b, 501, # + 0x013d, 501, # + 0x013f, 501, # + 0x0141, 501, # + 0x0143, 501, # + 0x0145, 501, # + 0x0147, 501, # + 0x014a, 501, # + 0x014c, 501, # + 0x014e, 501, # + 0x0150, 501, # + 0x0152, 501, # + 0x0154, 501, # + 0x0156, 501, # + 0x0158, 501, # + 0x015a, 501, # + 0x015c, 501, # + 0x015e, 501, # + 0x0160, 501, # + 0x0162, 501, # + 0x0164, 501, # + 0x0166, 501, # + 0x0168, 501, # + 0x016a, 501, # + 0x016c, 501, # + 0x016e, 501, # + 0x0170, 501, # + 0x0172, 501, # + 0x0174, 501, # + 0x0176, 501, # + 0x0178, 379, # + 0x0179, 501, # + 0x017b, 501, # + 0x017d, 501, # + 0x0181, 710, # + 0x0182, 501, # + 0x0184, 501, # + 0x0186, 706, # + 0x0187, 501, # + 0x018b, 501, # + 0x0190, 703, # + 0x0191, 501, # + 0x0193, 705, # + 0x0194, 707, # + 0x0196, 711, # + 0x0197, 709, # + 0x0198, 501, # + 0x019c, 711, # + 0x019d, 713, # + 0x01a0, 501, # + 0x01a2, 501, # + 0x01a4, 501, # + 0x01a7, 501, # + 0x01a9, 718, # + 0x01ac, 501, # + 0x01ae, 718, # + 0x01af, 501, # + 0x01b3, 501, # + 0x01b5, 501, # + 0x01b7, 719, # + 0x01b8, 501, # + 0x01bc, 501, # + 0x01c4, 502, # + 0x01c5, 501, # + 0x01c7, 502, # + 0x01c8, 501, # + 0x01ca, 502, # + 0x01cb, 501, # + 0x01cd, 501, # + 0x01cf, 501, # + 0x01d1, 501, # + 0x01d3, 501, # + 0x01d5, 501, # + 0x01d7, 501, # + 0x01d9, 501, # + 0x01db, 501, # + 0x01de, 501, # + 0x01e0, 501, # + 0x01e2, 501, # + 0x01e4, 501, # + 0x01e6, 501, # + 0x01e8, 501, # + 0x01ea, 501, # + 0x01ec, 501, # + 0x01ee, 501, # + 0x01f1, 502, # + 0x01f2, 501, # + 0x01f4, 501, # + 0x01fa, 501, # + 0x01fc, 501, # + 0x01fe, 501, # + 0x0200, 501, # + 0x0202, 501, # + 0x0204, 501, # + 0x0206, 501, # + 0x0208, 501, # + 0x020a, 501, # + 0x020c, 501, # + 0x020e, 501, # + 0x0210, 501, # + 0x0212, 501, # + 0x0214, 501, # + 0x0216, 501, # + 0x0386, 538, # + 0x038c, 564, # + 0x03e2, 501, # + 0x03e4, 501, # + 0x03e6, 501, # + 0x03e8, 501, # + 0x03ea, 501, # + 0x03ec, 501, # + 0x03ee, 501, # + 0x0460, 501, # + 0x0462, 501, # + 0x0464, 501, # + 0x0466, 501, # + 0x0468, 501, # + 0x046a, 501, # + 0x046c, 501, # + 0x046e, 501, # + 0x0470, 501, # + 0x0472, 501, # + 0x0474, 501, # + 0x0476, 501, # + 0x0478, 501, # + 0x047a, 501, # + 0x047c, 501, # + 0x047e, 501, # + 0x0480, 501, # + 0x0490, 501, # + 0x0492, 501, # + 0x0494, 501, # + 0x0496, 501, # + 0x0498, 501, # + 0x049a, 501, # + 0x049c, 501, # + 0x049e, 501, # + 0x04a0, 501, # + 0x04a2, 501, # + 0x04a4, 501, # + 0x04a6, 501, # + 0x04a8, 501, # + 0x04aa, 501, # + 0x04ac, 501, # + 0x04ae, 501, # + 0x04b0, 501, # + 0x04b2, 501, # + 0x04b4, 501, # + 0x04b6, 501, # + 0x04b8, 501, # + 0x04ba, 501, # + 0x04bc, 501, # + 0x04be, 501, # + 0x04c1, 501, # + 0x04c3, 501, # + 0x04c7, 501, # + 0x04cb, 501, # + 0x04d0, 501, # + 0x04d2, 501, # + 0x04d4, 501, # + 0x04d6, 501, # + 0x04d8, 501, # + 0x04da, 501, # + 0x04dc, 501, # + 0x04de, 501, # + 0x04e0, 501, # + 0x04e2, 501, # + 0x04e4, 501, # + 0x04e6, 501, # + 0x04e8, 501, # + 0x04ea, 501, # + 0x04ee, 501, # + 0x04f0, 501, # + 0x04f2, 501, # + 0x04f4, 501, # + 0x04f8, 501, # + 0x1e00, 501, # + 0x1e02, 501, # + 0x1e04, 501, # + 0x1e06, 501, # + 0x1e08, 501, # + 0x1e0a, 501, # + 0x1e0c, 501, # + 0x1e0e, 501, # + 0x1e10, 501, # + 0x1e12, 501, # + 0x1e14, 501, # + 0x1e16, 501, # + 0x1e18, 501, # + 0x1e1a, 501, # + 0x1e1c, 501, # + 0x1e1e, 501, # + 0x1e20, 501, # + 0x1e22, 501, # + 0x1e24, 501, # + 0x1e26, 501, # + 0x1e28, 501, # + 0x1e2a, 501, # + 0x1e2c, 501, # + 0x1e2e, 501, # + 0x1e30, 501, # + 0x1e32, 501, # + 0x1e34, 501, # + 0x1e36, 501, # + 0x1e38, 501, # + 0x1e3a, 501, # + 0x1e3c, 501, # + 0x1e3e, 501, # + 0x1e40, 501, # + 0x1e42, 501, # + 0x1e44, 501, # + 0x1e46, 501, # + 0x1e48, 501, # + 0x1e4a, 501, # + 0x1e4c, 501, # + 0x1e4e, 501, # + 0x1e50, 501, # + 0x1e52, 501, # + 0x1e54, 501, # + 0x1e56, 501, # + 0x1e58, 501, # + 0x1e5a, 501, # + 0x1e5c, 501, # + 0x1e5e, 501, # + 0x1e60, 501, # + 0x1e62, 501, # + 0x1e64, 501, # + 0x1e66, 501, # + 0x1e68, 501, # + 0x1e6a, 501, # + 0x1e6c, 501, # + 0x1e6e, 501, # + 0x1e70, 501, # + 0x1e72, 501, # + 0x1e74, 501, # + 0x1e76, 501, # + 0x1e78, 501, # + 0x1e7a, 501, # + 0x1e7c, 501, # + 0x1e7e, 501, # + 0x1e80, 501, # + 0x1e82, 501, # + 0x1e84, 501, # + 0x1e86, 501, # + 0x1e88, 501, # + 0x1e8a, 501, # + 0x1e8c, 501, # + 0x1e8e, 501, # + 0x1e90, 501, # + 0x1e92, 501, # + 0x1e94, 501, # + 0x1ea0, 501, # + 0x1ea2, 501, # + 0x1ea4, 501, # + 0x1ea6, 501, # + 0x1ea8, 501, # + 0x1eaa, 501, # + 0x1eac, 501, # + 0x1eae, 501, # + 0x1eb0, 501, # + 0x1eb2, 501, # + 0x1eb4, 501, # + 0x1eb6, 501, # + 0x1eb8, 501, # + 0x1eba, 501, # + 0x1ebc, 501, # + 0x1ebe, 501, # + 0x1ec0, 501, # + 0x1ec2, 501, # + 0x1ec4, 501, # + 0x1ec6, 501, # + 0x1ec8, 501, # + 0x1eca, 501, # + 0x1ecc, 501, # + 0x1ece, 501, # + 0x1ed0, 501, # + 0x1ed2, 501, # + 0x1ed4, 501, # + 0x1ed6, 501, # + 0x1ed8, 501, # + 0x1eda, 501, # + 0x1edc, 501, # + 0x1ede, 501, # + 0x1ee0, 501, # + 0x1ee2, 501, # + 0x1ee4, 501, # + 0x1ee6, 501, # + 0x1ee8, 501, # + 0x1eea, 501, # + 0x1eec, 501, # + 0x1eee, 501, # + 0x1ef0, 501, # + 0x1ef2, 501, # + 0x1ef4, 501, # + 0x1ef6, 501, # + 0x1ef8, 501, # + 0x1f59, 492, # + 0x1f5b, 492, # + 0x1f5d, 492, # + 0x1f5f, 492, # + 0x1fbc, 491, # + 0x1fcc, 491, # + 0x1fec, 493, # + 0x1ffc, 491] # toTitleSinglets = [ - 0x01c4, 501, # - 0x01c6, 499, # - 0x01c7, 501, # - 0x01c9, 499, # - 0x01ca, 501, # - 0x01cc, 499, # - 0x01f1, 501, # - 0x01f3, 499] # + 0x01c4, 501, # + 0x01c6, 499, # + 0x01c7, 501, # + 0x01c9, 499, # + 0x01ca, 501, # + 0x01cc, 499, # + 0x01f1, 501, # + 0x01f3, 499] # -proc binarySearch(c: RuneImpl, tab: openArray[RuneImpl], len, stride: int): int = +proc binarySearch(c: RuneImpl, tab: openArray[RuneImpl], len, stride: int): int = var n = len var t = 0 - while n > 1: + while n > 1: var m = n div 2 var p = t + m*stride if c >= tab[p]: @@ -1131,9 +1156,9 @@ proc binarySearch(c: RuneImpl, tab: openArray[RuneImpl], len, stride: int): int return t return -1 -proc toLower*(c: Rune): Rune {.rtl, extern: "nuc$1", procvar.} = +proc toLower*(c: Rune): Rune {.rtl, extern: "nuc$1", procvar.} = ## Converts `c` into lower case. This works for any Unicode character. - ## If possible, prefer `toLower` over `toUpper`. + ## If possible, prefer `toLower` over `toUpper`. var c = RuneImpl(c) var p = binarySearch(c, tolowerRanges, len(tolowerRanges) div 3, 3) if p >= 0 and c >= tolowerRanges[p] and c <= tolowerRanges[p+1]: @@ -1143,9 +1168,9 @@ proc toLower*(c: Rune): Rune {.rtl, extern: "nuc$1", procvar.} = return Rune(c + tolowerSinglets[p+1] - 500) return Rune(c) -proc toUpper*(c: Rune): Rune {.rtl, extern: "nuc$1", procvar.} = +proc toUpper*(c: Rune): Rune {.rtl, extern: "nuc$1", procvar.} = ## Converts `c` into upper case. This works for any Unicode character. - ## If possible, prefer `toLower` over `toUpper`. + ## If possible, prefer `toLower` over `toUpper`. var c = RuneImpl(c) var p = binarySearch(c, toupperRanges, len(toupperRanges) div 3, 3) if p >= 0 and c >= toupperRanges[p] and c <= toupperRanges[p+1]: @@ -1155,16 +1180,16 @@ proc toUpper*(c: Rune): Rune {.rtl, extern: "nuc$1", procvar.} = return Rune(c + toupperSinglets[p+1] - 500) return Rune(c) -proc toTitle*(c: Rune): Rune {.rtl, extern: "nuc$1", procvar.} = +proc toTitle*(c: Rune): Rune {.rtl, extern: "nuc$1", procvar.} = var c = RuneImpl(c) var p = binarySearch(c, toTitleSinglets, len(toTitleSinglets) div 2, 2) if p >= 0 and c == toTitleSinglets[p]: return Rune(c + toTitleSinglets[p+1] - 500) return Rune(c) -proc isLower*(c: Rune): bool {.rtl, extern: "nuc$1", procvar.} = +proc isLower*(c: Rune): bool {.rtl, extern: "nuc$1", procvar.} = ## returns true iff `c` is a lower case Unicode character - ## If possible, prefer `isLower` over `isUpper`. + ## If possible, prefer `isLower` over `isUpper`. var c = RuneImpl(c) # Note: toUpperRanges is correct here! var p = binarySearch(c, toupperRanges, len(toupperRanges) div 3, 3) @@ -1174,9 +1199,9 @@ proc isLower*(c: Rune): bool {.rtl, extern: "nuc$1", procvar.} = if p >= 0 and c == toupperSinglets[p]: return true -proc isUpper*(c: Rune): bool {.rtl, extern: "nuc$1", procvar.} = +proc isUpper*(c: Rune): bool {.rtl, extern: "nuc$1", procvar.} = ## returns true iff `c` is a upper case Unicode character - ## If possible, prefer `isLower` over `isUpper`. + ## If possible, prefer `isLower` over `isUpper`. var c = RuneImpl(c) # Note: toLowerRanges is correct here! var p = binarySearch(c, tolowerRanges, len(tolowerRanges) div 3, 3) @@ -1186,9 +1211,9 @@ proc isUpper*(c: Rune): bool {.rtl, extern: "nuc$1", procvar.} = if p >= 0 and c == tolowerSinglets[p]: return true -proc isAlpha*(c: Rune): bool {.rtl, extern: "nuc$1", procvar.} = +proc isAlpha*(c: Rune): bool {.rtl, extern: "nuc$1", procvar.} = ## returns true iff `c` is an *alpha* Unicode character (i.e. a letter) - if isUpper(c) or isLower(c): + if isUpper(c) or isLower(c): return true var c = RuneImpl(c) var p = binarySearch(c, alphaRanges, len(alphaRanges) div 2, 2) @@ -1197,11 +1222,11 @@ proc isAlpha*(c: Rune): bool {.rtl, extern: "nuc$1", procvar.} = p = binarySearch(c, alphaSinglets, len(alphaSinglets), 1) if p >= 0 and c == alphaSinglets[p]: return true - -proc isTitle*(c: Rune): bool {.rtl, extern: "nuc$1", procvar.} = + +proc isTitle*(c: Rune): bool {.rtl, extern: "nuc$1", procvar.} = return isUpper(c) and isLower(c) -proc isWhiteSpace*(c: Rune): bool {.rtl, extern: "nuc$1", procvar.} = +proc isWhiteSpace*(c: Rune): bool {.rtl, extern: "nuc$1", procvar.} = ## returns true iff `c` is a Unicode whitespace character var c = RuneImpl(c) var p = binarySearch(c, spaceRanges, len(spaceRanges) div 2, 2) @@ -1228,7 +1253,7 @@ iterator runes*(s: string): Rune = fastRuneAt(s, i, result, true) yield result -proc cmpRunesIgnoreCase*(a, b: string): int {.rtl, extern: "nuc$1", procvar.} = +proc cmpRunesIgnoreCase*(a, b: string): int {.rtl, extern: "nuc$1", procvar.} = ## compares two UTF8 strings and ignores the case. Returns: ## ## | 0 iff a == b diff --git a/lib/pure/unidecode/unidecode.nim b/lib/pure/unidecode/unidecode.nim index 798eef5d0..a83b9be0f 100644 --- a/lib/pure/unidecode/unidecode.nim +++ b/lib/pure/unidecode/unidecode.nim @@ -70,5 +70,5 @@ proc unidecode*(s: string): string = when isMainModule: loadUnidecodeTable("lib/pure/unidecode/unidecode.dat") - echo unidecode("Äußerst") + assert unidecode("Äußerst") == "Ausserst" diff --git a/lib/pure/uri.nim b/lib/pure/uri.nim index b0afb75f9..1890a9bf4 100644 --- a/lib/pure/uri.nim +++ b/lib/pure/uri.nim @@ -53,10 +53,10 @@ proc parseAuthority(authority: string, result: var Uri) = while true: case authority[i] of '@': - result.password = result.port - result.port = "" - result.username = result.hostname - result.hostname = "" + swap result.password, result.port + result.port.setLen(0) + swap result.username, result.hostname + result.hostname.setLen(0) inPort = false of ':': inPort = true @@ -75,7 +75,7 @@ proc parsePath(uri: string, i: var int, result: var Uri) = # The 'mailto' scheme's PATH actually contains the hostname/username if result.scheme.toLower == "mailto": parseAuthority(result.path, result) - result.path = "" + result.path.setLen(0) if uri[i] == '?': i.inc # Skip '?' @@ -85,13 +85,21 @@ proc parsePath(uri: string, i: var int, result: var Uri) = i.inc # Skip '#' i.inc parseUntil(uri, result.anchor, {}, i) -proc initUri(): Uri = +proc initUri*(): Uri = + ## Initializes a URI. result = Uri(scheme: "", username: "", password: "", hostname: "", port: "", path: "", query: "", anchor: "") -proc parseUri*(uri: string): Uri = - ## Parses a URI. - result = initUri() +proc resetUri(uri: var Uri) = + for f in uri.fields: + when f is string: + f.setLen(0) + else: + f = false + +proc parseUri*(uri: string, result: var Uri) = + ## Parses a URI. The `result` variable will be cleared before. + resetUri(result) var i = 0 @@ -105,7 +113,7 @@ proc parseUri*(uri: string): Uri = if uri[i] != ':': # Assume this is a reference URI (relative URI) i = 0 - result.scheme = "" + result.scheme.setLen(0) parsePath(uri, i, result) return i.inc # Skip ':' @@ -124,6 +132,11 @@ proc parseUri*(uri: string): Uri = # Path parsePath(uri, i, result) +proc parseUri*(uri: string): Uri = + ## Parses a URI and returns it. + result = initUri() + parseUri(uri, result) + proc removeDotSegments(path: string): string = var collection: seq[string] = @[] let endsWithSlash = path[path.len-1] == '/' diff --git a/lib/pure/xmldomparser.nim b/lib/pure/xmldomparser.nim index 7f34d72a8..050362435 100644 --- a/lib/pure/xmldomparser.nim +++ b/lib/pure/xmldomparser.nim @@ -155,7 +155,7 @@ proc loadXMLFile*(path: string): PDocument = return loadXMLStream(s) -when isMainModule: +when not defined(testing) and isMainModule: var xml = loadXMLFile("nim/xmldom/test.xml") #echo(xml.getElementsByTagName("m:test2")[0].namespaceURI) #echo(xml.getElementsByTagName("bla:test")[0].namespaceURI) diff --git a/lib/pure/xmlparser.nim b/lib/pure/xmlparser.nim index 755bfcdbc..840cae734 100644 --- a/lib/pure/xmlparser.nim +++ b/lib/pure/xmlparser.nim @@ -143,7 +143,7 @@ proc loadXml*(path: string): XmlNode = result = loadXml(path, errors) if errors.len > 0: raiseInvalidXml(errors) -when isMainModule: +when not defined(testing) and isMainModule: import os var errors: seq[string] = @[] diff --git a/lib/pure/xmltree.nim b/lib/pure/xmltree.nim index 0bf5b52a4..7526a989a 100644 --- a/lib/pure/xmltree.nim +++ b/lib/pure/xmltree.nim @@ -353,5 +353,6 @@ proc findAll*(n: XmlNode, tag: string): seq[XmlNode] = findAll(n, tag, result) when isMainModule: - assert """<a href="http://nim-lang.org">Nim rules.</a>""" == + let link = "http://nim-lang.org" + assert """<a href="""" & escape(link) & """">Nim rules.</a>""" == $(<>a(href="http://nim-lang.org", newText("Nim rules."))) diff --git a/lib/system.nim b/lib/system.nim index ba0690ace..1e6f76f3d 100644 --- a/lib/system.nim +++ b/lib/system.nim @@ -76,6 +76,10 @@ type void* {.magic: "VoidType".} ## meta type to denote the absence of any type auto* = expr any* = distinct auto + untyped* {.magic: Expr.} ## meta type to denote an expression that + ## is not resolved (for templates) + typed* {.magic: Stmt.} ## meta type to denote an expression that + ## is resolved (for templates) SomeSignedInt* = int|int8|int16|int32|int64 ## type class matching all signed integer types @@ -173,12 +177,6 @@ proc new*(T: typedesc): ref T = ## reference to it as result value new(result) -proc unsafeNew*[T](a: var ref T, size: int) {.magic: "New", noSideEffect.} - ## creates a new object of type ``T`` and returns a safe (traced) - ## reference to it in ``a``. This is **unsafe** as it allocates an object - ## of the passed ``size``. This should only be used for optimization - ## purposes when you know what you're doing! - proc internalNew*[T](a: var ref T) {.magic: "New", noSideEffect.} ## leaked implementation detail. Do not use. @@ -513,7 +511,13 @@ type ESynch: Exception ].} -proc sizeof*[T](x: T): Natural {.magic: "SizeOf", noSideEffect.} +proc unsafeNew*[T](a: var ref T, size: Natural) {.magic: "New", noSideEffect.} + ## creates a new object of type ``T`` and returns a safe (traced) + ## reference to it in ``a``. This is **unsafe** as it allocates an object + ## of the passed ``size``. This should only be used for optimization + ## purposes when you know what you're doing! + +proc sizeof*[T](x: T): int {.magic: "SizeOf", noSideEffect.} ## returns the size of ``x`` in bytes. Since this is a low-level proc, ## its usage is discouraged - using ``new`` for the most cases suffices ## that one never needs to know ``x``'s size. As a special semantic rule, @@ -547,7 +551,7 @@ proc dec*[T: Ordinal|uint|uint64](x: var T, y = 1) {.magic: "Dec", noSideEffect. ## exist, ``EOutOfRange`` is raised or a compile time error occurs. This is a ## short notation for: ``x = pred(x, y)``. -proc newSeq*[T](s: var seq[T], len: int) {.magic: "NewSeq", noSideEffect.} +proc newSeq*[T](s: var seq[T], len: Natural) {.magic: "NewSeq", noSideEffect.} ## creates a new sequence of type ``seq[T]`` with length ``len``. ## This is equivalent to ``s = @[]; setlen(s, len)``, but more ## efficient since no reallocation is needed. @@ -565,7 +569,7 @@ proc newSeq*[T](s: var seq[T], len: int) {.magic: "NewSeq", noSideEffect.} ## inputStrings[2] = "would crash" ## #inputStrings[3] = "out of bounds" -proc newSeq*[T](len = 0): seq[T] = +proc newSeq*[T](len = 0.Natural): seq[T] = ## creates a new sequence of type ``seq[T]`` with length ``len``. ## ## Note that the sequence will be filled with zeroed entries, which can be a @@ -659,7 +663,7 @@ proc `+` *(x: int): int {.magic: "UnaryPlusI", noSideEffect.} proc `+` *(x: int8): int8 {.magic: "UnaryPlusI", noSideEffect.} proc `+` *(x: int16): int16 {.magic: "UnaryPlusI", noSideEffect.} proc `+` *(x: int32): int32 {.magic: "UnaryPlusI", noSideEffect.} -proc `+` *(x: int64): int64 {.magic: "UnaryPlusI64", noSideEffect.} +proc `+` *(x: int64): int64 {.magic: "UnaryPlusI", noSideEffect.} ## Unary `+` operator for an integer. Has no effect. proc `-` *(x: int): int {.magic: "UnaryMinusI", noSideEffect.} @@ -704,6 +708,7 @@ proc `div` *(x, y: int32): int32 {.magic: "DivI", noSideEffect.} proc `div` *(x, y: int64): int64 {.magic: "DivI64", noSideEffect.} ## computes the integer division. This is roughly the same as ## ``floor(x/y)``. + ## ## .. code-block:: Nim ## 1 div 2 == 0 ## 2 div 2 == 1 @@ -723,6 +728,7 @@ proc `shr` *(x, y: int16): int16 {.magic: "ShrI", noSideEffect.} proc `shr` *(x, y: int32): int32 {.magic: "ShrI", noSideEffect.} proc `shr` *(x, y: int64): int64 {.magic: "ShrI64", noSideEffect.} ## computes the `shift right` operation of `x` and `y`. + ## ## .. code-block:: Nim ## 0b0001_0000'i8 shr 2 == 0b0100_0000'i8 ## 0b1000_0000'i8 shr 2 == 0b0000_0000'i8 @@ -937,7 +943,7 @@ proc `@` * [IDX, T](a: array[IDX, T]): seq[T] {. ## sequences with the array constructor: ``@[1, 2, 3]`` has the type ## ``seq[int]``, while ``[1, 2, 3]`` has the type ``array[0..2, int]``. -proc setLen*[T](s: var seq[T], newlen: int) {. +proc setLen*[T](s: var seq[T], newlen: Natural) {. magic: "SetLengthSeq", noSideEffect.} ## sets the length of `s` to `newlen`. ## ``T`` may be any sequence type. @@ -945,14 +951,14 @@ proc setLen*[T](s: var seq[T], newlen: int) {. ## ``s`` will be truncated. `s` cannot be nil! To initialize a sequence with ## a size, use ``newSeq`` instead. -proc setLen*(s: var string, newlen: int) {. +proc setLen*(s: var string, newlen: Natural) {. magic: "SetLengthStr", noSideEffect.} ## sets the length of `s` to `newlen`. ## If the current length is greater than the new length, ## ``s`` will be truncated. `s` cannot be nil! To initialize a string with ## a size, use ``newString`` instead. -proc newString*(len: int): string {. +proc newString*(len: Natural): string {. magic: "NewString", importc: "mnewString", noSideEffect.} ## returns a new string of length ``len`` but with uninitialized ## content. One needs to fill the string character after character @@ -960,7 +966,7 @@ proc newString*(len: int): string {. ## optimization purposes; the same effect can be achieved with the ## ``&`` operator or with ``add``. -proc newStringOfCap*(cap: int): string {. +proc newStringOfCap*(cap: Natural): string {. magic: "NewStringOfCap", importc: "rawNewString", noSideEffect.} ## returns a new string of length ``0`` but with capacity `cap`.This ## procedure exists only for optimization purposes; the same effect can @@ -1104,7 +1110,7 @@ var programResult* {.exportc: "nim_program_result".}: int ## prematurely using ``quit``, this value is ignored. proc quit*(errorcode: int = QuitSuccess) {. - magic: "Exit", importc: "exit", header: "<stdlib.h>", noReturn.} + magic: "Exit", importc: "exit", header: "<stdlib.h>", noreturn.} ## Stops the program immediately with an exit code. ## ## Before stopping the program the "quit procedures" are called in the @@ -1150,21 +1156,21 @@ proc shallowCopy*[T](x: var T, y: T) {.noSideEffect, magic: "ShallowCopy".} ## is a reason why the default assignment does a deep copy of sequences ## and strings. -proc del*[T](x: var seq[T], i: int) {.noSideEffect.} = +proc del*[T](x: var seq[T], i: Natural) {.noSideEffect.} = ## deletes the item at index `i` by putting ``x[high(x)]`` into position `i`. ## This is an O(1) operation. let xl = x.len shallowCopy(x[i], x[xl-1]) setLen(x, xl-1) -proc delete*[T](x: var seq[T], i: int) {.noSideEffect.} = +proc delete*[T](x: var seq[T], i: Natural) {.noSideEffect.} = ## deletes the item at index `i` by moving ``x[i+1..]`` by one position. ## This is an O(n) operation. let xl = x.len for j in i..xl-2: shallowCopy(x[j], x[j+1]) setLen(x, xl-1) -proc insert*[T](x: var seq[T], item: T, i = 0) {.noSideEffect.} = +proc insert*[T](x: var seq[T], item: T, i = 0.Natural) {.noSideEffect.} = ## inserts `item` into `x` at position `i`. let xl = x.len setLen(x, xl+1) @@ -1310,19 +1316,19 @@ proc substr*(s: string, first, last: int): string {. ## or `limit`:idx: a string's length. when not defined(nimrodVM): - proc zeroMem*(p: pointer, size: int) {.importc, noDecl, benign.} + proc zeroMem*(p: pointer, size: Natural) {.importc, noDecl, benign.} ## overwrites the contents of the memory at ``p`` with the value 0. ## Exactly ``size`` bytes will be overwritten. Like any procedure ## dealing with raw memory this is *unsafe*. - proc copyMem*(dest, source: pointer, size: int) {. + proc copyMem*(dest, source: pointer, size: Natural) {. importc: "memcpy", header: "<string.h>", benign.} ## copies the contents from the memory at ``source`` to the memory ## at ``dest``. Exactly ``size`` bytes will be copied. The memory ## regions may not overlap. Like any procedure dealing with raw ## memory this is *unsafe*. - proc moveMem*(dest, source: pointer, size: int) {. + proc moveMem*(dest, source: pointer, size: Natural) {. importc: "memmove", header: "<string.h>", benign.} ## copies the contents from the memory at ``source`` to the memory ## at ``dest``. Exactly ``size`` bytes will be copied. The memory @@ -1330,7 +1336,7 @@ when not defined(nimrodVM): ## and is thus somewhat more safe than ``copyMem``. Like any procedure ## dealing with raw memory this is still *unsafe*, though. - proc equalMem*(a, b: pointer, size: int): bool {. + proc equalMem*(a, b: pointer, size: Natural): bool {. importc: "equalMem", noDecl, noSideEffect.} ## compares the memory blocks ``a`` and ``b``. ``size`` bytes will ## be compared. If the blocks are equal, true is returned, false @@ -1338,7 +1344,7 @@ when not defined(nimrodVM): ## *unsafe*. when hostOS != "standalone": - proc alloc*(size: int): pointer {.noconv, rtl, tags: [], benign.} + proc alloc*(size: Natural): pointer {.noconv, rtl, tags: [], benign.} ## allocates a new memory block with at least ``size`` bytes. The ## block has to be freed with ``realloc(block, 0)`` or ## ``dealloc(block)``. The block is not initialized, so reading @@ -1353,7 +1359,7 @@ when not defined(nimrodVM): ## The allocated memory belongs to its allocating thread! ## Use `createSharedU` to allocate from a shared heap. cast[ptr T](alloc(T.sizeof * size)) - proc alloc0*(size: int): pointer {.noconv, rtl, tags: [], benign.} + proc alloc0*(size: Natural): pointer {.noconv, rtl, tags: [], benign.} ## allocates a new memory block with at least ``size`` bytes. The ## block has to be freed with ``realloc(block, 0)`` or ## ``dealloc(block)``. The block is initialized with all bytes @@ -1368,8 +1374,8 @@ when not defined(nimrodVM): ## The allocated memory belongs to its allocating thread! ## Use `createShared` to allocate from a shared heap. cast[ptr T](alloc0(T.sizeof * size)) - proc realloc*(p: pointer, newSize: int): pointer {.noconv, rtl, tags: [], - benign.} + proc realloc*(p: pointer, newSize: Natural): pointer {.noconv, rtl, tags: [], + benign.} ## grows or shrinks a given memory block. If p is **nil** then a new ## memory block is returned. In either way the block has at least ## ``newSize`` bytes. If ``newSize == 0`` and p is not **nil** @@ -1396,7 +1402,7 @@ when not defined(nimrodVM): ## Use `deallocShared` to deallocate from a shared heap. proc free*[T](p: ptr T) {.inline, benign.} = dealloc(p) - proc allocShared*(size: int): pointer {.noconv, rtl, benign.} + proc allocShared*(size: Natural): pointer {.noconv, rtl, benign.} ## allocates a new memory block on the shared heap with at ## least ``size`` bytes. The block has to be freed with ## ``reallocShared(block, 0)`` or ``deallocShared(block)``. The block @@ -1410,7 +1416,7 @@ when not defined(nimrodVM): ## is not initialized, so reading from it before writing to it is ## undefined behaviour! cast[ptr T](allocShared(T.sizeof * size)) - proc allocShared0*(size: int): pointer {.noconv, rtl, benign.} + proc allocShared0*(size: Natural): pointer {.noconv, rtl, benign.} ## allocates a new memory block on the shared heap with at ## least ``size`` bytes. The block has to be freed with ## ``reallocShared(block, 0)`` or ``deallocShared(block)``. @@ -1423,8 +1429,8 @@ when not defined(nimrodVM): ## The block is initialized with all bytes ## containing zero, so it is somewhat safer than ``createSharedU``. cast[ptr T](allocShared0(T.sizeof * size)) - proc reallocShared*(p: pointer, newSize: int): pointer {.noconv, rtl, - benign.} + proc reallocShared*(p: pointer, newSize: Natural): pointer {.noconv, rtl, + benign.} ## grows or shrinks a given memory block on the heap. If p is **nil** ## then a new memory block is returned. In either way the block has at ## least ``newSize`` bytes. If ``newSize == 0`` and p is not **nil** @@ -1526,10 +1532,10 @@ const NimMajor*: int = 0 ## is the major number of Nim's version. - NimMinor*: int = 10 + NimMinor*: int = 11 ## is the minor number of Nim's version. - NimPatch*: int = 3 + NimPatch*: int = 2 ## is the patch number of Nim's version. NimVersion*: string = $NimMajor & "." & $NimMinor & "." & $NimPatch @@ -1632,7 +1638,7 @@ proc min*(x, y: int16): int16 {.magic: "MinI", noSideEffect.} = if x <= y: x else: y proc min*(x, y: int32): int32 {.magic: "MinI", noSideEffect.} = if x <= y: x else: y -proc min*(x, y: int64): int64 {.magic: "MinI64", noSideEffect.} = +proc min*(x, y: int64): int64 {.magic: "MinI", noSideEffect.} = ## The minimum value of two integers. if x <= y: x else: y @@ -1650,7 +1656,7 @@ proc max*(x, y: int16): int16 {.magic: "MaxI", noSideEffect.} = if y <= x: x else: y proc max*(x, y: int32): int32 {.magic: "MaxI", noSideEffect.} = if y <= x: x else: y -proc max*(x, y: int64): int64 {.magic: "MaxI64", noSideEffect.} = +proc max*(x, y: int64): int64 {.magic: "MaxI", noSideEffect.} = ## The maximum value of two integers. if y <= x: x else: y @@ -1738,6 +1744,12 @@ iterator items*(E: typedesc[enum]): E = for v in low(E)..high(E): yield v +iterator items*[T](s: Slice[T]): T = + ## iterates over the slice `s`, yielding each value between `s.a` and `s.b` + ## (inclusively). + for x in s.a..s.b: + yield x + iterator pairs*[T](a: openArray[T]): tuple[key: int, val: T] {.inline.} = ## iterates over each item of `a`. Yields ``(index, a[index])`` pairs. var i = 0 @@ -2258,20 +2270,21 @@ when hostOS == "standalone": include panicoverride when not declared(sysFatal): - template sysFatal(exceptn: typedesc, message: string) = - when hostOS == "standalone": + when hostOS == "standalone": + proc sysFatal(exceptn: typedesc, message: string) {.inline.} = panic(message) - else: + + proc sysFatal(exceptn: typedesc, message, arg: string) {.inline.} = + rawoutput(message) + panic(arg) + else: + proc sysFatal(exceptn: typedesc, message: string) {.inline, noReturn.} = var e: ref exceptn new(e) e.msg = message raise e - template sysFatal(exceptn: typedesc, message, arg: string) = - when hostOS == "standalone": - rawoutput(message) - panic(arg) - else: + proc sysFatal(exceptn: typedesc, message, arg: string) {.inline, noReturn.} = var e: ref exceptn new(e) e.msg = message & arg @@ -2487,37 +2500,37 @@ when not defined(JS): #and not defined(NimrodVM): proc getFileSize*(f: File): int64 {.tags: [ReadIOEffect], benign.} ## retrieves the file size (in bytes) of `f`. - proc readBytes*(f: File, a: var openArray[int8|uint8], start, len: int): int {. + proc readBytes*(f: File, a: var openArray[int8|uint8], start, len: Natural): int {. tags: [ReadIOEffect], benign.} ## reads `len` bytes into the buffer `a` starting at ``a[start]``. Returns ## the actual number of bytes that have been read which may be less than ## `len` (if not as many bytes are remaining), but not greater. - proc readChars*(f: File, a: var openArray[char], start, len: int): int {. + proc readChars*(f: File, a: var openArray[char], start, len: Natural): int {. tags: [ReadIOEffect], benign.} ## reads `len` bytes into the buffer `a` starting at ``a[start]``. Returns ## the actual number of bytes that have been read which may be less than ## `len` (if not as many bytes are remaining), but not greater. - proc readBuffer*(f: File, buffer: pointer, len: int): int {. + proc readBuffer*(f: File, buffer: pointer, len: Natural): int {. tags: [ReadIOEffect], benign.} ## reads `len` bytes into the buffer pointed to by `buffer`. Returns ## the actual number of bytes that have been read which may be less than ## `len` (if not as many bytes are remaining), but not greater. - proc writeBytes*(f: File, a: openArray[int8|uint8], start, len: int): int {. + proc writeBytes*(f: File, a: openArray[int8|uint8], start, len: Natural): int {. tags: [WriteIOEffect], benign.} ## writes the bytes of ``a[start..start+len-1]`` to the file `f`. Returns ## the number of actual written bytes, which may be less than `len` in case ## of an error. - proc writeChars*(f: File, a: openArray[char], start, len: int): int {. + proc writeChars*(f: File, a: openArray[char], start, len: Natural): int {. tags: [WriteIOEffect], benign.} ## writes the bytes of ``a[start..start+len-1]`` to the file `f`. Returns ## the number of actual written bytes, which may be less than `len` in case ## of an error. - proc writeBuffer*(f: File, buffer: pointer, len: int): int {. + proc writeBuffer*(f: File, buffer: pointer, len: Natural): int {. tags: [WriteIOEffect], benign.} ## writes the bytes of buffer pointed to by the parameter `buffer` to the ## file `f`. Returns the number of actual written bytes, which may be less @@ -2539,7 +2552,7 @@ when not defined(JS): #and not defined(NimrodVM): when not defined(nimfix): {.deprecated: [fileHandle: getFileHandle].} - proc cstringArrayToSeq*(a: cstringArray, len: int): seq[string] = + proc cstringArrayToSeq*(a: cstringArray, len: Natural): seq[string] = ## converts a ``cstringArray`` to a ``seq[string]``. `a` is supposed to be ## of length ``len``. newSeq(result, len) @@ -2809,14 +2822,14 @@ elif defined(JS): proc getTotalMem(): int = return -1 proc dealloc(p: pointer) = discard - proc alloc(size: int): pointer = discard - proc alloc0(size: int): pointer = discard - proc realloc(p: pointer, newsize: int): pointer = discard + proc alloc(size: Natural): pointer = discard + proc alloc0(size: Natural): pointer = discard + proc realloc(p: pointer, newsize: Natural): pointer = discard - proc allocShared(size: int): pointer = discard - proc allocShared0(size: int): pointer = discard + proc allocShared(size: Natural): pointer = discard + proc allocShared0(size: Natural): pointer = discard proc deallocShared(p: pointer) = discard - proc reallocShared(p: pointer, newsize: int): pointer = discard + proc reallocShared(p: pointer, newsize: Natural): pointer = discard when defined(JS): include "system/jssys" @@ -3028,8 +3041,8 @@ proc instantiationInfo*(index = -1, fullPaths = false): tuple[ ## result = a[pos] ## ## when isMainModule: - ## testException(EInvalidIndex, tester(30)) - ## testException(EInvalidIndex, tester(1)) + ## testException(IndexError, tester(30)) + ## testException(IndexError, tester(1)) ## # --> Test failure at example.nim:20 with 'tester(1)' template currentSourcePath*: string = instantiationInfo(-1, true).filename @@ -3038,13 +3051,12 @@ template currentSourcePath*: string = instantiationInfo(-1, true).filename proc raiseAssert*(msg: string) {.noinline.} = sysFatal(AssertionError, msg) -when true: - proc failedAssertImpl*(msg: string) {.raises: [], tags: [].} = - # trick the compiler to not list ``EAssertionFailed`` when called - # by ``assert``. - type THide = proc (msg: string) {.noinline, raises: [], noSideEffect, - tags: [].} - THide(raiseAssert)(msg) +proc failedAssertImpl*(msg: string) {.raises: [], tags: [].} = + # trick the compiler to not list ``AssertionError`` when called + # by ``assert``. + type THide = proc (msg: string) {.noinline, raises: [], noSideEffect, + tags: [].} + THide(raiseAssert)(msg) template assert*(cond: bool, msg = "") = ## Raises ``AssertionError`` with `msg` if `cond` is false. Note @@ -3109,24 +3121,18 @@ when not defined(nimhygiene): template onFailedAssert*(msg: expr, code: stmt): stmt {.dirty, immediate.} = ## Sets an assertion failure handler that will intercept any assert - ## statements following `onFailedAssert` in the current lexical scope. - ## Can be defined multiple times in a single function. + ## statements following `onFailedAssert` in the current module scope. ## ## .. code-block:: nim - ## - ## proc example(x: int): TErrorCode = - ## onFailedAssert(msg): - ## log msg - ## return E_FAIL - ## - ## assert(...) - ## - ## onFailedAssert(msg): - ## raise newException(EMyException, msg) - ## - ## assert(...) - ## - template failedAssertImpl(msgIMPL: string): stmt {.dirty, immediate.} = + ## # module-wide policy to change the failed assert + ## # exception type in order to include a lineinfo + ## onFailedAssert(msg): + ## var e = new(TMyError) + ## e.msg = msg + ## e.lineinfo = instantiationInfo(-2) + ## raise e + ## + template failedAssertImpl(msgIMPL: string): stmt {.dirty.} = let msg = msgIMPL code @@ -3162,7 +3168,7 @@ when false: payload() when hostOS != "standalone": - proc insert*(x: var string, item: string, i = 0) {.noSideEffect.} = + proc insert*(x: var string, item: string, i = 0.Natural) {.noSideEffect.} = ## inserts `item` into `x` at position `i`. var xl = x.len setLen(x, xl+item.len) @@ -3202,7 +3208,7 @@ when hostOS != "standalone": if x == nil: x = y else: x.add(y) -proc locals*(): RootObj {.magic: "Locals", noSideEffect.} = +proc locals*(): RootObj {.magic: "Plugin", noSideEffect.} = ## generates a tuple constructor expression listing all the local variables ## in the current scope. This is quite fast as it does not rely ## on any debug or runtime information. Note that in constrast to what @@ -3251,4 +3257,20 @@ proc `^`*(x: int): int {.noSideEffect, magic: "Roof".} = ## overloaded ``[]`` or ``[]=`` accessors. discard +template `..^`*(a, b: expr): expr = + ## a shortcut for '.. ^' to avoid the common gotcha that a space between + ## '..' and '^' is required. + a .. ^b + +template `..<`*(a, b: expr): expr = + ## a shortcut for '.. <' to avoid the common gotcha that a space between + ## '..' and '<' is required. + a .. <b + +proc xlen*(x: string): int {.magic: "XLenStr", noSideEffect.} = discard +proc xlen*[T](x: seq[T]): int {.magic: "XLenSeq", noSideEffect.} = + ## returns the length of a sequence or a string without testing for 'nil'. + ## This is an optimization that rarely makes sense. + discard + {.pop.} #{.push warning[GcMem]: off.} diff --git a/lib/system/alloc.nim b/lib/system/alloc.nim index fd3ced832..ad3419808 100644 --- a/lib/system/alloc.nim +++ b/lib/system/alloc.nim @@ -8,7 +8,7 @@ # # Low level allocator for Nim. Has been designed to support the GC. -# TODO: +# TODO: # - eliminate "used" field # - make searching for block O(1) {.push profiler:off.} @@ -21,37 +21,37 @@ # used with a size of 0: const weirdUnmap = not (defined(amd64) or defined(i386)) or defined(windows) -when defined(posix): +when defined(posix): const - PROT_READ = 1 # page can be read - PROT_WRITE = 2 # page can be written - MAP_PRIVATE = 2'i32 # Changes are private - + PROT_READ = 1 # page can be read + PROT_WRITE = 2 # page can be written + MAP_PRIVATE = 2'i32 # Changes are private + when defined(macosx) or defined(bsd): const MAP_ANONYMOUS = 0x1000 - elif defined(solaris): + elif defined(solaris): const MAP_ANONYMOUS = 0x100 else: var MAP_ANONYMOUS {.importc: "MAP_ANONYMOUS", header: "<sys/mman.h>".}: cint - + proc mmap(adr: pointer, len: int, prot, flags, fildes: cint, off: int): pointer {.header: "<sys/mman.h>".} proc munmap(adr: pointer, len: int) {.header: "<sys/mman.h>".} - - proc osAllocPages(size: int): pointer {.inline.} = - result = mmap(nil, size, PROT_READ or PROT_WRITE, + + proc osAllocPages(size: int): pointer {.inline.} = + result = mmap(nil, size, PROT_READ or PROT_WRITE, MAP_PRIVATE or MAP_ANONYMOUS, -1, 0) if result == nil or result == cast[pointer](-1): raiseOutOfMem() - + proc osDeallocPages(p: pointer, size: int) {.inline} = when reallyOsDealloc: munmap(p, size) - -elif defined(windows): + +elif defined(windows): const - MEM_RESERVE = 0x2000 + MEM_RESERVE = 0x2000 MEM_COMMIT = 0x1000 MEM_TOP_DOWN = 0x100000 PAGE_READWRITE = 0x04 @@ -62,12 +62,12 @@ elif defined(windows): proc virtualAlloc(lpAddress: pointer, dwSize: int, flAllocationType, flProtect: int32): pointer {. header: "<windows.h>", stdcall, importc: "VirtualAlloc".} - - proc virtualFree(lpAddress: pointer, dwSize: int, + + proc virtualFree(lpAddress: pointer, dwSize: int, dwFreeType: int32) {.header: "<windows.h>", stdcall, importc: "VirtualFree".} - - proc osAllocPages(size: int): pointer {.inline.} = + + proc osAllocPages(size: int): pointer {.inline.} = result = virtualAlloc(nil, size, MEM_RESERVE or MEM_COMMIT, PAGE_READWRITE) if result == nil: raiseOutOfMem() @@ -82,7 +82,7 @@ elif defined(windows): when reallyOsDealloc: virtualFree(p, 0, MEM_RELEASE) #VirtualFree(p, size, MEM_DECOMMIT) -else: +else: {.error: "Port memory manager to your platform".} # --------------------- end of non-portable code ----------------------------- @@ -97,17 +97,17 @@ const InitialMemoryRequest = ChunkOsReturn div 2 # < ChunkOsReturn! SmallChunkSize = PageSize -type +type PTrunk = ptr TTrunk - TTrunk {.final.} = object + TTrunk {.final.} = object next: PTrunk # all nodes are connected with this pointer key: int # start address at bit 0 bits: array[0..IntsPerTrunk-1, int] # a bit vector - + TTrunkBuckets = array[0..255, PTrunk] - TIntSet {.final.} = object + TIntSet {.final.} = object data: TTrunkBuckets - + type TAlignType = BiggestFloat TFreeCell {.final, pure.} = object @@ -123,14 +123,14 @@ type prevSize: int # size of previous chunk; for coalescing size: int # if < PageSize it is a small chunk used: bool # later will be optimized into prevSize... - + TSmallChunk = object of TBaseChunk next, prev: PSmallChunk # chunks of the same size freeList: ptr TFreeCell - free: int # how many bytes remain + free: int # how many bytes remain acc: int # accumulator for small object allocation data: TAlignType # start of usable memory - + TBigChunk = object of TBaseChunk # not necessarily > PageSize! next, prev: PBigChunk # chunks of the same (or bigger) size align: int @@ -139,7 +139,7 @@ type template smallChunkOverhead(): expr = sizeof(TSmallChunk)-sizeof(TAlignType) template bigChunkOverhead(): expr = sizeof(TBigChunk)-sizeof(TAlignType) -proc roundup(x, v: int): int {.inline.} = +proc roundup(x, v: int): int {.inline.} = result = (x + (v-1)) and not (v-1) sysAssert(result >= x, "roundup: result < x") #return ((-x) and (v-1)) +% x @@ -153,7 +153,7 @@ sysAssert(roundup(65, 8) == 72, "roundup broken 2") # endings of big chunks. This is needed by the merging operation. The only # remaining operation is best-fit for big chunks. Since there is a size-limit # for big chunks (because greater than the limit means they are returned back -# to the OS), a fixed size array can be used. +# to the OS), a fixed size array can be used. type PLLChunk = ptr TLLChunk @@ -163,21 +163,21 @@ type next: PLLChunk # next low-level chunk; only needed for dealloc PAvlNode = ptr TAvlNode - TAvlNode {.pure, final.} = object - link: array[0..1, PAvlNode] # Left (0) and right (1) links + TAvlNode {.pure, final.} = object + link: array[0..1, PAvlNode] # Left (0) and right (1) links key, upperBound: int level: int - + TMemRegion {.final, pure.} = object minLargeObj, maxLargeObj: int freeSmallChunks: array[0..SmallChunkSize div MemAlign-1, PSmallChunk] llmem: PLLChunk currMem, maxMem, freeMem: int # memory sizes (allocated from OS) - lastSize: int # needed for the case that OS gives us pages linearly + lastSize: int # needed for the case that OS gives us pages linearly freeChunksList: PBigChunk # XXX make this a datastructure with O(1) access chunkStarts: TIntSet root, deleted, last, freeAvlNodes: PAvlNode - + # shared: var bottomData: TAvlNode @@ -191,7 +191,7 @@ proc initAllocator() = bottom.link[1] = bottom {.pop.} -proc incCurrMem(a: var TMemRegion, bytes: int) {.inline.} = +proc incCurrMem(a: var TMemRegion, bytes: int) {.inline.} = inc(a.currMem, bytes) proc decCurrMem(a: var TMemRegion, bytes: int) {.inline.} = @@ -199,11 +199,11 @@ proc decCurrMem(a: var TMemRegion, bytes: int) {.inline.} = dec(a.currMem, bytes) proc getMaxMem(a: var TMemRegion): int = - # Since we update maxPagesCount only when freeing pages, + # Since we update maxPagesCount only when freeing pages, # maxPagesCount may not be up to date. Thus we use the # maximum of these both values here: result = max(a.currMem, a.maxMem) - + proc llAlloc(a: var TMemRegion, size: int): pointer = # *low-level* alloc for the memory managers data structures. Deallocation # is done at he end of the allocator's life time. @@ -251,15 +251,15 @@ proc llDeallocAll(a: var TMemRegion) = var next = it.next osDeallocPages(it, PageSize) it = next - -proc intSetGet(t: TIntSet, key: int): PTrunk = + +proc intSetGet(t: TIntSet, key: int): PTrunk = var it = t.data[key and high(t.data)] - while it != nil: + while it != nil: if it.key == key: return it it = it.next result = nil -proc intSetPut(a: var TMemRegion, t: var TIntSet, key: int): PTrunk = +proc intSetPut(a: var TMemRegion, t: var TIntSet, key: int): PTrunk = result = intSetGet(t, key) if result == nil: result = cast[PTrunk](llAlloc(a, sizeof(result[]))) @@ -267,20 +267,20 @@ proc intSetPut(a: var TMemRegion, t: var TIntSet, key: int): PTrunk = t.data[key and high(t.data)] = result result.key = key -proc contains(s: TIntSet, key: int): bool = +proc contains(s: TIntSet, key: int): bool = var t = intSetGet(s, key shr TrunkShift) - if t != nil: + if t != nil: var u = key and TrunkMask result = (t.bits[u shr IntShift] and (1 shl (u and IntMask))) != 0 - else: + else: result = false - -proc incl(a: var TMemRegion, s: var TIntSet, key: int) = + +proc incl(a: var TMemRegion, s: var TIntSet, key: int) = var t = intSetPut(a, s, key shr TrunkShift) var u = key and TrunkMask t.bits[u shr IntShift] = t.bits[u shr IntShift] or (1 shl (u and IntMask)) -proc excl(s: var TIntSet, key: int) = +proc excl(s: var TIntSet, key: int) = var t = intSetGet(s, key shr TrunkShift) if t != nil: var u = key and TrunkMask @@ -304,11 +304,11 @@ iterator elements(t: TIntSet): int {.inline.} = w = w shr 1 inc(i) r = r.next - -proc isSmallChunk(c: PChunk): bool {.inline.} = + +proc isSmallChunk(c: PChunk): bool {.inline.} = return c.size <= SmallChunkSize-smallChunkOverhead() - -proc chunkUnused(c: PChunk): bool {.inline.} = + +proc chunkUnused(c: PChunk): bool {.inline.} = result = not c.used iterator allObjects(m: TMemRegion): pointer {.inline.} = @@ -319,7 +319,7 @@ iterator allObjects(m: TMemRegion): pointer {.inline.} = if not chunkUnused(c): if isSmallChunk(c): var c = cast[PSmallChunk](c) - + let size = c.size var a = cast[ByteAddress](addr(c.data)) let limit = a + c.acc @@ -334,17 +334,17 @@ proc isCell(p: pointer): bool {.inline.} = result = cast[ptr TFreeCell](p).zeroField >% 1 # ------------- chunk management ---------------------------------------------- -proc pageIndex(c: PChunk): int {.inline.} = +proc pageIndex(c: PChunk): int {.inline.} = result = cast[ByteAddress](c) shr PageShift -proc pageIndex(p: pointer): int {.inline.} = +proc pageIndex(p: pointer): int {.inline.} = result = cast[ByteAddress](p) shr PageShift -proc pageAddr(p: pointer): PChunk {.inline.} = +proc pageAddr(p: pointer): PChunk {.inline.} = result = cast[PChunk](cast[ByteAddress](p) and not PageMask) #sysAssert(Contains(allocator.chunkStarts, pageIndex(result))) -proc requestOsChunks(a: var TMemRegion, size: int): PBigChunk = +proc requestOsChunks(a: var TMemRegion, size: int): PBigChunk = incCurrMem(a, size) inc(a.freeMem, size) result = cast[PBigChunk](osAllocPages(size)) @@ -373,7 +373,7 @@ proc requestOsChunks(a: var TMemRegion, size: int): PBigChunk = result.prevSize = 0 # unknown a.lastSize = size # for next request -proc freeOsChunks(a: var TMemRegion, p: pointer, size: int) = +proc freeOsChunks(a: var TMemRegion, p: pointer, size: int) = # update next.prevSize: var c = cast[PChunk](p) var nxt = cast[ByteAddress](p) +% c.size @@ -387,36 +387,36 @@ proc freeOsChunks(a: var TMemRegion, p: pointer, size: int) = dec(a.freeMem, size) #c_fprintf(c_stdout, "[Alloc] back to OS: %ld\n", size) -proc isAccessible(a: TMemRegion, p: pointer): bool {.inline.} = +proc isAccessible(a: TMemRegion, p: pointer): bool {.inline.} = result = contains(a.chunkStarts, pageIndex(p)) -proc contains[T](list, x: T): bool = +proc contains[T](list, x: T): bool = var it = list while it != nil: if it == x: return true it = it.next - + proc writeFreeList(a: TMemRegion) = var it = a.freeChunksList c_fprintf(c_stdout, "freeChunksList: %p\n", it) - while it != nil: - c_fprintf(c_stdout, "it: %p, next: %p, prev: %p\n", + while it != nil: + c_fprintf(c_stdout, "it: %p, next: %p, prev: %p\n", it, it.next, it.prev) it = it.next -proc listAdd[T](head: var T, c: T) {.inline.} = +proc listAdd[T](head: var T, c: T) {.inline.} = sysAssert(c notin head, "listAdd 1") sysAssert c.prev == nil, "listAdd 2" sysAssert c.next == nil, "listAdd 3" c.next = head - if head != nil: + if head != nil: sysAssert head.prev == nil, "listAdd 4" head.prev = c head = c proc listRemove[T](head: var T, c: T) {.inline.} = sysAssert(c in head, "listRemove") - if c == head: + if c == head: head = c.next sysAssert c.prev == nil, "listRemove 2" if head != nil: head.prev = nil @@ -426,15 +426,15 @@ proc listRemove[T](head: var T, c: T) {.inline.} = if c.next != nil: c.next.prev = c.prev c.next = nil c.prev = nil - -proc updatePrevSize(a: var TMemRegion, c: PBigChunk, - prevSize: int) {.inline.} = + +proc updatePrevSize(a: var TMemRegion, c: PBigChunk, + prevSize: int) {.inline.} = var ri = cast[PChunk](cast[ByteAddress](c) +% c.size) sysAssert((cast[ByteAddress](ri) and PageMask) == 0, "updatePrevSize") if isAccessible(a, ri): ri.prevSize = prevSize - -proc freeBigChunk(a: var TMemRegion, c: PBigChunk) = + +proc freeBigChunk(a: var TMemRegion, c: PBigChunk) = var c = c sysAssert(c.size >= PageSize, "freeBigChunk") inc(a.freeMem, c.size) @@ -448,7 +448,7 @@ proc freeBigChunk(a: var TMemRegion, c: PBigChunk) = inc(c.size, ri.size) excl(a.chunkStarts, pageIndex(ri)) when coalescLeft: - if c.prevSize != 0: + if c.prevSize != 0: var le = cast[PChunk](cast[ByteAddress](c) -% c.prevSize) sysAssert((cast[ByteAddress](le) and PageMask) == 0, "freeBigChunk 4") if isAccessible(a, le) and chunkUnused(le): @@ -467,7 +467,7 @@ proc freeBigChunk(a: var TMemRegion, c: PBigChunk) = else: freeOsChunks(a, c, c.size) -proc splitChunk(a: var TMemRegion, c: PBigChunk, size: int) = +proc splitChunk(a: var TMemRegion, c: PBigChunk, size: int) = var rest = cast[PBigChunk](cast[ByteAddress](c) +% size) sysAssert(rest notin a.freeChunksList, "splitChunk") rest.size = c.size - size @@ -480,7 +480,7 @@ proc splitChunk(a: var TMemRegion, c: PBigChunk, size: int) = incl(a, a.chunkStarts, pageIndex(rest)) listAdd(a.freeChunksList, rest) -proc getBigChunk(a: var TMemRegion, size: int): PBigChunk = +proc getBigChunk(a: var TMemRegion, size: int): PBigChunk = # use first fit for now: sysAssert((size and PageMask) == 0, "getBigChunk 1") sysAssert(size > 0, "getBigChunk 2") @@ -488,7 +488,7 @@ proc getBigChunk(a: var TMemRegion, size: int): PBigChunk = block search: while result != nil: sysAssert chunkUnused(result), "getBigChunk 3" - if result.size == size: + if result.size == size: listRemove(a.freeChunksList, result) break search elif result.size > size: @@ -497,7 +497,7 @@ proc getBigChunk(a: var TMemRegion, size: int): PBigChunk = break search result = result.next sysAssert result != a.freeChunksList, "getBigChunk 4" - if size < InitialMemoryRequest: + if size < InitialMemoryRequest: result = requestOsChunks(a, InitialMemoryRequest) splitChunk(a, result, size) else: @@ -507,7 +507,7 @@ proc getBigChunk(a: var TMemRegion, size: int): PBigChunk = incl(a, a.chunkStarts, pageIndex(result)) dec(a.freeMem, size) -proc getSmallChunk(a: var TMemRegion): PSmallChunk = +proc getSmallChunk(a: var TMemRegion): PSmallChunk = var res = getBigChunk(a, PageSize) sysAssert res.prev == nil, "getSmallChunk 1" sysAssert res.next == nil, "getSmallChunk 2" @@ -521,15 +521,15 @@ proc allocInv(a: TMemRegion): bool = for s in low(a.freeSmallChunks)..high(a.freeSmallChunks): var c = a.freeSmallChunks[s] while c != nil: - if c.next == c: + if c.next == c: echo "[SYSASSERT] c.next == c" return false - if c.size != s * MemAlign: + if c.size != s * MemAlign: echo "[SYSASSERT] c.size != s * MemAlign" return false var it = c.freeList while it != nil: - if it.zeroField != 0: + if it.zeroField != 0: echo "[SYSASSERT] it.zeroField != 0" c_printf("%ld %p\n", it.zeroField, it) return false @@ -539,16 +539,16 @@ proc allocInv(a: TMemRegion): bool = proc rawAlloc(a: var TMemRegion, requestedSize: int): pointer = sysAssert(allocInv(a), "rawAlloc: begin") - sysAssert(roundup(65, 8) == 72, "rawAlloc 1") - sysAssert requestedSize >= sizeof(TFreeCell), "rawAlloc 2" + sysAssert(roundup(65, 8) == 72, "rawAlloc: roundup broken") + sysAssert(requestedSize >= sizeof(TFreeCell), "rawAlloc: requested size too small") var size = roundup(requestedSize, MemAlign) sysAssert(size >= requestedSize, "insufficient allocated size!") #c_fprintf(c_stdout, "alloc; size: %ld; %ld\n", requestedSize, size) - if size <= SmallChunkSize-smallChunkOverhead(): + if size <= SmallChunkSize-smallChunkOverhead(): # allocate a small block: for small chunks, we use only its next pointer var s = size div MemAlign var c = a.freeSmallChunks[s] - if c == nil: + if c == nil: c = getSmallChunk(a) c.freeList = nil sysAssert c.size == PageSize, "rawAlloc 3" @@ -567,7 +567,7 @@ proc rawAlloc(a: var TMemRegion, requestedSize: int): pointer = # c_fprintf(c_stdout, "csize: %lld; size %lld\n", c.size, size) sysAssert c.size == size, "rawAlloc 6" if c.freeList == nil: - sysAssert(c.acc + smallChunkOverhead() + size <= SmallChunkSize, + sysAssert(c.acc + smallChunkOverhead() + size <= SmallChunkSize, "rawAlloc 7") result = cast[pointer](cast[ByteAddress](addr(c.data)) +% c.acc) inc(c.acc, size) @@ -621,9 +621,9 @@ proc rawDealloc(a: var TMemRegion, p: pointer) = f.zeroField = 0 f.next = c.freeList c.freeList = f - when overwriteFree: + when overwriteFree: # set to 0xff to check for usage after free bugs: - c_memset(cast[pointer](cast[int](p) +% sizeof(TFreeCell)), -1'i32, + c_memset(cast[pointer](cast[int](p) +% sizeof(TFreeCell)), -1'i32, s -% sizeof(TFreeCell)) # check if it is not in the freeSmallChunks[s] list: if c.free < s: @@ -649,13 +649,13 @@ proc rawDealloc(a: var TMemRegion, p: pointer) = sysAssert(allocInv(a), "rawDealloc: end") when logAlloc: cprintf("rawDealloc: %p\n", p) -proc isAllocatedPtr(a: TMemRegion, p: pointer): bool = +proc isAllocatedPtr(a: TMemRegion, p: pointer): bool = if isAccessible(a, p): var c = pageAddr(p) if not chunkUnused(c): if isSmallChunk(c): var c = cast[PSmallChunk](c) - var offset = (cast[ByteAddress](p) and (PageSize-1)) -% + var offset = (cast[ByteAddress](p) and (PageSize-1)) -% smallChunkOverhead() result = (c.acc >% offset) and (offset %% c.size == 0) and (cast[ptr TFreeCell](p).zeroField >% 1) @@ -673,12 +673,12 @@ proc interiorAllocatedPtr(a: TMemRegion, p: pointer): pointer = if not chunkUnused(c): if isSmallChunk(c): var c = cast[PSmallChunk](c) - var offset = (cast[ByteAddress](p) and (PageSize-1)) -% + var offset = (cast[ByteAddress](p) and (PageSize-1)) -% smallChunkOverhead() if c.acc >% offset: sysAssert(cast[ByteAddress](addr(c.data)) +% offset == cast[ByteAddress](p), "offset is not what you think it is") - var d = cast[ptr TFreeCell](cast[ByteAddress](addr(c.data)) +% + var d = cast[ptr TFreeCell](cast[ByteAddress](addr(c.data)) +% offset -% (offset %% c.size)) if d.zeroField >% 1: result = d @@ -711,13 +711,13 @@ proc ptrSize(p: pointer): int = if not isSmallChunk(c): dec result, bigChunkOverhead() -proc alloc(allocator: var TMemRegion, size: int): pointer = +proc alloc(allocator: var TMemRegion, size: Natural): pointer = result = rawAlloc(allocator, size+sizeof(TFreeCell)) cast[ptr TFreeCell](result).zeroField = 1 # mark it as used sysAssert(not isAllocatedPtr(allocator, result), "alloc") result = cast[pointer](cast[ByteAddress](result) +% sizeof(TFreeCell)) -proc alloc0(allocator: var TMemRegion, size: int): pointer = +proc alloc0(allocator: var TMemRegion, size: Natural): pointer = result = alloc(allocator, size) zeroMem(result, size) @@ -730,7 +730,7 @@ proc dealloc(allocator: var TMemRegion, p: pointer) = rawDealloc(allocator, x) sysAssert(not isAllocatedPtr(allocator, x), "dealloc 3") -proc realloc(allocator: var TMemRegion, p: pointer, newsize: int): pointer = +proc realloc(allocator: var TMemRegion, p: pointer, newsize: Natural): pointer = if newsize > 0: result = alloc0(allocator, newsize) if p != nil: @@ -758,7 +758,7 @@ proc deallocOsPages(a: var TMemRegion) = proc getFreeMem(a: TMemRegion): int {.inline.} = result = a.freeMem proc getTotalMem(a: TMemRegion): int {.inline.} = result = a.currMem -proc getOccupiedMem(a: TMemRegion): int {.inline.} = +proc getOccupiedMem(a: TMemRegion): int {.inline.} = result = a.currMem - a.freeMem # ---------------------- thread memory region ------------------------------- @@ -774,16 +774,16 @@ template instantiateForRegion(allocator: expr) = proc deallocOsPages = deallocOsPages(allocator) - proc alloc(size: int): pointer = + proc alloc(size: Natural): pointer = result = alloc(allocator, size) - proc alloc0(size: int): pointer = + proc alloc0(size: Natural): pointer = result = alloc0(allocator, size) proc dealloc(p: pointer) = dealloc(allocator, p) - proc realloc(p: pointer, newsize: int): pointer = + proc realloc(p: pointer, newsize: Natural): pointer = result = realloc(allocator, p, newSize) when false: @@ -794,7 +794,7 @@ template instantiateForRegion(allocator: expr) = inc(result, it.size) it = it.next - proc getFreeMem(): int = + proc getFreeMem(): int = result = allocator.freeMem #sysAssert(result == countFreeMem()) @@ -807,7 +807,7 @@ template instantiateForRegion(allocator: expr) = var heapLock: TSysLock initSysLock(heapLock) - proc allocShared(size: int): pointer = + proc allocShared(size: Natural): pointer = when hasThreadSupport: acquireSys(heapLock) result = alloc(sharedHeap, size) @@ -815,20 +815,20 @@ template instantiateForRegion(allocator: expr) = else: result = alloc(size) - proc allocShared0(size: int): pointer = + proc allocShared0(size: Natural): pointer = result = allocShared(size) zeroMem(result, size) proc deallocShared(p: pointer) = - when hasThreadSupport: + when hasThreadSupport: acquireSys(heapLock) dealloc(sharedHeap, p) releaseSys(heapLock) else: dealloc(p) - proc reallocShared(p: pointer, newsize: int): pointer = - when hasThreadSupport: + proc reallocShared(p: pointer, newsize: Natural): pointer = + when hasThreadSupport: acquireSys(heapLock) result = realloc(sharedHeap, p, newsize) releaseSys(heapLock) diff --git a/lib/system/arithm.nim b/lib/system/arithm.nim index f68e2dcd9..ef153417c 100644 --- a/lib/system/arithm.nim +++ b/lib/system/arithm.nim @@ -10,11 +10,11 @@ # simple integer arithmetic with overflow checking -proc raiseOverflow {.compilerproc, noinline, noreturn.} = +proc raiseOverflow {.compilerproc, noinline.} = # a single proc to reduce code size to a minimum sysFatal(OverflowError, "over- or underflow") -proc raiseDivByZero {.compilerproc, noinline, noreturn.} = +proc raiseDivByZero {.compilerproc, noinline.} = sysFatal(DivByZeroError, "division by zero") proc addInt64(a, b: int64): int64 {.compilerProc, inline.} = @@ -327,13 +327,13 @@ when not declared(mulInt): # We avoid setting the FPU control word here for compatibility with libraries # written in other languages. -proc raiseFloatInvalidOp {.noinline, noreturn.} = +proc raiseFloatInvalidOp {.noinline.} = sysFatal(FloatInvalidOpError, "FPU operation caused a NaN result") proc nanCheck(x: float64) {.compilerProc, inline.} = if x != x: raiseFloatInvalidOp() -proc raiseFloatOverflow(x: float64) {.noinline, noreturn.} = +proc raiseFloatOverflow(x: float64) {.noinline.} = if x > 0.0: sysFatal(FloatOverflowError, "FPU operation caused an overflow") else: diff --git a/lib/system/atomics.nim b/lib/system/atomics.nim index 300fa85f3..c97d2fc7f 100644 --- a/lib/system/atomics.nim +++ b/lib/system/atomics.nim @@ -87,7 +87,7 @@ when someGcc and hasThreadSupport: proc atomicCompareExchange*[T: TAtomType](p, expected, desired: ptr T, weak: bool, success_memmodel: AtomMemModel, failure_memmodel: AtomMemModel): bool {. - importc: "__atomic_compare_exchange_n ", nodecl.} + importc: "__atomic_compare_exchange", nodecl.} ## This proc implements the generic version of atomic_compare_exchange. ## The proc is virtually identical to atomic_compare_exchange_n, except the desired ## value is also a pointer. diff --git a/lib/system/chcks.nim b/lib/system/chcks.nim index 2f6d25a12..6caf99d27 100644 --- a/lib/system/chcks.nim +++ b/lib/system/chcks.nim @@ -9,16 +9,16 @@ # Implementation of some runtime checks. -proc raiseRangeError(val: BiggestInt) {.compilerproc, noreturn, noinline.} = +proc raiseRangeError(val: BiggestInt) {.compilerproc, noinline.} = when hostOS == "standalone": sysFatal(RangeError, "value out of range") else: sysFatal(RangeError, "value out of range: ", $val) -proc raiseIndexError() {.compilerproc, noreturn, noinline.} = +proc raiseIndexError() {.compilerproc, noinline.} = sysFatal(IndexError, "index out of bounds") -proc raiseFieldError(f: string) {.compilerproc, noreturn, noinline.} = +proc raiseFieldError(f: string) {.compilerproc, noinline.} = sysFatal(FieldError, f, " is not accessible") proc chckIndx(i, a, b: int): int = diff --git a/lib/system/excpt.nim b/lib/system/excpt.nim index 1b3471978..189d52f57 100644 --- a/lib/system/excpt.nim +++ b/lib/system/excpt.nim @@ -58,7 +58,7 @@ proc pushSafePoint(s: PSafePoint) {.compilerRtl, inl.} = proc popSafePoint {.compilerRtl, inl.} = excHandler = excHandler.prev -proc pushCurrentException(e: ref Exception) {.compilerRtl, inl.} = +proc pushCurrentException(e: ref Exception) {.compilerRtl, inl.} = e.parent = currException currException = e @@ -69,12 +69,12 @@ proc popCurrentException {.compilerRtl, inl.} = const nativeStackTraceSupported* = (defined(macosx) or defined(linux)) and not NimStackTrace - hasSomeStackTrace = NimStackTrace or + hasSomeStackTrace = NimStackTrace or defined(nativeStackTrace) and nativeStackTraceSupported when defined(nativeStacktrace) and nativeStackTraceSupported: type - TDl_info {.importc: "Dl_info", header: "<dlfcn.h>", + TDl_info {.importc: "Dl_info", header: "<dlfcn.h>", final, pure.} = object dli_fname: cstring dli_fbase: pointer @@ -98,7 +98,7 @@ when defined(nativeStacktrace) and nativeStackTraceSupported: tempDlInfo: TDl_info # This is allowed to be expensive since it only happens during crashes # (but this way you don't need manual stack tracing) - var size = backtrace(cast[ptr pointer](addr(tempAddresses)), + var size = backtrace(cast[ptr pointer](addr(tempAddresses)), len(tempAddresses)) var enabled = false for i in 0..size-1: @@ -123,7 +123,7 @@ when defined(nativeStacktrace) and nativeStackTraceSupported: when not hasThreadSupport: var tempFrames: array [0..127, PFrame] # should not be alloc'd on stack - + proc auxWriteStackTrace(f: PFrame, s: var string) = when hasThreadSupport: var @@ -160,7 +160,7 @@ proc auxWriteStackTrace(f: PFrame, s: var string) = inc(i) b = b.prev for j in countdown(i-1, 0): - if tempFrames[j] == nil: + if tempFrames[j] == nil: add(s, "(") add(s, $skipped) add(s, " calls omitted) ...") @@ -214,41 +214,49 @@ proc raiseExceptionAux(e: ref Exception) = if not localRaiseHook(e): return if globalRaiseHook != nil: if not globalRaiseHook(e): return - if excHandler != nil: - if not excHandler.hasRaiseAction or excHandler.raiseAction(e): + when defined(cpp): + if e[] of OutOfMemError: + showErrorMessage(e.name) + quitOrDebug() + else: pushCurrentException(e) - c_longjmp(excHandler.context, 1) - elif e[] of OutOfMemError: - showErrorMessage(e.name) - quitOrDebug() + {.emit: "throw NimException(`e`, `e`->name);".} else: - when hasSomeStackTrace: - var buf = newStringOfCap(2000) - if isNil(e.trace): rawWriteStackTrace(buf) - else: add(buf, e.trace) - add(buf, "Error: unhandled exception: ") - if not isNil(e.msg): add(buf, e.msg) - add(buf, " [") - add(buf, $e.name) - add(buf, "]\n") - showErrorMessage(buf) + if excHandler != nil: + if not excHandler.hasRaiseAction or excHandler.raiseAction(e): + pushCurrentException(e) + c_longjmp(excHandler.context, 1) + elif e[] of OutOfMemError: + showErrorMessage(e.name) + quitOrDebug() else: - # ugly, but avoids heap allocations :-) - template xadd(buf, s, slen: expr) = - if L + slen < high(buf): - copyMem(addr(buf[L]), cstring(s), slen) - inc L, slen - template add(buf, s: expr) = - xadd(buf, s, s.len) - var buf: array [0..2000, char] - var L = 0 - add(buf, "Error: unhandled exception: ") - if not isNil(e.msg): add(buf, e.msg) - add(buf, " [") - xadd(buf, e.name, c_strlen(e.name)) - add(buf, "]\n") - showErrorMessage(buf) - quitOrDebug() + when hasSomeStackTrace: + var buf = newStringOfCap(2000) + if isNil(e.trace): rawWriteStackTrace(buf) + else: add(buf, e.trace) + add(buf, "Error: unhandled exception: ") + if not isNil(e.msg): add(buf, e.msg) + add(buf, " [") + add(buf, $e.name) + add(buf, "]\n") + showErrorMessage(buf) + else: + # ugly, but avoids heap allocations :-) + template xadd(buf, s, slen: expr) = + if L + slen < high(buf): + copyMem(addr(buf[L]), cstring(s), slen) + inc L, slen + template add(buf, s: expr) = + xadd(buf, s, s.len) + var buf: array [0..2000, char] + var L = 0 + add(buf, "Error: unhandled exception: ") + if not isNil(e.msg): add(buf, e.msg) + add(buf, " [") + xadd(buf, e.name, c_strlen(e.name)) + add(buf, "]\n") + showErrorMessage(buf) + quitOrDebug() proc raiseException(e: ref Exception, ename: cstring) {.compilerRtl.} = e.name = ename @@ -309,7 +317,7 @@ when not defined(noSignalHandler): proc signalHandler(sig: cint) {.exportc: "signalHandler", noconv.} = template processSignal(s, action: expr) {.immediate, dirty.} = if s == SIGINT: action("SIGINT: Interrupted by Ctrl-C.\n") - elif s == SIGSEGV: + elif s == SIGSEGV: action("SIGSEGV: Illegal storage access. (Attempt to read from nil?)\n") elif s == SIGABRT: when defined(endb): diff --git a/lib/system/mmdisp.nim b/lib/system/mmdisp.nim index 84e532049..a378f86e7 100644 --- a/lib/system/mmdisp.nim +++ b/lib/system/mmdisp.nim @@ -109,24 +109,24 @@ when defined(boehmgc): when not defined(useNimRtl): - proc alloc(size: int): pointer = + proc alloc(size: Natural): pointer = result = boehmAlloc(size) if result == nil: raiseOutOfMem() - proc alloc0(size: int): pointer = + proc alloc0(size: Natural): pointer = result = alloc(size) zeroMem(result, size) - proc realloc(p: pointer, newsize: int): pointer = + proc realloc(p: pointer, newsize: Natural): pointer = result = boehmRealloc(p, newsize) if result == nil: raiseOutOfMem() proc dealloc(p: pointer) = boehmDealloc(p) - proc allocShared(size: int): pointer = + proc allocShared(size: Natural): pointer = result = boehmAlloc(size) if result == nil: raiseOutOfMem() - proc allocShared0(size: int): pointer = + proc allocShared0(size: Natural): pointer = result = alloc(size) zeroMem(result, size) - proc reallocShared(p: pointer, newsize: int): pointer = + proc reallocShared(p: pointer, newsize: Natural): pointer = result = boehmRealloc(p, newsize) if result == nil: raiseOutOfMem() proc deallocShared(p: pointer) = boehmDealloc(p) @@ -196,24 +196,24 @@ when defined(boehmgc): elif defined(nogc) and defined(useMalloc): when not defined(useNimRtl): - proc alloc(size: int): pointer = + proc alloc(size: Natural): pointer = result = cmalloc(size) if result == nil: raiseOutOfMem() - proc alloc0(size: int): pointer = + proc alloc0(size: Natural): pointer = result = alloc(size) zeroMem(result, size) - proc realloc(p: pointer, newsize: int): pointer = + proc realloc(p: pointer, newsize: Natural): pointer = result = crealloc(p, newsize) if result == nil: raiseOutOfMem() proc dealloc(p: pointer) = cfree(p) - proc allocShared(size: int): pointer = + proc allocShared(size: Natural): pointer = result = cmalloc(size) if result == nil: raiseOutOfMem() - proc allocShared0(size: int): pointer = + proc allocShared0(size: Natural): pointer = result = alloc(size) zeroMem(result, size) - proc reallocShared(p: pointer, newsize: int): pointer = + proc reallocShared(p: pointer, newsize: Natural): pointer = result = crealloc(p, newsize) if result == nil: raiseOutOfMem() proc deallocShared(p: pointer) = cfree(p) diff --git a/lib/system/sysio.nim b/lib/system/sysio.nim index 468af1713..3f860655e 100644 --- a/lib/system/sysio.nim +++ b/lib/system/sysio.nim @@ -16,7 +16,7 @@ # of the standard library! -proc fputs(c: cstring, f: File) {.importc: "fputs", header: "<stdio.h>", +proc fputs(c: cstring, f: File) {.importc: "fputs", header: "<stdio.h>", tags: [WriteIOEffect].} proc fgets(c: cstring, n: int, f: File): cstring {. importc: "fgets", header: "<stdio.h>", tags: [ReadIOEffect].} @@ -26,11 +26,30 @@ proc ungetc(c: cint, f: File) {.importc: "ungetc", header: "<stdio.h>", tags: [].} proc putc(c: char, stream: File) {.importc: "putc", header: "<stdio.h>", tags: [WriteIOEffect].} -proc fprintf(f: File, frmt: cstring) {.importc: "fprintf", +proc fprintf(f: File, frmt: cstring) {.importc: "fprintf", header: "<stdio.h>", varargs, tags: [WriteIOEffect].} proc strlen(c: cstring): int {. importc: "strlen", header: "<string.h>", tags: [].} +when defined(posix): + proc getc_unlocked(stream: File): cint {.importc: "getc_unlocked", + header: "<stdio.h>", tags: [ReadIOEffect].} + + proc flockfile(stream: File) {.importc: "flockfile", header: "<stdio.h>", + tags: [ReadIOEffect].} + + proc funlockfile(stream: File) {.importc: "funlockfile", header: "<stdio.h>", + tags: [ReadIOEffect].} +elif false: + # doesn't work on Windows yet: + proc getc_unlocked(stream: File): cint {.importc: "_fgetc_nolock", + header: "<stdio.h>", tags: [ReadIOEffect].} + + proc flockfile(stream: File) {.importc: "_lock_file", header: "<stdio.h>", + tags: [ReadIOEffect].} + + proc funlockfile(stream: File) {.importc: "_unlock_file", header: "<stdio.h>", + tags: [ReadIOEffect].} # C routine that is used here: proc fread(buf: pointer, size, n: int, f: File): int {. @@ -67,39 +86,57 @@ const proc raiseEIO(msg: string) {.noinline, noreturn.} = sysFatal(IOError, msg) -proc readLine(f: File, line: var TaintedString): bool = - # of course this could be optimized a bit; but IO is slow anyway... - # and it was difficult to get this CORRECT with Ansi C's methods - setLen(line.string, 0) # reuse the buffer! - while true: - var c = fgetc(f) - if c < 0'i32: - if line.len > 0: break - else: return false - if c == 10'i32: break # LF - if c == 13'i32: # CR - c = fgetc(f) # is the next char LF? - if c != 10'i32: ungetc(c, f) # no, put the character back - break - add line.string, chr(int(c)) - result = true +when declared(getc_unlocked): + proc readLine(f: File, line: var TaintedString): bool = + setLen(line.string, 0) # reuse the buffer! + flockfile(f) + while true: + var c = getc_unlocked(f) + if c < 0'i32: + if line.len > 0: break + else: return false + if c == 10'i32: break # LF + if c == 13'i32: # CR + c = getc_unlocked(f) # is the next char LF? + if c != 10'i32: ungetc(c, f) # no, put the character back + break + add line.string, chr(int(c)) + result = true + funlockfile(f) +else: + proc readLine(f: File, line: var TaintedString): bool = + # of course this could be optimized a bit; but IO is slow anyway... + # and it was difficult to get this CORRECT with Ansi C's methods + setLen(line.string, 0) # reuse the buffer! + while true: + var c = fgetc(f) + if c < 0'i32: + if line.len > 0: break + else: return false + if c == 10'i32: break # LF + if c == 13'i32: # CR + c = fgetc(f) # is the next char LF? + if c != 10'i32: ungetc(c, f) # no, put the character back + break + add line.string, chr(int(c)) + result = true proc readLine(f: File): TaintedString = result = TaintedString(newStringOfCap(80)) if not readLine(f, result): raiseEIO("EOF reached") -proc write(f: File, i: int) = +proc write(f: File, i: int) = when sizeof(int) == 8: fprintf(f, "%lld", i) else: fprintf(f, "%ld", i) -proc write(f: File, i: BiggestInt) = +proc write(f: File, i: BiggestInt) = when sizeof(BiggestInt) == 8: fprintf(f, "%lld", i) else: fprintf(f, "%ld", i) - + proc write(f: File, b: bool) = if b: write(f, "true") else: write(f, "false") @@ -110,7 +147,7 @@ proc write(f: File, c: char) = putc(c, f) proc write(f: File, a: varargs[string, `$`]) = for x in items(a): write(f, x) -proc readAllBuffer(file: File): string = +proc readAllBuffer(file: File): string = # This proc is for File we want to read but don't know how many # bytes we need to read before the buffer is empty. result = "" @@ -123,8 +160,8 @@ proc readAllBuffer(file: File): string = buffer.setLen(bytesRead) result.add(buffer) break - -proc rawFileSize(file: File): int = + +proc rawFileSize(file: File): int = # this does not raise an error opposed to `getFileSize` var oldPos = ftell(file) discard fseek(file, 0, 2) # seek the end of the file @@ -141,8 +178,8 @@ proc readAllFile(file: File, len: int): string = proc readAllFile(file: File): string = var len = rawFileSize(file) result = readAllFile(file, len) - -proc readAll(file: File): TaintedString = + +proc readAll(file: File): TaintedString = # Separate handling needed because we need to buffer when we # don't know the overall length of the File. let len = if file != stdin: rawFileSize(file) else: -1 @@ -150,7 +187,7 @@ proc readAll(file: File): TaintedString = result = readAllFile(file, len).TaintedString else: result = readAllBuffer(file).TaintedString - + proc readFile(filename: string): TaintedString = var f = open(filename) try: @@ -229,7 +266,7 @@ proc open(f: var File, filename: string, elif bufSize == 0: discard setvbuf(f, nil, IONBF, 0) -proc reopen(f: File, filename: string, mode: FileMode = fmRead): bool = +proc reopen(f: File, filename: string, mode: FileMode = fmRead): bool = var p: pointer = freopen(filename, FormatOpen[mode], f) result = p != nil @@ -243,23 +280,23 @@ proc open(f: var File, filehandle: FileHandle, mode: FileMode): bool = proc fwrite(buf: pointer, size, n: int, f: File): int {. importc: "fwrite", noDecl.} -proc readBuffer(f: File, buffer: pointer, len: int): int = +proc readBuffer(f: File, buffer: pointer, len: Natural): int = result = fread(buffer, 1, len, f) -proc readBytes(f: File, a: var openArray[int8|uint8], start, len: int): int = +proc readBytes(f: File, a: var openArray[int8|uint8], start, len: Natural): int = result = readBuffer(f, addr(a[start]), len) -proc readChars(f: File, a: var openArray[char], start, len: int): int = +proc readChars(f: File, a: var openArray[char], start, len: Natural): int = result = readBuffer(f, addr(a[start]), len) {.push stackTrace:off, profiler:off.} -proc writeBytes(f: File, a: openArray[int8|uint8], start, len: int): int = +proc writeBytes(f: File, a: openArray[int8|uint8], start, len: Natural): int = var x = cast[ptr array[0..1000_000_000, int8]](a) result = writeBuffer(f, addr(x[start]), len) -proc writeChars(f: File, a: openArray[char], start, len: int): int = +proc writeChars(f: File, a: openArray[char], start, len: Natural): int = var x = cast[ptr array[0..1000_000_000, int8]](a) result = writeBuffer(f, addr(x[start]), len) -proc writeBuffer(f: File, buffer: pointer, len: int): int = +proc writeBuffer(f: File, buffer: pointer, len: Natural): int = result = fwrite(buffer, 1, len, f) proc write(f: File, s: string) = diff --git a/lib/windows/windows.nim b/lib/windows/windows.nim index 43c595a64..02c87132a 100644 --- a/lib/windows/windows.nim +++ b/lib/windows/windows.nim @@ -11,7 +11,7 @@ ## Unicode version. {.deadCodeElim: on.} - +{.push gcsafe.} type WideChar* = uint16 PWideChar* = ptr uint16 @@ -18513,11 +18513,15 @@ proc DisableThreadLibraryCalls*(hLibModule: HMODULE): WINBOOL{.stdcall, proc GetProcAddress*(hModule: HINST, lpProcName: LPCSTR): FARPROC{.stdcall, dynlib: "kernel32", importc: "GetProcAddress".} proc GetVersion*(): DWORD{.stdcall, dynlib: "kernel32", importc: "GetVersion".} -proc GlobalAlloc*(uFlags: int32, dwBytes: DWORD): HGLOBAL{.stdcall, +proc GlobalAlloc*(uFlags: int32, dwBytes: SIZE_T): HGLOBAL{.stdcall, dynlib: "kernel32", importc: "GlobalAlloc".} -proc GlobalReAlloc*(hMem: HGLOBAL, dwBytes: DWORD, uFlags: int32): HGLOBAL{. +proc GlobalAlloc*(uFlags: int32, dwBytes: DWORD): HGLOBAL{.stdcall, + dynlib: "kernel32", importc: "GlobalAlloc", deprecated.} +proc GlobalReAlloc*(hMem: HGLOBAL, dwBytes: SIZE_T, uFlags: int32): HGLOBAL{. stdcall, dynlib: "kernel32", importc: "GlobalReAlloc".} -proc GlobalSize*(hMem: HGLOBAL): DWORD{.stdcall, dynlib: "kernel32", +proc GlobalReAlloc*(hMem: HGLOBAL, dwBytes: DWORD, uFlags: int32): HGLOBAL{. + stdcall, dynlib: "kernel32", importc: "GlobalReAlloc", deprecated.} +proc GlobalSize*(hMem: HGLOBAL): SIZE_T{.stdcall, dynlib: "kernel32", importc: "GlobalSize".} proc GlobalFlags*(hMem: HGLOBAL): WINUINT{.stdcall, dynlib: "kernel32", importc: "GlobalFlags".} @@ -18541,10 +18545,14 @@ proc GlobalUnWire*(hMem: HGLOBAL): WINBOOL{.stdcall, dynlib: "kernel32", importc: "GlobalUnWire".} proc GlobalMemoryStatus*(lpBuffer: LPMEMORYSTATUS){.stdcall, dynlib: "kernel32", importc: "GlobalMemoryStatus".} -proc LocalAlloc*(uFlags: WINUINT, uBytes: WINUINT): HLOCAL{.stdcall, +proc LocalAlloc*(uFlags: WINUINT, uBytes: SIZE_T): HLOCAL{.stdcall, dynlib: "kernel32", importc: "LocalAlloc".} -proc LocalReAlloc*(hMem: HLOCAL, uBytes: WINUINT, uFlags: WINUINT): HLOCAL{.stdcall, +proc LocalAlloc*(uFlags: WINUINT, uBytes: DWORD): HLOCAL{.stdcall, + dynlib: "kernel32", importc: "LocalAlloc", deprecated.} +proc LocalReAlloc*(hMem: HLOCAL, uBytes: SIZE_T, uFlags: WINUINT): HLOCAL{.stdcall, dynlib: "kernel32", importc: "LocalReAlloc".} +proc LocalReAlloc*(hMem: HLOCAL, uBytes: DWORD, uFlags: WINUINT): HLOCAL{.stdcall, + dynlib: "kernel32", importc: "LocalReAlloc", deprecated.} proc LocalLock*(hMem: HLOCAL): LPVOID{.stdcall, dynlib: "kernel32", importc: "LocalLock".} proc LocalHandle*(pMem: LPCVOID): HLOCAL{.stdcall, dynlib: "kernel32", @@ -18564,38 +18572,71 @@ proc LocalCompact*(uMinFree: WINUINT): WINUINT{.stdcall, dynlib: "kernel32", proc FlushInstructionCache*(hProcess: HANDLE, lpBaseAddress: LPCVOID, dwSize: DWORD): WINBOOL{.stdcall, dynlib: "kernel32", importc: "FlushInstructionCache".} -proc VirtualAlloc*(lpAddress: LPVOID, dwSize: DWORD, flAllocationType: DWORD, +proc VirtualAlloc*(lpAddress: LPVOID, dwSize: SIZE_T, flAllocationType: DWORD, flProtect: DWORD): LPVOID{.stdcall, dynlib: "kernel32", importc: "VirtualAlloc".} -proc VirtualFree*(lpAddress: LPVOID, dwSize: DWORD, dwFreeType: DWORD): WINBOOL{. +proc VirtualAlloc*(lpAddress: LPVOID, dwSize: DWORD, flAllocationType: DWORD, + flProtect: DWORD): LPVOID{.stdcall, dynlib: "kernel32", + importc: "VirtualAlloc", deprecated.} +proc VirtualAllocEx*(hProcess: HANDLE, lpAddress: LPVOID, dwSize: SIZE_T, + flAllocationType: DWORD, flProtect: DWORD): LPVOID + {.stdcall, dynlib: "kernel32", importc: "VirtualAllocEx".} +proc VirtualAllocEx*(hProcess: HANDLE, lpAddress: LPVOID, dwSize: DWORD, + flAllocationType: DWORD, flProtect: DWORD): LPVOID + {.stdcall, dynlib: "kernel32", importc: "VirtualAllocEx", + deprecated.} +proc VirtualFree*(lpAddress: LPVOID, dwSize: SIZE_T, dwFreeType: DWORD): WINBOOL{. stdcall, dynlib: "kernel32", importc: "VirtualFree".} -proc VirtualProtect*(lpAddress: LPVOID, dwSize: DWORD, flNewProtect: DWORD, +proc VirtualFree*(lpAddress: LPVOID, dwSize: DWORD, dwFreeType: DWORD): WINBOOL{. + stdcall, dynlib: "kernel32", importc: "VirtualFree", deprecated.} +proc VirtualFreeEx*(hProcess: HANDLE, lpAddress: LPVOID, dwSize: SIZE_T, + dwFreeType: DWORD): WINBOOL + {.stdcall, dynlib: "kernel32", importc: "VirtualFree".} +proc VirtualFreeEx*(hProcess: HANDLE, lpAddress: LPVOID, dwSize: DWORD, + dwFreeType: DWORD): WINBOOL + {.stdcall, dynlib: "kernel32", importc: "VirtualFree".} +proc VirtualProtect*(lpAddress: LPVOID, dwSize: SIZE_T, flNewProtect: DWORD, lpflOldProtect: PDWORD): WINBOOL{.stdcall, dynlib: "kernel32", importc: "VirtualProtect".} +proc VirtualProtect*(lpAddress: LPVOID, dwSize: DWORD, flNewProtect: DWORD, + lpflOldProtect: PDWORD): WINBOOL{.stdcall, + dynlib: "kernel32", importc: "VirtualProtect", deprecated.} proc VirtualQuery*(lpAddress: LPCVOID, lpBuffer: PMEMORY_BASIC_INFORMATION, dwLength: DWORD): DWORD{.stdcall, dynlib: "kernel32", importc: "VirtualQuery".} -proc VirtualProtectEx*(hProcess: HANDLE, lpAddress: LPVOID, dwSize: DWORD, +proc VirtualProtectEx*(hProcess: HANDLE, lpAddress: LPVOID, dwSize: SIZE_T, flNewProtect: DWORD, lpflOldProtect: PDWORD): WINBOOL{. stdcall, dynlib: "kernel32", importc: "VirtualProtectEx".} +proc VirtualProtectEx*(hProcess: HANDLE, lpAddress: LPVOID, dwSize: DWORD, + flNewProtect: DWORD, lpflOldProtect: PDWORD): WINBOOL{. + stdcall, dynlib: "kernel32", importc: "VirtualProtectEx", deprecated.} proc VirtualQueryEx*(hProcess: HANDLE, lpAddress: LPCVOID, - lpBuffer: PMEMORY_BASIC_INFORMATION, dwLength: DWORD): DWORD{. + lpBuffer: PMEMORY_BASIC_INFORMATION, dwLength: SIZE_T): DWORD{. stdcall, dynlib: "kernel32", importc: "VirtualQueryEx".} -proc HeapCreate*(flOptions: DWORD, dwInitialSize: DWORD, dwMaximumSize: DWORD): HANDLE{. +proc VirtualQueryEx*(hProcess: HANDLE, lpAddress: LPCVOID, + lpBuffer: PMEMORY_BASIC_INFORMATION, dwLength: DWORD): DWORD{. + stdcall, dynlib: "kernel32", importc: "VirtualQueryEx", deprecated.} +proc HeapCreate*(flOptions: DWORD, dwInitialSize: SIZE_T, dwMaximumSize: SIZE_T): HANDLE{. stdcall, dynlib: "kernel32", importc: "HeapCreate".} +proc HeapCreate*(flOptions: DWORD, dwInitialSize: DWORD, dwMaximumSize: DWORD): HANDLE{. + stdcall, dynlib: "kernel32", importc: "HeapCreate", deprecated.} proc HeapDestroy*(hHeap: HANDLE): WINBOOL{.stdcall, dynlib: "kernel32", importc: "HeapDestroy".} -proc HeapAlloc*(hHeap: HANDLE, dwFlags: DWORD, dwBytes: DWORD): LPVOID{.stdcall, +proc HeapAlloc*(hHeap: HANDLE, dwFlags: DWORD, dwBytes: SIZE_T): LPVOID{.stdcall, dynlib: "kernel32", importc: "HeapAlloc".} -proc HeapReAlloc*(hHeap: HANDLE, dwFlags: DWORD, lpMem: LPVOID, dwBytes: DWORD): LPVOID{. +proc HeapAlloc*(hHeap: HANDLE, dwFlags: DWORD, dwBytes: DWORD): LPVOID{.stdcall, + dynlib: "kernel32", importc: "HeapAlloc", deprecated.} +proc HeapReAlloc*(hHeap: HANDLE, dwFlags: DWORD, lpMem: LPVOID, dwBytes: SIZE_T): LPVOID{. stdcall, dynlib: "kernel32", importc: "HeapReAlloc".} +proc HeapReAlloc*(hHeap: HANDLE, dwFlags: DWORD, lpMem: LPVOID, dwBytes: DWORD): LPVOID{. + stdcall, dynlib: "kernel32", importc: "HeapReAlloc", deprecated.} proc HeapFree*(hHeap: HANDLE, dwFlags: DWORD, lpMem: LPVOID): WINBOOL{.stdcall, dynlib: "kernel32", importc: "HeapFree".} -proc HeapSize*(hHeap: HANDLE, dwFlags: DWORD, lpMem: LPCVOID): DWORD{.stdcall, +proc HeapSize*(hHeap: HANDLE, dwFlags: DWORD, lpMem: LPCVOID): SIZE_T{.stdcall, dynlib: "kernel32", importc: "HeapSize".} proc HeapValidate*(hHeap: HANDLE, dwFlags: DWORD, lpMem: LPCVOID): WINBOOL{. stdcall, dynlib: "kernel32", importc: "HeapValidate".} -proc HeapCompact*(hHeap: HANDLE, dwFlags: DWORD): WINUINT{.stdcall, +proc HeapCompact*(hHeap: HANDLE, dwFlags: DWORD): SIZE_T{.stdcall, dynlib: "kernel32", importc: "HeapCompact".} proc GetProcessHeap*(): HANDLE{.stdcall, dynlib: "kernel32", importc: "GetProcessHeap".} @@ -19221,10 +19262,14 @@ proc FindNextChangeNotification*(hChangeHandle: HANDLE): WINBOOL{.stdcall, dynlib: "kernel32", importc: "FindNextChangeNotification".} proc FindCloseChangeNotification*(hChangeHandle: HANDLE): WINBOOL{.stdcall, dynlib: "kernel32", importc: "FindCloseChangeNotification".} -proc VirtualLock*(lpAddress: LPVOID, dwSize: DWORD): WINBOOL{.stdcall, +proc VirtualLock*(lpAddress: LPVOID, dwSize: SIZE_T): WINBOOL{.stdcall, dynlib: "kernel32", importc: "VirtualLock".} -proc VirtualUnlock*(lpAddress: LPVOID, dwSize: DWORD): WINBOOL{.stdcall, +proc VirtualLock*(lpAddress: LPVOID, dwSize: DWORD): WINBOOL{.stdcall, + dynlib: "kernel32", importc: "VirtualLock", deprecated.} +proc VirtualUnlock*(lpAddress: LPVOID, dwSize: SIZE_T): WINBOOL{.stdcall, dynlib: "kernel32", importc: "VirtualUnlock".} +proc VirtualUnlock*(lpAddress: LPVOID, dwSize: DWORD): WINBOOL{.stdcall, + dynlib: "kernel32", importc: "VirtualUnlock", deprecated.} proc MapViewOfFileEx*(hFileMappingObject: HANDLE, dwDesiredAccess: DWORD, dwFileOffsetHigh: DWORD, dwFileOffsetLow: DWORD, dwNumberOfBytesToMap: DWORD, lpBaseAddress: LPVOID): LPVOID{. @@ -23481,7 +23526,7 @@ proc ListView_EnsureVisible(hwndLV: HWND, i, fPartialOK: int32): LRESULT = MAKELPARAM(fPartialOK, 0)) proc ListView_FindItem(wnd: HWND, iStart: int32, lvfi: var LV_FINDINFO): int32 = - result = SendMessage(wnd, LVM_FINDITEM, WPARAM(iStart), + result = SendMessage(wnd, LVM_FINDITEM, WPARAM(iStart), cast[LPARAM](addr(lvfi))).int32 proc ListView_GetBkColor(wnd: HWND): LRESULT = @@ -23615,9 +23660,9 @@ proc ListView_SetTextBkColor(wnd: HWND, clrTextBk: COLORREF): LRESULT = proc ListView_SetTextColor(wnd: HWND, clrText: COLORREF): LRESULT = result = SendMessage(wnd, LVM_SETTEXTCOLOR, 0, LPARAM(clrText)) -proc ListView_SortItems(hwndLV: HWND, pfnCompare: PFNLVCOMPARE, +proc ListView_SortItems(hwndLV: HWND, pfnCompare: PFNLVCOMPARE, lPrm: LPARAM): LRESULT = - result = SendMessage(hwndLV, LVM_SORTITEMS, WPARAM(lPrm), + result = SendMessage(hwndLV, LVM_SORTITEMS, WPARAM(lPrm), cast[LPARAM](pfnCompare)) proc ListView_Update(hwndLV: HWND, i: int32): LRESULT = @@ -23924,3 +23969,5 @@ proc LOCALE_NEUTRAL(): DWORD = proc LOCALE_INVARIANT(): DWORD = result = MAKELCID(MAKELANGID(toU16(LANG_INVARIANT), SUBLANG_NEUTRAL), SORT_DEFAULT) + +{.pop.} diff --git a/lib/windows/winlean.nim b/lib/windows/winlean.nim index 584f7cf48..57b79c7de 100644 --- a/lib/windows/winlean.nim +++ b/lib/windows/winlean.nim @@ -10,6 +10,8 @@ ## This module implements a small wrapper for some needed Win API procedures, ## so that the Nim compiler does not depend on the huge Windows module. +{.deadCodeElim:on.} + const useWinUnicode* = not defined(useWinAnsi) @@ -29,7 +31,7 @@ type nLength*: int32 lpSecurityDescriptor*: pointer bInheritHandle*: WINBOOL - + TSTARTUPINFO* {.final, pure.} = object cb*: int32 lpReserved*: cstring @@ -59,7 +61,7 @@ type TFILETIME* {.final, pure.} = object ## CANNOT BE int64 BECAUSE OF ALIGNMENT dwLowDateTime*: DWORD dwHighDateTime*: DWORD - + TBY_HANDLE_FILE_INFORMATION* {.final, pure.} = object dwFileAttributes*: DWORD ftCreationTime*: TFILETIME @@ -94,26 +96,26 @@ const STD_ERROR_HANDLE* = -12'i32 DETACHED_PROCESS* = 8'i32 - + SW_SHOWNORMAL* = 1'i32 INVALID_HANDLE_VALUE* = THandle(-1) - + CREATE_UNICODE_ENVIRONMENT* = 1024'i32 proc closeHandle*(hObject: THandle): WINBOOL {.stdcall, dynlib: "kernel32", importc: "CloseHandle".} - + proc readFile*(hFile: THandle, Buffer: pointer, nNumberOfBytesToRead: int32, lpNumberOfBytesRead: ptr int32, lpOverlapped: pointer): WINBOOL{. stdcall, dynlib: "kernel32", importc: "ReadFile".} - + proc writeFile*(hFile: THandle, Buffer: pointer, nNumberOfBytesToWrite: int32, - lpNumberOfBytesWritten: ptr int32, + lpNumberOfBytesWritten: ptr int32, lpOverlapped: pointer): WINBOOL{. stdcall, dynlib: "kernel32", importc: "WriteFile".} proc createPipe*(hReadPipe, hWritePipe: var THandle, - lpPipeAttributes: var TSECURITY_ATTRIBUTES, + lpPipeAttributes: var TSECURITY_ATTRIBUTES, nSize: int32): WINBOOL{. stdcall, dynlib: "kernel32", importc: "CreatePipe".} @@ -159,7 +161,7 @@ proc setStdHandle*(nStdHandle: int32, hHandle: THandle): WINBOOL {.stdcall, proc flushFileBuffers*(hFile: THandle): WINBOOL {.stdcall, dynlib: "kernel32", importc: "FlushFileBuffers".} -proc getLastError*(): int32 {.importc: "GetLastError", +proc getLastError*(): int32 {.importc: "GetLastError", stdcall, dynlib: "kernel32".} when useWinUnicode: @@ -179,7 +181,7 @@ proc localFree*(p: pointer) {. importc: "LocalFree", stdcall, dynlib: "kernel32".} when useWinUnicode: - proc getCurrentDirectoryW*(nBufferLength: int32, + proc getCurrentDirectoryW*(nBufferLength: int32, lpBuffer: WideCString): int32 {. importc: "GetCurrentDirectoryW", dynlib: "kernel32", stdcall.} proc setCurrentDirectoryW*(lpPathName: WideCString): int32 {. @@ -191,8 +193,8 @@ when useWinUnicode: proc setEnvironmentVariableW*(lpName, lpValue: WideCString): int32 {. stdcall, dynlib: "kernel32", importc: "SetEnvironmentVariableW".} - proc getModuleFileNameW*(handle: THandle, buf: WideCString, - size: int32): int32 {.importc: "GetModuleFileNameW", + proc getModuleFileNameW*(handle: THandle, buf: WideCString, + size: int32): int32 {.importc: "GetModuleFileNameW", dynlib: "kernel32", stdcall.} else: proc getCurrentDirectoryA*(nBufferLength: int32, lpBuffer: cstring): int32 {. @@ -269,14 +271,14 @@ proc findClose*(hFindFile: THandle) {.stdcall, dynlib: "kernel32", when useWinUnicode: proc getFullPathNameW*(lpFileName: WideCString, nBufferLength: int32, - lpBuffer: WideCString, + lpBuffer: WideCString, lpFilePart: var WideCString): int32 {. - stdcall, dynlib: "kernel32", + stdcall, dynlib: "kernel32", importc: "GetFullPathNameW".} proc getFileAttributesW*(lpFileName: WideCString): int32 {. - stdcall, dynlib: "kernel32", + stdcall, dynlib: "kernel32", importc: "GetFileAttributesW".} - proc setFileAttributesW*(lpFileName: WideCString, + proc setFileAttributesW*(lpFileName: WideCString, dwFileAttributes: int32): WINBOOL {. stdcall, dynlib: "kernel32", importc: "SetFileAttributesW".} @@ -299,12 +301,12 @@ when useWinUnicode: else: proc getFullPathNameA*(lpFileName: cstring, nBufferLength: int32, lpBuffer: cstring, lpFilePart: var cstring): int32 {. - stdcall, dynlib: "kernel32", + stdcall, dynlib: "kernel32", importc: "GetFullPathNameA".} proc getFileAttributesA*(lpFileName: cstring): int32 {. - stdcall, dynlib: "kernel32", + stdcall, dynlib: "kernel32", importc: "GetFileAttributesA".} - proc setFileAttributesA*(lpFileName: cstring, + proc setFileAttributesA*(lpFileName: cstring, dwFileAttributes: int32): WINBOOL {. stdcall, dynlib: "kernel32", importc: "SetFileAttributesA".} @@ -324,10 +326,10 @@ else: proc getCommandLineA*(): cstring {. importc: "GetCommandLineA", stdcall, dynlib: "kernel32".} -proc rdFileTime*(f: TFILETIME): int64 = +proc rdFileTime*(f: TFILETIME): int64 = result = ze64(f.dwLowDateTime) or (ze64(f.dwHighDateTime) shl 32) -proc rdFileSize*(f: TWIN32_FIND_DATA): int64 = +proc rdFileSize*(f: TWIN32_FIND_DATA): int64 = result = ze64(f.nFileSizeLow) or (ze64(f.nFileSizeHigh) shl 32) proc getSystemTimeAsFileTime*(lpSystemTimeAsFileTime: var TFILETIME) {. @@ -347,7 +349,7 @@ else: lpParameters, lpDirectory: cstring, nShowCmd: int32): THandle{. stdcall, dynlib: "shell32.dll", importc: "ShellExecuteA".} - + proc getFileInformationByHandle*(hFile: THandle, lpFileInformation: ptr TBY_HANDLE_FILE_INFORMATION): WINBOOL{. stdcall, dynlib: "kernel32", importc: "GetFileInformationByHandle".} @@ -357,12 +359,12 @@ const WSASYS_STATUS_LEN* = 128 FD_SETSIZE* = 64 MSG_PEEK* = 2 - + INADDR_ANY* = 0 INADDR_LOOPBACK* = 0x7F000001 INADDR_BROADCAST* = -1 INADDR_NONE* = -1 - + ws2dll = "Ws2_32.dll" WSAEWOULDBLOCK* = 10035 @@ -376,31 +378,31 @@ type {.deprecated: [TSocketHandle: SocketHandle].} type - WSAData* {.importc: "WSADATA", header: "winsock2.h".} = object + WSAData* {.importc: "WSADATA", header: "winsock2.h".} = object wVersion, wHighVersion: int16 szDescription: array[0..WSADESCRIPTION_LEN, char] szSystemStatus: array[0..WSASYS_STATUS_LEN, char] iMaxSockets, iMaxUdpDg: int16 lpVendorInfo: cstring - - SockAddr* {.importc: "SOCKADDR", header: "winsock2.h".} = object + + SockAddr* {.importc: "SOCKADDR", header: "winsock2.h".} = object sa_family*: int16 # unsigned sa_data: array[0..13, char] InAddr* {.importc: "IN_ADDR", header: "winsock2.h".} = object s_addr*: int32 # IP address - - Sockaddr_in* {.importc: "SOCKADDR_IN", + + Sockaddr_in* {.importc: "SOCKADDR_IN", header: "winsock2.h".} = object sin_family*: int16 sin_port*: int16 # unsigned sin_addr*: InAddr sin_zero*: array[0..7, char] - In6_addr* {.importc: "IN6_ADDR", header: "winsock2.h".} = object + In6_addr* {.importc: "IN6_ADDR", header: "winsock2.h".} = object bytes*: array[0..15, char] - Sockaddr_in6* {.importc: "SOCKADDR_IN6", + Sockaddr_in6* {.importc: "SOCKADDR_IN6", header: "winsock2.h".} = object sin6_family*: int16 sin6_port*: int16 # unsigned @@ -430,23 +432,23 @@ type h_addrtype*: int16 h_length*: int16 h_addr_list*: cstringArray - + TFdSet* = object fd_count*: cint # unsigned fd_array*: array[0..FD_SETSIZE-1, SocketHandle] - + Timeval* = object tv_sec*, tv_usec*: int32 - + AddrInfo* = object - ai_flags*: cint ## Input flags. - ai_family*: cint ## Address family of socket. - ai_socktype*: cint ## Socket type. - ai_protocol*: cint ## Protocol of socket. - ai_addrlen*: int ## Length of socket address. + ai_flags*: cint ## Input flags. + ai_family*: cint ## Address family of socket. + ai_socktype*: cint ## Socket type. + ai_protocol*: cint ## Protocol of socket. + ai_addrlen*: int ## Length of socket address. ai_canonname*: cstring ## Canonical name of service location. - ai_addr*: ptr SockAddr ## Socket address of socket. - ai_next*: ptr AddrInfo ## Pointer to next in list. + ai_addr*: ptr SockAddr ## Socket address of socket. + ai_next*: ptr AddrInfo ## Pointer to next in list. SockLen* = cuint @@ -501,7 +503,7 @@ proc bindSocket*(s: SocketHandle, name: ptr SockAddr, namelen: SockLen): cint {. stdcall, importc: "bind", dynlib: ws2dll.} proc connect*(s: SocketHandle, name: ptr SockAddr, namelen: SockLen): cint {. stdcall, importc: "connect", dynlib: ws2dll.} -proc getsockname*(s: SocketHandle, name: ptr SockAddr, +proc getsockname*(s: SocketHandle, name: ptr SockAddr, namelen: ptr SockLen): cint {. stdcall, importc: "getsockname", dynlib: ws2dll.} proc getsockopt*(s: SocketHandle, level, optname: cint, optval: pointer, @@ -515,7 +517,7 @@ proc listen*(s: SocketHandle, backlog: cint): cint {. stdcall, importc: "listen", dynlib: ws2dll.} proc recv*(s: SocketHandle, buf: pointer, len, flags: cint): cint {. stdcall, importc: "recv", dynlib: ws2dll.} -proc recvfrom*(s: SocketHandle, buf: cstring, len, flags: cint, +proc recvfrom*(s: SocketHandle, buf: cstring, len, flags: cint, fromm: ptr SockAddr, fromlen: ptr SockLen): cint {. stdcall, importc: "recvfrom", dynlib: ws2dll.} proc select*(nfds: cint, readfds, writefds, exceptfds: ptr TFdSet, @@ -529,22 +531,22 @@ proc sendto*(s: SocketHandle, buf: pointer, len, flags: cint, proc shutdown*(s: SocketHandle, how: cint): cint {. stdcall, importc: "shutdown", dynlib: ws2dll.} - + proc getnameinfo*(a1: ptr SockAddr, a2: SockLen, a3: cstring, a4: SockLen, a5: cstring, a6: SockLen, a7: cint): cint {. stdcall, importc: "getnameinfo", dynlib: ws2dll.} - + proc inet_addr*(cp: cstring): int32 {. - stdcall, importc: "inet_addr", dynlib: ws2dll.} + stdcall, importc: "inet_addr", dynlib: ws2dll.} proc WSAFDIsSet(s: SocketHandle, set: var TFdSet): bool {. stdcall, importc: "__WSAFDIsSet", dynlib: ws2dll, noSideEffect.} -proc FD_ISSET*(socket: SocketHandle, set: var TFdSet): cint = +proc FD_ISSET*(socket: SocketHandle, set: var TFdSet): cint = result = if WSAFDIsSet(socket, set): 1'i32 else: 0'i32 -proc FD_SET*(socket: SocketHandle, s: var TFdSet) = +proc FD_SET*(socket: SocketHandle, s: var TFdSet) = if s.fd_count < FD_SETSIZE: s.fd_array[int(s.fd_count)] = socket inc(s.fd_count) @@ -575,8 +577,8 @@ type proc waitForMultipleObjects*(nCount: DWORD, lpHandles: PWOHandleArray, bWaitAll: WINBOOL, dwMilliseconds: DWORD): DWORD{. stdcall, dynlib: "kernel32", importc: "WaitForMultipleObjects".} - - + + # for memfiles.nim: const @@ -586,7 +588,7 @@ const FILE_SHARE_READ* = 1'i32 FILE_SHARE_DELETE* = 4'i32 FILE_SHARE_WRITE* = 2'i32 - + CREATE_ALWAYS* = 2'i32 CREATE_NEW* = 1'i32 OPEN_EXISTING* = 3'i32 @@ -628,7 +630,7 @@ proc setEndOfFile*(hFile: THandle): WINBOOL {.stdcall, dynlib: "kernel32", importc: "SetEndOfFile".} proc setFilePointer*(hFile: THandle, lDistanceToMove: LONG, - lpDistanceToMoveHigh: ptr LONG, + lpDistanceToMoveHigh: ptr LONG, dwMoveMethod: DWORD): DWORD {. stdcall, dynlib: "kernel32", importc: "SetFilePointer".} @@ -637,14 +639,14 @@ proc getFileSize*(hFile: THandle, lpFileSizeHigh: ptr DWORD): DWORD{.stdcall, proc mapViewOfFileEx*(hFileMappingObject: THandle, dwDesiredAccess: DWORD, dwFileOffsetHigh, dwFileOffsetLow: DWORD, - dwNumberOfBytesToMap: DWORD, + dwNumberOfBytesToMap: DWORD, lpBaseAddress: pointer): pointer{. stdcall, dynlib: "kernel32", importc: "MapViewOfFileEx".} proc createFileMappingW*(hFile: THandle, lpFileMappingAttributes: pointer, flProtect, dwMaximumSizeHigh: DWORD, - dwMaximumSizeLow: DWORD, + dwMaximumSizeLow: DWORD, lpName: pointer): THandle {. stdcall, dynlib: "kernel32", importc: "CreateFileMappingW".} @@ -702,7 +704,7 @@ proc getOverlappedResult*(hFile: THandle, lpOverlapped: TOVERLAPPED, lpNumberOfBytesTransferred: var DWORD, bWait: WINBOOL): WINBOOL{. stdcall, dynlib: "kernel32", importc: "GetOverlappedResult".} -const +const IOC_OUT* = 0x40000000 IOC_IN* = 0x80000000 IOC_WS2* = 0x08000000 @@ -725,7 +727,7 @@ var proc WSAIoctl*(s: SocketHandle, dwIoControlCode: DWORD, lpvInBuffer: pointer, cbInBuffer: DWORD, lpvOutBuffer: pointer, cbOutBuffer: DWORD, lpcbBytesReturned: PDWORD, lpOverlapped: POVERLAPPED, - lpCompletionRoutine: POVERLAPPED_COMPLETION_ROUTINE): cint + lpCompletionRoutine: POVERLAPPED_COMPLETION_ROUTINE): cint {.stdcall, importc: "WSAIoctl", dynlib: "Ws2_32.dll".} type @@ -746,7 +748,7 @@ proc WSASend*(s: SocketHandle, buf: ptr TWSABuf, bufCount: DWORD, proc get_osfhandle*(fd:FileHandle): THandle {. importc: "_get_osfhandle", header:"<io.h>".} -proc getSystemTimes*(lpIdleTime, lpKernelTime, +proc getSystemTimes*(lpIdleTime, lpKernelTime, lpUserTime: var TFILETIME): WINBOOL {.stdcall, dynlib: "kernel32", importc: "GetSystemTimes".} diff --git a/lib/wrappers/claro.nim b/lib/wrappers/claro.nim index d36b9f178..0fb0882bf 100644 --- a/lib/wrappers/claro.nim +++ b/lib/wrappers/claro.nim @@ -2710,7 +2710,7 @@ proc workspace_window_set_icon*(w: ptr TWorkspaceWindow, icon: ptr TImage){. claro_base_init() claro_graphics_init() -when isMainModule: +when not defined(testing) and isMainModule: var w = newWindow(nil, newBounds(100, 100, 230, 230), 0) window_set_title(w, "Hello, World!") diff --git a/lib/wrappers/iup.nim b/lib/wrappers/iup.nim index 27ab7d870..93e14cccd 100644 --- a/lib/wrappers/iup.nim +++ b/lib/wrappers/iup.nim @@ -1,13 +1,13 @@ # # Binding for the IUP GUI toolkit -# (c) 2012 Andreas Rumpf +# (c) 2012 Andreas Rumpf # C header files translated by hand # Licence of IUP follows: # **************************************************************************** # Copyright (C) 1994-2009 Tecgraf, PUC-Rio. -# +# # Permission is hereby granted, free of charge, to any person obtaining # a copy of this software and associated documentation files (the # "Software"), to deal in the Software without restriction, including @@ -30,12 +30,12 @@ {.deadCodeElim: on.} -when defined(windows): - const dllname = "iup(30|27|26|25|24).dll" +when defined(windows): + const dllname = "iup(|30|27|26|25|24).dll" elif defined(macosx): - const dllname = "libiup(3.0|2.7|2.6|2.5|2.4).dylib" -else: - const dllname = "libiup(3.0|2.7|2.6|2.5|2.4).so.1" + const dllname = "libiup(|3.0|2.7|2.6|2.5|2.4).dylib" +else: + const dllname = "libiup(|3.0|2.7|2.6|2.5|2.4).so(|.1)" const IUP_NAME* = "IUP - Portable User Interface" @@ -67,8 +67,8 @@ proc alarm*(title, msg, b1, b2, b3: cstring): cint {. importc: "IupAlarm", dynlib: dllname, cdecl.} proc scanf*(format: cstring): cint {. importc: "IupScanf", dynlib: dllname, cdecl, varargs.} -proc listDialog*(theType: cint, title: cstring, size: cint, - list: cstringArray, op, maxCol, maxLin: cint, +proc listDialog*(theType: cint, title: cstring, size: cint, + list: cstringArray, op, maxCol, maxLin: cint, marks: ptr cint): cint {. importc: "IupListDialog", dynlib: dllname, cdecl.} proc getText*(title, text: cstring): cint {. @@ -77,14 +77,14 @@ proc getColor*(x, y: cint, r, g, b: var byte): cint {. importc: "IupGetColor", dynlib: dllname, cdecl.} type - Iparamcb* = proc (dialog: PIhandle, paramIndex: cint, + Iparamcb* = proc (dialog: PIhandle, paramIndex: cint, userData: pointer): cint {.cdecl.} -proc getParam*(title: cstring, action: Iparamcb, userData: pointer, +proc getParam*(title: cstring, action: Iparamcb, userData: pointer, format: cstring): cint {. importc: "IupGetParam", cdecl, varargs, dynlib: dllname.} -proc getParamv*(title: cstring, action: Iparamcb, userData: pointer, - format: cstring, paramCount, paramExtra: cint, +proc getParamv*(title: cstring, action: Iparamcb, userData: pointer, + format: cstring, paramCount, paramExtra: cint, paramData: pointer): cint {. importc: "IupGetParamv", cdecl, dynlib: dllname.} @@ -96,11 +96,11 @@ proc open*(argc: ptr cint, argv: ptr cstringArray): cint {. proc close*() {.importc: "IupClose", cdecl, dynlib: dllname.} proc imageLibOpen*() {.importc: "IupImageLibOpen", cdecl, dynlib: dllname.} -proc mainLoop*(): cint {.importc: "IupMainLoop", cdecl, dynlib: dllname, +proc mainLoop*(): cint {.importc: "IupMainLoop", cdecl, dynlib: dllname, discardable.} proc loopStep*(): cint {.importc: "IupLoopStep", cdecl, dynlib: dllname, discardable.} -proc mainLoopLevel*(): cint {.importc: "IupMainLoopLevel", cdecl, +proc mainLoopLevel*(): cint {.importc: "IupMainLoopLevel", cdecl, dynlib: dllname, discardable.} proc flush*() {.importc: "IupFlush", cdecl, dynlib: dllname.} proc exitLoop*() {.importc: "IupExitLoop", cdecl, dynlib: dllname.} @@ -204,7 +204,7 @@ proc getCallback*(ih: PIhandle, name: cstring): Icallback {. importc: "IupGetCallback", cdecl, dynlib: dllname.} proc setCallback*(ih: PIhandle, name: cstring, fn: Icallback): Icallback {. importc: "IupSetCallback", cdecl, dynlib: dllname, discardable.} - + proc setCallbacks*(ih: PIhandle, name: cstring, fn: Icallback): PIhandle {. importc: "IupSetCallbacks", cdecl, dynlib: dllname, varargs, discardable.} @@ -235,7 +235,7 @@ proc getClassName*(ih: PIhandle): cstring {. importc: "IupGetClassName", cdecl, dynlib: dllname.} proc getClassType*(ih: PIhandle): cstring {. importc: "IupGetClassType", cdecl, dynlib: dllname.} -proc getClassAttributes*(classname: cstring, names: cstringArray, +proc getClassAttributes*(classname: cstring, names: cstringArray, n: cint): cint {. importc: "IupGetClassAttributes", cdecl, dynlib: dllname.} proc saveClassAttributes*(ih: PIhandle) {. @@ -378,12 +378,12 @@ const IUP_CONTINUE* = cint(-4) # IupPopup and IupShowXY Parameter Values - IUP_CENTER* = cint(0xFFFF) - IUP_LEFT* = cint(0xFFFE) - IUP_RIGHT* = cint(0xFFFD) - IUP_MOUSEPOS* = cint(0xFFFC) - IUP_CURRENT* = cint(0xFFFB) - IUP_CENTERPARENT* = cint(0xFFFA) + IUP_CENTER* = cint(0xFFFF) + IUP_LEFT* = cint(0xFFFE) + IUP_RIGHT* = cint(0xFFFD) + IUP_MOUSEPOS* = cint(0xFFFC) + IUP_CURRENT* = cint(0xFFFB) + IUP_CENTERPARENT* = cint(0xFFFA) IUP_TOP* = IUP_LEFT IUP_BOTTOM* = IUP_RIGHT @@ -397,10 +397,10 @@ const # SCROLL_CB Callback Values IUP_SBUP* = cint(0) IUP_SBDN* = cint(1) - IUP_SBPGUP* = cint(2) + IUP_SBPGUP* = cint(2) IUP_SBPGDN* = cint(3) IUP_SBPOSV* = cint(4) - IUP_SBDRAGV* = cint(5) + IUP_SBDRAGV* = cint(5) IUP_SBLEFT* = cint(6) IUP_SBRIGHT* = cint(7) IUP_SBPGLEFT* = cint(8) @@ -433,12 +433,12 @@ const IUP_MASK_EFLOAT* = "[+/-]?(/d+/.?/d*|/./d+)([eE][+/-]?/d+)?" IUP_MASK_INT* = "[+/-]?/d+" IUP_MASK_UINT* = "/d+" - + # from 32 to 126, all character sets are equal, # the key code i the same as the character code. const K_SP* = cint(ord(' ')) - K_exclam* = cint(ord('!')) + K_exclam* = cint(ord('!')) K_quotedbl* = cint(ord('\"')) K_numbersign* = cint(ord('#')) K_dollar* = cint(ord('$')) @@ -467,51 +467,51 @@ const K_semicolon* = cint(ord(';')) K_less* = cint(ord('<')) K_equal* = cint(ord('=')) - K_greater* = cint(ord('>')) - K_question* = cint(ord('?')) - K_at* = cint(ord('@')) - K_upperA* = cint(ord('A')) - K_upperB* = cint(ord('B')) - K_upperC* = cint(ord('C')) - K_upperD* = cint(ord('D')) - K_upperE* = cint(ord('E')) - K_upperF* = cint(ord('F')) - K_upperG* = cint(ord('G')) - K_upperH* = cint(ord('H')) - K_upperI* = cint(ord('I')) - K_upperJ* = cint(ord('J')) - K_upperK* = cint(ord('K')) - K_upperL* = cint(ord('L')) - K_upperM* = cint(ord('M')) - K_upperN* = cint(ord('N')) - K_upperO* = cint(ord('O')) - K_upperP* = cint(ord('P')) - K_upperQ* = cint(ord('Q')) - K_upperR* = cint(ord('R')) - K_upperS* = cint(ord('S')) - K_upperT* = cint(ord('T')) - K_upperU* = cint(ord('U')) - K_upperV* = cint(ord('V')) - K_upperW* = cint(ord('W')) - K_upperX* = cint(ord('X')) - K_upperY* = cint(ord('Y')) - K_upperZ* = cint(ord('Z')) - K_bracketleft* = cint(ord('[')) - K_backslash* = cint(ord('\\')) - K_bracketright* = cint(ord(']')) - K_circum* = cint(ord('^')) - K_underscore* = cint(ord('_')) - K_grave* = cint(ord('`')) - K_lowera* = cint(ord('a')) - K_lowerb* = cint(ord('b')) - K_lowerc* = cint(ord('c')) - K_lowerd* = cint(ord('d')) - K_lowere* = cint(ord('e')) - K_lowerf* = cint(ord('f')) + K_greater* = cint(ord('>')) + K_question* = cint(ord('?')) + K_at* = cint(ord('@')) + K_upperA* = cint(ord('A')) + K_upperB* = cint(ord('B')) + K_upperC* = cint(ord('C')) + K_upperD* = cint(ord('D')) + K_upperE* = cint(ord('E')) + K_upperF* = cint(ord('F')) + K_upperG* = cint(ord('G')) + K_upperH* = cint(ord('H')) + K_upperI* = cint(ord('I')) + K_upperJ* = cint(ord('J')) + K_upperK* = cint(ord('K')) + K_upperL* = cint(ord('L')) + K_upperM* = cint(ord('M')) + K_upperN* = cint(ord('N')) + K_upperO* = cint(ord('O')) + K_upperP* = cint(ord('P')) + K_upperQ* = cint(ord('Q')) + K_upperR* = cint(ord('R')) + K_upperS* = cint(ord('S')) + K_upperT* = cint(ord('T')) + K_upperU* = cint(ord('U')) + K_upperV* = cint(ord('V')) + K_upperW* = cint(ord('W')) + K_upperX* = cint(ord('X')) + K_upperY* = cint(ord('Y')) + K_upperZ* = cint(ord('Z')) + K_bracketleft* = cint(ord('[')) + K_backslash* = cint(ord('\\')) + K_bracketright* = cint(ord(']')) + K_circum* = cint(ord('^')) + K_underscore* = cint(ord('_')) + K_grave* = cint(ord('`')) + K_lowera* = cint(ord('a')) + K_lowerb* = cint(ord('b')) + K_lowerc* = cint(ord('c')) + K_lowerd* = cint(ord('d')) + K_lowere* = cint(ord('e')) + K_lowerf* = cint(ord('f')) K_lowerg* = cint(ord('g')) - K_lowerh* = cint(ord('h')) - K_loweri* = cint(ord('i')) - K_lowerj* = cint(ord('j')) + K_lowerh* = cint(ord('h')) + K_loweri* = cint(ord('i')) + K_lowerj* = cint(ord('j')) K_lowerk* = cint(ord('k')) K_lowerl* = cint(ord('l')) K_lowerm* = cint(ord('m')) @@ -553,19 +553,19 @@ proc isAltXkey*(c: cint): bool = return c > 768 and c < 1024 proc isSysXkey*(c: cint): bool = return c > 1024 and c < 1280 proc iUPxCODE*(c: cint): cint = return c + cint(128) # Normal (must be above 128) -proc iUPsxCODE*(c: cint): cint = +proc iUPsxCODE*(c: cint): cint = return c + cint(256) - # Shift (must have range to include the standard keys and the normal + # Shift (must have range to include the standard keys and the normal # extended keys, so must be above 256 proc iUPcxCODE*(c: cint): cint = return c + cint(512) # Ctrl proc iUPmxCODE*(c: cint): cint = return c + cint(768) # Alt -proc iUPyxCODE*(c: cint): cint = return c + cint(1024) # Sys (Win or Apple) +proc iUPyxCODE*(c: cint): cint = return c + cint(1024) # Sys (Win or Apple) const IUP_NUMMAXCODES* = 1280 ## 5*256=1280 Normal+Shift+Ctrl+Alt+Sys - K_HOME* = iUPxCODE(1) + K_HOME* = iUPxCODE(1) K_UP* = iUPxCODE(2) K_PGUP* = iUPxCODE(3) K_LEFT* = iUPxCODE(4) @@ -574,8 +574,8 @@ const K_END* = iUPxCODE(7) K_DOWN* = iUPxCODE(8) K_PGDN* = iUPxCODE(9) - K_INS* = iUPxCODE(10) - K_DEL* = iUPxCODE(11) + K_INS* = iUPxCODE(10) + K_DEL* = iUPxCODE(11) K_PAUSE* = iUPxCODE(12) K_ESC* = iUPxCODE(13) K_ccedilla* = iUPxCODE(14) @@ -728,13 +728,13 @@ const K_yPrint* = iUPyxCODE(K_Print) K_yMenu* = iUPyxCODE(K_Menu) - K_sPlus* = iUPsxCODE(K_plus) - K_sComma* = iUPsxCODE(K_comma) - K_sMinus* = iUPsxCODE(K_minus) - K_sPeriod* = iUPsxCODE(K_period) - K_sSlash* = iUPsxCODE(K_slash) + K_sPlus* = iUPsxCODE(K_plus) + K_sComma* = iUPsxCODE(K_comma) + K_sMinus* = iUPsxCODE(K_minus) + K_sPeriod* = iUPsxCODE(K_period) + K_sSlash* = iUPsxCODE(K_slash) K_sAsterisk* = iUPsxCODE(K_asterisk) - + K_cupperA* = iUPcxCODE(K_upperA) K_cupperB* = iUPcxCODE(K_upperB) K_cupperC* = iUPcxCODE(K_upperC) @@ -767,16 +767,16 @@ const K_c4* = iUPcxCODE(K_4) K_c5* = iUPcxCODE(K_5) K_c6* = iUPcxCODE(K_6) - K_c7* = iUPcxCODE(K_7) - K_c8* = iUPcxCODE(K_8) + K_c7* = iUPcxCODE(K_7) + K_c8* = iUPcxCODE(K_8) K_c9* = iUPcxCODE(K_9) K_c0* = iUPcxCODE(K_0) - K_cPlus* = iUPcxCODE(K_plus) - K_cComma* = iUPcxCODE(K_comma) - K_cMinus* = iUPcxCODE(K_minus) - K_cPeriod* = iUPcxCODE(K_period) - K_cSlash* = iUPcxCODE(K_slash) - K_cSemicolon* = iUPcxCODE(K_semicolon) + K_cPlus* = iUPcxCODE(K_plus) + K_cComma* = iUPcxCODE(K_comma) + K_cMinus* = iUPcxCODE(K_minus) + K_cPeriod* = iUPcxCODE(K_period) + K_cSlash* = iUPcxCODE(K_slash) + K_cSemicolon* = iUPcxCODE(K_semicolon) K_cEqual* = iUPcxCODE(K_equal) K_cBracketleft* = iUPcxCODE(K_bracketleft) K_cBracketright* = iUPcxCODE(K_bracketright) @@ -815,16 +815,16 @@ const K_m4* = iUPmxCODE(K_4) K_m5* = iUPmxCODE(K_5) K_m6* = iUPmxCODE(K_6) - K_m7* = iUPmxCODE(K_7) - K_m8* = iUPmxCODE(K_8) + K_m7* = iUPmxCODE(K_7) + K_m8* = iUPmxCODE(K_8) K_m9* = iUPmxCODE(K_9) K_m0* = iUPmxCODE(K_0) - K_mPlus* = iUPmxCODE(K_plus) - K_mComma* = iUPmxCODE(K_comma) - K_mMinus* = iUPmxCODE(K_minus) - K_mPeriod* = iUPmxCODE(K_period) - K_mSlash* = iUPmxCODE(K_slash) - K_mSemicolon* = iUPmxCODE(K_semicolon) + K_mPlus* = iUPmxCODE(K_plus) + K_mComma* = iUPmxCODE(K_comma) + K_mMinus* = iUPmxCODE(K_minus) + K_mPeriod* = iUPmxCODE(K_period) + K_mSlash* = iUPmxCODE(K_slash) + K_mSemicolon* = iUPmxCODE(K_semicolon) K_mEqual* = iUPmxCODE(K_equal) K_mBracketleft* = iUPmxCODE(K_bracketleft) K_mBracketright* = iUPmxCODE(K_bracketright) @@ -863,16 +863,16 @@ const K_y4* = iUPyxCODE(K_4) K_y5* = iUPyxCODE(K_5) K_y6* = iUPyxCODE(K_6) - K_y7* = iUPyxCODE(K_7) - K_y8* = iUPyxCODE(K_8) + K_y7* = iUPyxCODE(K_7) + K_y8* = iUPyxCODE(K_8) K_y9* = iUPyxCODE(K_9) K_y0* = iUPyxCODE(K_0) K_yPlus* = iUPyxCODE(K_plus) K_yComma* = iUPyxCODE(K_comma) - K_yMinus* = iUPyxCODE(K_minus) - K_yPeriod* = iUPyxCODE(K_period) - K_ySlash* = iUPyxCODE(K_slash) - K_ySemicolon* = iUPyxCODE(K_semicolon) + K_yMinus* = iUPyxCODE(K_minus) + K_yPeriod* = iUPyxCODE(K_period) + K_ySlash* = iUPyxCODE(K_slash) + K_ySemicolon* = iUPyxCODE(K_semicolon) K_yEqual* = iUPyxCODE(K_equal) K_yBracketleft* = iUPyxCODE(K_bracketleft) K_yBracketright* = iUPyxCODE(K_bracketright) @@ -893,11 +893,11 @@ proc dial*(theType: cstring): PIhandle {.cdecl, importc: "IupDial", dynlib: dlln proc matrix*(action: cstring): PIhandle {.cdecl, importc: "IupMatrix", dynlib: dllname.} # IupMatrix utilities -proc matSetAttribute*(ih: PIhandle, name: cstring, lin, col: cint, +proc matSetAttribute*(ih: PIhandle, name: cstring, lin, col: cint, value: cstring) {. cdecl, importc: "IupMatSetAttribute", dynlib: dllname.} -proc matStoreAttribute*(ih: PIhandle, name: cstring, lin, col: cint, - value: cstring) {.cdecl, +proc matStoreAttribute*(ih: PIhandle, name: cstring, lin, col: cint, + value: cstring) {.cdecl, importc: "IupMatStoreAttribute", dynlib: dllname.} proc matGetAttribute*(ih: PIhandle, name: cstring, lin, col: cint): cstring {. cdecl, importc: "IupMatGetAttribute", dynlib: dllname.} @@ -905,9 +905,9 @@ proc matGetInt*(ih: PIhandle, name: cstring, lin, col: cint): cint {. cdecl, importc: "IupMatGetInt", dynlib: dllname.} proc matGetFloat*(ih: PIhandle, name: cstring, lin, col: cint): cfloat {. cdecl, importc: "IupMatGetFloat", dynlib: dllname.} -proc matSetfAttribute*(ih: PIhandle, name: cstring, lin, col: cint, - format: cstring) {.cdecl, - importc: "IupMatSetfAttribute", +proc matSetfAttribute*(ih: PIhandle, name: cstring, lin, col: cint, + format: cstring) {.cdecl, + importc: "IupMatSetfAttribute", dynlib: dllname, varargs.} # Used by IupColorbar @@ -931,10 +931,10 @@ proc pPlotAddStr*(ih: PIhandle, x: cstring, y: cfloat) {. proc pPlotEnd*(ih: PIhandle): cint {. cdecl, importc: "IupPPlotEnd", dynlib: dllname.} -proc pPlotInsertStr*(ih: PIhandle, index, sampleIndex: cint, x: cstring, - y: cfloat) {.cdecl, importc: "IupPPlotInsertStr", +proc pPlotInsertStr*(ih: PIhandle, index, sampleIndex: cint, x: cstring, + y: cfloat) {.cdecl, importc: "IupPPlotInsertStr", dynlib: dllname.} -proc pPlotInsert*(ih: PIhandle, index, sampleIndex: cint, +proc pPlotInsert*(ih: PIhandle, index, sampleIndex: cint, x, y: cfloat) {. cdecl, importc: "IupPPlotInsert", dynlib: dllname.} diff --git a/lib/wrappers/pcre.nim b/lib/wrappers/pcre.nim index afa8f447a..67436f026 100644 --- a/lib/wrappers/pcre.nim +++ b/lib/wrappers/pcre.nim @@ -1,158 +1,190 @@ #************************************************ -# Perl-Compatible Regular Expressions * -#*********************************************** +# Perl-Compatible Regular Expressions * +#************************************************ # This is the public header file for the PCRE library, to be #included by -#applications that call the PCRE functions. +# applications that call the PCRE functions. # -# Copyright (c) 1997-2010 University of Cambridge +# Copyright (c) 1997-2014 University of Cambridge # #----------------------------------------------------------------------------- -#Redistribution and use in source and binary forms, with or without -#modification, are permitted provided that the following conditions are met: +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: # -# Redistributions of source code must retain the above copyright notice, +# * Redistributions of source code must retain the above copyright notice, # this list of conditions and the following disclaimer. # -# Redistributions in binary form must reproduce the above copyright +# * Redistributions in binary form must reproduce the above copyright # notice, this list of conditions and the following disclaimer in the # documentation and/or other materials provided with the distribution. # -# Neither the name of the University of Cambridge nor the names of its +# * Neither the name of the University of Cambridge nor the names of its # contributors may be used to endorse or promote products derived from # this software without specific prior written permission. # -#THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -#AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -#IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -#ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE -#LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -#CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -#SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -#INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -#CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -#ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -#POSSIBILITY OF SUCH DAMAGE. +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGE. #----------------------------------------------------------------------------- -# -{.deadcodeElim: on.} - -when not defined(pcreDll): - when hostOS == "windows": - const pcreDll = "pcre.dll" - elif hostOS == "macosx": - const pcreDll = "libpcre(.3|.1|).dylib" - else: - const pcreDll = "libpcre.so(.3|.1|)" - {.pragma: pcreImport, dynlib: pcreDll.} -else: - {.pragma: pcreImport, header: "<pcre.h>".} +{.deadCodeElim: on.} -# The current PCRE version information. +# The current PCRE version information. -const - MAJOR* = 8 - MINOR* = 31 - PRERELEASE* = true - DATE* = "2012-07-06" +const + PCRE_MAJOR* = 8 + PCRE_MINOR* = 36 + PCRE_PRERELEASE* = true + PCRE_DATE* = "2014-09-26" # When an application links to a PCRE DLL in Windows, the symbols that are # imported have to be identified as such. When building PCRE, the appropriate # export setting is defined in pcre_internal.h, which includes this file. So we -# don't change existing definitions of PCRE_EXP_DECL and PCRECPP_EXP_DECL. - -# Have to include stdlib.h in order to ensure that size_t is defined; -# it is needed here for malloc. - -# Allow for C++ users - -# Options. Some are compile-time only, some are run-time only, and some are -# both, so we keep them all distinct. - -const - CASELESS* = 0x00000001 - MULTILINE* = 0x00000002 - DOTALL* = 0x00000004 - EXTENDED* = 0x00000008 - ANCHORED* = 0x00000010 - DOLLAR_ENDONLY* = 0x00000020 - EXTRA* = 0x00000040 - NOTBOL* = 0x00000080 - NOTEOL* = 0x00000100 - UNGREEDY* = 0x00000200 - NOTEMPTY* = 0x00000400 - UTF8* = 0x00000800 - NO_AUTO_CAPTURE* = 0x00001000 - NO_UTF8_CHECK* = 0x00002000 - AUTO_CALLOUT* = 0x00004000 - PARTIAL_SOFT* = 0x00008000 - PARTIAL* = 0x00008000 # Backwards compatible synonym - DFA_SHORTEST* = 0x00010000 - DFA_RESTART* = 0x00020000 - FIRSTLINE* = 0x00040000 - DUPNAMES* = 0x00080000 - NEWLINE_CR* = 0x00100000 - NEWLINE_LF* = 0x00200000 - NEWLINE_CRLF* = 0x00300000 - NEWLINE_ANY* = 0x00400000 - NEWLINE_ANYCRLF* = 0x00500000 - BSR_ANYCRLF* = 0x00800000 - BSR_UNICODE* = 0x01000000 - JAVASCRIPT_COMPAT* = 0x02000000 - NO_START_OPTIMIZE* = 0x04000000 - NO_START_OPTIMISE* = 0x04000000 - PARTIAL_HARD* = 0x08000000 - NOTEMPTY_ATSTART* = 0x10000000 - UCP* = 0x20000000 - -# Exec-time and get/set-time error codes - -const - ERROR_NOMATCH* = (- 1) - ERROR_NULL* = (- 2) - ERROR_BADOPTION* = (- 3) - ERROR_BADMAGIC* = (- 4) - ERROR_UNKNOWN_OPCODE* = (- 5) - ERROR_UNKNOWN_NODE* = (- 5) # For backward compatibility - ERROR_NOMEMORY* = (- 6) - ERROR_NOSUBSTRING* = (- 7) - ERROR_MATCHLIMIT* = (- 8) - ERROR_CALLOUT* = (- 9) # Never used by PCRE itself - ERROR_BADUTF8* = (- 10) - ERROR_BADUTF8_OFFSET* = (- 11) - ERROR_PARTIAL* = (- 12) - ERROR_BADPARTIAL* = (- 13) - ERROR_INTERNAL* = (- 14) - ERROR_BADCOUNT* = (- 15) - ERROR_DFA_UITEM* = (- 16) - ERROR_DFA_UCOND* = (- 17) - ERROR_DFA_UMLIMIT* = (- 18) - ERROR_DFA_WSSIZE* = (- 19) - ERROR_DFA_RECURSE* = (- 20) - ERROR_RECURSIONLIMIT* = (- 21) - ERROR_NULLWSLIMIT* = (- 22) # No longer actually used - ERROR_BADNEWLINE* = (- 23) - ERROR_BADOFFSET* = (- 24) - ERROR_SHORTUTF8* = (- 25) - ERROR_RECURSELOOP* = (- 26) - ERROR_JIT_STACKLIMIT* = (- 27) - ERROR_BADMODE* = (- 28) - ERROR_BADENDIANNESS* = (- 29) - ERROR_DFA_BADRESTART* = (- 30) - -# Specific error codes for UTF-8 validity checks +# don't change existing definitions of PCRE_EXP_DECL and PCRECPP_EXP_DECL. + +# By default, we use the standard "extern" declarations. + +# Allow for C++ users + +# Public options. Some are compile-time only, some are run-time only, and some +# are both. Most of the compile-time options are saved with the compiled regex +# so that they can be inspected during studying (and therefore JIT compiling). +# Note that pcre_study() has its own set of options. Originally, all the options +# defined here used distinct bits. However, almost all the bits in a 32-bit word +# are now used, so in order to conserve them, option bits that were previously +# only recognized at matching time (i.e. by pcre_exec() or pcre_dfa_exec()) may +# also be used for compile-time options that affect only compiling and are not +# relevant for studying or JIT compiling. +# +# Some options for pcre_compile() change its behaviour but do not affect the +# behaviour of the execution functions. Other options are passed through to the +# execution functions and affect their behaviour, with or without affecting the +# behaviour of pcre_compile(). +# +# Options that can be passed to pcre_compile() are tagged Cx below, with these +# variants: +# +# C1 Affects compile only +# C2 Does not affect compile; affects exec, dfa_exec +# C3 Affects compile, exec, dfa_exec +# C4 Affects compile, exec, dfa_exec, study +# C5 Affects compile, exec, study +# +# Options that can be set for pcre_exec() and/or pcre_dfa_exec() are flagged +# with E and D, respectively. They take precedence over C3, C4, and C5 settings +# passed from pcre_compile(). Those that are compatible with JIT execution are +# flagged with J. + +const + CASELESS* = 0x00000001 # C1 + MULTILINE* = 0x00000002 # C1 + DOTALL* = 0x00000004 # C1 + EXTENDED* = 0x00000008 # C1 + ANCHORED* = 0x00000010 # C4 E D + DOLLAR_ENDONLY* = 0x00000020 # C2 + EXTRA* = 0x00000040 # C1 + NOTBOL* = 0x00000080 # E D J + NOTEOL* = 0x00000100 # E D J + UNGREEDY* = 0x00000200 # C1 + NOTEMPTY* = 0x00000400 # E D J + UTF8* = 0x00000800 # C4 ) + UTF16* = 0x00000800 # C4 ) Synonyms + UTF32* = 0x00000800 # C4 ) + NO_AUTO_CAPTURE* = 0x00001000 # C1 + NO_UTF8_CHECK* = 0x00002000 # C1 E D J ) + NO_UTF16_CHECK* = 0x00002000 # C1 E D J ) Synonyms + NO_UTF32_CHECK* = 0x00002000 # C1 E D J ) + AUTO_CALLOUT* = 0x00004000 # C1 + PARTIAL_SOFT* = 0x00008000 # E D J ) Synonyms + PARTIAL* = 0x00008000 # E D J ) + +# This pair use the same bit. +const + NEVER_UTF* = 0x00010000 # C1 ) Overlaid + DFA_SHORTEST* = 0x00010000 # D ) Overlaid +# This pair use the same bit. const - UTF8_ERR0* = 0 - UTF8_ERR1* = 1 - UTF8_ERR2* = 2 - UTF8_ERR3* = 3 - UTF8_ERR4* = 4 - UTF8_ERR5* = 5 - UTF8_ERR6* = 6 - UTF8_ERR7* = 7 - UTF8_ERR8* = 8 - UTF8_ERR9* = 9 + NO_AUTO_POSSESS* = 0x00020000 # C1 ) Overlaid + DFA_RESTART* = 0x00020000 # D ) Overlaid + +const + FIRSTLINE* = 0x00040000 # C3 + DUPNAMES* = 0x00080000 # C1 + NEWLINE_CR* = 0x00100000 # C3 E D + NEWLINE_LF* = 0x00200000 # C3 E D + NEWLINE_CRLF* = 0x00300000 # C3 E D + NEWLINE_ANY* = 0x00400000 # C3 E D + NEWLINE_ANYCRLF* = 0x00500000 # C3 E D + BSR_ANYCRLF* = 0x00800000 # C3 E D + BSR_UNICODE* = 0x01000000 # C3 E D + JAVASCRIPT_COMPAT* = 0x02000000 # C5 + NO_START_OPTIMIZE* = 0x04000000 # C2 E D ) Synonyms + NO_START_OPTIMISE* = 0x04000000 # C2 E D ) + PARTIAL_HARD* = 0x08000000 # E D J + NOTEMPTY_ATSTART* = 0x10000000 # E D J + UCP* = 0x20000000 # C3 + +## Exec-time and get/set-time error codes +const + ERROR_NOMATCH* = -1 + ERROR_NULL* = -2 + ERROR_BADOPTION* = -3 + ERROR_BADMAGIC* = -4 + ERROR_UNKNOWN_OPCODE* = -5 + ERROR_UNKNOWN_NODE* = -5 ## For backward compatibility + ERROR_NOMEMORY* = -6 + ERROR_NOSUBSTRING* = -7 + ERROR_MATCHLIMIT* = -8 + ERROR_CALLOUT* = -9 ## Never used by PCRE itself + ERROR_BADUTF8* = -10 ## Same for 8/16/32 + ERROR_BADUTF16* = -10 ## Same for 8/16/32 + ERROR_BADUTF32* = -10 ## Same for 8/16/32 + ERROR_BADUTF8_OFFSET* = -11 ## Same for 8/16 + ERROR_BADUTF16_OFFSET* = -11 ## Same for 8/16 + ERROR_PARTIAL* = -12 + ERROR_BADPARTIAL* = -13 + ERROR_INTERNAL* = -14 + ERROR_BADCOUNT* = -15 + ERROR_DFA_UITEM* = -16 + ERROR_DFA_UCOND* = -17 + ERROR_DFA_UMLIMIT* = -18 + ERROR_DFA_WSSIZE* = -19 + ERROR_DFA_RECURSE* = -20 + ERROR_RECURSIONLIMIT* = -21 + ERROR_NULLWSLIMIT* = -22 ## No longer actually used + ERROR_BADNEWLINE* = -23 + ERROR_BADOFFSET* = -24 + ERROR_SHORTUTF8* = -25 + ERROR_SHORTUTF16* = -25 ## Same for 8/16 + ERROR_RECURSELOOP* = -26 + ERROR_JIT_STACKLIMIT* = -27 + ERROR_BADMODE* = -28 + ERROR_BADENDIANNESS* = -29 + ERROR_DFA_BADRESTART* = -30 + ERROR_JIT_BADOPTION* = -31 + ERROR_BADLENGTH* = -32 + ERROR_UNSET* = -33 + +## Specific error codes for UTF-8 validity checks +const + UTF8_ERR0* = 0 + UTF8_ERR1* = 1 + UTF8_ERR2* = 2 + UTF8_ERR3* = 3 + UTF8_ERR4* = 4 + UTF8_ERR5* = 5 + UTF8_ERR6* = 6 + UTF8_ERR7* = 7 + UTF8_ERR8* = 8 + UTF8_ERR9* = 9 UTF8_ERR10* = 10 UTF8_ERR11* = 11 UTF8_ERR12* = 12 @@ -165,193 +197,305 @@ const UTF8_ERR19* = 19 UTF8_ERR20* = 20 UTF8_ERR21* = 21 + UTF8_ERR22* = 22 # Unused (was non-character) -# Request types for pcre_fullinfo() - -const - INFO_OPTIONS* = 0 - INFO_SIZE* = 1 - INFO_CAPTURECOUNT* = 2 - INFO_BACKREFMAX* = 3 - INFO_FIRSTBYTE* = 4 - INFO_FIRSTCHAR* = 4 # For backwards compatibility - INFO_FIRSTTABLE* = 5 - INFO_LASTLITERAL* = 6 - INFO_NAMEENTRYSIZE* = 7 - INFO_NAMECOUNT* = 8 - INFO_NAMETABLE* = 9 - INFO_STUDYSIZE* = 10 - INFO_DEFAULT_TABLES* = 11 - INFO_OKPARTIAL* = 12 - INFO_JCHANGED* = 13 - INFO_HASCRORLF* = 14 - INFO_MINLENGTH* = 15 - INFO_JIT* = 16 - INFO_JITSIZE* = 17 - INFO_MAXLOOKBEHIND* = 18 - -# Request types for pcre_config(). Do not re-arrange, in order to remain -# compatible. - -const - CONFIG_UTF8* = 0 - CONFIG_NEWLINE* = 1 - CONFIG_LINK_SIZE* = 2 - CONFIG_POSIX_MALLOC_THRESHOLD* = 3 - CONFIG_MATCH_LIMIT* = 4 - CONFIG_STACKRECURSE* = 5 - CONFIG_UNICODE_PROPERTIES* = 6 - CONFIG_MATCH_LIMIT_RECURSION* = 7 - CONFIG_BSR* = 8 - CONFIG_JIT* = 9 - CONFIG_JITTARGET* = 11 - -# Request types for pcre_study(). Do not re-arrange, in order to remain -# compatible. +## Specific error codes for UTF-16 validity checks +const + UTF16_ERR0* = 0 + UTF16_ERR1* = 1 + UTF16_ERR2* = 2 + UTF16_ERR3* = 3 + UTF16_ERR4* = 4 # Unused (was non-character) +## Specific error codes for UTF-32 validity checks const - STUDY_JIT_COMPILE* = 0x00000001 - STUDY_JIT_PARTIAL_SOFT_COMPILE* = 0x00000002 - STUDY_JIT_PARTIAL_HARD_COMPILE* = 0x00000004 - -# Bit flags for the pcre_extra structure. Do not re-arrange or redefine -# these bits, just add new ones on the end, in order to remain compatible. - -const - EXTRA_STUDY_DATA* = 0x00000001 - EXTRA_MATCH_LIMIT* = 0x00000002 - EXTRA_CALLOUT_DATA* = 0x00000004 - EXTRA_TABLES* = 0x00000008 - EXTRA_MATCH_LIMIT_RECURSION* = 0x00000010 - EXTRA_MARK* = 0x00000020 - EXTRA_EXECUTABLE_JIT* = 0x00000040 - -# Types - -type - TPcre*{.pure, final.} = object - PPcre* = ptr TPcre - Tjit_stack*{.pure, final.} = object - Pjit_stack* = ptr Tjit_stack - -# When PCRE is compiled as a C++ library, the subject pointer type can be -# replaced with a custom type. For conventional use, the public interface is a -# const char *. - -# The structure for passing additional data to pcre_exec(). This is defined in -# such as way as to be extensible. Always add new fields at the end, in order to -# remain compatible. - -type - TExtra*{.pure, final.} = object - flags*: int ## Bits for which fields are set - study_data*: pointer ## Opaque data from pcre_study() - match_limit*: int ## Maximum number of calls to match() - callout_data*: pointer ## Data passed back in callouts - tables*: cstring ## Pointer to character tables - match_limit_recursion*: int ## Max recursive calls to match() - mark*: ptr cstring ## For passing back a mark pointer - executable_jit*: pointer ## Contains a pointer to a compiled jit code - - -# The structure for passing out data via the pcre_callout_function. We use a -# structure so that new fields can be added on the end in future versions, -# without changing the API of the function, thereby allowing old clients to work -# without modification. - -type - TCalloutBlock*{.pure, final.} = object - version*: cint ## Identifies version of block - callout_number*: cint ## Number compiled into pattern - offset_vector*: ptr cint ## The offset vector - subject*: cstring ## The subject being matched - subject_length*: cint ## The length of the subject - start_match*: cint ## Offset to start of this match attempt - current_position*: cint ## Where we currently are in the subject - capture_top*: cint ## Max current capture - capture_last*: cint ## Most recently closed capture - callout_data*: pointer ## Data passed in with the call - pattern_position*: cint ## Offset to next item in the pattern - next_item_length*: cint ## Length of next item in the pattern - mark*: cstring ## Pointer to current mark or NULL - -# Indirection for store get and free functions. These can be set to -#alternative malloc/free functions if required. Special ones are used in the -#non-recursive case for "frames". There is also an optional callout function -#that is triggered by the (?) regex item. For Virtual Pascal, these definitions -#have to take another form. - -# User defined callback which provides a stack just before the match starts. + UTF32_ERR0* = 0 + UTF32_ERR1* = 1 + UTF32_ERR2* = 2 # Unused (was non-character) + UTF32_ERR3* = 3 +## Request types for pcre_fullinfo() +const + INFO_OPTIONS* = 0 + INFO_SIZE* = 1 + INFO_CAPTURECOUNT* = 2 + INFO_BACKREFMAX* = 3 + INFO_FIRSTBYTE* = 4 + INFO_FIRSTCHAR* = 4 ## For backwards compatibility + INFO_FIRSTTABLE* = 5 + INFO_LASTLITERAL* = 6 + INFO_NAMEENTRYSIZE* = 7 + INFO_NAMECOUNT* = 8 + INFO_NAMETABLE* = 9 + INFO_STUDYSIZE* = 10 + INFO_DEFAULT_TABLES* = 11 + INFO_OKPARTIAL* = 12 + INFO_JCHANGED* = 13 + INFO_HASCRORLF* = 14 + INFO_MINLENGTH* = 15 + INFO_JIT* = 16 + INFO_JITSIZE* = 17 + INFO_MAXLOOKBEHIND* = 18 + INFO_FIRSTCHARACTER* = 19 + INFO_FIRSTCHARACTERFLAGS* = 20 + INFO_REQUIREDCHAR* = 21 + INFO_REQUIREDCHARFLAGS* = 22 + INFO_MATCHLIMIT* = 23 + INFO_RECURSIONLIMIT* = 24 + INFO_MATCH_EMPTY* = 25 + +## Request types for pcre_config(). Do not re-arrange, in order to remain +## compatible. +const + CONFIG_UTF8* = 0 + CONFIG_NEWLINE* = 1 + CONFIG_LINK_SIZE* = 2 + CONFIG_POSIX_MALLOC_THRESHOLD* = 3 + CONFIG_MATCH_LIMIT* = 4 + CONFIG_STACKRECURSE* = 5 + CONFIG_UNICODE_PROPERTIES* = 6 + CONFIG_MATCH_LIMIT_RECURSION* = 7 + CONFIG_BSR* = 8 + CONFIG_JIT* = 9 + CONFIG_UTF16* = 10 + CONFIG_JITTARGET* = 11 + CONFIG_UTF32* = 12 + CONFIG_PARENS_LIMIT* = 13 + +## Request types for pcre_study(). Do not re-arrange, in order to remain +## compatible. +const + STUDY_JIT_COMPILE* = 0x0001 + STUDY_JIT_PARTIAL_SOFT_COMPILE* = 0x0002 + STUDY_JIT_PARTIAL_HARD_COMPILE* = 0x0004 + STUDY_EXTRA_NEEDED* = 0x0008 + +## Bit flags for the pcre[16|32]_extra structure. Do not re-arrange or redefine +## these bits, just add new ones on the end, in order to remain compatible. +const + EXTRA_STUDY_DATA* = 0x0001 + EXTRA_MATCH_LIMIT* = 0x0002 + EXTRA_CALLOUT_DATA* = 0x0004 + EXTRA_TABLES* = 0x0008 + EXTRA_MATCH_LIMIT_RECURSION* = 0x0010 + EXTRA_MARK* = 0x0020 + EXTRA_EXECUTABLE_JIT* = 0x0040 + +## Types +type + Pcre* = object + Pcre16* = object + Pcre32* = object + JitStack* = object + JitStack16* = object + JitStack32* = object + + +## The structure for passing additional data to pcre_exec(). This is defined in +## such as way as to be extensible. Always add new fields at the end, in order +## to remain compatible. +type + ExtraData* = object + flags*: clong ## Bits for which fields are set + study_data*: pointer ## Opaque data from pcre_study() + match_limit*: clong ## Maximum number of calls to match() + callout_data*: pointer ## Data passed back in callouts + tables*: pointer ## Pointer to character tables + match_limit_recursion*: clong ## Max recursive calls to match() + mark*: pointer ## For passing back a mark pointer + executable_jit*: pointer ## Contains a pointer to a compiled jit code + +## The structure for passing out data via the pcre_callout_function. We use a +## structure so that new fields can be added on the end in future versions, +## without changing the API of the function, thereby allowing old clients to +## work without modification. type - TJitCallback* = proc(p: pointer): ptr Tjit_stack{.cdecl.} - -# Exported PCRE functions - -proc compile*(a2: cstring, a3: cint, a4: ptr cstring, a5: ptr cint, - a6: ptr char): ptr TPcre{.cdecl, importc: "pcre_compile", - pcreImport.} -proc compile2*(a2: cstring, a3: cint, a4: ptr cint, a5: ptr cstring, - a6: ptr cint, a7: ptr char): ptr TPcre{.cdecl, - importc: "pcre_compile2", pcreImport.} -proc config*(a2: cint, a3: pointer): cint{.cdecl, importc: "pcre_config", - pcreImport.} -proc copy_named_substring*(a2: ptr TPcre, a3: cstring, a4: ptr cint, a5: cint, - a6: cstring, a7: cstring, a8: cint): cint{.cdecl, - importc: "pcre_copy_named_substring", pcreImport.} -proc copy_substring*(a2: cstring, a3: ptr cint, a4: cint, a5: cint, - a6: cstring, - a7: cint): cint{.cdecl, importc: "pcre_copy_substring", - pcreImport.} -proc dfa_exec*(a2: ptr TPcre, a3: ptr TExtra, a4: cstring, a5: cint, - a6: cint, a7: cint, a8: ptr cint, a9: cint, a10: ptr cint, - a11: cint): cint{.cdecl, importc: "pcre_dfa_exec", - pcreImport.} -proc exec*(a2: ptr TPcre, a3: ptr TExtra, a4: cstring, a5: cint, a6: cint, - a7: cint, a8: ptr cint, a9: cint): cint {. - cdecl, importc: "pcre_exec", pcreImport.} -proc free_substring*(a2: cstring){.cdecl, importc: "pcre_free_substring", - pcreImport.} -proc free_substring_list*(a2: cstringArray){.cdecl, - importc: "pcre_free_substring_list", pcreImport.} -proc fullinfo*(a2: ptr TPcre, a3: ptr TExtra, a4: cint, a5: pointer): cint{. - cdecl, importc: "pcre_fullinfo", pcreImport.} -proc get_named_substring*(a2: ptr TPcre, a3: cstring, a4: ptr cint, a5: cint, - a6: cstring, a7: cstringArray): cint{.cdecl, - importc: "pcre_get_named_substring", pcreImport.} -proc get_stringnumber*(a2: ptr TPcre, a3: cstring): cint{.cdecl, - importc: "pcre_get_stringnumber", pcreImport.} -proc get_stringtable_entries*(a2: ptr TPcre, a3: cstring, a4: cstringArray, - a5: cstringArray): cint{.cdecl, - importc: "pcre_get_stringtable_entries", pcreImport.} -proc get_substring*(a2: cstring, a3: ptr cint, a4: cint, a5: cint, - a6: cstringArray): cint{.cdecl, - importc: "pcre_get_substring", pcreImport.} -proc get_substring_list*(a2: cstring, a3: ptr cint, a4: cint, - a5: ptr cstringArray): cint{.cdecl, - importc: "pcre_get_substring_list", pcreImport.} -proc maketables*(): ptr char{.cdecl, importc: "pcre_maketables", - pcreImport.} -proc refcount*(a2: ptr TPcre, a3: cint): cint{.cdecl, importc: "pcre_refcount", - pcreImport.} -proc study*(a2: ptr TPcre, a3: cint, a4: var cstring): ptr TExtra{.cdecl, - importc: "pcre_study", pcreImport.} -proc version*(): cstring{.cdecl, importc: "pcre_version", pcreImport.} + CalloutBlock* = object + version* : cint ## Identifies version of block + # ------------------------ Version 0 ------------------------------- + callout_number* : cint ## Number compiled into pattern + offset_vector* : ptr cint ## The offset vector + subject* : cstring ## The subject being matched + subject_length* : cint ## The length of the subject + start_match* : cint ## Offset to start of this match attempt + current_position*: cint ## Where we currently are in the subject + capture_top* : cint ## Max current capture + capture_last* : cint ## Most recently closed capture + callout_data* : pointer ## Data passed in with the call + # ------------------- Added for Version 1 -------------------------- + pattern_position*: cint ## Offset to next item in the pattern + next_item_length*: cint ## Length of next item in the pattern + # ------------------- Added for Version 2 -------------------------- + mark* : pointer ## Pointer to current mark or NULL + # ------------------------------------------------------------------ + + +## User defined callback which provides a stack just before the match starts. +type + JitCallback* = proc (a: pointer): ptr JitStack {.cdecl.} + + +when not defined(usePcreHeader): + when hostOS == "windows": + const pcreDll = "pcre.dll" + elif hostOS == "macosx": + const pcreDll = "libpcre(.3|.1|).dylib" + else: + const pcreDll = "libpcre.so(.3|.1|)" + {.push dynlib: pcreDll.} +else: + {.push header: "<pcre.h>".} + +{.push cdecl, importc: "pcre_$1".} + +# Exported PCRE functions + +proc compile*(pattern: cstring, + options: cint, + errptr: ptr cstring, + erroffset: ptr cint, + tableptr: pointer): ptr Pcre + +proc compile2*(pattern: cstring, + options: cint, + errorcodeptr: ptr cint, + errptr: ptr cstring, + erroffset: ptr cint, + tableptr: pointer): ptr Pcre + +proc config*(what: cint, + where: pointer): cint + +proc copy_named_substring*(code: ptr Pcre, + subject: cstring, + ovector: ptr cint, + stringcount: cint, + stringname: cstring, + buffer: cstring, + buffersize: cint): cint + +proc copy_substring*(subject: cstring, + ovector: ptr cint, + stringcount: cint, + stringnumber: cint, + buffer: cstring, + buffersize: cint): cint + +proc dfa_exec*(code: ptr Pcre, + extra: ptr ExtraData, + subject: cstring, + length: cint, + startoffset: cint, + options: cint, + ovector: ptr cint, + ovecsize: cint, + workspace: ptr cint, + wscount: cint): cint + +proc exec*(code: ptr Pcre, + extra: ptr ExtraData, + subject: cstring, + length: cint, + startoffset: cint, + options: cint, + ovector: ptr cint, + ovecsize: cint): cint + +proc jit_exec*(code: ptr Pcre, + extra: ptr ExtraData, + subject: cstring, + length: cint, + startoffset: cint, + options: cint, + ovector: ptr cint, + ovecsize: cint, + jstack: ptr JitStack): cint + +proc free_substring*(stringptr: cstring) + +proc free_substring_list*(stringptr: cstringArray) + +proc fullinfo*(code: ptr Pcre, + extra: ptr ExtraData, + what: cint, + where: pointer): cint + +proc get_named_substring*(code: ptr Pcre, + subject: cstring, + ovector: ptr cint, + stringcount: cint, + stringname: cstring, + stringptr: cstringArray): cint + +proc get_stringnumber*(code: ptr Pcre, + name: cstring): cint + +proc get_stringtable_entries*(code: ptr Pcre, + name: cstring, + first: cstringArray, + last: cstringArray): cint + +proc get_substring*(subject: cstring, + ovector: ptr cint, + stringcount: cint, + stringnumber: cint, + stringptr: cstringArray): cint + +proc get_substring_list*(subject: cstring, + ovector: ptr cint, + stringcount: cint, + listptr: ptr cstringArray): cint + +proc maketables*(): pointer + +proc refcount*(code: ptr Pcre, + adjust: cint): cint + +proc study*(code: ptr Pcre, + options: cint, + errptr: ptr cstring): ptr ExtraData + +proc free_study*(extra: ptr ExtraData) + +proc version*(): cstring # Utility functions for byte order swaps. -proc pattern_to_host_byte_order*(a2: ptr TPcre, a3: ptr TExtra, - a4: ptr char): cint{.cdecl, importc: "pcre_pattern_to_host_byte_order", - pcreImport.} +proc pattern_to_host_byte_order*(code: ptr Pcre, + extra: ptr ExtraData, + tables: pointer): cint # JIT compiler related functions. -proc jit_stack_alloc*(a2: cint, a3: cint): ptr Tjit_stack{.cdecl, - importc: "pcre_jit_stack_alloc", pcreImport.} -proc jit_stack_free*(a2: ptr Tjit_stack){.cdecl, importc: "pcre_jit_stack_free", - pcreImport.} -proc assign_jit_stack*(a2: ptr TExtra, a3: TJitCallback, a4: pointer){.cdecl, - importc: "pcre_assign_jit_stack", pcreImport.} +proc jit_stack_alloc*(startsize: cint, + maxsize: cint): ptr JitStack + +proc jit_stack_free*(stack: ptr JitStack) + +proc assign_jit_stack*(extra: ptr ExtraData, + callback: JitCallback, + data: pointer) + +proc jit_free_unused_memory*() + + +# There was an odd function with `var cstring` instead of `ptr` +proc study*(code: ptr Pcre, + options: cint, + errptr: var cstring): ptr ExtraData {.deprecated.} + +{.pop.} +{.pop.} + + +{.deprecated: [MAJOR: PCRE_MAJOR, MINOR: PCRE_MINOR, + PRERELEASE: PCRE_PRERELEASE, DATE: PCRE_DATE].} + +{.deprecated: [TPcre: Pcre, TJitStack: JitStack].} +type + PPcre* {.deprecated.} = ptr Pcre + PJitStack* {.deprecated.} = ptr JitStack -var - pcre_free*: proc (p: ptr TPcre) {.cdecl.} +{.deprecated: [TExtra: ExtraData].} +{.deprecated: [TCalloutBlock: CalloutBlock].} +{.deprecated: [TJitCallback: JitCallback].} diff --git a/tests/assert/tfailedassert.nim b/tests/assert/tfailedassert.nim index 729cdcac7..1e6764471 100644 --- a/tests/assert/tfailedassert.nim +++ b/tests/assert/tfailedassert.nim @@ -30,7 +30,7 @@ proc bar: int = # local overrides that are active only # in this proc onFailedAssert(msg): echo "WARNING: " & msg - + assert(false, "first assertion from bar") onFailedAssert(msg): diff --git a/tests/assign/moverload_asgn2.nim b/tests/assign/moverload_asgn2.nim new file mode 100644 index 000000000..6620adbeb --- /dev/null +++ b/tests/assign/moverload_asgn2.nim @@ -0,0 +1,10 @@ +type + Concrete* = object + a*, b*: string + rc*: int # refcount + +proc `=`(d: var Concrete; src: Concrete) = + shallowCopy(d.a, src.a) + shallowCopy(d.b, src.b) + dec d.rc + d.rc = src.rc + 1 diff --git a/tests/assign/toverload_asgn1.nim b/tests/assign/toverload_asgn1.nim new file mode 100644 index 000000000..dbc3a71c4 --- /dev/null +++ b/tests/assign/toverload_asgn1.nim @@ -0,0 +1,75 @@ +discard """ + output: '''Concrete '=' +Concrete '=' +Concrete '=' +Concrete '=' +Concrete '=' +GenericT[T] '=' int +GenericT[T] '=' float +GenericT[T] '=' float +GenericT[T] '=' float +GenericT[T] '=' string +GenericT[T] '=' int8 +GenericT[T] '=' bool +GenericT[T] '=' bool +GenericT[T] '=' bool +GenericT[T] '=' bool''' +""" + +import typetraits + +type + Concrete = object + a, b: string + +proc `=`(d: var Concrete; src: Concrete) = + shallowCopy(d.a, src.a) + shallowCopy(d.b, src.b) + echo "Concrete '='" + +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 + +type + GenericT[T] = object + a, b: T + +proc `=`[T](d: var GenericT[T]; src: GenericT[T]) = + shallowCopy(d.a, src.a) + shallowCopy(d.b, src.b) + echo "GenericT[T] '=' ", type(T).name + +var ag: GenericT[int] +var bg: GenericT[int] + +ag = bg + +var xg, yg: array[0..2, GenericT[float]] +var cAg, cBg: GenericT[string] + +var cATupg, cBTupg: tuple[x: int, ha: GenericT[int8]] + +xg = yg +cAg = cBg +cATupg = cBTupg + +var caSeqg, cbSeqg: seq[GenericT[bool]] +newSeq(cbSeqg, 4) +caSeqg = cbSeqg + +when false: + type + Foo = object + case b: bool + of false: xx: GenericT[int] + of true: yy: bool + + var + a, b: Foo + a = b diff --git a/tests/assign/toverload_asgn2.nim b/tests/assign/toverload_asgn2.nim new file mode 100644 index 000000000..243c90494 --- /dev/null +++ b/tests/assign/toverload_asgn2.nim @@ -0,0 +1,22 @@ +discard """ + output: '''i value 88 +2aa''' +""" + +import moverload_asgn2 + +proc passAround(i: int): Concrete = + echo "i value ", i + result = Concrete(a: "aa", b: "bb", rc: 0) + +proc main = + let + i = 88 + v = passAround(i) + z = v.a + var + x: Concrete + x = v + echo x.rc, z # 2aa + +main() diff --git a/tests/async/tasynctry.nim b/tests/async/tasynctry.nim index 99433b9d8..f77198e2e 100644 --- a/tests/async/tasynctry.nim +++ b/tests/async/tasynctry.nim @@ -82,6 +82,15 @@ proc test3(): Future[int] {.async.} = result = 2 return +proc test4(): Future[int] {.async.} = + try: + discard await foo() + raise newException(ValueError, "Test4") + except OSError: + result = 1 + except: + result = 2 + var x = test() assert x.read @@ -90,3 +99,6 @@ assert x.read var y = test3() assert y.read == 2 + +y = test4() +assert y.read == 2 diff --git a/tests/async/tasynctry2.nim b/tests/async/tasynctry2.nim new file mode 100644 index 000000000..444a058be --- /dev/null +++ b/tests/async/tasynctry2.nim @@ -0,0 +1,16 @@ +discard """ + file: "tasynctry2.nim" + errormsg: "\'yield\' cannot be used within \'try\' in a non-inlined iterator" + line: 15 +""" +import asyncdispatch + +proc foo(): Future[bool] {.async.} = discard + +proc test5(): Future[int] {.async.} = + try: + discard await foo() + raise newException(ValueError, "Test5") + except: + discard await foo() + result = 0 diff --git a/tests/bind/tnicerrorforsymchoice.nim b/tests/bind/tnicerrorforsymchoice.nim index bf6d92927..5145fdcff 100644 --- a/tests/bind/tnicerrorforsymchoice.nim +++ b/tests/bind/tnicerrorforsymchoice.nim @@ -1,17 +1,17 @@ discard """ line: 18 - errormsg: "type mismatch: got (proc (TScgi) | proc (AsyncSocket, StringTableRef, string)" + errormsg: "type mismatch: got (proc (s: TScgi) | proc (client: AsyncSocket, headers: StringTableRef, input: string){.gcsafe, locks: 0.}" """ #bug #442 import scgi, sockets, asyncio, strtabs proc handleSCGIRequest[TScgi: ScgiState | AsyncScgiState](s: TScgi) = discard -proc handleSCGIRequest(client: AsyncSocket, headers: StringTableRef, +proc handleSCGIRequest(client: AsyncSocket, headers: StringTableRef, input: string) = discard -proc test(handle: proc (client: AsyncSocket, headers: StringTableRef, +proc test(handle: proc (client: AsyncSocket, headers: StringTableRef, input: string), b: int) = discard diff --git a/tests/ccgbugs/tarray_equality.nim b/tests/ccgbugs/tarray_equality.nim new file mode 100644 index 000000000..66a953439 --- /dev/null +++ b/tests/ccgbugs/tarray_equality.nim @@ -0,0 +1,15 @@ +discard """ + output: '''true +true''' +""" + +# bug #2489 + +let a = [1] +let b = [1] +echo a == b + +# bug #2498 +var x: array[0, int] +var y: array[0, int] +echo x == y diff --git a/tests/ccgbugs/tpartialcs.nim b/tests/ccgbugs/tpartialcs.nim new file mode 100644 index 000000000..12ff65c37 --- /dev/null +++ b/tests/ccgbugs/tpartialcs.nim @@ -0,0 +1,20 @@ + +# bug #2551 + +type Tup = tuple + A, a: int + +type Obj = object + A, a: int + +var x: Tup # This works. +var y: Obj # This doesn't. + +# bug #2212 + +proc f() = + let + p = 1.0 + P = 0.25 + 0.5 + +f() diff --git a/tests/closure/tinvalidclosure.nim b/tests/closure/tinvalidclosure.nim index 18968a6c6..c9136a736 100644 --- a/tests/closure/tinvalidclosure.nim +++ b/tests/closure/tinvalidclosure.nim @@ -1,6 +1,6 @@ discard """ line: 12 - errormsg: "type mismatch: got (proc (int){.closure, gcsafe, locks: 0.})" + errormsg: "type mismatch: got (proc (x: int){.closure, gcsafe, locks: 0.})" """ proc ugh[T](x: T) {.closure.} = diff --git a/tests/collections/tcounttable.nim b/tests/collections/tcounttable.nim new file mode 100644 index 000000000..ebbb1c8e5 --- /dev/null +++ b/tests/collections/tcounttable.nim @@ -0,0 +1,19 @@ +discard """ + output: "And we get here" +""" + +# bug #2625 + +const s_len = 32 + +import tables +var substr_counts: CountTable[string] = initCountTable[string]() +var my_string = "Hello, this is sadly broken for strings over 64 characters. Note that it *does* appear to work for short strings." +for i in 0..(my_string.len - s_len): + let s = my_string[i..i+s_len-1] + substr_counts[s] = 1 + # substr_counts[s] = substr_counts[s] + 1 # Also breaks, + 2 as well, etc. + # substr_counts.inc(s) # This works + #echo "Iteration ", i + +echo "And we get here" diff --git a/tests/collections/tsets.nim b/tests/collections/tsets.nim index 656c5b3f2..a5bbe8dbd 100644 --- a/tests/collections/tsets.nim +++ b/tests/collections/tsets.nim @@ -1,17 +1,37 @@ -discard """ - output: '''true -true''' -""" - import sets -var - a = initSet[int]() - b = initSet[int]() - c = initSet[string]() -for i in 0..5: a.incl(i) -for i in 1..6: b.incl(i) -for i in 0..5: c.incl($i) +block setEquality: + var + a = initSet[int]() + b = initSet[int]() + c = initSet[string]() + + for i in 0..5: a.incl(i) + for i in 1..6: b.incl(i) + for i in 0..5: c.incl($i) + + doAssert map(a, proc(x: int): int = x + 1) == b + doAssert map(a, proc(x: int): string = $x) == c + + +block setsContainingTuples: + var set = initSet[tuple[i: int, i64: int64, f: float]]() + set.incl( (i: 123, i64: 123'i64, f: 3.14) ) + doAssert set.contains( (i: 123, i64: 123'i64, f: 3.14) ) + doAssert( not set.contains( (i: 456, i64: 789'i64, f: 2.78) ) ) + + +block setWithTuplesWithSeqs: + var s = initSet[tuple[s: seq[int]]]() + s.incl( (s: @[1, 2, 3]) ) + doAssert s.contains( (s: @[1, 2, 3]) ) + doAssert( not s.contains((s: @[4, 5, 6])) ) + + +block setWithSequences: + var s = initSet[seq[int]]() + s.incl( @[1, 2, 3] ) + doAssert s.contains(@[1, 2, 3]) + doAssert( not s.contains(@[4, 5, 6]) ) + -echo map(a, proc(x: int): int = x + 1) == b -echo map(a, proc(x: int): string = $x) == c diff --git a/tests/collections/ttables.nim b/tests/collections/ttables.nim index 3a923610e..a10606843 100644 --- a/tests/collections/ttables.nim +++ b/tests/collections/ttables.nim @@ -22,15 +22,15 @@ const "---00": 346677844, "0": 34404, "1": 344004, - "10": 34484, + "10": 34484, "11": 34474, "12": 789, "19": 34464, - "2": 344774, "20": 34454, + "2": 344774, "20": 34454, "3": 342244, "30": 34141244, "34": 123456, "4": 3412344, "40": 344114, - "5": 341232144, "50": 344490, + "5": 341232144, "50": 344490, "6": 34214544, "60": 344491, "7": 3434544, "70": 344492, "8": 344544, "80": 344497, @@ -46,7 +46,7 @@ block tableTest1: for x in 0..1: for y in 0..1: assert t[(x,y)] == $x & $y - assert($t == + assert($t == "{(x: 0, y: 1): 01, (x: 0, y: 0): 00, (x: 1, y: 0): 10, (x: 1, y: 1): 11}") block tableTest2: @@ -55,14 +55,16 @@ block tableTest2: t["111"] = 1.000043 t["123"] = 1.23 t.del("111") - + t["012"] = 67.9 t["123"] = 1.5 # test overwriting - + assert t["123"] == 1.5 assert t["111"] == 0.0 # deleted assert(not hasKey(t, "111")) - + assert "123" in t + assert("111" notin t) + for key, val in items(data): t[key] = val.toFloat for key, val in items(data): assert t[key] == val.toFloat diff --git a/tests/collections/ttablesref.nim b/tests/collections/ttablesref.nim index 16b0d831e..0b641ebc7 100644 --- a/tests/collections/ttablesref.nim +++ b/tests/collections/ttablesref.nim @@ -22,15 +22,15 @@ const "---00": 346677844, "0": 34404, "1": 344004, - "10": 34484, + "10": 34484, "11": 34474, "12": 789, "19": 34464, - "2": 344774, "20": 34454, + "2": 344774, "20": 34454, "3": 342244, "30": 34141244, "34": 123456, "4": 3412344, "40": 344114, - "5": 341232144, "50": 344490, + "5": 341232144, "50": 344490, "6": 34214544, "60": 344491, "7": 3434544, "70": 344492, "8": 344544, "80": 344497, @@ -46,7 +46,7 @@ block tableTest1: for x in 0..1: for y in 0..1: assert t[(x,y)] == $x & $y - assert($t == + assert($t == "{(x: 0, y: 1): 01, (x: 0, y: 0): 00, (x: 1, y: 0): 10, (x: 1, y: 1): 11}") block tableTest2: @@ -55,17 +55,19 @@ block tableTest2: t["111"] = 1.000043 t["123"] = 1.23 t.del("111") - + t["012"] = 67.9 t["123"] = 1.5 # test overwriting - + assert t["123"] == 1.5 assert t["111"] == 0.0 # deleted + assert "123" in t assert(not hasKey(t, "111")) - + assert "111" notin t + for key, val in items(data): t[key] = val.toFloat for key, val in items(data): assert t[key] == val.toFloat - + block orderedTableTest1: var t = newOrderedTable[string, int](2) diff --git a/tests/cpp/tcppraise.nim b/tests/cpp/tcppraise.nim new file mode 100644 index 000000000..a9ea8e6ce --- /dev/null +++ b/tests/cpp/tcppraise.nim @@ -0,0 +1,17 @@ +discard """ + cmd: "nim cpp $file" + output: '''foo +bar +Need odd and >= 3 digits## +baz''' +""" + +# bug #1888 +echo "foo" +try: + echo "bar" + raise newException(ValueError, "Need odd and >= 3 digits") +# echo "baz" +except ValueError: + echo getCurrentExceptionMsg(), "##" +echo "baz" diff --git a/tests/cpp/tget_subsystem.nim b/tests/cpp/tget_subsystem.nim new file mode 100644 index 000000000..461914739 --- /dev/null +++ b/tests/cpp/tget_subsystem.nim @@ -0,0 +1,23 @@ +discard """ + cmd: "nim cpp $file" +""" + +{.emit: """ + +namespace System { + struct Input {}; +} + +struct SystemManager { + template <class T> + static T* getSubsystem() { return new T; } +}; + +""".} + +type Input {.importcpp: "System::Input".} = object +proc getSubsystem*[T](): ptr T {. + importcpp: "SystemManager::getSubsystem<'*0>()", nodecl.} + +let input: ptr Input = getSubsystem[Input]() + diff --git a/tests/cpp/tvector_iterator.nim b/tests/cpp/tvector_iterator.nim new file mode 100644 index 000000000..cb5ab33af --- /dev/null +++ b/tests/cpp/tvector_iterator.nim @@ -0,0 +1,19 @@ +discard """ + cmd: "nim cpp $file" +""" + +{.emit: """ + +template <class T> +struct Vector { + struct Iterator {}; +}; + +""".} + +type + Vector {.importcpp: "Vector".} [T] = object + VectorIterator {.importcpp: "Vector<'0>::Iterator".} [T] = object + +var x: VectorIterator[void] + diff --git a/tests/cpp/tvectorseq.nim b/tests/cpp/tvectorseq.nim new file mode 100644 index 000000000..6eb5dc9e4 --- /dev/null +++ b/tests/cpp/tvectorseq.nim @@ -0,0 +1,38 @@ +discard """ + output: '''(x: 1.0) +(x: 0.0)''' + cmd: "nim cpp $file" + disabled: "true" +""" + +# This cannot work yet because we omit type information for importcpp'ed types. +# Fixing this is not hard, but also requires fixing Urhonimo. + +# bug #2536 + +{.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} + +# hack around another codegen issue: Generics are attached to where they came +# from: +proc `$!`(v: seq[Vector3]): string = "(x: " & $v[0].x & ")" + +proc vec3List*(): seq[Vector3] = + let s = @[constructVector3(cfloat(1))] + echo($!s) + result = s + echo($!result) + +let f = vec3List() +#echo($!f) diff --git a/tests/destructor/tdestructor.nim b/tests/destructor/tdestructor.nim index cbaba3154..639dba941 100644 --- a/tests/destructor/tdestructor.nim +++ b/tests/destructor/tdestructor.nim @@ -40,7 +40,7 @@ type x: A y: B z: C - + TObjKind = enum A, B, C, D TCaseObj = object @@ -57,14 +57,14 @@ type q: TMyGeneric3[TMyObj, int, int] r: string -proc destroy(o: var TMyObj) {.override.} = +proc `=destroy`(o: var TMyObj) = if o.p != nil: dealloc o.p echo "myobj destroyed" -proc destroy(o: var TMyGeneric1) {.override.} = +proc `=destroy`(o: var TMyGeneric1) = echo "mygeneric1 destroyed" -proc destroy[A, B](o: var TMyGeneric2[A, B]) {.override.} = +proc `=destroy`[A, B](o: var TMyGeneric2[A, B]) = echo "mygeneric2 destroyed" proc open: TMyObj = @@ -83,12 +83,12 @@ proc mygeneric1() = proc mygeneric2[T](val: T) = var a = open() - + var b = TMyGeneric2[int, T](x: 10, y: val) echo "mygeneric2 constructed" var c = TMyGeneric3[int, int, string](x: 10, y: 20, z: "test") - + proc mygeneric3 = var x = TMyGeneric3[int, string, TMyGeneric1[int]]( x: 10, y: "test", z: TMyGeneric1[int](x: 10)) @@ -111,11 +111,11 @@ proc caseobj = block: echo "----" var o1 = TCaseObj(kind: A, x: TMyGeneric1[int](x: 10)) - + block: echo "----" var o2 = TCaseObj(kind: B, y: open()) - + block: echo "----" var o3 = TCaseObj(kind: D, innerKind: B, r: "test", diff --git a/tests/destructor/tdestructor2.nim b/tests/destructor/tdestructor2.nim index 6f966d861..34fa466af 100644 --- a/tests/destructor/tdestructor2.nim +++ b/tests/destructor/tdestructor2.nim @@ -5,14 +5,14 @@ discard """ {.experimental.} -type +type TMyObj = object x, y: int p: pointer - -proc destroy(o: var TMyObj) {.override.} = + +proc `=destroy`(o: var TMyObj) = if o.p != nil: dealloc o.p - + proc open: TMyObj = result = TMyObj(x: 1, y: 2, p: alloc(3)) diff --git a/tests/distinct/tdistinct_consts.nim b/tests/distinct/tdistinct_consts.nim new file mode 100644 index 000000000..4f6ced2d2 --- /dev/null +++ b/tests/distinct/tdistinct_consts.nim @@ -0,0 +1,20 @@ + +# bug #2641 + +type MyChar = distinct char +const c:MyChar = MyChar('a') + +type MyBool = distinct bool +const b:MyBool = MyBool(true) + +type MyBoolSet = distinct set[bool] +const bs:MyBoolSet = MyBoolSet({true}) + +type MyCharSet= distinct set[char] +const cs:MyCharSet = MyCharSet({'a'}) + +type MyBoolSeq = distinct seq[bool] +const bseq:MyBoolSeq = MyBoolSeq(@[true, false]) + +type MyBoolArr = distinct array[3, bool] +const barr:MyBoolArr = MyBoolArr([true, false, true]) diff --git a/tests/effects/tgcsafe.nim b/tests/effects/tgcsafe.nim index 0d5109439..d146794b6 100644 --- a/tests/effects/tgcsafe.nim +++ b/tests/effects/tgcsafe.nim @@ -1,5 +1,5 @@ discard """ - line: 16 + line: 17 errormsg: "'mainUnsafe' is not GC-safe" cmd: "nim $target --hints:on --threads:on $options $file" """ diff --git a/tests/exception/texceptionbreak.nim b/tests/exception/texceptionbreak.nim index 76e986787..00dd8ed9f 100644 --- a/tests/exception/texceptionbreak.nim +++ b/tests/exception/texceptionbreak.nim @@ -5,20 +5,20 @@ discard """ # First variety try: - raise newException(EOS, "Problem") -except EOS: + raise newException(OSError, "Problem") +except OSError: for y in [1, 2, 3]: discard try: discard - except EOS: + except OSError: discard echo "1" # Second Variety try: - raise newException(EOS, "Problem") -except EOS: + raise newException(OSError, "Problem") +except OSError: for y in [1, 2, 3]: discard for y in [1, 2, 3]: @@ -28,8 +28,8 @@ echo "2" # Third Variety try: - raise newException(EOS, "Problem") -except EOS: + raise newException(OSError, "Problem") +except OSError: block: break @@ -38,8 +38,8 @@ echo "3" # Fourth Variety block: try: - raise newException(EOS, "Problem") - except EOS: + raise newException(OSError, "Problem") + except OSError: break -echo "4" \ No newline at end of file +echo "4" diff --git a/tests/exception/texceptions.nim b/tests/exception/texceptions.nim index 69b2d0f6a..bdf338599 100644 --- a/tests/exception/texceptions.nim +++ b/tests/exception/texceptions.nim @@ -35,9 +35,9 @@ echo "" proc reraise_in_except = try: echo "BEFORE" - raise newException(EIO, "") + raise newException(IOError, "") - except EIO: + except IOError: echo "EXCEPT" raise @@ -52,7 +52,7 @@ echo "" proc return_in_except = try: echo "BEFORE" - raise newException(EIO, "") + raise newException(IOError, "") except: echo "EXCEPT" diff --git a/tests/exception/texcsub.nim b/tests/exception/texcsub.nim index 3dba357f9..02125d2c0 100644 --- a/tests/exception/texcsub.nim +++ b/tests/exception/texcsub.nim @@ -5,12 +5,12 @@ discard """ # Test inheritance for exception matching: try: - raise newException(EOS, "dummy message") -except E_Base: + raise newException(OSError, "dummy message") +except Exception: echo "caught!" -except: +except: echo "wtf!?" - + #OUT caught! diff --git a/tests/exception/tfinally4.nim b/tests/exception/tfinally4.nim index 05c57c4f5..3aa707ff6 100644 --- a/tests/exception/tfinally4.nim +++ b/tests/exception/tfinally4.nim @@ -8,19 +8,19 @@ discard """ var raiseEx = true var returnA = true var returnB = false - -proc main: int = + +proc main: int = try: #A try: #B if raiseEx: - raise newException(EOS, "") + raise newException(OSError, "") return 3 finally: #B echo "B1" if returnB: return 2 echo "B2" - except EOS: #A + except OSError: #A echo "catch" finally: #A echo "A1" diff --git a/tests/exception/tnestedreturn.nim b/tests/exception/tnestedreturn.nim index 591638f0e..1480764f1 100644 --- a/tests/exception/tnestedreturn.nim +++ b/tests/exception/tnestedreturn.nim @@ -7,7 +7,7 @@ discard """ proc test1() = - finally: echo "A" + defer: echo "A" try: raise newException(OSError, "Problem") @@ -19,7 +19,7 @@ test1() proc test2() = - finally: echo "B" + defer: echo "B" try: return diff --git a/tests/exception/tonraise.nim b/tests/exception/tonraise.nim index 1a555dd94..a155f0b8e 100644 --- a/tests/exception/tonraise.nim +++ b/tests/exception/tonraise.nim @@ -4,8 +4,8 @@ success''' """ type - ESomething = object of E_Base - ESomeOtherErr = object of E_Base + ESomething = object of Exception + ESomeOtherErr = object of Exception proc genErrors(s: string) = if s == "error!": @@ -17,14 +17,14 @@ proc foo() = var i = 0 try: inc i - onRaise(proc (e: ref E_Base): bool = + onRaise(proc (e: ref Exception): bool = echo "i: ", i) genErrors("errssor!") except ESomething: echo("ESomething happened") except: echo("Some other error happened") - + # test that raise handler is gone: try: genErrors("error!") diff --git a/tests/exception/treraise.nim b/tests/exception/treraise.nim index cbd0b5f8a..b2a11d34f 100644 --- a/tests/exception/treraise.nim +++ b/tests/exception/treraise.nim @@ -4,8 +4,8 @@ discard """ exitcode: "1" """ type - ESomething = object of E_Base - ESomeOtherErr = object of E_Base + ESomething = object of Exception + ESomeOtherErr = object of Exception proc genErrors(s: string) = if s == "error!": diff --git a/tests/generics/twrong_generic_object.nim b/tests/generics/twrong_generic_object.nim new file mode 100644 index 000000000..00d90c55e --- /dev/null +++ b/tests/generics/twrong_generic_object.nim @@ -0,0 +1,21 @@ +discard """ + errormsg: "cannot instantiate: 'GenericNodeObj'" + line: 21 +""" +# bug #2509 +type + GenericNodeObj[T] = ref object + obj: T + + Node* = ref object + children*: seq[Node] + parent*: Node + + nodeObj*: GenericNodeObj # [int] + +proc newNode*(nodeObj: GenericNodeObj): Node = + result = Node(nodeObj: nodeObj) + newSeq(result.children, 10) + +var genericObj = GenericNodeObj[int]() +var myNode = newNode(genericObj) diff --git a/tests/js/test2.nim b/tests/js/test2.nim index 5a734358c..1a42fbfda 100644 --- a/tests/js/test2.nim +++ b/tests/js/test2.nim @@ -1,6 +1,7 @@ discard """ output: '''foo -js 3.14''' +js 3.14 +7''' """ # This file tests the JavaScript generator @@ -20,3 +21,11 @@ else: proc foo(val: float): string = "js " & $val echo foo(3.14) + +# #2495 +type C = concept x + +proc test(x: C, T: typedesc): T = + cast[T](x) + +echo 7.test(int8) diff --git a/tests/js/tstringitems.nim b/tests/js/tstringitems.nim new file mode 100644 index 000000000..f4ea02fec --- /dev/null +++ b/tests/js/tstringitems.nim @@ -0,0 +1,24 @@ +discard """ + output: '''Hello +Hello''' +""" + +# bug #2581 + +const someVars = [ "Hello" ] +var someVars2 = [ "Hello" ] + +proc getSomeVar: string = + for i in someVars: + if i == "Hello": + result = i + break + +proc getSomeVar2: string = + for i in someVars2: + if i == "Hello": + result = i + break + +echo getSomeVar() +echo getSomeVar2() diff --git a/tests/js/tunittests.nim b/tests/js/tunittests.nim index af38cd9b9..8a264a5e0 100644 --- a/tests/js/tunittests.nim +++ b/tests/js/tunittests.nim @@ -1,3 +1,10 @@ +discard """ + disabled: "true" +""" + +# Unittest uses lambdalifting at compile-time which we disable for the JS +# codegen! So this cannot and will not work for quite some time. + import unittest suite "Bacon": diff --git a/tests/macros/tlexerex.nim b/tests/macros/tlexerex.nim new file mode 100644 index 000000000..d348a4bcc --- /dev/null +++ b/tests/macros/tlexerex.nim @@ -0,0 +1,16 @@ + +import macros + +macro match*(s: cstring|string; pos: int; sections: untyped): untyped = + for sec in sections.children: + expectKind sec, nnkOfBranch + expectLen sec, 2 + result = newStmtList() + +when isMainModule: + var input = "the input" + var pos = 0 + match input, pos: + of r"[a-zA-Z_]\w+": echo "an identifier" + of r"\d+": echo "an integer" + of r".": echo "something else" diff --git a/tests/macros/treturnsempty.nim b/tests/macros/treturnsempty.nim new file mode 100644 index 000000000..7af26a747 --- /dev/null +++ b/tests/macros/treturnsempty.nim @@ -0,0 +1,12 @@ +discard """ + errormsg: "type mismatch" + line: 11 +""" +# bug #2372 +macro foo(dummy: int): stmt = + discard + +proc takeStr(s: string) = echo s + +takeStr foo(12) + diff --git a/tests/macros/ttryparseexpr.nim b/tests/macros/ttryparseexpr.nim index af932eb7d..c7bbc8e5b 100644 --- a/tests/macros/ttryparseexpr.nim +++ b/tests/macros/ttryparseexpr.nim @@ -15,5 +15,6 @@ const valid = 45 a = test("foo&&") b = test("valid") + c = test("\"") # bug #2504 echo a, " ", b diff --git a/tests/macros/typesapi2.nim b/tests/macros/typesapi2.nim index 016295ba4..2e59d2154 100644 --- a/tests/macros/typesapi2.nim +++ b/tests/macros/typesapi2.nim @@ -1,4 +1,4 @@ -# tests to see if a symbol returned from macros.getType() can +# tests to see if a symbol returned from macros.getType() can # be used as a type import macros @@ -20,7 +20,7 @@ static: assert iii is TestFN proc foo11 : testTypesym(void) = echo "HI!" -static: assert foo11 is proc():void +static: assert foo11 is (proc():void {.nimcall.}) var sss: testTypesym(seq[int]) static: assert sss is seq[int] diff --git a/tests/manyloc/standalone/panicoverride.nim b/tests/manyloc/standalone/panicoverride.nim index efd2b21f9..d9b3f4388 100644 --- a/tests/manyloc/standalone/panicoverride.nim +++ b/tests/manyloc/standalone/panicoverride.nim @@ -7,13 +7,13 @@ proc exit(code: int) {.importc, header: "<stdlib.h>", cdecl.} proc rawoutput(s: string) = printf("%s\n", s) -proc panic(s: string) = +proc panic(s: string) {.noreturn.} = rawoutput(s) exit(1) # Alternatively we also could implement these 2 here: # -# template sysFatal(exceptn: typeDesc, message: string) -# template sysFatal(exceptn: typeDesc, message, arg: string) +# proc sysFatal(exceptn: typeDesc, message: string) {.noReturn.} +# proc sysFatal(exceptn: typeDesc, message, arg: string) {.noReturn.} {.pop.} diff --git a/tests/metatype/tautoproc.nim b/tests/metatype/tautoproc.nim index 562f508fc..ef5377096 100644 --- a/tests/metatype/tautoproc.nim +++ b/tests/metatype/tautoproc.nim @@ -1,11 +1,13 @@ discard """ - errormsg: "expression 'generate(builder)' has no type (or is ambiguous)" + output: "empty" """ # bug #898 +import typetraits + proc measureTime(e: auto) = - discard + echo e.type.name proc generate(a: int): void = discard diff --git a/tests/misc/tcharinc.nim b/tests/misc/tcharinc.nim new file mode 100644 index 000000000..1b5d19c18 --- /dev/null +++ b/tests/misc/tcharinc.nim @@ -0,0 +1,10 @@ +discard """ + output: "1" +""" + +var c = '\0' +while true: + if c == '\xFF': break + inc c + +echo "1" diff --git a/tests/misc/tgtk.nim b/tests/misc/tgtk.nim deleted file mode 100644 index 82227689d..000000000 --- a/tests/misc/tgtk.nim +++ /dev/null @@ -1,53 +0,0 @@ -discard """ - disabled: true -""" -import - gtk2, glib2, atk, gdk2, gdk2pixbuf, libglade2, pango, - pangoutils - -proc hello(widget: PWidget, data: pointer) {.cdecl.} = - write(stdout, "Hello World\n") - -proc delete_event(widget: PWidget, event: PEvent, - data: pointer): bool {.cdecl.} = - # If you return FALSE in the "delete_event" signal handler, - # GTK will emit the "destroy" signal. Returning TRUE means - # you don't want the window to be destroyed. - # This is useful for popping up 'are you sure you want to quit?' - # type dialogs. - write(stdout, "delete event occurred\n") - # Change TRUE to FALSE and the main window will be destroyed with - # a "delete_event". - return false - -# Another callback -proc mydestroy(widget: PWidget, data: pointer) {.cdecl.} = - gtk2.main_quit() - -proc mymain() = - # GtkWidget is the storage type for widgets - gtk2.nimrod_init() - var window = window_new(gtk2.WINDOW_TOPLEVEL) - discard g_signal_connect(window, "delete_event", - Gcallback(delete_event), nil) - discard g_signal_connect(window, "destroy", Gcallback(mydestroy), nil) - # Sets the border width of the window. - set_border_width(window, 10) - - # Creates a new button with the label "Hello World". - var button = button_new("Hello World") - - discard g_signal_connect(button, "clicked", Gcallback(hello), nil) - - # This packs the button into the window (a gtk container). - add(window, button) - - # The final step is to display this newly created widget. - show(button) - - # and the window - show(window) - - gtk2.main() - -mymain() diff --git a/tests/misc/tlibs.nim b/tests/misc/tlibs.nim deleted file mode 100644 index e7a02c7fd..000000000 --- a/tests/misc/tlibs.nim +++ /dev/null @@ -1,28 +0,0 @@ -discard """ - disabled: true -""" - -# Test wether the bindings at least compile... - -import - unicode, cgi, terminal, libcurl, - parsexml, parseopt, parsecfg, - osproc, complex, - sdl, smpeg, sdl_gfx, sdl_net, sdl_mixer, sdl_ttf, - sdl_image, sdl_mixer_nosmpeg, - cursorfont, xatom, xf86vmode, xkb, xrandr, xshm, xvlib, keysym, xcms, xi, - xkblib, xrender, xutil, x, xf86dga, xinerama, xlib, xresource, xv, - gtk2, glib2, pango, gdk2, - cairowin32, cairoxlib, - odbcsql, - gl, glut, glu, glx, glext, wingl, - lua, lualib, lauxlib, mysql, sqlite3, python, tcl, - db_postgres, db_mysql, db_sqlite, ropes, sockets, browsers, httpserver, - httpclient, parseutils, unidecode, xmldom, xmldomparser, xmltree, xmlparser, - htmlparser, re, graphics, colors, pegs, subexes, dialogs - -when defined(linux): - import - zlib, zipfiles - -writeln(stdout, "test compilation of binding modules") diff --git a/tests/misc/tnewlibs.nim b/tests/misc/tnewlibs.nim deleted file mode 100644 index 3b74a9b63..000000000 --- a/tests/misc/tnewlibs.nim +++ /dev/null @@ -1,21 +0,0 @@ -discard """ - disabled: true -""" - -# Test wether the bindings at least compile... - -import - tcl, - sdl, smpeg, sdl_gfx, sdl_net, sdl_mixer, sdl_ttf, - sdl_image, sdl_mixer_nosmpeg, - gtk2, glib2, pango, gdk2, - unicode, cgi, terminal, libcurl, - parsexml, parseopt, parsecfg, - osproc, - cairowin32, cairoxlib, - gl, glut, glu, glx, glext, wingl, - lua, lualib, lauxlib, mysql, sqlite3, db_mongo, md5, asyncio, mimetypes, - cookies, events, ftpclient, scgi, irc - - -writeln(stdout, "test compilation of binding modules") diff --git a/tests/misc/tunsigned64mod.nim b/tests/misc/tunsigned64mod.nim index 9ae0d535a..3007405a2 100644 --- a/tests/misc/tunsigned64mod.nim +++ b/tests/misc/tunsigned64mod.nim @@ -10,3 +10,17 @@ let t1 = v1 mod 2 # works let t2 = 7'u64 mod 2'u64 # works let t3 = v2 mod 2'u64 # Error: invalid type: 'range 0..1(uint64) let t4 = (v2 mod 2'u64).uint64 # works + +# bug #2550 + +var x: uint # doesn't work +echo x mod 2 == 0 + +var y: uint64 # doesn't work +echo y mod 2 == 0 + +var z: uint32 # works +echo z mod 2 == 0 + +var a: int # works +echo a mod 2 == 0 diff --git a/tests/misc/tunsignedinc.nim b/tests/misc/tunsignedinc.nim new file mode 100644 index 000000000..95622156f --- /dev/null +++ b/tests/misc/tunsignedinc.nim @@ -0,0 +1,14 @@ +discard """ + output: '''253''' +""" + +# bug #2427 + +import unsigned + +var x = 0'u8 +dec x # OverflowError +x -= 1 # OverflowError +x = x - 1 # No error + +echo x diff --git a/tests/objects/tillegal_recursion.nim b/tests/objects/tillegal_recursion.nim new file mode 100644 index 000000000..171a04f87 --- /dev/null +++ b/tests/objects/tillegal_recursion.nim @@ -0,0 +1,7 @@ +discard """ + errormsg: "illegal recursion in type 'object'" + line: 7 +""" +# bug #1691 +type + Foo = ref object of Foo diff --git a/tests/objects/tobjloop.nim b/tests/objects/tobjloop.nim new file mode 100644 index 000000000..9fea1e2fb --- /dev/null +++ b/tests/objects/tobjloop.nim @@ -0,0 +1,15 @@ +discard """ + output: "is Nil false" +""" +# bug #1658 + +type + Loop* = ref object + onBeforeSelect*: proc (L: Loop) + +var L: Loop +new L +L.onBeforeSelect = proc (bar: Loop) = + echo "is Nil ", bar.isNil + +L.onBeforeSelect(L) diff --git a/tests/objects/trefobjsyntax2.nim b/tests/objects/trefobjsyntax2.nim new file mode 100644 index 000000000..8ee209cc7 --- /dev/null +++ b/tests/objects/trefobjsyntax2.nim @@ -0,0 +1,19 @@ +# bug #2508 + +type + GenericNodeObj[T] = ref object + obj: T + + Node* = ref object + children*: seq[Node] + parent*: Node + + nodeObj*: GenericNodeObj[int] + +proc newNode*(nodeObj: GenericNodeObj): Node = + result = Node(nodeObj: nodeObj) + newSeq(result.children, 10) + +var genericObj = GenericNodeObj[int]() + +var myNode = newNode(genericObj) diff --git a/tests/overload/tspec.nim b/tests/overload/tspec.nim index 685df503a..f2002a390 100644 --- a/tests/overload/tspec.nim +++ b/tests/overload/tspec.nim @@ -11,7 +11,10 @@ ref T 123 2 1 -@[123, 2, 1]''' +@[123, 2, 1] +Called! +merge with var +merge no var''' """ # Things that's even in the spec now! @@ -79,3 +82,37 @@ proc takeV[T](a: varargs[T]) = takeV([123, 2, 1]) # takeV's T is "int", not "array of int" echo(@[123, 2, 1]) + +# bug #2600 + +type + FutureBase* = ref object of RootObj ## Untyped future. + + Future*[T] = ref object of FutureBase ## Typed future. + value: T ## Stored value + + FutureVar*[T] = distinct Future[T] + +proc newFuture*[T](): Future[T] = + new(result) + +proc newFutureVar*[T](): FutureVar[T] = + result = FutureVar[T](newFuture[T]()) + +proc mget*[T](future: FutureVar[T]): var T = + Future[T](future).value + +proc reset*[T](future: FutureVar[T]) = + echo "Called!" + +proc merge[T](x: Future[T]) = echo "merge no var" +proc merge[T](x: var Future[T]) = echo "merge with var" + +when true: + var foo = newFutureVar[string]() + foo.mget() = "" + foo.mget.add("Foobar") + foo.reset() + var bar = newFuture[int]() + bar.merge # merge with var + merge(newFuture[int]()) # merge no var diff --git a/tests/overload/tstmtoverload.nim b/tests/overload/tstmtoverload.nim new file mode 100644 index 000000000..f1944b637 --- /dev/null +++ b/tests/overload/tstmtoverload.nim @@ -0,0 +1,38 @@ + +# bug #2481 +import math + +template test(loopCount: int, extraI: int, testBody: stmt): stmt = + block: + for i in 0..loopCount-1: + testBody + echo "done extraI=", extraI + +template test(loopCount: int, extraF: float, testBody: stmt): stmt = + block: + test(loopCount, round(extraF), testBody) + +template test(loopCount: int, testBody: stmt): stmt = + block: + test(loopCount, 0, testBody) + echo "done extraI passed 0" + +when isMainModule: + var + loops = 0 + + test 0, 0: + loops += 1 + echo "test 0 complete, loops=", loops + + test 1, 1.0: + loops += 1 + echo "test 1.0 complete, loops=", loops + + when true: + # when true we get the following compile time error: + # b.nim(35, 6) Error: expression 'loops += 1' has no type (or is ambiguous) + loops = 0 + test 2: + loops += 1 + echo "test no extra complete, loops=", loops diff --git a/tests/parallel/tgc_unsafe2.nim b/tests/parallel/tgc_unsafe2.nim new file mode 100644 index 000000000..ec4605fe9 --- /dev/null +++ b/tests/parallel/tgc_unsafe2.nim @@ -0,0 +1,39 @@ +discard """ + line: 28 + nimout: '''tgc_unsafe2.nim(22, 5) Warning: 'trick' is not GC-safe as it accesses 'global' which is a global using GC'ed memory +tgc_unsafe2.nim(26, 5) Warning: 'track' is not GC-safe as it calls 'trick' +tgc_unsafe2.nim(28, 5) Error: 'consumer' is not GC-safe as it calls 'track' +''' + errormsg: "'consumer' is not GC-safe as it calls 'track'" +""" + +import threadpool + +type StringChannel = TChannel[string] +var channels: array[1..3, StringChannel] + +type + MyObject[T] = object + x: T + +var global: MyObject[string] +var globalB: MyObject[float] + +proc trick(ix: int) = + echo global.x + echo channels[ix].recv() + +proc track(ix: int) = trick(ix) + +proc consumer(ix: int) {.thread.} = + track(ix) + +proc main = + for ix in 1..3: channels[ix].open() + for ix in 1..3: spawn consumer(ix) + for ix in 1..3: channels[ix].send("test") + sync() + for ix in 1..3: channels[ix].close() + +when isMainModule: + main() diff --git a/tests/parallel/twrong_refcounts.nim b/tests/parallel/twrong_refcounts.nim new file mode 100644 index 000000000..db32a96d8 --- /dev/null +++ b/tests/parallel/twrong_refcounts.nim @@ -0,0 +1,53 @@ +discard """ + output: "Success" +""" + +import math, threadPool + +# --- + +type + Person = object + age: int + friend: ref Person + +var + people: seq[ref Person] = @[] + +proc newPerson(age:int): ref Person = + result.new() + result.age = age + +proc greet(p:Person) = + #echo p.age, ", ", p.friend.age + p.friend.age += 1 + +# --- + +proc setup = + for i in 0 .. <20: + people.add newPerson(i + 1) + for i in 0 .. <20: + people[i].friend = people[random(20)] + +proc update = + var countA: array[20, int] + var countB: array[20, int] + + for i, p in people: + countA[i] = getRefCount(p) + parallel: + for i in 0 .. people.high: + spawn greet(people[i][]) + for i, p in people: + countB[i] = getRefCount(p) + + for i in 0 .. <20: + doAssert countA[i] == countB[i] + echo "Success" + +# --- + +when isMainModule: + setup() + update() diff --git a/tests/parser/tinvcolonlocation1.nim b/tests/parser/tinvcolonlocation1.nim new file mode 100644 index 000000000..cacde48bd --- /dev/null +++ b/tests/parser/tinvcolonlocation1.nim @@ -0,0 +1,12 @@ +discard """ + file: "tinvcolonlocation1.nim" + line: 8 + column: 3 + errormsg: "':' expected" +""" +try #<- missing ':' + echo "try" +except: + echo "except" +finally: + echo "finally" diff --git a/tests/parser/tinvcolonlocation2.nim b/tests/parser/tinvcolonlocation2.nim new file mode 100644 index 000000000..2b6a92b9d --- /dev/null +++ b/tests/parser/tinvcolonlocation2.nim @@ -0,0 +1,15 @@ +discard """ + file: "tinvcolonlocation2.nim" + line: 11 + column: 1 + errormsg: "':' expected" +""" +try: + echo "try" +except #<- missing ':' + echo "except" +finally: +#<-- error will be here above, at the beginning of finally, +# since compiler tries to consome echo and part of except +# expression + echo "finally" diff --git a/tests/parser/tinvcolonlocation3.nim b/tests/parser/tinvcolonlocation3.nim new file mode 100644 index 000000000..2b30b1dbe --- /dev/null +++ b/tests/parser/tinvcolonlocation3.nim @@ -0,0 +1,12 @@ +discard """ + file: "tinvcolonlocation3.nim" + line: 12 + column: 3 + errormsg: "':' expected" +""" +try: + echo "try" +except: + echo "except" +finally #<- missing ':' + echo "finally" diff --git a/tests/parser/tstrongspaces.nim b/tests/parser/tstrongspaces.nim index 91506daf0..e70b91988 100644 --- a/tests/parser/tstrongspaces.nim +++ b/tests/parser/tstrongspaces.nim @@ -2,6 +2,12 @@ discard """ output: '''35 +true +true +4 +true +1 +false 77 (Field0: 1, Field1: 2, Field2: 2) ha @@ -9,11 +15,26 @@ true tester args all all args +19 +-3 +false +-2 ''' """ echo 2+5 * 5 +# Keyword operators +echo 1 + 16 shl 1 == 1 + (16 shl 1) +echo 2 and 1 in {0, 30} +echo 2+2 * 2 shr 1 +echo false or 2 and 1 in {0, 30} + +proc `^`(a, b: int): int = a + b div 2 +echo 19 mod 16 ^ 4 + 2 and 1 +echo 18 mod 16 ^ 4 > 0 + +# echo $foo gotcha let foo = 77 echo $foo @@ -27,7 +48,7 @@ when true: let b = 66 let c = 90 let bar = 8000 - if foo+4 * 4 == 8 and b&c | 9 ++ + if foo+4 * 4 == 8 and b&c | 9 ++ bar: echo "ho" else: @@ -50,3 +71,13 @@ const echo tester & " " & args|"all" echo "all" | tester & " " & args echo "all"|tester & " " & args + +# Test arrow like operators. See also tests/macros/tclosuremacro.nim +proc `+->`(a, b: int): int = a + b*4 +template `===>`(a, b: int): expr = a - b shr 1 + +echo 3 +-> 2 + 2 and 4 +var arrowed = 3+->2 + 2 and 4 # arrowed = 4 +echo arrowed ===> 15 +echo (2 * 3+->2) == (2*3 +-> 2) +echo arrowed ===> 2 + 3+->2 diff --git a/tests/parser/ttupleunpack.nim b/tests/parser/ttupleunpack.nim new file mode 100644 index 000000000..aaa06f9f4 --- /dev/null +++ b/tests/parser/ttupleunpack.nim @@ -0,0 +1,35 @@ +discard """ + file: "ttupleunpack.nim" + output: "" + exitcode: 0 +""" + +proc returnsTuple(): (int, int, int) = (4, 2, 3) + +proc main2 = + let (x, _, z) = returnsTuple() + +proc main() = + + proc foo(): tuple[x, y, z: int] = + return (4, 2, 3) + + var (x, _, y) = foo() + doAssert x == 4 + doAssert y == 3 + + var (a, _, _) = foo() + doAssert a == 4 + + var (aa, _, _) = foo() + doAssert aa == 4 + + iterator bar(): tuple[x, y, z: int] = + yield (1,2,3) + + for x, y, _ in bar(): + doAssert x == 1 + doAssert y == 2 + +main() +main2() diff --git a/tests/sets/tsets.nim b/tests/sets/tsets.nim index e370209ed..646175329 100644 --- a/tests/sets/tsets.nim +++ b/tests/sets/tsets.nim @@ -1,6 +1,7 @@ discard """ file: "tsets.nim" - output: "Ha ein F ist in s!" + output: '''Ha ein F ist in s! +false''' """ # Test the handling of sets @@ -15,30 +16,30 @@ type TAZ = range['a'..'z'] TAZset = set[TAZ] - TTokType* = enum + TTokType* = enum tkInvalid, tkEof, tkSymbol, - tkAddr, tkAnd, tkAs, tkAsm, tkBlock, tkBreak, tkCase, tkCast, tkConst, - tkContinue, tkConverter, tkDiscard, tkDiv, tkElif, tkElse, tkEnd, tkEnum, - tkExcept, tkException, tkFinally, tkFor, tkFrom, tkGeneric, tkIf, tkImplies, - tkImport, tkIn, tkInclude, tkIs, tkIsnot, tkIterator, tkLambda, tkMacro, - tkMethod, tkMod, tkNil, tkNot, tkNotin, tkObject, tkOf, tkOr, tkOut, tkProc, - tkPtr, tkRaise, tkRecord, tkRef, tkReturn, tkShl, tkShr, tkTemplate, tkTry, + tkAddr, tkAnd, tkAs, tkAsm, tkBlock, tkBreak, tkCase, tkCast, tkConst, + tkContinue, tkConverter, tkDiscard, tkDiv, tkElif, tkElse, tkEnd, tkEnum, + tkExcept, tkException, tkFinally, tkFor, tkFrom, tkGeneric, tkIf, tkImplies, + tkImport, tkIn, tkInclude, tkIs, tkIsnot, tkIterator, tkLambda, tkMacro, + tkMethod, tkMod, tkNil, tkNot, tkNotin, tkObject, tkOf, tkOr, tkOut, tkProc, + tkPtr, tkRaise, tkRecord, tkRef, tkReturn, tkShl, tkShr, tkTemplate, tkTry, tkType, tkVar, tkWhen, tkWhere, tkWhile, tkWith, tkWithout, tkXor, tkYield, - tkIntLit, tkInt8Lit, tkInt16Lit, tkInt32Lit, tkInt64Lit, tkFloatLit, - tkFloat32Lit, tkFloat64Lit, tkStrLit, tkRStrLit, tkTripleStrLit, tkCharLit, - tkRCharLit, tkParLe, tkParRi, tkBracketLe, tkBracketRi, tkCurlyLe, - tkCurlyRi, tkBracketDotLe, tkBracketDotRi, - tkCurlyDotLe, tkCurlyDotRi, + tkIntLit, tkInt8Lit, tkInt16Lit, tkInt32Lit, tkInt64Lit, tkFloatLit, + tkFloat32Lit, tkFloat64Lit, tkStrLit, tkRStrLit, tkTripleStrLit, tkCharLit, + tkRCharLit, tkParLe, tkParRi, tkBracketLe, tkBracketRi, tkCurlyLe, + tkCurlyRi, tkBracketDotLe, tkBracketDotRi, + tkCurlyDotLe, tkCurlyDotRi, tkParDotLe, tkParDotRi, - tkComma, tkSemiColon, tkColon, tkEquals, tkDot, tkDotDot, tkHat, tkOpr, + tkComma, tkSemiColon, tkColon, tkEquals, tkDot, tkDotDot, tkHat, tkOpr, tkComment, tkAccent, tkInd, tkSad, tkDed, tkSpaces, tkInfixOpr, tkPrefixOpr, tkPostfixOpr TTokTypeRange = range[tkSymbol..tkDed] TTokTypes* = set[TTokTypeRange] const - toktypes: TTokTypes = {TTokTypeRange(tkSymbol)..pred(tkIntLit), + toktypes: TTokTypes = {TTokTypeRange(tkSymbol)..pred(tkIntLit), tkStrLit..tkTripleStrLit} var @@ -62,3 +63,142 @@ for x in low(TTokTypeRange) .. high(TTokTypeRange): #OUT Ha ein F ist in s! +type + TMsgKind* = enum + errUnknown, errIllFormedAstX, errInternal, errCannotOpenFile, errGenerated, + errXCompilerDoesNotSupportCpp, errStringLiteralExpected, + errIntLiteralExpected, errInvalidCharacterConstant, + errClosingTripleQuoteExpected, errClosingQuoteExpected, + errTabulatorsAreNotAllowed, errInvalidToken, errLineTooLong, + errInvalidNumber, errNumberOutOfRange, errNnotAllowedInCharacter, + errClosingBracketExpected, errMissingFinalQuote, errIdentifierExpected, + errNewlineExpected, + errInvalidModuleName, + errOperatorExpected, errTokenExpected, errStringAfterIncludeExpected, + errRecursiveDependencyX, errOnOrOffExpected, errNoneSpeedOrSizeExpected, + errInvalidPragma, errUnknownPragma, errInvalidDirectiveX, + errAtPopWithoutPush, errEmptyAsm, errInvalidIndentation, + errExceptionExpected, errExceptionAlreadyHandled, + errYieldNotAllowedHere, errYieldNotAllowedInTryStmt, + errInvalidNumberOfYieldExpr, errCannotReturnExpr, errAttemptToRedefine, + errStmtInvalidAfterReturn, errStmtExpected, errInvalidLabel, + errInvalidCmdLineOption, errCmdLineArgExpected, errCmdLineNoArgExpected, + errInvalidVarSubstitution, errUnknownVar, errUnknownCcompiler, + errOnOrOffExpectedButXFound, errNoneBoehmRefcExpectedButXFound, + errNoneSpeedOrSizeExpectedButXFound, errGuiConsoleOrLibExpectedButXFound, + errUnknownOS, errUnknownCPU, errGenOutExpectedButXFound, + errArgsNeedRunOption, errInvalidMultipleAsgn, errColonOrEqualsExpected, + errExprExpected, errUndeclaredIdentifier, errUseQualifier, errTypeExpected, + errSystemNeeds, errExecutionOfProgramFailed, errNotOverloadable, + errInvalidArgForX, errStmtHasNoEffect, errXExpectsTypeOrValue, + errXExpectsArrayType, errIteratorCannotBeInstantiated, errExprXAmbiguous, + errConstantDivisionByZero, errOrdinalTypeExpected, + errOrdinalOrFloatTypeExpected, errOverOrUnderflow, + errCannotEvalXBecauseIncompletelyDefined, errChrExpectsRange0_255, + errDynlibRequiresExportc, errUndeclaredFieldX, errNilAccess, + errIndexOutOfBounds, errIndexTypesDoNotMatch, errBracketsInvalidForType, + errValueOutOfSetBounds, errFieldInitTwice, errFieldNotInit, + errExprXCannotBeCalled, errExprHasNoType, errExprXHasNoType, + errCastNotInSafeMode, errExprCannotBeCastedToX, errCommaOrParRiExpected, + errCurlyLeOrParLeExpected, errSectionExpected, errRangeExpected, + errMagicOnlyInSystem, errPowerOfTwoExpected, + errStringMayNotBeEmpty, errCallConvExpected, errProcOnlyOneCallConv, + errSymbolMustBeImported, errExprMustBeBool, errConstExprExpected, + errDuplicateCaseLabel, errRangeIsEmpty, errSelectorMustBeOfCertainTypes, + errSelectorMustBeOrdinal, errOrdXMustNotBeNegative, errLenXinvalid, + errWrongNumberOfVariables, errExprCannotBeRaised, errBreakOnlyInLoop, + errTypeXhasUnknownSize, errConstNeedsConstExpr, errConstNeedsValue, + errResultCannotBeOpenArray, errSizeTooBig, errSetTooBig, + errBaseTypeMustBeOrdinal, errInheritanceOnlyWithNonFinalObjects, + errInheritanceOnlyWithEnums, errIllegalRecursionInTypeX, + errCannotInstantiateX, errExprHasNoAddress, errXStackEscape, + errVarForOutParamNeeded, + errPureTypeMismatch, errTypeMismatch, errButExpected, errButExpectedX, + errAmbiguousCallXYZ, errWrongNumberOfArguments, + errXCannotBePassedToProcVar, + errXCannotBeInParamDecl, errPragmaOnlyInHeaderOfProc, errImplOfXNotAllowed, + errImplOfXexpected, errNoSymbolToBorrowFromFound, errDiscardValueX, + errInvalidDiscard, errIllegalConvFromXtoY, errCannotBindXTwice, + errInvalidOrderInArrayConstructor, + errInvalidOrderInEnumX, errEnumXHasHoles, errExceptExpected, errInvalidTry, + errOptionExpected, errXisNoLabel, errNotAllCasesCovered, + errUnknownSubstitionVar, errComplexStmtRequiresInd, errXisNotCallable, + errNoPragmasAllowedForX, errNoGenericParamsAllowedForX, + errInvalidParamKindX, errDefaultArgumentInvalid, errNamedParamHasToBeIdent, + errNoReturnTypeForX, errConvNeedsOneArg, errInvalidPragmaX, + errXNotAllowedHere, errInvalidControlFlowX, + errXisNoType, errCircumNeedsPointer, errInvalidExpression, + errInvalidExpressionX, errEnumHasNoValueX, errNamedExprExpected, + errNamedExprNotAllowed, errXExpectsOneTypeParam, + errArrayExpectsTwoTypeParams, errInvalidVisibilityX, errInitHereNotAllowed, + errXCannotBeAssignedTo, errIteratorNotAllowed, errXNeedsReturnType, + errNoReturnTypeDeclared, + errInvalidCommandX, errXOnlyAtModuleScope, + errXNeedsParamObjectType, + errTemplateInstantiationTooNested, errInstantiationFrom, + errInvalidIndexValueForTuple, errCommandExpectsFilename, + errMainModuleMustBeSpecified, + errXExpected, + errTIsNotAConcreteType, + errInvalidSectionStart, errGridTableNotImplemented, errGeneralParseError, + errNewSectionExpected, errWhitespaceExpected, errXisNoValidIndexFile, + errCannotRenderX, errVarVarTypeNotAllowed, errInstantiateXExplicitly, + errOnlyACallOpCanBeDelegator, errUsingNoSymbol, + errMacroBodyDependsOnGenericTypes, + errDestructorNotGenericEnough, + errInlineIteratorsAsProcParams, + errXExpectsTwoArguments, + errXExpectsObjectTypes, errXcanNeverBeOfThisSubtype, errTooManyIterations, + errCannotInterpretNodeX, errFieldXNotFound, errInvalidConversionFromTypeX, + errAssertionFailed, errCannotGenerateCodeForX, errXRequiresOneArgument, + errUnhandledExceptionX, errCyclicTree, errXisNoMacroOrTemplate, + errXhasSideEffects, errIteratorExpected, errLetNeedsInit, + errThreadvarCannotInit, errWrongSymbolX, errIllegalCaptureX, + errXCannotBeClosure, errXMustBeCompileTime, + errCannotInferTypeOfTheLiteral, + errCannotInferReturnType, + errGenericLambdaNotAllowed, + errCompilerDoesntSupportTarget, + errUser, + warnCannotOpenFile, + warnOctalEscape, warnXIsNeverRead, warnXmightNotBeenInit, + warnDeprecated, warnConfigDeprecated, + warnSmallLshouldNotBeUsed, warnUnknownMagic, warnRedefinitionOfLabel, + warnUnknownSubstitutionX, warnLanguageXNotSupported, + warnFieldXNotSupported, warnCommentXIgnored, + warnNilStatement, warnTypelessParam, + warnDifferentHeaps, warnWriteToForeignHeap, warnUnsafeCode, + warnEachIdentIsTuple, warnShadowIdent, + warnProveInit, warnProveField, warnProveIndex, warnGcUnsafe, warnGcUnsafe2, + warnUninit, warnGcMem, warnDestructor, warnLockLevel, warnResultShadowed, + warnUser, + hintSuccess, hintSuccessX, + hintLineTooLong, hintXDeclaredButNotUsed, hintConvToBaseNotNeeded, + hintConvFromXtoItselfNotNeeded, hintExprAlwaysX, hintQuitCalled, + hintProcessing, hintCodeBegin, hintCodeEnd, hintConf, hintPath, + hintConditionAlwaysTrue, hintName, hintPattern, + hintUser + +const + fatalMin* = errUnknown + fatalMax* = errInternal + errMin* = errUnknown + errMax* = errUser + warnMin* = warnCannotOpenFile + warnMax* = pred(hintSuccess) + hintMin* = hintSuccess + hintMax* = high(TMsgKind) + +type + TNoteKind* = range[warnMin..hintMax] # "notes" are warnings or hints + TNoteKinds* = set[TNoteKind] + +var + gNotes*: TNoteKinds = {low(TNoteKind)..high(TNoteKind)} - + {warnShadowIdent, warnUninit, + warnProveField, warnProveIndex, warnGcUnsafe} + + +#import compiler.msgs + +echo warnUninit in gNotes diff --git a/tests/stdlib/tdialogs.nim b/tests/stdlib/tdialogs.nim deleted file mode 100644 index f0203d319..000000000 --- a/tests/stdlib/tdialogs.nim +++ /dev/null @@ -1,17 +0,0 @@ -# Test the dialogs module - -import dialogs, gtk2 - -gtk2.nimrod_init() - -var x = chooseFilesToOpen(nil) -for a in items(x): - writeln(stdout, a) - -info(nil, "start with an info box") -warning(nil, "now a warning ...") -error(nil, "... and an error!") - -writeln(stdout, chooseFileToOpen(nil)) -writeln(stdout, chooseFileToSave(nil)) -writeln(stdout, chooseDir(nil)) diff --git a/tests/stdlib/treloop.nim b/tests/stdlib/treloop.nim new file mode 100644 index 000000000..35236708c --- /dev/null +++ b/tests/stdlib/treloop.nim @@ -0,0 +1,9 @@ +discard """ + output: "@[(, +, 1, 2, )]" +""" + +import re + +let str = "(+ 1 2)" +var tokenRE = re"""[\s,]*(~@|[\[\]{}()'`~^@]|"(?:\\.|[^\\"])*"|;.*|[^\s\[\]{}('"`,;)]*)""" +echo str.findAll(tokenRE) diff --git a/tests/template/tdefault_nil.nim b/tests/template/tdefault_nil.nim new file mode 100644 index 000000000..891166306 --- /dev/null +++ b/tests/template/tdefault_nil.nim @@ -0,0 +1,14 @@ + +# bug #2629 +import sequtils, os + +template glob_rst(basedir: string = nil): expr = + if baseDir.isNil: + to_seq(walk_files("*.rst")) + else: + to_seq(walk_files(basedir/"*.rst")) + +let + rst_files = concat(glob_rst(), glob_rst("docs")) + +when isMainModule: echo rst_files diff --git a/tests/template/tstmt_semchecked_twice.nim b/tests/template/tstmt_semchecked_twice.nim new file mode 100644 index 000000000..05c16c3c9 --- /dev/null +++ b/tests/template/tstmt_semchecked_twice.nim @@ -0,0 +1,30 @@ + +# bug #2585 + +type + RenderPass = object + state: ref int + + RenderData* = object + fb: int + walls: seq[RenderPass] + + Mat2 = int + Vector2[T] = T + Pixels=int + +template use*(fb: int, st: stmt) : stmt = + echo "a ", $fb + st + echo "a ", $fb + +proc render(rdat: var RenderData; passes: var openarray[RenderPass]; proj: Mat2; + indexType = 1) = + for i in 0 .. <len(passes): + echo "blah ", repr(passes[i]) + + + +proc render2*(rdat: var RenderData; screenSz: Vector2[Pixels]; proj: Mat2) = + use rdat.fb: + render(rdat, rdat.walls, proj, 1) diff --git a/tests/template/ttempl2.nim b/tests/template/ttempl2.nim index 142bbb8c7..aaa2f1344 100644 --- a/tests/template/ttempl2.nim +++ b/tests/template/ttempl2.nim @@ -3,12 +3,12 @@ discard """ line: 18 errormsg: "undeclared identifier: \'b\'" """ -template declareInScope(x: expr, t: typeDesc): stmt {.immediate.} = +template declareInScope(x: untyped, t: typeDesc): untyped {.immediate.} = var x: t - -template declareInNewScope(x: expr, t: typeDesc): stmt {.immediate.} = + +template declareInNewScope(x: untyped, t: typeDesc): untyped {.immediate.} = # open a new scope: - block: + block: var x: t declareInScope(a, int) diff --git a/tests/testament/categories.nim b/tests/testament/categories.nim index 2d66d2f8e..336cf211e 100644 --- a/tests/testament/categories.nim +++ b/tests/testament/categories.nim @@ -238,7 +238,7 @@ proc testStdlib(r: var TResults, pattern, options: string, cat: Category) = for test in os.walkFiles(pattern): let contents = readFile(test).string if contents.contains("when isMainModule"): - testSpec r, makeTest(test, options, cat, actionRun) + testSpec r, makeTest(test, options, cat, actionRunNoSpec) else: testNoSpec r, makeTest(test, options, cat, actionCompile) diff --git a/tests/testament/specs.nim b/tests/testament/specs.nim index 2a8a4ea24..9306bf025 100644 --- a/tests/testament/specs.nim +++ b/tests/testament/specs.nim @@ -10,13 +10,14 @@ import parseutils, strutils, os, osproc, streams, parsecfg const - cmdTemplate* = r"nim $target --hints:on $options $file" + cmdTemplate* = r"nim $target --hints:on -d:testing $options $file" type TTestAction* = enum actionCompile = "compile" actionRun = "run" actionReject = "reject" + actionRunNoSpec = "runNoSpec" TResultEnum* = enum reNimcCrash, # nim compiler seems to have crashed reMsgsDiffer, # error messages differ @@ -42,7 +43,8 @@ type action*: TTestAction file*, cmd*: string outp*: string - line*, exitCode*: int + line*, column*: int + exitCode*: int msg*: string ccodeCheck*: string err*: TResultEnum @@ -77,7 +79,7 @@ proc extractSpec(filename: string): string = when not defined(nimhygiene): {.pragma: inject.} -template parseSpecAux(fillResult: stmt) {.immediate.} = +template parseSpecAux(fillResult: untyped) = var ss = newStringStream(extractSpec(filename)) var p {.inject.}: CfgParser open(p, ss, filename, 1) @@ -91,13 +93,18 @@ template parseSpecAux(fillResult: stmt) {.immediate.} = fillResult close(p) -proc parseSpec*(filename: string): TSpec = - result.file = filename +proc specDefaults*(result: var TSpec) = result.msg = "" result.outp = "" result.nimout = "" result.ccodeCheck = "" result.cmd = cmdTemplate + result.line = 0 + result.column = 0 + +proc parseSpec*(filename: string): TSpec = + specDefaults(result) + result.file = filename parseSpecAux: case normalize(e.key) of "action": @@ -108,7 +115,8 @@ proc parseSpec*(filename: string): TSpec = else: echo ignoreMsg(p, e) of "file": result.file = e.value of "line": discard parseInt(e.value, result.line) - of "output": + of "column": discard parseInt(e.value, result.column) + of "output": result.action = actionRun result.outp = e.value of "outputsub": @@ -117,7 +125,7 @@ proc parseSpec*(filename: string): TSpec = result.substr = true of "sortoutput": result.sortoutput = parseCfgBool(e.value) - of "exitcode": + of "exitcode": discard parseInt(e.value, result.exitCode) of "msg": result.msg = e.value diff --git a/tests/testament/tester.nim b/tests/testament/tester.nim index 86ff6a689..93cb3cc7a 100644 --- a/tests/testament/tester.nim +++ b/tests/testament/tester.nim @@ -50,7 +50,7 @@ type let pegLineError = - peg"{[^(]*} '(' {\d+} ', ' \d+ ') ' ('Error') ':' \s* {.*}" + peg"{[^(]*} '(' {\d+} ', ' {\d+} ') ' ('Error') ':' \s* {.*}" pegOtherError = peg"'Error:' \s* {.*}" pegSuccess = peg"'Hint: operation successful'.*" pegOfInterest = pegLineError / pegOtherError @@ -77,11 +77,13 @@ proc callCompiler(cmdTemplate, filename, options: string, result.msg = "" result.file = "" result.outp = "" - result.line = -1 + result.line = 0 + result.column = 0 if err =~ pegLineError: result.file = extractFilename(matches[0]) result.line = parseInt(matches[1]) - result.msg = matches[2] + result.column = parseInt(matches[2]) + result.msg = matches[3] elif err =~ pegOtherError: result.msg = matches[0] elif suc =~ pegSuccess: @@ -149,8 +151,11 @@ proc cmpMsgs(r: var TResults, expected, given: TSpec, test: TTest) = elif extractFilename(expected.file) != extractFilename(given.file) and "internal error:" notin expected.msg: r.addResult(test, expected.file, given.file, reFilesDiffer) - elif expected.line != given.line and expected.line != 0: - r.addResult(test, $expected.line, $given.line, reLinesDiffer) + elif expected.line != given.line and expected.line != 0 or + expected.column != given.column and expected.column != 0: + r.addResult(test, $expected.line & ':' & $expected.column, + $given.line & ':' & $given.column, + reLinesDiffer) else: r.addResult(test, expected.msg, given.msg, reSuccess) inc(r.passed) @@ -210,7 +215,12 @@ proc testSpec(r: var TResults, test: TTest) = let tname = test.name.addFileExt(".nim") inc(r.total) styledEcho "Processing ", fgCyan, extractFilename(tname) - var expected = parseSpec(tname) + var expected: TSpec + if test.action != actionRunNoSpec: + expected = parseSpec(tname) + else: + specDefaults expected + expected.action = actionRunNoSpec if expected.err == reIgnored: r.addResult(test, "", "", reIgnored) inc(r.skipped) @@ -220,7 +230,7 @@ proc testSpec(r: var TResults, test: TTest) = var given = callCompiler(expected.cmd, test.name, test.options & " --hint[Path]:off --hint[Processing]:off", test.target) compilerOutputTests(test, given, expected, r) - of actionRun: + of actionRun, actionRunNoSpec: var given = callCompiler(expected.cmd, test.name, test.options, test.target) if given.err != reSuccess: diff --git a/tests/tuples/tuint_tuple.nim b/tests/tuples/tuint_tuple.nim new file mode 100644 index 000000000..24bcead5e --- /dev/null +++ b/tests/tuples/tuint_tuple.nim @@ -0,0 +1,10 @@ +# bug #1986 found by gdmoore + +proc test(): int64 = + return 0xdeadbeef.int64 + +const items = [ + (var1: test(), var2: 100'u32), + (var1: test(), var2: 192'u32) +] + diff --git a/tests/tuples/tuple_with_nil.nim b/tests/tuples/tuple_with_nil.nim new file mode 100644 index 000000000..26e4ae85e --- /dev/null +++ b/tests/tuples/tuple_with_nil.nim @@ -0,0 +1,766 @@ +import macros +from strutils import IdentStartChars +import parseutils +import unicode +import math +import fenv +import unsigned +import pegs +import streams + +type + FormatError = object of Exception ## Error in the format string. + + Writer = concept W + ## Writer to output a character `c`. + when (NimMajor, NimMinor, NimPatch) > (0, 10, 2): + write(W, 'c') + else: + block: + var x: W + write(x, char) + + FmtAlign = enum ## Format alignment + faDefault ## default for given format type + faLeft ## left aligned + faRight ## right aligned + faCenter ## centered + faPadding ## right aligned, fill characters after sign (numbers only) + + FmtSign = enum ## Format sign + fsMinus ## only unary minus, no reservered sign space for positive numbers + fsPlus ## unary minus and unary plus + fsSpace ## unary minus and reserved space for positive numbers + + FmtType = enum ## Format type + ftDefault ## default format for given parameter type + ftStr ## string + ftChar ## character + ftDec ## decimal integer + ftBin ## binary integer + ftOct ## octal integer + ftHex ## hexadecimal integer + ftFix ## real number in fixed point notation + ftSci ## real number in scientific notation + ftGen ## real number in generic form (either fixed point or scientific) + ftPercent ## real number multiplied by 100 and % added + + Format = tuple ## Formatting information. + typ: FmtType ## format type + precision: int ## floating point precision + width: int ## minimal width + fill: string ## the fill character, UTF8 + align: FmtAlign ## aligment + sign: FmtSign ## sign notation + baseprefix: bool ## whether binary, octal, hex should be prefixed by 0b, 0x, 0o + upcase: bool ## upper case letters in hex or exponential formats + comma: bool ## + arysep: string ## separator for array elements + + PartKind = enum pkStr, pkFmt + + Part = object + ## Information of a part of the target string. + case kind: PartKind ## type of the part + of pkStr: + str: string ## literal string + of pkFmt: + arg: int ## position argument + fmt: string ## format string + field: string ## field of argument to be accessed + index: int ## array index of argument to be accessed + nested: bool ## true if the argument contains nested formats + +const + DefaultPrec = 6 ## Default precision for floating point numbers. + DefaultFmt: Format = (ftDefault, -1, -1, nil, faDefault, fsMinus, false, false, false, nil) + ## Default format corresponding to the empty format string, i.e. + ## `x.format("") == x.format(DefaultFmt)`. + round_nums = [0.5, 0.05, 0.005, 0.0005, 0.00005, 0.000005, 0.0000005, 0.00000005] + ## Rounding offset for floating point numbers up to precision 8. + +proc write(s: var string; c: char) = + s.add(c) + +proc has(c: Captures; i: range[0..pegs.MaxSubpatterns-1]): bool {.nosideeffect, inline.} = + ## Tests whether `c` contains a non-empty capture `i`. + let b = c.bounds(i) + result = b.first <= b.last + +proc get(str: string; c: Captures; i: range[0..MaxSubpatterns-1]; def: char): char {.nosideeffect, inline.} = + ## If capture `i` is non-empty return that portion of `str` casted + ## to `char`, otherwise return `def`. + result = if c.has(i): str[c.bounds(i).first] else: def + +proc get(str: string; c: Captures; i: range[0..MaxSubpatterns-1]; def: string; begoff: int = 0): string {.nosideeffect, inline.} = + ## If capture `i` is non-empty return that portion of `str` as + ## string, otherwise return `def`. + let b = c.bounds(i) + result = if c.has(i): str.substr(b.first + begoff, b.last) else: def + +proc get(str: string; c: Captures; i: range[0..MaxSubpatterns-1]; def: int; begoff: int = 0): int {.nosideeffect, inline.} = + ## If capture `i` is non-empty return that portion of `str` + ## converted to int, otherwise return `def`. + if c.has(i): + discard str.parseInt(result, c.bounds(i).first + begoff) + else: + result = def + +proc parse(fmt: string): Format {.nosideeffect.} = + # Converts the format string `fmt` into a `Format` structure. + let p = + sequence(capture(?sequence(anyRune(), &charSet({'<', '>', '=', '^'}))), + capture(?charSet({'<', '>', '=', '^'})), + capture(?charSet({'-', '+', ' '})), + capture(?charSet({'#'})), + capture(?(+digits())), + capture(?charSet({','})), + capture(?sequence(charSet({'.'}), +digits())), + capture(?charSet({'b', 'c', 'd', 'e', 'E', 'f', 'F', 'g', 'G', 'n', 'o', 's', 'x', 'X', '%'})), + capture(?sequence(charSet({'a'}), *pegs.any()))) + # let p=peg"{(_&[<>=^])?}{[<>=^]?}{[-+ ]?}{[#]?}{[0-9]+?}{[,]?}{([.][0-9]+)?}{[bcdeEfFgGnosxX%]?}{(a.*)?}" + + var caps: Captures + if fmt.rawmatch(p, 0, caps) < 0: + raise newException(FormatError, "Invalid format string") + + result.fill = fmt.get(caps, 0, nil) + + case fmt.get(caps, 1, 0.char) + of '<': result.align = faLeft + of '>': result.align = faRight + of '^': result.align = faCenter + of '=': result.align = faPadding + else: result.align = faDefault + + case fmt.get(caps, 2, '-') + of '-': result.sign = fsMinus + of '+': result.sign = fsPlus + of ' ': result.sign = fsSpace + else: result.sign = fsMinus + + result.baseprefix = caps.has(3) + + result.width = fmt.get(caps, 4, -1) + + if caps.has(4) and fmt[caps.bounds(4).first] == '0': + if result.fill != nil: + raise newException(FormatError, "Leading 0 in with not allowed with explicit fill character") + if result.align != faDefault: + raise newException(FormatError, "Leading 0 in with not allowed with explicit alignment") + result.fill = "0" + result.align = faPadding + + result.comma = caps.has(5) + + result.precision = fmt.get(caps, 6, -1, 1) + + case fmt.get(caps, 7, 0.char) + of 's': result.typ = ftStr + of 'c': result.typ = ftChar + of 'd', 'n': result.typ = ftDec + of 'b': result.typ = ftBin + of 'o': result.typ = ftOct + of 'x': result.typ = ftHex + of 'X': result.typ = ftHex; result.upcase = true + of 'f', 'F': result.typ = ftFix + of 'e': result.typ = ftSci + of 'E': result.typ = ftSci; result.upcase = true + of 'g': result.typ = ftGen + of 'G': result.typ = ftGen; result.upcase = true + of '%': result.typ = ftPercent + else: result.typ = ftDefault + + result.arysep = fmt.get(caps, 8, nil, 1) + +proc getalign(fmt: Format; defalign: FmtAlign; slen: int) : tuple[left, right:int] {.nosideeffect.} = + ## Returns the number of left and right padding characters for a + ## given format alignment and width of the object to be printed. + ## + ## `fmt` + ## the format data + ## `default` + ## if `fmt.align == faDefault`, then this alignment is used + ## `slen` + ## the width of the object to be printed. + ## + ## The returned values `(left, right)` will be as minimal as possible + ## so that `left + slen + right >= fmt.width`. + result.left = 0 + result.right = 0 + if (fmt.width >= 0) and (slen < fmt.width): + let alg = if fmt.align == faDefault: defalign else: fmt.align + case alg: + of faLeft: result.right = fmt.width - slen + of faRight, faPadding: result.left = fmt.width - slen + of faCenter: + result.left = (fmt.width - slen) div 2 + result.right = fmt.width - slen - result.left + else: discard + +proc writefill(o: var Writer; fmt: Format; n: int; signum: int = 0) = + ## Write characters for filling. This function also writes the sign + ## of a numeric format and handles the padding alignment + ## accordingly. + ## + ## `o` + ## output object + ## `add` + ## output function + ## `fmt` + ## format to be used (important for padding aligment) + ## `n` + ## the number of filling characters to be written + ## `signum` + ## the sign of the number to be written, < 0 negative, > 0 positive, = 0 zero + if fmt.align == faPadding and signum != 0: + if signum < 0: write(o, '-') + elif fmt.sign == fsPlus: write(o, '+') + elif fmt.sign == fsSpace: write(o, ' ') + + if fmt.fill == nil: + for i in 1..n: write(o, ' ') + else: + for i in 1..n: + for c in fmt.fill: + write(o, c) + + if fmt.align != faPadding and signum != 0: + if signum < 0: write(o, '-') + elif fmt.sign == fsPlus: write(o, '+') + elif fmt.sign == fsSpace: write(o, ' ') + +proc writeformat(o: var Writer; s: string; fmt: Format) = + ## Write string `s` according to format `fmt` using output object + ## `o` and output function `add`. + if fmt.typ notin {ftDefault, ftStr}: + raise newException(FormatError, "String variable must have 's' format type") + + # compute alignment + let len = if fmt.precision < 0: runelen(s) else: min(runelen(s), fmt.precision) + var alg = getalign(fmt, faLeft, len) + writefill(o, fmt, alg.left) + var pos = 0 + for i in 0..len-1: + let rlen = runeLenAt(s, pos) + for j in pos..pos+rlen-1: write(o, s[j]) + pos += rlen + writefill(o, fmt, alg.right) + +proc writeformat(o: var Writer; c: char; fmt: Format) = + ## Write character `c` according to format `fmt` using output object + ## `o` and output function `add`. + if not (fmt.typ in {ftChar, ftDefault}): + raise newException(FormatError, "Character variable must have 'c' format type") + + # compute alignment + var alg = getalign(fmt, faLeft, 1) + writefill(o, fmt, alg.left) + write(o, c) + writefill(o, fmt, alg.right) + +proc writeformat(o: var Writer; c: Rune; fmt: Format) = + ## Write rune `c` according to format `fmt` using output object + ## `o` and output function `add`. + if not (fmt.typ in {ftChar, ftDefault}): + raise newException(FormatError, "Character variable must have 'c' format type") + + # compute alignment + var alg = getalign(fmt, faLeft, 1) + writefill(o, fmt, alg.left) + let s = c.toUTF8 + for c in s: write(o, c) + writefill(o, fmt, alg.right) + +proc abs(x: SomeUnsignedInt): SomeUnsignedInt {.inline.} = x + ## Return the absolute value of the unsigned int `x`. + +proc writeformat(o: var Writer; i: SomeInteger; fmt: Format) = + ## Write integer `i` according to format `fmt` using output object + ## `o` and output function `add`. + var fmt = fmt + if fmt.typ == ftDefault: + fmt.typ = ftDec + if not (fmt.typ in {ftBin, ftOct, ftHex, ftDec}): + raise newException(FormatError, "Integer variable must of one of the following types: b,o,x,X,d,n") + + var base: type(i) + var len = 0 + case fmt.typ: + of ftDec: + base = 10 + of ftBin: + base = 2 + if fmt.baseprefix: len += 2 + of ftOct: + base = 8 + if fmt.baseprefix: len += 2 + of ftHex: + base = 16 + if fmt.baseprefix: len += 2 + else: assert(false) + + if fmt.sign != fsMinus or i < 0: len.inc + + var x: type(i) = abs(i) + var irev: type(i) = 0 + var ilen = 0 + while x > 0.SomeInteger: + len.inc + ilen.inc + irev = irev * base + x mod base + x = x div base + if ilen == 0: + ilen.inc + len.inc + + var alg = getalign(fmt, faRight, len) + writefill(o, fmt, alg.left, if i >= 0.SomeInteger: 1 else: -1) + if fmt.baseprefix: + case fmt.typ + of ftBin: + write(o, '0') + write(o, 'b') + of ftOct: + write(o, '0') + write(o, 'o') + of ftHex: + write(o, '0') + write(o, 'x') + else: + raise newException(FormatError, "# only allowed with b, o, x or X") + while ilen > 0: + ilen.dec + let c = irev mod base + irev = irev div base + if c < 10: + write(o, ('0'.int + c.int).char) + elif fmt.upcase: + write(o, ('A'.int + c.int - 10).char) + else: + write(o, ('a'.int + c.int - 10).char) + writefill(o, fmt, alg.right) + +proc writeformat(o: var Writer; p: pointer; fmt: Format) = + ## Write pointer `i` according to format `fmt` using output object + ## `o` and output function `add`. + ## + ## Pointers are casted to unsigned int and formated as hexadecimal + ## with prefix unless specified otherwise. + var f = fmt + if f.typ == 0.char: + f.typ = 'x' + f.baseprefix = true + writeformat(o, add, cast[uint](p), f) + +proc writeformat(o: var Writer; x: SomeReal; fmt: Format) = + ## Write real number `x` according to format `fmt` using output + ## object `o` and output function `add`. + var fmt = fmt + # handle default format + if fmt.typ == ftDefault: + fmt.typ = ftGen + if fmt.precision < 0: fmt.precision = DefaultPrec + if not (fmt.typ in {ftFix, ftSci, ftGen, ftPercent}): + raise newException(FormatError, "Integer variable must of one of the following types: f,F,e,E,g,G,%") + + let positive = x >= 0 and classify(x) != fcNegZero + var len = 0 + + if fmt.sign != fsMinus or not positive: len.inc + + var prec = if fmt.precision < 0: DefaultPrec else: fmt.precision + var y = abs(x) + var exp = 0 + var numstr, frstr: array[0..31, char] + var numlen, frbeg, frlen = 0 + + if fmt.typ == ftPercent: y *= 100 + + case classify(x): + of fcNan: + numstr[0..2] = ['n', 'a', 'n'] + numlen = 3 + of fcInf, fcNegInf: + numstr[0..2] = ['f', 'n', 'i'] + numlen = 3 + of fcZero, fcNegZero: + numstr[0] = '0' + numlen = 1 + else: # a usual fractional number + if not (fmt.typ in {ftFix, ftPercent}): # not fixed point + exp = int(floor(log10(y))) + if fmt.typ == ftGen: + if prec == 0: prec = 1 + if -4 <= exp and exp < prec: + prec = prec-1-exp + exp = 0 + else: + prec = prec - 1 + len += 4 # exponent + else: + len += 4 # exponent + # shift y so that 1 <= abs(y) < 2 + if exp > 0: y /= pow(10.SomeReal, abs(exp).SomeReal) + elif exp < 0: y *= pow(10.SomeReal, abs(exp).SomeReal) + elif fmt.typ == ftPercent: + len += 1 # percent sign + + # handle rounding by adding +0.5 * LSB + if prec < len(round_nums): y += round_nums[prec] + + # split into integer and fractional part + var mult = 1'i64 + for i in 1..prec: mult *= 10 + var num = y.int64 + var fr = ((y - num.SomeReal) * mult.SomeReal).int64 + # build integer part string + while num != 0: + numstr[numlen] = ('0'.int + (num mod 10)).char + numlen.inc + num = num div 10 + if numlen == 0: + numstr[0] = '0' + numlen.inc + # build fractional part string + while fr != 0: + frstr[frlen] = ('0'.int + (fr mod 10)).char + frlen.inc + fr = fr div 10 + while frlen < prec: + frstr[frlen] = '0' + frlen.inc + # possible remove trailing 0 + if fmt.typ == ftGen: + while frbeg < frlen and frstr[frbeg] == '0': frbeg.inc + # update length of string + len += numlen; + if frbeg < frlen: + len += 1 + frlen - frbeg # decimal point and fractional string + + let alg = getalign(fmt, faRight, len) + writefill(o, fmt, alg.left, if positive: 1 else: -1) + for i in (numlen-1).countdown(0): write(o, numstr[i]) + if frbeg < frlen: + write(o, '.') + for i in (frlen-1).countdown(frbeg): write(o, frstr[i]) + if fmt.typ == ftSci or (fmt.typ == ftGen and exp != 0): + write(o, if fmt.upcase: 'E' else: 'e') + if exp >= 0: + write(o, '+') + else: + write(o, '-') + exp = -exp + if exp < 10: + write(o, '0') + write(o, ('0'.int + exp).char) + else: + var i=0 + while exp > 0: + numstr[i] = ('0'.int + exp mod 10).char + i+=1 + exp = exp div 10 + while i>0: + i-=1 + write(o, numstr[i]) + if fmt.typ == ftPercent: write(o, '%') + writefill(o, fmt, alg.right) + +proc writeformat(o: var Writer; b: bool; fmt: Format) = + ## Write boolean value `b` according to format `fmt` using output + ## object `o`. A boolean may be formatted numerically or as string. + ## In the former case true is written as 1 and false as 0, in the + ## latter the strings "true" and "false" are shown, respectively. + ## The default is string format. + if fmt.typ in {ftStr, ftDefault}: + writeformat(o, + if b: "true" + else: "false", + fmt) + elif fmt.typ in {ftBin, ftOct, ftHex, ftDec}: + writeformat(o, + if b: 1 + else: 0, + fmt) + else: + raise newException(FormatError, "Boolean values must of one of the following types: s,b,o,x,X,d,n") + +proc writeformat(o: var Writer; ary: openarray[any]; fmt: Format) = + ## Write array `ary` according to format `fmt` using output object + ## `o` and output function `add`. + if ary.len == 0: return + + var sep: string + var nxtfmt = fmt + if fmt.arysep == nil: + sep = "\t" + elif fmt.arysep.len == 0: + sep = "" + else: + let sepch = fmt.arysep[0] + let nxt = 1 + skipUntil(fmt.arysep, sepch, 1) + if nxt >= 1: + nxtfmt.arysep = fmt.arysep.substr(nxt) + sep = fmt.arysep.substr(1, nxt-1) + else: + nxtfmt.arysep = "" + sep = fmt.arysep.substr(1) + writeformat(o, ary[0], nxtfmt) + for i in 1..ary.len-1: + for c in sep: write(o, c) + writeformat(o, ary[i], nxtfmt) + +proc addformat[T](o: var Writer; x: T; fmt: Format = DefaultFmt) {.inline.} = + ## Write `x` formatted with `fmt` to `o`. + writeformat(o, x, fmt) + +proc addformat[T](o: var Writer; x: T; fmt: string) {.inline.} = + ## The same as `addformat(o, x, parse(fmt))`. + addformat(o, x, fmt.parse) + +proc addformat(s: var string; x: string) {.inline.} = + ## Write `x` to `s`. This is a fast specialized version for + ## appending unformatted strings. + add(s, x) + +proc addformat(f: File; x: string) {.inline.} = + ## Write `x` to `f`. This is a fast specialized version for + ## writing unformatted strings to a file. + write(f, x) + +proc addformat[T](f: File; x: T; fmt: Format = DefaultFmt) {.inline.} = + ## Write `x` to file `f` using format `fmt`. + var g = f + writeformat(g, x, fmt) + +proc addformat[T](f: File; x: T; fmt: string) {.inline.} = + ## Write `x` to file `f` using format string `fmt`. This is the same + ## as `addformat(f, x, parse(fmt))` + addformat(f, x, parse(fmt)) + +proc addformat(s: Stream; x: string) {.inline.} = + ## Write `x` to `s`. This is a fast specialized version for + ## writing unformatted strings to a stream. + write(s, x) + +proc addformat[T](s: Stream; x: T; fmt: Format = DefaultFmt) {.inline.} = + ## Write `x` to stream `s` using format `fmt`. + var g = s + writeformat(g, x, fmt) + +proc addformat[T](s: Stream; x: T; fmt: string) {.inline.} = + ## Write `x` to stream `s` using format string `fmt`. This is the same + ## as `addformat(s, x, parse(fmt))` + addformat(s, x, parse(fmt)) + +proc format[T](x: T; fmt: Format): string = + ## Return `x` formatted as a string according to format `fmt`. + result = "" + addformat(result, x, fmt) + +proc format[T](x: T; fmt: string): string = + ## Return `x` formatted as a string according to format string `fmt`. + result = format(x, fmt.parse) + +proc format[T](x: T): string {.inline.} = + ## Return `x` formatted as a string according to the default format. + ## The default format corresponds to an empty format string. + var fmt {.global.} : Format = DefaultFmt + result = format(x, fmt) + +proc unquoted(s: string): string {.compileTime.} = + ## Return `s` {{ and }} by single { and }, respectively. + result = "" + var pos = 0 + while pos < s.len: + let nxt = pos + skipUntil(s, {'{', '}'}) + result.add(s.substr(pos, nxt)) + pos = nxt + 2 + +proc splitfmt(s: string): seq[Part] {.compiletime, nosideeffect.} = + ## Split format string `s` into a sequence of "parts". + ## + + ## Each part is either a literal string or a format specification. A + ## format specification is a substring of the form + ## "{[arg][:format]}" where `arg` is either empty or a number + ## refering to the arg-th argument and an additional field or array + ## index. The format string is a string accepted by `parse`. + let subpeg = sequence(capture(digits()), + capture(?sequence(charSet({'.'}), *pegs.identStartChars(), *identChars())), + capture(?sequence(charSet({'['}), +digits(), charSet({']'}))), + capture(?sequence(charSet({':'}), *pegs.any()))) + result = @[] + var pos = 0 + while true: + let oppos = pos + skipUntil(s, {'{', '}'}, pos) + # reached the end + if oppos >= s.len: + if pos < s.len: + result.add(Part(kind: pkStr, str: s.substr(pos).unquoted)) + return + # skip double + if oppos + 1 < s.len and s[oppos] == s[oppos+1]: + result.add(Part(kind: pkStr, str: s.substr(pos, oppos))) + pos = oppos + 2 + continue + if s[oppos] == '}': + error("Single '}' encountered in format string") + if oppos > pos: + result.add(Part(kind: pkStr, str: s.substr(pos, oppos-1).unquoted)) + # find matching closing } + var lvl = 1 + var nested = false + pos = oppos + while lvl > 0: + pos.inc + pos = pos + skipUntil(s, {'{', '}'}, pos) + if pos >= s.len: + error("Single '{' encountered in format string") + if s[pos] == '{': + lvl.inc + if lvl == 2: + nested = true + if lvl > 2: + error("Too many nested format levels") + else: + lvl.dec + let clpos = pos + var fmtpart = Part(kind: pkFmt, arg: -1, fmt: s.substr(oppos+1, clpos-1), field: nil, index: int.high, nested: nested) + if fmtpart.fmt.len > 0: + var m: array[0..3, string] + if not fmtpart.fmt.match(subpeg, m): + error("invalid format string") + + if m[1] != nil and m[1].len > 0: + fmtpart.field = m[1].substr(1) + if m[2] != nil and m[2].len > 0: + discard parseInt(m[2].substr(1, m[2].len-2), fmtpart.index) + + if m[0].len > 0: discard parseInt(m[0], fmtpart.arg) + if m[3] == nil or m[3].len == 0: + fmtpart.fmt = "" + elif m[3][0] == ':': + fmtpart.fmt = m[3].substr(1) + else: + fmtpart.fmt = m[3] + result.add(fmtpart) + pos = clpos + 1 + +proc literal(s: string): NimNode {.compiletime, nosideeffect.} = + ## Return the nim literal of string `s`. This handles the case if + ## `s` is nil. + result = if s == nil: newNilLit() else: newLit(s) + +proc literal(b: bool): NimNode {.compiletime, nosideeffect.} = + ## Return the nim literal of boolean `b`. This is either `true` + ## or `false` symbol. + result = if b: "true".ident else: "false".ident + +proc literal[T](x: T): NimNode {.compiletime, nosideeffect.} = + ## Return the nim literal of value `x`. + when type(x) is enum: + result = ($x).ident + else: + result = newLit(x) + +proc generatefmt(fmtstr: string; + args: var openarray[tuple[arg:NimNode, cnt:int]]; + arg: var int;): seq[tuple[val, fmt:NimNode]] {.compiletime.} = + ## fmtstr + ## the format string + ## args + ## array of expressions for the arguments + ## arg + ## the number of the next argument for automatic parsing + ## + ## If arg is < 0 then the functions assumes that explicit numbering + ## must be used, otherwise automatic numbering is used starting at + ## `arg`. The value of arg is updated according to the number of + ## arguments being used. If arg == 0 then automatic and manual + ## numbering is not decided (because no explicit manual numbering is + ## fixed und no automatically numbered argument has been used so + ## far). + ## + ## The function returns a list of pairs `(val, fmt)` where `val` is + ## an expression to be formatted and `fmt` is the format string (or + ## Format). Therefore, the resulting string can be generated by + ## concatenating expressions `val.format(fmt)`. If `fmt` is `nil` + ## then `val` is a (literal) string expression. + try: + result = @[] + for part in splitfmt(fmtstr): + case part.kind + of pkStr: result.add((newLit(part.str), nil)) + of pkFmt: + # first compute the argument expression + # start with the correct index + var argexpr : NimNode + if part.arg >= 0: + if arg > 0: + error("Cannot switch from automatic field numbering to manual field specification") + if part.arg >= args.len: + error("Invalid explicit argument index: " & $part.arg) + argexpr = args[part.arg].arg + args[part.arg].cnt = args[part.arg].cnt + 1 + arg = -1 + else: + if arg < 0: + error("Cannot switch from manual field specification to automatic field numbering") + if arg >= args.len: + error("Too few arguments for format string") + argexpr = args[arg].arg + args[arg].cnt = args[arg].cnt + 1 + arg.inc + # possible field access + if part.field != nil and part.field.len > 0: + argexpr = newDotExpr(argexpr, part.field.ident) + # possible array access + if part.index < int.high: + argexpr = newNimNode(nnkBracketExpr).add(argexpr, newLit(part.index)) + # now the expression for the format data + var fmtexpr: NimNode + if part.nested: + # nested format string. Compute the format string by + # concatenating the parts of the substring. + for e in generatefmt(part.fmt, args, arg): + var newexpr = if part.fmt == nil: e.val else: newCall(bindsym"format", e.val, e.fmt) + if fmtexpr != nil and fmtexpr.kind != nnkNilLit: + fmtexpr = infix(fmtexpr, "&", newexpr) + else: + fmtexpr = newexpr + else: + # literal format string, precompute the format data + fmtexpr = newNimNode(nnkPar) + for field, val in part.fmt.parse.fieldPairs: + fmtexpr.add(newNimNode(nnkExprColonExpr).add(field.ident, literal(val))) + # add argument + result.add((argexpr, fmtexpr)) + finally: + discard + +proc addfmtfmt(fmtstr: string; args: NimNode; retvar: NimNode): NimNode {.compileTime.} = + var argexprs = newseq[tuple[arg:NimNode; cnt:int]](args.len) + result = newNimNode(nnkStmtListExpr) + # generate let bindings for arguments + for i in 0..args.len-1: + let argsym = gensym(nskLet, "arg" & $i) + result.add(newLetStmt(argsym, args[i])) + argexprs[i].arg = argsym + # add result values + var arg = 0 + for e in generatefmt(fmtstr, argexprs, arg): + if e.fmt == nil or e.fmt.kind == nnkNilLit: + result.add(newCall(bindsym"addformat", retvar, e.val)) + else: + result.add(newCall(bindsym"addformat", retvar, e.val, e.fmt)) + for i, arg in argexprs: + if arg.cnt == 0: + warning("Argument " & $(i+1) & " `" & args[i].repr & "` is not used in format string") + +macro addfmt(s: var string, fmtstr: string{lit}, args: varargs[expr]): expr = + ## The same as `s.add(fmtstr.fmt(args...))` but faster. + result = addfmtfmt($fmtstr, args, s) + +var s: string = "" +s.addfmt("a:{}", 42) diff --git a/tests/tuples/tuple_with_seq.nim b/tests/tuples/tuple_with_seq.nim new file mode 100644 index 000000000..39edb500f --- /dev/null +++ b/tests/tuples/tuple_with_seq.nim @@ -0,0 +1,46 @@ +discard """ + output: '''it's nil +@[1, 2, 3]''' +""" + +template foo(s: string = nil) = + if isNil(s): + echo "it's nil" + else: + echo s + +foo + + +# bug #2632 + +proc takeTup(x: tuple[s: string;x: seq[int]]) = + discard + +takeTup(("foo", @[])) + + +#proc foobar(): () = + +proc f(xs: seq[int]) = + discard + +proc g(t: tuple[n:int, xs:seq[int]]) = + discard + +when isMainModule: + f(@[]) # OK + g((1,@[1])) # OK + g((0,@[])) # NG + + +# bug #2630 +type T = tuple[a: seq[int], b: int] + +var t: T = (@[1,2,3], 7) + +proc test(s: seq[int]): T = + echo s + (s, 7) + +t = test(t.a) diff --git a/tests/types/temptyseqs.nim b/tests/types/temptyseqs.nim index 2b07ba679..834f63729 100644 --- a/tests/types/temptyseqs.nim +++ b/tests/types/temptyseqs.nim @@ -1,5 +1,13 @@ discard """ - output: "1" + output: '''1 +foo +bar +baz +foo +bar +baz +yes +no''' """ # bug #1708 @@ -24,3 +32,59 @@ when true: const foo2: seq[string] = @[] echo foo[0][0][0] + +proc takeEmpty(x: openArray[string] = []) = discard +takeEmpty() +takeEmpty([]) + +proc takeEmpty2(x: openArray[string] = @[]) = discard +takeEmpty2() +takeEmpty2([]) +takeEmpty2(@[]) + +#takeEmpty2([nil]) + +#rawMessage(errExecutionOfProgramFailed, []) + +# bug #2470 +const + stuff: seq[string] = @[] + +for str in stuff: + echo "str=", str + +# bug #1354 +proc foo4[T](more: seq[T] = @[]) = + var more2 = more + +foo4[int]() + +proc maino: int = + var wd: cstring = nil + inc result + +discard maino() + +proc varargso(a: varargs[string]) = + for x in a: + echo x + +varargso(["foo", "bar", "baz"]) +varargso("foo", "bar", "baz") + + +type + Flago = enum + tfNeedsInit, tfNotNil + +var s: set[Flago] = {tfNeedsInit} + +if {tfNeedsInit, tfNotNil} * s != {}: + echo "yes" +else: + echo "no" + +if {tfNeedsInit, tfNotNil} * s <= {tfNotNil}: + echo "yes" +else: + echo "no" diff --git a/tests/types/tisopr.nim b/tests/types/tisopr.nim index 8b7fe4e46..b9acfa5fb 100644 --- a/tests/types/tisopr.nim +++ b/tests/types/tisopr.nim @@ -1,5 +1,11 @@ discard """ - output: '''true true false yes''' + output: '''true true false yes +false +false +false +true +true +no''' """ proc IsVoid[T](): string = @@ -28,7 +34,7 @@ no s.items is iterator: float yes s.items is iterator: TNumber no s.items is iterator: object -type +type Iter[T] = iterator: T yes s.items is Iter[TNumber] @@ -51,3 +57,34 @@ yes Foo[4, int] is Bar[int] no Foo[4, int] is Baz[4] yes Foo[4, float] is Baz[4] + +# bug #2505 + +echo(8'i8 is int32) + +# bug #1853 +type SeqOrSet[E] = seq[E] or set[E] +type SeqOfInt = seq[int] +type SeqOrSetOfInt = SeqOrSet[int] + +# This prints "false", which seems less correct that (1) printing "true" or (2) +# raising a compiler error. +echo seq is SeqOrSet + +# This prints "false", as expected. +echo seq is SeqOrSetOfInt + +# This prints "true", as expected. +echo SeqOfInt is SeqOrSet + +# This causes an internal error (filename: compiler/semtypes.nim, line: 685). +echo SeqOfInt is SeqOrSetOfInt + +# bug #2522 +proc test[T](x: T) = + when T is typedesc: + echo "yes" + else: + echo "no" + +test(7) diff --git a/todo.txt b/todo.txt index fc62385a8..4972015a7 100644 --- a/todo.txt +++ b/todo.txt @@ -1,14 +1,26 @@ -version 0.10.4 +version 0.11.2 ============== -- improve GC-unsafety warnings -- make 'nil' work for 'add' and 'len' -- overloading of '=' +- The remaining bugs of the lambda lifting pass that is responsible to enable + closures and closure iterators need to be fixed. +- ``concept`` needs to be refined, a nice name for the feature is not enough. +- Destructors need to be refined. +- make '--implicitStatic:on' the default; then we can also clean up the + 'static[T]' mess in the compiler! + +- Finish the implementation of the 'parallel' statement. +- Deprecate ``immediate`` for templates and macros +- special case varargs[untyped] and varargs[typed] +- make 'nil' work for 'add': + - resizeString + - incrSeq + - addChar version 1.0 =========== +- macro support for '='; bind '=' to a memory region - remove echo $foo gotcha - add "all threads are blocked" detection to 'spawn' - figure out why C++ bootstrapping is so much slower @@ -37,8 +49,6 @@ Low priority: Misc ---- -- make '--implicitStatic:on' the default; then we can also clean up the - 'static[T]' mess in the compiler! - make tuple unpacking work in a non-var/let context - built-in 'getImpl' - prevent 'alloc(TypeWithGCedMemory)' @@ -52,6 +62,7 @@ Bugs - scopes are still broken for generic instantiation! - blocks can "export" an identifier but the CCG generates {} for them ... - ConcreteTypes in a 'case' means we don't check for duplicated case branches +- typedesc matches a generic type T! version 0.9.x diff --git a/tools/niminst/niminst.nim b/tools/niminst/niminst.nim index e50b251d3..f0ae45484 100644 --- a/tools/niminst/niminst.nim +++ b/tools/niminst/niminst.nim @@ -35,6 +35,7 @@ type actionNsis, # action: create NSIS installer actionScripts # action: create install and deinstall scripts actionZip, # action: create zip file + actionTargz, # action: create targz file actionDeb # action: prepare deb package FileCategory = enum @@ -171,6 +172,7 @@ proc parseCmdLine(c: var ConfigData) = of "csource": incl(c.actions, actionCSource) of "scripts": incl(c.actions, actionScripts) of "zip": incl(c.actions, actionZip) + of "targz": incl(c.actions, actionTargz) of "inno": incl(c.actions, actionInno) of "nsis": incl(c.actions, actionNsis) of "deb": incl(c.actions, actionDeb) @@ -181,10 +183,10 @@ proc parseCmdLine(c: var ConfigData) = break of cmdLongoption, cmdShortOption: case normalize(key.string) - of "help", "h": + of "help", "h": stdout.write(Usage) quit(0) - of "version", "v": + of "version", "v": stdout.write(Version & "\n") quit(0) of "o", "output": c.outdir = val @@ -240,7 +242,7 @@ proc incl(s: var seq[string], x: string): int = for i in 0.. <s.len: if cmpIgnoreStyle(s[i], x) == 0: return i s.add(x) - result = s.len-1 + result = s.len-1 proc platforms(c: var ConfigData, v: string) = for line in splitLines(v): @@ -277,17 +279,17 @@ proc parseIniFile(c: var ConfigData) = of "name": c.name = v of "displayname": c.displayName = v of "version": c.version = v - of "os": + of "os": c.oses = split(v, {';'}) hasCpuOs = true if c.explicitPlatforms: quit(errorStr(p, "you cannot have both 'platforms' and 'os'")) - of "cpu": + of "cpu": c.cpus = split(v, {';'}) hasCpuOs = true if c.explicitPlatforms: quit(errorStr(p, "you cannot have both 'platforms' and 'cpu'")) - of "platforms": + of "platforms": platforms(c, v) c.explicitPlatforms = true if hasCpuOs: @@ -389,7 +391,7 @@ proc readCFiles(c: var ConfigData, osA, cpuA: int) = of cfgKeyValuePair: case section of "ccompiler": pathFlags(p, k.key, k.value, c.ccompiler) - of "linker": + of "linker": pathFlags(p, k.key, k.value, c.linker) # HACK: we conditionally add ``-lm -ldl``, so remove them from the # linker flags: @@ -558,28 +560,68 @@ when haveZipLib: else: quit("Cannot open for writing: " & n) +proc targzDist(c: var ConfigData) = + let proj = toLower(c.name) & "-" & c.version + var n = "$#.tar.gz" % proj + let tmpDir = if c.outdir.len == 0: "build" else: c.outdir + + template processFile(z, dest, src) = + let s = src + let d = dest + echo "Copying ", s, " to ", tmpDir / d + let destdir = tmpdir / d.splitFile.dir + if not dirExists(destdir): createDir(destdir) + copyFile(s, tmpDir / d) + + processFile(z, proj / buildBatFile32, "build" / buildBatFile32) + processFile(z, proj / buildBatFile64, "build" / buildBatFile64) + processFile(z, proj / buildShFile, "build" / buildShFile) + processFile(z, proj / makeFile, "build" / makeFile) + processFile(z, proj / installShFile, installShFile) + processFile(z, proj / deinstallShFile, deinstallShFile) + for f in walkFiles(c.libpath / "lib/*.h"): + processFile(z, proj / "c_code" / extractFilename(f), f) + for osA in 1..c.oses.len: + for cpuA in 1..c.cpus.len: + var dir = buildDir(osA, cpuA) + for k, f in walkDir("build" / dir): + if k == pcFile: processFile(z, proj / dir / extractFilename(f), f) + + for cat in items({fcConfig..fcOther, fcUnix}): + for f in items(c.cat[cat]): processFile(z, proj / f, f) + + let oldDir = getCurrentDir() + setCurrentDir(tmpDir) + try: + #if execShellCmd("7z a -ttar $1.tar $1" % proj) != 0 or + # execShellCmd("7z a -tgzip $1.tar.gz $1.tar" % proj) != 0 or + if execShellCmd("7z a -tzip $1.zip $1" % proj) != 0: + echo("External program failed") + finally: + setCurrentDir(oldDir) + # -- prepare build files for .deb creation proc debDist(c: var ConfigData) = if not existsFile(getOutputDir(c) / "build.sh"): quit("No build.sh found.") if not existsFile(getOutputDir(c) / "install.sh"): quit("No install.sh found.") - + if c.debOpts.shortDesc == "": quit("shortDesc must be set in the .ini file.") if c.debOpts.licenses.len == 0: echo("[Warning] No licenses specified for .deb creation.") - + # -- Copy files into /tmp/.. echo("Copying source to tmp/niminst/deb/") var currentSource = getCurrentDir() var workingDir = getTempDir() / "niminst" / "deb" var upstreamSource = (c.name.toLower() & "-" & c.version) - + createDir(workingDir / upstreamSource) - + template copyNimDist(f, dest: string): stmt = createDir((workingDir / upstreamSource / dest).splitFile.dir) copyFile(currentSource / f, workingDir / upstreamSource / dest) - + # Don't copy all files, only the ones specified in the config: copyNimDist(buildShFile, buildShFile) copyNimDist(makeFile, makeFile) @@ -624,5 +666,7 @@ if actionZip in c.actions: zipDist(c) else: quit("libzip is not installed") +if actionTargz in c.actions: + targzDist(c) if actionDeb in c.actions: debDist(c) diff --git a/tools/niminst/nsis.tmpl b/tools/niminst/nsis.tmpl index c21bfb9d5..843a8cf44 100644 --- a/tools/niminst/nsis.tmpl +++ b/tools/niminst/nsis.tmpl @@ -1,5 +1,5 @@ #! stdtmpl(subsChar='?') | standard -#proc generateNsisSetup(c: ConfigData): string = +#proc generateNsisSetup(c: ConfigData): string = # result = "; NSIS script generated by niminst\n" & # "; To regenerate run ``niminst nsis`` or ``koch nsis``\n" @@ -35,8 +35,8 @@ ; Default installation folder ; This is changed later (in .onInit) to the root directory, if possible. - InstallDir "$LOCALAPPDATA\?{c.name}" - + InstallDir "$LOCALAPPDATA\?{c.name}-?{c.version}" + ; Get installation folder from registry if available InstallDirRegKey HKCU "Software\c.name\c.version" "" @@ -86,14 +86,14 @@ !insertmacro MUI_PAGE_INSTFILES !insertmacro MUI_PAGE_FINISH - + ; Setup the uninstaller pages !insertmacro MUI_UNPAGE_CONFIRM !insertmacro MUI_UNPAGE_INSTFILES - + ;-------------------------------- ;Languages - + !insertmacro MUI_LANGUAGE "English" ;-------------------------------- @@ -104,7 +104,7 @@ ; Nim binary. Section "Core Files" CoreSection ; This is a mandotory section - SectionIn RO + SectionIn RO ; Output files to the base installation directory SetOutPath "$INSTDIR" @@ -164,7 +164,7 @@ ; The downloadable sections. These sections are automatically generated by ; niminst and the template filters. - #var i = 0 + #var i = 0 #for download in c.downloads: # inc i # let d = download.split('|') @@ -207,7 +207,7 @@ CreateShortCut "$SMPROGRAMS\$ICONS_GROUP\?{e}.lnk" "$INSTDIR\?dir\?{startMenuEntry.toWin}" !insertmacro MUI_STARTMENU_WRITE_END # end if - + ignore: SectionEnd #end diff --git a/web/assets/style.css b/web/assets/style.css index b74cbc486..17541a118 100644 --- a/web/assets/style.css +++ b/web/assets/style.css @@ -125,8 +125,8 @@ pre .end { background:url("images/tabEnd.png") no-repeat left bottom; } opacity 1s ease-in-out; } #slideshow > div.active { visibility:visible; opacity:1; transition-delay:0s; } #slideshow > div.init { transition-delay:0s; } - #slideshow-nav { z-index:3; position:absolute; top:110px;; right:-12px; } - #slideshow-nav > div { margin:5px 0; width:23px; height:23px; background:url("images/slideshow-nav.png") no-repeat; } + #slideshow-nav { z-index:3; position:absolute; top:341px; left:18px; } + #slideshow-nav > div { display:inline-block; margin:5px 0; width:23px; height:23px; background:url("images/slideshow-nav.png") no-repeat; } #slideshow-nav > div:hover { background-image:url("images/slideshow-nav_active.png"); opacity:0.5; } #slideshow-nav > div.active { background-image:url("images/slideshow-nav_active.png"); opacity:1; } diff --git a/web/documentation.txt b/web/documentation.txt index dbb737cd9..67f8b4070 100644 --- a/web/documentation.txt +++ b/web/documentation.txt @@ -8,13 +8,13 @@ Nim's Documentation .. container:: libraries - - | `Standard Library <lib.html>`_ + - | `Standard Library <docs/lib.html>`_ | This document describes Nim's standard library. - - | `Language Manual <manual.html>`_ + - | `Language Manual <docs/manual.html>`_ | The Nim manual is a draft that will evolve into a proper specification. - - | `Compiler User Guide <nimc.html>`_ + - | `Compiler User Guide <docs/nimc.html>`_ | The user guide lists command line arguments, special features of the compiler, etc. @@ -26,11 +26,11 @@ Nim's Documentation .. container:: tools - - | `Source Code Filters <filters.html>`_ + - | `Source Code Filters <docs/filters.html>`_ | The Nim compiler supports source code filters as a simple yet powerful builtin templating system. - - | `Tools Documentation <tools.html>`_ + - | `Tools Documentation <docs/tools.html>`_ | Description of some tools that come with the standard distribution. @@ -41,16 +41,17 @@ Nim's Documentation .. container:: internals - - | `Garbage Collector <gc.html>`_ + - | `Garbage Collector <docs/gc.html>`_ | Additional documentation about Nim's GC and how to operate it in a realtime setting. - - | `Internal Documentation <intern.html>`_ - | The internal documentation describes how the compiler is implemented. Read - this if you want to hack the compiler. + - | `Internal Documentation <docs/intern.html>`_ + | The internal documentation describes how the compiler is implemented. + Read this if you want to hack the compiler. Search Options -------------- -`Documentation Index <theindex.html>`_ - The generated index. **Index + (Ctrl+F) == Joy** +`Documentation Index <docs/theindex.html>`_ - The generated +index. **Index + (Ctrl+F) == Joy** diff --git a/web/download.txt b/web/download.txt index 3d47467f2..6acc80b53 100644 --- a/web/download.txt +++ b/web/download.txt @@ -13,8 +13,8 @@ Binaries -------- Unfortunately for now we only provide builds for Windows. -* 32 bit: `nim-0.10.2_x32.exe <download/nim-0.10.2_x32.exe>`_ -* 64 bit: `nim-0.10.2_x64.exe <download/nim-0.10.2_x64.exe>`_ +* 32 bit: `nim-0.11.2_x32.exe <download/nim-0.11.2_x32.exe>`_ +* 64 bit: `nim-0.11.2_x64.exe <download/nim-0.11.2_x64.exe>`_ Installation based on generated C code @@ -24,8 +24,12 @@ This installation method is the preferred way for Linux, Mac OS X, and other Uni like systems. Binary packages may be provided later. -Download `nim-0.10.2.zip <download/nim-0.10.2.zip>`_, extract it and follow -these instructions: +Download one of these: + +* `nim-0.11.2.zip (28 MB) <download/nim-0.11.2.zip>`_ +* `nim-0.11.2.tar.xz (2.6MB) <download/nim-0.11.2.tar.xz>`_ + +Extract the file and follow these instructions: * sh build.sh * Add ``$your_install_dir/bin`` to your PATH. diff --git a/web/learn.txt b/web/learn.txt index 7a9600e57..bf0cc43ef 100644 --- a/web/learn.txt +++ b/web/learn.txt @@ -8,10 +8,10 @@ Learning Nim .. container:: tutorials - - | `Tutorial (part I) <tut1.html>`_ + - | `Tutorial (part I) <docs/tut1.html>`_ | Learn the basics of Nim's types, variables, procedures, control flow, etc... - - | `Tutorial (part II) <tut2.html>`_ + - | `Tutorial (part II) <docs/tut2.html>`_ | Learn Nim's more advanced features such as OOP, generics, macros, etc... @@ -52,5 +52,5 @@ Learning Nim Documentation ------------- -More examples of Nim code can be found in the `Nim Language Documentation <manual.html>`_. +More examples of Nim code can be found in the `Nim Language Documentation <docs/manual.html>`_. diff --git a/web/news.txt b/web/news.txt index 0b28c29bf..9719fb8d7 100644 --- a/web/news.txt +++ b/web/news.txt @@ -3,323 +3,423 @@ News ==== .. - 2015-03-01 Version 0.10.4 released + 2015-05-05 Version 0.11.2 released ================================== - Changes affecting backwards compatibility ----------------------------------------- - - Parameter names are finally properly ``gensym``'ed. This can break - templates though that used to rely on the fact that they are not. - (Bug #1915.) This means this doesn't compile anymore: - - .. code-block:: nim - - template doIt(body: stmt) {.immediate.} = - # this used to inject the 'str' parameter: - proc res(str: string) = - body - - doIt: - echo str # Error: undeclared identifier: 'str' - - Declare the ``doIt`` template as ``immediate, dirty`` to get the old - behaviour. - - Tuple field names are not ignored anymore, this caused too many problems - in practice so now the behaviour as it was for version 0.9.6: If field - names exist for the tuple type, they are checked. - - ``logging.level`` and ``logging.handlers`` are no longer exported. - ``addHandler``, ``getHandlers``, ``setLogFilter`` and ``getLogFilter`` - should be used instead. - - ``nim idetools`` has been replaced by a separate tool `nimsuggest`_. - - *arrow like* operators are not right associative anymore. - - Typeless parameters are now only allowed in templates and macros. The old - way turned out to be too error-prone. - - The 'addr' and 'type' operators are now parsed as unary function - application. This means ``type(x).name`` is now parsed as ``(type(x)).name`` - and not as ``type((x).name)``. Note that this also affects the AST - structure; for immediate macro parameters ``nkCall('addr', 'x')`` is - produced instead of ``nkAddr('x')``. - - ``concept`` is now a keyword and is used instead of ``generic``. - - The ``inc``, ``dec``, ``+=``, ``-=`` builtins now produce OverflowError - exceptions. This means code like the following: - - .. code-block:: nim - var x = low(T) - while x <= high(T): - echo x - inc x - - Needs to be replaced by something like this: - - .. code-block:: nim - var x = low(T).int - while x <= high(T).int: - echo x.T - inc x - - - **Negative indexing for slicing does not work anymore!** Instead - of ``a[0.. -1]`` you can - use ``a[0.. ^1]``. This also works with accessing a single - element ``a[^1]``. Note that we cannot detect this reliably as it is - determined at **runtime** whether negative indexing is used! - ``a[0.. -1]`` now produces the empty string/sequence. - - The compiler now warns about code like ``foo +=1`` which uses inconsistent - spacing around binary operators. Later versions of the language will parse - these as unary operators instead so that ``echo $foo`` finally can do what - people expect it to do. - Language Additions ------------------ - - For empty ``case object`` branches ``discard`` can finally be used instead - of ``nil``. - - Automatic dereferencing is now done for the first argument of a routine - call if overloading resolution produces no match otherwise. This feature - has to be enabled with the `experimental`_ pragma. - - Objects that do not use inheritance nor ``case`` can be put into ``const`` - sections. This means that finally this is possible and produces rather - nice code: - .. code-block:: nim - import tables + Bugfixes + -------- - const - foo = {"ah": "finally", "this": "is", "possible.": "nice!"}.toTable() +2015-05-04 Version 0.11.2 released +================================== - - Ordinary parameters can follow after a varargs parameter. This means the - following is finally accepted by the compiler: +This is just a bugfix release that fixes the most pressing regressions we +introduced with version 0.11.0. The way types are computed was +changed significantly causing all sort of problems. Sorry for the +inconvenience; we grew overconfident our large test suite would prevent these +things. - .. code-block:: nim - template takesBlock(a, b: int, x: varargs[expr]; blck: stmt) = - blck - echo a, b - takesBlock 1, 2, "some", 0.90, "random stuff": - echo "yay" +2015-04-30 Version 0.11.0 released +================================== - - Overloading by 'var T' is now finally possible: +With this release we are one step closer to reaching version 1.0 and by +extension the persistence of the Nim specification. As mentioned in the +previous release notes, starting with version 1.0, we will not be introducing +any more breaking changes to Nim. - .. code-block:: nim - proc varOrConst(x: var int) = echo "var" - proc varOrConst(x: int) = echo "const" +The *language* itself is very close to 1.0, the primary area that requires +more work is the standard library. - var x: int - varOrConst(x) # "var" - varOrConst(45) # "const" +Take a look at the `download <download.html>`_ page for binaries (Windows-only) +and 0.11.0 snapshots of the source code. The Windows installer now also +includes `Aporia <https://github.com/nim-lang/aporia>`_, +`Nimble <https://github.com/nim-lang/nimble>`_ and other useful tools to get +you started with Nim. - - Array and seq indexing can now use the builtin ``^`` operator to access - things from backwards: ``a[^1]`` is like Python's ``a[-1]``. +What's left to be done +~~~~~~~~~~~~~~~~~~~~~~ +The 1.0 release is expected by the end of this year. Rumors say it will be in +summer 2015. What's left: - Library additions - ----------------- +* Bug fixes, bug fixes, bug fixes, in particular: + - The remaining bugs of the lambda lifting pass that is responsible to enable + closures and closure iterators need to be fixed. + - ``concept`` needs to be refined, a nice name for the feature is not enough. + - Destructors need to be refined. + - ``static[T]`` needs to be fixed. + - Finish the implementation of the 'parallel' statement. +* ``immediate`` templates and macros will be deprecated as these will soon be + completely unnecessary, instead the ``typed`` or ``untyped`` metatypes can + be used. +* More of the standard library should be moved to Nimble packages and what's + left should use the features we have for concurrency and parallelism. - - ``reversed`` proc added to the ``unicode`` module. - - Added multipart param to httpclient's ``post`` and ``postContent`` together - with a ``newMultipartData`` proc. - - Added `%*` operator for JSON. - - The compiler is now available as Nimble package for c2nim. - Bugfixes - -------- +Changes affecting backwards compatibility +----------------------------------------- + +- Parameter names are finally properly ``gensym``'ed. This can break + templates though that used to rely on the fact that they are not. + (Bug #1915.) This means this doesn't compile anymore: + +.. code-block:: nim + + template doIt(body: stmt) {.immediate.} = + # this used to inject the 'str' parameter: + proc res(str: string) = + body + + doIt: + echo str # Error: undeclared identifier: 'str' +.. + + This used to inject the ``str`` parameter into the scope of the body. + Declare the ``doIt`` template as ``immediate, dirty`` to get the old + behaviour. +- Tuple field names are not ignored anymore, this caused too many problems + in practice so now the behaviour is as it was for version 0.9.6: If field + names exist for the tuple type, they are checked. +- ``logging.level`` and ``logging.handlers`` are no longer exported. + ``addHandler``, ``getHandlers``, ``setLogFilter`` and ``getLogFilter`` + should be used instead. +- ``nim idetools`` has been replaced by a separate + tool `nimsuggest <0.11.0/nimsuggest.html>`_. +- *arrow like* operators are not right associative anymore and are required + to end with either ``->``, ``~>`` or + ``=>``, not just ``>``. Examples of operators still considered arrow like: + ``->``, ``==>``, ``+=>``. On the other hand, the following operators are now + considered regular operators again: ``|>``, ``-+>``, etc. +- Typeless parameters are now only allowed in templates and macros. The old + way turned out to be too error-prone. +- The 'addr' and 'type' operators are now parsed as unary function + application. This means ``type(x).name`` is now parsed as ``(type(x)).name`` + and not as ``type((x).name)``. Note that this also affects the AST + structure; for immediate macro parameters ``nkCall('addr', 'x')`` is + produced instead of ``nkAddr('x')``. +- ``concept`` is now a keyword and is used instead of ``generic``. +- The ``inc``, ``dec``, ``+=``, ``-=`` builtins now produce OverflowError + exceptions. This means code like the following: + +.. code-block:: nim + var x = low(T) + while x <= high(T): + echo x + inc x + +Needs to be replaced by something like this: + +.. code-block:: nim + var x = low(T).int + while x <= high(T).int: + echo x.T + inc x + +- **Negative indexing for slicing does not work anymore!** Instead + of ``a[0.. -1]`` you can + use ``a[0.. ^1]``. This also works with accessing a single + element ``a[^1]``. Note that we cannot detect this reliably as it is + determined at **runtime** whether negative indexing is used! + ``a[0.. -1]`` now produces the empty string/sequence. +- The compiler now warns about code like ``foo +=1`` which uses inconsistent + spacing around binary operators. Later versions of the language will parse + these as unary operators instead so that ``echo $foo`` finally can do what + people expect it to do. +- ``system.untyped`` and ``system.typed`` have been introduced as aliases + for ``expr`` and ``stmt``. The new names capture the semantics much better + and most likely ``expr`` and ``stmt`` will be deprecated in favor of the + new names. +- The ``split`` method in module ``re`` has changed. It now handles the case + of matches having a length of 0, and empty strings being yielded from the + iterator. A notable change might be that a pattern being matched at the + beginning and end of a string, will result in an empty string being produced + at the start and the end of the iterator. +- The compiler and nimsuggest now count columns starting with 1, not 0 for + consistency with the rest of the world. + + +Language Additions +------------------ + +- For empty ``case object`` branches ``discard`` can finally be used instead + of ``nil``. +- Automatic dereferencing is now done for the first argument of a routine + call if overloading resolution produces no match otherwise. This feature + has to be enabled with + the `experimental <0.11.0/manual.html#pragmas-experimental-pragma>`_ pragma. +- Objects that do not use inheritance nor ``case`` can be put into ``const`` + sections. This means that finally this is possible and produces rather + nice code: + +.. code-block:: nim + import tables + + const + foo = {"ah": "finally", "this": "is", "possible.": "nice!"}.toTable() + + +- Ordinary parameters can follow after a varargs parameter. This means the + following is finally accepted by the compiler: + +.. code-block:: nim + template takesBlock(a, b: int, x: varargs[expr]; blck: stmt) = + blck + echo a, b + + takesBlock 1, 2, "some", 0.90, "random stuff": + echo "yay" + +- Overloading by 'var T' is now finally possible: + +.. code-block:: nim + proc varOrConst(x: var int) = echo "var" + proc varOrConst(x: int) = echo "const" + + var x: int + varOrConst(x) # "var" + varOrConst(45) # "const" + +- Array and seq indexing can now use the builtin ``^`` operator to access + things from backwards: ``a[^1]`` is like Python's ``a[-1]``. +- A first version of the specification and implementation of the overloading + of the assignment operator has arrived! +- ``system.len`` for strings and sequences now returns 0 for nil. + +- A single underscore can now be used to discard values when unpacking tuples: + +.. code-block:: nim + let (path, _, _) = os.splitFile("path/file.ext") + + +- ``marshal.$$`` and ``marshal.to`` can be executed at compile-time. +- Interoperability with C++ improved tremendously; C++'s templates and + operators can be wrapped directly. See + `this <0.11.0/nimc.html#additional-features-importcpp-pragma>`_ + for more information. +- ``macros.getType`` can be used to query an AST's type at compile-time. This + enables more powerful macros, for instance *currying* can now be done with + a macro. + + +Library additions +----------------- + +- ``reversed`` proc added to the ``unicode`` module. +- Added multipart param to httpclient's ``post`` and ``postContent`` together + with a ``newMultipartData`` proc. +- Added `%*` operator for JSON. +- The compiler is now available as Nimble package for c2nim. +- Added ``..^`` and ``..<`` templates to system so that the rather annoying + space between ``.. <`` and ``.. ^`` is not necessary anymore. +- Added ``system.xlen`` for strings and sequences to get back the old ``len`` + operation that doesn't check for ``nil`` for efficiency. + + +Bugfixes +-------- + +- Fixed internal compiler error when using ``char()`` in an echo call + (`#1788 <https://github.com/Araq/Nim/issues/1788>`_). +- Fixed Windows cross-compilation on Linux. +- Overload resolution now works for types distinguished only by a + ``static[int]`` param + (`#1056 <https://github.com/Araq/Nim/issues/1056>`_). +- Other fixes relating to generic types and static params. +- Fixed some compiler crashes with unnamed tuples + (`#1774 <https://github.com/Araq/Nim/issues/1774>`_). +- Fixed ``channels.tryRecv`` blocking + (`#1816 <https://github.com/Araq/Nim/issues/1816>`_). +- Fixed generic instantiation errors with ``typedesc`` + (`#419 <https://github.com/Araq/Nim/issues/419>`_). +- Fixed generic regression where the compiler no longer detected constant + expressions properly (`#544 <https://github.com/Araq/Nim/issues/544>`_). +- Fixed internal error with generic proc using ``static[T]`` in a specific + way (`#1049 <https://github.com/Araq/Nim/issues/1049>`_). +- More fixes relating to generics (`#1820 <https://github.com/Araq/Nim/issues/1820>`_, + `#1050 <https://github.com/Araq/Nim/issues/1050>`_, + `#1859 <https://github.com/Araq/Nim/issues/1859>`_, + `#1858 <https://github.com/Araq/Nim/issues/1858>`_). +- Fixed httpclient to properly encode queries. +- Many fixes to the ``uri`` module. +- Async sockets are now closed on error. +- Fixes to httpclient's handling of multipart data. +- Fixed GC segfaults with asynchronous sockets + (`#1796 <https://github.com/Araq/Nim/issues/1796>`_). +- Added more versions to openssl's DLL version list + (`076f993 <https://github.com/Araq/Nim/commit/076f993>`_). +- Fixed shallow copy in iterators being broken + (`#1803 <https://github.com/Araq/Nim/issues/1803>`_). +- ``nil`` can now be inserted into tables with the ``db_sqlite`` module + (`#1866 <https://github.com/Araq/Nim/issues/1866>`_). +- Fixed "Incorrect assembler generated" + (`#1907 <https://github.com/Araq/Nim/issues/1907>`_) +- Fixed "Expression templates that define macros are unusable in some contexts" + (`#1903 <https://github.com/Araq/Nim/issues/1903>`_) +- Fixed "a second level generic subclass causes the compiler to crash" + (`#1919 <https://github.com/Araq/Nim/issues/1919>`_) +- Fixed "nim 0.10.2 generates invalid AsyncHttpClient C code for MSVC " + (`#1901 <https://github.com/Araq/Nim/issues/1901>`_) +- Fixed "1 shl n produces wrong C code" + (`#1928 <https://github.com/Araq/Nim/issues/1928>`_) +- Fixed "Internal error on tuple yield" + (`#1838 <https://github.com/Araq/Nim/issues/1838>`_) +- Fixed "ICE with template" + (`#1915 <https://github.com/Araq/Nim/issues/1915>`_) +- Fixed "include the tool directory in the installer as it is required by koch" + (`#1947 <https://github.com/Araq/Nim/issues/1947>`_) +- Fixed "Can't compile if file location contains spaces on Windows" + (`#1955 <https://github.com/Araq/Nim/issues/1955>`_) +- Fixed "List comprehension macro only supports infix checks as guards" + (`#1920 <https://github.com/Araq/Nim/issues/1920>`_) +- Fixed "wrong field names of compatible tuples in generic types" + (`#1910 <https://github.com/Araq/Nim/issues/1910>`_) +- Fixed "Macros within templates no longer work as expected" + (`#1944 <https://github.com/Araq/Nim/issues/1944>`_) +- Fixed "Compiling for Standalone AVR broken in 0.10.2" + (`#1964 <https://github.com/Araq/Nim/issues/1964>`_) +- Fixed "Compiling for Standalone AVR broken in 0.10.2" + (`#1964 <https://github.com/Araq/Nim/issues/1964>`_) +- Fixed "Code generation for mitems with tuple elements" + (`#1833 <https://github.com/Araq/Nim/issues/1833>`_) +- Fixed "httpclient.HttpMethod should not be an enum" + (`#1962 <https://github.com/Araq/Nim/issues/1962>`_) +- Fixed "terminal / eraseScreen() throws an OverflowError" + (`#1906 <https://github.com/Araq/Nim/issues/1906>`_) +- Fixed "setControlCHook(nil) disables registered quit procs" + (`#1546 <https://github.com/Araq/Nim/issues/1546>`_) +- Fixed "Unexpected idetools behaviour" + (`#325 <https://github.com/Araq/Nim/issues/325>`_) +- Fixed "Unused lifted lambda does not compile" + (`#1642 <https://github.com/Araq/Nim/issues/1642>`_) +- Fixed "'low' and 'high' don't work with cstring asguments" + (`#2030 <https://github.com/Araq/Nim/issues/2030>`_) +- Fixed "Converting to int does not round in JS backend" + (`#1959 <https://github.com/Araq/Nim/issues/1959>`_) +- Fixed "Internal error genRecordField 2 when adding region to pointer." + (`#2039 <https://github.com/Araq/Nim/issues/2039>`_) +- Fixed "Macros fail to compile when compiled with --os:standalone" + (`#2041 <https://github.com/Araq/Nim/issues/2041>`_) +- Fixed "Reading from {.compileTime.} variables can cause code generation to fail" + (`#2022 <https://github.com/Araq/Nim/issues/2022>`_) +- Fixed "Passing overloaded symbols to templates fails inside generic procedures" + (`#1988 <https://github.com/Araq/Nim/issues/1988>`_) +- Fixed "Compiling iterator with object assignment in release mode causes "var not init"" + (`#2023 <https://github.com/Araq/Nim/issues/2023>`_) +- Fixed "calling a large number of macros doing some computation fails" + (`#1989 <https://github.com/Araq/Nim/issues/1989>`_) +- Fixed "Can't get Koch to install nim under Windows" + (`#2061 <https://github.com/Araq/Nim/issues/2061>`_) +- Fixed "Template with two stmt parameters segfaults compiler" + (`#2057 <https://github.com/Araq/Nim/issues/2057>`_) +- Fixed "`noSideEffect` not affected by `echo`" + (`#2011 <https://github.com/Araq/Nim/issues/2011>`_) +- Fixed "Compiling with the cpp backend ignores --passc" + (`#1601 <https://github.com/Araq/Nim/issues/1601>`_) +- Fixed "Put untyped procedure parameters behind the experimental pragma" + (`#1956 <https://github.com/Araq/Nim/issues/1956>`_) +- Fixed "generic regression" + (`#2073 <https://github.com/Araq/Nim/issues/2073>`_) +- Fixed "generic regression" + (`#2073 <https://github.com/Araq/Nim/issues/2073>`_) +- Fixed "Regression in template lookup with generics" + (`#2004 <https://github.com/Araq/Nim/issues/2004>`_) +- Fixed "GC's growObj is wrong for edge cases" + (`#2070 <https://github.com/Araq/Nim/issues/2070>`_) +- Fixed "Compiler internal error when creating an array out of a typeclass" + (`#1131 <https://github.com/Araq/Nim/issues/1131>`_) +- Fixed "GC's growObj is wrong for edge cases" + (`#2070 <https://github.com/Araq/Nim/issues/2070>`_) +- Fixed "Invalid Objective-C code generated when calling class method" + (`#2068 <https://github.com/Araq/Nim/issues/2068>`_) +- Fixed "walkDirRec Error" + (`#2116 <https://github.com/Araq/Nim/issues/2116>`_) +- Fixed "Typo in code causes compiler SIGSEGV in evalAtCompileTime" + (`#2113 <https://github.com/Araq/Nim/issues/2113>`_) +- Fixed "Regression on exportc" + (`#2118 <https://github.com/Araq/Nim/issues/2118>`_) +- Fixed "Error message" + (`#2102 <https://github.com/Araq/Nim/issues/2102>`_) +- Fixed "hint[path] = off not working in nim.cfg" + (`#2103 <https://github.com/Araq/Nim/issues/2103>`_) +- Fixed "compiler crashes when getting a tuple from a sequence of generic tuples" + (`#2121 <https://github.com/Araq/Nim/issues/2121>`_) +- Fixed "nim check hangs with when" + (`#2123 <https://github.com/Araq/Nim/issues/2123>`_) +- Fixed "static[T] param in nested type resolve/caching issue" + (`#2125 <https://github.com/Araq/Nim/issues/2125>`_) +- Fixed "repr should display ``\0``" + (`#2124 <https://github.com/Araq/Nim/issues/2124>`_) +- Fixed "'nim check' never ends in case of recursive dependency " + (`#2051 <https://github.com/Araq/Nim/issues/2051>`_) +- Fixed "From macros: Error: unhandled exception: sons is not accessible" + (`#2167 <https://github.com/Araq/Nim/issues/2167>`_) +- Fixed "`fieldPairs` doesn't work inside templates" + (`#1902 <https://github.com/Araq/Nim/issues/1902>`_) +- Fixed "fields iterator misbehavior on break statement" + (`#2134 <https://github.com/Araq/Nim/issues/2134>`_) +- Fixed "Fix for compiler not building anymore since #c3244ef1ff" + (`#2193 <https://github.com/Araq/Nim/issues/2193>`_) +- Fixed "JSON parser fails in cpp output mode" + (`#2199 <https://github.com/Araq/Nim/issues/2199>`_) +- Fixed "macros.getType mishandles void return" + (`#2211 <https://github.com/Araq/Nim/issues/2211>`_) +- Fixed "Regression involving templates instantiated within generics" + (`#2215 <https://github.com/Araq/Nim/issues/2215>`_) +- Fixed ""Error: invalid type" for 'not nil' on generic type." + (`#2216 <https://github.com/Araq/Nim/issues/2216>`_) +- Fixed "--threads:on breaks async" + (`#2074 <https://github.com/Araq/Nim/issues/2074>`_) +- Fixed "Type mismatch not always caught, can generate bad code for C backend." + (`#2169 <https://github.com/Araq/Nim/issues/2169>`_) +- Fixed "Failed C compilation when storing proc to own type in object" + (`#2233 <https://github.com/Araq/Nim/issues/2233>`_) +- Fixed "Unknown line/column number in constant declaration type conversion error" + (`#2252 <https://github.com/Araq/Nim/issues/2252>`_) +- Fixed "Adding {.compile.} fails if nimcache already exists." + (`#2247 <https://github.com/Araq/Nim/issues/2247>`_) +- Fixed "Two different type names generated for a single type (C backend)" + (`#2250 <https://github.com/Araq/Nim/issues/2250>`_) +- Fixed "Ambigous call when it should not be" + (`#2229 <https://github.com/Araq/Nim/issues/2229>`_) +- Fixed "Make sure we can load root urls" + (`#2227 <https://github.com/Araq/Nim/issues/2227>`_) +- Fixed "Failure to slice a string with an int subrange type" + (`#794 <https://github.com/Araq/Nim/issues/794>`_) +- Fixed "documentation error" + (`#2205 <https://github.com/Araq/Nim/issues/2205>`_) +- Fixed "Code growth when using `const`" + (`#1940 <https://github.com/Araq/Nim/issues/1940>`_) +- Fixed "Instances of generic types confuse overload resolution" + (`#2220 <https://github.com/Araq/Nim/issues/2220>`_) +- Fixed "Compiler error when initializing sdl2's EventType" + (`#2316 <https://github.com/Araq/Nim/issues/2316>`_) +- Fixed "Parallel disjoint checking can't handle `<`, `items`, or arrays" + (`#2287 <https://github.com/Araq/Nim/issues/2287>`_) +- Fixed "Strings aren't copied in parallel loop" + (`#2286 <https://github.com/Araq/Nim/issues/2286>`_) +- Fixed "JavaScript compiler crash with tables" + (`#2298 <https://github.com/Araq/Nim/issues/2298>`_) +- Fixed "Range checker too restrictive" + (`#1845 <https://github.com/Araq/Nim/issues/1845>`_) +- Fixed "Failure to slice a string with an int subrange type" + (`#794 <https://github.com/Araq/Nim/issues/794>`_) +- Fixed "Remind user when compiling in debug mode" + (`#1868 <https://github.com/Araq/Nim/issues/1868>`_) +- Fixed "Compiler user guide has jumbled options/commands." + (`#1819 <https://github.com/Araq/Nim/issues/1819>`_) +- Fixed "using `method`: 1 in a objects constructor fails when compiling" + (`#1791 <https://github.com/Araq/Nim/issues/1791>`_) - - Fixed internal compiler error when using ``char()`` in an echo call - (`#1788 <https://github.com/Araq/Nim/issues/1788>`_). - - Fixed Windows cross-compilation on Linux. - - Overload resolution now works for types distinguished only by a - ``static[int]`` param - (`#1056 <https://github.com/Araq/Nim/issues/1056>`_). - - Other fixes relating to generic types and static params. - - Fixed some compiler crashes with unnamed tuples - (`#1774 <https://github.com/Araq/Nim/issues/1774>`_). - - Fixed ``channels.tryRecv`` blocking - (`#1816 <https://github.com/Araq/Nim/issues/1816>`_). - - Fixed generic instantiation errors with ``typedesc`` - (`#419 <https://github.com/Araq/Nim/issues/419>`_). - - Fixed generic regression where the compiler no longer detected constant - expressions properly (`#544 <https://github.com/Araq/Nim/issues/544>`_). - - Fixed internal error with generic proc using ``static[T]`` in a specific - way (`#1049 <https://github.com/Araq/Nim/issues/1049>`_). - - More fixes relating to generics - (`#1820 <https://github.com/Araq/Nim/issues/1820>`_, - `#1050 <https://github.com/Araq/Nim/issues/1050>`_, - `#1859 <https://github.com/Araq/Nim/issues/1859>`_, - `#1858 <https://github.com/Araq/Nim/issues/1858>`_). - - Fixed httpclient to properly encode queries. - - Many fixes to the ``uri`` module. - - Async sockets are now closed on error. - - Fixes to httpclient's handling of multipart data. - - Fixed GC segfaults with asynchronous sockets - (`#1796 <https://github.com/Araq/Nim/issues/1796>`_). - - Added more versions to openssl's DLL version list - (`076f993 <https://github.com/Araq/Nim/commit/076f993>`_). - - Fixed shallow copy in iterators being broken - (`#1803 <https://github.com/Araq/Nim/issues/1803>`_). - - ``nil`` can now be inserted into tables with the ``db_sqlite`` module - (`#1866 <https://github.com/Araq/Nim/issues/1866>`_). - - Fixed "Incorrect assembler generated" - (`#1907 <https://github.com/Araq/Nim/issues/1907>`_) - - Fixed "Expression templates that define macros are unusable in some contexts" - (`#1903 <https://github.com/Araq/Nim/issues/1903>`_) - - Fixed "a second level generic subclass causes the compiler to crash" - (`#1919 <https://github.com/Araq/Nim/issues/1919>`_) - - Fixed "nim 0.10.2 generates invalid AsyncHttpClient C code for MSVC " - (`#1901 <https://github.com/Araq/Nim/issues/1901>`_) - - Fixed "1 shl n produces wrong C code" - (`#1928 <https://github.com/Araq/Nim/issues/1928>`_) - - Fixed "Internal error on tuple yield" - (`#1838 <https://github.com/Araq/Nim/issues/1838>`_) - - Fixed "ICE with template" - (`#1915 <https://github.com/Araq/Nim/issues/1915>`_) - - Fixed "include the tool directory in the installer as it is required by koch" - (`#1947 <https://github.com/Araq/Nim/issues/1947>`_) - - Fixed "Can't compile if file location contains spaces on Windows" - (`#1955 <https://github.com/Araq/Nim/issues/1955>`_) - - Fixed "List comprehension macro only supports infix checks as guards" - (`#1920 <https://github.com/Araq/Nim/issues/1920>`_) - - Fixed "wrong field names of compatible tuples in generic types" - (`#1910 <https://github.com/Araq/Nim/issues/1910>`_) - - Fixed "Macros within templates no longer work as expected" - (`#1944 <https://github.com/Araq/Nim/issues/1944>`_) - - Fixed "Compiling for Standalone AVR broken in 0.10.2" - (`#1964 <https://github.com/Araq/Nim/issues/1964>`_) - - Fixed "Compiling for Standalone AVR broken in 0.10.2" - (`#1964 <https://github.com/Araq/Nim/issues/1964>`_) - - Fixed "Code generation for mitems with tuple elements" - (`#1833 <https://github.com/Araq/Nim/issues/1833>`_) - - Fixed "httpclient.HttpMethod should not be an enum" - (`#1962 <https://github.com/Araq/Nim/issues/1962>`_) - - Fixed "terminal / eraseScreen() throws an OverflowError" - (`#1906 <https://github.com/Araq/Nim/issues/1906>`_) - - Fixed "setControlCHook(nil) disables registered quit procs" - (`#1546 <https://github.com/Araq/Nim/issues/1546>`_) - - Fixed "Unexpected idetools behaviour" - (`#325 <https://github.com/Araq/Nim/issues/325>`_) - - Fixed "Unused lifted lambda does not compile" - (`#1642 <https://github.com/Araq/Nim/issues/1642>`_) - - Fixed "'low' and 'high' don't work with cstring asguments" - (`#2030 <https://github.com/Araq/Nim/issues/2030>`_) - - Fixed "Converting to int does not round in JS backend" - (`#1959 <https://github.com/Araq/Nim/issues/1959>`_) - - Fixed "Internal error genRecordField 2 when adding region to pointer." - (`#2039 <https://github.com/Araq/Nim/issues/2039>`_) - - Fixed "Macros fail to compile when compiled with --os:standalone" - (`#2041 <https://github.com/Araq/Nim/issues/2041>`_) - - Fixed "Reading from {.compileTime.} variables can cause code generation to fail" - (`#2022 <https://github.com/Araq/Nim/issues/2022>`_) - - Fixed "Passing overloaded symbols to templates fails inside generic procedures" - (`#1988 <https://github.com/Araq/Nim/issues/1988>`_) - - Fixed "Compiling iterator with object assignment in release mode causes "var not init"" - (`#2023 <https://github.com/Araq/Nim/issues/2023>`_) - - Fixed "calling a large number of macros doing some computation fails" - (`#1989 <https://github.com/Araq/Nim/issues/1989>`_) - - Fixed "Can't get Koch to install nim under Windows" - (`#2061 <https://github.com/Araq/Nim/issues/2061>`_) - - Fixed "Template with two stmt parameters segfaults compiler" - (`#2057 <https://github.com/Araq/Nim/issues/2057>`_) - - Fixed "`noSideEffect` not affected by `echo`" - (`#2011 <https://github.com/Araq/Nim/issues/2011>`_) - - Fixed "Compiling with the cpp backend ignores --passc" - (`#1601 <https://github.com/Araq/Nim/issues/1601>`_) - - Fixed "Put untyped procedure parameters behind the experimental pragma" - (`#1956 <https://github.com/Araq/Nim/issues/1956>`_) - - Fixed "generic regression" - (`#2073 <https://github.com/Araq/Nim/issues/2073>`_) - - Fixed "generic regression" - (`#2073 <https://github.com/Araq/Nim/issues/2073>`_) - - Fixed "Regression in template lookup with generics" - (`#2004 <https://github.com/Araq/Nim/issues/2004>`_) - - Fixed "GC's growObj is wrong for edge cases" - (`#2070 <https://github.com/Araq/Nim/issues/2070>`_) - - Fixed "Compiler internal error when creating an array out of a typeclass" - (`#1131 <https://github.com/Araq/Nim/issues/1131>`_) - - Fixed "GC's growObj is wrong for edge cases" - (`#2070 <https://github.com/Araq/Nim/issues/2070>`_) - - Fixed "Invalid Objective-C code generated when calling class method" - (`#2068 <https://github.com/Araq/Nim/issues/2068>`_) - - Fixed "walkDirRec Error" - (`#2116 <https://github.com/Araq/Nim/issues/2116>`_) - - Fixed "Typo in code causes compiler SIGSEGV in evalAtCompileTime" - (`#2113 <https://github.com/Araq/Nim/issues/2113>`_) - - Fixed "Regression on exportc" - (`#2118 <https://github.com/Araq/Nim/issues/2118>`_) - - Fixed "Error message" - (`#2102 <https://github.com/Araq/Nim/issues/2102>`_) - - Fixed "hint[path] = off not working in nim.cfg" - (`#2103 <https://github.com/Araq/Nim/issues/2103>`_) - - Fixed "compiler crashes when getting a tuple from a sequence of generic tuples" - (`#2121 <https://github.com/Araq/Nim/issues/2121>`_) - - Fixed "nim check hangs with when" - (`#2123 <https://github.com/Araq/Nim/issues/2123>`_) - - Fixed "static[T] param in nested type resolve/caching issue" - (`#2125 <https://github.com/Araq/Nim/issues/2125>`_) - - Fixed "repr should display ``\0``" - (`#2124 <https://github.com/Araq/Nim/issues/2124>`_) - - Fixed "'nim check' never ends in case of recursive dependency " - (`#2051 <https://github.com/Araq/Nim/issues/2051>`_) - - Fixed "From macros: Error: unhandled exception: sons is not accessible" - (`#2167 <https://github.com/Araq/Nim/issues/2167>`_) - - Fixed "`fieldPairs` doesn't work inside templates" - (`#1902 <https://github.com/Araq/Nim/issues/1902>`_) - - Fixed "fields iterator misbehavior on break statement" - (`#2134 <https://github.com/Araq/Nim/issues/2134>`_) - - Fixed "Fix for compiler not building anymore since #c3244ef1ff" - (`#2193 <https://github.com/Araq/Nim/issues/2193>`_) - - Fixed "JSON parser fails in cpp output mode" - (`#2199 <https://github.com/Araq/Nim/issues/2199>`_) - - Fixed "macros.getType mishandles void return" - (`#2211 <https://github.com/Araq/Nim/issues/2211>`_) - - Fixed "Regression involving templates instantiated within generics" - (`#2215 <https://github.com/Araq/Nim/issues/2215>`_) - - Fixed ""Error: invalid type" for 'not nil' on generic type." - (`#2216 <https://github.com/Araq/Nim/issues/2216>`_) - - Fixed "--threads:on breaks async" - (`#2074 <https://github.com/Araq/Nim/issues/2074>`_) - - Fixed "Type mismatch not always caught, can generate bad code for C backend." - (`#2169 <https://github.com/Araq/Nim/issues/2169>`_) - - Fixed "Failed C compilation when storing proc to own type in object" - (`#2233 <https://github.com/Araq/Nim/issues/2233>`_) - - Fixed "Unknown line/column number in constant declaration type conversion error" - (`#2252 <https://github.com/Araq/Nim/issues/2252>`_) - - Fixed "Adding {.compile.} fails if nimcache already exists." - (`#2247 <https://github.com/Araq/Nim/issues/2247>`_) - - Fixed "Two different type names generated for a single type (C backend)" - (`#2250 <https://github.com/Araq/Nim/issues/2250>`_) - - Fixed "Ambigous call when it should not be" - (`#2229 <https://github.com/Araq/Nim/issues/2229>`_) - - Fixed "Make sure we can load root urls" - (`#2227 <https://github.com/Araq/Nim/issues/2227>`_) - - Fixed "Failure to slice a string with an int subrange type" - (`#794 <https://github.com/Araq/Nim/issues/794>`_) - - Fixed "documentation error" - (`#2205 <https://github.com/Araq/Nim/issues/2205>`_) - - Fixed "Code growth when using `const`" - (`#1940 <https://github.com/Araq/Nim/issues/1940>`_) - - Fixed "Instances of generic types confuse overload resolution" - (`#2220 <https://github.com/Araq/Nim/issues/2220>`_) - - Fixed "Compiler error when initializing sdl2's EventType" - (`#2316 <https://github.com/Araq/Nim/issues/2316>`_) - - Fixed "Parallel disjoint checking can't handle `<`, `items`, or arrays" - (`#2287 <https://github.com/Araq/Nim/issues/2287>`_) - - Fixed "Strings aren't copied in parallel loop" - (`#2286 <https://github.com/Araq/Nim/issues/2286>`_) - - Fixed "JavaScript compiler crash with tables" - (`#2298 <https://github.com/Araq/Nim/issues/2298>`_) - - Fixed "Range checker too restrictive" - (`#1845 <https://github.com/Araq/Nim/issues/1845>`_) - - Fixed "Failure to slice a string with an int subrange type" - (`#794 <https://github.com/Araq/Nim/issues/794>`_) - - Fixed "Remind user when compiling in debug mode" - (`#1868 <https://github.com/Araq/Nim/issues/1868>`_) - - Fixed "Compiler user guide has jumbled options/commands." - (`#1819 <https://github.com/Araq/Nim/issues/1819>`_) - - Fixed "using `method`: 1 in a objects constructor fails when compiling" - (`#1791 <https://github.com/Araq/Nim/issues/1791>`_) 2014-12-29 Version 0.10.2 released ================================== diff --git a/web/question.txt b/web/question.txt index 0733a2455..c4d997922 100644 --- a/web/question.txt +++ b/web/question.txt @@ -16,11 +16,11 @@ General FAQ language that tries to give the programmer ultimate power without compromises on runtime efficiency. This means it focuses on compile-time mechanisms in all their - various forms. Beneath a nice infix/indentation based syntax with a - powerful (AST based, hygienic) macro system lies a semantic model that supports - a soft realtime GC on thread local heaps. Asynchronous message passing is used - between threads, so no "stop the world" mechanism is necessary. An unsafe - shared memory heap is also provided for the increased efficiency that results + various forms. Beneath a nice infix/indentation based syntax with a + powerful (AST based, hygienic) macro system lies a semantic model that supports + a soft realtime GC on thread local heaps. Asynchronous message passing is used + between threads, so no "stop the world" mechanism is necessary. An unsafe + shared memory heap is also provided for the increased efficiency that results from that model. @@ -29,8 +29,8 @@ General FAQ Why yet another programming language? ------------------------------------- - Nim is one of the very few *programmable* statically typed languages, and - one of the even fewer that produces native binaries that require no + Nim is one of the very few *programmable* statically typed languages, and + one of the even fewer that produces native binaries that require no runtime or interpreter. @@ -48,12 +48,12 @@ General FAQ What is Nim's take on concurrency? ---------------------------------- - Nim primarily focusses on thread local (and garbage collected) heaps and - message passing between threads. Each thread has its own GC, so no + Nim primarily focusses on thread local (and garbage collected) heaps and + message passing between threads. Each thread has its own GC, so no "stop the world" mechanism is necessary. An unsafe shared memory heap is also provided. - Future versions will additionally include a GC "per thread group" + Future versions will additionally include a GC "per thread group" and Nim's type system will be enhanced to accurately model this shared memory heap. @@ -74,7 +74,7 @@ General FAQ ------------------ The compiler is in development and some important features are still missing. - However, the compiler is quite stable already: It is able to compile itself + However, the compiler is quite stable already: It is able to compile itself and a substantial body of other code. Until version 1.0.0 is released, minor incompatibilities with older versions of the compiler will be introduced. @@ -83,9 +83,9 @@ General FAQ How fast is Nim? ---------------- - Benchmarks show it to be comparable to C. Some language features (methods, + Benchmarks show it to be comparable to C. Some language features (methods, closures, message passing) are not yet as optimized as they could and will be. - The only overhead Nim has over C is the GC which has been tuned + The only overhead Nim has over C is the GC which has been tuned for years but still needs some work. @@ -96,7 +96,7 @@ General FAQ A JVM backend is almost impossible. The JVM is not expressive enough. It has never been designed as a general purpose VM anyway. A CLR backend is possible - but would require much work. + but would require much work. .. container:: standout @@ -112,6 +112,7 @@ General FAQ - jEdit: https://github.com/exhu/nimrod-misc/tree/master/jedit - TextMate: Available in bundle installer (`Repository <https://github.com/textmate/nim.tmbundle>`_) - Sublime Text: Available via Package Control (`Repository <https://github.com/Varriount/NimLime>`_) + - LiClipse: http://www.liclipse.com/ (Eclipse based plugin) .. container:: standout diff --git a/web/ticker.txt b/web/ticker.txt index 724d29231..4840e4039 100644 --- a/web/ticker.txt +++ b/web/ticker.txt @@ -1,13 +1,13 @@ +<a class="news" href="news.html#Z2015-05-04-version-0-11-2-released"> + <h4>May 4, 2015</h4> + <p>Nim version 0.11.2 has been released!</p> +</a> + <a class="news" href="news.html#Z2014-12-29-version-0-10-2-released"> <h4>Dec 29, 2014</h4> <p>Nim version 0.10.2 has been released!</p> </a> -<a class="news" href="news.html#Z2014-12-09-new-website-design"> - <h4>Dec 9, 2014</h4> - <p>The new website design and forum are now online!</p> -</a> - <a class="news" href="news.html#Z2014-02-11-nimrod-featured-in-dr-dobb-s-journal"> <h4>Feb 11, 2014</h4> <p>Nimrod featured in Dr. Dobb's Journal</p> diff --git a/web/website.ini b/web/website.ini index 6266f05bb..bb9f1b204 100644 --- a/web/website.ini +++ b/web/website.ini @@ -31,7 +31,7 @@ file: ticker.txt [Documentation] doc: "endb;intern;apis;lib;manual.txt;tut1;tut2;nimc;overview;filters" doc: "tools;niminst;nimgrep;gc;estp;idetools;docgen;koch;backends.txt" -doc: "nimfix.txt" +doc: "nimfix.txt;nimsuggest.txt" pdf: "manual.txt;lib;tut1;tut2;nimc;niminst;gc" srcdoc2: "system.nim" srcdoc2: "core/macros;pure/marshal;core/typeinfo;core/unsigned" @@ -49,7 +49,7 @@ srcdoc2: "pure/httpserver;pure/httpclient;pure/smtp;impure/ssl;pure/fsmonitor" srcdoc2: "pure/ropes;pure/unidecode/unidecode;pure/xmldom;pure/xmldomparser" srcdoc2: "pure/xmlparser;pure/htmlparser;pure/xmltree;pure/colors;pure/mimetypes" srcdoc2: "pure/json;pure/base64;pure/scgi;pure/redis;impure/graphics" -srcdoc2: "impure/rdstdin" +srcdoc2: "impure/rdstdin;impure/dialogs" srcdoc2: "pure/collections/tables;pure/collections/sets;pure/collections/lists" srcdoc2: "pure/collections/intsets;pure/collections/queues;pure/encodings" srcdoc2: "pure/events;pure/collections/sequtils;pure/cookies" @@ -60,7 +60,7 @@ srcdoc2: "packages/docutils/rst;packages/docutils/rstast" srcdoc2: "packages/docutils/rstgen;pure/logging;pure/asyncdispatch;pure/asyncnet" srcdoc2: "pure/rawsockets;pure/asynchttpserver;pure/net;pure/selectors;pure/future" srcdoc2: "pure/asyncfile" -srcdoc2: "pure/md5" +srcdoc2: "pure/md5;pure/rationals" srcdoc2: "posix/posix" srcdoc2: "pure/fenv" srcdoc2: "pure/basic2d;pure/basic3d" |