public class Parser2 {
// *****************************************************************
// the tokens of the language:
/** Opening bracket */
private final static String LPAREN = "(";
/** Closing bracket */
private final static String RPAREN = ")";
/** Operator for conjunction */
private final static String AND = "and";
/** Operator for disjunction */
private final static String OR = "or";
/** Operator for implication */
private final static String IMPLIES = "implies";
/** Operator for negation */
private final static String NOT = "not";
/** Constant: true */
private final static String TRUE = "true";
/** Constant: false */
private final static String FALSE = "false";
// *****************************************************************
// fields
/**
* Stores the string to be parsed as an array of characters.
*/
private byte[] input;
/**
* Index of the current character in the string.
*
* The string to be parsed is read character-by-character; this field stores
* the location of the character currently being read. As each character is
* read, this field should be incremented.
*/
private int index;
/**
* Index of the last accepted character.
*
* Sometimes a character is read that needs to be "put back"; for example,
* reading an opening bracket causes a recursive call of the
* {@link #parseToPrec(int) parseToPrec()} method, that will read an entire
* term up until the closing bracket; once the closing bracket is read in
* the recursive call, that call "puts the bracket back" by setting
* {@link #index index} to the value of backIndex
and returns
* the term it has read; in the calling method, the closing bracket is read
* (to check that brackets match).
*/
private int backIndex;
// *****************************************************************
/**
* Parse a string representing a term in propositional logic.
*
* @param s
* the String
string to be parsed
* @return the BoolTerm
representing that term
*/
public BoolTerm parse(String s) {
// store the string as an array of bytes, removing any leading
// or trailing spaces
input = s.trim().getBytes();
// move to start of input
backIndex = 0;
index = 0;
if (input.length == 0){
System.out.println("Empty input");
return null;
}
BoolTerm t = parseToPrec(Operators.MAX_PREC);
// check that input is fully read
if (index != input.length){
System.out.println("Expected input to end at character number " + index);
return null;
}
// otherwise
return t;
}
// *****************************************************************
/**
* parse a term
*
* This method parses a term according to the precedence of its operators;
* for example, a term "a or b and c" will be parsed as a or (b and c) since
* "and" has a lower precedence than "or".
*
* The argument gives a precedence value, and the routine will return the
* BoolTerm corresponding to the largest initial substring of the input that
* does not contain an operator with a higher precedence than the argument
* (unless such an operator with higher precedence is nested in
* parentheses). Thus, calling the routine with MAXPREC causes the entire
* string to be parsed, because no operator can have a higher precedence
* than MAXPREC.
*
* Thus a call parseToPrec(p) will construct a term from the largest
* possible initial segment of the input string, that contains no operators
* with precedence higher than p, unless they are nested in brackets.
*
* @param prec
* the precedence of an operator; the method recursively reads a
* term until an operator of greater precedence is read
* @return the BoolTerm
representing the term read
*/
private BoolTerm parseToPrec(int prec) {
BoolTerm t1, t2;
Operator op;
// read the first term
t1 = readOneTerm();
backIndex = index;
// read an operator
int pr; // the precedence of the operator
String s;
while (!endOfInput()) {
s = nextToken();
// if s is a closing bracket, then t1 is the largest subterm
// that can be read
if (s.equals(RPAREN)) {
index = backIndex;
return t1;
}
// otherwise, the next token should be an operator
op = getOperator(s);
pr = op.getPrecedence();
// only read further if the precedence of the new operator
// is no greater than prec
if (pr > prec) {
index = backIndex;
return t1;
} else // op has lower precedence
{
t2 = parseToPrec(pr);
t1 = new BoolTerm(op, new BoolTerm[] { t1, t2 });
}
}
return t1;
}
/**
* Parse the smallest initial segment that can be read as a valid term.
*
* @return A {@link BoolTerm BoolTerm} representing the initial term
*/
private BoolTerm readOneTerm() {
BoolTerm t;
String s = nextToken();
if (s.equals(LPAREN)) {
t = parseToPrec(Operators.MAX_PREC);
read(RPAREN);
return t;
}
if (s.equals(NOT)) {
t = parseToPrec(Operators.NOT_PREC);
return new BoolTerm(Operators.NOT, new BoolTerm[] { t });
}
if (s.equals(TRUE)) {
return new BoolTerm(Operators.TRUEC);
}
if (s.equals(FALSE)) {
return new BoolTerm(Operators.FALSEC);
}
t = new BoolTerm(Operators.makeVar(s));
return t;
}
/**
* Test whether the end of the input string has been reached
*
* @return true
when {@link #index index} points to the end of
* {@link #input input}; false
otherwise
*/
private boolean endOfInput() {
return (index == input.length);
}
// *****************************************************************
/**
* Parse a string as the name of an {@link Operator Operator}. The string
* should be the name of one of the operators declared in {@link Operators
* Operators.java}.
*
* @param s
* a string representing the name of an {@link Operator Operator}
* @return the {@link Operator Operator} representing the initial term
*/
private Operator getOperator(String s) {
if (s.equals(AND)) {
return Operators.AND;
}
if (s.equals(OR)) {
return Operators.OR;
}
if (s.equals(IMPLIES)) {
return Operators.IMPLIES;
}
else{
System.out.println(
"Expected one of \"and\", \"or\", or \"implies\"");
return null;
}
}
/**
* Parse the smallest initial segment that can be read as a token
*
* @return the smallest initial string that can be read as a token
*/
private String nextToken() {
if (input[index] == '(') {
index++;
readWhiteSpace();
return LPAREN;
}
if (input[index] == ')') {
index++;
readWhiteSpace();
return RPAREN;
}
return readLiteral();
}
/**
* Check that the next part of the input is a given string.
*
* @param s
* the string that is expected to be next in the input
*/
private void read(String s) {
String t = readNext(s.length());
if (!t.equals(s))
System.out.println("Expected " + s + " at character number "
+ (index - s.length()));
}
/**
* Remove any leading white space in the input. {@link #index index} is
* incremented while the current input is white space
*/
private void readWhiteSpace() {
while ((!endOfInput()) && isWhiteSpace())
index++;
}
/**
* Test whether the current input is white space.
*
* @return true
if the current input character is white space
*/
private boolean isWhiteSpace() {
byte b = input[index];
return (b == 9 || b == 10 || b == 32);
}
/**
* Test whether the current input character is alphanumeric
*
* @return true
if the current input character is a letter or a
* digit; false otherwise
*/
private boolean isAlphanum() {
byte b = input[index];
return isAlpha(b) || isNum(b);
}
/**
* Test whether the current input character is a letter
*
* @return true
if the current input character is a letter;
* false otherwise
*/
private boolean isAlpha(byte b) {
return (65 <= b && b < 91) || (97 <= b && b < 123);
}
/**
* Test whether the current input character is a digit
*
* @return true
if the current input character is a digit;
* false otherwise
*/
private boolean isNum(byte b) {
return 48 <= b && b < 58;
}
/**
* Read a number of characters in the input.
*
* @param n
* the number of characters to be read
* @return the string consisting of the next n
characters from
* input
*/
private String readNext(int n) {
if (index + n > input.length){
System.out.println("Unexpected end of input");
return null;
}
String s = new String(input, index, n);
index += n;
readWhiteSpace();
return s;
}
/**
* Read an alphanumeric string from input.
*
* @return the longest alphanumeric string currently to be read in the input
*/
private String readLiteral() {
int start = index;
while ((!endOfInput()) && isAlphanum()) {
index++;
}
String s = new String(input, start, index - start);
readWhiteSpace();
return s;
}
// Example use:
public static void main(String[] args) {
Parser p1 = new Parser();
try {
BoolTerm boolTerm = p1.parse("((a and b) or not(b and c))");
System.out.println(boolTerm.toString());
} catch (ParseException e) {
e.printStackTrace();
}
}
}// Parser