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