package com.drawbridge.jsengine.ast; import java.io.StringReader; import java.util.HashSet; import java.util.LinkedList; import com.drawbridge.jsengine.Scope; import com.drawbridge.jsengine.ast.Evaluator.EvaluatorException; import com.drawbridge.jsengine.jsobjects.ExecutionException; import com.drawbridge.utils.AnalyticUtils; import com.drawbridge.utils.Utils; import com.google.caja.lexer.CharProducer; import com.google.caja.lexer.FilePosition; import com.google.caja.lexer.InputSource; import com.google.caja.lexer.JsLexer; import com.google.caja.lexer.JsTokenQueue; import com.google.caja.lexer.JsTokenType; import com.google.caja.lexer.ParseException; import com.google.caja.lexer.Token; import com.google.caja.parser.AbstractParseTreeNode; import com.google.caja.parser.ParseTreeNode; import com.google.caja.parser.js.Statement; import com.google.caja.reporting.DevNullMessageQueue; import com.google.caja.reporting.MessagePart; import com.google.caja.util.Criterion; public class DBParser { private Scope mScope; private LinkedList<ParserListener> mParserListeners = new LinkedList<ParserListener>(); private LinkedList<ParseTreeNode> mStatements = new LinkedList<ParseTreeNode>(); private LinkedList<Evaluator> mEvaluators = new LinkedList<Evaluator>(); private ParserListener mSourceListener; private String mSnapshot; private LinkedList<String> mComments = new LinkedList<String>(); private static DBParser mInstance = null; public int mNumberOfParses = 0; public HashSet<Evaluator> mRecentlyChangedEvaluators = new HashSet<Evaluator>(); private boolean mVerboseOutput = true; public static boolean hasInstance() { return (mInstance != null); } public static DBParser getInstance() { if (mInstance == null) mInstance = new DBParser(); return mInstance; } private DBParser() { } public LinkedList<ParserListener> getParserListeners() //**onParseChange! { return mParserListeners; } public void setParams(ParserListener sourceListener, LinkedList<ParserListener> listeners, Scope scope, String input) { Utils.out.println(this.getClass(), "Set Parser Params"); mStatements.clear(); mParserListeners.clear(); mSnapshot = input; mSourceListener = sourceListener; mScope = scope; mParserListeners.addAll(listeners); mComments = new LinkedList<String>(); } public void parse() { if(mRecentlyChangedEvaluators != null) mRecentlyChangedEvaluators.clear(); mNumberOfParses++; CharProducer cp = CharProducer.Factory.create(new StringReader(mSnapshot), InputSource.PREDEFINED); JsLexer lexer = new JsLexer(cp); JsTokenQueue tokenQueue = new JsTokenQueue(lexer, InputSource.PREDEFINED, new Criterion<Token<JsTokenType>>() { public boolean accept(Token<JsTokenType> t) { if (t.type == JsTokenType.COMMENT) { FilePosition filePos = t.pos; while (mComments.size() <= filePos.endLineNo() - 1) mComments.add(""); mComments.set(filePos.endLineNo() - 1, t.text); } return JsTokenType.COMMENT != t.type && JsTokenType.LINE_CONTINUATION != t.type; } }); com.google.caja.parser.js.Parser parser = new com.google.caja.parser.js.Parser(tokenQueue, DevNullMessageQueue.singleton()); parser.setRecoverFromFailure(false); ParseTreeNode result = null; boolean exception = false; int lastLinePosition = 0; mComments = new LinkedList<String>(); String[] inputLines = mSnapshot.split("\n"); int evaluatorIndex = 0; try { Utils.out.println(getClass(), "Parsing"); while (!tokenQueue.isEmpty()) { lastLinePosition = tokenQueue.currentPosition().endLineNo() - 1; result = parser.parseStatement(); mStatements.add(result); Evaluator newEvaluator = Evaluator.getEvaluator(null, mScope, result); if (evaluatorIndex < mEvaluators.size()) { if (DBParser.areEvaluatorsEqual(newEvaluator, mEvaluators.get(evaluatorIndex))) { // Don't replace Utils.out.println(getClass(), "Not replacing eval due to equality: " + newEvaluator.getClass().getSimpleName()); // If it's an integer, we'd like to record the change for dialling if(newEvaluator instanceof IntegerLiteralEvaluator) mRecentlyChangedEvaluators.add( (IntegerLiteralEvaluator) newEvaluator); } else { recordRecentlyChangedEvaluator(mEvaluators.get(evaluatorIndex), newEvaluator); mEvaluators.set(evaluatorIndex, newEvaluator); } } else { mEvaluators.add(newEvaluator); } evaluatorIndex++; // Store the Comments while (mComments.size() <= result.getFilePosition().endLineNo() - 1) { mComments.add(""); } String inputLine = inputLines[result.getFilePosition().endLineNo() - 1]; if (mVerboseOutput) Utils.out.println(this.getClass(), ((AbstractParseTreeNode) result).toStringDeep()); mComments.set(result.getFilePosition().endLineNo() - 1, inputLine.substring(result.getFilePosition().endCharInLine() - 1)); } // Remove extra old evaluators int evalSize = mEvaluators.size(); for (int i = evaluatorIndex; i < evalSize && i < mEvaluators.size(); i++) mEvaluators.remove(i); } catch (ParseException e) { exception = true; Utils.err.println(getClass(), "Parse Exception"); MessagePart mp = e.getCajaMessage().getMessageParts().get(0); if (mp instanceof FilePosition) { notifyListenersOfException(((FilePosition) mp).endLineNo() - 1, e.getMessage()); } else { notifyListenersOfException(lastLinePosition, e.getMessage()); } } if (!exception) notifyListenersOfChange(); } public String getSnapshot() { return mSnapshot; } /** * Returns the positions of the last bit of recognized text in the snapshot * * @return LinkedList<String> */ public LinkedList<String> getComments() { return mComments; } public Scope getScope() { return mScope; } public void notifyOfEvalChange(ParserListener listener) { mSourceListener = listener; notifyListenersOfChange(); } private void notifyListenersOfChange() { // Utils.out.println(getClass(), "Notifying listeners:" + mParserListeners.size()); for (ParserListener listener : mParserListeners) { if (!listener.equals(mSourceListener)) listener.onParserChange(); } } private void notifyListenersOfException(int lineNumber, String message) { AnalyticUtils.recordSyntaxError(message, lineNumber); for (ParserListener listener : mParserListeners) { listener.onParserException(lineNumber, message); } } public void addParserListener(ParserListener newListener) { mParserListeners.add(newListener); } /** * Executes the E-AST * * @return * @throws EvaluatorException */ public Object evaluate() throws EvaluatorException { if (mScope == null) throw new RuntimeException("SCOPE IS NULL"); if (mEvaluators == null) throw new RuntimeException("Cannot evaluate a null parser output"); for (int i = 0; i < mEvaluators.size(); i++) { try { mEvaluators.get(i).evaluate(); } catch (ExecutionException e) { Utils.err.println("Error on line:" + (mEvaluators.get(i).getFilePosition().startLineNo() - 1)); e.printStackTrace(); this.notifyListenersOfException(mEvaluators.get(i).getFilePosition().startLineNo() - 1, e.getMessage()); } } return null; } public String getASTString() { String result = ""; for (int i = 0; i < mStatements.size(); i++) { result += "\n" + ((Statement) mStatements.get(i)).toString(); } return result; } public LinkedList<Evaluator> getEvaluators() { return mEvaluators; } public void printScope() { try { mScope.printScopeDeep(0); } catch (Exception e) { e.printStackTrace(); } } public void setSnapshot(String snapshot) { mSnapshot = snapshot; } public boolean containsEvaluator(Evaluator e) { return containsEvaluator(mEvaluators, e); } private boolean containsEvaluator(LinkedList<Evaluator> evals, Evaluator e) { for(int i = 0; i < evals.size(); i++) { if(evals.get(i) == e) return true; } for (Evaluator child : evals) { if (containsEvaluator(child.mChildren, e)) return true; } return false; } public static boolean areEvaluatorsEqual(Evaluator a, Evaluator b) { // Check type and children if (a != null & b != null) { if (a.getClass().equals(b.getClass())) { if (a.mChildren.size() == b.mChildren.size()) { for (int i = 0; i < a.mChildren.size(); i++) { if (!areEvaluatorsEqual(a.mChildren.get(i), b.mChildren.get(i))) return false; } } } if (a == b && a.getFilePosition().equals(b.getFilePosition())) return true; else return false; } else return false; } private void recordRecentlyChangedEvaluator(Evaluator old, Evaluator recent) { // Check type and children if (old != null && recent != null && !old.compare(recent)) { for (int i = 0; i < Math.max(old.mChildren.size(), recent.mChildren.size()); i++) { if (i >= old.mChildren.size()) { if(recent instanceof IntegerLiteralEvaluator) mRecentlyChangedEvaluators.add((IntegerLiteralEvaluator) recent); } else if (i < old.mChildren.size() && i < recent.mChildren.size()) { recordRecentlyChangedEvaluator(old.mChildren.get(i), recent.mChildren.get(i)); } } if (old.mChildren.size() == 0 && recent.mChildren.size() == 0) { if(recent instanceof IntegerLiteralEvaluator) mRecentlyChangedEvaluators.add((IntegerLiteralEvaluator) recent); } } else if (old == null && recent != null) { if(recent instanceof IntegerLiteralEvaluator) mRecentlyChangedEvaluators.add((IntegerLiteralEvaluator) recent); } if(recent instanceof IntegerLiteralEvaluator && recent != null) mRecentlyChangedEvaluators.add((IntegerLiteralEvaluator) recent); } public void setVerboseOutput(boolean b) { mVerboseOutput = b; } }