From 6d3fbf973f1b0f6cc492b67f30cea74e5b33fbf8 Mon Sep 17 00:00:00 2001 From: flaviut Date: Sun, 13 Apr 2014 17:02:18 -0400 Subject: [PATCH 001/270] Allow for nil chaining in JSON --- lib/pure/json.nim | 21 ++++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/lib/pure/json.nim b/lib/pure/json.nim index 7424bbae95..0ac1f98d44 100644 --- a/lib/pure/json.nim +++ b/lib/pure/json.nim @@ -628,16 +628,27 @@ proc len*(n: PJsonNode): int = of JObject: result = n.fields.len else: discard -proc `[]`*(node: PJsonNode, name: string): PJsonNode = - ## Gets a field from a `JObject`. Returns nil if the key is not found. +proc `[]`*(node: PJsonNode, name: string, default: PJsonNode = nil): PJsonNode = + ## Gets a field from a `JObject`. If node is nil, this returns default + ## This is to allow for chaining such as the following: + ## .. code-block Nimrod + ## foo["field1"]["field2"] + ## + ## If `field1` is undefined, it will return nil, and the access on `field2` will + ## pass through. Also returns default if the key is not found. + if isNil(node): + return default assert(node.kind == JObject) for key, item in items(node.fields): if key == name: return item - return nil + return default -proc `[]`*(node: PJsonNode, index: int): PJsonNode = - ## Gets the node at `index` in an Array. +proc `[]`*(node: PJsonNode, index: int, default: PJsonNode = nil): PJsonNode = + ## Gets the node at `index` in an Array.If `node` is nil, this returns nil to + ## allow for chaining. Results are undefined if the index is out of range. + if isNil(node): + return default assert(node.kind == JArray) return node.elems[index] From 4ff511280301d53ebac3a473f988d93ba9c065e2 Mon Sep 17 00:00:00 2001 From: flaviut Date: Sun, 13 Apr 2014 17:05:12 -0400 Subject: [PATCH 002/270] Add a couple words to docs --- lib/pure/json.nim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/pure/json.nim b/lib/pure/json.nim index 0ac1f98d44..3e8840e470 100644 --- a/lib/pure/json.nim +++ b/lib/pure/json.nim @@ -635,7 +635,7 @@ proc `[]`*(node: PJsonNode, name: string, default: PJsonNode = nil): PJsonNode = ## foo["field1"]["field2"] ## ## If `field1` is undefined, it will return nil, and the access on `field2` will - ## pass through. Also returns default if the key is not found. + ## pass through and return nil. Also returns default if the key is not found. if isNil(node): return default assert(node.kind == JObject) From db7fee6303652572812a57ae70e3f2ca28e8829d Mon Sep 17 00:00:00 2001 From: flaviut Date: Sun, 13 Apr 2014 17:19:59 -0400 Subject: [PATCH 003/270] Add tests for the nil passthrough --- lib/pure/json.nim | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/lib/pure/json.nim b/lib/pure/json.nim index 3e8840e470..7b2a0eed65 100644 --- a/lib/pure/json.nim +++ b/lib/pure/json.nim @@ -936,6 +936,14 @@ when isMainModule: raise newException(EInvalidValue, "That line was expected to fail") except EInvalidIndex: echo() + let passthroughTest = parseJson"""{ "a": [1, 2, 3, 4], "b": "asd" }""" + # nil passthrough + assert(passthroughTest["doesnt_exist"][1] == nil) + assert(passthroughTest["doesnt_exist"]["anything"] == nil) + # default param + assert(passthroughTest["doesnt_exist",%true].bval) + assert(passthroughTest["doesnt_exist"][1,%true].bval) + discard """ while true: var json = stdin.readLine() From 64d3b9a34d0b51a6507a9be7ebd2d6fd25d64977 Mon Sep 17 00:00:00 2001 From: flaviut Date: Sun, 13 Apr 2014 17:25:43 -0400 Subject: [PATCH 004/270] Fix subtle mistake in docs and formatting --- lib/pure/json.nim | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/lib/pure/json.nim b/lib/pure/json.nim index 7b2a0eed65..38cdcc3db1 100644 --- a/lib/pure/json.nim +++ b/lib/pure/json.nim @@ -629,13 +629,13 @@ proc len*(n: PJsonNode): int = else: discard proc `[]`*(node: PJsonNode, name: string, default: PJsonNode = nil): PJsonNode = - ## Gets a field from a `JObject`. If node is nil, this returns default + ## Gets a field from a `JObject`. If node is nil, this returns `default` ## This is to allow for chaining such as the following: ## .. code-block Nimrod ## foo["field1"]["field2"] ## ## If `field1` is undefined, it will return nil, and the access on `field2` will - ## pass through and return nil. Also returns default if the key is not found. + ## pass through and return `default`. Also returns `default` if the key is not found. if isNil(node): return default assert(node.kind == JObject) @@ -645,8 +645,9 @@ proc `[]`*(node: PJsonNode, name: string, default: PJsonNode = nil): PJsonNode = return default proc `[]`*(node: PJsonNode, index: int, default: PJsonNode = nil): PJsonNode = - ## Gets the node at `index` in an Array.If `node` is nil, this returns nil to - ## allow for chaining. Results are undefined if the index is out of range. + ## Gets the node at `index` in an Array.If `node` is nil, this returns `result`, + ## which is by default `nil` to allow for chaining. Results are undefined if + ## the index is out of range. if isNil(node): return default assert(node.kind == JArray) From ec59b790c52223ff8c8f3f489005df1b6e4a51b6 Mon Sep 17 00:00:00 2001 From: flaviut Date: Sun, 13 Apr 2014 17:42:52 -0400 Subject: [PATCH 005/270] JSON index accesses are bounds checked --- lib/pure/json.nim | 35 +++++++++++++++++++++++++++-------- 1 file changed, 27 insertions(+), 8 deletions(-) diff --git a/lib/pure/json.nim b/lib/pure/json.nim index 38cdcc3db1..4d6ecb837b 100644 --- a/lib/pure/json.nim +++ b/lib/pure/json.nim @@ -644,13 +644,16 @@ proc `[]`*(node: PJsonNode, name: string, default: PJsonNode = nil): PJsonNode = return item return default -proc `[]`*(node: PJsonNode, index: int, default: PJsonNode = nil): PJsonNode = +proc `[]`*(node: PJsonNode, index: int, default: PJsonNode = nil): PJsonNode {.raises: [EInvalidIndex].}= ## Gets the node at `index` in an Array.If `node` is nil, this returns `result`, - ## which is by default `nil` to allow for chaining. Results are undefined if - ## the index is out of range. + ## which is by default `nil` to allow for chaining. `EInvalidIndex` is raised if `index` + ## is out of bounds if isNil(node): return default assert(node.kind == JArray) + if index < 0 or index >= node.elems.len: + raise newException(EInvalidIndex, + "index " & $index & " is out of range [0, " & $node.elems.len & ")") return node.elems[index] proc hasKey*(node: PJsonNode, key: string): bool = @@ -937,13 +940,29 @@ when isMainModule: raise newException(EInvalidValue, "That line was expected to fail") except EInvalidIndex: echo() - let passthroughTest = parseJson"""{ "a": [1, 2, 3, 4], "b": "asd" }""" + let testJson = parseJson"""{ "a": [1, 2, 3, 4], "b": "asd" }""" # nil passthrough - assert(passthroughTest["doesnt_exist"][1] == nil) - assert(passthroughTest["doesnt_exist"]["anything"] == nil) + assert(testJson["doesnt_exist"][1] == nil) + assert(testJson["doesnt_exist"]["anything"] == nil) # default param - assert(passthroughTest["doesnt_exist",%true].bval) - assert(passthroughTest["doesnt_exist"][1,%true].bval) + assert(testJson["doesnt_exist",%true].bval) + assert(testJson["doesnt_exist"][1,%true].bval) + + # Bounds checking + try: + let a = testJson["a"][9] + assert(false, "EInvalidIndex not thrown") + except EInvalidIndex: + discard + try: + let a = testJson["a"][-1] + assert(false, "EInvalidIndex not thrown") + except EInvalidIndex: + discard + try: + assert(testJson["a"][0].num == 1, "Index doesn't correspond to its value") + except: + assert(false, "EInvalidIndex thrown for valid index") discard """ while true: From dd6c5f8929c45926f60f71cfac2bf574c883267b Mon Sep 17 00:00:00 2001 From: Grzegorz Adam Hankiewicz Date: Sat, 19 Apr 2014 21:45:39 +0200 Subject: [PATCH 006/270] Adds documentation about koch. --- doc/intern.txt | 2 + doc/koch.txt | 134 +++++++++++++++++++++++++++++++++++++++++++++++++ koch.nim | 2 + readme.md | 5 +- readme.txt | 5 +- web/nimrod.ini | 2 +- 6 files changed, 145 insertions(+), 5 deletions(-) create mode 100644 doc/koch.txt diff --git a/doc/intern.txt b/doc/intern.txt index c602e49337..a8466160c5 100644 --- a/doc/intern.txt +++ b/doc/intern.txt @@ -59,6 +59,8 @@ And for a debug version compatible with GDB:: The ``koch`` program is Nimrod's maintenance script. It is a replacement for make and shell scripting with the advantage that it is much more portable. +More information about its options can be found in the `koch `_ +documentation. Coding Guidelines diff --git a/doc/koch.txt b/doc/koch.txt new file mode 100644 index 0000000000..11d0fa33fb --- /dev/null +++ b/doc/koch.txt @@ -0,0 +1,134 @@ +=============================== + Nimrod maintenance script +=============================== + +:Version: |nimrodversion| + +.. contents:: + +.. raw:: html +

+ "A great chef is an artist that I truly respect" -- Robert Stack. +

+ + +Introduction +============ + +The `koch`:idx: program is Nimrod's maintenance script. It is a replacement +for make and shell scripting with the advantage that it is much more portable. +The word *koch* means *cook* in German. ``koch`` is used mainly to build the +Nimrod compiler, but it can also be used for other tasks. This document +describes the supported commands and their options. + + +Commands +======== + +boot command +------------ + +The `boot`:idx: command bootstraps the compiler, and it accepts different +options: + +-d:release + By default a debug version is created, passing this option will + force a release build, which is much faster and should be preferred + unless you are debugging the compiler. +-d:tinyc + Include the `Tiny C `_ backend. This + option is not supported on Windows. +-d:useGnuReadline + Includes the `rdstdin module `_ for `interactive + mode `_ (aka ``nimrod i``). + This is not needed on Windows. On other platforms this may + incorporate the GNU readline library. +-d:nativeStacktrace + Use native stack traces (only for Mac OS X or Linux). +-d:noCaas + Builds Nimrod without compiler as a service (CAAS) support. CAAS + support is required for functionality like Nimrod's `idetool + `_ command used to integrate the compiler with + `external IDEs `_. +-d:avoidTimeMachine + Only for Mac OS X, activating this switch will force excluding + the generated ``nimcache`` directories from Time Machine backups. + By default ``nimcache`` directories will be included in backups, + and just for the Nimrod compiler itself it means backing up 20MB + of generated files each time you update the compiler. Using this + option will make the compiler invoke the `tmutil + `_ + command on all ``nimcache`` directories, setting their backup + exclusion bit. + + You can use the following command to locate all ``nimcache`` + directories and check their backup exclusion bit:: + + $ find . -type d -name nimcache -exec tmutil isexcluded \{\} \; + +After compilation is finished you will hopefully end up with the nimrod +compiler in the ``bin`` directory. You can add Nimrod's ``bin`` directory to +your ``$PATH`` or use the `install command`_ to place it where it will be +found. + +clean command +------------- + +The `clean`:idx: command removes all generated files. + +csource command +--------------- + +The `csource`:idx: command builds the C sources for installation. It accepts +the same options as you would pass to the `boot command`_. + +inno command +------------ + +The `inno`:idx: command builds the `Inno Setup installer for Windows +`_. + +install command +--------------- + +The `install`:idx: command installs Nimrod to the specified directory, which +is required as a parameter. For example, on Unix platforms you could run:: + + $ ./koch install /usr/local/bin + +temp command +------------ + +The temp command builds the Nimrod compiler but with a different final name +(``nimrod_temp``), so it doesn't overwrite your normal compiler. You can use +this command to test different options, the same you would issue for the `boot +command`_. + +test command +------------ + +The `test`:idx: command can also be invoked with the alias ``tests``. This +command will compile and run ``tests/testament/tester.nim``, which is the main +driver of Nimrod's test suite. You can pass options to the ``test`` command, +they will be forwarded to the tester. See its source code for available +options. + +update command +-------------- + +The `update`:idx: command updates nimrod to the latest version from github. +For this command to work you need to have compiled ``koch`` itself with the +``-d:withUpdate`` switch. + +web command +----------- + +The `web`:idx: command converts the documentation in the ``doc`` directory +from rst to HTML. It also repeats the same operation but places the result in +the ``web/upload`` which can be used to update the website at +http://nimrod-lang.org. + +zip command +----------- + +The `zip`:idx: command builds the installation ZIP package. diff --git a/koch.nim b/koch.nim index 9d59344f2b..054db4f8f6 100644 --- a/koch.nim +++ b/koch.nim @@ -6,6 +6,8 @@ # See the file "copying.txt", included in this # distribution, for details about the copyright. # +# See doc/koch.txt for documentation. +# when defined(gcc) and defined(windows): when defined(x86): diff --git a/readme.md b/readme.md index 38e04233fc..e8c081d4ed 100644 --- a/readme.md +++ b/readme.md @@ -39,8 +39,9 @@ $ bin/nimrod c koch $ ./koch boot -d:release ``` -``koch install [dir]`` may then be used to install Nimrod, or you -can simply add it to your PATH. +``koch install [dir]`` may then be used to install Nimrod, or you can simply +add it to your PATH. More ``koch`` related options are documented in +[doc/koch.txt](doc/koch.txt). The above steps can be performed on Windows in a similar fashion, the ``build.bat`` and ``build64.bat`` (for x86_64 systems) are provided to be used diff --git a/readme.txt b/readme.txt index 3eaef0b355..3f6aef05cd 100644 --- a/readme.txt +++ b/readme.txt @@ -39,8 +39,9 @@ $ bin/nimrod c koch $ ./koch boot -d:release ``` -``koch install [dir]`` may then be used to install Nimrod, or you -can simply add it to your PATH. +``koch install [dir]`` may then be used to install Nimrod, or you can simply +add it to your PATH. More ``koch`` related options are documented in +[doc/koch.txt](doc/koch.txt). The above steps can be performed on Windows in a similar fashion, the ``build.bat`` and ``build64.bat`` (for x86_64 systems) are provided to be used diff --git a/web/nimrod.ini b/web/nimrod.ini index b29bcff309..3a05bad3d5 100644 --- a/web/nimrod.ini +++ b/web/nimrod.ini @@ -37,7 +37,7 @@ UNIX. We don't believe this to be a coincidence. - Jeremy S. Anderson.""" [Documentation] doc: "endb;intern;apis;lib;manual;tut1;tut2;nimrodc;overview;filters;trmacros" -doc: "tools;c2nim;niminst;nimgrep;gc;estp;idetools;docgen" +doc: "tools;c2nim;niminst;nimgrep;gc;estp;idetools;docgen;koch" pdf: "manual;lib;tut1;tut2;nimrodc;c2nim;niminst;gc" srcdoc2: "system.nim;impure/graphics;wrappers/sdl" srcdoc2: "core/macros;pure/marshal;core/typeinfo;core/unsigned" From f79dd728ea249badd1d9dfd8b617916b5bd0a2f1 Mon Sep 17 00:00:00 2001 From: Grzegorz Adam Hankiewicz Date: Sat, 19 Apr 2014 22:55:31 +0200 Subject: [PATCH 007/270] Updates list of files to build in doc directory. --- web/nimrod.ini | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/web/nimrod.ini b/web/nimrod.ini index 3a05bad3d5..2d0137ec14 100644 --- a/web/nimrod.ini +++ b/web/nimrod.ini @@ -64,6 +64,15 @@ srcdoc2: "pure/nimprof;pure/unittest;packages/docutils/highlite" srcdoc2: "packages/docutils/rst;packages/docutils/rstast" srcdoc2: "packages/docutils/rstgen;pure/logging;pure/asyncdispatch;pure/asyncnet" srcdoc2: "pure/rawsockets;pure/asynchttpserver;pure/net;pure/selectors" +srcdoc2: "wrappers/expat;wrappers/readline/history" +srcdoc2: "wrappers/libsvm.nim;wrappers/libuv" +srcdoc2: "wrappers/zip/zlib;wrappers/zip/libzip" +srcdoc2: "wrappers/libcurl;pure/md5;wrappers/mysql;wrappers/iup" +srcdoc2: "posix/posix;wrappers/odbcsql" +srcdoc2: "wrappers/tre;wrappers/openssl;wrappers/pcre" +srcdoc2: "wrappers/sqlite3;wrappers/postgres;wrappers/tinyc" +srcdoc2: "wrappers/readline/readline;wrappers/readline/rltypedefs" +srcdoc2: "wrappers/joyent_http_parser" webdoc: "wrappers/libcurl;pure/md5;wrappers/mysql;wrappers/iup" webdoc: "wrappers/sqlite3;wrappers/postgres;wrappers/tinyc" From e2f771a15098746f342a016dc2dbe1ed600cc0ff Mon Sep 17 00:00:00 2001 From: Grzegorz Adam Hankiewicz Date: Sat, 19 Apr 2014 23:41:53 +0200 Subject: [PATCH 008/270] Removes accidental module docstring. --- lib/wrappers/readline/readline.nim | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/wrappers/readline/readline.nim b/lib/wrappers/readline/readline.nim index 1f0dd564f7..bbe416534a 100644 --- a/lib/wrappers/readline/readline.nim +++ b/lib/wrappers/readline/readline.nim @@ -29,8 +29,8 @@ elif defined(macosx): else: const readlineDll* = "libreadline.so.6(|.0)" -## mangle "'TCommandFunc'" TCommandFunc -## mangle TvcpFunc TvcpFunc +# mangle "'TCommandFunc'" TCommandFunc +# mangle TvcpFunc TvcpFunc import rltypedefs From 03ffc344e129e38638a698fa0e83bb6c7b634913 Mon Sep 17 00:00:00 2001 From: Grzegorz Adam Hankiewicz Date: Sun, 20 Apr 2014 12:23:54 +0200 Subject: [PATCH 009/270] Version switch displays options used during `koch boot` --- compiler/commands.nim | 22 ++++++++++++++++++++++ compiler/options.nim | 2 +- lib/system/excpt.nim | 2 +- 3 files changed, 24 insertions(+), 2 deletions(-) diff --git a/compiler/commands.nim b/compiler/commands.nim index 8339219edb..366019c190 100644 --- a/compiler/commands.nim +++ b/compiler/commands.nim @@ -50,11 +50,33 @@ proc writeAdvancedUsage(pass: TCmdLinePass) = CPU[platform.hostCPU].name]) & AdvancedUsage) quit(0) +template bootSwitch(name, expr, userString: expr): expr = + # Helper to build boot constants, for debugging you can 'echo' the else part. + const name = if expr: " " & userString else: "" + +bootSwitch(usedAvoidTimeMachine, noTimeMachine, "-d:avoidTimeMachine") +bootSwitch(usedRelease, defined(release), "-d:release") +bootSwitch(usedTinyC, hasTinyCBackend, "-d:tinyc") +bootSwitch(usedGnuReadline, defined(useGnuReadline), "-d:useGnuReadline") +bootSwitch(usedNativeStacktrace, + defined(nativeStackTrace) and nativeStackTraceSupported, + "-d:nativeStackTrace") +bootSwitch(usedNoCaas, defined(noCaas), "-d:noCaas") +bootSwitch(usedFFI, hasFFI, "-d:useFFI") +bootSwitch(usedBoehm, defined(boehmgc), "--gc:boehm") +bootSwitch(usedMarkAndSweep, defined(gcmarkandsweep), "--gc:markAndSweep") +bootSwitch(usedGenerational, defined(gcgenerational), "--gc:generational") +bootSwitch(usedNoGC, defined(nogc), "--gc:none") + + proc writeVersionInfo(pass: TCmdLinePass) = if pass == passCmd1: msgWriteln(`%`(HelpMessage, [VersionAsString, platform.OS[platform.hostOS].name, CPU[platform.hostCPU].name])) + msgWriteln("active boot switches:" & usedRelease & usedAvoidTimeMachine & + usedTinyC & usedGnuReadline & usedNativeStacktrace & usedNoCaas & + usedFFI & usedBoehm & usedMarkAndSweep & usedGenerational & usedNoGC) quit(0) var diff --git a/compiler/options.nim b/compiler/options.nim index f053546669..02719cacce 100644 --- a/compiler/options.nim +++ b/compiler/options.nim @@ -16,7 +16,7 @@ const hasFFI* = defined(useFFI) newScopeForIf* = true useCaas* = not defined(noCaas) - noTimeMachine = defined(avoidTimeMachine) and defined(macosx) + noTimeMachine* = defined(avoidTimeMachine) and defined(macosx) type # please make sure we have under 32 options # (improves code efficiency a lot!) diff --git a/lib/system/excpt.nim b/lib/system/excpt.nim index 612a9e729f..e11a30e9b2 100644 --- a/lib/system/excpt.nim +++ b/lib/system/excpt.nim @@ -71,7 +71,7 @@ proc popCurrentException {.compilerRtl, inl.} = # some platforms have native support for stack traces: const - nativeStackTraceSupported = (defined(macosx) or defined(linux)) and + nativeStackTraceSupported* = (defined(macosx) or defined(linux)) and not nimrodStackTrace hasSomeStackTrace = nimrodStackTrace or defined(nativeStackTrace) and nativeStackTraceSupported From 72ff19fa924ae0df0e8fc01353579b6801b7693a Mon Sep 17 00:00:00 2001 From: Grzegorz Adam Hankiewicz Date: Sun, 20 Apr 2014 12:51:48 +0200 Subject: [PATCH 010/270] Mentions useFFI and garbage collector options. --- doc/koch.txt | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/doc/koch.txt b/doc/koch.txt index 11d0fa33fb..0321518081 100644 --- a/doc/koch.txt +++ b/doc/koch.txt @@ -65,6 +65,16 @@ options: directories and check their backup exclusion bit:: $ find . -type d -name nimcache -exec tmutil isexcluded \{\} \; +-d:useFFI + Nimrod code can use the `foreign function interface (FFI) + `_ at runtime, but macros + are limited to pure Nimrod code at compilation time. Enabling + this switch will allow macros to execute non-nimrod code at + compilation time (eg. open a file and write to it). +--gc:refc|v2|markAndSweep|boehm|none + Selects which garbage collection strategy to use for the compiler + and generated code. See the `Nimrod's Garbage Collector `_ + documentation for more information. After compilation is finished you will hopefully end up with the nimrod compiler in the ``bin`` directory. You can add Nimrod's ``bin`` directory to From a822d0bf0196f2c77f9032a5455c5102f673acad Mon Sep 17 00:00:00 2001 From: EXetoC Date: Sun, 20 Apr 2014 18:10:57 +0200 Subject: [PATCH 011/270] Add spawn test. Hangs most of the time on linux x64 at least. --- tests/system/tsysspawn.nim | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) create mode 100644 tests/system/tsysspawn.nim diff --git a/tests/system/tsysspawn.nim b/tests/system/tsysspawn.nim new file mode 100644 index 0000000000..0388918aa8 --- /dev/null +++ b/tests/system/tsysspawn.nim @@ -0,0 +1,29 @@ +discard """ + output: '''4 +8''' + cmd: "nimrod $target --threads:on $options $file" +""" + +var + x, y = 0 + +proc p1 = + for i in 0 .. 1_000_000: + discard + + inc x + +proc p2 = + for i in 0 .. 1_000_000: + discard + + inc y, 2 + +for i in 0.. 3: + spawn(p1()) + spawn(p2()) + +sync() + +echo x +echo y From a7db33e87640d4f3dc57cfc261763b690de3fd58 Mon Sep 17 00:00:00 2001 From: Billingsly Wetherfordshire Date: Sun, 20 Apr 2014 23:38:45 -0500 Subject: [PATCH 012/270] added md5 functions from --- lib/wrappers/openssl.nim | 67 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 67 insertions(+) diff --git a/lib/wrappers/openssl.nim b/lib/wrappers/openssl.nim index 90c398dceb..41ee52522d 100644 --- a/lib/wrappers/openssl.nim +++ b/lib/wrappers/openssl.nim @@ -433,3 +433,70 @@ else: proc SSLGetMode(s: PSSL): int = result = SSLctrl(s, SSL_CTRL_MODE, 0, nil) +# +type + MD5_LONG* = cuint +const + MD5_CBLOCK* = 64 + MD5_LBLOCK* = int(MD5_CBLOCK div 4) + MD5_DIGEST_LENGTH* = 16 +type + MD5_CTX* {.importc.} = object + A,B,C,D,Nl,Nh: MD5_LONG + data: array[MD5_LBLOCK, MD5_LONG] + num: cuint + +{.pragma: ic, importc: "$1".} +{.push callconv:cdecl, dynlib:DLLUtilName.} +proc MD5_Init*(c: var MD5_CTX): cint{.ic.} +proc MD5_Update*(c: var MD5_CTX; data: pointer; len: csize): cint{.ic.} +proc MD5_Final*(md: cstring; c: var MD5_CTX): cint{.ic.} +proc MD5*(d: ptr cuchar; n: csize; md: ptr cuchar): ptr cuchar{.ic.} +proc MD5_Transform*(c: var MD5_CTX; b: ptr cuchar){.ic.} +{.pop.} + +from strutils import toHex,toLower + +proc hexStr (buf:cstring): string = + # turn md5s output into a nice hex str + result = newStringOfCap(32) + for i in 0 .. <16: + result.add toHex(buf[i].ord, 2).toLower + +proc MD5_File* (file: string): string {.raises:[EIO,Ebase].} = + ## Generate MD5 hash for a file. Result is a 32 character + # hex string with lowercase characters (like the output + # of `md5sum` + const + sz = 512 + let f = open(file,fmRead) + var + buf: array[sz,char] + ctx: MD5_CTX + + discard md5_init(ctx) + while(let bytes = f.readChars(buf, 0, sz); bytes > 0): + discard md5_update(ctx, buf[0].addr, bytes) + + discard md5_final( buf[0].addr, ctx ) + f.close + + result = hexStr(buf) + +proc MD5_Str* (str:string): string {.raises:[EIO].} = + ##Generate MD5 hash for a string. Result is a 32 character + #hex string with lowercase characters + var + ctx: MD5_CTX + res: array[MD5_DIGEST_LENGTH,char] + input = str.cstring + discard md5_init(ctx) + + var i = 0 + while i < str.len: + let L = min(str.len - i, 512) + discard md5_update(ctx, input[i].addr, L) + i += L + + discard md5_final(res,ctx) + result = hexStr(res) From 357163570144d47a6d3be01fa7a9b2db99d6fa6e Mon Sep 17 00:00:00 2001 From: Billingsly Wetherfordshire Date: Sun, 20 Apr 2014 23:49:41 -0500 Subject: [PATCH 013/270] i left an {.importc.} in there --- lib/wrappers/openssl.nim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/wrappers/openssl.nim b/lib/wrappers/openssl.nim index 41ee52522d..3e86c65472 100644 --- a/lib/wrappers/openssl.nim +++ b/lib/wrappers/openssl.nim @@ -441,7 +441,7 @@ const MD5_LBLOCK* = int(MD5_CBLOCK div 4) MD5_DIGEST_LENGTH* = 16 type - MD5_CTX* {.importc.} = object + MD5_CTX* = object A,B,C,D,Nl,Nh: MD5_LONG data: array[MD5_LBLOCK, MD5_LONG] num: cuint From c1c889b9d778392e180a8aedc760a4c4d04f20fe Mon Sep 17 00:00:00 2001 From: ReneSac Date: Mon, 21 Apr 2014 12:38:10 -0300 Subject: [PATCH 014/270] Rearrangements on the webpage's docs tab. --- doc/docs.txt | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/doc/docs.txt b/doc/docs.txt index 2162d65647..8126da86c7 100644 --- a/doc/docs.txt +++ b/doc/docs.txt @@ -6,19 +6,19 @@ The documentation consists of several documents: - | `Tutorial (part II) `_ | The Nimrod tutorial part two deals with the advanced language constructs. +- | `Language Manual `_ + | The Nimrod manual is a draft that will evolve into a proper specification. + - | `Library documentation `_ | This document describes Nimrod's standard library. -- | `User guide `_ +- | `Compiler user guide `_ | The user guide lists command line arguments, special features of the compiler, etc. - | `Tools documentation `_ | Description of some tools that come with the standard distribution. -- | `Manual `_ - | The Nimrod manual is a draft that will evolve into a proper specification. - - | `GC `_ | Additional documentation about Nimrod's GC and how to operate it in a | realtime setting. From bc79f40281accea987b23b47778782f7684de38c Mon Sep 17 00:00:00 2001 From: Simon Hafner Date: Mon, 21 Apr 2014 13:20:17 -0500 Subject: [PATCH 015/270] changed pointer to array in sqlite callback --- lib/wrappers/sqlite3.nim | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/wrappers/sqlite3.nim b/lib/wrappers/sqlite3.nim index 586f763ae2..7b7f0874e6 100644 --- a/lib/wrappers/sqlite3.nim +++ b/lib/wrappers/sqlite3.nim @@ -106,15 +106,15 @@ type Pstmt* = ptr Tstmt Tvalue{.pure, final.} = object Pvalue* = ptr Tvalue - PPValue* = ptr Pvalue + PValueArg* = array[0..127, Pvalue] Tcallback* = proc (para1: pointer, para2: int32, para3, para4: cstringArray): int32{.cdecl.} Tbind_destructor_func* = proc (para1: pointer){.cdecl.} Tcreate_function_step_func* = proc (para1: Pcontext, para2: int32, - para3: PPValue){.cdecl.} + para3: PValueArg){.cdecl.} Tcreate_function_func_func* = proc (para1: Pcontext, para2: int32, - para3: PPValue){.cdecl.} + para3: PValueArg){.cdecl.} Tcreate_function_final_func* = proc (para1: Pcontext){.cdecl.} Tresult_func* = proc (para1: pointer){.cdecl.} Tcreate_collation_func* = proc (para1: pointer, para2: int32, para3: pointer, From 1d0175ed2791b9bc4295ca25f4958a9c6ab1b1a1 Mon Sep 17 00:00:00 2001 From: Grzegorz Adam Hankiewicz Date: Sun, 20 Apr 2014 16:42:39 +0200 Subject: [PATCH 016/270] Adds explicit titles to documentation index. --- compiler/docgen.nim | 10 ++++++++-- lib/packages/docutils/rstgen.nim | 13 ++++++++----- 2 files changed, 16 insertions(+), 7 deletions(-) diff --git a/compiler/docgen.nim b/compiler/docgen.nim index 6948c49797..4c98034013 100644 --- a/compiler/docgen.nim +++ b/compiler/docgen.nim @@ -541,8 +541,14 @@ proc genOutFile(d: PDoc): PRope = if toc != nil: toc = ropeFormatNamedVars(getConfigVar("doc.toc"), ["content"], [toc]) for i in countup(low(TSymKind), high(TSymKind)): app(code, d.section[i]) - if d.meta[metaTitle].len != 0: title = d.meta[metaTitle] - else: title = "Module " & extractFilename(changeFileExt(d.filename, "")) + + # Extract the title. Non API modules generate an entry in the index table. + if d.meta[metaTitle].len != 0: + title = d.meta[metaTitle] + setIndexTerm(d[], "", title) + else: + # Modules get an automatic title for the HTML, but no entry in the index. + title = "Module " & extractFilename(changeFileExt(d.filename, "")) let bodyname = if d.hasToc: "doc.body_toc" else: "doc.body_no_toc" content = ropeFormatNamedVars(getConfigVar(bodyname), ["title", diff --git a/lib/packages/docutils/rstgen.nim b/lib/packages/docutils/rstgen.nim index cead17f2b9..85a7111dc2 100644 --- a/lib/packages/docutils/rstgen.nim +++ b/lib/packages/docutils/rstgen.nim @@ -256,9 +256,11 @@ proc setIndexTerm*(d: var TRstGenerator, id, term: string, ## ## The ``d.theIndex`` string will be used to append the term in the format ## ``termfile#id``. The anchor will be the based on the name of the file - ## currently being parsed plus the `id`, which will be appended after a hash. - ## If `linkTitle` or `linkDesc` are not the empty string, two additional - ## columns with their contents will be added. + ## currently being parsed plus the specified `id`. The `id` will be appended + ## with a hash character only if its length is not zero, otherwise no + ## specific anchor will be generated. If `linkTitle` or `linkDesc` are not + ## the empty string, two additional columns with their contents will be + ## added. ## ## The index won't be written to disk unless you call ``writeIndexFile``. The ## purpose of the index is documented in the `docgen tools guide @@ -267,8 +269,9 @@ proc setIndexTerm*(d: var TRstGenerator, id, term: string, d.theIndex.add('\t') let htmlFile = changeFileExt(extractFilename(d.filename), HtmlExt) d.theIndex.add(htmlFile) - d.theIndex.add('#') - d.theIndex.add(id) + if id.len > 0: + d.theIndex.add('#') + d.theIndex.add(id) if linkTitle.len > 0 or linkDesc.len > 0: d.theIndex.add('\t' & linkTitle.quoteIndexColumn) d.theIndex.add('\t' & linkDesc.quoteIndexColumn) From d33edd21f39569073584f2243d968d95b0b1f5c1 Mon Sep 17 00:00:00 2001 From: Grzegorz Adam Hankiewicz Date: Sun, 20 Apr 2014 17:43:35 +0200 Subject: [PATCH 017/270] Adds documentation sections to the index. --- compiler/docgen.nim | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/compiler/docgen.nim b/compiler/docgen.nim index 4c98034013..6a90a45db2 100644 --- a/compiler/docgen.nim +++ b/compiler/docgen.nim @@ -513,6 +513,24 @@ proc generateJson(d: PDoc, n: PNode, jArray: PJsonNode = nil): PJsonNode = discard generateJson(d, lastSon(n.sons[0]), jArray) else: discard +proc stripTOCHTML(s: string): string = + ## Ugly quick hack to remove HTML tags from TOC titles. + ## + ## A TTocEntry.header field already contains rendered HTML tags. Instead of + ## implementing a proper version of rstgen.renderRstToOut() which recursively + ## renders an rst tree to plain text, we simply remove text found between + ## angled brackets. Given the limited possibilities of rst inside TOC titles + ## this should be enough. + result = s + var first = result.find('<') + while first >= 0: + let last = result.find('>', first) + if last < 0: + # Abort, since we didn't found a closing angled bracket. + return + result.delete(first, last) + first = result.find('<', first) + proc genSection(d: PDoc, kind: TSymKind) = const sectionNames: array[skModule..skTemplate, string] = [ "Imports", "Types", "Vars", "Lets", "Consts", "Vars", "Procs", "Methods", @@ -550,6 +568,13 @@ proc genOutFile(d: PDoc): PRope = # Modules get an automatic title for the HTML, but no entry in the index. title = "Module " & extractFilename(changeFileExt(d.filename, "")) + # Generate entries also for other entries in the toc. + for tocPart in d.tocPart: + # Use   to indicate the current level for the output HTML. + assert tocPart.n.level >= 0 + setIndexTerm(d[], tocPart.refname, tocPart.header.stripTOCHTML, + repeatStr(max(0, tocPart.n.level), " ") & tocPart.header) + let bodyname = if d.hasToc: "doc.body_toc" else: "doc.body_no_toc" content = ropeFormatNamedVars(getConfigVar(bodyname), ["title", "tableofcontents", "moduledesc", "date", "time", "content"], From 400dd208589a2841d2ef4de88ed3faf58539b96c Mon Sep 17 00:00:00 2001 From: Grzegorz Adam Hankiewicz Date: Sun, 20 Apr 2014 23:26:14 +0200 Subject: [PATCH 018/270] Splits index into documents and APIs. --- compiler/docgen.nim | 4 +- lib/packages/docutils/rstgen.nim | 238 +++++++++++++++++++++++++------ 2 files changed, 195 insertions(+), 47 deletions(-) diff --git a/compiler/docgen.nim b/compiler/docgen.nim index 6a90a45db2..ac432ab8f7 100644 --- a/compiler/docgen.nim +++ b/compiler/docgen.nim @@ -570,10 +570,10 @@ proc genOutFile(d: PDoc): PRope = # Generate entries also for other entries in the toc. for tocPart in d.tocPart: - # Use   to indicate the current level for the output HTML. + # Use spaces to indicate the current level for the output HTML. assert tocPart.n.level >= 0 setIndexTerm(d[], tocPart.refname, tocPart.header.stripTOCHTML, - repeatStr(max(0, tocPart.n.level), " ") & tocPart.header) + repeatChar(max(0, tocPart.n.level), ' ') & tocPart.header) let bodyname = if d.hasToc: "doc.body_toc" else: "doc.body_no_toc" content = ropeFormatNamedVars(getConfigVar(bodyname), ["title", diff --git a/lib/packages/docutils/rstgen.nim b/lib/packages/docutils/rstgen.nim index 85a7111dc2..d945a4b64b 100644 --- a/lib/packages/docutils/rstgen.nim +++ b/lib/packages/docutils/rstgen.nim @@ -23,7 +23,8 @@ ## many options and tweaking, but you are not limited to snippets and can ## generate `LaTeX documents `_ too. -import strutils, os, hashes, strtabs, rstast, rst, highlite +import strutils, os, hashes, strtabs, rstast, rst, highlite, tables, sequtils, + algorithm const HtmlExt = "html" @@ -120,8 +121,8 @@ proc initRstGenerator*(g: var TRstGenerator, target: TOutputTarget, proc writeIndexFile*(g: var TRstGenerator, outfile: string) = ## Writes the current index buffer to the specified output file. ## - ## You previously need to add entries to the index with the ``setIndexTerm`` - ## proc. If the index is empty the file won't be created. + ## You previously need to add entries to the index with the `setIndexTerm() + ## <#setIndexTerm>`_ proc. If the index is empty the file won't be created. if g.theIndex.len > 0: writeFile(outfile, g.theIndex) proc addXmlChar(dest: var string, c: char) = @@ -255,16 +256,21 @@ proc setIndexTerm*(d: var TRstGenerator, id, term: string, ## Adds a `term` to the index using the specified hyperlink identifier. ## ## The ``d.theIndex`` string will be used to append the term in the format - ## ``termfile#id``. The anchor will be the based on the name of the file - ## currently being parsed plus the specified `id`. The `id` will be appended - ## with a hash character only if its length is not zero, otherwise no - ## specific anchor will be generated. If `linkTitle` or `linkDesc` are not - ## the empty string, two additional columns with their contents will be - ## added. + ## ``termfile#id``. ## - ## The index won't be written to disk unless you call ``writeIndexFile``. The - ## purpose of the index is documented in the `docgen tools guide - ## `_. + ## The anchor will be the based on the name of the file currently being + ## parsed plus the specified `id`. The `id` will be appended with a hash + ## character only if its length is not zero, otherwise no specific anchor + ## will be generated. In general you should only pass an empty `id` value + ## for the title of standalone rst documents (they are special for the + ## `mergeIndexes() <#mergeIndexes>`_ proc). + ## + ## If `linkTitle` or `linkDesc` are not the empty string, two additional + ## columns with their contents will be added. + ## + ## The index won't be written to disk unless you call `writeIndexFile() + ## <#writeIndexFile>`_. The purpose of the index is documented in the `docgen + ## tools guide `_. d.theIndex.add(term) d.theIndex.add('\t') let htmlFile = changeFileExt(extractFilename(d.filename), HtmlExt) @@ -335,43 +341,18 @@ proc sortIndex(a: var openArray[TIndexEntry]) = a[j] <- v if h == 1: break -proc mergeIndexes*(dir: string): string = - ## merges all index files in `dir` and returns the generated index as HTML. - ## The result is no full HTML for flexibility. - var a: seq[TIndexEntry] - newSeq(a, 15_000) - setLen(a, 0) - var L = 0 - for kind, path in walkDir(dir): - if kind == pcFile and path.endsWith(IndexExt): - for line in lines(path): - let s = line.find('\t') - if s < 0: continue - setLen(a, L+1) - a[L].keyword = line.substr(0, s-1) - a[L].link = line.substr(s+1) - if a[L].link.find('\t') > 0: - let extraCols = a[L].link.split('\t') - a[L].link = extraCols[0] - assert extraCols.len == 3 - a[L].linkTitle = extraCols[1].unquoteIndexColumn - a[L].linkDesc = extraCols[2].unquoteIndexColumn - else: - a[L].linkTitle = nil - a[L].linkDesc = nil - inc L - sortIndex(a) +proc generateSymbolIndex(symbols: seq[TIndexEntry]): string = result = "" var i = 0 - while i < L: - result.addf("
$1
    \n", - [a[i].keyword]) + while i < symbols.len: + result.addf("
    $1
      \n", + [symbols[i].keyword]) var j = i - while j < L and a[i].keyword == a[j].keyword: + while j < symbols.len and symbols[i].keyword == symbols[j].keyword: let - url = a[j].link - text = if not a[j].linkTitle.isNil: a[j].linkTitle else: url - desc = if not a[j].linkDesc.isNil: a[j].linkDesc else: "" + url = symbols[j].link + text = if not symbols[j].linkTitle.isNil: symbols[j].linkTitle else: url + desc = if not symbols[j].linkDesc.isNil: symbols[j].linkDesc else: "" if desc.len > 0: result.addf("""
    • $2
    • @@ -382,6 +363,173 @@ proc mergeIndexes*(dir: string): string = inc j result.add("
    \n") i = j + +proc isDocumentationTitle(hyperlink: string): bool = + ## Returns true if the hyperlink is actually a documentation title. + ## + ## Documentation titles lack the hash. See `mergeIndexes() <#mergeIndexes>`_ + ## for a more detailed explanation. + result = hyperlink.find('#') < 0 + +proc `or`(x, y: string): string {.inline.} = + result = if x.isNil: y else: x + +proc stripTOCLevel(s: string): tuple[level: int, text: string] = + ## Returns the *level* of the toc along with the text without it. + for c in 0 .. |
characters to switch to `newLevel`. + ## + ## The amount of lists added/removed will be based on the `level` variable, + ## which will be reset to `newLevel` at the end of the proc. + result = "" + if level == newLevel: + return + if newLevel > level: + result = repeatStr(newLevel - level, "
    ") + else: + result = repeatStr(level - newLevel, "
") + level = newLevel + +proc generateDocumentationTOC(entries: seq[TIndexEntry]): + tuple[toc, titleRef: string] = + ## Returns the sequence of index entries in an HTML hierarchical list. + result.toc = "" + # Build a list of levels and extracted titles to make processing easier. + var + levels: seq[tuple[level: int, text: string]] + L = 0 + level = 1 + levels.newSeq(entries.len) + for entry in entries: + let (rawLevel, rawText) = stripTOCLevel(entry.linkTitle or entry.keyword) + if rawLevel < 1: + # This is a normal symbol, push it *inside* one level from the last one. + levels[L].level = level + 1 + else: + # The level did change, update the level indicator. + level = rawLevel + levels[L].level = rawLevel + + levels[L].text = rawText + inc L + + # Now generate hierarchical lists based on the precalculated levels. + result.toc = "
    \n" + level = 1 + L = 0 + while L < entries.len: + let link = entries[L].link + if link.isDocumentationTitle: + result.titleRef = link + else: + result.toc.add(level.indentToLevel(levels[L].level)) + result.toc.add("
  • " & + levels[L].text & "\n") + inc L + result.toc.add(level.indentToLevel(1) & "
\n") + assert(not result.titleRef.isNil, "Can't use this proc on an API index") + +proc generateDocumentationIndex( + docs: TTable[string, seq[TIndexEntry]]): string = + ## Returns all the documentation TOCs in an HTML hierarchical list. + result = "" + + # Sort the titles to generate their toc in alphabetical order. + var titles = toSeq(keys[string, seq[TIndexEntry]](docs)) + sort(titles, system.cmp) + + for title in titles: + let (list, titleRef) = generateDocumentationTOC(docs[title]) + result.add("\n") + +proc mergeIndexes*(dir: string): string = + ## Merges all index files in `dir` and returns the generated index as HTML. + ## + ## This proc will first scan `dir` for index files with the ``.idx`` + ## extension previously created by commands like ``nimrod doc|rst2html`` + ## which use the ``--index:on`` switch. These index files are the result of + ## calls to `setIndexTerm() <#setIndexTerm>`_ and `writeIndexFile() + ## <#writeIndexFile>`_, so they are simple tab separated files. + ## + ## As convention this proc will split index files into two categories: + ## documentation and API. API indices will be all joined together into a + ## single big sorted index, making the bulk of the final index. This is good + ## for API documentation because many symbols are repated in different + ## modules. On the other hand, documentation indices are essentially table of + ## contents plus a few special markers. These documents will be rendered in a + ## separate section which tries to maintain the order and hierarchy of the + ## symbols in the index file. + ## + ## To differentiate between a documentation and API file a convention is + ## used: indices which contain one entry without the HTML hash character (#) + ## will be considered `documentation`, since this hash-less entry is the + ## explicit title of the document. Indices without this explicit entry will + ## be considered `generated API` extracted out of a source ``.nim`` file. + ## + ## Returns the merged and sorted indices into a single HTML block which can + ## be further embedded into nimdoc templates. + var + symbols: seq[TIndexEntry] + docs = initTable[string, seq[TIndexEntry]](32) + newSeq(symbols, 15_000) + setLen(symbols, 0) + var L = 0 + # Scan index files and build the list of symbols. + for kind, path in walkDir(dir): + if kind == pcFile and path.endsWith(IndexExt): + var + fileEntries: seq[TIndexEntry] + foundTitle = "" + F = 0 + newSeq(fileEntries, 500) + setLen(fileEntries, 0) + for line in lines(path): + let s = line.find('\t') + if s < 0: continue + setLen(fileEntries, F+1) + fileEntries[F].keyword = line.substr(0, s-1) + fileEntries[F].link = line.substr(s+1) + # See if we detect a title, a link without a #foobar part. + if foundTitle.len < 1 and fileEntries[F].link.isDocumentationTitle: + foundTitle = fileEntries[F].keyword + + if fileEntries[F].link.find('\t') > 0: + let extraCols = fileEntries[F].link.split('\t') + fileEntries[F].link = extraCols[0] + assert extraCols.len == 3 + fileEntries[F].linkTitle = extraCols[1].unquoteIndexColumn + fileEntries[F].linkDesc = extraCols[2].unquoteIndexColumn + else: + fileEntries[F].linkTitle = nil + fileEntries[F].linkDesc = nil + inc F + # Depending on type add this to the list of symbols or table of APIs. + if foundTitle.len > 0: + docs[foundTitle] = fileEntries + else: + setLen(symbols, L + F) + for i in 0 .. 0: + result.add("

Documentation files

\n") + result.add(generateDocumentationIndex(docs)) + + # Generate the HTML block with symbols. + if L > 0: + sortIndex(symbols) + result.add("

API symbols

\n") + result.add(generateSymbolIndex(symbols)) + # ---------------------------------------------------------------------------- From 5962119feb1601ee326950b15f2769eb6825dc58 Mon Sep 17 00:00:00 2001 From: Grzegorz Adam Hankiewicz Date: Sun, 20 Apr 2014 23:35:59 +0200 Subject: [PATCH 019/270] Moves TOC index entry generation to rstgen module. We want it there so that index entries appear in the same order as they are found in the input text. --- compiler/docgen.nim | 25 ------------------------- lib/packages/docutils/rstgen.nim | 24 +++++++++++++++++++++++- 2 files changed, 23 insertions(+), 26 deletions(-) diff --git a/compiler/docgen.nim b/compiler/docgen.nim index ac432ab8f7..4c98034013 100644 --- a/compiler/docgen.nim +++ b/compiler/docgen.nim @@ -513,24 +513,6 @@ proc generateJson(d: PDoc, n: PNode, jArray: PJsonNode = nil): PJsonNode = discard generateJson(d, lastSon(n.sons[0]), jArray) else: discard -proc stripTOCHTML(s: string): string = - ## Ugly quick hack to remove HTML tags from TOC titles. - ## - ## A TTocEntry.header field already contains rendered HTML tags. Instead of - ## implementing a proper version of rstgen.renderRstToOut() which recursively - ## renders an rst tree to plain text, we simply remove text found between - ## angled brackets. Given the limited possibilities of rst inside TOC titles - ## this should be enough. - result = s - var first = result.find('<') - while first >= 0: - let last = result.find('>', first) - if last < 0: - # Abort, since we didn't found a closing angled bracket. - return - result.delete(first, last) - first = result.find('<', first) - proc genSection(d: PDoc, kind: TSymKind) = const sectionNames: array[skModule..skTemplate, string] = [ "Imports", "Types", "Vars", "Lets", "Consts", "Vars", "Procs", "Methods", @@ -568,13 +550,6 @@ proc genOutFile(d: PDoc): PRope = # Modules get an automatic title for the HTML, but no entry in the index. title = "Module " & extractFilename(changeFileExt(d.filename, "")) - # Generate entries also for other entries in the toc. - for tocPart in d.tocPart: - # Use spaces to indicate the current level for the output HTML. - assert tocPart.n.level >= 0 - setIndexTerm(d[], tocPart.refname, tocPart.header.stripTOCHTML, - repeatChar(max(0, tocPart.n.level), ' ') & tocPart.header) - let bodyname = if d.hasToc: "doc.body_toc" else: "doc.body_no_toc" content = ropeFormatNamedVars(getConfigVar(bodyname), ["title", "tableofcontents", "moduledesc", "date", "time", "content"], diff --git a/lib/packages/docutils/rstgen.nim b/lib/packages/docutils/rstgen.nim index d945a4b64b..9f1bde36aa 100644 --- a/lib/packages/docutils/rstgen.nim +++ b/lib/packages/docutils/rstgen.nim @@ -532,7 +532,24 @@ proc mergeIndexes*(dir: string): string = # ---------------------------------------------------------------------------- - +proc stripTOCHTML(s: string): string = + ## Ugly quick hack to remove HTML tags from TOC titles. + ## + ## A TTocEntry.header field already contains rendered HTML tags. Instead of + ## implementing a proper version of renderRstToOut() which recursively + ## renders an rst tree to plain text, we simply remove text found between + ## angled brackets. Given the limited possibilities of rst inside TOC titles + ## this should be enough. + result = s + var first = result.find('<') + while first >= 0: + let last = result.find('>', first) + if last < 0: + # Abort, since we didn't found a closing angled bracket. + return + result.delete(first, last) + first = result.find('<', first) + proc renderHeadline(d: PDoc, n: PRstNode, result: var string) = var tmp = "" for i in countup(0, len(n) - 1): renderRstToOut(d, n.sons[i], tmp) @@ -544,6 +561,11 @@ proc renderHeadline(d: PDoc, n: PRstNode, result: var string) = d.tocPart[length].refname = refname d.tocPart[length].n = n d.tocPart[length].header = tmp + + # Use spaces to indicate the current level for the output HTML index. + assert n.level >= 0 + setIndexTerm(d, refname, tmp.stripTOCHTML, + repeatChar(max(0, n.level), ' ') & tmp) dispA(d.target, result, "\n$3", From b1f26ae542adbdd7a076779f4aa1eb99f9636f1c Mon Sep 17 00:00:00 2001 From: Grzegorz Adam Hankiewicz Date: Sun, 20 Apr 2014 23:51:13 +0200 Subject: [PATCH 020/270] Picks the correct symbol text for theindex embedded tocs. --- lib/packages/docutils/rstgen.nim | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/lib/packages/docutils/rstgen.nim b/lib/packages/docutils/rstgen.nim index 9f1bde36aa..049d78b138 100644 --- a/lib/packages/docutils/rstgen.nim +++ b/lib/packages/docutils/rstgen.nim @@ -410,12 +410,13 @@ proc generateDocumentationTOC(entries: seq[TIndexEntry]): if rawLevel < 1: # This is a normal symbol, push it *inside* one level from the last one. levels[L].level = level + 1 + # Also, ignore the linkTitle and use directly the keyword. + levels[L].text = entry.keyword else: # The level did change, update the level indicator. level = rawLevel levels[L].level = rawLevel - - levels[L].text = rawText + levels[L].text = rawText inc L # Now generate hierarchical lists based on the precalculated levels. From 69fb40bd84c5b441e663cabdf072de7d16704eba Mon Sep 17 00:00:00 2001 From: Grzegorz Adam Hankiewicz Date: Mon, 21 Apr 2014 11:48:18 +0200 Subject: [PATCH 021/270] Refactors adhoc table of sequences into TIndexedDocs type. --- lib/packages/docutils/rstgen.nim | 154 ++++++++++++++++++------------- 1 file changed, 91 insertions(+), 63 deletions(-) diff --git a/lib/packages/docutils/rstgen.nim b/lib/packages/docutils/rstgen.nim index 049d78b138..a7745b2a58 100644 --- a/lib/packages/docutils/rstgen.nim +++ b/lib/packages/docutils/rstgen.nim @@ -208,6 +208,9 @@ proc dispA(target: TOutputTarget, dest: var string, if target != outLatex: addf(dest, xml, args) else: addf(dest, tex, args) +proc `or`(x, y: string): string {.inline.} = + result = if x.isNil: y else: x + proc renderRstToOut*(d: var TRstGenerator, n: PRstNode, result: var string) ## Writes into ``result`` the rst ast ``n`` using the ``d`` configuration. ## @@ -307,12 +310,32 @@ type linkTitle: string ## If not nil, contains a prettier text for the href linkDesc: string ## If not nil, the title attribute of the final href + TIndexedDocs {.pure, final.} = TTable[TIndexEntry, seq[TIndexEntry]] ## \ + ## Contains the index sequences for doc types. + ## + ## The key is a *fake* TIndexEntry which will contain the title of the + ## document in the `keyword` field and `link` will contain the html + ## filename for the document. `linkTitle` and `linkDesc` will be nil. The + ## value indexed by this TIndexEntry is a sequence with the real index + ## entries found in the ``.idx`` file. + + proc cmp(a, b: TIndexEntry): int = ## Sorts two ``TIndexEntry`` first by `keyword` field, then by `link`. result = cmpIgnoreStyle(a.keyword, b.keyword) if result == 0: result = cmpIgnoreStyle(a.link, b.link) +proc hash(x: TIndexEntry): int = + ## Returns the hash for the combined fields of the type. + ## + ## The hash is computed as the concatenated string of all available fields. + assert(not x.keyword.isNil) + assert(not x.link.isNil) + let value = x.keyword & x.link & (x.linkTitle or "") & (x.linkDesc or "") + result = value.hash + + proc `<-`(a: var TIndexEntry, b: TIndexEntry) = shallowCopy a.keyword, b.keyword shallowCopy a.link, b.link @@ -371,9 +394,6 @@ proc isDocumentationTitle(hyperlink: string): bool = ## for a more detailed explanation. result = hyperlink.find('#') < 0 -proc `or`(x, y: string): string {.inline.} = - result = if x.isNil: y else: x - proc stripTOCLevel(s: string): tuple[level: int, text: string] = ## Returns the *level* of the toc along with the text without it. for c in 0 .. ") level = newLevel -proc generateDocumentationTOC(entries: seq[TIndexEntry]): - tuple[toc, titleRef: string] = +proc generateDocumentationTOC(entries: seq[TIndexEntry]): string = ## Returns the sequence of index entries in an HTML hierarchical list. - result.toc = "" + result = "" # Build a list of levels and extracted titles to make processing easier. var + titleRef: string levels: seq[tuple[level: int, text: string]] L = 0 level = 1 @@ -420,34 +440,83 @@ proc generateDocumentationTOC(entries: seq[TIndexEntry]): inc L # Now generate hierarchical lists based on the precalculated levels. - result.toc = "
    \n" + result = "\n") - assert(not result.titleRef.isNil, "Can't use this proc on an API index") + result.add(level.indentToLevel(1) & "
\n") + assert(not titleRef.isNil, + "Can't use this proc on an API index, docs always have a title entry") -proc generateDocumentationIndex( - docs: TTable[string, seq[TIndexEntry]]): string = +proc generateDocumentationIndex(docs: TIndexedDocs): string = ## Returns all the documentation TOCs in an HTML hierarchical list. result = "" # Sort the titles to generate their toc in alphabetical order. - var titles = toSeq(keys[string, seq[TIndexEntry]](docs)) - sort(titles, system.cmp) + var titles = toSeq(keys[TIndexEntry, seq[TIndexEntry]](docs)) + sort(titles, cmp) for title in titles: - let (list, titleRef) = generateDocumentationTOC(docs[title]) - result.add("\n") + let tocList = generateDocumentationTOC(docs[title]) + result.add("\n") + +proc readIndexDir(dir: string): + tuple[symbols: seq[TIndexEntry], docs: TIndexedDocs] = + ## Walks `dir` reading ``.idx`` files converting them in TIndexEntry items. + ## + ## Returns the list of free symbol entries and the separate documentation + ## indexes found. See the documentation of ``mergeIndexes`` for details. + result.docs = initTable[TIndexEntry, seq[TIndexEntry]](32) + newSeq(result.symbols, 15_000) + setLen(result.symbols, 0) + var L = 0 + # Scan index files and build the list of symbols. + for kind, path in walkDir(dir): + if kind == pcFile and path.endsWith(IndexExt): + var + fileEntries: seq[TIndexEntry] + title: TIndexEntry + F = 0 + newSeq(fileEntries, 500) + setLen(fileEntries, 0) + for line in lines(path): + let s = line.find('\t') + if s < 0: continue + setLen(fileEntries, F+1) + fileEntries[F].keyword = line.substr(0, s-1) + fileEntries[F].link = line.substr(s+1) + # See if we detect a title, a link without a `#foobar` trailing part. + if title.keyword.isNil and fileEntries[F].link.isDocumentationTitle: + title.keyword = fileEntries[F].keyword + title.link = fileEntries[F].link + + if fileEntries[F].link.find('\t') > 0: + let extraCols = fileEntries[F].link.split('\t') + fileEntries[F].link = extraCols[0] + assert extraCols.len == 3 + fileEntries[F].linkTitle = extraCols[1].unquoteIndexColumn + fileEntries[F].linkDesc = extraCols[2].unquoteIndexColumn + else: + fileEntries[F].linkTitle = nil + fileEntries[F].linkDesc = nil + inc F + # Depending on type add this to the list of symbols or table of APIs. + if title.keyword.isNil: + setLen(result.symbols, L + F) + for i in 0 .. 0: - let extraCols = fileEntries[F].link.split('\t') - fileEntries[F].link = extraCols[0] - assert extraCols.len == 3 - fileEntries[F].linkTitle = extraCols[1].unquoteIndexColumn - fileEntries[F].linkDesc = extraCols[2].unquoteIndexColumn - else: - fileEntries[F].linkTitle = nil - fileEntries[F].linkDesc = nil - inc F - # Depending on type add this to the list of symbols or table of APIs. - if foundTitle.len > 0: - docs[foundTitle] = fileEntries - else: - setLen(symbols, L + F) - for i in 0 .. 0: + if symbols.len > 0: sortIndex(symbols) result.add("

API symbols

\n") result.add(generateSymbolIndex(symbols)) From 130a46df7d09d23ad06ccd936af982f4dab1faad Mon Sep 17 00:00:00 2001 From: Grzegorz Adam Hankiewicz Date: Mon, 21 Apr 2014 12:08:29 +0200 Subject: [PATCH 022/270] Generates quick jump list to documentation files. --- lib/packages/docutils/rstgen.nim | 30 ++++++++++++++++++++++++++---- 1 file changed, 26 insertions(+), 4 deletions(-) diff --git a/lib/packages/docutils/rstgen.nim b/lib/packages/docutils/rstgen.nim index a7745b2a58..8143b46d27 100644 --- a/lib/packages/docutils/rstgen.nim +++ b/lib/packages/docutils/rstgen.nim @@ -315,8 +315,9 @@ type ## ## The key is a *fake* TIndexEntry which will contain the title of the ## document in the `keyword` field and `link` will contain the html - ## filename for the document. `linkTitle` and `linkDesc` will be nil. The - ## value indexed by this TIndexEntry is a sequence with the real index + ## filename for the document. `linkTitle` and `linkDesc` will be nil. + ## + ## The value indexed by this TIndexEntry is a sequence with the real index ## entries found in the ``.idx`` file. @@ -466,8 +467,22 @@ proc generateDocumentationIndex(docs: TIndexedDocs): string = for title in titles: let tocList = generateDocumentationTOC(docs[title]) - result.add("\n") + result.add("\n") + +proc generateDocumentationJumps(docs: TIndexedDocs): string = + ## Returns a plain list of hyperlinks to documentation TOCs in HTML. + result = "Documents: " + + # Sort the titles to generate their toc in alphabetical order. + var titles = toSeq(keys[TIndexEntry, seq[TIndexEntry]](docs)) + sort(titles, cmp) + + var chunks: seq[string] = @[] + for title in titles: + chunks.add("" & title.keyword & "") + + result.add(chunks.join(", ") & ".
") proc readIndexDir(dir: string): tuple[symbols: seq[TIndexEntry], docs: TIndexedDocs] = @@ -516,6 +531,8 @@ proc readIndexDir(dir: string): result.symbols[L] = fileEntries[i] inc L else: + # Generate the symbolic anchor for index quickjumps. + title.linkTitle = "doc_toc_" & $result.docs.len result.docs[title] = fileEntries proc mergeIndexes*(dir: string): string = @@ -548,6 +565,11 @@ proc mergeIndexes*(dir: string): string = assert(not symbols.isNil) result = "" + # Generate a quick jump list of documents. + if docs.len > 0: + result.add(generateDocumentationJumps(docs)) + result.add("

") + # Generate the HTML block with API documents. if docs.len > 0: result.add("

Documentation files

\n") From fdf4bf2cfd1c696a76b23e00f96a76ad148f53e3 Mon Sep 17 00:00:00 2001 From: Grzegorz Adam Hankiewicz Date: Mon, 21 Apr 2014 12:22:18 +0200 Subject: [PATCH 023/270] Generates quick jump list to modules. --- lib/packages/docutils/rstgen.nim | 28 ++++++++++++++++++++++++---- 1 file changed, 24 insertions(+), 4 deletions(-) diff --git a/lib/packages/docutils/rstgen.nim b/lib/packages/docutils/rstgen.nim index 8143b46d27..225b272594 100644 --- a/lib/packages/docutils/rstgen.nim +++ b/lib/packages/docutils/rstgen.nim @@ -484,12 +484,24 @@ proc generateDocumentationJumps(docs: TIndexedDocs): string = result.add(chunks.join(", ") & ".
") +proc generateModuleJumps(modules: seq[string]): string = + ## Returns a plain list of hyperlinks to the list of modules. + result = "Modules: " + + var chunks: seq[string] = @[] + for name in modules: + chunks.add("" & name & "") + + result.add(chunks.join(", ") & ".
") + proc readIndexDir(dir: string): - tuple[symbols: seq[TIndexEntry], docs: TIndexedDocs] = + tuple[modules: seq[string], symbols: seq[TIndexEntry], docs: TIndexedDocs] = ## Walks `dir` reading ``.idx`` files converting them in TIndexEntry items. ## - ## Returns the list of free symbol entries and the separate documentation - ## indexes found. See the documentation of ``mergeIndexes`` for details. + ## Returns the list of found module names, the list of free symbol entries + ## and the different documentation indexes. The list of modules is sorted. + ## See the documentation of ``mergeIndexes`` for details. + result.modules = @[] result.docs = initTable[TIndexEntry, seq[TIndexEntry]](32) newSeq(result.symbols, 15_000) setLen(result.symbols, 0) @@ -530,11 +542,14 @@ proc readIndexDir(dir: string): for i in 0 .. ") + # Generate hyperlinks to all the linked modules. + if modules.len > 0: + result.add(generateModuleJumps(modules)) + result.add("

") + # Generate the HTML block with API documents. if docs.len > 0: result.add("

Documentation files

\n") From 7808ad97a569f4d202ae97520d3c45bd0063b508 Mon Sep 17 00:00:00 2001 From: Grzegorz Adam Hankiewicz Date: Mon, 21 Apr 2014 12:37:33 +0200 Subject: [PATCH 024/270] Indexes tocs of documents even if they don't show any. --- lib/packages/docutils/rstgen.nim | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/lib/packages/docutils/rstgen.nim b/lib/packages/docutils/rstgen.nim index 225b272594..854a79606e 100644 --- a/lib/packages/docutils/rstgen.nim +++ b/lib/packages/docutils/rstgen.nim @@ -633,11 +633,6 @@ proc renderHeadline(d: PDoc, n: PRstNode, result: var string) = d.tocPart[length].n = n d.tocPart[length].header = tmp - # Use spaces to indicate the current level for the output HTML index. - assert n.level >= 0 - setIndexTerm(d, refname, tmp.stripTOCHTML, - repeatChar(max(0, n.level), ' ') & tmp) - dispA(d.target, result, "\n$3", "\\rsth$4{$3}\\label{$2}\n", [$n.level, @@ -648,7 +643,12 @@ proc renderHeadline(d: PDoc, n: PRstNode, result: var string) = "\\rsth$4{$3}\\label{$2}\n", [ $n.level, refname, tmp, $chr(n.level - 1 + ord('A'))]) - + + # Generate index entry using spaces to indicate TOC level for the output HTML. + assert n.level >= 0 + setIndexTerm(d, refname, tmp.stripTOCHTML, + repeatChar(max(0, n.level), ' ') & tmp) + proc renderOverline(d: PDoc, n: PRstNode, result: var string) = if d.meta[metaTitle].len == 0: d.currentSection = d.meta[metaTitle] From 1f4f5d27eee7538bfc431e48ed5b50fdeeb7c265 Mon Sep 17 00:00:00 2001 From: Grzegorz Adam Hankiewicz Date: Mon, 21 Apr 2014 12:45:33 +0200 Subject: [PATCH 025/270] Updates some document titles to read better in the index. --- doc/apis.txt | 6 +++--- doc/c2nim.txt | 6 +++--- doc/endb.txt | 6 +++--- doc/estp.txt | 6 +++--- doc/nimgrep.txt | 6 +++--- doc/niminst.txt | 6 +++--- doc/subexes.txt | 1 - doc/tools.txt | 6 +++--- 8 files changed, 21 insertions(+), 22 deletions(-) diff --git a/doc/apis.txt b/doc/apis.txt index d76bd721e7..165279490e 100644 --- a/doc/apis.txt +++ b/doc/apis.txt @@ -1,6 +1,6 @@ -========== -API design -========== +================= +API naming design +================= The API is designed to be **easy to use** and consistent. Ease of use is measured by the number of calls to achieve a concrete high level action. diff --git a/doc/c2nim.txt b/doc/c2nim.txt index 6788ef569f..237af7fb22 100644 --- a/doc/c2nim.txt +++ b/doc/c2nim.txt @@ -1,6 +1,6 @@ -================================= - c2nim User's manual -================================= +======================= + c2nim User's manual +======================= :Author: Andreas Rumpf :Version: |nimrodversion| diff --git a/doc/endb.txt b/doc/endb.txt index d4437e5d92..2b50b044a3 100644 --- a/doc/endb.txt +++ b/doc/endb.txt @@ -1,6 +1,6 @@ -=========================================== - Embedded Nimrod Debugger User Guide -=========================================== +============================================== + Embedded Nimrod Debugger (ENDB) User Guide +============================================== :Author: Andreas Rumpf :Version: |nimrodversion| diff --git a/doc/estp.txt b/doc/estp.txt index 353e4576fd..94f4df09d3 100644 --- a/doc/estp.txt +++ b/doc/estp.txt @@ -1,6 +1,6 @@ -============================================ - Embedded Stack Trace Profiler User Guide -============================================ +=================================================== + Embedded Stack Trace Profiler (ESTP) User Guide +=================================================== :Author: Andreas Rumpf :Version: |nimrodversion| diff --git a/doc/nimgrep.txt b/doc/nimgrep.txt index c231bf31b3..67aaa427e4 100644 --- a/doc/nimgrep.txt +++ b/doc/nimgrep.txt @@ -1,6 +1,6 @@ -================================= - nimgrep User's manual -================================= +========================= + nimgrep User's manual +========================= :Author: Andreas Rumpf :Version: 0.9 diff --git a/doc/niminst.txt b/doc/niminst.txt index ebed396e4c..8d95d01c95 100644 --- a/doc/niminst.txt +++ b/doc/niminst.txt @@ -1,6 +1,6 @@ -================================= - niminst User's manual -================================= +========================= + niminst User's manual +========================= :Author: Andreas Rumpf :Version: |nimrodversion| diff --git a/doc/subexes.txt b/doc/subexes.txt index 10e0f4cc11..3b688fd0a0 100644 --- a/doc/subexes.txt +++ b/doc/subexes.txt @@ -1,4 +1,3 @@ -================================ Substitution Expressions (subex) ================================ diff --git a/doc/tools.txt b/doc/tools.txt index ae739e7785..0127bc4a8e 100644 --- a/doc/tools.txt +++ b/doc/tools.txt @@ -1,6 +1,6 @@ -===== -Tools -===== +=========================== +Tools available with Nimrod +=========================== The standard distribution ships with the following tools: From 61ded5ab6096effb247f8840450ef87ce855dbfa Mon Sep 17 00:00:00 2001 From: Grzegorz Adam Hankiewicz Date: Mon, 21 Apr 2014 21:07:13 +0200 Subject: [PATCH 026/270] Removes many fake `idx` entries. These *fake* entries are not needed any more now that the table of contents of documentation files appear in the index. --- doc/endb.txt | 6 +- doc/estp.txt | 2 +- doc/filters.txt | 8 +- doc/idetools.txt | 4 +- doc/intern.txt | 2 +- doc/manual.txt | 220 +++++++++++++++++++++++------------------------ doc/nimrodc.txt | 44 +++++----- doc/tut1.txt | 32 +++---- doc/tut2.txt | 16 ++-- 9 files changed, 167 insertions(+), 167 deletions(-) diff --git a/doc/endb.txt b/doc/endb.txt index 2b50b044a3..e9a01b583b 100644 --- a/doc/endb.txt +++ b/doc/endb.txt @@ -9,7 +9,7 @@ Nimrod comes with a platform independent debugger - -the `Embedded Nimrod Debugger`:idx: (`ENDB`:idx:). The debugger is +the Embedded Nimrod Debugger (ENDB). The debugger is *embedded* into your executable if it has been compiled with the ``--debugger:on`` command line option. This also defines the conditional symbol ``ENDB`` for you. @@ -102,7 +102,7 @@ necessary: A special pragma has been defined for this: The ``breakpoint`` pragma ------------------------- -The `breakpoint`:idx: pragma is syntactically a statement. It can be used +The ``breakpoint`` pragma is syntactically a statement. It can be used to mark the *following line* as a breakpoint: .. code-block:: Nimrod @@ -122,7 +122,7 @@ debugging. The ``watchpoint`` pragma ------------------------- -The `watchpoint`:idx: pragma is syntactically a statement. It can be used +The ``watchpoint`` pragma is syntactically a statement. It can be used to mark a location as a watchpoint: .. code-block:: Nimrod diff --git a/doc/estp.txt b/doc/estp.txt index 94f4df09d3..704bc33f29 100644 --- a/doc/estp.txt +++ b/doc/estp.txt @@ -7,7 +7,7 @@ Nimrod comes with a platform independent profiler - -the `Embedded Stack Trace Profiler`:idx: (`ESTP`:idx:). The profiler +the Embedded Stack Trace Profiler (ESTP). The profiler is *embedded* into your executable. To activate the profiler you need to do: * compile your program with the ``--profiler:on --stackTrace:on`` command diff --git a/doc/filters.txt b/doc/filters.txt index 683599a03d..961a0ea499 100644 --- a/doc/filters.txt +++ b/doc/filters.txt @@ -26,7 +26,7 @@ available parameters depend on the invoked filter. Pipe operator ============= -Filters can be combined with the ``|`` `pipe operator`:idx:\ :: +Filters can be combined with the ``|`` pipe operator:: #! strip(startswith="<") | stdtmpl #proc generateXML(name, age: string): string = @@ -46,7 +46,7 @@ after each filter application. Replace filter -------------- -The `replace`:idx: filter replaces substrings in each line. +The replace filter replaces substrings in each line. Parameters and their defaults: @@ -60,7 +60,7 @@ Parameters and their defaults: Strip filter ------------ -The `strip`:idx: filter simply removes leading and trailing whitespace from +The strip filter simply removes leading and trailing whitespace from each line. Parameters and their defaults: @@ -79,7 +79,7 @@ Parameters and their defaults: StdTmpl filter -------------- -The `stdtmpl`:idx: filter provides a simple templating engine for Nimrod. The +The stdtmpl filter provides a simple templating engine for Nimrod. The filter uses a line based parser: Lines prefixed with a *meta character* (default: ``#``) contain Nimrod code, other lines are verbatim. Because indentation-based parsing is not suited for a templating engine, control flow diff --git a/doc/idetools.txt b/doc/idetools.txt index d4f0f077de..65e4320022 100644 --- a/doc/idetools.txt +++ b/doc/idetools.txt @@ -16,7 +16,7 @@ Nimrod differs from many other compilers in that it is really fast, and being so fast makes it suited to provide external queries for text editors about the source code being written. Through the -`idetools`:idx: command of `the compiler `_, any IDE +``idetools`` command of `the compiler `_, any IDE can query a ``.nim`` source file and obtain useful information like definition of symbols or suggestions for completion. @@ -169,7 +169,7 @@ clicks it and after a second or two the IDE displays where that symbol is defined. Such latencies would be terrible for features like symbol suggestion, plus why wait at all if we can avoid it? -The idetools command can be run as a compiler service (`caas`:idx:), +The idetools command can be run as a compiler service (CAAS), where you first launch the compiler and it will stay online as a server, accepting queries in a telnet like fashion. The advantage of staying on is that for many queries the compiler can cache the diff --git a/doc/intern.txt b/doc/intern.txt index c602e49337..52b1af1904 100644 --- a/doc/intern.txt +++ b/doc/intern.txt @@ -156,7 +156,7 @@ generator. Compilation cache ================= -The implementation of the `compilation cache`:idx: is tricky: There are lots +The implementation of the compilation cache is tricky: There are lots of issues to be solved for the front- and backend. In the following sections *global* means *shared between modules* or *property of the whole program*. diff --git a/doc/manual.txt b/doc/manual.txt index 98af5aebc6..ea6a3fc41f 100644 --- a/doc/manual.txt +++ b/doc/manual.txt @@ -152,7 +152,7 @@ statements (simplified example):: Comments -------- -`Comments`:idx: start anywhere outside a string or character literal with the +Comments start anywhere outside a string or character literal with the hash character ``#``. Comments consist of a concatenation of `comment pieces`:idx:. A comment piece starts with ``#`` and runs until the end of the line. The end of line characters @@ -188,7 +188,7 @@ which code snippet the comment refers to. Identifiers & Keywords ---------------------- -`Identifiers`:idx: in Nimrod can be any string of letters, digits +Identifiers in Nimrod can be any string of letters, digits and underscores, beginning with a letter. Two immediate following underscores ``__`` are not allowed:: @@ -201,7 +201,7 @@ classified as a ``letter`` and may thus be part of an identifier but later versions of the language may assign some Unicode characters to belong to the operator characters instead. -The following `keywords`:idx: are reserved and cannot be used as identifiers: +The following keywords are reserved and cannot be used as identifiers: .. code-block:: nimrod :file: keywords.txt @@ -224,7 +224,7 @@ String literals Terminal symbol in the grammar: ``STR_LIT``. -`String literals`:idx: can be delimited by matching double quotes, and can +String literals can be delimited by matching double quotes, and can contain the following `escape sequences`:idx:\ : ================== =================================================== @@ -280,7 +280,7 @@ Raw string literals Terminal symbol in the grammar: ``RSTR_LIT``. -There are also `raw string literals`:idx: that are preceded with the +There are also raw string literals that are preceded with the letter ``r`` (or ``R``) and are delimited by matching double quotes (just like ordinary string literals) and do not interpret the escape sequences. This is especially convenient for regular expressions or Windows paths: @@ -313,7 +313,7 @@ Terminal symbols in the grammar: ``GENERALIZED_STR_LIT``, The construct ``identifier"string literal"`` (without whitespace between the identifier and the opening quotation mark) is a -`generalized raw string literal`:idx:. It is a shortcut for the construct +generalized raw string literal. It is a shortcut for the construct ``identifier(r"string literal")``, so it denotes a procedure call with a raw string literal as its only argument. Generalized raw string literals are especially convenient for embedding mini languages directly into Nimrod @@ -365,7 +365,7 @@ type is used for Unicode characters, it can represent any Unicode character. Numerical constants ------------------- -`Numerical constants`:idx: are of a single type and have the form:: +Numerical constants are of a single type and have the form:: hexdigit = digit | 'A'..'F' | 'a'..'f' octdigit = '0'..'7' @@ -440,7 +440,7 @@ is approximately 1.72826e35 according to the IEEE floating point standard. Operators --------- -In Nimrod one can define his own operators. An `operator`:idx: is any +In Nimrod one can define his own operators. An operator is any combination of the following characters:: = + - * / < > @@ -587,7 +587,7 @@ The grammar's start symbol is ``module``. Types ===== -All expressions have a `type`:idx: which is known at compile time. Nimrod +All expressions have a type which is known at compile time. Nimrod is statically typed. One can declare new types, which is in essence defining an identifier that can be used to denote this custom type. @@ -605,7 +605,7 @@ These are the major type classes: Ordinal types ------------- -`Ordinal types`:idx: have the following characteristics: +Ordinal types have the following characteristics: - Ordinal types are countable and ordered. This property allows the operation of functions as ``Inc``, ``Ord``, ``Dec`` on ordinal types to @@ -705,7 +705,7 @@ For further details, see `Convertible relation`_. Subrange types -------------- -A `subrange`:idx: type is a range of values from an ordinal type (the base +A subrange type is a range of values from an ordinal type (the base type). To define a subrange type, one must specify it's limiting values: the lowest and highest value of the type: @@ -806,7 +806,7 @@ the ``+``, ``-``, ``*``, ``/`` operators for floating point types. Boolean type ------------ -The `boolean`:idx: type is named `bool`:idx: in Nimrod and can be one of the two +The boolean type is named `bool`:idx: in Nimrod and can be one of the two pre-defined values ``true`` and ``false``. Conditions in while, if, elif, when statements need to be of type bool. @@ -830,7 +830,7 @@ The size of the bool type is one byte. Character type -------------- -The `character type`:idx: is named ``char`` in Nimrod. Its size is one byte. +The character type is named ``char`` in Nimrod. Its size is one byte. Thus it cannot represent an UTF-8 character, but a part of it. The reason for this is efficiency: for the overwhelming majority of use-cases, the resulting programs will still handle UTF-8 properly as UTF-8 was specially @@ -845,7 +845,7 @@ character. ``TRune`` is declared in the `unicode module `_. Enumeration types ----------------- -`Enumeration`:idx: types define a new type whose values consist of the ones +Enumeration types define a new type whose values consist of the ones specified. The values are ordered. Example: .. code-block:: nimrod @@ -915,7 +915,7 @@ via ``TMyEnum.value``: String type ----------- -All string literals are of the type `string`:idx:. A string in Nimrod is very +All string literals are of the type ``string``. A string in Nimrod is very similar to a sequence of characters. However, strings in Nimrod are both zero-terminated and have a length field. One can retrieve the length with the builtin ``len`` procedure; the length never counts the terminating zero. @@ -942,7 +942,7 @@ i-th *unichar*. The iterator ``runes`` from the `unicode module CString type ------------ -The `cstring`:idx: type represents a pointer to a zero-terminated char array +The ``cstring`` type represents a pointer to a zero-terminated char array compatible to the type ``char*`` in Ansi C. Its primary purpose lies in easy interfacing with C. The index operation ``s[i]`` means the i-th *char* of ``s``; however no bounds checking for ``cstring`` is performed making the @@ -968,20 +968,20 @@ not work. Structured types ---------------- -A variable of a `structured type`:idx: can hold multiple values at the same +A variable of a structured type can hold multiple values at the same time. Structured types can be nested to unlimited levels. Arrays, sequences, tuples, objects and sets belong to the structured types. Array and sequence types ------------------------ -`Arrays`:idx: are a homogeneous type, meaning that each element in the array +Arrays are a homogeneous type, meaning that each element in the array has the same type. Arrays always have a fixed length which is specified at compile time (except for open arrays). They can be indexed by any ordinal type. A parameter ``A`` may be an *open array*, in which case it is indexed by integers from 0 to ``len(A)-1``. An array expression may be constructed by the array constructor ``[]``. -`Sequences`:idx: are similar to arrays but of dynamic length which may change +Sequences are similar to arrays but of dynamic length which may change during runtime (like strings). Sequences are implemented as growable arrays, allocating pieces of memory as items are added. A sequence ``S`` is always indexed by integers from 0 to ``len(S)-1`` and its bounds are checked. @@ -1038,7 +1038,7 @@ supported because this is seldom needed and cannot be done efficiently. Varargs ------- -A `varargs`:idx: parameter is an openarray parameter that additionally +A ``varargs`` parameter is an openarray parameter that additionally allows to pass a variable number of arguments to a procedure. The compiler converts the list of arguments to an array implicitly: @@ -1073,7 +1073,7 @@ parameter ``a``. (Note that ``$`` applied to strings is a nop.) Tuples and object types ----------------------- -A variable of a `tuple`:idx: or `object`:idx: type is a heterogeneous storage +A variable of a tuple or object type is a heterogeneous storage container. A tuple or object defines various named *fields* of a type. A tuple also defines an *order* of the fields. Tuples are meant for heterogeneous storage @@ -1153,7 +1153,7 @@ For a ``ref object`` type ``system.new`` is invoked implicitly. Object variants --------------- Often an object hierarchy is overkill in certain situations where simple -`variant`:idx: types are needed. +variant types are needed. An example: @@ -1211,7 +1211,7 @@ branch switch ``system.reset`` has to be used. Set type -------- -The `set type`:idx: models the mathematical notion of a set. The set's +The set type models the mathematical notion of a set. The set's basetype can only be an ordinal type. The reason is that sets are implemented as high performance bit vectors. @@ -1246,7 +1246,7 @@ operation meaning Reference and pointer types --------------------------- -References (similar to `pointers`:idx: in other programming languages) are a +References (similar to pointers in other programming languages) are a way to introduce many-to-one relationships. This means different references can point to and modify the same location in memory (also called `aliasing`:idx:). @@ -1344,7 +1344,7 @@ Not nil annotation ------------------ All types for that ``nil`` is a valid value can be annotated to -exclude ``nil`` as a valid value with the `not nil`:idx: annotation: +exclude ``nil`` as a valid value with the ``not nil`` annotation: .. code-block:: nimrod type @@ -1369,7 +1369,7 @@ here. Memory regions -------------- -The types ``ref`` and ``ptr`` can get an optional `region`:idx: annotation. +The types ``ref`` and ``ptr`` can get an optional ``region`` annotation. A region has to be an object type. Regions are very useful to separate user space and kernel memory in the @@ -1428,7 +1428,7 @@ Future directions: Procedural type --------------- -A `procedural type`:idx: is internally a pointer to a procedure. ``nil`` is +A procedural type is internally a pointer to a procedure. ``nil`` is an allowed value for variables of a procedural type. Nimrod uses procedural types to achieve `functional`:idx: programming techniques. @@ -1536,7 +1536,7 @@ accesses its environment. If it does so, it has the calling convention Distinct type ------------- -A `distinct type`:idx: is new type derived from a `base type`:idx: that is +A ``distinct`` type is new type derived from a `base type`:idx: that is incompatible with its base type. In particular, it is an essential property of a distinct type that it **does not** imply a subtype relation between it and its base type. Explicit type conversions from a distinct type to its @@ -1654,7 +1654,7 @@ Currently only the dot accessor can be borrowed in this way. Void type --------- -The `void`:idx: type denotes the absense of any type. Parameters of +The ``void`` type denotes the absense of any type. Parameters of type ``void`` are treated as non-existent, ``void`` as a return type means that the procedure does not return a value: @@ -1887,7 +1887,7 @@ To be written. Statements and expressions ========================== -Nimrod uses the common statement/expression paradigm: `Statements`:idx: do not +Nimrod uses the common statement/expression paradigm: Statements do not produce a value in contrast to expressions. However, some expressions are statements. @@ -1904,7 +1904,7 @@ Statement list expression Statements can also occur in an expression context that looks like ``(stmt1; stmt2; ...; ex)``. This is called -an `statement list expression`:idx: or ``(;)``. The type +an statement list expression or ``(;)``. The type of ``(stmt1; stmt2; ...; ex)`` is the type of ``ex``. All the other statements must be of type ``void``. (One can use ``discard`` to produce a ``void`` type.) ``(;)`` does not introduce a new scope. @@ -1921,7 +1921,7 @@ Example: discard p(3, 4) # discard the return value of `p` -The `discard`:idx: statement evaluates its expression for side-effects and +The ``discard`` statement evaluates its expression for side-effects and throws the expression's resulting value away. Ignoring the return value of a procedure without using a discard statement is @@ -1949,7 +1949,7 @@ An empty ``discard`` statement is often used as a null statement: Var statement ------------- -`Var`:idx: statements declare new local and global variables and +Var statements declare new local and global variables and initialize them. A comma separated list of variables can be used to specify variables of the same type: @@ -2018,7 +2018,7 @@ initialized and does not rely on syntactic properties: let statement ------------- -A `Let`:idx: statement declares new local and global `single assignment`:idx: +A ``let`` statement declares new local and global `single assignment`:idx: variables and binds a value to them. The syntax is the of the ``var`` statement, except that the keyword ``var`` is replaced by the keyword ``let``. Let variables are not l-values and can thus not be passed to ``var`` parameters @@ -2060,7 +2060,7 @@ they contain such a type. Static statement/expression --------------------------- -A `static`:idx: statement/expression can be used to enforce compile +A static statement/expression can be used to enforce compile time evaluation explicitly. Enforced compile time evaluation can even evaluate code that has side effects: @@ -2094,7 +2094,7 @@ Example: else: echo("Boring name...") -The `if`:idx: statement is a simple way to make a branch in the control flow: +The ``if`` statement is a simple way to make a branch in the control flow: The expression after the keyword ``if`` is evaluated, if it is true the corresponding statements after the ``:`` are executed. Otherwise the expression after the ``elif`` is evaluated (if there is an @@ -2140,7 +2140,7 @@ Example: else: echo("unknown command") -The `case`:idx: statement is similar to the if statement, but it represents +The ``case`` statement is similar to the if statement, but it represents a multi-branch selection. The expression after the keyword ``case`` is evaluated and if its value is in a *slicelist* the corresponding statements (after the ``of`` keyword) are executed. If the value is not in any @@ -2194,7 +2194,7 @@ Example: else: echo("cannot happen!") -The `when`:idx: statement is almost identical to the ``if`` statement with some +The ``when`` statement is almost identical to the ``if`` statement with some exceptions: * Each condition (``expr``) has to be a constant expression (of type ``bool``). @@ -2216,7 +2216,7 @@ Example: .. code-block:: nimrod return 40+2 -The `return`:idx: statement ends the execution of the current procedure. +The ``return`` statement ends the execution of the current procedure. It is only allowed in procedures. If there is an ``expr``, this is syntactic sugar for: @@ -2243,7 +2243,7 @@ Example: .. code-block:: nimrod yield (1, 2, 3) -The `yield`:idx: statement is used instead of the ``return`` statement in +The ``yield`` statement is used instead of the ``return`` statement in iterators. It is only valid in iterators. Execution is returned to the body of the for loop that called the iterator. Yield does not end the iteration process, but execution is passed back to the iterator if the next iteration @@ -2266,7 +2266,7 @@ Example: break myblock # leave the block, in this case both for-loops echo(found) -The block statement is a means to group statements to a (named) `block`:idx:. +The block statement is a means to group statements to a (named) ``block``. Inside the block, the ``break`` statement is allowed to leave the block immediately. A ``break`` statement can contain a name of a surrounding block to specify which block is to leave. @@ -2280,7 +2280,7 @@ Example: .. code-block:: nimrod break -The `break`:idx: statement is used to leave a block immediately. If ``symbol`` +The ``break`` statement is used to leave a block immediately. If ``symbol`` is given, it is the name of the enclosing block that is to leave. If it is absent, the innermost block is left. @@ -2298,7 +2298,7 @@ Example: pw = readLine(stdin) -The `while`:idx: statement is executed until the ``expr`` evaluates to false. +The ``while`` statement is executed until the ``expr`` evaluates to false. Endless loops are no error. ``while`` statements open an `implicit block`, so that they can be left with a ``break`` statement. @@ -2306,7 +2306,7 @@ so that they can be left with a ``break`` statement. Continue statement ------------------ -A `continue`:idx: statement leads to the immediate next iteration of the +A ``continue`` statement leads to the immediate next iteration of the surrounding loop construct. It is only allowed within a loop. A continue statement is syntactic sugar for a nested block: @@ -2329,7 +2329,7 @@ Is equivalent to: Assembler statement ------------------- -The direct embedding of `assembler`:idx: code into Nimrod code is supported +The direct embedding of assembler code into Nimrod code is supported by the unsafe ``asm`` statement. Identifiers in the assembler code that refer to Nimrod identifiers shall be enclosed in a special character which can be specified in the statement's pragmas. The default special character is ``'`'``: @@ -2376,7 +2376,7 @@ Using statement **Warning**: The ``using`` statement is highly experimental! -The `using statement`:idx: provides syntactic convenience for procs that +The using statement provides syntactic convenience for procs that heavily use a single contextual parameter. When applied to a variable or a constant, it will instruct Nimrod to automatically consider the used symbol as a hidden leading parameter for any procedure calls, following the using @@ -2461,7 +2461,7 @@ the last expression as the result value, much like in an `expr` template. Table constructor ----------------- -A `table constructor`:idx: is syntactic sugar for an array constructor: +A table constructor is syntactic sugar for an array constructor: .. code-block:: nimrod {"key1": "value1", "key2", "key3": "value2"} @@ -2508,7 +2508,7 @@ only needed for low-level programming and are inherently unsafe. The addr operator ----------------- -The `addr`:idx: operator returns the address of an l-value. If the type of the +The ``addr`` operator returns the address of an l-value. If the type of the location is ``T``, the `addr` operator result is of the type ``ptr T``. An address is always an untraced reference. Taking the address of an object that resides on the stack is **unsafe**, as the pointer may live longer than the @@ -2609,7 +2609,7 @@ For object oriented programming, the syntax ``obj.method(args)`` can be used instead of ``method(obj, args)``. The parentheses can be omitted if there are no remaining arguments: ``obj.len`` (instead of ``len(obj)``). -This `method call syntax`:idx: is not restricted to objects, it can be used +This method call syntax is not restricted to objects, it can be used to supply any type of first argument for procedures: .. code-block:: nimrod @@ -2654,7 +2654,7 @@ Command invocation syntax ------------------------- Routines can be invoked without the ``()`` if the call is syntatically -a statement. This `command invocation syntax`:idx: also works for +a statement. This command invocation syntax also works for expressions, but then only a single argument may follow. This restriction means ``echo f 1, f 2`` is parsed as ``echo(f(1), f(2))`` and not as ``echo(f(1, f(2)))``. The method call syntax may be used to provide one @@ -2851,7 +2851,7 @@ The ``[]`` subscript operator for arrays/openarrays/sequences can be overloaded. Multi-methods ============= -Procedures always use static dispatch. `Multi-methods`:idx: use dynamic +Procedures always use static dispatch. Multi-methods use dynamic dispatch. .. code-block:: nimrod @@ -3104,7 +3104,7 @@ Example: line: int # the line the symbol was declared in code: PNode # the symbol's abstract syntax tree -A `type`:idx: section begins with the ``type`` keyword. It contains multiple +A type section begins with the ``type`` keyword. It contains multiple type definitions. A type definition binds a type to a name. Type definitions can be recursive or even mutually recursive. Mutually recursive types are only possible within a single ``type`` section. Nominal types like ``objects`` @@ -3141,7 +3141,7 @@ Example: close(f) -The statements after the `try`:idx: are executed in sequential order unless +The statements after the ``try`` are executed in sequential order unless an exception ``e`` is raised. If the exception type of ``e`` matches any listed in an ``except`` clause the corresponding statements are executed. The statements following the ``except`` clauses are called @@ -3163,7 +3163,7 @@ is not executed (if an exception occurs). Except and finally statements ----------------------------- -`except`:idx: and `finally`:idx: can also be used as a stand-alone statements. +``except`` and ``finally`` can also be used as a stand-alone statements. Any statements following them in the current block will be considered to be in an implicit try block: @@ -3212,9 +3212,9 @@ exception (unless a raise hook has been provided). OnRaise builtin --------------- -``system.onRaise`` can be used to override the behaviour of ``raise`` for a -single ``try`` statement. `onRaise`:idx: has to be called within the ``try`` -statement that should be affected. +`system.onRaise() `_ can be used to override the +behaviour of ``raise`` for a single ``try`` statement. ``onRaise`` has to be +called within the ``try`` statement that should be affected. This allows for a Lisp-like `condition system`:idx:\: @@ -3243,7 +3243,7 @@ Effect system Exception tracking ------------------ -Nimrod supports `exception tracking`:idx:. The `raises`:idx: pragma can be used +Nimrod supports exception tracking. The `raises`:idx: pragma can be used to explicitly define which exceptions a proc/iterator/method/converter is allowed to raise. The compiler verifies this: @@ -3351,7 +3351,7 @@ exception tracking. Effects pragma -------------- -The `effects`:idx: pragma has been designed to assist the programmer with the +The ``effects`` pragma has been designed to assist the programmer with the effects analysis. It is a statement that makes the compiler output all inferred effects up to the ``effects``'s position: @@ -3420,7 +3420,7 @@ Example: for str in inorder(root): writeln(stdout, str) -`Generics`:idx: are Nimrod's means to parametrize procs, iterators or types with +Generics are Nimrod's means to parametrize procs, iterators or types with `type parameters`:idx:. Depending on context, the brackets are used either to introduce type parameters or to instantiate a generic proc, iterator or type. @@ -3428,7 +3428,7 @@ introduce type parameters or to instantiate a generic proc, iterator or type. Is operator ----------- -The `is`:idx: operator checks for type equivalence at compile time. It is +The ``is`` operator checks for type equivalence at compile time. It is therefore very useful for type specialization within generic code: .. code-block:: nimrod @@ -3443,7 +3443,7 @@ therefore very useful for type specialization within generic code: Type operator ------------- -The `type`:idx: (in many other languages called `typeof`:idx:) operator can +The ``type`` (in many other languages called `typeof`:idx:) operator can be used to get the type of an expression: .. code-block:: nimrod @@ -3466,7 +3466,7 @@ other interpretations: Type Classes ------------ -A `type class`:idx: is a special pseudo-type that can be used to match against +A type class is a special pseudo-type that can be used to match against types in the context of overload resolution or the ``is`` operator. Nimrod supports the following built-in type classes: @@ -3684,7 +3684,7 @@ A symbol can be forced to be open by a `mixin`:idx: declaration: Bind statement -------------- -The `bind`:idx: statement is the counterpart to the ``mixin`` statement. It +The ``bind`` statement is the counterpart to the ``mixin`` statement. It can be used to explicitly declare identifiers that should be bound early (i.e. the identifiers should be looked up in the scope of the template/generic definition): @@ -3712,7 +3712,7 @@ scope is the default. Templates ========= -A `template`:idx: is a simple form of a macro: It is a simple substitution +A template is a simple form of a macro: It is a simple substitution mechanism that operates on Nimrod's abstract syntax trees. It is processed in the semantic pass of the compiler. @@ -3744,7 +3744,7 @@ expected. Ordinary vs immediate templates ------------------------------- -There are two different kinds of templates: `immediate`:idx: templates and +There are two different kinds of templates: immediate templates and ordinary templates. Ordinary templates take part in overloading resolution. As such their arguments need to be type checked before the template is invoked. So ordinary templates cannot receive undeclared identifiers: @@ -3939,7 +3939,7 @@ a template. ``inject`` and ``gensym`` have no effect in ``dirty`` templates. Macros ====== -A `macro`:idx: is a special kind of low level template. Macros can be used +A macro is a special kind of low level template. Macros can be used to implement `domain specific languages`:idx:. Like templates, macros come in the 2 flavors *immediate* and *ordinary*. @@ -4317,7 +4317,7 @@ This operator will be matched against assignments to missing fields. Term rewriting macros ===================== -`Term rewriting macros`:idx: are macros or templates that have not only +Term rewriting macros are macros or templates that have not only a *name* but also a *pattern* that is searched for after the semantic checking phase of the compiler: This means they provide an easy way to enhance the compilation pipeline with user defined optimizations: @@ -4653,7 +4653,7 @@ ordinary routines. Move optimization ----------------- -The ``call`` constraint is particularly useful to implement a `move`:idx: +The ``call`` constraint is particularly useful to implement a move optimization for types that have copying semantics: .. code-block:: nimrod @@ -4679,7 +4679,7 @@ optimization for types that have copying semantics: Modules ======= -Nimrod supports splitting a program into pieces by a `module`:idx: concept. +Nimrod supports splitting a program into pieces by a module concept. Each module needs to be in its own file and has its own `namespace`:idx:. Modules enable `information hiding`:idx: and `separate compilation`:idx:. A module may gain access to symbols of another module by the `import`:idx: @@ -4722,7 +4722,7 @@ This is best illustrated by an example: Import statement ~~~~~~~~~~~~~~~~ -After the `import`:idx: statement a list of module names can follow or a single +After the ``import`` statement a list of module names can follow or a single module name followed by an ``except`` to prevent some symbols to be imported: .. code-block:: nimrod @@ -4765,7 +4765,7 @@ Likewise the following does not make sense as the name is ``strutils`` already: From import statement ~~~~~~~~~~~~~~~~~~~~~ -After the `from`:idx: statement a module name follows followed by +After the ``from`` statement a module name follows followed by an ``import`` to list the symbols one likes to use without explict full qualification: @@ -4784,7 +4784,7 @@ in ``module``. Export statement ~~~~~~~~~~~~~~~~ -An `export`:idx: statement can be used for symbol fowarding so that client +An ``export`` statement can be used for symbol fowarding so that client modules don't need to import a module's dependencies: .. code-block:: nimrod @@ -4812,7 +4812,7 @@ Scope rules ----------- Identifiers are valid from the point of their declaration until the end of the block in which the declaration occurred. The range where the identifier -is known is the `scope`:idx: of the identifier. The exact scope of an +is known is the scope of the identifier. The exact scope of an identifier depends on the way it was declared. Block scope @@ -4885,7 +4885,7 @@ to access the feature becomes available. noSideEffect pragma ------------------- -The `noSideEffect`:idx: pragma is used to mark a proc/iterator to have no side +The ``noSideEffect`` pragma is used to mark a proc/iterator to have no side effects. This means that the proc/iterator only changes locations that are reachable from its parameters and the return value only depends on the arguments. If none of its parameters have the type ``var T`` @@ -4907,7 +4907,7 @@ proc with no side effects: destructor pragma ----------------- -The `destructor`:idx: pragma is used to mark a proc to act as a type destructor. +The ``destructor`` pragma is used to mark a proc to act as a type destructor. The proc must have a single parameter with a concrete type (the name of a generic type is allowed too). @@ -4967,25 +4967,25 @@ the ``finalizer`` parameter to ``new``. procvar pragma -------------- -The `procvar`:idx: pragma is used to mark a proc that it can be passed to a +The ``procvar`` pragma is used to mark a proc that it can be passed to a procedural variable. compileTime pragma ------------------ -The `compileTime`:idx: pragma is used to mark a proc to be used at compile +The ``compileTime`` pragma is used to mark a proc to be used at compile time only. No code will be generated for it. Compile time procs are useful as helpers for macros. noReturn pragma --------------- -The `noreturn`:idx: pragma is used to mark a proc that never returns. +The ``noreturn`` pragma is used to mark a proc that never returns. Acyclic pragma -------------- -The `acyclic`:idx: pragma can be used for object types to mark them as acyclic +The ``acyclic`` pragma can be used for object types to mark them as acyclic even though they seem to be cyclic. This is an **optimization** for the garbage collector to not consider objects of this type as part of a cycle: @@ -5016,13 +5016,13 @@ memory, but nothing worse happens. Final pragma ------------ -The `final`:idx: pragma can be used for an object type to specify that it +The ``final`` pragma can be used for an object type to specify that it cannot be inherited from. shallow pragma -------------- -The `shallow`:idx: pragma affects the semantics of a type: The compiler is +The ``shallow`` pragma affects the semantics of a type: The compiler is allowed to make a shallow copy. This can cause serious semantic issues and break memory safety! However, it can speed up assignments considerably, because the semantics of Nimrod require deep copying of sequences and strings. @@ -5042,14 +5042,14 @@ structure: Pure pragma ----------- -An object type can be marked with the `pure`:idx: pragma so that its type +An object type can be marked with the ``pure`` pragma so that its type field which is used for runtime type identification is omitted. This is necessary for binary compatibility with other compiled languages. AsmNoStackFrame pragma ---------------------- -A proc can be marked with the `AsmNoStackFrame`:idx: pragma to tell the compiler +A proc can be marked with the ``AsmNoStackFrame`` pragma to tell the compiler it should not generate a stack frame for the proc. There are also no exit statements like ``return result;`` generated and the generated C function is declared as ``__declspec(naked)`` or ``__attribute__((naked))`` (depending on @@ -5060,7 +5060,7 @@ assembler statements. error pragma ------------ -The `error`:idx: pragma is used to make the compiler output an error message +The ``error`` pragma is used to make the compiler output an error message with the given content. Compilation does not necessarily abort after an error though. @@ -5076,7 +5076,7 @@ operation is valid due to overloading and type conversions: fatal pragma ------------ -The `fatal`:idx: pragma is used to make the compiler output an error message +The ``fatal`` pragma is used to make the compiler output an error message with the given content. In contrast to the ``error`` pragma, compilation is guaranteed to be aborted by this pragma. Example: @@ -5086,17 +5086,17 @@ is guaranteed to be aborted by this pragma. Example: warning pragma -------------- -The `warning`:idx: pragma is used to make the compiler output a warning message +The ``warning`` pragma is used to make the compiler output a warning message with the given content. Compilation continues after the warning. hint pragma ----------- -The `hint`:idx: pragma is used to make the compiler output a hint message with +The ``hint`` pragma is used to make the compiler output a hint message with the given content. Compilation continues after the hint. line pragma ----------- -The `line`:idx: pragma can be used to affect line information of the annotated +The ``line`` pragma can be used to affect line information of the annotated statement as seen in stack backtraces: .. code-block:: nimrod @@ -5114,7 +5114,7 @@ If the ``line`` pragma is used with a parameter, the parameter needs be a linearScanEnd pragma -------------------- -The `linearScanEnd`:idx: pragma can be used to tell the compiler how to +The ``linearScanEnd`` pragma can be used to tell the compiler how to compile a Nimrod `case`:idx: statement. Syntactically it has to be used as a statement: @@ -5142,7 +5142,7 @@ whole ``case`` statement, the whole ``case`` statement uses linear scanning. computedGoto pragma ------------------- -The `computedGoto`:idx: pragma can be used to tell the compiler how to +The ``computedGoto`` pragma can be used to tell the compiler how to compile a Nimrod `case`:idx: in a ``while true`` statement. Syntactically it has to be used as a statement inside the loop: @@ -5187,7 +5187,7 @@ extension the pragma is simply ignored. unroll pragma ------------- -The `unroll`:idx: pragma can be used to tell the compiler that it should unroll +The ``unroll`` pragma can be used to tell the compiler that it should unroll a `for`:idx: or `while`:idx: loop for runtime efficiency: .. code-block:: nimrod @@ -5266,7 +5266,7 @@ but are used to override the settings temporarily. Example: register pragma --------------- -The `register`:idx: pragma is for variables only. It declares the variable as +The ``register`` pragma is for variables only. It declares the variable as ``register``, giving the compiler a hint that the variable should be placed in a hardware register for faster access. C compilers usually ignore this though and for good reasons: Often they do a better job without it anyway. @@ -5277,7 +5277,7 @@ example) it may provide benefits, though. global pragma ------------- -The `global`:idx: pragma can be applied to a variable within a proc to instruct +The ``global`` pragma can be applied to a variable within a proc to instruct the compiler to store it in a global location and initialize it once at program startup. @@ -5294,7 +5294,7 @@ and before any variable in a module that imports it. DeadCodeElim pragma ------------------- -The `deadCodeElim`:idx: pragma only applies to whole modules: It tells the +The ``deadCodeElim`` pragma only applies to whole modules: It tells the compiler to activate (or deactivate) dead code elimination for the module the pragma appears in. @@ -5312,7 +5312,7 @@ Example: .. NoForward pragma ---------------- - The `noforward`:idx: pragma can be used to turn on and off a special compilation + The ``noforward`` pragma can be used to turn on and off a special compilation mode that to large extent eliminates the need for forward declarations. In this mode, the proc definitions may appear out of order and the compiler will postpone their semantic analysis and compilation until it actually needs to generate code @@ -5360,7 +5360,7 @@ Example: Pragma pragma ------------- -The `pragma`:idx: pragma can be used to declare user defined pragmas. This is +The ``pragma`` pragma can be used to declare user defined pragmas. This is useful because Nimrod's templates and macros do not affect pragmas. User defined pragmas are in a different module-wide scope than all other symbols. They cannot be imported from a module. @@ -5404,7 +5404,7 @@ are documented here. Importc pragma -------------- -The `importc`:idx: pragma provides a means to import a proc or a variable +The ``importc`` pragma provides a means to import a proc or a variable from C. The optional argument is a string containing the C identifier. If the argument is missing, the C name is the Nimrod identifier *exactly as spelled*: @@ -5418,7 +5418,7 @@ the same feature under the same name. Exportc pragma -------------- -The `exportc`:idx: pragma provides a means to export a type, a variable, or a +The ``exportc`` pragma provides a means to export a type, a variable, or a procedure to C. Enums and constants can't be exported. The optional argument is a string containing the C identifier. If the argument is missing, the C name is the Nimrod identifier *exactly as spelled*: @@ -5432,7 +5432,7 @@ the same feature under the same name. Extern pragma ------------- -Like ``exportc`` or ``importc`` the `extern`:idx: pragma affects name +Like ``exportc`` or ``importc``, the ``extern`` pragma affects name mangling. The string literal passed to ``extern`` can be a format string: .. code-block:: Nimrod @@ -5445,7 +5445,7 @@ In the example the external name of ``p`` is set to ``prefixp``. Bycopy pragma ------------- -The `bycopy`:idx: pragma can be applied to an object or tuple type and +The ``bycopy`` pragma can be applied to an object or tuple type and instructs the compiler to pass the type by value to procs: .. code-block:: nimrod @@ -5457,13 +5457,13 @@ instructs the compiler to pass the type by value to procs: Byref pragma ------------ -The `byref`:idx: pragma can be applied to an object or tuple type and instructs +The ``byref`` pragma can be applied to an object or tuple type and instructs the compiler to pass the type by reference (hidden pointer) to procs. Varargs pragma -------------- -The `varargs`:idx: pragma can be applied to procedures only (and procedure +The ``varargs`` pragma can be applied to procedures only (and procedure types). It tells Nimrod that the proc can take a variable number of parameters after the last specified parameter. Nimrod string values will be converted to C strings automatically: @@ -5476,7 +5476,7 @@ strings automatically: Union pragma ------------ -The `union`:idx: pragma can be applied to any ``object`` type. It means all +The ``union`` pragma can be applied to any ``object`` type. It means all of the object's fields are overlaid in memory. This produces a ``union`` instead of a ``struct`` in the generated C/C++ code. The object declaration then must not use inheritance or any GC'ed memory but this is currently not @@ -5487,7 +5487,7 @@ should scan unions conservatively. Packed pragma ------------- -The `packed`:idx: pragma can be applied to any ``object`` type. It ensures +The ``packed`` pragma can be applied to any ``object`` type. It ensures that the fields of an object are packed back-to-back in memory. It is useful to store packets or messages from/to network or hardware drivers, and for interoperability with C. Combining packed pragma with inheritance is not @@ -5498,7 +5498,7 @@ compile-time error. Usage with inheritance should be defined and documented. Unchecked pragma ---------------- -The `unchecked`:idx: pragma can be used to mark a named array as ``unchecked`` +The ``unchecked`` pragma can be used to mark a named array as ``unchecked`` meaning its bounds are not checked. This is often useful when one wishes to implement his own flexibly sized arrays. Additionally an unchecked array is translated into a C array of undetermined size: @@ -5533,7 +5533,7 @@ runtime size of the array. Dynlib pragma for import ------------------------ -With the `dynlib`:idx: pragma a procedure or a variable can be imported from +With the ``dynlib`` pragma a procedure or a variable can be imported from a dynamic library (``.dll`` files for Windows, ``lib*.so`` files for UNIX). The non-optional argument has to be the name of the dynamic library: @@ -5623,7 +5623,7 @@ Thread pragma ------------- A proc that is executed as a new thread of execution should be marked by the -`thread pragma`:idx:. The compiler checks procedures marked as ``thread`` for +``thread`` pragma. The compiler checks procedures marked as ``thread`` for violations of the `no heap sharing restriction`:idx:\: This restriction implies that it is invalid to construct a data structure that consists of memory allocated from different (thread local) heaps. @@ -5664,7 +5664,7 @@ Future directions: Threadvar pragma ---------------- -A global variable can be marked with the `threadvar`:idx: pragma; it is +A global variable can be marked with the ``threadvar`` pragma; it is a `thread-local`:idx: variable then: .. code-block:: nimrod @@ -5716,7 +5716,7 @@ Taint mode ========== The Nimrod compiler and most parts of the standard library support -a `taint mode`:idx:. Input strings are declared with the `TaintedString`:idx: +a taint mode. Input strings are declared with the `TaintedString`:idx: string type declared in the ``system`` module. If the taint mode is turned on (via the ``--taintMode:on`` command line diff --git a/doc/nimrodc.txt b/doc/nimrodc.txt index 52e0a6eaf4..132ea42397 100644 --- a/doc/nimrodc.txt +++ b/doc/nimrodc.txt @@ -116,7 +116,7 @@ The default build of a project is a `debug build`:idx:. To compile a Search path handling -------------------- -Nimrod has the concept of a global `search path`:idx: (PATH) that is queried to +Nimrod has the concept of a global search path (PATH) that is queried to determine where to find imported modules or include files. If multiple files are found an ambiguity error is produced. @@ -174,7 +174,7 @@ is not shared between different projects. Cross compilation ================= -To `cross compile`:idx:, use for example:: +To cross compile, use for example:: nimrod c --cpu:i386 --os:linux --compile_only --gen_script myproject.nim @@ -200,7 +200,7 @@ DLL generation Nimrod supports the generation of DLLs. However, there must be only one instance of the GC per process/address space. This instance is contained in -``nimrtl.dll``. This means that every generated Nimrod `DLL`:idx: depends +``nimrtl.dll``. This means that every generated Nimrod DLL depends on ``nimrtl.dll``. To generate the "nimrtl.dll" file, use the command:: nimrod c -d:release lib/nimrtl.nim @@ -251,7 +251,7 @@ generator and are subject to change. NoDecl pragma ------------- -The `noDecl`:idx: pragma can be applied to almost any symbol (variable, proc, +The ``noDecl`` pragma can be applied to almost any symbol (variable, proc, type, etc.) and is sometimes useful for interoperability with C: It tells Nimrod that it should not generate a declaration for the symbol in the C code. For example: @@ -268,7 +268,7 @@ However, the ``header`` pragma is often the better alternative. Header pragma ------------- -The `header`:idx: pragma is very similar to the ``noDecl`` pragma: It can be +The ``header`` pragma is very similar to the ``noDecl`` pragma: It can be applied to almost any symbol and specifies that it should not be declared and instead the generated code should contain an ``#include``: @@ -287,7 +287,7 @@ encloses the header file in ``""`` in the generated C code. IncompleteStruct pragma ----------------------- -The `incompleteStruct`:idx: pragma tells the compiler to not use the +The ``incompleteStruct`` pragma tells the compiler to not use the underlying C ``struct`` in a ``sizeof`` expression: .. code-block:: Nimrod @@ -298,7 +298,7 @@ underlying C ``struct`` in a ``sizeof`` expression: Compile pragma -------------- -The `compile`:idx: pragma can be used to compile and link a C/C++ source file +The ``compile`` pragma can be used to compile and link a C/C++ source file with the project: .. code-block:: Nimrod @@ -311,7 +311,7 @@ of the file. Link pragma ----------- -The `link`:idx: pragma can be used to link an additional file with the project: +The ``link`` pragma can be used to link an additional file with the project: .. code-block:: Nimrod {.link: "myfile.o".} @@ -319,7 +319,7 @@ The `link`:idx: pragma can be used to link an additional file with the project: PassC pragma ------------ -The `passC`:idx: pragma can be used to pass additional parameters to the C +The ``passC`` pragma can be used to pass additional parameters to the C compiler like you would using the commandline switch ``--passC``: .. code-block:: Nimrod @@ -333,7 +333,7 @@ embed parameters from an external command at compile time: PassL pragma ------------ -The `passL`:idx: pragma can be used to pass additional parameters to the linker +The ``passL`` pragma can be used to pass additional parameters to the linker like you would using the commandline switch ``--passL``: .. code-block:: Nimrod @@ -348,7 +348,7 @@ embed parameters from an external command at compile time: Emit pragma ----------- -The `emit`:idx: pragma can be used to directly affect the output of the +The ``emit`` pragma can be used to directly affect the output of the compiler's code generator. So it makes your code unportable to other code generators/backends. Its usage is highly discouraged! However, it can be extremely useful for interfacing with `C++`:idx: or `Objective C`:idx: code. @@ -370,7 +370,7 @@ Example: ImportCpp pragma ---------------- -The `importcpp`:idx: pragma can be used to import `C++`:idx: methods. The +The ``importcpp`` pragma can be used to import `C++`:idx: methods. The generated code then uses the C++ method calling syntax: ``obj->method(arg)``. In addition with the ``header`` and ``emit`` pragmas this allows *sloppy* interfacing with libraries written in C++: @@ -408,7 +408,7 @@ emits C++ code. ImportObjC pragma ----------------- -The `importobjc`:idx: pragma can be used to import `Objective C`:idx: methods. +The ``importobjc`` pragma can be used to import `Objective C`:idx: methods. The generated code then uses the Objective C method calling syntax: ``[obj method param1: arg]``. In addition with the ``header`` and ``emit`` pragmas this allows *sloppy* @@ -458,7 +458,7 @@ emits Objective C code. CodegenDecl pragma ------------------ -The `codegenDecl`:idx: pragma can be used to directly influence Nimrod's code +The ``codegenDecl`` pragma can be used to directly influence Nimrod's code generator. It receives a format string that determines how the variable or proc is declared in the generated code: @@ -473,7 +473,7 @@ proc is declared in the generated code: InjectStmt pragma ----------------- -The `injectStmt`:idx: pragma can be used to inject a statement before every +The ``injectStmt`` pragma can be used to inject a statement before every other statement in the current module. It is only supposed to be used for debugging: @@ -485,28 +485,28 @@ debugging: LineDir option -------------- -The `lineDir`:idx: option can be turned on or off. If turned on the +The ``lineDir`` option can be turned on or off. If turned on the generated C code contains ``#line`` directives. This may be helpful for debugging with GDB. StackTrace option ----------------- -If the `stackTrace`:idx: option is turned on, the generated C contains code to +If the ``stackTrace`` option is turned on, the generated C contains code to ensure that proper stack traces are given if the program crashes or an uncaught exception is raised. LineTrace option ---------------- -The `lineTrace`:idx: option implies the ``stackTrace`` option. If turned on, +The ``lineTrace`` option implies the ``stackTrace`` option. If turned on, the generated C contains code to ensure that proper stack traces with line number information are given if the program crashes or an uncaught exception is raised. Debugger option --------------- -The `debugger`:idx: option enables or disables the *Embedded Nimrod Debugger*. +The ``debugger`` option enables or disables the *Embedded Nimrod Debugger*. See the documentation of endb_ for further information. @@ -518,7 +518,7 @@ ENDB. See the documentation of `endb `_ for further information. Volatile pragma --------------- -The `volatile`:idx: pragma is for variables only. It declares the variable as +The ``volatile`` pragma is for variables only. It declares the variable as ``volatile``, whatever that means in C/C++ (its semantics are not well defined in C/C++). @@ -530,7 +530,7 @@ DynlibOverride By default Nimrod's ``dynlib`` pragma causes the compiler to generate ``GetProcAddress`` (or their Unix counterparts) -calls to bind to a DLL. With the `dynlibOverride`:idx: command line switch this +calls to bind to a DLL. With the ``dynlibOverride`` command line switch this can be prevented and then via ``--passL`` the static library can be linked against. For instance, to link statically against Lua this command might work on Linux:: @@ -556,7 +556,7 @@ for further information. Nimrod interactive mode ======================= -The Nimrod compiler supports an `interactive mode`:idx:. This is also known as +The Nimrod compiler supports an interactive mode. This is also known as a `REPL`:idx: (*read eval print loop*). If Nimrod has been built with the ``-d:useGnuReadline`` switch, it uses the GNU readline library for terminal input management. To start Nimrod in interactive mode use the command diff --git a/doc/tut1.txt b/doc/tut1.txt index 46eda7ae37..8c6f140eb5 100644 --- a/doc/tut1.txt +++ b/doc/tut1.txt @@ -110,7 +110,7 @@ useful for embedding HTML code templates for example. Comments -------- -`Comments`:idx: start anywhere outside a string or character literal with the +Comments start anywhere outside a string or character literal with the hash character ``#``. Documentation comments start with ``##``. Multiline comments need to be aligned at the same column: @@ -224,7 +224,7 @@ different values! For safety use only constant values. Constants ========= -`Constants`:idx: are symbols which are bound to a value. The constant's value +Constants are symbols which are bound to a value. The constant's value cannot change. The compiler must be able to evaluate the expression in a constant declaration at compile time: @@ -369,7 +369,7 @@ he types in nothing (only presses RETURN). For statement ------------- -The `for`:idx: statement is a construct to loop over any element an *iterator* +The ``for`` statement is a construct to loop over any element an *iterator* provides. The example uses the built-in ``countup`` iterator: .. code-block:: nimrod @@ -481,7 +481,7 @@ Example: else: echo("unknown operating system") -The `when`:idx: statement is almost identical to the ``if`` statement with some +The ``when`` statement is almost identical to the ``if`` statement with some differences: * Each condition has to be a constant expression since it is evaluated by the @@ -791,7 +791,7 @@ However, this cannot be done for mutually recursive procedures: Here ``odd`` depends on ``even`` and vice versa. Thus ``even`` needs to be introduced to the compiler before it is completely defined. The syntax for -such a `forward declaration`:idx: is simple: just omit the ``=`` and the +such a forward declaration is simple: just omit the ``=`` and the procedure's body. Later versions of the language may get rid of the need for forward @@ -863,7 +863,7 @@ that are available for them in detail. Booleans -------- -The `boolean`:idx: type is named ``bool`` in Nimrod and consists of the two +The boolean type is named ``bool`` in Nimrod and consists of the two pre-defined values ``true`` and ``false``. Conditions in while, if, elif, when statements need to be of type bool. @@ -1030,7 +1030,7 @@ Enumeration and object types cannot be defined on the fly, but only within a Enumerations ------------ -A variable of an `enumeration`:idx: type can only be assigned a value of a +A variable of an enumeration type can only be assigned a value of a limited set. This set consists of ordered symbols. Each symbol is mapped to an integer value internally. The first symbol is represented at runtime by 0, the second by 1 and so on. Example: @@ -1069,7 +1069,7 @@ An explicit ordered enum can have *holes*: Ordinal types ------------- Enumerations without holes, integer types, ``char`` and ``bool`` (and -subranges) are called `ordinal`:idx: types. Ordinal types have quite +subranges) are called ordinal types. Ordinal types have quite a few special operations: ----------------- -------------------------------------------------------- @@ -1094,7 +1094,7 @@ checks turned on.) Subranges --------- -A `subrange`:idx: type is a range of values from an integer or enumeration type +A subrange type is a range of values from an integer or enumeration type (the base type). Example: .. code-block:: nimrod @@ -1117,7 +1117,7 @@ avoid this common programming error. Sets ---- -The `set type`:idx: models the mathematical notion of a set. The set's +The set type models the mathematical notion of a set. The set's basetype can only be an ordinal type. The reason is that sets are implemented as high performance bit vectors. @@ -1161,7 +1161,7 @@ constants that should be ``or``'ed together. Arrays ------ -An `array`:idx: is a simple fixed length container. Each element in +An array is a simple fixed length container. Each element in the array has the same type. The array's index type can be any ordinal type. Arrays can be constructed via ``[]``: @@ -1253,7 +1253,7 @@ to specify a range from zero to the specified index minus one: Sequences --------- -`Sequences`:idx: are similar to arrays but of dynamic length which may change +Sequences are similar to arrays but of dynamic length which may change during runtime (like strings). Since sequences are resizable they are always allocated on the heap and garbage collected. @@ -1471,7 +1471,7 @@ won't compile: Reference and pointer types --------------------------- -References (similar to `pointers`:idx: in other programming languages) are a +References (similar to pointers in other programming languages) are a way to introduce many-to-one relationships. This means different references can point to and modify the same location in memory. @@ -1513,7 +1513,7 @@ If a reference points to *nothing*, it has the value ``nil``. Procedural type --------------- -A `procedural type`:idx: is a (somewhat abstract) pointer to a procedure. +A procedural type is a (somewhat abstract) pointer to a procedure. ``nil`` is an allowed value for a variable of a procedural type. Nimrod uses procedural types to achieve `functional`:idx: programming techniques. @@ -1543,7 +1543,7 @@ listed in the `manual `_. Modules ======= -Nimrod supports splitting a program into pieces with a `module`:idx: concept. +Nimrod supports splitting a program into pieces with a module concept. Each module is in its own file. Modules enable `information hiding`:idx: and `separate compilation`:idx:. A module may gain access to symbols of another module by the `import`:idx: statement. Only top-level symbols that are marked @@ -1698,7 +1698,7 @@ define a shorter alias to use when qualifying symbols. Include statement ----------------- -The `include`:idx: statement does something fundamentally different than +The ``include`` statement does something fundamentally different than importing a module: it merely includes the contents of a file. The ``include`` statement is useful to split up a large module into several files: diff --git a/doc/tut2.txt b/doc/tut2.txt index ea6733c070..11b485f507 100644 --- a/doc/tut2.txt +++ b/doc/tut2.txt @@ -135,7 +135,7 @@ The ``EInvalidObjectConversion`` exception is raised if ``x`` is not a Object variants --------------- Often an object hierarchy is overkill in certain situations where simple -`variant`:idx: types are needed. +variant types are needed. An example: @@ -182,7 +182,7 @@ bound to a class. This has disadvantages: ``join`` a string method or an array method? Nimrod avoids these problems by not assigning methods to a class. All methods -in Nimrod are `multi-methods`:idx:. As we will see later, multi-methods are +in Nimrod are multi-methods. As we will see later, multi-methods are distinguished from procs only for dynamic binding purposes. @@ -194,7 +194,7 @@ The syntax ``obj.method(args)`` can be used instead of ``method(obj, args)``. If there are no remaining arguments, the parentheses can be omitted: ``obj.len`` (instead of ``len(obj)``). -This `method call syntax`:idx: is not restricted to objects, it can be used +This method call syntax is not restricted to objects, it can be used for any type: .. code-block:: nimrod @@ -343,7 +343,7 @@ evaluation or dead code elimination do not work with methods. Exceptions ========== -In Nimrod `exceptions`:idx: are objects. By convention, exception types are +In Nimrod exceptions are objects. By convention, exception types are prefixed with an 'E', not 'T'. The `system `_ module defines an exception hierarchy that you might want to stick to. Exceptions derive from E_Base, which provides the common interface. @@ -380,7 +380,7 @@ the template ``newException`` in the ``system`` module can be used: Try statement ------------- -The `try`:idx: statement handles exceptions: +The ``try`` statement handles exceptions: .. code-block:: nimrod # read the first two lines of a text file that should contain numbers @@ -513,7 +513,7 @@ procs with the list of raised exceptions. You can read more about Nimrod's Generics ======== -`Generics`:idx: are Nimrod's means to parametrize procs, iterators or types +Generics are Nimrod's means to parametrize procs, iterators or types with `type parameters`:idx:. They are most useful for efficient type safe containers: @@ -714,9 +714,9 @@ know how the Nimrod concrete syntax is converted to an abstract syntax tree (AST). The AST is documented in the `macros `_ module. Once your macro is finished, there are two ways to invoke it: -(1) invoking a macro like a procedure call (`expression macros`:idx:) +(1) invoking a macro like a procedure call (expression macros) (2) invoking a macro with the special ``macrostmt`` - syntax (`statement macros`:idx:) + syntax (statement macros) Expression Macros From d89918ab46c1c89e35c75bef0348307d06112079 Mon Sep 17 00:00:00 2001 From: Grzegorz Adam Hankiewicz Date: Mon, 21 Apr 2014 14:39:50 +0200 Subject: [PATCH 027/270] Links naming api with coding guidelines. --- doc/apis.txt | 7 +++++++ doc/intern.txt | 6 ++++-- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/doc/apis.txt b/doc/apis.txt index 165279490e..9906f6bbcc 100644 --- a/doc/apis.txt +++ b/doc/apis.txt @@ -79,3 +79,10 @@ string str identifier ident indentation indent ------------------- ------------ -------------------------------------- + + +Coding Guidelines +================= + +For coding guidelines see the `Internals of the Nimrod Compiler +`_ documentation. diff --git a/doc/intern.txt b/doc/intern.txt index 52b1af1904..ec6db8709f 100644 --- a/doc/intern.txt +++ b/doc/intern.txt @@ -69,8 +69,10 @@ Coding Guidelines * Max line length is 80 characters. * Provide spaces around binary operators if that enhances readability. * Use a space after a colon, but not before it. -* Start types with a capital ``T``, unless they are pointers/references which start - with ``P``. +* Start types with a capital ``T``, unless they are pointers/references which + start with ``P``. + +See also the `API naming design `_ document. Porting to new platforms From 831e12d8f878d27a1099ed4f2e696aabb35fdc64 Mon Sep 17 00:00:00 2001 From: Grzegorz Adam Hankiewicz Date: Mon, 21 Apr 2014 19:49:10 +0200 Subject: [PATCH 028/270] Fixes repeated index terms leading to same HTML anchor. Instead of a hash for the rst node tree (which will always be the same) a simple counter table is used. --- lib/packages/docutils/rstgen.nim | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/lib/packages/docutils/rstgen.nim b/lib/packages/docutils/rstgen.nim index 854a79606e..10cb1e486f 100644 --- a/lib/packages/docutils/rstgen.nim +++ b/lib/packages/docutils/rstgen.nim @@ -57,6 +57,9 @@ type currentSection: string ## \ ## Stores the empty string or the last headline/overline found in the rst ## document, so it can be used as a prettier name for term index generation. + seenIndexTerms: TTable[string, int] ## \ + ## Keeps count of same text index terms to generate different identifiers + ## for hyperlinks. See renderIndexTerm proc for details. PDoc = var TRstGenerator ## Alias to type less. @@ -112,6 +115,7 @@ proc initRstGenerator*(g: var TRstGenerator, target: TOutputTarget, g.options = options g.findFile = findFile g.currentSection = "" + g.seenIndexTerms = initTable[string, int]() g.msgHandler = msgHandler let s = config["split.item.toc"] @@ -295,8 +299,20 @@ proc hash(n: PRstNode): int = result = result !& hash(n.sons[i]) result = !$result -proc renderIndexTerm(d: PDoc, n: PRstNode, result: var string) = - let id = rstnodeToRefname(n) & '_' & $abs(hash(n)) +proc renderIndexTerm*(d: PDoc, n: PRstNode, result: var string) = + ## Renders the string decorated within \`foobar\`\:idx\: markers. + ## + ## Additionally adds the encosed text to the index as a term. Since we are + ## interested in different instances of the same term to have different + ## entries, a table is used to keep track of the amount of times a term has + ## previously appeared to give a different identifier value for each. + let refname = n.rstnodeToRefname + if d.seenIndexTerms.hasKey(refname): + d.seenIndexTerms[refname] = d.seenIndexTerms[refname] + 1 + else: + d.seenIndexTerms[refname] = 1 + let id = refname & '_' & $d.seenIndexTerms[refname] + var term = "" renderAux(d, n, term) setIndexTerm(d, id, term, d.currentSection) From ed713a8f5e78474efa09ba1fd8f30a5f38884dd0 Mon Sep 17 00:00:00 2001 From: Grzegorz Adam Hankiewicz Date: Mon, 21 Apr 2014 19:56:59 +0200 Subject: [PATCH 029/270] Fixes title and subtitle not setting the current section. --- lib/packages/docutils/rstgen.nim | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/packages/docutils/rstgen.nim b/lib/packages/docutils/rstgen.nim index 10cb1e486f..bd89fc4bc1 100644 --- a/lib/packages/docutils/rstgen.nim +++ b/lib/packages/docutils/rstgen.nim @@ -667,13 +667,13 @@ proc renderHeadline(d: PDoc, n: PRstNode, result: var string) = proc renderOverline(d: PDoc, n: PRstNode, result: var string) = if d.meta[metaTitle].len == 0: - d.currentSection = d.meta[metaTitle] for i in countup(0, len(n)-1): renderRstToOut(d, n.sons[i], d.meta[metaTitle]) + d.currentSection = d.meta[metaTitle] elif d.meta[metaSubtitle].len == 0: - d.currentSection = d.meta[metaSubtitle] for i in countup(0, len(n)-1): renderRstToOut(d, n.sons[i], d.meta[metaSubtitle]) + d.currentSection = d.meta[metaSubtitle] else: var tmp = "" for i in countup(0, len(n) - 1): renderRstToOut(d, n.sons[i], tmp) From fc0e43d6341bbd38aa84242cf21c2f31b07275f7 Mon Sep 17 00:00:00 2001 From: Grzegorz Adam Hankiewicz Date: Mon, 21 Apr 2014 20:11:22 +0200 Subject: [PATCH 030/270] Adds abstypes.txt to list of documentation to build. --- web/nimrod.ini | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/web/nimrod.ini b/web/nimrod.ini index 14701ecea4..16ea59f1bf 100644 --- a/web/nimrod.ini +++ b/web/nimrod.ini @@ -37,7 +37,7 @@ UNIX. We don't believe this to be a coincidence. - Jeremy S. Anderson.""" [Documentation] doc: "endb;intern;apis;lib;manual;tut1;tut2;nimrodc;overview;filters;trmacros" -doc: "tools;c2nim;niminst;nimgrep;gc;estp;idetools;docgen" +doc: "tools;c2nim;niminst;nimgrep;gc;estp;idetools;docgen;abstypes" pdf: "manual;lib;tut1;tut2;nimrodc;c2nim;niminst;gc" srcdoc2: "system.nim;impure/graphics;wrappers/sdl" srcdoc2: "core/macros;pure/marshal;core/typeinfo;core/unsigned" From d31638a10360543d3b7ff0e2290c64f95031e7bc Mon Sep 17 00:00:00 2001 From: Grzegorz Adam Hankiewicz Date: Mon, 21 Apr 2014 20:33:08 +0200 Subject: [PATCH 031/270] Reduces length of lines to remove compilation warnings. --- lib/packages/docutils/rstgen.nim | 30 ++++++++++++++++-------------- 1 file changed, 16 insertions(+), 14 deletions(-) diff --git a/lib/packages/docutils/rstgen.nim b/lib/packages/docutils/rstgen.nim index bd89fc4bc1..a70debbaa3 100644 --- a/lib/packages/docutils/rstgen.nim +++ b/lib/packages/docutils/rstgen.nim @@ -232,7 +232,7 @@ proc renderRstToOut*(d: var TRstGenerator, n: PRstNode, result: var string) proc renderAux(d: PDoc, n: PRstNode, result: var string) = for i in countup(0, len(n)-1): renderRstToOut(d, n.sons[i], result) -proc renderAux(d: PDoc, n: PRstNode, frmtA, frmtB: string, result: var string) = +proc renderAux(d: PDoc, n: PRstNode, frmtA, frmtB: string, result: var string) = var tmp = "" for i in countup(0, len(n)-1): renderRstToOut(d, n.sons[i], tmp) if d.target != outLatex: @@ -618,7 +618,8 @@ proc mergeIndexes*(dir: string): string = result.add(generateSymbolIndex(symbols)) -# ---------------------------------------------------------------------------- +# ---------------------------------------------------------------------------- + proc stripTOCHTML(s: string): string = ## Ugly quick hack to remove HTML tags from TOC titles. ## @@ -649,11 +650,9 @@ proc renderHeadline(d: PDoc, n: PRstNode, result: var string) = d.tocPart[length].n = n d.tocPart[length].header = tmp - dispA(d.target, result, - "\n$3", - "\\rsth$4{$3}\\label{$2}\n", [$n.level, - d.tocPart[length].refname, tmp, - $chr(n.level - 1 + ord('A'))]) + dispA(d.target, result, "\n$3", "\\rsth$4{$3}\\label{$2}\n", + [$n.level, d.tocPart[length].refname, tmp, $chr(n.level - 1 + ord('A'))]) else: dispA(d.target, result, "\n$3", "\\rsth$4{$3}\\label{$2}\n", [ @@ -688,7 +687,8 @@ proc renderTocEntry(d: PDoc, e: TTocEntry, result: var string) = "
  • $2
  • \n", "\\item\\label{$1_toc} $2\\ref{$1}\n", [e.refname, e.header]) -proc renderTocEntries*(d: var TRstGenerator, j: var int, lvl: int, result: var string) = +proc renderTocEntries*(d: var TRstGenerator, j: var int, lvl: int, + result: var string) = var tmp = "" while j <= high(d.tocPart): var a = abs(d.tocPart[j].n.level) @@ -832,7 +832,8 @@ proc renderRstToOut(d: PDoc, n: PRstNode, result: var string) = [tmp]) of rnField: renderField(d, n, result) of rnFieldName: - renderAux(d, n, "$1:", "\\item[$1:]", result) + renderAux(d, n, "$1:", + "\\item[$1:]", result) of rnFieldBody: renderAux(d, n, "$1", " $1\n", result) of rnIndex: @@ -891,8 +892,9 @@ proc renderRstToOut(d: PDoc, n: PRstNode, result: var string) = of rnRef: var tmp = "" renderAux(d, n, tmp) - dispA(d.target, result, "$1", - "$1\\ref{$2}", [tmp, rstnodeToRefname(n)]) + dispA(d.target, result, + "$1", + "$1\\ref{$2}", [tmp, rstnodeToRefname(n)]) of rnStandaloneHyperlink: renderAux(d, n, "$1", @@ -902,9 +904,9 @@ proc renderRstToOut(d: PDoc, n: PRstNode, result: var string) = var tmp1 = "" renderRstToOut(d, n.sons[0], tmp0) renderRstToOut(d, n.sons[1], tmp1) - dispA(d.target, result, "$1", - "\\href{$2}{$1}", - [tmp0, tmp1]) + dispA(d.target, result, + "$1", + "\\href{$2}{$1}", [tmp0, tmp1]) of rnDirArg, rnRaw: renderAux(d, n, result) of rnRawHtml: if d.target != outLatex: From 8b1430811a81b9e2c2d8ff0cb1fa348a52ebea7c Mon Sep 17 00:00:00 2001 From: Grzegorz Adam Hankiewicz Date: Mon, 21 Apr 2014 20:42:43 +0200 Subject: [PATCH 032/270] Assigns default title to module documentation. --- lib/packages/docutils/rstgen.nim | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/lib/packages/docutils/rstgen.nim b/lib/packages/docutils/rstgen.nim index a70debbaa3..d9d57c4456 100644 --- a/lib/packages/docutils/rstgen.nim +++ b/lib/packages/docutils/rstgen.nim @@ -72,10 +72,15 @@ proc initRstGenerator*(g: var TRstGenerator, target: TOutputTarget, ## ## You need to call this before using a ``TRstGenerator`` with any other ## procs in this module. Pass a non ``nil`` ``PStringTable`` value as - ## ``config`` with parameters used by the HTML output generator. If you - ## don't know what to use, pass the results of the ``defaultConfig()`` proc. - ## The ``filename`` is symbolic and used only for error reporting, you can - ## pass any non ``nil`` string here. + ## `config` with parameters used by the HTML output generator. If you don't + ## know what to use, pass the results of the `defaultConfig() + ## <#defaultConfig>_` proc. + ## + ## The `filename` will be used mostly for error reporting, you can pass any + ## non ``nil`` string here. If `filename` ends with the ``.nim`` extension, + ## the title for the document will be set by default to ``Module filename``. + ## This default title can be overriden by the embedded rst, but it helps to + ## prettify the generated index if no title is found. ## ## The ``TRstParseOptions``, ``TFindFileHandler`` and ``TMsgHandler`` types ## are defined in the the `packages/docutils/rst module `_. @@ -115,6 +120,9 @@ proc initRstGenerator*(g: var TRstGenerator, target: TOutputTarget, g.options = options g.findFile = findFile g.currentSection = "" + let fileParts = filename.splitFile + if fileParts.ext == ".nim": + g.currentSection = "Module " & fileParts.name g.seenIndexTerms = initTable[string, int]() g.msgHandler = msgHandler From bd29e007bee084ff7bf1bc9b49c2fb47d0aa3ff8 Mon Sep 17 00:00:00 2001 From: Grzegorz Adam Hankiewicz Date: Mon, 21 Apr 2014 20:54:26 +0200 Subject: [PATCH 033/270] Removes text TOCs from the symbol index. --- lib/packages/docutils/rstgen.nim | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/lib/packages/docutils/rstgen.nim b/lib/packages/docutils/rstgen.nim index d9d57c4456..8f223ca25f 100644 --- a/lib/packages/docutils/rstgen.nim +++ b/lib/packages/docutils/rstgen.nim @@ -562,8 +562,13 @@ proc readIndexDir(dir: string): inc F # Depending on type add this to the list of symbols or table of APIs. if title.keyword.isNil: - setLen(result.symbols, L + F) for i in 0 .. 0 and toc[0] == ' ': + continue + # Ok, non TOC entry, add it. + setLen(result.symbols, L + 1) result.symbols[L] = fileEntries[i] inc L result.modules.add(path.splitFile.name) From 51949fd373516ad6df92814aecbeeda8994e0b20 Mon Sep 17 00:00:00 2001 From: Grzegorz Adam Hankiewicz Date: Mon, 21 Apr 2014 23:27:26 +0200 Subject: [PATCH 034/270] Inserts the title index term at the beginning of the buffer. --- lib/packages/docutils/rstgen.nim | 55 ++++++++++++++++++++------------ 1 file changed, 34 insertions(+), 21 deletions(-) diff --git a/lib/packages/docutils/rstgen.nim b/lib/packages/docutils/rstgen.nim index 8f223ca25f..860fdc6182 100644 --- a/lib/packages/docutils/rstgen.nim +++ b/lib/packages/docutils/rstgen.nim @@ -76,11 +76,12 @@ proc initRstGenerator*(g: var TRstGenerator, target: TOutputTarget, ## know what to use, pass the results of the `defaultConfig() ## <#defaultConfig>_` proc. ## - ## The `filename` will be used mostly for error reporting, you can pass any - ## non ``nil`` string here. If `filename` ends with the ``.nim`` extension, - ## the title for the document will be set by default to ``Module filename``. - ## This default title can be overriden by the embedded rst, but it helps to - ## prettify the generated index if no title is found. + ## The `filename` parameter will be used for error reporting and creating + ## index hyperlinks to the file, but you can pass an empty string here if you + ## are parsing a stream in memory. If `filename` ends with the ``.nim`` + ## extension, the title for the document will be set by default to ``Module + ## filename``. This default title can be overriden by the embedded rst, but + ## it helps to prettify the generated index if no title is found. ## ## The ``TRstParseOptions``, ``TFindFileHandler`` and ``TMsgHandler`` types ## are defined in the the `packages/docutils/rst module `_. @@ -270,15 +271,19 @@ proc setIndexTerm*(d: var TRstGenerator, id, term: string, linkTitle, linkDesc = "") = ## Adds a `term` to the index using the specified hyperlink identifier. ## - ## The ``d.theIndex`` string will be used to append the term in the format - ## ``termfile#id``. + ## A new entry will be added to the index using the format + ## ``termfile#id``. The file part will come from the `filename` + ## parameter used in a previous call to the `initRstGenerator() + ## <#initRstGenerator>`_ proc. ## - ## The anchor will be the based on the name of the file currently being - ## parsed plus the specified `id`. The `id` will be appended with a hash - ## character only if its length is not zero, otherwise no specific anchor - ## will be generated. In general you should only pass an empty `id` value - ## for the title of standalone rst documents (they are special for the - ## `mergeIndexes() <#mergeIndexes>`_ proc). + ## The `id` will be appended with a hash character only if its length is not + ## zero, otherwise no specific anchor will be generated. In general you + ## should only pass an empty `id` value for the title of standalone rst + ## documents (they are special for the `mergeIndexes() <#mergeIndexes>`_ + ## proc, see `Index (idx) file format `_ + ## for more information). Unlike other index terms, title entries are + ## inserted at the beginning of the accumulated buffer to maintain a logical + ## order of entries. ## ## If `linkTitle` or `linkDesc` are not the empty string, two additional ## columns with their contents will be added. @@ -286,17 +291,25 @@ proc setIndexTerm*(d: var TRstGenerator, id, term: string, ## The index won't be written to disk unless you call `writeIndexFile() ## <#writeIndexFile>`_. The purpose of the index is documented in the `docgen ## tools guide `_. - d.theIndex.add(term) - d.theIndex.add('\t') + assert(not d.theIndex.isNil) + var + entry = term + isTitle = false + entry.add('\t') let htmlFile = changeFileExt(extractFilename(d.filename), HtmlExt) - d.theIndex.add(htmlFile) + entry.add(htmlFile) if id.len > 0: - d.theIndex.add('#') - d.theIndex.add(id) + entry.add('#') + entry.add(id) + else: + isTitle = true if linkTitle.len > 0 or linkDesc.len > 0: - d.theIndex.add('\t' & linkTitle.quoteIndexColumn) - d.theIndex.add('\t' & linkDesc.quoteIndexColumn) - d.theIndex.add("\n") + entry.add('\t' & linkTitle.quoteIndexColumn) + entry.add('\t' & linkDesc.quoteIndexColumn) + entry.add("\n") + + if isTitle: d.theIndex.insert(entry) + else: d.theIndex.add(entry) proc hash(n: PRstNode): int = if n.kind == rnLeaf: From f70015bee3f8e56e124d0ffe9b31e8cb77327475 Mon Sep 17 00:00:00 2001 From: Grzegorz Adam Hankiewicz Date: Mon, 21 Apr 2014 23:28:36 +0200 Subject: [PATCH 035/270] Adds documentation section about the idx file format. --- doc/docgen.txt | 72 ++++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 58 insertions(+), 14 deletions(-) diff --git a/doc/docgen.txt b/doc/docgen.txt index 520d5e8a04..30d0c6ff28 100644 --- a/doc/docgen.txt +++ b/doc/docgen.txt @@ -164,20 +164,10 @@ Index switch nimrod doc2 --index:on filename.nim This will generate an index of all the exported symbols in the input Nimrod -module, and put it into a neighboring file with the extension of `.idx`. The -index file is line oriented (newlines have to be escaped). Each line represents -a tab separated record of several columns, the first two mandatory, the rest -optional: - -1. Mandatory term being indexed. Terms can include quoting according to - Nimrod's rules (eg. ```^```) -2. Base filename plus anchor hyper link (eg. - ``algorithm.html#*,int,TSortOrder``). -3. Optional human readable string to display as hyper link. If the value is not - present or is the empty string, the hyper link will be rendered using the - term. -4. Optional title or description of the hyper link. Browsers usually display - this as a tooltip after hovering a moment over the hyper link. +module, and put it into a neighboring file with the extension of ``.idx``. The +index file is line oriented (newlines have to be escaped). Each line +represents a tab separated record of several columns, the first two mandatory, +the rest optional. See the `Index (idx) file format`_ section for details. Once index files have been generated for one or more modules, the Nimrod compiler command ``buildIndex directory`` can be run to go over all the index @@ -295,6 +285,60 @@ symbols in the `system module `_. `_ +Index (idx) file format +======================= + +Files with the ``.idx`` extension are generated when you use the `Index +switch`_ along with commands to generate documentation from source or text +files. You can programatically generate indices with the `setIndexTerm() +`_ and `writeIndexFile() +`_ procs. The purpose of ``idx`` files is to hold +the interesting symbols and their HTML references so they can be later +concatenated into a big index file with `mergeIndexes() +`_. This section documents the file format in +detail. + +Index files are line oriented and tab separated (newline and tab characters +have to be escaped). Each line represents a record with at least two fields, +but can have up to four (additional columns are ignored). The content of these +columns is: + +1. Mandatory term being indexed. Terms can include quoting according to + Nimrod's rules (eg. \`^\` like in `the actors module + `_). +2. Base filename plus anchor hyper link (eg. + ``algorithm.html#*,int,TSortOrder``). +3. Optional human readable string to display as hyper link. If the value is not + present or is the empty string, the hyper link will be rendered + using the term. Prefix whitespace indicates that this entry is + not for an API symbol but for a TOC entry. +4. Optional title or description of the hyper link. Browsers usually display + this as a tooltip after hovering a moment over the hyper link. + +The index generation tools try to differentiate between documentation +generated from ``.nim`` files and documentation generated from ``.txt`` or +``.rst`` files. The former are always closely related to source code and +consist mainly of API entries. The latter are generic documents meant for +human reading. + +To differentiate both types (documents and APIs), the index generator will add +to the index of documents an entry with the title of the document. Since the +title is the topmost element, it will be added with a second field containing +just the filename without any HTML anchor. By convention this entry without +anchor is the *title entry*, and since entries in the index file are added as +they are scanned, the title entry will be the first line. The title for APIs +is not present because it can be generated concatenating the name of the file +to the word **Module**. + +Normal symbols are added to the index with surrounding whitespaces removed. An +exception to this are table of content (TOC) entries. TOC entries are added to +the index file with their third column having as much prefix spaces as their +level is in the TOC (at least 1 character). The prefix whitespace helps to +filter TOC entries from API or text symbols. This is important because the +amount of spaces is used to replicate the hiearchy for document TOCs in the +final index, and TOC entries found in ``.nim`` files are discarded. + + Additional resources ==================== From 7f090983e92dbaa65977244a48710859c81c7e90 Mon Sep 17 00:00:00 2001 From: Grzegorz Adam Hankiewicz Date: Mon, 21 Apr 2014 23:37:12 +0200 Subject: [PATCH 036/270] Adds colon to symbols in the index. The colon makes it easier to get a unique match searching for symbols using words which may also appear as substrings or elsewhere. --- lib/packages/docutils/rstgen.nim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/packages/docutils/rstgen.nim b/lib/packages/docutils/rstgen.nim index 860fdc6182..46788f2ea9 100644 --- a/lib/packages/docutils/rstgen.nim +++ b/lib/packages/docutils/rstgen.nim @@ -406,7 +406,7 @@ proc generateSymbolIndex(symbols: seq[TIndexEntry]): string = result = "" var i = 0 while i < symbols.len: - result.addf("
    $1
      \n", + result.addf("
      $1:
        \n", [symbols[i].keyword]) var j = i while j < symbols.len and symbols[i].keyword == symbols[j].keyword: From 9ea55bf8875cd357e4164a27e358f61532ad1521 Mon Sep 17 00:00:00 2001 From: Dominik Picheta Date: Tue, 22 Apr 2014 00:36:46 +0100 Subject: [PATCH 037/270] Website updates. --- web/download.txt | 103 ++++++++++++++++++++++++++++++++++++++++++++++- web/news.txt | 14 ++++--- web/ticker.txt | 4 +- 3 files changed, 112 insertions(+), 9 deletions(-) diff --git a/web/download.txt b/web/download.txt index a306e516c8..0de31e8ce8 100644 --- a/web/download.txt +++ b/web/download.txt @@ -1,7 +1,103 @@ +You can download the latest version of the Nimrod compiler here. You can +use the binaries provided or build from source. + +Binaries +======== + +All installers and archives contain the html documentation and tools +(nimgrep, c2nim and babel). + +**Note:** The Nimrod compiler requires a C compiler to compile software. On +Windows we recommend that you use +`Mingw-w64 `_. GCC is recommended on Linux +and clang on Mac OS X. + +Installers +---------- + +Installers will be available soon. + + + +Archives +-------- + +Windows +~~~~~~~ + +The "full" version includes a full mingw distribution which includes a C +compiler. The "slim" version lacks this. + +* Full (i386, 32bit): ``_ + + .. raw:: html +

        + SHA256: 8ee18faaa3a3d5df482c7abd6aa7ea87a350d7328b80ce1e2d486b59a7a93956 +

        + +* Slim (i386, 32bit): ``_ + + .. raw:: html +

        + SHA256: 7024fb8ad8f98c0bd4949ae36ed11b52b4e401754bbd62a11199d6dc8628d857 +

        + +* Full (amd64, 64bit): ``_ + + .. raw:: html +

        + SHA256: cb33cacc1a84fec771323d24cb6d9795f4803882466a9f417b424990aa49e18a +

        + +* Slim (amd64, 64bit): ``_ + + .. raw:: html +

        + SHA256: fcf877e4bd1ebfa214749af6e4811cd8539af19f1d7b23017e4bd7f6cbfb3eba +

        + +Linux +~~~~~ + +* Linux (i386, 32bit): ``_ + + .. raw:: html +

        + SHA256: 79DD337A77AC4313A75F2C5EED8252F00BBBDEB1E0C3504660D4A52EA63DBA92 +

        +* Linux (amd64, 64bit): ``_ + + .. raw:: html +

        + SHA256: 6F6CB3C727BA8059B7605C02942AE7910C20C2A3DC6A8A600D90D50FE61F0D8C +

        +* Linux (ppc64, 64bit): ``_ + + .. raw:: html +

        + SHA256: 5DAC2D9F7F545929E04540E6E2594C68FC3126A3B2F7B1FA7DBA5E295B4A7D31 +

        + +Mac OS X +~~~~~~~~ + +* Mac OS X (amd64, 64bit): ``_ + + .. raw:: html +

        + SHA256: E6F3A8E434DF3E89686F043954C6DFC09ABEBC0FC09D3B9A6B35C2B3102F7C3C +

        + +If a binary for your platform is not available then you must build from source. +Bleeding edge binaries are available from the `Nimrod build farm `_. + +Source +====== + Starting with 0.9.4 we now advise people to build directly from the github `master `_ branch:: - git clone git://github.com/Araq/Nimrod.git + git clone -b master git://github.com/Araq/Nimrod.git cd Nimrod git clone --depth 1 git://github.com/nimrod-code/csources cd csources && sh build.sh @@ -9,4 +105,7 @@ github `master `_ branch:: bin/nimrod c koch ./koch boot -d:release -Prebuilt binaries will be available soon. +The ``master`` branch always contains the latest stable version of the compiler. +If you want bleeding edge then switch to the ``devel`` branch and follow +the same instructions outlined above. + diff --git a/web/news.txt b/web/news.txt index fa1c532e8f..4c41a0a76a 100644 --- a/web/news.txt +++ b/web/news.txt @@ -9,13 +9,15 @@ News The Nimrod development community is proud to announce the release of version 0.9.4 of the Nimrod compiler and tools. **Note: This release has to be -considered beta quality! Lots of new features have been implemented but most -do not fullfill our quality standards.** +considered beta quality! Lots of new features have been implemented but +unfortunately some do not fullfill our quality standards yet.** -This release can be downloaded from `github `_. -Prebuilt binaries will be available soon. +Prebuilt binaries and instructions for building from source are available +on the `download page `_. -This release includes about 1300 changes in total including various bug +This release includes about +`1400 changes `_ +in total including various bug fixes, new languages features and standard library additions and improvements. This release brings with it support for user-defined type classes, a brand new VM for executing Nimrod code at compile-time and new symbol binding @@ -79,6 +81,8 @@ syntax: var s = @[1, 2, 3, 4, 5] echo(s.map((x: int) => x * 5)) +A list of changes follows, for a comprehensive list of changes take a look +`here `_. Library Additions ----------------- diff --git a/web/ticker.txt b/web/ticker.txt index 691a14575f..f06b005727 100644 --- a/web/ticker.txt +++ b/web/ticker.txt @@ -1,4 +1,4 @@ - +

        Apr 21, 2014

        Nimrod version 0.9.4 has been released!

        @@ -46,4 +46,4 @@

        Mar 14, 2010

        Nimrod version 0.8.8 has been released!

        -
        \ No newline at end of file + From 15bd91a5dadc0d1e0eceb6ea8ebb5a8f144abafe Mon Sep 17 00:00:00 2001 From: Grzegorz Adam Hankiewicz Date: Tue, 22 Apr 2014 00:28:57 +0200 Subject: [PATCH 038/270] Adds example of custom object types used as table key. --- lib/pure/collections/tables.nim | 68 +++++++++++++++++++++++++++++++++ 1 file changed, 68 insertions(+) diff --git a/lib/pure/collections/tables.nim b/lib/pure/collections/tables.nim index cd28f9af08..33e558aee2 100644 --- a/lib/pure/collections/tables.nim +++ b/lib/pure/collections/tables.nim @@ -10,6 +10,47 @@ ## The ``tables`` module implements an efficient hash table that is ## a mapping from keys to values. ## +## If you are using simple standard types like ``int`` or ``string`` for the +## keys of the table you won't have any problems, but as soon as you try to use +## a more complex object as a key you will be greeted by a strange compiler +## error:: +## +## Error: type mismatch: got (Person) +## but expected one of: +## hashes.hash(x: openarray[A]): THash +## hashes.hash(x: int): THash +## hashes.hash(x: float): THash +## … +## +## What is happening here is that the types used for table keys require to have +## a ``hash()`` proc which will convert them to a `THash `_ +## value, and the compiler is listing all the hash functions it knows. After +## you add such a proc for your custom type everything will work. See this +## example: +## +## .. code-block:: nimrod +## type +## Person = object +## firstName, lastName: string +## +## proc hash(x: Person): THash = +## ## Piggyback on the already available string hash proc. +## ## +## ## Without this proc nothing works! +## result = hash(x.firstName & x.lastName) +## +## var +## salaries = initTable[Person, int]() +## p1, p2: Person +## +## p1.firstName = "Jon" +## p1.lastName = "Ross" +## salaries[p1] = 30_000 +## +## p2.firstName = "소진" +## p2.lastName = "박" +## salaries[p2] = 45_000 +## ## **Note:** The data types declared here have *value semantics*: This means ## that ``=`` performs a copy of the hash table. @@ -526,3 +567,30 @@ proc sort*[A](t: var TCountTable[A]) = if j < h: break if h == 1: break +when isMainModule: + type + Person = object + firstName, lastName: string + + proc hash(x: Person): THash = + ## Piggyback on the already available string hash proc. + ## + ## Without this proc nothing works! + result = hash(x.firstName & x.lastName) + + var + salaries = initTable[Person, int]() + p1, p2: Person + p1.firstName = "Jon" + p1.lastName = "Ross" + salaries[p1] = 30_000 + p2.firstName = "소진" + p2.lastName = "박" + salaries[p2] = 45_000 + var + s2 = initOrderedTable[Person, int]() + s3 = initCountTable[Person]() + s2[p1] = 30_000 + s2[p2] = 45_000 + s3[p1] = 30_000 + s3[p2] = 45_000 From fa961f16d0d2078a841fccb93231858e2afae94e Mon Sep 17 00:00:00 2001 From: enurlyx Date: Tue, 22 Apr 2014 19:42:32 +0200 Subject: [PATCH 039/270] 1) export UINT 2) Fixed parameter of FillRect RECT -> var RECT --- lib/windows/windows.nim | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/windows/windows.nim b/lib/windows/windows.nim index dd743ffa4c..41f760130e 100644 --- a/lib/windows/windows.nim +++ b/lib/windows/windows.nim @@ -62,7 +62,7 @@ type # BaseTsd.h -- Type definitions for the basic sized types type # WinDef.h -- Basic Windows Type Definitions # BaseTypes - UINT = int32 + UINT* = int32 ULONG* = int PULONG* = ptr int USHORT* = int16 @@ -19683,7 +19683,7 @@ proc SetSysColors*(cElements: int32, lpaElements: var wINT, dynlib: "user32", importc: "SetSysColors".} proc DrawFocusRect*(hDC: HDC, lprc: var RECT): WINBOOL{.stdcall, dynlib: "user32", importc: "DrawFocusRect".} -proc FillRect*(hDC: HDC, lprc: RECT, hbr: HBRUSH): int32{.stdcall, +proc FillRect*(hDC: HDC, lprc: var RECT, hbr: HBRUSH): int32{.stdcall, dynlib: "user32", importc: "FillRect".} proc FrameRect*(hDC: HDC, lprc: var RECT, hbr: HBRUSH): int32{.stdcall, dynlib: "user32", importc: "FrameRect".} From 7e2e5efe6f339f0d3ee30057aee16873a0e97383 Mon Sep 17 00:00:00 2001 From: Grzegorz Adam Hankiewicz Date: Tue, 22 Apr 2014 20:28:59 +0200 Subject: [PATCH 040/270] Adds git hash to compiler's version switch. Refs #1119. --- compiler/commands.nim | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/compiler/commands.nim b/compiler/commands.nim index 366019c190..e920cc8970 100644 --- a/compiler/commands.nim +++ b/compiler/commands.nim @@ -74,6 +74,11 @@ proc writeVersionInfo(pass: TCmdLinePass) = msgWriteln(`%`(HelpMessage, [VersionAsString, platform.OS[platform.hostOS].name, CPU[platform.hostCPU].name])) + + const gitHash = gorge("git log -n 1 --format=%H") + if gitHash.strip.len == 40: + msgWriteln("git hash: " & gitHash) + msgWriteln("active boot switches:" & usedRelease & usedAvoidTimeMachine & usedTinyC & usedGnuReadline & usedNativeStacktrace & usedNoCaas & usedFFI & usedBoehm & usedMarkAndSweep & usedGenerational & usedNoGC) From 71e1c8d642edb8aed5d6525fd564444141fd747f Mon Sep 17 00:00:00 2001 From: Araq Date: Tue, 22 Apr 2014 21:36:54 +0200 Subject: [PATCH 041/270] website updates --- tools/website.tmpl | 2 +- web/index.txt | 10 ++++++---- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/tools/website.tmpl b/tools/website.tmpl index bd68bdb062..1d62427362 100644 --- a/tools/website.tmpl +++ b/tools/website.tmpl @@ -71,7 +71,7 @@ + + + +Create a ``calculator.nim`` file with the following content (or reuse the one +from the previous section): + +.. code-block:: nimrod + + proc addTwoIntegers(a, b: int): int {.importc.} + + when isMainModule: + echo addTwoIntegers(3, 7) + +Compile the Nimrod code to JavaScript with ``nimrod js -o:calculator.js +calculator.nim`` and open ``host.html`` in a browser. If the browser supports +javascript, you should see the value ``10``. In JavaScript the `echo proc +`_ will modify the HTML DOM and append the string. Use the +`dom module `_ for specific DOM querying and modification procs. Backend code calling Nimrod From f10d3b5fa6ffa4bb177dc2b7d63d07edcd234244 Mon Sep 17 00:00:00 2001 From: Grzegorz Adam Hankiewicz Date: Sun, 22 Jun 2014 19:38:33 +0200 Subject: [PATCH 212/270] Adds examples of backend calling nimrod. --- doc/backends.txt | 113 +++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 110 insertions(+), 3 deletions(-) diff --git a/doc/backends.txt b/doc/backends.txt index 957a2d2ed4..8bf353ff72 100644 --- a/doc/backends.txt +++ b/doc/backends.txt @@ -110,8 +110,8 @@ Nimrod code calling the backend Nimrod code can interface with the backend through the `Foreign function interface `_ mainly through the `importc pragma `_. The ``importc`` pragma is the -*generic* way of making backend code available in Nimrod and is available in -all the target backends (JavaScript too). The C++ or Objective-C backends +*generic* way of making backend symbols available in Nimrod and is available +in all the target backends (JavaScript too). The C++ or Objective-C backends have their respective `ImportCpp `_ and `ImportObjC `_ pragmas to call methods from classes. @@ -215,11 +215,118 @@ javascript, you should see the value ``10``. In JavaScript the `echo proc Backend code calling Nimrod --------------------------- -mention NimMain +Backend code can interface with Nimrod code exposed through the `exportc +pragma `_. The ``exportc`` pragma is the *generic* +way of making Nimrod symbols available to the backends. By default the Nimrod +compiler will mangle all the Nimrod symbols to avoid any name collision, so +the most significant thing the ``exportc`` pragma does is maintain the Nimrod +symbol name, or if specified, use an alternative symbol for the backend in +case the symbol rules don't match. + +The JavaScript target doesn't have any further interfacing considerations +since it also has garbage collection, but the C targets require you to +initialize Nimrod's internals, which is done calling a ``NimMain`` function. +Also, C code requires you to specify a forward declaration for functions or +the compiler will asume certain types for the return value and parameters +which will likely make your program crash at runtime. + +The Nimrod compiler can generate a C interface header through the ``--header`` +command line switch. The generated header will contain all the exported +symbols and the ``NimMain`` proc which you need to call before any other +Nimrod code. + + +Nimrod invocation example from C +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Create a ``fib.nim`` file with the following content: + +.. code-block:: nimrod + + proc fib(a: cint): cint {.exportc.} = + if a <= 2: + result = 1 + else: + result = fib(a - 1) + fib(a - 2) + +Create a ``maths.c`` file with the following content: + +.. code-block:: c + + #include "fib.h" + #include + + int main(void) + { + NimMain(); + for (int f = 0; f < 10; f++) + printf("Fib of %d is %d\n", f, fib(f)); + return 0; + } + +Now you can run the following Unix like commands to first generate C sources +form the Nimrod code, then link it into a static binary:: + + $ nimrod c --noMain --noLinking --header:fib.h fib.nim + $ gcc -o m -Inimcache -Ipath/to/nimrod/lib nimcache/*.c maths.c + +The first command runs the Nimrod compiler with three special options to avoid +generating a ``main()`` function in the generated files, avoid linking the +object files into a final binary, and explicitly generate a header file for C +integration. All the generated files are placed into the ``nimcache`` +directory. That's why the next command compiles the ``maths.c`` source plus +all the ``.c`` files form ``nimcache``. In addition to this path, you also +have to tell the C compiler where to find Nimrod's ``nimbase.h`` header file. + +Instead of depending on the generation of the individual ``.c`` files you can +also ask the Nimrod compiler to generate a statically linked library:: + + $ nimrod c --app:staticLib --noMain --header fib.nim + $ gcc -o m -Inimcache -Ipath/to/nimrod/lib libfib.nim.a maths.c + +The Nimrod compiler will handle linking the source files generated in the +``nimcache`` directory into the ``libfib.nim.a`` static library, which you can +then link into your C program. Note that these commands are generic and will +vary for each system. For instance, on Linux systems you will likely need to +use ``-ldl`` too to link in required dlopen functionality. + + +Nimrod invocation example from JavaScript +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Create a ``mhost.html`` file with the following content: + +.. code-block:: + + + + + + +Create a ``fib.nim`` file with the following content (or reuse the one +from the previous section): + +.. code-block:: nimrod + + proc fib(a: cint): cint {.exportc.} = + if a <= 2: + result = 1 + else: + result = fib(a - 1) + fib(a - 2) + +Compile the Nimrod code to JavaScript with ``nimrod js -o:fib.js fib.nim`` and +open ``mhost.html`` in a browser. If the browser supports javascript, you +should see an alert box displaying the text ``Fib for 9 is 34``. + Memory management ================= +Strings and C strings +--------------------- + Garbage collection, life of objects ----------------------------------- From ff1fe8b4ec64264171b419faff5fba4cf88b9a78 Mon Sep 17 00:00:00 2001 From: Grzegorz Adam Hankiewicz Date: Sun, 22 Jun 2014 20:12:57 +0200 Subject: [PATCH 213/270] Mentions memory management. --- doc/backends.txt | 66 ++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 61 insertions(+), 5 deletions(-) diff --git a/doc/backends.txt b/doc/backends.txt index 8bf353ff72..66d702f4e6 100644 --- a/doc/backends.txt +++ b/doc/backends.txt @@ -265,7 +265,8 @@ Create a ``maths.c`` file with the following content: } Now you can run the following Unix like commands to first generate C sources -form the Nimrod code, then link it into a static binary:: +form the Nimrod code, then link them into a static binary along your main C +program:: $ nimrod c --noMain --noLinking --header:fib.h fib.nim $ gcc -o m -Inimcache -Ipath/to/nimrod/lib nimcache/*.c maths.c @@ -318,17 +319,72 @@ from the previous section): Compile the Nimrod code to JavaScript with ``nimrod js -o:fib.js fib.nim`` and open ``mhost.html`` in a browser. If the browser supports javascript, you -should see an alert box displaying the text ``Fib for 9 is 34``. +should see an alert box displaying the text ``Fib for 9 is 34``. As mentioned +earlier, JavaScript doesn't require an initialisation call to ``NimMain`` or +similar function and you can call the exported Nimrod proc directly. Memory management ================= +In the previous sections the ``NimMain()`` function reared its head. Since +JavaScript already provides automatic memory management, you can freely pass +objects between the two language without problems. In C and derivate languages +you need to be careful about what you do and how you share memory. The +previous examples only dealt with simple scalar values, but passing a Nimrod +string to C, or reading back a C string in Nimrod already requires you to be +aware of who controls what to avoid crashing. + + Strings and C strings --------------------- -Garbage collection, life of objects ------------------------------------ +The manual mentions that `Nimrod strings are implicitly convertible to +cstrings `_ which makes interaction usually +painless. Most C functions accepting a Nimrod string converted to a +``cstring`` will likely not need to keep this string around and by the time +they return the string won't be needed any more. However, for the rare cases +where a Nimrod string has to be preserved and made available to the C backend +as a ``cstring``, you will need to manually prevent the string data from being +freed with `GC_ref `_ and `GC_unref +`_. + +A similar thing happens with C code invoking Nimrod code which returns a +``cstring``. Consider the following proc: + +.. code-block:: nimrod + + proc gimme(): cstring {.exportc.} = + result = "Hey there C code! " & $random(100) + +Since Nimrod's garbage collector is not aware of the C code, once the +``gimme`` proc has finished it can reclaim the memory of the ``cstring``. +However, from a practical standpoint, the C code invoking the ``gimme`` +function directly will be able to use it since Nimrod's garbage collector has +not had a chance to run *yet*. This gives you enough time to make a copy for +the C side of the program, as calling any further Nimrod procs *might* trigger +garbage collection making the previously returned string garbage. Or maybe you +are `triggering yourself the collection `_. + + +Custom data types +----------------- + +Just like strings, custom data types that are to be shared between Nimrod and +the backend will need careful consideration of who controlls who. If you want +to hand a Nimrod reference to C code, you will need to use `GC_ref +`_ to mark the reference as used, so it does not get +freed. And for the C backend you will need to expose the `GC_unref +`_ proc to clean up this memory when it is not required +any more. + +Again, if you are wrapping a library which *mallocs* and *frees* data +structures, you need to expose the appropriate *free* function to Nimrod so +you can clean it up. And of course, once cleaned you should avoid accessing it +from Nimrod (or C for that matter). Typically C data structures have their own +``malloc_structure`` and ``free_structure`` specific functions, so wrapping +these for the Nimrod side should be enough. + Thread coordination -------------------- +=================== From 6ca24cd246a38bf353219f38e1725baf982c9f6e Mon Sep 17 00:00:00 2001 From: Grzegorz Adam Hankiewicz Date: Sun, 22 Jun 2014 20:19:09 +0200 Subject: [PATCH 214/270] Adds tidbit about threads. --- doc/backends.txt | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/doc/backends.txt b/doc/backends.txt index 66d702f4e6..8e16502e7a 100644 --- a/doc/backends.txt +++ b/doc/backends.txt @@ -387,4 +387,24 @@ these for the Nimrod side should be enough. Thread coordination -=================== +------------------- + +When the ``NimMain()`` function is called Nimrod initializes the garbage +collector to the current thread, which is usually the main thread of your +application. If your C code later spawns a different thread and calls Nimrod +code, the garbage collector will fail to work properly and you will crash. + +As long as you don't use the threadvar emulation Nimrod uses native thread +variables, of which you get a fresh version whenever you create a thread. You +can then attach a GC to this thread via + +.. code-block:: nimrod + + setStackBottom(addr(someLocal)) + initGC() + +At the moment this support is still experimental so you need to expose these +functions yourself or submit patches to request a public API. If the Nimrod +code you are calling is short lived, another possible solution is to disable +the garbage collector and enable it after the call from your background +thread. From 38cdd7595eeafe9906c42d258edeacaf50743bb1 Mon Sep 17 00:00:00 2001 From: Dominik Picheta Date: Sun, 22 Jun 2014 20:25:38 +0100 Subject: [PATCH 215/270] Remove varargs from json.`{}` --- lib/pure/json.nim | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/lib/pure/json.nim b/lib/pure/json.nim index 4e369b854d..508e564c57 100644 --- a/lib/pure/json.nim +++ b/lib/pure/json.nim @@ -713,14 +713,12 @@ proc `[]=`*(obj: PJsonNode, key: string, val: PJsonNode) = return obj.fields.add((key, val)) -proc `{}`*(node: PJsonNode, names: varargs[string]): PJsonNode = +proc `{}`*(node: PJsonNode, key: string): PJsonNode = ## Transverses the node and gets the given value. If any of the ## names does not exist, returns nil result = node - for name in names: - result = result[name] - if isNil(result): - return nil + if isNil(node): return nil + result = result[key] proc `{}=`*(node: PJsonNode, names: varargs[string], value: PJsonNode) = ## Transverses the node and tries to set the value at the given location @@ -1059,7 +1057,7 @@ when isMainModule: let testJson = parseJson"""{ "a": [1, 2, 3, 4], "b": "asd" }""" # nil passthrough - assert(testJson{"doesnt_exist", "anything"} == nil) + assert(testJson{"doesnt_exist"}{"anything"}.isNil) testJson{["c", "d"]} = %true assert(testJson["c"]["d"].bval) From 92e958b654a80657aa646aabd0f3bdc4e5c79940 Mon Sep 17 00:00:00 2001 From: def Date: Sun, 22 Jun 2014 23:15:53 +0200 Subject: [PATCH 216/270] Add keepIf proc and keepIfIt template to sequtils --- lib/pure/collections/sequtils.nim | 49 +++++++++++++++++++++++++++++++ 1 file changed, 49 insertions(+) diff --git a/lib/pure/collections/sequtils.nim b/lib/pure/collections/sequtils.nim index f5db9d3fab..8e50b9e204 100644 --- a/lib/pure/collections/sequtils.nim +++ b/lib/pure/collections/sequtils.nim @@ -182,6 +182,24 @@ proc filter*[T](seq1: seq[T], pred: proc(item: T): bool {.closure.}): seq[T] = ## assert f2 == @["yellow"] accumulateResult(filter(seq1, pred)) +proc keepIf*[T](seq1: var seq[T], pred: proc(item: T): bool {.closure.}) = + ## Keeps the items in the passed sequence if they fulfilled the predicate. + ## Same as the ``filter`` proc, but modifies the sequence directly. + ## + ## Example: + ## + ## .. code-block:: nimrod + ## var floats = @[13.0, 12.5, 5.8, 2.0, 6.1, 9.9, 10.1] + ## filter(floats, proc(x: float): bool = x > 10) + ## assert floats == @[13.0, 12.5, 10.1] + var pos = 0 + for i in 0 .. 10) + assert floats == @[13.0, 12.5, 10.1] + block: # filterIt test let temperatures = @[-272.15, -2.0, 24.5, 44.31, 99.9, -113.44] @@ -426,6 +470,11 @@ when isMainModule: assert acceptable == @[-2.0, 24.5, 44.31] assert notAcceptable == @[-272.15, 99.9, -113.44] + block: # keepIfIt test + var candidates = @["foo", "bar", "baz", "foobar"] + keepIfIt(candidates, it.len == 3 and it[0] == 'b') + assert candidates == @["bar", "baz"] + block: # toSeq test let numeric = @[1, 2, 3, 4, 5, 6, 7, 8, 9] From 55e7d91b317444d874b3ce88c44536842ef0821b Mon Sep 17 00:00:00 2001 From: Reimer Behrends Date: Wed, 25 Jun 2014 00:42:55 +0200 Subject: [PATCH 217/270] Fix generation of package-dependent init names. The code incorrectly used relative instead of absolute paths to see if *.babel files could be found, which could result in them not being located properly. Also added an underscore between the package and the module name for package_moduleInit() and package_moduleDatInit() so that there won't be spurious conflicts, e.g. for package A and module BC vs. package AB and module C. --- compiler/cgen.nim | 1 + compiler/modules.nim | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/compiler/cgen.nim b/compiler/cgen.nim index a5852c735c..e2f3b5ab0a 100644 --- a/compiler/cgen.nim +++ b/compiler/cgen.nim @@ -1050,6 +1050,7 @@ proc getSomeInitName(m: PSym, suffix: string): PRope = assert m.owner.kind == skPackage if {sfSystemModule, sfMainModule} * m.flags == {}: result = m.owner.name.s.mangle.toRope + result.app "_" result.app m.name.s result.app suffix diff --git a/compiler/modules.nim b/compiler/modules.nim index fb19407416..b102224cda 100644 --- a/compiler/modules.nim +++ b/compiler/modules.nim @@ -115,7 +115,7 @@ proc newModule(fileIdx: int32): PSym = new(result) result.id = - 1 # for better error checking result.kind = skModule - let filename = fileIdx.toFilename + let filename = fileIdx.toFullPath result.name = getIdent(splitFile(filename).name) if not isNimrodIdentifier(result.name.s): rawMessage(errInvalidModuleName, result.name.s) From b090b7ea4dd1d996aa80cab6a95d63187866771d Mon Sep 17 00:00:00 2001 From: Felix Krause Date: Wed, 25 Jun 2014 17:01:22 +0200 Subject: [PATCH 218/270] Fixed handling swap in vmgen --- compiler/vmgen.nim | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/compiler/vmgen.nim b/compiler/vmgen.nim index 186d27ae45..305ea7f9e9 100644 --- a/compiler/vmgen.nim +++ b/compiler/vmgen.nim @@ -787,10 +787,12 @@ proc genMagic(c: PCtx; n: PNode; dest: var TDest) = c.freeTemp(tmp) of mSwap: unused(n, dest) - var d = c.genx(n.sons[1]) - var tmp = c.genx(n.sons[2]) - c.gABC(n, opcSwap, d, tmp) - c.freeTemp(tmp) + var + d1 = c.genx(n.sons[1]) + d2 = c.genx(n.sons[2]) + c.gABC(n, opcSwap, d1, d2) + c.genAsgnPatch(n.sons[1], d1) + c.genAsgnPatch(n.sons[2], d2) of mIsNil: genUnaryABC(c, n, dest, opcIsNil) of mCopyStr: if dest < 0: dest = c.getTemp(n.typ) From bdd3b6c612a29723954f62e242888eedee0b35e4 Mon Sep 17 00:00:00 2001 From: Felix Krause Date: Wed, 25 Jun 2014 21:57:06 +0200 Subject: [PATCH 219/270] Added logical set operations to TSet --- lib/pure/collections/sets.nim | 49 +++++++++++++++++++++ tests/sets/tsets3.nim | 81 +++++++++++++++++++++++++++++++++++ 2 files changed, 130 insertions(+) create mode 100644 tests/sets/tsets3.nim diff --git a/lib/pure/collections/sets.nim b/lib/pure/collections/sets.nim index bc249ed635..380a33c5b0 100644 --- a/lib/pure/collections/sets.nim +++ b/lib/pure/collections/sets.nim @@ -112,6 +112,10 @@ proc incl*[A](s: var TSet[A], key: A) = ## includes an element `key` in `s`. inclImpl() +proc incl*[A](s: var TSet[A], other: TSet[A]) = + ## includes everything in `other` in `s` + for item in other: incl(s, item) + proc excl*[A](s: var TSet[A], key: A) = ## excludes `key` from the set `s`. var index = rawGet(s, key) @@ -119,6 +123,10 @@ proc excl*[A](s: var TSet[A], key: A) = s.data[index].slot = seDeleted dec(s.counter) +proc excl*[A](s: var TSet[A], other: TSet[A]) = + ## excludes everything in `other` from `s`. + for item in other: excl(s, item) + proc containsOrIncl*[A](s: var TSet[A], key: A): bool = ## returns true if `s` contains `key`, otherwise `key` is included in `s` ## and false is returned. @@ -147,6 +155,43 @@ proc `$`*[A](s: TSet[A]): string = ## The `$` operator for hash sets. dollarImpl() +proc union*[A](s1, s2: TSet[A]): TSet[A] = + ## returns a new set of all items that are contained in at + ## least one of `l` and `r` + result = s1 + incl(result, s2) + +proc intersection*[A](s1, s2: TSet[A]): TSet[A] = + ## returns a new set of all items that are contained in both `l` and `r` + result = initSet[A](min(s1.data.len, s2.data.len)) + for item in s1: + if item in s2: incl(result, item) + +proc symmetricDifference*[A](s1, s2: TSet[A]): TSet[A] = + ## returns a new set of all items that are contained in either + ## `l` or `r`, but not both + result = s1 + for item in s2: + if containsOrIncl(result, item): excl(result, item) + +proc `or`*[A](s1, s2: TSet[A]): TSet[A] {.inline.} = + ## alias for `union` + result = union(s1, s2) + +proc `and`*[A](s1, s2: TSet[A]): TSet[A] {.inline.} = + ## alias for `intersection` + result = intersection(s1, s2) + +proc `xor`*[A](s1, s2: TSet[A]): TSet[A] {.inline.} = + ## alias for `symmetricDifference` + result = symmetricDifference(s1, s2) + +proc disjoint*[A](s1, s2: TSet[A]): bool = + ## returns true iff `l` and `r` have no items in common + for item in s1: + if item in s2: return false + return true + # ------------------------------ ordered set ------------------------------ type @@ -211,6 +256,10 @@ proc incl*[A](s: var TOrderedSet[A], key: A) = ## includes an element `key` in `s`. inclImpl() +proc incl*[A](s: var TSet[A], other: TOrderedSet[A]) = + ## includes everything in `other` in `s` + for item in other: incl(s, item) + proc containsOrIncl*[A](s: var TOrderedSet[A], key: A): bool = ## returns true if `s` contains `key`, otherwise `key` is included in `s` ## and false is returned. diff --git a/tests/sets/tsets3.nim b/tests/sets/tsets3.nim new file mode 100644 index 0000000000..5d14a1d8f0 --- /dev/null +++ b/tests/sets/tsets3.nim @@ -0,0 +1,81 @@ +include sets + +let + s1: TSet[int] = toSet([1, 2, 4, 8, 16]) + s2: TSet[int] = toSet([1, 2, 3, 5, 8]) + s3: TSet[int] = toSet([3, 5, 7]) + +block union: + let + s1_s2 = union(s1, s2) + s1_s3 = s1 or s3 + s2_s3 = s2 or s3 + + assert s1_s2.len == 7 + assert s1_s3.len == 8 + assert s2_s3.len == 6 + + for i in s1: + assert i in s1_s2 + assert i in s1_s3 + for i in s2: + assert i in s1_s2 + assert i in s2_s3 + for i in s3: + assert i in s1_s3 + assert i in s2_s3 + + assert((s1 or s1) == s1) + assert((s2 or s1) == s1_s2) + +block intersection: + let + s1_s2 = intersection(s1, s2) + s1_s3 = intersection(s1, s3) + s2_s3 = s2 and s3 + + assert s1_s2.len == 3 + assert s1_s3.len == 0 + assert s2_s3.len == 2 + + for i in s1_s2: + assert i in s1 + assert i in s2 + for i in s1_s3: + assert i in s1 + assert i in s3 + for i in s2_s3: + assert i in s2 + assert i in s3 + + assert((s2 and s2) == s2) + assert((s3 and s2) == s2_s3) + +block symmetricDifference: + let + s1_s2 = symmetricDifference(s1, s2) + s1_s3 = s1 xor s3 + s2_s3 = s2 xor s3 + + assert s1_s2.len == 4 + assert s1_s3.len == 8 + assert s2_s3.len == 4 + + for i in s1: + assert i in s1_s2 xor i in s2 + assert i in s1_s3 xor i in s3 + for i in s2: + assert i in s1_s2 xor i in s1 + assert i in s2_s3 xor i in s3 + for i in s3: + assert i in s1_s3 xor i in s1 + assert i in s2_s3 xor i in s2 + + assert((s3 xor s3) == initSet[int]()) + assert((s3 xor s1) == s1_s3) + +block disjoint: + assert(not disjoint(s1, s2)) + assert disjoint(s1, s3) + assert(not disjoint(s2, s3)) + assert(not disjoint(s2, s2)) \ No newline at end of file From b8982b78d032c25192b639971e671e35b7fe5a10 Mon Sep 17 00:00:00 2001 From: Grzegorz Adam Hankiewicz Date: Thu, 26 Jun 2014 12:15:26 +0200 Subject: [PATCH 220/270] Rewords C derivate backend. Refs #1299. --- doc/backends.txt | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/doc/backends.txt b/doc/backends.txt index 8e16502e7a..6e77dfb3c0 100644 --- a/doc/backends.txt +++ b/doc/backends.txt @@ -24,12 +24,12 @@ platform's C compiler into a static binary. However there are other commands to compile to C++, Objective-C or JavaScript. This document tries to concentrate in a single place all the backend and interfacing options. -The Nimrod compiler supports mainly two backends: the C (and derivate) and the -JavaScript targets. The C target creates source files which can be compiled -into a library or a final executable. The JavaScript target generates a -``.js`` file which you call from an HTML file. On top of generating a library -or executable, Nimrod offers bidirectional interfacing with the backend -targets through generic and specific pragmas. +The Nimrod compiler supports mainly two backend families: the C, C++ and +Objective-C targets and the JavaScript target. `The C like targets`_ creates +source files which can be compiled into a library or a final executable. The +JavaScript target generates a ``.js`` file which you call from an HTML file. +On top of generating a library or executable, Nimrod offers bidirectional +interfacing with the backend targets through generic and specific pragmas. Backends From 14ba5263d3b6613156ce7b45b83b3d33dfc6840e Mon Sep 17 00:00:00 2001 From: Grzegorz Adam Hankiewicz Date: Thu, 26 Jun 2014 12:27:09 +0200 Subject: [PATCH 221/270] Mentions nodejs in js intro. Refs #1299. --- doc/backends.txt | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/doc/backends.txt b/doc/backends.txt index 6e77dfb3c0..0ab682c290 100644 --- a/doc/backends.txt +++ b/doc/backends.txt @@ -26,10 +26,13 @@ concentrate in a single place all the backend and interfacing options. The Nimrod compiler supports mainly two backend families: the C, C++ and Objective-C targets and the JavaScript target. `The C like targets`_ creates -source files which can be compiled into a library or a final executable. The -JavaScript target generates a ``.js`` file which you call from an HTML file. -On top of generating a library or executable, Nimrod offers bidirectional -interfacing with the backend targets through generic and specific pragmas. +source files which can be compiled into a library or a final executable. `The +JavaScript target`_ can generate a ``.js`` file which you reference from an +HTML file or create a `standalone nodejs program `_. + +On top of generating libraries or standalone applications, Nimrod offers +bidirectional interfacing with the backend targets through generic and +specific pragmas. Backends From 84643abd3e68de6c5a19db2407d08def3aa85461 Mon Sep 17 00:00:00 2001 From: Felix Krause Date: Wed, 25 Jun 2014 22:16:09 +0200 Subject: [PATCH 222/270] Fixed doc comments in sets.nim --- lib/pure/collections/sets.nim | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/pure/collections/sets.nim b/lib/pure/collections/sets.nim index 380a33c5b0..992cb94864 100644 --- a/lib/pure/collections/sets.nim +++ b/lib/pure/collections/sets.nim @@ -157,19 +157,19 @@ proc `$`*[A](s: TSet[A]): string = proc union*[A](s1, s2: TSet[A]): TSet[A] = ## returns a new set of all items that are contained in at - ## least one of `l` and `r` + ## least one of `s1` and `s2` result = s1 incl(result, s2) proc intersection*[A](s1, s2: TSet[A]): TSet[A] = - ## returns a new set of all items that are contained in both `l` and `r` + ## returns a new set of all items that are contained in both `s1` and `s2` result = initSet[A](min(s1.data.len, s2.data.len)) for item in s1: if item in s2: incl(result, item) proc symmetricDifference*[A](s1, s2: TSet[A]): TSet[A] = ## returns a new set of all items that are contained in either - ## `l` or `r`, but not both + ## `s1` or `s2`, but not both result = s1 for item in s2: if containsOrIncl(result, item): excl(result, item) @@ -187,7 +187,7 @@ proc `xor`*[A](s1, s2: TSet[A]): TSet[A] {.inline.} = result = symmetricDifference(s1, s2) proc disjoint*[A](s1, s2: TSet[A]): bool = - ## returns true iff `l` and `r` have no items in common + ## returns true iff `s1` and `s2` have no items in common for item in s1: if item in s2: return false return true From ac3f872fa3a29effc1338008f45fe3d7332efc0e Mon Sep 17 00:00:00 2001 From: Felix Krause Date: Thu, 26 Jun 2014 20:48:13 +0200 Subject: [PATCH 223/270] Fixed TSet proc names to conform with set --- lib/pure/collections/sets.nim | 6 +++--- tests/sets/tsets3.nim | 22 +++++++++++----------- 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/lib/pure/collections/sets.nim b/lib/pure/collections/sets.nim index 992cb94864..4ba67cb2e4 100644 --- a/lib/pure/collections/sets.nim +++ b/lib/pure/collections/sets.nim @@ -174,15 +174,15 @@ proc symmetricDifference*[A](s1, s2: TSet[A]): TSet[A] = for item in s2: if containsOrIncl(result, item): excl(result, item) -proc `or`*[A](s1, s2: TSet[A]): TSet[A] {.inline.} = +proc `+`*[A](s1, s2: TSet[A]): TSet[A] {.inline.} = ## alias for `union` result = union(s1, s2) -proc `and`*[A](s1, s2: TSet[A]): TSet[A] {.inline.} = +proc `*`*[A](s1, s2: TSet[A]): TSet[A] {.inline.} = ## alias for `intersection` result = intersection(s1, s2) -proc `xor`*[A](s1, s2: TSet[A]): TSet[A] {.inline.} = +proc `-+-`*[A](s1, s2: TSet[A]): TSet[A] {.inline.} = ## alias for `symmetricDifference` result = symmetricDifference(s1, s2) diff --git a/tests/sets/tsets3.nim b/tests/sets/tsets3.nim index 5d14a1d8f0..d2b15d72d1 100644 --- a/tests/sets/tsets3.nim +++ b/tests/sets/tsets3.nim @@ -8,8 +8,8 @@ let block union: let s1_s2 = union(s1, s2) - s1_s3 = s1 or s3 - s2_s3 = s2 or s3 + s1_s3 = s1 + s3 + s2_s3 = s2 + s3 assert s1_s2.len == 7 assert s1_s3.len == 8 @@ -25,14 +25,14 @@ block union: assert i in s1_s3 assert i in s2_s3 - assert((s1 or s1) == s1) - assert((s2 or s1) == s1_s2) + assert((s1 + s1) == s1) + assert((s2 + s1) == s1_s2) block intersection: let s1_s2 = intersection(s1, s2) s1_s3 = intersection(s1, s3) - s2_s3 = s2 and s3 + s2_s3 = s2 * s3 assert s1_s2.len == 3 assert s1_s3.len == 0 @@ -48,14 +48,14 @@ block intersection: assert i in s2 assert i in s3 - assert((s2 and s2) == s2) - assert((s3 and s2) == s2_s3) + assert((s2 * s2) == s2) + assert((s3 * s2) == s2_s3) block symmetricDifference: let s1_s2 = symmetricDifference(s1, s2) - s1_s3 = s1 xor s3 - s2_s3 = s2 xor s3 + s1_s3 = s1 -+- s3 + s2_s3 = s2 -+- s3 assert s1_s2.len == 4 assert s1_s3.len == 8 @@ -71,8 +71,8 @@ block symmetricDifference: assert i in s1_s3 xor i in s1 assert i in s2_s3 xor i in s2 - assert((s3 xor s3) == initSet[int]()) - assert((s3 xor s1) == s1_s3) + assert((s3 -+- s3) == initSet[int]()) + assert((s3 -+- s1) == s1_s3) block disjoint: assert(not disjoint(s1, s2)) From 79cdb26b4659005c8d9ae2d03c868b78fc2622f5 Mon Sep 17 00:00:00 2001 From: def Date: Fri, 27 Jun 2014 15:14:45 +0200 Subject: [PATCH 224/270] Add missing operators for uint and uint64: `+=`, `-=`, `*=` --- lib/core/unsigned.nim | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/lib/core/unsigned.nim b/lib/core/unsigned.nim index a3ddd4125d..db46c0c0ba 100644 --- a/lib/core/unsigned.nim +++ b/lib/core/unsigned.nim @@ -57,3 +57,16 @@ proc `<=`*[T: SomeUInt](x, y: T): bool {.magic: "LeU", noSideEffect.} proc `<`*[T: SomeUInt](x, y: T): bool {.magic: "LtU", noSideEffect.} ## Returns true iff ``unsigned(x) < unsigned(y)``. + +proc `+=`*[T: uint|uint64](x: var T, y: T) {.magic: "Inc", noSideEffect.} + ## Increments uints and uint64s, uint8..uint32 are TOrdinals, and already + ## have a definition in the System module. + +proc `-=`*[T: uint|uint64](x: var T, y: T) {.magic: "Dec", noSideEffect.} + ## Decrements uints and uint64s, uint8..uint32 are TOrdinals, and already + ## have a definition in the System module. + +proc `*=`*[T: uint|uint64](x: var T, y: T) {.inline, noSideEffect.} = + ## Binary `*=` operator for uints and uint64s, uint8..uint32 are TOrdinals, + ## and already have a definition in the System module. + x = x * y From 4f24facb7ca0e62cb19f4b3c3aceb63f31cf6569 Mon Sep 17 00:00:00 2001 From: def Date: Fri, 27 Jun 2014 16:32:08 +0200 Subject: [PATCH 225/270] Add inc and dec for uint and uint64 --- lib/core/unsigned.nim | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/lib/core/unsigned.nim b/lib/core/unsigned.nim index db46c0c0ba..c9f7d0d154 100644 --- a/lib/core/unsigned.nim +++ b/lib/core/unsigned.nim @@ -70,3 +70,11 @@ proc `*=`*[T: uint|uint64](x: var T, y: T) {.inline, noSideEffect.} = ## Binary `*=` operator for uints and uint64s, uint8..uint32 are TOrdinals, ## and already have a definition in the System module. x = x * y + +proc inc*[T: uint|uint64](x: var T, y = 1) {.magic: "Inc", noSideEffect.} + ## Increments uints and uint64s ``x`` by ``y``, uint8..uint32 are TOrdinals, + ## and already have a definition in the System module. + +proc dec*[T: uint|uint64](x: var T, y = 1) {.magic: "Dec", noSideEffect.} + ## Decrements uints and uint64s ``x`` by ``y``, uint8..uint32 are TOrdinals, + ## and already have a definition in the System module. From b405462a8677cf3c06644fa55fbba30f273b6039 Mon Sep 17 00:00:00 2001 From: Clay Sweetser Date: Fri, 27 Jun 2014 18:20:42 -0400 Subject: [PATCH 226/270] Add 'lib/pure/concurrency' path to nimrod.ini Fixes #1303 --- compiler/nimrod.ini | 1 + 1 file changed, 1 insertion(+) diff --git a/compiler/nimrod.ini b/compiler/nimrod.ini index 8b2353aaba..44e16cec85 100644 --- a/compiler/nimrod.ini +++ b/compiler/nimrod.ini @@ -79,6 +79,7 @@ Files: "lib/system/*.nim" Files: "lib/core/*.nim" Files: "lib/pure/*.nim" Files: "lib/pure/collections/*.nim" +Files: "lib/pure/concurrency/*.nim" Files: "lib/impure/*.nim" Files: "lib/wrappers/*.nim" From 9ea0d0c59e5f07b473f8c7bbb60ccb74bc0b8c63 Mon Sep 17 00:00:00 2001 From: Dominik Picheta Date: Sat, 28 Jun 2014 12:14:05 +0100 Subject: [PATCH 227/270] Futures with an error but no callback no longer raise. This was done as a safety measure against futures which may be accidentally discarded when they have an exception leading the exception being silently forgotten. Unfortunately we will need to come up with some other solution. --- lib/pure/asyncdispatch.nim | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/lib/pure/asyncdispatch.nim b/lib/pure/asyncdispatch.nim index 6d9e605f15..6292bfc120 100644 --- a/lib/pure/asyncdispatch.nim +++ b/lib/pure/asyncdispatch.nim @@ -26,12 +26,12 @@ export TPort ## **Note:** This module is still largely experimental. -# TODO: Discarded void PFutures need to checked for exception. +# TODO: Discarded void PFutures need to be checked for exception. # TODO: ``except`` statement (without `try`) does not work. # TODO: Multiple exception names in a ``except`` don't work. # TODO: The effect system (raises: []) has trouble with my try transformation. # TODO: Can't await in a 'except' body - +# TODO: getCurrentException(Msg) don't work # -- Futures @@ -77,7 +77,8 @@ proc fail*[T](future: PFuture[T], error: ref EBase) = # This is to prevent exceptions from being silently ignored when a future # is discarded. # TODO: This may turn out to be a bad idea. - raise error + # Turns out this is a bad idea. + #raise error proc `callback=`*(future: PFutureBase, cb: proc () {.closure,gcsafe.}) = ## Sets the callback proc to be called when the future completes. @@ -775,14 +776,16 @@ proc accept*(socket: TAsyncFD): PFuture[TAsyncFD] = # -- Await Macro -template createCb*(retFutureSym, iteratorNameSym: expr): stmt {.immediate.} = +template createCb*(retFutureSym, iteratorNameSym, + name: expr): stmt {.immediate.} = var nameIterVar = iteratorNameSym proc cb {.closure,gcsafe.} = try: if not nameIterVar.finished: var next = nameIterVar() if next == nil: - assert retFutureSym.finished, "Async procedure's return Future was not finished." + assert retFutureSym.finished, "Async procedure's (" & + name & ") return Future was not finished." else: next.callback = cb except: @@ -987,7 +990,8 @@ macro async*(prc: stmt): stmt {.immediate.} = # -> createCb(retFuture) var cbName = newIdentNode("cb") - var procCb = newCall("createCb", retFutureSym, iteratorNameSym) + var procCb = newCall("createCb", retFutureSym, iteratorNameSym, + newStrLitNode(prc[0].getName)) outerProcBody.add procCb # -> return retFuture @@ -1010,6 +1014,7 @@ macro async*(prc: stmt): stmt {.immediate.} = result[6] = outerProcBody #echo(treeRepr(result)) + #if prc[0].getName == "routeReq": #echo(toStrLit(result)) proc recvLine*(socket: TAsyncFD): PFuture[string] {.async.} = From f830aed869235518451c44659d47ca07b142f1aa Mon Sep 17 00:00:00 2001 From: Dominik Picheta Date: Sat, 28 Jun 2014 13:08:11 +0100 Subject: [PATCH 228/270] AsyncHttpServer now supports POST. --- lib/pure/asyncdispatch.nim | 3 ++- lib/pure/asynchttpserver.nim | 39 +++++++++++++++++++++++++++++------- 2 files changed, 34 insertions(+), 8 deletions(-) diff --git a/lib/pure/asyncdispatch.nim b/lib/pure/asyncdispatch.nim index 6292bfc120..12329951c4 100644 --- a/lib/pure/asyncdispatch.nim +++ b/lib/pure/asyncdispatch.nim @@ -779,6 +779,7 @@ proc accept*(socket: TAsyncFD): PFuture[TAsyncFD] = template createCb*(retFutureSym, iteratorNameSym, name: expr): stmt {.immediate.} = var nameIterVar = iteratorNameSym + #{.push stackTrace: off.} proc cb {.closure,gcsafe.} = try: if not nameIterVar.finished: @@ -791,7 +792,7 @@ template createCb*(retFutureSym, iteratorNameSym, except: retFutureSym.fail(getCurrentException()) cb() - + #{.pop.} proc generateExceptionCheck(futSym, exceptBranch, rootReceiver: PNimrodNode): PNimrodNode {.compileTime.} = if exceptBranch == nil: diff --git a/lib/pure/asynchttpserver.nim b/lib/pure/asynchttpserver.nim index 005c56ebc6..1b47cf5f1b 100644 --- a/lib/pure/asynchttpserver.nim +++ b/lib/pure/asynchttpserver.nim @@ -20,7 +20,7 @@ type protocol*: tuple[orig: string, major, minor: int] url*: TURL hostname*: string ## The hostname of the client that made the request. - body*: string # TODO + body*: string PAsyncHttpServer* = ref object socket: PAsyncSocket @@ -78,7 +78,7 @@ proc parseHeader(line: string): tuple[key, value: string] = i += line.skipWhiteSpace(i) i += line.parseUntil(result.value, {'\c', '\L'}, i) -proc parseProtocol(protocol: string): tuple[orig: string, major, minor: int] = +proc parseProtocol(protocol: string): tuple[orig: string, major, minor: int] = var i = protocol.skipIgnoreCase("HTTP/") if i != 5: raise newException(EInvalidValue, "Invalid request protocol. Got: " & @@ -88,6 +88,9 @@ proc parseProtocol(protocol: string): tuple[orig: string, major, minor: int] = i.inc # Skip . i.inc protocol.parseInt(result.minor, i) +proc sendStatus(client: PAsyncSocket, status: string): PFuture[void] = + client.send("HTTP/1.1 " & status & "\c\L") + proc processClient(client: PAsyncSocket, address: string, callback: proc (request: TRequest): PFuture[void]) {.async.} = # GET /path HTTP/1.1 @@ -97,6 +100,7 @@ proc processClient(client: PAsyncSocket, address: string, request.hostname = address assert client != nil request.client = client + var runCallback = true # First line - GET /path HTTP/1.1 let line = await client.recvLine() # TODO: Timeouts. @@ -106,8 +110,7 @@ proc processClient(client: PAsyncSocket, address: string, let lineParts = line.split(' ') if lineParts.len != 3: request.respond(Http400, "Invalid request. Got: " & line) - client.close() - return + runCallback = false let reqMethod = lineParts[0] let path = lineParts[1] @@ -132,13 +135,35 @@ proc processClient(client: PAsyncSocket, address: string, request.protocol = protocol.parseProtocol() except EInvalidValue: request.respond(Http400, "Invalid request protocol. Got: " & protocol) - return + runCallback = false + + if reqMethod.normalize == "post": + # Check for Expect header + if request.headers.hasKey("Expect"): + if request.headers["Expect"].toLower == "100-continue": + await client.sendStatus("100 Continue") + else: + await client.sendStatus("417 Expectation Failed") + # Read the body + # - Check for Content-length header + if request.headers.hasKey("Content-Length"): + var contentLength = 0 + if parseInt(request.headers["Content-Length"], contentLength) == 0: + await request.respond(Http400, "Bad Request. Invalid Content-Length.") + else: + request.body = await client.recv(contentLength) + assert request.body.len == contentLength + else: + await request.respond(Http400, "Bad Request. No Content-Length.") + runCallback = false + case reqMethod.normalize of "get", "post", "head", "put", "delete", "trace", "options", "connect", "patch": - await callback(request) + if runCallback: + await callback(request) else: - request.respond(Http400, "Invalid request method. Got: " & reqMethod) + await request.respond(Http400, "Invalid request method. Got: " & reqMethod) # Persistent connections if (request.protocol == HttpVer11 and From f6c4864f9fe2c4b8fdd874c4f55c2c38c36d476f Mon Sep 17 00:00:00 2001 From: def Date: Sat, 28 Jun 2014 14:38:48 +0200 Subject: [PATCH 229/270] Rename keepIfIt to keepItIf --- lib/pure/collections/sequtils.nim | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/lib/pure/collections/sequtils.nim b/lib/pure/collections/sequtils.nim index 8e50b9e204..27e02a5db5 100644 --- a/lib/pure/collections/sequtils.nim +++ b/lib/pure/collections/sequtils.nim @@ -270,16 +270,16 @@ template filterIt*(seq1, pred: expr): expr {.immediate.} = if pred: result.add(it) result -template keepIfIt*(varSeq, pred: expr) = +template keepItIf*(varSeq, pred: expr) = ## Convenience template around the ``keepIf`` proc to reduce typing. ## ## Unlike the `proc` version, the predicate needs to be an expression using - ## the ``it`` variable for testing, like: ``keepIfIt("abcxyz", it == 'x')``. + ## the ``it`` variable for testing, like: ``keepItIf("abcxyz", it == 'x')``. ## Example: ## ## .. code-block:: nimrod ## var candidates = @["foo", "bar", "baz", "foobar"] - ## keepIfIt(candidates, it.len == 3 and it[0] == 'b') + ## keepItIf(candidates, it.len == 3 and it[0] == 'b') ## assert candidates == @["bar", "baz"] var pos = 0 for i in 0 .. Date: Sat, 28 Jun 2014 16:24:14 +0200 Subject: [PATCH 230/270] Add type constraints for uint and uint64 to the System module's procs instead --- lib/core/unsigned.nim | 21 --------------------- lib/system.nim | 10 +++++----- 2 files changed, 5 insertions(+), 26 deletions(-) diff --git a/lib/core/unsigned.nim b/lib/core/unsigned.nim index c9f7d0d154..a3ddd4125d 100644 --- a/lib/core/unsigned.nim +++ b/lib/core/unsigned.nim @@ -57,24 +57,3 @@ proc `<=`*[T: SomeUInt](x, y: T): bool {.magic: "LeU", noSideEffect.} proc `<`*[T: SomeUInt](x, y: T): bool {.magic: "LtU", noSideEffect.} ## Returns true iff ``unsigned(x) < unsigned(y)``. - -proc `+=`*[T: uint|uint64](x: var T, y: T) {.magic: "Inc", noSideEffect.} - ## Increments uints and uint64s, uint8..uint32 are TOrdinals, and already - ## have a definition in the System module. - -proc `-=`*[T: uint|uint64](x: var T, y: T) {.magic: "Dec", noSideEffect.} - ## Decrements uints and uint64s, uint8..uint32 are TOrdinals, and already - ## have a definition in the System module. - -proc `*=`*[T: uint|uint64](x: var T, y: T) {.inline, noSideEffect.} = - ## Binary `*=` operator for uints and uint64s, uint8..uint32 are TOrdinals, - ## and already have a definition in the System module. - x = x * y - -proc inc*[T: uint|uint64](x: var T, y = 1) {.magic: "Inc", noSideEffect.} - ## Increments uints and uint64s ``x`` by ``y``, uint8..uint32 are TOrdinals, - ## and already have a definition in the System module. - -proc dec*[T: uint|uint64](x: var T, y = 1) {.magic: "Dec", noSideEffect.} - ## Decrements uints and uint64s ``x`` by ``y``, uint8..uint32 are TOrdinals, - ## and already have a definition in the System module. diff --git a/lib/system.nim b/lib/system.nim index f457078491..0cd1f77df4 100644 --- a/lib/system.nim +++ b/lib/system.nim @@ -431,12 +431,12 @@ proc pred*[T](x: Ordinal[T], y = 1): T {.magic: "Pred", noSideEffect.} ## an ordinal type. If such a value does not exist, ``EOutOfRange`` is raised ## or a compile time error occurs. -proc inc*[T](x: var Ordinal[T], y = 1) {.magic: "Inc", noSideEffect.} +proc inc*[T: Ordinal|uint|uint64](x: var T, y = 1) {.magic: "Inc", noSideEffect.} ## increments the ordinal ``x`` by ``y``. If such a value does not ## exist, ``EOutOfRange`` is raised or a compile time error occurs. This is a ## short notation for: ``x = succ(x, y)``. -proc dec*[T](x: var Ordinal[T], y = 1) {.magic: "Dec", noSideEffect.} +proc dec*[T: Ordinal|uint|uint64](x: var T, y = 1) {.magic: "Dec", noSideEffect.} ## decrements the ordinal ``x`` by ``y``. If such a value does not ## exist, ``EOutOfRange`` is raised or a compile time error occurs. This is a ## short notation for: ``x = pred(x, y)``. @@ -2745,13 +2745,13 @@ proc staticExec*(command: string, input = ""): string {. ## inside a pragma like `passC `_ or `passL ## `_. -proc `+=`*[T: TOrdinal](x: var T, y: T) {.magic: "Inc", noSideEffect.} +proc `+=`*[T: TOrdinal|uint|uint64](x: var T, y: T) {.magic: "Inc", noSideEffect.} ## Increments an ordinal -proc `-=`*[T: TOrdinal](x: var T, y: T) {.magic: "Dec", noSideEffect.} +proc `-=`*[T: TOrdinal|uint|uint64](x: var T, y: T) {.magic: "Dec", noSideEffect.} ## Decrements an ordinal -proc `*=`*[T: TOrdinal](x: var T, y: T) {.inline, noSideEffect.} = +proc `*=`*[T: TOrdinal|uint|uint64](x: var T, y: T) {.inline, noSideEffect.} = ## Binary `*=` operator for ordinals x = x * y From cbcf01fc06a64082a868d122403f7cd90afebb6b Mon Sep 17 00:00:00 2001 From: Reimer Behrends Date: Sat, 28 Jun 2014 00:37:17 +0200 Subject: [PATCH 231/270] Rename sequtils.distnct to sequtils.deduplicate. --- lib/pure/collections/sequtils.nim | 14 +++++--------- tests/seq/tsequtils.nim | 4 ++-- web/news.txt | 1 + 3 files changed, 8 insertions(+), 11 deletions(-) diff --git a/lib/pure/collections/sequtils.nim b/lib/pure/collections/sequtils.nim index f5db9d3fab..15bb2a154c 100644 --- a/lib/pure/collections/sequtils.nim +++ b/lib/pure/collections/sequtils.nim @@ -47,19 +47,15 @@ proc concat*[T](seqs: varargs[seq[T]]): seq[T] = result[i] = itm inc(i) -proc distnct*[T](seq1: seq[T]): seq[T] = +proc deduplicate*[T](seq1: seq[T]): seq[T] = ## Returns a new sequence without duplicates. ## - ## This proc is `misspelled` on purpose to avoid a clash with the keyword - ## ``distinct`` used to `define a derived type incompatible with its base - ## type `_. Example: - ## ## .. code-block:: nimrod ## let ## dup1 = @[1, 1, 3, 4, 2, 2, 8, 1, 4] ## dup2 = @["a", "a", "c", "d", "d"] - ## unique1 = distnct(dup1) - ## unique2 = distnct(dup2) + ## unique1 = deduplicate(dup1) + ## unique2 = deduplicate(dup2) ## assert unique1 == @[1, 3, 4, 2, 8] ## assert unique2 == @["a", "c", "d"] result = @[] @@ -387,8 +383,8 @@ when isMainModule: let dup1 = @[1, 1, 3, 4, 2, 2, 8, 1, 4] dup2 = @["a", "a", "c", "d", "d"] - unique1 = distnct(dup1) - unique2 = distnct(dup2) + unique1 = deduplicate(dup1) + unique2 = deduplicate(dup2) assert unique1 == @[1, 3, 4, 2, 8] assert unique2 == @["a", "c", "d"] diff --git a/tests/seq/tsequtils.nim b/tests/seq/tsequtils.nim index 7bc15ef9cc..3a7eeeffa9 100644 --- a/tests/seq/tsequtils.nim +++ b/tests/seq/tsequtils.nim @@ -50,6 +50,6 @@ var concatseq = concat(seq1,seq2) echo "Concat: ", $$(concatseq) var seq3 = @[1,2,3,4,5,5,5,7] -var discntseq = distnct(seq3) -echo "Distnct: ", $$(discntseq) +var dedupseq = deduplicate(seq3) +echo "Deduplicate: ", $$(dedupseq) diff --git a/web/news.txt b/web/news.txt index b7403a3c72..eef71a2f7a 100644 --- a/web/news.txt +++ b/web/news.txt @@ -19,6 +19,7 @@ News - Added module ``cpuinfo``. - Added module ``threadpool``. + - ``sequtils.distnct`` has been renamed to ``sequtils.deduplicate``. 2014-04-21 Version 0.9.4 released From 678f3d7f5bf7998816431f38604c0b9e0f27c90a Mon Sep 17 00:00:00 2001 From: Araq Date: Mon, 30 Jun 2014 16:45:55 +0200 Subject: [PATCH 232/270] fixes constant array indexing bug --- compiler/evals.nim | 1502 ----------------- compiler/semfold.nim | 13 +- compiler/vmgen.nim | 45 +- .../android/scripts/jnibuild.sh | 0 .../android/scripts/nimbuild.sh | 0 .../cross_calculator/android/scripts/tags.sh | 0 examples/cross_calculator/ios/scripts/tags.sh | 0 .../ios/scripts/xcode_prebuild.sh | 0 lib/pure/unidecode/gen.py | 0 tests/vm/tarrayboundeval.nim | 10 +- tinyc/tests/gcctestsuite.sh | 0 tinyc/texi2pod.pl | 0 12 files changed, 47 insertions(+), 1523 deletions(-) delete mode 100644 compiler/evals.nim mode change 100755 => 100644 examples/cross_calculator/android/scripts/jnibuild.sh mode change 100755 => 100644 examples/cross_calculator/android/scripts/nimbuild.sh mode change 100755 => 100644 examples/cross_calculator/android/scripts/tags.sh mode change 100755 => 100644 examples/cross_calculator/ios/scripts/tags.sh mode change 100755 => 100644 examples/cross_calculator/ios/scripts/xcode_prebuild.sh mode change 100755 => 100644 lib/pure/unidecode/gen.py mode change 100755 => 100644 tinyc/tests/gcctestsuite.sh mode change 100755 => 100644 tinyc/texi2pod.pl diff --git a/compiler/evals.nim b/compiler/evals.nim deleted file mode 100644 index 151adf6909..0000000000 --- a/compiler/evals.nim +++ /dev/null @@ -1,1502 +0,0 @@ -# -# -# The Nimrod Compiler -# (c) Copyright 2013 Andreas Rumpf -# -# See the file "copying.txt", included in this -# distribution, for details about the copyright. -# - -# This file implements the evaluator for Nimrod code. -# The evaluator is very slow, but simple. Since this -# is used mainly for evaluating macros and some other -# stuff at compile time, performance is not that -# important. - -import - strutils, magicsys, lists, options, ast, astalgo, trees, treetab, nimsets, - msgs, os, condsyms, idents, renderer, types, passes, semfold, transf, - parser, ropes, rodread, idgen, osproc, streams, evaltempl - -when hasFFI: - import evalffi - -type - PStackFrame* = ref TStackFrame - TStackFrame* = object - prc: PSym # current prc; proc that is evaluated - slots: TNodeSeq # parameters passed to the proc + locals; - # parameters come first - call: PNode - next: PStackFrame # for stacking - - TEvalMode* = enum ## reason for evaluation - emRepl, ## evaluate because in REPL mode - emConst, ## evaluate for 'const' according to spec - emOptimize, ## evaluate for optimization purposes (same as - ## emConst?) - emStatic ## evaluate for enforced compile time eval - ## ('static' context) - - TSandboxFlag* = enum ## what the evaluation engine should allow - allowCast, ## allow unsafe language feature: 'cast' - allowFFI, ## allow the FFI - allowInfiniteLoops ## allow endless loops - TSandboxFlags* = set[TSandboxFlag] - - TEvalContext* = object of passes.TPassContext - module*: PSym - tos*: PStackFrame # top of stack - lastException*: PNode - callsite: PNode # for 'callsite' magic - mode*: TEvalMode - features: TSandboxFlags - globals*: TIdNodeTable # state of global vars - getType*: proc(n: PNode): PNode {.closure.} - handleIsOperator*: proc(n: PNode): PNode {.closure.} - - PEvalContext* = ref TEvalContext - - TEvalFlag = enum - efNone, efLValue - TEvalFlags = set[TEvalFlag] - -const - evalMaxIterations = 500_000 # max iterations of all loops - evalMaxRecDepth = 10_000 # max recursion depth for evaluation - -# other idea: use a timeout! -> Wether code compiles depends on the machine -# the compiler runs on then! Bad idea! - -proc newStackFrame*(): PStackFrame = - new(result) - result.slots = @[] - -proc newEvalContext*(module: PSym, mode: TEvalMode): PEvalContext = - new(result) - result.module = module - result.mode = mode - result.features = {allowFFI} - initIdNodeTable(result.globals) - -proc pushStackFrame*(c: PEvalContext, t: PStackFrame) {.inline.} = - t.next = c.tos - c.tos = t - -proc popStackFrame*(c: PEvalContext) {.inline.} = - if c.tos != nil: c.tos = c.tos.next - else: InternalError("popStackFrame") - -proc evalMacroCall*(c: PEvalContext, n, nOrig: PNode, sym: PSym): PNode -proc evalAux(c: PEvalContext, n: PNode, flags: TEvalFlags): PNode - -proc raiseCannotEval(c: PEvalContext, info: TLineInfo): PNode = - if defined(debug) and gVerbosity >= 3: writeStackTrace() - result = newNodeI(nkExceptBranch, info) - # creating a nkExceptBranch without sons - # means that it could not be evaluated - -proc stackTraceAux(x: PStackFrame) = - if x != nil: - stackTraceAux(x.next) - var info = if x.call != nil: x.call.info else: UnknownLineInfo() - # we now use the same format as in system/except.nim - var s = toFilename(info) - var line = toLineNumber(info) - if line > 0: - add(s, '(') - add(s, $line) - add(s, ')') - if x.prc != nil: - for k in 1..max(1, 25-s.len): add(s, ' ') - add(s, x.prc.name.s) - MsgWriteln(s) - -proc stackTrace(c: PEvalContext, info: TLineInfo, msg: TMsgKind, arg = "") = - MsgWriteln("stack trace: (most recent call last)") - stackTraceAux(c.tos) - LocalError(info, msg, arg) - -template isSpecial(n: PNode): bool = n.kind == nkExceptBranch -template bailout() {.dirty.} = - if isSpecial(result): return - -template evalX(n, flags) {.dirty.} = - result = evalAux(c, n, flags) - bailout() - -proc myreset(n: PNode) = - when defined(system.reset): - var oldInfo = n.info - reset(n[]) - n.info = oldInfo - -proc evalIf(c: PEvalContext, n: PNode): PNode = - var i = 0 - var length = sonsLen(n) - while (i < length) and (sonsLen(n.sons[i]) >= 2): - evalX(n.sons[i].sons[0], {}) - if result.kind == nkIntLit and result.intVal != 0: - return evalAux(c, n.sons[i].sons[1], {}) - inc(i) - if (i < length) and (sonsLen(n.sons[i]) < 2): - result = evalAux(c, n.sons[i].sons[0], {}) - else: - result = emptyNode - -proc evalCase(c: PEvalContext, n: PNode): PNode = - evalX(n.sons[0], {}) - var res = result - result = emptyNode - for i in countup(1, sonsLen(n) - 1): - if n.sons[i].kind == nkOfBranch: - for j in countup(0, sonsLen(n.sons[i]) - 2): - if overlap(res, n.sons[i].sons[j]): - return evalAux(c, lastSon(n.sons[i]), {}) - else: - result = evalAux(c, lastSon(n.sons[i]), {}) - -var - gWhileCounter: int # Use a counter to prevent endless loops! - # We make this counter global, because otherwise - # nested loops could make the compiler extremely slow. - gNestedEvals: int # count the recursive calls to ``evalAux`` to prevent - # endless recursion - -proc evalWhile(c: PEvalContext, n: PNode): PNode = - while true: - evalX(n.sons[0], {}) - if getOrdValue(result) == 0: - result = emptyNode; break - result = evalAux(c, n.sons[1], {}) - case result.kind - of nkBreakStmt: - if result.sons[0].kind == nkEmpty: - result = emptyNode # consume ``break`` token - # Bugfix (see tmacro2): but break in any case! - break - of nkExceptBranch, nkReturnToken: break - else: nil - dec(gWhileCounter) - if gWhileCounter <= 0: - if allowInfiniteLoops in c.features: - gWhileCounter = 0 - else: - stackTrace(c, n.info, errTooManyIterations) - break - -proc evalBlock(c: PEvalContext, n: PNode): PNode = - result = evalAux(c, n.sons[1], {}) - if result.kind == nkBreakStmt: - if result.sons[0] != nil: - assert(result.sons[0].kind == nkSym) - if n.sons[0].kind != nkEmpty: - assert(n.sons[0].kind == nkSym) - if result.sons[0].sym.id == n.sons[0].sym.id: result = emptyNode - # blocks can only be left with an explicit label now! - #else: - # result = emptyNode # consume ``break`` token - -proc evalFinally(c: PEvalContext, n, exc: PNode): PNode = - var finallyNode = lastSon(n) - if finallyNode.kind == nkFinally: - result = evalAux(c, finallyNode, {}) - if result.kind != nkExceptBranch: result = exc - else: - result = exc - -proc evalTry(c: PEvalContext, n: PNode): PNode = - result = evalAux(c, n.sons[0], {}) - case result.kind - of nkBreakStmt, nkReturnToken: - nil - of nkExceptBranch: - if sonsLen(result) >= 1: - # creating a nkExceptBranch without sons means that it could not be - # evaluated - var exc = result - var i = 1 - var length = sonsLen(n) - while (i < length) and (n.sons[i].kind == nkExceptBranch): - var blen = sonsLen(n.sons[i]) - if blen == 1: - # general except section: - result = evalAux(c, n.sons[i].sons[0], {}) - exc = result - break - else: - for j in countup(0, blen - 2): - assert(n.sons[i].sons[j].kind == nkType) - let a = exc.typ.skipTypes(abstractPtrs) - let b = n.sons[i].sons[j].typ.skipTypes(abstractPtrs) - if a == b: - result = evalAux(c, n.sons[i].sons[blen - 1], {}) - exc = result - break - inc(i) - result = evalFinally(c, n, exc) - else: result = evalFinally(c, n, emptyNode) - -proc getNullValue(typ: PType, info: TLineInfo): PNode -proc getNullValueAux(obj: PNode, result: PNode) = - case obj.kind - of nkRecList: - for i in countup(0, sonsLen(obj) - 1): getNullValueAux(obj.sons[i], result) - of nkRecCase: - getNullValueAux(obj.sons[0], result) - for i in countup(1, sonsLen(obj) - 1): - getNullValueAux(lastSon(obj.sons[i]), result) - of nkSym: - var s = obj.sym - var p = newNodeIT(nkExprColonExpr, result.info, s.typ) - addSon(p, newSymNode(s, result.info)) - addSon(p, getNullValue(s.typ, result.info)) - addSon(result, p) - else: InternalError(result.info, "getNullValueAux") - -proc getNullValue(typ: PType, info: TLineInfo): PNode = - var t = skipTypes(typ, abstractRange-{tyTypeDesc}) - result = emptyNode - case t.kind - of tyBool, tyEnum, tyChar, tyInt..tyInt64: - result = newNodeIT(nkIntLit, info, t) - of tyUInt..tyUInt64: - result = newNodeIT(nkUIntLit, info, t) - of tyFloat..tyFloat128: - result = newNodeIt(nkFloatLit, info, t) - of tyVar, tyPointer, tyPtr, tyRef, tyCString, tySequence, tyString, tyExpr, - tyStmt, tyTypeDesc, tyStatic, tyProc: - result = newNodeIT(nkNilLit, info, t) - of tyObject: - result = newNodeIT(nkPar, info, t) - getNullValueAux(t.n, result) - # initialize inherited fields: - var base = t.sons[0] - while base != nil: - getNullValueAux(skipTypes(base, skipPtrs).n, result) - base = base.sons[0] - of tyArray, tyArrayConstr: - result = newNodeIT(nkBracket, info, t) - for i in countup(0, int(lengthOrd(t)) - 1): - addSon(result, getNullValue(elemType(t), info)) - of tyTuple: - # XXX nkExprColonExpr is out of fashion ... - result = newNodeIT(nkPar, info, t) - for i in countup(0, sonsLen(t) - 1): - var p = newNodeIT(nkExprColonExpr, info, t.sons[i]) - var field = if t.n != nil: t.n.sons[i].sym else: newSym( - skField, getIdent(":tmp" & $i), t.owner, info) - addSon(p, newSymNode(field, info)) - addSon(p, getNullValue(t.sons[i], info)) - addSon(result, p) - of tySet: - result = newNodeIT(nkCurly, info, t) - else: InternalError("getNullValue: " & $t.kind) - -proc evalVarValue(c: PEvalContext, n: PNode): PNode = - result = evalAux(c, n, {}) - if result.kind in {nkType..nkNilLit}: result = result.copyNode - -proc allocSlot(c: PStackFrame; sym: PSym): int = - result = sym.position + ord(sym.kind == skParam) - if result == 0 and sym.kind != skResult: - result = c.slots.len - if result == 0: result = 1 - sym.position = result - setLen(c.slots, max(result+1, c.slots.len)) - -proc setSlot(c: PStackFrame, sym: PSym, val: PNode) = - assert sym.owner == c.prc or sfFromGeneric in sym.flags - let idx = allocSlot(c, sym) - c.slots[idx] = val - -proc setVar(c: PEvalContext, v: PSym, n: PNode) = - if sfGlobal notin v.flags: setSlot(c.tos, v, n) - else: IdNodeTablePut(c.globals, v, n) - -proc evalVar(c: PEvalContext, n: PNode): PNode = - for i in countup(0, sonsLen(n) - 1): - let a = n.sons[i] - if a.kind == nkCommentStmt: continue - #assert(a.sons[0].kind == nkSym) can happen for transformed vars - if a.kind == nkVarTuple: - result = evalVarValue(c, a.lastSon) - if result.kind in {nkType..nkNilLit}: - result = result.copyNode - bailout() - if result.kind != nkPar: - return raiseCannotEval(c, n.info) - for i in 0 .. a.len-3: - var v = a.sons[i].sym - setVar(c, v, result.sons[i]) - else: - if a.sons[2].kind != nkEmpty: - result = evalVarValue(c, a.sons[2]) - bailout() - else: - result = getNullValue(a.sons[0].typ, a.sons[0].info) - if a.sons[0].kind == nkSym: - var v = a.sons[0].sym - setVar(c, v, result) - else: - # assign to a.sons[0]: - var x = result - evalX(a.sons[0], {}) - myreset(x) - x.kind = result.kind - x.typ = result.typ - case x.kind - of nkCharLit..nkInt64Lit: x.intVal = result.intVal - of nkFloatLit..nkFloat64Lit: x.floatVal = result.floatVal - of nkStrLit..nkTripleStrLit: x.strVal = result.strVal - of nkIdent: x.ident = result.ident - of nkSym: x.sym = result.sym - else: - if x.kind notin {nkEmpty..nkNilLit}: - discardSons(x) - for j in countup(0, sonsLen(result) - 1): addSon(x, result.sons[j]) - result = emptyNode - -proc aliasNeeded(n: PNode, flags: TEvalFlags): bool = - result = efLValue in flags or n.typ == nil or - n.typ.kind in {tyExpr, tyStatic, tyStmt, tyTypeDesc} - -proc evalVariable(c: PStackFrame, sym: PSym, flags: TEvalFlags): PNode = - # We need to return a node to the actual value, - # which can be modified. - assert sym.position != 0 or skResult == sym.kind - var x = c - while x != nil: - if sym.owner == x.prc: - result = x.slots[sym.position] - assert result != nil - if not aliasNeeded(result, flags): - result = copyTree(result) - return - x = x.next - #internalError(sym.info, "cannot eval " & sym.name.s & " " & $sym.position) - result = raiseCannotEval(nil, sym.info) - #result = emptyNode - -proc evalGlobalVar(c: PEvalContext, s: PSym, flags: TEvalFlags): PNode = - if sfCompileTime in s.flags or c.mode == emRepl or s.kind == skForVar: - result = IdNodeTableGet(c.globals, s) - if result != nil: - if not aliasNeeded(result, flags): - result = copyTree(result) - else: - when hasFFI: - if sfImportc in s.flags and allowFFI in c.features: - result = importcSymbol(s) - IdNodeTablePut(c.globals, s, result) - return result - - result = s.ast - if result == nil or result.kind == nkEmpty: - result = getNullValue(s.typ, s.info) - else: - result = evalAux(c, result, {}) - if isSpecial(result): return - IdNodeTablePut(c.globals, s, result) - else: - result = raiseCannotEval(nil, s.info) - -proc optBody(c: PEvalContext, s: PSym): PNode = - result = s.getBody - -proc evalCall(c: PEvalContext, n: PNode): PNode = - var d = newStackFrame() - d.call = n - var prc = n.sons[0] - let isClosure = prc.kind == nkClosure - setlen(d.slots, sonsLen(n) + ord(isClosure)) - if isClosure: - #debug prc - evalX(prc.sons[1], {efLValue}) - d.slots[sonsLen(n)] = result - result = evalAux(c, prc.sons[0], {}) - else: - result = evalAux(c, prc, {}) - - if isSpecial(result): return - prc = result - # bind the actual params to the local parameter of a new binding - if prc.kind != nkSym: - InternalError(n.info, "evalCall " & n.renderTree) - return - d.prc = prc.sym - if prc.sym.kind notin {skProc, skConverter, skMacro}: - InternalError(n.info, "evalCall") - return - for i in countup(1, sonsLen(n) - 1): - evalX(n.sons[i], {}) - d.slots[i] = result - if n.typ != nil: d.slots[0] = getNullValue(n.typ, n.info) - - when hasFFI: - if sfImportc in prc.sym.flags and allowFFI in c.features: - var newCall = newNodeI(nkCall, n.info, n.len) - newCall.sons[0] = evalGlobalVar(c, prc.sym, {}) - for i in 1 .. = 0) and (idx < sonsLen(x)): - result = x.sons[int(idx)] - if result.kind == nkExprColonExpr: result = result.sons[1] - if not aliasNeeded(result, flags): result = copyTree(result) - else: - stackTrace(c, n.info, errIndexOutOfBounds) - of nkBracket, nkMetaNode: - if (idx >= 0) and (idx < sonsLen(x)): - result = x.sons[int(idx)] - if not aliasNeeded(result, flags): result = copyTree(result) - else: - stackTrace(c, n.info, errIndexOutOfBounds) - of nkStrLit..nkTripleStrLit: - if efLValue in flags: return raiseCannotEval(c, n.info) - result = newNodeIT(nkCharLit, x.info, getSysType(tyChar)) - if (idx >= 0) and (idx < len(x.strVal)): - result.intVal = ord(x.strVal[int(idx) + 0]) - elif idx == len(x.strVal): - nil - else: - stackTrace(c, n.info, errIndexOutOfBounds) - else: stackTrace(c, n.info, errNilAccess) - -proc evalFieldAccess(c: PEvalContext, n: PNode, flags: TEvalFlags): PNode = - # a real field access; proc calls have already been transformed - # XXX: field checks! - evalX(n.sons[0], flags) - var x = result - if x.kind != nkPar: return raiseCannotEval(c, n.info) - # this is performance critical: - var field = n.sons[1].sym - result = x.sons[field.position] - if result.kind == nkExprColonExpr: result = result.sons[1] - if not aliasNeeded(result, flags): result = copyTree(result) - -proc evalAsgn(c: PEvalContext, n: PNode): PNode = - var a = n.sons[0] - if a.kind == nkBracketExpr and a.sons[0].typ.kind in {tyString, tyCString}: - evalX(a.sons[0], {efLValue}) - var x = result - evalX(a.sons[1], {}) - var idx = getOrdValue(result) - - evalX(n.sons[1], {}) - if result.kind notin {nkIntLit, nkCharLit}: return c.raiseCannotEval(n.info) - - if idx >= 0 and idx < len(x.strVal): - x.strVal[int(idx)] = chr(int(result.intVal)) - else: - stackTrace(c, n.info, errIndexOutOfBounds) - else: - evalX(n.sons[0], {efLValue}) - var x = result - evalX(n.sons[1], {}) - myreset(x) - x.kind = result.kind - x.typ = result.typ - case x.kind - of nkCharLit..nkInt64Lit: x.intVal = result.intVal - of nkFloatLit..nkFloat64Lit: x.floatVal = result.floatVal - of nkStrLit..nkTripleStrLit: x.strVal = result.strVal - of nkIdent: x.ident = result.ident - of nkSym: x.sym = result.sym - else: - if x.kind notin {nkEmpty..nkNilLit}: - discardSons(x) - for i in countup(0, sonsLen(result) - 1): addSon(x, result.sons[i]) - result = emptyNode - assert result.kind == nkEmpty - -proc evalSwap(c: PEvalContext, n: PNode): PNode = - evalX(n.sons[0], {efLValue}) - var x = result - evalX(n.sons[1], {efLValue}) - if x.kind != result.kind: - stackTrace(c, n.info, errCannotInterpretNodeX, $n.kind) - else: - case x.kind - of nkCharLit..nkInt64Lit: swap(x.intVal, result.intVal) - of nkFloatLit..nkFloat64Lit: swap(x.floatVal, result.floatVal) - of nkStrLit..nkTripleStrLit: swap(x.strVal, result.strVal) - of nkIdent: swap(x.ident, result.ident) - of nkSym: swap(x.sym, result.sym) - else: - var tmpn = copyTree(x) - discardSons(x) - for i in countup(0, sonsLen(result) - 1): addSon(x, result.sons[i]) - discardSons(result) - for i in countup(0, sonsLen(tmpn) - 1): addSon(result, tmpn.sons[i]) - result = emptyNode - -proc evalSym(c: PEvalContext, n: PNode, flags: TEvalFlags): PNode = - var s = n.sym - case s.kind - of skProc, skConverter, skMacro, skType: - result = n - #result = s.getBody - of skVar, skLet, skForVar, skTemp, skResult: - if sfGlobal notin s.flags: - result = evalVariable(c.tos, s, flags) - else: - result = evalGlobalVar(c, s, flags) - of skParam: - # XXX what about LValue? - if s.position + 1 <% c.tos.slots.len: - result = c.tos.slots[s.position + 1] - of skConst: result = s.ast - of skEnumField: result = newIntNodeT(s.position, n) - else: result = nil - let mask = if hasFFI and allowFFI in c.features: {sfForward} - else: {sfImportc, sfForward} - if result == nil or mask * s.flags != {}: - result = raiseCannotEval(c, n.info) - -proc evalIncDec(c: PEvalContext, n: PNode, sign: biggestInt): PNode = - evalX(n.sons[1], {efLValue}) - var a = result - evalX(n.sons[2], {}) - var b = result - case a.kind - of nkCharLit..nkInt64Lit: a.intval = a.intVal + sign * getOrdValue(b) - else: return raiseCannotEval(c, n.info) - result = emptyNode - -proc getStrValue(n: PNode): string = - case n.kind - of nkStrLit..nkTripleStrLit: result = n.strVal - else: - InternalError(n.info, "getStrValue") - result = "" - -proc evalEcho(c: PEvalContext, n: PNode): PNode = - for i in countup(1, sonsLen(n) - 1): - evalX(n.sons[i], {}) - Write(stdout, getStrValue(result)) - writeln(stdout, "") - result = emptyNode - -proc evalExit(c: PEvalContext, n: PNode): PNode = - if c.mode in {emRepl, emStatic}: - evalX(n.sons[1], {}) - Message(n.info, hintQuitCalled) - quit(int(getOrdValue(result))) - else: - result = raiseCannotEval(c, n.info) - -proc evalOr(c: PEvalContext, n: PNode): PNode = - evalX(n.sons[1], {}) - if result.intVal == 0: result = evalAux(c, n.sons[2], {}) - -proc evalAnd(c: PEvalContext, n: PNode): PNode = - evalX(n.sons[1], {}) - if result.intVal != 0: result = evalAux(c, n.sons[2], {}) - -proc evalNew(c: PEvalContext, n: PNode): PNode = - #if c.mode == emOptimize: return raiseCannotEval(c, n.info) - - # we ignore the finalizer for now and most likely forever :-) - evalX(n.sons[1], {efLValue}) - var a = result - var t = skipTypes(n.sons[1].typ, abstractVar) - if a.kind == nkEmpty: InternalError(n.info, "first parameter is empty") - myreset(a) - let u = getNullValue(t.sons[0], n.info) - a.kind = u.kind - a.typ = t - shallowCopy(a.sons, u.sons) - result = emptyNode - when false: - a.kind = nkRefTy - a.info = n.info - a.typ = t - a.sons = nil - addSon(a, getNullValue(t.sons[0], n.info)) - result = emptyNode - -proc evalDeref(c: PEvalContext, n: PNode, flags: TEvalFlags): PNode = - evalX(n.sons[0], {efLValue}) - case result.kind - of nkNilLit: stackTrace(c, n.info, errNilAccess) - of nkRefTy: - # XXX efLValue? - result = result.sons[0] - else: - if skipTypes(n.sons[0].typ, abstractInst).kind != tyRef: - result = raiseCannotEval(c, n.info) - -proc evalAddr(c: PEvalContext, n: PNode, flags: TEvalFlags): PNode = - evalX(n.sons[0], {efLValue}) - var a = result - var t = newType(tyPtr, c.module) - addSonSkipIntLit(t, a.typ) - result = newNodeIT(nkRefTy, n.info, t) - addSon(result, a) - -proc evalConv(c: PEvalContext, n: PNode): PNode = - result = evalAux(c, n.sons[1], {efLValue}) - if isSpecial(result): return - if result.typ != nil: - var a = result - result = foldConv(n, a) - if result == nil: - # foldConv() cannot deal with everything that we want to do here: - result = a - -proc evalCast(c: PEvalContext, n: PNode, flags: TEvalFlags): PNode = - if allowCast in c.features: - when hasFFI: - result = evalAux(c, n.sons[1], {efLValue}) - if isSpecial(result): return - InternalAssert result.typ != nil - result = fficast(result, n.typ) - else: - result = evalConv(c, n) - else: - result = raiseCannotEval(c, n.info) - -proc evalCheckedFieldAccess(c: PEvalContext, n: PNode, - flags: TEvalFlags): PNode = - result = evalAux(c, n.sons[0], flags) - -proc evalUpConv(c: PEvalContext, n: PNode, flags: TEvalFlags): PNode = - result = evalAux(c, n.sons[0], flags) - if isSpecial(result): return - var dest = skipTypes(n.typ, abstractPtrs) - var src = skipTypes(result.typ, abstractPtrs) - if inheritanceDiff(src, dest) > 0: - stackTrace(c, n.info, errInvalidConversionFromTypeX, typeToString(src)) - -proc evalRangeChck(c: PEvalContext, n: PNode): PNode = - evalX(n.sons[0], {}) - var x = result - evalX(n.sons[1], {}) - var a = result - evalX(n.sons[2], {}) - var b = result - if leValueConv(a, x) and leValueConv(x, b): - result = x # a <= x and x <= b - result.typ = n.typ - else: - stackTrace(c, n.info, errGenerated, - msgKindToString(errIllegalConvFromXtoY) % [ - typeToString(n.sons[0].typ), typeToString(n.typ)]) - -proc evalConvStrToCStr(c: PEvalContext, n: PNode): PNode = - result = evalAux(c, n.sons[0], {}) - if isSpecial(result): return - result.typ = n.typ - -proc evalConvCStrToStr(c: PEvalContext, n: PNode): PNode = - result = evalAux(c, n.sons[0], {}) - if isSpecial(result): return - result.typ = n.typ - -proc evalRaise(c: PEvalContext, n: PNode): PNode = - if c.mode in {emRepl, emStatic}: - if n.sons[0].kind != nkEmpty: - result = evalAux(c, n.sons[0], {}) - if isSpecial(result): return - var a = result - result = newNodeIT(nkExceptBranch, n.info, a.typ) - addSon(result, a) - c.lastException = result - elif c.lastException != nil: - result = c.lastException - else: - stackTrace(c, n.info, errExceptionAlreadyHandled) - result = newNodeIT(nkExceptBranch, n.info, nil) - addSon(result, ast.emptyNode) - else: - result = raiseCannotEval(c, n.info) - -proc evalReturn(c: PEvalContext, n: PNode): PNode = - if n.sons[0].kind != nkEmpty: - result = evalAsgn(c, n.sons[0]) - if isSpecial(result): return - result = newNodeIT(nkReturnToken, n.info, nil) - -proc evalProc(c: PEvalContext, n: PNode): PNode = - if n.sons[genericParamsPos].kind == nkEmpty: - var s = n.sons[namePos].sym - if (resultPos < sonsLen(n)) and (n.sons[resultPos].kind != nkEmpty): - var v = n.sons[resultPos].sym - result = getNullValue(v.typ, n.info) - if c.tos.slots.len == 0: setLen(c.tos.slots, 1) - c.tos.slots[0] = result - #IdNodeTablePut(c.tos.mapping, v, result) - result = evalAux(c, s.getBody, {}) - if result.kind == nkReturnToken: - result = c.tos.slots[0] - else: - result = evalAux(c, s.getBody, {}) - if result.kind == nkReturnToken: - result = emptyNode - else: - result = emptyNode - -proc evalHigh(c: PEvalContext, n: PNode): PNode = - result = evalAux(c, n.sons[1], {}) - if isSpecial(result): return - case skipTypes(n.sons[1].typ, abstractVar).kind - of tyOpenArray, tySequence, tyVarargs: - result = newIntNodeT(sonsLen(result)-1, n) - of tyString: result = newIntNodeT(len(result.strVal) - 1, n) - else: InternalError(n.info, "evalHigh") - -proc evalOf(c: PEvalContext, n: PNode): PNode = - result = evalAux(c, n.sons[1], {}) - if isSpecial(result): return - result = newIntNodeT(ord(inheritanceDiff(result.typ, n.sons[2].typ) >= 0), n) - -proc evalSetLengthStr(c: PEvalContext, n: PNode): PNode = - result = evalAux(c, n.sons[1], {efLValue}) - if isSpecial(result): return - var a = result - result = evalAux(c, n.sons[2], {}) - if isSpecial(result): return - var b = result - case a.kind - of nkStrLit..nkTripleStrLit: - var newLen = int(getOrdValue(b)) - setlen(a.strVal, newLen) - else: InternalError(n.info, "evalSetLengthStr") - result = emptyNode - -proc evalSetLengthSeq(c: PEvalContext, n: PNode): PNode = - result = evalAux(c, n.sons[1], {efLValue}) - if isSpecial(result): return - var a = result - result = evalAux(c, n.sons[2], {}) - if isSpecial(result): return - var b = result - if a.kind != nkBracket: - InternalError(n.info, "evalSetLengthSeq") - return - var newLen = int(getOrdValue(b)) - var oldLen = sonsLen(a) - setlen(a.sons, newLen) - for i in countup(oldLen, newLen - 1): - a.sons[i] = getNullValue(skipTypes(n.sons[1].typ, abstractVar), n.info) - result = emptyNode - -proc evalNewSeq(c: PEvalContext, n: PNode): PNode = - result = evalAux(c, n.sons[1], {efLValue}) - if isSpecial(result): return - var a = result - result = evalAux(c, n.sons[2], {}) - if isSpecial(result): return - var b = result - var t = skipTypes(n.sons[1].typ, abstractVar) - if a.kind == nkEmpty: InternalError(n.info, "first parameter is empty") - myreset(a) - a.kind = nkBracket - a.info = n.info - a.typ = t - a.sons = nil - var L = int(getOrdValue(b)) - newSeq(a.sons, L) - for i in countup(0, L-1): - a.sons[i] = getNullValue(t.sons[0], n.info) - result = emptyNode - -proc evalIncl(c: PEvalContext, n: PNode): PNode = - result = evalAux(c, n.sons[1], {efLValue}) - if isSpecial(result): return - var a = result - result = evalAux(c, n.sons[2], {}) - if isSpecial(result): return - var b = result - if not inSet(a, b): addSon(a, copyTree(b)) - result = emptyNode - -proc evalExcl(c: PEvalContext, n: PNode): PNode = - result = evalAux(c, n.sons[1], {efLValue}) - if isSpecial(result): return - var a = result - result = evalAux(c, n.sons[2], {}) - if isSpecial(result): return - var b = newNodeIT(nkCurly, n.info, n.sons[1].typ) - addSon(b, result) - var r = diffSets(a, b) - discardSons(a) - for i in countup(0, sonsLen(r) - 1): addSon(a, r.sons[i]) - result = emptyNode - -proc evalAppendStrCh(c: PEvalContext, n: PNode): PNode = - result = evalAux(c, n.sons[1], {efLValue}) - if isSpecial(result): return - var a = result - result = evalAux(c, n.sons[2], {}) - if isSpecial(result): return - var b = result - case a.kind - of nkStrLit..nkTripleStrLit: add(a.strVal, chr(int(getOrdValue(b)))) - else: return raiseCannotEval(c, n.info) - result = emptyNode - -proc evalConStrStr(c: PEvalContext, n: PNode): PNode = - # we cannot use ``evalOp`` for this as we can here have more than 2 arguments - var a = newNodeIT(nkStrLit, n.info, n.typ) - a.strVal = "" - for i in countup(1, sonsLen(n) - 1): - result = evalAux(c, n.sons[i], {}) - if isSpecial(result): return - a.strVal.add(getStrOrChar(result)) - result = a - -proc evalAppendStrStr(c: PEvalContext, n: PNode): PNode = - result = evalAux(c, n.sons[1], {efLValue}) - if isSpecial(result): return - var a = result - result = evalAux(c, n.sons[2], {}) - if isSpecial(result): return - var b = result - case a.kind - of nkStrLit..nkTripleStrLit: a.strVal = a.strVal & getStrOrChar(b) - else: return raiseCannotEval(c, n.info) - result = emptyNode - -proc evalAppendSeqElem(c: PEvalContext, n: PNode): PNode = - result = evalAux(c, n.sons[1], {efLValue}) - if isSpecial(result): return - var a = result - result = evalAux(c, n.sons[2], {}) - if isSpecial(result): return - var b = result - if a.kind == nkBracket: addSon(a, copyTree(b)) - else: return raiseCannotEval(c, n.info) - result = emptyNode - -proc evalRepr(c: PEvalContext, n: PNode): PNode = - result = evalAux(c, n.sons[1], {}) - if isSpecial(result): return - result = newStrNodeT(renderTree(result, {renderNoComments}), n) - -proc isEmpty(n: PNode): bool = - result = n != nil and n.kind == nkEmpty - -proc evalParseExpr(c: PEvalContext, n: PNode): PNode = - var code = evalAux(c, n.sons[1], {}) - var ast = parseString(code.getStrValue, code.info.toFilename, - code.info.line.int) - if sonsLen(ast) != 1: - GlobalError(code.info, errExprExpected, "multiple statements") - result = ast.sons[0] - #result.typ = newType(tyExpr, c.module) - -proc evalParseStmt(c: PEvalContext, n: PNode): PNode = - var code = evalAux(c, n.sons[1], {}) - result = parseString(code.getStrValue, code.info.toFilename, - code.info.line.int) - #result.typ = newType(tyStmt, c.module) - -proc evalTypeTrait*(trait, operand: PNode, context: PSym): PNode = - let typ = operand.typ.skipTypes({tyTypeDesc}) - case trait.sym.name.s.normalize - of "name": - result = newStrNode(nkStrLit, typ.typeToString(preferName)) - result.typ = newType(tyString, context) - result.info = trait.info - of "arity": - result = newIntNode(nkIntLit, typ.n.len-1) - result.typ = newType(tyInt, context) - result.info = trait.info - else: - internalAssert false - -proc expectString(n: PNode) = - if n.kind notin nkStrKinds: - GlobalError(n.info, errStringLiteralExpected) - -proc evalSlurp*(e: PNode, module: PSym): PNode = - expectString(e) - result = newNodeIT(nkStrLit, e.info, getSysType(tyString)) - try: - var filename = e.strVal.FindFile - result.strVal = readFile(filename) - # we produce a fake include statement for every slurped filename, so that - # the module dependencies are accurate: - appendToModule(module, newNode(nkIncludeStmt, e.info, @[ - newStrNode(nkStrLit, filename)])) - except EIO: - result.strVal = "" - LocalError(e.info, errCannotOpenFile, e.strVal) - -proc readOutput(p: PProcess): string = - result = "" - var output = p.outputStream - discard p.waitForExit - while not output.atEnd: - result.add(output.readLine) - -proc evalStaticExec*(cmd, input: PNode): PNode = - expectString(cmd) - var p = startCmd(cmd.strVal) - if input != nil: - expectString(input) - p.inputStream.write(input.strVal) - p.inputStream.close() - result = newStrNode(nkStrLit, p.readOutput) - result.typ = getSysType(tyString) - result.info = cmd.info - -proc evalExpandToAst(c: PEvalContext, original: PNode): PNode = - var - n = original.copyTree - macroCall = n.sons[1] - expandedSym = macroCall.sons[0].sym - - for i in countup(1, macroCall.sonsLen - 1): - macroCall.sons[i] = evalAux(c, macroCall.sons[i], {}) - - case expandedSym.kind - of skTemplate: - let genSymOwner = if c.tos != nil and c.tos.prc != nil: - c.tos.prc - else: - c.module - result = evalTemplate(macroCall, expandedSym, genSymOwner) - of skMacro: - # At this point macroCall.sons[0] is nkSym node. - # To be completely compatible with normal macro invocation, - # we want to replace it with nkIdent node featuring - # the original unmangled macro name. - macroCall.sons[0] = newIdentNode(expandedSym.name, expandedSym.info) - result = evalMacroCall(c, macroCall, original, expandedSym) - else: - InternalError(macroCall.info, - "ExpandToAst: expanded symbol is no macro or template") - result = emptyNode - -proc evalMagicOrCall(c: PEvalContext, n: PNode): PNode = - var m = getMagic(n) - case m - of mNone: result = evalCall(c, n) - of mOf: result = evalOf(c, n) - of mSizeOf: result = raiseCannotEval(c, n.info) - of mHigh: result = evalHigh(c, n) - of mExit: result = evalExit(c, n) - of mNew, mNewFinalize: result = evalNew(c, n) - of mNewSeq: result = evalNewSeq(c, n) - of mSwap: result = evalSwap(c, n) - of mInc: result = evalIncDec(c, n, 1) - of ast.mDec: result = evalIncDec(c, n, - 1) - of mEcho: result = evalEcho(c, n) - of mSetLengthStr: result = evalSetLengthStr(c, n) - of mSetLengthSeq: result = evalSetLengthSeq(c, n) - of mIncl: result = evalIncl(c, n) - of mExcl: result = evalExcl(c, n) - of mAnd: result = evalAnd(c, n) - of mOr: result = evalOr(c, n) - of mAppendStrCh: result = evalAppendStrCh(c, n) - of mAppendStrStr: result = evalAppendStrStr(c, n) - of mAppendSeqElem: result = evalAppendSeqElem(c, n) - of mParseExprToAst: result = evalParseExpr(c, n) - of mParseStmtToAst: result = evalParseStmt(c, n) - of mExpandToAst: result = evalExpandToAst(c, n) - of mTypeTrait: - let operand = evalAux(c, n.sons[1], {}) - result = evalTypeTrait(n[0], operand, c.module) - of mIs: - n.sons[1] = evalAux(c, n.sons[1], {}) - result = c.handleIsOperator(n) - of mSlurp: result = evalSlurp(evalAux(c, n.sons[1], {}), c.module) - of mStaticExec: - let cmd = evalAux(c, n.sons[1], {}) - let input = if n.sonsLen == 3: evalAux(c, n.sons[2], {}) else: nil - result = evalStaticExec(cmd, input) - of mNLen: - result = evalAux(c, n.sons[1], {efLValue}) - if isSpecial(result): return - var a = result - result = newNodeIT(nkIntLit, n.info, n.typ) - case a.kind - of nkEmpty..nkNilLit: nil - else: result.intVal = sonsLen(a) - of mNChild: - result = evalAux(c, n.sons[1], {efLValue}) - if isSpecial(result): return - var a = result - result = evalAux(c, n.sons[2], {efLValue}) - if isSpecial(result): return - var k = getOrdValue(result) - if not (a.kind in {nkEmpty..nkNilLit}) and (k >= 0) and (k < sonsLen(a)): - result = a.sons[int(k)] - if result == nil: result = newNode(nkEmpty) - else: - stackTrace(c, n.info, errIndexOutOfBounds) - result = emptyNode - of mNSetChild: - result = evalAux(c, n.sons[1], {efLValue}) - if isSpecial(result): return - var a = result - result = evalAux(c, n.sons[2], {efLValue}) - if isSpecial(result): return - var b = result - result = evalAux(c, n.sons[3], {efLValue}) - if isSpecial(result): return - var k = getOrdValue(b) - if (k >= 0) and (k < sonsLen(a)) and not (a.kind in {nkEmpty..nkNilLit}): - a.sons[int(k)] = result - else: - stackTrace(c, n.info, errIndexOutOfBounds) - result = emptyNode - of mNAdd: - result = evalAux(c, n.sons[1], {efLValue}) - if isSpecial(result): return - var a = result - result = evalAux(c, n.sons[2], {efLValue}) - if isSpecial(result): return - addSon(a, result) - result = a - of mNAddMultiple: - result = evalAux(c, n.sons[1], {efLValue}) - if isSpecial(result): return - var a = result - result = evalAux(c, n.sons[2], {efLValue}) - if isSpecial(result): return - for i in countup(0, sonsLen(result) - 1): addSon(a, result.sons[i]) - result = a - of mNDel: - result = evalAux(c, n.sons[1], {efLValue}) - if isSpecial(result): return - var a = result - result = evalAux(c, n.sons[2], {efLValue}) - if isSpecial(result): return - var b = result - result = evalAux(c, n.sons[3], {efLValue}) - if isSpecial(result): return - for i in countup(0, int(getOrdValue(result)) - 1): - delSon(a, int(getOrdValue(b))) - result = emptyNode - of mNKind: - result = evalAux(c, n.sons[1], {}) - if isSpecial(result): return - var a = result - result = newNodeIT(nkIntLit, n.info, n.typ) - result.intVal = ord(a.kind) - of mNIntVal: - result = evalAux(c, n.sons[1], {}) - if isSpecial(result): return - var a = result - result = newNodeIT(nkIntLit, n.info, n.typ) - case a.kind - of nkCharLit..nkInt64Lit: result.intVal = a.intVal - else: stackTrace(c, n.info, errFieldXNotFound, "intVal") - of mNFloatVal: - result = evalAux(c, n.sons[1], {}) - if isSpecial(result): return - var a = result - result = newNodeIT(nkFloatLit, n.info, n.typ) - case a.kind - of nkFloatLit..nkFloat64Lit: result.floatVal = a.floatVal - else: stackTrace(c, n.info, errFieldXNotFound, "floatVal") - of mNSymbol: - result = evalAux(c, n.sons[1], {efLValue}) - if isSpecial(result): return - if result.kind != nkSym: stackTrace(c, n.info, errFieldXNotFound, "symbol") - of mNIdent: - result = evalAux(c, n.sons[1], {}) - if isSpecial(result): return - if result.kind != nkIdent: stackTrace(c, n.info, errFieldXNotFound, "ident") - of mNGetType: - var ast = evalAux(c, n.sons[1], {}) - InternalAssert c.getType != nil - result = c.getType(ast) - of mNStrVal: - result = evalAux(c, n.sons[1], {}) - if isSpecial(result): return - var a = result - result = newNodeIT(nkStrLit, n.info, n.typ) - case a.kind - of nkStrLit..nkTripleStrLit: result.strVal = a.strVal - else: stackTrace(c, n.info, errFieldXNotFound, "strVal") - of mNSetIntVal: - result = evalAux(c, n.sons[1], {efLValue}) - if isSpecial(result): return - var a = result - result = evalAux(c, n.sons[2], {}) - if isSpecial(result): return - if a.kind in {nkCharLit..nkInt64Lit} and - result.kind in {nkCharLit..nkInt64Lit}: - a.intVal = result.intVal - else: - stackTrace(c, n.info, errFieldXNotFound, "intVal") - result = emptyNode - of mNSetFloatVal: - result = evalAux(c, n.sons[1], {efLValue}) - if isSpecial(result): return - var a = result - result = evalAux(c, n.sons[2], {}) - if isSpecial(result): return - if a.kind in {nkFloatLit..nkFloat64Lit} and - result.kind in {nkFloatLit..nkFloat64Lit}: - a.floatVal = result.floatVal - else: - stackTrace(c, n.info, errFieldXNotFound, "floatVal") - result = emptyNode - of mNSetSymbol: - result = evalAux(c, n.sons[1], {efLValue}) - if isSpecial(result): return - var a = result - result = evalAux(c, n.sons[2], {efLValue}) - if isSpecial(result): return - if a.kind == nkSym and result.kind == nkSym: - a.sym = result.sym - else: - stackTrace(c, n.info, errFieldXNotFound, "symbol") - result = emptyNode - of mNSetIdent: - result = evalAux(c, n.sons[1], {efLValue}) - if isSpecial(result): return - var a = result - result = evalAux(c, n.sons[2], {efLValue}) - if isSpecial(result): return - if a.kind == nkIdent and result.kind == nkIdent: - a.ident = result.ident - else: - stackTrace(c, n.info, errFieldXNotFound, "ident") - result = emptyNode - of mNSetType: - result = evalAux(c, n.sons[1], {efLValue}) - if isSpecial(result): return - var a = result - result = evalAux(c, n.sons[2], {efLValue}) - if isSpecial(result): return - InternalAssert result.kind == nkSym and result.sym.kind == skType - a.typ = result.sym.typ - result = emptyNode - of mNSetStrVal: - result = evalAux(c, n.sons[1], {efLValue}) - if isSpecial(result): return - var a = result - result = evalAux(c, n.sons[2], {}) - if isSpecial(result): return - - if a.kind in {nkStrLit..nkTripleStrLit} and - result.kind in {nkStrLit..nkTripleStrLit}: - a.strVal = result.strVal - else: stackTrace(c, n.info, errFieldXNotFound, "strVal") - result = emptyNode - of mNNewNimNode: - result = evalAux(c, n.sons[1], {}) - if isSpecial(result): return - var k = getOrdValue(result) - result = evalAux(c, n.sons[2], {efLValue}) - if result.kind == nkExceptBranch: return - var a = result - if k < 0 or k > ord(high(TNodeKind)): - internalError(n.info, "request to create a NimNode with invalid kind") - result = newNodeI(TNodeKind(int(k)), - if a.kind == nkNilLit: n.info else: a.info) - of mNCopyNimNode: - result = evalAux(c, n.sons[1], {efLValue}) - if isSpecial(result): return - result = copyNode(result) - of mNCopyNimTree: - result = evalAux(c, n.sons[1], {efLValue}) - if isSpecial(result): return - result = copyTree(result) - of mNBindSym: - # trivial implementation: - result = n.sons[1] - of mNGenSym: - evalX(n.sons[1], {efLValue}) - let k = getOrdValue(result) - evalX(n.sons[2], {efLValue}) - let b = result - let name = if b.strVal.len == 0: ":tmp" else: b.strVal - if k < 0 or k > ord(high(TSymKind)): - internalError(n.info, "request to create a symbol with invalid kind") - result = newSymNode(newSym(k.TSymKind, name.getIdent, c.module, n.info)) - incl(result.sym.flags, sfGenSym) - of mStrToIdent: - result = evalAux(c, n.sons[1], {}) - if isSpecial(result): return - if not (result.kind in {nkStrLit..nkTripleStrLit}): - stackTrace(c, n.info, errFieldXNotFound, "strVal") - return - var a = result - result = newNodeIT(nkIdent, n.info, n.typ) - result.ident = getIdent(a.strVal) - of mIdentToStr: - result = evalAux(c, n.sons[1], {}) - if isSpecial(result): return - var a = result - result = newNodeIT(nkStrLit, n.info, n.typ) - if a.kind == nkSym: - result.strVal = a.sym.name.s - else: - if a.kind != nkIdent: InternalError(n.info, "no ident node") - result.strVal = a.ident.s - of mEqIdent: - result = evalAux(c, n.sons[1], {}) - if isSpecial(result): return - var a = result - result = evalAux(c, n.sons[2], {}) - if isSpecial(result): return - var b = result - result = newNodeIT(nkIntLit, n.info, n.typ) - if (a.kind == nkIdent) and (b.kind == nkIdent): - if a.ident.id == b.ident.id: result.intVal = 1 - of mEqNimrodNode: - result = evalAux(c, n.sons[1], {efLValue}) - if isSpecial(result): return - var a = result - result = evalAux(c, n.sons[2], {efLValue}) - if isSpecial(result): return - var b = result - result = newNodeIT(nkIntLit, n.info, n.typ) - if (a == b) or - (b.kind in {nkNilLit, nkEmpty}) and (a.kind in {nkNilLit, nkEmpty}): - result.intVal = 1 - of mNLineInfo: - result = evalAux(c, n.sons[1], {}) - if isSpecial(result): return - result = newStrNodeT(result.info.toFileLineCol, n) - of mNHint: - result = evalAux(c, n.sons[1], {}) - if isSpecial(result): return - Message(n.info, hintUser, getStrValue(result)) - result = emptyNode - of mNWarning: - result = evalAux(c, n.sons[1], {}) - if isSpecial(result): return - Message(n.info, warnUser, getStrValue(result)) - result = emptyNode - of mNError: - result = evalAux(c, n.sons[1], {}) - if isSpecial(result): return - stackTrace(c, n.info, errUser, getStrValue(result)) - result = emptyNode - of mConStrStr: - result = evalConStrStr(c, n) - of mRepr: - result = evalRepr(c, n) - of mNewString: - result = evalAux(c, n.sons[1], {}) - if isSpecial(result): return - var a = result - result = newNodeIT(nkStrLit, n.info, n.typ) - result.strVal = newString(int(getOrdValue(a))) - of mNewStringOfCap: - result = evalAux(c, n.sons[1], {}) - if isSpecial(result): return - var a = result - result = newNodeIT(nkStrLit, n.info, n.typ) - result.strVal = newString(0) - of mNCallSite: - if c.callsite != nil: result = c.callsite - else: stackTrace(c, n.info, errFieldXNotFound, "callsite") - else: - result = evalAux(c, n.sons[1], {}) - if isSpecial(result): return - var a = result - var b: PNode = nil - var cc: PNode = nil - if sonsLen(n) > 2: - result = evalAux(c, n.sons[2], {}) - if isSpecial(result): return - b = result - if sonsLen(n) > 3: - result = evalAux(c, n.sons[3], {}) - if isSpecial(result): return - cc = result - if isEmpty(a) or isEmpty(b) or isEmpty(cc): result = emptyNode - else: result = evalOp(m, n, a, b, cc) - -proc evalAux(c: PEvalContext, n: PNode, flags: TEvalFlags): PNode = - result = emptyNode - dec(gNestedEvals) - if gNestedEvals <= 0: stackTrace(c, n.info, errTooManyIterations) - case n.kind - of nkSym: result = evalSym(c, n, flags) - of nkType..nkNilLit, nkTypeOfExpr: - # nkStrLit is VERY common in the traces, so we should avoid - # the 'copyNode' here. - result = n #.copyNode - of nkAsgn, nkFastAsgn: result = evalAsgn(c, n) - of nkCommand..nkHiddenCallConv: - result = evalMagicOrCall(c, n) - of nkDotExpr: result = evalFieldAccess(c, n, flags) - of nkBracketExpr: - result = evalArrayAccess(c, n, flags) - of nkDerefExpr, nkHiddenDeref: result = evalDeref(c, n, flags) - of nkAddr, nkHiddenAddr: result = evalAddr(c, n, flags) - of nkHiddenStdConv, nkHiddenSubConv, nkConv: result = evalConv(c, n) - of nkCurly, nkBracket, nkRange: - # flags need to be passed here for mNAddMultiple :-( - # XXX this is not correct in every case! - var a = copyNode(n) - for i in countup(0, sonsLen(n) - 1): - result = evalAux(c, n.sons[i], flags) - if isSpecial(result): return - addSon(a, result) - result = a - of nkPar, nkClosure: - var a = copyTree(n) - for i in countup(0, sonsLen(n) - 1): - var it = n.sons[i] - if it.kind == nkExprColonExpr: - result = evalAux(c, it.sons[1], flags) - if isSpecial(result): return - a.sons[i].sons[1] = result - else: - result = evalAux(c, it, flags) - if isSpecial(result): return - a.sons[i] = result - result = a - of nkObjConstr: - let t = skipTypes(n.typ, abstractInst) - var a: PNode - if t.kind == tyRef: - result = newNodeIT(nkRefTy, n.info, t) - a = getNullValue(t.sons[0], n.info) - addSon(result, a) - else: - a = getNullValue(t, n.info) - result = a - for i in countup(1, sonsLen(n) - 1): - let it = n.sons[i] - if it.kind == nkExprColonExpr: - let value = evalAux(c, it.sons[1], flags) - if isSpecial(value): return value - a.sons[it.sons[0].sym.position] = value - else: return raiseCannotEval(c, n.info) - of nkWhenStmt, nkIfStmt, nkIfExpr: result = evalIf(c, n) - of nkWhileStmt: result = evalWhile(c, n) - of nkCaseStmt: result = evalCase(c, n) - of nkVarSection, nkLetSection: result = evalVar(c, n) - of nkTryStmt: result = evalTry(c, n) - of nkRaiseStmt: result = evalRaise(c, n) - of nkReturnStmt: result = evalReturn(c, n) - of nkBreakStmt, nkReturnToken: result = n - of nkBlockExpr, nkBlockStmt: result = evalBlock(c, n) - of nkDiscardStmt: result = evalAux(c, n.sons[0], {}) - of nkCheckedFieldExpr: result = evalCheckedFieldAccess(c, n, flags) - of nkObjDownConv: result = evalAux(c, n.sons[0], flags) - of nkObjUpConv: result = evalUpConv(c, n, flags) - of nkChckRangeF, nkChckRange64, nkChckRange: result = evalRangeChck(c, n) - of nkStringToCString: result = evalConvStrToCStr(c, n) - of nkCStringToString: result = evalConvCStrToStr(c, n) - of nkStmtListExpr, nkStmtList: - for i in countup(0, sonsLen(n) - 1): - result = evalAux(c, n.sons[i], flags) - case result.kind - of nkExceptBranch, nkReturnToken, nkBreakStmt: break - else: nil - of nkProcDef, nkMethodDef, nkMacroDef, nkCommentStmt, nkPragma, - nkTypeSection, nkTemplateDef, nkConstSection, nkIteratorDef, - nkConverterDef, nkIncludeStmt, nkImportStmt, nkFromStmt: - nil - of nkMetaNode: - result = copyTree(n.sons[0]) - result.typ = n.typ - of nkPragmaBlock: - result = evalAux(c, n.sons[1], flags) - of nkCast: - result = evalCast(c, n, flags) - of nkIdentDefs, nkYieldStmt, nkAsmStmt, nkForStmt, nkPragmaExpr, - nkLambdaKinds, nkContinueStmt, nkIdent, nkParForStmt, nkBindStmt, - nkClosedSymChoice, nkOpenSymChoice: - result = raiseCannotEval(c, n.info) - of nkRefTy: - result = evalAux(c, n.sons[0], flags) - of nkEmpty: - # nkEmpty occurs once in each trace that I looked at - result = n - else: InternalError(n.info, "evalAux: " & $n.kind) - if result == nil: - InternalError(n.info, "evalAux: returned nil " & $n.kind) - inc(gNestedEvals) - -proc tryEval(c: PEvalContext, n: PNode): PNode = - #internalAssert nfTransf in n.flags - var n = transformExpr(c.module, n) - gWhileCounter = evalMaxIterations - gNestedEvals = evalMaxRecDepth - result = evalAux(c, n, {}) - -proc eval*(c: PEvalContext, n: PNode): PNode = - ## eval never returns nil! This simplifies the code a lot and - ## makes it faster too. - result = tryEval(c, n) - if result.kind == nkExceptBranch: - if sonsLen(result) >= 1: - stackTrace(c, n.info, errUnhandledExceptionX, typeToString(result.typ)) - else: - stackTrace(c, result.info, errCannotInterpretNodeX, renderTree(n)) - -proc evalConstExprAux*(p: PEvalContext, module, prc: PSym, e: PNode): PNode = - var s = newStackFrame() - s.call = e - s.prc = prc - pushStackFrame(p, s) - result = tryEval(p, e) - if result != nil and result.kind == nkExceptBranch: result = nil - popStackFrame(p) - -proc setupMacroParam(x: PNode): PNode = - result = x - if result.kind in {nkHiddenSubConv, nkHiddenStdConv}: result = result.sons[1] - -proc evalMacroCall(c: PEvalContext, n, nOrig: PNode, sym: PSym): PNode = - # XXX GlobalError() is ugly here, but I don't know a better solution for now - inc(evalTemplateCounter) - if evalTemplateCounter > 100: - GlobalError(n.info, errTemplateInstantiationTooNested) - - c.callsite = nOrig - var s = newStackFrame() - s.call = n - s.prc = sym - var L = n.safeLen - if L == 0: L = 1 - setlen(s.slots, L) - # return value: - s.slots[0] = newNodeIT(nkNilLit, n.info, sym.typ.sons[0]) - # setup parameters: - for i in 1 .. < L: s.slots[i] = setupMacroParam(n.sons[i]) - pushStackFrame(c, s) - discard eval(c, optBody(c, sym)) - result = s.slots[0] - popStackFrame(c) - if cyclicTree(result): GlobalError(n.info, errCyclicTree) - dec(evalTemplateCounter) - c.callsite = nil - -proc myOpen(module: PSym): PPassContext = - var c = newEvalContext(module, emRepl) - c.features = {allowCast, allowFFI, allowInfiniteLoops} - pushStackFrame(c, newStackFrame()) - result = c - -var oldErrorCount: int - -proc myProcess(c: PPassContext, n: PNode): PNode = - # don't eval errornous code: - if oldErrorCount == msgs.gErrorCounter: - result = eval(PEvalContext(c), n) - else: - result = n - oldErrorCount = msgs.gErrorCounter - -const evalPass* = makePass(myOpen, nil, myProcess, myProcess) - diff --git a/compiler/semfold.nim b/compiler/semfold.nim index 712d2efd2b..30e02dcc9d 100644 --- a/compiler/semfold.nim +++ b/compiler/semfold.nim @@ -1,7 +1,7 @@ # # # The Nimrod Compiler -# (c) Copyright 2012 Andreas Rumpf +# (c) Copyright 2014 Andreas Rumpf # # See the file "copying.txt", included in this # distribution, for details about the copyright. @@ -537,17 +537,18 @@ proc foldArrayAccess(m: PSym, n: PNode): PNode = var idx = getOrdValue(y) case x.kind of nkPar: - if (idx >= 0) and (idx < sonsLen(x)): + if idx >= 0 and idx < sonsLen(x): result = x.sons[int(idx)] if result.kind == nkExprColonExpr: result = result.sons[1] else: localError(n.info, errIndexOutOfBounds) - of nkBracket: - if (idx >= 0) and (idx < sonsLen(x)): result = x.sons[int(idx)] + of nkBracket: + idx = idx - x.typ.firstOrd + if idx >= 0 and idx < x.len: result = x.sons[int(idx)] else: localError(n.info, errIndexOutOfBounds) - of nkStrLit..nkTripleStrLit: + of nkStrLit..nkTripleStrLit: result = newNodeIT(nkCharLit, x.info, n.typ) - if (idx >= 0) and (idx < len(x.strVal)): + if idx >= 0 and idx < len(x.strVal): result.intVal = ord(x.strVal[int(idx)]) elif idx == len(x.strVal): discard diff --git a/compiler/vmgen.nim b/compiler/vmgen.nim index 305ea7f9e9..b3f05c713c 100644 --- a/compiler/vmgen.nim +++ b/compiler/vmgen.nim @@ -84,21 +84,27 @@ proc gABI(c: PCtx; n: PNode; opc: TOpcode; a, b: TRegister; imm: BiggestInt) = # Takes the `b` register and the immediate `imm`, appies the operation `opc`, # and stores the output value into `a`. # `imm` is signed and must be within [-127, 128] - assert(imm >= -127 and imm <= 128) - let ins = (opc.uint32 or (a.uint32 shl 8'u32) or - (b.uint32 shl 16'u32) or - (imm+byteExcess).uint32 shl 24'u32).TInstr - c.code.add(ins) - c.debug.add(n.info) + if imm >= -127 and imm <= 128: + let ins = (opc.uint32 or (a.uint32 shl 8'u32) or + (b.uint32 shl 16'u32) or + (imm+byteExcess).uint32 shl 24'u32).TInstr + c.code.add(ins) + c.debug.add(n.info) + else: + localError(n.info, errGenerated, + "VM: immediate value does not fit into an int8") proc gABx(c: PCtx; n: PNode; opc: TOpcode; a: TRegister = 0; bx: int) = # Applies `opc` to `bx` and stores it into register `a` # `bx` must be signed and in the range [-32767, 32768] - assert(bx >= -32767 and bx <= 32768) - let ins = (opc.uint32 or a.uint32 shl 8'u32 or - (bx+wordExcess).uint32 shl 16'u32).TInstr - c.code.add(ins) - c.debug.add(n.info) + if bx >= -32767 and bx <= 32768: + let ins = (opc.uint32 or a.uint32 shl 8'u32 or + (bx+wordExcess).uint32 shl 16'u32).TInstr + c.code.add(ins) + c.debug.add(n.info) + else: + localError(n.info, errGenerated, + "VM: immediate value does not fit into an int16") proc xjmp(c: PCtx; n: PNode; opc: TOpcode; a: TRegister = 0): TPosition = #assert opc in {opcJmp, opcFJmp, opcTJmp} @@ -485,11 +491,22 @@ proc genField(n: PNode): TRegister = "too large offset! cannot generate code for: " & s.name.s) result = s.position +proc genIndex(c: PCtx; n: PNode; arr: PType): TRegister = + if arr.skipTypes(abstractInst).kind == tyArray and (let x = firstOrd(arr); + x != 0): + let tmp = c.genx(n) + # freeing the temporary here means we can produce: regA = regA - Imm + c.freeTemp(tmp) + result = c.getTemp(n.typ) + c.gABI(n, opcSubImmInt, result, tmp, x.int) + else: + result = c.genx(n) + proc genAsgnPatch(c: PCtx; le: PNode, value: TRegister) = case le.kind of nkBracketExpr: let dest = c.genx(le.sons[0], {gfAddrOf}) - let idx = c.genx(le.sons[1]) + let idx = c.genIndex(le.sons[1], le.sons[0].typ) c.gABC(le, opcWrArr, dest, idx, value) c.freeTemp(dest) c.freeTemp(idx) @@ -1072,7 +1089,7 @@ proc genAsgn(c: PCtx; le, ri: PNode; requiresCopy: bool) = case le.kind of nkBracketExpr: let dest = c.genx(le.sons[0], {gfAddrOf}) - let idx = c.genx(le.sons[1]) + let idx = c.genIndex(le.sons[1], le.sons[0].typ) let tmp = c.genx(ri) if le.sons[0].typ.skipTypes(abstractVarRange-{tyTypeDesc}).kind in { tyString, tyCString}: @@ -1185,7 +1202,7 @@ proc genRdVar(c: PCtx; n: PNode; dest: var TDest; flags: TGenFlags) = proc genArrAccess2(c: PCtx; n: PNode; dest: var TDest; opc: TOpcode; flags: TGenFlags) = let a = c.genx(n.sons[0], flags) - let b = c.genx(n.sons[1], {}) + let b = c.genIndex(n.sons[1], n.sons[0].typ) if dest < 0: dest = c.getTemp(n.typ) if gfAddrOf notin flags and fitsRegister(n.typ): var cc = c.getTemp(n.typ) diff --git a/examples/cross_calculator/android/scripts/jnibuild.sh b/examples/cross_calculator/android/scripts/jnibuild.sh old mode 100755 new mode 100644 diff --git a/examples/cross_calculator/android/scripts/nimbuild.sh b/examples/cross_calculator/android/scripts/nimbuild.sh old mode 100755 new mode 100644 diff --git a/examples/cross_calculator/android/scripts/tags.sh b/examples/cross_calculator/android/scripts/tags.sh old mode 100755 new mode 100644 diff --git a/examples/cross_calculator/ios/scripts/tags.sh b/examples/cross_calculator/ios/scripts/tags.sh old mode 100755 new mode 100644 diff --git a/examples/cross_calculator/ios/scripts/xcode_prebuild.sh b/examples/cross_calculator/ios/scripts/xcode_prebuild.sh old mode 100755 new mode 100644 diff --git a/lib/pure/unidecode/gen.py b/lib/pure/unidecode/gen.py old mode 100755 new mode 100644 diff --git a/tests/vm/tarrayboundeval.nim b/tests/vm/tarrayboundeval.nim index 9b33a24154..07aac4c4ea 100644 --- a/tests/vm/tarrayboundeval.nim +++ b/tests/vm/tarrayboundeval.nim @@ -1,6 +1,7 @@ discard """ output: '''7 -8 8''' +8 8 +-2''' """ #bug 1063 @@ -21,3 +22,10 @@ type internal: array[int((KeyMax + 31)/32), cuint] echo myconst, " ", int((KeyMax + 31) / 32) + +#bug 1304 or something: + +const constArray: array [-3..2, int] = [-3, -2, -1, 0, 1, 2] + +echo constArray[-2] + diff --git a/tinyc/tests/gcctestsuite.sh b/tinyc/tests/gcctestsuite.sh old mode 100755 new mode 100644 diff --git a/tinyc/texi2pod.pl b/tinyc/texi2pod.pl old mode 100755 new mode 100644 From b89495ef0f6bb60a3296fbaa7c4397dcd269f73d Mon Sep 17 00:00:00 2001 From: Araq Date: Mon, 30 Jun 2014 19:39:57 +0200 Subject: [PATCH 233/270] corrected backends.txt --- compiler/vmgen.nim | 7 +++++++ doc/advopt.txt | 8 ++++---- doc/backends.txt | 18 +++++++----------- todo.txt | 2 ++ 4 files changed, 20 insertions(+), 15 deletions(-) diff --git a/compiler/vmgen.nim b/compiler/vmgen.nim index b3f05c713c..3819bed98a 100644 --- a/compiler/vmgen.nim +++ b/compiler/vmgen.nim @@ -9,6 +9,13 @@ ## This module implements the code generator for the VM. +# Important things to remember: +# - The VM does not distinguish between definitions ('var x = y') and +# assignments ('x = y'). For simple data types that fit into a register +# this doesn't matter. However it matters for strings and other complex +# types that use the 'node' field; the reason is that slots are +# re-used in a register based VM. XXX Come up with an example. + import unsigned, strutils, ast, astalgo, types, msgs, renderer, vmdef, trees, intsets, rodread, magicsys, options, lowerings diff --git a/doc/advopt.txt b/doc/advopt.txt index e3a62c31bc..08465e4572 100644 --- a/doc/advopt.txt +++ b/doc/advopt.txt @@ -1,8 +1,8 @@ Advanced commands: - //compileToC, cc compile project with C code generator, see `Backend language options`_ - //compileToCpp, cpp compile project to C++ code, see `Backend language options`_ - //compileToOC, objc compile project to Objective C code, see `Backend language options`_ - //js compile project to Javascript, see `Backend language options`_ + //compileToC, cc compile project with C code generator + //compileToCpp, cpp compile project to C++ code + //compileToOC, objc compile project to Objective C code + //js compile project to Javascript //rst2html convert a reStructuredText file to HTML //rst2tex convert a reStructuredText file to TeX //jsondoc extract the documentation to a json file diff --git a/doc/backends.txt b/doc/backends.txt index 0ab682c290..26576e7331 100644 --- a/doc/backends.txt +++ b/doc/backends.txt @@ -6,12 +6,7 @@ :Version: |nimrodversion| .. contents:: - - "If we all reacted the same way, we'd be predictable, and there's - always more than one way to view a situation. What's true for the - group is also true for the individual. It's simple: overspecialize, - and you breed in weakness. It's slow death." -- Major Motoko - Kusanagi + "Heresy grows from idleness." -- Unknown. Introduction @@ -160,7 +155,7 @@ Create a ``calculator.nim`` file with the following content: .. code-block:: nimrod {.compile: "logic.c".} - proc addTwoIntegers(a, b: int): int {.importc.} + proc addTwoIntegers(a, b: cint): cint {.importc.} when isMainModule: echo addTwoIntegers(3, 7) @@ -407,7 +402,8 @@ can then attach a GC to this thread via initGC() At the moment this support is still experimental so you need to expose these -functions yourself or submit patches to request a public API. If the Nimrod -code you are calling is short lived, another possible solution is to disable -the garbage collector and enable it after the call from your background -thread. +functions yourself or submit patches to request a public API. + +It is **not** safe to disable the garbage collector and enable it after the +call from your background thread even if the code you are calling is short +lived. diff --git a/todo.txt b/todo.txt index df18ff7e9e..f027be8d53 100644 --- a/todo.txt +++ b/todo.txt @@ -1,6 +1,8 @@ version 0.9.6 ============= +- some table related tests are wrong (memory usage checks) + Concurrency ----------- From 0339b9d386bd0efa3c31bd05c25cd9ed0f1c7dd5 Mon Sep 17 00:00:00 2001 From: Araq Date: Mon, 30 Jun 2014 22:24:08 +0200 Subject: [PATCH 234/270] fixes #1319 --- compiler/transf.nim | 5 ++++- tests/macros/tbindsym.nim | 25 +++++++++++++++++++++++++ 2 files changed, 29 insertions(+), 1 deletion(-) create mode 100644 tests/macros/tbindsym.nim diff --git a/compiler/transf.nim b/compiler/transf.nim index fb5e321b67..dece1ac18f 100644 --- a/compiler/transf.nim +++ b/compiler/transf.nim @@ -546,7 +546,7 @@ proc flattenTree(root: PNode): PNode = flattenTreeAux(result, root, op) else: result = root - + proc transformCall(c: PTransf, n: PNode): PTransNode = var n = flattenTree(n) var op = getMergeOp(n) @@ -565,6 +565,9 @@ proc transformCall(c: PTransf, n: PNode): PTransNode = inc(j) add(result, a.PTransNode) if len(result) == 2: result = result[1] + elif getMagic(n) == mNBindSym: + # for bindSym(myconst) we MUST NOT perform constant folding: + result = n.PTransNode else: let s = transformSons(c, n).PNode # bugfix: check after 'transformSons' if it's still a method call: diff --git a/tests/macros/tbindsym.nim b/tests/macros/tbindsym.nim new file mode 100644 index 0000000000..e1e3b51126 --- /dev/null +++ b/tests/macros/tbindsym.nim @@ -0,0 +1,25 @@ +discard """ + output: '''TFoo +TBar''' +""" + +# bug #1319 + +import macros + +type + TTextKind = enum + TFoo, TBar + +macro test: stmt = + var x = @[TFoo, TBar] + result = newStmtList() + for i in x: + result.add newCall(newIdentNode("echo"), + case i + of TFoo: + bindSym("TFoo") + of TBar: + bindSym("TBar")) + +test() From c41e099118f005325082c74317e2c73679299b9f Mon Sep 17 00:00:00 2001 From: Araq Date: Mon, 30 Jun 2014 22:36:52 +0200 Subject: [PATCH 235/270] fixes #1144 --- compiler/renderer.nim | 25 +++++++++++++------------ 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/compiler/renderer.nim b/compiler/renderer.nim index 6b62c48c51..0b1312ccc8 100644 --- a/compiler/renderer.nim +++ b/compiler/renderer.nim @@ -146,28 +146,29 @@ proc makeNimString(s: string): string = for i in countup(0, len(s)-1): add(result, toNimChar(s[i])) add(result, '\"') -proc putComment(g: var TSrcGen, s: string) = +proc putComment(g: var TSrcGen, s: string) = + if s.isNil: return var i = 0 var comIndent = 1 var isCode = (len(s) >= 2) and (s[1] != ' ') var ind = g.lineLen var com = "" - while true: + while true: case s[i] - of '\0': - break - of '\x0D': + of '\0': + break + of '\x0D': put(g, tkComment, com) com = "" inc(i) if s[i] == '\x0A': inc(i) optNL(g, ind) - of '\x0A': + of '\x0A': put(g, tkComment, com) com = "" inc(i) optNL(g, ind) - of '#': + of '#': add(com, s[i]) inc(i) comIndent = 0 @@ -175,10 +176,10 @@ proc putComment(g: var TSrcGen, s: string) = add(com, s[i]) inc(i) inc(comIndent) - of ' ', '\x09': + of ' ', '\x09': add(com, s[i]) inc(i) - else: + else: # we may break the comment into a multi-line comment if the line # gets too long: # compute length of the following word: @@ -195,10 +196,10 @@ proc putComment(g: var TSrcGen, s: string) = optNL(g) proc maxLineLength(s: string): int = - result = 0 + if s.isNil: return 0 var i = 0 var lineLen = 0 - while true: + while true: case s[i] of '\0': break @@ -459,7 +460,7 @@ proc lsub(n: PNode): int = of nkBreakStmt: result = lsub(n.sons[0]) + len("break_") of nkContinueStmt: result = lsub(n.sons[0]) + len("continue_") of nkPragma: result = lcomma(n) + 4 - of nkCommentStmt: result = len(n.comment) + of nkCommentStmt: result = if n.comment.isNil: 0 else: len(n.comment) of nkOfBranch: result = lcomma(n, 0, - 2) + lsub(lastSon(n)) + len("of_:_") of nkImportAs: result = lsub(n.sons[0]) + len("_as_") + lsub(n.sons[1]) of nkElifBranch: result = lsons(n) + len("elif_:_") From 3c818c89be8cc7890b9d9b7d165a2cf472a74d24 Mon Sep 17 00:00:00 2001 From: Araq Date: Mon, 30 Jun 2014 23:36:46 +0200 Subject: [PATCH 236/270] fixes #1285 --- compiler/patterns.nim | 2 +- compiler/pretty.nim | 62 +++++++------------------------------------ compiler/sem.nim | 2 +- compiler/semcall.nim | 4 +-- compiler/semexprs.nim | 26 +++++++++--------- compiler/semstmts.nim | 4 +-- compiler/semtempl.nim | 2 +- compiler/semtypes.nim | 8 +++--- compiler/sigmatch.nim | 6 ++--- compiler/suggest.nim | 38 +++++++++++++------------- 10 files changed, 55 insertions(+), 99 deletions(-) diff --git a/compiler/patterns.nim b/compiler/patterns.nim index d262790abb..5e21289b5a 100644 --- a/compiler/patterns.nim +++ b/compiler/patterns.nim @@ -287,7 +287,7 @@ proc applyRule*(c: PContext, s: PSym, n: PNode): PNode = # constraint not fullfilled: if not ok: return nil - markUsed(n, s) + markUsed(n.info, s) if ctx.subMatch: assert m.len == 3 m.sons[1] = result diff --git a/compiler/pretty.nim b/compiler/pretty.nim index 3a5bfe1970..17311f9e62 100644 --- a/compiler/pretty.nim +++ b/compiler/pretty.nim @@ -1,7 +1,7 @@ # # # The Nimrod Compiler -# (c) Copyright 2013 Andreas Rumpf +# (c) Copyright 2014 Andreas Rumpf # # See the file "copying.txt", included in this # distribution, for details about the copyright. @@ -149,8 +149,8 @@ proc checkDef(c: PGen; n: PNode) = if n.kind != nkSym: return checkDef(n, n.sym) -proc checkUse*(n: PNode, s: PSym) = - if n.info.fileIndex < 0: return +proc checkUse*(info: TLineInfo; s: PSym) = + if info.fileIndex < 0: return # we simply convert it to what it looks like in the definition # for consistency @@ -159,10 +159,10 @@ proc checkUse*(n: PNode, s: PSym) = if s.kind in {skType, skGenericParam} and sfAnon in s.flags: return let newName = s.name.s - loadFile(n.info) + loadFile(info) - let line = gSourceFiles[n.info.fileIndex].lines[n.info.line-1] - var first = min(n.info.col.int, line.len) + let line = gSourceFiles[info.fileIndex].lines[info.line-1] + var first = min(info.col.int, line.len) if first < 0: return #inc first, skipIgnoreCase(line, "proc ", first) while first > 0 and line[first-1] in Letters: dec first @@ -179,8 +179,8 @@ proc checkUse*(n: PNode, s: PSym) = if x.match(peg"\s* {\ident} \s* '=' \s* y$1 ('#' .*)?"): x = "" - system.shallowCopy(gSourceFiles[n.info.fileIndex].lines[n.info.line-1], x) - gSourceFiles[n.info.fileIndex].dirty = true + system.shallowCopy(gSourceFiles[info.fileIndex].lines[info.line-1], x) + gSourceFiles[info.fileIndex].dirty = true when false: var cannotRename = initIntSet() @@ -220,53 +220,9 @@ when false: result.add s[i] inc i - proc checkUse(c: PGen; n: PNode) = - if n.info.fileIndex < 0: return - let s = n.sym - # operators stay as they are: - if s.kind in {skResult, skTemp} or s.name.s[0] notin Letters: return - if s.kind in {skType, skGenericParam} and sfAnon in s.flags: return - - if s.id in cannotRename: return - - let newName = if rules.hasKey(s.name.s): rules[s.name.s] - else: beautifyName(s.name.s, n.sym.kind) - - loadFile(n.info) - - let line = gSourceFiles[n.info.fileIndex].lines[n.info.line-1] - var first = min(n.info.col.int, line.len) - if first < 0: return - #inc first, skipIgnoreCase(line, "proc ", first) - while first > 0 and line[first-1] in Letters: dec first - if first < 0: return - if line[first] == '`': inc first - - if {sfImportc, sfExportc} * s.flags != {}: - # careful, we must ensure the resulting name still matches the external - # name: - if newName != s.name.s and newName != s.loc.r.ropeToStr and - lfFullExternalName notin s.loc.flags: - #Message(n.info, errGenerated, - # "cannot rename $# to $# due to external name" % [s.name.s, newName]) - cannotRename.incl(s.id) - return - let last = first+identLen(line, first)-1 - if differ(line, first, last, newName): - # last-first+1 != newName.len or - var x = line.subStr(0, first-1) & newName & line.substr(last+1) - when removeTP: - # the WinAPI module is full of 'TX = X' which after the substitution - # becomes 'X = X'. We remove those lines: - if x.match(peg"\s* {\ident} \s* '=' \s* y$1 ('#' .*)?"): - x = "" - - system.shallowCopy(gSourceFiles[n.info.fileIndex].lines[n.info.line-1], x) - gSourceFiles[n.info.fileIndex].dirty = true - proc check(c: PGen, n: PNode) = case n.kind - of nkSym: checkUse(n, n.sym) + of nkSym: checkUse(n.info, n.sym) of nkBlockStmt, nkBlockExpr, nkBlockType: checkDef(c, n[0]) check(c, n.sons[1]) diff --git a/compiler/sem.nim b/compiler/sem.nim index e4ef6473f4..f12b638acd 100644 --- a/compiler/sem.nim +++ b/compiler/sem.nim @@ -305,7 +305,7 @@ proc semAfterMacroCall(c: PContext, n: PNode, s: PSym, proc semMacroExpr(c: PContext, n, nOrig: PNode, sym: PSym, flags: TExprFlags = {}): PNode = - markUsed(n, sym) + markUsed(n.info, sym) if sym == c.p.owner: globalError(n.info, errRecursiveDependencyX, sym.name.s) diff --git a/compiler/semcall.nim b/compiler/semcall.nim index 88a7f976cc..65a2d7ab89 100644 --- a/compiler/semcall.nim +++ b/compiler/semcall.nim @@ -251,7 +251,7 @@ proc inferWithMetatype(c: PContext, formal: PType, proc semResolvedCall(c: PContext, n: PNode, x: TCandidate): PNode = assert x.state == csMatch var finalCallee = x.calleeSym - markUsed(n.sons[0], finalCallee) + markUsed(n.sons[0].info, finalCallee) if finalCallee.ast == nil: internalError(n.info, "calleeSym.ast is nil") # XXX: remove this check! if finalCallee.ast.sons[genericParamsPos].kind != nkEmpty: @@ -283,7 +283,7 @@ proc explicitGenericSym(c: PContext, n: PNode, s: PSym): PNode = var m: TCandidate initCandidate(c, m, s, n) var newInst = generateInstance(c, s, m.bindings, n.info) - markUsed(n, s) + markUsed(n.info, s) result = newSymNode(newInst, n.info) proc explicitGenericInstantiation(c: PContext, n: PNode, s: PSym): PNode = diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim index 078e95fbee..e6542a3fe8 100644 --- a/compiler/semexprs.nim +++ b/compiler/semexprs.nim @@ -12,7 +12,7 @@ proc semTemplateExpr(c: PContext, n: PNode, s: PSym, flags: TExprFlags = {}): PNode = - markUsed(n, s) + markUsed(n.info, s) pushInfoContext(n.info) result = evalTemplate(n, s, getCurrOwner()) if efNoSemCheck notin flags: result = semAfterMacroCall(c, result, s, flags) @@ -78,7 +78,7 @@ proc inlineConst(n: PNode, s: PSym): PNode {.inline.} = proc semSym(c: PContext, n: PNode, s: PSym, flags: TExprFlags): PNode = case s.kind of skConst: - markUsed(n, s) + markUsed(n.info, s) case skipTypes(s.typ, abstractInst-{tyTypeDesc}).kind of tyNil, tyChar, tyInt..tyInt64, tyFloat..tyFloat128, tyTuple, tySet, tyUInt..tyUInt64: @@ -101,7 +101,7 @@ proc semSym(c: PContext, n: PNode, s: PSym, flags: TExprFlags): PNode = of skMacro: result = semMacroExpr(c, n, n, s, flags) of skTemplate: result = semTemplateExpr(c, n, s, flags) of skVar, skLet, skResult, skParam, skForVar: - markUsed(n, s) + markUsed(n.info, s) # if a proc accesses a global variable, it is not side effect free: if sfGlobal in s.flags: incl(c.p.owner.flags, sfSideEffect) @@ -123,13 +123,13 @@ proc semSym(c: PContext, n: PNode, s: PSym, flags: TExprFlags): PNode = n.typ = s.typ return n of skType: - markUsed(n, s) + markUsed(n.info, s) if s.typ.kind == tyStatic and s.typ.n != nil: return s.typ.n result = newSymNode(s, n.info) result.typ = makeTypeDesc(c, s.typ) else: - markUsed(n, s) + markUsed(n.info, s) result = newSymNode(s, n.info) type @@ -253,7 +253,7 @@ proc semConv(c: PContext, n: PNode): PNode = let it = op.sons[i] let status = checkConvertible(c, result.typ, it.typ) if status in {convOK, convNotNeedeed}: - markUsed(n, it.sym) + markUsed(n.info, it.sym) markIndirect(c, it.sym) return it localError(n.info, errUseQualifier, op.sons[0].sym.name.s) @@ -971,7 +971,7 @@ proc builtinFieldAccess(c: PContext, n: PNode, flags: TExprFlags): PNode = var s = qualifiedLookUp(c, n, {checkAmbiguity, checkUndeclared}) if s != nil: - markUsed(n.sons[1], s) + markUsed(n.sons[1].info, s) return semSym(c, n, s, flags) n.sons[0] = semExprWithType(c, n.sons[0], flags+{efDetermineType}) @@ -994,7 +994,7 @@ proc builtinFieldAccess(c: PContext, n: PNode, flags: TExprFlags): PNode = result = newSymNode(f) result.info = n.info result.typ = ty - markUsed(n, f) + markUsed(n.info, f) return of tyTypeParamsHolders: return readTypeParameter(c, ty, i, n.info) @@ -1026,7 +1026,7 @@ proc builtinFieldAccess(c: PContext, n: PNode, flags: TExprFlags): PNode = if f != nil: if fieldVisible(c, f): # is the access to a public field or in the same module or in a friend? - markUsed(n.sons[1], f) + markUsed(n.sons[1].info, f) n.sons[0] = makeDeref(n.sons[0]) n.sons[1] = newSymNode(f) # we now have the correct field n.typ = f.typ @@ -1039,7 +1039,7 @@ proc builtinFieldAccess(c: PContext, n: PNode, flags: TExprFlags): PNode = elif ty.kind == tyTuple and ty.n != nil: f = getSymFromList(ty.n, i) if f != nil: - markUsed(n.sons[1], f) + markUsed(n.sons[1].info, f) n.sons[0] = makeDeref(n.sons[0]) n.sons[1] = newSymNode(f) n.typ = f.typ @@ -1450,7 +1450,7 @@ proc semExpandToAst(c: PContext, n: PNode): PNode = if expandedSym.kind == skError: return n macroCall.sons[0] = newSymNode(expandedSym, macroCall.info) - markUsed(n, expandedSym) + markUsed(n.info, expandedSym) for i in countup(1, macroCall.len-1): macroCall.sons[i] = semExprWithType(c, macroCall[i], {}) @@ -1881,7 +1881,7 @@ proc semBlock(c: PContext, n: PNode): PNode = if sfGenSym notin labl.flags: addDecl(c, labl) n.sons[0] = newSymNode(labl, n.sons[0].info) - suggestSym(n.sons[0], labl) + suggestSym(n.sons[0].info, labl) n.sons[1] = semExpr(c, n.sons[1]) n.typ = n.sons[1].typ if isEmptyType(n.typ): n.kind = nkBlockStmt @@ -2001,7 +2001,7 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode = var s = qualifiedLookUp(c, n.sons[0], mode) if s != nil: if gCmd == cmdPretty and n.sons[0].kind == nkDotExpr: - pretty.checkUse(n.sons[0].sons[1], s) + pretty.checkUse(n.sons[0].sons[1].info, s) case s.kind of skMacro: if sfImmediate notin s.flags: diff --git a/compiler/semstmts.nim b/compiler/semstmts.nim index 11399b38bc..3cb9691eb9 100644 --- a/compiler/semstmts.nim +++ b/compiler/semstmts.nim @@ -33,7 +33,7 @@ proc semBreakOrContinue(c: PContext, n: PNode): PNode = x.info = n.info incl(s.flags, sfUsed) n.sons[0] = x - suggestSym(x, s) + suggestSym(x.info, s) else: localError(n.info, errInvalidControlFlowX, s.name.s) elif (c.p.nestedLoopCounter <= 0) and (c.p.nestedBlockCounter <= 0): @@ -319,7 +319,7 @@ proc semIdentDef(c: PContext, n: PNode, kind: TSymKind): PSym = incl(result.flags, sfGlobal) else: result = semIdentWithPragma(c, kind, n, {}) - suggestSym(n, result) + suggestSym(n.info, result) proc checkNilable(v: PSym) = if sfGlobal in v.flags and {tfNotNil, tfNeedsInit} * v.typ.flags != {}: diff --git a/compiler/semtempl.nim b/compiler/semtempl.nim index 84281dad5c..ee8b1ccb8f 100644 --- a/compiler/semtempl.nim +++ b/compiler/semtempl.nim @@ -59,7 +59,7 @@ proc symChoice(c: PContext, n: PNode, s: PSym, r: TSymChoiceRule): PNode = # (s.kind notin routineKinds or s.magic != mNone): # for instance 'nextTry' is both in tables.nim and astalgo.nim ... result = newSymNode(s, n.info) - markUsed(n, s) + markUsed(n.info, s) else: # semantic checking requires a type; ``fitNode`` deals with it # appropriately diff --git a/compiler/semtypes.nim b/compiler/semtypes.nim index 9d38c46199..0ecdeb5298 100644 --- a/compiler/semtypes.nim +++ b/compiler/semtypes.nim @@ -281,7 +281,7 @@ proc semTypeIdent(c: PContext, n: PNode): PSym = else: result = qualifiedLookUp(c, n, {checkAmbiguity, checkUndeclared}) if result != nil: - markUsed(n, result) + markUsed(n.info, result) if result.kind == skParam and result.typ.kind == tyTypeDesc: # This is a typedesc param. is it already bound? # it's not bound when it's used multiple times in the @@ -562,7 +562,7 @@ proc semRecordNodeAux(c: PContext, n: PNode, check: var TIntSet, pos: var int, let rec = rectype.sym for i in countup(0, sonsLen(n)-3): var f = semIdentWithPragma(c, skField, n.sons[i], {sfExported}) - suggestSym(n.sons[i], f) + suggestSym(n.sons[i].info, f) f.typ = typ f.position = pos if (rec != nil) and ({sfImportc, sfExportc} * rec.flags != {}) and @@ -827,7 +827,7 @@ proc liftParamType(c: PContext, procKind: TSymKind, genericParams: PNode, result = addImplicitGeneric(newTypeS(tyAnything, c)) of tyGenericParam: - markUsed(genericParams, paramType.sym) + markUsed(info, paramType.sym) if tfWildcard in paramType.flags: paramType.flags.excl tfWildcard paramType.sym.kind = skType @@ -1181,7 +1181,7 @@ proc semTypeNode(c: PContext, n: PNode, prev: PType): PType = else: assignType(prev, t) result = prev - markUsed(n, n.sym) + markUsed(n.info, n.sym) else: if n.sym.kind != skError: localError(n.info, errTypeExpected) result = newOrPrevType(tyError, prev, c) diff --git a/compiler/sigmatch.nim b/compiler/sigmatch.nim index 551e21d78b..e5bf08097d 100644 --- a/compiler/sigmatch.nim +++ b/compiler/sigmatch.nim @@ -62,7 +62,7 @@ type const isNilConversion = isConvertible # maybe 'isIntConv' fits better? -proc markUsed*(n: PNode, s: PSym) +proc markUsed*(info: TLineInfo, s: PSym) proc initCandidateAux(ctx: PContext, c: var TCandidate, callee: PType) {.inline.} = @@ -1058,7 +1058,7 @@ proc userConvMatch(c: PContext, m: var TCandidate, f, a: PType, dest = generateTypeInstance(c, m.bindings, arg, dest) let fdest = typeRel(m, f, dest) if fdest in {isEqual, isGeneric}: - markUsed(arg, c.converters[i]) + markUsed(arg.info, c.converters[i]) var s = newSymNode(c.converters[i]) s.typ = c.converters[i].typ s.info = arg.info @@ -1271,7 +1271,7 @@ proc paramTypesMatch*(m: var TCandidate, f, a: PType, result = nil else: # only one valid interpretation found: - markUsed(arg, arg.sons[best].sym) + markUsed(arg.info, arg.sons[best].sym) result = paramTypesMatchAux(m, f, arg.sons[best].typ, arg.sons[best], argOrig) diff --git a/compiler/suggest.nim b/compiler/suggest.nim index db95c480f2..c2bdfc5c33 100644 --- a/compiler/suggest.nim +++ b/compiler/suggest.nim @@ -246,18 +246,18 @@ var usageSym*: PSym lastLineInfo: TLineInfo -proc findUsages(node: PNode, s: PSym) = - if usageSym == nil and isTracked(node.info, s.name.s.len): +proc findUsages(info: TLineInfo; s: PSym) = + if usageSym == nil and isTracked(info, s.name.s.len): usageSym = s suggestWriteln(symToStr(s, isLocal=false, sectionUsage)) elif s == usageSym: - if lastLineInfo != node.info: - suggestWriteln(symToStr(s, isLocal=false, sectionUsage, node.info)) - lastLineInfo = node.info + if lastLineInfo != info: + suggestWriteln(symToStr(s, isLocal=false, sectionUsage, info)) + lastLineInfo = info -proc findDefinition(node: PNode, s: PSym) = - if node.isNil or s.isNil: return - if isTracked(node.info, s.name.s.len): +proc findDefinition(info: TLineInfo; s: PSym) = + if s.isNil: return + if isTracked(info, s.name.s.len): suggestWriteln(symToStr(s, isLocal=false, sectionDef)) suggestQuit() @@ -316,26 +316,26 @@ proc defFromSourceMap*(i: TLineInfo) = defFromLine(gSourceMaps[i.fileIndex].lines[i.line].entries, i.col) -proc suggestSym*(n: PNode, s: PSym) {.inline.} = +proc suggestSym*(info: TLineInfo; s: PSym) {.inline.} = ## misnamed: should be 'symDeclared' if optUsages in gGlobalOptions: - findUsages(n, s) + findUsages(info, s) if optDef in gGlobalOptions: - findDefinition(n, s) - if isServing and not n.isNil: - addToSourceMap(s, n.info) + findDefinition(info, s) + if isServing: + addToSourceMap(s, info) -proc markUsed(n: PNode, s: PSym) = +proc markUsed(info: TLineInfo; s: PSym) = incl(s.flags, sfUsed) if {sfDeprecated, sfError} * s.flags != {}: - if sfDeprecated in s.flags: message(n.info, warnDeprecated, s.name.s) - if sfError in s.flags: localError(n.info, errWrongSymbolX, s.name.s) - suggestSym(n, s) - if gCmd == cmdPretty: checkUse(n, s) + if sfDeprecated in s.flags: message(info, warnDeprecated, s.name.s) + if sfError in s.flags: localError(info, errWrongSymbolX, s.name.s) + suggestSym(info, s) + if gCmd == cmdPretty: checkUse(info, s) proc useSym*(sym: PSym): PNode = result = newSymNode(sym) - markUsed(result, sym) + markUsed(result.info, sym) proc suggestExpr*(c: PContext, node: PNode) = var cp = msgs.inCheckpoint(node.info) From e5f2a8ade4da6df5c79078954fa5a60ad4e8c16f Mon Sep 17 00:00:00 2001 From: Araq Date: Tue, 1 Jul 2014 00:13:07 +0200 Subject: [PATCH 237/270] fixes #1268 --- doc/manual.txt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/doc/manual.txt b/doc/manual.txt index 8a7cfba54b..b39efb7407 100644 --- a/doc/manual.txt +++ b/doc/manual.txt @@ -1089,7 +1089,8 @@ types with no overhead and few abstraction possibilities. The constructor ``()`` can be used to construct tuples. The order of the fields in the constructor must match the order of the tuple's definition. Different tuple-types are *equivalent* if they specify the same fields of the same type in the same -order. +order. The *names* of the fields also have to be identical but this might +change in a future version of the language. The assignment operator for tuples copies each component. The default assignment operator for objects copies each component. Overloading From 4860b46c7fd2850f8ec30c313434a2aed50c5561 Mon Sep 17 00:00:00 2001 From: Araq Date: Tue, 1 Jul 2014 00:24:40 +0200 Subject: [PATCH 238/270] fixes #1264 --- compiler/lexer.nim | 4 ++++ doc/manual.txt | 13 +++++++------ 2 files changed, 11 insertions(+), 6 deletions(-) diff --git a/compiler/lexer.nim b/compiler/lexer.nim index 2bfd8d1eb2..0b4e4348d2 100644 --- a/compiler/lexer.nim +++ b/compiler/lexer.nim @@ -537,6 +537,10 @@ proc getString(L: var TLexer, tok: var TToken, rawMode: bool) = tok.tokType = tkTripleStrLit # long string literal: inc(pos, 2) # skip "" # skip leading newline: + if buf[pos] in {' ', '\t'}: + var newpos = pos+1 + while buf[newpos] in {' ', '\t'}: inc newpos + if buf[newpos] in {CR, LF}: pos = newpos pos = handleCRLF(L, pos) buf = L.buf while true: diff --git a/doc/manual.txt b/doc/manual.txt index b39efb7407..e34e1b1648 100644 --- a/doc/manual.txt +++ b/doc/manual.txt @@ -263,15 +263,16 @@ String literals can also be delimited by three double quotes ``"""`` ... ``"""``. Literals in this form may run for several lines, may contain ``"`` and do not interpret any escape sequences. -For convenience, when the opening ``"""`` is immediately followed by a newline, -the newline is not included in the string. The ending of the string literal is -defined by the pattern ``"""[^"]``, so this: - +For convenience, when the opening ``"""`` is followed by a newline (there may +be whitespace between the opening ``"""`` and the newline), +the newline (and the preceding whitespace) is not included in the string. The +ending of the string literal is defined by the pattern ``"""[^"]``, so this: + .. code-block:: nimrod """"long string within quotes"""" - + Produces:: - + "long string within quotes" From 7c5f87e413a2a11cad591f7dc6fb015869f0125c Mon Sep 17 00:00:00 2001 From: Araq Date: Tue, 1 Jul 2014 00:59:40 +0200 Subject: [PATCH 239/270] fixes #1265 --- compiler/lexer.nim | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/compiler/lexer.nim b/compiler/lexer.nim index 0b4e4348d2..e343dfef62 100644 --- a/compiler/lexer.nim +++ b/compiler/lexer.nim @@ -424,8 +424,14 @@ proc getNumber(L: var TLexer): TToken = if (result.iNumber < low(int32)) or (result.iNumber > high(int32)): if result.tokType == tkIntLit: result.tokType = tkInt64Lit - elif result.tokType in {tkInt8Lit, tkInt16Lit}: - lexMessage(L, errInvalidNumber, result.literal) + elif result.tokType in {tkInt8Lit, tkInt16Lit, tkInt32Lit}: + lexMessage(L, errNumberOutOfRange, result.literal) + elif result.tokType == tkInt8Lit and + (result.iNumber < int8.low or result.iNumber > int8.high): + lexMessage(L, errNumberOutOfRange, result.literal) + elif result.tokType == tkInt16Lit and + (result.iNumber < int16.low or result.iNumber > int16.high): + lexMessage(L, errNumberOutOfRange, result.literal) except EInvalidValue: lexMessage(L, errInvalidNumber, result.literal) except EOverflow, EOutOfRange: From 505b77cb6634aa0b03e6623aec2cab1d22d9105e Mon Sep 17 00:00:00 2001 From: Araq Date: Tue, 1 Jul 2014 01:21:26 +0200 Subject: [PATCH 240/270] fixes #1135 --- doc/trmacros.txt | 275 ----------------------------------------------- web/nimrod.ini | 2 +- 2 files changed, 1 insertion(+), 276 deletions(-) delete mode 100644 doc/trmacros.txt diff --git a/doc/trmacros.txt b/doc/trmacros.txt deleted file mode 100644 index d5ad74e6ee..0000000000 --- a/doc/trmacros.txt +++ /dev/null @@ -1,275 +0,0 @@ -========================================================= - Term rewriting macros for Nimrod -========================================================= - -:Author: Andreas Rumpf - -Term rewriting macros are macros or templates that have not only a *name* but -also a *pattern* that is searched for after the semantic checking phase of -the compiler: This means they provide an easy way to enhance the compilation -pipeline with user defined optimizations: - -.. code-block:: nimrod - template optMul{`*`(a, 2)}(a: int): int = a+a - - let x = 3 - echo x * 2 - -The compiler now rewrites ``x * 2`` as ``x + x``. The code inside the -curlies is the pattern to match against. The operators ``*``, ``**``, -``|``, ``~`` have a special meaning in patterns if they are written in infix -notation, so to match verbatim against ``*`` the ordinary function call syntax -needs to be used. - - -Unfortunately optimizations are hard to get right and even the tiny example -is **wrong**: - -.. code-block:: nimrod - template optMul{`*`(a, 2)}(a: int): int = a+a - - proc f(): int = - echo "side effect!" - result = 55 - - echo f() * 2 - -We cannot duplicate 'a' if it denotes an expression that has a side effect! -Fortunately Nimrod supports side effect analysis: - -.. code-block:: nimrod - template optMul{`*`(a, 2)}(a: int{noSideEffect}): int = a+a - - proc f(): int = - echo "side effect!" - result = 55 - - echo f() * 2 # not optimized ;-) - -So what about ``2 * a``? We should tell the compiler ``*`` is commutative. We -cannot really do that however as the following code only swaps arguments -blindly: - -.. code-block:: nimrod - template mulIsCommutative{`*`(a, b)}(a, b: int): int = b*a - -What optimizers really need to do is a *canonicalization*: - -.. code-block:: nimrod - template canonMul{`*`(a, b)}(a: int{lit}, b: int): int = b*a - -The ``int{lit}`` parameter pattern matches against an expression of -type ``int``, but only if it's a literal. - - - -Parameter constraints -===================== - -The parameter constraint expression can use the operators ``|`` (or), -``&`` (and) and ``~`` (not) and the following predicates: - -=================== ===================================================== -Predicate Meaning -=================== ===================================================== -``atom`` The matching node has no children. -``lit`` The matching node is a literal like "abc", 12. -``sym`` The matching node must be a symbol (a bound - identifier). -``ident`` The matching node must be an identifier (an unbound - identifier). -``call`` The matching AST must be a call/apply expression. -``lvalue`` The matching AST must be an lvalue. -``sideeffect`` The matching AST must have a side effect. -``nosideeffect`` The matching AST must have no side effect. -``param`` A symbol which is a parameter. -``genericparam`` A symbol which is a generic parameter. -``module`` A symbol which is a module. -``type`` A symbol which is a type. -``var`` A symbol which is a variable. -``let`` A symbol which is a ``let`` variable. -``const`` A symbol which is a constant. -``result`` The special ``result`` variable. -``proc`` A symbol which is a proc. -``method`` A symbol which is a method. -``iterator`` A symbol which is an iterator. -``converter`` A symbol which is a converter. -``macro`` A symbol which is a macro. -``template`` A symbol which is a template. -``field`` A symbol which is a field in a tuple or an object. -``enumfield`` A symbol which is a field in an enumeration. -``forvar`` A for loop variable. -``label`` A label (used in ``block`` statements). -``nk*`` The matching AST must have the specified kind. - (Example: ``nkIfStmt`` denotes an ``if`` statement.) -``alias`` States that the marked parameter needs to alias - with *some* other parameter. -``noalias`` States that *every* other parameter must not alias - with the marked parameter. -=================== ===================================================== - -The ``alias`` and ``noalias`` predicates refer not only to the matching AST, -but also to every other bound parameter; syntactially they need to occur after -the ordinary AST predicates: - -.. code-block:: nimrod - template ex{a = b + c}(a: int{noalias}, b, c: int) = - # this transformation is only valid if 'b' and 'c' do not alias 'a': - a = b - inc a, b - - -Pattern operators -================= - -The operators ``*``, ``**``, ``|``, ``~`` have a special meaning in patterns -if they are written in infix notation. - - -The ``|`` operator ------------------- - -The ``|`` operator if used as infix operator creates an ordered choice: - -.. code-block:: nimrod - template t{0|1}(): expr = 3 - let a = 1 - # outputs 3: - echo a - -The matching is performed after the compiler performed some optimizations like -constant folding, so the following does not work: - -.. code-block:: nimrod - template t{0|1}(): expr = 3 - # outputs 1: - echo 1 - -The reason is that the compiler already transformed the 1 into "1" for -the ``echo`` statement. However, a term rewriting macro should not change the -semantics anyway. In fact they can be deactived with the ``--patterns:off`` -command line option or temporarily with the ``patterns`` pragma. - - -The ``{}`` operator -------------------- - -A pattern expression can be bound to a pattern parameter via the ``expr{param}`` -notation: - -.. code-block:: nimrod - template t{(0|1|2){x}}(x: expr): expr = x+1 - let a = 1 - # outputs 2: - echo a - - -The ``~`` operator ------------------- - -The ``~`` operator is the **not** operator in patterns: - -.. code-block:: nimrod - template t{x = (~x){y} and (~x){z}}(x, y, z: bool): stmt = - x = y - if x: x = z - - var - a = false - b = true - c = false - a = b and c - echo a - - -The ``*`` operator ------------------- - -The ``*`` operator can *flatten* a nested binary expression like ``a & b & c`` -to ``&(a, b, c)``: - -.. code-block:: nimrod - var - calls = 0 - - proc `&&`(s: varargs[string]): string = - result = s[0] - for i in 1..len(s)-1: result.add s[i] - inc calls - - template optConc{ `&&` * a }(a: string): expr = &&a - - let space = " " - echo "my" && (space & "awe" && "some " ) && "concat" - - # check that it's been optimized properly: - doAssert calls == 1 - - -The second operator of `*` must be a parameter; it is used to gather all the -arguments. The expression ``"my" && (space & "awe" && "some " ) && "concat"`` -is passed to ``optConc`` in ``a`` as a special list (of kind ``nkArgList``) -which is flattened into a call expression; thus the invocation of ``optConc`` -produces: - -.. code-block:: nimrod - `&&`("my", space & "awe", "some ", "concat") - - -The ``**`` operator -------------------- - -The ``**`` is much like the ``*`` operator, except that it gathers not only -all the arguments, but also the matched operators in reverse polish notation: - -.. code-block:: nimrod - import macros - - type - TMatrix = object - dummy: int - - proc `*`(a, b: TMatrix): TMatrix = nil - proc `+`(a, b: TMatrix): TMatrix = nil - proc `-`(a, b: TMatrix): TMatrix = nil - proc `$`(a: TMatrix): string = result = $a.dummy - proc mat21(): TMatrix = - result.dummy = 21 - - macro optM{ (`+`|`-`|`*`) ** a }(a: TMatrix): expr = - echo treeRepr(a) - result = newCall(bindSym"mat21") - - var x, y, z: TMatrix - - echo x + y * z - x - -This passes the expression ``x + y * z - x`` to the ``optM`` macro as -an ``nnkArgList`` node containing:: - - Arglist - Sym "x" - Sym "y" - Sym "z" - Sym "*" - Sym "+" - Sym "x" - Sym "-" - -(Which is the reverse polish notation of ``x + y * z - x``.) - - -Parameters -========== - -Parameters in a pattern are type checked in the matching process. If a -parameter is of the type ``varargs`` it is treated specially and it can match -0 or more arguments in the AST to be matched against: - -.. code-block:: nimrod - template optWrite{ - write(f, x) - ((write|writeln){w})(f, y) - }(x, y: varargs[expr], f: TFile, w: expr) = - w(f, x, y) - diff --git a/web/nimrod.ini b/web/nimrod.ini index fc652c158e..af9ac0cd9f 100644 --- a/web/nimrod.ini +++ b/web/nimrod.ini @@ -36,7 +36,7 @@ UNIX. We don't believe this to be a coincidence. - Jeremy S. Anderson.""" [Documentation] -doc: "endb;intern;apis;lib;manual;tut1;tut2;nimrodc;overview;filters;trmacros" +doc: "endb;intern;apis;lib;manual;tut1;tut2;nimrodc;overview;filters" doc: "tools;c2nim;niminst;nimgrep;gc;estp;idetools;docgen;koch;backends.txt" pdf: "manual;lib;tut1;tut2;nimrodc;c2nim;niminst;gc" srcdoc2: "system.nim;impure/graphics;wrappers/sdl" From 405cd7d1fbc92189e70763b8f22e888559214451 Mon Sep 17 00:00:00 2001 From: Araq Date: Tue, 1 Jul 2014 02:09:07 +0200 Subject: [PATCH 241/270] fixes #904 --- compiler/semexprs.nim | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim index e6542a3fe8..7f97124e12 100644 --- a/compiler/semexprs.nim +++ b/compiler/semexprs.nim @@ -792,6 +792,10 @@ proc semIndirectOp(c: PContext, n: PNode, flags: TExprFlags): PNode = n.flags.incl nfExprCall result = semOverloadedCallAnalyseEffects(c, n, nOrig, flags) if result == nil: return errorNode(c, n) + elif result.kind notin nkCallKinds: + # the semExpr() in overloadedCallOpr can even break this condition! + # See bug #904 of how to trigger it: + return result #result = afterCallActions(c, result, nOrig, flags) fixAbstractType(c, result) analyseIfAddressTakenInCall(c, result) From 212293197fe9e3584f8c36142935ce6d6f5b30c3 Mon Sep 17 00:00:00 2001 From: Araq Date: Tue, 1 Jul 2014 21:47:53 +0200 Subject: [PATCH 242/270] fixes #1161 --- compiler/parser.nim | 21 ++++++++++++--------- doc/grammar.txt | 5 ++++- tests/exprs/tstmtexprs.nim | 32 +++++++++++++++++++++++++++++++- 3 files changed, 47 insertions(+), 11 deletions(-) diff --git a/compiler/parser.nim b/compiler/parser.nim index 18de1570a2..6ff0c2dfcf 100644 --- a/compiler/parser.nim +++ b/compiler/parser.nim @@ -63,7 +63,7 @@ proc optInd*(p: var TParser, n: PNode) proc indAndComment*(p: var TParser, n: PNode) proc setBaseFlags*(n: PNode, base: TNumericalBase) proc parseSymbol*(p: var TParser, allowNil = false): PNode -proc parseTry(p: var TParser): PNode +proc parseTry(p: var TParser; isExpr: bool): PNode proc parseCase(p: var TParser): PNode # implementation @@ -845,7 +845,7 @@ proc parseIdentColonEquals(p: var TParser, flags: TDeclaredIdentFlags): PNode = addSon(result, parseTypeDesc(p)) else: addSon(result, ast.emptyNode) - if (p.tok.tokType != tkEquals) and not (withBothOptional in flags): + if p.tok.tokType != tkEquals and withBothOptional notin flags: parMessage(p, errColonOrEqualsExpected, p.tok) if p.tok.tokType == tkEquals: getTok(p) @@ -1004,13 +1004,13 @@ proc parseExpr(p: var TParser): PNode = #| expr = (ifExpr #| | whenExpr #| | caseExpr - #| | tryStmt) + #| | tryExpr) #| / simpleExpr case p.tok.tokType: of tkIf: result = parseIfExpr(p, nkIfExpr) of tkWhen: result = parseIfExpr(p, nkWhenExpr) of tkCase: result = parseCase(p) - of tkTry: result = parseTry(p) + of tkTry: result = parseTry(p, isExpr=true) else: result = simpleExpr(p) proc parseEnum(p: var TParser): PNode @@ -1363,22 +1363,25 @@ proc parseCase(p: var TParser): PNode = if wasIndented: p.currInd = oldInd -proc parseTry(p: var TParser): PNode = +proc parseTry(p: var TParser; isExpr: bool): PNode = #| tryStmt = 'try' colcom stmt &(IND{=}? 'except'|'finally') #| (IND{=}? 'except' exprList colcom stmt)* #| (IND{=}? 'finally' colcom stmt)? + #| tryExpr = 'try' colcom stmt &(optInd 'except'|'finally') + #| (optInd 'except' exprList colcom stmt)* + #| (optInd 'finally' colcom stmt)? result = newNodeP(nkTryStmt, p) getTok(p) eat(p, tkColon) skipComment(p, result) addSon(result, parseStmt(p)) var b: PNode = nil - while sameOrNoInd(p): + while sameOrNoInd(p) or isExpr: case p.tok.tokType - of tkExcept: + of tkExcept: b = newNodeP(nkExceptBranch, p) exprList(p, tkColon, b) - of tkFinally: + of tkFinally: b = newNodeP(nkFinally, p) getTokNoInd(p) eat(p, tkColon) @@ -1877,7 +1880,7 @@ proc complexOrSimpleStmt(p: var TParser): PNode = of tkIf: result = parseIfOrWhen(p, nkIfStmt) of tkWhile: result = parseWhile(p) of tkCase: result = parseCase(p) - of tkTry: result = parseTry(p) + of tkTry: result = parseTry(p, isExpr=false) of tkFinally: result = parseExceptBlock(p, nkFinally) of tkExcept: result = parseExceptBlock(p, nkExceptBranch) of tkFor: result = parseFor(p) diff --git a/doc/grammar.txt b/doc/grammar.txt index 47ae095f69..a544286785 100644 --- a/doc/grammar.txt +++ b/doc/grammar.txt @@ -86,7 +86,7 @@ distinct = 'distinct' optInd typeDesc expr = (ifExpr | whenExpr | caseExpr - | tryStmt) + | tryExpr) / simpleExpr typeKeyw = 'var' | 'ref' | 'ptr' | 'shared' | 'type' | 'tuple' | 'proc' | 'iterator' | 'distinct' | 'object' | 'enum' @@ -135,6 +135,9 @@ caseStmt = 'case' expr ':'? COMMENT? tryStmt = 'try' colcom stmt &(IND{=}? 'except'|'finally') (IND{=}? 'except' exprList colcom stmt)* (IND{=}? 'finally' colcom stmt)? +tryExpr = 'try' colcom stmt &(optInd 'except'|'finally') + (optInd 'except' exprList colcom stmt)* + (optInd 'finally' colcom stmt)? exceptBlock = 'except' colcom stmt forStmt = 'for' (identWithPragma ^+ comma) 'in' expr colcom stmt blockStmt = 'block' symbol? colcom stmt diff --git a/tests/exprs/tstmtexprs.nim b/tests/exprs/tstmtexprs.nim index ed00662875..d6b827b6d2 100644 --- a/tests/exprs/tstmtexprs.nim +++ b/tests/exprs/tstmtexprs.nim @@ -4,7 +4,8 @@ discard """ 1244 6 abcdefghijklmnopqrstuvwxyz -145 23''' +145 23 +3''' """ import strutils @@ -92,3 +93,32 @@ proc parseResponse(): PJsonNode = #bug #992 var se = @[1,2] let b = (se[1] = 1; 1) + + +# bug #1161 + +type + PFooBase = ref object of PObject + field: int + + PFoo[T] = ref object of PFooBase + field2: T + +var testIf = + if true: + 2 + else: + 3 + +var testCase = + case 8 + of 8: 9 + else: 10 + +var testTry = + try: + PFoo[string](field: 3, field2: "asfasf") + except: + PFooBase(field: 5) + +echo(testTry.field) From daf9dd701ef63a67a84966cc52e60f2bf00304c0 Mon Sep 17 00:00:00 2001 From: Araq Date: Tue, 1 Jul 2014 22:10:43 +0200 Subject: [PATCH 243/270] fixes #1166 --- compiler/lexer.nim | 21 +++++++++------------ 1 file changed, 9 insertions(+), 12 deletions(-) diff --git a/compiler/lexer.nim b/compiler/lexer.nim index e343dfef62..0e4dfc2ac9 100644 --- a/compiler/lexer.nim +++ b/compiler/lexer.nim @@ -347,7 +347,7 @@ proc getNumber(L: var TLexer): TToken = result.base = base2 while true: case L.buf[pos] - of 'A'..'Z', 'a'..'z', '2'..'9', '.': + of '2'..'9', '.': lexMessage(L, errInvalidNumber, result.literal) inc(pos) of '_': @@ -363,7 +363,7 @@ proc getNumber(L: var TLexer): TToken = result.base = base8 while true: case L.buf[pos] - of 'A'..'Z', 'a'..'z', '8'..'9', '.': + of '8'..'9', '.': lexMessage(L, errInvalidNumber, result.literal) inc(pos) of '_': @@ -377,25 +377,22 @@ proc getNumber(L: var TLexer): TToken = else: break of 'O': lexMessage(L, errInvalidNumber, result.literal) - of 'x', 'X': + of 'x', 'X': result.base = base16 - while true: + while true: case L.buf[pos] - of 'G'..'Z', 'g'..'z': - lexMessage(L, errInvalidNumber, result.literal) - inc(pos) - of '_': - if L.buf[pos+1] notin {'0'..'9', 'a'..'f', 'A'..'F'}: + of '_': + if L.buf[pos+1] notin {'0'..'9', 'a'..'f', 'A'..'F'}: lexMessage(L, errInvalidToken, "_") break inc(pos) - of '0'..'9': + of '0'..'9': xi = `shl`(xi, 4) or (ord(L.buf[pos]) - ord('0')) inc(pos) - of 'a'..'f': + of 'a'..'f': xi = `shl`(xi, 4) or (ord(L.buf[pos]) - ord('a') + 10) inc(pos) - of 'A'..'F': + of 'A'..'F': xi = `shl`(xi, 4) or (ord(L.buf[pos]) - ord('A') + 10) inc(pos) else: break From b3f390bd481a4e4a3f56d8a689450392fa408779 Mon Sep 17 00:00:00 2001 From: Araq Date: Tue, 1 Jul 2014 22:36:01 +0200 Subject: [PATCH 244/270] fixes #751 --- compiler/ccgexprs.nim | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/compiler/ccgexprs.nim b/compiler/ccgexprs.nim index e6b22819fd..1874e33b84 100644 --- a/compiler/ccgexprs.nim +++ b/compiler/ccgexprs.nim @@ -1398,10 +1398,10 @@ proc genSetOp(p: BProc, e: PNode, d: var TLoc, op: TMagic) = of mIncl: var ts = "NI" & $(size * 8) binaryStmtInExcl(p, e, d, - "$1 |=((" & ts & ")(1)<<(($2)%(sizeof(" & ts & ")*8)));$n") + "$1 |= ((" & ts & ")1)<<(($2)%(sizeof(" & ts & ")*8));$n") of mExcl: var ts = "NI" & $(size * 8) - binaryStmtInExcl(p, e, d, "$1 &= ~((" & ts & ")(1) << (($2) % (sizeof(" & + binaryStmtInExcl(p, e, d, "$1 &= ~(((" & ts & ")1) << (($2) % (sizeof(" & ts & ")*8)));$n") of mCard: if size <= 4: unaryExprChar(p, e, d, "#countBits32($1)") From b56f43c394a03bac441058be9d0754324352593d Mon Sep 17 00:00:00 2001 From: Araq Date: Tue, 1 Jul 2014 22:58:44 +0200 Subject: [PATCH 245/270] fixes #898 --- compiler/ast.nim | 2 +- compiler/semtypes.nim | 8 +++++++- tests/metatype/tautoproc.nim | 16 ++++++++++++++++ 3 files changed, 24 insertions(+), 2 deletions(-) create mode 100644 tests/metatype/tautoproc.nim diff --git a/compiler/ast.nim b/compiler/ast.nim index 7ff22e1848..516954b887 100644 --- a/compiler/ast.nim +++ b/compiler/ast.nim @@ -1048,7 +1048,7 @@ proc discardSons(father: PNode) = father.sons = nil when defined(useNodeIds): - const nodeIdToDebug* = 482228 # 612794 + const nodeIdToDebug* = 310841 # 612794 #612840 # 612905 # 614635 # 614637 # 614641 # 423408 #429107 # 430443 # 441048 # 441090 # 441153 diff --git a/compiler/semtypes.nim b/compiler/semtypes.nim index 0ecdeb5298..b075e603d0 100644 --- a/compiler/semtypes.nim +++ b/compiler/semtypes.nim @@ -864,7 +864,13 @@ proc semProcTypeNode(c: PContext, n, genericParams: PNode, var counter = 0 for i in countup(1, n.len - 1): var a = n.sons[i] - if a.kind != nkIdentDefs: illFormedAst(a) + if a.kind != nkIdentDefs: + # for some generic instantiations the passed ':env' parameter + # for closures has already been produced (see bug #898). We simply + # skip this parameter here. It'll then be re-generated in another LL + # pass over this instantiation: + if a.kind == nkSym and sfFromGeneric in a.sym.flags: continue + illFormedAst(a) checkMinSonsLen(a, 3) var typ: PType = nil diff --git a/tests/metatype/tautoproc.nim b/tests/metatype/tautoproc.nim new file mode 100644 index 0000000000..9e8ff0bcb0 --- /dev/null +++ b/tests/metatype/tautoproc.nim @@ -0,0 +1,16 @@ +# bug #898 + +proc measureTime(e: auto) = + discard + +proc generate(a: int): void = + discard + +proc runExample = + var builder: int = 0 + + measureTime: + builder.generate() + +measureTime: + discard From b119dc2f3b35f6fb84bc9661ea0fbbd006fba433 Mon Sep 17 00:00:00 2001 From: Araq Date: Tue, 1 Jul 2014 23:17:20 +0200 Subject: [PATCH 246/270] fixed build configuration detection --- compiler/commands.nim | 43 ++++++++++++++++++++++++------------------- 1 file changed, 24 insertions(+), 19 deletions(-) diff --git a/compiler/commands.nim b/compiler/commands.nim index e920cc8970..38c8dd2949 100644 --- a/compiler/commands.nim +++ b/compiler/commands.nim @@ -9,10 +9,34 @@ # This module handles the parsing of command line arguments. + +# We do this here before the 'import' statement so 'defined' does not get +# confused with 'TGCMode.gcGenerational' etc. +template bootSwitch(name, expr, userString: expr): expr = + # Helper to build boot constants, for debugging you can 'echo' the else part. + const name = if expr: " " & userString else: "" + +bootSwitch(usedRelease, defined(release), "-d:release") +bootSwitch(usedGnuReadline, defined(useGnuReadline), "-d:useGnuReadline") +bootSwitch(usedNoCaas, defined(noCaas), "-d:noCaas") +bootSwitch(usedBoehm, defined(boehmgc), "--gc:boehm") +bootSwitch(usedMarkAndSweep, defined(gcmarkandsweep), "--gc:markAndSweep") +bootSwitch(usedGenerational, defined(gcgenerational), "--gc:generational") +bootSwitch(usedNoGC, defined(nogc), "--gc:none") + import os, msgs, options, nversion, condsyms, strutils, extccomp, platform, lists, wordrecg, parseutils, babelcmd, idents +# but some have deps to imported modules. Yay. +bootSwitch(usedTinyC, hasTinyCBackend, "-d:tinyc") +bootSwitch(usedAvoidTimeMachine, noTimeMachine, "-d:avoidTimeMachine") +bootSwitch(usedNativeStacktrace, + defined(nativeStackTrace) and nativeStackTraceSupported, + "-d:nativeStackTrace") +bootSwitch(usedFFI, hasFFI, "-d:useFFI") + + proc writeCommandLineUsage*() type @@ -50,25 +74,6 @@ proc writeAdvancedUsage(pass: TCmdLinePass) = CPU[platform.hostCPU].name]) & AdvancedUsage) quit(0) -template bootSwitch(name, expr, userString: expr): expr = - # Helper to build boot constants, for debugging you can 'echo' the else part. - const name = if expr: " " & userString else: "" - -bootSwitch(usedAvoidTimeMachine, noTimeMachine, "-d:avoidTimeMachine") -bootSwitch(usedRelease, defined(release), "-d:release") -bootSwitch(usedTinyC, hasTinyCBackend, "-d:tinyc") -bootSwitch(usedGnuReadline, defined(useGnuReadline), "-d:useGnuReadline") -bootSwitch(usedNativeStacktrace, - defined(nativeStackTrace) and nativeStackTraceSupported, - "-d:nativeStackTrace") -bootSwitch(usedNoCaas, defined(noCaas), "-d:noCaas") -bootSwitch(usedFFI, hasFFI, "-d:useFFI") -bootSwitch(usedBoehm, defined(boehmgc), "--gc:boehm") -bootSwitch(usedMarkAndSweep, defined(gcmarkandsweep), "--gc:markAndSweep") -bootSwitch(usedGenerational, defined(gcgenerational), "--gc:generational") -bootSwitch(usedNoGC, defined(nogc), "--gc:none") - - proc writeVersionInfo(pass: TCmdLinePass) = if pass == passCmd1: msgWriteln(`%`(HelpMessage, [VersionAsString, From d02cc41596602ac8039517add1c453a45a7c3558 Mon Sep 17 00:00:00 2001 From: Araq Date: Tue, 1 Jul 2014 23:41:25 +0200 Subject: [PATCH 247/270] fixes #1275 --- compiler/ccgexprs.nim | 7 ++++--- tests/objects/tobjconstr2.nim | 12 ++++++++++++ 2 files changed, 16 insertions(+), 3 deletions(-) diff --git a/compiler/ccgexprs.nim b/compiler/ccgexprs.nim index 1874e33b84..4698082f1a 100644 --- a/compiler/ccgexprs.nim +++ b/compiler/ccgexprs.nim @@ -714,11 +714,12 @@ proc genFieldCheck(p: BProc, e: PNode, obj: PRope, field: PSym) = assert(it.sons[0].kind == nkSym) let op = it.sons[0].sym if op.magic == mNot: it = it.sons[1] - assert(it.sons[2].kind == nkSym) + let disc = it.sons[2].skipConv + assert(disc.kind == nkSym) initLoc(test, locNone, it.typ, OnStack) initLocExpr(p, it.sons[1], u) - initLoc(v, locExpr, it.sons[2].typ, OnUnknown) - v.r = ropef("$1.$2", [obj, it.sons[2].sym.loc.r]) + initLoc(v, locExpr, disc.typ, OnUnknown) + v.r = ropef("$1.$2", [obj, disc.sym.loc.r]) genInExprAux(p, it, u, v, test) let id = nodeTableTestOrSet(p.module.dataCache, newStrNode(nkStrLit, field.name.s), gBackendId) diff --git a/tests/objects/tobjconstr2.nim b/tests/objects/tobjconstr2.nim index cb47e146dc..8ef7004e40 100644 --- a/tests/objects/tobjconstr2.nim +++ b/tests/objects/tobjconstr2.nim @@ -20,3 +20,15 @@ type var a = Bar(y: 100, x: 200) # works var b = Bar(x: 100, y: 200) # used to fail + +# bug 1275 + +type + Graphic = object of TObject + case kind: range[0..1] + of 0: + radius: float + of 1: + size: tuple[w, h: float] + +var d = Graphic(kind: 1, size: (12.9, 6.9)) From 887a1ebe688a01259263ad6e11c9061cfc940456 Mon Sep 17 00:00:00 2001 From: Araq Date: Wed, 2 Jul 2014 00:22:23 +0200 Subject: [PATCH 248/270] tut1.txt no BOM; streamlined example a bit --- doc/tut1.txt | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/doc/tut1.txt b/doc/tut1.txt index 9874f267b9..a2aa835ee5 100644 --- a/doc/tut1.txt +++ b/doc/tut1.txt @@ -1,4 +1,4 @@ -======================== +======================== Nimrod Tutorial (Part I) ======================== @@ -1521,17 +1521,13 @@ techniques. Example: .. code-block:: nimrod + proc echoItem(x: int) = echo(x) - type - TCallback = proc (x: int) - - proc echoItem(x: Int) = echo(x) - - proc forEach(callback: TCallback) = + proc forEach(action: proc (x: int)) = const data = [2, 3, 5, 7, 11] for d in items(data): - callback(d) + action(d) forEach(echoItem) From 21be7bf85a4d8cdff1acfcc5c8b9bf9c0727b290 Mon Sep 17 00:00:00 2001 From: Araq Date: Wed, 2 Jul 2014 21:15:29 +0200 Subject: [PATCH 249/270] fixes #1310 --- compiler/vm.nim | 7 +++++++ compiler/vmgen.nim | 9 ++++++++- tests/vm/teval1.nim | 5 +++++ 3 files changed, 20 insertions(+), 1 deletion(-) diff --git a/compiler/vm.nim b/compiler/vm.nim index 0c2c23987b..7f5e0d1c53 100644 --- a/compiler/vm.nim +++ b/compiler/vm.nim @@ -138,6 +138,9 @@ proc createStrKeepNode(x: var TFullReg) = template createStr(x) = x.node = newNode(nkStrLit) +template createSet(x) = + x.node = newNode(nkCurly) + proc moveConst(x: var TFullReg, y: TFullReg) = if x.kind != y.kind: myreset(x) @@ -722,18 +725,22 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg = regs[ra].intVal = ord(containsSets(a, b) and not equalSets(a, b)) of opcMulSet: decodeBC(rkNode) + createSet(regs[ra]) move(regs[ra].node.sons, nimsets.intersectSets(regs[rb].node, regs[rc].node).sons) of opcPlusSet: decodeBC(rkNode) + createSet(regs[ra]) move(regs[ra].node.sons, nimsets.unionSets(regs[rb].node, regs[rc].node).sons) of opcMinusSet: decodeBC(rkNode) + createSet(regs[ra]) move(regs[ra].node.sons, nimsets.diffSets(regs[rb].node, regs[rc].node).sons) of opcSymdiffSet: decodeBC(rkNode) + createSet(regs[ra]) move(regs[ra].node.sons, nimsets.symdiffSets(regs[rb].node, regs[rc].node).sons) of opcConcatStr: diff --git a/compiler/vmgen.nim b/compiler/vmgen.nim index 3819bed98a..28e0a8fd6e 100644 --- a/compiler/vmgen.nim +++ b/compiler/vmgen.nim @@ -14,7 +14,12 @@ # assignments ('x = y'). For simple data types that fit into a register # this doesn't matter. However it matters for strings and other complex # types that use the 'node' field; the reason is that slots are -# re-used in a register based VM. XXX Come up with an example. +# re-used in a register based VM. Example: +# +# .. code-block:: nimrod +# let s = a & b # no matter what, create fresh node +# s = a & b # no matter what, keep the node +# import unsigned, strutils, ast, astalgo, types, msgs, renderer, vmdef, @@ -1335,6 +1340,8 @@ proc genVarSection(c: PCtx; n: PNode) = if a.sons[2].kind == nkEmpty: c.gABx(a, ldNullOpcode(s.typ), s.position, c.genType(s.typ)) else: + if not fitsRegister(s.typ): + c.gABx(a, ldNullOpcode(s.typ), s.position, c.genType(s.typ)) gen(c, a.sons[2], s.position.TRegister) else: # assign to a.sons[0]; happens for closures diff --git a/tests/vm/teval1.nim b/tests/vm/teval1.nim index a02f26592f..cdb4ad8e2b 100644 --- a/tests/vm/teval1.nim +++ b/tests/vm/teval1.nim @@ -16,4 +16,9 @@ const echo "##", x, "##" +# bug #1310 +static: + var i, j: set[int8] = {} + var k = i + j + From f9d7e8db2a8b89575df51768ce9e5aa66c47cc9f Mon Sep 17 00:00:00 2001 From: Araq Date: Sun, 6 Jul 2014 22:56:34 +0200 Subject: [PATCH 250/270] implements #1332 --- compiler/semtypes.nim | 4 ++-- doc/manual.txt | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/compiler/semtypes.nim b/compiler/semtypes.nim index b075e603d0..53fe4e266a 100644 --- a/compiler/semtypes.nim +++ b/compiler/semtypes.nim @@ -443,14 +443,14 @@ proc semCaseBranch(c: PContext, t, branch: PNode, branchIndex: int, elif isRange(b): branch.sons[i] = semCaseBranchRange(c, t, b, covered) else: + # constant sets and arrays are allowed: var r = semConstExpr(c, b) # for ``{}`` we want to trigger the type mismatch in ``fitNode``: - if r.kind != nkCurly or len(r) == 0: + if r.kind notin {nkCurly, nkBracket} or len(r) == 0: checkMinSonsLen(t, 1) branch.sons[i] = skipConv(fitNode(c, t.sons[0].typ, r)) inc(covered) else: - # constant sets have special rules # first element is special and will overwrite: branch.sons[i]: branch.sons[i] = semCaseBranchSetElem(c, t, r[0], covered) # other elements have to be added to ``branch`` diff --git a/doc/manual.txt b/doc/manual.txt index e34e1b1648..cb1bb83aae 100644 --- a/doc/manual.txt +++ b/doc/manual.txt @@ -2228,12 +2228,12 @@ type. If the expression is not of an ordinal type, and no ``else`` part is given, control passes after the ``case`` statement. -To suppress the static error in the ordinal case an ``else`` part with a ``nil`` -statement can be used. +To suppress the static error in the ordinal case an ``else`` part with an +empty ``discard`` statement can be used. As a special semantic extension, an expression in an ``of`` branch of a case -statement may evaluate to a set constructor; the set is then expanded into -a list of its elements: +statement may evaluate to a set or array constructor; the set or array is then +expanded into a list of its elements: .. code-block:: nimrod const From f16449ec22751ca0e864d70424760e540a80c804 Mon Sep 17 00:00:00 2001 From: Araq Date: Tue, 8 Jul 2014 02:02:58 +0200 Subject: [PATCH 251/270] fixes #1103; fixes #1297 --- compiler/vm.nim | 17 +++--- compiler/vmgen.nim | 82 +++++++++++++++++++++------- tests/casestmt/tcase_arrayconstr.nim | 19 +++++++ tests/macros/tbug1149.nim | 46 +++++++++++++++- todo.txt | 4 +- 5 files changed, 137 insertions(+), 31 deletions(-) create mode 100644 tests/casestmt/tcase_arrayconstr.nim diff --git a/compiler/vm.nim b/compiler/vm.nim index 7f5e0d1c53..f879e36bca 100644 --- a/compiler/vm.nim +++ b/compiler/vm.nim @@ -438,7 +438,6 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg = if regs[rc].intVal > high(int): stackTrace(c, tos, pc, errIndexOutOfBounds) let idx = regs[rc].intVal.int - # XXX what if the array is not 0-based? -> codegen should insert a sub let src = regs[rb].node if src.kind notin {nkEmpty..nkNilLit} and idx <% src.len: regs[ra].node = src.sons[idx] @@ -504,13 +503,13 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg = else: stackTrace(c, tos, pc, errNilAccess) of opcWrDeref: - # a[] = b + # a[] = c; b unused let ra = instr.regA - let rb = instr.regB + let rc = instr.regC case regs[ra].kind - of rkNodeAddr: putIntoNode(regs[ra].nodeAddr[], regs[rb]) - of rkRegisterAddr: regs[ra].regAddr[] = regs[rb] - of rkNode: putIntoNode(regs[ra].node, regs[rb]) + of rkNodeAddr: putIntoNode(regs[ra].nodeAddr[], regs[rc]) + of rkRegisterAddr: regs[ra].regAddr[] = regs[rc] + of rkNode: putIntoNode(regs[ra].node, regs[rc]) else: stackTrace(c, tos, pc, errNilAccess) of opcAddInt: decodeBC(rkInt) @@ -751,11 +750,11 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg = regs[ra].node.strVal.add getstr(regs[i]) of opcAddStrCh: decodeB(rkNode) - createStrKeepNode regs[ra] + #createStrKeepNode regs[ra] regs[ra].node.strVal.add(regs[rb].intVal.chr) of opcAddStrStr: decodeB(rkNode) - createStrKeepNode regs[ra] + #createStrKeepNode regs[ra] regs[ra].node.strVal.add(regs[rb].node.strVal) of opcAddSeqElem: decodeB(rkNode) @@ -992,7 +991,7 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg = return TFullReg(kind: rkNone) of opcSetLenStr: decodeB(rkNode) - createStrKeepNode regs[ra] + #createStrKeepNode regs[ra] regs[ra].node.strVal.setLen(regs[rb].intVal.int) of opcOf: decodeBC(rkInt) diff --git a/compiler/vmgen.nim b/compiler/vmgen.nim index 28e0a8fd6e..b04e605490 100644 --- a/compiler/vmgen.nim +++ b/compiler/vmgen.nim @@ -19,7 +19,13 @@ # .. code-block:: nimrod # let s = a & b # no matter what, create fresh node # s = a & b # no matter what, keep the node -# +# +# Also *stores* into non-temporary memory need to perform deep copies: +# a.b = x.y +# We used to generate opcAsgn for the *load* of 'x.y' but this is clearly +# wrong! We need to produce opcAsgn (the copy) for the *store*. This also +# solves the opcLdConst vs opcAsgnConst issue. Of course whether we need +# this copy depends on the involved types. import unsigned, strutils, ast, astalgo, types, msgs, renderer, vmdef, @@ -531,12 +537,12 @@ proc genAsgnPatch(c: PCtx; le: PNode, value: TRegister) = c.freeTemp(dest) of nkDerefExpr, nkHiddenDeref: let dest = c.genx(le.sons[0], {gfAddrOf}) - c.gABC(le, opcWrDeref, dest, value) + c.gABC(le, opcWrDeref, dest, 0, value) c.freeTemp(dest) of nkSym: if le.sym.isGlobal: let dest = c.genx(le, {gfAddrOf}) - c.gABC(le, opcWrDeref, dest, value) + c.gABC(le, opcWrDeref, dest, 0, value) c.freeTemp(dest) else: discard @@ -1097,6 +1103,25 @@ proc checkCanEval(c: PCtx; n: PNode) = not s.isOwnedBy(c.prc.sym) and s.owner != c.module: cannotEval(n) +proc isTemp(c: PCtx; dest: TDest): bool = + result = dest >= 0 and c.prc.slots[dest].kind >= slotTempUnknown + +template needsAdditionalCopy(n): expr = + not c.isTemp(dest) and not fitsRegister(n.typ) + +proc preventFalseAlias(c: PCtx; n: PNode; opc: TOpcode; + dest, idx, value: TRegister) = + # opcLdObj et al really means "load address". We sometimes have to create a + # copy in order to not introduce false aliasing: + # mylocal = a.b # needs a copy of the data! + if needsAdditionalCopy(n): + var cc = c.getTemp(n.typ) + c.gABC(n, whichAsgnOpc(n), cc, value) + c.gABC(n, opc, dest, idx, cc) + c.freeTemp(cc) + else: + c.gABC(n, opc, dest, idx, value) + proc genAsgn(c: PCtx; le, ri: PNode; requiresCopy: bool) = case le.kind of nkBracketExpr: @@ -1105,9 +1130,9 @@ proc genAsgn(c: PCtx; le, ri: PNode; requiresCopy: bool) = let tmp = c.genx(ri) if le.sons[0].typ.skipTypes(abstractVarRange-{tyTypeDesc}).kind in { tyString, tyCString}: - c.gABC(le, opcWrStrIdx, dest, idx, tmp) + c.preventFalseAlias(le, opcWrStrIdx, dest, idx, tmp) else: - c.gABC(le, opcWrArr, dest, idx, tmp) + c.preventFalseAlias(le, opcWrArr, dest, idx, tmp) c.freeTemp(tmp) of nkDotExpr, nkCheckedFieldExpr: # XXX field checks here @@ -1115,12 +1140,12 @@ proc genAsgn(c: PCtx; le, ri: PNode; requiresCopy: bool) = let dest = c.genx(left.sons[0], {gfAddrOf}) let idx = genField(left.sons[1]) let tmp = c.genx(ri) - c.gABC(left, opcWrObj, dest, idx, tmp) + c.preventFalseAlias(left, opcWrObj, dest, idx, tmp) c.freeTemp(tmp) of nkDerefExpr, nkHiddenDeref: let dest = c.genx(le.sons[0], {gfAddrOf}) let tmp = c.genx(ri) - c.gABC(le, opcWrDeref, dest, tmp) + c.preventFalseAlias(le, opcWrDeref, dest, 0, tmp) c.freeTemp(tmp) of nkSym: let s = le.sym @@ -1129,24 +1154,32 @@ proc genAsgn(c: PCtx; le, ri: PNode; requiresCopy: bool) = withTemp(tmp, le.typ): c.gen(le, tmp, {gfAddrOf}) let val = c.genx(ri) - c.gABC(le, opcWrDeref, tmp, val) + c.preventFalseAlias(le, opcWrDeref, tmp, 0, val) c.freeTemp(val) else: if s.kind == skForVar: c.setSlot s internalAssert s.position > 0 or (s.position == 0 and s.kind in {skParam,skResult}) var dest: TRegister = s.position + ord(s.kind == skParam) - gen(c, ri, dest) + if needsAdditionalCopy(le) and s.kind in {skResult, skVar, skParam}: + var cc = c.getTemp(le.typ) + gen(c, ri, cc) + c.gABC(le, whichAsgnOpc(le), dest, cc) + c.freeTemp(cc) + else: + gen(c, ri, dest) else: let dest = c.genx(le, {gfAddrOf}) genAsgn(c, dest, ri, requiresCopy) proc genLit(c: PCtx; n: PNode; dest: var TDest) = - var opc = opcLdConst + # opcLdConst is now always valid. We produce the necessary copy in the + # assignments now: + #var opc = opcLdConst if dest < 0: dest = c.getTemp(n.typ) - elif c.prc.slots[dest].kind == slotFixedVar: opc = opcAsgnConst + #elif c.prc.slots[dest].kind == slotFixedVar: opc = opcAsgnConst let lit = genLiteral(c, n) - c.gABx(n, opc, dest, lit) + c.gABx(n, opcLdConst, dest, lit) proc genTypeLit(c: PCtx; t: PType; dest: var TDest) = var n = newNode(nkType) @@ -1175,7 +1208,7 @@ proc genGlobalInit(c: PCtx; n: PNode; s: PSym) = let dest = c.getTemp(s.typ) c.gABx(n, opcLdGlobal, dest, s.position) let tmp = c.genx(s.ast) - c.gABC(n, opcWrDeref, dest, tmp) + c.preventFalseAlias(n, opcWrDeref, dest, 0, tmp) c.freeTemp(dest) c.freeTemp(tmp) @@ -1332,7 +1365,7 @@ proc genVarSection(c: PCtx; n: PNode) = if a.sons[2].kind != nkEmpty: let tmp = c.genx(a.sons[0], {gfAddrOf}) let val = c.genx(a.sons[2]) - c.gABC(a, opcWrDeref, tmp, val) + c.preventFalseAlias(a, opcWrDeref, tmp, 0, val) c.freeTemp(val) c.freeTemp(tmp) else: @@ -1342,7 +1375,14 @@ proc genVarSection(c: PCtx; n: PNode) = else: if not fitsRegister(s.typ): c.gABx(a, ldNullOpcode(s.typ), s.position, c.genType(s.typ)) - gen(c, a.sons[2], s.position.TRegister) + let le = a.sons[0] + if not fitsRegister(le.typ) and s.kind in {skResult, skVar, skParam}: + var cc = c.getTemp(le.typ) + gen(c, a.sons[2], cc) + c.gABC(le, whichAsgnOpc(le), s.position.TRegister, cc) + c.freeTemp(cc) + else: + gen(c, a.sons[2], s.position.TRegister) else: # assign to a.sons[0]; happens for closures if a.sons[2].kind == nkEmpty: @@ -1370,7 +1410,7 @@ proc genArrayConstr(c: PCtx, n: PNode, dest: var TDest) = c.gABx(n, opcLdNullReg, tmp, c.genType(intType)) for x in n: let a = c.genx(x) - c.gABC(n, whichAsgnOpc(x, opcWrArr), dest, tmp, a) + c.preventFalseAlias(n, whichAsgnOpc(x, opcWrArr), dest, tmp, a) c.gABI(n, opcAddImmInt, tmp, tmp, 1) c.freeTemp(a) c.freeTemp(tmp) @@ -1402,7 +1442,8 @@ proc genObjConstr(c: PCtx, n: PNode, dest: var TDest) = if it.kind == nkExprColonExpr and it.sons[0].kind == nkSym: let idx = genField(it.sons[0]) let tmp = c.genx(it.sons[1]) - c.gABC(it, whichAsgnOpc(it.sons[1], opcWrObj), dest, idx, tmp) + c.preventFalseAlias(it.sons[1], whichAsgnOpc(it.sons[1], opcWrObj), + dest, idx, tmp) c.freeTemp(tmp) else: internalError(n.info, "invalid object constructor") @@ -1416,11 +1457,12 @@ proc genTupleConstr(c: PCtx, n: PNode, dest: var TDest) = if it.kind == nkExprColonExpr: let idx = genField(it.sons[0]) let tmp = c.genx(it.sons[1]) - c.gABC(it, whichAsgnOpc(it.sons[1], opcWrObj), dest, idx, tmp) + c.preventFalseAlias(it.sons[1], whichAsgnOpc(it.sons[1], opcWrObj), + dest, idx, tmp) c.freeTemp(tmp) else: let tmp = c.genx(it) - c.gABC(it, whichAsgnOpc(it, opcWrObj), dest, i.TRegister, tmp) + c.preventFalseAlias(it, whichAsgnOpc(it, opcWrObj), dest, i.TRegister, tmp) c.freeTemp(tmp) proc genProc*(c: PCtx; s: PSym): int @@ -1658,7 +1700,7 @@ proc genProc(c: PCtx; s: PSym): int = c.gABC(body, opcEof, eofInstr.regA) c.optimizeJumps(result) s.offset = c.prc.maxSlots - #if s.name.s == "find": + #if s.name.s == "get_data": # echo renderTree(body) # c.echoCode(result) c.prc = oldPrc diff --git a/tests/casestmt/tcase_arrayconstr.nim b/tests/casestmt/tcase_arrayconstr.nim new file mode 100644 index 0000000000..cd7156600a --- /dev/null +++ b/tests/casestmt/tcase_arrayconstr.nim @@ -0,0 +1,19 @@ +discard """ + output: '''Not found! +Found!''' +""" + +const + md_extension = [".md", ".markdown"] + +proc test(ext: string) = + case ext + of ".txt", md_extension: + echo "Found!" + else: + echo "Not found!" + +test(".something") +# ensure it's not evaluated at compile-time: +var foo = ".markdown" +test(foo) diff --git a/tests/macros/tbug1149.nim b/tests/macros/tbug1149.nim index 5c4cb85309..d2bff61d3f 100644 --- a/tests/macros/tbug1149.nim +++ b/tests/macros/tbug1149.nim @@ -2,7 +2,13 @@ discard """ msg: '''a s d -f''' +f +TTaa +TTaa +TTaa +TTaa''' + +output: '''test''' """ type @@ -18,3 +24,41 @@ macro test(): stmt = echo i.s test() + + +# bug 1297 + +import macros + +type TType = tuple[s: string] + +macro echotest(): stmt = + var t: TType + t.s = "" + t.s.add("test") + result = newCall(newIdentNode("echo"), newStrLitNode(t.s)) + +echotest() + +# bug #1103 + +type + Td = tuple + a:string + b:int + +proc get_data(d: Td) : string {.compileTime.} = + result = d.a # Works if a literal string is used here. + # Bugs if line A or B is active. Works with C + result &= "aa" # A + #result.add("aa") # B + #result = result & "aa" # C + +macro m(s:static[Td]) : stmt = + echo get_data(s) + echo get_data(s) + result = newEmptyNode() + +const s=("TT", 3) +m(s) +m(s) diff --git a/todo.txt b/todo.txt index f027be8d53..8a1caee3b2 100644 --- a/todo.txt +++ b/todo.txt @@ -1,7 +1,8 @@ version 0.9.6 ============= -- some table related tests are wrong (memory usage checks) +- integrate the new LL into the devel branch +- start experimental branch Concurrency ----------- @@ -31,6 +32,7 @@ Misc - type API for macros; make 'spawn' a macro - markAndSweepGC should expose an API for fibers - prevent 'alloc(TypeWithGCedMemory)' +- some table related tests are wrong (memory usage checks) Bugs From 91ecae9c9328077f87053bc1fc313e950de4a373 Mon Sep 17 00:00:00 2001 From: Araq Date: Tue, 8 Jul 2014 08:48:48 +0200 Subject: [PATCH 252/270] fixes #1328 --- lib/system.nim | 3 +++ 1 file changed, 3 insertions(+) diff --git a/lib/system.nim b/lib/system.nim index 0cd1f77df4..ac259c560e 100644 --- a/lib/system.nim +++ b/lib/system.nim @@ -46,6 +46,7 @@ const on* = true ## alias for ``true`` off* = false ## alias for ``false`` +{.push warning[GcMem]: off.} {.push hints: off.} type @@ -2989,6 +2990,8 @@ proc deepCopy*[T](x: T): T {.magic: "DeepCopy", noSideEffect.} = discard ## performs a deep copy of `x`. This is also used by the code generator ## for the implementation of ``spawn``. +{.pop.} #{.push warning[GcMem]: off.} + when not defined(booting): type semistatic*[T] = static[T] | T From d3c79cec803dab88279e6657ccd10f612a3633c0 Mon Sep 17 00:00:00 2001 From: Araq Date: Tue, 8 Jul 2014 08:54:58 +0200 Subject: [PATCH 253/270] fixes #1167 --- doc/manual.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/manual.txt b/doc/manual.txt index cb1bb83aae..32b0541e9e 100644 --- a/doc/manual.txt +++ b/doc/manual.txt @@ -5466,7 +5466,7 @@ and warning message contains a symbol in brackets. This is the message's identifier that can be used to enable or disable it: .. code-block:: Nimrod - {.warning[LineTooLong]: off.} # turn off warning about too long lines + {.hint[LineTooLong]: off.} # turn off the hint about too long lines This is often better than disabling all warnings at once. From 39ce17a73e1e9f9e0eea0fa885a7969c96f1a3b4 Mon Sep 17 00:00:00 2001 From: Araq Date: Tue, 8 Jul 2014 16:37:45 +0200 Subject: [PATCH 254/270] fixes #933 --- compiler/vm.nim | 5 +---- tests/macros/tbug1149.nim | 18 +++++++++++++++++- 2 files changed, 18 insertions(+), 5 deletions(-) diff --git a/compiler/vm.nim b/compiler/vm.nim index f879e36bca..155617edbb 100644 --- a/compiler/vm.nim +++ b/compiler/vm.nim @@ -671,14 +671,11 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg = of opcLtu: decodeBC(rkInt) regs[ra].intVal = ord(regs[rb].intVal <% regs[rc].intVal) - of opcEqRef: + of opcEqRef, opcEqNimrodNode: decodeBC(rkInt) regs[ra].intVal = ord((regs[rb].node.kind == nkNilLit and regs[rc].node.kind == nkNilLit) or regs[rb].node == regs[rc].node) - of opcEqNimrodNode: - decodeBC(rkInt) - regs[ra].intVal = ord(regs[rb].node == regs[rc].node) of opcXor: decodeBC(rkInt) regs[ra].intVal = ord(regs[rb].intVal != regs[rc].intVal) diff --git a/tests/macros/tbug1149.nim b/tests/macros/tbug1149.nim index d2bff61d3f..3318fbfd83 100644 --- a/tests/macros/tbug1149.nim +++ b/tests/macros/tbug1149.nim @@ -6,7 +6,10 @@ f TTaa TTaa TTaa -TTaa''' +TTaa +true +true +nil''' output: '''test''' """ @@ -62,3 +65,16 @@ macro m(s:static[Td]) : stmt = const s=("TT", 3) m(s) m(s) + +# bug #933 + +proc nilcheck(): PNimrodNode {.compileTime.} = + echo(result == nil) # true + echo(result.isNil) # true + echo(repr(result)) # nil + +macro testnilcheck(): stmt = + result = newNimNode(nnkStmtList) + discard nilcheck() + +testnilcheck() From d80d8aa74d4e7471c262d95aedbe0971a1b2672a Mon Sep 17 00:00:00 2001 From: Araq Date: Tue, 8 Jul 2014 20:12:07 +0200 Subject: [PATCH 255/270] fixes #1323 --- compiler/vmgen.nim | 9 ++++++--- tests/macros/{tbug1149.nim => tbugs.nim} | 12 +++++++++++- todo.txt | 2 +- 3 files changed, 18 insertions(+), 5 deletions(-) rename tests/macros/{tbug1149.nim => tbugs.nim} (89%) diff --git a/compiler/vmgen.nim b/compiler/vmgen.nim index b04e605490..c1ec637ddb 100644 --- a/compiler/vmgen.nim +++ b/compiler/vmgen.nim @@ -1244,12 +1244,15 @@ proc genRdVar(c: PCtx; n: PNode; dest: var TDest; flags: TGenFlags) = # see tests/t99bott for an example that triggers it: cannotEval(n) +template needsRegLoad(): expr = + gfAddrOf notin flags and fitsRegister(n.typ.skipTypes({tyVar})) + proc genArrAccess2(c: PCtx; n: PNode; dest: var TDest; opc: TOpcode; flags: TGenFlags) = let a = c.genx(n.sons[0], flags) let b = c.genIndex(n.sons[1], n.sons[0].typ) if dest < 0: dest = c.getTemp(n.typ) - if gfAddrOf notin flags and fitsRegister(n.typ): + if needsRegLoad(): var cc = c.getTemp(n.typ) c.gABC(n, opc, cc, a, b) c.gABC(n, opcNodeToReg, dest, cc) @@ -1265,7 +1268,7 @@ proc genObjAccess(c: PCtx; n: PNode; dest: var TDest; flags: TGenFlags) = let a = c.genx(n.sons[0], flags) let b = genField(n.sons[1]) if dest < 0: dest = c.getTemp(n.typ) - if gfAddrOf notin flags and fitsRegister(n.typ.skipTypes({tyVar})): + if needsRegLoad(): var cc = c.getTemp(n.typ) c.gABC(n, opcLdObj, cc, a, b) c.gABC(n, opcNodeToReg, dest, cc) @@ -1700,7 +1703,7 @@ proc genProc(c: PCtx; s: PSym): int = c.gABC(body, opcEof, eofInstr.regA) c.optimizeJumps(result) s.offset = c.prc.maxSlots - #if s.name.s == "get_data": + #if s.name.s == "calc": # echo renderTree(body) # c.echoCode(result) c.prc = oldPrc diff --git a/tests/macros/tbug1149.nim b/tests/macros/tbugs.nim similarity index 89% rename from tests/macros/tbug1149.nim rename to tests/macros/tbugs.nim index 3318fbfd83..3db851dd15 100644 --- a/tests/macros/tbug1149.nim +++ b/tests/macros/tbugs.nim @@ -11,7 +11,8 @@ true true nil''' -output: '''test''' +output: '''test +2''' """ type @@ -78,3 +79,12 @@ macro testnilcheck(): stmt = discard nilcheck() testnilcheck() + +# bug #1323 + +proc calc(): array[1, int] = + result[0].inc() + result[0].inc() + +const c = calc() +echo c[0] diff --git a/todo.txt b/todo.txt index 8a1caee3b2..8450cbbc31 100644 --- a/todo.txt +++ b/todo.txt @@ -7,13 +7,13 @@ version 0.9.6 Concurrency ----------- +- 'gcsafe' inferrence needs to be fixed - the disjoint checker needs to deal with 'a = spawn f(); g = spawn f()' - implement 'deepCopy' builtin - implement 'foo[1..4] = spawn(f[4..7])' - support for exception propagation - Minor: The copying of the 'ref Promise' into the thead local storage only happens to work due to the write barrier's implementation -- 'gcsafe' inferrence needs to be fixed - implement lock levels --> first without the more complex race avoidance - document the new 'spawn' and 'parallel' statements From 27fdc5fe33cff3ba0ad33d055a07d060b0682250 Mon Sep 17 00:00:00 2001 From: Clay Sweetser Date: Tue, 8 Jul 2014 16:25:13 -0400 Subject: [PATCH 256/270] Fixes #542 --- lib/system.nim | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/lib/system.nim b/lib/system.nim index 0cd1f77df4..88f6be8642 100644 --- a/lib/system.nim +++ b/lib/system.nim @@ -2663,6 +2663,8 @@ when hostOS != "standalone": proc `[]`*[Idx, T](a: array[Idx, T], x: TSlice[int]): seq[T] = ## slice operation for arrays. Negative indexes are **not** supported ## because the array might have negative bounds. + when low(a) < 0: + {.error: "Slicing for arrays with negative indices is unsupported.".} var L = x.b - x.a + 1 newSeq(result, L) for i in 0.. Date: Wed, 9 Jul 2014 21:10:11 +0200 Subject: [PATCH 257/270] fixes #1011 --- compiler/semgnrc.nim | 37 ++++++++++++++++++++++++++++++++--- tests/generics/mdotlookup.nim | 8 ++++++++ tests/generics/tdotlookup.nim | 7 +++++++ 3 files changed, 49 insertions(+), 3 deletions(-) create mode 100644 tests/generics/mdotlookup.nim create mode 100644 tests/generics/tdotlookup.nim diff --git a/compiler/semgnrc.nim b/compiler/semgnrc.nim index 353017bdd3..1772b25469 100644 --- a/compiler/semgnrc.nim +++ b/compiler/semgnrc.nim @@ -82,7 +82,37 @@ proc lookup(c: PContext, n: PNode, flags: TSemGenericFlags, else: result = semGenericStmtSymbol(c, n, s) # else: leave as nkIdent + +proc newDot(n, b: PNode): PNode = + result = newNodeI(nkDotExpr, n.info) + result.add(n.sons[0]) + result.add(b) + +proc fuzzyLookup(c: PContext, n: PNode, flags: TSemGenericFlags, + ctx: var TIntSet): PNode = + assert n.kind == nkDotExpr + let luf = if withinMixin notin flags: {checkUndeclared} else: {} + var s = qualifiedLookUp(c, n, luf) + if s != nil: + result = semGenericStmtSymbol(c, n, s) + else: + result = n + let n = n[1] + let ident = considerQuotedIdent(n) + var s = searchInScopes(c, ident) + if s != nil: + if withinBind in flags: + result = newDot(result, symChoice(c, n, s, scClosed)) + elif s.name.id in ctx: + result = newDot(result, symChoice(c, n, s, scForceOpen)) + else: + let sym = semGenericStmtSymbol(c, n, s) + if sym.kind == nkSym: + result = newDot(result, symChoice(c, n, s, scForceOpen)) + else: + result = newDot(result, sym) + proc semGenericStmt(c: PContext, n: PNode, flags: TSemGenericFlags, ctx: var TIntSet): PNode = result = n @@ -91,10 +121,11 @@ proc semGenericStmt(c: PContext, n: PNode, of nkIdent, nkAccQuoted: result = lookup(c, n, flags, ctx) of nkDotExpr: - let luf = if withinMixin notin flags: {checkUndeclared} else: {} - var s = qualifiedLookUp(c, n, luf) - if s != nil: result = semGenericStmtSymbol(c, n, s) + #let luf = if withinMixin notin flags: {checkUndeclared} else: {} + #var s = qualifiedLookUp(c, n, luf) + #if s != nil: result = semGenericStmtSymbol(c, n, s) # XXX for example: ``result.add`` -- ``add`` needs to be looked up here... + result = fuzzyLookup(c, n, flags, ctx) of nkEmpty, nkSym..nkNilLit: # see tests/compile/tgensymgeneric.nim: # We need to open the gensym'ed symbol again so that the instantiation diff --git a/tests/generics/mdotlookup.nim b/tests/generics/mdotlookup.nim new file mode 100644 index 0000000000..7a5e0ccbf0 --- /dev/null +++ b/tests/generics/mdotlookup.nim @@ -0,0 +1,8 @@ +proc baz(o: any): int = 5 # if bar is exported, it works + +type MyObj = object + x: int + +proc foo*(b: any) = + var o: MyObj + echo b.baz, " ", o.x.baz, " ", b.baz() diff --git a/tests/generics/tdotlookup.nim b/tests/generics/tdotlookup.nim new file mode 100644 index 0000000000..b886cd8c91 --- /dev/null +++ b/tests/generics/tdotlookup.nim @@ -0,0 +1,7 @@ +discard """ + output: '''5 5 5''' +""" + +import mdotlookup + +foo(7) From 44353c585f16da7ca868d5e0e748065c415648e8 Mon Sep 17 00:00:00 2001 From: Araq Date: Thu, 10 Jul 2014 00:59:00 +0200 Subject: [PATCH 258/270] VM: fixes 'raise' --- compiler/vm.nim | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/compiler/vm.nim b/compiler/vm.nim index 155617edbb..66595856ab 100644 --- a/compiler/vm.nim +++ b/compiler/vm.nim @@ -902,10 +902,10 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg = c.exceptionInstr = pc let (newPc, newTos) = cleanUpOnException(c, tos) # -1 because of the following 'inc' - if pc-1 < 0: + if newPc-1 < 0: bailOut(c, tos) return - pc = newPc -1 + pc = newPc-1 if tos != newTos: tos = newTos move(regs, tos.slots) From e4e32bdfbf72931c7e0e5692a30c3030490178e1 Mon Sep 17 00:00:00 2001 From: Araq Date: Thu, 10 Jul 2014 00:59:26 +0200 Subject: [PATCH 259/270] better error messages for macro instantiations --- compiler/sem.nim | 3 +++ 1 file changed, 3 insertions(+) diff --git a/compiler/sem.nim b/compiler/sem.nim index f12b638acd..8025ef70de 100644 --- a/compiler/sem.nim +++ b/compiler/sem.nim @@ -305,6 +305,8 @@ proc semAfterMacroCall(c: PContext, n: PNode, s: PSym, proc semMacroExpr(c: PContext, n, nOrig: PNode, sym: PSym, flags: TExprFlags = {}): PNode = + pushInfoContext(nOrig.info) + markUsed(n.info, sym) if sym == c.p.owner: globalError(n.info, errRecursiveDependencyX, sym.name.s) @@ -315,6 +317,7 @@ proc semMacroExpr(c: PContext, n, nOrig: PNode, sym: PSym, result = evalMacroCall(c.module, n, nOrig, sym) if efNoSemCheck notin flags: result = semAfterMacroCall(c, result, sym, flags) + popInfoContext() proc forceBool(c: PContext, n: PNode): PNode = result = fitNode(c, getSysType(tyBool), n) From 82fc908ebb64a4768e2cb114bcd9902bca86e21f Mon Sep 17 00:00:00 2001 From: def Date: Fri, 11 Jul 2014 01:27:01 +0200 Subject: [PATCH 260/270] Export FileInfo object --- lib/pure/os.nim | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/lib/pure/os.nim b/lib/pure/os.nim index e2fc62d773..0b4538abc7 100644 --- a/lib/pure/os.nim +++ b/lib/pure/os.nim @@ -1769,16 +1769,16 @@ else: FileId = TIno type - FileInfo = object + FileInfo* = object ## Contains information associated with a file object. - id: tuple[device: DeviceId, file: FileId] # Device and file id. - kind: TPathComponent # Kind of file object - directory, symlink, etc. - size: BiggestInt # Size of file. - permissions: set[TFilePermission] # File permissions - linkCount: BiggestInt # Number of hard links the file object has. - lastAccessTime: TTime # Time file was last accessed. - lastWriteTime: TTime # Time file was last modified/written to. - creationTime: TTime # Time file was created. Not supported on all systems! + id*: tuple[device: DeviceId, file: FileId] # Device and file id. + kind*: TPathComponent # Kind of file object - directory, symlink, etc. + size*: BiggestInt # Size of file. + permissions*: set[TFilePermission] # File permissions + linkCount*: BiggestInt # Number of hard links the file object has. + lastAccessTime*: TTime # Time file was last accessed. + lastWriteTime*: TTime # Time file was last modified/written to. + creationTime*: TTime # Time file was created. Not supported on all systems! template rawToFormalFileInfo(rawInfo, formalInfo): expr = ## Transforms the native file info structure into the one nimrod uses. From 49ad6fc3d46ce5af146196af6277dfd11599a849 Mon Sep 17 00:00:00 2001 From: Araq Date: Fri, 11 Jul 2014 08:05:50 +0200 Subject: [PATCH 261/270] fixes latest regression --- compiler/semgnrc.nim | 2 +- todo.txt | 1 + web/news.txt | 2 ++ 3 files changed, 4 insertions(+), 1 deletion(-) diff --git a/compiler/semgnrc.nim b/compiler/semgnrc.nim index 1772b25469..934434951c 100644 --- a/compiler/semgnrc.nim +++ b/compiler/semgnrc.nim @@ -101,7 +101,7 @@ proc fuzzyLookup(c: PContext, n: PNode, flags: TSemGenericFlags, let n = n[1] let ident = considerQuotedIdent(n) var s = searchInScopes(c, ident) - if s != nil: + if s != nil and s.kind in routineKinds: if withinBind in flags: result = newDot(result, symChoice(c, n, s, scClosed)) elif s.name.id in ctx: diff --git a/todo.txt b/todo.txt index 8450cbbc31..d1387669d6 100644 --- a/todo.txt +++ b/todo.txt @@ -1,6 +1,7 @@ version 0.9.6 ============= +- scopes are still broken for generic instantiation! - integrate the new LL into the devel branch - start experimental branch diff --git a/web/news.txt b/web/news.txt index eef71a2f7a..a3019df517 100644 --- a/web/news.txt +++ b/web/news.txt @@ -12,6 +12,8 @@ News - ``spawn`` now uses an elaborate self-adapting thread pool and as such has been moved into its own module. So to use it, you now have to import ``threadpool``. + - The symbol binding rules in generics changed: ``bar`` in ``foo.bar`` is + now considered for implicit early binding. Library Additions From cf5c8a204e76ff9ed5edb6ec0ebe3b97ee90f553 Mon Sep 17 00:00:00 2001 From: Dominik Picheta Date: Sat, 12 Jul 2014 22:51:06 +0100 Subject: [PATCH 262/270] Many async optimisations. * Selectors implementation will now attempt to immediately execute an IO operation instead of waiting for a ready notification. * Removed recursion in asynchttpserver. * Improved buffered implementation of recvLine in asyncnet. * Optimised ``respond`` in asynchttpserver removing a possible "Delayed ACK" situation. --- lib/pure/asyncdispatch.nim | 30 ++++-- lib/pure/asynchttpserver.nim | 176 ++++++++++++++++++----------------- lib/pure/asyncnet.nim | 74 ++++++++++----- lib/pure/selectors.nim | 2 +- 4 files changed, 166 insertions(+), 116 deletions(-) diff --git a/lib/pure/asyncdispatch.nim b/lib/pure/asyncdispatch.nim index 12329951c4..14667a008f 100644 --- a/lib/pure/asyncdispatch.nim +++ b/lib/pure/asyncdispatch.nim @@ -552,7 +552,18 @@ when defined(windows) or defined(nimdoc): initAll() else: import selectors - from posix import EINTR, EAGAIN, EINPROGRESS, EWOULDBLOCK, MSG_PEEK + when defined(windows): + import winlean + const + EINTR = WSAEINPROGRESS + EINPROGRESS = WSAEINPROGRESS + EWOULDBLOCK = WSAEWOULDBLOCK + EAGAIN = EINPROGRESS + MSG_NOSIGNAL = 0 + else: + from posix import EINTR, EAGAIN, EINPROGRESS, EWOULDBLOCK, MSG_PEEK, + MSG_NOSIGNAL + type TAsyncFD* = distinct cint TCallback = proc (sock: TAsyncFD): bool {.closure,gcsafe.} @@ -693,12 +704,12 @@ else: proc cb(sock: TAsyncFD): bool = result = true - let res = recv(sock.TSocketHandle, addr readBuffer[0], size, + let res = recv(sock.TSocketHandle, addr readBuffer[0], size.cint, flags.cint) #echo("recv cb res: ", res) if res < 0: let lastError = osLastError() - if lastError.int32 notin {EINTR, EWOULDBLOCK, EAGAIN}: + if lastError.int32 notin {EINTR, EWOULDBLOCK, EAGAIN}: retFuture.fail(newException(EOS, osErrorMsg(lastError))) else: result = false # We still want this callback to be called. @@ -708,8 +719,8 @@ else: else: readBuffer.setLen(res) retFuture.complete(readBuffer) - - addRead(socket, cb) + if not cb(socket): + addRead(socket, cb) return retFuture proc send*(socket: TAsyncFD, data: string): PFuture[void] = @@ -721,7 +732,8 @@ else: result = true let netSize = data.len-written var d = data.cstring - let res = send(sock.TSocketHandle, addr d[written], netSize, 0.cint) + let res = send(sock.TSocketHandle, addr d[written], netSize.cint, + MSG_NOSIGNAL) if res < 0: let lastError = osLastError() if lastError.int32 notin {EINTR, EWOULDBLOCK, EAGAIN}: @@ -734,7 +746,8 @@ else: result = false # We still have data to send. else: retFuture.complete() - addWrite(socket, cb) + if not cb(socket): + addWrite(socket, cb) return retFuture proc acceptAddr*(socket: TAsyncFD): @@ -756,7 +769,8 @@ else: else: register(client.TAsyncFD) retFuture.complete(($inet_ntoa(sockAddress.sin_addr), client.TAsyncFD)) - addRead(socket, cb) + if not cb(socket): + addRead(socket, cb) return retFuture proc accept*(socket: TAsyncFD): PFuture[TAsyncFD] = diff --git a/lib/pure/asynchttpserver.nim b/lib/pure/asynchttpserver.nim index 1b47cf5f1b..6273d479ca 100644 --- a/lib/pure/asynchttpserver.nim +++ b/lib/pure/asynchttpserver.nim @@ -51,10 +51,15 @@ proc `==`*(protocol: tuple[orig: string, major, minor: int], proc newAsyncHttpServer*(): PAsyncHttpServer = new result -proc sendHeaders*(req: TRequest, headers: PStringTable) {.async.} = - ## Sends the specified headers to the requesting client. +proc addHeaders(msg: var string, headers: PStringTable) = for k, v in headers: - await req.client.send(k & ": " & v & "\c\L") + msg.add(k & ": " & v & "\c\L") + +proc sendHeaders*(req: TRequest, headers: PStringTable): PFuture[void] = + ## Sends the specified headers to the requesting client. + var msg = "" + addHeaders(msg, headers) + return req.client.send(msg) proc respond*(req: TRequest, code: THttpCode, content: string, headers: PStringTable = newStringTable()) {.async.} = @@ -64,9 +69,9 @@ proc respond*(req: TRequest, code: THttpCode, ## This procedure will **not** close the client socket. var customHeaders = headers customHeaders["Content-Length"] = $content.len - await req.client.send("HTTP/1.1 " & $code & "\c\L") - await sendHeaders(req, headers) - await req.client.send("\c\L" & content) + var msg = "HTTP/1.1 " & $code & "\c\L" + msg.addHeaders(customHeaders) + await req.client.send(msg & "\c\L" & content) proc newRequest(): TRequest = result.headers = newStringTable(modeCaseInsensitive) @@ -93,90 +98,91 @@ proc sendStatus(client: PAsyncSocket, status: string): PFuture[void] = proc processClient(client: PAsyncSocket, address: string, callback: proc (request: TRequest): PFuture[void]) {.async.} = - # GET /path HTTP/1.1 - # Header: val - # \n - var request = newRequest() - request.hostname = address - assert client != nil - request.client = client - var runCallback = true - - # First line - GET /path HTTP/1.1 - let line = await client.recvLine() # TODO: Timeouts. - if line == "": - client.close() - return - let lineParts = line.split(' ') - if lineParts.len != 3: - request.respond(Http400, "Invalid request. Got: " & line) - runCallback = false - - let reqMethod = lineParts[0] - let path = lineParts[1] - let protocol = lineParts[2] - - # Headers - var i = 0 while true: - i = 0 - let headerLine = await client.recvLine() - if headerLine == "": - client.close(); return - if headerLine == "\c\L": break - # TODO: Compiler crash - #let (key, value) = parseHeader(headerLine) - let kv = parseHeader(headerLine) - request.headers[kv.key] = kv.value + # GET /path HTTP/1.1 + # Header: val + # \n + var request = newRequest() + request.hostname = address + assert client != nil + request.client = client + var runCallback = true - request.reqMethod = reqMethod - request.url = parseUrl(path) - try: - request.protocol = protocol.parseProtocol() - except EInvalidValue: - request.respond(Http400, "Invalid request protocol. Got: " & protocol) - runCallback = false - - if reqMethod.normalize == "post": - # Check for Expect header - if request.headers.hasKey("Expect"): - if request.headers["Expect"].toLower == "100-continue": - await client.sendStatus("100 Continue") - else: - await client.sendStatus("417 Expectation Failed") - - # Read the body - # - Check for Content-length header - if request.headers.hasKey("Content-Length"): - var contentLength = 0 - if parseInt(request.headers["Content-Length"], contentLength) == 0: - await request.respond(Http400, "Bad Request. Invalid Content-Length.") - else: - request.body = await client.recv(contentLength) - assert request.body.len == contentLength - else: - await request.respond(Http400, "Bad Request. No Content-Length.") + # First line - GET /path HTTP/1.1 + let line = await client.recvLine() # TODO: Timeouts. + if line == "": + client.close() + return + let lineParts = line.split(' ') + if lineParts.len != 3: + request.respond(Http400, "Invalid request. Got: " & line) runCallback = false - case reqMethod.normalize - of "get", "post", "head", "put", "delete", "trace", "options", "connect", "patch": - if runCallback: - await callback(request) - else: - await request.respond(Http400, "Invalid request method. Got: " & reqMethod) + let reqMethod = lineParts[0] + let path = lineParts[1] + let protocol = lineParts[2] - # Persistent connections - if (request.protocol == HttpVer11 and - request.headers["connection"].normalize != "close") or - (request.protocol == HttpVer10 and - request.headers["connection"].normalize == "keep-alive"): - # In HTTP 1.1 we assume that connection is persistent. Unless connection - # header states otherwise. - # In HTTP 1.0 we assume that the connection should not be persistent. - # Unless the connection header states otherwise. - await processClient(client, address, callback) - else: - request.client.close() + # Headers + var i = 0 + while true: + i = 0 + let headerLine = await client.recvLine() + if headerLine == "": + client.close(); return + if headerLine == "\c\L": break + # TODO: Compiler crash + #let (key, value) = parseHeader(headerLine) + let kv = parseHeader(headerLine) + request.headers[kv.key] = kv.value + + request.reqMethod = reqMethod + request.url = parseUrl(path) + try: + request.protocol = protocol.parseProtocol() + except EInvalidValue: + request.respond(Http400, "Invalid request protocol. Got: " & protocol) + runCallback = false + + if reqMethod.normalize == "post": + # Check for Expect header + if request.headers.hasKey("Expect"): + if request.headers["Expect"].toLower == "100-continue": + await client.sendStatus("100 Continue") + else: + await client.sendStatus("417 Expectation Failed") + + # Read the body + # - Check for Content-length header + if request.headers.hasKey("Content-Length"): + var contentLength = 0 + if parseInt(request.headers["Content-Length"], contentLength) == 0: + await request.respond(Http400, "Bad Request. Invalid Content-Length.") + else: + request.body = await client.recv(contentLength) + assert request.body.len == contentLength + else: + await request.respond(Http400, "Bad Request. No Content-Length.") + runCallback = false + + case reqMethod.normalize + of "get", "post", "head", "put", "delete", "trace", "options", "connect", "patch": + if runCallback: + await callback(request) + else: + await request.respond(Http400, "Invalid request method. Got: " & reqMethod) + + # Persistent connections + if (request.protocol == HttpVer11 and + request.headers["connection"].normalize != "close") or + (request.protocol == HttpVer10 and + request.headers["connection"].normalize == "keep-alive"): + # In HTTP 1.1 we assume that connection is persistent. Unless connection + # header states otherwise. + # In HTTP 1.0 we assume that the connection should not be persistent. + # Unless the connection header states otherwise. + else: + request.client.close() + break proc serve*(server: PAsyncHttpServer, port: TPort, callback: proc (request: TRequest): PFuture[void], diff --git a/lib/pure/asyncnet.nim b/lib/pure/asyncnet.nim index d16c85c58e..6eb43b594c 100644 --- a/lib/pure/asyncnet.nim +++ b/lib/pure/asyncnet.nim @@ -110,12 +110,10 @@ proc recv*(socket: PAsyncSocket, size: int, if socket.currPos >= socket.bufLen: if (flags and MSG_PEEK) == MSG_PEEK: # We don't want to get another buffer if we're peeking. - result.setLen(read) - return + break let res = await socket.readIntoBuf(flags and (not MSG_PEEK)) if res == 0: - result.setLen(read) - return + break let chunk = min(socket.bufLen-socket.currPos, size-read) copyMem(addr(result[read]), addr(socket.buffer[socket.currPos]), chunk) @@ -181,28 +179,60 @@ proc recvLine*(socket: PAsyncSocket): PFuture[string] {.async.} = ## If the socket is disconnected in the middle of a line (before ``\r\L`` ## is read) then line will be set to ``""``. ## The partial line **will be lost**. - template addNLIfEmpty(): stmt = if result.len == 0: result.add("\c\L") - result = "" - var c = "" - while true: - c = await recv(socket, 1) - if c.len == 0: - return "" - if c == "\r": - c = await recv(socket, 1, MSG_PEEK) - if c.len > 0 and c == "\L": - let dummy = await recv(socket, 1) - assert dummy == "\L" - addNLIfEmpty() - return - elif c == "\L": - addNLIfEmpty() - return - add(result.string, c) + if socket.isBuffered: + result = "" + if socket.bufLen == 0: + let res = await socket.readIntoBuf(0) + if res == 0: + return + + var lastR = false + while true: + if socket.currPos >= socket.bufLen: + let res = await socket.readIntoBuf(0) + if res == 0: + result = "" + break + + case socket.buffer[socket.currPos] + of '\r': + lastR = true + addNLIfEmpty() + of '\L': + addNLIfEmpty() + socket.currPos.inc() + return + else: + if lastR: + socket.currPos.inc() + return + else: + result.add socket.buffer[socket.currPos] + socket.currPos.inc() + else: + + + result = "" + var c = "" + while true: + c = await recv(socket, 1) + if c.len == 0: + return "" + if c == "\r": + c = await recv(socket, 1, MSG_PEEK) + if c.len > 0 and c == "\L": + let dummy = await recv(socket, 1) + assert dummy == "\L" + addNLIfEmpty() + return + elif c == "\L": + addNLIfEmpty() + return + add(result.string, c) proc bindAddr*(socket: PAsyncSocket, port = TPort(0), address = "") = ## Binds ``address``:``port`` to the socket. diff --git a/lib/pure/selectors.nim b/lib/pure/selectors.nim index 3af5f699cf..bd53c2dbfd 100644 --- a/lib/pure/selectors.nim +++ b/lib/pure/selectors.nim @@ -163,7 +163,7 @@ elif defined(linux): proc newSelector*(): PSelector = new result result.epollFD = epoll_create(64) - result.events = cast[array[64, epoll_event]](alloc0(sizeof(epoll_event)*64)) + #result.events = cast[array[64, epoll_event]](alloc0(sizeof(epoll_event)*64)) result.fds = initTable[TSocketHandle, PSelectorKey]() if result.epollFD < 0: OSError(OSLastError()) From 634a416c6769516acab72448e54a1ebc6d27690e Mon Sep 17 00:00:00 2001 From: Dominik Picheta Date: Sun, 13 Jul 2014 00:11:05 +0100 Subject: [PATCH 263/270] Async fixes for Linux. --- lib/pure/asyncdispatch.nim | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/lib/pure/asyncdispatch.nim b/lib/pure/asyncdispatch.nim index 14667a008f..483e4d5af5 100644 --- a/lib/pure/asyncdispatch.nim +++ b/lib/pure/asyncdispatch.nim @@ -719,8 +719,9 @@ else: else: readBuffer.setLen(res) retFuture.complete(readBuffer) - if not cb(socket): - addRead(socket, cb) + # TODO: The following causes a massive slowdown. + #if not cb(socket): + addRead(socket, cb) return retFuture proc send*(socket: TAsyncFD, data: string): PFuture[void] = @@ -746,8 +747,9 @@ else: result = false # We still have data to send. else: retFuture.complete() - if not cb(socket): - addWrite(socket, cb) + # TODO: The following causes crashes. + #if not cb(socket): + addWrite(socket, cb) return retFuture proc acceptAddr*(socket: TAsyncFD): @@ -769,8 +771,7 @@ else: else: register(client.TAsyncFD) retFuture.complete(($inet_ntoa(sockAddress.sin_addr), client.TAsyncFD)) - if not cb(socket): - addRead(socket, cb) + addRead(socket, cb) return retFuture proc accept*(socket: TAsyncFD): PFuture[TAsyncFD] = From 374706b1c3e6ceec6d514c1a1ac01ec6facb6dc7 Mon Sep 17 00:00:00 2001 From: Dominik Picheta Date: Sun, 13 Jul 2014 10:19:48 +0100 Subject: [PATCH 264/270] Void futures are no longer discardable. --- lib/pure/asyncdispatch.nim | 11 +++++++++-- lib/pure/asynchttpserver.nim | 17 ++++++++--------- lib/pure/httpclient.nim | 2 +- 3 files changed, 18 insertions(+), 12 deletions(-) diff --git a/lib/pure/asyncdispatch.nim b/lib/pure/asyncdispatch.nim index 483e4d5af5..d93afce6cd 100644 --- a/lib/pure/asyncdispatch.nim +++ b/lib/pure/asyncdispatch.nim @@ -126,6 +126,15 @@ proc failed*(future: PFutureBase): bool = ## Determines whether ``future`` completed with an error. future.error != nil +proc asyncCheck*[T](future: PFuture[T]) = + ## Sets a callback on ``future`` which raises an exception if the future + ## finished with an error. + ## + ## This should be used instead of ``discard`` to discard void futures. + future.callback = + proc () = + if future.failed: raise future.error + when defined(windows) or defined(nimdoc): import winlean, sets, hashes type @@ -1021,8 +1030,6 @@ macro async*(prc: stmt): stmt {.immediate.} = result[4].del(i) if subtypeIsVoid: # Add discardable pragma. - if prc.kind == nnkProcDef: # TODO: This is a workaround for #1287 - result[4].add(newIdentNode("discardable")) if returnType.kind == nnkEmpty: # Add PFuture[void] result[3][0] = parseExpr("PFuture[void]") diff --git a/lib/pure/asynchttpserver.nim b/lib/pure/asynchttpserver.nim index 6273d479ca..7061940d69 100644 --- a/lib/pure/asynchttpserver.nim +++ b/lib/pure/asynchttpserver.nim @@ -106,7 +106,6 @@ proc processClient(client: PAsyncSocket, address: string, request.hostname = address assert client != nil request.client = client - var runCallback = true # First line - GET /path HTTP/1.1 let line = await client.recvLine() # TODO: Timeouts. @@ -115,8 +114,8 @@ proc processClient(client: PAsyncSocket, address: string, return let lineParts = line.split(' ') if lineParts.len != 3: - request.respond(Http400, "Invalid request. Got: " & line) - runCallback = false + await request.respond(Http400, "Invalid request. Got: " & line) + continue let reqMethod = lineParts[0] let path = lineParts[1] @@ -140,8 +139,9 @@ proc processClient(client: PAsyncSocket, address: string, try: request.protocol = protocol.parseProtocol() except EInvalidValue: - request.respond(Http400, "Invalid request protocol. Got: " & protocol) - runCallback = false + asyncCheck request.respond(Http400, "Invalid request protocol. Got: " & + protocol) + continue if reqMethod.normalize == "post": # Check for Expect header @@ -162,12 +162,11 @@ proc processClient(client: PAsyncSocket, address: string, assert request.body.len == contentLength else: await request.respond(Http400, "Bad Request. No Content-Length.") - runCallback = false + continue case reqMethod.normalize of "get", "post", "head", "put", "delete", "trace", "options", "connect", "patch": - if runCallback: - await callback(request) + await callback(request) else: await request.respond(Http400, "Invalid request method. Got: " & reqMethod) @@ -199,7 +198,7 @@ proc serve*(server: PAsyncHttpServer, port: TPort, # TODO: Causes compiler crash. #var (address, client) = await server.socket.acceptAddr() var fut = await server.socket.acceptAddr() - processClient(fut.client, fut.address, callback) + asyncCheck processClient(fut.client, fut.address, callback) proc close*(server: PAsyncHttpServer) = ## Terminates the async http server instance. diff --git a/lib/pure/httpclient.nim b/lib/pure/httpclient.nim index be06a7b8e8..9bacc80d62 100644 --- a/lib/pure/httpclient.nim +++ b/lib/pure/httpclient.nim @@ -654,7 +654,7 @@ when isMainModule: resp = await client.request("http://nimrod-lang.org/download.html") echo("Got response: ", resp.status) - main() + asyncCheck main() runForever() else: From cdcdab49b724eff4bb45d34d9cf9b2e08953b14f Mon Sep 17 00:00:00 2001 From: Dominik Picheta Date: Sun, 13 Jul 2014 11:02:34 +0100 Subject: [PATCH 265/270] Fix broken async tests. --- lib/pure/asynchttpserver.nim | 2 +- lib/pure/asyncnet.nim | 2 +- tests/async/tasyncawait.nim | 6 +++--- tests/async/tasyncdiscard.nim | 2 +- tests/async/tnestedpfuturetypeparam.nim | 2 +- 5 files changed, 7 insertions(+), 7 deletions(-) diff --git a/lib/pure/asynchttpserver.nim b/lib/pure/asynchttpserver.nim index 7061940d69..ee6658fd1f 100644 --- a/lib/pure/asynchttpserver.nim +++ b/lib/pure/asynchttpserver.nim @@ -213,5 +213,5 @@ when isMainModule: "Content-type": "text/plain; charset=utf-8"} await req.respond(Http200, "Hello World", headers.newStringTable()) - server.serve(TPort(5555), cb) + asyncCheck server.serve(TPort(5555), cb) runForever() diff --git a/lib/pure/asyncnet.nim b/lib/pure/asyncnet.nim index 6eb43b594c..fb9f1a26bb 100644 --- a/lib/pure/asyncnet.nim +++ b/lib/pure/asyncnet.nim @@ -271,7 +271,7 @@ when isMainModule: break else: echo("Got line: ", line) - main() + asyncCheck main() elif test == LowClient: var sock = newAsyncSocket() var f = connect(sock, "irc.freenode.net", TPort(6667)) diff --git a/tests/async/tasyncawait.nim b/tests/async/tasyncawait.nim index da49526776..2d65db4bdf 100644 --- a/tests/async/tasyncawait.nim +++ b/tests/async/tasyncawait.nim @@ -61,11 +61,11 @@ proc createServer(port: TPort) {.async.} = discard server.TSocketHandle.listen() while true: var client = await accept(server) - readMessages(client) + asyncCheck readMessages(client) # TODO: Test: readMessages(disp, await disp.accept(server)) -createServer(TPort(10335)) -launchSwarm(TPort(10335)) +asyncCheck createServer(TPort(10335)) +asyncCheck launchSwarm(TPort(10335)) while true: poll() if clientCount == swarmSize: break diff --git a/tests/async/tasyncdiscard.nim b/tests/async/tasyncdiscard.nim index 48d8a8c4d6..966851acc7 100644 --- a/tests/async/tasyncdiscard.nim +++ b/tests/async/tasyncdiscard.nim @@ -36,4 +36,4 @@ proc main {.async.} = discard await g() echo 6 -main() +asyncCheck main() diff --git a/tests/async/tnestedpfuturetypeparam.nim b/tests/async/tnestedpfuturetypeparam.nim index d0d87e5676..1db442170b 100644 --- a/tests/async/tnestedpfuturetypeparam.nim +++ b/tests/async/tnestedpfuturetypeparam.nim @@ -5,4 +5,4 @@ proc main {.async.} = await newAsyncSocket().connect("www.google.com", TPort(80)) let x = await f() -main() +asyncCheck main() From ac8ddb0720cc9cdb18c8ee3ae076b8cd5ae7f326 Mon Sep 17 00:00:00 2001 From: Dominik Picheta Date: Sun, 13 Jul 2014 22:32:39 +0100 Subject: [PATCH 266/270] Implement safe flags for socket operations. --- lib/pure/asyncdispatch.nim | 43 +++++++++++++++-------- lib/pure/asyncnet.nim | 37 +++++++++++--------- lib/pure/net.nim | 71 ++++++++++++++++++++++++++++++-------- lib/pure/rawsockets.nim | 5 +-- 4 files changed, 108 insertions(+), 48 deletions(-) diff --git a/lib/pure/asyncdispatch.nim b/lib/pure/asyncdispatch.nim index d93afce6cd..208e838721 100644 --- a/lib/pure/asyncdispatch.nim +++ b/lib/pure/asyncdispatch.nim @@ -11,8 +11,9 @@ include "system/inclrtl" import os, oids, tables, strutils, macros -import rawsockets -export TPort +import rawsockets, net + +export TPort, TSocketFlags #{.injectStmt: newGcInvariant().} @@ -353,7 +354,7 @@ when defined(windows) or defined(nimdoc): return retFuture proc recv*(socket: TAsyncFD, size: int, - flags: int = 0): PFuture[string] = + flags = {TSocketFlags.SafeDisconn}): PFuture[string] = ## Reads **up to** ``size`` bytes from ``socket``. Returned future will ## complete once all the data requested is read, a part of the data has been ## read, or the socket has disconnected in which case the future will @@ -373,7 +374,7 @@ when defined(windows) or defined(nimdoc): dataBuf.len = size var bytesReceived: DWord - var flagsio = flags.DWord + var flagsio = flags.toOSFlags().DWord var ol = PCustomOverlapped() GC_ref(ol) ol.data = TCompletionData(sock: socket, cb: @@ -403,7 +404,10 @@ when defined(windows) or defined(nimdoc): dealloc dataBuf.buf dataBuf.buf = nil GC_unref(ol) - retFuture.fail(newException(EOS, osErrorMsg(err))) + if flags.isDisconnectionError(err): + retFuture.complete("") + else: + 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 @@ -434,7 +438,8 @@ when defined(windows) or defined(nimdoc): # free ``ol``. return retFuture - proc send*(socket: TAsyncFD, data: string): PFuture[void] = + proc send*(socket: TAsyncFD, data: string, + flags = {TSocketFlags.SafeDisconn}): PFuture[void] = ## Sends ``data`` to ``socket``. The returned future will complete once all ## data has been sent. verifyPresence(socket) @@ -444,7 +449,7 @@ when defined(windows) or defined(nimdoc): dataBuf.buf = data # since this is not used in a callback, this is fine dataBuf.len = data.len - var bytesReceived, flags: DWord + var bytesReceived, lowFlags: DWord var ol = PCustomOverlapped() GC_ref(ol) ol.data = TCompletionData(sock: socket, cb: @@ -457,12 +462,15 @@ when defined(windows) or defined(nimdoc): ) let ret = WSASend(socket.TSocketHandle, addr dataBuf, 1, addr bytesReceived, - flags, cast[POverlapped](ol), nil) + lowFlags, cast[POverlapped](ol), nil) if ret == -1: let err = osLastError() if err.int32 != ERROR_IO_PENDING: - retFuture.fail(newException(EOS, osErrorMsg(err))) GC_unref(ol) + if flags.isDisconnectionError(err): + retFuture.complete() + else: + retFuture.fail(newException(EOS, osErrorMsg(err))) else: retFuture.complete() # We don't deallocate ``ol`` here because even though this completed @@ -706,7 +714,7 @@ else: return retFuture proc recv*(socket: TAsyncFD, size: int, - flags: int = 0): PFuture[string] = + flags = {TSocketFlags.SafeDisconn}): PFuture[string] = var retFuture = newFuture[string]() var readBuffer = newString(size) @@ -719,7 +727,10 @@ else: if res < 0: let lastError = osLastError() if lastError.int32 notin {EINTR, EWOULDBLOCK, EAGAIN}: - retFuture.fail(newException(EOS, osErrorMsg(lastError))) + if flags.isDisconnectionError(lastError): + retFuture.complete("") + else: + retFuture.fail(newException(EOS, osErrorMsg(lastError))) else: result = false # We still want this callback to be called. elif res == 0: @@ -733,7 +744,8 @@ else: addRead(socket, cb) return retFuture - proc send*(socket: TAsyncFD, data: string): PFuture[void] = + proc send*(socket: TAsyncFD, data: string, + flags = {TSocketFlags.SafeDisconn}): PFuture[void] = var retFuture = newFuture[void]() var written = 0 @@ -747,7 +759,10 @@ else: if res < 0: let lastError = osLastError() if lastError.int32 notin {EINTR, EWOULDBLOCK, EAGAIN}: - retFuture.fail(newException(EOS, osErrorMsg(lastError))) + if flags.isDisconnectionError(lastError): + retFuture.complete("") + else: + retFuture.fail(newException(EOS, osErrorMsg(lastError))) else: result = false # We still want this callback to be called. else: @@ -1065,7 +1080,7 @@ proc recvLine*(socket: TAsyncFD): PFuture[string] {.async.} = if c.len == 0: return "" if c == "\r": - c = await recv(socket, 1, MSG_PEEK) + c = await recv(socket, 1, {TSocketFlags.SafeDisconn, TSocketFlags.Peek}) if c.len > 0 and c == "\L": discard await recv(socket, 1) addNLIfEmpty() diff --git a/lib/pure/asyncnet.nim b/lib/pure/asyncnet.nim index fb9f1a26bb..374ac77e31 100644 --- a/lib/pure/asyncnet.nim +++ b/lib/pure/asyncnet.nim @@ -80,7 +80,8 @@ 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.} = +proc readIntoBuf(socket: PAsyncSocket, + flags: set[TSocketFlags]): 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) @@ -89,7 +90,7 @@ proc readIntoBuf(socket: PAsyncSocket, flags: int): PFuture[int] {.async.} = result = data.len proc recv*(socket: PAsyncSocket, size: int, - flags: int = 0): PFuture[string] {.async.} = + flags = {TSocketFlags.SafeDisconn}): 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 @@ -100,7 +101,7 @@ proc recv*(socket: PAsyncSocket, size: int, let originalBufPos = socket.currPos if socket.bufLen == 0: - let res = await socket.readIntoBuf(flags and (not MSG_PEEK)) + let res = await socket.readIntoBuf(flags - {TSocketFlags.Peek}) if res == 0: result.setLen(0) return @@ -108,10 +109,10 @@ proc recv*(socket: PAsyncSocket, size: int, var read = 0 while read < size: if socket.currPos >= socket.bufLen: - if (flags and MSG_PEEK) == MSG_PEEK: + if TSocketFlags.Peek in flags: # We don't want to get another buffer if we're peeking. break - let res = await socket.readIntoBuf(flags and (not MSG_PEEK)) + let res = await socket.readIntoBuf(flags - {TSocketFlags.Peek}) if res == 0: break @@ -120,18 +121,19 @@ proc recv*(socket: PAsyncSocket, size: int, read.inc(chunk) socket.currPos.inc(chunk) - if (flags and MSG_PEEK) == MSG_PEEK: + if TSocketFlags.Peek in flags: # Restore old buffer cursor position. socket.currPos = originalBufPos result.setLen(read) else: result = await recv(socket.fd.TAsyncFD, size, flags) -proc send*(socket: PAsyncSocket, data: string): PFuture[void] = +proc send*(socket: PAsyncSocket, data: string, + flags = {TSocketFlags.SafeDisconn}): 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) + result = send(socket.fd.TAsyncFD, data, flags) proc acceptAddr*(socket: PAsyncSocket): PFuture[tuple[address: string, client: PAsyncSocket]] = @@ -166,7 +168,8 @@ proc accept*(socket: PAsyncSocket): PFuture[PAsyncSocket] = retFut.complete(future.read.client) return retFut -proc recvLine*(socket: PAsyncSocket): PFuture[string] {.async.} = +proc recvLine*(socket: PAsyncSocket, + flags = {TSocketFlags.SafeDisconn}): PFuture[string] {.async.} = ## Reads a line of data from ``socket``. Returned future will complete once ## a full line is read or an error occurs. ## @@ -179,21 +182,23 @@ proc recvLine*(socket: PAsyncSocket): PFuture[string] {.async.} = ## If the socket is disconnected in the middle of a line (before ``\r\L`` ## is read) then line will be set to ``""``. ## The partial line **will be lost**. + ## + ## **Warning**: The ``Peek`` flag is not yet implemented. template addNLIfEmpty(): stmt = if result.len == 0: result.add("\c\L") - + assert TSocketFlags.Peek notin flags ## TODO: if socket.isBuffered: result = "" if socket.bufLen == 0: - let res = await socket.readIntoBuf(0) + let res = await socket.readIntoBuf(flags) if res == 0: return var lastR = false while true: if socket.currPos >= socket.bufLen: - let res = await socket.readIntoBuf(0) + let res = await socket.readIntoBuf(flags) if res == 0: result = "" break @@ -214,18 +219,16 @@ proc recvLine*(socket: PAsyncSocket): PFuture[string] {.async.} = result.add socket.buffer[socket.currPos] socket.currPos.inc() else: - - result = "" var c = "" while true: - c = await recv(socket, 1) + c = await recv(socket, 1, flags) if c.len == 0: return "" if c == "\r": - c = await recv(socket, 1, MSG_PEEK) + c = await recv(socket, 1, flags + {TSocketFlags.Peek}) if c.len > 0 and c == "\L": - let dummy = await recv(socket, 1) + let dummy = await recv(socket, 1, flags) assert dummy == "\L" addNLIfEmpty() return diff --git a/lib/pure/net.nim b/lib/pure/net.nim index e34c883275..ddc2bbe2dc 100644 --- a/lib/pure/net.nim +++ b/lib/pure/net.nim @@ -350,6 +350,30 @@ type ETimeout* = object of ESynch + TSocketFlags* {.pure.} = enum + Peek, + SafeDisconn ## Ensures disconnection exceptions (ECONNRESET, EPIPE etc) are not thrown. + +proc isDisconnectionError*(flags: set[TSocketFlags], + lastError: TOSErrorCode): bool = + ## Determines whether ``lastError`` is a disconnection error. Only does this + ## if flags contains ``SafeDisconn``. + when useWinVersion: + TSocketFlags.SafeDisconn in flags and + lastError.int32 in {WSAECONNRESET, WSAECONNABORTED, WSAENETRESET, + WSAEDISCON} + else: + TSocketFlags.SafeDisconn in flags and + lastError.int32 in {ECONNRESET, EPIPE, ENETRESET} + +proc toOSFlags*(socketFlags: set[TSocketFlags]): cint = + ## Converts the flags into the underlying OS representation. + for f in socketFlags: + case f + of TSocketFlags.Peek: + result = result or MSG_PEEK + of TSocketFlags.SafeDisconn: continue + proc createSocket(fd: TSocketHandle, isBuff: bool): PSocket = assert fd != osInvalidSocket new(result) @@ -470,7 +494,8 @@ when defined(ssl): if SSLSetFd(socket.sslHandle, socket.fd) != 1: SSLError() -proc socketError*(socket: PSocket, err: int = -1, async = false) = +proc socketError*(socket: PSocket, err: int = -1, async = false, + lastError = (-1).TOSErrorCode) = ## Raises an EOS error based on the error code returned by ``SSLGetError`` ## (for SSL sockets) and ``osLastError`` otherwise. ## @@ -500,17 +525,17 @@ proc socketError*(socket: PSocket, err: int = -1, async = false) = else: SSLError("Unknown Error") if err == -1 and not (when defined(ssl): socket.isSSL else: false): - let lastError = osLastError() + let lastE = if lastError.int == -1: osLastError() else: lastError if async: when useWinVersion: - if lastError.int32 == WSAEWOULDBLOCK: + if lastE.int32 == WSAEWOULDBLOCK: return - else: osError(lastError) + else: osError(lastE) else: - if lastError.int32 == EAGAIN or lastError.int32 == EWOULDBLOCK: + if lastE.int32 == EAGAIN or lastE.int32 == EWOULDBLOCK: return - else: osError(lastError) - else: osError(lastError) + else: osError(lastE) + else: osError(lastE) proc listen*(socket: PSocket, backlog = SOMAXCONN) {.tags: [FReadIO].} = ## Marks ``socket`` as accepting connections. @@ -881,7 +906,8 @@ proc recv*(socket: PSocket, data: pointer, size: int, timeout: int): int {. result = read -proc recv*(socket: PSocket, data: var string, size: int, timeout = -1): int = +proc recv*(socket: PSocket, data: var string, size: int, timeout = -1, + flags = {TSocketFlags.SafeDisconn}): int = ## Higher-level version of ``recv``. ## ## When 0 is returned the socket's connection has been closed. @@ -893,11 +919,15 @@ proc recv*(socket: PSocket, data: var string, size: int, timeout = -1): int = ## within the time specified an ETimeout exception will be raised. ## ## **Note**: ``data`` must be initialised. + ## + ## **Warning**: Only the ``SafeDisconn`` flag is currently supported. data.setLen(size) result = recv(socket, cstring(data), size, timeout) if result < 0: data.setLen(0) - socket.socketError(result) + let lastError = osLastError() + if flags.isDisconnectionError(lastError): return + socket.socketError(result, lastError = lastError) data.setLen(result) proc peekChar(socket: PSocket, c: var char): int {.tags: [FReadIO].} = @@ -920,7 +950,8 @@ proc peekChar(socket: PSocket, c: var char): int {.tags: [FReadIO].} = return result = recv(socket.fd, addr(c), 1, MSG_PEEK) -proc readLine*(socket: PSocket, line: var TaintedString, timeout = -1) {. +proc readLine*(socket: PSocket, line: var TaintedString, timeout = -1, + flags = {TSocketFlags.SafeDisconn}) {. tags: [FReadIO, FTime].} = ## Reads a line of data from ``socket``. ## @@ -934,11 +965,18 @@ proc readLine*(socket: PSocket, line: var TaintedString, timeout = -1) {. ## ## A timeout can be specified in miliseconds, if data is not received within ## the specified time an ETimeout exception will be raised. + ## + ## **Warning**: Only the ``SafeDisconn`` flag is currently supported. template addNLIfEmpty(): stmt = if line.len == 0: line.add("\c\L") + template raiseSockError(): stmt {.dirty, immediate.} = + let lastError = osLastError() + if flags.isDisconnectionError(lastError): setLen(line.string, 0); return + socket.socketError(n, lastError = lastError) + var waited = 0.0 setLen(line.string, 0) @@ -946,14 +984,14 @@ proc readLine*(socket: PSocket, line: var TaintedString, timeout = -1) {. var c: char discard waitFor(socket, waited, timeout, 1, "readLine") var n = recv(socket, addr(c), 1) - if n < 0: socket.socketError() - elif n == 0: return + if n < 0: raiseSockError() + elif n == 0: setLen(line.string, 0); return if c == '\r': discard waitFor(socket, waited, timeout, 1, "readLine") n = peekChar(socket, c) if n > 0 and c == '\L': discard recv(socket, addr(c), 1) - elif n <= 0: socket.socketError() + elif n <= 0: raiseSockError() addNLIfEmpty() return elif c == '\L': @@ -1021,11 +1059,14 @@ proc send*(socket: PSocket, data: pointer, size: int): int {. const MSG_NOSIGNAL = 0 result = send(socket.fd, data, size, int32(MSG_NOSIGNAL)) -proc send*(socket: PSocket, data: string) {.tags: [FWriteIO].} = +proc send*(socket: PSocket, data: string, + flags = {TSocketFlags.SafeDisconn}) {.tags: [FWriteIO].} = ## sends data to a socket. let sent = send(socket, cstring(data), data.len) if sent < 0: - socketError(socket) + let lastError = osLastError() + if flags.isDisconnectionError(lastError): return + socketError(socket, lastError = lastError) if sent != data.len: raise newException(EOS, "Could not send all data.") diff --git a/lib/pure/rawsockets.nim b/lib/pure/rawsockets.nim index 94189fd89a..d96741846e 100644 --- a/lib/pure/rawsockets.nim +++ b/lib/pure/rawsockets.nim @@ -21,11 +21,12 @@ const useWinVersion = defined(Windows) or defined(nimdoc) when useWinVersion: import winlean - export WSAEWOULDBLOCK + export WSAEWOULDBLOCK, WSAECONNRESET, WSAECONNABORTED, WSAENETRESET, + WSAEDISCON else: import posix export fcntl, F_GETFL, O_NONBLOCK, F_SETFL, EAGAIN, EWOULDBLOCK, MSG_NOSIGNAL, - EINTR, EINPROGRESS + EINTR, EINPROGRESS, ECONNRESET, EPIPE, ENETRESET export TSocketHandle, TSockaddr_in, TAddrinfo, INADDR_ANY, TSockAddr, TSockLen, inet_ntoa, recv, `==`, connect, send, accept, recvfrom, sendto From 21589529177a394ba33f884464746864c90e5771 Mon Sep 17 00:00:00 2001 From: Dominik Picheta Date: Sun, 13 Jul 2014 22:43:52 +0100 Subject: [PATCH 267/270] Linux async fixes. --- lib/pure/asyncdispatch.nim | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/pure/asyncdispatch.nim b/lib/pure/asyncdispatch.nim index 208e838721..6ace947d3e 100644 --- a/lib/pure/asyncdispatch.nim +++ b/lib/pure/asyncdispatch.nim @@ -722,7 +722,7 @@ else: proc cb(sock: TAsyncFD): bool = result = true let res = recv(sock.TSocketHandle, addr readBuffer[0], size.cint, - flags.cint) + flags.toOSFlags()) #echo("recv cb res: ", res) if res < 0: let lastError = osLastError() @@ -760,7 +760,7 @@ else: let lastError = osLastError() if lastError.int32 notin {EINTR, EWOULDBLOCK, EAGAIN}: if flags.isDisconnectionError(lastError): - retFuture.complete("") + retFuture.complete() else: retFuture.fail(newException(EOS, osErrorMsg(lastError))) else: From 089387c525ec7c8f89cff4d1a03d92ccc0e138d3 Mon Sep 17 00:00:00 2001 From: def Date: Mon, 14 Jul 2014 03:49:15 +0200 Subject: [PATCH 268/270] Add missing sql proc for db_mysql --- lib/impure/db_mysql.nim | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/lib/impure/db_mysql.nim b/lib/impure/db_mysql.nim index 32cda3e4dc..eec4daf00d 100644 --- a/lib/impure/db_mysql.nim +++ b/lib/impure/db_mysql.nim @@ -24,6 +24,15 @@ type FReadDb* = object of FDb ## effect that denotes a read operation FWriteDb* = object of FDb ## effect that denotes a write operation +proc sql*(query: string): TSqlQuery {.noSideEffect, inline.} = + ## constructs a TSqlQuery from the string `query`. This is supposed to be + ## used as a raw-string-literal modifier: + ## ``sql"update user set counter = counter + 1"`` + ## + ## If assertions are turned off, it does nothing. If assertions are turned + ## on, later versions will check the string for valid syntax. + result = TSqlQuery(query) + proc dbError(db: TDbConn) {.noreturn.} = ## raises an EDb exception. var e: ref EDb From e01c1f1ab3d9d1d4d1bcd7676b85815aeda6f460 Mon Sep 17 00:00:00 2001 From: Dominik Picheta Date: Mon, 14 Jul 2014 19:53:11 +0100 Subject: [PATCH 269/270] Fixes missing MSG_NOSIGNAL on Mac OS X. --- lib/posix/posix.nim | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/lib/posix/posix.nim b/lib/posix/posix.nim index cdca826ca1..8e66336c2e 100644 --- a/lib/posix/posix.nim +++ b/lib/posix/posix.nim @@ -1578,8 +1578,17 @@ var ## Terminates a record (if supported by the protocol). MSG_OOB* {.importc, header: "".}: cint ## Out-of-band data. - MSG_NOSIGNAL* {.importc, header: "".}: cint - ## No SIGPIPE generated when an attempt to send is made on a stream-oriented socket that is no longer connected. + +when defined(macosx): + var + MSG_HAVEMORE* {.importc, header: "".}: cint + MSG_NOSIGNAL* = MSG_HAVEMORE +else: + var + MSG_NOSIGNAL* {.importc, header: "".}: cint + ## No SIGPIPE generated when an attempt to send is made on a stream-oriented socket that is no longer connected. + +var MSG_PEEK* {.importc, header: "".}: cint ## Leave received data in queue. MSG_TRUNC* {.importc, header: "".}: cint From 41bb0bf9dcccdfcebdb0f823fea8b2853b89ea4e Mon Sep 17 00:00:00 2001 From: Dominik Picheta Date: Mon, 14 Jul 2014 21:00:51 +0100 Subject: [PATCH 270/270] Added debug code for futures. --- lib/pure/asyncdispatch.nim | 22 +++++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) diff --git a/lib/pure/asyncdispatch.nim b/lib/pure/asyncdispatch.nim index 6ace947d3e..d410f8ce16 100644 --- a/lib/pure/asyncdispatch.nim +++ b/lib/pure/asyncdispatch.nim @@ -41,6 +41,7 @@ type cb: proc () {.closure,gcsafe.} finished: bool error*: ref EBase + stackTrace: string ## For debugging purposes only. PFuture*[T] = ref object of PFutureBase value: T @@ -49,10 +50,23 @@ proc newFuture*[T](): PFuture[T] = ## Creates a new future. new(result) result.finished = false + result.stackTrace = getStackTrace() + +proc checkFinished[T](future: PFuture[T]) = + if future.finished: + echo("<----->") + echo(future.stackTrace) + echo("-----") + when T is string: + echo("Contents: ", future.value.repr) + echo("<----->") + echo("Future already finished, cannot finish twice.") + assert false proc complete*[T](future: PFuture[T], val: T) = ## Completes ``future`` with value ``val``. - assert(not future.finished, "Future already finished, cannot finish twice.") + #assert(not future.finished, "Future already finished, cannot finish twice.") + checkFinished(future) assert(future.error == nil) future.value = val future.finished = true @@ -61,7 +75,8 @@ proc complete*[T](future: PFuture[T], val: T) = proc complete*(future: PFuture[void]) = ## Completes a void ``future``. - assert(not future.finished, "Future already finished, cannot finish twice.") + #assert(not future.finished, "Future already finished, cannot finish twice.") + checkFinished(future) assert(future.error == nil) future.finished = true if future.cb != nil: @@ -69,7 +84,8 @@ proc complete*(future: PFuture[void]) = proc fail*[T](future: PFuture[T], error: ref EBase) = ## Completes ``future`` with ``error``. - assert(not future.finished, "Future already finished, cannot finish twice.") + #assert(not future.finished, "Future already finished, cannot finish twice.") + checkFinished(future) future.finished = true future.error = error if future.cb != nil: