diff options
-rw-r--r-- | build_all.sh | 4 | ||||
-rw-r--r-- | changelog.md | 2 | ||||
-rw-r--r-- | compiler/ccgexprs.nim | 10 | ||||
-rw-r--r-- | compiler/ccgstmts.nim | 31 | ||||
-rw-r--r-- | compiler/packagehandling.nim | 4 | ||||
-rw-r--r-- | compiler/types.nim | 17 | ||||
-rw-r--r-- | lib/pure/collections/tables.nim | 11 | ||||
-rw-r--r-- | lib/pure/net.nim | 12 | ||||
-rw-r--r-- | lib/pure/times.nim | 148 | ||||
-rw-r--r-- | testament/categories.nim | 21 | ||||
-rw-r--r-- | testament/specs.nim | 7 | ||||
-rw-r--r-- | testament/tester.nim | 57 | ||||
-rw-r--r-- | tests/errmsgs/tuncheckedarrayvar.nim | 9 | ||||
-rw-r--r-- | tests/macros/tmacros_issues.nim | 4 | ||||
-rw-r--r-- | tests/stdlib/ttimes.nim | 27 |
15 files changed, 202 insertions, 162 deletions
diff --git a/build_all.sh b/build_all.sh index 333814d62..e22facc1b 100644 --- a/build_all.sh +++ b/build_all.sh @@ -17,14 +17,14 @@ build_nim_csources(){ ## avoid changing dir in case of failure ( echo_run cd csources - echo_run sh build.sh + echo_run sh build.sh $@ ) # keep $nim_csources in case needed to investigate bootstrap issues # without having to rebuild from csources echo_run cp bin/nim $nim_csources } -[ -f $nim_csources ] || echo_run build_nim_csources +[ -f $nim_csources ] || echo_run build_nim_csources $@ # Note: if fails, may need to `cd csources && git pull` echo_run bin/nim c --skipUserCfg --skipParentCfg koch diff --git a/changelog.md b/changelog.md index adc35a079..91b8f5bbd 100644 --- a/changelog.md +++ b/changelog.md @@ -193,6 +193,8 @@ proc enumToString*(enums: openArray[enum]): string = - The switch ``-d:useWinAnsi`` is not supported anymore. +- In `times` module, procs `format` and `parse` accept a new optional `DateTimeLocale` argument for formatting/parsing dates in other languages. + ### Language additions diff --git a/compiler/ccgexprs.nim b/compiler/ccgexprs.nim index 97ce5a485..ec32f5975 100644 --- a/compiler/ccgexprs.nim +++ b/compiler/ccgexprs.nim @@ -718,14 +718,10 @@ proc isCppRef(p: BProc; typ: PType): bool {.inline.} = tfVarIsPtr notin skipTypes(typ, abstractInstOwned).flags proc genDeref(p: BProc, e: PNode, d: var TLoc) = - let - enforceDeref = lfEnforceDeref in d.flags - mt = mapType(p.config, e.sons[0].typ) - if mt in {ctArray, ctPtrToArray} and not enforceDeref: + let mt = mapType(p.config, e.sons[0].typ) + if mt in {ctArray, ctPtrToArray} and lfEnforceDeref notin d.flags: # XXX the amount of hacks for C's arrays is incredible, maybe we should # simply wrap them in a struct? --> Losing auto vectorization then? - #if e[0].kind != nkBracketExpr: - # message(e.info, warnUser, "CAME HERE " & renderTree(e)) expr(p, e.sons[0], d) if e.sons[0].typ.skipTypes(abstractInstOwned).kind == tyRef: d.storage = OnHeap @@ -762,7 +758,7 @@ proc genDeref(p: BProc, e: PNode, d: var TLoc) = e.kind == nkHiddenDeref: putIntoDest(p, d, e, rdLoc(a), a.storage) return - if enforceDeref and mt == ctPtrToArray: + if mt == ctPtrToArray and lfEnforceDeref in d.flags: # we lie about the type for better C interop: 'ptr array[3,T]' is # translated to 'ptr T', but for deref'ing this produces wrong code. # See tmissingderef. So we get rid of the deref instead. The codegen diff --git a/compiler/ccgstmts.nim b/compiler/ccgstmts.nim index 3be31503a..7552f0c63 100644 --- a/compiler/ccgstmts.nim +++ b/compiler/ccgstmts.nim @@ -83,18 +83,6 @@ proc genVarTuple(p: BProc, n: PNode) = # check with the boolean if the initializing code for the tuple should be ran lineCg(p, cpsStmts, "if ($1)$n", [hcrCond]) startBlock(p) - defer: - if forHcr: - # end the block where the tuple gets initialized - endBlock(p) - if forHcr or isGlobalInBlock: - # insert the registration of the globals for the different parts of the tuple at the - # start of the current scope (after they have been iterated) and init a boolean to - # check if any of them is newly introduced and the initializing code has to be ran - lineCg(p, cpsLocals, "NIM_BOOL $1 = NIM_FALSE;$n", [hcrCond]) - for curr in hcrGlobals: - lineCg(p, cpsLocals, "$1 |= hcrRegisterGlobal($4, \"$2\", sizeof($3), $5, (void**)&$2);$N", - [hcrCond, curr.loc.r, rdLoc(curr.loc), getModuleDllPath(p.module, n.sons[0].sym), curr.tp]) genLineDir(p, n) initLocExpr(p, n.sons[L-1], tup) @@ -123,7 +111,18 @@ proc genVarTuple(p: BProc, n: PNode) = if forHcr or isGlobalInBlock: hcrGlobals.add((loc: v.loc, tp: if traverseProc == nil: ~"NULL" else: traverseProc)) -proc genDeref(p: BProc, e: PNode, d: var TLoc) + if forHcr: + # end the block where the tuple gets initialized + endBlock(p) + if forHcr or isGlobalInBlock: + # insert the registration of the globals for the different parts of the tuple at the + # start of the current scope (after they have been iterated) and init a boolean to + # check if any of them is newly introduced and the initializing code has to be ran + lineCg(p, cpsLocals, "NIM_BOOL $1 = NIM_FALSE;$n", [hcrCond]) + for curr in hcrGlobals: + lineCg(p, cpsLocals, "$1 |= hcrRegisterGlobal($4, \"$2\", sizeof($3), $5, (void**)&$2);$N", + [hcrCond, curr.loc.r, rdLoc(curr.loc), getModuleDllPath(p.module, n.sons[0].sym), curr.tp]) + proc loadInto(p: BProc, le, ri: PNode, a: var TLoc) {.inline.} = if ri.kind in nkCallKinds and (ri.sons[0].kind != nkSym or @@ -364,13 +363,11 @@ proc genSingleVar(p: BProc, a: PNode) = lineCg(targetProc, cpsStmts, "if (hcrRegisterGlobal($3, \"$1\", sizeof($2), $4, (void**)&$1))$N", [v.loc.r, rdLoc(v.loc), getModuleDllPath(p.module, v), traverseProc]) startBlock(targetProc) - defer: - if forHcr: - endBlock(targetProc) - if a.sons[2].kind != nkEmpty: genLineDir(targetProc, a) loadInto(targetProc, a.sons[0], a.sons[2], v.loc) + if forHcr: + endBlock(targetProc) proc genClosureVar(p: BProc, a: PNode) = var immediateAsgn = a.sons[2].kind != nkEmpty diff --git a/compiler/packagehandling.nim b/compiler/packagehandling.nim index 0e9331128..548668824 100644 --- a/compiler/packagehandling.nim +++ b/compiler/packagehandling.nim @@ -41,11 +41,11 @@ proc getPackageName*(conf: ConfigRef; path: string): string = proc fakePackageName*(conf: ConfigRef; path: AbsoluteFile): string = # foo/../bar becomes foo7_7bar result = relativeTo(path, conf.projectPath, '/').string.multiReplace( - {"/": "7", "..": "_", "7": "77", "_": "__"}) + {"/": "7", "..": "_", "7": "77", "_": "__", ":": "8", "8": "88"}) proc demaglePackageName*(path: string): string = result = path.multiReplace( - {"77": "7", "__": "_", "_7": "../", "7": "/"}) + {"88": "8", "8": ":", "77": "7", "__": "_", "_7": "../", "7": "/"}) proc withPackageName*(conf: ConfigRef; path: AbsoluteFile): AbsoluteFile = let x = getPackageName(conf, path.string) diff --git a/compiler/types.nim b/compiler/types.nim index b0a6a4c17..d56794d2d 100644 --- a/compiler/types.nim +++ b/compiler/types.nim @@ -1199,9 +1199,9 @@ proc typeAllowedAux(marker: var IntSet, typ: PType, kind: TSymKind, case t2.kind of tyVar, tyLent: if taHeap notin flags: result = t2 # ``var var`` is illegal on the heap - of tyOpenArray: + of tyOpenArray, tyUncheckedArray: if kind != skParam: result = t - else: result = typeAllowedAux(marker, t2, kind, flags) + else: result = typeAllowedAux(marker, t2.sons[0], skParam, flags) else: if kind notin {skParam, skResult}: result = t else: result = typeAllowedAux(marker, t2, kind, flags) @@ -1235,14 +1235,21 @@ proc typeAllowedAux(marker: var IntSet, typ: PType, kind: TSymKind, result = nil of tyOrdinal: if kind != skParam: result = t - of tyGenericInst, tyDistinct, tyAlias, tyInferred, tyUncheckedArray: + of tyGenericInst, tyDistinct, tyAlias, tyInferred: result = typeAllowedAux(marker, lastSon(t), kind, flags) of tyRange: if skipTypes(t.sons[0], abstractInst-{tyTypeDesc}).kind notin {tyChar, tyEnum, tyInt..tyFloat128, tyUInt8..tyUInt32}: result = t of tyOpenArray, tyVarargs, tySink: - if kind != skParam: result = t - else: result = typeAllowedAux(marker, t.sons[0], skVar, flags) + if kind != skParam: + result = t + else: + result = typeAllowedAux(marker, t.sons[0], skVar, flags) + of tyUncheckedArray: + if kind != skParam and taHeap notin flags: + result = t + else: + result = typeAllowedAux(marker, lastSon(t), kind, flags) of tySequence, tyOpt: if t.sons[0].kind != tyEmpty: result = typeAllowedAux(marker, t.sons[0], skVar, flags+{taHeap}) diff --git a/lib/pure/collections/tables.nim b/lib/pure/collections/tables.nim index 76ca15b62..93ccb4058 100644 --- a/lib/pure/collections/tables.nim +++ b/lib/pure/collections/tables.nim @@ -2133,6 +2133,7 @@ type ## <#initCountTable,int>`_. data: seq[tuple[key: A, val: int]] counter: int + isSorted: bool CountTableRef*[A] = ref CountTable[A] ## Ref version of ## `CountTable<#CountTable>`_. ## @@ -2209,12 +2210,14 @@ proc `[]`*[A](t: CountTable[A], key: A): int = ## (key, value) pair in the table ## * `hasKey proc<#hasKey,CountTable[A],A>`_ for checking if a key ## is in the table + assert(not t.isSorted, "CountTable must not be used after sorting") ctget(t, key, 0) proc mget*[A](t: var CountTable[A], key: A): var int = ## Retrieves the value at ``t[key]``. The value can be modified. ## ## If ``key`` is not in ``t``, the ``KeyError`` exception is raised. + assert(not t.isSorted, "CountTable must not be used after sorting") get(t, key) proc `[]=`*[A](t: var CountTable[A], key: A, val: int) = @@ -2224,6 +2227,7 @@ proc `[]=`*[A](t: var CountTable[A], key: A, val: int) = ## * `[] proc<#[],CountTable[A],A>`_ for retrieving a value of a key ## * `inc proc<#inc,CountTable[A],A,int>`_ for incrementing a ## value of a key + assert(not t.isSorted, "CountTable must not be used after sorting") assert val >= 0 let h = rawGet(t, key) if h >= 0: @@ -2239,6 +2243,7 @@ proc inc*[A](t: var CountTable[A], key: A, val = 1) = a.inc('b', 10) doAssert a == toCountTable("aaabbbbbbbbbbb") + assert(not t.isSorted, "CountTable must not be used after sorting") var index = rawGet(t, key) if index >= 0: inc(t.data[index].val, val) @@ -2280,6 +2285,7 @@ proc hasKey*[A](t: CountTable[A], key: A): bool = ## * `[] proc<#[],CountTable[A],A>`_ for retrieving a value of a key ## * `getOrDefault proc<#getOrDefault,CountTable[A],A,int>`_ to return ## a custom value if the key doesn't exist + assert(not t.isSorted, "CountTable must not be used after sorting") result = rawGet(t, key) >= 0 proc contains*[A](t: CountTable[A], key: A): bool = @@ -2304,6 +2310,7 @@ proc len*[A](t: CountTable[A]): int = proc clear*[A](t: var CountTable[A]) = ## Resets the table so that it is empty. clearImpl() + t.isSorted = false func ctCmp[T](a, b: tuple[key: T, val: int]): int = result = system.cmp(a.val, b.val) @@ -2312,7 +2319,7 @@ proc sort*[A](t: var CountTable[A], order = SortOrder.Descending) = ## Sorts the count table so that, by default, the entry with the ## highest counter comes first. ## - ## **This is destructive! You must not modify ``t`` afterwards!** + ## **WARNING:** This is destructive! Once sorted, you must not modify ``t`` afterwards! ## ## You can use the iterators `pairs<#pairs.i,CountTable[A]>`_, ## `keys<#keys.i,CountTable[A]>`_, and `values<#values.i,CountTable[A]>`_ @@ -2327,6 +2334,7 @@ proc sort*[A](t: var CountTable[A], order = SortOrder.Descending) = doAssert toSeq(a.values) == @[1, 1, 2, 2, 5] t.data.sort(cmp=ctCmp, order=order) + t.isSorted = true proc merge*[A](s: var CountTable[A], t: CountTable[A]) = ## Merges the second table into the first one (must be declared as `var`). @@ -2336,6 +2344,7 @@ proc merge*[A](s: var CountTable[A], t: CountTable[A]) = a.merge(b) doAssert a == toCountTable("aaabbbccc") + assert(not s.isSorted, "CountTable must not be used after sorting") for key, value in t: s.inc(key, value) diff --git a/lib/pure/net.nim b/lib/pure/net.nim index f55d5d900..69a221a6c 100644 --- a/lib/pure/net.nim +++ b/lib/pure/net.nim @@ -1083,7 +1083,7 @@ proc waitFor(socket: Socket, waited: var float, timeout, size: int, ## For buffered sockets it can be as big as ``BufferSize``. ## ## If this function does not determine that there is data on the socket - ## within ``timeout`` ms, an ETimeout error will be raised. + ## within ``timeout`` ms, a TimeoutError error will be raised. result = 1 if size <= 0: assert false if timeout == -1: return size @@ -1138,7 +1138,7 @@ proc recv*(socket: Socket, data: var string, size: int, timeout = -1, ## lower than 0 is never returned. ## ## A timeout may be specified in milliseconds, if enough data is not received - ## within the time specified an TimeoutError exception will be raised. + ## within the time specified a TimeoutError exception will be raised. ## ## **Note**: ``data`` must be initialised. ## @@ -1165,7 +1165,7 @@ proc recv*(socket: Socket, size: int, timeout = -1, ## This function will throw an OSError exception when an error occurs. ## ## A timeout may be specified in milliseconds, if enough data is not received - ## within the time specified an ETimeout exception will be raised. + ## within the time specified a TimeoutError exception will be raised. ## ## ## **Warning**: Only the ``SafeDisconn`` flag is currently supported. @@ -1206,7 +1206,7 @@ proc readLine*(socket: Socket, line: var TaintedString, timeout = -1, ## An OSError exception will be raised in the case of a socket error. ## ## A timeout can be specified in milliseconds, if data is not received within - ## the specified time an ETimeout exception will be raised. + ## the specified time a TimeoutError exception will be raised. ## ## The ``maxLength`` parameter determines the maximum amount of characters ## that can be read. The result is truncated after that. @@ -1261,7 +1261,7 @@ proc recvLine*(socket: Socket, timeout = -1, ## An OSError exception will be raised in the case of a socket error. ## ## A timeout can be specified in milliseconds, if data is not received within - ## the specified time an ETimeout exception will be raised. + ## the specified time a TimeoutError exception will be raised. ## ## The ``maxLength`` parameter determines the maximum amount of characters ## that can be read. The result is truncated after that. @@ -1303,7 +1303,7 @@ proc skip*(socket: Socket, size: int, timeout = -1) = ## Skips ``size`` amount of bytes. ## ## An optional timeout can be specified in milliseconds, if skipping the - ## bytes takes longer than specified an ETimeout exception will be raised. + ## bytes takes longer than specified a TimeoutError exception will be raised. ## ## Returns the number of skipped bytes. var waited = 0.0 diff --git a/lib/pure/times.nim b/lib/pure/times.nim index dc79877fd..9312b7ba8 100644 --- a/lib/pure/times.nim +++ b/lib/pure/times.nim @@ -282,6 +282,12 @@ type dFri = "Friday" dSat = "Saturday" dSun = "Sunday" + + DateTimeLocale* = object + MMM*: array[mJan..mDec, string] + MMMM*: array[mJan..mDec, string] + ddd*: array[dMon..dSun, string] + dddd*: array[dMon..dSun, string] MonthdayRange* = range[1..31] HourRange* = range[0..23] @@ -421,6 +427,13 @@ const unitWeights: array[FixedTimeUnit, int64] = [ 7 * secondsInDay * 1e9.int64, ] +const DefaultLocale* = DateTimeLocale( + MMM: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"], + MMMM: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"], + ddd: ["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"], + dddd: ["Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"], +) + proc convert*[T: SomeInteger](unitFrom, unitTo: FixedTimeUnit, quantity: T): T {.inline.} = ## Convert a quantity of some duration unit to another duration unit. @@ -1894,7 +1907,7 @@ proc initTimeFormat*(format: string): TimeFormat = of tkPattern: result.patterns.add(stringToPattern(token).byte) -proc formatPattern(dt: DateTime, pattern: FormatPattern, result: var string) = +proc formatPattern(dt: DateTime, pattern: FormatPattern, result: var string, loc: DateTimeLocale) = template yearOfEra(dt: DateTime): int = if dt.year <= 0: abs(dt.year) + 1 else: dt.year @@ -1904,9 +1917,9 @@ proc formatPattern(dt: DateTime, pattern: FormatPattern, result: var string) = of dd: result.add dt.monthday.intToStr(2) of ddd: - result.add ($dt.weekday)[0..2] + result.add loc.ddd[dt.weekday] of dddd: - result.add $dt.weekday + result.add loc.dddd[dt.weekday] of h: result.add( if dt.hour == 0: "12" @@ -1932,9 +1945,9 @@ proc formatPattern(dt: DateTime, pattern: FormatPattern, result: var string) = of MM: result.add ord(dt.month).intToStr(2) of MMM: - result.add ($dt.month)[0..2] + result.add loc.MMM[dt.month] of MMMM: - result.add $dt.month + result.add loc.MMMM[dt.month] of s: result.add $dt.second of ss: @@ -2003,7 +2016,7 @@ proc formatPattern(dt: DateTime, pattern: FormatPattern, result: var string) = of Lit: assert false # Can't happen proc parsePattern(input: string, pattern: FormatPattern, i: var int, - parsed: var ParsedTime): bool = + parsed: var ParsedTime, loc: DateTimeLocale): bool = template takeInt(allowedWidth: Slice[int], allowSign = false): int = var sv: int var pd = parseInt(input, sv, i, allowedWidth.b, allowSign) @@ -2027,27 +2040,19 @@ proc parsePattern(input: string, pattern: FormatPattern, i: var int, parsed.monthday = some(monthday) result = monthday in MonthdayRange of ddd: - result = input.substr(i, i+2).toLowerAscii() in [ - "sun", "mon", "tue", "wed", "thu", "fri", "sat"] - if result: - i.inc 3 + result = false + for v in loc.ddd: + if input.substr(i, i+v.len-1).cmpIgnoreCase(v) == 0: + result = true + i.inc v.len + break of dddd: - if input.substr(i, i+5).cmpIgnoreCase("sunday") == 0: - i.inc 6 - elif input.substr(i, i+5).cmpIgnoreCase("monday") == 0: - i.inc 6 - elif input.substr(i, i+6).cmpIgnoreCase("tuesday") == 0: - i.inc 7 - elif input.substr(i, i+8).cmpIgnoreCase("wednesday") == 0: - i.inc 9 - elif input.substr(i, i+7).cmpIgnoreCase("thursday") == 0: - i.inc 8 - elif input.substr(i, i+5).cmpIgnoreCase("friday") == 0: - i.inc 6 - elif input.substr(i, i+7).cmpIgnoreCase("saturday") == 0: - i.inc 8 - else: - result = false + result = false + for v in loc.dddd: + if input.substr(i, i+v.len-1).cmpIgnoreCase(v) == 0: + result = true + i.inc v.len + break of h, H: parsed.hour = takeInt(1..2) result = parsed.hour in HourRange @@ -2069,62 +2074,21 @@ proc parsePattern(input: string, pattern: FormatPattern, i: var int, result = month in 1..12 parsed.month = some(month) of MMM: - case input.substr(i, i+2).toLowerAscii() - of "jan": parsed.month = some(1) - of "feb": parsed.month = some(2) - of "mar": parsed.month = some(3) - of "apr": parsed.month = some(4) - of "may": parsed.month = some(5) - of "jun": parsed.month = some(6) - of "jul": parsed.month = some(7) - of "aug": parsed.month = some(8) - of "sep": parsed.month = some(9) - of "oct": parsed.month = some(10) - of "nov": parsed.month = some(11) - of "dec": parsed.month = some(12) - else: - result = false - if result: - i.inc 3 + result = false + for n,v in loc.MMM: + if input.substr(i, i+v.len-1).cmpIgnoreCase(v) == 0: + result = true + i.inc v.len + parsed.month = some(n.int) + break of MMMM: - if input.substr(i, i+6).cmpIgnoreCase("january") == 0: - parsed.month = some(1) - i.inc 7 - elif input.substr(i, i+7).cmpIgnoreCase("february") == 0: - parsed.month = some(2) - i.inc 8 - elif input.substr(i, i+4).cmpIgnoreCase("march") == 0: - parsed.month = some(3) - i.inc 5 - elif input.substr(i, i+4).cmpIgnoreCase("april") == 0: - parsed.month = some(4) - i.inc 5 - elif input.substr(i, i+2).cmpIgnoreCase("may") == 0: - parsed.month = some(5) - i.inc 3 - elif input.substr(i, i+3).cmpIgnoreCase("june") == 0: - parsed.month = some(6) - i.inc 4 - elif input.substr(i, i+3).cmpIgnoreCase("july") == 0: - parsed.month = some(7) - i.inc 4 - elif input.substr(i, i+5).cmpIgnoreCase("august") == 0: - parsed.month = some(8) - i.inc 6 - elif input.substr(i, i+8).cmpIgnoreCase("september") == 0: - parsed.month = some(9) - i.inc 9 - elif input.substr(i, i+6).cmpIgnoreCase("october") == 0: - parsed.month = some(10) - i.inc 7 - elif input.substr(i, i+7).cmpIgnoreCase("november") == 0: - parsed.month = some(11) - i.inc 8 - elif input.substr(i, i+7).cmpIgnoreCase("december") == 0: - parsed.month = some(12) - i.inc 8 - else: - result = false + result = false + for n,v in loc.MMMM: + if input.substr(i, i+v.len-1).cmpIgnoreCase(v) == 0: + result = true + i.inc v.len + parsed.month = some(n.int) + break of s: parsed.second = takeInt(1..2) of ss: @@ -2294,7 +2258,7 @@ proc toDateTime(p: ParsedTime, zone: Timezone, f: TimeFormat, result.utcOffset = p.utcOffset.get() result = result.toTime.inZone(zone) -proc format*(dt: DateTime, f: TimeFormat): string {.raises: [].} = +proc format*(dt: DateTime, f: TimeFormat, loc: DateTimeLocale = DefaultLocale): string {.raises: [].} = ## Format ``dt`` using the format specified by ``f``. runnableExamples: let f = initTimeFormat("yyyy-MM-dd") @@ -2311,10 +2275,10 @@ proc format*(dt: DateTime, f: TimeFormat): string {.raises: [].} = result.add f.patterns[idx].char idx.inc else: - formatPattern(dt, f.patterns[idx].FormatPattern, result = result) + formatPattern(dt, f.patterns[idx].FormatPattern, result = result, loc = loc) idx.inc -proc format*(dt: DateTime, f: string): string +proc format*(dt: DateTime, f: string, loc: DateTimeLocale = DefaultLocale): string {.raises: [TimeFormatParseError].} = ## Shorthand for constructing a ``TimeFormat`` and using it to format ``dt``. ## @@ -2324,7 +2288,7 @@ proc format*(dt: DateTime, f: string): string let dt = initDateTime(01, mJan, 2000, 00, 00, 00, utc()) doAssert "2000-01-01" == format(dt, "yyyy-MM-dd") let dtFormat = initTimeFormat(f) - result = dt.format(dtFormat) + result = dt.format(dtFormat, loc) proc format*(dt: DateTime, f: static[string]): string {.raises: [].} = ## Overload that validates ``format`` at compile time. @@ -2358,12 +2322,14 @@ template formatValue*(result: var string; value: Time, specifier: string) = ## adapter for strformat. Not intended to be called directly. result.add format(value, specifier) -proc parse*(input: string, f: TimeFormat, zone: Timezone = local()): DateTime +proc parse*(input: string, f: TimeFormat, zone: Timezone = local(), loc: DateTimeLocale = DefaultLocale): DateTime {.raises: [TimeParseError, Defect].} = ## Parses ``input`` as a ``DateTime`` using the format specified by ``f``. ## If no UTC offset was parsed, then ``input`` is assumed to be specified in ## the ``zone`` timezone. If a UTC offset was parsed, the result will be ## converted to the ``zone`` timezone. + ## + ## Month and day names from the passed in ``loc`` are used. runnableExamples: let f = initTimeFormat("yyyy-MM-dd") let dt = initDateTime(01, mJan, 2000, 00, 00, 00, utc()) @@ -2385,7 +2351,7 @@ proc parse*(input: string, f: TimeFormat, zone: Timezone = local()): DateTime inpIdx.inc patIdx.inc else: - if not parsePattern(input, pattern, inpIdx, parsed): + if not parsePattern(input, pattern, inpIdx, parsed, loc): raiseParseException(f, input, "Failed on pattern '" & $pattern & "'") patIdx.inc @@ -2399,7 +2365,7 @@ proc parse*(input: string, f: TimeFormat, zone: Timezone = local()): DateTime result = toDateTime(parsed, zone, f, input) -proc parse*(input, f: string, tz: Timezone = local()): DateTime +proc parse*(input, f: string, tz: Timezone = local(), loc: DateTimeLocale = DefaultLocale): DateTime {.raises: [TimeParseError, TimeFormatParseError, Defect].} = ## Shorthand for constructing a ``TimeFormat`` and using it to parse ## ``input`` as a ``DateTime``. @@ -2410,13 +2376,13 @@ proc parse*(input, f: string, tz: Timezone = local()): DateTime let dt = initDateTime(01, mJan, 2000, 00, 00, 00, utc()) doAssert dt == parse("2000-01-01", "yyyy-MM-dd", utc()) let dtFormat = initTimeFormat(f) - result = input.parse(dtFormat, tz) + result = input.parse(dtFormat, tz, loc = loc) -proc parse*(input: string, f: static[string], zone: Timezone = local()): +proc parse*(input: string, f: static[string], zone: Timezone = local(), loc: DateTimeLocale = DefaultLocale): DateTime {.raises: [TimeParseError, Defect].} = ## Overload that validates ``f`` at compile time. const f2 = initTimeFormat(f) - result = input.parse(f2, zone) + result = input.parse(f2, zone, loc = loc) proc parseTime*(input, f: string, zone: Timezone): Time {.raises: [TimeParseError, TimeFormatParseError, Defect].} = diff --git a/testament/categories.nim b/testament/categories.nim index a2a0c5d5b..e0f57df8d 100644 --- a/testament/categories.nim +++ b/testament/categories.nim @@ -551,7 +551,8 @@ proc testNimblePackages(r: var TResults, cat: Category) = # ---------------------------------------------------------------------------- -const AdditionalCategories = ["debugger", "examples", "lib", "megatest"] +const AdditionalCategories = ["debugger", "examples", "lib"] +const MegaTestCat = "megatest" proc `&.?`(a, b: string): string = # candidate for the stdlib? @@ -679,22 +680,9 @@ proc runJoinedTest(r: var TResults, cat: Category, testsDir: string) = # --------------------------------------------------------------------------- -proc loadSkipFrom(name: string): seq[string] = - # One skip per line, comments start with # - # used by `nlvm` (at least) - try: - for line in lines(name): - let sline = line.strip() - if sline.len > 0 and not sline.startsWith("#"): - result.add sline - except: - echo "Could not load " & name & ", ignoring" - proc processCategory(r: var TResults, cat: Category, - options, testsDir, skipFrom: string, + options, testsDir: string, runJoinableTests: bool) = - let skips = loadSkipFrom(skipFrom) - case cat.string.normalize of "rodfiles": when false: @@ -750,9 +738,6 @@ proc processCategory(r: var TResults, cat: Category, for i, name in files: var test = makeTest(name, options, cat) - if skips.anyIt(it in name): - test.spec.err = reDisabled - if runJoinableTests or not isJoinableSpec(test.spec) or cat.string in specialCategories: discard "run the test" else: diff --git a/testament/specs.nim b/testament/specs.nim index 96b02746b..11286ceab 100644 --- a/testament/specs.nim +++ b/testament/specs.nim @@ -7,13 +7,15 @@ # distribution, for details about the copyright. # -import parseutils, strutils, os, osproc, streams, parsecfg +import sequtils, parseutils, strutils, os, osproc, streams, parsecfg var compilerPrefix* = findExe("nim") let isTravis* = existsEnv("TRAVIS") let isAppVeyor* = existsEnv("APPVEYOR") +var skips*: seq[string] + type TTestAction* = enum actionRun = "run" @@ -245,3 +247,6 @@ proc parseSpec*(filename: string): TSpec = of cfgEof: break close(p) + + if skips.anyIt(it in result.file): + result.err = reDisabled diff --git a/testament/tester.nim b/testament/tester.nim index b73190eba..f172a670d 100644 --- a/testament/tester.nim +++ b/testament/tester.nim @@ -44,6 +44,7 @@ Options: --directory:dir Change to directory dir before reading the tests or doing anything else. --colors:on|off Turn messagescoloring on|off. --backendLogging:on|off Disable or enable backend logging. By default turned on. + --megatest:on|off Enable or disable megatest. Default is on. --skipFrom:file Read tests to skip from `file` - one test per line, # comments ignored """ % resultsFile @@ -380,6 +381,15 @@ proc getTestSpecTarget(): TTarget = else: return targetC +proc checkDisabled(r: var TResults, test: TTest): bool = + if test.spec.err in {reDisabled, reJoined}: + # targetC is a lie, but parameter is required + r.addResult(test, targetC, "", "", test.spec.err) + inc(r.skipped) + inc(r.total) + return + true + proc testSpec(r: var TResults, test: TTest, targets: set[TTarget] = {}) = var expected = test.spec if expected.parseErrors.len > 0: @@ -387,12 +397,8 @@ proc testSpec(r: var TResults, test: TTest, targets: set[TTarget] = {}) = r.addResult(test, targetC, "", expected.parseErrors, reInvalidSpec) inc(r.total) return - if expected.err in {reDisabled, reJoined}: - # targetC is a lie, but parameter is required - r.addResult(test, targetC, "", "", expected.err) - inc(r.skipped) - inc(r.total) - return + if not checkDisabled(r, test): return + expected.targets.incl targets # still no target specified at all if expected.targets == {}: @@ -472,6 +478,8 @@ proc testSpec(r: var TResults, test: TTest, targets: set[TTarget] = {}) = proc testC(r: var TResults, test: TTest, action: TTestAction) = # runs C code. Doesn't support any specs, just goes by exit code. + if not checkDisabled(r, test): return + let tname = test.name.addFileExt(".c") inc(r.total) maybeStyledEcho "Processing ", fgCyan, extractFilename(tname) @@ -486,6 +494,8 @@ proc testC(r: var TResults, test: TTest, action: TTestAction) = proc testExec(r: var TResults, test: TTest) = # runs executable or script, just goes by exit code + if not checkDisabled(r, test): return + inc(r.total) let (outp, errC) = execCmdEx(test.options.strip()) var given: TSpec @@ -534,6 +544,19 @@ else: include categories +proc loadSkipFrom(name: string): seq[string] = + if name.len() == 0: return + + # One skip per line, comments start with # + # used by `nlvm` (at least) + try: + for line in lines(name): + let sline = line.strip() + if sline.len > 0 and not sline.startsWith("#"): + result.add sline + except: + echo "Could not load " & name & ", ignoring" + proc main() = os.putenv "NIMTEST_COLOR", "never" os.putenv "NIMTEST_OUTPUT_LVL", "PRINT_FAILURES" @@ -544,6 +567,7 @@ proc main() = var targetsStr = "" var isMainProcess = true var skipFrom = "" + var useMegatest = true var p = initOptParser() p.next() @@ -569,6 +593,14 @@ proc main() = quit Usage of "simulate": simulate = true + of "megatest": + case p.val.string: + of "on": + useMegatest = true + of "off": + useMegatest = false + else: + quit Usage of "backendlogging": case p.val.string: of "on": @@ -608,32 +640,37 @@ proc main() = if kind == pcDir and cat notin ["testdata", "nimcache"]: cats.add cat cats.add AdditionalCategories + if useMegatest: cats.add MegaTestCat var cmds: seq[string] for cat in cats: - cmds.add(myself & " pcat " & quoteShell(cat) & rest) + let runtype = if useMegatest: " pcat " else: " cat " + cmds.add(myself & runtype & quoteShell(cat) & rest) proc progressStatus(idx: int) = echo "progress[all]: i: " & $idx & " / " & $cats.len & " cat: " & cats[idx] if simulate: + skips = loadSkipFrom(skipFrom) for i, cati in cats: progressStatus(i) - processCategory(r, Category(cati), p.cmdLineRest.string, testsDir, skipFrom, runJoinableTests = false) + processCategory(r, Category(cati), p.cmdLineRest.string, testsDir, runJoinableTests = false) else: quit osproc.execProcesses(cmds, {poEchoCmd, poStdErrToStdOut, poUsePath, poParentStreams}, beforeRunEvent = progressStatus) of "c", "cat", "category": + skips = loadSkipFrom(skipFrom) var cat = Category(p.key) p.next - processCategory(r, cat, p.cmdLineRest.string, testsDir, skipFrom, runJoinableTests = true) + processCategory(r, cat, p.cmdLineRest.string, testsDir, runJoinableTests = true) of "pcat": + skips = loadSkipFrom(skipFrom) # 'pcat' is used for running a category in parallel. Currently the only # difference is that we don't want to run joinable tests here as they # are covered by the 'megatest' category. isMainProcess = false var cat = Category(p.key) p.next - processCategory(r, cat, p.cmdLineRest.string, testsDir, skipFrom, runJoinableTests = false) + processCategory(r, cat, p.cmdLineRest.string, testsDir, runJoinableTests = false) of "r", "run": # at least one directory is required in the path, to use as a category name let pathParts = split(p.key.string, {DirSep, AltSep}) diff --git a/tests/errmsgs/tuncheckedarrayvar.nim b/tests/errmsgs/tuncheckedarrayvar.nim new file mode 100644 index 000000000..17965914a --- /dev/null +++ b/tests/errmsgs/tuncheckedarrayvar.nim @@ -0,0 +1,9 @@ +discard """ +errormsg: ''' +invalid type: 'UncheckedArray[uint8]' for var +''' +""" + +var + rawMem = alloc0(20) + byteUA = cast[UncheckedArray[uint8]](rawMem) diff --git a/tests/macros/tmacros_issues.nim b/tests/macros/tmacros_issues.nim index 657f30fc4..d32b6a2a2 100644 --- a/tests/macros/tmacros_issues.nim +++ b/tests/macros/tmacros_issues.nim @@ -4,7 +4,7 @@ IntLit 5 proc (x: int): string => typeDesc[proc[string, int]] proc (x: int): void => typeDesc[proc[void, int]] proc (x: int) => typeDesc[proc[void, int]] -x => UncheckedArray[int] +x => seq[int] a s d @@ -111,7 +111,7 @@ block t2211: showType(proc(x:int): void) showType(proc(x:int)) - var x: UncheckedArray[int] + var x: seq[int] showType(x) diff --git a/tests/stdlib/ttimes.nim b/tests/stdlib/ttimes.nim index b29f5090b..ef6712171 100644 --- a/tests/stdlib/ttimes.nim +++ b/tests/stdlib/ttimes.nim @@ -452,6 +452,23 @@ suite "ttimes": doAssert dt.format("zz") == tz[2] doAssert dt.format("zzz") == tz[3] + test "format locale": + let loc = DateTimeLocale( + MMM: ["Fir","Sec","Thi","Fou","Fif","Six","Sev","Eig","Nin","Ten","Ele","Twe"], + MMMM: ["Firsty", "Secondy", "Thirdy", "Fourthy", "Fifthy", "Sixthy", "Seventhy", "Eighthy", "Ninthy", "Tenthy", "Eleventhy", "Twelfthy"], + ddd: ["Red", "Ora.", "Yel.", "Gre.", "Blu.", "Vio.", "Whi."], + dddd: ["Red", "Orange", "Yellow", "Green", "Blue", "Violet", "White"], + ) + var dt = initDateTime(5, mJan, 2010, 17, 01, 02, utc()) + check dt.format("d", loc) == "5" + check dt.format("dd", loc) == "05" + check dt.format("ddd", loc) == "Ora." + check dt.format("dddd", loc) == "Orange" + check dt.format("M", loc) == "1" + check dt.format("MM", loc) == "01" + check dt.format("MMM", loc) == "Fir" + check dt.format("MMMM", loc) == "Firsty" + test "parse": check $parse("20180101", "yyyyMMdd", utc()) == "2018-01-01T00:00:00Z" parseTestExcp("+120180101", "yyyyMMdd") @@ -473,6 +490,16 @@ suite "ttimes": parseTestExcp("2000 A", "yyyy g") + test "parse locale": + let loc = DateTimeLocale( + MMM: ["Fir","Sec","Thi","Fou","Fif","Six","Sev","Eig","Nin","Ten","Ele","Twe"], + MMMM: ["Firsty", "Secondy", "Thirdy", "Fourthy", "Fifthy", "Sixthy", "Seventhy", "Eighthy", "Ninthy", "Tenthy", "Eleventhy", "Twelfthy"], + ddd: ["Red", "Ora.", "Yel.", "Gre.", "Blu.", "Vio.", "Whi."], + dddd: ["Red", "Orange", "Yellow", "Green", "Blue", "Violet", "White"], + ) + check $parse("02 Fir 2019", "dd MMM yyyy", utc(), loc) == "2019-01-02T00:00:00Z" + check $parse("Fourthy 6, 2017", "MMMM d, yyyy", utc(), loc) == "2017-04-06T00:00:00Z" + test "countLeapYears": # 1920, 2004 and 2020 are leap years, and should be counted starting at the following year check countLeapYears(1920) + 1 == countLeapYears(1921) |