private def concat()

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
            }