Merge branch 'devel' of https://github.com/Araq/Nimrod into devel
This commit is contained in:
commit
2c97242739
13 changed files with 369 additions and 59 deletions
|
|
@ -404,7 +404,7 @@ proc evalOp(m: TMagic, n, a, b, c: PNode): PNode =
|
||||||
mExit, mInc, ast.mDec, mEcho, mSwap, mAppendStrCh,
|
mExit, mInc, ast.mDec, mEcho, mSwap, mAppendStrCh,
|
||||||
mAppendStrStr, mAppendSeqElem, mSetLengthStr, mSetLengthSeq,
|
mAppendStrStr, mAppendSeqElem, mSetLengthStr, mSetLengthSeq,
|
||||||
mParseExprToAst, mParseStmtToAst, mExpandToAst, mTypeTrait,
|
mParseExprToAst, mParseStmtToAst, mExpandToAst, mTypeTrait,
|
||||||
mNLen..mNError, mEqRef, mSlurp, mStaticExec, mNGenSym:
|
mNLen..mNError, mEqRef, mSlurp, mStaticExec, mNGenSym, mSpawn:
|
||||||
discard
|
discard
|
||||||
of mRand:
|
of mRand:
|
||||||
result = newIntNodeT(math.random(a.getInt.int), n)
|
result = newIntNodeT(math.random(a.getInt.int), n)
|
||||||
|
|
|
||||||
|
|
@ -253,6 +253,7 @@ proc findUsages(node: PNode, s: PSym) =
|
||||||
lastLineInfo = node.info
|
lastLineInfo = node.info
|
||||||
|
|
||||||
proc findDefinition(node: PNode, s: PSym) =
|
proc findDefinition(node: PNode, s: PSym) =
|
||||||
|
if node.isNil or s.isNil: return
|
||||||
if isTracked(node.info, s.name.s.len):
|
if isTracked(node.info, s.name.s.len):
|
||||||
suggestWriteln(symToStr(s, isLocal=false, sectionDef))
|
suggestWriteln(symToStr(s, isLocal=false, sectionDef))
|
||||||
suggestQuit()
|
suggestQuit()
|
||||||
|
|
|
||||||
|
|
@ -354,6 +354,11 @@ template handleJmpBack() {.dirty.} =
|
||||||
globalError(c.debug[pc], errTooManyIterations)
|
globalError(c.debug[pc], errTooManyIterations)
|
||||||
dec(c.loopIterations)
|
dec(c.loopIterations)
|
||||||
|
|
||||||
|
proc skipColon(n: PNode): PNode =
|
||||||
|
result = n
|
||||||
|
if n.kind == nkExprColonExpr:
|
||||||
|
result = n.sons[1]
|
||||||
|
|
||||||
proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
|
proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
|
||||||
var pc = start
|
var pc = start
|
||||||
var tos = tos
|
var tos = tos
|
||||||
|
|
@ -454,7 +459,7 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
|
||||||
decodeBC(rkNode)
|
decodeBC(rkNode)
|
||||||
let src = regs[rb].node
|
let src = regs[rb].node
|
||||||
if src.kind notin {nkEmpty..nkNilLit}:
|
if src.kind notin {nkEmpty..nkNilLit}:
|
||||||
let n = src.sons[rc]
|
let n = src.sons[rc].skipColon
|
||||||
regs[ra].node = n
|
regs[ra].node = n
|
||||||
else:
|
else:
|
||||||
stackTrace(c, tos, pc, errNilAccess)
|
stackTrace(c, tos, pc, errNilAccess)
|
||||||
|
|
@ -1099,6 +1104,7 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
|
||||||
c.module)
|
c.module)
|
||||||
of opcGorge:
|
of opcGorge:
|
||||||
decodeBC(rkNode)
|
decodeBC(rkNode)
|
||||||
|
createStr regs[ra]
|
||||||
regs[ra].node.strVal = opGorge(regs[rb].node.strVal,
|
regs[ra].node.strVal = opGorge(regs[rb].node.strVal,
|
||||||
regs[rc].node.strVal)
|
regs[rc].node.strVal)
|
||||||
of opcNError:
|
of opcNError:
|
||||||
|
|
|
||||||
|
|
@ -70,6 +70,9 @@ proc echoCode*(c: PCtx, start=0) {.deprecated.} =
|
||||||
echo buf
|
echo buf
|
||||||
|
|
||||||
proc gABC(ctx: PCtx; n: PNode; opc: TOpcode; a, b, c: TRegister = 0) =
|
proc gABC(ctx: PCtx; n: PNode; opc: TOpcode; a, b, c: TRegister = 0) =
|
||||||
|
## Takes the registers `b` and `c`, applies the operation `opc` to them, and
|
||||||
|
## stores the result into register `a`
|
||||||
|
## The node is needed for debug information
|
||||||
assert opc.ord < 255
|
assert opc.ord < 255
|
||||||
let ins = (opc.uint32 or (a.uint32 shl 8'u32) or
|
let ins = (opc.uint32 or (a.uint32 shl 8'u32) or
|
||||||
(b.uint32 shl 16'u32) or
|
(b.uint32 shl 16'u32) or
|
||||||
|
|
@ -78,6 +81,10 @@ proc gABC(ctx: PCtx; n: PNode; opc: TOpcode; a, b, c: TRegister = 0) =
|
||||||
ctx.debug.add(n.info)
|
ctx.debug.add(n.info)
|
||||||
|
|
||||||
proc gABI(c: PCtx; n: PNode; opc: TOpcode; a, b: TRegister; imm: BiggestInt) =
|
proc gABI(c: PCtx; n: PNode; opc: TOpcode; a, b: TRegister; imm: BiggestInt) =
|
||||||
|
# Takes the `b` register and the immediate `imm`, appies the operation `opc`,
|
||||||
|
# and stores the output value into `a`.
|
||||||
|
# `imm` is signed and must be within [-127, 128]
|
||||||
|
assert(imm >= -127 and imm <= 128)
|
||||||
let ins = (opc.uint32 or (a.uint32 shl 8'u32) or
|
let ins = (opc.uint32 or (a.uint32 shl 8'u32) or
|
||||||
(b.uint32 shl 16'u32) or
|
(b.uint32 shl 16'u32) or
|
||||||
(imm+byteExcess).uint32 shl 24'u32).TInstr
|
(imm+byteExcess).uint32 shl 24'u32).TInstr
|
||||||
|
|
@ -85,6 +92,9 @@ proc gABI(c: PCtx; n: PNode; opc: TOpcode; a, b: TRegister; imm: BiggestInt) =
|
||||||
c.debug.add(n.info)
|
c.debug.add(n.info)
|
||||||
|
|
||||||
proc gABx(c: PCtx; n: PNode; opc: TOpcode; a: TRegister = 0; bx: int) =
|
proc gABx(c: PCtx; n: PNode; opc: TOpcode; a: TRegister = 0; bx: int) =
|
||||||
|
# Applies `opc` to `bx` and stores it into register `a`
|
||||||
|
# `bx` must be signed and in the range [-32767, 32768]
|
||||||
|
assert(bx >= -32767 and bx <= 32768)
|
||||||
let ins = (opc.uint32 or a.uint32 shl 8'u32 or
|
let ins = (opc.uint32 or a.uint32 shl 8'u32 or
|
||||||
(bx+wordExcess).uint32 shl 16'u32).TInstr
|
(bx+wordExcess).uint32 shl 16'u32).TInstr
|
||||||
c.code.add(ins)
|
c.code.add(ins)
|
||||||
|
|
@ -316,15 +326,7 @@ proc genAndOr(c: PCtx; n: PNode; opc: TOpcode; dest: var TDest) =
|
||||||
c.patch(L1)
|
c.patch(L1)
|
||||||
|
|
||||||
proc canonValue*(n: PNode): PNode =
|
proc canonValue*(n: PNode): PNode =
|
||||||
if n.kind == nkExprColonExpr:
|
result = n
|
||||||
result = n.sons[1]
|
|
||||||
elif n.hasSubnodeWith(nkExprColonExpr):
|
|
||||||
result = n.copyNode
|
|
||||||
newSeq(result.sons, n.len)
|
|
||||||
for i in 0.. <n.len:
|
|
||||||
result.sons[i] = canonValue(n.sons[i])
|
|
||||||
else:
|
|
||||||
result = n
|
|
||||||
|
|
||||||
proc rawGenLiteral(c: PCtx; n: PNode): int =
|
proc rawGenLiteral(c: PCtx; n: PNode): int =
|
||||||
result = c.constants.len
|
result = c.constants.len
|
||||||
|
|
|
||||||
66
doc/tut1.txt
66
doc/tut1.txt
|
|
@ -132,7 +132,7 @@ a backslash:
|
||||||
TMyObject {.final, pure, acyclic.} = object # comment continues: \
|
TMyObject {.final, pure, acyclic.} = object # comment continues: \
|
||||||
# we have lots of space here to comment 'TMyObject'.
|
# we have lots of space here to comment 'TMyObject'.
|
||||||
# This line belongs to the comment as it's properly aligned.
|
# This line belongs to the comment as it's properly aligned.
|
||||||
|
|
||||||
|
|
||||||
Comments are tokens; they are only allowed at certain places in the input file
|
Comments are tokens; they are only allowed at certain places in the input file
|
||||||
as they belong to the syntax tree! This feature enables perfect source-to-source
|
as they belong to the syntax tree! This feature enables perfect source-to-source
|
||||||
|
|
@ -150,6 +150,18 @@ the syntax, watch their indentation:
|
||||||
**Note**: To comment out a large piece of code, it is often better to use a
|
**Note**: To comment out a large piece of code, it is often better to use a
|
||||||
``when false:`` statement.
|
``when false:`` statement.
|
||||||
|
|
||||||
|
.. code-block:: nimrod
|
||||||
|
when false:
|
||||||
|
brokenCode()
|
||||||
|
|
||||||
|
Another option is to use the `discard`_ statement together with
|
||||||
|
*long string literals* to create block comments:
|
||||||
|
|
||||||
|
.. code-block:: nimrod
|
||||||
|
discard """ You can have any nimrod code text commented
|
||||||
|
out inside this with no indentation restrictions.
|
||||||
|
yes("May I ask a pointless question?") """
|
||||||
|
|
||||||
|
|
||||||
Numbers
|
Numbers
|
||||||
-------
|
-------
|
||||||
|
|
@ -575,27 +587,46 @@ Some terminology: in the example ``question`` is called a (formal) *parameter*,
|
||||||
|
|
||||||
Result variable
|
Result variable
|
||||||
---------------
|
---------------
|
||||||
A procedure that returns a value has an implicit ``result`` variable that
|
A procedure that returns a value has an implicit ``result`` variable declared
|
||||||
represents the return value. A ``return`` statement with no expression is a
|
that represents the return value. A ``return`` statement with no expression is a
|
||||||
shorthand for ``return result``. So all three code snippets are equivalent:
|
shorthand for ``return result``. The ``result`` value is always returned
|
||||||
|
automatically at the end a procedure if there is no ``return`` statement at
|
||||||
|
the exit.
|
||||||
|
|
||||||
.. code-block:: nimrod
|
.. code-block:: nimrod
|
||||||
return 42
|
proc sumTillNegative(x: varargs[int]): int =
|
||||||
|
for i in x:
|
||||||
.. code-block:: nimrod
|
if i < 0:
|
||||||
result = 42
|
return
|
||||||
return
|
result = result + i
|
||||||
|
|
||||||
.. code-block:: nimrod
|
echo sumTillNegative() # echos 0
|
||||||
result = 42
|
echo sumTillNegative(3, 4, 5) # echos 12
|
||||||
return result
|
echo sumTillNegative(3, 4 , -1 , 6) # echos 7
|
||||||
|
|
||||||
|
The ``result`` variable is already implicitly declared at the start of the
|
||||||
|
function, so declaring it again with 'var result', for example, would shadow it
|
||||||
|
with a normal variable of the same name. The result variable is also already
|
||||||
|
initialised with the type's default value. Note that referential data types will
|
||||||
|
be ``nil`` at the start of the procedure, and thus may require manual
|
||||||
|
initialisation.
|
||||||
|
|
||||||
|
|
||||||
Parameters
|
Parameters
|
||||||
----------
|
----------
|
||||||
Parameters are constant in the procedure body. Their value cannot be changed
|
Parameters are constant in the procedure body. By default, their value cannot be
|
||||||
because this allows the compiler to implement parameter passing in the most
|
changed because this allows the compiler to implement parameter passing in the
|
||||||
efficient way. If the procedure needs to modify the argument for the
|
most efficient way. If a mutable variable is needed inside the procedure, it has
|
||||||
|
to be declared with ``var`` in the procedure body. Shadowing the parameter name
|
||||||
|
is possible, and actually an idiom:
|
||||||
|
|
||||||
|
.. code-block:: nimrod
|
||||||
|
proc printSeq(s: seq, nprinted: int = -1) =
|
||||||
|
var nprinted = if nprinted == -1: s.len else: min(nprinted, s.len)
|
||||||
|
for i in 0 .. <nprinted:
|
||||||
|
echo s[i]
|
||||||
|
|
||||||
|
If the procedure needs to modify the argument for the
|
||||||
caller, a ``var`` parameter can be used:
|
caller, a ``var`` parameter can be used:
|
||||||
|
|
||||||
.. code-block:: nimrod
|
.. code-block:: nimrod
|
||||||
|
|
@ -634,6 +665,9 @@ been declared with the ``discardable`` pragma:
|
||||||
|
|
||||||
p(3, 4) # now valid
|
p(3, 4) # now valid
|
||||||
|
|
||||||
|
The discard statement can also be used to create block comments as described
|
||||||
|
in the `Comments`_.
|
||||||
|
|
||||||
|
|
||||||
Named arguments
|
Named arguments
|
||||||
---------------
|
---------------
|
||||||
|
|
|
||||||
|
|
@ -26,8 +26,15 @@ proc createProcType(p, b: PNimrodNode): PNimrodNode {.compileTime.} =
|
||||||
for i in 0 .. <p.len:
|
for i in 0 .. <p.len:
|
||||||
let ident = p[i]
|
let ident = p[i]
|
||||||
var identDefs = newNimNode(nnkIdentDefs)
|
var identDefs = newNimNode(nnkIdentDefs)
|
||||||
identDefs.add newIdentNode("i" & $i)
|
case ident.kind
|
||||||
identDefs.add(ident)
|
of nnkExprColonExpr:
|
||||||
|
identDefs.add ident[0]
|
||||||
|
identDefs.add ident[1]
|
||||||
|
of nnkIdent:
|
||||||
|
identDefs.add newIdentNode("i" & $i)
|
||||||
|
identDefs.add(ident)
|
||||||
|
else:
|
||||||
|
error("Incorrect type list in proc type declaration.")
|
||||||
identDefs.add newEmptyNode()
|
identDefs.add newEmptyNode()
|
||||||
formalParams.add identDefs
|
formalParams.add identDefs
|
||||||
of nnkIdent:
|
of nnkIdent:
|
||||||
|
|
@ -47,7 +54,7 @@ proc createProcType(p, b: PNimrodNode): PNimrodNode {.compileTime.} =
|
||||||
macro `=>`*(p, b: expr): expr {.immediate.} =
|
macro `=>`*(p, b: expr): expr {.immediate.} =
|
||||||
## Syntax sugar for anonymous procedures.
|
## Syntax sugar for anonymous procedures.
|
||||||
##
|
##
|
||||||
## ..code-block:: nimrod
|
## .. code-block:: nimrod
|
||||||
##
|
##
|
||||||
## proc passTwoAndTwo(f: (int, int) -> int): int =
|
## proc passTwoAndTwo(f: (int, int) -> int): int =
|
||||||
## f(2, 2)
|
## f(2, 2)
|
||||||
|
|
@ -98,7 +105,7 @@ macro `=>`*(p, b: expr): expr {.immediate.} =
|
||||||
macro `->`*(p, b: expr): expr {.immediate.} =
|
macro `->`*(p, b: expr): expr {.immediate.} =
|
||||||
## Syntax sugar for procedure types.
|
## Syntax sugar for procedure types.
|
||||||
##
|
##
|
||||||
## ..code-block:: nimrod
|
## .. code-block:: nimrod
|
||||||
##
|
##
|
||||||
## proc pass2(f: (float, float) -> float): float =
|
## proc pass2(f: (float, float) -> float): float =
|
||||||
## f(2, 2)
|
## f(2, 2)
|
||||||
|
|
|
||||||
179
lib/pure/os.nim
179
lib/pure/os.nim
|
|
@ -448,6 +448,8 @@ proc getLastAccessTime*(file: string): TTime {.rtl, extern: "nos$1".} =
|
||||||
|
|
||||||
proc getCreationTime*(file: string): TTime {.rtl, extern: "nos$1".} =
|
proc getCreationTime*(file: string): TTime {.rtl, extern: "nos$1".} =
|
||||||
## Returns the `file`'s creation time.
|
## Returns the `file`'s creation time.
|
||||||
|
## Note that under posix OS's, the returned time may actually be the time at
|
||||||
|
## which the file's attribute's were last modified.
|
||||||
when defined(posix):
|
when defined(posix):
|
||||||
var res: TStat
|
var res: TStat
|
||||||
if stat(file, res) < 0'i32: osError(osLastError())
|
if stat(file, res) < 0'i32: osError(osLastError())
|
||||||
|
|
@ -777,6 +779,25 @@ proc isAbsolute*(path: string): bool {.rtl, noSideEffect, extern: "nos$1".} =
|
||||||
elif defined(posix):
|
elif defined(posix):
|
||||||
result = path[0] == '/'
|
result = path[0] == '/'
|
||||||
|
|
||||||
|
when defined(Windows):
|
||||||
|
proc openHandle(path: string, followSymlink=true): THandle =
|
||||||
|
var flags = FILE_FLAG_BACKUP_SEMANTICS or FILE_ATTRIBUTE_NORMAL
|
||||||
|
if not followSymlink:
|
||||||
|
flags = flags or FILE_FLAG_OPEN_REPARSE_POINT
|
||||||
|
|
||||||
|
when useWinUnicode:
|
||||||
|
result = createFileW(
|
||||||
|
newWideCString(path), 0'i32,
|
||||||
|
FILE_SHARE_DELETE or FILE_SHARE_READ or FILE_SHARE_WRITE,
|
||||||
|
nil, OPEN_EXISTING, flags, 0
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
result = createFileA(
|
||||||
|
path, 0'i32,
|
||||||
|
FILE_SHARE_DELETE or FILE_SHARE_READ or FILE_SHARE_WRITE,
|
||||||
|
nil, OPEN_EXISTING, flags, 0
|
||||||
|
)
|
||||||
|
|
||||||
proc sameFile*(path1, path2: string): bool {.rtl, extern: "nos$1",
|
proc sameFile*(path1, path2: string): bool {.rtl, extern: "nos$1",
|
||||||
tags: [FReadDir].} =
|
tags: [FReadDir].} =
|
||||||
## Returns True if both pathname arguments refer to the same physical
|
## Returns True if both pathname arguments refer to the same physical
|
||||||
|
|
@ -787,26 +808,8 @@ proc sameFile*(path1, path2: string): bool {.rtl, extern: "nos$1",
|
||||||
## sym-linked paths to the same file or directory.
|
## sym-linked paths to the same file or directory.
|
||||||
when defined(Windows):
|
when defined(Windows):
|
||||||
var success = true
|
var success = true
|
||||||
|
var f1 = openHandle(path1)
|
||||||
when useWinUnicode:
|
var f2 = openHandle(path2)
|
||||||
var p1 = newWideCString(path1)
|
|
||||||
var p2 = newWideCString(path2)
|
|
||||||
template openHandle(path: expr): expr =
|
|
||||||
createFileW(path, 0'i32, FILE_SHARE_DELETE or FILE_SHARE_READ or
|
|
||||||
FILE_SHARE_WRITE, nil, OPEN_EXISTING,
|
|
||||||
FILE_FLAG_BACKUP_SEMANTICS or FILE_ATTRIBUTE_NORMAL, 0)
|
|
||||||
|
|
||||||
var f1 = openHandle(p1)
|
|
||||||
var f2 = openHandle(p2)
|
|
||||||
|
|
||||||
else:
|
|
||||||
template openHandle(path: expr): expr =
|
|
||||||
createFileA(path, 0'i32, FILE_SHARE_DELETE or FILE_SHARE_READ or
|
|
||||||
FILE_SHARE_WRITE, nil, OPEN_EXISTING,
|
|
||||||
FILE_FLAG_BACKUP_SEMANTICS or FILE_ATTRIBUTE_NORMAL, 0)
|
|
||||||
|
|
||||||
var f1 = openHandle(path1)
|
|
||||||
var f2 = openHandle(path2)
|
|
||||||
|
|
||||||
var lastErr: TOSErrorCode
|
var lastErr: TOSErrorCode
|
||||||
if f1 != INVALID_HANDLE_VALUE and f2 != INVALID_HANDLE_VALUE:
|
if f1 != INVALID_HANDLE_VALUE and f2 != INVALID_HANDLE_VALUE:
|
||||||
|
|
@ -1747,4 +1750,140 @@ proc expandTilde*(path: string): string =
|
||||||
else:
|
else:
|
||||||
result = path
|
result = path
|
||||||
|
|
||||||
|
when defined(Windows):
|
||||||
|
type
|
||||||
|
DeviceId = int32
|
||||||
|
FileId = int64
|
||||||
|
else:
|
||||||
|
type
|
||||||
|
DeviceId = TDev
|
||||||
|
FileId = TIno
|
||||||
|
|
||||||
|
type
|
||||||
|
FileInfo = object
|
||||||
|
## Contains information associated with a file object.
|
||||||
|
id: tuple[device: DeviceId, file: FileId] # Device and file id.
|
||||||
|
kind: TPathComponent # Kind of file object - directory, symlink, etc.
|
||||||
|
size: BiggestInt # Size of file.
|
||||||
|
permissions: set[TFilePermission] # File permissions
|
||||||
|
linkCount: BiggestInt # Number of hard links the file object has.
|
||||||
|
lastAccessTime: TTime # Time file was last accessed.
|
||||||
|
lastWriteTime: TTime # Time file was last modified/written to.
|
||||||
|
creationTime: TTime # Time file was created. Not supported on all systems!
|
||||||
|
|
||||||
|
template rawToFormalFileInfo(rawInfo, formalInfo): expr =
|
||||||
|
## Transforms the native file info structure into the one nimrod uses.
|
||||||
|
## 'rawInfo' is either a 'TBY_HANDLE_FILE_INFORMATION' structure on Windows,
|
||||||
|
## or a 'TStat' structure on posix
|
||||||
|
when defined(Windows):
|
||||||
|
template toTime(e): expr = winTimeToUnixTime(rdFileTime(e))
|
||||||
|
template merge(a, b): expr = a or (b shl 32)
|
||||||
|
formalInfo.id.device = rawInfo.dwVolumeSerialNumber
|
||||||
|
formalInfo.id.file = merge(rawInfo.nFileIndexLow, rawInfo.nFileIndexHigh)
|
||||||
|
formalInfo.size = merge(rawInfo.nFileSizeLow, rawInfo.nFileSizeHigh)
|
||||||
|
formalInfo.linkCount = rawInfo.nNumberOfLinks
|
||||||
|
formalInfo.lastAccessTime = toTime(rawInfo.ftLastAccessTime)
|
||||||
|
formalInfo.lastWriteTime = toTime(rawInfo.ftLastWriteTime)
|
||||||
|
formalInfo.creationTime = toTime(rawInfo.ftCreationTime)
|
||||||
|
|
||||||
|
# Retrieve basic permissions
|
||||||
|
if (rawInfo.dwFileAttributes and FILE_ATTRIBUTE_READONLY) != 0'i32:
|
||||||
|
formalInfo.permissions = {fpUserExec, fpUserRead, fpGroupExec,
|
||||||
|
fpGroupRead, fpOthersExec, fpOthersRead}
|
||||||
|
else:
|
||||||
|
result.permissions = {fpUserExec..fpOthersRead}
|
||||||
|
|
||||||
|
# Retrieve basic file kind
|
||||||
|
result.kind = pcFile
|
||||||
|
if (rawInfo.dwFileAttributes and FILE_ATTRIBUTE_DIRECTORY) != 0'i32:
|
||||||
|
formalInfo.kind = pcDir
|
||||||
|
if (rawInfo.dwFileAttributes and FILE_ATTRIBUTE_REPARSE_POINT) != 0'i32:
|
||||||
|
formalInfo.kind = succ(result.kind)
|
||||||
|
|
||||||
|
else:
|
||||||
|
template checkAndIncludeMode(rawMode, formalMode: expr) =
|
||||||
|
if (rawInfo.st_mode and rawMode) != 0'i32:
|
||||||
|
formalInfo.permissions.incl(formalMode)
|
||||||
|
formalInfo.id = (rawInfo.st_dev, rawInfo.st_ino)
|
||||||
|
formalInfo.size = rawInfo.st_size
|
||||||
|
formalInfo.linkCount = rawInfo.st_Nlink
|
||||||
|
formalInfo.lastAccessTime = rawInfo.st_atime
|
||||||
|
formalInfo.lastWriteTime = rawInfo.st_mtime
|
||||||
|
formalInfo.creationTime = rawInfo.st_ctime
|
||||||
|
|
||||||
|
result.permissions = {}
|
||||||
|
checkAndIncludeMode(S_IRUSR, fpUserRead)
|
||||||
|
checkAndIncludeMode(S_IWUSR, fpUserWrite)
|
||||||
|
checkAndIncludeMode(S_IXUSR, fpUserExec)
|
||||||
|
|
||||||
|
checkAndIncludeMode(S_IRGRP, fpGroupRead)
|
||||||
|
checkAndIncludeMode(S_IWGRP, fpGroupWrite)
|
||||||
|
checkAndIncludeMode(S_IXGRP, fpGroupExec)
|
||||||
|
|
||||||
|
checkAndIncludeMode(S_IROTH, fpOthersRead)
|
||||||
|
checkAndIncludeMode(S_IWOTH, fpOthersWrite)
|
||||||
|
checkAndIncludeMode(S_IXOTH, fpOthersExec)
|
||||||
|
|
||||||
|
formalInfo.kind = pcFile
|
||||||
|
if S_ISDIR(rawInfo.st_mode): formalInfo.kind = pcDir
|
||||||
|
if S_ISLNK(rawInfo.st_mode): formalInfo.kind.inc()
|
||||||
|
|
||||||
|
proc getFileInfo*(handle: TFileHandle): FileInfo =
|
||||||
|
## Retrieves file information for the file object represented by the given
|
||||||
|
## handle.
|
||||||
|
##
|
||||||
|
## If the information cannot be retrieved, such as when the file handle
|
||||||
|
## is invalid, an error will be thrown.
|
||||||
|
# Done: ID, Kind, Size, Permissions, Link Count
|
||||||
|
when defined(Windows):
|
||||||
|
var rawInfo: TBY_HANDLE_FILE_INFORMATION
|
||||||
|
# We have to use the super special '_get_osfhandle' call (wrapped above)
|
||||||
|
# To transform the C file descripter to a native file handle.
|
||||||
|
var realHandle = get_osfhandle(handle)
|
||||||
|
if getFileInformationByHandle(realHandle, addr rawInfo) == 0:
|
||||||
|
osError(osLastError())
|
||||||
|
rawToFormalFileInfo(rawInfo, result)
|
||||||
|
else:
|
||||||
|
var rawInfo: TStat
|
||||||
|
if fstat(handle, rawInfo) < 0'i32:
|
||||||
|
osError(osLastError())
|
||||||
|
rawToFormalFileInfo(rawInfo, result)
|
||||||
|
|
||||||
|
proc getFileInfo*(file: TFile): FileInfo =
|
||||||
|
result = getFileInfo(file.fileHandle())
|
||||||
|
|
||||||
|
proc getFileInfo*(path: string, followSymlink = true): FileInfo =
|
||||||
|
## Retrieves file information for the file object pointed to by `path`.
|
||||||
|
##
|
||||||
|
## Due to intrinsic differences between operating systems, the information
|
||||||
|
## contained by the returned `FileInfo` structure will be slightly different
|
||||||
|
## across platforms, and in some cases, incomplete or inaccurate.
|
||||||
|
##
|
||||||
|
## When `followSymlink` is true, symlinks are followed and the information
|
||||||
|
## retrieved is information related to the symlink's target. Otherwise,
|
||||||
|
## information on the symlink itself is retrieved.
|
||||||
|
##
|
||||||
|
## If the information cannot be retrieved, such as when the path doesn't
|
||||||
|
## exist, or when permission restrictions prevent the program from retrieving
|
||||||
|
## file information, an error will be thrown.
|
||||||
|
when defined(Windows):
|
||||||
|
var
|
||||||
|
handle = openHandle(path, followSymlink)
|
||||||
|
rawInfo: TBY_HANDLE_FILE_INFORMATION
|
||||||
|
if handle == INVALID_HANDLE_VALUE:
|
||||||
|
osError(osLastError())
|
||||||
|
if getFileInformationByHandle(handle, addr rawInfo) == 0:
|
||||||
|
osError(osLastError())
|
||||||
|
rawToFormalFileInfo(rawInfo, result)
|
||||||
|
discard closeHandle(handle)
|
||||||
|
else:
|
||||||
|
var rawInfo: TStat
|
||||||
|
if followSymlink:
|
||||||
|
if lstat(path, rawInfo) < 0'i32:
|
||||||
|
osError(osLastError())
|
||||||
|
else:
|
||||||
|
if stat(path, rawInfo) < 0'i32:
|
||||||
|
osError(osLastError())
|
||||||
|
rawToFormalFileInfo(rawInfo, result)
|
||||||
|
|
||||||
{.pop.}
|
{.pop.}
|
||||||
|
|
|
||||||
|
|
@ -252,10 +252,8 @@ proc nimIntToStr(x: int): string {.compilerRtl.} =
|
||||||
|
|
||||||
proc nimFloatToStr(x: float): string {.compilerproc.} =
|
proc nimFloatToStr(x: float): string {.compilerproc.} =
|
||||||
var buf: array [0..59, char]
|
var buf: array [0..59, char]
|
||||||
c_sprintf(buf, "%#.f", x)
|
c_sprintf(buf, "%#.16e", x)
|
||||||
result = $buf
|
return $buf
|
||||||
if result[len(result)-1] == '.':
|
|
||||||
result.add("0")
|
|
||||||
|
|
||||||
proc nimInt64ToStr(x: int64): string {.compilerRtl.} =
|
proc nimInt64ToStr(x: int64): string {.compilerRtl.} =
|
||||||
result = newString(sizeof(x)*4)
|
result = newString(sizeof(x)*4)
|
||||||
|
|
|
||||||
|
|
@ -581,6 +581,7 @@ const
|
||||||
INVALID_FILE_SIZE* = -1'i32
|
INVALID_FILE_SIZE* = -1'i32
|
||||||
|
|
||||||
FILE_FLAG_BACKUP_SEMANTICS* = 33554432'i32
|
FILE_FLAG_BACKUP_SEMANTICS* = 33554432'i32
|
||||||
|
FILE_FLAG_OPEN_REPARSE_POINT* = 0x00200000'i32
|
||||||
|
|
||||||
# Error Constants
|
# Error Constants
|
||||||
const
|
const
|
||||||
|
|
@ -715,3 +716,6 @@ proc WSASend*(s: TSocketHandle, buf: ptr TWSABuf, bufCount: DWORD,
|
||||||
bytesSent: PDWord, flags: DWORD, lpOverlapped: POverlapped,
|
bytesSent: PDWord, flags: DWORD, lpOverlapped: POverlapped,
|
||||||
completionProc: POVERLAPPED_COMPLETION_ROUTINE): cint {.
|
completionProc: POVERLAPPED_COMPLETION_ROUTINE): cint {.
|
||||||
stdcall, importc: "WSASend", dynlib: "Ws2_32.dll".}
|
stdcall, importc: "WSASend", dynlib: "Ws2_32.dll".}
|
||||||
|
|
||||||
|
proc get_osfhandle*(fd:TFileHandle): THandle {.
|
||||||
|
importc:"_get_osfhandle", header:"<io.h>".}
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,7 @@ discard """
|
||||||
3
|
3
|
||||||
3
|
3
|
||||||
noReturn
|
noReturn
|
||||||
|
6
|
||||||
'''
|
'''
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
|
@ -36,8 +37,7 @@ echo doWithOneAndTwo((x, y) => x + y)
|
||||||
|
|
||||||
noReturn(() -> void => echo("noReturn"))
|
noReturn(() -> void => echo("noReturn"))
|
||||||
|
|
||||||
when false:
|
proc pass2(f: (int, int) -> int): (int) -> int =
|
||||||
proc pass2(f: (int, int) -> int): (int) -> int =
|
(x: int) -> int => f(2, x)
|
||||||
(x: int) -> int => f(2, x)
|
|
||||||
|
|
||||||
#echo pass2((x, y) => x + y)
|
echo pass2((x, y) => x + y)(4)
|
||||||
|
|
|
||||||
19
tests/macros/texprcolonexpr.nim
Normal file
19
tests/macros/texprcolonexpr.nim
Normal file
|
|
@ -0,0 +1,19 @@
|
||||||
|
discard """
|
||||||
|
msg: '''
|
||||||
|
Infix
|
||||||
|
Ident !"=>"
|
||||||
|
Call
|
||||||
|
Ident !"name"
|
||||||
|
Ident !"a"
|
||||||
|
ExprColonExpr
|
||||||
|
Ident !"b"
|
||||||
|
Ident !"cint"
|
||||||
|
NilLit nil
|
||||||
|
'''
|
||||||
|
"""
|
||||||
|
import macros
|
||||||
|
|
||||||
|
macro def(x: stmt): stmt {.immediate.} =
|
||||||
|
echo treeRepr(x)
|
||||||
|
|
||||||
|
def name(a, b:cint) => nil
|
||||||
93
tests/stdlib/tgetfileinfo.nim
Normal file
93
tests/stdlib/tgetfileinfo.nim
Normal file
|
|
@ -0,0 +1,93 @@
|
||||||
|
discard """
|
||||||
|
output: ""
|
||||||
|
"""
|
||||||
|
|
||||||
|
import os, strutils
|
||||||
|
# Cases
|
||||||
|
# 1 - String : Existing File : Symlink true
|
||||||
|
# 2 - String : Existing File : Symlink false
|
||||||
|
# 3 - String : Non-existing File : Symlink true
|
||||||
|
# 4 - String : Non-existing File : Symlink false
|
||||||
|
# 5 - Handle : Valid File
|
||||||
|
# 6 - Handle : Invalid File
|
||||||
|
# 7 - Handle : Valid Handle
|
||||||
|
# 8 - Handle : Invalid Handle
|
||||||
|
|
||||||
|
proc genBadFileName(limit = 100): string =
|
||||||
|
## Generates a filename of a nonexistant file.
|
||||||
|
## Returns "" if generation fails.
|
||||||
|
result = "a"
|
||||||
|
var hitLimit = true
|
||||||
|
|
||||||
|
for i in 0..100:
|
||||||
|
if existsFile(result):
|
||||||
|
result.add("a")
|
||||||
|
else:
|
||||||
|
hitLimit = false
|
||||||
|
break
|
||||||
|
if hitLimit:
|
||||||
|
result = ""
|
||||||
|
|
||||||
|
proc caseOneAndTwo(followLink: bool) =
|
||||||
|
try:
|
||||||
|
discard getFileInfo(getAppFilename(), followLink)
|
||||||
|
#echo("String : Existing File : Symlink $# : Success" % $followLink)
|
||||||
|
except EOS:
|
||||||
|
echo("String : Existing File : Symlink $# : Failure" % $followLink)
|
||||||
|
|
||||||
|
proc caseThreeAndFour(followLink: bool) =
|
||||||
|
var invalidName = genBadFileName()
|
||||||
|
try:
|
||||||
|
discard getFileInfo(invalidName, true)
|
||||||
|
echo("String : Non-existing File : Symlink $# : Failure" % $followLink)
|
||||||
|
except EOS:
|
||||||
|
#echo("String : Non-existing File : Symlink $# : Success" % $followLink)
|
||||||
|
|
||||||
|
proc testGetFileInfo =
|
||||||
|
# Case 1
|
||||||
|
caseOneAndTwo(true)
|
||||||
|
|
||||||
|
# Case 2
|
||||||
|
caseOneAndTwo(false)
|
||||||
|
|
||||||
|
# Case 3
|
||||||
|
caseThreeAndFour(true)
|
||||||
|
|
||||||
|
# Case 4
|
||||||
|
caseThreeAndFour(false)
|
||||||
|
|
||||||
|
# Case 5 and 7
|
||||||
|
block:
|
||||||
|
let
|
||||||
|
testFile = open(getAppFilename())
|
||||||
|
testHandle = fileHandle(testFile)
|
||||||
|
try:
|
||||||
|
discard getFileInfo(testFile)
|
||||||
|
#echo("Handle : Valid File : Success")
|
||||||
|
except EIO:
|
||||||
|
echo("Handle : Valid File : Failure")
|
||||||
|
|
||||||
|
try:
|
||||||
|
discard getFileInfo(testHandle)
|
||||||
|
#echo("Handle : Valid File : Success")
|
||||||
|
except EIO:
|
||||||
|
echo("Handle : Valid File : Failure")
|
||||||
|
|
||||||
|
# Case 6 and 8
|
||||||
|
block:
|
||||||
|
let
|
||||||
|
testFile: TFile = nil
|
||||||
|
testHandle = TFileHandle(-1)
|
||||||
|
try:
|
||||||
|
discard getFileInfo(testFile)
|
||||||
|
echo("Handle : Invalid File : Failure")
|
||||||
|
except EIO, EOS:
|
||||||
|
#echo("Handle : Invalid File : Success")
|
||||||
|
|
||||||
|
try:
|
||||||
|
discard getFileInfo(testHandle)
|
||||||
|
echo("Handle : Invalid File : Failure")
|
||||||
|
except EIO, EOS:
|
||||||
|
#echo("Handle : Invalid File : Success")
|
||||||
|
|
||||||
|
testGetFileInfo()
|
||||||
7
tests/system/tsysspawnbadarg.nim
Normal file
7
tests/system/tsysspawnbadarg.nim
Normal file
|
|
@ -0,0 +1,7 @@
|
||||||
|
discard """
|
||||||
|
line: 7
|
||||||
|
errormsg: "'spawn' takes a call expression of type void"
|
||||||
|
cmd: "nimrod $target --threads:on $options $file"
|
||||||
|
"""
|
||||||
|
|
||||||
|
spawn(1)
|
||||||
Loading…
Add table
Add a link
Reference in a new issue