From fc452787e7bba3301642c012fe8e2cdea243993b Mon Sep 17 00:00:00 2001 From: Araq Date: Fri, 17 Jan 2014 01:18:57 +0100 Subject: [PATCH] better html generator for the tester; fixes some VM bugs --- compiler/sem.nim | 29 +++++++---- compiler/semexprs.nim | 2 +- compiler/semmacrosanity.nim | 89 +++++++++++++++++++++++++++++++++ compiler/vm.nim | 33 ++++++++---- compiler/vmgen.nim | 30 +++++++++-- lib/pure/collections/tables.nim | 2 +- tests/testament/backend.nim | 3 +- tests/testament/categories.nim | 10 +++- tests/testament/htmlgen.nim | 48 ++++++++++++------ tests/testament/tester.nim | 2 +- 10 files changed, 202 insertions(+), 46 deletions(-) create mode 100644 compiler/semmacrosanity.nim diff --git a/compiler/sem.nim b/compiler/sem.nim index b53e7335c8..e89e32f4e0 100644 --- a/compiler/sem.nim +++ b/compiler/sem.nim @@ -15,7 +15,7 @@ import magicsys, parser, nversion, nimsets, semfold, importer, procfind, lookups, rodread, pragmas, passes, semdata, semtypinst, sigmatch, semthreads, intsets, transf, vmdef, vm, idgen, aliases, cgmeth, lambdalifting, - evaltempl, patterns, parampatterns, sempass2, pretty + evaltempl, patterns, parampatterns, sempass2, pretty, semmacrosanity # implementation @@ -186,16 +186,23 @@ when false: proc fixupTypeAfterEval(c: PContext, evaluated, eOrig: PNode): PNode = # recompute the types as 'eval' isn't guaranteed to construct types nor # that the types are sound: - result = semExprWithType(c, evaluated) - #result = fitNode(c, e.typ, result) inlined with special case: - let arg = result - result = indexTypesMatch(c, eOrig.typ, arg.typ, arg) - if result == nil: - result = arg - # for 'tcnstseq' we support [] to become 'seq' - if eOrig.typ.skipTypes(abstractInst).kind == tySequence and - arg.typ.skipTypes(abstractInst).kind == tyArrayConstr: - arg.typ = eOrig.typ + when true: + if eOrig.typ.kind in {tyExpr, tyStmt, tyTypeDesc}: + result = semExprWithType(c, evaluated) + else: + result = evaluated + semmacrosanity.annotateType(result, eOrig.typ) + else: + result = semExprWithType(c, evaluated) + #result = fitNode(c, e.typ, result) inlined with special case: + let arg = result + result = indexTypesMatch(c, eOrig.typ, arg.typ, arg) + if result == nil: + result = arg + # for 'tcnstseq' we support [] to become 'seq' + if eOrig.typ.skipTypes(abstractInst).kind == tySequence and + arg.typ.skipTypes(abstractInst).kind == tyArrayConstr: + arg.typ = eOrig.typ proc tryConstExpr(c: PContext, n: PNode): PNode = var e = semExprWithType(c, n) diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim index 1f7803c95d..0d74db1ce4 100644 --- a/compiler/semexprs.nim +++ b/compiler/semexprs.nim @@ -1068,7 +1068,7 @@ proc semSubscript(c: PContext, n: PNode, flags: TExprFlags): PNode = else: localError(n.info, errIndexTypesDoNotMatch) result = n - else: nil + else: discard proc semArrayAccess(c: PContext, n: PNode, flags: TExprFlags): PNode = result = semSubscript(c, n, flags) diff --git a/compiler/semmacrosanity.nim b/compiler/semmacrosanity.nim new file mode 100644 index 0000000000..eff944baca --- /dev/null +++ b/compiler/semmacrosanity.nim @@ -0,0 +1,89 @@ +# +# +# The Nimrod Compiler +# (c) Copyright 2014 Andreas Rumpf +# +# See the file "copying.txt", included in this +# distribution, for details about the copyright. +# + +## Implements type sanity checking for ASTs resulting from macros. Lots of +## room for improvement here. + +import ast, astalgo, msgs, types + +proc ithField(n: PNode, field: int): PSym = + result = nil + case n.kind + of nkRecList: + for i in countup(0, sonsLen(n) - 1): + result = ithField(n.sons[i], field-i) + if result != nil: return + of nkRecCase: + if n.sons[0].kind != nkSym: internalError(n.info, "ithField") + result = ithField(n.sons[0], field-1) + if result != nil: return + for i in countup(1, sonsLen(n) - 1): + case n.sons[i].kind + of nkOfBranch, nkElse: + result = ithField(lastSon(n.sons[i]), field-1) + if result != nil: return + else: internalError(n.info, "ithField(record case branch)") + of nkSym: + if field == 0: result = n.sym + else: discard + +proc annotateType*(n: PNode, t: PType) = + let x = t.skipTypes(abstractInst) + # Note: x can be unequal to t and we need to be careful to use 't' + # to not to skip tyGenericInst + case n.kind + of nkPar: + if x.kind == tyObject: + n.typ = t + for i in 0 .. = x.len: globalError n.info, "invalid " & $i & "th field" + else: annotateType(n.sons[i], x.sons[i]) + elif x.kind == tyProc and x.callConv == ccClosure: + n.typ = t + else: + globalError(n.info, "() must have an object or tuple type") + of nkBracket: + if x.kind in {tyArrayConstr, tyArray, tySequence, tyOpenarray}: + n.typ = t + for m in n: annotateType(m, x.elemType) + else: + globalError(n.info, "[] must have some form of array type") + of nkCurly: + if x.kind in {tySet}: + n.typ = t + for m in n: annotateType(m, x.elemType) + else: + globalError(n.info, "{} must have the set type") + of nkFloatLit..nkFloat128Lit: + if x.kind in {tyFloat..tyFloat128}: + n.typ = t + else: + globalError(n.info, "float literal must have some float type") + of nkCharLit..nkUInt64Lit: + if x.kind in {tyInt..tyUInt64, tyBool, tyChar, tyEnum}: + n.typ = t + else: + globalError(n.info, "integer literal must have some int type") + of nkStrLit..nkTripleStrLit: + if x.kind in {tyString, tyCString}: + n.typ = t + else: + globalError(n.info, "string literal must be of some string type") + of nkNilLit: + if x.kind in NilableTypes: + n.typ = t + else: + globalError(n.info, "nil literal must be of some pointer type") + else: discard diff --git a/compiler/vm.nim b/compiler/vm.nim index a670e5bd01..9ed18d29eb 100644 --- a/compiler/vm.nim +++ b/compiler/vm.nim @@ -172,7 +172,9 @@ proc asgnComplex(x, y: PNode) = else: if x.kind notin {nkEmpty..nkNilLit}: let y = y.copyValue - for i in countup(0, sonsLen(y) - 1): addSon(x, y.sons[i]) + for i in countup(0, sonsLen(y) - 1): + if i < x.len: x.sons[i] = y.sons[i] + else: addSon(x, y.sons[i]) template getstr(a: expr): expr = (if a.kind in {nkStrLit..nkTripleStrLit}: a.strVal else: $chr(int(a.intVal))) @@ -319,8 +321,11 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): PNode = decodeB(nkIntLit) regs[ra].intVal = regs[rb].intVal of opcAsgnStr: - decodeB(nkStrLit) - regs[ra].strVal = regs[rb].strVal + if regs[instr.regB].kind == nkNilLit: + decodeB(nkNilLit) + else: + decodeB(nkStrLit) + regs[ra].strVal = regs[rb].strVal of opcAsgnFloat: decodeB(nkFloatLit) regs[ra].floatVal = regs[rb].floatVal @@ -336,6 +341,8 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): PNode = # a = b[c] let rb = instr.regB let rc = instr.regC + if regs[rc].intVal > high(int): + stackTrace(c, tos, pc, errIndexOutOfBounds) let idx = regs[rc].intVal.int # XXX what if the array is not 0-based? -> codegen should insert a sub assert regs[rb].kind != nkMetaNode @@ -373,7 +380,7 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): PNode = let rb = instr.regB let rc = instr.regC # XXX this creates a wrong alias - #Message(c.debug[pc], warnUser, $regs[rb].len & " " & $rc) + #Message(c.debug[pc], warnUser, $regs[rb].safeLen & " " & $rc) asgnComplex(regs[ra], regs[rb].sons[rc]) of opcWrObj: # a.b = c @@ -427,8 +434,11 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): PNode = regs[ra].intVal = regs[rb].skipMeta.len - imm of opcLenStr: decodeBImm(nkIntLit) - assert regs[rb].kind in {nkStrLit..nkTripleStrLit} - regs[ra].intVal = regs[rb].strVal.len - imm + if regs[rb].kind == nkNilLit: + stackTrace(c, tos, pc, errNilAccess) + else: + assert regs[rb].kind in {nkStrLit..nkTripleStrLit} + regs[ra].intVal = regs[rb].strVal.len - imm of opcIncl: decodeB(nkCurly) if not inSet(regs[ra], regs[rb]): addSon(regs[ra], copyTree(regs[rb])) @@ -738,11 +748,11 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): PNode = inc pc ensureKind(nkBracket) let instr2 = c.code[pc] - let rb = instr2.regA + let count = regs[instr2.regA].intVal.int regs[ra].typ = typ - newSeq(regs[ra].sons, rb) - for i in 0 .. = 0: t.data[index].slot = seDeleted dec(t.counter) diff --git a/tests/testament/backend.nim b/tests/testament/backend.nim index 500535d9b8..87ac10b1fd 100644 --- a/tests/testament/backend.nim +++ b/tests/testament/backend.nim @@ -77,7 +77,8 @@ proc getMachine*: MachineId = name, system.hostOS, system.hostCPU).MachineId proc getCommit: CommitId = - let hash = "git log -n 1"() + const commLen = "commit ".len + let hash = "git log -n 1"()[commLen..commLen+10] let branch = "git symbolic-ref --short HEAD"() if hash.len == 0 or branch.len == 0: quit "cannot determine git HEAD" diff --git a/tests/testament/categories.nim b/tests/testament/categories.nim index 68a61a973f..5dd8414470 100644 --- a/tests/testament/categories.nim +++ b/tests/testament/categories.nim @@ -221,6 +221,14 @@ proc testStdlib(r: var TResults, pattern, options: string, cat: Category) = const AdditionalCategories = ["debugger", "tools", "examples", "stdlib"] +proc `&.?`(a, b: string): string = + # candidate for the stdlib? + result = if b.startswith(a): b else: a & b + +proc `&?.`(a, b: string): string = + # candidate for the stdlib? + result = if a.endswith(b): a else: a & b + proc processCategory(r: var TResults, cat: Category, options: string) = case cat.string.normalize of "rodfiles": @@ -252,5 +260,5 @@ proc processCategory(r: var TResults, cat: Category, options: string) = compileExample(r, "examples/gtk/*.nim", options, cat) compileExample(r, "examples/talk/*.nim", options, cat) else: - for name in os.walkFiles(cat.string / "t*.nim"): + for name in os.walkFiles("tests" & DirSep &.? cat.string / "t*.nim"): testSpec r, makeTest(name, options, cat) diff --git a/tests/testament/htmlgen.nim b/tests/testament/htmlgen.nim index 4abcacbd4a..e42b880706 100644 --- a/tests/testament/htmlgen.nim +++ b/tests/testament/htmlgen.nim @@ -47,39 +47,57 @@ div.tabContent.hide { display: none; } function init() { // Grab the tab links and content divs from the page var tabListItems = document.getElementById('tabs').childNodes; - for ( var i = 0; i < tabListItems.length; i++ ) { - if ( tabListItems[i].nodeName == "LI" ) { - var tabLink = getFirstChildWithTagName( tabListItems[i], 'A' ); - var id = getHash( tabLink.getAttribute('href') ); + for (var i = 0; i < tabListItems.length; i++) { + if (tabListItems[i].nodeName == "LI") { + var tabLink = getFirstChildWithTagName(tabListItems[i], 'A'); + var id = getHash(tabLink.getAttribute('href')); tabLinks[id] = tabLink; - contentDivs[id] = document.getElementById( id ); + contentDivs[id] = document.getElementById(id); } } // Assign onclick events to the tab links, and // highlight the first tab var i = 0; - for ( var id in tabLinks ) { + for (var id in tabLinks) { tabLinks[id].onclick = showTab; tabLinks[id].onfocus = function() { this.blur() }; - if ( i == 0 ) tabLinks[id].className = 'selected'; + if (i == 0) tabLinks[id].className = 'selected'; i++; } // Hide all content divs except the first var i = 0; - for ( var id in contentDivs ) { - if ( i != 0 ) contentDivs[id].className = 'tabContent hide'; + for (var id in contentDivs) { + if (i != 0) contentDivs[id].className = 'tabContent hide'; i++; } } - function getFirstChildWithTagName( element, tagName ) { - for ( var i = 0; i < element.childNodes.length; i++ ) { - if ( element.childNodes[i].nodeName == tagName ) return element.childNodes[i]; + function showTab() { + var selectedId = getHash(this.getAttribute('href')); + + // Highlight the selected tab, and dim all others. + // Also show the selected content div, and hide all others. + for (var id in contentDivs) { + if (id == selectedId) { + tabLinks[id].className = 'selected'; + contentDivs[id].className = 'tabContent'; + } else { + tabLinks[id].className = ''; + contentDivs[id].className = 'tabContent hide'; + } + } + // Stop the browser following the link + return false; + } + + function getFirstChildWithTagName(element, tagName) { + for (var i = 0; i < element.childNodes.length; i++) { + if (element.childNodes[i].nodeName == tagName) return element.childNodes[i]; } } - function getHash( url ) { - var hashPos = url.lastIndexOf ( '#' ); - return url.substring( hashPos + 1 ); + function getHash(url) { + var hashPos = url.lastIndexOf('#'); + return url.substring(hashPos + 1); } diff --git a/tests/testament/tester.nim b/tests/testament/tester.nim index 61d0072e32..8abea82248 100644 --- a/tests/testament/tester.nim +++ b/tests/testament/tester.nim @@ -245,7 +245,7 @@ proc main() = case action of "all": for kind, dir in walkDir("tests"): - if kind == pcDir and dir != "testament": + if kind == pcDir and dir notin ["testament", "testdata", "nimcache"]: processCategory(r, Category(dir), p.cmdLineRest.string) for a in AdditionalCategories: processCategory(r, Category(a), p.cmdLineRest.string)