From 373717968eeb851e30a300923aa62e4c2aec519d Mon Sep 17 00:00:00 2001 From: krolik Date: Tue, 12 May 2015 12:45:34 +0300 Subject: [PATCH 001/115] new now able to make distinction between ref and non-ref types, so we don't get 'ref ref' type when calling new on ref type --- lib/system.nim | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/lib/system.nim b/lib/system.nim index 1e6f76f3df..a56e915945 100644 --- a/lib/system.nim +++ b/lib/system.nim @@ -172,10 +172,16 @@ proc new*[T](a: var ref T) {.magic: "New", noSideEffect.} ## creates a new object of type ``T`` and returns a safe (traced) ## reference to it in ``a``. -proc new*(T: typedesc): ref T = +proc new*(T: typedesc): auto = ## creates a new object of type ``T`` and returns a safe (traced) ## reference to it as result value - new(result) + when (T is ref): + var r: T + else: + var r: ref T + new(r) + return r + proc internalNew*[T](a: var ref T) {.magic: "New", noSideEffect.} ## leaked implementation detail. Do not use. From 939c0a6bb8c21c036bdb7bc44cf866b947b11696 Mon Sep 17 00:00:00 2001 From: transfuturist Date: Fri, 15 May 2015 21:55:06 -0700 Subject: [PATCH 002/115] NimNode needs an items iterator to be used nicely in for statements --- lib/core/macros.nim | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/lib/core/macros.nim b/lib/core/macros.nim index 35f0f61c17..becfd2d940 100644 --- a/lib/core/macros.nim +++ b/lib/core/macros.nim @@ -712,6 +712,10 @@ iterator children*(n: NimNode): NimNode {.inline.}= for i in 0 .. high(n): yield n[i] +iterator items*(n: NimNode): NimNode {.inline.}= + for i in 0 .. high(n): + yield n[i] + template findChild*(n: NimNode; cond: expr): NimNode {. immediate, dirty.} = ## Find the first child node matching condition (or nil). From 7500eed0cfb56a1fc4812d57ac301ee9b7f50026 Mon Sep 17 00:00:00 2001 From: transfuturist Date: Sat, 16 May 2015 17:26:47 -0700 Subject: [PATCH 003/115] Better solution: deprecation --- lib/core/macros.nim | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/lib/core/macros.nim b/lib/core/macros.nim index becfd2d940..48e4566b3d 100644 --- a/lib/core/macros.nim +++ b/lib/core/macros.nim @@ -708,14 +708,12 @@ proc `$`*(node: NimNode): string {.compileTime.} = proc ident*(name: string): NimNode {.compileTime,inline.} = newIdentNode(name) ## Create a new ident node from a string -iterator children*(n: NimNode): NimNode {.inline.}= - for i in 0 .. high(n): - yield n[i] - iterator items*(n: NimNode): NimNode {.inline.}= for i in 0 .. high(n): yield n[i] +{.deprecated: [children: items].} + template findChild*(n: NimNode; cond: expr): NimNode {. immediate, dirty.} = ## Find the first child node matching condition (or nil). From 3d80fcb8c6ff1e03922f517681efad65d79563fb Mon Sep 17 00:00:00 2001 From: transfuturist Date: Sat, 16 May 2015 17:50:01 -0700 Subject: [PATCH 004/115] Add value check for NimNode items --- lib/core/macros.nim | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/lib/core/macros.nim b/lib/core/macros.nim index 48e4566b3d..41998250e5 100644 --- a/lib/core/macros.nim +++ b/lib/core/macros.nim @@ -708,9 +708,10 @@ proc `$`*(node: NimNode): string {.compileTime.} = proc ident*(name: string): NimNode {.compileTime,inline.} = newIdentNode(name) ## Create a new ident node from a string -iterator items*(n: NimNode): NimNode {.inline.}= - for i in 0 .. high(n): - yield n[i] +iterator items*(n: NimNode): NimNode {.inline.} = + if n[0] != nil: + for i in 0 .. high(n): + yield n[i] {.deprecated: [children: items].} From bc2aaea853d3bd1ad7641bf32335e3725f03afed Mon Sep 17 00:00:00 2001 From: transfuturist Date: Sun, 17 May 2015 15:05:26 -0700 Subject: [PATCH 005/115] Change check for NimNode items to node kind --- lib/core/macros.nim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/core/macros.nim b/lib/core/macros.nim index 41998250e5..0bfdffc5dc 100644 --- a/lib/core/macros.nim +++ b/lib/core/macros.nim @@ -709,7 +709,7 @@ proc ident*(name: string): NimNode {.compileTime,inline.} = newIdentNode(name) ## Create a new ident node from a string iterator items*(n: NimNode): NimNode {.inline.} = - if n[0] != nil: + if n.kind in {nnkType, nnkMetaNode .. nnkReturnToken}: for i in 0 .. high(n): yield n[i] From a31c36e98f7caa330160b55c21d48c06cce03a62 Mon Sep 17 00:00:00 2001 From: transfuturist Date: Sat, 20 Jun 2015 12:21:16 -0700 Subject: [PATCH 006/115] Undeprecate children --- lib/core/macros.nim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/core/macros.nim b/lib/core/macros.nim index 0bfdffc5dc..3fd2c85020 100644 --- a/lib/core/macros.nim +++ b/lib/core/macros.nim @@ -713,7 +713,7 @@ iterator items*(n: NimNode): NimNode {.inline.} = for i in 0 .. high(n): yield n[i] -{.deprecated: [children: items].} +iterator children*(n: NimNode): NimNode {.inline.} = items template findChild*(n: NimNode; cond: expr): NimNode {. immediate, dirty.} = From 558360e6fdf73de27f2bf9631124288c4db4d8fb Mon Sep 17 00:00:00 2001 From: transfuturist Date: Fri, 3 Jul 2015 13:48:13 -0700 Subject: [PATCH 007/115] Update macros.nim --- lib/core/macros.nim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/core/macros.nim b/lib/core/macros.nim index 3fd2c85020..98165dd17f 100644 --- a/lib/core/macros.nim +++ b/lib/core/macros.nim @@ -709,7 +709,7 @@ proc ident*(name: string): NimNode {.compileTime,inline.} = newIdentNode(name) ## Create a new ident node from a string iterator items*(n: NimNode): NimNode {.inline.} = - if n.kind in {nnkType, nnkMetaNode .. nnkReturnToken}: + if n.kind > nnkNilLit: for i in 0 .. high(n): yield n[i] From f09916d72034242a9bc4966ecf6f5590ed40d2cc Mon Sep 17 00:00:00 2001 From: coffeepots Date: Tue, 14 Jul 2015 11:58:26 +0100 Subject: [PATCH 008/115] Procs to decode Time to TimeInfo & TimeInterval Fills in the missing functionality of decoding Time to TimeInfo and TimeInterval, whilst also adding some procs to work with leap years and to get the day of the week based on a date. --- lib/pure/times.nim | 130 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 130 insertions(+) diff --git a/lib/pure/times.nim b/lib/pure/times.nim index e4d3f7494e..efed1bbc05 100644 --- a/lib/pure/times.nim +++ b/lib/pure/times.nim @@ -1039,6 +1039,126 @@ proc parse*(value, layout: string): TimeInfo = info.weekday = getLocalTime(timeInfoToTime(info)).weekday return info +# Leap year calculations are adapted from: +# from http://www.codeproject.com/Articles/7358/Ultra-fast-Algorithms-for-Working-with-Leap-Years +# The dayOfTheWeek procs are adapated from: +# from http://stason.org/TULARC/society/calendars/2-5-What-day-of-the-week-was-2-August-1953.html + +# Note: for leap years, start date is assumed to be 1 AD. +# counts the number of leap years up to January 1st of a given year. +# Keep in mind that if specified year is a leap year, the leap day +# has not happened before January 1st of that year. +proc countLeapYears(yearSpan: int): int = + (((yearSpan - 1) / 4) - ((yearSpan - 1) / 100) + ((yearSpan - 1)/400)).int + +proc countDays(yearSpan: int): int = + (yearSpan - 1) * 365 + countLeapYears(yearSpan) + +# counts the number of years spanned by a given number of days. +proc countYears(daySpan: int): int = + ((daySpan - countLeapYears(daySpan div 365)) div 365) + +proc countYearsAndDays(daySpan: int): tuple[years: int, days: int] = + let days = daySpan - countLeapYears(daySpan div 365) + result.years = days div 365 + result.days = days mod 365 + +type + SecondScale = enum ssMinute, ssHour, ssDay, ssMonth, ssYear + +# these are very approximate beyond day +const secondsIn*: array[SecondScale.low..SecondScale.high, int] =[ + 60, # minute + 60*60, # hour + 60*60*24, # day + 60*60*24*30, # month (estimate) + 60*60*24*30*12] # year (estimate) + +const + epochStartYear = 1970 + leapYearsSinceEpoch* = countLeapYears(epochStartYear) + +proc dayOfWeek*(day, month, year: int): WeekDay = + # This is for the Gregorian calendar + # Day & month start from one. + let + a = (14 - month) div 12 + y = year - a + m = month + (12*a) - 2 + d = (day + y + (y div 4) - (y div 100) + (y div 400) + (31*m) div 12) mod 7 + # The value of d is 0 for a Sunday, 1 for a Monday, 2 for a Tuesday, etc. so we must correct + # for the WeekDay type. + if d == 0: return dSun + result = (d-1).WeekDay + +proc dayOfWeekJulian*(day, month, year: int): WeekDay = + # This is for the Julian calendar + # Day & month start from one. + let + a = (14 - month) div 12 + y = year - a + m = month + (12*a) - 2 + d = (5 + day + y + (y div 4) + (31*m) div 12) mod 7 + # The value of d is 0 for a Sunday, 1 for a Monday, 2 for a Tuesday, etc. so we must correct + # for the WeekDay type. + if d == 0: return dSun + result = (d-1).WeekDay + +proc decodeTime*(t: Time): TimeInfo = + let + daysSinceEpoch = t.int div secondsIn[ssDay] + (yearsSinceEpoch, daysRemaining) = countYearsAndDays(daysSinceEpoch) + daySeconds = t.int mod secondsIn[ssDay] + + y = yearsSinceEpoch + epochStartYear + + var + mon = mJan + days = daysRemaining + daysInMonth = getDaysInMonth(mon, y) + + # calculate month and day remainder + while days > daysInMonth and mon <= mDec: + days -= daysInMonth + mon.inc + daysInMonth = getDaysInMonth(mon, y) + + let + yd = daysRemaining + m = mon # month is zero indexed enum + md = days + # NB: month is zero indexed but dayOfWeek expects 1 indexed. + wd = dayOfWeek(days, mon.int + 1, y).Weekday + h = daySeconds div secondsIn[ssHour] + 1 + mi = (daySeconds mod secondsIn[ssHour]) div secondsIn[ssMinute] + s = daySeconds mod secondsIn[ssMinute] + result = TimeInfo(year: y, yearday: yd, month: m, monthday: md, weekday: wd, hour: h, minute: mi, second: s) + +proc decodeTimeInterval*(t: Time): TimeInterval = + var + daysSinceEpoch = t.int div secondsIn[ssDay] + (yearsSinceEpoch, daysRemaining) = countYearsAndDays(daysSinceEpoch) + daySeconds = t.int mod secondsIn[ssDay] + + result.years = yearsSinceEpoch + epochStartYear + + var + mon = mJan + days = daysRemaining + daysInMonth = getDaysInMonth(mon, result.years) + + # calculate month and day remainder + while days > daysInMonth and mon <= mDec: + days -= daysInMonth + mon.inc + daysInMonth = getDaysInMonth(mon, result.years) + + result.months = mon.int + 1 # month is 1 indexed int + result.days = days + result.hours = daySeconds div secondsIn[ssHour] + 1 + result.minutes = (daySeconds mod secondsIn[ssHour]) div secondsIn[ssMinute] + result.seconds = daySeconds mod secondsIn[ssMinute] + # Milliseconds not available from Time when isMainModule: # $ date --date='@2147483647' @@ -1122,3 +1242,13 @@ when isMainModule: assert "15:04:00" in $s.parse(f) when not defined(testing): echo "Kitchen: " & $s.parse(f) + var ti = decodeTime(getTime()) + echo "Todays date after decoding: ", ti + var tint = decodeTimeInterval(getTime()) + echo "Todays date after decoding to interval: ", tint + # checking dayOfWeek matches known days + assert dayOfWeek(21, 9, 1900) == dFri + assert dayOfWeek(1, 1, 1970) == dThu + assert dayOfWeek(21, 9, 1970) == dMon + assert dayOfWeek(1, 1, 2000) == dSat + assert dayOfWeek(1, 1, 2021) == dFri From 39dd16b5ef0761fee0b9586a21b12e991c50e26e Mon Sep 17 00:00:00 2001 From: Jonathan Edwards Date: Tue, 14 Jul 2015 17:52:50 -0400 Subject: [PATCH 009/115] Added Documentation Style Guide; Linked from contributing.rst --- contributing.rst | 10 ++-- styleguide.rst | 140 +++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 146 insertions(+), 4 deletions(-) create mode 100644 styleguide.rst diff --git a/contributing.rst b/contributing.rst index 68b706c735..3b495f01fb 100644 --- a/contributing.rst +++ b/contributing.rst @@ -142,7 +142,7 @@ When contributing new procedures, be sure to add documentation, especially if the procedure is exported from the module. Documentation begins on the line following the ``proc`` definition, and is prefixed by ``##`` on each line. -Code examples are also encouraged. The RestructuredText Nim uses has a special +Code examples are also encouraged. The RestructuredText Nim uses has a special syntax for including examples. .. code-block:: nim @@ -155,8 +155,8 @@ syntax for including examples. ## echo someproc() # "something" result = "something" # single-hash comments do not produce documentation -The ``.. code-block:: nim`` followed by a newline and an indentation instructs the -``nim doc`` and ``nim doc2`` commands to produce syntax-highlighted example code with +The ``.. code-block:: nim`` followed by a newline and an indentation instructs the +``nim doc`` and ``nim doc2`` commands to produce syntax-highlighted example code with the documentation. When forward declaration is used, the documentation should be included with the @@ -186,7 +186,7 @@ or proc hello*(): string = # says hello result = "hello" - + the first is preferred. The Git stuff @@ -216,3 +216,5 @@ General commit rules git diff --check --cached || exit $? 3. Describe your commit and use your common sense. + +.. include:: styleguide.rst diff --git a/styleguide.rst b/styleguide.rst new file mode 100644 index 0000000000..f8fec910c1 --- /dev/null +++ b/styleguide.rst @@ -0,0 +1,140 @@ +Style Guide (Documentation) +=========================== + +General Guidelines +------------------ + +* Authors should document anything that is exported. +* Within a documentation for a procedure, a period (`.`) should follow each sentence in a comment block if there is more than one sentence in that block; otherwise, no period should be used. In other sections, complete sentences should have periods. Sentence fragments should have none. +* Documentation is parsed as RST (RestructuredText). +* Inline code should be surrounded by double tick marks (``` `` ```). With ReStructuredTest (RST), if you would like a character to immediately follow inline code (e.g., "``int8``s are great!"), escape the following character with a backslash (`\`). The preceding is typed as ``` ``int8``\s are great!```. + +Module-level documentation +-------------------------- + +Documentation of a module is placed at the top of the module itself. Each line of documentation begins with double hashes (`##`). +Code samples are encouraged, and should follow the general RST syntax: + +```nim +## The ``universe`` module computes the answer to life, the universe, and everything. +## +## .. code-block:: nim +## echo computeAnswerString*() # "42" +``` + +Within this top-level comment, you can indicate the authorship and copyright of the code, which will be featured in the produced documentation. + +```nim +## This is the best module ever. It provides answers to everything! +## +## :Author: Steve McQueen +## :Copyright: 1965 +## +``` + +Users are encouraged to leave a space between the last line of top-level documentation and the beginning of Nim code (the imports, etc.). + +Procs, Templates, Macros, Converters, and Iterators +--------------------------------------------------- + +The documentation of a procedure should begin with a capital letter and should be in present tense. Variables referenced in the documentation should be surrounded by double tick marks (``` `` ```). + +```nim + proc example1*(x: int) = + ## Prints the value of ``x`` + echo x +``` + +Whenever an example of usage would be helpful to the user, please include a sample within the documentation in RST format as below. + +```nim + proc addThree*(x, y, z: int8): int = + ## Adds three ``int8`` values, treating them as unsigned and + ## truncating the result + ## + ## .. code-block:: nim + ## echo addThree(3, 125, 6) # -122 + result = x +% y +% z +``` + +The commands ``nim doc`` and ``nim doc2`` will then correctly syntax highlight the Nim code within the documentation. + +Types +----- + +Types should also be documented. This documentation can also contain code samples, but those are likely better placed with the functions to which they refer. + +```nim +type + NamedQueue*[T] = object ## Provides a linked data structure with names + ## throughout. Named for convenience. I'm making + ## this comment long to show how you can, too. + name*: string ## The name of the item + val*: T ## Its value + next*: ref NamedQueue[T] ## The next item in the queue +``` + +You have some flexibility when placing the documentation: +```nim +type + NamedQueue*[T] = object + ## Provides a linked data structure with names + ## throughout. Named for convenience. I'm making + ## this comment long to show how you can, too. + name*: string ## The name of the item + val*: T ## Its value + next*: ref NamedQueue[T] ## The next item in the queue +``` + +Make sure to place the documentation beside or within the object. + +```nim +type + ## This documentation disappears. It's basically annotating the + ## ``type`` above. + NamedQueue*[T] = object + name*: string ## This becomes the main documentation for the object, which + ## is not what we want. + val*: T ## Its value + next*: ref NamedQueue[T] ## The next item in the queue + +``` + +Var, Let, and Const +------------------- + +When declaring module-wide constants and values, documentation is encouraged. The placement of doc comments is similar to the ``type`` sections. + +```nim +const + X* = 42 ## An awesome number + SpreadArray* = [ + [1,2,3], + [2,3,1], + [3,1,2], + ] ## Doc comment for ``SpreadArray`` +``` + +Placement of comments in other areas is usually allowed, but will not become part of the documentation output. + +```nim +const + BadMathVals* = [ + 3.14, ## pi + 2.72, ## e + 0.58, ## gamma + ] ## A bunch of badly rounded values +``` + +In the produced documentation, the comments beside the elements of ``BadMathVals`` do not make it into the final documentation. + +As a final note, Nim supports Unicode in comments just fine, so the above can be replaced with the following: + +```nim +const + BadMathVals* = [ + 3.14, ## π + 2.72, ## e + 0.58, ## γ + ] ## A bunch of badly rounded values (including π!) +``` From 6a490e95e4a0a173fdfe2da05e8025b21e236ae2 Mon Sep 17 00:00:00 2001 From: Jonathan Edwards Date: Tue, 14 Jul 2015 18:24:02 -0400 Subject: [PATCH 010/115] Corrected guide; polished it --- styleguide.rst | 36 +++++++++++++++++------------------- 1 file changed, 17 insertions(+), 19 deletions(-) diff --git a/styleguide.rst b/styleguide.rst index f8fec910c1..c6e04798b0 100644 --- a/styleguide.rst +++ b/styleguide.rst @@ -5,9 +5,9 @@ General Guidelines ------------------ * Authors should document anything that is exported. -* Within a documentation for a procedure, a period (`.`) should follow each sentence in a comment block if there is more than one sentence in that block; otherwise, no period should be used. In other sections, complete sentences should have periods. Sentence fragments should have none. -* Documentation is parsed as RST (RestructuredText). -* Inline code should be surrounded by double tick marks (``` `` ```). With ReStructuredTest (RST), if you would like a character to immediately follow inline code (e.g., "``int8``s are great!"), escape the following character with a backslash (`\`). The preceding is typed as ``` ``int8``\s are great!```. +* Within documentation for a procedure, a period (`.`) should follow each sentence in a comment block if there is more than one sentence in that block; otherwise, no period should be used. In other sections, complete sentences should have periods. Sentence fragments should have none. +* Documentation is parsed as ReStructuredText (RST). +* Inline code should be surrounded by double tick marks (``` `` ```). If you would like a character to immediately follow inline code (e.g., "``int8``s are great!"), escape the following character with a backslash (`\`). The preceding is typed as ``` ``int8``\s are great!```. Module-level documentation -------------------------- @@ -18,7 +18,7 @@ Code samples are encouraged, and should follow the general RST syntax: ```nim ## The ``universe`` module computes the answer to life, the universe, and everything. ## -## .. code-block:: nim +## .. code-block:: Nim ## echo computeAnswerString*() # "42" ``` @@ -32,7 +32,7 @@ Within this top-level comment, you can indicate the authorship and copyright of ## ``` -Users are encouraged to leave a space between the last line of top-level documentation and the beginning of Nim code (the imports, etc.). +Leave a space between the last line of top-level documentation and the beginning of Nim code (the imports, etc.). Procs, Templates, Macros, Converters, and Iterators --------------------------------------------------- @@ -45,7 +45,7 @@ The documentation of a procedure should begin with a capital letter and should b echo x ``` -Whenever an example of usage would be helpful to the user, please include a sample within the documentation in RST format as below. +Whenever an example of usage would be helpful to the user, you should include one within the documentation in RST format as below. ```nim proc addThree*(x, y, z: int8): int = @@ -62,7 +62,7 @@ The commands ``nim doc`` and ``nim doc2`` will then correctly syntax highlight t Types ----- -Types should also be documented. This documentation can also contain code samples, but those are likely better placed with the functions to which they refer. +Exported types should also be documented. This documentation can also contain code samples, but those are better placed with the functions to which they refer. ```nim type @@ -90,8 +90,8 @@ Make sure to place the documentation beside or within the object. ```nim type - ## This documentation disappears. It's basically annotating the - ## ``type`` above. + ## This documentation disappears because it annotates the ``type`` keyword + ## above, not ``NamedQueue``. NamedQueue*[T] = object name*: string ## This becomes the main documentation for the object, which ## is not what we want. @@ -115,26 +115,24 @@ const ] ## Doc comment for ``SpreadArray`` ``` -Placement of comments in other areas is usually allowed, but will not become part of the documentation output. +Placement of comments in other areas is usually allowed, but will not become part of the documentation output and should therefore be prefaced by a single hash (``#``). ```nim const BadMathVals* = [ - 3.14, ## pi - 2.72, ## e - 0.58, ## gamma + 3.14, # pi + 2.72, # e + 0.58, # gamma ] ## A bunch of badly rounded values ``` -In the produced documentation, the comments beside the elements of ``BadMathVals`` do not make it into the final documentation. - -As a final note, Nim supports Unicode in comments just fine, so the above can be replaced with the following: +Nim supports Unicode in comments, so the above can be replaced with the following: ```nim const BadMathVals* = [ - 3.14, ## π - 2.72, ## e - 0.58, ## γ + 3.14, # π + 2.72, # e + 0.58, # γ ] ## A bunch of badly rounded values (including π!) ``` From a98f609ae215d1fcf9922329b5d87bd1b9242800 Mon Sep 17 00:00:00 2001 From: Jonathan Edwards Date: Wed, 15 Jul 2015 16:40:13 -0400 Subject: [PATCH 011/115] Renamed file; made RST --- docstyle.rst | 140 +++++++++++++++++++++++++++++++++++++++++++++++++ styleguide.rst | 138 ------------------------------------------------ 2 files changed, 140 insertions(+), 138 deletions(-) create mode 100644 docstyle.rst delete mode 100644 styleguide.rst diff --git a/docstyle.rst b/docstyle.rst new file mode 100644 index 0000000000..d4443027b7 --- /dev/null +++ b/docstyle.rst @@ -0,0 +1,140 @@ +Documentation Style +=================== + +General Guidelines +------------------ + +* Authors should document anything that is exported. +* Within documentation for a procedure, a period (`.`) should follow each sentence in a comment block if there is more than one sentence in that block; otherwise, no period should be used. In other sections, complete sentences should have periods. Sentence fragments should have none. +* Documentation is parsed as ReStructuredText (RST). +* Inline code should be surrounded by double tick marks ("``` `` ```"). If you would like a character to immediately follow inline code (e.g., "``int8``s are great!"), escape the following character with a backslash (``\``). The preceding is typed as ``` ``int8``\s are great!```. + +Module-level documentation +-------------------------- + +Documentation of a module is placed at the top of the module itself. Each line of documentation begins with double hashes (``##``). +Code samples are encouraged, and should follow the general RST syntax: + +.. code-block:: Nim + + ## The ``universe`` module computes the answer to life, the universe, and everything. + ## + ## .. code-block:: Nim + ## echo computeAnswerString*() # "42" + + +Within this top-level comment, you can indicate the authorship and copyright of the code, which will be featured in the produced documentation. + +.. code-block:: Nim + + ## This is the best module ever. It provides answers to everything! + ## + ## :Author: Steve McQueen + ## :Copyright: 1965 + ## + +Leave a space between the last line of top-level documentation and the beginning of Nim code (the imports, etc.). + +Procs, Templates, Macros, Converters, and Iterators +--------------------------------------------------- + +The documentation of a procedure should begin with a capital letter and should be in present tense. Variables referenced in the documentation should be surrounded by double tick marks (``` `` ```). + +.. code-block:: Nim + + proc example1*(x: int) = + ## Prints the value of ``x`` + echo x + +Whenever an example of usage would be helpful to the user, you should include one within the documentation in RST format as below. + +.. code-block:: Nim + + proc addThree*(x, y, z: int8): int = + ## Adds three ``int8`` values, treating them as unsigned and + ## truncating the result + ## + ## .. code-block:: nim + ## echo addThree(3, 125, 6) # -122 + result = x +% y +% z + +The commands ``nim doc`` and ``nim doc2`` will then correctly syntax highlight the Nim code within the documentation. + +Types +----- + +Exported types should also be documented. This documentation can also contain code samples, but those are better placed with the functions to which they refer. + +.. code-block:: Nim + + type + NamedQueue*[T] = object ## Provides a linked data structure with names + ## throughout. Named for convenience. I'm making + ## this comment long to show how you can, too. + name*: string ## The name of the item + val*: T ## Its value + next*: ref NamedQueue[T] ## The next item in the queue + + +You have some flexibility when placing the documentation: + +.. code-block:: Nim + + type + NamedQueue*[T] = object + ## Provides a linked data structure with names + ## throughout. Named for convenience. I'm making + ## this comment long to show how you can, too. + name*: string ## The name of the item + val*: T ## Its value + next*: ref NamedQueue[T] ## The next item in the queue + +Make sure to place the documentation beside or within the object. + +.. code-block:: Nim + + type + ## This documentation disappears because it annotates the ``type`` keyword + ## above, not ``NamedQueue``. + NamedQueue*[T] = object + name*: string ## This becomes the main documentation for the object, which + ## is not what we want. + val*: T ## Its value + next*: ref NamedQueue[T] ## The next item in the queue + +Var, Let, and Const +------------------- + +When declaring module-wide constants and values, documentation is encouraged. The placement of doc comments is similar to the ``type`` sections. + +.. code-block:: Nim + + const + X* = 42 ## An awesome number + SpreadArray* = [ + [1,2,3], + [2,3,1], + [3,1,2], + ] ## Doc comment for ``SpreadArray`` + +Placement of comments in other areas is usually allowed, but will not become part of the documentation output and should therefore be prefaced by a single hash (``#``). + +.. code-block:: Nim + + const + BadMathVals* = [ + 3.14, # pi + 2.72, # e + 0.58, # gamma + ] ## A bunch of badly rounded values + +Nim supports Unicode in comments, so the above can be replaced with the following: + +.. code-block:: Nim + + const + BadMathVals* = [ + 3.14, # π + 2.72, # e + 0.58, # γ + ] ## A bunch of badly rounded values (including π!) diff --git a/styleguide.rst b/styleguide.rst deleted file mode 100644 index c6e04798b0..0000000000 --- a/styleguide.rst +++ /dev/null @@ -1,138 +0,0 @@ -Style Guide (Documentation) -=========================== - -General Guidelines ------------------- - -* Authors should document anything that is exported. -* Within documentation for a procedure, a period (`.`) should follow each sentence in a comment block if there is more than one sentence in that block; otherwise, no period should be used. In other sections, complete sentences should have periods. Sentence fragments should have none. -* Documentation is parsed as ReStructuredText (RST). -* Inline code should be surrounded by double tick marks (``` `` ```). If you would like a character to immediately follow inline code (e.g., "``int8``s are great!"), escape the following character with a backslash (`\`). The preceding is typed as ``` ``int8``\s are great!```. - -Module-level documentation --------------------------- - -Documentation of a module is placed at the top of the module itself. Each line of documentation begins with double hashes (`##`). -Code samples are encouraged, and should follow the general RST syntax: - -```nim -## The ``universe`` module computes the answer to life, the universe, and everything. -## -## .. code-block:: Nim -## echo computeAnswerString*() # "42" -``` - -Within this top-level comment, you can indicate the authorship and copyright of the code, which will be featured in the produced documentation. - -```nim -## This is the best module ever. It provides answers to everything! -## -## :Author: Steve McQueen -## :Copyright: 1965 -## -``` - -Leave a space between the last line of top-level documentation and the beginning of Nim code (the imports, etc.). - -Procs, Templates, Macros, Converters, and Iterators ---------------------------------------------------- - -The documentation of a procedure should begin with a capital letter and should be in present tense. Variables referenced in the documentation should be surrounded by double tick marks (``` `` ```). - -```nim - proc example1*(x: int) = - ## Prints the value of ``x`` - echo x -``` - -Whenever an example of usage would be helpful to the user, you should include one within the documentation in RST format as below. - -```nim - proc addThree*(x, y, z: int8): int = - ## Adds three ``int8`` values, treating them as unsigned and - ## truncating the result - ## - ## .. code-block:: nim - ## echo addThree(3, 125, 6) # -122 - result = x +% y +% z -``` - -The commands ``nim doc`` and ``nim doc2`` will then correctly syntax highlight the Nim code within the documentation. - -Types ------ - -Exported types should also be documented. This documentation can also contain code samples, but those are better placed with the functions to which they refer. - -```nim -type - NamedQueue*[T] = object ## Provides a linked data structure with names - ## throughout. Named for convenience. I'm making - ## this comment long to show how you can, too. - name*: string ## The name of the item - val*: T ## Its value - next*: ref NamedQueue[T] ## The next item in the queue -``` - -You have some flexibility when placing the documentation: -```nim -type - NamedQueue*[T] = object - ## Provides a linked data structure with names - ## throughout. Named for convenience. I'm making - ## this comment long to show how you can, too. - name*: string ## The name of the item - val*: T ## Its value - next*: ref NamedQueue[T] ## The next item in the queue -``` - -Make sure to place the documentation beside or within the object. - -```nim -type - ## This documentation disappears because it annotates the ``type`` keyword - ## above, not ``NamedQueue``. - NamedQueue*[T] = object - name*: string ## This becomes the main documentation for the object, which - ## is not what we want. - val*: T ## Its value - next*: ref NamedQueue[T] ## The next item in the queue - -``` - -Var, Let, and Const -------------------- - -When declaring module-wide constants and values, documentation is encouraged. The placement of doc comments is similar to the ``type`` sections. - -```nim -const - X* = 42 ## An awesome number - SpreadArray* = [ - [1,2,3], - [2,3,1], - [3,1,2], - ] ## Doc comment for ``SpreadArray`` -``` - -Placement of comments in other areas is usually allowed, but will not become part of the documentation output and should therefore be prefaced by a single hash (``#``). - -```nim -const - BadMathVals* = [ - 3.14, # pi - 2.72, # e - 0.58, # gamma - ] ## A bunch of badly rounded values -``` - -Nim supports Unicode in comments, so the above can be replaced with the following: - -```nim -const - BadMathVals* = [ - 3.14, # π - 2.72, # e - 0.58, # γ - ] ## A bunch of badly rounded values (including π!) -``` From a2f0fe03b634753fb26c6da899fcdefbb8e46035 Mon Sep 17 00:00:00 2001 From: coffeepots Date: Thu, 16 Jul 2015 10:20:34 +0100 Subject: [PATCH 012/115] Fixed dayOfWeekJulian, exported SecondScale --- lib/pure/times.nim | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/lib/pure/times.nim b/lib/pure/times.nim index efed1bbc05..a85998cb5c 100644 --- a/lib/pure/times.nim +++ b/lib/pure/times.nim @@ -1064,7 +1064,7 @@ proc countYearsAndDays(daySpan: int): tuple[years: int, days: int] = result.days = days mod 365 type - SecondScale = enum ssMinute, ssHour, ssDay, ssMonth, ssYear + SecondScale* = enum ssMinute, ssHour, ssDay, ssMonth, ssYear # these are very approximate beyond day const secondsIn*: array[SecondScale.low..SecondScale.high, int] =[ @@ -1099,10 +1099,7 @@ proc dayOfWeekJulian*(day, month, year: int): WeekDay = y = year - a m = month + (12*a) - 2 d = (5 + day + y + (y div 4) + (31*m) div 12) mod 7 - # The value of d is 0 for a Sunday, 1 for a Monday, 2 for a Tuesday, etc. so we must correct - # for the WeekDay type. - if d == 0: return dSun - result = (d-1).WeekDay + result = d.WeekDay proc decodeTime*(t: Time): TimeInfo = let @@ -1252,3 +1249,8 @@ when isMainModule: assert dayOfWeek(21, 9, 1970) == dMon assert dayOfWeek(1, 1, 2000) == dSat assert dayOfWeek(1, 1, 2021) == dFri + # Julian tests + assert dayOfWeekJulian(21, 9, 1900) == dFri + assert dayOfWeekJulian(21, 9, 1970) == dMon + assert dayOfWeekJulian(1, 1, 2000) == dSat + assert dayOfWeekJulian(1, 1, 2021) == dFri From 1670bf710fedfa59d0ca7514d6e5e2448ef00850 Mon Sep 17 00:00:00 2001 From: apense Date: Thu, 16 Jul 2015 20:44:54 -0400 Subject: [PATCH 013/115] Removed erroneous star --- docstyle.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docstyle.rst b/docstyle.rst index d4443027b7..5f7cf03d5c 100644 --- a/docstyle.rst +++ b/docstyle.rst @@ -20,7 +20,7 @@ Code samples are encouraged, and should follow the general RST syntax: ## The ``universe`` module computes the answer to life, the universe, and everything. ## ## .. code-block:: Nim - ## echo computeAnswerString*() # "42" + ## echo computeAnswerString() # "42" Within this top-level comment, you can indicate the authorship and copyright of the code, which will be featured in the produced documentation. From c701ed3c99009992a111a6fed7fa74c2c0c8a119 Mon Sep 17 00:00:00 2001 From: apense Date: Thu, 16 Jul 2015 22:21:45 -0400 Subject: [PATCH 014/115] Changed period requirements --- docstyle.rst | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docstyle.rst b/docstyle.rst index 5f7cf03d5c..38ed287340 100644 --- a/docstyle.rst +++ b/docstyle.rst @@ -5,7 +5,7 @@ General Guidelines ------------------ * Authors should document anything that is exported. -* Within documentation for a procedure, a period (`.`) should follow each sentence in a comment block if there is more than one sentence in that block; otherwise, no period should be used. In other sections, complete sentences should have periods. Sentence fragments should have none. +* Within documentation for a procedure, a period (`.`) should follow each sentence (or sentence fragment) in a comment block. The documentation may be limited to one sentence fragment, but if multiple sentences are within the procedure documentation, each should be complete and in present tense. * Documentation is parsed as ReStructuredText (RST). * Inline code should be surrounded by double tick marks ("``` `` ```"). If you would like a character to immediately follow inline code (e.g., "``int8``s are great!"), escape the following character with a backslash (``\``). The preceding is typed as ``` ``int8``\s are great!```. @@ -43,7 +43,7 @@ The documentation of a procedure should begin with a capital letter and should b .. code-block:: Nim proc example1*(x: int) = - ## Prints the value of ``x`` + ## Prints the value of ``x``. echo x Whenever an example of usage would be helpful to the user, you should include one within the documentation in RST format as below. @@ -52,7 +52,7 @@ Whenever an example of usage would be helpful to the user, you should include on proc addThree*(x, y, z: int8): int = ## Adds three ``int8`` values, treating them as unsigned and - ## truncating the result + ## truncating the result. ## ## .. code-block:: nim ## echo addThree(3, 125, 6) # -122 From a48cbfe5639466a44fb679fca8a1269a0a1d58b4 Mon Sep 17 00:00:00 2001 From: apense Date: Thu, 16 Jul 2015 22:32:40 -0400 Subject: [PATCH 015/115] Periods for everyone --- docstyle.rst | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/docstyle.rst b/docstyle.rst index 38ed287340..6b0e188fa4 100644 --- a/docstyle.rst +++ b/docstyle.rst @@ -5,7 +5,7 @@ General Guidelines ------------------ * Authors should document anything that is exported. -* Within documentation for a procedure, a period (`.`) should follow each sentence (or sentence fragment) in a comment block. The documentation may be limited to one sentence fragment, but if multiple sentences are within the procedure documentation, each should be complete and in present tense. +* Within documentation, a period (`.`) should follow each sentence (or sentence fragment) in a comment block. The documentation may be limited to one sentence fragment, but if multiple sentences are within the documentation, each sentence after the first should be complete and in present tense. * Documentation is parsed as ReStructuredText (RST). * Inline code should be surrounded by double tick marks ("``` `` ```"). If you would like a character to immediately follow inline code (e.g., "``int8``s are great!"), escape the following character with a backslash (``\``). The preceding is typed as ``` ``int8``\s are great!```. @@ -69,7 +69,7 @@ Exported types should also be documented. This documentation can also contain co type NamedQueue*[T] = object ## Provides a linked data structure with names - ## throughout. Named for convenience. I'm making + ## throughout. It is named for convenience. I'm making ## this comment long to show how you can, too. name*: string ## The name of the item val*: T ## Its value @@ -83,7 +83,7 @@ You have some flexibility when placing the documentation: type NamedQueue*[T] = object ## Provides a linked data structure with names - ## throughout. Named for convenience. I'm making + ## throughout. It is named for convenience. I'm making ## this comment long to show how you can, too. name*: string ## The name of the item val*: T ## Its value @@ -110,12 +110,12 @@ When declaring module-wide constants and values, documentation is encouraged. Th .. code-block:: Nim const - X* = 42 ## An awesome number + X* = 42 ## An awesome number. SpreadArray* = [ [1,2,3], [2,3,1], [3,1,2], - ] ## Doc comment for ``SpreadArray`` + ] ## Doc comment for ``SpreadArray``. Placement of comments in other areas is usually allowed, but will not become part of the documentation output and should therefore be prefaced by a single hash (``#``). @@ -126,7 +126,7 @@ Placement of comments in other areas is usually allowed, but will not become par 3.14, # pi 2.72, # e 0.58, # gamma - ] ## A bunch of badly rounded values + ] ## A bunch of badly rounded values. Nim supports Unicode in comments, so the above can be replaced with the following: @@ -137,4 +137,4 @@ Nim supports Unicode in comments, so the above can be replaced with the followin 3.14, # π 2.72, # e 0.58, # γ - ] ## A bunch of badly rounded values (including π!) + ] ## A bunch of badly rounded values (including π!). From a291f2c2718091e34dbe4a94db1753ea61aed2eb Mon Sep 17 00:00:00 2001 From: Zach Aysan Date: Fri, 17 Jul 2015 16:25:58 -0400 Subject: [PATCH 016/115] Create Ruby-like chomp proc to allow for easy string ending removal --- lib/pure/strutils.nim | 47 ++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 46 insertions(+), 1 deletion(-) diff --git a/lib/pure/strutils.nim b/lib/pure/strutils.nim index 93fcf4d3dc..a0fa7a2370 100644 --- a/lib/pure/strutils.nim +++ b/lib/pure/strutils.nim @@ -168,9 +168,54 @@ proc cmpIgnoreStyle*(a, b: string): int {.noSideEffect, inc(i) inc(j) +proc chomp*(s: string, chars: set[char] = Newlines): string {.noSideEffect, rtl.} = + var last = len(s) - 1 + + if chars == Newlines: + if s[last] == '\10': + last -= 1 + + if s[last] == '\13': + last -= 1 + + else: + if s[last] in chars: + last -= 1 + + return s[0..last] + +proc chomp*(s: string, c: char): string {.noSideEffect, rtl.} = + chomp(s, chars = {c}) + +proc chomp*(s: string, suffix: string): string {.noSideEffect, rtl.} = + var last = len(s) - 1 + + if suffix == "": + var previous: char + while last > 0: + + if s[last] == '\13' and previous == '\13': + break + + elif s[last] == '\13': + last -= 1 + previous = '\13' + + elif s[last] == '\10': + last -= 1 + previous = '\10' + + else: + break + + if s.endsWith(suffix): + last -= len(suffix) + + return s[0..last] + {.pop.} -proc strip*(s: string, leading = true, trailing = true, chars: set[char] = Whitespace): string +proc strip*(s: string, leading = true, trailing = true, chars: set[char] = Whitespace): string {.noSideEffect, rtl, extern: "nsuStrip".} = ## Strips `chars` from `s` and returns the resulting string. ## From d28862422be912b5f3e0cbcaf05f3712376624af Mon Sep 17 00:00:00 2001 From: Zach Aysan Date: Fri, 17 Jul 2015 16:26:10 -0400 Subject: [PATCH 017/115] Add tests for chomp --- tests/stdlib/tstrutil.nim | 25 +++++++++++++++++++++---- 1 file changed, 21 insertions(+), 4 deletions(-) diff --git a/tests/stdlib/tstrutil.nim b/tests/stdlib/tstrutil.nim index 3db484faa9..d807cd11ca 100644 --- a/tests/stdlib/tstrutil.nim +++ b/tests/stdlib/tstrutil.nim @@ -10,12 +10,29 @@ import proc testStrip() = write(stdout, strip(" ha ")) -proc main() = +proc testChomp = + assert "hello".chomp == "hello" + assert "hello\n".chomp == "hello" + assert "hello\r\n".chomp == "hello" + assert "hello\n\r".chomp == "hello\n" + assert "hello\n\n".chomp == "hello\n" + assert "hello\r".chomp == "hello" + assert "hello \n there".chomp == "hello \n there" + assert "hello".chomp("llo") == "he" + assert "hellos".chomp('s') == "hello" + assert "hellos".chomp({'s','z'}) == "hello" + assert "hellos".chomp({'o','s'}) == "hello" + assert "hello\r\n\r\n".chomp("") == "hello" + assert "hello\r\n\r\r\n".chomp("") == "hello\r\n\r" + assert "hello\n\n\n\n\n".chomp("") == "hello" + +proc main() = testStrip() + testChomp() for p in split("/home/a1:xyz:/usr/bin", {':'}): write(stdout, p) -proc testDelete = +proc testDelete = var s = "0123456789ABCDEFGH" delete(s, 4, 5) assert s == "01236789ABCDEFGH" @@ -24,8 +41,8 @@ proc testDelete = delete(s, 0, 0) assert s == "1236789ABCDEFG" -testDelete() - +testDelete() + assert(insertSep($1000_000) == "1_000_000") assert(insertSep($232) == "232") assert(insertSep($12345, ',') == "12,345") From 94149f7a48e1fc2730769ee181bbd09942051c9b Mon Sep 17 00:00:00 2001 From: Zach Aysan Date: Tue, 21 Jul 2015 15:04:10 -0400 Subject: [PATCH 018/115] Update tests to proposed changes --- tests/stdlib/tstrutil.nim | 55 +++++++++++++++++++++++++++------------ 1 file changed, 39 insertions(+), 16 deletions(-) diff --git a/tests/stdlib/tstrutil.nim b/tests/stdlib/tstrutil.nim index d807cd11ca..b15bf0e688 100644 --- a/tests/stdlib/tstrutil.nim +++ b/tests/stdlib/tstrutil.nim @@ -10,25 +10,48 @@ import proc testStrip() = write(stdout, strip(" ha ")) -proc testChomp = - assert "hello".chomp == "hello" - assert "hello\n".chomp == "hello" - assert "hello\r\n".chomp == "hello" - assert "hello\n\r".chomp == "hello\n" - assert "hello\n\n".chomp == "hello\n" - assert "hello\r".chomp == "hello" - assert "hello \n there".chomp == "hello \n there" - assert "hello".chomp("llo") == "he" - assert "hellos".chomp('s') == "hello" - assert "hellos".chomp({'s','z'}) == "hello" - assert "hellos".chomp({'o','s'}) == "hello" - assert "hello\r\n\r\n".chomp("") == "hello" - assert "hello\r\n\r\r\n".chomp("") == "hello\r\n\r" - assert "hello\n\n\n\n\n".chomp("") == "hello" +proc testRemoveSuffix = + var s = "hello\n\r" + s.removeSuffix + assert s == "hello\n" + s.removeSuffix + assert s == "hello" + s.removeSuffix + assert s == "hello" + + s = "hello\n\n" + s.removeSuffix + assert s == "hello\n" + + s = "hello\r" + s.removeSuffix + assert s == "hello" + + s = "hello \n there" + s.removeSuffix + assert s == "hello \n there" + + s = "hello" + s.removeSuffix("llo") + assert s == "he" + s.removeSuffix('e') + assert s == "h" + + s = "hellos" + s.removeSuffix({'s','z'}) + assert s == "hello" + s.removeSuffix({'l','o'}) + assert s == "hell" + + # Contrary to Chomp in other languages + # empty string does not change behaviour + s = "hello\r\n\r\n" + s.removeSuffix("") + assert s == "hello\r\n\r\n" proc main() = testStrip() - testChomp() + testRemoveSuffix() for p in split("/home/a1:xyz:/usr/bin", {':'}): write(stdout, p) From 99b29b3e97a3e6dd3e3396a00d9cf97fb2f538a3 Mon Sep 17 00:00:00 2001 From: Zach Aysan Date: Tue, 21 Jul 2015 15:10:03 -0400 Subject: [PATCH 019/115] Switch to removeSuffix name and modify the string in place --- lib/pure/strutils.nim | 73 ++++++++++++++++--------------------------- 1 file changed, 27 insertions(+), 46 deletions(-) diff --git a/lib/pure/strutils.nim b/lib/pure/strutils.nim index a0fa7a2370..4def0b3e80 100644 --- a/lib/pure/strutils.nim +++ b/lib/pure/strutils.nim @@ -168,52 +168,6 @@ proc cmpIgnoreStyle*(a, b: string): int {.noSideEffect, inc(i) inc(j) -proc chomp*(s: string, chars: set[char] = Newlines): string {.noSideEffect, rtl.} = - var last = len(s) - 1 - - if chars == Newlines: - if s[last] == '\10': - last -= 1 - - if s[last] == '\13': - last -= 1 - - else: - if s[last] in chars: - last -= 1 - - return s[0..last] - -proc chomp*(s: string, c: char): string {.noSideEffect, rtl.} = - chomp(s, chars = {c}) - -proc chomp*(s: string, suffix: string): string {.noSideEffect, rtl.} = - var last = len(s) - 1 - - if suffix == "": - var previous: char - while last > 0: - - if s[last] == '\13' and previous == '\13': - break - - elif s[last] == '\13': - last -= 1 - previous = '\13' - - elif s[last] == '\10': - last -= 1 - previous = '\10' - - else: - break - - if s.endsWith(suffix): - last -= len(suffix) - - return s[0..last] - -{.pop.} proc strip*(s: string, leading = true, trailing = true, chars: set[char] = Whitespace): string {.noSideEffect, rtl, extern: "nsuStrip".} = @@ -1441,6 +1395,33 @@ proc format*(formatstr: string, a: varargs[string, `$`]): string {.noSideEffect, {.pop.} +proc removeSuffix*(s: var string, chars: set[char] = Newlines) {.rtl.} = + var last = len(s) - 1 + + if chars == Newlines: + if s[last] == '\10': + last -= 1 + + if s[last] == '\13': + last -= 1 + + else: + if s[last] in chars: + last -= 1 + + s.setLen(last + 1) + +proc removeSuffix*(s: var string, c: char) {.rtl.} = + removeSuffix(s, chars = {c}) + +proc removeSuffix*(s: var string, suffix: string) {.rtl.} = + var newLen = s.len + + if s.endsWith(suffix): + newLen -= len(suffix) + + s.setLen(newLen) + when isMainModule: doAssert align("abc", 4) == " abc" doAssert align("a", 0) == "a" From d2c992c03d498b7427ed393fc203609578fd6c96 Mon Sep 17 00:00:00 2001 From: Zach Aysan Date: Tue, 21 Jul 2015 15:49:05 -0400 Subject: [PATCH 020/115] Add docs --- lib/pure/strutils.nim | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/lib/pure/strutils.nim b/lib/pure/strutils.nim index 4def0b3e80..3c6421bd53 100644 --- a/lib/pure/strutils.nim +++ b/lib/pure/strutils.nim @@ -1396,6 +1396,20 @@ proc format*(formatstr: string, a: varargs[string, `$`]): string {.noSideEffect, {.pop.} proc removeSuffix*(s: var string, chars: set[char] = Newlines) {.rtl.} = + ## Removes the first matching character from the string (in-place) given a + ## set of characters. If the set of characters is only equal to `Newlines` + ## then it will remove both the newline and return feed. + ## .. code-block:: nim + ## var + ## userInput = "Hello World!\r\n" + ## otherInput = "Hello!?!" + ## userInput.removeSuffix + ## userInput == "Hello World!" + ## userInput.removeSuffix({'!', '?'}) + ## userInput == "Hello World" + ## otherInput.removeSuffix({'!', '?'}) + ## otherInput == "Hello!?" + var last = len(s) - 1 if chars == Newlines: @@ -1412,9 +1426,22 @@ proc removeSuffix*(s: var string, chars: set[char] = Newlines) {.rtl.} = s.setLen(last + 1) proc removeSuffix*(s: var string, c: char) {.rtl.} = + ## Removes a single character (in-place) from a string. + ## .. code-block:: nim + ## var + ## table = "users" + ## table.removeSuffix('s') + ## table == "user" removeSuffix(s, chars = {c}) proc removeSuffix*(s: var string, suffix: string) {.rtl.} = + ## Remove the first matching suffix (in-place) from a string. + ## .. code-block:: nim + ## var + ## answers = "yeses" + ## answers.removeSuffix("es") + ## answers == "yes" + var newLen = s.len if s.endsWith(suffix): From d886c44931e8f69e6b70973e8d85b6b13cf4f75c Mon Sep 17 00:00:00 2001 From: Zach Aysan Date: Tue, 21 Jul 2015 15:51:47 -0400 Subject: [PATCH 021/115] Add extern pragma --- lib/pure/strutils.nim | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/lib/pure/strutils.nim b/lib/pure/strutils.nim index 3c6421bd53..e3f99b8958 100644 --- a/lib/pure/strutils.nim +++ b/lib/pure/strutils.nim @@ -1395,7 +1395,8 @@ proc format*(formatstr: string, a: varargs[string, `$`]): string {.noSideEffect, {.pop.} -proc removeSuffix*(s: var string, chars: set[char] = Newlines) {.rtl.} = +proc removeSuffix*(s: var string, chars: set[char] = Newlines) {. + rtl, extern: "nsuRemoveSuffixCharSet".} = ## Removes the first matching character from the string (in-place) given a ## set of characters. If the set of characters is only equal to `Newlines` ## then it will remove both the newline and return feed. @@ -1425,7 +1426,7 @@ proc removeSuffix*(s: var string, chars: set[char] = Newlines) {.rtl.} = s.setLen(last + 1) -proc removeSuffix*(s: var string, c: char) {.rtl.} = +proc removeSuffix*(s: var string, c: char) {.rtl, extern: "nsuRemoveSuffixChar".} = ## Removes a single character (in-place) from a string. ## .. code-block:: nim ## var @@ -1434,7 +1435,8 @@ proc removeSuffix*(s: var string, c: char) {.rtl.} = ## table == "user" removeSuffix(s, chars = {c}) -proc removeSuffix*(s: var string, suffix: string) {.rtl.} = +proc removeSuffix*(s: var string, suffix: string) {. + rtl, extern: "nsuRemoveSuffixString".} = ## Remove the first matching suffix (in-place) from a string. ## .. code-block:: nim ## var From a12c9cfb9d2e5df881ad90f0f4d7c211b8a14ce6 Mon Sep 17 00:00:00 2001 From: Charles Blake Date: Fri, 24 Jul 2015 14:38:20 -0400 Subject: [PATCH 022/115] Add MemFile text interface factored so users can optionally skip Nim string construction (e.g. to filter many lines out before building strings). Only Windows and/or Unix line endings are supported by default, though fiddling with delim/eat arguments can probably support archaic MacOS9 files. --- lib/pure/memfiles.nim | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/lib/pure/memfiles.nim b/lib/pure/memfiles.nim index 76ff6a8e19..ab42e90c7f 100644 --- a/lib/pure/memfiles.nim +++ b/lib/pure/memfiles.nim @@ -245,3 +245,35 @@ proc close*(f: var MemFile) = if error: raiseOSError(lastErr) +type Record* {.unchecked.} = object + Beg*: cstring + Len*: int + +iterator records*(mfile: MemFile, delim='\l', eat='\r'): Record {.inline.} = + proc c_memchr(cstr: cstring, c: char, n: csize): cstring {. + importc: "memchr", header: "" .} + proc `-!`(p, q: cstring): int {.inline.} = return cast[int](p) -% cast[int](q) + var rec: Record + var End: cstring + rec.Beg = cast[cstring](mfile.mem) + var remaining = mfile.size + while remaining > 0: + End = c_memchr(rec.Beg, delim, remaining) + if End == nil: # unterminated final record + rec.Len = remaining + yield rec + break + rec.Len = End -! rec.Beg # delimiter is not included + if eat != '\0' and rec.Len > 0 and rec.Beg[rec.Len - 1] == eat: + dec(rec.Len) # exclude extra pre-delim ch + yield rec + rec.Beg = cast[cstring](cast[int](End) +% 1) # skip delimiter + remaining = mfile.size - (rec.Beg -! cast[cstring](mfile.mem)) + +proc toString*(rec: Record): string {.inline.} = + proc toNimStr(str: cstring, len: int): string {. importc: "toNimStr" .} + result = toNimStr(cast[cstring](rec.Beg), rec.Len) + result[result.len] = '\0' + +iterator lines*(mfile: MemFile): string {.inline.} = + for rec in records(mfile): yield toString(rec) From 3f4255dd14e197a398a63c0473fcbbc8b50f419c Mon Sep 17 00:00:00 2001 From: Charles Blake Date: Fri, 24 Jul 2015 17:23:11 -0400 Subject: [PATCH 023/115] Beg->data, Len->size to address Varriount's issue. I was unaware of lower case members as a strict style convention. --- lib/pure/memfiles.nim | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/lib/pure/memfiles.nim b/lib/pure/memfiles.nim index ab42e90c7f..8e616b1e77 100644 --- a/lib/pure/memfiles.nim +++ b/lib/pure/memfiles.nim @@ -246,8 +246,8 @@ proc close*(f: var MemFile) = if error: raiseOSError(lastErr) type Record* {.unchecked.} = object - Beg*: cstring - Len*: int + data*: cstring + size*: int iterator records*(mfile: MemFile, delim='\l', eat='\r'): Record {.inline.} = proc c_memchr(cstr: cstring, c: char, n: csize): cstring {. @@ -255,24 +255,24 @@ iterator records*(mfile: MemFile, delim='\l', eat='\r'): Record {.inline.} = proc `-!`(p, q: cstring): int {.inline.} = return cast[int](p) -% cast[int](q) var rec: Record var End: cstring - rec.Beg = cast[cstring](mfile.mem) + rec.data = cast[cstring](mfile.mem) var remaining = mfile.size while remaining > 0: - End = c_memchr(rec.Beg, delim, remaining) + End = c_memchr(rec.data, delim, remaining) if End == nil: # unterminated final record - rec.Len = remaining + rec.size = remaining yield rec break - rec.Len = End -! rec.Beg # delimiter is not included - if eat != '\0' and rec.Len > 0 and rec.Beg[rec.Len - 1] == eat: - dec(rec.Len) # exclude extra pre-delim ch + rec.size = End -! rec.data # delimiter is not included + if eat != '\0' and rec.size > 0 and rec.data[rec.size - 1] == eat: + dec(rec.size) # exclude extra pre-delim ch yield rec - rec.Beg = cast[cstring](cast[int](End) +% 1) # skip delimiter - remaining = mfile.size - (rec.Beg -! cast[cstring](mfile.mem)) + rec.data = cast[cstring](cast[int](End) +% 1) # skip delimiter + remaining = mfile.size - (rec.data -! cast[cstring](mfile.mem)) proc toString*(rec: Record): string {.inline.} = proc toNimStr(str: cstring, len: int): string {. importc: "toNimStr" .} - result = toNimStr(cast[cstring](rec.Beg), rec.Len) + result = toNimStr(cast[cstring](rec.data), rec.size) result[result.len] = '\0' iterator lines*(mfile: MemFile): string {.inline.} = From 21e7e7c895568a79439f34f91a6132938cc742fc Mon Sep 17 00:00:00 2001 From: Yuriy Glukhov Date: Sat, 25 Jul 2015 22:33:53 -0700 Subject: [PATCH 024/115] Emit pragma support for JS backend. --- compiler/jsgen.nim | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/compiler/jsgen.nim b/compiler/jsgen.nim index 87408f3955..f32441d985 100644 --- a/compiler/jsgen.nim +++ b/compiler/jsgen.nim @@ -729,14 +729,13 @@ proc genBreakStmt(p: PProc, n: PNode) = p.blocks[idx].id = abs(p.blocks[idx].id) # label is used addf(p.body, "break L$1;$n" | "goto ::L$1::;$n", [rope(p.blocks[idx].id)]) -proc genAsmStmt(p: PProc, n: PNode) = +proc genAsmOrEmitStmt(p: PProc, n: PNode) = genLineDir(p, n) - assert(n.kind == nkAsmStmt) for i in countup(0, sonsLen(n) - 1): case n.sons[i].kind of nkStrLit..nkTripleStrLit: add(p.body, n.sons[i].strVal) of nkSym: add(p.body, mangleName(n.sons[i].sym)) - else: internalError(n.sons[i].info, "jsgen: genAsmStmt()") + else: internalError(n.sons[i].info, "jsgen: genAsmOrEmitStmt()") proc genIf(p: PProc, n: PNode, r: var TCompRes) = var cond, stmt: TCompRes @@ -1569,6 +1568,12 @@ proc genStmt(p: PProc, n: PNode) = gen(p, n, r) if r.res != nil: addf(p.body, "$#;$n", [r.res]) +proc genPragma(p: PProc, n: PNode) = + for it in n.sons: + case whichPragma(it) + of wEmit: genAsmOrEmitStmt(p, it.sons[1]) + else: discard + proc gen(p: PProc, n: PNode, r: var TCompRes) = r.typ = etyNone r.kind = resNone @@ -1668,12 +1673,13 @@ proc gen(p: PProc, n: PNode, r: var TCompRes) = if n.sons[0].kind != nkEmpty: genLineDir(p, n) gen(p, n.sons[0], r) - of nkAsmStmt: genAsmStmt(p, n) + of nkAsmStmt: genAsmOrEmitStmt(p, n) of nkTryStmt: genTry(p, n, r) of nkRaiseStmt: genRaiseStmt(p, n) of nkTypeSection, nkCommentStmt, nkIteratorDef, nkIncludeStmt, nkImportStmt, nkImportExceptStmt, nkExportStmt, nkExportExceptStmt, - nkFromStmt, nkTemplateDef, nkMacroDef, nkPragma: discard + nkFromStmt, nkTemplateDef, nkMacroDef: discard + of nkPragma: genPragma(p, n) of nkProcDef, nkMethodDef, nkConverterDef: var s = n.sons[namePos].sym if {sfExportc, sfCompilerProc} * s.flags == {sfExportc}: From ae303cc1c3feb7120202e7c711eb6044a3486834 Mon Sep 17 00:00:00 2001 From: Charles Blake Date: Sun, 26 Jul 2015 07:52:30 -0400 Subject: [PATCH 025/115] Rename [rR]ecord->[mM]emSlice, rec->ms, End->ending. Also update some comments. --- lib/pure/memfiles.nim | 38 +++++++++++++++++++------------------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/lib/pure/memfiles.nim b/lib/pure/memfiles.nim index 8e616b1e77..4f92b0e2f5 100644 --- a/lib/pure/memfiles.nim +++ b/lib/pure/memfiles.nim @@ -245,35 +245,35 @@ proc close*(f: var MemFile) = if error: raiseOSError(lastErr) -type Record* {.unchecked.} = object +type MemSlice* {.unchecked.} = object data*: cstring size*: int -iterator records*(mfile: MemFile, delim='\l', eat='\r'): Record {.inline.} = +iterator memSlices*(mfile: MemFile, delim='\l', eat='\r'): MemSlice {.inline.} = proc c_memchr(cstr: cstring, c: char, n: csize): cstring {. importc: "memchr", header: "" .} proc `-!`(p, q: cstring): int {.inline.} = return cast[int](p) -% cast[int](q) - var rec: Record - var End: cstring - rec.data = cast[cstring](mfile.mem) + var ms: MemSlice + var ending: cstring + ms.data = cast[cstring](mfile.mem) var remaining = mfile.size while remaining > 0: - End = c_memchr(rec.data, delim, remaining) - if End == nil: # unterminated final record - rec.size = remaining - yield rec + ending = c_memchr(ms.data, delim, remaining) + if ending == nil: # unterminated final slice + ms.size = remaining # Weird case..check eat? + yield ms break - rec.size = End -! rec.data # delimiter is not included - if eat != '\0' and rec.size > 0 and rec.data[rec.size - 1] == eat: - dec(rec.size) # exclude extra pre-delim ch - yield rec - rec.data = cast[cstring](cast[int](End) +% 1) # skip delimiter - remaining = mfile.size - (rec.data -! cast[cstring](mfile.mem)) + ms.size = ending -! ms.data # delim is NOT included + if eat != '\0' and ms.size > 0 and ms.data[ms.size - 1] == eat: + dec(ms.size) # trim pre-delim char + yield ms + ms.data = cast[cstring](cast[int](ending) +% 1) # skip delim + remaining = mfile.size - (ms.data -! cast[cstring](mfile.mem)) -proc toString*(rec: Record): string {.inline.} = +proc toString*(ms: MemSlice): string {.inline.} = proc toNimStr(str: cstring, len: int): string {. importc: "toNimStr" .} - result = toNimStr(cast[cstring](rec.data), rec.size) - result[result.len] = '\0' + result = toNimStr(cast[cstring](ms.data), ms.size) + result[result.len] = '\0' # toNimStr copies 1 extra byte but does not NUL-term iterator lines*(mfile: MemFile): string {.inline.} = - for rec in records(mfile): yield toString(rec) + for ms in memSlices(mfile): yield toString(ms) From 18df5dad113c24696c3cae45ffdfe663f7d7b4ef Mon Sep 17 00:00:00 2001 From: Charles Blake Date: Sun, 26 Jul 2015 09:02:43 -0400 Subject: [PATCH 026/115] Do not read past end of input. Ensure NUL termination. --- lib/system/sysstr.nim | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/system/sysstr.nim b/lib/system/sysstr.nim index 326c601bda..e4673b169e 100644 --- a/lib/system/sysstr.nim +++ b/lib/system/sysstr.nim @@ -82,7 +82,8 @@ proc copyStr(s: NimString, start: int): NimString {.compilerProc.} = proc toNimStr(str: cstring, len: int): NimString {.compilerProc.} = result = rawNewStringNoInit(len) result.len = len - c_memcpy(result.data, str, len + 1) + c_memcpy(result.data, str, len) + result[result.len] = '\0' proc cstrToNimstr(str: cstring): NimString {.compilerRtl.} = result = toNimStr(str, c_strlen(str)) From a2b6a630feaf7e6165feeb44281a8f9526f5b2a3 Mon Sep 17 00:00:00 2001 From: Charles Blake Date: Sun, 26 Jul 2015 09:17:04 -0400 Subject: [PATCH 027/115] Oops..[]= not defined yet here. Use .data instead. --- lib/system/sysstr.nim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/system/sysstr.nim b/lib/system/sysstr.nim index e4673b169e..9c98f84166 100644 --- a/lib/system/sysstr.nim +++ b/lib/system/sysstr.nim @@ -83,7 +83,7 @@ proc toNimStr(str: cstring, len: int): NimString {.compilerProc.} = result = rawNewStringNoInit(len) result.len = len c_memcpy(result.data, str, len) - result[result.len] = '\0' + result.data[len] = '\0' proc cstrToNimstr(str: cstring): NimString {.compilerRtl.} = result = toNimStr(str, c_strlen(str)) From f2c5676767fd9d9d25fc671a5be4577d029a8a16 Mon Sep 17 00:00:00 2001 From: Charles Blake Date: Tue, 28 Jul 2015 06:45:07 -0400 Subject: [PATCH 028/115] Remove NUL termination unneeded post toNimStr fix. --- lib/pure/memfiles.nim | 1 - 1 file changed, 1 deletion(-) diff --git a/lib/pure/memfiles.nim b/lib/pure/memfiles.nim index 4f92b0e2f5..90797ecc62 100644 --- a/lib/pure/memfiles.nim +++ b/lib/pure/memfiles.nim @@ -273,7 +273,6 @@ iterator memSlices*(mfile: MemFile, delim='\l', eat='\r'): MemSlice {.inline.} = proc toString*(ms: MemSlice): string {.inline.} = proc toNimStr(str: cstring, len: int): string {. importc: "toNimStr" .} result = toNimStr(cast[cstring](ms.data), ms.size) - result[result.len] = '\0' # toNimStr copies 1 extra byte but does not NUL-term iterator lines*(mfile: MemFile): string {.inline.} = for ms in memSlices(mfile): yield toString(ms) From 0b4c816cba8f11c28e983818e89ee8cbf20dff9e Mon Sep 17 00:00:00 2001 From: Charles Blake Date: Wed, 29 Jul 2015 06:14:44 -0400 Subject: [PATCH 029/115] cstring->pointer in MemSlice to make consistent with MemFile and also to make more clear the care required to use MemSlice instances. (E.g., memXXX functions rather than strXXX functions). --- lib/pure/memfiles.nim | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/lib/pure/memfiles.nim b/lib/pure/memfiles.nim index 90797ecc62..ccd45ab2ba 100644 --- a/lib/pure/memfiles.nim +++ b/lib/pure/memfiles.nim @@ -246,16 +246,16 @@ proc close*(f: var MemFile) = if error: raiseOSError(lastErr) type MemSlice* {.unchecked.} = object - data*: cstring + data*: pointer size*: int iterator memSlices*(mfile: MemFile, delim='\l', eat='\r'): MemSlice {.inline.} = - proc c_memchr(cstr: cstring, c: char, n: csize): cstring {. + proc c_memchr(cstr: pointer, c: char, n: csize): pointer {. importc: "memchr", header: "" .} - proc `-!`(p, q: cstring): int {.inline.} = return cast[int](p) -% cast[int](q) + proc `-!`(p, q: pointer): int {.inline.} = return cast[int](p) -% cast[int](q) var ms: MemSlice - var ending: cstring - ms.data = cast[cstring](mfile.mem) + var ending: pointer + ms.data = mfile.mem var remaining = mfile.size while remaining > 0: ending = c_memchr(ms.data, delim, remaining) @@ -264,11 +264,11 @@ iterator memSlices*(mfile: MemFile, delim='\l', eat='\r'): MemSlice {.inline.} = yield ms break ms.size = ending -! ms.data # delim is NOT included - if eat != '\0' and ms.size > 0 and ms.data[ms.size - 1] == eat: + if eat != '\0' and ms.size > 0 and cast[cstring](ms.data)[ms.size - 1] == eat: dec(ms.size) # trim pre-delim char yield ms - ms.data = cast[cstring](cast[int](ending) +% 1) # skip delim - remaining = mfile.size - (ms.data -! cast[cstring](mfile.mem)) + ms.data = cast[pointer](cast[int](ending) +% 1) # skip delim + remaining = mfile.size - (ms.data -! mfile.mem) proc toString*(ms: MemSlice): string {.inline.} = proc toNimStr(str: cstring, len: int): string {. importc: "toNimStr" .} From 3fe39798e6a124d8318c8a6aa1b0ede08bee1cf8 Mon Sep 17 00:00:00 2001 From: Ryan Gonzalez Date: Wed, 29 Jul 2015 11:47:21 -0500 Subject: [PATCH 030/115] Fix contributing guide rST --- contributing.rst | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/contributing.rst b/contributing.rst index 68b706c735..2fd04df366 100644 --- a/contributing.rst +++ b/contributing.rst @@ -66,12 +66,14 @@ Running tests You can run the tests with :: + ./koch tests which will run a good subset of tests. Some tests may fail. If you only want to see the output of failing tests, go for :: + ./koch tests --failing all You can also run only a single category of tests. A category is a subdirectory @@ -79,6 +81,7 @@ in the ``tests`` directory. There are a couple of special categories; for a list of these, see ``tests/testament/categories.nim``, at the bottom. :: + ./koch tests c lib Comparing tests @@ -92,6 +95,7 @@ reference test. You'll also need to the commit id, because that's what the tester needs to know in order to compare the two. :: + git checkout devel DEVEL_COMMIT=$(git rev-parse HEAD) ./koch tests @@ -99,6 +103,7 @@ the tester needs to know in order to compare the two. Then switch over to your changes and run the tester again. :: + git checkout your-changes ./koch tests @@ -106,6 +111,7 @@ Then you can ask the tester to create a ``testresults.html`` which will tell you if any new tests passed/failed. :: + ./koch --print html $DEVEL_COMMIT From 10da70d49e2f5e8030b5d797ea706bc4156cc100 Mon Sep 17 00:00:00 2001 From: apense Date: Wed, 29 Jul 2015 17:57:47 -0400 Subject: [PATCH 031/115] Fix double-ticks --- docstyle.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docstyle.rst b/docstyle.rst index 6b0e188fa4..d789b1df91 100644 --- a/docstyle.rst +++ b/docstyle.rst @@ -7,7 +7,7 @@ General Guidelines * Authors should document anything that is exported. * Within documentation, a period (`.`) should follow each sentence (or sentence fragment) in a comment block. The documentation may be limited to one sentence fragment, but if multiple sentences are within the documentation, each sentence after the first should be complete and in present tense. * Documentation is parsed as ReStructuredText (RST). -* Inline code should be surrounded by double tick marks ("``` `` ```"). If you would like a character to immediately follow inline code (e.g., "``int8``s are great!"), escape the following character with a backslash (``\``). The preceding is typed as ``` ``int8``\s are great!```. +* Inline code should be surrounded by double tick marks ("``````"). If you would like a character to immediately follow inline code (e.g., "``int8``s are great!"), escape the following character with a backslash (``\``). The preceding is typed as ``` ``int8``\s are great!```. Module-level documentation -------------------------- @@ -38,7 +38,7 @@ Leave a space between the last line of top-level documentation and the beginning Procs, Templates, Macros, Converters, and Iterators --------------------------------------------------- -The documentation of a procedure should begin with a capital letter and should be in present tense. Variables referenced in the documentation should be surrounded by double tick marks (``` `` ```). +The documentation of a procedure should begin with a capital letter and should be in present tense. Variables referenced in the documentation should be surrounded by double tick marks (``````). .. code-block:: Nim From 5e0b8d5ef6ce616361cedf963afde667edfadb89 Mon Sep 17 00:00:00 2001 From: coffeepots Date: Fri, 31 Jul 2015 09:44:37 +0100 Subject: [PATCH 032/115] Implemented changes suggested by dom96 * Removed extraneous exports (and converted const array to separate consts) * Renamed dayOfWeek, dayOfWeekJulian to getDayOfWeek and getDayOfWeekJulian * Renamed decodeTime procs to timeToTimeInfo and timeToTimeInterval * Added some basic descriptions to docs --- lib/pure/times.nim | 79 ++++++++++++++++++++++------------------------ 1 file changed, 38 insertions(+), 41 deletions(-) diff --git a/lib/pure/times.nim b/lib/pure/times.nim index a85998cb5c..e69357597d 100644 --- a/lib/pure/times.nim +++ b/lib/pure/times.nim @@ -1054,32 +1054,27 @@ proc countLeapYears(yearSpan: int): int = proc countDays(yearSpan: int): int = (yearSpan - 1) * 365 + countLeapYears(yearSpan) -# counts the number of years spanned by a given number of days. proc countYears(daySpan: int): int = + # counts the number of years spanned by a given number of days. ((daySpan - countLeapYears(daySpan div 365)) div 365) proc countYearsAndDays(daySpan: int): tuple[years: int, days: int] = + # counts the number of years spanned by a given number of days and the remainder as days. let days = daySpan - countLeapYears(daySpan div 365) result.years = days div 365 result.days = days mod 365 -type - SecondScale* = enum ssMinute, ssHour, ssDay, ssMonth, ssYear - -# these are very approximate beyond day -const secondsIn*: array[SecondScale.low..SecondScale.high, int] =[ - 60, # minute - 60*60, # hour - 60*60*24, # day - 60*60*24*30, # month (estimate) - 60*60*24*30*12] # year (estimate) +const + secondsInMin = 60 + secondsInHour = 60*60 + secondsInDay = 60*60*24 const epochStartYear = 1970 - leapYearsSinceEpoch* = countLeapYears(epochStartYear) + leapYearsSinceEpoch = countLeapYears(epochStartYear) -proc dayOfWeek*(day, month, year: int): WeekDay = - # This is for the Gregorian calendar +proc getDayOfWeek*(day, month, year: int): WeekDay = + ## Returns the day of the week enum from day, month and year. # Day & month start from one. let a = (14 - month) div 12 @@ -1091,8 +1086,8 @@ proc dayOfWeek*(day, month, year: int): WeekDay = if d == 0: return dSun result = (d-1).WeekDay -proc dayOfWeekJulian*(day, month, year: int): WeekDay = - # This is for the Julian calendar +proc getDayOfWeekJulian*(day, month, year: int): WeekDay = + ## Returns the day of the week enum from day, month and year, according to the Julian calender. # Day & month start from one. let a = (14 - month) div 12 @@ -1101,11 +1096,12 @@ proc dayOfWeekJulian*(day, month, year: int): WeekDay = d = (5 + day + y + (y div 4) + (31*m) div 12) mod 7 result = d.WeekDay -proc decodeTime*(t: Time): TimeInfo = +proc timeToTimeInfo*(t: Time): TimeInfo = + ## Converts a Time to TimeInfo. let - daysSinceEpoch = t.int div secondsIn[ssDay] + daysSinceEpoch = t.int div secondsInDay (yearsSinceEpoch, daysRemaining) = countYearsAndDays(daysSinceEpoch) - daySeconds = t.int mod secondsIn[ssDay] + daySeconds = t.int mod secondsInDay y = yearsSinceEpoch + epochStartYear @@ -1125,17 +1121,18 @@ proc decodeTime*(t: Time): TimeInfo = m = mon # month is zero indexed enum md = days # NB: month is zero indexed but dayOfWeek expects 1 indexed. - wd = dayOfWeek(days, mon.int + 1, y).Weekday - h = daySeconds div secondsIn[ssHour] + 1 - mi = (daySeconds mod secondsIn[ssHour]) div secondsIn[ssMinute] - s = daySeconds mod secondsIn[ssMinute] - result = TimeInfo(year: y, yearday: yd, month: m, monthday: md, weekday: wd, hour: h, minute: mi, second: s) + wd = getDayOfWeek(days, mon.int + 1, y).Weekday + h = daySeconds div secondsInHour + 1 + mi = (daySeconds mod secondsInHour) div secondsInMin + s = daySeconds mod secondsInMin + result = TimeInfo(year: y, yearday: yd, month: m, monthday: md, weekday: wd, hour: h, minute: mi, second: s) -proc decodeTimeInterval*(t: Time): TimeInterval = +proc timetoTimeInterval*(t: Time): TimeInterval = + ## Converts a Time to a TimeInterval. var - daysSinceEpoch = t.int div secondsIn[ssDay] + daysSinceEpoch = t.int div secondsInDay (yearsSinceEpoch, daysRemaining) = countYearsAndDays(daysSinceEpoch) - daySeconds = t.int mod secondsIn[ssDay] + daySeconds = t.int mod secondsInDay result.years = yearsSinceEpoch + epochStartYear @@ -1152,9 +1149,9 @@ proc decodeTimeInterval*(t: Time): TimeInterval = result.months = mon.int + 1 # month is 1 indexed int result.days = days - result.hours = daySeconds div secondsIn[ssHour] + 1 - result.minutes = (daySeconds mod secondsIn[ssHour]) div secondsIn[ssMinute] - result.seconds = daySeconds mod secondsIn[ssMinute] + result.hours = daySeconds div secondsInHour + 1 + result.minutes = (daySeconds mod secondsInHour) div secondsInMin + result.seconds = daySeconds mod secondsInMin # Milliseconds not available from Time when isMainModule: @@ -1239,18 +1236,18 @@ when isMainModule: assert "15:04:00" in $s.parse(f) when not defined(testing): echo "Kitchen: " & $s.parse(f) - var ti = decodeTime(getTime()) + var ti = timeToTimeInfo(getTime()) echo "Todays date after decoding: ", ti - var tint = decodeTimeInterval(getTime()) + var tint = timeToTimeInterval(getTime()) echo "Todays date after decoding to interval: ", tint # checking dayOfWeek matches known days - assert dayOfWeek(21, 9, 1900) == dFri - assert dayOfWeek(1, 1, 1970) == dThu - assert dayOfWeek(21, 9, 1970) == dMon - assert dayOfWeek(1, 1, 2000) == dSat - assert dayOfWeek(1, 1, 2021) == dFri + assert getDayOfWeek(21, 9, 1900) == dFri + assert getDayOfWeek(1, 1, 1970) == dThu + assert getDayOfWeek(21, 9, 1970) == dMon + assert getDayOfWeek(1, 1, 2000) == dSat + assert getDayOfWeek(1, 1, 2021) == dFri # Julian tests - assert dayOfWeekJulian(21, 9, 1900) == dFri - assert dayOfWeekJulian(21, 9, 1970) == dMon - assert dayOfWeekJulian(1, 1, 2000) == dSat - assert dayOfWeekJulian(1, 1, 2021) == dFri + assert getDayOfWeekJulian(21, 9, 1900) == dFri + assert getDayOfWeekJulian(21, 9, 1970) == dMon + assert getDayOfWeekJulian(1, 1, 2000) == dSat + assert getDayOfWeekJulian(1, 1, 2021) == dFri From f9d909bb947749666c0241f64bd8adef8c071a9b Mon Sep 17 00:00:00 2001 From: coffeepots Date: Fri, 31 Jul 2015 10:20:21 +0100 Subject: [PATCH 033/115] Removed unused leapYearsSinceEpoch --- lib/pure/times.nim | 3 --- 1 file changed, 3 deletions(-) diff --git a/lib/pure/times.nim b/lib/pure/times.nim index e69357597d..0b77f02c57 100644 --- a/lib/pure/times.nim +++ b/lib/pure/times.nim @@ -1068,10 +1068,7 @@ const secondsInMin = 60 secondsInHour = 60*60 secondsInDay = 60*60*24 - -const epochStartYear = 1970 - leapYearsSinceEpoch = countLeapYears(epochStartYear) proc getDayOfWeek*(day, month, year: int): WeekDay = ## Returns the day of the week enum from day, month and year. From ad896f03e8551db7387904ba3da7ed3ac741235b Mon Sep 17 00:00:00 2001 From: Charlie Barto Date: Fri, 31 Jul 2015 15:08:53 -0400 Subject: [PATCH 034/115] import the correct symbols on windows --- lib/pure/times.nim | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/lib/pure/times.nim b/lib/pure/times.nim index 5f8835c6a7..ad8f77dbf9 100644 --- a/lib/pure/times.nim +++ b/lib/pure/times.nim @@ -26,11 +26,6 @@ type WeekDay* = enum ## represents a weekday dMon, dTue, dWed, dThu, dFri, dSat, dSun -when not defined(JS): - var - timezone {.importc, header: "".}: int - tzname {.importc, header: "" .}: array[0..1, cstring] - when defined(posix) and not defined(JS): type TimeImpl {.importc: "time_t", header: "".} = int @@ -49,6 +44,9 @@ when defined(posix) and not defined(JS): proc posix_gettimeofday(tp: var Timeval, unused: pointer = nil) {. importc: "gettimeofday", header: "".} + var + timezone {.importc, header: "".}: int + tzname {.importc, header: "" .}: array[0..1, cstring] # we also need tzset() to make sure that tzname is initialized proc tzset() {.importc, header: "".} # calling tzset() implicitly to initialize tzname data. @@ -65,7 +63,10 @@ elif defined(windows): type Time* = distinct TimeImpl - + + var + timezone {.importc: "_timezone", header: "".}: int + tzname {.importc: "_tzname", header: ""}: array[0..1, cstring] elif defined(JS): type Time* {.importc.} = object From 70c4176105e08127da444a6419e1ad6d4c725dd7 Mon Sep 17 00:00:00 2001 From: def Date: Fri, 31 Jul 2015 23:16:52 +0200 Subject: [PATCH 035/115] Add Mipsel CPU support (untested) --- compiler/platform.nim | 3 ++- lib/system.nim | 3 ++- lib/system/platforms.nim | 2 ++ 3 files changed, 6 insertions(+), 2 deletions(-) diff --git a/compiler/platform.nim b/compiler/platform.nim index 4dd5d88366..f52017af72 100644 --- a/compiler/platform.nim +++ b/compiler/platform.nim @@ -158,7 +158,7 @@ type TSystemCPU* = enum # Also add CPU for in initialization section and # alias conditionals to condsyms (end of module). cpuNone, cpuI386, cpuM68k, cpuAlpha, cpuPowerpc, cpuPowerpc64, - cpuSparc, cpuVm, cpuIa64, cpuAmd64, cpuMips, cpuArm, + cpuSparc, cpuVm, cpuIa64, cpuAmd64, cpuMips, cpuMipsel, cpuArm, cpuJS, cpuNimrodVM, cpuAVR type @@ -180,6 +180,7 @@ const (name: "ia64", intSize: 64, endian: littleEndian, floatSize: 64, bit: 64), (name: "amd64", intSize: 64, endian: littleEndian, floatSize: 64, bit: 64), (name: "mips", intSize: 32, endian: bigEndian, floatSize: 64, bit: 32), + (name: "mipsel", intSize: 32, endian: littleEndian, floatSize: 64, bit: 32), (name: "arm", intSize: 32, endian: littleEndian, floatSize: 64, bit: 32), (name: "js", intSize: 32, endian: bigEndian,floatSize: 64,bit: 32), (name: "nimrodvm", intSize: 32, endian: bigEndian, floatSize: 64, bit: 32), diff --git a/lib/system.nim b/lib/system.nim index 91495f31a6..56b62d111d 100644 --- a/lib/system.nim +++ b/lib/system.nim @@ -1150,7 +1150,8 @@ const hostCPU* {.magic: "HostCPU".}: string = "" ## a string that describes the host CPU. Possible values: - ## "i386", "alpha", "powerpc", "powerpc64", "sparc", "amd64", "mips", "arm". + ## "i386", "alpha", "powerpc", "powerpc64", "sparc", "amd64", "mips", + ## "mipsel", "arm". seqShallowFlag = low(int) diff --git a/lib/system/platforms.nim b/lib/system/platforms.nim index 47a01d5fe4..0472181f5f 100644 --- a/lib/system/platforms.nim +++ b/lib/system/platforms.nim @@ -22,6 +22,7 @@ type ia64, ## Intel Itanium amd64, ## x86_64 (AMD64); 64 bit x86 compatible CPU mips, ## Mips based processor + mipsel, ## Little Endian Mips based processor arm, ## ARM based processor vm, ## Some Virtual machine: Nim's VM or JavaScript avr ## AVR based processor @@ -67,6 +68,7 @@ const elif defined(ia64): CpuPlatform.ia64 elif defined(amd64): CpuPlatform.amd64 elif defined(mips): CpuPlatform.mips + elif defined(mipsel): CpuPlatform.mipsel elif defined(arm): CpuPlatform.arm elif defined(vm): CpuPlatform.vm elif defined(avr): CpuPlatform.avr From 3d6a8b401a22f02c6dd604bc6caac09f415d3ddf Mon Sep 17 00:00:00 2001 From: Dominik Picheta Date: Fri, 31 Jul 2015 22:40:45 +0100 Subject: [PATCH 036/115] C source generation for mipsel. --- compiler/installer.ini | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/installer.ini b/compiler/installer.ini index 52a3d0886d..1ca615153a 100644 --- a/compiler/installer.ini +++ b/compiler/installer.ini @@ -6,7 +6,7 @@ Name: "Nim" Version: "$version" Platforms: """ windows: i386;amd64 - linux: i386;amd64;powerpc64;arm;sparc;mips;powerpc + linux: i386;amd64;powerpc64;arm;sparc;mips;mipsel;powerpc macosx: i386;amd64;powerpc64 solaris: i386;amd64;sparc freebsd: i386;amd64 From 130605567e93902df1cc65dade92ec433fdf9dff Mon Sep 17 00:00:00 2001 From: def Date: Fri, 31 Jul 2015 23:55:31 +0200 Subject: [PATCH 037/115] Add powerpc64el support (untested) --- compiler/platform.nim | 5 +++-- lib/system.nim | 4 ++-- lib/system/platforms.nim | 2 ++ 3 files changed, 7 insertions(+), 4 deletions(-) diff --git a/compiler/platform.nim b/compiler/platform.nim index f52017af72..153ada2b2b 100644 --- a/compiler/platform.nim +++ b/compiler/platform.nim @@ -158,8 +158,8 @@ type TSystemCPU* = enum # Also add CPU for in initialization section and # alias conditionals to condsyms (end of module). cpuNone, cpuI386, cpuM68k, cpuAlpha, cpuPowerpc, cpuPowerpc64, - cpuSparc, cpuVm, cpuIa64, cpuAmd64, cpuMips, cpuMipsel, cpuArm, - cpuJS, cpuNimrodVM, cpuAVR + cpuPowerpc64el, cpuSparc, cpuVm, cpuIa64, cpuAmd64, cpuMips, cpuMipsel, + cpuArm, cpuJS, cpuNimrodVM, cpuAVR type TEndian* = enum @@ -175,6 +175,7 @@ const (name: "alpha", intSize: 64, endian: littleEndian, floatSize: 64, bit: 64), (name: "powerpc", intSize: 32, endian: bigEndian, floatSize: 64, bit: 32), (name: "powerpc64", intSize: 64, endian: bigEndian, floatSize: 64,bit: 64), + (name: "powerpc64el", intSize: 64, endian: littleEndian, floatSize: 64,bit: 64), (name: "sparc", intSize: 32, endian: bigEndian, floatSize: 64, bit: 32), (name: "vm", intSize: 32, endian: littleEndian, floatSize: 64, bit: 32), (name: "ia64", intSize: 64, endian: littleEndian, floatSize: 64, bit: 64), diff --git a/lib/system.nim b/lib/system.nim index 56b62d111d..19af433dec 100644 --- a/lib/system.nim +++ b/lib/system.nim @@ -1150,8 +1150,8 @@ const hostCPU* {.magic: "HostCPU".}: string = "" ## a string that describes the host CPU. Possible values: - ## "i386", "alpha", "powerpc", "powerpc64", "sparc", "amd64", "mips", - ## "mipsel", "arm". + ## "i386", "alpha", "powerpc", "powerpc64", "powerpc64el", "sparc", + ## "amd64", "mips", "mipsel", "arm". seqShallowFlag = low(int) diff --git a/lib/system/platforms.nim b/lib/system/platforms.nim index 0472181f5f..243f48a3ab 100644 --- a/lib/system/platforms.nim +++ b/lib/system/platforms.nim @@ -18,6 +18,7 @@ type alpha, ## Alpha processor powerpc, ## 32 bit PowerPC powerpc64, ## 64 bit PowerPC + powerpc64el, ## Little Endian 64 bit PowerPC sparc, ## Sparc based processor ia64, ## Intel Itanium amd64, ## x86_64 (AMD64); 64 bit x86 compatible CPU @@ -64,6 +65,7 @@ const elif defined(alpha): CpuPlatform.alpha elif defined(powerpc): CpuPlatform.powerpc elif defined(powerpc64): CpuPlatform.powerpc64 + elif defined(powerpc64el): CpuPlatform.powerpc64el elif defined(sparc): CpuPlatform.sparc elif defined(ia64): CpuPlatform.ia64 elif defined(amd64): CpuPlatform.amd64 From 13389eab9bc879a32e74c9b11ce17d68968cd4fe Mon Sep 17 00:00:00 2001 From: def Date: Fri, 31 Jul 2015 23:57:50 +0200 Subject: [PATCH 038/115] Add arm64 support (untested) --- compiler/platform.nim | 3 ++- lib/system.nim | 2 +- lib/system/platforms.nim | 2 ++ 3 files changed, 5 insertions(+), 2 deletions(-) diff --git a/compiler/platform.nim b/compiler/platform.nim index 153ada2b2b..c4e7d453a1 100644 --- a/compiler/platform.nim +++ b/compiler/platform.nim @@ -159,7 +159,7 @@ type # alias conditionals to condsyms (end of module). cpuNone, cpuI386, cpuM68k, cpuAlpha, cpuPowerpc, cpuPowerpc64, cpuPowerpc64el, cpuSparc, cpuVm, cpuIa64, cpuAmd64, cpuMips, cpuMipsel, - cpuArm, cpuJS, cpuNimrodVM, cpuAVR + cpuArm, cpuArm64, cpuJS, cpuNimrodVM, cpuAVR type TEndian* = enum @@ -183,6 +183,7 @@ const (name: "mips", intSize: 32, endian: bigEndian, floatSize: 64, bit: 32), (name: "mipsel", intSize: 32, endian: littleEndian, floatSize: 64, bit: 32), (name: "arm", intSize: 32, endian: littleEndian, floatSize: 64, bit: 32), + (name: "arm64", intSize: 64, endian: littleEndian, floatSize: 64, bit: 64), (name: "js", intSize: 32, endian: bigEndian,floatSize: 64,bit: 32), (name: "nimrodvm", intSize: 32, endian: bigEndian, floatSize: 64, bit: 32), (name: "avr", intSize: 16, endian: littleEndian, floatSize: 32, bit: 16)] diff --git a/lib/system.nim b/lib/system.nim index 19af433dec..260d7fbcd2 100644 --- a/lib/system.nim +++ b/lib/system.nim @@ -1151,7 +1151,7 @@ const hostCPU* {.magic: "HostCPU".}: string = "" ## a string that describes the host CPU. Possible values: ## "i386", "alpha", "powerpc", "powerpc64", "powerpc64el", "sparc", - ## "amd64", "mips", "mipsel", "arm". + ## "amd64", "mips", "mipsel", "arm", "arm64". seqShallowFlag = low(int) diff --git a/lib/system/platforms.nim b/lib/system/platforms.nim index 243f48a3ab..0a7045fae0 100644 --- a/lib/system/platforms.nim +++ b/lib/system/platforms.nim @@ -25,6 +25,7 @@ type mips, ## Mips based processor mipsel, ## Little Endian Mips based processor arm, ## ARM based processor + arm64, ## ARM64 based processor vm, ## Some Virtual machine: Nim's VM or JavaScript avr ## AVR based processor @@ -72,6 +73,7 @@ const elif defined(mips): CpuPlatform.mips elif defined(mipsel): CpuPlatform.mipsel elif defined(arm): CpuPlatform.arm + elif defined(arm64): CpuPlatform.arm64 elif defined(vm): CpuPlatform.vm elif defined(avr): CpuPlatform.avr else: CpuPlatform.none From 9804033aa8f719e2b8cf74be76c4b693e8ea3ba9 Mon Sep 17 00:00:00 2001 From: def Date: Sat, 1 Aug 2015 00:00:51 +0200 Subject: [PATCH 039/115] Add new platforms to installer.ini (for linux at least) --- compiler/installer.ini | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/installer.ini b/compiler/installer.ini index 1ca615153a..f92d4a3eb5 100644 --- a/compiler/installer.ini +++ b/compiler/installer.ini @@ -6,7 +6,7 @@ Name: "Nim" Version: "$version" Platforms: """ windows: i386;amd64 - linux: i386;amd64;powerpc64;arm;sparc;mips;mipsel;powerpc + linux: i386;amd64;powerpc64;arm;sparc;mips;mipsel;powerpc;powerpc64el;arm64;mipsel macosx: i386;amd64;powerpc64 solaris: i386;amd64;sparc freebsd: i386;amd64 From 92ca736f0ac2e3b8c66516fb22ce8c5fbdac69d5 Mon Sep 17 00:00:00 2001 From: Dominik Picheta Date: Sat, 1 Aug 2015 13:43:51 +0100 Subject: [PATCH 040/115] Removed duplicate 'mipsel' from installer.ini. Ref #3164. --- compiler/installer.ini | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/installer.ini b/compiler/installer.ini index f92d4a3eb5..c8af388865 100644 --- a/compiler/installer.ini +++ b/compiler/installer.ini @@ -6,7 +6,7 @@ Name: "Nim" Version: "$version" Platforms: """ windows: i386;amd64 - linux: i386;amd64;powerpc64;arm;sparc;mips;mipsel;powerpc;powerpc64el;arm64;mipsel + linux: i386;amd64;powerpc64;arm;sparc;mips;mipsel;powerpc;powerpc64el;arm64 macosx: i386;amd64;powerpc64 solaris: i386;amd64;sparc freebsd: i386;amd64 From 9dde0ae3ac6c97704f64e0dee0d55c1a49faaf4b Mon Sep 17 00:00:00 2001 From: jonathonf Date: Sat, 1 Aug 2015 20:53:37 +0100 Subject: [PATCH 041/115] Set correct Host in request header Ensure the correct destination hostname is used in the request, otherwise the request will fail in one of a number of interesting ways when using a proxy. Fixes https://github.com/nim-lang/Nim/issues/3166 --- lib/pure/httpclient.nim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/pure/httpclient.nim b/lib/pure/httpclient.nim index e6b8088c59..98687359bb 100644 --- a/lib/pure/httpclient.nim +++ b/lib/pure/httpclient.nim @@ -402,7 +402,7 @@ proc request*(url: string, httpMethod: string, extraHeaders = "", headers.add(" HTTP/1.1\c\L") - add(headers, "Host: " & r.hostname & "\c\L") + add(headers, "Host: " & parseUri(url).hostname & "\c\L") if userAgent != "": add(headers, "User-Agent: " & userAgent & "\c\L") if proxy != nil and proxy.auth != "": From 9275d5c6178e7674882dbbaa257f6bb2b5a7b6bf Mon Sep 17 00:00:00 2001 From: Araq Date: Sun, 2 Aug 2015 00:05:56 +0200 Subject: [PATCH 042/115] emerald project has a chance of working --- compiler/vm.nim | 9 +++++---- compiler/vmgen.nim | 27 ++++++++++++++++++--------- 2 files changed, 23 insertions(+), 13 deletions(-) diff --git a/compiler/vm.nim b/compiler/vm.nim index 40d273ceb6..3166ba726d 100644 --- a/compiler/vm.nim +++ b/compiler/vm.nim @@ -120,10 +120,10 @@ template decodeBx(k: expr) {.immediate, dirty.} = template move(a, b: expr) {.immediate, dirty.} = system.shallowCopy(a, b) # XXX fix minor 'shallowCopy' overloading bug in compiler -proc createStrKeepNode(x: var TFullReg) = +proc createStrKeepNode(x: var TFullReg; keepNode=true) = if x.node.isNil: x.node = newNode(nkStrLit) - elif x.node.kind == nkNilLit: + elif x.node.kind == nkNilLit and keepNode: when defined(useNodeIds): let id = x.node.id system.reset(x.node[]) @@ -385,6 +385,7 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg = #if c.traceActive: # echo "PC ", pc, " ", c.code[pc].opcode, " ra ", ra # message(c.debug[pc], warnUser, "Trace") + case instr.opcode of opcEof: return regs[ra] of opcRet: @@ -407,8 +408,8 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg = decodeB(rkInt) regs[ra].intVal = regs[rb].intVal of opcAsgnStr: - decodeB(rkNode) - createStrKeepNode regs[ra] + decodeBC(rkNode) + createStrKeepNode regs[ra], rc != 0 regs[ra].node.strVal = regs[rb].node.strVal of opcAsgnFloat: decodeB(rkFloat) diff --git a/compiler/vmgen.nim b/compiler/vmgen.nim index 7abcbdb92f..1bee9788a6 100644 --- a/compiler/vmgen.nim +++ b/compiler/vmgen.nim @@ -74,8 +74,9 @@ proc codeListing(c: PCtx, result: var string, start=0; last = -1) = result.addf("\t$#\tr$#, L$#", ($opc).substr(3), x.regA, i+x.regBx-wordExcess) elif opc in {opcLdConst, opcAsgnConst}: - result.addf("\t$#\tr$#, $#", ($opc).substr(3), x.regA, - c.constants[x.regBx-wordExcess].renderTree) + let idx = x.regBx-wordExcess + result.addf("\t$#\tr$#, $# ($#)", ($opc).substr(3), x.regA, + c.constants[idx].renderTree, $idx) elif opc in {opcMarshalLoad, opcMarshalStore}: let y = c.code[i+1] result.addf("\t$#\tr$#, r$#, $#", ($opc).substr(3), x.regA, x.regB, @@ -184,7 +185,7 @@ proc getTemp(cc: PCtx; typ: PType): TRegister = return TRegister(i) # if register pressure is high, we re-use more aggressively: - if c.maxSlots >= HighRegisterPressure: + if c.maxSlots >= HighRegisterPressure and false: for i in 0 .. c.maxSlots-1: if not c.slots[i].inUse: c.slots[i] = (inUse: true, kind: k) @@ -1073,6 +1074,7 @@ const tyUInt, tyUInt8, tyUInt16, tyUInt32, tyUInt64} proc fitsRegister*(t: PType): bool = + assert t != nil t.skipTypes(abstractInst-{tyTypeDesc}).kind in { tyRange, tyEnum, tyBool, tyInt..tyUInt64, tyChar} @@ -1110,6 +1112,7 @@ proc genAddrDeref(c: PCtx; n: PNode; dest: var TDest; opc: TOpcode; if dest < 0: dest = c.getTemp(n.typ) if not isAddr: gABC(c, n, opc, dest, tmp) + assert n.typ != nil if gfAddrOf notin flags and fitsRegister(n.typ): c.gABC(n, opcNodeToReg, dest, dest) elif c.prc.slots[tmp].kind >= slotTempUnknown: @@ -1143,7 +1146,7 @@ proc whichAsgnOpc(n: PNode; opc: TOpcode): TOpcode = opc proc genAsgn(c: PCtx; dest: TDest; ri: PNode; requiresCopy: bool) = let tmp = c.genx(ri) assert dest >= 0 - gABC(c, ri, whichAsgnOpc(ri), dest, tmp) + gABC(c, ri, whichAsgnOpc(ri), dest, tmp, 1-ord(requiresCopy)) c.freeTemp(tmp) proc setSlot(c: PCtx; v: PSym) = @@ -1194,9 +1197,10 @@ proc preventFalseAlias(c: PCtx; n: PNode; opc: TOpcode; # 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! + assert n.typ != nil if needsAdditionalCopy(n): var cc = c.getTemp(n.typ) - c.gABC(n, whichAsgnOpc(n), cc, value) + c.gABC(n, whichAsgnOpc(n), cc, value, 0) c.gABC(n, opc, dest, idx, cc) c.freeTemp(cc) else: @@ -1241,10 +1245,11 @@ proc genAsgn(c: PCtx; le, ri: PNode; requiresCopy: bool) = internalAssert s.position > 0 or (s.position == 0 and s.kind in {skParam,skResult}) var dest: TRegister = s.position + ord(s.kind == skParam) + assert le.typ != nil 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.gABC(le, whichAsgnOpc(le), dest, cc, 1) c.freeTemp(cc) else: gen(c, ri, dest) @@ -1303,6 +1308,7 @@ proc genRdVar(c: PCtx; n: PNode; dest: var TDest; flags: TGenFlags) = if sfImportc in s.flags: c.importcSym(n.info, s) else: genGlobalInit(c, n, s) if dest < 0: dest = c.getTemp(n.typ) + assert s.typ != nil if gfAddrOf notin flags and fitsRegister(s.typ): var cc = c.getTemp(n.typ) c.gABx(n, opcLdGlobal, cc, s.position) @@ -1426,6 +1432,7 @@ proc getNullValue(typ: PType, info: TLineInfo): PNode = globalError(info, "cannot create null element for: " & $t.kind) proc ldNullOpcode(t: PType): TOpcode = + assert t != nil if fitsRegister(t): opcLdNullReg else: opcLdNull proc genVarSection(c: PCtx; n: PNode) = @@ -1453,7 +1460,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.preventFalseAlias(a, opcWrDeref, tmp, 0, val) + c.preventFalseAlias(a.sons[2], opcWrDeref, tmp, 0, val) c.freeTemp(val) c.freeTemp(tmp) else: @@ -1461,13 +1468,15 @@ 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: + assert s.typ != nil if not fitsRegister(s.typ): c.gABx(a, ldNullOpcode(s.typ), s.position, c.genType(s.typ)) let le = a.sons[0] + assert le.typ != nil 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.gABC(le, whichAsgnOpc(le), s.position.TRegister, cc, 1) c.freeTemp(cc) else: gen(c, a.sons[2], s.position.TRegister) @@ -1694,7 +1703,7 @@ proc gen(c: PCtx; n: PNode; dest: var TDest; flags: TGenFlags = {}) = c.freeTemp(tmp1) c.freeTemp(tmp2) if dest >= 0: - gABC(c, n, whichAsgnOpc(n), dest, tmp0) + gABC(c, n, whichAsgnOpc(n), dest, tmp0, 1) c.freeTemp(tmp0) else: dest = tmp0 From bd786812e7fd1cbd1b7a8f038000ac06ad08505e Mon Sep 17 00:00:00 2001 From: Araq Date: Sun, 2 Aug 2015 00:35:02 +0200 Subject: [PATCH 043/115] fixes #3171 --- compiler/pragmas.nim | 546 ++++++++++++++++--------------- tests/pragmas/tsym_as_pragma.nim | 8 + 2 files changed, 282 insertions(+), 272 deletions(-) create mode 100644 tests/pragmas/tsym_as_pragma.nim diff --git a/compiler/pragmas.nim b/compiler/pragmas.nim index c771155afb..128a90138a 100644 --- a/compiler/pragmas.nim +++ b/compiler/pragmas.nim @@ -591,280 +591,282 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: int, validPragmas: TSpecialWords): bool = var it = n.sons[i] var key = if it.kind == nkExprColonExpr: it.sons[0] else: it - if key.kind == nkIdent: - var userPragma = strTableGet(c.userPragmas, key.ident) - if userPragma != nil: - inc c.instCounter - if c.instCounter > 100: - globalError(it.info, errRecursiveDependencyX, userPragma.name.s) - pragma(c, sym, userPragma.ast, validPragmas) - # ensure the pragma is also remember for generic instantiations in other - # modules: - n.sons[i] = userPragma.ast - dec c.instCounter - else: - var k = whichKeyword(key.ident) - if k in validPragmas: - case k - of wExportc: - makeExternExport(sym, getOptionalStr(c, it, "$1"), it.info) - incl(sym.flags, sfUsed) # avoid wrong hints - of wImportc: makeExternImport(sym, getOptionalStr(c, it, "$1")) - of wImportCompilerProc: - processImportCompilerProc(sym, getOptionalStr(c, it, "$1")) - of wExtern: setExternName(sym, expectStrLit(c, it)) - of wImmediate: - if sym.kind in {skTemplate, skMacro}: incl(sym.flags, sfImmediate) - else: invalidPragma(it) - of wDirty: - if sym.kind == skTemplate: incl(sym.flags, sfDirty) - else: invalidPragma(it) - of wImportCpp: - processImportCpp(sym, getOptionalStr(c, it, "$1")) - of wImportObjC: - processImportObjC(sym, getOptionalStr(c, it, "$1")) - of wAlign: - if sym.typ == nil: invalidPragma(it) - var align = expectIntLit(c, it) - if (not isPowerOfTwo(align) and align != 0) or align >% high(int16): - localError(it.info, errPowerOfTwoExpected) - else: - sym.typ.align = align.int16 - of wSize: - if sym.typ == nil: invalidPragma(it) - var size = expectIntLit(c, it) - if not isPowerOfTwo(size) or size <= 0 or size > 8: - localError(it.info, errPowerOfTwoExpected) - else: - sym.typ.size = size - of wNodecl: - noVal(it) - incl(sym.loc.flags, lfNoDecl) - of wPure, wAsmNoStackFrame: - noVal(it) - if sym != nil: - if k == wPure and sym.kind in routineKinds: invalidPragma(it) - else: incl(sym.flags, sfPure) - of wVolatile: - noVal(it) - incl(sym.flags, sfVolatile) - of wRegister: - noVal(it) - incl(sym.flags, sfRegister) - of wThreadVar: - noVal(it) - incl(sym.flags, sfThread) - of wDeadCodeElim: pragmaDeadCodeElim(c, it) - of wNoForward: pragmaNoForward(c, it) - of wMagic: processMagic(c, it, sym) - of wCompileTime: - noVal(it) - incl(sym.flags, sfCompileTime) - incl(sym.loc.flags, lfNoDecl) - of wGlobal: - noVal(it) - incl(sym.flags, sfGlobal) - incl(sym.flags, sfPure) - of wMerge: - # only supported for backwards compat, doesn't do anything anymore - noVal(it) - of wConstructor: - noVal(it) - incl(sym.flags, sfConstructor) - of wHeader: - var lib = getLib(c, libHeader, getStrLitNode(c, it)) - addToLib(lib, sym) - incl(sym.flags, sfImportc) - incl(sym.loc.flags, lfHeader) - incl(sym.loc.flags, lfNoDecl) - # implies nodecl, because otherwise header would not make sense - if sym.loc.r == nil: sym.loc.r = rope(sym.name.s) - of wDestructor: - sym.flags.incl sfOverriden - if sym.name.s.normalize != "destroy": - localError(n.info, errGenerated, "destructor has to be named 'destroy'") - of wOverride: - sym.flags.incl sfOverriden - of wNosideeffect: - noVal(it) - incl(sym.flags, sfNoSideEffect) - if sym.typ != nil: incl(sym.typ.flags, tfNoSideEffect) - of wSideeffect: - noVal(it) - incl(sym.flags, sfSideEffect) - of wNoreturn: - noVal(it) - incl(sym.flags, sfNoReturn) - of wDynlib: - processDynLib(c, it, sym) - of wCompilerproc: - noVal(it) # compilerproc may not get a string! - if sfFromGeneric notin sym.flags: markCompilerProc(sym) - of wProcVar: - noVal(it) - incl(sym.flags, sfProcvar) - of wDeprecated: - if it.kind == nkExprColonExpr: deprecatedStmt(c, it) - elif sym != nil: incl(sym.flags, sfDeprecated) - else: incl(c.module.flags, sfDeprecated) - of wVarargs: - noVal(it) - if sym.typ == nil: invalidPragma(it) - else: incl(sym.typ.flags, tfVarargs) - of wBorrow: - if sym.kind == skType: - typeBorrow(sym, it) - else: - noVal(it) - incl(sym.flags, sfBorrow) - of wFinal: - noVal(it) - if sym.typ == nil: invalidPragma(it) - else: incl(sym.typ.flags, tfFinal) - of wInheritable: - noVal(it) - if sym.typ == nil or tfFinal in sym.typ.flags: invalidPragma(it) - else: incl(sym.typ.flags, tfInheritable) - of wAcyclic: - noVal(it) - if sym.typ == nil: invalidPragma(it) - else: incl(sym.typ.flags, tfAcyclic) - of wShallow: - noVal(it) - if sym.typ == nil: invalidPragma(it) - else: incl(sym.typ.flags, tfShallow) - of wThread: - noVal(it) - incl(sym.flags, sfThread) - incl(sym.flags, sfProcvar) - if sym.typ != nil: incl(sym.typ.flags, tfThread) - of wGcSafe: - noVal(it) - if sym.kind != skType: incl(sym.flags, sfThread) - if sym.typ != nil: incl(sym.typ.flags, tfGcSafe) - else: invalidPragma(it) - of wPacked: - noVal(it) - if sym.typ == nil: invalidPragma(it) - else: incl(sym.typ.flags, tfPacked) - of wHint: message(it.info, hintUser, expectStrLit(c, it)) - of wWarning: message(it.info, warnUser, expectStrLit(c, it)) - of wError: - if sym != nil and sym.isRoutine: - # This is subtle but correct: the error *statement* is only - # allowed for top level statements. Seems to be easier than - # distinguishing properly between - # ``proc p() {.error}`` and ``proc p() = {.error: "msg".}`` - noVal(it) - incl(sym.flags, sfError) - else: - localError(it.info, errUser, expectStrLit(c, it)) - of wFatal: fatal(it.info, errUser, expectStrLit(c, it)) - of wDefine: processDefine(c, it) - of wUndef: processUndef(c, it) - of wCompile: processCompile(c, it) - of wLink: processCommonLink(c, it, linkNormal) - of wLinksys: processCommonLink(c, it, linkSys) - of wPassl: extccomp.addLinkOption(expectStrLit(c, it)) - of wPassc: extccomp.addCompileOption(expectStrLit(c, it)) - of wBreakpoint: pragmaBreakpoint(c, it) - of wWatchPoint: pragmaWatchpoint(c, it) - of wPush: - processPush(c, n, i + 1) - result = true - of wPop: processPop(c, it) - of wPragma: - processPragma(c, n, i) - result = true - of wDiscardable: - noVal(it) - if sym != nil: incl(sym.flags, sfDiscardable) - of wNoInit: - noVal(it) - if sym != nil: incl(sym.flags, sfNoInit) - of wCodegenDecl: processCodegenDecl(c, it, sym) - of wChecks, wObjChecks, wFieldChecks, wRangechecks, wBoundchecks, - wOverflowchecks, wNilchecks, wAssertions, wWarnings, wHints, - wLinedir, wStacktrace, wLinetrace, wOptimization, - wCallconv, - wDebugger, wProfiler, wFloatchecks, wNanChecks, wInfChecks, - wPatterns: - if processOption(c, it): - # calling conventions (boring...): - localError(it.info, errOptionExpected) - of FirstCallConv..LastCallConv: - assert(sym != nil) - if sym.typ == nil: invalidPragma(it) - else: sym.typ.callConv = wordToCallConv(k) - of wEmit: pragmaEmit(c, it) - of wUnroll: pragmaUnroll(c, it) - of wLinearScanEnd, wComputedGoto: noVal(it) - of wEffects: - # is later processed in effect analysis: - noVal(it) - of wIncompleteStruct: - noVal(it) - if sym.typ == nil: invalidPragma(it) - else: incl(sym.typ.flags, tfIncompleteStruct) - of wUnchecked: - noVal(it) - if sym.typ == nil: invalidPragma(it) - else: incl(sym.typ.flags, tfUncheckedArray) - of wUnion: - noVal(it) - if sym.typ == nil: invalidPragma(it) - else: incl(sym.typ.flags, tfUnion) - of wRequiresInit: - noVal(it) - if sym.typ == nil: invalidPragma(it) - else: incl(sym.typ.flags, tfNeedsInit) - of wByRef: - noVal(it) - if sym == nil or sym.typ == nil: - if processOption(c, it): localError(it.info, errOptionExpected) - else: - incl(sym.typ.flags, tfByRef) - of wByCopy: - noVal(it) - if sym.kind != skType or sym.typ == nil: invalidPragma(it) - else: incl(sym.typ.flags, tfByCopy) - of wInject, wGensym: - # We check for errors, but do nothing with these pragmas otherwise - # as they are handled directly in 'evalTemplate'. - noVal(it) - if sym == nil: invalidPragma(it) - of wLine: pragmaLine(c, it) - of wRaises, wTags: pragmaRaisesOrTags(c, it) - of wLocks: - if sym == nil: pragmaLockStmt(c, it) - elif sym.typ == nil: invalidPragma(it) - else: sym.typ.lockLevel = pragmaLocks(c, it) - of wGuard: - if sym == nil or sym.kind notin {skVar, skLet, skField}: - invalidPragma(it) - else: - sym.guard = pragmaGuard(c, it, sym.kind) - of wGoto: - if sym == nil or sym.kind notin {skVar, skLet}: - invalidPragma(it) - else: - sym.flags.incl sfGoto - of wInjectStmt: - if it.kind != nkExprColonExpr: - localError(it.info, errExprExpected) - else: - it.sons[1] = c.semExpr(c, it.sons[1]) - of wExperimental: - noVal(it) - if isTopLevel(c): - c.module.flags.incl sfExperimental - else: - localError(it.info, "'experimental' pragma only valid as toplevel statement") - of wNoRewrite: - noVal(it) + if key.kind == nkBracketExpr: + processNote(c, it) + return + let ident = considerQuotedIdent(key) + var userPragma = strTableGet(c.userPragmas, ident) + if userPragma != nil: + inc c.instCounter + if c.instCounter > 100: + globalError(it.info, errRecursiveDependencyX, userPragma.name.s) + pragma(c, sym, userPragma.ast, validPragmas) + # ensure the pragma is also remember for generic instantiations in other + # modules: + n.sons[i] = userPragma.ast + dec c.instCounter + else: + var k = whichKeyword(ident) + if k in validPragmas: + case k + of wExportc: + makeExternExport(sym, getOptionalStr(c, it, "$1"), it.info) + incl(sym.flags, sfUsed) # avoid wrong hints + of wImportc: makeExternImport(sym, getOptionalStr(c, it, "$1")) + of wImportCompilerProc: + processImportCompilerProc(sym, getOptionalStr(c, it, "$1")) + of wExtern: setExternName(sym, expectStrLit(c, it)) + of wImmediate: + if sym.kind in {skTemplate, skMacro}: incl(sym.flags, sfImmediate) else: invalidPragma(it) + of wDirty: + if sym.kind == skTemplate: incl(sym.flags, sfDirty) + else: invalidPragma(it) + of wImportCpp: + processImportCpp(sym, getOptionalStr(c, it, "$1")) + of wImportObjC: + processImportObjC(sym, getOptionalStr(c, it, "$1")) + of wAlign: + if sym.typ == nil: invalidPragma(it) + var align = expectIntLit(c, it) + if (not isPowerOfTwo(align) and align != 0) or align >% high(int16): + localError(it.info, errPowerOfTwoExpected) + else: + sym.typ.align = align.int16 + of wSize: + if sym.typ == nil: invalidPragma(it) + var size = expectIntLit(c, it) + if not isPowerOfTwo(size) or size <= 0 or size > 8: + localError(it.info, errPowerOfTwoExpected) + else: + sym.typ.size = size + of wNodecl: + noVal(it) + incl(sym.loc.flags, lfNoDecl) + of wPure, wAsmNoStackFrame: + noVal(it) + if sym != nil: + if k == wPure and sym.kind in routineKinds: invalidPragma(it) + else: incl(sym.flags, sfPure) + of wVolatile: + noVal(it) + incl(sym.flags, sfVolatile) + of wRegister: + noVal(it) + incl(sym.flags, sfRegister) + of wThreadVar: + noVal(it) + incl(sym.flags, sfThread) + of wDeadCodeElim: pragmaDeadCodeElim(c, it) + of wNoForward: pragmaNoForward(c, it) + of wMagic: processMagic(c, it, sym) + of wCompileTime: + noVal(it) + incl(sym.flags, sfCompileTime) + incl(sym.loc.flags, lfNoDecl) + of wGlobal: + noVal(it) + incl(sym.flags, sfGlobal) + incl(sym.flags, sfPure) + of wMerge: + # only supported for backwards compat, doesn't do anything anymore + noVal(it) + of wConstructor: + noVal(it) + incl(sym.flags, sfConstructor) + of wHeader: + var lib = getLib(c, libHeader, getStrLitNode(c, it)) + addToLib(lib, sym) + incl(sym.flags, sfImportc) + incl(sym.loc.flags, lfHeader) + incl(sym.loc.flags, lfNoDecl) + # implies nodecl, because otherwise header would not make sense + if sym.loc.r == nil: sym.loc.r = rope(sym.name.s) + of wDestructor: + sym.flags.incl sfOverriden + if sym.name.s.normalize != "destroy": + localError(n.info, errGenerated, "destructor has to be named 'destroy'") + of wOverride: + sym.flags.incl sfOverriden + of wNosideeffect: + noVal(it) + incl(sym.flags, sfNoSideEffect) + if sym.typ != nil: incl(sym.typ.flags, tfNoSideEffect) + of wSideeffect: + noVal(it) + incl(sym.flags, sfSideEffect) + of wNoreturn: + noVal(it) + incl(sym.flags, sfNoReturn) + of wDynlib: + processDynLib(c, it, sym) + of wCompilerproc: + noVal(it) # compilerproc may not get a string! + if sfFromGeneric notin sym.flags: markCompilerProc(sym) + of wProcVar: + noVal(it) + incl(sym.flags, sfProcvar) + of wDeprecated: + if it.kind == nkExprColonExpr: deprecatedStmt(c, it) + elif sym != nil: incl(sym.flags, sfDeprecated) + else: incl(c.module.flags, sfDeprecated) + of wVarargs: + noVal(it) + if sym.typ == nil: invalidPragma(it) + else: incl(sym.typ.flags, tfVarargs) + of wBorrow: + if sym.kind == skType: + typeBorrow(sym, it) + else: + noVal(it) + incl(sym.flags, sfBorrow) + of wFinal: + noVal(it) + if sym.typ == nil: invalidPragma(it) + else: incl(sym.typ.flags, tfFinal) + of wInheritable: + noVal(it) + if sym.typ == nil or tfFinal in sym.typ.flags: invalidPragma(it) + else: incl(sym.typ.flags, tfInheritable) + of wAcyclic: + noVal(it) + if sym.typ == nil: invalidPragma(it) + else: incl(sym.typ.flags, tfAcyclic) + of wShallow: + noVal(it) + if sym.typ == nil: invalidPragma(it) + else: incl(sym.typ.flags, tfShallow) + of wThread: + noVal(it) + incl(sym.flags, sfThread) + incl(sym.flags, sfProcvar) + if sym.typ != nil: incl(sym.typ.flags, tfThread) + of wGcSafe: + noVal(it) + if sym.kind != skType: incl(sym.flags, sfThread) + if sym.typ != nil: incl(sym.typ.flags, tfGcSafe) + else: invalidPragma(it) + of wPacked: + noVal(it) + if sym.typ == nil: invalidPragma(it) + else: incl(sym.typ.flags, tfPacked) + of wHint: message(it.info, hintUser, expectStrLit(c, it)) + of wWarning: message(it.info, warnUser, expectStrLit(c, it)) + of wError: + if sym != nil and sym.isRoutine: + # This is subtle but correct: the error *statement* is only + # allowed for top level statements. Seems to be easier than + # distinguishing properly between + # ``proc p() {.error}`` and ``proc p() = {.error: "msg".}`` + noVal(it) + incl(sym.flags, sfError) + else: + localError(it.info, errUser, expectStrLit(c, it)) + of wFatal: fatal(it.info, errUser, expectStrLit(c, it)) + of wDefine: processDefine(c, it) + of wUndef: processUndef(c, it) + of wCompile: processCompile(c, it) + of wLink: processCommonLink(c, it, linkNormal) + of wLinksys: processCommonLink(c, it, linkSys) + of wPassl: extccomp.addLinkOption(expectStrLit(c, it)) + of wPassc: extccomp.addCompileOption(expectStrLit(c, it)) + of wBreakpoint: pragmaBreakpoint(c, it) + of wWatchPoint: pragmaWatchpoint(c, it) + of wPush: + processPush(c, n, i + 1) + result = true + of wPop: processPop(c, it) + of wPragma: + processPragma(c, n, i) + result = true + of wDiscardable: + noVal(it) + if sym != nil: incl(sym.flags, sfDiscardable) + of wNoInit: + noVal(it) + if sym != nil: incl(sym.flags, sfNoInit) + of wCodegenDecl: processCodegenDecl(c, it, sym) + of wChecks, wObjChecks, wFieldChecks, wRangechecks, wBoundchecks, + wOverflowchecks, wNilchecks, wAssertions, wWarnings, wHints, + wLinedir, wStacktrace, wLinetrace, wOptimization, + wCallconv, + wDebugger, wProfiler, wFloatchecks, wNanChecks, wInfChecks, + wPatterns: + if processOption(c, it): + # calling conventions (boring...): + localError(it.info, errOptionExpected) + of FirstCallConv..LastCallConv: + assert(sym != nil) + if sym.typ == nil: invalidPragma(it) + else: sym.typ.callConv = wordToCallConv(k) + of wEmit: pragmaEmit(c, it) + of wUnroll: pragmaUnroll(c, it) + of wLinearScanEnd, wComputedGoto: noVal(it) + of wEffects: + # is later processed in effect analysis: + noVal(it) + of wIncompleteStruct: + noVal(it) + if sym.typ == nil: invalidPragma(it) + else: incl(sym.typ.flags, tfIncompleteStruct) + of wUnchecked: + noVal(it) + if sym.typ == nil: invalidPragma(it) + else: incl(sym.typ.flags, tfUncheckedArray) + of wUnion: + noVal(it) + if sym.typ == nil: invalidPragma(it) + else: incl(sym.typ.flags, tfUnion) + of wRequiresInit: + noVal(it) + if sym.typ == nil: invalidPragma(it) + else: incl(sym.typ.flags, tfNeedsInit) + of wByRef: + noVal(it) + if sym == nil or sym.typ == nil: + if processOption(c, it): localError(it.info, errOptionExpected) + else: + incl(sym.typ.flags, tfByRef) + of wByCopy: + noVal(it) + if sym.kind != skType or sym.typ == nil: invalidPragma(it) + else: incl(sym.typ.flags, tfByCopy) + of wInject, wGensym: + # We check for errors, but do nothing with these pragmas otherwise + # as they are handled directly in 'evalTemplate'. + noVal(it) + if sym == nil: invalidPragma(it) + of wLine: pragmaLine(c, it) + of wRaises, wTags: pragmaRaisesOrTags(c, it) + of wLocks: + if sym == nil: pragmaLockStmt(c, it) + elif sym.typ == nil: invalidPragma(it) + else: sym.typ.lockLevel = pragmaLocks(c, it) + of wGuard: + if sym == nil or sym.kind notin {skVar, skLet, skField}: + invalidPragma(it) + else: + sym.guard = pragmaGuard(c, it, sym.kind) + of wGoto: + if sym == nil or sym.kind notin {skVar, skLet}: + invalidPragma(it) + else: + sym.flags.incl sfGoto + of wInjectStmt: + if it.kind != nkExprColonExpr: + localError(it.info, errExprExpected) + else: + it.sons[1] = c.semExpr(c, it.sons[1]) + of wExperimental: + noVal(it) + if isTopLevel(c): + c.module.flags.incl sfExperimental + else: + localError(it.info, "'experimental' pragma only valid as toplevel statement") + of wNoRewrite: + noVal(it) else: invalidPragma(it) - else: processNote(c, it) + else: invalidPragma(it) proc implicitPragmas*(c: PContext, sym: PSym, n: PNode, validPragmas: TSpecialWords) = diff --git a/tests/pragmas/tsym_as_pragma.nim b/tests/pragmas/tsym_as_pragma.nim new file mode 100644 index 0000000000..f6ba44dc9d --- /dev/null +++ b/tests/pragmas/tsym_as_pragma.nim @@ -0,0 +1,8 @@ + +# bug #3171 + +template newDataWindow(): stmt = + let eventClosure = proc (closure: pointer): bool {.closure, cdecl.} = + discard + +newDataWindow() From 5e325bc9f9be68841ce1aef6b64aada2aeb52918 Mon Sep 17 00:00:00 2001 From: Araq Date: Sun, 2 Aug 2015 01:16:47 +0200 Subject: [PATCH 044/115] made test green again --- tests/template/t_otemplates.nim | 678 ++++++++++++++++---------------- 1 file changed, 339 insertions(+), 339 deletions(-) diff --git a/tests/template/t_otemplates.nim b/tests/template/t_otemplates.nim index db535d818b..6c419f72f2 100644 --- a/tests/template/t_otemplates.nim +++ b/tests/template/t_otemplates.nim @@ -2,344 +2,344 @@ discard """ output: "Success" """ -# Ref: -# http://nim-lang.org/macros.html -# http://nim-lang.org/parseutils.html - - -# Imports -import tables, parseutils, macros, strutils -import annotate -export annotate - - -# Fields -const identChars = {'a'..'z', 'A'..'Z', '0'..'9', '_'} - - -# Procedure Declarations -proc parse_template(node: NimNode, value: string) {.compiletime.} - - -# Procedure Definitions -proc substring(value: string, index: int, length = -1): string {.compiletime.} = - ## Returns a string at most `length` characters long, starting at `index`. - return if length < 0: value.substr(index) - elif length == 0: "" - else: value.substr(index, index + length-1) - - -proc parse_thru_eol(value: string, index: int): int {.compiletime.} = - ## Reads until and past the end of the current line, unless - ## a non-whitespace character is encountered first - var remainder: string - var read = value.parseUntil(remainder, {0x0A.char}, index) - if remainder.skipWhitespace() == read: - return read + 1 - - -proc trim_after_eol(value: var string) {.compiletime.} = - ## Trims any whitespace at end after \n - var toTrim = 0 - for i in countdown(value.len-1, 0): - # If \n, return - if value[i] in [' ', '\t']: inc(toTrim) - else: break - - if toTrim > 0: - value = value.substring(0, value.len - toTrim) - - -proc trim_eol(value: var string) {.compiletime.} = - ## Removes everything after the last line if it contains nothing but whitespace - for i in countdown(value.len - 1, 0): - # If \n, trim and return - if value[i] == 0x0A.char: - value = value.substr(0, i) - break - - # This is the first character - if i == 0: - value = "" - break - - # Skip change - if not (value[i] in [' ', '\t']): break - - -proc detect_indent(value: string, index: int): int {.compiletime.} = - ## Detects how indented the line at `index` is. - # Seek to the beginning of the line. - var lastChar = index - for i in countdown(index, 0): - if value[i] == 0x0A.char: - # if \n, return the indentation level - return lastChar - i - elif not (value[i] in [' ', '\t']): - # if non-whitespace char, decrement lastChar - dec(lastChar) - - -proc parse_thru_string(value: string, i: var int, strType = '"') {.compiletime.} = - ## Parses until ending " or ' is reached. - inc(i) - if i < value.len-1: - inc(i, value.skipUntil({'\\', strType}, i)) - - -proc parse_to_close(value: string, index: int, open='(', close=')', opened=0): int {.compiletime.} = - ## Reads until all opened braces are closed - ## ignoring any strings "" or '' - var remainder = value.substring(index) - var open_braces = opened - result = 0 - - while result < remainder.len: - var c = remainder[result] - - if c == open: inc(open_braces) - elif c == close: dec(open_braces) - elif c == '"': remainder.parse_thru_string(result) - elif c == '\'': remainder.parse_thru_string(result, '\'') - - if open_braces == 0: break - else: inc(result) - - -iterator parse_stmt_list(value: string, index: var int): string = - ## Parses unguided ${..} block - var read = value.parse_to_close(index, open='{', close='}') - var expressions = value.substring(index + 1, read - 1).split({ ';', 0x0A.char }) - - for expression in expressions: - let value = expression.strip - if value.len > 0: - yield value - - #Increment index & parse thru EOL - inc(index, read + 1) - inc(index, value.parse_thru_eol(index)) - - -iterator parse_compound_statements(value, identifier: string, index: int): string = - ## Parses through several statements, i.e. if {} elif {} else {} - ## and returns the initialization of each as an empty statement - ## i.e. if x == 5 { ... } becomes if x == 5: nil. - - template get_next_ident(expected): stmt = - var nextIdent: string - discard value.parseWhile(nextIdent, {'$'} + identChars, i) - - var next: string - var read: int - - if nextIdent == "case": - # We have to handle case a bit differently - read = value.parseUntil(next, '$', i) - inc(i, read) - yield next.strip(leading=false) & "\n" - - else: - read = value.parseUntil(next, '{', i) - - if nextIdent in expected: - inc(i, read) - # Parse until closing }, then skip whitespace afterwards - read = value.parse_to_close(i, open='{', close='}') - inc(i, read + 1) - inc(i, value.skipWhitespace(i)) - yield next & ": nil\n" - - else: break - - - var i = index - while true: - # Check if next statement would be valid, given the identifier - if identifier in ["if", "when"]: - get_next_ident([identifier, "$elif", "$else"]) - - elif identifier == "case": - get_next_ident(["case", "$of", "$elif", "$else"]) - - elif identifier == "try": - get_next_ident(["try", "$except", "$finally"]) - - -proc parse_complex_stmt(value, identifier: string, index: var int): NimNode {.compiletime.} = - ## Parses if/when/try /elif /else /except /finally statements - - # Build up complex statement string - var stmtString = newString(0) - var numStatements = 0 - for statement in value.parse_compound_statements(identifier, index): - if statement[0] == '$': stmtString.add(statement.substr(1)) - else: stmtString.add(statement) - inc(numStatements) - - # Parse stmt string - result = parseExpr(stmtString) - - var resultIndex = 0 - - # Fast forward a bit if this is a case statement - if identifier == "case": - inc(resultIndex) - - while resultIndex < numStatements: - - # Detect indentation - let indent = detect_indent(value, index) - - # Parse until an open brace `{` - var read = value.skipUntil('{', index) - inc(index, read + 1) - - # Parse through EOL - inc(index, value.parse_thru_eol(index)) - - # Parse through { .. } - read = value.parse_to_close(index, open='{', close='}', opened=1) - - # Add parsed sub-expression into body - var body = newStmtList() - var stmtString = value.substring(index, read) - trim_after_eol(stmtString) - stmtString = reindent(stmtString, indent) - parse_template(body, stmtString) - inc(index, read + 1) - - # Insert body into result - var stmtIndex = macros.high(result[resultIndex]) - result[resultIndex][stmtIndex] = body - - # Parse through EOL again & increment result index - inc(index, value.parse_thru_eol(index)) - inc(resultIndex) - - -proc parse_simple_statement(value: string, index: var int): NimNode {.compiletime.} = - ## Parses for/while - - # Detect indentation - let indent = detect_indent(value, index) - - # Parse until an open brace `{` - var splitValue: string - var read = value.parseUntil(splitValue, '{', index) - result = parseExpr(splitValue & ":nil") - inc(index, read + 1) - - # Parse through EOL - inc(index, value.parse_thru_eol(index)) - - # Parse through { .. } - read = value.parse_to_close(index, open='{', close='}', opened=1) - - # Add parsed sub-expression into body - var body = newStmtList() - var stmtString = value.substring(index, read) - trim_after_eol(stmtString) - stmtString = reindent(stmtString, indent) - parse_template(body, stmtString) - inc(index, read + 1) - - # Insert body into result - var stmtIndex = macros.high(result) - result[stmtIndex] = body - - # Parse through EOL again - inc(index, value.parse_thru_eol(index)) - - -proc parse_until_symbol(node: NimNode, value: string, index: var int): bool {.compiletime.} = - ## Parses a string until a $ symbol is encountered, if - ## two $$'s are encountered in a row, a split will happen - ## removing one of the $'s from the resulting output - var splitValue: string - var read = value.parseUntil(splitValue, '$', index) - var insertionPoint = node.len - - inc(index, read + 1) - if index < value.len: - - case value[index] - of '$': - # Check for duplicate `$`, meaning this is an escaped $ - node.add newCall("add", ident("result"), newStrLitNode("$")) - inc(index) - - of '(': - # Check for open `(`, which means parse as simple single-line expression. - trim_eol(splitValue) - read = value.parse_to_close(index) + 1 - node.add newCall("add", ident("result"), - newCall(bindSym"strip", parseExpr("$" & value.substring(index, read))) - ) - inc(index, read) - - of '{': - # Check for open `{`, which means open statement list - trim_eol(splitValue) - for s in value.parse_stmt_list(index): - node.add parseExpr(s) - - else: - # Otherwise parse while valid `identChars` and make expression w/ $ - var identifier: string - read = value.parseWhile(identifier, identChars, index) - - if identifier in ["for", "while"]: - ## for/while means open simple statement - trim_eol(splitValue) - node.add value.parse_simple_statement(index) - - elif identifier in ["if", "when", "case", "try"]: - ## if/when/case/try means complex statement - trim_eol(splitValue) - node.add value.parse_complex_stmt(identifier, index) - - elif identifier.len > 0: - ## Treat as simple variable - node.add newCall("add", ident("result"), newCall("$", ident(identifier))) - inc(index, read) - - result = true - - # Insert - if splitValue.len > 0: - node.insert insertionPoint, newCall("add", ident("result"), newStrLitNode(splitValue)) - - -proc parse_template(node: NimNode, value: string) = - ## Parses through entire template, outputing valid - ## Nim code into the input `node` AST. - var index = 0 - while index < value.len and - parse_until_symbol(node, value, index): nil - - -macro tmpli*(body: expr): stmt = - result = newStmtList() - - result.add parseExpr("result = \"\"") - - var value = if body.kind in nnkStrLit..nnkTripleStrLit: body.strVal - else: body[1].strVal - - parse_template(result, reindent(value)) - - -macro tmpl*(body: expr): stmt = - result = newStmtList() - - var value = if body.kind in nnkStrLit..nnkTripleStrLit: body.strVal - else: body[1].strVal - - parse_template(result, reindent(value)) - - -# Run tests -when isMainModule: +# Ref: +# http://nim-lang.org/macros.html +# http://nim-lang.org/parseutils.html + + +# Imports +import tables, parseutils, macros, strutils +import annotate +export annotate + + +# Fields +const identChars = {'a'..'z', 'A'..'Z', '0'..'9', '_'} + + +# Procedure Declarations +proc parse_template(node: NimNode, value: string) {.compiletime.} + + +# Procedure Definitions +proc substring(value: string, index: int, length = -1): string {.compiletime.} = + ## Returns a string at most `length` characters long, starting at `index`. + return if length < 0: value.substr(index) + elif length == 0: "" + else: value.substr(index, index + length-1) + + +proc parse_thru_eol(value: string, index: int): int {.compiletime.} = + ## Reads until and past the end of the current line, unless + ## a non-whitespace character is encountered first + var remainder: string + var read = value.parseUntil(remainder, {0x0A.char}, index) + if remainder.skipWhitespace() == read: + return read + 1 + + +proc trim_after_eol(value: var string) {.compiletime.} = + ## Trims any whitespace at end after \n + var toTrim = 0 + for i in countdown(value.len-1, 0): + # If \n, return + if value[i] in [' ', '\t']: inc(toTrim) + else: break + + if toTrim > 0: + value = value.substring(0, value.len - toTrim) + + +proc trim_eol(value: var string) {.compiletime.} = + ## Removes everything after the last line if it contains nothing but whitespace + for i in countdown(value.len - 1, 0): + # If \n, trim and return + if value[i] == 0x0A.char: + value = value.substr(0, i) + break + + # This is the first character + if i == 0: + value = "" + break + + # Skip change + if not (value[i] in [' ', '\t']): break + + +proc detect_indent(value: string, index: int): int {.compiletime.} = + ## Detects how indented the line at `index` is. + # Seek to the beginning of the line. + var lastChar = index + for i in countdown(index, 0): + if value[i] == 0x0A.char: + # if \n, return the indentation level + return lastChar - i + elif not (value[i] in [' ', '\t']): + # if non-whitespace char, decrement lastChar + dec(lastChar) + + +proc parse_thru_string(value: string, i: var int, strType = '"') {.compiletime.} = + ## Parses until ending " or ' is reached. + inc(i) + if i < value.len-1: + inc(i, value.skipUntil({'\\', strType}, i)) + + +proc parse_to_close(value: string, index: int, open='(', close=')', opened=0): int {.compiletime.} = + ## Reads until all opened braces are closed + ## ignoring any strings "" or '' + var remainder = value.substring(index) + var open_braces = opened + result = 0 + + while result < remainder.len: + var c = remainder[result] + + if c == open: inc(open_braces) + elif c == close: dec(open_braces) + elif c == '"': remainder.parse_thru_string(result) + elif c == '\'': remainder.parse_thru_string(result, '\'') + + if open_braces == 0: break + else: inc(result) + + +iterator parse_stmt_list(value: string, index: var int): string = + ## Parses unguided ${..} block + var read = value.parse_to_close(index, open='{', close='}') + var expressions = value.substring(index + 1, read - 1).split({ ';', 0x0A.char }) + + for expression in expressions: + let value = expression.strip + if value.len > 0: + yield value + + #Increment index & parse thru EOL + inc(index, read + 1) + inc(index, value.parse_thru_eol(index)) + + +iterator parse_compound_statements(value, identifier: string, index: int): string = + ## Parses through several statements, i.e. if {} elif {} else {} + ## and returns the initialization of each as an empty statement + ## i.e. if x == 5 { ... } becomes if x == 5: nil. + + template get_next_ident(expected): stmt = + var nextIdent: string + discard value.parseWhile(nextIdent, {'$'} + identChars, i) + + var next: string + var read: int + + if nextIdent == "case": + # We have to handle case a bit differently + read = value.parseUntil(next, '$', i) + inc(i, read) + yield next.strip(leading=false) & "\n" + + else: + read = value.parseUntil(next, '{', i) + + if nextIdent in expected: + inc(i, read) + # Parse until closing }, then skip whitespace afterwards + read = value.parse_to_close(i, open='{', close='}') + inc(i, read + 1) + inc(i, value.skipWhitespace(i)) + yield next & ": nil\n" + + else: break + + + var i = index + while true: + # Check if next statement would be valid, given the identifier + if identifier in ["if", "when"]: + get_next_ident([identifier, "$elif", "$else"]) + + elif identifier == "case": + get_next_ident(["case", "$of", "$elif", "$else"]) + + elif identifier == "try": + get_next_ident(["try", "$except", "$finally"]) + + +proc parse_complex_stmt(value, identifier: string, index: var int): NimNode {.compiletime.} = + ## Parses if/when/try /elif /else /except /finally statements + + # Build up complex statement string + var stmtString = newString(0) + var numStatements = 0 + for statement in value.parse_compound_statements(identifier, index): + if statement[0] == '$': stmtString.add(statement.substr(1)) + else: stmtString.add(statement) + inc(numStatements) + + # Parse stmt string + result = parseExpr(stmtString) + + var resultIndex = 0 + + # Fast forward a bit if this is a case statement + if identifier == "case": + inc(resultIndex) + + while resultIndex < numStatements: + + # Detect indentation + let indent = detect_indent(value, index) + + # Parse until an open brace `{` + var read = value.skipUntil('{', index) + inc(index, read + 1) + + # Parse through EOL + inc(index, value.parse_thru_eol(index)) + + # Parse through { .. } + read = value.parse_to_close(index, open='{', close='}', opened=1) + + # Add parsed sub-expression into body + var body = newStmtList() + var stmtString = value.substring(index, read) + trim_after_eol(stmtString) + stmtString = reindent(stmtString, indent) + parse_template(body, stmtString) + inc(index, read + 1) + + # Insert body into result + var stmtIndex = result[resultIndex].len-1 + result[resultIndex][stmtIndex] = body + + # Parse through EOL again & increment result index + inc(index, value.parse_thru_eol(index)) + inc(resultIndex) + + +proc parse_simple_statement(value: string, index: var int): NimNode {.compiletime.} = + ## Parses for/while + + # Detect indentation + let indent = detect_indent(value, index) + + # Parse until an open brace `{` + var splitValue: string + var read = value.parseUntil(splitValue, '{', index) + result = parseExpr(splitValue & ":nil") + inc(index, read + 1) + + # Parse through EOL + inc(index, value.parse_thru_eol(index)) + + # Parse through { .. } + read = value.parse_to_close(index, open='{', close='}', opened=1) + + # Add parsed sub-expression into body + var body = newStmtList() + var stmtString = value.substring(index, read) + trim_after_eol(stmtString) + stmtString = reindent(stmtString, indent) + parse_template(body, stmtString) + inc(index, read + 1) + + # Insert body into result + var stmtIndex = result.len-1 + result[stmtIndex] = body + + # Parse through EOL again + inc(index, value.parse_thru_eol(index)) + + +proc parse_until_symbol(node: NimNode, value: string, index: var int): bool {.compiletime.} = + ## Parses a string until a $ symbol is encountered, if + ## two $$'s are encountered in a row, a split will happen + ## removing one of the $'s from the resulting output + var splitValue: string + var read = value.parseUntil(splitValue, '$', index) + var insertionPoint = node.len + + inc(index, read + 1) + if index < value.len: + + case value[index] + of '$': + # Check for duplicate `$`, meaning this is an escaped $ + node.add newCall("add", ident("result"), newStrLitNode("$")) + inc(index) + + of '(': + # Check for open `(`, which means parse as simple single-line expression. + trim_eol(splitValue) + read = value.parse_to_close(index) + 1 + node.add newCall("add", ident("result"), + newCall(bindSym"strip", parseExpr("$" & value.substring(index, read))) + ) + inc(index, read) + + of '{': + # Check for open `{`, which means open statement list + trim_eol(splitValue) + for s in value.parse_stmt_list(index): + node.add parseExpr(s) + + else: + # Otherwise parse while valid `identChars` and make expression w/ $ + var identifier: string + read = value.parseWhile(identifier, identChars, index) + + if identifier in ["for", "while"]: + ## for/while means open simple statement + trim_eol(splitValue) + node.add value.parse_simple_statement(index) + + elif identifier in ["if", "when", "case", "try"]: + ## if/when/case/try means complex statement + trim_eol(splitValue) + node.add value.parse_complex_stmt(identifier, index) + + elif identifier.len > 0: + ## Treat as simple variable + node.add newCall("add", ident("result"), newCall("$", ident(identifier))) + inc(index, read) + + result = true + + # Insert + if splitValue.len > 0: + node.insert insertionPoint, newCall("add", ident("result"), newStrLitNode(splitValue)) + + +proc parse_template(node: NimNode, value: string) = + ## Parses through entire template, outputing valid + ## Nim code into the input `node` AST. + var index = 0 + while index < value.len and + parse_until_symbol(node, value, index): nil + + +macro tmpli*(body: expr): stmt = + result = newStmtList() + + result.add parseExpr("result = \"\"") + + var value = if body.kind in nnkStrLit..nnkTripleStrLit: body.strVal + else: body[1].strVal + + parse_template(result, reindent(value)) + + +macro tmpl*(body: expr): stmt = + result = newStmtList() + + var value = if body.kind in nnkStrLit..nnkTripleStrLit: body.strVal + else: body[1].strVal + + parse_template(result, reindent(value)) + + +# Run tests +when isMainModule: include otests echo "Success" From 41e8c9b9acc16f65166648aaba38408dad42e7bc Mon Sep 17 00:00:00 2001 From: Araq Date: Sun, 2 Aug 2015 01:17:23 +0200 Subject: [PATCH 045/115] fixes #3158 --- compiler/sem.nim | 6 +++++- tests/template/twrongsymkind.nim | 20 ++++++++++++++++++++ 2 files changed, 25 insertions(+), 1 deletion(-) create mode 100644 tests/template/twrongsymkind.nim diff --git a/compiler/sem.nim b/compiler/sem.nim index d23dd1543a..041524f848 100644 --- a/compiler/sem.nim +++ b/compiler/sem.nim @@ -171,11 +171,15 @@ proc newSymS(kind: TSymKind, n: PNode, c: PContext): PSym = result = newSym(kind, considerQuotedIdent(n), getCurrOwner(), n.info) proc newSymG*(kind: TSymKind, n: PNode, c: PContext): PSym = + proc `$`(kind: TSymKind): string = substr(system.`$`(kind), 2).toLower + # like newSymS, but considers gensym'ed symbols if n.kind == nkSym: # and sfGenSym in n.sym.flags: result = n.sym - internalAssert result.kind == kind + if result.kind != kind: + localError(n.info, "cannot use symbol of kind '" & + $result.kind & "' as a '" & $kind & "'") # when there is a nested proc inside a template, semtmpl # will assign a wrong owner during the first pass over the # template; we must fix it here: see #909 diff --git a/tests/template/twrongsymkind.nim b/tests/template/twrongsymkind.nim new file mode 100644 index 0000000000..be3d8c6523 --- /dev/null +++ b/tests/template/twrongsymkind.nim @@ -0,0 +1,20 @@ +discard """ + errormsg: "cannot use symbol of kind 'var' as a 'param'" + line: 20 +""" + +# bug #3158 + +type + MyData = object + x: int + +template newDataWindow(data: ref MyData): stmt = + proc testProc(data: ref MyData) = + echo "Hello, ", data.x + testProc(data) + +var d: ref MyData +new(d) +d.x = 10 +newDataWindow(d) From e9899af9b20b1ac55c66b32a8ab354942ef2816f Mon Sep 17 00:00:00 2001 From: Araq Date: Sun, 2 Aug 2015 01:42:41 +0200 Subject: [PATCH 046/115] fixes #3135 --- lib/system/sets.nim | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/system/sets.nim b/lib/system/sets.nim index 22d6d57c0f..66877de307 100644 --- a/lib/system/sets.nim +++ b/lib/system/sets.nim @@ -19,9 +19,9 @@ proc countBits32(n: int32): int {.compilerproc.} = v = (v and 0x33333333'i32) +% ((v shr 2'i32) and 0x33333333'i32) result = ((v +% (v shr 4'i32) and 0xF0F0F0F'i32) *% 0x1010101'i32) shr 24'i32 -proc countBits64(n: int64): int {.compilerproc.} = - result = countBits32(toU32(n and 0xffff'i64)) + - countBits32(toU32(n shr 16'i64)) +proc countBits64(n: int64): int {.compilerproc.} = + result = countBits32(toU32(n and 0xffffffff'i64)) + + countBits32(toU32(n shr 32'i64)) proc cardSet(s: NimSet, len: int): int {.compilerproc.} = result = 0 From ea35cede8e99b936c4a3003640844335001fe0b3 Mon Sep 17 00:00:00 2001 From: Araq Date: Sun, 2 Aug 2015 01:51:06 +0200 Subject: [PATCH 047/115] fixes #3103 --- compiler/vm.nim | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/compiler/vm.nim b/compiler/vm.nim index 3166ba726d..d7495d77f1 100644 --- a/compiler/vm.nim +++ b/compiler/vm.nim @@ -510,8 +510,10 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg = of rkNode: if regs[rb].node.kind == nkNilLit: stackTrace(c, tos, pc, errNilAccess) - assert regs[rb].node.kind == nkRefTy - regs[ra].node = regs[rb].node.sons[0] + if regs[rb].node.kind == nkRefTy: + regs[ra].node = regs[rb].node.sons[0] + else: + stackTrace(c, tos, pc, errGenerated, "limited VM support for 'ref'") else: stackTrace(c, tos, pc, errNilAccess) of opcWrDeref: From f9750781c04cf0f2f278ef4ce8734ba9bc3e859c Mon Sep 17 00:00:00 2001 From: Araq Date: Sun, 2 Aug 2015 02:16:45 +0200 Subject: [PATCH 048/115] fixes #3080 --- lib/system/repr.nim | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/lib/system/repr.nim b/lib/system/repr.nim index 74396f4243..b4188527fe 100644 --- a/lib/system/repr.nim +++ b/lib/system/repr.nim @@ -256,7 +256,10 @@ when not defined(useNimRtl): of tyBool: add result, reprBool(cast[ptr bool](p)[]) of tyChar: add result, reprChar(cast[ptr char](p)[]) of tyString: reprStrAux(result, cast[ptr string](p)[]) - of tyCString: reprStrAux(result, $(cast[ptr cstring](p)[])) + of tyCString: + let cs = cast[ptr cstring](p)[] + if cs.isNil: add result, "nil" + else: reprStrAux(result, $cs) of tyRange: reprAux(result, p, typ.base, cl) of tyProc, tyPointer: if cast[PPointer](p)[] == nil: add result, "nil" From 1b4116702b411c82ccec4b558768689b2a1a0964 Mon Sep 17 00:00:00 2001 From: Araq Date: Sun, 2 Aug 2015 02:54:43 +0200 Subject: [PATCH 049/115] fixes #3052 --- compiler/semtypes.nim | 3 +++ 1 file changed, 3 insertions(+) diff --git a/compiler/semtypes.nim b/compiler/semtypes.nim index b518f0fb98..3bb1284fc0 100644 --- a/compiler/semtypes.nim +++ b/compiler/semtypes.nim @@ -23,6 +23,9 @@ proc newConstraint(c: PContext, k: TTypeKind): PType = proc semEnum(c: PContext, n: PNode, prev: PType): PType = if n.sonsLen == 0: return newConstraint(c, tyEnum) + elif n.sonsLen == 1: + # don't create an empty tyEnum; fixes #3052 + return errorType(c) var counter, x: BiggestInt e: PSym From eac484167c7b9d6115133bc936993ae7534cd4e9 Mon Sep 17 00:00:00 2001 From: Nycto Date: Sat, 1 Aug 2015 18:48:41 -0700 Subject: [PATCH 050/115] Fix multiple requires in a test --- lib/pure/unittest.nim | 2 +- tests/stdlib/tunittest.nim | 5 +++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/lib/pure/unittest.nim b/lib/pure/unittest.nim index c459023a96..064937ad8b 100644 --- a/lib/pure/unittest.nim +++ b/lib/pure/unittest.nim @@ -285,7 +285,7 @@ macro check*(conditions: stmt): stmt {.immediate.} = result = getAst(rewrite(checked, checked.lineinfo, checked.toStrLit)) -template require*(conditions: stmt): stmt {.immediate, dirty.} = +template require*(conditions: stmt): stmt {.immediate.} = ## Same as `check` except any failed test causes the program to quit ## immediately. Any teardown statements are not executed and the failed ## test output is not generated. diff --git a/tests/stdlib/tunittest.nim b/tests/stdlib/tunittest.nim index fb9b022438..1389214ea5 100644 --- a/tests/stdlib/tunittest.nim +++ b/tests/stdlib/tunittest.nim @@ -21,6 +21,11 @@ test "unittest typedescs": check(none(int) != some(1)) +test "unittest multiple requires": + require(true) + require(true) + + import math from strutils import parseInt proc defectiveRobot() = From fc54fbfb986be7ec2ec02ac27afe273ad7a28d36 Mon Sep 17 00:00:00 2001 From: Charles Blake Date: Sun, 2 Aug 2015 05:28:21 -0400 Subject: [PATCH 051/115] Nix unchecked which has no semantics for objects. --- lib/pure/memfiles.nim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/pure/memfiles.nim b/lib/pure/memfiles.nim index ccd45ab2ba..5113197f20 100644 --- a/lib/pure/memfiles.nim +++ b/lib/pure/memfiles.nim @@ -245,7 +245,7 @@ proc close*(f: var MemFile) = if error: raiseOSError(lastErr) -type MemSlice* {.unchecked.} = object +type MemSlice* = object data*: pointer size*: int From a9d09f28685aad5b4630bb3efd1276633419638b Mon Sep 17 00:00:00 2001 From: Charles Blake Date: Sun, 2 Aug 2015 12:03:11 -0400 Subject: [PATCH 052/115] Revert toNimStr fix as per Araq request. --- lib/system/sysstr.nim | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/lib/system/sysstr.nim b/lib/system/sysstr.nim index 9c98f84166..326c601bda 100644 --- a/lib/system/sysstr.nim +++ b/lib/system/sysstr.nim @@ -82,8 +82,7 @@ proc copyStr(s: NimString, start: int): NimString {.compilerProc.} = proc toNimStr(str: cstring, len: int): NimString {.compilerProc.} = result = rawNewStringNoInit(len) result.len = len - c_memcpy(result.data, str, len) - result.data[len] = '\0' + c_memcpy(result.data, str, len + 1) proc cstrToNimstr(str: cstring): NimString {.compilerRtl.} = result = toNimStr(str, c_strlen(str)) From 2bcd4e9a38e7d6d532fde43f9ee777a4363d383f Mon Sep 17 00:00:00 2001 From: Charles Blake Date: Sun, 2 Aug 2015 12:03:52 -0400 Subject: [PATCH 053/115] Add top of module comment. Re-order definitions to type, then converter, then 3 iterators lowest- to highest-level (also fastest to slowest) including a new intermediate iterator lines(MemFile, buffer) that is more like readLine(File) in case that helps anyone port code. Add doc comments. Also have toString just use newString+c_memcpy instead of currently fragile toNimStr which Araq wants a separate PR for. --- lib/pure/memfiles.nim | 40 +++++++++++++++++++++++++++++++++++----- 1 file changed, 35 insertions(+), 5 deletions(-) diff --git a/lib/pure/memfiles.nim b/lib/pure/memfiles.nim index 5113197f20..51f3662de4 100644 --- a/lib/pure/memfiles.nim +++ b/lib/pure/memfiles.nim @@ -11,6 +11,9 @@ ## ## This module provides support for `memory mapped files`:idx: ## (Posix's `mmap`:idx:) on the different operating systems. +## +## It also provides some fast iterators over lines in text files +## delimited in Unix or Windows styles (or similarly delimited records). when defined(windows): import winlean @@ -249,7 +252,23 @@ type MemSlice* = object data*: pointer size*: int +proc c_memcpy(a, b: pointer, n: int) {.importc: "memcpy", header: "".} + +proc toString*(ms: MemSlice): string {.inline.} = + ## Return a Nim string built from a MemSlice. + var buf = newString(ms.size) + c_memcpy(addr(buf[0]), ms.data, ms.size) + buf[ms.size] = '\0' + result = buf + iterator memSlices*(mfile: MemFile, delim='\l', eat='\r'): MemSlice {.inline.} = + ## Iterates over [optional eat]delim-delimited slices in a MemFile. + ## Default delimiting is [\r]\l which parse Unix or Windows text file lines. + ## Pass eat='\0' to be strictly delim-delimited. + ## This zero copy, memchr-limited method is probably the fastest way to + ## iterate through lines in a file, however the returned (data,size) objects + ## are NOT Nim strings or even terminated C strings. So, be careful how data + ## is accessed (e.g., use C mem* functions, not str* functions). proc c_memchr(cstr: pointer, c: char, n: csize): pointer {. importc: "memchr", header: "" .} proc `-!`(p, q: pointer): int {.inline.} = return cast[int](p) -% cast[int](q) @@ -270,9 +289,20 @@ iterator memSlices*(mfile: MemFile, delim='\l', eat='\r'): MemSlice {.inline.} = ms.data = cast[pointer](cast[int](ending) +% 1) # skip delim remaining = mfile.size - (ms.data -! mfile.mem) -proc toString*(ms: MemSlice): string {.inline.} = - proc toNimStr(str: cstring, len: int): string {. importc: "toNimStr" .} - result = toNimStr(cast[cstring](ms.data), ms.size) +iterator lines*(mfile: MemFile, buf: var TaintedString, delim='\l', eat='\r'): TaintedString {.inline.} = + ## Replace contents of passed buffer with each new line, like readLine(File). + ## Default delimiting is [\r]\l which parse Unix or Windows text file lines. + ## Pass eat='\0' to be strictly delim-delimited. + for ms in memSlices(mfile, delim, eat): + buf.setLen(ms.size) + c_memcpy(addr(buf[0]), ms.data, ms.size) + buf[ms.size] = '\0' + yield buf -iterator lines*(mfile: MemFile): string {.inline.} = - for ms in memSlices(mfile): yield toString(ms) +iterator lines*(mfile: MemFile, delim='\l', eat='\r'): TaintedString {.inline.} = + ## Return each line in a file as a Nim string, like lines(File). + ## Default delimiting is [\r]\l which parse Unix or Windows text file lines. + ## Pass eat='\0' to be strictly delim-delimited. + var buf = TaintedString(newStringOfCap(80)) + for line in lines(mfile, buf, delim, eat): + yield buf From 2252f80e72508b6be3f16be98e0f5815cf862343 Mon Sep 17 00:00:00 2001 From: Charles Blake Date: Sun, 2 Aug 2015 12:09:53 -0400 Subject: [PATCH 054/115] Add some very simple tests for memfiles iterators. --- tests/misc/tmemlines.nim | 5 +++++ tests/misc/tmemlinesBuf.nim | 6 ++++++ tests/misc/tmemslices.nim | 6 ++++++ 3 files changed, 17 insertions(+) create mode 100644 tests/misc/tmemlines.nim create mode 100644 tests/misc/tmemlinesBuf.nim create mode 100644 tests/misc/tmemslices.nim diff --git a/tests/misc/tmemlines.nim b/tests/misc/tmemlines.nim new file mode 100644 index 0000000000..19821ea26b --- /dev/null +++ b/tests/misc/tmemlines.nim @@ -0,0 +1,5 @@ +import memfiles +var inp = memfiles.open("readme.txt") +for line in lines(inp): + echo("#" & line & "#") +close(inp) diff --git a/tests/misc/tmemlinesBuf.nim b/tests/misc/tmemlinesBuf.nim new file mode 100644 index 0000000000..12160cbf91 --- /dev/null +++ b/tests/misc/tmemlinesBuf.nim @@ -0,0 +1,6 @@ +import memfiles +var inp = memfiles.open("readme.txt") +var buffer: TaintedString = "" +for line in lines(, buffer): + echo("#" & line & "#") +close(inp) diff --git a/tests/misc/tmemslices.nim b/tests/misc/tmemslices.nim new file mode 100644 index 0000000000..8c658ad28a --- /dev/null +++ b/tests/misc/tmemslices.nim @@ -0,0 +1,6 @@ +import memfiles +var inp = memfiles.open("readme.txt") +for mem in memSlices(inp): + if mem.size > 3: + echo("#" & toString(mem) & "#") +close(inp) From 63c4f204e8cb7d505b13a6707783e50b58b413ae Mon Sep 17 00:00:00 2001 From: Charles Blake Date: Sun, 2 Aug 2015 12:20:05 -0400 Subject: [PATCH 055/115] Add some little one-line doc comment of MemSlice. --- lib/pure/memfiles.nim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/pure/memfiles.nim b/lib/pure/memfiles.nim index 51f3662de4..f9f4965c44 100644 --- a/lib/pure/memfiles.nim +++ b/lib/pure/memfiles.nim @@ -248,7 +248,7 @@ proc close*(f: var MemFile) = if error: raiseOSError(lastErr) -type MemSlice* = object +type MemSlice* = object ## represent slice of a MemFile for iteration over delimited lines/records data*: pointer size*: int From ad67bfcf4637532538ad09ffd34dd592b201f2d2 Mon Sep 17 00:00:00 2001 From: Charles Blake Date: Sun, 2 Aug 2015 12:21:49 -0400 Subject: [PATCH 056/115] Quote the \ in doc comments. --- lib/pure/memfiles.nim | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/lib/pure/memfiles.nim b/lib/pure/memfiles.nim index f9f4965c44..d2d2a9c8fe 100644 --- a/lib/pure/memfiles.nim +++ b/lib/pure/memfiles.nim @@ -263,8 +263,8 @@ proc toString*(ms: MemSlice): string {.inline.} = iterator memSlices*(mfile: MemFile, delim='\l', eat='\r'): MemSlice {.inline.} = ## Iterates over [optional eat]delim-delimited slices in a MemFile. - ## Default delimiting is [\r]\l which parse Unix or Windows text file lines. - ## Pass eat='\0' to be strictly delim-delimited. + ## Default delimiting is [\\r]\\l which parse Unix or Windows text file lines. + ## Pass eat='\\0' to be strictly delim-delimited. ## This zero copy, memchr-limited method is probably the fastest way to ## iterate through lines in a file, however the returned (data,size) objects ## are NOT Nim strings or even terminated C strings. So, be careful how data @@ -291,8 +291,8 @@ iterator memSlices*(mfile: MemFile, delim='\l', eat='\r'): MemSlice {.inline.} = iterator lines*(mfile: MemFile, buf: var TaintedString, delim='\l', eat='\r'): TaintedString {.inline.} = ## Replace contents of passed buffer with each new line, like readLine(File). - ## Default delimiting is [\r]\l which parse Unix or Windows text file lines. - ## Pass eat='\0' to be strictly delim-delimited. + ## Default delimiting is [\\r]\\l which parse Unix or Windows text file lines. + ## Pass eat='\\0' to be strictly delim-delimited. for ms in memSlices(mfile, delim, eat): buf.setLen(ms.size) c_memcpy(addr(buf[0]), ms.data, ms.size) @@ -301,7 +301,7 @@ iterator lines*(mfile: MemFile, buf: var TaintedString, delim='\l', eat='\r'): T iterator lines*(mfile: MemFile, delim='\l', eat='\r'): TaintedString {.inline.} = ## Return each line in a file as a Nim string, like lines(File). - ## Default delimiting is [\r]\l which parse Unix or Windows text file lines. + ## Default delimiting is [\\r]\\l which parse Unix or Windows text file lines. ## Pass eat='\0' to be strictly delim-delimited. var buf = TaintedString(newStringOfCap(80)) for line in lines(mfile, buf, delim, eat): From 2e4e0ffd3d69e37c9c6273979359d41ae65bf48a Mon Sep 17 00:00:00 2001 From: Charles Blake Date: Sun, 2 Aug 2015 12:25:57 -0400 Subject: [PATCH 057/115] Add some example code blocks. --- lib/pure/memfiles.nim | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/lib/pure/memfiles.nim b/lib/pure/memfiles.nim index d2d2a9c8fe..e94ccaf3d9 100644 --- a/lib/pure/memfiles.nim +++ b/lib/pure/memfiles.nim @@ -268,7 +268,12 @@ iterator memSlices*(mfile: MemFile, delim='\l', eat='\r'): MemSlice {.inline.} = ## This zero copy, memchr-limited method is probably the fastest way to ## iterate through lines in a file, however the returned (data,size) objects ## are NOT Nim strings or even terminated C strings. So, be careful how data - ## is accessed (e.g., use C mem* functions, not str* functions). + ## is accessed (e.g., use C mem* functions, not str* functions). Example: + ## .. code-block:: nim + ## var count = 0 + ## for slice in memSlices(memfiles.open("foo")): + ## inc(count) + ## echo count proc c_memchr(cstr: pointer, c: char, n: csize): pointer {. importc: "memchr", header: "" .} proc `-!`(p, q: pointer): int {.inline.} = return cast[int](p) -% cast[int](q) @@ -302,7 +307,10 @@ iterator lines*(mfile: MemFile, buf: var TaintedString, delim='\l', eat='\r'): T iterator lines*(mfile: MemFile, delim='\l', eat='\r'): TaintedString {.inline.} = ## Return each line in a file as a Nim string, like lines(File). ## Default delimiting is [\\r]\\l which parse Unix or Windows text file lines. - ## Pass eat='\0' to be strictly delim-delimited. + ## Pass eat='\0' to be strictly delim-delimited. Example: + ## .. code-block:: nim + ## for line in lines(memfiles.open("foo")): + ## echo line var buf = TaintedString(newStringOfCap(80)) for line in lines(mfile, buf, delim, eat): yield buf From 1bea224c4a7f05cd3b3c7eef6b2246ddac371555 Mon Sep 17 00:00:00 2001 From: Charles Blake Date: Sun, 2 Aug 2015 12:29:24 -0400 Subject: [PATCH 058/115] Try to get examples to render right as HTML. --- lib/pure/memfiles.nim | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/lib/pure/memfiles.nim b/lib/pure/memfiles.nim index e94ccaf3d9..f1bd7d11c0 100644 --- a/lib/pure/memfiles.nim +++ b/lib/pure/memfiles.nim @@ -268,12 +268,14 @@ iterator memSlices*(mfile: MemFile, delim='\l', eat='\r'): MemSlice {.inline.} = ## This zero copy, memchr-limited method is probably the fastest way to ## iterate through lines in a file, however the returned (data,size) objects ## are NOT Nim strings or even terminated C strings. So, be careful how data - ## is accessed (e.g., use C mem* functions, not str* functions). Example: + ## is accessed (e.g., use C mem* functions, not str* functions). Example: + ## ## .. code-block:: nim ## var count = 0 ## for slice in memSlices(memfiles.open("foo")): ## inc(count) ## echo count + proc c_memchr(cstr: pointer, c: char, n: csize): pointer {. importc: "memchr", header: "" .} proc `-!`(p, q: pointer): int {.inline.} = return cast[int](p) -% cast[int](q) @@ -308,9 +310,11 @@ iterator lines*(mfile: MemFile, delim='\l', eat='\r'): TaintedString {.inline.} ## Return each line in a file as a Nim string, like lines(File). ## Default delimiting is [\\r]\\l which parse Unix or Windows text file lines. ## Pass eat='\0' to be strictly delim-delimited. Example: + ## ## .. code-block:: nim ## for line in lines(memfiles.open("foo")): ## echo line + var buf = TaintedString(newStringOfCap(80)) for line in lines(mfile, buf, delim, eat): yield buf From 4c1611fc13bbdc516fb08f1e12bf3a873489d705 Mon Sep 17 00:00:00 2001 From: singularperturbation Date: Sun, 2 Aug 2015 14:16:49 -0500 Subject: [PATCH 059/115] Fix for image directive with periods Saw in https://github.com/nim-lang/nimforum/issues/41 that there was an issue in rendering inline images. Traced back through rstgen and found that it was not counting something like: `.. image:: http://i.imgur.com/oCem13Y.png` as valid since it contained a period, and this wasn't in the set of valid characters, so the empty string is returned by default. Added a period to the allowable characters, and now renders correctly. Test case: ``` import rst, rstgen, strtabs var docConfig: StringTableRef docConfig = rstgen.defaultConfig() docConfig["doc.smiley_format"] = "/images/smilieys/$1.png" proc rstToHtml(content: string): string = result = rstgen.rstToHtml(content, {roSupportSmilies,roSupportMarkdown}, docConfig) var a: string = rstToHtml(".. image:: http://i.imgur.com/oCem13Y.png") echo a ``` --- 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 cc21b9382e..83e1b8b9f5 100644 --- a/lib/packages/docutils/rstgen.nim +++ b/lib/packages/docutils/rstgen.nim @@ -755,7 +755,7 @@ proc renderTocEntries*(d: var RstGenerator, j: var int, lvl: int, proc renderImage(d: PDoc, n: PRstNode, result: var string) = template valid(s): expr = - s.len > 0 and allCharsInSet(s, {'/',':','%','_','\\','\128'..'\xFF'} + + s.len > 0 and allCharsInSet(s, {'.','/',':','%','_','\\','\128'..'\xFF'} + Digits + Letters + WhiteSpace) var options = "" From 05754377621e43f32aafbeb149c1bd5927afd430 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Zieli=C5=84ski?= Date: Sun, 2 Aug 2015 22:55:22 +0200 Subject: [PATCH 060/115] Improve discoverability of 'of' operator --- doc/manual/types.txt | 3 ++- lib/system.nim | 1 + 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/doc/manual/types.txt b/doc/manual/types.txt index 2eb034ba2d..44a20d0935 100644 --- a/doc/manual/types.txt +++ b/doc/manual/types.txt @@ -588,7 +588,8 @@ can also be defined with indentation instead of ``[]``: Objects provide many features that tuples do not. Object provide inheritance and information hiding. Objects have access to their type at runtime, so that -the ``of`` operator can be used to determine the object's type. +the ``of`` operator can be used to determine the object's type. The ``of`` operator +is similar to the ``instanceof`` operator in Java. .. code-block:: nim type diff --git a/lib/system.nim b/lib/system.nim index dd1a0721a6..383002fcfc 100644 --- a/lib/system.nim +++ b/lib/system.nim @@ -487,6 +487,7 @@ type ## See the full `exception hierarchy`_. ObjectConversionError* = object of Exception ## \ ## Raised if an object is converted to an incompatible object type. + ## You can use ``of`` operator to check if conversion will succeed. ## ## See the full `exception hierarchy`_. FloatingPointError* = object of Exception ## \ From 02f97489b795cd33d49966e254b46fcc3f8072ba Mon Sep 17 00:00:00 2001 From: Zahary Karadjov Date: Tue, 21 Jul 2015 10:21:14 +0300 Subject: [PATCH 061/115] fix #1858 again; restores the support for static macro params --- compiler/evaltempl.nim | 50 ++++++++++++------- compiler/semcall.nim | 18 ++++++- compiler/seminst.nim | 25 +++++----- compiler/vm.nim | 32 +++++++++---- compiler/vmgen.nim | 21 +++++++- tests/macros/tstaticparamsmacro.nim | 74 +++++++++++++++++++++++++++++ 6 files changed, 179 insertions(+), 41 deletions(-) create mode 100644 tests/macros/tstaticparamsmacro.nim diff --git a/compiler/evaltempl.nim b/compiler/evaltempl.nim index 2b31129093..015c14ab5a 100644 --- a/compiler/evaltempl.nim +++ b/compiler/evaltempl.nim @@ -25,16 +25,21 @@ proc copyNode(ctx: TemplCtx, a, b: PNode): PNode = if ctx.instLines: result.info = b.info proc evalTemplateAux(templ, actual: PNode, c: var TemplCtx, result: PNode) = + template handleParam(param) = + let x = param + if x.kind == nkArgList: + for y in items(x): result.add(y) + else: + result.add copyTree(x) + case templ.kind of nkSym: var s = templ.sym if s.owner.id == c.owner.id: if s.kind == skParam and sfGenSym notin s.flags: - let x = actual.sons[s.position] - if x.kind == nkArgList: - for y in items(x): result.add(y) - else: - result.add copyTree(x) + handleParam actual.sons[s.position] + elif s.kind == skGenericParam: + handleParam actual.sons[s.owner.typ.len + s.position - 1] else: internalAssert sfGenSym in s.flags var x = PSym(idTableGet(c.mapping, s)) @@ -56,22 +61,35 @@ proc evalTemplateAux(templ, actual: PNode, c: var TemplCtx, result: PNode) = proc evalTemplateArgs(n: PNode, s: PSym): PNode = # if the template has zero arguments, it can be called without ``()`` # `n` is then a nkSym or something similar - var a: int - case n.kind - of nkCall, nkInfix, nkPrefix, nkPostfix, nkCommand, nkCallStrLit: - a = sonsLen(n) - else: a = 0 - var f = s.typ.sonsLen - if a > f: globalError(n.info, errWrongNumberOfArguments) + var totalParams = case n.kind + of nkCall, nkInfix, nkPrefix, nkPostfix, nkCommand, nkCallStrLit: expectedRegularParams + genericParams: + globalError(n.info, errWrongNumberOfArguments) + result = newNodeI(nkArgList, n.info) - for i in countup(1, f - 1): - var arg = if i < a: n.sons[i] else: copyTree(s.typ.n.sons[i].sym.ast) - if arg == nil or arg.kind == nkEmpty: + for i in 1 .. givenRegularParams: + result.addSon n.sons[i] + + # handle parameters with default values, which were + # not supplied by the user + for i in givenRegularParams+1 .. expectedRegularParams: + let default = s.typ.n.sons[i].sym.ast + if default.kind == nkEmpty: localError(n.info, errWrongNumberOfArguments) addSon(result, ast.emptyNode) else: - addSon(result, arg) + addSon(result, default.copyTree) + + # add any generic paramaters + for i in 1 .. genericParams: + result.addSon n.sons[givenRegularParams + i] var evalTemplateCounter* = 0 # to prevent endless recursion in templates instantiation diff --git a/compiler/semcall.nim b/compiler/semcall.nim index 571504c3a4..e2ff16c6e2 100644 --- a/compiler/semcall.nim +++ b/compiler/semcall.nim @@ -305,8 +305,22 @@ proc semResolvedCall(c: PContext, n: PNode, x: TCandidate): PNode = if containsGenericType(result.typ) or x.fauxMatch == tyUnknown: result.typ = newTypeS(x.fauxMatch, c) return - if finalCallee.ast.sons[genericParamsPos].kind != nkEmpty: - finalCallee = generateInstance(c, x.calleeSym, x.bindings, n.info) + let gp = finalCallee.ast.sons[genericParamsPos] + if gp.kind != nkEmpty: + if x.calleeSym.kind notin {skMacro, skTemplate}: + finalCallee = generateInstance(c, x.calleeSym, x.bindings, n.info) + else: + # For macros and templates, the resolved generic params + # are added as normal params. + for s in instantiateGenericParamList(c, gp, x.bindings): + case s.kind + of skConst: + x.call.add s.ast + of skType: + x.call.add newSymNode(s, n.info) + else: + internalAssert false + result = x.call instGenericConvertersSons(c, result, x) result.sons[0] = newSymNode(finalCallee, result.sons[0].info) diff --git a/compiler/seminst.nim b/compiler/seminst.nim index b2aef63a80..6ba107916a 100644 --- a/compiler/seminst.nim +++ b/compiler/seminst.nim @@ -10,14 +10,10 @@ # This module implements the instantiation of generic procs. # included from sem.nim -proc instantiateGenericParamList(c: PContext, n: PNode, pt: TIdTable, - entry: var TInstantiation) = - if n.kind != nkGenericParams: - internalError(n.info, "instantiateGenericParamList; no generic params") - newSeq(entry.concreteTypes, n.len) +iterator instantiateGenericParamList(c: PContext, n: PNode, pt: TIdTable): PSym = + internalAssert n.kind == nkGenericParams for i, a in n.pairs: - if a.kind != nkSym: - internalError(a.info, "instantiateGenericParamList; no symbol") + internalAssert a.kind == nkSym var q = a.sym if q.typ.kind notin {tyTypeDesc, tyGenericParam, tyStatic, tyIter}+tyTypeClasses: continue @@ -42,8 +38,7 @@ proc instantiateGenericParamList(c: PContext, n: PNode, pt: TIdTable, #t = ReplaceTypeVarsT(cl, t) s.typ = t if t.kind == tyStatic: s.ast = t.n - addDecl(c, s) - entry.concreteTypes[i] = t + yield s proc sameInstantiation(a, b: TInstantiation): bool = if a.concreteTypes.len == b.concreteTypes.len: @@ -217,7 +212,7 @@ proc generateInstance(c: PContext, fn: PSym, pt: TIdTable, ## The `pt` parameter is a type-unsafe mapping table used to link generic ## parameters to their concrete types within the generic instance. # no need to instantiate generic templates/macros: - if fn.kind in {skTemplate, skMacro}: return fn + internalAssert fn.kind notin {skMacro, skTemplate} # generates an instantiated proc if c.instCounter > 1000: internalError(fn.ast.info, "nesting too deep") inc(c.instCounter) @@ -234,12 +229,18 @@ proc generateInstance(c: PContext, fn: PSym, pt: TIdTable, result.ast = n pushOwner(result) openScope(c) - internalAssert n.sons[genericParamsPos].kind != nkEmpty + let gp = n.sons[genericParamsPos] + internalAssert gp.kind != nkEmpty n.sons[namePos] = newSymNode(result) pushInfoContext(info) var entry = TInstantiation.new entry.sym = result - instantiateGenericParamList(c, n.sons[genericParamsPos], pt, entry[]) + newSeq(entry.concreteTypes, gp.len) + var i = 0 + for s in instantiateGenericParamList(c, gp, pt): + addDecl(c, s) + entry.concreteTypes[i] = s.typ + inc i pushProcCon(c, result) instantiateProcType(c, pt, result, info) n.sons[genericParamsPos] = ast.emptyNode diff --git a/compiler/vm.nim b/compiler/vm.nim index d7495d77f1..fc63da1d4b 100644 --- a/compiler/vm.nim +++ b/compiler/vm.nim @@ -1467,12 +1467,20 @@ proc evalStaticStmt*(module: PSym, e: PNode, prc: PSym) = proc setupCompileTimeVar*(module: PSym, n: PNode) = discard evalConstExprAux(module, nil, n, emStaticStmt) -proc setupMacroParam(x: PNode): PNode = - result = x - if result.kind in {nkHiddenSubConv, nkHiddenStdConv}: result = result.sons[1] - result = canonValue(result) - result.flags.incl nfIsRef - result.typ = x.typ +proc setupMacroParam(x: PNode, typ: PType): TFullReg = + case typ.kind + of tyStatic: + putIntoReg(result, x) + of tyTypeDesc: + putIntoReg(result, x) + else: + result.kind = rkNode + var n = x + if n.kind in {nkHiddenSubConv, nkHiddenStdConv}: n = n.sons[1] + n = n.canonValue + n.flags.incl nfIsRef + n.typ = x.typ + result.node = n var evalMacroCounter: int @@ -1508,10 +1516,16 @@ proc evalMacroCall*(module: PSym, n, nOrig: PNode, sym: PSym): PNode = # return value: tos.slots[0].kind = rkNode tos.slots[0].node = newNodeIT(nkEmpty, n.info, sym.typ.sons[0]) + # setup parameters: - for i in 1 .. < min(tos.slots.len, L): - tos.slots[i].kind = rkNode - tos.slots[i].node = setupMacroParam(n.sons[i]) + for i in 1.. Date: Sun, 2 Aug 2015 23:23:13 +0300 Subject: [PATCH 062/115] Disable the new generic params handling for immediate template and macros Since immediate templates are not subjected to the standard sigmatching algorithm, they will have a number of deficiencies when it comes to generic params: Type dependencies between the parameters won't be honoured and the bound generic symbols won't be resolvable within their bodies. We could try to fix this, but it may be wiser to just deprecate immediate templates and macros now that we have working untyped parameters. Disabling the new features is admittedly not the greatest way to handle this situations as it introduces inconsistency in the language, but at least it makes the code backwards-compatible with the previous version of the compiler instead of triggering more serious problems. --- compiler/evaltempl.nim | 12 +++++++++++- compiler/vm.nim | 9 +++++---- compiler/vmgen.nim | 6 +++++- 3 files changed, 21 insertions(+), 6 deletions(-) diff --git a/compiler/evaltempl.nim b/compiler/evaltempl.nim index 015c14ab5a..82c4e8f573 100644 --- a/compiler/evaltempl.nim +++ b/compiler/evaltempl.nim @@ -66,7 +66,16 @@ proc evalTemplateArgs(n: PNode, s: PSym): PNode = else: 0 var - genericParams = s.ast[genericParamsPos].len + # XXX: Since immediate templates are not subjected to the + # standard sigmatching algorithm, they will have a number + # of deficiencies when it comes to generic params: + # Type dependencies between the parameters won't be honoured + # and the bound generic symbols won't be resolvable within + # their bodies. We could try to fix this, but it may be + # wiser to just deprecate immediate templates and macros + # now that we have working untyped parameters. + genericParams = if sfImmediate in s.flags: 0 + else: s.ast[genericParamsPos].len expectedRegularParams = Date: Mon, 3 Aug 2015 01:27:41 +0300 Subject: [PATCH 063/115] Rename compiler configuration file to fix "nim check" within the repo Prior to the change, "nim check" would fail on any file besides the main nim.nim, because the include paths for the documentation were not set correctly and this produced various compilation errors. --- compiler/{nim.nim.cfg => nim.cfg} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename compiler/{nim.nim.cfg => nim.cfg} (100%) diff --git a/compiler/nim.nim.cfg b/compiler/nim.cfg similarity index 100% rename from compiler/nim.nim.cfg rename to compiler/nim.cfg From e1913abdd4a397ad21aa2452c75e82e163df0c20 Mon Sep 17 00:00:00 2001 From: Araq Date: Sun, 2 Aug 2015 22:12:39 +0200 Subject: [PATCH 064/115] fixes #3140 --- compiler/semfold.nim | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/compiler/semfold.nim b/compiler/semfold.nim index 729222220c..2ab43a9c93 100644 --- a/compiler/semfold.nim +++ b/compiler/semfold.nim @@ -307,12 +307,12 @@ proc evalOp(m: TMagic, n, a, b, c: PNode): PNode = of mToU8: result = newIntNodeT(getInt(a) and 0x000000FF, n) of mToU16: result = newIntNodeT(getInt(a) and 0x0000FFFF, n) of mToU32: result = newIntNodeT(getInt(a) and 0x00000000FFFFFFFF'i64, n) - of mUnaryLt: result = newIntNodeT(getOrdValue(a) - 1, n) - of mSucc: result = newIntNodeT(getOrdValue(a) + getInt(b), n) - of mPred: result = newIntNodeT(getOrdValue(a) - getInt(b), n) - of mAddI: result = newIntNodeT(getInt(a) + getInt(b), n) - of mSubI: result = newIntNodeT(getInt(a) - getInt(b), n) - of mMulI: result = newIntNodeT(getInt(a) * getInt(b), n) + of mUnaryLt: result = newIntNodeT(getOrdValue(a) |-| 1, n) + of mSucc: result = newIntNodeT(getOrdValue(a) |+| getInt(b), n) + of mPred: result = newIntNodeT(getOrdValue(a) |-| getInt(b), n) + of mAddI: result = newIntNodeT(getInt(a) |+| getInt(b), n) + of mSubI: result = newIntNodeT(getInt(a) |-| getInt(b), n) + of mMulI: result = newIntNodeT(getInt(a) |*| getInt(b), n) of mMinI: if getInt(a) > getInt(b): result = newIntNodeT(getInt(b), n) else: result = newIntNodeT(getInt(a), n) @@ -338,11 +338,11 @@ proc evalOp(m: TMagic, n, a, b, c: PNode): PNode = of mDivI: let y = getInt(b) if y != 0: - result = newIntNodeT(getInt(a) div y, n) + result = newIntNodeT(`|div|`(getInt(a), y), n) of mModI: let y = getInt(b) if y != 0: - result = newIntNodeT(getInt(a) mod y, n) + result = newIntNodeT(`|mod|`(getInt(a), y), n) of mAddF64: result = newFloatNodeT(getFloat(a) + getFloat(b), n) of mSubF64: result = newFloatNodeT(getFloat(a) - getFloat(b), n) of mMulF64: result = newFloatNodeT(getFloat(a) * getFloat(b), n) From 4d6bcbc2f43d8556bd9dd7c5f997e6b769619a94 Mon Sep 17 00:00:00 2001 From: Charlie Barto Date: Sun, 2 Aug 2015 18:55:35 -0400 Subject: [PATCH 065/115] make this change VCC specific --- lib/pure/times.nim | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/lib/pure/times.nim b/lib/pure/times.nim index ad8f77dbf9..a8ba7fe972 100644 --- a/lib/pure/times.nim +++ b/lib/pure/times.nim @@ -58,15 +58,20 @@ elif defined(windows): when defined(vcc): # newest version of Visual C++ defines time_t to be of 64 bits type TimeImpl {.importc: "time_t", header: "".} = int64 + # visual c's c runtime exposes these under a different name + var + timezone {.importc: "_timezone", header: "".}: int + tzname {.importc: "_tzname", header: ""}: array[0..1, cstring] else: type TimeImpl {.importc: "time_t", header: "".} = int32 + var + timezone {.importc, header: "".}: int + tzname {.importc, header: "" .}: array[0..1, cstring] type Time* = distinct TimeImpl - var - timezone {.importc: "_timezone", header: "".}: int - tzname {.importc: "_tzname", header: ""}: array[0..1, cstring] + elif defined(JS): type Time* {.importc.} = object From 436b847a7ecc0b4ce5a7952ea9e6af846a0d5491 Mon Sep 17 00:00:00 2001 From: Yuriy Glukhov Date: Mon, 3 Aug 2015 14:11:48 +0300 Subject: [PATCH 066/115] Workaround for #3179. --- lib/system.nim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/system.nim b/lib/system.nim index 383002fcfc..85d16708ca 100644 --- a/lib/system.nim +++ b/lib/system.nim @@ -1490,7 +1490,7 @@ when not defined(nimrodVM): ## containing zero, so it is somewhat safer than ``createU``. ## The allocated memory belongs to its allocating thread! ## Use `createShared` to allocate from a shared heap. - cast[ptr T](alloc0(T.sizeof * size)) + cast[ptr T](alloc0(sizeof(T) * size)) proc realloc*(p: pointer, newSize: Natural): pointer {.noconv, rtl, tags: [], benign.} ## grows or shrinks a given memory block. If p is **nil** then a new From 303bc45745d202278d4be3e96c7c0864e748bad7 Mon Sep 17 00:00:00 2001 From: Dominik Picheta Date: Mon, 3 Aug 2015 17:05:14 +0100 Subject: [PATCH 067/115] Added documentation to macros.items. --- lib/core/macros.nim | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/core/macros.nim b/lib/core/macros.nim index d9e89e7f15..5d852cdb11 100644 --- a/lib/core/macros.nim +++ b/lib/core/macros.nim @@ -720,6 +720,7 @@ proc ident*(name: string): NimNode {.compileTime,inline.} = newIdentNode(name) ## Create a new ident node from a string iterator items*(n: NimNode): NimNode {.inline.} = + ## Iterates over the children of the NimNode ``n``. for i in 0 ..< n.len: yield n[i] From c6d609f88bf45371f99a1ed924dd4c4dc46ba181 Mon Sep 17 00:00:00 2001 From: Charles Blake Date: Mon, 3 Aug 2015 12:25:30 -0400 Subject: [PATCH 068/115] Fix accidentally deleted inp argument. --- tests/misc/tmemlinesBuf.nim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/misc/tmemlinesBuf.nim b/tests/misc/tmemlinesBuf.nim index 12160cbf91..21edc23223 100644 --- a/tests/misc/tmemlinesBuf.nim +++ b/tests/misc/tmemlinesBuf.nim @@ -1,6 +1,6 @@ import memfiles var inp = memfiles.open("readme.txt") var buffer: TaintedString = "" -for line in lines(, buffer): +for line in lines(inp, buffer): echo("#" & line & "#") close(inp) From c373c7209ab01ce9b2570fb0134ad52457f9ee91 Mon Sep 17 00:00:00 2001 From: Charles Blake Date: Mon, 3 Aug 2015 12:26:08 -0400 Subject: [PATCH 069/115] toString --> `$`. --- lib/pure/memfiles.nim | 2 +- tests/misc/tmemslices.nim | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/pure/memfiles.nim b/lib/pure/memfiles.nim index f1bd7d11c0..c646c31a18 100644 --- a/lib/pure/memfiles.nim +++ b/lib/pure/memfiles.nim @@ -254,7 +254,7 @@ type MemSlice* = object ## represent slice of a MemFile for iteration over deli proc c_memcpy(a, b: pointer, n: int) {.importc: "memcpy", header: "".} -proc toString*(ms: MemSlice): string {.inline.} = +proc `$`*(ms: MemSlice): string {.inline.} = ## Return a Nim string built from a MemSlice. var buf = newString(ms.size) c_memcpy(addr(buf[0]), ms.data, ms.size) diff --git a/tests/misc/tmemslices.nim b/tests/misc/tmemslices.nim index 8c658ad28a..951807cc4f 100644 --- a/tests/misc/tmemslices.nim +++ b/tests/misc/tmemslices.nim @@ -2,5 +2,5 @@ import memfiles var inp = memfiles.open("readme.txt") for mem in memSlices(inp): if mem.size > 3: - echo("#" & toString(mem) & "#") + echo("#" & $mem & "#") close(inp) From 9e8b4475e25adbe76331e6ab799443b1e2b62f7f Mon Sep 17 00:00:00 2001 From: Charles Blake Date: Mon, 3 Aug 2015 12:34:03 -0400 Subject: [PATCH 070/115] Spruce up doc comments as per dom96 suggestions. --- lib/pure/memfiles.nim | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/lib/pure/memfiles.nim b/lib/pure/memfiles.nim index c646c31a18..f78cd8766d 100644 --- a/lib/pure/memfiles.nim +++ b/lib/pure/memfiles.nim @@ -263,12 +263,12 @@ proc `$`*(ms: MemSlice): string {.inline.} = iterator memSlices*(mfile: MemFile, delim='\l', eat='\r'): MemSlice {.inline.} = ## Iterates over [optional eat]delim-delimited slices in a MemFile. - ## Default delimiting is [\\r]\\l which parse Unix or Windows text file lines. + ## Default delimiting is [\\r]\\l which parses Unix/Windows text file lines. ## Pass eat='\\0' to be strictly delim-delimited. ## This zero copy, memchr-limited method is probably the fastest way to - ## iterate through lines in a file, however the returned (data,size) objects - ## are NOT Nim strings or even terminated C strings. So, be careful how data - ## is accessed (e.g., use C mem* functions, not str* functions). Example: + ## iterate through lines in a file. The returned (data,size) objects are + ## NOT Nim strings or even terminated C strings. So, be careful how data + ## is accessed (e.g., think C mem* functions, not str* functions). Example: ## ## .. code-block:: nim ## var count = 0 @@ -298,8 +298,13 @@ iterator memSlices*(mfile: MemFile, delim='\l', eat='\r'): MemSlice {.inline.} = iterator lines*(mfile: MemFile, buf: var TaintedString, delim='\l', eat='\r'): TaintedString {.inline.} = ## Replace contents of passed buffer with each new line, like readLine(File). - ## Default delimiting is [\\r]\\l which parse Unix or Windows text file lines. - ## Pass eat='\\0' to be strictly delim-delimited. + ## Default delimiting is [\\r]\\l which parses Unix/Windows text file lines. + ## Pass eat='\\0' to be strictly delim-delimited. Example: + ## + ## .. code-block:: nim + ## var buffer: TaintedString = "" + ## for line in lines(memfiles.open("foo"), buffer): + ## echo line for ms in memSlices(mfile, delim, eat): buf.setLen(ms.size) c_memcpy(addr(buf[0]), ms.data, ms.size) @@ -308,7 +313,7 @@ iterator lines*(mfile: MemFile, buf: var TaintedString, delim='\l', eat='\r'): T iterator lines*(mfile: MemFile, delim='\l', eat='\r'): TaintedString {.inline.} = ## Return each line in a file as a Nim string, like lines(File). - ## Default delimiting is [\\r]\\l which parse Unix or Windows text file lines. + ## Default delimiting is [\\r]\\l which parses Unix/Windows text file lines. ## Pass eat='\0' to be strictly delim-delimited. Example: ## ## .. code-block:: nim From 5497c964d0aaf3fa0a9b3c33b6b6691e3cc5b985 Mon Sep 17 00:00:00 2001 From: Charles Blake Date: Mon, 3 Aug 2015 12:35:57 -0400 Subject: [PATCH 071/115] Move tests to stdlib. --- tests/{misc => stdlib}/tmemlines.nim | 0 tests/{misc => stdlib}/tmemlinesBuf.nim | 0 tests/{misc => stdlib}/tmemslices.nim | 0 3 files changed, 0 insertions(+), 0 deletions(-) rename tests/{misc => stdlib}/tmemlines.nim (100%) rename tests/{misc => stdlib}/tmemlinesBuf.nim (100%) rename tests/{misc => stdlib}/tmemslices.nim (100%) diff --git a/tests/misc/tmemlines.nim b/tests/stdlib/tmemlines.nim similarity index 100% rename from tests/misc/tmemlines.nim rename to tests/stdlib/tmemlines.nim diff --git a/tests/misc/tmemlinesBuf.nim b/tests/stdlib/tmemlinesBuf.nim similarity index 100% rename from tests/misc/tmemlinesBuf.nim rename to tests/stdlib/tmemlinesBuf.nim diff --git a/tests/misc/tmemslices.nim b/tests/stdlib/tmemslices.nim similarity index 100% rename from tests/misc/tmemslices.nim rename to tests/stdlib/tmemslices.nim From b9c1dd007279876f2f1d8a4085ffbfb0c9a27bb3 Mon Sep 17 00:00:00 2001 From: Dominik Picheta Date: Mon, 3 Aug 2015 18:42:02 +0100 Subject: [PATCH 072/115] Fixed macros.nim not compiling. Iterator aliases are not possible. --- lib/core/macros.nim | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/lib/core/macros.nim b/lib/core/macros.nim index 5d852cdb11..d371a92cfc 100644 --- a/lib/core/macros.nim +++ b/lib/core/macros.nim @@ -724,7 +724,10 @@ iterator items*(n: NimNode): NimNode {.inline.} = for i in 0 ..< n.len: yield n[i] -iterator children*(n: NimNode): NimNode {.inline.} = items +iterator children*(n: NimNode): NimNode {.inline.} = + ## Iterates over the children of the NimNode ``n``. + for i in 0 ..< n.len: + yield n[i] template findChild*(n: NimNode; cond: expr): NimNode {. immediate, dirty.} = From 202cc489034f6e4f41140d7af82e93b112fcd3dd Mon Sep 17 00:00:00 2001 From: Dominik Picheta Date: Mon, 3 Aug 2015 21:28:33 +0100 Subject: [PATCH 073/115] The async macro now supports transforming multiple async procs. This is a workaround for #3182, no forward declarations. You can now do this: ```nim async: proc bar(): Future[T] proc foo() = await bar() proc bar() {.async.} = echo(123); await foo() ``` --- lib/pure/asyncdispatch.nim | 25 ++++++++++++++++++------- 1 file changed, 18 insertions(+), 7 deletions(-) diff --git a/lib/pure/asyncdispatch.nim b/lib/pure/asyncdispatch.nim index 7523b29d5a..f49388b174 100644 --- a/lib/pure/asyncdispatch.nim +++ b/lib/pure/asyncdispatch.nim @@ -124,7 +124,8 @@ export Port, SocketFlag ## ## * The effect system (``raises: []``) does not work with async procedures. ## * Can't await in a ``except`` body - +## * Forward declarations for async procs are broken, +## link includes workaround: https://github.com/nim-lang/Nim/issues/3182. # TODO: Check if yielded future is nil and throw a more meaningful exception @@ -1412,12 +1413,12 @@ proc getName(node: NimNode): string {.compileTime.} = else: error("Unknown name.") -macro async*(prc: stmt): stmt {.immediate.} = - ## Macro which processes async procedures into the appropriate - ## iterators and yield statements. +proc asyncSingleProc(prc: NimNode): NimNode {.compileTime.} = + ## This macro transforms a single procedure into a closure iterator. + ## The ``async`` macro supports a stmtList holding multiple async procedures. if prc.kind notin {nnkProcDef, nnkLambda}: - error("Cannot transform this node kind into an async proc." & - " Proc definition or lambda node expected.") + error("Cannot transform this node kind into an async proc." & + " Proc definition or lambda node expected.") hint("Processing " & prc[0].getName & " as an async proc.") @@ -1504,9 +1505,19 @@ macro async*(prc: stmt): stmt {.immediate.} = result[6] = outerProcBody #echo(treeRepr(result)) - if prc[0].getName == "getAsync": + if prc[0].getName == "hubConnectionLoop": echo(toStrLit(result)) +macro async*(prc: stmt): stmt {.immediate.} = + ## Macro which processes async procedures into the appropriate + ## iterators and yield statements. + if prc.kind == nnkStmtList: + for oneProc in prc: + result = newStmtList() + result.add asyncSingleProc(oneProc) + else: + result = asyncSingleProc(prc) + proc recvLine*(socket: AsyncFD): Future[string] {.async.} = ## Reads a line of data from ``socket``. Returned future will complete once ## a full line is read or an error occurs. From f8f967720598ad75ec0e0db68a983cbcdd39145d Mon Sep 17 00:00:00 2001 From: Dominik Picheta Date: Tue, 4 Aug 2015 09:06:47 +0100 Subject: [PATCH 074/115] Updated documentation of system.new. Ref #2699. --- lib/system.nim | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/lib/system.nim b/lib/system.nim index 383002fcfc..93d76e78b8 100644 --- a/lib/system.nim +++ b/lib/system.nim @@ -178,7 +178,10 @@ proc new*[T](a: var ref T) {.magic: "New", noSideEffect.} proc new*(T: typedesc): auto = ## creates a new object of type ``T`` and returns a safe (traced) - ## reference to it as result value + ## reference to it as result value. + ## + ## When ``T`` is a ref type then the resulting type will be ``T``, + ## otherwise it will be ``ref T``. when (T is ref): var r: T else: From c03502943153a164ff8ccbe58c45117cb4eecd5b Mon Sep 17 00:00:00 2001 From: Erik Michaels-Ober Date: Tue, 4 Aug 2015 13:15:30 +0200 Subject: [PATCH 075/115] Fix documentation style reference The documentation style guide was renamed from styleguide to docstyle in a98f609ae215d1fcf9922329b5d87bd1b9242800 but this reference was not updated in that comment. --- contributing.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contributing.rst b/contributing.rst index 3b495f01fb..e6d34dcb05 100644 --- a/contributing.rst +++ b/contributing.rst @@ -217,4 +217,4 @@ General commit rules 3. Describe your commit and use your common sense. -.. include:: styleguide.rst +.. include:: docstyle.rst From ab26298a035ed794fc73724a6ae06eefd6c764d3 Mon Sep 17 00:00:00 2001 From: Araq Date: Tue, 4 Aug 2015 17:31:00 +0200 Subject: [PATCH 076/115] added system.unsafeAddr --- compiler/parampatterns.nim | 20 +++++++++++--------- compiler/semexprs.nim | 6 +++--- compiler/semmagic.nim | 6 +++--- lib/system.nim | 7 +++++++ tests/ccgbugs/tunsafeaddr.nim | 19 +++++++++++++++++++ todo.txt | 1 - web/news.txt | 5 +++++ 7 files changed, 48 insertions(+), 16 deletions(-) create mode 100644 tests/ccgbugs/tunsafeaddr.nim diff --git a/compiler/parampatterns.nim b/compiler/parampatterns.nim index b7fe269dfc..ae391945aa 100644 --- a/compiler/parampatterns.nim +++ b/compiler/parampatterns.nim @@ -178,13 +178,14 @@ type arDiscriminant, # is a discriminant arStrange # it is a strange beast like 'typedesc[var T]' -proc isAssignable*(owner: PSym, n: PNode): TAssignableResult = +proc isAssignable*(owner: PSym, n: PNode; isUnsafeAddr=false): TAssignableResult = ## 'owner' can be nil! result = arNone case n.kind of nkSym: - # don't list 'skLet' here: - if n.sym.kind in {skVar, skResult, skTemp}: + let kinds = if isUnsafeAddr: {skVar, skResult, skTemp, skParam, skLet} + else: {skVar, skResult, skTemp} + if n.sym.kind in kinds: if owner != nil and owner.id == n.sym.owner.id and sfGlobal notin n.sym.flags: result = arLocalLValue @@ -200,7 +201,7 @@ proc isAssignable*(owner: PSym, n: PNode): TAssignableResult = {tyVar, tyPtr, tyRef}: result = arLValue else: - result = isAssignable(owner, n.sons[0]) + result = isAssignable(owner, n.sons[0], isUnsafeAddr) if result != arNone and sfDiscriminant in n.sons[1].sym.flags: result = arDiscriminant of nkBracketExpr: @@ -208,23 +209,24 @@ proc isAssignable*(owner: PSym, n: PNode): TAssignableResult = {tyVar, tyPtr, tyRef}: result = arLValue else: - result = isAssignable(owner, n.sons[0]) + result = isAssignable(owner, n.sons[0], isUnsafeAddr) of nkHiddenStdConv, nkHiddenSubConv, nkConv: # Object and tuple conversions are still addressable, so we skip them # XXX why is 'tyOpenArray' allowed here? if skipTypes(n.typ, abstractPtrs-{tyTypeDesc}).kind in {tyOpenArray, tyTuple, tyObject}: - result = isAssignable(owner, n.sons[1]) + result = isAssignable(owner, n.sons[1], isUnsafeAddr) elif compareTypes(n.typ, n.sons[1].typ, dcEqIgnoreDistinct): # types that are equal modulo distinction preserve l-value: - result = isAssignable(owner, n.sons[1]) + result = isAssignable(owner, n.sons[1], isUnsafeAddr) of nkHiddenDeref, nkDerefExpr, nkHiddenAddr: result = arLValue of nkObjUpConv, nkObjDownConv, nkCheckedFieldExpr: - result = isAssignable(owner, n.sons[0]) + result = isAssignable(owner, n.sons[0], isUnsafeAddr) of nkCallKinds: # builtin slice keeps lvalue-ness: - if getMagic(n) == mSlice: result = isAssignable(owner, n.sons[1]) + if getMagic(n) == mSlice: + result = isAssignable(owner, n.sons[1], isUnsafeAddr) else: discard diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim index fba64776dd..b4308def3c 100644 --- a/compiler/semexprs.nim +++ b/compiler/semexprs.nim @@ -597,8 +597,8 @@ proc skipObjConv(n: PNode): PNode = of nkObjUpConv, nkObjDownConv: result = n.sons[0] else: result = n -proc isAssignable(c: PContext, n: PNode): TAssignableResult = - result = parampatterns.isAssignable(c.p.owner, n) +proc isAssignable(c: PContext, n: PNode; isUnsafeAddr=false): TAssignableResult = + result = parampatterns.isAssignable(c.p.owner, n, isUnsafeAddr) proc newHiddenAddrTaken(c: PContext, n: PNode): PNode = if n.kind == nkHiddenDeref and not (gCmd == cmdCompileToCpp or @@ -1700,7 +1700,7 @@ proc semMagic(c: PContext, n: PNode, s: PSym, flags: TExprFlags): PNode = case s.magic # magics that need special treatment of mAddr: checkSonsLen(n, 2) - result = semAddr(c, n.sons[1]) + result = semAddr(c, n.sons[1], s.name.s == "unsafeAddr") of mTypeOf: checkSonsLen(n, 2) result = semTypeOf(c, n.sons[1]) diff --git a/compiler/semmagic.nim b/compiler/semmagic.nim index 0a7846f1de..0afbf1f076 100644 --- a/compiler/semmagic.nim +++ b/compiler/semmagic.nim @@ -10,10 +10,10 @@ # This include file implements the semantic checking for magics. # included from sem.nim -proc semAddr(c: PContext; n: PNode): PNode = +proc semAddr(c: PContext; n: PNode; isUnsafeAddr=false): PNode = result = newNodeI(nkAddr, n.info) let x = semExprWithType(c, n) - if isAssignable(c, x) notin {arLValue, arLocalLValue}: + if isAssignable(c, x, isUnsafeAddr) notin {arLValue, arLocalLValue}: localError(n.info, errExprHasNoAddress) result.add x result.typ = makePtrType(c, x.typ) @@ -119,7 +119,7 @@ proc magicsAfterOverloadResolution(c: PContext, n: PNode, case n[0].sym.magic of mAddr: checkSonsLen(n, 2) - result = semAddr(c, n.sons[1]) + result = semAddr(c, n.sons[1], n[0].sym.name.s == "unsafeAddr") of mTypeOf: checkSonsLen(n, 2) result = semTypeOf(c, n.sons[1]) diff --git a/lib/system.nim b/lib/system.nim index 93d76e78b8..e5cae13363 100644 --- a/lib/system.nim +++ b/lib/system.nim @@ -153,6 +153,13 @@ proc `addr`*[T](x: var T): ptr T {.magic: "Addr", noSideEffect.} = ## Cannot be overloaded. discard +proc unsafeAddr*[T](x: var T): ptr T {.magic: "Addr", noSideEffect.} = + ## Builtin 'addr' operator for taking the address of a memory location. + ## This works even for ``let`` variables or parameters for better interop + ## with C and so it is considered even more unsafe than the ordinary ``addr``. + ## Cannot be overloaded. + discard + proc `type`*(x: expr): typeDesc {.magic: "TypeOf", noSideEffect.} = ## Builtin 'type' operator for accessing the type of an expression. ## Cannot be overloaded. diff --git a/tests/ccgbugs/tunsafeaddr.nim b/tests/ccgbugs/tunsafeaddr.nim new file mode 100644 index 0000000000..4f05c7c21b --- /dev/null +++ b/tests/ccgbugs/tunsafeaddr.nim @@ -0,0 +1,19 @@ +discard """ + output: '''12''' +""" + +{.emit: """ +long sum(long* a, long len) { + long i, result = 0; + for (i = 0; i < len; ++i) result += a[i]; + return result; +} +""".} + +proc sum(a: ptr int; len: int): int {.importc, nodecl.} + +proc main = + let foo = [8, 3, 1] + echo sum(unsafeAddr foo[0], foo.len) + +main() diff --git a/todo.txt b/todo.txt index 8734b8f19e..2955f29176 100644 --- a/todo.txt +++ b/todo.txt @@ -1,7 +1,6 @@ version 0.11.4 ============== -- ``unsafeAddr`` - document special cased varargs[untyped] and varargs[typed] - The remaining bugs of the lambda lifting pass that is responsible to enable diff --git a/web/news.txt b/web/news.txt index b72ae56d28..518793a4a8 100644 --- a/web/news.txt +++ b/web/news.txt @@ -57,6 +57,11 @@ News Language Additions ------------------ + - ``system.unsafeAddr`` can be used to access the address of a ``let`` + variable or parameter for C interoperability. Since technically this + makes parameters and ``let`` variables mutable, it is considered even more + unsafe than the ordinary ``addr`` builtin. + Bugfixes -------- From abf15ec6ceb99bed8bf79a1a7a566596d32097bc Mon Sep 17 00:00:00 2001 From: Charles Blake Date: Tue, 4 Aug 2015 12:11:33 -0400 Subject: [PATCH 077/115] Expand memSlices doc comment & use better example. Have lines() iterators docs point back to memSlices() for details. --- lib/pure/memfiles.nim | 41 +++++++++++++++++++++++++++-------------- 1 file changed, 27 insertions(+), 14 deletions(-) diff --git a/lib/pure/memfiles.nim b/lib/pure/memfiles.nim index f78cd8766d..a506d2c243 100644 --- a/lib/pure/memfiles.nim +++ b/lib/pure/memfiles.nim @@ -12,8 +12,8 @@ ## This module provides support for `memory mapped files`:idx: ## (Posix's `mmap`:idx:) on the different operating systems. ## -## It also provides some fast iterators over lines in text files -## delimited in Unix or Windows styles (or similarly delimited records). +## It also provides some fast iterators over lines in text files (or +## other "line-like", variable length, delimited records). when defined(windows): import winlean @@ -262,18 +262,30 @@ proc `$`*(ms: MemSlice): string {.inline.} = result = buf iterator memSlices*(mfile: MemFile, delim='\l', eat='\r'): MemSlice {.inline.} = - ## Iterates over [optional eat]delim-delimited slices in a MemFile. - ## Default delimiting is [\\r]\\l which parses Unix/Windows text file lines. - ## Pass eat='\\0' to be strictly delim-delimited. - ## This zero copy, memchr-limited method is probably the fastest way to - ## iterate through lines in a file. The returned (data,size) objects are - ## NOT Nim strings or even terminated C strings. So, be careful how data - ## is accessed (e.g., think C mem* functions, not str* functions). Example: + ## Iterates over [optional `eat`] `delim`-delimited slices in MemFile `mfile`. + ## + ## Default parameters parse lines ending in either Unix(\\l) or Windows(\\r\\l) + ## style on on a line-by-line basis. I.e., not every line needs the same ending. + ## Unlike readLine(File) & lines(File), archaic MacOS9 \\r-delimited lines + ## are not supported as a third option for each line. Such archaic MacOS9 + ## files can be handled by passing delim='\\r', eat='\\0', though. + ## + ## Non-default delimiters can be passed to allow iteration over other sorts + ## of "line-like" variable length records. Pass eat='\\0' to be strictly + ## `delim`-delimited. (Eating an optional prefix equal to '\\0' is not + ## supported.) + ## + ## This zero copy, memchr-limited interface is probably the fastest way to + ## iterate over line-like records in a file. However, returned (data,size) + ## objects are not Nim strings, bounds checked Nim arrays, or even terminated + ## C strings. So, care is required to access the data (e.g., think C mem* + ## functions, not str* functions). Example: ## ## .. code-block:: nim ## var count = 0 ## for slice in memSlices(memfiles.open("foo")): - ## inc(count) + ## if slice.size > 0 and cast[cstring](slice.data)[0] != '#': + ## inc(count) ## echo count proc c_memchr(cstr: pointer, c: char, n: csize): pointer {. @@ -298,13 +310,14 @@ iterator memSlices*(mfile: MemFile, delim='\l', eat='\r'): MemSlice {.inline.} = iterator lines*(mfile: MemFile, buf: var TaintedString, delim='\l', eat='\r'): TaintedString {.inline.} = ## Replace contents of passed buffer with each new line, like readLine(File). - ## Default delimiting is [\\r]\\l which parses Unix/Windows text file lines. - ## Pass eat='\\0' to be strictly delim-delimited. Example: + ## `delim`, `eat`, and delimiting logic is exactly as for memSlices, but Nim + ## strings are returned. Example: ## ## .. code-block:: nim ## var buffer: TaintedString = "" ## for line in lines(memfiles.open("foo"), buffer): ## echo line + for ms in memSlices(mfile, delim, eat): buf.setLen(ms.size) c_memcpy(addr(buf[0]), ms.data, ms.size) @@ -313,8 +326,8 @@ iterator lines*(mfile: MemFile, buf: var TaintedString, delim='\l', eat='\r'): T iterator lines*(mfile: MemFile, delim='\l', eat='\r'): TaintedString {.inline.} = ## Return each line in a file as a Nim string, like lines(File). - ## Default delimiting is [\\r]\\l which parses Unix/Windows text file lines. - ## Pass eat='\0' to be strictly delim-delimited. Example: + ## `delim`, `eat`, and delimiting logic is exactly as for memSlices, but Nim + ## strings are returned. Example: ## ## .. code-block:: nim ## for line in lines(memfiles.open("foo")): From 0487ad418040b0512603ec7f640307ab34895400 Mon Sep 17 00:00:00 2001 From: Charles Blake Date: Tue, 4 Aug 2015 13:16:27 -0400 Subject: [PATCH 078/115] Use hyperlinks to refer to other procs. --- lib/pure/memfiles.nim | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/lib/pure/memfiles.nim b/lib/pure/memfiles.nim index a506d2c243..15fa4c65e1 100644 --- a/lib/pure/memfiles.nim +++ b/lib/pure/memfiles.nim @@ -309,9 +309,10 @@ iterator memSlices*(mfile: MemFile, delim='\l', eat='\r'): MemSlice {.inline.} = remaining = mfile.size - (ms.data -! mfile.mem) iterator lines*(mfile: MemFile, buf: var TaintedString, delim='\l', eat='\r'): TaintedString {.inline.} = - ## Replace contents of passed buffer with each new line, like readLine(File). - ## `delim`, `eat`, and delimiting logic is exactly as for memSlices, but Nim - ## strings are returned. Example: + ## Replace contents of passed buffer with each new line, like + ## `readLine(File) `_. + ## `delim`, `eat`, and delimiting logic is exactly as for + ## `memSlices <#memSlices>`_, but Nim strings are returned. Example: ## ## .. code-block:: nim ## var buffer: TaintedString = "" @@ -325,9 +326,10 @@ iterator lines*(mfile: MemFile, buf: var TaintedString, delim='\l', eat='\r'): T yield buf iterator lines*(mfile: MemFile, delim='\l', eat='\r'): TaintedString {.inline.} = - ## Return each line in a file as a Nim string, like lines(File). - ## `delim`, `eat`, and delimiting logic is exactly as for memSlices, but Nim - ## strings are returned. Example: + ## Return each line in a file as a Nim string, like + ## `lines(File) `_. + ## `delim`, `eat`, and delimiting logic is exactly as for + ## `memSlices <#memSlices>`_, but Nim strings are returned. Example: ## ## .. code-block:: nim ## for line in lines(memfiles.open("foo")): From 5ec4b7946fd8c916ac0cf0d94b42837a571adc68 Mon Sep 17 00:00:00 2001 From: Charles Blake Date: Tue, 4 Aug 2015 13:25:41 -0400 Subject: [PATCH 079/115] Add note about what is included. --- lib/pure/memfiles.nim | 3 +++ 1 file changed, 3 insertions(+) diff --git a/lib/pure/memfiles.nim b/lib/pure/memfiles.nim index 15fa4c65e1..00929eaa24 100644 --- a/lib/pure/memfiles.nim +++ b/lib/pure/memfiles.nim @@ -270,6 +270,9 @@ iterator memSlices*(mfile: MemFile, delim='\l', eat='\r'): MemSlice {.inline.} = ## are not supported as a third option for each line. Such archaic MacOS9 ## files can be handled by passing delim='\\r', eat='\\0', though. ## + ## Delimiters are not part of the returned slice. A final, unterminated line + ## or record is returned just like any other. + ## ## Non-default delimiters can be passed to allow iteration over other sorts ## of "line-like" variable length records. Pass eat='\\0' to be strictly ## `delim`-delimited. (Eating an optional prefix equal to '\\0' is not From 0d8942d45e5d477224d486ab66adfcf38230aa8b Mon Sep 17 00:00:00 2001 From: Araq Date: Wed, 5 Aug 2015 21:27:53 +0200 Subject: [PATCH 080/115] destructors now work with overloaded assignment operators; fixes #2811; fixes #1632 --- compiler/semdestruct.nim | 9 ++ compiler/semexprs.nim | 5 +- compiler/semstmts.nim | 20 +++-- compiler/transf.nim | 83 ++++++++++++++++++- tests/destructor/tdestructor3.nim | 47 +++++++++++ tests/exception/tdefer1.nim | 26 +++++- .../tdictdestruct.nim | 0 7 files changed, 175 insertions(+), 15 deletions(-) create mode 100644 tests/destructor/tdestructor3.nim rename tests/{destructor => generics}/tdictdestruct.nim (100%) diff --git a/compiler/semdestruct.nim b/compiler/semdestruct.nim index aaab49a101..af671f6e00 100644 --- a/compiler/semdestruct.nim +++ b/compiler/semdestruct.nim @@ -177,6 +177,15 @@ proc instantiateDestructor(c: PContext, typ: PType): PType = else: return nil +proc createDestructorCall(c: PContext, s: PSym): PNode = + let varTyp = s.typ + if varTyp == nil or sfGlobal in s.flags: return + let destructableT = instantiateDestructor(c, varTyp) + if destructableT != nil: + let call = semStmt(c, newNode(nkCall, s.info, @[ + useSym(destructableT.destructor), useSym(s)])) + result = newNode(nkDefer, s.info, @[call]) + proc insertDestructors(c: PContext, varSection: PNode): tuple[outer, inner: PNode] = # Accepts a var or let section. diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim index b4308def3c..af6919d972 100644 --- a/compiler/semexprs.nim +++ b/compiler/semexprs.nim @@ -2259,7 +2259,10 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode = of nkStaticStmt: result = semStaticStmt(c, n) of nkDefer: - localError(n.info, errGenerated, "'defer' not allowed in this context") + n.sons[0] = semExpr(c, n.sons[0]) + if not n.sons[0].typ.isEmptyType: + localError(n.info, errGenerated, "'defer' takes a 'void' expression") + #localError(n.info, errGenerated, "'defer' not allowed in this context") else: localError(n.info, errInvalidExpressionX, renderTree(n, {renderNoComments})) diff --git a/compiler/semstmts.nim b/compiler/semstmts.nim index 84a09a7e63..b9b1759626 100644 --- a/compiler/semstmts.nim +++ b/compiler/semstmts.nim @@ -369,6 +369,15 @@ proc addToVarSection(c: PContext; result: var PNode; orig, identDefs: PNode) = else: result.add identDefs +proc addDefer(c: PContext; result: var PNode; s: PSym) = + let deferDestructorCall = createDestructorCall(c, s) + if deferDestructorCall != nil: + if result.kind != nkStmtList: + let oldResult = result + result = newNodeI(nkStmtList, result.info) + result.add oldResult + result.add deferDestructorCall + proc isDiscardUnderscore(v: PSym): bool = if v.name.s == "_": v.flags.incl(sfGenSym) @@ -469,6 +478,7 @@ proc semVarOrLet(c: PContext, n: PNode, symkind: TSymKind): PNode = if def.kind == nkPar: v.ast = def[j] v.typ = tup.sons[j] b.sons[j] = newSymNode(v) + addDefer(c, result, v) checkNilable(v) if sfCompileTime in v.flags: hasCompileTime = true if hasCompileTime: vm.setupCompileTimeVar(c.module, result) @@ -1371,7 +1381,7 @@ proc semStmtList(c: PContext, n: PNode, flags: TExprFlags): PNode = for i in countup(0, length - 1): let k = n.sons[i].kind case k - of nkFinally, nkExceptBranch, nkDefer: + of nkFinally, nkExceptBranch: # stand-alone finally and except blocks are # transformed into regular try blocks: # @@ -1424,14 +1434,6 @@ proc semStmtList(c: PContext, n: PNode, flags: TExprFlags): PNode = n.typ = n.sons[i].typ if not isEmptyType(n.typ): n.kind = nkStmtListExpr case n.sons[i].kind - of nkVarSection, nkLetSection: - let (outer, inner) = insertDestructors(c, n.sons[i]) - if outer != nil: - n.sons[i] = outer - var rest = newNode(nkStmtList, n.info, n.sons[i+1 .. length-1]) - inner.addSon(semStmtList(c, rest, flags)) - n.sons.setLen(i+1) - return of LastBlockStmts: for j in countup(i + 1, length - 1): case n.sons[j].kind diff --git a/compiler/transf.nim b/compiler/transf.nim index dddbd51c41..2ac5e1f399 100644 --- a/compiler/transf.nim +++ b/compiler/transf.nim @@ -16,6 +16,7 @@ # * converts "continue" to "break"; disambiguates "break" # * introduces method dispatchers # * performs lambda lifting for closure support +# * transforms 'defer' into a 'try finally' statement import intsets, strutils, lists, options, ast, astalgo, trees, treetab, msgs, os, @@ -44,6 +45,7 @@ type inlining: int # > 0 if we are in inlining context (copy vars) nestedProcs: int # > 0 if we are in a nested proc contSyms, breakSyms: seq[PSym] # to transform 'continue' and 'break' + deferDetected: bool PTransf = ref TTransfContext proc newTransNode(a: PNode): PTransNode {.inline.} = @@ -680,6 +682,14 @@ proc commonOptimizations*(c: PSym, n: PNode): PNode = result = n proc transform(c: PTransf, n: PNode): PTransNode = + when false: + var oldDeferAnchor: PNode + if n.kind in {nkElifBranch, nkOfBranch, nkExceptBranch, nkElifExpr, + nkElseExpr, nkElse, nkForStmt, nkWhileStmt, nkFinally, + nkBlockStmt, nkBlockExpr}: + oldDeferAnchor = c.deferAnchor + c.deferAnchor = n + case n.kind of nkSym: result = transformSym(c, n) @@ -712,13 +722,36 @@ proc transform(c: PTransf, n: PNode): PTransNode = result = transformFor(c, n) of nkParForStmt: result = transformSons(c, n) - of nkCaseStmt: result = transformCase(c, n) + of nkCaseStmt: + result = transformCase(c, n) + of nkWhileStmt: result = transformWhile(c, n) + of nkBlockStmt, nkBlockExpr: + result = transformBlock(c, n) + of nkDefer: + c.deferDetected = true + result = transformSons(c, n) + when false: + let deferPart = newNodeI(nkFinally, n.info) + deferPart.add n.sons[0] + let tryStmt = newNodeI(nkTryStmt, n.info) + if c.deferAnchor.isNil: + tryStmt.add c.root + c.root = tryStmt + result = PTransNode(tryStmt) + else: + # modify the corresponding *action*, don't rely on nkStmtList: + let L = c.deferAnchor.len-1 + tryStmt.add c.deferAnchor.sons[L] + c.deferAnchor.sons[L] = tryStmt + result = newTransNode(nkCommentStmt, n.info, 0) + tryStmt.addSon(deferPart) + # disable the original 'defer' statement: + n.kind = nkCommentStmt of nkContinueStmt: result = PTransNode(newNodeI(nkBreakStmt, n.info)) var labl = c.contSyms[c.contSyms.high] add(result, PTransNode(newSymNode(labl))) of nkBreakStmt: result = transformBreak(c, n) - of nkWhileStmt: result = transformWhile(c, n) of nkCallKinds: result = transformCall(c, n) of nkAddr, nkHiddenAddr: @@ -754,8 +787,6 @@ proc transform(c: PTransf, n: PNode): PTransNode = result = transformYield(c, n) else: result = transformSons(c, n) - of nkBlockStmt, nkBlockExpr: - result = transformBlock(c, n) of nkIdentDefs, nkConstDef: result = transformSons(c, n) # XXX comment handling really sucks: @@ -764,6 +795,8 @@ proc transform(c: PTransf, n: PNode): PTransNode = of nkClosure: return PTransNode(n) else: result = transformSons(c, n) + when false: + if oldDeferAnchor != nil: c.deferAnchor = oldDeferAnchor var cnst = getConstExpr(c.module, PNode(result)) # we inline constants if they are not complex constants: if cnst != nil and not dontInlineConstant(n, cnst): @@ -785,12 +818,52 @@ proc openTransf(module: PSym, filename: string): PTransf = result.breakSyms = @[] result.module = module +proc flattenStmts(n: PNode) = + var goOn = true + while goOn: + goOn = false + for i in 0.. Date: Wed, 5 Aug 2015 21:42:59 +0200 Subject: [PATCH 081/115] fix regressions --- compiler/transf.nim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/transf.nim b/compiler/transf.nim index 2ac5e1f399..5c7472a39c 100644 --- a/compiler/transf.nim +++ b/compiler/transf.nim @@ -840,7 +840,7 @@ proc liftDeferAux(n: PNode) = let deferPart = newNodeI(nkFinally, n.sons[i].info) deferPart.add n.sons[i].sons[0] var tryStmt = newNodeI(nkTryStmt, n.sons[i].info) - var body = newNodeI(nkStmtList, n.sons[i].info) + var body = newNodeI(n.kind, n.sons[i].info) if i < last: body.sons = n.sons[(i+1)..last] tryStmt.addSon(body) From 41a2a9f00bd7e823223ce4b0830d6c9d8d6bc294 Mon Sep 17 00:00:00 2001 From: Yuriy Glukhov Date: Thu, 6 Aug 2015 22:19:33 +0300 Subject: [PATCH 082/115] Fixes #3185 --- compiler/semexprs.nim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim index af6919d972..0e9b9ae5f1 100644 --- a/compiler/semexprs.nim +++ b/compiler/semexprs.nim @@ -2260,7 +2260,7 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode = result = semStaticStmt(c, n) of nkDefer: n.sons[0] = semExpr(c, n.sons[0]) - if not n.sons[0].typ.isEmptyType: + if not n.sons[0].typ.isEmptyType and not implicitlyDiscardable(n.sons[0]): localError(n.info, errGenerated, "'defer' takes a 'void' expression") #localError(n.info, errGenerated, "'defer' not allowed in this context") else: From 4a7c1d5fd7b0ed37fb4cd9b968f282ac48b53a4b Mon Sep 17 00:00:00 2001 From: Dominik Picheta Date: Thu, 6 Aug 2015 21:45:52 +0100 Subject: [PATCH 083/115] Don't crash on `nil` in get* procs in the JSON module. --- lib/pure/json.nim | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/lib/pure/json.nim b/lib/pure/json.nim index 49915b7e99..540a1a8eb7 100644 --- a/lib/pure/json.nim +++ b/lib/pure/json.nim @@ -608,29 +608,29 @@ proc newJArray*(): JsonNode = proc getStr*(n: JsonNode, default: string = ""): string = ## Retrieves the string value of a `JString JsonNode`. ## - ## Returns ``default`` if ``n`` is not a ``JString``. - if n.kind != JString: return default + ## Returns ``default`` if ``n`` is not a ``JString``, or if ``n`` is nil. + if n.isNil or n.kind != JString: return default else: return n.str proc getNum*(n: JsonNode, default: BiggestInt = 0): BiggestInt = ## Retrieves the int value of a `JInt JsonNode`. ## - ## Returns ``default`` if ``n`` is not a ``JInt``. - if n.kind != JInt: return default + ## Returns ``default`` if ``n`` is not a ``JInt``, or if ``n`` is nil. + if n.isNil or n.kind != JInt: return default else: return n.num proc getFNum*(n: JsonNode, default: float = 0.0): float = ## Retrieves the float value of a `JFloat JsonNode`. ## - ## Returns ``default`` if ``n`` is not a ``JFloat``. - if n.kind != JFloat: return default + ## Returns ``default`` if ``n`` is not a ``JFloat``, or if ``n`` is nil. + if n.isNil or n.kind != JFloat: return default else: return n.fnum proc getBVal*(n: JsonNode, default: bool = false): bool = ## Retrieves the bool value of a `JBool JsonNode`. ## - ## Returns ``default`` if ``n`` is not a ``JBool``. - if n.kind != JBool: return default + ## Returns ``default`` if ``n`` is not a ``JBool``, or if ``n`` is nil. + if n.isNil or n.kind != JBool: return default else: return n.bval proc getFields*(n: JsonNode, @@ -638,15 +638,15 @@ proc getFields*(n: JsonNode, seq[tuple[key: string, val: JsonNode]] = ## Retrieves the key, value pairs of a `JObject JsonNode`. ## - ## Returns ``default`` if ``n`` is not a ``JObject``. - if n.kind != JObject: return default + ## Returns ``default`` if ``n`` is not a ``JObject``, or if ``n`` is nil. + if n.isNil or n.kind != JObject: return default else: return n.fields proc getElems*(n: JsonNode, default: seq[JsonNode] = @[]): seq[JsonNode] = ## Retrieves the int value of a `JArray JsonNode`. ## - ## Returns ``default`` if ``n`` is not a ``JArray``. - if n.kind != JArray: return default + ## Returns ``default`` if ``n`` is not a ``JArray``, or if ``n`` is nil. + if n.isNil or n.kind != JArray: return default else: return n.elems proc `%`*(s: string): JsonNode = From 842a26317cf23859d7dd9ca44818d3fbf872899e Mon Sep 17 00:00:00 2001 From: Yuriy Glukhov Date: Fri, 7 Aug 2015 00:20:40 +0300 Subject: [PATCH 084/115] Fixed defer test. --- tests/exception/tdefer1.nim | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/exception/tdefer1.nim b/tests/exception/tdefer1.nim index d7c6917134..cb3d09b01f 100644 --- a/tests/exception/tdefer1.nim +++ b/tests/exception/tdefer1.nim @@ -3,6 +3,7 @@ discard """ hi 1 hi +2 B A''' """ From 7bc3d7da75c0d3b504c90842016e887407e6ed40 Mon Sep 17 00:00:00 2001 From: Yuriy Glukhov Date: Fri, 7 Aug 2015 17:22:28 +0300 Subject: [PATCH 085/115] Fixes #3186 --- compiler/semstmts.nim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/semstmts.nim b/compiler/semstmts.nim index b9b1759626..3d9363d773 100644 --- a/compiler/semstmts.nim +++ b/compiler/semstmts.nim @@ -1440,7 +1440,7 @@ proc semStmtList(c: PContext, n: PNode, flags: TExprFlags): PNode = of nkPragma, nkCommentStmt, nkNilLit, nkEmpty: discard else: localError(n.sons[j].info, errStmtInvalidAfterReturn) else: discard - if result.len == 1: + if result.len == 1 and result.sons[0].kind != nkDefer: result = result.sons[0] when defined(nimfix): if result.kind == nkCommentStmt and not result.comment.isNil and From 94e504113edfb93ac4eaf59cc78213323fa8a4c0 Mon Sep 17 00:00:00 2001 From: Araq Date: Fri, 7 Aug 2015 19:40:58 +0200 Subject: [PATCH 086/115] fixes #3193 --- lib/system.nim | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/system.nim b/lib/system.nim index 8cae7c5dbe..78769208c0 100644 --- a/lib/system.nim +++ b/lib/system.nim @@ -188,7 +188,7 @@ proc new*(T: typedesc): auto = ## reference to it as result value. ## ## When ``T`` is a ref type then the resulting type will be ``T``, - ## otherwise it will be ``ref T``. + ## otherwise it will be ``ref T``. when (T is ref): var r: T else: @@ -572,6 +572,7 @@ proc unsafeNew*[T](a: var ref T, size: Natural) {.magic: "New", noSideEffect.} ## purposes when you know what you're doing! proc sizeof*[T](x: T): int {.magic: "SizeOf", noSideEffect.} +proc sizeof*(x: typedesc): int {.magic: "SizeOf", noSideEffect.} ## returns the size of ``x`` in bytes. Since this is a low-level proc, ## its usage is discouraged - using ``new`` for the most cases suffices ## that one never needs to know ``x``'s size. As a special semantic rule, From 2e4b59f3d3fc1ac99601988a482bad86a7ef7f53 Mon Sep 17 00:00:00 2001 From: Araq Date: Fri, 7 Aug 2015 20:29:38 +0200 Subject: [PATCH 087/115] fixes #3192 --- compiler/lambdalifting.nim | 2 +- compiler/vm.nim | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/compiler/lambdalifting.nim b/compiler/lambdalifting.nim index d11776cf6d..c669fc7458 100644 --- a/compiler/lambdalifting.nim +++ b/compiler/lambdalifting.nim @@ -184,7 +184,7 @@ proc addHiddenParam(routine: PSym, param: PSym) = var params = routine.ast.sons[paramsPos] # -1 is correct here as param.position is 0 based but we have at position 0 # some nkEffect node: - param.position = params.len-1 + param.position = routine.typ.n.len-1 addSon(params, newSymNode(param)) incl(routine.typ.flags, tfCapturesEnv) assert sfFromGeneric in param.flags diff --git a/compiler/vm.nim b/compiler/vm.nim index d7495d77f1..e381af97fb 100644 --- a/compiler/vm.nim +++ b/compiler/vm.nim @@ -432,7 +432,7 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg = assert regs[rb].kind == rkNode let nb = regs[rb].node case nb.kind - of nkCharLit..nkInt64Lit: + of nkCharLit..nkUInt64Lit: ensureKind(rkInt) regs[ra].intVal = nb.intVal of nkFloatLit..nkFloat64Lit: From c733b3181ea21144fe485a5af3c78543a45d0564 Mon Sep 17 00:00:00 2001 From: Araq Date: Fri, 7 Aug 2015 21:28:45 +0200 Subject: [PATCH 088/115] breaking change: symbol lookups in generics follows spec more closely; fixes #2664 --- compiler/semgnrc.nim | 124 ++++++++++++++++---------------- lib/pure/collections/tables.nim | 8 +-- tests/generics/mclosed_sym.nim | 10 +++ tests/generics/tclosed_sym.nim | 11 +++ 4 files changed, 88 insertions(+), 65 deletions(-) create mode 100644 tests/generics/mclosed_sym.nim create mode 100644 tests/generics/tclosed_sym.nim diff --git a/compiler/semgnrc.nim b/compiler/semgnrc.nim index db910600bd..205af36a85 100644 --- a/compiler/semgnrc.nim +++ b/compiler/semgnrc.nim @@ -39,9 +39,9 @@ type proc semGenericStmt(c: PContext, n: PNode, flags: TSemGenericFlags, ctx: var GenericCtx): PNode -proc semGenericStmtScope(c: PContext, n: PNode, +proc semGenericStmtScope(c: PContext, n: PNode, flags: TSemGenericFlags, - ctx: var GenericCtx): PNode = + ctx: var GenericCtx): PNode = openScope(c) result = semGenericStmt(c, n, flags, ctx) closeScope(c) @@ -73,7 +73,7 @@ proc semGenericStmtSymbol(c: PContext, n: PNode, s: PSym, result = semGenericStmt(c, result, {}, ctx) else: result = symChoice(c, n, s, scOpen) - of skGenericParam: + of skGenericParam: if s.typ != nil and s.typ.kind == tyStatic: if s.typ.n != nil: result = s.typ.n @@ -85,18 +85,18 @@ proc semGenericStmtSymbol(c: PContext, n: PNode, s: PSym, of skParam: result = n styleCheckUse(n.info, s) - of skType: + of skType: if (s.typ != nil) and (s.typ.flags * {tfGenericTypeParam, tfImplicitTypeParam} == {}): result = newSymNodeTypeDesc(s, n.info) - else: + else: result = n styleCheckUse(n.info, s) else: result = newSymNode(s, n.info) styleCheckUse(n.info, s) -proc lookup(c: PContext, n: PNode, flags: TSemGenericFlags, +proc lookup(c: PContext, n: PNode, flags: TSemGenericFlags, ctx: var GenericCtx): PNode = result = n let ident = considerQuotedIdent(n) @@ -118,13 +118,13 @@ proc newDot(n, b: PNode): PNode = result.add(n.sons[0]) result.add(b) -proc fuzzyLookup(c: PContext, n: PNode, flags: TSemGenericFlags, +proc fuzzyLookup(c: PContext, n: PNode, flags: TSemGenericFlags, ctx: var GenericCtx; isMacro: var bool): PNode = assert n.kind == nkDotExpr semIdeForTemplateOrGenericCheck(n, ctx.cursorInBody) let luf = if withinMixin notin flags: {checkUndeclared} else: {} - + var s = qualifiedLookUp(c, n, luf) if s != nil: result = semGenericStmtSymbol(c, n, s, ctx) @@ -141,18 +141,20 @@ proc fuzzyLookup(c: PContext, n: PNode, flags: TSemGenericFlags, elif s.name.id in ctx.toMixin: result = newDot(result, symChoice(c, n, s, scForceOpen)) else: - let sym = semGenericStmtSymbol(c, n, s, ctx) - if sym.kind == nkSym: - result = newDot(result, symChoice(c, n, s, scForceOpen)) + let syms = semGenericStmtSymbol(c, n, s, ctx) + if syms.kind == nkSym: + let choice = symChoice(c, n, s, scForceOpen) + choice.kind = nkClosedSymChoice + result = newDot(result, choice) else: - result = newDot(result, sym) + result = newDot(result, syms) proc addTempDecl(c: PContext; n: PNode; kind: TSymKind) = let s = newSymS(skUnknown, getIdentNode(n), c) addPrelimDecl(c, s) styleCheckDef(n.info, s, kind) -proc semGenericStmt(c: PContext, n: PNode, +proc semGenericStmt(c: PContext, n: PNode, flags: TSemGenericFlags, ctx: var GenericCtx): PNode = result = n #if gCmd == cmdIdeTools: suggestStmt(c, n) @@ -181,16 +183,16 @@ proc semGenericStmt(c: PContext, n: PNode, result = semGenericStmt(c, n.sons[0], flags+{withinBind}, ctx) of nkMixinStmt: result = semMixinStmt(c, n, ctx.toMixin) - of nkCall, nkHiddenCallConv, nkInfix, nkPrefix, nkCommand, nkCallStrLit: + of nkCall, nkHiddenCallConv, nkInfix, nkPrefix, nkCommand, nkCallStrLit: # check if it is an expression macro: checkMinSonsLen(n, 1) let fn = n.sons[0] var s = qualifiedLookUp(c, fn, {}) if s == nil and withinMixin notin flags and - fn.kind in {nkIdent, nkAccQuoted} and + fn.kind in {nkIdent, nkAccQuoted} and considerQuotedIdent(fn).id notin ctx.toMixin: localError(n.info, errUndeclaredIdentifier, fn.renderTree) - + var first = 0 var mixinContext = false if s != nil: @@ -220,7 +222,7 @@ proc semGenericStmt(c: PContext, n: PNode, # we need to put the ``c`` in ``t(c)`` in a mixin context to prevent # the famous "undeclared identifier: it" bug: mixinContext = true - of skUnknown, skParam: + of skUnknown, skParam: # Leave it as an identifier. discard of skProc, skMethod, skIterators, skConverter: @@ -230,9 +232,9 @@ proc semGenericStmt(c: PContext, n: PNode, result.sons[0] = newSymNodeTypeDesc(s, fn.info) styleCheckUse(fn.info, s) first = 1 - of skType: + of skType: # bad hack for generics: - if (s.typ != nil) and (s.typ.kind != tyGenericParam): + if (s.typ != nil) and (s.typ.kind != tyGenericParam): result.sons[0] = newSymNodeTypeDesc(s, fn.info) styleCheckUse(fn.info, s) first = 1 @@ -244,34 +246,34 @@ proc semGenericStmt(c: PContext, n: PNode, result.sons[0] = fuzzyLookup(c, fn, flags, ctx, mixinContext) first = 1 # Consider 'when declared(globalsSlot): ThreadVarSetValue(globalsSlot, ...)' - # in threads.nim: the subtle preprocessing here binds 'globalsSlot' which + # in threads.nim: the subtle preprocessing here binds 'globalsSlot' which # is not exported and yet the generic 'threadProcWrapper' works correctly. let flags = if mixinContext: flags+{withinMixin} else: flags for i in countup(first, sonsLen(result) - 1): result.sons[i] = semGenericStmt(c, result.sons[i], flags, ctx) - of nkIfStmt: - for i in countup(0, sonsLen(n)-1): + of nkIfStmt: + for i in countup(0, sonsLen(n)-1): n.sons[i] = semGenericStmtScope(c, n.sons[i], flags, ctx) of nkWhenStmt: for i in countup(0, sonsLen(n)-1): n.sons[i] = semGenericStmt(c, n.sons[i], flags+{withinMixin}, ctx) - of nkWhileStmt: + of nkWhileStmt: openScope(c) - for i in countup(0, sonsLen(n)-1): + for i in countup(0, sonsLen(n)-1): n.sons[i] = semGenericStmt(c, n.sons[i], flags, ctx) closeScope(c) - of nkCaseStmt: + of nkCaseStmt: openScope(c) n.sons[0] = semGenericStmt(c, n.sons[0], flags, ctx) - for i in countup(1, sonsLen(n)-1): + for i in countup(1, sonsLen(n)-1): var a = n.sons[i] checkMinSonsLen(a, 1) var L = sonsLen(a) - for j in countup(0, L-2): + for j in countup(0, L-2): a.sons[j] = semGenericStmt(c, a.sons[j], flags, ctx) a.sons[L - 1] = semGenericStmtScope(c, a.sons[L-1], flags, ctx) closeScope(c) - of nkForStmt, nkParForStmt: + of nkForStmt, nkParForStmt: var L = sonsLen(n) openScope(c) n.sons[L - 2] = semGenericStmt(c, n.sons[L-2], flags, ctx) @@ -279,27 +281,27 @@ proc semGenericStmt(c: PContext, n: PNode, addTempDecl(c, n.sons[i], skForVar) n.sons[L - 1] = semGenericStmt(c, n.sons[L-1], flags, ctx) closeScope(c) - of nkBlockStmt, nkBlockExpr, nkBlockType: + of nkBlockStmt, nkBlockExpr, nkBlockType: checkSonsLen(n, 2) openScope(c) - if n.sons[0].kind != nkEmpty: + if n.sons[0].kind != nkEmpty: addTempDecl(c, n.sons[0], skLabel) n.sons[1] = semGenericStmt(c, n.sons[1], flags, ctx) closeScope(c) - of nkTryStmt: + of nkTryStmt: checkMinSonsLen(n, 2) n.sons[0] = semGenericStmtScope(c, n.sons[0], flags, ctx) - for i in countup(1, sonsLen(n)-1): + for i in countup(1, sonsLen(n)-1): var a = n.sons[i] checkMinSonsLen(a, 1) var L = sonsLen(a) - for j in countup(0, L-2): + for j in countup(0, L-2): a.sons[j] = semGenericStmt(c, a.sons[j], flags+{withinTypeDesc}, ctx) a.sons[L-1] = semGenericStmtScope(c, a.sons[L-1], flags, ctx) - of nkVarSection, nkLetSection: - for i in countup(0, sonsLen(n) - 1): + of nkVarSection, nkLetSection: + for i in countup(0, sonsLen(n) - 1): var a = n.sons[i] - if a.kind == nkCommentStmt: continue + if a.kind == nkCommentStmt: continue if (a.kind != nkIdentDefs) and (a.kind != nkVarTuple): illFormedAst(a) checkMinSonsLen(a, 3) var L = sonsLen(a) @@ -307,49 +309,49 @@ proc semGenericStmt(c: PContext, n: PNode, a.sons[L-1] = semGenericStmt(c, a.sons[L-1], flags, ctx) for j in countup(0, L-3): addTempDecl(c, getIdentNode(a.sons[j]), skVar) - of nkGenericParams: - for i in countup(0, sonsLen(n) - 1): + of nkGenericParams: + for i in countup(0, sonsLen(n) - 1): var a = n.sons[i] if (a.kind != nkIdentDefs): illFormedAst(a) checkMinSonsLen(a, 3) var L = sonsLen(a) - a.sons[L-2] = semGenericStmt(c, a.sons[L-2], flags+{withinTypeDesc}, ctx) - # do not perform symbol lookup for default expressions - for j in countup(0, L-3): + a.sons[L-2] = semGenericStmt(c, a.sons[L-2], flags+{withinTypeDesc}, ctx) + # do not perform symbol lookup for default expressions + for j in countup(0, L-3): addTempDecl(c, getIdentNode(a.sons[j]), skType) - of nkConstSection: - for i in countup(0, sonsLen(n) - 1): + of nkConstSection: + for i in countup(0, sonsLen(n) - 1): var a = n.sons[i] - if a.kind == nkCommentStmt: continue + if a.kind == nkCommentStmt: continue if (a.kind != nkConstDef): illFormedAst(a) checkSonsLen(a, 3) addTempDecl(c, getIdentNode(a.sons[0]), skConst) a.sons[1] = semGenericStmt(c, a.sons[1], flags+{withinTypeDesc}, ctx) a.sons[2] = semGenericStmt(c, a.sons[2], flags, ctx) of nkTypeSection: - for i in countup(0, sonsLen(n) - 1): + for i in countup(0, sonsLen(n) - 1): var a = n.sons[i] - if a.kind == nkCommentStmt: continue + if a.kind == nkCommentStmt: continue if (a.kind != nkTypeDef): illFormedAst(a) checkSonsLen(a, 3) addTempDecl(c, getIdentNode(a.sons[0]), skType) - for i in countup(0, sonsLen(n) - 1): + for i in countup(0, sonsLen(n) - 1): var a = n.sons[i] - if a.kind == nkCommentStmt: continue + if a.kind == nkCommentStmt: continue if (a.kind != nkTypeDef): illFormedAst(a) checkSonsLen(a, 3) - if a.sons[1].kind != nkEmpty: + if a.sons[1].kind != nkEmpty: openScope(c) a.sons[1] = semGenericStmt(c, a.sons[1], flags, ctx) a.sons[2] = semGenericStmt(c, a.sons[2], flags+{withinTypeDesc}, ctx) closeScope(c) - else: + else: a.sons[2] = semGenericStmt(c, a.sons[2], flags+{withinTypeDesc}, ctx) - of nkEnumTy: + of nkEnumTy: if n.sonsLen > 0: - if n.sons[0].kind != nkEmpty: + if n.sons[0].kind != nkEmpty: n.sons[0] = semGenericStmt(c, n.sons[0], flags+{withinTypeDesc}, ctx) - for i in countup(1, sonsLen(n) - 1): + for i in countup(1, sonsLen(n) - 1): var a: PNode case n.sons[i].kind of nkEnumFieldDef: a = n.sons[i].sons[0] @@ -360,26 +362,26 @@ proc semGenericStmt(c: PContext, n: PNode, discard of nkFormalParams: checkMinSonsLen(n, 1) - if n.sons[0].kind != nkEmpty: + if n.sons[0].kind != nkEmpty: n.sons[0] = semGenericStmt(c, n.sons[0], flags+{withinTypeDesc}, ctx) - for i in countup(1, sonsLen(n) - 1): + for i in countup(1, sonsLen(n) - 1): var a = n.sons[i] if (a.kind != nkIdentDefs): illFormedAst(a) checkMinSonsLen(a, 3) var L = sonsLen(a) a.sons[L-2] = semGenericStmt(c, a.sons[L-2], flags+{withinTypeDesc}, ctx) a.sons[L-1] = semGenericStmt(c, a.sons[L-1], flags, ctx) - for j in countup(0, L-3): + for j in countup(0, L-3): addTempDecl(c, getIdentNode(a.sons[j]), skParam) - of nkProcDef, nkMethodDef, nkConverterDef, nkMacroDef, nkTemplateDef, - nkIteratorDef, nkLambdaKinds: + of nkProcDef, nkMethodDef, nkConverterDef, nkMacroDef, nkTemplateDef, + nkIteratorDef, nkLambdaKinds: checkSonsLen(n, bodyPos + 1) if n.sons[namePos].kind != nkEmpty: addTempDecl(c, getIdentNode(n.sons[0]), skProc) openScope(c) - n.sons[genericParamsPos] = semGenericStmt(c, n.sons[genericParamsPos], + n.sons[genericParamsPos] = semGenericStmt(c, n.sons[genericParamsPos], flags, ctx) - if n.sons[paramsPos].kind != nkEmpty: + if n.sons[paramsPos].kind != nkEmpty: if n.sons[paramsPos].sons[0].kind != nkEmpty: addPrelimDecl(c, newSym(skUnknown, getIdent("result"), nil, n.info)) n.sons[paramsPos] = semGenericStmt(c, n.sons[paramsPos], flags, ctx) @@ -394,7 +396,7 @@ proc semGenericStmt(c: PContext, n: PNode, checkMinSonsLen(n, 2) result.sons[1] = semGenericStmt(c, n.sons[1], flags, ctx) else: - for i in countup(0, sonsLen(n) - 1): + for i in countup(0, sonsLen(n) - 1): result.sons[i] = semGenericStmt(c, n.sons[i], flags, ctx) proc semGenericStmt(c: PContext, n: PNode): PNode = diff --git a/lib/pure/collections/tables.nim b/lib/pure/collections/tables.nim index ec0d9623fb..be6b755ed2 100644 --- a/lib/pure/collections/tables.nim +++ b/lib/pure/collections/tables.nim @@ -226,6 +226,10 @@ proc `$`*[A, B](t: Table[A, B]): string = ## The `$` operator for hash tables. dollarImpl() +proc hasKey*[A, B](t: TableRef[A, B], key: A): bool = + ## returns true iff `key` is in the table `t`. + result = t[].hasKey(key) + template equalsImpl() = if s.counter == t.counter: # different insertion orders mean different 'data' seqs, so we have @@ -293,10 +297,6 @@ proc hasKeyOrPut*[A, B](t: var TableRef[A, B], key: A, val: B): bool = ## returns true iff `key` is in the table, otherwise inserts `value`. t[].hasKeyOrPut(key, val) -proc hasKey*[A, B](t: TableRef[A, B], key: A): bool = - ## returns true iff `key` is in the table `t`. - result = t[].hasKey(key) - proc contains*[A, B](t: TableRef[A, B], key: A): bool = ## alias of `hasKey` for use with the `in` operator. return hasKey[A, B](t, key) diff --git a/tests/generics/mclosed_sym.nim b/tests/generics/mclosed_sym.nim new file mode 100644 index 0000000000..bcccd9a85d --- /dev/null +++ b/tests/generics/mclosed_sym.nim @@ -0,0 +1,10 @@ + +type R* = object + +type Data*[T] = object + d*: T + +proc same(r:R, d:int) = echo "TEST2" + +proc doIt*(d:Data, r:R) = + r.same(1) # Expecting this to invoke the local `same()` method diff --git a/tests/generics/tclosed_sym.nim b/tests/generics/tclosed_sym.nim new file mode 100644 index 0000000000..ff620c2675 --- /dev/null +++ b/tests/generics/tclosed_sym.nim @@ -0,0 +1,11 @@ +discard """ + output: "TEST2" +""" + +# bug #2664 + +import mclosed_sym + +proc same(r:R, d:int) = echo "TEST1" + +doIt(Data[int](d:123), R()) From 87815cbdf76a7aa7c414d0fb82b46778fa1d8b92 Mon Sep 17 00:00:00 2001 From: Araq Date: Fri, 7 Aug 2015 22:32:06 +0200 Subject: [PATCH 089/115] attempt to fix bootstrapping; refs #3139 --- compiler/condsyms.nim | 1 + lib/system.nim | 4 +++- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/compiler/condsyms.nim b/compiler/condsyms.nim index aecbde66e4..297b865b22 100644 --- a/compiler/condsyms.nim +++ b/compiler/condsyms.nim @@ -90,3 +90,4 @@ proc initDefines*() = defineSymbol("nimnode") defineSymbol("nimnomagic64") defineSymbol("nimvarargstyped") + defineSymbol("nimtypedescfixed") diff --git a/lib/system.nim b/lib/system.nim index 78769208c0..7dae074f32 100644 --- a/lib/system.nim +++ b/lib/system.nim @@ -572,12 +572,14 @@ proc unsafeNew*[T](a: var ref T, size: Natural) {.magic: "New", noSideEffect.} ## purposes when you know what you're doing! proc sizeof*[T](x: T): int {.magic: "SizeOf", noSideEffect.} -proc sizeof*(x: typedesc): int {.magic: "SizeOf", noSideEffect.} ## returns the size of ``x`` in bytes. Since this is a low-level proc, ## its usage is discouraged - using ``new`` for the most cases suffices ## that one never needs to know ``x``'s size. As a special semantic rule, ## ``x`` may also be a type identifier (``sizeof(int)`` is valid). +when defined(nimtypedescfixed): + proc sizeof*(x: typedesc): int {.magic: "SizeOf", noSideEffect.} + proc `<`*[T](x: Ordinal[T]): T {.magic: "UnaryLt", noSideEffect.} ## unary ``<`` that can be used for nice looking excluding ranges: ## From 4f8d982d5b0944f7f2bfc905c428ed81c3bd75bd Mon Sep 17 00:00:00 2001 From: Araq Date: Sat, 8 Aug 2015 14:39:32 +0200 Subject: [PATCH 090/115] fixes #2670 --- compiler/semtempl.nim | 23 +++++++++++++++++++---- tests/template/twhen_gensym.nim | 13 +++++++++++++ 2 files changed, 32 insertions(+), 4 deletions(-) create mode 100644 tests/template/twhen_gensym.nim diff --git a/compiler/semtempl.nim b/compiler/semtempl.nim index a138981b7d..4d1eae48f2 100644 --- a/compiler/semtempl.nim +++ b/compiler/semtempl.nim @@ -184,10 +184,25 @@ proc addLocalDecl(c: var TemplCtx, n: var PNode, k: TSymKind) = else: let ident = getIdentNode(c, n) if not isTemplParam(c, ident): - let local = newGenSym(k, ident, c) - addPrelimDecl(c.c, local) - styleCheckDef(n.info, local) - replaceIdentBySym(n, newSymNode(local, n.info)) + # fix #2670, consider: + # + # when b: + # var a = "hi" + # else: + # var a = 5 + # echo a + # + # We need to ensure that both 'a' produce the same gensym'ed symbol. + # So we need only check the *current* scope. + let s = localSearchInScope(c.c, considerQuotedIdent ident) + if s != nil and s.owner == c.owner and sfGenSym in s.flags: + styleCheckUse(n.info, s) + replaceIdentBySym(n, newSymNode(s, n.info)) + else: + let local = newGenSym(k, ident, c) + addPrelimDecl(c.c, local) + styleCheckDef(n.info, local) + replaceIdentBySym(n, newSymNode(local, n.info)) else: replaceIdentBySym(n, ident) diff --git a/tests/template/twhen_gensym.nim b/tests/template/twhen_gensym.nim new file mode 100644 index 0000000000..d84ee6f036 --- /dev/null +++ b/tests/template/twhen_gensym.nim @@ -0,0 +1,13 @@ +discard """ + output: "hi" +""" + +# bug #2670 +template testTemplate(b: bool): stmt = + when b: + var a = "hi" + else: + var a = 5 + echo a + +testTemplate(true) From 930e90a73058b4ae51364f4c8c4f83a04149a645 Mon Sep 17 00:00:00 2001 From: tulayang Date: Sun, 9 Aug 2015 20:35:23 +0800 Subject: [PATCH 091/115] Modify proc parseHeader --- lib/pure/asynchttpserver.nim | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/lib/pure/asynchttpserver.nim b/lib/pure/asynchttpserver.nim index 9e036443c5..d9480475a1 100644 --- a/lib/pure/asynchttpserver.nim +++ b/lib/pure/asynchttpserver.nim @@ -126,8 +126,11 @@ proc parseHeader(line: string): tuple[key, value: string] = var i = 0 i = line.parseUntil(result.key, ':') inc(i) # skip : - i += line.skipWhiteSpace(i) - i += line.parseUntil(result.value, {'\c', '\L'}, i) + if i < len(line): + i += line.skipWhiteSpace(i) + i += line.parseUntil(result.value, {'\c', '\L'}, i) + else: + result.value = "" proc parseProtocol(protocol: string): tuple[orig: string, major, minor: int] = var i = protocol.skipIgnoreCase("HTTP/") From 35f8cc0bdde8e923eaca8830676f1b2ad6ffe300 Mon Sep 17 00:00:00 2001 From: Araq Date: Sun, 9 Aug 2015 20:18:49 +0200 Subject: [PATCH 092/115] fixes #2752 --- compiler/seminst.nim | 7 +++--- tests/generics/tdont_use_inner_scope.nim | 27 ++++++++++++++++++++++++ todo.txt | 4 ---- 3 files changed, 31 insertions(+), 7 deletions(-) create mode 100644 tests/generics/tdont_use_inner_scope.nim diff --git a/compiler/seminst.nim b/compiler/seminst.nim index b2aef63a80..df5865b6be 100644 --- a/compiler/seminst.nim +++ b/compiler/seminst.nim @@ -226,13 +226,14 @@ proc generateInstance(c: PContext, fn: PSym, pt: TIdTable, # NOTE: for access of private fields within generics from a different module # we set the friend module: c.friendModules.add(getModule(fn)) - #let oldScope = c.currentScope - #c.currentScope = fn.scope + let oldScope = c.currentScope + while not isTopLevel(c): c.currentScope = c.currentScope.parent result = copySym(fn, false) incl(result.flags, sfFromGeneric) result.owner = fn result.ast = n pushOwner(result) + openScope(c) internalAssert n.sons[genericParamsPos].kind != nkEmpty n.sons[namePos] = newSymNode(result) @@ -264,7 +265,7 @@ proc generateInstance(c: PContext, fn: PSym, pt: TIdTable, popInfoContext() closeScope(c) # close scope for parameters popOwner() - #c.currentScope = oldScope + c.currentScope = oldScope discard c.friendModules.pop() dec(c.instCounter) if result.kind == skMethod: finishMethod(c, result) diff --git a/tests/generics/tdont_use_inner_scope.nim b/tests/generics/tdont_use_inner_scope.nim new file mode 100644 index 0000000000..45b11fc228 --- /dev/null +++ b/tests/generics/tdont_use_inner_scope.nim @@ -0,0 +1,27 @@ + +# bug #2752 + +import future, sequtils + +proc myFilter[T](it: (iterator(): T), f: (proc(anything: T):bool)): (iterator(): T) = + iterator aNameWhichWillConflict(): T {.closure.}= + for x in it(): + if f(x): + yield x + result = aNameWhichWillConflict + + +iterator testIt():int {.closure.}= + yield -1 + yield 2 + +#let unusedVariable = myFilter(testIt, (x: int) => x > 0) + +proc onlyPos(it: (iterator(): int)): (iterator(): int)= + iterator aNameWhichWillConflict(): int {.closure.}= + var filtered = onlyPos(myFilter(it, (x:int) => x > 0)) + for x in filtered(): + yield x + result = aNameWhichWillConflict + +let x = onlyPos(testIt) diff --git a/todo.txt b/todo.txt index 2955f29176..d7e1f3504d 100644 --- a/todo.txt +++ b/todo.txt @@ -61,10 +61,8 @@ Bugs - VM: Pegs do not work at compile-time - VM: ptr/ref T cannot work in general -- scopes are still broken for generic instantiation! - blocks can "export" an identifier but the CCG generates {} for them ... - ConcreteTypes in a 'case' means we don't check for duplicated case branches -- typedesc matches a generic type T! version 0.9.x @@ -79,8 +77,6 @@ version 0.9.x - implement 'bits' pragmas - we need a magic thisModule symbol - optimize 'genericReset'; 'newException' leads to code bloat -- The 'do' notation might be trimmed so that its only purpose is to pass - multiple multi line constructs to a macro. version 0.9.X From cf4879bc48f39a7fbdd030431be4ffe968e2a555 Mon Sep 17 00:00:00 2001 From: Araq Date: Sun, 9 Aug 2015 20:28:49 +0200 Subject: [PATCH 093/115] added sequtils.reversed; refs #3148 --- lib/pure/collections/sequtils.nim | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/lib/pure/collections/sequtils.nim b/lib/pure/collections/sequtils.nim index 5fac1f0af0..1e75376dfe 100644 --- a/lib/pure/collections/sequtils.nim +++ b/lib/pure/collections/sequtils.nim @@ -451,6 +451,21 @@ template newSeqWith*(len: int, init: expr): expr = result[i] = init result +proc reversed*[T](s: seq[T]): seq[T] = + ## Takes a sequences and returns a new reversed one + ## + ## Example: + ## + ## .. code-block:: + ## + ## let s1 = @[1, 2, 3, 4, 5] + ## let s2 = @[5, 4, 3, 2, 1] + ## assert s2 == s1.reversed() + ## + result = s + let ln = result.len + for i in 0..(ln div 2)-1: swap(result[i], result[ln-i-1]) + when isMainModule: import strutils block: # concat test @@ -615,5 +630,13 @@ when isMainModule: #doAssert a.repeat(-1) == @[] # will not compile! doAssert b.repeat(3) == @[] + block: # reversed tests: + let + a = @[1, 2, 3] + b: seq[int] = @[] + + doAssert a.reversed == @[3, 2, 1] + doAssert b.reversed == @[] + when not defined(testing): echo "Finished doc tests" From 0fc43d3d950714d779221382bbb08775c508b4e9 Mon Sep 17 00:00:00 2001 From: Araq Date: Sun, 9 Aug 2015 23:07:33 +0200 Subject: [PATCH 094/115] updated todo.txt --- todo.txt | 3 +++ 1 file changed, 3 insertions(+) diff --git a/todo.txt b/todo.txt index d7e1f3504d..ee1d6fa87d 100644 --- a/todo.txt +++ b/todo.txt @@ -1,6 +1,9 @@ version 0.11.4 ============== +- deprecate toplevel .closure procs +- deprecate closure iterator construction via ``var cl = foo`` + - document special cased varargs[untyped] and varargs[typed] - The remaining bugs of the lambda lifting pass that is responsible to enable From 50861792f447d1a5213fcc9ebbfefae6ee146830 Mon Sep 17 00:00:00 2001 From: Araq Date: Sun, 9 Aug 2015 23:07:53 +0200 Subject: [PATCH 095/115] fixes #1329 --- compiler/vmgen.nim | 2 ++ tests/vm/tstaticprintseq.nim | 12 +++++++++++- 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/compiler/vmgen.nim b/compiler/vmgen.nim index 1bee9788a6..6a59b33840 100644 --- a/compiler/vmgen.nim +++ b/compiler/vmgen.nim @@ -1105,6 +1105,8 @@ proc genAddrDeref(c: PCtx; n: PNode; dest: var TDest; opc: TOpcode; # nkAddr we must not use 'unneededIndirection', but for deref we use it. if not isAddr and unneededIndirection(n.sons[0]): gen(c, n.sons[0], dest, newflags) + if gfAddrOf notin flags and fitsRegister(n.typ): + c.gABC(n, opcNodeToReg, dest, dest) elif isAddr and isGlobal(n.sons[0]): gen(c, n.sons[0], dest, flags+{gfAddrOf}) else: diff --git a/tests/vm/tstaticprintseq.nim b/tests/vm/tstaticprintseq.nim index b002d366c7..e4a6aa0812 100644 --- a/tests/vm/tstaticprintseq.nim +++ b/tests/vm/tstaticprintseq.nim @@ -18,7 +18,8 @@ bb aa bb 24 -2147483647 2147483647''' +2147483647 2147483647 +5''' """ const s = @[1,2,3] @@ -80,3 +81,12 @@ static: static: var foo2 = int32.high echo foo2, " ", int32.high + +# bug #1329 + +static: + var a: ref int + new(a) + a[] = 5 + + echo a[] From bbf9757b4e70c7a11701656bc7755d7d76669dad Mon Sep 17 00:00:00 2001 From: Araq Date: Sun, 9 Aug 2015 23:21:44 +0200 Subject: [PATCH 096/115] refs #2377 --- compiler/rodread.nim | 2 ++ 1 file changed, 2 insertions(+) diff --git a/compiler/rodread.nim b/compiler/rodread.nim index 92ce002409..e4530c2cc9 100644 --- a/compiler/rodread.nim +++ b/compiler/rodread.nim @@ -898,6 +898,8 @@ proc getBody*(s: PSym): PNode = ## it may perform an expensive reload operation. Otherwise it's a simple ## accessor. assert s.kind in routineKinds + # prevent crashes due to incorrect macro transformations (bug #2377) + if s.ast.isNil or bodyPos >= s.ast.len: return ast.emptyNode result = s.ast.sons[bodyPos] if result == nil: assert s.offset != 0 From f934c9213220bbb4f0ac13aa804ab14c3393202e Mon Sep 17 00:00:00 2001 From: Araq Date: Sun, 9 Aug 2015 23:37:13 +0200 Subject: [PATCH 097/115] fixes #3096 --- compiler/semtypes.nim | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/compiler/semtypes.nim b/compiler/semtypes.nim index 3bb1284fc0..5ae3d16c01 100644 --- a/compiler/semtypes.nim +++ b/compiler/semtypes.nim @@ -508,8 +508,9 @@ proc semRecordCase(c: PContext, n: PNode, check: var IntSet, pos: var int, var typ = skipTypes(a.sons[0].typ, abstractVar-{tyTypeDesc}) if not isOrdinalType(typ): localError(n.info, errSelectorMustBeOrdinal) - elif firstOrd(typ) < 0: - localError(n.info, errOrdXMustNotBeNegative, a.sons[0].sym.name.s) + elif firstOrd(typ) != 0: + localError(n.info, errGenerated, "low(" & $a.sons[0].sym.name.s & + ") must be 0 for discriminant") elif lengthOrd(typ) > 0x00007FFF: localError(n.info, errLenXinvalid, a.sons[0].sym.name.s) var chckCovered = true From 799e0f3274edd9d0fadcc58640afa32b3bdc6da1 Mon Sep 17 00:00:00 2001 From: Araq Date: Sun, 9 Aug 2015 23:53:22 +0200 Subject: [PATCH 098/115] fixes #3066 --- compiler/vmgen.nim | 3 +++ tests/vm/tforwardproc.nim | 17 +++++++++++++++++ 2 files changed, 20 insertions(+) create mode 100644 tests/vm/tforwardproc.nim diff --git a/compiler/vmgen.nim b/compiler/vmgen.nim index 6a59b33840..73fcc5a27a 100644 --- a/compiler/vmgen.nim +++ b/compiler/vmgen.nim @@ -1184,6 +1184,9 @@ proc checkCanEval(c: PCtx; n: PNode) = if s.kind in {skVar, skTemp, skLet, skParam, skResult} and not s.isOwnedBy(c.prc.sym) and s.owner != c.module: cannotEval(n) + elif s.kind in {skProc, skConverter, skMethod, + skIterator, skClosureIterator} and sfForward in s.flags: + cannotEval(n) proc isTemp(c: PCtx; dest: TDest): bool = result = dest >= 0 and c.prc.slots[dest].kind >= slotTempUnknown diff --git a/tests/vm/tforwardproc.nim b/tests/vm/tforwardproc.nim new file mode 100644 index 0000000000..727ac66415 --- /dev/null +++ b/tests/vm/tforwardproc.nim @@ -0,0 +1,17 @@ +discard """ + errormsg: "cannot evaluate at compile time: initArray" + line: 11 +""" + +# bug #3066 + +proc initArray(): array[10, int] + +const + someTable = initArray() + +proc initArray(): array[10, int] = + for f in 0..<10: + result[f] = 3 + +when isMainModule: echo repr(someTable) From 36a90c6044ecc1b5e193766b62ae4ea58aadf45d Mon Sep 17 00:00:00 2001 From: Araq Date: Mon, 10 Aug 2015 01:31:42 +0200 Subject: [PATCH 099/115] fixes #1965 --- compiler/semgnrc.nim | 4 ++-- tests/generics/mmodule_same_as_proc.nim | 2 ++ tests/generics/tmodule_same_as_proc.nim | 9 +++++++++ 3 files changed, 13 insertions(+), 2 deletions(-) create mode 100644 tests/generics/mmodule_same_as_proc.nim create mode 100644 tests/generics/tmodule_same_as_proc.nim diff --git a/compiler/semgnrc.nim b/compiler/semgnrc.nim index 205af36a85..e3b5989192 100644 --- a/compiler/semgnrc.nim +++ b/compiler/semgnrc.nim @@ -57,7 +57,7 @@ proc semGenericStmtSymbol(c: PContext, n: PNode, s: PSym, of skUnknown: # Introduced in this pass! Leave it as an identifier. result = n - of skProc, skMethod, skIterators, skConverter: + of skProc, skMethod, skIterators, skConverter, skModule: result = symChoice(c, n, s, scOpen) of skTemplate: if macroToExpand(s): @@ -225,7 +225,7 @@ proc semGenericStmt(c: PContext, n: PNode, of skUnknown, skParam: # Leave it as an identifier. discard - of skProc, skMethod, skIterators, skConverter: + of skProc, skMethod, skIterators, skConverter, skModule: result.sons[0] = symChoice(c, fn, s, scOption) first = 1 of skGenericParam: diff --git a/tests/generics/mmodule_same_as_proc.nim b/tests/generics/mmodule_same_as_proc.nim new file mode 100644 index 0000000000..048b98336e --- /dev/null +++ b/tests/generics/mmodule_same_as_proc.nim @@ -0,0 +1,2 @@ + +proc mmodule_same_as_proc*(x: string) = discard diff --git a/tests/generics/tmodule_same_as_proc.nim b/tests/generics/tmodule_same_as_proc.nim new file mode 100644 index 0000000000..113ca1bc3b --- /dev/null +++ b/tests/generics/tmodule_same_as_proc.nim @@ -0,0 +1,9 @@ + +# bug #1965 + +import mmodule_same_as_proc + +proc test[T](t: T) = + mmodule_same_as_proc"a" + +test(0) From d230b75e0a7cd49cf5c4e4f687515c296cd2ce5b Mon Sep 17 00:00:00 2001 From: Araq Date: Mon, 10 Aug 2015 01:49:56 +0200 Subject: [PATCH 100/115] removed sequtils.reversed again since it's already in algorithm.nim --- lib/pure/collections/sequtils.nim | 23 ----------------------- 1 file changed, 23 deletions(-) diff --git a/lib/pure/collections/sequtils.nim b/lib/pure/collections/sequtils.nim index 1e75376dfe..5fac1f0af0 100644 --- a/lib/pure/collections/sequtils.nim +++ b/lib/pure/collections/sequtils.nim @@ -451,21 +451,6 @@ template newSeqWith*(len: int, init: expr): expr = result[i] = init result -proc reversed*[T](s: seq[T]): seq[T] = - ## Takes a sequences and returns a new reversed one - ## - ## Example: - ## - ## .. code-block:: - ## - ## let s1 = @[1, 2, 3, 4, 5] - ## let s2 = @[5, 4, 3, 2, 1] - ## assert s2 == s1.reversed() - ## - result = s - let ln = result.len - for i in 0..(ln div 2)-1: swap(result[i], result[ln-i-1]) - when isMainModule: import strutils block: # concat test @@ -630,13 +615,5 @@ when isMainModule: #doAssert a.repeat(-1) == @[] # will not compile! doAssert b.repeat(3) == @[] - block: # reversed tests: - let - a = @[1, 2, 3] - b: seq[int] = @[] - - doAssert a.reversed == @[3, 2, 1] - doAssert b.reversed == @[] - when not defined(testing): echo "Finished doc tests" From c42b05adb46e846cf56efedf44d1f02da769b2b7 Mon Sep 17 00:00:00 2001 From: Yuriy Glukhov Date: Wed, 12 Aug 2015 12:04:26 +0300 Subject: [PATCH 101/115] Fixed --debugger:on option. Removed c_line from termios. --- compiler/ccgstmts.nim | 4 ++-- compiler/cgen.nim | 9 ++++----- lib/nimbase.h | 2 +- lib/posix/termios.nim | 1 - lib/system/debugger.nim | 12 ++++++------ 5 files changed, 13 insertions(+), 15 deletions(-) diff --git a/compiler/ccgstmts.nim b/compiler/ccgstmts.nim index f12a24fa21..f4a7c4400b 100644 --- a/compiler/ccgstmts.nim +++ b/compiler/ccgstmts.nim @@ -102,7 +102,7 @@ proc assignLabel(b: var TBlock): Rope {.inline.} = proc blockBody(b: var TBlock): Rope = result = b.sections[cpsLocals] if b.frameLen > 0: - result.addf("F.len+=$1;$n", [b.frameLen.rope]) + result.addf("FR.len+=$1;$n", [b.frameLen.rope]) result.add(b.sections[cpsInit]) result.add(b.sections[cpsStmts]) @@ -123,7 +123,7 @@ proc endBlock(p: BProc) = ~"}$n" let frameLen = p.blocks[topBlock].frameLen if frameLen > 0: - blockEnd.addf("F.len-=$1;$n", [frameLen.rope]) + blockEnd.addf("FR.len-=$1;$n", [frameLen.rope]) endBlock(p, blockEnd) proc genSimpleBlock(p: BProc, stmts: PNode) {.inline.} = diff --git a/compiler/cgen.nim b/compiler/cgen.nim index 2e95918cc7..13514818e4 100644 --- a/compiler/cgen.nim +++ b/compiler/cgen.nim @@ -375,7 +375,7 @@ proc localDebugInfo(p: BProc, s: PSym) = var a = "&" & s.loc.r if s.kind == skParam and ccgIntroducedPtr(s): a = s.loc.r lineF(p, cpsInit, - "F.s[$1].address = (void*)$3; F.s[$1].typ = $4; F.s[$1].name = $2;$n", + "FR.s[$1].address = (void*)$3; FR.s[$1].typ = $4; FR.s[$1].name = $2;$n", [p.maxFrameLen.rope, makeCString(normalize(s.name.s)), a, genTypeInfo(p.module, s.loc.t)]) inc(p.maxFrameLen) @@ -597,7 +597,7 @@ proc cgsym(m: BModule, name: string): Rope = of skProc, skMethod, skConverter, skIterators: genProc(m, sym) of skVar, skResult, skLet: genVarPrototype(m, sym) of skType: discard getTypeDesc(m, sym.typ) - else: internalError("cgsym: " & name) + else: internalError("cgsym: " & name & ": " & $sym.kind) else: # we used to exclude the system module from this check, but for DLL # generation support this sloppyness leads to hard to detect bugs, so @@ -623,7 +623,7 @@ proc retIsNotVoid(s: PSym): bool = proc initFrame(p: BProc, procname, filename: Rope): Rope = discard cgsym(p.module, "nimFrame") if p.maxFrameLen > 0: - discard cgsym(p.module, "TVarSlot") + discard cgsym(p.module, "VarSlot") result = rfmt(nil, "\tnimfrs($1, $2, $3, $4)$N", procname, filename, p.maxFrameLen.rope, p.blocks[0].frameLen.rope) @@ -1017,7 +1017,7 @@ proc genInitCode(m: BModule) = var procname = makeCString(m.module.name.s) add(prc, initFrame(m.initProc, procname, m.module.info.quotedFilename)) else: - add(prc, ~"\tTFrame F; F.len = 0;$N") + add(prc, ~"\tTFrame F; FR.len = 0;$N") add(prc, genSectionStart(cpsInit)) add(prc, m.preInitProc.s(cpsInit)) @@ -1329,4 +1329,3 @@ proc cgenWriteModules* = if generatedHeader != nil: writeHeader(generatedHeader) const cgenPass* = makePass(myOpen, myOpenCached, myProcess, myClose) - diff --git a/lib/nimbase.h b/lib/nimbase.h index e796ba7357..d3108812c6 100644 --- a/lib/nimbase.h +++ b/lib/nimbase.h @@ -347,7 +347,7 @@ struct TFrame { FR.procname = proc; FR.filename = file; FR.line = 0; FR.len = 0; nimFrame(&FR); #define nimfrs(proc, file, slots, length) \ - struct {TFrame* prev;NCSTRING procname;NI line;NCSTRING filename; NI len; TVarSlot s[slots];} FR; \ + struct {TFrame* prev;NCSTRING procname;NI line;NCSTRING filename; NI len; VarSlot s[slots];} FR; \ FR.procname = proc; FR.filename = file; FR.line = 0; FR.len = length; nimFrame((TFrame*)&FR); #define nimln(n, file) \ diff --git a/lib/posix/termios.nim b/lib/posix/termios.nim index 44f0290393..c3934c6a95 100644 --- a/lib/posix/termios.nim +++ b/lib/posix/termios.nim @@ -24,7 +24,6 @@ type c_oflag*: Cflag # output mode flags c_cflag*: Cflag # control mode flags c_lflag*: Cflag # local mode flags - c_line*: cuchar # line discipline c_cc*: array[NCCS, cuchar] # control characters # cc characters diff --git a/lib/system/debugger.nim b/lib/system/debugger.nim index 63ccd770bf..b18c61755b 100644 --- a/lib/system/debugger.nim +++ b/lib/system/debugger.nim @@ -21,7 +21,7 @@ type # only slots that are # needed are allocated and not 10_000, # except for the global data description. - f: Frame + f: TFrame slots: array[0..10_000, VarSlot] {.deprecated: [TVarSlot: VarSlot, TExtendedFrame: ExtendedFrame].} @@ -66,7 +66,7 @@ var dbgBP: array[0..127, Breakpoint] # breakpoints dbgBPlen: int dbgBPbloom: int64 # we use a bloom filter to speed up breakpoint checking - + dbgFilenames*: array[0..300, cstring] ## registered filenames; ## 'nil' terminated dbgFilenameLen: int @@ -197,7 +197,7 @@ proc genericHashAux(dest: pointer, n: ptr TNimNode, shallow: bool, result = genericHashAux(cast[pointer](d +% n.offset), n.typ, shallow, h) of nkList: result = h - for i in 0..n.len-1: + for i in 0..n.len-1: result = result !& genericHashAux(dest, n.sons[i], shallow, result) of nkCase: result = h !& hash(cast[pointer](d +% n.offset), n.typ.size) @@ -205,7 +205,7 @@ proc genericHashAux(dest: pointer, n: ptr TNimNode, shallow: bool, if m != nil: result = genericHashAux(dest, m, shallow, result) of nkNone: sysAssert(false, "genericHashAux") -proc genericHashAux(dest: pointer, mt: PNimType, shallow: bool, +proc genericHashAux(dest: pointer, mt: PNimType, shallow: bool, h: Hash): Hash = sysAssert(mt != nil, "genericHashAux 2") case mt.kind @@ -257,7 +257,7 @@ proc genericHashAux(dest: pointer, mt: PNimType, shallow: bool, proc genericHash(dest: pointer, mt: PNimType): int = result = genericHashAux(dest, mt, false, 0) - + proc dbgRegisterWatchpoint(address: pointer, name: cstring, typ: PNimType) {.compilerproc.} = let L = watchPointsLen @@ -285,7 +285,7 @@ var ## Only code compiled with the ``debugger:on`` switch calls this hook. dbgWatchpointHook*: proc (watchpointName: cstring) {.nimcall.} - + proc checkWatchpoints = let L = watchPointsLen for i in 0.. Date: Wed, 12 Aug 2015 18:31:38 +0300 Subject: [PATCH 102/115] Fixed fromSeconds function JS variant --- lib/pure/times.nim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/pure/times.nim b/lib/pure/times.nim index d9abbed1a1..f7c3b59c59 100644 --- a/lib/pure/times.nim +++ b/lib/pure/times.nim @@ -550,7 +550,7 @@ elif defined(JS): proc valueOf(time: Time): float {.importcpp: "getTime", tags:[]} - proc fromSeconds(since1970: float): Time = result = newDate(since1970) + proc fromSeconds(since1970: float): Time = result = newDate(since1970 * 1000) proc toSeconds(time: Time): float = result = time.valueOf() / 1000 From 2f721f0d9928a4ae25b2f2b3761d8861a13e877d Mon Sep 17 00:00:00 2001 From: Araq Date: Mon, 10 Aug 2015 02:50:15 +0200 Subject: [PATCH 103/115] 'parallel' now requires --experimental mode --- compiler/semexprs.nim | 2 ++ tests/parallel/nim.cfg | 1 + todo.txt | 1 - web/news.txt | 3 +++ 4 files changed, 6 insertions(+), 1 deletion(-) diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim index 0e9b9ae5f1..bb3ec9df0f 100644 --- a/compiler/semexprs.nim +++ b/compiler/semexprs.nim @@ -1720,6 +1720,8 @@ proc semMagic(c: PContext, n: PNode, s: PSym, flags: TExprFlags): PNode = result = newStrNodeT(renderTree(n[1], {renderNoComments}), n) result.typ = getSysType(tyString) of mParallel: + if not experimentalMode(c): + localError(n.info, "use the {.experimental.} pragma to enable 'parallel'") result = setMs(n, s) var x = n.lastSon if x.kind == nkDo: x = x.sons[bodyPos] diff --git a/tests/parallel/nim.cfg b/tests/parallel/nim.cfg index b81c897213..d0d2a93052 100644 --- a/tests/parallel/nim.cfg +++ b/tests/parallel/nim.cfg @@ -1 +1,2 @@ threads:on +--experimental diff --git a/todo.txt b/todo.txt index ee1d6fa87d..f9abed8910 100644 --- a/todo.txt +++ b/todo.txt @@ -13,7 +13,6 @@ version 0.11.4 - make '--implicitStatic:on' the default; then we can also clean up the 'static[T]' mess in the compiler! -- Mark the 'parallel' statement as experimental. - add "all threads are blocked" detection to 'spawn' - Deprecate ``immediate`` for templates and macros - make 'nil' work for 'add': diff --git a/web/news.txt b/web/news.txt index 518793a4a8..16ac286ae6 100644 --- a/web/news.txt +++ b/web/news.txt @@ -44,6 +44,9 @@ News been removed. - ``macros.high`` never worked and the manual says ``high`` cannot be overloaded, so we removed it with no deprecation cycle. + - To use the ``parallel`` statement you now have to + use the ``--experimental`` mode. + Library additions From 2299318d1e303ffdf5a9a79663e2c3b126bb5352 Mon Sep 17 00:00:00 2001 From: Araq Date: Wed, 12 Aug 2015 10:11:58 +0200 Subject: [PATCH 104/115] toplevel .closure procs are deprecated --- compiler/semstmts.nim | 3 +++ todo.txt | 1 - web/news.txt | 4 ++++ 3 files changed, 7 insertions(+), 1 deletion(-) diff --git a/compiler/semstmts.nim b/compiler/semstmts.nim index 3d9363d773..ffda6a1bb9 100644 --- a/compiler/semstmts.nim +++ b/compiler/semstmts.nim @@ -1220,6 +1220,9 @@ proc semProcAux(c: PContext, n: PNode, kind: TSymKind, if n.sons[patternPos].kind != nkEmpty: c.patterns.add(s) if isAnon: result.typ = s.typ + if isTopLevel(c) and s.kind != skClosureIterator and + s.typ.callConv == ccClosure: + message(s.info, warnDeprecated, "top level '.closure' calling convention") proc determineType(c: PContext, s: PSym) = if s.typ != nil: return diff --git a/todo.txt b/todo.txt index f9abed8910..57cbc83bf0 100644 --- a/todo.txt +++ b/todo.txt @@ -1,7 +1,6 @@ version 0.11.4 ============== -- deprecate toplevel .closure procs - deprecate closure iterator construction via ``var cl = foo`` - document special cased varargs[untyped] and varargs[typed] diff --git a/web/news.txt b/web/news.txt index 16ac286ae6..c93bb821f2 100644 --- a/web/news.txt +++ b/web/news.txt @@ -46,6 +46,10 @@ News overloaded, so we removed it with no deprecation cycle. - To use the ``parallel`` statement you now have to use the ``--experimental`` mode. + - Toplevel procs of calling convention ``closure`` never worked reliably + and are now deprecated and will be removed from the language. Instead you + have to insert type conversions + like ``(proc (a, b: int) {.closure.})(myToplevelProc)`` if necessary. From dd0acfdf375b45a226f44feb4f04aef2c0aa0ac3 Mon Sep 17 00:00:00 2001 From: Araq Date: Fri, 14 Aug 2015 02:16:12 +0200 Subject: [PATCH 105/115] todo.txt update --- todo.txt | 2 -- 1 file changed, 2 deletions(-) diff --git a/todo.txt b/todo.txt index 57cbc83bf0..0d4dbd2ad3 100644 --- a/todo.txt +++ b/todo.txt @@ -1,8 +1,6 @@ version 0.11.4 ============== -- deprecate closure iterator construction via ``var cl = foo`` - - document special cased varargs[untyped] and varargs[typed] - The remaining bugs of the lambda lifting pass that is responsible to enable From 134c44f3be373a94fd5ee144d98cd296ee967107 Mon Sep 17 00:00:00 2001 From: Araq Date: Fri, 14 Aug 2015 02:18:12 +0200 Subject: [PATCH 106/115] refs #2973; manual contains almost all pragmas now, few are left in the compiler guide --- doc/manual/pragmas.txt | 464 +++++++++++++++++++++++++++++++++++++++++ doc/nimc.txt | 457 ---------------------------------------- 2 files changed, 464 insertions(+), 457 deletions(-) diff --git a/doc/manual/pragmas.txt b/doc/manual/pragmas.txt index 51125f5767..8166994a94 100644 --- a/doc/manual/pragmas.txt +++ b/doc/manual/pragmas.txt @@ -511,3 +511,467 @@ Example: add "foo" add "bar" + +Implementation Specific Pragmas +=============================== + +This section describes additional pragmas that the current Nim implementation +supports but which should not be seen as part of the language specification. + + +Volatile pragma +--------------- +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++). + +**Note**: This pragma will not exist for the LLVM backend. + + +NoDecl pragma +------------- +The ``noDecl`` pragma can be applied to almost any symbol (variable, proc, +type, etc.) and is sometimes useful for interoperability with C: +It tells Nim that it should not generate a declaration for the symbol in +the C code. For example: + +.. code-block:: Nim + var + EACCES {.importc, noDecl.}: cint # pretend EACCES was a variable, as + # Nim does not know its value + +However, the ``header`` pragma is often the better alternative. + +**Note**: This will not work for the LLVM backend. + + +Header pragma +------------- +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``: + +.. code-block:: Nim + type + PFile {.importc: "FILE*", header: "".} = distinct pointer + # import C's FILE* type; Nim will treat it as a new pointer type + +The ``header`` pragma always expects a string constant. The string contant +contains the header file: As usual for C, a system header file is enclosed +in angle brackets: ``<>``. If no angle brackets are given, Nim +encloses the header file in ``""`` in the generated C code. + +**Note**: This will not work for the LLVM backend. + + +IncompleteStruct pragma +----------------------- +The ``incompleteStruct`` pragma tells the compiler to not use the +underlying C ``struct`` in a ``sizeof`` expression: + +.. code-block:: Nim + type + DIR* {.importc: "DIR", header: "", + final, pure, incompleteStruct.} = object + + +Compile pragma +-------------- +The ``compile`` pragma can be used to compile and link a C/C++ source file +with the project: + +.. code-block:: Nim + {.compile: "myfile.cpp".} + +**Note**: Nim computes a SHA1 checksum and only recompiles the file if it +has changed. You can use the ``-f`` command line option to force recompilation +of the file. + + +Link pragma +----------- +The ``link`` pragma can be used to link an additional file with the project: + +.. code-block:: Nim + {.link: "myfile.o".} + + +PassC pragma +------------ +The ``passC`` pragma can be used to pass additional parameters to the C +compiler like you would using the commandline switch ``--passC``: + +.. code-block:: Nim + {.passC: "-Wall -Werror".} + +Note that you can use ``gorge`` from the `system module `_ to +embed parameters from an external command at compile time: + +.. code-block:: Nim + {.passC: gorge("pkg-config --cflags sdl").} + +PassL pragma +------------ +The ``passL`` pragma can be used to pass additional parameters to the linker +like you would using the commandline switch ``--passL``: + +.. code-block:: Nim + {.passL: "-lSDLmain -lSDL".} + +Note that you can use ``gorge`` from the `system module `_ to +embed parameters from an external command at compile time: + +.. code-block:: Nim + {.passL: gorge("pkg-config --libs sdl").} + + +Emit pragma +----------- +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. + +Example: + +.. code-block:: Nim + {.emit: """ + static int cvariable = 420; + """.} + + {.push stackTrace:off.} + proc embedsC() = + var nimVar = 89 + # use backticks to access Nim symbols within an emit section: + {.emit: """fprintf(stdout, "%d\n", cvariable + (int)`nimVar`);""".} + {.pop.} + + embedsC() + +As can be seen from the example, to Nim symbols can be referred via backticks. +Use two backticks to produce a single verbatim backtick. + +For a toplevel emit statement the section where in the generated C/C++ file +the code should be emitted can be influenced via the +prefixes ``/*TYPESECTION*/`` or ``/*VARSECTION*/`` or ``/*INCLUDESECTION*/``: + +.. code-block:: Nim + {.emit: """/*TYPESECTION*/ + struct Vector3 { + public: + Vector3(): x(5) {} + Vector3(float x_): x(x_) {} + float x; + }; + """.} + + type Vector3 {.importcpp: "Vector3", nodecl} = object + x: cfloat + + proc constructVector3(a: cfloat): Vector3 {.importcpp: "Vector3(@)", nodecl} + + +ImportCpp pragma +---------------- + +**Note**: `c2nim `_ can parse a large subset of C++ and knows +about the ``importcpp`` pragma pattern language. It is not necessary +to know all the details described here. + + +Similar to the `importc pragma for C `_, the +``importcpp`` pragma can be used to import `C++`:idx: methods or C++ symbols +in general. The generated code then uses the C++ method calling +syntax: ``obj->method(arg)``. In combination with the ``header`` and ``emit`` +pragmas this allows *sloppy* interfacing with libraries written in C++: + +.. code-block:: Nim + # Horrible example of how to interface with a C++ engine ... ;-) + + {.link: "/usr/lib/libIrrlicht.so".} + + {.emit: """ + using namespace irr; + using namespace core; + using namespace scene; + using namespace video; + using namespace io; + using namespace gui; + """.} + + const + irr = "" + + type + IrrlichtDeviceObj {.final, header: irr, + importcpp: "IrrlichtDevice".} = object + IrrlichtDevice = ptr IrrlichtDeviceObj + + proc createDevice(): IrrlichtDevice {. + header: irr, importcpp: "createDevice(@)".} + proc run(device: IrrlichtDevice): bool {. + header: irr, importcpp: "#.run(@)".} + +The compiler needs to be told to generate C++ (command ``cpp``) for +this to work. The conditional symbol ``cpp`` is defined when the compiler +emits C++ code. + + +Namespaces +~~~~~~~~~~ + +The *sloppy interfacing* example uses ``.emit`` to produce ``using namespace`` +declarations. It is usually much better to instead refer to the imported name +via the ``namespace::identifier`` notation: + +.. code-block:: nim + type + IrrlichtDeviceObj {.final, header: irr, + importcpp: "irr::IrrlichtDevice".} = object + + +Importcpp for enums +~~~~~~~~~~~~~~~~~~~ + +When ``importcpp`` is applied to an enum type the numerical enum values are +annotated with the C++ enum type, like in this example: ``((TheCppEnum)(3))``. +(This turned out to be the simplest way to implement it.) + + +Importcpp for procs +~~~~~~~~~~~~~~~~~~~ + +Note that the ``importcpp`` variant for procs uses a somewhat cryptic pattern +language for maximum flexibility: + +- A hash ``#`` symbol is replaced by the first or next argument. +- A dot following the hash ``#.`` indicates that the call should use C++'s dot + or arrow notation. +- An at symbol ``@`` is replaced by the remaining arguments, separated by + commas. + +For example: + +.. code-block:: nim + proc cppMethod(this: CppObj, a, b, c: cint) {.importcpp: "#.CppMethod(@)".} + var x: ptr CppObj + cppMethod(x[], 1, 2, 3) + +Produces: + +.. code-block:: C + x->CppMethod(1, 2, 3) + +As a special rule to keep backwards compatibility with older versions of the +``importcpp`` pragma, if there is no special pattern +character (any of ``# ' @``) at all, C++'s +dot or arrow notation is assumed, so the above example can also be written as: + +.. code-block:: nim + proc cppMethod(this: CppObj, a, b, c: cint) {.importcpp: "CppMethod".} + +Note that the pattern language naturally also covers C++'s operator overloading +capabilities: + +.. code-block:: nim + proc vectorAddition(a, b: Vec3): Vec3 {.importcpp: "# + #".} + proc dictLookup(a: Dict, k: Key): Value {.importcpp: "#[#]".} + + +- An apostrophe ``'`` followed by an integer ``i`` in the range 0..9 + is replaced by the i'th parameter *type*. The 0th position is the result + type. This can be used to pass types to C++ function templates. Between + the ``'`` and the digit an asterisk can be used to get to the base type + of the type. (So it "takes away a star" from the type; ``T*`` becomes ``T``.) + Two stars can be used to get to the element type of the element type etc. + +For example: + +.. code-block:: nim + + type Input {.importcpp: "System::Input".} = object + proc getSubsystem*[T](): ptr T {.importcpp: "SystemManager::getSubsystem<'*0>()", nodecl.} + + let x: ptr Input = getSubsystem[Input]() + +Produces: + +.. code-block:: C + x = SystemManager::getSubsystem() + + +- ``#@`` is a special case to support a ``cnew`` operation. It is required so + that the call expression is inlined directly, without going through a + temporary location. This is only required to circumvent a limitation of the + current code generator. + +For example C++'s ``new`` operator can be "imported" like this: + +.. code-block:: nim + proc cnew*[T](x: T): ptr T {.importcpp: "(new '*0#@)", nodecl.} + + # constructor of 'Foo': + proc constructFoo(a, b: cint): Foo {.importcpp: "Foo(@)".} + + let x = cnew constructFoo(3, 4) + +Produces: + +.. code-block:: C + x = new Foo(3, 4) + +However, depending on the use case ``new Foo`` can also be wrapped like this +instead: + +.. code-block:: nim + proc newFoo(a, b: cint): ptr Foo {.importcpp: "new Foo(@)".} + + let x = newFoo(3, 4) + + +Wrapping constructors +~~~~~~~~~~~~~~~~~~~~~ + +Sometimes a C++ class has a private copy constructor and so code like +``Class c = Class(1,2);`` must not be generated but instead ``Class c(1,2);``. +For this purpose the Nim proc that wraps a C++ constructor needs to be +annotated with the `constructor`:idx: pragma. This pragma also helps to generate +faster C++ code since construction then doesn't invoke the copy constructor: + +.. code-block:: nim + # a better constructor of 'Foo': + proc constructFoo(a, b: cint): Foo {.importcpp: "Foo(@)", constructor.} + + +Wrapping destructors +~~~~~~~~~~~~~~~~~~~~ + +Since Nim generates C++ directly, any destructor is called implicitly by the +C++ compiler at the scope exits. This means that often one can get away with +not wrapping the destructor at all! However when it needs to be invoked +explicitly, it needs to be wrapped. But the pattern language already provides +everything that is required for that: + +.. code-block:: nim + proc destroyFoo(this: var Foo) {.importcpp: "#.~Foo()".} + + +Importcpp for objects +~~~~~~~~~~~~~~~~~~~~~ + +Generic ``importcpp``'ed objects are mapped to C++ templates. This means that +you can import C++'s templates rather easily without the need for a pattern +language for object types: + +.. code-block:: nim + type + StdMap {.importcpp: "std::map", header: "".} [K, V] = object + proc `[]=`[K, V](this: var StdMap[K, V]; key: K; val: V) {. + importcpp: "#[#] = #", header: "".} + + var x: StdMap[cint, cdouble] + x[6] = 91.4 + + +Produces: + +.. code-block:: C + std::map x; + x[6] = 91.4; + + +- If more precise control is needed, the apostrophe ``'`` can be used in the + supplied pattern to denote the concrete type parameters of the generic type. + See the usage of the apostrophe operator in proc patterns for more details. + +.. code-block:: nim + + type + VectorIterator {.importcpp: "std::vector<'0>::iterator".} [T] = object + + var x: VectorIterator[cint] + + +Produces: + +.. code-block:: C + + std::vector::iterator x; + + +ImportObjC pragma +----------------- +Similar to the `importc pragma for C `_, 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* interfacing with libraries written in Objective C: + +.. code-block:: Nim + # horrible example of how to interface with GNUStep ... + + {.passL: "-lobjc".} + {.emit: """ + #include + @interface Greeter:Object + { + } + + - (void)greet:(long)x y:(long)dummy; + @end + + #include + @implementation Greeter + + - (void)greet:(long)x y:(long)dummy + { + printf("Hello, World!\n"); + } + @end + + #include + """.} + + type + Id {.importc: "id", header: "", final.} = distinct int + + proc newGreeter: Id {.importobjc: "Greeter new", nodecl.} + proc greet(self: Id, x, y: int) {.importobjc: "greet", nodecl.} + proc free(self: Id) {.importobjc: "free", nodecl.} + + var g = newGreeter() + g.greet(12, 34) + g.free() + +The compiler needs to be told to generate Objective C (command ``objc``) for +this to work. The conditional symbol ``objc`` is defined when the compiler +emits Objective C code. + + +CodegenDecl pragma +------------------ + +The ``codegenDecl`` pragma can be used to directly influence Nim's code +generator. It receives a format string that determines how the variable or +proc is declared in the generated code: + +.. code-block:: nim + var + a {.codegenDecl: "$# progmem $#".}: int + + proc myinterrupt() {.codegenDecl: "__interrupt $# $#$#".} = + echo "realistic interrupt handler" + + +InjectStmt pragma +----------------- + +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: + +.. code-block:: nim + {.injectStmt: gcInvariants().} + + # ... complex code here that produces crashes ... + diff --git a/doc/nimc.txt b/doc/nimc.txt index 15c9f29559..95449d0600 100644 --- a/doc/nimc.txt +++ b/doc/nimc.txt @@ -263,454 +263,6 @@ Nim manual. Some of the features here only make sense for the C code generator and are subject to change. -NoDecl pragma -------------- -The ``noDecl`` pragma can be applied to almost any symbol (variable, proc, -type, etc.) and is sometimes useful for interoperability with C: -It tells Nim that it should not generate a declaration for the symbol in -the C code. For example: - -.. code-block:: Nim - var - EACCES {.importc, noDecl.}: cint # pretend EACCES was a variable, as - # Nim does not know its value - -However, the ``header`` pragma is often the better alternative. - -**Note**: This will not work for the LLVM backend. - - -Header pragma -------------- -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``: - -.. code-block:: Nim - type - PFile {.importc: "FILE*", header: "".} = distinct pointer - # import C's FILE* type; Nim will treat it as a new pointer type - -The ``header`` pragma always expects a string constant. The string contant -contains the header file: As usual for C, a system header file is enclosed -in angle brackets: ``<>``. If no angle brackets are given, Nim -encloses the header file in ``""`` in the generated C code. - -**Note**: This will not work for the LLVM backend. - - -IncompleteStruct pragma ------------------------ -The ``incompleteStruct`` pragma tells the compiler to not use the -underlying C ``struct`` in a ``sizeof`` expression: - -.. code-block:: Nim - type - DIR* {.importc: "DIR", header: "", - final, pure, incompleteStruct.} = object - - -Compile pragma --------------- -The ``compile`` pragma can be used to compile and link a C/C++ source file -with the project: - -.. code-block:: Nim - {.compile: "myfile.cpp".} - -**Note**: Nim computes a SHA1 checksum and only recompiles the file if it -has changed. You can use the ``-f`` command line option to force recompilation -of the file. - - -Link pragma ------------ -The ``link`` pragma can be used to link an additional file with the project: - -.. code-block:: Nim - {.link: "myfile.o".} - - -PassC pragma ------------- -The ``passC`` pragma can be used to pass additional parameters to the C -compiler like you would using the commandline switch ``--passC``: - -.. code-block:: Nim - {.passC: "-Wall -Werror".} - -Note that you can use ``gorge`` from the `system module `_ to -embed parameters from an external command at compile time: - -.. code-block:: Nim - {.passC: gorge("pkg-config --cflags sdl").} - -PassL pragma ------------- -The ``passL`` pragma can be used to pass additional parameters to the linker -like you would using the commandline switch ``--passL``: - -.. code-block:: Nim - {.passL: "-lSDLmain -lSDL".} - -Note that you can use ``gorge`` from the `system module `_ to -embed parameters from an external command at compile time: - -.. code-block:: Nim - {.passL: gorge("pkg-config --libs sdl").} - - -Emit pragma ------------ -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. - -Example: - -.. code-block:: Nim - {.emit: """ - static int cvariable = 420; - """.} - - {.push stackTrace:off.} - proc embedsC() = - var nimVar = 89 - # use backticks to access Nim symbols within an emit section: - {.emit: """fprintf(stdout, "%d\n", cvariable + (int)`nimVar`);""".} - {.pop.} - - embedsC() - -As can be seen from the example, to Nim symbols can be referred via backticks. -Use two backticks to produce a single verbatim backtick. - -For a toplevel emit statement the section where in the generated C/C++ file -the code should be emitted can be influenced via the -prefixes ``/*TYPESECTION*/`` or ``/*VARSECTION*/`` or ``/*INCLUDESECTION*/``: - -.. code-block:: Nim - {.emit: """/*TYPESECTION*/ - struct Vector3 { - public: - Vector3(): x(5) {} - Vector3(float x_): x(x_) {} - float x; - }; - """.} - - type Vector3 {.importcpp: "Vector3", nodecl} = object - x: cfloat - - proc constructVector3(a: cfloat): Vector3 {.importcpp: "Vector3(@)", nodecl} - - -ImportCpp pragma ----------------- - -**Note**: `c2nim `_ can parse a large subset of C++ and knows -about the ``importcpp`` pragma pattern language. It is not necessary -to know all the details described here. - - -Similar to the `importc pragma for C `_, the -``importcpp`` pragma can be used to import `C++`:idx: methods or C++ symbols -in general. The generated code then uses the C++ method calling -syntax: ``obj->method(arg)``. In combination with the ``header`` and ``emit`` -pragmas this allows *sloppy* interfacing with libraries written in C++: - -.. code-block:: Nim - # Horrible example of how to interface with a C++ engine ... ;-) - - {.link: "/usr/lib/libIrrlicht.so".} - - {.emit: """ - using namespace irr; - using namespace core; - using namespace scene; - using namespace video; - using namespace io; - using namespace gui; - """.} - - const - irr = "" - - type - IrrlichtDeviceObj {.final, header: irr, - importcpp: "IrrlichtDevice".} = object - IrrlichtDevice = ptr IrrlichtDeviceObj - - proc createDevice(): IrrlichtDevice {. - header: irr, importcpp: "createDevice(@)".} - proc run(device: IrrlichtDevice): bool {. - header: irr, importcpp: "#.run(@)".} - -The compiler needs to be told to generate C++ (command ``cpp``) for -this to work. The conditional symbol ``cpp`` is defined when the compiler -emits C++ code. - - -Namespaces -~~~~~~~~~~ - -The *sloppy interfacing* example uses ``.emit`` to produce ``using namespace`` -declarations. It is usually much better to instead refer to the imported name -via the ``namespace::identifier`` notation: - -.. code-block:: nim - type - IrrlichtDeviceObj {.final, header: irr, - importcpp: "irr::IrrlichtDevice".} = object - - -Importcpp for enums -~~~~~~~~~~~~~~~~~~~ - -When ``importcpp`` is applied to an enum type the numerical enum values are -annotated with the C++ enum type, like in this example: ``((TheCppEnum)(3))``. -(This turned out to be the simplest way to implement it.) - - -Importcpp for procs -~~~~~~~~~~~~~~~~~~~ - -Note that the ``importcpp`` variant for procs uses a somewhat cryptic pattern -language for maximum flexibility: - -- A hash ``#`` symbol is replaced by the first or next argument. -- A dot following the hash ``#.`` indicates that the call should use C++'s dot - or arrow notation. -- An at symbol ``@`` is replaced by the remaining arguments, separated by - commas. - -For example: - -.. code-block:: nim - proc cppMethod(this: CppObj, a, b, c: cint) {.importcpp: "#.CppMethod(@)".} - var x: ptr CppObj - cppMethod(x[], 1, 2, 3) - -Produces: - -.. code-block:: C - x->CppMethod(1, 2, 3) - -As a special rule to keep backwards compatibility with older versions of the -``importcpp`` pragma, if there is no special pattern -character (any of ``# ' @``) at all, C++'s -dot or arrow notation is assumed, so the above example can also be written as: - -.. code-block:: nim - proc cppMethod(this: CppObj, a, b, c: cint) {.importcpp: "CppMethod".} - -Note that the pattern language naturally also covers C++'s operator overloading -capabilities: - -.. code-block:: nim - proc vectorAddition(a, b: Vec3): Vec3 {.importcpp: "# + #".} - proc dictLookup(a: Dict, k: Key): Value {.importcpp: "#[#]".} - - -- An apostrophe ``'`` followed by an integer ``i`` in the range 0..9 - is replaced by the i'th parameter *type*. The 0th position is the result - type. This can be used to pass types to C++ function templates. Between - the ``'`` and the digit an asterisk can be used to get to the base type - of the type. (So it "takes away a star" from the type; ``T*`` becomes ``T``.) - Two stars can be used to get to the element type of the element type etc. - -For example: - -.. code-block:: nim - - type Input {.importcpp: "System::Input".} = object - proc getSubsystem*[T](): ptr T {.importcpp: "SystemManager::getSubsystem<'*0>()", nodecl.} - - let x: ptr Input = getSubsystem[Input]() - -Produces: - -.. code-block:: C - x = SystemManager::getSubsystem() - - -- ``#@`` is a special case to support a ``cnew`` operation. It is required so - that the call expression is inlined directly, without going through a - temporary location. This is only required to circumvent a limitation of the - current code generator. - -For example C++'s ``new`` operator can be "imported" like this: - -.. code-block:: nim - proc cnew*[T](x: T): ptr T {.importcpp: "(new '*0#@)", nodecl.} - - # constructor of 'Foo': - proc constructFoo(a, b: cint): Foo {.importcpp: "Foo(@)".} - - let x = cnew constructFoo(3, 4) - -Produces: - -.. code-block:: C - x = new Foo(3, 4) - -However, depending on the use case ``new Foo`` can also be wrapped like this -instead: - -.. code-block:: nim - proc newFoo(a, b: cint): ptr Foo {.importcpp: "new Foo(@)".} - - let x = newFoo(3, 4) - - -Wrapping constructors -~~~~~~~~~~~~~~~~~~~~~ - -Sometimes a C++ class has a private copy constructor and so code like -``Class c = Class(1,2);`` must not be generated but instead ``Class c(1,2);``. -For this purpose the Nim proc that wraps a C++ constructor needs to be -annotated with the `constructor`:idx: pragma. This pragma also helps to generate -faster C++ code since construction then doesn't invoke the copy constructor: - -.. code-block:: nim - # a better constructor of 'Foo': - proc constructFoo(a, b: cint): Foo {.importcpp: "Foo(@)", constructor.} - - -Wrapping destructors -~~~~~~~~~~~~~~~~~~~~ - -Since Nim generates C++ directly, any destructor is called implicitly by the -C++ compiler at the scope exits. This means that often one can get away with -not wrapping the destructor at all! However when it needs to be invoked -explicitly, it needs to be wrapped. But the pattern language already provides -everything that is required for that: - -.. code-block:: nim - proc destroyFoo(this: var Foo) {.importcpp: "#.~Foo()".} - - -Importcpp for objects -~~~~~~~~~~~~~~~~~~~~~ - -Generic ``importcpp``'ed objects are mapped to C++ templates. This means that -you can import C++'s templates rather easily without the need for a pattern -language for object types: - -.. code-block:: nim - type - StdMap {.importcpp: "std::map", header: "".} [K, V] = object - proc `[]=`[K, V](this: var StdMap[K, V]; key: K; val: V) {. - importcpp: "#[#] = #", header: "".} - - var x: StdMap[cint, cdouble] - x[6] = 91.4 - - -Produces: - -.. code-block:: C - std::map x; - x[6] = 91.4; - - -- If more precise control is needed, the apostrophe ``'`` can be used in the - supplied pattern to denote the concrete type parameters of the generic type. - See the usage of the apostrophe operator in proc patterns for more details. - -.. code-block:: nim - - type - VectorIterator {.importcpp: "std::vector<'0>::iterator".} [T] = object - - var x: VectorIterator[cint] - - -Produces: - -.. code-block:: C - - std::vector::iterator x; - - -ImportObjC pragma ------------------ -Similar to the `importc pragma for C `_, 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* interfacing with libraries written in Objective C: - -.. code-block:: Nim - # horrible example of how to interface with GNUStep ... - - {.passL: "-lobjc".} - {.emit: """ - #include - @interface Greeter:Object - { - } - - - (void)greet:(long)x y:(long)dummy; - @end - - #include - @implementation Greeter - - - (void)greet:(long)x y:(long)dummy - { - printf("Hello, World!\n"); - } - @end - - #include - """.} - - type - Id {.importc: "id", header: "", final.} = distinct int - - proc newGreeter: Id {.importobjc: "Greeter new", nodecl.} - proc greet(self: Id, x, y: int) {.importobjc: "greet", nodecl.} - proc free(self: Id) {.importobjc: "free", nodecl.} - - var g = newGreeter() - g.greet(12, 34) - g.free() - -The compiler needs to be told to generate Objective C (command ``objc``) for -this to work. The conditional symbol ``objc`` is defined when the compiler -emits Objective C code. - - -CodegenDecl pragma ------------------- - -The ``codegenDecl`` pragma can be used to directly influence Nim's code -generator. It receives a format string that determines how the variable or -proc is declared in the generated code: - -.. code-block:: nim - var - a {.codegenDecl: "$# progmem $#".}: int - - proc myinterrupt() {.codegenDecl: "__interrupt $# $#$#".} = - echo "realistic interrupt handler" - - -InjectStmt pragma ------------------ - -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: - -.. code-block:: nim - {.injectStmt: gcInvariants().} - - # ... complex code here that produces crashes ... - - LineDir option -------------- The ``lineDir`` option can be turned on or off. If turned on the @@ -744,15 +296,6 @@ The *breakpoint* pragma was specially added for the sake of debugging with ENDB. See the documentation of `endb `_ for further information. -Volatile pragma ---------------- -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++). - -**Note**: This pragma will not exist for the LLVM backend. - - DynlibOverride ============== From 2f8d4c17d85357c8d7760f830f4da0e675da8fd0 Mon Sep 17 00:00:00 2001 From: Yuriy Glukhov Date: Fri, 14 Aug 2015 12:57:07 +0300 Subject: [PATCH 107/115] Fixed compilation of times module with JS. --- lib/pure/times.nim | 34 ++++++++++++++++++---------------- 1 file changed, 18 insertions(+), 16 deletions(-) diff --git a/lib/pure/times.nim b/lib/pure/times.nim index f7c3b59c59..6d45dc7f13 100644 --- a/lib/pure/times.nim +++ b/lib/pure/times.nim @@ -70,7 +70,7 @@ elif defined(windows): type Time* = distinct TimeImpl - + elif defined(JS): type @@ -548,11 +548,9 @@ elif defined(JS): ## get the milliseconds from the start of the program return int(getTime() - startMilsecs) - proc valueOf(time: Time): float {.importcpp: "getTime", tags:[]} - proc fromSeconds(since1970: float): Time = result = newDate(since1970 * 1000) - proc toSeconds(time: Time): float = result = time.valueOf() / 1000 + proc toSeconds(time: Time): float = result = time.getTime() / 1000 proc getTimezone(): int = result = newDate().getTimezoneOffset() @@ -1109,9 +1107,10 @@ proc getDayOfWeekJulian*(day, month, year: int): WeekDay = proc timeToTimeInfo*(t: Time): TimeInfo = ## Converts a Time to TimeInfo. let - daysSinceEpoch = t.int div secondsInDay + secs = t.toSeconds().int + daysSinceEpoch = secs div secondsInDay (yearsSinceEpoch, daysRemaining) = countYearsAndDays(daysSinceEpoch) - daySeconds = t.int mod secondsInDay + daySeconds = secs mod secondsInDay y = yearsSinceEpoch + epochStartYear @@ -1137,12 +1136,14 @@ proc timeToTimeInfo*(t: Time): TimeInfo = s = daySeconds mod secondsInMin result = TimeInfo(year: y, yearday: yd, month: m, monthday: md, weekday: wd, hour: h, minute: mi, second: s) -proc timetoTimeInterval*(t: Time): TimeInterval = +proc timeToTimeInterval*(t: Time): TimeInterval = ## Converts a Time to a TimeInterval. - var - daysSinceEpoch = t.int div secondsInDay + + let + secs = t.toSeconds().int + daysSinceEpoch = secs div secondsInDay (yearsSinceEpoch, daysRemaining) = countYearsAndDays(daysSinceEpoch) - daySeconds = t.int mod secondsInDay + daySeconds = secs mod secondsInDay result.years = yearsSinceEpoch + epochStartYear @@ -1183,12 +1184,13 @@ when isMainModule: " ss t tt y yy yyy yyyy yyyyy z zz zzz ZZZ") == "27 27 Mon Monday 4 04 16 16 6 06 1 01 Jan January 29 29 P PM 5 75 975 1975 01975 0 00 00:00 UTC" - when not defined(JS) and sizeof(Time) == 8: - var t3 = getGMTime(fromSeconds(889067643645)) # Fri 7 Jun 19:20:45 BST 30143 - assert t3.format("d dd ddd dddd h hh H HH m mm M MM MMM MMMM s" & - " ss t tt y yy yyy yyyy yyyyy z zz zzz ZZZ") == - "7 07 Fri Friday 6 06 18 18 20 20 6 06 Jun June 45 45 P PM 3 43 143 0143 30143 0 00 00:00 UTC" - assert t3.format(":,[]()-/") == ":,[]()-/" + when not defined(JS): + when sizeof(Time) == 8: + var t3 = getGMTime(fromSeconds(889067643645)) # Fri 7 Jun 19:20:45 BST 30143 + assert t3.format("d dd ddd dddd h hh H HH m mm M MM MMM MMMM s" & + " ss t tt y yy yyy yyyy yyyyy z zz zzz ZZZ") == + "7 07 Fri Friday 6 06 18 18 20 20 6 06 Jun June 45 45 P PM 3 43 143 0143 30143 0 00 00:00 UTC" + assert t3.format(":,[]()-/") == ":,[]()-/" var t4 = getGMTime(fromSeconds(876124714)) # Mon 6 Oct 08:58:34 BST 1997 assert t4.format("M MM MMM MMMM") == "10 10 Oct October" From 22789a827571009b836fa20801961bedeea9f7ae Mon Sep 17 00:00:00 2001 From: Reimer Behrends Date: Fri, 14 Aug 2015 14:32:30 +0200 Subject: [PATCH 108/115] Fix exponentiation operation to avoid overflow. The exponentation implementation unnecessarily multiplied the result with itself at the end if the exponent was an even number. This led to overflow if result*result > high(int). --- lib/pure/math.nim | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/lib/pure/math.nim b/lib/pure/math.nim index 821ab738bc..8d95ea9c0a 100644 --- a/lib/pure/math.nim +++ b/lib/pure/math.nim @@ -427,10 +427,12 @@ proc `^`*[T](x, y: T): T = var (x, y) = (x, y) result = 1 - while y != 0: + while true: if (y and 1) != 0: result *= x y = y shr 1 + if y == 0: + break x *= x proc gcd*[T](x, y: T): T = From ac756935c1f5cb422884eaa54bb320bfc122c37f Mon Sep 17 00:00:00 2001 From: Ryan Gonzalez Date: Sat, 15 Aug 2015 12:43:05 -0500 Subject: [PATCH 109/115] Mention Howl's Nim support --- web/question.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/web/question.txt b/web/question.txt index c4d9979224..46ea7161c1 100644 --- a/web/question.txt +++ b/web/question.txt @@ -113,6 +113,7 @@ General FAQ - TextMate: Available in bundle installer (`Repository `_) - Sublime Text: Available via Package Control (`Repository `_) - LiClipse: http://www.liclipse.com/ (Eclipse based plugin) + - Howl: Included .. container:: standout From d66cc007502d62133603189f73fb53bc0a81ef5c Mon Sep 17 00:00:00 2001 From: Ryan Gonzalez Date: Sat, 15 Aug 2015 17:13:18 -0500 Subject: [PATCH 110/115] Give a nice error when no command is given --- compiler/msgs.nim | 3 ++- compiler/nim.nim | 2 ++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/compiler/msgs.nim b/compiler/msgs.nim index bb247ea54d..9bff6c123b 100644 --- a/compiler/msgs.nim +++ b/compiler/msgs.nim @@ -82,7 +82,7 @@ type errArrayExpectsTwoTypeParams, errInvalidVisibilityX, errInitHereNotAllowed, errXCannotBeAssignedTo, errIteratorNotAllowed, errXNeedsReturnType, errNoReturnTypeDeclared, - errInvalidCommandX, errXOnlyAtModuleScope, + errNoCommand, errInvalidCommandX, errXOnlyAtModuleScope, errXNeedsParamObjectType, errTemplateInstantiationTooNested, errInstantiationFrom, errInvalidIndexValueForTuple, errCommandExpectsFilename, @@ -316,6 +316,7 @@ const errIteratorNotAllowed: "iterators can only be defined at the module\'s top level", errXNeedsReturnType: "$1 needs a return type", errNoReturnTypeDeclared: "no return type declared", + errNoCommand: "no command given", errInvalidCommandX: "invalid command: \'$1\'", errXOnlyAtModuleScope: "\'$1\' is only allowed at top level", errXNeedsParamObjectType: "'$1' needs a parameter that has an object type", diff --git a/compiler/nim.nim b/compiler/nim.nim index 5da87dfa30..ce9028d537 100644 --- a/compiler/nim.nim +++ b/compiler/nim.nim @@ -58,6 +58,8 @@ proc handleCmdLine() = # command line can overwite the config file's settings extccomp.initVars() processCmdLine(passCmd2, "") + if options.command == "": + rawMessage(errNoCommand, command) mainCommand() if optHints in gOptions and hintGCStats in gNotes: echo(GC_getStatistics()) #echo(GC_getStatistics()) From 2552a0f4b0f80bf3fcfea1edc37d30f921450650 Mon Sep 17 00:00:00 2001 From: Araq Date: Sun, 16 Aug 2015 11:01:20 +0200 Subject: [PATCH 111/115] fixed test instruction --- contributing.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contributing.rst b/contributing.rst index 65b0d0975e..31f04a5e07 100644 --- a/contributing.rst +++ b/contributing.rst @@ -112,7 +112,7 @@ tell you if any new tests passed/failed. :: - ./koch --print html $DEVEL_COMMIT + ./koch tests --print html $DEVEL_COMMIT Deprecation From cd2fbb5f86809c36e4ef1e72259446f0aefaf2b7 Mon Sep 17 00:00:00 2001 From: Araq Date: Sun, 16 Aug 2015 11:15:04 +0200 Subject: [PATCH 112/115] staticExec: redirect stderr to stdout so stderr is returned too --- compiler/vmdeps.nim | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/compiler/vmdeps.nim b/compiler/vmdeps.nim index 73016108d7..2cc4a107b4 100644 --- a/compiler/vmdeps.nim +++ b/compiler/vmdeps.nim @@ -30,7 +30,7 @@ proc opGorge*(cmd, input, cache: string): string = return var readSuccessful = false try: - var p = startProcess(cmd, options={poEvalCommand}) + var p = startProcess(cmd, options={poEvalCommand, poStderrToStdout}) if input.len != 0: p.inputStream.write(input) p.inputStream.close() @@ -41,7 +41,7 @@ proc opGorge*(cmd, input, cache: string): string = if not readSuccessful: result = "" else: try: - var p = startProcess(cmd, options={poEvalCommand}) + var p = startProcess(cmd, options={poEvalCommand, poStderrToStdout}) if input.len != 0: p.inputStream.write(input) p.inputStream.close() From cc934a1a1bdbe896d1d9d2f2f1a6acccb249ed33 Mon Sep 17 00:00:00 2001 From: Araq Date: Sun, 16 Aug 2015 11:25:30 +0200 Subject: [PATCH 113/115] VM: repl supports global variables properly" --- compiler/vmgen.nim | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/compiler/vmgen.nim b/compiler/vmgen.nim index 858ec203e7..919c38e089 100644 --- a/compiler/vmgen.nim +++ b/compiler/vmgen.nim @@ -1183,7 +1183,7 @@ proc checkCanEval(c: PCtx; n: PNode) = let s = n.sym if {sfCompileTime, sfGlobal} <= s.flags: return if s.kind in {skVar, skTemp, skLet, skParam, skResult} and - not s.isOwnedBy(c.prc.sym) and s.owner != c.module: + not s.isOwnedBy(c.prc.sym) and s.owner != c.module and c.mode != emRepl: cannotEval(n) elif s.kind in {skProc, skConverter, skMethod, skIterator, skClosureIterator} and sfForward in s.flags: @@ -1850,13 +1850,13 @@ proc genProc(c: PCtx; s: PSym): int = c.prc = p # iterate over the parameters and allocate space for them: genParams(c, s.typ.n) - + # allocate additional space for any generically bound parameters if s.kind == skMacro and sfImmediate notin s.flags and s.ast[genericParamsPos].kind != nkEmpty: genGenericParams(c, s.ast[genericParamsPos]) - + if tfCapturesEnv in s.typ.flags: #let env = s.ast.sons[paramsPos].lastSon.sym #assert env.position == 2 From d81578cb9b4776356d610e2329ffc7f3323d13db Mon Sep 17 00:00:00 2001 From: Araq Date: Sun, 16 Aug 2015 13:33:05 +0200 Subject: [PATCH 114/115] workaround windows 'findNextFile' bug --- lib/pure/os.nim | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/lib/pure/os.nim b/lib/pure/os.nim index 48d255dca2..c2c28c2b14 100644 --- a/lib/pure/os.nim +++ b/lib/pure/os.nim @@ -1244,7 +1244,14 @@ iterator walkFiles*(pattern: string): string {.tags: [ReadDirEffect].} = while true: if not skipFindData(f) and (f.dwFileAttributes and FILE_ATTRIBUTE_DIRECTORY) == 0'i32: - yield splitFile(pattern).dir / extractFilename(getFilename(f)) + # Windows bug/gotcha: 't*.nim' matches 'tfoo.nims' -.- so we check + # that the file extensions have the same length ... + let ff = getFilename(f) + let dotPos = searchExtPos(pattern) + let idx = ff.len - pattern.len + dotPos + if dotPos < 0 or idx >= ff.len or ff[idx] == '.' or + pattern[dotPos+1] == '*': + yield splitFile(pattern).dir / extractFilename(ff) if findNextFile(res, f) == 0'i32: break findClose(res) else: # here we use glob From 69b32637b1f12000b64fa4db452323dc30b3567f Mon Sep 17 00:00:00 2001 From: Araq Date: Sun, 16 Aug 2015 13:40:52 +0200 Subject: [PATCH 115/115] implements experimental new config system based on NimScript --- compiler/commands.nim | 2 +- compiler/main.nim | 3 +- compiler/modules.nim | 20 +++-- compiler/nim.nim | 9 ++- compiler/options.nim | 6 +- compiler/scriptconfig.nim | 119 +++++++++++++++++++++++++++++ compiler/vm.nim | 4 +- compiler/vmhooks.nim | 12 ++- lib/system.nim | 108 +++++++++++++++------------ lib/system/nimscript.nim | 152 ++++++++++++++++++++++++++++++++++++++ lib/system/sysio.nim | 11 ++- tests/newconfig/tfoo.nim | 7 ++ tests/newconfig/tfoo.nims | 14 ++++ 13 files changed, 398 insertions(+), 69 deletions(-) create mode 100644 compiler/scriptconfig.nim create mode 100644 lib/system/nimscript.nim create mode 100644 tests/newconfig/tfoo.nim create mode 100644 tests/newconfig/tfoo.nims diff --git a/compiler/commands.nim b/compiler/commands.nim index 7a908b2703..fc28577aac 100644 --- a/compiler/commands.nim +++ b/compiler/commands.nim @@ -641,7 +641,7 @@ proc processSwitch*(pass: TCmdLinePass; p: OptParser) = proc processArgument*(pass: TCmdLinePass; p: OptParser; argsCount: var int): bool = if argsCount == 0: - options.command = p.key + if pass != passCmd2: options.command = p.key else: if pass == passCmd1: options.commandArgs.add p.key if argsCount == 1: diff --git a/compiler/main.nim b/compiler/main.nim index 47fae7fa74..014605cc92 100644 --- a/compiler/main.nim +++ b/compiler/main.nim @@ -116,7 +116,7 @@ proc interactivePasses = #incl(gGlobalOptions, optSafeCode) #setTarget(osNimrodVM, cpuNimrodVM) initDefines() - defineSymbol("nimrodvm") + defineSymbol("nimscript") when hasFFI: defineSymbol("nimffi") registerPass(verbosePass) registerPass(semPass) @@ -356,6 +356,7 @@ proc mainCommand* = gGlobalOptions.incl(optCaasEnabled) msgs.gErrorMax = high(int) # do not stop after first error serve(mainCommand) + of "nop": discard else: rawMessage(errInvalidCommandX, command) diff --git a/compiler/modules.nim b/compiler/modules.nim index 2d0267c935..ad68e63154 100644 --- a/compiler/modules.nim +++ b/compiler/modules.nim @@ -85,6 +85,15 @@ proc resetAllModules* = resetPackageCache() # for m in cgenModules(): echo "CGEN MODULE FOUND" +proc resetAllModulesHard* = + resetPackageCache() + gCompiledModules.setLen 0 + gMemCacheData.setLen 0 + magicsys.resetSysTypes() + # XXX + #gOwners = @[] + #rangeDestructorProc = nil + proc checkDepMem(fileIdx: int32): TNeedRecompile = template markDirty = resetModule(fileIdx) @@ -205,9 +214,8 @@ proc compileProject*(projectFileIdx = -1'i32) = compileSystemModule() discard compileModule(projectFile, {sfMainModule}) -var stdinModule: PSym -proc makeStdinModule*(): PSym = - if stdinModule == nil: - stdinModule = newModule(fileInfoIdx"stdin") - stdinModule.id = getID() - result = stdinModule +proc makeModule*(filename: string): PSym = + result = newModule(fileInfoIdx filename) + result.id = getID() + +proc makeStdinModule*(): PSym = makeModule"stdin" diff --git a/compiler/nim.nim b/compiler/nim.nim index ce9028d537..51f4cae92f 100644 --- a/compiler/nim.nim +++ b/compiler/nim.nim @@ -1,7 +1,7 @@ # # # The Nim Compiler -# (c) Copyright 2013 Andreas Rumpf +# (c) Copyright 2015 Andreas Rumpf # # See the file "copying.txt", included in this # distribution, for details about the copyright. @@ -16,7 +16,7 @@ when defined(gcc) and defined(windows): import commands, lexer, condsyms, options, msgs, nversion, nimconf, ropes, extccomp, strutils, os, osproc, platform, main, parseopt, service, - nodejs + nodejs, scriptconfig when hasTinyCBackend: import tccgen @@ -54,6 +54,11 @@ proc handleCmdLine() = else: gProjectPath = getCurrentDir() loadConfigs(DefaultConfig) # load all config files + let scriptFile = gProjectFull.changeFileExt("nims") + if fileExists(scriptFile): + runNimScript(scriptFile) + # 'nim foo.nims' means to just run the NimScript file and do nothing more: + if scriptFile == gProjectFull: return # now process command line arguments again, because some options in the # command line can overwite the config file's settings extccomp.initVars() diff --git a/compiler/options.nim b/compiler/options.nim index af1e21e605..adf2017d64 100644 --- a/compiler/options.nim +++ b/compiler/options.nim @@ -210,7 +210,7 @@ proc removeTrailingDirSep*(path: string): string = else: result = path -proc getGeneratedPath: string = +proc getNimcacheDir*: string = result = if nimcacheDir.len > 0: nimcacheDir else: gProjectPath.shortenDir / genSubDir @@ -266,7 +266,7 @@ proc toGeneratedFile*(path, ext: string): string = ## converts "/home/a/mymodule.nim", "rod" to "/home/a/nimcache/mymodule.rod" var (head, tail) = splitPath(path) #if len(head) > 0: head = shortenDir(head & dirSep) - result = joinPath([getGeneratedPath(), changeFileExt(tail, ext)]) + result = joinPath([getNimcacheDir(), changeFileExt(tail, ext)]) #echo "toGeneratedFile(", path, ", ", ext, ") = ", result when noTimeMachine: @@ -294,7 +294,7 @@ when noTimeMachine: proc completeGeneratedFilePath*(f: string, createSubDir: bool = true): string = var (head, tail) = splitPath(f) #if len(head) > 0: head = removeTrailingDirSep(shortenDir(head & dirSep)) - var subdir = getGeneratedPath() # / head + var subdir = getNimcacheDir() # / head if createSubDir: try: createDir(subdir) diff --git a/compiler/scriptconfig.nim b/compiler/scriptconfig.nim new file mode 100644 index 0000000000..1e4fc25afd --- /dev/null +++ b/compiler/scriptconfig.nim @@ -0,0 +1,119 @@ +# +# +# The Nim Compiler +# (c) Copyright 2015 Andreas Rumpf +# +# See the file "copying.txt", included in this +# distribution, for details about the copyright. +# + +## Implements the new configuration system for Nim. Uses Nim as a scripting +## language. + +import + ast, modules, passes, passaux, condsyms, + options, nimconf, lists, sem, semdata, llstream, vm, vmdef, commands, msgs, + os, times + +# we support 'cmpIgnoreStyle' natively for efficiency: +from strutils import cmpIgnoreStyle + +proc listDirs(a: VmArgs, filter: set[PathComponent]) = + let dir = getString(a, 0) + var result: seq[string] = @[] + for kind, path in walkDir(dir): + if kind in filter: result.add path + setResult(a, result) + +proc setupVM(module: PSym; scriptName: string): PEvalContext = + result = newCtx(module) + result.mode = emRepl + registerAdditionalOps(result) + + # captured vars: + var errorMsg: string + var vthisDir = scriptName.splitFile.dir + + template cbconf(name, body) {.dirty.} = + result.registerCallback "stdlib.system." & astToStr(name), + proc (a: VmArgs) = + body + + template cbos(name, body) {.dirty.} = + result.registerCallback "stdlib.system." & astToStr(name), + proc (a: VmArgs) = + try: + body + except OSError: + errorMsg = getCurrentExceptionMsg() + + # Idea: Treat link to file as a file, but ignore link to directory to prevent + # endless recursions out of the box. + cbos listFiles: + listDirs(a, {pcFile, pcLinkToFile}) + cbos listDirs: + listDirs(a, {pcDir}) + cbos removeDir: + os.removeDir getString(a, 0) + cbos removeFile: + os.removeFile getString(a, 0) + cbos createDir: + os.createDir getString(a, 0) + cbos getOsError: + setResult(a, errorMsg) + cbos setCurrentDir: + os.setCurrentDir getString(a, 0) + cbos getCurrentDir: + setResult(a, os.getCurrentDir()) + cbos moveFile: + os.moveFile(getString(a, 0), getString(a, 1)) + cbos getLastModificationTime: + setResult(a, toSeconds(getLastModificationTime(getString(a, 0)))) + + cbconf thisDir: + setResult(a, vthisDir) + cbconf put: + options.setConfigVar(getString(a, 0), getString(a, 1)) + cbconf get: + setResult(a, options.getConfigVar(a.getString 0)) + cbconf exists: + setResult(a, options.existsConfigVar(a.getString 0)) + cbconf nimcacheDir: + setResult(a, options.getNimcacheDir()) + cbconf paramStr: + setResult(a, os.paramStr(int a.getInt 0)) + cbconf paramCount: + setResult(a, os.paramCount()) + cbconf cmpIgnoreStyle: + setResult(a, strutils.cmpIgnoreStyle(a.getString 0, a.getString 1)) + cbconf setCommand: + options.command = a.getString 0 + cbconf getCommand: + setResult(a, options.command) + cbconf switch: + processSwitch(a.getString 0, a.getString 1, passPP, unknownLineInfo()) + + +proc runNimScript*(scriptName: string) = + passes.gIncludeFile = includeModule + passes.gImportModule = importModule + initDefines() + + defineSymbol("nimscript") + defineSymbol("nimconfig") + registerPass(semPass) + registerPass(evalPass) + + appendStr(searchPaths, options.libpath) + + var m = makeModule(scriptName) + incl(m.flags, sfMainModule) + vm.globalCtx = setupVM(m, scriptName) + + compileSystemModule() + processModule(m, llStreamOpen(scriptName, fmRead), nil) + + # ensure we load 'system.nim' again for the real non-config stuff! + resetAllModulesHard() + vm.globalCtx = nil + initDefines() diff --git a/compiler/vm.nim b/compiler/vm.nim index b1d71d73b4..57ed8397ca 100644 --- a/compiler/vm.nim +++ b/compiler/vm.nim @@ -1407,7 +1407,7 @@ include vmops # storing&loading the 'globals' environment to get what a component system # requires. var - globalCtx: PCtx + globalCtx*: PCtx proc setupGlobalCtx(module: PSym) = if globalCtx.isNil: @@ -1516,7 +1516,7 @@ proc evalMacroCall*(module: PSym, n, nOrig: PNode, sym: PSym): PNode = # return value: tos.slots[0].kind = rkNode tos.slots[0].node = newNodeIT(nkEmpty, n.info, sym.typ.sons[0]) - + # setup parameters: for i in 1.. y: result = 1 + else: result = 0 const pccHack = if defined(pcc): "_" else: "" # Hack for PCC - when not defined(NimrodVM): + when not defined(nimscript): when defined(windows): # work-around C's sucking abstraction: # BUGFIX: stdin and stdout should be binary files! @@ -2536,14 +2542,15 @@ when not defined(JS): #and not defined(NimrodVM): {.deprecated: [TFile: File, TFileHandle: FileHandle, TFileMode: FileMode].} - # text file handling: - var - stdin* {.importc: "stdin", header: "".}: File - ## The standard input stream. - stdout* {.importc: "stdout", header: "".}: File - ## The standard output stream. - stderr* {.importc: "stderr", header: "".}: File - ## The standard error stream. + when not defined(nimscript): + # text file handling: + var + stdin* {.importc: "stdin", header: "".}: File + ## The standard input stream. + stdout* {.importc: "stdout", header: "".}: File + ## The standard output stream. + stderr* {.importc: "stderr", header: "".}: File + ## The standard error stream. when defined(useStdoutAsStdmsg): template stdmsg*: File = stdout @@ -2738,7 +2745,7 @@ when not defined(JS): #and not defined(NimrodVM): inc(i) dealloc(a) - when not defined(NimrodVM): + when not defined(nimscript): proc atomicInc*(memLoc: var int, x: int = 1): int {.inline, discardable, benign.} ## atomic increment of `memLoc`. Returns the value after the operation. @@ -2749,27 +2756,27 @@ when not defined(JS): #and not defined(NimrodVM): include "system/atomics" - type - PSafePoint = ptr TSafePoint - TSafePoint {.compilerproc, final.} = object - prev: PSafePoint # points to next safe point ON THE STACK - status: int - context: C_JmpBuf - hasRaiseAction: bool - raiseAction: proc (e: ref Exception): bool {.closure.} - SafePoint = TSafePoint -# {.deprecated: [TSafePoint: SafePoint].} + type + PSafePoint = ptr TSafePoint + TSafePoint {.compilerproc, final.} = object + prev: PSafePoint # points to next safe point ON THE STACK + status: int + context: C_JmpBuf + hasRaiseAction: bool + raiseAction: proc (e: ref Exception): bool {.closure.} + SafePoint = TSafePoint + # {.deprecated: [TSafePoint: SafePoint].} when declared(initAllocator): initAllocator() when hasThreadSupport: include "system/syslocks" when hostOS != "standalone": include "system/threads" - elif not defined(nogc) and not defined(NimrodVM): + elif not defined(nogc) and not defined(nimscript): when not defined(useNimRtl) and not defined(createNimRtl): initStackBottom() when declared(initGC): initGC() - when not defined(NimrodVM): + when not defined(nimscript): proc setControlCHook*(hook: proc () {.noconv.} not nil) ## allows you to override the behaviour of your application when CTRL+C ## is pressed. Only one such hook is supported. @@ -2798,9 +2805,9 @@ when not defined(JS): #and not defined(NimrodVM): {.pop.} # stack trace {.pop.} # stack trace - when hostOS != "standalone" and not defined(NimrodVM): + when hostOS != "standalone" and not defined(nimscript): include "system/dyncalls" - when not defined(NimrodVM): + when not defined(nimscript): include "system/sets" when defined(gogc): @@ -2875,11 +2882,11 @@ when not defined(JS): #and not defined(NimrodVM): var res = TaintedString(newStringOfCap(80)) while f.readLine(res): yield res - when not defined(NimrodVM) and hasAlloc: + when not defined(nimscript) and hasAlloc: include "system/assign" include "system/repr" - when hostOS != "standalone" and not defined(NimrodVM): + when hostOS != "standalone" and not defined(nimscript): proc getCurrentException*(): ref Exception {.compilerRtl, inl, benign.} = ## retrieves the current exception; if there is none, nil is returned. result = currException @@ -2907,14 +2914,14 @@ when not defined(JS): #and not defined(NimrodVM): currException = exc {.push stack_trace: off, profiler:off.} - when defined(endb) and not defined(NimrodVM): + when defined(endb) and not defined(nimscript): include "system/debugger" when defined(profiler) or defined(memProfiler): include "system/profiler" {.pop.} # stacktrace - when not defined(NimrodVM): + when not defined(nimscript): proc likely*(val: bool): bool {.importc: "likely", nodecl, nosideeffect.} ## Hints the optimizer that `val` is likely going to be true. ## @@ -2992,7 +2999,7 @@ elif defined(JS): when defined(JS): include "system/jssys" include "system/reprjs" - elif defined(NimrodVM): + elif defined(nimscript): proc cmp(x, y: string): int = if x == y: return 0 if x < y: return -1 @@ -3305,7 +3312,7 @@ proc shallow*[T](s: var seq[T]) {.noSideEffect, inline.} = ## marks a sequence `s` as `shallow`:idx:. Subsequent assignments will not ## perform deep copies of `s`. This is only useful for optimization ## purposes. - when not defined(JS) and not defined(NimrodVM): + when not defined(JS) and not defined(nimscript): var s = cast[PGenericSeq](s) s.reserved = s.reserved or seqShallowFlag @@ -3313,7 +3320,7 @@ proc shallow*(s: var string) {.noSideEffect, inline.} = ## marks a string `s` as `shallow`:idx:. Subsequent assignments will not ## perform deep copies of `s`. This is only useful for optimization ## purposes. - when not defined(JS) and not defined(NimrodVM): + when not defined(JS) and not defined(nimscript): var s = cast[PGenericSeq](s) s.reserved = s.reserved or seqShallowFlag @@ -3402,7 +3409,7 @@ proc locals*(): RootObj {.magic: "Plugin", noSideEffect.} = ## # -> B is 1 discard -when hasAlloc and not defined(NimrodVM) and not defined(JS): +when hasAlloc and not defined(nimscript) and not defined(JS): proc deepCopy*[T](x: var T, y: T) {.noSideEffect, magic: "DeepCopy".} = ## performs a deep copy of `x`. This is also used by the code generator ## for the implementation of ``spawn``. @@ -3444,3 +3451,6 @@ proc xlen*[T](x: seq[T]): int {.magic: "XLenSeq", noSideEffect.} = discard {.pop.} #{.push warning[GcMem]: off, warning[Uninit]: off.} + +when defined(nimconfig): + include "system/nimscript" diff --git a/lib/system/nimscript.nim b/lib/system/nimscript.nim new file mode 100644 index 0000000000..6f5977869d --- /dev/null +++ b/lib/system/nimscript.nim @@ -0,0 +1,152 @@ +# +# +# Nim's Runtime Library +# (c) Copyright 2015 Andreas Rumpf +# +# See the file "copying.txt", included in this +# distribution, for details about the copyright. +# + + +# Nim's configuration system now uses Nim for scripting. This module provides +# a few things that are required for this to work. + +template builtin = discard + +proc listDirs*(dir: string): seq[string] = builtin +proc listFiles*(dir: string): seq[string] = builtin + +proc removeDir(dir: string) = builtin +proc removeFile(dir: string) = builtin +proc moveFile(src, dest: string) = builtin +proc createDir(dir: string) = builtin +proc getOsError: string = builtin +proc setCurrentDir(dir: string) = builtin +proc getCurrentDir(): string = builtin +proc paramStr*(i: int): string = builtin +proc paramCount*(): int = builtin + +proc switch*(key: string, val="") = builtin +proc getCommand*(): string = builtin +proc setCommand*(cmd: string) = builtin +proc cmpIgnoreStyle(a, b: string): int = builtin + +proc strip(s: string): string = + var i = 0 + while s[i] in {' ', '\c', '\L'}: inc i + result = s.substr(i) + +template `--`*(key, val: untyped) = switch(astToStr(key), strip astToStr(val)) +template `--`*(key: untyped) = switch(astToStr(key), "") + +type + ScriptMode* {.pure.} = enum + Silent, + Verbose, + Whatif + +var + mode*: ScriptMode ## Set this to influence how mkDir, rmDir, rmFile etc. + ## behave + +template checkOsError = + let err = getOsError() + if err.len > 0: raise newException(OSError, err) + +template log(msg: string, body: untyped) = + if mode == ScriptMode.Verbose or mode == ScriptMode.Whatif: + echo "[NimScript] ", msg + if mode != ScriptMode.WhatIf: + body + +proc rmDir*(dir: string) {.raises: [OSError].} = + log "rmDir: " & dir: + removeDir dir + checkOsError() + +proc rmFile*(dir: string) {.raises: [OSError].} = + log "rmFile: " & dir: + removeFile dir + checkOsError() + +proc mkDir*(dir: string) {.raises: [OSError].} = + log "mkDir: " & dir: + createDir dir + checkOsError() + +proc mvFile*(`from`, to: string) {.raises: [OSError].} = + log "mvFile: " & `from` & ", " & to: + moveFile `from`, to + checkOsError() + +proc exec*(command: string, input = "", cache = "") = + ## Executes an external process. + log "exec: " & command: + echo staticExec(command, input, cache) + +proc put*(key, value: string) = + ## Sets a configuration 'key' like 'gcc.options.always' to its value. + builtin + +proc get*(key: string): string = + ## Retrieves a configuration 'key' like 'gcc.options.always'. + builtin + +proc exists*(key: string): bool = + ## Checks for the existance of a configuration 'key' + ## like 'gcc.options.always'. + builtin + +proc nimcacheDir*(): string = + ## Retrieves the location of 'nimcache'. + builtin + +proc thisDir*(): string = + ## Retrieves the location of the current ``nims`` script file. + builtin + +proc cd*(dir: string) {.raises: [OSError].} = + ## Changes the current directory. + ## + ## The change is permanent for the rest of the execution, since this is just + ## a shortcut for `os.setCurrentDir() + ## `_ . Use the `withDir() + ## <#withDir>`_ template if you want to perform a temporary change only. + setCurrentDir(dir) + checkOsError() + +template withDir*(dir: string; body: untyped): untyped = + ## Changes the current directory temporarily. + ## + ## If you need a permanent change, use the `cd() <#cd>`_ proc. Usage example: + ## + ## .. code-block:: nimrod + ## withDir "foo": + ## # inside foo + ## #back to last dir + var curDir = getCurrentDir() + try: + cd(dir) + body + finally: + cd(curDir) + +template `==?`(a, b: string): bool = cmpIgnoreStyle(a, b) == 0 + +proc writeTask(name, desc: string) = + if desc.len > 0: + var spaces = " " + for i in 0 ..< 20 - name.len: spaces.add ' ' + echo name, spaces, desc + +template task*(name: untyped; description: string; body: untyped): untyped = + ## Defines a task. Hidden tasks are supported via an empty description. + proc `name Task`() = body + + let cmd = getCommand() + if cmd.len == 0 or cmd ==? "help" or cmd == "nop": + setCommand "nop" + writeTask(astToStr(name), description) + elif cmd ==? astToStr(name): + setCommand "nop" + `name Task`() diff --git a/lib/system/sysio.nim b/lib/system/sysio.nim index 5464ee126e..b3316920e2 100644 --- a/lib/system/sysio.nim +++ b/lib/system/sysio.nim @@ -182,7 +182,10 @@ proc readAllFile(file: File): string = proc readAll(file: File): TaintedString = # Separate handling needed because we need to buffer when we # don't know the overall length of the File. - let len = if file != stdin: rawFileSize(file) else: -1 + when declared(stdin): + let len = if file != stdin: rawFileSize(file) else: -1 + else: + let len = rawFileSize(file) if len > 0: result = readAllFile(file, len).TaintedString else: @@ -216,9 +219,9 @@ proc writeLine[Ty](f: File, x: varargs[Ty, `$`]) = for i in items(x): write(f, i) write(f, "\n") - -proc rawEcho(x: string) {.inline, compilerproc.} = write(stdout, x) -proc rawEchoNL() {.inline, compilerproc.} = write(stdout, "\n") +when declared(stdout): + proc rawEcho(x: string) {.inline, compilerproc.} = write(stdout, x) + proc rawEchoNL() {.inline, compilerproc.} = write(stdout, "\n") # interface to the C procs: diff --git a/tests/newconfig/tfoo.nim b/tests/newconfig/tfoo.nim new file mode 100644 index 0000000000..0ace7c88a7 --- /dev/null +++ b/tests/newconfig/tfoo.nim @@ -0,0 +1,7 @@ +discard """ + cmd: "nim default $file" + output: '''hello world!''' + msg: '''[NimScript] exec: gcc -v''' +""" + +echo "hello world!" diff --git a/tests/newconfig/tfoo.nims b/tests/newconfig/tfoo.nims new file mode 100644 index 0000000000..90205cddba --- /dev/null +++ b/tests/newconfig/tfoo.nims @@ -0,0 +1,14 @@ + +mode = ScriptMode.Whatif + +exec "gcc -v" + +--forceBuild + +task listDirs, "lists every subdirectory": + for x in listDirs("."): + echo "DIR ", x + +task default, "default target": + setCommand "c" +