diff options
49 files changed, 413 insertions, 265 deletions
diff --git a/compiler/ccgexprs.nim b/compiler/ccgexprs.nim index 635756220..6b3906226 100644 --- a/compiler/ccgexprs.nim +++ b/compiler/ccgexprs.nim @@ -303,10 +303,11 @@ proc genAssignment(p: BProc, dest, src: TLoc, flags: TAssignmentFlags) = linefmt(p, cpsStmts, "$1 = $2;$n", rdLoc(dest), rdLoc(src)) of tyObject: # XXX: check for subtyping? - if needsComplexAssignment(dest.t): - if asgnComplexity(dest.t.n) <= 4: - discard getTypeDesc(p.module, dest.t) - genOptAsgnObject(p, dest, src, flags, dest.t.n) + if needsComplexAssignment(ty): + if asgnComplexity(ty.n) <= 4: + discard getTypeDesc(p.module, ty) + internalAssert ty.n != nil + genOptAsgnObject(p, dest, src, flags, ty.n) else: genGenericAsgn(p, dest, src, flags) else: @@ -465,10 +466,11 @@ proc unaryArithOverflow(p: BProc, e: PNode, d: var TLoc, m: TMagic) = proc binaryArith(p: BProc, e: PNode, d: var TLoc, op: TMagic) = const binArithTab: array[mAddF64..mXor, string] = [ - "($1 + $2)", # AddF64 - "($1 - $2)", # SubF64 - "($1 * $2)", # MulF64 - "($1 / $2)", # DivF64 + "(($4)($1) + ($4)($2))", # AddF64 + "(($4)($1) - ($4)($2))", # SubF64 + "(($4)($1) * ($4)($2))", # MulF64 + "(($4)($1) / ($4)($2))", # DivF64 + "($4)((NU$3)($1) >> (NU$3)($2))", # ShrI "($4)((NU$3)($1) << (NU$3)($2))", # ShlI "($4)($1 & $2)", # BitandI @@ -641,14 +643,7 @@ proc genTupleElem(p: BProc, e: PNode, d: var TLoc) = case e.sons[1].kind of nkIntLit..nkUInt64Lit: i = int(e.sons[1].intVal) else: internalError(e.info, "genTupleElem") - when false: - if ty.n != nil: - var field = ty.n.sons[i].sym - if field == nil: InternalError(e.info, "genTupleElem") - if field.loc.r == nil: InternalError(e.info, "genTupleElem") - appf(r, ".$1", [field.loc.r]) - else: - appf(r, ".Field$1", [toRope(i)]) + appf(r, ".Field$1", [toRope(i)]) putIntoDest(p, d, ty.sons[i], r) proc genRecordField(p: BProc, e: PNode, d: var TLoc) = @@ -839,6 +834,7 @@ proc genAndOr(p: BProc, e: PNode, d: var TLoc, m: TMagic) = proc genEcho(p: BProc, n: PNode) = # this unusal way of implementing it ensures that e.g. ``echo("hallo", 45)`` # is threadsafe. + discard lists.IncludeStr(p.module.headerFiles, "<stdio.h>") var args: PRope = nil var a: TLoc for i in countup(1, n.len-1): @@ -1488,8 +1484,9 @@ proc binaryFloatArith(p: BProc, e: PNode, d: var TLoc, m: TMagic) = assert(e.sons[2].typ != nil) InitLocExpr(p, e.sons[1], a) InitLocExpr(p, e.sons[2], b) - putIntoDest(p, d, e.typ, rfmt(nil, "($2 $1 $3)", - toRope(opr[m]), rdLoc(a), rdLoc(b))) + putIntoDest(p, d, e.typ, rfmt(nil, "(($4)($2) $1 ($4)($3))", + toRope(opr[m]), rdLoc(a), rdLoc(b), + getSimpleTypeDesc(p.module, e[1].typ))) if optNanCheck in p.options: linefmt(p, cpsStmts, "#nanCheck($1);$n", rdLoc(d)) if optInfCheck in p.options: diff --git a/compiler/lexer.nim b/compiler/lexer.nim index 1d34c59ab..82bfa0ad4 100644 --- a/compiler/lexer.nim +++ b/compiler/lexer.nim @@ -408,12 +408,12 @@ proc GetNumber(L: var TLexer): TToken = (result.tokType == tkFloat64Lit): result.fnumber = parseFloat(result.literal) if result.tokType == tkIntLit: result.tokType = tkFloatLit - else: - result.iNumber = ParseBiggestInt(result.literal) - if (result.iNumber < low(int32)) or (result.iNumber > high(int32)): - if result.tokType == tkIntLit: + else: + result.iNumber = parseBiggestInt(result.literal) + if (result.iNumber < low(int32)) or (result.iNumber > high(int32)): + if result.tokType == tkIntLit: result.tokType = tkInt64Lit - elif result.tokType != tkInt64Lit: + elif result.tokType in {tkInt8Lit, tkInt16Lit}: lexMessage(L, errInvalidNumber, result.literal) except EInvalidValue: lexMessage(L, errInvalidNumber, result.literal) diff --git a/compiler/magicsys.nim b/compiler/magicsys.nim index 1972dec98..0c0b87222 100644 --- a/compiler/magicsys.nim +++ b/compiler/magicsys.nim @@ -74,7 +74,7 @@ proc getSysType(kind: TTypeKind): PType = of tyUInt64: result = sysTypeFromName("uint64") of tyFloat: result = sysTypeFromName("float") of tyFloat32: result = sysTypeFromName("float32") - of tyFloat64: result = sysTypeFromName("float64") + of tyFloat64: return sysTypeFromName("float64") of tyFloat128: result = sysTypeFromName("float128") of tyBool: result = sysTypeFromName("bool") of tyChar: result = sysTypeFromName("char") @@ -115,11 +115,16 @@ proc getIntLitType*(literal: PNode): PType = result = copyType(ti, ti.owner, false) result.n = literal +proc getFloatLitType*(literal: PNode): PType = + # for now we do not cache these: + result = newSysType(tyFloat, size=8) + result.n = literal + proc skipIntLit*(t: PType): PType {.inline.} = - if t.kind == tyInt and t.n != nil: - result = getSysType(tyInt) - else: - result = t + if t.n != nil: + if t.kind in {tyInt, tyFloat}: + return getSysType(t.kind) + result = t proc AddSonSkipIntLit*(father, son: PType) = if isNil(father.sons): father.sons = @[] diff --git a/compiler/msgs.nim b/compiler/msgs.nim index c0b1221ef..5363442b4 100644 --- a/compiler/msgs.nim +++ b/compiler/msgs.nim @@ -8,7 +8,10 @@ # import - options, strutils, os, tables, sockets, ropes, platform + options, strutils, os, tables, ropes, platform + +when useCaas: + import sockets type TMsgKind* = enum @@ -532,14 +535,19 @@ var gWarnCounter*: int = 0 gErrorMax*: int = 1 # stop after gErrorMax errors gSilence*: int # == 0 if we produce any output at all - stdoutSocket*: TSocket -proc SuggestWriteln*(s: string) = - if gSilence == 0: - if isNil(stdoutSocket): Writeln(stdout, s) - else: +when useCaas: + var stdoutSocket*: TSocket + +proc SuggestWriteln*(s: string) = + if gSilence == 0: + when useCaas: + if isNil(stdoutSocket): Writeln(stdout, s) + else: + Writeln(stdout, s) + stdoutSocket.send(s & "\c\L") + else: Writeln(stdout, s) - stdoutSocket.send(s & "\c\L") proc SuggestQuit*() = if not isServing: diff --git a/compiler/nimconf.nim b/compiler/nimconf.nim index 39375ffd3..507812d9c 100644 --- a/compiler/nimconf.nim +++ b/compiler/nimconf.nim @@ -213,8 +213,11 @@ proc getUserConfigPath(filename: string): string = proc getSystemConfigPath(filename: string): string = # try standard configuration file (installation did not distribute files # the UNIX way) - result = joinPath([getPrefixDir(), "config", filename]) - if not ExistsFile(result): result = "/etc/" & filename + let p = getPrefixDir() + result = joinPath([p, "config", filename]) + when defined(unix): + if not existsFile(result): result = joinPath([p, "etc", filename]) + if not existsFile(result): result = "/etc/" & filename proc LoadConfigs*(cfg: string) = # set default value (can be overwritten): diff --git a/compiler/nimrod.ini b/compiler/nimrod.ini index 49dcd25ba..482fe63c9 100644 --- a/compiler/nimrod.ini +++ b/compiler/nimrod.ini @@ -1,7 +1,6 @@ [Project] Name: "Nimrod" Version: "$version" -; Windows and i386 must be first! OS: "windows;linux;macosx;solaris;freebsd;netbsd;openbsd" CPU: "i386;amd64;powerpc64;arm" # ;sparc Authors: "Andreas Rumpf" diff --git a/compiler/options.nim b/compiler/options.nim index 2b25b2650..3c91d4439 100644 --- a/compiler/options.nim +++ b/compiler/options.nim @@ -15,6 +15,7 @@ const useEffectSystem* = true hasFFI* = defined(useFFI) newScopeForIf* = true + useCaas* = not defined(noCaas) type # please make sure we have under 32 options # (improves code efficiency a lot!) diff --git a/compiler/parser.nim b/compiler/parser.nim index d19149073..0d9d27e02 100644 --- a/compiler/parser.nim +++ b/compiler/parser.nim @@ -1412,7 +1412,6 @@ proc parseRoutine(p: var TParser, kind: TNodeKind): PNode = proc newCommentStmt(p: var TParser): PNode = #| commentStmt = COMMENT result = newNodeP(nkCommentStmt, p) - result.info.line = result.info.line - int16(1) - int16(p.tok.iNumber) result.comment = p.tok.literal getTok(p) diff --git a/compiler/renderer.nim b/compiler/renderer.nim index 70ce5c27d..efa4ecaba 100644 --- a/compiler/renderer.nim +++ b/compiler/renderer.nim @@ -329,7 +329,7 @@ proc atom(n: PNode): string = if n.flags * {nfBase2, nfBase8, nfBase16} == {}: result = $n.floatVal & "\'f32" else: - f = n.floatVal + f = n.floatVal.float32 result = litAux(n, (cast[PInt32](addr(f)))[], 4) & "\'f32" of nkFloat64Lit: if n.flags * {nfBase2, nfBase8, nfBase16} == {}: @@ -407,7 +407,8 @@ proc lsub(n: PNode): int = result = result + lcomma(n, 1) of nkExprColonExpr: result = lsons(n) + 2 of nkInfix: result = lsons(n) + 2 - of nkPrefix: result = lsons(n) + 1 + of nkPrefix: + result = lsons(n)+1+(if n.len > 0 and n.sons[1].kind == nkInfix: 2 else: 0) of nkPostfix: result = lsons(n) of nkCallStrLit: result = lsons(n) of nkPragmaExpr: result = lsub(n.sons[0]) + lcomma(n, 1) @@ -939,7 +940,12 @@ proc gsub(g: var TSrcGen, n: PNode, c: TContext) = gsub(g, n.sons[0]) if n.len > 1: put(g, tkSpaces, space) - gsub(g, n.sons[1]) + if n.sons[1].kind == nkInfix: + put(g, tkParLe, "(") + gsub(g, n.sons[1]) + put(g, tkParRi, ")") + else: + gsub(g, n.sons[1]) of nkPostfix: gsub(g, n.sons[1]) gsub(g, n.sons[0]) diff --git a/compiler/semcall.nim b/compiler/semcall.nim index b2f41d509..c8f150922 100644 --- a/compiler/semcall.nim +++ b/compiler/semcall.nim @@ -290,11 +290,14 @@ proc SearchForBorrowProc(c: PContext, startScope: PScope, fn: PSym): PSym = # New approach: generate fn(x, y, z) where x, y, z have the proper types # and use the overloading resolution mechanism: var call = newNode(nkCall) + var hasDistinct = false call.add(newIdentNode(fn.name, fn.info)) for i in 1.. <fn.typ.n.len: let param = fn.typ.n.sons[i] let t = skipTypes(param.typ, abstractVar-{tyTypeDesc}) + if t.kind == tyDistinct or param.typ.kind == tyDistinct: hasDistinct = true call.add(newNodeIT(nkEmpty, fn.info, t.baseOfDistinct)) - var resolved = semOverloadedCall(c, call, call, {fn.kind}) - if resolved != nil: - result = resolved.sons[0].sym + if hasDistinct: + var resolved = semOverloadedCall(c, call, call, {fn.kind}) + if resolved != nil: + result = resolved.sons[0].sym diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim index 30b28a35d..2681150e0 100644 --- a/compiler/semexprs.nim +++ b/compiler/semexprs.nim @@ -1710,6 +1710,7 @@ proc semObjConstr(c: PContext, n: PNode, flags: TExprFlags): PNode = var check: PNode = nil f: PSym + t = objType while true: check = nil f = lookupInRecordAndBuildCheck(c, it, t.n, id, check) @@ -1846,7 +1847,7 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode = of nkUInt64Lit: if result.typ == nil: result.typ = getSysType(tyUInt64) of nkFloatLit: - if result.typ == nil: result.typ = getSysType(tyFloat) + if result.typ == nil: result.typ = getFloatLitType(result) of nkFloat32Lit: if result.typ == nil: result.typ = getSysType(tyFloat32) of nkFloat64Lit: diff --git a/compiler/semfold.nim b/compiler/semfold.nim index ed7b14684..ca06ea1b6 100644 --- a/compiler/semfold.nim +++ b/compiler/semfold.nim @@ -21,7 +21,7 @@ proc getConstExpr*(m: PSym, n: PNode): PNode proc evalOp*(m: TMagic, n, a, b, c: PNode): PNode proc leValueConv*(a, b: PNode): bool proc newIntNodeT*(intVal: BiggestInt, n: PNode): PNode -proc newFloatNodeT*(floatVal: BiggestFloat, n: PNode): PNode +proc newFloatNodeT(floatVal: BiggestFloat, n: PNode): PNode proc newStrNodeT*(strVal: string, n: PNode): PNode # implementation @@ -43,7 +43,10 @@ proc newIntNodeT(intVal: BiggestInt, n: PNode): PNode = proc newFloatNodeT(floatVal: BiggestFloat, n: PNode): PNode = result = newFloatNode(nkFloatLit, floatVal) - result.typ = n.typ + if skipTypes(n.typ, abstractVarRange).kind == tyFloat: + result.typ = getFloatLitType(result) + else: + result.typ = n.typ result.info = n.info proc newStrNodeT(strVal: string, n: PNode): PNode = diff --git a/compiler/semstmts.nim b/compiler/semstmts.nim index 022d412ce..0ce58ba5c 100644 --- a/compiler/semstmts.nim +++ b/compiler/semstmts.nim @@ -775,9 +775,12 @@ proc typeSectionFinalPass(c: PContext, n: PNode) = if aa.kind in {nkRefTy, nkPtrTy} and aa.len == 1 and aa.sons[0].kind == nkObjectTy: # give anonymous object a dummy symbol: - assert s.typ.sons[0].sym == nil - s.typ.sons[0].sym = newSym(skType, getIdent(s.name.s & ":ObjectType"), - getCurrOwner(), s.info) + var st = s.typ + if st.kind == tyGenericBody: st = st.lastSon + InternalAssert st.kind in {tyPtr, tyRef} + InternalAssert st.sons[0].sym == nil + st.sons[0].sym = newSym(skType, getIdent(s.name.s & ":ObjectType"), + getCurrOwner(), s.info) proc SemTypeSection(c: PContext, n: PNode): PNode = typeSectionLeftSidePass(c, n) @@ -1212,7 +1215,7 @@ proc semStmtList(c: PContext, n: PNode): PNode = if n.sons[i].typ == EnforceVoidContext or usesResult(n.sons[i]): voidContext = true n.typ = EnforceVoidContext - elif i != last or voidContext: + if i != last or voidContext: discardCheck(n.sons[i]) else: n.typ = n.sons[i].typ diff --git a/compiler/semtypes.nim b/compiler/semtypes.nim index d8846d717..64140274e 100644 --- a/compiler/semtypes.nim +++ b/compiler/semtypes.nim @@ -558,7 +558,7 @@ proc semObjectNode(c: PContext, n: PNode, prev: PType): PType = checkSonsLen(n, 3) if n.sons[1].kind != nkEmpty: base = skipTypes(semTypeNode(c, n.sons[1].sons[0], nil), skipPtrs) - var concreteBase = skipGenericInvokation(base) + var concreteBase = skipGenericInvokation(base).skipTypes(skipPtrs) if concreteBase.kind == tyObject and tfFinal notin concreteBase.flags: addInheritedFields(c, check, pos, concreteBase) else: diff --git a/compiler/service.nim b/compiler/service.nim index 8e8fe20bf..1de83af7c 100644 --- a/compiler/service.nim +++ b/compiler/service.nim @@ -9,11 +9,13 @@ ## Implements the "compiler as a service" feature. -import - sockets, +import times, commands, options, msgs, nimconf, extccomp, strutils, os, platform, parseopt +when useCaas: + import sockets + # We cache modules and the dependency graph. However, we don't check for # file changes but expect the client to tell us about them, otherwise the # repeated CRC calculations may turn out to be too slow. @@ -80,19 +82,22 @@ proc serve*(action: proc (){.nimcall.}) = FlushFile(stdout) of "tcp", "": - var server = Socket() - let p = getConfigVar("server.port") - let port = if p.len > 0: parseInt(p).TPort else: 6000.TPort - server.bindAddr(port, getConfigVar("server.address")) - var inp = "".TaintedString - server.listen() - new(stdoutSocket) - while true: - accept(server, stdoutSocket) - stdoutSocket.readLine(inp) - execute inp.string - stdoutSocket.send("\c\L") - stdoutSocket.close() + when useCaas: + var server = Socket() + let p = getConfigVar("server.port") + let port = if p.len > 0: parseInt(p).TPort else: 6000.TPort + server.bindAddr(port, getConfigVar("server.address")) + var inp = "".TaintedString + server.listen() + new(stdoutSocket) + while true: + accept(server, stdoutSocket) + stdoutSocket.readLine(inp) + execute inp.string + stdoutSocket.send("\c\L") + stdoutSocket.close() + else: + quit "server.type not supported; compiler built without caas support" else: echo "Invalid server.type:", typ quit 1 diff --git a/compiler/sigmatch.nim b/compiler/sigmatch.nim index ceb8ad156..318acc660 100644 --- a/compiler/sigmatch.nim +++ b/compiler/sigmatch.nim @@ -263,15 +263,19 @@ proc isConvertibleToRange(f, a: PType): bool = a.kind in {tyFloat..tyFloat128}: result = true -proc handleFloatRange(f, a: PType): TTypeRelation = - if a.kind == f.kind: +proc handleFloatRange(f, a: PType): TTypeRelation = + if a.kind == f.kind: result = isEqual - else: + else: let ab = skipTypes(a, {tyRange}) var k = ab.kind if k == f.kind: result = isSubrange + elif isFloatLit(ab): result = isFromIntLit elif isIntLit(ab): result = isConvertible - elif k >= tyFloat and k <= tyFloat128: result = isConvertible + elif k >= tyFloat and k <= tyFloat128: + # conversion to "float32" is not as good: + if f.kind == tyFloat32: result = isConvertible + else: result = isIntConv else: result = isNone proc isObjectSubtype(a, f: PType): int = diff --git a/compiler/types.nim b/compiler/types.nim index 99bedfca7..f9c40e201 100644 --- a/compiler/types.nim +++ b/compiler/types.nim @@ -109,6 +109,9 @@ proc getOrdValue(n: PNode): biggestInt = proc isIntLit*(t: PType): bool {.inline.} = result = t.kind == tyInt and t.n != nil and t.n.kind == nkIntLit +proc isFloatLit*(t: PType): bool {.inline.} = + result = t.kind == tyFloat and t.n != nil and t.n.kind == nkFloatLit + proc isCompatibleToCString(a: PType): bool = if a.kind == tyArray: if (firstOrd(a.sons[0]) == 0) and diff --git a/compiler/vmdef.nim b/compiler/vmdef.nim index 050caa65c..515f2975b 100644 --- a/compiler/vmdef.nim +++ b/compiler/vmdef.nim @@ -122,7 +122,8 @@ type opcLdImmInt, # dest = immediate value opcWrGlobal, opcWrGlobalRef, - opcSetType # dest.typ = types[Bx] + opcSetType, # dest.typ = types[Bx] + opcTypeTrait TBlock* = object label*: PSym diff --git a/compiler/vmdeps.nim b/compiler/vmdeps.nim index 0e90a9b14..2a40276d1 100644 --- a/compiler/vmdeps.nim +++ b/compiler/vmdeps.nim @@ -35,21 +35,6 @@ proc opSlurp*(file: string, info: TLineInfo, module: PSym): string = result = "" LocalError(info, errCannotOpenFile, file) -proc opTypeTrait*(n: PNode, context: PSym): PNode = - ## XXX: This should be pretty much guaranteed to be true - # by the type traits procs' signatures, but until the - # code is more mature it doesn't hurt to be extra safe - internalAssert n.len >= 2 and n.sons[1].kind == nkSym - - let typ = n.sons[1].sym.typ.skipTypes({tyTypeDesc}) - case n.sons[0].sym.name.s.normalize - of "name": - result = newStrNode(nkStrLit, typ.typeToString(preferExported)) - result.typ = newType(tyString, context) - result.info = n.info - else: - internalAssert false - when false: proc opExpandToAst*(c: PEvalContext, original: PNode): PNode = var diff --git a/config/nimrod.cfg b/config/nimrod.cfg index c50051086..77cc742b2 100644 --- a/config/nimrod.cfg +++ b/config/nimrod.cfg @@ -67,7 +67,8 @@ hint[LineTooLong]=off gpp.options.linker = "-ldl" clang.options.linker = "-ldl" tcc.options.linker = "-ldl" - @else: + @end + @if bsd or haiku: # BSD got posix_spawn only recently, so we deactivate it for osproc: define:useFork # at least NetBSD has problems with thread local storage: diff --git a/install.txt b/install.txt index eefdfb682..11c502235 100644 --- a/install.txt +++ b/install.txt @@ -62,46 +62,3 @@ Currently, the following C compilers are supported under Windows: | http://www.digitalmars.com/download/freecompiler.html However, most testing is done with GCC. - - - -Bootstrapping from github -------------------------- - -To get the source code you need either of these: - -* A working web browser + tar(or equivalent): - https://github.com/Araq/Nimrod/tarball/master -* wget + tar: - ``wget --no-check-certificate "https://github.com/Araq/Nimrod/tarball/master"`` -* git: ``git clone git://github.com/Araq/Nimrod.git`` - -After downloading the source (and extracting it), you need to -extract ``build/csources.zip``: - -* ``cd build`` -* ``unzip csources.zip`` -* ``cd ..`` - -and then you can bootstrap with: - -On Windows -~~~~~~~~~~ - -* ``build.bat`` -* ``bin\nimrod c koch`` -* ``koch boot -d:release`` - -If you want a 64 bit build, make sure that you have a GCC built for Win64 and -execute ``build64.bat`` instead of ``build.bat``. - - -On UNIX -~~~~~~~ - -* ``./build.sh`` -* ``bin/nimrod c koch`` -* ``./koch boot -d:release`` - -Installation on UNIX can then be done with ``koch install [dir]``. - diff --git a/koch.nim b/koch.nim index 30ad8c597..97fcf5b2c 100644 --- a/koch.nim +++ b/koch.nim @@ -52,6 +52,7 @@ Boot options: (not needed on Windows) -d:useFFI build Nimrod with FFI support at compile time -d:nativeStacktrace use native stack traces (only for Mac OS X or Linux) + -d:noCaas build Nimrod without CAAS support """ proc exe(f: string): string = return addFileExt(f, ExeExt) diff --git a/lib/core/typeinfo.nim b/lib/core/typeinfo.nim index 4ce65ae91..edb4d1188 100644 --- a/lib/core/typeinfo.nim +++ b/lib/core/typeinfo.nim @@ -505,7 +505,7 @@ proc setBiggestFloat*(x: TAny, y: biggestFloat) = ## some float. case skipRange(x.rawtype).kind of tyFloat: cast[ptr Float](x.value)[] = y - of tyFloat32: cast[ptr Float32](x.value)[] = y + of tyFloat32: cast[ptr Float32](x.value)[] = y.float32 of tyFloat64: cast[ptr Float64](x.value)[] = y else: assert false diff --git a/lib/packages/docutils/rstgen.nim b/lib/packages/docutils/rstgen.nim index a393943fb..364f847cc 100644 --- a/lib/packages/docutils/rstgen.nim +++ b/lib/packages/docutils/rstgen.nim @@ -265,12 +265,12 @@ proc renderHeadline(d: PDoc, n: PRstNode, result: var string) = d.tocPart[length].header = tmp dispA(d.target, result, - "<h$1><a class=\"toc-backref\" id=\"$2\" href=\"#$2_toc\">$3</a></h$1>", + "\n<h$1><a class=\"toc-backref\" id=\"$2\" href=\"#$2_toc\">$3</a></h$1>", "\\rsth$4{$3}\\label{$2}\n", [$n.level, d.tocPart[length].refname, tmp, $chr(n.level - 1 + ord('A'))]) else: - dispA(d.target, result, "<h$1 id=\"$2\">$3</h$1>", + dispA(d.target, result, "\n<h$1 id=\"$2\">$3</h$1>", "\\rsth$4{$3}\\label{$2}\n", [ $n.level, refname, tmp, $chr(n.level - 1 + ord('A'))]) diff --git a/lib/posix/posix.nim b/lib/posix/posix.nim index 57b683c3a..cf260e9b7 100644 --- a/lib/posix/posix.nim +++ b/lib/posix/posix.nim @@ -30,7 +30,7 @@ from times import TTime const - hasSpawnH = true # should exist for every Posix system really nowadays + hasSpawnH = not defined(haiku) # should exist for every Posix system nowadays hasAioH = defined(linux) when false: @@ -842,48 +842,51 @@ var FE_UPWARD* {.importc, header: "<fenv.h>".}: cint FE_DFL_ENV* {.importc, header: "<fenv.h>".}: cint - MM_HARD* {.importc, header: "<fmtmsg.h>".}: cint - ## Source of the condition is hardware. - MM_SOFT* {.importc, header: "<fmtmsg.h>".}: cint - ## Source of the condition is software. - MM_FIRM* {.importc, header: "<fmtmsg.h>".}: cint - ## Source of the condition is firmware. - MM_APPL* {.importc, header: "<fmtmsg.h>".}: cint - ## Condition detected by application. - MM_UTIL* {.importc, header: "<fmtmsg.h>".}: cint - ## Condition detected by utility. - MM_OPSYS* {.importc, header: "<fmtmsg.h>".}: cint - ## Condition detected by operating system. - MM_RECOVER* {.importc, header: "<fmtmsg.h>".}: cint - ## Recoverable error. - MM_NRECOV* {.importc, header: "<fmtmsg.h>".}: cint - ## Non-recoverable error. - MM_HALT* {.importc, header: "<fmtmsg.h>".}: cint - ## Error causing application to halt. - MM_ERROR* {.importc, header: "<fmtmsg.h>".}: cint - ## Application has encountered a non-fatal fault. - MM_WARNING* {.importc, header: "<fmtmsg.h>".}: cint - ## Application has detected unusual non-error condition. - MM_INFO* {.importc, header: "<fmtmsg.h>".}: cint - ## Informative message. - MM_NOSEV* {.importc, header: "<fmtmsg.h>".}: cint - ## No severity level provided for the message. - MM_PRINT* {.importc, header: "<fmtmsg.h>".}: cint - ## Display message on standard error. - MM_CONSOLE* {.importc, header: "<fmtmsg.h>".}: cint - ## Display message on system console. - - MM_OK* {.importc, header: "<fmtmsg.h>".}: cint - ## The function succeeded. - MM_NOTOK* {.importc, header: "<fmtmsg.h>".}: cint - ## The function failed completely. - MM_NOMSG* {.importc, header: "<fmtmsg.h>".}: cint - ## The function was unable to generate a message on standard error, - ## but otherwise succeeded. - MM_NOCON* {.importc, header: "<fmtmsg.h>".}: cint - ## The function was unable to generate a console message, but - ## otherwise succeeded. - +when not defined(haiku): + var + MM_HARD* {.importc, header: "<fmtmsg.h>".}: cint + ## Source of the condition is hardware. + MM_SOFT* {.importc, header: "<fmtmsg.h>".}: cint + ## Source of the condition is software. + MM_FIRM* {.importc, header: "<fmtmsg.h>".}: cint + ## Source of the condition is firmware. + MM_APPL* {.importc, header: "<fmtmsg.h>".}: cint + ## Condition detected by application. + MM_UTIL* {.importc, header: "<fmtmsg.h>".}: cint + ## Condition detected by utility. + MM_OPSYS* {.importc, header: "<fmtmsg.h>".}: cint + ## Condition detected by operating system. + MM_RECOVER* {.importc, header: "<fmtmsg.h>".}: cint + ## Recoverable error. + MM_NRECOV* {.importc, header: "<fmtmsg.h>".}: cint + ## Non-recoverable error. + MM_HALT* {.importc, header: "<fmtmsg.h>".}: cint + ## Error causing application to halt. + MM_ERROR* {.importc, header: "<fmtmsg.h>".}: cint + ## Application has encountered a non-fatal fault. + MM_WARNING* {.importc, header: "<fmtmsg.h>".}: cint + ## Application has detected unusual non-error condition. + MM_INFO* {.importc, header: "<fmtmsg.h>".}: cint + ## Informative message. + MM_NOSEV* {.importc, header: "<fmtmsg.h>".}: cint + ## No severity level provided for the message. + MM_PRINT* {.importc, header: "<fmtmsg.h>".}: cint + ## Display message on standard error. + MM_CONSOLE* {.importc, header: "<fmtmsg.h>".}: cint + ## Display message on system console. + + MM_OK* {.importc, header: "<fmtmsg.h>".}: cint + ## The function succeeded. + MM_NOTOK* {.importc, header: "<fmtmsg.h>".}: cint + ## The function failed completely. + MM_NOMSG* {.importc, header: "<fmtmsg.h>".}: cint + ## The function was unable to generate a message on standard error, + ## but otherwise succeeded. + MM_NOCON* {.importc, header: "<fmtmsg.h>".}: cint + ## The function was unable to generate a console message, but + ## otherwise succeeded. + +var FNM_NOMATCH* {.importc, header: "<fnmatch.h>".}: cint ## The string does not match the specified pattern. FNM_PATHNAME* {.importc, header: "<fnmatch.h>".}: cint @@ -1809,8 +1812,9 @@ proc feholdexcept*(a1: ptr Tfenv): cint {.importc, header: "<fenv.h>".} proc fesetenv*(a1: ptr Tfenv): cint {.importc, header: "<fenv.h>".} proc feupdateenv*(a1: ptr TFenv): cint {.importc, header: "<fenv.h>".} -proc fmtmsg*(a1: int, a2: cstring, a3: cint, - a4, a5, a6: cstring): cint {.importc, header: "<fmtmsg.h>".} +when not defined(haiku): + proc fmtmsg*(a1: int, a2: cstring, a3: cint, + a4, a5, a6: cstring): cint {.importc, header: "<fmtmsg.h>".} proc fnmatch*(a1, a2: cstring, a3: cint): cint {.importc, header: "<fnmatch.h>".} proc ftw*(a1: cstring, diff --git a/lib/pure/httpclient.nim b/lib/pure/httpclient.nim index 15cc6abb8..2c0e7b835 100644 --- a/lib/pure/httpclient.nim +++ b/lib/pure/httpclient.nim @@ -10,6 +10,11 @@ ## This module implements a simple HTTP client that can be used to retrieve ## webpages/other data. ## +## +## **Note**: This module is not ideal, connection is not kept alive so sites with +## many redirects are expensive. As such in the future this module may change, +## and the current procedures will be deprecated. +## ## Retrieving a website ## ==================== ## @@ -62,8 +67,15 @@ ## that as long as the server is sending data an exception will not be raised, ## if however data does not reach client within the specified timeout an ETimeout ## exception will then be raised. +## +## Proxy +## ===== +## +## A proxy can be specified as a param to any of these procedures, the ``newProxy`` +## constructor should be used for this purpose. However, +## currently only basic authentication is supported. -import sockets, strutils, parseurl, parseutils, strtabs +import sockets, strutils, parseurl, parseutils, strtabs, base64 type TResponse* = tuple[ @@ -72,6 +84,10 @@ type headers: PStringTable, body: string] + PProxy* = ref object + url*: TUrl + auth*: string + EInvalidProtocol* = object of ESynch ## exception that is raised when server ## does not conform to the implemented ## protocol @@ -239,23 +255,34 @@ when not defined(ssl): else: let defaultSSLContext = newContext(verifyMode = CVerifyNone) +proc newProxy*(url: string, auth = ""): PProxy = + ## Constructs a new ``TProxy`` object. + result = PProxy(url: parseUrl(url), auth: auth) + proc request*(url: string, httpMethod = httpGET, extraHeaders = "", body = "", sslContext: PSSLContext = defaultSSLContext, - timeout = -1, userAgent = defUserAgent): TResponse = + timeout = -1, userAgent = defUserAgent, + proxy: PProxy = nil): TResponse = ## | Requests ``url`` with the specified ``httpMethod``. ## | Extra headers can be specified and must be seperated 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 = parseUrl(url) + var r = if proxy == nil: parseUrl(url) else: proxy.url var headers = substr($httpMethod, len("http")) - headers.add(" /" & r.path & r.query) + if proxy == nil: + headers.add(" /" & r.path & r.query) + else: + headers.add(" " & url) headers.add(" HTTP/1.1\c\L") add(headers, "Host: " & r.hostname & "\c\L") if userAgent != "": add(headers, "User-Agent: " & userAgent & "\c\L") + if proxy != nil and proxy.auth != "": + let auth = base64.encode(proxy.auth, newline = "") + add(headers, "Proxy-Authorization: basic " & auth & "\c\L") add(headers, extraHeaders) add(headers, "\c\L") @@ -299,30 +326,34 @@ proc getNewLocation(lastUrl: string, headers: PStringTable): string = proc get*(url: string, extraHeaders = "", maxRedirects = 5, sslContext: PSSLContext = defaultSSLContext, - timeout = -1, userAgent = defUserAgent): TResponse = + timeout = -1, userAgent = defUserAgent, + proxy: PProxy = nil): TResponse = ## | GETs the ``url`` and returns a ``TResponse`` object ## | This proc also handles redirection ## | 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, httpGET, extraHeaders, "", sslContext, timeout, userAgent) + result = request(url, httpGET, extraHeaders, "", sslContext, timeout, + userAgent, proxy) var lastURL = url for i in 1..maxRedirects: if result.status.redirection(): let redirectTo = getNewLocation(lastURL, result.headers) result = request(redirectTo, httpGET, extraHeaders, "", sslContext, - timeout, userAgent) + timeout, userAgent, proxy) lastUrl = redirectTo proc getContent*(url: string, extraHeaders = "", maxRedirects = 5, sslContext: PSSLContext = defaultSSLContext, - timeout = -1, userAgent = defUserAgent): string = + timeout = -1, userAgent = defUserAgent, + proxy: PProxy = nil): string = ## | GETs the body and returns it as a string. ## | Raises exceptions for the status codes ``4xx`` and ``5xx`` ## | 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 = get(url, extraHeaders, maxRedirects, sslContext, timeout, userAgent) + var r = get(url, extraHeaders, maxRedirects, sslContext, timeout, userAgent, + proxy) if r.status[0] in {'4','5'}: raise newException(EHTTPRequestErr, r.status) else: @@ -331,7 +362,8 @@ proc getContent*(url: string, extraHeaders = "", maxRedirects = 5, proc post*(url: string, extraHeaders = "", body = "", maxRedirects = 5, sslContext: PSSLContext = defaultSSLContext, - timeout = -1, userAgent = defUserAgent): TResponse = + timeout = -1, userAgent = defUserAgent, + proxy: PProxy = nil): TResponse = ## | POSTs ``body`` to the ``url`` and returns a ``TResponse`` object. ## | This proc adds the necessary Content-Length header. ## | This proc also handles redirection. @@ -339,27 +371,29 @@ proc post*(url: string, extraHeaders = "", body = "", ## | An optional timeout can be specified in miliseconds, if reading from the ## server takes longer than specified an ETimeout exception will be raised. var xh = extraHeaders & "Content-Length: " & $len(body) & "\c\L" - result = request(url, httpPOST, xh, body, sslContext, timeout, userAgent) + result = request(url, httpPOST, xh, body, sslContext, timeout, userAgent, + proxy) var lastUrl = "" for i in 1..maxRedirects: if result.status.redirection(): let redirectTo = getNewLocation(lastURL, result.headers) var meth = if result.status != "307": httpGet else: httpPost result = request(redirectTo, meth, xh, body, sslContext, timeout, - userAgent) + userAgent, proxy) lastUrl = redirectTo proc postContent*(url: string, extraHeaders = "", body = "", maxRedirects = 5, sslContext: PSSLContext = defaultSSLContext, - timeout = -1, userAgent = defUserAgent): string = + timeout = -1, userAgent = defUserAgent, + proxy: PProxy = nil): string = ## | POSTs ``body`` to ``url`` and returns the response's body as a string ## | Raises exceptions for the status codes ``4xx`` and ``5xx`` ## | 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 = post(url, extraHeaders, body, maxRedirects, sslContext, timeout, - userAgent) + userAgent, proxy) if r.status[0] in {'4','5'}: raise newException(EHTTPRequestErr, r.status) else: @@ -367,14 +401,15 @@ proc postContent*(url: string, extraHeaders = "", body = "", proc downloadFile*(url: string, outputFilename: string, sslContext: PSSLContext = defaultSSLContext, - timeout = -1, userAgent = defUserAgent) = + timeout = -1, userAgent = defUserAgent, + proxy: PProxy = nil) = ## | Downloads ``url`` and saves it to ``outputFilename`` ## | An optional timeout can be specified in miliseconds, if reading from the ## server takes longer than specified an ETimeout exception will be raised. var f: TFile if open(f, outputFilename, fmWrite): f.write(getContent(url, sslContext = sslContext, timeout = timeout, - userAgent = userAgent)) + userAgent = userAgent, proxy = proxy)) f.close() else: fileError("Unable to open file") diff --git a/lib/pure/os.nim b/lib/pure/os.nim index 176439ec7..43bdb16ee 100644 --- a/lib/pure/os.nim +++ b/lib/pure/os.nim @@ -164,7 +164,7 @@ else: # UNIX-like operating system ScriptExt* = "" DynlibFormat* = when defined(macosx): "lib$1.dylib" else: "lib$1.so" -when defined(macosx) or defined(bsd): +when defined(posix): var pathMax {.importc: "PATH_MAX", header: "<stdlib.h>".}: cint @@ -674,17 +674,12 @@ proc expandFilename*(filename: string): string {.rtl, extern: "nos$1", var L = GetFullPathNameA(filename, bufsize, result, unused) if L <= 0'i32 or L >= bufsize: OSError(OSLastError()) setLen(result, L) - elif defined(macosx) or defined(bsd): - # On Mac OS X 10.5, realpath does not allocate the buffer on its own - var pathBuffer: cstring = newString(pathMax) - var resultBuffer = realpath(filename, pathBuffer) - if resultBuffer == nil: OSError(OSLastError()) - result = $resultBuffer else: - var res = realpath(filename, nil) - if res == nil: OSError(OSLastError()) - result = $res - c_free(res) + # careful, realpath needs to take an allocated buffer according to Posix: + result = newString(pathMax) + var r = realpath(filename, result) + if r.isNil: OSError(OSLastError()) + setlen(result, c_strlen(result)) proc ChangeFileExt*(filename, ext: string): string {. noSideEffect, rtl, extern: "nos$1".} = diff --git a/lib/pure/strutils.nim b/lib/pure/strutils.nim index 73dd9132c..a4aa81578 100644 --- a/lib/pure/strutils.nim +++ b/lib/pure/strutils.nim @@ -1030,8 +1030,8 @@ type ffScientific ## use scientific notation (using ``e`` character) proc formatBiggestFloat*(f: BiggestFloat, format: TFloatFormat = ffDefault, - precision = 16): string {.noSideEffect, operator: 2, - rtl, extern: "nsu$1".} = + precision: range[0..32] = 16): string {. + noSideEffect, operator: 2, rtl, extern: "nsu$1".} = ## converts a floating point value `f` to a string. ## ## If ``format == ffDecimal`` then precision is the number of digits to @@ -1041,11 +1041,11 @@ proc formatBiggestFloat*(f: BiggestFloat, format: TFloatFormat = ffDefault, ## `precision`'s default value is the maximum number of meaningful digits ## after the decimal point for Nimrod's ``biggestFloat`` type. ## - ## If ``precision == 0``, it uses the + ## If ``precision == 0``, it tries to format it nicely. const floatFormatToChar: array[TFloatFormat, char] = ['g', 'f', 'e'] var frmtstr {.noinit.}: array[0..5, char] - buf: array[0..80, char] + buf {.noinit.}: array[0..2500, char] frmtstr[0] = '%' if precision > 0: frmtstr[1] = '#' @@ -1061,8 +1061,8 @@ proc formatBiggestFloat*(f: BiggestFloat, format: TFloatFormat = ffDefault, result = $buf proc formatFloat*(f: float, format: TFloatFormat = ffDefault, - precision = 16): string {.noSideEffect, operator: 2, - rtl, extern: "nsu$1".} = + precision: range[0..32] = 16): string {. + noSideEffect, operator: 2, rtl, extern: "nsu$1".} = ## converts a floating point value `f` to a string. ## ## If ``format == ffDecimal`` then precision is the number of digits to diff --git a/lib/pure/xmltree.nim b/lib/pure/xmltree.nim index 7621094f3..95b48a850 100644 --- a/lib/pure/xmltree.nim +++ b/lib/pure/xmltree.nim @@ -273,7 +273,7 @@ macro `<>`*(x: expr): expr {.immediate.} = ## Constructor macro for XML. Example usage: ## ## .. code-block:: nimrod - ## <>a(href="http://nimrod-code.org", "Nimrod rules.") + ## <>a(href="http://nimrod-code.org", newText("Nimrod rules.")) ## ## Produces an XML tree for:: ## @@ -334,3 +334,7 @@ proc findAll*(n: PXmlNode, tag: string): seq[PXmlNode] = ## process(imgTag) newSeq(result, 0) findAll(n, tag, result) + +when isMainModule: + assert """<a href="http://nimrod-code.org">Nimrod rules.</a>""" == + $(<>a(href="http://nimrod-code.org", newText("Nimrod rules."))) diff --git a/lib/system.nim b/lib/system.nim index 749124ac9..4ff1f1577 100644 --- a/lib/system.nim +++ b/lib/system.nim @@ -28,7 +28,9 @@ type uint64* {.magic: UInt64.} ## unsigned 64 bit integer type float* {.magic: Float.} ## default floating point type float32* {.magic: Float32.} ## 32 bit floating point type - float64* {.magic: Float64.} ## 64 bit floating point type + float64* {.magic: Float.} ## 64 bit floating point type + +# 'float64' is now an alias to 'float'; this solves many problems type # we need to start a new type section here, so that ``0`` can have a type bool* {.magic: Bool.} = enum ## built-in boolean type @@ -638,6 +640,14 @@ proc `<%` *(x, y: Int64): bool {.magic: "LtU64", noSideEffect.} # floating point operations: + +proc `+` *(x: float32): float32 {.magic: "UnaryPlusF64", noSideEffect.} +proc `-` *(x: float32): float32 {.magic: "UnaryMinusF64", noSideEffect.} +proc `+` *(x, y: float32): float32 {.magic: "AddF64", noSideEffect.} +proc `-` *(x, y: float32): float32 {.magic: "SubF64", noSideEffect.} +proc `*` *(x, y: float32): float32 {.magic: "MulF64", noSideEffect.} +proc `/` *(x, y: float32): float32 {.magic: "DivF64", noSideEffect.} + proc `+` *(x: float): float {.magic: "UnaryPlusF64", noSideEffect.} proc `-` *(x: float): float {.magic: "UnaryMinusF64", noSideEffect.} proc `+` *(x, y: float): float {.magic: "AddF64", noSideEffect.} @@ -646,6 +656,10 @@ proc `*` *(x, y: float): float {.magic: "MulF64", noSideEffect.} proc `/` *(x, y: float): float {.magic: "DivF64", noSideEffect.} ## computes the floating point division +proc `==` *(x, y: float32): bool {.magic: "EqF64", noSideEffect.} +proc `<=` *(x, y: float32): bool {.magic: "LeF64", noSideEffect.} +proc `<` *(x, y: float32): bool {.magic: "LtF64", noSideEffect.} + proc `==` *(x, y: float): bool {.magic: "EqF64", noSideEffect.} proc `<=` *(x, y: float): bool {.magic: "LeF64", noSideEffect.} proc `<` *(x, y: float): bool {.magic: "LtF64", noSideEffect.} @@ -1450,6 +1464,12 @@ proc isNil*[T: proc](x: T): bool {.noSideEffect, magic: "IsNil".} ## Fast check whether `x` is nil. This is sometimes more efficient than ## ``== nil``. +proc `==` *[I, T](x, y: array[I, T]): bool = + for f in low(x)..high(x): + if x[f] != y[f]: + return + result = true + proc `@`*[T](a: openArray[T]): seq[T] = ## turns an openarray into a sequence. This is not as efficient as turning ## a fixed length array into a sequence as it always copies every element @@ -1996,7 +2016,7 @@ when not defined(JS): #and not defined(NimrodVM): ## `content` completely to the file and closes the file afterwards. ## Raises an IO exception in case of an error. - proc write*(f: TFile, r: float) {.tags: [FWriteIO].} + proc write*(f: TFile, r: float32) {.tags: [FWriteIO].} proc write*(f: TFile, i: int) {.tags: [FWriteIO].} proc write*(f: TFile, i: biggestInt) {.tags: [FWriteIO].} proc write*(f: TFile, r: biggestFloat) {.tags: [FWriteIO].} diff --git a/lib/system/ansi_c.nim b/lib/system/ansi_c.nim index 06da34926..13e8496d2 100644 --- a/lib/system/ansi_c.nim +++ b/lib/system/ansi_c.nim @@ -58,7 +58,7 @@ else: proc c_longjmp(jmpb: C_JmpBuf, retval: cint) {. header: "<setjmp.h>", importc: "longjmp".} -proc c_setjmp(jmpb: var C_JmpBuf): cint {. +proc c_setjmp(jmpb: C_JmpBuf): cint {. header: "<setjmp.h>", importc: "setjmp".} proc c_signal(sig: cint, handler: proc (a: cint) {.noconv.}) {. diff --git a/lib/system/gc.nim b/lib/system/gc.nim index 36c008bef..f5b68b9db 100644 --- a/lib/system/gc.nim +++ b/lib/system/gc.nim @@ -759,7 +759,7 @@ else: # Used to traverse the stack and registers assuming # that 'setjmp' will save registers in the C stack. type PStackSlice = ptr array [0..7, pointer] - var registers: C_JmpBuf + var registers {.noinit.}: 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)) diff --git a/lib/system/gc_ms.nim b/lib/system/gc_ms.nim index 9ebc27a9f..2e3596985 100644 --- a/lib/system/gc_ms.nim +++ b/lib/system/gc_ms.nim @@ -449,7 +449,7 @@ else: # Used to traverse the stack and registers assuming # that 'setjmp' will save registers in the C stack. type PStackSlice = ptr array [0..7, pointer] - var registers: C_JmpBuf + var registers {.noinit.}: 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)) diff --git a/lib/system/sysio.nim b/lib/system/sysio.nim index 5407e0618..a877f8b28 100644 --- a/lib/system/sysio.nim +++ b/lib/system/sysio.nim @@ -91,7 +91,7 @@ proc write(f: TFile, i: biggestInt) = proc write(f: TFile, b: bool) = if b: write(f, "true") else: write(f, "false") -proc write(f: TFile, r: float) = fprintf(f, "%g", r) +proc write(f: TFile, r: float32) = fprintf(f, "%g", r) proc write(f: TFile, r: biggestFloat) = fprintf(f, "%g", r) proc write(f: TFile, c: Char) = putc(c, f) diff --git a/readme.md b/readme.md index 59546653b..f74f81283 100644 --- a/readme.md +++ b/readme.md @@ -6,8 +6,7 @@ documentation. Compiling the Nimrod compiler is quite straightforward. Because the Nimrod compiler itself is written in the Nimrod programming language the C source of an older version of the compiler are needed to bootstrap the -latest version. The C sources are however included with this repository under -the build directory. +latest version. The C sources are available in a separate repo [here](http://github.com/nimrod-code/csources). Pre-compiled snapshots of the compiler are also available on [Nimbuild](http://build.nimrod-code.org/). Your platform however may not @@ -53,7 +52,7 @@ and you can also get help in the IRC channel on [Freenode](irc://irc.freenode.net/nimrod) in #nimrod. ## License -The compiler and the standard library is licensed under the MIT license, +The compiler and the standard library are licensed under the MIT license, except for some modules where the documentation suggests otherwise. This means that you can use any license for your own programs developed with Nimrod, allowing you to create commercial applications. diff --git a/readme.txt b/readme.txt index 59546653b..f74f81283 100644 --- a/readme.txt +++ b/readme.txt @@ -6,8 +6,7 @@ documentation. Compiling the Nimrod compiler is quite straightforward. Because the Nimrod compiler itself is written in the Nimrod programming language the C source of an older version of the compiler are needed to bootstrap the -latest version. The C sources are however included with this repository under -the build directory. +latest version. The C sources are available in a separate repo [here](http://github.com/nimrod-code/csources). Pre-compiled snapshots of the compiler are also available on [Nimbuild](http://build.nimrod-code.org/). Your platform however may not @@ -53,7 +52,7 @@ and you can also get help in the IRC channel on [Freenode](irc://irc.freenode.net/nimrod) in #nimrod. ## License -The compiler and the standard library is licensed under the MIT license, +The compiler and the standard library are licensed under the MIT license, except for some modules where the documentation suggests otherwise. This means that you can use any license for your own programs developed with Nimrod, allowing you to create commercial applications. diff --git a/tests/compile/tinheritref.nim b/tests/compile/tinheritref.nim new file mode 100644 index 000000000..e5de6a4be --- /dev/null +++ b/tests/compile/tinheritref.nim @@ -0,0 +1,27 @@ +discard """ + output: "23" +""" + +# bug #554, #179 + +type T[E] = + ref object + elem: E + +var ob: T[int] + +ob = T[int](elem: 23) +echo ob.elem + +type + TTreeIteratorA* = ref object {.inheritable.} + + TKeysIteratorA* = ref object of TTreeIteratorA #compiles + + TTreeIterator* [T,D] = ref object {.inheritable.} + + TKeysIterator* [T,D] = ref object of TTreeIterator[T,D] #this not + +var + it: TKeysIterator[int, string] = nil + diff --git a/tests/compile/tobjconstr2.nim b/tests/compile/tobjconstr2.nim index ef5446999..cb47e146d 100644 --- a/tests/compile/tobjconstr2.nim +++ b/tests/compile/tobjconstr2.nim @@ -6,3 +6,17 @@ var s{.exportc.}: seq[TFoo] = @[] s.add TFoo(x: 42) echo s[0].x + + +# bug #563 +type + Foo = + object {.inheritable.} + x: int + + Bar = + object of Foo + y: int + +var a = Bar(y: 100, x: 200) # works +var b = Bar(x: 100, y: 200) # used to fail diff --git a/tests/compile/tvarious.nim b/tests/compile/tvarious.nim index e91de9245..25f48bb30 100644 --- a/tests/compile/tvarious.nim +++ b/tests/compile/tvarious.nim @@ -1,4 +1,7 @@ # Test various aspects + +# bug #572 +var a=12345678901'u64 var x = (x: 42, y: (a: 8, z: 10)) echo x.y diff --git a/tests/reject/texprstmt.nim b/tests/reject/texprstmt.nim new file mode 100644 index 000000000..b32394d8d --- /dev/null +++ b/tests/reject/texprstmt.nim @@ -0,0 +1,12 @@ +discard """ + line: 10 + errormsg: "value returned by statement has to be discarded" +""" + +# bug #578 + +proc test: string = + result = "blah" + result[1 .. -1] + +echo test() diff --git a/tests/reject/tinvalidborrow.nim b/tests/reject/tinvalidborrow.nim new file mode 100644 index 000000000..9ab9e8d64 --- /dev/null +++ b/tests/reject/tinvalidborrow.nim @@ -0,0 +1,17 @@ +discard """ + line: 11 + errormsg: "no symbol to borrow from found" +""" + +# bug #516 + +type + TAtom = culong + +proc `==`*(a, b: TAtom): bool {.borrow.} + +var + d, e: TAtom + +echo( $(d == e) ) + diff --git a/tests/run/tvarious1.nim b/tests/run/tvarious1.nim index d30d91234..9dd4af606 100644 --- a/tests/run/tvarious1.nim +++ b/tests/run/tvarious1.nim @@ -1,7 +1,8 @@ discard """ file: "tlenopenarray.nim" output: '''1 -0''' +0 +Whopie''' """ echo len([1_000_000]) #OUT 1 @@ -12,3 +13,17 @@ type proc `[]`(v: TVector; idx: int): int = TArray(v)[idx] var v: TVector echo v[2] + +# bug #569 + +import queues + +type + TWidget = object + names: TQueue[string] + +var w = TWidget(names: initQueue[string]()) + +add(w.names, "Whopie") + +for n in w.names: echo(n) diff --git a/todo.txt b/todo.txt index 6fce93290..732045a5e 100644 --- a/todo.txt +++ b/todo.txt @@ -22,6 +22,7 @@ version 0.9.4 Bugs ==== +- compilation of niminst takes way too long. looks like a regression - simple closure iterator doesn't work - docgen: sometimes effects are listed twice - 'result' is not properly cleaned for NRVO --> use uninit checking instead diff --git a/tools/niminst/buildbat.tmpl b/tools/niminst/buildbat.tmpl index 256f61b3d..f7ccbcbfa 100644 --- a/tools/niminst/buildbat.tmpl +++ b/tools/niminst/buildbat.tmpl @@ -1,11 +1,11 @@ #! stdtmpl(subsChar='?') | standard -#proc GenerateBuildBatchScript(c: TConfigData, target: TTarget): string = +#proc GenerateBuildBatchScript(c: TConfigData, winIndex, cpuIndex: int): string = # result = "@echo off\nREM Generated by niminst\n" SET CC=gcc SET LINKER=gcc SET COMP_FLAGS=?{c.ccompiler.flags} SET LINK_FLAGS=?{c.linker.flags} -SET BIN_DIR=?{firstBinPath(c)} +SET BIN_DIR=?{firstBinPath(c).slashify} if EXIST ..\koch.nim SET BIN_DIR=..\bin @@ -15,8 +15,8 @@ REM call the compiler: # block win32: # var linkCmd = "" -# for ff in items(c.cfiles[1][ord(target)]): -# let f = ff.replace('/', '\\') +# for ff in items(c.cfiles[winIndex][cpuIndex]): +# let f = ff.slashify ECHO %CC% %COMP_FLAGS% -Isrc -c ?{f} -o ?{changeFileExt(f, "o")} %CC% %COMP_FLAGS% -Isrc -c ?{f} -o ?{changeFileExt(f, "o")} # linkCmd.add(" " & changeFileExt(f, "o")) diff --git a/tools/niminst/buildsh.tmpl b/tools/niminst/buildsh.tmpl index 1ce182b63..244d73710 100644 --- a/tools/niminst/buildsh.tmpl +++ b/tools/niminst/buildsh.tmpl @@ -35,7 +35,7 @@ LINK_FLAGS="?{c.linker.flags}" ucpu=`uname -m` uos=`uname` # add(result, "# bin dir detection\n") -binDir=?{firstBinPath(c)} +binDir=?{firstBinPath(c).slashify} if [ -s ../koch.nim ]; then binDir="../bin" @@ -126,7 +126,8 @@ case $myos in # for cpuA in 1..c.cpus.len: ?{c.cpus[cpuA-1]}) # var linkCmd = "" -# for f in items(c.cfiles[osA][cpuA]): +# for ff in items(c.cfiles[osA][cpuA]): +# let f = ff.slashify echo "$CC $COMP_FLAGS -Isrc -c ?{f} -o ?{changeFileExt(f, "o")}" $CC $COMP_FLAGS -Isrc -c ?{f} -o ?{changeFileExt(f, "o")} # add(linkCmd, " \\\n" & changeFileExt(f, "o")) diff --git a/tools/niminst/deinstall.tmpl b/tools/niminst/deinstall.tmpl index 61141f78e..c961ac07a 100644 --- a/tools/niminst/deinstall.tmpl +++ b/tools/niminst/deinstall.tmpl @@ -39,10 +39,12 @@ if [ $# -eq 1 ] ; then esac echo "removing files..." -#for f in items(c.cat[fcUnixBin]): +#for ff in items(c.cat[fcUnixBin]): + #let f = ff.slashify rm -f $bindir/?f.skipRoot #end for -#for f in items(c.cat[fcConfig]): +#for ff in items(c.cat[fcConfig]): + #let f = ff.slashify rm -f $configdir/?f.skipRoot #end for rm -rf $docdir diff --git a/tools/niminst/inno.tmpl b/tools/niminst/inno.tmpl index df1460e42..0219dea1f 100644 --- a/tools/niminst/inno.tmpl +++ b/tools/niminst/inno.tmpl @@ -21,7 +21,7 @@ Name: english; MessagesFile: compiler:Default.isl [Files] #for i in low(TFileCategory)..fcWindows: # for f in items(c.cat[i]): -Source: ${expandFilename(f)}; DestDir: {app}\${splitFile(f).dir}; Flags: ignoreversion +Source: ${expandFilename(f).slashify}; DestDir: {app}\${splitFile(f).dir.slashify}; Flags: ignoreversion # end for #end for @@ -32,7 +32,7 @@ Name: {group}\Console for $c.displayName; Filename: {cmd} Name: {group}\$c.displayName; Filename: {app}\${c.name}.exe #end if #for f in items(c.cat[fcDocStart]): -Name: {group}\Documentation; Filename: {app}\$f +Name: {group}\Documentation; Filename: {app}\${f.slashify} #end for Name: {group}\{cm:UninstallProgram,$c.displayName}; Filename: {uninstallexe} @@ -65,7 +65,7 @@ begin setArrayLength(result, $c.binPaths.len); #var i = 0 #for b in items(c.binPaths): - result[$i] := ExpandConstant('{app}') + '\$b'; + result[$i] := ExpandConstant('{app}') + '\${b.slashify}'; #inc(i) #end for end; diff --git a/tools/niminst/install.tmpl b/tools/niminst/install.tmpl index bfba024c7..3eb07b8af 100644 --- a/tools/niminst/install.tmpl +++ b/tools/niminst/install.tmpl @@ -7,7 +7,7 @@ set -e if [ $# -eq 1 ] ; then # if c.cat[fcUnixBin].len > 0: - if test -f ?{c.cat[fcUnixBin][0]} + if test -f ?{c.cat[fcUnixBin][0].slashify} then echo "?c.displayName build detected" else @@ -65,35 +65,35 @@ if [ $# -eq 1 ] ; then # mk = unixDirVars[cat] & "/" & mk # if not createdDirs.hasKey(mk): # createdDirs[mk] = "true" - mkdir -p ?mk + mkdir -p ?{mk.slashify} # end if # end if # end for #end for #for f in items(c.cat[fcUnixBin]): - cp ?f $bindir/?f.skipRoot - chmod 755 $bindir/?f.skipRoot + cp ?f.slashify $bindir/?f.skipRoot.slashify + chmod 755 $bindir/?f.skipRoot.slashify #end for #for f in items(c.cat[fcConfig]): - cp ?f $configdir/?f.skipRoot - chmod 644 $configdir/?f.skipRoot + cp ?f.slashify $configdir/?f.skipRoot.slashify + chmod 644 $configdir/?f.skipRoot.slashify #end for #for f in items(c.cat[fcData]): - if [ -f ?f ]; then - cp ?f $datadir/?f.skipRoot - chmod 644 $datadir/?f.skipRoot + if [ -f ?f.slashify ]; then + cp ?f.slashify $datadir/?f.skipRoot.slashify + chmod 644 $datadir/?f.skipRoot.slashify fi #end for #for f in items(c.cat[fcDoc]): - if [ -f ?f ]; then - cp ?f $docdir/?f.skipRoot - chmod 644 $docdir/?f.skipRoot + if [ -f ?f.slashify ]; then + cp ?f.slashify $docdir/?f.skipRoot.slashify + chmod 644 $docdir/?f.skipRoot.slashify fi #end for #for f in items(c.cat[fcLib]): - cp ?f $libdir/?f.skipRoot - chmod 644 $libdir/?f.skipRoot + cp ?f.slashify $libdir/?f.skipRoot.slashify + chmod 644 $libdir/?f.skipRoot.slashify #end for echo "installation successful" diff --git a/tools/niminst/niminst.nim b/tools/niminst/niminst.nim index faad2fb15..4da734d13 100644 --- a/tools/niminst/niminst.nim +++ b/tools/niminst/niminst.nim @@ -27,7 +27,6 @@ const type TAppType = enum appConsole, appGUI - TTarget = enum tWin32 = 1, tWin64 = 2 TAction = enum actionNone, # action not yet known actionCSource # action: create C sources @@ -103,6 +102,9 @@ proc firstBinPath(c: TConfigData): string = proc `\`(a, b: string): string = result = if a.len == 0: b else: a & '\\' & b +proc slashify(s: string): string = + when defined(unix): s.replace('\\', '/') else: s.replace('/', '\\') + proc skipRoot(f: string): string = # "abc/def/xyz" --> "def/xyz" var i = 0 @@ -382,33 +384,41 @@ proc removeDuplicateFiles(c: var TConfigData) = for cpuB in 1..c.cpus.len: if osB != osA or cpuB != cpuA: var orig = buildDir(osB, cpuB) / f - if ExistsFile(orig) and ExistsFile(dup) and + if existsFile(orig) and existsFile(dup) and sameFileContent(orig, dup): # file is identical, so delete duplicate: - RemoveFile(dup) + removeFile(dup) c.cfiles[osA][cpuA][i] = orig proc writeInstallScripts(c: var TConfigData) = if c.installScript: - writeFile(installShFile, GenerateInstallScript(c), "\10") + writeFile(installShFile, generateInstallScript(c), "\10") if c.uninstallScript: - writeFile(deinstallShFile, GenerateDeinstallScript(c), "\10") + writeFile(deinstallShFile, generateDeinstallScript(c), "\10") proc srcdist(c: var TConfigData) = if not existsDir(getOutputDir(c) / "src"): createDir(getOutputDir(c) / "src") for x in walkFiles(c.libpath / "lib/*.h"): echo(getOutputDir(c) / "src" / extractFilename(x)) - CopyFile(dest=getOutputDir(c) / "src" / extractFilename(x), source=x) + copyFile(dest=getOutputDir(c) / "src" / extractFilename(x), source=x) + var winIndex = -1 + var intel32Index = -1 + var intel64Index = -1 for osA in 1..c.oses.len: + let osname = c.oses[osA-1] + if osname.cmpIgnoreStyle("windows") == 0: winIndex = osA-1 for cpuA in 1..c.cpus.len: + let cpuname = c.cpus[cpuA-1] + if cpuname.cmpIgnoreStyle("i386") == 0: intel32Index = cpuA-1 + elif cpuname.cmpIgnoreStyle("amd64") == 0: intel64Index = cpuA-1 var dir = getOutputDir(c) / buildDir(osA, cpuA) if existsDir(dir): removeDir(dir) createDir(dir) var cmd = ("nimrod compile -f --symbolfiles:off --compileonly " & "--gen_mapping --cc:gcc --skipUserCfg" & " --os:$# --cpu:$# $# $#") % - [c.oses[osA-1], c.cpus[cpuA-1], c.nimrodArgs, + [osname, cpuname, c.nimrodArgs, changeFileExt(c.infile, "nim")] echo(cmd) if execShellCmd(cmd) != 0: @@ -417,18 +427,23 @@ proc srcdist(c: var TConfigData) = for i in 0 .. c.cfiles[osA][cpuA].len-1: let dest = dir / extractFilename(c.cfiles[osA][cpuA][i]) let relDest = buildDir(osA, cpuA) / extractFilename(c.cfiles[osA][cpuA][i]) - CopyFile(dest=dest, source=c.cfiles[osA][cpuA][i]) + copyFile(dest=dest, source=c.cfiles[osA][cpuA][i]) c.cfiles[osA][cpuA][i] = relDest # second pass: remove duplicate files removeDuplicateFiles(c) - writeFile(getOutputDir(c) / buildShFile, GenerateBuildShellScript(c), "\10") - writeFile(getOutputDir(c) / buildBatFile32, GenerateBuildBatchScript(c, tWin32), "\13\10") - writeFile(getOutputDir(c) / buildBatFile64, GenerateBuildBatchScript(c, tWin64), "\13\10") + writeFile(getOutputDir(c) / buildShFile, generateBuildShellScript(c), "\10") + if winIndex >= 0: + if intel32Index >= 0: + writeFile(getOutputDir(c) / buildBatFile32, + generateBuildBatchScript(c, winIndex, intel32Index), "\13\10") + if intel64Index >= 0: + writeFile(getOutputDir(c) / buildBatFile64, + generateBuildBatchScript(c, winIndex, intel64Index), "\13\10") writeInstallScripts(c) # --------------------- generate inno setup ----------------------------------- proc setupDist(c: var TConfigData) = - var scrpt = GenerateInnoSetup(c) + var scrpt = generateInnoSetup(c) var n = "build" / "install_$#_$#.iss" % [toLower(c.name), c.version] writeFile(n, scrpt, "\13\10") when defined(windows): @@ -437,7 +452,7 @@ proc setupDist(c: var TConfigData) = var outcmd = if c.outdir.len == 0: "build" else: c.outdir var cmd = "$# $# /O$# $#" % [quoteIfContainsWhite(c.innoSetup.path), c.innoSetup.flags, outcmd, n] - Echo(cmd) + echo(cmd) if execShellCmd(cmd) == 0: removeFile(n) else: |