summary refs log tree commit diff stats
diff options
context:
space:
mode:
authorAraq <rumpf_a@web.de>2014-11-12 02:36:59 +0100
committerAraq <rumpf_a@web.de>2014-11-12 02:36:59 +0100
commitb2f577df23d5daae873df18ff345965f8dc7e47b (patch)
tree2307ab50c5aa1c3cd2f7d56dfda817bcf05a3da1
parent2d43fcafe0cedd4f78611dddccc31e1bef432aab (diff)
downloadNim-b2f577df23d5daae873df18ff345965f8dc7e47b.tar.gz
fixes #1473
-rw-r--r--compiler/lexer.nim19
-rw-r--r--compiler/msgs.nim10
-rw-r--r--compiler/parser.nim9
-rw-r--r--compiler/vm.nim30
-rw-r--r--compiler/vmdef.nim5
-rw-r--r--compiler/vmgen.nim9
-rw-r--r--compiler/vmops.nim5
-rw-r--r--lib/core/macros.nim24
-rw-r--r--tests/macros/ttryparseexpr.nim17
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