summaryrefslogtreecommitdiff
path: root/chapter05
diff options
context:
space:
mode:
Diffstat (limited to 'chapter05')
-rw-r--r--chapter05/src/main/java/com/seaofnodes/simple/Parser.java57
-rw-r--r--chapter05/src/main/java/com/seaofnodes/simple/type/TypeInteger.java2
-rw-r--r--chapter05/src/test/java/com/seaofnodes/simple/Chapter05Test.java31
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