/** Bottom-up parser for lab3, similar to lab 2 but includes both * declarations and evaluation (output) statements. * The possible input tokens are: id num ; = + - * / */ import java.io.*; import java.util.*; public class Program { ArrayList tokenList; int pos; Stack stateStack; Stack treeStack; public Program() { tokenList = new ArrayList(); pos = 0; stateStack = new Stack(); treeStack = new Stack(); } // A function to determine the type of token. If it's a simple (1 char) token // we just return that token. But in the case of a variable name or number // we need to return "id" or "num". This is so that we can look up the // appropriate "column" of the bottom-up parse table. public String findTokenType(String token) { String tokenType = ""; if (token.equals(";") || token.equals("=") || token.equals("+") || token.equals("-") || token.equals("*") || token.equals("/") || token.equals("$") || token.equals("stmt") || token.equals("prog") || token.equals("expr") || token.equals("op")) tokenType = token; else { try { Integer.parseInt(token); tokenType = "num"; } catch (Exception e) { // If it's not a number, let's assume it's an identifier. tokenType = "id"; } } return tokenType; } public void scan() throws IOException { // To get started, we read standard input for our example variable // declarations. Then we "scan" it by using the standard String Tokenizer // from the Java API, and put the tokens into an arraylist. BufferedReader in = new BufferedReader(new InputStreamReader(System.in)); System.out.printf("Please enter statements, separated by ';'. " + "Separate each token by a space.\n"); String line = in.readLine(); StringTokenizer tok = new StringTokenizer(line, " "); // Store token in list. We'll wait to see what kind of token it is // until the actual parsing takes place, as we encounter each token. while(tok.hasMoreTokens()) { tokenList.add(tok.nextToken()); } // At this point we have our tokens in tokenList. // debugging output //for (int i = 0; i < tokenList.size(); ++i) // System.out.printf("%s\n", tokenList.get(i)); } // Performs action on a "goto" transition in parse. // We push next state onto stack of states, and eat the input token. // To make this function, it was necessary to make the stack state global as well. public void goTo(int nextState) { stateStack.push(new Integer(nextState)); tokenList.remove(0); } /** bottomUpParse(): We assume that scan() has already been called, so that * the input tokens are now stored in the ArrayList called tokenList. * Here is how parsing works bottom up. * Start in state 0. Push the number 0 on the state stack. * We make our decision based on what state we're in, and next input symbol. * Next input symbol is at beginning of tokenList. * If it's a "goto", then we change state, push the new state number, and * consume the input token (get rid of first element from ArrayList). * If it's a reduce, we have to pop some states off the state-stack, go to * the state that's now at the top of the stack, then append a nonterminal * onto the beginning of the tokenList. * If it's the happy state, we're done. Also could have syntax error anytime. * Note that we are treating the tokenList differently to how we did top-down * parsing: it's a little easier to implement bottom-up parsing if we can * change the input stream (take things off, and append stuff), rather than * just traversing it with the pos (index) variable. */ public void bottomUpParse() { // To begin with, let's put a '$' at end of input. tokenList.add("$"); stateStack.push(new Integer(0)); // Start in state 0. We always keep the current state number on top of // the state-stack. // The "token" string is the lexeme of the token such as "x" for a variable. // The "input" string, as before, lets us look up the appropriate parse // action, and this needs to be the token type, as in "id". boolean done = false; while (true) { String token = tokenList.get(0); String input = findTokenType(token); int state = stateStack.peek().intValue(); System.out.printf("state %d, next input token '%s'\n", state, token); switch(state) { // State 0 is a typical "goto" case. goTo() handles this operation. case 0: if (input.equals("id")) goTo(2); else if (input.equals("num")) goTo(4); else if (input.equals("stmt")) goTo(1); else if (input.equals("expr")) goTo(3); else error("syntax error - identifier or number expected"); break; case 1: if (input.equals("id")) goTo(2); else if (input.equals("num")) goTo(4); // Reducing a program: prog --> stmt else if (input.equals("$")) { System.out.printf("Parse successful!\n"); done = true; } else if (input.equals("prog")) goTo(5); else if (input.equals("stmt")) goTo(1); else if (input.equals("expr")) goTo(3); else error("syntax error - id, num or eof expected"); break; case 2: if (input.equals("=")) goTo(6); // Reducing an expression: expr --> id else if (input.equals("id") || input.equals("num") || input.equals(";") || input.equals("+") || input.equals("-") || input.equals("*") || input.equals("/")) { stateStack.pop(); tokenList.add(0, "expr"); } else error("syntax error - unexpected eof"); break; case 3: if (input.equals("id")) goTo(2); else if (input.equals("num")) goTo(4); else if (input.equals(";")) goTo(7); else if (input.equals("expr")) goTo(8); else error("syntax error - id, num or semicolon expected"); break; // Reducing an expression: expr --> num case 4: if (input.equals("id") || input.equals("num") || input.equals(";") || input.equals("+") || input.equals("-") || input.equals("*") || input.equals("/")) { stateStack.pop(); tokenList.add(0, "expr"); } else error("syntax error - id, num, semicolon or operator expected"); break; // Reducing a program: prog --> stmt prog case 5: if (input.equals("$")) { System.out.println("Parse successful!"); done = true; } else error("syntax error - extra token(s) after declaration"); break; case 6: if (input.equals("num")) goTo(9); else error("syntax error - number expected"); break; // Reducing a statement: stmt --> expr ; case 7: if (input.equals("id") || input.equals("num") || input.equals("$")) { stateStack.pop(); stateStack.pop(); tokenList.add(0, "stmt"); } else error("syntax error - id, num or eof expected"); break; case 8: if (input.equals("id")) goTo(2); else if (input.equals("num")) goTo(4); else if (input.equals("+")) goTo(11); else if (input.equals("-")) goTo(12); else if (input.equals("*")) goTo(13); else if (input.equals("/")) goTo(14); else if (input.equals("expr")) goTo(8); else if (input.equals("op")) goTo(10); else error("syntax error - id, num or operator expected"); break; case 9: if (input.equals(";")) goTo(15); else error("syntax error - semicolon expected"); break; // Reducing an expression: expr --> expr expr op case 10: if (input.equals("id") || input.equals("num") || input.equals(";") || input.equals("+") || input.equals("-") || input.equals("*") || input.equals("/")) { stateStack.pop(); stateStack.pop(); stateStack.pop(); tokenList.add(0, "expr"); } else error("syntax error - id, num, semicolon or operator expected"); break; // Reducing an operator: op --> + | - | * | / case 11: case 12: case 13: case 14: if (input.equals("id") || input.equals("num") || input.equals(";") || input.equals("+") || input.equals("-") || input.equals("*") || input.equals("/")) { stateStack.pop(); tokenList.add(0, "op"); } else error("syntax error - id, num, semicolon or operator expected"); break; // Reducing a statement: stmt --> id = num ; case 15: if (input.equals("id") || input.equals("num") || input.equals("$")) { stateStack.pop(); stateStack.pop(); stateStack.pop(); stateStack.pop(); tokenList.add(0, "stmt"); } else error("syntax error - id, number or eof expected"); } if (done) break; } } public void error(String s) { System.out.printf("%s\n", s); System.exit(1); } }