From df5294e88cc409d7adf3e10666bfcb40e34472b9 Mon Sep 17 00:00:00 2001 From: Dibyendu Majumdar Date: Sun, 1 Oct 2023 18:01:54 +0100 Subject: 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 --- .../main/java/com/seaofnodes/simple/Parser.java | 94 +++++++++++----------- 1 file changed, 45 insertions(+), 49 deletions(-) (limited to 'chapter02/src/main/java/com') 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. *

* 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 * *

-     *     return expr ;
+     *     'return' expr ;
      * 
*/ 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 * */ - 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 { * */ 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 _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); } -- cgit v1.2.3