From bf9053315c75037a58230f8abc7f75ece636f806 Mon Sep 17 00:00:00 2001 From: Oscar Campbell Date: Wed, 3 Jun 2015 03:22:17 +0200 Subject: [PATCH 01/18] Ensure fresh compiler and local compiler + libs --- koch.nim | 3 +++ tests/testament/specs.nim | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/koch.nim b/koch.nim index fc292401a0..83737d751c 100644 --- a/koch.nim +++ b/koch.nim @@ -330,6 +330,9 @@ proc winRelease() = template `|`(a, b): expr = (if a.len > 0: a else: b) proc tests(args: string) = + # Since tests take a long time (on my machine) - lets make sure a stupid + # mistake - like forgetting to compile the compiler - isn't made... + exec "nim c --lib:./lib -d:release compiler/nim.nim" # we compile the tester with taintMode:on to have a basic # taint mode test :-) exec "nim cc --taintMode:on tests/testament/tester" diff --git a/tests/testament/specs.nim b/tests/testament/specs.nim index 9306bf0256..a4b1b82838 100644 --- a/tests/testament/specs.nim +++ b/tests/testament/specs.nim @@ -10,7 +10,7 @@ import parseutils, strutils, os, osproc, streams, parsecfg const - cmdTemplate* = r"nim $target --hints:on -d:testing $options $file" + cmdTemplate* = r"./compiler/nim $target --lib:./lib --hints:on -d:testing $options $file" type TTestAction* = enum From 51fc903dbf26a30add2ec75bbdc01a403cb2d46c Mon Sep 17 00:00:00 2001 From: Oscar Campbell Date: Wed, 3 Jun 2015 13:08:28 +0200 Subject: [PATCH 02/18] Made pathing (hopefully) Windows friendly. --- koch.nim | 2 +- tests/testament/specs.nim | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/koch.nim b/koch.nim index 83737d751c..8962acdc15 100644 --- a/koch.nim +++ b/koch.nim @@ -332,7 +332,7 @@ template `|`(a, b): expr = (if a.len > 0: a else: b) proc tests(args: string) = # Since tests take a long time (on my machine) - lets make sure a stupid # mistake - like forgetting to compile the compiler - isn't made... - exec "nim c --lib:./lib -d:release compiler/nim.nim" + exec "nim c --lib:lib -d:release compiler/nim.nim" # we compile the tester with taintMode:on to have a basic # taint mode test :-) exec "nim cc --taintMode:on tests/testament/tester" diff --git a/tests/testament/specs.nim b/tests/testament/specs.nim index a4b1b82838..99640f22c3 100644 --- a/tests/testament/specs.nim +++ b/tests/testament/specs.nim @@ -10,7 +10,7 @@ import parseutils, strutils, os, osproc, streams, parsecfg const - cmdTemplate* = r"./compiler/nim $target --lib:./lib --hints:on -d:testing $options $file" + cmdTemplate* = r"compiler" / "nim $target --lib:lib --hints:on -d:testing $options $file" type TTestAction* = enum From 071ccf3d026b03135dbc1e413a5312ac296191ac Mon Sep 17 00:00:00 2001 From: Oscar Campbell Date: Thu, 4 Jun 2015 07:33:27 +0200 Subject: [PATCH 03/18] Add "safety compilation". Improved, fixed messages - node ext searcher also looks for "iojs" if no node(js?) found. - koch ensures the local work dir compiler is freshly compiled before executing the tests. - the tester can only run in the repo dir - now the compiler and libs used are also explicitly the local dirs - so no confusion from system wide pathing ("sufficiently sandboxed"). - fixed expectations in tmitems.nim test to match changes in json mod. - re-layouted the columns / row printing slightly, making test fails pop out more, and everything "normal" layed back. --- compiler/nodejs.nim | 2 + koch.nim | 6 +- tests/stdlib/tmitems.nim | 4 +- tests/testament/tester.nim | 146 +++++++++++++++++++++++-------------- 4 files changed, 100 insertions(+), 58 deletions(-) diff --git a/compiler/nodejs.nim b/compiler/nodejs.nim index e2b79df19a..7f9f28aaf9 100644 --- a/compiler/nodejs.nim +++ b/compiler/nodejs.nim @@ -4,3 +4,5 @@ proc findNodeJs*(): string = result = findExe("nodejs") if result == "": result = findExe("node") + if result == "": + result = findExe("iojs") diff --git a/koch.nim b/koch.nim index 8962acdc15..83f363b1ae 100644 --- a/koch.nim +++ b/koch.nim @@ -330,12 +330,12 @@ proc winRelease() = template `|`(a, b): expr = (if a.len > 0: a else: b) proc tests(args: string) = - # Since tests take a long time (on my machine) - lets make sure a stupid - # mistake - like forgetting to compile the compiler - isn't made... - exec "nim c --lib:lib -d:release compiler/nim.nim" # we compile the tester with taintMode:on to have a basic # taint mode test :-) exec "nim cc --taintMode:on tests/testament/tester" + # Since tests take a long time (on my machine), and we want to defy Murhpys + # law - lets make sure the compiler really is freshly compiled! + exec "nim c --lib:lib -d:release --opt:speed compiler/nim.nim" let tester = quoteShell(getCurrentDir() / "tests/testament/tester".exe) let success = tryExec tester & " " & (args|"all") exec tester & " html" diff --git a/tests/stdlib/tmitems.nim b/tests/stdlib/tmitems.nim index 2c0a0392a5..544ad03345 100644 --- a/tests/stdlib/tmitems.nim +++ b/tests/stdlib/tmitems.nim @@ -11,8 +11,8 @@ fpqeew [11, 12, 13] [11, 12, 13] [11, 12, 13] -{"key1": 11, "key2": 12, "key3": 13} -[11, 12, 13] +{"key1":11,"key2":12,"key3":13} +[11,12,13] diff --git a/tests/testament/tester.nim b/tests/testament/tester.nim index 62dafee804..ebfea591a7 100644 --- a/tests/testament/tester.nim +++ b/tests/testament/tester.nim @@ -12,7 +12,7 @@ import parseutils, strutils, pegs, os, osproc, streams, parsecfg, json, marshal, backend, parseopt, specs, htmlgen, browsers, terminal, - algorithm, compiler/nodejs + algorithm, compiler/nodejs, re const resultsFile = "testresults.html" @@ -126,6 +126,9 @@ proc `$`(x: TResults): string = "Tests skipped: $2 / $3
\n") % [$x.passed, $x.skipped, $x.total] +proc leftAlign(s: string, count: Natural, padding = ' '): string = + s & repeat(padding, max(0, count - s.len)) + proc addResult(r: var TResults, test: TTest, expected, given: string, success: TResultEnum) = let name = test.name.extractFilename & test.options @@ -137,14 +140,19 @@ proc addResult(r: var TResults, test: TTest, expected = expected, given = given) r.data.addf("$#\t$#\t$#\t$#", name, expected, given, $success) - if success == reIgnored: - styledEcho styleBright, name, fgYellow, " [", $success, "]" - elif success != reSuccess: - styledEcho styleBright, name, fgRed, " [", $success, "]" - echo"Expected:" - styledEcho styleBright, expected - echo"Given:" - styledEcho styleBright, given + let alignedName = leftAlign(name, 72) # Make the total line length 78 chars + if success == reSuccess: + styledEcho fgCyan, alignedName, fgGreen, " [PASS]" + elif success == reIgnored: + styledEcho styleBright, fgCyan, alignedName, styleDim, fgYellow, " [SKIP]" + else: + styledEcho styleBright, fgCyan, alignedName, fgRed, " [FAIL]" + styledEcho styleBright, fgCyan, "Test \"", test.name, "\"", " in category \"", test.cat.string, "\"" + styledEcho styleBright, fgRed, "Failure: ", $success + styledEcho fgYellow, "Expected:" + styledEcho styleBright, expected, "\n" + styledEcho fgYellow, "Gotten:" + styledEcho styleBright, given, "\n" proc cmpMsgs(r: var TResults, expected, given: TSpec, test: TTest) = if strip(expected.msg) notin strip(given.msg): @@ -211,68 +219,100 @@ proc compilerOutputTests(test: TTest, given: var TSpec, expected: TSpec; if given.err == reSuccess: inc(r.passed) r.addResult(test, expectedmsg, givenmsg, given.err) +proc analyzeAndConsolidateOutput(s: string): string = + result = "" + let rows = s.splitLines + for i in 0 ..< rows.len: + if (let pos = find(rows[i], "Traceback (most recent call last)"); pos != -1): + result = substr(rows[i], pos) & "\n" + for i in i+1 ..< rows.len: + result.add rows[i] & "\n" + if not (rows[i] =~ re"^[^(]+\(\d+\)\s+"): + return + elif (let pos = find(rows[i], "SIGSEGV: Illegal storage access."); pos != -1): + result = substr(rows[i], pos) + return + proc testSpec(r: var TResults, test: TTest) = # major entry point for a single test let tname = test.name.addFileExt(".nim") inc(r.total) - styledEcho "Processing ", fgCyan, extractFilename(tname) var expected: TSpec if test.action != actionRunNoSpec: expected = parseSpec(tname) else: specDefaults expected expected.action = actionRunNoSpec + if expected.err == reIgnored: r.addResult(test, "", "", reIgnored) inc(r.skipped) - else: - case expected.action - of actionCompile: - var given = callCompiler(expected.cmd, test.name, - test.options & " --hint[Path]:off --hint[Processing]:off", test.target) - compilerOutputTests(test, given, expected, r) - of actionRun, actionRunNoSpec: - var given = callCompiler(expected.cmd, test.name, test.options, - test.target) - if given.err != reSuccess: - r.addResult(test, "", given.msg, given.err) - else: - var exeFile: string - if test.target == targetJS: - let (dir, file, ext) = splitFile(tname) - exeFile = dir / "nimcache" / file & ".js" - else: - exeFile = changeFileExt(tname, ExeExt) - if existsFile(exeFile): - let nodejs = findNodeJs() - if test.target == targetJS and nodejs == "": - r.addResult(test, expected.outp, "nodejs binary not in PATH", - reExeNotFound) - return - var (buf, exitCode) = execCmdEx( - (if test.target == targetJS: nodejs & " " else: "") & exeFile) - if exitCode != expected.exitCode: - r.addResult(test, "exitcode: " & $expected.exitCode, - "exitcode: " & $exitCode, reExitCodesDiffer) - else: - var bufB = strip(buf.string) - if expected.sortoutput: bufB = makeDeterministic(bufB) - if bufB != strip(expected.outp): - if not (expected.substr and expected.outp in bufB): - given.err = reOutputsDiffer - compilerOutputTests(test, given, expected, r) - else: - r.addResult(test, expected.outp, "executable not found", reExeNotFound) - of actionReject: - var given = callCompiler(expected.cmd, test.name, test.options, - test.target) - cmpMsgs(r, expected, given, test) + return + + case expected.action + of actionCompile: + var given = callCompiler(expected.cmd, test.name, + test.options & " --hint[Path]:off --hint[Processing]:off", test.target) + compilerOutputTests(test, given, expected, r) + of actionRun, actionRunNoSpec: + # In this branch of code "early return" pattern is clearer than deep + # nested conditionals - the empty rows in between to clarify the "danger" + var given = callCompiler(expected.cmd, test.name, test.options, + test.target) + + if given.err != reSuccess: + r.addResult(test, "", given.msg, given.err) + return + + let isJsTarget = test.target == targetJS + var exeFile: string + if isJsTarget: + let (dir, file, ext) = splitFile(tname) + exeFile = dir / "nimcache" / file & ".js" # *TODO* hardcoded "nimcache" + else: + exeFile = changeFileExt(tname, ExeExt) + + if not existsFile(exeFile): + r.addResult(test, expected.outp, "executable not found", reExeNotFound) + return + + let nodejs = if isJsTarget: findNodeJs() else: "" + if isJsTarget and nodejs == "": + r.addResult(test, expected.outp, "nodejs binary not in PATH", + reExeNotFound) + return + + let exeCmd = (if isJsTarget: nodejs & " " else: "") & exeFile + let (buf, exitCode) = execCmdEx(exeCmd) + let bufB = if expected.sortoutput: makeDeterministic(strip(buf.string)) + else: strip(buf.string) + + if exitCode != expected.exitCode: + r.addResult(test, "exitcode: " & $expected.exitCode, + "exitcode: " & $exitCode & "\n\nOutput:\n" & + analyzeAndConsolidateOutput(bufB), + reExitCodesDiffer) + return + + if bufB != strip(expected.outp): + if not (expected.substr and expected.outp in bufB): + given.err = reOutputsDiffer + r.addResult(test, expected.outp, bufB, reOutputsDiffer) + return + + compilerOutputTests(test, given, expected, r) + return + + of actionReject: + var given = callCompiler(expected.cmd, test.name, test.options, + test.target) + cmpMsgs(r, expected, given, test) + return proc testNoSpec(r: var TResults, test: TTest) = # does not extract the spec because the file is not supposed to have any let tname = test.name.addFileExt(".nim") inc(r.total) - styledEcho "Processing ", fgCyan, extractFilename(tname) let given = callCompiler(cmdTemplate, test.name, test.options, test.target) r.addResult(test, "", given.msg, given.err) if given.err == reSuccess: inc(r.passed) From 0a810fd10f81e11e6b738a18ff18e62c89479436 Mon Sep 17 00:00:00 2001 From: Oscar Campbell Date: Thu, 4 Jun 2015 22:53:39 +0200 Subject: [PATCH 04/18] Treat unitest's default esc.seq. output as empty. --- tests/testament/tester.nim | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/tests/testament/tester.nim b/tests/testament/tester.nim index ebfea591a7..2c4460395a 100644 --- a/tests/testament/tester.nim +++ b/tests/testament/tester.nim @@ -286,6 +286,7 @@ proc testSpec(r: var TResults, test: TTest) = let (buf, exitCode) = execCmdEx(exeCmd) let bufB = if expected.sortoutput: makeDeterministic(strip(buf.string)) else: strip(buf.string) + let expectedOut = strip(expected.outp) if exitCode != expected.exitCode: r.addResult(test, "exitcode: " & $expected.exitCode, @@ -294,11 +295,13 @@ proc testSpec(r: var TResults, test: TTest) = reExitCodesDiffer) return - if bufB != strip(expected.outp): - if not (expected.substr and expected.outp in bufB): - given.err = reOutputsDiffer - r.addResult(test, expected.outp, bufB, reOutputsDiffer) - return + # This is a bit ugly atm. + if bufB != expectedOut: + if not (expected.substr and expectedOut in bufB): + if not (expectedOut == "" and bufB == "\27[0m"): + given.err = reOutputsDiffer + r.addResult(test, expected.outp, bufB, reOutputsDiffer) + return compilerOutputTests(test, given, expected, r) return From c55f5dfca263788169c21fcff2dd78103a20e891 Mon Sep 17 00:00:00 2001 From: yglukhov Date: Wed, 27 May 2015 16:48:10 +0300 Subject: [PATCH 05/18] Perform lambda lifting for compile-time stuff when targeting JS. Enable unittest test. --- compiler/lambdalifting.nim | 6 +++++- lib/pure/unittest.nim | 9 ++++----- tests/js/tunittests.nim | 5 +---- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/compiler/lambdalifting.nim b/compiler/lambdalifting.nim index c68bc352cd..69b45c980d 100644 --- a/compiler/lambdalifting.nim +++ b/compiler/lambdalifting.nim @@ -946,7 +946,11 @@ proc transformOuterProc(o: POuterContext, n: PNode; it: TIter): PNode = proc liftLambdas*(fn: PSym, body: PNode): PNode = # XXX gCmd == cmdCompileToJS does not suffice! The compiletime stuff needs # the transformation even when compiling to JS ... - if body.kind == nkEmpty or gCmd == cmdCompileToJS or + + # However we can do lifting for the stuff which is *only* compiletime. + let isCompileTime = sfCompileTime in fn.flags or fn.kind == skMacro + + if body.kind == nkEmpty or (gCmd == cmdCompileToJS and not isCompileTime) or fn.skipGenericOwner.kind != skModule: # ignore forward declaration: result = body diff --git a/lib/pure/unittest.nim b/lib/pure/unittest.nim index 092b1fba26..dbbd3cabc1 100644 --- a/lib/pure/unittest.nim +++ b/lib/pure/unittest.nim @@ -99,8 +99,9 @@ template test*(name: expr, body: stmt): stmt {.immediate, dirty.} = body except: - checkpoint("Unhandled exception: " & getCurrentExceptionMsg()) - echo getCurrentException().getStackTrace() + when not defined(js): + checkpoint("Unhandled exception: " & getCurrentExceptionMsg()) + echo getCurrentException().getStackTrace() fail() finally: @@ -114,9 +115,7 @@ proc checkpoint*(msg: string) = template fail* = bind checkpoints for msg in items(checkpoints): - # this used to be 'echo' which now breaks due to a bug. XXX will revisit - # this issue later. - stdout.writeln msg + echo msg when not defined(ECMAScript): if abortOnError: quit(1) diff --git a/tests/js/tunittests.nim b/tests/js/tunittests.nim index 8a264a5e02..4b09c99a94 100644 --- a/tests/js/tunittests.nim +++ b/tests/js/tunittests.nim @@ -1,10 +1,7 @@ discard """ - disabled: "true" + output: '''[OK] >:)''' """ -# Unittest uses lambdalifting at compile-time which we disable for the JS -# codegen! So this cannot and will not work for quite some time. - import unittest suite "Bacon": From 4674b3eb8003b44656ae15ff0a5b7fc742714597 Mon Sep 17 00:00:00 2001 From: Adam Strzelecki Date: Tue, 2 Jun 2015 21:29:28 +0200 Subject: [PATCH 06/18] Introduce {.noRewrite.} expr pragma disabling TR Term rewriting macros/templates are currently greedy and they will rewrite as long as there is a match. So there was no way to ensure some rewrite happens only once, eg. when rewriting term to same term plus extra content. With new macro we can actually prevent further rewriting on marked expr or stmts, eg. with given example echo(...) will be rewritten just once: template pwnEcho{echo(x)}(x: expr) = {.noRewrite.}: echo("pwned!") echo "ab" --- compiler/ast.nim | 1 + compiler/patterns.nim | 4 +++- compiler/pragmas.nim | 4 +++- compiler/semstmts.nim | 2 ++ compiler/wordrecg.nim | 4 ++-- 5 files changed, 11 insertions(+), 4 deletions(-) diff --git a/compiler/ast.nim b/compiler/ast.nim index 044f213416..c349f81edb 100644 --- a/compiler/ast.nim +++ b/compiler/ast.nim @@ -423,6 +423,7 @@ type # but unfortunately it has measurable impact for compilation # efficiency nfTransf, # node has been transformed + nfNoRewrite # node should not be transformed anymore nfSem # node has been checked for semantics nfLL # node has gone through lambda lifting nfDotField # the call can use a dot operator diff --git a/compiler/patterns.nim b/compiler/patterns.nim index 368b0b37be..3f8b05940c 100644 --- a/compiler/patterns.nim +++ b/compiler/patterns.nim @@ -130,7 +130,9 @@ proc matchNested(c: PPatternContext, p, n: PNode, rpn: bool): bool = proc matches(c: PPatternContext, p, n: PNode): bool = # hidden conversions (?) - if isPatternParam(c, p): + if nfNoRewrite in n.flags: + result = false + elif isPatternParam(c, p): result = bindOrCheck(c, p.sym, n) elif n.kind == nkSym and p.kind == nkIdent: result = p.ident.id == n.sym.name.id diff --git a/compiler/pragmas.nim b/compiler/pragmas.nim index c048d78e9e..6f37fe756b 100644 --- a/compiler/pragmas.nim +++ b/compiler/pragmas.nim @@ -37,7 +37,7 @@ const wImportc, wExportc, wNodecl, wMagic, wDeprecated, wBorrow, wExtern, wImportCpp, wImportObjC, wError, wDiscardable, wGensym, wInject, wRaises, wTags, wLocks, wGcSafe} - exprPragmas* = {wLine, wLocks} + exprPragmas* = {wLine, wLocks, wNoRewrite} stmtPragmas* = {wChecks, wObjChecks, wFieldChecks, wRangechecks, wBoundchecks, wOverflowchecks, wNilchecks, wAssertions, wWarnings, wHints, wLinedir, wStacktrace, wLinetrace, wOptimization, wHint, wWarning, wError, @@ -859,6 +859,8 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: int, c.module.flags.incl sfExperimental else: localError(it.info, "'experimental' pragma only valid as toplevel statement") + of wNoRewrite: + noVal(it) else: invalidPragma(it) else: invalidPragma(it) else: processNote(c, it) diff --git a/compiler/semstmts.nim b/compiler/semstmts.nim index c355a5bf11..43cdca8669 100644 --- a/compiler/semstmts.nim +++ b/compiler/semstmts.nim @@ -1268,6 +1268,8 @@ proc semPragmaBlock(c: PContext, n: PNode): PNode = of wLocks: result = n result.typ = n.sons[1].typ + of wNoRewrite: + incl(result.flags, nfNoRewrite) else: discard proc semStaticStmt(c: PContext, n: PNode): PNode = diff --git a/compiler/wordrecg.nim b/compiler/wordrecg.nim index 63fd995c46..deb12536f8 100644 --- a/compiler/wordrecg.nim +++ b/compiler/wordrecg.nim @@ -55,7 +55,7 @@ type wFloatchecks, wNanChecks, wInfChecks, wAssertions, wPatterns, wWarnings, wHints, wOptimization, wRaises, wWrites, wReads, wSize, wEffects, wTags, - wDeadCodeElim, wSafecode, wNoForward, + wDeadCodeElim, wSafecode, wNoForward, wNoRewrite, wPragma, wCompileTime, wNoInit, wPassc, wPassl, wBorrow, wDiscardable, @@ -139,7 +139,7 @@ const "assertions", "patterns", "warnings", "hints", "optimization", "raises", "writes", "reads", "size", "effects", "tags", - "deadcodeelim", "safecode", "noforward", + "deadcodeelim", "safecode", "noforward", "norewrite", "pragma", "compiletime", "noinit", "passc", "passl", "borrow", "discardable", "fieldchecks", From 07c40add8fbc3b4c248c1403f903617a6bdb027e Mon Sep 17 00:00:00 2001 From: Adam Strzelecki Date: Tue, 2 Jun 2015 21:51:41 +0200 Subject: [PATCH 07/18] Parser: Properly represent parsePar in grammar --- compiler/parser.nim | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/compiler/parser.nim b/compiler/parser.nim index 0d2ba7cfc0..18ef06e64d 100644 --- a/compiler/parser.nim +++ b/compiler/parser.nim @@ -499,10 +499,12 @@ proc parsePar(p: var TParser): PNode = #| parKeyw = 'discard' | 'include' | 'if' | 'while' | 'case' | 'try' #| | 'finally' | 'except' | 'for' | 'block' | 'const' | 'let' #| | 'when' | 'var' | 'mixin' - #| par = '(' optInd (&parKeyw complexOrSimpleStmt ^+ ';' - #| | simpleExpr ('=' expr (';' complexOrSimpleStmt ^+ ';' )? )? - #| | (':' expr)? (',' (exprColonEqExpr comma?)*)? )? - #| optPar ')' + #| par = '(' optInd + #| ( &parKeyw complexOrSimpleStmt ^+ ';' + #| | ';' complexOrSimpleStmt ^+ ';' + #| | simpleExpr ( ('=' expr (';' complexOrSimpleStmt ^+ ';' )? ) + #| | (':' expr (',' exprColonEqExpr ^+ ',' )? ) ) ) + #| optPar ')' # # unfortunately it's ambiguous: (expr: expr) vs (exprStmt); however a # leading ';' could be used to enforce a 'stmt' context ... From 83369f4bce15d6d8f4d0bd9a7cb17b2def6daa54 Mon Sep 17 00:00:00 2001 From: Adam Strzelecki Date: Tue, 2 Jun 2015 21:56:44 +0200 Subject: [PATCH 08/18] Parser: Inline expr pragmas with parenthesis Previously pragmas could be attached only to whole statements, this change allows attaching pragmas to inline statements, eg.: template rewriteAdd{a + b}(a: expr, b: expr): expr = ({.noRewrite.}: a + b) + 1 Code above will cause a + b to be rewritten once, because rewriteAdd attaches {.noRewrite.} to resulting a + b expr. --- compiler/parser.nim | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/compiler/parser.nim b/compiler/parser.nim index 18ef06e64d..05b4df13de 100644 --- a/compiler/parser.nim +++ b/compiler/parser.nim @@ -64,6 +64,7 @@ proc setBaseFlags*(n: PNode, base: TNumericalBase) proc parseSymbol*(p: var TParser, allowNil = false): PNode proc parseTry(p: var TParser; isExpr: bool): PNode proc parseCase(p: var TParser): PNode +proc parseStmtPragma(p: var TParser): PNode # implementation proc getTok(p: var TParser) = @@ -502,6 +503,7 @@ proc parsePar(p: var TParser): PNode = #| par = '(' optInd #| ( &parKeyw complexOrSimpleStmt ^+ ';' #| | ';' complexOrSimpleStmt ^+ ';' + #| | pragmaStmt #| | simpleExpr ( ('=' expr (';' complexOrSimpleStmt ^+ ';' )? ) #| | (':' expr (',' exprColonEqExpr ^+ ',' )? ) ) ) #| optPar ')' @@ -523,6 +525,8 @@ proc parsePar(p: var TParser): PNode = getTok(p) optInd(p, result) semiStmtList(p, result) + elif p.tok.tokType == tkCurlyDotLe: + result.add(parseStmtPragma(p)) elif p.tok.tokType != tkParRi: var a = simpleExpr(p) if p.tok.tokType == tkEquals: From e132032b0f3af7fd03dd35b68a795c4dbc25ddfc Mon Sep 17 00:00:00 2001 From: apense Date: Thu, 4 Jun 2015 13:31:40 -0400 Subject: [PATCH 09/18] Fixed code indentation in procs.txt Now matches tut2.txt --- doc/manual/procs.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/manual/procs.txt b/doc/manual/procs.txt index 38e343686b..23b5e4d1e9 100644 --- a/doc/manual/procs.txt +++ b/doc/manual/procs.txt @@ -404,7 +404,7 @@ dispatch. result.a = a result.b = b -echo eval(newPlus(newPlus(newLit(1), newLit(2)), newLit(4))) + echo eval(newPlus(newPlus(newLit(1), newLit(2)), newLit(4))) In the example the constructors ``newLit`` and ``newPlus`` are procs because they should use static binding, but ``eval`` is a method because it From c126b8ad0f1c3b9749819737c9fd02fb82fd4497 Mon Sep 17 00:00:00 2001 From: apense Date: Thu, 4 Jun 2015 16:18:18 -0400 Subject: [PATCH 10/18] Fixed links in Error hierarchy Manual contained invalid links --- doc/exception_hierarchy_fragment.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/exception_hierarchy_fragment.txt b/doc/exception_hierarchy_fragment.txt index f4a419fc49..a02d9ccefa 100644 --- a/doc/exception_hierarchy_fragment.txt +++ b/doc/exception_hierarchy_fragment.txt @@ -11,8 +11,8 @@ * `FloatInvalidOpError `_ * `FloatOverflowError `_ * `FloatUnderflowError `_ - * `FieldError `_ - * `IndexError `_ + * `FieldError `_ + * `IndexError `_ * `ObjectAssignmentError `_ * `ObjectConversionError `_ * `ValueError `_ From 25c6304abbd855cd5b7f45227fcf44d1cb24490e Mon Sep 17 00:00:00 2001 From: Dominik Picheta Date: Thu, 4 Jun 2015 23:07:57 +0100 Subject: [PATCH 11/18] Hopefully fixes nimsuggest building against the compiler package. --- compiler/nimfix/nimfix.nim | 14 ++++++++------ compiler/nimfix/pretty.nim | 20 +++++++++++--------- compiler/nimfix/prettybase.nim | 7 ++++--- compiler/plugins/locals/locals.nim | 3 ++- 4 files changed, 25 insertions(+), 19 deletions(-) diff --git a/compiler/nimfix/nimfix.nim b/compiler/nimfix/nimfix.nim index 8caa23ee3d..3641aec36a 100644 --- a/compiler/nimfix/nimfix.nim +++ b/compiler/nimfix/nimfix.nim @@ -10,8 +10,10 @@ ## Nimfix is a tool that helps to convert old-style Nimrod code to Nim code. import strutils, os, parseopt -import options, commands, modules, sem, passes, passaux, pretty, msgs, nimconf, - extccomp, condsyms, lists +import compiler/options, compiler/commands, compiler/modules, compiler/sem, + compiler/passes, compiler/passaux, compiler/nimfix/pretty, + compiler/msgs, compiler/nimconf, + compiler/extccomp, compiler/condsyms, compiler/lists const Usage = """ Nimfix - Tool to patch Nim code @@ -24,7 +26,7 @@ Options: --wholeProject overwrite every processed file. --checkExtern:on|off style check also extern names --styleCheck:on|off|auto performs style checking for identifiers - and suggests an alternative spelling; + and suggests an alternative spelling; 'auto' corrects the spelling. --bestEffort try to fix the code even when there are errors. @@ -48,11 +50,11 @@ proc processCmdLine*(pass: TCmdLinePass, cmd: string) = var p = parseopt.initOptParser(cmd) var argsCount = 0 gOnlyMainfile = true - while true: + while true: parseopt.next(p) case p.kind - of cmdEnd: break - of cmdLongoption, cmdShortOption: + of cmdEnd: break + of cmdLongoption, cmdShortOption: case p.key.normalize of "overwritefiles": case p.val.normalize diff --git a/compiler/nimfix/pretty.nim b/compiler/nimfix/pretty.nim index d2d5b5e83c..1123afb9e5 100644 --- a/compiler/nimfix/pretty.nim +++ b/compiler/nimfix/pretty.nim @@ -10,9 +10,11 @@ ## This module implements the code "prettifier". This is part of the toolchain ## to convert Nim code into a consistent style. -import - strutils, os, options, ast, astalgo, msgs, ropes, idents, - intsets, strtabs, semdata, prettybase +import + strutils, os, intsets, strtabs + +import compiler/options, compiler/ast, compiler/astalgo, compiler/msgs, + compiler/semdata, compiler/nimfix/prettybase, compiler/ropes, compiler/idents type StyleCheck* {.pure.} = enum None, Warn, Auto @@ -92,7 +94,7 @@ proc beautifyName(s: string, k: TSymKind): string = proc replaceInFile(info: TLineInfo; newName: string) = loadFile(info) - + let line = gSourceFiles[info.fileIndex].lines[info.line-1] var first = min(info.col.int, line.len) if first < 0: return @@ -100,18 +102,18 @@ proc replaceInFile(info: TLineInfo; newName: string) = while first > 0 and line[first-1] in prettybase.Letters: dec first if first < 0: return if line[first] == '`': inc first - + let last = first+identLen(line, first)-1 if differ(line, first, last, newName): - # last-first+1 != newName.len or - var x = line.substr(0, first-1) & newName & line.substr(last+1) + # last-first+1 != newName.len or + var x = line.substr(0, first-1) & newName & line.substr(last+1) system.shallowCopy(gSourceFiles[info.fileIndex].lines[info.line-1], x) gSourceFiles[info.fileIndex].dirty = true proc checkStyle(info: TLineInfo, s: string, k: TSymKind; sym: PSym) = let beau = beautifyName(s, k) if s != beau: - if gStyleCheck == StyleCheck.Auto: + if gStyleCheck == StyleCheck.Auto: sym.name = getIdent(beau) replaceInFile(info, beau) else: @@ -137,7 +139,7 @@ proc styleCheckUseImpl(info: TLineInfo; s: PSym) = if info.fileIndex < 0: return # we simply convert it to what it looks like in the definition # for consistency - + # operators stay as they are: if s.kind in {skResult, skTemp} or s.name.s[0] notin prettybase.Letters: return diff --git a/compiler/nimfix/prettybase.nim b/compiler/nimfix/prettybase.nim index 5130d1863c..0f17cbcb19 100644 --- a/compiler/nimfix/prettybase.nim +++ b/compiler/nimfix/prettybase.nim @@ -7,7 +7,8 @@ # distribution, for details about the copyright. # -import ast, msgs, strutils, idents, lexbase, streams +import strutils, lexbase, streams +import compiler/ast, compiler/msgs, compiler/idents from os import splitFile type @@ -39,7 +40,7 @@ proc loadFile*(info: TLineInfo) = var pos = lex.bufpos while true: case lex.buf[pos] - of '\c': + of '\c': gSourceFiles[i].newline = "\c\L" break of '\L', '\0': @@ -70,7 +71,7 @@ proc replaceDeprecated*(info: TLineInfo; oldSym, newSym: PIdent) = while first > 0 and line[first-1] in Letters: dec first if first < 0: return if line[first] == '`': inc first - + let last = first+identLen(line, first)-1 if cmpIgnoreStyle(line[first..last], oldSym.s) == 0: var x = line.substr(0, first-1) & newSym.s & line.substr(last+1) diff --git a/compiler/plugins/locals/locals.nim b/compiler/plugins/locals/locals.nim index d89149f338..59e3d677d4 100644 --- a/compiler/plugins/locals/locals.nim +++ b/compiler/plugins/locals/locals.nim @@ -9,7 +9,8 @@ ## The builtin 'system.locals' implemented as a plugin. -import plugins, ast, astalgo, magicsys, lookups, semdata, lowerings +import compiler/plugins, compiler/ast, compiler/astalgo, compiler/magicsys, + compiler/lookups, compiler/semdata, compiler/lowerings proc semLocals(c: PContext, n: PNode): PNode = var counter = 0 From 52192ffa9f1436b6be15aae6ad7f5a63b49d5b03 Mon Sep 17 00:00:00 2001 From: Dominik Picheta Date: Thu, 4 Jun 2015 23:12:38 +0100 Subject: [PATCH 12/18] Fixes compilation with --gc:markandsweep. --- lib/system/gc_ms.nim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/system/gc_ms.nim b/lib/system/gc_ms.nim index aab28eba23..ee80c61e9c 100644 --- a/lib/system/gc_ms.nim +++ b/lib/system/gc_ms.nim @@ -58,7 +58,7 @@ type stat: GcStat additionalRoots: CellSeq # dummy roots for GC_ref/unref {.deprecated: [TWalkOp: WalkOp, TFinalizer: Finalizer, TGcStat: GcStat, - TGlobalMarkerProc: GlobalMarkerProc, TGcHeap, GcHeap].} + TGlobalMarkerProc: GlobalMarkerProc, TGcHeap: GcHeap].} var gch {.rtlThreadVar.}: GcHeap From 4a7c0e887211d5697c03f147141605e0f46d0736 Mon Sep 17 00:00:00 2001 From: Dominik Picheta Date: Thu, 4 Jun 2015 23:23:41 +0100 Subject: [PATCH 13/18] Add WSAEADDRINUSE to winlean. --- lib/windows/winlean.nim | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/windows/winlean.nim b/lib/windows/winlean.nim index 39f2be61da..9f04bab355 100644 --- a/lib/windows/winlean.nim +++ b/lib/windows/winlean.nim @@ -690,6 +690,7 @@ const ERROR_IO_PENDING* = 997 # a.k.a WSA_IO_PENDING FILE_FLAG_OVERLAPPED* = 1073741824 WSAECONNABORTED* = 10053 + WSAEADDRINUSE* = 10048 WSAECONNRESET* = 10054 WSAEDISCON* = 10101 WSAENETRESET* = 10052 From 4b0089bf58fb0922da0322745a8e1cd957fe0240 Mon Sep 17 00:00:00 2001 From: Dominik Picheta Date: Thu, 4 Jun 2015 23:25:29 +0100 Subject: [PATCH 14/18] Removed nimsuggest from this repo. --- compiler/nimsuggest/nimsuggest.nim | 328 +------------------------ compiler/nimsuggest/nimsuggest.nim.cfg | 17 -- 2 files changed, 2 insertions(+), 343 deletions(-) delete mode 100644 compiler/nimsuggest/nimsuggest.nim.cfg diff --git a/compiler/nimsuggest/nimsuggest.nim b/compiler/nimsuggest/nimsuggest.nim index 2c785d118b..2be368d680 100644 --- a/compiler/nimsuggest/nimsuggest.nim +++ b/compiler/nimsuggest/nimsuggest.nim @@ -7,330 +7,6 @@ # distribution, for details about the copyright. # -## Nimsuggest is a tool that helps to give editors IDE like capabilities. +## Nimsuggest has been moved to https://github.com/nim-lang/nimsuggest -import strutils, os, parseopt, parseutils, sequtils, net -# Do NOT import suggest. It will lead to wierd bugs with -# suggestionResultHook, because suggest.nim is included by sigmatch. -# So we import that one instead. -import options, commands, modules, sem, passes, passaux, msgs, nimconf, - extccomp, condsyms, lists, net, rdstdin, sexp, sigmatch, ast - -when defined(windows): - import winlean -else: - import posix - -const Usage = """ -Nimsuggest - Tool to give every editor IDE like capabilities for Nim -Usage: - nimsuggest [options] projectfile.nim - -Options: - --port:PORT port, by default 6000 - --address:HOST binds to that address, by default "" - --stdin read commands from stdin and write results to - stdout instead of using sockets - --epc use emacs epc mode - -The server then listens to the connection and takes line-based commands. - -In addition, all command line options of Nim that do not affect code generation -are supported. -""" -type - Mode = enum mstdin, mtcp, mepc - -var - gPort = 6000.Port - gAddress = "" - gMode: Mode - -const - seps = {':', ';', ' ', '\t'} - Help = "usage: sug|con|def|use file.nim[;dirtyfile.nim]:line:col\n"& - "type 'quit' to quit\n" & - "type 'debug' to toggle debug mode on/off\n" & - "type 'terse' to toggle terse mode on/off" - -type - EUnexpectedCommand = object of Exception - -proc parseQuoted(cmd: string; outp: var string; start: int): int = - var i = start - i += skipWhitespace(cmd, i) - if cmd[i] == '"': - i += parseUntil(cmd, outp, '"', i+1)+2 - else: - i += parseUntil(cmd, outp, seps, i) - result = i - -proc sexp(s: IdeCmd): SexpNode = sexp($s) - -proc sexp(s: TSymKind): SexpNode = sexp($s) - -proc sexp(s: Suggest): SexpNode = - # If you change the oder here, make sure to change it over in - # nim-mode.el too. - result = convertSexp([ - s.section, - s.symkind, - s.qualifiedPath.map(newSString), - s.filePath, - s.forth, - s.line, - s.column, - s.doc - ]) - -proc sexp(s: seq[Suggest]): SexpNode = - result = newSList() - for sug in s: - result.add(sexp(sug)) - -proc listEPC(): SexpNode = - let - argspecs = sexp("file line column dirtyfile".split(" ").map(newSSymbol)) - docstring = sexp("line starts at 1, column at 0, dirtyfile is optional") - result = newSList() - for command in ["sug", "con", "def", "use"]: - let - cmd = sexp(command) - methodDesc = newSList() - methodDesc.add(cmd) - methodDesc.add(argspecs) - methodDesc.add(docstring) - result.add(methodDesc) - -proc execute(cmd: IdeCmd, file, dirtyfile: string, line, col: int) = - gIdeCmd = cmd - if cmd == ideUse: - modules.resetAllModules() - var isKnownFile = true - let dirtyIdx = file.fileInfoIdx(isKnownFile) - - if dirtyfile.len != 0: msgs.setDirtyFile(dirtyIdx, dirtyfile) - else: msgs.setDirtyFile(dirtyIdx, nil) - - resetModule dirtyIdx - if dirtyIdx != gProjectMainIdx: - resetModule gProjectMainIdx - - gTrackPos = newLineInfo(dirtyIdx, line, col) - gErrorCounter = 0 - if not isKnownFile: - compileProject() - compileProject(dirtyIdx) - -proc executeEPC(cmd: IdeCmd, args: SexpNode) = - let - file = args[0].getStr - line = args[1].getNum - column = args[2].getNum - var dirtyfile = "" - if len(args) > 3: - dirtyfile = args[3].getStr(nil) - execute(cmd, file, dirtyfile, int(line), int(column)) - -proc returnEPC(socket: var Socket, uid: BiggestInt, s: SexpNode, return_symbol = "return") = - let response = $convertSexp([newSSymbol(return_symbol), uid, s]) - socket.send(toHex(len(response), 6)) - socket.send(response) - -proc connectToNextFreePort(server: Socket, host: string, start = 30000): int = - result = start - while true: - try: - server.bindaddr(Port(result), host) - return - except OsError: - when defined(windows): - let checkFor = WSAEADDRINUSE.OSErrorCode - else: - let checkFor = EADDRINUSE.OSErrorCode - if osLastError() != checkFor: - raise getCurrentException() - else: - result += 1 - -proc parseCmdLine(cmd: string) = - template toggle(sw) = - if sw in gGlobalOptions: - excl(gGlobalOptions, sw) - else: - incl(gGlobalOptions, sw) - return - - template err() = - echo Help - return - - var opc = "" - var i = parseIdent(cmd, opc, 0) - case opc.normalize - of "sug": gIdeCmd = ideSug - of "con": gIdeCmd = ideCon - of "def": gIdeCmd = ideDef - of "use": gIdeCmd = ideUse - of "quit": quit() - of "debug": toggle optIdeDebug - of "terse": toggle optIdeTerse - else: err() - var dirtyfile = "" - var orig = "" - i = parseQuoted(cmd, orig, i) - if cmd[i] == ';': - i = parseQuoted(cmd, dirtyfile, i+1) - i += skipWhile(cmd, seps, i) - var line = -1 - var col = 0 - i += parseInt(cmd, line, i) - i += skipWhile(cmd, seps, i) - i += parseInt(cmd, col, i) - - execute(gIdeCmd, orig, dirtyfile, line, col-1) - -proc serve() = - case gMode: - of mstdin: - echo Help - var line = "" - while readLineFromStdin("> ", line): - parseCmdLine line - echo "" - flushFile(stdout) - of mtcp: - var server = newSocket() - server.bindAddr(gPort, gAddress) - var inp = "".TaintedString - server.listen() - - while true: - var stdoutSocket = newSocket() - msgs.writelnHook = proc (line: string) = - stdoutSocket.send(line & "\c\L") - - accept(server, stdoutSocket) - - stdoutSocket.readLine(inp) - parseCmdLine inp.string - - stdoutSocket.send("\c\L") - stdoutSocket.close() - of mepc: - var server = newSocket() - let port = connectToNextFreePort(server, "localhost") - var inp = "".TaintedString - server.listen() - echo(port) - var client = newSocket() - # Wait for connection - accept(server, client) - while true: - var sizeHex = "" - if client.recv(sizeHex, 6) != 6: - raise newException(ValueError, "didn't get all the hexbytes") - var size = 0 - if parseHex(sizeHex, size) == 0: - raise newException(ValueError, "invalid size hex: " & $sizeHex) - var messageBuffer = "" - if client.recv(messageBuffer, size) != size: - raise newException(ValueError, "didn't get all the bytes") - let - message = parseSexp($messageBuffer) - messageType = message[0].getSymbol - case messageType: - of "call": - var results: seq[Suggest] = @[] - suggestionResultHook = proc (s: Suggest) = - results.add(s) - - let - uid = message[1].getNum - cmd = parseIdeCmd(message[2].getSymbol) - args = message[3] - executeEPC(cmd, args) - returnEPC(client, uid, sexp(results)) - of "return": - raise newException(EUnexpectedCommand, "no return expected") - of "return-error": - raise newException(EUnexpectedCommand, "no return expected") - of "epc-error": - stderr.writeln("recieved epc error: " & $messageBuffer) - raise newException(IOError, "epc error") - of "methods": - returnEPC(client, message[1].getNum, listEPC()) - else: - raise newException(EUnexpectedCommand, "unexpected call: " & messageType) - -proc mainCommand = - registerPass verbosePass - registerPass semPass - gCmd = cmdIdeTools - incl gGlobalOptions, optCaasEnabled - isServing = true - wantMainModule() - appendStr(searchPaths, options.libpath) - if gProjectFull.len != 0: - # current path is always looked first for modules - prependStr(searchPaths, gProjectPath) - - # do not stop after the first error: - msgs.gErrorMax = high(int) - compileProject() - serve() - -proc processCmdLine*(pass: TCmdLinePass, cmd: string) = - var p = parseopt.initOptParser(cmd) - while true: - parseopt.next(p) - case p.kind - of cmdEnd: break - of cmdLongoption, cmdShortOption: - case p.key.normalize - of "port": - gPort = parseInt(p.val).Port - gMode = mtcp - of "address": - gAddress = p.val - gMode = mtcp - of "stdin": gMode = mstdin - of "epc": - gMode = mepc - gVerbosity = 0 # Port number gotta be first. - else: processSwitch(pass, p) - of cmdArgument: - options.gProjectName = unixToNativePath(p.key) - # if processArgument(pass, p, argsCount): break - -proc handleCmdLine() = - if paramCount() == 0: - stdout.writeln(Usage) - else: - processCmdLine(passCmd1, "") - if gProjectName != "": - try: - gProjectFull = canonicalizePath(gProjectName) - except OSError: - gProjectFull = gProjectName - var p = splitFile(gProjectFull) - gProjectPath = p.dir - gProjectName = p.name - else: - gProjectPath = getCurrentDir() - loadConfigs(DefaultConfig) # load all config files - # now process command line arguments again, because some options in the - # command line can overwite the config file's settings - extccomp.initVars() - processCmdLine(passCmd2, "") - mainCommand() - -when false: - proc quitCalled() {.noconv.} = - writeStackTrace() - - addQuitProc(quitCalled) - -condsyms.initDefines() -defineSymbol "nimsuggest" -handleCmdline() +{.error: "This project has moved to the following repo: https://github.com/nim-lang/nimsuggest".} diff --git a/compiler/nimsuggest/nimsuggest.nim.cfg b/compiler/nimsuggest/nimsuggest.nim.cfg deleted file mode 100644 index acca173968..0000000000 --- a/compiler/nimsuggest/nimsuggest.nim.cfg +++ /dev/null @@ -1,17 +0,0 @@ -# Special configuration file for the Nim project - -gc:markAndSweep - -hint[XDeclaredButNotUsed]:off -path:"$projectPath/../.." - -path:"$lib/packages/docutils" -path:"../../compiler" - -define:useStdoutAsStdmsg -define:nimsuggest - -cs:partial -#define:useNodeIds -define:booting -#define:noDocgen From 25cc9befd538ec92938c9030d2e68658514f358c Mon Sep 17 00:00:00 2001 From: Adam Strzelecki Date: Fri, 5 Jun 2015 12:44:28 +0200 Subject: [PATCH 15/18] Fixup: Reverted TZipFileStream name change This fixes broken b0469c11e334e96cebe53cbe804b6a877831c85a that incompletely reverted TZipFileStream name change. --- lib/impure/zipfiles.nim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/impure/zipfiles.nim b/lib/impure/zipfiles.nim index d66d7d7f34..d8903f5c11 100644 --- a/lib/impure/zipfiles.nim +++ b/lib/impure/zipfiles.nim @@ -114,7 +114,7 @@ type atEnd: bool PZipFileStream* = - ref ZipFileStream ## a reader stream of a file within a zip archive + ref TZipFileStream ## a reader stream of a file within a zip archive proc fsClose(s: Stream) = zip_fclose(PZipFileStream(s).f) proc fsAtEnd(s: Stream): bool = PZipFileStream(s).atEnd From cb443ce73d8d451fc8a4168f035ec85350b349c7 Mon Sep 17 00:00:00 2001 From: Simon Hafner Date: Fri, 5 Jun 2015 11:04:15 -0500 Subject: [PATCH 16/18] moved sexp.nim to nimsuggest repo --- lib/pure/sexp.nim | 698 ---------------------------------------------- 1 file changed, 698 deletions(-) delete mode 100644 lib/pure/sexp.nim diff --git a/lib/pure/sexp.nim b/lib/pure/sexp.nim deleted file mode 100644 index be6188c9e1..0000000000 --- a/lib/pure/sexp.nim +++ /dev/null @@ -1,698 +0,0 @@ -# -# -# Nim's Runtime Library -# (c) Copyright 2015 Andreas Rumpf, Dominik Picheta -# -# See the file "copying.txt", included in this -# distribution, for details about the copyright. -# - -import - hashes, strutils, lexbase, streams, unicode, macros - -type - SexpEventKind* = enum ## enumeration of all events that may occur when parsing - sexpError, ## an error occurred during parsing - sexpEof, ## end of file reached - sexpString, ## a string literal - sexpSymbol, ## a symbol - sexpInt, ## an integer literal - sexpFloat, ## a float literal - sexpNil, ## the value ``nil`` - sexpDot, ## the dot to separate car/cdr - sexpListStart, ## start of a list: the ``(`` token - sexpListEnd, ## end of a list: the ``)`` token - - TokKind = enum # must be synchronized with SexpEventKind! - tkError, - tkEof, - tkString, - tkSymbol, - tkInt, - tkFloat, - tkNil, - tkDot, - tkParensLe, - tkParensRi - tkSpace - - SexpError* = enum ## enumeration that lists all errors that can occur - errNone, ## no error - errInvalidToken, ## invalid token - errParensRiExpected, ## ``)`` expected - errQuoteExpected, ## ``"`` expected - errEofExpected, ## EOF expected - - SexpParser* = object of BaseLexer ## the parser object. - a: string - tok: TokKind - kind: SexpEventKind - err: SexpError -{.deprecated: [TTokKind: TokKind].} - -const - errorMessages: array [SexpError, string] = [ - "no error", - "invalid token", - "')' expected", - "'\"' or \"'\" expected", - "EOF expected", - ] - tokToStr: array [TokKind, string] = [ - "invalid token", - "EOF", - "string literal", - "symbol", - "int literal", - "float literal", - "nil", - ".", - "(", ")", "space" - ] - -proc close*(my: var SexpParser) {.inline.} = - ## closes the parser `my` and its associated input stream. - lexbase.close(my) - -proc str*(my: SexpParser): string {.inline.} = - ## returns the character data for the events: ``sexpInt``, ``sexpFloat``, - ## ``sexpString`` - assert(my.kind in {sexpInt, sexpFloat, sexpString}) - result = my.a - -proc getInt*(my: SexpParser): BiggestInt {.inline.} = - ## returns the number for the event: ``sexpInt`` - assert(my.kind == sexpInt) - result = parseBiggestInt(my.a) - -proc getFloat*(my: SexpParser): float {.inline.} = - ## returns the number for the event: ``sexpFloat`` - assert(my.kind == sexpFloat) - result = parseFloat(my.a) - -proc kind*(my: SexpParser): SexpEventKind {.inline.} = - ## returns the current event type for the SEXP parser - result = my.kind - -proc getColumn*(my: SexpParser): int {.inline.} = - ## get the current column the parser has arrived at. - result = getColNumber(my, my.bufpos) - -proc getLine*(my: SexpParser): int {.inline.} = - ## get the current line the parser has arrived at. - result = my.lineNumber - -proc errorMsg*(my: SexpParser): string = - ## returns a helpful error message for the event ``sexpError`` - assert(my.kind == sexpError) - result = "($1, $2) Error: $3" % [$getLine(my), $getColumn(my), errorMessages[my.err]] - -proc errorMsgExpected*(my: SexpParser, e: string): string = - ## returns an error message "`e` expected" in the same format as the - ## other error messages - result = "($1, $2) Error: $3" % [$getLine(my), $getColumn(my), e & " expected"] - -proc handleHexChar(c: char, x: var int): bool = - result = true # Success - case c - of '0'..'9': x = (x shl 4) or (ord(c) - ord('0')) - of 'a'..'f': x = (x shl 4) or (ord(c) - ord('a') + 10) - of 'A'..'F': x = (x shl 4) or (ord(c) - ord('A') + 10) - else: result = false # error - -proc parseString(my: var SexpParser): TokKind = - result = tkString - var pos = my.bufpos + 1 - var buf = my.buf - while true: - case buf[pos] - of '\0': - my.err = errQuoteExpected - result = tkError - break - of '"': - inc(pos) - break - of '\\': - case buf[pos+1] - of '\\', '"', '\'', '/': - add(my.a, buf[pos+1]) - inc(pos, 2) - of 'b': - add(my.a, '\b') - inc(pos, 2) - of 'f': - add(my.a, '\f') - inc(pos, 2) - of 'n': - add(my.a, '\L') - inc(pos, 2) - of 'r': - add(my.a, '\C') - inc(pos, 2) - of 't': - add(my.a, '\t') - inc(pos, 2) - of 'u': - inc(pos, 2) - var r: int - if handleHexChar(buf[pos], r): inc(pos) - if handleHexChar(buf[pos], r): inc(pos) - if handleHexChar(buf[pos], r): inc(pos) - if handleHexChar(buf[pos], r): inc(pos) - add(my.a, toUTF8(Rune(r))) - else: - # don't bother with the error - add(my.a, buf[pos]) - inc(pos) - of '\c': - pos = lexbase.handleCR(my, pos) - buf = my.buf - add(my.a, '\c') - of '\L': - pos = lexbase.handleLF(my, pos) - buf = my.buf - add(my.a, '\L') - else: - add(my.a, buf[pos]) - inc(pos) - my.bufpos = pos # store back - -proc parseNumber(my: var SexpParser) = - var pos = my.bufpos - var buf = my.buf - if buf[pos] == '-': - add(my.a, '-') - inc(pos) - if buf[pos] == '.': - add(my.a, "0.") - inc(pos) - else: - while buf[pos] in Digits: - add(my.a, buf[pos]) - inc(pos) - if buf[pos] == '.': - add(my.a, '.') - inc(pos) - # digits after the dot: - while buf[pos] in Digits: - add(my.a, buf[pos]) - inc(pos) - if buf[pos] in {'E', 'e'}: - add(my.a, buf[pos]) - inc(pos) - if buf[pos] in {'+', '-'}: - add(my.a, buf[pos]) - inc(pos) - while buf[pos] in Digits: - add(my.a, buf[pos]) - inc(pos) - my.bufpos = pos - -proc parseSymbol(my: var SexpParser) = - var pos = my.bufpos - var buf = my.buf - if buf[pos] in IdentStartChars: - while buf[pos] in IdentChars: - add(my.a, buf[pos]) - inc(pos) - my.bufpos = pos - -proc getTok(my: var SexpParser): TokKind = - setLen(my.a, 0) - case my.buf[my.bufpos] - of '-', '0'..'9': # numbers that start with a . are not parsed - # correctly. - parseNumber(my) - if {'.', 'e', 'E'} in my.a: - result = tkFloat - else: - result = tkInt - of '"': #" # gotta fix nim-mode - result = parseString(my) - of '(': - inc(my.bufpos) - result = tkParensLe - of ')': - inc(my.bufpos) - result = tkParensRi - of '\0': - result = tkEof - of 'a'..'z', 'A'..'Z', '_': - parseSymbol(my) - if my.a == "nil": - result = tkNil - else: - result = tkSymbol - of ' ': - result = tkSpace - inc(my.bufpos) - of '.': - result = tkDot - inc(my.bufpos) - else: - inc(my.bufpos) - result = tkError - my.tok = result - -# ------------- higher level interface --------------------------------------- - -type - SexpNodeKind* = enum ## possible SEXP node types - SNil, - SInt, - SFloat, - SString, - SSymbol, - SList, - SCons - - SexpNode* = ref SexpNodeObj ## SEXP node - SexpNodeObj* {.acyclic.} = object - case kind*: SexpNodeKind - of SString: - str*: string - of SSymbol: - symbol*: string - of SInt: - num*: BiggestInt - of SFloat: - fnum*: float - of SList: - elems*: seq[SexpNode] - of SCons: - car: SexpNode - cdr: SexpNode - of SNil: - discard - - Cons = tuple[car: SexpNode, cdr: SexpNode] - - SexpParsingError* = object of ValueError ## is raised for a SEXP error - -proc raiseParseErr*(p: SexpParser, msg: string) {.noinline, noreturn.} = - ## raises an `ESexpParsingError` exception. - raise newException(SexpParsingError, errorMsgExpected(p, msg)) - -proc newSString*(s: string): SexpNode {.procvar.}= - ## Creates a new `SString SexpNode`. - new(result) - result.kind = SString - result.str = s - -proc newSStringMove(s: string): SexpNode = - new(result) - result.kind = SString - shallowCopy(result.str, s) - -proc newSInt*(n: BiggestInt): SexpNode {.procvar.} = - ## Creates a new `SInt SexpNode`. - new(result) - result.kind = SInt - result.num = n - -proc newSFloat*(n: float): SexpNode {.procvar.} = - ## Creates a new `SFloat SexpNode`. - new(result) - result.kind = SFloat - result.fnum = n - -proc newSNil*(): SexpNode {.procvar.} = - ## Creates a new `SNil SexpNode`. - new(result) - -proc newSCons*(car, cdr: SexpNode): SexpNode {.procvar.} = - ## Creates a new `SCons SexpNode` - new(result) - result.kind = SCons - result.car = car - result.cdr = cdr - -proc newSList*(): SexpNode {.procvar.} = - ## Creates a new `SList SexpNode` - new(result) - result.kind = SList - result.elems = @[] - -proc newSSymbol*(s: string): SexpNode {.procvar.} = - new(result) - result.kind = SSymbol - result.symbol = s - -proc newSSymbolMove(s: string): SexpNode = - new(result) - result.kind = SSymbol - shallowCopy(result.symbol, s) - -proc getStr*(n: SexpNode, default: string = ""): string = - ## Retrieves the string value of a `SString SexpNode`. - ## - ## Returns ``default`` if ``n`` is not a ``SString``. - if n.kind != SString: return default - else: return n.str - -proc getNum*(n: SexpNode, default: BiggestInt = 0): BiggestInt = - ## Retrieves the int value of a `SInt SexpNode`. - ## - ## Returns ``default`` if ``n`` is not a ``SInt``. - if n.kind != SInt: return default - else: return n.num - -proc getFNum*(n: SexpNode, default: float = 0.0): float = - ## Retrieves the float value of a `SFloat SexpNode`. - ## - ## Returns ``default`` if ``n`` is not a ``SFloat``. - if n.kind != SFloat: return default - else: return n.fnum - -proc getSymbol*(n: SexpNode, default: string = ""): string = - ## Retrieves the int value of a `SList SexpNode`. - ## - ## Returns ``default`` if ``n`` is not a ``SList``. - if n.kind != SSymbol: return default - else: return n.symbol - -proc getElems*(n: SexpNode, default: seq[SexpNode] = @[]): seq[SexpNode] = - ## Retrieves the int value of a `SList SexpNode`. - ## - ## Returns ``default`` if ``n`` is not a ``SList``. - if n.kind == SNil: return @[] - elif n.kind != SList: return default - else: return n.elems - -proc getCons*(n: SexpNode, defaults: Cons = (newSNil(), newSNil())): Cons = - ## Retrieves the cons value of a `SList SexpNode`. - ## - ## Returns ``default`` if ``n`` is not a ``SList``. - if n.kind == SCons: return (n.car, n.cdr) - elif n.kind == SList: return (n.elems[0], n.elems[1]) - else: return defaults - -proc sexp*(s: string): SexpNode = - ## Generic constructor for SEXP data. Creates a new `SString SexpNode`. - new(result) - result.kind = SString - result.str = s - -proc sexp*(n: BiggestInt): SexpNode = - ## Generic constructor for SEXP data. Creates a new `SInt SexpNode`. - new(result) - result.kind = SInt - result.num = n - -proc sexp*(n: float): SexpNode = - ## Generic constructor for SEXP data. Creates a new `SFloat SexpNode`. - new(result) - result.kind = SFloat - result.fnum = n - -proc sexp*(b: bool): SexpNode = - ## Generic constructor for SEXP data. Creates a new `SSymbol - ## SexpNode` with value t or `SNil SexpNode`. - new(result) - if b: - result.kind = SSymbol - result.symbol = "t" - else: - result.kind = SNil - -proc sexp*(elements: openArray[SexpNode]): SexpNode = - ## Generic constructor for SEXP data. Creates a new `SList SexpNode` - new(result) - result.kind = SList - newSeq(result.elems, elements.len) - for i, p in pairs(elements): result.elems[i] = p - -proc sexp*(s: SexpNode): SexpNode = - result = s - -proc toSexp(x: NimNode): NimNode {.compiletime.} = - case x.kind - of nnkBracket: - result = newNimNode(nnkBracket) - for i in 0 .. = 32 and r <= 127: - var c = chr(r) - case c - of '"': result.add("\\\"") #" # gotta fix nim-mode - of '\\': result.add("\\\\") - else: result.add(c) - else: - result.add("\\u") - result.add(toHex(r, 4)) - result.add("\"") - -proc copy*(p: SexpNode): SexpNode = - ## Performs a deep copy of `a`. - case p.kind - of SString: - result = newSString(p.str) - of SInt: - result = newSInt(p.num) - of SFloat: - result = newSFloat(p.fnum) - of SNil: - result = newSNil() - of SSymbol: - result = newSSymbol(p.symbol) - of SList: - result = newSList() - for i in items(p.elems): - result.elems.add(copy(i)) - of SCons: - result = newSCons(copy(p.car), copy(p.cdr)) - -proc toPretty(result: var string, node: SexpNode, indent = 2, ml = true, - lstArr = false, currIndent = 0) = - case node.kind - of SString: - if lstArr: result.indent(currIndent) - result.add(escapeJson(node.str)) - of SInt: - if lstArr: result.indent(currIndent) - result.add($node.num) - of SFloat: - if lstArr: result.indent(currIndent) - result.add($node.fnum) - of SNil: - if lstArr: result.indent(currIndent) - result.add("nil") - of SSymbol: - if lstArr: result.indent(currIndent) - result.add($node.symbol) - of SList: - if lstArr: result.indent(currIndent) - if len(node.elems) != 0: - result.add("(") - result.nl(ml) - for i in 0..len(node.elems)-1: - if i > 0: - result.add(" ") - result.nl(ml) # New Line - toPretty(result, node.elems[i], indent, ml, - true, newIndent(currIndent, indent, ml)) - result.nl(ml) - result.indent(currIndent) - result.add(")") - else: result.add("nil") - of SCons: - if lstArr: result.indent(currIndent) - result.add("(") - toPretty(result, node.car, indent, ml, - true, newIndent(currIndent, indent, ml)) - result.add(" . ") - toPretty(result, node.cdr, indent, ml, - true, newIndent(currIndent, indent, ml)) - result.add(")") - -proc pretty*(node: SexpNode, indent = 2): string = - ## Converts `node` to its Sexp Representation, with indentation and - ## on multiple lines. - result = "" - toPretty(result, node, indent) - -proc `$`*(node: SexpNode): string = - ## Converts `node` to its SEXP Representation on one line. - result = "" - toPretty(result, node, 0, false) - -iterator items*(node: SexpNode): SexpNode = - ## Iterator for the items of `node`. `node` has to be a SList. - assert node.kind == SList - for i in items(node.elems): - yield i - -iterator mitems*(node: var SexpNode): var SexpNode = - ## Iterator for the items of `node`. `node` has to be a SList. Items can be - ## modified. - assert node.kind == SList - for i in mitems(node.elems): - yield i - -proc eat(p: var SexpParser, tok: TokKind) = - if p.tok == tok: discard getTok(p) - else: raiseParseErr(p, tokToStr[tok]) - -proc parseSexp(p: var SexpParser): SexpNode = - ## Parses SEXP from a SEXP Parser `p`. - case p.tok - of tkString: - # we capture 'p.a' here, so we need to give it a fresh buffer afterwards: - result = newSStringMove(p.a) - p.a = "" - discard getTok(p) - of tkInt: - result = newSInt(parseBiggestInt(p.a)) - discard getTok(p) - of tkFloat: - result = newSFloat(parseFloat(p.a)) - discard getTok(p) - of tkNil: - result = newSNil() - discard getTok(p) - of tkSymbol: - result = newSSymbolMove(p.a) - p.a = "" - discard getTok(p) - of tkParensLe: - result = newSList() - discard getTok(p) - while p.tok notin {tkParensRi, tkDot}: - result.add(parseSexp(p)) - if p.tok != tkSpace: break - discard getTok(p) - if p.tok == tkDot: - eat(p, tkDot) - eat(p, tkSpace) - result.add(parseSexp(p)) - result = newSCons(result[0], result[1]) - eat(p, tkParensRi) - of tkSpace, tkDot, tkError, tkParensRi, tkEof: - raiseParseErr(p, "(") - -proc open*(my: var SexpParser, input: Stream) = - ## initializes the parser with an input stream. - lexbase.open(my, input) - my.kind = sexpError - my.a = "" - -proc parseSexp*(s: Stream): SexpNode = - ## Parses from a buffer `s` into a `SexpNode`. - var p: SexpParser - p.open(s) - discard getTok(p) # read first token - result = p.parseSexp() - p.close() - -proc parseSexp*(buffer: string): SexpNode = - ## Parses Sexp from `buffer`. - result = parseSexp(newStringStream(buffer)) - -when isMainModule: - let testSexp = parseSexp("""(1 (98 2) nil (2) foobar "foo" 9.234)""") - assert(testSexp[0].getNum == 1) - assert(testSexp[1][0].getNum == 98) - assert(testSexp[2].getElems == @[]) - assert(testSexp[4].getSymbol == "foobar") - assert(testSexp[5].getStr == "foo") - - let alist = parseSexp("""((1 . 2) (2 . "foo"))""") - assert(alist[0].getCons.car.getNum == 1) - assert(alist[0].getCons.cdr.getNum == 2) - assert(alist[1].getCons.cdr.getStr == "foo") - - # Generator: - var j = convertSexp([true, false, "foobar", [1, 2, "baz"]]) - assert($j == """(t nil "foobar" (1 2 "baz"))""") From 87a6d08e95c940775577c8f2bc5a74408c956a89 Mon Sep 17 00:00:00 2001 From: Dominik Picheta Date: Fri, 5 Jun 2015 17:10:17 +0100 Subject: [PATCH 17/18] Update version in compiler.nimble --- compiler.nimble | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/compiler.nimble b/compiler.nimble index 52eb4083b6..9d39af5025 100644 --- a/compiler.nimble +++ b/compiler.nimble @@ -1,6 +1,6 @@ [Package] name = "compiler" -version = "0.10.3" +version = "0.11.3" author = "Andreas Rumpf" description = "Compiler package providing the compiler sources as a library." license = "MIT" @@ -8,4 +8,4 @@ license = "MIT" InstallDirs = "doc, compiler" [Deps] -Requires: "nim >= 0.10.3" +Requires: "nim >= 0.11.3" From ee3d390b3ffc394fca27fef42e7e36277df22dad Mon Sep 17 00:00:00 2001 From: Oscar Campbell Date: Wed, 10 Jun 2015 19:52:26 +0200 Subject: [PATCH 18/18] Revert from columnar layout. Remove temp-fix for 2857. --- tests/testament/tester.nim | 18 ++++++------------ 1 file changed, 6 insertions(+), 12 deletions(-) diff --git a/tests/testament/tester.nim b/tests/testament/tester.nim index 2c4460395a..704d7e76e5 100644 --- a/tests/testament/tester.nim +++ b/tests/testament/tester.nim @@ -126,9 +126,6 @@ proc `$`(x: TResults): string = "Tests skipped: $2 / $3
\n") % [$x.passed, $x.skipped, $x.total] -proc leftAlign(s: string, count: Natural, padding = ' '): string = - s & repeat(padding, max(0, count - s.len)) - proc addResult(r: var TResults, test: TTest, expected, given: string, success: TResultEnum) = let name = test.name.extractFilename & test.options @@ -140,13 +137,12 @@ proc addResult(r: var TResults, test: TTest, expected = expected, given = given) r.data.addf("$#\t$#\t$#\t$#", name, expected, given, $success) - let alignedName = leftAlign(name, 72) # Make the total line length 78 chars if success == reSuccess: - styledEcho fgCyan, alignedName, fgGreen, " [PASS]" + styledEcho fgGreen, "PASS: ", fgCyan, name elif success == reIgnored: - styledEcho styleBright, fgCyan, alignedName, styleDim, fgYellow, " [SKIP]" + styledEcho styleDim, fgYellow, "SKIP: ", styleBright, fgCyan, name else: - styledEcho styleBright, fgCyan, alignedName, fgRed, " [FAIL]" + styledEcho styleBright, fgRed, "FAIL: ", fgCyan, name styledEcho styleBright, fgCyan, "Test \"", test.name, "\"", " in category \"", test.cat.string, "\"" styledEcho styleBright, fgRed, "Failure: ", $success styledEcho fgYellow, "Expected:" @@ -295,13 +291,11 @@ proc testSpec(r: var TResults, test: TTest) = reExitCodesDiffer) return - # This is a bit ugly atm. if bufB != expectedOut: if not (expected.substr and expectedOut in bufB): - if not (expectedOut == "" and bufB == "\27[0m"): - given.err = reOutputsDiffer - r.addResult(test, expected.outp, bufB, reOutputsDiffer) - return + given.err = reOutputsDiffer + r.addResult(test, expected.outp, bufB, reOutputsDiffer) + return compilerOutputTests(test, given, expected, r) return