// Minilog // Author: Chris Stone // Simple logical calculator, for inputs like // true or false // ( true and false) or false // ...etc... import java.util.LinkedList; import java.util.Scanner; class Minilog { private LinkedList toks; public void tokenize(String input) { this.toks = new LinkedList(); // Strings have a .split method that takes // a regular expression and uses it // to divide the string up into an array of // substrings. // The result is not a very good tokenizer, since // we are using whitespace (\s+) to determine // where one token ends and the other begins. // Thus, "true||false" would be one token, while // "true || false" would be three. I bet we // could do better for Unicalc... for (String token : input.split("\\s+")) this.toks.add(token); System.out.println("tokens: " + this.toks.toString()); return; } // Grammar: // S -> C | C or S // C -> B | B and C // B -> true | false | ( S ) /////////////////////// // Parsing Functions // /////////////////////// // Read in a prefix of the tokens matching S // S -> C | C or S // and summarize it (in this program, with the answer) // boolean S() { // Either way, we know we want to start by // looking for input matching C. boolean c = C(); // Peek ahead to see if there's more to this S or not. String next = toks.peek(); if ( "or".equals(next) ) { // Yes! toks.pop(); // Get rid of the "or" token, so we can get the rest boolean s = S(); return c || s; // Combine the result of the C part and S part } else { // Nope, the S was just a single C. return that result. return c; } } // Read in a prefix of the tokens matching S // C -> B | B and C // and summarize it (in this program, with the answer) // boolean C() { boolean b = B(); // Peek ahead. Is there more to this C? String next = toks.peek(); if ( "and".equals(next) ) { toks.pop(); boolean c = C(); return (b && c); } else { return b; } } // Read in a prefix of the tokens matching S // B -> true | false | ( S ) // and summarize it (in this program, with the answer) // boolean B() { // All the possibilities seem different. Let's peek. String next = toks.peek(); if ( "true".equals(next) ) { toks.pop(); // Don't forget to "remove" the B tokens! return true; } else if ( "false".equals(next) ) { toks.pop(); return false; } else if ( "(".equals(next) ) { // B -> ( S ) toks.pop(); // remove the left parenthesis boolean s = S(); // recursively parse something matching S // Now we expect to find a right parenthesis after the S. if ( ")".equals(toks.peek()) ) { toks.pop(); // It's there; remove it! return s; // The result of ( S ) is the same as the // result of S; the parens don't change anything. } else { // It would be better to create our own ParseError exception... throw new IllegalArgumentException("Missing right parenthesis!"); } } else { // Peeking doesn't give us anything we'd expect to see here. throw new IllegalArgumentException("Unexpected token: " + next); } } ////////////////////////// // Read-Eval-Print Loop // ////////////////////////// public static void main(String[] args) { // We want to read input not from a file (FileInputStream), // but directly from the keyboard. Scanner console = new Scanner(System.in); Minilog mini = new Minilog(); while (true) { // Prompt, and get input System.out.print("input> "); String input = console.nextLine(); // Quit if no input if (input.equals("")) break; // Tokenize mini.tokenize(input); // Parse (and evaluate) boolean result = mini.S(); if (! mini.toks.isEmpty() ) System.out.println("Leftover tokens: " + mini.toks.toString()); // Display results System.out.println("Result: " + result + "\n"); } } }