summary refs log tree commit diff stats
diff options
context:
space:
mode:
-rwxr-xr-xcompiler/ast.nim2
-rwxr-xr-xcompiler/evals.nim3
-rwxr-xr-xlib/core/macros.nim109
-rw-r--r--lib/pure/memfiles.nim203
-rwxr-xr-xlib/windows/windows.nim3
-rwxr-xr-xweb/news.txt4
6 files changed, 252 insertions, 72 deletions
diff --git a/compiler/ast.nim b/compiler/ast.nim
index eca51f704..1bf73fbec 100755
--- a/compiler/ast.nim
+++ b/compiler/ast.nim
@@ -341,7 +341,7 @@ type
   TMagic* = enum # symbols that require compiler magic:
     mNone, mDefined, mDefinedInScope, mLow, mHigh, mSizeOf, mIs, mOf,
     mEcho, mShallowCopy, mSlurp,
-    mAstToYaml, mParseExprToAst, mParseStmtToAst, mExpandToAst,
+    mParseExprToAst, mParseStmtToAst, mExpandToAst,
     mUnaryLt, mSucc, 
     mPred, mInc, mDec, mOrd, mNew, mNewFinalize, mNewSeq, mLengthOpenArray, 
     mLengthStr, mLengthArray, mLengthSeq, mIncl, mExcl, mCard, mChr, mGCref, 
diff --git a/compiler/evals.nim b/compiler/evals.nim
index 68a99a0a1..76bbf4c0a 100755
--- a/compiler/evals.nim
+++ b/compiler/evals.nim
@@ -1118,9 +1118,6 @@ proc evalMagicOrCall(c: PEvalContext, n: PNode): PNode =
     result = evalAux(c, n.sons[1], {})
     if isSpecial(result): return
     result = newStrNodeT(result.info.toFileLineCol, n)
-  of mAstToYaml:
-    var ast = evalAux(c, n.sons[1], {efLValue})
-    result = newStrNode(nkStrLit, ast.treeToYaml.ropeToStr)
   of mNHint: 
     result = evalAux(c, n.sons[1], {})
     if isSpecial(result): return 
diff --git a/lib/core/macros.nim b/lib/core/macros.nim
index 8a589896d..2e75e8d5d 100755
--- a/lib/core/macros.nim
+++ b/lib/core/macros.nim
@@ -189,40 +189,8 @@ proc toStrLit*(n: PNimrodNode): PNimrodNode {.compileTime.} =
   return newStrLitNode(repr(n))

 

 proc lineinfo*(n: PNimrodNode): string {.magic: "NLineInfo".}

-

-proc toLisp*(n: PNimrodNode): string {.compileTime.} =

-  ## Convert the AST `n` to a human-readable string

-  ##

-  ## 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

-  ## a certain expression/statement

-

-  if n == nil: return "nil"

-

-  result = $n.kind

-  add(result, "(")

-  

-  case n.kind

-  of nnkEmpty: nil # same as nil node in this representation

-  of nnkNilLit: add(result, "nil")

-  of nnkCharLit..nnkInt64Lit: add(result, $n.intVal)

-  of nnkFloatLit..nnkFloat64Lit: add(result, $n.floatVal)

-  of nnkStrLit..nnkTripleStrLit: add(result, $n.strVal)

-  of nnkIdent: add(result, $n.ident)

-  of nnkSym, nnkNone: assert false

-  else:

-    add(result, toLisp(n[0]))

-    for j in 1..n.len-1:

-      add(result, ", ")

-      add(result, toLisp(n[j]))

-

-  add(result, ")")

-

-proc toYaml*(n: PNimrodNode): string {.magic: "AstToYaml".}

-  ## Converts the AST `n` to an YAML string

-  ##

-  ## Provides more detailed, potentially harder to digest information

-  ## than `toLisp`

+  ## returns the position the node appears in the original source file

+  ## in the form filename(line, col)

 

 proc parseExpr*(s: string): expr {.magic: "ParseExprToAst".}

   ## Compiles the passed string to its AST representation.

@@ -275,8 +243,8 @@ proc newCall*(theProc: string,
   result.add(newIdentNode(theProc))

   result.add(args)

 

-proc nestList*(theProc: TNimrodIdent,  

-               x: PNimrodNode): PNimrodNode {.compileTime.} = 

+proc nestList*(theProc: TNimrodIdent,

+               x: PNimrodNode): PNimrodNode {.compileTime.} =

   ## nests the list `x` into a tree of call expressions:

   ## ``[a, b, c]`` is transformed into ``theProc(a, theProc(c, d))``

   var L = x.len

@@ -285,3 +253,72 @@ proc nestList*(theProc: TNimrodIdent,
   for i in countdown(L-3, 0):

     a = newCall(theProc, x[i], copyNimTree(a))

 

+proc treeRepr*(n: PNimrodNode): string {.compileTime.} =

+  ## Convert the AST `n` to a human-readable tree-like string

+  ##

+  ## see also `repr` and `lispRepr`

+

+  proc traverse(res: var string, level: int, n: PNimrodNode) =

+    for i in 0..level-1: res.add "  "

+    

+    if n == nil:

+      res.add "nil"

+    else:

+      res.add(($n.kind).substr(3))

+      

+      case n.kind

+      of nnkEmpty: nil # same as nil node in this representation

+      of nnkNilLit: res.add(" nil")

+      of nnkCharLit..nnkInt64Lit: res.add(" " & $n.intVal)

+      of nnkFloatLit..nnkFloat64Lit: res.add(" " & $n.floatVal)

+      of nnkStrLit..nnkTripleStrLit: res.add(" " & $n.strVal)

+      of nnkIdent: res.add(" " & $n.ident)

+      of nnkSym, nnkNone: assert false

+      else:

+        for j in 0..n.len-1:

+          res.add "\n"

+          traverse(res, level + 1, n[j])

+

+  result = ""

+  traverse(result, 0, n)

+

+proc lispRepr*(n: PNimrodNode): string {.compileTime.} =

+  ## Convert the AST `n` to a human-readable lisp-like string

+  ##

+  ## see also `repr` and `treeRepr`

+  

+  if n == nil: return "nil"

+

+  result = ($n.kind).substr(3)

+  add(result, "(")

+  

+  case n.kind

+  of nnkEmpty: nil # same as nil node in this representation

+  of nnkNilLit: add(result, "nil")

+  of nnkCharLit..nnkInt64Lit: add(result, $n.intVal)

+  of nnkFloatLit..nnkFloat64Lit: add(result, $n.floatVal)

+  of nnkStrLit..nnkTripleStrLit: add(result, $n.strVal)

+  of nnkIdent: add(result, $n.ident)

+  of nnkSym, nnkNone: assert false

+  else:

+    add(result, lispRepr(n[0]))

+    for j in 1..n.len-1:

+      add(result, ", ")

+      add(result, lispRepr(n[j]))

+

+  add(result, ")")

+

+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.

+  ##

+  ## 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

+  ## a certain expression/statement

+

+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.

+  ##

+  ## see `dumpTree`

+

diff --git a/lib/pure/memfiles.nim b/lib/pure/memfiles.nim
index ed7deeaa5..7fb73186a 100644
--- a/lib/pure/memfiles.nim
+++ b/lib/pure/memfiles.nim
@@ -9,43 +9,186 @@
 
 ## This module provides support for `memory mapped files`:idx:
 ## (Posix's `mmap`:idx:) on the different operating systems.
-## XXX Currently it is implemented with Nimrod's
-## basic IO facilities and does not use any platform specific code!
-## Oh and currently only ``fmRead`` is supported...
+
+when defined(windows):
+  import windows
+elif defined(posix):
+  import posix
+else:
+  {.error: "the memfiles module is not supported yet on your operating system!".}
+
+## mem
+## a pointer to the memory mapped file `f`. The pointer can be
+## used directly to change the contents of the file, if `f` was opened
+## with write access.
+
+## size
+## size of the memory mapped file `f`.
+
+when defined(windows):
+  type Tsize = int64
+else:
+  type Tsize = int
 
 type
   TMemFile* = object {.pure.} ## represents a memory mapped file
-    file: TFile
-    buffer: pointer
-    fileLen: int
-  
-proc open*(f: var TMemFile, filename: string, mode: TFileMode = fmRead): bool =
+    mem*: pointer # XXX: The compiler won't let me add comments on the next line
+    size*: Tsize
+
+    when defined(windows):
+      fHandle: int
+      mapHandle: int 
+    else:
+      handle: cint
+
+proc open*(
+  f           : var TMemFile,
+  filename    : string,
+  mode        : TFileMode = fmRead,
+  mappedSize  : int       = -1,
+  offset      : Tsize     = 0,
+  newFileSize : Tsize     = -1 ): bool =
   ## open a memory mapped file `f`. Returns true for success.
-  assert mode == fmRead
-  result = open(f.file, filename, mode)
-
-  var len = getFileSize(f.file)
-  if len < high(int):
-    f.fileLen = int(len)
-    f.buffer = alloc(f.fileLen)
-    if readBuffer(f.file, f.buffer, f.fileLen) != f.fileLen:
-      raise newException(EIO, "error while reading from file")
+
+  # The file can be resized only when write mode is used
+  assert newFileSize == -1 or mode != fmRead
+  var readonly = mode == fmRead
+
+  template rollback =
+    f.mem = nil
+    f.size = 0
+
+  when defined(windows):
+    template fail(msg: expr) =
+      rollback()
+      if f.fHandle != 0: discard CloseHandle(f.fHandle)
+      if f.mapHandle != 0: discard CloseHandle(f.mapHandle)
+      # return false
+      raise newException(EIO, msg)
+      
+    f.fHandle = CreateFileA(
+      filename,
+      if readonly: GENERIC_READ else: GENERIC_ALL,
+      FILE_SHARE_READ,
+      nil,
+      if newFileSize != -1: CREATE_ALWAYS else: OPEN_EXISTING,
+      if readonly: FILE_ATTRIBUTE_READONLY else: FILE_ATTRIBUTE_TEMPORARY,
+      0)
+
+    if f.fHandle == INVALID_HANDLE_VALUE:
+      fail "error opening file"
+
+    if newFileSize != -1:
+      var 
+        sizeHigh = int32(newFileSize shr 32)
+        sizeLow  = int32(newFileSize and 0xffffffff)
+
+      var status = SetFilePointer(f.fHandle, sizeLow, addr(sizeHigh), FILE_BEGIN)
+      if (status == INVALID_SET_FILE_POINTER and GetLastError() != NO_ERROR) or
+         (SetEndOfFile(f.fHandle) == 0):
+        fail "error setting file size"
+
+    f.mapHandle = CreateFileMapping(
+      f.fHandle, nil,
+      if readonly: PAGE_READONLY else: PAGE_READWRITE,
+      0, 0, nil)
+
+    if f.mapHandle == 0:
+      fail "error creating mapping"
+
+    f.mem = MapViewOfFileEx(
+      f.mapHandle,
+      if readonly: FILE_MAP_READ else: FILE_MAP_WRITE,
+      int32(offset shr 32),
+      int32(offset and 0xffffffff),
+      if mappedSize == -1: 0 else: mappedSize,
+      nil)
+
+    if f.mem == nil:
+      fail "error mapping view"
+
+    var hi, low: int32
+    low = GetFileSize(f.fHandle, addr(hi))
+    if low == INVALID_FILE_SIZE:
+      fail "error getting file size"
+    else:
+      var fileSize = (int64(hi) shr 32) or low
+      f.size = if mappedSize != -1: min(fileSize, mappedSize) else: fileSize
+
+    result = true
+
   else:
-    raise newException(EIO, "file too big to fit in memory")
+    template fail(msg: expr) =
+      rollback()
+      if f.handle != 0:
+        discard close(f.handle)
+      # return false
+      raise newException(system.EIO, msg)
+  
+    var flags = if readonly: O_RDONLY else: O_RDWR
+
+    if newFileSize != -1:
+      flags = flags or O_CREAT or O_TRUNC
+
+    f.handle = open(filename, flags)
+    if f.handle == -1:
+      # XXX: errno is supposed to be set here
+      # Is there an exception that wraps it?
+      fail "error opening file"
+
+    if newFileSize != -1:
+      if ftruncate(f.handle, newFileSize) == -1:
+        fail "error setting file size"
+
+    if mappedSize != -1:
+      f.size = mappedSize
+    else:
+      var stat: Tstat
+      if fstat(f.handle, stat) != -1:
+        # XXX: Hmm, this could be unsafe
+        # Why is mmap taking int anyway?
+        f.size = int(stat.st_size)
+      else:
+        fail "error getting file size"
+
+    f.mem = mmap(
+      nil,
+      f.size,
+      if readonly: PROT_READ else: PROT_READ or PROT_WRITE,
+      if readonly: MAP_PRIVATE else: MAP_SHARED,
+      f.handle,
+      offset)
+
+    if f.mem == cast[pointer](MAP_FAILED):
+      fail "file mapping failed"
+
+    result = true
 
 proc close*(f: var TMemFile) =
   ## closes the memory mapped file `f`. All changes are written back to the
   ## file system, if `f` was opened with write access.
-  dealloc(f.buffer)
-  close(f.file)
-
-proc mem*(f: var TMemFile): pointer {.inline.} =
-  ## retrives a pointer to the memory mapped file `f`. The pointer can be
-  ## used directly to change the contents of the file, if `f` was opened
-  ## with write access.
-  result = f.buffer
-
-proc size*(f: var TMemFile): int {.inline.} =
-  ## retrives the size of the memory mapped file `f`.
-  result = f.fileLen
+  
+  var error = false
+
+  when defined(windows):
+    if f.fHandle != INVALID_HANDLE_VALUE:
+      error = UnmapViewOfFile(f.mem) == 0
+      error = (CloseHandle(f.mapHandle) == 0) or error
+      error = (CloseHandle(f.fHandle) == 0) or error
+  else:
+    if f.handle != 0:
+      error = munmap(f.mem, f.size) != 0
+      error = (close(f.handle) != 0) or error
+
+  f.size = 0
+  f.mem = nil
+
+  when defined(windows):
+    f.fHandle = 0
+    f.mapHandle = 0
+  else:
+    f.handle = 0
+  
+  if error:
+    raise newException(system.EIO, "error closing file")
 
diff --git a/lib/windows/windows.nim b/lib/windows/windows.nim
index c8c977d9b..e93ffcf31 100755
--- a/lib/windows/windows.nim
+++ b/lib/windows/windows.nim
@@ -3104,6 +3104,9 @@ const
   STD_ERROR_HANDLE* =  DWORD(-12)

 

   INVALID_HANDLE_VALUE* = HANDLE(-1)

+  INVALID_SET_FILE_POINTER* = ULONG(-1)

+  INVALID_FILE_SIZE* = ULONG(-1)

+  INVALID_FILE_ATTRIBUTES* = ULONG(-1)

 

 const

   # GetStockObject

diff --git a/web/news.txt b/web/news.txt
index c9accb9c8..46c930fdc 100755
--- a/web/news.txt
+++ b/web/news.txt
@@ -101,8 +101,8 @@ Library Additions
 - Added ``xmltree.innerText``.
 - Added ``os.isAbsolute``.
 - Added ``parseutils.interpolatedFragments``.
-- Added ``macros.toLisp``, ``macros.toYaml``, ``macros.parseExpr``,
-  ``macros.parseStmt``, ``macros.getAst``.
+- Added ``macros.treeRepr``, ``macros.lispRepr``, ``macros.dumpTree``, 
+  ``macros.dumpLisp``, ``macros.parseExpr``, ``macros.parseStmt``, ``macros.getAst``.
 - Added ``locks`` core module for more flexible locking support.
 - Added ``irc`` module.
 - Added ``ftpclient`` module.