Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Added full set of escape characters

  • Loading branch information...
commit 5eb1af5b057ff8af2e48a312da54f2968282aaf3 1 parent 0c92569
@systay authored
View
4 cypher/CHANGES.txt
@@ -1,3 +1,7 @@
+1.9.M01
+--------------------
+o Added all the escape characters that Java uses
+
1.8.M06 (2012-07-06)
--------------------
o Fixes problem when graph elements are deleted multiple times in the same query
View
18 cypher/src/docs/dev/expressions.txt
@@ -16,3 +16,21 @@ An expression in Cypher can be:
* An aggregate function -- `avg(x.prop)`, `count(*)`
* Relationship types -- `:REL_TYPE`, +:\`REL TYPE`+, `:REL1|REL2`
* A path-pattern: `a-->()<--b`
+
+= Note on string literals =
+Strings can contain escape sequences such as the ones defined for Java:
+
+[options="header", cols=">,<", width="50%"]
+|===================
+|Escape seq|Character
+|`\t`|Tab
+|`\b`|Backspace
+|`\n`|Newline
+|`\r`|Carriage return
+|`\f`|Form feed
+|`\'`|Single quote
+|`\"`|Double quote
+|`\\`|Backslash
+|===================
+
+
View
2  cypher/src/main/scala/org/neo4j/cypher/CypherParser.scala
@@ -41,7 +41,7 @@ class CypherParser(version: String) {
v match {
case "1.7" => v17.parse(q)
case "1.8" => v18.parse(q)
- case "1.9" => v18.parse(q)
+ case "1.9" => v19.parse(q)
case _ => throw new SyntaxException("Versions supported are 1.7, 1.8 and 1.9")
}
}
View
2  cypher/src/main/scala/org/neo4j/cypher/internal/commands/ReturnItem.scala
@@ -41,7 +41,7 @@ case class ReturnItem(expression: Expression, name: String, renamed: Boolean = f
extends ReturnColumn {
def expressions(symbols: SymbolTable) = Map(name -> expression)
- override def toString = name
+ override def toString = expression + " as `"+ name + "`"
def rename(newName: String) = ReturnItem(expression, newName, renamed = true)
}
View
2  cypher/src/main/scala/org/neo4j/cypher/internal/commands/expressions/Literal.scala
@@ -37,4 +37,6 @@ case class Literal(v: Any) extends Expression {
def calculateType(symbols: SymbolTable): CypherType = CypherType.fromJava(v)
def symbolTableDependencies = Set()
+
+ override def toString() = "Literal(" + v.toString + ")"
}
View
48 cypher/src/main/scala/org/neo4j/cypher/internal/parser/v1_9/Expressions.scala
@@ -83,21 +83,35 @@ trait Expressions extends Base with ParserPattern with Predicates {
var result: Option[ParseResult[Expression]] = None
while (!ls.isEmpty && result.isEmpty) {
- val (pref, suf) = ls span { c => c != '\\' && c != startChar }
+ val (pref, suf) = ls span {
+ c => c != '\\' && c != startChar
+ }
idx += pref.length
sb ++= pref
- if (suf.isEmpty)
+ if (suf.isEmpty) {
result = Some(Failure("end of string missing", in))
+ } else {
- val first: Char = suf(0)
- first match {
- case c if c == startChar =>
+ val first: Char = suf(0)
+ if (first == startChar) {
result = Some(Success(Literal(sb.result()), in.drop(idx - in.offset + 2)))
- case '\\' if suf(1) == '\''||suf(1)=='\"' =>
- sb.append(suf(1))
- idx += 2
- ls = suf.drop(2)
+ } else {
+ val (escChars, afterEscape) = suf.splitAt(2)
+
+ if (escChars.size == 1) {
+ result = Some(Failure("invalid escape sequence", in))
+ } else {
+
+ ls = afterEscape
+ idx += 2
+
+ parseEscapeChars(escChars.tail, in) match {
+ case Left(c) => sb.append(c)
+ case Right(failure) => result = Some(failure)
+ }
+ }
+ }
}
}
@@ -108,6 +122,22 @@ trait Expressions extends Base with ParserPattern with Predicates {
}
}
+ case class EscapeProduct(result: Option[ParseResult[Expression]])
+
+ private def parseEscapeChars(suf: List[Char], in:Input): Either[Char, Failure] = suf match {
+ case '\\' :: tail => Left('\\')
+ case 'a' :: tail => Left('\u0007')
+ case 'b' :: tail => Left('\b')
+ case 'f' :: tail => Left('\f')
+ case 'n' :: tail => Left('\n')
+ case 'r' :: tail => Left('\r')
+ case 't' :: tail => Left('\t')
+ case 'v' :: tail => Left('\u000b')
+ case '\'' :: tail => Left('\'')
+ case '"' :: tail => Left('"')
+ case _ => Right(Failure("invalid escape sequence", in))
+ }
+
def numberLiteral: Parser[Expression] = number ^^ (x => {
val value: Any = if (x.contains("."))
x.toDouble
View
21 cypher/src/test/scala/org/neo4j/cypher/CypherParserTest.scala
@@ -45,13 +45,20 @@ class CypherParserTest extends JUnitSuite with Assertions {
returns(ReturnItem(Literal("apa"), "\"apa\"")))
}
- @Test def should_return_string_literal_with_escaped_quote_in() {
+ @Test def should_return_string_literal_with_escaped_quote_in1_8() {
testFrom_1_8("start s = node(1) return \"ap\\\"a\"",
Query.
start(NodeById("s", 1)).
returns(ReturnItem(Literal("ap\"a"), "\"ap\\\"a\"")))
}
+ @Test def should_return_string_literal_with_escaped_quote_in() {
+ testFrom_1_9("start s = node(1) return \"a\\tp\\\"a\"",
+ Query.
+ start(NodeById("s", 1)).
+ returns(ReturnItem(Literal("a\tp\"a"), "\"a\\tp\\\"a\"")))
+ }
+
@Test def allTheNodes() {
testAll("start s = NODE(*) return s",
Query.
@@ -1794,6 +1801,10 @@ foreach(x in [1,2,3] :
test_1_9(query, expectedQuery)
}
+ def testFrom_1_9(query: String, expectedQuery: Query) {
+ test_1_9(query, expectedQuery)
+ }
+
def testAll(query: String, expectedQuery: Query) {
test_1_7(query, expectedQuery)
test_1_8(query, expectedQuery)
@@ -1804,6 +1815,14 @@ foreach(x in [1,2,3] :
test_1_7(queryText, queryAst)
}
+ @Test
+ def special_should_not_exists() {
+ val parser = new CypherParser()
+ val v18 = parser.parse("cypher 1.8 start s = node(1) return \"ap\\\"a\"")
+ val v19 = parser.parse("cypher 1.9 start s = node(1) return \"ap\\\"a\"")
+ assertEquals(v18, v19)
+ }
+
def testQuery(version: Option[String], query: String, expectedQuery: Query) {
val parser = new CypherParser()
Please sign in to comment.
Something went wrong with that request. Please try again.