This is an automated email from the ASF dual-hosted git repository.

aradzinski pushed a commit to branch NLPCRAFT-427
in repository https://gitbox.apache.org/repos/asf/incubator-nlpcraft.git


The following commit(s) were added to refs/heads/NLPCRAFT-427 by this push:
     new d259d2d  WIP on NLPCRAFT-427
d259d2d is described below

commit d259d2d29d4ed7be45b37542acb58e753fa6e5b3
Author: Aaron Radzinski <[email protected]>
AuthorDate: Tue Aug 24 20:19:21 2021 -0700

    WIP on NLPCRAFT-427
---
 .../nlpcraft/common/makro/NCMacroCompiler.scala    |  43 +++--
 .../nlpcraft/common/makro/antlr4/NCMacroDsl.g4     |  16 +-
 .../nlpcraft/common/makro/antlr4/NCMacroDsl.interp |  12 +-
 .../nlpcraft/common/makro/antlr4/NCMacroDsl.tokens |  18 +-
 .../makro/antlr4/NCMacroDslBaseListener.java       |  24 +++
 .../common/makro/antlr4/NCMacroDslLexer.interp     |  15 +-
 .../common/makro/antlr4/NCMacroDslLexer.java       |  71 +++----
 .../common/makro/antlr4/NCMacroDslLexer.tokens     |  18 +-
 .../common/makro/antlr4/NCMacroDslListener.java    |  20 ++
 .../common/makro/antlr4/NCMacroDslParser.java      | 204 +++++++++++++++------
 .../nlpcraft/common/makro/NCMacroParserSpec.scala  |   2 +
 11 files changed, 322 insertions(+), 121 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 cab25fc..b3ef92e 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
@@ -31,6 +31,7 @@ import scala.collection.mutable
   */
 object NCMacroCompiler extends LazyLogging {
     private final val MAX_SYN = 10000
+    private final val MAX_QTY = 100
     
     /**
       *
@@ -49,8 +50,9 @@ object NCMacroCompiler extends LazyLogging {
       */
     class FiniteStateMachine(parser: P, in: String) extends 
NCMacroDslBaseListener {
         private val stack = new mutable.Stack[StackItem]
-
         private var expandedSyns: Set[String] = _
+        private var min = 1
+        private var max = 1
 
         /**
          *
@@ -114,13 +116,15 @@ object NCMacroCompiler extends LazyLogging {
             }
         }
 
-        override def exitGroup(ctx: NCMacroDslParser.GroupContext): Unit = {
+        override def exitMinMax(ctx: NCMacroDslParser.MinMaxContext): Unit = {
             implicit val evidence: ParserRuleContext = ctx
 
-            var min = 1
-            var max = 1
-
-            if (ctx.MINMAX() != null) {
+            if (ctx.minMaxShortcut() != null)
+                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) {
                 var s = ctx.MINMAX().getText
                 val orig = s
 
@@ -141,11 +145,15 @@ object NCMacroCompiler extends LazyLogging {
                 catch {
                     case _: NumberFormatException => throw 
compilerError(s"Invalid max quantifier: $orig")
                 }
-
-                if (min < 0 || max < 0 || min > max || max == 0)
-                    throw compilerError(s"[$min,$max] quantifiers should 
satisfy 'max >= min, min >= 0, max > 0'.")
             }
 
+            if (min < 0 || max < 0 || min > max || max == 0 || max > MAX_QTY)
+                throw compilerError(s"[$min,$max] quantifiers should be 'max 
>= min, min >= 0, max > 0, max <= $MAX_QTY'.")
+        }
+
+        override def exitGroup(ctx: NCMacroDslParser.GroupContext): Unit = {
+            implicit val evidence: ParserRuleContext = ctx
+
             val grp = stack.pop()
             
             // Remove dups.
@@ -160,6 +168,10 @@ object NCMacroCompiler extends LazyLogging {
             prn.buffer = prn.buffer.flatMap {
                 s => (for (z <- grp.buffer; i <- min to max) yield concat(s, 
s"$z " * i).trim).toSet
             }
+
+            // Reset.
+            min = 1
+            max = 1
         }
 
         override def exitSyn(ctx: P.SynContext): Unit = {
@@ -228,11 +240,16 @@ object NCMacroCompiler extends LazyLogging {
         charPos: Int, // 0, 1, 2, ...
         in: String
     ): String = {
-        val hold = NCCompilerUtils.mkErrorHolder(in, charPos)
+        val hldr = NCCompilerUtils.mkErrorHolder(in, charPos)
+
+        val aMsg = U.decapitalize(msg) match {
+            case s: String if s.last == '.' => s
+            case s: String => s + '.'
+        }
 
-        s"Macro compiler error at line $line - $msg\n" +
-        s"  |-- ${c("Macro:")} ${hold.origStr}\n" +
-        s"  +-- ${c("Error:")} ${hold.ptrStr}"
+        s"Macro compiler error at line $line - $aMsg\n" +
+        s"  |-- ${c("Macro:")} ${hldr.origStr}\n" +
+        s"  +-- ${c("Error:")} ${hldr.ptrStr}"
     }
     
     /**
diff --git 
a/nlpcraft/src/main/scala/org/apache/nlpcraft/common/makro/antlr4/NCMacroDsl.g4 
b/nlpcraft/src/main/scala/org/apache/nlpcraft/common/makro/antlr4/NCMacroDsl.g4
index 962f23a..c6ee2a1 100644
--- 
a/nlpcraft/src/main/scala/org/apache/nlpcraft/common/makro/antlr4/NCMacroDsl.g4
+++ 
b/nlpcraft/src/main/scala/org/apache/nlpcraft/common/makro/antlr4/NCMacroDsl.g4
@@ -25,13 +25,20 @@ expr
     ;
 item: syn | group;
 syn : (TXT | REGEX_TXT | IDL_TXT);
-group: LCURLY list RCURLY MINMAX?;
+group: LCURLY list RCURLY minMax?;
 list
     : expr
     | list VERT expr
     | list VERT UNDERSCORE
     | UNDERSCORE VERT list
     ;
+minMax
+    : minMaxShortcut
+    | MINMAX
+    ;
+minMaxShortcut
+    : QUESTION
+    ;
 
 // Lexer.
 LCURLY: '{';
@@ -39,7 +46,10 @@ RCURLY: '}';
 VERT: '|';
 COMMA: ',';
 UNDERSCORE: '_';
-fragment ESC_CHAR: [{}\\_[\]|,];
+LBR: '[';
+RBR: ']';
+QUESTION: '?';
+fragment ESC_CHAR: [{}\\_[\]|,/];
 fragment ESC: '\\' ESC_CHAR;
 fragment TXT_CHAR
     : [~!@#$%^&*?()+._]
@@ -64,9 +74,9 @@ fragment TXT_CHAR
     | '\uF900'..'\uFDCF'
     | '\uFDF0'..'\uFFFD'
     ; // Ignoring ['\u10000-'\uEFFFF].
-MINMAX: '[' [ 0-9,]+ ']';
 REGEX_TXT: '//' .*? '//';
 IDL_TXT: '^^' .*? '^^';
 TXT: (TXT_CHAR | ESC)+;
+MINMAX: '[' [ 0-9,]+ ']';
 WS: [ \r\t\u000C\n]+ -> skip ;
 ERR_CHAR: .;
\ No newline at end of file
diff --git 
a/nlpcraft/src/main/scala/org/apache/nlpcraft/common/makro/antlr4/NCMacroDsl.interp
 
b/nlpcraft/src/main/scala/org/apache/nlpcraft/common/makro/antlr4/NCMacroDsl.interp
index 6f2c738..b85d8dd 100644
--- 
a/nlpcraft/src/main/scala/org/apache/nlpcraft/common/makro/antlr4/NCMacroDsl.interp
+++ 
b/nlpcraft/src/main/scala/org/apache/nlpcraft/common/makro/antlr4/NCMacroDsl.interp
@@ -5,6 +5,9 @@ null
 '|'
 ','
 '_'
+'['
+']'
+'?'
 null
 null
 null
@@ -19,10 +22,13 @@ RCURLY
 VERT
 COMMA
 UNDERSCORE
-MINMAX
+LBR
+RBR
+QUESTION
 REGEX_TXT
 IDL_TXT
 TXT
+MINMAX
 WS
 ERR_CHAR
 
@@ -33,7 +39,9 @@ item
 syn
 group
 list
+minMax
+minMaxShortcut
 
 
 atn:
-[3, 24715, 42794, 33075, 47597, 16764, 15335, 30598, 22884, 3, 13, 58, 4, 2, 
9, 2, 4, 3, 9, 3, 4, 4, 9, 4, 4, 5, 9, 5, 4, 6, 9, 6, 4, 7, 9, 7, 3, 2, 3, 2, 
3, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 7, 3, 23, 10, 3, 12, 3, 14, 3, 26, 11, 3, 
3, 4, 3, 4, 5, 4, 30, 10, 4, 3, 5, 3, 5, 3, 6, 3, 6, 3, 6, 3, 6, 5, 6, 38, 10, 
6, 3, 7, 3, 7, 3, 7, 3, 7, 3, 7, 5, 7, 45, 10, 7, 3, 7, 3, 7, 3, 7, 3, 7, 3, 7, 
3, 7, 7, 7, 53, 10, 7, 12, 7, 14, 7, 56, 11, 7, 3, 7, 2, 4, 4, 12, 8, 2, 4, 6, 
8, 10, 12, 2, 3, 3, 2 [...]
\ No newline at end of file
+[3, 24715, 42794, 33075, 47597, 16764, 15335, 30598, 22884, 3, 16, 68, 4, 2, 
9, 2, 4, 3, 9, 3, 4, 4, 9, 4, 4, 5, 9, 5, 4, 6, 9, 6, 4, 7, 9, 7, 4, 8, 9, 8, 
4, 9, 9, 9, 3, 2, 3, 2, 3, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 7, 3, 27, 10, 3, 
12, 3, 14, 3, 30, 11, 3, 3, 4, 3, 4, 5, 4, 34, 10, 4, 3, 5, 3, 5, 3, 6, 3, 6, 
3, 6, 3, 6, 5, 6, 42, 10, 6, 3, 7, 3, 7, 3, 7, 3, 7, 3, 7, 5, 7, 49, 10, 7, 3, 
7, 3, 7, 3, 7, 3, 7, 3, 7, 3, 7, 7, 7, 57, 10, 7, 12, 7, 14, 7, 60, 11, 7, 3, 
8, 3, 8, 5, 8, 64, 10, 8, [...]
\ No newline at end of file
diff --git 
a/nlpcraft/src/main/scala/org/apache/nlpcraft/common/makro/antlr4/NCMacroDsl.tokens
 
b/nlpcraft/src/main/scala/org/apache/nlpcraft/common/makro/antlr4/NCMacroDsl.tokens
index 7ef9fd0..319aa8e 100644
--- 
a/nlpcraft/src/main/scala/org/apache/nlpcraft/common/makro/antlr4/NCMacroDsl.tokens
+++ 
b/nlpcraft/src/main/scala/org/apache/nlpcraft/common/makro/antlr4/NCMacroDsl.tokens
@@ -3,14 +3,20 @@ RCURLY=2
 VERT=3
 COMMA=4
 UNDERSCORE=5
-MINMAX=6
-REGEX_TXT=7
-IDL_TXT=8
-TXT=9
-WS=10
-ERR_CHAR=11
+LBR=6
+RBR=7
+QUESTION=8
+REGEX_TXT=9
+IDL_TXT=10
+TXT=11
+MINMAX=12
+WS=13
+ERR_CHAR=14
 '{'=1
 '}'=2
 '|'=3
 ','=4
 '_'=5
+'['=6
+']'=7
+'?'=8
diff --git 
a/nlpcraft/src/main/scala/org/apache/nlpcraft/common/makro/antlr4/NCMacroDslBaseListener.java
 
b/nlpcraft/src/main/scala/org/apache/nlpcraft/common/makro/antlr4/NCMacroDslBaseListener.java
index 35c078e..7bd6836 100644
--- 
a/nlpcraft/src/main/scala/org/apache/nlpcraft/common/makro/antlr4/NCMacroDslBaseListener.java
+++ 
b/nlpcraft/src/main/scala/org/apache/nlpcraft/common/makro/antlr4/NCMacroDslBaseListener.java
@@ -83,6 +83,30 @@ public class NCMacroDslBaseListener implements 
NCMacroDslListener {
         * <p>The default implementation does nothing.</p>
         */
        @Override public void exitList(NCMacroDslParser.ListContext ctx) { }
+       /**
+        * {@inheritDoc}
+        *
+        * <p>The default implementation does nothing.</p>
+        */
+       @Override public void enterMinMax(NCMacroDslParser.MinMaxContext ctx) { 
}
+       /**
+        * {@inheritDoc}
+        *
+        * <p>The default implementation does nothing.</p>
+        */
+       @Override public void exitMinMax(NCMacroDslParser.MinMaxContext ctx) { }
+       /**
+        * {@inheritDoc}
+        *
+        * <p>The default implementation does nothing.</p>
+        */
+       @Override public void 
enterMinMaxShortcut(NCMacroDslParser.MinMaxShortcutContext ctx) { }
+       /**
+        * {@inheritDoc}
+        *
+        * <p>The default implementation does nothing.</p>
+        */
+       @Override public void 
exitMinMaxShortcut(NCMacroDslParser.MinMaxShortcutContext ctx) { }
 
        /**
         * {@inheritDoc}
diff --git 
a/nlpcraft/src/main/scala/org/apache/nlpcraft/common/makro/antlr4/NCMacroDslLexer.interp
 
b/nlpcraft/src/main/scala/org/apache/nlpcraft/common/makro/antlr4/NCMacroDslLexer.interp
index 97a62a2..e7fb0fe 100644
--- 
a/nlpcraft/src/main/scala/org/apache/nlpcraft/common/makro/antlr4/NCMacroDslLexer.interp
+++ 
b/nlpcraft/src/main/scala/org/apache/nlpcraft/common/makro/antlr4/NCMacroDslLexer.interp
@@ -5,6 +5,9 @@ null
 '|'
 ','
 '_'
+'['
+']'
+'?'
 null
 null
 null
@@ -19,10 +22,13 @@ RCURLY
 VERT
 COMMA
 UNDERSCORE
-MINMAX
+LBR
+RBR
+QUESTION
 REGEX_TXT
 IDL_TXT
 TXT
+MINMAX
 WS
 ERR_CHAR
 
@@ -32,13 +38,16 @@ RCURLY
 VERT
 COMMA
 UNDERSCORE
+LBR
+RBR
+QUESTION
 ESC_CHAR
 ESC
 TXT_CHAR
-MINMAX
 REGEX_TXT
 IDL_TXT
 TXT
+MINMAX
 WS
 ERR_CHAR
 
@@ -50,4 +59,4 @@ mode names:
 DEFAULT_MODE
 
 atn:
-[3, 24715, 42794, 33075, 47597, 16764, 15335, 30598, 22884, 2, 13, 96, 8, 1, 
4, 2, 9, 2, 4, 3, 9, 3, 4, 4, 9, 4, 4, 5, 9, 5, 4, 6, 9, 6, 4, 7, 9, 7, 4, 8, 
9, 8, 4, 9, 9, 9, 4, 10, 9, 10, 4, 11, 9, 11, 4, 12, 9, 12, 4, 13, 9, 13, 4, 
14, 9, 14, 4, 15, 9, 15, 3, 2, 3, 2, 3, 3, 3, 3, 3, 4, 3, 4, 3, 5, 3, 5, 3, 6, 
3, 6, 3, 7, 3, 7, 3, 8, 3, 8, 3, 8, 3, 9, 5, 9, 48, 10, 9, 3, 10, 3, 10, 6, 10, 
52, 10, 10, 13, 10, 14, 10, 53, 3, 10, 3, 10, 3, 11, 3, 11, 3, 11, 3, 11, 7, 
11, 62, 10, 11, 12, 11,  [...]
\ No newline at end of file
+[3, 24715, 42794, 33075, 47597, 16764, 15335, 30598, 22884, 2, 16, 108, 8, 1, 
4, 2, 9, 2, 4, 3, 9, 3, 4, 4, 9, 4, 4, 5, 9, 5, 4, 6, 9, 6, 4, 7, 9, 7, 4, 8, 
9, 8, 4, 9, 9, 9, 4, 10, 9, 10, 4, 11, 9, 11, 4, 12, 9, 12, 4, 13, 9, 13, 4, 
14, 9, 14, 4, 15, 9, 15, 4, 16, 9, 16, 4, 17, 9, 17, 4, 18, 9, 18, 3, 2, 3, 2, 
3, 3, 3, 3, 3, 4, 3, 4, 3, 5, 3, 5, 3, 6, 3, 6, 3, 7, 3, 7, 3, 8, 3, 8, 3, 9, 
3, 9, 3, 10, 3, 10, 3, 11, 3, 11, 3, 11, 3, 12, 5, 12, 60, 10, 12, 3, 13, 3, 
13, 3, 13, 3, 13, 7, 13,  [...]
\ No newline at end of file
diff --git 
a/nlpcraft/src/main/scala/org/apache/nlpcraft/common/makro/antlr4/NCMacroDslLexer.java
 
b/nlpcraft/src/main/scala/org/apache/nlpcraft/common/makro/antlr4/NCMacroDslLexer.java
index a28fe8a..31e2d67 100644
--- 
a/nlpcraft/src/main/scala/org/apache/nlpcraft/common/makro/antlr4/NCMacroDslLexer.java
+++ 
b/nlpcraft/src/main/scala/org/apache/nlpcraft/common/makro/antlr4/NCMacroDslLexer.java
@@ -17,8 +17,8 @@ public class NCMacroDslLexer extends Lexer {
        protected static final PredictionContextCache _sharedContextCache =
                new PredictionContextCache();
        public static final int
-               LCURLY=1, RCURLY=2, VERT=3, COMMA=4, UNDERSCORE=5, MINMAX=6, 
REGEX_TXT=7, 
-               IDL_TXT=8, TXT=9, WS=10, ERR_CHAR=11;
+               LCURLY=1, RCURLY=2, VERT=3, COMMA=4, UNDERSCORE=5, LBR=6, 
RBR=7, QUESTION=8, 
+               REGEX_TXT=9, IDL_TXT=10, TXT=11, MINMAX=12, WS=13, ERR_CHAR=14;
        public static String[] channelNames = {
                "DEFAULT_TOKEN_CHANNEL", "HIDDEN"
        };
@@ -29,22 +29,23 @@ public class NCMacroDslLexer extends Lexer {
 
        private static String[] makeRuleNames() {
                return new String[] {
-                       "LCURLY", "RCURLY", "VERT", "COMMA", "UNDERSCORE", 
"ESC_CHAR", "ESC", 
-                       "TXT_CHAR", "MINMAX", "REGEX_TXT", "IDL_TXT", "TXT", 
"WS", "ERR_CHAR"
+                       "LCURLY", "RCURLY", "VERT", "COMMA", "UNDERSCORE", 
"LBR", "RBR", "QUESTION", 
+                       "ESC_CHAR", "ESC", "TXT_CHAR", "REGEX_TXT", "IDL_TXT", 
"TXT", "MINMAX", 
+                       "WS", "ERR_CHAR"
                };
        }
        public static final String[] ruleNames = makeRuleNames();
 
        private static String[] makeLiteralNames() {
                return new String[] {
-                       null, "'{'", "'}'", "'|'", "','", "'_'"
+                       null, "'{'", "'}'", "'|'", "','", "'_'", "'['", "']'", 
"'?'"
                };
        }
        private static final String[] _LITERAL_NAMES = makeLiteralNames();
        private static String[] makeSymbolicNames() {
                return new String[] {
-                       null, "LCURLY", "RCURLY", "VERT", "COMMA", 
"UNDERSCORE", "MINMAX", "REGEX_TXT", 
-                       "IDL_TXT", "TXT", "WS", "ERR_CHAR"
+                       null, "LCURLY", "RCURLY", "VERT", "COMMA", 
"UNDERSCORE", "LBR", "RBR", 
+                       "QUESTION", "REGEX_TXT", "IDL_TXT", "TXT", "MINMAX", 
"WS", "ERR_CHAR"
                };
        }
        private static final String[] _SYMBOLIC_NAMES = makeSymbolicNames();
@@ -106,34 +107,36 @@ public class NCMacroDslLexer extends Lexer {
        public ATN getATN() { return _ATN; }
 
        public static final String _serializedATN =
-               
"\3\u608b\ua72a\u8133\ub9ed\u417c\u3be7\u7786\u5964\2\r`\b\1\4\2\t\2\4"+
+               
"\3\u608b\ua72a\u8133\ub9ed\u417c\u3be7\u7786\u5964\2\20l\b\1\4\2\t\2\4"+
                
"\3\t\3\4\4\t\4\4\5\t\5\4\6\t\6\4\7\t\7\4\b\t\b\4\t\t\t\4\n\t\n\4\13\t"+
-               
"\13\4\f\t\f\4\r\t\r\4\16\t\16\4\17\t\17\3\2\3\2\3\3\3\3\3\4\3\4\3\5\3"+
-               
"\5\3\6\3\6\3\7\3\7\3\b\3\b\3\b\3\t\5\t\60\n\t\3\n\3\n\6\n\64\n\n\r\n\16"+
-               
"\n\65\3\n\3\n\3\13\3\13\3\13\3\13\7\13>\n\13\f\13\16\13A\13\13\3\13\3"+
-               
"\13\3\13\3\f\3\f\3\f\3\f\7\fJ\n\f\f\f\16\fM\13\f\3\f\3\f\3\f\3\r\3\r\6"+
-               
"\rT\n\r\r\r\16\rU\3\16\6\16Y\n\16\r\16\16\16Z\3\16\3\16\3\17\3\17\4?K"+
-               
"\2\20\3\3\5\4\7\5\t\6\13\7\r\2\17\2\21\2\23\b\25\t\27\n\31\13\33\f\35"+
-               
"\r\3\2\6\6\2..]_aa}\177\23\2#\\^^`|\u0080\u0080\u00a2\u0251\u025b\u0294"+
-               
"\u02b2\u0371\u0402\u0501\u1e04\u1ef5\u1f03\u2001\u200e\u200f\u2041\u2042"+
-               
"\u2072\u2191\u2c02\u2ff1\u3003\ud801\uf902\ufdd1\ufdf2\uffff\5\2\"\"."+
-               
".\62;\5\2\13\f\16\17\"\"\2b\2\3\3\2\2\2\2\5\3\2\2\2\2\7\3\2\2\2\2\t\3"+
-               
"\2\2\2\2\13\3\2\2\2\2\23\3\2\2\2\2\25\3\2\2\2\2\27\3\2\2\2\2\31\3\2\2"+
-               
"\2\2\33\3\2\2\2\2\35\3\2\2\2\3\37\3\2\2\2\5!\3\2\2\2\7#\3\2\2\2\t%\3\2"+
-               
"\2\2\13\'\3\2\2\2\r)\3\2\2\2\17+\3\2\2\2\21/\3\2\2\2\23\61\3\2\2\2\25"+
-               "9\3\2\2\2\27E\3\2\2\2\31S\3\2\2\2\33X\3\2\2\2\35^\3\2\2\2\37 
\7}\2\2 "+
-               
"\4\3\2\2\2!\"\7\177\2\2\"\6\3\2\2\2#$\7~\2\2$\b\3\2\2\2%&\7.\2\2&\n\3"+
-               
"\2\2\2\'(\7a\2\2(\f\3\2\2\2)*\t\2\2\2*\16\3\2\2\2+,\7^\2\2,-\5\r\7\2-"+
-               
"\20\3\2\2\2.\60\t\3\2\2/.\3\2\2\2\60\22\3\2\2\2\61\63\7]\2\2\62\64\t\4"+
-               
"\2\2\63\62\3\2\2\2\64\65\3\2\2\2\65\63\3\2\2\2\65\66\3\2\2\2\66\67\3\2"+
-               
"\2\2\678\7_\2\28\24\3\2\2\29:\7\61\2\2:;\7\61\2\2;?\3\2\2\2<>\13\2\2\2"+
-               
"=<\3\2\2\2>A\3\2\2\2?@\3\2\2\2?=\3\2\2\2@B\3\2\2\2A?\3\2\2\2BC\7\61\2"+
-               
"\2CD\7\61\2\2D\26\3\2\2\2EF\7`\2\2FG\7`\2\2GK\3\2\2\2HJ\13\2\2\2IH\3\2"+
-               
"\2\2JM\3\2\2\2KL\3\2\2\2KI\3\2\2\2LN\3\2\2\2MK\3\2\2\2NO\7`\2\2OP\7`\2"+
-               
"\2P\30\3\2\2\2QT\5\21\t\2RT\5\17\b\2SQ\3\2\2\2SR\3\2\2\2TU\3\2\2\2US\3"+
-               
"\2\2\2UV\3\2\2\2V\32\3\2\2\2WY\t\5\2\2XW\3\2\2\2YZ\3\2\2\2ZX\3\2\2\2Z"+
-               
"[\3\2\2\2[\\\3\2\2\2\\]\b\16\2\2]\34\3\2\2\2^_\13\2\2\2_\36\3\2\2\2\n"+
-               "\2/\65?KSUZ\3\b\2\2";
+               
"\13\4\f\t\f\4\r\t\r\4\16\t\16\4\17\t\17\4\20\t\20\4\21\t\21\4\22\t\22"+
+               
"\3\2\3\2\3\3\3\3\3\4\3\4\3\5\3\5\3\6\3\6\3\7\3\7\3\b\3\b\3\t\3\t\3\n\3"+
+               
"\n\3\13\3\13\3\13\3\f\5\f<\n\f\3\r\3\r\3\r\3\r\7\rB\n\r\f\r\16\rE\13\r"+
+               
"\3\r\3\r\3\r\3\16\3\16\3\16\3\16\7\16N\n\16\f\16\16\16Q\13\16\3\16\3\16"+
+               
"\3\16\3\17\3\17\6\17X\n\17\r\17\16\17Y\3\20\3\20\6\20^\n\20\r\20\16\20"+
+               
"_\3\20\3\20\3\21\6\21e\n\21\r\21\16\21f\3\21\3\21\3\22\3\22\4CO\2\23\3"+
+               
"\3\5\4\7\5\t\6\13\7\r\b\17\t\21\n\23\2\25\2\27\2\31\13\33\f\35\r\37\16"+
+               
"!\17#\20\3\2\6\7\2..\61\61]_aa}\177\23\2#\\^^`|\u0080\u0080\u00a2\u0251"+
+               
"\u025b\u0294\u02b2\u0371\u0402\u0501\u1e04\u1ef5\u1f03\u2001\u200e\u200f"+
+               
"\u2041\u2042\u2072\u2191\u2c02\u2ff1\u3003\ud801\uf902\ufdd1\ufdf2\uffff"+
+               
"\5\2\"\"..\62;\5\2\13\f\16\17\"\"\2n\2\3\3\2\2\2\2\5\3\2\2\2\2\7\3\2\2"+
+               
"\2\2\t\3\2\2\2\2\13\3\2\2\2\2\r\3\2\2\2\2\17\3\2\2\2\2\21\3\2\2\2\2\31"+
+               
"\3\2\2\2\2\33\3\2\2\2\2\35\3\2\2\2\2\37\3\2\2\2\2!\3\2\2\2\2#\3\2\2\2"+
+               
"\3%\3\2\2\2\5\'\3\2\2\2\7)\3\2\2\2\t+\3\2\2\2\13-\3\2\2\2\r/\3\2\2\2\17"+
+               
"\61\3\2\2\2\21\63\3\2\2\2\23\65\3\2\2\2\25\67\3\2\2\2\27;\3\2\2\2\31="+
+               
"\3\2\2\2\33I\3\2\2\2\35W\3\2\2\2\37[\3\2\2\2!d\3\2\2\2#j\3\2\2\2%&\7}"+
+               
"\2\2&\4\3\2\2\2\'(\7\177\2\2(\6\3\2\2\2)*\7~\2\2*\b\3\2\2\2+,\7.\2\2,"+
+               
"\n\3\2\2\2-.\7a\2\2.\f\3\2\2\2/\60\7]\2\2\60\16\3\2\2\2\61\62\7_\2\2\62"+
+               
"\20\3\2\2\2\63\64\7A\2\2\64\22\3\2\2\2\65\66\t\2\2\2\66\24\3\2\2\2\67"+
+               
"8\7^\2\289\5\23\n\29\26\3\2\2\2:<\t\3\2\2;:\3\2\2\2<\30\3\2\2\2=>\7\61"+
+               
"\2\2>?\7\61\2\2?C\3\2\2\2@B\13\2\2\2A@\3\2\2\2BE\3\2\2\2CD\3\2\2\2CA\3"+
+               
"\2\2\2DF\3\2\2\2EC\3\2\2\2FG\7\61\2\2GH\7\61\2\2H\32\3\2\2\2IJ\7`\2\2"+
+               
"JK\7`\2\2KO\3\2\2\2LN\13\2\2\2ML\3\2\2\2NQ\3\2\2\2OP\3\2\2\2OM\3\2\2\2"+
+               
"PR\3\2\2\2QO\3\2\2\2RS\7`\2\2ST\7`\2\2T\34\3\2\2\2UX\5\27\f\2VX\5\25\13"+
+               
"\2WU\3\2\2\2WV\3\2\2\2XY\3\2\2\2YW\3\2\2\2YZ\3\2\2\2Z\36\3\2\2\2[]\7]"+
+               
"\2\2\\^\t\4\2\2]\\\3\2\2\2^_\3\2\2\2_]\3\2\2\2_`\3\2\2\2`a\3\2\2\2ab\7"+
+               "_\2\2b 
\3\2\2\2ce\t\5\2\2dc\3\2\2\2ef\3\2\2\2fd\3\2\2\2fg\3\2\2\2gh\3"+
+               
"\2\2\2hi\b\21\2\2i\"\3\2\2\2jk\13\2\2\2k$\3\2\2\2\n\2;COWY_f\3\b\2\2";
        public static final ATN _ATN =
                new ATNDeserializer().deserialize(_serializedATN.toCharArray());
        static {
diff --git 
a/nlpcraft/src/main/scala/org/apache/nlpcraft/common/makro/antlr4/NCMacroDslLexer.tokens
 
b/nlpcraft/src/main/scala/org/apache/nlpcraft/common/makro/antlr4/NCMacroDslLexer.tokens
index 7ef9fd0..319aa8e 100644
--- 
a/nlpcraft/src/main/scala/org/apache/nlpcraft/common/makro/antlr4/NCMacroDslLexer.tokens
+++ 
b/nlpcraft/src/main/scala/org/apache/nlpcraft/common/makro/antlr4/NCMacroDslLexer.tokens
@@ -3,14 +3,20 @@ RCURLY=2
 VERT=3
 COMMA=4
 UNDERSCORE=5
-MINMAX=6
-REGEX_TXT=7
-IDL_TXT=8
-TXT=9
-WS=10
-ERR_CHAR=11
+LBR=6
+RBR=7
+QUESTION=8
+REGEX_TXT=9
+IDL_TXT=10
+TXT=11
+MINMAX=12
+WS=13
+ERR_CHAR=14
 '{'=1
 '}'=2
 '|'=3
 ','=4
 '_'=5
+'['=6
+']'=7
+'?'=8
diff --git 
a/nlpcraft/src/main/scala/org/apache/nlpcraft/common/makro/antlr4/NCMacroDslListener.java
 
b/nlpcraft/src/main/scala/org/apache/nlpcraft/common/makro/antlr4/NCMacroDslListener.java
index eed7fb4..eb0821d 100644
--- 
a/nlpcraft/src/main/scala/org/apache/nlpcraft/common/makro/antlr4/NCMacroDslListener.java
+++ 
b/nlpcraft/src/main/scala/org/apache/nlpcraft/common/makro/antlr4/NCMacroDslListener.java
@@ -67,4 +67,24 @@ public interface NCMacroDslListener extends 
ParseTreeListener {
         * @param ctx the parse tree
         */
        void exitList(NCMacroDslParser.ListContext ctx);
+       /**
+        * Enter a parse tree produced by {@link NCMacroDslParser#minMax}.
+        * @param ctx the parse tree
+        */
+       void enterMinMax(NCMacroDslParser.MinMaxContext ctx);
+       /**
+        * Exit a parse tree produced by {@link NCMacroDslParser#minMax}.
+        * @param ctx the parse tree
+        */
+       void exitMinMax(NCMacroDslParser.MinMaxContext ctx);
+       /**
+        * Enter a parse tree produced by {@link 
NCMacroDslParser#minMaxShortcut}.
+        * @param ctx the parse tree
+        */
+       void enterMinMaxShortcut(NCMacroDslParser.MinMaxShortcutContext ctx);
+       /**
+        * Exit a parse tree produced by {@link 
NCMacroDslParser#minMaxShortcut}.
+        * @param ctx the parse tree
+        */
+       void exitMinMaxShortcut(NCMacroDslParser.MinMaxShortcutContext ctx);
 }
\ No newline at end of file
diff --git 
a/nlpcraft/src/main/scala/org/apache/nlpcraft/common/makro/antlr4/NCMacroDslParser.java
 
b/nlpcraft/src/main/scala/org/apache/nlpcraft/common/makro/antlr4/NCMacroDslParser.java
index b0ce4f5..eef5f94 100644
--- 
a/nlpcraft/src/main/scala/org/apache/nlpcraft/common/makro/antlr4/NCMacroDslParser.java
+++ 
b/nlpcraft/src/main/scala/org/apache/nlpcraft/common/makro/antlr4/NCMacroDslParser.java
@@ -17,28 +17,28 @@ public class NCMacroDslParser extends Parser {
        protected static final PredictionContextCache _sharedContextCache =
                new PredictionContextCache();
        public static final int
-               LCURLY=1, RCURLY=2, VERT=3, COMMA=4, UNDERSCORE=5, MINMAX=6, 
REGEX_TXT=7, 
-               IDL_TXT=8, TXT=9, WS=10, ERR_CHAR=11;
+               LCURLY=1, RCURLY=2, VERT=3, COMMA=4, UNDERSCORE=5, LBR=6, 
RBR=7, QUESTION=8, 
+               REGEX_TXT=9, IDL_TXT=10, TXT=11, MINMAX=12, WS=13, ERR_CHAR=14;
        public static final int
                RULE_makro = 0, RULE_expr = 1, RULE_item = 2, RULE_syn = 3, 
RULE_group = 4, 
-               RULE_list = 5;
+               RULE_list = 5, RULE_minMax = 6, RULE_minMaxShortcut = 7;
        private static String[] makeRuleNames() {
                return new String[] {
-                       "makro", "expr", "item", "syn", "group", "list"
+                       "makro", "expr", "item", "syn", "group", "list", 
"minMax", "minMaxShortcut"
                };
        }
        public static final String[] ruleNames = makeRuleNames();
 
        private static String[] makeLiteralNames() {
                return new String[] {
-                       null, "'{'", "'}'", "'|'", "','", "'_'"
+                       null, "'{'", "'}'", "'|'", "','", "'_'", "'['", "']'", 
"'?'"
                };
        }
        private static final String[] _LITERAL_NAMES = makeLiteralNames();
        private static String[] makeSymbolicNames() {
                return new String[] {
-                       null, "LCURLY", "RCURLY", "VERT", "COMMA", 
"UNDERSCORE", "MINMAX", "REGEX_TXT", 
-                       "IDL_TXT", "TXT", "WS", "ERR_CHAR"
+                       null, "LCURLY", "RCURLY", "VERT", "COMMA", 
"UNDERSCORE", "LBR", "RBR", 
+                       "QUESTION", "REGEX_TXT", "IDL_TXT", "TXT", "MINMAX", 
"WS", "ERR_CHAR"
                };
        }
        private static final String[] _SYMBOLIC_NAMES = makeSymbolicNames();
@@ -117,9 +117,9 @@ public class NCMacroDslParser extends Parser {
                try {
                        enterOuterAlt(_localctx, 1);
                        {
-                       setState(12);
+                       setState(16);
                        expr(0);
-                       setState(13);
+                       setState(17);
                        match(EOF);
                        }
                }
@@ -171,11 +171,11 @@ public class NCMacroDslParser extends Parser {
                        enterOuterAlt(_localctx, 1);
                        {
                        {
-                       setState(16);
+                       setState(20);
                        item();
                        }
                        _ctx.stop = _input.LT(-1);
-                       setState(22);
+                       setState(26);
                        _errHandler.sync(this);
                        _alt = getInterpreter().adaptivePredict(_input,0,_ctx);
                        while ( _alt!=2 && 
_alt!=org.antlr.v4.runtime.atn.ATN.INVALID_ALT_NUMBER ) {
@@ -186,14 +186,14 @@ public class NCMacroDslParser extends Parser {
                                        {
                                        _localctx = new ExprContext(_parentctx, 
_parentState);
                                        pushNewRecursionContext(_localctx, 
_startState, RULE_expr);
-                                       setState(18);
+                                       setState(22);
                                        if (!(precpred(_ctx, 1))) throw new 
FailedPredicateException(this, "precpred(_ctx, 1)");
-                                       setState(19);
+                                       setState(23);
                                        item();
                                        }
                                        } 
                                }
-                               setState(24);
+                               setState(28);
                                _errHandler.sync(this);
                                _alt = 
getInterpreter().adaptivePredict(_input,0,_ctx);
                        }
@@ -235,7 +235,7 @@ public class NCMacroDslParser extends Parser {
                ItemContext _localctx = new ItemContext(_ctx, getState());
                enterRule(_localctx, 4, RULE_item);
                try {
-                       setState(27);
+                       setState(31);
                        _errHandler.sync(this);
                        switch (_input.LA(1)) {
                        case REGEX_TXT:
@@ -243,14 +243,14 @@ public class NCMacroDslParser extends Parser {
                        case TXT:
                                enterOuterAlt(_localctx, 1);
                                {
-                               setState(25);
+                               setState(29);
                                syn();
                                }
                                break;
                        case LCURLY:
                                enterOuterAlt(_localctx, 2);
                                {
-                               setState(26);
+                               setState(30);
                                group();
                                }
                                break;
@@ -294,7 +294,7 @@ public class NCMacroDslParser extends Parser {
                try {
                        enterOuterAlt(_localctx, 1);
                        {
-                       setState(29);
+                       setState(33);
                        _la = _input.LA(1);
                        if ( !((((_la) & ~0x3f) == 0 && ((1L << _la) & ((1L << 
REGEX_TXT) | (1L << IDL_TXT) | (1L << TXT))) != 0)) ) {
                        _errHandler.recoverInline(this);
@@ -323,7 +323,9 @@ public class NCMacroDslParser extends Parser {
                        return getRuleContext(ListContext.class,0);
                }
                public TerminalNode RCURLY() { return 
getToken(NCMacroDslParser.RCURLY, 0); }
-               public TerminalNode MINMAX() { return 
getToken(NCMacroDslParser.MINMAX, 0); }
+               public MinMaxContext minMax() {
+                       return getRuleContext(MinMaxContext.class,0);
+               }
                public GroupContext(ParserRuleContext parent, int 
invokingState) {
                        super(parent, invokingState);
                }
@@ -344,19 +346,19 @@ public class NCMacroDslParser extends Parser {
                try {
                        enterOuterAlt(_localctx, 1);
                        {
-                       setState(31);
+                       setState(35);
                        match(LCURLY);
-                       setState(32);
+                       setState(36);
                        list(0);
-                       setState(33);
+                       setState(37);
                        match(RCURLY);
-                       setState(35);
+                       setState(39);
                        _errHandler.sync(this);
                        switch ( 
getInterpreter().adaptivePredict(_input,2,_ctx) ) {
                        case 1:
                                {
-                               setState(34);
-                               match(MINMAX);
+                               setState(38);
+                               minMax();
                                }
                                break;
                        }
@@ -411,7 +413,7 @@ public class NCMacroDslParser extends Parser {
                        int _alt;
                        enterOuterAlt(_localctx, 1);
                        {
-                       setState(42);
+                       setState(46);
                        _errHandler.sync(this);
                        switch (_input.LA(1)) {
                        case LCURLY:
@@ -419,17 +421,17 @@ public class NCMacroDslParser extends Parser {
                        case IDL_TXT:
                        case TXT:
                                {
-                               setState(38);
+                               setState(42);
                                expr(0);
                                }
                                break;
                        case UNDERSCORE:
                                {
-                               setState(39);
+                               setState(43);
                                match(UNDERSCORE);
-                               setState(40);
+                               setState(44);
                                match(VERT);
-                               setState(41);
+                               setState(45);
                                list(1);
                                }
                                break;
@@ -437,7 +439,7 @@ public class NCMacroDslParser extends Parser {
                                throw new NoViableAltException(this);
                        }
                        _ctx.stop = _input.LT(-1);
-                       setState(52);
+                       setState(56);
                        _errHandler.sync(this);
                        _alt = getInterpreter().adaptivePredict(_input,5,_ctx);
                        while ( _alt!=2 && 
_alt!=org.antlr.v4.runtime.atn.ATN.INVALID_ALT_NUMBER ) {
@@ -445,18 +447,18 @@ public class NCMacroDslParser extends Parser {
                                        if ( _parseListeners!=null ) 
triggerExitRuleEvent();
                                        _prevctx = _localctx;
                                        {
-                                       setState(50);
+                                       setState(54);
                                        _errHandler.sync(this);
                                        switch ( 
getInterpreter().adaptivePredict(_input,4,_ctx) ) {
                                        case 1:
                                                {
                                                _localctx = new 
ListContext(_parentctx, _parentState);
                                                
pushNewRecursionContext(_localctx, _startState, RULE_list);
-                                               setState(44);
+                                               setState(48);
                                                if (!(precpred(_ctx, 3))) throw 
new FailedPredicateException(this, "precpred(_ctx, 3)");
-                                               setState(45);
+                                               setState(49);
                                                match(VERT);
-                                               setState(46);
+                                               setState(50);
                                                expr(0);
                                                }
                                                break;
@@ -464,18 +466,18 @@ public class NCMacroDslParser extends Parser {
                                                {
                                                _localctx = new 
ListContext(_parentctx, _parentState);
                                                
pushNewRecursionContext(_localctx, _startState, RULE_list);
-                                               setState(47);
+                                               setState(51);
                                                if (!(precpred(_ctx, 2))) throw 
new FailedPredicateException(this, "precpred(_ctx, 2)");
-                                               setState(48);
+                                               setState(52);
                                                match(VERT);
-                                               setState(49);
+                                               setState(53);
                                                match(UNDERSCORE);
                                                }
                                                break;
                                        }
                                        } 
                                }
-                               setState(54);
+                               setState(58);
                                _errHandler.sync(this);
                                _alt = 
getInterpreter().adaptivePredict(_input,5,_ctx);
                        }
@@ -492,6 +494,98 @@ public class NCMacroDslParser extends Parser {
                return _localctx;
        }
 
+       public static class MinMaxContext extends ParserRuleContext {
+               public MinMaxShortcutContext minMaxShortcut() {
+                       return getRuleContext(MinMaxShortcutContext.class,0);
+               }
+               public TerminalNode MINMAX() { return 
getToken(NCMacroDslParser.MINMAX, 0); }
+               public MinMaxContext(ParserRuleContext parent, int 
invokingState) {
+                       super(parent, invokingState);
+               }
+               @Override public int getRuleIndex() { return RULE_minMax; }
+               @Override
+               public void enterRule(ParseTreeListener listener) {
+                       if ( listener instanceof NCMacroDslListener ) 
((NCMacroDslListener)listener).enterMinMax(this);
+               }
+               @Override
+               public void exitRule(ParseTreeListener listener) {
+                       if ( listener instanceof NCMacroDslListener ) 
((NCMacroDslListener)listener).exitMinMax(this);
+               }
+       }
+
+       public final MinMaxContext minMax() throws RecognitionException {
+               MinMaxContext _localctx = new MinMaxContext(_ctx, getState());
+               enterRule(_localctx, 12, RULE_minMax);
+               try {
+                       setState(61);
+                       _errHandler.sync(this);
+                       switch (_input.LA(1)) {
+                       case QUESTION:
+                               enterOuterAlt(_localctx, 1);
+                               {
+                               setState(59);
+                               minMaxShortcut();
+                               }
+                               break;
+                       case MINMAX:
+                               enterOuterAlt(_localctx, 2);
+                               {
+                               setState(60);
+                               match(MINMAX);
+                               }
+                               break;
+                       default:
+                               throw new NoViableAltException(this);
+                       }
+               }
+               catch (RecognitionException re) {
+                       _localctx.exception = re;
+                       _errHandler.reportError(this, re);
+                       _errHandler.recover(this, re);
+               }
+               finally {
+                       exitRule();
+               }
+               return _localctx;
+       }
+
+       public static class MinMaxShortcutContext extends ParserRuleContext {
+               public TerminalNode QUESTION() { return 
getToken(NCMacroDslParser.QUESTION, 0); }
+               public MinMaxShortcutContext(ParserRuleContext parent, int 
invokingState) {
+                       super(parent, invokingState);
+               }
+               @Override public int getRuleIndex() { return 
RULE_minMaxShortcut; }
+               @Override
+               public void enterRule(ParseTreeListener listener) {
+                       if ( listener instanceof NCMacroDslListener ) 
((NCMacroDslListener)listener).enterMinMaxShortcut(this);
+               }
+               @Override
+               public void exitRule(ParseTreeListener listener) {
+                       if ( listener instanceof NCMacroDslListener ) 
((NCMacroDslListener)listener).exitMinMaxShortcut(this);
+               }
+       }
+
+       public final MinMaxShortcutContext minMaxShortcut() throws 
RecognitionException {
+               MinMaxShortcutContext _localctx = new 
MinMaxShortcutContext(_ctx, getState());
+               enterRule(_localctx, 14, RULE_minMaxShortcut);
+               try {
+                       enterOuterAlt(_localctx, 1);
+                       {
+                       setState(63);
+                       match(QUESTION);
+                       }
+               }
+               catch (RecognitionException re) {
+                       _localctx.exception = re;
+                       _errHandler.reportError(this, re);
+                       _errHandler.recover(this, re);
+               }
+               finally {
+                       exitRule();
+               }
+               return _localctx;
+       }
+
        public boolean sempred(RuleContext _localctx, int ruleIndex, int 
predIndex) {
                switch (ruleIndex) {
                case 1:
@@ -519,21 +613,23 @@ public class NCMacroDslParser extends Parser {
        }
 
        public static final String _serializedATN =
-               
"\3\u608b\ua72a\u8133\ub9ed\u417c\u3be7\u7786\u5964\3\r:\4\2\t\2\4\3\t"+
-               
"\3\4\4\t\4\4\5\t\5\4\6\t\6\4\7\t\7\3\2\3\2\3\2\3\3\3\3\3\3\3\3\3\3\7\3"+
-               
"\27\n\3\f\3\16\3\32\13\3\3\4\3\4\5\4\36\n\4\3\5\3\5\3\6\3\6\3\6\3\6\5"+
-               
"\6&\n\6\3\7\3\7\3\7\3\7\3\7\5\7-\n\7\3\7\3\7\3\7\3\7\3\7\3\7\7\7\65\n"+
-               
"\7\f\7\16\78\13\7\3\7\2\4\4\f\b\2\4\6\b\n\f\2\3\3\2\t\13\29\2\16\3\2\2"+
-               
"\2\4\21\3\2\2\2\6\35\3\2\2\2\b\37\3\2\2\2\n!\3\2\2\2\f,\3\2\2\2\16\17"+
-               
"\5\4\3\2\17\20\7\2\2\3\20\3\3\2\2\2\21\22\b\3\1\2\22\23\5\6\4\2\23\30"+
-               
"\3\2\2\2\24\25\f\3\2\2\25\27\5\6\4\2\26\24\3\2\2\2\27\32\3\2\2\2\30\26"+
-               
"\3\2\2\2\30\31\3\2\2\2\31\5\3\2\2\2\32\30\3\2\2\2\33\36\5\b\5\2\34\36"+
-               "\5\n\6\2\35\33\3\2\2\2\35\34\3\2\2\2\36\7\3\2\2\2\37 \t\2\2\2 
\t\3\2\2"+
-               
"\2!\"\7\3\2\2\"#\5\f\7\2#%\7\4\2\2$&\7\b\2\2%$\3\2\2\2%&\3\2\2\2&\13\3"+
-               
"\2\2\2\'(\b\7\1\2(-\5\4\3\2)*\7\7\2\2*+\7\5\2\2+-\5\f\7\3,\'\3\2\2\2,"+
-               
")\3\2\2\2-\66\3\2\2\2./\f\5\2\2/\60\7\5\2\2\60\65\5\4\3\2\61\62\f\4\2"+
-               
"\2\62\63\7\5\2\2\63\65\7\7\2\2\64.\3\2\2\2\64\61\3\2\2\2\658\3\2\2\2\66"+
-               
"\64\3\2\2\2\66\67\3\2\2\2\67\r\3\2\2\28\66\3\2\2\2\b\30\35%,\64\66";
+               
"\3\u608b\ua72a\u8133\ub9ed\u417c\u3be7\u7786\u5964\3\20D\4\2\t\2\4\3\t"+
+               
"\3\4\4\t\4\4\5\t\5\4\6\t\6\4\7\t\7\4\b\t\b\4\t\t\t\3\2\3\2\3\2\3\3\3\3"+
+               
"\3\3\3\3\3\3\7\3\33\n\3\f\3\16\3\36\13\3\3\4\3\4\5\4\"\n\4\3\5\3\5\3\6"+
+               
"\3\6\3\6\3\6\5\6*\n\6\3\7\3\7\3\7\3\7\3\7\5\7\61\n\7\3\7\3\7\3\7\3\7\3"+
+               
"\7\3\7\7\79\n\7\f\7\16\7<\13\7\3\b\3\b\5\b@\n\b\3\t\3\t\3\t\2\4\4\f\n"+
+               
"\2\4\6\b\n\f\16\20\2\3\3\2\13\r\2B\2\22\3\2\2\2\4\25\3\2\2\2\6!\3\2\2"+
+               
"\2\b#\3\2\2\2\n%\3\2\2\2\f\60\3\2\2\2\16?\3\2\2\2\20A\3\2\2\2\22\23\5"+
+               
"\4\3\2\23\24\7\2\2\3\24\3\3\2\2\2\25\26\b\3\1\2\26\27\5\6\4\2\27\34\3"+
+               
"\2\2\2\30\31\f\3\2\2\31\33\5\6\4\2\32\30\3\2\2\2\33\36\3\2\2\2\34\32\3"+
+               "\2\2\2\34\35\3\2\2\2\35\5\3\2\2\2\36\34\3\2\2\2\37\"\5\b\5\2 
\"\5\n\6"+
+               "\2!\37\3\2\2\2! 
\3\2\2\2\"\7\3\2\2\2#$\t\2\2\2$\t\3\2\2\2%&\7\3\2\2&\'"+
+               
"\5\f\7\2\')\7\4\2\2(*\5\16\b\2)(\3\2\2\2)*\3\2\2\2*\13\3\2\2\2+,\b\7\1"+
+               
"\2,\61\5\4\3\2-.\7\7\2\2./\7\5\2\2/\61\5\f\7\3\60+\3\2\2\2\60-\3\2\2\2"+
+               
"\61:\3\2\2\2\62\63\f\5\2\2\63\64\7\5\2\2\649\5\4\3\2\65\66\f\4\2\2\66"+
+               
"\67\7\5\2\2\679\7\7\2\28\62\3\2\2\28\65\3\2\2\29<\3\2\2\2:8\3\2\2\2:;"+
+               
"\3\2\2\2;\r\3\2\2\2<:\3\2\2\2=@\5\20\t\2>@\7\16\2\2?=\3\2\2\2?>\3\2\2"+
+               "\2@\17\3\2\2\2AB\7\n\2\2B\21\3\2\2\2\t\34!)\608:?";
        public static final ATN _ATN =
                new ATNDeserializer().deserialize(_serializedATN.toCharArray());
        static {
diff --git 
a/nlpcraft/src/test/scala/org/apache/nlpcraft/common/makro/NCMacroParserSpec.scala
 
b/nlpcraft/src/test/scala/org/apache/nlpcraft/common/makro/NCMacroParserSpec.scala
index c25fb2c..28c1a0d 100644
--- 
a/nlpcraft/src/test/scala/org/apache/nlpcraft/common/makro/NCMacroParserSpec.scala
+++ 
b/nlpcraft/src/test/scala/org/apache/nlpcraft/common/makro/NCMacroParserSpec.scala
@@ -138,6 +138,8 @@ class NCMacroParserSpec  {
         checkEq("a {b|_} d", Seq("a b d", "a d"))
         checkEq("a {b|_}       d", Seq("a b d", "a d"))
         checkEq("a {b}", Seq("a b"))
+        checkEq("a {b}?", Seq("a", "a b"))
+        checkEq("a {b}[2, 3]", Seq("a b b", "a b b b"))
         checkEq("a {b} {c|_}", Seq("a b", "a b c"))
         checkEq("a {{b|c}}", Seq("a b", "a c"))
         checkEq("a {b|_|{g\\}}[1,2]}", Seq("a", "a b", "a g}", "a g} g}"))

Reply via email to