|
@@ -0,0 +1,230 @@
|
|
|
+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.MinMaxOperator;
|
|
|
+import migl.util.ConsList;
|
|
|
+
|
|
|
+public class LispEval {
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Les operateurs gérés par l'interpréteur
|
|
|
+ */
|
|
|
+ private static Map<String, LispOperator> operators = new HashMap<>();
|
|
|
+ static {
|
|
|
+ //Définition des opérateurs
|
|
|
+ operators.put("define", new DefineOperator());
|
|
|
+ operators.put("set!", new DefineOperator());
|
|
|
+ operators.put("lambda", new DefineOperator());
|
|
|
+ operators.put("cons", new ConsOperator());
|
|
|
+ operators.put(">", new ComparatorOperator());
|
|
|
+ operators.put(">=", new ComparatorOperator());
|
|
|
+ operators.put("<", new ComparatorOperator());
|
|
|
+ operators.put("<=", new ComparatorOperator());
|
|
|
+ operators.put("=", new ComparatorOperator());
|
|
|
+ operators.put("min", new MinMaxOperator());
|
|
|
+ operators.put("max", new MinMaxOperator());
|
|
|
+ operators.put("quote", (op, lisp) -> {
|
|
|
+ if(lisp.size() != 1) {
|
|
|
+ throw new LispError(LispError.ERR_NUM_ARG);
|
|
|
+ }
|
|
|
+ return LispElement.generate(lisp.car().toString());
|
|
|
+ });
|
|
|
+ operators.put("if", (op, lisp) -> {
|
|
|
+ if(lisp.size() != 3) {
|
|
|
+ throw new LispError(LispError.ERR_NUM_ARG);
|
|
|
+ }
|
|
|
+ if(LispElement.getElement(lisp.car()).toBoolean()) {
|
|
|
+ return LispElement.getElement(lisp.cdr().car());
|
|
|
+ } else {
|
|
|
+ return LispElement.getElement(lisp.cdr().cdr().car());
|
|
|
+ }
|
|
|
+ });
|
|
|
+ operators.put("not", (op, lisp) -> {
|
|
|
+ if(lisp.size() != 1) {
|
|
|
+ throw new LispError(LispError.ERR_NUM_ARG);
|
|
|
+ }
|
|
|
+ boolean result = !LispElement.getElement(lisp.car()).toBoolean();
|
|
|
+ return LispElement.generate(result);
|
|
|
+ });
|
|
|
+ operators.put("and", (op, lisp) -> {
|
|
|
+ boolean result = true;
|
|
|
+ while(!lisp.isEmpty()) {
|
|
|
+ result = result && LispElement.getElement(lisp.car()).toBoolean();
|
|
|
+ lisp = lisp.cdr();
|
|
|
+ }
|
|
|
+ return LispElement.generate(result);
|
|
|
+ });
|
|
|
+ operators.put("or", (op, lisp) -> {
|
|
|
+ boolean result = false;
|
|
|
+ while(!lisp.isEmpty()) {
|
|
|
+ result = result || LispElement.getElement(lisp.car()).toBoolean();
|
|
|
+ lisp = lisp.cdr();
|
|
|
+ }
|
|
|
+ return LispElement.generate(result);
|
|
|
+ });
|
|
|
+ operators.put("+", (op, lisp) -> {
|
|
|
+ BigInteger resultInt = new BigInteger("0");
|
|
|
+ while(!lisp.isEmpty()){
|
|
|
+ LispElement eltInt = LispElement.getElement(lisp.car());
|
|
|
+ if(eltInt.value.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 = LispElement.getElement(lisp.car());
|
|
|
+ result += elt.toNumber();
|
|
|
+ lisp = lisp.cdr();
|
|
|
+ }
|
|
|
+ return LispElement.generate(result);
|
|
|
+ });
|
|
|
+ operators.put("*", (op, lisp) -> {
|
|
|
+ BigInteger resultInt = new BigInteger("1");
|
|
|
+ while(!lisp.isEmpty()){
|
|
|
+ LispElement eltInt = LispElement.getElement(lisp.car());
|
|
|
+ if(eltInt.value.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 = LispElement.getElement(lisp.car());
|
|
|
+ result *= elt.toNumber();
|
|
|
+ lisp = lisp.cdr();
|
|
|
+ }
|
|
|
+ return LispElement.generate(result);
|
|
|
+ });
|
|
|
+ operators.put("-", (op, lisp) -> {
|
|
|
+ switch(lisp.size()) {
|
|
|
+ case 1:
|
|
|
+ LispElement elt = LispElement.getElement(lisp.car());
|
|
|
+ if(elt.value.getClass() == Double.class) {
|
|
|
+ //return LispElement.generate(elt.toNumber() * -1); //Pb pitest qui remplace * par / or *-1 == /-1
|
|
|
+ double value = elt.toNumber();
|
|
|
+ return LispElement.generate(value - (value * 2));
|
|
|
+ }
|
|
|
+ return LispElement.generate(elt.toInt().multiply(new BigInteger("-1")));
|
|
|
+ case 2:
|
|
|
+ LispElement elt1 = LispElement.getElement(lisp.car());
|
|
|
+ LispElement elt2 = LispElement.getElement(lisp.cdr().car());
|
|
|
+ if(elt1.value.getClass() == Double.class || elt2.value.getClass() == Double.class) {
|
|
|
+ return LispElement.generate(elt1.toNumber() - elt2.toNumber());
|
|
|
+ }
|
|
|
+ return LispElement.generate(elt1.toInt().subtract(elt2.toInt()));
|
|
|
+ default:
|
|
|
+ throw new LispError(LispError.ERR_NUM_ARG);
|
|
|
+ }
|
|
|
+ });
|
|
|
+ operators.put("/", (op, lisp) -> {
|
|
|
+ if(lisp.size() != 2) {
|
|
|
+ throw new LispError(LispError.ERR_NUM_ARG);
|
|
|
+ }
|
|
|
+ LispElement elt1 = LispElement.getElement(lisp.car());
|
|
|
+ LispElement elt2 = LispElement.getElement(lisp.cdr().car());
|
|
|
+ if(elt2.toNumber() == 0) {
|
|
|
+ throw new LispError("Division by zero");
|
|
|
+ }
|
|
|
+ if(elt1.value.getClass() == Double.class || elt2.value.getClass() == Double.class) {
|
|
|
+ return LispElement.generate(elt1.toNumber() / elt2.toNumber());
|
|
|
+ }
|
|
|
+ return LispElement.generate(elt1.toInt().divide(elt2.toInt()));
|
|
|
+ });
|
|
|
+ }
|
|
|
+
|
|
|
+ private Object lispElt = null;
|
|
|
+
|
|
|
+ private ConsList<Object> lispList = null;
|
|
|
+
|
|
|
+ @SuppressWarnings("unchecked")
|
|
|
+ public LispEval(Object lisp) {
|
|
|
+ if(lisp == null) {
|
|
|
+ throw new IllegalArgumentException("Expression is null");
|
|
|
+ } else if(lisp instanceof ConsList) {
|
|
|
+ this.lispList = (ConsList<Object>) lisp;
|
|
|
+ } else {
|
|
|
+ this.lispElt = lisp;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ public LispElement evaluate() throws LispError {
|
|
|
+ if(this.lispElt != null) {
|
|
|
+ return this.evalElt();
|
|
|
+ }
|
|
|
+ return this.evalList();
|
|
|
+ }
|
|
|
+
|
|
|
+ public LispElement evalElt() throws LispError {
|
|
|
+ try {
|
|
|
+ return LispElement.getElement(this.lispElt);
|
|
|
+ } catch (IllegalArgumentException ex) {
|
|
|
+ throw new LispError(ex.getMessage(), ex);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ public LispElement evalList() throws LispError {
|
|
|
+ return evaluateList(this.lispList);
|
|
|
+ }
|
|
|
+
|
|
|
+ /* --- Méthode static utilisé pour l'évaluation (dans cette class et dans les autres) --- */
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Evalue un liste d'élément lisp parser dans un ConsList
|
|
|
+ *
|
|
|
+ * @param lisp La liste parser {@link #parse(String)}
|
|
|
+ * @return Valeur évaluer
|
|
|
+ * @throws LispError
|
|
|
+ */
|
|
|
+ public static LispElement evaluateList(ConsList<Object> lisp) throws LispError {
|
|
|
+ if(lisp.isEmpty()) {
|
|
|
+ return LispElement.generate("()");
|
|
|
+ }
|
|
|
+ String operator = LispElement.generate(lisp.car()).toStr();
|
|
|
+ LispOperator op = operators.get(operator);
|
|
|
+ if(op == null) {
|
|
|
+ if(DefineOperator.isLambda(operator)) {
|
|
|
+ return operators.get("lambda").apply(operator, lisp.cdr());
|
|
|
+ } else {
|
|
|
+ throw new LispError(operator + LispError.ERR_UNKNOW, new UnsupportedOperationException("Unknow operator"));
|
|
|
+ }
|
|
|
+ }
|
|
|
+ try {
|
|
|
+ return op.apply(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 static 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(operators.containsKey(name)) {
|
|
|
+ throw new LispError(name + LispError.ERR_INVALID);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+}
|