summaryrefslogtreecommitdiff
path: root/chapter02/src/main/java
diff options
context:
space:
mode:
authorDibyendu Majumdar <dibyendumajumdar@users.noreply.github.com>2023-10-01 18:01:54 +0100
committerGitHub <noreply@github.com>2023-10-01 10:01:54 -0700
commitdf5294e88cc409d7adf3e10666bfcb40e34472b9 (patch)
treea890d2a65704870a27211613b81f1e950f73a588 /chapter02/src/main/java
parent3f2e9e84e137462272ae1bb592f47cecc362bdff (diff)
downloadSimple-df5294e88cc409d7adf3e10666bfcb40e34472b9.tar.gz
Simple-df5294e88cc409d7adf3e10666bfcb40e34472b9.zip
Chapter 3 - add vars and lexical scope. (#7)
* Chapter 3 - add vars and lexical scope. * Fix typo * Overhaul graph viz Makes the scopes pointing into the graph being build more visually appealing. Constants are a printed with just the constant value. * Fix control edge color * Fix missing grouping operator parse Add a parens test. Shorten names 'Identifier' to 'Id' _lexer.match -> match require() passes a Node thru, so can be flow-coded. Uniform syntax error messages. Handy Lexer.toString for debugging. Drop Optional for null returns; less total code; less objects; less confusion. camelCase some fcns. * All parser helpers return Node, not void. Drop _ret. parseBlock and parseStatement now the same. * CLosely align chap2 and chap3 parsers Mostly whitespace aligned; dragged a few functions around to make more sense. I think the diff from Ch2 and Ch3 is down a few differences about what is a statement, plus all the symbol stuff for scopes and variables. * Add examples illustrating the symbol table creation. * Update docs and comments --------- Co-authored-by: Cliff Click <cliffc@acm.org>
Diffstat (limited to 'chapter02/src/main/java')
-rw-r--r--chapter02/src/main/java/com/seaofnodes/simple/Parser.java94
1 files changed, 45 insertions, 49 deletions
diff --git a/chapter02/src/main/java/com/seaofnodes/simple/Parser.java b/chapter02/src/main/java/com/seaofnodes/simple/Parser.java
index 27a7cffd..3ca13c3f 100644
--- a/chapter02/src/main/java/com/seaofnodes/simple/Parser.java
+++ b/chapter02/src/main/java/com/seaofnodes/simple/Parser.java
@@ -12,39 +12,38 @@ import com.seaofnodes.simple.type.*;
*/
public class Parser {
- private final Lexer _lexer;
-
/**
* A Global Static, unique to each compilation. This is a public, so we
* can make constants everywhere without having to thread the StartNode
* through the entire parser and optimizer.
* <p>
* To make the compiler multithreaded, this field will have to move into a TLS.
- */
+ */
public static StartNode START;
+ private final Lexer _lexer;
+
public Parser( String source ) {
_lexer = new Lexer(source);
Node.reset();
START = new StartNode();
}
-
+
public ReturnNode parse() {
+ require("return");
return parseReturn();
}
/**
- * Parses return statement.
+ * Parses returnStatement; "return" already parsed
*
* <pre>
- * return expr ;
+ * 'return' expr ;
* </pre>
*/
private ReturnNode parseReturn() {
- require("return");
- var returnExpr = parseExpression();
- require(";");
- return new ReturnNode(START, returnExpr);
+ var expr = require(parseExpression(),";");
+ return new ReturnNode(START, expr);
}
/**
@@ -54,9 +53,7 @@ public class Parser {
* expr : additiveExpr
* </pre>
*/
- private Node parseExpression() {
- return parseAddition();
- }
+ private Node parseExpression() { return parseAddition(); }
/**
* Parse an additive expression
@@ -72,7 +69,6 @@ public class Parser {
return lhs;
}
-
/**
* Parse an multiplicativeExpr expression
*
@@ -107,9 +103,9 @@ public class Parser {
* </pre>
*/
private Node parsePrimary() {
- if( _lexer.isNumber() )
- return parseIntegerLiteral();
- throw error("syntax error, expected integer literal");
+ if( _lexer.isNumber() ) return parseIntegerLiteral();
+ if( match("(") ) return require(parseExpression(),")");
+ throw errorSyntax("integer literal");
}
/**
@@ -123,24 +119,27 @@ public class Parser {
return (ConstantNode)new ConstantNode(_lexer.parseNumber()).peephole();
}
- static RuntimeException error(String errorMessage) {
- return new RuntimeException(errorMessage);
- }
-
//////////////////////////////////
// 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); }
// Require an exact match
- private void require(String syntax) {
- if( match(syntax) ) return;
- throw error("Syntax error, expected " + syntax + ": " + _lexer.getAnyNextToken());
+ private void require(String syntax) { require(null,syntax); }
+ private Node require(Node n, String syntax) {
+ if (match(syntax)) return n;
+ throw errorSyntax(syntax);
+ }
+
+ RuntimeException errorSyntax(String syntax) {
+ return error("Syntax error, expected " + syntax + ": " + _lexer.getAnyNextToken());
}
+ static RuntimeException error(String errorMessage) {
+ return new RuntimeException(errorMessage);
+ }
+
////////////////////////////////////
// Lexer components
@@ -165,6 +164,12 @@ public class Parser {
_input = buf;
}
+ // Very handy in the debugger, shows the unparsed program
+ @Override
+ public String toString() {
+ return new String(_input,_position,_input.length-_position);
+ }
+
// True if at EOF
private boolean isEOF() {
return _position >= _input.length;
@@ -191,8 +196,7 @@ public class Parser {
* Return the next non-white-space character
*/
private void skipWhiteSpace() {
- while (isWhiteSpace())
- _position++;
+ while (isWhiteSpace()) _position++;
}
@@ -202,10 +206,9 @@ public class Parser {
boolean match( String syntax ) {
skipWhiteSpace();
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) )
+ if( _position + len > _input.length ) return false;
+ for( int i = 0; i < len; i++ )
+ if( (char)_input[_position + i] != syntax.charAt(i) )
return false;
_position += len;
return true;
@@ -214,20 +217,13 @@ public class Parser {
// Used for errors
String getAnyNextToken() {
if( isEOF() ) return "";
- if( isIdentifierStart(peek()) )
- return parseIdentifier();
- if( isPunctuation(peek()) )
- return parsePunctuation();
+ if( isIdStart(peek()) ) return parseId();
+ if( isPunctuation(peek()) ) return parsePunctuation();
return String.valueOf(peek());
}
-
- boolean isNumber() {
- return isNumber(peek());
- }
-
- boolean isNumber(char ch) {
- return Character.isDigit(ch);
- }
+
+ boolean isNumber() { return isNumber(peek()); }
+ boolean isNumber(char ch) { return Character.isDigit(ch); }
private Type parseNumber() {
int start = _position;
@@ -239,18 +235,18 @@ public class Parser {
}
// First letter of an identifier
- private boolean isIdentifierStart(char ch) {
+ private boolean isIdStart(char ch) {
return Character.isAlphabetic(ch) || ch == '_';
}
// All characters of an identifier, e.g. "_x123"
- private boolean isIdentifierLetter(char ch) {
+ private boolean isIdLetter(char ch) {
return Character.isLetterOrDigit(ch) || ch == '_';
}
- private String parseIdentifier() {
+ private String parseId() {
int start = _position;
- while (isIdentifierLetter(nextChar())) ;
+ while (isIdLetter(nextChar())) ;
return new String(_input, start, --_position - start);
}