From b63dc4042b04a8650d30a97aec69f29acf6f51e0 Mon Sep 17 00:00:00 2001 From: Michael Voronin Date: Mon, 16 Apr 2018 11:59:41 +0300 Subject: [PATCH 01/31] [fix] Little fix for runnable example in pure/times --- 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 f6567cf58..b9655c4fb 100644 --- a/lib/pure/times.nim +++ b/lib/pure/times.nim @@ -535,7 +535,7 @@ proc `*`*(a: int64, b: Duration): Duration {.operator} = proc `*`*(a: Duration, b: int64): Duration {.operator} = ## Multiply a duration by some scalar. runnableExamples: - doAssert 5 * initDuration(seconds = 1) == initDuration(seconds = 5) + doAssert initDuration(seconds = 1) * 5 == initDuration(seconds = 5) b * a proc `div`*(a: Duration, b: int64): Duration {.operator} = From 52eac6fb64b180d61bd4b95a415449093c08ad67 Mon Sep 17 00:00:00 2001 From: Michael Voronin Date: Thu, 3 May 2018 10:25:30 +0300 Subject: [PATCH 02/31] [doc] Add/modify some runnable examples for pure/times --- lib/pure/times.nim | 140 +++++++++++++++++++++++++++++++++------------ 1 file changed, 104 insertions(+), 36 deletions(-) diff --git a/lib/pure/times.nim b/lib/pure/times.nim index b9655c4fb..64df3f3ae 100644 --- a/lib/pure/times.nim +++ b/lib/pure/times.nim @@ -306,12 +306,13 @@ proc fractional*(dur: Duration): Duration {.inline.} = doAssert dur.fractional == initDuration(nanoseconds = 5) initDuration(nanoseconds = dur.nanosecond) -const DurationZero* = initDuration() ## Zero value for durations. Useful for comparisons. - ## - ## .. code-block:: nim - ## - ## doAssert initDuration(seconds = 1) > DurationZero - ## doAssert initDuration(seconds = 0) == DurationZero +const DurationZero* = initDuration() ## \ + ## Zero value for durations. Useful for comparisons. + ## + ## .. code-block:: nim + ## + ## doAssert initDuration(seconds = 1) > DurationZero + ## doAssert initDuration(seconds = 0) == DurationZero proc `$`*(dur: Duration): string = ## Human friendly string representation of ``dur``. @@ -372,6 +373,9 @@ proc fromUnix*(unix: int64): Time {.benign, tags: [], raises: [], noSideEffect.} proc toUnix*(t: Time): int64 {.benign, tags: [], raises: [], noSideEffect.} = ## Convert ``t`` to a unix timestamp (seconds since ``1970-01-01T00:00:00Z``). + runnableExamples: + doAssert fromUnix(0).toUnix() == 0 + t.seconds proc fromWinTime*(win: int64): Time = @@ -564,10 +568,20 @@ proc `-`*(a: Time, b: Duration): Time {.operator, extern: "ntSubTime".} = proc `+=`*(a: var Time, b: Duration) {.operator.} = ## Modify ``a`` in place by subtracting ``b``. + runnableExamples: + var tm = fromUnix(0) + tm += initDuration(seconds = 1) + doAssert tm == fromUnix(1) + a = addImpl[Time](a, b) proc `-=`*(a: var Time, b: Duration) {.operator.} = ## Modify ``a`` in place by adding ``b``. + runnableExamples: + var tm = fromUnix(0) + tm -= initDuration(seconds = 1) + doAssert tm == fromUnix(-1) + a = subImpl[Time](a, b) proc `<`*(a, b: Time): bool {.operator, extern: "ntLtTime".} = @@ -617,6 +631,12 @@ proc toTime*(dt: DateTime): Time {.tags: [], raises: [], benign.} = proc `-`*(dt1, dt2: DateTime): Duration = ## Compute the duration between ``dt1`` and ``dt2``. + runnableExamples: + let dt1 = initDateTime(30, mMar, 2017, 00, 00, 00, utc()) + let dt2 = initDateTime(25, mMar, 2017, 00, 00, 00, utc()) + + doAssert dt1 - dt2 == initDuration(days = 5) + dt1.toTime - dt2.toTime proc `<`*(a, b: DateTime): bool = @@ -632,6 +652,7 @@ proc `==`*(a, b: DateTime): bool = return a.toTime == b.toTime proc initDateTime(zt: ZonedTime, zone: Timezone): DateTime = + ## Create a new ``DateTime`` using ``ZonedTime`` in the specified timezone. let s = zt.adjTime.seconds let epochday = (if s >= 0: s else: s - (secondsInDay - 1)) div secondsInDay var rem = s - epochday * secondsInDay @@ -917,11 +938,10 @@ proc `+`*(ti1, ti2: TimeInterval): TimeInterval = proc `-`*(ti: TimeInterval): TimeInterval = ## Reverses a time interval - ## - ## .. code-block:: nim - ## - ## let day = -initInterval(hours=24) - ## echo day # -> (milliseconds: 0, seconds: 0, minutes: 0, hours: -24, days: 0, months: 0, years: 0) + runnableExamples: + let day = -initTimeInterval(hours=24) + doAssert day.hours == -24 + result = TimeInterval( nanoseconds: -ti.nanoseconds, microseconds: -ti.microseconds, @@ -939,13 +959,10 @@ proc `-`*(ti1, ti2: TimeInterval): TimeInterval = ## Subtracts TimeInterval ``ti1`` from ``ti2``. ## ## Time components are subtracted one-by-one, see output: - ## - ## .. code-block:: nim - ## let a = fromUnix(1_000_000_000) - ## let b = fromUnix(1_500_000_000) - ## echo b.toTimeInterval - a.toTimeInterval - ## # (nanoseconds: 0, microseconds: 0, milliseconds: 0, seconds: -40, - ## minutes: -6, hours: 1, days: 5, weeks: 0, months: -2, years: 16) + runnableExamples: + let ti1 = initTimeInterval(hours=24) + let ti2 = initTimeInterval(hours=4) + doAssert (ti1 - ti2) == initTimeInterval(hours=20) result = ti1 + (-ti2) @@ -1100,12 +1117,26 @@ proc `-`*(dt: DateTime, interval: TimeInterval): DateTime = ## Subtract ``interval`` from ``dt``. Components from ``interval`` are subtracted ## in the order of their size, i.e first the ``years`` component, then the ``months`` ## component and so on. The returned ``DateTime`` will have the same timezone as the input. + runnableExamples: + let dt = initDateTime(30, mMar, 2017, 00, 00, 00, utc()) + doAssert $(dt - 5.days) == "2017-03-25T00:00:00+00:00" + dt + (-interval) proc `+`*(dt: DateTime, dur: Duration): DateTime = + runnableExamples: + let dt = initDateTime(30, mMar, 2017, 00, 00, 00, utc()) + let dur = initDuration(hours = 5) + doAssert $(dt + dur) == "2017-03-30T05:00:00+00:00" + (dt.toTime + dur).inZone(dt.timezone) proc `-`*(dt: DateTime, dur: Duration): DateTime = + runnableExamples: + let dt = initDateTime(30, mMar, 2017, 00, 00, 00, utc()) + let dur = initDuration(days = 5) + doAssert $(dt - dur) == "2017-03-25T00:00:00+00:00" + (dt.toTime - dur).inZone(dt.timezone) proc isStaticInterval(interval: TimeInterval): bool = @@ -1125,8 +1156,10 @@ proc `+`*(time: Time, interval: TimeInterval): Time = ## Adds `interval` to `time`. ## If `interval` contains any years, months, weeks or days the operation ## is performed in the local timezone. - ## - ## ``echo getTime() + 1.day`` + runnableExamples: + let tm = fromUnix(0) + doAssert tm + 5.seconds == fromUnix(5) + if interval.isStaticInterval: time + evaluateStaticInterval(interval) else: @@ -1136,14 +1169,21 @@ proc `+=`*(time: var Time, interval: TimeInterval) = ## Modifies `time` by adding `interval`. ## If `interval` contains any years, months, weeks or days the operation ## is performed in the local timezone. + runnableExamples: + var tm = fromUnix(0) + tm += 5.seconds + doAssert tm == fromUnix(5) + time = time + interval proc `-`*(time: Time, interval: TimeInterval): Time = ## Subtracts `interval` from Time `time`. ## If `interval` contains any years, months, weeks or days the operation ## is performed in the local timezone. - ## - ## ``echo getTime() - 1.day`` + runnableExamples: + let tm = fromUnix(5) + doAssert tm - 5.seconds == fromUnix(0) + if interval.isStaticInterval: time - evaluateStaticInterval(interval) else: @@ -1153,6 +1193,10 @@ proc `-=`*(time: var Time, interval: TimeInterval) = ## Modifies `time` by subtracting `interval`. ## If `interval` contains any years, months, weeks or days the operation ## is performed in the local timezone. + runnableExamples: + var tm = fromUnix(5) + tm -= 5.seconds + doAssert tm == fromUnix(0) time = time - interval proc formatToken(dt: DateTime, token: string, buf: var string) = @@ -1318,6 +1362,9 @@ proc format*(dt: DateTime, f: string): string {.tags: [].}= ## inserted without quoting them: ``:`` ``-`` ``(`` ``)`` ``/`` ``[`` ``]`` ## ``,``. However you don't need to necessarily separate format specifiers, a ## unambiguous format string like ``yyyyMMddhhmmss`` is valid too. + runnableExamples: + let dt = initDateTime(01, mJan, 2000, 12, 00, 00, utc()) + doAssert format(dt, "yyyy-MM-dd'T'HH:mm:sszzz") == "2000-01-01T12:00:00+00:00" result = "" var i = 0 @@ -1351,6 +1398,9 @@ proc format*(dt: DateTime, f: string): string {.tags: [].}= proc `$`*(dt: DateTime): string {.tags: [], raises: [], benign.} = ## Converts a `DateTime` object to a string representation. ## It uses the format ``yyyy-MM-dd'T'HH-mm-sszzz``. + runnableExamples: + let dt = initDateTime(01, mJan, 2000, 12, 00, 00, utc()) + doAssert $dt == "2000-01-01T12:00:00+00:00" try: result = format(dt, "yyyy-MM-dd'T'HH:mm:sszzz") # todo: optimize this except ValueError: assert false # cannot happen because format string is valid @@ -1358,6 +1408,10 @@ proc `$`*(dt: DateTime): string {.tags: [], raises: [], benign.} = proc `$`*(time: Time): string {.tags: [], raises: [], benign.} = ## converts a `Time` value to a string representation. It will use the local ## time zone and use the format ``yyyy-MM-dd'T'HH-mm-sszzz``. + runnableExamples: + let dt = initDateTime(01, mJan, 1970, 00, 00, 00, local()) + let tm = dt.toTime() + doAssert $tm == "1970-01-01T00:00:00" & format(dt, "zzz") $time.local {.pop.} @@ -1620,6 +1674,10 @@ proc parse*(value, layout: string, zone: Timezone = local()): DateTime = ## inserted without quoting them: ``:`` ``-`` ``(`` ``)`` ``/`` ``[`` ``]`` ## ``,``. However you don't need to necessarily separate format specifiers, a ## unambiguous format string like ``yyyyMMddhhmmss`` is valid too. + runnableExamples: + let tStr = "1970-01-01T00:00:00+00:00" + doAssert parse(tStr, "yyyy-MM-dd'T'HH:mm:sszzz") == fromUnix(0).utc + var i = 0 # pointer for format string var j = 0 # pointer for value string var token = "" @@ -1695,15 +1753,15 @@ proc toTimeInterval*(time: Time): TimeInterval = ## Converts a Time to a TimeInterval. ## ## To be used when diffing times. - ## - ## .. code-block:: nim - ## let a = fromSeconds(1_000_000_000) - ## let b = fromSeconds(1_500_000_000) - ## echo a, " ", b # real dates - ## echo a.toTimeInterval # meaningless value, don't use it by itself - ## echo b.toTimeInterval - a.toTimeInterval - ## # (nanoseconds: 0, microseconds: 0, milliseconds: 0, seconds: -40, - ## minutes: -6, hours: 1, days: 5, weeks: 0, months: -2, years: 16) + runnableExamples: + let a = fromUnix(10) + let dt = initDateTime(01, mJan, 1970, 00, 00, 00, local()) + doAssert a.toTimeInterval() == initTimeInterval( + years=1970, days=1, seconds=10, hours=convert( + Seconds, Hours, -dt.utcOffset + ) + ) + var dt = time.local initTimeInterval(dt.nanosecond, 0, 0, dt.second, dt.minute, dt.hour, dt.monthday, 0, dt.month.ord - 1, dt.year) @@ -1712,6 +1770,10 @@ proc initDateTime*(monthday: MonthdayRange, month: Month, year: int, hour: HourRange, minute: MinuteRange, second: SecondRange, nanosecond: NanosecondRange, zone: Timezone = local()): DateTime = ## Create a new ``DateTime`` in the specified timezone. + runnableExamples: + let dt1 = initDateTime(30, mMar, 2017, 00, 00, 00, 00, utc()) + doAssert $dt1 == "2017-03-30T00:00:00+00:00" + assertValidDate monthday, month, year let dt = DateTime( monthday: monthday, @@ -1728,6 +1790,9 @@ proc initDateTime*(monthday: MonthdayRange, month: Month, year: int, hour: HourRange, minute: MinuteRange, second: SecondRange, zone: Timezone = local()): DateTime = ## Create a new ``DateTime`` in the specified timezone. + runnableExamples: + let dt1 = initDateTime(30, mMar, 2017, 00, 00, 00, utc()) + doAssert $dt1 == "2017-03-30T00:00:00+00:00" initDateTime(monthday, month, year, hour, minute, second, 0, zone) when not defined(JS): @@ -1747,11 +1812,14 @@ when not defined(JS): ## The value of the result has no meaning. ## To generate useful timing values, take the difference between ## the results of two ``cpuTime`` calls: - ## - ## .. code-block:: nim - ## var t0 = cpuTime() - ## doWork() - ## echo "CPU time [s] ", cpuTime() - t0 + runnableExamples: + var t0 = cpuTime() + # some useless work here (calculate fibonacci) + var fib = @[0, 1, 1] + for i in 1..10: + fib.add(fib[^1] + fib[^2]) + echo "CPU time [s] ", cpuTime() - t0 + echo "Fib is [s] ", fib result = toFloat(int(getClock())) / toFloat(clocksPerSec) proc epochTime*(): float {.rtl, extern: "nt$1", tags: [TimeEffect].} = From 0ff0dfbfce6a26903709bf2f6e06d22c86e70c07 Mon Sep 17 00:00:00 2001 From: Michael Voronin Date: Thu, 3 May 2018 17:09:54 +0300 Subject: [PATCH 03/31] [refactoring] Rearrange functions for better readability/usage --- lib/pure/times.nim | 294 ++++++++++++++++++++++++--------------------- 1 file changed, 155 insertions(+), 139 deletions(-) diff --git a/lib/pure/times.nim b/lib/pure/times.nim index 64df3f3ae..c7063b252 100644 --- a/lib/pure/times.nim +++ b/lib/pure/times.nim @@ -229,31 +229,23 @@ proc normalize[T: Duration|Time](seconds, nanoseconds: int64): T = result.seconds -= 1 result.nanosecond = nanosecond.int -proc initTime*(unix: int64, nanosecond: NanosecondRange): Time = - ## Create a ``Time`` from a unix timestamp and a nanosecond part. - result.seconds = unix - result.nanosecond = nanosecond +# Forward declarations +proc utcZoneInfoFromUtc(time: Time): ZonedTime {.tags: [], raises: [], benign .} +proc utcZoneInfoFromTz(adjTime: Time): ZonedTime {.tags: [], raises: [], benign .} +proc localZoneInfoFromUtc(time: Time): ZonedTime {.tags: [], raises: [], benign .} +proc localZoneInfoFromTz(adjTime: Time): ZonedTime {.tags: [], raises: [], benign .} +proc initTime*(unix: int64, nanosecond: NanosecondRange): Time + {.tags: [], raises: [], benign noSideEffect.} + +proc initDuration*(nanoseconds, microseconds, milliseconds, + seconds, minutes, hours, days, weeks: int64 = 0): Duration + {.tags: [], raises: [], benign noSideEffect.} proc nanosecond*(time: Time): NanosecondRange = ## Get the fractional part of a ``Time`` as the number ## of nanoseconds of the second. time.nanosecond -proc initDuration*(nanoseconds, microseconds, milliseconds, - seconds, minutes, hours, days, weeks: int64 = 0): Duration = - let seconds = convert(Weeks, Seconds, weeks) + - convert(Days, Seconds, days) + - convert(Minutes, Seconds, minutes) + - convert(Hours, Seconds, hours) + - convert(Seconds, Seconds, seconds) + - convert(Milliseconds, Seconds, milliseconds) + - convert(Microseconds, Seconds, microseconds) + - convert(Nanoseconds, Seconds, nanoseconds) - let nanoseconds = (convert(Milliseconds, Nanoseconds, milliseconds mod 1000) + - convert(Microseconds, Nanoseconds, microseconds mod 1_000_000) + - nanoseconds mod 1_000_000_000).int - # Nanoseconds might be negative so we must normalize. - result = normalize[Duration](seconds, nanoseconds) proc weeks*(dur: Duration): int64 {.inline.} = ## Number of whole weeks represented by the duration. @@ -306,64 +298,6 @@ proc fractional*(dur: Duration): Duration {.inline.} = doAssert dur.fractional == initDuration(nanoseconds = 5) initDuration(nanoseconds = dur.nanosecond) -const DurationZero* = initDuration() ## \ - ## Zero value for durations. Useful for comparisons. - ## - ## .. code-block:: nim - ## - ## doAssert initDuration(seconds = 1) > DurationZero - ## doAssert initDuration(seconds = 0) == DurationZero - -proc `$`*(dur: Duration): string = - ## Human friendly string representation of ``dur``. - runnableExamples: - doAssert $initDuration(seconds = 2) == "2 seconds" - doAssert $initDuration(weeks = 1, days = 2) == "1 week and 2 days" - doAssert $initDuration(hours = 1, minutes = 2, seconds = 3) == "1 hour, 2 minutes, and 3 seconds" - doAssert $initDuration(milliseconds = -1500) == "-1 second and -500 milliseconds" - var parts = newSeq[string]() - var remS = dur.seconds - var remNs = dur.nanosecond.int - - # Normally ``nanoseconds`` should always be positive, but - # that makes no sense when printing. - if remS < 0: - remNs -= convert(Seconds, Nanoseconds, 1) - remS.inc 1 - - const unitStrings: array[FixedTimeUnit, string] = [ - "nanosecond", "microsecond", "millisecond", "second", "minute", "hour", "day", "week" - ] - - for unit in countdown(Weeks, Seconds): - let quantity = convert(Seconds, unit, remS) - remS = remS mod convert(unit, Seconds, 1) - - if quantity.abs == 1: - parts.add $quantity & " " & unitStrings[unit] - elif quantity != 0: - parts.add $quantity & " " & unitStrings[unit] & "s" - - for unit in countdown(Milliseconds, Nanoseconds): - let quantity = convert(Nanoseconds, unit, remNs) - remNs = remNs mod convert(unit, Nanoseconds, 1) - - if quantity.abs == 1: - parts.add $quantity & " " & unitStrings[unit] - elif quantity != 0: - parts.add $quantity & " " & unitStrings[unit] & "s" - - result = "" - if parts.len == 0: - result.add "0 nanoseconds" - elif parts.len == 1: - result = parts[0] - elif parts.len == 2: - result = parts[0] & " and " & parts[1] - else: - for part in parts[0..high(parts)-1]: - result.add part & ", " - result.add "and " & parts[high(parts)] proc fromUnix*(unix: int64): Time {.benign, tags: [], raises: [], noSideEffect.} = ## Convert a unix timestamp (seconds since ``1970-01-01T00:00:00Z``) to a ``Time``. @@ -468,11 +402,6 @@ proc getDayOfWeek*(monthday: MonthdayRange, month: Month, year: int): WeekDay {. # so we must correct for the WeekDay type. result = if wd == 0: dSun else: WeekDay(wd - 1) -# Forward declarations -proc utcZoneInfoFromUtc(time: Time): ZonedTime {.tags: [], raises: [], benign .} -proc utcZoneInfoFromTz(adjTime: Time): ZonedTime {.tags: [], raises: [], benign .} -proc localZoneInfoFromUtc(time: Time): ZonedTime {.tags: [], raises: [], benign .} -proc localZoneInfoFromTz(adjTime: Time): ZonedTime {.tags: [], raises: [], benign .} {. pragma: operator, rtl, noSideEffect, benign .} @@ -493,6 +422,86 @@ template lqImpl(a: Duration|Time, b: Duration|Time): bool = template eqImpl(a: Duration|Time, b: Duration|Time): bool = a.seconds == b.seconds and a.nanosecond == b.nanosecond +proc initDuration*(nanoseconds, microseconds, milliseconds, + seconds, minutes, hours, days, weeks: int64 = 0): Duration = + runnableExamples: + let dur = initDuration(seconds = 1, milliseconds = 1) + doAssert dur.milliseconds == 1 + doAssert dur.seconds == 1 + + let seconds = convert(Weeks, Seconds, weeks) + + convert(Days, Seconds, days) + + convert(Minutes, Seconds, minutes) + + convert(Hours, Seconds, hours) + + convert(Seconds, Seconds, seconds) + + convert(Milliseconds, Seconds, milliseconds) + + convert(Microseconds, Seconds, microseconds) + + convert(Nanoseconds, Seconds, nanoseconds) + let nanoseconds = (convert(Milliseconds, Nanoseconds, milliseconds mod 1000) + + convert(Microseconds, Nanoseconds, microseconds mod 1_000_000) + + nanoseconds mod 1_000_000_000).int + # Nanoseconds might be negative so we must normalize. + result = normalize[Duration](seconds, nanoseconds) + +const DurationZero* = initDuration() ## \ + ## Zero value for durations. Useful for comparisons. + ## + ## .. code-block:: nim + ## + ## doAssert initDuration(seconds = 1) > DurationZero + ## doAssert initDuration(seconds = 0) == DurationZero + +proc `$`*(dur: Duration): string = + ## Human friendly string representation of ``dur``. + runnableExamples: + doAssert $initDuration(seconds = 2) == "2 seconds" + doAssert $initDuration(weeks = 1, days = 2) == "1 week and 2 days" + doAssert $initDuration(hours = 1, minutes = 2, seconds = 3) == "1 hour, 2 minutes, and 3 seconds" + doAssert $initDuration(milliseconds = -1500) == "-1 second and -500 milliseconds" + var parts = newSeq[string]() + var remS = dur.seconds + var remNs = dur.nanosecond.int + + # Normally ``nanoseconds`` should always be positive, but + # that makes no sense when printing. + if remS < 0: + remNs -= convert(Seconds, Nanoseconds, 1) + remS.inc 1 + + const unitStrings: array[FixedTimeUnit, string] = [ + "nanosecond", "microsecond", "millisecond", "second", "minute", "hour", "day", "week" + ] + + for unit in countdown(Weeks, Seconds): + let quantity = convert(Seconds, unit, remS) + remS = remS mod convert(unit, Seconds, 1) + + if quantity.abs == 1: + parts.add $quantity & " " & unitStrings[unit] + elif quantity != 0: + parts.add $quantity & " " & unitStrings[unit] & "s" + + for unit in countdown(Milliseconds, Nanoseconds): + let quantity = convert(Nanoseconds, unit, remNs) + remNs = remNs mod convert(unit, Nanoseconds, 1) + + if quantity.abs == 1: + parts.add $quantity & " " & unitStrings[unit] + elif quantity != 0: + parts.add $quantity & " " & unitStrings[unit] & "s" + + result = "" + if parts.len == 0: + result.add "0 nanoseconds" + elif parts.len == 1: + result = parts[0] + elif parts.len == 2: + result = parts[0] & " and " & parts[1] + else: + for part in parts[0..high(parts)-1]: + result.add part & ", " + result.add "and " & parts[high(parts)] + proc `+`*(a, b: Duration): Duration {.operator.} = ## Add two durations together. runnableExamples: @@ -550,6 +559,11 @@ proc `div`*(a: Duration, b: int64): Duration {.operator} = let carryOver = convert(Seconds, Nanoseconds, a.seconds mod b) normalize[Duration](a.seconds div b, (a.nanosecond + carryOver) div b) +proc initTime*(unix: int64, nanosecond: NanosecondRange): Time = + ## Create a ``Time`` from a unix timestamp and a nanosecond part. + result.seconds = unix + result.nanosecond = nanosecond + proc `-`*(a, b: Time): Duration {.operator, extern: "ntDiffTime".} = ## Computes the duration between two points in time. subImpl[Duration](a, b) @@ -560,12 +574,6 @@ proc `+`*(a: Time, b: Duration): Time {.operator, extern: "ntAddTime".} = doAssert (fromUnix(0) + initDuration(seconds = 1)) == fromUnix(1) addImpl[Time](a, b) -proc `-`*(a: Time, b: Duration): Time {.operator, extern: "ntSubTime".} = - ## Subtracts a duration of time from a ``Time``. - runnableExamples: - doAssert (fromUnix(0) - initDuration(seconds = 1)) == fromUnix(-1) - subImpl[Time](a, b) - proc `+=`*(a: var Time, b: Duration) {.operator.} = ## Modify ``a`` in place by subtracting ``b``. runnableExamples: @@ -575,6 +583,12 @@ proc `+=`*(a: var Time, b: Duration) {.operator.} = a = addImpl[Time](a, b) +proc `-`*(a: Time, b: Duration): Time {.operator, extern: "ntSubTime".} = + ## Subtracts a duration of time from a ``Time``. + runnableExamples: + doAssert (fromUnix(0) - initDuration(seconds = 1)) == fromUnix(-1) + subImpl[Time](a, b) + proc `-=`*(a: var Time, b: Duration) {.operator.} = ## Modify ``a`` in place by adding ``b``. runnableExamples: @@ -629,28 +643,6 @@ proc toTime*(dt: DateTime): Time {.tags: [], raises: [], benign.} = seconds.inc dt.utcOffset result = initTime(seconds, dt.nanosecond) -proc `-`*(dt1, dt2: DateTime): Duration = - ## Compute the duration between ``dt1`` and ``dt2``. - runnableExamples: - let dt1 = initDateTime(30, mMar, 2017, 00, 00, 00, utc()) - let dt2 = initDateTime(25, mMar, 2017, 00, 00, 00, utc()) - - doAssert dt1 - dt2 == initDuration(days = 5) - - dt1.toTime - dt2.toTime - -proc `<`*(a, b: DateTime): bool = - ## Returns true iff ``a < b``, that is iff a happened before b. - return a.toTime < b.toTime - -proc `<=` * (a, b: DateTime): bool = - ## Returns true iff ``a <= b``. - return a.toTime <= b.toTime - -proc `==`*(a, b: DateTime): bool = - ## Returns true if ``a == b``, that is if both dates represent the same point in datetime. - return a.toTime == b.toTime - proc initDateTime(zt: ZonedTime, zone: Timezone): DateTime = ## Create a new ``DateTime`` using ``ZonedTime`` in the specified timezone. let s = zt.adjTime.seconds @@ -1084,6 +1076,37 @@ proc evaluateInterval(dt: DateTime, interval: TimeInterval): tuple[adjDur, absDu minutes = interval.minutes, hours = interval.hours) + +proc initDateTime*(monthday: MonthdayRange, month: Month, year: int, + hour: HourRange, minute: MinuteRange, second: SecondRange, + nanosecond: NanosecondRange, zone: Timezone = local()): DateTime = + ## Create a new ``DateTime`` in the specified timezone. + runnableExamples: + let dt1 = initDateTime(30, mMar, 2017, 00, 00, 00, 00, utc()) + doAssert $dt1 == "2017-03-30T00:00:00+00:00" + + assertValidDate monthday, month, year + let dt = DateTime( + monthday: monthday, + year: year, + month: month, + hour: hour, + minute: minute, + second: second, + nanosecond: nanosecond + ) + result = initDateTime(zone.zoneInfoFromTz(dt.toAdjTime), zone) + +proc initDateTime*(monthday: MonthdayRange, month: Month, year: int, + hour: HourRange, minute: MinuteRange, second: SecondRange, + zone: Timezone = local()): DateTime = + ## Create a new ``DateTime`` in the specified timezone. + runnableExamples: + let dt1 = initDateTime(30, mMar, 2017, 00, 00, 00, utc()) + doAssert $dt1 == "2017-03-30T00:00:00+00:00" + initDateTime(monthday, month, year, hour, minute, second, 0, zone) + + proc `+`*(dt: DateTime, interval: TimeInterval): DateTime = ## Adds ``interval`` to ``dt``. Components from ``interval`` are added ## in the order of their size, i.e first the ``years`` component, then the ``months`` @@ -1139,6 +1162,28 @@ proc `-`*(dt: DateTime, dur: Duration): DateTime = (dt.toTime - dur).inZone(dt.timezone) +proc `-`*(dt1, dt2: DateTime): Duration = + ## Compute the duration between ``dt1`` and ``dt2``. + runnableExamples: + let dt1 = initDateTime(30, mMar, 2017, 00, 00, 00, utc()) + let dt2 = initDateTime(25, mMar, 2017, 00, 00, 00, utc()) + + doAssert dt1 - dt2 == initDuration(days = 5) + + dt1.toTime - dt2.toTime + +proc `<`*(a, b: DateTime): bool = + ## Returns true iff ``a < b``, that is iff a happened before b. + return a.toTime < b.toTime + +proc `<=` * (a, b: DateTime): bool = + ## Returns true iff ``a <= b``. + return a.toTime <= b.toTime + +proc `==`*(a, b: DateTime): bool = + ## Returns true if ``a == b``, that is if both dates represent the same point in datetime. + return a.toTime == b.toTime + proc isStaticInterval(interval: TimeInterval): bool = interval.years == 0 and interval.months == 0 and interval.days == 0 and interval.weeks == 0 @@ -1766,35 +1811,6 @@ proc toTimeInterval*(time: Time): TimeInterval = initTimeInterval(dt.nanosecond, 0, 0, dt.second, dt.minute, dt.hour, dt.monthday, 0, dt.month.ord - 1, dt.year) -proc initDateTime*(monthday: MonthdayRange, month: Month, year: int, - hour: HourRange, minute: MinuteRange, second: SecondRange, - nanosecond: NanosecondRange, zone: Timezone = local()): DateTime = - ## Create a new ``DateTime`` in the specified timezone. - runnableExamples: - let dt1 = initDateTime(30, mMar, 2017, 00, 00, 00, 00, utc()) - doAssert $dt1 == "2017-03-30T00:00:00+00:00" - - assertValidDate monthday, month, year - let dt = DateTime( - monthday: monthday, - year: year, - month: month, - hour: hour, - minute: minute, - second: second, - nanosecond: nanosecond - ) - result = initDateTime(zone.zoneInfoFromTz(dt.toAdjTime), zone) - -proc initDateTime*(monthday: MonthdayRange, month: Month, year: int, - hour: HourRange, minute: MinuteRange, second: SecondRange, - zone: Timezone = local()): DateTime = - ## Create a new ``DateTime`` in the specified timezone. - runnableExamples: - let dt1 = initDateTime(30, mMar, 2017, 00, 00, 00, utc()) - doAssert $dt1 == "2017-03-30T00:00:00+00:00" - initDateTime(monthday, month, year, hour, minute, second, 0, zone) - when not defined(JS): type Clock {.importc: "clock_t".} = distinct int From 8a080acda915f24bdd61a01175dcef6f6735d90e Mon Sep 17 00:00:00 2001 From: Michael Voronin Date: Thu, 10 May 2018 10:28:08 +0300 Subject: [PATCH 04/31] [add] Add some wrappers --- lib/pure/times.nim | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/lib/pure/times.nim b/lib/pure/times.nim index c7063b252..8b134f226 100644 --- a/lib/pure/times.nim +++ b/lib/pure/times.nim @@ -1440,6 +1440,19 @@ proc format*(dt: DateTime, f: string): string {.tags: [].}= inc(i) formatToken(dt, currentF, result) +proc format*(time: Time, f: string, zone_info: proc(t: Time): DateTime): string {.tags: [].} = + ## converts a `Time` value to a string representation. It will use format from + ## ``format(dt: DateTime, f: string)``. + runnableExamples: + var dt = initDateTime(01, mJan, 1970, 00, 00, 00, local()) + var tm = dt.toTime() + doAssert format(tm, "yyyy-MM-dd'T'HH:mm:ss", local) == "1970-01-01T00:00:00" + dt = initDateTime(01, mJan, 1970, 00, 00, 00, utc()) + tm = dt.toTime() + doAssert format(tm, "yyyy-MM-dd'T'HH:mm:ss", utc) == "1970-01-01T00:00:00" + + zone_info(time).format(f) + proc `$`*(dt: DateTime): string {.tags: [], raises: [], benign.} = ## Converts a `DateTime` object to a string representation. ## It uses the format ``yyyy-MM-dd'T'HH-mm-sszzz``. @@ -1770,6 +1783,13 @@ proc parse*(value, layout: string, zone: Timezone = local()): DateTime = # Otherwise convert to `zone` result = dt.toTime.inZone(zone) +proc parseTime*(value, layout: string, zone: Timezone): Time = + ## Simple wrapper for parsing string to time + runnableExamples: + let tStr = "1970-01-01T00:00:00+00:00" + doAssert parseTime(tStr, "yyyy-MM-dd'T'HH:mm:sszzz", local()) == fromUnix(0) + parse(value, layout, zone).toTime() + proc countLeapYears*(yearSpan: int): int = ## Returns the number of leap years spanned by a given number of years. ## From 08d1b5892b69548bf9a63353b8128e52e222e53d Mon Sep 17 00:00:00 2001 From: Michael Voronin Date: Fri, 20 Apr 2018 17:48:29 +0300 Subject: [PATCH 05/31] [doc] Standardize deprecation warnings --- lib/pure/times.nim | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/lib/pure/times.nim b/lib/pure/times.nim index 8b134f226..a6b6137e6 100644 --- a/lib/pure/times.nim +++ b/lib/pure/times.nim @@ -1957,15 +1957,14 @@ proc getTimezone*(): int {.tags: [TimeEffect], raises: [], benign, deprecated.} proc timeInfoToTime*(dt: DateTime): Time {.tags: [], benign, deprecated.} = ## Converts a broken-down time structure to calendar time representation. ## - ## **Warning:** This procedure is deprecated since version 0.14.0. - ## Use ``toTime`` instead. + ## **Deprecated since v0.14.0:** use ``toTime`` instead. dt.toTime when defined(JS): var start = getTime() proc getStartMilsecs*(): int {.deprecated, tags: [TimeEffect], benign.} = - ## get the milliseconds from the start of the program. **Deprecated since - ## version 0.8.10.** Use ``epochTime`` or ``cpuTime`` instead. + ## get the milliseconds from the start of the program. + ## **Deprecated since v0.8.10:** use ``epochTime`` or ``cpuTime`` instead. let dur = getTime() - start result = (convert(Seconds, Milliseconds, dur.seconds) + convert(Nanoseconds, Milliseconds, dur.nanosecond)).int @@ -1979,19 +1978,19 @@ else: proc timeToTimeInterval*(t: Time): TimeInterval {.deprecated.} = ## Converts a Time to a TimeInterval. ## - ## **Warning:** This procedure is deprecated since version 0.14.0. - ## Use ``toTimeInterval`` instead. + ## **Deprecated since v0.14.0:** use ``toTimeInterval`` instead. # Milliseconds not available from Time t.toTimeInterval() proc getDayOfWeek*(day, month, year: int): WeekDay {.tags: [], raises: [], benign, deprecated.} = - ## **Warning:** This procedure is deprecated since version 0.18.0. + ## **Deprecated since v0.18.0:** use + ## ``getDayOfWeek(monthday: MonthdayRange; month: Month; year: int)`` instead. getDayOfWeek(day, month.Month, year) proc getDayOfWeekJulian*(day, month, year: int): WeekDay {.deprecated.} = ## Returns the day of the week enum from day, month and year, ## according to the Julian calendar. - ## **Warning:** This procedure is deprecated since version 0.18.0. + ## **Deprecated since v0.18.0:** # Day & month start from one. let a = (14 - month) div 12 From 66780c1f4fe4dc0d525e593c685cb7933bf81fe6 Mon Sep 17 00:00:00 2001 From: Clyybber Date: Mon, 28 May 2018 20:17:21 +0200 Subject: [PATCH 06/31] Small grammar/spelling fix for changelog (#7896) * Small grammar/spelling fix. * Tiny grammar fix --- changelog.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/changelog.md b/changelog.md index 813c4caf8..3482c9eba 100644 --- a/changelog.md +++ b/changelog.md @@ -76,13 +76,13 @@ - The `terminal` module now exports additional procs for generating ANSI color codes as strings. - Added the parameter ``val`` for the ``CritBitTree[int].inc`` proc. -- An exception raised from ``test`` block of ``unittest`` now show its type in - error message +- An exception raised from ``test`` block of ``unittest`` now shows its type in + the error message ### Language additions - Dot calls combined with explicit generic instantiations can now be written - as ``x.y[:z]``. ``x.y[:z]`` that is transformed into ``y[z](x)`` in the parser. + as ``x.y[:z]`` which is transformed into ``y[z](x)`` by the parser. - ``func`` is now an alias for ``proc {.noSideEffect.}``. - In order to make ``for`` loops and iterators more flexible to use Nim now supports so called "for-loop macros". See From 5866e64ebcc4d6a80fb3403b6a38317fe5a52e74 Mon Sep 17 00:00:00 2001 From: hlaaf Date: Tue, 29 May 2018 02:15:37 +0300 Subject: [PATCH 07/31] fix #7881, control characters in json output (#7887) * fix #7881, control characters in json output * Add test for control characters in json --- lib/pure/json.nim | 3 +++ 1 file changed, 3 insertions(+) diff --git a/lib/pure/json.nim b/lib/pure/json.nim index 2bb830bcb..e7ad5bd5a 100644 --- a/lib/pure/json.nim +++ b/lib/pure/json.nim @@ -558,6 +558,8 @@ proc escapeJson*(s: string; result: var string) = of '\t': result.add("\\t") of '\r': result.add("\\r") of '"': result.add("\\\"") + of '\0'..'\7': result.add("\\u000" & $ord(c)) + of '\14'..'\31': result.add("\\u00" & $ord(c)) of '\\': result.add("\\\\") else: result.add(c) result.add("\"") @@ -1581,6 +1583,7 @@ when isMainModule: doAssert(parsed2{"repository", "description"}.str=="IRC Library for Haskell", "Couldn't fetch via multiply nested key using {}") doAssert escapeJson("\10Foo🎃barÄ") == "\"\\nFoo🎃barÄ\"" + doAssert escapeJson("\0\7\20") == "\"\\u0000\\u0007\\u0020\"" # for #7887 # Test with extra data when not defined(js): From a075a912cfaba685ad1ba9e41d046265fdf104b8 Mon Sep 17 00:00:00 2001 From: Dmitry Atamanov Date: Tue, 29 May 2018 07:28:15 +0300 Subject: [PATCH 08/31] Add algorithm.upperBound (#7851) * Add algorithm.upperBound * Docs updated --- changelog.md | 1 + lib/pure/algorithm.nim | 43 ++++++++++++++++++++++++++++++++++++++---- 2 files changed, 40 insertions(+), 4 deletions(-) diff --git a/changelog.md b/changelog.md index 3482c9eba..27090a1df 100644 --- a/changelog.md +++ b/changelog.md @@ -57,6 +57,7 @@ - Added the proc ``times.convert`` for converting between different time units, e.g days to seconds. - Added the proc ``algorithm.binarySearch[T, K]`` with the ```cmp``` parameter. +- Added the proc ``algorithm.upperBound``. ### Library changes diff --git a/lib/pure/algorithm.nim b/lib/pure/algorithm.nim index 98330b680..81badfae6 100644 --- a/lib/pure/algorithm.nim +++ b/lib/pure/algorithm.nim @@ -122,14 +122,14 @@ const onlySafeCode = true proc lowerBound*[T, K](a: openArray[T], key: K, cmp: proc(x: T, k: K): int {.closure.}): int = - ## same as binarySearch except that if key is not in `a` then this - ## returns the location where `key` would be if it were. In other - ## words if you have a sorted sequence and you call + ## Returns a position to the first element in the `a` that is greater than `key`, or last + ## if no such element is found. In other words if you have a sorted sequence and you call ## insert(thing, elm, lowerBound(thing, elm)) ## the sequence will still be sorted. ## - ## `cmp` is the comparator function to use, the expected return values are + ## The first version uses `cmp` to compare the elements. The expected return values are ## the same as that of system.cmp. + ## The second version uses the default comparison function `cmp`. ## ## example:: ## @@ -150,6 +150,36 @@ proc lowerBound*[T, K](a: openArray[T], key: K, cmp: proc(x: T, k: K): int {.clo proc lowerBound*[T](a: openArray[T], key: T): int = lowerBound(a, key, cmp[T]) +proc upperBound*[T, K](a: openArray[T], key: K, cmp: proc(x: T, k: K): int {.closure.}): int = + ## Returns a position to the first element in the `a` that is not less + ## (i.e. greater or equal to) than `key`, or last if no such element is found. + ## In other words if you have a sorted sequence and you call + ## insert(thing, elm, upperBound(thing, elm)) + ## the sequence will still be sorted. + ## + ## The first version uses `cmp` to compare the elements. The expected return values are + ## the same as that of system.cmp. + ## The second version uses the default comparison function `cmp`. + ## + ## example:: + ## + ## var arr = @[1,2,3,4,6,7,8,9] + ## arr.insert(5, arr.upperBound(4)) + ## # after running the above arr is `[1,2,3,4,5,6,7,8,9]` + result = a.low + var count = a.high - a.low + 1 + var step, pos: int + while count != 0: + step = count shr 1 + pos = result + step + if cmp(a[pos], key) <= 0: + result = pos + 1 + count -= step + 1 + else: + count = step + +proc upperBound*[T](a: openArray[T], key: T): int = upperBound(a, key, cmp[T]) + template `<-` (a, b) = when false: a = b @@ -546,6 +576,11 @@ when isMainModule: doAssert lowerBound([1,2,2,3], 4, system.cmp[int]) == 4 doAssert lowerBound([1,2,3,10], 11) == 4 + block upperBound: + doAssert upperBound([1,2,4], 3, system.cmp[int]) == 2 + doAssert upperBound([1,2,2,3], 3, system.cmp[int]) == 4 + doAssert upperBound([1,2,3,5], 3) == 3 + block fillEmptySeq: var s = newSeq[int]() s.fill(0) From 25a41d5d90c707e8d926308c495c95e2cd13d984 Mon Sep 17 00:00:00 2001 From: andri lim Date: Tue, 29 May 2018 14:38:52 +0700 Subject: [PATCH 09/31] fixes #7818, correct internal representation of generic objects array construction (#7824) * defer skiptypes * defer skiptypes for tyRef & tyPtr * remove unneeded skipTypes --- compiler/sem.nim | 2 +- compiler/types.nim | 8 ++++++-- tests/array/t7818.nim | 45 +++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 52 insertions(+), 3 deletions(-) create mode 100644 tests/array/t7818.nim diff --git a/compiler/sem.nim b/compiler/sem.nim index 7bccf1556..55e1f47dc 100644 --- a/compiler/sem.nim +++ b/compiler/sem.nim @@ -158,7 +158,7 @@ proc commonType*(x, y: PType): PType = a = a.lastSon.skipTypes({tyGenericInst}) b = b.lastSon.skipTypes({tyGenericInst}) if a.kind == tyObject and b.kind == tyObject: - result = commonSuperclass(a, b) + result = commonSuperclass(a, b, k) # this will trigger an error later: if result.isNil or result == a: return x if result == b: return y diff --git a/compiler/types.nim b/compiler/types.nim index b5f4fbf54..7f9b8239f 100644 --- a/compiler/types.nim +++ b/compiler/types.nim @@ -1043,7 +1043,7 @@ proc inheritanceDiff*(a, b: PType): int = inc(result) result = high(int) -proc commonSuperclass*(a, b: PType): PType = +proc commonSuperclass*(a, b: PType, k: TTypeKind): PType = # quick check: are they the same? if sameObjectTypes(a, b): return a @@ -1059,8 +1059,12 @@ proc commonSuperclass*(a, b: PType): PType = x = x.sons[0] var y = b while y != nil: + var t = y # bug #7818, save type before skip y = skipTypes(y, skipPtrs) - if ancestors.contains(y.id): return y + if ancestors.contains(y.id): + # bug #7818, defer the previous skipTypes + if k in {tyRef, tyPtr}: t = y + return t y = y.sons[0] type diff --git a/tests/array/t7818.nim b/tests/array/t7818.nim new file mode 100644 index 000000000..5d73efec5 --- /dev/null +++ b/tests/array/t7818.nim @@ -0,0 +1,45 @@ +discard """ + msg: '''BracketExpr + Sym "array" + Infix + Ident ".." + IntLit 0 + IntLit 2 + BracketExpr + Sym "Vehicle" + Sym "int" +--------- +BracketExpr + Sym "array" + Infix + Ident ".." + IntLit 0 + IntLit 2 + BracketExpr + Sym "Vehicle" + Sym "int" +---------''' +""" + +# bug #7818 +# this is not a macro bug, but array construction bug +# I use macro to avoid object slicing +# see #7712 and #7637 +import macros + +type + Vehicle[T] = object of RootObj + tire: T + Car[T] = object of Vehicle[T] + Bike[T] = object of Vehicle[T] + +macro peek(n: typed): untyped = + echo getTypeImpl(n).treeRepr + echo "---------" + +var v = Vehicle[int](tire: 3) +var c = Car[int](tire: 4) +var b = Bike[int](tire: 2) + +peek([c, b, v]) +peek([v, c, b]) From 2107c81d6d53af538eae2f1bec7d77b02f5a80af Mon Sep 17 00:00:00 2001 From: Koki Fushimi Date: Wed, 30 May 2018 23:29:34 +0900 Subject: [PATCH 10/31] Add inverse hyperbolic, and cot, sec and csc; and their hyperbolic, inverse, inverse hyperbolic, and change to use defined functions in C or JS for logs and hyperbolics. (#7893) * Add secant, cosecant and cotangent. * Add hyperbolic functions of cotangent, secant and cosecant. * Add inverse hyperbolic functions. * Change to use defined function of C and JS. * Bug fix and refactoring. * Add change to changelog.md --- changelog.md | 2 + lib/pure/math.nim | 157 ++++++++++++++++++++++++++++------------------ 2 files changed, 98 insertions(+), 61 deletions(-) diff --git a/changelog.md b/changelog.md index 27090a1df..316d5f9ea 100644 --- a/changelog.md +++ b/changelog.md @@ -58,6 +58,8 @@ e.g days to seconds. - Added the proc ``algorithm.binarySearch[T, K]`` with the ```cmp``` parameter. - Added the proc ``algorithm.upperBound``. +- Added inverse hyperbolic functions, ``math.arcsinh``, ``math.arccosh`` and ``math.arctanh`` procs. +- Added cotangent, secant and cosecant procs ``math.cot``, ``math.sec`` and ``math.csc``; and their hyperbolic, inverse and inverse hyperbolic functions, ``math.coth``, ``math.sech``, ``math.csch``, ``math.arccot``, ``math.arcsec``, ``math.arccsc``, ``math.arccoth``, ``math.arcsech`` and ``math.arccsch`` procs. ### Library changes diff --git a/lib/pure/math.nim b/lib/pure/math.nim index 49d87f007..fc49008b9 100644 --- a/lib/pure/math.nim +++ b/lib/pure/math.nim @@ -130,7 +130,7 @@ proc sum*[T](x: openArray[T]): T {.noSideEffect.} = for i in items(x): result = result + i {.push noSideEffect.} -when not defined(JS): +when not defined(JS): # C proc sqrt*(x: float32): float32 {.importc: "sqrtf", header: "".} proc sqrt*(x: float64): float64 {.importc: "sqrt", header: "".} ## Computes the square root of `x`. @@ -144,12 +144,33 @@ when not defined(JS): proc log10*(x: float32): float32 {.importc: "log10f", header: "".} proc log10*(x: float64): float64 {.importc: "log10", header: "".} ## Computes the common logarithm (base 10) of `x` - proc log2*[T: float32|float64](x: T): T = return ln(x) / ln(2.0) + proc log2*(x: float32): float32 {.importc: "log2f", header: "".} + proc log2*(x: float64): float64 {.importc: "log2", header: "".} ## Computes the binary logarithm (base 2) of `x` proc exp*(x: float32): float32 {.importc: "expf", header: "".} proc exp*(x: float64): float64 {.importc: "exp", header: "".} ## Computes the exponential function of `x` (pow(E, x)) + proc sin*(x: float32): float32 {.importc: "sinf", header: "".} + proc sin*(x: float64): float64 {.importc: "sin", header: "".} + ## Computes the sine of `x` + proc cos*(x: float32): float32 {.importc: "cosf", header: "".} + proc cos*(x: float64): float64 {.importc: "cos", header: "".} + ## Computes the cosine of `x` + proc tan*(x: float32): float32 {.importc: "tanf", header: "".} + proc tan*(x: float64): float64 {.importc: "tan", header: "".} + ## Computes the tangent of `x` + + proc sinh*(x: float32): float32 {.importc: "sinhf", header: "".} + proc sinh*(x: float64): float64 {.importc: "sinh", header: "".} + ## Computes the hyperbolic sine of `x` + proc cosh*(x: float32): float32 {.importc: "coshf", header: "".} + proc cosh*(x: float64): float64 {.importc: "cosh", header: "".} + ## Computes the hyperbolic cosine of `x` + proc tanh*(x: float32): float32 {.importc: "tanhf", header: "".} + proc tanh*(x: float64): float64 {.importc: "tanh", header: "".} + ## Computes the hyperbolic tangent of `x` + proc arccos*(x: float32): float32 {.importc: "acosf", header: "".} proc arccos*(x: float64): float64 {.importc: "acos", header: "".} ## Computes the arc cosine of `x` @@ -166,33 +187,80 @@ when not defined(JS): ## results even when the resulting angle is near pi/2 or -pi/2 ## (`x` near 0). - proc cos*(x: float32): float32 {.importc: "cosf", header: "".} - proc cos*(x: float64): float64 {.importc: "cos", header: "".} - ## Computes the cosine of `x` + proc arcsinh*(x: float32): float32 {.importc: "asinhf", header: "".} + proc arcsinh*(x: float64): float64 {.importc: "asinh", header: "".} + ## Computes the inverse hyperbolic sine of `x` + proc arccosh*(x: float32): float32 {.importc: "acoshf", header: "".} + proc arccosh*(x: float64): float64 {.importc: "acosh", header: "".} + ## Computes the inverse hyperbolic cosine of `x` + proc arctanh*(x: float32): float32 {.importc: "atanhf", header: "".} + proc arctanh*(x: float64): float64 {.importc: "atanh", header: "".} + ## Computes the inverse hyperbolic tangent of `x` - proc cosh*(x: float32): float32 {.importc: "coshf", header: "".} - proc cosh*(x: float64): float64 {.importc: "cosh", header: "".} - ## Computes the hyperbolic cosine of `x` +else: # JS + proc sqrt*(x: float32): float32 {.importc: "Math.sqrt", nodecl.} + proc sqrt*(x: float64): float64 {.importc: "Math.sqrt", nodecl.} + proc ln*(x: float32): float32 {.importc: "Math.log", nodecl.} + proc ln*(x: float64): float64 {.importc: "Math.log", nodecl.} + proc log10*(x: float32): float32 {.importc: "Math.log10", nodecl.} + proc log10*(x: float64): float64 {.importc: "Math.log10", nodecl.} + proc log2*(x: float32): float32 {.importc: "Math.log2", nodecl.} + proc log2*(x: float64): float64 {.importc: "Math.log2", nodecl.} + proc exp*(x: float32): float32 {.importc: "Math.exp", nodecl.} + proc exp*(x: float64): float64 {.importc: "Math.exp", nodecl.} + + proc sin*[T: float32|float64](x: T): T {.importc: "Math.sin", nodecl.} + proc cos*[T: float32|float64](x: T): T {.importc: "Math.cos", nodecl.} + proc tan*[T: float32|float64](x: T): T {.importc: "Math.tan", nodecl.} + + proc sinh*[T: float32|float64](x: T): T {.importc: "Math.sinh", nodecl.} + proc cosh*[T: float32|float64](x: T): T {.importc: "Math.cosh", nodecl.} + proc tanh*[T: float32|float64](x: T): T {.importc: "Math.tanh", nodecl.} + + proc arcsin*[T: float32|float64](x: T): T {.importc: "Math.asin", nodecl.} + proc arccos*[T: float32|float64](x: T): T {.importc: "Math.acos", nodecl.} + proc arctan*[T: float32|float64](x: T): T {.importc: "Math.atan", nodecl.} + proc arctan2*[T: float32|float64](y, x: T): T {.importC: "Math.atan2", nodecl.} + + proc arcsinh*[T: float32|float64](x: T): T {.importc: "Math.asinh", nodecl.} + proc arccosh*[T: float32|float64](x: T): T {.importc: "Math.acosh", nodecl.} + proc arctanh*[T: float32|float64](x: T): T {.importc: "Math.atanh", nodecl.} + +proc cot*[T: float32|float64](x: T): T = 1.0 / tan(x) + ## Computes the cotangent of `x` +proc sec*[T: float32|float64](x: T): T = 1.0 / cos(x) + ## Computes the secant of `x`. +proc csc*[T: float32|float64](x: T): T = 1.0 / sin(x) + ## Computes the cosecant of `x` + +proc coth*[T: float32|float64](x: T): T = 1.0 / tanh(x) + ## Computes the hyperbolic cotangent of `x` +proc sech*[T: float32|float64](x: T): T = 1.0 / cosh(x) + ## Computes the hyperbolic secant of `x` +proc csch*[T: float32|float64](x: T): T = 1.0 / sinh(x) + ## Computes the hyperbolic cosecant of `x` + +proc arccot*[T: float32|float64](x: T): T = arctan(1.0 / x) + ## Computes the inverse cotangent of `x` +proc arcsec*[T: float32|float64](x: T): T = arccos(1.0 / x) + ## Computes the inverse secant of `x` +proc arccsc*[T: float32|float64](x: T): T = arcsin(1.0 / x) + ## Computes the inverse cosecant of `x` + +proc arccoth*[T: float32|float64](x: T): T = arctanh(1.0 / x) + ## Computes the inverse hyperbolic cotangent of `x` +proc arcsech*[T: float32|float64](x: T): T = arccosh(1.0 / x) + ## Computes the inverse hyperbolic secant of `x` +proc arccsch*[T: float32|float64](x: T): T = arcsinh(1.0 / x) + ## Computes the inverse hyperbolic cosecant of `x` + +when not defined(JS): # C proc hypot*(x, y: float32): float32 {.importc: "hypotf", header: "".} proc hypot*(x, y: float64): float64 {.importc: "hypot", header: "".} ## Computes the hypotenuse of a right-angle triangle with `x` and ## `y` as its base and height. Equivalent to ``sqrt(x*x + y*y)``. - proc sinh*(x: float32): float32 {.importc: "sinhf", header: "".} - proc sinh*(x: float64): float64 {.importc: "sinh", header: "".} - ## Computes the hyperbolic sine of `x` - proc sin*(x: float32): float32 {.importc: "sinf", header: "".} - proc sin*(x: float64): float64 {.importc: "sin", header: "".} - ## Computes the sine of `x` - - proc tan*(x: float32): float32 {.importc: "tanf", header: "".} - proc tan*(x: float64): float64 {.importc: "tan", header: "".} - ## Computes the tangent of `x` - proc tanh*(x: float32): float32 {.importc: "tanhf", header: "".} - proc tanh*(x: float64): float64 {.importc: "tanh", header: "".} - ## Computes the hyperbolic tangent of `x` - proc pow*(x, y: float32): float32 {.importc: "powf", header: "".} proc pow*(x, y: float64): float64 {.importc: "pow", header: "".} ## computes x to power raised of y. @@ -302,50 +370,17 @@ when not defined(JS): ## .. code-block:: nim ## echo fmod(-2.5, 0.3) ## -0.1 -else: - proc trunc*(x: float32): float32 {.importc: "Math.trunc", nodecl.} - proc trunc*(x: float64): float64 {.importc: "Math.trunc", nodecl.} +else: # JS + proc hypot*[T: float32|float64](x, y: T): T = return sqrt(x*x + y*y) + proc pow*(x, y: float32): float32 {.importC: "Math.pow", nodecl.} + proc pow*(x, y: float64): float64 {.importc: "Math.pow", nodecl.} proc floor*(x: float32): float32 {.importc: "Math.floor", nodecl.} proc floor*(x: float64): float64 {.importc: "Math.floor", nodecl.} proc ceil*(x: float32): float32 {.importc: "Math.ceil", nodecl.} proc ceil*(x: float64): float64 {.importc: "Math.ceil", nodecl.} - - proc sqrt*(x: float32): float32 {.importc: "Math.sqrt", nodecl.} - proc sqrt*(x: float64): float64 {.importc: "Math.sqrt", nodecl.} - proc ln*(x: float32): float32 {.importc: "Math.log", nodecl.} - proc ln*(x: float64): float64 {.importc: "Math.log", nodecl.} - proc log10*[T: float32|float64](x: T): T = return ln(x) / ln(10.0) - proc log2*[T: float32|float64](x: T): T = return ln(x) / ln(2.0) - - proc exp*(x: float32): float32 {.importc: "Math.exp", nodecl.} - proc exp*(x: float64): float64 {.importc: "Math.exp", nodecl.} proc round0(x: float): float {.importc: "Math.round", nodecl.} - - proc pow*(x, y: float32): float32 {.importC: "Math.pow", nodecl.} - proc pow*(x, y: float64): float64 {.importc: "Math.pow", nodecl.} - - proc arccos*(x: float32): float32 {.importc: "Math.acos", nodecl.} - proc arccos*(x: float64): float64 {.importc: "Math.acos", nodecl.} - proc arcsin*(x: float32): float32 {.importc: "Math.asin", nodecl.} - proc arcsin*(x: float64): float64 {.importc: "Math.asin", nodecl.} - proc arctan*(x: float32): float32 {.importc: "Math.atan", nodecl.} - proc arctan*(x: float64): float64 {.importc: "Math.atan", nodecl.} - proc arctan2*(y, x: float32): float32 {.importC: "Math.atan2", nodecl.} - proc arctan2*(y, x: float64): float64 {.importc: "Math.atan2", nodecl.} - - proc cos*(x: float32): float32 {.importc: "Math.cos", nodecl.} - proc cos*(x: float64): float64 {.importc: "Math.cos", nodecl.} - proc cosh*(x: float32): float32 = return (exp(x)+exp(-x))*0.5 - proc cosh*(x: float64): float64 = return (exp(x)+exp(-x))*0.5 - proc hypot*[T: float32|float64](x, y: T): T = return sqrt(x*x + y*y) - proc sinh*[T: float32|float64](x: T): T = return (exp(x)-exp(-x))*0.5 - proc sin*(x: float32): float32 {.importc: "Math.sin", nodecl.} - proc sin*(x: float64): float64 {.importc: "Math.sin", nodecl.} - proc tan*(x: float32): float32 {.importc: "Math.tan", nodecl.} - proc tan*(x: float64): float64 {.importc: "Math.tan", nodecl.} - proc tanh*[T: float32|float64](x: T): T = - var y = exp(2.0*x) - return (y-1.0)/(y+1.0) + proc trunc*(x: float32): float32 {.importc: "Math.trunc", nodecl.} + proc trunc*(x: float64): float64 {.importc: "Math.trunc", nodecl.} proc round*[T: float32|float64](x: T, places: int = 0): T = ## Round a floating point number. From 65070a6936bd6954f3bc95015af49b3ba3b007b7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Oscar=20Nihlg=C3=A5rd?= Date: Wed, 30 May 2018 18:14:21 +0200 Subject: [PATCH 11/31] Use truncation division in mod for floats (#7118) * Use truncation division in mod for floats * Add changelog entry * Add floorDiv/floorMod to math.nim * Update changelog --- changelog.md | 4 ++++ lib/pure/math.nim | 51 +++++++++++++++++++++++++++++++++++++---------- 2 files changed, 44 insertions(+), 11 deletions(-) diff --git a/changelog.md b/changelog.md index 316d5f9ea..2bbfd01f6 100644 --- a/changelog.md +++ b/changelog.md @@ -39,6 +39,9 @@ The proc is no longer deprecated. - ``posix.Timeval.tv_sec`` has changed type to ``posix.Time``. +- ``math.`mod` `` for floats now behaves the same as ``mod`` for integers + (previously it used floor division like Python). Use ``math.floorMod`` for the old behavior. + #### Breaking changes in the compiler - The undocumented ``#? braces`` parsing mode was removed. @@ -60,6 +63,7 @@ - Added the proc ``algorithm.upperBound``. - Added inverse hyperbolic functions, ``math.arcsinh``, ``math.arccosh`` and ``math.arctanh`` procs. - Added cotangent, secant and cosecant procs ``math.cot``, ``math.sec`` and ``math.csc``; and their hyperbolic, inverse and inverse hyperbolic functions, ``math.coth``, ``math.sech``, ``math.csch``, ``math.arccot``, ``math.arcsec``, ``math.arccsc``, ``math.arccoth``, ``math.arcsech`` and ``math.arccsch`` procs. +- Added the procs ``math.floorMod`` and ``math.floorDiv`` for floor based integer division. ### Library changes diff --git a/lib/pure/math.nim b/lib/pure/math.nim index fc49008b9..e07e9a200 100644 --- a/lib/pure/math.nim +++ b/lib/pure/math.nim @@ -363,13 +363,16 @@ when not defined(JS): # C ## .. code-block:: nim ## echo trunc(PI) # 3.0 - proc fmod*(x, y: float32): float32 {.importc: "fmodf", header: "".} - proc fmod*(x, y: float64): float64 {.importc: "fmod", header: "".} + proc fmod*(x, y: float32): float32 {.deprecated, importc: "fmodf", header: "".} + proc fmod*(x, y: float64): float64 {.deprecated, importc: "fmod", header: "".} ## Computes the remainder of `x` divided by `y` ## ## .. code-block:: nim ## echo fmod(-2.5, 0.3) ## -0.1 + proc `mod`*(x, y: float32): float32 {.importc: "fmodf", header: "".} + proc `mod`*(x, y: float64): float64 {.importc: "fmod", header: "".} + ## Computes the modulo operation for float operators. else: # JS proc hypot*[T: float32|float64](x, y: T): T = return sqrt(x*x + y*y) proc pow*(x, y: float32): float32 {.importC: "Math.pow", nodecl.} @@ -382,6 +385,10 @@ else: # JS proc trunc*(x: float32): float32 {.importc: "Math.trunc", nodecl.} proc trunc*(x: float64): float64 {.importc: "Math.trunc", nodecl.} + proc `mod`*(x, y: float32): float32 {.importcpp: "# % #".} + proc `mod`*(x, y: float64): float64 {.importcpp: "# % #".} + ## Computes the modulo operation for float operators. + proc round*[T: float32|float64](x: T, places: int = 0): T = ## Round a floating point number. ## @@ -397,6 +404,21 @@ proc round*[T: float32|float64](x: T, places: int = 0): T = var mult = pow(10.0, places.T) result = round0(x*mult)/mult +proc floorDiv*[T: SomeInteger](x, y: T): T = + ## Floor division is conceptually defined as ``floor(x / y)``. + ## This is different from the ``div`` operator, which is defined + ## as ``trunc(x / y)``. That is, ``div`` rounds towards ``0`` and ``floorDiv`` + ## rounds down. + result = x div y + let r = x mod y + if (r > 0 and y < 0) or (r < 0 and y > 0): result.dec 1 + +proc floorMod*[T: SomeNumber](x, y: T): T = + ## Floor modulus is conceptually defined as ``x - (floorDiv(x, y) * y). + ## This proc behaves the same as the ``%`` operator in python. + result = x mod y + if (result > 0 and y < 0) or (result < 0 and y > 0): result += y + when not defined(JS): proc c_frexp*(x: float32, exponent: var int32): float32 {. importc: "frexp", header: "".} @@ -461,15 +483,6 @@ proc sgn*[T: SomeNumber](x: T): int {.inline.} = ## `NaN`. ord(T(0) < x) - ord(x < T(0)) -proc `mod`*[T: float32|float64](x, y: T): T = - ## Computes the modulo operation for float operators. Equivalent - ## to ``x - y * floor(x/y)``. Note that the remainder will always - ## have the same sign as the divisor. - ## - ## .. code-block:: nim - ## echo (4.0 mod -3.1) # -2.2 - result = if y == 0.0: x else: x - y * (x/y).floor - {.pop.} {.pop.} @@ -634,3 +647,19 @@ when isMainModule: doAssert fac(2) == 2 doAssert fac(3) == 6 doAssert fac(4) == 24 + + block: # floorMod/floorDiv + doAssert floorDiv(8, 3) == 2 + doAssert floorMod(8, 3) == 2 + + doAssert floorDiv(8, -3) == -3 + doAssert floorMod(8, -3) == -1 + + doAssert floorDiv(-8, 3) == -3 + doAssert floorMod(-8, 3) == 1 + + doAssert floorDiv(-8, -3) == 2 + doAssert floorMod(-8, -3) == -2 + + doAssert floorMod(8.0, -3.0) ==~ -1.0 + doAssert floorMod(-8.5, 3.0) ==~ 0.5 From 1bbd83de3f63aa898a4650cefc19b563abcc562a Mon Sep 17 00:00:00 2001 From: data-man Date: Wed, 30 May 2018 20:55:53 +0300 Subject: [PATCH 12/31] Removed spaces in math module --- lib/pure/math.nim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/pure/math.nim b/lib/pure/math.nim index e07e9a200..6be19a339 100644 --- a/lib/pure/math.nim +++ b/lib/pure/math.nim @@ -651,7 +651,7 @@ when isMainModule: block: # floorMod/floorDiv doAssert floorDiv(8, 3) == 2 doAssert floorMod(8, 3) == 2 - + doAssert floorDiv(8, -3) == -3 doAssert floorMod(8, -3) == -1 From 664b949d5438b42736a621547929b573f3eaeedb Mon Sep 17 00:00:00 2001 From: data-man Date: Wed, 30 May 2018 23:49:56 +0300 Subject: [PATCH 13/31] Better error messages in scanf. --- lib/pure/strscans.nim | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/lib/pure/strscans.nim b/lib/pure/strscans.nim index b0af149b5..11f182495 100644 --- a/lib/pure/strscans.nim +++ b/lib/pure/strscans.nim @@ -316,6 +316,9 @@ macro scanf*(input: string; pattern: static[string]; results: varargs[typed]): b conds.add resLen template at(s: string; i: int): char = (if i < s.len: s[i] else: '\0') + template matchError() = + error("type mismatch between pattern '$" & pattern[p] & "' (position: " & $p & ") and " & $getType(results[i]) & + " var '" & repr(results[i]) & "'") var i = 0 var p = 0 @@ -338,37 +341,37 @@ macro scanf*(input: string; pattern: static[string]; results: varargs[typed]): b if i < results.len and getType(results[i]).typeKind == ntyString: matchBind "parseIdent" else: - error("no string var given for $w") + matchError inc i of 'b': if i < results.len and getType(results[i]).typeKind == ntyInt: matchBind "parseBin" else: - error("no int var given for $b") + matchError inc i of 'o': if i < results.len and getType(results[i]).typeKind == ntyInt: matchBind "parseOct" else: - error("no int var given for $o") + matchError inc i of 'i': if i < results.len and getType(results[i]).typeKind == ntyInt: matchBind "parseInt" else: - error("no int var given for $i") + matchError inc i of 'h': if i < results.len and getType(results[i]).typeKind == ntyInt: matchBind "parseHex" else: - error("no int var given for $h") + matchError inc i of 'f': if i < results.len and getType(results[i]).typeKind == ntyFloat: matchBind "parseFloat" else: - error("no float var given for $f") + matchError inc i of 's': conds.add newCall(bindSym"inc", idx, newCall(bindSym"skipWhitespace", inp, idx)) @@ -392,7 +395,7 @@ macro scanf*(input: string; pattern: static[string]; results: varargs[typed]): b conds.add newCall(bindSym"!=", resLen, newLit min) conds.add resLen else: - error("no string var given for $" & pattern[p]) + matchError inc i of '{': inc p @@ -414,7 +417,7 @@ macro scanf*(input: string; pattern: static[string]; results: varargs[typed]): b conds.add newCall(bindSym"!=", resLen, newLit 0) conds.add resLen else: - error("no var given for $" & expr) + error("no var given for $" & expr & " (position: " & $p & ")") inc i of '[': inc p From 60d437427b9bb67f090fb28d38a0d6addb178c63 Mon Sep 17 00:00:00 2001 From: data-man Date: Thu, 31 May 2018 01:26:11 +0300 Subject: [PATCH 14/31] Added Telegram channel to readme. --- readme.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/readme.md b/readme.md index 44c92d06b..fa0ab3894 100644 --- a/readme.md +++ b/readme.md @@ -16,6 +16,8 @@ the latest release, check out [Nim's website][nim-site]. Also where most development decisions get made. * [Gitter][nim-gitter] - an additional place to discuss Nim in real-time. There is a bridge between Gitter and the IRC channel. +* [Telegram][nim-telegram] - an additional place to discuss Nim in real-time. There + is the official Telegram channel. * [Stack Overflow][nim-stackoverflow] - a popular Q/A site for programming related topics that includes posts about Nim. * [Github Wiki][nim-wiki] - Misc user-contributed content. @@ -192,6 +194,7 @@ Copyright © 2006-2017 Andreas Rumpf, all rights reserved. [nim-stackoverflow]: https://stackoverflow.com/questions/tagged/nim [nim-stackoverflow-newest]: https://stackoverflow.com/questions/tagged/nim?sort=newest&pageSize=15 [nim-gitter]: https://gitter.im/nim-lang/Nim +[nim-telegram]: https://t.me/nim_lang [nim-bountysource]: https://www.bountysource.com/teams/nim [nim-bitcoin]: https://blockchain.info/address/1BXfuKM2uvoD6mbx4g5xM3eQhLzkCK77tJ [nimble-repo]: https://github.com/nim-lang/nimble From a55f49a7b4e03bf9cd0f6538a7c34fb29db22bcc Mon Sep 17 00:00:00 2001 From: data-man Date: Thu, 31 May 2018 01:37:34 +0300 Subject: [PATCH 15/31] Years were updated. --- copying.txt | 2 +- readme.md | 2 +- readme.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/copying.txt b/copying.txt index 98b3e568f..d2bf9a7a7 100644 --- a/copying.txt +++ b/copying.txt @@ -1,7 +1,7 @@ ===================================================== Nim -- a Compiler for Nim. https://nim-lang.org/ -Copyright (C) 2006-2017 Andreas Rumpf. All rights reserved. +Copyright (C) 2006-2018 Andreas Rumpf. All rights reserved. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/readme.md b/readme.md index fa0ab3894..bdc7c7549 100644 --- a/readme.md +++ b/readme.md @@ -182,7 +182,7 @@ Nim. You are explicitly permitted to develop commercial applications using Nim. Please read the [copying.txt](copying.txt) file for more details. -Copyright © 2006-2017 Andreas Rumpf, all rights reserved. +Copyright © 2006-2018 Andreas Rumpf, all rights reserved. [nim-site]: https://nim-lang.org [nim-forum]: https://forum.nim-lang.org diff --git a/readme.txt b/readme.txt index 7f9f50da3..8c6f25b29 100644 --- a/readme.txt +++ b/readme.txt @@ -14,5 +14,5 @@ allowing you to create commercial applications. Read copying.txt for more details. -Copyright (c) 2006-2016 Andreas Rumpf. +Copyright (c) 2006-2018 Andreas Rumpf. All rights reserved. From eb8f15d97c2aba8e0bf56a4da151d919814c9745 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Oscar=20Nihlg=C3=A5rd?= Date: Thu, 31 May 2018 09:38:43 +0200 Subject: [PATCH 16/31] Support multi byte characters in module names (#7916) --- compiler/cgen.nim | 2 +- tests/misc/åäö.nim | 8 ++++++++ tests/testament/tester.nim | 2 +- 3 files changed, 10 insertions(+), 2 deletions(-) create mode 100644 tests/misc/åäö.nim diff --git a/compiler/cgen.nim b/compiler/cgen.nim index 79bcf0491..6a16474c0 100644 --- a/compiler/cgen.nim +++ b/compiler/cgen.nim @@ -1099,7 +1099,7 @@ proc getSomeInitName(m: PSym, suffix: string): Rope = if {sfSystemModule, sfMainModule} * m.flags == {}: result = m.owner.name.s.mangle.rope result.add "_" - result.add m.name.s + result.add m.name.s.mangle result.add suffix proc getInitName(m: PSym): Rope = diff --git a/tests/misc/åäö.nim b/tests/misc/åäö.nim new file mode 100644 index 000000000..69bb3e22c --- /dev/null +++ b/tests/misc/åäö.nim @@ -0,0 +1,8 @@ +discard """ + action: run +""" + +# Tests that module names can contain multi byte characters + +let a = 1 +doAssert åäö.a == 1 \ No newline at end of file diff --git a/tests/testament/tester.nim b/tests/testament/tester.nim index 0c6f376d3..0185156ec 100644 --- a/tests/testament/tester.nim +++ b/tests/testament/tester.nim @@ -79,7 +79,7 @@ proc nimcacheDir(filename, options: string, target: TTarget): string = proc callCompiler(cmdTemplate, filename, options: string, target: TTarget, extraOptions=""): TSpec = let nimcache = nimcacheDir(filename, options, target) - let options = options & " --nimCache:" & nimcache.quoteShell & extraOptions + let options = options & " " & ("--nimCache:" & nimcache).quoteShell & extraOptions let c = parseCmdLine(cmdTemplate % ["target", targetToCmd[target], "options", options, "file", filename.quoteShell, "filedir", filename.getFileDir()]) From a573577cdc7d334c88088058dc00f0771ce9bb39 Mon Sep 17 00:00:00 2001 From: Michael Voronin Date: Thu, 31 May 2018 15:50:18 +0300 Subject: [PATCH 17/31] [add+change] Added toDurationParts proc to convert Duration to array[FixedTimeUnit, int64] of it's human-readable parts, use it in `$Duration` --- lib/pure/times.nim | 75 ++++++++++++++++++++++++++++++++-------------- 1 file changed, 53 insertions(+), 22 deletions(-) diff --git a/lib/pure/times.nim b/lib/pure/times.nim index a6b6137e6..8eda73cc1 100644 --- a/lib/pure/times.nim +++ b/lib/pure/times.nim @@ -183,6 +183,9 @@ type ## The point in time represented by ``ZonedTime`` is ``adjTime + utcOffset.seconds``. isDst*: bool ## Determines whether DST is in effect. + DurationParts* = array[FixedTimeUnit, int64] # Array of Duration parts starts + + {.deprecated: [TMonth: Month, TWeekDay: WeekDay, TTime: Time, TTimeInterval: TimeInterval, TTimeInfo: DateTime, TimeInfo: DateTime].} @@ -451,44 +454,55 @@ const DurationZero* = initDuration() ## \ ## doAssert initDuration(seconds = 1) > DurationZero ## doAssert initDuration(seconds = 0) == DurationZero -proc `$`*(dur: Duration): string = - ## Human friendly string representation of ``dur``. +proc toParts*(dur: Duration): DurationParts = + ## Converts a duration into an array consisting of fixed time units. + ## + ## Each value in the array gives information about a specific unit of + ## time, for example ``result[Days]`` gives a count of days. + ## + ## This procedure is useful for converting ``Duration`` values to strings. runnableExamples: - doAssert $initDuration(seconds = 2) == "2 seconds" - doAssert $initDuration(weeks = 1, days = 2) == "1 week and 2 days" - doAssert $initDuration(hours = 1, minutes = 2, seconds = 3) == "1 hour, 2 minutes, and 3 seconds" - doAssert $initDuration(milliseconds = -1500) == "-1 second and -500 milliseconds" - var parts = newSeq[string]() + var dp = toParts(initDuration(weeks=2, days=1)) + doAssert dp[Days] == 1 + doAssert dp[Weeks] == 2 + dp = toParts(initDuration(days = -1)) + doAssert dp[Days] == -1 + var remS = dur.seconds var remNs = dur.nanosecond.int - # Normally ``nanoseconds`` should always be positive, but - # that makes no sense when printing. - if remS < 0: + # Ensure the same sign for seconds and nanoseconds + if remS < 0 and remNs != 0: remNs -= convert(Seconds, Nanoseconds, 1) remS.inc 1 - const unitStrings: array[FixedTimeUnit, string] = [ - "nanosecond", "microsecond", "millisecond", "second", "minute", "hour", "day", "week" - ] - for unit in countdown(Weeks, Seconds): let quantity = convert(Seconds, unit, remS) remS = remS mod convert(unit, Seconds, 1) - if quantity.abs == 1: - parts.add $quantity & " " & unitStrings[unit] - elif quantity != 0: - parts.add $quantity & " " & unitStrings[unit] & "s" + result[unit] = quantity for unit in countdown(Milliseconds, Nanoseconds): let quantity = convert(Nanoseconds, unit, remNs) remNs = remNs mod convert(unit, Nanoseconds, 1) - if quantity.abs == 1: - parts.add $quantity & " " & unitStrings[unit] - elif quantity != 0: - parts.add $quantity & " " & unitStrings[unit] & "s" + result[unit] = quantity + +proc stringifyUnit*(value: int | int64, unit: string): string = + ## Stringify time unit with it's name, lowercased + runnableExamples: + doAssert stringifyUnit(2, "Seconds") == "2 seconds" + doAssert stringifyUnit(1, "Years") == "1 year" + result = "" + result.add($value) + result.add(" ") + if abs(value) != 1: + result.add(unit.toLowerAscii()) + else: + result.add(unit[0..^2].toLowerAscii()) + +proc humanizeParts(parts: seq[string]): string = + ## Make date string parts human-readable result = "" if parts.len == 0: @@ -502,6 +516,23 @@ proc `$`*(dur: Duration): string = result.add part & ", " result.add "and " & parts[high(parts)] +proc `$`*(dur: Duration): string = + ## Human friendly string representation of ``Duration``. + runnableExamples: + doAssert $initDuration(seconds = 2) == "2 seconds" + doAssert $initDuration(weeks = 1, days = 2) == "1 week and 2 days" + doAssert $initDuration(hours = 1, minutes = 2, seconds = 3) == "1 hour, 2 minutes, and 3 seconds" + doAssert $initDuration(milliseconds = -1500) == "-1 second and -500 milliseconds" + var parts = newSeq[string]() + var numParts = toParts(dur) + + for unit in countdown(Weeks, Nanoseconds): + let quantity = numParts[unit] + if quantity != 0.int64: + parts.add(stringifyUnit(quantity, $unit)) + + result = humanizeParts(parts) + proc `+`*(a, b: Duration): Duration {.operator.} = ## Add two durations together. runnableExamples: From 11fcc83f8a1cc54965eba55dbfb52625199f651c Mon Sep 17 00:00:00 2001 From: Michael Voronin Date: Thu, 31 May 2018 15:30:12 +0300 Subject: [PATCH 18/31] [add] Add procs for TimeInterval stringify --- lib/pure/times.nim | 35 ++++++++++++++++++++++++++++++++++- 1 file changed, 34 insertions(+), 1 deletion(-) diff --git a/lib/pure/times.nim b/lib/pure/times.nim index 8eda73cc1..956d9716c 100644 --- a/lib/pure/times.nim +++ b/lib/pure/times.nim @@ -35,7 +35,7 @@ # of the standard library! import - strutils, parseutils + strutils, parseutils, algorithm include "system/inclrtl" @@ -184,6 +184,8 @@ type isDst*: bool ## Determines whether DST is in effect. DurationParts* = array[FixedTimeUnit, int64] # Array of Duration parts starts + TimeIntervalParts* = array[TimeUnit, int] # Array of Duration parts starts + {.deprecated: [TMonth: Month, TWeekDay: WeekDay, TTime: Time, @@ -1014,6 +1016,37 @@ proc `$`*(m: Month): string = "November", "December"] return lookup[m] + +proc toParts* (ti: TimeInterval): TimeIntervalParts = + ## Converts a `TimeInterval` into an array consisting of its time units, + ## starting with nanoseconds and ending with years + ## + ## This procedure is useful for converting ``TimeInterval`` values to strings. + ## E.g. then you need to implement custom interval printing + runnableExamples: + var tp = toParts(initTimeInterval(years=1, nanoseconds=123)) + doAssert tp[Years] == 1 + doAssert tp[Nanoseconds] == 123 + + var index = 0 + for name, value in fieldPairs(ti): + result[index.TimeUnit()] = value + index += 1 + +proc `$`*(ti: TimeInterval): string = + ## Get string representation of `TimeInterval` + runnableExamples: + doAssert $initTimeInterval(years=1, nanoseconds=123) == "1 year and 123 nanoseconds" + doAssert $initTimeInterval() == "0 nanoseconds" + + var parts: seq[string] = @[] + var tiParts = toParts(ti) + for unit in countdown(Years, Nanoseconds): + if tiParts[unit] != 0: + parts.add(stringifyUnit(tiParts[unit], $unit)) + + result = humanizeParts(parts) + proc nanoseconds*(nanos: int): TimeInterval {.inline.} = ## TimeInterval of ``nanos`` nanoseconds. initTimeInterval(nanoseconds = nanos) From 8aeb24b4fde99d8667dc46a89bbd3e84b1860419 Mon Sep 17 00:00:00 2001 From: Michael Voronin Date: Tue, 24 Apr 2018 17:49:46 +0300 Subject: [PATCH 19/31] [add] add between function to calculate TI between two datetimes --- lib/pure/times.nim | 103 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 103 insertions(+) diff --git a/lib/pure/times.nim b/lib/pure/times.nim index 956d9716c..5915f11bb 100644 --- a/lib/pure/times.nim +++ b/lib/pure/times.nim @@ -1248,6 +1248,7 @@ proc `==`*(a, b: DateTime): bool = ## Returns true if ``a == b``, that is if both dates represent the same point in datetime. return a.toTime == b.toTime + proc isStaticInterval(interval: TimeInterval): bool = interval.years == 0 and interval.months == 0 and interval.days == 0 and interval.weeks == 0 @@ -1261,6 +1262,108 @@ proc evaluateStaticInterval(interval: TimeInterval): Duration = minutes = interval.minutes, hours = interval.hours) +proc between*(startDt, endDt:DateTime): TimeInterval = + ## Evaluate difference between two dates in ``TimeInterval`` format, so, it + ## will be relative. + ## + ## **Warning:** It's not recommended to use ``between`` for ``DateTime's`` in + ## different ``TimeZone's``. + ## ``a + between(a, b) == b`` is only guaranteed when ``a`` and ``b`` are in UTC. + runnableExamples: + var a = initDateTime(year = 2018, month = Month(3), monthday = 25, + hour = 0, minute = 59, second = 59, nanosecond = 1, + zone = utc()).local + var b = initDateTime(year = 2018, month = Month(3), monthday = 25, + hour = 1, minute = 1, second = 1, nanosecond = 0, + zone = utc()).local + doAssert between(a, b) == initTimeInterval( + nanoseconds=999, milliseconds=999, microseconds=999, seconds=1, minutes=1) + + a = parse("2018-01-09T00:00:00+00:00", "yyyy-MM-dd'T'HH:mm:sszzz", utc()) + b = parse("2018-01-10T23:00:00-02:00", "yyyy-MM-dd'T'HH:mm:sszzz") + doAssert between(a, b) == initTimeInterval(hours=1, days=2) + ## Though, here correct answer should be 1 day 25 hours (cause this day in + ## this tz is actually 26 hours). That's why operating different TZ is + ## discouraged + + var startDt = startDt.utc() + var endDt = endDt.utc() + + if endDt == startDt: + return initTimeInterval() + elif endDt < startDt: + return -between(endDt, startDt) + + var coeffs: array[FixedTimeUnit, int64] = unitWeights + var timeParts: array[FixedTimeUnit, int] + for unit in Nanoseconds..Weeks: + timeParts[unit] = 0 + + for unit in Seconds..Days: + coeffs[unit] = coeffs[unit] div unitWeights[Seconds] + + var startTimepart = initTime( + nanosecond = startDt.nanosecond, + unix = startDt.hour * coeffs[Hours] + startDt.minute * coeffs[Minutes] + + startDt.second + ) + var endTimepart = initTime( + nanosecond = endDt.nanosecond, + unix = endDt.hour * coeffs[Hours] + endDt.minute * coeffs[Minutes] + + endDt.second + ) + # We wand timeParts for Seconds..Hours be positive, so we'll borrow one day + if endTimepart < startTimepart: + timeParts[Days] = -1 + + let diffTime = endTimepart - startTimepart + timeParts[Seconds] = diffTime.seconds.int() + #Nanoseconds - preliminary count + timeParts[Nanoseconds] = diffTime.nanoseconds + for unit in countdown(Milliseconds, Microseconds): + timeParts[unit] += timeParts[Nanoseconds] div coeffs[unit].int() + timeParts[Nanoseconds] -= timeParts[unit] * coeffs[unit].int() + + #Counting Seconds .. Hours - final, Days - preliminary + for unit in countdown(Days, Minutes): + timeParts[unit] += timeParts[Seconds] div coeffs[unit].int() + # Here is accounted the borrowed day + timeParts[Seconds] -= timeParts[unit] * coeffs[unit].int() + + # Set Nanoseconds .. Hours in result + result.nanoseconds = timeParts[Nanoseconds] + result.microseconds = timeParts[Microseconds] + result.milliseconds = timeParts[Milliseconds] + result.seconds = timeParts[Seconds] + result.minutes = timeParts[Minutes] + result.hours = timeParts[Hours] + + #Days + if endDt.monthday.int + timeParts[Days] < startDt.monthday.int(): + if endDt.month > 1.Month: + endDt.month -= 1.Month + else: + endDt.month = 12.Month + endDt.year -= 1 + timeParts[Days] += endDt.monthday.int() + getDaysInMonth( + endDt.month, endDt.year) - startDt.monthday.int() + else: + timeParts[Days] += endDt.monthday.int() - + startDt.monthday.int() + + result.days = timeParts[Days] + + #Months + if endDt.month < startDt.month: + result.months = endDt.month.int() + 12 - startDt.month.int() + endDt.year -= 1 + else: + result.months = endDt.month.int() - + startDt.month.int() + + # Years + result.years = endDt.year - startDt.year + proc `+`*(time: Time, interval: TimeInterval): Time = ## Adds `interval` to `time`. ## If `interval` contains any years, months, weeks or days the operation From 5da74d5979dc125e0e6eba1e9a00ad346593bd7f Mon Sep 17 00:00:00 2001 From: Michael Voronin Date: Thu, 10 May 2018 13:02:38 +0300 Subject: [PATCH 20/31] [add] Add fraction of seconds to parse/format utils --- lib/pure/times.nim | 135 +++++++++++++++++++++++++-------------------- 1 file changed, 75 insertions(+), 60 deletions(-) diff --git a/lib/pure/times.nim b/lib/pure/times.nim index 5915f11bb..60b362665 100644 --- a/lib/pure/times.nim +++ b/lib/pure/times.nim @@ -35,7 +35,7 @@ # of the standard library! import - strutils, parseutils, algorithm + strutils, parseutils, algorithm, math include "system/inclrtl" @@ -1530,6 +1530,12 @@ proc formatToken(dt: DateTime, token: string, buf: var string) = buf.add(':') if minutes < 10: buf.add('0') buf.add($minutes) + of "fff": + buf.add(intToStr(convert(Nanoseconds, Milliseconds, dt.nanosecond), 3)) + of "ffffff": + buf.add(intToStr(convert(Nanoseconds, Microseconds, dt.nanosecond), 6)) + of "fffffffff": + buf.add(intToStr(dt.nanosecond, 9)) of "": discard else: @@ -1540,34 +1546,37 @@ proc format*(dt: DateTime, f: string): string {.tags: [].}= ## This procedure formats `dt` as specified by `f`. The following format ## specifiers are available: ## - ## ========== ================================================================================= ================================================ - ## Specifier Description Example - ## ========== ================================================================================= ================================================ - ## d Numeric value of the day of the month, it will be one or two digits long. ``1/04/2012 -> 1``, ``21/04/2012 -> 21`` - ## dd Same as above, but always two digits. ``1/04/2012 -> 01``, ``21/04/2012 -> 21`` - ## ddd Three letter string which indicates the day of the week. ``Saturday -> Sat``, ``Monday -> Mon`` - ## dddd Full string for the day of the week. ``Saturday -> Saturday``, ``Monday -> Monday`` - ## h The hours in one digit if possible. Ranging from 0-12. ``5pm -> 5``, ``2am -> 2`` - ## hh The hours in two digits always. If the hour is one digit 0 is prepended. ``5pm -> 05``, ``11am -> 11`` - ## H The hours in one digit if possible, randing from 0-24. ``5pm -> 17``, ``2am -> 2`` - ## HH The hours in two digits always. 0 is prepended if the hour is one digit. ``5pm -> 17``, ``2am -> 02`` - ## m The minutes in 1 digit if possible. ``5:30 -> 30``, ``2:01 -> 1`` - ## mm Same as above but always 2 digits, 0 is prepended if the minute is one digit. ``5:30 -> 30``, ``2:01 -> 01`` - ## M The month in one digit if possible. ``September -> 9``, ``December -> 12`` - ## MM The month in two digits always. 0 is prepended. ``September -> 09``, ``December -> 12`` - ## MMM Abbreviated three-letter form of the month. ``September -> Sep``, ``December -> Dec`` - ## MMMM Full month string, properly capitalized. ``September -> September`` - ## s Seconds as one digit if possible. ``00:00:06 -> 6`` - ## ss Same as above but always two digits. 0 is prepended. ``00:00:06 -> 06`` - ## t ``A`` when time is in the AM. ``P`` when time is in the PM. - ## tt Same as above, but ``AM`` and ``PM`` instead of ``A`` and ``P`` respectively. - ## y(yyyy) This displays the year to different digits. You most likely only want 2 or 4 'y's - ## yy Displays the year to two digits. ``2012 -> 12`` - ## yyyy Displays the year to four digits. ``2012 -> 2012`` - ## z Displays the timezone offset from UTC. ``GMT+7 -> +7``, ``GMT-5 -> -5`` - ## zz Same as above but with leading 0. ``GMT+7 -> +07``, ``GMT-5 -> -05`` - ## zzz Same as above but with ``:mm`` where *mm* represents minutes. ``GMT+7 -> +07:00``, ``GMT-5 -> -05:00`` - ## ========== ================================================================================= ================================================ + ## ============ ================================================================================= ================================================ + ## Specifier Description Example + ## ============ ================================================================================= ================================================ + ## d Numeric value of the day of the month, it will be one or two digits long. ``1/04/2012 -> 1``, ``21/04/2012 -> 21`` + ## dd Same as above, but always two digits. ``1/04/2012 -> 01``, ``21/04/2012 -> 21`` + ## ddd Three letter string which indicates the day of the week. ``Saturday -> Sat``, ``Monday -> Mon`` + ## dddd Full string for the day of the week. ``Saturday -> Saturday``, ``Monday -> Monday`` + ## h The hours in one digit if possible. Ranging from 0-12. ``5pm -> 5``, ``2am -> 2`` + ## hh The hours in two digits always. If the hour is one digit 0 is prepended. ``5pm -> 05``, ``11am -> 11`` + ## H The hours in one digit if possible, randing from 0-24. ``5pm -> 17``, ``2am -> 2`` + ## HH The hours in two digits always. 0 is prepended if the hour is one digit. ``5pm -> 17``, ``2am -> 02`` + ## m The minutes in 1 digit if possible. ``5:30 -> 30``, ``2:01 -> 1`` + ## mm Same as above but always 2 digits, 0 is prepended if the minute is one digit. ``5:30 -> 30``, ``2:01 -> 01`` + ## M The month in one digit if possible. ``September -> 9``, ``December -> 12`` + ## MM The month in two digits always. 0 is prepended. ``September -> 09``, ``December -> 12`` + ## MMM Abbreviated three-letter form of the month. ``September -> Sep``, ``December -> Dec`` + ## MMMM Full month string, properly capitalized. ``September -> September`` + ## s Seconds as one digit if possible. ``00:00:06 -> 6`` + ## ss Same as above but always two digits. 0 is prepended. ``00:00:06 -> 06`` + ## t ``A`` when time is in the AM. ``P`` when time is in the PM. + ## tt Same as above, but ``AM`` and ``PM`` instead of ``A`` and ``P`` respectively. + ## y(yyyy) This displays the year to different digits. You most likely only want 2 or 4 'y's + ## yy Displays the year to two digits. ``2012 -> 12`` + ## yyyy Displays the year to four digits. ``2012 -> 2012`` + ## z Displays the timezone offset from UTC. ``GMT+7 -> +7``, ``GMT-5 -> -5`` + ## zz Same as above but with leading 0. ``GMT+7 -> +07``, ``GMT-5 -> -05`` + ## zzz Same as above but with ``:mm`` where *mm* represents minutes. ``GMT+7 -> +07:00``, ``GMT-5 -> -05:00`` + ## fff Milliseconds display ``1000000 nanoseconds -> 1`` + ## ffffff Microseconds display ``1000000 nanoseconds -> 1000`` + ## fffffffff Nanoseconds display ``1000000 nanoseconds -> 1000000`` + ## ============ ================================================================================= ================================================ ## ## Other strings can be inserted by putting them in ``''``. For example ## ``hh'->'mm`` will give ``01->56``. The following characters can be @@ -1575,8 +1584,8 @@ proc format*(dt: DateTime, f: string): string {.tags: [].}= ## ``,``. However you don't need to necessarily separate format specifiers, a ## unambiguous format string like ``yyyyMMddhhmmss`` is valid too. runnableExamples: - let dt = initDateTime(01, mJan, 2000, 12, 00, 00, utc()) - doAssert format(dt, "yyyy-MM-dd'T'HH:mm:sszzz") == "2000-01-01T12:00:00+00:00" + let dt = initDateTime(01, mJan, 2000, 12, 00, 00, 01, utc()) + doAssert format(dt, "yyyy-MM-dd'T'HH:mm:ss'.'fffffffffzzz") == "2000-01-01T12:00:00.000000001+00:00" result = "" var i = 0 @@ -1853,6 +1862,11 @@ proc parseToken(dt: var DateTime; token, value: string; j: var int) = j += 4 dt.utcOffset += factor * value[j..j+1].parseInt() * 60 j += 2 + of "fff", "ffffff", "fffffffff": + var numStr = "" + let n = parseWhile(value[j..len(value) - 1], numStr, {'0'..'9'}) + dt.nanosecond = parseInt(numStr) * (10 ^ (9 - n)) + j += n else: # Ignore the token and move forward in the value string by the same length j += token.len @@ -1866,33 +1880,34 @@ proc parse*(value, layout: string, zone: Timezone = local()): DateTime = ## parsed, then the input will be assumed to be specified in the `zone` timezone ## already, so no timezone conversion will be done in that case. ## - ## ========== ================================================================================= ================================================ - ## Specifier Description Example - ## ========== ================================================================================= ================================================ - ## d Numeric value of the day of the month, it will be one or two digits long. ``1/04/2012 -> 1``, ``21/04/2012 -> 21`` - ## dd Same as above, but always two digits. ``1/04/2012 -> 01``, ``21/04/2012 -> 21`` - ## ddd Three letter string which indicates the day of the week. ``Saturday -> Sat``, ``Monday -> Mon`` - ## dddd Full string for the day of the week. ``Saturday -> Saturday``, ``Monday -> Monday`` - ## h The hours in one digit if possible. Ranging from 0-12. ``5pm -> 5``, ``2am -> 2`` - ## hh The hours in two digits always. If the hour is one digit 0 is prepended. ``5pm -> 05``, ``11am -> 11`` - ## H The hours in one digit if possible, randing from 0-24. ``5pm -> 17``, ``2am -> 2`` - ## HH The hours in two digits always. 0 is prepended if the hour is one digit. ``5pm -> 17``, ``2am -> 02`` - ## m The minutes in 1 digit if possible. ``5:30 -> 30``, ``2:01 -> 1`` - ## mm Same as above but always 2 digits, 0 is prepended if the minute is one digit. ``5:30 -> 30``, ``2:01 -> 01`` - ## M The month in one digit if possible. ``September -> 9``, ``December -> 12`` - ## MM The month in two digits always. 0 is prepended. ``September -> 09``, ``December -> 12`` - ## MMM Abbreviated three-letter form of the month. ``September -> Sep``, ``December -> Dec`` - ## MMMM Full month string, properly capitalized. ``September -> September`` - ## s Seconds as one digit if possible. ``00:00:06 -> 6`` - ## ss Same as above but always two digits. 0 is prepended. ``00:00:06 -> 06`` - ## t ``A`` when time is in the AM. ``P`` when time is in the PM. - ## tt Same as above, but ``AM`` and ``PM`` instead of ``A`` and ``P`` respectively. - ## yy Displays the year to two digits. ``2012 -> 12`` - ## yyyy Displays the year to four digits. ``2012 -> 2012`` - ## z Displays the timezone offset from UTC. ``Z`` is parsed as ``+0`` ``GMT+7 -> +7``, ``GMT-5 -> -5`` - ## zz Same as above but with leading 0. ``GMT+7 -> +07``, ``GMT-5 -> -05`` - ## zzz Same as above but with ``:mm`` where *mm* represents minutes. ``GMT+7 -> +07:00``, ``GMT-5 -> -05:00`` - ## ========== ================================================================================= ================================================ + ## ======================= ================================================================================= ================================================ + ## Specifier Description Example + ## ======================= ================================================================================= ================================================ + ## d Numeric value of the day of the month, it will be one or two digits long. ``1/04/2012 -> 1``, ``21/04/2012 -> 21`` + ## dd Same as above, but always two digits. ``1/04/2012 -> 01``, ``21/04/2012 -> 21`` + ## ddd Three letter string which indicates the day of the week. ``Saturday -> Sat``, ``Monday -> Mon`` + ## dddd Full string for the day of the week. ``Saturday -> Saturday``, ``Monday -> Monday`` + ## h The hours in one digit if possible. Ranging from 0-12. ``5pm -> 5``, ``2am -> 2`` + ## hh The hours in two digits always. If the hour is one digit 0 is prepended. ``5pm -> 05``, ``11am -> 11`` + ## H The hours in one digit if possible, randing from 0-24. ``5pm -> 17``, ``2am -> 2`` + ## HH The hours in two digits always. 0 is prepended if the hour is one digit. ``5pm -> 17``, ``2am -> 02`` + ## m The minutes in 1 digit if possible. ``5:30 -> 30``, ``2:01 -> 1`` + ## mm Same as above but always 2 digits, 0 is prepended if the minute is one digit. ``5:30 -> 30``, ``2:01 -> 01`` + ## M The month in one digit if possible. ``September -> 9``, ``December -> 12`` + ## MM The month in two digits always. 0 is prepended. ``September -> 09``, ``December -> 12`` + ## MMM Abbreviated three-letter form of the month. ``September -> Sep``, ``December -> Dec`` + ## MMMM Full month string, properly capitalized. ``September -> September`` + ## s Seconds as one digit if possible. ``00:00:06 -> 6`` + ## ss Same as above but always two digits. 0 is prepended. ``00:00:06 -> 06`` + ## t ``A`` when time is in the AM. ``P`` when time is in the PM. + ## tt Same as above, but ``AM`` and ``PM`` instead of ``A`` and ``P`` respectively. + ## yy Displays the year to two digits. ``2012 -> 12`` + ## yyyy Displays the year to four digits. ``2012 -> 2012`` + ## z Displays the timezone offset from UTC. ``Z`` is parsed as ``+0`` ``GMT+7 -> +7``, ``GMT-5 -> -5`` + ## zz Same as above but with leading 0. ``GMT+7 -> +07``, ``GMT-5 -> -05`` + ## zzz Same as above but with ``:mm`` where *mm* represents minutes. ``GMT+7 -> +07:00``, ``GMT-5 -> -05:00`` + ## fff/ffffff/fffffffff for consistency with format - nanoseconds ``1 -> 1 nanosecond`` + ## ======================= ================================================================================= ================================================ ## ## Other strings can be inserted by putting them in ``''``. For example ## ``hh'->'mm`` will give ``01->56``. The following characters can be @@ -1900,8 +1915,8 @@ proc parse*(value, layout: string, zone: Timezone = local()): DateTime = ## ``,``. However you don't need to necessarily separate format specifiers, a ## unambiguous format string like ``yyyyMMddhhmmss`` is valid too. runnableExamples: - let tStr = "1970-01-01T00:00:00+00:00" - doAssert parse(tStr, "yyyy-MM-dd'T'HH:mm:sszzz") == fromUnix(0).utc + let tStr = "1970-01-01T00:00:00.0+00:00" + doAssert parse(tStr, "yyyy-MM-dd'T'HH:mm:ss.fffzzz") == fromUnix(0).utc var i = 0 # pointer for format string var j = 0 # pointer for value string From 637ee7a1516a4ee8bacf6745f0df4852616cff7a Mon Sep 17 00:00:00 2001 From: Torro Date: Fri, 1 Jun 2018 16:20:30 +0200 Subject: [PATCH 21/31] Add note to import "std/sha1" to sha1.nim (doc) --- lib/std/sha1.nim | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/std/sha1.nim b/lib/std/sha1.nim index b18095ff6..c0b1bffcf 100644 --- a/lib/std/sha1.nim +++ b/lib/std/sha1.nim @@ -7,6 +7,8 @@ # distribution, for details about the copyright. # +## Note: Import ``std/sha1`` to use this module + import strutils const Sha1DigestSize = 20 From 3027ca292c3fbdb5387d0b8834c7e3dc960df75d Mon Sep 17 00:00:00 2001 From: Koki Fushimi Date: Sat, 2 Jun 2018 00:19:25 +0900 Subject: [PATCH 22/31] Support `div`, `mod`, floorDiv and floorMod for Rationals (#7918) * Support `div`, `mod`, floorDiv and floorMod for Ratinoals. * Bug fix and add tests. * Update changelog --- changelog.md | 1 + lib/pure/rationals.nim | 36 ++++++++++++++++++++++++++++++++++++ 2 files changed, 37 insertions(+) diff --git a/changelog.md b/changelog.md index 2bbfd01f6..514bfbaca 100644 --- a/changelog.md +++ b/changelog.md @@ -64,6 +64,7 @@ - Added inverse hyperbolic functions, ``math.arcsinh``, ``math.arccosh`` and ``math.arctanh`` procs. - Added cotangent, secant and cosecant procs ``math.cot``, ``math.sec`` and ``math.csc``; and their hyperbolic, inverse and inverse hyperbolic functions, ``math.coth``, ``math.sech``, ``math.csch``, ``math.arccot``, ``math.arcsec``, ``math.arccsc``, ``math.arccoth``, ``math.arcsech`` and ``math.arccsch`` procs. - Added the procs ``math.floorMod`` and ``math.floorDiv`` for floor based integer division. +- Added the procs ``rationals.`div```, ``rationals.`mod```, ``rationals.floorDiv`` and ``rationals.floorMod`` for rationals. ### Library changes diff --git a/lib/pure/rationals.nim b/lib/pure/rationals.nim index 7907b4e6c..3946cf85b 100644 --- a/lib/pure/rationals.nim +++ b/lib/pure/rationals.nim @@ -241,6 +241,33 @@ proc abs*[T](x: Rational[T]): Rational[T] = result.num = abs x.num result.den = abs x.den +proc `div`*[T: SomeInteger](x, y: Rational[T]): T = + ## Computes the rational truncated division. + (x.num * y.den) div (y.num * x.den) + +proc `mod`*[T: SomeInteger](x, y: Rational[T]): Rational[T] = + ## Computes the rational modulo by truncated division (remainder). + ## This is same as ``x - (x div y) * y``. + result = ((x.num * y.den) mod (y.num * x.den)) // (x.den * y.den) + reduce(result) + +proc floorDiv*[T: SomeInteger](x, y: Rational[T]): T = + ## Computes the rational floor division. + ## + ## Floor division is conceptually defined as ``floor(x / y)``. + ## This is different from the ``div`` operator, which is defined + ## as ``trunc(x / y)``. That is, ``div`` rounds towards ``0`` and ``floorDiv`` + ## rounds down. + floorDiv(x.num * y.den, y.num * x.den) + +proc floorMod*[T: SomeInteger](x, y: Rational[T]): Rational[T] = + ## Computes the rational modulo by floor division (modulo). + ## + ## This is same as ``x - floorDiv(x, y) * y``. + ## This proc behaves the same as the ``%`` operator in python. + result = floorMod(x.num * y.den, y.num * x.den) // (x.den * y.den) + reduce(result) + proc hash*[T](x: Rational[T]): Hash = ## Computes hash for rational `x` # reduce first so that hash(x) == hash(y) for x == y @@ -339,3 +366,12 @@ when isMainModule: assert toRational(0.33) == 33 // 100 assert toRational(0.22) == 11 // 50 assert toRational(10.0) == 10 // 1 + + assert (1//1) div (3//10) == 3 + assert (-1//1) div (3//10) == -3 + assert (3//10) mod (1//1) == 3//10 + assert (-3//10) mod (1//1) == -3//10 + assert floorDiv(1//1, 3//10) == 3 + assert floorDiv(-1//1, 3//10) == -4 + assert floorMod(3//10, 1//1) == 3//10 + assert floorMod(-3//10, 1//1) == 7//10 From 3acedd4cd95e75f04714590f5dbd5876e3a936e8 Mon Sep 17 00:00:00 2001 From: Koki Fushimi Date: Sat, 2 Jun 2018 00:20:28 +0900 Subject: [PATCH 23/31] Add imaginary unit. (#7922) --- lib/pure/complex.nim | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/lib/pure/complex.nim b/lib/pure/complex.nim index 98cab1a5a..ba5c571ce 100644 --- a/lib/pure/complex.nim +++ b/lib/pure/complex.nim @@ -25,6 +25,10 @@ type Complex* = tuple[re, im: float] ## a complex number, consisting of a real and an imaginary part +const + im*: Complex = (re: 0.0, im: 1.0) + ## The imaginary unit. √-1. + proc toComplex*(x: SomeInteger): Complex = ## Convert some integer ``x`` to a complex number. result.re = x From 8557a2e1a510d0e195af7b93977bb9866a45b796 Mon Sep 17 00:00:00 2001 From: Chris McIntyre Date: Fri, 1 Jun 2018 08:24:49 -0700 Subject: [PATCH 24/31] Fixed operator precedence in example (#7912) The order of operations was incorrect for the right-hand side of the `assert` statement on line 3199, based on the operator precedence for line 3197. --- doc/manual.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/manual.rst b/doc/manual.rst index 95af86a6a..d0a778bdd 100644 --- a/doc/manual.rst +++ b/doc/manual.rst @@ -3196,7 +3196,7 @@ notation. (Thus an operator can have more than two parameters): # Multiply and add result = a * b + c - assert `*+`(3, 4, 6) == `*`(a, `+`(b, c)) + assert `*+`(3, 4, 6) == `+`(`*`(a, b), c) Export marker From cb87bba82fa81240ea449e13a99280ce134096db Mon Sep 17 00:00:00 2001 From: Federico Ceratto Date: Fri, 1 Jun 2018 16:28:20 +0100 Subject: [PATCH 25/31] Update example (#7788) --- lib/impure/db_sqlite.nim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/impure/db_sqlite.nim b/lib/impure/db_sqlite.nim index b1541c1ba..6b8ca4881 100644 --- a/lib/impure/db_sqlite.nim +++ b/lib/impure/db_sqlite.nim @@ -31,7 +31,7 @@ ## ## .. code-block:: Nim ## import db_sqlite -## let db = open("localhost", "user", "password", "dbname") +## let db = open("mytest.db", nil, nil, nil) # user, password, database name can be nil ## db.close() ## ## Creating a table From b4626a220b2de0fee7360672915332c402bf9dc7 Mon Sep 17 00:00:00 2001 From: data-man Date: Fri, 1 Jun 2018 21:28:23 +0300 Subject: [PATCH 26/31] Added $ for openarrays. Fixes #7940. --- lib/system.nim | 8 ++++++++ tests/system/toString.nim | 1 + 2 files changed, 9 insertions(+) diff --git a/lib/system.nim b/lib/system.nim index 06c4f103b..13f16903a 100644 --- a/lib/system.nim +++ b/lib/system.nim @@ -3443,6 +3443,14 @@ when not defined(nimNoArrayToString): ## generic ``$`` operator for arrays that is lifted from the components collectionToString(x, "[", ", ", "]") +proc `$`*[T](x: openarray[T]): string = + ## generic ``$`` operator for openarrays that is lifted from the components + ## of `x`. Example: + ## + ## .. code-block:: nim + ## $(@[23, 45].toOpenArray(0, 1)) == "[23, 45]" + collectionToString(x, "[", ", ", "]") + proc quit*(errormsg: string, errorcode = QuitFailure) {.noReturn.} = ## a shorthand for ``echo(errormsg); quit(errorcode)``. echo(errormsg) diff --git a/tests/system/toString.nim b/tests/system/toString.nim index 37c678a74..ea10f998c 100644 --- a/tests/system/toString.nim +++ b/tests/system/toString.nim @@ -6,6 +6,7 @@ doAssert "@[23, 45]" == $(@[23, 45]) doAssert "[32, 45]" == $([32, 45]) doAssert """@["", "foo", "bar"]""" == $(@["", "foo", "bar"]) doAssert """["", "foo", "bar"]""" == $(["", "foo", "bar"]) +doAssert """["", "foo", "bar"]""" == $(@["", "foo", "bar"].toOpenArray(0, 2)) # bug #2395 let alphaSet: set[char] = {'a'..'c'} From 07ff9940f4a990b57cfaa3ad274c7bcff46356ff Mon Sep 17 00:00:00 2001 From: skilchen Date: Sat, 2 Jun 2018 04:23:50 +0200 Subject: [PATCH 27/31] fix strformat zeropadding for floats (#7934) --- lib/pure/strformat.nim | 22 ++++++++++++++++++++-- tests/stdlib/tstrformat.nim | 11 ++++++++++- 2 files changed, 30 insertions(+), 3 deletions(-) diff --git a/lib/pure/strformat.nim b/lib/pure/strformat.nim index a8b128460..12a102c9f 100644 --- a/lib/pure/strformat.nim +++ b/lib/pure/strformat.nim @@ -524,8 +524,26 @@ proc format*(value: SomeFloat; specifier: string; res: var string) = " of 'e', 'E', 'f', 'F', 'g', 'G' but got: " & spec.typ) var f = formatBiggestFloat(value, fmode, spec.precision) - if value >= 0.0 and spec.sign != '-': - f = spec.sign & f + var sign = false + if value >= 0.0: + if spec.sign != '-': + f = spec.sign & f + sign = true + else: + sign = true + + if spec.padWithZero: + var sign_str = "" + if sign: + sign_str = $f[0] + f = f[1..^1] + + let toFill = spec.minimumWidth - f.len - ord(sign) + if toFill > 0: + f = repeat('0', toFill) & f + if sign: + f = sign_str & f + # the default for numbers is right-alignment: let align = if spec.align == '\0': '>' else: spec.align let result = alignString(f, spec.minimumWidth, diff --git a/tests/stdlib/tstrformat.nim b/tests/stdlib/tstrformat.nim index 4e5c614a7..b4cbd41e0 100644 --- a/tests/stdlib/tstrformat.nim +++ b/tests/stdlib/tstrformat.nim @@ -10,4 +10,13 @@ proc `$`(o: Obj): string = "foobar" var o: Obj doAssert fmt"{o}" == "foobar" -doAssert fmt"{o:10}" == "foobar " \ No newline at end of file +doAssert fmt"{o:10}" == "foobar " + +# see issue #7932 +doAssert fmt"{15:08}" == "00000015" # int, works +doAssert fmt"{1.5:08}" == "000001.5" # float, works +doAssert fmt"{1.5:0>8}" == "000001.5" # workaround using fill char works for positive floats +doAssert fmt"{-1.5:0>8}" == "0000-1.5" # even that does not work for negative floats +doAssert fmt"{-1.5:08}" == "-00001.5" # works +doAssert fmt"{1.5:+08}" == "+00001.5" # works +doAssert fmt"{1.5: 08}" == " 00001.5" # works From 91765e583da91b8288e18e74d4196713dc2ede18 Mon Sep 17 00:00:00 2001 From: skilchen Date: Sat, 2 Jun 2018 04:24:32 +0200 Subject: [PATCH 28/31] fix handling of default precision in formatBiggestFloat for the js backend (#7928) --- lib/pure/strutils.nim | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/lib/pure/strutils.nim b/lib/pure/strutils.nim index ee3072c85..a4fd20fdb 100644 --- a/lib/pure/strutils.nim +++ b/lib/pure/strutils.nim @@ -1854,6 +1854,10 @@ proc formatBiggestFloat*(f: BiggestFloat, format: FloatFormatMode = ffDefault, ## ## If ``precision == -1``, it tries to format it nicely. when defined(js): + var precision = precision + if precision == -1: + # use the same default precision as c_sprintf + precision = 6 var res: cstring case format of ffDefault: @@ -1863,6 +1867,9 @@ proc formatBiggestFloat*(f: BiggestFloat, format: FloatFormatMode = ffDefault, of ffScientific: {.emit: "`res` = `f`.toExponential(`precision`);".} result = $res + if 1.0 / f == -Inf: + # JavaScript removes the "-" from negative Zero, add it back here + result = "-" & $res for i in 0 ..< result.len: # Depending on the locale either dot or comma is produced, # but nothing else is possible: From 381acc09e9a5c2d61a67f0f1f6b60d2efb577657 Mon Sep 17 00:00:00 2001 From: Varriount Date: Sun, 3 Jun 2018 14:37:41 -0400 Subject: [PATCH 29/31] Update sqlite example to use empty strings. Update sqlite example to use empty strings to use empty strings instead of nil. --- lib/impure/db_sqlite.nim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/impure/db_sqlite.nim b/lib/impure/db_sqlite.nim index 6b8ca4881..fd25b2b94 100644 --- a/lib/impure/db_sqlite.nim +++ b/lib/impure/db_sqlite.nim @@ -57,7 +57,7 @@ ## ## import db_sqlite, math ## -## let theDb = open("mytest.db", nil, nil, nil) +## let theDb = open("mytest.db", "", "", "") ## ## theDb.exec(sql"Drop table if exists myTestTbl") ## theDb.exec(sql("""create table myTestTbl ( From 395b7506cf62cb86f34430c1a034a127ce2ad392 Mon Sep 17 00:00:00 2001 From: Yuriy Glukhov Date: Mon, 4 Jun 2018 12:12:18 +0300 Subject: [PATCH 30/31] Expose GC_setStackBottom (#7885) --- lib/system.nim | 14 +++++++++----- lib/system/gc_common.nim | 4 ++-- lib/system/gc_regions.nim | 2 +- lib/system/mmdisp.nim | 8 ++++---- lib/system/threads.nim | 2 +- 5 files changed, 17 insertions(+), 13 deletions(-) diff --git a/lib/system.nim b/lib/system.nim index 13f16903a..b8aa170ea 100644 --- a/lib/system.nim +++ b/lib/system.nim @@ -1523,7 +1523,6 @@ const hasAlloc = (hostOS != "standalone" or not defined(nogc)) and not defined(n when not defined(JS) and not defined(nimscript) and hostOS != "standalone": include "system/cgprocs" when not defined(JS) and not defined(nimscript) and hasAlloc: - proc setStackBottom(theStackBottom: pointer) {.compilerRtl, noinline, benign.} proc addChar(s: NimString, c: char): NimString {.compilerProc, benign.} proc add *[T](x: var seq[T], y: T) {.magic: "AppendSeqElem", noSideEffect.} @@ -2639,6 +2638,11 @@ when not defined(nimscript) and hasAlloc: proc GC_unref*(x: string) {.magic: "GCunref", benign.} ## see the documentation of `GC_ref`. + when not defined(JS) and not defined(nimscript) and hasAlloc: + proc nimGC_setStackBottom*(theStackBottom: pointer) {.compilerRtl, noinline, benign.} + ## Expands operating GC stack range to `theStackBottom`. Does nothing + ## if current stack bottom is already lower than `theStackBottom`. + else: template GC_disable* = {.warning: "GC_disable is a no-op in JavaScript".} @@ -2898,16 +2902,16 @@ when not defined(JS): #and not defined(nimscript): # WARNING: This is very fragile! An array size of 8 does not work on my # Linux 64bit system. -- That's because the stack direction is the other # way round. - when declared(setStackBottom): + when declared(nimGC_setStackBottom): var locals {.volatile.}: pointer locals = addr(locals) - setStackBottom(locals) + nimGC_setStackBottom(locals) proc initStackBottomWith(locals: pointer) {.inline, compilerproc.} = # We need to keep initStackBottom around for now to avoid # bootstrapping problems. - when declared(setStackBottom): - setStackBottom(locals) + when declared(nimGC_setStackBottom): + nimGC_setStackBottom(locals) {.push profiler: off.} var diff --git a/lib/system/gc_common.nim b/lib/system/gc_common.nim index 246e55f14..939776a58 100644 --- a/lib/system/gc_common.nim +++ b/lib/system/gc_common.nim @@ -200,7 +200,7 @@ when declared(threadType): if threadType == ThreadType.None: initAllocator() var stackTop {.volatile.}: pointer - setStackBottom(addr(stackTop)) + nimGC_setStackBottom(addr(stackTop)) initGC() threadType = ThreadType.ForeignThread @@ -257,7 +257,7 @@ when nimCoroutines: gch.activeStack.setPosition(addr(sp)) when not defined(useNimRtl): - proc setStackBottom(theStackBottom: pointer) = + proc nimGC_setStackBottom(theStackBottom: pointer) = # Initializes main stack of the thread. when nimCoroutines: if gch.stack.next == nil: diff --git a/lib/system/gc_regions.nim b/lib/system/gc_regions.nim index 83c65e8ce..06fded86b 100644 --- a/lib/system/gc_regions.nim +++ b/lib/system/gc_regions.nim @@ -401,7 +401,7 @@ proc getFreeMem*(r: MemRegion): int = r.remaining proc getTotalMem*(r: MemRegion): int = result = r.totalSize -proc setStackBottom(theStackBottom: pointer) = discard +proc nimGC_setStackBottom(theStackBottom: pointer) = discard proc nimGCref(x: pointer) {.compilerProc.} = discard proc nimGCunref(x: pointer) {.compilerProc.} = discard diff --git a/lib/system/mmdisp.nim b/lib/system/mmdisp.nim index a156e9bb0..2c9b1e502 100644 --- a/lib/system/mmdisp.nim +++ b/lib/system/mmdisp.nim @@ -146,7 +146,7 @@ when defined(boehmgc): proc getFreeMem(): int = return boehmGetFreeBytes() proc getTotalMem(): int = return boehmGetHeapSize() - proc setStackBottom(theStackBottom: pointer) = discard + proc nimGC_setStackBottom(theStackBottom: pointer) = discard proc initGC() = boehmGCinit() @@ -305,7 +305,7 @@ elif defined(gogc): goRuntime_ReadMemStats(addr mstats) result = int(mstats.sys) - proc setStackBottom(theStackBottom: pointer) = discard + proc nimGC_setStackBottom(theStackBottom: pointer) = discard proc alloc(size: Natural): pointer = result = c_malloc(size) @@ -449,7 +449,7 @@ elif defined(nogc) and defined(useMalloc): proc getFreeMem(): int = discard proc getTotalMem(): int = discard - proc setStackBottom(theStackBottom: pointer) = discard + proc nimGC_setStackBottom(theStackBottom: pointer) = discard proc initGC() = discard @@ -523,7 +523,7 @@ elif defined(nogc): proc growObj(old: pointer, newsize: int): pointer = result = realloc(old, newsize) - proc setStackBottom(theStackBottom: pointer) = discard + proc nimGC_setStackBottom(theStackBottom: pointer) = discard proc nimGCref(p: pointer) {.compilerproc, inline.} = discard proc nimGCunref(p: pointer) {.compilerproc, inline.} = discard diff --git a/lib/system/threads.nim b/lib/system/threads.nim index 2c10e0244..861bde13f 100644 --- a/lib/system/threads.nim +++ b/lib/system/threads.nim @@ -440,7 +440,7 @@ proc threadProcWrapStackFrame[TArg](thrd: ptr Thread[TArg]) = threadProcWrapDispatch[TArg] when not hasSharedHeap: # init the GC for refc/markandsweep - setStackBottom(addr(p)) + nimGC_setStackBottom(addr(p)) initGC() when declared(threadType): threadType = ThreadType.NimThread From 582786d0684b76a5bab5faa54304b2873bdcff5e Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Mon, 4 Jun 2018 12:20:56 +0200 Subject: [PATCH 31/31] db_mysql.nim: Work around string nil comparison logic --- lib/impure/db_mysql.nim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/impure/db_mysql.nim b/lib/impure/db_mysql.nim index 3b461d2f6..ca0e29d11 100644 --- a/lib/impure/db_mysql.nim +++ b/lib/impure/db_mysql.nim @@ -128,7 +128,7 @@ proc dbFormat(formatstr: SqlQuery, args: varargs[string]): string = var a = 0 for c in items(string(formatstr)): if c == '?': - if args[a] == nil: + if args[a].isNil: add(result, "NULL") else: add(result, dbQuote(args[a]))