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