Sfoglia il codice sorgente

Refactoring LispParser

Arthur Brandao 6 anni fa
parent
commit
9bc9750235
2 ha cambiato i file con 169 aggiunte e 146 eliminazioni
  1. 3 146
      src/migl/lisp/LispImpl.java
  2. 166 0
      src/migl/lisp/LispParser.java

+ 3 - 146
src/migl/lisp/LispImpl.java

@@ -2,18 +2,13 @@ package migl.lisp;
 
 import java.math.BigInteger;
 import java.util.HashMap;
-import java.util.LinkedList;
 import java.util.Map;
-import java.util.Queue;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
 
 import migl.lisp.operator.ComparatorOperator;
 import migl.lisp.operator.ConsOperator;
 import migl.lisp.operator.DefineOperator;
 import migl.lisp.operator.MinMaxOperator;
 import migl.util.ConsList;
-import migl.util.ConsListFactory;
 
 public class LispImpl implements Lisp {
 	
@@ -152,25 +147,11 @@ public class LispImpl implements Lisp {
 	
 	@Override
 	public Object parse(String expr) throws LispError {
-		//Analyse l'expression
-		if(!this.verify(expr)) {
+		LispParser lp = new LispParser(expr);
+		if(!lp.verify()) {
 			throw new LispError("Invalid Format");
 		}
-		//Remplace les nil par ()
-		expr = expr.replaceAll(" nil ", " () ").replaceAll("\\(nil ", "(() ").replaceAll(" nil\\)", " ())");
-		//Explose l'expression
-		Queue<String> explode = this.explode(expr);
-		//Analyse le type d'expression
-		String val = explode.poll();
-		if("(".equals(val)){
-			return this.parseList(explode);
-		} else {
-			//Element seul
-			if(explode.size() > 0) {
-				throw new LispError("Invalid Format");
-			}
-			return LispElement.valueOf(val).value;
-		}
+		return lp.parse();
 	}
 
 	@Override
@@ -189,130 +170,6 @@ public class LispImpl implements Lisp {
 		
 	}
 	
-	/* --- Verification --- */
-	
-	/**
-	 * Verifie que le format d'un String correspond bien à une expression Lisp
-	 * 
-	 * @param expr Le String à examiner
-	 * @return
-	 */
-	private boolean verify(String expr) {
-		expr = expr.trim();
-		//Pas vide
-		if(expr.length() == 0) {
-			return false;
-		}
-		//Liste ou element
-		if(expr.charAt(0) == '(') {
-			return this.verifyList(expr);
-		}
-		return this.verifyElement(expr);
-	}
-	
-	/**
-	 * Verifie que le format d'un String correspond bien à une liste Lisp
-	 * 
-	 * @param expr Le String à examiner
-	 * @return
-	 */
-	private boolean verifyList(String expr) {
-		Pattern p = Pattern.compile("\\(([ |\t]*[A-Za-z0-9\\.\\+\\-\\/\\*<>=#!]+[ |\t]*)*\\)");
-		Matcher m = p.matcher(expr);
-		//Si pas de correspondance
-		if(!m.find()) {
-			return false;
-		} 
-		//Si toute la chaine
-		else if(m.end() - m.start() == expr.length()) {
-			return true;
-		} 
-		//Si commence au debut mais finit avant la fin il y a alors des elements hors de la liste
-		else if(m.start() == 0) {
-			return false;
-		} 
-		//Si il y a une liste dans la liste
-		else {
-			StringBuilder builder = new StringBuilder();
-			builder.append(expr.substring(0, m.start())).append("list").append(expr.substring(m.end()));
-			return this.verifyList(builder.toString());
-		}
-	}
-	
-	/**
-	 * Verifie que le format d'un String correspond bien à un element Lisp
-	 * 
-	 * @param expr Le String à examiner
-	 * @return
-	 */
-	private boolean verifyElement(String expr) {
-		Pattern p = Pattern.compile("[ |\t]*[A-Za-z0-9\\.\\+\\-\\/\\*<>=#]+[ |\t]*");
-		Matcher m = p.matcher(expr);
-		return m.find();
-	}
-	
-	/* --- Parser --- */
-	
-	/**
-	 * Découpe un string pour l'analyse Lisp
-	 * 
-	 * @param expr L'expression à analyser
-	 * @return L'expression découpée
-	 */
-	private Queue<String> explode(String expr) {
-		String[] tmp = expr.trim().split("[ |\t]");
-		Queue<String> result = new LinkedList<>();
-		for(String str : tmp) {
-			char[] charArray = str.toCharArray();
-			StringBuilder builder = new StringBuilder();
-			for(char c : charArray) {
-				if(c == '(') {
-					//Si il y a une chaine dans le builder à mettre avant
-					if(builder.length() != 0) {
-						result.add(builder.toString());
-						builder = new StringBuilder();
-					}
-					result.add("" + c);
-				} else if(c == ')') {
-					//Si il y a une chaine dans le builder à mettre avant
-					if(builder.length() != 0) {
-						result.add(builder.toString());
-						builder = new StringBuilder();
-					}
-					result.add("" + c);
-				} else {
-					builder.append(c);
-				}
-			}
-			//Si il reste une chaine dans le builder à la fin
-			if(builder.length() != 0) {
-				result.add(builder.toString());
-			}
-		}
-		return result;
-	}
-	
-	/**
-	 * Parse une file d'element en Lisp
-	 * 
-	 * @param queue La file avec les valeurs récupérées {@link #explode(String)}
-	 * @return Liste d'element
-	 * @throws LispError Format invalide
-	 */
-	private Object parseList(Queue<String> queue) throws LispError {
-		ConsList<Object> list = ConsListFactory.nil();
-		String val = queue.poll();
-		while(!")".equals(val)) {
-			if("(".equals(val)) {
-				list = list.append(this.parseList(queue));
-			} else {
-				list = list.append(LispElement.valueOf(val).value);
-			}
-			val = queue.poll();
-		}
-		return list;
-	}
-	
 	/* --- Evaluation --- */
 	
 	/**

+ 166 - 0
src/migl/lisp/LispParser.java

@@ -0,0 +1,166 @@
+package migl.lisp;
+
+import java.util.LinkedList;
+import java.util.Queue;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import migl.util.ConsList;
+import migl.util.ConsListFactory;
+
+public class LispParser {
+	
+	private static final String REGEX_LISP_ELEMENT = "[ |\t]*[A-Za-z0-9\\.\\+\\-\\/\\*<>=#]+[ |\t]*";
+	
+	private static final String REGEX_LISP_LIST = "\\(([ |\t]*[A-Za-z0-9\\.\\+\\-\\/\\*<>=#!]+[ |\t]*)*\\)";
+	
+	private String expr;
+	
+	private Queue<String> explode = new LinkedList<>();
+	
+	public LispParser(String lispExpr) {
+		if(lispExpr == null) {
+			throw new IllegalArgumentException("Expression is null");
+		}
+		this.expr = lispExpr.trim();
+	}
+	
+	/* --- Verification de l'expression --- */
+	
+	/**
+	 * Verifie que le format d'un String correspond bien à une expression Lisp
+	 * 
+	 * @param expr Le String à examiner
+	 * @return
+	 */
+	public boolean verify() {
+		//Pas vide
+		if(this.expr.length() == 0) {
+			return false;
+		}
+		//Liste ou element
+		if(this.expr.charAt(0) == '(') {
+			return this.verifyList();
+		}
+		return this.verifyElement();
+	}
+	
+	/**
+	 * Verifie que le format d'un String correspond bien à un element Lisp
+	 * 
+	 * @param expr Le String à examiner
+	 * @return
+	 */
+	private boolean verifyElement() {
+		Pattern p = Pattern.compile(REGEX_LISP_ELEMENT);
+		Matcher m = p.matcher(this.expr);
+		return m.find();
+	}
+	
+	/**
+	 * Verifie que le format d'un String correspond bien à une liste Lisp
+	 * 
+	 * @param lispExpr Le String à examiner
+	 * @return
+	 */
+	private boolean verifyList() {
+		return this.verifyList(this.expr);
+	}
+	
+	/**
+	 * Verifie que le format d'un String correspond bien à une liste Lisp
+	 * 
+	 * @param lispExpr Le String à examiner
+	 * @return
+	 */
+	private boolean verifyList(String lispExpr) {
+		Pattern p = Pattern.compile(REGEX_LISP_LIST);
+		Matcher m = p.matcher(lispExpr);
+		//Si pas de correspondance
+		if(!m.find()) {
+			return false;
+		} 
+		//Si toute la chaine
+		else if(m.end() - m.start() == lispExpr.length()) {
+			return true;
+		} 
+		//Si commence au debut mais finit avant la fin il y a alors des elements hors de la liste
+		else if(m.start() == 0) {
+			return false;
+		} 
+		//Si il y a une liste dans la liste
+		else {
+			StringBuilder builder = new StringBuilder();
+			builder.append(lispExpr.substring(0, m.start())).append("list").append(lispExpr.substring(m.end()));
+			return this.verifyList(builder.toString());
+		}
+	}
+	
+	/* --- Parse les elements --- */
+	
+	public Object parse() throws LispError {
+		//Remplace les nil par ()
+		String lispExpr = this.expr.replaceAll(" nil ", " () ")
+				.replaceAll("\\(nil ", "(() ")
+				.replaceAll(" nil\\)", " ())");
+		//Traitement de la chaine pour l'analyer
+		this.explode(lispExpr);
+		//Analyse le type d'expression
+		String val = this.explode.poll();
+		if("(".equals(val)){
+			return this.parseList();
+		} else {
+			//Element seul
+			if(this.explode.size() > 0) {
+				throw new LispError("Invalid Format");
+			}
+			return LispElement.valueOf(val).value;
+		}
+	}
+	
+	private void explode(String lispExpr) {
+		String[] tmp = lispExpr.trim().split("[ |\t]");
+		for(String str : tmp) {
+			char[] charArray = str.toCharArray();
+			StringBuilder builder = new StringBuilder();
+			for(char c : charArray) {
+				if(c == '(') {
+					//Si il y a une chaine dans le builder à mettre avant
+					if(builder.length() != 0) {
+						this.explode.add(builder.toString());
+						builder = new StringBuilder();
+					}
+					this.explode.add("" + c);
+				} else if(c == ')') {
+					//Si il y a une chaine dans le builder à mettre avant
+					if(builder.length() != 0) {
+						this.explode.add(builder.toString());
+						builder = new StringBuilder();
+					}
+					this.explode.add("" + c);
+				} else {
+					builder.append(c);
+				}
+			}
+			//Si il reste une chaine dans le builder à la fin
+			if(builder.length() != 0) {
+				this.explode.add(builder.toString());
+			}
+		}
+	}
+	
+	private Object parseList() {
+		ConsList<Object> list = ConsListFactory.nil();
+		String val = this.explode.poll();
+		while(!")".equals(val)) {
+			if("(".equals(val)) {
+				list = list.append(this.parseList());
+			} else {
+				list = list.append(LispElement.valueOf(val).value);
+			}
+			val = this.explode.poll();
+		}
+		return list;
+	}
+
+}