package com.seaofnodes.simple; import com.seaofnodes.simple.node.*; import com.seaofnodes.simple.type.*; import java.util.*; /** * The Parser converts a Simple source program to the Sea of Nodes intermediate * representation directly in one pass. There is no intermediate Abstract * Syntax Tree structure. *
* This is a simple recursive descent parser. All lexical analysis is done here as well. */ public class Parser { /** * 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; public StopNode STOP; // The Lexer. Thin wrapper over a byte[] buffer with a cursor. private final Lexer _lexer; /** * Current ScopeNode - ScopeNodes change as we parse code, but at any point of time * there is one current ScopeNode. The reason the current ScopeNode can change is to do with how * we handle branching. See {@link #parseIf()}. *
* Each ScopeNode contains a stack of lexical scopes, each scope is a symbol table that binds * variable names to Nodes. The top of this stack represents current scope. *
* We keep a list of all ScopeNodes so that we can show them in graphs.
* @see #parseIf()
* @see #_xScopes
*/
public ScopeNode _scope;
/**
* List of keywords disallowed as identifiers
*/
private final HashSet
* '{' statements '}'
*
* Does not parse the opening or closing '{}'
* @return a {@link Node} or {@code null}
*/
private Node parseBlock() {
// Enter a new scope
_scope.push();
while (!peek('}') && !_lexer.isEOF())
parseStatement();
// Exit scope
_scope.pop();
return null;
}
/**
* Parses a statement
*
*
* returnStatement | declStatement | blockStatement | ifStatement | expressionStatement
*
* @return a {@link Node} or {@code null}
*/
private Node parseStatement() {
if (matchx("return") ) return parseReturn();
else if (matchx("int")) return parseDecl();
else if (match ("{" )) return require(parseBlock(),"}");
else if (matchx("if" )) return parseIf();
else if (matchx("while")) return parseWhile();
else if (matchx("#showGraph")) return require(showGraph(),";");
else if (matchx(";")) return null; // Empty statement
else return parseExpressionStatement();
}
/**
* Parses a while statement
*
*
* while ( expression ) statement
*
* @return a {@link Node}, never {@code null}
*/
private Node parseWhile() {
require("(");
// Loop region has two control inputs, the first is the entry
// point, and second is back edge that is set after loop is parsed
// (see end_loop() call below). Note that the absence of back edge is
// used as an indicator to switch off peepholes of the region and
// associated phis; see {@code inProgress()}.
ctrl(new LoopNode(ctrl()).peephole()); // Note we set back edge to null here
// At loop head, we clone the current Scope (this includes all
// names in every nesting level within the Scope).
// We create phis eagerly for all the names we find, see dup().
// Save the current scope as the loop head
ScopeNode head = _scope.keep();
// Clone the head Scope to create a new Scope for the body.
// Create phis eagerly as part of cloning
_xScopes.push(_scope = _scope.dup(true)); // The true argument triggers creating phis
// Parse predicate
var pred = require(parseExpression(), ")");
// IfNode takes current control and predicate
IfNode ifNode = (IfNode)new IfNode(ctrl(), pred).keep().peephole();
// Setup projection nodes
Node ifT = new ProjNode(ifNode, 0, "True" ).peephole();
ifNode.unkeep();
Node ifF = new ProjNode(ifNode, 1, "False").peephole();
// Clone the body Scope to create the exit Scope
// which accounts for any side effects in the predicate
// The exit Scope will be the final scope after the loop,
// And its control input is the False branch of the loop predicate
// Note that body Scope is still our current scope
var exit = _scope.dup();
_xScopes.push(exit);
exit.ctrl(ifF);
// Parse the true side, which corresponds to loop body
// Our current scope is the body Scope
ctrl(ifT); // set ctrl token to ifTrue projection
parseStatement(); // Parse loop body
// The true branch loops back, so whatever is current control (_scope.ctrl) gets
// added to head loop as input. endLoop() updates the head scope,
// and goes through all the phis that were created earlier. For each
// phi, it sets the second input to the corresponding input from the back edge.
// If the phi is redundant, it is replaced by its sole input.
head.endLoop(_scope, exit);
head.unkeep().kill();
_xScopes.pop(); // Cleanup
_xScopes.pop(); // Cleanup
// At exit the false control is the current control, and
// the scope is the exit scope after the exit test.
return (_scope = exit);
}
/**
* Parses a statement
*
*
* if ( expression ) statement [else statement]
*
* @return a {@link Node}, never {@code null}
*/
private Node parseIf() {
require("(");
// Parse predicate
var pred = require(parseExpression(), ")");
// IfNode takes current control and predicate
IfNode ifNode = (IfNode)new IfNode(ctrl(), pred).
* 'return' expr ;
*
* @return an expression {@link Node}, never {@code null}
*/
private Node parseReturn() {
var expr = require(parseExpression(), ";");
Node ret = STOP.addReturn(new ReturnNode(ctrl(), expr).peephole());
ctrl(new ConstantNode(Type.XCONTROL).peephole()); // Kill control
return ret;
}
/**
* Dumps out the node graph
* @return {@code null}
*/
private Node showGraph() {
System.out.println(new GraphVisualizer().generateDotOutput(this));
return null;
}
/**
* Parses an expression statement
*
*
* name '=' expression ';'
*
* @return an expression {@link Node}, never {@code null}
*/
private Node parseExpressionStatement() {
var name = requireId();
require("=");
var expr = require(parseExpression(), ";");
if( _scope.update(name, expr)==null )
throw error("Undefined name '" + name + "'");
return expr;
}
/**
* Parses a declStatement
*
*
* 'int' name = expression ';'
*
* @return an expression {@link Node}, never {@code null}
*/
private Node parseDecl() {
// Type is 'int' for now
var name = requireId();
require("=");
var expr = require(parseExpression(), ";");
if( _scope.define(name,expr) == null )
throw error("Redefining name '" + name + "'");
return expr;
}
/**
* Parse an expression of the form:
*
*
* expr : compareExpr
*
* @return an expression {@link Node}, never {@code null}
*/
private Node parseExpression() { return parseComparison(); }
/**
* Parse an expression of the form:
*
*
* expr : additiveExpr op additiveExpr
*
* @return an comparator expression {@link Node}, never {@code null}
*/
private Node parseComparison() {
var lhs = parseAddition();
if (match("==")) return new BoolNode.EQ(lhs, parseComparison()).peephole();
if (match("!=")) return new NotNode(new BoolNode.EQ(lhs, parseComparison()).peephole()).peephole();
if (match("<=")) return new BoolNode.LE(lhs, parseComparison()).peephole();
if (match("<" )) return new BoolNode.LT(lhs, parseComparison()).peephole();
if (match(">=")) return new BoolNode.LE(parseComparison(), lhs).peephole();
if (match(">" )) return new BoolNode.LT(parseComparison(), lhs).peephole();
return lhs;
}
/**
* Parse an additive expression
*
*
* additiveExpr : multiplicativeExpr (('+' | '-') multiplicativeExpr)*
*
* @return an add expression {@link Node}, never {@code null}
*/
private Node parseAddition() {
var lhs = parseMultiplication();
if (match("+")) return new AddNode(lhs, parseAddition()).peephole();
if (match("-")) return new SubNode(lhs, parseAddition()).peephole();
return lhs;
}
/**
* Parse an multiplicativeExpr expression
*
*
* multiplicativeExpr : unaryExpr (('*' | '/') unaryExpr)*
*
* @return a multiply expression {@link Node}, never {@code null}
*/
private Node parseMultiplication() {
var lhs = parseUnary();
if (match("*")) return new MulNode(lhs, parseMultiplication()).peephole();
if (match("/")) return new DivNode(lhs, parseMultiplication()).peephole();
return lhs;
}
/**
* Parse a unary minus expression.
*
*
* unaryExpr : ('-') unaryExpr | primaryExpr
*
* @return a unary expression {@link Node}, never {@code null}
*/
private Node parseUnary() {
if (match("-")) return new MinusNode(parseUnary()).peephole();
return parsePrimary();
}
/**
* Parse a primary expression:
*
*
* primaryExpr : integerLiteral | Identifier | true | false | '(' expression ')'
*
* @return a primary {@link Node}, never {@code null}
*/
private Node parsePrimary() {
if( _lexer.isNumber() ) return parseIntegerLiteral();
if( match("(") ) return require(parseExpression(), ")");
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) throw errorSyntax("an identifier or expression");
Node n = _scope.lookup(name);
if( n!=null ) return n;
throw error("Undefined name '" + name + "'");
}
/**
* Parse integer literal
*
*
* integerLiteral: [1-9][0-9]* | [0]
*
*/
private ConstantNode parseIntegerLiteral() {
return (ConstantNode) new ConstantNode(_lexer.parseNumber()).peephole();
}
//////////////////////////////////
// Utilities for lexical analysis
// Return true and skip if "syntax" is next in the stream.
private boolean match (String syntax) { return _lexer.match (syntax); }
// Match must be "exact", not be followed by more id letters
private boolean matchx(String syntax) { return _lexer.matchx(syntax); }
// Return true and do NOT skip if 'ch' is next
private boolean peek(char ch) { return _lexer.peek(ch); }
// Require and return an identifier
private String requireId() {
String id = _lexer.matchId();
if (id != null && !KEYWORDS.contains(id) ) return id;
throw error("Expected an identifier, found '"+id+"'");
}
// Require an exact match
private void require(String syntax) { require(null, syntax); }
private