summary refs log tree commit diff stats
diff options
context:
space:
mode:
-rw-r--r--compiler/nimconf.nim5
-rw-r--r--compiler/nimrod.nimrod.cfg (renamed from compiler/nimrod.cfg)0
-rw-r--r--compiler/sem.nim50
-rw-r--r--compiler/semexprs.nim14
-rw-r--r--compiler/semstmts.nim2
-rw-r--r--lib/core/macros.nim2
-rw-r--r--lib/pure/fsmonitor.nim10
-rw-r--r--lib/pure/htmlparser.nim30
-rw-r--r--lib/system.nim53
-rw-r--r--lib/system/channels.nim3
-rw-r--r--lib/system/threads.nim3
-rw-r--r--readme.md4
-rw-r--r--readme.txt4
-rw-r--r--tests/compile/tgeneric.nim8
-rw-r--r--tests/run/tdrdobbs_examples.nim134
-rw-r--r--tests/run/tvarious1.nim14
16 files changed, 273 insertions, 63 deletions
diff --git a/compiler/nimconf.nim b/compiler/nimconf.nim
index 507812d9c..7ec566a01 100644
--- a/compiler/nimconf.nim
+++ b/compiler/nimconf.nim
@@ -243,11 +243,6 @@ proc LoadConfigs*(cfg: string) =
     readConfigFile(pd / cfg)
     
     if gProjectName.len != 0:
-      var conffile = changeFileExt(gProjectFull, "cfg")
-      if conffile != pd / cfg and existsFile(conffile):
-        readConfigFile(conffile)
-        rawMessage(warnConfigDeprecated, conffile)
-      
       # new project wide config file:
       readConfigFile(changeFileExt(gProjectFull, "nimrod.cfg"))
  
diff --git a/compiler/nimrod.cfg b/compiler/nimrod.nimrod.cfg
index ac8f732f1..ac8f732f1 100644
--- a/compiler/nimrod.cfg
+++ b/compiler/nimrod.nimrod.cfg
diff --git a/compiler/sem.nim b/compiler/sem.nim
index ea53afbeb..3ace623bc 100644
--- a/compiler/sem.nim
+++ b/compiler/sem.nim
@@ -90,6 +90,24 @@ proc commonType*(x, y: PType): PType =
     let idx = ord(b.kind in {tyArray, tyArrayConstr})
     if a.sons[idx].kind == tyEmpty: return y
     #elif b.sons[idx].kind == tyEmpty: return x
+  elif a.kind == tyRange and b.kind == tyRange:
+    # consider:  (range[0..3], range[0..4]) here. We should make that
+    # range[0..4]. But then why is (range[0..4], 6) not range[0..6]?
+    # But then why is (2,4) not range[2..4]? But I think this would break
+    # too much code. So ... it's the same range or the base type. This means
+    #  type(if b: 0 else 1) == int and not range[0..1]. For now. In the long
+    # run people expect ranges to work properly within a tuple.
+    if not sameType(a, b):
+      result = skipTypes(a, {tyRange}).skipIntLit
+    when false:
+      if a.kind != tyRange and b.kind == tyRange:
+        # XXX This really needs a better solution, but a proper fix now breaks
+        # code.
+        result = a #.skipIntLit
+      elif a.kind == tyRange and b.kind != tyRange:
+        result = b #.skipIntLit
+      elif a.kind in IntegralTypes and a.n != nil:
+        result = a #.skipIntLit
   else:
     var k = tyNone
     if a.kind in {tyRef, tyPtr}:
@@ -103,7 +121,7 @@ proc commonType*(x, y: PType): PType =
       if result.isNil: return x
       if k != tyNone:
         let r = result
-        result = NewType(k, r.owner)
+        result = newType(k, r.owner)
         result.addSonSkipIntLit(r)
 
 proc isTopLevel(c: PContext): bool {.inline.} = 
@@ -140,26 +158,27 @@ proc IsOpImpl(c: PContext, n: PNode): PNode
 proc semMacroExpr(c: PContext, n, nOrig: PNode, sym: PSym,
                   semCheck: bool = true): PNode
 
-proc symFromType(t: PType, info: TLineInfo): PSym =
-  if t.sym != nil: return t.sym
-  result = newSym(skType, getIdent"AnonType", t.owner, info)
-  result.flags.incl sfAnon
-  result.typ = t
+when false:
+  proc symFromType(t: PType, info: TLineInfo): PSym =
+    if t.sym != nil: return t.sym
+    result = newSym(skType, getIdent"AnonType", t.owner, info)
+    result.flags.incl sfAnon
+    result.typ = t
 
-proc symNodeFromType(c: PContext, t: PType, info: TLineInfo): PNode =
-  result = newSymNode(symFromType(t, info), info)
-  result.typ = makeTypeDesc(c, t)
+  proc symNodeFromType(c: PContext, t: PType, info: TLineInfo): PNode =
+    result = newSymNode(symFromType(t, info), info)
+    result.typ = makeTypeDesc(c, t)
 
 proc createEvalContext(c: PContext, mode: TEvalMode): PEvalContext =
   result = newEvalContext(c.module, mode)
   result.getType = proc (n: PNode): PNode =
-    var e = tryExpr(c, n)
-    if e == nil:
-      result = symNodeFromType(c, errorType(c), n.info)
-    elif e.typ == nil:
+    result = tryExpr(c, n)
+    if result == nil:
+      result = newSymNode(errorSym(c, n))
+    elif result.typ == nil:
       result = newSymNode(getSysSym"void")
     else:
-      result = symNodeFromType(c, e.typ, n.info)
+      result.typ = makeTypeDesc(c, result.typ)
 
   result.handleIsOperator = proc (n: PNode): PNode =
     result = IsOpImpl(c, n)
@@ -210,7 +229,8 @@ proc semAfterMacroCall(c: PContext, n: PNode, s: PSym): PNode =
     of tyTypeDesc:
       if n.kind == nkStmtList: result.kind = nkStmtListType
       var typ = semTypeNode(c, result, nil)
-      result = symNodeFromType(c, typ, n.info)
+      result.typ = makeTypeDesc(c, typ)
+      #result = symNodeFromType(c, typ, n.info)
     else:
       result = semExpr(c, result)
       result = fitNode(c, s.typ.sons[0], result)
diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim
index 337224aef..775d4e7a0 100644
--- a/compiler/semexprs.nim
+++ b/compiler/semexprs.nim
@@ -191,7 +191,7 @@ proc isCastable(dst, src: PType): bool =
 proc isSymChoice(n: PNode): bool {.inline.} =
   result = n.kind in nkSymChoices
 
-proc semConv(c: PContext, n: PNode, s: PSym): PNode =
+proc semConv(c: PContext, n: PNode): PNode =
   if sonsLen(n) != 2:
     LocalError(n.info, errConvNeedsOneArg)
     return n
@@ -738,8 +738,7 @@ proc semIndirectOp(c: PContext, n: PNode, flags: TExprFlags): PNode =
   elif t != nil and t.kind == tyTypeDesc:
     if n.len == 1: return semObjConstr(c, n, flags)
     let destType = t.skipTypes({tyTypeDesc, tyGenericInst})
-    result = semConv(c, n, symFromType(destType, n.info))
-    return
+    return semConv(c, n)
   else:
     result = overloadedCallOpr(c, n)
     # Now that nkSym does not imply an iteration over the proc/iterator space,
@@ -1048,7 +1047,9 @@ proc semSubscript(c: PContext, n: PNode, flags: TExprFlags): PNode =
     # The result so far is a tyTypeDesc bound 
     # a tyGenericBody. The line below will substitute
     # it with the instantiated type.
-    result = symNodeFromType(c, semTypeNode(c, n, nil), n.info)
+    result = n
+    result.typ = makeTypeDesc(c, semTypeNode(c, n, nil))
+    #result = symNodeFromType(c, semTypeNode(c, n, nil), n.info)
   of tyTuple: 
     checkSonsLen(n, 2)
     n.sons[0] = makeDeref(n.sons[0])
@@ -1883,7 +1884,8 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode =
     result = semExpr(c, n.sons[0], flags)
   of nkTypeOfExpr, nkTupleTy, nkRefTy..nkEnumTy:
     var typ = semTypeNode(c, n, nil).skipTypes({tyTypeDesc})
-    result = symNodeFromType(c, typ, n.info)
+    result.typ = makeTypeDesc(c, typ)
+    #result = symNodeFromType(c, typ, n.info)
   of nkCall, nkInfix, nkPrefix, nkPostfix, nkCommand, nkCallStrLit: 
     # check if it is an expression macro:
     checkMinSonsLen(n, 1)
@@ -1906,7 +1908,7 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode =
       of skType:
         # XXX think about this more (``set`` procs)
         if n.len == 2:
-          result = semConv(c, n, s)
+          result = semConv(c, n)
         elif n.len == 1:
           result = semObjConstr(c, n, flags)
         elif Contains(c.AmbiguousSymbols, s.id): 
diff --git a/compiler/semstmts.nim b/compiler/semstmts.nim
index da8ba50a8..a1805fdec 100644
--- a/compiler/semstmts.nim
+++ b/compiler/semstmts.nim
@@ -1087,6 +1087,8 @@ proc semIterator(c: PContext, n: PNode): PNode =
   # -- at least for 0.9.2.
   if s.typ.callConv == ccClosure:
     incl(s.typ.flags, tfCapturesEnv)
+  else:
+    s.typ.callConv = ccInline
   when false:
     if s.typ.callConv != ccInline: 
       s.typ.callConv = ccClosure
diff --git a/lib/core/macros.nim b/lib/core/macros.nim
index fc93a157d..d01d4ebee 100644
--- a/lib/core/macros.nim
+++ b/lib/core/macros.nim
@@ -494,7 +494,7 @@ const
 
 from strutils import cmpIgnoreStyle, format
 
-proc ExpectKind*(n: PNimrodNode; k: set[TNimrodNodeKind]) {.compileTime.} =
+proc expectKind*(n: PNimrodNode; k: set[TNimrodNodeKind]) {.compileTime.} =
   assert n.kind in k, "Expected one of $1, got $2".format(k, n.kind)
 
 proc newProc*(name = newEmptyNode(); params: openarray[PNimrodNode] = [];  
diff --git a/lib/pure/fsmonitor.nim b/lib/pure/fsmonitor.nim
index a554cf963..d6584c1a0 100644
--- a/lib/pure/fsmonitor.nim
+++ b/lib/pure/fsmonitor.nim
@@ -64,10 +64,10 @@ const
 proc newMonitor*(): PFSMonitor =
   ## Creates a new file system monitor.
   new(result)
-  result.fd = inotifyInit()
   result.targets = initTable[cint, string]()
+  result.fd = inotifyInit()
   if result.fd < 0:
-    OSError()
+    OSError(OSLastError())
 
 proc add*(monitor: PFSMonitor, target: string,
                filters = {MonitorAll}): cint {.discardable.} =
@@ -93,7 +93,7 @@ proc add*(monitor: PFSMonitor, target: string,
   
   result = inotifyAddWatch(monitor.fd, target, INFilter.uint32)
   if result < 0:
-    OSError()
+    OSError(OSLastError())
   monitor.targets.add(result, target)
 
 proc del*(monitor: PFSMonitor, wd: cint) =
@@ -101,7 +101,7 @@ proc del*(monitor: PFSMonitor, wd: cint) =
   ##
   ## If ``wd`` is not a part of ``monitor`` an EOS error is raised.
   if inotifyRmWatch(monitor.fd, wd) < 0:
-    OSError()
+    OSError(OSLastError())
 
 proc getEvent(m: PFSMonitor, fd: cint): seq[TMonitorEvent] =
   result = @[]
@@ -184,7 +184,7 @@ proc FSMonitorRead(h: PObject) =
 proc toDelegate(m: PFSMonitor): PDelegate =
   result = newDelegate()
   result.deleVal = m
-  result.fd = m.fd
+  result.fd = (type(result.fd))(m.fd)
   result.mode = fmRead
   result.handleRead = FSMonitorRead
   result.open = true
diff --git a/lib/pure/htmlparser.nim b/lib/pure/htmlparser.nim
index d60d2e583..060f0e386 100644
--- a/lib/pure/htmlparser.nim
+++ b/lib/pure/htmlparser.nim
@@ -17,11 +17,37 @@
 ##
 ##   echo loadHtml("mydirty.html")
 ##
-##
 ## Every tag in the resulting tree is in lower case.
 ##
 ## **Note:** The resulting ``PXmlNode`` already uses the ``clientData`` field, 
 ## so it cannot be used by clients of this library.
+##
+## Example: Transforming hyperlinks
+## ================================
+##
+## This code demonstrates how you can iterate over all the tags in an HTML file
+## and write back the modified version. In this case we look for hyperlinks
+## ending with the extension ``.rst`` and convert them to ``.html``.
+##
+## .. code-block:: nimrod
+##
+##   import htmlparser
+##   import xmltree  # To use '$' for PXmlNode
+##   import strtabs  # To access PXmlAttributes
+##   import os       # To use splitFile
+##   import strutils # To use cmpIgnoreCase
+##
+##   proc transformHyperlinks() =
+##     let html = loadHTML("input.html")
+##
+##     for a in html.findAll("a"):
+##       let href = a.attrs["href"]
+##       if not href.isNil:
+##         let (dir, filename, ext) = splitFile(href)
+##         if cmpIgnoreCase(ext, ".rst") == 0:
+##           a.attrs["href"] = dir / filename & ".html"
+##
+##     writeFile("output.html", $html)
 
 import strutils, streams, parsexml, xmltree, unicode, strtabs
 
@@ -528,7 +554,7 @@ proc parseHtml*(s: PStream, filename: string,
   ## parses the XML from stream `s` and returns a ``PXmlNode``. Every
   ## occured parsing error is added to the `errors` sequence.
   var x: TXmlParser
-  open(x, s, filename, {reportComments})
+  open(x, s, filename, {reportComments, reportWhitespace})
   next(x)
   # skip the DOCTYPE:
   if x.kind == xmlSpecial: next(x)
diff --git a/lib/system.nim b/lib/system.nim
index b2d19a885..dc5a406d1 100644
--- a/lib/system.nim
+++ b/lib/system.nim
@@ -374,10 +374,10 @@ proc newSeq*[T](s: var seq[T], len: int) {.magic: "NewSeq", noSideEffect.}
   ## This is equivalent to ``s = @[]; setlen(s, len)``, but more
   ## efficient since no reallocation is needed.
   ##
-  ## Note that the sequence will be filled with uninitialized entries, which
-  ## can be a problem for sequences containing strings. After the creation of
-  ## the sequence you should assign entries to the sequence instead of adding
-  ## them. Example:
+  ## Note that the sequence will be filled with zeroed entries, which can be a
+  ## problem for sequences containing strings since their value will be
+  ## ``nil``. After the creation of the sequence you should assign entries to
+  ## the sequence instead of adding them. Example:
   ##
   ## .. code-block:: nimrod
   ##   var inputStrings : seq[string]
@@ -390,10 +390,10 @@ proc newSeq*[T](s: var seq[T], len: int) {.magic: "NewSeq", noSideEffect.}
 proc newSeq*[T](len = 0): seq[T] =
   ## creates a new sequence of type ``seq[T]`` with length ``len``.
   ##
-  ## Note that the sequence will be filled with uninitialized entries, which
-  ## can be a problem for sequences containing strings. After the creation of
-  ## the sequence you should assign entries to the sequence instead of adding
-  ## them. Example:
+  ## Note that the sequence will be filled with zeroed entries, which can be a
+  ## problem for sequences containing strings since their value will be
+  ## ``nil``. After the creation of the sequence you should assign entries to
+  ## the sequence instead of adding them. Example:
   ##
   ## .. code-block:: nimrod
   ##   var inputStrings = newSeq[string](3)
@@ -999,11 +999,17 @@ type
     ## platform-dependant in general.
 
 when defined(windows):
-  type clong* {.importc: "long", nodecl.} = int32
-    ## This is the same as the type ``long`` in *C*.
+  type
+    clong* {.importc: "long", nodecl.} = int32
+      ## This is the same as the type ``long`` in *C*.
+    culong* {.importc: "unsigned long", nodecl.} = uint32
+      ## This is the same as the type ``unsigned long`` in *C*.
 else:
-  type clong* {.importc: "long", nodecl.} = int
-    ## This is the same as the type ``long`` in *C*.
+  type
+    clong* {.importc: "long", nodecl.} = int
+      ## This is the same as the type ``long`` in *C*.
+    culong* {.importc: "unsigned long", nodecl.} = uint
+      ## This is the same as the type ``unsigned long`` in *C*.
 
 type # these work for most platforms:
   cchar* {.importc: "char", nodecl.} = char
@@ -1032,8 +1038,6 @@ type # these work for most platforms:
     ## This is the same as the type ``unsigned short`` in *C*.
   cuint* {.importc: "int", nodecl.} = uint32
     ## This is the same as the type ``unsigned int`` in *C*.
-  culong* {.importc: "unsigned long", nodecl.} = uint
-    ## This is the same as the type ``unsigned long`` in *C*.
   culonglong* {.importc: "unsigned long long", nodecl.} = uint64
     ## This is the same as the type ``unsigned long long`` in *C*.
 
@@ -1042,10 +1046,10 @@ type # these work for most platforms:
     ## high value is large enough to disable bounds checking in practice.
     ## Use `cstringArrayToSeq` to convert it into a ``seq[string]``.
   
-  PFloat32* = ptr Float32 ## an alias for ``ptr float32``
-  PFloat64* = ptr Float64 ## an alias for ``ptr float64``
-  PInt64* = ptr Int64 ## an alias for ``ptr int64``
-  PInt32* = ptr Int32 ## an alias for ``ptr int32``
+  PFloat32* = ptr float32 ## an alias for ``ptr float32``
+  PFloat64* = ptr float64 ## an alias for ``ptr float64``
+  PInt64* = ptr int64 ## an alias for ``ptr int64``
+  PInt32* = ptr int32 ## an alias for ``ptr int32``
 
 proc toFloat*(i: int): float {.
   magic: "ToFloat", noSideEffect, importc: "toFloat".}
@@ -2615,12 +2619,13 @@ type
   PNimrodNode* {.magic: "PNimrodNode".} = ref TNimrodNode
     ## represents a Nimrod AST node. Macros operate on this type.
 
-template eval*(blk: stmt): stmt =
-  ## executes a block of code at compile time just as if it was a macro
-  ## optionally, the block can return an AST tree that will replace the 
-  ## eval expression
-  macro payload: stmt {.gensym.} = blk
-  payload()
+when false:
+  template eval*(blk: stmt): stmt =
+    ## executes a block of code at compile time just as if it was a macro
+    ## optionally, the block can return an AST tree that will replace the 
+    ## eval expression
+    macro payload: stmt {.gensym.} = blk
+    payload()
 
 when hostOS != "standalone":
   proc insert*(x: var string, item: string, i = 0) {.noSideEffect.} = 
diff --git a/lib/system/channels.nim b/lib/system/channels.nim
index d0294322a..9c3cc93e0 100644
--- a/lib/system/channels.nim
+++ b/lib/system/channels.nim
@@ -13,6 +13,9 @@
 ##

 ## **Note:** The current implementation of message passing is slow and does

 ## not work with cyclic data structures.

+  
+when not defined(NimString): 
+  {.error: "You must not import this module explicitly".}
 

 type

   pbytes = ptr array[0.. 0xffff, byte]

diff --git a/lib/system/threads.nim b/lib/system/threads.nim
index 7d74de92d..104ca63c1 100644
--- a/lib/system/threads.nim
+++ b/lib/system/threads.nim
@@ -39,6 +39,9 @@
 ##    createThread(thr[i], threadFunc, (i*10, i*10+5))
 ##  joinThreads(thr)
   
+when not defined(NimString): 
+  {.error: "You must not import this module explicitly".}
+
 const
   maxRegisters = 256 # don't think there is an arch with more registers
   useStackMaskHack = false ## use the stack mask hack for better performance
diff --git a/readme.md b/readme.md
index c49e24f4e..8d42c66db 100644
--- a/readme.md
+++ b/readme.md
@@ -9,7 +9,7 @@ the C source of an older version of the compiler are needed to bootstrap the
 latest version. The C sources are available in a separate repo [here](http://github.com/nimrod-code/csources).
 
 Pre-compiled snapshots of the compiler are also available on
-[Nimbuild](http://build.nimrod-code.org/). Your platform however may not 
+[Nimbuild](http://build.nimrod-lang.org/). Your platform however may not 
 currently be built for.
 
 The compiler currently supports the following platform and architecture 
@@ -47,7 +47,7 @@ The above steps can be performed on Windows in a similar fashion, the
 instead of ``build.sh``.
 
 ## Getting help
-A [forum](http://forum.nimrod-code.org/) is available if you have any
+A [forum](http://forum.nimrod-lang.org/) is available if you have any
 questions, and you can also get help in the IRC channel on
 [Freenode](irc://irc.freenode.net/nimrod) in #nimrod. If you ask questions on
 [StackOverflow use the nimrod
diff --git a/readme.txt b/readme.txt
index c49e24f4e..8d42c66db 100644
--- a/readme.txt
+++ b/readme.txt
@@ -9,7 +9,7 @@ the C source of an older version of the compiler are needed to bootstrap the
 latest version. The C sources are available in a separate repo [here](http://github.com/nimrod-code/csources).
 
 Pre-compiled snapshots of the compiler are also available on
-[Nimbuild](http://build.nimrod-code.org/). Your platform however may not 
+[Nimbuild](http://build.nimrod-lang.org/). Your platform however may not 
 currently be built for.
 
 The compiler currently supports the following platform and architecture 
@@ -47,7 +47,7 @@ The above steps can be performed on Windows in a similar fashion, the
 instead of ``build.sh``.
 
 ## Getting help
-A [forum](http://forum.nimrod-code.org/) is available if you have any
+A [forum](http://forum.nimrod-lang.org/) is available if you have any
 questions, and you can also get help in the IRC channel on
 [Freenode](irc://irc.freenode.net/nimrod) in #nimrod. If you ask questions on
 [StackOverflow use the nimrod
diff --git a/tests/compile/tgeneric.nim b/tests/compile/tgeneric.nim
index 8bda15c42..9292b729f 100644
--- a/tests/compile/tgeneric.nim
+++ b/tests/compile/tgeneric.nim
@@ -8,4 +8,12 @@ proc foo(models: seq[TTable[string, float]]): seq[float] =
   for model in models.items:
     result.add model["foobar"]
 
+# bug #686
+type TType[T; A] = array[A, T]
+
+proc foo[T](p: TType[T, range[0..1]]) =
+  echo "foo"
+proc foo[T](p: TType[T, range[0..2]]) =
+  echo "bar"
+
 
diff --git a/tests/run/tdrdobbs_examples.nim b/tests/run/tdrdobbs_examples.nim
new file mode 100644
index 000000000..d1e0585d2
--- /dev/null
+++ b/tests/run/tdrdobbs_examples.nim
@@ -0,0 +1,134 @@
+discard """
+  output: '''108
+11 -1 1936
+4.000000000000002-e001
+true
+truefalse'''
+"""
+
+proc `++`(x: var int; y: int = 1; z: int = 0) =
+  x = x + y + z
+
+var g = 70
+++g
+g ++ 7
+g.`++`(10, 20)
+echo g 
+
+
+#let lv = stdin.readline
+#var vv = stdin.readline
+#vv = "abc" # valid, reassignment allowed
+#lv = "abc" # fails at compile time
+
+#proc square(x: int): int = x*x
+
+template square(x: int): int =
+  # ensure 'x' is only evaluated once:
+  let y = x
+  y * y
+
+proc mostSignificantBit(n: int): int =
+  # naive algorithm:
+  var n = n
+  while n != 0:
+    n = n shr 1
+    result += 1
+  result -= 1
+
+const msb3999 = mostSignificantBit(3999)
+
+echo msb3999, " ", mostSignificantBit(0), " ", square(44)
+
+proc filter[T](a: openarray[T], predicate: proc (x: T): bool): seq[T] =
+  result = @[] # @[] constructs the empty seq
+  for x in a:
+    if predicate(x): result.add(x)
+
+proc map[T, S](a: openarray[T], fn: proc (x: T): S): seq[S] =
+  newSeq(result, a.len)
+  for i in 0 .. <a.len: result[i] = fn(a[i])
+
+
+type
+  FormulaKind = enum
+    fkVar,        ## element is a variable like 'X'
+    fkLit,        ## element is a literal like 0.1
+    fkAdd,        ## element is an addition operation
+    fkMul,        ## element is a multiplication operation
+    fkExp         ## element is an exponentiation operation 
+
+type
+  Formula = ref object
+    case kind: FormulaKind
+    of fkVar: name: string
+    of fkLit: value: float
+    of fkAdd, fkMul, fkExp: left, right: Formula
+
+from math import pow
+
+proc evaluate(n: Formula, varToVal: proc (name: string): float): float =
+  case n.kind
+  of fkVar: varToVal(n.name)
+  of fkLit: n.value
+  of fkAdd: evaluate(n.left, varToVal) + evaluate(n.right, varToVal)
+  of fkMul: evaluate(n.left, varToVal) * evaluate(n.right, varToVal)
+  of fkExp: pow(evaluate(n.left, varToVal), evaluate(n.right, varToVal))
+
+echo evaluate(Formula(kind: fkLit, value: 0.4), nil)
+
+proc isPolyTerm(n: Formula): bool =
+  n.kind == fkMul and n.left.kind == fkLit and (let e = n.right; 
+    e.kind == fkExp and e.left.kind == fkVar and e.right.kind == fkLit)
+
+proc isPolynomial(n: Formula): bool =
+  isPolyTerm(n) or 
+    (n.kind == fkAdd and isPolynomial(n.left) and isPolynomial(n.right))
+
+let myFormula = Formula(kind: fkMul,
+                        left: Formula(kind: fkLit, value: 2.0),
+                        right: Formula(kind: fkExp, 
+                          left: Formula(kind: fkVar, name: "x"),
+                          right: Formula(kind: fkLit, value: 5.0)))
+
+echo isPolyTerm(myFormula)
+
+proc pat2kind(pattern: string): FormulaKind =
+  case pattern
+  of "^": fkExp
+  of "*": fkMul
+  of "+": fkAdd
+  of "x": fkVar
+  of "c": fkLit
+  else:   fkVar # no error reporting for reasons of simplicity
+
+import macros
+
+proc matchAgainst(n, pattern: PNimrodNode): PNimrodNode {.compileTime.} =
+  template `@`(current, field: expr): expr =
+    newDotExpr(current, newIdentNode(astToStr(field)))
+
+  template `==@`(n, pattern: expr): expr =
+    newCall("==", n@kind, newIdentNode($pat2kind($pattern.ident)))
+
+  case pattern.kind
+  of CallNodes:
+    result = newCall("and",
+      n ==@ pattern[0],
+      matchAgainst(n@left, pattern[1]))
+    if pattern.len == 3:
+      result = newCall("and", result.copy,
+        matchAgainst(n@right, pattern[2]))
+  of nnkIdent:
+    result = n ==@ pattern
+  of nnkPar:
+    result = matchAgainst(n, pattern[0])
+  else:
+    error "invalid pattern"
+
+macro `=~` (n: Formula, pattern: expr): bool =
+  result = matchAgainst(n, pattern)
+
+proc isPolyTerm2(n: Formula): bool = n =~ c * x^c
+
+echo isPolyTerm2(myFormula), isPolyTerm2(Formula(kind: fkLit, value: 0.7))
diff --git a/tests/run/tvarious1.nim b/tests/run/tvarious1.nim
index 9dd4af606..6e4612ae3 100644
--- a/tests/run/tvarious1.nim
+++ b/tests/run/tvarious1.nim
@@ -2,7 +2,8 @@ discard """
   file: "tlenopenarray.nim"
   output: '''1
 0
-Whopie'''
+Whopie
+12'''
 """
 
 echo len([1_000_000]) #OUT 1
@@ -27,3 +28,14 @@ var w = TWidget(names: initQueue[string]())
 add(w.names, "Whopie")
 
 for n in w.names: echo(n)
+
+# bug #681
+
+type TSomeRange = object
+  hour: range[0..23]
+
+var value: string
+var val12 = TSomeRange(hour: 12)
+
+value = $(if val12.hour > 12: val12.hour - 12 else: val12.hour)
+echo value