summary refs log tree commit diff stats
path: root/lib
diff options
context:
space:
mode:
Diffstat (limited to 'lib')
-rwxr-xr-xlib/core/macros.nim23
-rwxr-xr-xlib/core/typeinfo.nim3
-rwxr-xr-xlib/pure/times.nim174
-rwxr-xr-xlib/system.nim41
-rwxr-xr-xlib/system/assign.nim12
-rwxr-xr-xlib/system/excpt.nim9
6 files changed, 230 insertions, 32 deletions
diff --git a/lib/core/macros.nim b/lib/core/macros.nim
index 8b08952f0..b0f237924 100755
--- a/lib/core/macros.nim
+++ b/lib/core/macros.nim
@@ -36,7 +36,7 @@ type
     nnkMacroDef, nnkTemplateDef, nnkIteratorDef, nnkOfBranch, 

     nnkElifBranch, nnkExceptBranch, nnkElse, nnkMacroStmt, 

     nnkAsmStmt, nnkPragma, nnkPragmaBlock, nnkIfStmt, nnkWhenStmt, 

-    nnkForStmt, nnkWhileStmt, nnkCaseStmt, 

+    nnkForStmt, nnkParForStmt, nnkWhileStmt, nnkCaseStmt, 

     nnkTypeSection, nnkVarSection, nnkLetSection, nnkConstSection, 

     nnkConstDef, nnkTypeDef, 

     nnkYieldStmt, nnkTryStmt, nnkFinally, nnkRaiseStmt, 

@@ -217,10 +217,19 @@ template emit*(s: expr): stmt =
   ## Example:

   ## 

   ##   emit("echo " & '"' & "hello world".toUpper & '"')

-  ##

-  block:

-    const evaluated = s

-    eval: result = evaluated.parseStmt

+  ##
+  block:
+    const evaluated = s
+    eval: result = evaluated.parseStmt
+  when false:

+    template once(x: expr): expr =
+      block:
+        const y = x
+        y
+
+    macro `payload`(x: stmt): stmt = result = once(s).parseStmt
+    `payload`()
+
 

 proc expectKind*(n: PNimrodNode, k: TNimrodNodeKind) {.compileTime.} =

   ## checks that `n` is of kind `k`. If this is not the case,

@@ -323,7 +332,7 @@ proc lispRepr*(n: PNimrodNode): string {.compileTime.} =
 

 macro dumpTree*(s: stmt): stmt = echo s[1].treeRepr

   ## Accepts a block of nimrod code and prints the parsed abstract syntax

-  ## tree using the `toTree` function.

+  ## tree using the `toTree` function. Printing is done *at compile time*.

   ##

   ## You can use this as a tool to explore the Nimrod's abstract syntax 

   ## tree and to discover what kind of nodes must be created to represent

@@ -331,7 +340,7 @@ macro dumpTree*(s: stmt): stmt = echo s[1].treeRepr
 

 macro dumpLisp*(s: stmt): stmt = echo s[1].lispRepr

   ## Accepts a block of nimrod code and prints the parsed abstract syntax

-  ## tree using the `toLisp` function.

+  ## tree using the `toLisp` function. Printing is done *at compile time*.

   ##

   ## See `dumpTree`.

 

diff --git a/lib/core/typeinfo.nim b/lib/core/typeinfo.nim
index 9cc8e4dfd..ca2b68cc3 100755
--- a/lib/core/typeinfo.nim
+++ b/lib/core/typeinfo.nim
@@ -479,7 +479,8 @@ proc setBiggestFloat*(x: TAny, y: biggestFloat) =
 proc getString*(x: TAny): string = 
   ## retrieve the string value out of `x`. `x` needs to represent a string.
   assert x.rawtype.kind == tyString
-  result = cast[ptr string](x.value)[]
+  if not isNil(cast[ptr pointer](x.value)[]):
+    result = cast[ptr string](x.value)[]
 
 proc setString*(x: TAny, y: string) = 
   ## sets the string value of `x`. `x` needs to represent a string.
diff --git a/lib/pure/times.nim b/lib/pure/times.nim
index 968b317d6..4cb873b1c 100755
--- a/lib/pure/times.nim
+++ b/lib/pure/times.nim
@@ -24,7 +24,11 @@ type
     mJan, mFeb, mMar, mApr, mMay, mJun, mJul, mAug, mSep, mOct, mNov, mDec
   TWeekDay* = enum ## represents a weekday
     dMon, dTue, dWed, dThu, dFri, dSat, dSun
-    
+
+var
+  timezone {.importc, header: "<time.h>".}: int
+  tzname {.importc, header: "<time.h>" .}: array[0..1, cstring]
+
 when defined(posix): 
   type
     TTimeImpl {.importc: "time_t", header: "<sys/time.h>".} = int
@@ -112,6 +116,20 @@ type
     yearday*: range[0..365]   ## The number of days since January 1,
                               ## in the range 0 to 365.
                               ## Always 0 if the target is ECMAScript.
+    isDST*: bool              ## Determines whether DST is in effect. Always
+                              ## ``False`` if time is UTC.
+    tzname*: string           ## The timezone this time is in. E.g. GMT
+    timezone*: int            ## The offset of the (non-DST) timezone in seconds
+                              ## west of UTC.
+
+  TTimeInterval* = object
+    miliseconds*: int ## The number of miliseconds
+    seconds*: int     ## The number of seconds
+    minutes*: int     ## The number of minutes
+    hours*: int       ## The number of hours
+    days*: int        ## The number of days
+    months*: int      ## The number of months
+    years*: int       ## The number of years
 
 proc getTime*(): TTime ## gets the current calendar time
 proc getLocalTime*(t: TTime): TTimeInfo
@@ -122,7 +140,7 @@ proc getGMTime*(t: TTime): TTimeInfo
   ## expressed in Coordinated Universal Time (UTC).
 
 proc TimeInfoToTime*(timeInfo: TTimeInfo): TTime
-  ## converts a broken-down time structure, expressed as local time, to
+  ## converts a broken-down time structure to
   ## calendar time representation. The function ignores the specified
   ## contents of the structure members `weekday` and `yearday` and recomputes
   ## them from the other information in the broken-down time structure.
@@ -146,10 +164,90 @@ proc `<=` * (a, b: TTime): bool {.
   ## returns true iff ``a <= b``.
   result = a - b <= 0
 
+proc getTzname*(): tuple[nonDST, DST: string]
+  ## returns the local timezone; ``nonDST`` is the name of the local non-DST
+  ## timezone, ``DST`` is the name of the local DST timezone.
+
+proc getTimezone*(): int
+  ## returns the offset of the local (non-DST) timezone in seconds west of UTC.
+
 proc getStartMilsecs*(): int {.deprecated.}
   ## get the miliseconds from the start of the program. **Deprecated since
   ## version 0.8.10.** Use ``epochTime`` or ``cpuTime`` instead.
 
+proc newInterval*(miliseconds, seconds, minutes, hours, days, months, 
+                  years: int = 0): TTimeInterval =
+  ## creates a new ``TTimeInterval``.
+  result.miliseconds = miliseconds
+  result.seconds = seconds
+  result.minutes = minutes
+  result.hours = hours
+  result.days = days
+  result.months = months
+  result.years = years
+
+proc isLeapYear(year: int): bool =
+  if year mod 400 == 0:
+     return true
+  elif year mod 100 == 0: 
+     return false
+  elif year mod 4 == 0: 
+     return true
+  else:
+     return false
+
+proc getDaysInMonth(month: TMonth, year: int): int =
+  # http://www.dispersiondesign.com/articles/time/number_of_days_in_a_month
+  if month == mFeb: # Feb
+    if isLeapYear(year):
+      result = 29
+    else:
+      result = 28
+  elif month in [mApr, mJun, mSep, mNov]: result = 30
+  else: result = 31  
+
+proc calculateSeconds(a: TTimeInfo, interval: TTimeInterval): float =
+  var anew = a
+  var newinterv = interval
+  result = 0.0
+  
+  newinterv.months += interval.years * 12
+  var curMonth = anew.month
+  for mth in 1 .. newinterv.months:
+    result += float(getDaysInMonth(curMonth, anew.year) * 24 * 60 * 60)
+    if curMonth == mDec:
+      curMonth = mJan
+      anew.year.inc()
+    else:
+      curMonth.inc()
+  result += float(newinterv.days * 24 * 60 * 60)
+  result += float(newinterv.minutes * 60 * 60)
+  result += newinterv.seconds.float
+  result += newinterv.miliseconds / 1000
+
+proc `+`*(a: TTimeInfo, interval: TTimeInterval): TTimeInfo =
+  ## adds ``interval`` time.
+  ##
+  ## **Note:** This has been only briefly tested and it may not be very accurate.
+  let t = timeInfoToTime(a)
+  var secs = calculateSeconds(a, interval)
+  if a.tzname == "UTC":
+    result = getGMTime(TTime(float(t) + secs))
+  else:
+    result = getLocalTime(TTime(float(t) + secs))
+
+proc `-`*(a: TTimeInfo, interval: TTimeInterval): TTimeInfo =
+  ## subtracts ``interval`` time.
+  ##
+  ## **Note:** This has been only briefly tested, it is inaccurate especially
+  ## when you subtract so much that you reach the Julian calendar.
+  let t = timeInfoToTime(a)
+  var secs = calculateSeconds(a, interval)
+  if a.tzname == "UTC":
+    result = getGMTime(TTime(float(t) - secs))
+  else:
+    result = getLocalTime(TTime(float(t) - secs))
+
 when not defined(ECMAScript):  
   proc epochTime*(): float {.rtl, extern: "nt$1".}
     ## gets time after the UNIX epoch (1970) in seconds. It is a float
@@ -169,7 +267,8 @@ when not defined(ECMAScript):
     ##   doWork()
     ##   echo "CPU time [s] ", cpuTime() - t0
 
-when not defined(ECMAScript):  
+when not defined(ECMAScript):
+  
   # C wrapper:
   type
     structTM {.importc: "struct tm", final.} = object
@@ -205,7 +304,7 @@ when not defined(ECMAScript):
     clocksPerSec {.importc: "CLOCKS_PER_SEC", nodecl.}: int
     
   # our own procs on top of that:
-  proc tmToTimeInfo(tm: structTM): TTimeInfo =
+  proc tmToTimeInfo(tm: structTM, local: bool): TTimeInfo =
     const
       weekDays: array [0..6, TWeekDay] = [
         dSun, dMon, dTue, dWed, dThu, dFri, dSat]
@@ -217,6 +316,18 @@ when not defined(ECMAScript):
     result.year = tm.year + 1900'i32
     result.weekday = weekDays[int(tm.weekDay)]
     result.yearday = int(tm.yearday)
+    result.isDST = tm.isDST > 0
+    #result.tzname = if local: getTzname()[if result.isDST: 0 else: 1] else: "UTC"
+    # TODO: ^^^ Crashes the compiler.
+    if local:
+      if result.isDST:
+        result.tzname = getTzname()[0]
+      if not result.isDST:
+        result.tzname = getTzname()[1]
+    else:
+      result.tzname = "UTC"
+    
+    result.timezone = if local: getTimezone() else: 0
   
   proc timeInfoToTM(t: TTimeInfo): structTM =
     const
@@ -229,7 +340,7 @@ when not defined(ECMAScript):
     result.year = t.year - 1900
     result.weekday = weekDays[t.weekDay]
     result.yearday = t.yearday
-    result.isdst = -1
+    result.isdst = if t.isDST: 1 else: 0
   
   when not defined(useNimRtl):
     proc `-` (a, b: TTime): int64 =
@@ -251,13 +362,13 @@ when not defined(ECMAScript):
   proc getTime(): TTime = return timec(nil)
   proc getLocalTime(t: TTime): TTimeInfo =
     var a = t
-    result = tmToTimeInfo(localtime(addr(a))[])
+    result = tmToTimeInfo(localtime(addr(a))[], true)
     # copying is needed anyway to provide reentrancity; thus
     # the convertion is not expensive
   
   proc getGMTime(t: TTime): TTimeInfo =
     var a = t
-    result = tmToTimeInfo(gmtime(addr(a))[])
+    result = tmToTimeInfo(gmtime(addr(a))[], false)
     # copying is needed anyway to provide reentrancity; thus
     # the convertion is not expensive
   
@@ -294,7 +405,13 @@ when not defined(ECMAScript):
   proc winTimeToUnixTime*(t: int64): TTime = 
     ## converts a Windows time to a UNIX `TTime` (``time_t``)
     result = TTime((t - epochDiff) div rateDiff)
-    
+ 
+  proc getTzname(): tuple[nonDST, DST: string] =
+    return ($tzname[0], $tzname[1])
+  
+  proc getTimezone(): int =
+    return timezone
+  
   when not defined(useNimRtl):
     proc epochTime(): float = 
       when defined(posix):
@@ -417,18 +534,21 @@ proc format*(info: TTimeInfo, f: string): string =
   ##    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``                                
+  ##    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 ``:00``.                                                    ``GMT+7 -> +07:00``, ``GMT-5 -> -05:00``
+  ##    ZZZ      Displays the name of the timezone.                                                 ``GMT -> GMT``, ``EST -> EST``
   ## ==========  =================================================================================  ================================================
-
+  ##
+  ## Other strings can be inserted by putting them in ``''``. For example ``hh'->'mm`` will give ``01->56``.
 
   result = ""
   var i = 0
   var currentF = ""
   while True:
     case f[i]
-    of '\0':
-      break
-    of ' ', '-', '/', ':', '\'':
+    of ' ', '-', '/', ':', '\'', '\0':
       case currentF
       of "d":
         result.add($info.monthday)
@@ -437,9 +557,9 @@ proc format*(info: TTimeInfo, f: string): string =
           result.add("0")
         result.add($info.monthday)
       of "ddd":
-        result.add(($info.month)[0 .. 2])
+        result.add(($info.monthday)[0 .. 2])
       of "dddd":
-        result.add($info.month)
+        result.add($info.monthday)
       of "h":
         result.add($(info.hour - 12))
       of "hh":
@@ -460,11 +580,11 @@ proc format*(info: TTimeInfo, f: string): string =
           result.add('0')
         result.add($info.minute)
       of "M":
-        result.add($(int(info.month)))
+        result.add($(int(info.month)+1))
       of "MM":
         if int(info.month) < 10:
           result.add('0')
-        result.add($(int(info.month)))
+        result.add($(int(info.month)+1))
       of "MMM":
         result.add(($info.month)[0..2])
       of "MMMM":
@@ -500,12 +620,32 @@ proc format*(info: TTimeInfo, f: string): string =
       of "yyyyy":
         result.add('0')
         result.add($info.year)
+      of "z":
+        let hrs = (info.timezone div 60) div 60
+        result.add($hrs)
+      of "zz":
+        let hrs = (info.timezone div 60) div 60
+        
+        result.add($hrs)
+        if hrs.abs < 10:
+          var atIndex = result.len-(($hrs).len-(if hrs < 0: 1 else: 0))
+          result.insert("0", atIndex)
+      of "zzz":
+        let hrs = (info.timezone div 60) div 60
+        
+        result.add($hrs & ":00")
+        if hrs.abs < 10:
+          var atIndex = result.len-(($hrs & ":00").len-(if hrs < 0: 1 else: 0))
+          result.insert("0", atIndex)
+      of "ZZZ":
+        result.add(info.tzname)
       of "":
         nil # Do nothing.
       else:
         raise newException(EInvalidValue, "Invalid format string: " & currentF)
       
       currentF = ""
+      if f[i] == '\0': break
       
       if f[i] == '\'':
         inc(i) # Skip '
diff --git a/lib/system.nim b/lib/system.nim
index 4a7ac0654..449b56c2f 100755
--- a/lib/system.nim
+++ b/lib/system.nim
@@ -49,9 +49,13 @@ type
                                 ## a type description (for templates)
   void* {.magic: "VoidType".}  ## meta type to denote the absense of any type
   
-  TInteger* = int|char|int8|int16|int32|int64|bool|enum
+  TInteger* = int|int8|int16|int32|int64
     ## type class matching all integer types
 
+  TOrdinal* = TInteger|bool|enum
+    ## type class matching all ordinal types; however this includes enums with
+    ## holes.
+
   TNumber* = TInteger|float|float32|float64
     ## type class matching all number types
 
@@ -190,6 +194,7 @@ type
     msg* {.exportc: "message".}: string ## the exception's message. Not
                                         ## providing an exception message 
                                         ## is bad style.
+    trace: string
 
   EAsynch* = object of E_Base ## Abstract exception class for
                               ## *asynchronous exceptions* (interrupts).
@@ -1195,6 +1200,15 @@ iterator `..`*[S, T](a: S, b: T): T {.inline.} =
     yield res
     inc res
 
+iterator `||`*[S, T](a: S, b: T, annotation=""): T {.
+  inline, magic: "OmpParFor", sideEffect.} =
+  ## parallel loop iterator. Same as `..` but the loop may run in parallel.
+  ## `annotation` is an additional annotation for the code generator to use.
+  ## Note that the compiler maps that to
+  ## the ``#pragma omp parallel for`` construct of `OpenMP`:idx: and as
+  ## such isn't aware of the parallelism in your code. Be careful.
+  nil
+
 proc min*(x, y: int): int {.magic: "MinI", noSideEffect.}
 proc min*(x, y: int8): int8 {.magic: "MinI", noSideEffect.}
 proc min*(x, y: int16): int16 {.magic: "MinI", noSideEffect.}
@@ -1900,6 +1914,10 @@ when not defined(EcmaScript) and not defined(NimrodVM):
   when hostOS != "standalone":
     proc getStackTrace*(): string
       ## gets the current stack trace. This is only works for debug builds.
+
+    proc getStackTrace*(e: ref E_Base): string
+      ## gets the stack trace associated with `e`, which is the stack that
+      ## lead to the ``raise`` statement. This is only works for debug builds.
       
   {.push stack_trace: off.}
   when hostOS == "standalone":
@@ -2177,19 +2195,19 @@ proc `*=`*[T](x: var ordinal[T], y: ordinal[T]) {.inline, noSideEffect.} =
   ## Binary `*=` operator for ordinals
   x = x * y
 
-proc `+=` *(x: var float, y:float) {.inline, noSideEffect.} =
+proc `+=`*[T: float|float32|float64] (x: var T, y: T) {.inline, noSideEffect.} =
   ## Increments in placee a floating point number
   x = x + y
 
-proc `-=` *(x: var float, y:float) {.inline, noSideEffect.} =
+proc `-=`*[T: float|float32|float64] (x: var T, y: T) {.inline, noSideEffect.} =
   ## Decrements in place a floating point number
   x = x - y
 
-proc `*=` *(x: var float, y:float) {.inline, noSideEffect.} =
+proc `*=`*[T: float|float32|float64] (x: var T, y: T) {.inline, noSideEffect.} =
   ## Multiplies in place a floating point number
   x = x * y
 
-proc `/=` *(x: var float, y:float) {.inline, noSideEffect.} =
+proc `/=`*[T: float|float32|float64] (x: var T, y: T) {.inline, noSideEffect.} =
   ## Divides in place a floating point number
   x = x / y
 
@@ -2283,5 +2301,18 @@ template eval*(blk: stmt): stmt =
     macro payload(x: stmt): stmt = blk
     payload()
 
+proc insert*(x: var string, item: string, i = 0) {.noSideEffect.} = 
+  ## inserts `item` into `x` at position `i`.
+  var xl = x.len
+  setLen(x, xl+item.len)
+  var j = xl-1
+  while j >= i:
+    shallowCopy(x[j+item.len], x[j])
+    dec(j)
+  j = 0
+  while j < item.len:
+    x[j+i] = item[j]
+    inc(j)
+
 when defined(initDebugger):
   initDebugger()
diff --git a/lib/system/assign.nim b/lib/system/assign.nim
index d1055fbb5..59c44a6cc 100755
--- a/lib/system/assign.nim
+++ b/lib/system/assign.nim
@@ -7,6 +7,8 @@
 #    distribution, for details about the copyright.
 #
 
+proc genericResetAux(dest: Pointer, n: ptr TNimNode)
+
 proc genericAssignAux(dest, src: Pointer, mt: PNimType, shallow: bool)
 proc genericAssignAux(dest, src: Pointer, n: ptr TNimNode, shallow: bool) =
   var
@@ -20,10 +22,16 @@ proc genericAssignAux(dest, src: Pointer, n: ptr TNimNode, shallow: bool) =
     for i in 0..n.len-1:
       genericAssignAux(dest, src, n.sons[i], shallow)
   of nkCase:
+    var dd = selectBranch(dest, n)
+    var m = selectBranch(src, n)
+    # reset if different branches are in use; note different branches also
+    # imply that's not self-assignment (``x = x``)!
+    if m != dd and dd != nil: 
+      genericResetAux(dest, dd)
     copyMem(cast[pointer](d +% n.offset), cast[pointer](s +% n.offset),
             n.typ.size)
-    var m = selectBranch(src, n)
-    if m != nil: genericAssignAux(dest, src, m, shallow)
+    if m != nil:
+      genericAssignAux(dest, src, m, shallow)
   of nkNone: sysAssert(false, "genericAssignAux")
   #else:
   #  echo "ugh memory corruption! ", n.kind
diff --git a/lib/system/excpt.nim b/lib/system/excpt.nim
index a523e4e04..204fba376 100755
--- a/lib/system/excpt.nim
+++ b/lib/system/excpt.nim
@@ -194,6 +194,9 @@ proc quitOrDebug() {.inline.} =
 
 proc raiseException(e: ref E_Base, ename: CString) {.compilerRtl.} =
   e.name = ename
+  when hasSomeStackTrace:
+    e.trace = ""
+    rawWriteStackTrace(e.trace)
   if localRaiseHook != nil:
     if not localRaiseHook(e): return
   if globalRaiseHook != nil:
@@ -253,6 +256,12 @@ proc getStackTrace(): string =
   else:
     result = "No stack traceback available\n"
 
+proc getStackTrace(e: ref E_Base): string =
+  if not isNil(e) and not isNil(e.trace):
+    result = e.trace
+  else:
+    result = ""
+
 when defined(endb):
   var
     dbgAborting: bool # whether the debugger wants to abort