fixes #1473
This commit is contained in:
parent
2d43fcafe0
commit
b2f577df23
9 changed files with 104 additions and 26 deletions
|
|
@ -116,7 +116,8 @@ type
|
||||||
literal*: string # the parsed (string) literal; and
|
literal*: string # the parsed (string) literal; and
|
||||||
# documentation comments are here too
|
# documentation comments are here too
|
||||||
line*, col*: int
|
line*, col*: int
|
||||||
|
|
||||||
|
TErrorHandler* = proc (info: TLineInfo; msg: TMsgKind; arg: string)
|
||||||
TLexer* = object of TBaseLexer
|
TLexer* = object of TBaseLexer
|
||||||
fileIdx*: int32
|
fileIdx*: int32
|
||||||
indentAhead*: int # if > 0 an indendation has already been read
|
indentAhead*: int # if > 0 an indendation has already been read
|
||||||
|
|
@ -124,7 +125,7 @@ type
|
||||||
# needs so much look-ahead
|
# needs so much look-ahead
|
||||||
currLineIndent*: int
|
currLineIndent*: int
|
||||||
strongSpaces*: bool
|
strongSpaces*: bool
|
||||||
|
errorHandler*: TErrorHandler
|
||||||
|
|
||||||
var gLinesCompiled*: int # all lines that have been compiled
|
var gLinesCompiled*: int # all lines that have been compiled
|
||||||
|
|
||||||
|
|
@ -222,12 +223,18 @@ proc getColumn(L: TLexer): int =
|
||||||
proc getLineInfo(L: TLexer): TLineInfo =
|
proc getLineInfo(L: TLexer): TLineInfo =
|
||||||
result = newLineInfo(L.fileIdx, L.lineNumber, getColNumber(L, L.bufpos))
|
result = newLineInfo(L.fileIdx, L.lineNumber, getColNumber(L, L.bufpos))
|
||||||
|
|
||||||
proc lexMessage(L: TLexer, msg: TMsgKind, arg = "") =
|
proc dispMessage(L: TLexer; info: TLineInfo; msg: TMsgKind; arg: string) =
|
||||||
msgs.message(getLineInfo(L), msg, arg)
|
if L.errorHandler.isNil:
|
||||||
|
msgs.message(info, msg, arg)
|
||||||
|
else:
|
||||||
|
L.errorHandler(info, msg, arg)
|
||||||
|
|
||||||
proc lexMessagePos(L: var TLexer, msg: TMsgKind, pos: int, arg = "") =
|
proc lexMessage(L: TLexer, msg: TMsgKind, arg = "") =
|
||||||
|
L.dispMessage(getLineInfo(L), msg, arg)
|
||||||
|
|
||||||
|
proc lexMessagePos(L: var TLexer, msg: TMsgKind, pos: int, arg = "") =
|
||||||
var info = newLineInfo(L.fileIdx, L.lineNumber, pos - L.lineStart)
|
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]) =
|
proc matchUnderscoreChars(L: var TLexer, tok: var TToken, chars: set[char]) =
|
||||||
var pos = L.bufpos # use registers for pos, buf
|
var pos = L.bufpos # use registers for pos, buf
|
||||||
|
|
|
||||||
|
|
@ -450,7 +450,7 @@ type
|
||||||
projPath*: string # This is relative to the project's root
|
projPath*: string # This is relative to the project's root
|
||||||
shortName*: string # short name of the module
|
shortName*: string # short name of the module
|
||||||
quotedName*: PRope # cached quoted short name for codegen
|
quotedName*: PRope # cached quoted short name for codegen
|
||||||
# purpoes
|
# purposes
|
||||||
|
|
||||||
lines*: seq[PRope] # the source code of the module
|
lines*: seq[PRope] # the source code of the module
|
||||||
# used for better error messages and
|
# used for better error messages and
|
||||||
|
|
@ -789,6 +789,14 @@ proc writeSurroundingSrc(info: TLineInfo) =
|
||||||
msgWriteln(indent & info.sourceLine.ropeToStr)
|
msgWriteln(indent & info.sourceLine.ropeToStr)
|
||||||
msgWriteln(indent & repeatChar(info.col, ' ') & '^')
|
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,
|
proc liMessage(info: TLineInfo, msg: TMsgKind, arg: string,
|
||||||
eh: TErrorHandling) =
|
eh: TErrorHandling) =
|
||||||
var frmt: string
|
var frmt: string
|
||||||
|
|
|
||||||
|
|
@ -41,8 +41,7 @@ type
|
||||||
proc parseAll*(p: var TParser): PNode
|
proc parseAll*(p: var TParser): PNode
|
||||||
proc closeParser*(p: var TParser)
|
proc closeParser*(p: var TParser)
|
||||||
proc parseTopLevelStmt*(p: var TParser): PNode
|
proc parseTopLevelStmt*(p: var TParser): PNode
|
||||||
proc parseString*(s: string, filename: string = "", line: int = 0): PNode
|
|
||||||
|
|
||||||
# helpers for the other parsers
|
# helpers for the other parsers
|
||||||
proc isOperator*(tok: TToken): bool
|
proc isOperator*(tok: TToken): bool
|
||||||
proc getTok*(p: var TParser)
|
proc getTok*(p: var TParser)
|
||||||
|
|
@ -96,7 +95,7 @@ proc parMessage(p: TParser, msg: TMsgKind, arg = "") =
|
||||||
|
|
||||||
proc parMessage(p: TParser, msg: TMsgKind, tok: TToken) =
|
proc parMessage(p: TParser, msg: TMsgKind, tok: TToken) =
|
||||||
## Produce and emit a parser message to output about the token `tok`
|
## 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.} =
|
template withInd(p: expr, body: stmt) {.immediate.} =
|
||||||
let oldInd = p.currInd
|
let oldInd = p.currInd
|
||||||
|
|
@ -1995,7 +1994,8 @@ proc parseTopLevelStmt(p: var TParser): PNode =
|
||||||
if result.kind == nkEmpty: parMessage(p, errExprExpected, p.tok)
|
if result.kind == nkEmpty: parMessage(p, errExprExpected, p.tok)
|
||||||
break
|
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.
|
## Parses a string into an AST, returning the top node.
|
||||||
## `filename` and `line`, although optional, provide info so that the
|
## `filename` and `line`, although optional, provide info so that the
|
||||||
## compiler can generate correct error messages referring to the original
|
## 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
|
# XXX for now the builtin 'parseStmt/Expr' functions do not know about strong
|
||||||
# spaces...
|
# spaces...
|
||||||
openParser(parser, filename, stream, false)
|
openParser(parser, filename, stream, false)
|
||||||
|
parser.lex.errorHandler = errorHandler
|
||||||
|
|
||||||
result = parser.parseAll
|
result = parser.parseAll
|
||||||
closeParser(parser)
|
closeParser(parser)
|
||||||
|
|
|
||||||
|
|
@ -814,7 +814,8 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
|
||||||
if prc.offset < -1:
|
if prc.offset < -1:
|
||||||
# it's a callback:
|
# it's a callback:
|
||||||
c.callbacks[-prc.offset-2].value(
|
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:
|
elif sfImportc in prc.flags:
|
||||||
if allowFFI notin c.features:
|
if allowFFI notin c.features:
|
||||||
globalError(c.debug[pc], errGenerated, "VM not allowed to do FFI")
|
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:
|
of opcParseExprToAst:
|
||||||
decodeB(rkNode)
|
decodeB(rkNode)
|
||||||
# c.debug[pc].line.int - countLines(regs[rb].strVal) ?
|
# c.debug[pc].line.int - countLines(regs[rb].strVal) ?
|
||||||
|
var error: string
|
||||||
let ast = parseString(regs[rb].node.strVal, c.debug[pc].toFullPath,
|
let ast = parseString(regs[rb].node.strVal, c.debug[pc].toFullPath,
|
||||||
c.debug[pc].line.int)
|
c.debug[pc].line.int,
|
||||||
if sonsLen(ast) != 1:
|
proc (info: TLineInfo; msg: TMsgKind; arg: string) =
|
||||||
globalError(c.debug[pc], errExprExpected, "multiple statements")
|
if error.isNil: error = formatMsg(info, msg, arg))
|
||||||
regs[ra].node = ast.sons[0]
|
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:
|
of opcParseStmtToAst:
|
||||||
decodeB(rkNode)
|
decodeB(rkNode)
|
||||||
|
var error: string
|
||||||
let ast = parseString(regs[rb].node.strVal, c.debug[pc].toFullPath,
|
let ast = parseString(regs[rb].node.strVal, c.debug[pc].toFullPath,
|
||||||
c.debug[pc].line.int)
|
c.debug[pc].line.int,
|
||||||
regs[ra].node = ast
|
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:
|
of opcCallSite:
|
||||||
ensureKind(rkNode)
|
ensureKind(rkNode)
|
||||||
if c.callsite != nil: regs[ra].node = c.callsite
|
if c.callsite != nil: regs[ra].node = c.callsite
|
||||||
|
|
|
||||||
|
|
@ -92,6 +92,7 @@ type
|
||||||
opcGorge,
|
opcGorge,
|
||||||
opcParseExprToAst,
|
opcParseExprToAst,
|
||||||
opcParseStmtToAst,
|
opcParseStmtToAst,
|
||||||
|
opcQueryErrorFlag,
|
||||||
opcNError,
|
opcNError,
|
||||||
opcNWarning,
|
opcNWarning,
|
||||||
opcNHint,
|
opcNHint,
|
||||||
|
|
@ -174,6 +175,7 @@ type
|
||||||
VmArgs* = object
|
VmArgs* = object
|
||||||
ra*, rb*, rc*: Natural
|
ra*, rb*, rc*: Natural
|
||||||
slots*: pointer
|
slots*: pointer
|
||||||
|
currentException*: PNode
|
||||||
VmCallback* = proc (args: VmArgs) {.closure.}
|
VmCallback* = proc (args: VmArgs) {.closure.}
|
||||||
|
|
||||||
PCtx* = ref TCtx
|
PCtx* = ref TCtx
|
||||||
|
|
@ -195,6 +197,7 @@ type
|
||||||
loopIterations*: int
|
loopIterations*: int
|
||||||
comesFromHeuristic*: TLineInfo # Heuristic for better macro stack traces
|
comesFromHeuristic*: TLineInfo # Heuristic for better macro stack traces
|
||||||
callbacks*: seq[tuple[key: string, value: VmCallback]]
|
callbacks*: seq[tuple[key: string, value: VmCallback]]
|
||||||
|
errorFlag*: string
|
||||||
|
|
||||||
TPosition* = distinct int
|
TPosition* = distinct int
|
||||||
|
|
||||||
|
|
@ -204,7 +207,7 @@ proc newCtx*(module: PSym): PCtx =
|
||||||
PCtx(code: @[], debug: @[],
|
PCtx(code: @[], debug: @[],
|
||||||
globals: newNode(nkStmtListExpr), constants: newNode(nkStmtList), types: @[],
|
globals: newNode(nkStmtListExpr), constants: newNode(nkStmtList), types: @[],
|
||||||
prc: PProc(blocks: @[]), module: module, loopIterations: MaxLoopIterations,
|
prc: PProc(blocks: @[]), module: module, loopIterations: MaxLoopIterations,
|
||||||
comesFromHeuristic: unknownLineInfo(), callbacks: @[])
|
comesFromHeuristic: unknownLineInfo(), callbacks: @[], errorFlag: "")
|
||||||
|
|
||||||
proc refresh*(c: PCtx, module: PSym) =
|
proc refresh*(c: PCtx, module: PSym) =
|
||||||
c.module = module
|
c.module = module
|
||||||
|
|
|
||||||
|
|
@ -987,8 +987,13 @@ proc genMagic(c: PCtx; n: PNode; dest: var TDest) =
|
||||||
unused(n, dest)
|
unused(n, dest)
|
||||||
genUnaryStmt(c, n, opcNWarning)
|
genUnaryStmt(c, n, opcNWarning)
|
||||||
of mNError:
|
of mNError:
|
||||||
unused(n, dest)
|
if n.len <= 1:
|
||||||
genUnaryStmt(c, n, opcNError)
|
# query error condition:
|
||||||
|
c.gABC(n, opcQueryErrorFlag, dest)
|
||||||
|
else:
|
||||||
|
# setter
|
||||||
|
unused(n, dest)
|
||||||
|
genUnaryStmt(c, n, opcNError)
|
||||||
of mNCallSite:
|
of mNCallSite:
|
||||||
if dest < 0: dest = c.getTemp(n.typ)
|
if dest < 0: dest = c.getTemp(n.typ)
|
||||||
c.gABC(n, opcCallSite, dest)
|
c.gABC(n, opcCallSite, dest)
|
||||||
|
|
|
||||||
|
|
@ -44,6 +44,10 @@ template wrap2svoid(op) {.immediate, dirty.} =
|
||||||
op(getString(a, 0), getString(a, 1))
|
op(getString(a, 0), getString(a, 1))
|
||||||
systemop op
|
systemop op
|
||||||
|
|
||||||
|
proc getCurrentExceptionMsgWrapper(a: VmArgs) {.nimcall.} =
|
||||||
|
setResult(a, if a.currentException.isNil: ""
|
||||||
|
else: a.currentException.sons[2].strVal)
|
||||||
|
|
||||||
proc registerAdditionalOps*(c: PCtx) =
|
proc registerAdditionalOps*(c: PCtx) =
|
||||||
wrap1f(sqrt)
|
wrap1f(sqrt)
|
||||||
wrap1f(ln)
|
wrap1f(ln)
|
||||||
|
|
@ -73,3 +77,4 @@ proc registerAdditionalOps*(c: PCtx) =
|
||||||
wrap1s(dirExists)
|
wrap1s(dirExists)
|
||||||
wrap1s(fileExists)
|
wrap1s(fileExists)
|
||||||
wrap2svoid(writeFile)
|
wrap2svoid(writeFile)
|
||||||
|
systemop getCurrentExceptionMsg
|
||||||
|
|
|
||||||
|
|
@ -249,13 +249,29 @@ proc lineinfo*(n: PNimrodNode): string {.magic: "NLineInfo", noSideEffect.}
|
||||||
## returns the position the node appears in the original source file
|
## returns the position the node appears in the original source file
|
||||||
## in the form filename(line, col)
|
## in the form filename(line, col)
|
||||||
|
|
||||||
proc parseExpr*(s: string): PNimrodNode {.magic: "ParseExprToAst", noSideEffect.}
|
proc internalParseExpr(s: string): PNimrodNode {.
|
||||||
## Compiles the passed string to its AST representation.
|
magic: "ParseExprToAst", noSideEffect.}
|
||||||
## Expects a single expression.
|
|
||||||
|
|
||||||
proc parseStmt*(s: string): PNimrodNode {.magic: "ParseStmtToAst", 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.
|
## Compiles the passed string to its AST representation.
|
||||||
## Expects one or more statements.
|
## 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 {.noSideEffect, compileTime.} =
|
||||||
|
## Compiles the passed string to its AST representation.
|
||||||
|
## 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.}
|
proc getAst*(macroOrTemplate: expr): PNimrodNode {.magic: "ExpandToAst", noSideEffect.}
|
||||||
## Obtains the AST nodes returned from a macro or template invocation.
|
## Obtains the AST nodes returned from a macro or template invocation.
|
||||||
|
|
|
||||||
17
tests/macros/ttryparseexpr.nim
Normal file
17
tests/macros/ttryparseexpr.nim
Normal file
|
|
@ -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
|
||||||
Loading…
Add table
Add a link
Reference in a new issue