diff options
29 files changed, 433 insertions, 114 deletions
diff --git a/compiler/ccgexprs.nim b/compiler/ccgexprs.nim index eda0d23bc..e346e1b53 100644 --- a/compiler/ccgexprs.nim +++ b/compiler/ccgexprs.nim @@ -212,22 +212,36 @@ proc optAsgnLoc(a: TLoc, t: PType, field: PRope): TLoc = result.heapRoot = a.heapRoot proc genOptAsgnTuple(p: BProc, dest, src: TLoc, flags: TAssignmentFlags) = + let newflags = + if src.k == locData: + flags + { needToCopy } + elif tfShallow in dest.t.flags: + flags - { needToCopy } + else: + flags for i in 0 .. <dest.t.len: let t = dest.t.sons[i] let field = ropef("Field$1", i.toRope) genAssignment(p, optAsgnLoc(dest, t, field), - optAsgnLoc(src, t, field), flags) + optAsgnLoc(src, t, field), newflags) proc genOptAsgnObject(p: BProc, dest, src: TLoc, flags: TAssignmentFlags, t: PNode) = if t == nil: return + let newflags = + if src.k == locData: + flags + { needToCopy } + elif tfShallow in dest.t.flags: + flags - { needToCopy } + else: + flags case t.kind of nkSym: let field = t.sym genAssignment(p, optAsgnLoc(dest, field.typ, field.loc.r), - optAsgnLoc(src, field.typ, field.loc.r), flags) + optAsgnLoc(src, field.typ, field.loc.r), newflags) of nkRecList: - for child in items(t): genOptAsgnObject(p, dest, src, flags, child) + for child in items(t): genOptAsgnObject(p, dest, src, newflags, child) else: discard proc genGenericAsgn(p: BProc, dest, src: TLoc, flags: TAssignmentFlags) = @@ -264,13 +278,13 @@ proc genAssignment(p: BProc, dest, src: TLoc, flags: TAssignmentFlags) = of tyRef: genRefAssign(p, dest, src, flags) of tySequence: - if needToCopy notin flags: + if needToCopy notin flags and src.k != locData: genRefAssign(p, dest, src, flags) else: linefmt(p, cpsStmts, "#genericSeqAssign($1, $2, $3);$n", addrLoc(dest), rdLoc(src), genTypeInfo(p.module, dest.t)) of tyString: - if needToCopy notin flags: + if needToCopy notin flags and src.k != locData: genRefAssign(p, dest, src, flags) else: if dest.s == OnStack or not usesNativeGC(): @@ -355,6 +369,22 @@ proc putLocIntoDest(p: BProc, d: var TLoc, s: TLoc) = else: d = s # ``d`` is free, so fill it with ``s`` +proc putDataIntoDest(p: BProc, d: var TLoc, t: PType, r: PRope) = + var a: TLoc + if d.k != locNone: + # need to generate an assignment here + initLoc(a, locData, getUniqueType(t), OnUnknown) + a.r = r + if lfNoDeepCopy in d.flags: genAssignment(p, d, a, {}) + else: genAssignment(p, d, a, {needToCopy}) + else: + # we cannot call initLoc() here as that would overwrite + # the flags field! + d.k = locData + d.t = getUniqueType(t) + d.r = r + d.a = -1 + proc putIntoDest(p: BProc, d: var TLoc, t: PType, r: PRope) = var a: TLoc if d.k != locNone: @@ -1788,7 +1818,7 @@ proc exprComplexConst(p: BProc, n: PNode, d: var TLoc) = if d.k == locNone: fillLoc(d, locData, t, tmp, OnHeap) else: - putIntoDest(p, d, t, tmp) + putDataIntoDest(p, d, t, tmp) proc expr(p: BProc, n: PNode, d: var TLoc) = case n.kind @@ -1842,7 +1872,9 @@ proc expr(p: BProc, n: PNode, d: var TLoc) = of nkNilLit: if not isEmptyType(n.typ): putIntoDest(p, d, n.typ, genLiteral(p, n)) - of nkStrLit..nkTripleStrLit, nkIntLit..nkUInt64Lit, + of nkStrLit..nkTripleStrLit: + putDataIntoDest(p, d, n.typ, genLiteral(p, n)) + of nkIntLit..nkUInt64Lit, nkFloatLit..nkFloat128Lit, nkCharLit: putIntoDest(p, d, n.typ, genLiteral(p, n)) of nkCall, nkHiddenCallConv, nkInfix, nkPrefix, nkPostfix, nkCommand, diff --git a/compiler/jsgen.nim b/compiler/jsgen.nim index a34d8f123..373a11e9a 100644 --- a/compiler/jsgen.nim +++ b/compiler/jsgen.nim @@ -564,7 +564,7 @@ proc genTry(p: PProc, n: PNode, r: var TCompRes) = moveInto(p, a, r) var i = 1 if p.target == targetJS and length > 1 and n.sons[i].kind == nkExceptBranch: - appf(p.body, "} catch (EXC) {$n") + appf(p.body, "} catch (EXC) {$n lastJSError = EXC;$n") elif p.target == targetLua: appf(p.body, "end)$n") while i < length and n.sons[i].kind == nkExceptBranch: @@ -1114,6 +1114,8 @@ proc createVar(p: PProc, typ: PType, indirect: bool): PRope = var e = elemType(t) if length > 32: useMagic(p, "arrayConstr") + # XXX: arrayConstr depends on nimCopy. This line shouldn't be necessary. + useMagic(p, "nimCopy") result = ropef("arrayConstr($1, $2, $3)", [toRope(length), createVar(p, e, false), genTypeInfo(p, e)]) else: @@ -1314,7 +1316,7 @@ proc genMagic(p: PProc, n: PNode, r: var TCompRes) = if skipTypes(n.sons[1].typ, abstractVarRange).kind == tyCString: binaryExpr(p, n, r, "", "$1 += $2") else: - binaryExpr(p, n, r, "", "$1 = ($1.slice(0,-1)).concat($2)") + binaryExpr(p, n, r, "", "$1 = ($1.slice(0, -1)).concat($2)") # XXX: make a copy of $2, because of Javascript's sucking semantics of mAppendSeqElem: binaryExpr(p, n, r, "", "$1.push($2)") of mConStrStr: genConStrStr(p, n, r) @@ -1341,7 +1343,7 @@ proc genMagic(p: PProc, n: PNode, r: var TCompRes) = of ast.mDec: if optOverflowCheck notin p.options: binaryExpr(p, n, r, "", "$1 -= $2") else: binaryExpr(p, n, r, "subInt", "$1 = subInt($1, $2)") - of mSetLengthStr: binaryExpr(p, n, r, "", "$1.length = ($2)-1") + of mSetLengthStr: binaryExpr(p, n, r, "", "$1.length = $2+1; $1[$1.length-1] = 0") of mSetLengthSeq: binaryExpr(p, n, r, "", "$1.length = $2") of mCard: unaryExpr(p, n, r, "SetCard", "SetCard($1)") of mLtSet: binaryExpr(p, n, r, "SetLt", "SetLt($1, $2)") @@ -1698,7 +1700,12 @@ proc myClose(b: PPassContext, n: PNode): PNode = var m = BModule(b) if sfMainModule in m.module.flags: let code = wholeCode(m) - var outfile = changeFileExt(completeCFilePath(m.module.filename), "js") + let outfile = + if options.outFile.len > 0: + if options.outFile.isAbsolute: options.outFile + else: getCurrentDir() / options.outFile + else: + changeFileExt(completeCFilePath(m.module.filename), "js") discard writeRopeIfNotEqual(con(genHeader(), code), outfile) proc myOpenCached(s: PSym, rd: PRodReader): PPassContext = diff --git a/compiler/nimrod.nim b/compiler/nimrod.nim index 38d440ade..efe3d83bf 100644 --- a/compiler/nimrod.nim +++ b/compiler/nimrod.nim @@ -61,8 +61,12 @@ proc handleCmdLine() = tccgen.run() if optRun in gGlobalOptions: if gCmd == cmdCompileToJS: - var ex = quoteShell( - completeCFilePath(changeFileExt(gProjectFull, "js").prependCurDir)) + var ex: string + if options.outFile.len > 0: + ex = options.outFile.prependCurDir.quoteShell + else: + ex = quoteShell( + completeCFilePath(changeFileExt(gProjectFull, "js").prependCurDir)) execExternalProgram("node " & ex & ' ' & service.arguments) else: var binPath: string diff --git a/doc/lib.txt b/doc/lib.txt index 62efe6a5d..a209357f7 100644 --- a/doc/lib.txt +++ b/doc/lib.txt @@ -16,10 +16,11 @@ Pure libraries do not depend on any external ``*.dll`` or ``lib*.so`` binary while impure libraries do. A wrapper is an impure library that is a very low-level interface to a C library. -Read this `document <apis.html>`_ for a quick overview of the API design. If -you can't find here some functionality you are looking for you could try using -the 3rd party `package manager Babel <https://github.com/nimrod-code/babel>`_ -and its list of packages. +Read this `document <apis.html>`_ for a quick overview of the API design. + +The `bottom <#babel>`_ of this page includes a list of 3rd party packages +created by the Nimrod community. These packages are a useful addition to the +modules in the standard library. Pure libraries @@ -573,3 +574,35 @@ Scientific computing * `libsvm <libsvm.html>`_ Low level wrapper for `lib svm <http://www.csie.ntu.edu.tw/~cjlin/libsvm/>`_. + +Babel +==================== + +Babel is a package manager for the Nimrod programming language. +For instructions on how to install Babel packages see +`its README <https://github.com/nimrod-code/babel#readme>`_. + +Official packages +----------------- + +These packages are officially supported and will therefore be continually +maintained to ensure that they work with the latest versions of the Nimrod +compiler. + +.. raw:: html + + <div id="officialPkgList"></div> + +Unofficial packages +------------------- + +These packages have been developed by independent Nimrod developers and as +such may not always be up to date with the latest developments in the +Nimrod programming language. + +.. raw:: html + + <div id="unofficialPkgList"></div> + + <script type="text/javascript" src="babelpkglist.js"></script> + <script type="text/javascript" src="http://build.nimrod-lang.org/packages?callback=gotPackageList"></script> diff --git a/doc/manual.txt b/doc/manual.txt index 298f6329b..775679788 100644 --- a/doc/manual.txt +++ b/doc/manual.txt @@ -1390,7 +1390,7 @@ In order to make generic code easier tor write ``ptr T`` is a subtype of ``ptr[R, T]`` for any ``R``. Furthermore the subtype relation of the region object types is lifted to -the pointer types: If ``A <: B`` then ``ptr[T, A] <: ptr[T, B]``. This can be +the pointer types: If ``A <: B`` then ``ptr[A, T] <: ptr[B, T]``. This can be used to model subregions of memory. As a special typing rule ``ptr[R, T]`` is not compatible to ``pointer`` to prevent the following from compiling: diff --git a/koch.nim b/koch.nim index ce01d36a5..d7da56590 100644 --- a/koch.nim +++ b/koch.nim @@ -51,7 +51,6 @@ Boot options: -d:tinyc include the Tiny C backend (not supported on Windows) -d:useGnuReadline use the GNU readline library for interactive mode (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 """ @@ -170,7 +169,7 @@ const ".idx" ] ignore = [ - ".bzrignore", "nimrod", "nimrod.exe", "koch", "koch.exe" + ".bzrignore", "nimrod", "nimrod.exe", "koch", "koch.exe", ".gitignore" ] proc cleanAux(dir: string) = diff --git a/lib/pure/asyncdispatch.nim b/lib/pure/asyncdispatch.nim index 880458ee5..5e638dc74 100644 --- a/lib/pure/asyncdispatch.nim +++ b/lib/pure/asyncdispatch.nim @@ -353,11 +353,11 @@ when defined(windows) or defined(nimdoc): var retFuture = newFuture[string]() var dataBuf: TWSABuf - dataBuf.buf = newString(size) + dataBuf.buf = cast[cstring](alloc0(size)) dataBuf.len = size var bytesReceived: DWord - var flagsio = flags.dword + var flagsio = flags.DWord var ol = cast[PCustomOverlapped](alloc0(sizeof(TCustomOverlapped))) ol.data = TCompletionData(sock: socket, cb: proc (sock: TAsyncFD, bytesCount: DWord, errcode: TOSErrorCode) = @@ -367,10 +367,12 @@ when defined(windows) or defined(nimdoc): retFuture.complete("") else: var data = newString(bytesCount) + assert bytesCount <= size copyMem(addr data[0], addr dataBuf.buf[0], bytesCount) retFuture.complete($data) else: retFuture.fail(newException(EOS, osErrorMsg(errcode))) + dealloc dataBuf.buf ) let ret = WSARecv(socket.TSocketHandle, addr dataBuf, 1, addr bytesReceived, @@ -378,8 +380,9 @@ when defined(windows) or defined(nimdoc): if ret == -1: let err = osLastError() if err.int32 != ERROR_IO_PENDING: - retFuture.fail(newException(EOS, osErrorMsg(err))) + dealloc dataBuf.buf dealloc(ol) + retFuture.fail(newException(EOS, 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 @@ -401,7 +404,9 @@ when defined(windows) or defined(nimdoc): else: bytesReceived var data = newString(realSize) + assert realSize <= size copyMem(addr data[0], addr dataBuf.buf[0], realSize) + #dealloc dataBuf.buf retFuture.complete($data) # We don't deallocate ``ol`` here because even though this completed # immediately poll will still be notified about its completion and it will @@ -415,7 +420,7 @@ when defined(windows) or defined(nimdoc): var retFuture = newFuture[void]() var dataBuf: TWSABuf - dataBuf.buf = data + dataBuf.buf = data # since this is not used in a callback, this is fine dataBuf.len = data.len var bytesReceived, flags: DWord diff --git a/lib/pure/asynchttpserver.nim b/lib/pure/asynchttpserver.nim index 74b044e05..2ebd7036d 100644 --- a/lib/pure/asynchttpserver.nim +++ b/lib/pure/asynchttpserver.nim @@ -92,8 +92,11 @@ proc processClient(client: PAsyncSocket, address: string, # GET /path HTTP/1.1 # Header: val # \n - var request = newRequest() + request.hostname = address + assert client != nil + request.client = client + # First line - GET /path HTTP/1.1 let line = await client.recvLine() # TODO: Timeouts. if line == "": @@ -102,6 +105,8 @@ proc processClient(client: PAsyncSocket, address: string, let lineParts = line.split(' ') if lineParts.len != 3: request.respond(Http400, "Invalid request. Got: " & line) + client.close() + return let reqMethod = lineParts[0] let path = lineParts[1] @@ -127,15 +132,11 @@ proc processClient(client: PAsyncSocket, address: string, except EInvalidValue: request.respond(Http400, "Invalid request protocol. Got: " & protocol) return - request.hostname = address - request.client = client case reqMethod.normalize - of "get": + of "get", "post", "head", "put", "delete", "trace", "options", "connect", "patch": await callback(request) else: - echo(reqMethod.repr) - echo(line.repr) request.respond(Http400, "Invalid request method. Got: " & reqMethod) # Persistent connections diff --git a/lib/pure/asyncnet.nim b/lib/pure/asyncnet.nim index daa6c8839..b1abf627b 100644 --- a/lib/pure/asyncnet.nim +++ b/lib/pure/asyncnet.nim @@ -80,18 +80,53 @@ proc connect*(socket: PAsyncSocket, address: string, port: TPort, ## or an error occurs. result = connect(socket.fd.TAsyncFD, address, port, af) +proc readIntoBuf(socket: PAsyncSocket, flags: int): PFuture[int] {.async.} = + var data = await recv(socket.fd.TAsyncFD, BufferSize, flags) + if data.len != 0: + copyMem(addr socket.buffer[0], addr data[0], data.len) + socket.bufLen = data.len + socket.currPos = 0 + result = data.len + proc recv*(socket: PAsyncSocket, size: int, - flags: int = 0): PFuture[string] = + flags: int = 0): PFuture[string] {.async.} = ## Reads ``size`` bytes from ``socket``. Returned future will complete once ## all of the requested data is read. If socket is disconnected during the ## recv operation then the future may complete with only a part of the ## requested data read. If socket is disconnected and no data is available ## to be read then the future will complete with a value of ``""``. - result = recv(socket.fd.TAsyncFD, size, flags) + if socket.isBuffered: + result = newString(size) + + template returnNow(readBytes: int) = + result.setLen(readBytes) + # Only increase buffer position when not peeking. + if (flags and MSG_PEEK) != MSG_PEEK: + socket.currPos.inc(readBytes) + return + + if socket.bufLen == 0: + let res = await socket.readIntoBuf(flags and (not MSG_PEEK)) + if res == 0: returnNow(0) + + var read = 0 + while read < size: + if socket.currPos >= socket.bufLen: + let res = await socket.readIntoBuf(flags and (not MSG_PEEK)) + if res == 0: returnNow(read) + + let chunk = min(socket.bufLen-socket.currPos, size-read) + copyMem(addr(result[read]), addr(socket.buffer[socket.currPos+read]), chunk) + read.inc(chunk) + + returnNow(read) + else: + result = await recv(socket.fd.TAsyncFD, size, flags) proc send*(socket: PAsyncSocket, data: string): PFuture[void] = ## Sends ``data`` to ``socket``. The returned future will complete once all ## data has been sent. + assert socket != nil result = send(socket.fd.TAsyncFD, data) proc acceptAddr*(socket: PAsyncSocket): diff --git a/lib/pure/collections/sets.nim b/lib/pure/collections/sets.nim index ad3fe7218..bc249ed63 100644 --- a/lib/pure/collections/sets.nim +++ b/lib/pure/collections/sets.nim @@ -68,6 +68,15 @@ template rawInsertImpl() {.dirty.} = proc rawGet[A](s: TSet[A], key: A): int = rawGetImpl() +proc mget*[A](s: var TSet[A], key: A): var A = + ## returns the element that is actually stored in 's' which has the same + ## value as 'key' or raises the ``EInvalidKey`` exception. This is useful + ## when one overloaded 'hash' and '==' but still needs reference semantics + ## for sharing. + var index = rawGet(s, key) + if index >= 0: result = t.data[index].key + else: raise newException(EInvalidKey, "key not found: " & $key) + proc contains*[A](s: TSet[A], key: A): bool = ## returns true iff `key` is in `s`. var index = rawGet(s, key) diff --git a/lib/pure/collections/tables.nim b/lib/pure/collections/tables.nim index 40ae57b5a..412bebeee 100644 --- a/lib/pure/collections/tables.nim +++ b/lib/pure/collections/tables.nim @@ -191,7 +191,13 @@ proc `$`*[A, B](t: TTable[A, B]): string = dollarImpl() proc `==`*[A, B](s, t: TTable[A, B]): bool = - s.counter == t.counter and s.data == t.data + if s.counter == t.counter: + # different insertion orders mean different 'data' seqs, so we have + # to use the slow route here: + for key, val in s: + if not hasKey(t, key): return false + if mget(t, key) != val: return false + return true proc indexBy*[A, B, C](collection: A, index: proc(x: B): C): TTable[C, B] = ## Index the collection with the proc provided. diff --git a/lib/pure/hashes.nim b/lib/pure/hashes.nim index ee05ad7e2..5784a96c1 100644 --- a/lib/pure/hashes.nim +++ b/lib/pure/hashes.nim @@ -129,3 +129,7 @@ proc hash*(x: float): THash {.inline.} = proc hash*[A](x: openArray[A]): THash = for it in items(x): result = result !& hash(it) result = !$result + +proc hash*[A](x: set[A]): THash = + for it in items(x): result = result !& hash(it) + result = !$result diff --git a/lib/pure/json.nim b/lib/pure/json.nim index 7424bbae9..4250847e5 100644 --- a/lib/pure/json.nim +++ b/lib/pure/json.nim @@ -861,26 +861,97 @@ proc parseJson(p: var TJsonParser): PJsonNode = of tkError, tkCurlyRi, tkBracketRi, tkColon, tkComma, tkEof: raiseParseErr(p, "{") -proc parseJson*(s: PStream, filename: string): PJsonNode = - ## Parses from a stream `s` into a `PJsonNode`. `filename` is only needed - ## for nice error messages. - var p: TJsonParser - p.open(s, filename) - discard getTok(p) # read first token - result = p.parseJson() - p.close() - -proc parseJson*(buffer: string): PJsonNode = - ## Parses JSON from `buffer`. - result = parseJson(newStringStream(buffer), "input") - -proc parseFile*(filename: string): PJsonNode = - ## Parses `file` into a `PJsonNode`. - var stream = newFileStream(filename, fmRead) - if stream == nil: - raise newException(EIO, "cannot read from file: " & filename) - result = parseJson(stream, filename) - +when not defined(js): + proc parseJson*(s: PStream, filename: string): PJsonNode = + ## Parses from a stream `s` into a `PJsonNode`. `filename` is only needed + ## for nice error messages. + var p: TJsonParser + p.open(s, filename) + discard getTok(p) # read first token + result = p.parseJson() + p.close() + + proc parseJson*(buffer: string): PJsonNode = + ## Parses JSON from `buffer`. + result = parseJson(newStringStream(buffer), "input") + + proc parseFile*(filename: string): PJsonNode = + ## Parses `file` into a `PJsonNode`. + var stream = newFileStream(filename, fmRead) + if stream == nil: + raise newException(EIO, "cannot read from file: " & filename) + result = parseJson(stream, filename) +else: + from math import `mod` + type + TJSObject = object + proc parseNativeJson(x: cstring): TJSObject {.importc: "JSON.parse".} + + proc getVarType(x): TJsonNodeKind = + result = JNull + proc getProtoName(y): cstring + {.importc: "Object.prototype.toString.call".} + case $getProtoName(x) # TODO: Implicit returns fail here. + of "[object Array]": return JArray + of "[object Object]": return JObject + of "[object Number]": + if cast[float](x) mod 1.0 == 0: + return JInt + else: + return JFloat + of "[object Boolean]": return JBool + of "[object Null]": return JNull + of "[object String]": return JString + else: assert false + + proc len(x: TJSObject): int = + assert x.getVarType == JArray + asm """ + return `x`.length; + """ + + proc `[]`(x: TJSObject, y: string): TJSObject = + assert x.getVarType == JObject + asm """ + return `x`[`y`]; + """ + + proc `[]`(x: TJSObject, y: int): TJSObject = + assert x.getVarType == JArray + asm """ + return `x`[`y`]; + """ + + proc convertObject(x: TJSObject): PJsonNode = + case getVarType(x) + of JArray: + result = newJArray() + for i in 0 .. <x.len: + result.add(x[i].convertObject()) + of JObject: + result = newJObject() + asm """for (property in `x`) { + if (`x`.hasOwnProperty(property)) { + """ + var nimProperty: cstring + var nimValue: TJSObject + asm "`nimProperty` = property; `nimValue` = `x`[property];" + result[$nimProperty] = nimValue.convertObject() + asm "}}" + of JInt: + result = newJInt(cast[int](x)) + of JFloat: + result = newJFloat(cast[float](x)) + of JString: + result = newJString($cast[cstring](x)) + of JBool: + result = newJBool(cast[bool](x)) + of JNull: + result = newJNull() + + proc parseJson*(buffer: string): PJsonNode = + return parseNativeJson(buffer).convertObject() + when false: import os var s = newFileStream(ParamStr(1), fmRead) diff --git a/lib/pure/math.nim b/lib/pure/math.nim index d258e9a7c..3997b059f 100644 --- a/lib/pure/math.nim +++ b/lib/pure/math.nim @@ -19,8 +19,8 @@ when defined(Posix) and not defined(haiku): {.passl: "-lm".} - -import times +when not defined(js): + import times const PI* = 3.1415926535897932384626433 ## the circle constant PI (Ludolph's number) diff --git a/lib/pure/streams.nim b/lib/pure/streams.nim index e944dd2fc..3b6dc87a5 100644 --- a/lib/pure/streams.nim +++ b/lib/pure/streams.nim @@ -239,45 +239,47 @@ proc newStringStream*(s: string = ""): PStringStream = result.readDataImpl = ssReadData result.writeDataImpl = ssWriteData -type - PFileStream* = ref TFileStream ## a stream that encapsulates a `TFile` - TFileStream* = object of TStream - f: TFile - -proc fsClose(s: PStream) = - if PFileStream(s).f != nil: - close(PFileStream(s).f) - PFileStream(s).f = nil -proc fsFlush(s: PStream) = flushFile(PFileStream(s).f) -proc fsAtEnd(s: PStream): bool = return endOfFile(PFileStream(s).f) -proc fsSetPosition(s: PStream, pos: int) = setFilePos(PFileStream(s).f, pos) -proc fsGetPosition(s: PStream): int = return int(getFilePos(PFileStream(s).f)) - -proc fsReadData(s: PStream, buffer: pointer, bufLen: int): int = - result = readBuffer(PFileStream(s).f, buffer, bufLen) - -proc fsWriteData(s: PStream, buffer: pointer, bufLen: int) = - if writeBuffer(PFileStream(s).f, buffer, bufLen) != bufLen: - raise newEIO("cannot write to stream") - -proc newFileStream*(f: TFile): PFileStream = - ## creates a new stream from the file `f`. - new(result) - result.f = f - result.closeImpl = fsClose - result.atEndImpl = fsAtEnd - result.setPositionImpl = fsSetPosition - result.getPositionImpl = fsGetPosition - result.readDataImpl = fsReadData - result.writeDataImpl = fsWriteData - result.flushImpl = fsFlush - -proc newFileStream*(filename: string, mode: TFileMode): PFileStream = - ## creates a new stream from the file named `filename` with the mode `mode`. - ## If the file cannot be opened, nil is returned. See the `system - ## <system.html>`_ module for a list of available TFileMode enums. - var f: TFile - if open(f, filename, mode): result = newFileStream(f) +when not defined(js): + + type + PFileStream* = ref TFileStream ## a stream that encapsulates a `TFile` + TFileStream* = object of TStream + f: TFile + + proc fsClose(s: PStream) = + if PFileStream(s).f != nil: + close(PFileStream(s).f) + PFileStream(s).f = nil + proc fsFlush(s: PStream) = flushFile(PFileStream(s).f) + proc fsAtEnd(s: PStream): bool = return endOfFile(PFileStream(s).f) + proc fsSetPosition(s: PStream, pos: int) = setFilePos(PFileStream(s).f, pos) + proc fsGetPosition(s: PStream): int = return int(getFilePos(PFileStream(s).f)) + + proc fsReadData(s: PStream, buffer: pointer, bufLen: int): int = + result = readBuffer(PFileStream(s).f, buffer, bufLen) + + proc fsWriteData(s: PStream, buffer: pointer, bufLen: int) = + if writeBuffer(PFileStream(s).f, buffer, bufLen) != bufLen: + raise newEIO("cannot write to stream") + + proc newFileStream*(f: TFile): PFileStream = + ## creates a new stream from the file `f`. + new(result) + result.f = f + result.closeImpl = fsClose + result.atEndImpl = fsAtEnd + result.setPositionImpl = fsSetPosition + result.getPositionImpl = fsGetPosition + result.readDataImpl = fsReadData + result.writeDataImpl = fsWriteData + result.flushImpl = fsFlush + + proc newFileStream*(filename: string, mode: TFileMode): PFileStream = + ## creates a new stream from the file named `filename` with the mode `mode`. + ## If the file cannot be opened, nil is returned. See the `system + ## <system.html>`_ module for a list of available TFileMode enums. + var f: TFile + if open(f, filename, mode): result = newFileStream(f) when true: diff --git a/lib/pure/strutils.nim b/lib/pure/strutils.nim index b63224cec..bd6814dcc 100644 --- a/lib/pure/strutils.nim +++ b/lib/pure/strutils.nim @@ -264,6 +264,19 @@ iterator split*(s: string, sep: char): string = yield substr(s, first, last-1) inc(last) +iterator split*(s: string, sep: string): string = + ## Splits the string `s` into substrings using a string separator. + ## + ## Substrings are separated by the string `sep`. + var last = 0 + if len(s) > 0: + while last <= len(s): + var first = last + while last < len(s) and s.substr(last, last + <sep.len) != sep: + inc(last) + yield substr(s, first, last-1) + inc(last, sep.len) + iterator splitLines*(s: string): string = ## Splits the string `s` into its containing lines. Every newline ## combination (CR, LF, CR-LF) is supported. The result strings contain @@ -329,6 +342,13 @@ proc split*(s: string, sep: char): seq[string] {.noSideEffect, ## of substrings. accumulateResult(split(s, sep)) +proc split*(s: string, sep: string): seq[string] {.noSideEffect, + rtl, extern: "nsuSplitString".} = + ## Splits the string `s` into substrings using a string separator. + ## + ## Substrings are separated by the string `sep`. + accumulateResult(split(s, sep)) + proc toHex*(x: BiggestInt, len: int): string {.noSideEffect, rtl, extern: "nsuToHex".} = ## Converts `x` to its hexadecimal representation. The resulting string diff --git a/lib/system/jssys.nim b/lib/system/jssys.nim index 1720804c4..52f8873cf 100644 --- a/lib/system/jssys.nim +++ b/lib/system/jssys.nim @@ -12,7 +12,7 @@ when defined(nodejs): else: proc alert*(s: cstring) {.importc, nodecl.} -proc log*(s: cstring) {.importc: "console.log", nodecl.} +proc log*(s: cstring) {.importc: "console.log", varargs, nodecl.} type PSafePoint = ptr TSafePoint @@ -27,11 +27,19 @@ type line: int # current line number filename: cstring + PJSError = ref object + columnNumber {.importc.}: int + fileName {.importc.}: cstring + lineNumber {.importc.}: int + message {.importc.}: cstring + stack {.importc.}: cstring + var framePtr {.importc, nodecl, volatile.}: PCallFrame excHandler {.importc, nodecl, volatile.}: PSafePoint = nil # list of exception handlers # a global variable for the root of all try blocks + lastJSError {.importc, nodecl, volatile.}: PJSError = nil {.push stacktrace: off, profiler:off.} proc nimBoolToStr(x: bool): string {.compilerproc.} = @@ -43,8 +51,12 @@ proc nimCharToStr(x: char): string {.compilerproc.} = result[0] = x proc getCurrentExceptionMsg*(): string = - if excHandler != nil: return $excHandler.exc.msg - return "" + if excHandler != nil and excHandler.exc != nil: + return $excHandler.exc.msg + elif lastJSError != nil: + return $lastJSError.message + else: + return "" proc auxWriteStackTrace(f: PCallFrame): string = type @@ -77,11 +89,13 @@ proc auxWriteStackTrace(f: PCallFrame): string = add(result, "\n") proc rawWriteStackTrace(): string = - if framePtr == nil: - result = "No stack traceback available\n" - else: - result = "Traceback (most recent call last)\n"& auxWriteStackTrace(framePtr) + if framePtr != nil: + result = "Traceback (most recent call last)\n" & auxWriteStackTrace(framePtr) framePtr = nil + elif lastJSError != nil: + result = $lastJSError.stack + else: + result = "No stack traceback available\n" proc raiseException(e: ref E_Base, ename: cstring) {. compilerproc, asmNoStackFrame.} = @@ -472,17 +486,17 @@ proc ze*(a: int): int {.compilerproc.} = proc ze64*(a: int64): int64 {.compilerproc.} = result = a -proc ToU8(a: int): int8 {.asmNoStackFrame, compilerproc.} = +proc toU8*(a: int): int8 {.asmNoStackFrame, compilerproc.} = asm """ return `a`; """ -proc ToU16(a: int): int16 {.asmNoStackFrame, compilerproc.} = +proc toU16*(a: int): int16 {.asmNoStackFrame, compilerproc.} = asm """ return `a`; """ -proc ToU32(a: int): int32 {.asmNoStackFrame, compilerproc.} = +proc toU32*(a: int64): int32 {.asmNoStackFrame, compilerproc.} = asm """ return `a`; """ @@ -503,17 +517,17 @@ proc nimCopy(x: pointer, ti: PNimType): pointer {.compilerproc.} proc nimCopyAux(dest, src: Pointer, n: ptr TNimNode) {.compilerproc.} = case n.kind - of nkNone: sysAssert(false, "NimCopyAux") + of nkNone: sysAssert(false, "nimCopyAux") of nkSlot: - asm "`dest`[`n`.offset] = NimCopy(`src`[`n`.offset], `n`.typ);" + asm "`dest`[`n`.offset] = nimCopy(`src`[`n`.offset], `n`.typ);" of nkList: for i in 0..n.len-1: - NimCopyAux(dest, src, n.sons[i]) + nimCopyAux(dest, src, n.sons[i]) of nkCase: asm """ - `dest`[`n`.offset] = NimCopy(`src`[`n`.offset], `n`.typ); + `dest`[`n`.offset] = nimCopy(`src`[`n`.offset], `n`.typ); for (var i = 0; i < `n`.sons.length; ++i) { - NimCopyAux(`dest`, `src`, `n`.sons[i][1]); + nimCopyAux(`dest`, `src`, `n`.sons[i][1]); } """ @@ -534,17 +548,17 @@ proc nimCopy(x: pointer, ti: PNimType): pointer = for (var key in `x`) { `result`[key] = `x`[key]; } """ of tyTuple, tyObject: - if ti.base != nil: result = NimCopy(x, ti.base) + if ti.base != nil: result = nimCopy(x, ti.base) elif ti.kind == tyObject: asm "`result` = {m_type: `ti`};" else: asm "`result` = {};" - NimCopyAux(result, x, ti.node) + nimCopyAux(result, x, ti.node) of tySequence, tyArrayConstr, tyOpenArray, tyArray: asm """ `result` = new Array(`x`.length); for (var i = 0; i < `x`.length; ++i) { - `result`[i] = NimCopy(`x`[i], `ti`.base); + `result`[i] = nimCopy(`x`[i], `ti`.base); } """ of tyString: @@ -584,12 +598,12 @@ proc genericReset(x: Pointer, ti: PNimType): pointer {.compilerproc.} = else: result = nil -proc ArrayConstr(len: int, value: pointer, typ: PNimType): pointer {. +proc arrayConstr(len: int, value: pointer, typ: PNimType): pointer {. asmNoStackFrame, compilerproc.} = # types are fake asm """ var result = new Array(`len`); - for (var i = 0; i < `len`; ++i) result[i] = NimCopy(`value`, `typ`); + for (var i = 0; i < `len`; ++i) result[i] = nimCopy(`value`, `typ`); return result; """ diff --git a/tools/nimweb.nim b/tools/nimweb.nim index 5c78f3f45..9a83a5cca 100644 --- a/tools/nimweb.nim +++ b/tools/nimweb.nim @@ -348,6 +348,10 @@ proc buildNewsRss(c: var TConfigData, destPath: string) = generateRss(destFilename, parseNewsTitles(srcFilename)) +proc buildJS(destPath: string) = + exec("nimrod js -d:release --out:$1 web/babelpkglist.nim" % + [destPath / "babelpkglist.js"]) + proc main(c: var TConfigData) = const cmd = "nimrod rst2html --compileonly $1 -o:web/$2.temp web/$2.txt" @@ -377,6 +381,7 @@ proc main(c: var TConfigData) = quit("[Error] cannot write file: " & outfile) removeFile(temp) copyDir("web/assets", "web/upload/assets") + buildJS("web/upload") buildNewsRss(c, "web/upload") buildAddDoc(c, "web/upload") buildDocSamples(c, "web/upload") diff --git a/web/assets/images/link_aporia.png b/web/assets/images/link_aporia.png index 6256792d7..145e5ddf2 100644 --- a/web/assets/images/link_aporia.png +++ b/web/assets/images/link_aporia.png Binary files differdiff --git a/web/assets/images/link_forum.png b/web/assets/images/link_forum.png index d153231a7..2973b42bc 100644 --- a/web/assets/images/link_forum.png +++ b/web/assets/images/link_forum.png Binary files differdiff --git a/web/assets/images/link_nimbuild.png b/web/assets/images/link_nimbuild.png index ad94f9c82..4b3f943fe 100644 --- a/web/assets/images/link_nimbuild.png +++ b/web/assets/images/link_nimbuild.png Binary files differdiff --git a/web/assets/images/logo.png b/web/assets/images/logo.png index f6b95bf05..31ee0a6e1 100644 --- a/web/assets/images/logo.png +++ b/web/assets/images/logo.png Binary files differdiff --git a/web/assets/images/quote.png b/web/assets/images/quote.png index 52d529284..e9426158c 100644 --- a/web/assets/images/quote.png +++ b/web/assets/images/quote.png Binary files differdiff --git a/web/assets/images/sidebar.png b/web/assets/images/sidebar.png index 8488f8acf..77624480e 100644 --- a/web/assets/images/sidebar.png +++ b/web/assets/images/sidebar.png Binary files differdiff --git a/web/assets/images/sidebar_h2.png b/web/assets/images/sidebar_h2.png index 5de3da291..d1409b57f 100644 --- a/web/assets/images/sidebar_h2.png +++ b/web/assets/images/sidebar_h2.png Binary files differdiff --git a/web/assets/images/sidebar_head.png b/web/assets/images/sidebar_head.png index 734d5709b..05885d9f3 100644 --- a/web/assets/images/sidebar_head.png +++ b/web/assets/images/sidebar_head.png Binary files differdiff --git a/web/assets/images/site_foot.png b/web/assets/images/site_foot.png index d94632b20..a2efa0460 100644 --- a/web/assets/images/site_foot.png +++ b/web/assets/images/site_foot.png Binary files differdiff --git a/web/assets/images/site_neck.png b/web/assets/images/site_neck.png index cab3dc75a..d4f42c6b7 100644 --- a/web/assets/images/site_neck.png +++ b/web/assets/images/site_neck.png Binary files differdiff --git a/web/babelpkglist.nim b/web/babelpkglist.nim new file mode 100644 index 000000000..378d4ce30 --- /dev/null +++ b/web/babelpkglist.nim @@ -0,0 +1,72 @@ +import base64, strutils, json, htmlgen, dom, algorithm + +type + TData = object + content {.importc.}: cstring + +proc decodeContent(content: string): string = + result = "" + for line in content.splitLines: + if line != "": + result.add decode(line) + +proc contains(x: seq[PJSonNode], s: string): bool = + for i in x: + assert i.kind == JString + if i.str == s: return true + +proc processContent(content: string) = + var jsonDoc = parseJson(content) + assert jsonDoc.kind == JArray + var jsonArr = jsonDoc.elems + + jsonArr.sort do (x, y: PJsonNode) -> int: + system.cmp(x["name"].str, y["name"].str) + + var + officialList = "" + officialCount = 0 + unofficialList = "" + unofficialCount = 0 + + for pkg in jsonArr: + assert pkg.kind == JObject + let pkgWeb = + if pkg.hasKey("web"): pkg["web"].str + else: pkg["url"].str + let listItem = li(a(href=pkgWeb, pkg["name"].str), " ", pkg["description"].str) + if pkg["url"].str.startsWith("git://github.com/nimrod-code") or + "official" in pkg["tags"].elems: + officialCount.inc + officialList.add listItem & "\n" + else: + unofficialCount.inc + unofficialList.add listItem & "\n" + + var officialPkgListDiv = document.getElementById("officialPkgList") + + officialPkgListDiv.innerHTML.add( + p("There are currently " & $officialCount & + " official packages in the Babel package repository.") & + ul(officialList) + ) + + var unofficialPkgListDiv = document.getElementById("unofficialPkgList") + + unofficialPkgListDiv.innerHTML.add( + p("There are currently " & $unofficialCount & + " unofficial packages in the Babel package repository.") & + ul(unofficialList) + ) + +proc gotPackageList(apiReply: TData) {.exportc.} = + let decoded = decodeContent($apiReply.content) + try: + processContent(decoded) + except: + var officialPkgListDiv = document.getElementById("officialPkgList") + var unofficialPkgListDiv = document.getElementById("unofficialPkgList") + let msg = p("Unable to retrieve package list: ", + code(getCurrentExceptionMsg())) + officialPkgListDiv.innerHTML = msg + unofficialPkgListDiv.innerHTML = msg |