Merge branch 'devel'

This commit is contained in:
Araq
2014-11-03 18:10:06 +01:00
32 changed files with 1003 additions and 248 deletions

View File

@@ -921,6 +921,7 @@ proc genAsmOrEmitStmt(p: BProc, t: PNode, isAsmStmt=false): PRope =
app(result, x)
app(result, "\\n\"\n")
else:
res.add(tnl)
result = res.toRope
proc genAsmStmt(p: BProc, t: PNode) =

View File

@@ -11,7 +11,7 @@
import
intsets, options, ast, astalgo, msgs, idents, renderer, types, magicsys,
sempass2
sempass2, strutils
proc genConv(n: PNode, d: PType, downcast: bool): PNode =
var dest = skipTypes(d, abstractPtrs)
@@ -44,7 +44,8 @@ proc methodCall*(n: PNode): PNode =
result.sons[i] = genConv(result.sons[i], disp.typ.sons[i], true)
# save for incremental compilation:
var gMethods: seq[TSymSeq] = @[]
var
gMethods: seq[tuple[methods: TSymSeq, dispatcher: PSym]] = @[]
proc sameMethodBucket(a, b: PSym): bool =
result = false
@@ -80,31 +81,70 @@ proc attachDispatcher(s: PSym, dispatcher: PNode) =
else:
s.ast.add(dispatcher)
proc createDispatcher(s: PSym): PSym =
var disp = copySym(s)
incl(disp.flags, sfDispatcher)
excl(disp.flags, sfExported)
disp.typ = copyType(disp.typ, disp.typ.owner, false)
# we can't inline the dispatcher itself (for now):
if disp.typ.callConv == ccInline: disp.typ.callConv = ccDefault
disp.ast = copyTree(s.ast)
disp.ast.sons[bodyPos] = ast.emptyNode
disp.loc.r = nil
if s.typ.sons[0] != nil:
if disp.ast.sonsLen > resultPos:
disp.ast.sons[resultPos].sym = copySym(s.ast.sons[resultPos].sym)
else:
# We've encountered a method prototype without a filled-in
# resultPos slot. We put a placeholder in there that will
# be updated in fixupDispatcher().
disp.ast.addSon(ast.emptyNode)
attachDispatcher(s, newSymNode(disp))
# attach to itself to prevent bugs:
attachDispatcher(disp, newSymNode(disp))
return disp
proc fixupDispatcher(meth, disp: PSym) =
# We may have constructed the dispatcher from a method prototype
# and need to augment the incomplete dispatcher with information
# from later definitions, particularly the resultPos slot. Also,
# the lock level of the dispatcher needs to be updated/checked
# against that of the method.
if disp.ast.sonsLen > resultPos and meth.ast.sonsLen > resultPos and
disp.ast.sons[resultPos] == ast.emptyNode:
disp.ast.sons[resultPos] = copyTree(meth.ast.sons[resultPos])
# The following code works only with lock levels, so we disable
# it when they're not available.
when declared(TLockLevel):
proc `<`(a, b: TLockLevel): bool {.borrow.}
proc `==`(a, b: TLockLevel): bool {.borrow.}
if disp.typ.lockLevel == UnspecifiedLockLevel:
disp.typ.lockLevel = meth.typ.lockLevel
elif meth.typ.lockLevel != UnspecifiedLockLevel and
meth.typ.lockLevel != disp.typ.lockLevel:
message(meth.info, warnLockLevel,
"method has lock level $1, but another method has $2" %
[$meth.typ.lockLevel, $disp.typ.lockLevel])
# XXX The following code silences a duplicate warning in
# checkMethodeffects() in sempass2.nim for now.
if disp.typ.lockLevel < meth.typ.lockLevel:
disp.typ.lockLevel = meth.typ.lockLevel
proc methodDef*(s: PSym, fromCache: bool) =
var L = len(gMethods)
for i in countup(0, L - 1):
let disp = gMethods[i][0]
var disp = gMethods[i].dispatcher
if sameMethodBucket(disp, s):
add(gMethods[i], s)
add(gMethods[i].methods, s)
attachDispatcher(s, lastSon(disp.ast))
fixupDispatcher(s, disp)
when useEffectSystem: checkMethodEffects(disp, s)
return
add(gMethods, @[s])
# create a new dispatcher:
if not fromCache:
var disp = copySym(s)
incl(disp.flags, sfDispatcher)
excl(disp.flags, sfExported)
disp.typ = copyType(disp.typ, disp.typ.owner, false)
# we can't inline the dispatcher itself (for now):
if disp.typ.callConv == ccInline: disp.typ.callConv = ccDefault
disp.ast = copyTree(s.ast)
disp.ast.sons[bodyPos] = ast.emptyNode
if s.typ.sons[0] != nil:
disp.ast.sons[resultPos].sym = copySym(s.ast.sons[resultPos].sym)
attachDispatcher(s, newSymNode(disp))
# attach to itself to prevent bugs:
attachDispatcher(disp, newSymNode(disp))
add(gMethods, (methods: @[s], dispatcher: createDispatcher(s)))
if fromCache:
internalError(s.info, "no method dispatcher found")
proc relevantCol(methods: TSymSeq, col: int): bool =
# returns true iff the position is relevant
@@ -194,8 +234,9 @@ proc generateMethodDispatchers*(): PNode =
result = newNode(nkStmtList)
for bucket in countup(0, len(gMethods) - 1):
var relevantCols = initIntSet()
for col in countup(1, sonsLen(gMethods[bucket][0].typ) - 1):
if relevantCol(gMethods[bucket], col): incl(relevantCols, col)
sortBucket(gMethods[bucket], relevantCols)
addSon(result, newSymNode(genDispatcher(gMethods[bucket], relevantCols)))
for col in countup(1, sonsLen(gMethods[bucket].methods[0].typ) - 1):
if relevantCol(gMethods[bucket].methods, col): incl(relevantCols, col)
sortBucket(gMethods[bucket].methods, relevantCols)
addSon(result,
newSymNode(genDispatcher(gMethods[bucket].methods, relevantCols)))

View File

@@ -393,7 +393,9 @@ proc processSwitch(switch, arg: string, pass: TCmdLinePass, info: TLineInfo) =
of "linedir": processOnOffSwitch({optLineDir}, arg, pass, info)
of "assertions", "a": processOnOffSwitch({optAssert}, arg, pass, info)
of "deadcodeelim": processOnOffSwitchG({optDeadCodeElim}, arg, pass, info)
of "threads": processOnOffSwitchG({optThreads}, arg, pass, info)
of "threads":
processOnOffSwitchG({optThreads}, arg, pass, info)
if optThreads in gGlobalOptions: incl(gNotes, warnGcUnsafe)
of "tlsemulation": processOnOffSwitchG({optTlsEmulation}, arg, pass, info)
of "taintmode": processOnOffSwitchG({optTaintMode}, arg, pass, info)
of "implicitstatic":

View File

@@ -383,6 +383,8 @@ proc genItem(d: PDoc, n, nameNode: PNode, k: TSymKind) =
var seeSrcRope: PRope = nil
let docItemSeeSrc = getConfigVar("doc.item.seesrc")
if docItemSeeSrc.len > 0 and options.docSeeSrcUrl.len > 0:
# XXX toFilename doesn't really work. We need to ensure that this keeps
# returning a relative path.
let urlRope = ropeFormatNamedVars(options.docSeeSrcUrl,
["path", "line"], [n.info.toFilename.toRope, toRope($n.info.line)])
dispA(seeSrcRope, "$1", "", [ropeFormatNamedVars(docItemSeeSrc,

View File

@@ -92,7 +92,7 @@ proc rawImportSymbol(c: PContext, s: PSym) =
if s.kind == skConverter: addConverter(c, s)
if hasPattern(s): addPattern(c, s)
proc importSymbol(c: PContext, n: PNode, fromMod: PSym) =
proc importSymbol(c: PContext, n: PNode, fromMod: PSym) =
let ident = lookups.considerQuotedIdent(n)
let s = strTableGet(fromMod.tab, ident)
if s == nil:
@@ -153,12 +153,14 @@ proc importModuleAs(n: PNode, realModule: PSym): PSym =
localError(n.info, errGenerated, "module alias must be an identifier")
elif n.sons[1].ident.id != realModule.name.id:
# some misguided guy will write 'import abc.foo as foo' ...
result = createModuleAlias(realModule, n.sons[1].ident, n.sons[1].info)
result = createModuleAlias(realModule, n.sons[1].ident, realModule.info)
proc myImportModule(c: PContext, n: PNode): PSym =
proc myImportModule(c: PContext, n: PNode): PSym =
var f = checkModuleName(n)
if f != InvalidFileIDX:
result = importModuleAs(n, gImportModule(c.module, f))
if result.info.fileIndex == n.info.fileIndex:
localError(n.info, errGenerated, "A module cannot import itself")
if sfDeprecated in result.flags:
message(n.info, warnDeprecated, result.name.s)
@@ -171,7 +173,7 @@ proc evalImport(c: PContext, n: PNode): PNode =
# ``addDecl`` needs to be done before ``importAllSymbols``!
addDecl(c, m) # add symbol to symbol table of module
importAllSymbolsExcept(c, m, emptySet)
importForwarded(c, m.ast, emptySet)
#importForwarded(c, m.ast, emptySet)
proc evalFrom(c: PContext, n: PNode): PNode =
result = n
@@ -196,4 +198,4 @@ proc evalImportExcept*(c: PContext, n: PNode): PNode =
let ident = lookups.considerQuotedIdent(n.sons[i])
exceptSet.incl(ident.id)
importAllSymbolsExcept(c, m, exceptSet)
importForwarded(c, m.ast, exceptSet)
#importForwarded(c, m.ast, exceptSet)

View File

@@ -126,7 +126,7 @@ Files: "start.bat"
BinPath: r"bin;dist\mingw\bin;dist"
; Section | dir | zipFile | size hint (in KB) | url | exe start menu entry
Download: r"Documentation|doc|docs.zip|13824|http://nim-lang.org/download/docs-${version}.zip"
Download: r"Documentation|doc|docs.zip|13824|http://nim-lang.org/download/docs-${version}.zip|doc\overview.html"
Download: r"C Compiler (MingW)|dist|mingw.zip|82944|http://nim-lang.org/download/${mingw}.zip"
Download: r"Aporia IDE|dist|aporia.zip|97997|http://nim-lang.org/download/aporia-0.1.3.zip|aporia\bin\aporia.exe"
; for now only NSIS supports optional downloads

View File

@@ -1935,11 +1935,13 @@ proc semExport(c: PContext, n: PNode): PNode =
while s != nil:
if s.kind in ExportableSymKinds+{skModule}:
x.add(newSymNode(s, a.info))
strTableAdd(c.module.tab, s)
s = nextOverloadIter(o, c, a)
if c.module.ast.isNil:
c.module.ast = newNodeI(nkStmtList, n.info)
assert c.module.ast.kind == nkStmtList
c.module.ast.add x
when false:
if c.module.ast.isNil:
c.module.ast = newNodeI(nkStmtList, n.info)
assert c.module.ast.kind == nkStmtList
c.module.ast.add x
result = n
proc setGenericParams(c: PContext, n: PNode) =

View File

@@ -624,6 +624,9 @@ proc transformCall(c: PTransf, n: PNode): PTransNode =
# bugfix: check after 'transformSons' if it's still a method call:
# use the dispatcher for the call:
if s.sons[0].kind == nkSym and s.sons[0].sym.kind == skMethod:
let t = lastSon(s.sons[0].sym.ast)
if t.kind != nkSym or sfDispatcher notin t.sym.flags:
methodDef(s.sons[0].sym, false)
result = methodCall(s).PTransNode
else:
result = s.PTransNode

View File

@@ -41,7 +41,6 @@ doc.item = """
<dt id="$itemSym"><a name="$itemSymOrID"></a><pre>$header</pre></dt>
<dd>
$desc
$seeSrc
</dd>
"""
@@ -93,6 +92,11 @@ doc.file = """<?xml version="1.0" encoding="utf-8" ?>
<title>$title</title>
<style type="text/css">
body {
color: black;
background: white;
}
span.DecNumber {color: blue}
span.BinNumber {color: blue}
span.HexNumber {color: blue}

View File

@@ -113,7 +113,7 @@ proc nsis(args: string) =
" nsis compiler/nimrod") % NimrodVersion)
proc install(args: string) =
exec("$# cc -r $# --var:version=$# scripts compiler/nimrod.ini" %
exec("$# cc -r $# --var:version=$# --var:mingw=mingw32 scripts compiler/nimrod.ini" %
[findNim(), compileNimInst, NimrodVersion])
exec("sh ./install.sh $#" % args)

View File

@@ -620,6 +620,8 @@ proc `body=`*(someProc: PNimrodNode, val: PNimrodNode) {.compileTime.} =
someProc[high(someProc)] = val
else:
badNodeKind someProc.kind, "body="
proc basename*(a: PNimrodNode): PNimrodNode {.compiletime.}
proc `$`*(node: PNimrodNode): string {.compileTime.} =
@@ -627,6 +629,8 @@ proc `$`*(node: PNimrodNode): string {.compileTime.} =
case node.kind
of nnkIdent:
result = $node.ident
of nnkPostfix:
result = $node.basename.ident & "*"
of nnkStrLit..nnkTripleStrLit:
result = node.strVal
else:
@@ -669,7 +673,7 @@ proc insert*(a: PNimrodNode; pos: int; b: PNimrodNode) {.compileTime.} =
a[i + 1] = a[i]
a[pos] = b
proc basename*(a: PNimrodNode): PNimrodNode {.compiletime.} =
proc basename*(a: PNimrodNode): PNimrodNode =
## Pull an identifier from prefix/postfix expressions
case a.kind
of nnkIdent: return a

View File

@@ -57,7 +57,8 @@ when false:
binding: seq[MYSQL_BIND]
discard mysql_stmt_close(stmt)
proc dbQuote(s: string): string =
proc dbQuote*(s: string): string =
## DB quotes the string.
result = "'"
for c in items(s):
if c == '\'': add(result, "''")
@@ -69,7 +70,10 @@ proc dbFormat(formatstr: TSqlQuery, args: varargs[string]): string =
var a = 0
for c in items(string(formatstr)):
if c == '?':
add(result, dbQuote(args[a]))
if args[a] == nil:
add(result, "NULL")
else:
add(result, dbQuote(args[a]))
inc(a)
else:
add(result, c)
@@ -115,7 +119,10 @@ iterator fastRows*(db: TDbConn, query: TSqlQuery,
if row == nil: break
for i in 0..L-1:
setLen(result[i], 0)
add(result[i], row[i])
if row[i] == nil:
result[i] = nil
else:
add(result[i], row[i])
yield result
properFreeResult(sqlres, row)
@@ -132,7 +139,10 @@ proc getRow*(db: TDbConn, query: TSqlQuery,
if row != nil:
for i in 0..L-1:
setLen(result[i], 0)
add(result[i], row[i])
if row[i] == nil:
result[i] = nil
else:
add(result[i], row[i])
properFreeResult(sqlres, row)
proc getAllRows*(db: TDbConn, query: TSqlQuery,
@@ -150,7 +160,11 @@ proc getAllRows*(db: TDbConn, query: TSqlQuery,
if row == nil: break
setLen(result, j+1)
newSeq(result[j], L)
for i in 0..L-1: result[j][i] = $row[i]
for i in 0..L-1:
if row[i] == nil:
result[j][i] = nil
else:
result[j][i] = $row[i]
inc(j)
mysql.FreeResult(sqlres)

View File

@@ -48,7 +48,8 @@ proc dbError*(msg: string) {.noreturn.} =
e.msg = msg
raise e
proc dbQuote(s: string): string =
proc dbQuote*(s: string): string =
## DB quotes the string.
result = "'"
for c in items(s):
if c == '\'': add(result, "''")
@@ -60,7 +61,10 @@ proc dbFormat(formatstr: TSqlQuery, args: varargs[string]): string =
var a = 0
for c in items(string(formatstr)):
if c == '?':
add(result, dbQuote(args[a]))
if args[a] == nil:
add(result, "NULL")
else:
add(result, dbQuote(args[a]))
inc(a)
else:
add(result, c)
@@ -124,7 +128,10 @@ proc setRow(res: PPGresult, r: var TRow, line, cols: int32) =
for col in 0..cols-1:
setLen(r[col], 0)
var x = PQgetvalue(res, line, col)
add(r[col], x)
if x == nil:
r[col] = nil
else:
add(r[col], x)
iterator fastRows*(db: TDbConn, query: TSqlQuery,
args: varargs[string, `$`]): TRow {.tags: [FReadDB].} =

113
lib/impure/fenv.nim Normal file
View File

@@ -0,0 +1,113 @@
#
#
# Nimrod's Runtime Library
# (c) Copyright 2014 Andreas Rumpf
#
# See the file "copying.txt", included in this
# distribution, for details about the copyright.
#
## Floating-point environment. Handling of floating-point rounding and
## exceptions (overflow, zero-devide, etc.).
{.deadCodeElim:on.}
when defined(Posix) and not defined(haiku):
{.passl: "-lm".}
var
FE_DIVBYZERO* {.importc, header: "<fenv.h>".}: cint
## division by zero
FE_INEXACT* {.importc, header: "<fenv.h>".}: cint
## inexact result
FE_INVALID* {.importc, header: "<fenv.h>".}: cint
## invalid operation
FE_OVERFLOW* {.importc, header: "<fenv.h>".}: cint
## result not representable due to overflow
FE_UNDERFLOW* {.importc, header: "<fenv.h>".}: cint
## result not representable due to underflow
FE_ALL_EXCEPT* {.importc, header: "<fenv.h>".}: cint
## bitwise OR of all supported exceptions
FE_DOWNWARD* {.importc, header: "<fenv.h>".}: cint
## round toward -Inf
FE_TONEAREST* {.importc, header: "<fenv.h>".}: cint
## round to nearest
FE_TOWARDZERO* {.importc, header: "<fenv.h>".}: cint
## round toward 0
FE_UPWARD* {.importc, header: "<fenv.h>".}: cint
## round toward +Inf
FE_DFL_ENV* {.importc, header: "<fenv.h>".}: cint
## macro of type pointer to fenv_t to be used as the argument
## to functions taking an argument of type fenv_t; in this
## case the default environment will be used
type
TFloatClass* = enum ## describes the class a floating point value belongs to.
## This is the type that is returned by `classify`.
fcNormal, ## value is an ordinary nonzero floating point value
fcSubnormal, ## value is a subnormal (a very small) floating point value
fcZero, ## value is zero
fcNegZero, ## value is the negative zero
fcNan, ## value is Not-A-Number (NAN)
fcInf, ## value is positive infinity
fcNegInf ## value is negative infinity
Tfenv* {.importc: "fenv_t", header: "<fenv.h>", final, pure.} =
object ## Represents the entire floating-point environment. The
## floating-point environment refers collectively to any
## floating-point status flags and control modes supported
## by the implementation.
Tfexcept* {.importc: "fexcept_t", header: "<fenv.h>", final, pure.} =
object ## Represents the floating-point status flags collectively,
## including any status the implementation associates with the
## flags. A floating-point status flag is a system variable
## whose value is set (but never cleared) when a floating-point
## exception is raised, which occurs as a side effect of
## exceptional floating-point arithmetic to provide auxiliary
## information. A floating-point control mode is a system variable
## whose value may be set by the user to affect the subsequent
## behavior of floating-point arithmetic.
proc feclearexcept*(excepts: cint): cint {.importc, header: "<fenv.h>".}
## Clear the supported exceptions represented by `excepts`.
proc fegetexceptflag*(flagp: ptr Tfexcept, excepts: cint): cint {.
importc, header: "<fenv.h>".}
## Store implementation-defined representation of the exception flags
## indicated by `excepts` in the object pointed to by `flagp`.
proc feraiseexcept*(excepts: cint): cint {.importc, header: "<fenv.h>".}
## Raise the supported exceptions represented by `excepts`.
proc fesetexceptflag*(flagp: ptr Tfexcept, excepts: cint): cint {.
importc, header: "<fenv.h>".}
## Set complete status for exceptions indicated by `excepts` according to
## the representation in the object pointed to by `flagp`.
proc fetestexcept*(excepts: cint): cint {.importc, header: "<fenv.h>".}
## Determine which of subset of the exceptions specified by `excepts` are
## currently set.
proc fegetround*(): cint {.importc, header: "<fenv.h>".}
## Get current rounding direction.
proc fesetround*(roundingDirection: cint): cint {.importc, header: "<fenv.h>".}
## Establish the rounding direction represented by `roundingDirection`.
proc fegetenv*(envp: ptr Tfenv): cint {.importc, header: "<fenv.h>".}
## Store the current floating-point environment in the object pointed
## to by `envp`.
proc feholdexcept*(envp: ptr Tfenv): cint {.importc, header: "<fenv.h>".}
## Save the current environment in the object pointed to by `envp`, clear
## exception flags and install a non-stop mode (if available) for all
## exceptions.
proc fesetenv*(a1: ptr Tfenv): cint {.importc, header: "<fenv.h>".}
## Establish the floating-point environment represented by the object
## pointed to by `envp`.
proc feupdateenv*(envp: ptr Tfenv): cint {.importc, header: "<fenv.h>".}
## Save current exceptions in temporary storage, install environment
## represented by object pointed to by `envp` and raise exceptions
## according to saved exceptions.

View File

@@ -99,22 +99,6 @@ type
l_pid*: TPid ## Process ID of the process holding the lock;
## returned with F_GETLK.
Tfenv* {.importc: "fenv_t", header: "<fenv.h>", final, pure.} =
object ## Represents the entire floating-point environment. The
## floating-point environment refers collectively to any
## floating-point status flags and control modes supported
## by the implementation.
Tfexcept* {.importc: "fexcept_t", header: "<fenv.h>", final, pure.} =
object ## Represents the floating-point status flags collectively,
## including any status the implementation associates with the
## flags. A floating-point status flag is a system variable
## whose value is set (but never cleared) when a floating-point
## exception is raised, which occurs as a side effect of
## exceptional floating-point arithmetic to provide auxiliary
## information. A floating-point control mode is a system variable
## whose value may be set by the user to affect the subsequent
## behavior of floating-point arithmetic.
TFTW* {.importc: "struct FTW", header: "<ftw.h>", final, pure.} = object
base*: cint
level*: cint
@@ -834,18 +818,6 @@ var
## The application expects to access the specified data once and
## then not reuse it thereafter.
FE_DIVBYZERO* {.importc, header: "<fenv.h>".}: cint
FE_INEXACT* {.importc, header: "<fenv.h>".}: cint
FE_INVALID* {.importc, header: "<fenv.h>".}: cint
FE_OVERFLOW* {.importc, header: "<fenv.h>".}: cint
FE_UNDERFLOW* {.importc, header: "<fenv.h>".}: cint
FE_ALL_EXCEPT* {.importc, header: "<fenv.h>".}: cint
FE_DOWNWARD* {.importc, header: "<fenv.h>".}: cint
FE_TONEAREST* {.importc, header: "<fenv.h>".}: cint
FE_TOWARDZERO* {.importc, header: "<fenv.h>".}: cint
FE_UPWARD* {.importc, header: "<fenv.h>".}: cint
FE_DFL_ENV* {.importc, header: "<fenv.h>".}: cint
when not defined(haiku) and not defined(OpenBSD):
var
MM_HARD* {.importc, header: "<fmtmsg.h>".}: cint
@@ -1821,20 +1793,6 @@ proc posix_fadvise*(a1: cint, a2, a3: TOff, a4: cint): cint {.
proc posix_fallocate*(a1: cint, a2, a3: TOff): cint {.
importc, header: "<fcntl.h>".}
proc feclearexcept*(a1: cint): cint {.importc, header: "<fenv.h>".}
proc fegetexceptflag*(a1: ptr Tfexcept, a2: cint): cint {.
importc, header: "<fenv.h>".}
proc feraiseexcept*(a1: cint): cint {.importc, header: "<fenv.h>".}
proc fesetexceptflag*(a1: ptr Tfexcept, a2: cint): cint {.
importc, header: "<fenv.h>".}
proc fetestexcept*(a1: cint): cint {.importc, header: "<fenv.h>".}
proc fegetround*(): cint {.importc, header: "<fenv.h>".}
proc fesetround*(a1: cint): cint {.importc, header: "<fenv.h>".}
proc fegetenv*(a1: ptr Tfenv): cint {.importc, header: "<fenv.h>".}
proc feholdexcept*(a1: ptr Tfenv): cint {.importc, header: "<fenv.h>".}
proc fesetenv*(a1: ptr Tfenv): cint {.importc, header: "<fenv.h>".}
proc feupdateenv*(a1: ptr Tfenv): cint {.importc, header: "<fenv.h>".}
when not defined(haiku) and not defined(OpenBSD):
proc fmtmsg*(a1: int, a2: cstring, a3: cint,
a4, a5, a6: cstring): cint {.importc, header: "<fmtmsg.h>".}

View File

@@ -115,3 +115,60 @@ macro `->`*(p, b: expr): expr {.immediate.} =
## f(2, 2)
result = createProcType(p, b)
type ListComprehension = object
var lc*: ListComprehension
macro `[]`*(lc: ListComprehension, comp, typ: expr): expr =
## List comprehension, returns a sequence. `comp` is the actual list
## comprehension, for example ``x | (x <- 1..10, x mod 2 == 0)``. `typ` is
## the type that will be stored inside the result seq.
##
## .. code-block:: nimrod
##
## echo lc[x | (x <- 1..10, x mod 2 == 0), int]
##
## const n = 20
## echo lc[(x,y,z) | (x <- 1..n, y <- x..n, z <- y..n, x*x + y*y == z*z),
## tuple[a,b,c: int]]
expectLen(comp, 3)
expectKind(comp, nnkInfix)
expectKind(comp[0], nnkIdent)
assert($comp[0].ident == "|")
result = newCall(
newDotExpr(
newIdentNode("result"),
newIdentNode("add")),
comp[1])
for i in countdown(comp[2].len-1, 0):
let x = comp[2][i]
expectKind(x, nnkInfix)
expectMinLen(x, 1)
if x[0].kind == nnkIdent and $x[0].ident == "<-":
expectLen(x, 3)
result = newNimNode(nnkForStmt).add(x[1], x[2], result)
else:
result = newIfStmt((x, result))
result = newNimNode(nnkCall).add(
newNimNode(nnkPar).add(
newNimNode(nnkLambda).add(
newEmptyNode(),
newEmptyNode(),
newEmptyNode(),
newNimNode(nnkFormalParams).add(
newNimNode(nnkBracketExpr).add(
newIdentNode("seq"),
typ)),
newEmptyNode(),
newEmptyNode(),
newStmtList(
newAssignment(
newIdentNode("result"),
newNimNode(nnkPrefix).add(
newIdentNode("@"),
newNimNode(nnkBracket))),
result))))

View File

@@ -7,47 +7,71 @@
# distribution, for details about the copyright.
#
## This module implements a simple high performance `JSON`:idx:
## parser. JSON (JavaScript Object Notation) is a lightweight
## data-interchange format that is easy for humans to read and write
## (unlike XML). It is easy for machines to parse and generate.
## JSON is based on a subset of the JavaScript Programming Language,
## Standard ECMA-262 3rd Edition - December 1999.
## This module implements a simple high performance `JSON`:idx: parser. `JSON
## (JavaScript Object Notation) <http://www.json.org>`_ is a lightweight
## data-interchange format that is easy for humans to read and write (unlike
## XML). It is easy for machines to parse and generate. JSON is based on a
## subset of the JavaScript Programming Language, `Standard ECMA-262 3rd
## Edition - December 1999
## <http://www.ecma-international.org/publications/files/ECMA-ST/Ecma-262.pdf>`_.
##
## Usage example:
## Parsing small values quickly can be done with the convenience `parseJson()
## <#parseJson,string>`_ proc which returns the whole JSON tree. If you are
## parsing very big JSON inputs or want to skip most of the items in them you
## can initialize your own `TJsonParser <#TJsonParser>`_ with the `open()
## <#open>`_ proc and call `next() <#next>`_ in a loop to process the
## individual parsing events.
##
## If you need to create JSON objects from your Nimrod types you can call procs
## like `newJObject() <#newJObject>`_ (or their equivalent `%()
## <#%,openArray[tuple[string,PJsonNode]]>`_ generic constructor). For
## consistency you can provide your own ``%`` operators for custom object
## types:
##
## .. code-block:: nimrod
## let
## small_json = """{"test": 1.3, "key2": true}"""
## jobj = parseJson(small_json)
## assert (jobj.kind == JObject)
## echo($jobj["test"].fnum)
## echo($jobj["key2"].bval)
## type
## Person = object ## Generic person record.
## age: int ## The age of the person.
## name: string ## The name of the person.
##
## Results in:
## proc `%`(p: Person): PJsonNode =
## ## Converts a Person into a PJsonNode.
## result = %[("age", %p.age), ("name", %p.name)]
##
## .. code-block:: nimrod
## proc test() =
## # Tests making some jsons.
## var p: Person
## p.age = 24
## p.name = "Minah"
## echo(%p) # { "age": 24, "name": "Minah"}
##
## 1.3000000000000000e+00
## true
## p.age = 33
## p.name = "Sojin"
## echo(%p) # { "age": 33, "name": "Sojin"}
##
## If you don't need special logic in your Nimrod objects' serialization code
## you can also use the `marshal module <marshal.html>`_ which converts objects
## directly to JSON.
import
hashes, strutils, lexbase, streams, unicode
type
TJsonEventKind* = enum ## enumeration of all events that may occur when parsing
jsonError, ## an error ocurred during parsing
jsonEof, ## end of file reached
jsonString, ## a string literal
jsonInt, ## an integer literal
jsonFloat, ## a float literal
jsonTrue, ## the value ``true``
jsonFalse, ## the value ``false``
jsonNull, ## the value ``null``
jsonObjectStart, ## start of an object: the ``{`` token
jsonObjectEnd, ## end of an object: the ``}`` token
jsonArrayStart, ## start of an array: the ``[`` token
jsonArrayEnd ## start of an array: the ``]`` token
TJsonEventKind* = enum ## Events that may occur when parsing. \
##
## You compare these values agains the result of the `kind() proc <#kind>`_.
jsonError, ## An error ocurred during parsing.
jsonEof, ## End of file reached.
jsonString, ## A string literal.
jsonInt, ## An integer literal.
jsonFloat, ## A float literal.
jsonTrue, ## The value ``true``.
jsonFalse, ## The value ``false``.
jsonNull, ## The value ``null``.
jsonObjectStart, ## Start of an object: the ``{`` token.
jsonObjectEnd, ## End of an object: the ``}`` token.
jsonArrayStart, ## Start of an array: the ``[`` token.
jsonArrayEnd ## Start of an array: the ``]`` token.
TTokKind = enum # must be synchronized with TJsonEventKind!
tkError,
@@ -65,7 +89,7 @@ type
tkColon,
tkComma
TJsonError* = enum ## enumeration that lists all errors that can occur
TJsonError = enum ## enumeration that lists all errors that can occur
errNone, ## no error
errInvalidToken, ## invalid token
errStringExpected, ## string expected
@@ -82,7 +106,9 @@ type
stateEof, stateStart, stateObject, stateArray, stateExpectArrayComma,
stateExpectObjectComma, stateExpectColon, stateExpectValue
TJsonParser* = object of TBaseLexer ## the parser object.
TJsonParser* = object of TBaseLexer ## The JSON parser object. \
##
## Create a variable of this type and use `open() <#open>`_ on it.
a: string
tok: TTokKind
kind: TJsonEventKind
@@ -117,59 +143,129 @@ const
]
proc open*(my: var TJsonParser, input: PStream, filename: string) =
## initializes the parser with an input stream. `Filename` is only used
## for nice error messages.
lexbase.open(my, input)
## Initializes the JSON parser with an `input stream <streams.html>`_.
##
## The `filename` parameter is not strictly required and is used only for
## nice error messages. You can pass ``nil`` as long as you never use procs
## like `errorMsg() <#errorMsg>`_ or `errorMsgExpected()
## <#errorMsgExpected>`_ but passing a dummy filename like ``<input string>``
## is safer and more user friendly. Example:
##
## .. code-block:: nimrod
## import json, streams
##
## var
## s = newStringStream("some valid json")
## p: TJsonParser
## p.open(s, "<input string>")
##
## Once opened, you can process JSON parsing events with the `next()
## <#next>`_ proc.
my.filename = filename
my.state = @[stateStart]
my.kind = jsonError
my.a = ""
proc close*(my: var TJsonParser) {.inline.} =
## closes the parser `my` and its associated input stream.
proc close*(my: var TJsonParser) {.inline.} =
## Closes the parser `my` and its associated input stream.
##
## Example:
##
## .. code-block:: nimrod
## var
## s = newStringStream("some valid json")
## p: TJsonParser
## p.open(s, "<input string>")
## finally: p.close
## # write here parsing of input
lexbase.close(my)
proc str*(my: TJsonParser): string {.inline.} =
## returns the character data for the events: ``jsonInt``, ``jsonFloat``,
## ``jsonString``
## Returns the character data for the `events <#TJsonEventKind>`_
## ``jsonInt``, ``jsonFloat`` and ``jsonString``.
##
## This proc will `assert <system.html#assert>`_ in debug builds when used
## with other event types. See `next() <#next>`_ for an usage example.
assert(my.kind in {jsonInt, jsonFloat, jsonString})
return my.a
proc getInt*(my: TJsonParser): BiggestInt {.inline.} =
## returns the number for the event: ``jsonInt``
## Returns the number for the `jsonInt <#TJsonEventKind>`_ event.
##
## This proc will `assert <system.html#assert>`_ in debug builds when used
## with other event types. See `next() <#next>`_ for an usage example.
assert(my.kind == jsonInt)
return parseBiggestInt(my.a)
proc getFloat*(my: TJsonParser): float {.inline.} =
## returns the number for the event: ``jsonFloat``
## Returns the number for the `jsonFloat <#TJsonEventKind>`_ event.
##
## This proc will `assert <system.html#assert>`_ in debug builds when used
## with other event types. See `next() <#next>`_ for an usage example.
assert(my.kind == jsonFloat)
return parseFloat(my.a)
proc kind*(my: TJsonParser): TJsonEventKind {.inline.} =
## returns the current event type for the JSON parser
## Returns the current event type for the `JSON parser <#TJsonParser>`_.
##
## Call this proc just after `next() <#next>`_ to act on the new event.
return my.kind
proc getColumn*(my: TJsonParser): int {.inline.} =
## get the current column the parser has arrived at.
## Get the current column the parser has arrived at.
##
## While this is mostly used by procs like `errorMsg() <#errorMsg>`_ you can
## use it as well to show user warnings if you are validating JSON values
## during parsing. See `next() <#next>`_ for the full example:
##
## .. code-block:: nimrod
## case parser.kind
## ...
## of jsonString:
## let inputValue = parser.str
## if previousValues.contains(inputValue):
## echo "$1($2, $3) Warning: repeated value '$4'" % [
## parser.getFilename, $parser.getLine, $parser.getColumn,
## inputValue]
## ...
result = getColNumber(my, my.bufpos)
proc getLine*(my: TJsonParser): int {.inline.} =
## get the current line the parser has arrived at.
## Get the current line the parser has arrived at.
##
## While this is mostly used by procs like `errorMsg() <#errorMsg>`_ you can
## use it as well to indicate user warnings if you are validating JSON values
## during parsing. See `next() <#next>`_ and `getColumn() <#getColumn>`_ for
## examples.
result = my.lineNumber
proc getFilename*(my: TJsonParser): string {.inline.} =
## get the filename of the file that the parser processes.
## Get the filename of the file that the parser is processing.
##
## This is the value you pass to the `open() <#open>`_ proc. While this is
## mostly used by procs like `errorMsg() <#errorMsg>`_ you can use it as well
## to indicate user warnings if you are validating JSON values during
## parsing. See `next() <#next>`_ and `getColumn() <#getColumn>`_ for
## examples.
result = my.filename
proc errorMsg*(my: TJsonParser): string =
## returns a helpful error message for the event ``jsonError``
## Returns a helpful error message for the `jsonError <#TJsonEventKind>`_
## event.
##
## This proc will `assert <system.html#assert>`_ in debug builds when used
## with other event types. See `next() <#next>`_ for an usage example.
assert(my.kind == jsonError)
result = "$1($2, $3) Error: $4" % [
my.filename, $getLine(my), $getColumn(my), errorMessages[my.err]]
proc errorMsgExpected*(my: TJsonParser, e: string): string =
## returns an error message "`e` expected" in the same format as the
## other error messages
## Returns an error message "`e` expected".
##
## The message is in the same format as the other error messages which
## include the parser filename, line and column values. This is used by
## `raiseParseErr() <#raiseParseErr>`_ to raise an `EJsonParsingError
## <#EJsonParsingError>`_.
result = "$1($2, $3) Error: $4" % [
my.filename, $getLine(my), $getColumn(my), e & " expected"]
@@ -382,7 +478,32 @@ proc getTok(my: var TJsonParser): TTokKind =
my.tok = result
proc next*(my: var TJsonParser) =
## retrieves the first/next event. This controls the parser.
## Retrieves the first/next event for the `JSON parser <#TJsonParser>`_.
##
## You are meant to call this method inside an infinite loop. After each
## call, check the result of the `kind() <#kind>`_ proc to know what has to
## be done next (eg. break out due to end of file). Here is a basic example
## which simply echoes all found elements by the parser:
##
## .. code-block:: nimrod
## parser.open(stream, "<input string>")
## while true:
## parser.next
## case parser.kind
## of jsonError:
## echo parser.errorMsg
## break
## of jsonEof: break
## of jsonString: echo parser.str
## of jsonInt: echo parser.getInt
## of jsonFloat: echo parser.getFloat
## of jsonTrue: echo "true"
## of jsonFalse: echo "false"
## of jsonNull: echo "null"
## of jsonObjectStart: echo "{"
## of jsonObjectEnd: echo "}"
## of jsonArrayStart: echo "["
## of jsonArrayEnd: echo "]"
var tk = getTok(my)
var i = my.state.len-1
# the following code is a state machine. If we had proper coroutines,
@@ -502,7 +623,16 @@ proc next*(my: var TJsonParser) =
# ------------- higher level interface ---------------------------------------
type
TJsonNodeKind* = enum ## possible JSON node types
TJsonNodeKind* = enum ## Possible `JSON node <#TJsonNodeKind>`_ types. \
##
## To build nodes use the helper procs
## `newJNull() <#newJNull>`_,
## `newJBool() <#newJBool>`_,
## `newJInt() <#newJInt>`_,
## `newJFloat() <#newJFloat>`_,
## `newJString() <#newJString>`_,
## `newJObject() <#newJObject>`_ and
## `newJArray() <#newJArray>`_.
JNull,
JBool,
JInt,
@@ -511,8 +641,9 @@ type
JObject,
JArray
PJsonNode* = ref TJsonNode ## JSON node
TJsonNode* {.final, pure, acyclic.} = object
PJsonNode* = ref TJsonNode ## Reference to a `JSON node <#TJsonNode>`_.
TJsonNode* {.final, pure, acyclic.} = object ## `Object variant \
## <manual.html#object-variants>`_ wrapping all possible JSON types.
case kind*: TJsonNodeKind
of JString:
str*: string
@@ -529,14 +660,36 @@ type
of JArray:
elems*: seq[PJsonNode]
EJsonParsingError* = object of EInvalidValue ## is raised for a JSON error
EJsonParsingError* = object of EInvalidValue ## Raised during JSON parsing. \
##
## Example:
##
## .. code-block:: nimrod
## let smallJson = """{"test: 1.3, "key2": true}"""
## try:
## discard parseJson(smallJson)
## # --> Bad JSON! input(1, 18) Error: : expected
## except EJsonParsingError:
## echo "Bad JSON! " & getCurrentExceptionMsg()
proc raiseParseErr*(p: TJsonParser, msg: string) {.noinline, noreturn.} =
## raises an `EJsonParsingError` exception.
## Raises an `EJsonParsingError <#EJsonParsingError>`_ exception.
##
## The message for the exception will be built passing the `msg` parameter to
## the `errorMsgExpected() <#errorMsgExpected>`_ proc.
raise newException(EJsonParsingError, errorMsgExpected(p, msg))
proc newJString*(s: string): PJsonNode =
## Creates a new `JString PJsonNode`.
## Creates a new `JString PJsonNode <#TJsonNodeKind>`_.
##
## Example:
##
## .. code-block:: nimrod
## var node = newJString("A string")
## echo node
## # --> "A string"
##
## Or you can use the shorter `%() proc <#%,string>`_.
new(result)
result.kind = JString
result.str = s
@@ -547,80 +700,206 @@ proc newJStringMove(s: string): PJsonNode =
shallowCopy(result.str, s)
proc newJInt*(n: BiggestInt): PJsonNode =
## Creates a new `JInt PJsonNode`.
## Creates a new `JInt PJsonNode <#TJsonNodeKind>`_.
##
## Example:
##
## .. code-block:: nimrod
## var node = newJInt(900_100_200_300)
## echo node
## # --> 900100200300
##
## Or you can use the shorter `%() proc <#%,BiggestInt>`_.
new(result)
result.kind = JInt
result.num = n
proc newJFloat*(n: float): PJsonNode =
## Creates a new `JFloat PJsonNode`.
## Creates a new `JFloat PJsonNode <#TJsonNodeKind>`_.
##
## Example:
##
## .. code-block:: nimrod
## var node = newJFloat(3.14)
## echo node
## # --> 3.14
##
## Or you can use the shorter `%() proc <#%,float>`_.
new(result)
result.kind = JFloat
result.fnum = n
proc newJBool*(b: bool): PJsonNode =
## Creates a new `JBool PJsonNode`.
## Creates a new `JBool PJsonNode <#TJsonNodeKind>`_.
##
## Example:
##
## .. code-block:: nimrod
## var node = newJBool(true)
## echo node
## # --> true
##
## Or you can use the shorter `%() proc <#%,bool>`_.
new(result)
result.kind = JBool
result.bval = b
proc newJNull*(): PJsonNode =
## Creates a new `JNull PJsonNode`.
## Creates a new `JNull PJsonNode <#TJsonNodeKind>`_.
##
## Example:
##
## .. code-block:: nimrod
## var node = newJNull()
## echo node
## # --> null
new(result)
proc newJObject*(): PJsonNode =
## Creates a new `JObject PJsonNode`
## Creates a new `JObject PJsonNode <#TJsonNodeKind>`_.
##
## The `PJsonNode <#PJsonNode>`_ will be initialized with an empty ``fields``
## sequence to which you can add new elements. Example:
##
## .. code-block:: nimrod
## var node = newJObject()
## node.add("age", newJInt(24))
## node.add("name", newJString("Minah"))
## echo node
## # --> { "age": 24, "name": "Minah"}
##
## Or you can use the shorter `%() proc
## <#%,openArray[tuple[string,PJsonNode]]>`_.
new(result)
result.kind = JObject
result.fields = @[]
proc newJArray*(): PJsonNode =
## Creates a new `JArray PJsonNode`
## Creates a new `JArray PJsonNode <#TJsonNodeKind>`_.
##
## The `PJsonNode <#PJsonNode>`_ will be initialized with an empty ``elems``
## sequence to which you can add new elements. Example:
##
## .. code-block:: nimrod
## var node = newJArray()
## node.add(newJString("Mixing types"))
## node.add(newJInt(42))
## node.add(newJString("is madness"))
## node.add(newJFloat(3.14))
## echo node
## # --> [ "Mixing types", 42, "is madness", 3.14]
##
## Or you can use the shorter `%() proc <#%,openArray[PJsonNode]>`_.
new(result)
result.kind = JArray
result.elems = @[]
proc `%`*(s: string): PJsonNode =
## Generic constructor for JSON data. Creates a new `JString PJsonNode`.
## Creates a new `JString PJsonNode <#TJsonNodeKind>`_.
##
## Example:
##
## .. code-block:: nimrod
## var node = %"A string"
## echo node
## # --> "A string"
##
## This generic constructor is equivalent to the `newJString()
## <#newJString>`_ proc.
new(result)
result.kind = JString
result.str = s
proc `%`*(n: BiggestInt): PJsonNode =
## Generic constructor for JSON data. Creates a new `JInt PJsonNode`.
## Creates a new `JInt PJsonNode <#TJsonNodeKind>`_.
##
## Example:
##
## .. code-block:: nimrod
## var node = %900_100_200_300
## echo node
## # --> 900100200300
##
## This generic constructor is equivalent to the `newJInt() <#newJInt>`_
## proc.
new(result)
result.kind = JInt
result.num = n
proc `%`*(n: float): PJsonNode =
## Generic constructor for JSON data. Creates a new `JFloat PJsonNode`.
## Creates a new `JFloat PJsonNode <#TJsonNodeKind>`_.
##
## Example:
##
## .. code-block:: nimrod
## var node = %3.14
## echo node
## # --> 3.14
##
## This generic constructor is equivalent to the `newJFloat() <#newJFloat>`_
## proc.
new(result)
result.kind = JFloat
result.fnum = n
proc `%`*(b: bool): PJsonNode =
## Generic constructor for JSON data. Creates a new `JBool PJsonNode`.
## Creates a new `JBool PJsonNode <#TJsonNodeKind>`_.
##
## Example:
##
## .. code-block:: nimrod
## var node = %true
## echo node
## # --> true
##
## This generic constructor is equivalent to the `newJBool() <#newJBool>`_
## proc.
new(result)
result.kind = JBool
result.bval = b
proc `%`*(keyVals: openArray[tuple[key: string, val: PJsonNode]]): PJsonNode =
## Generic constructor for JSON data. Creates a new `JObject PJsonNode`
## Creates a new `JObject PJsonNode <#TJsonNodeKind>`_.
##
## Unlike the `newJObject() <#newJObject>`_ proc, which returns an object
## that has to be further manipulated, you can use this generic constructor
## to create JSON objects with all their fields in one go. Example:
##
## .. code-block:: nimrod
## let node = %[("age", %24), ("name", %"Minah")]
## echo node
## # --> { "age": 24, "name": "Minah"}
new(result)
result.kind = JObject
newSeq(result.fields, keyVals.len)
for i, p in pairs(keyVals): result.fields[i] = p
proc `%`*(elements: openArray[PJsonNode]): PJsonNode =
## Generic constructor for JSON data. Creates a new `JArray PJsonNode`
## Creates a new `JArray PJsonNode <#TJsonNodeKind>`_.
##
## Unlike the `newJArray() <#newJArray>`_ proc, which returns an object
## that has to be further manipulated, you can use this generic constructor
## to create JSON arrays with all their values in one go. Example:
##
## .. code-block:: nimrod
## let node = %[%"Mixing types", %42,
## %"is madness", %3.14,]
## echo node
## # --> [ "Mixing types", 42, "is madness", 3.14]
new(result)
result.kind = JArray
newSeq(result.elems, elements.len)
for i, p in pairs(elements): result.elems[i] = p
proc `==`* (a,b: PJsonNode): bool =
## Check two nodes for equality
## Check two `PJsonNode <#PJsonNode>`_ nodes for equality.
##
## Example:
##
## .. code-block:: nimrod
## assert(%1 == %1)
## assert(%1 != %2)
if a.isNil:
if b.isNil: return true
return false
@@ -644,7 +923,21 @@ proc `==`* (a,b: PJsonNode): bool =
a.fields == b.fields
proc hash* (n:PJsonNode): THash =
## Compute the hash for a JSON node
## Computes the hash for a JSON node.
##
## The `THash <hashes.html#THash>`_ allows JSON nodes to be used as keys for
## `sets <sets.html>`_ or `tables <tables.html>`_. Example:
##
## .. code-block:: nimrod
## import json, sets
##
## var
## uniqueValues = initSet[PJsonNode]()
## values = %[%1, %2, %1, %2, %3]
## for value in values.elems:
## discard uniqueValues.containsOrIncl(value)
## echo uniqueValues
## # --> {1, 2, 3}
case n.kind
of JArray:
result = hash(n.elems)
@@ -662,17 +955,40 @@ proc hash* (n:PJsonNode): THash =
result = hash(0)
proc len*(n: PJsonNode): int =
## If `n` is a `JArray`, it returns the number of elements.
## If `n` is a `JObject`, it returns the number of pairs.
## Else it returns 0.
## Returns the number of children items for this `PJsonNode <#PJsonNode>`_.
##
## If `n` is a `JArray <#TJsonNodeKind>`_, it will return the number of
## elements. If `n` is a `JObject <#TJsonNodeKind>`_, it will return the
## number of key-value pairs. For all other types this proc returns zero.
## Example:
##
## .. code-block:: nimrod
## let
## n1 = %[("age", %33), ("name", %"Sojin")]
## n2 = %[%1, %2, %3, %4, %5, %6, %7]
## n3 = %"Some odd string we have here"
## echo n1.len # --> 2
## echo n2.len # --> 7
## echo n3.len # --> 0
##
case n.kind
of JArray: result = n.elems.len
of JObject: result = n.fields.len
else: discard
proc `[]`*(node: PJsonNode, name: string): PJsonNode =
## Gets a field from a `JObject`, which must not be nil.
## If the value at `name` does not exist, returns nil
## Gets a named field from a `JObject <#TJsonNodeKind>`_ `PJsonNode
## <#PJsonNode>`_.
##
## Returns the value for `name` or nil if `node` doesn't contain such a
## field. This proc will `assert <system.html#assert>`_ in debug builds if
## `name` is ``nil`` or `node` is not a ``JObject``. On release builds it
## will likely crash. Example:
##
## .. code-block:: nimrod
## let node = %[("age", %40), ("name", %"Britney")]
## echo node["name"]
## # --> "Britney"
assert(not isNil(node))
assert(node.kind == JObject)
for key, item in items(node.fields):
@@ -681,35 +997,92 @@ proc `[]`*(node: PJsonNode, name: string): PJsonNode =
return nil
proc `[]`*(node: PJsonNode, index: int): PJsonNode =
## Gets the node at `index` in an Array. Result is undefined if `index`
## is out of bounds
## Gets the `index` item from a `JArray <#TJsonNodeKind>`_ `PJsonNode
## <#PJsonNode>`_.
##
## Returns the specified item. Result is undefined if `index` is out of
## bounds. This proc will `assert <system.html#assert>`_ in debug builds if
## `node` is ``nil`` or not a ``JArray``. Example:
##
## .. code-block:: nimrod
## let node = %[%"Mixing types", %42,
## %"is madness", %3.14,]
## echo node[2]
## # --> "is madness"
assert(not isNil(node))
assert(node.kind == JArray)
return node.elems[index]
proc hasKey*(node: PJsonNode, key: string): bool =
## Checks if `key` exists in `node`.
## Returns `true` if `key` exists in a `JObject <#TJsonNodeKind>`_ `PJsonNode
## <#PJsonNode>`_.
##
## This proc will `assert <system.html#assert>`_ in debug builds if `node` is
## not a ``JObject``. On release builds it will likely crash. Example:
##
## .. code-block:: nimrod
## let node = %[("age", %40), ("name", %"Britney")]
## echo node.hasKey("email")
## # --> false
assert(node.kind == JObject)
for k, item in items(node.fields):
if k == key: return true
proc existsKey*(node: PJsonNode, key: string): bool {.deprecated.} = node.hasKey(key)
## Deprecated for `hasKey`
## Deprecated for `hasKey() <#hasKey>`_.
proc add*(father, child: PJsonNode) =
## Adds `child` to a JArray node `father`.
## Adds `child` to a `JArray <#TJsonNodeKind>`_ `PJsonNode <#PJsonNode>`_
## `father` node.
##
## This proc will `assert <system.html#assert>`_ in debug builds if `node` is
## not a ``JArray``. On release builds it will likely crash. Example:
##
## .. code-block:: nimrod
## var node = %[%"Mixing types", %42]
## node.add(%"is madness")
## echo node
## # --> false
assert father.kind == JArray
father.elems.add(child)
proc add*(obj: PJsonNode, key: string, val: PJsonNode) =
## Adds ``(key, val)`` pair to the JObject node `obj`. For speed
## reasons no check for duplicate keys is performed!
## But ``[]=`` performs the check.
## Adds ``(key, val)`` pair to a `JObject <#TJsonNodeKind>`_ `PJsonNode
## <#PJsonNode>`_ `obj` node.
##
## For speed reasons no check for duplicate keys is performed! But ``[]=``
## performs the check.
##
## This proc will `assert <system.html#assert>`_ in debug builds if `node` is
## not a ``JObject``. On release builds it will likely crash. Example:
##
## .. code-block:: nimrod
## var node = newJObject()
## node.add("age", newJInt(12))
## # This is wrong! But we need speed…
## node.add("age", newJInt(24))
## echo node
## # --> { "age": 12, "age": 24}
assert obj.kind == JObject
obj.fields.add((key, val))
proc `[]=`*(obj: PJsonNode, key: string, val: PJsonNode) =
## Sets a field from a `JObject`. Performs a check for duplicate keys.
## Sets a field from a `JObject <#TJsonNodeKind>`_ `PJsonNode
## <#PJsonNode>`_ `obj` node.
##
## Unlike the `add() <#add,PJsonNode,string,PJsonNode>`_ proc this will
## perform a check for duplicate keys and replace existing values.
##
## This proc will `assert <system.html#assert>`_ in debug builds if `node` is
## not a ``JObject``. On release builds it will likely crash. Example:
##
## .. code-block:: nimrod
## var node = newJObject()
## node["age"] = %12
## # The new value replaces the previous one.
## node["age"] = %24
## echo node
## # --> { "age": 24}
assert(obj.kind == JObject)
for i in 0..obj.fields.len-1:
if obj.fields[i].key == key:
@@ -736,6 +1109,18 @@ proc `{}=`*(node: PJsonNode, names: varargs[string], value: PJsonNode) =
proc delete*(obj: PJsonNode, key: string) =
## Deletes ``obj[key]`` preserving the order of the other (key, value)-pairs.
##
## If `key` doesn't exist in `obj` ``EInvalidIndex`` will be raised. This
## proc will `assert <system.html#assert>`_ in debug builds if `node` is not
## a ``JObject``. On release builds it will likely crash. Example:
##
## .. code-block:: nimrod
## var node = %[("age", %37), ("name", %"Chris"), ("male", %false)]
## echo node
## # --> { "age": 37, "name": "Chris", "male": false}
## node.delete("age")
## echo node
## # --> { "name": "Chris", "male": false}
assert(obj.kind == JObject)
for i in 0..obj.fields.len-1:
if obj.fields[i].key == key:
@@ -744,7 +1129,9 @@ proc delete*(obj: PJsonNode, key: string) =
raise newException(EInvalidIndex, "key not in object")
proc copy*(p: PJsonNode): PJsonNode =
## Performs a deep copy of `a`.
## Performs a deep copy of `p`.
##
## Modifications to the copy won't affect the original.
case p.kind
of JString:
result = newJString(p.str)
@@ -779,6 +1166,12 @@ proc nl(s: var string, ml: bool) =
proc escapeJson*(s: string): string =
## Converts a string `s` to its JSON representation.
##
## Example:
##
## .. code-block:: nimrod
## echo """name: "Torbjørn"""".escapeJson
## # --> "name: \"Torbj\u00F8rn\""
result = newStringOfCap(s.len + s.len shr 3)
result.add("\"")
for x in runes(s):
@@ -850,24 +1243,58 @@ proc toPretty(result: var string, node: PJsonNode, indent = 2, ml = true,
result.add("null")
proc pretty*(node: PJsonNode, indent = 2): string =
## Converts `node` to its JSON Representation, with indentation and
## on multiple lines.
## Converts `node` to a pretty JSON representation.
##
## The representation will have indentation use multiple lines. Example:
##
## .. code-block:: nimrod
## let node = %[("age", %33), ("name", %"Sojin")]
## echo node
## # --> { "age": 33, "name": "Sojin"}
## echo node.pretty
## # --> {
## # "age": 33,
## # "name": "Sojin"
## # }
result = ""
toPretty(result, node, indent)
proc `$`*(node: PJsonNode): string =
## Converts `node` to its JSON Representation on one line.
## Converts `node` to its JSON representation on one line.
result = ""
toPretty(result, node, 1, false)
iterator items*(node: PJsonNode): PJsonNode =
## Iterator for the items of `node`. `node` has to be a JArray.
## Iterator for the items of `node`.
##
## This proc will `assert <system.html#assert>`_ in debug builds if `node` is
## not a `JArray <#TJsonNodeKind>`_. On release builds it will likely crash.
## Example:
##
## .. code-block:: nimrod
## let numbers = %[%1, %2, %3]
## for n in numbers.items:
## echo "Number ", n
## ## --> Number 1
## ## Number 2
## ## Number 3
assert node.kind == JArray
for i in items(node.elems):
yield i
iterator pairs*(node: PJsonNode): tuple[key: string, val: PJsonNode] =
## Iterator for the child elements of `node`. `node` has to be a JObject.
## Iterator for the child elements of `node`.
##
## This proc will `assert <system.html#assert>`_ in debug builds if `node` is
## not a `JObject <#TJsonNodeKind>`_. On release builds it will likely crash.
## Example:
##
## .. code-block:: nimrod
## var node = %[("age", %37), ("name", %"Chris")]
## for key, value in node.pairs:
## echo "Key: ", key, ", value: ", value
## # --> Key: age, value: 37
## # Key: name, value: "Chris"
assert node.kind == JObject
for key, val in items(node.fields):
yield (key, val)
@@ -926,8 +1353,12 @@ proc parseJson(p: var TJsonParser): PJsonNode =
when not defined(js):
proc parseJson*(s: PStream, filename: string): PJsonNode =
## Parses from a stream `s` into a `PJsonNode`. `filename` is only needed
## for nice error messages.
## Generic convenience proc to parse stream `s` into a `PJsonNode`.
##
## This wraps around `open() <#open>`_ and `next() <#next>`_ to return the
## full JSON DOM. Errors will be raised as exceptions, this requires the
## `filename` parameter to not be ``nil`` to avoid crashes.
assert(not isNil(filename))
var p: TJsonParser
p.open(s, filename)
discard getTok(p) # read first token
@@ -936,10 +1367,28 @@ when not defined(js):
proc parseJson*(buffer: string): PJsonNode =
## Parses JSON from `buffer`.
##
## Specialized version around `parseJson(PStream, string)
## <#parseJson,PStream,string>`_. Example:
##
## .. code-block:: nimrod
## let
## smallJson = """{"test": 1.3, "key2": true}"""
## jobj = parseJson(smallJson)
## assert jobj.kind == JObject
##
## assert jobj["test"].kind == JFloat
## echo jobj["test"].fnum # --> 1.3
##
## assert jobj["key2"].kind == JBool
## echo jobj["key2"].bval # --> true
result = parseJson(newStringStream(buffer), "input")
proc parseFile*(filename: string): PJsonNode =
## Parses `file` into a `PJsonNode`.
##
## Specialized version around `parseJson(PStream, string)
## <#parseJson,PStream,string>`_.
var stream = newFileStream(filename, fmRead)
if stream == nil:
raise newException(EIO, "cannot read from file: " & filename)

View File

@@ -14,7 +14,7 @@
## <backends.html#the-javascript-target>`_.
include "system/inclrtl"
import "impure/fenv"
{.push debugger:off .} # the user does not want to trace a part
# of the standard library!
@@ -40,17 +40,6 @@ const
## after the decimal point
## for Nimrod's ``float`` type.
type
TFloatClass* = enum ## describes the class a floating point value belongs to.
## This is the type that is returned by `classify`.
fcNormal, ## value is an ordinary nonzero floating point value
fcSubnormal, ## value is a subnormal (a very small) floating point value
fcZero, ## value is zero
fcNegZero, ## value is the negative zero
fcNan, ## value is Not-A-Number (NAN)
fcInf, ## value is positive infinity
fcNegInf ## value is negative infinity
proc classify*(x: float): TFloatClass =
## classifies a floating point value. Returns `x`'s class as specified by
## `TFloatClass`.

View File

@@ -164,8 +164,11 @@ proc resume*(p: PProcess) {.rtl, extern: "nosp$1", tags: [].}
## Resumes the process `p`.
proc terminate*(p: PProcess) {.rtl, extern: "nosp$1", tags: [].}
## Terminates the process `p`.
## Stop the process `p`. On Posix OSs the procedure sends SIGTERM to the process. On Windows the Win32 API function TerminateProcess() is called to stop the process.
proc kill*(p: PProcess) {.rtl, extern: "nosp$1", tags: [].}
## Kill the process `p`. On Posix OSs the procedure sends SIGKILL to the process. On Windows kill() is an alias for terminate().
proc running*(p: PProcess): bool {.rtl, extern: "nosp$1", tags: [].}
## Returns true iff the process `p` is still running. Returns immediately.
@@ -475,6 +478,9 @@ when defined(Windows) and not defined(useNimRtl):
if running(p):
discard terminateProcess(p.fProcessHandle, 0)
proc kill(p: PProcess) =
terminate(p)
proc waitForExit(p: PProcess, timeout: int = -1): int =
discard waitForSingleObject(p.fProcessHandle, timeout.int32)
@@ -815,10 +821,10 @@ elif not defined(useNimRtl):
discard close(p.errHandle)
proc suspend(p: PProcess) =
if kill(-p.id, SIGSTOP) != 0'i32: osError(osLastError())
if kill(p.id, SIGSTOP) != 0'i32: osError(osLastError())
proc resume(p: PProcess) =
if kill(-p.id, SIGCONT) != 0'i32: osError(osLastError())
if kill(p.id, SIGCONT) != 0'i32: osError(osLastError())
proc running(p: PProcess): bool =
var ret = waitpid(p.id, p.exitCode, WNOHANG)
@@ -826,11 +832,13 @@ elif not defined(useNimRtl):
result = ret == int(p.id)
proc terminate(p: PProcess) =
if kill(-p.id, SIGTERM) == 0'i32:
if p.running():
if kill(-p.id, SIGKILL) != 0'i32: osError(osLastError())
else: osError(osLastError())
if kill(p.id, SIGTERM) != 0'i32:
osError(osLastError())
proc kill(p: PProcess) =
if kill(p.id, SIGKILL) != 0'i32:
osError(osLastError())
proc waitForExit(p: PProcess, timeout: int = -1): int =
#if waitPid(p.id, p.exitCode, 0) == int(p.id):
# ``waitPid`` fails if the process is not running anymore. But then

View File

@@ -13,24 +13,30 @@
const someGcc = defined(gcc) or defined(llvm_gcc) or defined(clang)
when someGcc and hasThreadSupport:
type
AtomMemModel* = enum
ATOMIC_RELAXED, ## No barriers or synchronization.
ATOMIC_CONSUME, ## Data dependency only for both barrier and
## synchronization with another thread.
ATOMIC_ACQUIRE, ## Barrier to hoisting of code and synchronizes with
## release (or stronger)
## semantic stores from another thread.
ATOMIC_RELEASE, ## Barrier to sinking of code and synchronizes with
## acquire (or stronger)
## semantic loads from another thread.
ATOMIC_ACQ_REL, ## Full barrier in both directions and synchronizes
## with acquire loads
## and release stores in another thread.
ATOMIC_SEQ_CST ## Full barrier in both directions and synchronizes
## with acquire loads
## and release stores in all threads.
type AtomMemModel* = distinct cint
var ATOMIC_RELAXED* {.importc: "__ATOMIC_RELAXED", nodecl.}: AtomMemModel
## No barriers or synchronization.
var ATOMIC_CONSUME* {.importc: "__ATOMIC_CONSUME", nodecl.}: AtomMemModel
## Data dependency only for both barrier and
## synchronization with another thread.
var ATOMIC_ACQUIRE* {.importc: "__ATOMIC_ACQUIRE", nodecl.}: AtomMemModel
## Barrier to hoisting of code and synchronizes with
## release (or stronger)
## semantic stores from another thread.
var ATOMIC_RELEASE* {.importc: "__ATOMIC_RELEASE", nodecl.}: AtomMemModel
## Barrier to sinking of code and synchronizes with
## acquire (or stronger)
## semantic loads from another thread.
var ATOMIC_ACQ_REL* {.importc: "__ATOMIC_ACQ_REL", nodecl.}: AtomMemModel
## Full barrier in both directions and synchronizes
## with acquire loads
## and release stores in another thread.
var ATOMIC_SEQ_CST* {.importc: "__ATOMIC_SEQ_CST", nodecl.}: AtomMemModel
## Full barrier in both directions and synchronizes
## with acquire loads
## and release stores in all threads.
type
TAtomType* = TNumber|pointer|ptr|char
## Type Class representing valid types for use with atomic procs
@@ -166,14 +172,14 @@ else:
result = p[]
proc atomicInc*(memLoc: var int, x: int = 1): int =
when defined(gcc) and hasThreadSupport:
when someGcc and hasThreadSupport:
result = atomic_add_fetch(memLoc.addr, x, ATOMIC_RELAXED)
else:
inc(memLoc, x)
result = memLoc
proc atomicDec*(memLoc: var int, x: int = 1): int =
when defined(gcc) and hasThreadSupport:
when someGcc and hasThreadSupport:
when declared(atomic_sub_fetch):
result = atomic_sub_fetch(memLoc.addr, x, ATOMIC_RELAXED)
else:
@@ -196,7 +202,7 @@ else:
# XXX is this valid for 'int'?
when (defined(x86) or defined(amd64)) and (defined(gcc) or defined(llvm_gcc)):
when (defined(x86) or defined(amd64)) and someGcc:
proc cpuRelax {.inline.} =
{.emit: """asm volatile("pause" ::: "memory");""".}
elif (defined(x86) or defined(amd64)) and defined(vcc):

View File

@@ -226,15 +226,16 @@ proc recv*[TMsg](c: var TChannel[TMsg]): TMsg =
llRecv(q, addr(result), cast[PNimType](getTypeInfo(result)))
releaseSys(q.lock)
proc tryRecv*[TMsg](c: var TChannel[TMsg]): tuple[dataAvaliable: bool,
proc tryRecv*[TMsg](c: var TChannel[TMsg]): tuple[dataAvailable: bool,
msg: TMsg] =
## try to receives a message from the channel `c` if available. Otherwise
## it returns ``(false, default(msg))``.
var q = cast[PRawChannel](addr(c))
if q.mask != ChannelDeadMask:
lockChannel(q):
if q.mask != ChannelDeadMask:
if tryAcquireSys(q.lock):
llRecv(q, addr(result.msg), cast[PNimType](getTypeInfo(result.msg)))
result.dataAvaliable = true
result.dataAvailable = true
releaseSys(q.lock)
proc peek*[TMsg](c: var TChannel[TMsg]): int =
## returns the current number of messages in the channel `c`. Returns -1

View File

@@ -39,9 +39,7 @@ $ bin/nimrod c koch
$ ./koch boot -d:release
```
``koch install [dir]`` may then be used to install Nimrod, or you can simply
add it to your PATH. More ``koch`` related options are documented in
[doc/koch.txt](doc/koch.txt).
Add Nimrod to your PATH afterwards.
The above steps can be performed on Windows in a similar fashion, the
``build.bat`` and ``build64.bat`` (for x86_64 systems) are provided to be used
@@ -62,5 +60,5 @@ allowing you to create commercial applications.
Read copying.txt for more details.
Copyright (c) 2004-2014 Andreas Rumpf.
Copyright (c) 2006-2014 Andreas Rumpf.
All rights reserved.

25
tests/method/tmproto.nim Normal file
View File

@@ -0,0 +1,25 @@
type
Obj1 = ref object {.inheritable.}
Obj2 = ref object of Obj1
method beta(x: Obj1): int
proc delta(x: Obj2): int =
beta(x)
method beta(x: Obj2): int
proc alpha(x: Obj1): int =
beta(x)
method beta(x: Obj1): int = 1
method beta(x: Obj2): int = 2
proc gamma(x: Obj1): int =
beta(x)
doAssert alpha(Obj1()) == 1
doAssert gamma(Obj1()) == 1
doAssert alpha(Obj2()) == 2
doAssert gamma(Obj2()) == 2
doAssert delta(Obj2()) == 2

22
tests/method/trecmeth.nim Normal file
View File

@@ -0,0 +1,22 @@
# Note: We only compile this to verify that code generation
# for recursive methods works, no code is being executed
type
Obj = ref object of TObject
# Mutual recursion
method alpha(x: Obj)
method beta(x: Obj)
method alpha(x: Obj) =
beta(x)
method beta(x: Obj) =
alpha(x)
# Simple recursion
method gamma(x: Obj) =
gamma(x)

View File

@@ -4,6 +4,9 @@ discard """
import mexporta
# bug #1029:
from rawsockets import accept
# B.TMyObject has been imported implicitly here:
var x: TMyObject
echo($x, q(0), q"0")

View File

@@ -0,0 +1,9 @@
discard """
file: "tselfimport.nim"
line: 7
errormsg: "A module cannot import itself"
"""
import strutils as su # guard against regression
import tselfimport #ERROR
echo("Hello World")

View File

@@ -0,0 +1,21 @@
import os, osproc
when defined(Windows):
const ProgramWhichDoesNotEnd = "notepad"
else:
const ProgramWhichDoesNotEnd = "/bin/sh"
echo("starting " & ProgramWhichDoesNotEnd)
var process = startProcess(ProgramWhichDoesNotEnd)
sleep(500)
echo("stopping process")
process.terminate()
var TimeToWait = 5000
while process.running() and TimeToWait > 0:
sleep(100)
TimeToWait = TimeToWait - 100
if process.running():
echo("FAILED")
else:
echo("SUCCESS")

View File

@@ -149,11 +149,6 @@
CreateShortCut "$DESKTOP\?{c.displayName}.lnk" "$INSTDIR\?{c.name}.exe"
#end if
; Add shortcuts for the documentation
#for f in items(c.cat[fcDocStart]):
CreateShortCut "$SMPROGRAMS\$ICONS_GROUP\?{splitFile(f).name}.lnk" "$INSTDIR\?{f.toWin}"
#end for
; Write the shortcut to the uninstaller
CreateShortCut "$SMPROGRAMS\$ICONS_GROUP\Uninstall.lnk" "$INSTDIR\uninstaller.exe"
!insertmacro MUI_STARTMENU_WRITE_END
@@ -162,6 +157,7 @@
; Section for adding tools to the PATH variable
Section "Setup Path Environment" PathSection
${EnvVarUpdate} $R0 "PATH" "A" "HKCU" "$INSTDIR\dist\mingw"
${EnvVarUpdate} $R0 "PATH" "A" "HKCU" "$INSTDIR\dist\mingw\bin"
${EnvVarUpdate} $R0 "PATH" "A" "HKCU" "$INSTDIR\bin"
${EnvVarUpdate} $R0 "PATH" "A" "HKCU" "$INSTDIR\dist\babel"
SectionEnd
@@ -192,20 +188,26 @@
${If} $0 == "success"
ZipDLL::extractall "$TEMP\?zipName" "$INSTDIR\?dir"
Delete "$TEMP\?zipName"
${ElseIf} $0 == "cancel"
MessageBox MB_ICONQUESTION|MB_YESNO|MB_TOPMOST \
"Download of component '?sectionName' cancelled. Continue installation process??" \
IDYES ignore
abort
${Else}
MessageBox MB_ICONSTOP|MB_ABORTRETRYIGNORE "Error: $0" IDRETRY retry IDIGNORE ignore
MessageBox MB_ICONSTOP|MB_ABORTRETRYIGNORE|MB_TOPMOST "Error: $0" \
IDRETRY retry IDIGNORE ignore
abort
${EndIf}
; Shortcuts
# if d.len >= 6:
# let startMenuEntry = d[5]
# let e = splitFile(startMenuEntry).name.capitalize
CreateShortCut "$SMPROGRAMS\$ICONS_GROUP\?{e}.lnk" "$INSTDIR\?dir\?{startMenuEntry.toWin}"
# end if
; Shortcuts
!insertmacro MUI_STARTMENU_WRITE_BEGIN Application
CreateShortCut "$SMPROGRAMS\$ICONS_GROUP\?{e}.lnk" "$INSTDIR\?dir\?{startMenuEntry.toWin}"
!insertmacro MUI_STARTMENU_WRITE_END
# end if
ignore:
SectionEnd
#end
@@ -242,6 +244,7 @@
; Remove entries from the PATH environment variable
${un.EnvVarUpdate} $R0 "PATH" "R" "HKCU" "$INSTDIR\dist\mingw"
${un.EnvVarUpdate} $R0 "PATH" "R" "HKCU" "$INSTDIR\dist\mingw\bin"
${un.EnvVarUpdate} $R0 "PATH" "R" "HKCU" "$INSTDIR\bin"
${un.EnvVarUpdate} $R0 "PATH" "R" "HKCU" "$INSTDIR\dist\babel"
SectionEnd
@@ -252,4 +255,4 @@
Function .onInit
${GetRoot} "$EXEDIR" $R0
strCpy $INSTDIR "$R0\?{c.name}"
FunctionEnd
FunctionEnd

View File

@@ -45,9 +45,10 @@ proc initConfigData(c: var TConfigData) =
c.gitCommit = "master"
c.numProcessors = countProcessors()
# Attempts to obtain the git current commit.
let (output, code) = execCmdEx("git log -n 1 --format=%H")
if code == 0 and output.strip.len == 40:
c.gitCommit = output.strip
when false:
let (output, code) = execCmdEx("git log -n 1 --format=%H")
if code == 0 and output.strip.len == 40:
c.gitCommit = output.strip
c.quotations = initTable[string, tuple[quote, author: string]]()
include "website.tmpl"

View File

@@ -9,8 +9,10 @@ and clang on Mac OS X.
Binaries
========
Unfortunately for now we only provide 32 bit builds for
Windows: `nimrod_0.9.6.exe <download/nimrod_0.9.6.exe>`_
Unfortunately for now we only provide builds for Windows.
* 32 bit: `nimrod_0.9.6.exe <download/nimrod_0.9.6.exe>`_
* 64 bit: `nimrod_0.9.6_x64.exe <download/nimrod_0.9.6_x64.exe>`_
Installation based on generated C code

View File

@@ -5,7 +5,7 @@ News
2014-10-19 Nimrod version 0.9.6 released
========================================
**Note: 0.9.6 is the last release of Nimrod. The language has been renamed to
**Note: 0.9.6 is the last release of Nimrod. The language is being renamed to
Nim. Nim slightly breaks compatibility.**
This is a maintenance release. The upcoming 0.10.0 release has
@@ -37,13 +37,20 @@ Changes affecting backwards compatibility
will disappear soon!
Compiler improvements
---------------------
- Multi method dispatching performance has been improved by a factor of 10x for
pathological cases.
Language Additions
------------------
- This version introduces the new ``deprecated`` pragma statement that is used
- This version introduces the ``deprecated`` pragma statement that is used
to handle the upcoming massive amount of symbol renames.
- ``spawn`` can now wrap proc that have a return value. It then returns a flow
variable of the wrapped return type.
- ``spawn`` can now wrap proc that has a return value. It then returns a data
flow variable of the wrapped return type.
Library Additions

View File

@@ -26,9 +26,10 @@ file: ticker.txt
[Quotations]
# Page: quote - Person
# Bad things will happen if you use multiple dashes here.
index: """The most important thing in the programming language is the name.
A language will not succeed without a good name. I have recently invented a
very good name and now I am looking for a suitable language. - D. E. Knuth"""
index: """Is it so bad, then, to be misunderstood? Pythagoras was misunderstood,
and Socrates, and Jesus, and Luther, and Copernicus, and Galileo, and Newton,
and every pure and wise spirit that ever took flesh. To be great is to be
misunderstood. - Ralph Waldo Emerson"""
documentation: """Incorrect documentation is often worse than no documentation.
- Bertrand Meyer"""
download: """There are two major products that come out of Berkeley: LSD and