Parser: Support '' and "" escapes in character and string literals.
M CHANGES.md +2 -0
@@ 7,6 7,8 @@ Glass 0.6 — ????-??-??
   * The `$` prefix can now be used for hexadecimal numbers.
   * Symbols can no longer start with the `$` character (bc).
   * The `>>>` unsigned shift right operator is now supported.
+  * The `'` character can now be written as `''` in character literals.
+  * The `"` character can now be written as `""` in string literals.
 
 Glass 0.5 — 2017-01-18
 ----------------------

          
M README.md +3 -0
@@ 303,6 303,9 @@ Literals
   * Character: `'c'`
   * String: `"abc"`
 
+Character literals can contain the `'` character by repeating it as `''`, and
+string literals can contain the `"` character by repeating it as `""`.
+
 Character and string literals support the following escape sequences:
 
   * `\0` (NUL)

          
M src/main/java/nl/grauw/glass/Parser.java +31 -5
@@ 253,9 253,7 @@ public class Parser {
 	private class ArgumentStringState extends State {
 		public State parse(char character) {
 			if (character == '"') {
-				expressionBuilder.addValueToken(new StringLiteral(accumulator.toString()));
-				accumulator.setLength(0);
-				return argumentOperatorState;
+				return argumentStringDoubleQuoteState;
 			} else if (character == '\\') {
 				return argumentStringEscapeState;
 			} else if (character == '\n' || character == '\0') {

          
@@ 267,6 265,20 @@ public class Parser {
 		}
 	}
 
+	private ArgumentStringDoubleQuoteState argumentStringDoubleQuoteState = new ArgumentStringDoubleQuoteState();
+	private class ArgumentStringDoubleQuoteState extends State {
+		public State parse(char character) {
+			if (character == '"') {
+				accumulator.append(character);
+				return argumentStringState;
+			} else {
+				expressionBuilder.addValueToken(new StringLiteral(accumulator.toString()));
+				accumulator.setLength(0);
+				return argumentOperatorState.parse(character);
+			}
+		}
+	}
+
 	private ArgumentStringEscapeState argumentStringEscapeState = new ArgumentStringEscapeState();
 	private class ArgumentStringEscapeState extends State {
 		public State parse(char character) {

          
@@ 311,9 323,11 @@ public class Parser {
 	private ArgumentCharacterState argumentCharacterState = new ArgumentCharacterState();
 	private class ArgumentCharacterState extends State {
 		public State parse(char character) {
-			if (character == '\\') {
+			if (character == '\'') {
+				return argumentCharacterDoubleQuoteState;
+			} else if (character == '\\') {
 				return argumentCharacterEscapeState;
-			} else if (character == '\'' || character == '\n' || character == '\0') {
+			} else if (character == '\n' || character == '\0') {
 				throw new SyntaxError();
 			} else {
 				accumulator.append(character);

          
@@ 322,6 336,18 @@ public class Parser {
 		}
 	}
 
+	private ArgumentCharacterDoubleQuoteState argumentCharacterDoubleQuoteState = new ArgumentCharacterDoubleQuoteState();
+	private class ArgumentCharacterDoubleQuoteState extends State {
+		public State parse(char character) {
+			if (character == '\'') {
+				accumulator.append(character);
+				return argumentCharacterEndState;
+			} else {
+				throw new SyntaxError();
+			}
+		}
+	}
+
 	private ArgumentCharacterEscapeState argumentCharacterEscapeState = new ArgumentCharacterEscapeState();
 	private class ArgumentCharacterEscapeState extends State {
 		public State parse(char character) {

          
M src/test/java/nl/grauw/glass/ParserTest.java +11 -1
@@ 116,6 116,11 @@ public class ParserTest extends TestBase
 	}
 
 	@Test
+	public void testCharacterLiteralDoubleQuote() {
+		assertEquals('\'', ((CharacterLiteral)parseExpression("''''")).getCharacter());
+	}
+
+	@Test
 	public void testCharacterLiteralEscape() {
 		assertEquals('\0', ((CharacterLiteral)parseExpression("'\\0'")).getCharacter());
 		assertEquals('\7', ((CharacterLiteral)parseExpression("'\\a'")).getCharacter());

          
@@ 138,7 143,7 @@ public class ParserTest extends TestBase
 
 	@Test
 	public void testCharacterLiteralTooShort() {
-		assertSyntaxError(0, 1, 1, () -> {
+		assertSyntaxError(0, 1, 2, () -> {
 			parseExpression("''");
 		});
 	}

          
@@ 163,6 168,11 @@ public class ParserTest extends TestBase
 	}
 
 	@Test
+	public void testStringLiteralDoubleQuote() {
+		assertEquals("x\"z", ((StringLiteral)parseExpression("\"x\"\"z\"")).getString());
+	}
+
+	@Test
 	public void testStringLiteralEscape() {
 		assertEquals("x\0z", ((StringLiteral)parseExpression("\"x\\0z\"")).getString());
 		assertEquals("x\7z", ((StringLiteral)parseExpression("\"x\\az\"")).getString());