/**
*
*/
package xhail.core.parser;
import java.util.Collection;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
import xhail.core.Logger;
import xhail.core.statements.Display;
import xhail.core.statements.Example;
import xhail.core.statements.ModeB;
import xhail.core.statements.ModeH;
import xhail.core.terms.Atom;
import xhail.core.terms.Number;
import xhail.core.terms.Placemarker;
import xhail.core.terms.Placemarker.Type;
import xhail.core.terms.Quotation;
import xhail.core.terms.Scheme;
import xhail.core.terms.SchemeTerm;
import xhail.core.terms.Term;
import xhail.core.terms.Variable;
/**
* @author stefano
*
*/
public class Parser {
public static class ParserErrorException extends Exception {
private static final long serialVersionUID = 506164049971528165L;
public ParserErrorException(String message) {
super(message);
}
}
public static class StringIterator implements Iterator<Character> {
private int pos = -1;
private final String source;
public StringIterator(String source) {
this.source = source;
}
@Override
public boolean hasNext() {
return null == source ? false : source.length() > 1 + pos;
}
@Override
public Character next() {
return hasNext() ? source.charAt(++pos) : null;
}
public void pushBack() {
if (pos > -1)
pos -= 1;
}
}
public static Collection<Atom> parseAnswer(String source) {
if (null == source)
throw new IllegalArgumentException("Illegal 'source' argument in Parser.parseAnswer(String): " + source);
try {
Parser parser = new Parser(source);
Collection<Atom> result = parser.parseAnswer();
parser.parseEOF();
return result;
} catch (ParserErrorException e) {
Logger.error(e.getMessage());
return null;
}
}
public static Display parseDisplay(String source) {
if (null == source)
throw new IllegalArgumentException("Illegal 'source' argument in Parser.parseDisplay(String): " + source);
try {
Parser parser = new Parser(source);
Display result = parser.parseDisplay();
parser.parseEOF();
return result;
} catch (ParserErrorException e) {
Logger.error(e.getMessage());
return null;
}
}
public static Example parseExample(String source) {
if (null == source)
throw new IllegalArgumentException("Illegal 'source' argument in Parser.parseExample(String): " + source);
try {
Parser parser = new Parser(source);
Example result = parser.parseExample();
parser.parseEOF();
return result;
} catch (ParserErrorException e) {
Logger.error(e.getMessage());
return null;
}
}
public static Atom parseToken(String source) {
if (null == source)
throw new IllegalArgumentException("Illegal 'source' argument in Parser.parseToken(String): " + source);
try {
Parser parser = new Parser(source);
Atom result = parser.parseGroundAtom();
parser.parseEOF();
return result;
} catch (ParserErrorException e) {
Logger.error(e.getMessage());
return null;
}
}
public static ModeB parseModeB(String source) {
if (null == source)
throw new IllegalArgumentException("Illegal 'source' argument in Parser.parseModeB(String): " + source);
try {
Parser parser = new Parser(source);
ModeB result = parser.parseModeB();
parser.parseEOF();
return result;
} catch (ParserErrorException e) {
Logger.error(e.getMessage());
return null;
}
}
public static ModeH parseModeH(String source) {
if (null == source)
throw new IllegalArgumentException("Illegal 'source' argument in Parser.parseModeH(String): " + source);
try {
Parser parser = new Parser(source);
ModeH result = parser.parseModeH();
parser.parseEOF();
return result;
} catch (ParserErrorException e) {
Logger.error(e.getMessage());
return null;
}
}
private Character current;
private final Iterator<Character> iterator;
private final String source;
public Parser(String source) {
if (null == source)
throw new IllegalArgumentException("Illegal 'source' argument in Parser.Parser(String): " + source);
this.source = source;
this.iterator = new StringIterator(this.source);
this.current = this.iterator.next();
}
public final String getSource() {
return source;
}
private Set<Atom> parseAnswer() throws ParserErrorException {
skip();
Set<Atom> result = new HashSet<>();
while (null != current && Character.isLowerCase(current)) {
result.add(parseGroundAtom());
skip();
}
return result;
}
private void parseAt() throws ParserErrorException {
skip();
if (null == current)
throw new ParserErrorException("expected '@' but EOF found in '" + source + "'");
if ('@' != current)
throw new ParserErrorException("expected '@' but '" + current + "' found in '" + source + "'");
current = iterator.next();
}
private Atom parseAtom() throws ParserErrorException {
Atom.Builder result = new Atom.Builder(parseIdentifier());
skip();
if (null != current && '(' == current) {
parseLeftParen();
result.addTerm(parseTerm());
skip();
while (null != current && ',' == current) {
parseComma();
result.addTerm(parseTerm());
skip();
}
parseRightParen();
}
return result.build();
}
private void parseColon() throws ParserErrorException {
skip();
if (null == current)
throw new ParserErrorException("expected ':' but EOF found in '" + source + "'");
if (':' != current)
throw new ParserErrorException("expected ':' but '" + current + "' found in '" + source + "'");
current = iterator.next();
}
private void parseComma() throws ParserErrorException {
skip();
if (null == current)
throw new ParserErrorException("expected ',' but EOF found in '" + source + "'");
if (',' != current)
throw new ParserErrorException("expected ',' but '" + current + "' found in '" + source + "'");
current = iterator.next();
}
private void parseDash() throws ParserErrorException {
skip();
if (null == current)
throw new ParserErrorException("expected '-' but EOF found in '" + source + "'");
if ('-' != current)
throw new ParserErrorException("expected '-' but '" + current + "' found in '" + source + "'");
current = iterator.next();
}
private Display parseDisplay() throws ParserErrorException {
String identifier = parseIdentifier();
parseSlash();
if (null == current)
throw new ParserErrorException("expected '0..9' but EOF found in '" + source + "'");
if (!Character.isDigit(current))
throw new ParserErrorException("expected '0..9' but '" + current + "' found in '" + source + "'");
Number number = parseNumber();
return new Display.Builder(identifier).setArity(number.getValue()).build();
}
private void parseEOF() throws ParserErrorException {
skip();
if (null != current)
throw new ParserErrorException("expected EOF but '" + current + "' found in '" + source + "'");
}
private void parseEqual() throws ParserErrorException {
skip();
if (null == current)
throw new ParserErrorException("expected '=' but EOF found in '" + source + "'");
if ('=' != current)
throw new ParserErrorException("expected '=' but '" + current + "' found in '" + source + "'");
current = iterator.next();
}
private Example parseExample() throws ParserErrorException {
Atom atom = parseGroundAtom();
boolean negated = "not".equals(atom.getIdentifier());
if (negated)
atom = parseGroundAtom();
Example.Builder result = new Example.Builder(atom).setNegated(negated);
if (null != current && '=' == current) {
parseEqual();
result.setWeight(parseNumber().getValue());
}
if (null != current && '@' == current) {
parseAt();
result.setPriority(parseNumber().getValue());
}
return result.build();
}
private Atom parseGroundAtom() throws ParserErrorException {
Atom.Builder result = new Atom.Builder(parseIdentifier());
skip();
if (null != current && '(' == current) {
parseLeftParen();
result.addTerm(parseGroundTerm());
skip();
while (null != current && ',' == current) {
parseComma();
result.addTerm(parseGroundTerm());
skip();
}
parseRightParen();
}
return result.build();
}
private Term parseGroundTerm() throws ParserErrorException {
skip();
if (null == current)
throw new ParserErrorException("expected 'TERM' but EOF found in '" + source + "'");
if (Character.isLowerCase(current))
return parseAtom();
if (Character.isDigit(current) || '-' == current)
return parseNumber();
if ('\"' == current)
return parseQuotation();
throw new ParserErrorException("expected 'TERM' but '" + current + "' found in '" + source + "'");
}
private String parseIdentifier() throws ParserErrorException {
skip();
if (null == current)
throw new ParserErrorException("expected 'a..z' but EOF found in '" + source + "'");
if (!Character.isLowerCase(current))
throw new ParserErrorException("expected 'a..z' but '" + current + "' found in '" + source + "'");
String result = "";
while (null != current && (Character.isLowerCase(current) || Character.isUpperCase(current) || Character.isDigit(current) || '_' == current)) {
result += current;
current = iterator.next();
}
return result;
}
private void parseLeftParen() throws ParserErrorException {
skip();
if (null == current)
throw new ParserErrorException("expected '(' but EOF found in '" + source + "'");
if ('(' != current)
throw new ParserErrorException("expected '(' but '" + current + "' found in '" + source + "'");
current = iterator.next();
}
private ModeB parseModeB() throws ParserErrorException {
Scheme scheme = parseScheme();
boolean negated = "not".equals(scheme.getIdentifier());
if (negated)
scheme = parseScheme();
ModeB.Builder result = new ModeB.Builder(scheme).setNegated(negated);
if (null != current && ':' == current) {
parseColon();
result.setUpper(parseNumber().getValue());
}
if (null != current && '=' == current) {
parseEqual();
result.setWeight(parseNumber().getValue());
}
if (null != current && '@' == current) {
parseAt();
result.setPriority(parseNumber().getValue());
}
return result.build();
}
private ModeH parseModeH() throws ParserErrorException {
ModeH.Builder result = new ModeH.Builder(parseScheme());
if (null != current && ':' == current) {
parseColon();
int value = parseNumber().getValue();
if (null != current && '-' == current) {
result.setLower(value);
parseDash();
result.setUpper(parseNumber().getValue());
} else
result.setUpper(value);
}
if (null != current && '=' == current) {
parseEqual();
result.setWeight(parseNumber().getValue());
}
if (null != current && '@' == current) {
parseAt();
result.setPriority(parseNumber().getValue());
}
return result.build();
}
private Number parseNumber() throws ParserErrorException {
skip();
if (null == current)
throw new ParserErrorException("expected '-' or '0..9' but EOF found in '" + source + "'");
boolean negative;
if (negative = (current == '-')) {
current = iterator.next();
skip();
}
if (null == current)
throw new ParserErrorException("expected '0..9' but EOF found in '" + source + "'");
if (!Character.isDigit(current))
throw new ParserErrorException("expected '0..9' but '" + current + "' found in '" + source + "'");
int result = (current - '0');
current = iterator.next();
while (null != current && Character.isDigit(current)) {
result = 10 * result + (current - '0');
current = iterator.next();
}
return new Number.Builder(negative ? -result : result).build();
}
private Placemarker parsePlacemarker() throws ParserErrorException {
skip();
if (null == current)
throw new ParserErrorException("expected 'TERM' but EOF found in '" + source + "'");
Type type;
if ('+' == current) {
type = Type.INPUT;
current = iterator.next();
} else if ('-' == current) {
type = Type.OUTPUT;
current = iterator.next();
} else if ('$' == current) {
type = Type.CONSTANT;
current = iterator.next();
} else
throw new ParserErrorException("expected '+', '-' or '$' but '" + current + "' found in '" + source + "'");
return new Placemarker.Builder(parseIdentifier()).setType(type).build();
}
private Quotation parseQuotation() throws ParserErrorException {
skip();
if (null == current)
throw new ParserErrorException("expected '\"' but EOF found in '" + source + "'");
if ('\"' != current)
throw new ParserErrorException("expected '\"' but '" + current + "' found in '" + source + "'");
String result = "" + current;
current = iterator.next();
while (null != current && '\"' != current) {
result += current;
current = iterator.next();
}
if (null == current)
throw new ParserErrorException("expected '\"' but EOF found in '" + source + "'");
result += current;
current = iterator.next();
return new Quotation.Builder(result).build();
}
private void parseRightParen() throws ParserErrorException {
skip();
if (null == current)
throw new ParserErrorException("expected ')' but EOF found in '" + source + "'");
if (')' != current)
throw new ParserErrorException("expected ')' but '" + current + "' found in '" + source + "'");
current = iterator.next();
}
private Scheme parseScheme() throws ParserErrorException {
Scheme.Builder result = new Scheme.Builder(parseIdentifier());
skip();
if (null != current && '(' == current) {
parseLeftParen();
result.addTerm(parseSchemeTerm());
skip();
while (null != current && ',' == current) {
parseComma();
result.addTerm(parseSchemeTerm());
skip();
}
parseRightParen();
}
return result.build();
}
private SchemeTerm parseSchemeTerm() throws ParserErrorException {
skip();
if (null == current)
throw new ParserErrorException("expected 'SCHEMETERM' but EOF found in '" + source + "'");
if (Character.isLowerCase(current))
return parseScheme();
if ('+' == current || '-' == current || '$' == current)
return parsePlacemarker();
if (Character.isDigit(current) || '-' == current)
return parseNumber();
if ('\"' == current)
return parseQuotation();
throw new ParserErrorException("expected 'SCHEMETERM' but '" + current + "' found in '" + source + "'");
}
private void parseSlash() throws ParserErrorException {
skip();
if (null == current)
throw new ParserErrorException("expected '/' but EOF found in '" + source + "'");
if ('/' != current)
throw new ParserErrorException("expected '/' but '" + current + "' found in '" + source + "'");
current = iterator.next();
}
private Term parseTerm() throws ParserErrorException {
skip();
if (null == current)
throw new ParserErrorException("expected 'TERM' but EOF found in '" + source + "'");
if (Character.isLowerCase(current))
return parseAtom();
if (Character.isUpperCase(current) || '_' == current)
return parseVariable();
if (Character.isDigit(current) || '-' == current)
return parseNumber();
if ('\"' == current)
return parseQuotation();
throw new ParserErrorException("expected 'TERM' but '" + current + "' found in '" + source + "'");
}
private Variable parseVariable() throws ParserErrorException {
skip();
if (null == current)
throw new ParserErrorException("expected 'A..Z' or '_' but EOF found in '" + source + "'");
if (!Character.isUpperCase(current) && '_' != current)
throw new ParserErrorException("expected 'A..Z' or '_' but '" + current + "' found in '" + source + "'");
String result = "";
while (null != current && (Character.isLowerCase(current) || Character.isUpperCase(current) || Character.isDigit(current) || '_' == current)) {
result += current;
current = iterator.next();
}
return new Variable.Builder(result).build();
}
private void skip() {
while (null != current && current <= ' ')
current = iterator.next();
}
}