@@ -1160,7 +1160,7 @@ public static ParseTree compile(TokenStream stream) throws ConfigCompileExceptio
11601160 int parens = 0 ;
11611161 Token t = null ;
11621162
1163- int bracketCount = 0 ;
1163+ int braceCount = 0 ;
11641164
11651165 // Create a Token array to iterate over, rather than using the LinkedList's O(n) get() method.
11661166 Token [] tokenArray = stream .toArray (new Token [stream .size ()]);
@@ -1177,13 +1177,13 @@ public static ParseTree compile(TokenStream stream) throws ConfigCompileExceptio
11771177 tree .addChild (b );
11781178 tree = b ;
11791179 parents .push (b );
1180- bracketCount ++;
1180+ braceCount ++;
11811181 constructCount .push (new AtomicInteger (0 ));
11821182 continue ;
11831183 }
11841184
11851185 if (t .type == TType .RCURLY_BRACKET ) {
1186- bracketCount --;
1186+ braceCount --;
11871187 if (constructCount .peek ().get () > 1 ) {
11881188 //We need to autoconcat some stuff
11891189 int stacks = constructCount .peek ().get ();
@@ -1575,14 +1575,50 @@ public static ParseTree compile(TokenStream stream) throws ConfigCompileExceptio
15751575
15761576 assert t != null ;
15771577
1578+ // Handle mismatching square brackets "[]".
1579+ assert arrayStack .size () != 0 : "The last element of arrayStack should be present, but it was popped." ;
15781580 if (arrayStack .size () != 1 ) {
1579- throw new ConfigCompileException ("Mismatched square brackets" , t .target );
1581+
1582+ // Some starting square bracket '[' was not closed at the end of the script.
1583+ // Find the last '[' that was not closed and use that as target instead of the last line of the script.
1584+ Target target = traceMismatchedOpenToken (stream , TType .LSQUARE_BRACKET , TType .RSQUARE_BRACKET );
1585+ assert target != null : "Mismatched bracket was detected, but target-finding code could not find it." ;
1586+ if (target == null ) {
1587+ target = t .target ;
1588+ }
1589+
1590+ // Throw a CRE.
1591+ throw new ConfigCompileException ("Mismatched square brackets" , target );
15801592 }
1593+
1594+ // Handle mismatching parentheses "()".
15811595 if (parens != 0 ) {
1582- throw new ConfigCompileException ("Mismatched parenthesis" , t .target );
1596+
1597+ // Some starting parentheses '(' was not closed at the end of the script.
1598+ // Find the last '(' that was not closed and use that as target instead of the last line of the script.
1599+ Target target = traceMismatchedOpenToken (stream , TType .FUNC_START , TType .FUNC_END );
1600+ assert target != null : "Mismatched parentheses was detected, but target-finding code could not find it." ;
1601+ if (target == null ) {
1602+ target = t .target ;
1603+ }
1604+
1605+ // Throw a CRE.
1606+ throw new ConfigCompileException ("Mismatched parentheses" , target );
15831607 }
1584- if (bracketCount != 0 ) {
1585- throw new ConfigCompileException ("Mismatched curly braces" , t .target );
1608+
1609+ // Handle mismatching curly braces "{}".
1610+ if (braceCount != 0 ) {
1611+
1612+ // Some starting curly brace '{' was not closed at the end of the script.
1613+ // Find the last '{' that was not closed and use that as target instead of the last line of the script.
1614+ Target target = traceMismatchedOpenToken (stream , TType .LCURLY_BRACKET , TType .RCURLY_BRACKET );
1615+ assert target != null : "Mismatched curly brace was detected, but target-finding code could not find it." ;
1616+ if (target == null ) {
1617+ target = t .target ;
1618+ }
1619+
1620+ // Throw a CRE.
1621+ throw new ConfigCompileException ("Mismatched curly braces" , target );
15861622 }
15871623
15881624 Stack <List <Procedure >> procs = new Stack <>();
@@ -1608,6 +1644,34 @@ public static ParseTree compile(TokenStream stream) throws ConfigCompileExceptio
16081644 return tree ;
16091645 }
16101646
1647+ /**
1648+ * Trace target of mismatching open tokens such as '(' in '()' or '{' in '{}'. This should be used when it is
1649+ * known that there are more start than close tokens, but no target is known for the extra start token.
1650+ * @param stream - The token stream to scan.
1651+ * @param openType - The open type, which would be {@link TType#FUNC_START (} for a parentheses check.
1652+ * @param closeType - The close type, which would be {@link TType#FUNC_END )} for a parentheses check.
1653+ * @return The target of the last occurrence of the opening type that did not have a matching closing type.
1654+ * Returns null of no target was found.
1655+ */
1656+ private static Target traceMismatchedOpenToken (TokenStream stream , TType openType , TType closeType ) {
1657+ // Some starting parentheses '(' was not closed at the end of the script.
1658+ // Find the last '(' that was not closed and use that as target instead of the last line of the script.
1659+ Iterator <Token > iterator = stream .descendingIterator ();
1660+ int closingCount = 0 ;
1661+ while (iterator .hasNext ()) {
1662+ Token token = iterator .next ();
1663+ if (token .type == closeType ) {
1664+ closingCount ++;
1665+ } else if (token .type == openType ) {
1666+ if (closingCount <= 0 ) {
1667+ return token .target ;
1668+ }
1669+ closingCount --;
1670+ }
1671+ }
1672+ return null ;
1673+ }
1674+
16111675 /**
16121676 * Recurses down the tree and ensures that breaks don't bubble up past procedures or the root code tree.
16131677 *
0 commit comments