|
@@ -1,150 +1,7 @@
|
|
package migl.lisp;
|
|
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 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("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()));
|
|
|
|
- });
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
@Override
|
|
@Override
|
|
public Object parse(String expr) throws LispError {
|
|
public Object parse(String expr) throws LispError {
|
|
LispParser lp = new LispParser(expr);
|
|
LispParser lp = new LispParser(expr);
|
|
@@ -155,69 +12,9 @@ public class LispImpl implements Lisp {
|
|
}
|
|
}
|
|
|
|
|
|
@Override
|
|
@Override
|
|
- @SuppressWarnings("unchecked")
|
|
|
|
public Object evaluate(Object lisp) throws LispError {
|
|
public Object evaluate(Object lisp) throws LispError {
|
|
- if(lisp instanceof ConsList) {
|
|
|
|
- return evaluateList((ConsList<Object>) lisp);
|
|
|
|
- }
|
|
|
|
- //Si element seul on évalue directement sa valeur
|
|
|
|
- try {
|
|
|
|
- //return LispElement.generate(lisp);
|
|
|
|
- return LispElement.getElement(lisp);
|
|
|
|
- } catch (IllegalArgumentException ex) {
|
|
|
|
- throw new LispError(ex.getMessage(), ex);
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- /* --- 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
|
|
|
|
- */
|
|
|
|
- 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);
|
|
|
|
- }
|
|
|
|
|
|
+ LispEval le = new LispEval(lisp);
|
|
|
|
+ return le.evaluate();
|
|
}
|
|
}
|
|
|
|
|
|
}
|
|
}
|