diff --git a/compiler/ast.nim b/compiler/ast.nim index d487b5380a..1462d58d5d 100644 --- a/compiler/ast.nim +++ b/compiler/ast.nim @@ -314,7 +314,7 @@ type # XXX put this into an include file to avoid this issue! tyNone, tyBool, tyChar, tyEmpty, tyArrayConstr, tyNil, tyExpr, tyStmt, tyTypeDesc, - tyGenericInvokation, # ``T[a, b]`` for types to invoke + tyGenericInvocation, # ``T[a, b]`` for types to invoke tyGenericBody, # ``T[a, b, body]`` last parameter is the body tyGenericInst, # ``T[a, b, realInstance]`` instantiated generic type # realInstance will be a concrete type like tyObject @@ -377,7 +377,7 @@ type tyFromExpr #\ # This is a type representing an expression that depends - # on generic parameters (the exprsesion is stored in t.n) + # on generic parameters (the expression is stored in t.n) # It will be converted to a real type only during generic # instantiation and prior to this it has the potential to # be any type. @@ -459,7 +459,7 @@ type tfNeedsInit, # type constains a "not nil" constraint somewhere or some # other type so that it requires inititalization - tfHasShared, # type constains a "shared" constraint modifier somewhere + tfVarIsPtr, # 'var' type is translated like 'ptr' even in C++ mode tfHasMeta, # type contains "wildcard" sub-types such as generic params # or other type classes tfHasGCedMem, # type contains GC'ed memory @@ -522,7 +522,7 @@ const skError* = skUnknown # type flags that are essential for type equality: - eqTypeFlags* = {tfIterator, tfShared, tfNotNil} + eqTypeFlags* = {tfIterator, tfShared, tfNotNil, tfVarIsPtr} type TMagic* = enum # symbols that require compiler magic: @@ -655,7 +655,6 @@ type locGlobalVar, # location is a global variable locParam, # location is a parameter locField, # location is a record field - locArrayElem, # location is an array element locExpr, # "location" is really an expression locProc, # location is a proc (an address of a procedure) locData, # location is a constant @@ -669,14 +668,15 @@ type lfDynamicLib, # link symbol to dynamic library lfExportLib, # export symbol for dynamic library generation lfHeader, # include header file for symbol - lfImportCompilerProc # ``importc`` of a compilerproc + lfImportCompilerProc, # ``importc`` of a compilerproc + lfSingleUse # no location yet and will only be used once TStorageLoc* = enum OnUnknown, # location is unknown (stack, heap or static) OnStack, # location is on hardware stack OnHeap # location is on heap or global # (reference counting needed) TLocFlags* = set[TLocFlag] - TLoc*{.final.} = object + TLoc* = object k*: TLocKind # kind of location s*: TStorageLoc flags*: TLocFlags # location's flags @@ -859,7 +859,7 @@ const OverloadableSyms* = {skProc, skMethod, skIterator, skClosureIterator, skConverter, skModule, skTemplate, skMacro} - GenericTypes*: TTypeKinds = {tyGenericInvokation, tyGenericBody, + GenericTypes*: TTypeKinds = {tyGenericInvocation, tyGenericBody, tyGenericParam} StructuralEquivTypes*: TTypeKinds = {tyArrayConstr, tyNil, tyTuple, tyArray, @@ -918,50 +918,6 @@ const # only used when 'gCmd == cmdPretty': Indicates that the symbol has been # imported via 'importc: "fullname"' and no format string. -# creator procs: -proc newSym*(symKind: TSymKind, name: PIdent, owner: PSym, - info: TLineInfo): PSym -proc newType*(kind: TTypeKind, owner: PSym): PType -proc newNode*(kind: TNodeKind): PNode -proc newIntNode*(kind: TNodeKind, intVal: BiggestInt): PNode -proc newIntTypeNode*(kind: TNodeKind, intVal: BiggestInt, typ: PType): PNode -proc newFloatNode*(kind: TNodeKind, floatVal: BiggestFloat): PNode -proc newStrNode*(kind: TNodeKind, strVal: string): PNode -proc newIdentNode*(ident: PIdent, info: TLineInfo): PNode -proc newSymNode*(sym: PSym): PNode -proc newNodeI*(kind: TNodeKind, info: TLineInfo): PNode -proc newNodeIT*(kind: TNodeKind, info: TLineInfo, typ: PType): PNode -proc initStrTable*(x: var TStrTable) -proc initTable*(x: var TTable) -proc initIdTable*(x: var TIdTable) -proc initObjectSet*(x: var TObjectSet) -proc initIdNodeTable*(x: var TIdNodeTable) -proc initNodeTable*(x: var TNodeTable) - -# copy procs: -proc copyType*(t: PType, owner: PSym, keepId: bool): PType -proc copySym*(s: PSym, keepId: bool = false): PSym -proc assignType*(dest, src: PType) -proc copyStrTable*(dest: var TStrTable, src: TStrTable) -proc copyTable*(dest: var TTable, src: TTable) -proc copyObjectSet*(dest: var TObjectSet, src: TObjectSet) -proc copyIdTable*(dest: var TIdTable, src: TIdTable) -proc sonsLen*(n: PNode): int {.inline.} -proc sonsLen*(n: PType): int {.inline.} -proc lastSon*(n: PNode): PNode {.inline.} -proc lastSon*(n: PType): PType {.inline.} -proc newSons*(father: PNode, length: int) -proc newSons*(father: PType, length: int) -proc addSon*(father, son: PNode) -proc delSon*(father: PNode, idx: int) -proc hasSonWith*(n: PNode, kind: TNodeKind): bool -proc hasSubnodeWith*(n: PNode, kind: TNodeKind): bool -proc replaceSons*(n: PNode, oldKind, newKind: TNodeKind) -proc copyNode*(src: PNode): PNode - # does not copy its sons! -proc copyTree*(src: PNode): PNode - # does copy its sons! - proc isCallExpr*(n: PNode): bool = result = n.kind in nkCallKinds @@ -988,7 +944,61 @@ proc `[]`*(n: PNode, i: int): PNode {.inline.} = template `{}`*(n: PNode, i: int): expr = n[i -| n] template `{}=`*(n: PNode, i: int, s: PNode): stmt = n.sons[i -| n] = s - + +when defined(useNodeIds): + const nodeIdToDebug* = -1 # 884953 # 612794 + #612840 # 612905 # 614635 # 614637 # 614641 + # 423408 + #429107 # 430443 # 441048 # 441090 # 441153 + var gNodeId: int + +proc newNode*(kind: TNodeKind): PNode = + new(result) + result.kind = kind + #result.info = UnknownLineInfo() inlined: + result.info.fileIndex = int32(- 1) + result.info.col = int16(- 1) + result.info.line = int16(- 1) + when defined(useNodeIds): + result.id = gNodeId + if result.id == nodeIdToDebug: + echo "KIND ", result.kind + writeStackTrace() + inc gNodeId + +proc newIntNode*(kind: TNodeKind, intVal: BiggestInt): PNode = + result = newNode(kind) + result.intVal = intVal + +proc newIntTypeNode*(kind: TNodeKind, intVal: BiggestInt, typ: PType): PNode = + result = newIntNode(kind, intVal) + result.typ = typ + +proc newFloatNode*(kind: TNodeKind, floatVal: BiggestFloat): PNode = + result = newNode(kind) + result.floatVal = floatVal + +proc newStrNode*(kind: TNodeKind, strVal: string): PNode = + result = newNode(kind) + result.strVal = strVal + +proc newSym*(symKind: TSymKind, name: PIdent, owner: PSym, + info: TLineInfo): PSym = + # generates a symbol and initializes the hash field too + new(result) + result.name = name + result.kind = symKind + result.flags = {} + result.info = info + result.options = gOptions + result.owner = owner + result.offset = - 1 + result.id = getID() + when debugIds: + registerId(result) + #if result.id < 2000: + # MessageOut(name.s & " has id: " & toString(result.id)) + var emptyNode* = newNode(nkEmpty) # There is a single empty node that is shared! Do not overwrite it! @@ -1031,80 +1041,43 @@ const # for all kind of hash tables: GrowthFactor* = 2 # must be power of 2, > 0 StartSize* = 8 # must be power of 2, > 0 -proc copyStrTable(dest: var TStrTable, src: TStrTable) = +proc copyStrTable*(dest: var TStrTable, src: TStrTable) = dest.counter = src.counter if isNil(src.data): return setLen(dest.data, len(src.data)) for i in countup(0, high(src.data)): dest.data[i] = src.data[i] -proc copyIdTable(dest: var TIdTable, src: TIdTable) = +proc copyIdTable*(dest: var TIdTable, src: TIdTable) = dest.counter = src.counter if isNil(src.data): return newSeq(dest.data, len(src.data)) for i in countup(0, high(src.data)): dest.data[i] = src.data[i] -proc copyTable(dest: var TTable, src: TTable) = +proc copyTable*(dest: var TTable, src: TTable) = dest.counter = src.counter if isNil(src.data): return setLen(dest.data, len(src.data)) for i in countup(0, high(src.data)): dest.data[i] = src.data[i] -proc copyObjectSet(dest: var TObjectSet, src: TObjectSet) = +proc copyObjectSet*(dest: var TObjectSet, src: TObjectSet) = dest.counter = src.counter if isNil(src.data): return setLen(dest.data, len(src.data)) for i in countup(0, high(src.data)): dest.data[i] = src.data[i] -proc discardSons(father: PNode) = +proc discardSons*(father: PNode) = father.sons = nil -when defined(useNodeIds): - const nodeIdToDebug* = -1 # 884953 # 612794 - #612840 # 612905 # 614635 # 614637 # 614641 - # 423408 - #429107 # 430443 # 441048 # 441090 # 441153 - var gNodeId: int - -proc newNode(kind: TNodeKind): PNode = - new(result) - result.kind = kind - #result.info = UnknownLineInfo() inlined: - result.info.fileIndex = int32(- 1) - result.info.col = int16(- 1) - result.info.line = int16(- 1) - when defined(useNodeIds): - result.id = gNodeId - if result.id == nodeIdToDebug: - echo "KIND ", result.kind - writeStackTrace() - inc gNodeId - -proc newIntNode(kind: TNodeKind, intVal: BiggestInt): PNode = - result = newNode(kind) - result.intVal = intVal - -proc newIntTypeNode(kind: TNodeKind, intVal: BiggestInt, typ: PType): PNode = - result = newIntNode(kind, intVal) - result.typ = typ - -proc newFloatNode(kind: TNodeKind, floatVal: BiggestFloat): PNode = - result = newNode(kind) - result.floatVal = floatVal - -proc newStrNode(kind: TNodeKind, strVal: string): PNode = - result = newNode(kind) - result.strVal = strVal - proc withInfo*(n: PNode, info: TLineInfo): PNode = n.info = info return n -proc newIdentNode(ident: PIdent, info: TLineInfo): PNode = +proc newIdentNode*(ident: PIdent, info: TLineInfo): PNode = result = newNode(nkIdent) result.ident = ident result.info = info -proc newSymNode(sym: PSym): PNode = +proc newSymNode*(sym: PSym): PNode = result = newNode(nkSym) result.sym = sym result.typ = sym.typ @@ -1116,7 +1089,7 @@ proc newSymNode*(sym: PSym, info: TLineInfo): PNode = result.typ = sym.typ result.info = info -proc newNodeI(kind: TNodeKind, info: TLineInfo): PNode = +proc newNodeI*(kind: TNodeKind, info: TLineInfo): PNode = new(result) result.kind = kind result.info = info @@ -1155,11 +1128,16 @@ proc newNode*(kind: TNodeKind, info: TLineInfo, sons: TNodeSeq = @[], writeStackTrace() inc gNodeId -proc newNodeIT(kind: TNodeKind, info: TLineInfo, typ: PType): PNode = +proc newNodeIT*(kind: TNodeKind, info: TLineInfo, typ: PType): PNode = result = newNode(kind) result.info = info result.typ = typ +proc addSon*(father, son: PNode) = + assert son != nil + if isNil(father.sons): father.sons = @[] + add(father.sons, son) + var emptyParams = newNode(nkFormalParams) emptyParams.addSon(emptyNode) @@ -1181,7 +1159,7 @@ proc `$`*(x: TLockLevel): string = elif x.ord == UnknownLockLevel.ord: result = "" else: result = $int16(x) -proc newType(kind: TTypeKind, owner: PSym): PType = +proc newType*(kind: TTypeKind, owner: PSym): PType = new(result) result.kind = kind result.owner = owner @@ -1201,8 +1179,38 @@ proc mergeLoc(a: var TLoc, b: TLoc) = if a.t == nil: a.t = b.t if a.r == nil: a.r = b.r #if a.a == 0: a.a = b.a + +proc newSons*(father: PNode, length: int) = + if isNil(father.sons): + newSeq(father.sons, length) + else: + setLen(father.sons, length) + +proc newSons*(father: PType, length: int) = + if isNil(father.sons): + newSeq(father.sons, length) + else: + setLen(father.sons, length) + +proc sonsLen*(n: PType): int = + if isNil(n.sons): result = 0 + else: result = len(n.sons) + +proc len*(n: PType): int = + if isNil(n.sons): result = 0 + else: result = len(n.sons) -proc assignType(dest, src: PType) = +proc sonsLen*(n: PNode): int = + if isNil(n.sons): result = 0 + else: result = len(n.sons) + +proc lastSon*(n: PNode): PNode = + result = n.sons[sonsLen(n) - 1] + +proc lastSon*(n: PType): PType = + result = n.sons[sonsLen(n) - 1] + +proc assignType*(dest, src: PType) = dest.kind = src.kind dest.flags = src.flags dest.callConv = src.callConv @@ -1223,7 +1231,7 @@ proc assignType(dest, src: PType) = newSons(dest, sonsLen(src)) for i in countup(0, sonsLen(src) - 1): dest.sons[i] = src.sons[i] -proc copyType(t: PType, owner: PSym, keepId: bool): PType = +proc copyType*(t: PType, owner: PSym, keepId: bool): PType = result = newType(t.kind, owner) assignType(result, t) if keepId: @@ -1232,7 +1240,7 @@ proc copyType(t: PType, owner: PSym, keepId: bool): PType = when debugIds: registerId(result) result.sym = t.sym # backend-info should not be copied -proc copySym(s: PSym, keepId: bool = false): PSym = +proc copySym*(s: PSym, keepId: bool = false): PSym = result = newSym(s.kind, s.name, s.owner, s.info) #result.ast = nil # BUGFIX; was: s.ast which made problems result.typ = s.typ @@ -1266,24 +1274,7 @@ proc createModuleAlias*(s: PSym, newIdent: PIdent, info: TLineInfo): PSym = # XXX once usedGenerics is used, ensure module aliases keep working! assert s.usedGenerics == nil -proc newSym(symKind: TSymKind, name: PIdent, owner: PSym, - info: TLineInfo): PSym = - # generates a symbol and initializes the hash field too - new(result) - result.name = name - result.kind = symKind - result.flags = {} - result.info = info - result.options = gOptions - result.owner = owner - result.offset = - 1 - result.id = getID() - when debugIds: - registerId(result) - #if result.id < 2000: - # MessageOut(name.s & " has id: " & toString(result.id)) - -proc initStrTable(x: var TStrTable) = +proc initStrTable*(x: var TStrTable) = x.counter = 0 newSeq(x.data, StartSize) @@ -1294,46 +1285,28 @@ proc initTable(x: var TTable) = x.counter = 0 newSeq(x.data, StartSize) -proc initIdTable(x: var TIdTable) = +proc initIdTable*(x: var TIdTable) = x.counter = 0 newSeq(x.data, StartSize) -proc initObjectSet(x: var TObjectSet) = +proc resetIdTable*(x: var TIdTable) = + x.counter = 0 + # clear and set to old initial size: + setLen(x.data, 0) + setLen(x.data, StartSize) + +proc initObjectSet*(x: var TObjectSet) = x.counter = 0 newSeq(x.data, StartSize) -proc initIdNodeTable(x: var TIdNodeTable) = +proc initIdNodeTable*(x: var TIdNodeTable) = x.counter = 0 newSeq(x.data, StartSize) -proc initNodeTable(x: var TNodeTable) = +proc initNodeTable*(x: var TNodeTable) = x.counter = 0 newSeq(x.data, StartSize) -proc sonsLen(n: PType): int = - if isNil(n.sons): result = 0 - else: result = len(n.sons) - -proc len*(n: PType): int = - if isNil(n.sons): result = 0 - else: result = len(n.sons) - -proc newSons(father: PType, length: int) = - if isNil(father.sons): - newSeq(father.sons, length) - else: - setLen(father.sons, length) - -proc sonsLen(n: PNode): int = - if isNil(n.sons): result = 0 - else: result = len(n.sons) - -proc newSons(father: PNode, length: int) = - if isNil(father.sons): - newSeq(father.sons, length) - else: - setLen(father.sons, length) - proc skipTypes*(t: PType, kinds: TTypeKinds): PType = ## Used throughout the compiler code to test whether a type tree contains or ## doesn't contain a specific type/types - it is often the case that only the @@ -1348,9 +1321,9 @@ proc isGCedMem*(t: PType): bool {.inline.} = proc propagateToOwner*(owner, elem: PType) = const HaveTheirOwnEmpty = {tySequence, tySet} - owner.flags = owner.flags + (elem.flags * {tfHasShared, tfHasMeta}) + owner.flags = owner.flags + (elem.flags * {tfHasMeta}) if tfNotNil in elem.flags: - if owner.kind in {tyGenericInst, tyGenericBody, tyGenericInvokation}: + if owner.kind in {tyGenericInst, tyGenericBody, tyGenericInvocation}: owner.flags.incl tfNotNil elif owner.kind notin HaveTheirOwnEmpty: owner.flags.incl tfNeedsInit @@ -1359,9 +1332,6 @@ proc propagateToOwner*(owner, elem: PType) = if owner.kind in HaveTheirOwnEmpty: discard else: owner.flags.incl tfNeedsInit - if tfShared in elem.flags: - owner.flags.incl tfHasShared - if elem.isMetaType: owner.flags.incl tfHasMeta @@ -1374,22 +1344,17 @@ proc rawAddSon*(father, son: PType) = add(father.sons, son) if not son.isNil: propagateToOwner(father, son) -proc addSon(father, son: PNode) = - assert son != nil - if isNil(father.sons): father.sons = @[] - add(father.sons, son) - proc addSonNilAllowed*(father, son: PNode) = if isNil(father.sons): father.sons = @[] add(father.sons, son) -proc delSon(father: PNode, idx: int) = +proc delSon*(father: PNode, idx: int) = if isNil(father.sons): return var length = sonsLen(father) for i in countup(idx, length - 2): father.sons[i] = father.sons[i + 1] setLen(father.sons, length - 1) -proc copyNode(src: PNode): PNode = +proc copyNode*(src: PNode): PNode = # does not copy its sons! if src == nil: return nil @@ -1426,7 +1391,7 @@ proc shallowCopy*(src: PNode): PNode = of nkStrLit..nkTripleStrLit: result.strVal = src.strVal else: newSeq(result.sons, sonsLen(src)) -proc copyTree(src: PNode): PNode = +proc copyTree*(src: PNode): PNode = # copy a whole syntax tree; performs deep copying if src == nil: return nil @@ -1447,14 +1412,8 @@ proc copyTree(src: PNode): PNode = newSeq(result.sons, sonsLen(src)) for i in countup(0, sonsLen(src) - 1): result.sons[i] = copyTree(src.sons[i]) - -proc lastSon(n: PNode): PNode = - result = n.sons[sonsLen(n) - 1] -proc lastSon(n: PType): PType = - result = n.sons[sonsLen(n) - 1] - -proc hasSonWith(n: PNode, kind: TNodeKind): bool = +proc hasSonWith*(n: PNode, kind: TNodeKind): bool = for i in countup(0, sonsLen(n) - 1): if n.sons[i].kind == kind: return true @@ -1476,7 +1435,7 @@ proc containsNode*(n: PNode, kinds: TNodeKinds): bool = for i in countup(0, sonsLen(n) - 1): if n.kind in kinds or containsNode(n.sons[i], kinds): return true -proc hasSubnodeWith(n: PNode, kind: TNodeKind): bool = +proc hasSubnodeWith*(n: PNode, kind: TNodeKind): bool = case n.kind of nkEmpty..nkNilLit: result = n.kind == kind else: diff --git a/compiler/astalgo.nim b/compiler/astalgo.nim index f23e9a9833..8d132ab260 100644 --- a/compiler/astalgo.nim +++ b/compiler/astalgo.nim @@ -130,8 +130,8 @@ proc skipConvAndClosure*(n: PNode): PNode = proc sameValue*(a, b: PNode): bool = result = false case a.kind - of nkCharLit..nkInt64Lit: - if b.kind in {nkCharLit..nkInt64Lit}: result = a.intVal == b.intVal + of nkCharLit..nkUInt64Lit: + if b.kind in {nkCharLit..nkUInt64Lit}: result = a.intVal == b.intVal of nkFloatLit..nkFloat64Lit: if b.kind in {nkFloatLit..nkFloat64Lit}: result = a.floatVal == b.floatVal of nkStrLit..nkTripleStrLit: @@ -145,13 +145,13 @@ proc leValue*(a, b: PNode): bool = # a <= b? result = false case a.kind - of nkCharLit..nkInt64Lit: - if b.kind in {nkCharLit..nkInt64Lit}: result = a.intVal <= b.intVal - of nkFloatLit..nkFloat64Lit: + of nkCharLit..nkUInt32Lit: + if b.kind in {nkCharLit..nkUInt32Lit}: result = a.intVal <= b.intVal + of nkFloatLit..nkFloat64Lit: if b.kind in {nkFloatLit..nkFloat64Lit}: result = a.floatVal <= b.floatVal - of nkStrLit..nkTripleStrLit: + of nkStrLit..nkTripleStrLit: if b.kind in {nkStrLit..nkTripleStrLit}: result = a.strVal <= b.strVal - else: + else: # don't raise an internal error for 'nimrod check': #InternalError(a.info, "leValue") discard @@ -387,6 +387,9 @@ proc debugType(n: PType, maxRecDepth=100): PRope = if n.sym != nil: app(result, " ") app(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)) if (n.kind != tyString) and (sonsLen(n) > 0) and maxRecDepth != 0: app(result, "(") for i in countup(0, sonsLen(n) - 1): @@ -445,21 +448,21 @@ proc debugTree(n: PNode, indent: int, maxRecDepth: int; proc debug(n: PSym) = if n == nil: - writeln(stdout, "null") + msgWriteln("null") elif n.kind == skUnknown: - writeln(stdout, "skUnknown") + msgWriteln("skUnknown") else: #writeln(stdout, ropeToStr(symToYaml(n, 0, 1))) - writeln(stdout, "$1_$2: $3, $4, $5, $6" % [ + 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]) proc debug(n: PType) = - writeln(stdout, ropeToStr(debugType(n))) + msgWriteln(ropeToStr(debugType(n))) proc debug(n: PNode) = - writeln(stdout, ropeToStr(debugTree(n, 0, 100))) + msgWriteln(ropeToStr(debugTree(n, 0, 100))) const EmptySeq = @[] @@ -678,9 +681,8 @@ proc initIdentIter(ti: var TIdentIter, tab: TStrTable, s: PIdent): PSym = else: result = nextIdentIter(ti, tab) proc nextIdentIter(ti: var TIdentIter, tab: TStrTable): PSym = - var h, start: THash - h = ti.h and high(tab.data) - start = h + var h = ti.h and high(tab.data) + var start = h result = tab.data[h] while result != nil: if result.name.id == ti.name.id: break diff --git a/compiler/canonicalizer.nim b/compiler/canonicalizer.nim index 02010961dd..50d3fd017e 100644 --- a/compiler/canonicalizer.nim +++ b/compiler/canonicalizer.nim @@ -119,8 +119,8 @@ proc hashType(c: var MD5Context, t: PType) = c.hashSym(t.sym) case t.kind - of tyGenericBody, tyGenericInst, tyGenericInvokation: - for i in countup(0, sonsLen(t) -1 -ord(t.kind != tyGenericInvokation)): + of tyGenericBody, tyGenericInst, tyGenericInvocation: + for i in countup(0, sonsLen(t) -1 -ord(t.kind != tyGenericInvocation)): c.hashType t.sons[i] of tyUserTypeClass: internalAssert t.sym != nil and t.sym.owner != nil diff --git a/compiler/ccgcalls.nim b/compiler/ccgcalls.nim index cb8dcc25b3..200ff91e03 100644 --- a/compiler/ccgcalls.nim +++ b/compiler/ccgcalls.nim @@ -45,12 +45,20 @@ proc fixupCall(p: BProc, le, ri: PNode, d: var TLoc, genAssignment(p, d, tmp, {}) # no need for deep copying else: app(pl, ~")") - if d.k == locNone: getTemp(p, typ.sons[0], d) - assert(d.t != nil) # generate an assignment to d: - var list: TLoc - initLoc(list, locCall, d.t, OnUnknown) - list.r = pl - genAssignment(p, d, list, {}) # no need for deep copying + if p.module.compileToCpp and lfSingleUse in d.flags: + # do not generate spurious temporaries for C++! For C we're better off + # with them to prevent undefined behaviour and because the codegen + # is free to emit expressions multiple times! + d.k = locCall + d.r = pl + excl d.flags, lfSingleUse + else: + if d.k == locNone: getTemp(p, typ.sons[0], d) + assert(d.t != nil) # generate an assignment to d: + var list: TLoc + initLoc(list, locCall, d.t, OnUnknown) + list.r = pl + genAssignment(p, d, list, {}) # no need for deep copying else: app(pl, ~");$n") line(p, cpsStmts, pl) @@ -90,7 +98,8 @@ proc openArrayLoc(p: BProc, n: PNode): PRope = of tyOpenArray, tyVarargs, tyArray, tyArrayConstr: "($1)+($2), ($3)-($2)+1" of tyString, tySequence: - if skipTypes(n.typ, abstractInst).kind == tyVar: + if skipTypes(n.typ, abstractInst).kind == tyVar and + not compileToCpp(p.module): "(*$1)->data+($2), ($3)-($2)+1" else: "$1->data+($2), ($3)-($2)+1" @@ -102,7 +111,8 @@ proc openArrayLoc(p: BProc, n: PNode): PRope = of tyOpenArray, tyVarargs: result = ropef("$1, $1Len0", [rdLoc(a)]) of tyString, tySequence: - if skipTypes(n.typ, abstractInst).kind == tyVar: + if skipTypes(n.typ, abstractInst).kind == tyVar and + not compileToCpp(p.module): result = ropef("(*$1)->data, (*$1)->$2", [a.rdLoc, lenField(p)]) else: result = ropef("$1->data, $1->$2", [a.rdLoc, lenField(p)]) @@ -114,8 +124,8 @@ proc genArgStringToCString(p: BProc, n: PNode): PRope {.inline.} = var a: TLoc initLocExpr(p, n.sons[0], a) result = ropef("$1->data", [a.rdLoc]) - -proc genArg(p: BProc, n: PNode, param: PSym): PRope = + +proc genArg(p: BProc, n: PNode, param: PSym; call: PNode): PRope = var a: TLoc if n.kind == nkStringToCString: result = genArgStringToCString(p, n) @@ -125,8 +135,20 @@ proc genArg(p: BProc, n: PNode, param: PSym): PRope = elif ccgIntroducedPtr(param): initLocExpr(p, n, a) result = addrLoc(a) + elif p.module.compileToCpp and param.typ.kind == tyVar and + n.kind == nkHiddenAddr: + initLocExprSingleUse(p, n.sons[0], a) + # if the proc is 'importc'ed but not 'importcpp'ed then 'var T' still + # means '*T'. See posix.nim for lots of examples that do that in the wild. + let callee = call.sons[0] + if callee.kind == nkSym and + {sfImportC, sfInfixCall, sfCompilerProc} * callee.sym.flags == {sfImportC} and + {lfHeader, lfNoDecl} * callee.sym.loc.flags != {}: + result = addrLoc(a) + else: + result = rdLoc(a) else: - initLocExpr(p, n, a) + initLocExprSingleUse(p, n, a) result = rdLoc(a) proc genArgNoParam(p: BProc, n: PNode): PRope = @@ -134,7 +156,7 @@ proc genArgNoParam(p: BProc, n: PNode): PRope = if n.kind == nkStringToCString: result = genArgStringToCString(p, n) else: - initLocExpr(p, n, a) + initLocExprSingleUse(p, n, a) result = rdLoc(a) proc genPrefixCall(p: BProc, le, ri: PNode, d: var TLoc) = @@ -152,7 +174,7 @@ proc genPrefixCall(p: BProc, le, ri: PNode, d: var TLoc) = if params != nil: app(params, ~", ") if i < sonsLen(typ): assert(typ.n.sons[i].kind == nkSym) - app(params, genArg(p, ri.sons[i], typ.n.sons[i].sym)) + app(params, genArg(p, ri.sons[i], typ.n.sons[i].sym, ri)) else: app(params, genArgNoParam(p, ri.sons[i])) fixupCall(p, le, ri, d, op.r, params) @@ -178,7 +200,7 @@ proc genClosureCall(p: BProc, le, ri: PNode, d: var TLoc) = assert(sonsLen(typ) == sonsLen(typ.n)) if i < sonsLen(typ): assert(typ.n.sons[i].kind == nkSym) - app(pl, genArg(p, ri.sons[i], typ.n.sons[i].sym)) + app(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, ~", ") @@ -274,26 +296,28 @@ proc genThisArg(p: BProc; ri: PNode; i: int; typ: PType): PRope = assert(typ.n.sons[i].kind == nkSym) # if the parameter is lying (tyVar) and thus we required an additional deref, # skip the deref: + var ri = ri[i] + while ri.kind == nkObjDownConv: ri = ri[0] if typ.sons[i].kind == tyVar: - let x = if ri[i].kind == nkHiddenAddr: ri[i][0] else: ri[i] - if x.kind in {nkHiddenDeref, nkDerefExpr}: - result = genArgNoParam(p, x[0]) - result.app("->") - elif x.typ.kind in {tyVar, tyPtr}: + let x = if ri.kind == nkHiddenAddr: ri[0] else: ri + if x.typ.kind == tyPtr: result = genArgNoParam(p, x) result.app("->") + elif x.kind in {nkHiddenDeref, nkDerefExpr} and x[0].typ.kind == tyPtr: + result = genArgNoParam(p, x[0]) + result.app("->") else: result = genArgNoParam(p, x) result.app(".") elif typ.sons[i].kind == tyPtr: - if ri.sons[i].kind in {nkAddr, nkHiddenAddr}: - result = genArgNoParam(p, ri.sons[i][0]) + if ri.kind in {nkAddr, nkHiddenAddr}: + result = genArgNoParam(p, ri[0]) result.app(".") else: - result = genArgNoParam(p, ri.sons[i]) + result = genArgNoParam(p, ri) result.app("->") else: - result = genArgNoParam(p, ri.sons[i]) #, typ.n.sons[i].sym) + result = genArgNoParam(p, ri) #, typ.n.sons[i].sym) result.app(".") proc genPatternCall(p: BProc; ri: PNode; pat: string; typ: PType): PRope = @@ -367,12 +391,20 @@ proc genInfixCall(p: BProc, le, ri: PNode, d: var TLoc) = # simpler version of 'fixupCall' that works with the pl+params combination: var typ = skipTypes(ri.sons[0].typ, abstractInst) if typ.sons[0] != nil: - if d.k == locNone: getTemp(p, typ.sons[0], d) - assert(d.t != nil) # generate an assignment to d: - var list: TLoc - initLoc(list, locCall, d.t, OnUnknown) - list.r = pl - genAssignment(p, d, list, {}) # no need for deep copying + if p.module.compileToCpp and lfSingleUse in d.flags: + # do not generate spurious temporaries for C++! For C we're better off + # with them to prevent undefined behaviour and because the codegen + # is free to emit expressions multiple times! + d.k = locCall + d.r = pl + excl d.flags, lfSingleUse + else: + if d.k == locNone: getTemp(p, typ.sons[0], d) + assert(d.t != nil) # generate an assignment to d: + var list: TLoc + initLoc(list, locCall, d.t, OnUnknown) + list.r = pl + genAssignment(p, d, list, {}) # no need for deep copying else: app(pl, ~";$n") line(p, cpsStmts, pl) @@ -398,15 +430,27 @@ proc genNamedParamCall(p: BProc, ri: PNode, d: var TLoc) = assert(typ.kind == tyProc) var length = sonsLen(ri) assert(sonsLen(typ) == sonsLen(typ.n)) - - if length > 1: - app(pl, genArg(p, ri.sons[1], typ.n.sons[1].sym)) - app(pl, ~" ") - app(pl, op.r) - if length > 2: - app(pl, ~": ") - app(pl, genArg(p, ri.sons[2], typ.n.sons[2].sym)) - for i in countup(3, length-1): + + # don't call 'ropeToStr' 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) + if length > 1: + app(pl, ~": ") + app(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) + if length > 2: + app(pl, ~": ") + app(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?") @@ -415,7 +459,7 @@ proc genNamedParamCall(p: BProc, ri: PNode, d: var TLoc) = app(pl, ~" ") app(pl, param.name.s) app(pl, ~": ") - app(pl, genArg(p, ri.sons[i], param)) + app(pl, genArg(p, ri.sons[i], param, ri)) if typ.sons[0] != nil: if isInvalidReturnType(typ.sons[0]): if sonsLen(ri) > 1: app(pl, ~" ") diff --git a/compiler/ccgexprs.nim b/compiler/ccgexprs.nim index 93bb5dbf56..5f5aa63080 100644 --- a/compiler/ccgexprs.nim +++ b/compiler/ccgexprs.nim @@ -56,11 +56,6 @@ proc genLiteral(p: BProc, n: PNode, ty: PType): PRope = case skipTypes(ty, abstractVarRange).kind of tyChar, tyNil: result = intLiteral(n.intVal) - of tyInt: - if n.intVal >= low(int32) and n.intVal <= high(int32): - result = int32Literal(int32(n.intVal)) - else: - result = intLiteral(n.intVal) of tyBool: if n.intVal != 0: result = ~"NIM_TRUE" else: result = ~"NIM_FALSE" @@ -83,11 +78,13 @@ proc genLiteral(p: BProc, n: PNode, ty: PType): PRope = else: result = toRope("NIM_NIL") of nkStrLit..nkTripleStrLit: - if skipTypes(ty, abstractVarRange).kind == tyString: + if n.strVal.isNil: + result = ropecg(p.module, "((#NimStringDesc*) NIM_NIL)", []) + elif skipTypes(ty, abstractVarRange).kind == tyString: var id = nodeTableTestOrSet(p.module.dataCache, n, gBackendId) if id == gBackendId: # string literal not found in the cache: - result = ropecg(p.module, "((#NimStringDesc*) &$1)", + result = ropecg(p.module, "((#NimStringDesc*) &$1)", [getStrLit(p.module, n.strVal)]) else: result = ropecg(p.module, "((#NimStringDesc*) &TMP$1)", [toRope(id)]) @@ -156,7 +153,7 @@ proc getStorageLoc(n: PNode): TStorageLoc = of skVar, skForVar, skResult, skLet: if sfGlobal in n.sym.flags: result = OnHeap else: result = OnStack - of skConst: + of skConst: if sfGlobal in n.sym.flags: result = OnHeap else: result = OnUnknown else: result = OnUnknown @@ -234,7 +231,7 @@ proc genOptAsgnTuple(p: BProc, dest, src: TLoc, flags: TAssignmentFlags) = for i in 0 .. > (NU$3)($2))", # ShrI "($4)((NU$3)($1) << (NU$3)($2))", # ShlI "($4)($1 & $2)", # BitandI @@ -615,7 +612,7 @@ proc genEqProc(p: BProc, e: PNode, d: var TLoc) = initLocExpr(p, e.sons[1], a) initLocExpr(p, e.sons[2], b) if a.t.callConv == ccClosure: - putIntoDest(p, d, e.typ, + putIntoDest(p, d, e.typ, ropef("($1.ClPrc == $2.ClPrc && $1.ClEnv == $2.ClEnv)", [ rdLoc(a), rdLoc(b)])) else: @@ -662,9 +659,14 @@ proc unaryArith(p: BProc, e: PNode, d: var TLoc, op: TMagic) = ropef(unArithTab[op], [rdLoc(a), toRope(getSize(t) * 8), getSimpleTypeDesc(p.module, e.typ)])) +proc isCppRef(p: BProc; typ: PType): bool {.inline.} = + result = p.module.compileToCpp and + skipTypes(typ, abstractInst).kind == tyVar and + tfVarIsPtr notin skipTypes(typ, abstractInst).flags + 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: @@ -672,12 +674,17 @@ proc genDeref(p: BProc, e: PNode, d: var TLoc; enforceDeref=false) = expr(p, e.sons[0], d) else: var a: TLoc - initLocExpr(p, e.sons[0], a) - case skipTypes(a.t, abstractInst).kind + 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: + putIntoDest(p, d, e.typ, rdLoc(a)) + return of tyPtr: d.s = OnUnknown # BUGFIX! else: internalError(e.info, "genDeref " & $a.t.kind) @@ -698,7 +705,7 @@ proc genAddr(p: BProc, e: PNode, d: var TLoc) = initLocExpr(p, e.sons[0], a) putIntoDest(p, d, e.typ, con("&", a.r)) #Message(e.info, warnUser, "HERE NEW &") - elif mapType(e.sons[0].typ) == ctArray: + elif mapType(e.sons[0].typ) == ctArray or isCppRef(p, e.sons[0].typ): expr(p, e.sons[0], d) else: var a: TLoc @@ -709,7 +716,7 @@ template inheritLocation(d: var TLoc, a: TLoc) = if d.k == locNone: d.s = a.s if d.heapRoot == nil: d.heapRoot = if a.heapRoot != nil: a.heapRoot else: a.r - + proc genRecordFieldAux(p: BProc, e: PNode, d, a: var TLoc): PType = initLocExpr(p, e.sons[0], a) if e.sons[1].kind != nkSym: internalError(e.info, "genRecordFieldAux") @@ -916,6 +923,7 @@ proc genAndOr(p: BProc, e: PNode, d: var TLoc, m: TMagic) = L: TLabel tmp: TLoc getTemp(p, e.typ, tmp) # force it into a temp! + inc p.splitDecls expr(p, e.sons[1], tmp) L = getLabel(p) if m == mOr: @@ -928,6 +936,7 @@ proc genAndOr(p: BProc, e: PNode, d: var TLoc, m: TMagic) = d = tmp else: genAssignment(p, d, tmp, {}) # no need for deep copying + dec p.splitDecls proc genEcho(p: BProc, n: PNode) = # this unusal way of implementing it ensures that e.g. ``echo("hallo", 45)`` @@ -938,7 +947,7 @@ proc genEcho(p: BProc, n: PNode) = var a: TLoc for i in countup(0, n.len-1): initLocExpr(p, n.sons[i], a) - appf(args, ", ($1)->data", [rdLoc(a)]) + appf(args, ", $1? ($1)->data:\"nil\"", [rdLoc(a)]) linefmt(p, cpsStmts, "printf($1$2);$n", makeCString(repeatStr(n.len, "%s") & tnl), args) @@ -1047,7 +1056,7 @@ proc genSeqElemAppend(p: BProc, e: PNode, d: var TLoc) = genAssignment(p, dest, b, {needToCopy, afDestIsNil}) gcUsage(e) -proc genReset(p: BProc, n: PNode) = +proc genReset(p: BProc, n: PNode) = var a: TLoc initLocExpr(p, n.sons[1], a) linefmt(p, cpsStmts, "#genericReset((void*)$1, $2);$n", @@ -1106,14 +1115,14 @@ proc genNewSeqAux(p: BProc, dest: TLoc, length: PRope) = else: call.r = ropecg(p.module, "($1) #newSeq($2, $3)", args) genAssignment(p, dest, call, {needToKeepAlive}) - + proc genNewSeq(p: BProc, e: PNode) = var a, b: TLoc initLocExpr(p, e.sons[1], a) initLocExpr(p, e.sons[2], b) genNewSeqAux(p, a, b.rdLoc) gcUsage(e) - + proc genObjConstr(p: BProc, e: PNode, d: var TLoc) = var tmp: TLoc var t = e.typ.skipTypes(abstractInst) @@ -1154,7 +1163,7 @@ proc genObjConstr(p: BProc, e: PNode, d: var TLoc) = d = tmp else: genAssignment(p, d, tmp, {}) - + proc genSeqConstr(p: BProc, t: PNode, d: var TLoc) = var arr: TLoc if d.k == locNone: @@ -1178,7 +1187,7 @@ proc genArrToSeq(p: BProc, t: PNode, d: var TLoc) = getTemp(p, t.typ, d) # generate call to newSeq before adding the elements per hand: var L = int(lengthOrd(t.sons[1].typ)) - + genNewSeqAux(p, d, intLiteral(L)) initLocExpr(p, t.sons[1], a) for i in countup(0, L - 1): @@ -1188,7 +1197,7 @@ proc genArrToSeq(p: BProc, t: PNode, d: var TLoc) = initLoc(arr, locExpr, elemType(skipTypes(t.sons[1].typ, abstractInst)), a.s) arr.r = rfmt(nil, "$1[$2]", rdLoc(a), intLiteral(i)) genAssignment(p, elem, arr, {afDestIsNil, needToCopy}) - + proc genNewFinalize(p: BProc, e: PNode) = var a, b, f: TLoc @@ -1236,14 +1245,15 @@ proc genOf(p: BProc, x: PNode, typ: PType, d: var TLoc) = var t = skipTypes(a.t, abstractInst) while t.kind in {tyVar, tyPtr, tyRef}: if t.kind != tyVar: nilCheck = r - r = rfmt(nil, "(*$1)", r) + if t.kind != tyVar or not p.module.compileToCpp: + r = rfmt(nil, "(*$1)", r) t = skipTypes(t.lastSon, typedescInst) if not p.module.compileToCpp: while t.kind == tyObject and t.sons[0] != nil: app(r, ~".Sup") t = skipTypes(t.sons[0], typedescInst) if isObjLackingTypeField(t): - globalError(x.info, errGenerated, + globalError(x.info, errGenerated, "no 'of' operator available for pure objects") if nilCheck != nil: r = rfmt(p.module, "(($1) && ($2))", nilCheck, genOfHelper(p, dest, r)) @@ -1260,7 +1270,7 @@ proc genRepr(p: BProc, e: PNode, d: var TLoc) = var t = skipTypes(e.sons[1].typ, abstractVarRange) case t.kind of tyInt..tyInt64, tyUInt..tyUInt64: - putIntoDest(p, d, e.typ, + putIntoDest(p, d, e.typ, ropecg(p.module, "#reprInt((NI64)$1)", [rdLoc(a)])) of tyFloat..tyFloat128: putIntoDest(p, d, e.typ, ropecg(p.module, "#reprFloat($1)", [rdLoc(a)])) @@ -1283,13 +1293,13 @@ proc genRepr(p: BProc, e: PNode, d: var TLoc) = of tyOpenArray, tyVarargs: putIntoDest(p, b, e.typ, ropef("$1, $1Len0", [rdLoc(a)])) of tyString, tySequence: - putIntoDest(p, b, e.typ, + putIntoDest(p, b, e.typ, ropef("$1->data, $1->$2", [rdLoc(a), lenField(p)])) of tyArray, tyArrayConstr: putIntoDest(p, b, e.typ, ropef("$1, $2", [rdLoc(a), toRope(lengthOrd(a.t))])) else: internalError(e.sons[0].info, "genRepr()") - putIntoDest(p, d, e.typ, + putIntoDest(p, d, e.typ, ropecg(p.module, "#reprOpenArray($1, $2)", [rdLoc(b), genTypeInfo(p.module, elemType(t))])) of tyCString, tyArray, tyArrayConstr, tyRef, tyPtr, tyPointer, tyNil, @@ -1375,7 +1385,7 @@ proc genSwap(p: BProc, e: PNode, d: var TLoc) = genAssignment(p, b, tmp, {}) proc rdSetElemLoc(a: TLoc, setType: PType): PRope = - # read a location of an set element; it may need a substraction operation + # read a location of an set element; it may need a subtraction operation # before the set operation result = rdCharLoc(a) assert(setType.kind == tySet) @@ -1418,7 +1428,7 @@ proc genInOp(p: BProc, e: PNode, d: var TLoc) = # do not emit the set, but generate a bunch of comparisons; and if we do # so, we skip the unnecessary range check: This is a semantical extension # that code now relies on. :-/ XXX - let ea = if e.sons[2].kind in {nkChckRange, nkChckRange64}: + let ea = if e.sons[2].kind in {nkChckRange, nkChckRange64}: e.sons[2].sons[0] else: e.sons[2] @@ -1503,7 +1513,7 @@ proc genSetOp(p: BProc, e: PNode, d: var TLoc, op: TMagic) = initLocExpr(p, e.sons[2], b) if d.k == locNone: getTemp(p, a.t, d) lineF(p, cpsStmts, - "for ($1 = 0; $1 < $2; $1++) $n" & + "for ($1 = 0; $1 < $2; $1++) $n" & " $3[$1] = $4[$1] $6 $5[$1];$n", [ rdLoc(i), toRope(size), rdLoc(d), rdLoc(a), rdLoc(b), toRope(lookupOpr[op])]) @@ -1534,7 +1544,7 @@ proc genSomeCast(p: BProc, e: PNode, d: var TLoc) = proc genCast(p: BProc, e: PNode, d: var TLoc) = const floatTypes = {tyFloat..tyFloat128} - let + let destt = skipTypes(e.typ, abstractRange) srct = skipTypes(e.sons[1].typ, abstractRange) if destt.kind in floatTypes or srct.kind in floatTypes: @@ -1641,7 +1651,7 @@ proc genMagicExpr(p: BProc, e: PNode, d: var TLoc, op: TMagic) = of mRepr: genRepr(p, e, d) of mGetTypeInfo: genGetTypeInfo(p, e, d) of mSwap: genSwap(p, e, d) - of mUnaryLt: + of mUnaryLt: if optOverflowCheck notin p.options: unaryExpr(p, e, d, "($1 - 1)") else: unaryExpr(p, e, d, "#subInt($1, 1)") of mPred: @@ -1815,10 +1825,10 @@ proc genTupleConstr(p: BProc, n: PNode, d: var TLoc) = proc isConstClosure(n: PNode): bool {.inline.} = result = n.sons[0].kind == nkSym and isRoutine(n.sons[0].sym) and n.sons[1].kind == nkNilLit - + proc genClosure(p: BProc, n: PNode, d: var TLoc) = assert n.kind == nkClosure - + if isConstClosure(n): inc(p.labels) var tmp = con("LOC", toRope(p.labels)) @@ -1863,7 +1873,8 @@ proc upConv(p: BProc, n: PNode, d: var TLoc) = var t = skipTypes(a.t, abstractInst) while t.kind in {tyVar, tyPtr, tyRef}: if t.kind != tyVar: nilCheck = r - r = ropef("(*$1)", [r]) + if t.kind != tyVar or not p.module.compileToCpp: + r = ropef("(*$1)", [r]) t = skipTypes(t.lastSon, abstractInst) if not p.module.compileToCpp: while t.kind == tyObject and t.sons[0] != nil: @@ -1904,7 +1915,7 @@ proc downConv(p: BProc, n: PNode, d: var TLoc) = if isRef: # it can happen that we end up generating '&&x->Sup' here, so we pack # the '&x->Sup' into a temporary and then those address is taken - # (see bug #837). However sometimes using a temporary is not correct: + # (see bug #837). However sometimes using a temporary is not correct: # init(TFigure(my)) # where it is passed to a 'var TFigure'. We test # this by ensuring the destination is also a pointer: if d.k == locNone and skipTypes(n.typ, abstractInst).kind in {tyRef, tyPtr, tyVar}: @@ -1921,13 +1932,13 @@ proc exprComplexConst(p: BProc, n: PNode, d: var TLoc) = discard getTypeDesc(p.module, t) # so that any fields are initialized var id = nodeTableTestOrSet(p.module.dataCache, n, gBackendId) var tmp = con("TMP", toRope(id)) - + if id == gBackendId: # expression not found in the cache: inc(gBackendId) appf(p.module.s[cfsData], "NIM_CONST $1 $2 = $3;$n", [getTypeDesc(p.module, t), tmp, genConstExpr(p, n)]) - + if d.k == locNone: fillLoc(d, locData, t, tmp, OnHeap) else: @@ -1968,7 +1979,7 @@ proc expr(p: BProc, n: PNode, d: var TLoc) = internalError n.info, "expr: var not init " & sym.name.s & "_" & $sym.id if sfThread in sym.flags: accessThreadLocalVar(p, sym) - if emulatedThreadVars(): + if emulatedThreadVars(): putIntoDest(p, d, sym.loc.t, con("NimTV->", sym.loc.r)) else: putLocIntoDest(p, d, sym.loc) @@ -1985,7 +1996,7 @@ proc expr(p: BProc, n: PNode, d: var TLoc) = #echo "FAILED FOR PRCO ", p.prc.name.s #debug p.prc.typ.n #echo renderTree(p.prc.ast, {renderIds}) - internalError(n.info, "expr: param not init " & sym.name.s & "_" & $sym.id) + internalError(n.info, "expr: param not init " & sym.name.s & "_" & $sym.id) putLocIntoDest(p, d, sym.loc) else: internalError(n.info, "expr(" & $sym.kind & "); unknown symbol") of nkNilLit: @@ -2073,8 +2084,8 @@ proc expr(p: BProc, n: PNode, d: var TLoc) = genAsgn(p, n, fastAsgn=p.prc != nil) of nkDiscardStmt: if n.sons[0].kind != nkEmpty: - var a: TLoc genLineDir(p, n) + var a: TLoc initLocExpr(p, n.sons[0], a) of nkAsmStmt: genAsmStmt(p, n) of nkTryStmt: @@ -2085,29 +2096,25 @@ proc expr(p: BProc, n: PNode, d: var TLoc) = # we have to emit the type information for object types here to support # separate compilation: genTypeSection(p.module, n) - of nkCommentStmt, nkIteratorDef, nkIncludeStmt, - nkImportStmt, nkImportExceptStmt, nkExportStmt, nkExportExceptStmt, - nkFromStmt, nkTemplateDef, nkMacroDef: + of nkCommentStmt, nkIteratorDef, nkIncludeStmt, + nkImportStmt, nkImportExceptStmt, nkExportStmt, nkExportExceptStmt, + nkFromStmt, nkTemplateDef, nkMacroDef: discard of nkPragma: genPragma(p, n) of nkPragmaBlock: expr(p, n.lastSon, d) - of nkProcDef, nkMethodDef, nkConverterDef: - if (n.sons[genericParamsPos].kind == nkEmpty): + of nkProcDef, nkMethodDef, nkConverterDef: + if n.sons[genericParamsPos].kind == nkEmpty: var prc = n.sons[namePos].sym # due to a bug/limitation in the lambda lifting, unused inner procs # are not transformed correctly. We work around this issue (#411) here # by ensuring it's no inner proc (owner is a module): - # - # We also check whether the proc captures its environment here to - # prevent issue #1642. - if prc.skipGenericOwner.kind == skModule and - tfCapturesEnv in prc.typ.flags: + if prc.skipGenericOwner.kind == skModule: if (optDeadCodeElim notin gGlobalOptions and sfDeadCodeElim notin getModule(prc).flags) or ({sfExportc, sfCompilerProc} * prc.flags == {sfExportc}) or (sfExportc in prc.flags and lfExportLib in prc.loc.flags) or - (prc.kind == skMethod): - # we have not only the header: + (prc.kind == skMethod): + # we have not only the header: if prc.getBody.kind != nkEmpty or lfDynamicLib in prc.loc.flags: genProc(p.module, prc) of nkParForStmt: genParForStmt(p, n) @@ -2130,7 +2137,7 @@ proc genConstSimpleList(p: BProc, n: PNode): PRope = proc genConstSeq(p: BProc, n: PNode, t: PType): PRope = var data = ropef("{{$1, $1}", n.len.toRope) - if n.len > 0: + if n.len > 0: # array part needs extra curlies: data.app(", {") for i in countup(0, n.len - 1): @@ -2138,14 +2145,14 @@ proc genConstSeq(p: BProc, n: PNode, t: PType): PRope = data.app genConstExpr(p, n.sons[i]) data.app("}") data.app("}") - + inc(gBackendId) result = con("CNSTSEQ", gBackendId.toRope) - + appcg(p.module, cfsData, - "NIM_CONST struct {$n" & + "NIM_CONST struct {$n" & " #TGenericSeq Sup;$n" & - " $1 data[$2];$n" & + " $1 data[$2];$n" & "} $3 = $4;$n", [ getTypeDesc(p.module, t.sons[0]), n.len.toRope, result, data]) @@ -2159,7 +2166,7 @@ proc genConstExpr(p: BProc, n: PNode): PRope = var cs: TBitSet toBitSet(n, cs) result = genRawSetData(cs, int(getSize(n.typ))) - of nkBracket, nkPar, nkClosure: + of nkBracket, nkPar, nkClosure, nkObjConstr: var t = skipTypes(n.typ, abstractInst) if t.kind == tySequence: result = genConstSeq(p, n, t) diff --git a/compiler/ccgmerge.nim b/compiler/ccgmerge.nim index 36da68d234..f4f8378346 100644 --- a/compiler/ccgmerge.nim +++ b/compiler/ccgmerge.nim @@ -223,7 +223,7 @@ proc processMergeInfo(L: var TBaseLexer, m: BModule) = of "typeInfo": readIntSet(L, m.typeInfoMarker) of "labels": m.labels = decodeVInt(L.buf, L.bufpos) of "hasframe": m.frameDeclared = decodeVInt(L.buf, L.bufpos) != 0 - else: internalError("ccgmerge: unkown key: " & k) + else: internalError("ccgmerge: unknown key: " & k) when not defined(nimhygiene): {.pragma: inject.} diff --git a/compiler/ccgstmts.nim b/compiler/ccgstmts.nim index f0870a5df6..a4938c9ac1 100644 --- a/compiler/ccgstmts.nim +++ b/compiler/ccgstmts.nim @@ -48,7 +48,7 @@ proc genVarTuple(p: BProc, n: PNode) = return genLineDir(p, n) initLocExpr(p, n.sons[L-1], tup) - var t = tup.t + var t = tup.t.getUniqueType for i in countup(0, L-3): var v = n.sons[i].sym if sfCompileTime in v.flags: continue @@ -202,8 +202,20 @@ proc genSingleVar(p: BProc, a: PNode) = genVarPrototypeAux(generatedHeader, v) registerGcRoot(p, v) else: + let imm = isAssignedImmediately(a.sons[2]) + if imm and p.module.compileToCpp and p.splitDecls == 0 and + not containsHiddenPointer(v.typ): + # C++ really doesn't like things like 'Foo f; f = x' as that invokes a + # parameterless constructor followed by an assignment operator. So we + # generate better code here: + genLineDir(p, a) + let decl = localVarDecl(p, v) + var tmp: TLoc + initLocExprSingleUse(p, a.sons[2], tmp) + lineF(p, cpsStmts, "$# = $#;$n", decl, tmp.rdLoc) + return assignLocalVar(p, v) - initLocalVar(p, v, isAssignedImmediately(a.sons[2])) + initLocalVar(p, v, imm) if a.sons[2].kind != nkEmpty: genLineDir(targetProc, a) @@ -242,16 +254,7 @@ proc genConstStmt(p: BProc, t: PNode) = elif c.typ.kind in ConstantDataTypes and lfNoDecl notin c.loc.flags and c.ast.len != 0: if not emitLazily(c): requestConstImpl(p, c) - when false: - # generate the data: - fillLoc(c.loc, locData, c.typ, mangleName(c), OnUnknown) - if sfImportc in c.flags: - appf(p.module.s[cfsData], "extern NIM_CONST $1 $2;$n", - [getTypeDesc(p.module, c.typ), c.loc.r]) - else: - appf(p.module.s[cfsData], "NIM_CONST $1 $2 = $3;$n", - [getTypeDesc(p.module, c.typ), c.loc.r, genConstExpr(p, c.ast)]) - + proc genIf(p: BProc, n: PNode, d: var TLoc) = # # { if (!expr1) goto L1; @@ -275,17 +278,22 @@ proc genIf(p: BProc, n: PNode, d: var TLoc) = let it = n.sons[i] if it.len == 2: when newScopeForIf: startBlock(p) - initLocExpr(p, it.sons[0], a) + initLocExprSingleUse(p, it.sons[0], a) lelse = getLabel(p) inc(p.labels) - lineFF(p, cpsStmts, "if (!$1) goto $2;$n", - "br i1 $1, label %LOC$3, label %$2$nLOC$3: $n", - [rdLoc(a), lelse, toRope(p.labels)]) + lineF(p, cpsStmts, "if (!$1) goto $2;$n", + [rdLoc(a), lelse]) when not newScopeForIf: startBlock(p) - expr(p, it.sons[1], d) + if p.module.compileToCpp: + # avoid "jump to label crosses initialization" error: + app(p.s(cpsStmts), "{") + expr(p, it.sons[1], d) + app(p.s(cpsStmts), "}") + else: + expr(p, it.sons[1], d) endBlock(p) if sonsLen(n) > 1: - lineFF(p, cpsStmts, "goto $1;$n", "br label %$1$n", [lend]) + lineF(p, cpsStmts, "goto $1;$n", [lend]) fixLabel(p, lelse) elif it.len == 1: startBlock(p) @@ -344,7 +352,7 @@ proc genReturnStmt(p: BProc, t: PNode) = # consume it before we return. var safePoint = p.finallySafePoints[p.finallySafePoints.len-1] linefmt(p, cpsStmts, "if ($1.status != 0) #popCurrentException();$n", safePoint) - lineFF(p, cpsStmts, "goto BeforeRet;$n", "br label %BeforeRet$n", []) + lineF(p, cpsStmts, "goto BeforeRet;$n", []) proc genComputedGoto(p: BProc; n: PNode) = # first pass: Generate array of computed labels: diff --git a/compiler/ccgtypes.nim b/compiler/ccgtypes.nim index 90996d9cdb..9a5a3ab341 100644 --- a/compiler/ccgtypes.nim +++ b/compiler/ccgtypes.nim @@ -27,17 +27,7 @@ proc isKeyword(w: PIdent): bool = proc mangleName(s: PSym): PRope = result = s.loc.r - if result == nil: - if gCmd == cmdCompileToLLVM: - case s.kind - of skProc, skMethod, skConverter, skConst, skIterators: - result = ~"@" - of skVar, skForVar, skResult, skLet: - if sfGlobal in s.flags: result = ~"@" - else: result = ~"%" - of skTemp, skParam, skType, skEnumField, skModule: - result = ~"%" - else: internalError(s.info, "mangleName") + if result == nil: when oKeepVariableNames: let keepOrigName = s.kind in skLocalVars - {skForVar} and {sfFromGeneric, sfGlobal, sfShadowed, sfGenSym} * s.flags == {} and @@ -103,13 +93,11 @@ proc typeName(typ: PType): PRope = else: ~"TY" proc getTypeName(typ: PType): PRope = - if (typ.sym != nil) and ({sfImportc, sfExportc} * typ.sym.flags != {}) and - (gCmd != cmdCompileToLLVM): + if typ.sym != nil and {sfImportc, sfExportc} * typ.sym.flags != {}: result = typ.sym.loc.r else: if typ.loc.r == nil: - typ.loc.r = if gCmd != cmdCompileToLLVM: con(typ.typeName, typ.id.toRope) - else: con([~"%", typ.typeName, typ.id.toRope]) + typ.loc.r = con(typ.typeName, typ.id.toRope) result = typ.loc.r if result == nil: internalError("getTypeName: " & $typ.kind) @@ -161,7 +149,13 @@ proc mapType(typ: PType): TCTypeKind = proc mapReturnType(typ: PType): TCTypeKind = if skipTypes(typ, typedescInst).kind == tyArray: result = ctPtr else: result = mapType(typ) - + +proc isImportedType(t: PType): bool = + result = t.sym != nil and sfImportc in t.sym.flags + +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 needsComplexAssignment(typ: PType): bool = result = containsGarbageCollectedRef(typ) @@ -170,19 +164,20 @@ proc isObjLackingTypeField(typ: PType): bool {.inline.} = result = (typ.kind == tyObject) and ((tfFinal in typ.flags) and (typ.sons[0] == nil) or isPureObject(typ)) -proc isInvalidReturnType(rettype: PType): bool = +proc isInvalidReturnType(rettype: PType): bool = # Arrays and sets cannot be returned by a C procedure, because C is # such a poor programming language. # We exclude records with refs too. This enhances efficiency and # is necessary for proper code generation of assignments. if rettype == nil: result = true - else: + else: case mapType(rettype) - of ctArray: + of ctArray: result = not (skipTypes(rettype, typedescInst).kind in {tyVar, tyRef, tyPtr}) of ctStruct: let t = skipTypes(rettype, typedescInst) + if rettype.isImportedCppType or t.isImportedCppType: return false result = needsComplexAssignment(t) or (t.kind == tyObject and not isObjLackingTypeField(t)) else: result = false @@ -193,9 +188,6 @@ const "N_SYSCALL", # this is probably not correct for all platforms, # but one can #define it to what one wants "N_INLINE", "N_NOINLINE", "N_FASTCALL", "N_CLOSURE", "N_NOCONV"] - CallingConvToStrLLVM: array[TCallingConvention, string] = ["fastcc $1", - "stdcall $1", "ccc $1", "safecall $1", "syscall $1", "$1 alwaysinline", - "$1 noinline", "fastcc $1", "ccc $1", "$1"] proc cacheGetType(tab: TIdTable, key: PType): PRope = # returns nil if we need to declare this type @@ -234,77 +226,8 @@ proc fillResult(param: PSym) = incl(param.loc.flags, lfIndirect) param.loc.s = OnUnknown -proc getParamTypeDesc(m: BModule, t: PType, check: var IntSet): PRope = - when false: - if t.Kind in {tyRef, tyPtr, tyVar}: - var b = skipTypes(t.lastson, typedescInst) - if b.kind == tySet and mapSetType(b) == ctArray: - return getTypeDescAux(m, b, check) - result = getTypeDescAux(m, t, check) - -proc paramStorageLoc(param: PSym): TStorageLoc = - if param.typ.skipTypes({tyVar, tyTypeDesc}).kind notin {tyArray, tyOpenArray}: - result = OnStack - else: - result = OnUnknown - -proc genProcParams(m: BModule, t: PType, rettype, params: var PRope, - check: var IntSet, declareEnvironment=true) = - params = nil - if (t.sons[0] == nil) or isInvalidReturnType(t.sons[0]): - rettype = ~"void" - else: - rettype = getTypeDescAux(m, t.sons[0], check) - for i in countup(1, sonsLen(t.n) - 1): - 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, ~", ") - fillLoc(param.loc, locParam, param.typ, mangleName(param), - param.paramStorageLoc) - app(params, getParamTypeDesc(m, param.typ, check)) - if ccgIntroducedPtr(param): - app(params, ~"*") - incl(param.loc.flags, lfIndirect) - param.loc.s = OnUnknown - app(params, ~" ") - app(params, param.loc.r) - # declare the len field for open arrays: - var arr = param.typ - if arr.kind == tyVar: arr = arr.sons[0] - var j = 0 - while arr.kind in {tyOpenArray, tyVarargs}: - # this fixes the 'sort' bug: - if param.typ.kind == tyVar: param.loc.s = OnUnknown - # need to pass hidden parameter: - appff(params, ", NI $1Len$2", ", @NI $1Len$2", [param.loc.r, j.toRope]) - 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, ", ") - app(params, getTypeDescAux(m, arr, check)) - if (mapReturnType(t.sons[0]) != ctArray) or (gCmd == cmdCompileToLLVM): - app(params, "*") - appff(params, " Result", " @Result", []) - if t.callConv == ccClosure and declareEnvironment: - if params != nil: app(params, ", ") - app(params, "void* ClEnv") - if tfVarargs in t.flags: - if params != nil: app(params, ", ") - app(params, "...") - if params == nil and gCmd != cmdCompileToLLVM: app(params, "void)") - else: app(params, ")") - params = con("(", params) - -proc isImportedType(t: PType): bool = - result = t.sym != nil and sfImportc in t.sym.flags - -proc isImportedCppType(t: PType): bool = - result = t.sym != nil and sfInfixCall in t.sym.flags - proc typeNameOrLiteral(t: PType, literal: string): PRope = - if (t.sym != nil) and (sfImportc in t.sym.flags) and (t.sym.magic == mNone): + if t.sym != nil and sfImportc in t.sym.flags and t.sym.magic == mNone: result = getTypeName(t) else: result = toRope(literal) @@ -341,7 +264,10 @@ proc getSimpleTypeDesc(m: BModule, typ: PType): PRope = result = typeNameOrLiteral(typ, NumericalTypeToStr[typ.kind]) of tyDistinct, tyRange, tyOrdinal: result = getSimpleTypeDesc(m, typ.sons[0]) else: result = nil - + +proc pushType(m: BModule, typ: PType) = + add(m.typeStack, typ) + proc getTypePre(m: BModule, typ: PType): PRope = if typ == nil: result = toRope("void") else: @@ -368,6 +294,81 @@ proc getTypeForward(m: BModule, typ: PType): PRope = [structOrUnion(typ), result]) idTablePut(m.forwTypeCache, typ, result) else: internalError("getTypeForward(" & $typ.kind & ')') + +proc getTypeDescWeak(m: BModule; t: PType; check: var IntSet): PRope = + ## 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: + var etB = t.skipTypes(abstractInst) + case etB.kind + of tyObject, tyTuple: + if isImportedCppType(etB) and t.kind == tyGenericInst: + result = getTypeDescAux(m, t, check) + else: + let x = getUniqueType(etB) + result = getTypeForward(m, x) + pushType(m, x) + else: + result = getTypeDescAux(m, t, check) + +proc paramStorageLoc(param: PSym): TStorageLoc = + if param.typ.skipTypes({tyVar, tyTypeDesc}).kind notin {tyArray, tyOpenArray}: + result = OnStack + else: + result = OnUnknown + +proc genProcParams(m: BModule, t: PType, rettype, params: var PRope, + check: var IntSet, declareEnvironment=true) = + params = nil + if (t.sons[0] == nil) or isInvalidReturnType(t.sons[0]): + rettype = ~"void" + else: + rettype = getTypeDescAux(m, t.sons[0], check) + for i in countup(1, sonsLen(t.n) - 1): + 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, ~", ") + fillLoc(param.loc, locParam, param.typ, mangleName(param), + param.paramStorageLoc) + if ccgIntroducedPtr(param): + app(params, getTypeDescWeak(m, param.typ, check)) + app(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) + # declare the len field for open arrays: + var arr = param.typ + if arr.kind == tyVar: arr = arr.sons[0] + var j = 0 + while arr.kind in {tyOpenArray, tyVarargs}: + # 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]) + 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 (mapReturnType(t.sons[0]) != ctArray): + app(params, getTypeDescWeak(m, arr, check)) + app(params, "*") + else: + app(params, getTypeDescAux(m, arr, check)) + appf(params, " Result", []) + if t.callConv == ccClosure and declareEnvironment: + if params != nil: app(params, ", ") + app(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) proc mangleRecFieldName(field: PSym, rectype: PType): PRope = if (rectype.sym != nil) and @@ -421,14 +422,18 @@ proc genRecordFieldsAux(m: BModule, n: PNode, if accessExpr != nil: ae = ropef("$1.$2", [accessExpr, sname]) else: ae = sname fillLoc(field.loc, locField, field.typ, ae, OnUnknown) - let fieldType = field.loc.t.skipTypes(abstractInst) - if fieldType.kind == tyArray and tfUncheckedArray in fieldType.flags: - appf(result, "$1 $2[SEQ_DECL_SIZE];$n", - [getTypeDescAux(m, fieldType.elemType, 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]) + # for importcpp'ed objects, we only need to set field.loc, but don't + # have to recurse via 'getTypeDescAux'. And not doing so prevents problems + # with heavily templatized C++ code: + 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", + [getTypeDescAux(m, fieldType.elemType, 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]) else: internalError(n.info, "genRecordFieldsAux()") proc getRecordFields(m: BModule, typ: PType, check: var IntSet): PRope = @@ -483,9 +488,6 @@ proc getTupleDesc(m: BModule, typ: PType, name: PRope, else: app(result, desc) app(result, "};" & tnl) -proc pushType(m: BModule, typ: PType) = - add(m.typeStack, typ) - proc getTypeDescAux(m: BModule, typ: PType, check: var IntSet): PRope = # returns only the type's name var t = getUniqueType(typ) @@ -493,13 +495,16 @@ proc getTypeDescAux(m: BModule, typ: PType, check: var IntSet): PRope = if t.sym != nil: useHeader(m, t.sym) result = getTypePre(m, t) if result != nil: return - if containsOrIncl(check, t.id): + if containsOrIncl(check, t.id): + if isImportedCppType(typ) or isImportedCppType(t): return internalError("cannot generate C type for: " & typeToString(typ)) # XXX: this BUG is hard to fix -> we need to introduce helper structs, # but determining when this needs to be done is hard. We should split # C type generation into an analysis and a code generation phase somehow. case t.kind - of tyRef, tyPtr, tyVar: + of tyRef, tyPtr, tyVar: + var star = if t.kind == tyVar and tfVarIsPtr notin typ.flags and + compileToCpp(m): "&" else: "*" var et = t.lastSon var etB = et.skipTypes(abstractInst) if etB.kind in {tyArrayConstr, tyArray, tyOpenArray, tyVarargs}: @@ -507,27 +512,28 @@ proc getTypeDescAux(m: BModule, typ: PType, check: var IntSet): PRope = # ``var set[char]`` in `getParamTypeDesc` et = elemType(etB) etB = et.skipTypes(abstractInst) + star[0] = '*' case etB.kind of tyObject, tyTuple: if isImportedCppType(etB) and et.kind == tyGenericInst: - result = con(getTypeDescAux(m, et, check), "*") + result = con(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, "*") + result = con(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, "**") + result = con(name, "*" & star) idTablePut(m.typeCache, t, result) pushType(m, x) else: # else we have a strong dependency :-( - result = con(getTypeDescAux(m, et, check), "*") + result = con(getTypeDescAux(m, et, check), star) idTablePut(m.typeCache, t, result) of tyOpenArray, tyVarargs: result = con(getTypeDescAux(m, t.sons[0], check), "*") @@ -670,7 +676,7 @@ proc genProcHeader(m: BModule, prc: PSym): PRope = rettype, params: PRope genCLineDir(result, prc.info) # using static is needed for inline procs - if gCmd != cmdCompileToLLVM and lfExportLib in prc.loc.flags: + if lfExportLib in prc.loc.flags: if m.isHeaderFile: result.app "N_LIB_IMPORT " else: @@ -696,16 +702,7 @@ proc getNimNode(m: BModule): PRope = result = ropef("$1[$2]", [m.typeNodesName, toRope(m.typeNodes)]) inc(m.typeNodes) -when false: - proc getNimType(m: BModule): PRope = - result = ropef("$1[$2]", [m.nimTypesName, toRope(m.nimTypes)]) - inc(m.nimTypes) - - proc allocMemTI(m: BModule, typ: PType, name: PRope) = - var tmp = getNimType(m) - appf(m.s[cfsTypeInit2], "$2 = &$1;$n", [tmp, name]) - -proc genTypeInfoAuxBase(m: BModule, typ: PType, name, base: PRope) = +proc genTypeInfoAuxBase(m: BModule; typ, origType: PType; name, base: PRope) = var nimtypeKind: int #allocMemTI(m, typ, name) if isObjLackingTypeField(typ): @@ -715,6 +712,7 @@ proc genTypeInfoAuxBase(m: BModule, typ: PType, name, base: PRope) = var size: PRope if tfIncompleteStruct in typ.flags: size = toRope"void*" + elif m.compileToCpp: size = getTypeDesc(m, origType) else: size = getTypeDesc(m, typ) appf(m.s[cfsTypeInit3], "$1.size = sizeof($2);$n" & "$1.kind = $3;$n" & "$1.base = $4;$n", @@ -730,13 +728,13 @@ proc genTypeInfoAuxBase(m: BModule, typ: PType, name, base: PRope) = appf(m.s[cfsVars], "TNimType $1; /* $2 */$n", [name, toRope(typeToString(typ))]) -proc genTypeInfoAux(m: BModule, typ: PType, name: PRope) = +proc genTypeInfoAux(m: BModule, typ, origType: PType, name: PRope) = var base: PRope if (sonsLen(typ) > 0) and (typ.sons[0] != nil): base = genTypeInfo(m, typ.sons[0]) else: base = toRope("0") - genTypeInfoAuxBase(m, typ, name, base) + genTypeInfoAuxBase(m, typ, origType, name, base) proc discriminatorTableName(m: BModule, objtype: PType, d: PSym): PRope = # bugfix: we need to search the type that contains the discriminator: @@ -814,11 +812,12 @@ proc genObjectFields(m: BModule, typ: PType, n: PNode, expr: PRope) = field.loc.r, genTypeInfo(m, field.typ), makeCString(field.name.s)]) else: internalError(n.info, "genObjectFields") -proc genObjectInfo(m: BModule, typ: PType, name: PRope) = - if typ.kind == tyObject: genTypeInfoAux(m, typ, name) - else: genTypeInfoAuxBase(m, typ, name, toRope("0")) +proc genObjectInfo(m: BModule, typ, origType: PType, name: PRope) = + if typ.kind == tyObject: genTypeInfoAux(m, typ, origType, name) + else: genTypeInfoAuxBase(m, typ, origType, name, toRope("0")) var tmp = getNimNode(m) - genObjectFields(m, typ, typ.n, tmp) + if not isImportedCppType(typ): + genObjectFields(m, typ, typ.n, tmp) appf(m.s[cfsTypeInit3], "$1.node = &$2;$n", [name, tmp]) var t = typ.sons[0] while t != nil: @@ -827,7 +826,7 @@ proc genObjectInfo(m: BModule, typ: PType, name: PRope) = t = t.sons[0] proc genTupleInfo(m: BModule, typ: PType, name: PRope) = - genTypeInfoAuxBase(m, typ, name, toRope("0")) + genTypeInfoAuxBase(m, typ, typ, name, toRope("0")) var expr = getNimNode(m) var length = sonsLen(typ) if length > 0: @@ -854,7 +853,7 @@ proc genEnumInfo(m: BModule, typ: PType, name: PRope) = # optimizations here: The ``typ`` field is never set, as it is redundant # anyway. We generate a cstring array and a loop over it. Exceptional # positions will be reset after the loop. - genTypeInfoAux(m, typ, name) + genTypeInfoAux(m, typ, typ, name) var nodePtrs = getTempName() var length = sonsLen(typ.n) appf(m.s[cfsTypeInit1], "static TNimNode* $1[$2];$n", @@ -894,13 +893,13 @@ proc genEnumInfo(m: BModule, typ: PType, name: PRope) = proc genSetInfo(m: BModule, typ: PType, name: PRope) = assert(typ.sons[0] != nil) - genTypeInfoAux(m, typ, name) + 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]) proc genArrayInfo(m: BModule, typ: PType, name: PRope) = - genTypeInfoAuxBase(m, typ, name, genTypeInfo(m, typ.sons[1])) + genTypeInfoAuxBase(m, typ, typ, name, genTypeInfo(m, typ.sons[1])) proc fakeClosureType(owner: PSym): PType = # we generate the same RTTI as for a tuple[pointer, ref tuple[]] @@ -946,23 +945,23 @@ proc genTypeInfo(m: BModule, t: PType): PRope = case t.kind of tyEmpty: result = toRope"0" of tyPointer, tyBool, tyChar, tyCString, tyString, tyInt..tyUInt64, tyVar: - genTypeInfoAuxBase(m, t, result, toRope"0") + genTypeInfoAuxBase(m, t, t, result, toRope"0") of tyProc: if t.callConv != ccClosure: - genTypeInfoAuxBase(m, t, result, toRope"0") + genTypeInfoAuxBase(m, t, t, result, toRope"0") else: genTupleInfo(m, fakeClosureType(t.owner), result) of tySequence, tyRef: - genTypeInfoAux(m, t, result) + genTypeInfoAux(m, t, t, result) if gSelectedGC >= gcMarkAndSweep: let markerProc = genTraverseProc(m, t, tiNew) appf(m.s[cfsTypeInit3], "$1.marker = $2;$n", [result, markerProc]) - of tyPtr, tyRange: genTypeInfoAux(m, t, result) + of tyPtr, tyRange: genTypeInfoAux(m, t, t, result) of tyArrayConstr, tyArray: genArrayInfo(m, t, result) of tySet: genSetInfo(m, t, result) of tyEnum: genEnumInfo(m, t, result) - of tyObject: genObjectInfo(m, t, result) - of tyTuple: + of tyObject: genObjectInfo(m, t, origType, result) + of tyTuple: # if t.n != nil: genObjectInfo(m, t, result) # else: # BUGFIX: use consistently RTTI without proper field names; otherwise diff --git a/compiler/ccgutils.nim b/compiler/ccgutils.nim index 1e1fcd6fb3..59b9611fc9 100644 --- a/compiler/ccgutils.nim +++ b/compiler/ccgutils.nim @@ -69,7 +69,20 @@ when false: proc echoStats*() = for i in countup(low(TTypeKind), high(TTypeKind)): echo i, " ", gTypeTable[i].counter - + +proc slowSearch(key: PType; k: TTypeKind): PType = + # tuples are quite horrible as C does not support them directly and + # tuple[string, string] is a (strange) subtype of + # tuple[nameA, nameB: string]. This bites us here, so we + # use 'sameBackendType' instead of 'sameType'. + if idTableHasObjectAsKey(gTypeTable[k], key): return key + for h in countup(0, high(gTypeTable[k].data)): + var t = PType(gTypeTable[k].data[h].key) + if t != nil and sameBackendType(t, key): + return t + idTablePut(gTypeTable[k], key, key) + result = key + proc getUniqueType*(key: PType): PType = # this is a hotspot in the compiler! if key == nil: return @@ -86,7 +99,7 @@ proc getUniqueType*(key: PType): PType = gCanonicalTypes[k] = key result = key of tyTypeDesc, tyTypeClasses, tyGenericParam, tyFromExpr, tyFieldAccessor: - internalError("GetUniqueType") + internalError("getUniqueType") of tyDistinct: if key.deepCopy != nil: result = key else: result = getUniqueType(lastSon(key)) @@ -96,23 +109,20 @@ proc getUniqueType*(key: PType): PType = #if obj.sym != nil and obj.sym.name.s == "TOption": # echo "for ", typeToString(key), " I returned " # debug result - of tyArrayConstr, tyGenericInvokation, tyGenericBody, + of tyPtr, tyRef, tyVar: + let elemType = lastSon(key) + if elemType.kind in {tyBool, tyChar, tyInt..tyUInt64}: + # no canonicalization for integral types, so that e.g. ``ptr pid_t`` is + # produced instead of ``ptr NI``. + result = key + else: + result = slowSearch(key, k) + of tyArrayConstr, tyGenericInvocation, tyGenericBody, tyOpenArray, tyArray, tySet, tyRange, tyTuple, - tyPtr, tyRef, tySequence, tyForward, tyVarargs, tyProxy, tyVar: - # tuples are quite horrible as C does not support them directly and - # tuple[string, string] is a (strange) subtype of - # tuple[nameA, nameB: string]. This bites us here, so we - # use 'sameBackendType' instead of 'sameType'. - + tySequence, tyForward, tyVarargs, tyProxy: # we have to do a slow linear search because types may need # to be compared by their structure: - if idTableHasObjectAsKey(gTypeTable[k], key): return key - for h in countup(0, high(gTypeTable[k].data)): - var t = PType(gTypeTable[k].data[h].key) - if t != nil and sameBackendType(t, key): - return t - idTablePut(gTypeTable[k], key, key) - result = key + result = slowSearch(key, k) of tyObject: if tfFromGeneric notin key.flags: # fast case; lookup per id suffices: @@ -123,9 +133,9 @@ proc getUniqueType*(key: PType): PType = else: # ugly slow case: need to compare by structure if idTableHasObjectAsKey(gTypeTable[k], key): return key - for h in countup(0, high(gTypeTable[k].data)): + for h in countup(0, high(gTypeTable[k].data)): var t = PType(gTypeTable[k].data[h].key) - if t != nil and sameType(t, key): + if t != nil and sameBackendType(t, key): return t idTablePut(gTypeTable[k], key, key) result = key @@ -139,14 +149,8 @@ proc getUniqueType*(key: PType): PType = result = key else: # ugh, we need the canon here: - if idTableHasObjectAsKey(gTypeTable[k], key): return key - for h in countup(0, high(gTypeTable[k].data)): - var t = PType(gTypeTable[k].data[h].key) - if t != nil and sameBackendType(t, key): - return t - idTablePut(gTypeTable[k], key, key) - result = key - + result = slowSearch(key, k) + proc tableGetType*(tab: TIdTable, key: PType): RootRef = # returns nil if we need to declare this type result = idTableGet(tab, key) diff --git a/compiler/cgen.nim b/compiler/cgen.nim index 480c131ae6..cc376d87a7 100644 --- a/compiler/cgen.nim +++ b/compiler/cgen.nim @@ -25,15 +25,6 @@ when options.hasTinyCBackend: var generatedHeader: BModule -proc ropeff(cformat, llvmformat: string, args: varargs[PRope]): PRope = - if gCmd == cmdCompileToLLVM: result = ropef(llvmformat, args) - else: result = ropef(cformat, args) - -proc appff(dest: var PRope, cformat, llvmformat: string, - args: varargs[PRope]) = - if gCmd == cmdCompileToLLVM: appf(dest, llvmformat, args) - else: appf(dest, cformat, args) - proc addForwardedProc(m: BModule, prc: PSym) = m.forwardedProcs.add(prc) inc(gForwardedProcsCounter) @@ -137,79 +128,8 @@ proc ropecg(m: BModule, frmt: TFormatStr, args: varargs[PRope]): PRope = if i - 1 >= start: app(result, substr(frmt, start, i - 1)) -const compileTimeRopeFmt = false - -when compileTimeRopeFmt: - import macros - - type TFmtFragmentKind = enum - ffSym, - ffLit, - ffParam - - type TFragment = object - case kind: TFmtFragmentKind - of ffSym, ffLit: - value: string - of ffParam: - intValue: int - - iterator fmtStringFragments(s: string): tuple[kind: TFmtFragmentKind, - value: string, - intValue: int] = - # This is a bit less featured version of the ropecg's algorithm - # (be careful when replacing ropecg calls) - var - i = 0 - length = s.len - - while i < length: - var start = i - case s[i] - of '$': - let n = s[i+1] - case n - of '$': - inc i, 2 - of '0'..'9': - # XXX: use the new case object construction syntax when it's ready - yield (kind: ffParam, value: "", intValue: n.ord - ord('1')) - inc i, 2 - start = i - else: - inc i - of '#': - inc i - var j = i - while s[i] in IdentChars: inc i - yield (kind: ffSym, value: substr(s, j, i-1), intValue: 0) - start = i - else: discard - - while i < length: - if s[i] != '$' and s[i] != '#': inc i - else: break - - if i - 1 >= start: - yield (kind: ffLit, value: substr(s, start, i-1), intValue: 0) - - macro rfmt(m: BModule, fmt: static[string], args: varargs[PRope]): expr = - ## Experimental optimized rope-formatting operator - ## The run-time code it produces will be very fast, but will it speed up - ## the compilation of nimrod itself or will the macro execution time - ## offset the gains? - result = newCall(bindSym"ropeConcat") - for frag in fmtStringFragments(fmt): - case frag.kind - of ffSym: - result.add(newCall(bindSym"cgsym", m, newStrLitNode(frag.value))) - of ffLit: - result.add(newCall(bindSym"~", newStrLitNode(frag.value))) - of ffParam: - result.add(args[frag.intValue]) -else: - template rfmt(m: BModule, fmt: string, args: varargs[PRope]): expr = - ropecg(m, fmt, args) +template rfmt(m: BModule, fmt: string, args: varargs[PRope]): expr = + ropecg(m, fmt, args) proc appcg(m: BModule, c: var PRope, frmt: TFormatStr, args: varargs[PRope]) = @@ -242,24 +162,14 @@ proc lineCg(p: BProc, s: TCProcSection, frmt: TFormatStr, args: varargs[PRope]) = app(p.s(s), indentLine(p, ropecg(p.module, frmt, args))) -when compileTimeRopeFmt: - template linefmt(p: BProc, s: TCProcSection, frmt: TFormatStr, - args: varargs[PRope]) = - line(p, s, rfmt(p.module, frmt, args)) -else: - 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: TFormatStr, + args: varargs[PRope]) = + app(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 lineFF(p: BProc, s: TCProcSection, cformat, llvmformat: string, - args: varargs[PRope]) = - if gCmd == cmdCompileToLLVM: lineF(p, s, llvmformat, args) - else: lineF(p, s, cformat, args) - proc safeLineNm(info: TLineInfo): int = result = toLinenumber(info) if result < 0: result = 0 # negative numbers are not allowed in #line @@ -267,8 +177,8 @@ proc safeLineNm(info: TLineInfo): int = proc genCLineDir(r: var PRope, filename: string, line: int) = assert line >= 0 if optLineDir in gOptions: - appff(r, "$N#line $2 $1$N", "; line $2 \"$1\"$n", - [toRope(makeSingleLineCString(filename)), toRope(line)]) + appf(r, "$N#line $2 $1$N", + [toRope(makeSingleLineCString(filename)), toRope(line)]) proc genCLineDir(r: var PRope, info: TLineInfo) = genCLineDir(r, info.toFullPath, info.safeLineNm) @@ -402,11 +312,8 @@ proc initLocalVar(p: BProc, v: PSym, immediateAsgn: bool) = proc getTemp(p: BProc, t: PType, result: var TLoc; needsInit=false) = inc(p.labels) - if gCmd == cmdCompileToLLVM: - result.r = con("%LOC", toRope(p.labels)) - else: - result.r = con("LOC", toRope(p.labels)) - linefmt(p, cpsLocals, "$1 $2;$n", getTypeDesc(p.module, t), result.r) + result.r = con("LOC", toRope(p.labels)) + linefmt(p, cpsLocals, "$1 $2;$n", getTypeDesc(p.module, t), result.r) result.k = locTemp #result.a = - 1 result.t = getUniqueType(t) @@ -446,29 +353,6 @@ proc deinitGCFrame(p: BProc): PRope = if p.gcFrameId > 0: result = ropecg(p.module, "if (((NU)&GCFRAME) < 4096) #nimGCFrame(&GCFRAME);$n") - -proc cstringLit(p: BProc, r: var PRope, s: string): PRope = - if gCmd == cmdCompileToLLVM: - inc(p.module.labels) - inc(p.labels) - result = ropef("%LOC$1", [toRope(p.labels)]) - appf(p.module.s[cfsData], "@C$1 = private constant [$2 x i8] $3$n", - [toRope(p.module.labels), toRope(len(s)), makeLLVMString(s)]) - appf(r, "$1 = getelementptr [$2 x i8]* @C$3, %NI 0, %NI 0$n", - [result, toRope(len(s)), toRope(p.module.labels)]) - else: - result = makeCString(s) - -proc cstringLit(m: BModule, r: var PRope, s: string): PRope = - if gCmd == cmdCompileToLLVM: - inc(m.labels, 2) - result = ropef("%MOC$1", [toRope(m.labels - 1)]) - appf(m.s[cfsData], "@MOC$1 = private constant [$2 x i8] $3$n", - [toRope(m.labels), toRope(len(s)), makeLLVMString(s)]) - appf(r, "$1 = getelementptr [$2 x i8]* @MOC$3, %NI 0, %NI 0$n", - [result, toRope(len(s)), toRope(m.labels)]) - else: - result = makeCString(s) proc allocParam(p: BProc, s: PSym) = assert(s.kind == skParam) @@ -494,22 +378,26 @@ proc localDebugInfo(p: BProc, s: PSym) = inc(p.maxFrameLen) inc p.blocks[p.blocks.len-1].frameLen -proc assignLocalVar(p: BProc, s: PSym) = - #assert(s.loc.k == locNone) // not yet assigned - # this need not be fullfilled for inline procs; they are regenerated - # for each module that uses them! +proc localVarDecl(p: BProc; s: PSym): PRope = if s.loc.k == locNone: fillLoc(s.loc, locLocalVar, s.typ, mangleName(s), OnStack) if s.kind == skLet: incl(s.loc.flags, lfNoDeepCopy) - var decl = getTypeDesc(p.module, s.loc.t) + result = getTypeDesc(p.module, s.loc.t) if s.constraint.isNil: - if sfRegister in s.flags: app(decl, " register") + if sfRegister in s.flags: app(result, " register") #elif skipTypes(s.typ, abstractInst).kind in GcTypeKinds: # app(decl, " GC_GUARD") - if sfVolatile in s.flags: app(decl, " volatile") - appf(decl, " $1;$n", [s.loc.r]) + if sfVolatile in s.flags: app(result, " volatile") + app(result, " ") + app(result, s.loc.r) else: - decl = ropef(s.cgDeclFrmt & ";$n", decl, s.loc.r) + result = ropef(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) line(p, cpsLocals, decl) localDebugInfo(p, s) @@ -552,13 +440,11 @@ proc assignGlobalVar(p: BProc, s: PSym) = {optStackTrace, optEndb}: appcg(p.module, p.module.s[cfsDebugInit], "#dbgRegisterGlobal($1, &$2, $3);$n", - [cstringLit(p, p.module.s[cfsDebugInit], - normalize(s.owner.name.s & '.' & s.name.s)), + [makeCString(normalize(s.owner.name.s & '.' & s.name.s)), s.loc.r, genTypeInfo(p.module, s.typ)]) proc assignParam(p: BProc, s: PSym) = assert(s.loc.r != nil) - if sfAddrTaken in s.flags and gCmd == cmdCompileToLLVM: allocParam(p, s) localDebugInfo(p, s) proc fillProcLoc(sym: PSym) = @@ -586,6 +472,11 @@ proc initLocExpr(p: BProc, e: PNode, result: var TLoc) = initLoc(result, locNone, e.typ, OnUnknown) expr(p, e, result) +proc initLocExprSingleUse(p: BProc, e: PNode, result: var TLoc) = + initLoc(result, locNone, e.typ, OnUnknown) + result.flags.incl lfSingleUse + expr(p, e, result) + proc lenField(p: BProc): PRope = result = toRope(if p.module.compileToCpp: "len" else: "Sup.len") @@ -647,7 +538,6 @@ proc symInDynamicLib(m: BModule, sym: PSym) = let isCall = isGetProcAddr(lib) var extname = sym.loc.r if not isCall: loadDynamicLib(m, lib) - if gCmd == cmdCompileToLLVM: incl(sym.loc.flags, lfIndirect) var tmp = mangleDynLibProc(sym) sym.loc.r = tmp # from now on we only need the internal name sym.typ.sym = nil # generate a new name @@ -663,7 +553,7 @@ proc symInDynamicLib(m: BModule, sym: PSym) = params.app(", ") let load = ropef("\t$1 = ($2) ($3$4));$n", [tmp, getTypeDesc(m, sym.typ), - params, cstringLit(m, m.s[cfsDynLibInit], ropeToStr(extname))]) + params, makeCString(ropeToStr(extname))]) var last = lastSon(n) if last.kind == nkHiddenStdConv: last = last.sons[1] internalAssert(last.kind == nkStrLit) @@ -678,10 +568,8 @@ proc symInDynamicLib(m: BModule, sym: PSym) = appcg(m, m.s[cfsDynLibInit], "\t$1 = ($2) #nimGetProcAddr($3, $4);$n", [tmp, getTypeDesc(m, sym.typ), - lib.name, cstringLit(m, m.s[cfsDynLibInit], ropeToStr(extname))]) - appff(m.s[cfsVars], "$2 $1;$n", - "$1 = linkonce global $2 zeroinitializer$n", - [sym.loc.r, getTypeDesc(m, sym.loc.t)]) + lib.name, makeCString(ropeToStr(extname))]) + appf(m.s[cfsVars], "$2 $1;$n", [sym.loc.r, getTypeDesc(m, sym.loc.t)]) proc varInDynamicLib(m: BModule, sym: PSym) = var lib = sym.annex @@ -694,7 +582,7 @@ proc varInDynamicLib(m: BModule, sym: PSym) = appcg(m, m.s[cfsDynLibInit], "$1 = ($2*) #nimGetProcAddr($3, $4);$n", [tmp, getTypeDesc(m, sym.typ), - lib.name, cstringLit(m, m.s[cfsDynLibInit], ropeToStr(extname))]) + lib.name, makeCString(ropeToStr(extname))]) appf(m.s[cfsVars], "$2* $1;$n", [sym.loc.r, getTypeDesc(m, sym.loc.t)]) @@ -717,13 +605,13 @@ proc cgsym(m: BModule, name: string): PRope = rawMessage(errSystemNeeds, name) result = sym.loc.r -proc generateHeaders(m: BModule) = +proc generateHeaders(m: BModule) = app(m.s[cfsHeaders], tnl & "#include \"nimbase.h\"" & tnl) var it = PStrEntry(m.headerFiles.head) - while it != nil: + while it != nil: if it.data[0] notin {'\"', '<'}: appf(m.s[cfsHeaders], "$N#include \"$1\"$N", [toRope(it.data)]) - else: + else: appf(m.s[cfsHeaders], "$N#include $1$N", [toRope(it.data)]) it = PStrEntry(it.next) @@ -796,16 +684,17 @@ proc genProcAux(m: BModule, prc: PSym) = app(generatedProc, initGCFrame(p)) if optStackTrace in prc.options: app(generatedProc, p.s(cpsLocals)) - var procname = cstringLit(p, generatedProc, prc.name.s) + var procname = makeCString(prc.name.s) app(generatedProc, initFrame(p, procname, prc.info.quotedFilename)) else: app(generatedProc, p.s(cpsLocals)) - if (optProfiler in prc.options) and (gCmd != cmdCompileToLLVM): + 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, ~"\tBeforeRet: ;$n") + if p.beforeRetNeeded: app(generatedProc, ~"\t}BeforeRet: ;$n") app(generatedProc, deinitGCFrame(p)) if optStackTrace in prc.options: app(generatedProc, deinitFrame(p)) app(generatedProc, returnStmt) @@ -825,7 +714,6 @@ proc genProcPrototype(m: BModule, sym: PSym) = not containsOrIncl(m.declaredThings, sym.id): app(m.s[cfsVars], rfmt(nil, "extern $1 $2;$n", getTypeDesc(m, sym.loc.t), mangleDynLibProc(sym))) - if gCmd == cmdCompileToLLVM: incl(sym.loc.flags, lfIndirect) elif not containsOrIncl(m.declaredProtos, sym.id): var header = genProcHeader(m, sym) if sym.typ.callConv != ccInline and crossesCppBoundary(m, sym): @@ -923,21 +811,17 @@ proc addIntTypes(result: var PRope) {.inline.} = proc getCopyright(cfile: string): PRope = if optCompileOnly in gGlobalOptions: - result = ropeff("/* Generated by Nim Compiler v$1 */$N" & + result = ropef("/* Generated by Nim Compiler v$1 */$N" & "/* (c) 2015 Andreas Rumpf */$N" & "/* The generated code is subject to the original license. */$N", - "; Generated by Nim Compiler v$1$N" & - "; (c) 2012 Andreas Rumpf$N", [toRope(VersionAsString)]) + [toRope(VersionAsString)]) else: - result = ropeff("/* Generated by Nim Compiler v$1 */$N" & + result = ropef("/* 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", - "; Generated by Nim Compiler v$1$N" & - "; (c) 2015 Andreas Rumpf$N" & - "; Compiled for: $2, $3, $4$N" & - "; Command for LLVM compiler:$N $5$N", [toRope(VersionAsString), + [toRope(VersionAsString), toRope(platform.OS[targetOS].name), toRope(platform.CPU[targetCPU].name), toRope(extccomp.CC[extccomp.cCompiler].name), @@ -1088,13 +972,11 @@ proc registerModuleToMain(m: PSym) = var init = m.getInitName datInit = m.getDatInitName - appff(mainModProcs, "NIM_EXTERNC N_NOINLINE(void, $1)(void);$N", - "declare void $1() noinline$N", [init]) - appff(mainModProcs, "NIM_EXTERNC N_NOINLINE(void, $1)(void);$N", - "declare void $1() noinline$N", [datInit]) + appf(mainModProcs, "NIM_EXTERNC N_NOINLINE(void, $1)(void);$N", [init]) + appf(mainModProcs, "NIM_EXTERNC N_NOINLINE(void, $1)(void);$N", [datInit]) if sfSystemModule notin m.flags: - appff(mainDatInit, "\t$1();$N", "call void ()* $1$n", [datInit]) - let initCall = ropeff("\t$1();$N", "call void ()* $1$n", [init]) + appf(mainDatInit, "\t$1();$N", [datInit]) + let initCall = ropef("\t$1();$N", [init]) if sfMainModule in m.flags: app(mainModInit, initCall) else: @@ -1102,8 +984,7 @@ proc registerModuleToMain(m: PSym) = proc genInitCode(m: BModule) = var initname = getInitName(m.module) - var prc = ropeff("NIM_EXTERNC N_NOINLINE(void, $1)(void) {$N", - "define void $1() noinline {$n", [initname]) + var prc = ropef("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)]) @@ -1124,7 +1005,7 @@ proc genInitCode(m: BModule) = # declare it nevertheless: m.frameDeclared = true if not m.preventStackTrace: - var procname = cstringLit(m.initProc, prc, m.module.name.s) + var procname = makeCString(m.module.name.s) app(prc, initFrame(m.initProc, procname, m.module.info.quotedFilename)) else: app(prc, ~"\tTFrame F; F.len = 0;$N") @@ -1145,8 +1026,8 @@ proc genInitCode(m: BModule) = app(prc, deinitGCFrame(m.initProc)) appf(prc, "}$N$N") - prc.appff("NIM_EXTERNC N_NOINLINE(void, $1)(void) {$N", - "define void $1() noinline {$n", [getDatInitName(m.module)]) + prc.appf("NIM_EXTERNC N_NOINLINE(void, $1)(void) {$N", + [getDatInitName(m.module)]) for i in cfsTypeInit1..cfsDynLibInit: app(prc, genSectionStart(i)) diff --git a/compiler/cgendata.nim b/compiler/cgendata.nim index 508f980747..bb98454a7f 100644 --- a/compiler/cgendata.nim +++ b/compiler/cgendata.nim @@ -82,6 +82,9 @@ type maxFrameLen*: int # max length of frame descriptor module*: BModule # used to prevent excessive parameter passing withinLoop*: int # > 0 if we are within a loop + splitDecls*: int # > 0 if we are in some context for C++ that + # requires 'T x = T()' to become 'T x; x = T()' + # (yes, C++ is weird like that) gcFrameId*: Natural # for the GC stack marking gcFrameType*: PRope # the struct {} we put the GC markers into diff --git a/compiler/commands.nim b/compiler/commands.nim index 78fa9249c9..a2d02e469b 100644 --- a/compiler/commands.nim +++ b/compiler/commands.nim @@ -52,7 +52,7 @@ proc processSwitch*(switch, arg: string, pass: TCmdLinePass, info: TLineInfo) const HelpMessage = "Nim Compiler Version $1 (" & CompileDate & ") [$2: $3]\n" & - "Copyright (c) 2006-2014 by Andreas Rumpf\n" + "Copyright (c) 2006-2015 by Andreas Rumpf\n" const Usage = slurp"doc/basicopt.txt".replace("//", "") @@ -141,7 +141,7 @@ proc expectNoArg(switch, arg: string, pass: TCmdLinePass, info: TLineInfo) = if arg != "": localError(info, errCmdLineNoArgExpected, addPrefix(switch)) proc processSpecificNote(arg: string, state: TSpecialWord, pass: TCmdLinePass, - info: TLineInfo) = + info: TLineInfo; orig: string) = var id = "" # arg = "X]:on|off" var i = 0 var n = hintMin @@ -149,17 +149,17 @@ proc processSpecificNote(arg: string, state: TSpecialWord, pass: TCmdLinePass, add(id, arg[i]) inc(i) if i < len(arg) and (arg[i] == ']'): inc(i) - else: invalidCmdLineOption(pass, arg, info) + else: invalidCmdLineOption(pass, orig, info) if i < len(arg) and (arg[i] in {':', '='}): inc(i) - else: invalidCmdLineOption(pass, arg, info) - if state == wHint: + else: invalidCmdLineOption(pass, orig, info) + if state == wHint: var x = findStr(msgs.HintsToStr, id) if x >= 0: n = TNoteKind(x + ord(hintMin)) - else: invalidCmdLineOption(pass, arg, info) - else: + else: localError(info, "unknown hint: " & id) + else: var x = findStr(msgs.WarningsToStr, id) if x >= 0: n = TNoteKind(x + ord(warnMin)) - else: invalidCmdLineOption(pass, arg, info) + else: localError(info, "unknown warning: " & id) case whichKeyword(substr(arg, i)) of wOn: incl(gNotes, n) of wOff: excl(gNotes, n) @@ -326,7 +326,7 @@ proc processSwitch(switch, arg: string, pass: TCmdLinePass, info: TLineInfo) = of "link": expectArg(switch, arg, pass, info) if pass in {passCmd2, passPP}: addFileToLink(arg) - of "debuginfo": + of "debuginfo": expectNoArg(switch, arg, pass, info) incl(gGlobalOptions, optCDebug) of "embedsrc": @@ -368,16 +368,26 @@ proc processSwitch(switch, arg: string, pass: TCmdLinePass, info: TLineInfo) = defineSymbol("nogc") else: localError(info, errNoneBoehmRefcExpectedButXFound, arg) of "warnings", "w": processOnOffSwitch({optWarns}, arg, pass, info) - of "warning": processSpecificNote(arg, wWarning, pass, info) - of "hint": processSpecificNote(arg, wHint, pass, info) + of "warning": processSpecificNote(arg, wWarning, pass, info, switch) + of "hint": processSpecificNote(arg, wHint, pass, info, switch) of "hints": processOnOffSwitch({optHints}, arg, pass, info) of "threadanalysis": processOnOffSwitchG({optThreadAnalysis}, arg, pass, info) of "stacktrace": processOnOffSwitch({optStackTrace}, arg, pass, info) of "linetrace": processOnOffSwitch({optLineTrace}, arg, pass, info) - of "debugger": - processOnOffSwitch({optEndb}, arg, pass, info) - if optEndb in gOptions: defineSymbol("endb") - else: undefSymbol("endb") + of "debugger": + case arg.normalize + of "on", "endb": + gOptions.incl optEndb + defineSymbol("endb") + of "off": + gOptions.excl optEndb + undefSymbol("endb") + of "native", "gdb": + incl(gGlobalOptions, optCDebug) + gOptions = gOptions + {optLineDir} - {optEndb} + undefSymbol("endb") + else: + localError(info, "expected endb|gdb but found " & arg) of "profiler": processOnOffSwitch({optProfiler}, arg, pass, info) if optProfiler in gOptions: defineSymbol("profiler") diff --git a/compiler/condsyms.nim b/compiler/condsyms.nim index 8a5189167a..7ddf44d4ac 100644 --- a/compiler/condsyms.nim +++ b/compiler/condsyms.nim @@ -89,6 +89,7 @@ proc initDefines*() = defineSymbol("nimparsebiggestfloatmagic") defineSymbol("nimalias") defineSymbol("nimlocks") + defineSymbol("nimnode") # add platform specific symbols: for c in low(CPU)..high(CPU): diff --git a/compiler/docgen.nim b/compiler/docgen.nim index 3f4f39c279..5439922aff 100644 --- a/compiler/docgen.nim +++ b/compiler/docgen.nim @@ -126,7 +126,7 @@ proc ropeFormatNamedVars(frmt: TFormatStr, varnames: openArray[string], if not (frmt[i] in {'A'..'Z', '_', 'a'..'z', '\x80'..'\xFF'}): break var idx = getVarIdx(varnames, id) if idx >= 0: app(result, varvalues[idx]) - else: rawMessage(errUnkownSubstitionVar, id) + else: rawMessage(errUnknownSubstitionVar, id) of '{': var id = "" inc(i) @@ -138,7 +138,7 @@ proc ropeFormatNamedVars(frmt: TFormatStr, varnames: openArray[string], # search for the variable: var idx = getVarIdx(varnames, id) if idx >= 0: app(result, varvalues[idx]) - else: rawMessage(errUnkownSubstitionVar, id) + else: rawMessage(errUnknownSubstitionVar, id) else: internalError("ropeFormatNamedVars") var start = i while i < L: @@ -272,7 +272,7 @@ proc complexName(k: TSymKind, n: PNode, baseName: string): string = ## type)?(,param type)*``. The callable type part will be added only if the ## node is not a proc, as those are the common ones. The suffix will be a dot ## and a single letter representing the type of the callable. The parameter - ## types will be added with a preceeding dash. Return types won't be added. + ## types will be added with a preceding dash. Return types won't be added. ## ## If you modify the output of this proc, please update the anchor generation ## section of ``doc/docgen.txt``. diff --git a/compiler/extccomp.nim b/compiler/extccomp.nim index bc888315ff..5f6033e574 100644 --- a/compiler/extccomp.nim +++ b/compiler/extccomp.nim @@ -380,7 +380,7 @@ proc setCC*(ccname: string) = cCompiler = nameToCC(ccname) if cCompiler == ccNone: rawMessage(errUnknownCcompiler, ccname) compileOptions = getConfigVar(cCompiler, ".options.always") - linkOptions = getConfigVar(cCompiler, ".options.linker") + linkOptions = "" ccompilerpath = getConfigVar(cCompiler, ".path") for i in countup(low(CC), high(CC)): undefSymbol(CC[i].name) defineSymbol(CC[cCompiler].name) @@ -389,8 +389,8 @@ 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) = - if find(linkOptions, option, 0) < 0: addOpt(linkOptions, option) +proc addLinkOption*(option: string) = + addOpt(linkOptions, option) proc addCompileOption*(option: string) = if strutils.find(compileOptions, option, 0) < 0: @@ -401,7 +401,7 @@ proc initVars*() = for i in countup(low(CC), high(CC)): undefSymbol(CC[i].name) defineSymbol(CC[cCompiler].name) addCompileOption(getConfigVar(cCompiler, ".options.always")) - addLinkOption(getConfigVar(cCompiler, ".options.linker")) + #addLinkOption(getConfigVar(cCompiler, ".options.linker")) if len(ccompilerpath) == 0: ccompilerpath = getConfigVar(cCompiler, ".path") @@ -428,13 +428,17 @@ proc addFileToLink*(filename: string) = prependStr(toLink, filename) # BUGFIX: was ``appendStr`` -proc execExternalProgram*(cmd: string, prettyCmd = "") = +proc execWithEcho(cmd: string, prettyCmd = ""): int = if optListCmd in gGlobalOptions or gVerbosity > 0: if prettyCmd != "": msgWriteln(prettyCmd) else: msgWriteln(cmd) - if execCmd(cmd) != 0: rawMessage(errExecutionOfProgramFailed, "") + result = execCmd(cmd) + +proc execExternalProgram*(cmd: string, prettyCmd = "") = + if execWithEcho(cmd, prettyCmd) != 0: + rawMessage(errExecutionOfProgramFailed, "") proc generateScript(projectFile: string, script: PRope) = let (dir, name, ext) = splitFile(projectFile) @@ -549,13 +553,13 @@ proc getCompileCFileCmd*(cfilename: string, isExternal = false): string = cfile = quoteShell(cfile) result = quoteShell(compilePattern % [ "file", cfile, "objfile", objfile, "options", options, - "include", includeCmd, "nimrod", getPrefixDir(), + "include", includeCmd, "nim", getPrefixDir(), "nim", getPrefixDir(), "lib", libpath]) add(result, ' ') addf(result, CC[c].compileTmpl, [ "file", cfile, "objfile", objfile, "options", options, "include", includeCmd, - "nimrod", quoteShell(getPrefixDir()), + "nim", quoteShell(getPrefixDir()), "nim", quoteShell(getPrefixDir()), "lib", quoteShell(libpath)]) @@ -622,15 +626,17 @@ proc callCCompiler*(projectfile: string) = if gNumberOfProcessors == 0: gNumberOfProcessors = countProcessors() var res = 0 if gNumberOfProcessors <= 1: - for i in countup(0, high(cmds)): res = max(execCmd(cmds[i]), res) + for i in countup(0, high(cmds)): + res = execWithEcho(cmds[i]) + if res != 0: rawMessage(errExecutionOfProgramFailed, []) elif optListCmd in gGlobalOptions or gVerbosity > 1: - res = execProcesses(cmds, {poEchoCmd, poUseShell, poParentStreams}, + res = execProcesses(cmds, {poEchoCmd, poUsePath, poParentStreams}, gNumberOfProcessors) elif gVerbosity == 1: - res = execProcesses(cmds, {poUseShell, poParentStreams}, + res = execProcesses(cmds, {poUsePath, poParentStreams}, gNumberOfProcessors, prettyCb) else: - res = execProcesses(cmds, {poUseShell, poParentStreams}, + res = execProcesses(cmds, {poUsePath, poParentStreams}, gNumberOfProcessors) if res != 0: if gNumberOfProcessors <= 1: @@ -673,15 +679,16 @@ proc callCCompiler*(projectfile: string) = if not exefile.isAbsolute(): exefile = joinPath(splitFile(projectfile).dir, exefile) exefile = quoteShell(exefile) - let linkOptions = getLinkOptions() + let linkOptions = getLinkOptions() & " " & + getConfigVar(cCompiler, ".options.linker") linkCmd = quoteShell(linkCmd % ["builddll", builddll, "buildgui", buildgui, "options", linkOptions, "objfiles", objfiles, - "exefile", exefile, "nimrod", getPrefixDir(), "lib", libpath]) + "exefile", exefile, "nim", getPrefixDir(), "lib", libpath]) linkCmd.add ' ' addf(linkCmd, CC[c].linkTmpl, ["builddll", builddll, "buildgui", buildgui, "options", linkOptions, "objfiles", objfiles, "exefile", exefile, - "nimrod", quoteShell(getPrefixDir()), + "nim", quoteShell(getPrefixDir()), "lib", quoteShell(libpath)]) if optCompileOnly notin gGlobalOptions: if gVerbosity == 1: @@ -710,7 +717,8 @@ proc writeMapping*(gSymbolMapping: PRope) = app(code, strutils.escape(getCompileOptions())) app(code, "\n[Linker]\nFlags=") - app(code, strutils.escape(getLinkOptions())) + app(code, strutils.escape(getLinkOptions() & " " & + getConfigVar(cCompiler, ".options.linker"))) app(code, "\n[Environment]\nlibpath=") app(code, strutils.escape(libpath)) diff --git a/compiler/forloops.nim b/compiler/forloops.nim new file mode 100644 index 0000000000..efe000968c --- /dev/null +++ b/compiler/forloops.nim @@ -0,0 +1,89 @@ +# +# +# The Nim Compiler +# (c) Copyright 2015 Andreas Rumpf +# +# See the file "copying.txt", included in this +# distribution, for details about the copyright. +# + +## This module implements for loop detection for better C code generation. + +import ast, astalgo + +const + someCmp = {mEqI, mEqI64, mEqF64, mEqEnum, mEqCh, mEqB, mEqRef, mEqProc, + mEqUntracedRef, mLeI, mLeI64, mLeF64, mLeU, mLeU64, mLeEnum, + mLeCh, mLeB, mLePtr, mLtI, mLtI64, mLtF64, mLtU, mLtU64, mLtEnum, + mLtCh, mLtB, mLtPtr} + +proc isCounter(s: PSym): bool {.inline.} = + s.kind in {skResult, skVar, skLet, skTemp} and + {sfGlobal, sfAddrTaken} * s.flags == {} + +proc isCall(n: PNode): bool {.inline.} = + n.kind in nkCallKinds and n[0].kind == nkSym + +proc fromSystem(op: PSym): bool = sfSystemModule in getModule(op).flags + +proc getCounter(lastStmt: PNode): PSym = + if lastStmt.isCall: + let op = lastStmt.sym + if op.magic in {mDec, mInc} or + ((op.name.s == "+=" or op.name.s == "-=") and op.fromSystem): + if op[1].kind == nkSym and isCounter(op[1].sym): + result = op[1].sym + +proc counterInTree(n, loop: PNode; counter: PSym): bool = + # prune the search tree: within the loop the counter may be used: + if n == loop: return + case n.kind + of nkSym: + if n.sym == counter: return true + of nkVarSection, nkLetSection: + # definitions are fine! + for it in n: + if counterInTree(it.lastSon): return true + else: + for i in 0 .. When to create the closure? --> for the (count) occurence! + # --> When to create the closure? --> for the (count) occurrence! discard """ for i in foo(): ... diff --git a/compiler/lexer.nim b/compiler/lexer.nim index 4fbac2d5cb..c867621212 100644 --- a/compiler/lexer.nim +++ b/compiler/lexer.nim @@ -107,7 +107,7 @@ type TToken* = object # a Nim token tokType*: TTokType # the type of the token indent*: int # the indentation; != -1 if the token has been - # preceeded with indentation + # preceded with indentation ident*: PIdent # the parsed identifier iNumber*: BiggestInt # the parsed integer literal fNumber*: BiggestFloat # the parsed floating point literal @@ -181,10 +181,8 @@ proc prettyTok*(tok: TToken): string = else: result = tokToStr(tok) proc printTok*(tok: TToken) = - write(stdout, tok.line, ":", tok.col, "\t") - write(stdout, TokTypeToStr[tok.tokType]) - write(stdout, " ") - writeln(stdout, tokToStr(tok)) + msgWriteln($tok.line & ":" & $tok.col & "\t" & + TokTypeToStr[tok.tokType] & " " & tokToStr(tok)) var dummyIdent: PIdent @@ -679,7 +677,7 @@ proc getOperator(L: var TLexer, tok: var TToken) = inc(pos) endOperator(L, tok, pos, h) # advance pos but don't store it in L.bufpos so the next token (which might - # be an operator too) gets the preceeding spaces: + # be an operator too) gets the preceding spaces: tok.strongSpaceB = 0 while buf[pos] == ' ': inc pos diff --git a/compiler/llstream.nim b/compiler/llstream.nim index be469548d3..18ca4aec78 100644 --- a/compiler/llstream.nim +++ b/compiler/llstream.nim @@ -30,47 +30,32 @@ type PLLStream* = ref TLLStream -proc llStreamOpen*(data: string): PLLStream -proc llStreamOpen*(f: var File): PLLStream -proc llStreamOpen*(filename: string, mode: FileMode): PLLStream -proc llStreamOpen*(): PLLStream -proc llStreamOpenStdIn*(): PLLStream -proc llStreamClose*(s: PLLStream) -proc llStreamRead*(s: PLLStream, buf: pointer, bufLen: int): int -proc llStreamReadLine*(s: PLLStream, line: var string): bool -proc llStreamReadAll*(s: PLLStream): string -proc llStreamWrite*(s: PLLStream, data: string) -proc llStreamWrite*(s: PLLStream, data: char) -proc llStreamWrite*(s: PLLStream, buf: pointer, buflen: int) -proc llStreamWriteln*(s: PLLStream, data: string) -# implementation - -proc llStreamOpen(data: string): PLLStream = +proc llStreamOpen*(data: string): PLLStream = new(result) result.s = data result.kind = llsString -proc llStreamOpen(f: var File): PLLStream = +proc llStreamOpen*(f: File): PLLStream = new(result) result.f = f result.kind = llsFile -proc llStreamOpen(filename: string, mode: FileMode): PLLStream = +proc llStreamOpen*(filename: string, mode: FileMode): PLLStream = new(result) result.kind = llsFile if not open(result.f, filename, mode): result = nil -proc llStreamOpen(): PLLStream = +proc llStreamOpen*(): PLLStream = new(result) result.kind = llsNone -proc llStreamOpenStdIn(): PLLStream = +proc llStreamOpenStdIn*(): PLLStream = new(result) result.kind = llsStdIn result.s = "" result.lineOffset = -1 -proc llStreamClose(s: PLLStream) = +proc llStreamClose*(s: PLLStream) = case s.kind of llsNone, llsString, llsStdIn: discard @@ -130,7 +115,7 @@ proc llReadFromStdin(s: PLLStream, buf: pointer, bufLen: int): int = copyMem(buf, addr(s.s[s.rd]), result) inc(s.rd, result) -proc llStreamRead(s: PLLStream, buf: pointer, bufLen: int): int = +proc llStreamRead*(s: PLLStream, buf: pointer, bufLen: int): int = case s.kind of llsNone: result = 0 @@ -144,7 +129,7 @@ proc llStreamRead(s: PLLStream, buf: pointer, bufLen: int): int = of llsStdIn: result = llReadFromStdin(s, buf, bufLen) -proc llStreamReadLine(s: PLLStream, line: var string): bool = +proc llStreamReadLine*(s: PLLStream, line: var string): bool = setLen(line, 0) case s.kind of llsNone: @@ -168,7 +153,7 @@ proc llStreamReadLine(s: PLLStream, line: var string): bool = of llsStdIn: result = readLine(stdin, line) -proc llStreamWrite(s: PLLStream, data: string) = +proc llStreamWrite*(s: PLLStream, data: string) = case s.kind of llsNone, llsStdIn: discard @@ -178,11 +163,11 @@ proc llStreamWrite(s: PLLStream, data: string) = of llsFile: write(s.f, data) -proc llStreamWriteln(s: PLLStream, data: string) = +proc llStreamWriteln*(s: PLLStream, data: string) = llStreamWrite(s, data) llStreamWrite(s, "\n") -proc llStreamWrite(s: PLLStream, data: char) = +proc llStreamWrite*(s: PLLStream, data: char) = var c: char case s.kind of llsNone, llsStdIn: @@ -194,7 +179,7 @@ proc llStreamWrite(s: PLLStream, data: char) = c = data discard writeBuffer(s.f, addr(c), sizeof(c)) -proc llStreamWrite(s: PLLStream, buf: pointer, buflen: int) = +proc llStreamWrite*(s: PLLStream, buf: pointer, buflen: int) = case s.kind of llsNone, llsStdIn: discard @@ -206,7 +191,7 @@ proc llStreamWrite(s: PLLStream, buf: pointer, buflen: int) = of llsFile: discard writeBuffer(s.f, buf, buflen) -proc llStreamReadAll(s: PLLStream): string = +proc llStreamReadAll*(s: PLLStream): string = const bufSize = 2048 case s.kind diff --git a/compiler/lookups.nim b/compiler/lookups.nim index 21d07f2804..6d3379bb90 100644 --- a/compiler/lookups.nim +++ b/compiler/lookups.nim @@ -176,7 +176,7 @@ proc addInterfaceDeclAux(c: PContext, sym: PSym) = if sfExported in sym.flags: # add to interface: if c.module != nil: strTableAdd(c.module.tab, sym) - else: internalError(sym.info, "AddInterfaceDeclAux") + else: internalError(sym.info, "addInterfaceDeclAux") proc addInterfaceDeclAt*(c: PContext, scope: PScope, sym: PSym) = addDeclAt(scope, sym) diff --git a/compiler/main.nim b/compiler/main.nim index 06dcad7b0e..f7592a891d 100644 --- a/compiler/main.nim +++ b/compiler/main.nim @@ -62,7 +62,7 @@ proc commandCompileToC = compileProject() cgenWriteModules() if gCmd != cmdRun: - extccomp.callCCompiler(changeFileExt(gProjectFull, "")) + extccomp.callCCompiler(if gProjectName == "-": "stdinfile" else: changeFileExt(gProjectFull, "")) if isServing: # caas will keep track only of the compilation commands @@ -251,7 +251,6 @@ proc mainCommand* = commandCompileToC() of "cpp", "compiletocpp": gCmd = cmdCompileToCpp - if cCompiler == ccGcc: setCC("gcc") defineSymbol("cpp") commandCompileToC() of "objc", "compiletooc": diff --git a/compiler/modules.nim b/compiler/modules.nim index db05ccc6cb..a2b739efc7 100644 --- a/compiler/modules.nim +++ b/compiler/modules.nim @@ -116,7 +116,7 @@ proc newModule(fileIdx: int32): PSym = result.kind = skModule let filename = fileIdx.toFullPath result.name = getIdent(splitFile(filename).name) - if not isNimIdentifier(result.name.s): + if result.name.s != "-" and not isNimIdentifier(result.name.s): rawMessage(errInvalidModuleName, result.name.s) result.info = newLineInfo(fileIdx, 1, 1) diff --git a/compiler/msgs.nim b/compiler/msgs.nim index e78414804e..088468dcc1 100644 --- a/compiler/msgs.nim +++ b/compiler/msgs.nim @@ -69,7 +69,7 @@ type errInvalidOrderInArrayConstructor, errInvalidOrderInEnumX, errEnumXHasHoles, errExceptExpected, errInvalidTry, errOptionExpected, errXisNoLabel, errNotAllCasesCovered, - errUnkownSubstitionVar, errComplexStmtRequiresInd, errXisNotCallable, + errUnknownSubstitionVar, errComplexStmtRequiresInd, errXisNotCallable, errNoPragmasAllowedForX, errNoGenericParamsAllowedForX, errInvalidParamKindX, errDefaultArgumentInvalid, errNamedParamHasToBeIdent, errNoReturnTypeForX, errConvNeedsOneArg, errInvalidPragmaX, @@ -89,7 +89,7 @@ type errTIsNotAConcreteType, errInvalidSectionStart, errGridTableNotImplemented, errGeneralParseError, errNewSectionExpected, errWhitespaceExpected, errXisNoValidIndexFile, - errCannotRenderX, errVarVarTypeNotAllowed, errInstantiateXExplicitely, + errCannotRenderX, errVarVarTypeNotAllowed, errInstantiateXExplicitly, errOnlyACallOpCanBeDelegator, errUsingNoSymbol, errMacroBodyDependsOnGenericTypes, errDestructorNotGenericEnough, @@ -113,7 +113,7 @@ type warnSmallLshouldNotBeUsed, warnUnknownMagic, warnRedefinitionOfLabel, warnUnknownSubstitutionX, warnLanguageXNotSupported, warnFieldXNotSupported, warnCommentXIgnored, - warnNilStatement, warnAnalysisLoophole, + warnNilStatement, warnTypelessParam, warnDifferentHeaps, warnWriteToForeignHeap, warnUnsafeCode, warnEachIdentIsTuple, warnShadowIdent, warnProveInit, warnProveField, warnProveIndex, warnGcUnsafe, warnGcUnsafe2, @@ -280,7 +280,7 @@ const errOptionExpected: "option expected, but found \'$1\'", errXisNoLabel: "\'$1\' is not a label", errNotAllCasesCovered: "not all cases are covered", - errUnkownSubstitionVar: "unknown substitution variable: \'$1\'", + errUnknownSubstitionVar: "unknown substitution variable: \'$1\'", errComplexStmtRequiresInd: "complex statement requires indentation", errXisNotCallable: "\'$1\' is not callable", errNoPragmasAllowedForX: "no pragmas allowed for $1", @@ -312,7 +312,7 @@ const errXOnlyAtModuleScope: "\'$1\' is only allowed at top level", errXNeedsParamObjectType: "'$1' needs a parameter that has an object type", errTemplateInstantiationTooNested: "template/macro instantiation too nested", - errInstantiationFrom: "instantiation from here", + errInstantiationFrom: "template/generic instantiation from here", errInvalidIndexValueForTuple: "invalid index value for tuple subscript", errCommandExpectsFilename: "command expects a filename argument", errMainModuleMustBeSpecified: "please, specify a main module in the project configuration file", @@ -326,12 +326,12 @@ const errXisNoValidIndexFile: "\'$1\' is no valid index file", errCannotRenderX: "cannot render reStructuredText element \'$1\'", errVarVarTypeNotAllowed: "type \'var var\' is not allowed", - errInstantiateXExplicitely: "instantiate '$1' explicitely", + errInstantiateXExplicitly: "instantiate '$1' explicitly", errOnlyACallOpCanBeDelegator: "only a call operator can be a delegator", errUsingNoSymbol: "'$1' is not a variable, constant or a proc name", errMacroBodyDependsOnGenericTypes: "the macro body cannot be compiled, " & "because the parameter '$1' has a generic type", - errDestructorNotGenericEnough: "Destructor signarue is too specific. " & + errDestructorNotGenericEnough: "Destructor signature is too specific. " & "A destructor must be associated will all instantiations of a generic type", errInlineIteratorsAsProcParams: "inline iterators can be used as parameters only for " & "templates, macros and other inline iterators", @@ -360,7 +360,7 @@ const errCannotInferReturnType: "cannot infer the return type of the proc", errGenericLambdaNotAllowed: "A nested proc can have generic parameters only when " & "it is used as an operand to another routine and the types " & - "of the generic paramers can be infered from the expected signature.", + "of the generic paramers can be inferred from the expected signature.", errCompilerDoesntSupportTarget: "The current compiler \'$1\' doesn't support the requested compilation target", errUser: "$1", warnCannotOpenFile: "cannot open \'$1\' [CannotOpenFile]", @@ -377,7 +377,7 @@ const warnFieldXNotSupported: "field \'$1\' not supported [FieldXNotSupported]", warnCommentXIgnored: "comment \'$1\' ignored [CommentXIgnored]", warnNilStatement: "'nil' statement is deprecated; use an empty 'discard' statement instead [NilStmt]", - warnAnalysisLoophole: "thread analysis incomplete due to unknown call '$1' [AnalysisLoophole]", + warnTypelessParam: "'$1' has no type. Typeless parameters are deprecated; only allowed for 'template' [TypelessParam]", warnDifferentHeaps: "possible inconsistency of thread local heaps [DifferentHeaps]", warnWriteToForeignHeap: "write to foreign heap [WriteToForeignHeap]", warnUnsafeCode: "unsafe code: '$1' [UnsafeCode]", @@ -420,7 +420,7 @@ const "RedefinitionOfLabel", "UnknownSubstitutionX", "LanguageXNotSupported", "FieldXNotSupported", "CommentXIgnored", "NilStmt", - "AnalysisLoophole", "DifferentHeaps", "WriteToForeignHeap", + "TypelessParam", "DifferentHeaps", "WriteToForeignHeap", "UnsafeCode", "EachIdentIsTuple", "ShadowIdent", "ProveInit", "ProveField", "ProveIndex", "GcUnsafe", "GcUnsafe2", "Uninit", "GcMem", "Destructor", "LockLevel", "User"] @@ -445,7 +445,7 @@ type TNoteKind* = range[warnMin..hintMax] # "notes" are warnings or hints TNoteKinds* = set[TNoteKind] - TFileInfo*{.final.} = object + TFileInfo* = object fullPath: string # This is a canonical full filesystem path projPath*: string # This is relative to the project's root shortName*: string # short name of the module @@ -460,9 +460,9 @@ type # and parsed; usually 'nil' but is used # for 'nimsuggest' - TLineInfo*{.final.} = object # This is designed to be as small as possible, + TLineInfo* = object # This is designed to be as small as possible, # because it is used - # in syntax nodes. We safe space here by using + # in syntax nodes. We save space here by using # two int16 and an int32. # On 64 bit and on 32 bit systems this is # only 8 bytes. @@ -495,7 +495,7 @@ proc toCChar*(c: char): string = 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 + # this could trigger an internalError(). See the ropes module for # further information. const MaxLineLength = 64 @@ -524,7 +524,7 @@ proc newFileInfo(fullPath, projPath: string): TFileInfo = if optEmbedOrigSrc in gGlobalOptions or true: result.lines = @[] -proc fileInfoIdx*(filename: string): int32 = +proc fileInfoIdx*(filename: string; isKnownFile: var bool): int32 = var canon: string pseudoPath = false @@ -541,11 +541,16 @@ proc fileInfoIdx*(filename: string): int32 = if filenameToIndexTbl.hasKey(canon): result = filenameToIndexTbl[canon] else: + isKnownFile = false result = fileInfos.len.int32 fileInfos.add(newFileInfo(canon, if pseudoPath: filename else: canon.shortenDir)) filenameToIndexTbl[canon] = result +proc fileInfoIdx*(filename: string): int32 = + var dummy: bool + result = fileInfoIdx(filename, dummy) + proc newLineInfo*(fileInfoIdx: int32, line, col: int): TLineInfo = result.fileIndex = fileInfoIdx result.line = int16(line) @@ -693,7 +698,9 @@ proc msgWriteln*(s: string) = #if gCmd == cmdIdeTools and optCDebug notin gGlobalOptions: return - if optStdout in gGlobalOptions: + if not isNil(writelnHook): + writelnHook(s) + elif optStdout in gGlobalOptions: if eStdErr in errorOutputs: writeln(stderr, s) else: if eStdOut in errorOutputs: writeln(stdout, s) @@ -717,7 +724,10 @@ type proc handleError(msg: TMsgKind, eh: TErrorHandling, s: string) = template quit = if defined(debug) or gVerbosity >= 3 or msg == errInternal: - writeStackTrace() + if stackTraceAvailable() and isNil(writelnHook): + writeStackTrace() + else: + msgWriteln("No stack traceback available\nTo create a stacktrace, rerun compilation with ./koch temp " & options.command & " ") quit 1 if msg >= fatalMin and msg <= fatalMax: diff --git a/compiler/nim.nim b/compiler/nim.nim index 617758b2de..b8ba2c6da9 100644 --- a/compiler/nim.nim +++ b/compiler/nim.nim @@ -9,9 +9,9 @@ when defined(gcc) and defined(windows): when defined(x86): - {.link: "icons/nimrod.res".} + {.link: "icons/nim.res".} else: - {.link: "icons/nimrod_icon.o".} + {.link: "icons/nim_icon.o".} import commands, lexer, condsyms, options, msgs, nversion, nimconf, ropes, @@ -61,6 +61,8 @@ proc handleCmdLine() = if gCmd == cmdRun: tccgen.run(commands.arguments) if optRun in gGlobalOptions: + if gProjectName == "-": + gProjectFull = "stdinfile" if gCmd == cmdCompileToJS: var ex: string if options.outFile.len > 0: diff --git a/compiler/nim.nimrod.cfg b/compiler/nim.nim.cfg similarity index 100% rename from compiler/nim.nimrod.cfg rename to compiler/nim.nim.cfg diff --git a/compiler/nimblecmd.nim b/compiler/nimblecmd.nim index 049b94aa96..23f3331a23 100644 --- a/compiler/nimblecmd.nim +++ b/compiler/nimblecmd.nim @@ -33,7 +33,7 @@ proc `<.`(a, b: string): bool = while true: let ii = parseInt(a, verA, i) let jj = parseInt(b, verB, j) - # if A has no number left, but B has, B is prefered: 0.8 vs 0.8.3 + # if A has no number left, but B has, B is preferred: 0.8 vs 0.8.3 if ii <= 0 or jj <= 0: return jj > 0 if verA < verB: return true elif verA > verB: return false diff --git a/compiler/nimconf.nim b/compiler/nimconf.nim index bcf9b53596..711b476e65 100644 --- a/compiler/nimconf.nim +++ b/compiler/nimconf.nim @@ -11,10 +11,10 @@ import llstream, nversion, commands, os, strutils, msgs, platform, condsyms, lexer, - options, idents, wordrecg + options, idents, wordrecg, strtabs # ---------------- configuration file parser ----------------------------- -# we use Nim's scanner here to safe space and work +# we use Nim's scanner here to save space and work proc ppGetTok(L: var TLexer, tok: var TToken) = # simple filter @@ -82,17 +82,17 @@ proc doElif(L: var TLexer, tok: var TToken) = proc jumpToDirective(L: var TLexer, tok: var TToken, dest: TJumpDest) = var nestedIfs = 0 while true: - if (tok.ident != nil) and (tok.ident.s == "@"): + if tok.ident != nil and tok.ident.s == "@": ppGetTok(L, tok) case whichKeyword(tok.ident) of wIf: inc(nestedIfs) of wElse: - if (dest == jdElseEndif) and (nestedIfs == 0): + if dest == jdElseEndif and nestedIfs == 0: doElse(L, tok) break of wElif: - if (dest == jdElseEndif) and (nestedIfs == 0): + if dest == jdElseEndif and nestedIfs == 0: doElif(L, tok) break of wEnd: @@ -119,9 +119,10 @@ proc parseDirective(L: var TLexer, tok: var TToken) = of wElif: doElif(L, tok) of wElse: doElse(L, tok) of wEnd: doEnd(L, tok) - of wWrite: + of wWrite: ppGetTok(L, tok) - msgs.msgWriteln(tokToStr(tok)) + msgs.msgWriteln(strtabs.`%`(tokToStr(tok), options.gConfigVars, + {useEnvironment, useKey})) ppGetTok(L, tok) else: case tok.ident.s.normalize @@ -157,7 +158,7 @@ proc checkSymbol(L: TLexer, tok: TToken) = proc parseAssignment(L: var TLexer, tok: var TToken) = if tok.ident.id == getIdent("-").id or tok.ident.id == getIdent("--").id: confTok(L, tok) # skip unnecessary prefix - var info = getLineInfo(L, tok) # safe for later in case of an error + var info = getLineInfo(L, tok) # save for later in case of an error checkSymbol(L, tok) var s = tokToStr(tok) confTok(L, tok) # skip symbol @@ -178,9 +179,10 @@ proc parseAssignment(L: var TLexer, tok: var TToken) = if tok.tokType == tkBracketRi: confTok(L, tok) else: lexMessage(L, errTokenExpected, "']'") add(val, ']') - if tok.tokType in {tkColon, tkEquals}: + let percent = tok.ident.id == getIdent("%=").id + if tok.tokType in {tkColon, tkEquals} or percent: if len(val) > 0: add(val, ':') - confTok(L, tok) # skip ':' or '=' + confTok(L, tok) # skip ':' or '=' or '%' checkSymbol(L, tok) add(val, tokToStr(tok)) confTok(L, tok) # skip symbol @@ -189,7 +191,11 @@ proc parseAssignment(L: var TLexer, tok: var TToken) = checkSymbol(L, tok) add(val, tokToStr(tok)) confTok(L, tok) - processSwitch(s, val, passPP, info) + if percent: + processSwitch(s, strtabs.`%`(val, options.gConfigVars, + {useEnvironment, useEmpty}), passPP, info) + else: + processSwitch(s, val, passPP, info) proc readConfigFile(filename: string) = var @@ -246,6 +252,11 @@ proc loadConfigs*(cfg: string) = if gProjectName.len != 0: # new project wide config file: - let projectConfig = changeFileExt(gProjectFull, "nim.cfg") - if fileExists(projectConfig): readConfigFile(projectConfig) - else: readConfigFile(changeFileExt(gProjectFull, "nimrod.cfg")) + var projectConfig = changeFileExt(gProjectFull, "nimcfg") + if not fileExists(projectConfig): + projectConfig = changeFileExt(gProjectFull, "nim.cfg") + if not fileExists(projectConfig): + projectConfig = changeFileExt(gProjectFull, "nimrod.cfg") + if fileExists(projectConfig): + rawMessage(warnDeprecated, projectConfig) + readConfigFile(projectConfig) diff --git a/compiler/nimfix/nimfix.nim.cfg b/compiler/nimfix/nimfix.nim.cfg index 31a41e080c..533563a985 100644 --- a/compiler/nimfix/nimfix.nim.cfg +++ b/compiler/nimfix/nimfix.nim.cfg @@ -2,7 +2,7 @@ # gc:markAndSweep hint[XDeclaredButNotUsed]:off -path:"$projectPath/../.." +path:"$projectPath/.." path:"$lib/packages/docutils" path:"$nim/compiler" diff --git a/compiler/nimsuggest/nimsuggest.nim b/compiler/nimsuggest/nimsuggest.nim index 6edea06e59..b45ca475c9 100644 --- a/compiler/nimsuggest/nimsuggest.nim +++ b/compiler/nimsuggest/nimsuggest.nim @@ -87,8 +87,9 @@ proc action(cmd: string) = i += skipWhile(cmd, seps, i) i += parseInt(cmd, col, i) + var isKnownFile = true if orig.len == 0: err() - let dirtyIdx = orig.fileInfoIdx + let dirtyIdx = orig.fileInfoIdx(isKnownFile) if dirtyfile.len != 0: msgs.setDirtyFile(dirtyIdx, dirtyfile) else: msgs.setDirtyFile(dirtyIdx, nil) @@ -99,7 +100,10 @@ proc action(cmd: string) = gTrackPos = newLineInfo(dirtyIdx, line, col) #echo dirtyfile, gDirtyBufferIdx, " project ", gProjectMainIdx gErrorCounter = 0 - compileProject() + if not isKnownFile: + compileProject(dirtyIdx) + else: + compileProject() proc serve() = # do not stop after the first error: @@ -116,13 +120,17 @@ proc serve() = server.bindAddr(gPort, gAddress) var inp = "".TaintedString server.listen() - var stdoutSocket = newSocket() - msgs.writelnHook = proc (line: string) = - stdoutSocket.send(line & "\c\L") + while true: + var stdoutSocket = newSocket() + msgs.writelnHook = proc (line: string) = + stdoutSocket.send(line & "\c\L") + accept(server, stdoutSocket) + stdoutSocket.readLine(inp) action inp.string + stdoutSocket.send("\c\L") stdoutSocket.close() diff --git a/compiler/options.nim b/compiler/options.nim index d6d9389f5e..1b4a624abf 100644 --- a/compiler/options.nim +++ b/compiler/options.nim @@ -283,19 +283,19 @@ when noTimeMachine: var p = startProcess("/usr/bin/tmutil", args = ["addexclusion", dir]) discard p.waitForExit p.close - except E_Base, EOS: + except Exception: discard -proc completeGeneratedFilePath*(f: string, createSubDir: bool = true): string = +proc completeGeneratedFilePath*(f: string, createSubDir: bool = true): string = var (head, tail) = splitPath(f) #if len(head) > 0: head = removeTrailingDirSep(shortenDir(head & dirSep)) var subdir = getGeneratedPath() # / head if createSubDir: - try: + try: createDir(subdir) when noTimeMachine: excludeDirFromTimeMachine(subdir) - except OSError: + except OSError: writeln(stdout, "cannot create directory: " & subdir) quit(1) result = joinPath(subdir, tail) diff --git a/compiler/parser.nim b/compiler/parser.nim index aae0ce7f95..8fbf033d89 100644 --- a/compiler/parser.nim +++ b/compiler/parser.nim @@ -198,8 +198,8 @@ proc isSigilLike(tok: TToken): bool {.inline.} = proc isRightAssociative(tok: TToken): bool {.inline.} = ## Determines whether the token is right assocative. - result = tok.tokType == tkOpr and (tok.ident.s[0] == '^' or - (let L = tok.ident.s.len; L > 1 and tok.ident.s[L-1] == '>')) + result = tok.tokType == tkOpr and tok.ident.s[0] == '^' + # or (let L = tok.ident.s.len; L > 1 and tok.ident.s[L-1] == '>')) proc getPrecedence(tok: TToken, strongSpaces: bool): int = ## Calculates the precedence of the given token. @@ -1597,6 +1597,7 @@ proc parseEnum(p: var TParser): PNode = optInd(p, result) while true: var a = parseSymbol(p) + if a.kind == nkEmpty: return if p.tok.indent >= 0 and p.tok.indent <= p.currInd: add(result, a) break diff --git a/compiler/passes.nim b/compiler/passes.nim index f9c3d75f94..96088bd880 100644 --- a/compiler/passes.nim +++ b/compiler/passes.nim @@ -170,7 +170,11 @@ proc processModule(module: PSym, stream: PLLStream, rd: PRodReader) = openPasses(a, module) if stream == nil: let filename = fileIdx.toFullPathConsiderDirty - s = llStreamOpen(filename, fmRead) + if module.name.s == "-": + module.name.s = "stdinfile" + s = llStreamOpen(stdin) + else: + s = llStreamOpen(filename, fmRead) if s == nil: rawMessage(errCannotOpenFile, filename) return diff --git a/compiler/patterns.nim b/compiler/patterns.nim index 9ac0988c5e..368b0b37be 100644 --- a/compiler/patterns.nim +++ b/compiler/patterns.nim @@ -275,7 +275,7 @@ proc applyRule*(c: PContext, s: PSym, n: PNode): PNode = if arg != rs and aliases.isPartOf(rs, arg) == arYes: ok = true break - # constraint not fullfilled: + # constraint not fulfilled: if not ok: return nil of aqNoAlias: # it MUST not alias with any other param: @@ -284,7 +284,7 @@ proc applyRule*(c: PContext, s: PSym, n: PNode): PNode = if arg != rs and aliases.isPartOf(rs, arg) != arNo: ok = false break - # constraint not fullfilled: + # constraint not fulfilled: if not ok: return nil markUsed(n.info, s) diff --git a/compiler/platform.nim b/compiler/platform.nim index 8360a9dcc1..a21e732482 100644 --- a/compiler/platform.nim +++ b/compiler/platform.nim @@ -138,7 +138,7 @@ const props: {ospNeedsPIC, ospPosix, ospLacksThreadVars}), (name: "VxWorks", parDir: "..", dllFrmt: "lib$1.so", altDirSep: "/", objExt: ".o", newLine: "\x0A", pathSep: ";", dirSep: "\\", - scriptExt: ".sh", curDir: ".", exeExt: "", extSep: ".", + scriptExt: ".sh", curDir: ".", exeExt: ".vxe", extSep: ".", props: {ospNeedsPIC, ospPosix, ospLacksThreadVars}), (name: "JS", parDir: "..", dllFrmt: "lib$1.so", altDirSep: "/", diff --git a/compiler/pragmas.nim b/compiler/pragmas.nim index 90f87696bb..78ee490e28 100644 --- a/compiler/pragmas.nim +++ b/compiler/pragmas.nim @@ -735,11 +735,10 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: int, incl(sym.flags, sfProcvar) if sym.typ != nil: incl(sym.typ.flags, tfThread) of wGcSafe: - if optThreadAnalysis in gGlobalOptions: - noVal(it) - if sym.kind != skType: incl(sym.flags, sfThread) - if sym.typ != nil: incl(sym.typ.flags, tfGcSafe) - else: invalidPragma(it) + noVal(it) + if sym.kind != skType: incl(sym.flags, sfThread) + if sym.typ != nil: incl(sym.typ.flags, tfGcSafe) + else: invalidPragma(it) of wPacked: noVal(it) if sym.typ == nil: invalidPragma(it) diff --git a/compiler/rodread.nim b/compiler/rodread.nim index 3b3538e5d1..545a8dda93 100644 --- a/compiler/rodread.nim +++ b/compiler/rodread.nim @@ -666,7 +666,7 @@ proc newRodReader(modfilename: string, crc: TCrc32, r.readerIndex = readerIndex r.filename = modfilename initIdTable(r.syms) - # we terminate the file explicitely with ``\0``, so the cast to `cstring` + # we terminate the file explicitly with ``\0``, so the cast to `cstring` # is safe: r.s = cast[cstring](r.memfile.mem) if startsWith(r.s, "NIM:"): diff --git a/compiler/rodwrite.nim b/compiler/rodwrite.nim index 9fed7ac52e..0f211b4baf 100644 --- a/compiler/rodwrite.nim +++ b/compiler/rodwrite.nim @@ -200,7 +200,7 @@ proc encodeType(w: PRodWriter, t: PType, result: var string) = return # we need no surrounding [] here because the type is in a line of its own if t.kind == tyForward: internalError("encodeType: tyForward") - # for the new rodfile viewer we use a preceeding [ so that the data section + # for the new rodfile viewer we use a preceding [ so that the data section # can easily be disambiguated: add(result, '[') encodeVInt(ord(t.kind), result) diff --git a/compiler/ropes.nim b/compiler/ropes.nim index b140816943..ad6801d18c 100644 --- a/compiler/ropes.nim +++ b/compiler/ropes.nim @@ -52,7 +52,7 @@ # Note that the left and right pointers are not needed for leaves. # Leaves have relatively high memory overhead (~30 bytes on a 32 # bit machines) and we produce many of them. This is why we cache and -# share leaves accross different rope trees. +# share leaves across different rope trees. # To cache them they are inserted in a `cache` array. import diff --git a/compiler/sem.nim b/compiler/sem.nim index 214f471d66..a909482458 100644 --- a/compiler/sem.nim +++ b/compiler/sem.nim @@ -95,15 +95,6 @@ proc inferWithMetatype(c: PContext, formal: PType, var commonTypeBegin = PType(kind: tyExpr) -proc isEmptyContainer(t: PType): bool = - case t.kind - of tyExpr, tyNil: result = true - of tyArray, tyArrayConstr: result = t.sons[1].kind == tyEmpty - of tySet, tySequence, tyOpenArray, tyVarargs: - result = t.sons[0].kind == tyEmpty - of tyGenericInst: result = isEmptyContainer(t.lastSon) - else: result = false - proc commonType*(x, y: PType): PType = # new type relation that is used for array constructors, # if expressions, etc.: @@ -130,9 +121,11 @@ proc commonType*(x, y: PType): PType = elif a.kind == tyTuple and b.kind == tyTuple and a.len == b.len: var nt: PType for i in 0.. 0 and n.sons[0].kind == nkExprColonExpr: - for i in countup(0, sonsLen(n) - 1): + elif sonsLen(n) > 0 and n.sons[0].kind == nkExprColonExpr: + # named tuple? + for i in countup(0, sonsLen(n) - 1): var m = n.sons[i].sons[0] - if m.kind != nkSym: + if m.kind != nkSym: internalError(m.info, "changeType(): invalid tuple constr") return - var f = getSymFromList(newType.n, m.sym.name) - if f == nil: - internalError(m.info, "changeType(): invalid identifier") - return - changeType(n.sons[i].sons[1], f.typ, check) + if tup.n != nil: + var f = getSymFromList(newType.n, m.sym.name) + if f == nil: + internalError(m.info, "changeType(): invalid identifier") + return + changeType(n.sons[i].sons[1], f.typ, check) + else: + changeType(n.sons[i].sons[1], tup.sons[i], check) else: for i in countup(0, sonsLen(n) - 1): - var m = n.sons[i] - var a = newNodeIT(nkExprColonExpr, m.info, newType.sons[i]) - addSon(a, newSymNode(newType.n.sons[i].sym)) - addSon(a, m) - changeType(m, tup.sons[i], check) + changeType(n.sons[i], tup.sons[i], check) + when false: + var m = n.sons[i] + var a = newNodeIT(nkExprColonExpr, m.info, newType.sons[i]) + addSon(a, newSymNode(newType.n.sons[i].sym)) + addSon(a, m) + changeType(m, tup.sons[i], check) of nkCharLit..nkUInt64Lit: if check: let value = n.intVal @@ -578,11 +586,12 @@ proc skipObjConv(n: PNode): PNode = proc isAssignable(c: PContext, n: PNode): TAssignableResult = result = parampatterns.isAssignable(c.p.owner, n) -proc newHiddenAddrTaken(c: PContext, n: PNode): PNode = - if n.kind == nkHiddenDeref: +proc newHiddenAddrTaken(c: PContext, n: PNode): PNode = + if n.kind == nkHiddenDeref and not (gCmd == cmdCompileToCpp or + sfCompileToCpp in c.module.flags): checkSonsLen(n, 1) result = n.sons[0] - else: + else: result = newNodeIT(nkHiddenAddr, n.info, makeVarType(c, n.typ)) addSon(result, n) if isAssignable(c, n) notin {arLValue, arLocalLValue}: @@ -677,7 +686,9 @@ proc evalAtCompileTime(c: PContext, n: PNode): PNode = # implicit statics. if n.len > 1: for i in 1 .. 'result' n.sons[1] = takeImplicitAddr(c, ri) + x.typ.flags.incl tfVarIsPtr template resultTypeIsInferrable(typ: PType): expr = typ.isMetaType and typ.kind != tyTypeDesc @@ -1327,7 +1342,12 @@ proc semProcBody(c: PContext, n: PNode): PNode = if c.p.owner.kind notin {skMacro, skTemplate} and c.p.resultSym != nil and c.p.resultSym.typ.isMetaType: - localError(c.p.resultSym.info, errCannotInferReturnType) + if isEmptyType(result.typ): + # we inferred a 'void' return type: + c.p.resultSym.typ = nil + c.p.owner.typ.sons[0] = nil + else: + localError(c.p.resultSym.info, errCannotInferReturnType) closeScope(c) @@ -1492,7 +1512,9 @@ proc semExpandToAst(c: PContext, n: PNode): PNode = # Preserve the magic symbol in order to be handled in evals.nim internalAssert n.sons[0].sym.magic == mExpandToAst - n.typ = getSysSym("PNimrodNode").typ # expandedSym.getReturnType + #n.typ = getSysSym("PNimrodNode").typ # expandedSym.getReturnType + n.typ = if getCompilerProc("NimNode") != nil: sysTypeFromName"NimNode" + else: sysTypeFromName"PNimrodNode" result = n proc semExpandToAst(c: PContext, n: PNode, magicSym: PSym, @@ -1620,7 +1642,7 @@ proc semShallowCopy(c: PContext, n: PNode, flags: TExprFlags): PNode = result = semDirectOp(c, n, flags) proc createFlowVar(c: PContext; t: PType; info: TLineInfo): PType = - result = newType(tyGenericInvokation, c.module) + result = newType(tyGenericInvocation, c.module) addSonSkipIntLit(result, magicsys.getCompilerProc("FlowVar").typ) addSonSkipIntLit(result, t) result = instGenericContainer(c, info, result, allowMetaTypes = false) @@ -1900,7 +1922,7 @@ proc semObjConstr(c: PContext, n: PNode, flags: TExprFlags): PNode = it.sons[0] = newSymNode(f) e = fitNode(c, f.typ, e) # small hack here in a nkObjConstr the ``nkExprColonExpr`` node can have - # 3 childen the last being the field check + # 3 children the last being the field check if check != nil: check.sons[0] = it.sons[0] it.add(check) @@ -1984,7 +2006,7 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode = if result.kind == nkSym: markIndirect(c, result.sym) # if isGenericRoutine(result.sym): - # localError(n.info, errInstantiateXExplicitely, s.name.s) + # localError(n.info, errInstantiateXExplicitly, s.name.s) of nkSym: # because of the changed symbol binding, this does not mean that we # don't have to check the symbol for semantics here again! @@ -2113,7 +2135,8 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode = of nkCurly: result = semSetConstr(c, n) of nkBracket: result = semArrayConstr(c, n, flags) of nkObjConstr: result = semObjConstr(c, n, flags) - of nkLambdaKinds: result = semLambda(c, n, flags) + of nkLambda: result = semLambda(c, n, flags) + of nkDo: result = semDo(c, n, flags) of nkDerefExpr: result = semDeref(c, n) of nkAddr: result = n diff --git a/compiler/semfields.nim b/compiler/semfields.nim index 823bef2258..e086e73f81 100644 --- a/compiler/semfields.nim +++ b/compiler/semfields.nim @@ -19,12 +19,13 @@ type proc instFieldLoopBody(c: TFieldInstCtx, n: PNode, forLoop: PNode): PNode = case n.kind - of nkEmpty..pred(nkIdent), succ(nkIdent)..nkNilLit: result = n - of nkIdent: + of nkEmpty..pred(nkIdent), succ(nkSym)..nkNilLit: result = n + of nkIdent, nkSym: result = n + let ident = considerQuotedIdent(n) var L = sonsLen(forLoop) if c.replaceByFieldName: - if n.ident.id == forLoop[0].ident.id: + if ident.id == considerQuotedIdent(forLoop[0]).id: let fieldName = if c.tupleType.isNil: c.field.name.s elif c.tupleType.n.isNil: "Field" & $c.tupleIndex else: c.tupleType.n.sons[c.tupleIndex].sym.name.s @@ -32,7 +33,7 @@ proc instFieldLoopBody(c: TFieldInstCtx, n: PNode, forLoop: PNode): PNode = return # other fields: for i in ord(c.replaceByFieldName)..L-3: - if n.ident.id == forLoop[i].ident.id: + if ident.id == considerQuotedIdent(forLoop[i]).id: var call = forLoop.sons[L-2] var tupl = call.sons[i+1-ord(c.replaceByFieldName)] if c.field.isNil: @@ -155,7 +156,7 @@ proc semForFields(c: PContext, n: PNode, m: TMagic): PNode = dec(c.p.nestedLoopCounter) # for TR macros this 'while true: ...; break' loop is pretty bad, so # we avoid it now if we can: - if hasSonWith(stmts, nkBreakStmt): + if containsNode(stmts, {nkBreakStmt}): var b = newNodeI(nkBreakStmt, n.info) b.add(ast.emptyNode) stmts.add(b) diff --git a/compiler/semfold.nim b/compiler/semfold.nim index 1988c512e6..a3f1b1c130 100644 --- a/compiler/semfold.nim +++ b/compiler/semfold.nim @@ -665,8 +665,8 @@ proc getConstExpr(m: PSym, n: PNode): PNode = of mLow: result = newIntNodeT(firstOrd(n.sons[1].typ), n) of mHigh: - if skipTypes(n.sons[1].typ, abstractVar).kind notin - {tyOpenArray, tyVarargs, tySequence, tyString}: + if skipTypes(n.sons[1].typ, abstractVar).kind notin + {tySequence, tyString, tyCString, tyOpenArray, tyVarargs}: result = newIntNodeT(lastOrd(skipTypes(n[1].typ, abstractVar)), n) else: var a = getArrayConstr(m, n.sons[1]) diff --git a/compiler/semgnrc.nim b/compiler/semgnrc.nim index 13941fa585..2601f05acf 100644 --- a/compiler/semgnrc.nim +++ b/compiler/semgnrc.nim @@ -243,7 +243,7 @@ proc semGenericStmt(c: PContext, n: PNode, elif fn.kind == nkDotExpr: result.sons[0] = fuzzyLookup(c, fn, flags, ctx, mixinContext) first = 1 - # Consider 'when defined(globalsSlot): ThreadVarSetValue(globalsSlot, ...)' + # Consider 'when declared(globalsSlot): ThreadVarSetValue(globalsSlot, ...)' # in threads.nim: the subtle preprocessing here binds 'globalsSlot' which # is not exported and yet the generic 'threadProcWrapper' works correctly. let flags = if mixinContext: flags+{withinMixin} else: flags diff --git a/compiler/seminst.nim b/compiler/seminst.nim index 81a4465c59..a10f275193 100644 --- a/compiler/seminst.nim +++ b/compiler/seminst.nim @@ -36,7 +36,7 @@ proc instantiateGenericParamList(c: PContext, n: PNode, pt: TIdTable, elif t.kind == tyGenericParam: localError(a.info, errCannotInstantiateX, q.name.s) t = errorType(c) - elif t.kind == tyGenericInvokation: + elif t.kind == tyGenericInvocation: #t = instGenericContainer(c, a, t) t = generateTypeInstance(c, pt, a, t) #t = ReplaceTypeVarsT(cl, t) @@ -77,11 +77,12 @@ proc removeDefaultParamValues(n: PNode) = proc freshGenSyms(n: PNode, owner: PSym, symMap: var TIdTable) = # we need to create a fresh set of gensym'ed symbols: if n.kind == nkSym and sfGenSym in n.sym.flags: - var x = PSym(idTableGet(symMap, n.sym)) + let s = n.sym + var x = PSym(idTableGet(symMap, s)) if x == nil: - x = copySym(n.sym, false) + x = copySym(s, false) x.owner = owner - idTablePut(symMap, n.sym, x) + idTablePut(symMap, s, x) n.sym = x else: for i in 0 .. 1: resetIdTable(cl.symMap) result.sons[i] = replaceTypeVarsT(cl, result.sons[i]) propagateToOwner(result, result.sons[i]) - let param = replaceTypeVarsN(cl, originalParams[i]) - result.n.sons[i] = param - if param.kind == nkSym: - # XXX: this won't be true for void params - # implement pass-through of void params and - # the "sort by distance to point" container + internalAssert originalParams[i].kind == nkSym + when true: + let oldParam = originalParams[i].sym + let param = copySym(oldParam) + param.owner = prc + param.typ = result.sons[i] + param.ast = oldParam.ast.copyTree + # don't be lazy here and call replaceTypeVarsN(cl, originalParams[i])! + result.n.sons[i] = newSymNode(param) + addDecl(c, param) + else: + let param = replaceTypeVarsN(cl, originalParams[i]) + result.n.sons[i] = param param.sym.owner = prc - addDecl(c, param.sym) - + addDecl(c, result.n.sons[i].sym) + + resetIdTable(cl.symMap) result.sons[0] = replaceTypeVarsT(cl, result.sons[0]) result.n.sons[0] = originalParams[0].copyTree @@ -234,7 +250,7 @@ proc generateInstance(c: PContext, fn: PSym, pt: TIdTable, pragma(c, result, n.sons[pragmasPos], allRoutinePragmas) if isNil(n.sons[bodyPos]): n.sons[bodyPos] = copyTree(fn.getBody) - instantiateBody(c, n, result) + instantiateBody(c, n, fn.typ.n, result) sideEffectsCheck(c, result) paramsTypeCheck(c, result.typ) else: diff --git a/compiler/sempass2.nim b/compiler/sempass2.nim index dcebf86acc..60153e0521 100644 --- a/compiler/sempass2.nim +++ b/compiler/sempass2.nim @@ -23,7 +23,7 @@ import # Predefined effects: # io, time (time dependent), gc (performs GC'ed allocation), exceptions, # side effect (accesses global), store (stores into *type*), -# store_unkown (performs some store) --> store(any)|store(x) +# store_unknown (performs some store) --> store(any)|store(x) # load (loads from *type*), recursive (recursive call), unsafe, # endless (has endless loops), --> user effects are defined over *patterns* # --> a TR macro can annotate the proc with user defined annotations @@ -194,6 +194,9 @@ proc warnAboutGcUnsafe(n: PNode) = #assert false message(n.info, warnGcUnsafe, renderTree(n)) +template markGcUnsafe(a: PEffects) = + a.gcUnsafe = true + proc useVar(a: PEffects, n: PNode) = let s = n.sym if isLocalVar(a, s): @@ -209,7 +212,7 @@ proc useVar(a: PEffects, n: PNode) = if (tfHasGCedMem in s.typ.flags or s.typ.isGCedMem) and tfGcSafe notin s.typ.flags: if warnGcUnsafe in gNotes: warnAboutGcUnsafe(n) - a.gcUnsafe = true + markGcUnsafe(a) type TIntersection = seq[tuple[id, count: int]] # a simple count table @@ -230,7 +233,7 @@ proc getEbase(): PType = proc excType(n: PNode): PType = # reraise is like raising E_Base: - let t = if n.kind == nkEmpty: getEbase() else: n.typ + let t = if n.kind == nkEmpty or n.typ.isNil: getEbase() else: n.typ result = skipTypes(t, skipPtrs) proc createRaise(n: PNode): PNode = @@ -448,7 +451,7 @@ proc propagateEffects(tracked: PEffects, n: PNode, s: PSym) = if notGcSafe(s.typ) and sfImportc notin s.flags: if warnGcUnsafe in gNotes: warnAboutGcUnsafe(n) - tracked.gcUnsafe = true + markGcUnsafe(tracked) mergeLockLevels(tracked, n, s.getLockLevel) proc notNilCheck(tracked: PEffects, n: PNode, paramType: PType) = @@ -502,13 +505,13 @@ proc trackOperand(tracked: PEffects, n: PNode, paramType: PType) = # assume GcUnsafe unless in its type; 'forward' does not matter: if notGcSafe(op) and not isOwnedProcVar(a, tracked.owner): if warnGcUnsafe in gNotes: warnAboutGcUnsafe(n) - tracked.gcUnsafe = true + markGcUnsafe(tracked) else: mergeEffects(tracked, effectList.sons[exceptionEffects], n) mergeTags(tracked, effectList.sons[tagEffects], n) if notGcSafe(op): if warnGcUnsafe in gNotes: warnAboutGcUnsafe(n) - tracked.gcUnsafe = true + markGcUnsafe(tracked) notNilCheck(tracked, n, paramType) proc breaksBlock(n: PNode): bool = @@ -656,7 +659,7 @@ proc track(tracked: PEffects, n: PNode) = # and it's not a recursive call: if not (a.kind == nkSym and a.sym == tracked.owner): warnAboutGcUnsafe(n) - tracked.gcUnsafe = true + markGcUnsafe(tracked) for i in 1 .. s.typ.lockLevel: - localError(s.info, - "declared lock level is $1, but real lock level is $2" % - [$s.typ.lockLevel, $t.maxLockLevel]) + if sfThread in s.flags and t.gcUnsafe: + if optThreads in gGlobalOptions and optThreadAnalysis in gGlobalOptions: + localError(s.info, "'$1' is not GC-safe" % s.name.s) + else: + localError(s.info, warnGcUnsafe2, s.name.s) + if not t.gcUnsafe: + s.typ.flags.incl tfGcSafe + if s.typ.lockLevel == UnspecifiedLockLevel: + s.typ.lockLevel = t.maxLockLevel + elif t.maxLockLevel > s.typ.lockLevel: + #localError(s.info, + message(s.info, warnLockLevel, + "declared lock level is $1, but real lock level is $2" % + [$s.typ.lockLevel, $t.maxLockLevel]) proc trackTopLevelStmt*(module: PSym; n: PNode) = if n.kind in {nkPragma, nkMacroDef, nkTemplateDef, nkProcDef, diff --git a/compiler/semstmts.nim b/compiler/semstmts.nim index e50a80bcfb..4a2e107d7f 100644 --- a/compiler/semstmts.nim +++ b/compiler/semstmts.nim @@ -359,13 +359,17 @@ proc semVarOrLet(c: PContext, n: PNode, symkind: TSymKind): PNode = var def: PNode if a.sons[length-1].kind != nkEmpty: def = semExprWithType(c, a.sons[length-1], {efAllowDestructor}) + if def.typ.kind == tyTypeDesc and c.p.owner.kind != skMacro: + # prevent the all too common 'var x = int' bug: + localError(def.info, "'typedesc' metatype is not valid here; typed '=' instead of ':'?") + def.typ = errorType(c) if typ != nil: if typ.isMetaType: def = inferWithMetatype(c, typ, def) typ = def.typ else: # BUGFIX: ``fitNode`` is needed here! - # check type compability between def.typ and typ + # check type compatibility between def.typ and typ def = fitNode(c, typ, def) #changeType(def.skipConv, typ, check=true) else: @@ -666,8 +670,8 @@ proc checkForMetaFields(n: PNode) = let t = n.sym.typ case t.kind of tySequence, tySet, tyArray, tyOpenArray, tyVar, tyPtr, tyRef, - tyProc, tyGenericInvokation, tyGenericInst: - let start = ord(t.kind in {tyGenericInvokation, tyGenericInst}) + tyProc, tyGenericInvocation, tyGenericInst: + let start = ord(t.kind in {tyGenericInvocation, tyGenericInst}) for i in start .. 0: @@ -640,7 +640,7 @@ proc semPatternBody(c: var TemplCtx, n: PNode): PNode = for i in countup(0, sonsLen(n) - 1): result.sons[i] = semPatternBody(c, n.sons[i]) else: - # dotExpr is ambiguous: note that we explicitely allow 'x.TemplateParam', + # dotExpr is ambiguous: note that we explicitly allow 'x.TemplateParam', # so we use the generic code for nkDotExpr too case n.kind of nkDotExpr, nkAccQuoted: diff --git a/compiler/semtypes.nim b/compiler/semtypes.nim index 8d16555932..0735b76cec 100644 --- a/compiler/semtypes.nim +++ b/compiler/semtypes.nim @@ -10,14 +10,14 @@ # this module does the semantic checking of type declarations # included from sem.nim -proc newOrPrevType(kind: TTypeKind, prev: PType, c: PContext): PType = - if prev == nil: +proc newOrPrevType(kind: TTypeKind, prev: PType, c: PContext): PType = + if prev == nil: result = newTypeS(kind, c) - else: + else: result = prev if result.kind == tyForward: result.kind = kind -proc newConstraint(c: PContext, k: TTypeKind): PType = +proc newConstraint(c: PContext, k: TTypeKind): PType = result = newTypeS(tyBuiltInTypeClass, c) result.addSonSkipIntLit(newTypeS(k, c)) @@ -32,22 +32,22 @@ proc semEnum(c: PContext, n: PNode, prev: PType): PType = result = newOrPrevType(tyEnum, prev, c) result.n = newNodeI(nkEnumTy, n.info) checkMinSonsLen(n, 1) - if n.sons[0].kind != nkEmpty: + if n.sons[0].kind != nkEmpty: base = semTypeNode(c, n.sons[0].sons[0], nil) - if base.kind != tyEnum: + if base.kind != tyEnum: localError(n.sons[0].info, errInheritanceOnlyWithEnums) counter = lastOrd(base) + 1 rawAddSon(result, base) let isPure = result.sym != nil and sfPure in result.sym.flags var hasNull = false - for i in countup(1, sonsLen(n) - 1): + for i in countup(1, sonsLen(n) - 1): case n.sons[i].kind - of nkEnumFieldDef: + of nkEnumFieldDef: e = newSymS(skEnumField, n.sons[i].sons[0], c) var v = semConstExpr(c, n.sons[i].sons[1]) var strVal: PNode = nil - case skipTypes(v.typ, abstractInst-{tyTypeDesc}).kind - of tyTuple: + case skipTypes(v.typ, abstractInst-{tyTypeDesc}).kind + of tyTuple: if sonsLen(v) == 2: strVal = v.sons[1] # second tuple part is the string value if skipTypes(strVal.typ, abstractInst).kind in {tyString, tyCString}: @@ -63,14 +63,14 @@ proc semEnum(c: PContext, n: PNode, prev: PType): PType = x = getOrdValue(v) if i != 1: if x != counter: incl(result.flags, tfEnumHasHoles) - if x < counter: + if x < counter: localError(n.sons[i].info, errInvalidOrderInEnumX, e.name.s) x = counter e.ast = strVal # might be nil counter = x - of nkSym: + of nkSym: e = n.sons[i].sym - of nkIdent, nkAccQuoted: + of nkIdent, nkAccQuoted: e = newSymS(skEnumField, n.sons[i], c) else: illFormedAst(n[i]) @@ -87,28 +87,28 @@ proc semEnum(c: PContext, n: PNode, prev: PType): PType = inc(counter) if not hasNull: incl(result.flags, tfNeedsInit) -proc semSet(c: PContext, n: PNode, prev: PType): PType = +proc semSet(c: PContext, n: PNode, prev: PType): PType = result = newOrPrevType(tySet, prev, c) - if sonsLen(n) == 2: + if sonsLen(n) == 2: var base = semTypeNode(c, n.sons[1], nil) addSonSkipIntLit(result, base) if base.kind == tyGenericInst: base = lastSon(base) if base.kind != tyGenericParam: - if not isOrdinalType(base): + if not isOrdinalType(base): localError(n.info, errOrdinalTypeExpected) - elif lengthOrd(base) > MaxSetElements: + elif lengthOrd(base) > MaxSetElements: localError(n.info, errSetTooBig) else: localError(n.info, errXExpectsOneTypeParam, "set") addSonSkipIntLit(result, errorType(c)) - -proc semContainer(c: PContext, n: PNode, kind: TTypeKind, kindStr: string, - prev: PType): PType = + +proc semContainer(c: PContext, n: PNode, kind: TTypeKind, kindStr: string, + prev: PType): PType = result = newOrPrevType(kind, prev, c) - if sonsLen(n) == 2: + if sonsLen(n) == 2: var base = semTypeNode(c, n.sons[1], nil) addSonSkipIntLit(result, base) - else: + else: localError(n.info, errXExpectsOneTypeParam, kindStr) addSonSkipIntLit(result, errorType(c)) @@ -140,23 +140,23 @@ proc semAnyRef(c: PContext; n: PNode; kind: TTypeKind; prev: PType): PType = var base = semTypeNode(c, n.lastSon, nil) addSonSkipIntLit(result, base) -proc semVarType(c: PContext, n: PNode, prev: PType): PType = - if sonsLen(n) == 1: +proc semVarType(c: PContext, n: PNode, prev: PType): PType = + if sonsLen(n) == 1: result = newOrPrevType(tyVar, prev, c) var base = semTypeNode(c, n.sons[0], nil) - if base.kind == tyVar: + if base.kind == tyVar: localError(n.info, errVarVarTypeNotAllowed) base = base.sons[0] addSonSkipIntLit(result, base) else: result = newConstraint(c, tyVar) -proc semDistinct(c: PContext, n: PNode, prev: PType): PType = +proc semDistinct(c: PContext, n: PNode, prev: PType): PType = if n.len == 0: return newConstraint(c, tyDistinct) result = newOrPrevType(tyDistinct, prev, c) addSonSkipIntLit(result, semTypeNode(c, n.sons[0], nil)) if n.len > 1: result.n = n[1] - + proc semRangeAux(c: PContext, n: PNode, prev: PType): PType = assert isRange(n) checkSonsLen(n, 3) @@ -164,11 +164,11 @@ proc semRangeAux(c: PContext, n: PNode, prev: PType): PType = result.n = newNodeI(nkRange, n.info) if (n[1].kind == nkEmpty) or (n[2].kind == nkEmpty): localError(n.info, errRangeIsEmpty) - + var range: array[2, PNode] range[0] = semExprWithType(c, n[1], {efDetermineType}) range[1] = semExprWithType(c, n[2], {efDetermineType}) - + var rangeT: array[2, PType] for i in 0..1: rangeT[i] = range[i].typ.skipTypes({tyStatic}).skipIntLit @@ -179,13 +179,13 @@ proc semRangeAux(c: PContext, n: PNode, prev: PType): PType = localError(n.info, errOrdinalTypeExpected) elif enumHasHoles(rangeT[0]): localError(n.info, errEnumXHasHoles, rangeT[0].sym.name.s) - + for i in 0..1: if hasGenericArguments(range[i]): result.n.addSon makeStaticExpr(c, range[i]) else: result.n.addSon semConstExpr(c, range[i]) - + if weakLeValue(result.n[0], result.n[1]) == impNo: localError(n.info, errRangeIsEmpty) @@ -201,10 +201,10 @@ proc semRange(c: PContext, n: PNode, prev: PType): PType = incl(result.flags, tfNeedsInit) elif n.sons[1].kind in {nkCharLit..nkUInt64Lit} and n.sons[1].intVal < 0: incl(result.flags, tfNeedsInit) - elif n.sons[0].kind in {nkFloatLit..nkFloat64Lit} and + elif n.sons[0].kind in {nkFloatLit..nkFloat64Lit} and n.sons[0].floatVal > 0.0: incl(result.flags, tfNeedsInit) - elif n.sons[1].kind in {nkFloatLit..nkFloat64Lit} and + elif n.sons[1].kind in {nkFloatLit..nkFloat64Lit} and n.sons[1].floatVal < 0.0: incl(result.flags, tfNeedsInit) else: @@ -243,13 +243,13 @@ proc semArrayIndex(c: PContext, n: PNode): PType = else: let x = semConstExpr(c, e) if x.kind in {nkIntLit..nkUInt64Lit}: - result = makeRangeType(c, 0, x.intVal-1, n.info, + result = makeRangeType(c, 0, x.intVal-1, n.info, x.typ.skipTypes({tyTypeDesc})) else: result = x.typ.skipTypes({tyTypeDesc}) #localError(n[1].info, errConstExprExpected) -proc semArray(c: PContext, n: PNode, prev: PType): PType = +proc semArray(c: PContext, n: PNode, prev: PType): PType = var base: PType result = newOrPrevType(tyArray, prev, c) if sonsLen(n) == 3: @@ -260,20 +260,20 @@ proc semArray(c: PContext, n: PNode, prev: PType): PType = if indx.kind notin {tyGenericParam, tyStatic, tyFromExpr}: if not isOrdinalType(indx): localError(n.sons[1].info, errOrdinalTypeExpected) - elif enumHasHoles(indx): + elif enumHasHoles(indx): localError(n.sons[1].info, errEnumXHasHoles, indx.sym.name.s) base = semTypeNode(c, n.sons[2], nil) addSonSkipIntLit(result, base) - else: + else: localError(n.info, errArrayExpectsTwoTypeParams) result = newOrPrevType(tyError, prev, c) - -proc semOrdinal(c: PContext, n: PNode, prev: PType): PType = + +proc semOrdinal(c: PContext, n: PNode, prev: PType): PType = result = newOrPrevType(tyOrdinal, prev, c) - if sonsLen(n) == 2: + if sonsLen(n) == 2: var base = semTypeNode(c, n.sons[1], nil) - if base.kind != tyGenericParam: - if not isOrdinalType(base): + if base.kind != tyGenericParam: + if not isOrdinalType(base): localError(n.sons[1].info, errOrdinalTypeExpected) addSonSkipIntLit(result, base) else: @@ -281,7 +281,7 @@ proc semOrdinal(c: PContext, n: PNode, prev: PType): PType = result = newOrPrevType(tyError, prev, c) proc semTypeIdent(c: PContext, n: PNode): PSym = - if n.kind == nkSym: + if n.kind == nkSym: result = n.sym else: when defined(nimfix): @@ -307,7 +307,7 @@ proc semTypeIdent(c: PContext, n: PNode): PSym = result = result.typ.sym.copySym result.typ = copyType(result.typ, result.typ.owner, true) result.typ.flags.incl tfUnresolved - + if result.kind == skGenericParam: if result.typ.kind == tyGenericParam and result.typ.len == 0 and tfWildcard in result.typ.flags: @@ -319,7 +319,7 @@ proc semTypeIdent(c: PContext, n: PNode): PSym = localError(n.info, errTypeExpected) return errorSym(c, n) - if result.kind != skType: + if result.kind != skType: # this implements the wanted ``var v: V, x: V`` feature ... var ov: TOverloadIter var amb = initOverloadIter(ov, c, n) @@ -332,55 +332,60 @@ proc semTypeIdent(c: PContext, n: PNode): PSym = if result.typ.kind != tyGenericParam: # XXX get rid of this hack! var oldInfo = n.info + when defined(useNodeIds): + let oldId = n.id reset(n[]) + when defined(useNodeIds): + n.id = oldId n.kind = nkSym n.sym = result n.info = oldInfo + n.typ = result.typ else: localError(n.info, errIdentifierExpected) result = errorSym(c, n) - -proc semTuple(c: PContext, n: PNode, prev: PType): PType = + +proc semTuple(c: PContext, n: PNode, prev: PType): PType = if n.sonsLen == 0: return newConstraint(c, tyTuple) var typ: PType result = newOrPrevType(tyTuple, prev, c) result.n = newNodeI(nkRecList, n.info) var check = initIntSet() var counter = 0 - for i in countup(0, sonsLen(n) - 1): + for i in countup(0, sonsLen(n) - 1): var a = n.sons[i] if (a.kind != nkIdentDefs): illFormedAst(a) checkMinSonsLen(a, 3) var length = sonsLen(a) - if a.sons[length - 2].kind != nkEmpty: + if a.sons[length - 2].kind != nkEmpty: typ = semTypeNode(c, a.sons[length - 2], nil) else: localError(a.info, errTypeExpected) typ = errorType(c) - if a.sons[length - 1].kind != nkEmpty: + if a.sons[length - 1].kind != nkEmpty: localError(a.sons[length - 1].info, errInitHereNotAllowed) - for j in countup(0, length - 3): + for j in countup(0, length - 3): var field = newSymG(skField, a.sons[j], c) field.typ = typ field.position = counter inc(counter) - if containsOrIncl(check, field.name.id): + if containsOrIncl(check, field.name.id): localError(a.sons[j].info, errAttemptToRedefine, field.name.s) else: addSon(result.n, newSymNode(field)) addSonSkipIntLit(result, typ) if gCmd == cmdPretty: styleCheckDef(a.sons[j].info, field) -proc semIdentVis(c: PContext, kind: TSymKind, n: PNode, - allowed: TSymFlags): PSym = +proc semIdentVis(c: PContext, kind: TSymKind, n: PNode, + allowed: TSymFlags): PSym = # identifier with visibility - if n.kind == nkPostfix: - if sonsLen(n) == 2 and n.sons[0].kind == nkIdent: + if n.kind == nkPostfix: + if sonsLen(n) == 2 and n.sons[0].kind == nkIdent: # for gensym'ed identifiers the identifier may already have been # transformed to a symbol and we need to use that here: result = newSymG(kind, n.sons[1], c) var v = n.sons[0].ident - if sfExported in allowed and v.id == ord(wStar): + if sfExported in allowed and v.id == ord(wStar): incl(result.flags, sfExported) else: localError(n.sons[0].info, errInvalidVisibilityX, v.s) @@ -388,7 +393,7 @@ proc semIdentVis(c: PContext, kind: TSymKind, n: PNode, illFormedAst(n) else: result = newSymG(kind, n, c) - + proc semIdentWithPragma(c: PContext, kind: TSymKind, n: PNode, allowed: TSymFlags): PSym = if n.kind == nkPragmaExpr: @@ -410,31 +415,31 @@ proc semIdentWithPragma(c: PContext, kind: TSymKind, n: PNode, proc checkForOverlap(c: PContext, t: PNode, currentEx, branchIndex: int) = let ex = t[branchIndex][currentEx].skipConv for i in countup(1, branchIndex): - for j in countup(0, sonsLen(t.sons[i]) - 2): + for j in countup(0, sonsLen(t.sons[i]) - 2): if i == branchIndex and j == currentEx: break if overlap(t.sons[i].sons[j].skipConv, ex): localError(ex.info, errDuplicateCaseLabel) - + proc semBranchRange(c: PContext, t, a, b: PNode, covered: var BiggestInt): PNode = checkMinSonsLen(t, 1) let ac = semConstExpr(c, a) let bc = semConstExpr(c, b) let at = fitNode(c, t.sons[0].typ, ac).skipConvTakeType let bt = fitNode(c, t.sons[0].typ, bc).skipConvTakeType - + result = newNodeI(nkRange, a.info) result.add(at) result.add(bt) if emptyRange(ac, bc): localError(b.info, errRangeIsEmpty) else: covered = covered + getOrdValue(bc) - getOrdValue(ac) + 1 -proc semCaseBranchRange(c: PContext, t, b: PNode, - covered: var BiggestInt): PNode = +proc semCaseBranchRange(c: PContext, t, b: PNode, + covered: var BiggestInt): PNode = checkSonsLen(b, 3) result = semBranchRange(c, t, b.sons[1], b.sons[2], covered) -proc semCaseBranchSetElem(c: PContext, t, b: PNode, - covered: var BiggestInt): PNode = +proc semCaseBranchSetElem(c: PContext, t, b: PNode, + covered: var BiggestInt): PNode = if isRange(b): checkSonsLen(b, 3) result = semBranchRange(c, t, b.sons[1], b.sons[2], covered) @@ -445,9 +450,10 @@ proc semCaseBranchSetElem(c: PContext, t, b: PNode, result = fitNode(c, t.sons[0].typ, b) inc(covered) -proc semCaseBranch(c: PContext, t, branch: PNode, branchIndex: int, - covered: var BiggestInt) = - for i in countup(0, sonsLen(branch) - 2): +proc semCaseBranch(c: PContext, t, branch: PNode, branchIndex: int, + covered: var BiggestInt) = + + for i in countup(0, sonsLen(branch) - 2): var b = branch.sons[i] if b.kind == nkRange: branch.sons[i] = b @@ -456,8 +462,11 @@ proc semCaseBranch(c: PContext, t, branch: PNode, branchIndex: int, else: # constant sets and arrays are allowed: var r = semConstExpr(c, b) - # for ``{}`` we want to trigger the type mismatch in ``fitNode``: - if r.kind notin {nkCurly, nkBracket} or len(r) == 0: + if r.kind in {nkCurly, nkBracket} and len(r) == 0 and sonsLen(branch)==2: + # discarding ``{}`` and ``[]`` branches silently + delSon(branch, 0) + return + elif r.kind notin {nkCurly, nkBracket} or len(r) == 0: checkMinSonsLen(t, 1) branch.sons[i] = skipConv(fitNode(c, t.sons[0].typ, r)) inc(covered) @@ -471,7 +480,7 @@ proc semCaseBranch(c: PContext, t, branch: PNode, branchIndex: int, var L = branch.len swap(branch.sons[L-2], branch.sons[L-1]) checkForOverlap(c, t, i, branchIndex) - + proc semRecordNodeAux(c: PContext, n: PNode, check: var IntSet, pos: var int, father: PNode, rectype: PType) proc semRecordCase(c: PContext, n: PNode, check: var IntSet, pos: var int, @@ -505,11 +514,11 @@ proc semRecordCase(c: PContext, n: PNode, check: var IntSet, pos: var int, else: illFormedAst(n) delSon(b, sonsLen(b) - 1) semRecordNodeAux(c, lastSon(n.sons[i]), check, pos, b, rectype) - if chckCovered and (covered != lengthOrd(a.sons[0].typ)): + if chckCovered and (covered != lengthOrd(a.sons[0].typ)): localError(a.info, errNotAllCasesCovered) addSon(father, a) -proc semRecordNodeAux(c: PContext, n: PNode, check: var IntSet, pos: var int, +proc semRecordNodeAux(c: PContext, n: PNode, check: var IntSet, pos: var int, father: PNode, rectype: PType) = if n == nil: return case n.kind @@ -547,12 +556,12 @@ proc semRecordNodeAux(c: PContext, n: PNode, check: var IntSet, pos: var int, semRecordNodeAux(c, branch, check, pos, father, rectype) of nkRecCase: semRecordCase(c, n, check, pos, father, rectype) - of nkNilLit: + of nkNilLit: if father.kind != nkRecList: addSon(father, newNodeI(nkRecList, n.info)) of nkRecList: # attempt to keep the nesting at a sane level: var a = if father.kind == nkRecList: father else: copyNode(n) - for i in countup(0, sonsLen(n) - 1): + for i in countup(0, sonsLen(n) - 1): semRecordNodeAux(c, n.sons[i], check, pos, a, rectype) if a != father: addSon(father, a) of nkIdentDefs: @@ -561,10 +570,10 @@ proc semRecordNodeAux(c: PContext, n: PNode, check: var IntSet, pos: var int, var a: PNode if father.kind != nkRecList and length>=4: a = newNodeI(nkRecList, n.info) else: a = ast.emptyNode - if n.sons[length-1].kind != nkEmpty: + if n.sons[length-1].kind != nkEmpty: localError(n.sons[length-1].info, errInitHereNotAllowed) var typ: PType - if n.sons[length-2].kind == nkEmpty: + if n.sons[length-2].kind == nkEmpty: localError(n.info, errTypeExpected) typ = errorType(c) else: @@ -577,7 +586,7 @@ proc semRecordNodeAux(c: PContext, n: PNode, check: var IntSet, pos: var int, f.typ = typ f.position = pos if (rec != nil) and ({sfImportc, sfExportc} * rec.flags != {}) and - (f.loc.r == nil): + (f.loc.r == nil): f.loc.r = toRope(f.name.s) f.flags = f.flags + ({sfImportc, sfExportc} * rec.flags) inc(pos) @@ -589,8 +598,8 @@ proc semRecordNodeAux(c: PContext, n: PNode, check: var IntSet, pos: var int, if a.kind != nkEmpty: addSon(father, a) of nkEmpty: discard else: illFormedAst(n) - -proc addInheritedFieldsAux(c: PContext, check: var IntSet, pos: var int, + +proc addInheritedFieldsAux(c: PContext, check: var IntSet, pos: var int, n: PNode) = case n.kind of nkRecCase: @@ -609,31 +618,31 @@ proc addInheritedFieldsAux(c: PContext, check: var IntSet, pos: var int, inc(pos) else: internalError(n.info, "addInheritedFieldsAux()") -proc skipGenericInvokation(t: PType): PType {.inline.} = +proc skipGenericInvocation(t: PType): PType {.inline.} = result = t - if result.kind == tyGenericInvokation: + if result.kind == tyGenericInvocation: result = result.sons[0] if result.kind == tyGenericBody: result = lastSon(result) -proc addInheritedFields(c: PContext, check: var IntSet, pos: var int, +proc addInheritedFields(c: PContext, check: var IntSet, pos: var int, obj: PType) = assert obj.kind == tyObject - if (sonsLen(obj) > 0) and (obj.sons[0] != nil): - addInheritedFields(c, check, pos, obj.sons[0].skipGenericInvokation) + if (sonsLen(obj) > 0) and (obj.sons[0] != nil): + addInheritedFields(c, check, pos, obj.sons[0].skipGenericInvocation) addInheritedFieldsAux(c, check, pos, obj.n) proc semObjectNode(c: PContext, n: PNode, prev: PType): PType = if n.sonsLen == 0: return newConstraint(c, tyObject) var check = initIntSet() - var pos = 0 + var pos = 0 var base: PType = nil # n.sons[0] contains the pragmas (if any). We process these later... checkSonsLen(n, 3) - if n.sons[1].kind != nkEmpty: + if n.sons[1].kind != nkEmpty: base = skipTypes(semTypeNode(c, n.sons[1].sons[0], nil), skipPtrs) - var concreteBase = skipGenericInvokation(base).skipTypes(skipPtrs) - if concreteBase.kind == tyObject and tfFinal notin concreteBase.flags: + 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: @@ -672,8 +681,9 @@ proc addParamOrResult(c: PContext, param: PSym, kind: TSymKind) = elif param.typ.kind == tyTypeDesc: addDecl(c, param) else: - # within a macro, every param has the type PNimrodNode! - let nn = getSysSym"PNimrodNode" + # within a macro, every param has the type NimNode! + let nn = if getCompilerProc("NimNode") != nil: getSysSym"NimNode" + else: getSysSym"PNimrodNode" var a = copySym(param) a.typ = nn.typ addDecl(c, a) @@ -713,7 +723,7 @@ proc liftParamType(c: PContext, procKind: TSymKind, genericParams: PNode, genericParams.addSon(newSymNode(s)) result = typeClass addDecl(c, s) - + # XXX: There are codegen errors if this is turned into a nested proc template liftingWalk(typ: PType, anonFlag = false): expr = liftParamType(c, procKind, genericParams, typ, paramName, info, anonFlag) @@ -732,7 +742,7 @@ proc liftParamType(c: PContext, procKind: TSymKind, genericParams: PNode, case paramType.kind: of tyAnything: result = addImplicitGeneric(newTypeS(tyGenericParam, c)) - + of tyStatic: # proc(a: expr{string}, b: expr{nkLambda}) # overload on compile time values and AST trees @@ -743,7 +753,7 @@ proc liftParamType(c: PContext, procKind: TSymKind, genericParams: PNode, localError(info, errMacroBodyDependsOnGenericTypes, paramName) result = addImplicitGeneric(c.newTypeWithSons(tyStatic, @[base])) result.flags.incl({tfHasStatic, tfUnresolved}) - + of tyTypeDesc: if tfUnresolved notin paramType.flags: # naked typedescs are not bindOnce types @@ -751,12 +761,12 @@ proc liftParamType(c: PContext, procKind: TSymKind, genericParams: PNode, paramTypId.id == typedescId.id: paramTypId = nil result = addImplicitGeneric( c.newTypeWithSons(tyTypeDesc, @[paramType.base])) - + of tyDistinct: if paramType.sonsLen == 1: # disable the bindOnce behavior for the type class result = liftingWalk(paramType.sons[0], true) - + of tySequence, tySet, tyArray, tyOpenArray, tyVar, tyPtr, tyRef, tyProc: # XXX: this is a bit strange, but proc(s: seq) @@ -775,21 +785,22 @@ proc liftParamType(c: PContext, procKind: TSymKind, genericParams: PNode, if lifted != nil: paramType.sons[i] = lifted result = paramType - + of tyGenericBody: - result = newTypeS(tyGenericInvokation, c) + result = newTypeS(tyGenericInvocation, c) result.rawAddSon(paramType) - + for i in 0 .. paramType.sonsLen - 2: - let dummyType = if paramType.sons[i].kind == tyStatic: tyUnknown - else: tyAnything - result.rawAddSon newTypeS(dummyType, c) - + if paramType.sons[i].kind == tyStatic: + result.rawAddSon makeTypeFromExpr(c, ast.emptyNode) # aka 'tyUnknown' + else: + result.rawAddSon newTypeS(tyAnything, c) + if paramType.lastSon.kind == tyUserTypeClass: result.kind = tyUserTypeClassInst result.rawAddSon paramType.lastSon return addImplicitGeneric(result) - + result = instGenericContainer(c, paramType.sym.info, result, allowMetaTypes = true) result = newTypeWithSons(c, tyCompositeTypeClass, @[paramType, result]) @@ -821,8 +832,8 @@ proc liftParamType(c: PContext, procKind: TSymKind, genericParams: PNode, if liftBody != nil: result = liftBody result.shouldHaveMeta - - of tyGenericInvokation: + + of tyGenericInvocation: for i in 1 .. tyGenericInvokation") + if x.kind == tyGenericInvocation or f.sons[0].kind != tyGenericBody: + #InternalError("typeRel: tyGenericInvocation -> tyGenericInvocation") # simply no match for now: discard elif x.kind == tyGenericInst and @@ -896,7 +897,7 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation = # we steal the generic parameters from the tyGenericBody: for i in countup(1, sonsLen(f) - 1): var x = PType(idTableGet(c.bindings, f.sons[0].sons[i - 1])) - if x == nil or x.kind in {tyGenericInvokation, tyGenericParam}: + if x == nil or x.kind in {tyGenericInvocation, tyGenericParam}: internalError("wrong instantiated type!") put(c.bindings, f.sons[i], x) @@ -1015,9 +1016,17 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation = if result != isNone: put(c.bindings, f, aOrig) else: result = isNone + elif prev.kind == tyStatic: + if aOrig.kind == tyStatic: + result = typeRel(c, prev.lastSon, a) + if result != isNone and prev.n != nil: + if not exprStructuralEquivalent(prev.n, aOrig.n): + result = isNone + else: result = isNone else: - result = typeRel(c, prev, aOrig) - + # XXX endless recursion? + #result = typeRel(c, prev, aOrig) + result = isNone of tyTypeDesc: var prev = PType(idTableGet(c.bindings, f)) if prev == nil: @@ -1056,7 +1065,7 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation = of tyFromExpr: # fix the expression, so it contains the already instantiated types - if f.n == nil: return isGeneric + if f.n == nil or f.n.kind == nkEmpty: return isGeneric let reevaluated = tryResolvingStaticExpr(c, f.n) case reevaluated.typ.kind of tyTypeDesc: @@ -1156,6 +1165,15 @@ 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 paramTypesMatchAux(m: var TCandidate, f, argType: PType, argSemantized, argOrig: PNode): PNode = var @@ -1178,8 +1196,7 @@ proc paramTypesMatchAux(m: var TCandidate, f, argType: PType, if argType.kind == tyStatic: if m.callee.kind == tyGenericBody and tfGenericTypeParam notin argType.flags: - result = newNodeI(nkType, argOrig.info) - result.typ = makeTypeFromExpr(c, arg) + result = newNodeIT(nkType, argOrig.info, makeTypeFromExpr(c, arg)) return else: var evaluated = c.semTryConstExpr(c, arg) @@ -1252,12 +1269,25 @@ proc paramTypesMatchAux(m: var TCandidate, f, argType: PType, result = implicitConv(nkHiddenStdConv, f, result, m, c) of isGeneric: inc(m.genericMatches) - result = copyTree(arg) - result.typ = getInstantiatedType(c, arg, m, f) - # BUG: f may not be the right key! - if skipTypes(result.typ, abstractVar-{tyTypeDesc}).kind in {tyTuple}: - result = implicitConv(nkHiddenStdConv, f, copyTree(arg), m, c) - # BUGFIX: use ``result.typ`` and not `f` here + when true: + if arg.typ == nil: + result = arg + elif skipTypes(arg.typ, abstractVar-{tyTypeDesc}).kind == tyTuple: + result = implicitConv(nkHiddenStdConv, f, copyTree(arg), m, c) + elif arg.typ.isEmptyContainer: + result = arg.copyTree + result.typ = getInstantiatedType(c, arg, m, f) + else: + result = arg + else: + # XXX Why is this ever necessary? arg's type should not be retrofitted + # to match formal's type in this way! + result = copyTree(arg) + result.typ = getInstantiatedType(c, arg, m, f) + # BUG: f may not be the right key! + if skipTypes(result.typ, abstractVar-{tyTypeDesc}).kind in {tyTuple}: + result = implicitConv(nkHiddenStdConv, f, copyTree(arg), m, c) + # BUGFIX: use ``result.typ`` and not `f` here of isFromIntLit: # too lazy to introduce another ``*matches`` field, so we conflate # ``isIntConv`` and ``isIntLit`` here: @@ -1441,13 +1471,14 @@ proc matchesAux(c: PContext, n, nOrig: PNode, return checkConstraint(n.sons[a].sons[1]) if m.baseTypeMatch: - assert(container == nil) + #assert(container == nil) container = newNodeIT(nkBracket, n.sons[a].info, arrayConstr(c, arg)) addSon(container, arg) setSon(m.call, formal.position + 1, container) if f != formalLen - 1: container = nil - else: + else: setSon(m.call, formal.position + 1, arg) + inc f else: # unnamed param if f >= formalLen: @@ -1466,7 +1497,7 @@ proc matchesAux(c: PContext, n, nOrig: PNode, n.sons[a] = prepareOperand(c, formal.typ, n.sons[a]) var arg = paramTypesMatch(m, formal.typ, n.sons[a].typ, n.sons[a], nOrig.sons[a]) - if (arg != nil) and m.baseTypeMatch and (container != nil): + if arg != nil and m.baseTypeMatch and container != nil: addSon(container, arg) incrIndexType(container.typ) else: @@ -1480,7 +1511,7 @@ proc matchesAux(c: PContext, n, nOrig: PNode, internalError(n.sons[a].info, "matches") return formal = m.callee.n.sons[f].sym - if containsOrIncl(marker, formal.position): + if containsOrIncl(marker, formal.position) and container.isNil: # already in namedParams: localError(n.sons[a].info, errCannotBindXTwice, formal.name.s) m.state = csNoMatch @@ -1493,17 +1524,22 @@ proc matchesAux(c: PContext, n, nOrig: PNode, m.state = csNoMatch return if m.baseTypeMatch: - assert(container == nil) - container = newNodeIT(nkBracket, n.sons[a].info, arrayConstr(c, arg)) + #assert(container == nil) + if container.isNil: + container = newNodeIT(nkBracket, n.sons[a].info, arrayConstr(c, arg)) addSon(container, arg) setSon(m.call, formal.position + 1, implicitConv(nkHiddenStdConv, formal.typ, container, m, c)) - if f != formalLen - 1: container = nil + #if f != formalLen - 1: container = nil + + # pick the formal from the end, so that 'x, y, varargs, z' works: + f = max(f, formalLen - n.len + a + 1) else: setSon(m.call, formal.position + 1, arg) + inc(f) + container = nil checkConstraint(n.sons[a]) inc(a) - inc(f) proc semFinishOperands*(c: PContext, n: PNode) = # this needs to be called to ensure that after overloading resolution every diff --git a/compiler/transf.nim b/compiler/transf.nim index f511ed69fc..2f520aa202 100644 --- a/compiler/transf.nim +++ b/compiler/transf.nim @@ -321,6 +321,7 @@ proc transformYield(c: PTransf, n: PNode): PTransNode = proc transformAddrDeref(c: PTransf, n: PNode, a, b: TNodeKind): PTransNode = result = transformSons(c, n) + if gCmd == cmdCompileToCpp or sfCompileToCpp in c.module.flags: return var n = result.PNode case n.sons[0].kind of nkObjUpConv, nkObjDownConv, nkChckRange, nkChckRangeF, nkChckRange64: @@ -491,7 +492,8 @@ proc transformFor(c: PTransf, n: PNode): PTransNode = var newC = newTransCon(getCurrOwner(c)) newC.forStmt = n newC.forLoopBody = loopBody - internalAssert iter.kind == skIterator + # this can fail for 'nimsuggest' and 'check': + if iter.kind != skIterator: return result # generate access statements for the parameters (unless they are constant) pushTransCon(c, newC) for i in countup(1, sonsLen(call) - 1): diff --git a/compiler/treetab.nim b/compiler/treetab.nim index 63f3fc6e21..8d66d56c74 100644 --- a/compiler/treetab.nim +++ b/compiler/treetab.nim @@ -28,8 +28,9 @@ proc hashTree(n: PNode): THash = of nkFloatLit..nkFloat64Lit: if (n.floatVal >= - 1000000.0) and (n.floatVal <= 1000000.0): result = result !& toInt(n.floatVal) - of nkStrLit..nkTripleStrLit: - result = result !& hash(n.strVal) + of nkStrLit..nkTripleStrLit: + if not n.strVal.isNil: + result = result !& hash(n.strVal) else: for i in countup(0, sonsLen(n) - 1): result = result !& hashTree(n.sons[i]) diff --git a/compiler/types.nim b/compiler/types.nim index 78d390f133..5c3be75532 100644 --- a/compiler/types.nim +++ b/compiler/types.nim @@ -250,7 +250,7 @@ proc containsObject(t: PType): bool = proc isObjectWithTypeFieldPredicate(t: PType): bool = result = t.kind == tyObject and t.sons[0] == nil and - not (t.sym != nil and sfPure in t.sym.flags) and + not (t.sym != nil and {sfPure, sfInfixCall} * t.sym.flags != {}) and tfFinal notin t.flags proc analyseObjectWithTypeFieldAux(t: PType, @@ -396,7 +396,7 @@ proc rangeToStr(n: PNode): string = const typeToStr: array[TTypeKind, string] = ["None", "bool", "Char", "empty", "Array Constructor [$1]", "nil", "expr", "stmt", "typeDesc", - "GenericInvokation", "GenericBody", "GenericInst", "GenericParam", + "GenericInvocation", "GenericBody", "GenericInst", "GenericParam", "distinct $1", "enum", "ordinal[$1]", "array[$1, $2]", "object", "tuple", "set[$1]", "range[$1]", "ptr ", "ref ", "var ", "seq[$1]", "proc", "pointer", "OpenArray[$1]", "string", "CString", "Forward", @@ -432,9 +432,9 @@ proc typeToString(typ: PType, prefer: TPreferedDesc = preferName): string = result = $t.n.intVal else: result = "int literal(" & $t.n.intVal & ")" - of tyGenericBody, tyGenericInst, tyGenericInvokation: + of tyGenericBody, tyGenericInst, tyGenericInvocation: result = typeToString(t.sons[0]) & '[' - for i in countup(1, sonsLen(t) -1 -ord(t.kind != tyGenericInvokation)): + for i in countup(1, sonsLen(t) -1 -ord(t.kind != tyGenericInvocation)): if i > 1: add(result, ", ") add(result, typeToString(t.sons[i], preferGenericArg)) add(result, ']') @@ -649,7 +649,7 @@ proc lengthOrd(t: PType): BiggestInt = type TDistinctCompare* = enum ## how distinct types are to be compared dcEq, ## a and b should be the same type - dcEqIgnoreDistinct, ## compare symetrically: (distinct a) == b, a == b + dcEqIgnoreDistinct, ## compare symmetrically: (distinct a) == b, a == b ## or a == (distinct b) dcEqOrDistinctOf ## a equals b or a is distinct of b @@ -796,7 +796,7 @@ template ifFastObjectTypeCheckFailed(a, b: PType, body: stmt) {.immediate.} = else: # expensive structural equality test; however due to the way generic and # objects work, if one of the types does **not** contain tfFromGeneric, - # they cannot be equal. The check ``a.sym.Id == b.sym.Id`` checks + # they cannot be equal. The check ``a.sym.id == b.sym.id`` checks # for the same origin and is essential because we don't want "pure" # structural type equivalence: # @@ -941,7 +941,7 @@ proc sameTypeAux(x, y: PType, c: var TSameTypeClosure): bool = result = sameChildrenAux(a, b, c) and sameFlags(a, b) if result and ExactGenericParams in c.flags: result = a.sym.position == b.sym.position - of tyGenericInvokation, tyGenericBody, tySequence, + of tyGenericInvocation, tyGenericBody, tySequence, tyOpenArray, tySet, tyRef, tyPtr, tyVar, tyArrayConstr, tyArray, tyProc, tyConst, tyMutable, tyVarargs, tyIter, tyOrdinal, tyTypeClasses, tyFieldAccessor: @@ -1029,16 +1029,18 @@ proc typeAllowedAux(marker: var IntSet, typ: PType, kind: TSymKind, proc typeAllowedNode(marker: var IntSet, n: PNode, kind: TSymKind, flags: TTypeAllowedFlags = {}): PType = - if n != nil: + if n != nil: result = typeAllowedAux(marker, n.typ, kind, flags) #if not result: debug(n.typ) if result == nil: case n.kind - of nkNone..nkNilLit: + of nkNone..nkNilLit: discard else: for i in countup(0, sonsLen(n) - 1): - result = typeAllowedNode(marker, n.sons[i], kind, flags) + let it = n.sons[i] + if it.kind == nkRecCase and kind == skConst: return n.typ + result = typeAllowedNode(marker, it, kind, flags) if result != nil: break proc matchType*(a: PType, pattern: openArray[tuple[k:TTypeKind, i:int]], @@ -1085,7 +1087,7 @@ proc typeAllowedAux(marker: var IntSet, typ: PType, kind: TSymKind, if taField notin flags: result = t of tyTypeClasses: if not (tfGenericTypeParam in t.flags or taField notin flags): result = t - of tyGenericBody, tyGenericParam, tyGenericInvokation, + of tyGenericBody, tyGenericParam, tyGenericInvocation, tyNone, tyForward, tyFromExpr, tyFieldAccessor: result = t of tyNil: @@ -1098,7 +1100,7 @@ proc typeAllowedAux(marker: var IntSet, typ: PType, kind: TSymKind, result = typeAllowedAux(marker, lastSon(t), kind, flags) of tyRange: if skipTypes(t.sons[0], abstractInst-{tyTypeDesc}).kind notin - {tyChar, tyEnum, tyInt..tyFloat128}: result = t + {tyChar, tyEnum, tyInt..tyFloat128, tyUInt8..tyUInt32}: result = t of tyOpenArray, tyVarargs: if kind != skParam: result = t else: result = typeAllowedAux(marker, t.sons[0], skVar, flags) @@ -1118,7 +1120,7 @@ proc typeAllowedAux(marker: var IntSet, typ: PType, kind: TSymKind, result = typeAllowedAux(marker, t.sons[i], kind, flags) if result != nil: break of tyObject, tyTuple: - if kind == skConst and t.kind == tyObject: return t + if kind == skConst and t.kind == tyObject and t.sons[0] != nil: return t let flags = flags+{taField} for i in countup(0, sonsLen(t) - 1): result = typeAllowedAux(marker, t.sons[i], kind, flags) diff --git a/compiler/vm.nim b/compiler/vm.nim index 7edbb3fd20..a36de1c20d 100644 --- a/compiler/vm.nim +++ b/compiler/vm.nim @@ -123,8 +123,12 @@ proc createStrKeepNode(x: var TFullReg) = if x.node.isNil: x.node = newNode(nkStrLit) elif x.node.kind == nkNilLit: + when defined(useNodeIds): + let id = x.node.id system.reset(x.node[]) x.node.kind = nkStrLit + when defined(useNodeIds): + x.node.id = id elif x.node.kind notin {nkStrLit..nkTripleStrLit} or nfAllConst in x.node.flags: # XXX this is hacky; tests/txmlgen triggers it: @@ -154,7 +158,7 @@ proc moveConst(x: var TFullReg, y: TFullReg) = of rkNodeAddr: x.nodeAddr = y.nodeAddr # this seems to be the best way to model the reference semantics -# of PNimrodNode: +# of system.NimNode: template asgnRef(x, y: expr) = moveConst(x, y) proc copyValue(src: PNode): PNode = @@ -772,10 +776,14 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg = stackTrace(c, tos, pc, errNilAccess) of opcEcho: let rb = instr.regB - for i in ra..ra+rb-1: - #if regs[i].kind != rkNode: debug regs[i] - write(stdout, regs[i].node.strVal) - writeln(stdout, "") + if rb == 1: + msgWriteln(regs[ra].node.strVal) + else: + var outp = "" + for i in ra..ra+rb-1: + #if regs[i].kind != rkNode: debug regs[i] + outp.add(regs[i].node.strVal) + msgWriteln(outp) of opcContainsSet: decodeBC(rkInt) regs[ra].intVal = ord(inSet(regs[rb].node, regs[rc].regToNode)) @@ -1087,14 +1095,20 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg = of opcNAdd: decodeBC(rkNode) var u = regs[rb].node - u.add(regs[rc].node) + if u.kind notin {nkEmpty..nkNilLit}: + u.add(regs[rc].node) + else: + stackTrace(c, tos, pc, errGenerated, "cannot add to node kind: " & $u.kind) regs[ra].node = u of opcNAddMultiple: decodeBC(rkNode) let x = regs[rc].node var u = regs[rb].node - # XXX can be optimized: - for i in 0.. n.safeLen and sym.typ.len > 1: - globalError(n.info, "got $#, but expected $# argument(s)" % [$ """ -# Chunk of HTML emmited for each entry in the HTML table of contents. +# Chunk of HTML emitted for each entry in the HTML table of contents. # Available variables are: # * $desc: the actual docstring of the item. # * $header: the full version of name, including types, pragmas, tags, etc. @@ -45,7 +45,7 @@ $seeSrc """ -# Chunk of HTML emmited for each entry in the HTML table of contents. +# Chunk of HTML emitted for each entry in the HTML table of contents. # See doc.item for available substitution variables. doc.item.toc = """
  • `_ documentation. diff --git a/doc/astspec.txt b/doc/astspec.txt index 5c4274093c..4c27272e2b 100644 --- a/doc/astspec.txt +++ b/doc/astspec.txt @@ -1,14 +1,14 @@ The AST in Nim ================= This section describes how the AST is modelled with Nim's type system. -The AST consists of nodes (``PNimrodNode``) with a variable number of +The AST consists of nodes (``NimNode``) with a variable number of children. Each node has a field named ``kind`` which describes what the node contains: .. code-block:: nim type - TNimrodNodeKind = enum ## kind of a node; only explanatory + NimNodeKind = enum ## kind of a node; only explanatory nnkNone, ## invalid node kind nnkEmpty, ## empty node nnkIdent, ## node contains an identifier @@ -18,11 +18,11 @@ contains: nnkCaseStmt, ## node represents a case statement ... ## many more - PNimrodNode = ref TNimrodNode - TNimrodNode {.final.} = object - case kind: TNimrodNodeKind ## the node's kind + NimNode = ref NimNodeObj + NimNodeObj = object + case kind: NimNodeKind ## the node's kind of nnkNone, nnkEmpty, nnkNilLit: - nil ## node contains no additional fields + discard ## node contains no additional fields of nnkCharLit..nnkInt64Lit: intVal: biggestInt ## the int literal of nnkFloatLit..nnkFloat64Lit: @@ -30,13 +30,13 @@ contains: of nnkStrLit..nnkTripleStrLit: strVal: string ## the string literal of nnkIdent: - ident: TNimrodIdent ## the identifier + ident: NimIdent ## the identifier of nnkSym: - symbol: PNimrodSymbol ## the symbol (after symbol lookup phase) + symbol: NimSymbol ## the symbol (after symbol lookup phase) else: - sons: seq[PNimrodNode] ## the node's sons (or children) + sons: seq[NimNode] ## the node's sons (or children) -For the ``PNimrodNode`` type, the ``[]`` operator has been overloaded: +For the ``NimNode`` type, the ``[]`` operator has been overloaded: ``n[i]`` is ``n``'s ``i``-th child. To specify the AST for the different Nim constructs, the notation @@ -73,10 +73,7 @@ Nim expression corresponding AST ----------------- --------------------------------------------- Identifiers are ``nnkIdent`` nodes. After the name lookup pass these nodes -get transferred into ``nnkSym`` nodes. However, a macro receives an AST that -has not been checked for semantics and thus the identifiers have not been -looked up. Macros should deal with ``nnkIdent`` nodes and do not need to deal -with ``nnkSym`` nodes. +get transferred into ``nnkSym`` nodes. Calls/expressions @@ -171,13 +168,13 @@ AST: nnkStrLit("hallo")) -Dereference operator ``^`` --------------------------- +Dereference operator ``[]`` +--------------------------- Concrete syntax: .. code-block:: nim - x^ + x[] AST: @@ -573,4 +570,3 @@ Other node kinds are especially designed to make AST manipulations easier. These are explained here. To be written. - diff --git a/doc/backends.txt b/doc/backends.txt index c7b60da26c..c7939baec9 100644 --- a/doc/backends.txt +++ b/doc/backends.txt @@ -225,7 +225,7 @@ The JavaScript target doesn't have any further interfacing considerations since it also has garbage collection, but the C targets require you to initialize Nim's internals, which is done calling a ``NimMain`` function. Also, C code requires you to specify a forward declaration for functions or -the compiler will asume certain types for the return value and parameters +the compiler will assume certain types for the return value and parameters which will likely make your program crash at runtime. The Nim compiler can generate a C interface header through the ``--header`` @@ -427,7 +427,7 @@ Custom data types ----------------- Just like strings, custom data types that are to be shared between Nim and -the backend will need careful consideration of who controlls who. If you want +the backend will need careful consideration of who controls who. If you want to hand a Nim reference to C code, you will need to use `GC_ref `_ to mark the reference as used, so it does not get freed. And for the C backend you will need to expose the `GC_unref diff --git a/doc/basicopt.txt b/doc/basicopt.txt index e366b2718e..7d08f11593 100644 --- a/doc/basicopt.txt +++ b/doc/basicopt.txt @@ -6,7 +6,6 @@ Command: //compile, c compile project with default code generator (C) //doc generate the documentation for inputfile //doc2 generate the documentation for the whole project - //i start Nim in interactive mode (limited) Arguments: arguments are passed to the program being run (if --run option is selected) @@ -14,7 +13,6 @@ Options: -p, --path:PATH add path to search paths -d, --define:SYMBOL define a conditional symbol -u, --undef:SYMBOL undefine a conditional symbol - --symbol:SYMBOL declare a conditional symbol -f, --forceBuild force rebuilding of all modules --stackTrace:on|off turn stack tracing on|off --lineTrace:on|off turn line tracing on|off @@ -31,6 +29,7 @@ Options: --infChecks:on|off turn Inf checks on|off --deadCodeElim:on|off whole program dead code elimination on|off --opt:none|speed|size optimize not at all or for speed|size + --debugger:native|endb use native debugger (gdb) | ENDB (experimental) --app:console|gui|lib|staticlib generate a console app|GUI app|DLL|static library -r, --run run the compiled program with given arguments diff --git a/doc/gc.txt b/doc/gc.txt index ac8d46cfa3..f51421bcd0 100644 --- a/doc/gc.txt +++ b/doc/gc.txt @@ -87,14 +87,14 @@ is triggered. Time measurement ---------------- -The GC's way of measing time uses (see ``lib/system/timers.nim`` for the +The GC's way of measuring time uses (see ``lib/system/timers.nim`` for the implementation): 1) ``QueryPerformanceCounter`` and ``QueryPerformanceFrequency`` on Windows. 2) ``mach_absolute_time`` on Mac OS X. 3) ``gettimeofday`` on Posix systems. -As such it supports a resolution of nano seconds internally; however the API +As such it supports a resolution of nanoseconds internally; however the API uses microseconds for convenience. diff --git a/doc/idetools.txt b/doc/idetools.txt index 48197f8bf9..2ffe46d4bf 100644 --- a/doc/idetools.txt +++ b/doc/idetools.txt @@ -27,7 +27,7 @@ integrations `_ already available. -Idetools invokation +Idetools invocation =================== Specifying the location of the query @@ -35,7 +35,7 @@ Specifying the location of the query All of the available idetools commands require you to specify a query location through the ``--track`` or ``--trackDirty`` switches. -The general idetools invokations are:: +The general idetools invocations are:: nim idetools --track:FILE,LINE,COL proj.nim @@ -129,7 +129,7 @@ the suggestions sorted first by scope (from innermost to outermost) and then by item name. -Invokation context +Invocation context ------------------ The ``--context`` idetools switch is very similar to the suggestions @@ -163,7 +163,7 @@ running/debugged user project. Compiler as a service (CAAS) ============================ -The ocasional use of idetools is acceptable for things like +The occasional use of idetools is acceptable for things like definitions, where the user puts the cursor on a symbol or double clicks it and after a second or two the IDE displays where that symbol is defined. Such latencies would be terrible for features @@ -533,10 +533,10 @@ run it manually. First you have to compile the tester:: Running the ``caasdriver`` without parameters will attempt to process all the test cases in all three operation modes. If a test succeeds nothing will be printed and the process will exit with zero. If any -test fails, the specific line of the test preceeding the failure +test fails, the specific line of the test preceding the failure and the failure itself will be dumped to stdout, along with a final indicator of the success state and operation mode. You can pass the -parameter ``verbose`` to force all output even on successfull tests. +parameter ``verbose`` to force all output even on successful tests. The normal operation mode is called ``ProcRun`` and it involves starting a process for each command or query, similar to running diff --git a/doc/intern.txt b/doc/intern.txt index a103703d7e..9582fc96f5 100644 --- a/doc/intern.txt +++ b/doc/intern.txt @@ -236,7 +236,7 @@ too. Type converters fall into this category: If in the above example module ``B`` is re-compiled, but ``A`` is not then ``B`` needs to be aware of ``toBool`` even though ``toBool`` is not referenced -in ``B`` *explicitely*. +in ``B`` *explicitly*. Both the multi method and the type converter problems are solved by storing them in special sections in the ROD file that are loaded *unconditionally* @@ -370,7 +370,7 @@ needed as the data structures needs to be rebuilt periodically anyway. Complete traversal is done in this way:: - for each page decriptor d: + for each page descriptor d: for each bit in d: if bit == 1: traverse the pointer belonging to this bit @@ -406,7 +406,7 @@ The generated code looks roughly like this: setRef(&r->left) } -Note that for systems with a continous stack (which most systems have) +Note that for systems with a continuous stack (which most systems have) the check whether the ref is on the stack is very cheap (only two comparisons). diff --git a/doc/lib.txt b/doc/lib.txt index b7c94b505a..76920c6a94 100644 --- a/doc/lib.txt +++ b/doc/lib.txt @@ -185,10 +185,19 @@ Math libraries * `complex `_ This module implements complex numbers and their mathematical operations. +* `rationals `_ + This module implements rational numbers and their mathematical operations. + * `fenv `_ Floating-point environment. Handling of floating-point rounding and exceptions (overflow, zero-devide, etc.). +* `basic2d `_ + Basic 2d support with vectors, points, matrices and some basic utilities. + +* `basic3d `_ + Basic 3d support with vectors, points, matrices and some basic utilities. + Internet Protocols and Support ------------------------------ @@ -218,9 +227,6 @@ Internet Protocols and Support * `smtp `_ This module implement a simple SMTP client. -* `irc `_ - This module implements an asynchronous IRC client. - * `ftpclient `_ This module implements an FTP client. @@ -367,7 +373,7 @@ Miscellaneous ------------- * `events `_ - This module implements an event system that is not dependant on external + This module implements an event system that is not dependent on external graphical toolkits. * `oids `_ diff --git a/doc/manual/about.txt b/doc/manual/about.txt index 13307279b4..78167efe3d 100644 --- a/doc/manual/about.txt +++ b/doc/manual/about.txt @@ -25,9 +25,9 @@ with ``'``. An example:: ifStmt = 'if' expr ':' stmts ('elif' expr ':' stmts)* ('else' stmts)? -The binary ``^*`` operator is used as a shorthand for 0 or more occurances +The binary ``^*`` operator is used as a shorthand for 0 or more occurrences separated by its second argument; likewise ``^+`` means 1 or more -occurances: ``a ^+ b`` is short for ``a (b a)*`` +occurrences: ``a ^+ b`` is short for ``a (b a)*`` and ``a ^* b`` is short for ``(a (b a)*)?``. Example:: arrayConstructor = '[' expr ^* ',' ']' diff --git a/doc/manual/syntax.txt b/doc/manual/syntax.txt index c975e7f480..cf44eb5881 100644 --- a/doc/manual/syntax.txt +++ b/doc/manual/syntax.txt @@ -12,10 +12,8 @@ Binary operators have 11 different levels of precedence. Associativity ------------- -Binary operators whose first character is ``^`` or its last character -is ``>`` are right-associative, all other binary operators are left-associative. - -Exception: The single "greater than" ``>`` operator is left-associative too. +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:. @@ -61,7 +59,7 @@ Precedence level Operators First charact Strong spaces ------------- -The number of spaces preceeding a non-keyword operator affects precedence +The number of spaces preceding a non-keyword operator affects precedence if the experimental parser directive ``#!strongSpaces`` is used. Indentation is not used to determine the number of spaces. If 2 or more operators have the same number of preceding spaces the precedence table applies, so ``1 + 3 * 4`` diff --git a/doc/manual/typedesc.txt b/doc/manual/typedesc.txt index 97ab18b56a..5087c12047 100644 --- a/doc/manual/typedesc.txt +++ b/doc/manual/typedesc.txt @@ -25,7 +25,7 @@ For the purposes of code generation, all static params are treated as generic params - the proc will be compiled separately for each unique supplied value (or combination of values). -Furthermore, the system module defines a `semistatic[T]` type than can be +Furthermore, the system module defines a `semistatic[T]` type that can be used to declare procs accepting both static and run-time values, which can optimize their body according to the supplied param using the `isStatic(p)` predicate: diff --git a/doc/manual/types.txt b/doc/manual/types.txt index 32ff19f75b..a207011219 100644 --- a/doc/manual/types.txt +++ b/doc/manual/types.txt @@ -1142,7 +1142,7 @@ modules like `db_sqlite `_. Void type --------- -The ``void`` type denotes the absense of any type. Parameters of +The ``void`` type denotes the absence of any type. Parameters of type ``void`` are treated as non-existent, ``void`` as a return type means that the procedure does not return a value: diff --git a/doc/nimc.txt b/doc/nimc.txt index 80fcf927be..c6b0b52554 100644 --- a/doc/nimc.txt +++ b/doc/nimc.txt @@ -1,45 +1,45 @@ -=================================== - Nim Compiler User Guide -=================================== - -:Author: Andreas Rumpf -:Version: |nimversion| - -.. contents:: - - "Look at you, hacker. A pathetic creature of meat and bone, panting and - sweating as you run through my corridors. How can you challenge a perfect, - immortal machine?" - - -Introduction -============ - -This document describes the usage of the *Nim compiler* -on the different supported platforms. It is not a definition of the Nim -programming language (therefore is the `manual `_). - -Nim is free software; it is licensed under the -`MIT License `_. - - -Compiler Usage -============== - -Command line switches ---------------------- -Basic command line switches are: - -Usage: - -.. include:: basicopt.txt - ----- - -Advanced command line switches are: - -.. include:: advopt.txt - +=================================== + Nim Compiler User Guide +=================================== + +:Author: Andreas Rumpf +:Version: |nimversion| + +.. contents:: + + "Look at you, hacker. A pathetic creature of meat and bone, panting and + sweating as you run through my corridors. How can you challenge a perfect, + immortal machine?" + + +Introduction +============ + +This document describes the usage of the *Nim compiler* +on the different supported platforms. It is not a definition of the Nim +programming language (therefore is the `manual `_). + +Nim is free software; it is licensed under the +`MIT License `_. + + +Compiler Usage +============== + +Command line switches +--------------------- +Basic command line switches are: + +Usage: + +.. include:: basicopt.txt + +---- + +Advanced command line switches are: + +.. include:: advopt.txt + List of warnings @@ -53,21 +53,16 @@ Name Description ========================== ============================================ CannotOpenFile Some file not essential for the compiler's working could not be opened. -OctalEscape The code contains an unsupported octal +OctalEscape The code contains an unsupported octal sequence. Deprecated The code uses a deprecated symbol. ConfigDeprecated The project makes use of a deprecated config file. -SmallLshouldNotBeUsed The letter 'l' should not be used as an +SmallLshouldNotBeUsed The letter 'l' should not be used as an identifier. -AnalysisLoophole The thread analysis was incomplete due to - an indirect call. -DifferentHeaps The code mixes different local heaps in a - very dangerous way. -WriteToForeignHeap The code contains a threading error. -EachIdentIsTuple The code contains a confusing ``var`` +EachIdentIsTuple The code contains a confusing ``var`` declaration. -ShadowIdent A local variable shadows another local +ShadowIdent A local variable shadows another local variable of an outer scope. User Some user defined warning. ========================== ============================================ @@ -103,30 +98,30 @@ enable builds in release mode (``-d:release``) where certain safety checks are omitted for better performance. Another common use is the ``-d:ssl`` switch to activate `SSL sockets `_. - -Configuration files -------------------- - -**Note:** The *project file name* is the name of the ``.nim`` file that is -passed as a command line argument to the compiler. - - -The ``nim`` executable processes configuration files in the following -directories (in this order; later files overwrite previous settings): - -1) ``$nim/config/nim.cfg``, ``/etc/nim.cfg`` (UNIX) or ``%NIMROD%/config/nim.cfg`` (Windows). This file can be skipped with the ``--skipCfg`` command line option. -2) ``/home/$user/.config/nim.cfg`` (UNIX) or ``%APPDATA%/nim.cfg`` (Windows). This file can be skipped with the ``--skipUserCfg`` command line option. -3) ``$parentDir/nim.cfg`` where ``$parentDir`` stands for any parent directory of the project file's path. These files can be skipped with the ``--skipParentCfg`` command line option. -4) ``$projectDir/nim.cfg`` where ``$projectDir`` stands for the project file's path. This file can be skipped with the ``--skipProjCfg`` command line option. -5) A project can also have a project specific configuration file named ``$project.nim.cfg`` that resides in the same directory as ``$project.nim``. This file can be skipped with the ``--skipProjCfg`` command line option. - - -Command line settings have priority over configuration file settings. - -The default build of a project is a `debug build`:idx:. To compile a -`release build`:idx: define the ``release`` symbol:: - - nim c -d:release myproject.nim + +Configuration files +------------------- + +**Note:** The *project file name* is the name of the ``.nim`` file that is +passed as a command line argument to the compiler. + + +The ``nim`` executable processes configuration files in the following +directories (in this order; later files overwrite previous settings): + +1) ``$nim/config/nim.cfg``, ``/etc/nim.cfg`` (UNIX) or ``%NIMROD%/config/nim.cfg`` (Windows). This file can be skipped with the ``--skipCfg`` command line option. +2) ``/home/$user/.config/nim.cfg`` (UNIX) or ``%APPDATA%/nim.cfg`` (Windows). This file can be skipped with the ``--skipUserCfg`` command line option. +3) ``$parentDir/nim.cfg`` where ``$parentDir`` stands for any parent directory of the project file's path. These files can be skipped with the ``--skipParentCfg`` command line option. +4) ``$projectDir/nim.cfg`` where ``$projectDir`` stands for the project file's path. This file can be skipped with the ``--skipProjCfg`` command line option. +5) A project can also have a project specific configuration file named ``$project.nim.cfg`` that resides in the same directory as ``$project.nim``. This file can be skipped with the ``--skipProjCfg`` command line option. + + +Command line settings have priority over configuration file settings. + +The default build of a project is a `debug build`:idx:. To compile a +`release build`:idx: define the ``release`` symbol:: + + nim c -d:release myproject.nim Search path handling @@ -138,8 +133,8 @@ found an ambiguity error is produced. ``nim dump`` shows the contents of the PATH. -However before the PATH is used the current directory is checked for the -file's existance. So if PATH contains ``$lib`` and ``$lib/bar`` and the +However before the PATH is used the current directory is checked for the +file's existence. So if PATH contains ``$lib`` and ``$lib/bar`` and the directory structure looks like this:: $lib/x.nim @@ -152,83 +147,83 @@ And ``main`` imports ``x``, ``foo/x`` is imported. If ``other`` imports ``x`` then both ``$lib/x.nim`` and ``$lib/bar/x.nim`` match and so the compiler should reject it. Currently however this check is not implemented and instead the first matching file is used. - - -Generated C code directory --------------------------- + + +Generated C code directory +-------------------------- The generated files that Nim produces all go into a subdirectory called ``nimcache`` in your project directory. This makes it easy to delete all generated files. Files generated in this directory follow a naming logic which you can read about in the `Nim Backend Integration document `_. -However, the generated C code is not platform independent. C code generated for -Linux does not compile on Windows, for instance. The comment on top of the -C file lists the OS, CPU and CC the file has been compiled for. - - -Compilation cache -================= - -**Warning**: The compilation cache is still highly experimental! - -The ``nimcache`` directory may also contain so called `rod`:idx: -or `symbol files`:idx:. These files are pre-compiled modules that are used by -the compiler to perform `incremental compilation`:idx:. This means that only -modules that have changed since the last compilation (or the modules depending -on them etc.) are re-compiled. However, per default no symbol files are -generated; use the ``--symbolFiles:on`` command line switch to activate them. - -Unfortunately due to technical reasons the ``--symbolFiles:on`` needs -to *aggregate* some generated C code. This means that the resulting executable -might contain some cruft even when dead code elimination is turned on. So -the final release build should be done with ``--symbolFiles:off``. - -Due to the aggregation of C code it is also recommended that each project -resides in its own directory so that the generated ``nimcache`` directory -is not shared between different projects. - - -Cross compilation -================= - -To cross compile, use for example:: - - nim c --cpu:i386 --os:linux --compile_only --gen_script myproject.nim - -Then move the C code and the compile script ``compile_myproject.sh`` to your +However, the generated C code is not platform independent. C code generated for +Linux does not compile on Windows, for instance. The comment on top of the +C file lists the OS, CPU and CC the file has been compiled for. + + +Compilation cache +================= + +**Warning**: The compilation cache is still highly experimental! + +The ``nimcache`` directory may also contain so called `rod`:idx: +or `symbol files`:idx:. These files are pre-compiled modules that are used by +the compiler to perform `incremental compilation`:idx:. This means that only +modules that have changed since the last compilation (or the modules depending +on them etc.) are re-compiled. However, per default no symbol files are +generated; use the ``--symbolFiles:on`` command line switch to activate them. + +Unfortunately due to technical reasons the ``--symbolFiles:on`` needs +to *aggregate* some generated C code. This means that the resulting executable +might contain some cruft even when dead code elimination is turned on. So +the final release build should be done with ``--symbolFiles:off``. + +Due to the aggregation of C code it is also recommended that each project +resides in its own directory so that the generated ``nimcache`` directory +is not shared between different projects. + + +Cross compilation +================= + +To cross compile, use for example:: + + nim c --cpu:i386 --os:linux --compile_only --gen_script myproject.nim + +Then move the C code and the compile script ``compile_myproject.sh`` to your Linux i386 machine and run the script. Another way is to make Nim invoke a cross compiler toolchain:: - - nim c --cpu:arm --os:linux myproject.nim - -For cross compilation, the compiler invokes a C compiler named -like ``$cpu.$os.$cc`` (for example arm.linux.gcc) and the configuration + + nim c --cpu:arm --os:linux myproject.nim + +For cross compilation, the compiler invokes a C compiler named +like ``$cpu.$os.$cc`` (for example arm.linux.gcc) and the configuration system is used to provide meaningful defaults. For example for ``ARM`` your configuration file should contain something like:: arm.linux.gcc.path = "/usr/bin" arm.linux.gcc.exe = "arm-linux-gcc" arm.linux.gcc.linkerexe = "arm-linux-gcc" - - -DLL generation -============== - -Nim supports the generation of DLLs. However, there must be only one -instance of the GC per process/address space. This instance is contained in -``nimrtl.dll``. This means that every generated Nim DLL depends -on ``nimrtl.dll``. To generate the "nimrtl.dll" file, use the command:: - - nim c -d:release lib/nimrtl.nim - -To link against ``nimrtl.dll`` use the command:: - - nim c -d:useNimRtl myprog.nim - -**Note**: Currently the creation of ``nimrtl.dll`` with thread support has -never been tested and is unlikely to work! + + +DLL generation +============== + +Nim supports the generation of DLLs. However, there must be only one +instance of the GC per process/address space. This instance is contained in +``nimrtl.dll``. This means that every generated Nim DLL depends +on ``nimrtl.dll``. To generate the "nimrtl.dll" file, use the command:: + + nim c -d:release lib/nimrtl.nim + +To link against ``nimrtl.dll`` use the command:: + + nim c -d:useNimRtl myprog.nim + +**Note**: Currently the creation of ``nimrtl.dll`` with thread support has +never been tested and is unlikely to work! Additional compilation switches @@ -247,10 +242,10 @@ Define Effect version. ``useFork`` Makes ``osproc`` use ``fork`` instead of ``posix_spawn``. ``useNimRtl`` Compile and link against ``nimrtl.dll``. -``useMalloc`` Makes Nim use C's `malloc`:idx: instead of Nim's +``useMalloc`` Makes Nim use C's `malloc`:idx: instead of Nim's own memory manager. This only works with ``gc:none``. -``useRealtimeGC`` Enables support of Nim's GC for *soft* realtime - systems. See the documentation of the `gc `_ +``useRealtimeGC`` Enables support of Nim's GC for *soft* realtime + systems. See the documentation of the `gc `_ for further information. ``nodejs`` The JS target is actually ``node.js``. ``ssl`` Enables OpenSSL support for the sockets module. @@ -258,84 +253,84 @@ Define Effect ``uClibc`` Use uClibc instead of libc. (Relevant for Unix-like OSes) ================== ========================================================= - - -Additional Features -=================== - -This section describes Nim's additional features that are not listed in the -Nim manual. Some of the features here only make sense for the C code -generator and are subject to change. - - -NoDecl pragma -------------- -The ``noDecl`` pragma can be applied to almost any symbol (variable, proc, -type, etc.) and is sometimes useful for interoperability with C: -It tells Nim that it should not generate a declaration for the symbol in -the C code. For example: - -.. code-block:: Nim - var - EACCES {.importc, noDecl.}: cint # pretend EACCES was a variable, as - # Nim does not know its value - -However, the ``header`` pragma is often the better alternative. - -**Note**: This will not work for the LLVM backend. - - -Header pragma -------------- -The ``header`` pragma is very similar to the ``noDecl`` pragma: It can be -applied to almost any symbol and specifies that it should not be declared -and instead the generated code should contain an ``#include``: - -.. code-block:: Nim - type - PFile {.importc: "FILE*", header: "".} = distinct pointer - # import C's FILE* type; Nim will treat it as a new pointer type - -The ``header`` pragma always expects a string constant. The string contant -contains the header file: As usual for C, a system header file is enclosed -in angle brackets: ``<>``. If no angle brackets are given, Nim -encloses the header file in ``""`` in the generated C code. - -**Note**: This will not work for the LLVM backend. - - -IncompleteStruct pragma ------------------------ -The ``incompleteStruct`` pragma tells the compiler to not use the -underlying C ``struct`` in a ``sizeof`` expression: - -.. code-block:: Nim - type - DIR* {.importc: "DIR", header: "", - final, pure, incompleteStruct.} = object - - -Compile pragma --------------- -The ``compile`` pragma can be used to compile and link a C/C++ source file -with the project: - -.. code-block:: Nim - {.compile: "myfile.cpp".} - -**Note**: Nim computes a CRC checksum and only recompiles the file if it -has changed. You can use the ``-f`` command line option to force recompilation -of the file. - - -Link pragma ------------ -The ``link`` pragma can be used to link an additional file with the project: - -.. code-block:: Nim - {.link: "myfile.o".} - - + + +Additional Features +=================== + +This section describes Nim's additional features that are not listed in the +Nim manual. Some of the features here only make sense for the C code +generator and are subject to change. + + +NoDecl pragma +------------- +The ``noDecl`` pragma can be applied to almost any symbol (variable, proc, +type, etc.) and is sometimes useful for interoperability with C: +It tells Nim that it should not generate a declaration for the symbol in +the C code. For example: + +.. code-block:: Nim + var + EACCES {.importc, noDecl.}: cint # pretend EACCES was a variable, as + # Nim does not know its value + +However, the ``header`` pragma is often the better alternative. + +**Note**: This will not work for the LLVM backend. + + +Header pragma +------------- +The ``header`` pragma is very similar to the ``noDecl`` pragma: It can be +applied to almost any symbol and specifies that it should not be declared +and instead the generated code should contain an ``#include``: + +.. code-block:: Nim + type + PFile {.importc: "FILE*", header: "".} = distinct pointer + # import C's FILE* type; Nim will treat it as a new pointer type + +The ``header`` pragma always expects a string constant. The string contant +contains the header file: As usual for C, a system header file is enclosed +in angle brackets: ``<>``. If no angle brackets are given, Nim +encloses the header file in ``""`` in the generated C code. + +**Note**: This will not work for the LLVM backend. + + +IncompleteStruct pragma +----------------------- +The ``incompleteStruct`` pragma tells the compiler to not use the +underlying C ``struct`` in a ``sizeof`` expression: + +.. code-block:: Nim + type + DIR* {.importc: "DIR", header: "", + final, pure, incompleteStruct.} = object + + +Compile pragma +-------------- +The ``compile`` pragma can be used to compile and link a C/C++ source file +with the project: + +.. code-block:: Nim + {.compile: "myfile.cpp".} + +**Note**: Nim computes a CRC checksum and only recompiles the file if it +has changed. You can use the ``-f`` command line option to force recompilation +of the file. + + +Link pragma +----------- +The ``link`` pragma can be used to link an additional file with the project: + +.. code-block:: Nim + {.link: "myfile.o".} + + PassC pragma ------------ The ``passC`` pragma can be used to pass additional parameters to the C @@ -365,76 +360,76 @@ embed parameters from an external command at compile time: {.passL: gorge("pkg-config --libs sdl").} -Emit pragma ------------ -The ``emit`` pragma can be used to directly affect the output of the -compiler's code generator. So it makes your code unportable to other code -generators/backends. Its usage is highly discouraged! However, it can be -extremely useful for interfacing with `C++`:idx: or `Objective C`:idx: code. - -Example: - -.. code-block:: Nim - {.emit: """ - static int cvariable = 420; - """.} - +Emit pragma +----------- +The ``emit`` pragma can be used to directly affect the output of the +compiler's code generator. So it makes your code unportable to other code +generators/backends. Its usage is highly discouraged! However, it can be +extremely useful for interfacing with `C++`:idx: or `Objective C`:idx: code. + +Example: + +.. code-block:: Nim + {.emit: """ + static int cvariable = 420; + """.} + {.push stackTrace:off.} - proc embedsC() = - var nimVar = 89 - # use backticks to access Nim symbols within an emit section: - {.emit: """fprintf(stdout, "%d\n", cvariable + (int)`nimVar`);""".} + proc embedsC() = + var nimVar = 89 + # use backticks to access Nim symbols within an emit section: + {.emit: """fprintf(stdout, "%d\n", cvariable + (int)`nimVar`);""".} {.pop.} - - embedsC() + + embedsC() As can be seen from the example, to Nim symbols can be referred via backticks. Use two backticks to produce a single verbatim backtick. - -ImportCpp pragma + +ImportCpp pragma ---------------- **Note**: `c2nim `_ can parse a large subset of C++ and knows about the ``importcpp`` pragma pattern language. It is not necessary to know all the details described here. - + Similar to the `importc pragma for C `_, the ``importcpp`` pragma can be used to import `C++`:idx: methods or C++ symbols -in general. The generated code then uses the C++ method calling +in general. The generated code then uses the C++ method calling syntax: ``obj->method(arg)``. In combination with the ``header`` and ``emit`` pragmas this allows *sloppy* interfacing with libraries written in C++: - -.. code-block:: Nim - # Horrible example of how to interface with a C++ engine ... ;-) - - {.link: "/usr/lib/libIrrlicht.so".} - - {.emit: """ - using namespace irr; - using namespace core; - using namespace scene; - using namespace video; - using namespace io; - using namespace gui; - """.} - - const - irr = "" - - type + +.. code-block:: Nim + # Horrible example of how to interface with a C++ engine ... ;-) + + {.link: "/usr/lib/libIrrlicht.so".} + + {.emit: """ + using namespace irr; + using namespace core; + using namespace scene; + using namespace video; + using namespace io; + using namespace gui; + """.} + + const + irr = "" + + type IrrlichtDeviceObj {.final, header: irr, - importcpp: "IrrlichtDevice".} = object - IrrlichtDevice = ptr IrrlichtDeviceObj - - proc createDevice(): IrrlichtDevice {. - header: irr, importcpp: "createDevice(@)".} - proc run(device: IrrlichtDevice): bool {. + importcpp: "IrrlichtDevice".} = object + IrrlichtDevice = ptr IrrlichtDeviceObj + + proc createDevice(): IrrlichtDevice {. + header: irr, importcpp: "createDevice(@)".} + proc run(device: IrrlichtDevice): bool {. header: irr, importcpp: "#.run(@)".} - -The compiler needs to be told to generate C++ (command ``cpp``) for -this to work. The conditional symbol ``cpp`` is defined when the compiler + +The compiler needs to be told to generate C++ (command ``cpp``) for +this to work. The conditional symbol ``cpp`` is defined when the compiler emits C++ code. @@ -446,9 +441,9 @@ declarations. It is usually much better to instead refer to the imported name via the ``namespace::identifier`` notation: .. code-block:: nim - type + type IrrlichtDeviceObj {.final, header: irr, - importcpp: "irr::IrrlichtDevice".} = object + importcpp: "irr::IrrlichtDevice".} = object Importcpp for enums @@ -574,7 +569,7 @@ language for object types: type StdMap {.importcpp: "std::map", header: "".} [K, V] = object proc `[]=`[K, V](this: var StdMap[K, V]; key: K; val: V) {. - importcpp: "#[#] = #".} + importcpp: "#[#] = #", header: "".} var x: StdMap[cint, cdouble] x[6] = 91.4 @@ -586,61 +581,61 @@ Produces: std::map x; x[6] = 91.4; - -ImportObjC pragma ------------------ + +ImportObjC pragma +----------------- Similar to the `importc pragma for C `_, the ``importobjc`` pragma can be used to import `Objective C`:idx: methods. The generated code then uses the Objective C method calling syntax: ``[obj method param1: arg]``. In addition with the ``header`` and ``emit`` pragmas this allows *sloppy* interfacing with libraries written in Objective C: - -.. code-block:: Nim - # horrible example of how to interface with GNUStep ... - - {.passL: "-lobjc".} - {.emit: """ - #include - @interface Greeter:Object - { - } - - - (void)greet:(long)x y:(long)dummy; - @end - - #include - @implementation Greeter - - - (void)greet:(long)x y:(long)dummy - { - printf("Hello, World!\n"); - } - @end - - #include - """.} - - type - Id {.importc: "id", header: "", final.} = distinct int - - proc newGreeter: Id {.importobjc: "Greeter new", nodecl.} - proc greet(self: Id, x, y: int) {.importobjc: "greet", nodecl.} - proc free(self: Id) {.importobjc: "free", nodecl.} - - var g = newGreeter() - g.greet(12, 34) - g.free() - -The compiler needs to be told to generate Objective C (command ``objc``) for -this to work. The conditional symbol ``objc`` is defined when the compiler -emits Objective C code. + +.. code-block:: Nim + # horrible example of how to interface with GNUStep ... + + {.passL: "-lobjc".} + {.emit: """ + #include + @interface Greeter:Object + { + } + + - (void)greet:(long)x y:(long)dummy; + @end + + #include + @implementation Greeter + + - (void)greet:(long)x y:(long)dummy + { + printf("Hello, World!\n"); + } + @end + + #include + """.} + + type + Id {.importc: "id", header: "", final.} = distinct int + + proc newGreeter: Id {.importobjc: "Greeter new", nodecl.} + proc greet(self: Id, x, y: int) {.importobjc: "greet", nodecl.} + proc free(self: Id) {.importobjc: "free", nodecl.} + + var g = newGreeter() + g.greet(12, 34) + g.free() + +The compiler needs to be told to generate Objective C (command ``objc``) for +this to work. The conditional symbol ``objc`` is defined when the compiler +emits Objective C code. CodegenDecl pragma ------------------ The ``codegenDecl`` pragma can be used to directly influence Nim's code -generator. It receives a format string that determines how the variable or +generator. It receives a format string that determines how the variable or proc is declared in the generated code: .. code-block:: nim @@ -660,56 +655,56 @@ debugging: .. code-block:: nim {.injectStmt: gcInvariants().} - + # ... complex code here that produces crashes ... - - -LineDir option --------------- -The ``lineDir`` option can be turned on or off. If turned on the -generated C code contains ``#line`` directives. This may be helpful for -debugging with GDB. - - -StackTrace option ------------------ -If the ``stackTrace`` option is turned on, the generated C contains code to -ensure that proper stack traces are given if the program crashes or an -uncaught exception is raised. - - -LineTrace option ----------------- -The ``lineTrace`` option implies the ``stackTrace`` option. If turned on, -the generated C contains code to ensure that proper stack traces with line -number information are given if the program crashes or an uncaught exception -is raised. - -Debugger option ---------------- -The ``debugger`` option enables or disables the *Embedded Nim Debugger*. -See the documentation of endb_ for further information. - - -Breakpoint pragma ------------------ -The *breakpoint* pragma was specially added for the sake of debugging with -ENDB. See the documentation of `endb `_ for further information. - - -Volatile pragma ---------------- -The ``volatile`` pragma is for variables only. It declares the variable as -``volatile``, whatever that means in C/C++ (its semantics are not well defined -in C/C++). - -**Note**: This pragma will not exist for the LLVM backend. + + +LineDir option +-------------- +The ``lineDir`` option can be turned on or off. If turned on the +generated C code contains ``#line`` directives. This may be helpful for +debugging with GDB. + + +StackTrace option +----------------- +If the ``stackTrace`` option is turned on, the generated C contains code to +ensure that proper stack traces are given if the program crashes or an +uncaught exception is raised. + + +LineTrace option +---------------- +The ``lineTrace`` option implies the ``stackTrace`` option. If turned on, +the generated C contains code to ensure that proper stack traces with line +number information are given if the program crashes or an uncaught exception +is raised. + +Debugger option +--------------- +The ``debugger`` option enables or disables the *Embedded Nim Debugger*. +See the documentation of endb_ for further information. + + +Breakpoint pragma +----------------- +The *breakpoint* pragma was specially added for the sake of debugging with +ENDB. See the documentation of `endb `_ for further information. + + +Volatile pragma +--------------- +The ``volatile`` pragma is for variables only. It declares the variable as +``volatile``, whatever that means in C/C++ (its semantics are not well defined +in C/C++). + +**Note**: This pragma will not exist for the LLVM backend. DynlibOverride ============== -By default Nim's ``dynlib`` pragma causes the compiler to generate +By default Nim's ``dynlib`` pragma causes the compiler to generate ``GetProcAddress`` (or their Unix counterparts) calls to bind to a DLL. With the ``dynlibOverride`` command line switch this can be prevented and then via ``--passL`` the static library can be linked @@ -736,28 +731,28 @@ Nim provides the `doc`:idx: and `doc2`:idx: commands to generate HTML documentation from ``.nim`` source files. Only exported symbols will appear in the output. For more details `see the docgen documentation `_. -Nim idetools integration -======================== - -Nim provides language integration with external IDEs through the -idetools command. See the documentation of `idetools `_ -for further information. - - -Nim interactive mode -==================== - -The Nim compiler supports an interactive mode. This is also known as -a `REPL`:idx: (*read eval print loop*). If Nim has been built with the -``-d:useGnuReadline`` switch, it uses the GNU readline library for terminal -input management. To start Nim in interactive mode use the command -``nim i``. To quit use the ``quit()`` command. To determine whether an input -line is an incomplete statement to be continued these rules are used: - -1. The line ends with ``[-+*/\\<>!\?\|%&$@~,;:=#^]\s*$`` (operator symbol followed by optional whitespace). -2. The line starts with a space (indentation). -3. The line is within a triple quoted string literal. However, the detection - does not work if the line contains more than one ``"""``. +Nim idetools integration +======================== + +Nim provides language integration with external IDEs through the +idetools command. See the documentation of `idetools `_ +for further information. + + +Nim interactive mode +==================== + +The Nim compiler supports an interactive mode. This is also known as +a `REPL`:idx: (*read eval print loop*). If Nim has been built with the +``-d:useGnuReadline`` switch, it uses the GNU readline library for terminal +input management. To start Nim in interactive mode use the command +``nim i``. To quit use the ``quit()`` command. To determine whether an input +line is an incomplete statement to be continued these rules are used: + +1. The line ends with ``[-+*/\\<>!\?\|%&$@~,;:=#^]\s*$`` (operator symbol followed by optional whitespace). +2. The line starts with a space (indentation). +3. The line is within a triple quoted string literal. However, the detection + does not work if the line contains more than one ``"""``. Nim for embedded systems @@ -768,107 +763,107 @@ for 16bit micro controllers is feasible. Use the `standalone`:idx: target (``--os:standalone``) for a bare bones standard library that lacks any OS features. -To make the compiler output code for a 16bit target use the ``--cpu:avr`` +To make the compiler output code for a 16bit target use the ``--cpu:avr`` target. For example, to generate code for an `AVR`:idx: processor use this command:: - + nim c --cpu:avr --os:standalone --deadCodeElim:on --genScript x.nim For the ``standalone`` target one needs to provide a file ``panicoverride.nim``. See ``tests/manyloc/standalone/panicoverride.nim`` for an example implementation. - + Nim for realtime systems ======================== -See the documentation of Nim's soft realtime `GC `_ for further +See the documentation of Nim's soft realtime `GC `_ for further information. - -Debugging with Nim -================== - -Nim comes with its own *Embedded Nim Debugger*. See -the documentation of endb_ for further information. - - -Optimizing for Nim -================== - -Nim has no separate optimizer, but the C code that is produced is very -efficient. Most C compilers have excellent optimizers, so usually it is -not needed to optimize one's code. Nim has been designed to encourage -efficient code: The most readable code in Nim is often the most efficient -too. - -However, sometimes one has to optimize. Do it in the following order: - -1. switch off the embedded debugger (it is **slow**!) -2. turn on the optimizer and turn off runtime checks -3. profile your code to find where the bottlenecks are -4. try to find a better algorithm -5. do low-level optimizations - -This section can only help you with the last item. - - -Optimizing string handling --------------------------- - -String assignments are sometimes expensive in Nim: They are required to -copy the whole string. However, the compiler is often smart enough to not copy -strings. Due to the argument passing semantics, strings are never copied when -passed to subroutines. The compiler does not copy strings that are a result from -a procedure call, because the callee returns a new string anyway. -Thus it is efficient to do: - -.. code-block:: Nim - var s = procA() # assignment will not copy the string; procA allocates a new - # string already - -However it is not efficient to do: - -.. code-block:: Nim - var s = varA # assignment has to copy the whole string into a new buffer! - -For ``let`` symbols a copy is not always necessary: - -.. code-block:: Nim - let s = varA # may only copy a pointer if it safe to do so - - -If you know what you're doing, you can also mark single string (or sequence) -objects as `shallow`:idx:\: - -.. code-block:: Nim - var s = "abc" - shallow(s) # mark 's' as shallow string - var x = s # now might not copy the string! - -Usage of ``shallow`` is always safe once you know the string won't be modified -anymore, similar to Ruby's `freeze`:idx:. - - -The compiler optimizes string case statements: A hashing scheme is used for them -if several different string constants are used. So code like this is reasonably -efficient: - -.. code-block:: Nim - case normalize(k.key) - of "name": c.name = v - of "displayname": c.displayName = v - of "version": c.version = v - of "os": c.oses = split(v, {';'}) - of "cpu": c.cpus = split(v, {';'}) - of "authors": c.authors = split(v, {';'}) - of "description": c.description = v - of "app": - case normalize(v) - of "console": c.app = appConsole - of "gui": c.app = appGUI - else: quit(errorStr(p, "expected: console or gui")) - of "license": c.license = UnixToNativePath(k.value) - else: quit(errorStr(p, "unknown variable: " & k.key)) + +Debugging with Nim +================== + +Nim comes with its own *Embedded Nim Debugger*. See +the documentation of endb_ for further information. + + +Optimizing for Nim +================== + +Nim has no separate optimizer, but the C code that is produced is very +efficient. Most C compilers have excellent optimizers, so usually it is +not needed to optimize one's code. Nim has been designed to encourage +efficient code: The most readable code in Nim is often the most efficient +too. + +However, sometimes one has to optimize. Do it in the following order: + +1. switch off the embedded debugger (it is **slow**!) +2. turn on the optimizer and turn off runtime checks +3. profile your code to find where the bottlenecks are +4. try to find a better algorithm +5. do low-level optimizations + +This section can only help you with the last item. + + +Optimizing string handling +-------------------------- + +String assignments are sometimes expensive in Nim: They are required to +copy the whole string. However, the compiler is often smart enough to not copy +strings. Due to the argument passing semantics, strings are never copied when +passed to subroutines. The compiler does not copy strings that are a result from +a procedure call, because the callee returns a new string anyway. +Thus it is efficient to do: + +.. code-block:: Nim + var s = procA() # assignment will not copy the string; procA allocates a new + # string already + +However it is not efficient to do: + +.. code-block:: Nim + var s = varA # assignment has to copy the whole string into a new buffer! + +For ``let`` symbols a copy is not always necessary: + +.. code-block:: Nim + let s = varA # may only copy a pointer if it safe to do so + + +If you know what you're doing, you can also mark single string (or sequence) +objects as `shallow`:idx:\: + +.. code-block:: Nim + var s = "abc" + shallow(s) # mark 's' as shallow string + var x = s # now might not copy the string! + +Usage of ``shallow`` is always safe once you know the string won't be modified +anymore, similar to Ruby's `freeze`:idx:. + + +The compiler optimizes string case statements: A hashing scheme is used for them +if several different string constants are used. So code like this is reasonably +efficient: + +.. code-block:: Nim + case normalize(k.key) + of "name": c.name = v + of "displayname": c.displayName = v + of "version": c.version = v + of "os": c.oses = split(v, {';'}) + of "cpu": c.cpus = split(v, {';'}) + of "authors": c.authors = split(v, {';'}) + of "description": c.description = v + of "app": + case normalize(v) + of "console": c.app = appConsole + of "gui": c.app = appGUI + else: quit(errorStr(p, "expected: console or gui")) + of "license": c.license = UnixToNativePath(k.value) + else: quit(errorStr(p, "unknown variable: " & k.key)) diff --git a/doc/nimgrep.txt b/doc/nimgrep.txt index e2f7b228fa..2d429e8b59 100644 --- a/doc/nimgrep.txt +++ b/doc/nimgrep.txt @@ -25,9 +25,9 @@ Compile nimgrep with the command:: And copy the executable somewhere in your ``$PATH``. -Command line switches -===================== - +Command line switches +===================== + Usage: nimgrep [options] [pattern] [replacement] (file/directory)* Options: @@ -37,7 +37,7 @@ Options: --re pattern is a regular expression (default); extended syntax for the regular expression is always turned on --recursive process directories recursively - --confirm confirm each occurence/replacement; there is a chance + --confirm confirm each occurrence/replacement; there is a chance to abort any time without touching the file --stdin read pattern from stdin (to avoid the shell's confusing quoting rules) diff --git a/doc/niminst.txt b/doc/niminst.txt index d743c5187d..ca05cc514d 100644 --- a/doc/niminst.txt +++ b/doc/niminst.txt @@ -190,6 +190,6 @@ Real world example The installers for the Nim compiler itself are generated by niminst. Have a look at its configuration file: -.. include:: compiler/nim.ini +.. include:: compiler/installer.ini :literal: diff --git a/doc/tut1.txt b/doc/tut1.txt index b2991ba809..cb5a0c8dd5 100644 --- a/doc/tut1.txt +++ b/doc/tut1.txt @@ -749,7 +749,8 @@ Forward declarations -------------------- Every variable, procedure, etc. needs to be declared before it can be used. -(The reason for this is compilation efficiency.) +(The reason for this is that it is non-trivial to do better than that in a +language that supports meta programming as extensively as Nim does.) However, this cannot be done for mutually recursive procedures: .. code-block:: nim @@ -767,7 +768,7 @@ introduced to the compiler before it is completely defined. The syntax for such a forward declaration is simple: just omit the ``=`` and the procedure's body. -Later versions of the language may get rid of the need for forward +Later versions of the language will weaken the requirements for forward declarations. The example also shows that a proc's body can consist of a single expression diff --git a/doc/tut2.txt b/doc/tut2.txt index dbf50894bd..4d30b1445e 100644 --- a/doc/tut2.txt +++ b/doc/tut2.txt @@ -37,7 +37,7 @@ Object Oriented Programming While Nim's support for object oriented programming (OOP) is minimalistic, powerful OOP techniques can be used. OOP is seen as *one* way to design a program, not *the only* way. Often a procedural approach leads to simpler -and more efficient code. In particular, prefering composition over inheritance +and more efficient code. In particular, preferring composition over inheritance is often the better design. @@ -142,7 +142,7 @@ An example: .. code-block:: nim - # This is an example how an abstract syntax tree could be modeled in Nim + # This is an example how an abstract syntax tree could be modelled in Nim type NodeKind = enum # the different node types nkInt, # a leaf with an integer value @@ -335,7 +335,7 @@ As the example demonstrates, invocation of a multi-method cannot be ambiguous: Collide 2 is preferred over collide 1 because the resolution works from left to right. Thus ``Unit, Thing`` is preferred over ``Thing, Unit``. -**Perfomance note**: Nim does not produce a virtual method table, but +**Performance note**: Nim does not produce a virtual method table, but generates dispatch trees. This avoids the expensive indirect branch for method calls and enables inlining. However, other optimizations like compile time evaluation or dead code elimination do not work with methods. @@ -735,14 +735,6 @@ regular expressions: return tkUnknown -Term rewriting macros ---------------------- - -Term rewriting macros can be used to enhance the compilation process -with user defined optimizations; see this `document `_ for -further information. - - Building your first macro ------------------------- diff --git a/examples/cross_calculator/android/readme.txt b/examples/cross_calculator/android/readme.txt index c04d1d3043..51fa9c6fda 100644 --- a/examples/cross_calculator/android/readme.txt +++ b/examples/cross_calculator/android/readme.txt @@ -10,7 +10,7 @@ just declared as a native method which will be resolved at runtime. The scripts nimbuild.sh and jnibuild.sh are in charge of building the Nim code and generating the jni bridge from the java code respectively. Finally, the ndk-build command from the android ndk tools has to be run to build the binary -libary which will be installed along the final apk. +library which will be installed along the final apk. All these steps are wrapped in the ant build script through the customization of the -post-compile rule. If you have the android ndk tools installed and you diff --git a/examples/cross_calculator/ios/src/NRViewController.m b/examples/cross_calculator/ios/src/NRViewController.m index 1f92f2d38f..f629bfc093 100644 --- a/examples/cross_calculator/ios/src/NRViewController.m +++ b/examples/cross_calculator/ios/src/NRViewController.m @@ -55,7 +55,7 @@ [self.bText resignFirstResponder]; } -/** Custom loadView method for backwards compatiblity. +/** Custom loadView method for backwards compatibility. * Unfortunately I've been unable to coerce Xcode 4.4 to generate nib files * which are compatible with my trusty iOS 3.0 ipod touch so in order to be * fully compatible for all devices we have to build the interface manually in diff --git a/examples/cross_calculator/nimrod_backend/backend.nim b/examples/cross_calculator/nim_backend/backend.nim similarity index 100% rename from examples/cross_calculator/nimrod_backend/backend.nim rename to examples/cross_calculator/nim_backend/backend.nim diff --git a/examples/cross_todo/nimrod_commandline/nimrod.cfg b/examples/cross_calculator/nim_commandline/nim.cfg similarity index 81% rename from examples/cross_todo/nimrod_commandline/nimrod.cfg rename to examples/cross_calculator/nim_commandline/nim.cfg index c1aedcf6ae..41c0344309 100644 --- a/examples/cross_todo/nimrod_commandline/nimrod.cfg +++ b/examples/cross_calculator/nim_commandline/nim.cfg @@ -1,4 +1,4 @@ # Nimrod configuration file. # The file is used only to add the path of the backend to the compiler options. -path="../nimrod_backend" +path="../nim_backend" diff --git a/examples/cross_calculator/nimrod_commandline/nimcalculator.nim b/examples/cross_calculator/nim_commandline/nimcalculator.nim similarity index 97% rename from examples/cross_calculator/nimrod_commandline/nimcalculator.nim rename to examples/cross_calculator/nim_commandline/nimcalculator.nim index 440834ca81..69d62a90ca 100644 --- a/examples/cross_calculator/nimrod_commandline/nimcalculator.nim +++ b/examples/cross_calculator/nim_commandline/nimcalculator.nim @@ -21,7 +21,7 @@ type cmdParams, # Two valid parameters were provided cmdInteractive # No parameters were provided, run interactive mode - TParamConfig = object of TObject + TParamConfig = object of RootObj action: TCommand # store the type of operation paramA, paramB: int # possibly store the valid parameters @@ -63,7 +63,7 @@ proc parseCmdLine(): TParamConfig = stdout.write USAGE quit "Unexpected option: " & key, 2 of cmdEnd: break - except EInvalidValue: + except ValueError: stdout.write USAGE quit "Invalid value " & val & " for parameter " & key, 3 @@ -85,7 +85,7 @@ proc parseUserInput(question: string): int = try: result = input.parseInt break - except EInvalidValue: + except ValueError: if input.len < 1: quit "Blank line detected, quitting.", 0 echo "Sorry, `$1' doesn't seem to be a valid integer" % input diff --git a/examples/cross_calculator/nimrod_commandline/readme.txt b/examples/cross_calculator/nim_commandline/readme.txt similarity index 60% rename from examples/cross_calculator/nimrod_commandline/readme.txt rename to examples/cross_calculator/nim_commandline/readme.txt index 5430e7b47b..f95bd962e0 100644 --- a/examples/cross_calculator/nimrod_commandline/readme.txt +++ b/examples/cross_calculator/nim_commandline/readme.txt @@ -1,10 +1,10 @@ -In this directory you will find the nimrod commandline version of the +In this directory you will find the nim commandline version of the cross-calculator sample. The commandline interface can be used non interactively through switches, or interactively when running the command without parameters. Compilation is fairly easy despite having the source split in different -directories. Thanks to the nimrod.cfg file, which adds the ../nimrod_backend +directories. Thanks to the nim.cfg file, which adds the ../nim_backend directory as a search path, you can compile and run the example just fine from -the command line with 'nimrod c -r nimcalculator.nim'. +the command line with 'nim c -r nimcalculator.nim'. diff --git a/examples/cross_todo/nimrod_backend/backend.nim b/examples/cross_todo/nim_backend/backend.nim similarity index 94% rename from examples/cross_todo/nimrod_backend/backend.nim rename to examples/cross_todo/nim_backend/backend.nim index 89e7d0b7e0..5b49bc4a92 100644 --- a/examples/cross_todo/nimrod_backend/backend.nim +++ b/examples/cross_todo/nim_backend/backend.nim @@ -13,7 +13,7 @@ type text*: string ## Description of the task to do. priority*: int ## The priority can be any user defined integer. isDone*: bool ## Done todos are still kept marked. - modificationDate: TTime ## The modification time can't be modified from + modificationDate: Time ## The modification time can't be modified from ## outside of this module, use the ## getModificationDate accessor. @@ -64,7 +64,7 @@ proc openDatabase*(path: string): TDbConn = # - Procs related to TTodo objects # proc initFromDB(id: int64; text: string; priority: int, isDone: bool; - modificationDate: TTime): TTodo = + modificationDate: Time): TTodo = ## Returns an initialized TTodo object created from database parameters. ## ## The proc assumes all values are right. Note this proc is NOT exported. @@ -81,7 +81,7 @@ proc getId*(todo: TTodo): int64 = return todo.id -proc getModificationDate*(todo: TTodo): TTime = +proc getModificationDate*(todo: TTodo): Time = ## Returns the last modification date of a TTodo entry. return todo.modificationDate @@ -91,7 +91,7 @@ proc update*(todo: var TTodo; conn: TDbConn): bool = ## ## Use this method if you (or another entity) have modified the database and ## want to update the object you have with whatever the database has stored. - ## Returns true if the update suceeded, or false if the object was not found + ## Returns true if the update succeeded, or false if the object was not found ## in the database any more, in which case you should probably get rid of the ## TTodo object. assert(todo.id >= 0, "The identifier of the todo entry can't be negative") @@ -99,14 +99,14 @@ proc update*(todo: var TTodo; conn: TDbConn): bool = FROM Todos WHERE id = ?""" try: - let rows = conn.GetAllRows(query, $todo.id) + let rows = conn.getAllRows(query, $todo.id) if len(rows) < 1: return assert(1 == len(rows), "Woah, didn't expect so many rows") todo.text = rows[0][0] todo.priority = rows[0][1].parseInt todo.isDone = rows[0][2].parseBool - todo.modificationDate = TTime(rows[0][3].parseInt) + todo.modificationDate = Time(rows[0][3].parseInt) result = true except: echo("Something went wrong selecting for id " & $todo.id) @@ -202,12 +202,12 @@ proc getPagedTodos*(conn: TDbConn; params: TPagedParams; #echo("Query " & string(query)) #echo("args: " & args.join(", ")) - var newId: biggestInt + var newId: BiggestInt for row in conn.fastRows(query, args): let numChars = row[0].parseBiggestInt(newId) assert(numChars > 0, "Huh, couldn't parse identifier from database?") result.add(initFromDB(int64(newId), row[1], row[2].parseInt, - row[3].parseBool, TTime(row[4].parseInt))) + row[3].parseBool, Time(row[4].parseInt))) proc getTodo*(conn: TDbConn; todoId: int64): ref TTodo = diff --git a/examples/cross_todo/nimrod_backend/readme.txt b/examples/cross_todo/nim_backend/readme.txt similarity index 78% rename from examples/cross_todo/nimrod_backend/readme.txt rename to examples/cross_todo/nim_backend/readme.txt index 6529f2e67b..16cb592fcc 100644 --- a/examples/cross_todo/nimrod_backend/readme.txt +++ b/examples/cross_todo/nim_backend/readme.txt @@ -1,4 +1,4 @@ -This directory contains the nimrod backend code for the todo cross platform +This directory contains the nim backend code for the todo cross platform example. Unlike the cross platform calculator example, this backend features more code, @@ -8,7 +8,7 @@ The test is not embedded directly in the backend.nim file to avoid being able to access internal data types and procs not exported and replicate the environment of client code. -In a bigger project with several people you could run `nimrod doc backend.nim` +In a bigger project with several people you could run `nim doc backend.nim` (or use the doc2 command for a whole project) and provide the generated html documentation to another programer for her to implement an interface without having to look at the source code. diff --git a/examples/cross_todo/nimrod_backend/testbackend.nim b/examples/cross_todo/nim_backend/testbackend.nim similarity index 100% rename from examples/cross_todo/nimrod_backend/testbackend.nim rename to examples/cross_todo/nim_backend/testbackend.nim diff --git a/examples/cross_calculator/nimrod_commandline/nimrod.cfg b/examples/cross_todo/nim_commandline/nim.cfg similarity index 81% rename from examples/cross_calculator/nimrod_commandline/nimrod.cfg rename to examples/cross_todo/nim_commandline/nim.cfg index c1aedcf6ae..41c0344309 100644 --- a/examples/cross_calculator/nimrod_commandline/nimrod.cfg +++ b/examples/cross_todo/nim_commandline/nim.cfg @@ -1,4 +1,4 @@ # Nimrod configuration file. # The file is used only to add the path of the backend to the compiler options. -path="../nimrod_backend" +path="../nim_backend" diff --git a/examples/cross_todo/nimrod_commandline/nimtodo.nim b/examples/cross_todo/nim_commandline/nimtodo.nim similarity index 98% rename from examples/cross_todo/nimrod_commandline/nimtodo.nim rename to examples/cross_todo/nim_commandline/nimtodo.nim index 1067177c38..4ab17e7a21 100644 --- a/examples/cross_todo/nimrod_commandline/nimtodo.nim +++ b/examples/cross_todo/nim_commandline/nimtodo.nim @@ -69,11 +69,11 @@ template parseTodoIdAndSetCommand(newCommand: TCommand): stmt = ## Helper to parse a big todo identifier into todoId and set command. try: let numChars = val.parseBiggestInt(newId) - if numChars < 1: raise newException(EInvalidValue, "Empty string?") + if numChars < 1: raise newException(ValueError, "Empty string?") result.command = newCommand result.todoId = newId - except EOverflow: - raise newException(EInvalidValue, "Value $1 too big" % val) + except OverflowError: + raise newException(ValueError, "Value $1 too big" % val) template verifySingleCommand(actions: stmt): stmt = @@ -111,7 +111,7 @@ proc parseCmdLine(): TParamConfig = usesListParams = false p = initOptParser() key, val: TaintedString - newId: biggestInt + newId: BiggestInt result.initDefaults @@ -178,7 +178,7 @@ proc parseCmdLine(): TParamConfig = abort("Unexpected option '$1'." % [key], 6) of cmdEnd: break - except EInvalidValue: + except ValueError: abort("Invalid integer value '$1' for parameter '$2'." % [val, key], 7) if not specifiedCommand: diff --git a/examples/cross_todo/nimrod_commandline/readme.txt b/examples/cross_todo/nim_commandline/readme.txt similarity index 100% rename from examples/cross_todo/nimrod_commandline/readme.txt rename to examples/cross_todo/nim_commandline/readme.txt diff --git a/examples/talk/hoisting.nim b/examples/talk/hoisting.nim index df13ba2cb0..54e00884f9 100644 --- a/examples/talk/hoisting.nim +++ b/examples/talk/hoisting.nim @@ -14,7 +14,7 @@ template optRe{re(x)}(x: string{lit}): Regex = g template `=~`(s: string, pattern: Regex): bool = - when not definedInScope(matches): + when not declaredInScope(matches): var matches {.inject.}: array[maxSubPatterns, string] match(s, pattern, matches) diff --git a/icons/nimrod.ico b/icons/nim.ico similarity index 100% rename from icons/nimrod.ico rename to icons/nim.ico diff --git a/icons/nim.rc b/icons/nim.rc new file mode 100644 index 0000000000..c053e08e9e --- /dev/null +++ b/icons/nim.rc @@ -0,0 +1,3 @@ +nimicon ICON "nim.ico" + + diff --git a/icons/nimrod.res b/icons/nim.res similarity index 100% rename from icons/nimrod.res rename to icons/nim.res diff --git a/icons/nimrod_icon.o b/icons/nim_icon.o similarity index 100% rename from icons/nimrod_icon.o rename to icons/nim_icon.o diff --git a/icons/nimrod.rc b/icons/nimrod.rc deleted file mode 100644 index 6f36b81451..0000000000 --- a/icons/nimrod.rc +++ /dev/null @@ -1,3 +0,0 @@ -nimrodicon ICON "nimrod.ico" - - diff --git a/koch.nim b/koch.nim index bc7631bcc4..d365262c12 100644 --- a/koch.nim +++ b/koch.nim @@ -40,7 +40,7 @@ Options: --help, -h shows this help and quits Possible Commands: boot [options] bootstraps with given command line options - install [bindir] installs to given directory + install [bindir] installs to given directory; Unix only! clean cleans Nimrod project; removes generated files web [options] generates the website and the full documentation website [options] generates only the website @@ -97,13 +97,13 @@ const compileNimInst = "-d:useLibzipSrc tools/niminst/niminst" proc csource(args: string) = - exec("$4 cc $1 -r $3 --var:version=$2 --var:mingw=none csource compiler/nim.ini $1" % + exec("$4 cc $1 -r $3 --var:version=$2 --var:mingw=none csource compiler/installer.ini $1" % [args, VersionAsString, compileNimInst, findNim()]) proc zip(args: string) = - exec("$3 cc -r $2 --var:version=$1 --var:mingw=none scripts compiler/nim.ini" % + exec("$3 cc -r $2 --var:version=$1 --var:mingw=none scripts compiler/installer.ini" % [VersionAsString, compileNimInst, findNim()]) - exec("$# --var:version=$# --var:mingw=none zip compiler/nim.ini" % + exec("$# --var:version=$# --var:mingw=none zip compiler/installer.ini" % ["tools/niminst/niminst".exe, VersionAsString]) proc buildTool(toolname, args: string) = @@ -121,27 +121,24 @@ proc nsis(args: string) = " nsis compiler/nim") % [VersionAsString, $(sizeof(pointer)*8)]) proc install(args: string) = - exec("$# cc -r $# --var:version=$# --var:mingw=none scripts compiler/nim.ini" % + exec("$# cc -r $# --var:version=$# --var:mingw=none scripts compiler/installer.ini" % [findNim(), compileNimInst, VersionAsString]) exec("sh ./install.sh $#" % args) proc web(args: string) = - exec("$# cc -r tools/nimweb.nim $# web/nim --putenv:nimversion=$#" % + exec("$# cc -r tools/nimweb.nim $# web/website.ini --putenv:nimversion=$#" % [findNim(), args, VersionAsString]) proc website(args: string) = - exec("$# cc -r tools/nimweb.nim $# --website web/nim --putenv:nimversion=$#" % + exec("$# cc -r tools/nimweb.nim $# --website web/website.ini --putenv:nimversion=$#" % [findNim(), args, VersionAsString]) proc pdf(args="") = - exec("$# cc -r tools/nimweb.nim $# --pdf web/nim --putenv:nimversion=$#" % + exec("$# cc -r tools/nimweb.nim $# --pdf web/website.ini --putenv:nimversion=$#" % [findNim(), args, VersionAsString]) # -------------- boot --------------------------------------------------------- -const - bootOptions = "" # options to pass to the bootstrap process - proc findStartNim: string = # we try several things before giving up: # * bin/nim @@ -180,11 +177,13 @@ proc thVersion(i: int): string = 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 - exec i.thVersion & " c $# $# compiler" / "nim.nim" % [bootOptions, args] + exec i.thVersion & " $# $# compiler" / "nim.nim" % [bootOptions, args] if sameFileContent(output, i.thVersion): copyExe(output, finalDest) echo "executables are equal: SUCCESS!" @@ -270,13 +269,13 @@ when defined(withUpdate): echo("Fetching updates from repo...") var pullout = execCmdEx(git & " pull origin master") if pullout[1] != 0: - quit("An error has occured.") + quit("An error has occurred.") else: if pullout[0].startsWith("Already up-to-date."): quit("No new changes fetched from the repo. " & "Local branch must be ahead of it. Exiting...") else: - quit("An error has occured.") + quit("An error has occurred.") else: echo("No repo or executable found!") @@ -360,7 +359,7 @@ of cmdArgument: of "boot": boot(op.cmdLineRest) of "clean": clean(op.cmdLineRest) of "web": web(op.cmdLineRest) - of "website": website(op.cmdLineRest) + of "website": website(op.cmdLineRest & " --googleAnalytics:UA-48159761-1") of "web0": # undocumented command for Araq-the-merciful: web(op.cmdLineRest & " --googleAnalytics:UA-48159761-1") diff --git a/koch.nimrod.cfg b/koch.nim.cfg similarity index 100% rename from koch.nimrod.cfg rename to koch.nim.cfg diff --git a/lib/core/locks.nim b/lib/core/locks.nim index 766b7b5363..8a809fc84b 100644 --- a/lib/core/locks.nim +++ b/lib/core/locks.nim @@ -21,7 +21,7 @@ type ## is performed. Deprecated, do not use anymore! AquireEffect* {.deprecated.} = object of LockEffect ## \ ## effect that denotes that some lock is - ## aquired. Deprecated, do not use anymore! + ## acquired. Deprecated, do not use anymore! ReleaseEffect* {.deprecated.} = object of LockEffect ## \ ## effect that denotes that some lock is ## released. Deprecated, do not use anymore! diff --git a/lib/core/macros.nim b/lib/core/macros.nim index 3e0da79be6..0888a87675 100644 --- a/lib/core/macros.nim +++ b/lib/core/macros.nim @@ -1,7 +1,7 @@ # # # Nim's Runtime Library -# (c) Copyright 2013 Andreas Rumpf +# (c) Copyright 2015 Andreas Rumpf # # See the file "copying.txt", included in this # distribution, for details about the copyright. @@ -15,7 +15,7 @@ include "system/inclrtl" ## .. include:: ../doc/astspec.txt type - TNimrodNodeKind* = enum + NimNodeKind* = enum nnkNone, nnkEmpty, nnkIdent, nnkSym, nnkType, nnkCharLit, nnkIntLit, nnkInt8Lit, nnkInt16Lit, nnkInt32Lit, nnkInt64Lit, nnkUIntLit, nnkUInt8Lit, @@ -72,20 +72,26 @@ type nnkEnumFieldDef, nnkArglist, nnkPattern nnkReturnToken - TNimNodeKinds* = set[TNimrodNodeKind] - TNimrodTypeKind* = enum + NimNodeKinds* = set[NimNodeKind] + NimTypeKind* = enum ntyNone, ntyBool, ntyChar, ntyEmpty, ntyArrayConstr, ntyNil, ntyExpr, ntyStmt, - ntyTypeDesc, ntyGenericInvokation, ntyGenericBody, ntyGenericInst, + ntyTypeDesc, ntyGenericInvocation, ntyGenericBody, ntyGenericInst, ntyGenericParam, ntyDistinct, ntyEnum, ntyOrdinal, ntyArray, ntyObject, ntyTuple, ntySet, ntyRange, ntyPtr, ntyRef, ntyVar, ntySequence, ntyProc, ntyPointer, ntyOpenArray, ntyString, ntyCString, ntyForward, ntyInt, ntyInt8, ntyInt16, ntyInt32, ntyInt64, - ntyFloat, ntyFloat32, ntyFloat64, ntyFloat128 - TNimTypeKinds* = set[TNimrodTypeKind] - TNimrodSymKind* = enum + ntyFloat, ntyFloat32, ntyFloat64, ntyFloat128, + ntyUInt, ntyUInt8, ntyUInt16, ntyUInt32, ntyUInt64, + ntyBigNum, + ntyConst, ntyMutable, ntyVarargs, + ntyIter, + ntyError + + TNimTypeKinds* {.deprecated.} = set[NimTypeKind] + NimSymKind* = enum nskUnknown, nskConditional, nskDynLib, nskParam, nskGenericParam, nskTemp, nskModule, nskType, nskVar, nskLet, nskConst, nskResult, @@ -94,22 +100,28 @@ type nskEnumField, nskForVar, nskLabel, nskStub - TNimSymKinds* = set[TNimrodSymKind] + TNimSymKinds* {.deprecated.} = set[NimSymKind] type - TNimrodIdent* = object of RootObj - ## represents a Nimrod identifier in the AST + NimIdent* = object of RootObj + ## represents a Nim identifier in the AST - TNimrodSymbol {.final.} = object # hidden - PNimrodSymbol* {.compilerproc.} = ref TNimrodSymbol - ## represents a Nimrod *symbol* in the compiler; a *symbol* is a looked-up + NimSymObj = object # hidden + NimSym* = ref NimSymObj + ## represents a Nim *symbol* in the compiler; a *symbol* is a looked-up ## *ident*. +{.deprecated: [TNimrodNodeKind: NimNodeKind, TNimNodeKinds: NimNodeKinds, + TNimrodTypeKind: NimTypeKind, TNimrodSymKind: NimSymKind, + TNimrodIdent: NimIdent, PNimrodSymbol: NimSym].} + const nnkLiterals* = {nnkCharLit..nnkNilLit} nnkCallKinds* = {nnkCall, nnkInfix, nnkPrefix, nnkPostfix, nnkCommand, nnkCallStrLit} +{.push warning[deprecated]: off.} + proc `[]`*(n: PNimrodNode, i: int): PNimrodNode {.magic: "NChild", noSideEffect.} ## get `n`'s `i`'th child. @@ -117,31 +129,31 @@ proc `[]=`*(n: PNimrodNode, i: int, child: PNimrodNode) {.magic: "NSetChild", noSideEffect.} ## set `n`'s `i`'th child to `child`. -proc `!`*(s: string): TNimrodIdent {.magic: "StrToIdent", noSideEffect.} +proc `!`*(s: string): NimIdent {.magic: "StrToIdent", noSideEffect.} ## constructs an identifier from the string `s` -proc `$`*(i: TNimrodIdent): string {.magic: "IdentToStr", noSideEffect.} - ## converts a Nimrod identifier to a string +proc `$`*(i: NimIdent): string {.magic: "IdentToStr", noSideEffect.} + ## converts a Nim identifier to a string -proc `$`*(s: PNimrodSymbol): string {.magic: "IdentToStr", noSideEffect.} - ## converts a Nimrod symbol to a string +proc `$`*(s: NimSym): string {.magic: "IdentToStr", noSideEffect.} + ## converts a Nim symbol to a string -proc `==`*(a, b: TNimrodIdent): bool {.magic: "EqIdent", noSideEffect.} - ## compares two Nimrod identifiers +proc `==`*(a, b: NimIdent): bool {.magic: "EqIdent", noSideEffect.} + ## compares two Nim identifiers proc `==`*(a, b: PNimrodNode): bool {.magic: "EqNimrodNode", noSideEffect.} - ## compares two Nimrod nodes + ## compares two Nim nodes proc len*(n: PNimrodNode): int {.magic: "NLen", noSideEffect.} ## returns the number of children of `n`. proc add*(father, child: PNimrodNode): PNimrodNode {.magic: "NAdd", discardable, - noSideEffect.} + noSideEffect, locks: 0.} ## Adds the `child` to the `father` node. Returns the ## father node so that calls can be nested. proc add*(father: PNimrodNode, children: varargs[PNimrodNode]): PNimrodNode {. - magic: "NAddMultiple", discardable, noSideEffect.} + magic: "NAddMultiple", discardable, noSideEffect, locks: 0.} ## Adds each child of `children` to the `father` node. ## Returns the `father` node so that calls can be nested. @@ -153,15 +165,27 @@ proc kind*(n: PNimrodNode): TNimrodNodeKind {.magic: "NKind", noSideEffect.} proc intVal*(n: PNimrodNode): BiggestInt {.magic: "NIntVal", noSideEffect.} proc floatVal*(n: PNimrodNode): BiggestFloat {.magic: "NFloatVal", noSideEffect.} -proc symbol*(n: PNimrodNode): PNimrodSymbol {.magic: "NSymbol", noSideEffect.} -proc ident*(n: PNimrodNode): TNimrodIdent {.magic: "NIdent", noSideEffect.} -proc typ*(n: PNimrodNode): typedesc {.magic: "NGetType", noSideEffect.} +proc symbol*(n: PNimrodNode): NimSym {.magic: "NSymbol", noSideEffect.} +proc ident*(n: PNimrodNode): NimIdent {.magic: "NIdent", noSideEffect.} + +proc getType*(n: PNimrodNode): PNimrodNode {.magic: "NGetType", noSideEffect.} + ## with 'getType' you can access the node's `type`:idx:. A Nim type is + ## mapped to a Nim AST too, so it's slightly confusing but it means the same + ## API can be used to traverse types. Recursive types are flattened for you + ## so there is no danger of infinite recursions during traversal. To + ## resolve recursive types, you have to call 'getType' again. To see what + ## kind of type it is, call `typeKind` on getType's result. + +proc typeKind*(n: PNimrodNode): NimTypeKind {.magic: "NGetType", noSideEffect.} + ## Returns the type kind of the node 'n' that should represent a type, that + ## means the node should have been obtained via `getType`. + proc strVal*(n: PNimrodNode): string {.magic: "NStrVal", noSideEffect.} proc `intVal=`*(n: PNimrodNode, val: BiggestInt) {.magic: "NSetIntVal", noSideEffect.} proc `floatVal=`*(n: PNimrodNode, val: BiggestFloat) {.magic: "NSetFloatVal", noSideEffect.} -proc `symbol=`*(n: PNimrodNode, val: PNimrodSymbol) {.magic: "NSetSymbol", noSideEffect.} -proc `ident=`*(n: PNimrodNode, val: TNimrodIdent) {.magic: "NSetIdent", noSideEffect.} +proc `symbol=`*(n: PNimrodNode, val: NimSym) {.magic: "NSetSymbol", noSideEffect.} +proc `ident=`*(n: PNimrodNode, val: NimIdent) {.magic: "NSetIdent", noSideEffect.} #proc `typ=`*(n: PNimrodNode, typ: typedesc) {.magic: "NSetType".} # this is not sound! Unfortunately forbidding 'typ=' is not enough, as you # can easily do: @@ -177,13 +201,13 @@ proc newNimNode*(kind: TNimrodNodeKind, proc copyNimNode*(n: PNimrodNode): PNimrodNode {.magic: "NCopyNimNode", noSideEffect.} proc copyNimTree*(n: PNimrodNode): PNimrodNode {.magic: "NCopyNimTree", noSideEffect.} -proc error*(msg: string) {.magic: "NError", gcsafe.} +proc error*(msg: string) {.magic: "NError", benign.} ## writes an error message at compile time -proc warning*(msg: string) {.magic: "NWarning", gcsafe.} +proc warning*(msg: string) {.magic: "NWarning", benign.} ## writes a warning message at compile time -proc hint*(msg: string) {.magic: "NHint", gcsafe.} +proc hint*(msg: string) {.magic: "NHint", benign.} ## writes a hint message at compile time proc newStrLitNode*(s: string): PNimrodNode {.compileTime, noSideEffect.} = @@ -201,7 +225,7 @@ proc newFloatLitNode*(f: BiggestFloat): PNimrodNode {.compileTime.} = result = newNimNode(nnkFloatLit) result.floatVal = f -proc newIdentNode*(i: TNimrodIdent): PNimrodNode {.compileTime.} = +proc newIdentNode*(i: NimIdent): PNimrodNode {.compileTime.} = ## creates an identifier node from `i` result = newNimNode(nnkIdent) result.ident = i @@ -212,7 +236,7 @@ proc newIdentNode*(i: string): PNimrodNode {.compileTime.} = result.ident = !i type - TBindSymRule* = enum ## specifies how ``bindSym`` behaves + BindSymRule* = enum ## specifies how ``bindSym`` behaves brClosed, ## only the symbols in current scope are bound brOpen, ## open wrt overloaded symbols, but may be a single ## symbol if not ambiguous (the rules match that of @@ -221,7 +245,9 @@ type ## if not ambiguous (this cannot be achieved with ## any other means in the language currently) -proc bindSym*(ident: string, rule: TBindSymRule = brClosed): PNimrodNode {. +{.deprecated: [TBindSymRule: BindSymRule].} + +proc bindSym*(ident: string, rule: BindSymRule = brClosed): PNimrodNode {. magic: "NBindSym", noSideEffect.} ## creates a node that binds `ident` to a symbol node. The bound symbol ## may be an overloaded symbol. @@ -232,16 +258,16 @@ proc bindSym*(ident: string, rule: TBindSymRule = brClosed): PNimrodNode {. ## If ``rule == brForceOpen`` always an ``nkOpenSymChoice`` tree is ## returned even if the symbol is not ambiguous. -proc genSym*(kind: TNimrodSymKind = nskLet; ident = ""): PNimrodNode {. +proc genSym*(kind: NimSymKind = nskLet; ident = ""): PNimrodNode {. magic: "NGenSym", noSideEffect.} ## generates a fresh symbol that is guaranteed to be unique. The symbol ## needs to occur in a declaration context. -proc callsite*(): PNimrodNode {.magic: "NCallSite", gcsafe.} - ## returns the AST if the invokation expression that invoked this macro. +proc callsite*(): PNimrodNode {.magic: "NCallSite", benign.} + ## returns the AST of the invocation expression that invoked this macro. proc toStrLit*(n: PNimrodNode): PNimrodNode {.compileTime.} = - ## converts the AST `n` to the concrete Nimrod code and wraps that + ## converts the AST `n` to the concrete Nim code and wraps that ## in a string literal node return newStrLitNode(repr(n)) @@ -317,7 +343,7 @@ proc expectKind*(n: PNimrodNode, k: TNimrodNodeKind) {.compileTime.} = ## checks that `n` is of kind `k`. If this is not the case, ## compilation aborts with an error message. This is useful for writing ## macros that check the AST that is passed to them. - if n.kind != k: error("macro expects a node of kind: " & $k) + if n.kind != k: error("Expected a node of kind " & $k & ", got " & $n.kind) proc expectMinLen*(n: PNimrodNode, min: int) {.compileTime.} = ## checks that `n` has at least `min` children. If this is not the case, @@ -339,7 +365,7 @@ proc newCall*(theProc: PNimrodNode, result.add(theProc) result.add(args) -proc newCall*(theProc: TNimrodIdent, +proc newCall*(theProc: NimIdent, args: varargs[PNimrodNode]): PNimrodNode {.compileTime.} = ## produces a new call node. `theProc` is the proc that is called with ## the arguments ``args[0..]``. @@ -375,7 +401,7 @@ proc newLit*(s: string): PNimrodNode {.compileTime.} = result = newNimNode(nnkStrLit) result.strVal = s -proc nestList*(theProc: TNimrodIdent, +proc nestList*(theProc: NimIdent, x: PNimrodNode): PNimrodNode {.compileTime.} = ## nests the list `x` into a tree of call expressions: ## ``[a, b, c]`` is transformed into ``theProc(a, theProc(c, d))``. @@ -387,11 +413,11 @@ proc nestList*(theProc: TNimrodIdent, # This could easily user code and so should be fixed in evals.nim somehow. result = newCall(theProc, x[i], copyNimTree(result)) -proc treeRepr*(n: PNimrodNode): string {.compileTime.} = +proc treeRepr*(n: PNimrodNode): string {.compileTime, benign.} = ## Convert the AST `n` to a human-readable tree-like string. ## ## See also `repr` and `lispRepr`. - proc traverse(res: var string, level: int, n: PNimrodNode) = + proc traverse(res: var string, level: int, n: PNimrodNode) {.benign.} = for i in 0..level-1: res.add " " res.add(($n.kind).substr(3)) @@ -412,7 +438,7 @@ proc treeRepr*(n: PNimrodNode): string {.compileTime.} = result = "" traverse(result, 0, n) -proc lispRepr*(n: PNimrodNode): string {.compileTime.} = +proc lispRepr*(n: PNimrodNode): string {.compileTime, benign.} = ## Convert the AST `n` to a human-readable lisp-like string, ## ## See also `repr` and `treeRepr`. @@ -430,10 +456,11 @@ proc lispRepr*(n: PNimrodNode): string {.compileTime.} = of nnkSym: add(result, $n.symbol) of nnkNone: assert false else: - add(result, lispRepr(n[0])) - for j in 1..n.len-1: - add(result, ", ") - add(result, lispRepr(n[j])) + if n.len > 0: + add(result, lispRepr(n[0])) + for j in 1..n.len-1: + add(result, ", ") + add(result, lispRepr(n[j])) add(result, ")") @@ -554,10 +581,8 @@ const CallNodes* = {nnkCall, nnkInfix, nnkPrefix, nnkPostfix, nnkCommand, nnkCallStrLit, nnkHiddenCallConv} -from strutils import cmpIgnoreStyle, format - proc expectKind*(n: PNimrodNode; k: set[TNimrodNodeKind]) {.compileTime.} = - assert n.kind in k, "Expected one of $1, got $2".format(k, n.kind) + assert n.kind in k, "Expected one of " & $k & ", got " & $n.kind proc newProc*(name = newEmptyNode(); params: openArray[PNimrodNode] = [newEmptyNode()]; body: PNimrodNode = newStmtList(), procType = nnkProcDef): PNimrodNode {.compileTime.} = @@ -627,7 +652,7 @@ proc `pragma=`*(someProc: PNimrodNode; val: PNimrodNode){.compileTime.}= template badNodeKind(k; f): stmt{.immediate.} = - assert false, "Invalid node kind $# for macros.`$2`".format(k, f) + assert false, "Invalid node kind " & $k & " for macros.`" & $f & "`" proc body*(someProc: PNimrodNode): PNimrodNode {.compileTime.} = case someProc.kind: @@ -651,7 +676,7 @@ proc `body=`*(someProc: PNimrodNode, val: PNimrodNode) {.compileTime.} = else: badNodeKind someProc.kind, "body=" -proc basename*(a: PNimrodNode): PNimrodNode {.compiletime.} +proc basename*(a: PNimrodNode): PNimrodNode {.compiletime, benign.} proc `$`*(node: PNimrodNode): string {.compileTime.} = @@ -749,6 +774,22 @@ proc copy*(node: PNimrodNode): PNimrodNode {.compileTime.} = ## An alias for copyNimTree(). return node.copyNimTree() +proc cmpIgnoreStyle(a, b: cstring): int {.noSideEffect.} = + proc toLower(c: char): char {.inline.} = + if c in {'A'..'Z'}: result = chr(ord(c) + (ord('a') - ord('A'))) + else: result = c + var i = 0 + var j = 0 + while true: + while a[i] == '_': inc(i) + while b[j] == '_': inc(j) # BUGFIX: typo + var aa = toLower(a[i]) + var bb = toLower(b[j]) + result = ord(aa) - ord(bb) + if result != 0 or aa == '\0': break + inc(i) + inc(j) + proc eqIdent* (a, b: string): bool = cmpIgnoreStyle(a, b) == 0 ## Check if two idents are identical. @@ -784,3 +825,5 @@ when not defined(booting): macro payload: stmt {.gensym.} = result = parseStmt(e) payload() + +{.pop.} diff --git a/lib/core/typeinfo.nim b/lib/core/typeinfo.nim index 0046924a1b..c3ff665915 100644 --- a/lib/core/typeinfo.nim +++ b/lib/core/typeinfo.nim @@ -66,9 +66,9 @@ type ppointer = ptr pointer pbyteArray = ptr array[0.. 0xffff, int8] - TGenSeq = object + TGenericSeq {.importc.} = object len, space: int - PGenSeq = ptr TGenSeq + PGenSeq = ptr TGenericSeq const GenericSeqSize = (2 * sizeof(int)) diff --git a/lib/impure/db_mysql.nim b/lib/impure/db_mysql.nim index 968a2923ad..dab84c2d50 100644 --- a/lib/impure/db_mysql.nim +++ b/lib/impure/db_mysql.nim @@ -16,11 +16,11 @@ type TDbConn* = PMySQL ## encapsulates a database connection TRow* = seq[string] ## a row of a dataset. NULL database values will be ## transformed always to the empty string. - EDb* = object of EIO ## exception that is raised if a database error occurs + EDb* = object of IOError ## exception that is raised if a database error occurs TSqlQuery* = distinct string ## an SQL query string - FDb* = object of FIO ## effect that denotes a database operation + FDb* = object of IOEffect ## effect that denotes a database operation FReadDb* = object of FDb ## effect that denotes a read operation FWriteDb* = object of FDb ## effect that denotes a write operation diff --git a/lib/impure/rdstdin.nim b/lib/impure/rdstdin.nim index 07ef13fd97..aaf2ed1cac 100644 --- a/lib/impure/rdstdin.nim +++ b/lib/impure/rdstdin.nim @@ -1,7 +1,7 @@ # # # Nim's Runtime Library -# (c) Copyright 2012 Andreas Rumpf +# (c) Copyright 2015 Andreas Rumpf # # See the file "copying.txt", included in this # distribution, for details about the copyright. @@ -13,6 +13,8 @@ ## is used. This suffices because Windows' console already provides the ## wanted functionality. +{.deadCodeElim: on.} + when defined(Windows): proc readLineFromStdin*(prompt: string): TaintedString {. tags: [ReadIOEffect, WriteIOEffect].} = @@ -31,9 +33,31 @@ when defined(Windows): stdout.write(prompt) result = readLine(stdin, line) + proc readPasswordFromStdin*(prompt: string, password: var TaintedString): + bool {.tags: [ReadIOEffect, WriteIOEffect].} = + ## Reads a `password` from stdin without printing it. `password` must not + ## be ``nil``! Returns ``false`` if the end of the file has been reached, + ## ``true`` otherwise. + proc getch(): cint {.header: "", importc: "_getch".} + + password.setLen(0) + var c: char + stdout.write(prompt) + while true: + c = getch().char + case c + of '\r', chr(0xA): + break + of '\b': + password.setLen(password.len - 1) + else: + password.add(c) + stdout.write "\n" + # TODO: How to detect EOF on Windows? + else: - import readline, history - + import readline, history, termios, unsigned + proc readLineFromStdin*(prompt: string): TaintedString {. tags: [ReadIOEffect, WriteIOEffect].} = var buffer = readline.readLine(prompt) @@ -55,8 +79,26 @@ else: result = true # initialization: - # disable auto-complete: + # disable auto-complete: proc doNothing(a, b: cint): cint {.cdecl, procvar.} = discard - + discard readline.bind_key('\t'.ord, doNothing) + proc readPasswordFromStdin*(prompt: string, password: var TaintedString): + bool {.tags: [ReadIOEffect, WriteIOEffect].} = + password.setLen(0) + let fd = stdin.getFileHandle() + var cur, old: Termios + discard fd.tcgetattr(cur.addr) + old = cur + cur.lflag = cur.lflag and not Tcflag(ECHO) + discard fd.tcsetattr(TCSADRAIN, cur.addr) + stdout.write prompt + result = stdin.readLine(password) + stdout.write "\n" + discard fd.tcsetattr(TCSADRAIN, old.addr) + +proc readPasswordFromStdin*(prompt: string): TaintedString = + ## Reads a password from stdin without printing it. + result = TaintedString("") + discard readPasswordFromStdin(prompt, result) diff --git a/lib/impure/re.nim b/lib/impure/re.nim index a7bebb81c6..7d5ff8948b 100644 --- a/lib/impure/re.nim +++ b/lib/impure/re.nim @@ -9,6 +9,12 @@ ## Regular expression support for Nim. Consider using the pegs module ## instead. +## +## **Note:** The 're' proc defaults to the **extended regular expression +## syntax** which lets you use whitespace freely to make your regexes readable. +## However, this means to match whitespace ``\s`` or something similar has +## to be used. +## ## This module is implemented by providing a wrapper around the ## `PRCE (Perl-Compatible Regular Expressions) `_ ## C library. This means that your application will depend on the PRCE diff --git a/lib/js/dom.nim b/lib/js/dom.nim index 91b260a642..870213db3e 100644 --- a/lib/js/dom.nim +++ b/lib/js/dom.nim @@ -55,6 +55,7 @@ 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.} @@ -91,6 +92,7 @@ type TFrame* {.importc.} = object of TWindow TDocument* {.importc.} = object of TEventHandlers + addEventListener*: proc(ev: cstring, cb: proc(ev: ref TEvent) ) {.nimcall.} alinkColor*: cstring bgColor*: cstring charset*: cstring @@ -110,6 +112,7 @@ type getElementById*: proc (id: cstring): ref TNode {.nimcall.} getElementsByName*: proc (name: cstring): seq[ref TNode] {.nimcall.} getElementsByTagName*: proc (name: cstring): seq[ref TNode] {.nimcall.} + getElementsByClassName*: proc (name: cstring): seq[ref TNode] {.nimcall.} getSelection*: proc (): cstring {.nimcall.} handleEvent*: proc (event: ref TEvent) {.nimcall.} open*: proc () {.nimcall.} @@ -196,6 +199,12 @@ type 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, @@ -212,6 +221,8 @@ type 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 @@ -223,20 +234,23 @@ type previousSibling*: ref TNode appendChild*: proc (child: ref TNode) {.nimcall.} appendData*: proc (data: cstring) {.nimcall.} - cloneNode*: proc (copyContent: bool) {.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 (): seq[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 @@ -336,6 +350,7 @@ type setAttribute*: proc (attr, value: cstring, caseSensitive=false) {.nimcall.} TEvent* {.importc.} = object of RootObj + target*: ref TNode altKey*, ctrlKey*, shiftKey*: bool button*: int clientX*, clientY*: int @@ -450,3 +465,4 @@ proc isFinite*(x: BiggestFloat): bool {.importc, nodecl.} 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.} diff --git a/lib/nimbase.h b/lib/nimbase.h index b72e60ac24..50c7968acb 100644 --- a/lib/nimbase.h +++ b/lib/nimbase.h @@ -379,7 +379,7 @@ static inline void GCGuard (void *ptr) { asm volatile ("" :: "X" (ptr)); } # define GC_GUARD #endif -/* Test to see if nimrod and the C compiler agree on the size of a pointer. +/* Test to see if Nim and the C compiler agree on the size of a pointer. On disagreement, your C compiler will say something like: "error: 'assert_numbits' declared as an array with a negative size" */ typedef int assert_numbits[sizeof(NI) == sizeof(void*) && NIM_INTBITS == sizeof(NI)*8 ? 1 : -1]; @@ -390,3 +390,12 @@ typedef int assert_numbits[sizeof(NI) == sizeof(void*) && NIM_INTBITS == sizeof( #else # define NIM_EXTERNC #endif + +/* ---------------- platform specific includes ----------------------- */ + +/* VxWorks related includes */ +#if defined(__VXWORKS__) +# include +# include +# include +#endif diff --git a/lib/nimrtl.nimrod.cfg b/lib/nimrtl.nim.cfg similarity index 100% rename from lib/nimrtl.nimrod.cfg rename to lib/nimrtl.nim.cfg diff --git a/lib/packages/docutils/rstgen.nim b/lib/packages/docutils/rstgen.nim index 14614b2edb..e1e5908774 100644 --- a/lib/packages/docutils/rstgen.nim +++ b/lib/packages/docutils/rstgen.nim @@ -1145,7 +1145,7 @@ proc formatNamedVars*(frmt: string, varnames: openArray[string], proc defaultConfig*(): StringTableRef = ## Returns a default configuration for embedded HTML generation. ## - ## The returned ``StringTableRef`` contains the paramters used by the HTML + ## The returned ``StringTableRef`` contains the parameters used by the HTML ## engine to build the final output. For information on what these parameters ## are and their purpose, please look up the file ``config/nimdoc.cfg`` ## bundled with the compiler. diff --git a/lib/posix/posix.nim b/lib/posix/posix.nim index fbd07ca25e..7d3e3ddbaa 100644 --- a/lib/posix/posix.nim +++ b/lib/posix/posix.nim @@ -70,17 +70,20 @@ const STDIN_FILENO* = 0 ## File number of stdin; STDOUT_FILENO* = 1 ## File number of stdout; -when defined(endb): - # to not break bootstrapping again ... - type - TDIR* {.importc: "DIR", header: "", - final, pure, incompleteStruct.} = object - ## A type representing a directory stream. -else: - type - TDIR* {.importc: "DIR", header: "", - final, pure.} = object - ## A type representing a directory stream. + DT_UNKNOWN* = 0 ## Unknown file type. + DT_FIFO* = 1 ## Named pipe, or FIFO. + DT_CHR* = 2 ## Character device. + DT_DIR* = 4 ## Directory. + DT_BLK* = 6 ## Block device. + DT_REG* = 8 ## Regular file. + DT_LNK* = 10 ## Symbolic link. + DT_SOCK* = 12 ## UNIX domain socket. + DT_WHT* = 14 + +type + TDIR* {.importc: "DIR", header: "", + incompleteStruct.} = object + ## A type representing a directory stream. type SocketHandle* = distinct cint # The type used to represent socket descriptors @@ -91,6 +94,10 @@ type Tdirent* {.importc: "struct dirent", header: "", final, pure.} = object ## dirent_t struct d_ino*: Tino ## File serial number. + 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) d_name*: array [0..255, char] ## Name of entry. Tflock* {.importc: "struct flock", final, pure, @@ -1739,12 +1746,10 @@ when hasSpawnH: when defined(linux): # better be safe than sorry; Linux has this flag, macosx doesn't, don't # know about the other OSes - when defined(tcc): - # TCC doesn't define __USE_GNU, so we can't get the magic number from - # spawn.h - const POSIX_SPAWN_USEVFORK* = cint(0x40) - else: - var POSIX_SPAWN_USEVFORK* {.importc, header: "".}: cint + + # Non-GNU systems like TCC and musl-libc don't define __USE_GNU, so we + # can't get the magic number from spawn.h + const POSIX_SPAWN_USEVFORK* = cint(0x40) else: # macosx lacks this, so we define the constant to be 0 to not affect # OR'ing of flags: diff --git a/lib/posix/termios.nim b/lib/posix/termios.nim new file mode 100644 index 0000000000..830e8a2076 --- /dev/null +++ b/lib/posix/termios.nim @@ -0,0 +1,264 @@ +# +# +# Nim's Runtime Library +# (c) Copyright 2015 Andreas Rumpf +# +# See the file "copying.txt", included in this +# distribution, for details about the copyright. +# + +{.deadCodeElim: on.} +import posix + +type + Speed* = cuint + Tcflag* = cuint + +const + NCCS* = 32 + +type + Termios* = object {.importc: "struct termios", header: "", 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 + VINTR* = 0 + VQUIT* = 1 + VERASE* = 2 + VKILL* = 3 + VEOF* = 4 + VTIME* = 5 + VMIN* = 6 + VSWTC* = 7 + VSTART* = 8 + VSTOP* = 9 + VSUSP* = 10 + VEOL* = 11 + VREPRINT* = 12 + VDISCARD* = 13 + VWERASE* = 14 + VLNEXT* = 15 + VEOL2* = 16 + +# iflag bits + +const + IGNBRK* = 1 + BRKINT* = 2 + IGNPAR* = 4 + PARMRK* = 10 + INPCK* = 20 + ISTRIP* = 40 + INLCR* = 100 + IGNCR* = 200 + ICRNL* = 400 + IUCLC* = 1000 + IXON* = 2000 + IXANY* = 4000 + IXOFF* = 10000 + IMAXBEL* = 20000 + IUTF8* = 40000 + +# oflag bits + +const + OPOST* = 1 + OLCUC* = 2 + ONLCR* = 4 + OCRNL* = 10 + ONOCR* = 20 + ONLRET* = 40 + OFILL* = 100 + OFDEL* = 200 + NLDLY* = 400 + NL0* = 0 + NL1* = 400 + CRDLY* = 3000 + CR0* = 0 + CR1* = 1000 + CR2* = 2000 + CR3* = 3000 + TABDLY* = 14000 + TAB0* = 0 + TAB1* = 4000 + TAB2* = 10000 + TAB3* = 14000 + BSDLY* = 20000 + BS0* = 0 + BS1* = 20000 + FFDLY* = 0o000000100000 + FF0* = 0 + FF1* = 0o000000100000 + VTDLY* = 40000 + VT0* = 0 + VT1* = 40000 + XTABS* = 14000 + +# cflag bit meaning + +const + CBAUD* = 10017 + B0* = 0 + B50* = 1 + B75* = 2 + B110* = 3 + B134* = 4 + B150* = 5 + B200* = 6 + B300* = 7 + B600* = 10 + B1200* = 11 + B1800* = 12 + B2400* = 13 + B4800* = 14 + B9600* = 15 + B19200* = 16 + B38400* = 17 + EXTA* = B19200 + EXTB* = B38400 + CSIZE* = 60 + CS5* = 0 + CS6* = 20 + CS7* = 40 + CS8* = 60 + CSTOPB* = 100 + CREAD* = 200 + PARENB* = 400 + PARODD* = 1000 + HUPCL* = 2000 + CLOCAL* = 4000 + CBAUDEX* = 10000 + B57600* = 10001 + B115200* = 10002 + B230400* = 10003 + B460800* = 10004 + B500000* = 10005 + B576000* = 10006 + B921600* = 10007 + B1000000* = 10010 + B1152000* = 10011 + B1500000* = 10012 + B2000000* = 10013 + B2500000* = 10014 + B3000000* = 10015 + B3500000* = 10016 + B4000000* = 10017 + MAX_BAUD* = B4000000 + CIBAUD* = 2003600000 + CMSPAR* = 0o010000000000 + CRTSCTS* = 0o020000000000 + +# lflag bits + +const + ISIG* = 1 + ICANON* = 2 + XCASE* = 4 + ECHO* = 10 + ECHOE* = 20 + ECHOK* = 40 + ECHONL* = 100 + NOFLSH* = 200 + TOSTOP* = 400 + ECHOCTL* = 1000 + ECHOPRT* = 2000 + ECHOKE* = 4000 + FLUSHO* = 10000 + PENDIN* = 40000 + IEXTEN* = 0o000000100000 + EXTPROC* = 0o000000200000 + +# tcflow() and TCXONC use these + +const + TCOOFF* = 0 + TCOON* = 1 + TCIOFF* = 2 + TCION* = 3 + +# tcflush() and TCFLSH use these + +const + TCIFLUSH* = 0 + TCOFLUSH* = 1 + TCIOFLUSH* = 2 + +# tcsetattr uses these + +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. + +template cceq*(val, c: expr): expr = + c == val and val != POSIX_VDISABLE + +# Return the output baud rate stored in *TERMIOS_P. + +proc cfGetOspeed*(termios: ptr Termios): Speed {.importc: "cfgetospeed", + header: "".} +# Return the input baud rate stored in *TERMIOS_P. + +proc cfGetIspeed*(termios: ptr Termios): Speed {.importc: "cfgetispeed", + header: "".} +# Set the output baud rate stored in *TERMIOS_P to SPEED. + +proc cfSetOspeed*(termios: ptr Termios; speed: Speed): cint {. + importc: "cfsetospeed", header: "".} +# Set the input baud rate stored in *TERMIOS_P to SPEED. + +proc cfSetIspeed*(termios: ptr Termios; speed: Speed): cint {. + importc: "cfsetispeed", header: "".} +# Set both the input and output baud rates in *TERMIOS_OP to SPEED. + +proc cfSetSpeed*(termios: ptr Termios; speed: Speed): cint {. + importc: "cfsetspeed", header: "".} +# Put the state of FD into *TERMIOS_P. + +proc tcGetAttr*(fd: cint; termios: ptr Termios): cint {. + importc: "tcgetattr", header: "".} +# Set the state of FD to *TERMIOS_P. +# Values for OPTIONAL_ACTIONS (TCSA*) are in . + +proc tcSetAttr*(fd: cint; optional_actions: cint; termios: ptr Termios): cint {. + importc: "tcsetattr", header: "".} +# Set *TERMIOS_P to indicate raw mode. + +proc cfMakeRaw*(termios: ptr Termios) {.importc: "cfmakeraw", + header: "".} +# Send zero bits on FD. + +proc tcSendBreak*(fd: cint; duration: cint): cint {.importc: "tcsendbreak", + header: "".} +# 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: "".} +# Flush pending data on FD. +# Values for QUEUE_SELECTOR (TC{I,O,IO}FLUSH) are in . + +proc tcFlush*(fd: cint; queue_selector: cint): cint {.importc: "tcflush", + header: "".} +# Suspend or restart transmission on FD. +# Values for ACTION (TC[IO]{OFF,ON}) are in . + +proc tcFlow*(fd: cint; action: cint): cint {.importc: "tcflow", + header: "".} +# Get process group ID for session leader for controlling terminal FD. + +proc tcGetSid*(fd: cint): TPid {.importc: "tcgetsid", header: "".} diff --git a/lib/pure/actors.nim b/lib/pure/actors.nim index f2c50ce4cb..8c61ce7df3 100644 --- a/lib/pure/actors.nim +++ b/lib/pure/actors.nim @@ -200,7 +200,7 @@ template schedule = if minIdx >= 0: p.actors[minIdx].i.send(t) else: - raise newException(EDeadThread, "cannot send message; thread died") + raise newException(DeadThreadError, "cannot send message; thread died") proc spawn*[TIn, TOut](p: var TActorPool[TIn, TOut], input: TIn, action: proc (input: TIn): TOut {.thread.} diff --git a/lib/pure/algorithm.nim b/lib/pure/algorithm.nim index 0358a9a819..20bfc5c7c7 100644 --- a/lib/pure/algorithm.nim +++ b/lib/pure/algorithm.nim @@ -220,3 +220,62 @@ proc product*[T](x: openArray[seq[T]]): seq[seq[T]] = result.add(res) index = 0 indexes[index] -=1 + +proc nextPermutation*[T](x: var openarray[T]): bool {.discardable.} = + ## Calculates the next lexicographic permutation, directly modifying ``x``. + ## The result is whether a permutation happened, otherwise we have reached + ## the last-ordered permutation. + ## + ## .. code-block:: nim + ## + ## var v = @[0, 1, 2, 3, 4, 5, 6, 7, 8, 9] + ## v.nextPermutation() + ## echo v + if x.len < 2: + return false + + var i = x.high + while i > 0 and x[i-1] >= x[i]: + dec i + + if i == 0: + return false + + var j = x.high + while j >= i and x[j] <= x[i-1]: + dec j + + swap x[j], x[i-1] + x.reverse(i, x.high) + + result = true + +proc prevPermutation*[T](x: var openarray[T]): bool {.discardable.} = + ## Calculates the previous lexicographic permutation, directly modifying + ## ``x``. The result is whether a permutation happened, otherwise we have + ## reached the first-ordered permutation. + ## + ## .. code-block:: nim + ## + ## var v = @[0, 1, 2, 3, 4, 5, 6, 7, 9, 8] + ## v.prevPermutation() + ## echo v + if x.len < 2: + return false + + var i = x.high + while i > 0 and x[i-1] <= x[i]: + dec i + + if i == 0: + return false + + x.reverse(i, x.high) + + var j = x.high + while j >= i and x[j-1] < x[i-1]: + dec j + + swap x[i-1], x[j] + + result = true diff --git a/lib/pure/asyncdispatch.nim b/lib/pure/asyncdispatch.nim index bbd8ed8958..d6ed660307 100644 --- a/lib/pure/asyncdispatch.nim +++ b/lib/pure/asyncdispatch.nim @@ -606,7 +606,7 @@ when defined(windows) or defined(nimdoc): 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 immediatelly when it was disconnected, even when there is still + # 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 @@ -1064,6 +1064,17 @@ proc accept*(socket: TAsyncFD, # -- Await Macro +proc skipUntilStmtList(node: PNimrodNode): PNimrodNode {.compileTime.} = + # Skips a nest of StmtList's. + result = node + if node[0].kind == nnkStmtList: + result = skipUntilStmtList(node[0]) + +proc skipStmtList(node: PNimrodNode): PNimrodNode {.compileTime.} = + result = node + if node[0].kind == nnkStmtList: + result = node[0] + template createCb(retFutureSym, iteratorNameSym, name: expr): stmt {.immediate.} = var nameIterVar = iteratorNameSym @@ -1211,26 +1222,53 @@ proc processBody(node, retFutureSym: PNimrodNode, of nnkTryStmt: # try: await x; except: ... result = newNimNode(nnkStmtList, node) + template wrapInTry(n, tryBody: PNimrodNode) = + var temp = n + n[0] = tryBody + tryBody = temp + + # Transform ``except`` body. + # TODO: Could we perform some ``await`` transformation here to get it + # working in ``except``? + tryBody[1] = processBody(n[1], retFutureSym, subTypeIsVoid, nil) + proc processForTry(n: PNimrodNode, i: var int, res: PNimrodNode): bool {.compileTime.} = + ## Transforms the body of the tryStmt. Does not transform the + ## body in ``except``. + ## Returns true if the tryStmt node was transformed into an ifStmt. result = false - while i < n[0].len: - var processed = processBody(n[0][i], retFutureSym, subTypeIsVoid, n) - if processed.kind != n[0][i].kind or processed.len != n[0][i].len: + var skipped = n.skipStmtList() + while i < skipped.len: + var processed = processBody(skipped[i], retFutureSym, + subTypeIsVoid, n) + + # Check if we transformed the node into an exception check. + # This suggests skipped[i] contains ``await``. + if processed.kind != skipped[i].kind or processed.len != skipped[i].len: + processed = processed.skipUntilStmtList() expectKind(processed, nnkStmtList) expectKind(processed[2][1], nnkElse) i.inc - discard processForTry(n, i, processed[2][1][0]) + + if not processForTry(n, i, processed[2][1][0]): + # We need to wrap the nnkElse nodes back into a tryStmt. + # As they are executed if an exception does not happen + # inside the awaited future. + # The following code will wrap the nodes inside the + # original tryStmt. + wrapInTry(n, processed[2][1][0]) + res.add processed result = true else: - res.add n[0][i] + res.add skipped[i] i.inc var i = 0 if not processForTry(node, i, result): - var temp = node - temp[0] = result - result = temp + # If the tryStmt hasn't been transformed we can just put the body + # back into it. + wrapInTry(node, result) return else: discard @@ -1329,8 +1367,8 @@ macro async*(prc: stmt): stmt {.immediate.} = result[6] = outerProcBody #echo(treeRepr(result)) - #if prc[0].getName == "catch": - # echo(toStrLit(result)) + if prc[0].getName == "test3": + 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/asyncdispatch.nimrod.cfg b/lib/pure/asyncdispatch.nim.cfg similarity index 100% rename from lib/pure/asyncdispatch.nimrod.cfg rename to lib/pure/asyncdispatch.nim.cfg diff --git a/lib/pure/asyncfile.nim b/lib/pure/asyncfile.nim index f75b6fc438..752c01eac2 100644 --- a/lib/pure/asyncfile.nim +++ b/lib/pure/asyncfile.nim @@ -35,7 +35,7 @@ type offset: int64 when defined(windows): - proc getDesiredAccess(mode: TFileMode): int32 = + proc getDesiredAccess(mode: FileMode): int32 = case mode of fmRead: result = GENERIC_READ @@ -44,7 +44,7 @@ when defined(windows): of fmReadWrite, fmReadWriteExisting: result = GENERIC_READ or GENERIC_WRITE - proc getCreationDisposition(mode: TFileMode, filename: string): int32 = + proc getCreationDisposition(mode: FileMode, filename: string): int32 = case mode of fmRead, fmReadWriteExisting: OPEN_EXISTING @@ -54,7 +54,7 @@ when defined(windows): else: CREATE_NEW else: - proc getPosixFlags(mode: TFileMode): cint = + proc getPosixFlags(mode: FileMode): cint = case mode of fmRead: result = O_RDONLY @@ -74,7 +74,7 @@ proc getFileSize(f: AsyncFile): int64 = var high: DWord let low = getFileSize(f.fd.THandle, addr high) if low == INVALID_FILE_SIZE: - raiseOSError() + raiseOSError(osLastError()) return (high shl 32) or low proc openAsync*(filename: string, mode = fmRead): AsyncFile = @@ -95,7 +95,7 @@ proc openAsync*(filename: string, mode = fmRead): AsyncFile = nil, creationDisposition, flags, 0).TAsyncFd if result.fd.THandle == INVALID_HANDLE_VALUE: - raiseOSError() + raiseOSError(osLastError()) register(result.fd) @@ -108,7 +108,7 @@ proc openAsync*(filename: string, mode = fmRead): AsyncFile = let perm = S_IRUSR or S_IWUSR or S_IRGRP or S_IWGRP or S_IROTH result.fd = open(filename, flags, perm).TAsyncFD if result.fd.cint == -1: - raiseOSError() + raiseOSError(osLastError()) register(result.fd) @@ -185,7 +185,7 @@ proc read*(f: AsyncFile, size: int): Future[string] = if res < 0: let lastError = osLastError() if lastError.int32 != EAGAIN: - retFuture.fail(newException(EOS, osErrorMsg(lastError))) + retFuture.fail(newException(OSError, osErrorMsg(lastError))) else: result = false # We still want this callback to be called. elif res == 0: @@ -227,7 +227,7 @@ proc setFilePos*(f: AsyncFile, pos: int64) = when not defined(windows): let ret = lseek(f.fd.cint, pos, SEEK_SET) if ret == -1: - raiseOSError() + raiseOSError(osLastError()) proc readAll*(f: AsyncFile): Future[string] {.async.} = ## Reads all data from the specified file. @@ -299,7 +299,7 @@ proc write*(f: AsyncFile, data: string): Future[void] = if res < 0: let lastError = osLastError() if lastError.int32 != EAGAIN: - retFuture.fail(newException(EOS, osErrorMsg(lastError))) + retFuture.fail(newException(OSError, osErrorMsg(lastError))) else: result = false # We still want this callback to be called. else: @@ -318,8 +318,8 @@ proc close*(f: AsyncFile) = ## Closes the file specified. when defined(windows): if not closeHandle(f.fd.THandle).bool: - raiseOSError() + raiseOSError(osLastError()) else: if close(f.fd.cint) == -1: - raiseOSError() + raiseOSError(osLastError()) diff --git a/lib/pure/asyncftpclient.nim b/lib/pure/asyncftpclient.nim index 48a6c59b96..8558ad10d3 100644 --- a/lib/pure/asyncftpclient.nim +++ b/lib/pure/asyncftpclient.nim @@ -149,7 +149,7 @@ proc createDir*(ftp: AsyncFtpClient, dir: string, recursive = false){.async.} = assertReply reply, "257" proc chmod*(ftp: AsyncFtpClient, path: string, - permissions: set[TFilePermission]) {.async.} = + permissions: set[FilePermission]) {.async.} = ## Changes permission of ``path`` to ``permissions``. var userOctal = 0 var groupOctal = 0 @@ -188,7 +188,7 @@ proc retrText*(ftp: AsyncFtpClient, file: string): Future[string] {.async.} = result = await ftp.getLines() -proc getFile(ftp: AsyncFtpClient, file: TFile, total: BiggestInt, +proc getFile(ftp: AsyncFtpClient, file: File, total: BiggestInt, onProgressChanged: ProgressChangedProc) {.async.} = assert ftp.dsockConnected var progress = 0 @@ -240,7 +240,7 @@ proc retrFile*(ftp: AsyncFtpClient, file, dest: string, await getFile(ftp, destFile, fileSize, onProgressChanged) -proc doUpload(ftp: AsyncFtpClient, file: TFile, +proc doUpload(ftp: AsyncFtpClient, file: File, onProgressChanged: ProgressChangedProc) {.async.} = assert ftp.dsockConnected diff --git a/lib/pure/asyncio.nim b/lib/pure/asyncio.nim index d40c3849e9..6a7cbe396d 100644 --- a/lib/pure/asyncio.nim +++ b/lib/pure/asyncio.nim @@ -675,7 +675,7 @@ when isMainModule: echo(data) echo("Finished reading! " & $no) - proc testAccept(s: AsyncSocket, disp: PDispatcher, no: int) = + proc testAccept(s: AsyncSocket, disp: Dispatcher, no: int) = echo("Accepting client! " & $no) var client: AsyncSocket new(client) @@ -691,7 +691,7 @@ when isMainModule: var d = newDispatcher() var s = asyncSocket() - s.connect("amber.tenthbit.net", TPort(6667)) + s.connect("amber.tenthbit.net", Port(6667)) s.handleConnect = proc (s: AsyncSocket) = testConnect(s, 1) @@ -704,7 +704,7 @@ when isMainModule: server.handleAccept = proc (s: AsyncSocket) = testAccept(s, d, 78) - server.bindAddr(TPort(5555)) + server.bindAddr(Port(5555)) server.listen() d.register(server) diff --git a/lib/pure/basic2d.nim b/lib/pure/basic2d.nim index f2fc1566b2..a344cd053f 100644 --- a/lib/pure/basic2d.nim +++ b/lib/pure/basic2d.nim @@ -18,7 +18,7 @@ import strutils ## ## Quick start example: ## -## # Create a matrix wich first rotates, then scales and at last translates +## # Create a matrix which first rotates, then scales and at last translates ## ## var m:TMatrix2d=rotate(DEG90) & scale(2.0) & move(100.0,200.0) ## @@ -256,7 +256,7 @@ proc `$`* (t:TMatrix2d):string {.noInit.} = proc isUniform*(t:TMatrix2d,tol=1.0e-6):bool= ## Checks if the transform is uniform, that is - ## perpendicular axes of equal lenght, which means (for example) + ## perpendicular axes of equal length, which means (for example) ## it cannot transform a circle into an ellipse. ## `tol` is used as tolerance for both equal length comparison ## and perp. comparison. @@ -305,7 +305,7 @@ proc equals*(m1:TMatrix2d,m2:TMatrix2d,tol=1.0e-6):bool= abs(m1.ty-m2.ty)<=tol proc `=~`*(m1,m2:TMatrix2d):bool= - ## Checks if `m1`and `m2` is aproximately equal, using a + ## Checks if `m1`and `m2` is approximately equal, using a ## tolerance of 1e-6. equals(m1,m2) diff --git a/lib/pure/basic3d.nim b/lib/pure/basic3d.nim index c00764fc5b..18ebed67b2 100644 --- a/lib/pure/basic3d.nim +++ b/lib/pure/basic3d.nim @@ -23,7 +23,7 @@ import times ## ## Quick start example: ## -## # Create a matrix wich first rotates, then scales and at last translates +## # 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) ## @@ -320,7 +320,7 @@ proc rotateZ*(angle:float):TMatrix3d {.noInit.}= proc isUniform*(m:TMatrix3d,tol=1.0e-6):bool= ## Checks if the transform is uniform, that is - ## perpendicular axes of equal lenght, which means (for example) + ## 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 ## and perpendicular comparison. @@ -483,7 +483,7 @@ proc equals*(m1:TMatrix3d,m2:TMatrix3d,tol=1.0e-6):bool= abs(m1.tw-m2.tw)<=tol proc `=~`*(m1,m2:TMatrix3d):bool= - ## Checks if `m1` and `m2` is aproximately equal, using a + ## Checks if `m1` and `m2` is approximately equal, using a ## tolerance of 1e-6. equals(m1,m2) @@ -788,7 +788,7 @@ proc angleTo*(v1,v2:TVector3d):float= proc arbitraryAxis*(norm:TVector3d):TMatrix3d {.noInit.}= ## Computes the rotation matrix that would transform ## world z vector into `norm`. The inverse of this matrix - ## is useful to tranform a planar 3d object to 2d space. + ## is useful to transform a planar 3d object to 2d space. ## This is the same algorithm used to interpret DXF and DWG files. const lim=1.0/64.0 var ax,ay,az:TVector3d @@ -812,7 +812,7 @@ 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, - ## an arbitrary normalized vector `v1`is returned. + ## an arbitrary normalized vector `v1` is returned. var vmag1=v1.len vmag2=v2.len diff --git a/lib/pure/browsers.nim b/lib/pure/browsers.nim index 52035ee488..c6a6033181 100644 --- a/lib/pure/browsers.nim +++ b/lib/pure/browsers.nim @@ -42,7 +42,7 @@ proc openDefaultBrowser*(url: string) = for b in getEnv("BROWSER").string.split(PathSep): try: # we use ``startProcess`` here because we don't want to block! - discard startProcess(command=b, args=[url], options={poUseShell}) + discard startProcess(command=b, args=[url], options={poUsePath}) return except OSError: discard diff --git a/lib/pure/collections/LockFreeHash.nim b/lib/pure/collections/LockFreeHash.nim index 5640838b1c..c3954468ac 100644 --- a/lib/pure/collections/LockFreeHash.nim +++ b/lib/pure/collections/LockFreeHash.nim @@ -404,7 +404,7 @@ proc setVal[K,V](table: var PConcTable[K,V], key: int, val: int, #echo("tomb old slot then set in new table") nextTable = copySlotAndCheck(table,idx) return setVal(nextTable, key, val, expVal, match) - # Finaly ready to add new val to table + # Finally ready to add new val to table while true: if match and oldVal != expVal: #echo("set failed, no match oldVal= " & $oldVal & " expVal= " & $expVal) diff --git a/lib/pure/collections/critbits.nim b/lib/pure/collections/critbits.nim index 06babc6bbe..3d10e39aa3 100644 --- a/lib/pure/collections/critbits.nim +++ b/lib/pure/collections/critbits.nim @@ -174,7 +174,7 @@ proc excl*[T](c: var CritBitTree[T], key: string) = iterator leaves[T](n: Node[T]): Node[T] = if n != nil: # XXX actually we could compute the necessary stack size in advance: - # it's rougly log2(c.count). + # it's roughly log2(c.count). var stack = @[n] while stack.len > 0: var it = stack.pop diff --git a/lib/pure/collections/intsets.nim b/lib/pure/collections/intsets.nim index b46e040caa..7520e6e463 100644 --- a/lib/pure/collections/intsets.nim +++ b/lib/pure/collections/intsets.nim @@ -191,9 +191,10 @@ proc `$`*(s: IntSet): string = ## The `$` operator for int sets. dollarImpl() -proc empty*(s: IntSet): bool {.inline.} = +proc empty*(s: IntSet): bool {.inline, deprecated.} = ## returns true if `s` is empty. This is safe to call even before - ## the set has been initialized with `initIntSet`. + ## the set has been initialized with `initIntSet`. Note this never + ## worked reliably and so is deprecated. result = s.counter == 0 when isMainModule: diff --git a/lib/pure/collections/sequtils.nim b/lib/pure/collections/sequtils.nim index befc9bacb5..e690e8ebaa 100644 --- a/lib/pure/collections/sequtils.nim +++ b/lib/pure/collections/sequtils.nim @@ -320,7 +320,7 @@ template foldl*(sequence, operation: expr): expr = ## ## The ``operation`` parameter should be an expression which uses the ## variables ``a`` and ``b`` for each step of the fold. Since this is a left - ## fold, for non associative binary operations like substraction think that + ## fold, for non associative binary operations like subtraction think that ## the sequence of numbers 1, 2 and 3 will be parenthesized as (((1) - 2) - ## 3). Example: ## @@ -328,12 +328,12 @@ template foldl*(sequence, operation: expr): expr = ## let ## numbers = @[5, 9, 11] ## addition = foldl(numbers, a + b) - ## substraction = foldl(numbers, a - b) + ## subtraction = foldl(numbers, a - b) ## multiplication = foldl(numbers, a * b) ## words = @["nim", "is", "cool"] ## concatenation = foldl(words, a & b) ## assert addition == 25, "Addition is (((5)+9)+11)" - ## assert substraction == -15, "Substraction is (((5)-9)-11)" + ## assert subtraction == -15, "Subtraction is (((5)-9)-11)" ## assert multiplication == 495, "Multiplication is (((5)*9)*11)" ## assert concatenation == "nimiscool" assert sequence.len > 0, "Can't fold empty sequences" @@ -356,7 +356,7 @@ template foldr*(sequence, operation: expr): expr = ## ## The ``operation`` parameter should be an expression which uses the ## variables ``a`` and ``b`` for each step of the fold. Since this is a right - ## fold, for non associative binary operations like substraction think that + ## fold, for non associative binary operations like subtraction think that ## the sequence of numbers 1, 2 and 3 will be parenthesized as (1 - (2 - ## (3))). Example: ## @@ -364,12 +364,12 @@ template foldr*(sequence, operation: expr): expr = ## let ## numbers = @[5, 9, 11] ## addition = foldr(numbers, a + b) - ## substraction = foldr(numbers, a - b) + ## subtraction = foldr(numbers, a - b) ## multiplication = foldr(numbers, a * b) ## words = @["nim", "is", "cool"] ## concatenation = foldr(words, a & b) ## assert addition == 25, "Addition is (5+(9+(11)))" - ## assert substraction == 7, "Substraction is (5-(9-(11)))" + ## assert subtraction == 7, "Subtraction is (5-(9-(11)))" ## assert multiplication == 495, "Multiplication is (5*(9*(11)))" ## assert concatenation == "nimiscool" assert sequence.len > 0, "Can't fold empty sequences" @@ -382,7 +382,7 @@ template foldr*(sequence, operation: expr): expr = result = operation result -template mapIt*(seq1, typ, pred: expr): expr = +template mapIt*(seq1, typ, op: expr): expr = ## Convenience template around the ``map`` proc to reduce typing. ## ## The template injects the ``it`` variable which you can use directly in an @@ -397,10 +397,10 @@ template mapIt*(seq1, typ, pred: expr): expr = ## assert strings == @["4", "8", "12", "16"] var result {.gensym.}: seq[typ] = @[] for it {.inject.} in items(seq1): - result.add(pred) + result.add(op) result -template mapIt*(varSeq, pred: expr) = +template mapIt*(varSeq, op: expr) = ## Convenience template around the mutable ``map`` proc to reduce typing. ## ## The template injects the ``it`` variable which you can use directly in an @@ -413,7 +413,7 @@ template mapIt*(varSeq, pred: expr) = ## assert nums[0] + nums[3] == 15 for i in 0 .. `_. ## @@ -94,7 +105,7 @@ iterator items*[A](s: HashSet[A]): A = ## # --> {(a: 1, b: 3), (a: 0, b: 4)} assert s.isValid, "The set needs to be initialized." for h in 0..high(s.data): - if s.data[h].slot == seFilled: yield s.data[h].key + if isFilled(s.data[h].hcode): yield s.data[h].key const growthFactor = 2 @@ -103,25 +114,44 @@ 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 rightSize*(count: int): int {.inline.} = + ## Return the value of `initialSize` to support `count` items. + ## + ## If more items are expected to be added, simply add that + ## expected extra amount to the parameter before calling this. + ## + ## Internally, we want mustRehash(rightSize(x), x) == false. + result = nextPowerOfTwo(count * 3 div 2 + 4) -template rawGetImpl() {.dirty.} = - var h: THash = hash(key) and high(s.data) # start with real hash value - while s.data[h].slot != seEmpty: - if s.data[h].key == key and s.data[h].slot == seFilled: +proc nextTry(h, maxHash: THash): THash {.inline.} = + result = (h + 1) and maxHash + +template rawGetKnownHCImpl() {.dirty.} = + var h: THash = hc and high(s.data) # start with real hash value + while isFilled(s.data[h].hcode): + # Compare hc THEN key with boolean short circuit. This makes the common case + # zero ==key's for missing (e.g.inserts) and exactly one ==key for present. + # It does slow down succeeding lookups by one extra THash cmp&and..usually + # just a few clock cycles, generally worth it for any non-integer-like A. + if s.data[h].hcode == hc and s.data[h].key == key: # compare hc THEN key return h h = nextTry(h, high(s.data)) - result = -1 + result = -1 - h # < 0 => MISSING; insert idx = -1 - result + +template rawGetImpl() {.dirty.} = + hc = hash(key) + if hc == 0: # This almost never taken branch should be very predictable. + hc = 314159265 # Value doesn't matter; Any non-zero favorite is fine. + rawGetKnownHCImpl() template rawInsertImpl() {.dirty.} = - var h: THash = hash(key) and high(data) - while data[h].slot == seFilled: - h = nextTry(h, high(data)) data[h].key = key - data[h].slot = seFilled + data[h].hcode = hc -proc rawGet[A](s: HashSet[A], key: A): int = +proc rawGetKnownHC[A](s: HashSet[A], key: A, hc: THash): int {.inline.} = + rawGetKnownHCImpl() + +proc rawGet[A](s: HashSet[A], key: A, hc: var THash): int {.inline.} = rawGetImpl() proc mget*[A](s: var HashSet[A], key: A): var A = @@ -130,7 +160,8 @@ proc mget*[A](s: var HashSet[A], key: A): var A = ## when one overloaded 'hash' and '==' but still needs reference semantics ## for sharing. assert s.isValid, "The set needs to be initialized." - var index = rawGet(s, key) + var hc: THash + var index = rawGet(s, key, hc) if index >= 0: result = s.data[index].key else: raise newException(KeyError, "key not found: " & $key) @@ -147,33 +178,43 @@ proc contains*[A](s: HashSet[A], key: A): bool = ## values.excl(2) ## assert(not values.contains(2)) assert s.isValid, "The set needs to be initialized." - var index = rawGet(s, key) + var hc: THash + var index = rawGet(s, key, hc) result = index >= 0 -proc rawInsert[A](s: var HashSet[A], data: var KeyValuePairSeq[A], key: A) = +proc rawInsert[A](s: var HashSet[A], data: var KeyValuePairSeq[A], key: A, + hc: THash, h: THash) = rawInsertImpl() proc enlarge[A](s: var HashSet[A]) = var n: KeyValuePairSeq[A] newSeq(n, len(s.data) * growthFactor) - for i in countup(0, high(s.data)): - if s.data[i].slot == seFilled: rawInsert(s, n, s.data[i].key) - swap(s.data, n) + swap(s.data, n) # n is now old seq + for i in countup(0, high(n)): + if isFilled(n[i].hcode): + var j = -1 - rawGetKnownHC(s, n[i].key, n[i].hcode) + rawInsert(s, s.data, n[i].key, n[i].hcode, j) template inclImpl() {.dirty.} = - var index = rawGet(s, key) + var hc: THash + var index = rawGet(s, key, hc) if index < 0: - if mustRehash(len(s.data), s.counter): enlarge(s) - rawInsert(s, s.data, key) + if mustRehash(len(s.data), s.counter): + enlarge(s) + index = rawGetKnownHC(s, key, hc) + rawInsert(s, s.data, key, hc, -1 - index) inc(s.counter) template containsOrInclImpl() {.dirty.} = - var index = rawGet(s, key) + var hc: THash + var index = rawGet(s, key, hc) if index >= 0: result = true else: - if mustRehash(len(s.data), s.counter): enlarge(s) - rawInsert(s, s.data, key) + if mustRehash(len(s.data), s.counter): + enlarge(s) + index = rawGetKnownHC(s, key, hc) + rawInsert(s, s.data, key, hc, -1 - index) inc(s.counter) proc incl*[A](s: var HashSet[A], key: A) = @@ -204,6 +245,11 @@ proc incl*[A](s: var HashSet[A], other: HashSet[A]) = assert other.isValid, "The set `other` needs to be initialized." for item in other: incl(s, item) +template doWhile(a: expr, b: stmt): stmt = + while true: + b + if not a: break + proc excl*[A](s: var HashSet[A], key: A) = ## Excludes `key` from the set `s`. ## @@ -215,10 +261,22 @@ proc excl*[A](s: var HashSet[A], key: A) = ## s.excl(2) ## assert s.len == 3 assert s.isValid, "The set needs to be initialized." - var index = rawGet(s, key) - if index >= 0: - s.data[index].slot = seDeleted + var hc: THash + var i = rawGet(s, key, hc) + var msk = high(s.data) + if i >= 0: + s.data[i].hcode = 0 dec(s.counter) + while true: # KnuthV3 Algo6.4R adapted for i=i+1 instead of i=i-1 + var j = i # The correctness of this depends on (h+1) in nextTry, + var r = j # though may be adaptable to other simple sequences. + s.data[i].hcode = 0 # mark current EMPTY + doWhile ((i >= r and r > j) or (r > j and j > i) or (j > i and i >= r)): + i = (i + 1) and msk # increment mod table size + if isEmpty(s.data[i].hcode): # end of collision cluster; So all done + return + r = s.data[i].hcode and msk # "home" location of key@i + shallowCopy(s.data[j], s.data[i]) # data[j] will be marked EMPTY next loop proc excl*[A](s: var HashSet[A], other: HashSet[A]) = ## Excludes everything in `other` from `s`. @@ -255,9 +313,9 @@ proc init*[A](s: var HashSet[A], initialSize=64) = ## Initializes a hash set. ## ## The `initialSize` parameter needs to be a power of two. You can use - ## `math.nextPowerOfTwo() `_ to guarantee that at - ## runtime. All set variables have to be initialized before you can use them - ## with other procs from this module with the exception of `isValid() + ## `math.nextPowerOfTwo() `_ or `rightSize` to + ## guarantee that at runtime. All set variables must be initialized before + ## use with other procs from this module with the exception of `isValid() ## <#isValid,TSet[A]>`_ and `len() <#len,TSet[A]>`_. ## ## You can call this proc on a previously initialized hash set, which will @@ -295,7 +353,7 @@ proc toSet*[A](keys: openArray[A]): HashSet[A] = ## var numbers = toSet([1, 2, 3, 4, 5]) ## assert numbers.contains(2) ## assert numbers.contains(4) - result = initSet[A](nextPowerOfTwo(keys.len+10)) + result = initSet[A](rightSize(keys.len)) for key in items(keys): result.incl(key) template dollarImpl(): stmt {.dirty.} = @@ -494,7 +552,7 @@ proc map*[A, B](data: HashSet[A], op: proc (x: A): B {.closure.}): HashSet[B] = type OrderedKeyValuePair[A] = tuple[ - slot: SlotEnum, next: int, key: A] + hcode: THash, next: int, key: A] OrderedKeyValuePairSeq[A] = seq[OrderedKeyValuePair[A]] OrderedSet* {.myShallow.}[A] = object ## \ ## A generic hash set that remembers insertion order. @@ -546,7 +604,7 @@ template forAllOrderedPairs(yieldStmt: stmt) {.dirty, immediate.} = var h = s.first while h >= 0: var nxt = s.data[h].next - if s.data[h].slot == seFilled: yieldStmt + if isFilled(s.data[h].hcode): yieldStmt h = nxt iterator items*[A](s: OrderedSet[A]): A = @@ -571,7 +629,10 @@ iterator items*[A](s: OrderedSet[A]): A = forAllOrderedPairs: yield s.data[h].key -proc rawGet[A](s: OrderedSet[A], key: A): int = +proc rawGetKnownHC[A](s: OrderedSet[A], key: A, hc: THash): int {.inline.} = + rawGetKnownHCImpl() + +proc rawGet[A](s: OrderedSet[A], key: A, hc: var THash): int {.inline.} = rawGetImpl() proc contains*[A](s: OrderedSet[A], key: A): bool = @@ -585,11 +646,12 @@ proc contains*[A](s: OrderedSet[A], key: A): bool = ## values.incl(2) ## assert values.contains(2) assert s.isValid, "The set needs to be initialized." - var index = rawGet(s, key) + var hc: THash + var index = rawGet(s, key, hc) result = index >= 0 -proc rawInsert[A](s: var OrderedSet[A], - data: var OrderedKeyValuePairSeq[A], key: A) = +proc rawInsert[A](s: var OrderedSet[A], data: var OrderedKeyValuePairSeq[A], + key: A, hc: THash, h: THash) = rawInsertImpl() data[h].next = -1 if s.first < 0: s.first = h @@ -602,12 +664,13 @@ proc enlarge[A](s: var OrderedSet[A]) = var h = s.first s.first = -1 s.last = -1 - while h >= 0: - var nxt = s.data[h].next - if s.data[h].slot == seFilled: - rawInsert(s, n, s.data[h].key) - h = nxt swap(s.data, n) + while h >= 0: + var nxt = n[h].next + if isFilled(n[h].hcode): + var j = -1 - rawGetKnownHC(s, n[h].key, n[h].hcode) + rawInsert(s, s.data, n[h].key, n[h].hcode, j) + h = nxt proc incl*[A](s: var OrderedSet[A], key: A) = ## Includes an element `key` in `s`. @@ -655,10 +718,10 @@ proc containsOrIncl*[A](s: var OrderedSet[A], key: A): bool = proc init*[A](s: var OrderedSet[A], initialSize=64) = ## Initializes an ordered hash set. ## - ## The `initialSize` parameter needs to be a power of too. You can use - ## `math.nextPowerOfTwo() `_ to guarantee that at - ## runtime. All set variables have to be initialized before you can use them - ## with other procs from this module with the exception of `isValid() + ## The `initialSize` parameter needs to be a power of two. You can use + ## `math.nextPowerOfTwo() `_ or `rightSize` to + ## guarantee that at runtime. All set variables must be initialized before + ## use with other procs from this module with the exception of `isValid() ## <#isValid,TOrderedSet[A]>`_ and `len() <#len,TOrderedSet[A]>`_. ## ## You can call this proc on a previously initialized ordered hash set to @@ -698,7 +761,7 @@ proc toOrderedSet*[A](keys: openArray[A]): OrderedSet[A] = ## var numbers = toOrderedSet([1, 2, 3, 4, 5]) ## assert numbers.contains(2) ## assert numbers.contains(4) - result = initOrderedSet[A](nextPowerOfTwo(keys.len+10)) + result = initOrderedSet[A](rightSize(keys.len)) for key in items(keys): result.incl(key) proc `$`*[A](s: OrderedSet[A]): string = @@ -726,7 +789,7 @@ proc `==`*[A](s, t: OrderedSet[A]): bool = while h >= 0 and g >= 0: var nxh = s.data[h].next var nxg = t.data[g].next - if s.data[h].slot == seFilled and s.data[g].slot == seFilled: + if isFilled(s.data[h].hcode) and isFilled(s.data[g].hcode): if s.data[h].key == s.data[g].key: inc compared else: @@ -901,6 +964,11 @@ proc testModule() = 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() diff --git a/lib/pure/collections/tables.nim b/lib/pure/collections/tables.nim index 9dcc971481..959688d6a7 100644 --- a/lib/pure/collections/tables.nim +++ b/lib/pure/collections/tables.nim @@ -11,7 +11,7 @@ ## (also often named `dictionary`:idx: in other programming languages) that is ## a mapping from keys to values. ``Table`` is the usual hash table, ## ``OrderedTable`` is like ``Table`` but remembers insertion order -## and ``CountTable`` is a mapping from a key to its number of occurances. +## and ``CountTable`` is a mapping from a key to its number of occurrences. ## For consistency with every other data type in Nim these have **value** ## semantics, this means that ``=`` performs a copy of the hash table. ## For **reference** semantics use the ``Ref`` variant: ``TableRef``, @@ -71,8 +71,7 @@ import {.pragma: myShallow.} type - SlotEnum = enum seEmpty, seFilled, seDeleted - KeyValuePair[A, B] = tuple[slot: SlotEnum, key: A, val: B] + KeyValuePair[A, B] = tuple[hcode: THash, key: A, val: B] KeyValuePairSeq[A, B] = seq[KeyValuePair[A, B]] Table* {.myShallow.}[A, B] = object ## generic hash table data: KeyValuePairSeq[A, B] @@ -84,6 +83,14 @@ type when not defined(nimhygiene): {.pragma: dirty.} +# hcode for real keys cannot be zero. hcode==0 signifies an empty slot. These +# two procs retain clarity of that encoding without the space cost of an enum. +proc isEmpty(hcode: THash): bool {.inline.} = + result = hcode == 0 + +proc isFilled(hcode: THash): bool {.inline.} = + result = hcode != 0 + proc len*[A, B](t: Table[A, B]): int = ## returns the number of keys in `t`. result = t.counter @@ -91,28 +98,28 @@ proc len*[A, B](t: Table[A, B]): int = iterator pairs*[A, B](t: Table[A, B]): tuple[key: A, val: B] = ## iterates over any (key, value) pair in the table `t`. for h in 0..high(t.data): - if t.data[h].slot == seFilled: yield (t.data[h].key, t.data[h].val) + if isFilled(t.data[h].hcode): yield (t.data[h].key, t.data[h].val) iterator mpairs*[A, B](t: var Table[A, B]): tuple[key: A, val: var B] = ## iterates over any (key, value) pair in the table `t`. The values ## can be modified. for h in 0..high(t.data): - if t.data[h].slot == seFilled: yield (t.data[h].key, t.data[h].val) + if isFilled(t.data[h].hcode): yield (t.data[h].key, t.data[h].val) iterator keys*[A, B](t: Table[A, B]): A = ## iterates over any key in the table `t`. for h in 0..high(t.data): - if t.data[h].slot == seFilled: yield t.data[h].key + if isFilled(t.data[h].hcode): yield t.data[h].key iterator values*[A, B](t: Table[A, B]): B = ## iterates over any value in the table `t`. for h in 0..high(t.data): - if t.data[h].slot == seFilled: yield t.data[h].val + if isFilled(t.data[h].hcode): yield t.data[h].val iterator mvalues*[A, B](t: var Table[A, B]): var B = ## iterates over any value in the table `t`. The values can be modified. for h in 0..high(t.data): - if t.data[h].slot == seFilled: yield t.data[h].val + if isFilled(t.data[h].hcode): yield t.data[h].val const growthFactor = 2 @@ -121,26 +128,57 @@ 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 rightSize*(count: int): int {.inline.} = + ## Return the value of `initialSize` to support `count` items. + ## + ## If more items are expected to be added, simply add that + ## expected extra amount to the parameter before calling this. + ## + ## Internally, we want mustRehash(rightSize(x), x) == false. + result = nextPowerOfTwo(count * 3 div 2 + 4) -template rawGetImpl() {.dirty.} = - var h: THash = hash(key) and high(t.data) # start with real hash value - while t.data[h].slot != seEmpty: - if t.data[h].key == key and t.data[h].slot == seFilled: +proc nextTry(h, maxHash: THash): THash {.inline.} = + result = (h + 1) and maxHash + +template rawGetKnownHCImpl() {.dirty.} = + var h: THash = hc and high(t.data) # start with real hash value + while isFilled(t.data[h].hcode): + # Compare hc THEN key with boolean short circuit. This makes the common case + # zero ==key's for missing (e.g.inserts) and exactly one ==key for present. + # It does slow down succeeding lookups by one extra THash cmp&and..usually + # just a few clock cycles, generally worth it for any non-integer-like A. + if t.data[h].hcode == hc and t.data[h].key == key: return h h = nextTry(h, high(t.data)) - result = -1 + result = -1 - h # < 0 => MISSING; insert idx = -1 - result + +template rawGetImpl() {.dirty.} = + hc = hash(key) + if hc == 0: # This almost never taken branch should be very predictable. + hc = 314159265 # Value doesn't matter; Any non-zero favorite is fine. + rawGetKnownHCImpl() + +template rawGetDeepImpl() {.dirty.} = # Search algo for unconditional add + hc = hash(key) + if hc == 0: + hc = 314159265 + var h: THash = hc and high(t.data) + while isFilled(t.data[h].hcode): + h = nextTry(h, high(t.data)) + result = h template rawInsertImpl() {.dirty.} = - var h: THash = hash(key) and high(data) - while data[h].slot == seFilled: - h = nextTry(h, high(data)) data[h].key = key data[h].val = val - data[h].slot = seFilled + data[h].hcode = hc -proc rawGet[A, B](t: Table[A, B], key: A): int = +proc rawGetKnownHC[A, B](t: Table[A, B], key: A, hc: THash): int {.inline.} = + rawGetKnownHCImpl() + +proc rawGetDeep[A, B](t: Table[A, B], key: A, hc: var THash): int {.inline.} = + rawGetDeepImpl() + +proc rawGet[A, B](t: Table[A, B], key: A, hc: var THash): int {.inline.} = rawGetImpl() proc `[]`*[A, B](t: Table[A, B], key: A): B = @@ -148,63 +186,87 @@ proc `[]`*[A, B](t: Table[A, B], key: A): B = ## default empty value for the type `B` is returned ## and no exception is raised. One can check with ``hasKey`` whether the key ## exists. - var index = rawGet(t, key) + var hc: THash + var index = rawGet(t, key, hc) if index >= 0: result = t.data[index].val proc mget*[A, B](t: var Table[A, B], key: A): var B = ## retrieves the value at ``t[key]``. The value can be modified. ## If `key` is not in `t`, the ``EInvalidKey`` exception is raised. - var index = rawGet(t, key) + var hc: THash + var index = rawGet(t, key, hc) if index >= 0: result = t.data[index].val else: raise newException(KeyError, "key not found: " & $key) iterator allValues*[A, B](t: Table[A, B]; key: A): B = ## iterates over any value in the table `t` that belongs to the given `key`. var h: THash = hash(key) and high(t.data) - while t.data[h].slot != seEmpty: - if t.data[h].key == key and t.data[h].slot == seFilled: + while isFilled(t.data[h].hcode): + if t.data[h].key == key: yield t.data[h].val h = nextTry(h, high(t.data)) proc hasKey*[A, B](t: Table[A, B], key: A): bool = ## returns true iff `key` is in the table `t`. - result = rawGet(t, key) >= 0 + var hc: THash + result = rawGet(t, key, hc) >= 0 proc rawInsert[A, B](t: var Table[A, B], data: var KeyValuePairSeq[A, B], - key: A, val: B) = + key: A, val: B, hc: THash, h: THash) = rawInsertImpl() proc enlarge[A, B](t: var Table[A, B]) = var n: KeyValuePairSeq[A, B] newSeq(n, len(t.data) * growthFactor) - for i in countup(0, high(t.data)): - if t.data[i].slot == seFilled: rawInsert(t, n, t.data[i].key, t.data[i].val) swap(t.data, n) + for i in countup(0, high(n)): + if isFilled(n[i].hcode): + var j = -1 - rawGetKnownHC(t, n[i].key, n[i].hcode) + rawInsert(t, t.data, n[i].key, n[i].val, n[i].hcode, j) template addImpl() {.dirty.} = if mustRehash(len(t.data), t.counter): enlarge(t) - rawInsert(t, t.data, key, val) + var hc: THash + var j = rawGetDeep(t, key, hc) + rawInsert(t, t.data, key, val, hc, j) + inc(t.counter) + +template maybeRehashPutImpl() {.dirty.} = + if mustRehash(len(t.data), t.counter): + enlarge(t) + index = rawGetKnownHC(t, key, hc) + index = -1 - index # important to transform for mgetOrPutImpl + rawInsert(t, t.data, key, val, hc, index) inc(t.counter) template putImpl() {.dirty.} = - var index = rawGet(t, key) - if index >= 0: - t.data[index].val = val - else: - addImpl() + var hc: THash + var index = rawGet(t, key, hc) + if index >= 0: t.data[index].val = val + else: maybeRehashPutImpl() -when false: - # not yet used: - template hasKeyOrPutImpl() {.dirty.} = - var index = rawGet(t, key) - if index >= 0: - t.data[index].val = val - result = true - else: - if mustRehash(len(t.data), t.counter): enlarge(t) - rawInsert(t, t.data, key, val) - inc(t.counter) - result = false +template mgetOrPutImpl() {.dirty.} = + var hc: THash + var index = rawGet(t, key, hc) + if index < 0: maybeRehashPutImpl() # not present: insert (flipping index) + result = t.data[index].val # either way return modifiable val + +template hasKeyOrPutImpl() {.dirty.} = + var hc: THash + var index = rawGet(t, key, hc) + if index < 0: + result = false + maybeRehashPutImpl() + else: result = true + +proc mgetOrPut*[A, B](t: var Table[A, B], key: A, val: B): var B = + ## retrieves value at ``t[key]`` or puts ``val`` if not present, either way + ## returning a value which can be modified. + mgetOrPutImpl() + +proc hasKeyOrPut*[A, B](t: var Table[A, B], key: A, val: B): bool = + ## returns true iff `key` is in the table, otherwise inserts `value`. + hasKeyOrPutImpl() proc `[]=`*[A, B](t: var Table[A, B], key: A, val: B) = ## puts a (key, value)-pair into `t`. @@ -213,20 +275,37 @@ proc `[]=`*[A, B](t: var Table[A, B], key: A, val: B) = proc add*[A, B](t: var Table[A, B], key: A, val: B) = ## puts a new (key, value)-pair into `t` even if ``t[key]`` already exists. addImpl() - + +template doWhile(a: expr, b: stmt): stmt = + while true: + b + if not a: break + proc del*[A, B](t: var Table[A, B], key: A) = ## deletes `key` from hash table `t`. - let index = rawGet(t, key) - if index >= 0: - t.data[index].slot = seDeleted + var hc: THash + var i = rawGet(t, key, hc) + let msk = high(t.data) + if i >= 0: + t.data[i].hcode = 0 dec(t.counter) + while true: # KnuthV3 Algo6.4R adapted for i=i+1 instead of i=i-1 + var j = i # The correctness of this depends on (h+1) in nextTry, + var r = j # though may be adaptable to other simple sequences. + t.data[i].hcode = 0 # mark current EMPTY + doWhile ((i >= r and r > j) or (r > j and j > i) or (j > i and i >= r)): + i = (i + 1) and msk # increment mod table size + if isEmpty(t.data[i].hcode): # end of collision cluster; So all done + return + r = t.data[i].hcode and msk # "home" location of key@i + shallowCopy(t.data[j], t.data[i]) # data[j] will be marked EMPTY next loop proc initTable*[A, B](initialSize=64): Table[A, B] = ## creates a new hash table that is empty. ## ## `initialSize` needs to be a power of two. If you need to accept runtime ## values for this you could use the ``nextPowerOfTwo`` proc from the - ## `math `_ module. + ## `math `_ module or the ``rightSize`` proc from this module. assert isPowerOfTwo(initialSize) result.counter = 0 newSeq(result.data, initialSize) @@ -234,7 +313,7 @@ proc initTable*[A, B](initialSize=64): Table[A, B] = proc toTable*[A, B](pairs: openArray[tuple[key: A, val: B]]): Table[A, B] = ## creates a new hash table that contains the given `pairs`. - result = initTable[A, B](nextPowerOfTwo(pairs.len+10)) + result = initTable[A, B](rightSize(pairs.len)) for key, val in items(pairs): result[key] = val template dollarImpl(): stmt {.dirty.} = @@ -252,7 +331,7 @@ template dollarImpl(): stmt {.dirty.} = proc `$`*[A, B](t: Table[A, B]): string = ## The `$` operator for hash tables. dollarImpl() - + template equalsImpl() = if s.counter == t.counter: # different insertion orders mean different 'data' seqs, so we have @@ -262,10 +341,10 @@ template equalsImpl() = if not t.hasKey(key): return false if t[key] != val: return false return true - + proc `==`*[A, B](s, t: Table[A, B]): bool = equalsImpl() - + proc indexBy*[A, B, C](collection: A, index: proc(x: B): C): Table[C, B] = ## Index the collection with the proc provided. # TODO: As soon as supported, change collection: A to collection: A[B] @@ -280,28 +359,28 @@ proc len*[A, B](t: TableRef[A, B]): int = iterator pairs*[A, B](t: TableRef[A, B]): tuple[key: A, val: B] = ## iterates over any (key, value) pair in the table `t`. for h in 0..high(t.data): - if t.data[h].slot == seFilled: yield (t.data[h].key, t.data[h].val) + if isFilled(t.data[h].hcode): yield (t.data[h].key, t.data[h].val) iterator mpairs*[A, B](t: TableRef[A, B]): tuple[key: A, val: var B] = ## iterates over any (key, value) pair in the table `t`. The values ## can be modified. for h in 0..high(t.data): - if t.data[h].slot == seFilled: yield (t.data[h].key, t.data[h].val) + if isFilled(t.data[h].hcode): yield (t.data[h].key, t.data[h].val) iterator keys*[A, B](t: TableRef[A, B]): A = ## iterates over any key in the table `t`. for h in 0..high(t.data): - if t.data[h].slot == seFilled: yield t.data[h].key + if isFilled(t.data[h].hcode): yield t.data[h].key iterator values*[A, B](t: TableRef[A, B]): B = ## iterates over any value in the table `t`. for h in 0..high(t.data): - if t.data[h].slot == seFilled: yield t.data[h].val + if isFilled(t.data[h].hcode): yield t.data[h].val iterator mvalues*[A, B](t: TableRef[A, B]): var B = ## iterates over any value in the table `t`. The values can be modified. for h in 0..high(t.data): - if t.data[h].slot == seFilled: yield t.data[h].val + if isFilled(t.data[h].hcode): yield t.data[h].val proc `[]`*[A, B](t: TableRef[A, B], key: A): B = ## retrieves the value at ``t[key]``. If `key` is not in `t`, @@ -315,6 +394,15 @@ proc mget*[A, B](t: TableRef[A, B], key: A): var B = ## If `key` is not in `t`, the ``EInvalidKey`` exception is raised. t[].mget(key) +proc mgetOrPut*[A, B](t: TableRef[A, B], key: A, val: B): var B = + ## retrieves value at ``t[key]`` or puts ``val`` if not present, either way + ## returning a value which can be modified. + t[].mgetOrPut(key, val) + +proc hasKeyOrPut*[A, B](t: var TableRef[A, B], key: A, val: B): bool = + ## returns true iff `key` is in the table, otherwise inserts `value`. + t[].hasKeyOrPut(key, val) + proc hasKey*[A, B](t: TableRef[A, B], key: A): bool = ## returns true iff `key` is in the table `t`. result = t[].hasKey(key) @@ -326,7 +414,7 @@ proc `[]=`*[A, B](t: TableRef[A, B], key: A, val: B) = proc add*[A, B](t: TableRef[A, B], key: A, val: B) = ## puts a new (key, value)-pair into `t` even if ``t[key]`` already exists. t[].add(key, val) - + proc del*[A, B](t: TableRef[A, B], key: A) = ## deletes `key` from hash table `t`. t[].del(key) @@ -347,7 +435,7 @@ proc `$`*[A, B](t: TableRef[A, B]): string = proc `==`*[A, B](s, t: TableRef[A, B]): bool = if isNil(s): result = isNil(t) elif isNil(t): result = false - else: result = equalsImpl() + else: equalsImpl() proc newTableFrom*[A, B, C](collection: A, index: proc(x: B): C): TableRef[C, B] = ## Index the collection with the proc provided. @@ -360,7 +448,7 @@ proc newTableFrom*[A, B, C](collection: A, index: proc(x: B): C): TableRef[C, B] type OrderedKeyValuePair[A, B] = tuple[ - slot: SlotEnum, next: int, key: A, val: B] + hcode: THash, next: int, key: A, val: B] OrderedKeyValuePairSeq[A, B] = seq[OrderedKeyValuePair[A, B]] OrderedTable* {. myShallow.}[A, B] = object ## table that remembers insertion order @@ -378,7 +466,7 @@ template forAllOrderedPairs(yieldStmt: stmt) {.dirty, immediate.} = var h = t.first while h >= 0: var nxt = t.data[h].next - if t.data[h].slot == seFilled: yieldStmt + if isFilled(t.data[h].hcode): yieldStmt h = nxt iterator pairs*[A, B](t: OrderedTable[A, B]): tuple[key: A, val: B] = @@ -409,7 +497,13 @@ iterator mvalues*[A, B](t: var OrderedTable[A, B]): var B = forAllOrderedPairs: yield t.data[h].val -proc rawGet[A, B](t: OrderedTable[A, B], key: A): int = +proc rawGetKnownHC[A, B](t: OrderedTable[A, B], key: A, hc: THash): int = + rawGetKnownHCImpl() + +proc rawGetDeep[A, B](t: OrderedTable[A, B], key: A, hc: var THash): int {.inline.} = + rawGetDeepImpl() + +proc rawGet[A, B](t: OrderedTable[A, B], key: A, hc: var THash): int = rawGetImpl() proc `[]`*[A, B](t: OrderedTable[A, B], key: A): B = @@ -417,23 +511,26 @@ proc `[]`*[A, B](t: OrderedTable[A, B], key: A): B = ## default empty value for the type `B` is returned ## and no exception is raised. One can check with ``hasKey`` whether the key ## exists. - var index = rawGet(t, key) + var hc: THash + var index = rawGet(t, key, hc) if index >= 0: result = t.data[index].val proc mget*[A, B](t: var OrderedTable[A, B], key: A): var B = ## retrieves the value at ``t[key]``. The value can be modified. ## If `key` is not in `t`, the ``EInvalidKey`` exception is raised. - var index = rawGet(t, key) + var hc: THash + var index = rawGet(t, key, hc) if index >= 0: result = t.data[index].val else: raise newException(KeyError, "key not found: " & $key) proc hasKey*[A, B](t: OrderedTable[A, B], key: A): bool = ## returns true iff `key` is in the table `t`. - result = rawGet(t, key) >= 0 + var hc: THash + result = rawGet(t, key, hc) >= 0 proc rawInsert[A, B](t: var OrderedTable[A, B], data: var OrderedKeyValuePairSeq[A, B], - key: A, val: B) = + key: A, val: B, hc: THash, h: THash) = rawInsertImpl() data[h].next = -1 if t.first < 0: t.first = h @@ -446,12 +543,13 @@ proc enlarge[A, B](t: var OrderedTable[A, B]) = var h = t.first t.first = -1 t.last = -1 - while h >= 0: - var nxt = t.data[h].next - if t.data[h].slot == seFilled: - rawInsert(t, n, t.data[h].key, t.data[h].val) - h = nxt swap(t.data, n) + while h >= 0: + var nxt = n[h].next + if isFilled(n[h].hcode): + var j = -1 - rawGetKnownHC(t, n[h].key, n[h].hcode) + rawInsert(t, t.data, n[h].key, n[h].val, n[h].hcode, j) + h = nxt proc `[]=`*[A, B](t: var OrderedTable[A, B], key: A, val: B) = ## puts a (key, value)-pair into `t`. @@ -461,12 +559,21 @@ proc add*[A, B](t: var OrderedTable[A, B], key: A, val: B) = ## puts a new (key, value)-pair into `t` even if ``t[key]`` already exists. addImpl() +proc mgetOrPut*[A, B](t: var OrderedTable[A, B], key: A, val: B): var B = + ## retrieves value at ``t[key]`` or puts ``value`` if not present, either way + ## returning a value which can be modified. + mgetOrPutImpl() + +proc hasKeyOrPut*[A, B](t: var OrderedTable[A, B], key: A, val: B): bool = + ## returns true iff `key` is in the table, otherwise inserts `value`. + hasKeyOrPutImpl() + proc initOrderedTable*[A, B](initialSize=64): OrderedTable[A, B] = ## creates a new ordered hash table that is empty. ## ## `initialSize` needs to be a power of two. If you need to accept runtime ## values for this you could use the ``nextPowerOfTwo`` proc from the - ## `math `_ module. + ## `math `_ module or the ``rightSize`` proc from this module. assert isPowerOfTwo(initialSize) result.counter = 0 result.first = -1 @@ -476,7 +583,7 @@ proc initOrderedTable*[A, B](initialSize=64): OrderedTable[A, B] = proc toOrderedTable*[A, B](pairs: openArray[tuple[key: A, val: B]]): OrderedTable[A, B] = ## creates a new ordered hash table that contains the given `pairs`. - result = initOrderedTable[A, B](nextPowerOfTwo(pairs.len+10)) + result = initOrderedTable[A, B](rightSize(pairs.len)) for key, val in items(pairs): result[key] = val proc `$`*[A, B](t: OrderedTable[A, B]): string = @@ -537,7 +644,7 @@ template forAllOrderedPairs(yieldStmt: stmt) {.dirty, immediate.} = var h = t.first while h >= 0: var nxt = t.data[h].next - if t.data[h].slot == seFilled: yieldStmt + if isFilled(t.data[h].hcode): yieldStmt h = nxt iterator pairs*[A, B](t: OrderedTableRef[A, B]): tuple[key: A, val: B] = @@ -580,6 +687,15 @@ proc mget*[A, B](t: OrderedTableRef[A, B], key: A): var B = ## If `key` is not in `t`, the ``EInvalidKey`` exception is raised. result = t[].mget(key) +proc mgetOrPut*[A, B](t: OrderedTableRef[A, B], key: A, val: B): var B = + ## retrieves value at ``t[key]`` or puts ``val`` if not present, either way + ## returning a value which can be modified. + result = t[].mgetOrPut(key, val) + +proc hasKeyOrPut*[A, B](t: var OrderedTableRef[A, B], key: A, val: B): bool = + ## returns true iff `key` is in the table, otherwise inserts `val`. + result = t[].hasKeyOrPut(key, val) + proc hasKey*[A, B](t: OrderedTableRef[A, B], key: A): bool = ## returns true iff `key` is in the table `t`. result = t[].hasKey(key) @@ -597,14 +713,14 @@ proc newOrderedTable*[A, B](initialSize=64): OrderedTableRef[A, B] = ## ## `initialSize` needs to be a power of two. If you need to accept runtime ## values for this you could use the ``nextPowerOfTwo`` proc from the - ## `math `_ module. + ## `math `_ module or the ``rightSize`` proc from this module. new(result) result[] = initOrderedTable[A, B]() proc newOrderedTable*[A, B](pairs: openArray[tuple[key: A, val: B]]): OrderedTableRef[A, B] = ## creates a new ordered hash table that contains the given `pairs`. - result = newOrderedTable[A, B](nextPowerOfTwo(pairs.len+10)) + result = newOrderedTable[A, B](rightSize(pairs.len)) for key, val in items(pairs): result[key] = val proc `$`*[A, B](t: OrderedTableRef[A, B]): string = @@ -665,7 +781,7 @@ proc rawGet[A](t: CountTable[A], key: A): int = while t.data[h].val != 0: if t.data[h].key == key: return h h = nextTry(h, high(t.data)) - result = -1 + result = -1 - h # < 0 => MISSING; insert idx = -1 - result proc `[]`*[A](t: CountTable[A], key: A): int = ## retrieves the value at ``t[key]``. If `key` is not in `t`, @@ -702,21 +818,27 @@ proc enlarge[A](t: var CountTable[A]) = proc `[]=`*[A](t: var CountTable[A], key: A, val: int) = ## puts a (key, value)-pair into `t`. `val` has to be positive. assert val > 0 - putImpl() + 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 proc initCountTable*[A](initialSize=64): CountTable[A] = ## creates a new count table that is empty. ## ## `initialSize` needs to be a power of two. If you need to accept runtime ## values for this you could use the ``nextPowerOfTwo`` proc from the - ## `math `_ module. + ## `math `_ module or the ``rightSize`` proc in this module. assert isPowerOfTwo(initialSize) result.counter = 0 newSeq(result.data, initialSize) proc toCountTable*[A](keys: openArray[A]): CountTable[A] = ## creates a new count table with every key in `keys` having a count of 1. - result = initCountTable[A](nextPowerOfTwo(keys.len+10)) + result = initCountTable[A](rightSize(keys.len)) for key in items(keys): result[key] = 1 proc `$`*[A](t: CountTable[A]): string = @@ -827,13 +949,13 @@ proc newCountTable*[A](initialSize=64): CountTableRef[A] = ## ## `initialSize` needs to be a power of two. If you need to accept runtime ## values for this you could use the ``nextPowerOfTwo`` proc from the - ## `math `_ module. + ## `math `_ module or the ``rightSize`` method in this module. new(result) result[] = initCountTable[A](initialSize) proc newCountTable*[A](keys: openArray[A]): CountTableRef[A] = ## creates a new count table with every key in `keys` having a count of 1. - result = newCountTable[A](nextPowerOfTwo(keys.len+10)) + result = newCountTable[A](rightSize(keys.len)) for key in items(keys): result[key] = 1 proc `$`*[A](t: CountTableRef[A]): string = diff --git a/lib/pure/colors.nim b/lib/pure/colors.nim index 7942255cb5..f24cc0072f 100644 --- a/lib/pure/colors.nim +++ b/lib/pure/colors.nim @@ -46,7 +46,7 @@ proc `+`*(a, b: Color): Color = colorOp(satPlus) proc `-`*(a, b: Color): Color = - ## substracts two colors: This uses saturated artithmetic, so that each color + ## subtracts two colors: This uses saturated artithmetic, so that each color ## component cannot overflow (255 is used as a maximum). colorOp(satMinus) @@ -392,7 +392,7 @@ proc parseColor*(name: string): Color = result = Color(parseHexInt(name)) else: var idx = binaryStrSearch(colorNames, name) - if idx < 0: raise newException(ValueError, "unkown color: " & name) + if idx < 0: raise newException(ValueError, "unknown color: " & name) result = colorNames[idx][1] proc isColor*(name: string): bool = diff --git a/lib/pure/complex.nim b/lib/pure/complex.nim index 04cffe4e47..8577bf7a15 100644 --- a/lib/pure/complex.nim +++ b/lib/pure/complex.nim @@ -27,6 +27,11 @@ type {.deprecated: [TComplex: Complex].} +proc toComplex*(x: SomeInteger): Complex = + ## Convert some integer ``x`` to a complex number. + result.re = x + result.im = 0 + proc `==` *(x, y: Complex): bool = ## Compare two complex numbers `x` and `y` for equality. result = x.re == y.re and x.im == y.im @@ -269,27 +274,101 @@ proc tan*(z: Complex): Complex = ## Returns the tangent of `z`. result = sin(z)/cos(z) +proc arctan*(z: Complex): Complex = + ## Returns the inverse tangent of `z`. + var i: Complex = (0.0,1.0) + result = 0.5*i*(ln(1-i*z)-ln(1+i*z)) + proc cot*(z: Complex): Complex = ## Returns the cotangent of `z`. result = cos(z)/sin(z) +proc arccot*(z: Complex): Complex = + ## Returns the inverse cotangent of `z`. + var i: Complex = (0.0,1.0) + result = 0.5*i*(ln(1-i/z)-ln(1+i/z)) + proc sec*(z: Complex): Complex = ## Returns the secant of `z`. result = 1.0/cos(z) +proc arcsec*(z: Complex): Complex = + ## Returns the inverse secant of `z`. + var i: Complex = (0.0,1.0) + result = -i*ln(i*sqrt(1-1/(z*z))+1/z) + proc csc*(z: Complex): Complex = ## Returns the cosecant of `z`. result = 1.0/sin(z) +proc arccsc*(z: Complex): Complex = + ## Returns the inverse cosecant of `z`. + var i: Complex = (0.0,1.0) + result = -i*ln(sqrt(1-1/(z*z))+i/z) + proc sinh*(z: Complex): Complex = ## Returns the hyperbolic sine of `z`. result = 0.5*(exp(z)-exp(-z)) +proc arcsinh*(z: Complex): Complex = + ## Returns the inverse hyperbolic sine of `z`. + result = ln(z+sqrt(z*z+1)) + proc cosh*(z: Complex): Complex = ## Returns the hyperbolic cosine of `z`. result = 0.5*(exp(z)+exp(-z)) +proc arccosh*(z: Complex): Complex = + ## Returns the inverse hyperbolic cosine of `z`. + result = ln(z+sqrt(z*z-1)) + +proc tanh*(z: Complex): Complex = + ## Returns the hyperbolic tangent of `z`. + result = sinh(z)/cosh(z) + +proc arctanh*(z: Complex): Complex = + ## Returns the inverse hyperbolic tangent of `z`. + result = 0.5*(ln((1+z)/(1-z))) + +proc sech*(z: Complex): Complex = + ## Returns the hyperbolic secant of `z`. + result = 2/(exp(z)+exp(-z)) + +proc arcsech*(z: Complex): Complex = + ## Returns the inverse hyperbolic secant of `z`. + result = ln(1/z+sqrt(1/z+1)*sqrt(1/z-1)) + +proc csch*(z: Complex): Complex = + ## Returns the hyperbolic cosecant of `z`. + result = 2/(exp(z)-exp(-z)) + +proc arccsch*(z: Complex): Complex = + ## Returns the inverse hyperbolic cosecant of `z`. + result = ln(1/z+sqrt(1/(z*z)+1)) + +proc coth*(z: Complex): Complex = + ## Returns the hyperbolic cotangent of `z`. + result = cosh(z)/sinh(z) + +proc arccoth*(z: Complex): Complex = + ## Returns the inverse hyperbolic cotangent of `z`. + result = 0.5*(ln(1+1/z)-ln(1-1/z)) + +proc phase*(z: Complex): float = + ## Returns the phase of `z`. + arctan2(z.im, z.re) + +proc polar*(z: Complex): tuple[r, phi: float] = + ## Returns `z` in polar coordinates. + result.r = abs(z) + result.phi = phase(z) + +proc rect*(r: float, phi: float): Complex = + ## Returns the complex number with polar coordinates `r` and `phi`. + result.re = r * cos(phi) + result.im = r * sin(phi) + proc `$`*(z: Complex): string = ## Returns `z`'s string representation as ``"(re, im)"``. @@ -343,7 +422,22 @@ when isMainModule: assert( csc(a) =~ 1.0/sin(a) ) assert( arcsin(a) =~ (0.427078586392476, 1.528570919480998) ) assert( arccos(a) =~ (1.14371774040242, -1.52857091948100) ) + assert( arctan(a) =~ (1.338972522294494, 0.402359478108525) ) - assert( cosh(a) =~ (-0.642148124715520, 1.068607421382778) ) + assert( cosh(a) =~ (-0.642148124715520, 1.068607421382778) ) assert( sinh(a) =~ (-0.489056259041294, 1.403119250622040) ) - \ No newline at end of file + assert( tanh(a) =~ (1.1667362572409199,-0.243458201185725) ) + assert( sech(a) =~ 1/cosh(a) ) + assert( csch(a) =~ 1/sinh(a) ) + assert( coth(a) =~ 1/tanh(a) ) + assert( arccosh(a) =~ (1.528570919480998, 1.14371774040242) ) + assert( arcsinh(a) =~ (1.469351744368185, 1.06344002357775) ) + assert( arctanh(a) =~ (0.173286795139986, 1.17809724509617) ) + assert( arcsech(a) =~ arccosh(1/a) ) + assert( arccsch(a) =~ arcsinh(1/a) ) + assert( arccoth(a) =~ arctanh(1/a) ) + + assert( phase(a) == 1.1071487177940904 ) + var t = polar(a) + assert( rect(t.r, t.phi) =~ a ) + assert( rect(1.0, 2.0) =~ (-0.4161468365471424, 0.9092974268256817) ) diff --git a/lib/pure/encodings.nim b/lib/pure/encodings.nim index 298a6072d5..25c7ad9ef7 100644 --- a/lib/pure/encodings.nim +++ b/lib/pure/encodings.nim @@ -301,7 +301,7 @@ proc getCurrentEncoding*(): string = proc open*(destEncoding = "UTF-8", srcEncoding = "CP1252"): EncodingConverter = ## opens a converter that can convert from `srcEncoding` to `destEncoding`. - ## Raises `EIO` if it cannot fullfill the request. + ## Raises `EIO` if it cannot fulfill the request. when not defined(windows): result = iconvOpen(destEncoding, srcEncoding) if result == nil: diff --git a/lib/pure/events.nim b/lib/pure/events.nim index 77faa6a667..44e9ed2865 100644 --- a/lib/pure/events.nim +++ b/lib/pure/events.nim @@ -9,7 +9,7 @@ ## :Author: Alex Mitchell ## -## This module implements an event system that is not dependant on external +## This module implements an event system that is not dependent on external ## graphical toolkits. It was originally called ``NimEE`` because ## it was inspired by Python's PyEE module. There are two ways you can use ## events: one is a python-inspired way; the other is more of a C-style way. diff --git a/lib/pure/fsmonitor.nim b/lib/pure/fsmonitor.nim index bf4aef61c9..e6919b661b 100644 --- a/lib/pure/fsmonitor.nim +++ b/lib/pure/fsmonitor.nim @@ -29,7 +29,7 @@ type FSMonitorObj = object of RootObj fd: cint handleEvent: proc (m: FSMonitor, ev: MonitorEvent) {.closure.} - targets: TTable[cint, string] + targets: Table[cint, string] MonitorEventType* = enum ## Monitor event type MonitorAccess, ## File was accessed. @@ -64,7 +64,7 @@ type const MaxEvents = 100 -proc newMonitor*(): PFSMonitor = +proc newMonitor*(): FSMonitor = ## Creates a new file system monitor. new(result) result.targets = initTable[cint, string]() @@ -72,7 +72,7 @@ proc newMonitor*(): PFSMonitor = if result.fd < 0: raiseOSError(osLastError()) -proc add*(monitor: PFSMonitor, target: string, +proc add*(monitor: FSMonitor, target: string, filters = {MonitorAll}): cint {.discardable.} = ## Adds ``target`` which may be a directory or a file to the list of ## watched paths of ``monitor``. @@ -99,14 +99,14 @@ proc add*(monitor: PFSMonitor, target: string, raiseOSError(osLastError()) monitor.targets.add(result, target) -proc del*(monitor: PFSMonitor, wd: cint) = +proc del*(monitor: FSMonitor, wd: cint) = ## Removes watched directory or file as specified by ``wd`` from ``monitor``. ## ## If ``wd`` is not a part of ``monitor`` an EOS error is raised. if inotifyRmWatch(monitor.fd, wd) < 0: raiseOSError(osLastError()) -proc getEvent(m: PFSMonitor, fd: cint): seq[TMonitorEvent] = +proc getEvent(m: FSMonitor, fd: cint): seq[MonitorEvent] = result = @[] let size = (sizeof(TINotifyEvent)+2000)*MaxEvents var buffer = newString(size) @@ -118,7 +118,7 @@ proc getEvent(m: PFSMonitor, fd: cint): seq[TMonitorEvent] = var i = 0 while i < le: var event = cast[ptr TINotifyEvent](addr(buffer[i])) - var mev: TMonitorEvent + var mev: MonitorEvent mev.wd = event.wd if event.len.int != 0: let cstr = event.name.addr.cstring @@ -137,7 +137,7 @@ proc getEvent(m: PFSMonitor, fd: cint): seq[TMonitorEvent] = # Find the MovedFrom event. mev.oldPath = movedFrom[event.cookie.cint].old mev.newPath = "" # Set later - # Delete it from the TTable + # Delete it from the Table movedFrom.del(event.cookie.cint) elif (event.mask.int and IN_ACCESS) != 0: mev.kind = MonitorAccess elif (event.mask.int and IN_ATTRIB) != 0: mev.kind = MonitorAttrib @@ -164,26 +164,26 @@ proc getEvent(m: PFSMonitor, fd: cint): seq[TMonitorEvent] = # If movedFrom events have not been matched with a moveTo. File has # been moved to an unwatched location, emit a MonitorDelete. for cookie, t in pairs(movedFrom): - var mev: TMonitorEvent + var mev: MonitorEvent mev.kind = MonitorDelete mev.wd = t.wd mev.name = t.old result.add(mev) -proc FSMonitorRead(h: PObject) = - var events = PFSMonitor(h).getEvent(PFSMonitor(h).fd) - #var newEv: TMonitorEvent +proc FSMonitorRead(h: RootRef) = + var events = FSMonitor(h).getEvent(FSMonitor(h).fd) + #var newEv: MonitorEvent for ev in events: - var target = PFSMonitor(h).targets[ev.wd] + var target = FSMonitor(h).targets[ev.wd] var newEv = ev if newEv.kind == MonitorMoved: newEv.oldPath = target / newEv.oldPath newEv.newPath = target / newEv.name else: newEv.fullName = target / newEv.name - PFSMonitor(h).handleEvent(PFSMonitor(h), newEv) + FSMonitor(h).handleEvent(FSMonitor(h), newEv) -proc toDelegate(m: PFSMonitor): PDelegate = +proc toDelegate(m: FSMonitor): Delegate = result = newDelegate() result.deleVal = m result.fd = (type(result.fd))(m.fd) @@ -191,8 +191,8 @@ proc toDelegate(m: PFSMonitor): PDelegate = result.handleRead = FSMonitorRead result.open = true -proc register*(d: PDispatcher, monitor: PFSMonitor, - handleEvent: proc (m: PFSMonitor, ev: TMonitorEvent) {.closure.}) = +proc register*(d: Dispatcher, monitor: FSMonitor, + handleEvent: proc (m: FSMonitor, ev: MonitorEvent) {.closure.}) = ## Registers ``monitor`` with dispatcher ``d``. monitor.handleEvent = handleEvent var deleg = toDelegate(monitor) @@ -204,7 +204,7 @@ when isMainModule: var monitor = newMonitor() echo monitor.add("/home/dom/inotifytests/") disp.register(monitor, - proc (m: PFSMonitor, ev: TMonitorEvent) = + proc (m: FSMonitor, ev: MonitorEvent) = echo("Got event: ", ev.kind) if ev.kind == MonitorMoved: echo("From ", ev.oldPath, " to ", ev.newPath) diff --git a/lib/pure/ftpclient.nim b/lib/pure/ftpclient.nim index 40ee5951bb..46af1d5281 100644 --- a/lib/pure/ftpclient.nim +++ b/lib/pure/ftpclient.nim @@ -107,7 +107,7 @@ type EInvalidReply: ReplyError, EFTP: FTPError ].} -proc ftpClient*(address: string, port = TPort(21), +proc ftpClient*(address: string, port = Port(21), user, pass = ""): FtpClient = ## Create a ``FtpClient`` object. new(result) @@ -120,10 +120,10 @@ proc ftpClient*(address: string, port = TPort(21), result.csock = socket() if result.csock == invalidSocket: raiseOSError(osLastError()) -template blockingOperation(sock: TSocket, body: stmt) {.immediate.} = +template blockingOperation(sock: Socket, body: stmt) {.immediate.} = body -template blockingOperation(sock: asyncio.PAsyncSocket, body: stmt) {.immediate.} = +template blockingOperation(sock: asyncio.AsyncSocket, body: stmt) {.immediate.} = sock.setBlocking(true) body sock.setBlocking(false) @@ -145,14 +145,14 @@ proc send*[T](ftp: FtpBase[T], m: string): TaintedString = proc assertReply(received: TaintedString, expected: string) = if not received.string.startsWith(expected): - raise newException(EInvalidReply, + raise newException(ReplyError, "Expected reply '$1' got: $2" % [ expected, received.string]) proc assertReply(received: TaintedString, expected: varargs[string]) = for i in items(expected): if received.string.startsWith(i): return - raise newException(EInvalidReply, + raise newException(ReplyError, "Expected reply '$1' got: $2" % [expected.join("' or '"), received.string]) @@ -161,7 +161,7 @@ proc createJob[T](ftp: FtpBase[T], nimcall,gcsafe.}, cmd: FTPJobType) = if ftp.jobInProgress: - raise newException(EFTP, "Unable to do two jobs at once.") + raise newException(FTPError, "Unable to do two jobs at once.") ftp.jobInProgress = true new(ftp.job) ftp.job.prc = prc @@ -182,11 +182,11 @@ proc deleteJob[T](ftp: FtpBase[T]) = ftp.job.file.close() ftp.dsock.close() -proc handleTask(s: PAsyncSocket, ftp: PAsyncFTPClient) = +proc handleTask(s: AsyncSocket, ftp: AsyncFTPClient) = if ftp.jobInProgress: if ftp.job.typ in {JRetr, JStore}: if epochTime() - ftp.job.lastProgressReport >= 1.0: - var r: TFTPEvent + var r: FTPEvent ftp.job.lastProgressReport = epochTime() r.typ = EvTransferProgress r.bytesTotal = ftp.job.total @@ -195,22 +195,22 @@ proc handleTask(s: PAsyncSocket, ftp: PAsyncFTPClient) = r.filename = ftp.job.filename r.currentJob = ftp.job.typ ftp.job.oneSecond = 0 - ftp.handleEvent(PAsyncFTPClient(ftp), r) + ftp.handleEvent(ftp, r) -proc handleWrite(s: PAsyncSocket, ftp: PAsyncFTPClient) = +proc handleWrite(s: AsyncSocket, ftp: AsyncFTPClient) = if ftp.jobInProgress: if ftp.job.typ == JStore: assert (not ftp.job.prc(ftp, true)) -proc handleConnect(s: PAsyncSocket, ftp: PAsyncFTPClient) = +proc handleConnect(s: AsyncSocket, ftp: AsyncFTPClient) = ftp.dsockConnected = true assert(ftp.jobInProgress) if ftp.job.typ == JStore: - s.setHandleWrite(proc (s: PAsyncSocket) = handleWrite(s, ftp)) + s.setHandleWrite(proc (s: AsyncSocket) = handleWrite(s, ftp)) else: s.delHandleWrite() -proc handleRead(s: PAsyncSocket, ftp: PAsyncFTPClient) = +proc handleRead(s: AsyncSocket, ftp: AsyncFTPClient) = assert ftp.jobInProgress assert ftp.job.typ != JStore # This can never return true, because it shouldn't check for code @@ -219,19 +219,19 @@ proc handleRead(s: PAsyncSocket, ftp: PAsyncFTPClient) = proc pasv[T](ftp: FtpBase[T]) = ## Negotiate a data connection. - when T is TSocket: + when T is Socket: ftp.dsock = socket() if ftp.dsock == invalidSocket: raiseOSError(osLastError()) - elif T is PAsyncSocket: + elif T is AsyncSocket: ftp.dsock = asyncSocket() ftp.dsock.handleRead = - proc (s: PAsyncSocket) = + proc (s: AsyncSocket) = handleRead(s, ftp) ftp.dsock.handleConnect = - proc (s: PAsyncSocket) = + proc (s: AsyncSocket) = handleConnect(s, ftp) ftp.dsock.handleTask = - proc (s: PAsyncSocket) = + proc (s: AsyncSocket) = handleTask(s, ftp) ftp.disp.register(ftp.dsock) else: @@ -244,8 +244,8 @@ proc pasv[T](ftp: FtpBase[T]) = var ip = nums[0.. -3] var port = nums[-2.. -1] var properPort = port[0].parseInt()*256+port[1].parseInt() - ftp.dsock.connect(ip.join("."), TPort(properPort.toU16)) - when T is PAsyncSocket: + ftp.dsock.connect(ip.join("."), Port(properPort.toU16)) + when T is AsyncSocket: ftp.dsockConnected = false else: ftp.dsockConnected = true @@ -255,10 +255,10 @@ proc normalizePathSep(path: string): string = proc connect*[T](ftp: FtpBase[T]) = ## Connect to the FTP server specified by ``ftp``. - when T is PAsyncSocket: + when T is AsyncSocket: blockingOperation(ftp.csock): ftp.csock.connect(ftp.address, ftp.port) - elif T is TSocket: + elif T is Socket: ftp.csock.connect(ftp.address, ftp.port) else: {.fatal: "Incorrect socket instantiation".} @@ -292,13 +292,13 @@ proc getLines[T](ftp: FtpBase[T], async: bool = false): bool = ## It doesn't if `async` is true, because it doesn't check for 226 then. if ftp.dsockConnected: var r = TaintedString"" - when T is PAsyncSocket: + when T is AsyncSocket: if ftp.asyncDSock.readLine(r): if r.string == "": ftp.dsockConnected = false else: ftp.job.lines.add(r.string & "\n") - elif T is TSocket: + elif T is Socket: assert(not async) ftp.dsock.readLine(r) if r.string == "": @@ -309,7 +309,7 @@ proc getLines[T](ftp: FtpBase[T], async: bool = false): bool = {.fatal: "Incorrect socket instantiation".} if not async: - var readSocks: seq[TSocket] = @[ftp.csock] + var readSocks: seq[Socket] = @[ftp.csock] # This is only needed here. Asyncio gets this socket... blockingOperation(ftp.csock): if readSocks.select(1) != 0 and ftp.csock in readSocks: @@ -372,7 +372,7 @@ proc createDir*[T](ftp: FtpBase[T], dir: string, recursive: bool = false) = assertReply reply, "257" proc chmod*[T](ftp: FtpBase[T], path: string, - permissions: set[TFilePermission]) = + permissions: set[FilePermission]) = ## Changes permission of ``path`` to ``permissions``. var userOctal = 0 var groupOctal = 0 @@ -431,8 +431,8 @@ proc getFile[T](ftp: FtpBase[T], async = false): bool = var bytesRead = 0 var returned = false if async: - when T is TSocket: - raise newException(EFTP, "FTPClient must be async.") + when T is Socket: + raise newException(FTPError, "FTPClient must be async.") else: bytesRead = ftp.dsock.recvAsync(r, BufferSize) returned = bytesRead != -1 @@ -447,9 +447,9 @@ proc getFile[T](ftp: FtpBase[T], async = false): bool = elif returned and r2 == "": ftp.dsockConnected = false - when T is TSocket: + when T is Socket: if not async: - var readSocks: seq[TSocket] = @[ftp.csock] + var readSocks: seq[Socket] = @[ftp.csock] blockingOperation(ftp.csock): if readSocks.select(1) != 0 and ftp.csock in readSocks: assertReply ftp.expectReply(), "226" @@ -467,10 +467,10 @@ proc retrFile*[T](ftp: FtpBase[T], file, dest: string, async = false) = var reply = ftp.send("RETR " & file.normalizePathSep) assertReply reply, ["125", "150"] if {'(', ')'} notin reply.string: - raise newException(EInvalidReply, "Reply has no file size.") + raise newException(ReplyError, "Reply has no file size.") var fileSize: BiggestInt if reply.string.captureBetween('(', ')').parseBiggestInt(fileSize) == 0: - raise newException(EInvalidReply, "Reply has no file size.") + raise newException(ReplyError, "Reply has no file size.") ftp.job.total = fileSize ftp.job.lastProgressReport = epochTime() @@ -545,10 +545,10 @@ proc close*[T](ftp: FtpBase[T]) = ftp.csock.close() ftp.dsock.close() -proc csockHandleRead(s: PAsyncSocket, ftp: PAsyncFTPClient) = +proc csockHandleRead(s: AsyncSocket, ftp: AsyncFTPClient) = if ftp.jobInProgress: assertReply ftp.expectReply(), "226" # Make sure the transfer completed. - var r: TFTPEvent + var r: FTPEvent case ftp.job.typ of JRetrText: r.typ = EvLines @@ -557,21 +557,21 @@ proc csockHandleRead(s: PAsyncSocket, ftp: PAsyncFTPClient) = r.typ = EvRetr r.filename = ftp.job.filename if ftp.job.progress != ftp.job.total: - raise newException(EFTP, "Didn't download full file.") + raise newException(FTPError, "Didn't download full file.") of JStore: r.typ = EvStore r.filename = ftp.job.filename if ftp.job.progress != ftp.job.total: - raise newException(EFTP, "Didn't upload full file.") + raise newException(FTPError, "Didn't upload full file.") ftp.deleteJob() ftp.handleEvent(ftp, r) -proc asyncFTPClient*(address: string, port = TPort(21), +proc asyncFTPClient*(address: string, port = Port(21), user, pass = "", - handleEvent: proc (ftp: PAsyncFTPClient, ev: TFTPEvent) {.closure,gcsafe.} = - (proc (ftp: PAsyncFTPClient, ev: TFTPEvent) = discard)): PAsyncFTPClient = - ## Create a ``PAsyncFTPClient`` object. + handleEvent: proc (ftp: AsyncFTPClient, ev: FTPEvent) {.closure,gcsafe.} = + (proc (ftp: AsyncFTPClient, ev: FTPEvent) = discard)): AsyncFTPClient = + ## Create a ``AsyncFTPClient`` object. ## ## Use this if you want to use asyncio's dispatcher. var dres: AsyncFtpClient @@ -588,7 +588,7 @@ proc asyncFTPClient*(address: string, port = TPort(21), csockHandleRead(s, dres) result = dres -proc register*(d: PDispatcher, ftp: PAsyncFTPClient): PDelegate {.discardable.} = +proc register*(d: Dispatcher, ftp: AsyncFTPClient): Delegate {.discardable.} = ## Registers ``ftp`` with dispatcher ``d``. ftp.disp = d return ftp.disp.register(ftp.csock) diff --git a/lib/pure/htmlparser.nim b/lib/pure/htmlparser.nim index e2cbb4949c..5e4eba4e53 100644 --- a/lib/pure/htmlparser.nim +++ b/lib/pure/htmlparser.nim @@ -552,7 +552,7 @@ proc parse(x: var XmlParser, errors: var seq[string]): XmlNode = proc parseHtml*(s: Stream, filename: string, errors: var seq[string]): XmlNode = ## parses the XML from stream `s` and returns a ``PXmlNode``. Every - ## occured parsing error is added to the `errors` sequence. + ## occurred parsing error is added to the `errors` sequence. var x: XmlParser open(x, s, filename, {reportComments, reportWhitespace}) next(x) @@ -581,7 +581,7 @@ proc parseHtml*(s: Stream): XmlNode = proc loadHtml*(path: string, errors: var seq[string]): XmlNode = ## Loads and parses HTML from file specified by ``path``, and returns - ## a ``PXmlNode``. Every occured parsing error is added to + ## a ``PXmlNode``. Every occurred parsing error is added to ## the `errors` sequence. var s = newFileStream(path, fmRead) if s == nil: raise newException(IOError, "Unable to read file: " & path) diff --git a/lib/pure/httpclient.nim b/lib/pure/httpclient.nim index 3c14018870..37af14df30 100644 --- a/lib/pure/httpclient.nim +++ b/lib/pure/httpclient.nim @@ -385,7 +385,7 @@ proc request*(url: string, httpMethod: string, extraHeaders = "", userAgent = defUserAgent, proxy: Proxy = nil): Response = ## | Requests ``url`` with the custom method string specified by the ## | ``httpMethod`` parameter. - ## | Extra headers can be specified and must be seperated by ``\c\L`` + ## | Extra headers can be specified and must be separated by ``\c\L`` ## | An optional timeout can be specified in miliseconds, if reading from the ## server takes longer than specified an ETimeout exception will be raised. var r = if proxy == nil: parseUri(url) else: proxy.url @@ -436,7 +436,7 @@ proc request*(url: string, httpMethod = httpGET, extraHeaders = "", body = "", sslContext = defaultSSLContext, timeout = -1, userAgent = defUserAgent, proxy: Proxy = nil): Response = ## | Requests ``url`` with the specified ``httpMethod``. - ## | Extra headers can be specified and must be seperated by ``\c\L`` + ## | Extra headers can be specified and must be separated by ``\c\L`` ## | An optional timeout can be specified in miliseconds, if reading from the ## server takes longer than specified an ETimeout exception will be raised. result = request(url, $httpMethod, extraHeaders, body, sslContext, timeout, diff --git a/lib/pure/httpserver.nim b/lib/pure/httpserver.nim index 38a068ea1b..5efdbe297b 100644 --- a/lib/pure/httpserver.nim +++ b/lib/pure/httpserver.nim @@ -363,7 +363,7 @@ proc run*(handleRequest: proc (client: Socket, port = Port(80)) = ## encapsulates the server object and main loop var s: TServer - open(s, port) + open(s, port, reuseAddr = true) #echo("httpserver running on port ", s.port) while true: next(s) diff --git a/lib/pure/json.nim b/lib/pure/json.nim index 873e4b78ee..2038b246dc 100644 --- a/lib/pure/json.nim +++ b/lib/pure/json.nim @@ -30,13 +30,32 @@ ## ## 1.3000000000000000e+00 ## true +## +## This module can also be used to comfortably create JSON using the `%*` +## operator: +## +## .. code-block:: nim +## +## var hisName = "John" +## let herAge = 31 +## var j = %* +## [ +## { +## "name": hisName, +## "age": 30 +## }, +## { +## "name": "Susan", +## "age": herAge +## } +## ] import - hashes, strutils, lexbase, streams, unicode + hashes, strutils, lexbase, streams, unicode, macros type JsonEventKind* = enum ## enumeration of all events that may occur when parsing - jsonError, ## an error ocurred during parsing + jsonError, ## an error occurred during parsing jsonEof, ## end of file reached jsonString, ## a string literal jsonInt, ## an integer literal @@ -625,6 +644,29 @@ proc `%`*(elements: openArray[JsonNode]): JsonNode = newSeq(result.elems, elements.len) for i, p in pairs(elements): result.elems[i] = p +proc toJson(x: PNimrodNode): PNimrodNode {.compiletime.} = + case x.kind + of nnkBracket: + result = newNimNode(nnkBracket) + for i in 0 .. 16: + when sizeof(int) > 2: result = result or (result shr 16) - when sizeof(int) > 8: + when sizeof(int) > 1: result = result or (result shr 8) result = result or (result shr 4) result = result or (result shr 2) @@ -129,25 +129,25 @@ proc variance*(x: openArray[float]): float {.noSideEffect.} = result = result + diff*diff result = result / toFloat(len(x)) -proc random*(max: int): int {.gcsafe.} +proc random*(max: int): int {.benign.} ## returns a random number in the range 0..max-1. The sequence of ## random number is always the same, unless `randomize` is called ## which initializes the random number generator with a "random" ## number, i.e. a tickcount. -proc random*(max: float): float {.gcsafe.} +proc random*(max: float): float {.benign.} ## returns a random number in the range 0..` for negative exponents. + assert y >= 0 + var (x, y) = (x, y) + result = 1 + + while y != 0: + if (y and 1) != 0: + result *= x + y = y shr 1 + x *= x + +proc gcd*[T](x, y: T): T = + ## Computes the greatest common divisor of ``x`` and ``y``. + var (x,y) = (x,y) + while y != 0: + x = x mod y + swap x, y + abs x + +proc lcm*[T](x, y: T): T = + ## Computes the least common multiple of ``x`` and ``y``. + x div gcd(x, y) * y + when isMainModule and not defined(JS): proc gettime(dummy: ptr cint): cint {.importc: "time", header: "".} diff --git a/lib/pure/net.nim b/lib/pure/net.nim index 4eacfea784..bed7515420 100644 --- a/lib/pure/net.nim +++ b/lib/pure/net.nim @@ -60,7 +60,8 @@ type sslHasPeekChar: bool sslPeekChar: char of false: nil - + lastError: OSErrorCode ## stores the last error on this socket + Socket* = ref SocketImpl SOBool* = enum ## Boolean socket options. @@ -231,6 +232,15 @@ when defined(ssl): if SSLSetFd(socket.sslHandle, socket.fd) != 1: raiseSSLError() +proc getSocketError*(socket: Socket): OSErrorCode = + ## Checks ``osLastError`` for a valid error. If it has been reset it uses + ## the last error stored in the socket object. + result = osLastError() + if result == 0.OSErrorCode: + result = socket.lastError + if result == 0.OSErrorCode: + raise newException(OSError, "No valid socket error code available") + proc socketError*(socket: Socket, err: int = -1, async = false, lastError = (-1).OSErrorCode) = ## Raises an OSError based on the error code returned by ``SSLGetError`` @@ -258,7 +268,7 @@ proc socketError*(socket: Socket, err: int = -1, async = false, of SSL_ERROR_WANT_X509_LOOKUP: raiseSSLError("Function for x509 lookup has been called.") of SSL_ERROR_SYSCALL: - var errStr = "IO error has occured " + var errStr = "IO error has occurred " let sslErr = ErrPeekLastError() if sslErr == 0 and err == 0: errStr.add "because an EOF was observed that violates the protocol" @@ -276,7 +286,7 @@ proc socketError*(socket: Socket, err: int = -1, async = false, else: raiseSSLError("Unknown Error") if err == -1 and not (when defined(ssl): socket.isSSL else: false): - let lastE = if lastError.int == -1: osLastError() else: lastError + var lastE = if lastError.int == -1: getSocketError(socket) else: lastError if async: when useWinVersion: if lastE.int32 == WSAEWOULDBLOCK: @@ -294,7 +304,8 @@ proc listen*(socket: Socket, backlog = SOMAXCONN) {.tags: [ReadIOEffect].} = ## queue of pending connections. ## ## Raises an EOS error upon failure. - if listen(socket.fd, backlog) < 0'i32: raiseOSError(osLastError()) + if rawsockets.listen(socket.fd, backlog) < 0'i32: + raiseOSError(osLastError()) proc bindAddr*(socket: Socket, port = Port(0), address = "") {. tags: [ReadIOEffect].} = @@ -321,7 +332,8 @@ proc bindAddr*(socket: Socket, port = Port(0), address = "") {. dealloc(aiList) proc acceptAddr*(server: Socket, client: var Socket, address: var string, - flags = {SocketFlag.SafeDisconn}) {.tags: [ReadIOEffect].} = + flags = {SocketFlag.SafeDisconn}) {. + tags: [ReadIOEffect], gcsafe, locks: 0.} = ## Blocks until a connection is being made from a client. When a connection ## is made sets ``client`` to the client socket and ``address`` to the address ## of the connecting client. @@ -442,6 +454,8 @@ proc close*(socket: Socket) = # shutdown i.e not wait for the peers "close notify" alert with a second # call to SSLShutdown let res = SSLShutdown(socket.sslHandle) + SSLFree(socket.sslHandle) + socket.sslHandle = nil if res == 0: discard elif res != 1: @@ -568,6 +582,10 @@ proc readIntoBuf(socket: Socket, flags: int32): int = result = recv(socket.fd, addr(socket.buffer), cint(socket.buffer.high), flags) else: result = recv(socket.fd, addr(socket.buffer), cint(socket.buffer.high), flags) + if result < 0: + # Save it in case it gets reset (the Nim codegen occassionally may call + # Win API functions which reset it). + socket.lastError = osLastError() if result <= 0: socket.bufLen = 0 socket.currPos = 0 @@ -623,6 +641,9 @@ proc recv*(socket: Socket, data: pointer, size: int): int {.tags: [ReadIOEffect] result = recv(socket.fd, data, size.cint, 0'i32) else: result = recv(socket.fd, data, size.cint, 0'i32) + if result < 0: + # Save the error in case it gets reset. + socket.lastError = osLastError() proc waitFor(socket: Socket, waited: var float, timeout, size: int, funcName: string): int {.tags: [TimeEffect].} = @@ -695,7 +716,7 @@ proc recv*(socket: Socket, data: var string, size: int, timeout = -1, result = recv(socket, cstring(data), size, timeout) if result < 0: data.setLen(0) - let lastError = osLastError() + let lastError = getSocketError(socket) if flags.isDisconnectionError(lastError): return socket.socketError(result, lastError = lastError) data.setLen(result) @@ -743,7 +764,7 @@ proc readLine*(socket: Socket, line: var TaintedString, timeout = -1, line.add("\c\L") template raiseSockError(): stmt {.dirty, immediate.} = - let lastError = osLastError() + let lastError = getSocketError(socket) if flags.isDisconnectionError(lastError): setLen(line.string, 0); return socket.socketError(n, lastError = lastError) @@ -886,7 +907,7 @@ proc connectAsync(socket: Socket, name: string, port = Port(0), af: Domain = AF_INET) {.tags: [ReadIOEffect].} = ## A variant of ``connect`` for non-blocking sockets. ## - ## This procedure will immediatelly return, it will not block until a connection + ## This procedure will immediately return, it will not block until a connection ## is made. It is up to the caller to make sure the connection has been established ## by checking (using ``select``) whether the socket is writeable. ## @@ -938,8 +959,12 @@ proc connect*(socket: Socket, address: string, port = Port(0), timeout: int, doAssert socket.handshake() socket.fd.setBlocking(true) -proc isSsl*(socket: Socket): bool = return socket.isSSL +proc isSsl*(socket: Socket): bool = ## Determines whether ``socket`` is a SSL socket. + when defined(ssl): + result = socket.isSSL + else: + result = false proc getFd*(socket: Socket): SocketHandle = return socket.fd ## Returns the socket's file descriptor diff --git a/lib/pure/nimprof.nim b/lib/pure/nimprof.nim index 3598cdd3ad..cce2a20ae8 100644 --- a/lib/pure/nimprof.nim +++ b/lib/pure/nimprof.nim @@ -132,7 +132,7 @@ else: proc hook(st: TStackTrace) {.nimcall.} = if interval == 0: hookAux(st, 1) - elif getTicks() - t0 > interval: + elif int64(t0) == 0 or getTicks() - t0 > interval: hookAux(st, 1) t0 = getTicks() diff --git a/lib/pure/nimprof.nimrod.cfg b/lib/pure/nimprof.nim.cfg similarity index 100% rename from lib/pure/nimprof.nimrod.cfg rename to lib/pure/nimprof.nim.cfg diff --git a/lib/pure/os.nim b/lib/pure/os.nim index 147614d3de..bf581667bd 100644 --- a/lib/pure/os.nim +++ b/lib/pure/os.nim @@ -185,7 +185,7 @@ const proc osErrorMsg*(): string {.rtl, extern: "nos$1", deprecated.} = ## Retrieves the operating system's error flag, ``errno``. ## On Windows ``GetLastError`` is checked before ``errno``. - ## Returns "" if no error occured. + ## Returns "" if no error occurred. ## ## **Deprecated since version 0.9.4**: use the other ``osErrorMsg`` proc. @@ -1010,8 +1010,16 @@ proc copyFile*(source, dest: string) {.rtl, extern: "nos$1", proc moveFile*(source, dest: string) {.rtl, extern: "nos$1", tags: [ReadIOEffect, WriteIOEffect].} = ## Moves a file from `source` to `dest`. If this fails, `OSError` is raised. - if c_rename(source, dest) != 0'i32: - raise newException(OSError, $strerror(errno)) + when defined(Windows): + when useWinUnicode: + let s = newWideCString(source) + let d = newWideCString(dest) + if moveFileW(s, d, 0'i32) == 0'i32: raiseOSError(osLastError()) + else: + if moveFileA(source, dest, 0'i32) == 0'i32: raiseOSError(osLastError()) + else: + if c_rename(source, dest) != 0'i32: + raise newException(OSError, $strerror(errno)) when not declared(ENOENT) and not defined(Windows): when NoFakeVars: @@ -1074,8 +1082,12 @@ when defined(windows): # because we support Windows GUI applications, things get really # messy here... when useWinUnicode: - proc strEnd(cstr: WideCString, c = 0'i32): WideCString {. - importc: "wcschr", header: "".} + when defined(cpp): + proc strEnd(cstr: WideCString, c = 0'i32): WideCString {. + importcpp: "(NI16*)wcschr((const wchar_t *)#, #)", header: "".} + else: + proc strEnd(cstr: WideCString, c = 0'i32): WideCString {. + importc: "wcschr", header: "".} else: proc strEnd(cstr: cstring, c = 0'i32): cstring {. importc: "strchr", header: "".} @@ -1087,7 +1099,7 @@ when defined(windows): var env = getEnvironmentStringsW() e = env - if e == nil: return # an error occured + if e == nil: return # an error occurred while true: var eend = strEnd(e) add(environment, $e) @@ -1098,7 +1110,7 @@ when defined(windows): var env = getEnvironmentStringsA() e = env - if e == nil: return # an error occured + if e == nil: return # an error occurred while true: var eend = strEnd(e) add(environment, $e) @@ -1170,7 +1182,7 @@ proc putEnv*(key, val: string) {.tags: [WriteEnvEffect].} = ## If an error occurs, `EInvalidEnvVar` is raised. # Note: by storing the string in the environment sequence, - # we gurantee that we don't free the memory before the program + # we guarantee that we don't free the memory before the program # ends (this is needed for POSIX compliance). It is also needed so that # the process itself may access its modified environment variables! var indx = findEnvVar(key) @@ -1287,8 +1299,16 @@ iterator walkDir*(dir: string): tuple[kind: PathComponent, path: string] {. if y != "." and y != "..": var s: TStat y = dir / y - if lstat(y, s) < 0'i32: break var k = pcFile + + when defined(linux) or defined(macosx) or defined(bsd): + if x.d_type != DT_UNKNOWN: + if x.d_type == DT_DIR: k = pcDir + if x.d_type == DT_LNK: k = succ(k) + yield (k, y) + continue + + if lstat(y, s) < 0'i32: break if S_ISDIR(s.st_mode): k = pcDir if S_ISLNK(s.st_mode): k = succ(k) yield (k, y) @@ -1335,7 +1355,7 @@ proc rawRemoveDir(dir: string) = if rmdir(dir) != 0'i32 and errno != ENOENT: raiseOSError(osLastError()) proc removeDir*(dir: string) {.rtl, extern: "nos$1", tags: [ - WriteDirEffect, ReadDirEffect].} = + WriteDirEffect, ReadDirEffect], benign.} = ## Removes the directory `dir` including all subdirectories and files ## in `dir` (recursively). ## @@ -1381,7 +1401,7 @@ proc createDir*(dir: string) {.rtl, extern: "nos$1", tags: [WriteDirEffect].} = rawCreateDir(dir) proc copyDir*(source, dest: string) {.rtl, extern: "nos$1", - tags: [WriteIOEffect, ReadIOEffect].} = + tags: [WriteIOEffect, ReadIOEffect], benign.} = ## Copies a directory from `source` to `dest`. ## ## If this fails, `OSError` is raised. On the Windows platform this proc will @@ -1442,7 +1462,7 @@ proc createHardlink*(src, dest: string) = proc parseCmdLine*(c: string): seq[string] {. noSideEffect, rtl, extern: "nos$1".} = ## Splits a command line into several components; - ## This proc is only occassionally useful, better use the `parseopt` module. + ## This proc is only occasionally useful, better use the `parseopt` module. ## ## On Windows, it uses the following parsing rules ## (see http://msdn.microsoft.com/en-us/library/17w5ykft.aspx ): @@ -1554,7 +1574,7 @@ proc copyFileWithPermissions*(source, dest: string, proc copyDirWithPermissions*(source, dest: string, ignorePermissionErrors = true) {.rtl, extern: "nos$1", - tags: [WriteIOEffect, ReadIOEffect].} = + tags: [WriteIOEffect, ReadIOEffect], benign.} = ## Copies a directory from `source` to `dest` preserving file permissions. ## ## If this fails, `OSError` is raised. This is a wrapper proc around `copyDir() diff --git a/lib/pure/osproc.nim b/lib/pure/osproc.nim index 6361dfb092..cd37000199 100644 --- a/lib/pure/osproc.nim +++ b/lib/pure/osproc.nim @@ -146,7 +146,7 @@ proc startProcess*(command: string, ## of `args` to `command` carefully escaping/quoting any special characters, ## since it will be passed *as is* to the system shell. Each system/shell may ## feature different escaping rules, so try to avoid this kind of shell - ## invokation if possible as it leads to non portable software. + ## invocation if possible as it leads to non portable software. ## ## Return value: The newly created process object. Nil is never returned, ## but ``EOS`` is raised in case of an error. @@ -260,7 +260,7 @@ proc execProcesses*(cmds: openArray[string], for i in 0..m-1: if beforeRunEvent != nil: beforeRunEvent(i) - q[i] = startCmd(cmds[i], options=options) + q[i] = startProcess(cmds[i], options=options + {poEvalCommand}) when defined(noBusyWaiting): var r = 0 for i in m..high(cmds): @@ -275,7 +275,7 @@ proc execProcesses*(cmds: openArray[string], if q[r] != nil: close(q[r]) if beforeRunEvent != nil: beforeRunEvent(i) - q[r] = startCmd(cmds[i], options=options) + q[r] = startProcess(cmds[i], options=options + {poEvalCommand}) r = (r + 1) mod n else: var i = m @@ -288,7 +288,7 @@ proc execProcesses*(cmds: openArray[string], if q[r] != nil: close(q[r]) if beforeRunEvent != nil: beforeRunEvent(i) - q[r] = startCmd(cmds[i], options=options) + q[r] = startProcess(cmds[i], options=options + {poEvalCommand}) inc(i) if i > high(cmds): break for j in 0..m-1: @@ -298,7 +298,7 @@ proc execProcesses*(cmds: openArray[string], for i in 0..high(cmds): if beforeRunEvent != nil: beforeRunEvent(i) - var p = startCmd(cmds[i], options=options) + var p = startProcess(cmds[i], options=options + {poEvalCommand}) result = max(waitForExit(p), result) close(p) @@ -644,14 +644,14 @@ elif not defined(useNimRtl): var pid: TPid var sysArgs = allocCStringArray(sysArgsRaw) - finally: deallocCStringArray(sysArgs) + defer: deallocCStringArray(sysArgs) var sysEnv = if env == nil: envToCStringArray() else: envToCStringArray(env) - finally: deallocCStringArray(sysEnv) + defer: deallocCStringArray(sysEnv) var data: TStartProcessData data.sysCommand = sysCommand @@ -748,7 +748,7 @@ elif not defined(useNimRtl): if pipe(data.pErrorPipe) != 0: raiseOSError(osLastError()) - finally: + defer: discard close(data.pErrorPipe[readIdx]) var pid: TPid @@ -956,7 +956,7 @@ proc execCmdEx*(command: string, options: set[ProcessOption] = { exitCode: int] {.tags: [ExecIOEffect, ReadIOEffect], gcsafe.} = ## a convenience proc that runs the `command`, grabs all its output and ## exit code and returns both. - var p = startCmd(command, options) + var p = startProcess(command, options=options + {poEvalCommand}) var outp = outputStream(p) result = (TaintedString"", -1) var line = newStringOfCap(120).TaintedString diff --git a/lib/pure/parsecfg.nim b/lib/pure/parsecfg.nim index bb9d2aed25..bb64c8134f 100644 --- a/lib/pure/parsecfg.nim +++ b/lib/pure/parsecfg.nim @@ -35,7 +35,7 @@ type cfgSectionStart, ## a ``[section]`` has been parsed cfgKeyValuePair, ## a ``key=value`` pair has been detected cfgOption, ## a ``--key=value`` command line option - cfgError ## an error ocurred during parsing + cfgError ## an error occurred during parsing CfgEvent* = object of RootObj ## describes a parsing event case kind*: CfgEventKind ## the kind of the event diff --git a/lib/pure/parseopt.nim b/lib/pure/parseopt.nim index b1e2444c3c..4c92a7cdfe 100644 --- a/lib/pure/parseopt.nim +++ b/lib/pure/parseopt.nim @@ -11,9 +11,12 @@ ## It supports one convenience iterator over all command line options and some ## lower-level features. ## -## **Deprecated since version 0.9.3:** Use the `parseopt2 `_ -## module instead as this version has issues with spaces in arguments. -{.deprecated.} +## Supported syntax: +## +## 1. short options - ``-abcd``, where a, b, c, d are names +## 2. long option - ``--foo:bar``, ``--foo=bar`` or ``--foo`` +## 3. argument - everything else + {.push debugger: off.} include "system/inclrtl" diff --git a/lib/pure/parseopt2.nim b/lib/pure/parseopt2.nim index 8ed519fa4b..73b498fe02 100644 --- a/lib/pure/parseopt2.nim +++ b/lib/pure/parseopt2.nim @@ -60,7 +60,7 @@ proc initOptParser*(cmdline: string): OptParser {.rtl, deprecated.} = ## Initalizes option parses with cmdline. Splits cmdline in on spaces ## and calls initOptParser(openarray[string]) ## Do not use. - if cmdline == "": # backward compatibilty + if cmdline == "": # backward compatibility return initOptParser(seq[string](nil)) else: return initOptParser(cmdline.split) diff --git a/lib/pure/parseutils.nim b/lib/pure/parseutils.nim index 2c677fdc20..eb649a878c 100644 --- a/lib/pure/parseutils.nim +++ b/lib/pure/parseutils.nim @@ -181,7 +181,7 @@ proc parseWhile*(s: string, token: var string, validChars: set[char], token = substr(s, start, i-1) proc captureBetween*(s: string, first: char, second = '\0', start = 0): string = - ## Finds the first occurence of ``first``, then returns everything from there + ## Finds the first occurrence of ``first``, then returns everything from there ## up to ``second``(if ``second`` is '\0', then ``first`` is used). var i = skipUntil(s, first, start)+1+start result = "" @@ -240,7 +240,7 @@ proc parseBiggestFloat*(s: string, number: var BiggestFloat, start = 0): int {. proc parseFloat*(s: string, number: var float, start = 0): int {. rtl, extern: "npuParseFloat", noSideEffect.} = ## parses a float starting at `start` and stores the value into `number`. - ## Result is the number of processed chars or 0 if there occured a parsing + ## Result is the number of processed chars or 0 if there occurred a parsing ## error. var bf: BiggestFloat result = parseBiggestFloat(s, bf, start) diff --git a/lib/pure/parsexml.nim b/lib/pure/parsexml.nim index 39dead3c00..b957c0cf7b 100644 --- a/lib/pure/parsexml.nim +++ b/lib/pure/parsexml.nim @@ -57,7 +57,7 @@ import type XmlEventKind* = enum ## enumation of all events that may occur when parsing - xmlError, ## an error ocurred during parsing + xmlError, ## an error occurred during parsing xmlEof, ## end of file reached xmlCharData, ## character data xmlWhitespace, ## whitespace has been parsed diff --git a/lib/pure/rationals.nim b/lib/pure/rationals.nim new file mode 100644 index 0000000000..04aa8316a3 --- /dev/null +++ b/lib/pure/rationals.nim @@ -0,0 +1,275 @@ +# +# +# Nim's Runtime Library +# (c) Copyright 2015 Dennis Felsing +# +# See the file "copying.txt", included in this +# distribution, for details about the copyright. +# + + +## This module implements rational numbers, consisting of a numerator `num` and +## a denominator `den`, both of type int. The denominator can not be 0. + +import math + +type Rational*[T] = object + ## a rational number, consisting of a numerator and denominator + num*, den*: T + +proc initRational*[T](num, den: T): Rational[T] = + ## Create a new rational number. + result.num = num + result.den = den + +proc `//`*[T](num, den: T): Rational[T] = initRational[T](num, den) + ## A friendlier version of `initRational`. Example usage: + ## + ## .. code-block:: nim + ## var x = 1//3 + 1//5 + +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] = + ## Convert some integer `x` to a rational number. + result.num = x + result.den = 1 + +proc toFloat*[T](x: Rational[T]): float = + ## Convert a rational number `x` to a float. + x.num / x.den + +proc toInt*[T](x: Rational[T]): int = + ## Convert a rational number `x` to an int. Conversion rounds towards 0 if + ## `x` does not contain an integer value. + x.num div x.den + +proc reduce*[T](x: var Rational[T]) = + ## Reduce rational `x`. + let common = gcd(x.num, x.den) + if x.den > 0: + x.num = x.num div common + x.den = x.den div common + elif x.den < 0: + x.num = -x.num div common + x.den = -x.den div common + else: + raise newException(DivByZeroError, "division by zero") + +proc `+` *[T](x, y: Rational[T]): Rational[T] = + ## Add two rational numbers. + let common = lcm(x.den, y.den) + result.num = common div x.den * x.num + common div y.den * y.num + result.den = common + reduce(result) + +proc `+` *[T](x: Rational[T], y: T): Rational[T] = + ## Add rational `x` to int `y`. + result.num = x.num + y * x.den + result.den = x.den + +proc `+` *[T](x: T, y: Rational[T]): Rational[T] = + ## Add int `x` to rational `y`. + result.num = x * y.den + y.num + result.den = y.den + +proc `+=` *[T](x: var Rational[T], y: Rational[T]) = + ## Add rational `y` to rational `x`. + let common = lcm(x.den, y.den) + x.num = common div x.den * x.num + common div y.den * y.num + x.den = common + reduce(x) + +proc `+=` *[T](x: var Rational[T], y: T) = + ## Add int `y` to rational `x`. + x.num += y * x.den + +proc `-` *[T](x: Rational[T]): Rational[T] = + ## Unary minus for rational numbers. + result.num = -x.num + result.den = x.den + +proc `-` *[T](x, y: Rational[T]): Rational[T] = + ## Subtract two rational numbers. + let common = lcm(x.den, y.den) + result.num = common div x.den * x.num - common div y.den * y.num + result.den = common + reduce(result) + +proc `-` *[T](x: Rational[T], y: T): Rational[T] = + ## Subtract int `y` from rational `x`. + result.num = x.num - y * x.den + result.den = x.den + +proc `-` *[T](x: T, y: Rational[T]): Rational[T] = + ## Subtract rational `y` from int `x`. + result.num = - x * y.den + y.num + result.den = y.den + +proc `-=` *[T](x: var Rational[T], y: Rational[T]) = + ## Subtract rational `y` from rational `x`. + let common = lcm(x.den, y.den) + x.num = common div x.den * x.num - common div y.den * y.num + x.den = common + reduce(x) + +proc `-=` *[T](x: var Rational[T], y: T) = + ## Subtract int `y` from rational `x`. + x.num -= y * x.den + +proc `*` *[T](x, y: Rational[T]): Rational[T] = + ## Multiply two rational numbers. + result.num = x.num * y.num + result.den = x.den * y.den + reduce(result) + +proc `*` *[T](x: Rational[T], y: T): Rational[T] = + ## Multiply rational `x` with int `y`. + result.num = x.num * y + result.den = x.den + reduce(result) + +proc `*` *[T](x: T, y: Rational[T]): Rational[T] = + ## Multiply int `x` with rational `y`. + result.num = x * y.num + result.den = y.den + reduce(result) + +proc `*=` *[T](x: var Rational[T], y: Rational[T]) = + ## Multiply rationals `y` to `x`. + x.num *= y.num + x.den *= y.den + reduce(x) + +proc `*=` *[T](x: var Rational[T], y: T) = + ## Multiply int `y` to rational `x`. + x.num *= y + reduce(x) + +proc reciprocal*[T](x: Rational[T]): Rational[T] = + ## Calculate the reciprocal of `x`. (1/x) + if x.num > 0: + result.num = x.den + result.den = x.num + elif x.num < 0: + result.num = -x.den + result.den = -x.num + else: + raise newException(DivByZeroError, "division by zero") + +proc `/`*[T](x, y: Rational[T]): Rational[T] = + ## Divide rationals `x` by `y`. + result.num = x.num * y.den + result.den = x.den * y.num + reduce(result) + +proc `/`*[T](x: Rational[T], y: T): Rational[T] = + ## Divide rational `x` by int `y`. + result.num = x.num + result.den = x.den * y + reduce(result) + +proc `/`*[T](x: T, y: Rational[T]): Rational[T] = + ## Divide int `x` by Rational `y`. + result.num = x * y.den + result.den = y.num + reduce(result) + +proc `/=`*[T](x: var Rational[T], y: Rational[T]) = + ## Divide rationals `x` by `y` in place. + x.num *= y.den + x.den *= y.num + reduce(x) + +proc `/=`*[T](x: var Rational[T], y: T) = + ## Divide rational `x` by int `y` in place. + x.den *= y + reduce(x) + +proc cmp*(x, y: Rational): int = + ## Compares two rationals. + (x - y).num + +proc `<` *(x, y: Rational): bool = + (x - y).num < 0 + +proc `<=` *(x, y: Rational): bool = + (x - y).num <= 0 + +proc `==` *(x, y: Rational): bool = + (x - y).num == 0 + +proc abs*[T](x: Rational[T]): Rational[T] = + result.num = abs x.num + result.den = abs x.den + +when isMainModule: + var + z = Rational[int](num: 0, den: 1) + o = initRational(num=1, den=1) + a = initRational(1, 2) + b = -1 // -2 + m1 = -1 // 1 + tt = 10 // 2 + + assert( a == a ) + assert( (a-a) == z ) + assert( (a+b) == o ) + assert( (a/b) == o ) + assert( (a*b) == 1 // 4 ) + assert( (3/a) == 6 // 1 ) + assert( (a/3) == 1 // 6 ) + assert( a*b == 1 // 4 ) + assert( tt*z == z ) + assert( 10*a == tt ) + assert( a*10 == tt ) + assert( tt/10 == a ) + assert( a-m1 == 3 // 2 ) + assert( a+m1 == -1 // 2 ) + assert( m1+tt == 16 // 4 ) + assert( m1-tt == 6 // -1 ) + + assert( z < o ) + assert( z <= o ) + assert( z == z ) + assert( cmp(z, o) < 0 ) + assert( cmp(o, z) > 0 ) + + assert( o == o ) + assert( o >= o ) + assert( not(o > o) ) + assert( cmp(o, o) == 0 ) + assert( cmp(z, z) == 0 ) + + assert( a == b ) + assert( a >= b ) + assert( not(b > a) ) + assert( cmp(a, b) == 0 ) + + var x = 1//3 + + x *= 5//1 + assert( x == 5//3 ) + x += 2 // 9 + assert( x == 17//9 ) + x -= 9//18 + assert( x == 25//18 ) + x /= 1//2 + assert( x == 50//18 ) + + var y = 1//3 + + y *= 4 + assert( y == 4//3 ) + y += 5 + assert( y == 19//3 ) + y -= 2 + assert( y == 13//3 ) + y /= 9 + assert( y == 13//27 ) + + assert toRational[int, int](5) == 5//1 + assert abs(toFloat(y) - 0.4814814814814815) < 1.0e-7 + assert toInt(z) == 0 diff --git a/lib/pure/ropes.nim b/lib/pure/ropes.nim index 995dff2aa2..4cc64a1543 100644 --- a/lib/pure/ropes.nim +++ b/lib/pure/ropes.nim @@ -43,7 +43,7 @@ proc isConc(r: Rope): bool {.inline.} = return isNil(r.data) # Note that the left and right pointers are not needed for leafs. # Leaves have relatively high memory overhead (~30 bytes on a 32 # bit machine) and we produce many of them. This is why we cache and -# share leafs accross different rope trees. +# share leafs across different rope trees. # To cache them they are inserted in another tree, a splay tree for best # performance. But for the caching tree we use the leaf's left and right # pointers. diff --git a/lib/pure/selectors.nim b/lib/pure/selectors.nim index bd2564937f..b6bc9dd3a4 100644 --- a/lib/pure/selectors.nim +++ b/lib/pure/selectors.nim @@ -35,7 +35,7 @@ type when defined(nimdoc): type Selector* = ref object - ## An object which holds file descripters to be checked for read/write + ## An object which holds file descriptors to be checked for read/write ## status. fds: Table[SocketHandle, SelectorKey] @@ -48,12 +48,21 @@ when defined(nimdoc): events: set[Event]): SelectorKey {.discardable.} = ## Updates the events which ``fd`` wants notifications for. + proc unregister*(s: Selector, fd: SocketHandle): SelectorKey {.discardable.} = + ## Unregisters file descriptor ``fd`` from selector ``s``. + + proc close*(s: Selector) = + ## Closes the selector + proc select*(s: Selector, timeout: int): seq[ReadyInfo] = ## The ``events`` field of the returned ``key`` contains the original events ## for which the ``fd`` was bound. This is contrary to the ``events`` field ## of the ``TReadyInfo`` tuple which determines which events are ready ## on the ``fd``. + proc newSelector*(): Selector = + ## Creates a new selector + proc contains*(s: Selector, fd: SocketHandle): bool = ## Determines whether selector contains a file descriptor. @@ -78,8 +87,6 @@ elif defined(linux): proc register*(s: Selector, fd: SocketHandle, events: set[Event], data: RootRef): SelectorKey {.discardable.} = - ## Registers file descriptor ``fd`` to selector ``s`` with a set of TEvent - ## ``events``. var event = createEventStruct(events, fd) if events != {}: if epoll_ctl(s.epollFD, EPOLL_CTL_ADD, fd, addr(event)) != 0: @@ -92,7 +99,6 @@ elif defined(linux): proc update*(s: Selector, fd: SocketHandle, events: set[Event]): SelectorKey {.discardable.} = - ## Updates the events which ``fd`` wants notifications for. if s.fds[fd].events != events: if events == {}: # This fd is idle -- it should not be registered to epoll. @@ -204,7 +210,6 @@ elif not defined(nimdoc): proc update*(s: Selector, fd: SocketHandle, events: set[Event]): SelectorKey {.discardable.} = - ## Updates the events which ``fd`` wants notifications for. if not s.fds.hasKey(fd): raise newException(ValueError, "File descriptor not found.") @@ -299,7 +304,7 @@ when isMainModule and not defined(nimdoc): sock: Socket var sock = socket() - if sock == sockets.InvalidSocket: raiseOSError(osLastError()) + if sock == sockets.invalidSocket: raiseOSError(osLastError()) #sock.setBlocking(false) sock.connect("irc.freenode.net", Port(6667)) diff --git a/lib/pure/smtp.nimrod.cfg b/lib/pure/smtp.nim.cfg similarity index 100% rename from lib/pure/smtp.nimrod.cfg rename to lib/pure/smtp.nim.cfg diff --git a/lib/pure/sockets.nim b/lib/pure/sockets.nim index 11eeefcb9a..3afb545c8d 100644 --- a/lib/pure/sockets.nim +++ b/lib/pure/sockets.nim @@ -657,6 +657,8 @@ proc close*(socket: Socket) = when defined(ssl): if socket.isSSL: discard SSLShutdown(socket.sslHandle) + SSLFree(socket.sslHandle) + socket.sslHandle = nil proc getServByName*(name, proto: string): Servent {.tags: [ReadIOEffect].} = ## Searches the database from the beginning and finds the first entry for @@ -851,7 +853,7 @@ proc connectAsync*(socket: Socket, name: string, port = Port(0), af: Domain = AF_INET) {.tags: [ReadIOEffect].} = ## A variant of ``connect`` for non-blocking sockets. ## - ## This procedure will immediatelly return, it will not block until a connection + ## This procedure will immediately return, it will not block until a connection ## is made. It is up to the caller to make sure the connection has been established ## by checking (using ``select``) whether the socket is writeable. ## @@ -1467,7 +1469,7 @@ proc recvAsync*(socket: Socket, s: var TaintedString): bool {. of SSL_ERROR_ZERO_RETURN: raiseSslError("TLS/SSL connection failed to initiate, socket closed prematurely.") of SSL_ERROR_WANT_CONNECT, SSL_ERROR_WANT_ACCEPT: - raiseSslError("Unexpected error occured.") # This should just not happen. + raiseSslError("Unexpected error occurred.") # This should just not happen. of SSL_ERROR_WANT_WRITE, SSL_ERROR_WANT_READ: return false of SSL_ERROR_WANT_X509_LOOKUP: @@ -1610,7 +1612,7 @@ proc sendAsync*(socket: Socket, data: string): int {.tags: [WriteIOEffect].} = of SSL_ERROR_ZERO_RETURN: raiseSslError("TLS/SSL connection failed to initiate, socket closed prematurely.") of SSL_ERROR_WANT_CONNECT, SSL_ERROR_WANT_ACCEPT: - raiseSslError("Unexpected error occured.") # This should just not happen. + raiseSslError("Unexpected error occurred.") # This should just not happen. of SSL_ERROR_WANT_WRITE, SSL_ERROR_WANT_READ: return 0 of SSL_ERROR_WANT_X509_LOOKUP: diff --git a/lib/pure/streams.nim b/lib/pure/streams.nim index 55351ffd44..67c80e5926 100644 --- a/lib/pure/streams.nim +++ b/lib/pure/streams.nim @@ -122,41 +122,41 @@ proc read[T](s: Stream, result: var T) = raise newEIO("cannot read from stream") proc readChar*(s: Stream): char = - ## reads a char from the stream `s`. Raises `EIO` if an error occured. + ## reads a char from the stream `s`. Raises `EIO` if an error occurred. ## Returns '\0' as an EOF marker. if readData(s, addr(result), sizeof(result)) != 1: result = '\0' proc readBool*(s: Stream): bool = - ## reads a bool from the stream `s`. Raises `EIO` if an error occured. + ## reads a bool from the stream `s`. Raises `EIO` if an error occurred. read(s, result) proc readInt8*(s: Stream): int8 = - ## reads an int8 from the stream `s`. Raises `EIO` if an error occured. + ## reads an int8 from the stream `s`. Raises `EIO` if an error occurred. read(s, result) proc readInt16*(s: Stream): int16 = - ## reads an int16 from the stream `s`. Raises `EIO` if an error occured. + ## reads an int16 from the stream `s`. Raises `EIO` if an error occurred. read(s, result) proc readInt32*(s: Stream): int32 = - ## reads an int32 from the stream `s`. Raises `EIO` if an error occured. + ## reads an int32 from the stream `s`. Raises `EIO` if an error occurred. read(s, result) proc readInt64*(s: Stream): int64 = - ## reads an int64 from the stream `s`. Raises `EIO` if an error occured. + ## reads an int64 from the stream `s`. Raises `EIO` if an error occurred. read(s, result) proc readFloat32*(s: Stream): float32 = - ## reads a float32 from the stream `s`. Raises `EIO` if an error occured. + ## reads a float32 from the stream `s`. Raises `EIO` if an error occurred. read(s, result) proc readFloat64*(s: Stream): float64 = - ## reads a float64 from the stream `s`. Raises `EIO` if an error occured. + ## reads a float64 from the stream `s`. Raises `EIO` if an error occurred. read(s, result) proc readStr*(s: Stream, length: int): TaintedString = ## reads a string of length `length` from the stream `s`. Raises `EIO` if - ## an error occured. + ## an error occurred. result = newString(length).TaintedString var L = readData(s, addr(string(result)[0]), length) if L != length: setLen(result.string, L) @@ -183,7 +183,7 @@ proc readLine*(s: Stream, line: var TaintedString): bool = proc readLine*(s: Stream): TaintedString = ## Reads a line from a stream `s`. Note: This is not very efficient. Raises - ## `EIO` if an error occured. + ## `EIO` if an error occurred. result = TaintedString"" while true: var c = readChar(s) diff --git a/lib/pure/strtabs.nim b/lib/pure/strtabs.nim index 5b7149d8e3..727d5a386d 100644 --- a/lib/pure/strtabs.nim +++ b/lib/pure/strtabs.nim @@ -112,7 +112,7 @@ proc `[]`*(t: StringTableRef, key: string): string {.rtl, extern: "nstGet".} = proc mget*(t: StringTableRef, key: string): var string {. rtl, extern: "nstTake".} = ## retrieves the location at ``t[key]``. If `key` is not in `t`, the - ## ``EInvalidKey`` exception is raised. + ## ``KeyError`` exception is raised. var index = rawGet(t, key) if index >= 0: result = t.data[index].val else: raise newException(KeyError, "key does not exist: " & key) @@ -158,7 +158,7 @@ proc getValue(t: StringTableRef, flags: set[FormatFlag], key: string): string = else: result = "" if result.len == 0: if useKey in flags: result = '$' & key - elif not (useEmpty in flags): raiseFormatException(key) + elif useEmpty notin flags: raiseFormatException(key) proc newStringTable*(mode: StringTableMode): StringTableRef {. rtl, extern: "nst$1".} = diff --git a/lib/pure/strutils.nim b/lib/pure/strutils.nim index e17d99dc20..655203cdae 100644 --- a/lib/pure/strutils.nim +++ b/lib/pure/strutils.nim @@ -395,11 +395,13 @@ proc toHex*(x: BiggestInt, len: int): string {.noSideEffect, const HexChars = "0123456789ABCDEF" var - shift: BiggestInt + n = x result = newString(len) for j in countdown(len-1, 0): - result[j] = HexChars[toU32(x shr shift) and 0xF'i32] - shift = shift + 4 + result[j] = HexChars[n and 0xF] + n = n shr 4 + # handle negative overflow + if n == 0 and x < 0: n = -1 proc intToStr*(x: int, minchars: int = 1): string {.noSideEffect, rtl, extern: "nsuIntToStr".} = @@ -497,26 +499,47 @@ proc parseEnum*[T: enum](s: string, default: T): T = return e result = default -proc repeatChar*(count: int, c: char = ' '): string {.noSideEffect, +proc repeat*(c: char, count: int): string {.noSideEffect, rtl, extern: "nsuRepeatChar".} = ## Returns a string of length `count` consisting only of ## the character `c`. You can use this proc to left align strings. Example: ## ## .. code-block:: nim + ## proc tabexpand(indent: int, text: string, tabsize: int = 4) = + ## echo '\t'.repeat(indent div tabsize), ' '.repeat(indent mod tabsize), text + ## + ## tabexpand(4, "At four") + ## tabexpand(5, "At five") + ## tabexpand(6, "At six") + result = newString(count) + for i in 0..count-1: result[i] = c + +proc repeat*(s: string, n: int): string {.noSideEffect, + rtl, extern: "nsuRepeatStr".} = + ## Returns String `s` concatenated `n` times. Example: + ## + ## .. code-block:: nim + ## echo "+++ STOP ".repeat(4), "+++" + result = newStringOfCap(n * s.len) + for i in 1..n: result.add(s) + +template spaces*(n: int): string = repeat(' ',n) + ## Returns a String with `n` space characters. You can use this proc + ## to left align strings. Example: + ## + ## .. code-block:: nim ## let ## width = 15 ## text1 = "Hello user!" ## text2 = "This is a very long string" - ## echo text1 & repeatChar(max(0, width - text1.len)) & "|" - ## echo text2 & repeatChar(max(0, width - text2.len)) & "|" - result = newString(count) - for i in 0..count-1: result[i] = c + ## echo text1 & spaces(max(0, width - text1.len)) & "|" + ## echo text2 & spaces(max(0, width - text2.len)) & "|" -proc repeatStr*(count: int, s: string): string {.noSideEffect, - rtl, extern: "nsuRepeatStr".} = - ## Returns `s` concatenated `count` times. - result = newStringOfCap(count*s.len) - for i in 0..count-1: result.add(s) +proc repeatChar*(count: int, c: char = ' '): string {.deprecated.} = repeat(c, count) + ## deprecated: use repeat() or spaces() + +proc repeatStr*(count: int, s: string): string {.deprecated.} = repeat(s, count) + ## deprecated: use repeat(string, count) or string.repeat(count) proc align*(s: string, count: int, padding = ' '): string {. noSideEffect, rtl, extern: "nsuAlignString".} = @@ -815,8 +838,8 @@ proc rfind*(s: string, sub: char, start: int = -1): int {.noSideEffect, proc count*(s: string, sub: string, overlapping: bool = false): int {.noSideEffect, rtl, extern: "nsuCountString".} = - ## Count the occurences of a substring `sub` in the string `s`. - ## Overlapping occurences of `sub` only count when `overlapping` + ## Count the occurrences of a substring `sub` in the string `s`. + ## Overlapping occurrences of `sub` only count when `overlapping` ## is set to true. var i = 0 while true: @@ -831,14 +854,14 @@ proc count*(s: string, sub: string, overlapping: bool = false): int {.noSideEffe proc count*(s: string, sub: char): int {.noSideEffect, rtl, extern: "nsuCountChar".} = - ## Count the occurences of the character `sub` in the string `s`. + ## Count the occurrences of the character `sub` in the string `s`. for c in s: if c == sub: inc result proc count*(s: string, subs: set[char]): int {.noSideEffect, rtl, extern: "nsuCountCharSet".} = - ## Count the occurences of the group of character `subs` in the string `s`. + ## Count the occurrences of the group of character `subs` in the string `s`. for c in s: if c in subs: inc result @@ -898,7 +921,7 @@ proc replaceWord*(s, sub: string, by = ""): string {.noSideEffect, rtl, extern: "nsuReplaceWord".} = ## Replaces `sub` in `s` by the string `by`. ## - ## Each occurance of `sub` has to be surrounded by word boundaries + ## Each occurrence of `sub` has to be surrounded by word boundaries ## (comparable to ``\\w`` in regular expressions), otherwise it is not ## replaced. const wordChars = {'a'..'z', 'A'..'Z', '0'..'9', '_', '\128'..'\255'} diff --git a/lib/pure/subexes.nim b/lib/pure/subexes.nim index adcfdd2883..d701b85b1d 100644 --- a/lib/pure/subexes.nim +++ b/lib/pure/subexes.nim @@ -353,11 +353,11 @@ when isMainModule: proc `%`(formatstr: string, a: openarray[string]): string = result = newStringOfCap(formatstr.len + a.len shl 4) - addf(result, formatstr.TSubex, a) + addf(result, formatstr.Subex, a) proc `%`(formatstr: string, a: string): string = result = newStringOfCap(formatstr.len + a.len) - addf(result, formatstr.TSubex, [a]) + addf(result, formatstr.Subex, [a]) doAssert "$# $3 $# $#" % ["a", "b", "c"] == "a c b c" diff --git a/lib/pure/terminal.nim b/lib/pure/terminal.nim index 8607066f3e..e0e2aa247f 100644 --- a/lib/pure/terminal.nim +++ b/lib/pure/terminal.nim @@ -45,6 +45,21 @@ when defined(windows): var oldAttr = getAttributes() + proc winGetch(): cint {.header: "", importc: "_getch".} +else: + import termios, unsigned + + 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 + discard fd.tcsetattr(time, addr mode) + proc setCursorPos*(x, y: int) = ## sets the terminal's cursor to the (x,y) position. (0,0) is the ## upper left of the screen. @@ -349,6 +364,19 @@ macro styledEcho*(m: varargs[expr]): stmt = result.add(newCall(bindSym"write", bindSym"stdout", newStrLitNode("\n"))) result.add(newCall(bindSym"resetAttributes")) +proc getch*(): char = + ## Read a single character from the terminal, blocking until it is entered. + ## The character is not printed to the terminal. + when defined(windows): + result = winGetch().char + else: + let fd = getFileHandle(stdin) + var oldMode: Termios + discard fd.tcgetattr(addr oldMode) + fd.setRaw() + result = stdin.readChar() + discard fd.tcsetattr(TCSADRAIN, addr oldMode) + when isMainModule: system.addQuitProc(resetAttributes) write(stdout, "never mind") diff --git a/lib/pure/times.nim b/lib/pure/times.nim index 1cabd381b7..e32ea786ae 100644 --- a/lib/pure/times.nim +++ b/lib/pure/times.nim @@ -16,7 +16,7 @@ # of the standard library! import - strutils + strutils, parseutils include "system/inclrtl" @@ -63,44 +63,44 @@ elif defined(windows): elif defined(JS): type Time* {.importc.} = object - getDay: proc (): int {.tags: [], raises: [], gcsafe.} - getFullYear: proc (): int {.tags: [], raises: [], gcsafe.} - getHours: proc (): int {.tags: [], raises: [], gcsafe.} - getMilliseconds: proc (): int {.tags: [], raises: [], gcsafe.} - getMinutes: proc (): int {.tags: [], raises: [], gcsafe.} - getMonth: proc (): int {.tags: [], raises: [], gcsafe.} - getSeconds: proc (): int {.tags: [], raises: [], gcsafe.} - getTime: proc (): int {.tags: [], raises: [], gcsafe.} - getTimezoneOffset: proc (): int {.tags: [], raises: [], gcsafe.} - getDate: proc (): int {.tags: [], raises: [], gcsafe.} - getUTCDate: proc (): int {.tags: [], raises: [], gcsafe.} - getUTCFullYear: proc (): int {.tags: [], raises: [], gcsafe.} - getUTCHours: proc (): int {.tags: [], raises: [], gcsafe.} - getUTCMilliseconds: proc (): int {.tags: [], raises: [], gcsafe.} - getUTCMinutes: proc (): int {.tags: [], raises: [], gcsafe.} - getUTCMonth: proc (): int {.tags: [], raises: [], gcsafe.} - getUTCSeconds: proc (): int {.tags: [], raises: [], gcsafe.} - getUTCDay: proc (): int {.tags: [], raises: [], gcsafe.} - getYear: proc (): int {.tags: [], raises: [], gcsafe.} - parse: proc (s: cstring): Time {.tags: [], raises: [], gcsafe.} - setDate: proc (x: int) {.tags: [], raises: [], gcsafe.} - setFullYear: proc (x: int) {.tags: [], raises: [], gcsafe.} - setHours: proc (x: int) {.tags: [], raises: [], gcsafe.} - setMilliseconds: proc (x: int) {.tags: [], raises: [], gcsafe.} - setMinutes: proc (x: int) {.tags: [], raises: [], gcsafe.} - setMonth: proc (x: int) {.tags: [], raises: [], gcsafe.} - setSeconds: proc (x: int) {.tags: [], raises: [], gcsafe.} - setTime: proc (x: int) {.tags: [], raises: [], gcsafe.} - setUTCDate: proc (x: int) {.tags: [], raises: [], gcsafe.} - setUTCFullYear: proc (x: int) {.tags: [], raises: [], gcsafe.} - setUTCHours: proc (x: int) {.tags: [], raises: [], gcsafe.} - setUTCMilliseconds: proc (x: int) {.tags: [], raises: [], gcsafe.} - setUTCMinutes: proc (x: int) {.tags: [], raises: [], gcsafe.} - setUTCMonth: proc (x: int) {.tags: [], raises: [], gcsafe.} - setUTCSeconds: proc (x: int) {.tags: [], raises: [], gcsafe.} - setYear: proc (x: int) {.tags: [], raises: [], gcsafe.} - toGMTString: proc (): cstring {.tags: [], raises: [], gcsafe.} - toLocaleString: proc (): cstring {.tags: [], raises: [], gcsafe.} + getDay: proc (): int {.tags: [], raises: [], benign.} + getFullYear: proc (): int {.tags: [], raises: [], benign.} + getHours: proc (): int {.tags: [], raises: [], benign.} + getMilliseconds: proc (): int {.tags: [], raises: [], benign.} + getMinutes: proc (): int {.tags: [], raises: [], benign.} + getMonth: proc (): int {.tags: [], raises: [], benign.} + getSeconds: proc (): int {.tags: [], raises: [], benign.} + getTime: proc (): int {.tags: [], raises: [], benign.} + getTimezoneOffset: proc (): int {.tags: [], raises: [], benign.} + getDate: proc (): int {.tags: [], raises: [], benign.} + getUTCDate: proc (): int {.tags: [], raises: [], benign.} + getUTCFullYear: proc (): int {.tags: [], raises: [], benign.} + getUTCHours: proc (): int {.tags: [], raises: [], benign.} + getUTCMilliseconds: proc (): int {.tags: [], raises: [], benign.} + getUTCMinutes: proc (): int {.tags: [], raises: [], benign.} + getUTCMonth: proc (): int {.tags: [], raises: [], benign.} + getUTCSeconds: proc (): int {.tags: [], raises: [], benign.} + getUTCDay: proc (): int {.tags: [], raises: [], benign.} + getYear: proc (): int {.tags: [], raises: [], benign.} + parse: proc (s: cstring): Time {.tags: [], raises: [], benign.} + setDate: proc (x: int) {.tags: [], raises: [], benign.} + setFullYear: proc (x: int) {.tags: [], raises: [], benign.} + setHours: proc (x: int) {.tags: [], raises: [], benign.} + setMilliseconds: proc (x: int) {.tags: [], raises: [], benign.} + setMinutes: proc (x: int) {.tags: [], raises: [], benign.} + setMonth: proc (x: int) {.tags: [], raises: [], benign.} + setSeconds: proc (x: int) {.tags: [], raises: [], benign.} + setTime: proc (x: int) {.tags: [], raises: [], benign.} + setUTCDate: proc (x: int) {.tags: [], raises: [], benign.} + setUTCFullYear: proc (x: int) {.tags: [], raises: [], benign.} + setUTCHours: proc (x: int) {.tags: [], raises: [], benign.} + setUTCMilliseconds: proc (x: int) {.tags: [], raises: [], benign.} + setUTCMinutes: proc (x: int) {.tags: [], raises: [], benign.} + setUTCMonth: proc (x: int) {.tags: [], raises: [], benign.} + setUTCSeconds: proc (x: int) {.tags: [], raises: [], benign.} + setYear: proc (x: int) {.tags: [], raises: [], benign.} + toGMTString: proc (): cstring {.tags: [], raises: [], benign.} + toLocaleString: proc (): cstring {.tags: [], raises: [], benign.} type TimeInfo* = object of RootObj ## represents a time in different parts @@ -139,42 +139,42 @@ type {.deprecated: [TMonth: Month, TWeekDay: WeekDay, TTime: Time, TTimeInterval: TimeInterval, TTimeInfo: TimeInfo].} -proc getTime*(): Time {.tags: [TimeEffect], gcsafe.} +proc getTime*(): Time {.tags: [TimeEffect], benign.} ## gets the current calendar time as a UNIX epoch value (number of seconds ## elapsed since 1970) with integer precission. Use epochTime for higher ## resolution. -proc getLocalTime*(t: Time): TimeInfo {.tags: [TimeEffect], raises: [], gcsafe.} +proc getLocalTime*(t: Time): TimeInfo {.tags: [TimeEffect], raises: [], benign.} ## converts the calendar time `t` to broken-time representation, ## expressed relative to the user's specified time zone. -proc getGMTime*(t: Time): TimeInfo {.tags: [TimeEffect], raises: [], gcsafe.} +proc getGMTime*(t: Time): TimeInfo {.tags: [TimeEffect], raises: [], benign.} ## converts the calendar time `t` to broken-down time representation, ## expressed in Coordinated Universal Time (UTC). -proc timeInfoToTime*(timeInfo: TimeInfo): Time {.tags: [], gcsafe.} +proc timeInfoToTime*(timeInfo: TimeInfo): Time {.tags: [], benign.} ## converts a broken-down time structure to ## calendar time representation. The function ignores the specified ## contents of the structure members `weekday` and `yearday` and recomputes ## them from the other information in the broken-down time structure. -proc fromSeconds*(since1970: float): Time {.tags: [], raises: [], gcsafe.} +proc fromSeconds*(since1970: float): Time {.tags: [], raises: [], benign.} ## Takes a float which contains the number of seconds since the unix epoch and ## returns a time object. -proc fromSeconds*(since1970: int64): Time {.tags: [], raises: [], gcsafe.} = +proc fromSeconds*(since1970: int64): Time {.tags: [], raises: [], benign.} = ## Takes an int which contains the number of seconds since the unix epoch and ## returns a time object. fromSeconds(float(since1970)) -proc toSeconds*(time: Time): float {.tags: [], raises: [], gcsafe.} +proc toSeconds*(time: Time): float {.tags: [], raises: [], benign.} ## Returns the time in seconds since the unix epoch. -proc `$` *(timeInfo: TimeInfo): string {.tags: [], raises: [], gcsafe.} +proc `$` *(timeInfo: TimeInfo): string {.tags: [], raises: [], benign.} ## converts a `TimeInfo` object to a string representation. -proc `$` *(time: Time): string {.tags: [], raises: [], gcsafe.} +proc `$` *(time: Time): string {.tags: [], raises: [], benign.} ## converts a calendar time to a string representation. proc `-`*(a, b: Time): int64 {. - rtl, extern: "ntDiffTime", tags: [], raises: [].} + rtl, extern: "ntDiffTime", tags: [], raises: [], benign.} ## computes the difference of two calendar times. Result is in seconds. proc `<`*(a, b: Time): bool {. @@ -194,14 +194,14 @@ proc `==`*(a, b: Time): bool {. when not defined(JS): proc getTzname*(): tuple[nonDST, DST: string] {.tags: [TimeEffect], raises: [], - gcsafe.} + benign.} ## returns the local timezone; ``nonDST`` is the name of the local non-DST ## timezone, ``DST`` is the name of the local DST timezone. -proc getTimezone*(): int {.tags: [TimeEffect], raises: [], gcsafe.} +proc getTimezone*(): int {.tags: [TimeEffect], raises: [], benign.} ## returns the offset of the local (non-DST) timezone in seconds west of UTC. -proc getStartMilsecs*(): int {.deprecated, tags: [TimeEffect], gcsafe.} +proc getStartMilsecs*(): int {.deprecated, tags: [TimeEffect], benign.} ## get the miliseconds from the start of the program. **Deprecated since ## version 0.8.10.** Use ``epochTime`` or ``cpuTime`` instead. @@ -744,6 +744,285 @@ proc format*(info: TimeInfo, f: string): string = {.pop.} +proc parseToken(info: var TimeInfo; token, value: string; j: var int) = + ## Helper of the parse proc to parse individual tokens. + var sv: int + case token + of "d": + var pd = parseInt(value[j..j+1], sv) + info.monthday = sv + j += pd + of "dd": + info.monthday = value[j..j+1].parseInt() + j += 2 + of "ddd": + case value[j..j+2].toLower(): + of "sun": + info.weekday = dSun + of "mon": + info.weekday = dMon + of "tue": + info.weekday = dTue + of "wed": + info.weekday = dWed + of "thu": + info.weekday = dThu + of "fri": + info.weekday = dFri + of "sat": + info.weekday = dSat + else: + raise newException(ValueError, "invalid day of week ") + j += 3 + of "dddd": + if value.len >= j+6 and value[j..j+5].cmpIgnoreCase("sunday") == 0: + info.weekday = dSun + j += 6 + elif value.len >= j+6 and value[j..j+5].cmpIgnoreCase("monday") == 0: + info.weekday = dMon + j += 6 + elif value.len >= j+7 and value[j..j+6].cmpIgnoreCase("tuesday") == 0: + info.weekday = dTue + j += 7 + elif value.len >= j+9 and value[j..j+8].cmpIgnoreCase("wednesday") == 0: + info.weekday = dWed + j += 9 + elif value.len >= j+8 and value[j..j+7].cmpIgnoreCase("thursday") == 0: + info.weekday = dThu + j += 8 + elif value.len >= j+6 and value[j..j+5].cmpIgnoreCase("friday") == 0: + info.weekday = dFri + j += 6 + elif value.len >= j+8 and value[j..j+7].cmpIgnoreCase("saturday") == 0: + info.weekday = dSat + j += 8 + else: + raise newException(ValueError, "invalid day of week ") + of "h", "H": + var pd = parseInt(value[j..j+1], sv) + info.hour = sv + j += pd + of "hh", "HH": + info.hour = value[j..j+1].parseInt() + j += 2 + of "m": + var pd = parseInt(value[j..j+1], sv) + info.minute = sv + j += pd + of "mm": + info.minute = value[j..j+1].parseInt() + j += 2 + of "M": + var pd = parseInt(value[j..j+1], sv) + info.month = Month(sv-1) + info.monthday = sv + j += pd + of "MM": + var month = value[j..j+1].parseInt() + j += 2 + info.month = Month(month-1) + of "MMM": + case value[j..j+2].toLower(): + of "jan": + info.month = mJan + of "feb": + info.month = mFeb + of "mar": + info.month = mMar + of "apr": + info.month = mApr + of "may": + info.month = mMay + of "jun": + info.month = mJun + of "jul": + info.month = mJul + of "aug": + info.month = mAug + of "sep": + info.month = mSep + of "oct": + info.month = mOct + of "nov": + info.month = mNov + of "dec": + info.month = mDec + else: + raise newException(ValueError, "invalid month") + j += 3 + of "MMMM": + if value.len >= j+7 and value[j..j+6].cmpIgnoreCase("january") == 0: + info.month = mJan + j += 7 + elif value.len >= j+8 and value[j..j+7].cmpIgnoreCase("february") == 0: + info.month = mFeb + j += 8 + elif value.len >= j+5 and value[j..j+4].cmpIgnoreCase("march") == 0: + info.month = mMar + j += 5 + elif value.len >= j+5 and value[j..j+4].cmpIgnoreCase("april") == 0: + info.month = mApr + j += 5 + elif value.len >= j+3 and value[j..j+2].cmpIgnoreCase("may") == 0: + info.month = mMay + j += 3 + elif value.len >= j+4 and value[j..j+3].cmpIgnoreCase("june") == 0: + info.month = mJun + j += 4 + elif value.len >= j+4 and value[j..j+3].cmpIgnoreCase("july") == 0: + info.month = mJul + j += 4 + elif value.len >= j+6 and value[j..j+5].cmpIgnoreCase("august") == 0: + info.month = mAug + j += 6 + elif value.len >= j+9 and value[j..j+8].cmpIgnoreCase("september") == 0: + info.month = mSep + j += 9 + elif value.len >= j+7 and value[j..j+6].cmpIgnoreCase("october") == 0: + info.month = mOct + j += 7 + elif value.len >= j+8 and value[j..j+7].cmpIgnoreCase("november") == 0: + info.month = mNov + j += 8 + elif value.len >= j+8 and value[j..j+7].cmpIgnoreCase("december") == 0: + info.month = mDec + j += 8 + else: + raise newException(ValueError, "invalid month") + of "s": + var pd = parseInt(value[j..j+1], sv) + info.second = sv + j += pd + of "ss": + info.second = value[j..j+1].parseInt() + j += 2 + of "t": + if value[j] == 'P' and info.hour > 0 and info.hour < 12: + info.hour += 12 + j += 1 + of "tt": + if value[j..j+1] == "PM" and info.hour > 0 and info.hour < 12: + info.hour += 12 + j += 2 + of "yy": + # Assumes current century + var year = value[j..j+1].parseInt() + var thisCen = getLocalTime(getTime()).year div 100 + info.year = thisCen*100 + year + j += 2 + of "yyyy": + info.year = value[j..j+3].parseInt() + j += 4 + of "z": + if value[j] == '+': + info.timezone = parseInt($value[j+1]) + elif value[j] == '-': + info.timezone = 0-parseInt($value[j+1]) + else: + raise newException(ValueError, "Sign for timezone " & value[j]) + j += 2 + of "zz": + if value[j] == '+': + info.timezone = value[j+1..j+2].parseInt() + elif value[j] == '-': + info.timezone = 0-value[j+1..j+2].parseInt() + else: + raise newException(ValueError, "Sign for timezone " & value[j]) + j += 3 + of "zzz": + if value[j] == '+': + info.timezone = value[j+1..j+2].parseInt() + elif value[j] == '-': + info.timezone = 0-value[j+1..j+2].parseInt() + else: + raise newException(ValueError, "Sign for timezone " & value[j]) + j += 6 + of "ZZZ": + info.tzname = value[j..j+2].toUpper() + j += 3 + else: + # Ignore the token and move forward in the value string by the same length + j += token.len + +proc parse*(value, layout: string): TimeInfo = + ## This function parses a date/time string using the standard format identifiers (below) + ## The function defaults information not provided in the format string from the running program (timezone, month, year, etc) + ## + ## ========== ================================================================================= ================================================ + ## Specifier Description Example + ## ========== ================================================================================= ================================================ + ## d Numeric value of the day of the month, it will be one or two digits long. ``1/04/2012 -> 1``, ``21/04/2012 -> 21`` + ## dd Same as above, but always two digits. ``1/04/2012 -> 01``, ``21/04/2012 -> 21`` + ## ddd Three letter string which indicates the day of the week. ``Saturday -> Sat``, ``Monday -> Mon`` + ## dddd Full string for the day of the week. ``Saturday -> Saturday``, ``Monday -> Monday`` + ## h The hours in one digit if possible. Ranging from 0-12. ``5pm -> 5``, ``2am -> 2`` + ## hh The hours in two digits always. If the hour is one digit 0 is prepended. ``5pm -> 05``, ``11am -> 11`` + ## H The hours in one digit if possible, randing from 0-24. ``5pm -> 17``, ``2am -> 2`` + ## HH The hours in two digits always. 0 is prepended if the hour is one digit. ``5pm -> 17``, ``2am -> 02`` + ## m The minutes in 1 digit if possible. ``5:30 -> 30``, ``2:01 -> 1`` + ## mm Same as above but always 2 digits, 0 is prepended if the minute is one digit. ``5:30 -> 30``, ``2:01 -> 01`` + ## M The month in one digit if possible. ``September -> 9``, ``December -> 12`` + ## MM The month in two digits always. 0 is prepended. ``September -> 09``, ``December -> 12`` + ## MMM Abbreviated three-letter form of the month. ``September -> Sep``, ``December -> Dec`` + ## MMMM Full month string, properly capitalized. ``September -> September`` + ## s Seconds as one digit if possible. ``00:00:06 -> 6`` + ## ss Same as above but always two digits. 0 is prepended. ``00:00:06 -> 06`` + ## t ``A`` when time is in the AM. ``P`` when time is in the PM. + ## tt Same as above, but ``AM`` and ``PM`` instead of ``A`` and ``P`` respectively. + ## yy Displays the year to two digits. ``2012 -> 12`` + ## yyyy Displays the year to four digits. ``2012 -> 2012`` + ## z Displays the timezone offset from UTC. ``GMT+7 -> +7``, ``GMT-5 -> -5`` + ## zz Same as above but with leading 0. ``GMT+7 -> +07``, ``GMT-5 -> -05`` + ## zzz Same as above but with ``:00``. ``GMT+7 -> +07:00``, ``GMT-5 -> -05:00`` + ## ZZZ Displays the name of the timezone. ``GMT -> GMT``, ``EST -> EST`` + ## ========== ================================================================================= ================================================ + ## + ## Other strings can be inserted by putting them in ``''``. For example + ## ``hh'->'mm`` will give ``01->56``. The following characters can be + ## inserted without quoting them: ``:`` ``-`` ``(`` ``)`` ``/`` ``[`` ``]`` + ## ``,``. However you don't need to necessarily separate format specifiers, a + ## unambiguous format string like ``yyyyMMddhhmmss`` is valid too. + var i = 0 # pointer for format string + var j = 0 # pointer for value string + var token = "" + # Assumes current day of month, month and year, but time is reset to 00:00:00. Weekday will be reset after parsing. + var info = getLocalTime(getTime()) + info.hour = 0 + info.minute = 0 + info.second = 0 + while true: + case layout[i] + of ' ', '-', '/', ':', '\'', '\0', '(', ')', '[', ']', ',': + if token.len > 0: + parseToken(info, token, value, j) + # Reset token + token = "" + # Break if at end of line + if layout[i] == '\0': break + # Skip separator and everything between single quotes + # These are literals in both the layout and the value string + if layout[i] == '\'': + inc(i) + inc(j) + while layout[i] != '\'' and layout.len-1 > i: + inc(i) + inc(j) + else: + inc(i) + inc(j) + else: + # Check if the letter being added matches previous accumulated buffer. + if token.len < 1 or token[high(token)] == layout[i]: + token.add(layout[i]) + inc(i) + else: + parseToken(info, token, value, j) + token = "" + # Reset weekday as it might not have been provided and the default may be wrong + info.weekday = getLocalTime(timeInfoToTime(info)).weekday + return info + + when isMainModule: # $ date --date='@2147483647' # Tue 19 Jan 03:14:07 GMT 2038 @@ -778,3 +1057,51 @@ when isMainModule: # Interval tests assert((t4 - initInterval(years = 2)).format("yyyy") == "1995") assert((t4 - initInterval(years = 7, minutes = 34, seconds = 24)).format("yyyy mm ss") == "1990 24 10") + + var s = "Tuesday at 09:04am on Dec 15, 2015" + 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") + # 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") + # RubyDate = "Mon Jan 02 15:04:05 -0700 2006" + s = "Mon Jan 02 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") + # RFC822 = "02 Jan 06 15:04 MST" + s = "02 Jan 06 15:04 MST" + f = "dd MMM yy HH:mm ZZZ" + assert($s.parse(f) == "Mon Jan 2 15:04:00 2006") + # RFC822Z = "02 Jan 06 15:04 -0700" # RFC822 with numeric zone + s = "02 Jan 06 15:04 -07:00" + f = "dd MMM yy HH:mm zzz" + assert($s.parse(f) == "Mon Jan 2 15:04:00 2006") + # RFC850 = "Monday, 02-Jan-06 15:04:05 MST" + s = "Monday, 02-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") + # RFC1123 = "Mon, 02 Jan 2006 15:04:05 MST" + s = "Mon, 02 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") + # RFC1123Z = "Mon, 02 Jan 2006 15:04:05 -0700" # RFC1123 with numeric zone + s = "Mon, 02 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") + # RFC3339 = "2006-01-02T15:04:05Z07:00" + s = "2006-01-02T15:04:05Z-07:00" + f = "yyyy-MM-ddTHH:mm:ssZzzz" + assert($s.parse(f) == "Mon Jan 2 15:04:05 2006") + # RFC3339Nano = "2006-01-02T15:04:05.999999999Z07:00" + s = "2006-01-02T15:04:05.999999999Z-07:00" + f = "yyyy-MM-ddTHH:mm:ss.999999999Zzzz" + assert($s.parse(f) == "Mon Jan 2 15:04:05 2006") + # Kitchen = "3:04PM" + s = "3:04PM" + f = "h:mmtt" + echo "Kitchen: " & $s.parse(f) \ No newline at end of file diff --git a/lib/pure/unicode.nim b/lib/pure/unicode.nim index b892ec8b40..42e6a31951 100644 --- a/lib/pure/unicode.nim +++ b/lib/pure/unicode.nim @@ -1235,11 +1235,12 @@ proc reversed*(s: string): string = ## returns the reverse of `s`, interpreting it as unicode characters. Unicode ## combining characters are correctly interpreted as well: ## - ## .. code-block: + ## .. code-block:: nim + ## ## assert reversed("Reverse this!") == "!siht esreveR" ## assert reversed("先秦兩漢") == "漢兩秦先" ## assert reversed("as⃝df̅") == "f̅ds⃝a" - ## assert reversed("a⃞b⃞c⃞") == "c⃞b⃞a⃞" + ## assert reversed("a⃞b⃞c⃞") == "c⃞b⃞a⃞" var i = 0 lastI = 0 diff --git a/lib/pure/unittest.nim b/lib/pure/unittest.nim index 8dc9fe0d4b..f4e42ee63f 100644 --- a/lib/pure/unittest.nim +++ b/lib/pure/unittest.nim @@ -39,6 +39,7 @@ when declared(stdout): when not defined(ECMAScript): import terminal + system.addQuitProc(resetAttributes) type TestStatus* = enum OK, FAILED @@ -234,5 +235,3 @@ if envOutLvl.len > 0: if $opt == envOutLvl: outputLevel = opt break - -system.addQuitProc(resetAttributes) diff --git a/lib/pure/uri.nim b/lib/pure/uri.nim index 1627b6ca30..9a6e273a88 100644 --- a/lib/pure/uri.nim +++ b/lib/pure/uri.nim @@ -20,6 +20,7 @@ type {.deprecated: [TUrl: Url, TUri: Uri].} +{.push warning[deprecated]: off.} proc `$`*(url: Url): string {.deprecated.} = ## **Deprecated since 0.9.6**: Use ``Uri`` instead. return string(url) @@ -44,6 +45,7 @@ proc add*(url: var Url, a: Url) {.deprecated.} = ## ## **Deprecated since 0.9.6**: Use ``Uri`` instead. url = url / a +{.pop.} proc parseAuthority(authority: string, result: var Uri) = var i = 0 @@ -182,10 +184,10 @@ proc combine*(base: Uri, reference: Uri): Uri = ## assert foo.path == "/baz" ## ## let bar = combine(parseUri("http://example.com/foo/bar"), parseUri("baz")) - ## assert foo.path == "/foo/baz" + ## assert bar.path == "/foo/baz" ## ## let bar = combine(parseUri("http://example.com/foo/bar/"), parseUri("baz")) - ## assert foo.path == "/foo/bar/baz" + ## assert bar.path == "/foo/bar/baz" template setAuthority(dest, src: expr): stmt = dest.hostname = src.hostname @@ -239,10 +241,10 @@ proc `/`*(x: Uri, path: string): Uri = ## assert foo.path == "/foo/bar/baz" ## ## let bar = parseUri("http://example.com/foo/bar") / parseUri("baz") - ## assert foo.path == "/foo/bar/baz" + ## assert bar.path == "/foo/bar/baz" ## ## let bar = parseUri("http://example.com/foo/bar/") / parseUri("baz") - ## assert foo.path == "/foo/bar/baz" + ## assert bar.path == "/foo/bar/baz" result = x if result.path[result.path.len-1] == '/': if path[0] == '/': diff --git a/lib/pure/xmldom.nim b/lib/pure/xmldom.nim index 660932d92e..2e55ff1829 100644 --- a/lib/pure/xmldom.nim +++ b/lib/pure/xmldom.nim @@ -642,7 +642,7 @@ proc isEmpty(s: string): bool = return true proc normalize*(n: PNode) = - ## Merges all seperated TextNodes together, and removes any empty TextNodes + ## Merges all separated TextNodes together, and removes any empty TextNodes var curTextNode: PNode = nil var i: int = 0 diff --git a/lib/pure/xmlparser.nim b/lib/pure/xmlparser.nim index 8591e894ce..755bfcdbc4 100644 --- a/lib/pure/xmlparser.nim +++ b/lib/pure/xmlparser.nim @@ -103,7 +103,7 @@ proc parse(x: var XmlParser, errors: var seq[string]): XmlNode = proc parseXml*(s: Stream, filename: string, errors: var seq[string]): XmlNode = ## parses the XML from stream `s` and returns a ``PXmlNode``. Every - ## occured parsing error is added to the `errors` sequence. + ## occurred parsing error is added to the `errors` sequence. var x: XmlParser open(x, s, filename, {reportComments}) while true: @@ -129,7 +129,7 @@ proc parseXml*(s: Stream): XmlNode = proc loadXml*(path: string, errors: var seq[string]): XmlNode = ## Loads and parses XML from file specified by ``path``, and returns - ## a ``PXmlNode``. Every occured parsing error is added to the `errors` + ## a ``PXmlNode``. Every occurred parsing error is added to the `errors` ## sequence. var s = newFileStream(path, fmRead) if s == nil: raise newException(IOError, "Unable to read file: " & path) diff --git a/lib/system.nim b/lib/system.nim index 12c5c63036..abf31c8217 100644 --- a/lib/system.nim +++ b/lib/system.nim @@ -73,7 +73,7 @@ type expr* {.magic: Expr.} ## meta type to denote an expression (for templates) stmt* {.magic: Stmt.} ## meta type to denote a statement (for templates) typedesc* {.magic: TypeDesc.} ## meta type to denote a type description - void* {.magic: "VoidType".} ## meta type to denote the absense of any type + void* {.magic: "VoidType".} ## meta type to denote the absence of any type auto* = expr any* = distinct auto @@ -124,7 +124,7 @@ proc declared*(x: expr): bool {.magic: "Defined", noSideEffect.} ## feature or not: ## ## .. code-block:: Nim - ## when not defined(strutils.toUpper): + ## when not declared(strutils.toUpper): ## # provide our own toUpper proc here, because strutils is ## # missing it. @@ -343,10 +343,10 @@ type ## ## Each exception has to inherit from `Exception`. See the full `exception ## hierarchy`_. - parent: ref Exception ## parent exception (can be used as a stack) - name: cstring ## The exception's name is its Nim identifier. - ## This field is filled automatically in the - ## ``raise`` statement. + parent*: ref Exception ## parent exception (can be used as a stack) + name: cstring ## The exception's name is its Nim identifier. + ## This field is filled automatically in the + ## ``raise`` statement. msg* {.exportc: "message".}: string ## the exception's message. Not ## providing an exception message ## is bad style. @@ -357,7 +357,7 @@ type ## ## See the full `exception hierarchy`_. IOError* = object of SystemError ## \ - ## Raised if an IO error occured. + ## Raised if an IO error occurred. ## ## See the full `exception hierarchy`_. OSError* = object of SystemError ## \ @@ -370,11 +370,11 @@ type ## ## See the full `exception hierarchy`_. ResourceExhaustedError* = object of SystemError ## \ - ## Raised if a resource request could not be fullfilled. + ## Raised if a resource request could not be fulfilled. ## ## See the full `exception hierarchy`_. ArithmeticError* = object of Exception ## \ - ## Raised if any kind of arithmetic error occured. + ## Raised if any kind of arithmetic error occurred. ## ## See the full `exception hierarchy`_. DivByZeroError* = object of ArithmeticError ## \ @@ -578,7 +578,7 @@ proc len*(x: cstring): int {.magic: "LengthStr", noSideEffect.} proc len*[I, T](x: array[I, T]): int {.magic: "LengthArray", noSideEffect.} proc len*[T](x: seq[T]): int {.magic: "LengthSeq", noSideEffect.} ## returns the length of an array, an openarray, a sequence or a string. - ## This is rougly the same as ``high(T)-low(T)+1``, but its resulting type is + ## This is roughly the same as ``high(T)-low(T)+1``, but its resulting type is ## always an int. # set routines: @@ -865,7 +865,7 @@ proc contains*[T](x: set[T], y: T): bool {.magic: "InSet", noSideEffect.} ## passes its arguments in reverse order. proc contains*[T](s: Slice[T], value: T): bool {.noSideEffect, inline.} = - ## Checks if `value` is withing the range of `s`; returns true iff + ## Checks if `value` is within the range of `s`; returns true iff ## `value >= s.a and value <= s.b` ## ## .. code-block:: Nim @@ -1455,7 +1455,8 @@ template `>%` *(x, y: expr): expr {.immediate.} = y <% x proc `$`*(x: int): string {.magic: "IntToStr", noSideEffect.} ## The stringify operator for an integer argument. Returns `x` - ## converted to a decimal string. + ## converted to a decimal string. ``$`` is Nim's general way of + ## spelling `toString`:idx:. proc `$`*(x: int64): string {.magic: "Int64ToStr", noSideEffect.} ## The stringify operator for an integer argument. Returns `x` @@ -2098,17 +2099,16 @@ when not defined(nimrodVM) and hostOS != "standalone": ## returns an informative string about the GC's activity. This may be useful ## for tweaking. - # XXX mark these as 'locks: 0' once 0.10.0 has been released - proc GC_ref*[T](x: ref T) {.magic: "GCref", gcsafe.} - proc GC_ref*[T](x: seq[T]) {.magic: "GCref", gcsafe.} - proc GC_ref*(x: string) {.magic: "GCref", gcsafe.} + proc GC_ref*[T](x: ref T) {.magic: "GCref", benign.} + proc GC_ref*[T](x: seq[T]) {.magic: "GCref", benign.} + proc GC_ref*(x: string) {.magic: "GCref", benign.} ## marks the object `x` as referenced, so that it will not be freed until ## it is unmarked via `GC_unref`. If called n-times for the same object `x`, ## n calls to `GC_unref` are needed to unmark `x`. - proc GC_unref*[T](x: ref T) {.magic: "GCunref", gcsafe.} - proc GC_unref*[T](x: seq[T]) {.magic: "GCunref", gcsafe.} - proc GC_unref*(x: string) {.magic: "GCunref", gcsafe.} + proc GC_unref*[T](x: ref T) {.magic: "GCunref", benign.} + proc GC_unref*[T](x: seq[T]) {.magic: "GCunref", benign.} + proc GC_unref*(x: string) {.magic: "GCunref", benign.} ## see the documentation of `GC_ref`. template accumulateResult*(iter: expr) = @@ -2194,7 +2194,8 @@ elif hostOS != "standalone": inc(i) {.pop.} -proc echo*(x: varargs[expr, `$`]) {.magic: "Echo", tags: [WriteIOEffect], benign.} +proc echo*(x: varargs[expr, `$`]) {.magic: "Echo", tags: [WriteIOEffect], + benign, sideEffect.} ## Writes and flushes the parameters to the standard output. ## ## Special built-in that takes a variable number of arguments. Each argument @@ -2247,14 +2248,9 @@ when not declared(sysFatal): e.msg = message & arg raise e -when defined(nimlocks): - proc getTypeInfo*[T](x: T): pointer {.magic: "GetTypeInfo", gcsafe, locks: 0.} - ## get type information for `x`. Ordinary code should not use this, but - ## the `typeinfo` module instead. -else: - proc getTypeInfo*[T](x: T): pointer {.magic: "GetTypeInfo", gcsafe.} - ## get type information for `x`. Ordinary code should not use this, but - ## the `typeinfo` module instead. +proc getTypeInfo*[T](x: T): pointer {.magic: "GetTypeInfo", benign.} + ## get type information for `x`. Ordinary code should not use this, but + ## the `typeinfo` module instead. {.push stackTrace: off.} proc abs*(x: int): int {.magic: "AbsI", noSideEffect.} = @@ -2454,19 +2450,15 @@ when not defined(JS): #and not defined(NimrodVM): ## Returns ``false`` if the end of the file has been reached, ``true`` ## otherwise. If ``false`` is returned `line` contains no new data. - when not defined(booting): - proc writeln*[Ty](f: File, x: varargs[Ty, `$`]) {.inline, - tags: [WriteIOEffect], gcsafe, locks: 0.} - ## writes the values `x` to `f` and then writes "\n". - ## May throw an IO exception. - else: - proc writeln*[Ty](f: File, x: varargs[Ty, `$`]) {.inline, - tags: [WriteIOEffect].} + proc writeln*[Ty](f: File, x: varargs[Ty, `$`]) {.inline, + tags: [WriteIOEffect], benign.} + ## writes the values `x` to `f` and then writes "\n". + ## May throw an IO exception. proc getFileSize*(f: File): int64 {.tags: [ReadIOEffect], benign.} ## retrieves the file size (in bytes) of `f`. - proc readBytes*(f: File, a: var openArray[int8], start, len: int): int {. + proc readBytes*(f: File, a: var openArray[int8|uint8], start, len: int): 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 @@ -2484,7 +2476,7 @@ when not defined(JS): #and not defined(NimrodVM): ## 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], start, len: int): int {. + proc writeBytes*(f: File, a: openArray[int8|uint8], start, len: int): 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 @@ -2575,7 +2567,7 @@ when not defined(JS): #and not defined(NimrodVM): initAllocator() when hasThreadSupport: include "system/syslocks" - include "system/threads" + when hostOS != "standalone": include "system/threads" elif not defined(nogc) and not defined(NimrodVM) and hostOS != "standalone": when not defined(useNimRtl) and not defined(createNimRtl): initStackBottom() initGC() @@ -3134,9 +3126,17 @@ proc shallow*(s: var string) {.noSideEffect, inline.} = s.reserved = s.reserved or seqShallowFlag type - TNimrodNode {.final.} = object - PNimrodNode* {.magic: "PNimrodNode".} = ref TNimrodNode - ## represents a Nim AST node. Macros operate on this type. + NimNodeObj = object + +when defined(nimnode): + type + NimNode* {.magic: "PNimrodNode".} = ref NimNodeObj + ## represents a Nim AST node. Macros operate on this type. + {.deprecated: [PNimrodNode: NimNode].} +else: + type + PNimrodNode* {.magic: "PNimrodNode".} = ref NimNodeObj + ## represents a Nim AST node. Macros operate on this type. when false: template eval*(blk: stmt): stmt = @@ -3160,7 +3160,7 @@ when hostOS != "standalone": x[j+i] = item[j] inc(j) -proc compiles*(x): bool {.magic: "Compiles", noSideEffect.} = +proc compiles*(x: expr): bool {.magic: "Compiles", noSideEffect.} = ## Special compile-time procedure that checks whether `x` can be compiled ## without any semantic error. ## This can be used to check whether a type supports some operation: diff --git a/lib/system/arithm.nim b/lib/system/arithm.nim index c4df287cf1..f68e2dcd9f 100644 --- a/lib/system/arithm.nim +++ b/lib/system/arithm.nim @@ -15,7 +15,7 @@ proc raiseOverflow {.compilerproc, noinline, noreturn.} = sysFatal(OverflowError, "over- or underflow") proc raiseDivByZero {.compilerproc, noinline, noreturn.} = - sysFatal(DivByZeroError, "divison by zero") + sysFatal(DivByZeroError, "division by zero") proc addInt64(a, b: int64): int64 {.compilerProc, inline.} = result = a +% b diff --git a/lib/system/assign.nim b/lib/system/assign.nim index 429a92d341..78995954f7 100644 --- a/lib/system/assign.nim +++ b/lib/system/assign.nim @@ -27,7 +27,7 @@ proc genericAssignAux(dest, src: pointer, n: ptr TNimNode, var m = selectBranch(src, n) # reset if different branches are in use; note different branches also # imply that's not self-assignment (``x = x``)! - if m != dd and dd != nil: + if m != dd and dd != nil: genericResetAux(dest, dd) copyMem(cast[pointer](d +% n.offset), cast[pointer](s +% n.offset), n.typ.size) @@ -205,9 +205,13 @@ proc genericReset(dest: pointer, mt: PNimType) = case mt.kind of tyString, tyRef, tySequence: unsureAsgnRef(cast[PPointer](dest), nil) - of tyObject, tyTuple: - # we don't need to reset m_type field for tyObject + of tyTuple: genericResetAux(dest, mt.node) + of tyObject: + genericResetAux(dest, mt.node) + # also reset the type field for tyObject, for correct branch switching! + var pint = cast[ptr PNimType](dest) + pint[] = nil of tyArray, tyArrayConstr: for i in 0..(mt.size div mt.base.size)-1: genericReset(cast[pointer](d +% i*% mt.base.size), mt.base) diff --git a/lib/system/cgprocs.nim b/lib/system/cgprocs.nim index 089846578e..f3acc81f24 100644 --- a/lib/system/cgprocs.nim +++ b/lib/system/cgprocs.nim @@ -13,7 +13,7 @@ proc addChar(s: NimString, c: char): NimString {.compilerProc, benign.} type TLibHandle = pointer # private type - TProcAddr = pointer # libary loading and loading of procs: + TProcAddr = pointer # library loading and loading of procs: proc nimLoadLibrary(path: string): TLibHandle {.compilerproc.} proc nimUnloadLibrary(lib: TLibHandle) {.compilerproc.} diff --git a/lib/system/dyncalls.nim b/lib/system/dyncalls.nim index e0d99cf88a..44f7b67c31 100644 --- a/lib/system/dyncalls.nim +++ b/lib/system/dyncalls.nim @@ -8,7 +8,7 @@ # # This file implements the ability to call native procs from libraries. -# It is not possible to do this in a platform independant way, unfortunately. +# It is not possible to do this in a platform independent way, unfortunately. # However, the interface has been designed to take platform differences into # account and been ported to all major platforms. @@ -80,15 +80,22 @@ elif defined(windows) or defined(dos): # Native Windows Implementation # ======================================================================= # - type - THINSTANCE {.importc: "HINSTANCE".} = pointer + when defined(cpp): + type + THINSTANCE {.importc: "HINSTANCE".} = object + x: pointer + proc getProcAddress(lib: THINSTANCE, name: cstring): TProcAddr {. + importcpp: "(void*)GetProcAddress(@)", header: "", stdcall.} + else: + type + THINSTANCE {.importc: "HINSTANCE".} = pointer + proc getProcAddress(lib: THINSTANCE, name: cstring): TProcAddr {. + importc: "GetProcAddress", header: "", stdcall.} proc freeLibrary(lib: THINSTANCE) {. importc: "FreeLibrary", header: "", stdcall.} proc winLoadLibrary(path: cstring): THINSTANCE {. importc: "LoadLibraryA", header: "", stdcall.} - proc getProcAddress(lib: THINSTANCE, name: cstring): TProcAddr {. - importc: "GetProcAddress", header: "", stdcall.} proc nimUnloadLibrary(lib: TLibHandle) = freeLibrary(cast[THINSTANCE](lib)) diff --git a/lib/system/excpt.nim b/lib/system/excpt.nim index 417a8634f6..1b3471978a 100644 --- a/lib/system/excpt.nim +++ b/lib/system/excpt.nim @@ -175,6 +175,8 @@ proc auxWriteStackTrace(f: PFrame, s: var string) = add(s, tempFrames[j].procname) add(s, "\n") +proc stackTraceAvailable*(): bool + when hasSomeStackTrace: proc rawWriteStackTrace(s: var string) = when NimStackTrace: @@ -188,6 +190,18 @@ when hasSomeStackTrace: auxWriteStackTraceWithBacktrace(s) else: add(s, "No stack traceback available\n") + proc stackTraceAvailable(): bool = + when NimStackTrace: + if framePtr == nil: + result = false + else: + result = true + elif defined(nativeStackTrace) and nativeStackTraceSupported: + result = true + else: + result = false +else: + proc stackTraceAvailable*(): bool = result = false proc quitOrDebug() {.inline.} = when not defined(endb): diff --git a/lib/system/gc.nim b/lib/system/gc.nim index 844f286907..1f4279c8f5 100644 --- a/lib/system/gc.nim +++ b/lib/system/gc.nim @@ -528,20 +528,9 @@ proc growObj(old: pointer, newsize: int, gch: var TGcHeap): pointer = zeroMem(cast[pointer](cast[ByteAddress](res)+% oldsize +% sizeof(TCell)), newsize-oldsize) sysAssert((cast[ByteAddress](res) and (MemAlign-1)) == 0, "growObj: 3") - sysAssert(res.refcount shr rcShift <=% 1, "growObj: 4") - #if res.refcount <% rcIncrement: - # add(gch.zct, res) - #else: # XXX: what to do here? - # decRef(ol) - if (ol.refcount and ZctFlag) != 0: - var j = gch.zct.len-1 - var d = gch.zct.d - while j >= 0: - if d[j] == ol: - d[j] = res - break - dec(j) - if canbeCycleRoot(ol): excl(gch.cycleRoots, ol) + # This can be wrong for intermediate temps that are nevertheless on the + # heap because of lambda lifting: + #gcAssert(res.refcount shr rcShift <=% 1, "growObj: 4") when logGC: writeCell("growObj old cell", ol) writeCell("growObj new cell", res) @@ -549,7 +538,26 @@ proc growObj(old: pointer, newsize: int, gch: var TGcHeap): pointer = gcTrace(res, csAllocated) when reallyDealloc: sysAssert(allocInv(gch.region), "growObj before dealloc") - rawDealloc(gch.region, ol) + if ol.refcount shr rcShift <=% 1: + # free immediately to save space: + if (ol.refcount and ZctFlag) != 0: + var j = gch.zct.len-1 + var d = gch.zct.d + while j >= 0: + if d[j] == ol: + d[j] = res + break + dec(j) + if canbeCycleRoot(ol): excl(gch.cycleRoots, ol) + rawDealloc(gch.region, ol) + else: + # we split the old refcount in 2 parts. XXX This is still not entirely + # correct if the pointer that receives growObj's result is on the stack. + # A better fix would be to emit the location specific write barrier for + # 'growObj', but this is lost of more work and who knows what new problems + # this would create. + res.refcount = rcIncrement + decRef(ol) else: sysAssert(ol.typ != nil, "growObj: 5") zeroMem(ol, sizeof(TCell)) @@ -876,7 +884,7 @@ elif stackIncreases: var jmpbufSize {.importc: "sizeof(jmp_buf)", nodecl.}: int # a little hack to get the size of a TJmpBuf in the generated C code - # in a platform independant way + # in a platform independent way template forEachStackSlot(gch, gcMark: expr) {.immediate, dirty.} = var registers: C_JmpBuf diff --git a/lib/system/gc2.nim b/lib/system/gc2.nim index b0173b78f3..ae2d2c85d5 100644 --- a/lib/system/gc2.nim +++ b/lib/system/gc2.nim @@ -128,7 +128,7 @@ type # cycle roots table that uses a cheap linear scan # to find only possitively dead objects. # One strategy is to perform it only for new objects - # allocated between the invocations of CollectZCT. + # allocated between the invocations of collectZCT. # This index indicates the start of the range of # such new objects within the table. when withRealTime: @@ -140,7 +140,7 @@ var gch* {.rtlThreadVar.}: TGcHeap when not defined(useNimRtl): - InstantiateForRegion(gch.region) + instantiateForRegion(gch.region) template acquire(gch: TGcHeap) = when hasThreadSupport and hasSharedHeap: @@ -233,11 +233,11 @@ template addCycleRoot(cycleRoots: var TCellSeq, c: PCell) = proc cellToUsr(cell: PCell): pointer {.inline.} = # convert object (=pointer to refcount) to pointer to userdata - result = cast[pointer](cast[TAddress](cell)+%TAddress(sizeof(TCell))) + result = cast[pointer](cast[ByteAddress](cell)+%ByteAddress(sizeof(TCell))) proc usrToCell*(usr: pointer): PCell {.inline.} = # convert pointer to userdata to object (=pointer to refcount) - result = cast[PCell](cast[TAddress](usr)-%TAddress(sizeof(TCell))) + result = cast[PCell](cast[ByteAddress](usr)-%ByteAddress(sizeof(TCell))) proc canbeCycleRoot(c: PCell): bool {.inline.} = result = ntfAcyclic notin c.typ.flags @@ -255,10 +255,10 @@ when BitsPerPage mod (sizeof(int)*8) != 0: # forward declarations: proc collectCT(gch: var TGcHeap) -proc IsOnStack*(p: pointer): bool {.noinline.} +proc isOnStack*(p: pointer): bool {.noinline.} proc forAllChildren(cell: PCell, op: TWalkOp) proc doOperation(p: pointer, op: TWalkOp) -proc forAllChildrenAux(dest: Pointer, mt: PNimType, op: TWalkOp) +proc forAllChildrenAux(dest: pointer, mt: PNimType, op: TWalkOp) # we need the prototype here for debugging purposes proc prepareDealloc(cell: PCell) = @@ -432,7 +432,7 @@ proc nimGCunrefNoCycle(p: pointer) {.compilerProc, inline.} = sysAssert(allocInv(gch.region), "end nimGCunrefNoCycle 2") sysAssert(allocInv(gch.region), "end nimGCunrefNoCycle 5") -template doAsgnRef(dest: ppointer, src: pointer, +template doAsgnRef(dest: PPointer, src: pointer, heapType = LocalHeap, cycleFlag = MaybeCyclic): stmt = sysAssert(not isOnStack(dest), "asgnRef") # BUGFIX: first incRef then decRef! @@ -440,20 +440,20 @@ template doAsgnRef(dest: ppointer, src: pointer, if dest[] != nil: doDecRef(usrToCell(dest[]), heapType, cycleFlag) dest[] = src -proc asgnRef(dest: ppointer, src: pointer) {.compilerProc, inline.} = +proc asgnRef(dest: PPointer, src: pointer) {.compilerProc, inline.} = # the code generator calls this proc! doAsgnRef(dest, src, LocalHeap, MaybeCyclic) -proc asgnRefNoCycle(dest: ppointer, src: pointer) {.compilerProc, inline.} = +proc asgnRefNoCycle(dest: PPointer, src: pointer) {.compilerProc, inline.} = # the code generator calls this proc if it is known at compile time that no # cycle is possible. doAsgnRef(dest, src, LocalHeap, Acyclic) -proc unsureAsgnRef(dest: ppointer, src: pointer) {.compilerProc.} = +proc unsureAsgnRef(dest: PPointer, src: pointer) {.compilerProc.} = # unsureAsgnRef updates the reference counters only if dest is not on the # stack. It is used by the code generator if it cannot decide wether a # reference is in the stack or not (this can happen for var parameters). - if not IsOnStack(dest): + if not isOnStack(dest): if src != nil: doIncRef(usrToCell(src)) # XXX we must detect a shared heap here # better idea may be to just eliminate the need for unsureAsgnRef @@ -470,16 +470,16 @@ proc unsureAsgnRef(dest: ppointer, src: pointer) {.compilerProc.} = when hasThreadSupport and hasSharedHeap: # shared heap version of the above procs - proc asgnRefSh(dest: ppointer, src: pointer) {.compilerProc, inline.} = + proc asgnRefSh(dest: PPointer, src: pointer) {.compilerProc, inline.} = doAsgnRef(dest, src, SharedHeap, MaybeCyclic) - proc asgnRefNoCycleSh(dest: ppointer, src: pointer) {.compilerProc, inline.} = + proc asgnRefNoCycleSh(dest: PPointer, src: pointer) {.compilerProc, inline.} = doAsgnRef(dest, src, SharedHeap, Acyclic) proc initGC() = when not defined(useNimRtl): when traceGC: - for i in low(TCellState)..high(TCellState): Init(states[i]) + for i in low(TCellState)..high(TCellState): init(states[i]) gch.cycleThreshold = InitialCycleThreshold gch.stat.stackScans = 0 gch.stat.cycleCollections = 0 @@ -491,11 +491,11 @@ proc initGC() = init(gch.zct) init(gch.tempStack) init(gch.freeStack) - Init(gch.cycleRoots) - Init(gch.decStack) + init(gch.cycleRoots) + init(gch.decStack) proc forAllSlotsAux(dest: pointer, n: ptr TNimNode, op: TWalkOp) = - var d = cast[TAddress](dest) + var d = cast[ByteAddress](dest) case n.kind of nkSlot: forAllChildrenAux(cast[pointer](d +% n.offset), n.typ, op) of nkList: @@ -503,7 +503,7 @@ proc forAllSlotsAux(dest: pointer, n: ptr TNimNode, op: TWalkOp) = # inlined for speed if n.sons[i].kind == nkSlot: if n.sons[i].typ.kind in {tyRef, tyString, tySequence}: - doOperation(cast[ppointer](d +% n.sons[i].offset)[], op) + doOperation(cast[PPointer](d +% n.sons[i].offset)[], op) else: forAllChildrenAux(cast[pointer](d +% n.sons[i].offset), n.sons[i].typ, op) @@ -514,19 +514,19 @@ proc forAllSlotsAux(dest: pointer, n: ptr TNimNode, op: TWalkOp) = if m != nil: forAllSlotsAux(dest, m, op) of nkNone: sysAssert(false, "forAllSlotsAux") -proc forAllChildrenAux(dest: Pointer, mt: PNimType, op: TWalkOp) = - var d = cast[TAddress](dest) +proc forAllChildrenAux(dest: pointer, mt: PNimType, op: TWalkOp) = + var d = cast[ByteAddress](dest) if dest == nil: return # nothing to do if ntfNoRefs notin mt.flags: - case mt.Kind + case mt.kind of tyRef, tyString, tySequence: # leaf: - doOperation(cast[ppointer](d)[], op) + doOperation(cast[PPointer](d)[], op) of tyObject, tyTuple: forAllSlotsAux(dest, mt.node, op) of tyArray, tyArrayConstr, tyOpenArray: for i in 0..(mt.size div mt.base.size)-1: forAllChildrenAux(cast[pointer](d +% i *% mt.base.size), mt.base, op) - else: nil + else: discard proc forAllChildren(cell: PCell, op: TWalkOp) = sysAssert(cell != nil, "forAllChildren: 1") @@ -536,18 +536,18 @@ proc forAllChildren(cell: PCell, op: TWalkOp) = if marker != nil: marker(cellToUsr(cell), op.int) else: - case cell.typ.Kind + case cell.typ.kind of tyRef: # common case forAllChildrenAux(cellToUsr(cell), cell.typ.base, op) of tySequence: - var d = cast[TAddress](cellToUsr(cell)) + var d = cast[ByteAddress](cellToUsr(cell)) var s = cast[PGenericSeq](d) if s != nil: let baseAddr = d +% GenericSeqSize for i in 0..s.len-1: forAllChildrenAux(cast[pointer](baseAddr +% i *% cell.typ.base.size), cell.typ.base, op) - else: nil + else: discard proc addNewObjToZCT(res: PCell, gch: var TGcHeap) {.inline.} = # we check the last 8 entries (cache line) for a slot that could be reused. @@ -605,7 +605,7 @@ proc rawNewObj(typ: PNimType, size: int, gch: var TGcHeap, rc1: bool): pointer = var res = cast[PCell](rawAlloc(gch.region, size + sizeof(TCell))) sysAssert(allocInv(gch.region), "rawNewObj after rawAlloc") - sysAssert((cast[TAddress](res) and (MemAlign-1)) == 0, "newObj: 2") + sysAssert((cast[ByteAddress](res) and (MemAlign-1)) == 0, "newObj: 2") res.typ = typ @@ -708,10 +708,10 @@ proc growObj(old: pointer, newsize: int, gch: var TGcHeap): pointer = # call user-defined move code # call user-defined default constructor copyMem(res, ol, oldsize + sizeof(TCell)) - zeroMem(cast[pointer](cast[TAddress](res)+% oldsize +% sizeof(TCell)), + zeroMem(cast[pointer](cast[ByteAddress](res)+% oldsize +% sizeof(TCell)), newsize-oldsize) - sysAssert((cast[TAddress](res) and (MemAlign-1)) == 0, "growObj: 3") + sysAssert((cast[ByteAddress](res) and (MemAlign-1)) == 0, "growObj: 3") sysAssert(res.refcount shr rcShift <=% 1, "growObj: 4") when false: @@ -786,10 +786,10 @@ type FromChildren, FromRoot -proc CollectZCT(gch: var TGcHeap): bool +proc collectZCT(gch: var TGcHeap): bool template pseudoRecursion(typ: TRecursionType, body: stmt): stmt = - # + discard proc trimCycleRoots(gch: var TGcHeap, startIdx = gch.cycleRootsTrimIdx) = var i = startIdx @@ -967,17 +967,17 @@ proc collectCycles(gch: var TGcHeap) = maybedeads, collected - Deinit(gch.cycleRoots) - Init(gch.cycleRoots) + deinit(gch.cycleRoots) + init(gch.cycleRoots) - Deinit(gch.freeStack) - Init(gch.freeStack) + deinit(gch.freeStack) + init(gch.freeStack) when MarkingSkipsAcyclicObjects: # Collect the acyclic objects that became unreachable due to collected # cyclic objects. - discard CollectZCT(gch) - # CollectZCT may add new cycle candidates and we may decide to loop here + discard collectZCT(gch) + # collectZCT may add new cycle candidates and we may decide to loop here # if gch.cycleRoots.len > 0: repeat var gcDebugging* = false @@ -988,7 +988,7 @@ proc gcMark(gch: var TGcHeap, p: pointer) {.inline.} = # the addresses are not as cells on the stack, so turn them to cells: sysAssert(allocInv(gch.region), "gcMark begin") var cell = usrToCell(p) - var c = cast[TAddress](cell) + var c = cast[ByteAddress](cell) if c >% PageSize: # fast check: does it look like a cell? var objStart = cast[PCell](interiorAllocatedPtr(gch.region, cell)) @@ -997,6 +997,7 @@ proc gcMark(gch: var TGcHeap, p: pointer) {.inline.} = if objStart.color != rcReallyDead: if gcDebugging: # writeCell("marking ", objStart) + discard else: inc objStart.refcount, rcIncrement gch.decStack.add objStart @@ -1009,6 +1010,7 @@ proc gcMark(gch: var TGcHeap, p: pointer) {.inline.} = # coincidence due to the conservative stack marking. when debugGC: # writeCell("marking dead object", objStart) + discard when false: if isAllocatedPtr(gch.region, cell): sysAssert false, "allocated pointer but not interior?" @@ -1024,12 +1026,12 @@ proc markThreadStacks(gch: var TGcHeap) = while it != nil: # mark registers: for i in 0 .. high(it.registers): gcMark(gch, it.registers[i]) - var sp = cast[TAddress](it.stackBottom) - var max = cast[TAddress](it.stackTop) + var sp = cast[ByteAddress](it.stackBottom) + var max = cast[ByteAddress](it.stackTop) # XXX stack direction? # XXX unroll this loop: while sp <=% max: - gcMark(gch, cast[ppointer](sp)[]) + gcMark(gch, cast[PPointer](sp)[]) sp = sp +% sizeof(pointer) it = it.next @@ -1051,8 +1053,8 @@ when not defined(useNimRtl): # the first init must be the one that defines the stack bottom: if gch.stackBottom == nil: gch.stackBottom = theStackBottom else: - var a = cast[TAddress](theStackBottom) # and not PageMask - PageSize*2 - var b = cast[TAddress](gch.stackBottom) + var a = cast[ByteAddress](theStackBottom) # and not PageMask - PageSize*2 + var b = cast[ByteAddress](gch.stackBottom) #c_fprintf(c_stdout, "old: %p new: %p;\n",gch.stackBottom,theStackBottom) when stackIncreases: gch.stackBottom = cast[pointer](min(a, b)) @@ -1067,15 +1069,15 @@ proc stackSize(): int {.noinline.} = var jmpbufSize {.importc: "sizeof(jmp_buf)", nodecl.}: int # a little hack to get the size of a TJmpBuf in the generated C code - # in a platform independant way + # in a platform independent way when defined(sparc): # For SPARC architecture. proc isOnStack(p: pointer): bool = var stackTop {.volatile.}: pointer stackTop = addr(stackTop) - var b = cast[TAddress](gch.stackBottom) - var a = cast[TAddress](stackTop) - var x = cast[TAddress](p) + var b = cast[ByteAddress](gch.stackBottom) + var a = cast[ByteAddress](stackTop) + var x = cast[ByteAddress](p) result = a <=% x and x <=% b proc markStackAndRegisters(gch: var TGcHeap) {.noinline, cdecl.} = @@ -1092,7 +1094,7 @@ when defined(sparc): # For SPARC architecture. # Addresses decrease as the stack grows. while sp <= max: gcMark(gch, sp[]) - sp = cast[ppointer](cast[TAddress](sp) +% sizeof(pointer)) + sp = cast[PPointer](cast[ByteAddress](sp) +% sizeof(pointer)) elif defined(ELATE): {.error: "stack marking code is to be written for this architecture".} @@ -1104,20 +1106,20 @@ elif stackIncreases: proc isOnStack(p: pointer): bool = var stackTop {.volatile.}: pointer stackTop = addr(stackTop) - var a = cast[TAddress](gch.stackBottom) - var b = cast[TAddress](stackTop) - var x = cast[TAddress](p) + var a = cast[ByteAddress](gch.stackBottom) + var b = cast[ByteAddress](stackTop) + var x = cast[ByteAddress](p) result = a <=% x and x <=% b proc markStackAndRegisters(gch: var TGcHeap) {.noinline, cdecl.} = var registers: C_JmpBuf if c_setjmp(registers) == 0'i32: # To fill the C stack with registers. - var max = cast[TAddress](gch.stackBottom) - var sp = cast[TAddress](addr(registers)) +% jmpbufSize -% sizeof(pointer) + var max = cast[ByteAddress](gch.stackBottom) + var sp = cast[ByteAddress](addr(registers)) +% jmpbufSize -% sizeof(pointer) # sp will traverse the JMP_BUF as well (jmp_buf size is added, # otherwise sp would be below the registers structure). while sp >=% max: - gcMark(gch, cast[ppointer](sp)[]) + gcMark(gch, cast[PPointer](sp)[]) sp = sp -% sizeof(pointer) else: @@ -1127,9 +1129,9 @@ else: proc isOnStack(p: pointer): bool = var stackTop {.volatile.}: pointer stackTop = addr(stackTop) - var b = cast[TAddress](gch.stackBottom) - var a = cast[TAddress](stackTop) - var x = cast[TAddress](p) + var b = cast[ByteAddress](gch.stackBottom) + var a = cast[ByteAddress](stackTop) + var x = cast[ByteAddress](p) result = a <=% x and x <=% b proc markStackAndRegisters(gch: var TGcHeap) {.noinline, cdecl.} = @@ -1141,18 +1143,18 @@ else: if c_setjmp(registers) == 0'i32: # To fill the C stack with registers. when MinimumStackMarking: # mark the registers - var jmpbufPtr = cast[TAddress](addr(registers)) + var jmpbufPtr = cast[ByteAddress](addr(registers)) var jmpbufEnd = jmpbufPtr +% jmpbufSize while jmpbufPtr <=% jmpbufEnd: - gcMark(gch, cast[ppointer](jmpbufPtr)[]) + gcMark(gch, cast[PPointer](jmpbufPtr)[]) jmpbufPtr = jmpbufPtr +% sizeof(pointer) - var sp = cast[TAddress](gch.stackTop) + var sp = cast[ByteAddress](gch.stackTop) else: - var sp = cast[TAddress](addr(registers)) + var sp = cast[ByteAddress](addr(registers)) # mark the user stack - var max = cast[TAddress](gch.stackBottom) + var max = cast[ByteAddress](gch.stackBottom) # loop unrolled: while sp <% max - 8*sizeof(pointer): gcMark(gch, cast[PStackSlice](sp)[0]) @@ -1166,7 +1168,7 @@ else: sp = sp +% sizeof(pointer)*8 # last few entries: while sp <=% max: - gcMark(gch, cast[ppointer](sp)[]) + gcMark(gch, cast[PPointer](sp)[]) sp = sp +% sizeof(pointer) # ---------------------------------------------------------------------------- @@ -1202,7 +1204,7 @@ proc releaseCell(gch: var TGcHeap, cell: PCell) = # recursion). # We can ignore it now as the ZCT cleaner will reach it soon. -proc CollectZCT(gch: var TGcHeap): bool = +proc collectZCT(gch: var TGcHeap): bool = const workPackage = 100 var L = addr(gch.zct.len) @@ -1213,8 +1215,8 @@ proc CollectZCT(gch: var TGcHeap): bool = while L[] > 0: var c = gch.zct.d[0] - sysAssert c.isBitUp(rcZct), "CollectZCT: rcZct missing!" - sysAssert(isAllocatedPtr(gch.region, c), "CollectZCT: isAllocatedPtr") + sysAssert c.isBitUp(rcZct), "collectZCT: rcZct missing!" + sysAssert(isAllocatedPtr(gch.region, c), "collectZCT: isAllocatedPtr") # remove from ZCT: c.clearBit(rcZct) @@ -1263,7 +1265,7 @@ proc unmarkStackAndRegisters(gch: var TGcHeap) = # XXX no need for an atomic dec here: if c.refcount--(LocalHeap): # the object survived only because of a stack reference - # it still doesn't have heap refernces + # it still doesn't have heap references addZCT(gch.zct, c) if canbeCycleRoot(c): @@ -1295,7 +1297,7 @@ proc collectCTBody(gch: var TGcHeap) = sysAssert gch.zct.len == 0, "zct is not null after collect cycles" inc(gch.stat.cycleCollections) gch.cycleThreshold = max(InitialCycleThreshold, getOccupiedMem() * - cycleIncrease) + CycleIncrease) gch.stat.maxThreshold = max(gch.stat.maxThreshold, gch.cycleThreshold) unmarkStackAndRegisters(gch) sysAssert(allocInv(gch.region), "collectCT: end") @@ -1346,10 +1348,10 @@ when not defined(useNimRtl): proc GC_setStrategy(strategy: GC_Strategy) = case strategy - of gcThroughput: nil - of gcResponsiveness: nil - of gcOptimizeSpace: nil - of gcOptimizeTime: nil + of gcThroughput: discard + of gcResponsiveness: discard + of gcOptimizeSpace: discard + of gcOptimizeTime: discard proc GC_enableMarkAndSweep() = gch.cycleThreshold = InitialCycleThreshold diff --git a/lib/system/gc_ms.nim b/lib/system/gc_ms.nim index 9c3ee8ce2b..6fbe942394 100644 --- a/lib/system/gc_ms.nim +++ b/lib/system/gc_ms.nim @@ -297,10 +297,12 @@ proc growObj(old: pointer, newsize: int, gch: var TGcHeap): pointer = zeroMem(cast[pointer](cast[ByteAddress](res)+% oldsize +% sizeof(TCell)), newsize-oldsize) sysAssert((cast[ByteAddress](res) and (MemAlign-1)) == 0, "growObj: 3") - when withBitvectors: excl(gch.allocated, ol) - when reallyDealloc: rawDealloc(gch.region, ol) - else: - zeroMem(ol, sizeof(TCell)) + when false: + # this is wrong since seqs can be shared via 'shallow': + when withBitvectors: excl(gch.allocated, ol) + when reallyDealloc: rawDealloc(gch.region, ol) + else: + zeroMem(ol, sizeof(TCell)) when withBitvectors: incl(gch.allocated, res) when useCellIds: inc gch.idGenerator @@ -474,7 +476,7 @@ elif stackIncreases: var jmpbufSize {.importc: "sizeof(jmp_buf)", nodecl.}: int # a little hack to get the size of a TJmpBuf in the generated C code - # in a platform independant way + # in a platform independent way proc markStackAndRegisters(gch: var TGcHeap) {.noinline, cdecl.} = var registers: C_JmpBuf diff --git a/lib/system/hti.nim b/lib/system/hti.nim index e599668a72..aff0c0e6f9 100644 --- a/lib/system/hti.nim +++ b/lib/system/hti.nim @@ -11,7 +11,7 @@ when declared(NimString): # we are in system module: {.pragma: codegenType, compilerproc.} else: - {.pragma: codegenType.} + {.pragma: codegenType, importc.} type # This should be he same as ast.TTypeKind @@ -26,7 +26,7 @@ type tyExpr, tyStmt, tyTypeDesc, - tyGenericInvokation, # ``T[a, b]`` for types to invoke + tyGenericInvocation, # ``T[a, b]`` for types to invoke tyGenericBody, # ``T[a, b, body]`` last parameter is the body tyGenericInst, # ``T[a, b, realInstance]`` instantiated generic type tyGenericParam, # ``a`` in the example @@ -65,7 +65,7 @@ type tyBigNum, TNimNodeKind = enum nkNone, nkSlot, nkList, nkCase - TNimNode {.codegenType, final.} = object + TNimNode {.codegenType.} = object kind: TNimNodeKind offset: int typ: ptr TNimType @@ -78,7 +78,7 @@ type ntfAcyclic = 1, # type cannot form a cycle ntfEnumHole = 2 # enum has holes and thus `$` for them needs the slow # version - TNimType {.codegenType, final.} = object + TNimType {.codegenType.} = object size: int kind: TNimKind flags: set[TNimTypeFlag] diff --git a/lib/system/jssys.nim b/lib/system/jssys.nim index 9b4a7d5568..15b00f8f1a 100644 --- a/lib/system/jssys.nim +++ b/lib/system/jssys.nim @@ -128,7 +128,7 @@ proc raiseOverflow {.exportc: "raiseOverflow", noreturn.} = raise newException(OverflowError, "over- or underflow") proc raiseDivByZero {.exportc: "raiseDivByZero", noreturn.} = - raise newException(DivByZeroError, "divison by zero") + raise newException(DivByZeroError, "division by zero") proc raiseRangeError() {.compilerproc, noreturn.} = raise newException(RangeError, "value out of range") diff --git a/lib/system/repr.nim b/lib/system/repr.nim index 2de603ceae..f1029ff6a7 100644 --- a/lib/system/repr.nim +++ b/lib/system/repr.nim @@ -30,14 +30,16 @@ proc reprStrAux(result: var string, s: string) = add result, "nil" return add result, reprPointer(cast[pointer](s)) & "\"" - for c in items(s): + for i in 0.. = 0: + let len = if file != stdin: rawFileSize(file) else: -1 + if len > 0: result = readAllFile(file, len).TaintedString else: result = readAllBuffer(file).TaintedString @@ -183,11 +183,17 @@ proc rawEchoNL() {.inline, compilerproc.} = write(stdout, "\n") when (defined(windows) and not defined(useWinAnsi)) or defined(nimdoc): include "system/widestrs" -when defined(windows) and not defined(useWinAnsi): - proc wfopen(filename, mode: WideCString): pointer {. - importc: "_wfopen", nodecl.} - proc wfreopen(filename, mode: WideCString, stream: File): File {. - importc: "_wfreopen", nodecl.} +when defined(windows) and not defined(useWinAnsi): + when defined(cpp): + proc wfopen(filename, mode: WideCString): pointer {. + importcpp: "_wfopen((const wchar_t*)#, (const wchar_t*)#)", nodecl.} + proc wfreopen(filename, mode: WideCString, stream: File): File {. + importcpp: "_wfreopen((const wchar_t*)#, (const wchar_t*)#, #)", nodecl.} + else: + proc wfopen(filename, mode: WideCString): pointer {. + importc: "_wfopen", nodecl.} + proc wfreopen(filename, mode: WideCString, stream: File): File {. + importc: "_wfreopen", nodecl.} proc fopen(filename, mode: cstring): pointer = var f = newWideCString(filename) @@ -240,14 +246,14 @@ proc fwrite(buf: pointer, size, n: int, f: File): int {. proc readBuffer(f: File, buffer: pointer, len: int): int = result = fread(buffer, 1, len, f) -proc readBytes(f: File, a: var openArray[int8], start, len: int): int = +proc readBytes(f: File, a: var openArray[int8|uint8], start, len: int): int = result = readBuffer(f, addr(a[start]), len) proc readChars(f: File, a: var openArray[char], start, len: int): int = result = readBuffer(f, addr(a[start]), len) {.push stackTrace:off, profiler:off.} -proc writeBytes(f: File, a: openArray[int8], start, len: int): int = +proc writeBytes(f: File, a: openArray[int8|uint8], start, len: int): 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 = diff --git a/lib/system/sysstr.nim b/lib/system/sysstr.nim index 440d040a5a..cfbc24f0cf 100644 --- a/lib/system/sysstr.nim +++ b/lib/system/sysstr.nim @@ -208,7 +208,7 @@ proc setLengthSeq(seq: PGenericSeq, elemSize, newLen: int): PGenericSeq {. when compileOption("gc", "v2"): for i in newLen..result.len-1: let len0 = gch.tempStack.len - forAllChildrenAux(cast[pointer](cast[TAddress](result) +% + forAllChildrenAux(cast[pointer](cast[ByteAddress](result) +% GenericSeqSize +% (i*%elemSize)), extGetCellType(result).base, waPush) let len1 = gch.tempStack.len @@ -222,9 +222,9 @@ proc setLengthSeq(seq: PGenericSeq, elemSize, newLen: int): PGenericSeq {. extGetCellType(result).base, waZctDecRef) # XXX: zeroing out the memory can still result in crashes if a wiped-out - # cell is aliased by another pointer (ie proc paramter or a let variable). + # cell is aliased by another pointer (ie proc parameter or a let variable). # This is a tought problem, because even if we don't zeroMem here, in the - # presense of user defined destructors, the user will expect the cell to be + # presence of user defined destructors, the user will expect the cell to be # "destroyed" thus creating the same problem. We can destoy the cell in the # finalizer of the sequence, but this makes destruction non-deterministic. zeroMem(cast[pointer](cast[ByteAddress](result) +% GenericSeqSize +% @@ -264,7 +264,17 @@ proc nimFloatToStr(f: float): string {.compilerproc.} = buf[n] = '.' buf[n+1] = '0' buf[n+2] = '\0' - result = $buf + # On Windows nice numbers like '1.#INF', '-1.#INF' or '1.#NAN' are produced. + # We want to get rid of these here: + if buf[n-1] == 'N': + result = "nan" + elif buf[n-1] == 'F': + if buf[0] == '-': + result = "-inf" + else: + result = "inf" + else: + result = $buf proc strtod(buf: cstring, endptr: ptr cstring): float64 {.importc, header: "", noSideEffect.} diff --git a/lib/system/threads.nim b/lib/system/threads.nim index 496c31af14..81d9e5d73c 100644 --- a/lib/system/threads.nim +++ b/lib/system/threads.nim @@ -84,8 +84,18 @@ when defined(windows): importc: "TlsAlloc", stdcall, header: "".} proc threadVarSetValue(dwTlsIndex: TThreadVarSlot, lpTlsValue: pointer) {. importc: "TlsSetValue", stdcall, header: "".} - proc threadVarGetValue(dwTlsIndex: TThreadVarSlot): pointer {. + proc tlsGetValue(dwTlsIndex: TThreadVarSlot): pointer {. importc: "TlsGetValue", stdcall, header: "".} + + proc getLastError(): uint32 {. + importc: "GetLastError", stdcall, header: "".} + proc setLastError(x: uint32) {. + importc: "SetLastError", stdcall, header: "".} + + proc threadVarGetValue(dwTlsIndex: TThreadVarSlot): pointer = + let realLastError = getLastError() + result = tlsGetValue(dwTlsIndex) + setLastError(realLastError) else: proc threadVarAlloc(): TThreadVarSlot {. importc: "TlsAlloc", stdcall, dynlib: "kernel32".} @@ -95,7 +105,9 @@ when defined(windows): importc: "TlsGetValue", stdcall, dynlib: "kernel32".} else: - {.passL: "-pthread".} + when not defined(macosx): + {.passL: "-pthread".} + {.passC: "-pthread".} type diff --git a/lib/system/timers.nim b/lib/system/timers.nim index e58ff7adc0..e5de791ac8 100644 --- a/lib/system/timers.nim +++ b/lib/system/timers.nim @@ -27,9 +27,9 @@ when defined(windows): proc `-`(a, b: TTicks): TNanos = var frequency: int64 QueryPerformanceFrequency(frequency) - var performanceCounterRate = 1000000000.0 / toFloat(frequency.int) + var performanceCounterRate = 1e+9'f64 / float64(frequency) - result = ((a.int64 - b.int64).int.toFloat * performanceCounterRate).TNanos + result = TNanos(float64(a.int64 - b.int64) * performanceCounterRate) elif defined(macosx): type diff --git a/lib/windows/windows.nim b/lib/windows/windows.nim index f0895217bb..43c595a644 100644 --- a/lib/windows/windows.nim +++ b/lib/windows/windows.nim @@ -11616,7 +11616,7 @@ type dwPageSize*: DWORD lpMinimumApplicationAddress*: LPVOID lpMaximumApplicationAddress*: LPVOID - dwActiveProcessorMask*: DWORD + dwActiveProcessorMask*: DWORD_PTR dwNumberOfProcessors*: DWORD dwProcessorType*: DWORD dwAllocationGranularity*: DWORD diff --git a/lib/windows/winlean.nim b/lib/windows/winlean.nim index 51a12141b1..584f7cf482 100644 --- a/lib/windows/winlean.nim +++ b/lib/windows/winlean.nim @@ -284,6 +284,10 @@ when useWinUnicode: bFailIfExists: cint): cint {. importc: "CopyFileW", stdcall, dynlib: "kernel32".} + proc moveFileW*(lpExistingFileName, lpNewFileName: WideCString, + bFailIfExists: cint): cint {. + importc: "MoveFileW", stdcall, dynlib: "kernel32".} + proc getEnvironmentStringsW*(): WideCString {. stdcall, dynlib: "kernel32", importc: "GetEnvironmentStringsW".} proc freeEnvironmentStringsW*(para1: WideCString): int32 {. @@ -308,6 +312,10 @@ else: bFailIfExists: cint): cint {. importc: "CopyFileA", stdcall, dynlib: "kernel32".} + proc moveFileA*(lpExistingFileName, lpNewFileName: cstring, + bFailIfExists: cint): cint {. + importc: "MoveFileA", stdcall, dynlib: "kernel32".} + proc getEnvironmentStringsA*(): cstring {. stdcall, dynlib: "kernel32", importc: "GetEnvironmentStringsA".} proc freeEnvironmentStringsA*(para1: cstring): int32 {. diff --git a/lib/wrappers/claro.nim b/lib/wrappers/claro.nim index fb06da8188..d36b9f1783 100644 --- a/lib/wrappers/claro.nim +++ b/lib/wrappers/claro.nim @@ -64,7 +64,7 @@ proc node_move*(n: ptr TNode, oldlist: ptr TList, newlist: ptr TList){. cdecl, importc: "node_move", dynlib: clarodll.} type - TClaroObj*{.pure.} = object + TClaroObj*{.pure, inheritable.} = object typ*: array[0..64 - 1, char] destroy_pending*: cint event_handlers*: TList @@ -86,7 +86,7 @@ type TEventHandler*{.pure.} = object typ*: array[0..32 - 1, char] data*: pointer - func*: TEventFunc # the function that handles this event + fun*: TEventFunc # the function that handles this event # #define event_handler(n) void n ( TClaroObj *object, event_t *event ) @@ -121,10 +121,10 @@ proc object_set_parent*(obj: ptr TClaroObj, parent: ptr TClaroObj){.cdecl, # event functions proc object_addhandler*(obj: ptr TClaroObj, event: cstring, - func: TEventFunc){.cdecl, + fun: TEventFunc){.cdecl, importc: "object_addhandler", dynlib: clarodll.} proc object_addhandler_interface*(obj: ptr TClaroObj, event: cstring, - func: TEventFunc, data: pointer){.cdecl, + fun: TEventFunc, data: pointer){.cdecl, importc: "object_addhandler_interface", dynlib: clarodll.} proc event_send*(obj: ptr TClaroObj, event: cstring, fmt: cstring): cint{. varargs, cdecl, importc: "event_send", dynlib: clarodll.} @@ -258,7 +258,7 @@ proc image_load_inline_png*(parent: ptr TClaroObj, data: cstring, ## len size of data when true: - nil + discard else: # status icons are not supported on all platforms yet: type @@ -682,7 +682,7 @@ const type TCanvas*{.pure.} = object of TWidget surface*: cairo.PSurface - cr*: Cairo.PContext + cr*: cairo.PContext surfdata*: pointer fontdata*: pointer font_height*: cint @@ -854,7 +854,7 @@ proc canvas_cairo_buffered_text_display_count*(widget: ptr TCanvas, text: cstring, width: cint): cint{.cdecl, importc: "canvas_cairo_buffered_text_display_count", dynlib: clarodll.} -proc canvas_get_cairo_context*(widget: ptr TCanvas): Cairo.PContext {.cdecl, +proc canvas_get_cairo_context*(widget: ptr TCanvas): cairo.PContext {.cdecl, importc: "canvas_get_cairo_context", dynlib: clarodll.} type diff --git a/lib/wrappers/libffi/msvc/win32.c b/lib/wrappers/libffi/msvc/win32.c index d1149a85eb..2754fd35d7 100644 --- a/lib/wrappers/libffi/msvc/win32.c +++ b/lib/wrappers/libffi/msvc/win32.c @@ -90,7 +90,7 @@ noclean: // If the return value pointer is NULL, assume no return value. /* - Intel asm is weird. We have to explicitely specify 'DWORD PTR' in the nexr instruction, + Intel asm is weird. We have to explicitly specify 'DWORD PTR' in the nexr instruction, otherwise only one BYTE will be compared (instead of a DWORD)! */ cmp DWORD PTR [ebp + 24], 0 diff --git a/lib/wrappers/libuv.nim b/lib/wrappers/libuv.nim index 3189ec4083..a52ae0f635 100644 --- a/lib/wrappers/libuv.nim +++ b/lib/wrappers/libuv.nim @@ -30,9 +30,9 @@ type CheckProc* = proc (handle: PCheck, status: cint) {.cdecl.} IdleProc* = proc (handle: PIdle, status: cint) {.cdecl.} - PSockAddr* = ptr TSockAddr + PSockAddr* = ptr SockAddr - GetAddrInfoProc* = proc (handle: PGetAddrInfo, status: cint, res: ptr TAddrInfo) + GetAddrInfoProc* = proc (handle: PGetAddrInfo, status: cint, res: ptr AddrInfo) ExitProc* = proc (a2: PProcess, exit_status: cint, term_signal: cint) FsProc* = proc (req: PFS) @@ -210,7 +210,7 @@ type cunsigned = int UdpSendProc* = proc (req: PUdpSend, status: cint) - UdpRecvProc* = proc (handle: PUdp, nread: cssize, buf: TBuf, adr: ptr TSockAddr, flags: cunsigned) + UdpRecvProc* = proc (handle: PUdp, nread: cssize, buf: TBuf, adr: ptr SockAddr, flags: cunsigned) TUdp* {.pure, final, importc: "uv_udp_t", header: "uv.h".} = object loop* {.importc: "loop".}: PLoop @@ -366,7 +366,7 @@ type tcp_port* {.importc: "tcp_port".}: TPort socket_send_buffer_size* {.importc: "socket_send_buffer_size".}: int socket_recv_buffer_size* {.importc: "socket_receive_buffer_size".}: int - servers* {.importc: "servers".}: ptr TInAddr + servers* {.importc: "servers".}: ptr InAddr nservers* {.importc: "nservers".}: int domains* {.importc: "domains".}: ptr cstring ndomains* {.importc: "ndomains".}: int @@ -450,19 +450,19 @@ proc write*(req: PWrite, handle: PStream, bufs: ptr TBuf, bufcnt: cint, send_han proc tcp_init*(a2: PLoop, handle: PTcp): cint{. importc: "uv_tcp_init", header: "uv.h".} -proc tcp_bind*(handle: PTcp, a3: TSockAddrIn): cint{. +proc tcp_bind*(handle: PTcp, a3: SockAddrIn): cint{. importc: "uv_tcp_bind", header: "uv.h".} proc tcp_bind6*(handle: PTcp, a3: TSockAddrIn6): cint{. importc: "uv_tcp_bind6", header: "uv.h".} -proc tcp_getsockname*(handle: PTcp, name: ptr TSockAddr, namelen: var cint): cint{. +proc tcp_getsockname*(handle: PTcp, name: ptr SockAddr, namelen: var cint): cint{. importc: "uv_tcp_getsockname", header: "uv.h".} -proc tcp_getpeername*(handle: PTcp, name: ptr TSockAddr, namelen: var cint): cint{. +proc tcp_getpeername*(handle: PTcp, name: ptr SockAddr, namelen: var cint): cint{. importc: "uv_tcp_getpeername", header: "uv.h".} -proc tcp_connect*(req: PConnect, handle: PTcp, address: TSockAddrIn, cb: ConnectProc): cint{. +proc tcp_connect*(req: PConnect, handle: PTcp, address: SockAddrIn, cb: ConnectProc): cint{. importc: "uv_tcp_connect", header: "uv.h".} proc tcp_connect6*(req: PConnect, handle: PTcp, address: TSockAddrIn6, cb: ConnectProc): cint{. @@ -471,16 +471,16 @@ proc tcp_connect6*(req: PConnect, handle: PTcp, address: TSockAddrIn6, cb: Conne proc udp_init*(a2: PLoop, handle: PUdp): cint{. importc: "uv_udp_init", header: "uv.h".} -proc udp_bind*(handle: PUdp, adr: TSockAddrIn, flags: cunsigned): cint{. +proc udp_bind*(handle: PUdp, adr: SockAddrIn, flags: cunsigned): cint{. importc: "uv_udp_bind", header: "uv.h".} proc udp_bind6*(handle: PUdp, adr: TSockAddrIn6, flags: cunsigned): cint{. importc: "uv_udp_bind6", header: "uv.h".} -proc udp_getsockname*(handle: PUdp, name: ptr TSockAddr, namelen: var cint): cint{. +proc udp_getsockname*(handle: PUdp, name: ptr SockAddr, namelen: var cint): cint{. importc: "uv_udp_getsockname", header: "uv.h".} -proc udp_send*(req: PUdpSend, handle: PUdp, bufs: ptr TBuf, bufcnt: cint, adr: TSockAddrIn, send_cb: UdpSendProc): cint{. +proc udp_send*(req: PUdpSend, handle: PUdp, bufs: ptr TBuf, bufcnt: cint, adr: SockAddrIn, send_cb: UdpSendProc): cint{. importc: "uv_udp_send", header: "uv.h".} proc udp_send6*(req: PUdpSend, handle: PUdp, bufs: ptr TBuf, bufcnt: cint, adr: TSockAddrIn6, send_cb: UdpSendProc): cint{. @@ -492,7 +492,7 @@ proc udp_recv_start*(handle: PUdp, alloc_cb: AllocProc, recv_cb: UdpRecvProc): c proc udp_recv_stop*(handle: PUdp): cint{. importc: "uv_udp_recv_stop", header: "uv.h".} -proc tty_init*(a2: PLoop, a3: pTTy, fd: TFile): cint{. +proc tty_init*(a2: PLoop, a3: pTTy, fd: File): cint{. importc: "uv_tty_init", header: "uv.h".} proc tty_set_mode*(a2: pTTy, mode: cint): cint{. @@ -504,13 +504,13 @@ proc tty_get_winsize*(a2: pTTy, width: var cint, height: var cint): cint{. proc tty_reset_mode*() {. importc: "uv_tty_reset_mode", header: "uv.h".} -proc guess_handle*(file: TFile): THandleType{. +proc guess_handle*(file: File): THandleType{. importc: "uv_guess_handle", header: "uv.h".} proc pipe_init*(a2: PLoop, handle: PPipe, ipc: int): cint{. importc: "uv_pipe_init", header: "uv.h".} -proc pipe_open*(a2: PPipe, file: TFile){. +proc pipe_open*(a2: PPipe, file: File){. importc: "uv_pipe_open", header: "uv.h".} proc pipe_bind*(handle: PPipe, name: cstring): cint{. @@ -576,10 +576,10 @@ proc ares_init_options*(a2: PLoop, channel: PAresChannel, options: PAresOptions, proc ares_destroy*(a2: PLoop, channel: PAresChannel){. importc: "uv_ares_destroy", header: "uv.h".} -proc getaddrinfo*(a2: PLoop, handle: PGetAddrInfo,getaddrinfo_cb: GetAddrInfoProc, node: cstring, service: cstring, hints: ptr TAddrInfo): cint{. +proc getaddrinfo*(a2: PLoop, handle: PGetAddrInfo,getaddrinfo_cb: GetAddrInfoProc, node: cstring, service: cstring, hints: ptr AddrInfo): cint{. importc: "uv_getaddrinfo", header: "uv.h".} -proc freeaddrinfo*(ai: ptr TAddrInfo){. +proc freeaddrinfo*(ai: ptr AddrInfo){. importc: "uv_freeaddrinfo", header: "uv.h".} proc spawn*(a2: PLoop, a3: PProcess, options: TProcessOptions): cint{. @@ -594,19 +594,19 @@ proc queue_work*(loop: PLoop, req: PWork, work_cb: WorkProc, after_work_cb: Afte proc req_cleanup*(req: PFS){. importc: "uv_fs_req_cleanup", header: "uv.h".} -proc close*(loop: PLoop, req: PFS, file: TFile, cb: FsProc): cint{. +proc close*(loop: PLoop, req: PFS, file: File, cb: FsProc): cint{. importc: "uv_fs_close", header: "uv.h".} proc open*(loop: PLoop, req: PFS, path: cstring, flags: cint, mode: cint, cb: FsProc): cint{. importc: "uv_fs_open", header: "uv.h".} -proc read*(loop: PLoop, req: PFS, file: TFile, buf: pointer, length: csize, offset: coff, cb: FsProc): cint{. +proc read*(loop: PLoop, req: PFS, file: File, buf: pointer, length: csize, offset: coff, cb: FsProc): cint{. importc: "uv_fs_read", header: "uv.h".} proc unlink*(loop: PLoop, req: PFS, path: cstring, cb: FsProc): cint{. importc: "uv_fs_unlink", header: "uv.h".} -proc write*(loop: PLoop, req: PFS, file: TFile, buf: pointer, length: csize, offset: coff, cb: FsProc): cint{. +proc write*(loop: PLoop, req: PFS, file: File, buf: pointer, length: csize, offset: coff, cb: FsProc): cint{. importc: "uv_fs_write", header: "uv.h".} proc mkdir*(loop: PLoop, req: PFS, path: cstring, mode: cint, cb: FsProc): cint{. @@ -621,22 +621,22 @@ proc readdir*(loop: PLoop, req: PFS, path: cstring, flags: cint, cb: FsProc): ci proc stat*(loop: PLoop, req: PFS, path: cstring, cb: FsProc): cint{. importc: "uv_fs_stat", header: "uv.h".} -proc fstat*(loop: PLoop, req: PFS, file: TFile, cb: FsProc): cint{. +proc fstat*(loop: PLoop, req: PFS, file: File, cb: FsProc): cint{. importc: "uv_fs_fstat", header: "uv.h".} proc rename*(loop: PLoop, req: PFS, path: cstring, new_path: cstring, cb: FsProc): cint{. importc: "uv_fs_rename", header: "uv.h".} -proc fsync*(loop: PLoop, req: PFS, file: TFile, cb: FsProc): cint{. +proc fsync*(loop: PLoop, req: PFS, file: File, cb: FsProc): cint{. importc: "uv_fs_fsync", header: "uv.h".} -proc fdatasync*(loop: PLoop, req: PFS, file: TFile, cb: FsProc): cint{. +proc fdatasync*(loop: PLoop, req: PFS, file: File, cb: FsProc): cint{. importc: "uv_fs_fdatasync", header: "uv.h".} -proc ftruncate*(loop: PLoop, req: PFS, file: TFile, offset: coff, cb: FsProc): cint{. +proc ftruncate*(loop: PLoop, req: PFS, file: File, offset: coff, cb: FsProc): cint{. importc: "uv_fs_ftruncate", header: "uv.h".} -proc sendfile*(loop: PLoop, req: PFS, out_fd: TFile, in_fd: TFile, in_offset: coff, length: csize, cb: FsProc): cint{. +proc sendfile*(loop: PLoop, req: PFS, out_fd: File, in_fd: File, in_offset: coff, length: csize, cb: FsProc): cint{. importc: "uv_fs_sendfile", header: "uv.h".} proc chmod*(loop: PLoop, req: PFS, path: cstring, mode: cint, cb: FsProc): cint{. @@ -645,7 +645,7 @@ proc chmod*(loop: PLoop, req: PFS, path: cstring, mode: cint, cb: FsProc): cint{ proc utime*(loop: PLoop, req: PFS, path: cstring, atime: cdouble, mtime: cdouble, cb: FsProc): cint{. importc: "uv_fs_utime", header: "uv.h".} -proc futime*(loop: PLoop, req: PFS, file: TFile, atime: cdouble, mtime: cdouble, cb: FsProc): cint{. +proc futime*(loop: PLoop, req: PFS, file: File, atime: cdouble, mtime: cdouble, cb: FsProc): cint{. importc: "uv_fs_futime", header: "uv.h".} proc lstat*(loop: PLoop, req: PFS, path: cstring, cb: FsProc): cint{. @@ -660,25 +660,25 @@ proc symlink*(loop: PLoop, req: PFS, path: cstring, new_path: cstring, flags: ci proc readlink*(loop: PLoop, req: PFS, path: cstring, cb: FsProc): cint{. importc: "uv_fs_readlink", header: "uv.h".} -proc fchmod*(loop: PLoop, req: PFS, file: TFile, mode: cint, cb: FsProc): cint{. +proc fchmod*(loop: PLoop, req: PFS, file: File, mode: cint, cb: FsProc): cint{. importc: "uv_fs_fchmod", header: "uv.h".} proc chown*(loop: PLoop, req: PFS, path: cstring, uid: cint, gid: cint, cb: FsProc): cint{. importc: "uv_fs_chown", header: "uv.h".} -proc fchown*(loop: PLoop, req: PFS, file: TFile, uid: cint, gid: cint, cb: FsProc): cint{. +proc fchown*(loop: PLoop, req: PFS, file: File, uid: cint, gid: cint, cb: FsProc): cint{. importc: "uv_fs_fchown", header: "uv.h".} proc event_init*(loop: PLoop, handle: PFSEvent, filename: cstring, cb: FsEventProc): cint{. importc: "uv_fs_event_init", header: "uv.h".} -proc ip4_addr*(ip: cstring, port: cint): TSockAddrIn{. +proc ip4_addr*(ip: cstring, port: cint): SockAddrIn{. importc: "uv_ip4_addr", header: "uv.h".} proc ip6_addr*(ip: cstring, port: cint): TSockAddrIn6{. importc: "uv_ip6_addr", header: "uv.h".} -proc ip4_name*(src: ptr TSockAddrIn, dst: cstring, size: csize): cint{. +proc ip4_name*(src: ptr SockAddrIn, dst: cstring, size: csize): cint{. importc: "uv_ip4_name", header: "uv.h".} proc ip6_name*(src: ptr TSockAddrIn6, dst: cstring, size: csize): cint{. diff --git a/lib/wrappers/mysql.nim b/lib/wrappers/mysql.nim index 9161f56722..937a8952af 100644 --- a/lib/wrappers/mysql.nim +++ b/lib/wrappers/mysql.nim @@ -13,7 +13,7 @@ when defined(Unix): when defined(macosx): const - lib = "libmysqlclient.(15|16|17[18).dylib" + lib = "libmysqlclient.(15|16|17|18).dylib" else: const lib = "libmysqlclient.so.(15|16|17|18)" @@ -63,9 +63,9 @@ type const SCRAMBLE_LENGTH* = 20 # Length of random string sent by server on handshake; # this is also length of obfuscated password, - # recieved from client + # received from client SCRAMBLE_LENGTH_323* = 8 # length of password stored in the db: - # new passwords are preceeded with '*' + # new passwords are preceded with '*' SCRAMBLED_PASSWORD_CHAR_LENGTH* = SCRAMBLE_LENGTH * 2 + 1 SCRAMBLED_PASSWORD_CHAR_LENGTH_323* = SCRAMBLE_LENGTH_323 * 2 NOT_NULL_FLAG* = 1 # Field can't be NULL @@ -146,7 +146,7 @@ const MAX_MEDIUMINT_WIDTH* = 8 # Max width for a INT24 w.o. sign MAX_INT_WIDTH* = 10 # Max width for a LONG w.o. sign MAX_BIGINT_WIDTH* = 20 # Max width for a LONGLONG - MAX_CHAR_WIDTH* = 255 # Max length for a CHAR colum + MAX_CHAR_WIDTH* = 255 # Max length for a CHAR column MAX_BLOB_WIDTH* = 8192 # Default width for blob type @@ -558,7 +558,7 @@ type Tstatus* = enum STATUS_READY, STATUS_GET_RESULT, STATUS_USE_RESULT Tprotocol_type* = enum # There are three types of queries - the ones that have to go to - # the master, the ones that go to a slave, and the adminstrative + # the master, the ones that go to a slave, and the administrative # type which must happen on the pivot connectioin PROTOCOL_DEFAULT, PROTOCOL_TCP, PROTOCOL_SOCKET, PROTOCOL_PIPE, PROTOCOL_MEMORY @@ -790,7 +790,7 @@ proc server_init*(argc: cint, argv: cstringArray, groups: cstringArray): cint{. proc server_end*(){.cdecl, dynlib: lib, importc: "mysql_server_end".} # mysql_server_init/end need to be called when using libmysqld or # libmysqlclient (exactly, mysql_server_init() is called by mysql_init() so - # you don't need to call it explicitely; but you need to call + # you don't need to call it explicitly; but you need to call # mysql_server_end() to free memory). The names are a bit misleading # (mysql_SERVER* to be used when using libmysqlCLIENT). So we add more general # names which suit well whether you're using libmysqld or libmysqlclient. We diff --git a/lib/wrappers/readline/history.nim b/lib/wrappers/readline/history.nim index caa857ceb4..495bc15e42 100644 --- a/lib/wrappers/readline/history.nim +++ b/lib/wrappers/readline/history.nim @@ -231,7 +231,7 @@ proc history_truncate_file*(a2: cstring, a3: cint): cint{.cdecl, # -1) If there was an error in expansion. # 2) If the returned line should just be printed. # -# If an error ocurred in expansion, then OUTPUT contains a descriptive +# If an error occurred in expansion, then OUTPUT contains a descriptive # error message. proc history_expand*(a2: cstring, a3: cstringArray): cint{.cdecl, diff --git a/lib/wrappers/readline/tweaked/history.h b/lib/wrappers/readline/tweaked/history.h index 53bd642b11..b791237904 100644 --- a/lib/wrappers/readline/tweaked/history.h +++ b/lib/wrappers/readline/tweaked/history.h @@ -217,7 +217,7 @@ extern int history_truncate_file PARAMS((const char *, int)); -1) If there was an error in expansion. 2) If the returned line should just be printed. - If an error ocurred in expansion, then OUTPUT contains a descriptive + If an error occurred in expansion, then OUTPUT contains a descriptive error message. */ extern int history_expand PARAMS((char *, char **)); diff --git a/lib/wrappers/sdl/sdl.nim b/lib/wrappers/sdl/sdl.nim index 449b651f9f..5bb5b7ec20 100644 --- a/lib/wrappers/sdl/sdl.nim +++ b/lib/wrappers/sdl/sdl.nim @@ -89,7 +89,7 @@ # As most games will need it. # # April 02 2001 - DL : Added SDL_getenv.h definitions and tested version -# 1.2.0 compatability. +# 1.2.0 compatibility. # # March 13 2001 - MT : Added Linux compatibility. # @@ -118,7 +118,7 @@ # # November 30 2001 - DL : SDL_NOFRAME added as pointed out by Simon Rushton. # -# December 11 2001 - DL : Added $WEAKPACKAGEUNIT ON to facilitate useage in +# December 11 2001 - DL : Added $WEAKPACKAGEUNIT ON to facilitate usage in # Components # # January 05 2002 - DL : Added SDL_Swap32 function as suggested by Matthias @@ -209,7 +209,7 @@ # forgot to apply Michalis Kamburelis' patch to the implementation section. now fixed # # Revision 1.14 2004/12/23 23:42:18 savage -# Applied Patches supplied by Michalis Kamburelis ( THANKS! ), for greater FreePascal compatability. +# Applied Patches supplied by Michalis Kamburelis ( THANKS! ), for greater FreePascal compatibility. # # Revision 1.13 2004/09/30 22:31:59 savage # Updated with slightly different header comments @@ -221,7 +221,7 @@ # Updated so that Library name defines are correctly defined for MacOS X. # # Revision 1.10 2004/07/20 23:57:33 savage -# Thanks to Paul Toth for spotting an error in the SDL Audio Convertion structures. +# Thanks to Paul Toth for spotting an error in the SDL Audio Conversion structures. # In TSDL_AudioCVT the filters variable should point to and array of pointers and not what I had there previously. # # Revision 1.9 2004/07/03 22:07:22 savage @@ -243,7 +243,7 @@ # SDL_GetEnv Fix so that it is not define twice for FPC. Thanks to Rene Hugentobler for pointing out this bug, # # Revision 1.3 2004/02/18 22:35:51 savage -# Brought sdl.pas up to 1.2.7 compatability +# Brought sdl.pas up to 1.2.7 compatibility # Thus... # Added SDL_GL_STEREO, # SDL_GL_MULTISAMPLEBUFFERS, diff --git a/lib/wrappers/sdl/sdl_mixer.nim b/lib/wrappers/sdl/sdl_mixer.nim index 33a71508a2..2f86646359 100644 --- a/lib/wrappers/sdl/sdl_mixer.nim +++ b/lib/wrappers/sdl/sdl_mixer.nim @@ -136,7 +136,7 @@ # Windows unit not used in this file, so it was removed to keep the code tidy. # # Revision 1.3 2004/03/31 10:05:08 savage -# Better defines for Endianess under FreePascal and Borland compilers. +# Better defines for Endianness under FreePascal and Borland compilers. # # Revision 1.2 2004/03/30 20:23:28 savage # Tidied up use of UNIX compiler directive. diff --git a/lib/wrappers/zip/zlib.nim b/lib/wrappers/zip/zlib.nim index e3530d566d..8bdb47106d 100644 --- a/lib/wrappers/zip/zlib.nim +++ b/lib/wrappers/zip/zlib.nim @@ -232,7 +232,7 @@ proc uncompress*(sourceBuf: cstring, sourceLen: int): string = return # Make sure memory allocated by inflateInit2() is freed eventually. - finally: discard inflateEnd(z) + defer: discard inflateEnd(z) # Decompress all of self. while true: diff --git a/readme.md b/readme.md index d5968982b3..e716c0d0da 100644 --- a/readme.md +++ b/readme.md @@ -39,8 +39,7 @@ $ bin/nim c koch $ ./koch boot -d:release ``` -``koch install [dir]`` may then be used to install Nim, or you can simply -add it to your PATH. More ``koch`` related options are documented in +``koch install [dir]`` may then be used to install Nim, but lots of things don't work then so don't do that. Add it to your PATH instead. More ``koch`` related options are documented in [doc/koch.txt](doc/koch.txt). The above steps can be performed on Windows in a similar fashion, the @@ -62,12 +61,28 @@ allowing you to create commercial applications. Read copying.txt for more details. -Copyright (c) 2006-2014 Andreas Rumpf. +Copyright (c) 2006-2015 Andreas Rumpf. All rights reserved. # Build Status -| |Linux|Windows|Mac| -|---|---|---|---| -| x86 | ![](http://178.62.143.63:8010/buildstatusimage?builder=linux-x32-builder) | ![](http://178.62.143.63:8010/buildstatusimage?builder=windows-x32-builder) | ![](http://178.62.143.63:8010/buildstatusimage?builder=mac-x32-builder) -| x86_64 | ![](http://178.62.143.63:8010/buildstatusimage?builder=linux-x64-builder) | ![](http://178.62.143.63:8010/buildstatusimage?builder=windows-x64-builder) | ![](http://178.62.143.63:8010/buildstatusimage?builder=mac-x64-builder) -| arm | ![](http://178.62.143.63:8010/buildstatusimage?builder=linux-arm5-builder) | +[**Build Waterfall**][waterfall] + +| | Linux | Windows | Mac | +| ------ | ----- | ------- | --- | +| x86 | ![linux-x86][linux-x86-img] | ![windows-x86][windows-x86-img] | ![mac-x86][mac-x86-img] | +| x86_64 | ![linux-x86_64][linux-x86_64-img] | ![windows-x86_64][windows-x86_64-img] | ![mac-x86_64][mac-x86_64-img] | +| arm | ![linux-armv5][linux-arm5-img]
    ![linux-armv6][linux-arm6-img]
    ![linux-armv7][linux-arm7-img] | | | + +[linux-x86-img]: http://buildbot.nim-lang.org/buildstatusimage?builder=linux-x32-builder +[linux-x86_64-img]: http://buildbot.nim-lang.org/buildstatusimage?builder=linux-x64-builder +[linux-arm5-img]: http://buildbot.nim-lang.org/buildstatusimage?builder=linux-arm5-builder +[linux-arm6-img]: http://buildbot.nim-lang.org/buildstatusimage?builder=linux-arm6-builder +[linux-arm7-img]: http://buildbot.nim-lang.org/buildstatusimage?builder=linux-arm7-builder + +[windows-x86-img]: http://buildbot.nim-lang.org/buildstatusimage?builder=windows-x32-builder +[windows-x86_64-img]: http://buildbot.nim-lang.org/buildstatusimage?builder=windows-x64-builder + +[mac-x86-img]: http://buildbot.nim-lang.org/buildstatusimage?builder=mac-x32-builder +[mac-x86_64-img]: http://buildbot.nim-lang.org/buildstatusimage?builder=mac-x64-builder + +[waterfall]: http://buildbot.nim-lang.org/waterfall diff --git a/tests/actiontable/tactiontable2.nim b/tests/actiontable/tactiontable2.nim index 99bb3dca09..fbc65a67d7 100644 --- a/tests/actiontable/tactiontable2.nim +++ b/tests/actiontable/tactiontable2.nim @@ -1,6 +1,5 @@ discard """ - line: 21 - errormsg: "invalid type: 'Table[string, proc (string){.gcsafe.}]'" + output: "action 3 arg" """ import tables diff --git a/tests/assert/tfailedassert.nim b/tests/assert/tfailedassert.nim index d03d3136bd..16769f5298 100644 --- a/tests/assert/tfailedassert.nim +++ b/tests/assert/tfailedassert.nim @@ -10,7 +10,7 @@ tfailedassert.nim:27 false assertion from foo type TLineInfo = tuple[filename: string, line: int] - TMyError = object of E_Base + TMyError = object of Exception lineinfo: TLineInfo EMyError = ref TMyError diff --git a/tests/async/tasyncexceptions.nim b/tests/async/tasyncexceptions.nim index 30ef417564..c4379f7d87 100644 --- a/tests/async/tasyncexceptions.nim +++ b/tests/async/tasyncexceptions.nim @@ -19,7 +19,6 @@ proc processClient(fd: int) {.async.} = var foo = line[0] if foo == 'g': raise newException(EBase, "foobar") - proc serve() {.async.} = diff --git a/tests/async/tasynctry.nim b/tests/async/tasynctry.nim index 66ea40d498..99433b9d89 100644 --- a/tests/async/tasynctry.nim +++ b/tests/async/tasynctry.nim @@ -49,3 +49,44 @@ proc catch() {.async.} = assert false asyncCheck catch() + +proc test(): Future[bool] {.async.} = + result = false + try: + raise newException(OSError, "Foobar") + except: + result = true + return + +proc foo(): Future[bool] {.async.} = discard + +proc test2(): Future[bool] {.async.} = + result = false + try: + discard await foo() + raise newException(OSError, "Foobar") + except: + result = true + return + +proc test3(): Future[int] {.async.} = + result = 0 + try: + try: + discard await foo() + raise newException(OSError, "Hello") + except: + result = 1 + raise + except: + result = 2 + return + +var x = test() +assert x.read + +x = test2() +assert x.read + +var y = test3() +assert y.read == 2 diff --git a/tests/bind/tnicerrorforsymchoice.nim b/tests/bind/tnicerrorforsymchoice.nim index e1ff090dd8..bf6d92927f 100644 --- a/tests/bind/tnicerrorforsymchoice.nim +++ b/tests/bind/tnicerrorforsymchoice.nim @@ -1,6 +1,6 @@ discard """ line: 18 - errormsg: "type mismatch: got (proc (TScgi) | proc (AsyncSocket, StringTableRef, string){.gcsafe.})" + errormsg: "type mismatch: got (proc (TScgi) | proc (AsyncSocket, StringTableRef, string)" """ #bug #442 diff --git a/tests/caas/issue_416_template_shift.txt b/tests/caas/issue_416_template_shift.txt index b1f47c1ac6..e911c1360b 100644 --- a/tests/caas/issue_416_template_shift.txt +++ b/tests/caas/issue_416_template_shift.txt @@ -6,7 +6,7 @@ def\tskType\tsystem.string\tstring > idetools --track:$TESTNIM,12,35 --def $SILENT def\tskLet\t$MODULE.failtest.input\tTaintedString -# The following fail because they seem shifted one colum to the right. +# The following fail because they seem shifted one column to the right. > idetools --track:$TESTNIM,12,16 --def $SILENT def\tskTemplate\tsequtils.toSeq\tproc \(expr\): expr > idetools --track:$TESTNIM,12,22 --def $SILENT diff --git a/tests/caas/its_full_of_procs.nim b/tests/caas/its_full_of_procs.nim index 45347490c0..8f8b667647 100644 --- a/tests/caas/its_full_of_procs.nim +++ b/tests/caas/its_full_of_procs.nim @@ -2,7 +2,7 @@ import unicode, sequtils # This example shows that idetools returns proc as signature for everything # which can be called. While a clever person would use the second column to -# differentiate betwen procs, methods and others, why does the output contain +# differentiate between procs, methods and others, why does the output contain # incorrect information? type diff --git a/tests/casestmt/tcase_emptyset_when.nim b/tests/casestmt/tcase_emptyset_when.nim new file mode 100644 index 0000000000..e9b1ec2dff --- /dev/null +++ b/tests/casestmt/tcase_emptyset_when.nim @@ -0,0 +1,24 @@ +discard """ + file: "tcaseofwhen.nim" + outputsub: "compiles for 1\ni am always two\ndefault for 3\nset is 4 not 5\narray is 6 not 7\ndefault for 8" + exitcode: "0" +""" + +proc whenCase(a: int) = + case a + of (when compiles(whenCase(1)): 1 else: {}): echo "compiles for 1" + of {}: echo "me not fail" + of 2: echo "i am always two" + of []: echo "me neither" + of {4,5}: echo "set is 4 not 5" + of [6,7]: echo "array is 6 not 7" + of (when compiles(neverCompilesIBet()): 3 else: {}): echo "compiles for 3" + #of {},[]: echo "me neither" + else: echo "default for ", a + +whenCase(1) +whenCase(2) +whenCase(3) +whenCase(4) +whenCase(6) +whenCase(8) diff --git a/tests/ccgbugs/tmissingderef.nim b/tests/ccgbugs/tmissingderef.nim new file mode 100644 index 0000000000..edff1dd4e1 --- /dev/null +++ b/tests/ccgbugs/tmissingderef.nim @@ -0,0 +1,30 @@ +discard """ + output: '''255 +1 1 +0.5''' +""" + +# bug #1181 + +type + TFoo = object + x: int32 + +proc mainowar = + var foo: TFoo + foo.x = 0xff + var arr1 = cast[ptr array[4, uint8]](addr foo)[] # Fails. + echo arr1[when cpuEndian == littleEndian: 0 else: 3] + + var i = 1i32 + let x = addr i + var arr2 = cast[ptr array[4, uint8]](x)[] # Fails. + echo arr2[when cpuEndian == littleEndian: 0 else: 3], " ", i + + # bug #1715 + var a: array[2, float32] = [0.5'f32, 0.7] + let p = addr a + var b = p[] + echo b[0] + +mainowar() diff --git a/tests/ccgbugs/trecursive_closure.nim b/tests/ccgbugs/trecursive_closure.nim new file mode 100644 index 0000000000..50c363a4a4 --- /dev/null +++ b/tests/ccgbugs/trecursive_closure.nim @@ -0,0 +1,8 @@ +# bug #2233 +type MalType = object + fun: proc: MalType + +proc f(x: proc: MalType) = + discard x() + +f(nil) diff --git a/tests/clearmsg/ta.nim b/tests/clearmsg/ta.nim index b21522d127..38449c319e 100644 --- a/tests/clearmsg/ta.nim +++ b/tests/clearmsg/ta.nim @@ -1,5 +1,5 @@ discard """ - errormsg: 'type mismatch: got (mc.typ)' + errormsg: "type mismatch: got (mc.typ)" line: 12 """ diff --git a/tests/closure/tfib50.nim b/tests/closure/tfib50.nim new file mode 100644 index 0000000000..719aa3ad54 --- /dev/null +++ b/tests/closure/tfib50.nim @@ -0,0 +1,22 @@ +discard """ + output: "20365011074" +""" + +import tables + +proc memoize(f: proc (a: int64): int64): proc (a: int64): int64 = + var previous = initTable[int64, int64]() + return proc(i: int64): int64 = + if not previous.hasKey i: + previous[i] = f(i) + return previous[i] + +var fib: proc(a: int64): int64 + +fib = memoize(proc (i: int64): int64 = + if i == 0 or i == 1: + return 1 + return fib(i-1) + fib(i-2) +) + +echo fib(50) diff --git a/tests/closure/tissue1642.nim b/tests/closure/tissue1642.nim index d396630c86..e3028c88e5 100644 --- a/tests/closure/tissue1642.nim +++ b/tests/closure/tissue1642.nim @@ -1,5 +1,6 @@ discard """ file: "tissue1642.nim" + disabled: true """ block: var i = 0 diff --git a/tests/closure/ttimeinfo.nim b/tests/closure/ttimeinfo.nim new file mode 100644 index 0000000000..3138ae72e7 --- /dev/null +++ b/tests/closure/ttimeinfo.nim @@ -0,0 +1,15 @@ +# bug #2073 + +import sequtils +import times + +# 1 +proc f(n: int): TimeInfo = + TimeInfo(year: n, month: mJan, monthday: 1) + +echo toSeq(2000 || 2015).map(f) + +# 2 +echo toSeq(2000 || 2015).map(proc (n: int): TimeInfo = + TimeInfo(year: n, month: mJan, monthday: 1) +) diff --git a/tests/collections/tindexby.nim b/tests/collections/tindexby.nim new file mode 100644 index 0000000000..f374d5504c --- /dev/null +++ b/tests/collections/tindexby.nim @@ -0,0 +1,22 @@ +import tables + +doAssert indexBy(newSeq[int](), proc(x: int):int = x) == initTable[int, int](), "empty int table" + +var tbl1 = initTable[int, int]() +tbl1.add(1,1) +tbl1.add(2,2) +doAssert indexBy(@[1,2], proc(x: int):int = x) == tbl1, "int table" + +type + TElem = object + foo: int + bar: string + +let + elem1 = TElem(foo: 1, bar: "bar") + elem2 = TElem(foo: 2, bar: "baz") + +var tbl2 = initTable[string, TElem]() +tbl2.add("bar", elem1) +tbl2.add("baz", elem2) +doAssert indexBy(@[elem1,elem2], proc(x: TElem): string = x.bar) == tbl2, "element table" diff --git a/tests/table/ttableconstr.nim b/tests/collections/ttableconstr.nim similarity index 100% rename from tests/table/ttableconstr.nim rename to tests/collections/ttableconstr.nim diff --git a/tests/collections/ttables.nim b/tests/collections/ttables.nim index f374d5504c..3a923610ec 100644 --- a/tests/collections/ttables.nim +++ b/tests/collections/ttables.nim @@ -1,22 +1,152 @@ -import tables +discard """ + output: '''true''' +""" -doAssert indexBy(newSeq[int](), proc(x: int):int = x) == initTable[int, int](), "empty int table" +import hashes, tables -var tbl1 = initTable[int, int]() -tbl1.add(1,1) -tbl1.add(2,2) -doAssert indexBy(@[1,2], proc(x: int):int = x) == tbl1, "int table" +const + data = { + "34": 123456, "12": 789, + "90": 343, "0": 34404, + "1": 344004, "2": 344774, + "3": 342244, "4": 3412344, + "5": 341232144, "6": 34214544, + "7": 3434544, "8": 344544, + "9": 34435644, "---00": 346677844, + "10": 34484, "11": 34474, "19": 34464, + "20": 34454, "30": 34141244, "40": 344114, + "50": 344490, "60": 344491, "70": 344492, + "80": 344497} -type - TElem = object - foo: int - bar: string - -let - elem1 = TElem(foo: 1, bar: "bar") - elem2 = TElem(foo: 2, bar: "baz") + sorteddata = { + "---00": 346677844, + "0": 34404, + "1": 344004, + "10": 34484, + "11": 34474, + "12": 789, + "19": 34464, + "2": 344774, "20": 34454, + "3": 342244, "30": 34141244, + "34": 123456, + "4": 3412344, "40": 344114, + "5": 341232144, "50": 344490, + "6": 34214544, "60": 344491, + "7": 3434544, "70": 344492, + "8": 344544, "80": 344497, + "9": 34435644, + "90": 343} + +block tableTest1: + var t = initTable[tuple[x, y: int], string]() + t[(0,0)] = "00" + t[(1,0)] = "10" + t[(0,1)] = "01" + t[(1,1)] = "11" + for x in 0..1: + for y in 0..1: + assert t[(x,y)] == $x & $y + assert($t == + "{(x: 0, y: 1): 01, (x: 0, y: 0): 00, (x: 1, y: 0): 10, (x: 1, y: 1): 11}") + +block tableTest2: + var t = initTable[string, float]() + t["test"] = 1.2345 + t["111"] = 1.000043 + t["123"] = 1.23 + t.del("111") -var tbl2 = initTable[string, TElem]() -tbl2.add("bar", elem1) -tbl2.add("baz", elem2) -doAssert indexBy(@[elem1,elem2], proc(x: TElem): string = x.bar) == tbl2, "element table" + 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")) + + for key, val in items(data): t[key] = val.toFloat + for key, val in items(data): assert t[key] == val.toFloat + + assert(not t.hasKeyOrPut("456", 4.0)) # test absent key + assert t.hasKeyOrPut("012", 3.0) # test present key + var x = t.mgetOrPut("111", 1.5) # test absent key + x = x * 2 + assert x == 3.0 + x = t.mgetOrPut("test", 1.5) # test present key + x = x * 2 + assert x == 2 * 1.2345 + +block orderedTableTest1: + var t = initOrderedTable[string, int](2) + for key, val in items(data): t[key] = val + for key, val in items(data): assert t[key] == val + var i = 0 + # `pairs` needs to yield in insertion order: + for key, val in pairs(t): + assert key == data[i][0] + assert val == data[i][1] + inc(i) + + for key, val in mpairs(t): val = 99 + for val in mvalues(t): assert val == 99 + +block countTableTest1: + var s = data.toTable + var t = initCountTable[string]() + for k in s.keys: t.inc(k) + for k in t.keys: assert t[k] == 1 + t.inc("90", 3) + t.inc("12", 2) + t.inc("34", 1) + assert t.largest()[0] == "90" + + t.sort() + var i = 0 + for k, v in t.pairs: + case i + of 0: assert k == "90" and v == 4 + of 1: assert k == "12" and v == 3 + of 2: assert k == "34" and v == 2 + else: break + inc i + +block mpairsTableTest1: + var t = initTable[string, int]() + t["a"] = 1 + t["b"] = 2 + t["c"] = 3 + t["d"] = 4 + for k, v in t.mpairs: + if k == "a" or k == "c": + v = 9 + + for k, v in t.pairs: + if k == "a" or k == "c": + assert v == 9 + else: + assert v != 1 and v != 3 + +block SyntaxTest: + var x = toTable[int, string]({:}) + +proc orderedTableSortTest() = + var t = initOrderedTable[string, int](2) + for key, val in items(data): t[key] = val + for key, val in items(data): assert t[key] == val + t.sort(proc (x, y: tuple[key: string, val: int]): int = cmp(x.key, y.key)) + var i = 0 + # `pairs` needs to yield in sorted order: + for key, val in pairs(t): + doAssert key == sorteddata[i][0] + doAssert val == sorteddata[i][1] + inc(i) + + # check that lookup still works: + for key, val in pairs(t): + doAssert val == t[key] + # check that insert still works: + t["newKeyHere"] = 80 + + +orderedTableSortTest() +echo "true" + diff --git a/tests/table/ttables2.nim b/tests/collections/ttables2.nim similarity index 78% rename from tests/table/ttables2.nim rename to tests/collections/ttables2.nim index 611f3f8ecb..6f3fa841af 100644 --- a/tests/table/ttables2.nim +++ b/tests/collections/ttables2.nim @@ -16,5 +16,15 @@ proc run1() = # occupied Memory stays constant, but for i in 1 .. 50: # aborts at run: 44 on win32 with 3.2GB with out of memory TestHashIntInt() +# bug #2107 + +var delTab = initTable[int,int](4) + +for i in 1..4: + delTab[i] = i + delTab.del(i) +delTab[5] = 5 + + run1() echo "true" diff --git a/tests/table/ptables.nim b/tests/collections/ttablesref.nim similarity index 97% rename from tests/table/ptables.nim rename to tests/collections/ttablesref.nim index 79a9aab171..b57aedf4a8 100644 --- a/tests/table/ptables.nim +++ b/tests/collections/ttablesref.nim @@ -47,7 +47,7 @@ block tableTest1: for y in 0..1: assert t[(x,y)] == $x & $y assert($t == - "{(x: 0, y: 0): 00, (x: 0, y: 1): 01, (x: 1, y: 0): 10, (x: 1, y: 1): 11}") + "{(x: 0, y: 1): 01, (x: 0, y: 0): 00, (x: 1, y: 0): 10, (x: 1, y: 1): 11}") block tableTest2: var t = newTable[string, float]() @@ -84,7 +84,7 @@ block orderedTableTest1: block countTableTest1: var s = data.toTable var t = newCountTable[string]() - for k in s.Keys: t.inc(k) + for k in s.keys: t.inc(k) for k in t.keys: assert t[k] == 1 t.inc("90", 3) t.inc("12", 2) diff --git a/tests/table/ptables2.nim b/tests/collections/ttablesref2.nim similarity index 100% rename from tests/table/ptables2.nim rename to tests/collections/ttablesref2.nim diff --git a/tests/cpp/trawsockets.nim b/tests/cpp/trawsockets.nim new file mode 100644 index 0000000000..bc129de579 --- /dev/null +++ b/tests/cpp/trawsockets.nim @@ -0,0 +1,5 @@ +discard """ + cmd: "nim cpp $file" +""" + +import rawsockets diff --git a/tests/cpp/ttypeinfo.nim b/tests/cpp/ttypeinfo.nim new file mode 100644 index 0000000000..1529c86e94 --- /dev/null +++ b/tests/cpp/ttypeinfo.nim @@ -0,0 +1,5 @@ +discard """ + cmd: "nim cpp $file" +""" + +import typeinfo diff --git a/tests/deprecated/tdeprecated.nim b/tests/deprecated/tdeprecated.nim index f41f0a72fa..ed3d2733af 100644 --- a/tests/deprecated/tdeprecated.nim +++ b/tests/deprecated/tdeprecated.nim @@ -1,6 +1,5 @@ discard """ - line: 9 - errormsg: "'a' is deprecated [Deprecated]" + nimout: "'a' is deprecated [Deprecated]" """ var @@ -8,4 +7,3 @@ var a[8] = 1 - diff --git a/tests/destructor/tdestructor2.nim b/tests/destructor/tdestructor2.nim index 1bdf4993b9..6f966d861b 100644 --- a/tests/destructor/tdestructor2.nim +++ b/tests/destructor/tdestructor2.nim @@ -1,6 +1,6 @@ discard """ - line: 20 - errormsg: " usage of a type with a destructor in a non destructible context" + line: 23 + nimout: " usage of a type with a destructor in a non destructible context" """ {.experimental.} @@ -19,5 +19,9 @@ proc open: TMyObj = proc `$`(x: TMyObj): string = $x.y -echo open() +proc foo = + discard open() + +# XXX doesn't trigger this yet: +#echo open() diff --git a/tests/dll/client.nimrod.cfg b/tests/dll/client.nim.cfg similarity index 100% rename from tests/dll/client.nimrod.cfg rename to tests/dll/client.nim.cfg diff --git a/tests/dll/server.nimrod.cfg b/tests/dll/server.nim.cfg similarity index 100% rename from tests/dll/server.nimrod.cfg rename to tests/dll/server.nim.cfg diff --git a/tests/effects/teffects1.nim b/tests/effects/teffects1.nim index 27f89f5fa1..ea1ea7b211 100644 --- a/tests/effects/teffects1.nim +++ b/tests/effects/teffects1.nim @@ -1,5 +1,4 @@ discard """ - line: 2166 file: "system.nim" errormsg: "can raise an unlisted exception: ref IOError" """ diff --git a/tests/exception/tdefer1.nim b/tests/exception/tdefer1.nim new file mode 100644 index 0000000000..61439530a1 --- /dev/null +++ b/tests/exception/tdefer1.nim @@ -0,0 +1,18 @@ +discard """ + output: '''hi +hi''' +""" + +# bug #1742 + +template test(): expr = + let a = 0 + defer: echo "hi" + a + +let i = test() + +import strutils +let x = try: parseInt("133a") + except: -1 + finally: echo "hi" diff --git a/tests/exprs/thighCString.nim b/tests/exprs/thighCString.nim new file mode 100644 index 0000000000..543966df46 --- /dev/null +++ b/tests/exprs/thighCString.nim @@ -0,0 +1,6 @@ +discard """ + output: "5" +""" +let test = cstring("foobar") + +echo high(test) diff --git a/tests/fields/tfields_in_template.nim b/tests/fields/tfields_in_template.nim new file mode 100644 index 0000000000..9352a7a519 --- /dev/null +++ b/tests/fields/tfields_in_template.nim @@ -0,0 +1,15 @@ +discard """ + output: '''n +n''' +""" + +# bug #1902 +# This works. +for name, value in (n: "v").fieldPairs: + echo name + +# This doesn't compile - "expression 'name' has no type (or is ambiguous)". +template wrapper: stmt = + for name, value in (n: "v").fieldPairs: + echo name +wrapper() diff --git a/tests/fields/tfields_with_break.nim b/tests/fields/tfields_with_break.nim new file mode 100644 index 0000000000..1f26326921 --- /dev/null +++ b/tests/fields/tfields_with_break.nim @@ -0,0 +1,33 @@ +discard """ + output: '''(one: 1, two: 2, three: 3) +1 +2 +3 +(one: 4, two: 5, three: 6) +4 +(one: 7, two: 8, three: 9) +7 +8 +9''' +""" + +# bug #2134 +type + TestType = object + one: int + two: int + three: int + +var + ab = TestType(one:1, two:2, three:3) + ac = TestType(one:4, two:5, three:6) + ad = TestType(one:7, two:8, three:9) + tstSeq = [ab, ac, ad] + +for tstElement in mitems(tstSeq): + echo tstElement + for tstField in fields(tstElement): + #for tstField in [1,2,4,6]: + echo tstField + if tstField == 4: + break diff --git a/tests/gc/gcleak4.nim b/tests/gc/gcleak4.nim index 6f2b8a1fe6..54e74ac7b9 100644 --- a/tests/gc/gcleak4.nim +++ b/tests/gc/gcleak4.nim @@ -38,12 +38,14 @@ proc newPlus(a, b: ref TExpr): ref TPlusExpr = result.b = b result.op2 = $getOccupiedMem() +const Limit = when compileOption("gc", "markAndSweep"): 5*1024*1024 else: 500_000 + for i in 0..100_000: var s: array[0..11, ref TExpr] for j in 0..high(s): s[j] = newPlus(newPlus(newLit(j), newLit(2)), newLit(4)) if eval(s[j]) != j+6: quit "error: wrong result" - if getOccupiedMem() > 500_000: quit("still a leak!") + if getOccupiedMem() > Limit: quit("still a leak!") echo "no leak: ", getOccupiedMem() diff --git a/tests/gc/growobjcrash.nim b/tests/gc/growobjcrash.nim new file mode 100644 index 0000000000..a16468c7e8 --- /dev/null +++ b/tests/gc/growobjcrash.nim @@ -0,0 +1,29 @@ +discard """ + output: "works" +""" + +import cgi, strtabs + +proc handleRequest(query: string): StringTableRef = + iterator foo(): StringTableRef {.closure.} = + var params = {:}.newStringTable() + for key, val in cgi.decodeData(query): + params[key] = val + yield params + + let x = foo + result = x() + +const Limit = when compileOption("gc", "markAndSweep"): 5*1024*1024 else: 700_000 + +proc main = + var counter = 0 + for i in 0 .. 100_000: + for k, v in handleRequest("nick=Elina2&type=activate"): + inc counter + if counter mod 100 == 0: + if getOccupiedMem() > Limit: + quit "but now a leak" + +main() +echo "works" diff --git a/tests/generics/t1056.nim b/tests/generics/t1056.nim index 73a24a76aa..b1fe258943 100644 --- a/tests/generics/t1056.nim +++ b/tests/generics/t1056.nim @@ -1,6 +1,5 @@ discard """ output: '''TMatrix[3, 3, system.int] -3 3''' """ @@ -22,5 +21,5 @@ proc echoMat2(a: TMat2) = var m = TMatrix[3,3,int](data: [1,2,3,4,5,6,7,8,9]) echoMatrix m -echoMat2 m +#echoMat2 m diff --git a/tests/generics/twrong_field_caching.nim b/tests/generics/twrong_field_caching.nim new file mode 100644 index 0000000000..595c58eb72 --- /dev/null +++ b/tests/generics/twrong_field_caching.nim @@ -0,0 +1,68 @@ +discard """ + output: '''a23: 2x3 +a32: 3x2 +transpose A +t32: 3x2 +transpose B +x23: 2x3 (2x3) +x32: 3x2 (3x2)''' +""" + +# bug #2125 +# Suppose we have the following type for a rectangular array: + +type + RectArray*[R, C: static[int], T] = distinct array[R * C, T] + +var a23: RectArray[2, 3, int] +var a32: RectArray[3, 2, int] + +echo "a23: ", a23.R, "x", a23.C +echo "a32: ", a32.R, "x", a32.C + +# Output: +# a23: 2x3 +# a32: 3x2 + +# Looking good. Let's add a proc: +proc transpose*[R, C, T](m: RectArray[R, C, T]): RectArray[C, R, T] = + echo "transpose A" + +var t32 = a23.transpose + +echo "t32: ", t32.R, "x", t32.C + +# Output: +# t32: 3x2 + + +# Everything is still OK. Now let's use the rectangular array inside another +# generic type: +type + Matrix*[R, C: static[int], T] = object + theArray*: RectArray[R, C, T] + +#var m23: Matrix[2, 3, int] +#var m32: Matrix[3, 2, int] + +#echo "m23: ", m23.R, "x", m23.C, " (", m23.theArray.R, "x", m23.theArray.C, ")" +#echo "m32: ", m32.R, "x", m32.C, " (", m32.theArray.R, "x", m32.theArray.C, ")" + +# Output: +# m23: 2x3 (2x3) +# m32: 3x2 (3x2) + + +# Everything is still as expected. Now let's add the following proc: +proc transpose*[R, C, T](m: Matrix[R, C, T]): Matrix[C, R, T] = + echo "transpose B" + +var x23: Matrix[2, 3, int] +var x32 = x23.transpose + +echo "x23: ", x23.R, "x", x23.C, " (", x23.theArray.R, "x", x23.theArray.C, ")" +echo "x32: ", x32.R, "x", x32.C, " (", x32.theArray.R, "x", x32.theArray.C, ")" + +# Output: +# x23: 2x3 (2x3) +# x32: 3x2 (3x2) <--- this is incorrect. R and C do not match! diff --git a/tests/generics/twrong_floatlit_type.nim b/tests/generics/twrong_floatlit_type.nim new file mode 100644 index 0000000000..2db8b43535 --- /dev/null +++ b/tests/generics/twrong_floatlit_type.nim @@ -0,0 +1,118 @@ +discard """ + errormsg: "type mismatch" + line: 116 +""" + +# bug #2169 +import strutils, math + +type + Point2D*[S] = object + x*, y*: S + Matrix2x3*[S] = distinct array[6, S] ## Row major order + + Vector2D*[S] = object + x*, y*: S + +proc `[]`*[T](m: Matrix2x3[T], i: int): T = array[6, T](m)[i] + +template M11*[T](m: Matrix2x3[T]): T = m[0] +template M12*[T](m: Matrix2x3[T]): T = m[1] +template M13*[T](m: Matrix2x3[T]): T = m[2] +template M21*[T](m: Matrix2x3[T]): T = m[3] +template M22*[T](m: Matrix2x3[T]): T = m[4] +template M23*[T](m: Matrix2x3[T]): T = m[5] + +proc identity*[T](): Matrix2x3[T] = + Matrix2x3[T]([T(1.0), 0.0, 0.0, 0.0, 1.0, 0.0]) + +proc translation*[T](p: Point2D[T]): Matrix2x3[T] = + Matrix2x3[T]([T(1.0), T(0.0), p.x, T(0.0), T(1.0), p.y]) + +proc translation*[T](p: Vector2D[T]): Matrix2x3[T] = + Matrix2x3[T]([T(1.0), T(0.0), p.x, T(0.0), T(1.0), p.y]) + +proc scale*[T](v: Vector2D[T]): Matrix2x3[T] = + Matrix2x3[T]([v.x, T(0.0), T(0.0), T(0.0), v.y, T(0.0)]) + +proc rotation*[T](th: T): Matrix2x3[T] = + let + c = T(cos(th.float)) + s = T(sin(th.float)) + + Matrix2x3[T]([c, -s, T(0.0), s, c, T(0.0)]) + +proc `*`*[T](a, b: Matrix2x3[T]): Matrix2x3[T] = + # Here we pretend that row 3 is [0,0,0,1] without + # actually storing it in the matrix. + Matrix2x3[T]([a.M11*b.M11 + a.M12*b.M21, + a.M11*b.M12 + a.M12*b.M22, + a.M11*b.M13 + a.M12*b.M23 + a.M13, + + a.M21*b.M11 + a.M22*b.M21, + a.M21*b.M12 + a.M22*b.M22, + a.M21*b.M13 + a.M22*b.M23 + a.M23]) + +proc `*`*[T](a: Matrix2x3[T], p: Point2D[T]): Point2D[T] = + let + x = a.M11*p.x + a.M12*p.y + a.M13 + y = a.M21*p.x + a.M22*p.y + a.M23 + + Point2D[T](x: x, y: y) + +# making these so things like "line" that need a constructor don't stick out. +# 2x2 determinant: |a b| +# |c d| = ad - bc + +# String rendering +# +template ff[S](x: S): string = + formatFloat(float(x), ffDefault, 0) + +proc `$`*[S](p: Point2D[S]): string = + "P($1, $2)" % [ff(p.x), ff(p.y)] + +proc `$`*[S](p: Vector2D[S]): string = + "V($1, $2)" % [ff(p.x), ff(p.y)] + +proc `$`*[S](m: Matrix2x3[S]): string = + "M($1 $2 $3/$4 $5 $6)" % [ff(m.M11), ff(m.M12), ff(m.M13), + ff(m.M21), ff(m.M22), ff(m.M23)] + +# +# Vector operators. +proc `-`*[S](a: Vector2D[S]): Vector2D[S] = + Vector2D[S](x: -a.x, y: -a.y) + +proc `+`*[S](a, b: Vector2D[S]): Vector2D[S] = + Vector2D[S](x: a.x + b.x, y: a.y + b.y) + +proc `-`*[S](a, b: Vector2D[S]): Vector2D[S] = + Vector2D[S](x: a.x - b.x, y: a.y - b.y) + +proc `*`*[S](v: Vector2D[S], sc: S): Vector2D[S] = + Vector2D[S](x: v.x*sc, y: v.y*sc) + +proc `*`*[S](sc: S, v: Vector2D[S]): Vector2D[S] = + Vector2D[S](x: v.x*sc, y: v.y*sc) + +proc `/`*[S](v: Vector2D[S], sc: S): Vector2D[S] = + Vector2D[S](x: v.x/sc, y: v.y/sc) + +proc `/`*[S](sc: S; v: Vector2D[S]): Vector2D[S] = + Vector2D[S](x: sc/v.x, y: sc/v.y) + +proc `/`*[S](a, b: Vector2D[S]): Vector2D[S] = + Vector2D[S](x: a.x/b.x, y: a.y/b.y) +#proc vec[S](x, y: S): Vector2D[S] +proc vec[S](x, y: S): Vector2D[S] = + Vector2D[S](x: x, y: y) + +if isMainModule: + # Comment out this let, and the program will fail to + # compile with a type mismatch, as expected. + + let s3 = scale(vec(4.0, 4.0)) + let barf = translation(Point2D[float32](x: 1, y: 1)) * rotation(float(0.7)) + + echo "Badness ", barf diff --git a/tests/global/tglobal.nim b/tests/global/tglobal.nim index 84c4510c1f..d44a62afc5 100644 --- a/tests/global/tglobal.nim +++ b/tests/global/tglobal.nim @@ -1,6 +1,6 @@ discard """ - file: "toop1.nim" output: "in globalaux2: 10\ntotal globals: 2\nint value: 100\nstring value: second" + disabled: "true" """ import globalaux, globalaux2 diff --git a/tests/implicit/timplictderef.nim b/tests/implicit/timplictderef.nim index 99b0b645be..fcb647217c 100644 --- a/tests/implicit/timplictderef.nim +++ b/tests/implicit/timplictderef.nim @@ -1,9 +1,10 @@ discard """ - output: "2" + output: '''2 +88''' """ type - TValue* {.pure, final.} = object of TObject + TValue* {.pure, final.} = object of RootObj a: int PValue = ref TValue PPValue = ptr PValue @@ -16,3 +17,19 @@ var sp: PPValue = addr x sp.a = 2 if sp.a == 2: echo 2 # with sp[].a the error is gone +# Test the new auto-deref a little + +{.experimental.} + +proc p(x: var int; y: int) = x += y + +block: + var x: ref int + new(x) + + x.p(44) + + var indirect = p + x.indirect(44) + + echo x[] diff --git a/tests/iter/tconcat.nim b/tests/iter/tconcat.nim new file mode 100644 index 0000000000..477ac5e268 --- /dev/null +++ b/tests/iter/tconcat.nim @@ -0,0 +1,24 @@ +discard """ + output: '''1 +2 +3 +4 +20 +21 +22 +23''' +""" + +proc toIter*[T](s: Slice[T]): iterator: T = + iterator it: T {.closure.} = + for x in s.a..s.b: + yield x + return it + +iterator concat*[T](its: varargs[T, toIter]): auto = + for i in its: + for x in i(): + yield x + +for i in concat(1..4, 20..23): + echo i diff --git a/tests/macros/macro_bug.nim b/tests/macros/macro_bug.nim new file mode 100644 index 0000000000..0d0fa76ac8 --- /dev/null +++ b/tests/macros/macro_bug.nim @@ -0,0 +1,17 @@ +import macros + +macro macro_bug*(s: stmt): stmt {.immediate.} = + s.expectKind({nnkProcDef, nnkMethodDef}) + + var params = s.params + + let genericParams = s[2] + result = newNimNode(nnkProcDef).add( + s.name, s[1], genericParams, params, pragma(s), newEmptyNode()) + + var body = body(s) + + # Fails here. + var call = newCall("macro_bug", s.params[1][0]) + body.insert(0, call) + result.add(body) diff --git a/tests/macros/tmacrotypes.nim b/tests/macros/tmacrotypes.nim index f19aa2ddb8..9916689306 100644 --- a/tests/macros/tmacrotypes.nim +++ b/tests/macros/tmacrotypes.nim @@ -1,16 +1,16 @@ discard """ - disabled: true + nimout: '''void +int''' """ -import macros, typetraits +import macros -macro checkType(ex, expected: expr): stmt {.immediate.} = - var t = ex.typ - assert t.name == expected.strVal +macro checkType(ex: stmt; expected: expr): stmt = + var t = ex.getType() + echo t proc voidProc = echo "hello" -proc intProc(a, b): int = 10 +proc intProc(a: int, b: float): int = 10 checkType(voidProc(), "void") checkType(intProc(10, 20.0), "int") -checkType(noproc(10, 20.0), "Error Type") diff --git a/tests/macros/tnimrodnode_for_runtime.nim b/tests/macros/tnimnode_for_runtime.nim similarity index 88% rename from tests/macros/tnimrodnode_for_runtime.nim rename to tests/macros/tnimnode_for_runtime.nim index e73c8430f8..69c7aedd28 100644 --- a/tests/macros/tnimrodnode_for_runtime.nim +++ b/tests/macros/tnimnode_for_runtime.nim @@ -1,6 +1,5 @@ discard """ output: "bla" - disabled: true """ import macros diff --git a/tests/macros/tsame_name_497.nim b/tests/macros/tsame_name_497.nim new file mode 100644 index 0000000000..ed5d5c6d88 --- /dev/null +++ b/tests/macros/tsame_name_497.nim @@ -0,0 +1,9 @@ +discard """ + disabled: true +""" + +import macro_bug + +type TObj = object + +proc f(o: TObj) {.macro_bug.} = discard diff --git a/tests/macros/typesapi.nim b/tests/macros/typesapi.nim new file mode 100644 index 0000000000..670b39c9e7 --- /dev/null +++ b/tests/macros/typesapi.nim @@ -0,0 +1,17 @@ +discard """ + nimout: '''proc (x: int): string => typeDesc[proc[string, int]] +proc (x: int): void => typeDesc[proc[void, int]] +proc (x: int) => typeDesc[proc[void, int]]''' +""" + +#2211 + +import macros + +macro showType(t:stmt): stmt = + let ty = t.getType + echo t.repr, " => ", ty.repr + +showType(proc(x:int): string) +showType(proc(x:int): void) +showType(proc(x:int)) diff --git a/tests/manyloc/argument_parser/argument_parser.nim b/tests/manyloc/argument_parser/argument_parser.nim index af671cd852..a507a08e40 100644 --- a/tests/manyloc/argument_parser/argument_parser.nim +++ b/tests/manyloc/argument_parser/argument_parser.nim @@ -302,7 +302,7 @@ template build_specification_lookup(): ## Returns the table used to keep pointers to all of the specifications. var result {.gensym.}: OrderedTable[string, ptr Tparameter_specification] result = initOrderedTable[string, ptr Tparameter_specification]( - nextPowerOfTwo(expected.len)) + tables.rightSize(expected.len)) for i in 0..expected.len-1: for param_to_detect in expected[i].names: if result.hasKey(param_to_detect): diff --git a/tests/manyloc/keineschweine/dependencies/chipmunk/chipmunk.nim b/tests/manyloc/keineschweine/dependencies/chipmunk/chipmunk.nim index d079a2e72d..493a2106cd 100644 --- a/tests/manyloc/keineschweine/dependencies/chipmunk/chipmunk.nim +++ b/tests/manyloc/keineschweine/dependencies/chipmunk/chipmunk.nim @@ -74,7 +74,7 @@ type contacts*: PContact stamp*: TTimestamp handler*: PCollisionHandler - swappedColl*: bool32 + swappedColl*: Bool32 state*: TArbiterState PCollisionHandler* = ptr TCollisionHandler TCollisionHandler*{.pf.} = object @@ -108,7 +108,7 @@ type #/ Collision begin event function callback type. #/ Returning false from a begin callback causes the collision to be ignored until #/ the the separate callback is called when the objects stop colliding. - TCollisionBeginFunc* = proc (arb: PArbiter; space: PSpace; data: pointer): Bool{. + TCollisionBeginFunc* = proc (arb: PArbiter; space: PSpace; data: pointer): bool{. cdecl.} #/ Collision pre-solve event function callback type. #/ Returning false from a pre-step callback causes the collision to be ignored until the next step. @@ -142,14 +142,14 @@ type PSpatialIndex = ptr TSpatialIndex TSpatialIndex{.pf.} = object klass: PSpatialIndexClass - bbfunc: TSpatialIndexBBFunc + bbfun: TSpatialIndexBBFunc staticIndex: PSpatialIndex dynamicIndex: PSpatialIndex TSpatialIndexDestroyImpl* = proc (index: PSpatialIndex){.cdecl.} TSpatialIndexCountImpl* = proc (index: PSpatialIndex): cint{.cdecl.} TSpatialIndexEachImpl* = proc (index: PSpatialIndex; - func: TSpatialIndexIteratorFunc; data: pointer){. + fun: TSpatialIndexIteratorFunc; data: pointer){. cdecl.} TSpatialIndexContainsImpl* = proc (index: PSpatialIndex; obj: pointer; hashid: THashValue): Bool32 {.cdecl.} @@ -161,15 +161,15 @@ type TSpatialIndexReindexObjectImpl* = proc (index: PSpatialIndex; obj: pointer; hashid: THashValue){.cdecl.} TSpatialIndexReindexQueryImpl* = proc (index: PSpatialIndex; - func: TSpatialIndexQueryFunc; data: pointer){.cdecl.} + fun: TSpatialIndexQueryFunc; data: pointer){.cdecl.} TSpatialIndexPointQueryImpl* = proc (index: PSpatialIndex; point: TVector; - func: TSpatialIndexQueryFunc; + fun: TSpatialIndexQueryFunc; data: pointer){.cdecl.} TSpatialIndexSegmentQueryImpl* = proc (index: PSpatialIndex; obj: pointer; - a: TVector; b: TVector; t_exit: CpFloat; func: TSpatialIndexSegmentQueryFunc; + a: TVector; b: TVector; t_exit: CpFloat; fun: TSpatialIndexSegmentQueryFunc; data: pointer){.cdecl.} TSpatialIndexQueryImpl* = proc (index: PSpatialIndex; obj: pointer; - bb: TBB; func: TSpatialIndexQueryFunc; + bb: TBB; fun: TSpatialIndexQueryFunc; data: pointer){.cdecl.} PSpatialIndexClass* = ptr TSpatialIndexClass TSpatialIndexClass*{.pf.} = object @@ -279,14 +279,14 @@ type PSegmentQueryInfo* = ptr TSegmentQueryInfo #/ Segment query info struct. TSegmentQueryInfo*{.pf.} = object - shape*: PShape #/ The shape that was hit, NULL if no collision occured. + shape*: PShape #/ The shape that was hit, NULL if no collision occurred. t*: CpFloat #/ The normalized distance along the query segment in the range [0, 1]. n*: TVector #/ The normal of the surface hit. TShapeType*{.size: sizeof(cint).} = enum CP_CIRCLE_SHAPE, CP_SEGMENT_SHAPE, CP_POLY_SHAPE, CP_NUM_SHAPES TShapeCacheDataImpl* = proc (shape: PShape; p: TVector; rot: TVector): TBB{.cdecl.} TShapeDestroyImpl* = proc (shape: PShape){.cdecl.} - TShapePointQueryImpl* = proc (shape: PShape; p: TVector): bool32 {.cdecl.} + TShapePointQueryImpl* = proc (shape: PShape; p: TVector): Bool32 {.cdecl.} TShapeSegmentQueryImpl* = proc (shape: PShape; a: TVector; b: TVector; info: PSegmentQueryInfo){.cdecl.} PShapeClass* = ptr TShapeClass @@ -427,7 +427,7 @@ defGetter(PSpace, CpFloat, currDt, CurrentTimeStep) #/ returns true from inside a callback and objects cannot be added/removed. -proc isLocked*(space: PSpace): Bool{.inline.} = +proc isLocked*(space: PSpace): bool{.inline.} = result = space.locked.bool #/ Set a default collision handler for this space. @@ -478,24 +478,24 @@ proc removeBody*(space: PSpace; body: PBody){. proc RemoveConstraint*(space: PSpace; constraint: PConstraint){. cdecl, importc: "cpSpaceRemoveConstraint", dynlib: Lib.} #/ Test if a collision shape has been added to the space. -proc containsShape*(space: PSpace; shape: PShape): Bool{. +proc containsShape*(space: PSpace; shape: PShape): bool{. cdecl, importc: "cpSpaceContainsShape", dynlib: Lib.} #/ Test if a rigid body has been added to the space. -proc containsBody*(space: PSpace; body: PBody): Bool{. +proc containsBody*(space: PSpace; body: PBody): bool{. cdecl, importc: "cpSpaceContainsBody", dynlib: Lib.} #/ Test if a constraint has been added to the space. -proc containsConstraint*(space: PSpace; constraint: PConstraint): Bool{. +proc containsConstraint*(space: PSpace; constraint: PConstraint): bool{. cdecl, importc: "cpSpaceContainsConstraint", dynlib: Lib.} #/ Schedule a post-step callback to be called when cpSpaceStep() finishes. #/ @c obj is used a key, you can only register one callback per unique value for @c obj -proc addPostStepCallback*(space: PSpace; func: TPostStepFunc; +proc addPostStepCallback*(space: PSpace; fun: TPostStepFunc; obj: pointer; data: pointer){. cdecl, importc: "cpSpaceAddPostStepCallback", dynlib: Lib.} #/ Query the space at a point and call @c func for each shape found. proc pointQuery*(space: PSpace; point: TVector; layers: TLayers; - group: TGroup; func: TSpacePointQueryFunc; data: pointer){. + group: TGroup; fun: TSpacePointQueryFunc; data: pointer){. cdecl, importc: "cpSpacePointQuery", dynlib: Lib.} #/ Query the space at a point and return the first shape found. Returns NULL if no shapes were found. @@ -506,7 +506,7 @@ proc pointQueryFirst*(space: PSpace; point: TVector; layers: TLayers; #/ Perform a directed line segment query (like a raycast) against the space calling @c func for each shape intersected. proc segmentQuery*(space: PSpace; start: TVector; to: TVector; layers: TLayers; group: TGroup; - func: TSpaceSegmentQueryFunc; data: pointer){. + fun: TSpaceSegmentQueryFunc; data: pointer){. cdecl, importc: "cpSpaceSegmentQuery", dynlib: Lib.} #/ Perform a directed line segment query (like a raycast) against the space and return the first shape hit. Returns NULL if no shapes were hit. proc segmentQueryFirst*(space: PSpace; start: TVector; to: TVector; @@ -517,26 +517,26 @@ proc segmentQueryFirst*(space: PSpace; start: TVector; to: TVector; #/ Perform a fast rectangle query on the space calling @c func for each shape found. #/ Only the shape's bounding boxes are checked for overlap, not their full shape. proc BBQuery*(space: PSpace; bb: TBB; layers: TLayers; group: TGroup; - func: TSpaceBBQueryFunc; data: pointer){. + fun: TSpaceBBQueryFunc; data: pointer){. cdecl, importc: "cpSpaceBBQuery", dynlib: Lib.} #/ Query a space for any shapes overlapping the given shape and call @c func for each shape found. -proc shapeQuery*(space: PSpace; shape: PShape; func: TSpaceShapeQueryFunc; data: pointer): Bool {. +proc shapeQuery*(space: PSpace; shape: PShape; fun: TSpaceShapeQueryFunc; data: pointer): bool {. cdecl, importc: "cpSpaceShapeQuery", dynlib: Lib.} #/ Call cpBodyActivate() for any shape that is overlaps the given shape. proc activateShapesTouchingShape*(space: PSpace; shape: PShape){. cdecl, importc: "cpSpaceActivateShapesTouchingShape", dynlib: Lib.} #/ Call @c func for each body in the space. -proc eachBody*(space: PSpace; func: TSpaceBodyIteratorFunc; data: pointer){. +proc eachBody*(space: PSpace; fun: TSpaceBodyIteratorFunc; data: pointer){. cdecl, importc: "cpSpaceEachBody", dynlib: Lib.} #/ Call @c func for each shape in the space. -proc eachShape*(space: PSpace; func: TSpaceShapeIteratorFunc; +proc eachShape*(space: PSpace; fun: TSpaceShapeIteratorFunc; data: pointer){. cdecl, importc: "cpSpaceEachShape", dynlib: Lib.} #/ Call @c func for each shape in the space. -proc eachConstraint*(space: PSpace; func: TSpaceConstraintIteratorFunc; +proc eachConstraint*(space: PSpace; fun: TSpaceConstraintIteratorFunc; data: pointer){. cdecl, importc: "cpSpaceEachConstraint", dynlib: Lib.} #/ Update the collision detection info for the static shapes in the space. @@ -674,7 +674,7 @@ proc dist*(v1, v2: TVector): CpFloat {.inline.} = proc distsq*(v1, v2: TVector): CpFloat {.inline.} = result = (v1 - v2).lenSq #vlengthsq(vsub(v1, v2)) #/ Returns true if the distance between v1 and v2 is less than dist. -proc near*(v1, v2: TVector; dist: CpFloat): Bool{.inline.} = +proc near*(v1, v2: TVector; dist: CpFloat): bool{.inline.} = result = v1.distSq(v2) < dist * dist @@ -706,13 +706,13 @@ proc Sleep*(body: PBody){.importc: "cpBodySleep", dynlib: Lib.} proc SleepWithGroup*(body: PBody; group: PBody){. importc: "cpBodySleepWithGroup", dynlib: Lib.} #/ Returns true if the body is sleeping. -proc isSleeping*(body: PBody): Bool {.inline.} = +proc isSleeping*(body: PBody): bool {.inline.} = return body.node.root != nil #/ Returns true if the body is static. proc isStatic*(body: PBody): bool {.inline.} = return body.node.idleTime == CpInfinity #/ Returns true if the body has not been added to a space. -proc isRogue*(body: PBody): Bool {.inline.} = +proc isRogue*(body: PBody): bool {.inline.} = return body.space == nil # #define CP_DefineBodyStructGetter(type, member, name) \ @@ -808,15 +808,15 @@ proc kineticEnergy*(body: PBOdy): CpFloat = result = (body.v.dot(body.v) * body.m) + (body.w * body.w * body.i) #/ Call @c func once for each shape attached to @c body and added to the space. -proc eachShape*(body: PBody; func: TBodyShapeIteratorFunc; +proc eachShape*(body: PBody; fun: TBodyShapeIteratorFunc; data: pointer){. cdecl, importc: "cpBodyEachShape", dynlib: Lib.} #/ Call @c func once for each constraint attached to @c body and added to the space. -proc eachConstraint*(body: PBody; func: TBodyConstraintIteratorFunc; +proc eachConstraint*(body: PBody; fun: TBodyConstraintIteratorFunc; data: pointer) {. cdecl, importc: "cpBodyEachConstraint", dynlib: Lib.} #/ Call @c func once for each arbiter that is currently active on the body. -proc eachArbiter*(body: PBody; func: TBodyArbiterIteratorFunc; +proc eachArbiter*(body: PBody; fun: TBodyArbiterIteratorFunc; data: pointer){. cdecl, importc: "cpBodyEachArbiter", dynlib: Lib.} #/ Allocate a spatial hash. @@ -824,10 +824,10 @@ proc SpaceHashAlloc*(): PSpaceHash{. cdecl, importc: "cpSpaceHashAlloc", dynlib: Lib.} #/ Initialize a spatial hash. proc SpaceHashInit*(hash: PSpaceHash; celldim: CpFloat; numcells: cint; - bbfunc: TSpatialIndexBBFunc; staticIndex: PSpatialIndex): PSpatialIndex{. + bbfun: TSpatialIndexBBFunc; staticIndex: PSpatialIndex): PSpatialIndex{. cdecl, importc: "cpSpaceHashInit", dynlib: Lib.} #/ Allocate and initialize a spatial hash. -proc SpaceHashNew*(celldim: CpFloat; cells: cint; bbfunc: TSpatialIndexBBFunc; +proc SpaceHashNew*(celldim: CpFloat; cells: cint; bbfun: TSpatialIndexBBFunc; staticIndex: PSpatialIndex): PSpatialIndex{. cdecl, importc: "cpSpaceHashNew", dynlib: Lib.} #/ Change the cell dimensions and table size of the spatial hash to tune it. @@ -842,18 +842,18 @@ proc SpaceHashResize*(hash: PSpaceHash; celldim: CpFloat; numcells: cint){. #/ Allocate a bounding box tree. proc BBTreeAlloc*(): PBBTree{.cdecl, importc: "cpBBTreeAlloc", dynlib: Lib.} #/ Initialize a bounding box tree. -proc BBTreeInit*(tree: PBBTree; bbfunc: TSpatialIndexBBFunc; +proc BBTreeInit*(tree: PBBTree; bbfun: TSpatialIndexBBFunc; staticIndex: ptr TSpatialIndex): ptr TSpatialIndex{.cdecl, importc: "cpBBTreeInit", dynlib: Lib.} #/ Allocate and initialize a bounding box tree. -proc BBTreeNew*(bbfunc: TSpatialIndexBBFunc; staticIndex: PSpatialIndex): PSpatialIndex{. +proc BBTreeNew*(bbfun: TSpatialIndexBBFunc; staticIndex: PSpatialIndex): PSpatialIndex{. cdecl, importc: "cpBBTreeNew", dynlib: Lib.} #/ Perform a static top down optimization of the tree. proc BBTreeOptimize*(index: PSpatialIndex){. cdecl, importc: "cpBBTreeOptimize", dynlib: Lib.} #/ Set the velocity function for the bounding box tree to enable temporal coherence. -proc BBTreeSetVelocityFunc*(index: PSpatialIndex; func: TBBTreeVelocityFunc){. +proc BBTreeSetVelocityFunc*(index: PSpatialIndex; fun: TBBTreeVelocityFunc){. cdecl, importc: "cpBBTreeSetVelocityFunc", dynlib: Lib.} #MARK: Single Axis Sweep @@ -864,12 +864,12 @@ proc Sweep1DAlloc*(): ptr TSweep1D{.cdecl, importc: "cpSweep1DAlloc", dynlib: Lib.} #/ Initialize a 1D sort and sweep broadphase. -proc Sweep1DInit*(sweep: ptr TSweep1D; bbfunc: TSpatialIndexBBFunc; +proc Sweep1DInit*(sweep: ptr TSweep1D; bbfun: TSpatialIndexBBFunc; staticIndex: ptr TSpatialIndex): ptr TSpatialIndex{.cdecl, importc: "cpSweep1DInit", dynlib: Lib.} #/ Allocate and initialize a 1D sort and sweep broadphase. -proc Sweep1DNew*(bbfunc: TSpatialIndexBBFunc; staticIndex: ptr TSpatialIndex): ptr TSpatialIndex{. +proc Sweep1DNew*(bbfun: TSpatialIndexBBFunc; staticIndex: ptr TSpatialIndex): ptr TSpatialIndex{. cdecl, importc: "cpSweep1DNew", dynlib: Lib.} @@ -1359,7 +1359,7 @@ defCProp(SlideJoint, TVector, anchr2, Anchr2) defCProp(SlideJoint, CpFloat, min, Min) defCProp(SlideJoint, CpFloat, max, Max) -proc pivotJointGetClass*(): PConstraintClass {. +proc PivotJointGetClass*(): PConstraintClass {. cdecl, importc: "cpPivotJointGetClass", dynlib: Lib.} #/ Allocate a pivot joint diff --git a/tests/manyloc/keineschweine/dependencies/enet/enet.nim b/tests/manyloc/keineschweine/dependencies/enet/enet.nim index df1b743ee0..93857207a2 100644 --- a/tests/manyloc/keineschweine/dependencies/enet/enet.nim +++ b/tests/manyloc/keineschweine/dependencies/enet/enet.nim @@ -20,7 +20,7 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. const Lib = "libenet.so.1(|.0.3)" -{.deadCodeElim: ON.} +{.deadCodeElim: on.} const ENET_VERSION_MAJOR* = 1 ENET_VERSION_MINOR* = 3 diff --git a/tests/manyloc/keineschweine/dependencies/genpacket/genpacket_enet.nim b/tests/manyloc/keineschweine/dependencies/genpacket/genpacket_enet.nim index 44d00db53d..4f2fb1ea3c 100644 --- a/tests/manyloc/keineschweine/dependencies/genpacket/genpacket_enet.nim +++ b/tests/manyloc/keineschweine/dependencies/genpacket/genpacket_enet.nim @@ -9,15 +9,6 @@ template defPacketImports*(): stmt {.immediate, dirty.} = import macros, macro_dsl, estreams from strutils import format -proc `$`*[T](x: seq[T]): string = - result = "[seq len=" - result.add($x.len) - result.add ':' - for i in 0.. = 60.0: - echo("PING -> redis") - assert(database.r.ping() == "PONG") - database.lastPing = t - -proc getCommits*(database: TDb, - plStr: var seq[string]): seq[TEntry] = - result = @[] - var commitsRaw = database.r.lrange("commits", 0, -1) - for c in items(commitsRaw): - var commit: TCommit - commit.hash = c - for key, value in database.r.hPairs(c): - case normalize(key) - of "commitmsg": commit.commitMsg = value - of "date": commit.date = TTime(parseInt(value)) - of "username": commit.username = value - else: - echo(key) - assert(false) - - var platformsRaw = database.r.lrange(c & ":platforms", 0, -1) - var platforms: seq[TPlatform] = @[] - for p in items(platformsRaw): - var platform: TPlatform - for key, value in database.r.hPairs(p & ":" & c): - case normalize(key) - of "buildresult": - platform.buildResult = parseInt(value).TBuildResult - of "testresult": - platform.testResult = parseInt(value).TTestResult - of "failreason": - platform.failReason = value - of "total": - platform.total = parseBiggestInt(value) - of "passed": - platform.passed = parseBiggestInt(value) - of "skipped": - platform.skipped = parseBiggestInt(value) - of "failed": - platform.failed = parseBiggestInt(value) - of "csources": - platform.csources = if value == "t": true else: false - else: - echo(normalize(key)) - assert(false) - - platform.platform = p - - platforms.add(platform) - if p notin plStr: - plStr.add(p) - result.add((commit, platforms)) - -proc commitExists*(database: TDb, commit: string, starts = false): bool = - # TODO: Consider making the 'commits' list a set. - for c in items(database.r.lrange("commits", 0, -1)): - if starts: - if c.startsWith(commit): return true - else: - if c == commit: return true - return false - -proc platformExists*(database: TDb, commit: string, platform: string): bool = - for p in items(database.r.lrange(commit & ":" & "platforms", 0, -1)): - if p == platform: return true - -proc expandHash*(database: TDb, commit: string): string = - for c in items(database.r.lrange("commits", 0, -1)): - if c.startsWith(commit): return c - assert false - -proc isNewest*(database: TDb, commit: string): bool = - return database.r.lIndex("commits", 0) == commit - -proc getNewest*(database: TDb): string = - return database.r.lIndex("commits", 0) - -proc addPlatform*(database: TDb, commit: string, platform: string) = - assert database.commitExists(commit) - assert (not database.platformExists(commit, platform)) - var name = platform & ":" & commit - if database.r.exists(name): - if failOnExisting: quit("[FAIL] " & name & " already exists!", 1) - else: echo("[Warning] " & name & " already exists!") - - discard database.r.lPush(commit & ":" & "platforms", platform) - -proc `[]`*(p: seq[TPlatform], name: string): TPlatform = - for platform in items(p): - if platform.platform == name: - return platform - raise newException(EInvalidValue, name & " platforms not found in commits.") - -proc contains*(p: seq[TPlatform], s: string): bool = - for i in items(p): - if i.platform == s: - return True - - -type - PState = ref TState - TState = object of TObject - dispatcher: PDispatcher - sock: PAsyncSocket - ircClient: PAsyncIRC - hubPort: TPort - database: TDb - dbConnected: bool - - TSeenType = enum - PSeenJoin, PSeenPart, PSeenMsg, PSeenNick, PSeenQuit - - TSeen = object - nick: string - channel: string - timestamp: TTime - case kind*: TSeenType - of PSeenJoin: nil - of PSeenPart, PSeenQuit, PSeenMsg: - msg: string - of PSeenNick: - newNick: string - -const - ircServer = "irc.freenode.net" - joinChans = @["#nim"] - botNickname = "NimBot" - -proc setSeen(d: TDb, s: TSeen) = - discard d.r.del("seen:" & s.nick) - - var hashToSet = @[("type", $s.kind.int), ("channel", s.channel), - ("timestamp", $s.timestamp.int)] - case s.kind - of PSeenJoin: discard - of PSeenPart, PSeenMsg, PSeenQuit: - hashToSet.add(("msg", s.msg)) - of PSeenNick: - hashToSet.add(("newnick", s.newNick)) - - d.r.hMSet("seen:" & s.nick, hashToSet) - -proc getSeen(d: TDb, nick: string, s: var TSeen): bool = - if d.r.exists("seen:" & nick): - result = true - s.nick = nick - # Get the type first - s.kind = d.r.hGet("seen:" & nick, "type").parseInt.TSeenType - - for key, value in d.r.hPairs("seen:" & nick): - case normalize(key) - of "type": - #s.kind = value.parseInt.TSeenType - of "channel": - s.channel = value - of "timestamp": - s.timestamp = TTime(value.parseInt) - of "msg": - s.msg = value - of "newnick": - s.newNick = value - -template createSeen(typ: TSeenType, n, c: string): stmt {.immediate, dirty.} = - var seenNick: TSeen - seenNick.kind = typ - seenNick.nick = n - seenNick.channel = c - seenNick.timestamp = getTime() - -proc parseReply(line: string, expect: string): Bool = - var jsonDoc = parseJson(line) - return jsonDoc["reply"].str == expect - -proc limitCommitMsg(m: string): string = - ## Limits the message to 300 chars and adds ellipsis. - var m1 = m - if NewLines in m1: - m1 = m1.splitLines()[0] - - if m1.len >= 300: - m1 = m1[0..300] - - if m1.len >= 300 or NewLines in m: m1.add("... ") - - if NewLines in m: m1.add($m.splitLines().len & " more lines") - - return m1 - -proc handleWebMessage(state: PState, line: string) = - echo("Got message from hub: " & line) - var json = parseJson(line) - if json.hasKey("payload"): - for i in 0..min(4, json["payload"]["commits"].len-1): - var commit = json["payload"]["commits"][i] - # Create the message - var message = "" - message.add(json["payload"]["repository"]["owner"]["name"].str & "/" & - json["payload"]["repository"]["name"].str & " ") - message.add(commit["id"].str[0..6] & " ") - message.add(commit["author"]["name"].str & " ") - message.add("[+" & $commit["added"].len & " ") - message.add("±" & $commit["modified"].len & " ") - message.add("-" & $commit["removed"].len & "]: ") - message.add(limitCommitMsg(commit["message"].str)) - - # Send message to #nim. - state.ircClient.privmsg(joinChans[0], message) - elif json.hasKey("redisinfo"): - assert json["redisinfo"].hasKey("port") - #let redisPort = json["redisinfo"]["port"].num - state.dbConnected = true - -proc hubConnect(state: PState) -proc handleConnect(s: PAsyncSocket, state: PState) = - try: - # Send greeting - var obj = newJObject() - obj["name"] = newJString("irc") - obj["platform"] = newJString("?") - state.sock.send($obj & "\c\L") - - # Wait for reply. - var line = "" - sleep(1500) - if state.sock.recvLine(line): - assert(line != "") - doAssert parseReply(line, "OK") - echo("The hub accepted me!") - else: - raise newException(EInvalidValue, - "Hub didn't accept me. Waited 1.5 seconds.") - - # ask for the redis info - var riobj = newJObject() - riobj["do"] = newJString("redisinfo") - state.sock.send($riobj & "\c\L") - - except EOS: - echo(getCurrentExceptionMsg()) - s.close() - echo("Waiting 5 seconds...") - sleep(5000) - state.hubConnect() - -proc handleRead(s: PAsyncSocket, state: PState) = - var line = "" - if state.sock.recvLine(line): - if line != "": - # Handle the message - state.handleWebMessage(line) - else: - echo("Disconnected from hub: ", OSErrorMsg()) - s.close() - echo("Reconnecting...") - state.hubConnect() - else: - echo(OSErrorMsg()) - -proc hubConnect(state: PState) = - state.sock = AsyncSocket() - state.sock.connect("127.0.0.1", state.hubPort) - state.sock.handleConnect = - proc (s: PAsyncSocket) = - handleConnect(s, state) - state.sock.handleRead = - proc (s: PAsyncSocket) = - handleRead(s, state) - - state.dispatcher.register(state.sock) - -proc handleIrc(irc: PAsyncIRC, event: TIRCEvent, state: PState) = - case event.typ - of EvConnected: discard - of EvDisconnected: - while not state.ircClient.isConnected: - try: - state.ircClient.connect() - except: - echo("Error reconnecting: ", getCurrentExceptionMsg()) - - echo("Waiting 5 seconds...") - sleep(5000) - echo("Reconnected successfully!") - of EvMsg: - echo("< ", event.raw) - case event.cmd - of MPrivMsg: - let msg = event.params[event.params.len-1] - let words = msg.split(' ') - template pm(msg: string): stmt = - state.ircClient.privmsg(event.origin, msg) - case words[0] - of "!ping": pm("pong") - of "!lag": - if state.ircClient.getLag != -1.0: - var lag = state.ircClient.getLag - lag = lag * 1000.0 - pm($int(lag) & "ms between me and the server.") - else: - pm("Unknown.") - of "!seen": - if words.len > 1: - let nick = words[1] - if nick == botNickname: - pm("Yes, I see myself.") - echo(nick) - var seenInfo: TSeen - if state.database.getSeen(nick, seenInfo): - #var mSend = "" - case seenInfo.kind - of PSeenMsg: - pm("$1 was last seen on $2 in $3 saying: $4" % - [seenInfo.nick, $seenInfo.timestamp, - seenInfo.channel, seenInfo.msg]) - of PSeenJoin: - pm("$1 was last seen on $2 joining $3" % - [seenInfo.nick, $seenInfo.timestamp, seenInfo.channel]) - of PSeenPart: - pm("$1 was last seen on $2 leaving $3 with message: $4" % - [seenInfo.nick, $seenInfo.timestamp, seenInfo.channel, - seenInfo.msg]) - of PSeenQuit: - pm("$1 was last seen on $2 quitting with message: $3" % - [seenInfo.nick, $seenInfo.timestamp, seenInfo.msg]) - of PSeenNick: - pm("$1 was last seen on $2 changing nick to $3" % - [seenInfo.nick, $seenInfo.timestamp, seenInfo.newNick]) - - else: - pm("I have not seen " & nick) - else: - pm("Syntax: !seen ") - - # TODO: ... commands - - # -- Seen - # Log this as activity. - createSeen(PSeenMsg, event.nick, event.origin) - seenNick.msg = msg - state.database.setSeen(seenNick) - of MJoin: - createSeen(PSeenJoin, event.nick, event.origin) - state.database.setSeen(seenNick) - of MPart: - createSeen(PSeenPart, event.nick, event.origin) - let msg = event.params[event.params.high] - seenNick.msg = msg - state.database.setSeen(seenNick) - of MQuit: - createSeen(PSeenQuit, event.nick, event.origin) - let msg = event.params[event.params.high] - seenNick.msg = msg - state.database.setSeen(seenNick) - of MNick: - createSeen(PSeenNick, event.nick, "#nim") - seenNick.newNick = event.params[0] - state.database.setSeen(seenNick) - else: - discard # TODO: ? - -proc open(port: TPort = TPort(5123)): PState = - var res: PState - new(res) - res.dispatcher = newDispatcher() - - res.hubPort = port - res.hubConnect() - let hirc = - proc (a: PAsyncIRC, ev: TIRCEvent) = - handleIrc(a, ev, res) - # Connect to the irc server. - res.ircClient = AsyncIrc(ircServer, nick = botNickname, user = botNickname, - joinChans = joinChans, ircEvent = hirc) - res.ircClient.connect() - res.dispatcher.register(res.ircClient) - - res.dbConnected = false - result = res - -var state = tircbot.open() # Connect to the website and the IRC server. - -while state.dispatcher.poll(): - if state.dbConnected: - state.database.keepAlive() diff --git a/tests/stdlib/tmarshal.nim b/tests/stdlib/tmarshal.nim index 1b83aab53e..a778d2f77c 100644 --- a/tests/stdlib/tmarshal.nim +++ b/tests/stdlib/tmarshal.nim @@ -6,11 +6,11 @@ import marshal template testit(x: expr) = discard $$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"]] -testit(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"]] +testit(x) var test2: tuple[name: string, s: int] = ("tuple test", 56) testit(test2) @@ -24,7 +24,7 @@ type of blah: help: string else: - nil + discard PNode = ref TNode TNode = object diff --git a/tests/stdlib/tmitems.nim b/tests/stdlib/tmitems.nim index 2297f0ee93..2c0a0392a5 100644 --- a/tests/stdlib/tmitems.nim +++ b/tests/stdlib/tmitems.nim @@ -11,8 +11,8 @@ fpqeew [11, 12, 13] [11, 12, 13] [11, 12, 13] -{ "key1": 11, "key2": 12, "key3": 13} -[ 11, 12, 13] +{"key1": 11, "key2": 12, "key3": 13} +[11, 12, 13] diff --git a/tests/stdlib/tpermutations.nim b/tests/stdlib/tpermutations.nim new file mode 100644 index 0000000000..a6e07ded6f --- /dev/null +++ b/tests/stdlib/tpermutations.nim @@ -0,0 +1,19 @@ +discard """ + output: '''@[0, 2, 1] +@[1, 0, 2] +@[1, 2, 0] +@[2, 0, 1] +@[2, 1, 0] +@[2, 0, 1] +@[1, 2, 0] +@[1, 0, 2] +@[0, 2, 1] +@[0, 1, 2]''' +""" +import algorithm + +var v = @[0, 1, 2] +while v.nextPermutation(): + echo v +while v.prevPermutation(): + echo v diff --git a/tests/stdlib/tstrutil.nim b/tests/stdlib/tstrutil.nim index da65d1f89e..3db484faa9 100644 --- a/tests/stdlib/tstrutil.nim +++ b/tests/stdlib/tstrutil.nim @@ -2,18 +2,18 @@ discard """ file: "tstrutil.nim" output: "ha/home/a1xyz/usr/bin" """ -# test the new strutils module - -import - strutils - -proc testStrip() = - write(stdout, strip(" ha ")) - -proc main() = - testStrip() - for p in split("/home/a1:xyz:/usr/bin", {':'}): - write(stdout, p) +# test the new strutils module + +import + strutils + +proc testStrip() = + write(stdout, strip(" ha ")) + +proc main() = + testStrip() + for p in split("/home/a1:xyz:/usr/bin", {':'}): + write(stdout, p) proc testDelete = var s = "0123456789ABCDEFGH" @@ -25,25 +25,34 @@ proc testDelete = assert s == "1236789ABCDEFG" testDelete() - + assert(insertSep($1000_000) == "1_000_000") assert(insertSep($232) == "232") assert(insertSep($12345, ',') == "12,345") assert(insertSep($0) == "0") - -assert(editDistance("prefix__hallo_suffix", "prefix__hallo_suffix") == 0) -assert(editDistance("prefix__hallo_suffix", "prefix__hallo_suffi1") == 1) -assert(editDistance("prefix__hallo_suffix", "prefix__HALLO_suffix") == 5) -assert(editDistance("prefix__hallo_suffix", "prefix__ha_suffix") == 3) -assert(editDistance("prefix__hallo_suffix", "prefix") == 14) -assert(editDistance("prefix__hallo_suffix", "suffix") == 14) -assert(editDistance("prefix__hallo_suffix", "prefix__hao_suffix") == 2) + +assert(editDistance("prefix__hallo_suffix", "prefix__hallo_suffix") == 0) +assert(editDistance("prefix__hallo_suffix", "prefix__hallo_suffi1") == 1) +assert(editDistance("prefix__hallo_suffix", "prefix__HALLO_suffix") == 5) +assert(editDistance("prefix__hallo_suffix", "prefix__ha_suffix") == 3) +assert(editDistance("prefix__hallo_suffix", "prefix") == 14) +assert(editDistance("prefix__hallo_suffix", "suffix") == 14) +assert(editDistance("prefix__hallo_suffix", "prefix__hao_suffix") == 2) assert "/1/2/3".rfind('/') == 4 assert "/1/2/3".rfind('/', 1) == 0 assert "/1/2/3".rfind('0') == -1 - -main() -#OUT ha/home/a1xyz/usr/bin +assert(toHex(100i16, 32) == "00000000000000000000000000000064") +assert(toHex(-100i16, 32) == "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFF9C") +assert(' '.repeat(8)== " ") +assert(" ".repeat(8) == " ") +assert(spaces(8) == " ") + +assert(' '.repeat(0) == "") +assert(" ".repeat(0) == "") +assert(spaces(0) == "") + +main() +#OUT ha/home/a1xyz/usr/bin diff --git a/tests/table/ttables.nim b/tests/table/ttables.nim deleted file mode 100644 index de4aaed5eb..0000000000 --- a/tests/table/ttables.nim +++ /dev/null @@ -1,128 +0,0 @@ -discard """ - output: '''true''' -""" - -import hashes, tables - -const - data = { - "34": 123456, "12": 789, - "90": 343, "0": 34404, - "1": 344004, "2": 344774, - "3": 342244, "4": 3412344, - "5": 341232144, "6": 34214544, - "7": 3434544, "8": 344544, - "9": 34435644, "---00": 346677844, - "10": 34484, "11": 34474, "19": 34464, - "20": 34454, "30": 34141244, "40": 344114, - "50": 344490, "60": 344491, "70": 344492, - "80": 344497} - - sorteddata = { - "---00": 346677844, - "0": 34404, - "1": 344004, - "10": 34484, - "11": 34474, - "12": 789, - "19": 34464, - "2": 344774, "20": 34454, - "3": 342244, "30": 34141244, - "34": 123456, - "4": 3412344, "40": 344114, - "5": 341232144, "50": 344490, - "6": 34214544, "60": 344491, - "7": 3434544, "70": 344492, - "8": 344544, "80": 344497, - "9": 34435644, - "90": 343} - -block tableTest1: - var t = initTable[tuple[x, y: int], string]() - t[(0,0)] = "00" - t[(1,0)] = "10" - t[(0,1)] = "01" - t[(1,1)] = "11" - for x in 0..1: - for y in 0..1: - assert t[(x,y)] == $x & $y - assert($t == - "{(x: 0, y: 0): 00, (x: 0, y: 1): 01, (x: 1, y: 0): 10, (x: 1, y: 1): 11}") - -block tableTest2: - var t = initTable[string, float]() - t["test"] = 1.2345 - 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")) - - 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 = initOrderedTable[string, int](2) - for key, val in items(data): t[key] = val - for key, val in items(data): assert t[key] == val - var i = 0 - # `pairs` needs to yield in insertion order: - for key, val in pairs(t): - assert key == data[i][0] - assert val == data[i][1] - inc(i) - - for key, val in mpairs(t): val = 99 - for val in mvalues(t): assert val == 99 - -block countTableTest1: - var s = data.toTable - var t = initCountTable[string]() - for k in s.keys: t.inc(k) - for k in t.keys: assert t[k] == 1 - t.inc("90", 3) - t.inc("12", 2) - t.inc("34", 1) - assert t.largest()[0] == "90" - - t.sort() - var i = 0 - for k, v in t.pairs: - case i - of 0: assert k == "90" and v == 4 - of 1: assert k == "12" and v == 3 - of 2: assert k == "34" and v == 2 - else: break - inc i - -block SyntaxTest: - var x = toTable[int, string]({:}) - -proc orderedTableSortTest() = - var t = initOrderedTable[string, int](2) - for key, val in items(data): t[key] = val - for key, val in items(data): assert t[key] == val - t.sort(proc (x, y: tuple[key: string, val: int]): int = cmp(x.key, y.key)) - var i = 0 - # `pairs` needs to yield in sorted order: - for key, val in pairs(t): - doAssert key == sorteddata[i][0] - doAssert val == sorteddata[i][1] - inc(i) - - # check that lookup still works: - for key, val in pairs(t): - doAssert val == t[key] - # check that insert still works: - t["newKeyHere"] = 80 - - -orderedTableSortTest() -echo "true" - diff --git a/tests/template/t2do.nim b/tests/template/t2do.nim new file mode 100644 index 0000000000..b87e3328c2 --- /dev/null +++ b/tests/template/t2do.nim @@ -0,0 +1,22 @@ +discard """ + output: "8.0" +""" + +# bug #2057 + +proc mpf_get_d(x: int): float = float(x) +proc mpf_cmp_d(a: int; b: float): int = 0 + +template toFloatHelper(result: expr; tooSmall, tooLarge: stmt) {.immediate.} = + result = mpf_get_d(a) + if result == 0.0 and mpf_cmp_d(a,0.0) != 0: + tooSmall + if result == Inf: + tooLarge + +proc toFloat*(a: int): float = + toFloatHelper(result) + do: raise newException(ValueError, "number too small"): + raise newException(ValueError, "number too large") + +echo toFloat(8) diff --git a/tests/template/tparams_gensymed.nim b/tests/template/tparams_gensymed.nim index 4178812af6..6c4413866f 100644 --- a/tests/template/tparams_gensymed.nim +++ b/tests/template/tparams_gensymed.nim @@ -12,3 +12,51 @@ template genNodeKind(kind, name: expr): stmt = result.add(c) genNodeKind(nnkNone, None) + + +# Test that generics in templates still work (regression to fix #1915) + +# bug #2004 + +type Something = object + +proc testA(x: Something) = discard + +template def(name: expr) {.immediate.} = + proc testB[T](reallyUniqueName: T) = + `test name`(reallyUniqueName) +def A + +var x: Something +testB(x) + + +# bug #2215 +# Test that templates in generics still work (regression to fix the +# regression...) + +template forStatic(index: expr, slice: Slice[int], predicate: stmt): + stmt {.immediate.} = + const a = slice.a + const b = slice.b + when a <= b: + template iteration(i: int) = + block: + const index = i + predicate + template iterateStartingFrom(i: int): stmt = + when i <= b: + iteration i + iterateStartingFrom i + 1 + iterateStartingFrom a + +proc concreteProc(x: int) = + forStatic i, 0..3: + echo i + +proc genericProc(x: any) = + forStatic i, 0..3: + echo i + +concreteProc(7) # This works +genericProc(7) # This doesn't compile diff --git a/tests/template/tscope.nim b/tests/template/tscope.nim new file mode 100644 index 0000000000..2d5841af39 --- /dev/null +++ b/tests/template/tscope.nim @@ -0,0 +1,12 @@ +discard """ + errormsg: "redefinition of 'x'" +""" + +var x = 1 +template quantity(): stmt {.immediate.} = + # Causes internal error in compiler/sem.nim + proc unit*(x = 1.0): float = 12 + # Throws the correct error: redefinition of 'x' + #proc unit*(y = 1.0): float = 12 +quantity() +var x = 2 diff --git a/tests/testament/categories.nim b/tests/testament/categories.nim index 5cd5c15457..ab1e46d6f4 100644 --- a/tests/testament/categories.nim +++ b/tests/testament/categories.nim @@ -120,7 +120,8 @@ proc gcTests(r: var TResults, cat: Category, options: string) = " --gc:markAndSweep", cat, actionRun) testSpec r, makeTest("tests/gc" / filename, options & " -d:release --gc:markAndSweep", cat, actionRun) - + + test "growobjcrash" test "gcbench" test "gcleak" test "gcleak2" @@ -229,17 +230,17 @@ proc testStdlib(r: var TResults, pattern, options: string, cat: Category) = else: testNoSpec r, makeTest(test, options, cat, actionCompile) -# ----------------------------- babel ---------------------------------------- +# ----------------------------- nimble ---------------------------------------- type PackageFilter = enum pfCoreOnly pfExtraOnly pfAll let - babelExe = findExe("babel") - babelDir = getHomeDir() / ".babel" - packageDir = babelDir / "pkgs" - packageIndex = babelDir / "packages.json" + nimbleExe = findExe("nimble") + nimbleDir = getHomeDir() / ".nimble" + packageDir = nimbleDir / "pkgs" + packageIndex = nimbleDir / "packages.json" proc waitForExitEx(p: Process): int = var outp = outputStream(p) @@ -254,7 +255,7 @@ proc waitForExitEx(p: Process): int = proc getPackageDir(package: string): string = ## TODO - Replace this with dom's version comparison magic. - var commandOutput = execCmdEx("babel path $#" % package) + var commandOutput = execCmdEx("nimble path $#" % package) if commandOutput.exitCode != QuitSuccess: return "" else: @@ -267,7 +268,7 @@ iterator listPackages(filter: PackageFilter): tuple[name, url: string] = let name = package["name"].str url = package["url"].str - isCorePackage = "nimrod-code" in normalize(url) + isCorePackage = "nim-lang" in normalize(url) case filter: of pfCoreOnly: if isCorePackage: @@ -278,13 +279,13 @@ iterator listPackages(filter: PackageFilter): tuple[name, url: string] = of pfAll: yield (name, url) -proc testBabelPackages(r: var TResults, cat: Category, filter: PackageFilter) = - if babelExe == "": - echo("[Warning] - Cannot run babel tests: Babel binary not found.") +proc testNimblePackages(r: var TResults, cat: Category, filter: PackageFilter) = + if nimbleExe == "": + echo("[Warning] - Cannot run nimble tests: Nimble binary not found.") return - if execCmd("$# update" % babelExe) == QuitFailure: - echo("[Warning] - Cannot run babel tests: Babel update failed.") + if execCmd("$# update" % nimbleExe) == QuitFailure: + echo("[Warning] - Cannot run nimble tests: Nimble update failed.") return let packageFileTest = makeTest("PackageFileParsed", "", cat) @@ -293,7 +294,7 @@ proc testBabelPackages(r: var TResults, cat: Category, filter: PackageFilter) = var test = makeTest(name, "", cat) echo(url) let - installProcess = startProcess(babelExe, "", ["install", "-y", name]) + installProcess = startProcess(nimbleExe, "", ["install", "-y", name]) installStatus = waitForExitEx(installProcess) installProcess.close if installStatus != QuitSuccess: @@ -303,7 +304,7 @@ proc testBabelPackages(r: var TResults, cat: Category, filter: PackageFilter) = let buildPath = getPackageDir(name)[0.. -3] let - buildProcess = startProcess(babelExe, buildPath, ["build"]) + buildProcess = startProcess(nimbleExe, buildPath, ["build"]) buildStatus = waitForExitEx(buildProcess) buildProcess.close if buildStatus != QuitSuccess: @@ -311,13 +312,13 @@ proc testBabelPackages(r: var TResults, cat: Category, filter: PackageFilter) = r.addResult(test, "", "", reSuccess) r.addResult(packageFileTest, "", "", reSuccess) except JsonParsingError: - echo("[Warning] - Cannot run babel tests: Invalid package file.") + echo("[Warning] - Cannot run nimble tests: Invalid package file.") r.addResult(packageFileTest, "", "", reBuildFailed) # ---------------------------------------------------------------------------- -const AdditionalCategories = ["debugger", "examples", "lib", "babel-core"] +const AdditionalCategories = ["debugger", "examples", "lib", "nimble-core"] proc `&.?`(a, b: string): string = # candidate for the stdlib? @@ -330,8 +331,9 @@ proc `&?.`(a, b: string): string = proc processCategory(r: var TResults, cat: Category, options: string) = case cat.string.normalize of "rodfiles": - compileRodFiles(r, cat, options) - runRodFiles(r, cat, options) + discard # Disabled for now + #compileRodFiles(r, cat, options) + #runRodFiles(r, cat, options) of "js": # XXX JS doesn't need to be special anymore jsTests(r, cat, options) @@ -354,12 +356,12 @@ proc processCategory(r: var TResults, cat: Category, options: string) = compileExample(r, "examples/*.nim", options, cat) compileExample(r, "examples/gtk/*.nim", options, cat) compileExample(r, "examples/talk/*.nim", options, cat) - of "babel-core": - testBabelPackages(r, cat, pfCoreOnly) - of "babel-extra": - testBabelPackages(r, cat, pfExtraOnly) - of "babel-all": - testBabelPackages(r, cat, pfAll) + of "nimble-core": + testNimblePackages(r, cat, pfCoreOnly) + of "nimble-extra": + testNimblePackages(r, cat, pfExtraOnly) + of "nimble-all": + testNimblePackages(r, cat, pfAll) else: for name in os.walkFiles("tests" & DirSep &.? cat.string / "t*.nim"): testSpec r, makeTest(name, options, cat) diff --git a/tests/testament/specs.nim b/tests/testament/specs.nim index e079eed629..2a8a4ea24a 100644 --- a/tests/testament/specs.nim +++ b/tests/testament/specs.nim @@ -48,6 +48,7 @@ type err*: TResultEnum substr*, sortoutput*: bool targets*: set[TTarget] + nimout*: string const targetToExt*: array[TTarget, string] = ["c", "cpp", "m", "js"] @@ -94,6 +95,7 @@ proc parseSpec*(filename: string): TSpec = result.file = filename result.msg = "" result.outp = "" + result.nimout = "" result.ccodeCheck = "" result.cmd = cmdTemplate parseSpecAux: @@ -124,6 +126,8 @@ proc parseSpec*(filename: string): TSpec = of "errormsg": result.msg = e.value result.action = actionReject + of "nimout": + result.nimout = e.value of "disabled": if parseCfgBool(e.value): result.err = reIgnored of "cmd": result.cmd = e.value diff --git a/tests/testament/tester.nim b/tests/testament/tester.nim index 865ba9c757..881a41ce63 100644 --- a/tests/testament/tester.nim +++ b/tests/testament/tester.nim @@ -64,7 +64,9 @@ proc callCompiler(cmdTemplate, filename, options: string, var suc = "" var err = "" var x = newStringOfCap(120) + result.nimout = "" while outp.readLine(x.TaintedString) or running(p): + result.nimout.add(x & "\n") if x =~ pegOfInterest: # `err` should contain the last error/warning message err = x @@ -112,7 +114,9 @@ proc addResult(r: var TResults, test: TTest, expected = expected, given = given) r.data.addf("$#\t$#\t$#\t$#", name, expected, given, $success) - if success notin {reSuccess, reIgnored}: + if success == reIgnored: + styledEcho styleBright, name, fgYellow, " [", $success, "]" + elif success != reSuccess: styledEcho styleBright, name, fgRed, " [", $success, "]" echo"Expected:" styledEcho styleBright, expected @@ -134,7 +138,7 @@ proc cmpMsgs(r: var TResults, expected, given: TSpec, test: TTest) = proc generatedFile(path, name: string, target: TTarget): string = let ext = targetToExt[target] result = path / "nimcache" / - (if target == targetJS: path.splitPath.tail & "_" else: "") & + (if target == targetJS: path.splitPath.tail & "_" else: "compiler_") & name.changeFileExt(ext) proc codegenCheck(test: TTest, check: string, given: var TSpec) = @@ -151,6 +155,13 @@ proc codegenCheck(test: TTest, check: string, given: var TSpec) = except IOError: given.err = reCodeNotFound +proc nimoutCheck(test: TTest; expectedNimout: string; given: var TSpec) = + if expectedNimout.len > 0: + let exp = expectedNimout.strip.replace("\C\L", "\L") + let giv = given.nimout.strip.replace("\C\L", "\L") + if exp notin giv: + given.err = reMsgsDiffer + proc makeDeterministic(s: string): string = var x = splitLines(s) sort(x, system.cmp) @@ -172,6 +183,7 @@ proc testSpec(r: var TResults, test: TTest) = test.target) if given.err == reSuccess: codegenCheck(test, expected.ccodeCheck, given) + nimoutCheck(test, expected.nimout, given) r.addResult(test, "", given.msg, given.err) if given.err == reSuccess: inc(r.passed) of actionRun: @@ -205,6 +217,7 @@ proc testSpec(r: var TResults, test: TTest) = given.err = reOutputsDiffer if given.err == reSuccess: codeGenCheck(test, expected.ccodeCheck, given) + nimoutCheck(test, expected.nimout, given) if given.err == reSuccess: inc(r.passed) r.addResult(test, expected.outp, buf.string, given.err) else: diff --git a/tests/trmacros/tor.nim b/tests/trmacros/tor.nim index dc72a96cd6..5008515826 100644 --- a/tests/trmacros/tor.nim +++ b/tests/trmacros/tor.nim @@ -1,5 +1,5 @@ discard """ - output: '''3060 + output: '''3030 true 3''' """ diff --git a/tests/tuples/tgeneric_tuple.nim b/tests/tuples/tgeneric_tuple.nim new file mode 100644 index 0000000000..32f0815964 --- /dev/null +++ b/tests/tuples/tgeneric_tuple.nim @@ -0,0 +1,9 @@ +# bug #2121 + +type + Item[K,V] = tuple + key: K + value: V + +var q = newseq[Item[int,int]](0) +let (x,y) = q[0] diff --git a/tests/types/tauto_canbe_void.nim b/tests/types/tauto_canbe_void.nim new file mode 100644 index 0000000000..60e83c510d --- /dev/null +++ b/tests/types/tauto_canbe_void.nim @@ -0,0 +1,9 @@ + +import future + +template tempo(s: expr) = + s("arg") + +tempo((s: string)->auto => echo(s)) +tempo((s: string) => echo(s)) + diff --git a/tests/types/temptyseqs.nim b/tests/types/temptyseqs.nim index f8d22bdb83..2b07ba679a 100644 --- a/tests/types/temptyseqs.nim +++ b/tests/types/temptyseqs.nim @@ -5,7 +5,7 @@ discard """ # bug #1708 let foo = { "1" : (bar: @["1"]), - "2" : (baz: @[]) + "2" : (bar: @[]) } # bug #871 diff --git a/tests/types/tforwty2.nim b/tests/types/tforwty2.nim index d103314c57..52af1c7dd3 100644 --- a/tests/types/tforwty2.nim +++ b/tests/types/tforwty2.nim @@ -1,5 +1,5 @@ # Test for a hard to fix internal error -# occured in the SDL library +# occurred in the SDL library {.push dynlib: "SDL.dll", callconv: cdecl.} diff --git a/tests/vm/tconsttable.nim b/tests/vm/tconsttable.nim new file mode 100644 index 0000000000..64a74a59d5 --- /dev/null +++ b/tests/vm/tconsttable.nim @@ -0,0 +1,19 @@ +discard """ + output: '''is +finally +nice!''' +""" + +import tables + +const + foo = {"ah": "finally", "this": "is", "possible.": "nice!"}.toTable() + +# protect against overly smart compiler: +var x = "this" + +echo foo[x] +x = "ah" +echo foo[x] +x = "possible." +echo foo[x] diff --git a/tinyc/arm-gen.c b/tinyc/arm-gen.c index 42feecf73d..050a8ad888 100644 --- a/tinyc/arm-gen.c +++ b/tinyc/arm-gen.c @@ -1506,7 +1506,7 @@ void gen_opf(int op) case TOK_UGE: case TOK_ULE: case TOK_UGT: - error("unsigned comparision on floats?"); + error("unsigned comparison on floats?"); break; case TOK_LT: op=TOK_Nset; diff --git a/tinyc/c67-gen.c b/tinyc/c67-gen.c index 04f8a12b74..77c68a2792 100644 --- a/tinyc/c67-gen.c +++ b/tinyc/c67-gen.c @@ -235,7 +235,7 @@ void gsym(int t) } // these are regs that tcc doesn't really know about, -// but asign them unique values so the mapping routines +// but assign them unique values so the mapping routines // can distinquish them #define C67_A0 105 diff --git a/tinyc/i386-asm.c b/tinyc/i386-asm.c index 21b28d7a09..12ff8f2ba9 100644 --- a/tinyc/i386-asm.c +++ b/tinyc/i386-asm.c @@ -1105,7 +1105,7 @@ static void subst_asm_operand(CString *add_str, } } -/* generate prolog and epilog code for asm statment */ +/* generate prolog and epilog code for asm statement */ static void asm_gen_code(ASMOperand *operands, int nb_operands, int nb_outputs, int is_output, uint8_t *clobber_regs, diff --git a/tinyc/lib/bcheck.c b/tinyc/lib/bcheck.c index 0ec2a4b479..c59d04eb8d 100644 --- a/tinyc/lib/bcheck.c +++ b/tinyc/lib/bcheck.c @@ -628,7 +628,7 @@ int __bound_delete_region(void *p) } /* return the size of the region starting at p, or EMPTY_SIZE if non - existant region. */ + existent region. */ static unsigned long get_region_size(void *p) { unsigned long addr = (unsigned long)p; diff --git a/tinyc/tcc-doc.html b/tinyc/tcc-doc.html index e40532ed0b..bd856d2566 100644 --- a/tinyc/tcc-doc.html +++ b/tinyc/tcc-doc.html @@ -927,7 +927,7 @@ They can be defined several times in the same source. Use 'b'

    4.4 Directives

    -

    All directives are preceeded by a '.'. The following directives are +

    All directives are preceded by a '.'. The following directives are supported: