diff options
Diffstat (limited to 'compiler')
32 files changed, 658 insertions, 851 deletions
diff --git a/compiler/ast.nim b/compiler/ast.nim index 5a5d87d06..0790df0c4 100644 --- a/compiler/ast.nim +++ b/compiler/ast.nim @@ -238,7 +238,7 @@ type sfMainModule, # module is the main module sfSystemModule, # module is the system module sfNoReturn, # proc never returns (an exit proc) - sfAddrTaken, # the variable's address is taken (ex- or implicitely); + sfAddrTaken, # the variable's address is taken (ex- or implicitly); # *OR*: a proc is indirectly called (used as first class) sfCompilerProc, # proc is a compiler proc, that is a C proc that is # needed for the code generator @@ -257,7 +257,7 @@ type # for interfacing with C++, JS sfNamedParamCall, # symbol needs named parameter call syntax in target # language; for interfacing with Objective C - sfDiscardable, # returned value may be discarded implicitely + sfDiscardable, # returned value may be discarded implicitly sfDestructor, # proc is destructor sfGenSym # symbol is 'gensym'ed; do not add to symbol table @@ -335,12 +335,19 @@ type tyConst, tyMutable, tyVarargs, tyIter, # unused tyProxy # used as errornous type (for idetools) - tyTypeClass, + tyTypeClass + tyAnd + tyOr + tyNot + tyAnything + tyParametricTypeClass # structured similarly to tyGenericInst + # lastSon is the body of the type class const tyPureObject* = tyTuple GcTypeKinds* = {tyRef, tySequence, tyString} tyError* = tyProxy # as an errornous node should match everything + tyTypeClasses* = {tyTypeClass, tyParametricTypeClass, tyAnd, tyOr, tyNot, tyAnything} type TTypeKinds* = set[TTypeKind] @@ -377,6 +384,7 @@ type # used as return types for return type inference) tfAll, # type class requires all constraints to be met (default) tfAny, # type class requires any constraint to be met + tfNot, # type class with a negative check tfCapturesEnv, # whether proc really captures some environment tfByCopy, # pass object/tuple by copy (C backend) tfByRef, # pass object/tuple by reference (C backend) @@ -1416,3 +1424,4 @@ proc isAtom*(n: PNode): bool {.inline.} = proc isEmptyType*(t: PType): bool {.inline.} = ## 'void' and 'stmt' types are often equivalent to 'nil' these days: result = t == nil or t.kind in {tyEmpty, tyStmt} + diff --git a/compiler/ccgcalls.nim b/compiler/ccgcalls.nim index 1d6df3c15..07fba95a3 100644 --- a/compiler/ccgcalls.nim +++ b/compiler/ccgcalls.nim @@ -290,6 +290,7 @@ proc genCall(p: BProc, e: PNode, d: var TLoc) = genNamedParamCall(p, e, d) else: genPrefixCall(p, nil, e, d) + postStmtActions(p) when false: if d.s == onStack and containsGarbageCollectedRef(d.t): keepAlive(p, d) @@ -303,6 +304,7 @@ proc genAsgnCall(p: BProc, le, ri: PNode, d: var TLoc) = genNamedParamCall(p, ri, d) else: genPrefixCall(p, le, ri, d) + postStmtActions(p) when false: if d.s == onStack and containsGarbageCollectedRef(d.t): keepAlive(p, d) diff --git a/compiler/ccgexprs.nim b/compiler/ccgexprs.nim index 15f7f0f34..daa0c8d88 100644 --- a/compiler/ccgexprs.nim +++ b/compiler/ccgexprs.nim @@ -254,6 +254,7 @@ proc genGenericAsgn(p: BProc, dest, src: TLoc, flags: TAssignmentFlags) = if needToCopy notin flags or tfShallow in skipTypes(dest.t, abstractVarRange).flags: if dest.s == OnStack or not usesNativeGC(): + useStringh(p.module) linefmt(p, cpsStmts, "memcpy((void*)$1, (NIM_CONST void*)$2, sizeof($3));$n", addrLoc(dest), addrLoc(src), rdLoc(dest)) @@ -330,6 +331,7 @@ proc genAssignment(p: BProc, dest, src: TLoc, flags: TAssignmentFlags) = if needsComplexAssignment(dest.t): genGenericAsgn(p, dest, src, flags) else: + useStringh(p.module) linefmt(p, cpsStmts, "memcpy((void*)$1, (NIM_CONST void*)$2, sizeof($1));$n", rdLoc(dest), rdLoc(src)) @@ -341,11 +343,13 @@ proc genAssignment(p: BProc, dest, src: TLoc, flags: TAssignmentFlags) = "#genericAssignOpenArray((void*)$1, (void*)$2, $1Len0, $3);$n", addrLoc(dest), addrLoc(src), genTypeInfo(p.module, dest.t)) else: + useStringh(p.module) linefmt(p, cpsStmts, "memcpy((void*)$1, (NIM_CONST void*)$2, sizeof($1[0])*$1Len0);$n", rdLoc(dest), rdLoc(src)) of tySet: if mapType(ty) == ctArray: + useStringh(p.module) linefmt(p, cpsStmts, "memcpy((void*)$1, (NIM_CONST void*)$2, $3);$n", rdLoc(dest), rdLoc(src), toRope(getSize(dest.t))) else: @@ -1391,6 +1395,7 @@ proc genSetOp(p: BProc, e: PNode, d: var TLoc, op: TMagic) = lineF(p, cpsStmts, lookupOpr[op], [rdLoc(i), toRope(size), rdLoc(d), rdLoc(a), rdLoc(b)]) of mEqSet: + useStringh(p.module) binaryExprChar(p, e, d, "(memcmp($1, $2, " & $(size) & ")==0)") of mMulSet, mPlusSet, mMinusSet, mSymDiffSet: # we inline the simple for loop for better code generation: @@ -1642,6 +1647,7 @@ proc genSetConstr(p: BProc, e: PNode, d: var TLoc) = if d.k == locNone: getTemp(p, e.typ, d) if getSize(e.typ) > 8: # big set: + useStringh(p.module) lineF(p, cpsStmts, "memset($1, 0, sizeof($1));$n", [rdLoc(d)]) for i in countup(0, sonsLen(e) - 1): if e.sons[i].kind == nkRange: diff --git a/compiler/ccgstmts.nim b/compiler/ccgstmts.nim index 75cabf414..ac4bbb79f 100644 --- a/compiler/ccgstmts.nim +++ b/compiler/ccgstmts.nim @@ -906,7 +906,12 @@ proc genPragma(p: BProc, n: PNode) = of wEmit: genEmit(p, it) of wBreakpoint: genBreakPoint(p, it) of wWatchpoint: genWatchpoint(p, it) - else: nil + of wInjectStmt: + var p = newProc(nil, p.module) + p.options = p.options - {optLineTrace, optStackTrace} + genStmts(p, it.sons[1]) + p.module.injectStmt = p.s(cpsStmts) + else: discard proc FieldDiscriminantCheckNeeded(p: BProc, asgn: PNode): bool = if optFieldCheck in p.options: diff --git a/compiler/ccgutils.nim b/compiler/ccgutils.nim index c37754511..310f7204a 100644 --- a/compiler/ccgutils.nim +++ b/compiler/ccgutils.nim @@ -86,7 +86,7 @@ proc GetUniqueType*(key: PType): PType = if result == nil: gCanonicalTypes[k] = key result = key - of tyTypeDesc, tyTypeClass: + of tyTypeDesc, tyTypeClasses: InternalError("value expected, but got a type") of tyGenericParam: InternalError("GetUniqueType") diff --git a/compiler/cgen.nim b/compiler/cgen.nim index 910e675e1..6ccef5fde 100644 --- a/compiler/cgen.nim +++ b/compiler/cgen.nim @@ -72,6 +72,11 @@ proc isSimpleConst(typ: PType): bool = {tyTuple, tyObject, tyArray, tyArrayConstr, tySet, tySequence} and not (t.kind == tyProc and t.callConv == ccClosure) +proc useStringh(m: BModule) = + if not m.includesStringh: + m.includesStringh = true + discard lists.IncludeStr(m.headerFiles, "<string.h>") + proc useHeader(m: BModule, sym: PSym) = if lfHeader in sym.loc.Flags: assert(sym.annex != nil) @@ -284,6 +289,9 @@ proc genLineDir(p: BProc, t: PNode) = linefmt(p, cpsStmts, "nimln($1, $2);$n", line.toRope, t.info.quotedFilename) +proc postStmtActions(p: BProc) {.inline.} = + app(p.s(cpsStmts), p.module.injectStmt) + proc accessThreadLocalVar(p: BProc, s: PSym) proc emulatedThreadVars(): bool {.inline.} @@ -358,19 +366,24 @@ proc resetLoc(p: BProc, loc: var TLoc) = # field, so disabling this should be safe: genObjectInit(p, cpsStmts, loc.t, loc, true) else: + useStringh(p.module) linefmt(p, cpsStmts, "memset((void*)$1, 0, sizeof($2));$n", addrLoc(loc), rdLoc(loc)) # XXX: We can be extra clever here and call memset only # on the bytes following the m_type field? genObjectInit(p, cpsStmts, loc.t, loc, true) -proc constructLoc(p: BProc, loc: TLoc, section = cpsStmts) = +proc constructLoc(p: BProc, loc: TLoc, isTemp = false) = if not isComplexValueType(skipTypes(loc.t, abstractRange)): - linefmt(p, section, "$1 = 0;$n", rdLoc(loc)) + linefmt(p, cpsStmts, "$1 = 0;$n", rdLoc(loc)) else: - linefmt(p, section, "memset((void*)$1, 0, sizeof($2));$n", - addrLoc(loc), rdLoc(loc)) - genObjectInit(p, section, loc.t, loc, true) + if not isTemp or containsGarbageCollectedRef(loc.t): + # don't use memset for temporary values for performance if we can + # avoid it: + useStringh(p.module) + linefmt(p, cpsStmts, "memset((void*)$1, 0, sizeof($2));$n", + addrLoc(loc), rdLoc(loc)) + genObjectInit(p, cpsStmts, loc.t, loc, true) proc initLocalVar(p: BProc, v: PSym, immediateAsgn: bool) = if sfNoInit notin v.flags: @@ -396,7 +409,7 @@ proc getTemp(p: BProc, t: PType, result: var TLoc) = result.t = getUniqueType(t) result.s = OnStack result.flags = {} - constructLoc(p, result) + constructLoc(p, result, isTemp=true) proc keepAlive(p: BProc, toKeepAlive: TLoc) = when false: @@ -418,6 +431,7 @@ proc keepAlive(p: BProc, toKeepAlive: TLoc) = if not isComplexValueType(skipTypes(toKeepAlive.t, abstractVarRange)): linefmt(p, cpsStmts, "$1 = $2;$n", rdLoc(result), rdLoc(toKeepAlive)) else: + useStringh(p.module) linefmt(p, cpsStmts, "memcpy((void*)$1, (NIM_CONST void*)$2, sizeof($3));$n", addrLoc(result), addrLoc(toKeepAlive), rdLoc(result)) @@ -933,9 +947,9 @@ proc genMainProc(m: BModule) = const CommonMainBody = "\tsystemDatInit();$n" & + "\tsystemInit();$n" & "$1" & "$2" & - "\tsystemInit();$n" & "$3" & "$4" PosixNimMain = @@ -1108,6 +1122,9 @@ proc newPostInitProc(m: BModule): BProc = # little hack so that unique temporaries are generated: result.labels = 200_000 +proc initProcOptions(m: BModule): TOptions = + if sfSystemModule in m.module.flags: gOptions-{optStackTrace} else: gOptions + proc rawNewModule(module: PSym, filename: string): BModule = new(result) InitLinkedList(result.headerFiles) @@ -1120,7 +1137,7 @@ proc rawNewModule(module: PSym, filename: string): BModule = result.module = module result.typeInfoMarker = initIntSet() result.initProc = newProc(nil, result) - result.initProc.options = gOptions + result.initProc.options = initProcOptions(result) result.preInitProc = newPreInitProc(result) result.postInitProc = newPostInitProc(result) initNodeTable(result.dataCache) @@ -1128,7 +1145,12 @@ proc rawNewModule(module: PSym, filename: string): BModule = result.forwardedProcs = @[] result.typeNodesName = getTempName() result.nimTypesName = getTempName() - result.PreventStackTrace = sfSystemModule in module.flags + # no line tracing for the init sections of the system module so that we + # don't generate a TFrame which can confuse the stack botton initialization: + if sfSystemModule in module.flags: + result.PreventStackTrace = true + excl(result.preInitProc.options, optStackTrace) + excl(result.postInitProc.options, optStackTrace) proc nullify[T](arr: var T) = for i in low(arr)..high(arr): @@ -1141,7 +1163,7 @@ proc resetModule*(m: var BModule) = m.declaredProtos = initIntSet() initIdTable(m.forwTypeCache) m.initProc = newProc(nil, m) - m.initProc.options = gOptions + m.initProc.options = initProcOptions(m) m.preInitProc = newPreInitProc(m) m.postInitProc = newPostInitProc(m) initNodeTable(m.dataCache) @@ -1231,7 +1253,7 @@ proc myProcess(b: PPassContext, n: PNode): PNode = result = n if b == nil or passes.skipCodegen(n): return var m = BModule(b) - m.initProc.options = gOptions + m.initProc.options = initProcOptions(m) genStmts(m.initProc, n) proc finishModule(m: BModule) = @@ -1318,7 +1340,7 @@ proc myClose(b: PPassContext, n: PNode): PNode = if b == nil or passes.skipCodegen(n): return var m = BModule(b) if n != nil: - m.initProc.options = gOptions + m.initProc.options = initProcOptions(m) genStmts(m.initProc, n) # cached modules need to registered too: registerModuleToMain(m.module) diff --git a/compiler/cgendata.nim b/compiler/cgendata.nim index b5888d0f4..a803c0ba1 100644 --- a/compiler/cgendata.nim +++ b/compiler/cgendata.nim @@ -91,6 +91,7 @@ type FrameDeclared*: bool # hack for ROD support so that we don't declare # a frame var twice in an init proc isHeaderFile*: bool # C source file is the header file + includesStringh*: bool # C source file already includes ``<string.h>`` cfilename*: string # filename of the module (including path, # without extension) typeCache*: TIdTable # cache the generated types @@ -110,6 +111,7 @@ type labels*: natural # for generating unique module-scope names extensionLoaders*: array['0'..'9', PRope] # special procs for the # OpenGL wrapper + injectStmt*: PRope var mainModProcs*, mainModInit*, mainDatInit*: PRope # parts of the main module diff --git a/compiler/commands.nim b/compiler/commands.nim index d3266930b..fa7841275 100644 --- a/compiler/commands.nim +++ b/compiler/commands.nim @@ -249,10 +249,13 @@ proc processSwitch(switch, arg: string, pass: TCmdlinePass, info: TLineInfo) = expectArg(switch, arg, pass, info) addPath(processPath(arg), info) of "babelpath": - if pass in {passCmd2, passPP}: + if pass in {passCmd2, passPP} and not options.gNoBabelPath: expectArg(switch, arg, pass, info) let path = processPath(arg, notRelativeToProj=true) babelpath(path, info) + of "nobabelpath": + expectNoArg(switch, arg, pass, info) + options.gNoBabelPath = true of "excludepath": expectArg(switch, arg, pass, info) let path = processPath(arg) diff --git a/compiler/docgen.nim b/compiler/docgen.nim index 9929b4bd9..d44018a2b 100644 --- a/compiler/docgen.nim +++ b/compiler/docgen.nim @@ -11,10 +11,10 @@ # semantic checking is done for the code. Cross-references are generated # by knowing how the anchors are going to be named. -import - ast, strutils, strtabs, options, msgs, os, ropes, idents, - wordrecg, syntaxes, renderer, lexer, rstast, rst, rstgen, times, highlite, - importer, sempass2 +import + ast, strutils, strtabs, options, msgs, os, ropes, idents, + wordrecg, syntaxes, renderer, lexer, rstast, rst, rstgen, times, highlite, + importer, sempass2, json type TSections = array[TSymKind, PRope] @@ -25,7 +25,7 @@ type indexValFilename: string PDoc* = ref TDocumentor - + proc compilerMsgHandler(filename: string, line, col: int, msgKind: rst.TMsgKind, arg: string) {.procvar.} = # translate msg kind: @@ -41,69 +41,69 @@ proc compilerMsgHandler(filename: string, line, col: int, of mwUnknownSubstitution: k = warnUnknownSubstitutionX of mwUnsupportedLanguage: k = warnLanguageXNotSupported GlobalError(newLineInfo(filename, line, col), k, arg) - + proc parseRst(text, filename: string, line, column: int, hasToc: var bool, rstOptions: TRstParseOptions): PRstNode = result = rstParse(text, filename, line, column, hasToc, rstOptions, options.FindFile, compilerMsgHandler) -proc newDocumentor*(filename: string, config: PStringTable): PDoc = +proc newDocumentor*(filename: string, config: PStringTable): PDoc = new(result) initRstGenerator(result[], (if gCmd != cmdRst2Tex: outHtml else: outLatex), options.gConfigVars, filename, {roSupportRawDirective}, options.FindFile, compilerMsgHandler) result.id = 100 -proc dispA(dest: var PRope, xml, tex: string, args: openarray[PRope]) = +proc dispA(dest: var PRope, xml, tex: string, args: openarray[PRope]) = if gCmd != cmdRst2Tex: appf(dest, xml, args) else: appf(dest, tex, args) - -proc getVarIdx(varnames: openarray[string], id: string): int = - for i in countup(0, high(varnames)): - if cmpIgnoreStyle(varnames[i], id) == 0: + +proc getVarIdx(varnames: openarray[string], id: string): int = + for i in countup(0, high(varnames)): + if cmpIgnoreStyle(varnames[i], id) == 0: return i result = -1 -proc ropeFormatNamedVars(frmt: TFormatStr, varnames: openarray[string], - varvalues: openarray[PRope]): PRope = +proc ropeFormatNamedVars(frmt: TFormatStr, varnames: openarray[string], + varvalues: openarray[PRope]): PRope = var i = 0 var L = len(frmt) result = nil var num = 0 - while i < L: - if frmt[i] == '$': + while i < L: + if frmt[i] == '$': inc(i) # skip '$' case frmt[i] - of '#': + of '#': app(result, varvalues[num]) inc(num) inc(i) - of '$': + of '$': app(result, "$") inc(i) - of '0'..'9': + of '0'..'9': var j = 0 - while true: + while true: j = (j * 10) + Ord(frmt[i]) - ord('0') inc(i) - if (i > L + 0 - 1) or not (frmt[i] in {'0'..'9'}): break + if (i > L + 0 - 1) or not (frmt[i] in {'0'..'9'}): break if j > high(varvalues) + 1: internalError("ropeFormatNamedVars") num = j app(result, varvalues[j - 1]) - of 'A'..'Z', 'a'..'z', '\x80'..'\xFF': + of 'A'..'Z', 'a'..'z', '\x80'..'\xFF': var id = "" - while true: + while true: add(id, frmt[i]) inc(i) - if not (frmt[i] in {'A'..'Z', '_', 'a'..'z', '\x80'..'\xFF'}): break + if not (frmt[i] in {'A'..'Z', '_', 'a'..'z', '\x80'..'\xFF'}): break var idx = getVarIdx(varnames, id) if idx >= 0: app(result, varvalues[idx]) else: rawMessage(errUnkownSubstitionVar, id) - of '{': + of '{': var id = "" inc(i) - while frmt[i] != '}': + while frmt[i] != '}': if frmt[i] == '\0': rawMessage(errTokenExpected, "}") add(id, frmt[i]) inc(i) @@ -124,17 +124,17 @@ proc genComment(d: PDoc, n: PNode): string = var dummyHasToc: bool if n.comment != nil and startsWith(n.comment, "##"): renderRstToOut(d[], parseRst(n.comment, toFilename(n.info), - toLineNumber(n.info), toColumn(n.info), + toLineNumber(n.info), toColumn(n.info), dummyHasToc, d.options + {roSkipPounds}), result) -proc genRecComment(d: PDoc, n: PNode): PRope = +proc genRecComment(d: PDoc, n: PNode): PRope = if n == nil: return nil result = genComment(d, n).toRope - if result == nil: + if result == nil: if n.kind notin {nkEmpty..nkNilLit}: for i in countup(0, len(n)-1): result = genRecComment(d, n.sons[i]) - if result != nil: return + if result != nil: return else: n.comment = nil @@ -158,10 +158,10 @@ proc extractDocComment*(s: PSym, d: PDoc = nil): string = else: result = n.comment.substr(2).replace("\n##", "\n").strip -proc isVisible(n: PNode): bool = +proc isVisible(n: PNode): bool = result = false - if n.kind == nkPostfix: - if n.len == 2 and n.sons[0].kind == nkIdent: + if n.kind == nkPostfix: + if n.len == 2 and n.sons[0].kind == nkIdent: var v = n.sons[0].ident result = v.id == ord(wStar) or v.id == ord(wMinus) elif n.kind == nkSym: @@ -171,36 +171,36 @@ proc isVisible(n: PNode): bool = result = {sfExported, sfFromGeneric, sfForward}*n.sym.flags == {sfExported} elif n.kind == nkPragmaExpr: result = isVisible(n.sons[0]) - -proc getName(d: PDoc, n: PNode, splitAfter = -1): string = + +proc getName(d: PDoc, n: PNode, splitAfter = -1): string = case n.kind of nkPostfix: result = getName(d, n.sons[1], splitAfter) of nkPragmaExpr: result = getName(d, n.sons[0], splitAfter) of nkSym: result = esc(d.target, n.sym.renderDefinitionName, splitAfter) of nkIdent: result = esc(d.target, n.ident.s, splitAfter) - of nkAccQuoted: - result = esc(d.target, "`") + of nkAccQuoted: + result = esc(d.target, "`") for i in 0.. <n.len: result.add(getName(d, n[i], splitAfter)) result.add esc(d.target, "`") else: internalError(n.info, "getName()") result = "" -proc getRstName(n: PNode): PRstNode = +proc getRstName(n: PNode): PRstNode = case n.kind of nkPostfix: result = getRstName(n.sons[1]) of nkPragmaExpr: result = getRstName(n.sons[0]) of nkSym: result = newRstNode(rnLeaf, n.sym.renderDefinitionName) of nkIdent: result = newRstNode(rnLeaf, n.ident.s) - of nkAccQuoted: + of nkAccQuoted: result = getRstName(n.sons[0]) for i in 1 .. <n.len: result.text.add(getRstName(n[i]).text) else: internalError(n.info, "getRstName()") result = nil -proc genItem(d: PDoc, n, nameNode: PNode, k: TSymKind) = - if not isVisible(nameNode): return +proc genItem(d: PDoc, n, nameNode: PNode, k: TSymKind) = + if not isVisible(nameNode): return var name = toRope(getName(d, nameNode)) var result: PRope = nil var literal = "" @@ -208,73 +208,89 @@ proc genItem(d: PDoc, n, nameNode: PNode, k: TSymKind) = var comm = genRecComment(d, n) # call this here for the side-effect! var r: TSrcGen initTokRender(r, n, {renderNoBody, renderNoComments, renderDocComments}) - while true: + while true: getNextTok(r, kind, literal) case kind - of tkEof: - break - of tkComment: - dispA(result, "<span class=\"Comment\">$1</span>", "\\spanComment{$1}", + of tkEof: + break + of tkComment: + dispA(result, "<span class=\"Comment\">$1</span>", "\\spanComment{$1}", [toRope(esc(d.target, literal))]) - of tokKeywordLow..tokKeywordHigh: - dispA(result, "<span class=\"Keyword\">$1</span>", "\\spanKeyword{$1}", + of tokKeywordLow..tokKeywordHigh: + dispA(result, "<span class=\"Keyword\">$1</span>", "\\spanKeyword{$1}", [toRope(literal)]) - of tkOpr: - dispA(result, "<span class=\"Operator\">$1</span>", "\\spanOperator{$1}", + of tkOpr: + dispA(result, "<span class=\"Operator\">$1</span>", "\\spanOperator{$1}", [toRope(esc(d.target, literal))]) - of tkStrLit..tkTripleStrLit: - dispA(result, "<span class=\"StringLit\">$1</span>", + of tkStrLit..tkTripleStrLit: + dispA(result, "<span class=\"StringLit\">$1</span>", "\\spanStringLit{$1}", [toRope(esc(d.target, literal))]) - of tkCharLit: - dispA(result, "<span class=\"CharLit\">$1</span>", "\\spanCharLit{$1}", + of tkCharLit: + dispA(result, "<span class=\"CharLit\">$1</span>", "\\spanCharLit{$1}", [toRope(esc(d.target, literal))]) - of tkIntLit..tkUInt64Lit: - dispA(result, "<span class=\"DecNumber\">$1</span>", + of tkIntLit..tkUInt64Lit: + dispA(result, "<span class=\"DecNumber\">$1</span>", "\\spanDecNumber{$1}", [toRope(esc(d.target, literal))]) - of tkFloatLit..tkFloat128Lit: - dispA(result, "<span class=\"FloatNumber\">$1</span>", + of tkFloatLit..tkFloat128Lit: + dispA(result, "<span class=\"FloatNumber\">$1</span>", "\\spanFloatNumber{$1}", [toRope(esc(d.target, literal))]) - of tkSymbol: - dispA(result, "<span class=\"Identifier\">$1</span>", + of tkSymbol: + dispA(result, "<span class=\"Identifier\">$1</span>", "\\spanIdentifier{$1}", [toRope(esc(d.target, literal))]) - of tkSpaces, tkInvalid: + of tkSpaces, tkInvalid: app(result, literal) - of tkParLe, tkParRi, tkBracketLe, tkBracketRi, tkCurlyLe, tkCurlyRi, - tkBracketDotLe, tkBracketDotRi, tkCurlyDotLe, tkCurlyDotRi, tkParDotLe, - tkParDotRi, tkComma, tkSemiColon, tkColon, tkEquals, tkDot, tkDotDot, - tkAccent, tkColonColon, - tkGStrLit, tkGTripleStrLit, tkInfixOpr, tkPrefixOpr, tkPostfixOpr: - dispA(result, "<span class=\"Other\">$1</span>", "\\spanOther{$1}", + of tkParLe, tkParRi, tkBracketLe, tkBracketRi, tkCurlyLe, tkCurlyRi, + tkBracketDotLe, tkBracketDotRi, tkCurlyDotLe, tkCurlyDotRi, tkParDotLe, + tkParDotRi, tkComma, tkSemiColon, tkColon, tkEquals, tkDot, tkDotDot, + tkAccent, tkColonColon, + tkGStrLit, tkGTripleStrLit, tkInfixOpr, tkPrefixOpr, tkPostfixOpr: + dispA(result, "<span class=\"Other\">$1</span>", "\\spanOther{$1}", [toRope(esc(d.target, literal))]) inc(d.id) - app(d.section[k], ropeFormatNamedVars(getConfigVar("doc.item"), - ["name", "header", "desc", "itemID"], + app(d.section[k], ropeFormatNamedVars(getConfigVar("doc.item"), + ["name", "header", "desc", "itemID"], [name, result, comm, toRope(d.id)])) - app(d.toc[k], ropeFormatNamedVars(getConfigVar("doc.item.toc"), + app(d.toc[k], ropeFormatNamedVars(getConfigVar("doc.item.toc"), ["name", "header", "desc", "itemID"], [ toRope(getName(d, nameNode, d.splitAfter)), result, comm, toRope(d.id)])) setIndexTerm(d[], $d.id, getName(d, nameNode)) -proc checkForFalse(n: PNode): bool = +proc genJSONItem(d: PDoc, n, nameNode: PNode, k: TSymKind): PJsonNode = + if not isVisible(nameNode): return + var + name = getName(d, nameNode) + comm = genRecComment(d, n).ropeToStr() + r: TSrcGen + + initTokRender(r, n, {renderNoBody, renderNoComments, renderDocComments}) + + result = %{ "name": %name, "type": %($k) } + + if comm != nil and comm != "": + result["description"] = %comm + if r.buf != nil: + result["code"] = %r.buf + +proc checkForFalse(n: PNode): bool = result = n.kind == nkIdent and IdentEq(n.ident, "false") - -proc traceDeps(d: PDoc, n: PNode) = + +proc traceDeps(d: PDoc, n: PNode) = const k = skModule if d.section[k] != nil: app(d.section[k], ", ") - dispA(d.section[k], - "<a class=\"reference external\" href=\"$1.html\">$1</a>", + dispA(d.section[k], + "<a class=\"reference external\" href=\"$1.html\">$1</a>", "$1", [toRope(getModuleName(n))]) -proc generateDoc*(d: PDoc, n: PNode) = +proc generateDoc*(d: PDoc, n: PNode) = case n.kind of nkCommentStmt: app(d.modDesc, genComment(d, n)) - of nkProcDef: + of nkProcDef: when useEffectSystem: documentRaises(n) genItem(d, n, n.sons[namePos], skProc) of nkMethodDef: when useEffectSystem: documentRaises(n) genItem(d, n, n.sons[namePos], skMethod) - of nkIteratorDef: + of nkIteratorDef: when useEffectSystem: documentRaises(n) genItem(d, n, n.sons[namePos], skIterator) of nkMacroDef: genItem(d, n, n.sons[namePos], skMacro) @@ -284,27 +300,69 @@ proc generateDoc*(d: PDoc, n: PNode) = genItem(d, n, n.sons[namePos], skConverter) of nkTypeSection, nkVarSection, nkLetSection, nkConstSection: for i in countup(0, sonsLen(n) - 1): - if n.sons[i].kind != nkCommentStmt: + if n.sons[i].kind != nkCommentStmt: # order is always 'type var let const': - genItem(d, n.sons[i], n.sons[i].sons[0], + genItem(d, n.sons[i], n.sons[i].sons[0], succ(skType, ord(n.kind)-ord(nkTypeSection))) - of nkStmtList: + of nkStmtList: for i in countup(0, sonsLen(n) - 1): generateDoc(d, n.sons[i]) - of nkWhenStmt: + of nkWhenStmt: # generate documentation for the first branch only: if not checkForFalse(n.sons[0].sons[0]): generateDoc(d, lastSon(n.sons[0])) of nkImportStmt: - for i in 0 .. sonsLen(n)-1: traceDeps(d, n.sons[i]) + for i in 0 .. sonsLen(n)-1: traceDeps(d, n.sons[i]) of nkFromStmt, nkImportExceptStmt: traceDeps(d, n.sons[0]) else: nil -proc genSection(d: PDoc, kind: TSymKind) = +proc generateJson(d: PDoc, n: PNode, jArray: PJsonNode = nil): PJsonNode = + case n.kind + of nkCommentStmt: + if n.comment != nil and startsWith(n.comment, "##"): + let stripped = n.comment.substr(2).strip + result = %{ "comment": %stripped } + of nkProcDef: + when useEffectSystem: documentRaises(n) + result = genJSONItem(d, n, n.sons[namePos], skProc) + of nkMethodDef: + when useEffectSystem: documentRaises(n) + result = genJSONItem(d, n, n.sons[namePos], skMethod) + of nkIteratorDef: + when useEffectSystem: documentRaises(n) + result = genJSONItem(d, n, n.sons[namePos], skIterator) + of nkMacroDef: + result = genJSONItem(d, n, n.sons[namePos], skMacro) + of nkTemplateDef: + result = genJSONItem(d, n, n.sons[namePos], skTemplate) + of nkConverterDef: + when useEffectSystem: documentRaises(n) + result = genJSONItem(d, n, n.sons[namePos], skConverter) + of nkTypeSection, nkVarSection, nkLetSection, nkConstSection: + for i in countup(0, sonsLen(n) - 1): + if n.sons[i].kind != nkCommentStmt: + # order is always 'type var let const': + result = genJSONItem(d, n.sons[i], n.sons[i].sons[0], + succ(skType, ord(n.kind)-ord(nkTypeSection))) + of nkStmtList: + var elem = jArray + if elem == nil: elem = newJArray() + for i in countup(0, sonsLen(n) - 1): + var r = generateJson(d, n.sons[i], elem) + if r != nil: + elem.add(r) + if result == nil: result = elem + of nkWhenStmt: + # generate documentation for the first branch only: + if not checkForFalse(n.sons[0].sons[0]) and jArray != nil: + discard generateJson(d, lastSon(n.sons[0]), jArray) + else: nil + +proc genSection(d: PDoc, kind: TSymKind) = const sectionNames: array[skModule..skTemplate, string] = [ - "Imports", "Types", "Vars", "Lets", "Consts", "Vars", "Procs", "Methods", + "Imports", "Types", "Vars", "Lets", "Consts", "Vars", "Procs", "Methods", "Iterators", "Converters", "Macros", "Templates" ] - if d.section[kind] == nil: return + if d.section[kind] == nil: return var title = sectionNames[kind].toRope d.section[kind] = ropeFormatNamedVars(getConfigVar("doc.section"), [ "sectionid", "sectionTitle", "sectionTitleID", "content"], [ @@ -313,7 +371,7 @@ proc genSection(d: PDoc, kind: TSymKind) = "sectionid", "sectionTitle", "sectionTitleID", "content"], [ ord(kind).toRope, title, toRope(ord(kind) + 50), d.toc[kind]]) -proc genOutFile(d: PDoc): PRope = +proc genOutFile(d: PDoc): PRope = var code, content: PRope title = "" @@ -321,7 +379,7 @@ proc genOutFile(d: PDoc): PRope = var tmp = "" renderTocEntries(d[], j, 1, tmp) var toc = tmp.toRope - for i in countup(low(TSymKind), high(TSymKind)): + for i in countup(low(TSymKind), high(TSymKind)): genSection(d, i) app(toc, d.toc[i]) if toc != nil: @@ -329,30 +387,30 @@ proc genOutFile(d: PDoc): PRope = for i in countup(low(TSymKind), high(TSymKind)): app(code, d.section[i]) if d.meta[metaTitle].len != 0: title = d.meta[metaTitle] else: title = "Module " & extractFilename(changeFileExt(d.filename, "")) - + let bodyname = if d.hasToc: "doc.body_toc" else: "doc.body_no_toc" - content = ropeFormatNamedVars(getConfigVar(bodyname), ["title", + content = ropeFormatNamedVars(getConfigVar(bodyname), ["title", "tableofcontents", "moduledesc", "date", "time", "content"], - [title.toRope, toc, d.modDesc, toRope(getDateStr()), + [title.toRope, toc, d.modDesc, toRope(getDateStr()), toRope(getClockStr()), code]) - if optCompileOnly notin gGlobalOptions: + if optCompileOnly notin gGlobalOptions: # XXX what is this hack doing here? 'optCompileOnly' means raw output!? - code = ropeFormatNamedVars(getConfigVar("doc.file"), ["title", - "tableofcontents", "moduledesc", "date", "time", - "content", "author", "version"], - [title.toRope, toc, d.modDesc, toRope(getDateStr()), - toRope(getClockStr()), content, d.meta[metaAuthor].toRope, + code = ropeFormatNamedVars(getConfigVar("doc.file"), ["title", + "tableofcontents", "moduledesc", "date", "time", + "content", "author", "version"], + [title.toRope, toc, d.modDesc, toRope(getDateStr()), + toRope(getClockStr()), content, d.meta[metaAuthor].toRope, d.meta[metaVersion].toRope]) - else: + else: code = content result = code proc generateIndex*(d: PDoc) = if optGenIndex in gGlobalOptions: - writeIndexFile(d[], splitFile(options.outFile).dir / + writeIndexFile(d[], splitFile(options.outFile).dir / splitFile(d.filename).name & indexExt) -proc writeOutput*(d: PDoc, filename, outExt: string, useWarning = false) = +proc writeOutput*(d: PDoc, filename, outExt: string, useWarning = false) = var content = genOutFile(d) if optStdout in gGlobalOptions: writeRope(stdout, content) @@ -361,7 +419,7 @@ proc writeOutput*(d: PDoc, filename, outExt: string, useWarning = false) = proc CommandDoc*() = var ast = parseFile(gProjectMainIdx) - if ast == nil: return + if ast == nil: return var d = newDocumentor(gProjectFull, options.gConfigVars) d.hasToc = true generateDoc(d, ast) @@ -388,12 +446,26 @@ proc CommandRst2TeX*() = splitter = "\\-" CommandRstAux(gProjectFull, TexExt) +proc CommandJSON*() = + var ast = parseFile(gProjectMainIdx) + if ast == nil: return + var d = newDocumentor(gProjectFull, options.gConfigVars) + d.hasToc = true + var json = generateJson(d, ast) + var content = newRope(pretty(json)) + + if optStdout in gGlobalOptions: + writeRope(stdout, content) + else: + echo getOutFile(gProjectFull, JsonExt) + writeRope(content, getOutFile(gProjectFull, JsonExt), useWarning = false) + proc CommandBuildIndex*() = var content = mergeIndexes(gProjectFull).toRope - - let code = ropeFormatNamedVars(getConfigVar("doc.file"), ["title", - "tableofcontents", "moduledesc", "date", "time", - "content", "author", "version"], - ["Index".toRope, nil, nil, toRope(getDateStr()), + + let code = ropeFormatNamedVars(getConfigVar("doc.file"), ["title", + "tableofcontents", "moduledesc", "date", "time", + "content", "author", "version"], + ["Index".toRope, nil, nil, toRope(getDateStr()), toRope(getClockStr()), content, nil, nil]) writeRope(code, getOutFile("theindex", HtmlExt)) diff --git a/compiler/evals.nim b/compiler/evals.nim index 7e2c8a41d..b4ea973e8 100644 --- a/compiler/evals.nim +++ b/compiler/evals.nim @@ -550,9 +550,7 @@ proc evalSym(c: PEvalContext, n: PNode, flags: TEvalFlags): PNode = of skProc, skConverter, skMacro, skType: result = n #result = s.getBody - of skForVar: - result = evalGlobalVar(c, s, flags) - of skVar, skLet, skTemp, skResult: + of skVar, skLet, skForVar, skTemp, skResult: if sfGlobal notin s.flags: result = evalVariable(c.tos, s, flags) else: diff --git a/compiler/extccomp.nim b/compiler/extccomp.nim index 13eb972f6..bbbbbfb8e 100644 --- a/compiler/extccomp.nim +++ b/compiler/extccomp.nim @@ -463,9 +463,9 @@ proc getCompileOptions: string = proc getLinkOptions: string = result = linkOptions for linkedLib in items(cLinkedLibs): - result.add(cc[ccompiler].linkLibCmd % linkedLib.quoteIfContainsWhite) + result.add(cc[ccompiler].linkLibCmd % linkedLib.quoteShell) for libDir in items(cLibs): - result.add([cc[ccompiler].linkDirCmd, libDir.quoteIfContainsWhite]) + result.add([cc[ccompiler].linkDirCmd, libDir.quoteShell]) proc needsExeExt(): bool {.inline.} = result = (optGenScript in gGlobalOptions and targetOS == osWindows) or @@ -485,10 +485,10 @@ proc getCompileCFileCmd*(cfilename: string, isExternal = false): string = var includeCmd, compilePattern: string if not noAbsolutePaths(): # compute include paths: - includeCmd = cc[c].includeCmd & quoteIfContainsWhite(libpath) + includeCmd = cc[c].includeCmd & quoteShell(libpath) for includeDir in items(cIncludes): - includeCmd.add([cc[c].includeCmd, includeDir.quoteIfContainsWhite]) + includeCmd.add([cc[c].includeCmd, includeDir.quoteShell]) compilePattern = JoinPath(ccompilerpath, exe) else: @@ -501,17 +501,17 @@ proc getCompileCFileCmd*(cfilename: string, isExternal = false): string = toObjFile(cfile) else: completeCFilePath(toObjFile(cfile)) - cfile = quoteIfContainsWhite(AddFileExt(cfile, cExt)) - objfile = quoteIfContainsWhite(objfile) - result = quoteIfContainsWhite(compilePattern % [ + cfile = quoteShell(AddFileExt(cfile, cExt)) + objfile = quoteShell(objfile) + result = quoteShell(compilePattern % [ "file", cfile, "objfile", objfile, "options", options, "include", includeCmd, "nimrod", getPrefixDir(), "lib", libpath]) add(result, ' ') addf(result, cc[c].compileTmpl, [ "file", cfile, "objfile", objfile, "options", options, "include", includeCmd, - "nimrod", quoteIfContainsWhite(getPrefixDir()), - "lib", quoteIfContainsWhite(libpath)]) + "nimrod", quoteShell(getPrefixDir()), + "lib", quoteShell(libpath)]) proc footprint(filename: string): TCrc32 = result = crcFromFile(filename) >< @@ -590,7 +590,7 @@ proc CallCCompiler*(projectfile: string) = while it != nil: let objFile = if noAbsolutePaths(): it.data.extractFilename else: it.data add(objfiles, ' ') - add(objfiles, quoteIfContainsWhite( + add(objfiles, quoteShell( addFileExt(objFile, cc[ccompiler].objExt))) it = PStrEntry(it.next) @@ -602,8 +602,8 @@ proc CallCCompiler*(projectfile: string) = var linkerExe = getConfigVar(c, ".linkerexe") if len(linkerExe) == 0: linkerExe = cc[c].linkerExe if needsExeExt(): linkerExe = addFileExt(linkerExe, "exe") - if noAbsolutePaths(): linkCmd = quoteIfContainsWhite(linkerExe) - else: linkCmd = quoteIfContainsWhite(JoinPath(ccompilerpath, linkerExe)) + if noAbsolutePaths(): linkCmd = quoteShell(linkerExe) + else: linkCmd = quoteShell(JoinPath(ccompilerpath, linkerExe)) if optGenGuiApp in gGlobalOptions: buildGui = cc[c].buildGui else: buildGui = "" var exefile: string @@ -614,20 +614,21 @@ proc CallCCompiler*(projectfile: string) = exefile = splitFile(projectFile).name & platform.os[targetOS].exeExt buildDll = "" if options.outFile.len > 0: - exefile = options.outFile + exefile = options.outFile.expandTilde if not noAbsolutePaths(): - exefile = joinPath(splitFile(projectFile).dir, exefile) - exefile = quoteIfContainsWhite(exefile) + if not exeFile.isAbsolute(): + exefile = joinPath(splitFile(projectFile).dir, exefile) + exefile = quoteShell(exefile) let linkOptions = getLinkOptions() - linkCmd = quoteIfContainsWhite(linkCmd % ["builddll", builddll, + linkCmd = quoteShell(linkCmd % ["builddll", builddll, "buildgui", buildgui, "options", linkOptions, "objfiles", objfiles, "exefile", exefile, "nimrod", getPrefixDir(), "lib", libpath]) linkCmd.add ' ' addf(linkCmd, cc[c].linkTmpl, ["builddll", builddll, "buildgui", buildgui, "options", linkOptions, "objfiles", objfiles, "exefile", exefile, - "nimrod", quoteIfContainsWhite(getPrefixDir()), - "lib", quoteIfContainsWhite(libpath)]) + "nimrod", quoteShell(getPrefixDir()), + "lib", quoteShell(libpath)]) if optCompileOnly notin gGlobalOptions: execExternalProgram(linkCmd) else: linkCmd = "" diff --git a/compiler/jsgen.nim b/compiler/jsgen.nim index 54ee43069..a3c88824d 100644 --- a/compiler/jsgen.nim +++ b/compiler/jsgen.nim @@ -130,7 +130,7 @@ proc mapType(typ: PType): TJSTypeKind = result = etyObject of tyNil: result = etyNull of tyGenericInst, tyGenericParam, tyGenericBody, tyGenericInvokation, tyNone, - tyForward, tyEmpty, tyExpr, tyStmt, tyTypeDesc, tyTypeClass: + tyForward, tyEmpty, tyExpr, tyStmt, tyTypeDesc, tyTypeClasses: result = etyNone of tyProc: result = etyProc of tyCString: result = etyString diff --git a/compiler/lists.nim b/compiler/lists.nim index 67b32f919..22b1a183a 100644 --- a/compiler/lists.nim +++ b/compiler/lists.nim @@ -91,13 +91,17 @@ proc Remove*(list: var TLinkedList, entry: PListEntry) = if entry.prev != nil: entry.prev.next = entry.next proc bringToFront*(list: var TLinkedList, entry: PListEntry) = - if entry == list.head: return - if entry == list.tail: list.tail = entry.prev - if entry.next != nil: entry.next.prev = entry.prev - if entry.prev != nil: entry.prev.next = entry.next - entry.prev = nil - entry.next = list.head - list.head = entry + when true: + list.remove entry + list.prepend entry + else: + if entry == list.head: return + if entry == list.tail: list.tail = entry.prev + if entry.next != nil: entry.next.prev = entry.prev + if entry.prev != nil: entry.prev.next = entry.next + entry.prev = nil + entry.next = list.head + list.head = entry proc ExcludeStr*(list: var TLinkedList, data: string) = var it = list.head diff --git a/compiler/main.nim b/compiler/main.nim index 7cfc6d406..9ffe99454 100644 --- a/compiler/main.nim +++ b/compiler/main.nim @@ -9,8 +9,8 @@ # implements the command dispatcher and several commands -import - llstream, strutils, ast, astalgo, lexer, syntaxes, renderer, options, msgs, +import + llstream, strutils, ast, astalgo, lexer, syntaxes, renderer, options, msgs, os, condsyms, rodread, rodwrite, times, wordrecg, sem, semdata, idents, passes, docgen, extccomp, cgen, jsgen, json, nversion, @@ -98,7 +98,7 @@ proc CommandCompileToC = # rodread.rodcompilerProcs # rodread.gTypeTable # rodread.gMods - + # !! ropes.cache # semthreads.computed? # @@ -166,7 +166,7 @@ proc commandEval(exp: string) = proc CommandPrettyOld = var projectFile = addFileExt(mainCommandArg(), NimExt) var module = parseFile(projectFile.fileInfoIdx) - if module != nil: + if module != nil: renderModule(module, getOutFile(mainCommandArg(), "pretty." & NimExt)) proc CommandPretty = @@ -175,24 +175,24 @@ proc CommandPretty = registerPass(prettyPass) compileProject() pretty.overwriteFiles() - + proc CommandScan = var f = addFileExt(mainCommandArg(), nimExt) var stream = LLStreamOpen(f, fmRead) - if stream != nil: - var + if stream != nil: + var L: TLexer tok: TToken initToken(tok) openLexer(L, f, stream) - while true: + while true: rawGetTok(L, tok) PrintTok(tok) - if tok.tokType == tkEof: break + if tok.tokType == tkEof: break CloseLexer(L) - else: + else: rawMessage(errCannotOpenFile, f) - + proc CommandSuggest = if isServing: # XXX: hacky work-around ahead @@ -246,7 +246,7 @@ proc resetMemory = for i in low(buckets)..high(buckets): buckets[i] = nil idAnon = nil - + # XXX: clean these global vars # ccgstmts.gBreakpoints # ccgthreadvars.nimtv @@ -262,7 +262,7 @@ proc resetMemory = # rodread.rodcompilerProcs # rodread.gTypeTable # rodread.gMods - + # !! ropes.cache # semthreads.computed? # @@ -289,7 +289,7 @@ const proc MainCommand* = when SimiluateCaasMemReset: gGlobalOptions.incl(optCaasEnabled) - + # In "nimrod serve" scenario, each command must reset the registered passes clearPasses() gLastCmdTime = epochTime() @@ -301,7 +301,7 @@ proc MainCommand* = passes.gIncludeFile = includeModule passes.gImportModule = importModule case command.normalize - of "c", "cc", "compile", "compiletoc": + of "c", "cc", "compile", "compiletoc": # compile means compileToC currently gCmd = cmdCompileToC wantMainModule() @@ -325,13 +325,13 @@ proc MainCommand* = when hasTinyCBackend: extccomp.setCC("tcc") CommandCompileToC() - else: + else: rawMessage(errInvalidCommandX, command) - of "js", "compiletojs": + of "js", "compiletojs": gCmd = cmdCompileToJS wantMainModule() CommandCompileToJS() - of "compiletollvm": + of "compiletollvm": gCmd = cmdCompileToLLVM wantMainModule() when has_LLVM_Backend: @@ -353,21 +353,27 @@ proc MainCommand* = wantMainModule() DefineSymbol("nimdoc") CommandDoc2() - of "rst2html": + of "rst2html": gCmd = cmdRst2html LoadConfigs(DocConfig) wantMainModule() CommandRst2Html() - of "rst2tex": + of "rst2tex": gCmd = cmdRst2tex LoadConfigs(DocTexConfig) wantMainModule() CommandRst2TeX() + of "jsondoc": + gCmd = cmdDoc + LoadConfigs(DocConfig) + wantMainModule() + DefineSymbol("nimdoc") + CommandJSON() of "buildindex": gCmd = cmdDoc LoadConfigs(DocConfig) CommandBuildIndex() - of "gendepend": + of "gendepend": gCmd = cmdGenDepend wantMainModule() CommandGenDepend() @@ -400,16 +406,16 @@ proc MainCommand* = gCmd = cmdCheck wantMainModule() CommandCheck() - of "parse": + of "parse": gCmd = cmdParse wantMainModule() discard parseFile(gProjectMainIdx) - of "scan": + of "scan": gCmd = cmdScan wantMainModule() CommandScan() MsgWriteln("Beware: Indentation tokens depend on the parser\'s state!") - of "i": + of "i": gCmd = cmdInteractive CommandInteractive() of "e": @@ -427,12 +433,14 @@ proc MainCommand* = of "serve": isServing = true gGlobalOptions.incl(optCaasEnabled) - msgs.gErrorMax = high(int) # do not stop after first error + msgs.gErrorMax = high(int) # do not stop after first error serve(MainCommand) else: rawMessage(errInvalidCommandX, command) - - if msgs.gErrorCounter == 0 and gCmd notin {cmdInterpret, cmdRun, cmdDump}: + + if (msgs.gErrorCounter == 0 and + gCmd notin {cmdInterpret, cmdRun, cmdDump} and + gVerbosity > 0): rawMessage(hintSuccessX, [$gLinesCompiled, formatFloat(epochTime() - gLastCmdTime, ffDecimal, 3), formatSize(getTotalMem())]) diff --git a/compiler/nimconf.nim b/compiler/nimconf.nim index 507812d9c..7ec566a01 100644 --- a/compiler/nimconf.nim +++ b/compiler/nimconf.nim @@ -243,11 +243,6 @@ proc LoadConfigs*(cfg: string) = readConfigFile(pd / cfg) if gProjectName.len != 0: - var conffile = changeFileExt(gProjectFull, "cfg") - if conffile != pd / cfg and existsFile(conffile): - readConfigFile(conffile) - rawMessage(warnConfigDeprecated, conffile) - # new project wide config file: readConfigFile(changeFileExt(gProjectFull, "nimrod.cfg")) diff --git a/compiler/nimrod.dot b/compiler/nimrod.dot deleted file mode 100644 index e9663d7c5..000000000 --- a/compiler/nimrod.dot +++ /dev/null @@ -1,591 +0,0 @@ -digraph nimrod { -times -> strutils; -os -> strutils; -os -> times; -posix -> times; -os -> posix; -nhashes -> strutils; -nstrtabs -> os; -nstrtabs -> nhashes; -nstrtabs -> strutils; -options -> os; -options -> lists; -options -> strutils; -options -> nstrtabs; -msgs -> options; -msgs -> strutils; -msgs -> os; -crc -> strutils; -platform -> strutils; -ropes -> msgs; -ropes -> strutils; -ropes -> platform; -ropes -> nhashes; -ropes -> crc; -idents -> nhashes; -idents -> strutils; -ast -> msgs; -ast -> nhashes; -ast -> nversion; -ast -> options; -ast -> strutils; -ast -> crc; -ast -> ropes; -ast -> idents; -ast -> lists; -astalgo -> ast; -astalgo -> nhashes; -astalgo -> strutils; -astalgo -> options; -astalgo -> msgs; -astalgo -> ropes; -astalgo -> idents; -condsyms -> ast; -condsyms -> astalgo; -condsyms -> msgs; -condsyms -> nhashes; -condsyms -> platform; -condsyms -> strutils; -condsyms -> idents; -hashes -> strutils; -strtabs -> os; -strtabs -> hashes; -strtabs -> strutils; -osproc -> strutils; -osproc -> os; -osproc -> strtabs; -osproc -> streams; -osproc -> posix; -extccomp -> lists; -extccomp -> ropes; -extccomp -> os; -extccomp -> strutils; -extccomp -> osproc; -extccomp -> platform; -extccomp -> condsyms; -extccomp -> options; -extccomp -> msgs; -wordrecg -> nhashes; -wordrecg -> strutils; -wordrecg -> idents; -commands -> os; -commands -> msgs; -commands -> options; -commands -> nversion; -commands -> condsyms; -commands -> strutils; -commands -> extccomp; -commands -> platform; -commands -> lists; -commands -> wordrecg; -llstream -> strutils; -lexbase -> llstream; -lexbase -> strutils; -scanner -> nhashes; -scanner -> options; -scanner -> msgs; -scanner -> strutils; -scanner -> platform; -scanner -> idents; -scanner -> lexbase; -scanner -> llstream; -scanner -> wordrecg; -nimconf -> llstream; -nimconf -> nversion; -nimconf -> commands; -nimconf -> os; -nimconf -> strutils; -nimconf -> msgs; -nimconf -> platform; -nimconf -> condsyms; -nimconf -> scanner; -nimconf -> options; -nimconf -> idents; -nimconf -> wordrecg; -pnimsyn -> llstream; -pnimsyn -> scanner; -pnimsyn -> idents; -pnimsyn -> strutils; -pnimsyn -> ast; -pnimsyn -> msgs; -pbraces -> llstream; -pbraces -> scanner; -pbraces -> idents; -pbraces -> strutils; -pbraces -> ast; -pbraces -> msgs; -pbraces -> pnimsyn; -rnimsyn -> scanner; -rnimsyn -> options; -rnimsyn -> idents; -rnimsyn -> strutils; -rnimsyn -> ast; -rnimsyn -> msgs; -rnimsyn -> lists; -filters -> llstream; -filters -> os; -filters -> wordrecg; -filters -> idents; -filters -> strutils; -filters -> ast; -filters -> astalgo; -filters -> msgs; -filters -> options; -filters -> rnimsyn; -ptmplsyn -> llstream; -ptmplsyn -> os; -ptmplsyn -> wordrecg; -ptmplsyn -> idents; -ptmplsyn -> strutils; -ptmplsyn -> ast; -ptmplsyn -> astalgo; -ptmplsyn -> msgs; -ptmplsyn -> options; -ptmplsyn -> rnimsyn; -ptmplsyn -> filters; -syntaxes -> strutils; -syntaxes -> llstream; -syntaxes -> ast; -syntaxes -> astalgo; -syntaxes -> idents; -syntaxes -> scanner; -syntaxes -> options; -syntaxes -> msgs; -syntaxes -> pnimsyn; -syntaxes -> pbraces; -syntaxes -> ptmplsyn; -syntaxes -> filters; -syntaxes -> rnimsyn; -paslex -> nhashes; -paslex -> options; -paslex -> msgs; -paslex -> strutils; -paslex -> platform; -paslex -> idents; -paslex -> lexbase; -paslex -> wordrecg; -paslex -> scanner; -pasparse -> os; -pasparse -> llstream; -pasparse -> scanner; -pasparse -> paslex; -pasparse -> idents; -pasparse -> wordrecg; -pasparse -> strutils; -pasparse -> ast; -pasparse -> astalgo; -pasparse -> msgs; -pasparse -> options; -rodread -> os; -rodread -> options; -rodread -> strutils; -rodread -> nversion; -rodread -> ast; -rodread -> astalgo; -rodread -> msgs; -rodread -> platform; -rodread -> condsyms; -rodread -> ropes; -rodread -> idents; -rodread -> crc; -trees -> ast; -trees -> astalgo; -trees -> scanner; -trees -> msgs; -trees -> strutils; -types -> ast; -types -> astalgo; -types -> trees; -types -> msgs; -types -> strutils; -types -> platform; -magicsys -> ast; -magicsys -> astalgo; -magicsys -> nhashes; -magicsys -> msgs; -magicsys -> platform; -magicsys -> nversion; -magicsys -> times; -magicsys -> idents; -magicsys -> rodread; -nimsets -> ast; -nimsets -> astalgo; -nimsets -> trees; -nimsets -> nversion; -nimsets -> msgs; -nimsets -> platform; -nimsets -> bitsets; -nimsets -> types; -nimsets -> rnimsyn; -passes -> strutils; -passes -> lists; -passes -> options; -passes -> ast; -passes -> astalgo; -passes -> llstream; -passes -> msgs; -passes -> platform; -passes -> os; -passes -> condsyms; -passes -> idents; -passes -> rnimsyn; -passes -> types; -passes -> extccomp; -passes -> math; -passes -> magicsys; -passes -> nversion; -passes -> nimsets; -passes -> syntaxes; -passes -> times; -passes -> rodread; -treetab -> nhashes; -treetab -> ast; -treetab -> astalgo; -treetab -> types; -semdata -> strutils; -semdata -> lists; -semdata -> options; -semdata -> scanner; -semdata -> ast; -semdata -> astalgo; -semdata -> trees; -semdata -> treetab; -semdata -> wordrecg; -semdata -> ropes; -semdata -> msgs; -semdata -> platform; -semdata -> os; -semdata -> condsyms; -semdata -> idents; -semdata -> rnimsyn; -semdata -> types; -semdata -> extccomp; -semdata -> math; -semdata -> magicsys; -semdata -> nversion; -semdata -> nimsets; -semdata -> pnimsyn; -semdata -> times; -semdata -> passes; -semdata -> rodread; -lookups -> ast; -lookups -> astalgo; -lookups -> idents; -lookups -> semdata; -lookups -> types; -lookups -> msgs; -lookups -> options; -lookups -> rodread; -lookups -> rnimsyn; -importer -> strutils; -importer -> os; -importer -> ast; -importer -> astalgo; -importer -> msgs; -importer -> options; -importer -> idents; -importer -> rodread; -importer -> lookups; -importer -> semdata; -importer -> passes; -rodwrite -> os; -rodwrite -> options; -rodwrite -> strutils; -rodwrite -> nversion; -rodwrite -> ast; -rodwrite -> astalgo; -rodwrite -> msgs; -rodwrite -> platform; -rodwrite -> condsyms; -rodwrite -> ropes; -rodwrite -> idents; -rodwrite -> crc; -rodwrite -> rodread; -rodwrite -> passes; -rodwrite -> importer; -semfold -> strutils; -semfold -> lists; -semfold -> options; -semfold -> ast; -semfold -> astalgo; -semfold -> trees; -semfold -> treetab; -semfold -> nimsets; -semfold -> times; -semfold -> nversion; -semfold -> platform; -semfold -> math; -semfold -> msgs; -semfold -> os; -semfold -> condsyms; -semfold -> idents; -semfold -> rnimsyn; -semfold -> types; -evals -> strutils; -evals -> magicsys; -evals -> lists; -evals -> options; -evals -> ast; -evals -> astalgo; -evals -> trees; -evals -> treetab; -evals -> nimsets; -evals -> msgs; -evals -> os; -evals -> condsyms; -evals -> idents; -evals -> rnimsyn; -evals -> types; -evals -> passes; -evals -> semfold; -procfind -> ast; -procfind -> astalgo; -procfind -> msgs; -procfind -> semdata; -procfind -> types; -procfind -> trees; -pragmas -> os; -pragmas -> platform; -pragmas -> condsyms; -pragmas -> ast; -pragmas -> astalgo; -pragmas -> idents; -pragmas -> semdata; -pragmas -> msgs; -pragmas -> rnimsyn; -pragmas -> wordrecg; -pragmas -> ropes; -pragmas -> options; -pragmas -> strutils; -pragmas -> lists; -pragmas -> extccomp; -pragmas -> math; -pragmas -> magicsys; -pragmas -> trees; -sem -> strutils; -sem -> nhashes; -sem -> lists; -sem -> options; -sem -> scanner; -sem -> ast; -sem -> astalgo; -sem -> trees; -sem -> treetab; -sem -> wordrecg; -sem -> ropes; -sem -> msgs; -sem -> os; -sem -> condsyms; -sem -> idents; -sem -> rnimsyn; -sem -> types; -sem -> platform; -sem -> math; -sem -> magicsys; -sem -> pnimsyn; -sem -> nversion; -sem -> nimsets; -sem -> semdata; -sem -> evals; -sem -> semfold; -sem -> importer; -sem -> procfind; -sem -> lookups; -sem -> rodread; -sem -> pragmas; -sem -> passes; -rst -> os; -rst -> msgs; -rst -> strutils; -rst -> platform; -rst -> nhashes; -rst -> ropes; -rst -> options; -highlite -> nhashes; -highlite -> options; -highlite -> msgs; -highlite -> strutils; -highlite -> platform; -highlite -> idents; -highlite -> lexbase; -highlite -> wordrecg; -highlite -> scanner; -docgen -> ast; -docgen -> astalgo; -docgen -> strutils; -docgen -> nhashes; -docgen -> options; -docgen -> nversion; -docgen -> msgs; -docgen -> os; -docgen -> ropes; -docgen -> idents; -docgen -> wordrecg; -docgen -> math; -docgen -> syntaxes; -docgen -> rnimsyn; -docgen -> scanner; -docgen -> rst; -docgen -> times; -docgen -> highlite; -ccgutils -> ast; -ccgutils -> astalgo; -ccgutils -> ropes; -ccgutils -> lists; -ccgutils -> nhashes; -ccgutils -> strutils; -ccgutils -> types; -ccgutils -> msgs; -cgmeth -> options; -cgmeth -> ast; -cgmeth -> astalgo; -cgmeth -> msgs; -cgmeth -> idents; -cgmeth -> rnimsyn; -cgmeth -> types; -cgmeth -> magicsys; -cgen -> ast; -cgen -> astalgo; -cgen -> strutils; -cgen -> nhashes; -cgen -> trees; -cgen -> platform; -cgen -> magicsys; -cgen -> extccomp; -cgen -> options; -cgen -> nversion; -cgen -> nimsets; -cgen -> msgs; -cgen -> crc; -cgen -> bitsets; -cgen -> idents; -cgen -> lists; -cgen -> types; -cgen -> ccgutils; -cgen -> os; -cgen -> times; -cgen -> ropes; -cgen -> math; -cgen -> passes; -cgen -> rodread; -cgen -> wordrecg; -cgen -> rnimsyn; -cgen -> treetab; -cgen -> cgmeth; -jsgen -> ast; -jsgen -> astalgo; -jsgen -> strutils; -jsgen -> nhashes; -jsgen -> trees; -jsgen -> platform; -jsgen -> magicsys; -jsgen -> extccomp; -jsgen -> options; -jsgen -> nversion; -jsgen -> nimsets; -jsgen -> msgs; -jsgen -> crc; -jsgen -> bitsets; -jsgen -> idents; -jsgen -> lists; -jsgen -> types; -jsgen -> os; -jsgen -> times; -jsgen -> ropes; -jsgen -> math; -jsgen -> passes; -jsgen -> ccgutils; -jsgen -> wordrecg; -jsgen -> rnimsyn; -jsgen -> rodread; -interact -> llstream; -interact -> strutils; -interact -> ropes; -interact -> nstrtabs; -interact -> msgs; -passaux -> strutils; -passaux -> ast; -passaux -> astalgo; -passaux -> passes; -passaux -> msgs; -passaux -> options; -depends -> os; -depends -> options; -depends -> ast; -depends -> astalgo; -depends -> msgs; -depends -> ropes; -depends -> idents; -depends -> passes; -depends -> importer; -transf -> strutils; -transf -> lists; -transf -> options; -transf -> ast; -transf -> astalgo; -transf -> trees; -transf -> treetab; -transf -> evals; -transf -> msgs; -transf -> os; -transf -> idents; -transf -> rnimsyn; -transf -> types; -transf -> passes; -transf -> semfold; -transf -> magicsys; -transf -> cgmeth; -main -> llstream; -main -> strutils; -main -> ast; -main -> astalgo; -main -> scanner; -main -> syntaxes; -main -> rnimsyn; -main -> options; -main -> msgs; -main -> os; -main -> lists; -main -> condsyms; -main -> paslex; -main -> pasparse; -main -> rodread; -main -> rodwrite; -main -> ropes; -main -> trees; -main -> wordrecg; -main -> sem; -main -> semdata; -main -> idents; -main -> passes; -main -> docgen; -main -> extccomp; -main -> cgen; -main -> jsgen; -main -> platform; -main -> interact; -main -> nimconf; -main -> importer; -main -> passaux; -main -> depends; -main -> transf; -main -> evals; -main -> types; -parseopt -> os; -parseopt -> strutils; -nimrod -> times; -nimrod -> commands; -nimrod -> scanner; -nimrod -> condsyms; -nimrod -> options; -nimrod -> msgs; -nimrod -> nversion; -nimrod -> nimconf; -nimrod -> ropes; -nimrod -> extccomp; -nimrod -> strutils; -nimrod -> os; -nimrod -> platform; -nimrod -> main; -nimrod -> parseopt; -} diff --git a/compiler/nimrod.nim b/compiler/nimrod.nim index 3fa80cebb..2bc94e3f8 100644 --- a/compiler/nimrod.nim +++ b/compiler/nimrod.nim @@ -13,9 +13,9 @@ when defined(gcc) and defined(windows): else: {.link: "icons/nimrod_icon.o".} -import - commands, lexer, condsyms, options, msgs, nversion, nimconf, ropes, - extccomp, strutils, os, platform, main, parseopt, service +import + commands, lexer, condsyms, options, msgs, nversion, nimconf, ropes, + extccomp, strutils, os, osproc, platform, main, parseopt, service when hasTinyCBackend: import tccgen @@ -23,7 +23,7 @@ when hasTinyCBackend: when defined(profiler) or defined(memProfiler): {.hint: "Profiling support is turned on!".} import nimprof - + proc prependCurDir(f: string): string = when defined(unix): if os.isAbsolute(f): result = f @@ -61,12 +61,18 @@ proc HandleCmdLine() = tccgen.run() if optRun in gGlobalOptions: if gCmd == cmdCompileToJS: - var ex = quoteIfContainsWhite( + var ex = quoteShell( completeCFilePath(changeFileExt(gProjectFull, "js").prependCurDir)) execExternalProgram("node " & ex & ' ' & service.arguments) else: - var ex = quoteIfContainsWhite( - changeFileExt(gProjectFull, exeExt).prependCurDir) + var binPath: string + if options.outFile.len > 0: + # If the user specified an outFile path, use that directly. + binPath = options.outFile.prependCurDir + else: + # Figure out ourselves a valid binary name. + binPath = changeFileExt(gProjectFull, exeExt).prependCurDir + var ex = quoteShell(binPath) execExternalProgram(ex & ' ' & service.arguments) when defined(GC_setMaxPause): @@ -76,5 +82,7 @@ when compileOption("gc", "v2") or compileOption("gc", "refc"): # the new correct mark&sweet collector is too slow :-/ GC_disableMarkAndSweep() condsyms.InitDefines() -HandleCmdLine() -quit(int8(msgs.gErrorCounter > 0)) + +when not defined(selftest): + HandleCmdLine() + quit(int8(msgs.gErrorCounter > 0)) diff --git a/compiler/nimrod.cfg b/compiler/nimrod.nimrod.cfg index ac8f732f1..9fa1b8cba 100644 --- a/compiler/nimrod.cfg +++ b/compiler/nimrod.nimrod.cfg @@ -11,4 +11,5 @@ path:"$projectPath/.." path:"$lib/packages/docutils" define:booting +import:testability diff --git a/compiler/options.nim b/compiler/options.nim index ea6b91321..c62744485 100644 --- a/compiler/options.nim +++ b/compiler/options.nim @@ -111,6 +111,7 @@ var gDirtyBufferIdx* = 0'i32 # indicates the fileIdx of the dirty version of # the tracked source X, saved by the CAAS client. gDirtyOriginalIdx* = 0'i32 # the original source file of the dirtified buffer. + gNoBabelPath* = false proc importantComments*(): bool {.inline.} = gCmd in {cmdDoc, cmdIdeTools} proc usesNativeGC*(): bool {.inline.} = gSelectedGC >= gcRefc @@ -132,6 +133,7 @@ const NimExt* = "nim" RodExt* = "rod" HtmlExt* = "html" + JsonExt* = "json" TexExt* = "tex" IniExt* = "ini" DefaultConfig* = "nimrod.cfg" diff --git a/compiler/pragmas.nim b/compiler/pragmas.nim index 8c2425de3..6f1e7af25 100644 --- a/compiler/pragmas.nim +++ b/compiler/pragmas.nim @@ -43,7 +43,8 @@ const wFatal, wDefine, wUndef, wCompile, wLink, wLinkSys, wPure, wPush, wPop, wBreakpoint, wWatchpoint, wPassL, wPassC, wDeadCodeElim, wDeprecated, wFloatChecks, wInfChecks, wNanChecks, wPragma, wEmit, wUnroll, - wLinearScanEnd, wPatterns, wEffects, wNoForward, wComputedGoto} + wLinearScanEnd, wPatterns, wEffects, wNoForward, wComputedGoto, + wInjectStmt} lambdaPragmas* = {FirstCallConv..LastCallConv, wImportc, wExportc, wNodecl, wNosideEffect, wSideEffect, wNoreturn, wDynLib, wHeader, wDeprecated, wExtern, wThread, wImportcpp, wImportobjc, wNoStackFrame, @@ -722,6 +723,11 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: int, of wOperator: if sym == nil: invalidPragma(it) else: sym.position = expectIntLit(c, it) + of wInjectStmt: + if it.kind != nkExprColonExpr: + localError(it.info, errExprExpected) + else: + it.sons[1] = c.semExpr(c, it.sons[1]) else: invalidPragma(it) else: invalidPragma(it) else: processNote(c, it) diff --git a/compiler/renderer.nim b/compiler/renderer.nim index f6fb0f8c0..61babed66 100644 --- a/compiler/renderer.nim +++ b/compiler/renderer.nim @@ -421,10 +421,11 @@ proc lsub(n: PNode): int = of nkElifExpr: result = lsons(n) + len("_elif_:_") of nkElseExpr: result = lsub(n.sons[0]) + len("_else:_") # type descriptions of nkTypeOfExpr: result = lsub(n.sons[0]) + len("type_") - of nkRefTy: result = lsub(n.sons[0]) + len("ref_") - of nkPtrTy: result = lsub(n.sons[0]) + len("ptr_") - of nkVarTy: result = lsub(n.sons[0]) + len("var_") - of nkDistinctTy: result = lsub(n.sons[0]) + len("Distinct_") + of nkRefTy: result = (if n.len > 0: lsub(n.sons[0])+1 else: 0) + len("ref") + of nkPtrTy: result = (if n.len > 0: lsub(n.sons[0])+1 else: 0) + len("ptr") + 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) + + len("Distinct") of nkTypeDef: result = lsons(n) + 3 of nkOfInherit: result = lsub(n.sons[0]) + len("of_") of nkProcTy: result = lsons(n) + len("proc_") diff --git a/compiler/ropes.nim b/compiler/ropes.nim index 707c29123..ce81aae37 100644 --- a/compiler/ropes.nim +++ b/compiler/ropes.nim @@ -283,11 +283,10 @@ proc ropef(frmt: TFormatStr, args: varargs[PRope]): PRope = assert(RopeInvariant(result)) {.push stack_trace: off, line_trace: off.} -proc `~`*(r: expr[string]): PRope = +proc `~`*(r: string): PRope = # this is the new optimized "to rope" operator # the mnemonic is that `~` looks a bit like a rope :) - var r {.global.} = r.ropef - return r + return r.ropef {.pop.} proc appf(c: var PRope, frmt: TFormatStr, args: varargs[PRope]) = diff --git a/compiler/sem.nim b/compiler/sem.nim index ea53afbeb..3ace623bc 100644 --- a/compiler/sem.nim +++ b/compiler/sem.nim @@ -90,6 +90,24 @@ proc commonType*(x, y: PType): PType = let idx = ord(b.kind in {tyArray, tyArrayConstr}) if a.sons[idx].kind == tyEmpty: return y #elif b.sons[idx].kind == tyEmpty: return x + elif a.kind == tyRange and b.kind == tyRange: + # consider: (range[0..3], range[0..4]) here. We should make that + # range[0..4]. But then why is (range[0..4], 6) not range[0..6]? + # But then why is (2,4) not range[2..4]? But I think this would break + # too much code. So ... it's the same range or the base type. This means + # type(if b: 0 else 1) == int and not range[0..1]. For now. In the long + # run people expect ranges to work properly within a tuple. + if not sameType(a, b): + result = skipTypes(a, {tyRange}).skipIntLit + when false: + if a.kind != tyRange and b.kind == tyRange: + # XXX This really needs a better solution, but a proper fix now breaks + # code. + result = a #.skipIntLit + elif a.kind == tyRange and b.kind != tyRange: + result = b #.skipIntLit + elif a.kind in IntegralTypes and a.n != nil: + result = a #.skipIntLit else: var k = tyNone if a.kind in {tyRef, tyPtr}: @@ -103,7 +121,7 @@ proc commonType*(x, y: PType): PType = if result.isNil: return x if k != tyNone: let r = result - result = NewType(k, r.owner) + result = newType(k, r.owner) result.addSonSkipIntLit(r) proc isTopLevel(c: PContext): bool {.inline.} = @@ -140,26 +158,27 @@ proc IsOpImpl(c: PContext, n: PNode): PNode proc semMacroExpr(c: PContext, n, nOrig: PNode, sym: PSym, semCheck: bool = true): PNode -proc symFromType(t: PType, info: TLineInfo): PSym = - if t.sym != nil: return t.sym - result = newSym(skType, getIdent"AnonType", t.owner, info) - result.flags.incl sfAnon - result.typ = t +when false: + proc symFromType(t: PType, info: TLineInfo): PSym = + if t.sym != nil: return t.sym + result = newSym(skType, getIdent"AnonType", t.owner, info) + result.flags.incl sfAnon + result.typ = t -proc symNodeFromType(c: PContext, t: PType, info: TLineInfo): PNode = - result = newSymNode(symFromType(t, info), info) - result.typ = makeTypeDesc(c, t) + proc symNodeFromType(c: PContext, t: PType, info: TLineInfo): PNode = + result = newSymNode(symFromType(t, info), info) + result.typ = makeTypeDesc(c, t) proc createEvalContext(c: PContext, mode: TEvalMode): PEvalContext = result = newEvalContext(c.module, mode) result.getType = proc (n: PNode): PNode = - var e = tryExpr(c, n) - if e == nil: - result = symNodeFromType(c, errorType(c), n.info) - elif e.typ == nil: + result = tryExpr(c, n) + if result == nil: + result = newSymNode(errorSym(c, n)) + elif result.typ == nil: result = newSymNode(getSysSym"void") else: - result = symNodeFromType(c, e.typ, n.info) + result.typ = makeTypeDesc(c, result.typ) result.handleIsOperator = proc (n: PNode): PNode = result = IsOpImpl(c, n) @@ -210,7 +229,8 @@ proc semAfterMacroCall(c: PContext, n: PNode, s: PSym): PNode = of tyTypeDesc: if n.kind == nkStmtList: result.kind = nkStmtListType var typ = semTypeNode(c, result, nil) - result = symNodeFromType(c, typ, n.info) + result.typ = makeTypeDesc(c, typ) + #result = symNodeFromType(c, typ, n.info) else: result = semExpr(c, result) result = fitNode(c, s.typ.sons[0], result) diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim index 337224aef..310aabc32 100644 --- a/compiler/semexprs.nim +++ b/compiler/semexprs.nim @@ -191,7 +191,7 @@ proc isCastable(dst, src: PType): bool = proc isSymChoice(n: PNode): bool {.inline.} = result = n.kind in nkSymChoices -proc semConv(c: PContext, n: PNode, s: PSym): PNode = +proc semConv(c: PContext, n: PNode): PNode = if sonsLen(n) != 2: LocalError(n.info, errConvNeedsOneArg) return n @@ -322,7 +322,7 @@ proc isOpImpl(c: PContext, n: PNode): PNode = var match: bool let t2 = n[2].typ case t2.kind - of tyTypeClass: + of tyTypeClasses: var m: TCandidate InitCandidate(m, t2) match = matchUserTypeClass(c, m, emptyNode, t2, t1) != nil @@ -508,7 +508,7 @@ proc fixAbstractType(c: PContext, n: PNode) = changeType(it.sons[1], s, check=true) n.sons[i] = it.sons[1] of nkBracket: - # an implicitely constructed array (passed to an open array): + # an implicitly constructed array (passed to an open array): n.sons[i] = semArrayConstr(c, it, {}) else: nil @@ -738,8 +738,7 @@ proc semIndirectOp(c: PContext, n: PNode, flags: TExprFlags): PNode = elif t != nil and t.kind == tyTypeDesc: if n.len == 1: return semObjConstr(c, n, flags) let destType = t.skipTypes({tyTypeDesc, tyGenericInst}) - result = semConv(c, n, symFromType(destType, n.info)) - return + return semConv(c, n) else: result = overloadedCallOpr(c, n) # Now that nkSym does not imply an iteration over the proc/iterator space, @@ -1048,7 +1047,9 @@ proc semSubscript(c: PContext, n: PNode, flags: TExprFlags): PNode = # The result so far is a tyTypeDesc bound # a tyGenericBody. The line below will substitute # it with the instantiated type. - result = symNodeFromType(c, semTypeNode(c, n, nil), n.info) + result = n + result.typ = makeTypeDesc(c, semTypeNode(c, n, nil)) + #result = symNodeFromType(c, semTypeNode(c, n, nil), n.info) of tyTuple: checkSonsLen(n, 2) n.sons[0] = makeDeref(n.sons[0]) @@ -1883,7 +1884,8 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode = result = semExpr(c, n.sons[0], flags) of nkTypeOfExpr, nkTupleTy, nkRefTy..nkEnumTy: var typ = semTypeNode(c, n, nil).skipTypes({tyTypeDesc}) - result = symNodeFromType(c, typ, n.info) + result.typ = makeTypeDesc(c, typ) + #result = symNodeFromType(c, typ, n.info) of nkCall, nkInfix, nkPrefix, nkPostfix, nkCommand, nkCallStrLit: # check if it is an expression macro: checkMinSonsLen(n, 1) @@ -1906,7 +1908,7 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode = of skType: # XXX think about this more (``set`` procs) if n.len == 2: - result = semConv(c, n, s) + result = semConv(c, n) elif n.len == 1: result = semObjConstr(c, n, flags) elif Contains(c.AmbiguousSymbols, s.id): diff --git a/compiler/seminst.nim b/compiler/seminst.nim index 0cf5086a8..d7d64fd54 100644 --- a/compiler/seminst.nim +++ b/compiler/seminst.nim @@ -20,7 +20,8 @@ proc instantiateGenericParamList(c: PContext, n: PNode, pt: TIdTable, if a.kind != nkSym: InternalError(a.info, "instantiateGenericParamList; no symbol") var q = a.sym - if q.typ.kind notin {tyTypeDesc, tyGenericParam, tyTypeClass, tyExpr}: continue + if q.typ.kind notin {tyTypeDesc, tyGenericParam, tyExpr}+tyTypeClasses: + continue var s = newSym(skType, q.name, getCurrOwner(), q.info) s.flags = s.flags + {sfUsed, sfFromGeneric} var t = PType(IdTableGet(pt, q.typ)) @@ -193,7 +194,7 @@ proc fixupProcType(c: PContext, genericType: PType, if result == nil: return case genericType.kind - of tyGenericParam, tyTypeClass: + of tyGenericParam, tyTypeClasses: result = inst.concreteTypes[genericType.sym.position] of tyTypeDesc: result = inst.concreteTypes[genericType.sym.position] diff --git a/compiler/semstmts.nim b/compiler/semstmts.nim index da8ba50a8..a1805fdec 100644 --- a/compiler/semstmts.nim +++ b/compiler/semstmts.nim @@ -1087,6 +1087,8 @@ proc semIterator(c: PContext, n: PNode): PNode = # -- at least for 0.9.2. if s.typ.callConv == ccClosure: incl(s.typ.flags, tfCapturesEnv) + else: + s.typ.callConv = ccInline when false: if s.typ.callConv != ccInline: s.typ.callConv = ccClosure diff --git a/compiler/semthreads.nim b/compiler/semthreads.nim index 595ab0454..eded99325 100644 --- a/compiler/semthreads.nim +++ b/compiler/semthreads.nim @@ -380,7 +380,8 @@ proc analyseThreadProc*(prc: PSym) = var formals = skipTypes(prc.typ, abstractInst).n for i in 1 .. formals.len-1: var formal = formals.sons[i].sym - c.mapping[formal.id] = toTheirs # thread receives foreign data! + # the input is copied and belongs to the thread: + c.mapping[formal.id] = toMine discard analyse(c, prc.getBody) proc needsGlobalAnalysis*: bool = diff --git a/compiler/semtypes.nim b/compiler/semtypes.nim index 92f47f585..6c9c476d9 100644 --- a/compiler/semtypes.nim +++ b/compiler/semtypes.nim @@ -676,8 +676,13 @@ proc liftParamType(c: PContext, procKind: TSymKind, genericParams: PNode, if lifted != nil: paramType.sons[i] = lifted result = paramType - - if result != nil: + + if paramType.lastSon.kind == tyTypeClass: + result = paramType + result.kind = tyParametricTypeClass + result = addImplicitGeneric(copyType(result, + getCurrOwner(), false)) + elif result != nil: result.kind = tyGenericInvokation result.sons.setLen(result.sons.len - 1) of tyTypeClass: diff --git a/compiler/sigmatch.nim b/compiler/sigmatch.nim index 00f3b2b10..cacf4782e 100644 --- a/compiler/sigmatch.nim +++ b/compiler/sigmatch.nim @@ -203,7 +203,7 @@ proc describeArgs*(c: PContext, n: PNode, startIdx = 1): string = add(result, argTypeToString(arg)) if i != sonsLen(n) - 1: add(result, ", ") -proc typeRel*(c: var TCandidate, f, a: PType): TTypeRelation +proc typeRel*(c: var TCandidate, f, a: PType, doBind = true): TTypeRelation proc concreteType(c: TCandidate, t: PType): PType = case t.kind of tyArrayConstr: @@ -213,7 +213,7 @@ proc concreteType(c: TCandidate, t: PType): PType = addSonSkipIntLit(result, t.sons[1]) # XXX: semantic checking for the type? of tyNil: result = nil # what should it be? - of tyGenericParam: + of tyGenericParam, tyAnything: result = t while true: result = PType(idTableGet(c.bindings, t)) @@ -385,8 +385,23 @@ proc typeRangeRel(f, a: PType): TTypeRelation {.noinline.} = else: result = isNone -proc typeRel(c: var TCandidate, f, a: PType): TTypeRelation = - # is a subtype of f? +proc typeRel(c: var TCandidate, f, a: PType, doBind = true): TTypeRelation = + # typeRel can be used to establish various relationships between types: + # + # 1) When used with concrete types, it will check for type equivalence + # or a subtype relationship. + # + # 2) When used with a concrete type against a type class (such as generic + # signature of a proc), it will check whether the concrete type is a member + # of the designated type class. + # + # 3) When used with two type classes, it will check whether the types + # matching the first type class are a strict subset of the types matching + # the other. This allows us to compare the signatures of generic procs in + # order to give preferrence to the most specific one: + # + # seq[seq[any]] is a strict subset of seq[any] and hence more specific. + result = isNone assert(f != nil) assert(a != nil) @@ -397,6 +412,50 @@ proc typeRel(c: var TCandidate, f, a: PType): TTypeRelation = return typeRel(c, f, lastSon(a)) if a.kind == tyVar and f.kind != tyVar: return typeRel(c, f, a.sons[0]) + + template bindingRet(res) = + when res == isGeneric: put(c.bindings, f, a) + return res + + case a.kind + of tyOr: + # seq[int|string] vs seq[number] + # both int and string must match against number + for branch in a.sons: + if typeRel(c, f, branch, false) == isNone: + return isNone + + return isGeneric + + of tyAnd: + # seq[Sortable and Iterable] vs seq[Sortable] + # only one match is enough + for branch in a.sons: + if typeRel(c, f, branch, false) != isNone: + return isGeneric + + return isNone + + of tyNot: + case f.kind + of tyNot: + # seq[!int] vs seq[!number] + # seq[float] matches the first, but not the second + # we must turn the problem around: + # is number a subset of int? + return typeRel(c, a.lastSon, f.lastSon) + + else: + # negative type classes are essentially infinite, + # so only the `any` type class is their superset + return if f.kind == tyAnything: isGeneric + else: isNone + + of tyAnything: + return if f.kind == tyAnything: isGeneric + else: isNone + else: nil + case f.kind of tyEnum: if a.kind == f.kind and sameEnumTypes(f, a): result = isEqual @@ -485,9 +544,12 @@ proc typeRel(c: var TCandidate, f, a: PType): TTypeRelation = of tyOrdinal: if isOrdinalType(a): var x = if a.kind == tyOrdinal: a.sons[0] else: a - - result = typeRel(c, f.sons[0], x) - if result < isGeneric: result = isNone + + if f.sonsLen == 0: + result = isGeneric + else: + result = typeRel(c, f.sons[0], x) + if result < isGeneric: result = isNone elif a.kind == tyGenericParam: result = isGeneric of tyForward: InternalError("forward type in typeRel()") @@ -574,13 +636,17 @@ proc typeRel(c: var TCandidate, f, a: PType): TTypeRelation = (a.sons[1].kind == tyChar): result = isConvertible else: nil - of tyEmpty: + + of tyEmpty: if a.kind == tyEmpty: result = isEqual - of tyGenericInst: + + of tyGenericInst: result = typeRel(c, lastSon(f), a) - of tyGenericBody: + + of tyGenericBody: let ff = lastSon(f) if ff != nil: result = typeRel(c, ff, a) + of tyGenericInvokation: var x = a.skipGenericAlias if x.kind == tyGenericInvokation or f.sons[0].kind != tyGenericBody: @@ -604,6 +670,38 @@ proc typeRel(c: var TCandidate, f, a: PType): TTypeRelation = if x == nil or x.kind in {tyGenericInvokation, tyGenericParam}: InternalError("wrong instantiated type!") put(c.bindings, f.sons[i], x) + + of tyAnd: + for branch in f.sons: + if typeRel(c, branch, a) == isNone: + return isNone + + bindingRet isGeneric + + of tyOr: + for branch in f.sons: + if typeRel(c, branch, a) != isNone: + bindingRet isGeneric + + return isNone + + of tyNot: + for branch in f.sons: + if typeRel(c, branch, a) != isNone: + return isNone + + bindingRet isGeneric + + of tyAnything: + var prev = PType(idTableGet(c.bindings, f)) + if prev == nil: + var concrete = concreteType(c, a) + if concrete != nil and doBind: + put(c.bindings, f, concrete) + return isGeneric + else: + return typeRel(c, prev, a) + of tyGenericParam, tyTypeClass: var x = PType(idTableGet(c.bindings, f)) if x == nil: @@ -634,7 +732,7 @@ proc typeRel(c: var TCandidate, f, a: PType): TTypeRelation = if concrete == nil: result = isNone else: - put(c.bindings, f, concrete) + if doBind: put(c.bindings, f, concrete) elif a.kind == tyEmpty: result = isGeneric elif x.kind == tyGenericParam: @@ -809,8 +907,8 @@ proc ParamTypesMatchAux(c: PContext, m: var TCandidate, f, argType: PType, InternalAssert a.len > 0 r = typeRel(m, f.lastSon, a.lastSon) else: - let match = matchTypeClass(m, fMaybeExpr, a) - if match != isGeneric: r = isNone + let match = matchTypeClass(m.bindings, fMaybeExpr, a) + if not match: r = isNone else: # XXX: Ideally, this should happen much earlier somewhere near # semOpAux, but to do that, we need to be able to query the @@ -827,7 +925,7 @@ proc ParamTypesMatchAux(c: PContext, m: var TCandidate, f, argType: PType, if r == isGeneric: put(m.bindings, f, arg.typ) - of tyTypeClass: + of tyTypeClass, tyParametricTypeClass: if fMaybeExpr.n != nil: let match = matchUserTypeClass(c, m, arg, fMaybeExpr, a) if match != nil: @@ -1130,7 +1228,7 @@ proc matches*(c: PContext, n, nOrig: PNode, m: var TCandidate) = var f = 1 while f < sonsLen(m.callee.n): var formal = m.callee.n.sons[f].sym - if not ContainsOrIncl(marker, formal.position): + if not ContainsOrIncl(marker, formal.position): if formal.ast == nil: if formal.typ.kind == tyVarargs: var container = newNodeIT(nkBracket, n.info, arrayConstr(c, n.info)) @@ -1145,7 +1243,7 @@ proc matches*(c: PContext, n, nOrig: PNode, m: var TCandidate) = setSon(m.call, formal.position + 1, copyTree(formal.ast)) inc(f) -proc argtypeMatches*(c: PContext, f, a: PType): bool = +proc argtypeMatches*(c: PContext, f, a: PType): bool = var m: TCandidate initCandidate(m, f) let res = paramTypesMatch(c, m, f, a, ast.emptyNode, nil) @@ -1155,3 +1253,121 @@ proc argtypeMatches*(c: PContext, f, a: PType): bool = result = res != nil include suggest + +tests: + var dummyOwner = newSym(skModule, getIdent("test_module"), nil, UnknownLineInfo()) + + proc `|` (t1, t2: PType): PType = + result = newType(tyOr, dummyOwner) + result.rawAddSon(t1) + result.rawAddSon(t2) + + proc `&` (t1, t2: PType): PType = + result = newType(tyAnd, dummyOwner) + result.rawAddSon(t1) + result.rawAddSon(t2) + + proc `!` (t: PType): PType = + result = newType(tyNot, dummyOwner) + result.rawAddSon(t) + + proc seq(t: PType): PType = + result = newType(tySequence, dummyOwner) + result.rawAddSon(t) + + proc array(x: int, t: PType): PType = + result = newType(tyArray, dummyOwner) + + var n = newNodeI(nkRange, UnknownLineInfo()) + addSon(n, newIntNode(nkIntLit, 0)) + addSon(n, newIntNode(nkIntLit, x)) + let range = newType(tyRange, dummyOwner) + + result.rawAddSon(range) + result.rawAddSon(t) + + suite "type classes": + let + int = newType(tyInt, dummyOwner) + float = newType(tyFloat, dummyOwner) + string = newType(tyString, dummyOwner) + ordinal = newType(tyOrdinal, dummyOwner) + any = newType(tyAnything, dummyOwner) + number = int | float + + var TFoo = newType(tyObject, dummyOwner) + TFoo.sym = newSym(skType, getIdent"TFoo", dummyOwner, UnknownLineInfo()) + + var T1 = newType(tyGenericParam, dummyOwner) + T1.sym = newSym(skType, getIdent"T1", dummyOwner, UnknownLineInfo()) + T1.sym.position = 0 + + var T2 = newType(tyGenericParam, dummyOwner) + T2.sym = newSym(skType, getIdent"T2", dummyOwner, UnknownLineInfo()) + T2.sym.position = 1 + + setup: + var c: TCandidate + InitCandidate(c, nil) + + template yes(x, y) = + test astToStr(x) & " is " & astToStr(y): + check typeRel(c, y, x) == isGeneric + + template no(x, y) = + test astToStr(x) & " is not " & astToStr(y): + check typeRel(c, y, x) == isNone + + yes seq(any), array(10, int) | seq(any) + # Sure, seq[any] is directly included + + yes seq(int), seq(any) + yes seq(int), seq(number) + # Sure, the int sequence is certainly + # part of the number sequences (and all sequences) + + no seq(any), seq(float) + # Nope, seq[any] includes types that are not seq[float] (e.g. seq[int]) + + yes seq(int|string), seq(any) + # Sure + + yes seq(int&string), seq(any) + # Again + + yes seq(int&string), seq(int) + # A bit more complicated + # seq[int&string] is not a real type, but it's analogous to + # seq[Sortable and Iterable], which is certainly a subset of seq[Sortable] + + no seq(int|string), seq(int|float) + # Nope, seq[string] is not included in not included in + # the seq[int|float] set + + no seq(!(int|string)), seq(string) + # A sequence that is neither seq[int] or seq[string] + # is obviously not seq[string] + + no seq(!int), seq(number) + # Now your head should start to hurt a bit + # A sequence that is not seq[int] is not necessarily a number sequence + # it could well be seq[string] for example + + yes seq(!(int|string)), seq(!string) + # all sequnece types besides seq[int] and seq[string] + # are subset of all sequence types that are not seq[string] + + no seq(!(int|string)), seq(!(string|TFoo)) + # Nope, seq[TFoo] is included in the first set, but not in the second + + no seq(!string), seq(!number) + # Nope, seq[int] in included in the first set, but not in the second + + yes seq(!number), seq(any) + yes seq(!int), seq(any) + no seq(any), seq(!any) + no seq(!int), seq(!any) + + yes int, ordinal + no string, ordinal + diff --git a/compiler/testability.nim b/compiler/testability.nim new file mode 100644 index 000000000..ceefd0a5e --- /dev/null +++ b/compiler/testability.nim @@ -0,0 +1,5 @@ +template tests*(body: stmt) {.immediate.} = + when defined(selftest): + when not defined(unittest): import unittest + body + diff --git a/compiler/types.nim b/compiler/types.nim index 4dec9ea2f..7e07a0667 100644 --- a/compiler/types.nim +++ b/compiler/types.nim @@ -382,33 +382,34 @@ proc mutateTypeAux(marker: var TIntSet, t: PType, iter: TTypeMutator, if t.n != nil: result.n = mutateNode(marker, t.n, iter, closure) assert(result != nil) -proc mutateType(t: PType, iter: TTypeMutator, closure: PObject): PType = +proc mutateType(t: PType, iter: TTypeMutator, closure: PObject): PType = var marker = InitIntSet() result = mutateTypeAux(marker, t, iter, closure) -proc ValueToString(a: PNode): string = +proc ValueToString(a: PNode): string = case a.kind of nkCharLit..nkUInt64Lit: result = $(a.intVal) of nkFloatLit..nkFloat128Lit: result = $(a.floatVal) of nkStrLit..nkTripleStrLit: result = a.strVal else: result = "<invalid value>" -proc rangeToStr(n: PNode): string = +proc rangeToStr(n: PNode): string = assert(n.kind == nkRange) result = ValueToString(n.sons[0]) & ".." & ValueToString(n.sons[1]) const - typeToStr: array[TTypeKind, string] = ["None", "bool", "Char", "empty", - "Array Constructor [$1]", "nil", "expr", "stmt", "typeDesc", - "GenericInvokation", "GenericBody", "GenericInst", "GenericParam", - "distinct $1", "enum", "ordinal[$1]", "array[$1, $2]", "object", "tuple", - "set[$1]", "range[$1]", "ptr ", "ref ", "var ", "seq[$1]", "proc", + typeToStr: array[TTypeKind, string] = ["None", "bool", "Char", "empty", + "Array Constructor [$1]", "nil", "expr", "stmt", "typeDesc", + "GenericInvokation", "GenericBody", "GenericInst", "GenericParam", + "distinct $1", "enum", "ordinal[$1]", "array[$1, $2]", "object", "tuple", + "set[$1]", "range[$1]", "ptr ", "ref ", "var ", "seq[$1]", "proc", "pointer", "OpenArray[$1]", "string", "CString", "Forward", "int", "int8", "int16", "int32", "int64", "float", "float32", "float64", "float128", "uint", "uint8", "uint16", "uint32", "uint64", "bignum", "const ", - "!", "varargs[$1]", "iter[$1]", "Error Type", "TypeClass"] + "!", "varargs[$1]", "iter[$1]", "Error Type", "TypeClass", + "ParametricTypeClass", "and", "or", "not", "any"] proc consToStr(t: PType): string = if t.len > 0: result = t.typeToString @@ -421,7 +422,7 @@ proc constraintsToStr(t: PType): string = if i > 0: result.add(sep) result.add(t.sons[i].consToStr) -proc TypeToString(typ: PType, prefer: TPreferedDesc = preferName): string = +proc TypeToString(typ: PType, prefer: TPreferedDesc = preferName): string = var t = typ result = "" if t == nil: return @@ -861,7 +862,7 @@ proc SameTypeAux(x, y: PType, c: var TSameTypeClosure): bool = of tyGenericParam, tyGenericInvokation, tyGenericBody, tySequence, tyOpenArray, tySet, tyRef, tyPtr, tyVar, tyArrayConstr, tyArray, tyProc, tyConst, tyMutable, tyVarargs, tyIter, - tyOrdinal, tyTypeClass: + tyOrdinal, tyTypeClasses: CycleCheck() result = sameChildrenAux(a, b, c) and sameFlags(a, b) if result and (a.kind == tyProc): @@ -1042,7 +1043,7 @@ proc typeAllowedAux(marker: var TIntSet, typ: PType, kind: TSymKind, # XXX er ... no? these should not be allowed! of tyEmpty: result = taField in flags - of tyTypeClass: + of tyTypeClasses: result = true of tyGenericBody, tyGenericParam, tyForward, tyNone, tyGenericInvokation: result = false diff --git a/compiler/wordrecg.nim b/compiler/wordrecg.nim index b37a7bb4f..39b19646e 100644 --- a/compiler/wordrecg.nim +++ b/compiler/wordrecg.nim @@ -60,7 +60,7 @@ type wPassc, wPassl, wBorrow, wDiscardable, wFieldChecks, wWatchPoint, wSubsChar, - wAcyclic, wShallow, wUnroll, wLinearScanEnd, wComputedGoto, + wAcyclic, wShallow, wUnroll, wLinearScanEnd, wComputedGoto, wInjectStmt, wWrite, wGensym, wInject, wDirty, wInheritable, wThreadVar, wEmit, wNoStackFrame, wImplicitStatic, wGlobal, wCodegenDecl, @@ -142,7 +142,8 @@ const "compiletime", "noinit", "passc", "passl", "borrow", "discardable", "fieldchecks", "watchpoint", - "subschar", "acyclic", "shallow", "unroll", "linearscanend", "computedgoto", + "subschar", "acyclic", "shallow", "unroll", "linearscanend", + "computedgoto", "injectstmt", "write", "gensym", "inject", "dirty", "inheritable", "threadvar", "emit", "nostackframe", "implicitstatic", "global", "codegendecl", |