package nars.io;
import com.google.common.collect.Iterators;
import static com.google.common.collect.Iterators.singletonIterator;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import nars.util.EventEmitter.EventObserver;
import nars.util.Events;
import nars.util.Events.Perceive;
import nars.storage.Memory;
import nars.NAR;
import nars.config.Parameters;
import nars.entity.Item;
import nars.util.Plugin;
import nars.entity.Sentence;
import nars.entity.Task;
import nars.io.Output.IN;
import nars.io.Narsese.InvalidInputException;
/*import nars.io.nlp.Englisch;
import nars.io.nlp.NaturalLanguagePerception;
import nars.io.nlp.Twenglish;*/
/**
* Default handlers for text perception.
* Parses input text into sequences of AbstractTask's which input into
* Memory via NAR input channel & buffer port.
*
* TODO break into separate subclasses for each text mode
*/
public class DefaultTextPerception implements Plugin, EventObserver {
private Memory memory;
public List<TextReaction> parsers;
public Narsese narsese;
//public Englisch englisch;
//public Twenglish twenglish;
private boolean enableNarsese = true;
private boolean enableNaturalLanguage = true; //the NLP mode we should strive for
private boolean enableEnglisch = false;
private boolean enableTwenglish = false; //the events should be introduced event-wise
//or with a higher order copula a1...an-1 =/> an, because a &/ statement alone is useless for temporal inference
@Override
public boolean setEnabled(NAR n, boolean enabled) {
if (enabled) {
this.memory = n.memory;
this.narsese = new Narsese(memory);
//this.englisch = new Englisch();
//this.twenglish = new Twenglish(memory);
this.parsers = getParsers();
}
n.memory.event.set(this, enabled, Events.Perceive.class);
return true;
}
@Override
public void event(Class event, Object[] arguments) {
if (event == Perceive.class) {
Object o = arguments[1];
InPort i = (InPort)arguments[0];
Iterator<Item> it = i.postprocess( perceive(o) );
if (it!=null)
while (it.hasNext())
i.queue(it.next());
}
}
/* Perceive an input object by calling an appropriate perception system according to the object type. */
public Iterator<? extends Item> perceive(final Object o) {
Exception error;
try {
if (o instanceof String) {
return perceive((String) o);
} else if (o instanceof Sentence) {
//TEMPORARY
Sentence s = (Sentence) o;
return perceive(s.term.toString() + s.punctuation + " " + s.truth.toString());
} else if (o instanceof Task) {
return Iterators.forArray((Task)o);
}
error = new IOException("Input unrecognized: " + o + " [" + o.getClass() + "]");
}
catch (Exception e) {
if (Parameters.DEBUG)
throw e;
error = e;
}
return singletonIterator(new Echo(Output.ERR.class, error) );
}
public List<TextReaction> getParsers() {
ArrayList<TextReaction> parsers = new ArrayList();
//integer, # of cycles to step
parsers.add(new TextReaction() {
final String spref = Symbols.INPUT_LINE_PREFIX + ':';
@Override public Object react(String input) {
input = input.trim();
if (input.startsWith(spref))
input = input.substring(spref.length());
if (!Character.isDigit(input.charAt(0)))
return null;
if (input.length() > 8) {
//if input > ~8 chars it wont fit as 32bit integer anyway so terminate early.
//parseInt is sort of expensive
return null;
}
try {
int cycles = Integer.parseInt(input);
return new PauseInput(cycles);
}
catch (NumberFormatException e) { }
return null;
}
});
//reset
parsers.add(new TextReaction() {
@Override public Object react(String input) {
if (input.equals(Symbols.RESET_COMMAND) || (input.startsWith("*") && !input.startsWith("*start") && !input.startsWith("*decisionthreshold")
&& !input.startsWith("*stop") && !input.startsWith("*volume"))) //TODO!
return new Reset(input);
return null;
}
});
//reboot
parsers.add(new TextReaction() {
@Override public Object react(String input) {
if (input.equals(Symbols.REBOOT_COMMAND)) {
//immediately reset the memory
memory.emit(IN.class, "reboot");
memory.reset();
return new Reboot();
}
return null;
}
});
// TODO implement these with AbstractTask's
// //stop
// parsers.add(new TextReaction() {
// @Override
// public Object react(String input) {
// if (!memory.isWorking()) {
// if (input.equals(Symbols.STOP_COMMAND)) {
// memory.output(Output.IN.class, input);
// memory.setWorking(false);
// return Boolean.TRUE;
// }
// }
// return null;
// }
// });
//
// //start
// parsers.add(new TextReaction() {
// @Override public Object react(String input) {
// if (memory.isWorking()) {
// if (input.equals(Symbols.START_COMMAND)) {
// memory.setWorking(true);
// memory.output(Output.IN.class, input);
// return Boolean.TRUE;
// }
// }
// return null;
// }
// });
//silence
parsers.add(new TextReaction() {
@Override public Object react(String input) {
if (input.indexOf(Symbols.SET_NOISE_LEVEL_COMMAND)==0) {
String[] p = input.split("=");
if (p.length == 2) {
int noiseLevel = Integer.parseInt(p[1].trim());
return new SetVolume(noiseLevel);
}
}
if (input.indexOf(Symbols.SET_DECISION_LEVEL_COMMAND)==0) {
String[] p = input.split("=");
if (p.length == 2) {
double threshold = Double.parseDouble(p[1].trim());
return new SetDecisionThreshold(threshold);
}
}
return null;
}
});
// //URL include
// parsers.add(new TextReaction() {
// @Override
// public Object react(Memory m, String input) {
// char c = input.charAt(0);
// if (c == Symbols.URL_INCLUDE_MARK) {
// try {
// nar.addInput(new TextInput(new URL(input.substring(1))));
// } catch (IOException ex) {
// m.output(ERR.class, ex);
// }
// return true;
// }
// return false;
// }
// });
//echo
//TODO standardize on an echo/comment format
parsers.add(new TextReaction() {
@Override
public Object react(String input) {
char c = input.charAt(0);
if (c == Symbols.ECHO_MARK) {
String echoString = input.substring(1);
return new Echo(Echo.class, echoString);
}
final String it = input.trim();
if (it.startsWith("OUT:") || it.startsWith("//") || it.startsWith("***") ) {
return new Echo(Echo.class, input);
}
return null;
}
});
parsers.add(new BindJavascriptExpression(memory));
//narsese
parsers.add(new TextReaction() {
@Override
public Object react(String input) {
if (enableNarsese) {
char c = input.charAt(0);
if (c != Symbols.COMMENT_MARK) {
try {
Item task = narsese.parseNarsese(new StringBuilder(input));
if (task != null) {
return task;
}
} catch (InvalidInputException ex) {
return ex;
}
}
}
return null;
}
});
//englisch
parsers.add(new TextReaction() {
@Override
public Object react(String line) {
/*if (enableEnglisch) {
//if (!possiblyNarsese(line))
{
List<AbstractTask> l;
try {
l = englisch.parse(line, narsese, true);
if ((l == null) || (l.isEmpty()))
return null;
return l;
} catch (InvalidInputException ex) {
return null;
}
}
}*/
return null;
}
});
//englisch
parsers.add(new TextReaction() {
@Override
public Object react(String line) {
/*if (enableTwenglish) {
//if (!possiblyNarsese(line))
{
List<AbstractTask> l;
try {
l = twenglish.parse(line, narsese, true);
if ((l == null) || (l.isEmpty()))
return null;
return l;
} catch (InvalidInputException ex) {
return null;
}
}
}*/
return null;
}
});
// natural language
parsers.add(new TextReaction() {
@Override
public Object react(String line) {
/*if (enableNaturalLanguage) {
//if (!possiblyNarsese(line))
{
List<AbstractTask> l = NaturalLanguagePerception.parseLine(line, narsese, "word");
if ((l == null) || (l.isEmpty()))
return null;
return l;
}
}*/
return null;
}
});
return parsers;
}
protected Iterator<Item> perceive(final String line) {
Exception lastException = null;
for (final TextReaction p : parsers) {
Object result = p.react(line);
if (result!=null) {
if (result instanceof Iterator) {
return (Iterator<Item>)result;
}
if (result instanceof Collection) {
return ((Collection<Item>)result).iterator();
}
if (result instanceof Item) {
return singletonIterator((Item)result);
}
else if (result.equals(Boolean.TRUE)) {
return null;
}
else if (result instanceof Exception) {
lastException = (Exception)result;
}
}
}
String errorMessage = "Invalid input: \'" + line + "\'";
if (lastException!=null) {
errorMessage += " : " + lastException.toString();
}
memory.emit(Output.ERR.class, errorMessage);
return null;
}
public void enableEnglisch(boolean enableEnglisch) {
this.enableEnglisch = enableEnglisch;
}
public void enableNarsese(boolean enableNarsese) {
this.enableNarsese = enableNarsese;
}
}