==== News ==== 2016-XX-XX Version 0.13.1 released ================================== Changes affecting backwards compatibility ----------------------------------------- - ``--out`` and ``--nimcache`` command line arguments are now relative to current directory. Previously they were relative to project directory. - The json module now stores the name/value pairs in objects internally as a hash table of type ``fields*: Table[string, JsonNode]`` instead of a sequence. This means that order is no longer preserved. When using the ``table.mpairs`` iterator only the returned values can be modified, no longer the keys. - The deprecated Nim shebang notation ``#!`` was removed from the language. Use ``#?`` instead. - The ``using`` statement now means something completely different. You can use the new experimental ``this`` pragma to achieve a similar effect to what the old ``using`` statement tried to achieve. - Typeless parameters have been removed from the language since it would clash with ``using``. Library Additions ----------------- - The rlocks module has been added providing reentrant lock synchronization primitive. Compiler Additions ------------------ - Added a new ``--noCppExceptions`` switch that allows to use default exception handling (no ``throw`` or ``try``/``catch`` generated) when compiling to C++ code. Language Additions ------------------ - Nim now supports a ``.this`` pragma for more notational convenience. - Nim now supports a different ``using`` statement for more convenience. - Nim now supports ``partial`` object declarations to mitigate the problems that arise when types are mutually dependent and yet should be kept in different modules. 2016-01-27 Nim in Action is now available! ========================================== .. raw::html New in Manning Early Access Program: Nim in Action! We are proud to announce that *Nim in Action*, a book about the Nim programming language, is now available! The book is available at this URL: `https://www.manning.com/books/nim-in-action `_ The first three chapters are available for download as an eBook through Manning's Early Access program. You can download a free sample of the book containing the first chapter as well! *Nim in Action* is currently being written and is expected to be completed by Summer 2016. If you purchase the eBook you will start receiving new chapters as they become available. You can also purchase the printed book together with the eBook for a slightly higher price. If you do read the book, even if it's just the first chapter, then please share any comments, suggestions and questions on the `Nim forum `_ or in Manning's own `Author Online forum! `_ 2016-01-18 Version 0.13.0 released ================================== Once again we are proud to announce the latest release of the Nim compiler and related tools. This release comes just 3 months after the last release! A new version of Nimble which depends on this release, has also been released. See `this `_ forum thread for more information about the Nimble release. This release of Nim includes over 116 bug fixes, many of which are related to closures. The lambda lifting algorithm in the compiler has been completely rewritten, and some changes have been made to the semantics of closures in Nim as a result. These changes may affect backwards compatibility and are all described in the section below. With this release, we are one step closer to Nim version 1.0. The 1.0 release will be a big milestone for Nim, because after that version is released there will be no more breaking changes made to the language or the standard library. That being said, the next release will likely be Nim 0.14. It will focus on improvements to the GC and concurrency. We will in particular be looking at ways to add multi-core support to async await. Standard library improvements are also on our roadmap but may not make it for Nim 0.14. As always you can download the latest version of Nim from the `download `_ page. Happy coding! Changes affecting backwards compatibility ----------------------------------------- - ``macros.newLit`` for ``bool`` now produces false/true symbols which actually work with the bool datatype. - When compiling to JS: ``Node``, ``NodeType`` and ``Document`` are no longer defined. Use the types defined in ``dom.nim`` instead. - The check ``x is iterator`` (used for instance in concepts) was always a weird special case (you could not use ``x is proc``) and was removed from the language. - Top level routines cannot have the calling convention ``closure`` anymore. - The ``redis`` module has been moved out of the standard library. It can now be installed via Nimble and is located here: https://github.com/nim-lang/redis - ``math.RunningStat`` and its associated procs have been moved from the ``math`` module to a new ``stats`` module. Syntax changes ~~~~~~~~~~~~~~ The parser now considers leading whitespace in front of operators to determine if an operator is used in prefix or infix position. This means that finally ``echo $foo`` is parsed as people expect, which is as ``echo($foo)``. It used to be parsed as ``(echo) $ (foo)``. ``echo $ foo`` continues to be parsed as ``(echo) $ (foo)``. This also means that ``-1`` is always parsed as prefix operator so code like ``0..kArraySize div 2 -1`` needs to be changed to ``0..kArraySize div 2 - 1``. This release also adds multi-line comments to Nim. The syntax for them is: ``#[ comment here ]#``. For more details read the section of the `manual `_. Iterator changes ~~~~~~~~~~~~~~~~ Implicit return type inference for iterators has been removed from the language. The following used to work: .. code-block:: nim iterator it = yield 7 This was a strange special case and has been removed. Now you need to write it like so which is consistent with procs: .. code-block:: nim iterator it: auto = yield 7 Closure changes ~~~~~~~~~~~~~~~ The semantics of closures changed: Capturing variables that are in loops do not produce a new environment. Nim closures behave like JavaScript closures now. The following used to work as the environment creation used to be attached to the loop body: .. code-block:: nim proc outer = var s: seq[proc(): int {.closure.}] = @[] for i in 0 ..< 30: let ii = i s.add(proc(): int = return ii*ii) This behaviour has changed in 0.13.0 and now needs to be written as: .. code-block:: nim proc outer = var s: seq[proc(): int {.closure.}] = @[] for i in 0 ..< 30: (proc () = let ii = i s.add(proc(): int = return ii*ii))() The reason is that environment creations are now only performed once per proc call. This change is subtle and unfortunate, but: 1. Affects almost no code out there. 2. Is easier to implement and we are at a point in Nim's development process where simple+stable wins over perfect-in-theory+unstable-in-practice. 3. Implies programmers are more in control of where memory is allocated which is beneficial for a systems programming language. Bugfixes -------- The list below has been generated based on the commits in Nim's git repository. As such it lists only the issues which have been closed via a commit, for a full list see `this link on Github `_. - Fixed "Generic arguments cannot be used in templates (raising undeclared identifier)" (`#3498 `_) - Fixed "multimethods: Error: internal error: cgmeth.genConv" (`#3550 `_) - Fixed "nimscript - SIGSEGV in except block" (`#3546 `_) - Fixed "Bool literals in macros do not work." (`#3541 `_) - Fixed "Docs: nativesocket.html - 404" (`#3582 `_) - Fixed ""not nil" return types never trigger an error or warning" (`#2285 `_) - Fixed "No warning or error is raised even if not nil is specified " (`#3222 `_) - Fixed "Incorrect fsmonitor add() filter logic" (`#3611 `_) - Fixed ""nimble install nimsuggest" failed" (`#3622 `_) - Fixed "compile time `excl ` cause SIGSEGV" (`#3639 `_) - Fixed "Unable to echo unsigned ints at compile-time" (`#2514 `_) - Fixed "Nested closure iterator produces internal error" (`#1725 `_) - Fixed "C Error on walkDirRec closure" (`#3636 `_) - Fixed "Error in generated c code" (`#3201 `_) - Fixed "C Compile-time error with generic proc type." (`#2659 `_) - Fixed "ICE dereferencing array pointer" (`#2240 `_) - Fixed "Lambda lifting crash" (`#2007 `_) - Fixed "Can't reference outer variables from a closure in an iterator" (`#2604 `_) - Fixed "M&S collector breaks with nested for loops." (`#603 `_) - Fixed "Regression: bad C codegen" (`#3723 `_) - Fixed "JS backend - handle bool type in case statement" (`#3722 `_) - Fixed "linenoise compilation with cpp" (`#3720 `_) - Fixed "(???,???) duplicate case label" (`#3665 `_) - Fixed "linenoise compilation with cpp" (`#3720 `_) - Fixed "Update list of backward incompatibilities for Nim 0.12.0 in the main site" (`#3689 `_) - Fixed "Can't compile nimble with latest devel - codegen bug" (`#3730 `_) 2016-01-18 Andreas Rumpf's talk at OSCON Amsterdam ================================================== In case you have missed it, here is Andreas' Nim: An Overview talk at OSCON Amsterdam. .. raw:: html 2015-10-27 Version 0.12.0 released ================================== The Nim community of developers is proud to announce the new version of the Nim compiler. This has been a long time coming as the last release has been made over 5 months ago! This release includes some changes which affect backwards compatibility, one major change is that now the hash table ``[]`` operators now raise a ``KeyError`` exception when the key does not exist. Some of the more exciting new features include: the ability to unpack tuples in any assignment context, the introduction of `NimScript `_, and improvements to the type inference of lambdas. There are of course many many many bug fixes included with this release. We are getting closer and closer to a 1.0 release and are hoping that only a few 0.x releases will be necessary before we are happy to release version 1.0. As always you can download the latest version of Nim from the `download `_ page. For a more detailed list of changes look below. Some of the upcoming breaking changes are also documented in this forum `thread `_. Changes affecting backwards compatibility ----------------------------------------- - The regular expression modules, ``re`` and ``nre`` now depend on version 8.36 of PCRE. If you have an older version you may see a message similar to ``could not import: pcre_free_study`` output when you start your program. See `this issue `_ for more information. - ``tables.[]``, ``strtabs.[]``, ``critbits.[]`` **now raise** the ``KeyError`` **exception when the key does not exist**! Use the new ``getOrDefault`` instead to get the old behaviour. Compile all your code with ``-d:nimTableGet`` to get a listing of where your code uses ``[]``! - The ``rawsockets`` module has been renamed to ``nativesockets`` to avoid confusion with TCP/IP raw sockets, so ``newNativeSocket`` should be used instead of ``newRawSocket``. - The ``miliseconds`` property of ``times.TimeInterval`` is now ``milliseconds``. Code accessing that property is deprecated and code using ``miliseconds`` during object initialization or as a named parameter of ``initInterval()`` will need to be updated. - ``std.logging`` functions no longer do formatting and semantically treat their arguments just like ``echo`` does. Affected functions: ``log``, ``debug``, ``info``, ``warn``, ``error``, ``fatal``. Custom subtypes of ``Logger`` also need to be adjusted accordingly. - Floating point numbers can now look like ``2d`` (float64) and ``2f`` (float32) which means imports like ``import scene/2d/sprite`` do not work anymore. Instead quotes have to be used: ``import "scene/2d/sprite"``. The former code never was valid Nim. - The Windows API wrapper (``windows.nim``) is now not part of the official distribution anymore. Instead use the ``oldwinapi`` Nimble package. - There is now a clear distinction between ``--os:standalone`` and ``--gc:none``. So if you use ``--os:standalone`` ensure you also use ``--gc:none``. ``--os:standalone`` without ``--gc:none`` is now a version that doesn't depend on any OS but includes the GC. However this version is currently untested! - All procedures which construct a ``Socket``/``AsyncSocket`` now need to specify the socket domain, type and protocol. The param name ``typ: SockType`` (in ``newSocket``/``newAsyncSocket`` procs) was also renamed to ``sockType``. The param ``af`` in the ``connect`` procs was removed. This affects ``asyncnet``, ``asyncdispatch``, ``net``, and ``rawsockets``. - ``varargs[typed]`` and ``varargs[untyped]`` have been refined and now work as expected. However ``varargs[untyped]`` is not an alias anymore for ``varargs[expr]``. So if your code breaks for ``varargs[untyped]``, use ``varargs[expr]`` instead. The same applies to ``varargs[typed]`` vs ``varargs[stmt]``. - ``sequtils.delete`` doesn't take confusing default arguments anymore. - ``system.free`` was an error-prone alias to ``system.dealloc`` and has been removed. - ``macros.high`` never worked and the manual says ``high`` cannot be overloaded, so we removed it with no deprecation cycle. - To use the ``parallel`` statement you now have to use the ``--experimental`` mode. - Toplevel procs of calling convention ``closure`` never worked reliably and are now deprecated and will be removed from the language. Instead you have to insert type conversions like ``(proc (a, b: int) {.closure.})(myToplevelProc)`` if necessary. - The modules ``libffi``, ``sdl``, ``windows``, ``zipfiles``, ``libzip``, ``zlib``, ``zzip``, ``dialogs``, ``expat``, ``graphics``, ``libcurl``, ``sphinx`` have been moved out of the stdlib and are Nimble packages now. - The constant fights between 32 and 64 bit DLLs on Windows have been put to an end: The standard distribution now ships with 32 and 64 bit versions of all the DLLs the standard library needs. This means that the following DLLs are now split into 32 and 64 versions: * ``pcre.dll``: Split into ``pcre32.dll`` and ``pcre64.dll``. * ``pdcurses.dll``: Split into ``pdcurses32.dll`` and ``pdcurses64.dll``. * ``sqlite3.dll``: Split into ``sqlite3_32.dll`` and ``sqlite3_64.dll``. * ``ssleay32.dll``: Split into ``ssleay32.dll`` and ``ssleay64.dll``. * ``libeay32.dll``: Split into ``libeay32.dll`` and ``libeay64.dll``. Compile with ``-d:nimOldDLLs`` to make the stdlib use the old DLL names. - Nim VM now treats objects as ``nkObjConstr`` nodes, and not ``nkPar`` nodes as it was previousl
#
#
#            Nim's Runtime Library
#        (c) Copyright 2015 Andreas Rumpf
#
#    See the file "copying.txt", included in this
#    distribution, for details about the copyright.
#

## This module contains procs for `serialization`:idx: and `deserialization`:idx:
## of arbitrary Nim data structures. The serialization format uses `JSON`:idx:.
##
## **Restriction**: For objects their type is **not** serialized. This means
## essentially that it does not work if the object has some other runtime
## type than its compiletime type.
##
##
## Basic usage
## ===========
##
## .. code-block:: nim
##
##   type
##     A = object of RootObj
##     B = object of A
##       f: int
##
##   var
##     a: ref A
##     b: ref B
##
##   new(b)
##   a = b
##   echo($$a[]) # produces "{}", not "{f: 0}"
##
##   # unmarshal
##   let c = to[B]("""{"f": 2}""")
##   assert typeof(c) is B
##   assert c.f == 2
##
##   # marshal
##   let s = $$c
##   assert s == """{"f": 2}"""
##
## **Note**: The ``to`` and ``$$`` operations are available at compile-time!
##
##
## See also
## ========
## * `streams module <streams.html>`_
## * `json module <json.html>`_


import streams, typeinfo, json, intsets, tables, unicode

proc ptrToInt(x: pointer): int {.inline.} =
  result = cast[int](x) # don't skip alignment

proc storeAny(s: Stream, a: Any, stored: var IntSet) =
  case a.kind
  of akNone: assert false
  of akBool: s.write($getBool(a))
  of akChar:
    let ch = getChar(a)
    if ch < '\128':
      s.write(escapeJson($ch))
    else:
      s.write($int(ch))
  of akArray, akSequence:
    s.write("[")
    for i in 0 .. a.len-1:
      if i > 0: s.write(", ")
      storeAny(s, a[i], stored)
    s.write("]")
  of akObject, akTuple:
    s.write("{")
    var i = 0
    for key, val in fields(a):
      if i > 0: s.write(", ")
      s.write(escapeJson(key))
      s.write(": ")
      storeAny(s, val, stored)
      inc(i)
    s.write("}")
  of akSet:
    s.write("[")
    var i = 0
    for e in elements(a):
      if i > 0: s.write(", ")
      s.write($e)
      inc(i)
    s.write("]")
  of akRange: storeAny(s, skipRange(a), stored)
  of akEnum: s.write(getEnumField(a).escapeJson)
  of akPtr, akRef:
    var x = a.getPointer
    if isNil(x): s.write("null")
    elif stored.containsOrIncl(x.ptrToInt):
      # already stored, so we simply write out the pointer as an int:
      s.write($x.ptrToInt)
    else:
      # else as a [value, key] pair:
      # (reversed order for convenient x[0] access!)
      s.write("[")
      s.write($x.ptrToInt)
      s.write(", ")
      storeAny(s, a[], stored)
      s.write("]")
  of akProc, akPointer, akCString: s.write($a.getPointer.ptrToInt)
  of akString:
    var x = getString(a)
    if x.validateUtf8() == -1: s.write(escapeJson(x))
    else:
      s.write("[")
      var i = 0
      for c in x:
        if i > 0: s.write(", ")
        s.write($ord(c))
        inc(i)
      s.write("]")
  of akInt..akInt64, akUInt..akUInt64: s.write($getBiggestInt(a))
  of akFloat..akFloat128: s.write($getBiggestFloat(a))

proc loadAny(p: var JsonParser, a: Any, t: var Table[BiggestInt, pointer]) =
  case a.kind
  of akNone: assert false
  of akBool:
    case p.kind
    of jsonFalse: setBiggestInt(a, 0)
    of jsonTrue: setBiggestInt(a, 1)
    else: raiseParseErr(p, "'true' or 'false' expected for a bool")
    next(p)
  of akChar:
    if p.kind == jsonString:
      var x = p.str
      if x.len == 1:
        setBiggestInt(a, ord(x[0]))
        next(p)
        return
    elif p.kind == jsonInt:
      setBiggestInt(a, getInt(p))
      next(p)
      return
    raiseParseErr(p, "string of length 1 expected for a char")
  of akEnum:
    if p.kind == jsonString:
      setBiggestInt(a, getEnumOrdinal(a, p.str))
      next(p)
      return
    raiseParseErr(p, "string expected for an enum")
  of akArray:
    if p.kind != jsonArrayStart: raiseParseErr(p, "'[' expected for an array")
    next(p)
    var i = 0
    while p.kind != jsonArrayEnd and p.kind != jsonEof:
      loadAny(p, a[i], t)
      inc(i)
    if p.kind == jsonArrayEnd: next(p)
    else: raiseParseErr(p, "']' end of array expected")
  of akSequence:
    case p.kind
    of jsonNull:
      setPointer(a, nil)
      next(p)
    of jsonArrayStart:
      next(p)
      invokeNewSeq(a, 0)
      var i = 0
      while p.kind != jsonArrayEnd and p.kind != jsonEof:
        extendSeq(a)
        loadAny(p, a[i], t)
        inc(i)
      if p.kind == jsonArrayEnd: next(p)
      else: raiseParseErr(p, "")
    else:
      raiseParseErr(p, "'[' expected for a seq")
  of akObject, akTuple:
    if a.kind == akObject: setObjectRuntimeType(a)
    if p.kind != jsonObjectStart: raiseParseErr(p, "'{' expected for an object")
    next(p)
    while p.kind != jsonObjectEnd and p.kind != jsonEof:
      if p.kind != jsonString:
        raiseParseErr(p, "string expected for a field name")
      var fieldName = p.str
      next(p)
      loadAny(p, a[fieldName], t)
    if p.kind == jsonObjectEnd: next(p)
    else: raiseParseErr(p, "'}' end of object expected")
  of akSet:
    if p.kind != jsonArrayStart: raiseParseErr(p, "'[' expected for a set")
    next(p)
    while p.kind != jsonArrayEnd and p.kind != jsonEof:
      if p.kind != jsonInt: raiseParseErr(p, "int expected for a set")
      inclSetElement(a, p.getInt.int)
      next(p)
    if p.kind == jsonArrayEnd: next(p)
    else: raiseParseErr(p, "']' end of array expected")
  of akPtr, akRef:
    case p.kind
    of jsonNull:
      setPointer(a, nil)
      next(p)
    of jsonInt:
      setPointer(a, t.getOrDefault(p.getInt))
      next(p)
    of jsonArrayStart:
      next(p)
      if a.kind == akRef: invokeNew(a)
      else: setPointer(a, alloc0(a.baseTypeSize))
      if p.kind == jsonInt:
        t[p.getInt] = getPointer(a)
        next(p)
      else: raiseParseErr(p, "index for ref type expected")
      loadAny(p, a[], t)
      if p.kind == jsonArrayEnd: next(p)
      else: raiseParseErr(p, "']' end of ref-address pair expected")
    else: raiseParseErr(p, "int for pointer type expected")
  of akProc, akPointer, akCString:
    case p.kind
    of jsonNull:
      setPointer(a, nil)
      next(p)
    of jsonInt:
      setPointer(a, cast[pointer](p.getInt.int))
      next(p)
    else: raiseParseErr(p, "int for pointer type expected")
  of akString:
    case p.kind
    of jsonNull:
      setPointer(a, nil)
      next(p)
    of jsonString:
      setString(a, p.str)
      next(p)
    of jsonArrayStart:
      next(p)
      var str = ""
      while p.kind == jsonInt:
        let code = p.getInt()
        if code < 0 or code > 255:
          raiseParseErr(p, "invalid charcode: " & $code)
        str.add(chr(code))
        next(p)
      if p.kind == jsonArrayEnd: next(p)
      else: raiseParseErr(p, "an array of charcodes expected for string")
      setString(a, str)
    else: raiseParseErr(p, "string expected")
  of akInt..akInt64, akUInt..akUInt64:
    if p.kind == jsonInt:
      setBiggestInt(a, getInt(p))
      next(p)
      return
    raiseParseErr(p, "int expected")
  of akFloat..akFloat128:
    if p.kind == jsonFloat:
      setBiggestFloat(a, getFloat(p))
      next(p)
      return
    raiseParseErr(p, "float expected")
  of akRange: loadAny(p, a.skipRange, t)

proc loadAny(s: Stream, a: Any, t: var Table[BiggestInt, pointer]) =
  var p: JsonParser
  open(p, s, "unknown file")
  next(p)
  loadAny(p, a, t)
  close(p)

proc load*[T](s: Stream, data: var T) =
  ## Loads `data` from the stream `s`. Raises `IOError` in case of an error.
  runnableExamples:
    import marshal, streams
    var s = newStringStream("[1, 3, 5]")
    var a: array[3, int]
    load(s, a)
    assert a == [1, 3, 5]

  var tab = initTable[BiggestInt, pointer]()
  loadAny(s, toAny(data), tab)

proc store*[T](s: Stream, data: T) =
  ## Stores `data` into the stream `s`. Raises `IOError` in case of an error.
  runnableExamples:
    import marshal, streams
    var s = newStringStream("")
    var a = [1, 3, 5]
    store(s, a)
    s.setPosition(0)
    assert s.readAll() == "[1, 3, 5]"

  var stored = initIntSet()
  var d: T
  shallowCopy(d, data)
  storeAny(s, toAny(d), stored)

proc `$$`*[T](x: T): string =
  ## Returns a string representation of `x` (serialization, marshalling).
  ##
  ## **Note:** to serialize `x` to JSON use `$(%x)` from the ``json`` module.
  runnableExamples:
    type
      Foo = object
        id: int
        bar: string
    let x = Foo(id: 1, bar: "baz")
    ## serialize:
    let y = $$x
    assert y == """{"id": 1, "bar": "baz"}"""

  var stored = initIntSet()
  var d: T
  shallowCopy(d, x)
  var s = newStringStream()
  storeAny(s, toAny(d), stored)
  result = s.data

proc to*[T](data: string): T =
  ## Reads data and transforms it to a type ``T`` (deserialization, unmarshalling).
  runnableExamples:
    type
      Foo = object
        id: int
        bar: string
    let y = """{"id": 1, "bar": "baz"}"""
    assert typeof(y) is string
    ## deserialize to type 'Foo':
    let z = y.to[:Foo]
    assert typeof(z) is Foo
    assert z.id == 1
    assert z.bar == "baz"

  var tab = initTable[BiggestInt, pointer]()
  loadAny(newStringStream(data), toAny(result), tab)


when not defined(testing) and isMainModule:
  template testit(x: untyped) = echo($$to[type(x)]($$x))

  var x: array[0..4, array[0..4, string]] = [
    ["test", "1", "2", "3", "4"], ["test", "1", "2", "3", "4"],
    ["test", "1", "2", "3", "4"], ["test", "1", "2", "3", "4"],
    ["test", "1", "2", "3", "4"]]
  testit(x)
  var test2: tuple[name: string, s: uint] = ("tuple test", 56u)
  testit(test2)

  type
    TE = enum
      blah, blah2

    TestObj = object
      test, asd: int
      case test2: TE
      of blah:
        help: string
      else:
        nil

    PNode = ref Node
    Node = object
      next, prev: PNode
      data: string

  proc buildList(): PNode =
    new(result)
    new(result.next)
    new(result.prev)
    result.data = "middle"
    result.next.data = "next"
    result.prev.data = "prev"
    result.next.next = result.prev
    result.next.prev = result
    result.prev.next = result
    result.prev.prev = result.next

  var test3: TestObj
  test3.test = 42
  test3 = TestObj(test2: blah)
  testit(test3)

  var test4: ref tuple[a, b: string]
  new(test4)
  test4.a = "ref string test: A"
  test4.b = "ref string test: B"
  testit(test4)

  var test5 = @[(0, 1), (2, 3), (4, 5)]
  testit(test5)

  var test6: set[char] = {'A'..'Z', '_'}
  testit(test6)

  var test7 = buildList()
  echo($$test7)
  testit(test7)

  type
    A {.inheritable.} = object
    B = object of A
      f: int

  var
    a: ref A
    b: ref B
  new(b)
  a = b
  echo($$a[]) # produces "{}", not "{f: 0}"
y ----------------------------------------- - The compiler does not skip the linking step anymore even if no file has changed. - ``os.splitFile(".xyz")`` now returns ``("", ".xyz", "")`` instead of ``("", "", ".xyz")``. So filenames starting with a dot are handled differently. - ``strutils.split(s: string, seps: set[char])`` never yields the empty string anymore. This behaviour is probably more appropriate for whitespace splitting. - The compiler now stops after the ``--version`` command line switch. - Removed support for enum inheritance in the parser; enum inheritance has never been documented anyway. - The ``msg`` field of ``system.E_base`` has now the type ``string``, instead of ``cstring``. This improves memory safety. 2009-10-21 Version 0.8.2 released ================================= 2009-09-12 Version 0.8.0 released ================================= 2009-06-08 Version 0.7.10 released ================================== 2009-05-08 Version 0.7.8 released ================================= 2009-04-22 Version 0.7.6 released ================================= 2008-11-16 Version 0.7.0 released ================================= 2008-08-22 Version 0.6.0 released ================================= Nimrod version 0.6.0 has been released! **This is the first version of the compiler that is able to compile itself!**