diff options
| author | Yichen Yan <oraluben@outlook.com> | 2023-11-01 22:08:30 +0800 |
|---|---|---|
| committer | Dibyendu Majumdar <dibyendumajumdar@users.noreply.github.com> | 2023-11-01 18:56:50 +0000 |
| commit | d3ffe6f9a935888233283962ce077ec25a01bec9 (patch) | |
| tree | d93ccfbdb034562d2fd17e4c2151cf5cddbe64e9 | |
| parent | 3cad2b3c0af2418d51bb1ee46b1e24c5b362ba63 (diff) | |
| download | Simple-d3ffe6f9a935888233283962ce077ec25a01bec9.tar.gz Simple-d3ffe6f9a935888233283962ce077ec25a01bec9.zip | |
Bail out unclosed block (#25)
* Report unclosed block in parsing
* Fix tests
* Resolve comments and rebase onto chapter5
* Test `isEOF` after consume whitespaces
6 files changed, 63 insertions, 15 deletions
diff --git a/chapter03/src/main/java/com/seaofnodes/simple/Parser.java b/chapter03/src/main/java/com/seaofnodes/simple/Parser.java index 12e156ed..3d06a9c3 100644 --- a/chapter03/src/main/java/com/seaofnodes/simple/Parser.java +++ b/chapter03/src/main/java/com/seaofnodes/simple/Parser.java @@ -39,7 +39,7 @@ public class Parser { } public ReturnNode parse() { - return (ReturnNode) parseBlock(); + return (ReturnNode) parseBlock(false); } @@ -50,14 +50,22 @@ public class Parser { * '{' statements '}' * </pre> */ - private Node parseBlock() { + private Node parseBlock(boolean requireClosingBracket) { // Enter a new scope _scope.push(); Node n = null; - while (!match("}") && !_lexer.isEOF()) { + do { + if (match("}")) { + if (requireClosingBracket) break; + else throw error("unexpected '}' token"); + } else if (_lexer.isEOF()) { + if (requireClosingBracket) require("}"); + else break; + } + Node n0 = parseStatement(); if (n0 != null) n = n0; // Allow null returns from eg showGraph - } + } while (true); // Exit scope _scope.pop(); return n; @@ -73,7 +81,7 @@ public class Parser { private Node parseStatement() { if (match("return")) return parseReturn(); else if (match("int")) return parseDecl(); - else if (match("{")) return parseBlock(); + else if (match("{")) return parseBlock(true); else if (match("#showGraph")) return showGraph(); else return parseExpressionStatement(); } diff --git a/chapter03/src/test/java/com/seaofnodes/simple/Chapter03Test.java b/chapter03/src/test/java/com/seaofnodes/simple/Chapter03Test.java index 5af07ec3..21faacac 100644 --- a/chapter03/src/test/java/com/seaofnodes/simple/Chapter03Test.java +++ b/chapter03/src/test/java/com/seaofnodes/simple/Chapter03Test.java @@ -168,4 +168,11 @@ public class Chapter03Test { // this test used to fail in chapter 1 assertEquals("return -100;", new Parser("return -100;").parse().print()); } + + @Test + public void testBad6() { + exceptionRule.expect(RuntimeException.class); + exceptionRule.expectMessage("Syntax error, expected }:"); + new Parser("int a=1; int b=2; int c=0; { int b=3; c=a+b;").parse(); + } } diff --git a/chapter04/src/main/java/com/seaofnodes/simple/Parser.java b/chapter04/src/main/java/com/seaofnodes/simple/Parser.java index 147515f6..275a4ebd 100644 --- a/chapter04/src/main/java/com/seaofnodes/simple/Parser.java +++ b/chapter04/src/main/java/com/seaofnodes/simple/Parser.java @@ -52,7 +52,7 @@ public class Parser { try { _scope.define("$ctrl", new ProjNode(START, 0, "$ctrl").peephole()); _scope.define("arg" , new ProjNode(START, 1, "arg" ).peephole()); - return (ReturnNode) parseBlock(); + return (ReturnNode) parseBlock(false); } finally { _scope.pop(); @@ -66,14 +66,22 @@ public class Parser { * '{' statements '}' * </pre> */ - private Node parseBlock() { + private Node parseBlock(boolean requireClosingBracket) { // Enter a new scope _scope.push(); Node n = null; - while (!match("}") && !_lexer.isEOF()) { + do { + if (match("}")) { + if (requireClosingBracket) break; + else throw error("unexpected '}' token"); + } else if (_lexer.isEOF()) { + if (requireClosingBracket) require("}"); + else break; + } + Node n0 = parseStatement(); if (n0 != null) n = n0; // Allow null returns from eg showGraph - } + } while (true); // Exit scope _scope.pop(); return n; @@ -89,7 +97,7 @@ public class Parser { private Node parseStatement() { if (match("return")) return parseReturn(); else if (match("int")) return parseDecl(); - else if (match("{")) return parseBlock(); + else if (match("{")) return parseBlock(true); else if (match("#showGraph")) return showGraph(); else return parseExpressionStatement(); } diff --git a/chapter04/src/test/java/com/seaofnodes/simple/Chapter04Test.java b/chapter04/src/test/java/com/seaofnodes/simple/Chapter04Test.java index 4782e568..ed999039 100644 --- a/chapter04/src/test/java/com/seaofnodes/simple/Chapter04Test.java +++ b/chapter04/src/test/java/com/seaofnodes/simple/Chapter04Test.java @@ -277,4 +277,11 @@ public class Chapter04Test { // this test used to fail in chapter 1 assertEquals("return -100;", new Parser("return -100;").parse().print()); } + + @Test + public void testBad6() { + exceptionRule.expect(RuntimeException.class); + exceptionRule.expectMessage("Syntax error, expected }:"); + new Parser("int a=1; int b=2; int c=0; { int b=3; c=a+b;").parse(); + } } diff --git a/chapter05/src/main/java/com/seaofnodes/simple/Parser.java b/chapter05/src/main/java/com/seaofnodes/simple/Parser.java index 79b50672..a9bec582 100644 --- a/chapter05/src/main/java/com/seaofnodes/simple/Parser.java +++ b/chapter05/src/main/java/com/seaofnodes/simple/Parser.java @@ -93,7 +93,7 @@ public class Parser { try { _scope.define(CTRL , new ProjNode(START, 0, CTRL ).peephole()); _scope.define("arg", new ProjNode(START, 1, "arg").peephole()); - parseBlock(); + parseBlock(false); return STOP; } finally { @@ -111,14 +111,22 @@ public class Parser { * </pre> * @return a {@link Node} or {@code null} */ - private Node parseBlock() { + private Node parseBlock(boolean requireClosingBracket) { // Enter a new scope _scope.push(); Node n = null; - while (!match("}") && !_lexer.isEOF()) { + do { + if (match("}")) { + if (requireClosingBracket) break; + else throw error("unexpected '}' token"); + } else if (_lexer.isEOF()) { + if (requireClosingBracket) require("}"); + else break; + } + Node n0 = parseStatement(); if (n0 != null) n = n0; // Allow null returns from eg showGraph - } + } while (true); // Exit scope _scope.pop(); return n; @@ -135,7 +143,7 @@ public class Parser { private Node parseStatement() { if (matchx("return") ) return parseReturn(); else if (matchx("int")) return parseDecl(); - else if (match ("{" )) return parseBlock(); + else if (match ("{" )) return parseBlock(true); else if (matchx("if" )) return parseIf(); else if (matchx("#showGraph")) return require(showGraph(),";"); else return parseExpressionStatement(); diff --git a/chapter05/src/test/java/com/seaofnodes/simple/Chapter05Test.java b/chapter05/src/test/java/com/seaofnodes/simple/Chapter05Test.java index 5ac309f3..0516fb73 100644 --- a/chapter05/src/test/java/com/seaofnodes/simple/Chapter05Test.java +++ b/chapter05/src/test/java/com/seaofnodes/simple/Chapter05Test.java @@ -430,4 +430,14 @@ return c; // this test used to fail in chapter 1 assertEquals("return -100;", new Parser("return -100;").parse().print()); } + + @Test + public void testBad6() { + try { + new Parser("int a=1; int b=2; int c=0; { int b=3; c=a+b;").parse(); + fail(); + } catch( RuntimeException e ) { + assertEquals("Syntax error, expected }: ",e.getMessage()); + } + } } |
