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 f94daf8 WIP.
f94daf8 is described below
commit f94daf85c0817e339a9ccb243dc1b73cc219c07a
Author: Aaron Radzinski <[email protected]>
AuthorDate: Thu Feb 25 17:20:47 2021 -0800
WIP.
---
.../nlpcraft/common/makro/NCMacroCompiler.scala | 13 +-
.../model/intent/impl/antlr4/NCIntentDsl.g4 | 10 +-
.../model/intent/impl/ver2/NCBaseDslCompiler.scala | 787 ++++++++++-----------
.../intent/impl/ver2/NCIntentDslCompiler.scala | 93 ++-
.../model/intent/dsl/NCDslCompilerSpec.scala | 72 +-
5 files changed, 520 insertions(+), 455 deletions(-)
diff --git
a/nlpcraft/src/main/scala/org/apache/nlpcraft/common/makro/NCMacroCompiler.scala
b/nlpcraft/src/main/scala/org/apache/nlpcraft/common/makro/NCMacroCompiler.scala
index a8d9d7c..340b3e6 100644
---
a/nlpcraft/src/main/scala/org/apache/nlpcraft/common/makro/NCMacroCompiler.scala
+++
b/nlpcraft/src/main/scala/org/apache/nlpcraft/common/makro/NCMacroCompiler.scala
@@ -211,7 +211,7 @@ object NCMacroCompiler extends LazyLogging {
line: Int,
charPos: Int,
msg: String,
- e: RecognitionException): Unit = throw new
NCE(mkCompilerError(msg, line, charPos, in))
+ e: RecognitionException): Unit = throw new
NCE(mkCompilerError(msg, line, charPos, in))
}
/**
@@ -224,16 +224,17 @@ object NCMacroCompiler extends LazyLogging {
private def mkCompilerError(
msg: String,
line: Int,
- charPos: Int,
+ charPos: Int, // 1, 2, ...
in: String
): String = {
val dash = "-" * in.length
- val pos = Math.max(1, charPos)
- val charPosPtr = dash.substring(0, pos - 1) + r("^") +
dash.substring(pos)
+ val pos = Math.max(0, charPos - 1)
+ val posPtr = dash.substring(0, pos) + r("^") + dash.substring(pos + 1)
+ val inPtr = in.substring(0, pos) + r(in.charAt(pos)) +
in.substring(pos + 1)
s"Macro compiler error at line $line:$charPos - $msg\n" +
- s" |-- ${c("Macro:")} $in\n" +
- s" +-- ${c("Error:")} $charPosPtr"
+ s" |-- ${c("Macro:")} $inPtr\n" +
+ s" +-- ${c("Location:")} $posPtr"
}
/**
diff --git
a/nlpcraft/src/main/scala/org/apache/nlpcraft/model/intent/impl/antlr4/NCIntentDsl.g4
b/nlpcraft/src/main/scala/org/apache/nlpcraft/model/intent/impl/antlr4/NCIntentDsl.g4
index 278746f..f273be2 100644
---
a/nlpcraft/src/main/scala/org/apache/nlpcraft/model/intent/impl/antlr4/NCIntentDsl.g4
+++
b/nlpcraft/src/main/scala/org/apache/nlpcraft/model/intent/impl/antlr4/NCIntentDsl.g4
@@ -129,11 +129,19 @@ EXP: [Ee] [+\-]? INT;
fragment UNI_CHAR // International chars.
: '\u00B7'
| '\u0300'..'\u036F'
+ | '\u00A0'..'\u00FF' /* Latin-1 Supplement. */
+ | '\u0100'..'\u017F' /* Latin Extended-A. */
+ | '\u0180'..'\u024F' /* Latin Extended-B. */
+ | '\u1E02'..'\u1EF3' /* Latin Extended Additional. */
+ | '\u0259'..'\u0292' /* IPA Extensions. */
+ | '\u02B0'..'\u02FF' /* Spacing modifier letters. */
| '\u203F'..'\u2040'
| '\u00C0'..'\u00D6'
| '\u00D8'..'\u00F6'
| '\u00F8'..'\u02FF'
- | '\u0370'..'\u037D'
+ | '\u0370'..'\u03FF' /* Greek and Coptic. */
+ | '\u1F01'..'\u1FFF' /* Greek Extended. */
+ | '\u0400'..'\u04FF' /* Cyrillic. */
| '\u037F'..'\u1FFF'
| '\u200C'..'\u200D'
| '\u2070'..'\u218F'
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 298cbbb..eb0a48e 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
@@ -17,19 +17,35 @@
package org.apache.nlpcraft.model.intent.impl.ver2
+import org.antlr.v4.runtime.ParserRuleContext
import org.antlr.v4.runtime.tree.{TerminalNode ⇒ TN}
import org.apache.commons.lang3.StringUtils
import org.apache.nlpcraft.model.NCToken
import org.apache.nlpcraft.model.intent.utils.ver2.{NCDslTermContext,
NCDslTermRetVal}
import org.apache.nlpcraft.common._
-import java.lang.{Double ⇒ JDouble, IllegalArgumentException ⇒ IAE, Long ⇒
JLong}
+import java.lang.{Double ⇒ JDouble, Long ⇒ JLong}
import java.time.LocalDate
import java.util.{Collections, ArrayList ⇒ JArrayList, HashMap ⇒ JHashMap}
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
+
+ /**
+ *
+ * @param errMsg
+ * @param ctx
+ * @return
+ */
+ def newSyntaxError(errMsg: String)(implicit ctx: ParserRuleContext): NCE =
{
+ val tok = ctx.start
+
+ syntaxError(errMsg, tok.getLine, tok.getCharPositionInLine)
+ }
+
type StackType = mutable.ArrayStack[NCDslTermRetVal]
type Instr = (NCToken, StackType, NCDslTermContext) ⇒ Unit
@@ -51,18 +67,19 @@ trait NCBaseDslCompiler {
def pushBool(any: Boolean, usedTok: Boolean)(implicit stack: StackType):
Unit =
stack.push(NCDslTermRetVal(Boolean.box(any), usedTok))
- def errUnaryOp(op: String, v: AnyRef): IAE =
- new IAE(s"Unexpected '$op' DSL operation for value: $v")
- def errBinaryOp(op: String, v1: AnyRef, v2: AnyRef): IAE =
- new IAE(s"Unexpected '$op' DSL operation for values: $v1, $v2")
- def errUnknownFun(fun: String): IAE =
- new IAE(s"Unknown DSL function: $fun()")
- def errMinParamNum(min: Int, fun: String): IAE =
- new IAE(s"Invalid number of parameters for DSL function ($min is
required): $fun()")
- def errParamNum(fun: String): IAE =
- new IAE(s"Invalid number of parameters for DSL function: $fun()")
- def errParamType(fun: String, param: AnyRef, expectType: String): IAE =
- new IAE(s"Expecting '$expectType' type of parameter for DSL function
'$fun()', found: $param")
+ // 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")
/**
*
@@ -126,7 +143,7 @@ trait NCBaseDslCompiler {
else if (isJDouble(v1) && isJLong(v2)) pushBool(asJDouble(v1) <
asJLong(v2), usedTok)
else if (isJDouble(v1) && isJDouble(v2)) pushBool(asJDouble(v1) <
asJDouble(v2), usedTok)
else
- throw errBinaryOp("<", v1, v2)
+ throw rtBinaryOpError("<", v1, v2)
}
else if (gt != null) {
if (isJLong(v1) && isJLong(v2)) pushBool(asJLong(v1) >
asJLong(v2), usedTok)
@@ -134,7 +151,7 @@ trait NCBaseDslCompiler {
else if (isJDouble(v1) && isJLong(v2)) pushBool(asJDouble(v1) >
asJLong(v2), usedTok)
else if (isJDouble(v1) && isJDouble(v2)) pushBool(asJDouble(v1) >
asJDouble(v2), usedTok)
else
- throw errBinaryOp(">", v1, v2)
+ throw rtBinaryOpError(">", v1, v2)
}
else if (lteq != null) {
if (isJLong(v1) && isJLong(v2)) pushBool(asJLong(v1) <=
asJLong(v2), usedTok)
@@ -142,7 +159,7 @@ trait NCBaseDslCompiler {
else if (isJDouble(v1) && isJLong(v2)) pushBool(asJDouble(v1) <=
asJLong(v2), usedTok)
else if (isJDouble(v1) && isJDouble(v2)) pushBool(asJDouble(v1) <=
asJDouble(v2), usedTok)
else
- throw errBinaryOp("<=", v1, v2)
+ throw rtBinaryOpError("<=", v1, v2)
}
else {
assert(gteq != null)
@@ -152,7 +169,7 @@ trait NCBaseDslCompiler {
else if (isJDouble(v1) && isJLong(v2)) pushBool(asJDouble(v1) >=
asJLong(v2), usedTok)
else if (isJDouble(v1) && isJDouble(v2)) pushBool(asJDouble(v1) >=
asJDouble(v2), usedTok)
else
- throw errBinaryOp(">=", v1, v2)
+ throw rtBinaryOpError(">=", v1, v2)
}
}
@@ -174,12 +191,12 @@ trait NCBaseDslCompiler {
else if (isJDouble(v1) && isJLong(v2)) pushDouble(asJDouble(v1) *
asJLong(v2), usedTok)
else if (isJDouble(v1) && isJDouble(v2)) pushDouble(asJDouble(v1)
* asJDouble(v2), usedTok)
else
- throw errBinaryOp("*", v1, v2)
+ throw rtBinaryOpError("*", v1, v2)
}
else if (mod != null) {
if (isJLong(v1) && isJLong(v2)) pushLong(asJLong(v1) %
asJLong(v2), usedTok)
else
- throw errBinaryOp("%", v1, v2)
+ throw rtBinaryOpError("%", v1, v2)
}
else {
assert(div != null)
@@ -189,7 +206,7 @@ trait NCBaseDslCompiler {
else if (isJDouble(v1) && isJLong(v2)) pushDouble(asJDouble(v1) /
asJLong(v2), usedTok)
else if (isJDouble(v1) && isJDouble(v2)) pushDouble(asJDouble(v1)
/ asJDouble(v2), usedTok)
else
- throw errBinaryOp("/", v1, v2)
+ throw rtBinaryOpError("/", v1, v2)
}
}
@@ -205,7 +222,7 @@ trait NCBaseDslCompiler {
val (v1, v2, f1, f2) = pop2()
if (!isBoolean(v1) || !isBoolean(v2))
- throw errBinaryOp(if (and != null) "&&" else "||", v1, v2)
+ throw rtBinaryOpError(if (and != null) "&&" else "||", v1, v2)
if (and != null)
pushBool(asBool(v1) && asBool(v2), f1 || f2) // Note logical OR
for used token flag.
@@ -234,7 +251,7 @@ trait NCBaseDslCompiler {
if (isJLong(v1) && isJLong(v2))
asJLong(v1) == asJLong(v2)
else
- throw errBinaryOp(op, v1, v2)
+ throw rtBinaryOpError(op, v1, v2)
}
@@ -265,7 +282,7 @@ trait NCBaseDslCompiler {
else if (isJDouble(v1) && isJLong(v2)) pushDouble(asJDouble(v1) +
asJLong(v2), usedTok)
else if (isJDouble(v1) && isJDouble(v2)) pushDouble(asJDouble(v1)
+ asJDouble(v2), usedTok)
else
- throw errBinaryOp("+", v1, v2)
+ throw rtBinaryOpError("+", v1, v2)
}
else {
assert(minus != null)
@@ -275,7 +292,7 @@ trait NCBaseDslCompiler {
else if (isJDouble(v1) && isJLong(v2)) pushDouble(asJDouble(v1) -
asJLong(v2), usedTok)
else if (isJDouble(v1) && isJDouble(v2)) pushDouble(asJDouble(v1)
- asJDouble(v2), usedTok)
else
- throw errBinaryOp("-", v1, v2)
+ throw rtBinaryOpError("-", v1, v2)
}
}
@@ -294,14 +311,14 @@ trait NCBaseDslCompiler {
if (isJDouble(v)) pushDouble(-asJDouble(v), usedTok)
else if (isJLong(v)) pushLong(-asJLong(v), usedTok)
else
- throw errUnaryOp("-", v)
+ throw rtUnaryOpError("-", v)
}
else {
assert(not != null)
if (isBoolean(v)) pushBool(!asBool(v), usedTok)
else
- throw errUnaryOp("!", v)
+ throw rtUnaryOpError("!", v)
}
}
@@ -334,419 +351,385 @@ trait NCBaseDslCompiler {
(_, stack, _) ⇒ pushAny(atom, false)(stack)
}
-
- private final val FUNCTIONS = Set(
- "meta_token", "meta_model", "meta_intent",
"meta_req", "meta_user",
- "meta_company", "meta_sys", "meta_conv", "json",
"if",
- "id", "ancestors", "parent", "groups",
"value",
- "aliases", "start_idx", "end_idx", "req_id",
"req_normtext",
- "req_tstamp", "req_addr", "req_agent", "user_id",
"user_fname",
- "user_lname", "user_email", "user_admin",
"user_signup_tstamp", "comp_id",
- "comp_name", "comp_website", "comp_country",
"comp_region", "comp_city",
- "comp_addr", "comp_postcode", "trim", "strip",
"uppercase",
- "lowercase", "is_alpha", "is_alphanum",
"is_whitespace", "is_num",
- "is_numspace", "is_alphaspace", "is_alphanumspace",
"substring", "index",
- "regex", "soundex", "split",
"split_trim", "replace",
- "abs", "ceil", "floor", "rint",
"round",
- "signum", "sqrt", "cbrt", "pi",
"euler",
- "acos", "asin", "atan", "cos",
"sin",
- "tan", "cosh", "sinh", "tanh",
"atn2",
- "degrees", "radians", "exp", "expm1",
"hypot",
- "log", "log10", "log1p", "pow",
"rand",
- "square", "list", "map", "get",
"index",
- "has", "tail", "add", "remove",
"first",
- "last", "keys", "values", "length",
"count",
- "take", "drop", "size", "length",
"reverse",
- "is_empty", "non_empty", "to_string", "avg",
"max",
- "min", "stdev", "sum", "year",
"month",
- "day_of_month", "day_of_week", "day_of_year", "hour",
"min",
- "sec", "week_of_month", "week_of_year", "quarter",
"msec",
- "now"
- )
/**
*
* @param id
* @return
*/
- def parseCallExpr(id: TN): Instr = {
+ def parseCallExpr(id: TN): Instr = (tok, stack: StackType, termCtx) ⇒ {
val fun = id.getText
- if (!FUNCTIONS.contains(fun))
- throw errUnknownFun(fun)
+ implicit val evidence = stack
- (tok, stack: StackType, termCtx) ⇒ {
- implicit val evidence = stack
+ def ensureStack(min: Int): Unit =
+ if (stack.size < min)
+ throw rtMinParamNumError(min, fun)
- def ensureStack(min: Int): Unit =
- if (stack.size < min)
- throw errMinParamNum(min, fun)
+ def get1Str(): (String, Boolean) = {
+ ensureStack(1)
- def get1Str(): (String, Boolean) = {
- ensureStack(1)
+ val (v, f) = pop1()
- val (v, f) = pop1()
+ if (!isString(v))
+ throw rtParamTypeError(fun, v, "string")
- if (!isString(v))
- throw errParamType(fun, v, "string")
-
- (asString(v), f)
- }
- def get1Double(): (JDouble, Boolean) = {
- ensureStack(1)
-
- val (v, f) = pop1()
+ (asString(v), f)
+ }
+ def get1Double(): (JDouble, Boolean) = {
+ ensureStack(1)
- if (!isJDouble(v))
- throw errParamType(fun, v, "double")
+ val (v, f) = pop1()
- (asJDouble(v), f)
- }
- def get2Doubles(): (JDouble, JDouble, Boolean) = {
- ensureStack(2)
+ if (!isJDouble(v))
+ throw rtParamTypeError(fun, v, "double")
- val (v1, v2, f1, f2) = pop2()
+ (asJDouble(v), f)
+ }
+ def get2Doubles(): (JDouble, JDouble, Boolean) = {
+ ensureStack(2)
- if (!isJDouble(v1))
- throw errParamType(fun, v1, "double")
- if (!isJDouble(v2))
- throw errParamType(fun, v2, "double")
+ val (v1, v2, f1, f2) = pop2()
- (asJDouble(v1), asJDouble(v2), f1 || f2)
- }
- def get1Any(): (AnyRef, Boolean) = {
- ensureStack(1)
+ if (!isJDouble(v1))
+ throw rtParamTypeError(fun, v1, "double")
+ if (!isJDouble(v2))
+ throw rtParamTypeError(fun, v2, "double")
- pop1()
- }
-
- /*
- * String operations.
- */
- def doTrim(): Unit = get1Str() match { case (s, f) ⇒
pushAny(s.trim, f) }
- def doUppercase(): Unit = get1Str() match { case (s, f) ⇒
pushAny(s.toUpperCase, f) }
- def doLowercase(): Unit = get1Str() match { case (s, f) ⇒
pushAny(s.toLowerCase, f) }
- def doIsAlpha(): Unit = get1Str() match { case (s, f) ⇒
pushBool(StringUtils.isAlpha(asString(s)), f) }
- def doIsNum(): Unit = get1Str() match { case (s, f) ⇒
pushBool(StringUtils.isNumeric(asString(s)), f) }
- def doIsAlphaNum(): Unit = get1Str() match { case (s, f) ⇒
pushBool(StringUtils.isAlphanumeric(asString(s)), f) }
- def doIsWhitespace(): Unit = get1Str() match { case (s, f) ⇒
pushBool(StringUtils.isWhitespace(asString(s)), f) }
- def doIsAlphaSpace(): Unit = get1Str() match { case (s, f) ⇒
pushBool(StringUtils.isAlphaSpace(asString(s)), f) }
- def doIsAlphaNumSpace(): Unit = get1Str() match { case (s, f) ⇒
pushBool(StringUtils.isAlphanumericSpace(asString(s)), f) }
- def doIsNumSpace(): Unit = get1Str() match { case (s, f) ⇒
pushBool(StringUtils.isNumericSpace(asString(s)), f) }
-
- def doSplit(): Unit = {
- ensureStack(2)
-
- val (v1, v2, f1, f2) = pop2()
-
- if (!isString(v1))
- errParamType(fun, v1, "string")
- if (!isString(v2))
- errParamType(fun, v2, "string")
-
- asString(v1).split(asString(v2)).foreach { pushAny(_, f1 ||
f2) }
- }
- def doSplitTrim(): Unit = {
- ensureStack(2)
+ (asJDouble(v1), asJDouble(v2), f1 || f2)
+ }
+ def get1Any(): (AnyRef, Boolean) = {
+ ensureStack(1)
- val (v1, v2, f1, f2) = pop2()
+ pop1()
+ }
- if (!isString(v1))
- errParamType(fun, v1, "string")
- if (!isString(v2))
- errParamType(fun, v2, "string")
+ /*
+ * String operations.
+ */
+ def doTrim(): Unit = get1Str() match { case (s, f) ⇒ pushAny(s.trim,
f) }
+ def doUppercase(): Unit = get1Str() match { case (s, f) ⇒
pushAny(s.toUpperCase, f) }
+ def doLowercase(): Unit = get1Str() match { case (s, f) ⇒
pushAny(s.toLowerCase, f) }
+ def doIsAlpha(): Unit = get1Str() match { case (s, f) ⇒
pushBool(StringUtils.isAlpha(asString(s)), f) }
+ def doIsNum(): Unit = get1Str() match { case (s, f) ⇒
pushBool(StringUtils.isNumeric(asString(s)), f) }
+ def doIsAlphaNum(): Unit = get1Str() match { case (s, f) ⇒
pushBool(StringUtils.isAlphanumeric(asString(s)), f) }
+ def doIsWhitespace(): Unit = get1Str() match { case (s, f) ⇒
pushBool(StringUtils.isWhitespace(asString(s)), f) }
+ def doIsAlphaSpace(): Unit = get1Str() match { case (s, f) ⇒
pushBool(StringUtils.isAlphaSpace(asString(s)), f) }
+ def doIsAlphaNumSpace(): Unit = get1Str() match { case (s, f) ⇒
pushBool(StringUtils.isAlphanumericSpace(asString(s)), f) }
+ def doIsNumSpace(): Unit = get1Str() match { case (s, f) ⇒
pushBool(StringUtils.isNumericSpace(asString(s)), f) }
+
+ def doSplit(): Unit = {
+ ensureStack(2)
+
+ val (v1, v2, f1, f2) = pop2()
+
+ if (!isString(v1))
+ rtParamTypeError(fun, v1, "string")
+ if (!isString(v2))
+ rtParamTypeError(fun, v2, "string")
+
+ asString(v1).split(asString(v2)).foreach { pushAny(_, f1 || f2) }
+ }
+ def doSplitTrim(): Unit = {
+ ensureStack(2)
- asString(v1).split(asString(v2)).foreach { s ⇒
pushAny(s.strip, f1 || f2) }
- }
+ val (v1, v2, f1, f2) = pop2()
- /*
- * Collection, statistical operations.
- */
- def doList(): Unit = {
- val jl = new JArrayList[Object]() // Empty list is allowed.
- var f = false
+ if (!isString(v1))
+ rtParamTypeError(fun, v1, "string")
+ if (!isString(v2))
+ rtParamTypeError(fun, v2, "string")
- stack.drain { x ⇒
- jl.add(x.retVal)
- f = f || x.usedTok
- }
+ asString(v1).split(asString(v2)).foreach { s ⇒ pushAny(s.strip, f1
|| f2) }
+ }
- Collections.reverse(jl)
+ /*
+ * Collection, statistical operations.
+ */
+ def doList(): Unit = {
+ val jl = new JArrayList[Object]() // Empty list is allowed.
+ var f = false
- pushAny(jl, f)
+ stack.drain { x ⇒
+ jl.add(x.retVal)
+ f = f || x.usedTok
}
- def doMap(): Unit = {
- if (stack.size % 2 != 0)
- errParamNum(fun)
- val jm = new JHashMap[Object, Object]()
- var f = false
+ Collections.reverse(jl)
- val keys = mutable.Buffer.empty[AnyRef]
- val vals = mutable.Buffer.empty[AnyRef]
+ pushAny(jl, f)
+ }
+ def doMap(): Unit = {
+ if (stack.size % 2 != 0)
+ rtParamNumError(fun)
- var idx = 0
+ val jm = new JHashMap[Object, Object]()
+ var f = false
- stack.drain { x ⇒
- if (idx % 2 == 0) keys += x.retVal else vals += x.retVal
- f = f || x.usedTok
+ val keys = mutable.Buffer.empty[AnyRef]
+ val vals = mutable.Buffer.empty[AnyRef]
- idx += 1
- }
+ var idx = 0
- for ((k, v) ← keys zip vals)
- jm.put(k, v)
+ stack.drain { x ⇒
+ if (idx % 2 == 0) keys += x.retVal else vals += x.retVal
+ f = f || x.usedTok
- pushAny(jm, f)
+ idx += 1
}
- /*
- * Metadata operations.
- */
- def doTokenMeta(): Unit = get1Str() match { case (s, _) ⇒
pushAny(tok.meta(s), true) }
- def doModelMeta(): Unit = get1Str() match { case (s, _) ⇒
pushAny(tok.getModel.meta(s), false) }
- def doReqMeta(): Unit = get1Str() match { case (s, _) ⇒
pushAny(termCtx.reqMeta.get(s).orNull, false) }
- def doSysMeta(): Unit = get1Str() match { case (s, _) ⇒
pushAny(U.sysEnv(s).orNull, false) }
- def doUserMeta(): Unit = get1Str() match { case (s, _) ⇒
pushAny(termCtx.usrMeta.get(s).orNull, false) }
- def doConvMeta(): Unit = get1Str() match { case (s, _) ⇒
pushAny(termCtx.convMeta.get(s).orNull, false) }
- def doCompMeta(): Unit = get1Str() match { case (s, _) ⇒
pushAny(termCtx.compMeta.get(s).orNull, false) }
- def doIntentMeta(): Unit = get1Str() match { case (s, _) ⇒
pushAny(termCtx.intentMeta.get(s).orNull, false) }
-
- /*
- * Math operations.
- */
- def doAbs(): Unit = get1Any() match {
- case (a: JLong, f) ⇒ pushLong(Math.abs(a), f)
- case (a: JDouble, f) ⇒ pushDouble(Math.abs(a), f)
- case x ⇒ errParamType(fun, x, "numeric")
- }
- def doSquare(): Unit = get1Any() match {
- case (a: JLong, f) ⇒ pushLong(a * a, f)
- case (a: JDouble, f) ⇒ pushDouble(a * a, f)
- case x ⇒ errParamType(fun, x, "numeric")
- }
- def doCeil(): Unit = get1Double() match { case (a: JDouble, f) ⇒
pushDouble(Math.ceil(a), f) }
- def doFloor(): Unit = get1Double() match { case (a: JDouble, f) ⇒
pushDouble(Math.floor(a), f) }
- def doSignum(): Unit = get1Double() match { case (a: JDouble, f) ⇒
pushDouble(Math.signum(a), f) }
- def doAcos(): Unit = get1Double() match { case (a: JDouble, f) ⇒
pushDouble(Math.acos(a), f) }
- def doAsin(): Unit = get1Double() match { case (a: JDouble, f) ⇒
pushDouble(Math.asin(a), f) }
- def doSin(): Unit = get1Double() match { case (a: JDouble, f) ⇒
pushDouble(Math.sin(a), f) }
- def doCos(): Unit = get1Double() match { case (a: JDouble, f) ⇒
pushDouble(Math.cos(a), f) }
- def doRint(): Unit = get1Double() match { case (a: JDouble, f) ⇒
pushDouble(Math.rint(a), f) }
- def doRound(): Unit = get1Double() match { case (a: JDouble, f) ⇒
pushLong(Math.round(a), f) }
- def doSqrt(): Unit = get1Double() match { case (a: JDouble, f) ⇒
pushDouble(Math.sqrt(a), f) }
- def doCbrt(): Unit = get1Double() match { case (a: JDouble, f) ⇒
pushDouble(Math.cbrt(a), f) }
- def doAtan(): Unit = get1Double() match { case (a: JDouble, f) ⇒
pushDouble(Math.atan(a), f) }
- def doTan(): Unit = get1Double() match { case (a: JDouble, f) ⇒
pushDouble(Math.tan(a), f) }
- def doCosh(): Unit = get1Double() match { case (a: JDouble, f) ⇒
pushDouble(Math.cosh(a), f) }
- def doSinh(): Unit = get1Double() match { case (a: JDouble, f) ⇒
pushDouble(Math.sinh(a), f) }
- def doTanh(): Unit = get1Double() match { case (a: JDouble, f) ⇒
pushDouble(Math.tanh(a), f) }
- def doLog(): Unit = get1Double() match { case (a: JDouble, f) ⇒
pushDouble(Math.log(a), f) }
- def doLog1p(): Unit = get1Double() match { case (a: JDouble, f) ⇒
pushDouble(Math.log1p(a), f) }
- def doLog10(): Unit = get1Double() match { case (a: JDouble, f) ⇒
pushDouble(Math.log10(a), f) }
- def doDegrees(): Unit = get1Double() match { case (a: JDouble, f)
⇒ pushDouble(Math.toDegrees(a), f) }
- def doRadians(): Unit = get1Double() match { case (a: JDouble, f)
⇒ pushDouble(Math.toRadians(a), f) }
- def doExp(): Unit = get1Double() match { case (a: JDouble, f) ⇒
pushDouble(Math.exp(a), f) }
- def doExpm1(): Unit = get1Double() match { case (a: JDouble, f) ⇒
pushDouble(Math.expm1(a), f) }
- def doRandom(): Unit = pushDouble(Math.random, false)
- def doPi(): Unit = pushDouble(Math.PI, false)
- def doEuler(): Unit = pushDouble(Math.E, false)
- def doPow(): Unit = get2Doubles() match { case (a1: JDouble, a2:
JDouble, f) ⇒ pushDouble(Math.pow(a1, a2), f) }
- def doHypot(): Unit = get2Doubles() match { case (a1: JDouble, a2:
JDouble, f) ⇒ pushDouble(Math.hypot(a1, a2), f) }
- def doAtan2(): Unit = get2Doubles() match { case (a1: JDouble, a2:
JDouble, f) ⇒ pushDouble(Math.atan2(a1, a2), f) }
-
- /*
- * User operations.
- */
- def doUserId(): Unit = pushLong(termCtx.req.getUser.getId, false)
-
- /*
- * Company operations.
- */
- def doCompId(): Unit = pushLong(termCtx.req.getCompany.getId,
false)
-
- /*
- * Request operations.
- */
- def doReqId(): Unit = pushAny(termCtx.req.getServerRequestId,
false)
-
- /*
- * Date-time operations.
- */
- def doYear(): Unit = pushLong(LocalDate.now.getYear,false)
- def doMonth(): Unit = pushLong(LocalDate.now.getMonthValue,false)
- def doDayOfMonth(): Unit =
pushLong(LocalDate.now.getDayOfMonth,false)
- def doDayOfWeek(): Unit =
pushLong(LocalDate.now.getDayOfWeek.getValue,false)
- def doDayOfYear(): Unit =
pushLong(LocalDate.now.getDayOfYear,false)
-
- def doJson(): Unit = get1Str() match { case (s, f) ⇒
pushAny(U.jsonToJavaMap(asString(s)), f) }
- def doIf(): Unit = {
- ensureStack(3)
-
- val (v1, v2, v3, f1, f2, f3) = pop3()
-
- if (!isBoolean(v1))
- throw errParamType(fun, v1, "boolean")
-
- if (asBool(v1))
- pushAny(v2, f1 || f2)
- else
- pushAny(v3, f1 || f3)
- }
+ for ((k, v) ← keys zip vals)
+ jm.put(k, v)
- fun match {
- // Metadata access.
- case "meta_token" ⇒ doTokenMeta()
- case "meta_model" ⇒ doModelMeta()
- case "meta_intent" ⇒ doIntentMeta()
- case "meta_req" ⇒ doReqMeta()
- case "meta_user" ⇒ doUserMeta()
- case "meta_company" ⇒ doCompMeta()
- case "meta_sys" ⇒ doSysMeta()
- case "meta_conv" ⇒ doConvMeta()
-
- // Converts JSON to map.
- case "json" ⇒ doJson()
-
- // Inline if-statement.
- case "if" ⇒ doIf()
-
- // Token functions.
- case "id" ⇒ pushAny(tok.getId, true)
- case "ancestors" ⇒ pushAny(tok.getAncestors, true)
- case "parent" ⇒ pushAny(tok.getParentId, true)
- case "groups" ⇒ pushAny(tok.getGroups, true)
- case "value" ⇒ pushAny(tok.getValue, true)
- case "aliases" ⇒ pushAny(tok.getAliases, true)
- case "start_idx" ⇒ pushLong(tok.getStartCharIndex, true)
- case "end_idx" ⇒ pushLong(tok.getEndCharIndex, true)
-
- // Request data.
- case "req_id" ⇒ doReqId()
- case "req_normtext" ⇒
- case "req_tstamp" ⇒
- case "req_addr" ⇒
- case "req_agent" ⇒
-
- // User data.
- case "user_id" ⇒ doUserId()
- case "user_fname" ⇒
- case "user_lname" ⇒
- case "user_email" ⇒
- case "user_admin" ⇒
- case "user_signup_tstamp" ⇒
-
- // Company data.
- case "comp_id" ⇒ doCompId()
- case "comp_name" ⇒
- case "comp_website" ⇒
- case "comp_country" ⇒
- case "comp_region" ⇒
- case "comp_city" ⇒
- case "comp_addr" ⇒
- case "comp_postcode" ⇒
-
- // String functions.
- case "trim" ⇒ doTrim()
- case "strip" ⇒ doTrim()
- case "uppercase" ⇒ doUppercase()
- case "lowercase" ⇒ doLowercase()
- case "is_alpha" ⇒ doIsAlpha()
- case "is_alphanum" ⇒ doIsAlphaNum()
- case "is_whitespace" ⇒ doIsWhitespace()
- case "is_num" ⇒ doIsNum()
- case "is_numspace" ⇒ doIsNumSpace()
- case "is_alphaspace" ⇒ doIsAlphaSpace()
- case "is_alphanumspace" ⇒ doIsAlphaNumSpace()
- case "substring" ⇒
- case "index" ⇒
- case "regex" ⇒
- case "soundex" ⇒
- case "split" ⇒ doSplit()
- case "split_trim" ⇒ doSplitTrim()
- case "replace" ⇒
-
- // Math functions.
- case "abs" ⇒ doAbs()
- case "ceil" ⇒ doCeil()
- case "floor" ⇒ doFloor()
- case "rint" ⇒ doRint()
- case "round" ⇒ doRound()
- case "signum" ⇒ doSignum()
- case "sqrt" ⇒ doSqrt()
- case "cbrt" ⇒ doCbrt()
- case "pi" ⇒ doPi()
- case "euler" ⇒ doEuler()
- case "acos" ⇒ doAcos()
- case "asin" ⇒ doAsin()
- case "atan" ⇒ doAtan()
- case "cos" ⇒ doCos()
- case "sin" ⇒ doSin()
- case "tan" ⇒ doTan()
- case "cosh" ⇒ doCosh()
- case "sinh" ⇒ doSinh()
- case "tanh" ⇒ doTanh()
- case "atn2" ⇒ doAtan2()
- case "degrees" ⇒ doDegrees()
- case "radians" ⇒ doRadians()
- case "exp" ⇒ doExp()
- case "expm1" ⇒ doExpm1()
- case "hypot" ⇒ doHypot()
- case "log" ⇒ doLog()
- case "log10" ⇒ doLog10()
- case "log1p" ⇒ doLog1p()
- case "pow" ⇒ doPow()
- case "rand" ⇒ doRandom()
- case "square" ⇒ doSquare()
-
- // Collection functions.
- case "list" ⇒ doList()
- case "map" ⇒ doMap()
- case "get" ⇒
- case "index" ⇒
- case "has" ⇒
- case "tail" ⇒
- case "add" ⇒
- case "remove" ⇒
- case "first" ⇒
- case "last" ⇒
- case "keys" ⇒
- case "values" ⇒
- case "length" ⇒
- case "count" ⇒
- case "take" ⇒
- case "drop" ⇒
- case "size" ⇒
- case "length" ⇒
- case "reverse" ⇒
- case "is_empty" ⇒
- case "non_empty" ⇒
- case "to_string" ⇒
-
- // Statistical operations.
- case "avg" ⇒
- case "max" ⇒ // Works for numerics as well.
- case "min" ⇒ // Works for numerics as well.
- case "stdev" ⇒
- case "sum" ⇒
-
- // Date-time functions.
- case "year" ⇒ doYear() // 2021.
- case "month" ⇒ doMonth() // 1 ... 12.
- case "day_of_month" ⇒ doDayOfMonth() // 1 ... 31.
- case "day_of_week" ⇒ doDayOfWeek()
- case "day_of_year" ⇒ doDayOfYear()
- case "hour" ⇒
- case "min" ⇒
- case "sec" ⇒
- case "week_of_month" ⇒
- case "week_of_year" ⇒
- case "quarter" ⇒
- case "msec" ⇒
- case "now" ⇒ // Epoc time.
-
- case _ ⇒ throw errUnknownFun(fun) // Assertion.
- }
+ pushAny(jm, f)
+ }
+
+ /*
+ * Metadata operations.
+ */
+ def doTokenMeta(): Unit = get1Str() match { case (s, _) ⇒
pushAny(tok.meta(s), true) }
+ def doModelMeta(): Unit = get1Str() match { case (s, _) ⇒
pushAny(tok.getModel.meta(s), false) }
+ def doReqMeta(): Unit = get1Str() match { case (s, _) ⇒
pushAny(termCtx.reqMeta.get(s).orNull, false) }
+ def doSysMeta(): Unit = get1Str() match { case (s, _) ⇒
pushAny(U.sysEnv(s).orNull, false) }
+ def doUserMeta(): Unit = get1Str() match { case (s, _) ⇒
pushAny(termCtx.usrMeta.get(s).orNull, false) }
+ def doConvMeta(): Unit = get1Str() match { case (s, _) ⇒
pushAny(termCtx.convMeta.get(s).orNull, false) }
+ def doCompMeta(): Unit = get1Str() match { case (s, _) ⇒
pushAny(termCtx.compMeta.get(s).orNull, false) }
+ def doIntentMeta(): Unit = get1Str() match { case (s, _) ⇒
pushAny(termCtx.intentMeta.get(s).orNull, false) }
+
+ /*
+ * Math operations.
+ */
+ def doAbs(): Unit = get1Any() match {
+ case (a: JLong, f) ⇒ pushLong(Math.abs(a), f)
+ case (a: JDouble, f) ⇒ pushDouble(Math.abs(a), f)
+ case x ⇒ rtParamTypeError(fun, x, "numeric")
+ }
+ def doSquare(): Unit = get1Any() match {
+ case (a: JLong, f) ⇒ pushLong(a * a, f)
+ case (a: JDouble, f) ⇒ pushDouble(a * a, f)
+ case x ⇒ rtParamTypeError(fun, x, "numeric")
+ }
+ def doCeil(): Unit = get1Double() match { case (a: JDouble, f) ⇒
pushDouble(Math.ceil(a), f) }
+ def doFloor(): Unit = get1Double() match { case (a: JDouble, f) ⇒
pushDouble(Math.floor(a), f) }
+ def doSignum(): Unit = get1Double() match { case (a: JDouble, f) ⇒
pushDouble(Math.signum(a), f) }
+ def doAcos(): Unit = get1Double() match { case (a: JDouble, f) ⇒
pushDouble(Math.acos(a), f) }
+ def doAsin(): Unit = get1Double() match { case (a: JDouble, f) ⇒
pushDouble(Math.asin(a), f) }
+ def doSin(): Unit = get1Double() match { case (a: JDouble, f) ⇒
pushDouble(Math.sin(a), f) }
+ def doCos(): Unit = get1Double() match { case (a: JDouble, f) ⇒
pushDouble(Math.cos(a), f) }
+ def doRint(): Unit = get1Double() match { case (a: JDouble, f) ⇒
pushDouble(Math.rint(a), f) }
+ def doRound(): Unit = get1Double() match { case (a: JDouble, f) ⇒
pushLong(Math.round(a), f) }
+ def doSqrt(): Unit = get1Double() match { case (a: JDouble, f) ⇒
pushDouble(Math.sqrt(a), f) }
+ def doCbrt(): Unit = get1Double() match { case (a: JDouble, f) ⇒
pushDouble(Math.cbrt(a), f) }
+ def doAtan(): Unit = get1Double() match { case (a: JDouble, f) ⇒
pushDouble(Math.atan(a), f) }
+ def doTan(): Unit = get1Double() match { case (a: JDouble, f) ⇒
pushDouble(Math.tan(a), f) }
+ def doCosh(): Unit = get1Double() match { case (a: JDouble, f) ⇒
pushDouble(Math.cosh(a), f) }
+ def doSinh(): Unit = get1Double() match { case (a: JDouble, f) ⇒
pushDouble(Math.sinh(a), f) }
+ def doTanh(): Unit = get1Double() match { case (a: JDouble, f) ⇒
pushDouble(Math.tanh(a), f) }
+ def doLog(): Unit = get1Double() match { case (a: JDouble, f) ⇒
pushDouble(Math.log(a), f) }
+ def doLog1p(): Unit = get1Double() match { case (a: JDouble, f) ⇒
pushDouble(Math.log1p(a), f) }
+ def doLog10(): Unit = get1Double() match { case (a: JDouble, f) ⇒
pushDouble(Math.log10(a), f) }
+ def doDegrees(): Unit = get1Double() match { case (a: JDouble, f) ⇒
pushDouble(Math.toDegrees(a), f) }
+ def doRadians(): Unit = get1Double() match { case (a: JDouble, f) ⇒
pushDouble(Math.toRadians(a), f) }
+ def doExp(): Unit = get1Double() match { case (a: JDouble, f) ⇒
pushDouble(Math.exp(a), f) }
+ def doExpm1(): Unit = get1Double() match { case (a: JDouble, f) ⇒
pushDouble(Math.expm1(a), f) }
+ def doRandom(): Unit = pushDouble(Math.random, false)
+ def doPi(): Unit = pushDouble(Math.PI, false)
+ def doEuler(): Unit = pushDouble(Math.E, false)
+ def doPow(): Unit = get2Doubles() match { case (a1: JDouble, a2:
JDouble, f) ⇒ pushDouble(Math.pow(a1, a2), f) }
+ def doHypot(): Unit = get2Doubles() match { case (a1: JDouble, a2:
JDouble, f) ⇒ pushDouble(Math.hypot(a1, a2), f) }
+ def doAtan2(): Unit = get2Doubles() match { case (a1: JDouble, a2:
JDouble, f) ⇒ pushDouble(Math.atan2(a1, a2), f) }
+
+ /*
+ * User operations.
+ */
+ def doUserId(): Unit = pushLong(termCtx.req.getUser.getId, false)
+
+ /*
+ * Company operations.
+ */
+ def doCompId(): Unit = pushLong(termCtx.req.getCompany.getId, false)
+
+ /*
+ * Request operations.
+ */
+ def doReqId(): Unit = pushAny(termCtx.req.getServerRequestId, false)
+
+ /*
+ * Date-time operations.
+ */
+ def doYear(): Unit = pushLong(LocalDate.now.getYear,false)
+ def doMonth(): Unit = pushLong(LocalDate.now.getMonthValue,false)
+ def doDayOfMonth(): Unit = pushLong(LocalDate.now.getDayOfMonth,false)
+ def doDayOfWeek(): Unit =
pushLong(LocalDate.now.getDayOfWeek.getValue,false)
+ def doDayOfYear(): Unit = pushLong(LocalDate.now.getDayOfYear,false)
+
+ def doJson(): Unit = get1Str() match { case (s, f) ⇒
pushAny(U.jsonToJavaMap(asString(s)), f) }
+ def doIf(): Unit = {
+ ensureStack(3)
+
+ val (v1, v2, v3, f1, f2, f3) = pop3()
+
+ if (!isBoolean(v1))
+ throw rtParamTypeError(fun, v1, "boolean")
+
+ if (asBool(v1))
+ pushAny(v2, f1 || f2)
+ else
+ pushAny(v3, f1 || f3)
+ }
+
+ fun match {
+ // Metadata access.
+ case "meta_token" ⇒ doTokenMeta()
+ case "meta_model" ⇒ doModelMeta()
+ case "meta_intent" ⇒ doIntentMeta()
+ case "meta_req" ⇒ doReqMeta()
+ case "meta_user" ⇒ doUserMeta()
+ case "meta_company" ⇒ doCompMeta()
+ case "meta_sys" ⇒ doSysMeta()
+ case "meta_conv" ⇒ doConvMeta()
+
+ // Converts JSON to map.
+ case "json" ⇒ doJson()
+
+ // Inline if-statement.
+ case "if" ⇒ doIf()
+
+ // Token functions.
+ case "id" ⇒ pushAny(tok.getId, true)
+ case "ancestors" ⇒ pushAny(tok.getAncestors, true)
+ case "parent" ⇒ pushAny(tok.getParentId, true)
+ case "groups" ⇒ pushAny(tok.getGroups, true)
+ case "value" ⇒ pushAny(tok.getValue, true)
+ case "aliases" ⇒ pushAny(tok.getAliases, true)
+ case "start_idx" ⇒ pushLong(tok.getStartCharIndex, true)
+ case "end_idx" ⇒ pushLong(tok.getEndCharIndex, true)
+
+ // Request data.
+ case "req_id" ⇒ doReqId()
+ case "req_normtext" ⇒
+ case "req_tstamp" ⇒
+ case "req_addr" ⇒
+ case "req_agent" ⇒
+
+ // User data.
+ case "user_id" ⇒ doUserId()
+ case "user_fname" ⇒
+ case "user_lname" ⇒
+ case "user_email" ⇒
+ case "user_admin" ⇒
+ case "user_signup_tstamp" ⇒
+
+ // Company data.
+ case "comp_id" ⇒ doCompId()
+ case "comp_name" ⇒
+ case "comp_website" ⇒
+ case "comp_country" ⇒
+ case "comp_region" ⇒
+ case "comp_city" ⇒
+ case "comp_addr" ⇒
+ case "comp_postcode" ⇒
+
+ // String functions.
+ case "trim" ⇒ doTrim()
+ case "strip" ⇒ doTrim()
+ case "uppercase" ⇒ doUppercase()
+ case "lowercase" ⇒ doLowercase()
+ case "is_alpha" ⇒ doIsAlpha()
+ case "is_alphanum" ⇒ doIsAlphaNum()
+ case "is_whitespace" ⇒ doIsWhitespace()
+ case "is_num" ⇒ doIsNum()
+ case "is_numspace" ⇒ doIsNumSpace()
+ case "is_alphaspace" ⇒ doIsAlphaSpace()
+ case "is_alphanumspace" ⇒ doIsAlphaNumSpace()
+ case "substring" ⇒
+ case "index" ⇒
+ case "regex" ⇒
+ case "soundex" ⇒
+ case "split" ⇒ doSplit()
+ case "split_trim" ⇒ doSplitTrim()
+ case "replace" ⇒
+
+ // Math functions.
+ case "abs" ⇒ doAbs()
+ case "ceil" ⇒ doCeil()
+ case "floor" ⇒ doFloor()
+ case "rint" ⇒ doRint()
+ case "round" ⇒ doRound()
+ case "signum" ⇒ doSignum()
+ case "sqrt" ⇒ doSqrt()
+ case "cbrt" ⇒ doCbrt()
+ case "pi" ⇒ doPi()
+ case "euler" ⇒ doEuler()
+ case "acos" ⇒ doAcos()
+ case "asin" ⇒ doAsin()
+ case "atan" ⇒ doAtan()
+ case "cos" ⇒ doCos()
+ case "sin" ⇒ doSin()
+ case "tan" ⇒ doTan()
+ case "cosh" ⇒ doCosh()
+ case "sinh" ⇒ doSinh()
+ case "tanh" ⇒ doTanh()
+ case "atn2" ⇒ doAtan2()
+ case "degrees" ⇒ doDegrees()
+ case "radians" ⇒ doRadians()
+ case "exp" ⇒ doExp()
+ case "expm1" ⇒ doExpm1()
+ case "hypot" ⇒ doHypot()
+ case "log" ⇒ doLog()
+ case "log10" ⇒ doLog10()
+ case "log1p" ⇒ doLog1p()
+ case "pow" ⇒ doPow()
+ case "rand" ⇒ doRandom()
+ case "square" ⇒ doSquare()
+
+ // Collection functions.
+ case "list" ⇒ doList()
+ case "map" ⇒ doMap()
+ case "get" ⇒
+ case "index" ⇒
+ case "has" ⇒
+ case "tail" ⇒
+ case "add" ⇒
+ case "remove" ⇒
+ case "first" ⇒
+ case "last" ⇒
+ case "keys" ⇒
+ case "values" ⇒
+ case "length" ⇒
+ case "count" ⇒
+ case "take" ⇒
+ case "drop" ⇒
+ case "size" ⇒
+ case "length" ⇒
+ case "reverse" ⇒
+ case "is_empty" ⇒
+ case "non_empty" ⇒
+ case "to_string" ⇒
+
+ // Statistical operations.
+ case "avg" ⇒
+ case "max" ⇒ // Works for numerics as well.
+ case "min" ⇒ // Works for numerics as well.
+ case "stdev" ⇒
+ case "sum" ⇒
+
+ // Date-time functions.
+ case "year" ⇒ doYear() // 2021.
+ case "month" ⇒ doMonth() // 1 ... 12.
+ case "day_of_month" ⇒ doDayOfMonth() // 1 ... 31.
+ case "day_of_week" ⇒ doDayOfWeek()
+ case "day_of_year" ⇒ doDayOfYear()
+ case "hour" ⇒
+ case "min" ⇒
+ case "sec" ⇒
+ case "week_of_month" ⇒
+ case "week_of_year" ⇒
+ case "quarter" ⇒
+ case "msec" ⇒
+ case "now" ⇒ // Epoc time.
+
+ case _ ⇒ throw rtUnknownFunError(fun) // Assertion.
}
}
}
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 0a687c9..4bbbb8c 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
@@ -25,7 +25,6 @@ import
org.apache.nlpcraft.model.intent.impl.antlr4.{NCIntentDslParser ⇒ Parse
import org.apache.nlpcraft.model.intent.utils.ver2._
import org.apache.nlpcraft.model.{NCMetadata, NCRequest, NCToken,
NCTokenPredicateContext, NCTokenPredicateResult}
-import java.lang.{IllegalArgumentException ⇒ IAE}
import java.util.Optional
import java.util.regex.{Pattern, PatternSyntaxException}
import scala.collection.mutable
@@ -36,12 +35,12 @@ object NCIntentDslCompiler extends LazyLogging {
// Compiler cache.
private val cache = new mutable.HashMap[String, NCDslIntent]
- private var mdlId: String = _
-
/**
*
+ * @param dsl
+ * @param mdlId
*/
- class FiniteStateMachine(dsl: String) extends NCIntentDslBaseListener with
NCBaseDslCompiler {
+ class FiniteStateMachine(dsl: String, mdlId: String) extends
NCIntentDslBaseListener with NCBaseDslCompiler {
// Intent components.
private var id: String = _
private var ordered: Boolean = false
@@ -107,6 +106,8 @@ object NCIntentDslCompiler extends LazyLogging {
override def exitOrderedDecl(ctx: Parser.OrderedDeclContext): Unit =
ordered = ctx.BOOL().getText == "true"
override def exitFlowDecl(ctx: Parser.FlowDeclContext): Unit = {
+ implicit val evidence: ParserRuleContext = ctx
+
val regex = ctx.qstring().getText
if (regex != null && regex.length > 2)
@@ -117,15 +118,17 @@ object NCIntentDslCompiler extends LazyLogging {
Pattern.compile(flowRegex.get)
catch {
case e: PatternSyntaxException ⇒
- throw new IAE(s"${e.getDescription} in DSL intent flow
regex '${e.getPattern}' near index ${e.getIndex}.")
+ newSyntaxError(s"${e.getDescription} in DSL intent
flow regex '${e.getPattern}' near index ${e.getIndex}.")
}
}
override def exitTerm(ctx: Parser.TermContext): Unit = {
+ implicit val evidence: ParserRuleContext = ctx
+
if (min < 0 || min > max)
- throw new IAE(s"Invalid DSL intent term min quantifiers: $min
(must be min >= 0 && min <= max).")
+ throw newSyntaxError(s"Invalid DSL intent term min
quantifiers: $min (must be min >= 0 && min <= max).")
if (max < 1)
- throw new IAE(s"Invalid DSL intent term max quantifiers: $max
(must be max >= 1).")
+ throw newSyntaxError(s"Invalid DSL intent term max
quantifiers: $max (must be max >= 1).")
val pred =
if (termMtdName != null) { // User-code defined term.
@@ -152,7 +155,8 @@ object NCIntentDslCompiler extends LazyLogging {
(res.getResult, res.wasTokenUsed())
}
catch {
- case e: Exception ⇒ throw new IAE(s"Failed to
invoke custom DSL intent term: $mdlCls.$termMtdName", e)
+ case e: Exception ⇒
+ throw runtimeError(s"Failed to invoke custom
DSL intent term: $mdlCls.$termMtdName", e)
}
}
}
@@ -169,7 +173,7 @@ object NCIntentDslCompiler extends LazyLogging {
val x = stack.pop()
if (!isBoolean(x.retVal))
- throw new IAE(s"DSL intent term does not return
boolean value: ${ctx.getText}")
+ throw runtimeError(s"DSL intent term does not
return boolean value: ${ctx.getText}")
(asBool(x.retVal), x.usedTok)
}
@@ -202,24 +206,51 @@ 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)
}
/**
- * Custom error handler.
+ *
+ * @param msg
+ * @param line
+ * @param charPos
+ * @param dsl
+ * @param mdlId
+ * @return
*/
- class CompilerErrorListener(dsl: String) extends BaseErrorListener {
- /**
- *
- * @param len
- * @param pos
- * @return
- */
- private def makeCharPosPointer(len: Int, pos: Int): String = {
- val s = (for (_ ← 1 to len) yield '-').mkString("")
+ private def mkSyntaxError(msg: String, line: Int, charPos: Int, dsl:
String, mdlId: String): String = {
+ val dash = "-" * dsl.length
+ val pos = Math.max(0, charPos - 1)
+ val posPtr = dash.substring(0, pos) + r("^") + dash.substring(pos + 1)
+ val dslPtr = dsl.substring(0, pos) + r(dsl.charAt(pos)) +
dsl.substring(pos + 1)
+
+ s"Intent DSL syntax error at line $line:$charPos - $msg\n" +
+ s" |-- ${c("Model:")} $mdlId\n" +
+ s" |-- ${c("Intent:")} $dslPtr\n" +
+ s" +-- ${c("Location:")} $posPtr"
+ }
- s.substring(0, pos - 1) + '^' + s.substring(pos)
- }
+ /**
+ *
+ * @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.
+ *
+ * @param dsl
+ * @param mdlId
+ */
+ class CompilerErrorListener(dsl: String, mdlId: String) extends
BaseErrorListener {
/**
*
* @param recognizer
@@ -235,15 +266,7 @@ object NCIntentDslCompiler extends LazyLogging {
line: Int,
charPos: Int,
msg: String,
- e: RecognitionException): Unit = {
-
- val errMsg = s"Intent DSL syntax error at line $line:$charPos -
$msg\n" +
- s" |-- ${c("Model:")} $mdlId\n" +
- s" |-- ${c("Intent:")} $dsl\n" +
- s" +-- ${c("Error:")} ${makeCharPosPointer(dsl.length,
charPos)}"
-
- throw new NCE(errMsg)
- }
+ e: RecognitionException): Unit = throw new NCE(mkSyntaxError(msg,
line, charPos, dsl, mdlId))
}
/**
@@ -258,9 +281,7 @@ object NCIntentDslCompiler extends LazyLogging {
): NCDslIntent = {
require(dsl != null)
- val src = dsl.strip()
-
- this.mdlId = mdlId
+ val src = dsl.strip().replace("\r\n", " ").replace("\n", " ")
val intent: NCDslIntent = cache.getOrElseUpdate(src, {
// ANTLR4 armature.
@@ -271,11 +292,11 @@ object NCIntentDslCompiler extends LazyLogging {
// Set custom error handlers.
lexer.removeErrorListeners()
parser.removeErrorListeners()
- lexer.addErrorListener(new CompilerErrorListener(src))
- parser.addErrorListener(new CompilerErrorListener(src))
+ lexer.addErrorListener(new CompilerErrorListener(src, mdlId))
+ parser.addErrorListener(new CompilerErrorListener(src, mdlId))
// State automata.
- val fsm = new FiniteStateMachine(src)
+ val fsm = new FiniteStateMachine(src, mdlId)
// Parse the input DSL and walk built AST.
(new ParseTreeWalker).walk(fsm, parser.intent())
diff --git
a/nlpcraft/src/test/scala/org/apache/nlpcraft/model/intent/dsl/NCDslCompilerSpec.scala
b/nlpcraft/src/test/scala/org/apache/nlpcraft/model/intent/dsl/NCDslCompilerSpec.scala
index f2b0f23..9e2ddf2 100644
---
a/nlpcraft/src/test/scala/org/apache/nlpcraft/model/intent/dsl/NCDslCompilerSpec.scala
+++
b/nlpcraft/src/test/scala/org/apache/nlpcraft/model/intent/dsl/NCDslCompilerSpec.scala
@@ -25,22 +25,74 @@ import org.junit.jupiter.api.Test
* Tests for DSL compiler.
*/
class NCDslCompilerSpec {
+ /**
+ *
+ * @param dsl
+ */
+ private def checkOk(dsl: String): Unit = {
+ try {
+ NCIntentDslCompiler.compile(dsl, "mdl.id")
+
+ assert(true)
+ }
+ catch {
+ case _: Exception ⇒ assert(false)
+ }
+ }
+
+ /**
+ *
+ * @param txt
+ */
+ private def checkError(txt: String): Unit = {
+ try {
+ NCIntentDslCompiler.compile(txt, "mdl.id")
+
+ assert(false)
+ } catch {
+ case e: NCE ⇒
+ println(e.getMessage)
+ assert(true)
+ }
+ }
+
@Test
@throws[NCException]
- def test(): Unit = {
- val intent = NCIntentDslCompiler.compile(
+ def testOk(): Unit = {
+ checkOk(
"""
- |intent=i1 flow="a[^0-9]b" meta={'a': true, 'b': {'arr': [1, 2,
3]}} term(t1)={2 == 2 && size(id()) != -25}
- |""".stripMargin, "mdl.id"
+ |intent=i1
+ |flow="a[^0-9]b"
+ |meta={'a': true, 'b': {'arr': [1, 2, 3]}}
+ |term(t1)={2 == 2 && size(id()) != -25}
+ |""".stripMargin
)
- val intent2 = NCIntentDslCompiler.compile(
+ checkOk(
"""
- |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"))}
- |""".stripMargin, "mdl.id"
+ |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"))}
+ |""".stripMargin
)
+ }
- () // Breakpoint.
-
- // val ret = intent2.terms.head.pred(_, _, _)
+ @Test
+ @throws[NCException]
+ def testFail(): Unit = {
+ checkError(
+ """
+ |intent=i1
+ |flow="a[^0-9]b"
+ |meta={{'a': true, 'b': {'arr': [1, 2, 3]}}
+ |term(t1)={2 == 2 && size(id()) != -25}
+ |""".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
+ )
}
}