|
@@ -1,6 +1,9 @@
|
|
package migl.lisp;
|
|
package migl.lisp;
|
|
|
|
|
|
|
|
+import java.math.BigInteger;
|
|
|
|
+import java.util.HashMap;
|
|
import java.util.LinkedList;
|
|
import java.util.LinkedList;
|
|
|
|
+import java.util.Map;
|
|
import java.util.Queue;
|
|
import java.util.Queue;
|
|
import java.util.regex.Matcher;
|
|
import java.util.regex.Matcher;
|
|
import java.util.regex.Pattern;
|
|
import java.util.regex.Pattern;
|
|
@@ -10,6 +13,128 @@ import migl.util.ConsListFactory;
|
|
|
|
|
|
public class LispImpl implements Lisp {
|
|
public class LispImpl implements Lisp {
|
|
|
|
|
|
|
|
+ /**
|
|
|
|
+ * 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("not", (lisp) -> {
|
|
|
|
+ if(lisp.size() != 1) {
|
|
|
|
+ throw new LispError("Incorrect number of argument");
|
|
|
|
+ }
|
|
|
|
+ boolean result = !getElement(lisp.car()).toBoolean();
|
|
|
|
+ return LispElement.generate(result);
|
|
|
|
+ });
|
|
|
|
+ operators.put("and", (lisp) -> {
|
|
|
|
+ if(lisp.size() != 2) {
|
|
|
|
+ throw new LispError("Incorrect number of argument");
|
|
|
|
+ }
|
|
|
|
+ boolean result = getElement(lisp.car()).toBoolean() && getElement(lisp.cdr().car()).toBoolean();
|
|
|
|
+ return LispElement.generate(result);
|
|
|
|
+ });
|
|
|
|
+ operators.put("or", (lisp) -> {
|
|
|
|
+ if(lisp.size() != 2) {
|
|
|
|
+ throw new LispError("Incorrect number of argument");
|
|
|
|
+ }
|
|
|
|
+ boolean result = getElement(lisp.car()).toBoolean() || getElement(lisp.cdr().car()).toBoolean();
|
|
|
|
+ return LispElement.generate(result);
|
|
|
|
+ });
|
|
|
|
+ operators.put(">", (lisp) -> {
|
|
|
|
+ if(lisp.size() != 2) {
|
|
|
|
+ throw new LispError("Incorrect number of argument");
|
|
|
|
+ }
|
|
|
|
+ boolean result = getElement(lisp.car()).toNumber() > getElement(lisp.cdr().car()).toNumber();
|
|
|
|
+ return LispElement.generate(result);
|
|
|
|
+ });
|
|
|
|
+ operators.put(">=", (lisp) -> {
|
|
|
|
+ if(lisp.size() != 2) {
|
|
|
|
+ throw new LispError("Incorrect number of argument");
|
|
|
|
+ }
|
|
|
|
+ boolean result = getElement(lisp.car()).toNumber() >= getElement(lisp.cdr().car()).toNumber();
|
|
|
|
+ return LispElement.generate(result);
|
|
|
|
+ });
|
|
|
|
+ operators.put("<", (lisp) -> {
|
|
|
|
+ if(lisp.size() != 2) {
|
|
|
|
+ throw new LispError("Incorrect number of argument");
|
|
|
|
+ }
|
|
|
|
+ boolean result = getElement(lisp.car()).toNumber() < getElement(lisp.cdr().car()).toNumber();
|
|
|
|
+ return LispElement.generate(result);
|
|
|
|
+ });
|
|
|
|
+ operators.put("<=", (lisp) -> {
|
|
|
|
+ if(lisp.size() != 2) {
|
|
|
|
+ throw new LispError("Incorrect number of argument");
|
|
|
|
+ }
|
|
|
|
+ boolean result = getElement(lisp.car()).toNumber() <= getElement(lisp.cdr().car()).toNumber();
|
|
|
|
+ return LispElement.generate(result);
|
|
|
|
+ });
|
|
|
|
+ operators.put("=", (lisp) -> {
|
|
|
|
+ if(lisp.size() != 2) {
|
|
|
|
+ throw new LispError("Incorrect number of argument");
|
|
|
|
+ }
|
|
|
|
+ boolean result = getElement(lisp.car()).toNumber() == getElement(lisp.cdr().car()).toNumber();
|
|
|
|
+ return LispElement.generate(result);
|
|
|
|
+ });
|
|
|
|
+ operators.put("+", (lisp) -> {
|
|
|
|
+ boolean isInt = true;
|
|
|
|
+ double result = 0;
|
|
|
|
+ while(!lisp.isEmpty()) {
|
|
|
|
+ LispElement<?> elt = getElement(lisp.car());
|
|
|
|
+ result += elt.toNumber();
|
|
|
|
+ isInt = isInt && elt.value.getClass() == BigInteger.class;
|
|
|
|
+ lisp = lisp.cdr();
|
|
|
|
+ }
|
|
|
|
+ if(isInt) {
|
|
|
|
+ return LispElement.generate((int) result);
|
|
|
|
+ }
|
|
|
|
+ return LispElement.generate(result);
|
|
|
|
+ });
|
|
|
|
+ operators.put("*", (lisp) -> {
|
|
|
|
+ boolean isInt = true;
|
|
|
|
+ double result = 1;
|
|
|
|
+ while(!lisp.isEmpty()) {
|
|
|
|
+ LispElement<?> elt = getElement(lisp.car());
|
|
|
|
+ result *= elt.toNumber();
|
|
|
|
+ isInt = isInt && elt.value.getClass() == BigInteger.class;
|
|
|
|
+ lisp = lisp.cdr();
|
|
|
|
+ }
|
|
|
|
+ if(isInt) {
|
|
|
|
+ return LispElement.generate((int) result);
|
|
|
|
+ }
|
|
|
|
+ return LispElement.generate(result);
|
|
|
|
+ });
|
|
|
|
+ operators.put("-", (lisp) -> {
|
|
|
|
+ switch(lisp.size()) {
|
|
|
|
+ case 1:
|
|
|
|
+ LispElement<?> elt = getElement(lisp.car());
|
|
|
|
+ if(elt.value.getClass() == Double.class) {
|
|
|
|
+ return LispElement.generate(elt.toNumber() * -1);
|
|
|
|
+ }
|
|
|
|
+ return LispElement.generate(elt.toInt().multiply(new BigInteger("-1")));
|
|
|
|
+ case 2:
|
|
|
|
+ LispElement<?> elt1 = getElement(lisp.car());
|
|
|
|
+ LispElement<?> elt2 = 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("Incorrect number of argument");
|
|
|
|
+ }
|
|
|
|
+ });
|
|
|
|
+ operators.put("/", (lisp) -> {
|
|
|
|
+ if(lisp.size() != 2) {
|
|
|
|
+ throw new LispError("Incorrect number of argument");
|
|
|
|
+ }
|
|
|
|
+ LispElement<?> elt1 = getElement(lisp.car());
|
|
|
|
+ LispElement<?> elt2 = 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().divide(elt2.toInt()));
|
|
|
|
+ });
|
|
|
|
+ }
|
|
|
|
+
|
|
@Override
|
|
@Override
|
|
public Object parse(String expr) throws LispError {
|
|
public Object parse(String expr) throws LispError {
|
|
//Analyse l'expression
|
|
//Analyse l'expression
|
|
@@ -32,14 +157,19 @@ public class LispImpl implements Lisp {
|
|
}
|
|
}
|
|
|
|
|
|
@Override
|
|
@Override
|
|
|
|
+ @SuppressWarnings("unchecked")
|
|
public Object evaluate(Object ex) throws LispError {
|
|
public Object evaluate(Object ex) throws LispError {
|
|
- return LispEval.evaluate(ex).value;
|
|
|
|
|
|
+ if(ex instanceof ConsList) {
|
|
|
|
+ return evaluate((ConsList<Object>) ex);
|
|
|
|
+ }
|
|
|
|
+ return evaluate(ConsListFactory.singleton(ex));
|
|
}
|
|
}
|
|
|
|
|
|
/* --- Verification --- */
|
|
/* --- Verification --- */
|
|
|
|
|
|
/**
|
|
/**
|
|
* Verifie que le format d'un String correspond bien à une expression Lisp
|
|
* Verifie que le format d'un String correspond bien à une expression Lisp
|
|
|
|
+ *
|
|
* @param expr Le String à examiner
|
|
* @param expr Le String à examiner
|
|
* @return
|
|
* @return
|
|
*/
|
|
*/
|
|
@@ -58,6 +188,7 @@ public class LispImpl implements Lisp {
|
|
|
|
|
|
/**
|
|
/**
|
|
* Verifie que le format d'un String correspond bien à une liste Lisp
|
|
* Verifie que le format d'un String correspond bien à une liste Lisp
|
|
|
|
+ *
|
|
* @param expr Le String à examiner
|
|
* @param expr Le String à examiner
|
|
* @return
|
|
* @return
|
|
*/
|
|
*/
|
|
@@ -86,6 +217,7 @@ public class LispImpl implements Lisp {
|
|
|
|
|
|
/**
|
|
/**
|
|
* Verifie que le format d'un String correspond bien à un element Lisp
|
|
* Verifie que le format d'un String correspond bien à un element Lisp
|
|
|
|
+ *
|
|
* @param expr Le String à examiner
|
|
* @param expr Le String à examiner
|
|
* @return
|
|
* @return
|
|
*/
|
|
*/
|
|
@@ -99,6 +231,7 @@ public class LispImpl implements Lisp {
|
|
|
|
|
|
/**
|
|
/**
|
|
* Découpe un string pour l'analyse Lisp
|
|
* Découpe un string pour l'analyse Lisp
|
|
|
|
+ *
|
|
* @param expr L'expression à analyser
|
|
* @param expr L'expression à analyser
|
|
* @return L'expression découpée
|
|
* @return L'expression découpée
|
|
*/
|
|
*/
|
|
@@ -137,6 +270,7 @@ public class LispImpl implements Lisp {
|
|
|
|
|
|
/**
|
|
/**
|
|
* Parse une file d'element en Lisp
|
|
* Parse une file d'element en Lisp
|
|
|
|
+ *
|
|
* @param queue La file avec les valeurs parser par explode
|
|
* @param queue La file avec les valeurs parser par explode
|
|
* @return Liste d'element
|
|
* @return Liste d'element
|
|
* @throws LispError Format invalide
|
|
* @throws LispError Format invalide
|
|
@@ -154,5 +288,42 @@ public class LispImpl implements Lisp {
|
|
}
|
|
}
|
|
return list;
|
|
return list;
|
|
}
|
|
}
|
|
|
|
+
|
|
|
|
+ /* --- Evaluation --- */
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * Evalue un liste d'élément lisp parser dans un ConsList
|
|
|
|
+ *
|
|
|
|
+ * @param lisp La liste parser {@link #parse(String)}
|
|
|
|
+ * @return Valeur évaluer
|
|
|
|
+ * @throws LispError
|
|
|
|
+ */
|
|
|
|
+ private static LispElement<?> evaluate(ConsList<Object> lisp) throws LispError {
|
|
|
|
+ LispOperator op = operators.get(LispElement.generate(lisp.car()).toStr());
|
|
|
|
+ if(op == null) {
|
|
|
|
+ throw new LispError(new UnsupportedOperationException("Unknow expression"));
|
|
|
|
+ }
|
|
|
|
+ try {
|
|
|
|
+ return op.apply(lisp.cdr());
|
|
|
|
+ } catch (IllegalStateException ex) {
|
|
|
|
+ throw new LispError(ex.getMessage(), ex);
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * 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")
|
|
|
|
+ private static LispElement<?> getElement(Object elt) throws LispError {
|
|
|
|
+ if(elt instanceof ConsList) {
|
|
|
|
+ return evaluate((ConsList<Object>) elt);
|
|
|
|
+ }
|
|
|
|
+ return LispElement.generate(elt);
|
|
|
|
+ }
|
|
|
|
|
|
}
|
|
}
|