summary refs log tree commit diff stats
diff options
context:
space:
mode:
authorAraq <rumpf_a@web.de>2012-07-05 00:03:33 +0200
committerAraq <rumpf_a@web.de>2012-07-05 00:03:33 +0200
commit36247e0947699a56d5bc51d48188b6dda1815587 (patch)
treeaf837b557356d4495ce93d5fe735c9ccab169e2e
parent8ef48a34e5916deccd58a312228c7d2efe39d170 (diff)
downloadNim-36247e0947699a56d5bc51d48188b6dda1815587.tar.gz
added devel/logging; weakrefs test; next steps for proper unsigned support
-rwxr-xr-xcompiler/evals.nim2
-rwxr-xr-xcompiler/msgs.nim2
-rwxr-xr-xcompiler/renderer.nim4
-rwxr-xr-xcompiler/semexprs.nim1
-rwxr-xr-xcompiler/semfold.nim5
-rwxr-xr-xcompiler/sigmatch.nim6
-rwxr-xr-xcompiler/transf.nim13
-rw-r--r--devel/logging.nim145
-rwxr-xr-xdoc/manual.txt30
-rwxr-xr-xlib/system/repr.nim5
-rwxr-xr-xlib/system/sysio.nim2
-rw-r--r--tests/gc/weakrefs.nim48
-rw-r--r--tests/specials.nim2
-rwxr-xr-xweb/news.txt3
14 files changed, 243 insertions, 25 deletions
diff --git a/compiler/evals.nim b/compiler/evals.nim
index adff11567..40f1f8e01 100755
--- a/compiler/evals.nim
+++ b/compiler/evals.nim
@@ -829,7 +829,7 @@ proc isEmpty(n: PNode): bool =
 # Maybe the lexer should mark both the beginning and the end of expressions,
 # then this function could be removed.
 proc stringStartingLine(s: PNode): int =
-  result = s.info.line - countLines(s.strVal)
+  result = s.info.line.int - countLines(s.strVal)
 
 proc evalParseExpr(c: PEvalContext, n: PNode): PNode =
   var code = evalAux(c, n.sons[1], {})
diff --git a/compiler/msgs.nim b/compiler/msgs.nim
index efd83cfd9..03ee28df3 100755
--- a/compiler/msgs.nim
+++ b/compiler/msgs.nim
@@ -345,7 +345,7 @@ const
     warnAnalysisLoophole: "thread analysis incomplete due to unkown call '$1' [AnalysisLoophole]",
     warnDifferentHeaps: "possible inconsistency of thread local heaps [DifferentHeaps]",
     warnWriteToForeignHeap: "write to foreign heap [WriteToForeignHeap]",
-    warnImplicitNarrowing: "implicit narrowing conversion: '$1'",
+    warnImplicitNarrowing: "implicit narrowing conversion: '$1' [ImplicitNarrowing]",
     warnUser: "$1 [User]", 
     hintSuccess: "operation successful [Success]", 
     hintSuccessX: "operation successful ($# lines compiled; $# sec total; $#) [SuccessX]", 
diff --git a/compiler/renderer.nim b/compiler/renderer.nim
index 30daf7c52..58c60834f 100755
--- a/compiler/renderer.nim
+++ b/compiler/renderer.nim
@@ -1209,8 +1209,8 @@ proc initTokRender(r: var TSrcGen, n: PNode, renderFlags: TRenderFlags = {}) =
 proc getNextTok(r: var TSrcGen, kind: var TTokType, literal: var string) = 
   if r.idx < len(r.tokens): 
     kind = r.tokens[r.idx].kind
-    var length = r.tokens[r.idx].length
-    literal = substr(r.buf, r.pos + 0, r.pos + 0 + length - 1)
+    var length = r.tokens[r.idx].length.int
+    literal = substr(r.buf, r.pos, r.pos + length - 1)
     inc(r.pos, length)
     inc(r.idx)
   else: 
diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim
index 501b7ed72..22af8b4e0 100755
--- a/compiler/semexprs.nim
+++ b/compiler/semexprs.nim
@@ -1302,6 +1302,7 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode =
   of nkNilLit: 
     result.typ = getSysType(tyNil)
   of nkIntLit: 
+    # XXX this is stupid:
     if result.typ == nil: 
       let i = result.intVal
       if i >= low(int32) and i <= high(int32):
diff --git a/compiler/semfold.nim b/compiler/semfold.nim
index 66d7e98fc..ce9e03513 100755
--- a/compiler/semfold.nim
+++ b/compiler/semfold.nim
@@ -190,8 +190,8 @@ proc evalOp(m: TMagic, n, a, b, c: PNode): PNode =
   of mCopyStr: result = newStrNodeT(substr(getStr(a), int(getOrdValue(b))), n)
   of mCopyStrLast: 
     result = newStrNodeT(substr(getStr(a), int(getOrdValue(b)), 
-                                         int(getOrdValue(c))), n)
-  of mFloatToStr: result = newStrNodeT($(getFloat(a)), n)
+                                           int(getOrdValue(c))), n)
+  of mFloatToStr: result = newStrNodeT($getFloat(a), n)
   of mCStrToStr, mCharToStr: result = newStrNodeT(getStrOrChar(a), n)
   of mStrToStr: result = a
   of mEnumToStr: result = newStrNodeT(ordinalValToString(a), n)
@@ -298,6 +298,7 @@ proc getAppType(n: PNode): PNode =
     result = newStrNodeT("console", n)
 
 proc foldConv*(n, a: PNode): PNode = 
+  # XXX range checks?
   case skipTypes(n.typ, abstractRange).kind
   of tyInt..tyInt64: 
     case skipTypes(a.typ, abstractRange).kind
diff --git a/compiler/sigmatch.nim b/compiler/sigmatch.nim
index 7a5d959df..6819bb37f 100755
--- a/compiler/sigmatch.nim
+++ b/compiler/sigmatch.nim
@@ -162,7 +162,7 @@ proc handleRange(f, a: PType, min, max: TTypeKind): TTypeRelation =
   else:
     var k = skipTypes(a, {tyRange}).kind
     if k == f.kind: result = isSubtype
-    elif k == tyInt:
+    elif k == tyInt and f.kind in {tyRange, tyInt8..tyUInt64}:
       # and a.n != nil and a.n.intVal >= firstOrd(f) and
       #                    a.n.intVal <= lastOrd(f):
       # integer literal in the proper range; we want ``i16 + 4`` to stay an
@@ -188,7 +188,9 @@ proc handleFloatRange(f, a: PType): TTypeRelation =
   else: 
     var k = skipTypes(a, {tyRange}).kind
     if k == f.kind: result = isSubtype
-    elif (k >= tyFloat) and (k <= tyFloat128): result = isConvertible
+    elif k == tyInt and f.kind >= tyFloat and f.kind <= tyFloat128:
+      result = isIntConv
+    elif k >= tyFloat and k <= tyFloat128: result = isConvertible
     else: result = isNone
   
 proc isObjectSubtype(a, f: PType): bool =
diff --git a/compiler/transf.nim b/compiler/transf.nim
index 19530b82a..bf6354665 100755
--- a/compiler/transf.nim
+++ b/compiler/transf.nim
@@ -640,6 +640,14 @@ proc dontInlineConstant(orig, cnst: PNode): bool {.inline.} =
   result = orig.kind == nkSym and cnst.kind in {nkCurly, nkPar, nkBracket} and 
       cnst.len != 0
 
+proc warnNarrowingConversion(n: PNode) =
+  if n.kind == nkHiddenStdConv:
+    var dest = skipTypes(n.typ, abstractVarRange)
+    var source = skipTypes(n.sons[1].typ, abstractVarRange)
+    if source.kind == tyInt and
+       source.size > dest.size and n.sons[1].kind != nkIntLit:
+      Message(n.info, warnImplicitNarrowing, renderTree(n.sons[1]))
+
 proc transform(c: PTransf, n: PNode): PTransNode = 
   case n.kind
   of nkSym: 
@@ -725,8 +733,9 @@ proc transform(c: PTransf, n: PNode): PTransNode =
   var cnst = getConstExpr(c.module, PNode(result))
   # we inline constants if they are not complex constants:
   if cnst != nil and not dontInlineConstant(n, cnst):
-    result = PTransNode(cnst) # do not miss an optimization  
- 
+    result = PTransNode(cnst) # do not miss an optimization
+  warnNarrowingConversion(result.pnode)
+
 proc processTransf(context: PPassContext, n: PNode): PNode = 
   # Note: For interactive mode we cannot call 'passes.skipCodegen' and skip
   # this step! We have to rely that the semantic pass transforms too errornous
diff --git a/devel/logging.nim b/devel/logging.nim
new file mode 100644
index 000000000..7ae4d7eee
--- /dev/null
+++ b/devel/logging.nim
@@ -0,0 +1,145 @@
+#
+#
+#            Nimrod's Runtime Library
+#        (c) Copyright 2012 Andreas Rumpf
+#
+#    See the file "copying.txt", included in this
+#    distribution, for details about the copyright.
+#
+
+## This module implements a simple logger. It is based on the following design:
+## * Runtime log formating is a bug: Sooner or later every log file is parsed.
+## * Keep it simple: If this library does not fullfill your needs, write your 
+##   own. Trying to support every logging feature just leads to bloat.
+## 
+## Format is:: 
+##
+##   DEBUG|INFO|... (2009-11-02 00:00:00)? (Component: )? Message
+##
+## 
+
+type
+  TLevel* = enum  ## logging level
+    lvlAll,       ## all levels active
+    lvlDebug,     ## debug level (and any above) active
+    lvlInfo,      ## info level (and any above) active
+    lvlWarn,      ## warn level (and any above) active
+    lvlError,     ## error level (and any above) active
+    lvlFatal      ## fatal level (and any above) active
+
+const
+  LevelNames*: array [TLevel, string] = [
+    "DEBUG", "DEBUG", "INFO", "WARN", "ERROR", "FATAL"
+  ]
+
+type
+  TLogger* = object of TObject ## abstract logger; the base type of all loggers
+    levelThreshold*: TLevel    ## only messages of level >= levelThreshold 
+                               ## should be processed
+  TConsoleLogger* = object of TLogger ## logger that writes the messages to the
+                                      ## console
+  
+  TFileLogger* = object of TLogger ## logger that writes the messages to a file
+    f: TFile
+    
+  TRollingFileLogger* = object of TFileLogger ## logger that writes the 
+                                              ## message to a file
+    maxlines: int # maximum number of lines
+    lines: seq[string]
+
+method log*(L: ref TLogger, level: TLevel,
+            frmt: string, args: openArray[string]) =
+  ## override this method in custom loggers. Default implementation does
+  ## nothing.
+  nil
+  
+method log*(L: ref TConsoleLogger, level: TLevel,
+            frmt: string, args: openArray[string]) = 
+  Writeln(stdout, LevelNames[level], " ", frmt % args)
+
+method log*(L: ref TFileLogger, level: TLevel, 
+            frmt: string, args: openArray[string]) = 
+  Writeln(L.f, LevelNames[level], " ", frmt % args)
+
+proc defaultFilename*(): string = 
+  ## returns the default filename for a logger
+  var (path, name, ext) = splitFile(getAppFilename())
+  result = changeFileExt(path / name & "_" & getDateStr(), "log")
+
+proc substituteLog*(frmt: string): string = 
+  ## converts $date to the current date
+  ## converts $time to the current time
+  ## converts $app to getAppFilename()
+  ## converts 
+  result = newStringOfCap(frmt.len + 20)
+  var i = 0
+  while i < frmt.len: 
+    if frmt[i] != '$': 
+      result.add(frmt[i])
+      inc(i)
+    else:
+      inc(i)
+      var v = ""
+      var app = getAppFilename()
+      while frmt[i] in IdentChars: 
+        v.add(toLower(frmt[i]))
+        inc(i)
+      case v
+      of "date": result.add(getDateStr())
+      of "time": result.add(getClockStr())
+      of "app":  result.add(app)
+      of "appdir": result.add(app.splitFile.dir)
+      of "appname": result.add(app.splitFile.name)
+      
+
+proc newFileLogger*(filename = defaultFilename(), 
+                    mode: TFileMode = fmAppend,
+                    levelThreshold = lvlNone): ref TFileLogger = 
+  new(result)
+  result.levelThreshold = levelThreshold
+  result.f = open(filename, mode)
+
+proc newRollingFileLogger*(filename = defaultFilename(), 
+                           mode: TFileMode = fmAppend,
+                           levelThreshold = lvlNone,
+                           maxLines = 1000): ref TFileLogger = 
+  new(result)
+  result.levelThreshold = levelThreshold
+  result.maxLines = maxLines
+  result.f = open(filename, mode)
+
+var
+  level* = lvlNone
+  handlers*: seq[ref TLogger] = @[]
+
+proc logLoop(level: TLevel, msg: string) =
+  for logger in items(handlers): 
+    if level >= logger.levelThreshold:
+      log(logger, level, msg)
+
+template log*(level: TLevel, msg: string) =
+  ## logs a message of the given level
+  bind logLoop
+  if level >= logging.Level:
+    logLoop(level, frmt, args)
+
+template debug*(msg: string) =
+  ## logs a debug message
+  log(lvlDebug, msg)
+
+template info*(msg: string) = 
+  ## logs an info message
+  log(lvlInfo, msg)
+
+template warn*(msg: string) = 
+  ## logs a warning message
+  log(lvlWarn, msg)
+
+template error*(msg: string) = 
+  ## logs an error message
+  log(lvlError, msg)
+  
+template fatal*(msg: string) =  
+  ## logs a fatal error message
+  log(lvlFatal, msg)
+
diff --git a/doc/manual.txt b/doc/manual.txt
index 4f7e8db15..02c1294e5 100755
--- a/doc/manual.txt
+++ b/doc/manual.txt
@@ -794,7 +794,7 @@ variadic proc, it is implicitely converted to ``cstring`` too:
   

   printf("This works %s", "as expected")

 

-Even though the conversion is implict, it is not *safe*: The garbage collector

+Even though the conversion is implicit, it is not *safe*: The garbage collector

 does not consider a ``cstring`` to be a root and may collect the underlying 

 memory. However in practice this almost never happens as the GC considers

 stack roots conservatively. One can use the builtin procs ``GC_ref`` and

@@ -1461,22 +1461,28 @@ algorithm returns true:
   # XXX range types?

   proc isImplicitlyConvertible(a, b: PType): bool =

     case a.kind

-    of int8:    result = b.kind in {int16, int32, int64, int}

-    of int16:   result = b.kind in {int32, int64, int}

-    of int32:   result = b.kind in {int64, int}

-    of float:   result = b.kind in {float32, float64}

-    of float32: result = b.kind in {float64, float}

-    of float64: result = b.kind in {float32, float}

+    of int:     result = b in {int8, int16, int32, int64, uint, uint8, uint16,
+                               uint32, uint64, float, float32, float64}
+    of int8:    result = b in {int16, int32, int64, int}

+    of int16:   result = b in {int32, int64, int}

+    of int32:   result = b in {int64, int}

+    of uint:    result = b in {uint32, uint64}
+    of uint8:   result = b in {uint16, uint32, uint64}
+    of uint16:  result = b in {uint32, uint64}
+    of uint32:  result = b in {uint64}
+    of float:   result = b in {float32, float64}

+    of float32: result = b in {float64, float}

+    of float64: result = b in {float32, float}

     of seq:

-      result = b.kind == openArray and typeEquals(a.baseType, b.baseType)

+      result = b == openArray and typeEquals(a.baseType, b.baseType)

     of array:

-      result = b.kind == openArray and typeEquals(a.baseType, b.baseType)

+      result = b == openArray and typeEquals(a.baseType, b.baseType)

       if a.baseType == char and a.indexType.rangeA == 0:

-        result = b.kind = cstring

+        result = b = cstring

     of cstring, ptr:

-      result = b.kind == pointer

+      result = b == pointer

     of string:

-      result = b.kind == cstring

+      result = b == cstring

 

 A type ``a`` is **explicitly** convertible to type ``b`` iff the following

 algorithm returns true:

diff --git a/lib/system/repr.nim b/lib/system/repr.nim
index ec02f5e08..83fa7aa1d 100755
--- a/lib/system/repr.nim
+++ b/lib/system/repr.nim
@@ -75,7 +75,7 @@ proc addSetElem(result: var string, elem: int, typ: PNimType) =
   of tyBool: add result, reprBool(bool(elem))
   of tyChar: add result, reprChar(chr(elem))
   of tyRange: addSetElem(result, elem, typ.base)
-  of tyInt..tyInt64: add result, reprInt(elem)
+  of tyInt..tyInt64, tyUInt8, tyUInt16: add result, reprInt(elem)
   else: # data corrupt --> inform the user
     add result, " (invalid data!)"
 
@@ -221,6 +221,9 @@ when not defined(useNimRtl):
     of tyInt16: add result, $int(cast[ptr Int16](p)[])
     of tyInt32: add result, $int(cast[ptr Int32](p)[])
     of tyInt64: add result, $(cast[ptr Int64](p)[])
+    of tyUInt8: add result, $ze(cast[ptr Int8](p)[])
+    of tyUInt16: add result, $ze(cast[ptr Int16](p)[])
+    
     of tyFloat: add result, $(cast[ptr float](p)[])
     of tyFloat32: add result, $(cast[ptr float32](p)[])
     of tyFloat64: add result, $(cast[ptr float64](p)[])
diff --git a/lib/system/sysio.nim b/lib/system/sysio.nim
index 15f87896a..ac0880f79 100755
--- a/lib/system/sysio.nim
+++ b/lib/system/sysio.nim
@@ -199,7 +199,7 @@ proc Open(f: var TFile, filename: string,
   var p: pointer = fopen(filename, FormatOpen[mode])
   result = (p != nil)
   f = cast[TFile](p)
-  if bufSize > 0 and bufSize <= high(cint):
+  if bufSize > 0 and bufSize <= high(cint).int:
     if setvbuf(f, nil, IOFBF, bufSize.cint) != 0'i32:
       raise newException(EOutOfMemory, "out of memory")
   elif bufSize == 0:
diff --git a/tests/gc/weakrefs.nim b/tests/gc/weakrefs.nim
new file mode 100644
index 000000000..3b12d7998
--- /dev/null
+++ b/tests/gc/weakrefs.nim
@@ -0,0 +1,48 @@
+discard """
+  output: "true"
+"""
+
+import intsets
+
+type
+  TMyObject = object
+    id: int
+  StrongObject = ref TMyObject
+  WeakObject = object
+    id: int
+    data: ptr TMyObject
+
+var
+  gid: int # for id generation
+  valid = initIntSet()
+
+proc finalizer(x: StrongObject) =
+  valid.excl(x.id)
+
+proc create: StrongObject =
+  new(result, finalizer)
+  result.id = gid
+  valid.incl(gid)
+  inc gid
+
+proc register(s: StrongObject): WeakObject =
+  result.data = cast[ptr TMyObject](s)
+  result.id = s.id
+
+proc access(w: WeakObject): StrongObject =
+  ## returns nil if the object doesn't exist anymore
+  if valid.contains(w.id):
+    result = cast[StrongObject](w.data)
+
+proc main =
+  var s: seq[WeakObject]
+  newSeq(s, 10_000)
+  for i in 0 .. s.high:
+    s[i] = register(create())
+  # test that we have at least 80% unreachable weak objects by now:
+  var unreachable = 0
+  for i in 0 .. s.high:
+    if access(s[i]) == nil: inc unreachable
+  echo unreachable > 8_000
+
+main()
diff --git a/tests/specials.nim b/tests/specials.nim
index 7e1a58d78..f0f471c02 100644
--- a/tests/specials.nim
+++ b/tests/specials.nim
@@ -131,7 +131,7 @@ proc runGcTests(r: var TResults, options: string) =
   test "gctest"
   # disabled for now as it somehow runs very slowly ('delete' bug?) but works:
   test "gcleak3"
-  
+  test "weakrefs"
 
 # ------------------------- threading tests -----------------------------------
 
diff --git a/web/news.txt b/web/news.txt
index aa255a0b1..d4a03488c 100755
--- a/web/news.txt
+++ b/web/news.txt
@@ -87,6 +87,7 @@ Changes affecting backwards compatibility
 - Deprecated the ``ssl`` module.
 - Deprecated ``nimrod pretty`` as it never worked good enough and has some
   inherent problems.
+- The integer promotion rules changed.
 
 
 Compiler Additions
@@ -120,6 +121,8 @@ Language Additions
 - Stand-alone ``finally`` and ``except`` blocks are now supported.
 - Macros and templates can now be invoked as pragmas.
 - The apostrophe in type suffixes for numerical literal is now optional.
+- Unsigned integer types have been added.
+- The integer promotion rules changed.
 
 
 2012-02-09 Version 0.8.14 released