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

Backslash escape character is always stripped #224

Draft
wants to merge 3 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions java/common/util/Strings.java
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
import java.text.DecimalFormatSymbols;
import java.util.Arrays;
import java.util.Locale;

import static com.vaticle.typeql.lang.common.TypeQLToken.Char.INDENTATION;
import static com.vaticle.typeql.lang.common.TypeQLToken.Char.NEW_LINE;

Expand All @@ -38,6 +39,14 @@ public static String escapeRegex(String regex) {
return regex.replaceAll("/", "\\\\/");
}

public static String unescapeString(String string) {
return string.replaceAll("\\\\(.)", "$1");
}

public static String escapeString(String string) {
return string.replaceAll("(\\\\|\")", "\\\\$1");
}

/**
* @param string a string to quote and escape
* @return a string, surrounded with double quotes and escaped
Expand All @@ -46,6 +55,10 @@ public static String quoteString(String string) {
return "\"" + string + "\"";
}

public static String unquoteString(String string) {
return string.substring(1, string.length() - 1);
}

public static String indent(Object object) {
return indent(object.toString());
}
Expand Down
13 changes: 5 additions & 8 deletions java/parser/Parser.java
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,8 @@
import static com.vaticle.typeql.lang.common.exception.ErrorMessage.ILLEGAL_GRAMMAR;
import static com.vaticle.typeql.lang.common.exception.ErrorMessage.ILLEGAL_STATE;
import static com.vaticle.typeql.lang.common.util.Strings.unescapeRegex;
import static com.vaticle.typeql.lang.common.util.Strings.unescapeString;
import static com.vaticle.typeql.lang.common.util.Strings.unquoteString;
import static com.vaticle.typeql.lang.pattern.variable.UnboundVariable.hidden;
import static java.util.stream.Collectors.toList;
import static org.antlr.v4.runtime.atn.PredictionMode.LL_EXACT_AMBIG_DETECTION;
Expand Down Expand Up @@ -714,7 +716,7 @@ public ThingConstraint.Value<?> visitPredicate(TypeQLParser.PredicateContext ctx
// LITERAL INPUT VALUES ====================================================

public String getRegex(TerminalNode string) {
return unescapeRegex(unquoteString(string));
return unescapeRegex(unescapeString(unquoteString(string.getText())));
}

@Override
Expand Down Expand Up @@ -767,13 +769,8 @@ private String getString(TerminalNode string) {
assert start != null && end != null;
assert start.equals(TypeQLToken.Char.QUOTE_DOUBLE) || start.equals(TypeQLToken.Char.QUOTE_SINGLE);
assert end.equals(TypeQLToken.Char.QUOTE_DOUBLE) || end.equals(TypeQLToken.Char.QUOTE_SINGLE);

// Remove surrounding quotes
return unquoteString(string);
}

private String unquoteString(TerminalNode string) {
return string.getText().substring(1, string.getText().length() - 1);
// Remove surrounding quotes and backslash escapes
return unescapeString(unquoteString(str));
}

private long getLong(TerminalNode number) {
Expand Down
26 changes: 13 additions & 13 deletions java/parser/test/ParserTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
import com.vaticle.typeql.lang.TypeQL;
import com.vaticle.typeql.lang.common.TypeQLArg;
import com.vaticle.typeql.lang.common.exception.TypeQLException;
import com.vaticle.typeql.lang.common.util.Strings;
import com.vaticle.typeql.lang.pattern.Conjunction;
import com.vaticle.typeql.lang.pattern.Pattern;
import com.vaticle.typeql.lang.pattern.variable.ThingVariable;
Expand Down Expand Up @@ -950,17 +951,16 @@ public void testDefineValueTypeQuery() {

@Test
public void testEscapeString() {
// ANTLR will see this as a string that looks like:
// "This has \"double quotes\" and a single-quoted backslash: '\\'"
final String input = "This has \\\"double quotes\\\" and a single-quoted backslash: \\'\\\\\\'";
final String attributeValue = "This has \"double quotes\" and a single-quoted backslash: '\\'";
final String escapedValue = attributeValue.replace("\\", "\\\\").replace("\"", "\\\"");

final String query = "insert\n" +
"$_ isa movie,\n" +
" has title \"" + input + "\";";
" has title \"" + escapedValue + "\";";
TypeQLInsert parsed = TypeQL.parseQuery(query).asInsert();
TypeQLInsert expected = insert(var().isa("movie").has("title", input));
TypeQLInsert built = insert(var().isa("movie").has("title", attributeValue));

assertQueryEquals(expected, parsed, query);
assertQueryEquals(built, parsed, query);
}


Expand Down Expand Up @@ -1272,23 +1272,23 @@ public void whenParsingAggregateWithWrongName_Throw() {
public void defineAttributeTypeRegex() {
final String query = "define\n" +
"digit sub attribute,\n" +
" regex '\\d';";
" regex '\\\\d';";
TypeQLDefine parsed = parseQuery(query);
TypeQLDefine expected = define(type("digit").sub("attribute").regex("\\d"));
assertQueryEquals(expected, parsed, query.replace("'", "\""));
}

@Test
public void undefineAttributeTypeRegex() {
final String query = "undefine\ndigit regex '\\d';";
final String query = "undefine\ndigit regex '\\\\d';";
TypeQLUndefine parsed = parseQuery(query);
TypeQLUndefine expected = undefine(type("digit").regex("\\d"));
assertQueryEquals(expected, parsed, query.replace("'", "\""));
}

@Test
public void regexPredicateParsesCharacterClassesCorrectly() {
final String query = "match\n$x like '\\d';";
final String query = "match\n$x like '\\\\d';";
TypeQLMatch parsed = parseQuery(query);
TypeQLMatch expected = match(var("x").like("\\d"));
assertQueryEquals(expected, parsed, query.replace("'", "\""));
Expand All @@ -1298,29 +1298,29 @@ public void regexPredicateParsesCharacterClassesCorrectly() {
public void regexPredicateParsesQuotesCorrectly() {
final String query = "match\n$x like '\\\"';";
TypeQLMatch parsed = parseQuery(query);
TypeQLMatch expected = match(var("x").like("\\\""));
TypeQLMatch expected = match(var("x").like("\""));
assertQueryEquals(expected, parsed, query.replace("'", "\""));
}

@Test
public void regexPredicateParsesBackslashesCorrectly() {
final String query = "match\n$x like '\\\\';";
TypeQLMatch parsed = parseQuery(query);
TypeQLMatch expected = match(var("x").like("\\\\"));
TypeQLMatch expected = match(var("x").like("\\"));
assertQueryEquals(expected, parsed, query.replace("'", "\""));
}

@Test
public void regexPredicateParsesNewlineCorrectly() {
final String query = "match\n$x like '\\n';";
final String query = "match\n$x like '\\\\n';";
TypeQLMatch parsed = parseQuery(query);
TypeQLMatch expected = match(var("x").like("\\n"));
assertQueryEquals(expected, parsed, query.replace("'", "\""));
}

@Test
public void regexPredicateParsesForwardSlashesCorrectly() {
final String query = "match\n$x like '\\/';";
final String query = "match\n$x like '\\\\/';";
TypeQLMatch parsed = parseQuery(query);
TypeQLMatch expected = match(var("x").like("/"));
assertQueryEquals(expected, parsed, query.replace("'", "\""));
Expand Down
7 changes: 4 additions & 3 deletions java/pattern/constraint/ThingConstraint.java
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@
import static com.vaticle.typeql.lang.common.exception.ErrorMessage.MISSING_CONSTRAINT_RELATION_PLAYER;
import static com.vaticle.typeql.lang.common.exception.ErrorMessage.MISSING_CONSTRAINT_VALUE;
import static com.vaticle.typeql.lang.common.util.Strings.escapeRegex;
import static com.vaticle.typeql.lang.common.util.Strings.escapeString;
import static com.vaticle.typeql.lang.common.util.Strings.quoteString;
import static com.vaticle.typeql.lang.pattern.variable.UnboundVariable.hidden;

Expand Down Expand Up @@ -650,11 +651,11 @@ public java.lang.String toString() {
StringBuilder operation = new StringBuilder();

if (predicate().equals(LIKE)) {
operation.append(LIKE).append(SPACE).append(quoteString(escapeRegex(value())));
operation.append(LIKE).append(SPACE).append(quoteString(escapeString(escapeRegex(value()))));
} else if (predicate().equals(EQ)) {
operation.append(quoteString(value()));
operation.append(quoteString(escapeString(value())));
} else {
operation.append(predicate()).append(SPACE).append(quoteString(value()));
operation.append(predicate()).append(SPACE).append(quoteString(escapeString(value())));
}

return operation.toString();
Expand Down
3 changes: 2 additions & 1 deletion java/pattern/constraint/TypeConstraint.java
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@
import static com.vaticle.typeql.lang.common.exception.ErrorMessage.INVALID_CASTING;
import static com.vaticle.typeql.lang.common.exception.ErrorMessage.MISSING_PATTERNS;
import static com.vaticle.typeql.lang.common.util.Strings.escapeRegex;
import static com.vaticle.typeql.lang.common.util.Strings.escapeString;
import static com.vaticle.typeql.lang.common.util.Strings.quoteString;
import static com.vaticle.typeql.lang.pattern.variable.UnboundVariable.hidden;

Expand Down Expand Up @@ -380,7 +381,7 @@ public TypeConstraint.Regex asRegex() {

@Override
public String toString() {
return REGEX.toString() + SPACE + quoteString(escapeRegex(regex().pattern()));
return REGEX.toString() + SPACE + quoteString(escapeString(escapeRegex(regex().pattern())));
}

@Override
Expand Down
2 changes: 1 addition & 1 deletion java/query/test/TypeQLQueryTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,7 @@ public void testInsertQueryToString() {
@Test
public void testEscapeStrings() {
assertEquals("insert\n$x \"hello\nworld\";", TypeQL.insert(var("x").eq("hello\nworld")).toString());
assertEquals("insert\n$x \"hello\\nworld\";", TypeQL.insert(var("x").eq("hello\\nworld")).toString());
assertEquals("insert\n$x \"hello\\\\nworld\";", TypeQL.insert(var("x").eq("hello\\nworld")).toString());
}

@Test
Expand Down