This commit is contained in:
Araq 2014-11-12 02:36:59 +01:00
commit b2f577df23
9 changed files with 104 additions and 26 deletions

View file

@ -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

View file

@ -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

View file

@ -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)

View file

@ -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

View file

@ -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

View file

@ -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)

View file

@ -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

View file

@ -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.

View 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