summaryrefslogtreecommitdiff
path: root/chapter01
diff options
context:
space:
mode:
authorThalia Archibald <thalia@archibald.dev>2024-06-21 12:58:51 -0700
committerGitHub <noreply@github.com>2024-06-21 20:58:51 +0100
commitfd8442075faa603b3956d51d81486424624c5355 (patch)
tree029c1afb736ff0e350c2b032318e5fc376c7e1e4 /chapter01
parente5f4f7b85fbb91a293a7d89d7b85adfbaeeb51f4 (diff)
downloadSimple-fd8442075faa603b3956d51d81486424624c5355.tar.gz
Simple-fd8442075faa603b3956d51d81486424624c5355.zip
Backport tests (#87)
* Fix unexpected number parse errors * Add chapter titles * Split test files by chapter * Backport rename Fuzzer08Test to FuzzerWrap * Replace graph build scripts with Makefile * Backport Chapter05Test.testRegress1 * Backport require space after 'return' * Backport check EOF after return statement
Diffstat (limited to 'chapter01')
-rw-r--r--chapter01/README.md2
-rw-r--r--chapter01/docs/Makefile12
-rw-r--r--chapter01/src/main/java/com/seaofnodes/simple/Parser.java39
-rw-r--r--chapter01/src/test/java/com/seaofnodes/simple/Chapter01Test.java23
4 files changed, 68 insertions, 8 deletions
diff --git a/chapter01/README.md b/chapter01/README.md
index d4882f8a..e7ea73bc 100644
--- a/chapter01/README.md
+++ b/chapter01/README.md
@@ -1,4 +1,4 @@
-# Chapter 1
+# Chapter 1: Introduction
In this chapter we aim to compile simple scripts such as:
diff --git a/chapter01/docs/Makefile b/chapter01/docs/Makefile
new file mode 100644
index 00000000..12659b8f
--- /dev/null
+++ b/chapter01/docs/Makefile
@@ -0,0 +1,12 @@
+GV := $(wildcard *.gv)
+SVG := $(patsubst %.gv,%.svg,$(GV))
+
+.PHONY: all
+all: $(SVG)
+
+%.svg: %.gv
+ dot -Tsvg $< > $@
+
+.PHONY: clean
+clean:
+ @rm -f $(SVG)
diff --git a/chapter01/src/main/java/com/seaofnodes/simple/Parser.java b/chapter01/src/main/java/com/seaofnodes/simple/Parser.java
index d79d23ff..8975a165 100644
--- a/chapter01/src/main/java/com/seaofnodes/simple/Parser.java
+++ b/chapter01/src/main/java/com/seaofnodes/simple/Parser.java
@@ -31,8 +31,22 @@ public class Parser {
}
public ReturnNode parse() {
- require("return");
- return parseReturn();
+ var ret = (ReturnNode) parseStatement();
+ if (!_lexer.isEOF()) throw error("Syntax error, unexpected " + _lexer.getAnyNextToken());
+ return ret;
+ }
+
+ /**
+ * Parses a statement
+ *
+ * <pre>
+ * returnStatement
+ * </pre>
+ * @return a {@link Node} or {@code null}
+ */
+ private Node parseStatement() {
+ if (matchx("return")) return parseReturn();
+ throw errorSyntax("a statement");
}
/**
@@ -89,7 +103,9 @@ public class Parser {
// Utilities for lexical analysis
// Return true and skip if "syntax" is next in the stream.
- private boolean match(String syntax) { return _lexer.match(syntax); }
+ private boolean match (String syntax) { return _lexer.match (syntax); }
+ // Match must be "exact", not be followed by more id letters
+ private boolean matchx(String syntax) { return _lexer.matchx(syntax); }
// Require an exact match
private void require(String syntax) { require(null, syntax); }
@@ -174,10 +190,18 @@ public class Parser {
return true;
}
+ boolean matchx(String syntax) {
+ if( !match(syntax) ) return false;
+ if( !isIdLetter(peek()) ) return true;
+ _position -= syntax.length();
+ return false;
+ }
+
// Used for errors
String getAnyNextToken() {
if (isEOF()) return "";
if (isIdStart(peek())) return parseId();
+ if (isNumber(peek())) return parseNumberString();
if (isPunctuation(peek())) return parsePunctuation();
return String.valueOf(peek());
}
@@ -186,13 +210,16 @@ public class Parser {
boolean isNumber(char ch) {return Character.isDigit(ch);}
private long parseNumber() {
- int start = _position;
- while (isNumber(nextChar())) ;
- String snum = new String(_input, start, --_position - start);
+ String snum = parseNumberString();
if (snum.length() > 1 && snum.charAt(0) == '0')
throw error("Syntax error: integer values cannot start with '0'");
return Long.parseLong(snum);
}
+ private String parseNumberString() {
+ int start = _position;
+ while (isNumber(nextChar())) ;
+ return new String(_input, start, --_position - start);
+ }
// First letter of an identifier
private boolean isIdStart(char ch) {
diff --git a/chapter01/src/test/java/com/seaofnodes/simple/Chapter01Test.java b/chapter01/src/test/java/com/seaofnodes/simple/Chapter01Test.java
index 738ed702..6989d0b9 100644
--- a/chapter01/src/test/java/com/seaofnodes/simple/Chapter01Test.java
+++ b/chapter01/src/test/java/com/seaofnodes/simple/Chapter01Test.java
@@ -44,7 +44,7 @@ public class Chapter01Test {
new Parser("ret").parse();
fail();
} catch( RuntimeException e ) {
- assertEquals("Syntax error, expected return: ret",e.getMessage());
+ assertEquals("Syntax error, expected a statement: ret",e.getMessage());
}
}
@@ -82,4 +82,25 @@ public class Chapter01Test {
exceptionRule.expectMessage("Syntax error, expected integer literal");
new Parser("return -100;").parse();
}
+
+ @Test
+ public void testBad6() {
+ try {
+ new Parser("return100").parse();
+ fail();
+ } catch( RuntimeException e ) {
+ assertEquals("Syntax error, expected a statement: return100",e.getMessage());
+ }
+ }
+
+ @Test
+ public void testBad7() {
+ try {
+ new Parser("return 1;}").parse();
+ fail();
+ } catch( RuntimeException e ) {
+ assertEquals("Syntax error, unexpected }",e.getMessage());
+ }
+ }
+
}