diff --git a/compiler/jsgen.nim b/compiler/jsgen.nim index a34d8f1239..373a11e9ac 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 38d440adeb..efe3d83bfe 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 62efe6a5d5..a209357f7c 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 `_ 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 `_ -and its list of packages. +Read this `document `_ 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 `_ Low level wrapper for `lib svm `_. + +Babel +==================== + +Babel is a package manager for the Nimrod programming language. +For instructions on how to install Babel packages see +`its 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 + +
+ +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 + +
+ + + diff --git a/lib/pure/json.nim b/lib/pure/json.nim index 7424bbae95..4250847e56 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() +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 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 .. `_ module for a list of available TFileMode enums. - var f: TFile - if open(f, filename, mode): result = newFileStream(f) + 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 + ## `_ 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 b63224cec4..bd6814dcca 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 + 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