|
@@ -0,0 +1,407 @@
|
|
|
+package migl.lisp;
|
|
|
+
|
|
|
+import java.math.BigInteger;
|
|
|
+import java.util.HashMap;
|
|
|
+import java.util.Map;
|
|
|
+
|
|
|
+import migl.lisp.operator.ComparatorOperator;
|
|
|
+import migl.lisp.operator.ConsOperator;
|
|
|
+import migl.lisp.operator.DefineOperator;
|
|
|
+import migl.lisp.operator.LispOperator;
|
|
|
+import migl.lisp.operator.MathOperator;
|
|
|
+import migl.lisp.operator.MinMaxOperator;
|
|
|
+import migl.util.ConsList;
|
|
|
+import migl.util.ConsListFactory;
|
|
|
+
|
|
|
+/**
|
|
|
+ * Evaluateur lisp
|
|
|
+ * @author Arthur Brandao
|
|
|
+ */
|
|
|
+public class LispEval {
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Les operateurs gérés par l'interpréteur
|
|
|
+ */
|
|
|
+ private Map<String, LispOperator> operators = new HashMap<>();
|
|
|
+
|
|
|
+ /**
|
|
|
+ * L'interpreteur utilisant cet evaluateur pour les appels récursifs
|
|
|
+ */
|
|
|
+ private final Lisp interpreter;
|
|
|
+
|
|
|
+ /**
|
|
|
+ * L'instance de gestion des variables et des fonctions
|
|
|
+ */
|
|
|
+ private final DefineOperator define = new DefineOperator();
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Un element lisp
|
|
|
+ */
|
|
|
+ private Object lispElt = null;
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Une liste d'elements lisp
|
|
|
+ */
|
|
|
+ private ConsList<Object> lispList = null;
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Creation d'un evaluateur lisp
|
|
|
+ */
|
|
|
+ public LispEval(Lisp interpreter) {
|
|
|
+ this(interpreter, null);
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Creation d'un evaluateur lisp
|
|
|
+ * @param lisp Un element lisp (Object) ou une liste de lisp (ConsList<Object>)
|
|
|
+ */
|
|
|
+ public LispEval(Lisp interpreter, Object lisp) {
|
|
|
+ this.interpreter = interpreter;
|
|
|
+ if(lisp != null) {
|
|
|
+ this.setLisp(lisp);
|
|
|
+ }
|
|
|
+ this.setUpOperators();
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Change l'objet lisp à evaluer
|
|
|
+ * @param lisp Un element lisp (Object) ou une liste de lisp (ConsList<Object>)
|
|
|
+ */
|
|
|
+ @SuppressWarnings("unchecked")
|
|
|
+ public final void setLisp(Object lisp) {
|
|
|
+ if(lisp == null) {
|
|
|
+ throw new IllegalArgumentException("Expression is null");
|
|
|
+ } else if(lisp instanceof ConsList) {
|
|
|
+ this.lispElt = null;
|
|
|
+ this.lispList = (ConsList<Object>) lisp;
|
|
|
+ } else {
|
|
|
+ this.lispElt = lisp;
|
|
|
+ this.lispList = null;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Définit les opérateurs reconnus par l'évaluateur
|
|
|
+ */
|
|
|
+ private final void setUpOperators() {
|
|
|
+ operators.put("define", this.define);
|
|
|
+ operators.put("set!", this.define);
|
|
|
+ operators.put("lambda", this.define);
|
|
|
+ ConsOperator cons = new ConsOperator();
|
|
|
+ operators.put("cons", cons);
|
|
|
+ operators.put("car", cons);
|
|
|
+ operators.put("cdr", cons);
|
|
|
+ ComparatorOperator comp = new ComparatorOperator();
|
|
|
+ operators.put(">", comp);
|
|
|
+ operators.put(">=", comp);
|
|
|
+ operators.put("<", comp);
|
|
|
+ operators.put("<=", comp);
|
|
|
+ operators.put("=", comp);
|
|
|
+ MinMaxOperator minMax = new MinMaxOperator();
|
|
|
+ operators.put("min", minMax);
|
|
|
+ operators.put("max", minMax);
|
|
|
+ MathOperator math = new MathOperator();
|
|
|
+ operators.put("cbrt", math);
|
|
|
+ operators.put("ceil", math);
|
|
|
+ operators.put("floor", math);
|
|
|
+ operators.put("log10", math);
|
|
|
+ operators.put("cos", math);
|
|
|
+ operators.put("rint", math);
|
|
|
+ operators.put("round", math);
|
|
|
+ operators.put("signum", math);
|
|
|
+ operators.put("quote", (eval, op, lisp) -> {
|
|
|
+ if(lisp.size() != 1) {
|
|
|
+ throw new LispError(LispError.ERR_NUM_ARG);
|
|
|
+ }
|
|
|
+ return LispElement.generate(lisp.car().toString());
|
|
|
+ });
|
|
|
+ operators.put("if", (eval, op, lisp) -> {
|
|
|
+ if(lisp.size() != 3) {
|
|
|
+ throw new LispError(LispError.ERR_NUM_ARG);
|
|
|
+ }
|
|
|
+ if(this.getElement(lisp.car()).toBoolean()) {
|
|
|
+ return eval.getElement(lisp.cdr().car());
|
|
|
+ } else {
|
|
|
+ return eval.getElement(lisp.cdr().cdr().car());
|
|
|
+ }
|
|
|
+ });
|
|
|
+ operators.put("not", (eval, op, lisp) -> {
|
|
|
+ if(lisp.size() != 1) {
|
|
|
+ throw new LispError(LispError.ERR_NUM_ARG);
|
|
|
+ }
|
|
|
+ boolean result = !eval.getElement(lisp.car()).toBoolean();
|
|
|
+ return LispElement.generate(result);
|
|
|
+ });
|
|
|
+ operators.put("and", (eval, op, lisp) -> {
|
|
|
+ boolean result = true;
|
|
|
+ while(!lisp.isEmpty()) {
|
|
|
+ result = result && eval.getElement(lisp.car()).toBoolean();
|
|
|
+ lisp = lisp.cdr();
|
|
|
+ }
|
|
|
+ return LispElement.generate(result);
|
|
|
+ });
|
|
|
+ operators.put("or", (eval, op, lisp) -> {
|
|
|
+ boolean result = false;
|
|
|
+ while(!lisp.isEmpty()) {
|
|
|
+ result = result || eval.getElement(lisp.car()).toBoolean();
|
|
|
+ lisp = lisp.cdr();
|
|
|
+ }
|
|
|
+ return LispElement.generate(result);
|
|
|
+ });
|
|
|
+ operators.put("+", (eval, op, lisp) -> {
|
|
|
+ BigInteger resultInt = new BigInteger("0");
|
|
|
+ while(!lisp.isEmpty()){
|
|
|
+ LispElement eltInt = eval.getElement(lisp.car());
|
|
|
+ if(eltInt.getValue().getClass() != BigInteger.class) break;
|
|
|
+ resultInt = resultInt.add(eltInt.toInt());
|
|
|
+ lisp = lisp.cdr();
|
|
|
+ }
|
|
|
+ //Si on finit la liste avec que des entier on retourne
|
|
|
+ if(lisp.isEmpty()) return LispElement.generate(resultInt);
|
|
|
+ //Sinon on continue en passant en double
|
|
|
+ double result = resultInt.doubleValue();
|
|
|
+ while(!lisp.isEmpty()) {
|
|
|
+ LispElement elt = eval.getElement(lisp.car());
|
|
|
+ result += elt.toNumber();
|
|
|
+ lisp = lisp.cdr();
|
|
|
+ }
|
|
|
+ return LispElement.generate(result);
|
|
|
+ });
|
|
|
+ operators.put("*", (eval, op, lisp) -> {
|
|
|
+ BigInteger resultInt = new BigInteger("1");
|
|
|
+ while(!lisp.isEmpty()){
|
|
|
+ LispElement eltInt = eval.getElement(lisp.car());
|
|
|
+ if(eltInt.getValue().getClass() != BigInteger.class) break;
|
|
|
+ resultInt = resultInt.multiply(eltInt.toInt());
|
|
|
+ lisp = lisp.cdr();
|
|
|
+ }
|
|
|
+ //Si on finit la liste avec que des entier on retourne
|
|
|
+ if(lisp.isEmpty()) return LispElement.generate(resultInt);
|
|
|
+ //Sinon on continue en passant en double
|
|
|
+ double result = resultInt.doubleValue();
|
|
|
+ while(!lisp.isEmpty()) {
|
|
|
+ LispElement elt = eval.getElement(lisp.car());
|
|
|
+ result *= elt.toNumber();
|
|
|
+ lisp = lisp.cdr();
|
|
|
+ }
|
|
|
+ return LispElement.generate(result);
|
|
|
+ });
|
|
|
+ operators.put("-", (eval, op, lisp) -> {
|
|
|
+ switch(lisp.size()) {
|
|
|
+ case 1:
|
|
|
+ LispElement elt = eval.getElement(lisp.car());
|
|
|
+ if(elt.isInt()) {
|
|
|
+ return LispElement.generate(elt.toInt().multiply(new BigInteger("-1")));
|
|
|
+ }
|
|
|
+ return LispElement.generate(elt.toNumber() * -1); //Pb pitest qui remplace * par / or *-1 == /-1
|
|
|
+ case 2:
|
|
|
+ LispElement elt1 = eval.getElement(lisp.car());
|
|
|
+ LispElement elt2 = eval.getElement(lisp.cdr().car());
|
|
|
+ if(elt1.isInt() && elt2.isInt()) {
|
|
|
+ return LispElement.generate(elt1.toInt().subtract(elt2.toInt()));
|
|
|
+ }
|
|
|
+ return LispElement.generate(elt1.toNumber() - elt2.toNumber());
|
|
|
+ default:
|
|
|
+ throw new LispError(LispError.ERR_NUM_ARG);
|
|
|
+ }
|
|
|
+ });
|
|
|
+ operators.put("/", (eval, op, lisp) -> {
|
|
|
+ if(lisp.size() != 2) {
|
|
|
+ throw new LispError(LispError.ERR_NUM_ARG);
|
|
|
+ }
|
|
|
+ LispElement elt1 = eval.getElement(lisp.car());
|
|
|
+ LispElement elt2 = eval.getElement(lisp.cdr().car());
|
|
|
+ if(elt2.toNumber() == 0) {
|
|
|
+ throw new LispError("Division by zero");
|
|
|
+ }
|
|
|
+ if(elt1.isInt() && elt2.isInt()) {
|
|
|
+ return LispElement.generate(elt1.toInt().divide(elt2.toInt()));
|
|
|
+ }
|
|
|
+ return LispElement.generate(elt1.toNumber() / elt2.toNumber());
|
|
|
+ });
|
|
|
+ operators.put("abs", (eval, op, lisp) -> {
|
|
|
+ if(lisp.size() != 1) {
|
|
|
+ throw new LispError(LispError.ERR_NUM_ARG);
|
|
|
+ }
|
|
|
+ LispElement le = eval.getElement(lisp.car());
|
|
|
+ if(le.isInt()) {
|
|
|
+ return LispElement.generate(le.toInt().abs());
|
|
|
+ }
|
|
|
+ return LispElement.generate(Math.abs(le.toNumber()));
|
|
|
+ });
|
|
|
+ operators.put("pow", (eval, op, lisp) -> {
|
|
|
+ if(lisp.size() != 2) {
|
|
|
+ throw new LispError(LispError.ERR_NUM_ARG);
|
|
|
+ }
|
|
|
+ return LispElement.generate(Math.pow(eval.getElement(lisp.car()).toNumber(), eval.getElement(lisp.cdr().car()).toNumber()));
|
|
|
+ });
|
|
|
+ operators.put("list", (eval, op, lisp) -> {
|
|
|
+ LispList list = LispList.nil();
|
|
|
+ while(!lisp.isEmpty()) {
|
|
|
+ list.append(eval.getElement(lisp.car()));
|
|
|
+ lisp = lisp.cdr();
|
|
|
+ }
|
|
|
+ return LispElement.generate(list);
|
|
|
+ });
|
|
|
+ operators.put("map", (eval, op, lisp) -> {
|
|
|
+ if(lisp.size() != 2) {
|
|
|
+ throw new LispError(LispError.ERR_NUM_ARG);
|
|
|
+ }
|
|
|
+ //Regarde si le parametre est une fonction lambda
|
|
|
+ if(!eval.getDefine().isLambda(lisp.car())) {
|
|
|
+ throw new LispError(lisp.car() + LispError.ERR_INVALID);
|
|
|
+ }
|
|
|
+ //Regarde si le second parametre est une expression lisp à evaluer
|
|
|
+ if(!(lisp.cdr().car() instanceof ConsList)) {
|
|
|
+ throw new LispError(lisp.cdr().car() + LispError.ERR_INVALID);
|
|
|
+ }
|
|
|
+ //Evalue l'expression la valeurs des parametres
|
|
|
+ @SuppressWarnings("unchecked")
|
|
|
+ LispElement res = eval.evaluateList((ConsList<Object>) lisp.cdr().car());
|
|
|
+ if(!res.isList()) {
|
|
|
+ return LispElement.generate(LispList.nil());
|
|
|
+ }
|
|
|
+ //Pour chaque element de la liste evalue la fonction
|
|
|
+ LispList result = LispList.nil();
|
|
|
+ LispList list = res.toList();
|
|
|
+ for(int i = 0; i < list.size(); i++) {
|
|
|
+ ConsList<Object> cl = ConsListFactory.asList(lisp.car(), list.get(i));
|
|
|
+ result.append(eval.evaluateList(cl));
|
|
|
+ }
|
|
|
+ return LispElement.generate(result);
|
|
|
+ });
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Evalue l'element lisp
|
|
|
+ * @return L'element retourner par l'évaluation
|
|
|
+ * @throws LispError
|
|
|
+ */
|
|
|
+ public LispElement evaluate() throws LispError {
|
|
|
+ if(this.lispElt != null) {
|
|
|
+ return this.evalElt();
|
|
|
+ } else if(this.lispList != null) {
|
|
|
+ return this.evaluateList();
|
|
|
+ }
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Evalue un element
|
|
|
+ * @return L'element retourner par l'évaluation
|
|
|
+ * @throws LispError
|
|
|
+ */
|
|
|
+ public LispElement evalElt() throws LispError {
|
|
|
+ try {
|
|
|
+ return this.getElement(this.lispElt);
|
|
|
+ } catch (IllegalArgumentException ex) {
|
|
|
+ throw new LispError(ex.getMessage(), ex);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Evalue une liste d'elements lisp
|
|
|
+ * @return L'element retourner par l'évaluation
|
|
|
+ * @throws LispError
|
|
|
+ */
|
|
|
+ public LispElement evaluateList() throws LispError {
|
|
|
+ return this.evaluateList(this.lispList);
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Evalue un liste d'élément lisp parser dans un ConsList
|
|
|
+ *
|
|
|
+ * @param lisp La liste parser {@link #parse(String)}
|
|
|
+ * @return Valeur évaluer
|
|
|
+ * @throws LispError
|
|
|
+ */
|
|
|
+ @SuppressWarnings("unchecked")
|
|
|
+ public LispElement evaluateList(ConsList<Object> lisp) throws LispError {
|
|
|
+ //Expression vide
|
|
|
+ if(lisp.isEmpty()) {
|
|
|
+ return LispElement.generate("()");
|
|
|
+ }
|
|
|
+ //LCPF
|
|
|
+ if(lisp.car() instanceof ConsList) {
|
|
|
+ LispElement elt = this.evaluateList((ConsList<Object>) lisp.car());
|
|
|
+ ConsList<Object> cl = ConsListFactory.asList(elt.toStr(), lisp.cdr().car());
|
|
|
+ return this.evaluateList(cl);
|
|
|
+ }
|
|
|
+ //Recherche l'operateur
|
|
|
+ String operator = LispElement.generate(lisp.car()).toStr();
|
|
|
+ LispOperator op = this.operators.get(operator);
|
|
|
+ if(op == null) {
|
|
|
+ //On suppose que l'operateur est une expression lambda
|
|
|
+ return this.operators.get("lambda").apply(this, operator, lisp.cdr());
|
|
|
+ }
|
|
|
+ try {
|
|
|
+ return op.apply(this, operator, lisp.cdr());
|
|
|
+ } catch (IllegalStateException ex) {
|
|
|
+ throw new LispError(ex.getMessage(), ex);
|
|
|
+ } catch (IllegalArgumentException ex) {
|
|
|
+ throw new LispError("List Lisp malformed: " + ex.getMessage(), ex);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Verifie que le nom pour une variable n'est pas interdit
|
|
|
+ *
|
|
|
+ * @param name Nom pour la variable
|
|
|
+ * @throws LispError Si le nom n'est pas valide
|
|
|
+ */
|
|
|
+ public void verifyForbiddenName(String name) throws LispError {
|
|
|
+ //Verifie que ce n'est pas une valeur (double, bool, ...)
|
|
|
+ try {
|
|
|
+ LispElement.valueOf(name).toStr();
|
|
|
+ } catch (IllegalStateException ex) {
|
|
|
+ throw new LispError(name + LispError.ERR_INVALID, ex);
|
|
|
+ }
|
|
|
+ //Verifie que ce n'est pas le nom d'un operateur
|
|
|
+ if(this.operators.containsKey(name)) {
|
|
|
+ throw new LispError(name + LispError.ERR_INVALID);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Récupère un élément dans la liste lisp
|
|
|
+ * Si l'element est une liste elle seras alors analyser
|
|
|
+ *
|
|
|
+ * @param elt L'element à récupérer
|
|
|
+ * @return L'element ou le resultat de l'analyse si l'element est une liste
|
|
|
+ * @throws LispError
|
|
|
+ */
|
|
|
+ @SuppressWarnings("unchecked")
|
|
|
+ public LispElement getElement(Object elt) throws LispError {
|
|
|
+ if(elt instanceof ConsList) {
|
|
|
+ return this.evaluateList((ConsList<Object>) elt);
|
|
|
+ } else if(elt instanceof String) {
|
|
|
+ //Si c'est un string qui n'est pas un nom reservé
|
|
|
+ String str = (String) elt;
|
|
|
+ boolean skip = false;
|
|
|
+ try {
|
|
|
+ this.verifyForbiddenName(str);
|
|
|
+ } catch (LispError ex) {
|
|
|
+ skip = true;
|
|
|
+ }
|
|
|
+ if(!skip) return this.define.eval(elt);
|
|
|
+ }
|
|
|
+ return LispElement.generate(elt);
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Retourne l'interpreteur utilisant cet évaluateur
|
|
|
+ * @return
|
|
|
+ */
|
|
|
+ public Lisp getInterpreter() {
|
|
|
+ return this.interpreter;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Retourne l'instance de gestion des variables et des fonctions
|
|
|
+ * @return
|
|
|
+ */
|
|
|
+ public DefineOperator getDefine() {
|
|
|
+ return this.define;
|
|
|
+ }
|
|
|
+
|
|
|
+}
|