diff options
| author | Cliff Click <cliffc@acm.org> | 2023-10-29 09:32:35 -0700 |
|---|---|---|
| committer | Dibyendu Majumdar <dibyendumajumdar@users.noreply.github.com> | 2023-11-01 18:56:50 +0000 |
| commit | ccd83187b172f8ba3cd7c49b59a080fb027a184b (patch) | |
| tree | 87fb77fb0629d6db2de5560970f8b480ae57ecfc | |
| parent | 0a669bf5fa11a63516ea8f5316b0079c2eee4bde (diff) | |
| download | Simple-ccd83187b172f8ba3cd7c49b59a080fb027a184b.tar.gz Simple-ccd83187b172f8ba3cd7c49b59a080fb027a184b.zip | |
Fix bugs using keywords as ids
Add an exact 'matchx' for keywords. Add a list of keywords.
int true=0; return true; // Expected id, found 'true'
int else=arg; if(else) else=2; else else=1; return else; // Expected id, found 'else'
inta=1; return a; // undefined inta
3 files changed, 71 insertions, 19 deletions
diff --git a/chapter05/src/main/java/com/seaofnodes/simple/Parser.java b/chapter05/src/main/java/com/seaofnodes/simple/Parser.java index a23f8d70..7ea74306 100644 --- a/chapter05/src/main/java/com/seaofnodes/simple/Parser.java +++ b/chapter05/src/main/java/com/seaofnodes/simple/Parser.java @@ -49,6 +49,19 @@ public class Parser { public ScopeNode _scope; /** + * List of keywords disallowed as identifiers + */ + private final HashSet<String> KEYWORDS = new HashSet<>(){{ + add("else"); + add("false"); + add("if"); + add("return"); + add("then"); + add("true"); + }}; + + + /** * We clone ScopeNodes when control flows branch; it is useful to have * a list of all active ScopeNodes for purposes of visualization of the SoN graph */ @@ -116,11 +129,11 @@ public class Parser { * </pre> */ private Node parseStatement() { - if (match("return") ) return parseReturn(); - else if (match("int")) return parseDecl(); - else if (match("{" )) return parseBlock(); - else if (match("if" )) return parseIf(); - else if (match("#showGraph")) return showGraph(); + if (matchx("return") ) return parseReturn(); + else if (matchx("int")) return parseDecl(); + else if (match ("{" )) return parseBlock(); + else if (matchx("if" )) return parseIf(); + else if (matchx("#showGraph")) return showGraph(); else return parseExpressionStatement(); } @@ -147,7 +160,7 @@ public class Parser { // Parse the false side _scope = fScope; // Restore scope, then parse else block if any ctrl(ifF); // Ctrl token is now set to ifFalse projection - if (match("else")) parseStatement(); + if (matchx("else")) parseStatement(); if( tScope.nIns() != ndefs || fScope.nIns() != ndefs ) throw error("Cannot define a new name on one arm of an if"); @@ -286,13 +299,13 @@ public class Parser { private Node parsePrimary() { if( _lexer.isNumber() ) return parseIntegerLiteral(); if( match("(") ) return require(parseExpression(), ")"); - if( match("true" ) ) return new ConstantNode(TypeInteger.constant(1)).peephole(); - if( match("false") ) return new ConstantNode(TypeInteger.constant(0)).peephole(); - String name = requireId(); - Node id = _scope.lookup(name); - if( id==null ) - throw error("Undefined name '" + name + "'"); - return id; + if( matchx("true" ) ) return new ConstantNode(TypeInteger.constant(1)).peephole(); + if( matchx("false") ) return new ConstantNode(TypeInteger.constant(0)).peephole(); + String name = _lexer.matchId(); + if( name == null) return null; + Node n = _scope.lookup(name); + if( n!=null ) return n; + throw error("Undefined name '" + name + "'"); } /** @@ -310,13 +323,15 @@ 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 not be followed by more id letters + private boolean matchx(String syntax) { return _lexer.matchx(syntax); } // Require and return an identifier private String requireId() { String id = _lexer.matchId(); - if (id != null) return id; - throw error("identifier"); + if (id != null && !KEYWORDS.contains(id) ) return id; + throw error("Expected an identifier, found '"+id+"'"); } // Require an exact match @@ -402,11 +417,19 @@ public class Parser { int len = syntax.length(); if (_position + len > _input.length) return false; for (int i = 0; i < len; i++) - if ((char) _input[_position + i] != syntax.charAt(i)) return false; + if ((char) _input[_position + i] != syntax.charAt(i)) + return false; _position += len; return true; } + boolean matchx(String syntax) { + if( !match(syntax) ) return false; + if( !isIdLetter(peek()) ) return true; + _position -= syntax.length(); + return false; + } + // Return an identifier or null String matchId() { skipWhiteSpace(); diff --git a/chapter05/src/main/java/com/seaofnodes/simple/type/TypeInteger.java b/chapter05/src/main/java/com/seaofnodes/simple/type/TypeInteger.java index e23840e0..f75a6788 100644 --- a/chapter05/src/main/java/com/seaofnodes/simple/type/TypeInteger.java +++ b/chapter05/src/main/java/com/seaofnodes/simple/type/TypeInteger.java @@ -25,7 +25,7 @@ public class TypeInteger extends Type { @Override public String toString() { - return _print(new StringBuilder()).toString(); + return _print(new StringBuilder()).toString(); } @Override diff --git a/chapter05/src/test/java/com/seaofnodes/simple/Chapter05Test.java b/chapter05/src/test/java/com/seaofnodes/simple/Chapter05Test.java index 8f09620e..32ac5aac 100644 --- a/chapter05/src/test/java/com/seaofnodes/simple/Chapter05Test.java +++ b/chapter05/src/test/java/com/seaofnodes/simple/Chapter05Test.java @@ -95,6 +95,26 @@ return c; StopNode stop = new Parser("return true;").parse(); assertEquals("return 1;",stop.toString()); } + + @Test + public void testChapter5Keyword1() { + try { + new Parser("int true=0; return true;").parse(); + fail(); + } catch( RuntimeException e ) { + assertEquals("Expected an identifier, found 'true'",e.getMessage()); + } + } + + @Test + public void testChapter5Keyword2() { + try { + new Parser("int else=arg; if(else) else=2; else else=1; return else;").parse(); + fail(); + } catch( RuntimeException e ) { + assertEquals("Expected an identifier, found 'else'",e.getMessage()); + } + } @Test public void testChapter4Peephole() { @@ -197,6 +217,16 @@ return c; } @Test + public void testChapter4Bug3() { + try { + new Parser("inta=1; return a;").parse(); + fail(); + } catch( RuntimeException e ) { + assertEquals("Undefined name 'inta'",e.getMessage()); + } + } + + @Test public void testVarDecl() { Parser parser = new Parser("int a=1; return a;"); StopNode ret = parser.parse(); @@ -242,7 +272,6 @@ return c; } } - @Test public void testChapter2ParseGrammar() { Node._disablePeephole = true; // disable peephole so we can observe full graph |
