summary refs log tree commit diff stats
diff options
context:
space:
mode:
-rw-r--r--compiler/ccgexprs.nim46
-rw-r--r--compiler/jsgen.nim15
-rw-r--r--compiler/nimrod.nim8
-rw-r--r--doc/lib.txt41
-rw-r--r--doc/manual.txt2
-rw-r--r--koch.nim3
-rw-r--r--lib/pure/asyncdispatch.nim13
-rw-r--r--lib/pure/asynchttpserver.nim13
-rw-r--r--lib/pure/asyncnet.nim39
-rw-r--r--lib/pure/collections/sets.nim9
-rw-r--r--lib/pure/collections/tables.nim8
-rw-r--r--lib/pure/hashes.nim4
-rw-r--r--lib/pure/json.nim111
-rw-r--r--lib/pure/math.nim4
-rw-r--r--lib/pure/streams.nim80
-rw-r--r--lib/pure/strutils.nim20
-rw-r--r--lib/system/jssys.nim54
-rw-r--r--tools/nimweb.nim5
-rw-r--r--web/assets/images/link_aporia.pngbin1585 -> 1526 bytes
-rw-r--r--web/assets/images/link_forum.pngbin1107 -> 1048 bytes
-rw-r--r--web/assets/images/link_nimbuild.pngbin955 -> 896 bytes
-rw-r--r--web/assets/images/logo.pngbin125152 -> 101053 bytes
-rw-r--r--web/assets/images/quote.pngbin1062 -> 1045 bytes
-rw-r--r--web/assets/images/sidebar.pngbin1029 -> 971 bytes
-rw-r--r--web/assets/images/sidebar_h2.pngbin2104 -> 2044 bytes
-rw-r--r--web/assets/images/sidebar_head.pngbin39194 -> 34993 bytes
-rw-r--r--web/assets/images/site_foot.pngbin4039 -> 3109 bytes
-rw-r--r--web/assets/images/site_neck.pngbin391 -> 317 bytes
-rw-r--r--web/babelpkglist.nim72
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