Sfoglia il codice sorgente

Refactoring evaluation des expressions

Arthur Brandao 6 anni fa
parent
commit
88e7d4652b

+ 2 - 2
src/migl/lisp/LispElement.java

@@ -179,13 +179,13 @@ public class LispElement {
 	@SuppressWarnings("unchecked")
 	public static LispElement getElement(Object elt) throws LispError {
 		if(elt instanceof ConsList) {
-			return LispImpl.evaluateList((ConsList<Object>) elt);
+			return LispEval.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 {
-				LispImpl.verifyForbiddenName(str);
+				LispEval.verifyForbiddenName(str);
 			} catch (LispError ex) {
 				skip = true;
 			}

+ 230 - 0
src/migl/lisp/LispEval.java

@@ -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);
+		}
+	}
+	
+}

+ 2 - 205
src/migl/lisp/LispImpl.java

@@ -1,150 +1,7 @@
 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 {
 	
-	
-	
-	/**
-	 * 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
 	public Object parse(String expr) throws LispError {
 		LispParser lp = new LispParser(expr);
@@ -155,69 +12,9 @@ public class LispImpl implements Lisp {
 	}
 
 	@Override
-	@SuppressWarnings("unchecked")
 	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();
 	}
 
 }

+ 3 - 3
src/migl/lisp/operator/DefineOperator.java

@@ -6,8 +6,8 @@ import java.util.HashMap;
 import migl.lisp.Lisp;
 import migl.lisp.LispElement;
 import migl.lisp.LispError;
+import migl.lisp.LispEval;
 import migl.lisp.LispFactory;
-import migl.lisp.LispImpl;
 import migl.lisp.LispOperator;
 import migl.util.ConsList;
 
@@ -121,7 +121,7 @@ public class DefineOperator implements LispOperator {
 			throw new LispError(le.toString() + LispError.ERR_INVALID, ex);
 		}
 		//Verification de sa validité
-		LispImpl.verifyForbiddenName(key);
+		LispEval.verifyForbiddenName(key);
 		//Ajoute dans la map
 		define.put(key, lisp.cdr());
 		//Evalue pour le retour
@@ -146,7 +146,7 @@ public class DefineOperator implements LispOperator {
 			throw new LispError(ex);
 		}
 		//Verification de sa validité
-		LispImpl.verifyForbiddenName(key);
+		LispEval.verifyForbiddenName(key);
 		//Regarde si la valeur est deja presente
 		if(!define.containsKey(key)) {
 			throw new LispError(key + LispError.ERR_UNKNOW);