Merge branch 'devel' into alloc-overloads

This commit is contained in:
EXetoC 2014-03-06 02:46:31 +01:00
commit d1bc6cf098
160 changed files with 5218 additions and 1629 deletions

View file

@ -62,7 +62,7 @@ type
nkTripleStrLit, # a triple string literal """ nkTripleStrLit, # a triple string literal """
nkNilLit, # the nil literal nkNilLit, # the nil literal
# end of atoms # end of atoms
nkMetaNode, # difficult to explain; represents itself nkMetaNode_Obsolete, # difficult to explain; represents itself
# (used for macros) # (used for macros)
nkDotCall, # used to temporarily flag a nkCall node; nkDotCall, # used to temporarily flag a nkCall node;
# this is used # this is used
@ -409,7 +409,9 @@ type
# efficiency # efficiency
nfTransf, # node has been transformed nfTransf, # node has been transformed
nfSem # node has been checked for semantics nfSem # node has been checked for semantics
nfDelegate # the call can use a delegator nfDotField # the call can use a dot operator
nfDotSetter # the call can use a setter dot operarator
nfExplicitCall # x.y() was used instead of x.y
nfExprCall # this is an attempt to call a regular expression nfExprCall # this is an attempt to call a regular expression
nfIsRef # this node is a 'ref' node; used for the VM nfIsRef # this node is a 'ref' node; used for the VM
@ -479,12 +481,15 @@ type
skStub, # symbol is a stub and not yet loaded from the ROD skStub, # symbol is a stub and not yet loaded from the ROD
# file (it is loaded on demand, which may # file (it is loaded on demand, which may
# mean: never) # mean: never)
skPackage # symbol is a package (used for canonicalization)
TSymKinds* = set[TSymKind] TSymKinds* = set[TSymKind]
const const
routineKinds* = {skProc, skMethod, skIterator, skConverter, routineKinds* = {skProc, skMethod, skIterator, skConverter,
skMacro, skTemplate} skMacro, skTemplate}
tfIncompleteStruct* = tfVarargs tfIncompleteStruct* = tfVarargs
tfUncheckedArray* = tfVarargs
tfUnion* = tfNoSideEffect
skError* = skUnknown skError* = skUnknown
# type flags that are essential for type equality: # type flags that are essential for type equality:
@ -843,7 +848,8 @@ const
ExportableSymKinds* = {skVar, skConst, skProc, skMethod, skType, skIterator, ExportableSymKinds* = {skVar, skConst, skProc, skMethod, skType, skIterator,
skMacro, skTemplate, skConverter, skEnumField, skLet, skStub} skMacro, skTemplate, skConverter, skEnumField, skLet, skStub}
PersistentNodeFlags*: TNodeFlags = {nfBase2, nfBase8, nfBase16, PersistentNodeFlags*: TNodeFlags = {nfBase2, nfBase8, nfBase16,
nfAllConst, nfDelegate, nfIsRef} nfDotSetter, nfDotField,
nfAllConst,nfIsRef}
namePos* = 0 namePos* = 0
patternPos* = 1 # empty except for term rewriting macros patternPos* = 1 # empty except for term rewriting macros
genericParamsPos* = 2 genericParamsPos* = 2
@ -1044,6 +1050,10 @@ proc newStrNode(kind: TNodeKind, strVal: string): PNode =
result = newNode(kind) result = newNode(kind)
result.strVal = strVal result.strVal = strVal
proc withInfo*(n: PNode, info: TLineInfo): PNode =
n.info = info
return n
proc newIdentNode(ident: PIdent, info: TLineInfo): PNode = proc newIdentNode(ident: PIdent, info: TLineInfo): PNode =
result = newNode(nkIdent) result = newNode(nkIdent)
result.ident = ident result.ident = ident
@ -1105,10 +1115,6 @@ proc newNodeIT(kind: TNodeKind, info: TLineInfo, typ: PType): PNode =
result.info = info result.info = info
result.typ = typ result.typ = typ
proc newMetaNodeIT*(tree: PNode, info: TLineInfo, typ: PType): PNode =
result = newNodeIT(nkMetaNode, info, typ)
result.add(tree)
var emptyParams = newNode(nkFormalParams) var emptyParams = newNode(nkFormalParams)
emptyParams.addSon(emptyNode) emptyParams.addSon(emptyNode)

View file

@ -337,6 +337,9 @@ proc treeToYamlAux(n: PNode, marker: var TIntSet, indent: int,
appf(result, ",$N$1\"floatVal\": $2", appf(result, ",$N$1\"floatVal\": $2",
[istr, toRope(n.floatVal.toStrMaxPrecision)]) [istr, toRope(n.floatVal.toStrMaxPrecision)])
of nkStrLit..nkTripleStrLit: of nkStrLit..nkTripleStrLit:
if n.strVal.isNil:
appf(result, ",$N$1\"strVal\": null", [istr])
else:
appf(result, ",$N$1\"strVal\": $2", [istr, makeYamlString(n.strVal)]) appf(result, ",$N$1\"strVal\": $2", [istr, makeYamlString(n.strVal)])
of nkSym: of nkSym:
appf(result, ",$N$1\"sym\": $2", appf(result, ",$N$1\"sym\": $2",
@ -407,6 +410,9 @@ proc debugTree(n: PNode, indent: int, maxRecDepth: int): PRope =
appf(result, ",$N$1\"floatVal\": $2", appf(result, ",$N$1\"floatVal\": $2",
[istr, toRope(n.floatVal.toStrMaxPrecision)]) [istr, toRope(n.floatVal.toStrMaxPrecision)])
of nkStrLit..nkTripleStrLit: of nkStrLit..nkTripleStrLit:
if n.strVal.isNil:
appf(result, ",$N$1\"strVal\": null", [istr])
else:
appf(result, ",$N$1\"strVal\": $2", [istr, makeYamlString(n.strVal)]) appf(result, ",$N$1\"strVal\": $2", [istr, makeYamlString(n.strVal)])
of nkSym: of nkSym:
appf(result, ",$N$1\"sym\": $2_$3", appf(result, ",$N$1\"sym\": $2_$3",

View file

@ -540,7 +540,7 @@ proc typeAtom(p: var TParser): PNode =
if p.tok.s == "unsigned": if p.tok.s == "unsigned":
isUnsigned = true isUnsigned = true
elif p.tok.s == "signed" or p.tok.s == "int": elif p.tok.s == "signed" or p.tok.s == "int":
nil discard
else: else:
add(x, p.tok.s) add(x, p.tok.s)
getTok(p, nil) getTok(p, nil)
@ -681,11 +681,6 @@ proc parseField(p: var TParser, kind: TNodeKind): PNode =
else: result = mangledIdent(p.tok.s, p) else: result = mangledIdent(p.tok.s, p)
getTok(p, result) getTok(p, result)
proc takeOnlyFirstField(p: TParser, isUnion: bool): bool =
# if we generate an interface to a header file, *all* fields can be
# generated:
result = isUnion and p.options.header.len == 0
proc parseStructBody(p: var TParser, isUnion: bool, proc parseStructBody(p: var TParser, isUnion: bool,
kind: TNodeKind = nkRecList): PNode = kind: TNodeKind = nkRecList): PNode =
result = newNodeP(kind, p) result = newNodeP(kind, p)
@ -698,7 +693,6 @@ proc parseStructBody(p: var TParser, isUnion: bool,
var i = parseField(p, kind) var i = parseField(p, kind)
t = parseTypeSuffix(p, t) t = parseTypeSuffix(p, t)
addSon(def, i, t, ast.emptyNode) addSon(def, i, t, ast.emptyNode)
if not takeOnlyFirstField(p, isUnion) or sonsLen(result) < 1:
addSon(result, def) addSon(result, def)
if p.tok.xkind != pxComma: break if p.tok.xkind != pxComma: break
getTok(p, def) getTok(p, def)
@ -710,11 +704,12 @@ proc structPragmas(p: TParser, name: PNode, origName: string): PNode =
result = newNodeP(nkPragmaExpr, p) result = newNodeP(nkPragmaExpr, p)
addSon(result, exportSym(p, name, origName)) addSon(result, exportSym(p, name, origName))
var pragmas = newNodeP(nkPragma, p) var pragmas = newNodeP(nkPragma, p)
addSon(pragmas, newIdentNodeP("pure", p), newIdentNodeP("final", p)) #addSon(pragmas, newIdentNodeP("pure", p), newIdentNodeP("final", p))
if p.options.header.len > 0: if p.options.header.len > 0:
addSon(pragmas, newIdentStrLitPair("importc", origName, p), addSon(pragmas, newIdentStrLitPair("importc", origName, p),
newIdentStrLitPair("header", p.options.header, p)) newIdentStrLitPair("header", p.options.header, p))
addSon(result, pragmas) if pragmas.len > 0: addSon(result, pragmas)
else: addSon(result, ast.emptyNode)
proc enumPragmas(p: TParser, name: PNode): PNode = proc enumPragmas(p: TParser, name: PNode): PNode =
result = newNodeP(nkPragmaExpr, p) result = newNodeP(nkPragmaExpr, p)
@ -728,7 +723,11 @@ proc enumPragmas(p: TParser, name: PNode): PNode =
proc parseStruct(p: var TParser, isUnion: bool): PNode = proc parseStruct(p: var TParser, isUnion: bool): PNode =
result = newNodeP(nkObjectTy, p) result = newNodeP(nkObjectTy, p)
addSon(result, ast.emptyNode, ast.emptyNode) # no pragmas, no inheritance var pragmas = ast.emptyNode
if isUnion:
pragmas = newNodeP(nkPragma, p)
addSon(pragmas, newIdentNodeP("union", p))
addSon(result, pragmas, ast.emptyNode) # no inheritance
if p.tok.xkind == pxCurlyLe: if p.tok.xkind == pxCurlyLe:
addSon(result, parseStructBody(p, isUnion)) addSon(result, parseStructBody(p, isUnion))
else: else:
@ -746,7 +745,7 @@ proc directDeclarator(p: var TParser, a: PNode, ident: ptr PNode): PNode =
result = declarator(p, a, ident) result = declarator(p, a, ident)
eat(p, pxParRi, result) eat(p, pxParRi, result)
else: else:
nil discard
return parseTypeSuffix(p, a) return parseTypeSuffix(p, a)
proc declarator(p: var TParser, a: PNode, ident: ptr PNode): PNode = proc declarator(p: var TParser, a: PNode, ident: ptr PNode): PNode =
@ -1165,7 +1164,7 @@ proc enumSpecifier(p: var TParser): PNode =
proc setBaseFlags(n: PNode, base: TNumericalBase) = proc setBaseFlags(n: PNode, base: TNumericalBase) =
case base case base
of base10: nil of base10: discard
of base2: incl(n.flags, nfBase2) of base2: incl(n.flags, nfBase2)
of base8: incl(n.flags, nfBase8) of base8: incl(n.flags, nfBase8)
of base16: incl(n.flags, nfBase16) of base16: incl(n.flags, nfBase16)
@ -1686,7 +1685,7 @@ proc switchStatement(p: var TParser): PNode =
break break
of "case", "default": of "case", "default":
break break
else: nil else: discard
addSon(result, statement(p)) addSon(result, statement(p))
if sonsLen(result) == 0: if sonsLen(result) == 0:
# translate empty statement list to Nimrod's ``nil`` statement # translate empty statement list to Nimrod's ``nil`` statement

View file

@ -103,7 +103,7 @@ proc parseDefBody(p: var TParser, m: var TMacro, params: seq[string]) =
m.body.add(tok) m.body.add(tok)
of pxDirConc: of pxDirConc:
# just ignore this token: this implements token merging correctly # just ignore this token: this implements token merging correctly
nil discard
else: else:
m.body.add(p.tok) m.body.add(p.tok)
# we do not want macro expansion here: # we do not want macro expansion here:
@ -166,7 +166,7 @@ proc parseStmtList(p: var TParser): PNode =
of pxDirectiveParLe, pxDirective: of pxDirectiveParLe, pxDirective:
case p.tok.s case p.tok.s
of "else", "endif", "elif": break of "else", "endif", "elif": break
else: nil else: discard
addSon(result, statement(p)) addSon(result, statement(p))
proc eatEndif(p: var TParser) = proc eatEndif(p: var TParser) =

416
compiler/canonicalizer.nim Normal file
View file

@ -0,0 +1,416 @@
#
#
# The Nimrod Compiler
# (c) Copyright 2014 Andreas Rumpf
#
# See the file "copying.txt", included in this
# distribution, for details about the copyright.
#
## This module implements the canonalization for the various caching mechanisms.
import strutils, db_sqlite, md5
var db: TDbConn
# We *hash* the relevant information into 128 bit hashes. This should be good
# enough to prevent any collisions.
type
TUid = distinct MD5Digest
# For name mangling we encode these hashes via a variant of base64 (called
# 'base64a') and prepend the *primary* identifier to ease the debugging pain.
# So a signature like:
#
# proc gABI(c: PCtx; n: PNode; opc: TOpcode; a, b: TRegister; imm: BiggestInt)
#
# is mangled into:
# gABI_MTdmOWY5MTQ1MDcyNGQ3ZA
#
# This is a good compromise between correctness and brevity. ;-)
const
cb64 = [
"A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N",
"O", "P", "Q", "R", "S", "T" "U", "V", "W", "X", "Y", "Z",
"a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n",
"o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z",
"0", "1", "2", "3", "4", "5", "6", "7", "8", "9",
"_A", "_B"]
proc toBase64a(s: cstring, len: int): string =
## encodes `s` into base64 representation. After `lineLen` characters, a
## `newline` is added.
result = newStringOfCap(((len + 2) div 3) * 4)
var i = 0
while i < s.len - 2:
let a = ord(s[i])
let b = ord(s[i+1])
let c = ord(s[i+2])
result.add cb64[a shr 2]
result.add cb64[((a and 3) shl 4) or ((b and 0xF0) shr 4)]
result.add cb64[((b and 0x0F) shl 2) or ((c and 0xC0) shr 6)]
result.add cb64[c and 0x3F]
inc(i, 3)
if i < s.len-1:
let a = ord(s[i])
let b = ord(s[i+1])
result.add cb64[a shr 2]
result.add cb64[((a and 3) shl 4) or ((b and 0xF0) shr 4)]
result.add cb64[((b and 0x0F) shl 2)]
elif i < s.len:
let a = ord(s[i])
result.add cb64[a shr 2]
result.add cb64[(a and 3) shl 4]
proc toBase64a(u: TUid): string = toBase64a(cast[cstring](u), sizeof(u))
proc `&=`(c: var MD5Context, s: string) = md5Update(c, s, s.len)
proc hashSym(c: var MD5Context, s: PSym) =
if sfAnon in s.flags or s.kind == skGenericParam:
c &= ":anon"
else:
var it = s.owner
while it != nil:
hashSym(c, it)
c &= "."
it = s.owner
c &= s.name.s
proc hashTree(c: var MD5Context, n: PNode) =
if n == nil:
c &= "noTreeKind"
return
var k = n.kind
md5Update(c, cast[cstring](addr(k)), 1)
# we really must not hash line information. 'n.typ' is debatable but
# shouldn't be necessary for now and avoids potential infinite recursions.
case n.kind
of nkEmpty, nkNilLit, nkType: discard
of nkIdent:
c &= n.ident.s
of nkSym:
hashSym(c, n.sym)
of nkCharLit..nkUInt64Lit:
var v = n.intVal
md5Update(c, cast[cstring](addr(v)), sizeof(v))
of nkFloatLit..nkFloat64Lit:
var v = n.floatVal
md5Update(c, cast[cstring](addr(v)), sizeof(v))
of nkStrLit..nkTripleStrLit:
c &= n.strVal
else:
for i in 0.. <n.len: hashTree(c, n.sons[i])
proc hashType(c: var MD5Context, t: PType) =
# modelled after 'typeToString'
if t == nil:
c &= "noTypeKind"
return
var k = t.kind
md5Update(c, cast[cstring](addr(k)), 1)
if t.sym != nil and sfAnon notin t.sym.flags:
# t.n for literals, but not for e.g. objects!
if t.kind in {tyFloat, tyInt}: c.hashNode(t.n)
c.hashSym(t.sym)
case t.kind
of tyGenericBody, tyGenericInst, tyGenericInvokation:
for i in countup(0, sonsLen(t) -1 -ord(t.kind != tyGenericInvokation)):
c.hashType t.sons[i]
of tyUserTypeClass:
internalAssert t.sym != nil and t.sym.owner != nil
c &= t.sym.owner.name.s
of tyUserTypeClassInst:
let body = t.base
c.hashSym body.sym
for i in countup(1, sonsLen(t) - 2):
c.hashType t.sons[i]
of tyFromExpr, tyFieldAccessor:
c.hashTree(t.n)
of tyArrayConstr:
c.hashTree(t.sons[0].n)
c.hashType(t.sons[1])
of tyTuple:
if t.n != nil:
assert(sonsLen(t.n) == sonsLen(t))
for i in countup(0, sonsLen(t.n) - 1):
assert(t.n.sons[i].kind == nkSym)
c &= t.n.sons[i].sym.name.s
c &= ":"
c.hashType(t.sons[i])
c &= ","
else:
for i in countup(0, sonsLen(t) - 1): c.hashType t.sons[i]
of tyRange:
c.hashTree(t.n)
c.hashType(t.sons[0])
of tyProc:
c &= (if tfIterator in t.flags: "iterator " else: "proc ")
for i in 0.. <t.len: c.hashType(t.sons[i])
md5Update(c, cast[cstring](addr(t.callConv)), 1)
if tfNoSideEffect in t.flags: c &= ".noSideEffect"
if tfThread in t.flags: c &= ".thread"
else:
for i in 0.. <t.len: c.hashType(t.sons[i])
if tfShared in t.flags: c &= "shared"
if tfNotNil in t.flags: c &= "not nil"
proc canonConst(n: PNode): TUid =
var c: MD5Context
md5Init(c)
c.hashTree(n)
c.hashType(n.typ)
md5Final(c, MD5Digest(result))
proc canonSym(s: PSym): TUid
var c: MD5Context
md5Init(c)
c.hashSym(s)
md5Final(c, MD5Digest(result))
proc pushType(w: PRodWriter, t: PType) =
# check so that the stack does not grow too large:
if iiTableGet(w.index.tab, t.id) == InvalidKey:
w.tstack.add(t)
proc pushSym(w: PRodWriter, s: PSym) =
# check so that the stack does not grow too large:
if iiTableGet(w.index.tab, s.id) == InvalidKey:
w.sstack.add(s)
proc encodeNode(w: PRodWriter, fInfo: TLineInfo, n: PNode,
result: var string) =
if n == nil:
# nil nodes have to be stored too:
result.add("()")
return
result.add('(')
encodeVInt(ord(n.kind), result)
# we do not write comments for now
# Line information takes easily 20% or more of the filesize! Therefore we
# omit line information if it is the same as the father's line information:
if fInfo.fileIndex != n.info.fileIndex:
result.add('?')
encodeVInt(n.info.col, result)
result.add(',')
encodeVInt(n.info.line, result)
result.add(',')
encodeVInt(fileIdx(w, toFilename(n.info)), result)
elif fInfo.line != n.info.line:
result.add('?')
encodeVInt(n.info.col, result)
result.add(',')
encodeVInt(n.info.line, result)
elif fInfo.col != n.info.col:
result.add('?')
encodeVInt(n.info.col, result)
var f = n.flags * PersistentNodeFlags
if f != {}:
result.add('$')
encodeVInt(cast[int32](f), result)
if n.typ != nil:
result.add('^')
encodeVInt(n.typ.id, result)
pushType(w, n.typ)
case n.kind
of nkCharLit..nkInt64Lit:
if n.intVal != 0:
result.add('!')
encodeVBiggestInt(n.intVal, result)
of nkFloatLit..nkFloat64Lit:
if n.floatVal != 0.0:
result.add('!')
encodeStr($n.floatVal, result)
of nkStrLit..nkTripleStrLit:
if n.strVal != "":
result.add('!')
encodeStr(n.strVal, result)
of nkIdent:
result.add('!')
encodeStr(n.ident.s, result)
of nkSym:
result.add('!')
encodeVInt(n.sym.id, result)
pushSym(w, n.sym)
else:
for i in countup(0, sonsLen(n) - 1):
encodeNode(w, n.info, n.sons[i], result)
add(result, ')')
proc encodeLoc(w: PRodWriter, loc: TLoc, result: var string) =
var oldLen = result.len
result.add('<')
if loc.k != low(loc.k): encodeVInt(ord(loc.k), result)
if loc.s != low(loc.s):
add(result, '*')
encodeVInt(ord(loc.s), result)
if loc.flags != {}:
add(result, '$')
encodeVInt(cast[int32](loc.flags), result)
if loc.t != nil:
add(result, '^')
encodeVInt(cast[int32](loc.t.id), result)
pushType(w, loc.t)
if loc.r != nil:
add(result, '!')
encodeStr(ropeToStr(loc.r), result)
if loc.a != 0:
add(result, '?')
encodeVInt(loc.a, result)
if oldLen + 1 == result.len:
# no data was necessary, so remove the '<' again:
setLen(result, oldLen)
else:
add(result, '>')
proc encodeType(w: PRodWriter, t: PType, result: var string) =
if t == nil:
# nil nodes have to be stored too:
result.add("[]")
return
# we need no surrounding [] here because the type is in a line of its own
if t.kind == tyForward: internalError("encodeType: tyForward")
# for the new rodfile viewer we use a preceeding [ so that the data section
# can easily be disambiguated:
add(result, '[')
encodeVInt(ord(t.kind), result)
add(result, '+')
encodeVInt(t.id, result)
if t.n != nil:
encodeNode(w, unknownLineInfo(), t.n, result)
if t.flags != {}:
add(result, '$')
encodeVInt(cast[int32](t.flags), result)
if t.callConv != low(t.callConv):
add(result, '?')
encodeVInt(ord(t.callConv), result)
if t.owner != nil:
add(result, '*')
encodeVInt(t.owner.id, result)
pushSym(w, t.owner)
if t.sym != nil:
add(result, '&')
encodeVInt(t.sym.id, result)
pushSym(w, t.sym)
if t.size != - 1:
add(result, '/')
encodeVBiggestInt(t.size, result)
if t.align != 2:
add(result, '=')
encodeVInt(t.align, result)
encodeLoc(w, t.loc, result)
for i in countup(0, sonsLen(t) - 1):
if t.sons[i] == nil:
add(result, "^()")
else:
add(result, '^')
encodeVInt(t.sons[i].id, result)
pushType(w, t.sons[i])
proc encodeLib(w: PRodWriter, lib: PLib, info: TLineInfo, result: var string) =
add(result, '|')
encodeVInt(ord(lib.kind), result)
add(result, '|')
encodeStr(ropeToStr(lib.name), result)
add(result, '|')
encodeNode(w, info, lib.path, result)
proc encodeSym(w: PRodWriter, s: PSym, result: var string) =
if s == nil:
# nil nodes have to be stored too:
result.add("{}")
return
# we need no surrounding {} here because the symbol is in a line of its own
encodeVInt(ord(s.kind), result)
result.add('+')
encodeVInt(s.id, result)
result.add('&')
encodeStr(s.name.s, result)
if s.typ != nil:
result.add('^')
encodeVInt(s.typ.id, result)
pushType(w, s.typ)
result.add('?')
if s.info.col != -1'i16: encodeVInt(s.info.col, result)
result.add(',')
if s.info.line != -1'i16: encodeVInt(s.info.line, result)
result.add(',')
encodeVInt(fileIdx(w, toFilename(s.info)), result)
if s.owner != nil:
result.add('*')
encodeVInt(s.owner.id, result)
pushSym(w, s.owner)
if s.flags != {}:
result.add('$')
encodeVInt(cast[int32](s.flags), result)
if s.magic != mNone:
result.add('@')
encodeVInt(ord(s.magic), result)
if s.options != w.options:
result.add('!')
encodeVInt(cast[int32](s.options), result)
if s.position != 0:
result.add('%')
encodeVInt(s.position, result)
if s.offset != - 1:
result.add('`')
encodeVInt(s.offset, result)
encodeLoc(w, s.loc, result)
if s.annex != nil: encodeLib(w, s.annex, s.info, result)
if s.constraint != nil:
add(result, '#')
encodeNode(w, unknownLineInfo(), s.constraint, result)
# lazy loading will soon reload the ast lazily, so the ast needs to be
# the last entry of a symbol:
if s.ast != nil:
# we used to attempt to save space here by only storing a dummy AST if
# it is not necessary, but Nimrod's heavy compile-time evaluation features
# make that unfeasible nowadays:
encodeNode(w, s.info, s.ast, result)
proc createDb() =
db.exec(sql"""
create table if not exists Module(
id integer primary key,
name varchar(256) not null,
fullpath varchar(256) not null,
interfHash varchar(256) not null,
fullHash varchar(256) not null,
created timestamp not null default (DATETIME('now')),
);""")
db.exec(sql"""
create table if not exists Symbol(
id integer primary key,
module integer not null,
name varchar(max) not null,
data varchar(max) not null,
created timestamp not null default (DATETIME('now')),
foreign key (module) references module(id)
);""")
db.exec(sql"""
create table if not exists Type(
id integer primary key,
module integer not null,
name varchar(max) not null,
data varchar(max) not null,
created timestamp not null default (DATETIME('now')),
foreign key (module) references module(id)
);""")
#db.exec(sql"""
# --create unique index if not exists TsstNameIx on TestResult(name);
# """, [])

View file

@ -593,7 +593,7 @@ proc unaryArith(p: BProc, e: PNode, d: var TLoc, op: TMagic) =
proc genDeref(p: BProc, e: PNode, d: var TLoc) = proc genDeref(p: BProc, e: PNode, d: var TLoc) =
var a: TLoc var a: TLoc
if mapType(e.sons[0].typ) == ctArray: if mapType(e.sons[0].typ) in {ctArray, ctPtrToArray}:
# XXX the amount of hacks for C's arrays is incredible, maybe we should # XXX the amount of hacks for C's arrays is incredible, maybe we should
# simply wrap them in a struct? --> Losing auto vectorization then? # simply wrap them in a struct? --> Losing auto vectorization then?
expr(p, e.sons[0], d) expr(p, e.sons[0], d)
@ -736,7 +736,7 @@ proc genArrayElem(p: BProc, e: PNode, d: var TLoc) =
var ty = skipTypes(skipTypes(a.t, abstractVarRange), abstractPtrs) var ty = skipTypes(skipTypes(a.t, abstractVarRange), abstractPtrs)
var first = intLiteral(firstOrd(ty)) var first = intLiteral(firstOrd(ty))
# emit range check: # emit range check:
if (optBoundsCheck in p.options): if optBoundsCheck in p.options and tfUncheckedArray notin ty.flags:
if not isConstExpr(e.sons[1]): if not isConstExpr(e.sons[1]):
# semantic pass has already checked for const index expressions # semantic pass has already checked for const index expressions
if firstOrd(ty) == 0: if firstOrd(ty) == 0:
@ -1049,7 +1049,7 @@ proc genObjConstr(p: BProc, e: PNode, d: var TLoc) =
app(tmp2.r, field.loc.r) app(tmp2.r, field.loc.r)
tmp2.k = locTemp tmp2.k = locTemp
tmp2.t = field.loc.t tmp2.t = field.loc.t
tmp2.s = OnHeap tmp2.s = if isRef: OnHeap else: OnStack
tmp2.heapRoot = tmp.r tmp2.heapRoot = tmp.r
expr(p, it.sons[1], tmp2) expr(p, it.sons[1], tmp2)
if d.k == locNone: if d.k == locNone:
@ -1376,7 +1376,7 @@ proc genSetOp(p: BProc, e: PNode, d: var TLoc, op: TMagic) =
getTemp(p, getSysType(tyInt), i) # our counter getTemp(p, getSysType(tyInt), i) # our counter
initLocExpr(p, e.sons[1], a) initLocExpr(p, e.sons[1], a)
initLocExpr(p, e.sons[2], b) initLocExpr(p, e.sons[2], b)
if d.k == locNone: getTemp(p, a.t, d) if d.k == locNone: getTemp(p, getSysType(tyBool), d)
lineF(p, cpsStmts, lookupOpr[op], lineF(p, cpsStmts, lookupOpr[op],
[rdLoc(i), toRope(size), rdLoc(d), rdLoc(a), rdLoc(b)]) [rdLoc(i), toRope(size), rdLoc(d), rdLoc(a), rdLoc(b)])
of mEqSet: of mEqSet:
@ -1915,7 +1915,6 @@ proc expr(p: BProc, n: PNode, d: var TLoc) =
internalError(n.info, "expr: proc not init " & sym.name.s) internalError(n.info, "expr: proc not init " & sym.name.s)
putLocIntoDest(p, d, sym.loc) putLocIntoDest(p, d, sym.loc)
of nkClosure: genClosure(p, n, d) of nkClosure: genClosure(p, n, d)
of nkMetaNode: expr(p, n.sons[0], d)
of nkEmpty: discard of nkEmpty: discard
of nkWhileStmt: genWhileStmt(p, n) of nkWhileStmt: genWhileStmt(p, n)

View file

@ -145,33 +145,6 @@ proc atEndMark(buf: cstring, pos: int): bool =
while s < NimMergeEndMark.len and buf[pos+s] == NimMergeEndMark[s]: inc s while s < NimMergeEndMark.len and buf[pos+s] == NimMergeEndMark[s]: inc s
result = s == NimMergeEndMark.len result = s == NimMergeEndMark.len
when false:
proc readVerbatimSection(L: var TBaseLexer): PRope =
var pos = L.bufpos
var buf = L.buf
result = newMutableRope(30_000)
while true:
case buf[pos]
of CR:
pos = nimlexbase.HandleCR(L, pos)
buf = L.buf
result.data.add(tnl)
of LF:
pos = nimlexbase.HandleLF(L, pos)
buf = L.buf
result.data.add(tnl)
of '\0':
InternalError("ccgmerge: expected: " & NimMergeEndMark)
break
else:
if atEndMark(buf, pos):
inc pos, NimMergeEndMark.len
break
result.data.add(buf[pos])
inc pos
L.bufpos = pos
freezeMutableRope(result)
proc readVerbatimSection(L: var TBaseLexer): PRope = proc readVerbatimSection(L: var TBaseLexer): PRope =
var pos = L.bufpos var pos = L.bufpos
var buf = L.buf var buf = L.buf

View file

@ -65,6 +65,7 @@ proc startBlock(p: BProc, start: TFormatStr = "{$n",
setLen(p.blocks, result + 1) setLen(p.blocks, result + 1)
p.blocks[result].id = p.labels p.blocks[result].id = p.labels
p.blocks[result].nestedTryStmts = p.nestedTryStmts.len.int16 p.blocks[result].nestedTryStmts = p.nestedTryStmts.len.int16
p.blocks[result].nestedExceptStmts = p.inExceptBlock.int16
proc assignLabel(b: var TBlock): PRope {.inline.} = proc assignLabel(b: var TBlock): PRope {.inline.} =
b.label = con("LA", b.id.toRope) b.label = con("LA", b.id.toRope)
@ -260,37 +261,57 @@ proc genIf(p: BProc, n: PNode, d: var TLoc) =
else: internalError(n.info, "genIf()") else: internalError(n.info, "genIf()")
if sonsLen(n) > 1: fixLabel(p, lend) if sonsLen(n) > 1: fixLabel(p, lend)
proc blockLeaveActions(p: BProc, howMany: int) =
var L = p.nestedTryStmts.len proc blockLeaveActions(p: BProc, howManyTrys, howManyExcepts: int) =
# danger of endless recursion! we workaround this here by a temp stack # Called by return and break stmts.
# Deals with issues faced when jumping out of try/except/finally stmts,
var stack: seq[PNode] var stack: seq[PNode]
newSeq(stack, howMany) newSeq(stack, 0)
for i in countup(1, howMany):
stack[i-1] = p.nestedTryStmts[L-i]
setLen(p.nestedTryStmts, L-howMany)
var alreadyPoppedCnt = p.inExceptBlock var alreadyPoppedCnt = p.inExceptBlock
for tryStmt in items(stack): for i in countup(1, howManyTrys):
if gCmd != cmdCompileToCpp: if gCmd != cmdCompileToCpp:
# Pop safe points generated by try
if alreadyPoppedCnt > 0: if alreadyPoppedCnt > 0:
dec alreadyPoppedCnt dec alreadyPoppedCnt
else: else:
linefmt(p, cpsStmts, "#popSafePoint();$n") linefmt(p, cpsStmts, "#popSafePoint();$n")
# Pop this try-stmt of the list of nested trys
# so we don't infinite recurse on it in the next step.
var tryStmt = p.nestedTryStmts.pop
stack.add(tryStmt)
# Find finally-stmt for this try-stmt
# and generate a copy of its sons
var finallyStmt = lastSon(tryStmt) var finallyStmt = lastSon(tryStmt)
if finallyStmt.kind == nkFinally: if finallyStmt.kind == nkFinally:
genStmts(p, finallyStmt.sons[0]) genStmts(p, finallyStmt.sons[0])
# push old elements again: # push old elements again:
for i in countdown(howMany-1, 0): for i in countdown(howManyTrys-1, 0):
p.nestedTryStmts.add(stack[i]) p.nestedTryStmts.add(stack[i])
if gCmd != cmdCompileToCpp: if gCmd != cmdCompileToCpp:
for i in countdown(p.inExceptBlock-1, 0): # Pop exceptions that was handled by the
# except-blocks we are in
for i in countdown(howManyExcepts-1, 0):
linefmt(p, cpsStmts, "#popCurrentException();$n") linefmt(p, cpsStmts, "#popCurrentException();$n")
proc genReturnStmt(p: BProc, t: PNode) = proc genReturnStmt(p: BProc, t: PNode) =
p.beforeRetNeeded = true p.beforeRetNeeded = true
genLineDir(p, t) genLineDir(p, t)
if (t.sons[0].kind != nkEmpty): genStmts(p, t.sons[0]) if (t.sons[0].kind != nkEmpty): genStmts(p, t.sons[0])
blockLeaveActions(p, min(1, p.nestedTryStmts.len)) blockLeaveActions(p,
howManyTrys = p.nestedTryStmts.len,
howManyExcepts = p.inExceptBlock)
if (p.finallySafePoints.len > 0):
# If we're in a finally block, and we came here by exception
# consume it before we return.
var safePoint = p.finallySafePoints[p.finallySafePoints.len-1]
linefmt(p, cpsStmts, "if ($1.status != 0) #popCurrentException();$n", safePoint)
lineFF(p, cpsStmts, "goto BeforeRet;$n", "br label %BeforeRet$n", []) lineFF(p, cpsStmts, "goto BeforeRet;$n", "br label %BeforeRet$n", [])
proc genComputedGoto(p: BProc; n: PNode) = proc genComputedGoto(p: BProc; n: PNode) =
@ -450,7 +471,9 @@ proc genBreakStmt(p: BProc, t: PNode) =
if idx < 0 or not p.blocks[idx].isLoop: if idx < 0 or not p.blocks[idx].isLoop:
internalError(t.info, "no loop to break") internalError(t.info, "no loop to break")
let label = assignLabel(p.blocks[idx]) let label = assignLabel(p.blocks[idx])
blockLeaveActions(p, p.nestedTryStmts.len - p.blocks[idx].nestedTryStmts) blockLeaveActions(p,
p.nestedTryStmts.len - p.blocks[idx].nestedTryStmts,
p.inExceptBlock - p.blocks[idx].nestedExceptStmts)
genLineDir(p, t) genLineDir(p, t)
lineF(p, cpsStmts, "goto $1;$n", [label]) lineF(p, cpsStmts, "goto $1;$n", [label])
@ -827,7 +850,9 @@ proc genTry(p: BProc, t: PNode, d: var TLoc) =
discard pop(p.nestedTryStmts) discard pop(p.nestedTryStmts)
endBlock(p) # end of else block endBlock(p) # end of else block
if i < length and t.sons[i].kind == nkFinally: if i < length and t.sons[i].kind == nkFinally:
p.finallySafePoints.add(safePoint)
exprBlock(p, t.sons[i].sons[0], d) exprBlock(p, t.sons[i].sons[0], d)
discard pop(p.finallySafePoints)
linefmt(p, cpsStmts, "if ($1.status != 0) #reraiseException();$n", safePoint) linefmt(p, cpsStmts, "if ($1.status != 0) #reraiseException();$n", safePoint)
proc genAsmOrEmitStmt(p: BProc, t: PNode, isAsmStmt=false): PRope = proc genAsmOrEmitStmt(p: BProc, t: PNode, isAsmStmt=false): PRope =

View file

@ -185,7 +185,7 @@ proc mapType(typ: PType): TCTypeKind =
of tyPtr, tyVar, tyRef: of tyPtr, tyVar, tyRef:
var base = skipTypes(typ.sons[0], typedescInst) var base = skipTypes(typ.sons[0], typedescInst)
case base.kind case base.kind
of tyOpenArray, tyArrayConstr, tyArray, tyVarargs: result = ctArray of tyOpenArray, tyArrayConstr, tyArray, tyVarargs: result = ctPtrToArray
else: result = ctPtr else: result = ctPtr
of tyPointer: result = ctPtr of tyPointer: result = ctPtr
of tySequence: result = ctNimSeq of tySequence: result = ctNimSeq
@ -377,9 +377,12 @@ proc getTypePre(m: BModule, typ: PType): PRope =
result = getSimpleTypeDesc(m, typ) result = getSimpleTypeDesc(m, typ)
if result == nil: result = cacheGetType(m.typeCache, typ) if result == nil: result = cacheGetType(m.typeCache, typ)
proc structOrUnion(t: PType): PRope =
(if tfUnion in t.flags: toRope("union") else: toRope("struct"))
proc getForwardStructFormat(): string = proc getForwardStructFormat(): string =
if gCmd == cmdCompileToCpp: result = "struct $1;$n" if gCmd == cmdCompileToCpp: result = "$1 $2;$n"
else: result = "typedef struct $1 $1;$n" else: result = "typedef $1 $2 $2;$n"
proc getTypeForward(m: BModule, typ: PType): PRope = proc getTypeForward(m: BModule, typ: PType): PRope =
result = cacheGetType(m.forwTypeCache, typ) result = cacheGetType(m.forwTypeCache, typ)
@ -390,7 +393,8 @@ proc getTypeForward(m: BModule, typ: PType): PRope =
of tySequence, tyTuple, tyObject: of tySequence, tyTuple, tyObject:
result = getTypeName(typ) result = getTypeName(typ)
if not isImportedType(typ): if not isImportedType(typ):
appf(m.s[cfsForwardTypes], getForwardStructFormat(), [result]) appf(m.s[cfsForwardTypes], getForwardStructFormat(),
[structOrUnion(typ), result])
idTablePut(m.forwTypeCache, typ, result) idTablePut(m.forwTypeCache, typ, result)
else: internalError("getTypeForward(" & $typ.kind & ')') else: internalError("getTypeForward(" & $typ.kind & ')')
@ -445,7 +449,12 @@ proc genRecordFieldsAux(m: BModule, n: PNode,
if accessExpr != nil: ae = ropef("$1.$2", [accessExpr, sname]) if accessExpr != nil: ae = ropef("$1.$2", [accessExpr, sname])
else: ae = sname else: ae = sname
fillLoc(field.loc, locField, field.typ, ae, OnUnknown) fillLoc(field.loc, locField, field.typ, ae, OnUnknown)
appf(result, "$1 $2;$n", [getTypeDescAux(m, field.loc.t, check), sname]) let fieldType = field.loc.t
if fieldType.kind == tyArray and tfUncheckedArray in fieldType.flags:
appf(result, "$1 $2[SEQ_DECL_SIZE];$n",
[getTypeDescAux(m, fieldType.elemType, check), sname])
else:
appf(result, "$1 $2;$n", [getTypeDescAux(m, fieldType, check), sname])
else: internalError(n.info, "genRecordFieldsAux()") else: internalError(n.info, "genRecordFieldsAux()")
proc getRecordFields(m: BModule, typ: PType, check: var TIntSet): PRope = proc getRecordFields(m: BModule, typ: PType, check: var TIntSet): PRope =
@ -455,23 +464,24 @@ proc getRecordDesc(m: BModule, typ: PType, name: PRope,
check: var TIntSet): PRope = check: var TIntSet): PRope =
# declare the record: # declare the record:
var hasField = false var hasField = false
let aStruct = structOrUnion(typ)
if typ.kind == tyObject: if typ.kind == tyObject:
if typ.sons[0] == nil: if typ.sons[0] == nil:
if (typ.sym != nil and sfPure in typ.sym.flags) or tfFinal in typ.flags: if (typ.sym != nil and sfPure in typ.sym.flags) or tfFinal in typ.flags:
result = ropecg(m, "struct $1 {$n", [name]) result = ropecg(m, "$1 $2 {$n", [aStruct, name])
else: else:
result = ropecg(m, "struct $1 {$n#TNimType* m_type;$n", [name]) result = ropecg(m, "$1 $2 {$n#TNimType* m_type;$n", [aStruct, name])
hasField = true hasField = true
elif gCmd == cmdCompileToCpp: elif gCmd == cmdCompileToCpp:
result = ropecg(m, "struct $1 : public $2 {$n", result = ropecg(m, "$1 $2 : public $3 {$n",
[name, getTypeDescAux(m, typ.sons[0], check)]) [aStruct, name, getTypeDescAux(m, typ.sons[0], check)])
hasField = true hasField = true
else: else:
result = ropecg(m, "struct $1 {$n $2 Sup;$n", result = ropecg(m, "$1 $2 {$n $3 Sup;$n",
[name, getTypeDescAux(m, typ.sons[0], check)]) [aStruct, name, getTypeDescAux(m, typ.sons[0], check)])
hasField = true hasField = true
else: else:
result = ropef("struct $1 {$n", [name]) result = ropef("$1 $2 {$n", [aStruct, name])
var desc = getRecordFields(m, typ, check) var desc = getRecordFields(m, typ, check)
if (desc == nil) and not hasField: if (desc == nil) and not hasField:
appf(result, "char dummy;$n", []) appf(result, "char dummy;$n", [])
@ -481,7 +491,7 @@ proc getRecordDesc(m: BModule, typ: PType, name: PRope,
proc getTupleDesc(m: BModule, typ: PType, name: PRope, proc getTupleDesc(m: BModule, typ: PType, name: PRope,
check: var TIntSet): PRope = check: var TIntSet): PRope =
result = ropef("struct $1 {$n", [name]) result = ropef("$1 $2 {$n", [structOrUnion(typ), name])
var desc: PRope = nil var desc: PRope = nil
for i in countup(0, sonsLen(typ) - 1): for i in countup(0, sonsLen(typ) - 1):
appf(desc, "$1 Field$2;$n", appf(desc, "$1 Field$2;$n",
@ -557,7 +567,8 @@ proc getTypeDescAux(m: BModule, typ: PType, check: var TIntSet): PRope =
if result == nil: if result == nil:
result = getTypeName(t) result = getTypeName(t)
if not isImportedType(t): if not isImportedType(t):
appf(m.s[cfsForwardTypes], getForwardStructFormat(), [result]) appf(m.s[cfsForwardTypes], getForwardStructFormat(),
[structOrUnion(t), result])
idTablePut(m.forwTypeCache, t, result) idTablePut(m.forwTypeCache, t, result)
assert(cacheGetType(m.typeCache, t) == nil) assert(cacheGetType(m.typeCache, t) == nil)
idTablePut(m.typeCache, t, con(result, "*")) idTablePut(m.typeCache, t, con(result, "*"))
@ -588,7 +599,8 @@ proc getTypeDescAux(m: BModule, typ: PType, check: var TIntSet): PRope =
if result == nil: if result == nil:
result = getTypeName(t) result = getTypeName(t)
if not isImportedType(t): if not isImportedType(t):
appf(m.s[cfsForwardTypes], getForwardStructFormat(), [result]) appf(m.s[cfsForwardTypes], getForwardStructFormat(),
[structOrUnion(t), result])
idTablePut(m.forwTypeCache, t, result) idTablePut(m.forwTypeCache, t, result)
idTablePut(m.typeCache, t, result) # always call for sideeffects: idTablePut(m.typeCache, t, result) # always call for sideeffects:
if t.kind != tyTuple: recdesc = getRecordDesc(m, t, result, check) if t.kind != tyTuple: recdesc = getRecordDesc(m, t, result, check)

View file

@ -130,7 +130,6 @@ proc getUniqueType*(key: PType): PType =
idTablePut(gTypeTable[k], key, key) idTablePut(gTypeTable[k], key, key)
result = key result = key
of tyProc: of tyProc:
# tyVar is not 100% correct, but would speeds things up a little:
if key.callConv != ccClosure: if key.callConv != ccClosure:
result = key result = key
else: else:

View file

@ -761,6 +761,8 @@ proc genProcAux(m: BModule, prc: PSym) =
var returnStmt: PRope = nil var returnStmt: PRope = nil
assert(prc.ast != nil) assert(prc.ast != nil)
if sfPure notin prc.flags and prc.typ.sons[0] != nil: if sfPure notin prc.flags and prc.typ.sons[0] != nil:
if resultPos >= prc.ast.len:
internalError(prc.info, "proc has no result symbol")
var res = prc.ast.sons[resultPos].sym # get result symbol var res = prc.ast.sons[resultPos].sym # get result symbol
if not isInvalidReturnType(prc.typ.sons[0]): if not isInvalidReturnType(prc.typ.sons[0]):
if sfNoInit in prc.flags: incl(res.flags, sfNoInit) if sfNoInit in prc.flags: incl(res.flags, sfNoInit)
@ -962,8 +964,8 @@ proc genMainProc(m: BModule) =
NimMainBody = NimMainBody =
"N_CDECL(void, NimMain)(void) {$N" & "N_CDECL(void, NimMain)(void) {$N" &
"\tPreMain();$N" & "\tPreMain();$N" &
"$1$N" & "$1" &
"}$N" "}$N$N"
PosixNimMain = PosixNimMain =
"int cmdCount;$N" & "int cmdCount;$N" &
@ -977,20 +979,20 @@ proc genMainProc(m: BModule) =
"\tcmdCount = argc;$N" & "\tcmdCount = argc;$N" &
"\tgEnv = env;$N" & "\tgEnv = env;$N" &
MainProcsWithResult & MainProcsWithResult &
"}$N" "}$N$N"
StandaloneCMain = StandaloneCMain =
"int main(void) {$N" & "int main(void) {$N" &
MainProcs & MainProcs &
"\treturn 0;$N" & "\treturn 0;$N" &
"}$N" "}$N$N"
WinNimMain = NimMainBody WinNimMain = NimMainBody
WinCMain = "N_STDCALL(int, WinMain)(HINSTANCE hCurInstance, $N" & WinCMain = "N_STDCALL(int, WinMain)(HINSTANCE hCurInstance, $N" &
" HINSTANCE hPrevInstance, $N" & " HINSTANCE hPrevInstance, $N" &
" LPSTR lpCmdLine, int nCmdShow) {$N" & " LPSTR lpCmdLine, int nCmdShow) {$N" &
MainProcsWithResult & "}$N" MainProcsWithResult & "}$N$N"
WinNimDllMain = "N_LIB_EXPORT " & NimMainBody WinNimDllMain = "N_LIB_EXPORT " & NimMainBody
@ -998,14 +1000,14 @@ proc genMainProc(m: BModule) =
"BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fwdreason, $N" & "BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fwdreason, $N" &
" LPVOID lpvReserved) {$N" & " LPVOID lpvReserved) {$N" &
"\tif(fwdreason == DLL_PROCESS_ATTACH) {$N" & MainProcs & "}$N" & "\tif(fwdreason == DLL_PROCESS_ATTACH) {$N" & MainProcs & "}$N" &
"\treturn 1;$N}$N" "\treturn 1;$N}$N$N"
PosixNimDllMain = WinNimDllMain PosixNimDllMain = WinNimDllMain
PosixCDllMain = PosixCDllMain =
"void NIM_POSIX_INIT NimMainInit(void) {$N" & "void NIM_POSIX_INIT NimMainInit(void) {$N" &
MainProcs & MainProcs &
"}$N" "}$N$N"
var nimMain, otherMain: TFormatStr var nimMain, otherMain: TFormatStr
if platform.targetOS == osWindows and if platform.targetOS == osWindows and
@ -1034,7 +1036,7 @@ proc genMainProc(m: BModule) =
platform.targetOS == osStandalone: "".toRope platform.targetOS == osStandalone: "".toRope
else: ropecg(m, "\t#initStackBottom();$N") else: ropecg(m, "\t#initStackBottom();$N")
inc(m.labels) inc(m.labels)
appcg(m, m.s[cfsProcs], "void PreMain() {$N" & PreMainBody & "}$N", [ appcg(m, m.s[cfsProcs], "void PreMain() {$N" & PreMainBody & "}$N$N", [
mainDatInit, initStackBottomCall, gBreakpoints, otherModsInit]) mainDatInit, initStackBottomCall, gBreakpoints, otherModsInit])
appcg(m, m.s[cfsProcs], nimMain, [mainModInit, toRope(m.labels)]) appcg(m, m.s[cfsProcs], nimMain, [mainModInit, toRope(m.labels)])
@ -1042,8 +1044,10 @@ proc genMainProc(m: BModule) =
appcg(m, m.s[cfsProcs], otherMain, []) appcg(m, m.s[cfsProcs], otherMain, [])
proc getSomeInitName(m: PSym, suffix: string): PRope = proc getSomeInitName(m: PSym, suffix: string): PRope =
assert m.kind == skModule
assert m.owner.kind == skPackage
if {sfSystemModule, sfMainModule} * m.flags == {}: if {sfSystemModule, sfMainModule} * m.flags == {}:
result = m.info.toFullPath.getPackageName.mangle.toRope result = m.owner.name.s.mangle.toRope
result.app m.name.s result.app m.name.s
result.app suffix result.app suffix

View file

@ -41,7 +41,8 @@ type
ctInt, ctInt8, ctInt16, ctInt32, ctInt64, ctInt, ctInt8, ctInt16, ctInt32, ctInt64,
ctFloat, ctFloat32, ctFloat64, ctFloat128, ctFloat, ctFloat32, ctFloat64, ctFloat128,
ctUInt, ctUInt8, ctUInt16, ctUInt32, ctUInt64, ctUInt, ctUInt8, ctUInt16, ctUInt32, ctUInt64,
ctArray, ctStruct, ctPtr, ctNimStr, ctNimSeq, ctProc, ctCString ctArray, ctPtrToArray, ctStruct, ctPtr, ctNimStr, ctNimSeq, ctProc,
ctCString
TCFileSections* = array[TCFileSection, PRope] # represents a generated C file TCFileSections* = array[TCFileSection, PRope] # represents a generated C file
TCProcSection* = enum # the sections a generated C proc consists of TCProcSection* = enum # the sections a generated C proc consists of
cpsLocals, # section of local variables for C proc cpsLocals, # section of local variables for C proc
@ -57,6 +58,7 @@ type
sections*: TCProcSections # the code beloging sections*: TCProcSections # the code beloging
isLoop*: bool # whether block is a loop isLoop*: bool # whether block is a loop
nestedTryStmts*: int16 # how many try statements is it nested into nestedTryStmts*: int16 # how many try statements is it nested into
nestedExceptStmts*: int16 # how many except statements is it nested into
frameLen*: int16 frameLen*: int16
TCProc{.final.} = object # represents C proc that is currently generated TCProc{.final.} = object # represents C proc that is currently generated
@ -68,6 +70,8 @@ type
inExceptBlock*: int # are we currently inside an except block? inExceptBlock*: int # are we currently inside an except block?
# leaving such scopes by raise or by return must # leaving such scopes by raise or by return must
# execute any applicable finally blocks # execute any applicable finally blocks
finallySafePoints*: seq[PRope] # For correctly cleaning up exceptions when
# using return in finally statements
labels*: Natural # for generating unique labels in the C proc labels*: Natural # for generating unique labels in the C proc
blocks*: seq[TBlock] # nested blocks blocks*: seq[TBlock] # nested blocks
breakIdx*: int # the block that will be exited breakIdx*: int # the block that will be exited
@ -140,6 +144,7 @@ proc newProc*(prc: PSym, module: BModule): BProc =
else: result.options = gOptions else: result.options = gOptions
newSeq(result.blocks, 1) newSeq(result.blocks, 1)
result.nestedTryStmts = @[] result.nestedTryStmts = @[]
result.finallySafePoints = @[]
iterator cgenModules*: var BModule = iterator cgenModules*: var BModule =
for i in 0..high(gModules): for i in 0..high(gModules):

View file

@ -47,6 +47,7 @@ proc initDefines*() =
defineSymbol("nimeffects") defineSymbol("nimeffects")
defineSymbol("nimbabel") defineSymbol("nimbabel")
defineSymbol("nimcomputedgoto") defineSymbol("nimcomputedgoto")
defineSymbol("nimunion")
# add platform specific symbols: # add platform specific symbols:
case targetCPU case targetCPU

View file

@ -42,17 +42,23 @@ proc compilerMsgHandler(filename: string, line, col: int,
of mwUnsupportedLanguage: k = warnLanguageXNotSupported of mwUnsupportedLanguage: k = warnLanguageXNotSupported
globalError(newLineInfo(filename, line, col), k, arg) globalError(newLineInfo(filename, line, col), k, arg)
proc docgenFindFile(s: string): string {.procvar.} =
result = options.findFile(s)
if result.len == 0:
result = getCurrentDir() / s
if not existsFile(result): result = ""
proc parseRst(text, filename: string, proc parseRst(text, filename: string,
line, column: int, hasToc: var bool, line, column: int, hasToc: var bool,
rstOptions: TRstParseOptions): PRstNode = rstOptions: TRstParseOptions): PRstNode =
result = rstParse(text, filename, line, column, hasToc, rstOptions, result = rstParse(text, filename, line, column, hasToc, rstOptions,
options.findFile, compilerMsgHandler) docgenFindFile, compilerMsgHandler)
proc newDocumentor*(filename: string, config: PStringTable): PDoc = proc newDocumentor*(filename: string, config: PStringTable): PDoc =
new(result) new(result)
initRstGenerator(result[], (if gCmd != cmdRst2tex: outHtml else: outLatex), initRstGenerator(result[], (if gCmd != cmdRst2tex: outHtml else: outLatex),
options.gConfigVars, filename, {roSupportRawDirective}, options.gConfigVars, filename, {roSupportRawDirective},
options.findFile, compilerMsgHandler) docgenFindFile, compilerMsgHandler)
result.id = 100 result.id = 100
proc dispA(dest: var PRope, xml, tex: string, args: openArray[PRope]) = proc dispA(dest: var PRope, xml, tex: string, args: openArray[PRope]) =

View file

@ -9,7 +9,7 @@
## This module implements the 'implies' relation for guards. ## This module implements the 'implies' relation for guards.
import ast, astalgo, msgs, magicsys, nimsets, trees, types, renderer import ast, astalgo, msgs, magicsys, nimsets, trees, types, renderer, idents
const const
someEq = {mEqI, mEqI64, mEqF64, mEqEnum, mEqCh, mEqB, mEqRef, mEqProc, someEq = {mEqI, mEqI64, mEqF64, mEqEnum, mEqCh, mEqB, mEqRef, mEqProc,
@ -69,9 +69,23 @@ proc isLetLocation(m: PNode, isApprox: bool): bool =
proc interestingCaseExpr*(m: PNode): bool = isLetLocation(m, true) proc interestingCaseExpr*(m: PNode): bool = isLetLocation(m, true)
proc swapArgs(fact: PNode, newOp: string, m: TMagic): PNode = proc getMagicOp(name: string, m: TMagic): PSym =
result = newSym(skProc, getIdent(name), nil, unknownLineInfo())
result.magic = m
let
opLe = getMagicOp("<=", mLeI)
opLt = getMagicOp("<", mLtI)
opAnd = getMagicOp("and", mAnd)
opOr = getMagicOp("or", mOr)
opNot = getMagicOp("not", mNot)
opIsNil = getMagicOp("isnil", mIsNil)
opContains = getMagicOp("contains", mInSet)
opEq = getMagicOp("==", mEqI)
proc swapArgs(fact: PNode, newOp: PSym): PNode =
result = newNodeI(nkCall, fact.info, 3) result = newNodeI(nkCall, fact.info, 3)
result.sons[0] = newSymNode(getSysMagic(newOp, m)) result.sons[0] = newSymNode(newOp)
result.sons[1] = fact.sons[2] result.sons[1] = fact.sons[2]
result.sons[2] = fact.sons[1] result.sons[2] = fact.sons[1]
@ -82,9 +96,9 @@ proc neg(n: PNode): PNode =
result = n.sons[1] result = n.sons[1]
of someLt: of someLt:
# not (a < b) == a >= b == b <= a # not (a < b) == a >= b == b <= a
result = swapArgs(n, "<=", mLeI) result = swapArgs(n, opLe)
of someLe: of someLe:
result = swapArgs(n, "<", mLtI) result = swapArgs(n, opLt)
of mInSet: of mInSet:
if n.sons[1].kind != nkCurly: return nil if n.sons[1].kind != nkCurly: return nil
let t = n.sons[2].typ.skipTypes(abstractInst) let t = n.sons[2].typ.skipTypes(abstractInst)
@ -110,7 +124,7 @@ proc neg(n: PNode): PNode =
b = n.sons[2].neg b = n.sons[2].neg
if a != nil and b != nil: if a != nil and b != nil:
result = newNodeI(nkCall, n.info, 3) result = newNodeI(nkCall, n.info, 3)
result.sons[0] = newSymNode(getSysMagic("and", mAnd)) result.sons[0] = newSymNode(opAnd)
result.sons[1] = a result.sons[1] = a
result.sons[2] = b result.sons[2] = b
elif a != nil: elif a != nil:
@ -120,12 +134,12 @@ proc neg(n: PNode): PNode =
else: else:
# leave not (a == 4) as it is # leave not (a == 4) as it is
result = newNodeI(nkCall, n.info, 2) result = newNodeI(nkCall, n.info, 2)
result.sons[0] = newSymNode(getSysMagic("not", mNot)) result.sons[0] = newSymNode(opNot)
result.sons[1] = n result.sons[1] = n
proc buildIsNil(arg: PNode): PNode = proc buildIsNil(arg: PNode): PNode =
result = newNodeI(nkCall, arg.info, 2) result = newNodeI(nkCall, arg.info, 2)
result.sons[0] = newSymNode(getSysMagic("isNil", mIsNil)) result.sons[0] = newSymNode(opIsNil)
result.sons[1] = arg result.sons[1] = arg
proc usefulFact(n: PNode): PNode = proc usefulFact(n: PNode): PNode =
@ -154,7 +168,7 @@ proc usefulFact(n: PNode): PNode =
b = usefulFact(n.sons[2]) b = usefulFact(n.sons[2])
if a != nil and b != nil: if a != nil and b != nil:
result = newNodeI(nkCall, n.info, 3) result = newNodeI(nkCall, n.info, 3)
result.sons[0] = newSymNode(getSysMagic("and", mAnd)) result.sons[0] = newSymNode(opAnd)
result.sons[1] = a result.sons[1] = a
result.sons[2] = b result.sons[2] = b
elif a != nil: elif a != nil:
@ -177,7 +191,7 @@ proc usefulFact(n: PNode): PNode =
b = usefulFact(n.sons[2]).neg b = usefulFact(n.sons[2]).neg
if a != nil and b != nil: if a != nil and b != nil:
result = newNodeI(nkCall, n.info, 3) result = newNodeI(nkCall, n.info, 3)
result.sons[0] = newSymNode(getSysMagic("and", mAnd)) result.sons[0] = newSymNode(opAnd)
result.sons[1] = a result.sons[1] = a
result.sons[2] = b result.sons[2] = b
result = result.neg result = result.neg
@ -520,7 +534,7 @@ proc buildOf(it, loc: PNode): PNode =
s.typ = settype(loc) s.typ = settype(loc)
for i in 0..it.len-2: s.sons[i] = it.sons[i] for i in 0..it.len-2: s.sons[i] = it.sons[i]
result = newNodeI(nkCall, it.info, 3) result = newNodeI(nkCall, it.info, 3)
result.sons[0] = newSymNode(getSysMagic("contains", mInSet)) result.sons[0] = newSymNode(opContains)
result.sons[1] = s result.sons[1] = s
result.sons[2] = loc result.sons[2] = loc
@ -532,20 +546,20 @@ proc buildElse(n: PNode): PNode =
for j in 0..branch.len-2: for j in 0..branch.len-2:
s.add(branch.sons[j]) s.add(branch.sons[j])
result = newNodeI(nkCall, n.info, 3) result = newNodeI(nkCall, n.info, 3)
result.sons[0] = newSymNode(getSysMagic("contains", mInSet)) result.sons[0] = newSymNode(opContains)
result.sons[1] = s result.sons[1] = s
result.sons[2] = n.sons[0] result.sons[2] = n.sons[0]
proc addDiscriminantFact*(m: var TModel, n: PNode) = proc addDiscriminantFact*(m: var TModel, n: PNode) =
var fact = newNodeI(nkCall, n.info, 3) var fact = newNodeI(nkCall, n.info, 3)
fact.sons[0] = newSymNode(getSysMagic("==", mEqI)) fact.sons[0] = newSymNode(opEq)
fact.sons[1] = n.sons[0] fact.sons[1] = n.sons[0]
fact.sons[2] = n.sons[1] fact.sons[2] = n.sons[1]
m.add fact m.add fact
proc addAsgnFact*(m: var TModel, key, value: PNode) = proc addAsgnFact*(m: var TModel, key, value: PNode) =
var fact = newNodeI(nkCall, key.info, 3) var fact = newNodeI(nkCall, key.info, 3)
fact.sons[0] = newSymNode(getSysMagic("==", mEqI)) fact.sons[0] = newSymNode(opEq)
fact.sons[1] = key fact.sons[1] = key
fact.sons[2] = value fact.sons[2] = value
m.add fact m.add fact

View file

@ -275,11 +275,11 @@ const # magic checked op; magic unchecked op; checked op; unchecked op
["nimMax", "nimMax", "nimMax($1, $2)", "nimMax($1, $2)"], # MaxI64 ["nimMax", "nimMax", "nimMax($1, $2)", "nimMax($1, $2)"], # MaxI64
["nimMin", "nimMin", "nimMin($1, $2)", "nimMin($1, $2)"], # MinF64 ["nimMin", "nimMin", "nimMin($1, $2)", "nimMin($1, $2)"], # MinF64
["nimMax", "nimMax", "nimMax($1, $2)", "nimMax($1, $2)"], # MaxF64 ["nimMax", "nimMax", "nimMax($1, $2)", "nimMax($1, $2)"], # MaxF64
["AddU", "AddU", "AddU($1, $2)", "AddU($1, $2)"], # AddU ["addU", "addU", "addU($1, $2)", "addU($1, $2)"], # addU
["SubU", "SubU", "SubU($1, $2)", "SubU($1, $2)"], # SubU ["subU", "subU", "subU($1, $2)", "subU($1, $2)"], # subU
["MulU", "MulU", "MulU($1, $2)", "MulU($1, $2)"], # MulU ["mulU", "mulU", "mulU($1, $2)", "mulU($1, $2)"], # mulU
["DivU", "DivU", "DivU($1, $2)", "DivU($1, $2)"], # DivU ["divU", "divU", "divU($1, $2)", "divU($1, $2)"], # divU
["ModU", "ModU", "ModU($1, $2)", "ModU($1, $2)"], # ModU ["modU", "modU", "modU($1, $2)", "modU($1, $2)"], # modU
["", "", "($1 == $2)", "($1 == $2)"], # EqI ["", "", "($1 == $2)", "($1 == $2)"], # EqI
["", "", "($1 <= $2)", "($1 <= $2)"], # LeI ["", "", "($1 <= $2)", "($1 <= $2)"], # LeI
["", "", "($1 < $2)", "($1 < $2)"], # LtI ["", "", "($1 < $2)", "($1 < $2)"], # LtI
@ -289,10 +289,10 @@ const # magic checked op; magic unchecked op; checked op; unchecked op
["", "", "($1 == $2)", "($1 == $2)"], # EqF64 ["", "", "($1 == $2)", "($1 == $2)"], # EqF64
["", "", "($1 <= $2)", "($1 <= $2)"], # LeF64 ["", "", "($1 <= $2)", "($1 <= $2)"], # LeF64
["", "", "($1 < $2)", "($1 < $2)"], # LtF64 ["", "", "($1 < $2)", "($1 < $2)"], # LtF64
["LeU", "LeU", "LeU($1, $2)", "LeU($1, $2)"], # LeU ["leU", "leU", "leU($1, $2)", "leU($1, $2)"], # leU
["LtU", "LtU", "LtU($1, $2)", "LtU($1, $2)"], # LtU ["ltU", "ltU", "ltU($1, $2)", "ltU($1, $2)"], # ltU
["LeU64", "LeU64", "LeU64($1, $2)", "LeU64($1, $2)"], # LeU64 ["leU64", "leU64", "leU64($1, $2)", "leU64($1, $2)"], # leU64
["LtU64", "LtU64", "LtU64($1, $2)", "LtU64($1, $2)"], # LtU64 ["ltU64", "ltU64", "ltU64($1, $2)", "ltU64($1, $2)"], # ltU64
["", "", "($1 == $2)", "($1 == $2)"], # EqEnum ["", "", "($1 == $2)", "($1 == $2)"], # EqEnum
["", "", "($1 <= $2)", "($1 <= $2)"], # LeEnum ["", "", "($1 <= $2)", "($1 <= $2)"], # LeEnum
["", "", "($1 < $2)", "($1 < $2)"], # LtEnum ["", "", "($1 < $2)", "($1 < $2)"], # LtEnum
@ -309,10 +309,10 @@ const # magic checked op; magic unchecked op; checked op; unchecked op
["", "", "($1 == $2)", "($1 == $2)"], # EqCString ["", "", "($1 == $2)", "($1 == $2)"], # EqCString
["", "", "($1 != $2)", "($1 != $2)"], # Xor ["", "", "($1 != $2)", "($1 != $2)"], # Xor
["", "", "($1 == $2)", "($1 == $2)"], # EqProc ["", "", "($1 == $2)", "($1 == $2)"], # EqProc
["NegInt", "", "NegInt($1)", "-($1)"], # UnaryMinusI ["negInt", "", "negInt($1)", "-($1)"], # UnaryMinusI
["NegInt64", "", "NegInt64($1)", "-($1)"], # UnaryMinusI64 ["negInt64", "", "negInt64($1)", "-($1)"], # UnaryMinusI64
["AbsInt", "", "AbsInt($1)", "Math.abs($1)"], # AbsI ["absInt", "", "absInt($1)", "Math.abs($1)"], # AbsI
["AbsInt64", "", "AbsInt64($1)", "Math.abs($1)"], # AbsI64 ["absInt64", "", "absInt64($1)", "Math.abs($1)"], # AbsI64
["", "", "!($1)", "!($1)"], # Not ["", "", "!($1)", "!($1)"], # Not
["", "", "+($1)", "+($1)"], # UnaryPlusI ["", "", "+($1)", "+($1)"], # UnaryPlusI
["", "", "~($1)", "~($1)"], # BitnotI ["", "", "~($1)", "~($1)"], # BitnotI
@ -327,9 +327,9 @@ const # magic checked op; magic unchecked op; checked op; unchecked op
["Ze16ToI64", "Ze16ToI64", "Ze16ToI64($1)", "Ze16ToI64($1)"], # mZe16ToI64 ["Ze16ToI64", "Ze16ToI64", "Ze16ToI64($1)", "Ze16ToI64($1)"], # mZe16ToI64
["Ze32ToI64", "Ze32ToI64", "Ze32ToI64($1)", "Ze32ToI64($1)"], # mZe32ToI64 ["Ze32ToI64", "Ze32ToI64", "Ze32ToI64($1)", "Ze32ToI64($1)"], # mZe32ToI64
["ZeIToI64", "ZeIToI64", "ZeIToI64($1)", "ZeIToI64($1)"], # mZeIToI64 ["ZeIToI64", "ZeIToI64", "ZeIToI64($1)", "ZeIToI64($1)"], # mZeIToI64
["ToU8", "ToU8", "ToU8($1)", "ToU8($1)"], # ToU8 ["toU8", "toU8", "toU8($1)", "toU8($1)"], # toU8
["ToU16", "ToU16", "ToU16($1)", "ToU16($1)"], # ToU16 ["toU16", "toU16", "toU16($1)", "toU16($1)"], # toU16
["ToU32", "ToU32", "ToU32($1)", "ToU32($1)"], # ToU32 ["toU32", "toU32", "toU32($1)", "toU32($1)"], # toU32
["", "", "$1", "$1"], # ToFloat ["", "", "$1", "$1"], # ToFloat
["", "", "$1", "$1"], # ToBiggestFloat ["", "", "$1", "$1"], # ToBiggestFloat
["", "", "Math.floor($1)", "Math.floor($1)"], # ToInt ["", "", "Math.floor($1)", "Math.floor($1)"], # ToInt
@ -375,11 +375,11 @@ const # magic checked op; magic unchecked op; checked op; unchecked op
["nimMax", "nimMax", "nimMax($1, $2)", "nimMax($1, $2)"], # MaxI64 ["nimMax", "nimMax", "nimMax($1, $2)", "nimMax($1, $2)"], # MaxI64
["nimMin", "nimMin", "nimMin($1, $2)", "nimMin($1, $2)"], # MinF64 ["nimMin", "nimMin", "nimMin($1, $2)", "nimMin($1, $2)"], # MinF64
["nimMax", "nimMax", "nimMax($1, $2)", "nimMax($1, $2)"], # MaxF64 ["nimMax", "nimMax", "nimMax($1, $2)", "nimMax($1, $2)"], # MaxF64
["AddU", "AddU", "AddU($1, $2)", "AddU($1, $2)"], # AddU ["addU", "addU", "addU($1, $2)", "addU($1, $2)"], # addU
["SubU", "SubU", "SubU($1, $2)", "SubU($1, $2)"], # SubU ["subU", "subU", "subU($1, $2)", "subU($1, $2)"], # subU
["MulU", "MulU", "MulU($1, $2)", "MulU($1, $2)"], # MulU ["mulU", "mulU", "mulU($1, $2)", "mulU($1, $2)"], # mulU
["DivU", "DivU", "DivU($1, $2)", "DivU($1, $2)"], # DivU ["divU", "divU", "divU($1, $2)", "divU($1, $2)"], # divU
["ModU", "ModU", "ModU($1, $2)", "ModU($1, $2)"], # ModU ["modU", "modU", "modU($1, $2)", "modU($1, $2)"], # modU
["", "", "($1 == $2)", "($1 == $2)"], # EqI ["", "", "($1 == $2)", "($1 == $2)"], # EqI
["", "", "($1 <= $2)", "($1 <= $2)"], # LeI ["", "", "($1 <= $2)", "($1 <= $2)"], # LeI
["", "", "($1 < $2)", "($1 < $2)"], # LtI ["", "", "($1 < $2)", "($1 < $2)"], # LtI
@ -389,10 +389,10 @@ const # magic checked op; magic unchecked op; checked op; unchecked op
["", "", "($1 == $2)", "($1 == $2)"], # EqF64 ["", "", "($1 == $2)", "($1 == $2)"], # EqF64
["", "", "($1 <= $2)", "($1 <= $2)"], # LeF64 ["", "", "($1 <= $2)", "($1 <= $2)"], # LeF64
["", "", "($1 < $2)", "($1 < $2)"], # LtF64 ["", "", "($1 < $2)", "($1 < $2)"], # LtF64
["LeU", "LeU", "LeU($1, $2)", "LeU($1, $2)"], # LeU ["leU", "leU", "leU($1, $2)", "leU($1, $2)"], # leU
["LtU", "LtU", "LtU($1, $2)", "LtU($1, $2)"], # LtU ["ltU", "ltU", "ltU($1, $2)", "ltU($1, $2)"], # ltU
["LeU64", "LeU64", "LeU64($1, $2)", "LeU64($1, $2)"], # LeU64 ["leU64", "leU64", "leU64($1, $2)", "leU64($1, $2)"], # leU64
["LtU64", "LtU64", "LtU64($1, $2)", "LtU64($1, $2)"], # LtU64 ["ltU64", "ltU64", "ltU64($1, $2)", "ltU64($1, $2)"], # ltU64
["", "", "($1 == $2)", "($1 == $2)"], # EqEnum ["", "", "($1 == $2)", "($1 == $2)"], # EqEnum
["", "", "($1 <= $2)", "($1 <= $2)"], # LeEnum ["", "", "($1 <= $2)", "($1 <= $2)"], # LeEnum
["", "", "($1 < $2)", "($1 < $2)"], # LtEnum ["", "", "($1 < $2)", "($1 < $2)"], # LtEnum
@ -409,10 +409,10 @@ const # magic checked op; magic unchecked op; checked op; unchecked op
["", "", "($1 == $2)", "($1 == $2)"], # EqCString ["", "", "($1 == $2)", "($1 == $2)"], # EqCString
["", "", "($1 != $2)", "($1 != $2)"], # Xor ["", "", "($1 != $2)", "($1 != $2)"], # Xor
["", "", "($1 == $2)", "($1 == $2)"], # EqProc ["", "", "($1 == $2)", "($1 == $2)"], # EqProc
["NegInt", "", "NegInt($1)", "-($1)"], # UnaryMinusI ["negInt", "", "negInt($1)", "-($1)"], # UnaryMinusI
["NegInt64", "", "NegInt64($1)", "-($1)"], # UnaryMinusI64 ["negInt64", "", "negInt64($1)", "-($1)"], # UnaryMinusI64
["AbsInt", "", "AbsInt($1)", "Math.abs($1)"], # AbsI ["absInt", "", "absInt($1)", "Math.abs($1)"], # AbsI
["AbsInt64", "", "AbsInt64($1)", "Math.abs($1)"], # AbsI64 ["absInt64", "", "absInt64($1)", "Math.abs($1)"], # AbsI64
["", "", "not ($1)", "not ($1)"], # Not ["", "", "not ($1)", "not ($1)"], # Not
["", "", "+($1)", "+($1)"], # UnaryPlusI ["", "", "+($1)", "+($1)"], # UnaryPlusI
["", "", "~($1)", "~($1)"], # BitnotI ["", "", "~($1)", "~($1)"], # BitnotI
@ -427,9 +427,9 @@ const # magic checked op; magic unchecked op; checked op; unchecked op
["Ze16ToI64", "Ze16ToI64", "Ze16ToI64($1)", "Ze16ToI64($1)"], # mZe16ToI64 ["Ze16ToI64", "Ze16ToI64", "Ze16ToI64($1)", "Ze16ToI64($1)"], # mZe16ToI64
["Ze32ToI64", "Ze32ToI64", "Ze32ToI64($1)", "Ze32ToI64($1)"], # mZe32ToI64 ["Ze32ToI64", "Ze32ToI64", "Ze32ToI64($1)", "Ze32ToI64($1)"], # mZe32ToI64
["ZeIToI64", "ZeIToI64", "ZeIToI64($1)", "ZeIToI64($1)"], # mZeIToI64 ["ZeIToI64", "ZeIToI64", "ZeIToI64($1)", "ZeIToI64($1)"], # mZeIToI64
["ToU8", "ToU8", "ToU8($1)", "ToU8($1)"], # ToU8 ["toU8", "toU8", "toU8($1)", "toU8($1)"], # toU8
["ToU16", "ToU16", "ToU16($1)", "ToU16($1)"], # ToU16 ["toU16", "toU16", "toU16($1)", "toU16($1)"], # toU16
["ToU32", "ToU32", "ToU32($1)", "ToU32($1)"], # ToU32 ["toU32", "toU32", "toU32($1)", "toU32($1)"], # toU32
["", "", "$1", "$1"], # ToFloat ["", "", "$1", "$1"], # ToFloat
["", "", "$1", "$1"], # ToBiggestFloat ["", "", "$1", "$1"], # ToBiggestFloat
["", "", "Math.floor($1)", "Math.floor($1)"], # ToInt ["", "", "Math.floor($1)", "Math.floor($1)"], # ToInt
@ -812,8 +812,8 @@ proc genAsgnAux(p: PProc, x, y: PNode, noCopyNeeded: bool) =
if needsNoCopy(y) or noCopyNeeded: if needsNoCopy(y) or noCopyNeeded:
appf(p.body, "$1 = $2;$n", [a.rdLoc, b.rdLoc]) appf(p.body, "$1 = $2;$n", [a.rdLoc, b.rdLoc])
else: else:
useMagic(p, "NimCopy") useMagic(p, "nimCopy")
appf(p.body, "$1 = NimCopy($2, $3);$n", appf(p.body, "$1 = nimCopy($2, $3);$n",
[a.res, b.res, genTypeInfo(p, y.typ)]) [a.res, b.res, genTypeInfo(p, y.typ)])
of etyBaseIndex: of etyBaseIndex:
if a.typ != etyBaseIndex or b.typ != etyBaseIndex: if a.typ != etyBaseIndex or b.typ != etyBaseIndex:
@ -1113,8 +1113,8 @@ proc createVar(p: PProc, typ: PType, indirect: bool): PRope =
var length = int(lengthOrd(t)) var length = int(lengthOrd(t))
var e = elemType(t) var e = elemType(t)
if length > 32: if length > 32:
useMagic(p, "ArrayConstr") useMagic(p, "arrayConstr")
result = ropef("ArrayConstr($1, $2, $3)", [toRope(length), result = ropef("arrayConstr($1, $2, $3)", [toRope(length),
createVar(p, e, false), genTypeInfo(p, e)]) createVar(p, e, false), genTypeInfo(p, e)])
else: else:
result = toRope("[") result = toRope("[")
@ -1171,8 +1171,8 @@ proc genVarInit(p: PProc, v: PSym, n: PNode) =
if needsNoCopy(n): if needsNoCopy(n):
s = a.res s = a.res
else: else:
useMagic(p, "NimCopy") useMagic(p, "nimCopy")
s = ropef("NimCopy($1, $2)", [a.res, genTypeInfo(p, n.typ)]) s = ropef("nimCopy($1, $2)", [a.res, genTypeInfo(p, n.typ)])
of etyBaseIndex: of etyBaseIndex:
if (a.typ != etyBaseIndex): internalError(n.info, "genVarInit") if (a.typ != etyBaseIndex): internalError(n.info, "genVarInit")
if {sfAddrTaken, sfGlobal} * v.flags != {}: if {sfAddrTaken, sfGlobal} * v.flags != {}:
@ -1600,7 +1600,6 @@ proc gen(p: PProc, n: PNode, r: var TCompRes) =
if lfNoDecl in s.loc.flags or s.magic != mNone: discard if lfNoDecl in s.loc.flags or s.magic != mNone: discard
elif not p.g.generatedSyms.containsOrIncl(s.id): elif not p.g.generatedSyms.containsOrIncl(s.id):
app(p.locals, genProc(p, s)) app(p.locals, genProc(p, s))
of nkMetaNode: gen(p, n.sons[0], r)
of nkType: r.res = genTypeInfo(p, n.typ) of nkType: r.res = genTypeInfo(p, n.typ)
of nkStmtList, nkStmtListExpr: of nkStmtList, nkStmtListExpr:
# this shows the distinction is nice for backends and should be kept # this shows the distinction is nice for backends and should be kept

View file

@ -117,6 +117,7 @@ type
TEnv {.final.} = object of TObject TEnv {.final.} = object of TObject
attachedNode: PNode attachedNode: PNode
createdVar: PSym # if != nil it is a used environment createdVar: PSym # if != nil it is a used environment
createdVarComesFromIter: bool
capturedVars: seq[PSym] # captured variables in this environment capturedVars: seq[PSym] # captured variables in this environment
deps: seq[TDep] # dependencies deps: seq[TDep] # dependencies
up: PEnv up: PEnv
@ -571,7 +572,14 @@ proc rawClosureCreation(o: POuterContext, scope: PEnv; env: PSym): PNode =
# maybe later: (sfByCopy in local.flags) # maybe later: (sfByCopy in local.flags)
# add ``env.param = param`` # add ``env.param = param``
result.add(newAsgnStmt(fieldAccess, newSymNode(local), env.info)) result.add(newAsgnStmt(fieldAccess, newSymNode(local), env.info))
# it can happen that we already captured 'local' in some other environment
# then we capture by copy for now. This is not entirely correct but better
# than nothing:
let existing = idNodeTableGet(o.localsToAccess, local)
if existing.isNil:
idNodeTablePut(o.localsToAccess, local, fieldAccess) idNodeTablePut(o.localsToAccess, local, fieldAccess)
else:
result.add(newAsgnStmt(fieldAccess, existing, env.info))
# add support for 'up' references: # add support for 'up' references:
for e, field in items(scope.deps): for e, field in items(scope.deps):
# add ``env.up = env2`` # add ``env.up = env2``
@ -584,6 +592,8 @@ proc generateClosureCreation(o: POuterContext, scope: PEnv): PNode =
proc generateIterClosureCreation(o: POuterContext; env: PEnv; proc generateIterClosureCreation(o: POuterContext; env: PEnv;
scope: PNode): PSym = scope: PNode): PSym =
if env.createdVarComesFromIter or env.createdVar.isNil:
# we have to create a new closure:
result = newClosureCreationVar(o, env) result = newClosureCreationVar(o, env)
let cc = rawClosureCreation(o, env, result) let cc = rawClosureCreation(o, env, result)
var insertPoint = scope.sons[0] var insertPoint = scope.sons[0]
@ -592,6 +602,9 @@ proc generateIterClosureCreation(o: POuterContext; env: PEnv;
assert cc.kind == nkStmtList and insertPoint.kind == nkStmtList assert cc.kind == nkStmtList and insertPoint.kind == nkStmtList
for x in cc: insertPoint.add(x) for x in cc: insertPoint.add(x)
if env.createdVar == nil: env.createdVar = result if env.createdVar == nil: env.createdVar = result
else:
result = env.createdVar
env.createdVarComesFromIter = true
proc interestingIterVar(s: PSym): bool {.inline.} = proc interestingIterVar(s: PSym): bool {.inline.} =
result = s.kind in {skVar, skLet, skTemp, skForVar} and sfGlobal notin s.flags result = s.kind in {skVar, skLet, skTemp, skForVar} and sfGlobal notin s.flags
@ -637,6 +650,22 @@ proc outerProcSons(o: POuterContext, n: PNode) =
let x = transformOuterProc(o, n.sons[i]) let x = transformOuterProc(o, n.sons[i])
if x != nil: n.sons[i] = x if x != nil: n.sons[i] = x
proc liftIterSym*(n: PNode): PNode =
# transforms (iter) to (let env = newClosure[iter](); (iter, env))
let iter = n.sym
assert iter.kind == skIterator
result = newNodeIT(nkStmtListExpr, n.info, n.typ)
var env = copySym(getHiddenParam(iter))
env.kind = skLet
var v = newNodeI(nkVarSection, n.info)
addVar(v, newSymNode(env))
result.add(v)
# add 'new' statement:
result.add(newCall(getSysSym"internalNew", env))
result.add makeClosure(iter, env, n.info)
proc transformOuterProc(o: POuterContext, n: PNode): PNode = proc transformOuterProc(o: POuterContext, n: PNode): PNode =
if n == nil: return nil if n == nil: return nil
case n.kind case n.kind
@ -649,18 +678,23 @@ proc transformOuterProc(o: POuterContext, n: PNode): PNode =
return indirectAccess(newSymNode(o.closureParam), local, n.info) return indirectAccess(newSymNode(o.closureParam), local, n.info)
var closure = PEnv(idTableGet(o.lambdasToEnv, local)) var closure = PEnv(idTableGet(o.lambdasToEnv, local))
if closure != nil:
# we need to replace the lambda with '(lambda, env)':
if local.kind == skIterator and local.typ.callConv == ccClosure: if local.kind == skIterator and local.typ.callConv == ccClosure:
# consider: [i1, i2, i1] Since we merged the iterator's closure # consider: [i1, i2, i1] Since we merged the iterator's closure
# with the captured owning variables, we need to generate the # with the captured owning variables, we need to generate the
# closure generation code again: # closure generation code again:
#if local == o.fn: message(n.info, errRecursiveDependencyX, local.name.s) if local == o.fn: message(n.info, errRecursiveDependencyX, local.name.s)
# XXX why doesn't this work? # XXX why doesn't this work?
if closure.isNil:
return liftIterSym(n)
else:
let createdVar = generateIterClosureCreation(o, closure, let createdVar = generateIterClosureCreation(o, closure,
closure.attachedNode) closure.attachedNode)
return makeClosure(local, createdVar, n.info) return makeClosure(local, createdVar, n.info)
if closure != nil:
# we need to replace the lambda with '(lambda, env)':
let a = closure.createdVar let a = closure.createdVar
if a != nil: if a != nil:
return makeClosure(local, a, n.info) return makeClosure(local, a, n.info)
@ -773,22 +807,6 @@ proc liftLambdasForTopLevel*(module: PSym, body: PNode): PNode =
# ------------------- iterator transformation -------------------------------- # ------------------- iterator transformation --------------------------------
proc liftIterSym*(n: PNode): PNode =
# transforms (iter) to (let env = newClosure[iter](); (iter, env))
let iter = n.sym
assert iter.kind == skIterator
result = newNodeIT(nkStmtListExpr, n.info, n.typ)
var env = copySym(getHiddenParam(iter))
env.kind = skLet
var v = newNodeI(nkVarSection, n.info)
addVar(v, newSymNode(env))
result.add(v)
# add 'new' statement:
result.add(newCall(getSysSym"internalNew", env))
result.add makeClosure(iter, env, n.info)
proc liftForLoop*(body: PNode): PNode = proc liftForLoop*(body: PNode): PNode =
# problem ahead: the iterator could be invoked indirectly, but then # problem ahead: the iterator could be invoked indirectly, but then
# we don't know what environment to create here: # we don't know what environment to create here:

View file

@ -32,6 +32,7 @@ proc considerAcc*(n: PNode): PIdent =
of nkSym: id.add(x.sym.name.s) of nkSym: id.add(x.sym.name.s)
else: globalError(n.info, errIdentifierExpected, renderTree(n)) else: globalError(n.info, errIdentifierExpected, renderTree(n))
result = getIdent(id) result = getIdent(id)
of nkOpenSymChoice, nkClosedSymChoice: result = n.sons[0].sym.name
else: else:
globalError(n.info, errIdentifierExpected, renderTree(n)) globalError(n.info, errIdentifierExpected, renderTree(n))
@ -91,7 +92,7 @@ proc errorSym*(c: PContext, n: PNode): PSym =
result.typ = errorType(c) result.typ = errorType(c)
incl(result.flags, sfDiscardable) incl(result.flags, sfDiscardable)
# pretend it's imported from some unknown module to prevent cascading errors: # pretend it's imported from some unknown module to prevent cascading errors:
if gCmd != cmdInteractive: if gCmd != cmdInteractive and c.inCompilesContext == 0:
c.importTable.addSym(result) c.importTable.addSym(result)
type type

View file

@ -1,7 +1,7 @@
# #
# #
# The Nimrod Compiler # The Nimrod Compiler
# (c) Copyright 2013 Andreas Rumpf # (c) Copyright 2014 Andreas Rumpf
# #
# See the file "copying.txt", included in this # See the file "copying.txt", included in this
# distribution, for details about the copyright. # distribution, for details about the copyright.
@ -20,7 +20,7 @@ type
TModuleInMemory* = object TModuleInMemory* = object
compiledAt*: float compiledAt*: float
crc*: TCrc32 crc*: TCrc32
deps*: seq[int32] ## XXX: slurped files are not currently tracked deps*: seq[int32] ## XXX: slurped files are currently not tracked
needsRecompile*: TNeedRecompile needsRecompile*: TNeedRecompile
crcStatus*: TCrcStatus crcStatus*: TCrcStatus
@ -83,7 +83,7 @@ proc resetAllModules* =
for i in 0..gCompiledModules.high: for i in 0..gCompiledModules.high:
if gCompiledModules[i] != nil: if gCompiledModules[i] != nil:
resetModule(i.int32) resetModule(i.int32)
resetPackageCache()
# for m in cgenModules(): echo "CGEN MODULE FOUND" # for m in cgenModules(): echo "CGEN MODULE FOUND"
proc checkDepMem(fileIdx: int32): TNeedRecompile = proc checkDepMem(fileIdx: int32): TNeedRecompile =
@ -120,8 +120,9 @@ proc newModule(fileIdx: int32): PSym =
if not isNimrodIdentifier(result.name.s): if not isNimrodIdentifier(result.name.s):
rawMessage(errInvalidModuleName, result.name.s) rawMessage(errInvalidModuleName, result.name.s)
result.owner = result # a module belongs to itself
result.info = newLineInfo(fileIdx, 1, 1) result.info = newLineInfo(fileIdx, 1, 1)
result.owner = newSym(skPackage, getIdent(getPackageName(filename)), nil,
result.info)
result.position = fileIdx result.position = fileIdx
growCache gMemCacheData, fileIdx growCache gMemCacheData, fileIdx

View file

@ -67,7 +67,7 @@ type
errAmbiguousCallXYZ, errWrongNumberOfArguments, errAmbiguousCallXYZ, errWrongNumberOfArguments,
errXCannotBePassedToProcVar, errXCannotBePassedToProcVar,
errXCannotBeInParamDecl, errPragmaOnlyInHeaderOfProc, errImplOfXNotAllowed, errXCannotBeInParamDecl, errPragmaOnlyInHeaderOfProc, errImplOfXNotAllowed,
errImplOfXexpected, errNoSymbolToBorrowFromFound, errDiscardValue, errImplOfXexpected, errNoSymbolToBorrowFromFound, errDiscardValueX,
errInvalidDiscard, errIllegalConvFromXtoY, errCannotBindXTwice, errInvalidDiscard, errIllegalConvFromXtoY, errCannotBindXTwice,
errInvalidOrderInArrayConstructor, errInvalidOrderInArrayConstructor,
errInvalidOrderInEnumX, errEnumXHasHoles, errExceptExpected, errInvalidTry, errInvalidOrderInEnumX, errEnumXHasHoles, errExceptExpected, errInvalidTry,
@ -89,10 +89,12 @@ type
errInvalidIndexValueForTuple, errCommandExpectsFilename, errInvalidIndexValueForTuple, errCommandExpectsFilename,
errMainModuleMustBeSpecified, errMainModuleMustBeSpecified,
errXExpected, errXExpected,
errTIsNotAConcreteType,
errInvalidSectionStart, errGridTableNotImplemented, errGeneralParseError, errInvalidSectionStart, errGridTableNotImplemented, errGeneralParseError,
errNewSectionExpected, errWhitespaceExpected, errXisNoValidIndexFile, errNewSectionExpected, errWhitespaceExpected, errXisNoValidIndexFile,
errCannotRenderX, errVarVarTypeNotAllowed, errInstantiateXExplicitely, errCannotRenderX, errVarVarTypeNotAllowed, errInstantiateXExplicitely,
errOnlyACallOpCanBeDelegator, errUsingNoSymbol, errOnlyACallOpCanBeDelegator, errUsingNoSymbol,
errMacroBodyDependsOnGenericTypes,
errDestructorNotGenericEnough, errDestructorNotGenericEnough,
errXExpectsTwoArguments, errXExpectsTwoArguments,
@ -103,6 +105,7 @@ type
errXhasSideEffects, errIteratorExpected, errLetNeedsInit, errXhasSideEffects, errIteratorExpected, errLetNeedsInit,
errThreadvarCannotInit, errWrongSymbolX, errIllegalCaptureX, errThreadvarCannotInit, errWrongSymbolX, errIllegalCaptureX,
errXCannotBeClosure, errXMustBeCompileTime, errXCannotBeClosure, errXMustBeCompileTime,
errCannotInferTypeOfTheLiteral,
errUser, errUser,
warnCannotOpenFile, warnCannotOpenFile,
warnOctalEscape, warnXIsNeverRead, warnXmightNotBeenInit, warnOctalEscape, warnXIsNeverRead, warnXmightNotBeenInit,
@ -263,7 +266,7 @@ const
errImplOfXNotAllowed: "implementation of \'$1\' is not allowed", errImplOfXNotAllowed: "implementation of \'$1\' is not allowed",
errImplOfXexpected: "implementation of \'$1\' expected", errImplOfXexpected: "implementation of \'$1\' expected",
errNoSymbolToBorrowFromFound: "no symbol to borrow from found", errNoSymbolToBorrowFromFound: "no symbol to borrow from found",
errDiscardValue: "value returned by statement has to be discarded", errDiscardValueX: "value of type '$1' has to be discarded",
errInvalidDiscard: "statement returns no value that can be discarded", errInvalidDiscard: "statement returns no value that can be discarded",
errIllegalConvFromXtoY: "conversion from $1 to $2 is invalid", errIllegalConvFromXtoY: "conversion from $1 to $2 is invalid",
errCannotBindXTwice: "cannot bind parameter \'$1\' twice", errCannotBindXTwice: "cannot bind parameter \'$1\' twice",
@ -312,6 +315,7 @@ const
errCommandExpectsFilename: "command expects a filename argument", errCommandExpectsFilename: "command expects a filename argument",
errMainModuleMustBeSpecified: "please, specify a main module in the project configuration file", errMainModuleMustBeSpecified: "please, specify a main module in the project configuration file",
errXExpected: "\'$1\' expected", errXExpected: "\'$1\' expected",
errTIsNotAConcreteType: "\'$1\' is not a concrete type.",
errInvalidSectionStart: "invalid section start", errInvalidSectionStart: "invalid section start",
errGridTableNotImplemented: "grid table is not implemented", errGridTableNotImplemented: "grid table is not implemented",
errGeneralParseError: "general parse error", errGeneralParseError: "general parse error",
@ -323,6 +327,8 @@ const
errInstantiateXExplicitely: "instantiate '$1' explicitely", errInstantiateXExplicitely: "instantiate '$1' explicitely",
errOnlyACallOpCanBeDelegator: "only a call operator can be a delegator", errOnlyACallOpCanBeDelegator: "only a call operator can be a delegator",
errUsingNoSymbol: "'$1' is not a variable, constant or a proc name", errUsingNoSymbol: "'$1' is not a variable, constant or a proc name",
errMacroBodyDependsOnGenericTypes: "the macro body cannot be compiled, " &
"because the parameter '$1' has a generic type",
errDestructorNotGenericEnough: "Destructor signarue is too specific. " & errDestructorNotGenericEnough: "Destructor signarue is too specific. " &
"A destructor must be associated will all instantiations of a generic type", "A destructor must be associated will all instantiations of a generic type",
errXExpectsTwoArguments: "\'$1\' expects two arguments", errXExpectsTwoArguments: "\'$1\' expects two arguments",
@ -346,6 +352,7 @@ const
errIllegalCaptureX: "illegal capture '$1'", errIllegalCaptureX: "illegal capture '$1'",
errXCannotBeClosure: "'$1' cannot have 'closure' calling convention", errXCannotBeClosure: "'$1' cannot have 'closure' calling convention",
errXMustBeCompileTime: "'$1' can only be used in compile-time context", errXMustBeCompileTime: "'$1' can only be used in compile-time context",
errCannotInferTypeOfTheLiteral: "cannot infer the type of the $1",
errUser: "$1", errUser: "$1",
warnCannotOpenFile: "cannot open \'$1\' [CannotOpenFile]", warnCannotOpenFile: "cannot open \'$1\' [CannotOpenFile]",
warnOctalEscape: "octal escape sequences do not exist; leading zero is ignored [OctalEscape]", warnOctalEscape: "octal escape sequences do not exist; leading zero is ignored [OctalEscape]",

View file

@ -17,11 +17,11 @@ proc execute*(program: string) =
passes.gIncludeFile = includeModule passes.gIncludeFile = includeModule
passes.gImportModule = importModule passes.gImportModule = importModule
initDefines() initDefines()
LoadConfigs(DefaultConfig) loadConfigs(DefaultConfig)
initDefines() initDefines()
DefineSymbol("nimrodvm") defineSymbol("nimrodvm")
when hasFFI: DefineSymbol("nimffi") when hasFFI: defineSymbol("nimffi")
registerPass(verbosePass) registerPass(verbosePass)
registerPass(semPass) registerPass(semPass)
registerPass(vmPass) registerPass(vmPass)
@ -30,4 +30,4 @@ proc execute*(program: string) =
compileSystemModule() compileSystemModule()
var m = makeStdinModule() var m = makeStdinModule()
incl(m.flags, sfMainModule) incl(m.flags, sfMainModule)
processModule(m, LLStreamOpen(program), nil) processModule(m, llStreamOpen(program), nil)

View file

@ -17,4 +17,6 @@ import:testability
cincludes: "$lib/wrappers/libffi/common" cincludes: "$lib/wrappers/libffi/common"
@end @end
define:useStdoutAsStdmsg
cs:partial cs:partial

View file

@ -209,21 +209,42 @@ proc getGeneratedPath: string =
result = if nimcacheDir.len > 0: nimcacheDir else: gProjectPath.shortenDir / result = if nimcacheDir.len > 0: nimcacheDir else: gProjectPath.shortenDir /
genSubDir genSubDir
template newPackageCache(): expr =
newStringTable(when FileSystemCaseSensitive:
modeCaseInsensitive
else:
modeCaseSensitive)
var packageCache = newPackageCache()
proc resetPackageCache*() = packageCache = newPackageCache()
iterator myParentDirs(p: string): string =
# XXX os's parentDirs is stupid (multiple yields) and triggers an old bug...
var current = p
while true:
current = current.parentDir
if current.len == 0: break
yield current
proc getPackageName*(path: string): string = proc getPackageName*(path: string): string =
var q = 1 var parents = 0
var b = 0 block packageSearch:
if path[len(path)-1] in {DirSep, AltSep}: q = 2 for d in myParentDirs(path):
for i in countdown(len(path)-q, 0): if packageCache.hasKey(d):
if path[i] in {DirSep, AltSep}: #echo "from cache ", d, " |", packageCache[d], "|", path.splitFile.name
if b == 0: b = i return packageCache[d]
else: inc parents
let x = path.substr(i+1, b-1) for file in walkFiles(d / "*.babel"):
case x.normalize result = file.splitFile.name
of "lib", "src", "source", "package", "pckg", "library", "private": break packageSearch
b = i # we also store if we didn't find anything:
else: if result.isNil: result = ""
return x.replace('.', '_') for d in myParentDirs(path):
result = "" #echo "set cache ", d, " |", result, "|", parents
packageCache[d] = result
dec parents
if parents <= 0: break
proc withPackageName*(path: string): string = proc withPackageName*(path: string): string =
let x = path.getPackageName let x = path.getPackageName

View file

@ -281,7 +281,7 @@ proc parseSymbol(p: var TParser): PNode =
add(result, newIdentNodeP(getIdent"{}", p)) add(result, newIdentNodeP(getIdent"{}", p))
getTok(p) getTok(p)
eat(p, tkCurlyRi) eat(p, tkCurlyRi)
of tokKeywordLow..tokKeywordHigh, tkSymbol, tkOpr, tkDotDot: of tokKeywordLow..tokKeywordHigh, tkSymbol, tkOpr, tkDot, tkDotDot:
add(result, newIdentNodeP(p.tok.ident, p)) add(result, newIdentNodeP(p.tok.ident, p))
getTok(p) getTok(p)
of tkIntLit..tkCharLit: of tkIntLit..tkCharLit:

View file

@ -342,7 +342,7 @@ proc getSymbol(L: var TLexer, tok: var TToken) =
h = h +% ord(c) h = h +% ord(c)
h = h +% h shl 10 h = h +% h shl 10
h = h xor (h shr 6) h = h xor (h shr 6)
of '_': nil of '_': discard
else: break else: break
inc(pos) inc(pos)
h = h +% h shl 3 h = h +% h shl 3

View file

@ -335,7 +335,7 @@ proc exprColonEqExprList(p: var TParser, kind, elemKind: TNodeKind,
proc setBaseFlags(n: PNode, base: TNumericalBase) = proc setBaseFlags(n: PNode, base: TNumericalBase) =
case base case base
of base10: nil of base10: discard
of base2: incl(n.flags, nfBase2) of base2: incl(n.flags, nfBase2)
of base8: incl(n.flags, nfBase8) of base8: incl(n.flags, nfBase8)
of base16: incl(n.flags, nfBase16) of base16: incl(n.flags, nfBase16)
@ -466,7 +466,7 @@ proc lowestExprAux(p: var TParser, v: var PNode, limit: int): TTokKind =
eat(p, pxCurlyDirRi) eat(p, pxCurlyDirRi)
opNode.ident = getIdent("&") opNode.ident = getIdent("&")
else: else:
nil discard
of pxMinus: of pxMinus:
if p.tok.xkind == pxPer: if p.tok.xkind == pxPer:
getTok(p) getTok(p)
@ -477,7 +477,7 @@ proc lowestExprAux(p: var TParser, v: var PNode, limit: int): TTokKind =
of pxNeq: of pxNeq:
opNode.ident = getIdent("!=") opNode.ident = getIdent("!=")
else: else:
nil discard
skipCom(p, opNode) # read sub-expression with higher priority skipCom(p, opNode) # read sub-expression with higher priority
nextop = lowestExprAux(p, v2, opPred) nextop = lowestExprAux(p, v2, opPred)
addSon(node, opNode) addSon(node, opNode)
@ -505,7 +505,7 @@ proc fixExpr(n: PNode): PNode =
(n.sons[2].kind in {nkCharLit, nkStrLit}): (n.sons[2].kind in {nkCharLit, nkStrLit}):
n.sons[0].ident = getIdent("&") # fix operator n.sons[0].ident = getIdent("&") # fix operator
else: else:
nil discard
if not (n.kind in {nkEmpty..nkNilLit}): if not (n.kind in {nkEmpty..nkNilLit}):
for i in countup(0, sonsLen(n) - 1): result.sons[i] = fixExpr(n.sons[i]) for i in countup(0, sonsLen(n) - 1): result.sons[i] = fixExpr(n.sons[i])
@ -603,7 +603,7 @@ proc parseStmtList(p: var TParser): PNode =
of pxCurlyDirLe, pxStarDirLe: of pxCurlyDirLe, pxStarDirLe:
if not isHandledDirective(p): break if not isHandledDirective(p): break
else: else:
nil discard
addSon(result, parseStmt(p)) addSon(result, parseStmt(p))
if sonsLen(result) == 1: result = result.sons[0] if sonsLen(result) == 1: result = result.sons[0]
@ -732,7 +732,7 @@ proc parseRepeat(p: var TParser): PNode =
addSon(b, c) addSon(b, c)
addSon(a, b) addSon(a, b)
if b.sons[0].kind == nkIdent and b.sons[0].ident.id == getIdent("false").id: if b.sons[0].kind == nkIdent and b.sons[0].ident.id == getIdent("false").id:
nil discard
else: else:
addSon(s, a) addSon(s, a)
addSon(result, s) addSon(result, s)
@ -840,7 +840,7 @@ proc parseParam(p: var TParser): PNode =
getTok(p) getTok(p)
v = newNodeP(nkVarTy, p) v = newNodeP(nkVarTy, p)
else: else:
nil discard
while true: while true:
case p.tok.xkind case p.tok.xkind
of pxSymbol: a = createIdentNodeP(p.tok.ident, p) of pxSymbol: a = createIdentNodeP(p.tok.ident, p)
@ -1133,7 +1133,7 @@ proc parseRecordPart(p: var TParser): PNode =
proc exSymbol(n: var PNode) = proc exSymbol(n: var PNode) =
case n.kind case n.kind
of nkPostfix: of nkPostfix:
nil discard
of nkPragmaExpr: of nkPragmaExpr:
exSymbol(n.sons[0]) exSymbol(n.sons[0])
of nkIdent, nkAccQuoted: of nkIdent, nkAccQuoted:
@ -1154,7 +1154,7 @@ proc fixRecordDef(n: var PNode) =
for i in countup(0, sonsLen(n) - 1): fixRecordDef(n.sons[i]) for i in countup(0, sonsLen(n) - 1): fixRecordDef(n.sons[i])
of nkIdentDefs: of nkIdentDefs:
for i in countup(0, sonsLen(n) - 3): exSymbol(n.sons[i]) for i in countup(0, sonsLen(n) - 3): exSymbol(n.sons[i])
of nkNilLit, nkEmpty: nil of nkNilLit, nkEmpty: discard
else: internalError(n.info, "fixRecordDef(): " & $n.kind) else: internalError(n.info, "fixRecordDef(): " & $n.kind)
proc addPragmaToIdent(ident: var PNode, pragma: PNode) = proc addPragmaToIdent(ident: var PNode, pragma: PNode) =
@ -1191,7 +1191,7 @@ proc parseRecordBody(p: var TParser, result, definition: PNode) =
if definition != nil: addPragmaToIdent(definition.sons[0], parseCommand(p)) if definition != nil: addPragmaToIdent(definition.sons[0], parseCommand(p))
else: internalError(result.info, "anonymous record is not supported") else: internalError(result.info, "anonymous record is not supported")
else: else:
nil discard
opt(p, pxSemicolon) opt(p, pxSemicolon)
skipCom(p, result) skipCom(p, result)
@ -1399,7 +1399,7 @@ proc fixVarSection(p: var TParser, counter: PNode) =
proc exSymbols(n: PNode) = proc exSymbols(n: PNode) =
case n.kind case n.kind
of nkEmpty..nkNilLit: nil of nkEmpty..nkNilLit: discard
of nkProcDef..nkIteratorDef: exSymbol(n.sons[namePos]) of nkProcDef..nkIteratorDef: exSymbol(n.sons[namePos])
of nkWhenStmt, nkStmtList: of nkWhenStmt, nkStmtList:
for i in countup(0, sonsLen(n) - 1): exSymbols(n.sons[i]) for i in countup(0, sonsLen(n) - 1): exSymbols(n.sons[i])
@ -1410,7 +1410,7 @@ proc exSymbols(n: PNode) =
exSymbol(n.sons[i].sons[0]) exSymbol(n.sons[i].sons[0])
if n.sons[i].sons[2].kind == nkObjectTy: if n.sons[i].sons[2].kind == nkObjectTy:
fixRecordDef(n.sons[i].sons[2]) fixRecordDef(n.sons[i].sons[2])
else: nil else: discard
proc parseBegin(p: var TParser, result: PNode) = proc parseBegin(p: var TParser, result: PNode) =
getTok(p) getTok(p)

View file

@ -23,7 +23,7 @@ const
wMagic, wNosideeffect, wSideeffect, wNoreturn, wDynlib, wHeader, wMagic, wNosideeffect, wSideeffect, wNoreturn, wDynlib, wHeader,
wCompilerproc, wProcVar, wDeprecated, wVarargs, wCompileTime, wMerge, wCompilerproc, wProcVar, wDeprecated, wVarargs, wCompileTime, wMerge,
wBorrow, wExtern, wImportCompilerProc, wThread, wImportCpp, wImportObjC, wBorrow, wExtern, wImportCompilerProc, wThread, wImportCpp, wImportObjC,
wNoStackFrame, wError, wDiscardable, wNoInit, wDestructor, wCodegenDecl, wAsmNoStackFrame, wError, wDiscardable, wNoInit, wDestructor, wCodegenDecl,
wGensym, wInject, wRaises, wTags, wOperator, wDelegator} wGensym, wInject, wRaises, wTags, wOperator, wDelegator}
converterPragmas* = procPragmas converterPragmas* = procPragmas
methodPragmas* = procPragmas methodPragmas* = procPragmas
@ -47,12 +47,12 @@ const
wInjectStmt} wInjectStmt}
lambdaPragmas* = {FirstCallConv..LastCallConv, wImportc, wExportc, wNodecl, lambdaPragmas* = {FirstCallConv..LastCallConv, wImportc, wExportc, wNodecl,
wNosideeffect, wSideeffect, wNoreturn, wDynlib, wHeader, wNosideeffect, wSideeffect, wNoreturn, wDynlib, wHeader,
wDeprecated, wExtern, wThread, wImportCpp, wImportObjC, wNoStackFrame, wDeprecated, wExtern, wThread, wImportCpp, wImportObjC, wAsmNoStackFrame,
wRaises, wTags} wRaises, wTags}
typePragmas* = {wImportc, wExportc, wDeprecated, wMagic, wAcyclic, wNodecl, typePragmas* = {wImportc, wExportc, wDeprecated, wMagic, wAcyclic, wNodecl,
wPure, wHeader, wCompilerproc, wFinal, wSize, wExtern, wShallow, wPure, wHeader, wCompilerproc, wFinal, wSize, wExtern, wShallow,
wImportCpp, wImportObjC, wError, wIncompleteStruct, wByCopy, wByRef, wImportCpp, wImportObjC, wError, wIncompleteStruct, wByCopy, wByRef,
wInheritable, wGensym, wInject, wRequiresInit} wInheritable, wGensym, wInject, wRequiresInit, wUnchecked, wUnion}
fieldPragmas* = {wImportc, wExportc, wDeprecated, wExtern, fieldPragmas* = {wImportc, wExportc, wDeprecated, wExtern,
wImportCpp, wImportObjC, wError} wImportCpp, wImportObjC, wError}
varPragmas* = {wImportc, wExportc, wVolatile, wRegister, wThreadVar, wNodecl, varPragmas* = {wImportc, wExportc, wVolatile, wRegister, wThreadVar, wNodecl,
@ -97,8 +97,25 @@ proc makeExternImport(s: PSym, extname: string) =
incl(s.flags, sfImportc) incl(s.flags, sfImportc)
excl(s.flags, sfForward) excl(s.flags, sfForward)
proc makeExternExport(s: PSym, extname: string) = const invalidIdentChars = AllChars - IdentChars
proc validateExternCName(s: PSym, info: TLineInfo) =
## Validates that the symbol name in s.loc.r is a valid C identifier.
##
## Valid identifiers are those alphanumeric including the underscore not
## starting with a number. If the check fails, a generic error will be
## displayed to the user.
let target = ropeToStr(s.loc.r)
if target.len < 1 or (not (target[0] in IdentStartChars)) or
(not target.allCharsInSet(IdentChars)):
localError(info, errGenerated, "invalid exported symbol")
proc makeExternExport(s: PSym, extname: string, info: TLineInfo) =
setExternName(s, extname) setExternName(s, extname)
case gCmd
of cmdCompileToC, cmdCompileToCpp, cmdCompileToOC:
validateExternCName(s, info)
else: discard
incl(s.flags, sfExportc) incl(s.flags, sfExportc)
proc processImportCompilerProc(s: PSym, extname: string) = proc processImportCompilerProc(s: PSym, extname: string) =
@ -515,7 +532,7 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: int,
if k in validPragmas: if k in validPragmas:
case k case k
of wExportc: of wExportc:
makeExternExport(sym, getOptionalStr(c, it, "$1")) makeExternExport(sym, getOptionalStr(c, it, "$1"), it.info)
incl(sym.flags, sfUsed) # avoid wrong hints incl(sym.flags, sfUsed) # avoid wrong hints
of wImportc: makeExternImport(sym, getOptionalStr(c, it, "$1")) of wImportc: makeExternImport(sym, getOptionalStr(c, it, "$1"))
of wImportCompilerProc: of wImportCompilerProc:
@ -548,9 +565,11 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: int,
of wNodecl: of wNodecl:
noVal(it) noVal(it)
incl(sym.loc.flags, lfNoDecl) incl(sym.loc.flags, lfNoDecl)
of wPure, wNoStackFrame: of wPure, wAsmNoStackFrame:
noVal(it) noVal(it)
if sym != nil: incl(sym.flags, sfPure) if sym != nil:
if k == wPure and sym.kind in routineKinds: invalidPragma(it)
else: incl(sym.flags, sfPure)
of wVolatile: of wVolatile:
noVal(it) noVal(it)
incl(sym.flags, sfVolatile) incl(sym.flags, sfVolatile)
@ -601,7 +620,7 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: int,
processDynLib(c, it, sym) processDynLib(c, it, sym)
of wCompilerproc: of wCompilerproc:
noVal(it) # compilerproc may not get a string! noVal(it) # compilerproc may not get a string!
makeExternExport(sym, "$1") makeExternExport(sym, "$1", it.info)
incl(sym.flags, sfCompilerProc) incl(sym.flags, sfCompilerProc)
incl(sym.flags, sfUsed) # suppress all those stupid warnings incl(sym.flags, sfUsed) # suppress all those stupid warnings
registerCompilerProc(sym) registerCompilerProc(sym)
@ -699,6 +718,14 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: int,
noVal(it) noVal(it)
if sym.typ == nil: invalidPragma(it) if sym.typ == nil: invalidPragma(it)
else: incl(sym.typ.flags, tfIncompleteStruct) else: incl(sym.typ.flags, tfIncompleteStruct)
of wUnchecked:
noVal(it)
if sym.typ == nil: invalidPragma(it)
else: incl(sym.typ.flags, tfUncheckedArray)
of wUnion:
noVal(it)
if sym.typ == nil: invalidPragma(it)
else: incl(sym.typ.flags, tfUnion)
of wRequiresInit: of wRequiresInit:
noVal(it) noVal(it)
if sym.typ == nil: invalidPragma(it) if sym.typ == nil: invalidPragma(it)

View file

@ -558,15 +558,18 @@ proc longMode(n: PNode, start: int = 0, theEnd: int = - 1): bool =
result = true result = true
break break
proc gstmts(g: var TSrcGen, n: PNode, c: TContext) = proc gstmts(g: var TSrcGen, n: PNode, c: TContext, doIndent=true) =
if n.kind == nkEmpty: return if n.kind == nkEmpty: return
if n.kind in {nkStmtList, nkStmtListExpr, nkStmtListType}: if n.kind in {nkStmtList, nkStmtListExpr, nkStmtListType}:
indentNL(g) if doIndent: indentNL(g)
for i in countup(0, sonsLen(n) - 1): for i in countup(0, sonsLen(n) - 1):
optNL(g) optNL(g)
if n.sons[i].kind in {nkStmtList, nkStmtListExpr, nkStmtListType}:
gstmts(g, n.sons[i], c, doIndent=false)
else:
gsub(g, n.sons[i]) gsub(g, n.sons[i])
gcoms(g) gcoms(g)
dedent(g) if doIndent: dedent(g)
else: else:
if rfLongMode in c.flags: indentNL(g) if rfLongMode in c.flags: indentNL(g)
gsub(g, n) gsub(g, n)
@ -1268,7 +1271,7 @@ proc gsub(g: var TSrcGen, n: PNode, c: TContext) =
put(g, tkBracketLe, "[") put(g, tkBracketLe, "[")
gcomma(g, n) gcomma(g, n)
put(g, tkBracketRi, "]") put(g, tkBracketRi, "]")
of nkMetaNode: of nkMetaNode_Obsolete:
put(g, tkParLe, "(META|") put(g, tkParLe, "(META|")
gsub(g, n.sons[0]) gsub(g, n.sons[0])
put(g, tkParRi, ")") put(g, tkParRi, ")")

View file

@ -890,7 +890,7 @@ proc loadStub*(s: PSym) =
# deactivate the GC here because we do a deep recursion and generate no # deactivate the GC here because we do a deep recursion and generate no
# garbage when restoring parts of the object graph anyway. # garbage when restoring parts of the object graph anyway.
# Since we die with internal errors if this fails, so no try-finally is # Since we die with internal errors if this fails, no try-finally is
# necessary. # necessary.
GC_disable() GC_disable()
rawLoadStub(s) rawLoadStub(s)

View file

@ -120,7 +120,8 @@ proc commonType*(x, y: PType): PType =
if a.kind == tyObject and b.kind == tyObject: if a.kind == tyObject and b.kind == tyObject:
result = commonSuperclass(a, b) result = commonSuperclass(a, b)
# this will trigger an error later: # this will trigger an error later:
if result.isNil: return x if result.isNil or result == a: return x
if result == b: return y
if k != tyNone: if k != tyNone:
let r = result let r = result
result = newType(k, r.owner) result = newType(k, r.owner)
@ -219,7 +220,7 @@ proc tryConstExpr(c: PContext, n: PNode): PNode =
return nil return nil
result = fixupTypeAfterEval(c, result, e) result = fixupTypeAfterEval(c, result, e)
except: except ERecoverableError:
return nil return nil
proc semConstExpr(c: PContext, n: PNode): PNode = proc semConstExpr(c: PContext, n: PNode): PNode =
@ -332,6 +333,8 @@ proc myOpen(module: PSym): PPassContext =
c.semOperand = semOperand c.semOperand = semOperand
c.semConstBoolExpr = semConstBoolExpr c.semConstBoolExpr = semConstBoolExpr
c.semOverloadedCall = semOverloadedCall c.semOverloadedCall = semOverloadedCall
c.semInferredLambda = semInferredLambda
c.semGenerateInstance = generateInstance
c.semTypeNode = semTypeNode c.semTypeNode = semTypeNode
pushProcCon(c, module) pushProcCon(c, module)
pushOwner(c.module) pushOwner(c.module)

View file

@ -82,7 +82,7 @@ proc notFoundError*(c: PContext, n: PNode, errors: seq[string]) =
# fail fast: # fail fast:
globalError(n.info, errTypeMismatch, "") globalError(n.info, errTypeMismatch, "")
var result = msgKindToString(errTypeMismatch) var result = msgKindToString(errTypeMismatch)
add(result, describeArgs(c, n, 1 + ord(nfDelegate in n.flags))) add(result, describeArgs(c, n, 1 + ord(nfDotField in n.flags)))
add(result, ')') add(result, ')')
var candidates = "" var candidates = ""
@ -138,15 +138,33 @@ proc resolveOverloads(c: PContext, n, orig: PNode,
let overloadsState = result.state let overloadsState = result.state
if overloadsState != csMatch: if overloadsState != csMatch:
if nfDelegate in n.flags: if nfDotField in n.flags:
internalAssert f.kind == nkIdent internalAssert f.kind == nkIdent and n.sonsLen >= 2
let calleeName = newStrNode(nkStrLit, f.ident.s) let calleeName = newStrNode(nkStrLit, f.ident.s).withInfo(n.info)
calleeName.info = n.info
let callOp = newIdentNode(idDelegator, n.info) # leave the op head symbol empty,
n.sons[0..0] = [callOp, calleeName] # we are going to try multiple variants
orig.sons[0..0] = [callOp, calleeName] n.sons[0..1] = [nil, n[1], calleeName]
orig.sons[0..1] = [nil, orig[1], calleeName]
template tryOp(x) =
let op = newIdentNode(getIdent(x), n.info)
n.sons[0] = op
orig.sons[0] = op
pickBest(op)
if nfExplicitCall in n.flags:
tryOp ".()"
if result.state in {csEmpty, csNoMatch}:
tryOp "."
elif nfDotSetter in n.flags:
internalAssert f.kind == nkIdent and n.sonsLen == 3
let calleeName = newStrNode(nkStrLit, f.ident.s[0.. -2]).withInfo(n.info)
let callOp = newIdentNode(getIdent".=", n.info)
n.sons[0..1] = [callOp, n[1], calleeName]
orig.sons[0..1] = [callOp, orig[1], calleeName]
pickBest(callOp) pickBest(callOp)
if overloadsState == csEmpty and result.state == csEmpty: if overloadsState == csEmpty and result.state == csEmpty:

View file

@ -81,6 +81,9 @@ type
semOverloadedCall*: proc (c: PContext, n, nOrig: PNode, semOverloadedCall*: proc (c: PContext, n, nOrig: PNode,
filter: TSymKinds): PNode {.nimcall.} filter: TSymKinds): PNode {.nimcall.}
semTypeNode*: proc(c: PContext, n: PNode, prev: PType): PType {.nimcall.} semTypeNode*: proc(c: PContext, n: PNode, prev: PType): PType {.nimcall.}
semInferredLambda*: proc(c: PContext, pt: TIdTable, n: PNode): PNode
semGenerateInstance*: proc (c: PContext, fn: PSym, pt: TIdTable,
info: TLineInfo): PSym
includedFiles*: TIntSet # used to detect recursive include files includedFiles*: TIntSet # used to detect recursive include files
userPragmas*: TStrTable userPragmas*: TStrTable
evalContext*: PEvalContext evalContext*: PEvalContext
@ -211,7 +214,6 @@ proc makeTypeDesc*(c: PContext, typ: PType): PType =
proc makeTypeSymNode*(c: PContext, typ: PType, info: TLineInfo): PNode = proc makeTypeSymNode*(c: PContext, typ: PType, info: TLineInfo): PNode =
let typedesc = makeTypeDesc(c, typ) let typedesc = makeTypeDesc(c, typ)
rawAddSon(typedesc, newTypeS(tyNone, c))
let sym = newSym(skType, idAnon, getCurrOwner(), info).linkTo(typedesc) let sym = newSym(skType, idAnon, getCurrOwner(), info).linkTo(typedesc)
return newSymNode(sym, info) return newSymNode(sym, info)

View file

@ -55,7 +55,9 @@ proc doDestructorStuff(c: PContext, s: PSym, n: PNode) =
useSym(destructableT.destructor), useSym(destructableT.destructor),
n.sons[paramsPos][1][0]])) n.sons[paramsPos][1][0]]))
proc destroyField(c: PContext, field: PSym, holder: PNode): PNode = proc destroyFieldOrFields(c: PContext, field: PNode, holder: PNode): PNode
proc destroySym(c: PContext, field: PSym, holder: PNode): PNode =
let destructableT = instantiateDestructor(c, field.typ) let destructableT = instantiateDestructor(c, field.typ)
if destructableT != nil: if destructableT != nil:
result = newNode(nkCall, field.info, @[ result = newNode(nkCall, field.info, @[
@ -70,56 +72,49 @@ proc destroyCase(c: PContext, n: PNode, holder: PNode): PNode =
for i in countup(1, n.len - 1): for i in countup(1, n.len - 1):
# of A, B: # of A, B:
var caseBranch = newNode(n[i].kind, n[i].info, n[i].sons[0 .. -2]) var caseBranch = newNode(n[i].kind, n[i].info, n[i].sons[0 .. -2])
let recList = n[i].lastSon
var destroyRecList = newNode(nkStmtList, n[i].info, @[])
template addField(f: expr): stmt =
let stmt = destroyField(c, f, holder)
if stmt != nil:
destroyRecList.addSon(stmt)
inc nonTrivialFields
case recList.kind let stmt = destroyFieldOrFields(c, n[i].lastSon, holder)
of nkSym: if stmt == nil:
addField(recList.sym) caseBranch.addSon(newNode(nkStmtList, n[i].info, @[]))
of nkRecList:
for j in countup(0, recList.len - 1):
addField(recList[j].sym)
else: else:
internalAssert false caseBranch.addSon(stmt)
nonTrivialFields += stmt.len
caseBranch.addSon(destroyRecList)
result.addSon(caseBranch) result.addSon(caseBranch)
# maybe no fields were destroyed? # maybe no fields were destroyed?
if nonTrivialFields == 0: if nonTrivialFields == 0:
result = nil result = nil
proc destroyFieldOrFields(c: PContext, field: PNode, holder: PNode): PNode =
template maybeAddLine(e: expr): stmt =
let stmt = e
if stmt != nil:
if result == nil: result = newNode(nkStmtList)
result.addSon(stmt)
case field.kind
of nkRecCase:
maybeAddLine destroyCase(c, field, holder)
of nkSym:
maybeAddLine destroySym(c, field.sym, holder)
of nkRecList:
for son in field:
maybeAddLine destroyFieldOrFields(c, son, holder)
else:
internalAssert false
proc generateDestructor(c: PContext, t: PType): PNode = proc generateDestructor(c: PContext, t: PType): PNode =
## generate a destructor for a user-defined object or tuple type ## generate a destructor for a user-defined object or tuple type
## returns nil if the destructor turns out to be trivial ## returns nil if the destructor turns out to be trivial
template addLine(e: expr): stmt =
if result == nil: result = newNode(nkStmtList)
result.addSon(e)
# XXX: This may be true for some C-imported types such as # XXX: This may be true for some C-imported types such as
# Tposix_spawnattr # Tposix_spawnattr
if t.n == nil or t.n.sons == nil: return if t.n == nil or t.n.sons == nil: return
internalAssert t.n.kind == nkRecList internalAssert t.n.kind == nkRecList
let destructedObj = newIdentNode(destructorParam, unknownLineInfo()) let destructedObj = newIdentNode(destructorParam, unknownLineInfo())
# call the destructods of all fields # call the destructods of all fields
for s in countup(0, t.n.sons.len - 1): result = destroyFieldOrFields(c, t.n, destructedObj)
case t.n.sons[s].kind
of nkRecCase:
let stmt = destroyCase(c, t.n.sons[s], destructedObj)
if stmt != nil: addLine(stmt)
of nkSym:
let stmt = destroyField(c, t.n.sons[s].sym, destructedObj)
if stmt != nil: addLine(stmt)
else:
# XXX just skip it for now so that the compiler doesn't crash, but
# please zahary fix it! arbitrary nesting of nkRecList/nkRecCase is
# possible. Any thread example seems to trigger this.
discard
# base classes' destructors will be automatically called by # base classes' destructors will be automatically called by
# semProcAux for both auto-generated and user-defined destructors # semProcAux for both auto-generated and user-defined destructors

View file

@ -204,7 +204,14 @@ proc semConv(c: PContext, n: PNode): PNode =
if not isSymChoice(op): if not isSymChoice(op):
let status = checkConvertible(c, result.typ, op.typ) let status = checkConvertible(c, result.typ, op.typ)
case status case status
of convOK: discard of convOK:
# handle SomeProcType(SomeGenericProc)
# XXX: This needs fixing. checkConvertible uses typeRel internally, but
# doesn't bother to perform the work done in paramTypeMatchAux/fitNode
# so we are redoing the typeRel work here. Why does semConv exist as a
# separate proc from fitNode?
if op.kind == nkSym and op.sym.isGenericRoutine:
result.sons[1] = fitNode(c, result.typ, result.sons[1])
of convNotNeedeed: of convNotNeedeed:
message(n.info, hintConvFromXtoItselfNotNeeded, result.typ.typeToString) message(n.info, hintConvFromXtoItselfNotNeeded, result.typ.typeToString)
of convNotLegal: of convNotLegal:
@ -340,21 +347,20 @@ proc semIs(c: PContext, n: PNode): PNode =
result = n result = n
n.typ = getSysType(tyBool) n.typ = getSysType(tyBool)
n.sons[1] = semExprWithType(c, n[1], {efDetermineType}) n.sons[1] = semExprWithType(c, n[1], {efDetermineType, efWantIterator})
if n[2].kind notin {nkStrLit..nkTripleStrLit}: if n[2].kind notin {nkStrLit..nkTripleStrLit}:
let t2 = semTypeNode(c, n[2], nil) let t2 = semTypeNode(c, n[2], nil)
n.sons[2] = newNodeIT(nkType, n[2].info, t2) n.sons[2] = newNodeIT(nkType, n[2].info, t2)
if n[1].typ.kind != tyTypeDesc: let lhsType = n[1].typ
n.sons[1] = makeTypeSymNode(c, n[1].typ, n[1].info) if lhsType.kind != tyTypeDesc:
elif n[1].typ.sonsLen == 0: n.sons[1] = makeTypeSymNode(c, lhsType, n[1].info)
elif lhsType.base.kind == tyNone:
# this is a typedesc variable, leave for evals # this is a typedesc variable, leave for evals
return return
let t1 = n[1].typ.sons[0]
# BUGFIX: don't evaluate this too early: ``T is void`` # BUGFIX: don't evaluate this too early: ``T is void``
if not containsGenericType(t1): result = isOpImpl(c, n) if not n[1].typ.base.containsGenericType: result = isOpImpl(c, n)
proc semOpAux(c: PContext, n: PNode) = proc semOpAux(c: PContext, n: PNode) =
const flags = {efDetermineType} const flags = {efDetermineType}
@ -635,9 +641,11 @@ proc evalAtCompileTime(c: PContext, n: PNode): PNode =
result = evalStaticExpr(c.module, call, c.p.owner) result = evalStaticExpr(c.module, call, c.p.owner)
if result.isNil: if result.isNil:
localError(n.info, errCannotInterpretNodeX, renderTree(call)) localError(n.info, errCannotInterpretNodeX, renderTree(call))
else: result = fixupTypeAfterEval(c, result, n)
else: else:
result = evalConstExpr(c.module, call) result = evalConstExpr(c.module, call)
if result.isNil: result = n if result.isNil: result = n
else: result = fixupTypeAfterEval(c, result, n)
#if result != n: #if result != n:
# echo "SUCCESS evaluated at compile time: ", call.renderTree # echo "SUCCESS evaluated at compile time: ", call.renderTree
@ -647,6 +655,8 @@ proc semStaticExpr(c: PContext, n: PNode): PNode =
if result.isNil: if result.isNil:
localError(n.info, errCannotInterpretNodeX, renderTree(n)) localError(n.info, errCannotInterpretNodeX, renderTree(n))
result = emptyNode result = emptyNode
else:
result = fixupTypeAfterEval(c, result, a)
proc semOverloadedCallAnalyseEffects(c: PContext, n: PNode, nOrig: PNode, proc semOverloadedCallAnalyseEffects(c: PContext, n: PNode, nOrig: PNode,
flags: TExprFlags): PNode = flags: TExprFlags): PNode =
@ -687,6 +697,7 @@ proc semIndirectOp(c: PContext, n: PNode, flags: TExprFlags): PNode =
# it is a static call! # it is a static call!
result = n.sons[0] result = n.sons[0]
result.kind = nkCall result.kind = nkCall
result.flags.incl nfExplicitCall
for i in countup(1, sonsLen(n) - 1): addSon(result, n.sons[i]) for i in countup(1, sonsLen(n) - 1): addSon(result, n.sons[i])
return semExpr(c, result, flags) return semExpr(c, result, flags)
else: else:
@ -910,8 +921,8 @@ proc builtinFieldAccess(c: PContext, n: PNode, flags: TExprFlags): PNode =
var ty = n.sons[0].typ var ty = n.sons[0].typ
var f: PSym = nil var f: PSym = nil
result = nil result = nil
if isTypeExpr(n.sons[0]) or ty.kind == tyTypeDesc and ty.len == 1: if isTypeExpr(n.sons[0]) or ty.kind == tyTypeDesc and ty.base.kind != tyNone:
if ty.kind == tyTypeDesc: ty = ty.sons[0] if ty.kind == tyTypeDesc: ty = ty.base
case ty.kind case ty.kind
of tyEnum: of tyEnum:
# look up if the identifier belongs to the enum: # look up if the identifier belongs to the enum:
@ -992,7 +1003,7 @@ proc dotTransformation(c: PContext, n: PNode): PNode =
else: else:
var i = considerAcc(n.sons[1]) var i = considerAcc(n.sons[1])
result = newNodeI(nkDotCall, n.info) result = newNodeI(nkDotCall, n.info)
result.flags.incl nfDelegate result.flags.incl nfDotField
addSon(result, newIdentNode(i, n[1].info)) addSon(result, newIdentNode(i, n[1].info))
addSon(result, copyTree(n[0])) addSon(result, copyTree(n[0]))
@ -1075,12 +1086,13 @@ proc semArrayAccess(c: PContext, n: PNode, flags: TExprFlags): PNode =
proc propertyWriteAccess(c: PContext, n, nOrig, a: PNode): PNode = proc propertyWriteAccess(c: PContext, n, nOrig, a: PNode): PNode =
var id = considerAcc(a[1]) var id = considerAcc(a[1])
let setterId = newIdentNode(getIdent(id.s & '='), n.info) var setterId = newIdentNode(getIdent(id.s & '='), n.info)
# a[0] is already checked for semantics, that does ``builtinFieldAccess`` # a[0] is already checked for semantics, that does ``builtinFieldAccess``
# this is ugly. XXX Semantic checking should use the ``nfSem`` flag for # this is ugly. XXX Semantic checking should use the ``nfSem`` flag for
# nodes? # nodes?
let aOrig = nOrig[0] let aOrig = nOrig[0]
result = newNode(nkCall, n.info, sons = @[setterId, a[0], semExpr(c, n[1])]) result = newNode(nkCall, n.info, sons = @[setterId, a[0], semExpr(c, n[1])])
result.flags.incl nfDotSetter
let orig = newNode(nkCall, n.info, sons = @[setterId, aOrig[0], nOrig[1]]) let orig = newNode(nkCall, n.info, sons = @[setterId, aOrig[0], nOrig[1]])
result = semOverloadedCallAnalyseEffects(c, result, orig, {}) result = semOverloadedCallAnalyseEffects(c, result, orig, {})
@ -1770,22 +1782,6 @@ proc semBlock(c: PContext, n: PNode): PNode =
closeScope(c) closeScope(c)
dec(c.p.nestedBlockCounter) dec(c.p.nestedBlockCounter)
proc buildCall(n: PNode): PNode =
if n.kind == nkDotExpr and n.len == 2:
# x.y --> y(x)
result = newNodeI(nkCall, n.info, 2)
result.sons[0] = n.sons[1]
result.sons[1] = n.sons[0]
elif n.kind in nkCallKinds and n.sons[0].kind == nkDotExpr:
# x.y(a) -> y(x, a)
let a = n.sons[0]
result = newNodeI(nkCall, n.info, n.len+1)
result.sons[0] = a.sons[1]
result.sons[1] = a.sons[0]
for i in 1 .. <n.len: result.sons[i+1] = n.sons[i]
else:
result = n
proc doBlockIsStmtList(n: PNode): bool = proc doBlockIsStmtList(n: PNode): bool =
result = n.kind == nkDo and result = n.kind == nkDo and
n[paramsPos].sonsLen == 1 and n[paramsPos].sonsLen == 1 and
@ -1819,6 +1815,10 @@ proc semExport(c: PContext, n: PNode): PNode =
c.module.ast.add x c.module.ast.add x
result = n result = n
proc setGenericParams(c: PContext, n: PNode) =
for i in 1 .. <n.len:
n[i].typ = semTypeNode(c, n[i], nil)
proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode = proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode =
result = n result = n
if gCmd == cmdIdeTools: suggestExpr(c, n) if gCmd == cmdIdeTools: suggestExpr(c, n)
@ -1833,8 +1833,8 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode =
result = symChoice(c, n, s, scClosed) result = symChoice(c, n, s, scClosed)
if result.kind == nkSym: if result.kind == nkSym:
markIndirect(c, result.sym) markIndirect(c, result.sym)
if isGenericRoutine(result.sym): # if isGenericRoutine(result.sym):
localError(n.info, errInstantiateXExplicitely, s.name.s) # localError(n.info, errInstantiateXExplicitely, s.name.s)
of nkSym: of nkSym:
# because of the changed symbol binding, this does not mean that we # because of the changed symbol binding, this does not mean that we
# don't have to check the symbol for semantics here again! # don't have to check the symbol for semantics here again!
@ -1890,7 +1890,7 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode =
of nkCall, nkInfix, nkPrefix, nkPostfix, nkCommand, nkCallStrLit: of nkCall, nkInfix, nkPrefix, nkPostfix, nkCommand, nkCallStrLit:
# check if it is an expression macro: # check if it is an expression macro:
checkMinSonsLen(n, 1) checkMinSonsLen(n, 1)
let mode = if nfDelegate in n.flags: {} else: {checkUndeclared} let mode = if nfDotField in n.flags: {} else: {checkUndeclared}
var s = qualifiedLookUp(c, n.sons[0], mode) var s = qualifiedLookUp(c, n.sons[0], mode)
if s != nil: if s != nil:
if gCmd == cmdPretty and n.sons[0].kind == nkDotExpr: if gCmd == cmdPretty and n.sons[0].kind == nkDotExpr:
@ -1924,10 +1924,12 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode =
else: else:
#liMessage(n.info, warnUser, renderTree(n)); #liMessage(n.info, warnUser, renderTree(n));
result = semIndirectOp(c, n, flags) result = semIndirectOp(c, n, flags)
elif isSymChoice(n.sons[0]) or n[0].kind == nkBracketExpr and elif n[0].kind == nkBracketExpr and isSymChoice(n[0][0]):
isSymChoice(n[0][0]): # indirectOp can deal with explicit instantiations; the fixes
# the 'newSeq[T](x)' bug
setGenericParams(c, n.sons[0])
result = semDirectOp(c, n, flags) result = semDirectOp(c, n, flags)
elif nfDelegate in n.flags: elif isSymChoice(n.sons[0]) or nfDotField in n.flags:
result = semDirectOp(c, n, flags) result = semDirectOp(c, n, flags)
else: else:
result = semIndirectOp(c, n, flags) result = semIndirectOp(c, n, flags)

View file

@ -537,7 +537,7 @@ proc foldArrayAccess(m: PSym, n: PNode): PNode =
if result.kind == nkExprColonExpr: result = result.sons[1] if result.kind == nkExprColonExpr: result = result.sons[1]
else: else:
localError(n.info, errIndexOutOfBounds) localError(n.info, errIndexOutOfBounds)
of nkBracket, nkMetaNode: of nkBracket:
if (idx >= 0) and (idx < sonsLen(x)): result = x.sons[int(idx)] if (idx >= 0) and (idx < sonsLen(x)): result = x.sons[int(idx)]
else: localError(n.info, errIndexOutOfBounds) else: localError(n.info, errIndexOutOfBounds)
of nkStrLit..nkTripleStrLit: of nkStrLit..nkTripleStrLit:

View file

@ -1,7 +1,7 @@
# #
# #
# The Nimrod Compiler # The Nimrod Compiler
# (c) Copyright 2012 Andreas Rumpf # (c) Copyright 2014 Andreas Rumpf
# #
# See the file "copying.txt", included in this # See the file "copying.txt", included in this
# distribution, for details about the copyright. # distribution, for details about the copyright.
@ -215,8 +215,7 @@ proc semGenericStmt(c: PContext, n: PNode,
if (a.kind != nkIdentDefs) and (a.kind != nkVarTuple): illFormedAst(a) if (a.kind != nkIdentDefs) and (a.kind != nkVarTuple): illFormedAst(a)
checkMinSonsLen(a, 3) checkMinSonsLen(a, 3)
var L = sonsLen(a) var L = sonsLen(a)
a.sons[L-2] = semGenericStmt(c, a.sons[L-2], flags+{withinTypeDesc}, a.sons[L-2] = semGenericStmt(c, a.sons[L-2], flags+{withinTypeDesc}, ctx)
ctx)
a.sons[L-1] = semGenericStmt(c, a.sons[L-1], flags, ctx) a.sons[L-1] = semGenericStmt(c, a.sons[L-1], flags, ctx)
for j in countup(0, L-3): for j in countup(0, L-3):
addPrelimDecl(c, newSymS(skUnknown, getIdentNode(a.sons[j]), c)) addPrelimDecl(c, newSymS(skUnknown, getIdentNode(a.sons[j]), c))
@ -226,8 +225,7 @@ proc semGenericStmt(c: PContext, n: PNode,
if (a.kind != nkIdentDefs): illFormedAst(a) if (a.kind != nkIdentDefs): illFormedAst(a)
checkMinSonsLen(a, 3) checkMinSonsLen(a, 3)
var L = sonsLen(a) var L = sonsLen(a)
a.sons[L-2] = semGenericStmt(c, a.sons[L-2], flags+{withinTypeDesc}, a.sons[L-2] = semGenericStmt(c, a.sons[L-2], flags+{withinTypeDesc}, ctx)
ctx)
# do not perform symbol lookup for default expressions # do not perform symbol lookup for default expressions
for j in countup(0, L-3): for j in countup(0, L-3):
addPrelimDecl(c, newSymS(skUnknown, getIdentNode(a.sons[j]), c)) addPrelimDecl(c, newSymS(skUnknown, getIdentNode(a.sons[j]), c))
@ -281,8 +279,7 @@ proc semGenericStmt(c: PContext, n: PNode,
if (a.kind != nkIdentDefs): illFormedAst(a) if (a.kind != nkIdentDefs): illFormedAst(a)
checkMinSonsLen(a, 3) checkMinSonsLen(a, 3)
var L = sonsLen(a) var L = sonsLen(a)
a.sons[L-2] = semGenericStmt(c, a.sons[L-2], flags+{withinTypeDesc}, a.sons[L-2] = semGenericStmt(c, a.sons[L-2], flags+{withinTypeDesc}, ctx)
ctx)
a.sons[L-1] = semGenericStmt(c, a.sons[L-1], flags, ctx) a.sons[L-1] = semGenericStmt(c, a.sons[L-1], flags, ctx)
for j in countup(0, L-3): for j in countup(0, L-3):
addPrelimDecl(c, newSymS(skUnknown, getIdentNode(a.sons[j]), c)) addPrelimDecl(c, newSymS(skUnknown, getIdentNode(a.sons[j]), c))

View file

@ -84,10 +84,10 @@ proc initVar(a: PEffects, n: PNode) =
proc initVarViaNew(a: PEffects, n: PNode) = proc initVarViaNew(a: PEffects, n: PNode) =
if n.kind != nkSym: return if n.kind != nkSym: return
let s = n.sym let s = n.sym
if {tfNeedsInit, tfNotNil} * s.typ.flags == {tfNotNil}: if {tfNeedsInit, tfNotNil} * s.typ.flags <= {tfNotNil}:
# 'x' is not nil, but that doesn't mean it's not nil children # 'x' is not nil, but that doesn't mean it's not nil children
# are initialized: # are initialized:
initVarViaNew(a, n) initVar(a, n)
proc useVar(a: PEffects, n: PNode) = proc useVar(a: PEffects, n: PNode) =
let s = n.sym let s = n.sym
@ -466,8 +466,7 @@ proc track(tracked: PEffects, n: PNode) =
mergeEffects(tracked, effectList.sons[exceptionEffects], n) mergeEffects(tracked, effectList.sons[exceptionEffects], n)
mergeTags(tracked, effectList.sons[tagEffects], n) mergeTags(tracked, effectList.sons[tagEffects], n)
for i in 1 .. <len(n): trackOperand(tracked, n.sons[i], paramType(op, i)) for i in 1 .. <len(n): trackOperand(tracked, n.sons[i], paramType(op, i))
if a.kind == nkSym and a.sym.magic in {mNew, mNewFinalize, if a.kind == nkSym and a.sym.magic in {mNew, mNewFinalize, mNewSeq}:
mNewSeq, mShallowCopy}:
# may not look like an assignment, but it is: # may not look like an assignment, but it is:
initVarViaNew(tracked, n.sons[1]) initVarViaNew(tracked, n.sons[1])
for i in 0 .. <safeLen(n): for i in 0 .. <safeLen(n):
@ -477,7 +476,6 @@ proc track(tracked: PEffects, n: PNode) =
if warnProveField in gNotes: checkFieldAccess(tracked.guards, n) if warnProveField in gNotes: checkFieldAccess(tracked.guards, n)
of nkTryStmt: trackTryStmt(tracked, n) of nkTryStmt: trackTryStmt(tracked, n)
of nkPragma: trackPragmaStmt(tracked, n) of nkPragma: trackPragmaStmt(tracked, n)
of nkMacroDef, nkTemplateDef: discard
of nkAsgn, nkFastAsgn: of nkAsgn, nkFastAsgn:
track(tracked, n.sons[1]) track(tracked, n.sons[1])
initVar(tracked, n.sons[0]) initVar(tracked, n.sons[0])
@ -527,7 +525,9 @@ proc track(tracked: PEffects, n: PNode) =
if sfDiscriminant in x.sons[0].sym.flags: if sfDiscriminant in x.sons[0].sym.flags:
addDiscriminantFact(tracked.guards, x) addDiscriminantFact(tracked.guards, x)
setLen(tracked.guards, oldFacts) setLen(tracked.guards, oldFacts)
of nkTypeSection: discard of nkTypeSection, nkProcDef, nkConverterDef, nkMethodDef, nkIteratorDef,
nkMacroDef, nkTemplateDef:
discard
else: else:
for i in 0 .. <safeLen(n): track(tracked, n.sons[i]) for i in 0 .. <safeLen(n): track(tracked, n.sons[i])
@ -581,22 +581,26 @@ proc setEffectsForProcType*(t: PType, n: PNode) =
if not isNil(tagsSpec): if not isNil(tagsSpec):
effects.sons[tagEffects] = tagsSpec effects.sons[tagEffects] = tagsSpec
proc initEffects(effects: PNode; s: PSym; t: var TEffects) =
newSeq(effects.sons, effectListLen)
effects.sons[exceptionEffects] = newNodeI(nkArgList, s.info)
effects.sons[tagEffects] = newNodeI(nkArgList, s.info)
t.exc = effects.sons[exceptionEffects]
t.tags = effects.sons[tagEffects]
t.owner = s
t.init = @[]
t.guards = @[]
proc trackProc*(s: PSym, body: PNode) = proc trackProc*(s: PSym, body: PNode) =
var effects = s.typ.n.sons[0] var effects = s.typ.n.sons[0]
internalAssert effects.kind == nkEffectList internalAssert effects.kind == nkEffectList
# effects already computed? # effects already computed?
if sfForward in s.flags: return if sfForward in s.flags: return
if effects.len == effectListLen: return if effects.len == effectListLen: return
newSeq(effects.sons, effectListLen)
effects.sons[exceptionEffects] = newNodeI(nkArgList, body.info)
effects.sons[tagEffects] = newNodeI(nkArgList, body.info)
var t: TEffects var t: TEffects
t.exc = effects.sons[exceptionEffects] initEffects(effects, s, t)
t.tags = effects.sons[tagEffects]
t.owner = s
t.init = @[]
t.guards = @[]
track(t, body) track(t, body)
if not isEmptyType(s.typ.sons[0]) and tfNeedsInit in s.typ.sons[0].flags and if not isEmptyType(s.typ.sons[0]) and tfNeedsInit in s.typ.sons[0].flags and
@ -619,3 +623,12 @@ proc trackProc*(s: PSym, body: PNode) =
# after the check, use the formal spec: # after the check, use the formal spec:
effects.sons[tagEffects] = tagsSpec effects.sons[tagEffects] = tagsSpec
proc trackTopLevelStmt*(module: PSym; n: PNode) =
if n.kind in {nkPragma, nkMacroDef, nkTemplateDef, nkProcDef,
nkTypeSection, nkConverterDef, nkMethodDef, nkIteratorDef}:
return
var effects = newNode(nkEffectList, n.info)
var t: TEffects
initEffects(effects, module, t)
track(t, n)

View file

@ -126,7 +126,7 @@ proc implicitlyDiscardable(n: PNode): bool =
proc fixNilType(n: PNode) = proc fixNilType(n: PNode) =
if isAtom(n): if isAtom(n):
if n.kind != nkNilLit and n.typ != nil: if n.kind != nkNilLit and n.typ != nil:
localError(n.info, errDiscardValue) localError(n.info, errDiscardValueX, n.typ.typeToString)
elif n.kind in {nkStmtList, nkStmtListExpr}: elif n.kind in {nkStmtList, nkStmtListExpr}:
n.kind = nkStmtList n.kind = nkStmtList
for it in n: fixNilType(it) for it in n: fixNilType(it)
@ -143,7 +143,8 @@ proc discardCheck(c: PContext, result: PNode) =
while n.kind in skipForDiscardable: while n.kind in skipForDiscardable:
n = n.lastSon n = n.lastSon
n.typ = nil n.typ = nil
elif c.inTypeClass > 0 and result.typ.kind == tyBool: elif c.inTypeClass > 0:
if result.typ.kind == tyBool:
let verdict = semConstExpr(c, result) let verdict = semConstExpr(c, result)
if verdict.intVal == 0: if verdict.intVal == 0:
localError(result.info, "type class predicate failed") localError(result.info, "type class predicate failed")
@ -154,7 +155,7 @@ proc discardCheck(c: PContext, result: PNode) =
else: else:
var n = result var n = result
while n.kind in skipForDiscardable: n = n.lastSon while n.kind in skipForDiscardable: n = n.lastSon
localError(n.info, errDiscardValue) localError(n.info, errDiscardValueX, result.typ.typeToString)
proc semIf(c: PContext, n: PNode): PNode = proc semIf(c: PContext, n: PNode): PNode =
result = n result = n
@ -331,6 +332,7 @@ proc checkNilable(v: PSym) =
proc semVarOrLet(c: PContext, n: PNode, symkind: TSymKind): PNode = proc semVarOrLet(c: PContext, n: PNode, symkind: TSymKind): PNode =
var b: PNode var b: PNode
result = copyNode(n) result = copyNode(n)
var hasCompileTime = false
for i in countup(0, sonsLen(n)-1): for i in countup(0, sonsLen(n)-1):
var a = n.sons[i] var a = n.sons[i]
if gCmd == cmdIdeTools: suggestStmt(c, a) if gCmd == cmdIdeTools: suggestStmt(c, a)
@ -349,7 +351,12 @@ proc semVarOrLet(c: PContext, n: PNode, symkind: TSymKind): PNode =
# BUGFIX: ``fitNode`` is needed here! # BUGFIX: ``fitNode`` is needed here!
# check type compability between def.typ and typ: # check type compability between def.typ and typ:
if typ != nil: def = fitNode(c, typ, def) if typ != nil: def = fitNode(c, typ, def)
else: typ = skipIntLit(def.typ) else:
typ = skipIntLit(def.typ)
if typ.kind in {tySequence, tyArray, tySet} and
typ.lastSon.kind == tyEmpty:
localError(def.info, errCannotInferTypeOfTheLiteral,
($typ.kind).substr(2).toLower)
else: else:
def = ast.emptyNode def = ast.emptyNode
if symkind == skLet: localError(a.info, errLetNeedsInit) if symkind == skLet: localError(a.info, errLetNeedsInit)
@ -405,6 +412,8 @@ proc semVarOrLet(c: PContext, n: PNode, symkind: TSymKind): PNode =
v.typ = tup.sons[j] v.typ = tup.sons[j]
b.sons[j] = newSymNode(v) b.sons[j] = newSymNode(v)
checkNilable(v) checkNilable(v)
if sfCompileTime in v.flags: hasCompileTime = true
if hasCompileTime: vm.setupCompileTimeVar(c.module, result)
proc semConst(c: PContext, n: PNode): PNode = proc semConst(c: PContext, n: PNode): PNode =
result = copyNode(n) result = copyNode(n)
@ -764,6 +773,29 @@ proc typeSectionRightSidePass(c: PContext, n: PNode) =
s.ast = a s.ast = a
popOwner() popOwner()
proc checkForMetaFields(n: PNode) =
template checkMeta(t) =
if t != nil and t.isMetaType and tfGenericTypeParam notin t.flags:
localError(n.info, errTIsNotAConcreteType, t.typeToString)
case n.kind
of nkRecList, nkRecCase:
for s in n: checkForMetaFields(s)
of nkOfBranch, nkElse:
checkForMetaFields(n.lastSon)
of nkSym:
let t = n.sym.typ
case t.kind
of tySequence, tySet, tyArray, tyOpenArray, tyVar, tyPtr, tyRef,
tyProc, tyGenericInvokation, tyGenericInst:
let start = ord(t.kind in {tyGenericInvokation, tyGenericInst})
for i in start .. <t.sons.len:
checkMeta(t.sons[i])
else:
checkMeta(t)
else:
internalAssert false
proc typeSectionFinalPass(c: PContext, n: PNode) = proc typeSectionFinalPass(c: PContext, n: PNode) =
for i in countup(0, sonsLen(n) - 1): for i in countup(0, sonsLen(n) - 1):
var a = n.sons[i] var a = n.sons[i]
@ -780,6 +812,8 @@ proc typeSectionFinalPass(c: PContext, n: PNode) =
assignType(s.typ, t) assignType(s.typ, t)
s.typ.id = t.id # same id s.typ.id = t.id # same id
checkConstructedType(s.info, s.typ) checkConstructedType(s.info, s.typ)
if s.typ.kind in {tyObject, tyTuple}:
checkForMetaFields(s.typ.n)
let aa = a.sons[2] let aa = a.sons[2]
if aa.kind in {nkRefTy, nkPtrTy} and aa.len == 1 and if aa.kind in {nkRefTy, nkPtrTy} and aa.len == 1 and
aa.sons[0].kind == nkObjectTy: aa.sons[0].kind == nkObjectTy:
@ -883,12 +917,19 @@ proc semLambda(c: PContext, n: PNode, flags: TExprFlags): PNode =
s = n[namePos].sym s = n[namePos].sym
pushOwner(s) pushOwner(s)
openScope(c) openScope(c)
var gp: PNode
if n.sons[genericParamsPos].kind != nkEmpty: if n.sons[genericParamsPos].kind != nkEmpty:
illFormedAst(n) # process parameters: n.sons[genericParamsPos] = semGenericParamList(c, n.sons[genericParamsPos])
gp = n.sons[genericParamsPos]
else:
gp = newNodeI(nkGenericParams, n.info)
if n.sons[paramsPos].kind != nkEmpty: if n.sons[paramsPos].kind != nkEmpty:
var gp = newNodeI(nkGenericParams, n.info)
semParamList(c, n.sons[paramsPos], gp, s) semParamList(c, n.sons[paramsPos], gp, s)
paramsTypeCheck(c, s.typ) # paramsTypeCheck(c, s.typ)
if sonsLen(gp) > 0 and n.sons[genericParamsPos].kind == nkEmpty:
# we have a list of implicit type parameters:
n.sons[genericParamsPos] = gp
else: else:
s.typ = newTypeS(tyProc, c) s.typ = newTypeS(tyProc, c)
rawAddSon(s.typ, nil) rawAddSon(s.typ, nil)
@ -900,6 +941,7 @@ proc semLambda(c: PContext, n: PNode, flags: TExprFlags): PNode =
localError(n.sons[bodyPos].info, errImplOfXNotAllowed, s.name.s) localError(n.sons[bodyPos].info, errImplOfXNotAllowed, s.name.s)
#if efDetermineType notin flags: #if efDetermineType notin flags:
# XXX not good enough; see tnamedparamanonproc.nim # XXX not good enough; see tnamedparamanonproc.nim
if n.sons[genericParamsPos].kind == nkEmpty:
pushProcCon(c, s) pushProcCon(c, s)
addResult(c, s.typ.sons[0], n.info, skProc) addResult(c, s.typ.sons[0], n.info, skProc)
let semBody = hloBody(c, semProcBody(c, n.sons[bodyPos])) let semBody = hloBody(c, semProcBody(c, n.sons[bodyPos]))
@ -913,6 +955,34 @@ proc semLambda(c: PContext, n: PNode, flags: TExprFlags): PNode =
popOwner() popOwner()
result.typ = s.typ result.typ = s.typ
proc semInferredLambda(c: PContext, pt: TIdTable, n: PNode): PNode =
var n = n
n = replaceTypesInBody(c, pt, n)
result = n
n.sons[genericParamsPos] = emptyNode
n.sons[paramsPos] = n.typ.n
openScope(c)
var s = n.sons[namePos].sym
addParams(c, n.typ.n, skProc)
pushProcCon(c, s)
addResult(c, n.typ.sons[0], n.info, skProc)
let semBody = hloBody(c, semProcBody(c, n.sons[bodyPos]))
n.sons[bodyPos] = transformBody(c.module, semBody, n.sons[namePos].sym)
addResultNode(c, n)
popProcCon(c)
closeScope(c)
s.ast = result
# alternative variant (not quite working):
# var prc = arg[0].sym
# let inferred = c.semGenerateInstance(c, prc, m.bindings, arg.info)
# result = inferred.ast
# result.kind = arg.kind
proc activate(c: PContext, n: PNode) = proc activate(c: PContext, n: PNode) =
# XXX: This proc is part of my plan for getting rid of # XXX: This proc is part of my plan for getting rid of
# forward declarations. stay tuned. # forward declarations. stay tuned.
@ -1250,7 +1320,7 @@ proc semStmtList(c: PContext, n: PNode, flags: TExprFlags): PNode =
if n.sons[i].typ == enforceVoidContext or usesResult(n.sons[i]): if n.sons[i].typ == enforceVoidContext or usesResult(n.sons[i]):
voidContext = true voidContext = true
n.typ = enforceVoidContext n.typ = enforceVoidContext
if i == last and efWantValue in flags: if i == last and (length == 1 or efWantValue in flags):
n.typ = n.sons[i].typ n.typ = n.sons[i].typ
if not isEmptyType(n.typ): n.kind = nkStmtListExpr if not isEmptyType(n.typ): n.kind = nkStmtListExpr
elif i != last or voidContext or c.inTypeClass > 0: elif i != last or voidContext or c.inTypeClass > 0:
@ -1263,8 +1333,8 @@ proc semStmtList(c: PContext, n: PNode, flags: TExprFlags): PNode =
let (outer, inner) = insertDestructors(c, n.sons[i]) let (outer, inner) = insertDestructors(c, n.sons[i])
if outer != nil: if outer != nil:
n.sons[i] = outer n.sons[i] = outer
for j in countup(i+1, length-1): var rest = newNode(nkStmtList, n.info, n.sons[i+1 .. length-1])
inner.addSon(semStmt(c, n.sons[j])) inner.addSon(semStmtList(c, rest, flags))
n.sons.setLen(i+1) n.sons.setLen(i+1)
return return
of LastBlockStmts: of LastBlockStmts:

View file

@ -358,7 +358,7 @@ proc analyse(c: PProcCtx, n: PNode): TThreadOwner =
of nkConstSection: result = analyseConstSection(c, n) of nkConstSection: result = analyseConstSection(c, n)
of nkTypeSection, nkCommentStmt: result = toVoid of nkTypeSection, nkCommentStmt: result = toVoid
of nkIfStmt, nkWhileStmt, nkTryStmt, nkCaseStmt, nkStmtList, nkBlockStmt, of nkIfStmt, nkWhileStmt, nkTryStmt, nkCaseStmt, nkStmtList, nkBlockStmt,
nkElifBranch, nkElse, nkExceptBranch, nkOfBranch: nkElifBranch, nkElse, nkExceptBranch, nkOfBranch, nkFinally:
for i in 0 .. <n.len: discard analyse(c, n[i]) for i in 0 .. <n.len: discard analyse(c, n[i])
result = toVoid result = toVoid
of nkBreakStmt, nkContinueStmt: result = toVoid of nkBreakStmt, nkContinueStmt: result = toVoid

View file

@ -653,6 +653,10 @@ proc liftParamType(c: PContext, procKind: TSymKind, genericParams: PNode,
var paramTypId = if not anon and paramType.sym != nil: paramType.sym.name var paramTypId = if not anon and paramType.sym != nil: paramType.sym.name
else: nil else: nil
template maybeLift(typ: PType): expr =
let lifted = liftingWalk(typ)
(if lifted != nil: lifted else: typ)
template addImplicitGeneric(e: expr): expr = template addImplicitGeneric(e: expr): expr =
addImplicitGenericImpl(e, paramTypId) addImplicitGenericImpl(e, paramTypId)
@ -663,15 +667,19 @@ proc liftParamType(c: PContext, procKind: TSymKind, genericParams: PNode,
of tyStatic: of tyStatic:
# proc(a: expr{string}, b: expr{nkLambda}) # proc(a: expr{string}, b: expr{nkLambda})
# overload on compile time values and AST trees # overload on compile time values and AST trees
result = addImplicitGeneric(c.newTypeWithSons(tyStatic, paramType.sons)) let base = paramType.base.maybeLift
if base.isMetaType and procKind == skMacro:
localError(info, errMacroBodyDependsOnGenericTypes, paramName)
result = addImplicitGeneric(c.newTypeWithSons(tyStatic, @[base]))
result.flags.incl tfHasStatic result.flags.incl tfHasStatic
of tyTypeDesc: of tyTypeDesc:
if tfUnresolved notin paramType.flags: if tfUnresolved notin paramType.flags:
# naked typedescs are not bindOnce types # naked typedescs are not bindOnce types
if paramType.sonsLen == 0 and paramTypId != nil and if paramType.base.kind == tyNone and paramTypId != nil and
paramTypId.id == typedescId.id: paramTypId = nil paramTypId.id == typedescId.id: paramTypId = nil
result = addImplicitGeneric(c.newTypeWithSons(tyTypeDesc, paramType.sons)) result = addImplicitGeneric(
c.newTypeWithSons(tyTypeDesc, @[paramType.base]))
of tyDistinct: of tyDistinct:
if paramType.sonsLen == 1: if paramType.sonsLen == 1:
@ -705,7 +713,6 @@ proc liftParamType(c: PContext, procKind: TSymKind, genericParams: PNode,
# result.rawAddSon(copyType(paramType.sons[i], getCurrOwner(), true)) # result.rawAddSon(copyType(paramType.sons[i], getCurrOwner(), true))
result = instGenericContainer(c, paramType.sym.info, result, result = instGenericContainer(c, paramType.sym.info, result,
allowMetaTypes = true) allowMetaTypes = true)
result.lastSon.shouldHaveMeta
result = newTypeWithSons(c, tyCompositeTypeClass, @[paramType, result]) result = newTypeWithSons(c, tyCompositeTypeClass, @[paramType, result])
result = addImplicitGeneric(result) result = addImplicitGeneric(result)
@ -1011,7 +1018,8 @@ proc semTypeNode(c: PContext, n: PNode, prev: PType): PType =
of mOrdinal: result = semOrdinal(c, n, prev) of mOrdinal: result = semOrdinal(c, n, prev)
of mSeq: result = semContainer(c, n, tySequence, "seq", prev) of mSeq: result = semContainer(c, n, tySequence, "seq", prev)
of mVarargs: result = semVarargs(c, n, prev) of mVarargs: result = semVarargs(c, n, prev)
of mExpr, mTypeDesc: of mTypeDesc: result = makeTypeDesc(c, semTypeNode(c, n[1], nil))
of mExpr:
result = semTypeNode(c, n.sons[0], nil) result = semTypeNode(c, n.sons[0], nil)
if result != nil: if result != nil:
result = copyType(result, getCurrOwner(), false) result = copyType(result, getCurrOwner(), false)
@ -1030,9 +1038,8 @@ proc semTypeNode(c: PContext, n: PNode, prev: PType): PType =
if s.kind != skError: localError(n.info, errTypeExpected) if s.kind != skError: localError(n.info, errTypeExpected)
result = newOrPrevType(tyError, prev, c) result = newOrPrevType(tyError, prev, c)
elif s.kind == skParam and s.typ.kind == tyTypeDesc: elif s.kind == skParam and s.typ.kind == tyTypeDesc:
assert s.typ.len > 0 internalAssert s.typ.base.kind != tyNone and prev == nil
internalAssert prev == nil result = s.typ.base
result = s.typ.sons[0]
elif prev == nil: elif prev == nil:
result = s.typ result = s.typ
else: else:

View file

@ -29,6 +29,7 @@ proc checkConstructedType*(info: TLineInfo, typ: PType) =
localError(info, errVarVarTypeNotAllowed) localError(info, errVarVarTypeNotAllowed)
elif computeSize(t) == szIllegalRecursion: elif computeSize(t) == szIllegalRecursion:
localError(info, errIllegalRecursionInTypeX, typeToString(t)) localError(info, errIllegalRecursionInTypeX, typeToString(t))
when false: when false:
if t.kind == tyObject and t.sons[0] != nil: if t.kind == tyObject and t.sons[0] != nil:
if t.sons[0].kind != tyObject or tfFinal in t.sons[0].flags: if t.sons[0].kind != tyObject or tfFinal in t.sons[0].flags:
@ -153,6 +154,9 @@ proc replaceTypeVarsN(cl: var TReplTypeVars, n: PNode): PNode =
discard discard
of nkSym: of nkSym:
result.sym = replaceTypeVarsS(cl, n.sym) result.sym = replaceTypeVarsS(cl, n.sym)
if result.sym.typ.kind == tyEmpty:
# don't add the 'void' field
result = newNode(nkRecList, n.info)
of nkRecWhen: of nkRecWhen:
var branch: PNode = nil # the branch to take var branch: PNode = nil # the branch to take
for i in countup(0, sonsLen(n) - 1): for i in countup(0, sonsLen(n) - 1):
@ -216,7 +220,7 @@ proc handleGenericInvokation(cl: var TReplTypeVars, t: PType): PType =
# is difficult to handle: # is difficult to handle:
var body = t.sons[0] var body = t.sons[0]
if body.kind != tyGenericBody: internalError(cl.info, "no generic body") if body.kind != tyGenericBody: internalError(cl.info, "no generic body")
var header: PType = nil var header: PType = t
# search for some instantiation here: # search for some instantiation here:
if cl.allowMetaTypes: if cl.allowMetaTypes:
result = PType(idTableGet(cl.localCache, t)) result = PType(idTableGet(cl.localCache, t))
@ -228,11 +232,13 @@ proc handleGenericInvokation(cl: var TReplTypeVars, t: PType): PType =
if x.kind == tyGenericParam: if x.kind == tyGenericParam:
x = lookupTypeVar(cl, x) x = lookupTypeVar(cl, x)
if x != nil: if x != nil:
if header == nil: header = instCopyType(cl, t) if header == t: header = instCopyType(cl, t)
header.sons[i] = x header.sons[i] = x
propagateToOwner(header, x) propagateToOwner(header, x)
else:
propagateToOwner(header, x)
if header != nil: if header != t:
# search again after first pass: # search again after first pass:
result = searchInstTypes(header) result = searchInstTypes(header)
if result != nil: return if result != nil: return
@ -240,6 +246,7 @@ proc handleGenericInvokation(cl: var TReplTypeVars, t: PType): PType =
header = instCopyType(cl, t) header = instCopyType(cl, t)
result = newType(tyGenericInst, t.sons[0].owner) result = newType(tyGenericInst, t.sons[0].owner)
result.flags = header.flags
# be careful not to propagate unnecessary flags here (don't use rawAddSon) # be careful not to propagate unnecessary flags here (don't use rawAddSon)
result.sons = @[header.sons[0]] result.sons = @[header.sons[0]]
# ugh need another pass for deeply recursive generic types (e.g. PActor) # ugh need another pass for deeply recursive generic types (e.g. PActor)
@ -406,14 +413,22 @@ proc replaceTypeVarsTAux(cl: var TReplTypeVars, t: PType): PType =
else: discard else: discard
proc initTypeVars(p: PContext, pt: TIdTable, info: TLineInfo): TReplTypeVars =
initIdTable(result.symMap)
copyIdTable(result.typeMap, pt)
initIdTable(result.localCache)
result.info = info
result.c = p
proc replaceTypesInBody*(p: PContext, pt: TIdTable, n: PNode): PNode =
var cl = initTypeVars(p, pt, n.info)
pushInfoContext(n.info)
result = replaceTypeVarsN(cl, n)
popInfoContext()
proc generateTypeInstance*(p: PContext, pt: TIdTable, info: TLineInfo, proc generateTypeInstance*(p: PContext, pt: TIdTable, info: TLineInfo,
t: PType): PType = t: PType): PType =
var cl: TReplTypeVars var cl = initTypeVars(p, pt, info)
initIdTable(cl.symMap)
copyIdTable(cl.typeMap, pt)
initIdTable(cl.localCache)
cl.info = info
cl.c = p
pushInfoContext(info) pushInfoContext(info)
result = replaceTypeVarsT(cl, t) result = replaceTypeVarsT(cl, t)
popInfoContext() popInfoContext()

View file

@ -51,6 +51,8 @@ type
isSubtype, isSubtype,
isSubrange, # subrange of the wanted type; no type conversion isSubrange, # subrange of the wanted type; no type conversion
# but apart from that counts as ``isSubtype`` # but apart from that counts as ``isSubtype``
isInferred, # generic proc was matched against a concrete type
isInferredConvertible, # same as above, but requiring proc CC conversion
isGeneric, isGeneric,
isFromIntLit, # conversion *from* int literal; proven safe isFromIntLit, # conversion *from* int literal; proven safe
isEqual isEqual
@ -105,6 +107,7 @@ proc initCandidate*(ctx: PContext, c: var TCandidate, callee: PSym,
var bound = binding[i].typ var bound = binding[i].typ
if bound != nil and formalTypeParam.kind != tyTypeDesc: if bound != nil and formalTypeParam.kind != tyTypeDesc:
bound = bound.skipTypes({tyTypeDesc}) bound = bound.skipTypes({tyTypeDesc})
assert bound != nil
put(c.bindings, formalTypeParam, bound) put(c.bindings, formalTypeParam, bound)
proc newCandidate*(ctx: PContext, callee: PSym, proc newCandidate*(ctx: PContext, callee: PSym,
@ -337,34 +340,60 @@ proc recordRel(c: var TCandidate, f, a: PType): TTypeRelation =
proc allowsNil(f: PType): TTypeRelation {.inline.} = proc allowsNil(f: PType): TTypeRelation {.inline.} =
result = if tfNotNil notin f.flags: isSubtype else: isNone result = if tfNotNil notin f.flags: isSubtype else: isNone
proc procTypeRel(c: var TCandidate, f, a: PType): TTypeRelation =
proc inconsistentVarTypes(f, a: PType): bool {.inline.} = proc inconsistentVarTypes(f, a: PType): bool {.inline.} =
result = f.kind != a.kind and (f.kind == tyVar or a.kind == tyVar) result = f.kind != a.kind and (f.kind == tyVar or a.kind == tyVar)
proc procParamTypeRel(c: var TCandidate, f, a: PType): TTypeRelation =
var f = f
if a.isMetaType:
if f.isMetaType:
# We are matching a generic proc (as proc param)
# to another generic type appearing in the proc
# signature. There is a change that the target
# type is already fully-determined, so we are
# going to try resolve it
f = generateTypeInstance(c.c, c.bindings, c.call.info, f)
if f == nil or f.isMetaType:
# no luck resolving the type, so the inference fails
return isNone
let reverseRel = typeRel(c, a, f)
if reverseRel == isGeneric:
result = isInferred
inc c.genericMatches
else:
result = typeRel(c, f, a)
if result <= isSubtype or inconsistentVarTypes(f, a):
result = isNone
if result == isEqual:
inc c.exactMatches
proc procTypeRel(c: var TCandidate, f, a: PType): TTypeRelation =
case a.kind case a.kind
of tyProc: of tyProc:
if sonsLen(f) != sonsLen(a): return if sonsLen(f) != sonsLen(a): return
# Note: We have to do unification for the parameters before the
# return type!
result = isEqual # start with maximum; also correct for no result = isEqual # start with maximum; also correct for no
# params at all # params at all
for i in countup(1, sonsLen(f)-1):
var m = typeRel(c, f.sons[i], a.sons[i]) template checkParam(f, a) =
if m <= isSubtype or inconsistentVarTypes(f.sons[i], a.sons[i]): result = minRel(result, procParamTypeRel(c, f, a))
return isNone if result == isNone: return
else: result = minRel(m, result)
# Note: We have to do unification for the parameters before the
# return type!
for i in 1 .. <f.sonsLen:
checkParam(f.sons[i], a.sons[i])
if f.sons[0] != nil: if f.sons[0] != nil:
if a.sons[0] != nil: if a.sons[0] != nil:
var m = typeRel(c, f.sons[0], a.sons[0]) checkParam(f.sons[0], a.sons[0])
# Subtype is sufficient for return types!
if m < isSubtype or inconsistentVarTypes(f.sons[0], a.sons[0]):
return isNone
elif m == isSubtype: result = isConvertible
else: result = minRel(m, result)
else: else:
return isNone return isNone
elif a.sons[0] != nil: elif a.sons[0] != nil:
return isNone return isNone
if tfNoSideEffect in f.flags and tfNoSideEffect notin a.flags: if tfNoSideEffect in f.flags and tfNoSideEffect notin a.flags:
return isNone return isNone
elif tfThread in f.flags and a.flags * {tfThread, tfNoSideEffect} == {}: elif tfThread in f.flags and a.flags * {tfThread, tfNoSideEffect} == {}:
@ -375,7 +404,8 @@ proc procTypeRel(c: var TCandidate, f, a: PType): TTypeRelation =
elif f.callConv != a.callConv: elif f.callConv != a.callConv:
# valid to pass a 'nimcall' thingie to 'closure': # valid to pass a 'nimcall' thingie to 'closure':
if f.callConv == ccClosure and a.callConv == ccDefault: if f.callConv == ccClosure and a.callConv == ccDefault:
result = isConvertible result = if result != isInferred: isConvertible
else: isInferredConvertible
else: else:
return isNone return isNone
when useEffectSystem: when useEffectSystem:
@ -401,18 +431,8 @@ proc typeRangeRel(f, a: PType): TTypeRelation {.noinline.} =
proc matchUserTypeClass*(c: PContext, m: var TCandidate, proc matchUserTypeClass*(c: PContext, m: var TCandidate,
ff, a: PType): TTypeRelation = ff, a: PType): TTypeRelation =
#if f.n == nil:
# let r = typeRel(m, f, a)
# return if r == isGeneric: arg else: nil
var body = ff.skipTypes({tyUserTypeClassInst}) var body = ff.skipTypes({tyUserTypeClassInst})
# var prev = PType(idTableGet(m.bindings, f))
# if prev != nil:
# if sameType(prev, a): return arg
# else: return nil
# pushInfoContext(arg.info)
openScope(c) openScope(c)
inc c.inTypeClass inc c.inTypeClass
@ -461,7 +481,6 @@ proc matchUserTypeClass*(c: PContext, m: var TCandidate,
else: discard else: discard
return isGeneric return isGeneric
# put(m.bindings, f, a)
proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation = proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation =
# typeRel can be used to establish various relationships between types: # typeRel can be used to establish various relationships between types:
@ -851,7 +870,7 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation =
else: else:
internalAssert a.sons != nil and a.sons.len > 0 internalAssert a.sons != nil and a.sons.len > 0
c.typedescMatched = true c.typedescMatched = true
result = typeRel(c, f.sons[0], a.sons[0]) result = typeRel(c, f.base, a.base)
else: else:
result = isNone result = isNone
else: else:
@ -883,20 +902,25 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation =
of tyTypeDesc: of tyTypeDesc:
var prev = PType(idTableGet(c.bindings, f)) var prev = PType(idTableGet(c.bindings, f))
if prev == nil: if prev == nil:
if a.kind == tyTypeDesc: # proc foo(T: typedesc, x: T)
if f.sons[0].kind == tyNone: # when `f` is an unresolved typedesc, `a` could be any
# type, so we should not perform this check earlier
if a.kind != tyTypeDesc: return isNone
if f.base.kind == tyNone:
result = isGeneric result = isGeneric
else: else:
result = typeRel(c, f.sons[0], a.sons[0]) result = typeRel(c, f.base, a.base)
if result != isNone: if result != isNone:
put(c.bindings, f, a) put(c.bindings, f, a)
else: else:
result = isNone if tfUnresolved in f.flags:
result = typeRel(c, prev.base, a)
elif a.kind == tyTypeDesc:
result = typeRel(c, prev.base, a.base)
else: else:
internalAssert prev.sonsLen == 1 result = isNone
let toMatch = if tfUnresolved in f.flags: a
else: a.sons[0]
result = typeRel(c, prev.sons[0], toMatch)
of tyStmt: of tyStmt:
result = isGeneric result = isGeneric
@ -1000,11 +1024,33 @@ proc paramTypesMatchAux(m: var TCandidate, f, argType: PType,
argType = arg.typ argType = arg.typ
var var
a = if c.inTypeClass > 0: argType.skipTypes({tyTypeDesc}) a = if c.inTypeClass > 0: argType.skipTypes({tyTypeDesc, tyFieldAccessor})
else: argType else: argType
r = typeRel(m, f, a) r = typeRel(m, f, a)
if r != isNone and m.calleeSym != nil and
m.calleeSym.kind in {skMacro, skTemplate}:
# XXX: duplicating this is ugly, maybe we should move this
# directly into typeRel using return-like templates
case r
of isConvertible, isIntConv: inc(m.convMatches)
of isSubtype, isSubrange: inc(m.subtypeMatches)
of isGeneric, isInferred: inc(m.genericMatches)
of isInferredConvertible: inc(m.genericMatches); inc(m.convMatches)
of isFromIntLit: inc(m.intConvMatches, 256)
of isEqual: inc(m.exactMatches)
of isNone: discard
if f.kind == tyStmt and argOrig.kind == nkDo:
return argOrig[bodyPos]
elif f.kind == tyTypeDesc:
return arg
elif f.kind == tyStatic:
return arg.typ.n
else:
return argOrig
case r case r
of isConvertible: of isConvertible:
inc(m.convMatches) inc(m.convMatches)
@ -1021,18 +1067,18 @@ proc paramTypesMatchAux(m: var TCandidate, f, argType: PType,
inc(m.subtypeMatches) inc(m.subtypeMatches)
#result = copyTree(arg) #result = copyTree(arg)
result = implicitConv(nkHiddenStdConv, f, copyTree(arg), m, c) result = implicitConv(nkHiddenStdConv, f, copyTree(arg), m, c)
of isInferred, isInferredConvertible:
inc(m.genericMatches)
if arg.kind in {nkProcDef, nkIteratorDef} + nkLambdaKinds:
result = c.semInferredLambda(c, m.bindings, arg)
else:
let inferred = c.semGenerateInstance(c, arg.sym, m.bindings, arg.info)
result = newSymNode(inferred, arg.info)
if r == isInferredConvertible:
inc(m.convMatches)
result = implicitConv(nkHiddenStdConv, f, result, m, c)
of isGeneric: of isGeneric:
inc(m.genericMatches) inc(m.genericMatches)
if m.calleeSym != nil and m.calleeSym.kind in {skMacro, skTemplate}:
if f.kind == tyStmt and argOrig.kind == nkDo:
result = argOrig[bodyPos]
elif f.kind == tyTypeDesc:
result = arg
elif f.kind == tyStatic:
result = arg.typ.n
else:
result = argOrig
else:
result = copyTree(arg) result = copyTree(arg)
result.typ = getInstantiatedType(c, arg, m, f) result.typ = getInstantiatedType(c, arg, m, f)
# BUG: f may not be the right key! # BUG: f may not be the right key!
@ -1081,6 +1127,7 @@ proc paramTypesMatch*(m: var TCandidate, f, a: PType,
# incorrect to simply use the first fitting match. However, to implement # incorrect to simply use the first fitting match. However, to implement
# this correctly is inefficient. We have to copy `m` here to be able to # this correctly is inefficient. We have to copy `m` here to be able to
# roll back the side effects of the unification algorithm. # roll back the side effects of the unification algorithm.
let c = m.c let c = m.c
var x, y, z: TCandidate var x, y, z: TCandidate
initCandidate(c, x, m.callee) initCandidate(c, x, m.callee)

View file

@ -754,6 +754,7 @@ proc transformStmt*(module: PSym, n: PNode): PNode =
result = processTransf(c, n, module) result = processTransf(c, n, module)
result = liftLambdasForTopLevel(module, result) result = liftLambdasForTopLevel(module, result)
incl(result.flags, nfTransf) incl(result.flags, nfTransf)
when useEffectSystem: trackTopLevelStmt(module, result)
proc transformExpr*(module: PSym, n: PNode): PNode = proc transformExpr*(module: PSym, n: PNode): PNode =
if nfTransf in n.flags: if nfTransf in n.flags:

View file

@ -431,8 +431,8 @@ proc typeToString(typ: PType, prefer: TPreferedDesc = preferName): string =
add(result, typeToString(t.sons[i])) add(result, typeToString(t.sons[i]))
add(result, ']') add(result, ']')
of tyTypeDesc: of tyTypeDesc:
if t.len == 0: result = "typedesc" if t.base.kind == tyNone: result = "typedesc"
else: result = "typedesc[" & typeToString(t.sons[0]) & "]" else: result = "typedesc[" & typeToString(t.base) & "]"
of tyStatic: of tyStatic:
internalAssert t.len > 0 internalAssert t.len > 0
result = "static[" & typeToString(t.sons[0]) & "]" result = "static[" & typeToString(t.sons[0]) & "]"
@ -452,7 +452,8 @@ proc typeToString(typ: PType, prefer: TPreferedDesc = preferName): string =
of tyProc: "proc" of tyProc: "proc"
of tyObject: "object" of tyObject: "object"
of tyTuple: "tuple" of tyTuple: "tuple"
else: (internalAssert(false); "") of tyOpenArray: "openarray"
else: typeToStr[t.base.kind]
of tyUserTypeClassInst: of tyUserTypeClassInst:
let body = t.base let body = t.base
result = body.sym.name.s & "[" result = body.sym.name.s & "["
@ -463,7 +464,7 @@ proc typeToString(typ: PType, prefer: TPreferedDesc = preferName): string =
of tyAnd: of tyAnd:
result = typeToString(t.sons[0]) & " and " & typeToString(t.sons[1]) result = typeToString(t.sons[0]) & " and " & typeToString(t.sons[1])
of tyOr: of tyOr:
result = typeToString(t.sons[0]) & " and " & typeToString(t.sons[1]) result = typeToString(t.sons[0]) & " or " & typeToString(t.sons[1])
of tyNot: of tyNot:
result = "not " & typeToString(t.sons[0]) result = "not " & typeToString(t.sons[0])
of tyExpr: of tyExpr:
@ -1042,7 +1043,8 @@ proc typeAllowedAux(marker: var TIntSet, typ: PType, kind: TSymKind,
of tyEmpty: of tyEmpty:
result = taField in flags result = taField in flags
of tyTypeClasses: of tyTypeClasses:
result = true result = tfGenericTypeParam in t.flags or
taField notin flags
of tyGenericBody, tyGenericParam, tyGenericInvokation, of tyGenericBody, tyGenericParam, tyGenericInvokation,
tyNone, tyForward, tyFromExpr, tyFieldAccessor: tyNone, tyForward, tyFromExpr, tyFieldAccessor:
result = false result = false
@ -1231,8 +1233,7 @@ proc computeSizeAux(typ: PType, a: var BiggestInt): BiggestInt =
of tyGenericInst, tyDistinct, tyGenericBody, tyMutable, tyConst, tyIter: of tyGenericInst, tyDistinct, tyGenericBody, tyMutable, tyConst, tyIter:
result = computeSizeAux(lastSon(typ), a) result = computeSizeAux(lastSon(typ), a)
of tyTypeDesc: of tyTypeDesc:
result = if typ.len == 1: computeSizeAux(typ.sons[0], a) result = computeSizeAux(typ.base, a)
else: szUnknownSize
of tyForward: return szIllegalRecursion of tyForward: return szIllegalRecursion
else: else:
#internalError("computeSizeAux()") #internalError("computeSizeAux()")
@ -1258,7 +1259,7 @@ proc containsGenericTypeIter(t: PType, closure: PObject): bool =
return true return true
if t.kind == tyTypeDesc: if t.kind == tyTypeDesc:
if t.sons[0].kind == tyNone: return true if t.base.kind == tyNone: return true
if containsGenericTypeIter(t.base, closure): return true if containsGenericTypeIter(t.base, closure): return true
return false return false

File diff suppressed because it is too large Load diff

View file

@ -8,7 +8,7 @@
# #
## This module contains the type definitions for the new evaluation engine. ## This module contains the type definitions for the new evaluation engine.
## An instruction is 1-2 int32s in memory, it is a register based VM. ## An instruction is 1-3 int32s in memory, it is a register based VM.
import ast, passes, msgs, intsets import ast, passes, msgs, intsets
@ -32,17 +32,17 @@ type
opcAsgnFloat, opcAsgnFloat,
opcAsgnRef, opcAsgnRef,
opcAsgnComplex, opcAsgnComplex,
opcRegToNode,
opcNodeToReg,
opcLdArr, # a = b[c] opcLdArr, # a = b[c]
opcLdArrRef,
opcWrArr, # a[b] = c opcWrArr, # a[b] = c
opcWrArrRef,
opcLdObj, # a = b.c opcLdObj, # a = b.c
opcLdObjRef,
opcWrObj, # a.b = c opcWrObj, # a.b = c
opcWrObjRef, opcAddrReg,
opcAddr, opcAddrNode,
opcDeref, opcLdDeref,
opcWrDeref,
opcWrStrIdx, opcWrStrIdx,
opcLdStrIdx, # a = b[c] opcLdStrIdx, # a = b[c]
@ -117,15 +117,13 @@ type
opcNew, opcNew,
opcNewSeq, opcNewSeq,
opcLdNull, # dest = nullvalue(types[Bx]) opcLdNull, # dest = nullvalue(types[Bx])
opcLdNullReg,
opcLdConst, # dest = constants[Bx] opcLdConst, # dest = constants[Bx]
opcAsgnConst, # dest = copy(constants[Bx]) opcAsgnConst, # dest = copy(constants[Bx])
opcLdGlobal, # dest = globals[Bx] opcLdGlobal, # dest = globals[Bx]
opcLdImmInt, # dest = immediate value opcLdImmInt, # dest = immediate value
opcNBindSym, opcNBindSym,
opcWrGlobal,
opcWrGlobalRef,
opcGlobalAlias, # load an alias to a global into a register
opcGlobalOnce, # used to introduce an assignment to a global once
opcSetType, # dest.typ = types[Bx] opcSetType, # dest.typ = types[Bx]
opcTypeTrait opcTypeTrait
@ -159,14 +157,13 @@ type
slotTempInt, # some temporary int slotTempInt, # some temporary int
slotTempFloat, # some temporary float slotTempFloat, # some temporary float
slotTempStr, # some temporary string slotTempStr, # some temporary string
slotTempComplex # some complex temporary (n.sons field is used) slotTempComplex # some complex temporary (s.node field is used)
PProc* = ref object PProc* = ref object
blocks*: seq[TBlock] # blocks; temp data structure blocks*: seq[TBlock] # blocks; temp data structure
sym*: PSym
slots*: array[TRegister, tuple[inUse: bool, kind: TSlotKind]] slots*: array[TRegister, tuple[inUse: bool, kind: TSlotKind]]
maxSlots*: int maxSlots*: int
globals*: array[TRegister, int] # hack: to support passing globals byref
# we map a slot persistently to a global
PCtx* = ref TCtx PCtx* = ref TCtx
TCtx* = object of passes.TPassContext # code gen context TCtx* = object of passes.TPassContext # code gen context
@ -183,6 +180,7 @@ type
callsite*: PNode callsite*: PNode
mode*: TEvalMode mode*: TEvalMode
features*: TSandboxFlags features*: TSandboxFlags
traceActive*: bool
TPosition* = distinct int TPosition* = distinct int

View file

@ -139,25 +139,12 @@ proc getTemp(c: PCtx; typ: PType): TRegister =
if not c.slots[i].inUse: if not c.slots[i].inUse:
c.slots[i] = (inUse: true, kind: k) c.slots[i] = (inUse: true, kind: k)
return TRegister(i) return TRegister(i)
if c.maxSlots >= high(TRegister):
internalError("cannot generate code; too many registers required")
result = TRegister(c.maxSlots) result = TRegister(c.maxSlots)
c.slots[c.maxSlots] = (inUse: true, kind: k) c.slots[c.maxSlots] = (inUse: true, kind: k)
inc c.maxSlots inc c.maxSlots
proc getGlobalSlot(c: PCtx; n: PNode; s: PSym): TRegister =
let p = c.prc
for i in 0 .. p.maxSlots-1:
if p.globals[i] == s.id: return TRegister(i)
result = TRegister(p.maxSlots)
p.slots[p.maxSlots] = (inUse: true, kind: slotFixedVar)
p.globals[p.maxSlots] = s.id
inc p.maxSlots
# XXX this is still not correct! We need to load the global in a proc init
# section, otherwise control flow could lead to a usage before it's been
# loaded.
c.gABx(n, opcGlobalAlias, result, s.position)
# XXX add some internal asserts here
proc freeTemp(c: PCtx; r: TRegister) = proc freeTemp(c: PCtx; r: TRegister) =
let c = c.prc let c = c.prc
if c.slots[r].kind >= slotSomeTemp: c.slots[r].inUse = false if c.slots[r].kind >= slotSomeTemp: c.slots[r].inUse = false
@ -216,10 +203,12 @@ proc genx(c: PCtx; n: PNode; flags: TGenFlags = {}): TRegister =
internalAssert tmp >= 0 internalAssert tmp >= 0
result = TRegister(tmp) result = TRegister(tmp)
proc clearDest(n: PNode; dest: var TDest) {.inline.} = proc clearDest(c: PCtx; n: PNode; dest: var TDest) {.inline.} =
# stmt is different from 'void' in meta programming contexts. # stmt is different from 'void' in meta programming contexts.
# So we only set dest to -1 if 'void': # So we only set dest to -1 if 'void':
if n.typ.isNil or n.typ.kind == tyEmpty: dest = -1 if dest >= 0 and (n.typ.isNil or n.typ.kind == tyEmpty):
c.freeTemp(dest)
dest = -1
proc isNotOpr(n: PNode): bool = proc isNotOpr(n: PNode): bool =
n.kind in nkCallKinds and n.sons[0].kind == nkSym and n.kind in nkCallKinds and n.sons[0].kind == nkSym and
@ -259,7 +248,7 @@ proc genWhile(c: PCtx; n: PNode) =
proc genBlock(c: PCtx; n: PNode; dest: var TDest) = proc genBlock(c: PCtx; n: PNode; dest: var TDest) =
withBlock(n.sons[0].sym): withBlock(n.sons[0].sym):
c.gen(n.sons[1], dest) c.gen(n.sons[1], dest)
clearDest(n, dest) c.clearDest(n, dest)
proc genBreak(c: PCtx; n: PNode) = proc genBreak(c: PCtx; n: PNode) =
let L1 = c.xjmp(n, opcJmp) let L1 = c.xjmp(n, opcJmp)
@ -297,14 +286,16 @@ proc genIf(c: PCtx, n: PNode; dest: var TDest) =
else: else:
c.gen(it.sons[0], tmp) c.gen(it.sons[0], tmp)
elsePos = c.xjmp(it.sons[0], opcFJmp, tmp) # if false elsePos = c.xjmp(it.sons[0], opcFJmp, tmp) # if false
c.clearDest(n, dest)
c.gen(it.sons[1], dest) # then part c.gen(it.sons[1], dest) # then part
if i < sonsLen(n)-1: if i < sonsLen(n)-1:
endings.add(c.xjmp(it.sons[1], opcJmp, 0)) endings.add(c.xjmp(it.sons[1], opcJmp, 0))
c.patch(elsePos) c.patch(elsePos)
else: else:
c.clearDest(n, dest)
c.gen(it.sons[0], dest) c.gen(it.sons[0], dest)
for endPos in endings: c.patch(endPos) for endPos in endings: c.patch(endPos)
clearDest(n, dest) c.clearDest(n, dest)
proc genAndOr(c: PCtx; n: PNode; opc: TOpcode; dest: var TDest) = proc genAndOr(c: PCtx; n: PNode; opc: TOpcode; dest: var TDest) =
# asgn dest, a # asgn dest, a
@ -317,9 +308,12 @@ proc genAndOr(c: PCtx; n: PNode; opc: TOpcode; dest: var TDest) =
c.gen(n.sons[2], dest) c.gen(n.sons[2], dest)
c.patch(L1) c.patch(L1)
proc nilLiteral(n: PNode): PNode =
result = n
proc rawGenLiteral(c: PCtx; n: PNode): int = proc rawGenLiteral(c: PCtx; n: PNode): int =
result = c.constants.len result = c.constants.len
c.constants.add n c.constants.add n.nilLiteral
internalAssert result < 0x7fff internalAssert result < 0x7fff
proc sameConstant*(a, b: PNode): bool = proc sameConstant*(a, b: PNode): bool =
@ -385,8 +379,8 @@ proc genCase(c: PCtx; n: PNode; dest: var TDest) =
if i < sonsLen(n)-1: if i < sonsLen(n)-1:
endings.add(c.xjmp(it.lastSon, opcJmp, 0)) endings.add(c.xjmp(it.lastSon, opcJmp, 0))
c.patch(elsePos) c.patch(elsePos)
c.clearDest(n, dest)
for endPos in endings: c.patch(endPos) for endPos in endings: c.patch(endPos)
clearDest(n, dest)
proc genType(c: PCtx; typ: PType): int = proc genType(c: PCtx; typ: PType): int =
for i, t in c.types: for i, t in c.types:
@ -400,6 +394,7 @@ proc genTry(c: PCtx; n: PNode; dest: var TDest) =
var endings: seq[TPosition] = @[] var endings: seq[TPosition] = @[]
let elsePos = c.xjmp(n, opcTry, 0) let elsePos = c.xjmp(n, opcTry, 0)
c.gen(n.sons[0], dest) c.gen(n.sons[0], dest)
c.clearDest(n, dest)
c.patch(elsePos) c.patch(elsePos)
for i in 1 .. <n.len: for i in 1 .. <n.len:
let it = n.sons[i] let it = n.sons[i]
@ -415,6 +410,7 @@ proc genTry(c: PCtx; n: PNode; dest: var TDest) =
# general except section: # general except section:
c.gABx(it, opcExcept, 0, 0) c.gABx(it, opcExcept, 0, 0)
c.gen(it.lastSon, dest) c.gen(it.lastSon, dest)
c.clearDest(n, dest)
if i < sonsLen(n)-1: if i < sonsLen(n)-1:
endings.add(c.xjmp(it, opcJmp, 0)) endings.add(c.xjmp(it, opcJmp, 0))
c.patch(endExcept) c.patch(endExcept)
@ -425,8 +421,8 @@ proc genTry(c: PCtx; n: PNode; dest: var TDest) =
c.gABx(fin, opcFinally, 0, 0) c.gABx(fin, opcFinally, 0, 0)
if fin.kind == nkFinally: if fin.kind == nkFinally:
c.gen(fin.sons[0], dest) c.gen(fin.sons[0], dest)
c.clearDest(n, dest)
c.gABx(fin, opcFinallyEnd, 0, 0) c.gABx(fin, opcFinallyEnd, 0, 0)
clearDest(n, dest)
proc genRaise(c: PCtx; n: PNode) = proc genRaise(c: PCtx; n: PNode) =
let dest = genx(c, n.sons[0]) let dest = genx(c, n.sons[0])
@ -455,21 +451,37 @@ proc genCall(c: PCtx; n: PNode; dest: var TDest) =
c.gABC(n, opcIndCallAsgn, dest, x, n.len) c.gABC(n, opcIndCallAsgn, dest, x, n.len)
c.freeTempRange(x, n.len) c.freeTempRange(x, n.len)
template isGlobal(s: PSym): bool = sfGlobal in s.flags and s.kind != skForVar
proc needsAsgnPatch(n: PNode): bool = proc needsAsgnPatch(n: PNode): bool =
n.kind in {nkBracketExpr, nkDotExpr, nkCheckedFieldExpr} n.kind in {nkBracketExpr, nkDotExpr, nkCheckedFieldExpr,
nkDerefExpr, nkHiddenDeref} or (n.kind == nkSym and n.sym.isGlobal)
proc genAsgnPatch(c: PCtx; le: PNode, value: TRegister) = proc genAsgnPatch(c: PCtx; le: PNode, value: TRegister) =
case le.kind case le.kind
of nkBracketExpr: of nkBracketExpr:
let dest = c.genx(le.sons[0]) let dest = c.genx(le.sons[0], {gfAddrOf})
let idx = c.genx(le.sons[1]) let idx = c.genx(le.sons[1])
c.gABC(le, opcWrArrRef, dest, idx, value) c.gABC(le, opcWrArr, dest, idx, value)
c.freeTemp(dest)
c.freeTemp(idx)
of nkDotExpr, nkCheckedFieldExpr: of nkDotExpr, nkCheckedFieldExpr:
# XXX field checks here # XXX field checks here
let left = if le.kind == nkDotExpr: le else: le.sons[0] let left = if le.kind == nkDotExpr: le else: le.sons[0]
let dest = c.genx(left.sons[0]) let dest = c.genx(left.sons[0], {gfAddrOf})
let idx = c.genx(left.sons[1]) let idx = c.genx(left.sons[1])
c.gABC(left, opcWrObjRef, dest, idx, value) c.gABC(left, opcWrObj, dest, idx, value)
c.freeTemp(dest)
c.freeTemp(idx)
of nkDerefExpr, nkHiddenDeref:
let dest = c.genx(le.sons[0], {gfAddrOf})
c.gABC(le, opcWrDeref, dest, value)
c.freeTemp(dest)
of nkSym:
if le.sym.isGlobal:
let dest = c.genx(le, {gfAddrOf})
c.gABC(le, opcWrDeref, dest, value)
c.freeTemp(dest)
else: else:
discard discard
@ -579,10 +591,10 @@ proc genAddSubInt(c: PCtx; n: PNode; dest: var TDest; opc: TOpcode) =
proc genConv(c: PCtx; n, arg: PNode; dest: var TDest; opc=opcConv) = proc genConv(c: PCtx; n, arg: PNode; dest: var TDest; opc=opcConv) =
let tmp = c.genx(arg) let tmp = c.genx(arg)
c.gABx(n, opcSetType, tmp, genType(c, arg.typ))
if dest < 0: dest = c.getTemp(n.typ) if dest < 0: dest = c.getTemp(n.typ)
c.gABC(n, opc, dest, tmp) c.gABC(n, opc, dest, tmp)
c.gABx(n, opc, 0, genType(c, n.typ)) c.gABx(n, opc, 0, genType(c, n.typ))
c.gABx(n, opc, 0, genType(c, arg.typ))
c.freeTemp(tmp) c.freeTemp(tmp)
proc genCard(c: PCtx; n: PNode; dest: var TDest) = proc genCard(c: PCtx; n: PNode; dest: var TDest) =
@ -608,6 +620,7 @@ proc genMagic(c: PCtx; n: PNode; dest: var TDest) =
c.genAddSubInt(n, dest, opcAddInt) c.genAddSubInt(n, dest, opcAddInt)
of mInc, mDec: of mInc, mDec:
unused(n, dest) unused(n, dest)
# XXX generates inefficient code for globals
var d = c.genx(n.sons[1]).TDest var d = c.genx(n.sons[1]).TDest
c.genAddSubInt(n, d, if m == mInc: opcAddInt else: opcSubInt) c.genAddSubInt(n, d, if m == mInc: opcAddInt else: opcSubInt)
c.genAsgnPatch(n.sons[1], d) c.genAsgnPatch(n.sons[1], d)
@ -621,6 +634,7 @@ proc genMagic(c: PCtx; n: PNode; dest: var TDest) =
c.genNewSeq(n) c.genNewSeq(n)
of mNewString: of mNewString:
genUnaryABC(c, n, dest, opcNewStr) genUnaryABC(c, n, dest, opcNewStr)
# XXX buggy
of mNewStringOfCap: of mNewStringOfCap:
# we ignore the 'cap' argument and translate it as 'newString(0)'. # we ignore the 'cap' argument and translate it as 'newString(0)'.
# eval n.sons[1] for possible side effects: # eval n.sons[1] for possible side effects:
@ -629,6 +643,7 @@ proc genMagic(c: PCtx; n: PNode; dest: var TDest) =
if dest < 0: dest = c.getTemp(n.typ) if dest < 0: dest = c.getTemp(n.typ)
c.gABC(n, opcNewStr, dest, tmp) c.gABC(n, opcNewStr, dest, tmp)
c.freeTemp(tmp) c.freeTemp(tmp)
# XXX buggy
of mLengthOpenArray, mLengthArray, mLengthSeq: of mLengthOpenArray, mLengthArray, mLengthSeq:
genUnaryABI(c, n, dest, opcLenSeq) genUnaryABI(c, n, dest, opcLenSeq)
of mLengthStr: of mLengthStr:
@ -860,7 +875,6 @@ proc genMagic(c: PCtx; n: PNode; dest: var TDest) =
of mNGenSym: genBinaryABC(c, n, dest, opcGenSym) of mNGenSym: genBinaryABC(c, n, dest, opcGenSym)
of mMinI, mMaxI, mMinI64, mMaxI64, mAbsF64, mMinF64, mMaxF64, mAbsI, mAbsI64: of mMinI, mMaxI, mMinI64, mMaxI64, mAbsF64, mMinF64, mMaxF64, mAbsI, mAbsI64:
c.genCall(n, dest) c.genCall(n, dest)
clearDest(n, dest)
of mExpandToAst: of mExpandToAst:
if n.len != 2: if n.len != 2:
globalError(n.info, errGenerated, "expandToAst requires 1 argument") globalError(n.info, errGenerated, "expandToAst requires 1 argument")
@ -891,6 +905,10 @@ const
tyFloat, tyFloat32, tyFloat64, tyFloat128, tyFloat, tyFloat32, tyFloat64, tyFloat128,
tyUInt, tyUInt8, tyUInt16, tyUInt32, tyUInt64} tyUInt, tyUInt8, tyUInt16, tyUInt32, tyUInt64}
proc fitsRegister*(t: PType): bool =
t.skipTypes(abstractInst-{tyTypeDesc}).kind in {
tyRange, tyEnum, tyBool, tyInt..tyUInt64}
proc requiresCopy(n: PNode): bool = proc requiresCopy(n: PNode): bool =
if n.typ.skipTypes(abstractInst-{tyTypeDesc}).kind in atomicTypes: if n.typ.skipTypes(abstractInst-{tyTypeDesc}).kind in atomicTypes:
result = false result = false
@ -902,22 +920,33 @@ proc requiresCopy(n: PNode): bool =
proc unneededIndirection(n: PNode): bool = proc unneededIndirection(n: PNode): bool =
n.typ.skipTypes(abstractInst-{tyTypeDesc}).kind == tyRef n.typ.skipTypes(abstractInst-{tyTypeDesc}).kind == tyRef
proc skipDeref(n: PNode): PNode =
if n.kind in {nkDerefExpr, nkHiddenDeref} and unneededIndirection(n.sons[0]):
result = n.sons[0]
else:
result = n
proc genAddrDeref(c: PCtx; n: PNode; dest: var TDest; opc: TOpcode; proc genAddrDeref(c: PCtx; n: PNode; dest: var TDest; opc: TOpcode;
flags: TGenFlags) = flags: TGenFlags) =
# a nop for certain types # a nop for certain types
let flags = if opc == opcAddr: flags+{gfAddrOf} else: flags let isAddr = opc in {opcAddrNode, opcAddrReg}
if unneededIndirection(n.sons[0]): let newflags = if isAddr: flags+{gfAddrOf} else: flags
gen(c, n.sons[0], dest, flags) # consider:
# proc foo(f: var ref int) =
# f = new(int)
# proc blah() =
# var x: ref int
# foo x
#
# The type of 'f' is 'var ref int' and of 'x' is 'ref int'. Hence for
# nkAddr we must not use 'unneededIndirection', but for deref we use it.
if not isAddr and unneededIndirection(n.sons[0]):
gen(c, n.sons[0], dest, newflags)
else: else:
let tmp = c.genx(n.sons[0], flags) let tmp = c.genx(n.sons[0], newflags)
if dest < 0: dest = c.getTemp(n.typ) if dest < 0: dest = c.getTemp(n.typ)
if not isAddr:
gABC(c, n, opc, dest, tmp) gABC(c, n, opc, dest, tmp)
if gfAddrOf notin flags and fitsRegister(n.typ):
c.gABC(n, opcNodeToReg, dest, dest)
elif c.prc.slots[tmp].kind >= slotTempUnknown:
gABC(c, n, opcAddrNode, dest, tmp)
else:
gABC(c, n, opcAddrReg, dest, tmp)
c.freeTemp(tmp) c.freeTemp(tmp)
proc whichAsgnOpc(n: PNode): TOpcode = proc whichAsgnOpc(n: PNode): TOpcode =
@ -935,8 +964,7 @@ proc whichAsgnOpc(n: PNode): TOpcode =
proc isRef(t: PType): bool = t.skipTypes(abstractRange-{tyTypeDesc}).kind == tyRef proc isRef(t: PType): bool = t.skipTypes(abstractRange-{tyTypeDesc}).kind == tyRef
proc whichAsgnOpc(n: PNode; opc: TOpcode): TOpcode = proc whichAsgnOpc(n: PNode; opc: TOpcode): TOpcode = opc
if isRef(n.typ): succ(opc) else: opc
proc genAsgn(c: PCtx; dest: TDest; ri: PNode; requiresCopy: bool) = proc genAsgn(c: PCtx; dest: TDest; ri: PNode; requiresCopy: bool) =
let tmp = c.genx(ri) let tmp = c.genx(ri)
@ -944,8 +972,6 @@ proc genAsgn(c: PCtx; dest: TDest; ri: PNode; requiresCopy: bool) =
gABC(c, ri, whichAsgnOpc(ri), dest, tmp) gABC(c, ri, whichAsgnOpc(ri), dest, tmp)
c.freeTemp(tmp) c.freeTemp(tmp)
template isGlobal(s: PSym): bool = sfGlobal in s.flags and s.kind != skForVar
proc setSlot(c: PCtx; v: PSym) = proc setSlot(c: PCtx; v: PSym) =
# XXX generate type initialization here? # XXX generate type initialization here?
if v.position == 0: if v.position == 0:
@ -954,40 +980,67 @@ proc setSlot(c: PCtx; v: PSym) =
kind: if v.kind == skLet: slotFixedLet else: slotFixedVar) kind: if v.kind == skLet: slotFixedLet else: slotFixedVar)
inc c.prc.maxSlots inc c.prc.maxSlots
proc cannotEval(n: PNode) {.noinline.} =
globalError(n.info, errGenerated, "cannot evaluate at compile time: " &
n.renderTree)
proc isOwnedBy(a, b: PSym): bool =
var a = a.owner
while a != nil and a.kind != skModule:
if a == b: return true
a = a.owner
proc checkCanEval(c: PCtx; n: PNode) =
# we need to ensure that we don't evaluate 'x' here:
# proc foo() = var x ...
let s = n.sym
if s.position == 0:
if s.kind in {skVar, skTemp, skLet, skParam, skResult} and
not s.isOwnedBy(c.prc.sym) and s.owner != c.module:
cannotEval(n)
proc genAsgn(c: PCtx; le, ri: PNode; requiresCopy: bool) = proc genAsgn(c: PCtx; le, ri: PNode; requiresCopy: bool) =
case le.kind case le.kind
of nkBracketExpr: of nkBracketExpr:
let dest = c.genx(le.sons[0]) let dest = c.genx(le.sons[0], {gfAddrOf})
let idx = c.genx(le.sons[1]) let idx = c.genx(le.sons[1])
let tmp = c.genx(ri) let tmp = c.genx(ri)
if le.sons[0].typ.skipTypes(abstractVarRange-{tyTypeDesc}).kind in { if le.sons[0].typ.skipTypes(abstractVarRange-{tyTypeDesc}).kind in {
tyString, tyCString}: tyString, tyCString}:
c.gABC(le, opcWrStrIdx, dest, idx, tmp) c.gABC(le, opcWrStrIdx, dest, idx, tmp)
else: else:
c.gABC(le, whichAsgnOpc(le, opcWrArr), dest, idx, tmp) c.gABC(le, opcWrArr, dest, idx, tmp)
c.freeTemp(tmp) c.freeTemp(tmp)
of nkDotExpr, nkCheckedFieldExpr: of nkDotExpr, nkCheckedFieldExpr:
# XXX field checks here # XXX field checks here
let left = if le.kind == nkDotExpr: le else: le.sons[0] let left = if le.kind == nkDotExpr: le else: le.sons[0]
let dest = c.genx(left.sons[0]) let dest = c.genx(left.sons[0], {gfAddrOf})
let idx = c.genx(left.sons[1]) let idx = c.genx(left.sons[1])
let tmp = c.genx(ri) let tmp = c.genx(ri)
c.gABC(left, whichAsgnOpc(left, opcWrObj), dest, idx, tmp) c.gABC(left, opcWrObj, dest, idx, tmp)
c.freeTemp(tmp)
of nkDerefExpr, nkHiddenDeref:
let dest = c.genx(le.sons[0], {gfAddrOf})
let tmp = c.genx(ri)
c.gABC(le, opcWrDeref, dest, tmp)
c.freeTemp(tmp) c.freeTemp(tmp)
of nkSym: of nkSym:
let s = le.sym let s = le.sym
checkCanEval(c, le)
if s.isGlobal: if s.isGlobal:
withTemp(tmp, le.typ): withTemp(tmp, le.typ):
gen(c, ri, tmp) c.gen(le, tmp, {gfAddrOf})
c.gABx(le, whichAsgnOpc(le, opcWrGlobal), tmp, s.position) let val = c.genx(ri)
c.gABC(le, opcWrDeref, tmp, val)
c.freeTemp(val)
else: else:
if s.kind == skForVar and c.mode == emRepl: c.setSlot s if s.kind == skForVar: c.setSlot s
internalAssert s.position > 0 or (s.position == 0 and internalAssert s.position > 0 or (s.position == 0 and
s.kind in {skParam,skResult}) s.kind in {skParam,skResult})
var dest: TRegister = s.position + ord(s.kind == skParam) var dest: TRegister = s.position + ord(s.kind == skParam)
gen(c, ri, dest) gen(c, ri, dest)
else: else:
let dest = c.genx(le) let dest = c.genx(le, {gfAddrOf})
genAsgn(c, dest, ri, requiresCopy) genAsgn(c, dest, ri, requiresCopy)
proc genLit(c: PCtx; n: PNode; dest: var TDest) = proc genLit(c: PCtx; n: PNode; dest: var TDest) =
@ -1013,22 +1066,22 @@ proc importcSym(c: PCtx; info: TLineInfo; s: PSym) =
localError(info, errGenerated, localError(info, errGenerated,
"cannot 'importc' variable at compile time") "cannot 'importc' variable at compile time")
proc cannotEval(n: PNode) {.noinline.} = proc getNullValue*(typ: PType, info: TLineInfo): PNode
globalError(n.info, errGenerated, "cannot evaluate at compile time: " &
n.renderTree)
proc genGlobalInit(c: PCtx; n: PNode; s: PSym) = proc genGlobalInit(c: PCtx; n: PNode; s: PSym) =
c.globals.add(emptyNode.copyNode) c.globals.add(getNullValue(s.typ, n.info))
s.position = c.globals.len s.position = c.globals.len
# This is rather hard to support, due to the laziness of the VM code # This is rather hard to support, due to the laziness of the VM code
# generator. See tests/compile/tmacro2 for why this is necesary: # generator. See tests/compile/tmacro2 for why this is necesary:
# var decls{.compileTime.}: seq[PNimrodNode] = @[] # var decls{.compileTime.}: seq[PNimrodNode] = @[]
c.gABx(n, opcGlobalOnce, 0, s.position) let dest = c.getTemp(s.typ)
c.gABx(n, opcLdGlobal, dest, s.position)
let tmp = c.genx(s.ast) let tmp = c.genx(s.ast)
c.gABx(n, whichAsgnOpc(n, opcWrGlobal), tmp, s.position) c.gABC(n, opcWrDeref, dest, tmp)
c.freeTemp(dest)
c.freeTemp(tmp) c.freeTemp(tmp)
proc genRdVar(c: PCtx; n: PNode; dest: var TDest) = proc genRdVar(c: PCtx; n: PNode; dest: var TDest; flags: TGenFlags) =
let s = n.sym let s = n.sym
if s.isGlobal: if s.isGlobal:
if sfCompileTime in s.flags or c.mode == emRepl: if sfCompileTime in s.flags or c.mode == emRepl:
@ -1038,9 +1091,12 @@ proc genRdVar(c: PCtx; n: PNode; dest: var TDest) =
if s.position == 0: if s.position == 0:
if sfImportc in s.flags: c.importcSym(n.info, s) if sfImportc in s.flags: c.importcSym(n.info, s)
else: genGlobalInit(c, n, s) else: genGlobalInit(c, n, s)
if dest < 0: if dest < 0: dest = c.getTemp(n.typ)
dest = c.getGlobalSlot(n, s) if gfAddrOf notin flags and fitsRegister(s.typ):
#c.gABx(n, opcAliasGlobal, dest, s.position) var cc = c.getTemp(n.typ)
c.gABx(n, opcLdGlobal, cc, s.position)
c.gABC(n, opcNodeToReg, dest, cc)
c.freeTemp(cc)
else: else:
c.gABx(n, opcLdGlobal, dest, s.position) c.gABx(n, opcLdGlobal, dest, s.position)
else: else:
@ -1061,7 +1117,13 @@ proc genAccess(c: PCtx; n: PNode; dest: var TDest; opc: TOpcode;
let a = c.genx(n.sons[0], flags) let a = c.genx(n.sons[0], flags)
let b = c.genx(n.sons[1], {}) let b = c.genx(n.sons[1], {})
if dest < 0: dest = c.getTemp(n.typ) if dest < 0: dest = c.getTemp(n.typ)
c.gABC(n, (if gfAddrOf in flags: succ(opc) else: opc), dest, a, b) if gfAddrOf notin flags and fitsRegister(n.typ):
var cc = c.getTemp(n.typ)
c.gABC(n, opc, cc, a, b)
c.gABC(n, opcNodeToReg, dest, cc)
c.freeTemp(cc)
else:
c.gABC(n, opc, dest, a, b)
c.freeTemp(a) c.freeTemp(a)
c.freeTemp(b) c.freeTemp(b)
@ -1079,7 +1141,6 @@ proc genArrAccess(c: PCtx; n: PNode; dest: var TDest; flags: TGenFlags) =
else: else:
genAccess(c, n, dest, opcLdArr, flags) genAccess(c, n, dest, opcLdArr, flags)
proc getNullValue*(typ: PType, info: TLineInfo): PNode
proc getNullValueAux(obj: PNode, result: PNode) = proc getNullValueAux(obj: PNode, result: PNode) =
case obj.kind case obj.kind
of nkRecList: of nkRecList:
@ -1132,6 +1193,9 @@ proc getNullValue(typ: PType, info: TLineInfo): PNode =
result = newNodeIT(nkCurly, info, t) result = newNodeIT(nkCurly, info, t)
else: internalError("getNullValue: " & $t.kind) else: internalError("getNullValue: " & $t.kind)
proc ldNullOpcode(t: PType): TOpcode =
if fitsRegister(t): opcLdNullReg else: opcLdNull
proc genVarSection(c: PCtx; n: PNode) = proc genVarSection(c: PCtx; n: PNode) =
for a in n: for a in n:
if a.kind == nkCommentStmt: continue if a.kind == nkCommentStmt: continue
@ -1142,12 +1206,14 @@ proc genVarSection(c: PCtx; n: PNode) =
setSlot(c, a[i].sym) setSlot(c, a[i].sym)
# v = t[i] # v = t[i]
var v: TDest = -1 var v: TDest = -1
genRdVar(c, a[i], v) checkCanEval(c, a[i])
c.gABC(n, opcLdObj, v, tmp, i) genRdVar(c, a[i], v, {gfAddrOf})
c.gABC(n, opcWrObj, v, tmp, i)
# XXX globals? # XXX globals?
c.freeTemp(tmp) c.freeTemp(tmp)
elif a.sons[0].kind == nkSym: elif a.sons[0].kind == nkSym:
let s = a.sons[0].sym let s = a.sons[0].sym
checkCanEval(c, a.sons[0])
if s.isGlobal: if s.isGlobal:
if s.position == 0: if s.position == 0:
if sfImportc in s.flags: c.importcSym(a.info, s) if sfImportc in s.flags: c.importcSym(a.info, s)
@ -1155,27 +1221,28 @@ proc genVarSection(c: PCtx; n: PNode) =
let sa = if s.ast.isNil: getNullValue(s.typ, a.info) else: s.ast let sa = if s.ast.isNil: getNullValue(s.typ, a.info) else: s.ast
c.globals.add(sa) c.globals.add(sa)
s.position = c.globals.len s.position = c.globals.len
# "Once support" is unnecessary here
if a.sons[2].kind == nkEmpty: if a.sons[2].kind == nkEmpty:
when false: when false:
withTemp(tmp, s.typ): withTemp(tmp, s.typ):
c.gABx(a, opcLdNull, tmp, c.genType(s.typ)) c.gABx(a, opcLdNull, tmp, c.genType(s.typ))
c.gABx(a, whichAsgnOpc(a.sons[0], opcWrGlobal), tmp, s.position) c.gABx(a, whichAsgnOpc(a.sons[0], opcWrGlobal), tmp, s.position)
else: else:
let tmp = genx(c, a.sons[2]) let tmp = c.genx(a.sons[0], {gfAddrOf})
c.gABx(a, whichAsgnOpc(a.sons[0], opcWrGlobal), tmp, s.position) let val = c.genx(a.sons[2])
c.gABC(a, opcWrDeref, tmp, val)
c.freeTemp(val)
c.freeTemp(tmp) c.freeTemp(tmp)
else: else:
setSlot(c, s) setSlot(c, s)
if a.sons[2].kind == nkEmpty: if a.sons[2].kind == nkEmpty:
c.gABx(a, opcLdNull, s.position, c.genType(s.typ)) c.gABx(a, ldNullOpcode(s.typ), s.position, c.genType(s.typ))
else: else:
gen(c, a.sons[2], s.position.TRegister) gen(c, a.sons[2], s.position.TRegister)
else: else:
# assign to a.sons[0]; happens for closures # assign to a.sons[0]; happens for closures
if a.sons[2].kind == nkEmpty: if a.sons[2].kind == nkEmpty:
let tmp = genx(c, a.sons[0]) let tmp = genx(c, a.sons[0])
c.gABx(a, opcLdNull, tmp, c.genType(a.sons[0].typ)) c.gABx(a, ldNullOpcode(a[0].typ), tmp, c.genType(a.sons[0].typ))
c.freeTemp(tmp) c.freeTemp(tmp)
else: else:
genAsgn(c, a.sons[0], a.sons[2], true) genAsgn(c, a.sons[0], a.sons[2], true)
@ -1183,10 +1250,19 @@ proc genVarSection(c: PCtx; n: PNode) =
proc genArrayConstr(c: PCtx, n: PNode, dest: var TDest) = proc genArrayConstr(c: PCtx, n: PNode, dest: var TDest) =
if dest < 0: dest = c.getTemp(n.typ) if dest < 0: dest = c.getTemp(n.typ)
c.gABx(n, opcLdNull, dest, c.genType(n.typ)) c.gABx(n, opcLdNull, dest, c.genType(n.typ))
if n.len > 0:
let intType = getSysType(tyInt) let intType = getSysType(tyInt)
let seqType = n.typ.skipTypes(abstractVar-{tyTypeDesc})
if seqType.kind == tySequence:
var tmp = c.getTemp(intType)
c.gABx(n, opcLdImmInt, tmp, n.len)
c.gABx(n, opcNewSeq, dest, c.genType(seqType))
c.gABx(n, opcNewSeq, tmp, 0)
c.freeTemp(tmp)
if n.len > 0:
var tmp = getTemp(c, intType) var tmp = getTemp(c, intType)
c.gABx(n, opcLdNull, tmp, c.genType(intType)) c.gABx(n, opcLdNullReg, tmp, c.genType(intType))
for x in n: for x in n:
let a = c.genx(x) let a = c.genx(x)
c.gABC(n, whichAsgnOpc(x, opcWrArr), dest, tmp, a) c.gABC(n, whichAsgnOpc(x, opcWrArr), dest, tmp, a)
@ -1250,9 +1326,10 @@ proc gen(c: PCtx; n: PNode; dest: var TDest; flags: TGenFlags = {}) =
case n.kind case n.kind
of nkSym: of nkSym:
let s = n.sym let s = n.sym
checkCanEval(c, n)
case s.kind case s.kind
of skVar, skForVar, skTemp, skLet, skParam, skResult: of skVar, skForVar, skTemp, skLet, skParam, skResult:
genRdVar(c, n, dest) genRdVar(c, n, dest, flags)
of skProc, skConverter, skMacro, skTemplate, skMethod, skIterator: of skProc, skConverter, skMacro, skTemplate, skMethod, skIterator:
# 'skTemplate' is only allowed for 'getAst' support: # 'skTemplate' is only allowed for 'getAst' support:
if sfImportc in s.flags: c.importcSym(n.info, s) if sfImportc in s.flags: c.importcSym(n.info, s)
@ -1281,7 +1358,7 @@ proc gen(c: PCtx; n: PNode; dest: var TDest; flags: TGenFlags = {}) =
genMagic(c, n, dest) genMagic(c, n, dest)
else: else:
genCall(c, n, dest) genCall(c, n, dest)
clearDest(n, dest) clearDest(c, n, dest)
of nkCharLit..nkInt64Lit: of nkCharLit..nkInt64Lit:
if isInt16Lit(n): if isInt16Lit(n):
if dest < 0: dest = c.getTemp(n.typ) if dest < 0: dest = c.getTemp(n.typ)
@ -1298,8 +1375,8 @@ proc gen(c: PCtx; n: PNode; dest: var TDest; flags: TGenFlags = {}) =
of nkDotExpr: genObjAccess(c, n, dest, flags) of nkDotExpr: genObjAccess(c, n, dest, flags)
of nkCheckedFieldExpr: genCheckedObjAccess(c, n, dest, flags) of nkCheckedFieldExpr: genCheckedObjAccess(c, n, dest, flags)
of nkBracketExpr: genArrAccess(c, n, dest, flags) of nkBracketExpr: genArrAccess(c, n, dest, flags)
of nkDerefExpr, nkHiddenDeref: genAddrDeref(c, n, dest, opcDeref, flags) of nkDerefExpr, nkHiddenDeref: genAddrDeref(c, n, dest, opcLdDeref, flags)
of nkAddr, nkHiddenAddr: genAddrDeref(c, n, dest, opcAddr, flags) of nkAddr, nkHiddenAddr: genAddrDeref(c, n, dest, opcAddrNode, flags)
of nkWhenStmt, nkIfStmt, nkIfExpr: genIf(c, n, dest) of nkWhenStmt, nkIfStmt, nkIfExpr: genIf(c, n, dest)
of nkCaseStmt: genCase(c, n, dest) of nkCaseStmt: genCase(c, n, dest)
of nkWhileStmt: of nkWhileStmt:
@ -1467,7 +1544,7 @@ proc genProc(c: PCtx; s: PSym): int =
# procs easily: # procs easily:
let body = s.getBody let body = s.getBody
let procStart = c.xjmp(body, opcJmp, 0) let procStart = c.xjmp(body, opcJmp, 0)
var p = PProc(blocks: @[]) var p = PProc(blocks: @[], sym: s)
let oldPrc = c.prc let oldPrc = c.prc
c.prc = p c.prc = p
# iterate over the parameters and allocate space for them: # iterate over the parameters and allocate space for them:
@ -1484,9 +1561,9 @@ proc genProc(c: PCtx; s: PSym): int =
c.gABC(body, opcEof, eofInstr.regA) c.gABC(body, opcEof, eofInstr.regA)
c.optimizeJumps(result) c.optimizeJumps(result)
s.offset = c.prc.maxSlots s.offset = c.prc.maxSlots
#if s.name.s == "concatStyleInterpolation": #if s.name.s == "xmlConstructor":
# c.echoCode(result)
# echo renderTree(body) # echo renderTree(body)
# c.echoCode(result)
c.prc = oldPrc c.prc = oldPrc
else: else:
c.prc.maxSlots = s.offset c.prc.maxSlots = s.offset

View file

@ -62,8 +62,8 @@ type
wWatchPoint, wSubsChar, wWatchPoint, wSubsChar,
wAcyclic, wShallow, wUnroll, wLinearScanEnd, wComputedGoto, wInjectStmt, wAcyclic, wShallow, wUnroll, wLinearScanEnd, wComputedGoto, wInjectStmt,
wWrite, wGensym, wInject, wDirty, wInheritable, wThreadVar, wEmit, wWrite, wGensym, wInject, wDirty, wInheritable, wThreadVar, wEmit,
wNoStackFrame, wAsmNoStackFrame,
wImplicitStatic, wGlobal, wCodegenDecl, wImplicitStatic, wGlobal, wCodegenDecl, wUnchecked,
wAuto, wBool, wCatch, wChar, wClass, wAuto, wBool, wCatch, wChar, wClass,
wConst_cast, wDefault, wDelete, wDouble, wDynamic_cast, wConst_cast, wDefault, wDelete, wDouble, wDynamic_cast,
@ -145,7 +145,7 @@ const
"subschar", "acyclic", "shallow", "unroll", "linearscanend", "subschar", "acyclic", "shallow", "unroll", "linearscanend",
"computedgoto", "injectstmt", "computedgoto", "injectstmt",
"write", "gensym", "inject", "dirty", "inheritable", "threadvar", "emit", "write", "gensym", "inject", "dirty", "inheritable", "threadvar", "emit",
"nostackframe", "implicitstatic", "global", "codegendecl", "asmnostackframe", "implicitstatic", "global", "codegendecl", "unchecked",
"auto", "bool", "catch", "char", "class", "auto", "bool", "catch", "char", "class",
"const_cast", "default", "delete", "double", "const_cast", "default", "delete", "double",

View file

@ -1701,11 +1701,11 @@ algorithm returns true:
result = isOrdinal(t) or t.kind in {float, float32, float64} result = isOrdinal(t) or t.kind in {float, float32, float64}
proc isExplicitlyConvertible(a, b: PType): bool = proc isExplicitlyConvertible(a, b: PType): bool =
result = false
if isImplicitlyConvertible(a, b): return true if isImplicitlyConvertible(a, b): return true
if typeEqualsOrDistinct(a, b): return true if typeEqualsOrDistinct(a, b): return true
if isIntegralType(a) and isIntegralType(b): return true if isIntegralType(a) and isIntegralType(b): return true
if isSubtype(a, b) or isSubtype(b, a): return true if isSubtype(a, b) or isSubtype(b, a): return true
return false
The convertible relation can be relaxed by a user-defined type The convertible relation can be relaxed by a user-defined type
`converter`:idx:. `converter`:idx:.
@ -1774,7 +1774,7 @@ Example:
.. code-block:: nimrod .. code-block:: nimrod
proc p(x, y: int): int = proc p(x, y: int): int =
return x + y result = x + y
discard p(3, 4) # discard the return value of `p` discard p(3, 4) # discard the return value of `p`
@ -1789,7 +1789,7 @@ been declared with the `discardable`:idx: pragma:
.. code-block:: nimrod .. code-block:: nimrod
proc p(x, y: int): int {.discardable.} = proc p(x, y: int): int {.discardable.} =
return x + y result = x + y
p(3, 4) # now valid p(3, 4) # now valid
@ -2231,6 +2231,8 @@ Instead of:
Using statement Using statement
--------------- ---------------
**Warning**: The ``using`` statement is highly experimental!
The `using statement`:idx: provides syntactic convenience for procs that The `using statement`:idx: provides syntactic convenience for procs that
heavily use a single contextual parameter. When applied to a variable or a heavily use a single contextual parameter. When applied to a variable or a
constant, it will instruct Nimrod to automatically consider the used symbol as constant, it will instruct Nimrod to automatically consider the used symbol as
@ -2438,7 +2440,7 @@ A procedure cannot modify its parameters (unless the parameters have the type
.. code-block:: nimrod .. code-block:: nimrod
proc `$` (x: int): string = proc `$` (x: int): string =
# converts an integer to a string; this is a prefix operator. # converts an integer to a string; this is a prefix operator.
return intToStr(x) result = intToStr(x)
Operators with one parameter are prefix operators, operators with two Operators with one parameter are prefix operators, operators with two
parameters are infix operators. (However, the parser distinguishes these from parameters are infix operators. (However, the parser distinguishes these from
@ -2452,7 +2454,7 @@ notation. (Thus an operator can have more than two parameters):
.. code-block:: nimrod .. code-block:: nimrod
proc `*+` (a, b, c: int): int = proc `*+` (a, b, c: int): int =
# Multiply and add # Multiply and add
return a * b + c result = a * b + c
assert `*+`(3, 4, 6) == `*`(a, `+`(b, c)) assert `*+`(3, 4, 6) == `*`(a, `+`(b, c))
@ -2498,7 +2500,7 @@ different; for this a special setter syntax is needed:
proc host*(s: TSocket): int {.inline.} = proc host*(s: TSocket): int {.inline.} =
## getter of hostAddr ## getter of hostAddr
return s.FHost s.FHost
var var
s: TSocket s: TSocket
@ -2648,11 +2650,12 @@ return values. This can be done in a cleaner way by returning a tuple:
.. code-block:: nimrod .. code-block:: nimrod
proc divmod(a, b: int): tuple[res, remainder: int] = proc divmod(a, b: int): tuple[res, remainder: int] =
return (a div b, a mod b) (a div b, a mod b)
var t = divmod(8, 5) var t = divmod(8, 5)
assert t.res == 1 assert t.res == 1
assert t.remainder = 3 assert t.remainder == 3
One can use `tuple unpacking`:idx: to access the tuple's fields: One can use `tuple unpacking`:idx: to access the tuple's fields:
@ -2724,7 +2727,7 @@ dispatch.
method eval(e: ref TPlusExpr): int = method eval(e: ref TPlusExpr): int =
# watch out: relies on dynamic binding # watch out: relies on dynamic binding
return eval(e.a) + eval(e.b) result = eval(e.a) + eval(e.b)
proc newLit(x: int): ref TLiteral = proc newLit(x: int): ref TLiteral =
new(result) new(result)
@ -2923,13 +2926,13 @@ parameters of an outer factory proc:
.. code-block:: nimrod .. code-block:: nimrod
proc mycount(a, b: int): iterator (): int = proc mycount(a, b: int): iterator (): int =
return iterator (): int = result = iterator (): int =
var x = a var x = a
while x <= b: while x <= b:
yield x yield x
inc x inc x
let foo = mycount 1, 4 let foo = mycount(1, 4)
for f in foo(): for f in foo():
echo f echo f
@ -3373,9 +3376,9 @@ module to illustrate this:
## requires `x` and `y` to be of the same tuple type ## requires `x` and `y` to be of the same tuple type
## generic ``==`` operator for tuples that is lifted from the components ## generic ``==`` operator for tuples that is lifted from the components
## of `x` and `y`. ## of `x` and `y`.
result = true
for a, b in fields(x, y): for a, b in fields(x, y):
if a != b: return false if a != b: result = false
return true
Alternatively, the ``distinct`` type modifier can be applied to the type class Alternatively, the ``distinct`` type modifier can be applied to the type class
to allow each param matching the type class to bind to a different type. to allow each param matching the type class to bind to a different type.
@ -3997,9 +4000,9 @@ predicate:
proc re(pattern: semistatic[string]): TRegEx = proc re(pattern: semistatic[string]): TRegEx =
when isStatic(pattern): when isStatic(pattern):
return precompiledRegex(pattern) result = precompiledRegex(pattern)
else: else:
return compile(pattern) result = compile(pattern)
Static params can also appear in the signatures of generic types: Static params can also appear in the signatures of generic types:
@ -4103,6 +4106,59 @@ types that will match the typedesc param:
The constraint can be a concrete type or a type class. The constraint can be a concrete type or a type class.
Special Operators
=================
dot operators
-------------
Nimrod offers a special family of dot operators that can be used to
intercept and rewrite proc call and field access attempts, referring
to previously undeclared symbol names. They can be used to provide a
fluent interface to objects lying outside the static confines of the
Nimrod's type system such as values from dynamic scripting languages
or dynamic file formats such as JSON or XML.
When Nimrod encounters an expression that cannot be resolved by the
standard overload resolution rules, the current scope will be searched
for a dot operator that can be matched against a re-written form of
the expression, where the unknown field or proc name is converted to
an additional static string parameter:
.. code-block:: nimrod
a.b # becomes `.`(a, "b")
a.b(c, d) # becomes `.`(a, "b", c, d)
The matched dot operators can be symbols of any callable kind (procs,
templates and macros), depending on the desired effect:
.. code-block:: nimrod
proc `.` (js: PJsonNode, field: string): JSON = js[field]
var js = parseJson("{ x: 1, y: 2}")
echo js.x # outputs 1
echo js.y # outputs 2
The following dot operators are available:
operator `.`
------------
This operator will be matched against both field accesses and method calls.
operator `.()`
---------------
This operator will be matched exclusively against method calls. It has higher
precedence than the `.` operator and this allows you to handle expressions like
`x.y` and `x.y()` differently if you are interfacing with a scripting language
for example.
operator `.=`
-------------
This operator will be matched against assignments to missing fields.
.. code-block:: nimrod
a.b = c # becomes `.=`(a, "b", c)
Term rewriting macros Term rewriting macros
===================== =====================
@ -4506,7 +4562,7 @@ This is best illustrated by an example:
proc p*(x: A.T1): A.T1 = proc p*(x: A.T1): A.T1 =
# this works because the compiler has already # this works because the compiler has already
# added T1 to A's interface symbol table # added T1 to A's interface symbol table
return x + 1 result = x + 1
Import statement Import statement
@ -4755,42 +4811,6 @@ This may change in future versions of language, but for now use
the ``finalizer`` parameter to ``new``. the ``finalizer`` parameter to ``new``.
delegator pragma
----------------
**Note**: The design of the delegator feature is subject to change.
The delegator pragma can be used to intercept and rewrite proc call and field
access attempts referring to previously undeclared symbol names. It can be used
to provide a fluent interface to objects lying outside the static confines of
the Nimrod's type system such as values from dynamic scripting languages or
dynamic file formats such as JSON or XML.
A delegator is a special form of the `()` operator marked with the delagator
pragma. When Nimrod encounters an expression that cannot be resolved by the
standard overload resolution, any delegators in the current scope will be
matched against a rewritten form of the expression following the standard
signature matching rules. In the rewritten expression, the name of the unknown
proc or field name is inserted as an additional static string parameter always
appearing in the leading position:
.. code-block:: nimrod
a.b => delegator("b", a)
a.b(c, d) => delegator("b", a, c)
a b, c, d => delegator("a", b, c, d)
The delegators can be any callable symbol type (procs, templates, macros)
depending on the desired effect:
.. code-block:: nimrod
proc `()` (field: string, js: PJsonNode): JSON {.delegator.} = js[field]
var js = parseJson("{ x: 1, y: 2}")
echo js.x # outputs 1
echo js.y # outputs 2
procvar pragma procvar pragma
-------------- --------------
The `procvar`:idx: pragma is used to mark a proc that it can be passed to a The `procvar`:idx: pragma is used to mark a proc that it can be passed to a
@ -4873,16 +4893,16 @@ field which is used for runtime type identification is omitted. This is
necessary for binary compatibility with other compiled languages. necessary for binary compatibility with other compiled languages.
NoStackFrame pragma AsmNoStackFrame pragma
------------------- ----------------------
A proc can be marked with the `noStackFrame`:idx: pragma to tell the compiler A proc can be marked with the `AsmNoStackFrame`:idx: pragma to tell the compiler
it should not generate a stack frame for the proc. There are also no exit it should not generate a stack frame for the proc. There are also no exit
statements like ``return result;`` generated and the generated C function is statements like ``return result;`` generated and the generated C function is
declared as ``__declspec(naked)`` or ``__attribute__((naked))`` (depending on declared as ``__declspec(naked)`` or ``__attribute__((naked))`` (depending on
the used C compiler). the used C compiler).
**Note**: This pragma should only be used by procs which consist solely of assembler **Note**: This pragma should only be used by procs which consist solely of
statements. assembler statements.
error pragma error pragma
------------ ------------
@ -5134,6 +5154,8 @@ Example:
.. code-block:: nimrod .. code-block:: nimrod
{.deadCodeElim: on.} {.deadCodeElim: on.}
..
NoForward pragma NoForward pragma
---------------- ----------------
The `noforward`:idx: pragma can be used to turn on and off a special compilation The `noforward`:idx: pragma can be used to turn on and off a special compilation
@ -5180,6 +5202,7 @@ Example:
foo(10) foo(10)
Pragma pragma Pragma pragma
------------- -------------
@ -5197,7 +5220,7 @@ Example:
{.pragma: rtl, importc, dynlib: "client.dll", cdecl.} {.pragma: rtl, importc, dynlib: "client.dll", cdecl.}
proc p*(a, b: int): int {.rtl.} = proc p*(a, b: int): int {.rtl.} =
return a+b result = a+b
In the example a new pragma named ``rtl`` is introduced that either imports In the example a new pragma named ``rtl`` is introduced that either imports
a symbol from a dynamic library or exports the symbol for dynamic library a symbol from a dynamic library or exports the symbol for dynamic library
@ -5297,6 +5320,53 @@ strings automatically:
printf("hallo %s", "world") # "world" will be passed as C string printf("hallo %s", "world") # "world" will be passed as C string
Union pragma
------------
The `union`:idx: pragma can be applied to any ``object`` type. It means all
of the object's fields are overlaid in memory. This produces a ``union``
instead of a ``struct`` in the generated C/C++ code. The object declaration
then must not use inheritance or any GC'ed memory but this is currently not
checked.
**Future directions**: GC'ed memory should be allowed in unions and the GC
should scan unions conservatively.
Unchecked pragma
----------------
The `unchecked`:idx: pragma can be used to mark a named array as ``unchecked``
meaning its bounds are not checked. This is often useful when one wishes to
implement his own flexibly sized arrays. Additionally an unchecked array is
translated into a C array of undetermined size:
.. code-block:: nimrod
type
ArrayPart{.unchecked.} = array[0..0, int]
MySeq = object
len, cap: int
data: ArrayPart
Produces roughly this C code:
.. code-block:: C
typedef struct {
NI len;
NI cap;
NI data[];
} MySeq;
The bounds checking done at compile time is not disabled for now, so to access
``s.data[C]`` (where ``C`` is a constant) the array's index needs needs to
include ``C``.
The base type of the unchecked array may not contain any GC'ed memory but this
is currently not checked.
**Future directions**: GC'ed memory should be allowed in unchecked arrays and
there should be an explicit annotation of how the GC is to determine the
runtime size of the array.
Dynlib pragma for import Dynlib pragma for import
------------------------ ------------------------
With the `dynlib`:idx: pragma a procedure or a variable can be imported from With the `dynlib`:idx: pragma a procedure or a variable can be imported from

View file

@ -690,8 +690,8 @@ Nimrod provides the ability to overload procedures similar to C++:
.. code-block:: nimrod .. code-block:: nimrod
proc toString(x: int): string = ... proc toString(x: int): string = ...
proc toString(x: bool): string = proc toString(x: bool): string =
if x: return "true" if x: result = "true"
else: return "false" else: result = "false"
echo(toString(13)) # calls the toString(x: int) proc echo(toString(13)) # calls the toString(x: int) proc
echo(toString(true)) # calls the toString(x: bool) proc echo(toString(true)) # calls the toString(x: bool) proc
@ -1569,7 +1569,7 @@ This is best illustrated by an example:
proc p*(x: A.T1): A.T1 = proc p*(x: A.T1): A.T1 =
# this works because the compiler has already # this works because the compiler has already
# added T1 to A's interface symbol table # added T1 to A's interface symbol table
return x + 1 result = x + 1
A symbol of a module *can* be *qualified* with the ``module.symbol`` syntax. If A symbol of a module *can* be *qualified* with the ``module.symbol`` syntax. If
@ -1600,11 +1600,11 @@ rules apply:
.. code-block:: nimrod .. code-block:: nimrod
# Module A # Module A
proc x*(a: int): string = return $a proc x*(a: int): string = result = $a
.. code-block:: nimrod .. code-block:: nimrod
# Module B # Module B
proc x*(a: string): string = return $a proc x*(a: string): string = result = $a
.. code-block:: nimrod .. code-block:: nimrod
# Module C # Module C

View file

@ -126,7 +126,7 @@ The syntax for type conversions is ``destination_type(expression_to_convert)``
.. code-block:: nimrod .. code-block:: nimrod
proc getID(x: TPerson): int = proc getID(x: TPerson): int =
return TStudent(x).id TStudent(x).id
The ``EInvalidObjectConversion`` exception is raised if ``x`` is not a The ``EInvalidObjectConversion`` exception is raised if ``x`` is not a
``TStudent``. ``TStudent``.
@ -238,7 +238,7 @@ is needed:
proc host*(s: TSocket): int {.inline.} = proc host*(s: TSocket): int {.inline.} =
## getter of hostAddr ## getter of hostAddr
return s.FHost s.FHost
var var
s: TSocket s: TSocket

View file

@ -36,7 +36,7 @@ block mainLoop:
case x.kind case x.kind
of xmlEof: break mainLoop of xmlEof: break mainLoop
of xmlElementClose: break of xmlElementClose: break
else: nil else: discard
x.next() # skip ``xmlElementClose`` x.next() # skip ``xmlElementClose``
# now we have the description for the ``a`` element # now we have the description for the ``a`` element
var desc = "" var desc = ""

View file

@ -7,7 +7,7 @@ import os, streams, parsexml, strutils
if paramCount() < 1: if paramCount() < 1:
quit("Usage: htmltitle filename[.html]") quit("Usage: htmltitle filename[.html]")
var filename = addFileExt(ParamStr(1), "html") var filename = addFileExt(paramStr(1), "html")
var s = newFileStream(filename, fmRead) var s = newFileStream(filename, fmRead)
if s == nil: quit("cannot open the file " & filename) if s == nil: quit("cannot open the file " & filename)
var x: TXmlParser var x: TXmlParser
@ -23,13 +23,13 @@ while true:
title.add(x.charData) title.add(x.charData)
x.next() x.next()
if x.kind == xmlElementEnd and cmpIgnoreCase(x.elementName, "title") == 0: if x.kind == xmlElementEnd and cmpIgnoreCase(x.elementName, "title") == 0:
Echo("Title: " & title) echo("Title: " & title)
quit(0) # Success! quit(0) # Success!
else: else:
echo(x.errorMsgExpected("/title")) echo(x.errorMsgExpected("/title"))
of xmlEof: break # end of file reached of xmlEof: break # end of file reached
else: nil # ignore other events else: discard # ignore other events
x.close() x.close()
quit("Could not determine title!") quit("Could not determine title!")

View file

@ -58,6 +58,15 @@ Boot options:
proc exe(f: string): string = return addFileExt(f, ExeExt) proc exe(f: string): string = return addFileExt(f, ExeExt)
proc findNim(): string =
var nimrod = "nimrod".exe
result = "bin" / nimrod
if existsFile(result): return
for dir in split(getEnv("PATH"), PathSep):
if existsFile(dir / nimrod): return dir / nimrod
# assume there is a symlink to the exe or something:
return nimrod
proc exec(cmd: string) = proc exec(cmd: string) =
echo(cmd) echo(cmd)
if execShellCmd(cmd) != 0: quit("FAILURE") if execShellCmd(cmd) != 0: quit("FAILURE")
@ -70,15 +79,15 @@ const
compileNimInst = "-d:useLibzipSrc tools/niminst/niminst" compileNimInst = "-d:useLibzipSrc tools/niminst/niminst"
proc csource(args: string) = proc csource(args: string) =
exec("nimrod cc $1 -r $3 --var:version=$2 csource compiler/nimrod.ini $1" % exec("$4 cc $1 -r $3 --var:version=$2 csource compiler/nimrod.ini $1" %
[args, NimrodVersion, compileNimInst]) [args, NimrodVersion, compileNimInst, findNim()])
proc zip(args: string) = proc zip(args: string) =
exec("nimrod cc -r $2 --var:version=$1 zip compiler/nimrod.ini" % exec("$3 cc -r $2 --var:version=$1 zip compiler/nimrod.ini" %
[NimrodVersion, compileNimInst]) [NimrodVersion, compileNimInst, findNim()])
proc buildTool(toolname, args: string) = proc buildTool(toolname, args: string) =
exec("nimrod cc $# $#" % [args, toolname]) exec("$# cc $# $#" % [findNim(), args, toolname])
copyFile(dest="bin"/ splitFile(toolname).name.exe, source=toolname.exe) copyFile(dest="bin"/ splitFile(toolname).name.exe, source=toolname.exe)
proc inno(args: string) = proc inno(args: string) =
@ -90,13 +99,13 @@ proc inno(args: string) =
NimrodVersion) NimrodVersion)
proc install(args: string) = proc install(args: string) =
exec("nimrod cc -r $# --var:version=$# scripts compiler/nimrod.ini" % exec("$# cc -r $# --var:version=$# scripts compiler/nimrod.ini" %
[compileNimInst, NimrodVersion]) [findNim(), compileNimInst, NimrodVersion])
exec("sh ./install.sh $#" % args) exec("sh ./install.sh $#" % args)
proc web(args: string) = proc web(args: string) =
exec(("nimrod cc -r tools/nimweb.nim web/nimrod --putenv:nimrodversion=$#" & exec("$# cc -r tools/nimweb.nim web/nimrod --putenv:nimrodversion=$#" %
" --path:$#") % [NimrodVersion, getCurrentDir()]) [findNim(), NimrodVersion])
# -------------- boot --------------------------------------------------------- # -------------- boot ---------------------------------------------------------

View file

@ -516,7 +516,7 @@ proc last*(node: PNimrodNode): PNimrodNode {.compileTime.} = node[node.high]
const const
RoutineNodes* = {nnkProcDef, nnkMethodDef, nnkDo, nnkLambda} RoutineNodes* = {nnkProcDef, nnkMethodDef, nnkDo, nnkLambda, nnkIteratorDef}
AtomicNodes* = {nnkNone..nnkNilLit} AtomicNodes* = {nnkNone..nnkNilLit}
CallNodes* = {nnkCall, nnkInfix, nnkPrefix, nnkPostfix, nnkCommand, CallNodes* = {nnkCall, nnkInfix, nnkPrefix, nnkPostfix, nnkCommand,
nnkCallStrLit, nnkHiddenCallConv} nnkCallStrLit, nnkHiddenCallConv}

View file

@ -87,7 +87,7 @@ proc newRow(L: int): TRow =
proc properFreeResult(sqlres: mysql.PRES, row: cstringArray) = proc properFreeResult(sqlres: mysql.PRES, row: cstringArray) =
if row != nil: if row != nil:
while mysql.FetchRow(sqlres) != nil: nil while mysql.FetchRow(sqlres) != nil: discard
mysql.FreeResult(sqlres) mysql.FreeResult(sqlres)
iterator fastRows*(db: TDbConn, query: TSqlQuery, iterator fastRows*(db: TDbConn, query: TSqlQuery,

View file

@ -285,8 +285,8 @@ static N_INLINE(NI32, float32ToInt32)(float x) {
typedef struct TStringDesc* string; typedef struct TStringDesc* string;
/* declared size of a sequence: */ /* declared size of a sequence/variable length array: */
#if defined(__GNUC__) #if defined(__GNUC__) || defined(__clang__) || defined(_MSC_VER)
# define SEQ_DECL_SIZE /* empty is correct! */ # define SEQ_DECL_SIZE /* empty is correct! */
#else #else
# define SEQ_DECL_SIZE 1000000 # define SEQ_DECL_SIZE 1000000

View file

@ -0,0 +1,6 @@
[Package]
name = "docutils"
version = "0.9.0"
author = "Andreas Rumpf"
description = "Nimrod's reStructuredText processor."
license = "MIT"

View file

@ -61,9 +61,8 @@ proc getSourceLanguage*(name: string): TSourceLanguage =
if cmpIgnoreStyle(name, sourceLanguageToStr[i]) == 0: if cmpIgnoreStyle(name, sourceLanguageToStr[i]) == 0:
return i return i
result = langNone result = langNone
proc initGeneralTokenizer*(g: var TGeneralTokenizer, buf: cstring) =
proc initGeneralTokenizer*(g: var TGeneralTokenizer, buf: string) = g.buf = buf
g.buf = cstring(buf)
g.kind = low(TTokenClass) g.kind = low(TTokenClass)
g.start = 0 g.start = 0
g.length = 0 g.length = 0
@ -71,6 +70,8 @@ proc initGeneralTokenizer*(g: var TGeneralTokenizer, buf: string) =
var pos = 0 # skip initial whitespace: var pos = 0 # skip initial whitespace:
while g.buf[pos] in {' ', '\x09'..'\x0D'}: inc(pos) while g.buf[pos] in {' ', '\x09'..'\x0D'}: inc(pos)
g.pos = pos g.pos = pos
proc initGeneralTokenizer*(g: var TGeneralTokenizer, buf: string) =
initGeneralTokenizer(g, cstring(buf))
proc deinitGeneralTokenizer*(g: var TGeneralTokenizer) = proc deinitGeneralTokenizer*(g: var TGeneralTokenizer) =
discard discard

View file

@ -1543,7 +1543,7 @@ proc dirRaw(p: var TRstParser): PRstNode =
elif cmpIgnoreCase(result.sons[0].sons[0].text, "latex") == 0: elif cmpIgnoreCase(result.sons[0].sons[0].text, "latex") == 0:
dirRawAux(p, result, rnRawLatex, parseLiteralBlock) dirRawAux(p, result, rnRawLatex, parseLiteralBlock)
else: else:
rstMessage(p, meInvalidDirective, result.sons[0].text) rstMessage(p, meInvalidDirective, result.sons[0].sons[0].text)
else: else:
dirRawAux(p, result, rnRaw, parseSectionWrapper) dirRawAux(p, result, rnRaw, parseSectionWrapper)

90
lib/posix/epoll.nim Normal file
View file

@ -0,0 +1,90 @@
#
#
# Nimrod's Runtime Library
# (c) Copyright 2013 Dominik Picheta
#
# See the file "copying.txt", included in this
# distribution, for details about the copyright.
#
from posix import TSocketHandle
const
EPOLLIN* = 0x00000001
EPOLLPRI* = 0x00000002
EPOLLOUT* = 0x00000004
EPOLLERR* = 0x00000008
EPOLLHUP* = 0x00000010
EPOLLRDNORM* = 0x00000040
EPOLLRDBAND* = 0x00000080
EPOLLWRNORM* = 0x00000100
EPOLLWRBAND* = 0x00000200
EPOLLMSG* = 0x00000400
EPOLLRDHUP* = 0x00002000
EPOLLWAKEUP* = 1 shl 29
EPOLLONESHOT* = 1 shl 30
EPOLLET* = 1 shl 31
# Valid opcodes ( "op" parameter ) to issue to epoll_ctl().
const
EPOLL_CTL_ADD* = 1 # Add a file descriptor to the interface.
EPOLL_CTL_DEL* = 2 # Remove a file descriptor from the interface.
EPOLL_CTL_MOD* = 3 # Change file descriptor epoll_event structure.
type
epoll_data* {.importc: "union epoll_data",
header: "<sys/epoll.h>", pure, final.} = object # TODO: This is actually a union.
#thePtr* {.importc: "ptr".}: pointer
fd*: cint # \
#u32*: uint32
#u64*: uint64
epoll_event* {.importc: "struct epoll_event", header: "<sys/epoll.h>", pure, final.} = object
events*: uint32 # Epoll events
data*: epoll_data # User data variable
proc epoll_create*(size: cint): cint {.importc: "epoll_create",
header: "<sys/epoll.h>".}
## Creates an epoll instance. Returns an fd for the new instance.
## The "size" parameter is a hint specifying the number of file
## descriptors to be associated with the new instance. The fd
## returned by epoll_create() should be closed with close().
proc epoll_create1*(flags: cint): cint {.importc: "epoll_create1",
header: "<sys/epoll.h>".}
## Same as epoll_create but with an FLAGS parameter. The unused SIZE
## parameter has been dropped.
proc epoll_ctl*(epfd: cint; op: cint; fd: cint | TSocketHandle; event: ptr epoll_event): cint {.
importc: "epoll_ctl", header: "<sys/epoll.h>".}
## Manipulate an epoll instance "epfd". Returns 0 in case of success,
## -1 in case of error ( the "errno" variable will contain the
## specific error code ) The "op" parameter is one of the EPOLL_CTL_*
## constants defined above. The "fd" parameter is the target of the
## operation. The "event" parameter describes which events the caller
## is interested in and any associated user data.
proc epoll_wait*(epfd: cint; events: ptr epoll_event; maxevents: cint;
timeout: cint): cint {.importc: "epoll_wait",
header: "<sys/epoll.h>".}
## Wait for events on an epoll instance "epfd". Returns the number of
## triggered events returned in "events" buffer. Or -1 in case of
## error with the "errno" variable set to the specific error code. The
## "events" parameter is a buffer that will contain triggered
## events. The "maxevents" is the maximum number of events to be
## returned ( usually size of "events" ). The "timeout" parameter
## specifies the maximum wait time in milliseconds (-1 == infinite).
##
## This function is a cancellation point and therefore not marked with
## __THROW.
#proc epoll_pwait*(epfd: cint; events: ptr epoll_event; maxevents: cint;
# timeout: cint; ss: ptr sigset_t): cint {.
# importc: "epoll_pwait", header: "<sys/epoll.h>".}
# Same as epoll_wait, but the thread's signal mask is temporarily
# and atomically replaced with the one provided as parameter.
#
# This function is a cancellation point and therefore not marked with
# __THROW.

25
lib/posix/linux.nim Normal file
View file

@ -0,0 +1,25 @@
import posix
const
CSIGNAL* = 0x000000FF
CLONE_VM* = 0x00000100
CLONE_FS* = 0x00000200
CLONE_FILES* = 0x00000400
CLONE_SIGHAND* = 0x00000800
CLONE_PTRACE* = 0x00002000
CLONE_VFORK* = 0x00004000
CLONE_PARENT* = 0x00008000
CLONE_THREAD* = 0x00010000
CLONE_NEWNS* = 0x00020000
CLONE_SYSVSEM* = 0x00040000
CLONE_SETTLS* = 0x00080000
CLONE_PARENT_SETTID* = 0x00100000
CLONE_CHILD_CLEARTID* = 0x00200000
CLONE_DETACHED* = 0x00400000
CLONE_UNTRACED* = 0x00800000
CLONE_CHILD_SETTID* = 0x01000000
CLONE_STOPPED* = 0x02000000
# fn should be of type proc (a2: pointer): void {.cdecl.}
proc clone*(fn: pointer; child_stack: pointer; flags: cint;
arg: pointer; ptid: ptr TPid; tls: pointer; ctid: ptr TPid): cint {.importc, header: "<sched.h>".}

View file

@ -2066,6 +2066,7 @@ proc pthread_spin_unlock*(a1: ptr Tpthread_spinlock): cint {.
proc pthread_testcancel*() {.importc, header: "<pthread.h>".} proc pthread_testcancel*() {.importc, header: "<pthread.h>".}
proc exitnow*(code: int): void {.importc: "_exit", header: "<unistd.h>".}
proc access*(a1: cstring, a2: cint): cint {.importc, header: "<unistd.h>".} proc access*(a1: cstring, a2: cint): cint {.importc, header: "<unistd.h>".}
proc alarm*(a1: cint): cint {.importc, header: "<unistd.h>".} proc alarm*(a1: cint): cint {.importc, header: "<unistd.h>".}
proc chdir*(a1: cstring): cint {.importc, header: "<unistd.h>".} proc chdir*(a1: cstring): cint {.importc, header: "<unistd.h>".}
@ -2265,6 +2266,7 @@ proc gmtime_r*(a1: var TTime, a2: var Ttm): ptr Ttm {.importc, header: "<time.h>
proc localtime*(a1: var TTime): ptr Ttm {.importc, header: "<time.h>".} proc localtime*(a1: var TTime): ptr Ttm {.importc, header: "<time.h>".}
proc localtime_r*(a1: var TTime, a2: var Ttm): ptr Ttm {.importc, header: "<time.h>".} proc localtime_r*(a1: var TTime, a2: var Ttm): ptr Ttm {.importc, header: "<time.h>".}
proc mktime*(a1: var Ttm): TTime {.importc, header: "<time.h>".} proc mktime*(a1: var Ttm): TTime {.importc, header: "<time.h>".}
proc timegm*(a1: var Ttm): TTime {.importc, header: "<time.h>".}
proc nanosleep*(a1, a2: var Ttimespec): cint {.importc, header: "<time.h>".} proc nanosleep*(a1, a2: var Ttimespec): cint {.importc, header: "<time.h>".}
proc strftime*(a1: cstring, a2: int, a3: cstring, proc strftime*(a1: cstring, a2: int, a3: cstring,
a4: var Ttm): int {.importc, header: "<time.h>".} a4: var Ttm): int {.importc, header: "<time.h>".}
@ -2356,7 +2358,7 @@ proc FD_ZERO*(a1: var TFdSet) {.importc, header: "<sys/select.h>".}
proc pselect*(a1: cint, a2, a3, a4: ptr TFdSet, a5: ptr Ttimespec, proc pselect*(a1: cint, a2, a3, a4: ptr TFdSet, a5: ptr Ttimespec,
a6: var Tsigset): cint {.importc, header: "<sys/select.h>".} a6: var Tsigset): cint {.importc, header: "<sys/select.h>".}
proc select*(a1: cint, a2, a3, a4: ptr TFdSet, a5: ptr Ttimeval): cint {. proc select*(a1: cint | TSocketHandle, a2, a3, a4: ptr TFdSet, a5: ptr Ttimeval): cint {.
importc, header: "<sys/select.h>".} importc, header: "<sys/select.h>".}
when hasSpawnH: when hasSpawnH:

View file

@ -167,7 +167,7 @@ proc asyncSocket*(domain: TDomain = AF_INET, typ: TType = SOCK_STREAM,
result = newAsyncSocket() result = newAsyncSocket()
result.socket = socket(domain, typ, protocol, buffered) result.socket = socket(domain, typ, protocol, buffered)
result.proto = protocol result.proto = protocol
if result.socket == InvalidSocket: OSError(OSLastError()) if result.socket == invalidSocket: osError(osLastError())
result.socket.setBlocking(false) result.socket.setBlocking(false)
proc toAsyncSocket*(sock: TSocket, state: TInfo = SockConnected): PAsyncSocket = proc toAsyncSocket*(sock: TSocket, state: TInfo = SockConnected): PAsyncSocket =
@ -357,7 +357,7 @@ proc acceptAddr*(server: PAsyncSocket, client: var PAsyncSocket,
client.sslNeedAccept = false client.sslNeedAccept = false
client.info = SockConnected client.info = SockConnected
if c == InvalidSocket: SocketError(server.socket) if c == invalidSocket: socketError(server.socket)
c.setBlocking(false) # TODO: Needs to be tested. c.setBlocking(false) # TODO: Needs to be tested.
# deleg.open is set in ``toDelegate``. # deleg.open is set in ``toDelegate``.
@ -481,7 +481,7 @@ proc recvLine*(s: PAsyncSocket, line: var TaintedString): bool {.deprecated.} =
of RecvDisconnected: of RecvDisconnected:
result = true result = true
of RecvFail: of RecvFail:
s.SocketError(async = true) s.socketError(async = true)
result = false result = false
{.pop.} {.pop.}
@ -615,11 +615,11 @@ proc poll*(d: PDispatcher, timeout: int = 500): bool =
if d.hasDataBuffered(d.deleVal): if d.hasDataBuffered(d.deleVal):
hasDataBufferedCount.inc() hasDataBufferedCount.inc()
d.handleRead(d.deleVal) d.handleRead(d.deleVal)
if hasDataBufferedCount > 0: return True if hasDataBufferedCount > 0: return true
if readDg.len() == 0 and writeDg.len() == 0: if readDg.len() == 0 and writeDg.len() == 0:
## TODO: Perhaps this shouldn't return if errorDg has something? ## TODO: Perhaps this shouldn't return if errorDg has something?
return False return false
if select(readDg, writeDg, errorDg, timeout) != 0: if select(readDg, writeDg, errorDg, timeout) != 0:
for i in 0..len(d.delegates)-1: for i in 0..len(d.delegates)-1:

922
lib/pure/asyncio2.nim Normal file
View file

@ -0,0 +1,922 @@
#
#
# Nimrod's Runtime Library
# (c) Copyright 2014 Dominik Picheta
#
# See the file "copying.txt", included in this
# distribution, for details about the copyright.
#
import os, oids, tables, strutils, macros
import sockets2, net
## Asyncio2
## --------
##
## This module implements a brand new asyncio module based on Futures.
## IOCP is used under the hood on Windows and the selectors module is used for
## other operating systems.
# -- Futures
type
PFutureBase* = ref object of PObject
cb: proc () {.closure.}
finished: bool
PFuture*[T] = ref object of PFutureBase
value: T
error: ref EBase
proc newFuture*[T](): PFuture[T] =
## Creates a new future.
new(result)
result.finished = false
proc complete*[T](future: PFuture[T], val: T) =
## Completes ``future`` with value ``val``.
assert(not future.finished, "Future already finished, cannot finish twice.")
assert(future.error == nil)
future.value = val
future.finished = true
if future.cb != nil:
future.cb()
proc fail*[T](future: PFuture[T], error: ref EBase) =
## Completes ``future`` with ``error``.
assert(not future.finished, "Future already finished, cannot finish twice.")
future.finished = true
future.error = error
if future.cb != nil:
future.cb()
proc `callback=`*(future: PFutureBase, cb: proc () {.closure.}) =
## Sets the callback proc to be called when the future completes.
##
## If future has already completed then ``cb`` will be called immediately.
##
## **Note**: You most likely want the other ``callback`` setter which
## passes ``future`` as a param to the callback.
future.cb = cb
if future.finished:
future.cb()
proc `callback=`*[T](future: PFuture[T],
cb: proc (future: PFuture[T]) {.closure.}) =
## Sets the callback proc to be called when the future completes.
##
## If future has already completed then ``cb`` will be called immediately.
future.callback = proc () = cb(future)
proc read*[T](future: PFuture[T]): T =
## Retrieves the value of ``future``. Future must be finished otherwise
## this function will fail with a ``EInvalidValue`` exception.
##
## If the result of the future is an error then that error will be raised.
if future.finished:
if future.error != nil: raise future.error
return future.value
else:
# TODO: Make a custom exception type for this?
raise newException(EInvalidValue, "Future still in progress.")
proc finished*[T](future: PFuture[T]): bool =
## Determines whether ``future`` has completed.
##
## ``True`` may indicate an error or a value. Use ``hasError`` to distinguish.
future.finished
proc failed*[T](future: PFuture[T]): bool =
## Determines whether ``future`` completed with an error.
future.error != nil
# TODO: Get rid of register. Do it implicitly.
when defined(windows) or defined(nimdoc):
import winlean
type
TCompletionKey = dword
TCompletionData* = object
sock: TSocketHandle
cb: proc (sock: TSocketHandle, bytesTransferred: DWORD,
errcode: TOSErrorCode) {.closure.}
PDispatcher* = ref object
ioPort: THandle
hasHandles: bool
TCustomOverlapped = object
Internal*: DWORD
InternalHigh*: DWORD
Offset*: DWORD
OffsetHigh*: DWORD
hEvent*: THANDLE
data*: TCompletionData
PCustomOverlapped = ptr TCustomOverlapped
proc newDispatcher*(): PDispatcher =
## Creates a new Dispatcher instance.
new result
result.ioPort = CreateIOCompletionPort(INVALID_HANDLE_VALUE, 0, 0, 1)
proc register*(p: PDispatcher, sock: TSocketHandle) =
## Registers ``sock`` with the dispatcher ``p``.
if CreateIOCompletionPort(sock.THandle, p.ioPort,
cast[TCompletionKey](sock), 1) == 0:
OSError(OSLastError())
p.hasHandles = true
proc poll*(p: PDispatcher, timeout = 500) =
## Waits for completion events and processes them.
if not p.hasHandles:
raise newException(EInvalidValue, "No handles registered in dispatcher.")
let llTimeout =
if timeout == -1: winlean.INFINITE
else: timeout.int32
var lpNumberOfBytesTransferred: DWORD
var lpCompletionKey: ULONG
var lpOverlapped: POverlapped
let res = GetQueuedCompletionStatus(p.ioPort, addr lpNumberOfBytesTransferred,
addr lpCompletionKey, addr lpOverlapped, llTimeout).bool
# http://stackoverflow.com/a/12277264/492186
# TODO: http://www.serverframework.com/handling-multiple-pending-socket-read-and-write-operations.html
var customOverlapped = cast[PCustomOverlapped](lpOverlapped)
if res:
# This is useful for ensuring the reliability of the overlapped struct.
assert customOverlapped.data.sock == lpCompletionKey.TSocketHandle
customOverlapped.data.cb(customOverlapped.data.sock,
lpNumberOfBytesTransferred, TOSErrorCode(-1))
dealloc(customOverlapped)
else:
let errCode = OSLastError()
if lpOverlapped != nil:
assert customOverlapped.data.sock == lpCompletionKey.TSocketHandle
customOverlapped.data.cb(customOverlapped.data.sock,
lpNumberOfBytesTransferred, errCode)
dealloc(customOverlapped)
else:
if errCode.int32 == WAIT_TIMEOUT:
# Timed out
discard
else: OSError(errCode)
var connectExPtr: pointer = nil
var acceptExPtr: pointer = nil
var getAcceptExSockAddrsPtr: pointer = nil
proc initPointer(s: TSocketHandle, func: var pointer, guid: var TGUID): bool =
# Ref: https://github.com/powdahound/twisted/blob/master/twisted/internet/iocpreactor/iocpsupport/winsock_pointers.c
var bytesRet: DWord
func = nil
result = WSAIoctl(s, SIO_GET_EXTENSION_FUNCTION_POINTER, addr guid,
sizeof(TGUID).dword, addr func, sizeof(pointer).DWORD,
addr bytesRet, nil, nil) == 0
proc initAll() =
let dummySock = socket()
if not initPointer(dummySock, connectExPtr, WSAID_CONNECTEX):
OSError(OSLastError())
if not initPointer(dummySock, acceptExPtr, WSAID_ACCEPTEX):
OSError(OSLastError())
if not initPointer(dummySock, getAcceptExSockAddrsPtr, WSAID_GETACCEPTEXSOCKADDRS):
OSError(OSLastError())
proc connectEx(s: TSocketHandle, name: ptr TSockAddr, namelen: cint,
lpSendBuffer: pointer, dwSendDataLength: dword,
lpdwBytesSent: PDWORD, lpOverlapped: POverlapped): bool =
if connectExPtr.isNil: raise newException(EInvalidValue, "Need to initialise ConnectEx().")
let func =
cast[proc (s: TSocketHandle, name: ptr TSockAddr, namelen: cint,
lpSendBuffer: pointer, dwSendDataLength: dword,
lpdwBytesSent: PDWORD, lpOverlapped: POverlapped): bool {.stdcall.}](connectExPtr)
result = func(s, name, namelen, lpSendBuffer, dwSendDataLength, lpdwBytesSent,
lpOverlapped)
proc acceptEx(listenSock, acceptSock: TSocketHandle, lpOutputBuffer: pointer,
dwReceiveDataLength, dwLocalAddressLength,
dwRemoteAddressLength: DWORD, lpdwBytesReceived: PDWORD,
lpOverlapped: POverlapped): bool =
if acceptExPtr.isNil: raise newException(EInvalidValue, "Need to initialise AcceptEx().")
let func =
cast[proc (listenSock, acceptSock: TSocketHandle, lpOutputBuffer: pointer,
dwReceiveDataLength, dwLocalAddressLength,
dwRemoteAddressLength: DWORD, lpdwBytesReceived: PDWORD,
lpOverlapped: POverlapped): bool {.stdcall.}](acceptExPtr)
result = func(listenSock, acceptSock, lpOutputBuffer, dwReceiveDataLength,
dwLocalAddressLength, dwRemoteAddressLength, lpdwBytesReceived,
lpOverlapped)
proc getAcceptExSockaddrs(lpOutputBuffer: pointer,
dwReceiveDataLength, dwLocalAddressLength, dwRemoteAddressLength: DWORD,
LocalSockaddr: ptr ptr TSockAddr, LocalSockaddrLength: lpint,
RemoteSockaddr: ptr ptr TSockAddr, RemoteSockaddrLength: lpint) =
if getAcceptExSockAddrsPtr.isNil:
raise newException(EInvalidValue, "Need to initialise getAcceptExSockAddrs().")
let func =
cast[proc (lpOutputBuffer: pointer,
dwReceiveDataLength, dwLocalAddressLength,
dwRemoteAddressLength: DWORD, LocalSockaddr: ptr ptr TSockAddr,
LocalSockaddrLength: lpint, RemoteSockaddr: ptr ptr TSockAddr,
RemoteSockaddrLength: lpint) {.stdcall.}](getAcceptExSockAddrsPtr)
func(lpOutputBuffer, dwReceiveDataLength, dwLocalAddressLength,
dwRemoteAddressLength, LocalSockaddr, LocalSockaddrLength,
RemoteSockaddr, RemoteSockaddrLength)
proc connect*(p: PDispatcher, socket: TSocketHandle, address: string, port: TPort,
af = AF_INET): PFuture[int] =
## Connects ``socket`` to server at ``address:port``.
##
## Returns a ``PFuture`` which will complete when the connection succeeds
## or an error occurs.
var retFuture = newFuture[int]()# TODO: Change to void when that regression is fixed.
# Apparently ``ConnectEx`` expects the socket to be initially bound:
var saddr: Tsockaddr_in
saddr.sin_family = int16(toInt(af))
saddr.sin_port = 0
saddr.sin_addr.s_addr = INADDR_ANY
if bindAddr(socket, cast[ptr TSockAddr](addr(saddr)),
sizeof(saddr).TSockLen) < 0'i32:
OSError(OSLastError())
var aiList = getAddrInfo(address, port, af)
var success = false
var lastError: TOSErrorCode
var it = aiList
while it != nil:
# "the OVERLAPPED structure must remain valid until the I/O completes"
# http://blogs.msdn.com/b/oldnewthing/archive/2011/02/02/10123392.aspx
var ol = cast[PCustomOverlapped](alloc0(sizeof(TCustomOverlapped)))
ol.data = TCompletionData(sock: socket, cb:
proc (sock: TSocketHandle, bytesCount: DWord, errcode: TOSErrorCode) =
if not retFuture.finished:
if errcode == TOSErrorCode(-1):
retFuture.complete(0)
else:
retFuture.fail(newException(EOS, osErrorMsg(errcode)))
)
var ret = connectEx(socket, it.ai_addr, sizeof(TSockAddrIn).cint,
nil, 0, nil, cast[POverlapped](ol))
if ret:
# Request to connect completed immediately.
success = true
retFuture.complete(0)
# We don't deallocate ``ol`` here because even though this completed
# immediately poll will still be notified about its completion and it will
# free ``ol``.
break
else:
lastError = OSLastError()
if lastError.int32 == ERROR_IO_PENDING:
# In this case ``ol`` will be deallocated in ``poll``.
success = true
break
else:
dealloc(ol)
success = false
it = it.ai_next
dealloc(aiList)
if not success:
retFuture.fail(newException(EOS, osErrorMsg(lastError)))
return retFuture
proc recv*(p: PDispatcher, socket: TSocketHandle, size: int,
flags: int = 0): PFuture[string] =
## Reads ``size`` bytes from ``socket``. Returned future will complete once
## all of the requested data is read. If socket is disconnected during the
## recv operation then the future may complete with only a part of the
## requested data read. If socket is disconnected and no data is available
## to be read then the future will complete with a value of ``""``.
var retFuture = newFuture[string]()
var dataBuf: TWSABuf
dataBuf.buf = newString(size)
dataBuf.len = size
var bytesReceived: DWord
var flagsio = flags.dword
var ol = cast[PCustomOverlapped](alloc0(sizeof(TCustomOverlapped)))
ol.data = TCompletionData(sock: socket, cb:
proc (sock: TSocketHandle, bytesCount: DWord, errcode: TOSErrorCode) =
if not retFuture.finished:
if errcode == TOSErrorCode(-1):
if bytesCount == 0 and dataBuf.buf[0] == '\0':
retFuture.complete("")
else:
var data = newString(size)
copyMem(addr data[0], addr dataBuf.buf[0], size)
retFuture.complete($data)
else:
retFuture.fail(newException(EOS, osErrorMsg(errcode)))
)
let ret = WSARecv(socket, addr dataBuf, 1, addr bytesReceived,
addr flagsio, cast[POverlapped](ol), nil)
if ret == -1:
let err = OSLastError()
if err.int32 != ERROR_IO_PENDING:
retFuture.fail(newException(EOS, osErrorMsg(err)))
dealloc(ol)
elif ret == 0 and bytesReceived == 0 and dataBuf.buf[0] == '\0':
# We have to ensure that the buffer is empty because WSARecv will tell
# us immediatelly when it was disconnected, even when there is still
# data in the buffer.
# We want to give the user as much data as we can. So we only return
# the empty string (which signals a disconnection) when there is
# nothing left to read.
retFuture.complete("")
# TODO: "For message-oriented sockets, where a zero byte message is often
# allowable, a failure with an error code of WSAEDISCON is used to
# indicate graceful closure."
# ~ http://msdn.microsoft.com/en-us/library/ms741688%28v=vs.85%29.aspx
else:
# Request to read completed immediately.
var data = newString(size)
copyMem(addr data[0], addr dataBuf.buf[0], size)
retFuture.complete($data)
# We don't deallocate ``ol`` here because even though this completed
# immediately poll will still be notified about its completion and it will
# free ``ol``.
return retFuture
proc send*(p: PDispatcher, socket: TSocketHandle, data: string): PFuture[int] =
## Sends ``data`` to ``socket``. The returned future will complete once all
## data has been sent.
var retFuture = newFuture[int]()
var dataBuf: TWSABuf
dataBuf.buf = data
dataBuf.len = data.len
var bytesReceived, flags: DWord
var ol = cast[PCustomOverlapped](alloc0(sizeof(TCustomOverlapped)))
ol.data = TCompletionData(sock: socket, cb:
proc (sock: TSocketHandle, bytesCount: DWord, errcode: TOSErrorCode) =
if not retFuture.finished:
if errcode == TOSErrorCode(-1):
retFuture.complete(0)
else:
retFuture.fail(newException(EOS, osErrorMsg(errcode)))
)
let ret = WSASend(socket, addr dataBuf, 1, addr bytesReceived,
flags, cast[POverlapped](ol), nil)
if ret == -1:
let err = osLastError()
if err.int32 != ERROR_IO_PENDING:
retFuture.fail(newException(EOS, osErrorMsg(err)))
dealloc(ol)
else:
retFuture.complete(0)
# We don't deallocate ``ol`` here because even though this completed
# immediately poll will still be notified about its completion and it will
# free ``ol``.
return retFuture
proc acceptAddr*(p: PDispatcher, socket: TSocketHandle):
PFuture[tuple[address: string, client: TSocketHandle]] =
## Accepts a new connection. Returns a future containing the client socket
## corresponding to that connection and the remote address of the client.
## The future will complete when the connection is successfully accepted.
var retFuture = newFuture[tuple[address: string, client: TSocketHandle]]()
var clientSock = socket()
if clientSock == OSInvalidSocket: osError(osLastError())
const lpOutputLen = 1024
var lpOutputBuf = newString(lpOutputLen)
var dwBytesReceived: DWORD
let dwReceiveDataLength = 0.DWORD # We don't want any data to be read.
let dwLocalAddressLength = DWORD(sizeof (TSockaddr_in) + 16)
let dwRemoteAddressLength = DWORD(sizeof(TSockaddr_in) + 16)
template completeAccept(): stmt {.immediate, dirty.} =
var listenSock = socket
let setoptRet = setsockopt(clientSock, SOL_SOCKET,
SO_UPDATE_ACCEPT_CONTEXT, addr listenSock,
sizeof(listenSock).TSockLen)
if setoptRet != 0: osError(osLastError())
var LocalSockaddr, RemoteSockaddr: ptr TSockAddr
var localLen, remoteLen: int32
getAcceptExSockaddrs(addr lpOutputBuf[0], dwReceiveDataLength,
dwLocalAddressLength, dwRemoteAddressLength,
addr LocalSockaddr, addr localLen,
addr RemoteSockaddr, addr remoteLen)
# TODO: IPv6. Check ``sa_family``. http://stackoverflow.com/a/9212542/492186
retFuture.complete(
(address: $inet_ntoa(cast[ptr Tsockaddr_in](remoteSockAddr).sin_addr),
client: clientSock)
)
var ol = cast[PCustomOverlapped](alloc0(sizeof(TCustomOverlapped)))
ol.data = TCompletionData(sock: socket, cb:
proc (sock: TSocketHandle, bytesCount: DWord, errcode: TOSErrorCode) =
if not retFuture.finished:
if errcode == TOSErrorCode(-1):
completeAccept()
else:
retFuture.fail(newException(EOS, osErrorMsg(errcode)))
)
# http://msdn.microsoft.com/en-us/library/windows/desktop/ms737524%28v=vs.85%29.aspx
let ret = acceptEx(socket, clientSock, addr lpOutputBuf[0],
dwReceiveDataLength,
dwLocalAddressLength,
dwRemoteAddressLength,
addr dwBytesReceived, cast[POverlapped](ol))
if not ret:
let err = osLastError()
if err.int32 != ERROR_IO_PENDING:
retFuture.fail(newException(EOS, osErrorMsg(err)))
dealloc(ol)
else:
completeAccept()
# We don't deallocate ``ol`` here because even though this completed
# immediately poll will still be notified about its completion and it will
# free ``ol``.
return retFuture
initAll()
else:
import selectors
from posix import EINTR, EAGAIN, EINPROGRESS, EWOULDBLOCK, MSG_PEEK
type
TCallback = proc (sock: TSocketHandle): bool {.closure.}
PData* = ref object of PObject
sock: TSocketHandle
readCBs: seq[TCallback]
writeCBs: seq[TCallback]
PDispatcher* = ref object
selector: PSelector
proc newDispatcher*(): PDispatcher =
new result
result.selector = newSelector()
proc update(p: PDispatcher, sock: TSocketHandle, events: set[TEvent]) =
assert sock in p.selector
echo("Update: ", events)
if events == {}:
discard p.selector.unregister(sock)
else:
discard p.selector.update(sock, events)
proc addRead(p: PDispatcher, sock: TSocketHandle, cb: TCallback) =
if sock notin p.selector:
var data = PData(sock: sock, readCBs: @[cb], writeCBs: @[])
p.selector.register(sock, {EvRead}, data.PObject)
else:
p.selector[sock].data.PData.readCBs.add(cb)
p.update(sock, p.selector[sock].events + {EvRead})
proc addWrite(p: PDispatcher, sock: TSocketHandle, cb: TCallback) =
if sock notin p.selector:
var data = PData(sock: sock, readCBs: @[], writeCBs: @[cb])
p.selector.register(sock, {EvWrite}, data.PObject)
else:
p.selector[sock].data.PData.writeCBs.add(cb)
p.update(sock, p.selector[sock].events + {EvWrite})
proc poll*(p: PDispatcher, timeout = 500) =
for info in p.selector.select(timeout):
let data = PData(info.key.data)
assert data.sock == info.key.fd
echo("R: ", data.readCBs.len, " W: ", data.writeCBs.len, ". ", info.events)
if EvRead in info.events:
var newReadCBs: seq[TCallback] = @[]
for cb in data.readCBs:
if not cb(data.sock):
# Callback wants to be called again.
newReadCBs.add(cb)
data.readCBs = newReadCBs
if EvWrite in info.events:
var newWriteCBs: seq[TCallback] = @[]
for cb in data.writeCBs:
if not cb(data.sock):
# Callback wants to be called again.
newWriteCBs.add(cb)
data.writeCBs = newWriteCBs
var newEvents: set[TEvent]
if data.readCBs.len != 0: newEvents = {EvRead}
if data.writeCBs.len != 0: newEvents = newEvents + {EvWrite}
p.update(data.sock, newEvents)
proc connect*(p: PDispatcher, socket: TSocketHandle, address: string, port: TPort,
af = AF_INET): PFuture[int] =
var retFuture = newFuture[int]()
proc cb(sock: TSocketHandle): bool =
# We have connected.
retFuture.complete(0)
return true
var aiList = getAddrInfo(address, port, af)
var success = false
var lastError: TOSErrorCode
var it = aiList
while it != nil:
var ret = connect(socket, it.ai_addr, it.ai_addrlen.TSocklen)
if ret == 0:
# Request to connect completed immediately.
success = true
retFuture.complete(0)
break
else:
lastError = osLastError()
if lastError.int32 == EINTR or lastError.int32 == EINPROGRESS:
success = true
addWrite(p, socket, cb)
break
else:
success = false
it = it.ai_next
dealloc(aiList)
if not success:
retFuture.fail(newException(EOS, osErrorMsg(lastError)))
return retFuture
proc recv*(p: PDispatcher, socket: TSocketHandle, size: int,
flags: int = 0): PFuture[string] =
var retFuture = newFuture[string]()
var readBuffer = newString(size)
var sizeRead = 0
proc cb(sock: TSocketHandle): bool =
result = true
let netSize = size - sizeRead
let res = recv(sock, addr readBuffer[sizeRead], netSize, flags.cint)
if res < 0:
let lastError = osLastError()
if lastError.int32 notin {EINTR, EWOULDBLOCK, EAGAIN}:
retFuture.fail(newException(EOS, osErrorMsg(lastError)))
else:
result = false # We still want this callback to be called.
elif res == 0:
# Disconnected
if sizeRead == 0:
retFuture.complete("")
else:
readBuffer.setLen(sizeRead)
retFuture.complete(readBuffer)
else:
sizeRead.inc(res)
if res != netSize:
result = false # We want to read all the data requested.
else:
retFuture.complete(readBuffer)
addRead(p, socket, cb)
return retFuture
proc send*(p: PDispatcher, socket: TSocketHandle, data: string): PFuture[int] =
var retFuture = newFuture[int]()
var written = 0
proc cb(sock: TSocketHandle): bool =
result = true
let netSize = data.len-written
var d = data.cstring
let res = send(sock, addr d[written], netSize, 0.cint)
if res < 0:
let lastError = osLastError()
if lastError.int32 notin {EINTR, EWOULDBLOCK, EAGAIN}:
retFuture.fail(newException(EOS, osErrorMsg(lastError)))
else:
result = false # We still want this callback to be called.
else:
written.inc(res)
if res != netSize:
result = false # We still have data to send.
else:
retFuture.complete(0)
addWrite(p, socket, cb)
return retFuture
proc acceptAddr*(p: PDispatcher, socket: TSocketHandle):
PFuture[tuple[address: string, client: TSocketHandle]] =
var retFuture = newFuture[tuple[address: string, client: TSocketHandle]]()
proc cb(sock: TSocketHandle): bool =
result = true
var sockAddress: Tsockaddr_in
var addrLen = sizeof(sockAddress).TSocklen
var client = accept(sock, cast[ptr TSockAddr](addr(sockAddress)),
addr(addrLen))
if client == osInvalidSocket:
let lastError = osLastError()
assert lastError.int32 notin {EWOULDBLOCK, EAGAIN}
if lastError.int32 == EINTR:
return false
else:
retFuture.fail(newException(EOS, osErrorMsg(lastError)))
else:
retFuture.complete(($inet_ntoa(sockAddress.sin_addr), client))
addRead(p, socket, cb)
return retFuture
proc accept*(p: PDispatcher, socket: TSocketHandle): PFuture[TSocketHandle] =
## Accepts a new connection. Returns a future containing the client socket
## corresponding to that connection.
## The future will complete when the connection is successfully accepted.
var retFut = newFuture[TSocketHandle]()
var fut = p.acceptAddr(socket)
fut.callback =
proc (future: PFuture[tuple[address: string, client: TSocketHandle]]) =
assert future.finished
if future.failed:
retFut.fail(future.error)
else:
retFut.complete(future.read.client)
return retFut
# -- Await Macro
template createCb*(cbName, varNameIterSym, retFutureSym: expr): stmt {.immediate, dirty.} =
proc cbName {.closure.} =
if not varNameIterSym.finished:
var next = varNameIterSym()
if next == nil:
assert retFutureSym.finished, "Async procedure's return Future was not finished."
else:
next.callback = cbName
template createVar(futSymName: string, asyncProc: PNimrodNode,
valueReceiver: expr) {.immediate, dirty.} =
# TODO: Used template here due to bug #926
result = newNimNode(nnkStmtList)
var futSym = newIdentNode(futSymName) #genSym(nskVar, "future")
result.add newVarStmt(futSym, asyncProc) # -> var future<x> = y
result.add newNimNode(nnkYieldStmt).add(futSym) # -> yield future<x>
valueReceiver = newDotExpr(futSym, newIdentNode("read")) # -> future<x>.read
proc processBody(node, retFutureSym: PNimrodNode): PNimrodNode {.compileTime.} =
result = node
case node.kind
of nnkReturnStmt:
result = newNimNode(nnkStmtList)
result.add newCall(newIdentNode("complete"), retFutureSym,
if node[0].kind == nnkEmpty: newIdentNode("result") else: node[0])
result.add newNimNode(nnkYieldStmt).add(newNilLit())
of nnkCommand:
if node[0].ident == !"await":
case node[1].kind
of nnkIdent:
# await x
result = newNimNode(nnkYieldStmt).add(node[1]) # -> yield x
of nnkCall:
# await foo(p, x)
var futureValue: PNimrodNode
createVar("future" & $node[1][0].toStrLit, node[1], futureValue)
result.add futureValue
else:
error("Invalid node kind in 'await', got: " & $node[1].kind)
elif node[1].kind == nnkCommand and node[1][0].kind == nnkIdent and
node[1][0].ident == !"await":
# foo await x
var newCommand = node
createVar("future" & $node[0].ident, node[1][0], newCommand[1])
result.add newCommand
of nnkVarSection, nnkLetSection:
case node[0][2].kind
of nnkCommand:
if node[0][2][0].ident == !"await":
# var x = await y
var newVarSection = node # TODO: Should this use copyNimNode?
createVar("future" & $node[0][0].ident, node[0][2][1],
newVarSection[0][2])
result.add newVarSection
else: discard
of nnkAsgn:
case node[1].kind
of nnkCommand:
if node[1][0].ident == !"await":
# x = await y
var newAsgn = node
createVar("future" & $node[0].ident, node[1][1], newAsgn[1])
result.add newAsgn
else: discard
of nnkDiscardStmt:
# discard await x
if node[0][0].ident == !"await":
var dummy = newNimNode(nnkStmtList)
createVar("futureDiscard_" & $toStrLit(node[0][1]), node[0][1], dummy)
else: discard
for i in 0 .. <result.len:
result[i] = processBody(result[i], retFutureSym)
#echo(treeRepr(result))
proc getName(node: PNimrodNode): string {.compileTime.} =
case node.kind
of nnkPostfix:
return $node[1].ident
of nnkIdent:
return $node.ident
else:
assert false
macro async*(prc: stmt): stmt {.immediate.} =
expectKind(prc, nnkProcDef)
hint("Processing " & prc[0].getName & " as an async proc.")
# Verify that the return type is a PFuture[T]
if prc[3][0].kind == nnkIdent:
error("Expected return type of 'PFuture' got '" & $prc[3][0] & "'")
elif prc[3][0].kind == nnkBracketExpr:
if $prc[3][0][0] != "PFuture":
error("Expected return type of 'PFuture' got '" & $prc[3][0][0] & "'")
# TODO: Why can't I use genSym? I get illegal capture errors for Syms.
# TODO: It seems genSym is broken. Change all usages back to genSym when fixed
var outerProcBody = newNimNode(nnkStmtList)
# -> var retFuture = newFuture[T]()
var retFutureSym = newIdentNode("retFuture") #genSym(nskVar, "retFuture")
outerProcBody.add(
newVarStmt(retFutureSym,
newCall(
newNimNode(nnkBracketExpr).add(
newIdentNode("newFuture"),
prc[3][0][1])))) # Get type from return type of this proc.
# -> iterator nameIter(): PFutureBase {.closure.} =
# -> var result: T
# -> <proc_body>
# -> complete(retFuture, result)
var iteratorNameSym = newIdentNode($prc[0].getName & "Iter") #genSym(nskIterator, $prc[0].ident & "Iter")
var procBody = prc[6].processBody(retFutureSym)
procBody.insert(0, newNimNode(nnkVarSection).add(
newIdentDefs(newIdentNode("result"), prc[3][0][1]))) # -> var result: T
procBody.add(
newCall(newIdentNode("complete"),
retFutureSym, newIdentNode("result"))) # -> complete(retFuture, result)
var closureIterator = newProc(iteratorNameSym, [newIdentNode("PFutureBase")],
procBody, nnkIteratorDef)
closureIterator[4] = newNimNode(nnkPragma).add(newIdentNode("closure"))
outerProcBody.add(closureIterator)
# -> var nameIterVar = nameIter
# -> var first = nameIterVar()
var varNameIterSym = newIdentNode($prc[0].getName & "IterVar") #genSym(nskVar, $prc[0].ident & "IterVar")
var varNameIter = newVarStmt(varNameIterSym, iteratorNameSym)
outerProcBody.add varNameIter
var varFirstSym = genSym(nskVar, "first")
var varFirst = newVarStmt(varFirstSym, newCall(varNameIterSym))
outerProcBody.add varFirst
# -> createCb(cb, nameIter, retFuture)
var cbName = newIdentNode("cb")
var procCb = newCall("createCb", cbName, varNameIterSym, retFutureSym)
outerProcBody.add procCb
# -> first.callback = cb
outerProcBody.add newAssignment(
newDotExpr(varFirstSym, newIdentNode("callback")),
cbName)
# -> return retFuture
outerProcBody.add newNimNode(nnkReturnStmt).add(retFutureSym)
result = prc
# Remove the 'async' pragma.
for i in 0 .. <result[4].len:
if result[4][i].ident == !"async":
result[4].del(i)
result[6] = outerProcBody
echo(toStrLit(result))
proc recvLine*(p: PDispatcher, socket: TSocketHandle): PFuture[string] {.async.} =
## Reads a line of data from ``socket``. Returned future will complete once
## a full line is read or an error occurs.
##
## If a full line is read ``\r\L`` is not
## added to ``line``, however if solely ``\r\L`` is read then ``line``
## will be set to it.
##
## If the socket is disconnected, ``line`` will be set to ``""``.
template addNLIfEmpty(): stmt =
if result.len == 0:
result.add("\c\L")
result = ""
var c = ""
while true:
c = await p.recv(socket, 1)
if c.len == 0:
return
if c == "\r":
c = await p.recv(socket, 1, MSG_PEEK)
if c.len > 0 and c == "\L":
discard await p.recv(socket, 1)
addNLIfEmpty()
return
elif c == "\L":
addNLIfEmpty()
return
add(result.string, c)
when isMainModule:
var p = newDispatcher()
var sock = socket()
sock.setBlocking false
when false:
# Await tests
proc main(p: PDispatcher): PFuture[int] {.async.} =
discard await p.connect(sock, "irc.freenode.net", TPort(6667))
while true:
var line = await p.recvLine(sock)
echo("Line is: ", line.repr)
if line == "":
echo "Disconnected"
break
proc peekTest(p: PDispatcher): PFuture[int] {.async.} =
discard await p.connect(sock, "localhost", TPort(6667))
while true:
var line = await p.recv(sock, 1, MSG_PEEK)
var line2 = await p.recv(sock, 1)
echo(line.repr)
echo(line2.repr)
echo("---")
if line2 == "": break
sleep(500)
var f = main(p)
else:
when false:
var f = p.connect(sock, "irc.freenode.org", TPort(6667))
f.callback =
proc (future: PFuture[int]) =
echo("Connected in future!")
echo(future.read)
for i in 0 .. 50:
var recvF = p.recv(sock, 10)
recvF.callback =
proc (future: PFuture[string]) =
echo("Read ", future.read.len, ": ", future.read.repr)
else:
sock.bindAddr(TPort(6667))
sock.listen()
proc onAccept(future: PFuture[TSocketHandle]) =
echo "Accepted"
var t = p.send(future.read, "test\c\L")
t.callback =
proc (future: PFuture[int]) =
echo(future.read)
var f = p.accept(sock)
f.callback = onAccept
var f = p.accept(sock)
f.callback = onAccept
while true:
p.poll()

View file

@ -241,3 +241,7 @@ proc `<=`*[A](s, t: TSet[A]): bool =
proc `==`*[A](s, t: TSet[A]): bool = proc `==`*[A](s, t: TSet[A]): bool =
s.counter == t.counter and s <= t s.counter == t.counter and s <= t
proc map*[A, B](data: TSet[A], op: proc (x: A): B {.closure.}): TSet[B] =
result = initSet[B]()
for item in data: result.incl(op(item))

View file

@ -14,7 +14,7 @@
type type
TLibHandle* = pointer ## a handle to a dynamically loaded library TLibHandle* = pointer ## a handle to a dynamically loaded library
proc loadLib*(path: string): TLibHandle proc loadLib*(path: string, global_symbols=false): TLibHandle
## loads a library from `path`. Returns nil if the library could not ## loads a library from `path`. Returns nil if the library could not
## be loaded. ## be loaded.
@ -53,6 +53,7 @@ when defined(posix):
# #
var var
RTLD_NOW {.importc: "RTLD_NOW", header: "<dlfcn.h>".}: int RTLD_NOW {.importc: "RTLD_NOW", header: "<dlfcn.h>".}: int
RTLD_GLOBAL {.importc: "RTLD_GLOBAL", header: "<dlfcn.h>".}: int
proc dlclose(lib: TLibHandle) {.importc, header: "<dlfcn.h>".} proc dlclose(lib: TLibHandle) {.importc, header: "<dlfcn.h>".}
proc dlopen(path: CString, mode: int): TLibHandle {. proc dlopen(path: CString, mode: int): TLibHandle {.
@ -60,7 +61,10 @@ when defined(posix):
proc dlsym(lib: TLibHandle, name: cstring): pointer {. proc dlsym(lib: TLibHandle, name: cstring): pointer {.
importc, header: "<dlfcn.h>".} importc, header: "<dlfcn.h>".}
proc loadLib(path: string): TLibHandle = return dlopen(path, RTLD_NOW) proc loadLib(path: string, global_symbols=false): TLibHandle =
var flags = RTLD_NOW
if global_symbols: flags = flags or RTLD_GLOBAL
return dlopen(path, flags)
proc loadLib(): TLibHandle = return dlopen(nil, RTLD_NOW) proc loadLib(): TLibHandle = return dlopen(nil, RTLD_NOW)
proc unloadLib(lib: TLibHandle) = dlclose(lib) proc unloadLib(lib: TLibHandle) = dlclose(lib)
proc symAddr(lib: TLibHandle, name: cstring): pointer = proc symAddr(lib: TLibHandle, name: cstring): pointer =
@ -81,7 +85,7 @@ elif defined(windows) or defined(dos):
proc getProcAddress(lib: THINSTANCE, name: cstring): pointer {. proc getProcAddress(lib: THINSTANCE, name: cstring): pointer {.
importc: "GetProcAddress", header: "<windows.h>", stdcall.} importc: "GetProcAddress", header: "<windows.h>", stdcall.}
proc loadLib(path: string): TLibHandle = proc loadLib(path: string, global_symbols=false): TLibHandle =
result = cast[TLibHandle](winLoadLibrary(path)) result = cast[TLibHandle](winLoadLibrary(path))
proc loadLib(): TLibHandle = proc loadLib(): TLibHandle =
result = cast[TLibHandle](winLoadLibrary(nil)) result = cast[TLibHandle](winLoadLibrary(nil))

View file

@ -1,12 +1,17 @@
# #
# #
# Nimrod's Runtime Library # Nimrod's Runtime Library
# (c) Copyright 2012 Andreas Rumpf # (c) Copyright 2014 Andreas Rumpf
# #
# See the file "copying.txt", included in this # See the file "copying.txt", included in this
# distribution, for details about the copyright. # distribution, for details about the copyright.
# #
## **Warning**: This module uses ``immediate`` macros which are known to
## cause problems. Do yourself a favor and import the module
## as ``from htmlgen import nil`` and then fully qualify the macros.
##
##
## This module implements a simple `XML`:idx: and `HTML`:idx: code ## This module implements a simple `XML`:idx: and `HTML`:idx: code
## generator. Each commonly used HTML tag has a corresponding macro ## generator. Each commonly used HTML tag has a corresponding macro
## that generates a string with its HTML representation. ## that generates a string with its HTML representation.
@ -15,11 +20,11 @@
## ##
## .. code-block:: nimrod ## .. code-block:: nimrod
## var nim = "Nimrod" ## var nim = "Nimrod"
## echo h1(a(href="http://nimrod-code.org", nim)) ## echo h1(a(href="http://nimrod-lang.org", nim))
## ##
## Writes the string:: ## Writes the string::
## ##
## <h1><a href="http://nimrod-code.org">Nimrod</a></h1> ## <h1><a href="http://nimrod-lang.org">Nimrod</a></h1>
## ##
import import

55
lib/pure/net.nim Normal file
View file

@ -0,0 +1,55 @@
#
#
# Nimrod's Runtime Library
# (c) Copyright 2014 Dominik Picheta
#
# See the file "copying.txt", included in this
# distribution, for details about the copyright.
#
## This module implements a high-level cross-platform sockets interface.
import sockets2, os
type
TSocket* = TSocketHandle
proc bindAddr*(socket: TSocket, port = TPort(0), address = "") {.
tags: [FReadIO].} =
## binds an address/port number to a socket.
## Use address string in dotted decimal form like "a.b.c.d"
## or leave "" for any address.
if address == "":
var name: TSockaddr_in
when defined(windows):
name.sin_family = toInt(AF_INET).int16
else:
name.sin_family = toInt(AF_INET)
name.sin_port = htons(int16(port))
name.sin_addr.s_addr = htonl(INADDR_ANY)
if bindAddr(socket, cast[ptr TSockAddr](addr(name)),
sizeof(name).TSocklen) < 0'i32:
osError(osLastError())
else:
var aiList = getAddrInfo(address, port, AF_INET)
if bindAddr(socket, aiList.ai_addr, aiList.ai_addrlen.TSocklen) < 0'i32:
dealloc(aiList)
osError(osLastError())
dealloc(aiList)
proc setBlocking*(s: TSocket, blocking: bool) {.tags: [].} =
## Sets blocking mode on socket
when defined(Windows):
var mode = clong(ord(not blocking)) # 1 for non-blocking, 0 for blocking
if ioctlsocket(s, FIONBIO, addr(mode)) == -1:
osError(osLastError())
else: # BSD sockets
var x: int = fcntl(s, F_GETFL, 0)
if x == -1:
osError(osLastError())
else:
var mode = if blocking: x and not O_NONBLOCK else: x or O_NONBLOCK
if fcntl(s, F_SETFL, mode) == -1:
osError(osLastError())

View file

@ -67,7 +67,7 @@ when withThreads:
proc hookAux(st: TStackTrace, costs: int) = proc hookAux(st: TStackTrace, costs: int) =
# this is quite performance sensitive! # this is quite performance sensitive!
when withThreads: Acquire profilingLock when withThreads: acquire profilingLock
inc totalCalls inc totalCalls
var last = high(st) var last = high(st)
while last > 0 and isNil(st[last]): dec last while last > 0 and isNil(st[last]): dec last
@ -106,7 +106,7 @@ proc hookAux(st: TStackTrace, costs: int) =
h = ((5 * h) + 1) and high(profileData) h = ((5 * h) + 1) and high(profileData)
inc chain inc chain
maxChainLen = max(maxChainLen, chain) maxChainLen = max(maxChainLen, chain)
when withThreads: Release profilingLock when withThreads: release profilingLock
when defined(memProfiler): when defined(memProfiler):
const const

View file

@ -1037,7 +1037,10 @@ proc execShellCmd*(command: string): int {.rtl, extern: "nos$1",
## the process has finished. To execute a program without having a ## the process has finished. To execute a program without having a
## shell involved, use the `execProcess` proc of the `osproc` ## shell involved, use the `execProcess` proc of the `osproc`
## module. ## module.
when defined(linux):
result = c_system(command) shr 8 result = c_system(command) shr 8
else:
result = c_system(command)
# Environment handling cannot be put into RTL, because the ``envPairs`` # Environment handling cannot be put into RTL, because the ``envPairs``
# iterator depends on ``environment``. # iterator depends on ``environment``.
@ -1189,7 +1192,8 @@ iterator walkFiles*(pattern: string): string {.tags: [FReadDir].} =
res = findFirstFile(pattern, f) res = findFirstFile(pattern, f)
if res != -1: if res != -1:
while true: while true:
if not skipFindData(f): if not skipFindData(f) and
(f.dwFileAttributes and FILE_ATTRIBUTE_DIRECTORY) == 0'i32:
yield splitFile(pattern).dir / extractFilename(getFilename(f)) yield splitFile(pattern).dir / extractFilename(getFilename(f))
if findNextFile(res, f) == 0'i32: break if findNextFile(res, f) == 0'i32: break
findClose(res) findClose(res)

View file

@ -13,13 +13,16 @@
include "system/inclrtl" include "system/inclrtl"
import import
strutils, os, strtabs, streams, sequtils strutils, os, strtabs, streams
when defined(windows): when defined(windows):
import winlean import winlean
else: else:
import posix import posix
when defined(linux):
import linux
type type
TProcess = object of TObject TProcess = object of TObject
when defined(windows): when defined(windows):
@ -44,7 +47,7 @@ type
poStdErrToStdOut, ## merge stdout and stderr to the stdout stream poStdErrToStdOut, ## merge stdout and stderr to the stdout stream
poParentStreams ## use the parent's streams poParentStreams ## use the parent's streams
template poUseShell*: TProcessOption {.deprecated.} = poUsePath const poUseShell* {.deprecated.} = poUsePath
## Deprecated alias for poUsePath. ## Deprecated alias for poUsePath.
proc quoteShellWindows*(s: string): string {.noSideEffect, rtl, extern: "nosp$1".} = proc quoteShellWindows*(s: string): string {.noSideEffect, rtl, extern: "nosp$1".} =
@ -165,6 +168,9 @@ proc processID*(p: PProcess): int {.rtl, extern: "nosp$1".} =
proc waitForExit*(p: PProcess, timeout: int = -1): int {.rtl, proc waitForExit*(p: PProcess, timeout: int = -1): int {.rtl,
extern: "nosp$1", tags: [].} extern: "nosp$1", tags: [].}
## waits for the process to finish and returns `p`'s error code. ## waits for the process to finish and returns `p`'s error code.
##
## **Warning**: Be careful when using waitForExit for processes created without
## poParentStreams because they may fill output buffers, causing deadlock.
proc peekExitCode*(p: PProcess): int {.tags: [].} proc peekExitCode*(p: PProcess): int {.tags: [].}
## return -1 if the process is still running. Otherwise the process' exit code ## return -1 if the process is still running. Otherwise the process' exit code
@ -590,6 +596,23 @@ elif not defined(useNimRtl):
copyMem(result[i], addr(x[0]), x.len+1) copyMem(result[i], addr(x[0]), x.len+1)
inc(i) inc(i)
type TStartProcessData = object
sysCommand: cstring
sysArgs: cstringArray
sysEnv: cstringArray
workingDir: cstring
pStdin, pStdout, pStderr, pErrorPipe: array[0..1, cint]
optionPoUsePath: bool
optionPoParentStreams: bool
optionPoStdErrToStdOut: bool
proc startProcessAuxSpawn(data: TStartProcessData): TPid {.tags: [FExecIO, FReadEnv].}
proc startProcessAuxFork(data: TStartProcessData): TPid {.tags: [FExecIO, FReadEnv].}
{.push stacktrace: off, profiler: off.}
proc startProcessAfterFork(data: ptr TStartProcessData) {.
tags: [FExecIO, FReadEnv], cdecl.}
{.pop.}
proc startProcess(command: string, proc startProcess(command: string,
workingDir: string = "", workingDir: string = "",
args: openArray[string] = [], args: openArray[string] = [],
@ -604,100 +627,48 @@ elif not defined(useNimRtl):
pipe(pStderr) != 0'i32: pipe(pStderr) != 0'i32:
osError(osLastError()) osError(osLastError())
var sys_command: string var sysCommand: string
var sys_args_raw: seq[string] var sysArgsRaw: seq[string]
if poEvalCommand in options: if poEvalCommand in options:
sys_command = "/bin/sh" sysCommand = "/bin/sh"
sys_args_raw = @[sys_command, "-c", command] sysArgsRaw = @[sysCommand, "-c", command]
assert args.len == 0 assert args.len == 0
else: else:
sys_command = command sysCommand = command
sys_args_raw = @[command] sysArgsRaw = @[command]
for arg in args.items: for arg in args.items:
sys_args_raw.add arg sysArgsRaw.add arg
var sys_args = allocCStringArray(sys_args_raw)
finally: deallocCStringArray(sys_args)
var pid: TPid var pid: TPid
when defined(posix_spawn) and not defined(useFork):
var attr: Tposix_spawnattr
var fops: Tposix_spawn_file_actions
template chck(e: expr) = var sysArgs = allocCStringArray(sysArgsRaw)
if e != 0'i32: osError(osLastError()) finally: deallocCStringArray(sysArgs)
chck posix_spawn_file_actions_init(fops) var sysEnv = if env == nil:
chck posix_spawnattr_init(attr) envToCStringArray()
var mask: Tsigset
chck sigemptyset(mask)
chck posix_spawnattr_setsigmask(attr, mask)
chck posix_spawnattr_setpgroup(attr, 0'i32)
chck posix_spawnattr_setflags(attr, POSIX_SPAWN_USEVFORK or
POSIX_SPAWN_SETSIGMASK or
POSIX_SPAWN_SETPGROUP)
if poParentStreams notin options:
chck posix_spawn_file_actions_addclose(fops, pStdin[writeIdx])
chck posix_spawn_file_actions_adddup2(fops, pStdin[readIdx], readIdx)
chck posix_spawn_file_actions_addclose(fops, pStdout[readIdx])
chck posix_spawn_file_actions_adddup2(fops, pStdout[writeIdx], writeIdx)
chck posix_spawn_file_actions_addclose(fops, pStderr[readIdx])
if poStdErrToStdOut in options:
chck posix_spawn_file_actions_adddup2(fops, pStdout[writeIdx], 2)
else: else:
chck posix_spawn_file_actions_adddup2(fops, p_stderr[writeIdx], 2) envToCStringArray(env)
var sys_env = if env == nil: envToCStringArray() else: envToCStringArray(env) finally: deallocCStringArray(sysEnv)
var res: cint
# This is incorrect!
if workingDir.len > 0: os.setCurrentDir(workingDir)
if poUsePath in options:
res = posix_spawnp(pid, sys_command, fops, attr, sys_args, sys_env)
else:
res = posix_spawn(pid, sys_command, fops, attr, sys_args, sys_env)
deallocCStringArray(sys_env)
discard posix_spawn_file_actions_destroy(fops)
discard posix_spawnattr_destroy(attr)
chck res
else: var data: TStartProcessData
pid = fork() data.sysCommand = sysCommand
if pid < 0: osError(osLastError()) data.sysArgs = sysArgs
if pid == 0: data.sysEnv = sysEnv
## child process: data.pStdin = pStdin
data.pStdout = pStdout
data.pStderr = pStderr
data.optionPoParentStreams = poParentStreams in options
data.optionPoUsePath = poUsePath in options
data.optionPoStdErrToStdOut = poStdErrToStdOut in options
data.workingDir = workingDir
if poParentStreams notin options:
discard close(p_stdin[writeIdx])
if dup2(p_stdin[readIdx], readIdx) < 0: osError(osLastError())
discard close(p_stdout[readIdx])
if dup2(p_stdout[writeIdx], writeIdx) < 0: osError(osLastError())
discard close(p_stderr[readIdx])
if poStdErrToStdOut in options:
if dup2(p_stdout[writeIdx], 2) < 0: osError(osLastError())
else:
if dup2(p_stderr[writeIdx], 2) < 0: osError(osLastError())
# Create a new process group when defined(posix_spawn) and not defined(useFork) and not defined(useClone) and not defined(linux):
if setpgid(0, 0) == -1: quit("setpgid call failed: " & $strerror(errno)) pid = startProcessAuxSpawn(data)
else:
pid = startProcessAuxFork(data)
if workingDir.len > 0: os.setCurrentDir(workingDir)
if env == nil:
if poUsePath in options:
discard execvp(sys_command, sys_args)
else:
discard execv(sys_command, sys_args)
else:
var c_env = envToCStringArray(env)
if poUsePath in options:
discard execvpe(sys_command, sys_args, c_env)
else:
discard execve(sys_command, sys_args, c_env)
# too risky to raise an exception here:
quit("execve call failed: " & $strerror(errno))
# Parent process. Copy process information. # Parent process. Copy process information.
if poEchoCmd in options: if poEchoCmd in options:
echo(command, " ", join(args, " ")) echo(command, " ", join(args, " "))
@ -723,6 +694,137 @@ elif not defined(useNimRtl):
discard close(pStdin[readIdx]) discard close(pStdin[readIdx])
discard close(pStdout[writeIdx]) discard close(pStdout[writeIdx])
proc startProcessAuxSpawn(data: TStartProcessData): TPid =
var attr: Tposix_spawnattr
var fops: Tposix_spawn_file_actions
template chck(e: expr) =
if e != 0'i32: osError(osLastError())
chck posix_spawn_file_actions_init(fops)
chck posix_spawnattr_init(attr)
var mask: Tsigset
chck sigemptyset(mask)
chck posix_spawnattr_setsigmask(attr, mask)
chck posix_spawnattr_setpgroup(attr, 0'i32)
chck posix_spawnattr_setflags(attr, POSIX_SPAWN_USEVFORK or
POSIX_SPAWN_SETSIGMASK or
POSIX_SPAWN_SETPGROUP)
if not data.optionPoParentStreams:
chck posix_spawn_file_actions_addclose(fops, data.pStdin[writeIdx])
chck posix_spawn_file_actions_adddup2(fops, data.pStdin[readIdx], readIdx)
chck posix_spawn_file_actions_addclose(fops, data.pStdout[readIdx])
chck posix_spawn_file_actions_adddup2(fops, data.pStdout[writeIdx], writeIdx)
chck posix_spawn_file_actions_addclose(fops, data.pStderr[readIdx])
if data.optionPoStdErrToStdOut:
chck posix_spawn_file_actions_adddup2(fops, data.pStdout[writeIdx], 2)
else:
chck posix_spawn_file_actions_adddup2(fops, data.pStderr[writeIdx], 2)
var res: cint
# FIXME: chdir is global to process
if data.workingDir.len > 0:
setCurrentDir($data.workingDir)
var pid: TPid
if data.optionPoUsePath:
res = posix_spawnp(pid, data.sysCommand, fops, attr, data.sysArgs, data.sysEnv)
else:
res = posix_spawn(pid, data.sysCommand, fops, attr, data.sysArgs, data.sysEnv)
discard posix_spawn_file_actions_destroy(fops)
discard posix_spawnattr_destroy(attr)
chck res
return pid
proc startProcessAuxFork(data: TStartProcessData): TPid =
if pipe(data.pErrorPipe) != 0:
osError(osLastError())
finally:
discard close(data.pErrorPipe[readIdx])
var pid: TPid
var dataCopy = data
when defined(useClone):
const stackSize = 65536
let stackEnd = cast[clong](alloc(stackSize))
let stack = cast[pointer](stackEnd + stackSize)
let fn: pointer = startProcessAfterFork
pid = clone(fn, stack,
cint(CLONE_VM or CLONE_VFORK or SIGCHLD),
pointer(addr dataCopy), nil, nil, nil)
discard close(data.pErrorPipe[writeIdx])
dealloc(stack)
else:
pid = fork()
if pid == 0:
startProcessAfterFork(addr(dataCopy))
exitnow(1)
discard close(data.pErrorPipe[writeIdx])
if pid < 0: osError(osLastError())
var error: cint
let sizeRead = read(data.pErrorPipe[readIdx], addr error, sizeof(error))
if sizeRead == sizeof(error):
osError($strerror(error))
return pid
{.push stacktrace: off, profiler: off.}
proc startProcessFail(data: ptr TStartProcessData) =
var error: cint = errno
discard write(data.pErrorPipe[writeIdx], addr error, sizeof(error))
exitnow(1)
when defined(macosx):
var environ {.importc.}: cstringArray
proc startProcessAfterFork(data: ptr TStartProcessData) =
# Warning: no GC here!
# Or anythink that touches global structures - all called nimrod procs
# must be marked with noStackFrame. Inspect C code after making changes.
if not data.optionPoParentStreams:
discard close(data.pStdin[writeIdx])
if dup2(data.pStdin[readIdx], readIdx) < 0:
startProcessFail(data)
discard close(data.pStdout[readIdx])
if dup2(data.pStdout[writeIdx], writeIdx) < 0:
startProcessFail(data)
discard close(data.pStderr[readIdx])
if data.optionPoStdErrToStdOut:
if dup2(data.pStdout[writeIdx], 2) < 0:
startProcessFail(data)
else:
if dup2(data.pStderr[writeIdx], 2) < 0:
startProcessFail(data)
if data.workingDir.len > 0:
if chdir(data.workingDir) < 0:
startProcessFail(data)
discard close(data.pErrorPipe[readIdx])
discard fcntl(data.pErrorPipe[writeIdx], F_SETFD, FD_CLOEXEC)
if data.optionPoUsePath:
when defined(macosx):
# MacOSX doesn't have execvpe, so we need workaround.
# On MacOSX we can arrive here only from fork, so this is safe:
environ = data.sysEnv
discard execvp(data.sysCommand, data.sysArgs)
else:
discard execvpe(data.sysCommand, data.sysArgs, data.sysEnv)
else:
discard execve(data.sysCommand, data.sysArgs, data.sysEnv)
startProcessFail(data)
{.pop}
proc close(p: PProcess) = proc close(p: PProcess) =
if p.inStream != nil: close(p.inStream) if p.inStream != nil: close(p.inStream)
if p.outStream != nil: close(p.outStream) if p.outStream != nil: close(p.outStream)
@ -791,7 +893,10 @@ elif not defined(useNimRtl):
proc csystem(cmd: cstring): cint {.nodecl, importc: "system".} proc csystem(cmd: cstring): cint {.nodecl, importc: "system".}
proc execCmd(command: string): int = proc execCmd(command: string): int =
when defined(linux):
result = csystem(command) shr 8 result = csystem(command) shr 8
else:
result = csystem(command)
proc createFdSet(fd: var TFdSet, s: seq[PProcess], m: var int) = proc createFdSet(fd: var TFdSet, s: seq[PProcess], m: var int) =
FD_ZERO(fd) FD_ZERO(fd)

View file

@ -836,7 +836,9 @@ iterator findAll*(s: string, pattern: TPeg, start = 0): string =
while i < s.len: while i < s.len:
c.ml = 0 c.ml = 0
var L = rawMatch(s, pattern, i, c) var L = rawMatch(s, pattern, i, c)
if L < 0: break if L < 0:
inc(i, 1)
else:
yield substr(s, i, i+L-1) yield substr(s, i, i+L-1)
inc(i, L) inc(i, L)

250
lib/pure/selectors.nim Normal file
View file

@ -0,0 +1,250 @@
#
#
# Nimrod's Runtime Library
# (c) Copyright 2014 Dominik Picheta
#
# See the file "copying.txt", included in this
# distribution, for details about the copyright.
#
# TODO: Docs.
import tables, os, unsigned, hashes
when defined(linux): import posix, epoll
elif defined(windows): import winlean
proc hash*(x: TSocketHandle): THash {.borrow.}
type
TEvent* = enum
EvRead, EvWrite
PSelectorKey* = ref object
fd*: TSocketHandle
events*: set[TEvent] ## The events which ``fd`` listens for.
data*: PObject ## User object.
TReadyInfo* = tuple[key: PSelectorKey, events: set[TEvent]]
when defined(linux) or defined(nimdoc):
type
PSelector* = ref object
epollFD: cint
events: array[64, ptr epoll_event]
fds: TTable[TSocketHandle, PSelectorKey]
proc createEventStruct(events: set[TEvent], fd: TSocketHandle): epoll_event =
if EvRead in events:
result.events = EPOLLIN
if EvWrite in events:
result.events = result.events or EPOLLOUT
result.data.fd = fd.cint
proc register*(s: PSelector, fd: TSocketHandle, events: set[TEvent],
data: PObject): PSelectorKey {.discardable.} =
## Registers file descriptor ``fd`` to selector ``s`` with a set of TEvent
## ``events``.
if s.fds.hasKey(fd):
raise newException(EInvalidValue, "File descriptor already exists.")
var event = createEventStruct(events, fd)
if epoll_ctl(s.epollFD, EPOLL_CTL_ADD, fd, addr(event)) != 0:
OSError(OSLastError())
var key = PSelectorKey(fd: fd, events: events, data: data)
s.fds[fd] = key
result = key
proc update*(s: PSelector, fd: TSocketHandle,
events: set[TEvent]): PSelectorKey {.discardable.} =
## Updates the events which ``fd`` wants notifications for.
if not s.fds.hasKey(fd):
raise newException(EInvalidValue, "File descriptor not found.")
var event = createEventStruct(events, fd)
s.fds[fd].events = events
echo("About to update")
if epoll_ctl(s.epollFD, EPOLL_CTL_MOD, fd, addr(event)) != 0:
OSError(OSLastError())
echo("finished updating")
result = s.fds[fd]
proc unregister*(s: PSelector, fd: TSocketHandle): PSelectorKey {.discardable.} =
if not s.fds.hasKey(fd):
raise newException(EInvalidValue, "File descriptor not found.")
if epoll_ctl(s.epollFD, EPOLL_CTL_DEL, fd, nil) != 0:
OSError(OSLastError())
result = s.fds[fd]
s.fds.del(fd)
proc close*(s: PSelector) =
if s.epollFD.close() != 0: OSError(OSLastError())
dealloc(addr s.events) # TODO: Test this
proc select*(s: PSelector, timeout: int): seq[TReadyInfo] =
##
## The ``events`` field of the returned ``key`` contains the original events
## for which the ``fd`` was bound. This is contrary to the ``events`` field
## of the ``TReadyInfo`` tuple which determines which events are ready
## on the ``fd``.
result = @[]
let evNum = epoll_wait(s.epollFD, s.events[0], 64.cint, timeout.cint)
if evNum < 0: OSError(OSLastError())
if evNum == 0: return @[]
for i in 0 .. <evNum:
var evSet: set[TEvent] = {}
if (s.events[i].events and EPOLLIN) != 0: evSet = evSet + {EvRead}
if (s.events[i].events and EPOLLOUT) != 0: evSet = evSet + {EvWrite}
let selectorKey = s.fds[s.events[i].data.fd.TSocketHandle]
result.add((selectorKey, evSet))
proc newSelector*(): PSelector =
new result
result.epollFD = epoll_create(64)
result.events = cast[array[64, ptr epoll_event]](alloc0(sizeof(epoll_event)*64))
result.fds = initTable[TSocketHandle, PSelectorKey]()
if result.epollFD < 0:
OSError(OSLastError())
proc contains*(s: PSelector, fd: TSocketHandle): bool =
## Determines whether selector contains a file descriptor.
return s.fds.hasKey(fd)
proc `[]`*(s: PSelector, fd: TSocketHandle): PSelectorKey =
## Retrieves the selector key for ``fd``.
return s.fds[fd]
elif defined(windows):
type
PSelector* = ref object
fds: TTable[TSocketHandle, PSelectorKey]
proc register*(s: PSelector, fd: TSocketHandle, events: set[TEvent],
data: PObject): PSelectorKey {.discardable.} =
if s.fds.hasKey(fd):
raise newException(EInvalidValue, "File descriptor already exists.")
var sk = PSelectorKey(fd: fd, events: events, data: data)
s.fds[fd] = sk
result = sk
proc update*(s: PSelector, fd: TSocketHandle,
events: set[TEvent]): PSelectorKey {.discardable.} =
## Updates the events which ``fd`` wants notifications for.
if not s.fds.hasKey(fd):
raise newException(EInvalidValue, "File descriptor not found.")
s.fds[fd].events = events
result = s.fds[fd]
proc unregister*(s: PSelector, fd: TSocketHandle): PSelectorKey {.discardable.} =
result = s.fds[fd]
s.fds.del(fd)
proc close*(s: PSelector) = nil
proc timeValFromMilliseconds(timeout: int): TTimeVal =
if timeout != -1:
var seconds = timeout div 1000
result.tv_sec = seconds.int32
result.tv_usec = ((timeout - seconds * 1000) * 1000).int32
proc createFdSet(rd, wr: var TFdSet, fds: TTable[TSocketHandle, PSelectorKey],
m: var int) =
FD_ZERO(rd); FD_ZERO(wr)
for k, v in pairs(fds):
if EvRead in v.events:
m = max(m, int(k))
FD_SET(k, rd)
if EvWrite in v.events:
m = max(m, int(k))
FD_SET(k, wr)
proc getReadyFDs(rd, wr: var TFdSet, fds: TTable[TSocketHandle, PSelectorKey]):
seq[TReadyInfo] =
result = @[]
for k, v in pairs(fds):
var events: set[TEvent] = {}
if FD_ISSET(k, rd) != 0'i32:
events = events + {EvRead}
if FD_ISSET(k, wr) != 0'i32:
events = events + {EvWrite}
result.add((v, events))
proc select(fds: TTable[TSocketHandle, PSelectorKey], timeout = 500):
seq[TReadyInfo] =
var tv {.noInit.}: TTimeVal = timeValFromMilliseconds(timeout)
var rd, wr: TFdSet
var m = 0
createFdSet(rd, wr, fds, m)
var retCode = 0
if timeout != -1:
retCode = int(select(TSocketHandle(m+1), addr(rd), addr(wr), nil, addr(tv)))
else:
retCode = int(select(TSocketHandle(m+1), addr(rd), addr(wr), nil, nil))
if retCode < 0:
OSError(OSLastError())
elif retCode == 0:
return @[]
else:
return getReadyFDs(rd, wr, fds)
proc select*(s: PSelector, timeout: int): seq[TReadyInfo] =
result = select(s.fds, timeout)
proc newSelector*(): PSelector =
new result
result.fds = initTable[TSocketHandle, PSelectorKey]()
proc contains*(s: PSelector, fd: TSocketHandle): bool =
return s.fds.hasKey(fd)
proc `[]`*(s: PSelector, fd: TSocketHandle): PSelectorKey =
return s.fds[fd]
elif defined(bsd) or defined(macosx):
# TODO: kqueue
{.error: "Sorry your platform is not supported yet.".}
else:
{.error: "Sorry your platform is not supported.".}
when isMainModule:
# Select()
import sockets
type
PSockWrapper = ref object of PObject
sock: TSocket
var sock = socket()
sock.setBlocking(false)
sock.connect("irc.freenode.net", TPort(6667))
var selector = newSelector()
var data = PSockWrapper(sock: sock)
let key = selector.register(sock.getFD, {EvWrite}, data)
var i = 0
while true:
let ready = selector.select(1000)
echo ready.len
if ready.len > 0: echo ready[0].events
i.inc
if i == 6:
assert selector.unregister(sock.getFD).fd == sock.getFD
selector.close()
break

213
lib/pure/sockets2.nim Normal file
View file

@ -0,0 +1,213 @@
#
#
# Nimrod's Runtime Library
# (c) Copyright 2014 Dominik Picheta
#
# See the file "copying.txt", included in this
# distribution, for details about the copyright.
#
## This module implements a low-level cross-platform sockets interface. Look
## at the ``net`` module for the higher-level version.
import unsigned, os
when hostos == "solaris":
{.passl: "-lsocket -lnsl".}
when defined(Windows):
import winlean
else:
import posix
export fcntl, F_GETFL, O_NONBLOCK, F_SETFL
export TSocketHandle, TSockaddr_in, TAddrinfo, INADDR_ANY, TSockAddr, TSockLen,
inet_ntoa, recv, `==`, connect, send, accept
type
TPort* = distinct uint16 ## port type
TDomain* = enum ## domain, which specifies the protocol family of the
## created socket. Other domains than those that are listed
## here are unsupported.
AF_UNIX, ## for local socket (using a file). Unsupported on Windows.
AF_INET = 2, ## for network protocol IPv4 or
AF_INET6 = 23 ## for network protocol IPv6.
TType* = enum ## second argument to `socket` proc
SOCK_STREAM = 1, ## reliable stream-oriented service or Stream Sockets
SOCK_DGRAM = 2, ## datagram service or Datagram Sockets
SOCK_RAW = 3, ## raw protocols atop the network layer.
SOCK_SEQPACKET = 5 ## reliable sequenced packet service
TProtocol* = enum ## third argument to `socket` proc
IPPROTO_TCP = 6, ## Transmission control protocol.
IPPROTO_UDP = 17, ## User datagram protocol.
IPPROTO_IP, ## Internet protocol. Unsupported on Windows.
IPPROTO_IPV6, ## Internet Protocol Version 6. Unsupported on Windows.
IPPROTO_RAW, ## Raw IP Packets Protocol. Unsupported on Windows.
IPPROTO_ICMP ## Control message protocol. Unsupported on Windows.
TServent* {.pure, final.} = object ## information about a service
name*: string
aliases*: seq[string]
port*: TPort
proto*: string
Thostent* {.pure, final.} = object ## information about a given host
name*: string
aliases*: seq[string]
addrtype*: TDomain
length*: int
addrList*: seq[string]
when defined(windows):
let
osInvalidSocket* = winlean.INVALID_SOCKET
const
IOCPARM_MASK* = 127
IOC_IN* = int(-2147483648)
FIONBIO* = IOC_IN.int32 or ((sizeof(int32) and IOCPARM_MASK) shl 16) or
(102 shl 8) or 126
proc ioctlsocket*(s: TSocketHandle, cmd: clong,
argptr: ptr clong): cint {.
stdcall, importc: "ioctlsocket", dynlib: "ws2_32.dll".}
else:
let
osInvalidSocket* = posix.INVALID_SOCKET
proc `==`*(a, b: TPort): bool {.borrow.}
## ``==`` for ports.
proc `$`*(p: TPort): string {.borrow.}
## returns the port number as a string
proc toInt*(domain: TDomain): cint
## Converts the TDomain enum to a platform-dependent ``cint``.
proc toInt*(typ: TType): cint
## Converts the TType enum to a platform-dependent ``cint``.
proc toInt*(p: TProtocol): cint
## Converts the TProtocol enum to a platform-dependent ``cint``.
when defined(posix):
proc toInt(domain: TDomain): cint =
case domain
of AF_UNIX: result = posix.AF_UNIX
of AF_INET: result = posix.AF_INET
of AF_INET6: result = posix.AF_INET6
else: discard
proc toInt(typ: TType): cint =
case typ
of SOCK_STREAM: result = posix.SOCK_STREAM
of SOCK_DGRAM: result = posix.SOCK_DGRAM
of SOCK_SEQPACKET: result = posix.SOCK_SEQPACKET
of SOCK_RAW: result = posix.SOCK_RAW
else: discard
proc toInt(p: TProtocol): cint =
case p
of IPPROTO_TCP: result = posix.IPPROTO_TCP
of IPPROTO_UDP: result = posix.IPPROTO_UDP
of IPPROTO_IP: result = posix.IPPROTO_IP
of IPPROTO_IPV6: result = posix.IPPROTO_IPV6
of IPPROTO_RAW: result = posix.IPPROTO_RAW
of IPPROTO_ICMP: result = posix.IPPROTO_ICMP
else: discard
else:
proc toInt(domain: TDomain): cint =
result = toU16(ord(domain))
proc toInt(typ: TType): cint =
result = cint(ord(typ))
proc toInt(p: TProtocol): cint =
result = cint(ord(p))
proc socket*(domain: TDomain = AF_INET, typ: TType = SOCK_STREAM,
protocol: TProtocol = IPPROTO_TCP): TSocketHandle =
## Creates a new socket; returns `InvalidSocket` if an error occurs.
# TODO: The function which will use this will raise EOS.
socket(toInt(domain), toInt(typ), toInt(protocol))
proc close*(socket: TSocketHandle) =
## closes a socket.
when defined(windows):
discard winlean.closeSocket(socket)
else:
discard posix.close(socket)
# TODO: These values should not be discarded. An EOS should be raised.
# http://stackoverflow.com/questions/12463473/what-happens-if-you-call-close-on-a-bsd-socket-multiple-times
proc bindAddr*(socket: TSocketHandle, name: ptr TSockAddr, namelen: TSockLen): cint =
result = bindSocket(socket, name, namelen)
proc listen*(socket: TSocketHandle, backlog = SOMAXCONN) {.tags: [FReadIO].} =
## Marks ``socket`` as accepting connections.
## ``Backlog`` specifies the maximum length of the
## queue of pending connections.
when defined(windows):
if winlean.listen(socket, cint(backlog)) < 0'i32: osError(osLastError())
else:
if posix.listen(socket, cint(backlog)) < 0'i32: osError(osLastError())
proc getAddrInfo*(address: string, port: TPort, af: TDomain = AF_INET, typ: TType = SOCK_STREAM,
prot: TProtocol = IPPROTO_TCP): ptr TAddrInfo =
##
##
## **Warning**: The resulting ``ptr TAddrInfo`` must be freed using ``dealloc``!
var hints: TAddrInfo
result = nil
hints.ai_family = toInt(af)
hints.ai_socktype = toInt(typ)
hints.ai_protocol = toInt(prot)
var gaiResult = getAddrInfo(address, $port, addr(hints), result)
if gaiResult != 0'i32:
when defined(windows):
OSError(OSLastError())
else:
raise newException(EOS, $gai_strerror(gaiResult))
proc dealloc*(ai: ptr TAddrInfo) =
freeaddrinfo(ai)
proc ntohl*(x: int32): int32 =
## Converts 32-bit integers from network to host byte order.
## On machines where the host byte order is the same as network byte order,
## this is a no-op; otherwise, it performs a 4-byte swap operation.
when cpuEndian == bigEndian: result = x
else: result = (x shr 24'i32) or
(x shr 8'i32 and 0xff00'i32) or
(x shl 8'i32 and 0xff0000'i32) or
(x shl 24'i32)
proc ntohs*(x: int16): int16 =
## Converts 16-bit integers from network to host byte order. On machines
## where the host byte order is the same as network byte order, this is
## a no-op; otherwise, it performs a 2-byte swap operation.
when cpuEndian == bigEndian: result = x
else: result = (x shr 8'i16) or (x shl 8'i16)
proc htonl*(x: int32): int32 =
## Converts 32-bit integers from host to network byte order. On machines
## where the host byte order is the same as network byte order, this is
## a no-op; otherwise, it performs a 4-byte swap operation.
result = sockets2.ntohl(x)
proc htons*(x: int16): int16 =
## Converts 16-bit positive integers from host to network byte order.
## On machines where the host byte order is the same as network byte
## order, this is a no-op; otherwise, it performs a 2-byte swap operation.
result = sockets2.ntohs(x)
when defined(Windows):
var wsa: TWSADATA
if WSAStartup(0x0101'i16, addr wsa) != 0: OSError(OSLastError())

View file

@ -211,7 +211,9 @@ proc initInterval*(miliseconds, seconds, minutes, hours, days, months,
result.months = months result.months = months
result.years = years result.years = years
proc isLeapYear(year: int): bool = proc isLeapYear*(year: int): bool =
## returns true if ``year`` is a leap year
if year mod 400 == 0: if year mod 400 == 0:
return true return true
elif year mod 100 == 0: elif year mod 100 == 0:
@ -221,7 +223,9 @@ proc isLeapYear(year: int): bool =
else: else:
return false return false
proc getDaysInMonth(month: TMonth, year: int): int = proc getDaysInMonth*(month: TMonth, year: int): int =
## gets the amount of days in a ``month`` of a ``year``
# http://www.dispersiondesign.com/articles/time/number_of_days_in_a_month # http://www.dispersiondesign.com/articles/time/number_of_days_in_a_month
case month case month
of mFeb: result = if isLeapYear(year): 29 else: 28 of mFeb: result = if isLeapYear(year): 29 else: 28
@ -553,6 +557,119 @@ proc `$`*(m: TMonth): string =
"November", "December"] "November", "December"]
return lookup[m] return lookup[m]
proc format_token(info: TTimeInfo, token: string, buf: var string) =
## Helper of the format proc to parse individual tokens.
##
## Pass the found token in the user input string, and the buffer where the
## final string is being built. This has to be a var value because certain
## formatting tokens require modifying the previous characters.
case token
of "d":
buf.add($info.monthday)
of "dd":
if info.monthday < 10:
buf.add("0")
buf.add($info.monthday)
of "ddd":
buf.add(($info.weekday)[0 .. 2])
of "dddd":
buf.add($info.weekday)
of "h":
buf.add($(if info.hour > 12: info.hour - 12 else: info.hour))
of "hh":
let amerHour = if info.hour > 12: info.hour - 12 else: info.hour
if amerHour < 10:
buf.add('0')
buf.add($amerHour)
of "H":
buf.add($info.hour)
of "HH":
if info.hour < 10:
buf.add('0')
buf.add($info.hour)
of "m":
buf.add($info.minute)
of "mm":
if info.minute < 10:
buf.add('0')
buf.add($info.minute)
of "M":
buf.add($(int(info.month)+1))
of "MM":
if info.month < mOct:
buf.add('0')
buf.add($(int(info.month)+1))
of "MMM":
buf.add(($info.month)[0..2])
of "MMMM":
buf.add($info.month)
of "s":
buf.add($info.second)
of "ss":
if info.second < 10:
buf.add('0')
buf.add($info.second)
of "t":
if info.hour >= 12:
buf.add('P')
else: buf.add('A')
of "tt":
if info.hour >= 12:
buf.add("PM")
else: buf.add("AM")
of "y":
var fr = ($info.year).len()-1
if fr < 0: fr = 0
buf.add(($info.year)[fr .. ($info.year).len()-1])
of "yy":
var fr = ($info.year).len()-2
if fr < 0: fr = 0
var fyear = ($info.year)[fr .. ($info.year).len()-1]
if fyear.len != 2: fyear = repeatChar(2-fyear.len(), '0') & fyear
buf.add(fyear)
of "yyy":
var fr = ($info.year).len()-3
if fr < 0: fr = 0
var fyear = ($info.year)[fr .. ($info.year).len()-1]
if fyear.len != 3: fyear = repeatChar(3-fyear.len(), '0') & fyear
buf.add(fyear)
of "yyyy":
var fr = ($info.year).len()-4
if fr < 0: fr = 0
var fyear = ($info.year)[fr .. ($info.year).len()-1]
if fyear.len != 4: fyear = repeatChar(4-fyear.len(), '0') & fyear
buf.add(fyear)
of "yyyyy":
var fr = ($info.year).len()-5
if fr < 0: fr = 0
var fyear = ($info.year)[fr .. ($info.year).len()-1]
if fyear.len != 5: fyear = repeatChar(5-fyear.len(), '0') & fyear
buf.add(fyear)
of "z":
let hrs = (info.timezone div 60) div 60
buf.add($hrs)
of "zz":
let hrs = (info.timezone div 60) div 60
buf.add($hrs)
if hrs.abs < 10:
var atIndex = buf.len-(($hrs).len-(if hrs < 0: 1 else: 0))
buf.insert("0", atIndex)
of "zzz":
let hrs = (info.timezone div 60) div 60
buf.add($hrs & ":00")
if hrs.abs < 10:
var atIndex = buf.len-(($hrs & ":00").len-(if hrs < 0: 1 else: 0))
buf.insert("0", atIndex)
of "ZZZ":
buf.add(info.tzname)
of "":
discard
else:
raise newException(EInvalidValue, "Invalid format string: " & token)
proc format*(info: TTimeInfo, f: string): string = proc format*(info: TTimeInfo, f: string): string =
## This function formats `info` as specified by `f`. The following format ## This function formats `info` as specified by `f`. The following format
## specifiers are available: ## specifiers are available:
@ -587,8 +704,11 @@ proc format*(info: TTimeInfo, f: string): string =
## ZZZ Displays the name of the timezone. ``GMT -> GMT``, ``EST -> EST`` ## ZZZ Displays the name of the timezone. ``GMT -> GMT``, ``EST -> EST``
## ========== ================================================================================= ================================================ ## ========== ================================================================================= ================================================
## ##
## Other strings can be inserted by putting them in ``''``. For example ``hh'->'mm`` will give ``01->56``. ## Other strings can be inserted by putting them in ``''``. For example
## The following characters can be inserted without quoting them: ``:`` ``-`` ``(`` ``)`` ``/`` ``[`` ``]`` ``,`` ## ``hh'->'mm`` will give ``01->56``. The following characters can be
## inserted without quoting them: ``:`` ``-`` ``(`` ``)`` ``/`` ``[`` ``]``
## ``,``. However you don't need to necessarily separate format specifiers, a
## unambiguous format string like ``yyyyMMddhhmmss`` is valid too.
result = "" result = ""
var i = 0 var i = 0
@ -596,111 +716,7 @@ proc format*(info: TTimeInfo, f: string): string =
while true: while true:
case f[i] case f[i]
of ' ', '-', '/', ':', '\'', '\0', '(', ')', '[', ']', ',': of ' ', '-', '/', ':', '\'', '\0', '(', ')', '[', ']', ',':
case currentF format_token(info, currentF, result)
of "d":
result.add($info.monthday)
of "dd":
if info.monthday < 10:
result.add("0")
result.add($info.monthday)
of "ddd":
result.add(($info.weekday)[0 .. 2])
of "dddd":
result.add($info.weekday)
of "h":
result.add($(if info.hour > 12: info.hour - 12 else: info.hour))
of "hh":
let amerHour = if info.hour > 12: info.hour - 12 else: info.hour
if amerHour < 10:
result.add('0')
result.add($amerHour)
of "H":
result.add($info.hour)
of "HH":
if info.hour < 10:
result.add('0')
result.add($info.hour)
of "m":
result.add($info.minute)
of "mm":
if info.minute < 10:
result.add('0')
result.add($info.minute)
of "M":
result.add($(int(info.month)+1))
of "MM":
if info.month < mOct:
result.add('0')
result.add($(int(info.month)+1))
of "MMM":
result.add(($info.month)[0..2])
of "MMMM":
result.add($info.month)
of "s":
result.add($info.second)
of "ss":
if info.second < 10:
result.add('0')
result.add($info.second)
of "t":
if info.hour >= 12:
result.add('P')
else: result.add('A')
of "tt":
if info.hour >= 12:
result.add("PM")
else: result.add("AM")
of "y":
var fr = ($info.year).len()-1
if fr < 0: fr = 0
result.add(($info.year)[fr .. ($info.year).len()-1])
of "yy":
var fr = ($info.year).len()-2
if fr < 0: fr = 0
var fyear = ($info.year)[fr .. ($info.year).len()-1]
if fyear.len != 2: fyear = repeatChar(2-fyear.len(), '0') & fyear
result.add(fyear)
of "yyy":
var fr = ($info.year).len()-3
if fr < 0: fr = 0
var fyear = ($info.year)[fr .. ($info.year).len()-1]
if fyear.len != 3: fyear = repeatChar(3-fyear.len(), '0') & fyear
result.add(fyear)
of "yyyy":
var fr = ($info.year).len()-4
if fr < 0: fr = 0
var fyear = ($info.year)[fr .. ($info.year).len()-1]
if fyear.len != 4: fyear = repeatChar(4-fyear.len(), '0') & fyear
result.add(fyear)
of "yyyyy":
var fr = ($info.year).len()-5
if fr < 0: fr = 0
var fyear = ($info.year)[fr .. ($info.year).len()-1]
if fyear.len != 5: fyear = repeatChar(5-fyear.len(), '0') & fyear
result.add(fyear)
of "z":
let hrs = (info.timezone div 60) div 60
result.add($hrs)
of "zz":
let hrs = (info.timezone div 60) div 60
result.add($hrs)
if hrs.abs < 10:
var atIndex = result.len-(($hrs).len-(if hrs < 0: 1 else: 0))
result.insert("0", atIndex)
of "zzz":
let hrs = (info.timezone div 60) div 60
result.add($hrs & ":00")
if hrs.abs < 10:
var atIndex = result.len-(($hrs & ":00").len-(if hrs < 0: 1 else: 0))
result.insert("0", atIndex)
of "ZZZ":
result.add(info.tzname)
of "":
discard
else:
raise newException(EInvalidValue, "Invalid format string: " & currentF)
currentF = "" currentF = ""
if f[i] == '\0': break if f[i] == '\0': break
@ -712,7 +728,15 @@ proc format*(info: TTimeInfo, f: string): string =
inc(i) inc(i)
else: result.add(f[i]) else: result.add(f[i])
else: currentF.add(f[i]) else:
# Check if the letter being added matches previous accumulated buffer.
if currentF.len < 1 or currentF[high(currentF)] == f[i]:
currentF.add(f[i])
else:
format_token(info, currentF, result)
dec(i) # Move position back to re-process the character separately.
currentF = ""
inc(i) inc(i)
{.pop.} {.pop.}
@ -723,12 +747,16 @@ when isMainModule:
var t = getGMTime(fromSeconds(2147483647)) var t = getGMTime(fromSeconds(2147483647))
echo t.format("ddd dd MMM hh:mm:ss ZZZ yyyy") echo t.format("ddd dd MMM hh:mm:ss ZZZ yyyy")
echo t.format("ddd ddMMMhhmmssZZZyyyy")
assert t.format("ddd dd MMM hh:mm:ss ZZZ yyyy") == "Tue 19 Jan 03:14:07 UTC 2038"
assert t.format("ddd ddMMMhh:mm:ssZZZyyyy") == "Tue 19Jan03:14:07UTC2038" assert t.format("ddd ddMMMhh:mm:ssZZZyyyy") == "Tue 19Jan03:14:07UTC2038"
assert t.format("d dd ddd dddd h hh H HH m mm M MM MMM MMMM s" & assert t.format("d dd ddd dddd h hh H HH m mm M MM MMM MMMM s" &
" ss t tt y yy yyy yyyy yyyyy z zz zzz ZZZ") == " ss t tt y yy yyy yyyy yyyyy z zz zzz ZZZ") ==
"19 19 Tue Tuesday 3 03 3 03 14 14 1 01 Jan January 7 07 A AM 8 38 038 2038 02038 0 00 00:00 UTC" "19 19 Tue Tuesday 3 03 3 03 14 14 1 01 Jan January 7 07 A AM 8 38 038 2038 02038 0 00 00:00 UTC"
assert t.format("yyyyMMddhhmmss") == "20380119031407"
var t2 = getGMTime(fromSeconds(160070789)) # Mon 27 Jan 16:06:29 GMT 1975 var t2 = getGMTime(fromSeconds(160070789)) # Mon 27 Jan 16:06:29 GMT 1975
assert t2.format("d dd ddd dddd h hh H HH m mm M MM MMM MMMM s" & assert t2.format("d dd ddd dddd h hh H HH m mm M MM MMM MMMM s" &
" ss t tt y yy yyy yyyy yyyyy z zz zzz ZZZ") == " ss t tt y yy yyy yyyy yyyyy z zz zzz ZZZ") ==

6
lib/stdlib.babel Normal file
View file

@ -0,0 +1,6 @@
[Package]
name = "stdlib"
version = "0.9.0"
author = "Dominik Picheta"
description = "Nimrod's standard library."
license = "MIT"

View file

@ -185,6 +185,8 @@ proc `..`*[T](b: T): TSlice[T] {.noSideEffect, inline.} =
when not defined(niminheritable): when not defined(niminheritable):
{.pragma: inheritable.} {.pragma: inheritable.}
when not defined(nimunion):
{.pragma: unchecked.}
const NoFakeVars* = defined(NimrodVM) ## true if the backend doesn't support \ const NoFakeVars* = defined(NimrodVM) ## true if the backend doesn't support \
## "fake variables" like 'var EBADF {.importc.}: cint'. ## "fake variables" like 'var EBADF {.importc.}: cint'.
@ -194,9 +196,10 @@ when not defined(JS):
TGenericSeq {.compilerproc, pure, inheritable.} = object TGenericSeq {.compilerproc, pure, inheritable.} = object
len, reserved: int len, reserved: int
PGenericSeq {.exportc.} = ptr TGenericSeq PGenericSeq {.exportc.} = ptr TGenericSeq
UncheckedCharArray {.unchecked.} = array[0..100_000_000, char]
# len and space without counting the terminating zero: # len and space without counting the terminating zero:
NimStringDesc {.compilerproc, final.} = object of TGenericSeq NimStringDesc {.compilerproc, final.} = object of TGenericSeq
data: array[0..100_000_000, char] data: UncheckedCharArray
NimString = ptr NimStringDesc NimString = ptr NimStringDesc
when not defined(JS) and not defined(NimrodVM): when not defined(JS) and not defined(NimrodVM):
@ -1559,7 +1562,7 @@ when not defined(NimrodVM):
proc seqToPtr[T](x: seq[T]): pointer {.inline, nosideeffect.} = proc seqToPtr[T](x: seq[T]): pointer {.inline, nosideeffect.} =
result = cast[pointer](x) result = cast[pointer](x)
else: else:
proc seqToPtr[T](x: seq[T]): pointer {.noStackFrame, nosideeffect.} = proc seqToPtr[T](x: seq[T]): pointer {.asmNoStackFrame, nosideeffect.} =
asm """return `x`""" asm """return `x`"""
proc `==` *[T](x, y: seq[T]): bool {.noSideEffect.} = proc `==` *[T](x, y: seq[T]): bool {.noSideEffect.} =
@ -1842,7 +1845,7 @@ type
len*: int ## length of the inspectable slots len*: int ## length of the inspectable slots
when defined(JS): when defined(JS):
proc add*(x: var string, y: cstring) {.noStackFrame.} = proc add*(x: var string, y: cstring) {.asmNoStackFrame.} =
asm """ asm """
var len = `x`[0].length-1; var len = `x`[0].length-1;
for (var i = 0; i < `y`.length; ++i) { for (var i = 0; i < `y`.length; ++i) {
@ -2059,8 +2062,10 @@ when not defined(JS): #and not defined(NimrodVM):
## Flushes `f`'s buffer. ## Flushes `f`'s buffer.
proc readAll*(file: TFile): TaintedString {.tags: [FReadIO].} proc readAll*(file: TFile): TaintedString {.tags: [FReadIO].}
## Reads all data from the stream `file`. Raises an IO exception ## Reads all data from the stream `file`.
## in case of an error ##
## Raises an IO exception in case of an error. It is an error if the
## current file position is not at the beginning of the file.
proc readFile*(filename: string): TaintedString {.tags: [FReadIO].} proc readFile*(filename: string): TaintedString {.tags: [FReadIO].}
## Opens a file named `filename` for reading. Then calls `readAll` ## Opens a file named `filename` for reading. Then calls `readAll`

View file

@ -111,7 +111,7 @@ const
when asmVersion and not defined(gcc) and not defined(llvm_gcc): when asmVersion and not defined(gcc) and not defined(llvm_gcc):
# assembler optimized versions for compilers that # assembler optimized versions for compilers that
# have an intel syntax assembler: # have an intel syntax assembler:
proc addInt(a, b: int): int {.compilerProc, noStackFrame.} = proc addInt(a, b: int): int {.compilerProc, asmNoStackFrame.} =
# a in eax, and b in edx # a in eax, and b in edx
asm """ asm """
mov eax, `a` mov eax, `a`
@ -121,7 +121,7 @@ when asmVersion and not defined(gcc) and not defined(llvm_gcc):
theEnd: theEnd:
""" """
proc subInt(a, b: int): int {.compilerProc, noStackFrame.} = proc subInt(a, b: int): int {.compilerProc, asmNoStackFrame.} =
asm """ asm """
mov eax, `a` mov eax, `a`
sub eax, `b` sub eax, `b`
@ -130,7 +130,7 @@ when asmVersion and not defined(gcc) and not defined(llvm_gcc):
theEnd: theEnd:
""" """
proc negInt(a: int): int {.compilerProc, noStackFrame.} = proc negInt(a: int): int {.compilerProc, asmNoStackFrame.} =
asm """ asm """
mov eax, `a` mov eax, `a`
neg eax neg eax
@ -139,7 +139,7 @@ when asmVersion and not defined(gcc) and not defined(llvm_gcc):
theEnd: theEnd:
""" """
proc divInt(a, b: int): int {.compilerProc, noStackFrame.} = proc divInt(a, b: int): int {.compilerProc, asmNoStackFrame.} =
asm """ asm """
mov eax, `a` mov eax, `a`
mov ecx, `b` mov ecx, `b`
@ -150,7 +150,7 @@ when asmVersion and not defined(gcc) and not defined(llvm_gcc):
theEnd: theEnd:
""" """
proc modInt(a, b: int): int {.compilerProc, noStackFrame.} = proc modInt(a, b: int): int {.compilerProc, asmNoStackFrame.} =
asm """ asm """
mov eax, `a` mov eax, `a`
mov ecx, `b` mov ecx, `b`
@ -162,7 +162,7 @@ when asmVersion and not defined(gcc) and not defined(llvm_gcc):
mov eax, edx mov eax, edx
""" """
proc mulInt(a, b: int): int {.compilerProc, noStackFrame.} = proc mulInt(a, b: int): int {.compilerProc, asmNoStackFrame.} =
asm """ asm """
mov eax, `a` mov eax, `a`
mov ecx, `b` mov ecx, `b`

View file

@ -802,7 +802,7 @@ when defined(sparc): # For SPARC architecture.
# Addresses decrease as the stack grows. # Addresses decrease as the stack grows.
while sp <= max: while sp <= max:
gcMark(gch, sp[]) gcMark(gch, sp[])
sp = cast[ppointer](cast[TAddress](sp) +% sizeof(pointer)) sp = cast[PPointer](cast[TAddress](sp) +% sizeof(pointer))
elif defined(ELATE): elif defined(ELATE):
{.error: "stack marking code is to be written for this architecture".} {.error: "stack marking code is to be written for this architecture".}

View file

@ -23,9 +23,9 @@ type
PCallFrame = ptr TCallFrame PCallFrame = ptr TCallFrame
TCallFrame {.importc, nodecl, final.} = object TCallFrame {.importc, nodecl, final.} = object
prev: PCallFrame prev: PCallFrame
procname: CString procname: cstring
line: int # current line number line: int # current line number
filename: CString filename: cstring
var var
framePtr {.importc, nodecl, volatile.}: PCallFrame framePtr {.importc, nodecl, volatile.}: PCallFrame
@ -48,7 +48,7 @@ proc getCurrentExceptionMsg*(): string =
proc auxWriteStackTrace(f: PCallFrame): string = proc auxWriteStackTrace(f: PCallFrame): string =
type type
TTempFrame = tuple[procname: CString, line: int] TTempFrame = tuple[procname: cstring, line: int]
var var
it = f it = f
i = 0 i = 0
@ -84,7 +84,7 @@ proc rawWriteStackTrace(): string =
framePtr = nil framePtr = nil
proc raiseException(e: ref E_Base, ename: cstring) {. proc raiseException(e: ref E_Base, ename: cstring) {.
compilerproc, noStackFrame.} = compilerproc, asmNoStackFrame.} =
e.name = ename e.name = ename
if excHandler != nil: if excHandler != nil:
excHandler.exc = e excHandler.exc = e
@ -104,7 +104,7 @@ proc raiseException(e: ref E_Base, ename: cstring) {.
alert(buf) alert(buf)
asm """throw `e`;""" asm """throw `e`;"""
proc reraiseException() {.compilerproc, noStackFrame.} = proc reraiseException() {.compilerproc, asmNoStackFrame.} =
if excHandler == nil: if excHandler == nil:
raise newException(ENoExceptionToReraise, "no exception to reraise") raise newException(ENoExceptionToReraise, "no exception to reraise")
else: else:
@ -125,7 +125,7 @@ proc raiseIndexError() {.compilerproc, noreturn.} =
proc raiseFieldError(f: string) {.compilerproc, noreturn.} = proc raiseFieldError(f: string) {.compilerproc, noreturn.} =
raise newException(EInvalidField, f & " is not accessible") raise newException(EInvalidField, f & " is not accessible")
proc SetConstr() {.varargs, noStackFrame, compilerproc.} = proc SetConstr() {.varargs, asmNoStackFrame, compilerproc.} =
asm """ asm """
var result = {}; var result = {};
for (var i = 0; i < arguments.length; ++i) { for (var i = 0; i < arguments.length; ++i) {
@ -141,7 +141,7 @@ proc SetConstr() {.varargs, noStackFrame, compilerproc.} =
return result; return result;
""" """
proc cstrToNimstr(c: cstring): string {.noStackFrame, compilerproc.} = proc cstrToNimstr(c: cstring): string {.asmNoStackFrame, compilerproc.} =
asm """ asm """
var result = []; var result = [];
for (var i = 0; i < `c`.length; ++i) { for (var i = 0; i < `c`.length; ++i) {
@ -151,7 +151,7 @@ proc cstrToNimstr(c: cstring): string {.noStackFrame, compilerproc.} =
return result; return result;
""" """
proc toJSStr(s: string): cstring {.noStackFrame, compilerproc.} = proc toJSStr(s: string): cstring {.asmNoStackFrame, compilerproc.} =
asm """ asm """
var len = `s`.length-1; var len = `s`.length-1;
var result = new Array(len); var result = new Array(len);
@ -162,7 +162,7 @@ proc toJSStr(s: string): cstring {.noStackFrame, compilerproc.} =
return result.join(""); return result.join("");
""" """
proc mnewString(len: int): string {.noStackFrame, compilerproc.} = proc mnewString(len: int): string {.asmNoStackFrame, compilerproc.} =
asm """ asm """
var result = new Array(`len`+1); var result = new Array(`len`+1);
result[0] = 0; result[0] = 0;
@ -170,7 +170,7 @@ proc mnewString(len: int): string {.noStackFrame, compilerproc.} =
return result; return result;
""" """
proc SetCard(a: int): int {.compilerproc, noStackFrame.} = proc SetCard(a: int): int {.compilerproc, asmNoStackFrame.} =
# argument type is a fake # argument type is a fake
asm """ asm """
var result = 0; var result = 0;
@ -178,14 +178,14 @@ proc SetCard(a: int): int {.compilerproc, noStackFrame.} =
return result; return result;
""" """
proc SetEq(a, b: int): bool {.compilerproc, noStackFrame.} = proc SetEq(a, b: int): bool {.compilerproc, asmNoStackFrame.} =
asm """ asm """
for (var elem in `a`) { if (!`b`[elem]) return false; } for (var elem in `a`) { if (!`b`[elem]) return false; }
for (var elem in `b`) { if (!`a`[elem]) return false; } for (var elem in `b`) { if (!`a`[elem]) return false; }
return true; return true;
""" """
proc SetLe(a, b: int): bool {.compilerproc, noStackFrame.} = proc SetLe(a, b: int): bool {.compilerproc, asmNoStackFrame.} =
asm """ asm """
for (var elem in `a`) { if (!`b`[elem]) return false; } for (var elem in `a`) { if (!`b`[elem]) return false; }
return true; return true;
@ -194,7 +194,7 @@ proc SetLe(a, b: int): bool {.compilerproc, noStackFrame.} =
proc SetLt(a, b: int): bool {.compilerproc.} = proc SetLt(a, b: int): bool {.compilerproc.} =
result = SetLe(a, b) and not SetEq(a, b) result = SetLe(a, b) and not SetEq(a, b)
proc SetMul(a, b: int): int {.compilerproc, noStackFrame.} = proc SetMul(a, b: int): int {.compilerproc, asmNoStackFrame.} =
asm """ asm """
var result = {}; var result = {};
for (var elem in `a`) { for (var elem in `a`) {
@ -203,7 +203,7 @@ proc SetMul(a, b: int): int {.compilerproc, noStackFrame.} =
return result; return result;
""" """
proc SetPlus(a, b: int): int {.compilerproc, noStackFrame.} = proc SetPlus(a, b: int): int {.compilerproc, asmNoStackFrame.} =
asm """ asm """
var result = {}; var result = {};
for (var elem in `a`) { result[elem] = true; } for (var elem in `a`) { result[elem] = true; }
@ -211,7 +211,7 @@ proc SetPlus(a, b: int): int {.compilerproc, noStackFrame.} =
return result; return result;
""" """
proc SetMinus(a, b: int): int {.compilerproc, noStackFrame.} = proc SetMinus(a, b: int): int {.compilerproc, asmNoStackFrame.} =
asm """ asm """
var result = {}; var result = {};
for (var elem in `a`) { for (var elem in `a`) {
@ -220,7 +220,7 @@ proc SetMinus(a, b: int): int {.compilerproc, noStackFrame.} =
return result; return result;
""" """
proc cmpStrings(a, b: string): int {.noStackFrame, compilerProc.} = proc cmpStrings(a, b: string): int {.asmNoStackFrame, compilerProc.} =
asm """ asm """
if (`a` == `b`) return 0; if (`a` == `b`) return 0;
if (!`a`) return -1; if (!`a`) return -1;
@ -234,7 +234,7 @@ proc cmpStrings(a, b: string): int {.noStackFrame, compilerProc.} =
proc cmp(x, y: string): int = return cmpStrings(x, y) proc cmp(x, y: string): int = return cmpStrings(x, y)
proc eqStrings(a, b: string): bool {.noStackFrame, compilerProc.} = proc eqStrings(a, b: string): bool {.asmNoStackFrame, compilerProc.} =
asm """ asm """
if (`a` == `b`) return true; if (`a` == `b`) return true;
if ((!`a`) || (!`b`)) return false; if ((!`a`) || (!`b`)) return false;
@ -300,7 +300,7 @@ type
setAttributeNode*: proc (attr: ref TNode) {.nimcall.} setAttributeNode*: proc (attr: ref TNode) {.nimcall.}
when defined(kwin): when defined(kwin):
proc rawEcho {.compilerproc, nostackframe.} = proc rawEcho {.compilerproc, asmNoStackFrame.} =
asm """ asm """
var buf = ""; var buf = "";
for (var i = 0; i < arguments.length; ++i) { for (var i = 0; i < arguments.length; ++i) {
@ -312,7 +312,7 @@ when defined(kwin):
elif defined(nodejs): elif defined(nodejs):
proc ewriteln(x: cstring) = log(x) proc ewriteln(x: cstring) = log(x)
proc rawEcho {.compilerproc, nostackframe.} = proc rawEcho {.compilerproc, asmNoStackFrame.} =
asm """ asm """
var buf = ""; var buf = "";
for (var i = 0; i < arguments.length; ++i) { for (var i = 0; i < arguments.length; ++i) {
@ -345,42 +345,42 @@ else:
node.appendChild(document.createElement("br")) node.appendChild(document.createElement("br"))
# Arithmetic: # Arithmetic:
proc addInt(a, b: int): int {.noStackFrame, compilerproc.} = proc addInt(a, b: int): int {.asmNoStackFrame, compilerproc.} =
asm """ asm """
var result = `a` + `b`; var result = `a` + `b`;
if (result > 2147483647 || result < -2147483648) `raiseOverflow`(); if (result > 2147483647 || result < -2147483648) `raiseOverflow`();
return result; return result;
""" """
proc subInt(a, b: int): int {.noStackFrame, compilerproc.} = proc subInt(a, b: int): int {.asmNoStackFrame, compilerproc.} =
asm """ asm """
var result = `a` - `b`; var result = `a` - `b`;
if (result > 2147483647 || result < -2147483648) `raiseOverflow`(); if (result > 2147483647 || result < -2147483648) `raiseOverflow`();
return result; return result;
""" """
proc mulInt(a, b: int): int {.noStackFrame, compilerproc.} = proc mulInt(a, b: int): int {.asmNoStackFrame, compilerproc.} =
asm """ asm """
var result = `a` * `b`; var result = `a` * `b`;
if (result > 2147483647 || result < -2147483648) `raiseOverflow`(); if (result > 2147483647 || result < -2147483648) `raiseOverflow`();
return result; return result;
""" """
proc divInt(a, b: int): int {.noStackFrame, compilerproc.} = proc divInt(a, b: int): int {.asmNoStackFrame, compilerproc.} =
asm """ asm """
if (`b` == 0) `raiseDivByZero`(); if (`b` == 0) `raiseDivByZero`();
if (`b` == -1 && `a` == 2147483647) `raiseOverflow`(); if (`b` == -1 && `a` == 2147483647) `raiseOverflow`();
return Math.floor(`a` / `b`); return Math.floor(`a` / `b`);
""" """
proc modInt(a, b: int): int {.noStackFrame, compilerproc.} = proc modInt(a, b: int): int {.asmNoStackFrame, compilerproc.} =
asm """ asm """
if (`b` == 0) `raiseDivByZero`(); if (`b` == 0) `raiseDivByZero`();
if (`b` == -1 && `a` == 2147483647) `raiseOverflow`(); if (`b` == -1 && `a` == 2147483647) `raiseOverflow`();
return Math.floor(`a` % `b`); return Math.floor(`a` % `b`);
""" """
proc addInt64(a, b: int): int {.noStackFrame, compilerproc.} = proc addInt64(a, b: int): int {.asmNoStackFrame, compilerproc.} =
asm """ asm """
var result = `a` + `b`; var result = `a` + `b`;
if (result > 9223372036854775807 if (result > 9223372036854775807
@ -388,7 +388,7 @@ proc addInt64(a, b: int): int {.noStackFrame, compilerproc.} =
return result; return result;
""" """
proc subInt64(a, b: int): int {.noStackFrame, compilerproc.} = proc subInt64(a, b: int): int {.asmNoStackFrame, compilerproc.} =
asm """ asm """
var result = `a` - `b`; var result = `a` - `b`;
if (result > 9223372036854775807 if (result > 9223372036854775807
@ -396,7 +396,7 @@ proc subInt64(a, b: int): int {.noStackFrame, compilerproc.} =
return result; return result;
""" """
proc mulInt64(a, b: int): int {.noStackFrame, compilerproc.} = proc mulInt64(a, b: int): int {.asmNoStackFrame, compilerproc.} =
asm """ asm """
var result = `a` * `b`; var result = `a` * `b`;
if (result > 9223372036854775807 if (result > 9223372036854775807
@ -404,90 +404,89 @@ proc mulInt64(a, b: int): int {.noStackFrame, compilerproc.} =
return result; return result;
""" """
proc divInt64(a, b: int): int {.noStackFrame, compilerproc.} = proc divInt64(a, b: int): int {.asmNoStackFrame, compilerproc.} =
asm """ asm """
if (`b` == 0) `raiseDivByZero`(); if (`b` == 0) `raiseDivByZero`();
if (`b` == -1 && `a` == 9223372036854775807) `raiseOverflow`(); if (`b` == -1 && `a` == 9223372036854775807) `raiseOverflow`();
return Math.floor(`a` / `b`); return Math.floor(`a` / `b`);
""" """
proc modInt64(a, b: int): int {.noStackFrame, compilerproc.} = proc modInt64(a, b: int): int {.asmNoStackFrame, compilerproc.} =
asm """ asm """
if (`b` == 0) `raiseDivByZero`(); if (`b` == 0) `raiseDivByZero`();
if (`b` == -1 && `a` == 9223372036854775807) `raiseOverflow`(); if (`b` == -1 && `a` == 9223372036854775807) `raiseOverflow`();
return Math.floor(`a` % `b`); return Math.floor(`a` % `b`);
""" """
proc NegInt(a: int): int {.compilerproc.} = proc negInt(a: int): int {.compilerproc.} =
result = a*(-1) result = a*(-1)
proc NegInt64(a: int64): int64 {.compilerproc.} = proc negInt64(a: int64): int64 {.compilerproc.} =
result = a*(-1) result = a*(-1)
proc AbsInt(a: int): int {.compilerproc.} = proc absInt(a: int): int {.compilerproc.} =
result = if a < 0: a*(-1) else: a result = if a < 0: a*(-1) else: a
proc AbsInt64(a: int64): int64 {.compilerproc.} = proc absInt64(a: int64): int64 {.compilerproc.} =
result = if a < 0: a*(-1) else: a result = if a < 0: a*(-1) else: a
proc LeU(a, b: int): bool {.compilerproc.} = proc leU(a, b: int): bool {.compilerproc.} =
result = abs(a) <= abs(b) result = abs(a) <= abs(b)
proc LtU(a, b: int): bool {.compilerproc.} = proc ltU(a, b: int): bool {.compilerproc.} =
result = abs(a) < abs(b) result = abs(a) < abs(b)
proc LeU64(a, b: int64): bool {.compilerproc.} = proc leU64(a, b: int64): bool {.compilerproc.} =
result = abs(a) <= abs(b) result = abs(a) <= abs(b)
proc ltU64(a, b: int64): bool {.compilerproc.} =
proc LtU64(a, b: int64): bool {.compilerproc.} =
result = abs(a) < abs(b) result = abs(a) < abs(b)
proc AddU(a, b: int): int {.compilerproc.} = proc addU(a, b: int): int {.compilerproc.} =
result = abs(a) + abs(b) result = abs(a) + abs(b)
proc AddU64(a, b: int64): int64 {.compilerproc.} = proc addU64(a, b: int64): int64 {.compilerproc.} =
result = abs(a) + abs(b) result = abs(a) + abs(b)
proc SubU(a, b: int): int {.compilerproc.} = proc subU(a, b: int): int {.compilerproc.} =
result = abs(a) - abs(b) result = abs(a) - abs(b)
proc SubU64(a, b: int64): int64 {.compilerproc.} = proc subU64(a, b: int64): int64 {.compilerproc.} =
result = abs(a) - abs(b) result = abs(a) - abs(b)
proc MulU(a, b: int): int {.compilerproc.} = proc mulU(a, b: int): int {.compilerproc.} =
result = abs(a) * abs(b) result = abs(a) * abs(b)
proc MulU64(a, b: int64): int64 {.compilerproc.} = proc mulU64(a, b: int64): int64 {.compilerproc.} =
result = abs(a) * abs(b) result = abs(a) * abs(b)
proc DivU(a, b: int): int {.compilerproc.} = proc divU(a, b: int): int {.compilerproc.} =
result = abs(a) div abs(b) result = abs(a) div abs(b)
proc DivU64(a, b: int64): int64 {.compilerproc.} = proc divU64(a, b: int64): int64 {.compilerproc.} =
result = abs(a) div abs(b) result = abs(a) div abs(b)
proc ModU(a, b: int): int {.compilerproc.} = proc modU(a, b: int): int {.compilerproc.} =
result = abs(a) mod abs(b) result = abs(a) mod abs(b)
proc ModU64(a, b: int64): int64 {.compilerproc.} = proc modU64(a, b: int64): int64 {.compilerproc.} =
result = abs(a) mod abs(b) result = abs(a) mod abs(b)
proc Ze(a: int): int {.compilerproc.} = proc ze*(a: int): int {.compilerproc.} =
result = a
proc Ze64(a: int64): int64 {.compilerproc.} =
result = a result = a
proc ToU8(a: int): int8 {.noStackFrame, compilerproc.} = proc ze64*(a: int64): int64 {.compilerproc.} =
result = a
proc ToU8(a: int): int8 {.asmNoStackFrame, compilerproc.} =
asm """ asm """
return `a`; return `a`;
""" """
proc ToU16(a: int): int16 {.noStackFrame, compilerproc.} = proc ToU16(a: int): int16 {.asmNoStackFrame, compilerproc.} =
asm """ asm """
return `a`; return `a`;
""" """
proc ToU32(a: int): int32 {.noStackFrame, compilerproc.} = proc ToU32(a: int): int32 {.asmNoStackFrame, compilerproc.} =
asm """ asm """
return `a`; return `a`;
""" """
proc nimMin(a, b: int): int {.compilerproc.} = return if a <= b: a else: b proc nimMin(a, b: int): int {.compilerproc.} = return if a <= b: a else: b
proc nimMax(a, b: int): int {.compilerproc.} = return if a >= b: a else: b proc nimMax(a, b: int): int {.compilerproc.} = return if a >= b: a else: b
@ -500,9 +499,9 @@ proc isFatPointer(ti: PNimType): bool =
tyArray, tyArrayConstr, tyTuple, tyArray, tyArrayConstr, tyTuple,
tyOpenArray, tySet, tyVar, tyRef, tyPtr} tyOpenArray, tySet, tyVar, tyRef, tyPtr}
proc NimCopy(x: pointer, ti: PNimType): pointer {.compilerproc.} proc nimCopy(x: pointer, ti: PNimType): pointer {.compilerproc.}
proc NimCopyAux(dest, src: Pointer, n: ptr TNimNode) {.compilerproc.} = proc nimCopyAux(dest, src: Pointer, n: ptr TNimNode) {.compilerproc.} =
case n.kind case n.kind
of nkNone: sysAssert(false, "NimCopyAux") of nkNone: sysAssert(false, "NimCopyAux")
of nkSlot: of nkSlot:
@ -518,7 +517,7 @@ proc NimCopyAux(dest, src: Pointer, n: ptr TNimNode) {.compilerproc.} =
} }
""" """
proc NimCopy(x: pointer, ti: PNimType): pointer = proc nimCopy(x: pointer, ti: PNimType): pointer =
case ti.kind case ti.kind
of tyPtr, tyRef, tyVar, tyNil: of tyPtr, tyRef, tyVar, tyNil:
if not isFatPointer(ti): if not isFatPointer(ti):
@ -586,7 +585,7 @@ proc genericReset(x: Pointer, ti: PNimType): pointer {.compilerproc.} =
result = nil result = nil
proc ArrayConstr(len: int, value: pointer, typ: PNimType): pointer {. proc ArrayConstr(len: int, value: pointer, typ: PNimType): pointer {.
noStackFrame, compilerproc.} = asmNoStackFrame, compilerproc.} =
# types are fake # types are fake
asm """ asm """
var result = new Array(`len`); var result = new Array(`len`);
@ -620,7 +619,7 @@ proc isObj(obj, subclass: PNimType): bool {.compilerproc.} =
x = x.base x = x.base
return true return true
proc addChar(x: string, c: char) {.compilerproc, noStackFrame.} = proc addChar(x: string, c: char) {.compilerproc, asmNoStackFrame.} =
asm """ asm """
`x`[`x`.length-1] = `c`; `x`.push(0); `x`[`x`.length-1] = `c`; `x`.push(0);
""" """

View file

@ -16,8 +16,12 @@ const
type type
THandle* = int THandle* = int
LONG* = int32 LONG* = int32
ULONG* = int
PULONG* = ptr int
WINBOOL* = int32 WINBOOL* = int32
DWORD* = int32 DWORD* = int32
PDWORD* = ptr DWORD
LPINT* = ptr int32
HDC* = THandle HDC* = THandle
HGLRC* = THandle HGLRC* = THandle
@ -195,14 +199,14 @@ else:
importc: "GetCurrentDirectoryA", dynlib: "kernel32", stdcall.} importc: "GetCurrentDirectoryA", dynlib: "kernel32", stdcall.}
proc setCurrentDirectoryA*(lpPathName: cstring): int32 {. proc setCurrentDirectoryA*(lpPathName: cstring): int32 {.
importc: "SetCurrentDirectoryA", dynlib: "kernel32", stdcall.} importc: "SetCurrentDirectoryA", dynlib: "kernel32", stdcall.}
proc createDirectoryA*(pathName: cstring, security: pointer=nil): int32 {. proc createDirectoryA*(pathName: cstring, security: Pointer=nil): int32 {.
importc: "CreateDirectoryA", dynlib: "kernel32", stdcall.} importc: "CreateDirectoryA", dynlib: "kernel32", stdcall.}
proc removeDirectoryA*(lpPathName: cstring): int32 {. proc removeDirectoryA*(lpPathName: cstring): int32 {.
importc: "RemoveDirectoryA", dynlib: "kernel32", stdcall.} importc: "RemoveDirectoryA", dynlib: "kernel32", stdcall.}
proc setEnvironmentVariableA*(lpName, lpValue: cstring): int32 {. proc setEnvironmentVariableA*(lpName, lpValue: cstring): int32 {.
stdcall, dynlib: "kernel32", importc: "SetEnvironmentVariableA".} stdcall, dynlib: "kernel32", importc: "SetEnvironmentVariableA".}
proc getModuleFileNameA*(handle: THandle, buf: cstring, size: int32): int32 {. proc getModuleFileNameA*(handle: THandle, buf: CString, size: int32): int32 {.
importc: "GetModuleFileNameA", dynlib: "kernel32", stdcall.} importc: "GetModuleFileNameA", dynlib: "kernel32", stdcall.}
when useWinUnicode: when useWinUnicode:
@ -300,7 +304,7 @@ else:
dwFileAttributes: int32): WINBOOL {. dwFileAttributes: int32): WINBOOL {.
stdcall, dynlib: "kernel32", importc: "SetFileAttributesA".} stdcall, dynlib: "kernel32", importc: "SetFileAttributesA".}
proc copyFileA*(lpExistingFileName, lpNewFileName: cstring, proc copyFileA*(lpExistingFileName, lpNewFileName: CString,
bFailIfExists: cint): cint {. bFailIfExists: cint): cint {.
importc: "CopyFileA", stdcall, dynlib: "kernel32".} importc: "CopyFileA", stdcall, dynlib: "kernel32".}
@ -632,3 +636,76 @@ when not useWinUnicode:
proc unmapViewOfFile*(lpBaseAddress: pointer): WINBOOL {.stdcall, proc unmapViewOfFile*(lpBaseAddress: pointer): WINBOOL {.stdcall,
dynlib: "kernel32", importc: "UnmapViewOfFile".} dynlib: "kernel32", importc: "UnmapViewOfFile".}
type
TOVERLAPPED* {.final, pure.} = object
Internal*: DWORD
InternalHigh*: DWORD
Offset*: DWORD
OffsetHigh*: DWORD
hEvent*: THANDLE
POVERLAPPED* = ptr TOVERLAPPED
POVERLAPPED_COMPLETION_ROUTINE* = proc (para1: DWORD, para2: DWORD,
para3: POVERLAPPED){.stdcall.}
TGUID* {.final, pure.} = object
D1*: int32
D2*: int16
D3*: int16
D4*: array [0..7, int8]
const
ERROR_IO_PENDING* = 997
proc CreateIoCompletionPort*(FileHandle: THANDLE, ExistingCompletionPort: THANDLE,
CompletionKey: DWORD,
NumberOfConcurrentThreads: DWORD): THANDLE{.stdcall,
dynlib: "kernel32", importc: "CreateIoCompletionPort".}
proc GetQueuedCompletionStatus*(CompletionPort: THandle,
lpNumberOfBytesTransferred: PDWORD, lpCompletionKey: PULONG,
lpOverlapped: ptr POverlapped,
dwMilliseconds: DWORD): WINBOOL{.stdcall,
dynlib: "kernel32", importc: "GetQueuedCompletionStatus".}
const
IOC_OUT* = 0x40000000
IOC_IN* = 0x80000000
IOC_WS2* = 0x08000000
IOC_INOUT* = IOC_IN or IOC_OUT
template WSAIORW*(x,y): expr = (IOC_INOUT or x or y)
const
SIO_GET_EXTENSION_FUNCTION_POINTER* = WSAIORW(IOC_WS2,6).DWORD
SO_UPDATE_ACCEPT_CONTEXT* = 0x700B
var
WSAID_CONNECTEX*: TGUID = TGUID(D1: 0x25a207b9, D2: 0xddf3'i16, D3: 0x4660, D4: [
0x8e'i8, 0xe9'i8, 0x76'i8, 0xe5'i8, 0x8c'i8, 0x74'i8, 0x06'i8, 0x3e'i8])
WSAID_ACCEPTEX*: TGUID = TGUID(D1: 0xb5367df1'i32, D2: 0xcbac'i16, D3: 0x11cf, D4: [
0x95'i8, 0xca'i8, 0x00'i8, 0x80'i8, 0x5f'i8, 0x48'i8, 0xa1'i8, 0x92'i8])
WSAID_GETACCEPTEXSOCKADDRS*: TGUID = TGUID(D1: 0xb5367df2'i32, D2: 0xcbac'i16, D3: 0x11cf, D4: [
0x95'i8, 0xca'i8, 0x00'i8, 0x80'i8, 0x5f'i8, 0x48'i8, 0xa1'i8, 0x92'i8])
proc WSAIoctl*(s: TSocketHandle, dwIoControlCode: DWORD, lpvInBuffer: pointer,
cbInBuffer: DWORD, lpvOutBuffer: pointer, cbOutBuffer: DWORD,
lpcbBytesReturned: PDword, lpOverlapped: POVERLAPPED,
lpCompletionRoutine: POVERLAPPED_COMPLETION_ROUTINE): cint
{.stdcall, importc: "WSAIoctl", dynlib: "Ws2_32.dll".}
type
TWSABuf* {.importc: "WSABUF", header: "winsock2.h".} = object
len*: ULONG
buf*: cstring
proc WSARecv*(s: TSocketHandle, buf: ptr TWSABuf, bufCount: DWORD,
bytesReceived, flags: PDWORD, lpOverlapped: POverlapped,
completionProc: POVERLAPPED_COMPLETION_ROUTINE): cint {.
stdcall, importc: "WSARecv", dynlib: "Ws2_32.dll".}
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".}

View file

@ -134,6 +134,7 @@ proc gzerror*(thefile: gzFile, errnum: var int32): pbytef{.cdecl, dynlib: libz,
importc: "gzerror".} importc: "gzerror".}
proc adler32*(adler: uLong, buf: pbytef, length: uInt): uLong{.cdecl, proc adler32*(adler: uLong, buf: pbytef, length: uInt): uLong{.cdecl,
dynlib: libz, importc: "adler32".} dynlib: libz, importc: "adler32".}
## **Warning**: Adler-32 requires at least a few hundred bytes to get rolling.
proc crc32*(crc: uLong, buf: pbytef, length: uInt): uLong{.cdecl, dynlib: libz, proc crc32*(crc: uLong, buf: pbytef, length: uInt): uLong{.cdecl, dynlib: libz,
importc: "crc32".} importc: "crc32".}
proc deflateInitu*(strm: var TZStream, level: int32, version: cstring, proc deflateInitu*(strm: var TZStream, level: int32, version: cstring,

View file

@ -33,7 +33,7 @@ If you are on a fairly modern *nix system, the following steps should work:
$ git clone git://github.com/Araq/Nimrod.git $ git clone git://github.com/Araq/Nimrod.git
$ cd Nimrod $ cd Nimrod
$ git clone --depth 1 git://github.com/nimrod-code/csources $ git clone --depth 1 git://github.com/nimrod-code/csources
$ cd csources && ./build.sh $ cd csources && sh build.sh
$ cd .. $ cd ..
$ bin/nimrod c koch $ bin/nimrod c koch
$ ./koch boot -d:release $ ./koch boot -d:release

View file

@ -1,6 +1,6 @@
discard """ discard """
line: 21 line: 21
errormsg: "invalid type: 'TTable'" errormsg: "invalid type: 'TTable[string, proc (string)]'"
""" """
import tables import tables

View file

@ -7,4 +7,4 @@ type
proc ha() = proc ha() =
var var
x: TExport # no error x: TExport # no error
nil discard

View file

@ -4,4 +4,4 @@ type
TExport* = enum x, y, z TExport* = enum x, y, z
proc foo*(x: int) = proc foo*(x: int) =
nil discard

View file

@ -1,4 +1,4 @@
type type
TExport* = enum x, y, z # exactly the same type! TExport* = enum x, y, z # exactly the same type!
proc foo*(x: int) = nil proc foo*(x: int) = discard

View file

@ -0,0 +1,64 @@
discard """
file: "tasyncawait.nim"
cmd: "nimrod cc --hints:on $# $#"
output: "5000"
"""
import asyncio2, sockets2, net, strutils
var disp = newDispatcher()
var msgCount = 0
const
swarmSize = 50
messagesToSend = 100
var clientCount = 0
proc sendMessages(disp: PDispatcher, client: TSocketHandle): PFuture[int] {.async.} =
for i in 0 .. <messagesToSend:
discard await disp.send(client, "Message " & $i & "\c\L")
proc launchSwarm(disp: PDispatcher, port: TPort): PFuture[int] {.async.} =
for i in 0 .. <swarmSize:
var sock = socket()
#disp.register(sock)
discard await disp.connect(sock, "localhost", port)
when true:
discard await sendMessages(disp, sock)
sock.close()
else:
# Issue #932: https://github.com/Araq/Nimrod/issues/932
var msgFut = sendMessages(disp, sock)
msgFut.callback =
proc () =
sock.close()
proc readMessages(disp: PDispatcher, client: TSocketHandle): PFuture[int] {.async.} =
while true:
var line = await disp.recvLine(client)
if line == "":
client.close()
clientCount.inc
break
else:
if line.startswith("Message "):
msgCount.inc
else:
doAssert false
proc createServer(disp: PDispatcher, port: TPort): PFuture[int] {.async.} =
var server = socket()
#disp.register(server)
server.bindAddr(port)
server.listen()
while true:
discard readMessages(disp, await disp.accept(server))
discard disp.createServer(TPort(10335))
discard disp.launchSwarm(TPort(10335))
while true:
disp.poll()
if clientCount == swarmSize: break
assert msgCount == swarmSize * messagesToSend
echo msgCount

View file

@ -19,8 +19,8 @@ of eB, eC: write(stdout, "b or c")
case x case x
of "Andreas", "Rumpf": write(stdout, "Hallo Meister!") of "Andreas", "Rumpf": write(stdout, "Hallo Meister!")
of "aa", "bb": write(stdout, "Du bist nicht mein Meister") of "aa", "bb": write(stdout, "Du bist nicht mein Meister")
of "cc", "hash", "when": nil of "cc", "hash", "when": discard
of "will", "it", "finally", "be", "generated": nil of "will", "it", "finally", "be", "generated": discard
var z = case i var z = case i
of 1..5, 8, 9: "aa" of 1..5, 8, 9: "aa"

View file

@ -19,5 +19,18 @@ var
new(a) new(a)
q(a) q(a)
# bug #914
var x = newWideCString("Hello")
echo "success" echo "success"
# bug #833
type
PFuture*[T] = ref object
value*: T
finished*: bool
cb: proc (future: PFuture[T]) {.closure.}
var k = PFuture[void]()

View file

@ -0,0 +1,17 @@
discard """
output: '''true
true'''
"""
import sets
var
a = initSet[int]()
b = initSet[int]()
c = initSet[string]()
for i in 0..5: a.incl(i)
for i in 1..6: b.incl(i)
for i in 0..5: c.incl($i)
echo map(a, proc(x: int): int = x + 1) == b
echo map(a, proc(x: int): string = $x) == c

View file

@ -24,3 +24,5 @@ ok supports(`+`, 34)
no compiles(4+5.0 * "hallo") no compiles(4+5.0 * "hallo")
no compiles(undeclaredIdentifier)
no compiles(undeclaredIdentifier)

View file

@ -12,7 +12,7 @@ var
thr: array [0..5, TThread[tuple[a, b: int]]] thr: array [0..5, TThread[tuple[a, b: int]]]
L, M, N: TLock L, M, N: TLock
proc doNothing() = nil proc doNothing() = discard
proc threadFunc(interval: tuple[a, b: int]) {.thread.} = proc threadFunc(interval: tuple[a, b: int]) {.thread.} =
doNothing() doNothing()

View file

@ -12,6 +12,13 @@ myobj destroyed
---- ----
mygeneric3 constructed mygeneric3 constructed
mygeneric1 destroyed mygeneric1 destroyed
----
mygeneric1 destroyed
----
myobj destroyed
----
----
myobj destroyed
''' '''
""" """
@ -32,6 +39,22 @@ type
y: B y: B
z: C z: C
TObjKind = enum A, B, C, D
TCaseObj = object
case kind: TObjKind
of A:
x: TMyGeneric1[int]
of B, C:
y: TMyObj
else:
case innerKind: TObjKind
of A, B, C:
p: TMyGeneric3[int, float, string]
of D:
q: TMyGeneric3[TMyObj, int, int]
r: string
proc destruct(o: var TMyObj) {.destructor.} = proc destruct(o: var TMyObj) {.destructor.} =
if o.p != nil: dealloc o.p if o.p != nil: dealloc o.p
echo "myobj destroyed" echo "myobj destroyed"
@ -57,13 +80,13 @@ proc mygeneric1() =
echo "mygeneric1 constructed" echo "mygeneric1 constructed"
proc mygeneric2[T](val: T) = proc mygeneric2[T](val: T) =
var var a = open()
a = open()
b = TMyGeneric2[int, T](x: 10, y: val)
c = TMyGeneric3[int, int, string](x: 10, y: 20, z: "test")
var b = TMyGeneric2[int, T](x: 10, y: val)
echo "mygeneric2 constructed" echo "mygeneric2 constructed"
var c = TMyGeneric3[int, int, string](x: 10, y: 20, z: "test")
proc mygeneric3 = proc mygeneric3 =
var x = TMyGeneric3[int, string, TMyGeneric1[int]]( var x = TMyGeneric3[int, string, TMyGeneric1[int]](
x: 10, y: "test", z: TMyGeneric1[int](x: 10)) x: 10, y: "test", z: TMyGeneric1[int](x: 10))
@ -82,3 +105,24 @@ mygeneric2[int](10)
echo "----" echo "----"
mygeneric3() mygeneric3()
proc caseobj =
block:
echo "----"
var o1 = TCaseObj(kind: A, x: TMyGeneric1[int](x: 10))
block:
echo "----"
var o2 = TCaseObj(kind: B, y: open())
block:
echo "----"
var o3 = TCaseObj(kind: D, innerKind: B, r: "test",
p: TMyGeneric3[int, float, string](x: 10, y: 1.0, z: "test"))
block:
echo "----"
var o4 = TCaseObj(kind: D, innerKind: D, r: "test",
q: TMyGeneric3[TMyObj, int, int](x: open(), y: 1, z: 0))
caseobj()

View file

@ -6,7 +6,7 @@ type
PDict[TK, TV] = ref TDict[TK, TV] PDict[TK, TV] = ref TDict[TK, TV]
proc fakeNew[T](x: var ref T, destroy: proc (a: ref T) {.nimcall.}) = proc fakeNew[T](x: var ref T, destroy: proc (a: ref T) {.nimcall.}) =
nil discard
proc destroyDict[TK, TV](a: PDict[TK, TV]) = proc destroyDict[TK, TV](a: PDict[TK, TV]) =
return return

View file

@ -11,3 +11,19 @@ proc q[T](x, y: T): T {.discardable.} =
p(8, 2) p(8, 2)
q[float](0.8, 0.2) q[float](0.8, 0.2)
# bug #942
template maybeMod(x: Tinteger, module:Natural):expr =
if module > 0: x mod module
else: x
proc foo(b: int):int =
var x = 1
result = x.maybeMod(b) # Works fine
proc bar(b: int):int =
result = 1
result = result.maybeMod(b) # Error: value returned by statement has to be discarded
echo foo(0)
echo bar(0)

View file

@ -1,6 +1,6 @@
discard """ discard """
line: 10 line: 10
errormsg: "value returned by statement has to be discarded" errormsg: "value of type 'bool' has to be discarded"
""" """
proc p = proc p =

View file

@ -4,7 +4,7 @@ type
PMenuItem = ref object PMenuItem = ref object
proc createMenuItem*(menu: PMenu, label: string, proc createMenuItem*(menu: PMenu, label: string,
action: proc (i: PMenuItem, p: pointer) {.cdecl.}) = nil action: proc (i: PMenuItem, p: pointer) {.cdecl.}) = discard
var s: PMenu var s: PMenu
createMenuItem(s, "Go to definition...", createMenuItem(s, "Go to definition...",

View file

@ -0,0 +1,45 @@
discard """
file: "tnestedbreak.nim"
output: "1\n2\n3\n4"
"""
# First variety
try:
raise newException(EOS, "Problem")
except EOS:
for y in [1, 2, 3]:
discard
try:
discard
except EOS:
discard
echo "1"
# Second Variety
try:
raise newException(EOS, "Problem")
except EOS:
for y in [1, 2, 3]:
discard
for y in [1, 2, 3]:
discard
echo "2"
# Third Variety
try:
raise newException(EOS, "Problem")
except EOS:
block:
break
echo "3"
# Fourth Variety
block:
try:
raise newException(EOS, "Problem")
except EOS:
break
echo "4"

Some files were not shown because too many files have changed in this diff Show more