Merge branch 'devel' of https://github.com/Araq/Nimrod into devel

This commit is contained in:
Araq
2014-04-21 00:07:40 +02:00
13 changed files with 369 additions and 59 deletions

View File

@@ -404,7 +404,7 @@ proc evalOp(m: TMagic, n, a, b, c: PNode): PNode =
mExit, mInc, ast.mDec, mEcho, mSwap, mAppendStrCh,
mAppendStrStr, mAppendSeqElem, mSetLengthStr, mSetLengthSeq,
mParseExprToAst, mParseStmtToAst, mExpandToAst, mTypeTrait,
mNLen..mNError, mEqRef, mSlurp, mStaticExec, mNGenSym:
mNLen..mNError, mEqRef, mSlurp, mStaticExec, mNGenSym, mSpawn:
discard
of mRand:
result = newIntNodeT(math.random(a.getInt.int), n)

View File

@@ -253,6 +253,7 @@ proc findUsages(node: PNode, s: PSym) =
lastLineInfo = node.info
proc findDefinition(node: PNode, s: PSym) =
if node.isNil or s.isNil: return
if isTracked(node.info, s.name.s.len):
suggestWriteln(symToStr(s, isLocal=false, sectionDef))
suggestQuit()

View File

@@ -354,6 +354,11 @@ template handleJmpBack() {.dirty.} =
globalError(c.debug[pc], errTooManyIterations)
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 =
var pc = start
var tos = tos
@@ -454,7 +459,7 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
decodeBC(rkNode)
let src = regs[rb].node
if src.kind notin {nkEmpty..nkNilLit}:
let n = src.sons[rc]
let n = src.sons[rc].skipColon
regs[ra].node = n
else:
stackTrace(c, tos, pc, errNilAccess)
@@ -1099,6 +1104,7 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
c.module)
of opcGorge:
decodeBC(rkNode)
createStr regs[ra]
regs[ra].node.strVal = opGorge(regs[rb].node.strVal,
regs[rc].node.strVal)
of opcNError:

View File

@@ -70,6 +70,9 @@ proc echoCode*(c: PCtx, start=0) {.deprecated.} =
echo buf
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
let ins = (opc.uint32 or (a.uint32 shl 8'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)
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
(b.uint32 shl 16'u32) or
(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)
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
(bx+wordExcess).uint32 shl 16'u32).TInstr
c.code.add(ins)
@@ -316,15 +326,7 @@ proc genAndOr(c: PCtx; n: PNode; opc: TOpcode; dest: var TDest) =
c.patch(L1)
proc canonValue*(n: PNode): PNode =
if n.kind == nkExprColonExpr:
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
result = n
proc rawGenLiteral(c: PCtx; n: PNode): int =
result = c.constants.len

View File

@@ -132,7 +132,7 @@ a backslash:
TMyObject {.final, pure, acyclic.} = object # comment continues: \
# we have lots of space here to comment 'TMyObject'.
# 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
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
``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
-------
@@ -575,27 +587,46 @@ Some terminology: in the example ``question`` is called a (formal) *parameter*,
Result variable
---------------
A procedure that returns a value has an implicit ``result`` variable that
represents the return value. A ``return`` statement with no expression is a
shorthand for ``return result``. So all three code snippets are equivalent:
A procedure that returns a value has an implicit ``result`` variable declared
that represents the return value. A ``return`` statement with no expression is a
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
return 42
.. code-block:: nimrod
result = 42
return
.. code-block:: nimrod
result = 42
return result
proc sumTillNegative(x: varargs[int]): int =
for i in x:
if i < 0:
return
result = result + i
echo sumTillNegative() # echos 0
echo sumTillNegative(3, 4, 5) # echos 12
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 are constant in the procedure body. Their value cannot be changed
because this allows the compiler to implement parameter passing in the most
efficient way. If the procedure needs to modify the argument for the
Parameters are constant in the procedure body. By default, their value cannot be
changed because this allows the compiler to implement parameter passing in 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:
.. code-block:: nimrod
@@ -634,6 +665,9 @@ been declared with the ``discardable`` pragma:
p(3, 4) # now valid
The discard statement can also be used to create block comments as described
in the `Comments`_.
Named arguments
---------------

View File

@@ -26,8 +26,15 @@ proc createProcType(p, b: PNimrodNode): PNimrodNode {.compileTime.} =
for i in 0 .. <p.len:
let ident = p[i]
var identDefs = newNimNode(nnkIdentDefs)
identDefs.add newIdentNode("i" & $i)
identDefs.add(ident)
case ident.kind
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()
formalParams.add identDefs
of nnkIdent:
@@ -47,7 +54,7 @@ proc createProcType(p, b: PNimrodNode): PNimrodNode {.compileTime.} =
macro `=>`*(p, b: expr): expr {.immediate.} =
## Syntax sugar for anonymous procedures.
##
## ..code-block:: nimrod
## .. code-block:: nimrod
##
## proc passTwoAndTwo(f: (int, int) -> int): int =
## f(2, 2)
@@ -98,7 +105,7 @@ macro `=>`*(p, b: expr): expr {.immediate.} =
macro `->`*(p, b: expr): expr {.immediate.} =
## Syntax sugar for procedure types.
##
## ..code-block:: nimrod
## .. code-block:: nimrod
##
## proc pass2(f: (float, float) -> float): float =
## f(2, 2)

View File

@@ -448,6 +448,8 @@ proc getLastAccessTime*(file: string): TTime {.rtl, extern: "nos$1".} =
proc getCreationTime*(file: string): TTime {.rtl, extern: "nos$1".} =
## 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):
var res: TStat
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):
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",
tags: [FReadDir].} =
## 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.
when defined(Windows):
var success = true
when useWinUnicode:
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 f1 = openHandle(path1)
var f2 = openHandle(path2)
var lastErr: TOSErrorCode
if f1 != INVALID_HANDLE_VALUE and f2 != INVALID_HANDLE_VALUE:
@@ -1747,4 +1750,140 @@ proc expandTilde*(path: string): string =
else:
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.}

View File

@@ -252,10 +252,8 @@ proc nimIntToStr(x: int): string {.compilerRtl.} =
proc nimFloatToStr(x: float): string {.compilerproc.} =
var buf: array [0..59, char]
c_sprintf(buf, "%#.f", x)
result = $buf
if result[len(result)-1] == '.':
result.add("0")
c_sprintf(buf, "%#.16e", x)
return $buf
proc nimInt64ToStr(x: int64): string {.compilerRtl.} =
result = newString(sizeof(x)*4)

View File

@@ -581,6 +581,7 @@ const
INVALID_FILE_SIZE* = -1'i32
FILE_FLAG_BACKUP_SEMANTICS* = 33554432'i32
FILE_FLAG_OPEN_REPARSE_POINT* = 0x00200000'i32
# Error Constants
const
@@ -715,3 +716,6 @@ proc WSASend*(s: TSocketHandle, buf: ptr TWSABuf, bufCount: DWORD,
bytesSent: PDWord, flags: DWORD, lpOverlapped: POverlapped,
completionProc: POVERLAPPED_COMPLETION_ROUTINE): cint {.
stdcall, importc: "WSASend", dynlib: "Ws2_32.dll".}
proc get_osfhandle*(fd:TFileHandle): THandle {.
importc:"_get_osfhandle", header:"<io.h>".}

View File

@@ -5,6 +5,7 @@ discard """
3
3
noReturn
6
'''
"""
@@ -36,8 +37,7 @@ echo doWithOneAndTwo((x, y) => x + y)
noReturn(() -> void => echo("noReturn"))
when false:
proc pass2(f: (int, int) -> int): (int) -> int =
(x: int) -> int => f(2, x)
proc pass2(f: (int, int) -> int): (int) -> int =
(x: int) -> int => f(2, x)
#echo pass2((x, y) => x + y)
echo pass2((x, y) => x + y)(4)

View 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

View 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()

View 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)