package nars.gui.input;
import java.util.Scanner;
import org.parboiled.BaseParser;
import static org.parboiled.BaseParser.EOI;
import org.parboiled.Parboiled;
import org.parboiled.Rule;
import org.parboiled.errors.InvalidInputError;
import org.parboiled.parserunners.RecoveringParseRunner;
import org.parboiled.support.MatcherPath;
import static org.parboiled.support.ParseTreeUtils.printNodeTree;
import org.parboiled.support.ParsingResult;
/**
* NARese, syntax and language for interacting with a NAR in NARS.
* @see https://code.google.com/p/open-nars/wiki/InputOutputFormat
*/
public class NarseseParser extends BaseParser<Object> {
public Rule Input() {
return sequence(zeroOrMore(Task()), WhiteSpace(), EOI);
}
public Rule Task() {
//TODO separate goal into an alternate form "!" because it does not use a tense
return sequence(
WhiteSpace(),
optional(Budget()),
WhiteSpace(),
Term(),
SentenceTypeChar(),
WhiteSpace(),
//optional(Tense())
optional(Truth()));
}
Rule Budget() {
return sequence("%", ShortFloat(), optional(sequence(";", ShortFloat(), optional(sequence(";", ShortFloat())))));
}
Rule Truth() {
return sequence("%", ShortFloat(), optional(sequence(";", ShortFloat())));
}
Rule ShortFloat() {
//TODO use more specific shortfloat number
return Number();
}
Rule SentenceTypeChar() {
return anyOf(".?!");
}
Rule OperationExecution() {
//TODO
// "(^"<word> {","<term>} ")" // (an operation to be executed) */
return string("(^)");
}
Rule Copula() {
/*<copula> ::= "-->" // inheritance
| "<->" // similarity
| "{--" // instance
| "--]" // property
| "{-]" // instance-property
| "==>" // implication
| "=/>" // (predictive implication)
| "=|>" // (concurrent implication)
| "=\>" // (retrospective implication)
| "<=>" // equivalence
| "</>" // (predictive equivalence)
| "<|>" // (concurrent equivalence)*/
//TODO use separate rules for each so a parse can identify them
return sequence("<", WhiteSpace(), Term(), WhiteSpace(), CopulaOperator(), WhiteSpace(), Term(), WhiteSpace(),">");
}
Rule CopulaOperator() {
return firstOf(string("-->"), string("<->"));
}
Rule Term() {
/*
<term> ::= <word> // an atomic constant term
| <variable> // an atomic variable term
| <compound-term> // a term with internal structure
| <statement> // a statement can serve as a term
*/
return firstOf(Literal(), QuotedLiteral(), Variable(), CompoundTerm(), Copula(), OperationExecution());
}
Rule Literal() {
return oneOrMore(noneOf(" ,.!?<>-=|&()<>[]{}#$\""));
}
Rule QuotedLiteral() {
return sequence("\"", AnyString(), "\"");
}
Rule AnyString() {
//TODO handle \" escape
return oneOrMore(noneOf("\""));
}
Rule Variable() {
/*
<variable> ::= "$"<word> // independent variable
| "#"[<word>] // dependent variable
| "?"[<word>] // query variable in question
*/
return firstOf(IndependentVariable(), DependentVariable(), QueryVariable());
}
Rule IndependentVariable() { return sequence("$", Literal()); }
Rule DependentVariable() { return sequence("#", optional(Literal())); }
Rule QueryVariable() { return sequence("?", optional(Literal())); }
Rule CompoundTerm() {
/*
<compound-term> ::= "{" <term> {","<term>} "}" // extensional set
| "[" <term> {","<term>} "]" // intensional set
| "(&," <term> {","<term>} ")" // extensional intersection
| "(|," <term> {","<term>} ")" // intensional intersection
| "(*," <term> {","<term>} ")" // product
| "(/," <term> {","<term>} ")" // extensional image
| "(\," <term> {","<term>} ")" // intensional image
| "(||," <term> {","<term>} ")" // disjunction
| "(&&," <term> {","<term>} ")" // conjunction
| "(&/," <term> {","<term>} ")" // (sequential events)
| "(&|," <term> {","<term>} ")" // (parallel events)
| "(--," <term> ")" // negation
| "(-," <term> "," <term> ")" // extensional difference
| "(~," <term> "," <term> ")" // intensional difference
*/
return sequence("{", Term(), zeroOrMore(sequence(",",Term())),"}");
//TODO add the remaining ones
}
Rule Number() {
return sequence(
optional('-'),
oneOrMore(Digit()),
optional('.', oneOrMore(Digit()))
);
}
Rule Digit() {
return charRange('0', '9');
}
Rule WhiteSpace() {
return zeroOrMore(anyOf(" \t\f\n"));
}
public static NarseseParser newParser() {
return Parboiled.createParser(NarseseParser.class);
}
public static void main(String[] args) {
NarseseParser p = NarseseParser.newParser();
Scanner sc = new Scanner(System.in);
while (true) {
String input = sc.nextLine();
RecoveringParseRunner rpr = new RecoveringParseRunner(p.Input());
ParsingResult r = rpr.run(input);
System.out.println("valid? " + (r.matched && (r.parseErrors.isEmpty())) );
for (Object e : r.parseErrors) {
if (e instanceof InvalidInputError) {
InvalidInputError iie = (InvalidInputError) e;
System.err.println(e);
if (iie.getErrorMessage()!=null)
System.err.println(iie.getErrorMessage());
for (MatcherPath m : iie.getFailedMatchers()) {
System.err.println(" ?-> " + m);
}
System.err.println(" at: " + iie.getStartIndex() + " to " + iie.getEndIndex());
}
else {
System.err.println(e);
}
}
System.out.println(printNodeTree(r));
}
}
}