You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@nlpcraft.apache.org by ar...@apache.org on 2021/02/28 06:39:27 UTC
[incubator-nlpcraft] branch NLPCRAFT-206 updated: WIP on error
handling.
This is an automated email from the ASF dual-hosted git repository.
aradzinski pushed a commit to branch NLPCRAFT-206
in repository https://gitbox.apache.org/repos/asf/incubator-nlpcraft.git
The following commit(s) were added to refs/heads/NLPCRAFT-206 by this push:
new f17a3a8 WIP on error handling.
f17a3a8 is described below
commit f17a3a8ab3706945422d824326c9d368e507f821
Author: Aaron Radzinzski <ar...@datalingvo.com>
AuthorDate: Sat Feb 27 22:39:17 2021 -0800
WIP on error handling.
---
.../org/apache/nlpcraft/common/util/NCUtils.scala | 8 ++
.../model/intent/impl/ver2/NCBaseDslCompiler.scala | 73 ++++++++------
.../intent/impl/ver2/NCIntentDslCompiler.scala | 106 ++++++++++++---------
.../model/intent/dsl/NCIntentDslCompilerSpec.scala | 7 ++
4 files changed, 118 insertions(+), 76 deletions(-)
diff --git a/nlpcraft/src/main/scala/org/apache/nlpcraft/common/util/NCUtils.scala b/nlpcraft/src/main/scala/org/apache/nlpcraft/common/util/NCUtils.scala
index 5927455..1d16e4c 100644
--- a/nlpcraft/src/main/scala/org/apache/nlpcraft/common/util/NCUtils.scala
+++ b/nlpcraft/src/main/scala/org/apache/nlpcraft/common/util/NCUtils.scala
@@ -529,6 +529,14 @@ object NCUtils extends LazyLogging {
@throws[NCE]
def readTextGzipPath(path: String, enc: String, log: Logger = logger): List[String] =
readTextGzipFile(new File(path), enc, log)
+
+ /**
+ *
+ * @param s
+ * @return
+ */
+ def decapitalize(s: String): String =
+ s.head.toLower + s.tail
/**
* Converts given name into properly capitalized first and last name.
diff --git a/nlpcraft/src/main/scala/org/apache/nlpcraft/model/intent/impl/ver2/NCBaseDslCompiler.scala b/nlpcraft/src/main/scala/org/apache/nlpcraft/model/intent/impl/ver2/NCBaseDslCompiler.scala
index 3b6cfdf..b2c0b64 100644
--- a/nlpcraft/src/main/scala/org/apache/nlpcraft/model/intent/impl/ver2/NCBaseDslCompiler.scala
+++ b/nlpcraft/src/main/scala/org/apache/nlpcraft/model/intent/impl/ver2/NCBaseDslCompiler.scala
@@ -31,8 +31,8 @@ import scala.collection.mutable
//noinspection DuplicatedCode
trait NCBaseDslCompiler {
- def syntaxError(errMsg: String, line: Int, pos: Int): NCE
- def runtimeError(errMsg: String, cause: Exception = null): NCE
+ def syntaxError(errMsg: String, srcName: String, line: Int, pos: Int): NCE
+ def runtimeError(errMsg: String, srcName: String, line: Int, pos: Int, cause: Exception = null): NCE
/**
*
@@ -43,7 +43,18 @@ trait NCBaseDslCompiler {
def newSyntaxError(errMsg: String)(implicit ctx: ParserRuleContext): NCE = {
val tok = ctx.start
- syntaxError(errMsg, tok.getLine, tok.getCharPositionInLine)
+ syntaxError(errMsg, tok.getInputStream.getSourceName, tok.getLine, tok.getCharPositionInLine)
+ }
+ /**
+ *
+ * @param errMsg
+ * @param ctx
+ * @return
+ */
+ def newRuntimeError(errMsg: String, cause: Exception = null)(implicit ctx: ParserRuleContext): NCE = {
+ val src = ctx.start.getTokenSource
+
+ runtimeError(errMsg, src.getSourceName, src.getLine, src.getCharPositionInLine, cause)
}
type StackType = mutable.ArrayStack[NCDslTermRetVal]
@@ -68,18 +79,18 @@ trait NCBaseDslCompiler {
stack.push(NCDslTermRetVal(Boolean.box(any), usedTok))
// Runtime errors.
- def rtUnaryOpError(op: String, v: AnyRef): NCE =
- runtimeError(s"Unexpected '$op' DSL operation for value: $v")
- def rtBinaryOpError(op: String, v1: AnyRef, v2: AnyRef): NCE =
- runtimeError(s"Unexpected '$op' DSL operation for values: $v1, $v2")
- def rtUnknownFunError(fun: String): NCE =
- runtimeError(s"Unknown DSL function: $fun()")
- def rtMinParamNumError(min: Int, fun: String): NCE =
- runtimeError(s"Invalid number of parameters for DSL function ($min is required): $fun()")
- def rtParamNumError(fun: String): NCE =
- runtimeError(s"Invalid number of parameters for DSL function: $fun()")
- def rtParamTypeError(fun: String, param: AnyRef, expectType: String): NCE =
- runtimeError(s"Expecting '$expectType' type of parameter for DSL function '$fun()', found: $param")
+ def rtUnaryOpError(op: String, v: AnyRef)(implicit ctx: ParserRuleContext): NCE =
+ newRuntimeError(s"Unexpected '$op' DSL operation for value: $v")
+ def rtBinaryOpError(op: String, v1: AnyRef, v2: AnyRef)(implicit ctx: ParserRuleContext): NCE =
+ newRuntimeError(s"Unexpected '$op' DSL operation for values: $v1, $v2")
+ def rtUnknownFunError(fun: String)(implicit ctx: ParserRuleContext): NCE =
+ newRuntimeError(s"Unknown DSL function: $fun()")
+ def rtMinParamNumError(min: Int, fun: String)(implicit ctx: ParserRuleContext): NCE =
+ newRuntimeError(s"Invalid number of parameters for DSL function ($min is required): $fun()")
+ def rtParamNumError(fun: String)(implicit ctx: ParserRuleContext): NCE =
+ newRuntimeError(s"Invalid number of parameters for DSL function: $fun()")
+ def rtParamTypeError(fun: String, param: AnyRef, expectType: String)(implicit ctx: ParserRuleContext): NCE =
+ newRuntimeError(s"Expecting '$expectType' type of parameter for DSL function '$fun()', found: $param")
/**
*
@@ -131,8 +142,8 @@ trait NCBaseDslCompiler {
* @param lteq
* @param gteq
*/
- def parseCompExpr(lt: TN, gt: TN, lteq: TN, gteq: TN): Instr = (_, stack: StackType, _) ⇒ {
- implicit val s = stack
+ def parseCompExpr(lt: TN, gt: TN, lteq: TN, gteq: TN)(implicit ctx: ParserRuleContext): Instr = (_, stack: StackType, _) ⇒ {
+ implicit val s: StackType = stack
val (v1, v2, f1, f2) = pop2()
val usedTok = f1 || f2
@@ -179,8 +190,8 @@ trait NCBaseDslCompiler {
* @param mod
* @param div
*/
- def parseMultExpr(mult: TN, mod: TN, div: TN): Instr = (_, stack: StackType, _) ⇒ {
- implicit val s = stack
+ def parseMultExpr(mult: TN, mod: TN, div: TN)(implicit ctx: ParserRuleContext): Instr = (_, stack: StackType, _) ⇒ {
+ implicit val s: StackType = stack
val (v1, v2, f1, f2) = pop2()
val usedTok = f1 || f2
@@ -216,8 +227,8 @@ trait NCBaseDslCompiler {
* @param or
* @return
*/
- def parseLogExpr(and: TN, or : TN): Instr = (_, stack: StackType, _) ⇒ {
- implicit val s = stack
+ def parseLogExpr(and: TN, or : TN)(implicit ctx: ParserRuleContext): Instr = (_, stack: StackType, _) ⇒ {
+ implicit val s: StackType = stack
val (v1, v2, f1, f2) = pop2()
@@ -239,8 +250,8 @@ trait NCBaseDslCompiler {
* @param neq
* @return
*/
- def parseEqExpr(eq: TN, neq: TN): Instr = (_, stack: StackType, _) ⇒ {
- implicit val s = stack
+ def parseEqExpr(eq: TN, neq: TN)(implicit ctx: ParserRuleContext): Instr = (_, stack: StackType, _) ⇒ {
+ implicit val s: StackType = stack
val (v1, v2, f1, f2) = pop2()
val usedTok = f1 || f2
@@ -269,8 +280,8 @@ trait NCBaseDslCompiler {
* @param plus
* @param minus
*/
- def parsePlusExpr(plus: TN, minus: TN): Instr = (_, stack: StackType, _) ⇒ {
- implicit val s = stack
+ def parsePlusExpr(plus: TN, minus: TN)(implicit ctx: ParserRuleContext): Instr = (_, stack: StackType, _) ⇒ {
+ implicit val s: StackType = stack
val (v1, v2, f1, f2) = pop2()
val usedTok = f1 || f2
@@ -302,8 +313,8 @@ trait NCBaseDslCompiler {
* @param not
* @return
*/
- def parseUnaryExpr(minus: TN, not: TN): Instr = (_, stack: StackType, _) ⇒ {
- implicit val s = stack
+ def parseUnaryExpr(minus: TN, not: TN)(implicit ctx: ParserRuleContext): Instr = (_, stack: StackType, _) ⇒ {
+ implicit val s: StackType = stack
val (v, usedTok) = pop1()
@@ -327,7 +338,7 @@ trait NCBaseDslCompiler {
* @param txt
* @return
*/
- def parseAtom(txt: String): Instr = {
+ def parseAtom(txt: String)(implicit ctx: ParserRuleContext): Instr = {
val atom =
if (txt == "null") null // Try 'null'.
else if (txt == "true") Boolean.box(true) // Try 'boolean'.
@@ -357,10 +368,10 @@ trait NCBaseDslCompiler {
* @param id
* @return
*/
- def parseCallExpr(id: TN): Instr = (tok, stack: StackType, termCtx) ⇒ {
+ def parseCallExpr(id: TN)(implicit ctx: ParserRuleContext): Instr = (tok, stack: StackType, termCtx) ⇒ {
val fun = id.getText
- implicit val evidence = stack
+ implicit val s2: StackType = stack
def ensureStack(min: Int): Unit =
if (stack.size < min)
@@ -388,7 +399,7 @@ trait NCBaseDslCompiler {
}
def get2Doubles(): (JDouble, JDouble, Boolean) = {
ensureStack(2)
-
+
val (v1, v2, f1, f2) = pop2()
if (!isJDouble(v1))
diff --git a/nlpcraft/src/main/scala/org/apache/nlpcraft/model/intent/impl/ver2/NCIntentDslCompiler.scala b/nlpcraft/src/main/scala/org/apache/nlpcraft/model/intent/impl/ver2/NCIntentDslCompiler.scala
index 3f8014b..989963d 100644
--- a/nlpcraft/src/main/scala/org/apache/nlpcraft/model/intent/impl/ver2/NCIntentDslCompiler.scala
+++ b/nlpcraft/src/main/scala/org/apache/nlpcraft/model/intent/impl/ver2/NCIntentDslCompiler.scala
@@ -62,14 +62,14 @@ object NCIntentDslCompiler extends LazyLogging {
/*
* Shared/common implementation.
*/
- override def exitUnaryExpr(ctx: Parser.UnaryExprContext): Unit = termInstrs += parseUnaryExpr(ctx.MINUS(), ctx.NOT())
- override def exitMultExpr(ctx: Parser.MultExprContext): Unit = termInstrs += parseMultExpr(ctx.MULT(), ctx.MOD(), ctx.DIV())
- override def exitPlusExpr(ctx: Parser.PlusExprContext): Unit = termInstrs += parsePlusExpr(ctx.PLUS(), ctx.MINUS())
- override def exitCompExpr(ctx: Parser.CompExprContext): Unit = termInstrs += parseCompExpr(ctx.LT(), ctx.GT(), ctx.LTEQ(), ctx.GTEQ())
- override def exitLogExpr(ctx: Parser.LogExprContext): Unit = termInstrs += parseLogExpr(ctx.AND, ctx.OR())
- override def exitEqExpr(ctx: Parser.EqExprContext): Unit = termInstrs += parseEqExpr(ctx.EQ, ctx.NEQ())
- override def exitCallExpr(ctx: Parser.CallExprContext): Unit = termInstrs += parseCallExpr(ctx.ID())
- override def exitAtom(ctx: Parser.AtomContext): Unit = termInstrs += parseAtom(ctx.getText)
+ override def exitUnaryExpr(ctx: Parser.UnaryExprContext): Unit = termInstrs += parseUnaryExpr(ctx.MINUS(), ctx.NOT())(ctx)
+ override def exitMultExpr(ctx: Parser.MultExprContext): Unit = termInstrs += parseMultExpr(ctx.MULT(), ctx.MOD(), ctx.DIV())(ctx)
+ override def exitPlusExpr(ctx: Parser.PlusExprContext): Unit = termInstrs += parsePlusExpr(ctx.PLUS(), ctx.MINUS())(ctx)
+ override def exitCompExpr(ctx: Parser.CompExprContext): Unit = termInstrs += parseCompExpr(ctx.LT(), ctx.GT(), ctx.LTEQ(), ctx.GTEQ())(ctx)
+ override def exitLogExpr(ctx: Parser.LogExprContext): Unit = termInstrs += parseLogExpr(ctx.AND, ctx.OR())(ctx)
+ override def exitEqExpr(ctx: Parser.EqExprContext): Unit = termInstrs += parseEqExpr(ctx.EQ, ctx.NEQ())(ctx)
+ override def exitCallExpr(ctx: Parser.CallExprContext): Unit = termInstrs += parseCallExpr(ctx.ID())(ctx)
+ override def exitAtom(ctx: Parser.AtomContext): Unit = termInstrs += parseAtom(ctx.getText)(ctx)
/**
*
@@ -118,17 +118,17 @@ object NCIntentDslCompiler extends LazyLogging {
Pattern.compile(flowRegex.get)
catch {
case e: PatternSyntaxException ⇒
- newSyntaxError(s"${e.getDescription} in DSL intent flow regex '${e.getPattern}' near index ${e.getIndex}.")
+ newSyntaxError(s"${e.getDescription} in intent flow regex '${e.getPattern}' near index ${e.getIndex}.")
}
}
override def exitTerm(ctx: Parser.TermContext): Unit = {
- implicit val evidence: ParserRuleContext = ctx
+ implicit val c: ParserRuleContext = ctx
if (min < 0 || min > max)
- throw newSyntaxError(s"Invalid DSL intent term min quantifiers: $min (must be min >= 0 && min <= max).")
+ throw newSyntaxError(s"Invalid intent term min quantifiers: $min (must be min >= 0 && min <= max).")
if (max < 1)
- throw newSyntaxError(s"Invalid DSL intent term max quantifiers: $max (must be max >= 1).")
+ throw newSyntaxError(s"Invalid intent term max quantifiers: $max (must be max >= 1).")
val pred =
if (termMtdName != null) { // User-code defined term.
@@ -156,7 +156,7 @@ object NCIntentDslCompiler extends LazyLogging {
}
catch {
case e: Exception ⇒
- throw runtimeError(s"Failed to invoke custom DSL intent term: $mdlCls.$termMtdName", e)
+ throw newRuntimeError(s"Failed to invoke custom intent term: $mdlCls.$termMtdName", e)
}
}
}
@@ -173,7 +173,7 @@ object NCIntentDslCompiler extends LazyLogging {
val x = stack.pop()
if (!isBoolean(x.retVal))
- throw runtimeError(s"DSL intent term does not return boolean value: ${ctx.getText}")
+ throw newRuntimeError(s"Intent term does not return boolean value: ${ctx.getText}")
(asBool(x.retVal), x.usedTok)
}
@@ -206,9 +206,11 @@ object NCIntentDslCompiler extends LazyLogging {
NCDslIntent(dsl, id, ordered, if (meta == null) Map.empty else meta, flowRegex, terms.toArray)
}
-
- override def syntaxError(errMsg: String, line: Int, pos: Int): NCE = throw new NCE(mkSyntaxError(errMsg, line, pos, dsl, mdlId))
- override def runtimeError(errMsg: String, cause: Exception = null): NCE = throw new NCE(mkRuntimeError(errMsg, dsl, mdlId), cause)
+
+ override def syntaxError(errMsg: String, srcName: String, line: Int, pos: Int): NCE =
+ throw new NCE(mkSyntaxError(errMsg, srcName, line, pos, dsl, mdlId))
+ override def runtimeError(errMsg: String, srcName: String, line: Int, pos: Int, cause: Exception = null): NCE =
+ throw new NCE(mkRuntimeError(errMsg, srcName, line, pos, dsl, mdlId), cause)
}
/**
@@ -222,38 +224,51 @@ object NCIntentDslCompiler extends LazyLogging {
*/
private def mkSyntaxError(
msg: String,
+ srcName: String,
line: Int, // 1, 2, ...
charPos: Int, // 0, 1, 2, ...
dsl: String,
+ mdlId: String): String = mkError("syntax", msg, srcName, line, charPos, dsl, mdlId)
+
+ /**
+ *
+ * @param msg
+ * @param dsl
+ * @param mdlId
+ * @param srcName
+ * @param line
+ * @param charPos
+ * @return
+ */
+ private def mkRuntimeError(
+ msg: String,
+ srcName: String,
+ line: Int,
+ charPos: Int,
+ dsl: String,
+ mdlId: String): String = mkError("runtime", msg, srcName, line, charPos, dsl, mdlId)
+
+ private def mkError(
+ kind: String,
+ msg: String,
+ srcName: String,
+ line: Int,
+ charPos: Int,
+ dsl: String,
mdlId: String): String = {
- val aLine = dsl.split("\n")(line - 1)
- val preLen = aLine.length
- val dslLine = aLine.strip()
- val postLen = dslLine.length
- val delta = preLen - postLen
+ val dslLine = dsl.split("\n")(line - 1)
val dash = "-" * dslLine.length
- val pos = Math.max(0, charPos - delta)
+ val pos = Math.max(0, charPos)
val posPtr = dash.substring(0, pos) + r("^") + y(dash.substring(pos + 1))
val dslPtr = dslLine.substring(0, pos) + r(dslLine.charAt(pos)) + y(dslLine.substring(pos + 1))
-
- s"Intent DSL syntax error at line $line:${charPos + 1} - $msg\n" +
+ val src = if (srcName == "<unknown>") "<inline>"else srcName
+
+ s"Intent DSL $kind error in '$src' at line $line:${charPos + 1} - ${U.decapitalize(msg)}\n" +
s" |-- ${c("Model:")} $mdlId\n" +
- s" |-- ${c("Intent:")} $dslPtr\n" +
- s" +-- ${c("Location:")} $posPtr"
+ s" |-- ${c("Line:")} $dslPtr\n" +
+ s" +-- ${c("Position:")} $posPtr"
}
-
- /**
- *
- * @param msg
- * @param dsl
- * @param mdlId
- * @return
- */
- private def mkRuntimeError(msg: String, dsl: String, mdlId: String): String =
- s"$msg\n" +
- s" |-- ${c("Model:")} $mdlId\n" +
- s" +-- ${c("Intent:")} $dsl\n"
-
+
/**
* Custom error handler.
*
@@ -263,20 +278,21 @@ object NCIntentDslCompiler extends LazyLogging {
class CompilerErrorListener(dsl: String, mdlId: String) extends BaseErrorListener {
/**
*
- * @param recognizer
- * @param offendingSymbol
+ * @param recog
+ * @param badSymbol
* @param line
* @param charPos
* @param msg
* @param e
*/
override def syntaxError(
- recognizer: Recognizer[_, _],
- offendingSymbol: scala.Any,
+ recog: Recognizer[_, _],
+ badSymbol: scala.Any,
line: Int, // 1, 2, ...
charPos: Int, // 1, 2, ...
msg: String,
- e: RecognitionException): Unit = throw new NCE(mkSyntaxError(msg, line, charPos - 1, dsl, mdlId))
+ e: RecognitionException): Unit =
+ throw new NCE(mkSyntaxError(msg, recog.getInputStream.getSourceName, line, charPos - 1, dsl, mdlId))
}
/**
diff --git a/nlpcraft/src/test/scala/org/apache/nlpcraft/model/intent/dsl/NCIntentDslCompilerSpec.scala b/nlpcraft/src/test/scala/org/apache/nlpcraft/model/intent/dsl/NCIntentDslCompilerSpec.scala
index 331a491..5b90de5 100644
--- a/nlpcraft/src/test/scala/org/apache/nlpcraft/model/intent/dsl/NCIntentDslCompilerSpec.scala
+++ b/nlpcraft/src/test/scala/org/apache/nlpcraft/model/intent/dsl/NCIntentDslCompilerSpec.scala
@@ -88,6 +88,13 @@ class NCIntentDslCompilerSpec {
checkError(
"""
|intent=i1
+ | flow="a[^0-9b"
+ | term(t1)={true}
+ |""".stripMargin
+ )
+ checkError(
+ """
+ |intent=i1
| flow="a[^0-9]b"
| term(t1)={has(json("{'a': true, 'b\'2': {'arr': [1, 2, 3]}}"), map("k1\"", 'v1\'v1', "k2", "v2"))}[1:2]
|""".stripMargin