From 452e4015dbe8ddc35d251f344eee1177dabe69ac Mon Sep 17 00:00:00 2001 From: Sam Harwell Date: Sun, 17 Jul 2016 09:51:16 -0500 Subject: [PATCH] Add warning for unsupported delimiter characters Fixes #84 --- src/org/stringtemplate/v4/STGroup.java | 35 +++++++++++++++++++ src/org/stringtemplate/v4/compiler/Group.g | 19 ++++++++-- src/org/stringtemplate/v4/misc/ErrorType.java | 1 + .../v4/test/TestGroupSyntax.java | 24 +++++++++++++ 4 files changed, 77 insertions(+), 2 deletions(-) diff --git a/src/org/stringtemplate/v4/STGroup.java b/src/org/stringtemplate/v4/STGroup.java index a33ca094..6dbc7085 100644 --- a/src/org/stringtemplate/v4/STGroup.java +++ b/src/org/stringtemplate/v4/STGroup.java @@ -80,6 +80,25 @@ public class STGroup { TEMPLATE_FILE_EXTENSION = ".st"; } + private static final boolean[] RESERVED_CHARACTERS = new boolean[127]; + static { + for (char c = 'a'; c <= 'z'; c++) { + RESERVED_CHARACTERS[c] = true; + } + + for (char c = 'A'; c <= 'Z'; c++) { + RESERVED_CHARACTERS[c] = true; + } + + for (char c = '0'; c <= '9'; c++) { + RESERVED_CHARACTERS[c] = true; + } + + RESERVED_CHARACTERS['@'] = true; + RESERVED_CHARACTERS['-'] = true; + RESERVED_CHARACTERS['_'] = true; + } + /** When we use key as a value in a dictionary, this is how we signify. */ public static final String DICT_KEY = "key"; public static final String DEFAULT_KEY = "default"; @@ -293,6 +312,22 @@ public synchronized void unload() { /** Force a load if it makes sense for the group. */ public void load() { } + /** + * Determines if a specified character may be used as a user-specified delimiter. + * + * @param c The character + * @return {@code true} if the character is reserved by the StringTemplate + * language; otherwise, {@code false} if the character may be used as a + * delimiter. + * + * @since 4.0.9 + */ + public static boolean isReservedCharacter(char c) { + return c >= 0 + && c < RESERVED_CHARACTERS.length + && RESERVED_CHARACTERS[c]; + } + protected CompiledST lookupImportedTemplate(String name) { if ( imports.size()==0 ) return null; for (STGroup g : imports) { diff --git a/src/org/stringtemplate/v4/compiler/Group.g b/src/org/stringtemplate/v4/compiler/Group.g index 1932b883..743ddd66 100644 --- a/src/org/stringtemplate/v4/compiler/Group.g +++ b/src/org/stringtemplate/v4/compiler/Group.g @@ -190,8 +190,23 @@ groupName returns [String name] delimiters : 'delimiters' a=STRING ',' b=STRING { - group.delimiterStartChar=$a.getText().charAt(1); - group.delimiterStopChar=$b.getText().charAt(1); + boolean supported = true; + char startCharacter = $a.getText().charAt(1); + if (STGroup.isReservedCharacter(startCharacter)) { + group.errMgr.compileTimeError(ErrorType.UNSUPPORTED_DELIMITER, null, $a, String.valueOf(startCharacter)); + supported = false; + } + + char stopCharacter = $b.getText().charAt(1); + if (STGroup.isReservedCharacter(stopCharacter)) { + group.errMgr.compileTimeError(ErrorType.UNSUPPORTED_DELIMITER, null, $b, String.valueOf(stopCharacter)); + supported = false; + } + + if (supported) { + group.delimiterStartChar=$a.getText().charAt(1); + group.delimiterStopChar=$b.getText().charAt(1); + } } ; diff --git a/src/org/stringtemplate/v4/misc/ErrorType.java b/src/org/stringtemplate/v4/misc/ErrorType.java index 3f2e64da..dc662248 100644 --- a/src/org/stringtemplate/v4/misc/ErrorType.java +++ b/src/org/stringtemplate/v4/misc/ErrorType.java @@ -59,6 +59,7 @@ public enum ErrorType { INVALID_TEMPLATE_NAME("invalid template name or path: %s"), ANON_ARGUMENT_MISMATCH("anonymous template has %s arg(s) but mapped across %s value(s)"), REQUIRED_PARAMETER_AFTER_OPTIONAL("required parameters (%s) must appear before optional parameters"), + UNSUPPORTED_DELIMITER("unsupported delimiter character: %s"), // INTERNAL ERRORS INTERNAL_ERROR("%s"), diff --git a/test/org/stringtemplate/v4/test/TestGroupSyntax.java b/test/org/stringtemplate/v4/test/TestGroupSyntax.java index 31d59fe9..edf029ca 100644 --- a/test/org/stringtemplate/v4/test/TestGroupSyntax.java +++ b/test/org/stringtemplate/v4/test/TestGroupSyntax.java @@ -119,6 +119,30 @@ public class TestGroupSyntax extends BaseTest { assertEquals(expected, result); } + /** + * This is a regression test for antlr/stringtemplate4#84. + */ + @Test public void testSetUnsupportedDelimiters_At() throws Exception { + String templates = + "delimiters \"@\", \"@\"" + Misc.newline + + "ta(x) ::= \"[]\"" + Misc.newline; + + writeFile(tmpdir, "t.stg", templates); + ErrorBuffer errors = new ErrorBuffer(); + STGroup group = new STGroupFile(tmpdir+"/"+"t.stg"); + group.setListener(errors); + ST st = group.getInstanceOf("ta"); + st.add("x", "hi"); + String expected = "[hi]"; + String result = st.render(); + assertEquals(expected, result); + + String expectedErrors = "[t.stg 1:11: unsupported delimiter character: @, " + + "t.stg 1:16: unsupported delimiter character: @]"; + String resultErrors = errors.errors.toString(); + assertEquals(expectedErrors, resultErrors); + } + @Test public void testSingleTemplateWithArgs() throws Exception { String templates = "t(a,b) ::= \"[]\"" + Misc.newline;