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

Conflicts:
	lib/pure/osproc.nim
This commit is contained in:
Michał Zieliński 2014-01-26 17:06:21 +01:00
commit e0e0b409e4
64 changed files with 1141 additions and 574 deletions

View file

@ -336,27 +336,53 @@ type
tyConst, tyMutable, tyVarargs, tyConst, tyMutable, tyVarargs,
tyIter, # unused tyIter, # unused
tyProxy # used as errornous type (for idetools) tyProxy # used as errornous type (for idetools)
tyTypeClass
tyParametricTypeClass # structured similarly to tyGenericInst
# lastSon is the body of the type class
tyBuiltInTypeClass # Type such as the catch-all object, tuple, seq, etc tyBuiltInTypeClass #\
# Type such as the catch-all object, tuple, seq, etc
tyCompositeTypeClass # tyUserTypeClass #\
# the body of a user-defined type class
tyAnd, tyOr, tyNot # boolean type classes such as `string|int`,`not seq`, tyUserTypeClassInst #\
# Instance of a parametric user-defined type class.
# Structured similarly to tyGenericInst.
# tyGenericInst represents concrete types, while
# this is still a "generic param" that will bind types
# and resolves them during sigmatch and instantiation.
tyCompositeTypeClass #\
# Type such as seq[Number]
# The notes for tyUserTypeClassInst apply here as well
# sons[0]: the original expression used by the user.
# sons[1]: fully expanded and instantiated meta type
# (potentially following aliases)
tyAnd, tyOr, tyNot #\
# boolean type classes such as `string|int`,`not seq`,
# `Sortable and Enumable`, etc # `Sortable and Enumable`, etc
tyAnything # a type class matching any type tyAnything #\
# a type class matching any type
tyStatic # a value known at compile type (the underlying type is .base) tyStatic #\
# a value known at compile type (the underlying type is .base)
tyFromExpr # This is a type representing an expression that depends tyFromExpr #\
# This is a type representing an expression that depends
# on generic parameters (the exprsesion is stored in t.n) # on generic parameters (the exprsesion is stored in t.n)
# It will be converted to a real type only during generic # It will be converted to a real type only during generic
# instantiation and prior to this it has the potential to # instantiation and prior to this it has the potential to
# be any type. # be any type.
tyFieldAccessor #\
# Expressions such as Type.field (valid in contexts such
# as the `is` operator and magics like `high` and `low`).
# Could be lifted to a single argument proc returning the
# field value.
# sons[0]: type of containing object or tuple
# sons[1]: field type
# .n: nkDotExpr storing the field name
const const
tyPureObject* = tyTuple tyPureObject* = tyTuple
GcTypeKinds* = {tyRef, tySequence, tyString} GcTypeKinds* = {tyRef, tySequence, tyString}
@ -364,8 +390,9 @@ const
tyUnknownTypes* = {tyError, tyFromExpr} tyUnknownTypes* = {tyError, tyFromExpr}
tyTypeClasses* = {tyTypeClass, tyBuiltInTypeClass, tyCompositeTypeClass, tyTypeClasses* = {tyBuiltInTypeClass, tyCompositeTypeClass,
tyParametricTypeClass, tyAnd, tyOr, tyNot, tyAnything} tyUserTypeClass, tyUserTypeClassInst,
tyAnd, tyOr, tyNot, tyAnything}
tyMetaTypes* = {tyGenericParam, tyTypeDesc, tyStatic, tyExpr} + tyTypeClasses tyMetaTypes* = {tyGenericParam, tyTypeDesc, tyStatic, tyExpr} + tyTypeClasses

View file

@ -146,7 +146,8 @@ proc genClosureCall(p: BProc, le, ri: PNode, d: var TLoc) =
proc addComma(r: PRope): PRope = proc addComma(r: PRope): PRope =
result = if r == nil: r else: con(r, ~", ") result = if r == nil: r else: con(r, ~", ")
const CallPattern = "$1.ClEnv? $1.ClPrc($3$1.ClEnv) : (($4)($1.ClPrc))($2)" const PatProc = "$1.ClEnv? $1.ClPrc($3$1.ClEnv):(($4)($1.ClPrc))($2)"
const PatIter = "$1.ClPrc($3$1.ClEnv)" # we know the env exists
var op: TLoc var op: TLoc
initLocExpr(p, ri.sons[0], op) initLocExpr(p, ri.sons[0], op)
var pl: PRope var pl: PRope
@ -164,9 +165,10 @@ proc genClosureCall(p: BProc, le, ri: PNode, d: var TLoc) =
if i < length - 1: app(pl, ~", ") if i < length - 1: app(pl, ~", ")
template genCallPattern {.dirty.} = template genCallPattern {.dirty.} =
lineF(p, cpsStmts, CallPattern & ";$n", op.r, pl, pl.addComma, rawProc) lineF(p, cpsStmts, callPattern & ";$n", op.r, pl, pl.addComma, rawProc)
let rawProc = getRawProcType(p, typ) let rawProc = getRawProcType(p, typ)
let callPattern = if tfIterator in typ.flags: PatIter else: PatProc
if typ.sons[0] != nil: if typ.sons[0] != nil:
if isInvalidReturnType(typ.sons[0]): if isInvalidReturnType(typ.sons[0]):
if sonsLen(ri) > 1: app(pl, ~", ") if sonsLen(ri) > 1: app(pl, ~", ")
@ -190,7 +192,7 @@ proc genClosureCall(p: BProc, le, ri: PNode, d: var TLoc) =
assert(d.t != nil) # generate an assignment to d: assert(d.t != nil) # generate an assignment to d:
var list: TLoc var list: TLoc
initLoc(list, locCall, d.t, OnUnknown) initLoc(list, locCall, d.t, OnUnknown)
list.r = ropef(CallPattern, op.r, pl, pl.addComma, rawProc) list.r = ropef(callPattern, op.r, pl, pl.addComma, rawProc)
genAssignment(p, d, list, {}) # no need for deep copying genAssignment(p, d, list, {}) # no need for deep copying
else: else:
genCallPattern() genCallPattern()

View file

@ -87,7 +87,7 @@ proc getUniqueType*(key: PType): PType =
gCanonicalTypes[k] = key gCanonicalTypes[k] = key
result = key result = key
of tyTypeDesc, tyTypeClasses, tyGenericParam, of tyTypeDesc, tyTypeClasses, tyGenericParam,
tyFromExpr, tyStatic: tyFromExpr, tyStatic, tyFieldAccessor:
internalError("GetUniqueType") internalError("GetUniqueType")
of tyGenericInst, tyDistinct, tyOrdinal, tyMutable, tyConst, tyIter: of tyGenericInst, tyDistinct, tyOrdinal, tyMutable, tyConst, tyIter:
result = getUniqueType(lastSon(key)) result = getUniqueType(lastSon(key))

View file

@ -913,21 +913,21 @@ proc addIntTypes(result: var PRope) {.inline.} =
proc getCopyright(cfilenoext: string): PRope = proc getCopyright(cfilenoext: string): PRope =
if optCompileOnly in gGlobalOptions: if optCompileOnly in gGlobalOptions:
result = ropeff("/* Generated by Nimrod Compiler v$1 */$n" & result = ropeff("/* Generated by Nimrod Compiler v$1 */$N" &
"/* (c) 2014 Andreas Rumpf */$n" & "/* (c) 2014 Andreas Rumpf */$N" &
"/* The generated code is subject to the original license. */$n", "/* The generated code is subject to the original license. */$N",
"; Generated by Nimrod Compiler v$1$n" & "; Generated by Nimrod Compiler v$1$N" &
"; (c) 2012 Andreas Rumpf$n", [toRope(VersionAsString)]) "; (c) 2012 Andreas Rumpf$N", [toRope(VersionAsString)])
else: else:
result = ropeff("/* Generated by Nimrod Compiler v$1 */$n" & result = ropeff("/* Generated by Nimrod Compiler v$1 */$N" &
"/* (c) 2014 Andreas Rumpf */$n" & "/* (c) 2014 Andreas Rumpf */$N" &
"/* The generated code is subject to the original license. */$n" & "/* The generated code is subject to the original license. */$N" &
"/* Compiled for: $2, $3, $4 */$n" & "/* Compiled for: $2, $3, $4 */$N" &
"/* Command for C compiler:$n $5 */$n", "/* Command for C compiler:$n $5 */$N",
"; Generated by Nimrod Compiler v$1$n" & "; Generated by Nimrod Compiler v$1$N" &
"; (c) 2014 Andreas Rumpf$n" & "; (c) 2014 Andreas Rumpf$N" &
"; Compiled for: $2, $3, $4$n" & "; Compiled for: $2, $3, $4$N" &
"; Command for LLVM compiler:$n $5$n", [toRope(VersionAsString), "; Command for LLVM compiler:$N $5$N", [toRope(VersionAsString),
toRope(platform.OS[targetOS].name), toRope(platform.OS[targetOS].name),
toRope(platform.CPU[targetCPU].name), toRope(platform.CPU[targetCPU].name),
toRope(extccomp.CC[extccomp.cCompiler].name), toRope(extccomp.CC[extccomp.cCompiler].name),
@ -941,61 +941,71 @@ proc genFilenames(m: BModule): PRope =
discard cgsym(m, "dbgRegisterFilename") discard cgsym(m, "dbgRegisterFilename")
result = nil result = nil
for i in 0.. <fileInfos.len: for i in 0.. <fileInfos.len:
result.appf("dbgRegisterFilename($1);$n", fileInfos[i].projPath.makeCString) result.appf("dbgRegisterFilename($1);$N", fileInfos[i].projPath.makeCString)
proc genMainProc(m: BModule) = proc genMainProc(m: BModule) =
const const
PreMainBody = PreMainBody =
"\tsystemDatInit();$n" & "\tsystemDatInit();$N" &
"\tsystemInit();$n" & "\tsystemInit();$N" &
"$1" & "$1" &
"$2" & "$2" &
"$3" & "$3" &
"$4" "$4"
MainProcs = MainProcs =
"\tPreMain();$n" & "\tNimMain();$N"
"\tNimMain();$n"
MainProcsWithResult = MainProcsWithResult =
MainProcs & "\treturn nim_program_result;$n" MainProcs & "\treturn nim_program_result;$N"
NimMainBody =
"N_CDECL(void, NimMain)(void) {$N" &
"\tPreMain();$N" &
"$1$N" &
"}$N"
PosixNimMain = PosixNimMain =
"int cmdCount;$n" & "int cmdCount;$N" &
"char** cmdLine;$n" & "char** cmdLine;$N" &
"char** gEnv;$n" & "char** gEnv;$N" &
"N_CDECL(void, NimMain)(void) {$n$1}$n" NimMainBody
PosixCMain = "int main(int argc, char** args, char** env) {$n" & PosixCMain =
"\tcmdLine = args;$n" & "\tcmdCount = argc;$n" & "\tgEnv = env;$n" & "int main(int argc, char** args, char** env) {$N" &
"\tcmdLine = args;$N" &
"\tcmdCount = argc;$N" &
"\tgEnv = env;$N" &
MainProcsWithResult & MainProcsWithResult &
"}$n" "}$N"
StandaloneCMain = "int main(void) {$n" & StandaloneCMain =
"int main(void) {$N" &
MainProcs & MainProcs &
"\treturn 0;$n" & "}$n" "\treturn 0;$N" &
"}$N"
WinNimMain = "N_CDECL(void, NimMain)(void) {$n$1}$n" 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"
WinNimDllMain = "N_LIB_EXPORT N_CDECL(void, NimMain)(void) {$n$1}$n" WinNimDllMain = "N_LIB_EXPORT " & NimMainBody
WinCDllMain = WinCDllMain =
"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) {" & MainProcs & "}$n" & "\tif(fwdreason == DLL_PROCESS_ATTACH) {$N" & MainProcs & "}$N" &
"\treturn 1;$n}$n" "\treturn 1;$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"
var nimMain, otherMain: TFormatStr var nimMain, otherMain: TFormatStr
if platform.targetOS == osWindows and if platform.targetOS == osWindows and
@ -1022,9 +1032,9 @@ proc genMainProc(m: BModule) =
let initStackBottomCall = if emulatedThreadVars() or let initStackBottomCall = if emulatedThreadVars() or
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", [
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)])
@ -1049,8 +1059,8 @@ proc registerModuleToMain(m: PSym) =
appff(mainModProcs, "N_NOINLINE(void, $1)(void);$N", appff(mainModProcs, "N_NOINLINE(void, $1)(void);$N",
"declare void $1() noinline$N", [datInit]) "declare void $1() noinline$N", [datInit])
if sfSystemModule notin m.flags: if sfSystemModule notin m.flags:
appff(mainDatInit, "\t$1();$n", "call void ()* $1$n", [datInit]) appff(mainDatInit, "\t$1();$N", "call void ()* $1$n", [datInit])
let initCall = ropeff("\t$1();$n", "call void ()* $1$n", [init]) let initCall = ropeff("\t$1();$N", "call void ()* $1$n", [init])
if sfMainModule in m.flags: if sfMainModule in m.flags:
app(mainModInit, initCall) app(mainModInit, initCall)
else: else:
@ -1058,7 +1068,7 @@ proc registerModuleToMain(m: PSym) =
proc genInitCode(m: BModule) = proc genInitCode(m: BModule) =
var initname = getInitName(m.module) var initname = getInitName(m.module)
var prc = ropeff("N_NOINLINE(void, $1)(void) {$n", var prc = ropeff("N_NOINLINE(void, $1)(void) {$N",
"define void $1() noinline {$n", [initname]) "define void $1() noinline {$n", [initname])
if m.typeNodes > 0: if m.typeNodes > 0:
appcg(m, m.s[cfsTypeInit1], "static #TNimNode $1[$2];$n", appcg(m, m.s[cfsTypeInit1], "static #TNimNode $1[$2];$n",
@ -1101,7 +1111,7 @@ proc genInitCode(m: BModule) =
app(prc, deinitGCFrame(m.initProc)) app(prc, deinitGCFrame(m.initProc))
appf(prc, "}$N$N") appf(prc, "}$N$N")
prc.appff("N_NOINLINE(void, $1)(void) {$n", prc.appff("N_NOINLINE(void, $1)(void) {$N",
"define void $1() noinline {$n", [getDatInitName(m.module)]) "define void $1() noinline {$n", [getDatInitName(m.module)])
for i in cfsTypeInit1..cfsDynLibInit: for i in cfsTypeInit1..cfsDynLibInit:

View file

@ -130,7 +130,7 @@ proc mapType(typ: PType): TJSTypeKind =
result = etyObject result = etyObject
of tyNil: result = etyNull of tyNil: result = etyNull
of tyGenericInst, tyGenericParam, tyGenericBody, tyGenericInvokation, of tyGenericInst, tyGenericParam, tyGenericBody, tyGenericInvokation,
tyNone, tyFromExpr, tyForward, tyEmpty, tyNone, tyFromExpr, tyForward, tyEmpty, tyFieldAccessor,
tyExpr, tyStmt, tyStatic, tyTypeDesc, tyTypeClasses: tyExpr, tyStmt, tyStatic, tyTypeDesc, tyTypeClasses:
result = etyNone result = etyNone
of tyProc: result = etyProc of tyProc: result = etyProc

View file

@ -116,7 +116,7 @@ type
TDep = tuple[e: PEnv, field: PSym] TDep = tuple[e: PEnv, field: PSym]
TEnv {.final.} = object of TObject TEnv {.final.} = object of TObject
attachedNode: PNode attachedNode: PNode
closure: PSym # if != nil it is a used environment createdVar: PSym # if != nil it is a used environment
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
@ -130,12 +130,99 @@ type
TOuterContext {.final.} = object TOuterContext {.final.} = object
fn: PSym # may also be a module! fn: PSym # may also be a module!
currentEnv: PEnv currentEnv: PEnv
isIter: bool # first class iterator?
capturedVars, processed: TIntSet capturedVars, processed: TIntSet
localsToEnv: TIdTable # PSym->PEnv mapping localsToEnv: TIdTable # PSym->PEnv mapping
localsToAccess: TIdNodeTable localsToAccess: TIdNodeTable
lambdasToEnv: TIdTable # PSym->PEnv mapping lambdasToEnv: TIdTable # PSym->PEnv mapping
up: POuterContext up: POuterContext
closureParam, state, resultSym: PSym # only if isIter
tup: PType # only if isIter
proc getStateType(iter: PSym): PType =
var n = newNodeI(nkRange, iter.info)
addSon(n, newIntNode(nkIntLit, -1))
addSon(n, newIntNode(nkIntLit, 0))
result = newType(tyRange, iter)
result.n = n
rawAddSon(result, getSysType(tyInt))
proc createStateField(iter: PSym): PSym =
result = newSym(skField, getIdent(":state"), iter, iter.info)
result.typ = getStateType(iter)
proc newIterResult(iter: PSym): PSym =
if resultPos < iter.ast.len:
result = iter.ast.sons[resultPos].sym
else:
# XXX a bit hacky:
result = newSym(skResult, getIdent":result", iter, iter.info)
result.typ = iter.typ.sons[0]
incl(result.flags, sfUsed)
iter.ast.add newSymNode(result)
proc addHiddenParam(routine: PSym, param: PSym) =
var params = routine.ast.sons[paramsPos]
# -1 is correct here as param.position is 0 based but we have at position 0
# some nkEffect node:
param.position = params.len-1
addSon(params, newSymNode(param))
incl(routine.typ.flags, tfCapturesEnv)
#echo "produced environment: ", param.id, " for ", routine.name.s
proc getHiddenParam(routine: PSym): PSym =
let params = routine.ast.sons[paramsPos]
let hidden = lastSon(params)
assert hidden.kind == nkSym
result = hidden.sym
proc getEnvParam(routine: PSym): PSym =
let params = routine.ast.sons[paramsPos]
let hidden = lastSon(params)
if hidden.kind == nkSym and hidden.sym.name.s == paramName:
result = hidden.sym
proc addField(tup: PType, s: PSym) =
var field = newSym(skField, s.name, s.owner, s.info)
let t = skipIntLit(s.typ)
field.typ = t
field.position = sonsLen(tup)
addSon(tup.n, newSymNode(field))
rawAddSon(tup, t)
proc initIterContext(c: POuterContext, iter: PSym) =
c.fn = iter
c.capturedVars = initIntSet()
var cp = getEnvParam(iter)
if cp == nil:
c.tup = newType(tyTuple, iter)
c.tup.n = newNodeI(nkRecList, iter.info)
cp = newSym(skParam, getIdent(paramName), iter, iter.info)
incl(cp.flags, sfFromGeneric)
cp.typ = newType(tyRef, iter)
rawAddSon(cp.typ, c.tup)
addHiddenParam(iter, cp)
c.state = createStateField(iter)
addField(c.tup, c.state)
else:
c.tup = cp.typ.sons[0]
assert c.tup.kind == tyTuple
if c.tup.len > 0:
c.state = c.tup.n[0].sym
else:
c.state = createStateField(iter)
addField(c.tup, c.state)
c.closureParam = cp
if iter.typ.sons[0] != nil:
c.resultSym = newIterResult(iter)
#iter.ast.add(newSymNode(c.resultSym))
proc newOuterContext(fn: PSym, up: POuterContext = nil): POuterContext = proc newOuterContext(fn: PSym, up: POuterContext = nil): POuterContext =
new(result) new(result)
result.fn = fn result.fn = fn
@ -144,6 +231,8 @@ proc newOuterContext(fn: PSym, up: POuterContext = nil): POuterContext =
initIdNodeTable(result.localsToAccess) initIdNodeTable(result.localsToAccess)
initIdTable(result.localsToEnv) initIdTable(result.localsToEnv)
initIdTable(result.lambdasToEnv) initIdTable(result.lambdasToEnv)
result.isIter = fn.kind == skIterator and fn.typ.callConv == ccClosure
if result.isIter: initIterContext(result, fn)
proc newInnerContext(fn: PSym): PInnerContext = proc newInnerContext(fn: PSym): PInnerContext =
new(result) new(result)
@ -159,17 +248,12 @@ proc newEnv(outerProc: PSym, up: PEnv, n: PNode): PEnv =
result.up = up result.up = up
result.attachedNode = n result.attachedNode = n
proc addField(tup: PType, s: PSym) =
var field = newSym(skField, s.name, s.owner, s.info)
let t = skipIntLit(s.typ)
field.typ = t
field.position = sonsLen(tup)
addSon(tup.n, newSymNode(field))
rawAddSon(tup, t)
proc addCapturedVar(e: PEnv, v: PSym) = proc addCapturedVar(e: PEnv, v: PSym) =
for x in e.capturedVars: for x in e.capturedVars:
if x == v: return if x == v: return
# XXX meh, just add the state field for every closure for now, it's too
# hard to figure out if it comes from a closure iterator:
if e.tup.len == 0: addField(e.tup, createStateField(v.owner))
e.capturedVars.add(v) e.capturedVars.add(v)
addField(e.tup, v) addField(e.tup, v)
@ -189,6 +273,7 @@ proc indirectAccess(a: PNode, b: PSym, info: TLineInfo): PNode =
# returns a[].b as a node # returns a[].b as a node
var deref = newNodeI(nkHiddenDeref, info) var deref = newNodeI(nkHiddenDeref, info)
deref.typ = a.typ.sons[0] deref.typ = a.typ.sons[0]
assert deref.typ.kind == tyTuple
let field = getSymFromList(deref.typ.n, b.name) let field = getSymFromList(deref.typ.n, b.name)
assert field != nil, b.name.s assert field != nil, b.name.s
addSon(deref, a) addSon(deref, a)
@ -205,33 +290,24 @@ proc newCall(a, b: PSym): PNode =
result.add newSymNode(a) result.add newSymNode(a)
result.add newSymNode(b) result.add newSymNode(b)
proc addHiddenParam(routine: PSym, param: PSym) =
var params = routine.ast.sons[paramsPos]
# -1 is correct here as param.position is 0 based but we have at position 0
# some nkEffect node:
param.position = params.len-1
addSon(params, newSymNode(param))
incl(routine.typ.flags, tfCapturesEnv)
#echo "produced environment: ", param.id, " for ", routine.name.s
proc getHiddenParam(routine: PSym): PSym =
let params = routine.ast.sons[paramsPos]
let hidden = lastSon(params)
assert hidden.kind == nkSym
result = hidden.sym
proc isInnerProc(s, outerProc: PSym): bool {.inline.} = proc isInnerProc(s, outerProc: PSym): bool {.inline.} =
result = s.kind in {skProc, skMethod, skConverter} and result = (s.kind in {skProc, skMethod, skConverter} or
s.kind == skIterator and s.typ.callConv == ccClosure) and
s.skipGenericOwner == outerProc s.skipGenericOwner == outerProc
#s.typ.callConv == ccClosure #s.typ.callConv == ccClosure
proc addClosureParam(i: PInnerContext, e: PEnv) = proc addClosureParam(i: PInnerContext, e: PEnv) =
var cp = newSym(skParam, getIdent(paramName), i.fn, i.fn.info) var cp = getEnvParam(i.fn)
if cp == nil:
cp = newSym(skParam, getIdent(paramName), i.fn, i.fn.info)
incl(cp.flags, sfFromGeneric) incl(cp.flags, sfFromGeneric)
cp.typ = newType(tyRef, i.fn) cp.typ = newType(tyRef, i.fn)
rawAddSon(cp.typ, e.tup) rawAddSon(cp.typ, e.tup)
addHiddenParam(i.fn, cp)
else:
e.tup = cp.typ.sons[0]
assert e.tup.kind == tyTuple
i.closureParam = cp i.closureParam = cp
addHiddenParam(i.fn, i.closureParam)
#echo "closure param added for ", i.fn.name.s, " ", i.fn.id #echo "closure param added for ", i.fn.name.s, " ", i.fn.id
proc dummyClosureParam(o: POuterContext, i: PInnerContext) = proc dummyClosureParam(o: POuterContext, i: PInnerContext) =
@ -306,7 +382,9 @@ proc gatherVars(o: POuterContext, i: PInnerContext, n: PNode) =
var s = n.sym var s = n.sym
if interestingVar(s) and i.fn.id != s.owner.id: if interestingVar(s) and i.fn.id != s.owner.id:
captureVar(o, i, s, n.info) captureVar(o, i, s, n.info)
elif isInnerProc(s, o.fn) and tfCapturesEnv in s.typ.flags and s != i.fn: elif s.kind in {skProc, skMethod, skConverter} and
s.skipGenericOwner == o.fn and
tfCapturesEnv in s.typ.flags and s != i.fn:
# call to some other inner proc; we need to track the dependencies for # call to some other inner proc; we need to track the dependencies for
# this: # this:
let env = PEnv(idTableGet(o.lambdasToEnv, i.fn)) let env = PEnv(idTableGet(o.lambdasToEnv, i.fn))
@ -314,7 +392,7 @@ proc gatherVars(o: POuterContext, i: PInnerContext, n: PNode) =
if o.currentEnv != env: if o.currentEnv != env:
discard addDep(o.currentEnv, env, i.fn) discard addDep(o.currentEnv, env, i.fn)
internalError(n.info, "too complex environment handling required") internalError(n.info, "too complex environment handling required")
of nkEmpty..pred(nkSym), succ(nkSym)..nkNilLit: discard of nkEmpty..pred(nkSym), succ(nkSym)..nkNilLit, nkClosure: discard
else: else:
for k in countup(0, sonsLen(n) - 1): for k in countup(0, sonsLen(n) - 1):
gatherVars(o, i, n.sons[k]) gatherVars(o, i, n.sons[k])
@ -366,10 +444,11 @@ proc transformInnerProc(o: POuterContext, i: PInnerContext, n: PNode): PNode =
else: else:
# captured symbol? # captured symbol?
result = idNodeTableGet(i.localsToAccess, n.sym) result = idNodeTableGet(i.localsToAccess, n.sym)
of nkLambdaKinds: of nkLambdaKinds, nkIteratorDef:
if n.typ != nil:
result = transformInnerProc(o, i, n.sons[namePos]) result = transformInnerProc(o, i, n.sons[namePos])
of nkProcDef, nkMethodDef, nkConverterDef, nkMacroDef, nkTemplateDef, of nkProcDef, nkMethodDef, nkConverterDef, nkMacroDef, nkTemplateDef,
nkIteratorDef: nkClosure:
# don't recurse here: # don't recurse here:
discard discard
else: else:
@ -400,7 +479,8 @@ proc searchForInnerProcs(o: POuterContext, n: PNode) =
if inner.closureParam != nil: if inner.closureParam != nil:
let ti = transformInnerProc(o, inner, body) let ti = transformInnerProc(o, inner, body)
if ti != nil: n.sym.ast.sons[bodyPos] = ti if ti != nil: n.sym.ast.sons[bodyPos] = ti
of nkLambdaKinds: of nkLambdaKinds, nkIteratorDef:
if n.typ != nil:
searchForInnerProcs(o, n.sons[namePos]) searchForInnerProcs(o, n.sons[namePos])
of nkWhileStmt, nkForStmt, nkParForStmt, nkBlockStmt: of nkWhileStmt, nkForStmt, nkParForStmt, nkBlockStmt:
# some nodes open a new scope, so they are candidates for the insertion # some nodes open a new scope, so they are candidates for the insertion
@ -438,7 +518,7 @@ proc searchForInnerProcs(o: POuterContext, n: PNode) =
else: else:
internalError(it.info, "transformOuter") internalError(it.info, "transformOuter")
of nkProcDef, nkMethodDef, nkConverterDef, nkMacroDef, nkTemplateDef, of nkProcDef, nkMethodDef, nkConverterDef, nkMacroDef, nkTemplateDef,
nkIteratorDef: nkClosure:
# don't recurse here: # don't recurse here:
# XXX recurse here and setup 'up' pointers # XXX recurse here and setup 'up' pointers
discard discard
@ -463,19 +543,20 @@ proc addVar*(father, v: PNode) =
addSon(vpart, ast.emptyNode) addSon(vpart, ast.emptyNode)
addSon(father, vpart) addSon(father, vpart)
proc getClosureVar(o: POuterContext, e: PEnv): PSym = proc newClosureCreationVar(o: POuterContext; e: PEnv): PSym =
if e.closure == nil:
result = newSym(skVar, getIdent(envName), o.fn, e.attachedNode.info) result = newSym(skVar, getIdent(envName), o.fn, e.attachedNode.info)
incl(result.flags, sfShadowed) incl(result.flags, sfShadowed)
result.typ = newType(tyRef, o.fn) result.typ = newType(tyRef, o.fn)
result.typ.rawAddSon(e.tup) result.typ.rawAddSon(e.tup)
e.closure = result
proc getClosureVar(o: POuterContext; e: PEnv): PSym =
if e.createdVar == nil:
result = newClosureCreationVar(o, e)
e.createdVar = result
else: else:
result = e.closure result = e.createdVar
proc generateClosureCreation(o: POuterContext, scope: PEnv): PNode =
var env = getClosureVar(o, scope)
proc rawClosureCreation(o: POuterContext, scope: PEnv; env: PSym): PNode =
result = newNodeI(nkStmtList, env.info) result = newNodeI(nkStmtList, env.info)
var v = newNodeI(nkVarSection, env.info) var v = newNodeI(nkVarSection, env.info)
addVar(v, newSymNode(env)) addVar(v, newSymNode(env))
@ -497,16 +578,90 @@ proc generateClosureCreation(o: POuterContext, scope: PEnv): PNode =
result.add(newAsgnStmt(indirectAccess(env, field, env.info), result.add(newAsgnStmt(indirectAccess(env, field, env.info),
newSymNode(getClosureVar(o, e)), env.info)) newSymNode(getClosureVar(o, e)), env.info))
proc generateClosureCreation(o: POuterContext, scope: PEnv): PNode =
var env = getClosureVar(o, scope)
result = rawClosureCreation(o, scope, env)
proc generateIterClosureCreation(o: POuterContext; env: PEnv;
scope: PNode): PSym =
result = newClosureCreationVar(o, env)
let cc = rawClosureCreation(o, env, result)
var insertPoint = scope.sons[0]
if insertPoint.kind == nkEmpty: scope.sons[0] = cc
else:
assert cc.kind == nkStmtList and insertPoint.kind == nkStmtList
for x in cc: insertPoint.add(x)
if env.createdVar == nil: env.createdVar = result
proc interestingIterVar(s: PSym): bool {.inline.} =
result = s.kind in {skVar, skLet, skTemp, skForVar} and sfGlobal notin s.flags
proc transformOuterProc(o: POuterContext, n: PNode): PNode
proc transformYield(c: POuterContext, n: PNode): PNode =
inc c.state.typ.n.sons[1].intVal
let stateNo = c.state.typ.n.sons[1].intVal
var stateAsgnStmt = newNodeI(nkAsgn, n.info)
stateAsgnStmt.add(indirectAccess(newSymNode(c.closureParam),c.state,n.info))
stateAsgnStmt.add(newIntTypeNode(nkIntLit, stateNo, getSysType(tyInt)))
var retStmt = newNodeI(nkReturnStmt, n.info)
if n.sons[0].kind != nkEmpty:
var a = newNodeI(nkAsgn, n.sons[0].info)
var retVal = transformOuterProc(c, n.sons[0])
addSon(a, newSymNode(c.resultSym))
addSon(a, if retVal.isNil: n.sons[0] else: retVal)
retStmt.add(a)
else:
retStmt.add(emptyNode)
var stateLabelStmt = newNodeI(nkState, n.info)
stateLabelStmt.add(newIntTypeNode(nkIntLit, stateNo, getSysType(tyInt)))
result = newNodeI(nkStmtList, n.info)
result.add(stateAsgnStmt)
result.add(retStmt)
result.add(stateLabelStmt)
proc transformReturn(c: POuterContext, n: PNode): PNode =
result = newNodeI(nkStmtList, n.info)
var stateAsgnStmt = newNodeI(nkAsgn, n.info)
stateAsgnStmt.add(indirectAccess(newSymNode(c.closureParam),c.state,n.info))
stateAsgnStmt.add(newIntTypeNode(nkIntLit, -1, getSysType(tyInt)))
result.add(stateAsgnStmt)
result.add(n)
proc outerProcSons(o: POuterContext, n: PNode) =
for i in countup(0, sonsLen(n) - 1):
let x = transformOuterProc(o, n.sons[i])
if x != nil: n.sons[i] = x
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
of nkEmpty..pred(nkSym), succ(nkSym)..nkNilLit: discard of nkEmpty..pred(nkSym), succ(nkSym)..nkNilLit: discard
of nkSym: of nkSym:
var local = n.sym var local = n.sym
if o.isIter and interestingIterVar(local) and o.fn.id == local.owner.id:
if not containsOrIncl(o.capturedVars, local.id): addField(o.tup, local)
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: if closure != nil:
# we need to replace the lambda with '(lambda, env)': # we need to replace the lambda with '(lambda, env)':
let a = closure.closure if local.kind == skIterator and local.typ.callConv == ccClosure:
# consider: [i1, i2, i1] Since we merged the iterator's closure
# with the captured owning variables, we need to generate the
# closure generation code again:
#if local == o.fn: message(n.info, errRecursiveDependencyX, local.name.s)
# XXX why doesn't this work?
let createdVar = generateIterClosureCreation(o, closure,
closure.attachedNode)
return makeClosure(local, createdVar, n.info)
let a = closure.createdVar
if a != nil: if a != nil:
return makeClosure(local, a, n.info) return makeClosure(local, a, n.info)
else: else:
@ -516,7 +671,7 @@ proc transformOuterProc(o: POuterContext, n: PNode): PNode =
if scope.sons[0].kind == nkEmpty: if scope.sons[0].kind == nkEmpty:
# change the empty node to contain the closure construction: # change the empty node to contain the closure construction:
scope.sons[0] = generateClosureCreation(o, closure) scope.sons[0] = generateClosureCreation(o, closure)
let x = closure.closure let x = closure.createdVar
assert x != nil assert x != nil
return makeClosure(local, x, n.info) return makeClosure(local, x, n.info)
@ -535,20 +690,47 @@ proc transformOuterProc(o: POuterContext, n: PNode): PNode =
assert result != nil, "cannot find: " & local.name.s assert result != nil, "cannot find: " & local.name.s
# else it is captured by copy and this means that 'outer' should continue # else it is captured by copy and this means that 'outer' should continue
# to access the local as a local. # to access the local as a local.
of nkLambdaKinds: of nkLambdaKinds, nkIteratorDef:
if n.typ != nil:
result = transformOuterProc(o, n.sons[namePos]) result = transformOuterProc(o, n.sons[namePos])
of nkProcDef, nkMethodDef, nkConverterDef, nkMacroDef, nkTemplateDef, of nkProcDef, nkMethodDef, nkConverterDef, nkMacroDef, nkTemplateDef,
nkIteratorDef: nkClosure:
# don't recurse here: # don't recurse here:
discard discard
of nkHiddenStdConv, nkHiddenSubConv, nkConv: of nkHiddenStdConv, nkHiddenSubConv, nkConv:
let x = transformOuterProc(o, n.sons[1]) let x = transformOuterProc(o, n.sons[1])
if x != nil: n.sons[1] = x if x != nil: n.sons[1] = x
result = transformOuterConv(n) result = transformOuterConv(n)
of nkYieldStmt:
if o.isIter: result = transformYield(o, n)
else: outerProcSons(o, n)
of nkReturnStmt:
if o.isIter: result = transformReturn(o, n)
else: outerProcSons(o, n)
else: else:
for i in countup(0, sonsLen(n) - 1): outerProcSons(o, n)
let x = transformOuterProc(o, n.sons[i])
if x != nil: n.sons[i] = x proc liftIterator(c: POuterContext, body: PNode): PNode =
let iter = c.fn
result = newNodeI(nkStmtList, iter.info)
var gs = newNodeI(nkGotoState, iter.info)
gs.add(indirectAccess(newSymNode(c.closureParam), c.state, iter.info))
result.add(gs)
var state0 = newNodeI(nkState, iter.info)
state0.add(newIntNode(nkIntLit, 0))
result.add(state0)
let newBody = transformOuterProc(c, body)
if newBody != nil:
result.add(newBody)
else:
result.add(body)
var stateAsgnStmt = newNodeI(nkAsgn, iter.info)
stateAsgnStmt.add(indirectAccess(newSymNode(c.closureParam),
c.state,iter.info))
stateAsgnStmt.add(newIntTypeNode(nkIntLit, -1, getSysType(tyInt)))
result.add(stateAsgnStmt)
proc liftLambdas*(fn: PSym, body: PNode): PNode = proc liftLambdas*(fn: PSym, body: PNode): PNode =
# XXX gCmd == cmdCompileToJS does not suffice! The compiletime stuff needs # XXX gCmd == cmdCompileToJS does not suffice! The compiletime stuff needs
@ -572,6 +754,9 @@ proc liftLambdas*(fn: PSym, body: PNode): PNode =
if resultPos < sonsLen(ast) and ast.sons[resultPos].kind == nkSym: if resultPos < sonsLen(ast) and ast.sons[resultPos].kind == nkSym:
idTablePut(o.localsToEnv, ast.sons[resultPos].sym, o.currentEnv) idTablePut(o.localsToEnv, ast.sons[resultPos].sym, o.currentEnv)
searchForInnerProcs(o, body) searchForInnerProcs(o, body)
if o.isIter:
result = liftIterator(o, ex)
else:
discard transformOuterProc(o, body) discard transformOuterProc(o, body)
result = ex result = ex
@ -588,140 +773,15 @@ proc liftLambdasForTopLevel*(module: PSym, body: PNode): PNode =
# ------------------- iterator transformation -------------------------------- # ------------------- iterator transformation --------------------------------
discard """
iterator chain[S, T](a, b: *S->T, args: *S): T =
for x in a(args): yield x
for x in b(args): yield x
let c = chain(f, g)
for x in c: echo x
# translated to:
let c = chain( (f, newClosure(f)), (g, newClosure(g)), newClosure(chain))
"""
type
TIterContext {.final, pure.} = object
iter, closureParam, state, resultSym: PSym
capturedVars: TIntSet
tup: PType
proc newIterResult(iter: PSym): PSym =
result = iter.ast.sons[resultPos].sym
when false:
result = newSym(skResult, getIdent":result", iter, iter.info)
result.typ = iter.typ.sons[0]
incl(result.flags, sfUsed)
proc interestingIterVar(s: PSym): bool {.inline.} =
result = s.kind in {skVar, skLet, skTemp, skForVar} and sfGlobal notin s.flags
proc transfIterBody(c: var TIterContext, n: PNode): PNode =
# gather used vars for closure generation
if n == nil: return nil
case n.kind
of nkSym:
var s = n.sym
if interestingIterVar(s) and c.iter.id == s.owner.id:
if not containsOrIncl(c.capturedVars, s.id): addField(c.tup, s)
result = indirectAccess(newSymNode(c.closureParam), s, n.info)
of nkEmpty..pred(nkSym), succ(nkSym)..nkNilLit: discard
of nkYieldStmt:
inc c.state.typ.n.sons[1].intVal
let stateNo = c.state.typ.n.sons[1].intVal
var stateAsgnStmt = newNodeI(nkAsgn, n.info)
stateAsgnStmt.add(indirectAccess(newSymNode(c.closureParam),c.state,n.info))
stateAsgnStmt.add(newIntTypeNode(nkIntLit, stateNo, getSysType(tyInt)))
var retStmt = newNodeI(nkReturnStmt, n.info)
if n.sons[0].kind != nkEmpty:
var a = newNodeI(nkAsgn, n.sons[0].info)
var retVal = transfIterBody(c, n.sons[0])
addSon(a, newSymNode(c.resultSym))
addSon(a, if retVal.isNil: n.sons[0] else: retVal)
retStmt.add(a)
else:
retStmt.add(emptyNode)
var stateLabelStmt = newNodeI(nkState, n.info)
stateLabelStmt.add(newIntTypeNode(nkIntLit, stateNo, getSysType(tyInt)))
result = newNodeI(nkStmtList, n.info)
result.add(stateAsgnStmt)
result.add(retStmt)
result.add(stateLabelStmt)
of nkReturnStmt:
result = newNodeI(nkStmtList, n.info)
var stateAsgnStmt = newNodeI(nkAsgn, n.info)
stateAsgnStmt.add(indirectAccess(newSymNode(c.closureParam),c.state,n.info))
stateAsgnStmt.add(newIntTypeNode(nkIntLit, -1, getSysType(tyInt)))
result.add(stateAsgnStmt)
result.add(n)
else:
for i in countup(0, sonsLen(n)-1):
let x = transfIterBody(c, n.sons[i])
if x != nil: n.sons[i] = x
proc getStateType(iter: PSym): PType =
var n = newNodeI(nkRange, iter.info)
addSon(n, newIntNode(nkIntLit, -1))
addSon(n, newIntNode(nkIntLit, 0))
result = newType(tyRange, iter)
result.n = n
rawAddSon(result, getSysType(tyInt))
proc liftIterator*(iter: PSym, body: PNode): PNode =
var c: TIterContext
c.iter = iter
c.capturedVars = initIntSet()
c.tup = newType(tyTuple, iter)
c.tup.n = newNodeI(nkRecList, iter.info)
var cp = newSym(skParam, getIdent(paramName), iter, iter.info)
incl(cp.flags, sfFromGeneric)
cp.typ = newType(tyRef, iter)
rawAddSon(cp.typ, c.tup)
c.closureParam = cp
addHiddenParam(iter, cp)
c.state = newSym(skField, getIdent(":state"), iter, iter.info)
c.state.typ = getStateType(iter)
addField(c.tup, c.state)
if iter.typ.sons[0] != nil:
c.resultSym = newIterResult(iter)
iter.ast.add(newSymNode(c.resultSym))
result = newNodeI(nkStmtList, iter.info)
var gs = newNodeI(nkGotoState, iter.info)
gs.add(indirectAccess(newSymNode(c.closureParam), c.state, iter.info))
result.add(gs)
var state0 = newNodeI(nkState, iter.info)
state0.add(newIntNode(nkIntLit, 0))
result.add(state0)
let newBody = transfIterBody(c, body)
if newBody != nil:
result.add(newBody)
else:
result.add(body)
var stateAsgnStmt = newNodeI(nkAsgn, iter.info)
stateAsgnStmt.add(indirectAccess(newSymNode(c.closureParam),
c.state,iter.info))
stateAsgnStmt.add(newIntTypeNode(nkIntLit, -1, getSysType(tyInt)))
result.add(stateAsgnStmt)
proc liftIterSym*(n: PNode): PNode = proc liftIterSym*(n: PNode): PNode =
# transforms (iter) to (let env = newClosure[iter](); (iter, env)) # transforms (iter) to (let env = newClosure[iter](); (iter, env))
result = newNodeIT(nkStmtListExpr, n.info, n.typ)
let iter = n.sym let iter = n.sym
assert iter.kind == skIterator assert iter.kind == skIterator
result = newNodeIT(nkStmtListExpr, n.info, n.typ)
var env = copySym(getHiddenParam(iter)) var env = copySym(getHiddenParam(iter))
env.kind = skLet env.kind = skLet
var v = newNodeI(nkVarSection, n.info) var v = newNodeI(nkVarSection, n.info)
addVar(v, newSymNode(env)) addVar(v, newSymNode(env))
result.add(v) result.add(v)
@ -766,7 +826,7 @@ proc liftForLoop*(body: PNode): PNode =
# static binding? # static binding?
var env: PSym var env: PSym
if call[0].kind == nkSym and call[0].sym.kind == skIterator: if call[0].kind == nkSym and call[0].sym.kind == skIterator:
# createClose() # createClosure()
let iter = call[0].sym let iter = call[0].sym
assert iter.kind == skIterator assert iter.kind == skIterator
env = copySym(getHiddenParam(iter)) env = copySym(getHiddenParam(iter))

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.

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.
@ -18,10 +18,10 @@
# In fact the grammar is generated from this file: # In fact the grammar is generated from this file:
when isMainModule: when isMainModule:
import pegs import pegs
var outp = open("compiler/grammar.txt", fmWrite) var outp = open("doc/grammar.txt", fmWrite)
for line in lines("compiler/parser.nim"): for line in lines("compiler/parser.nim"):
if line =~ peg" \s* '#| ' {.*}": if line =~ peg" \s* '#| ' {.*}":
outp.writeln matches[0] outp.write matches[0], "\L"
outp.close outp.close
import import
@ -31,9 +31,10 @@ type
TParser*{.final.} = object # a TParser object represents a module that TParser*{.final.} = object # a TParser object represents a module that
# is being parsed # is being parsed
currInd: int # current indentation currInd: int # current indentation
firstTok: bool firstTok, strongSpaces: bool
lex*: TLexer # the lexer that is used for parsing lex*: TLexer # the lexer that is used for parsing
tok*: TToken # the current token tok*: TToken # the current token
inPragma: int
proc parseAll*(p: var TParser): PNode proc parseAll*(p: var TParser): PNode
proc openParser*(p: var TParser, filename: string, inputstream: PLLStream) proc openParser*(p: var TParser, filename: string, inputstream: PLLStream)
@ -518,14 +519,14 @@ proc parsePar(p: var TParser): PNode =
eat(p, tkParRi) eat(p, tkParRi)
proc identOrLiteral(p: var TParser, mode: TPrimaryMode): PNode = proc identOrLiteral(p: var TParser, mode: TPrimaryMode): PNode =
#| generalizedLit = GENERALIZED_STR_LIT | GENERALIZED_TRIPLESTR_LIT #| literal = | INT_LIT | INT8_LIT | INT16_LIT | INT32_LIT | INT64_LIT
#| identOrLiteral = generalizedLit | symbol
#| | INT_LIT | INT8_LIT | INT16_LIT | INT32_LIT | INT64_LIT
#| | UINT_LIT | UINT8_LIT | UINT16_LIT | UINT32_LIT | UINT64_LIT #| | UINT_LIT | UINT8_LIT | UINT16_LIT | UINT32_LIT | UINT64_LIT
#| | FLOAT_LIT | FLOAT32_LIT | FLOAT64_LIT #| | FLOAT_LIT | FLOAT32_LIT | FLOAT64_LIT
#| | STR_LIT | RSTR_LIT | TRIPLESTR_LIT #| | STR_LIT | RSTR_LIT | TRIPLESTR_LIT
#| | CHAR_LIT #| | CHAR_LIT
#| | NIL #| | NIL
#| generalizedLit = GENERALIZED_STR_LIT | GENERALIZED_TRIPLESTR_LIT
#| identOrLiteral = generalizedLit | symbol | literal
#| | par | arrayConstr | setOrTableConstr #| | par | arrayConstr | setOrTableConstr
#| | castExpr #| | castExpr
#| tupleConstr = '(' optInd (exprColonEqExpr comma?)* optPar ')' #| tupleConstr = '(' optInd (exprColonEqExpr comma?)* optPar ')'
@ -634,12 +635,15 @@ proc namedParams(p: var TParser, callee: PNode,
addSon(result, a) addSon(result, a)
exprColonEqExprListAux(p, endTok, result) exprColonEqExprListAux(p, endTok, result)
proc parseMacroColon(p: var TParser, x: PNode): PNode
proc primarySuffix(p: var TParser, r: PNode): PNode = proc primarySuffix(p: var TParser, r: PNode): PNode =
#| primarySuffix = '(' (exprColonEqExpr comma?)* ')' doBlocks? #| primarySuffix = '(' (exprColonEqExpr comma?)* ')' doBlocks?
#| | doBlocks #| | doBlocks
#| | '.' optInd ('type' | 'addr' | symbol) generalizedLit? #| | '.' optInd ('type' | 'addr' | symbol) generalizedLit?
#| | '[' optInd indexExprList optPar ']' #| | '[' optInd indexExprList optPar ']'
#| | '{' optInd indexExprList optPar '}' #| | '{' optInd indexExprList optPar '}'
#| | &( '`'|IDENT|literal|'cast') expr ^+ ',' # command syntax
#| (doBlock | macroColon)?
result = r result = r
while p.tok.indent < 0: while p.tok.indent < 0:
case p.tok.tokType case p.tok.tokType
@ -661,7 +665,26 @@ proc primarySuffix(p: var TParser, r: PNode): PNode =
result = namedParams(p, result, nkBracketExpr, tkBracketRi) result = namedParams(p, result, nkBracketExpr, tkBracketRi)
of tkCurlyLe: of tkCurlyLe:
result = namedParams(p, result, nkCurlyExpr, tkCurlyRi) result = namedParams(p, result, nkCurlyExpr, tkCurlyRi)
else: break of tkSymbol, tkAccent, tkIntLit..tkCharLit, tkNil, tkCast:
if p.inPragma == 0:
# actually parsing {.push hints:off.} as {.push(hints:off).} is a sweet
# solution, but pragmas.nim can't handle that
let a = result
result = newNodeP(nkCommand, p)
addSon(result, a)
while p.tok.tokType != tkEof:
let a = parseExpr(p)
addSon(result, a)
if p.tok.tokType != tkComma: break
getTok(p)
optInd(p, a)
if p.tok.tokType == tkDo:
parseDoBlocks(p, result)
else:
result = parseMacroColon(p, result)
break
else:
break
proc primary(p: var TParser, mode: TPrimaryMode): PNode proc primary(p: var TParser, mode: TPrimaryMode): PNode
@ -713,6 +736,7 @@ proc parseIfExpr(p: var TParser, kind: TNodeKind): PNode =
proc parsePragma(p: var TParser): PNode = proc parsePragma(p: var TParser): PNode =
#| pragma = '{.' optInd (exprColonExpr comma?)* optPar ('.}' | '}') #| pragma = '{.' optInd (exprColonExpr comma?)* optPar ('.}' | '}')
result = newNodeP(nkPragma, p) result = newNodeP(nkPragma, p)
inc p.inPragma
getTok(p) getTok(p)
optInd(p, result) optInd(p, result)
while p.tok.tokType notin {tkCurlyDotRi, tkCurlyRi, tkEof}: while p.tok.tokType notin {tkCurlyDotRi, tkCurlyRi, tkEof}:
@ -724,6 +748,7 @@ proc parsePragma(p: var TParser): PNode =
optPar(p) optPar(p)
if p.tok.tokType in {tkCurlyDotRi, tkCurlyRi}: getTok(p) if p.tok.tokType in {tkCurlyDotRi, tkCurlyRi}: getTok(p)
else: parMessage(p, errTokenExpected, ".}") else: parMessage(p, errTokenExpected, ".}")
dec p.inPragma
proc identVis(p: var TParser): PNode = proc identVis(p: var TParser): PNode =
#| identVis = symbol opr? # postfix position #| identVis = symbol opr? # postfix position
@ -965,7 +990,7 @@ proc primary(p: var TParser, mode: TPrimaryMode): PNode =
of tkTuple: result = parseTuple(p, mode == pmTypeDef) of tkTuple: result = parseTuple(p, mode == pmTypeDef)
of tkProc: result = parseProcExpr(p, mode notin {pmTypeDesc, pmTypeDef}) of tkProc: result = parseProcExpr(p, mode notin {pmTypeDesc, pmTypeDef})
of tkIterator: of tkIterator:
when true: when false:
if mode in {pmTypeDesc, pmTypeDef}: if mode in {pmTypeDesc, pmTypeDef}:
result = parseProcExpr(p, false) result = parseProcExpr(p, false)
result.kind = nkIteratorTy result.kind = nkIteratorTy
@ -976,7 +1001,8 @@ proc primary(p: var TParser, mode: TPrimaryMode): PNode =
result = ast.emptyNode result = ast.emptyNode
else: else:
result = parseProcExpr(p, mode notin {pmTypeDesc, pmTypeDef}) result = parseProcExpr(p, mode notin {pmTypeDesc, pmTypeDef})
result.kind = nkIteratorTy if result.kind == nkLambda: result.kind = nkIteratorDef
else: result.kind = nkIteratorTy
of tkEnum: of tkEnum:
if mode == pmTypeDef: if mode == pmTypeDef:
result = parseEnum(p) result = parseEnum(p)
@ -1022,6 +1048,7 @@ proc parseTypeDesc(p: var TParser): PNode =
proc parseTypeDefAux(p: var TParser): PNode = proc parseTypeDefAux(p: var TParser): PNode =
#| typeDefAux = simpleExpr #| typeDefAux = simpleExpr
#| | 'generic' typeClass
result = simpleExpr(p, pmTypeDef) result = simpleExpr(p, pmTypeDef)
proc makeCall(n: PNode): PNode = proc makeCall(n: PNode): PNode =
@ -1031,39 +1058,12 @@ proc makeCall(n: PNode): PNode =
result = newNodeI(nkCall, n.info) result = newNodeI(nkCall, n.info)
result.add n result.add n
proc parseExprStmt(p: var TParser): PNode = proc parseMacroColon(p: var TParser, x: PNode): PNode =
#| exprStmt = simpleExpr #| macroColon = ':' stmt? ( IND{=} 'of' exprList ':' stmt
#| (( '=' optInd expr )
#| / ( expr ^+ comma
#| doBlocks
#| / ':' stmt? ( IND{=} 'of' exprList ':' stmt
#| | IND{=} 'elif' expr ':' stmt #| | IND{=} 'elif' expr ':' stmt
#| | IND{=} 'except' exprList ':' stmt #| | IND{=} 'except' exprList ':' stmt
#| | IND{=} 'else' ':' stmt )* #| | IND{=} 'else' ':' stmt )*
#| ))? result = x
var a = simpleExpr(p)
if p.tok.tokType == tkEquals:
getTok(p)
optInd(p, result)
var b = parseExpr(p)
result = newNodeI(nkAsgn, a.info)
addSon(result, a)
addSon(result, b)
else:
if p.tok.indent < 0 and isExprStart(p):
result = newNode(nkCommand, a.info, @[a])
while true:
var e = parseExpr(p)
addSon(result, e)
if p.tok.tokType != tkComma: break
getTok(p)
optInd(p, result)
else:
result = a
if p.tok.tokType == tkDo and p.tok.indent < 0:
result = makeCall(result)
parseDoBlocks(p, result)
return result
if p.tok.tokType == tkColon and p.tok.indent < 0: if p.tok.tokType == tkColon and p.tok.indent < 0:
result = makeCall(result) result = makeCall(result)
getTok(p) getTok(p)
@ -1096,6 +1096,38 @@ proc parseExprStmt(p: var TParser): PNode =
addSon(result, b) addSon(result, b)
if b.kind == nkElse: break if b.kind == nkElse: break
proc parseExprStmt(p: var TParser): PNode =
#| exprStmt = simpleExpr
#| (( '=' optInd expr )
#| / ( expr ^+ comma
#| doBlocks
#| / macroColon
#| ))?
var a = simpleExpr(p)
if p.tok.tokType == tkEquals:
getTok(p)
optInd(p, result)
var b = parseExpr(p)
result = newNodeI(nkAsgn, a.info)
addSon(result, a)
addSon(result, b)
else:
if p.tok.indent < 0 and isExprStart(p):
result = newNode(nkCommand, a.info, @[a])
while true:
var e = parseExpr(p)
addSon(result, e)
if p.tok.tokType != tkComma: break
getTok(p)
optInd(p, result)
else:
result = a
if p.tok.tokType == tkDo and p.tok.indent < 0:
result = makeCall(result)
parseDoBlocks(p, result)
return result
result = parseMacroColon(p, result)
proc parseModuleName(p: var TParser, kind: TNodeKind): PNode = proc parseModuleName(p: var TParser, kind: TNodeKind): PNode =
result = parseExpr(p) result = parseExpr(p)
when false: when false:
@ -1177,8 +1209,7 @@ proc parseReturnOrRaise(p: var TParser, kind: TNodeKind): PNode =
if p.tok.tokType == tkComment: if p.tok.tokType == tkComment:
skipComment(p, result) skipComment(p, result)
addSon(result, ast.emptyNode) addSon(result, ast.emptyNode)
elif p.tok.indent >= 0 and p.tok.indent <= p.currInd or elif p.tok.indent >= 0 and p.tok.indent <= p.currInd or not isExprStart(p):
p.tok.tokType == tkEof:
# NL terminates: # NL terminates:
addSon(result, ast.emptyNode) addSon(result, ast.emptyNode)
else: else:
@ -1641,6 +1672,9 @@ proc parseTypeClassParam(p: var TParser): PNode =
result = p.parseSymbol result = p.parseSymbol
proc parseTypeClass(p: var TParser): PNode = proc parseTypeClass(p: var TParser): PNode =
#| typeClassParam = ('var')? symbol
#| typeClass = typeClassParam ^* ',' (pragma)? ('of' typeDesc ^* ',')?
#| &IND{>} stmt
result = newNodeP(nkTypeClassTy, p) result = newNodeP(nkTypeClassTy, p)
getTok(p) getTok(p)
var args = newNode(nkArgList) var args = newNode(nkArgList)

View file

@ -426,6 +426,8 @@ proc lsub(n: PNode): int =
of nkVarTy: result = (if n.len > 0: lsub(n.sons[0])+1 else: 0) + len("var") of nkVarTy: result = (if n.len > 0: lsub(n.sons[0])+1 else: 0) + len("var")
of nkDistinctTy: result = (if n.len > 0: lsub(n.sons[0])+1 else: 0) + of nkDistinctTy: result = (if n.len > 0: lsub(n.sons[0])+1 else: 0) +
len("Distinct") len("Distinct")
of nkStaticTy: result = (if n.len > 0: lsub(n.sons[0]) else: 0) +
len("static[]")
of nkTypeDef: result = lsons(n) + 3 of nkTypeDef: result = lsons(n) + 3
of nkOfInherit: result = lsub(n.sons[0]) + len("of_") of nkOfInherit: result = lsub(n.sons[0]) + len("of_")
of nkProcTy: result = lsons(n) + len("proc_") of nkProcTy: result = lsons(n) + len("proc_")
@ -701,6 +703,19 @@ proc gproc(g: var TSrcGen, n: PNode) =
gcoms(g) gcoms(g)
dedent(g) dedent(g)
proc gTypeClassTy(g: var TSrcGen, n: PNode) =
var c: TContext
initContext(c)
putWithSpace(g, tkGeneric, "generic")
gsons(g, n[0], c) # arglist
gsub(g, n[1]) # pragmas
gsub(g, n[2]) # of
gcoms(g)
indentNL(g)
gcoms(g)
gstmts(g, n[3], c)
dedent(g)
proc gblock(g: var TSrcGen, n: PNode) = proc gblock(g: var TSrcGen, n: PNode) =
var c: TContext var c: TContext
initContext(c) initContext(c)
@ -1054,6 +1069,12 @@ proc gsub(g: var TSrcGen, n: PNode, c: TContext) =
gsub(g, n.sons[0]) gsub(g, n.sons[0])
else: else:
put(g, tkShared, "shared") put(g, tkShared, "shared")
of nkStaticTy:
put(g, tkStatic, "static")
put(g, tkBracketLe, "[")
if n.len > 0:
gsub(g, n.sons[0])
put(g, tkBracketRi, "]")
of nkEnumTy: of nkEnumTy:
if sonsLen(n) > 0: if sonsLen(n) > 0:
putWithSpace(g, tkEnum, "enum") putWithSpace(g, tkEnum, "enum")
@ -1251,6 +1272,13 @@ proc gsub(g: var TSrcGen, n: PNode, c: TContext) =
put(g, tkParLe, "(META|") put(g, tkParLe, "(META|")
gsub(g, n.sons[0]) gsub(g, n.sons[0])
put(g, tkParRi, ")") put(g, tkParRi, ")")
of nkGotoState, nkState:
var c: TContext
initContext c
putWithSpace g, tkSymbol, if n.kind == nkState: "state" else: "goto"
gsons(g, n, c)
of nkTypeClassTy:
gTypeClassTy(g, n)
else: else:
#nkNone, nkExplicitTypeListCall: #nkNone, nkExplicitTypeListCall:
internalError(n.info, "rnimsyn.gsub(" & $n.kind & ')') internalError(n.info, "rnimsyn.gsub(" & $n.kind & ')')

View file

@ -116,7 +116,10 @@ proc generateDestructor(c: PContext, t: PType): PNode =
let stmt = destroyField(c, t.n.sons[s].sym, destructedObj) let stmt = destroyField(c, t.n.sons[s].sym, destructedObj)
if stmt != nil: addLine(stmt) if stmt != nil: addLine(stmt)
else: else:
internalAssert false # 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

@ -239,7 +239,8 @@ proc semLowHigh(c: PContext, n: PNode, m: TMagic): PNode =
localError(n.info, errXExpectsTypeOrValue, opToStr[m]) localError(n.info, errXExpectsTypeOrValue, opToStr[m])
else: else:
n.sons[1] = semExprWithType(c, n.sons[1], {efDetermineType}) n.sons[1] = semExprWithType(c, n.sons[1], {efDetermineType})
var typ = skipTypes(n.sons[1].typ, abstractVarRange+{tyTypeDesc}) var typ = skipTypes(n.sons[1].typ, abstractVarRange +
{tyTypeDesc, tyFieldAccessor})
case typ.kind case typ.kind
of tySequence, tyString, tyOpenArray, tyVarargs: of tySequence, tyString, tyOpenArray, tyVarargs:
n.typ = getSysType(tyInt) n.typ = getSysType(tyInt)
@ -247,7 +248,7 @@ proc semLowHigh(c: PContext, n: PNode, m: TMagic): PNode =
n.typ = typ.sons[0] # indextype n.typ = typ.sons[0] # indextype
of tyInt..tyInt64, tyChar, tyBool, tyEnum, tyUInt8, tyUInt16, tyUInt32: of tyInt..tyInt64, tyChar, tyBool, tyEnum, tyUInt8, tyUInt16, tyUInt32:
# do not skip the range! # do not skip the range!
n.typ = n.sons[1].typ.skipTypes(abstractVar) n.typ = n.sons[1].typ.skipTypes(abstractVar + {tyFieldAccessor})
of tyGenericParam: of tyGenericParam:
# prepare this for resolving in semtypinst: # prepare this for resolving in semtypinst:
# we must use copyTree here in order to avoid creating a cycle # we must use copyTree here in order to avoid creating a cycle
@ -306,7 +307,7 @@ proc isOpImpl(c: PContext, n: PNode): PNode =
n[1].typ != nil and n[1].typ.kind == tyTypeDesc and n[1].typ != nil and n[1].typ.kind == tyTypeDesc and
n[2].kind in {nkStrLit..nkTripleStrLit, nkType} n[2].kind in {nkStrLit..nkTripleStrLit, nkType}
let t1 = n[1].typ.skipTypes({tyTypeDesc}) let t1 = n[1].typ.skipTypes({tyTypeDesc, tyFieldAccessor})
if n[2].kind in {nkStrLit..nkTripleStrLit}: if n[2].kind in {nkStrLit..nkTripleStrLit}:
case n[2].strVal.normalize case n[2].strVal.normalize
@ -321,24 +322,13 @@ proc isOpImpl(c: PContext, n: PNode): PNode =
t.callConv == ccClosure and t.callConv == ccClosure and
tfIterator in t.flags)) tfIterator in t.flags))
else: else:
var match: bool var t2 = n[2].typ.skipTypes({tyTypeDesc})
let t2 = n[2].typ let lifted = liftParamType(c, skType, newNodeI(nkArgList, n.info),
case t2.kind t2, ":anon", n.info)
of tyTypeClasses: if lifted != nil: t2 = lifted
var m: TCandidate var m: TCandidate
initCandidate(c, m, t2) initCandidate(c, m, t2)
match = matchUserTypeClass(c, m, emptyNode, t2, t1) != nil let match = typeRel(m, t2, t1) != isNone
of tyOrdinal:
var m: TCandidate
initCandidate(c, m, t2)
match = isOrdinalType(t1)
of tySequence, tyArray, tySet:
var m: TCandidate
initCandidate(c, m, t2)
match = typeRel(m, t2, t1) != isNone
else:
match = sameType(t1, t2)
result = newIntNode(nkIntLit, ord(match)) result = newIntNode(nkIntLit, ord(match))
result.typ = n.typ result.typ = n.typ
@ -948,6 +938,13 @@ proc builtinFieldAccess(c: PContext, n: PNode, flags: TExprFlags): PNode =
let foundTyp = makeTypeDesc(c, rawTyp) let foundTyp = makeTypeDesc(c, rawTyp)
return newSymNode(copySym(tParam.sym).linkTo(foundTyp), n.info) return newSymNode(copySym(tParam.sym).linkTo(foundTyp), n.info)
return return
of tyObject, tyTuple:
if ty.n.kind == nkRecList:
for field in ty.n.sons:
if field.sym.name == i:
n.typ = newTypeWithSons(c, tyFieldAccessor, @[ty, field.sym.typ])
n.typ.n = copyTree(n)
return n
else: else:
# echo "TYPE FIELD ACCESS" # echo "TYPE FIELD ACCESS"
# debug ty # debug ty

View file

@ -252,8 +252,7 @@ proc evalIs(n, a: PNode): PNode =
else: else:
# XXX semexprs.isOpImpl is slightly different and requires a context. yay. # XXX semexprs.isOpImpl is slightly different and requires a context. yay.
let t2 = n[2].typ let t2 = n[2].typ
var match = if t2.kind == tyTypeClass: true var match = sameType(t1, t2)
else: sameType(t1, t2)
result = newIntNode(nkIntLit, ord(match)) result = newIntNode(nkIntLit, ord(match))
result.typ = n.typ result.typ = n.typ

View file

@ -868,6 +868,8 @@ proc semProcAnnotation(c: PContext, prc: PNode): PNode =
return semStmt(c, x) return semStmt(c, x)
proc semLambda(c: PContext, n: PNode, flags: TExprFlags): PNode = proc semLambda(c: PContext, n: PNode, flags: TExprFlags): PNode =
# XXX semProcAux should be good enough for this now, we will eventually
# remove semLambda
result = semProcAnnotation(c, n) result = semProcAnnotation(c, n)
if result != nil: return result if result != nil: return result
result = n result = n
@ -922,7 +924,7 @@ proc activate(c: PContext, n: PNode) =
of nkCallKinds: of nkCallKinds:
for i in 1 .. <n.len: activate(c, n[i]) for i in 1 .. <n.len: activate(c, n[i])
else: else:
nil discard
proc maybeAddResult(c: PContext, s: PSym, n: PNode) = proc maybeAddResult(c: PContext, s: PSym, n: PNode) =
if s.typ.sons[0] != nil and if s.typ.sons[0] != nil and
@ -949,8 +951,14 @@ proc semProcAux(c: PContext, n: PNode, kind: TSymKind,
checkSonsLen(n, bodyPos + 1) checkSonsLen(n, bodyPos + 1)
var s: PSym var s: PSym
var typeIsDetermined = false var typeIsDetermined = false
var isAnon = false
if n[namePos].kind != nkSym: if n[namePos].kind != nkSym:
assert phase == stepRegisterSymbol assert phase == stepRegisterSymbol
if n[namePos].kind == nkEmpty:
s = newSym(kind, idAnon, getCurrOwner(), n.info)
isAnon = true
else:
s = semIdentDef(c, n.sons[0], kind) s = semIdentDef(c, n.sons[0], kind)
n.sons[namePos] = newSymNode(s) n.sons[namePos] = newSymNode(s)
s.ast = n s.ast = n
@ -992,11 +1000,13 @@ proc semProcAux(c: PContext, n: PNode, kind: TSymKind,
rawAddSon(s.typ, nil) rawAddSon(s.typ, nil)
if n.sons[patternPos].kind != nkEmpty: if n.sons[patternPos].kind != nkEmpty:
n.sons[patternPos] = semPattern(c, n.sons[patternPos]) n.sons[patternPos] = semPattern(c, n.sons[patternPos])
if s.kind == skIterator: s.typ.flags.incl(tfIterator) if s.kind == skIterator:
s.typ.flags.incl(tfIterator)
var proto = searchForProc(c, s.scope, s) var proto = searchForProc(c, s.scope, s)
if proto == nil: if proto == nil:
s.typ.callConv = lastOptionEntry(c).defaultCC if s.kind == skIterator and isAnon: s.typ.callConv = ccClosure
else: s.typ.callConv = lastOptionEntry(c).defaultCC
# add it here, so that recursive procs are possible: # add it here, so that recursive procs are possible:
if sfGenSym in s.flags: discard if sfGenSym in s.flags: discard
elif kind in OverloadableSyms: elif kind in OverloadableSyms:
@ -1074,6 +1084,7 @@ proc semProcAux(c: PContext, n: PNode, kind: TSymKind,
popOwner() popOwner()
if n.sons[patternPos].kind != nkEmpty: if n.sons[patternPos].kind != nkEmpty:
c.patterns.add(s) c.patterns.add(s)
if isAnon: result.typ = s.typ
proc determineType(c: PContext, s: PSym) = proc determineType(c: PContext, s: PSym) =
if s.typ != nil: return if s.typ != nil: return
@ -1236,7 +1247,7 @@ proc semStmtList(c: PContext, n: PNode): 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 or voidContext: if i != last or voidContext or c.inTypeClass > 0:
discardCheck(c, n.sons[i]) discardCheck(c, n.sons[i])
else: else:
n.typ = n.sons[i].typ n.typ = n.sons[i].typ

View file

@ -710,6 +710,11 @@ proc liftParamType(c: PContext, procKind: TSymKind, genericParams: PNode,
result = addImplicitGeneric(result) result = addImplicitGeneric(result)
of tyGenericInst: of tyGenericInst:
if paramType.lastSon.kind == tyUserTypeClass:
var cp = copyType(paramType, getCurrOwner(), false)
cp.kind = tyUserTypeClassInst
return addImplicitGeneric(cp)
for i in 1 .. (paramType.sons.len - 2): for i in 1 .. (paramType.sons.len - 2):
var lifted = liftingWalk(paramType.sons[i]) var lifted = liftingWalk(paramType.sons[i])
if lifted != nil: if lifted != nil:
@ -731,7 +736,7 @@ proc liftParamType(c: PContext, procKind: TSymKind, genericParams: PNode,
allowMetaTypes = true) allowMetaTypes = true)
result = liftingWalk(expanded) result = liftingWalk(expanded)
of tyTypeClass, tyBuiltInTypeClass, tyAnd, tyOr, tyNot: of tyUserTypeClass, tyBuiltInTypeClass, tyAnd, tyOr, tyNot:
result = addImplicitGeneric(copyType(paramType, getCurrOwner(), true)) result = addImplicitGeneric(copyType(paramType, getCurrOwner(), true))
of tyExpr: of tyExpr:
@ -923,7 +928,7 @@ proc freshType(res, prev: PType): PType {.inline.} =
proc semTypeClass(c: PContext, n: PNode, prev: PType): PType = proc semTypeClass(c: PContext, n: PNode, prev: PType): PType =
# if n.sonsLen == 0: return newConstraint(c, tyTypeClass) # if n.sonsLen == 0: return newConstraint(c, tyTypeClass)
result = newOrPrevType(tyTypeClass, prev, c) result = newOrPrevType(tyUserTypeClass, prev, c)
result.n = n result.n = n
let let

View file

@ -116,8 +116,8 @@ proc hasGenericArguments*(n: PNode): bool =
(n.sym.kind == skType and (n.sym.kind == skType and
n.sym.typ.flags * {tfGenericTypeParam, tfImplicitTypeParam} != {}) n.sym.typ.flags * {tfGenericTypeParam, tfImplicitTypeParam} != {})
else: else:
for s in n.sons: for i in 0.. <n.safeLen:
if hasGenericArguments(s): return true if hasGenericArguments(n.sons[i]): return true
return false return false
proc reResolveCallsWithTypedescParams(cl: var TReplTypeVars, n: PNode): PNode = proc reResolveCallsWithTypedescParams(cl: var TReplTypeVars, n: PNode): PNode =
@ -361,6 +361,9 @@ proc replaceTypeVarsTAux(cl: var TReplTypeVars, t: PType): PType =
elif t.sonsLen > 0: elif t.sonsLen > 0:
result = makeTypeDesc(cl.c, replaceTypeVarsT(cl, t.sons[0])) result = makeTypeDesc(cl.c, replaceTypeVarsT(cl, t.sons[0]))
of tyUserTypeClass:
result = t
of tyGenericInst: of tyGenericInst:
result = instCopyType(cl, t) result = instCopyType(cl, t)
for i in 1 .. <result.sonsLen: for i in 1 .. <result.sonsLen:

View file

@ -103,7 +103,7 @@ proc initCandidate*(ctx: PContext, c: var TCandidate, callee: PSym,
for i in 1..min(sonsLen(typeParams), sonsLen(binding)-1): for i in 1..min(sonsLen(typeParams), sonsLen(binding)-1):
var formalTypeParam = typeParams.sons[i-1].typ var formalTypeParam = typeParams.sons[i-1].typ
var bound = binding[i].typ var bound = binding[i].typ
if formalTypeParam.kind != tyTypeDesc: if bound != nil and formalTypeParam.kind != tyTypeDesc:
bound = bound.skipTypes({tyTypeDesc}) bound = bound.skipTypes({tyTypeDesc})
put(c.bindings, formalTypeParam, bound) put(c.bindings, formalTypeParam, bound)
@ -140,7 +140,7 @@ proc sumGeneric(t: PType): int =
result = ord(t.kind == tyGenericInvokation) result = ord(t.kind == tyGenericInvokation)
for i in 0 .. <t.len: result += t.sons[i].sumGeneric for i in 0 .. <t.len: result += t.sons[i].sumGeneric
break break
of tyGenericParam, tyExpr, tyStatic, tyStmt, tyTypeDesc, tyTypeClass: break of tyGenericParam, tyExpr, tyStatic, tyStmt, tyTypeDesc: break
else: return 0 else: return 0
proc complexDisambiguation(a, b: PType): int = proc complexDisambiguation(a, b: PType): int =
@ -399,6 +399,70 @@ proc typeRangeRel(f, a: PType): TTypeRelation {.noinline.} =
else: else:
result = isNone result = isNone
proc matchUserTypeClass*(c: PContext, m: var TCandidate,
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 prev = PType(idTableGet(m.bindings, f))
# if prev != nil:
# if sameType(prev, a): return arg
# else: return nil
# pushInfoContext(arg.info)
openScope(c)
inc c.inTypeClass
finally:
dec c.inTypeClass
closeScope(c)
if ff.kind == tyUserTypeClassInst:
for i in 1 .. <(ff.len - 1):
var
typeParamName = ff.base.sons[i-1].sym.name
typ = ff.sons[i]
param = newSym(skType, typeParamName, body.sym, body.sym.info)
param.typ = makeTypeDesc(c, typ)
addDecl(c, param)
for param in body.n[0]:
var
dummyName: PNode
dummyType: PType
if param.kind == nkVarTy:
dummyName = param[0]
dummyType = makeVarType(c, a)
else:
dummyName = param
dummyType = a
internalAssert dummyName.kind == nkIdent
var dummyParam = newSym(skType, dummyName.ident, body.sym, body.sym.info)
dummyParam.typ = dummyType
addDecl(c, dummyParam)
var checkedBody = c.semTryExpr(c, copyTree(body.n[3]), bufferErrors = false)
m.errors = bufferedMsgs
clearBufferedMsgs()
if checkedBody == nil: return isNone
if checkedBody.kind == nkStmtList:
for stmt in checkedBody:
case stmt.kind
of nkReturnStmt: discard
of nkTypeSection: discard
of nkConstDef: discard
else: discard
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:
# #
@ -418,6 +482,11 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation =
result = isNone result = isNone
assert(f != nil) assert(f != nil)
if f.kind == tyExpr:
put(c.bindings, f, aOrig)
return isGeneric
assert(aOrig != nil) assert(aOrig != nil)
# var and static arguments match regular modifier-free types # var and static arguments match regular modifier-free types
@ -751,6 +820,12 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation =
else: else:
return isNone return isNone
of tyUserTypeClass, tyUserTypeClassInst:
considerPreviousT:
result = matchUserTypeClass(c.c, c, f, a)
if result == isGeneric:
put(c.bindings, f, a)
of tyCompositeTypeClass: of tyCompositeTypeClass:
considerPreviousT: considerPreviousT:
if typeRel(c, f.sons[1], a) != isNone: if typeRel(c, f.sons[1], a) != isNone:
@ -759,7 +834,7 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation =
else: else:
return isNone return isNone
of tyGenericParam, tyTypeClass: of tyGenericParam:
var x = PType(idTableGet(c.bindings, f)) var x = PType(idTableGet(c.bindings, f))
if x == nil: if x == nil:
if c.calleeSym != nil and c.calleeSym.kind == skType and if c.calleeSym != nil and c.calleeSym.kind == skType and
@ -822,7 +897,7 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation =
else: a.sons[0] else: a.sons[0]
result = typeRel(c, prev.sons[0], toMatch) result = typeRel(c, prev.sons[0], toMatch)
of tyExpr, tyStmt: of tyStmt:
result = isGeneric result = isGeneric
of tyProxy: of tyProxy:
@ -904,57 +979,6 @@ proc localConvMatch(c: PContext, m: var TCandidate, f, a: PType,
result.typ = getInstantiatedType(c, arg, m, base(f)) result.typ = getInstantiatedType(c, arg, m, base(f))
m.baseTypeMatch = true m.baseTypeMatch = true
proc matchUserTypeClass*(c: PContext, m: var TCandidate,
arg: PNode, f, a: PType): PNode =
if f.n == nil:
let r = typeRel(m, f, a)
return if r == isGeneric: arg else: nil
var prev = PType(idTableGet(m.bindings, f))
if prev != nil:
if sameType(prev, a): return arg
else: return nil
# pushInfoContext(arg.info)
openScope(c)
inc c.inTypeClass
finally:
dec c.inTypeClass
closeScope(c)
for param in f.n[0]:
var
dummyName: PNode
dummyType: PType
if param.kind == nkVarTy:
dummyName = param[0]
dummyType = makeVarType(c, a)
else:
dummyName = param
dummyType = a
internalAssert dummyName.kind == nkIdent
var dummyParam = newSym(skType, dummyName.ident, f.sym, f.sym.info)
dummyParam.typ = dummyType
addDecl(c, dummyParam)
for stmt in f.n[3]:
var e = c.semTryExpr(c, copyTree(stmt), bufferErrors = false)
m.errors = bufferedMsgs
clearBufferedMsgs()
if e == nil: return nil
case e.kind
of nkReturnStmt: discard
of nkTypeSection: discard
of nkConstDef: discard
else: discard
result = arg
put(m.bindings, f, a)
proc paramTypesMatchAux(m: var TCandidate, f, argType: PType, proc paramTypesMatchAux(m: var TCandidate, f, argType: PType,
argSemantized, argOrig: PNode): PNode = argSemantized, argOrig: PNode): PNode =
var var
@ -975,25 +999,9 @@ proc paramTypesMatchAux(m: var TCandidate, f, argType: PType,
argType = arg.typ argType = arg.typ
var var
r: TTypeRelation
a = if c.inTypeClass > 0: argType.skipTypes({tyTypeDesc}) a = if c.inTypeClass > 0: argType.skipTypes({tyTypeDesc})
else: argType else: argType
case fMaybeStatic.kind
of tyTypeClass, tyParametricTypeClass:
if fMaybeStatic.n != nil:
let match = matchUserTypeClass(c, m, arg, fMaybeStatic, a)
if match != nil:
r = isGeneric
arg = match
else:
r = isNone
else:
r = typeRel(m, f, a)
of tyExpr:
r = isGeneric
put(m.bindings, f, arg.typ)
else:
r = typeRel(m, f, a) r = typeRel(m, f, a)
case r case r

View file

@ -113,8 +113,8 @@ proc newAsgnStmt(c: PTransf, le: PNode, ri: PTransNode): PTransNode =
result[1] = ri result[1] = ri
proc transformSymAux(c: PTransf, n: PNode): PNode = proc transformSymAux(c: PTransf, n: PNode): PNode =
if n.sym.kind == skIterator and n.sym.typ.callConv == ccClosure: #if n.sym.kind == skIterator and n.sym.typ.callConv == ccClosure:
return liftIterSym(n) # return liftIterSym(n)
var b: PNode var b: PNode
var tc = c.transCon var tc = c.transCon
if sfBorrow in n.sym.flags: if sfBorrow in n.sym.flags:
@ -389,7 +389,7 @@ proc transformConv(c: PTransf, n: PNode): PTransNode =
result[0] = transform(c, n.sons[1]) result[0] = transform(c, n.sons[1])
else: else:
result = transform(c, n.sons[1]) result = transform(c, n.sons[1])
of tyGenericParam, tyOrdinal, tyTypeClass: of tyGenericParam, tyOrdinal:
result = transform(c, n.sons[1]) result = transform(c, n.sons[1])
# happens sometimes for generated assignments, etc. # happens sometimes for generated assignments, etc.
else: else:
@ -636,6 +636,8 @@ proc transform(c: PTransf, n: PNode): PTransNode =
s.ast.sons[bodyPos] = n.sons[bodyPos] s.ast.sons[bodyPos] = n.sons[bodyPos]
#n.sons[bodyPos] = liftLambdas(s, n) #n.sons[bodyPos] = liftLambdas(s, n)
#if n.kind == nkMethodDef: methodDef(s, false) #if n.kind == nkMethodDef: methodDef(s, false)
#if n.kind == nkIteratorDef and n.typ != nil:
# return liftIterSym(n.sons[namePos]).PTransNode
result = PTransNode(n) result = PTransNode(n)
of nkMacroDef: of nkMacroDef:
# XXX no proper closure support yet: # XXX no proper closure support yet:
@ -708,6 +710,7 @@ proc transform(c: PTransf, n: PNode): PTransNode =
# XXX comment handling really sucks: # XXX comment handling really sucks:
if importantComments(): if importantComments():
PNode(result).comment = n.comment PNode(result).comment = n.comment
of nkClosure: return PTransNode(n)
else: else:
result = transformSons(c, n) result = transformSons(c, n)
var cnst = getConstExpr(c.module, PNode(result)) var cnst = getConstExpr(c.module, PNode(result))
@ -738,8 +741,8 @@ proc transformBody*(module: PSym, n: PNode, prc: PSym): PNode =
var c = openTransf(module, "") var c = openTransf(module, "")
result = processTransf(c, n, prc) result = processTransf(c, n, prc)
result = liftLambdas(prc, result) result = liftLambdas(prc, result)
if prc.kind == skIterator and prc.typ.callConv == ccClosure: #if prc.kind == skIterator and prc.typ.callConv == ccClosure:
result = lambdalifting.liftIterator(prc, result) # result = lambdalifting.liftIterator(prc, result)
incl(result.flags, nfTransf) incl(result.flags, nfTransf)
when useEffectSystem: trackProc(prc, result) when useEffectSystem: trackProc(prc, result)

View file

@ -404,9 +404,10 @@ const
"float", "float32", "float64", "float128", "float", "float32", "float64", "float128",
"uint", "uint8", "uint16", "uint32", "uint64", "uint", "uint8", "uint16", "uint32", "uint64",
"bignum", "const ", "bignum", "const ",
"!", "varargs[$1]", "iter[$1]", "Error Type", "TypeClass", "!", "varargs[$1]", "iter[$1]", "Error Type",
"ParametricTypeClass", "BuiltInTypeClass", "CompositeTypeClass", "BuiltInTypeClass", "UserTypeClass",
"and", "or", "not", "any", "static", "TypeFromExpr"] "UserTypeClassInst", "CompositeTypeClass",
"and", "or", "not", "any", "static", "TypeFromExpr", "FieldAccessor"]
proc typeToString(typ: PType, prefer: TPreferedDesc = preferName): string = proc typeToString(typ: PType, prefer: TPreferedDesc = preferName): string =
var t = typ var t = typ
@ -434,11 +435,30 @@ proc typeToString(typ: PType, prefer: TPreferedDesc = preferName): string =
of tyStatic: of tyStatic:
internalAssert t.len > 0 internalAssert t.len > 0
result = "static[" & typeToString(t.sons[0]) & "]" result = "static[" & typeToString(t.sons[0]) & "]"
of tyTypeClass: of tyUserTypeClass:
internalAssert t.sym != nil and t.sym.owner != nil internalAssert t.sym != nil and t.sym.owner != nil
return t.sym.owner.name.s return t.sym.owner.name.s
of tyBuiltInTypeClass: of tyBuiltInTypeClass:
return "TypeClass" result = case t.base.kind:
of tyVar: "var"
of tyRef: "ref"
of tyPtr: "ptr"
of tySequence: "seq"
of tyArray: "array"
of tySet: "set"
of tyRange: "range"
of tyDistinct: "distinct"
of tyProc: "proc"
of tyObject: "object"
of tyTuple: "tuple"
else: (internalAssert false; "")
of tyUserTypeClassInst:
let body = t.base
result = body.sym.name.s & "["
for i in countup(1, sonsLen(t) - 2):
if i > 1: add(result, ", ")
add(result, typeToString(t.sons[i]))
result.add "]"
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:
@ -448,7 +468,7 @@ proc typeToString(typ: PType, prefer: TPreferedDesc = preferName): string =
of tyExpr: of tyExpr:
internalAssert t.len == 0 internalAssert t.len == 0
result = "expr" result = "expr"
of tyFromExpr: of tyFromExpr, tyFieldAccessor:
result = renderTree(t.n) result = renderTree(t.n)
of tyArray: of tyArray:
if t.sons[0].kind == tyRange: if t.sons[0].kind == tyRange:
@ -546,7 +566,8 @@ proc firstOrd(t: PType): BiggestInt =
else: else:
assert(t.n.sons[0].kind == nkSym) assert(t.n.sons[0].kind == nkSym)
result = t.n.sons[0].sym.position result = t.n.sons[0].sym.position
of tyGenericInst, tyDistinct, tyConst, tyMutable, tyTypeDesc: of tyGenericInst, tyDistinct, tyConst, tyMutable,
tyTypeDesc, tyFieldAccessor:
result = firstOrd(lastSon(t)) result = firstOrd(lastSon(t))
else: else:
internalError("invalid kind for first(" & $t.kind & ')') internalError("invalid kind for first(" & $t.kind & ')')
@ -579,7 +600,8 @@ proc lastOrd(t: PType): BiggestInt =
of tyEnum: of tyEnum:
assert(t.n.sons[sonsLen(t.n) - 1].kind == nkSym) assert(t.n.sons[sonsLen(t.n) - 1].kind == nkSym)
result = t.n.sons[sonsLen(t.n) - 1].sym.position result = t.n.sons[sonsLen(t.n) - 1].sym.position
of tyGenericInst, tyDistinct, tyConst, tyMutable, tyTypeDesc: of tyGenericInst, tyDistinct, tyConst, tyMutable,
tyTypeDesc, tyFieldAccessor:
result = lastOrd(lastSon(t)) result = lastOrd(lastSon(t))
of tyProxy: result = 0 of tyProxy: result = 0
else: else:
@ -875,9 +897,9 @@ proc sameTypeAux(x, y: PType, c: var TSameTypeClosure): bool =
of tyGenericInvokation, tyGenericBody, tySequence, of tyGenericInvokation, tyGenericBody, tySequence,
tyOpenArray, tySet, tyRef, tyPtr, tyVar, tyArrayConstr, tyOpenArray, tySet, tyRef, tyPtr, tyVar, tyArrayConstr,
tyArray, tyProc, tyConst, tyMutable, tyVarargs, tyIter, tyArray, tyProc, tyConst, tyMutable, tyVarargs, tyIter,
tyOrdinal, tyTypeClasses: tyOrdinal, tyTypeClasses, tyFieldAccessor:
cycleCheck() cycleCheck()
if a.kind == tyTypeClass and a.n != nil: return a.n == b.n if a.kind == tyUserTypeClass and a.n != nil: return a.n == b.n
result = sameChildrenAux(a, b, c) and sameFlags(a, b) result = sameChildrenAux(a, b, c) and sameFlags(a, b)
if result and a.kind == tyProc: if result and a.kind == tyProc:
result = ((IgnoreCC in c.flags) or a.callConv == b.callConv) and result = ((IgnoreCC in c.flags) or a.callConv == b.callConv) and
@ -1021,7 +1043,7 @@ proc typeAllowedAux(marker: var TIntSet, typ: PType, kind: TSymKind,
of tyTypeClasses: of tyTypeClasses:
result = true result = true
of tyGenericBody, tyGenericParam, tyGenericInvokation, of tyGenericBody, tyGenericParam, tyGenericInvokation,
tyNone, tyForward, tyFromExpr: tyNone, tyForward, tyFromExpr, tyFieldAccessor:
result = false result = false
of tyNil: of tyNil:
result = kind == skConst result = kind == skConst
@ -1231,8 +1253,15 @@ proc getSize(typ: PType): BiggestInt =
if result < 0: internalError("getSize: " & $typ.kind) if result < 0: internalError("getSize: " & $typ.kind)
proc containsGenericTypeIter(t: PType, closure: PObject): bool = proc containsGenericTypeIter(t: PType, closure: PObject): bool =
result = t.kind in GenericTypes + tyTypeClasses + {tyTypeDesc,tyFromExpr} or if t.kind in GenericTypes + tyTypeClasses + {tyFromExpr}:
t.kind == tyStatic and t.n == nil return true
if t.kind == tyTypeDesc:
if t.sonsLen == 0: return true
if containsGenericTypeIter(t.base, closure): return true
return false
return t.kind == tyStatic and t.n == nil
proc containsGenericType*(t: PType): bool = proc containsGenericType*(t: PType): bool =
result = iterOverType(t, containsGenericTypeIter, nil) result = iterOverType(t, containsGenericTypeIter, nil)

View file

@ -802,7 +802,7 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): PNode =
let t1 = regs[rb].typ.skipTypes({tyTypeDesc}) let t1 = regs[rb].typ.skipTypes({tyTypeDesc})
let t2 = c.types[regs[rc].intVal.int] let t2 = c.types[regs[rc].intVal.int]
# XXX: This should use the standard isOpImpl # XXX: This should use the standard isOpImpl
let match = if t2.kind == tyTypeClass: true let match = if t2.kind == tyUserTypeClass: true
else: sameType(t1, t2) else: sameType(t1, t2)
regs[ra].intVal = ord(match) regs[ra].intVal = ord(match)
of opcSetLenSeq: of opcSetLenSeq:
@ -1057,6 +1057,7 @@ proc fixType(result, n: PNode) {.inline.} =
# XXX do it deeply for complex values; there seems to be no simple # XXX do it deeply for complex values; there seems to be no simple
# solution except to check it deeply here. # solution except to check it deeply here.
#if result.typ.isNil: result.typ = n.typ #if result.typ.isNil: result.typ = n.typ
discard
proc execute(c: PCtx, start: int): PNode = proc execute(c: PCtx, start: int): PNode =
var tos = PStackFrame(prc: nil, comesFrom: 0, next: nil) var tos = PStackFrame(prc: nil, comesFrom: 0, next: nil)
@ -1118,6 +1119,7 @@ proc evalConstExprAux(module, prc: PSym, n: PNode, mode: TEvalMode): PNode =
var c = globalCtx var c = globalCtx
c.mode = mode c.mode = mode
let start = genExpr(c, n, requiresValue = mode!=emStaticStmt) let start = genExpr(c, n, requiresValue = mode!=emStaticStmt)
if c.code[start].opcode == opcEof: return emptyNode
assert c.code[start].opcode != opcEof assert c.code[start].opcode != opcEof
var tos = PStackFrame(prc: prc, comesFrom: 0, next: nil) var tos = PStackFrame(prc: prc, comesFrom: 0, next: nil)
newSeq(tos.slots, c.prc.maxSlots) newSeq(tos.slots, c.prc.maxSlots)

192
doc/docgen.txt Normal file
View file

@ -0,0 +1,192 @@
===================================
Nimrod DocGen Tools Guide
===================================
:Author: Erik O'Leary
:Version: |nimrodversion|
.. contents::
Introduction
============
This document describes the `documentation generation tools`:idx: built into
the `Nimrod compiler <nimrodc.html>`_, which can generate HTML and JSON output
from input .nim files and projects, as well as HTML and LaTeX from input RST
(reStructuredText) files. The output documentation will include module
dependencies (``import``), any top-level documentation comments (##), and
exported symbols (*), including procedures, types, and variables.
Documentation Comments
----------------------
Any comments which are preceded by a double-hash (##), are interpreted as
documentation. Comments are parsed as RST (see `reference
<http://docutils.sourceforge.net/docs/user/rst/quickref.html>`_), providing
Nimrod module authors the ability to easily generate richly formatted
documentation with only their well-documented code.
Example:
.. code-block:: nimrod
type TPerson* = object
## This type contains a description of a person
name: string
age: int
Outputs::
TPerson* = object
name: string
age: int
This type contains a description of a person
Field documentation comments can be added to fields like so:
.. code-block:: nimrod
var numValues: int ## \
## `numValues` stores the number of values
Note that without the `*` following the name of the type, the documentation for
this type would not be generated. Documentation will only be generated for
*exported* types/procedures/etc.
Nimrod file input
-----------------
The following examples will generate documentation for the below contrived
*Nimrod* module, aptly named 'sample.nim'
sample.nim:
.. code-block:: nimrod
## This module is a sample.
import strutils
proc helloWorld*(times: int) =
## Takes an integer and outputs
## as many "hello world!"s
for i in 0 .. times-1:
echo "hello world!"
helloWorld(5)
Document Types
==============
HTML
----
Generation of HTML documents is done via both the ``doc`` and ``doc2``
commands. These command take either a single .nim file, outputting a single
.html file with the same base filename, or multiple .nim files, outputting
multiple .html files and, optionally, an index file.
The ``doc`` command::
nimrod doc sample
Partial Output::
...
proc helloWorld*(times: int)
...
Output can be viewed in full here: `docgen_sample.html <docgen_sample.html>`_.
The next command, called ``doc2``, is very similar to the ``doc`` command, but
will be run after the compiler performs semantic checking on the input nimrod
module(s), which allows it to process macros.
The ``doc2`` command::
nimrod doc2 sample
Partial Output::
...
proc helloWorld(times: int) {.raises: [], tags: [].}
...
The full output can be seen here: `docgen_sample2.html <docgen_sample2.html>`_.
As you can see, the tool has extracted additional information provided to it by
the compiler beyond what the ``doc`` command provides, such as pragmas attached
implicitly by the compiler. This type of information is not available from
looking at the AST (Abstract Syntax Tree) prior to semantic checking, as the
``doc`` command does.
JSON
----
Generation of JSON documents is done via the ``jsondoc`` command. This command
takes in a .nim file, and outputs a .json file with the same base filename.
Note that this tool is built off of the ``doc`` command, and therefore is
performed before semantic checking.
The ``jsondoc`` command::
nimrod jsondoc sample
Output::
[
{
"comment": "This module is a sample."
},
{
"name": "helloWorld",
"type": "skProc",
"description": "Takes an integer and outputs as many &quot;hello world!&quot;s",
"code": "proc helloWorld*(times: int)"
}
]
Related Options
===============
``--project`` switch
::
nimrod doc2 --project sample
This will recursively generate documentation of all nimrod modules imported
into the input module, including system modules. Be careful with this command,
as it may end up sprinkling html files all over your filesystem!
``--index`` switch
::
nimrod doc2 --index:on sample
This will generate an index of all the exported symbols in the input Nimrod
module, and put it into a neighboring file with the extension of `.idx`.
Other Input Formats
===================
The *Nimrod compiler* also has support for RST (reStructuredText) files with
the ``rst2html`` and ``rst2tex`` commands. Documents like this one are
initially written in a dialect of RST which adds support for nimrod sourcecode
highlighting with the ``.. code-block:: nimrod`` prefix. ``code-block`` also
supports highlighting of C++ and some other c-like languages.
Usage::
nimrod rst2html docgen.txt
Output::
You're reading it!
The input can be viewed here `docgen.txt <docgen.txt>`_. The ``rst2tex``
command is invoked identically to ``rst2html``, but outputs a .tex file instead
of .html.
Additional Resources
=========
`Nimrod Compiler User Guide <nimrodc.html#command-line-switches>`_
`RST Quick Reference
<http://docutils.sourceforge.net/docs/user/rst/quickref.html>`_

12
doc/docgen_sample.nim Normal file
View file

@ -0,0 +1,12 @@
## This module is a sample.
import strutils
proc helloWorld*(times: int) =
## Takes an integer and outputs
## as many "hello world!"s
for i in 0 .. times-1:
echo "hello world!"
helloWorld(5)

View file

@ -42,14 +42,14 @@ par = '(' optInd (&parKeyw complexOrSimpleStmt ^+ ';'
| simpleExpr ('=' expr (';' complexOrSimpleStmt ^+ ';' )? )? | simpleExpr ('=' expr (';' complexOrSimpleStmt ^+ ';' )? )?
| (':' expr)? (',' (exprColonEqExpr comma?)*)? )? | (':' expr)? (',' (exprColonEqExpr comma?)*)? )?
optPar ')' optPar ')'
generalizedLit = GENERALIZED_STR_LIT | GENERALIZED_TRIPLESTR_LIT literal = | INT_LIT | INT8_LIT | INT16_LIT | INT32_LIT | INT64_LIT
identOrLiteral = generalizedLit | symbol
| INT_LIT | INT8_LIT | INT16_LIT | INT32_LIT | INT64_LIT
| UINT_LIT | UINT8_LIT | UINT16_LIT | UINT32_LIT | UINT64_LIT | UINT_LIT | UINT8_LIT | UINT16_LIT | UINT32_LIT | UINT64_LIT
| FLOAT_LIT | FLOAT32_LIT | FLOAT64_LIT | FLOAT_LIT | FLOAT32_LIT | FLOAT64_LIT
| STR_LIT | RSTR_LIT | TRIPLESTR_LIT | STR_LIT | RSTR_LIT | TRIPLESTR_LIT
| CHAR_LIT | CHAR_LIT
| NIL | NIL
generalizedLit = GENERALIZED_STR_LIT | GENERALIZED_TRIPLESTR_LIT
identOrLiteral = generalizedLit | symbol | literal
| par | arrayConstr | setOrTableConstr | par | arrayConstr | setOrTableConstr
| castExpr | castExpr
tupleConstr = '(' optInd (exprColonEqExpr comma?)* optPar ')' tupleConstr = '(' optInd (exprColonEqExpr comma?)* optPar ')'
@ -59,6 +59,8 @@ primarySuffix = '(' (exprColonEqExpr comma?)* ')' doBlocks?
| '.' optInd ('type' | 'addr' | symbol) generalizedLit? | '.' optInd ('type' | 'addr' | symbol) generalizedLit?
| '[' optInd indexExprList optPar ']' | '[' optInd indexExprList optPar ']'
| '{' optInd indexExprList optPar '}' | '{' optInd indexExprList optPar '}'
| &( '`'|IDENT|literal|'cast') expr ^+ ',' # command syntax
(doBlock | macroColon)?
condExpr = expr colcom expr optInd condExpr = expr colcom expr optInd
('elif' expr colcom expr optInd)* ('elif' expr colcom expr optInd)*
'else' colcom expr 'else' colcom expr
@ -95,18 +97,19 @@ primary = typeKeyw typeDescK
/ 'bind' primary / 'bind' primary
typeDesc = simpleExpr typeDesc = simpleExpr
typeDefAux = simpleExpr typeDefAux = simpleExpr
| 'generic' typeClass
macroColon = ':' stmt? ( IND{=} 'of' exprList ':' stmt
| IND{=} 'elif' expr ':' stmt
| IND{=} 'except' exprList ':' stmt
| IND{=} 'else' ':' stmt )*
exprStmt = simpleExpr exprStmt = simpleExpr
(( '=' optInd expr ) (( '=' optInd expr )
/ ( expr ^+ comma / ( expr ^+ comma
doBlocks doBlocks
/ ':' stmt? ( IND{=} 'of' exprList ':' stmt / macroColon
| IND{=} 'elif' expr ':' stmt
| IND{=} 'except' exprList ':' stmt
| IND{=} 'else' ':' stmt )*
))? ))?
moduleName = expr ('as' expr)? importStmt = 'import' optInd expr
importStmt = 'import' optInd moduleName ((comma expr)*
((comma moduleName)*
/ 'except' optInd (expr ^+ comma)) / 'except' optInd (expr ^+ comma))
includeStmt = 'include' optInd expr ^+ comma includeStmt = 'include' optInd expr ^+ comma
fromStmt = 'from' moduleName 'import' optInd expr (comma expr)* fromStmt = 'from' moduleName 'import' optInd expr (comma expr)*
@ -161,6 +164,9 @@ objectCase = 'case' identWithPragma ':' typeDesc ':'? COMMENT?
objectPart = IND{>} objectPart^+IND{=} DED objectPart = IND{>} objectPart^+IND{=} DED
/ objectWhen / objectCase / 'nil' / declColonEquals / objectWhen / objectCase / 'nil' / declColonEquals
object = 'object' pragma? ('of' typeDesc)? COMMENT? objectPart object = 'object' pragma? ('of' typeDesc)? COMMENT? objectPart
typeClassParam = ('var')? symbol
typeClass = typeClassParam ^* ',' (pragma)? ('of' typeDesc ^* ',')?
&IND{>} stmt
distinct = 'distinct' optInd typeDesc distinct = 'distinct' optInd typeDesc
typeDef = identWithPragma genericParamList? '=' optInd typeDefAux typeDef = identWithPragma genericParamList? '=' optInd typeDefAux
indAndComment? indAndComment?

View file

@ -2261,8 +2261,8 @@ from different modules having the same name.
using sdl.SetTimer using sdl.SetTimer
Note that ``using`` only *adds* to the current context, it doesn't remove or Note that ``using`` only *adds* to the current context, it doesn't remove or
replace, **neither** does it create a new scope. What this means is that if you replace, **neither** does it create a new scope. What this means is that if one
apply this to multiple variables the compiler will find conflicts in what applies this to multiple variables the compiler will find conflicts in what
variable to use: variable to use:
.. code-block:: nimrod .. code-block:: nimrod
@ -2275,7 +2275,7 @@ variable to use:
echo b echo b
When the compiler reaches the second ``add`` call, both ``a`` and ``b`` could When the compiler reaches the second ``add`` call, both ``a`` and ``b`` could
be used with the proc, so you get ``Error: expression '(a|b)' has no type (or be used with the proc, so one gets ``Error: expression '(a|b)' has no type (or
is ambiguous)``. To solve this you would need to nest ``using`` with a is ambiguous)``. To solve this you would need to nest ``using`` with a
``block`` statement so as to control the reach of the ``using`` statement. ``block`` statement so as to control the reach of the ``using`` statement.
@ -2368,8 +2368,8 @@ The `addr`:idx: operator returns the address of an l-value. If the type of the
location is ``T``, the `addr` operator result is of the type ``ptr T``. An location is ``T``, the `addr` operator result is of the type ``ptr T``. An
address is always an untraced reference. Taking the address of an object that address is always an untraced reference. Taking the address of an object that
resides on the stack is **unsafe**, as the pointer may live longer than the resides on the stack is **unsafe**, as the pointer may live longer than the
object on the stack and can thus reference a non-existing object. You can get object on the stack and can thus reference a non-existing object. One can get
the address of variables, but you can't use it on variables declared through the address of variables, but one can't use it on variables declared through
``let`` statements: ``let`` statements:
.. code-block:: nimrod .. code-block:: nimrod
@ -2764,7 +2764,7 @@ First class iterators
There are 2 kinds of iterators in Nimrod: *inline* and *closure* iterators. There are 2 kinds of iterators in Nimrod: *inline* and *closure* iterators.
An `inline iterator`:idx: is an iterator that's always inlined by the compiler An `inline iterator`:idx: is an iterator that's always inlined by the compiler
leading to zero overhead for the abstraction, but may result in a heavy leading to zero overhead for the abstraction, but may result in a heavy
increasee in code size. Inline iterators are second class increase in code size. Inline iterators are second class
citizens; one cannot pass them around like first class procs. citizens; one cannot pass them around like first class procs.
In contrast to that, a `closure iterator`:idx: can be passed around: In contrast to that, a `closure iterator`:idx: can be passed around:
@ -2837,6 +2837,23 @@ The builtin ``system.finished`` can be used to determine if an iterator has
finished its operation; no exception is raised on an attempt to invoke an finished its operation; no exception is raised on an attempt to invoke an
iterator that has already finished its work. iterator that has already finished its work.
Closure iterators are *resumable functions* and so one has to provide the
arguments to every call. To get around this limitation one can capture
parameters of an outer factory proc:
.. code-block:: nimrod
proc mycount(a, b: int): iterator (): int =
return iterator (): int =
var x = a
while x <= b:
yield x
inc x
let foo = mycount 1, 4
for f in foo():
echo f
Type sections Type sections
============= =============
@ -2923,9 +2940,9 @@ in an implicit try block:
finally: close(f) finally: close(f)
... ...
The ``except`` statement has a limitation in this form: you can't specify the The ``except`` statement has a limitation in this form: one can't specify the
type of the exception, you have to catch everything. Also, if you want to use type of the exception, one has to catch everything. Also, if one wants to use
both ``finally`` and ``except`` you need to reverse the usual sequence of the both ``finally`` and ``except`` one needs to reverse the usual sequence of the
statements. Example: statements. Example:
.. code-block:: nimrod .. code-block:: nimrod
@ -3353,7 +3370,7 @@ currently matched type. These instances can act both as variables of the type,
when used in contexts, where a value is expected, and as the type itself, when when used in contexts, where a value is expected, and as the type itself, when
used in a contexts, where a type is expected. used in a contexts, where a type is expected.
Please note that the ``is`` operator allows you to easily verify the precise Please note that the ``is`` operator allows one to easily verify the precise
type signatures of the required operations, but since type inference and type signatures of the required operations, but since type inference and
default parameters are still applied in the provided block, it's also possible default parameters are still applied in the provided block, it's also possible
to encode usage protocols that doesn't reveal implementation details. to encode usage protocols that doesn't reveal implementation details.

View file

@ -538,6 +538,13 @@ on Linux::
nimrod c --dynlibOverride:lua --passL:liblua.lib program.nim nimrod c --dynlibOverride:lua --passL:liblua.lib program.nim
Nimrod documentation tools
==========================
Nimrod provides the `doc`:idx: and `doc2`:idx: commands to generate HTML
documentation from ``.nim`` source files. Only exported symbols will appear in
the output. For more details `see the docgen documentation <docgen.html>`_.
Nimrod idetools integration Nimrod idetools integration
=========================== ===========================

View file

@ -42,7 +42,7 @@ Possible Commands:
csource [options] builds the C sources for installation csource [options] builds the C sources for installation
zip builds the installation ZIP package zip builds the installation ZIP package
inno [options] builds the Inno Setup installer (for Windows) inno [options] builds the Inno Setup installer (for Windows)
tests run the testsuite tests [options] run the testsuite
update updates nimrod to the latest version from github update updates nimrod to the latest version from github
(compile koch with -d:withUpdate to enable) (compile koch with -d:withUpdate to enable)
temp options creates a temporary compiler for testing temp options creates a temporary compiler for testing
@ -260,11 +260,14 @@ when defined(withUpdate):
# -------------- tests -------------------------------------------------------- # -------------- tests --------------------------------------------------------
template `|`(a, b): expr = (if a.len > 0: a else: b)
proc tests(args: string) = proc tests(args: string) =
# we compile the tester with taintMode:on to have a basic # we compile the tester with taintMode:on to have a basic
# taint mode test :-) # taint mode test :-)
exec("nimrod cc --taintMode:on tests/testament/tester") exec "nimrod cc --taintMode:on tests/testament/tester"
exec(getCurrentDir() / "tests/testament/tester".exe & " all") exec quoteShell(getCurrentDir() / "tests/testament/tester".exe) & " " &
(args|"all")
proc temp(args: string) = proc temp(args: string) =
var output = "compiler" / "nimrod".exe var output = "compiler" / "nimrod".exe

View file

@ -300,9 +300,12 @@ when not defined(booting):
## that should be inserted verbatim in the program ## that should be inserted verbatim in the program
## Example: ## Example:
## ##
## .. code-block:: nimrod
## emit("echo " & '"' & "hello world".toUpper & '"') ## emit("echo " & '"' & "hello world".toUpper & '"')
## ##
eval: result = e.parseStmt macro payload: stmt {.gensym.} =
result = e.parseStmt
payload()
proc expectKind*(n: PNimrodNode, k: TNimrodNodeKind) {.compileTime.} = proc expectKind*(n: PNimrodNode, k: TNimrodNodeKind) {.compileTime.} =
## checks that `n` is of kind `k`. If this is not the case, ## checks that `n` is of kind `k`. If this is not the case,
@ -387,7 +390,7 @@ proc treeRepr*(n: PNimrodNode): string {.compileTime.} =
res.add(($n.kind).substr(3)) res.add(($n.kind).substr(3))
case n.kind case n.kind
of nnkEmpty: nil # same as nil node in this representation of nnkEmpty: discard # same as nil node in this representation
of nnkNilLit: res.add(" nil") of nnkNilLit: res.add(" nil")
of nnkCharLit..nnkInt64Lit: res.add(" " & $n.intVal) of nnkCharLit..nnkInt64Lit: res.add(" " & $n.intVal)
of nnkFloatLit..nnkFloat64Lit: res.add(" " & $n.floatVal) of nnkFloatLit..nnkFloat64Lit: res.add(" " & $n.floatVal)
@ -412,7 +415,7 @@ proc lispRepr*(n: PNimrodNode): string {.compileTime.} =
add(result, "(") add(result, "(")
case n.kind case n.kind
of nnkEmpty: nil # same as nil node in this representation of nnkEmpty: discard # same as nil node in this representation
of nnkNilLit: add(result, "nil") of nnkNilLit: add(result, "nil")
of nnkCharLit..nnkInt64Lit: add(result, $n.intVal) of nnkCharLit..nnkInt64Lit: add(result, $n.intVal)
of nnkFloatLit..nnkFloat64Lit: add(result, $n.floatVal) of nnkFloatLit..nnkFloat64Lit: add(result, $n.floatVal)
@ -645,10 +648,13 @@ iterator children*(n: PNimrodNode): PNimrodNode {.inline.}=
for i in 0 .. high(n): for i in 0 .. high(n):
yield n[i] yield n[i]
template findChild*(n: PNimrodNode; cond: expr): PNimrodNode {.immediate, dirty.} = template findChild*(n: PNimrodNode; cond: expr): PNimrodNode {.
## Find the first child node matching condition (or nil) immediate, dirty.} =
## var res = findChild(n, it.kind == nnkPostfix and it.basename.ident == !"foo") ## Find the first child node matching condition (or nil).
##
## .. code-block:: nimrod
## var res = findChild(n, it.kind == nnkPostfix and
## it.basename.ident == !"foo")
block: block:
var result: PNimrodNode var result: PNimrodNode
for it in n.children: for it in n.children:
@ -736,6 +742,6 @@ proc addIdentIfAbsent*(dest: PNimrodNode, ident: string) {.compiletime.} =
if ident.eqIdent($node): return if ident.eqIdent($node): return
of nnkExprColonExpr: of nnkExprColonExpr:
if ident.eqIdent($node[0]): return if ident.eqIdent($node[0]): return
else: nil else: discard
dest.add(ident(ident)) dest.add(ident(ident))

View file

@ -139,27 +139,27 @@ type
proc newDelegate*(): PDelegate = proc newDelegate*(): PDelegate =
## Creates a new delegate. ## Creates a new delegate.
new(result) new(result)
result.handleRead = (proc (h: PObject) = nil) result.handleRead = (proc (h: PObject) = discard)
result.handleWrite = (proc (h: PObject) = nil) result.handleWrite = (proc (h: PObject) = discard)
result.handleError = (proc (h: PObject) = nil) result.handleError = (proc (h: PObject) = discard)
result.hasDataBuffered = (proc (h: PObject): bool = return false) result.hasDataBuffered = (proc (h: PObject): bool = return false)
result.task = (proc (h: PObject) = nil) result.task = (proc (h: PObject) = discard)
result.mode = fmRead result.mode = fmRead
proc newAsyncSocket(): PAsyncSocket = proc newAsyncSocket(): PAsyncSocket =
new(result) new(result)
result.info = SockIdle result.info = SockIdle
result.handleRead = (proc (s: PAsyncSocket) = nil) result.handleRead = (proc (s: PAsyncSocket) = discard)
result.handleWrite = nil result.handleWrite = nil
result.handleConnect = (proc (s: PAsyncSocket) = nil) result.handleConnect = (proc (s: PAsyncSocket) = discard)
result.handleAccept = (proc (s: PAsyncSocket) = nil) result.handleAccept = (proc (s: PAsyncSocket) = discard)
result.handleTask = (proc (s: PAsyncSocket) = nil) result.handleTask = (proc (s: PAsyncSocket) = discard)
result.lineBuffer = "".TaintedString result.lineBuffer = "".TaintedString
result.sendBuffer = "" result.sendBuffer = ""
proc AsyncSocket*(domain: TDomain = AF_INET, typ: TType = SOCK_STREAM, proc asyncSocket*(domain: TDomain = AF_INET, typ: TType = SOCK_STREAM,
protocol: TProtocol = IPPROTO_TCP, protocol: TProtocol = IPPROTO_TCP,
buffered = true): PAsyncSocket = buffered = true): PAsyncSocket =
## Initialises an AsyncSocket object. If a socket cannot be initialised ## Initialises an AsyncSocket object. If a socket cannot be initialised

View file

@ -95,7 +95,7 @@ type
EInvalidReply* = object of ESynch EInvalidReply* = object of ESynch
EFTP* = object of ESynch EFTP* = object of ESynch
proc FTPClient*(address: string, port = TPort(21), proc ftpClient*(address: string, port = TPort(21),
user, pass = ""): PFTPClient = user, pass = ""): PFTPClient =
## Create a ``PFTPClient`` object. ## Create a ``PFTPClient`` object.
new(result) new(result)
@ -315,7 +315,7 @@ proc listDirs*(ftp: PFTPClient, dir: string = "",
assertReply ftp.send("NLST " & dir.normalizePathSep), ["125", "150"] assertReply ftp.send("NLST " & dir.normalizePathSep), ["125", "150"]
if not async: if not async:
while not ftp.job.prc(ftp, false): nil while not ftp.job.prc(ftp, false): discard
result = splitLines(ftp.job.lines) result = splitLines(ftp.job.lines)
ftp.deleteJob() ftp.deleteJob()
else: return @[] else: return @[]
@ -390,7 +390,7 @@ proc list*(ftp: PFTPClient, dir: string = "", async = false): string =
assertReply(ftp.send("LIST" & " " & dir.normalizePathSep), ["125", "150"]) assertReply(ftp.send("LIST" & " " & dir.normalizePathSep), ["125", "150"])
if not async: if not async:
while not ftp.job.prc(ftp, false): nil while not ftp.job.prc(ftp, false): discard
result = ftp.job.lines result = ftp.job.lines
ftp.deleteJob() ftp.deleteJob()
else: else:
@ -405,7 +405,7 @@ proc retrText*(ftp: PFTPClient, file: string, async = false): string =
assertReply ftp.send("RETR " & file.normalizePathSep), ["125", "150"] assertReply ftp.send("RETR " & file.normalizePathSep), ["125", "150"]
if not async: if not async:
while not ftp.job.prc(ftp, false): nil while not ftp.job.prc(ftp, false): discard
result = ftp.job.lines result = ftp.job.lines
ftp.deleteJob() ftp.deleteJob()
else: else:
@ -460,7 +460,7 @@ proc retrFile*(ftp: PFTPClient, file, dest: string, async = false) =
ftp.job.filename = file.normalizePathSep ftp.job.filename = file.normalizePathSep
if not async: if not async:
while not ftp.job.prc(ftp, false): nil while not ftp.job.prc(ftp, false): discard
ftp.deleteJob() ftp.deleteJob()
proc doUpload(ftp: PFTPClient, async = false): bool = proc doUpload(ftp: PFTPClient, async = false): bool =
@ -518,7 +518,7 @@ proc store*(ftp: PFTPClient, file, dest: string, async = false) =
assertReply ftp.send("STOR " & dest.normalizePathSep), ["125", "150"] assertReply ftp.send("STOR " & dest.normalizePathSep), ["125", "150"]
if not async: if not async:
while not ftp.job.prc(ftp, false): nil while not ftp.job.prc(ftp, false): discard
ftp.deleteJob() ftp.deleteJob()
proc close*(ftp: PFTPClient) = proc close*(ftp: PFTPClient) =
@ -554,10 +554,10 @@ proc csockHandleRead(s: PAsyncSocket, ftp: PAsyncFTPClient) =
ftp.handleEvent(ftp, r) ftp.handleEvent(ftp, r)
proc AsyncFTPClient*(address: string, port = TPort(21), proc asyncFTPClient*(address: string, port = TPort(21),
user, pass = "", user, pass = "",
handleEvent: proc (ftp: PAsyncFTPClient, ev: TFTPEvent) {.closure.} = handleEvent: proc (ftp: PAsyncFTPClient, ev: TFTPEvent) {.closure.} =
(proc (ftp: PAsyncFTPClient, ev: TFTPEvent) = nil)): PAsyncFTPClient = (proc (ftp: PAsyncFTPClient, ev: TFTPEvent) = discard)): PAsyncFTPClient =
## Create a ``PAsyncFTPClient`` object. ## Create a ``PAsyncFTPClient`` object.
## ##
## Use this if you want to use asyncio's dispatcher. ## Use this if you want to use asyncio's dispatcher.
@ -604,7 +604,7 @@ when isMainModule:
ftp.close() ftp.close()
echo d.len echo d.len
else: assert(false) else: assert(false)
var ftp = AsyncFTPClient("picheta.me", user = "test", pass = "asf", handleEvent = hev) var ftp = asyncFTPClient("picheta.me", user = "test", pass = "asf", handleEvent = hev)
d.register(ftp) d.register(ftp)
d.len.echo() d.len.echo()
@ -618,7 +618,7 @@ when isMainModule:
when isMainModule and false: when isMainModule and false:
var ftp = FTPClient("picheta.me", user = "asdasd", pass = "asfwq") var ftp = ftpClient("picheta.me", user = "asdasd", pass = "asfwq")
ftp.connect() ftp.connect()
echo ftp.pwd() echo ftp.pwd()
echo ftp.list() echo ftp.list()

View file

@ -1586,7 +1586,7 @@ proc getAppFilename*(): string {.rtl, extern: "nos$1", tags: [FReadIO].} =
# little heuristic that may work on other POSIX-like systems: # little heuristic that may work on other POSIX-like systems:
result = string(getEnv("_")) result = string(getEnv("_"))
if len(result) == 0: if len(result) == 0:
result = string(ParamStr(0)) result = string(paramStr(0))
# POSIX guaranties that this contains the executable # POSIX guaranties that this contains the executable
# as it has been executed by the calling process # as it has been executed by the calling process
if len(result) > 0 and result[0] != DirSep: # not an absolute path? if len(result) > 0 and result[0] != DirSep: # not an absolute path?

View file

@ -664,9 +664,8 @@ elif not defined(useNimRtl):
chck res chck res
else: else:
pid = fork()
Pid = fork() if pid < 0: osError(osLastError())
if Pid < 0: osError(osLastError())
if pid == 0: if pid == 0:
## child process: ## child process:

View file

@ -306,7 +306,7 @@ proc backrefIgnoreStyle*(index: range[1..MaxSubPatterns]): TPeg {.
proc spaceCost(n: TPeg): int = proc spaceCost(n: TPeg): int =
case n.kind case n.kind
of pkEmpty: nil of pkEmpty: discard
of pkTerminal, pkTerminalIgnoreCase, pkTerminalIgnoreStyle, pkChar, of pkTerminal, pkTerminalIgnoreCase, pkTerminalIgnoreStyle, pkChar,
pkGreedyRepChar, pkCharChoice, pkGreedyRepSet, pkGreedyRepChar, pkCharChoice, pkGreedyRepSet,
pkAny..pkWhitespace, pkGreedyAny: pkAny..pkWhitespace, pkGreedyAny:
@ -1117,7 +1117,7 @@ proc handleHexChar(c: var TPegLexer, xi: var int) =
of 'A'..'F': of 'A'..'F':
xi = (xi shl 4) or (ord(c.buf[c.bufpos]) - ord('A') + 10) xi = (xi shl 4) or (ord(c.buf[c.bufpos]) - ord('A') + 10)
inc(c.bufpos) inc(c.bufpos)
else: nil else: discard
proc getEscapedChar(c: var TPegLexer, tok: var TToken) = proc getEscapedChar(c: var TPegLexer, tok: var TToken) =
inc(c.bufpos) inc(c.bufpos)
@ -1347,7 +1347,7 @@ proc getTok(c: var TPegLexer, tok: var TToken) =
of "i": tok.modifier = modIgnoreCase of "i": tok.modifier = modIgnoreCase
of "y": tok.modifier = modIgnoreStyle of "y": tok.modifier = modIgnoreStyle
of "v": tok.modifier = modVerbatim of "v": tok.modifier = modVerbatim
else: nil else: discard
setLen(tok.literal, 0) setLen(tok.literal, 0)
if c.buf[c.bufpos] == '$': if c.buf[c.bufpos] == '$':
getDollar(c, tok) getDollar(c, tok)
@ -1494,7 +1494,7 @@ proc primary(p: var TPegParser): TPeg =
of tkCurlyAt: of tkCurlyAt:
getTok(p) getTok(p)
return !*\primary(p).token(p) return !*\primary(p).token(p)
else: nil else: discard
case p.tok.kind case p.tok.kind
of tkIdentifier: of tkIdentifier:
if p.identIsVerbatim: if p.identIsVerbatim:

View file

@ -192,7 +192,7 @@ when defined(Posix):
of AF_UNIX: result = posix.AF_UNIX of AF_UNIX: result = posix.AF_UNIX
of AF_INET: result = posix.AF_INET of AF_INET: result = posix.AF_INET
of AF_INET6: result = posix.AF_INET6 of AF_INET6: result = posix.AF_INET6
else: nil else: discard
proc toInt(typ: TType): cint = proc toInt(typ: TType): cint =
case typ case typ
@ -200,7 +200,7 @@ when defined(Posix):
of SOCK_DGRAM: result = posix.SOCK_DGRAM of SOCK_DGRAM: result = posix.SOCK_DGRAM
of SOCK_SEQPACKET: result = posix.SOCK_SEQPACKET of SOCK_SEQPACKET: result = posix.SOCK_SEQPACKET
of SOCK_RAW: result = posix.SOCK_RAW of SOCK_RAW: result = posix.SOCK_RAW
else: nil else: discard
proc toInt(p: TProtocol): cint = proc toInt(p: TProtocol): cint =
case p case p
@ -210,7 +210,7 @@ when defined(Posix):
of IPPROTO_IPV6: result = posix.IPPROTO_IPV6 of IPPROTO_IPV6: result = posix.IPPROTO_IPV6
of IPPROTO_RAW: result = posix.IPPROTO_RAW of IPPROTO_RAW: result = posix.IPPROTO_RAW
of IPPROTO_ICMP: result = posix.IPPROTO_ICMP of IPPROTO_ICMP: result = posix.IPPROTO_ICMP
else: nil else: discard
else: else:
proc toInt(domain: TDomain): cint = proc toInt(domain: TDomain): cint =

View file

@ -447,6 +447,7 @@ proc chr*(u: range[0..255]): char {.magic: "Chr", noSideEffect.}
# -------------------------------------------------------------------------- # --------------------------------------------------------------------------
# built-in operators # built-in operators
when not defined(JS):
proc ze*(x: int8): int {.magic: "Ze8ToI", noSideEffect.} proc ze*(x: int8): int {.magic: "Ze8ToI", noSideEffect.}
## zero extends a smaller integer type to ``int``. This treats `x` as ## zero extends a smaller integer type to ``int``. This treats `x` as
## unsigned. ## unsigned.
@ -479,7 +480,6 @@ proc toU32*(x: int64): int32 {.magic: "ToU32", noSideEffect.}
## treats `x` as unsigned and converts it to an ``int32`` by taking the ## treats `x` as unsigned and converts it to an ``int32`` by taking the
## last 32 bits from `x`. ## last 32 bits from `x`.
# integer calculations: # integer calculations:
proc `+` *(x: int): int {.magic: "UnaryPlusI", noSideEffect.} proc `+` *(x: int): int {.magic: "UnaryPlusI", noSideEffect.}
proc `+` *(x: int8): int8 {.magic: "UnaryPlusI", noSideEffect.} proc `+` *(x: int8): int8 {.magic: "UnaryPlusI", noSideEffect.}
@ -1330,7 +1330,7 @@ iterator `||`*[S, T](a: S, b: T, annotation=""): T {.
## such isn't aware of the parallelism in your code! Be careful! Later ## such isn't aware of the parallelism in your code! Be careful! Later
## versions of ``||`` will get proper support by Nimrod's code generator ## versions of ``||`` will get proper support by Nimrod's code generator
## and GC. ## and GC.
nil discard
{.push stackTrace:off.} {.push stackTrace:off.}
proc min*(x, y: int): int {.magic: "MinI", noSideEffect.} = proc min*(x, y: int): int {.magic: "MinI", noSideEffect.} =

View file

@ -11,7 +11,7 @@
# use the heap (and nor exceptions) do not include the GC or memory allocator. # use the heap (and nor exceptions) do not include the GC or memory allocator.
var var
errorMessageWriter*: (proc(msg: string): void {.tags: [FWriteIO].}) errorMessageWriter*: (proc(msg: string) {.tags: [FWriteIO].})
## Function that will be called ## Function that will be called
## instead of stdmsg.write when printing stacktrace. ## instead of stdmsg.write when printing stacktrace.
## Unstable API. ## Unstable API.
@ -80,9 +80,9 @@ when defined(nativeStacktrace) and nativeStackTraceSupported:
type type
TDl_info {.importc: "Dl_info", header: "<dlfcn.h>", TDl_info {.importc: "Dl_info", header: "<dlfcn.h>",
final, pure.} = object final, pure.} = object
dli_fname: CString dli_fname: cstring
dli_fbase: pointer dli_fbase: pointer
dli_sname: CString dli_sname: cstring
dli_saddr: pointer dli_saddr: pointer
proc backtrace(symbols: ptr pointer, size: int): int {. proc backtrace(symbols: ptr pointer, size: int): int {.

View file

@ -472,17 +472,17 @@ proc Ze(a: int): int {.compilerproc.} =
proc Ze64(a: int64): int64 {.compilerproc.} = proc Ze64(a: int64): int64 {.compilerproc.} =
result = a result = a
proc toU8(a: int): int8 {.noStackFrame, compilerproc.} = proc ToU8(a: int): int8 {.noStackFrame, compilerproc.} =
asm """ asm """
return `a`; return `a`;
""" """
proc toU16(a: int): int16 {.noStackFrame, compilerproc.} = proc ToU16(a: int): int16 {.noStackFrame, compilerproc.} =
asm """ asm """
return `a`; return `a`;
""" """
proc toU32(a: int): int32 {.noStackFrame, compilerproc.} = proc ToU32(a: int): int32 {.noStackFrame, compilerproc.} =
asm """ asm """
return `a`; return `a`;
""" """

View file

@ -3,7 +3,7 @@ discard """
WARNING: false first asseertion from bar WARNING: false first asseertion from bar
ERROR: false second assertion from bar ERROR: false second assertion from bar
-1 -1
tests/run/tfailedassert.nim:27 false assertion from foo tests/assert/tfailedassert.nim:27 false assertion from foo
''' '''
""" """

View file

@ -4,7 +4,7 @@ type
TButtonClicked = proc(button: PButton) {.nimcall.} TButtonClicked = proc(button: PButton) {.nimcall.}
proc newButton*(onClick: TButtonClicked) = proc newButton*(onClick: TButtonClicked) =
nil discard
proc main() = proc main() =
newButton(onClick = proc(b: PButton) = newButton(onClick = proc(b: PButton) =

32
tests/iter/tanoniter1.nim Normal file
View file

@ -0,0 +1,32 @@
discard """
output: '''1
2
3
4
1
2'''
"""
proc factory(a, b: int): iterator (): int =
iterator foo(): int =
var x = a
while x <= b:
yield x
inc x
return foo
proc factory2(a, b: int): iterator (): int =
return iterator (): int =
var x = a
while x <= b:
yield x
inc x
let foo = factory 1, 4
for f in foo():
echo f
let foo2 = factory2 1,2
for f in foo2(): echo f

View file

@ -1,6 +1,6 @@
discard """ discard """
output: '''true''' output: '''true'''
cmd: "nimrod cc --gc:none --hints:on $# $#" cmd: "nimrod cc --gc:none --hints:on --warnings:off $# $#"
""" """
import hashes import hashes

7
tests/macros/tmemit.nim Normal file
View file

@ -0,0 +1,7 @@
discard """
out: '''HELLO WORLD'''
"""
import macros, strutils
emit("echo " & '"' & "hello world".toUpper & '"')

View file

@ -0,0 +1,12 @@
discard """
output: "12"
"""
proc foo(x: int): int = x-1
proc foo(x, y: int): int = x-y
let x = foo 7.foo, # comment here
foo(1, foo 8)
# 12 = 6 - -6
echo x

View file

@ -0,0 +1,17 @@
discard """
file: "tdomulttest.nim"
output: "555\ntest\nmulti lines\n99999999\nend"
disabled: true
"""
proc foo(bar, baz: proc (x: int): int) =
echo bar(555)
echo baz(99999999)
foo do (x: int) -> int:
return x
do (x: int) -> int:
echo("test")
echo("multi lines")
return x
echo("end")

15
tests/parser/tinvwhen.nim Normal file
View file

@ -0,0 +1,15 @@
discard """
file: "tinvwhen.nim"
line: 11
errormsg: "invalid indentation"
"""
# This was parsed even though it should not!
proc chdir(path: cstring): cint {.importc: "chdir", header: "dirHeader".}
proc getcwd(buf: cstring, buflen: cint): cstring
when defined(unix): {.importc: "getcwd", header: "<unistd.h>".} #ERROR_MSG invalid indentation
elif defined(windows): {.importc: "getcwd", header: "<direct.h>"}
else: {.error: "os library not ported to your OS. Please help!".}

View file

@ -1,18 +1,8 @@
version 0.9.4 version 0.9.4
============= =============
- test&finish first class iterators: - better debugging support for writes to locations
* nested iterators
- ensure (ref T)(a, b) works as a type conversion and type constructor
- document new templating symbol binding rules - document new templating symbol binding rules
- make '--implicitStatic:on' the default
- special rule for ``[]=``
- ``=`` should be overloadable; requires specialization for ``=``; general
lift mechanism in the compiler is already implemented for 'fields'
- built-in 'getImpl'
- optimize 'genericReset'; 'newException' leads to code bloat
- stack-less GC
- fix eval in macros.nim - fix eval in macros.nim
@ -27,8 +17,6 @@ Bugs
- docgen: sometimes effects are listed twice - docgen: sometimes effects are listed twice
- 'result' is not properly cleaned for NRVO --> use uninit checking instead - 'result' is not properly cleaned for NRVO --> use uninit checking instead
- sneaking with qualifiedLookup() is really broken! - sneaking with qualifiedLookup() is really broken!
- aporia.nim(968, 5) Error: ambiguous identifier: 'DELETE' --
use a qualifier
- blocks can "export" an identifier but the CCG generates {} for them ... - blocks can "export" an identifier but the CCG generates {} for them ...
- osproc execProcesses can deadlock if all processes fail (as experienced - osproc execProcesses can deadlock if all processes fail (as experienced
in c++ mode) in c++ mode)
@ -37,12 +25,29 @@ Bugs
version 0.9.x version 0.9.x
============= =============
- macros as type pragmas - ensure (ref T)(a, b) works as a type conversion and type constructor
- optimize 'genericReset'; 'newException' leads to code bloat
- stack-less GC
- implement strongSpaces:on
- make '--implicitStatic:on' the default
- implicit deref for parameter matching - implicit deref for parameter matching
- special rule for ``[]=``
- ``=`` should be overloadable; requires specialization for ``=``; general
lift mechanism in the compiler is already implemented for 'fields'
- built-in 'getImpl'
- change comment handling in the AST; that's lots of work as c2nim and pas2nim
make use of the fast every node can have a comment!
version 0.9.X
=============
- macros as type pragmas
- lazy overloading resolution: - lazy overloading resolution:
* special case ``tyStmt`` * special case ``tyStmt``
- FFI: - FFI:
* test libffi on windows
* test: times.format with the FFI * test: times.format with the FFI
- document NimMain and check whether it works for threading - document NimMain and check whether it works for threading
- 'quote' without 'do' doesn't work: parser/grammar issue; could be supported - 'quote' without 'do' doesn't work: parser/grammar issue; could be supported
@ -54,7 +59,6 @@ version 0.9.X
- implement the missing features wrt inheritance - implement the missing features wrt inheritance
- better support for macros that rewrite procs - better support for macros that rewrite procs
- macros need access to types and symbols (partially implemented) - macros need access to types and symbols (partially implemented)
- perhaps: change comment handling in the AST
- enforce 'simpleExpr' more often --> doesn't work; tkProc is - enforce 'simpleExpr' more often --> doesn't work; tkProc is
part of primary! part of primary!
- the typeDesc/expr unification is weird and only necessary because of - the typeDesc/expr unification is weird and only necessary because of

View file

@ -204,6 +204,18 @@ proc exec(cmd: string) =
echo(cmd) echo(cmd)
if os.execShellCmd(cmd) != 0: quit("external program failed") if os.execShellCmd(cmd) != 0: quit("external program failed")
proc buildDocSamples(c: var TConfigData, destPath: string) =
## Special case documentation sample proc.
##
## The docgen sample needs to be generated twice with different commands, so
## it didn't make much sense to integrate into the existing generic
## documentation builders.
const src = "doc"/"docgen_sample.nim"
Exec("nimrod doc $# -o:$# $#" %
[c.nimrodArgs, destPath / "docgen_sample.html", src])
Exec("nimrod doc2 $# -o:$# $#" %
[c.nimrodArgs, destPath / "docgen_sample2.html", src])
proc buildDoc(c: var TConfigData, destPath: string) = proc buildDoc(c: var TConfigData, destPath: string) =
# call nim for the documentation: # call nim for the documentation:
for d in items(c.doc): for d in items(c.doc):
@ -352,7 +364,9 @@ proc main(c: var TConfigData) =
copyDir("web/assets", "web/upload/assets") copyDir("web/assets", "web/upload/assets")
buildNewsRss(c, "web/upload") buildNewsRss(c, "web/upload")
buildAddDoc(c, "web/upload") buildAddDoc(c, "web/upload")
buildDocSamples(c, "web/upload")
buildDoc(c, "web/upload") buildDoc(c, "web/upload")
buildDocSamples(c, "doc")
buildDoc(c, "doc") buildDoc(c, "doc")
buildPdfDoc(c, "doc") buildPdfDoc(c, "doc")

View file

@ -28,8 +28,8 @@ Changes affecting backwards compatibility
require an error code to be passed to them. This error code can be retrieved require an error code to be passed to them. This error code can be retrieved
using the new ``OSLastError`` proc. using the new ``OSLastError`` proc.
- ``os.parentDir`` now returns "" if there is no parent dir. - ``os.parentDir`` now returns "" if there is no parent dir.
- In CGI scripts stacktraces are shown user only if cgi.setStackTraceStdout - In CGI scripts stacktraces are shown to the user only
is used. if ``cgi.setStackTraceStdout`` is used.
- The symbol binding rules for clean templates changed: ``bind`` for any - The symbol binding rules for clean templates changed: ``bind`` for any
symbol that's not a parameter is now the default. ``mixin`` can be used symbol that's not a parameter is now the default. ``mixin`` can be used
to require instantiation scope for a symbol. to require instantiation scope for a symbol.
@ -71,8 +71,11 @@ Language Additions
- Added a new ``delegator pragma`` for handling calls to missing procs and - Added a new ``delegator pragma`` for handling calls to missing procs and
fields at compile-time. fields at compile-time.
- The overload resolution now supports ``static[T]`` params that must be - The overload resolution now supports ``static[T]`` params that must be
evaluatable at compile-time. evaluable at compile-time.
- Support for user-defined type classes has been added. - Support for user-defined type classes has been added.
- The *command syntax* is supported in a lot more contexts.
- Anonymous iterators are now supported and iterators can capture variables
of an outer proc.
Tools improvements Tools improvements

View file

@ -37,7 +37,7 @@ UNIX. We don't believe this to be a coincidence. - Jeremy S. Anderson."""
[Documentation] [Documentation]
doc: "endb;intern;apis;lib;manual;tut1;tut2;nimrodc;overview;filters;trmacros" doc: "endb;intern;apis;lib;manual;tut1;tut2;nimrodc;overview;filters;trmacros"
doc: "tools;c2nim;niminst;nimgrep;gc;estp;idetools" doc: "tools;c2nim;niminst;nimgrep;gc;estp;idetools;docgen"
pdf: "manual;lib;tut1;tut2;nimrodc;c2nim;niminst;gc" pdf: "manual;lib;tut1;tut2;nimrodc;c2nim;niminst;gc"
srcdoc2: "system.nim;impure/graphics;wrappers/sdl" srcdoc2: "system.nim;impure/graphics;wrappers/sdl"
srcdoc2: "core/macros;pure/marshal;core/typeinfo;core/unsigned" srcdoc2: "core/macros;pure/marshal;core/typeinfo;core/unsigned"