in nlpcraft/src/main/scala/org/apache/nlpcraft/internal/makro/NCMacroCompiler.scala [65:146]
private def concat(optS: String, s: String): String = if optS.isEmpty then s else s"$optS $s"
/**
*
* @param errMsg
* @param ctx
*/
private def compilerError(errMsg: String)(implicit ctx: ParserRuleContext): NCException =
val tok = ctx.stop
new NCException(mkCompilerError(errMsg, tok.getLine, tok.getCharPositionInLine, in))
/**
*
* @param buf
* @param ctx
*/
private def checkMaxSyn(buf: mutable.Buffer[String])(implicit ctx: ParserRuleContext): Unit =
if buf.sizeIs > MAX_SYN then
throw compilerError(s"Exceeded max number ($MAX_SYN) of macro expansions: ${buf.size}")
override def enterExpr(ctx: NCMacroDslParser.ExprContext): Unit =
val buf = mutable.Buffer.empty[String]
// NOTE: do not allow expression's buffer to be empty.
// Add harmless empty string.
buf += ""
stack.push(StackItem(buf, isGroup = false))
override def enterGroup(ctx: NCMacroDslParser.GroupContext): Unit =
// NOTE: group cannot be empty based on the BNF grammar.
stack.push(StackItem(mutable.Buffer.empty[String], isGroup = true))
override def exitExpr(ctx: NCMacroDslParser.ExprContext): Unit =
given ParserRuleContext = ctx
if stack.size > 1 then
val expr = stack.pop()
val prn = stack.top
checkMaxSyn(expr.buffer)
require(expr.buffer.nonEmpty)
if prn.isGroup then
prn.buffer ++= expr.buffer
else
prn.buffer = for (z <- expr.buffer; i <- prn.buffer.indices) yield concat(prn.buffer(i), z)
override def exitMinMax(ctx: NCMacroDslParser.MinMaxContext): Unit =
given ParserRuleContext = ctx
if ctx.minMaxShortcut() != null then
ctx.minMaxShortcut().getText match
case "?" =>
min = 0
max = 1
case c => throw compilerError(s"Invalid min/max shortcut '$c' in: ${ctx.getText}")
else if ctx.MINMAX() != null then
var s = ctx.MINMAX().getText
val orig = s
s = s.substring(1, s.length - 1)
val comma = s.indexOf(',')
if comma == -1 || comma == 0 || comma == s.length - 1 then
throw compilerError(s"Invalid min/max quantifier: $orig")
try min = java.lang.Integer.parseInt(s.substring(0, comma).trim)
catch case _: NumberFormatException => throw compilerError(s"Invalid min quantifier: $orig")
try max = java.lang.Integer.parseInt(s.substring(comma + 1).trim)
catch case _: NumberFormatException => throw compilerError(s"Invalid max quantifier: $orig")
if min < 0 || max < 0 || min > max || max == 0 || max > MAX_QTY then
throw compilerError(s"[$min,$max] quantifiers should be 'max >= min, min >= 0, max > 0, max <= $MAX_QTY'.")
override def exitGroup(ctx: NCMacroDslParser.GroupContext): Unit =
given ParserRuleContext = ctx
val grp = stack.pop()
// Remove dups.
grp.buffer = grp.buffer.distinct
checkMaxSyn(grp.buffer)
require(grp.isGroup)
val prn = stack.top
prn.buffer = prn.buffer.flatMap {
s => (for (z <- grp.buffer; i <- min to max) yield concat(s, s"$z " * i).trim).toSet
}