|
@@ -16,16 +16,73 @@ import migl.util.ConsList;
|
|
|
* @author Arthur Brandao
|
|
|
*/
|
|
|
public class LispEval {
|
|
|
-
|
|
|
+
|
|
|
/**
|
|
|
* Les operateurs gérés par l'interpréteur
|
|
|
*/
|
|
|
- private static Map<String, LispOperator> operators = new HashMap<>();
|
|
|
- static {
|
|
|
+ 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;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ private final void setUpOperators() {
|
|
|
//Définition des opérateurs
|
|
|
- operators.put("define", new DefineOperator());
|
|
|
- operators.put("set!", new DefineOperator());
|
|
|
- operators.put("lambda", new DefineOperator());
|
|
|
+ operators.put("define", this.define);
|
|
|
+ operators.put("set!", this.define);
|
|
|
+ operators.put("lambda", this.define);
|
|
|
operators.put("cons", new ConsOperator());
|
|
|
operators.put(">", new ComparatorOperator());
|
|
|
operators.put(">=", new ComparatorOperator());
|
|
@@ -42,108 +99,108 @@ public class LispEval {
|
|
|
operators.put("rint", new MathOperator());
|
|
|
operators.put("round", new MathOperator());
|
|
|
operators.put("signum", new MathOperator());
|
|
|
- operators.put("quote", (op, lisp) -> {
|
|
|
+ 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", (op, lisp) -> {
|
|
|
+ operators.put("if", (eval, 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());
|
|
|
+ if(this.getElement(lisp.car()).toBoolean()) {
|
|
|
+ return eval.getElement(lisp.cdr().car());
|
|
|
} else {
|
|
|
- return LispElement.getElement(lisp.cdr().cdr().car());
|
|
|
+ return eval.getElement(lisp.cdr().cdr().car());
|
|
|
}
|
|
|
});
|
|
|
- operators.put("not", (op, lisp) -> {
|
|
|
+ operators.put("not", (eval, op, lisp) -> {
|
|
|
if(lisp.size() != 1) {
|
|
|
throw new LispError(LispError.ERR_NUM_ARG);
|
|
|
}
|
|
|
- boolean result = !LispElement.getElement(lisp.car()).toBoolean();
|
|
|
+ boolean result = !eval.getElement(lisp.car()).toBoolean();
|
|
|
return LispElement.generate(result);
|
|
|
});
|
|
|
- operators.put("and", (op, lisp) -> {
|
|
|
+ operators.put("and", (eval, op, lisp) -> {
|
|
|
boolean result = true;
|
|
|
while(!lisp.isEmpty()) {
|
|
|
- result = result && LispElement.getElement(lisp.car()).toBoolean();
|
|
|
+ result = result && eval.getElement(lisp.car()).toBoolean();
|
|
|
lisp = lisp.cdr();
|
|
|
}
|
|
|
return LispElement.generate(result);
|
|
|
});
|
|
|
- operators.put("or", (op, lisp) -> {
|
|
|
+ operators.put("or", (eval, op, lisp) -> {
|
|
|
boolean result = false;
|
|
|
while(!lisp.isEmpty()) {
|
|
|
- result = result || LispElement.getElement(lisp.car()).toBoolean();
|
|
|
+ result = result || eval.getElement(lisp.car()).toBoolean();
|
|
|
lisp = lisp.cdr();
|
|
|
}
|
|
|
return LispElement.generate(result);
|
|
|
});
|
|
|
- operators.put("+", (op, lisp) -> {
|
|
|
+ operators.put("+", (eval, 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);
|
|
|
+ while(!lisp.isEmpty()){
|
|
|
+ LispElement eltInt = eval.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 = eval.getElement(lisp.car());
|
|
|
+ result += elt.toNumber();
|
|
|
+ lisp = lisp.cdr();
|
|
|
+ }
|
|
|
+ return LispElement.generate(result);
|
|
|
});
|
|
|
- operators.put("*", (op, lisp) -> {
|
|
|
+ operators.put("*", (eval, 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);
|
|
|
+ while(!lisp.isEmpty()){
|
|
|
+ LispElement eltInt = eval.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 = eval.getElement(lisp.car());
|
|
|
+ result *= elt.toNumber();
|
|
|
+ lisp = lisp.cdr();
|
|
|
+ }
|
|
|
+ return LispElement.generate(result);
|
|
|
});
|
|
|
- operators.put("-", (op, lisp) -> {
|
|
|
+ operators.put("-", (eval, op, lisp) -> {
|
|
|
switch(lisp.size()) {
|
|
|
- case 1:
|
|
|
- LispElement elt = LispElement.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 = LispElement.getElement(lisp.car());
|
|
|
- LispElement elt2 = LispElement.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);
|
|
|
+ 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("/", (op, lisp) -> {
|
|
|
+ operators.put("/", (eval, 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());
|
|
|
+ LispElement elt1 = eval.getElement(lisp.car());
|
|
|
+ LispElement elt2 = eval.getElement(lisp.cdr().car());
|
|
|
if(elt2.toNumber() == 0) {
|
|
|
throw new LispError("Division by zero");
|
|
|
}
|
|
@@ -152,66 +209,24 @@ public class LispEval {
|
|
|
}
|
|
|
return LispElement.generate(elt1.toNumber() / elt2.toNumber());
|
|
|
});
|
|
|
- operators.put("abs", (op, lisp) -> {
|
|
|
+ operators.put("abs", (eval, op, lisp) -> {
|
|
|
if(lisp.size() != 1) {
|
|
|
throw new LispError(LispError.ERR_NUM_ARG);
|
|
|
}
|
|
|
- LispElement le = LispElement.getElement(lisp.car());
|
|
|
+ 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", (op, lisp) -> {
|
|
|
+ operators.put("pow", (eval, op, lisp) -> {
|
|
|
if(lisp.size() != 2) {
|
|
|
throw new LispError(LispError.ERR_NUM_ARG);
|
|
|
}
|
|
|
- return LispElement.generate(Math.pow(LispElement.getElement(lisp.car()).toNumber(), LispElement.getElement(lisp.cdr().car()).toNumber()));
|
|
|
+ return LispElement.generate(Math.pow(eval.getElement(lisp.car()).toNumber(), eval.getElement(lisp.cdr().car()).toNumber()));
|
|
|
});
|
|
|
}
|
|
|
|
|
|
- /**
|
|
|
- * Un element lisp
|
|
|
- */
|
|
|
- private Object lispElt = null;
|
|
|
-
|
|
|
- /**
|
|
|
- * Une liste d'elements lisp
|
|
|
- */
|
|
|
- private ConsList<Object> lispList = null;
|
|
|
-
|
|
|
- /**
|
|
|
- * Creation d'un evaluateur lisp
|
|
|
- */
|
|
|
- public LispEval() {
|
|
|
-
|
|
|
- }
|
|
|
-
|
|
|
- /**
|
|
|
- * Creation d'un evaluateur lisp
|
|
|
- * @param lisp Un element lisp (Object) ou une liste de lisp (ConsList<Object>)
|
|
|
- */
|
|
|
- public LispEval(Object lisp) {
|
|
|
- this.setLisp(lisp);
|
|
|
- }
|
|
|
-
|
|
|
- /**
|
|
|
- * 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;
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
/**
|
|
|
* Evalue l'element lisp
|
|
|
* @return L'element retourner par l'évaluation
|
|
@@ -221,11 +236,11 @@ public class LispEval {
|
|
|
if(this.lispElt != null) {
|
|
|
return this.evalElt();
|
|
|
} else if(this.lispList != null) {
|
|
|
- return this.evalList();
|
|
|
+ return this.evaluateList();
|
|
|
}
|
|
|
return null;
|
|
|
}
|
|
|
-
|
|
|
+
|
|
|
/**
|
|
|
* Evalue un element
|
|
|
* @return L'element retourner par l'évaluation
|
|
@@ -233,23 +248,21 @@ public class LispEval {
|
|
|
*/
|
|
|
public LispElement evalElt() throws LispError {
|
|
|
try {
|
|
|
- return LispElement.getElement(this.lispElt);
|
|
|
+ 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 evalList() throws LispError {
|
|
|
- return evaluateList(this.lispList);
|
|
|
+ public LispElement evaluateList() throws LispError {
|
|
|
+ return this.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
|
|
|
*
|
|
@@ -257,35 +270,35 @@ public class LispEval {
|
|
|
* @return Valeur évaluer
|
|
|
* @throws LispError
|
|
|
*/
|
|
|
- public static LispElement evaluateList(ConsList<Object> lisp) throws LispError {
|
|
|
+ public 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);
|
|
|
+ LispOperator op = this.operators.get(operator);
|
|
|
if(op == null) {
|
|
|
- if(DefineOperator.isLambda(operator)) {
|
|
|
- return operators.get("lambda").apply(operator, lisp.cdr());
|
|
|
+ if(this.define.isLambda(operator)) {
|
|
|
+ return this.operators.get("lambda").apply(this, operator, lisp.cdr());
|
|
|
} else {
|
|
|
throw new LispError(operator + LispError.ERR_UNKNOW, new UnsupportedOperationException("Unknow operator"));
|
|
|
}
|
|
|
}
|
|
|
try {
|
|
|
- return op.apply(operator, lisp.cdr());
|
|
|
+ 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 static void verifyForbiddenName(String name) throws LispError {
|
|
|
+ public void verifyForbiddenName(String name) throws LispError {
|
|
|
//Verifie que ce n'est pas une valeur (double, bool, ...)
|
|
|
try {
|
|
|
LispElement.valueOf(name).toStr();
|
|
@@ -293,9 +306,51 @@ public class LispEval {
|
|
|
throw new LispError(name + LispError.ERR_INVALID, ex);
|
|
|
}
|
|
|
//Verifie que ce n'est pas le nom d'un operateur
|
|
|
- if(operators.containsKey(name)) {
|
|
|
+ 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;
|
|
|
+ }
|
|
|
+
|
|
|
}
|