diff options
author | Araq <rumpf_a@web.de> | 2014-11-12 02:36:59 +0100 |
---|---|---|
committer | Araq <rumpf_a@web.de> | 2014-11-12 02:36:59 +0100 |
commit | b2f577df23d5daae873df18ff345965f8dc7e47b (patch) | |
tree | 2307ab50c5aa1c3cd2f7d56dfda817bcf05a3da1 | |
parent | 2d43fcafe0cedd4f78611dddccc31e1bef432aab (diff) | |
download | Nim-b2f577df23d5daae873df18ff345965f8dc7e47b.tar.gz |
fixes #1473
-rw-r--r-- | compiler/lexer.nim | 19 | ||||
-rw-r--r-- | compiler/msgs.nim | 10 | ||||
-rw-r--r-- | compiler/parser.nim | 9 | ||||
-rw-r--r-- | compiler/vm.nim | 30 | ||||
-rw-r--r-- | compiler/vmdef.nim | 5 | ||||
-rw-r--r-- | compiler/vmgen.nim | 9 | ||||
-rw-r--r-- | compiler/vmops.nim | 5 | ||||
-rw-r--r-- | lib/core/macros.nim | 24 | ||||
-rw-r--r-- | tests/macros/ttryparseexpr.nim | 17 |
9 files changed, 103 insertions, 25 deletions
diff --git a/compiler/lexer.nim b/compiler/lexer.nim index cbc87972d..dbeec8acf 100644 --- a/compiler/lexer.nim +++ b/compiler/lexer.nim @@ -116,7 +116,8 @@ type literal*: string # the parsed (string) literal; and # documentation comments are here too line*, col*: int - + + TErrorHandler* = proc (info: TLineInfo; msg: TMsgKind; arg: string) TLexer* = object of TBaseLexer fileIdx*: int32 indentAhead*: int # if > 0 an indendation has already been read @@ -124,7 +125,7 @@ type # needs so much look-ahead currLineIndent*: int strongSpaces*: bool - + errorHandler*: TErrorHandler var gLinesCompiled*: int # all lines that have been compiled @@ -222,12 +223,18 @@ proc getColumn(L: TLexer): int = proc getLineInfo(L: TLexer): TLineInfo = result = newLineInfo(L.fileIdx, L.lineNumber, getColNumber(L, L.bufpos)) -proc lexMessage(L: TLexer, msg: TMsgKind, arg = "") = - msgs.message(getLineInfo(L), msg, arg) +proc dispMessage(L: TLexer; info: TLineInfo; msg: TMsgKind; arg: string) = + if L.errorHandler.isNil: + msgs.message(info, msg, arg) + else: + L.errorHandler(info, msg, arg) + +proc lexMessage(L: TLexer, msg: TMsgKind, arg = "") = + L.dispMessage(getLineInfo(L), msg, arg) -proc lexMessagePos(L: var TLexer, msg: TMsgKind, pos: int, arg = "") = +proc lexMessagePos(L: var TLexer, msg: TMsgKind, pos: int, arg = "") = var info = newLineInfo(L.fileIdx, L.lineNumber, pos - L.lineStart) - msgs.message(info, msg, arg) + L.dispMessage(info, msg, arg) proc matchUnderscoreChars(L: var TLexer, tok: var TToken, chars: set[char]) = var pos = L.bufpos # use registers for pos, buf diff --git a/compiler/msgs.nim b/compiler/msgs.nim index 944446624..7f4f81dd0 100644 --- a/compiler/msgs.nim +++ b/compiler/msgs.nim @@ -450,7 +450,7 @@ type projPath*: string # This is relative to the project's root shortName*: string # short name of the module quotedName*: PRope # cached quoted short name for codegen - # purpoes + # purposes lines*: seq[PRope] # the source code of the module # used for better error messages and @@ -789,6 +789,14 @@ proc writeSurroundingSrc(info: TLineInfo) = msgWriteln(indent & info.sourceLine.ropeToStr) msgWriteln(indent & repeatChar(info.col, ' ') & '^') +proc formatMsg*(info: TLineInfo, msg: TMsgKind, arg: string): string = + let frmt = case msg + of warnMin..warnMax: PosWarningFormat + of hintMin..hintMax: PosHintFormat + else: PosErrorFormat + result = frmt % [toMsgFilename(info), coordToStr(info.line), + coordToStr(info.col), getMessageStr(msg, arg)] + proc liMessage(info: TLineInfo, msg: TMsgKind, arg: string, eh: TErrorHandling) = var frmt: string diff --git a/compiler/parser.nim b/compiler/parser.nim index 20c037c00..e4de75a07 100644 --- a/compiler/parser.nim +++ b/compiler/parser.nim @@ -41,8 +41,7 @@ type proc parseAll*(p: var TParser): PNode proc closeParser*(p: var TParser) proc parseTopLevelStmt*(p: var TParser): PNode -proc parseString*(s: string, filename: string = "", line: int = 0): PNode - + # helpers for the other parsers proc isOperator*(tok: TToken): bool proc getTok*(p: var TParser) @@ -96,7 +95,7 @@ proc parMessage(p: TParser, msg: TMsgKind, arg = "") = proc parMessage(p: TParser, msg: TMsgKind, tok: TToken) = ## Produce and emit a parser message to output about the token `tok` - lexMessage(p.lex, msg, prettyTok(tok)) + parMessage(p, msg, prettyTok(tok)) template withInd(p: expr, body: stmt) {.immediate.} = let oldInd = p.currInd @@ -1995,7 +1994,8 @@ proc parseTopLevelStmt(p: var TParser): PNode = if result.kind == nkEmpty: parMessage(p, errExprExpected, p.tok) break -proc parseString(s: string, filename: string = "", line: int = 0): PNode = +proc parseString*(s: string; filename: string = ""; line: int = 0; + errorHandler: TErrorHandler = nil): PNode = ## Parses a string into an AST, returning the top node. ## `filename` and `line`, although optional, provide info so that the ## compiler can generate correct error messages referring to the original @@ -2007,6 +2007,7 @@ proc parseString(s: string, filename: string = "", line: int = 0): PNode = # XXX for now the builtin 'parseStmt/Expr' functions do not know about strong # spaces... openParser(parser, filename, stream, false) + parser.lex.errorHandler = errorHandler result = parser.parseAll closeParser(parser) diff --git a/compiler/vm.nim b/compiler/vm.nim index e5b357a11..a15807b24 100644 --- a/compiler/vm.nim +++ b/compiler/vm.nim @@ -814,7 +814,8 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg = if prc.offset < -1: # it's a callback: c.callbacks[-prc.offset-2].value( - VmArgs(ra: ra, rb: rb, rc: rc, slots: cast[pointer](regs))) + VmArgs(ra: ra, rb: rb, rc: rc, slots: cast[pointer](regs), + currentException: c.currentExceptionB)) elif sfImportc in prc.flags: if allowFFI notin c.features: globalError(c.debug[pc], errGenerated, "VM not allowed to do FFI") @@ -1146,16 +1147,31 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg = of opcParseExprToAst: decodeB(rkNode) # c.debug[pc].line.int - countLines(regs[rb].strVal) ? + var error: string let ast = parseString(regs[rb].node.strVal, c.debug[pc].toFullPath, - c.debug[pc].line.int) - if sonsLen(ast) != 1: - globalError(c.debug[pc], errExprExpected, "multiple statements") - regs[ra].node = ast.sons[0] + c.debug[pc].line.int, + proc (info: TLineInfo; msg: TMsgKind; arg: string) = + if error.isNil: error = formatMsg(info, msg, arg)) + if not error.isNil: + c.errorFlag = error + elif sonsLen(ast) != 1: + c.errorFlag = formatMsg(c.debug[pc], errExprExpected, "multiple statements") + else: + regs[ra].node = ast.sons[0] of opcParseStmtToAst: decodeB(rkNode) + var error: string let ast = parseString(regs[rb].node.strVal, c.debug[pc].toFullPath, - c.debug[pc].line.int) - regs[ra].node = ast + c.debug[pc].line.int, + proc (info: TLineInfo; msg: TMsgKind; arg: string) = + if error.isNil: error = formatMsg(info, msg, arg)) + if not error.isNil: + c.errorFlag = error + else: + regs[ra].node = ast + of opcQueryErrorFlag: + createStr regs[ra] + regs[ra].node.strVal = c.errorFlag of opcCallSite: ensureKind(rkNode) if c.callsite != nil: regs[ra].node = c.callsite diff --git a/compiler/vmdef.nim b/compiler/vmdef.nim index d7cdafb69..c06606318 100644 --- a/compiler/vmdef.nim +++ b/compiler/vmdef.nim @@ -92,6 +92,7 @@ type opcGorge, opcParseExprToAst, opcParseStmtToAst, + opcQueryErrorFlag, opcNError, opcNWarning, opcNHint, @@ -174,6 +175,7 @@ type VmArgs* = object ra*, rb*, rc*: Natural slots*: pointer + currentException*: PNode VmCallback* = proc (args: VmArgs) {.closure.} PCtx* = ref TCtx @@ -195,6 +197,7 @@ type loopIterations*: int comesFromHeuristic*: TLineInfo # Heuristic for better macro stack traces callbacks*: seq[tuple[key: string, value: VmCallback]] + errorFlag*: string TPosition* = distinct int @@ -204,7 +207,7 @@ proc newCtx*(module: PSym): PCtx = PCtx(code: @[], debug: @[], globals: newNode(nkStmtListExpr), constants: newNode(nkStmtList), types: @[], prc: PProc(blocks: @[]), module: module, loopIterations: MaxLoopIterations, - comesFromHeuristic: unknownLineInfo(), callbacks: @[]) + comesFromHeuristic: unknownLineInfo(), callbacks: @[], errorFlag: "") proc refresh*(c: PCtx, module: PSym) = c.module = module diff --git a/compiler/vmgen.nim b/compiler/vmgen.nim index e0e34306c..da31eab3d 100644 --- a/compiler/vmgen.nim +++ b/compiler/vmgen.nim @@ -987,8 +987,13 @@ proc genMagic(c: PCtx; n: PNode; dest: var TDest) = unused(n, dest) genUnaryStmt(c, n, opcNWarning) of mNError: - unused(n, dest) - genUnaryStmt(c, n, opcNError) + if n.len <= 1: + # query error condition: + c.gABC(n, opcQueryErrorFlag, dest) + else: + # setter + unused(n, dest) + genUnaryStmt(c, n, opcNError) of mNCallSite: if dest < 0: dest = c.getTemp(n.typ) c.gABC(n, opcCallSite, dest) diff --git a/compiler/vmops.nim b/compiler/vmops.nim index aa25d208a..ef2bfabb2 100644 --- a/compiler/vmops.nim +++ b/compiler/vmops.nim @@ -44,6 +44,10 @@ template wrap2svoid(op) {.immediate, dirty.} = op(getString(a, 0), getString(a, 1)) systemop op +proc getCurrentExceptionMsgWrapper(a: VmArgs) {.nimcall.} = + setResult(a, if a.currentException.isNil: "" + else: a.currentException.sons[2].strVal) + proc registerAdditionalOps*(c: PCtx) = wrap1f(sqrt) wrap1f(ln) @@ -73,3 +77,4 @@ proc registerAdditionalOps*(c: PCtx) = wrap1s(dirExists) wrap1s(fileExists) wrap2svoid(writeFile) + systemop getCurrentExceptionMsg diff --git a/lib/core/macros.nim b/lib/core/macros.nim index db2bfa9a6..b61fe1d17 100644 --- a/lib/core/macros.nim +++ b/lib/core/macros.nim @@ -249,13 +249,29 @@ proc lineinfo*(n: PNimrodNode): string {.magic: "NLineInfo", noSideEffect.} ## returns the position the node appears in the original source file ## in the form filename(line, col) -proc parseExpr*(s: string): PNimrodNode {.magic: "ParseExprToAst", noSideEffect.} +proc internalParseExpr(s: string): PNimrodNode {. + magic: "ParseExprToAst", noSideEffect.} + +proc internalParseStmt(s: string): PNimrodNode {. + magic: "ParseStmtToAst", noSideEffect.} + +proc internalErrorFlag*(): string {.magic: "NError", noSideEffect.} + ## Some builtins set an error flag. This is then turned into a proper + ## exception. **Note**: Ordinary application code should not call this. + +proc parseExpr*(s: string): PNimrodNode {.noSideEffect, compileTime.} = ## Compiles the passed string to its AST representation. - ## Expects a single expression. + ## Expects a single expression. Raises ``ValueError`` for parsing errors. + result = internalParseExpr(s) + let x = internalErrorFlag() + if x.len > 0: raise newException(ValueError, x) -proc parseStmt*(s: string): PNimrodNode {.magic: "ParseStmtToAst", noSideEffect.} +proc parseStmt*(s: string): PNimrodNode {.noSideEffect, compileTime.} = ## Compiles the passed string to its AST representation. - ## Expects one or more statements. + ## Expects one or more statements. Raises ``ValueError`` for parsing errors. + result = internalParseStmt(s) + let x = internalErrorFlag() + if x.len > 0: raise newException(ValueError, x) proc getAst*(macroOrTemplate: expr): PNimrodNode {.magic: "ExpandToAst", noSideEffect.} ## Obtains the AST nodes returned from a macro or template invocation. diff --git a/tests/macros/ttryparseexpr.nim b/tests/macros/ttryparseexpr.nim new file mode 100644 index 000000000..2a6f4437d --- /dev/null +++ b/tests/macros/ttryparseexpr.nim @@ -0,0 +1,17 @@ +discard """ + outputsub: '''Error: invalid indentation''' +""" + +# feature request #1473 +import macros + +macro test(text: string): expr = + try: + result = parseExpr(text.strVal) + except ValueError: + result = newLit getCurrentExceptionMsg() + +const + a = test("foo&&") + +echo a |