Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix quote parsing #3762

Merged
merged 11 commits into from
May 5, 2021
6 changes: 5 additions & 1 deletion src/main/java/ch/njol/skript/lang/SkriptParser.java
Original file line number Diff line number Diff line change
Expand Up @@ -1351,11 +1351,15 @@ static int countUnescaped(final String pattern, final char c, final int start, f
* @return Index of the end quote
*/
private static int nextQuote(final String s, final int from) {
boolean inExpression = false;
for (int i = from; i < s.length(); i++) {
if (s.charAt(i) == '"') {
char c = s.charAt(i);
if (c == '"' && !inExpression) {
if (i == s.length() - 1 || s.charAt(i + 1) != '"')
return i;
i++;
} else if (c == '%') {
inExpression = !inExpression;
}
}
return -1;
Expand Down
47 changes: 39 additions & 8 deletions src/main/java/ch/njol/skript/lang/VariableString.java
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,7 @@ public static VariableString newInstance(String s) {

/**
* Tests whether a string is correctly quoted, i.e. only has doubled double quotes in it.
* Singular double quotes are only allowed between percentage signs.
*
* @param s The string
* @param withQuotes Whether s must be surrounded by double quotes or not
Expand All @@ -156,12 +157,22 @@ public static boolean isQuotedCorrectly(String s, boolean withQuotes) {
if (withQuotes && (!s.startsWith("\"") || !s.endsWith("\"")))
return false;
boolean quote = false;
boolean percentage = false;
for (int i = withQuotes ? 1 : 0; i < (withQuotes ? s.length() - 1 : s.length()); i++) {
if (s.charAt(i) != '"') {
if (quote)
return false;
} else {
if (percentage) {
if (s.charAt(i) == '%')
percentage = false;

continue;
}

if (quote && s.charAt(i) != '"')
return false;

if (s.charAt(i) == '"') {
quote = !quote;
} else if (s.charAt(i) == '%') {
percentage = true;
}
}
return !quote;
Expand Down Expand Up @@ -190,7 +201,7 @@ public static String unquote(String s, boolean surroundingQuotes) {
*/
@Nullable
public static VariableString newInstance(String orig, StringMode mode) {
if (!isQuotedCorrectly(orig, false))
if (mode != StringMode.VARIABLE_NAME && !isQuotedCorrectly(orig, false))
return null;
int n = StringUtils.count(orig, '%');
if (n % 2 != 0) {
Expand All @@ -199,7 +210,26 @@ public static VariableString newInstance(String orig, StringMode mode) {
}

// We must not parse color codes yet, as JSON support would be broken :(
String s = orig.replace("\"\"", "\"");
String s;
if (mode != StringMode.VARIABLE_NAME) {
// Replace every double " character with a single ", except for those in expressions (between %)
StringBuilder stringBuilder = new StringBuilder();

boolean expression = false;
for (int i = 0; i < orig.length(); i++) {
char c = orig.charAt(i);
stringBuilder.append(c);

if (c == '%')
expression = !expression;

if (!expression && c == '"')
i++;
}
s = stringBuilder.toString();
} else {
s = orig;
}

List<Object> string = new ArrayList<>(n / 2 + 2); // List of strings and expressions

Expand Down Expand Up @@ -275,7 +305,8 @@ public static VariableString newInstance(String orig, StringMode mode) {
Object[] sa = string.toArray();
if (string.size() == 1 && string.get(0) instanceof Expression &&
((Expression<?>) string.get(0)).getReturnType() == String.class &&
((Expression<?>) string.get(0)).isSingle()) {
((Expression<?>) string.get(0)).isSingle() &&
mode == StringMode.MESSAGE) {
String expr = ((Expression<?>) string.get(0)).toString(null, false);
Skript.warning(expr + " is already a text, so you should not put it in one (e.g. " + expr + " instead of " + "\"%" + expr.replace("\"", "\"\"") + "%\")");
}
Expand Down Expand Up @@ -680,7 +711,7 @@ public void change(Event e, @Nullable Object[] delta, ChangeMode mode) throws Un

@Override
public boolean getAnd() {
return false;
return true;
}

@Override
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
function myFunction_five_nine_zero(s: string) :: string:
return {_s}

test "double quote parsing":
assert "Testing" is set with "simple string failed"

assert "Testing %1 + 1%" is set with "simple string with expression failed"

assert "Testing """ is set with "simple string with escaped quote failed"

assert "Testing %length of "abc"%" is set with "string with expression with string ##1 failed"
assert "%myFunction_five_nine_zero("Hello")% world" is "Hello world" with "string with expression with string ##2 failed"


assert {_abc} is not set with "simple variable failed"
assert {_abc%%} is not set with "simple variable with escaped percentage sign failed"
assert {_abc%1 + 1%} is not set with "simple variable with expression failed"

assert {_%subtext of "test" from characters 1 to 1%} is not set with "variable with expression with string failed"
4 changes: 2 additions & 2 deletions src/test/skript/tests/syntaxes/expressions/ExprEntities.sk
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
test "entities in chunk":
spawn 10 sheep at spawn of world "world"
wait 1 tick
assert size of all entities in chunk at spawn of world "world" >= 10 with "Size of all entities in spawn chunk is not > 10: %size of all entities in chunk at spawn of world ""world""%"
assert size of all entities in chunk at spawn of world "world" >= 10 with "Size of all entities in spawn chunk is not > 10: %size of all entities in chunk at spawn of world "world"%"
delete all entities in chunk at spawn of world "world"
assert size of all entities in chunk at spawn of world "world" = 0 with "Size of all entities in spawn chunk != 0: %size of all entities in chunk at spawn of world ""world""%"
assert size of all entities in chunk at spawn of world "world" = 0 with "Size of all entities in spawn chunk != 0: %size of all entities in chunk at spawn of world "world"%"