diff options
Diffstat (limited to 'dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/output/MIParser.java')
-rw-r--r-- | dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/output/MIParser.java | 560 |
1 files changed, 560 insertions, 0 deletions
diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/output/MIParser.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/output/MIParser.java new file mode 100644 index 00000000000..2b9a8929f00 --- /dev/null +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/output/MIParser.java @@ -0,0 +1,560 @@ +/******************************************************************************* + * Copyright (c) 2000, 2007 QNX Software Systems and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * QNX Software Systems - Initial API and implementation + * Wind River Systems - Modified for new DSF Reference Implementation + *******************************************************************************/ + +package org.eclipse.cdt.dsf.mi.service.command.output; + +import java.util.ArrayList; +import java.util.List; + +/** +<pre> +`OUTPUT :' + `( OUT-OF-BAND-RECORD )* [ RESULT-RECORD ] "(gdb)" NL' + +`RESULT-RECORD :' + ` [ TOKEN ] "^" RESULT-CLASS ( "," RESULT )* NL' + +`OUT-OF-BAND-RECORD :' + `ASYNC-RECORD | STREAM-RECORD' + +`ASYNC-RECORD :' + `EXEC-ASYNC-OUTPUT | STATUS-ASYNC-OUTPUT | NOTIFY-ASYNC-OUTPUT' + +`EXEC-ASYNC-OUTPUT :' + `[ TOKEN ] "*" ASYNC-OUTPUT' + +`STATUS-ASYNC-OUTPUT :' + `[ TOKEN ] "+" ASYNC-OUTPUT' + +`NOTIFY-ASYNC-OUTPUT :' + `[ TOKEN ] "=" ASYNC-OUTPUT' + +`ASYNC-OUTPUT :' + `ASYNC-CLASS ( "," RESULT )* NL' + +`RESULT-CLASS :' + `"done" | "running" | "connected" | "error" | "exit"' + +`ASYNC-CLASS :' + `"stopped" | OTHERS' (where OTHERS will be added depending on the + needs--this is still in development). + +`RESULT :' + ` VARIABLE "=" VALUE' + +`VARIABLE :' + ` STRING ' + +`VALUE :' + ` CONST | TUPLE | LIST ' + +`CONST :' + `C-STRING' + +`TUPLE :' + ` "{}" | "{" RESULT ( "," RESULT )* "}" ' + +`LIST :' + ` "[]" | "[" VALUE ( "," VALUE )* "]" | "[" RESULT ( "," RESULT )* + "]" ' + +`STREAM-RECORD :' + `CONSOLE-STREAM-OUTPUT | TARGET-STREAM-OUTPUT | LOG-STREAM-OUTPUT' + +`CONSOLE-STREAM-OUTPUT :' + `"~" C-STRING' + +`TARGET-STREAM-OUTPUT :' + `"@" C-STRING' + +`LOG-STREAM-OUTPUT :' + `"&" C-STRING' + +`NL :' + `CR | CR-LF' + +`TOKEN :' + _any sequence of digits_. + +`C-STRING :' + `""" SEVEN-BIT-ISO-C-STRING-CONTENT """' +</pre> + */ +public class MIParser { + public enum RecordType { ResultRecord, OOBRecord, PrimaryPrompt } + + public String primaryPrompt = "(gdb)"; //$NON-NLS-1$ + public String cliPrompt = primaryPrompt; + public String secondaryPrompt = ">"; //$NON-NLS-1$ + + public RecordType getRecordType(String line) { + int i = 0; + if (Character.isDigit(line.charAt(0))) { + i = 1; + while (i < line.length() && Character.isDigit(line.charAt(i))) { + i++; + } + } + + if (i < line.length() && line.charAt(i) == '^') { + return RecordType.ResultRecord; + } else if (line.startsWith(primaryPrompt, i)) { + return RecordType.PrimaryPrompt; + //break; // Do nothing. + } else { + return RecordType.OOBRecord; + } + } + + /** + * + */ + public MIResultRecord parseMIResultRecord(String line) { + StringBuffer buffer = new StringBuffer(line); + // Fetch the Token/Id + int id = parseToken(buffer); + // Consume the '^' + buffer.deleteCharAt(0); + + MIResultRecord rr = new MIResultRecord(); + rr.setToken(id); + if (buffer.toString().startsWith(MIResultRecord.DONE)) { + rr.setResultClass(MIResultRecord.DONE); + buffer.delete(0, MIResultRecord.DONE.length()); + } else if (buffer.toString().startsWith(MIResultRecord.ERROR)) { + rr.setResultClass(MIResultRecord.ERROR); + buffer.delete(0, MIResultRecord.ERROR.length()); + } else if (buffer.toString().startsWith(MIResultRecord.EXIT)) { + rr.setResultClass(MIResultRecord.EXIT); + buffer.delete(0, MIResultRecord.EXIT.length()); + } else if (buffer.toString().startsWith(MIResultRecord.RUNNING)) { + rr.setResultClass(MIResultRecord.RUNNING); + buffer.delete(0, MIResultRecord.RUNNING.length()); + } else if (buffer.toString().startsWith(MIResultRecord.CONNECTED)) { + rr.setResultClass(MIResultRecord.CONNECTED); + buffer.delete(0, MIResultRecord.CONNECTED.length()); + } else { + // Error throw an exception? + } + + // Results are separated by commas. + if (buffer.length() > 0 && buffer.charAt(0) == ',') { + buffer.deleteCharAt(0); + MIResult[] res = processMIResults(new FSB(buffer)); + rr.setMIResults(res); + } + return rr; + } + + /** + * Find OutOfBand Records depending on the starting token. + */ + public MIOOBRecord parseMIOOBRecord(String line) { + StringBuffer buffer = new StringBuffer(line); + int id = parseToken(buffer); + MIOOBRecord oob = null; + char c = buffer.length() != 0 ? buffer.charAt(0) : 0; + if (c == '*' || c == '+' || c == '=') { + // Consume the first char + buffer.deleteCharAt(0); + MIAsyncRecord async = null; + switch (c) { + case '*' : + async = new MIExecAsyncOutput(); + break; + + case '+' : + async = new MIStatusAsyncOutput(); + break; + + case '=' : + async = new MINotifyAsyncOutput(); + break; + } + async.setToken(id); + // Extract the Async-Class + int i = buffer.toString().indexOf(','); + if (i != -1) { + String asyncClass = buffer.substring(0, i); + async.setAsyncClass(asyncClass); + // Consume the async-class and the comma + buffer.delete(0, i + 1); + } else { + async.setAsyncClass(buffer.toString().trim()); + buffer.setLength(0); + } + MIResult[] res = processMIResults(new FSB(buffer)); + async.setMIResults(res); + oob = async; + } else if (c == '~' || c == '@' || c == '&') { + // Consume the first char + buffer.deleteCharAt(0); + MIStreamRecord stream = null; + switch (c) { + case '~' : + stream = new MIConsoleStreamOutput(); + break; + + case '@' : + stream = new MITargetStreamOutput(); + break; + + case '&' : + stream = new MILogStreamOutput(); + break; + } + // translateCString() assumes that the leading " is deleted + if (buffer.length() > 0 && buffer.charAt(0) == '"') { + buffer.deleteCharAt(0); + } + stream.setCString(translateCString(new FSB(buffer))); + oob = stream; + } else { + // Badly format MI line, just pass it to the user as target stream + MIStreamRecord stream = new MITargetStreamOutput(); + stream.setCString(line + "\n"); //$NON-NLS-1$ + oob = stream; + } + return oob; + } + + private int parseToken(StringBuffer buffer) { + int id = -1; + // Fetch the Token/Id + if (Character.isDigit(buffer.charAt(0))) { + int i = 1; + while (i < buffer.length() && Character.isDigit(buffer.charAt(i))) { + i++; + } + String numbers = buffer.substring(0, i); + try { + id = Integer.parseInt(numbers); + } catch (NumberFormatException e) { + } + // Consume the token. + buffer.delete(0, i); + } + return id; + } + + /** + * Assuming that the usual leading comma was consumed. + * Extract the MI Result comma seperated responses. + */ + private MIResult[] processMIResults(FSB buffer) { + List<MIResult> aList = new ArrayList<MIResult>(); + MIResult result = processMIResult(buffer); + if (result != null) { + aList.add(result); + } + while (buffer.length() > 0 && buffer.charAt(0) == ',') { + buffer.deleteCharAt(0); + result = processMIResult(buffer); + if (result != null) { + aList.add(result); + } + } + return aList.toArray(new MIResult[aList.size()]); + } + + /** + * Construct the DsfMIResult. Characters will be consume/delete + * moving forward constructing the AST. + */ + private MIResult processMIResult(FSB buffer) { + MIResult result = new MIResult(); + int equal; + if (buffer.length() > 0 && Character.isLetter(buffer.charAt(0)) && (equal = buffer.indexOf('=')) != -1) { + String variable = buffer.substring(0, equal); + result.setVariable(variable); + buffer.delete(0, equal + 1); + MIValue value = processMIValue(buffer); + result.setMIValue(value); + } else if(buffer.length()>0 && buffer.charAt(0)=='"') { + // This an error but we just swallow it and move on. + MIValue value = processMIValue(buffer); + result.setMIValue(value); + } else { + result.setVariable(buffer.toString()); + result.setMIValue(new MIConst()); // Empty string:??? + buffer.setLength(0); + } + return result; + } + + /** + * Find a DsfMIValue implementation or return null. + */ + private MIValue processMIValue(FSB buffer) { + MIValue value = null; + if (buffer.length() > 0) { + if (buffer.charAt(0) == '{') { + buffer.deleteCharAt(0); + value = processMITuple(buffer); + } else if (buffer.charAt(0) == '[') { + buffer.deleteCharAt(0); + value = processMIList(buffer); + } else if (buffer.charAt(0) == '"') { + buffer.deleteCharAt(0); + MIConst cnst = new MIConst(); + cnst.setCString(translateCString(buffer)); + value = cnst; + } + } + return value; + } + + /** + * Assuming the starting '{' was deleted form the StringBuffer, + * go to the closing '}' consuming/deleting all the characters. + * This is usually call by processMIvalue(); + */ + private MIValue processMITuple(FSB buffer) { + MITuple tuple = new MITuple(); + List<MIValue> valueList = new ArrayList<MIValue>(); + List<MIResult> resultList = new ArrayList<MIResult>(); + // Catch closing '}' + while (buffer.length() > 0 && buffer.charAt(0) != '}') { + // Try for the DsfMIValue first + MIValue value = processMIValue(buffer); + if (value != null) { + valueList.add(value); + } else { + MIResult result = processMIResult(buffer); + if (result != null) { + resultList.add(result); + } + } + if (buffer.length() > 0 && buffer.charAt(0) == ',') { + buffer.deleteCharAt(0); + } + } + if (buffer.length() > 0 && buffer.charAt(0) == '}') { + buffer.deleteCharAt(0); + } + MIValue[] values = valueList.toArray(new MIValue[valueList.size()]); + MIResult[] res = resultList.toArray(new MIResult[resultList.size()]); + tuple.setMIValues(values); + tuple.setMIResults(res); + return tuple; + } + + /** + * Assuming the leading '[' was deleted, find the closing + * ']' consuming/delete chars from the StringBuffer. + */ + private MIValue processMIList(FSB buffer) { + MIList list = new MIList(); + List<MIValue> valueList = new ArrayList<MIValue>(); + List<MIResult> resultList = new ArrayList<MIResult>(); + // catch closing ']' + while (buffer.length() > 0 && buffer.charAt(0) != ']') { + // Try for the DsfMIValue first + MIValue value = processMIValue(buffer); + if (value != null) { + valueList.add(value); + } else { + MIResult result = processMIResult(buffer); + if (result != null) { + resultList.add(result); + } + } + if (buffer.length() > 0 && buffer.charAt(0) == ',') { + buffer.deleteCharAt(0); + } + } + if (buffer.length() > 0 && buffer.charAt(0) == ']') { + buffer.deleteCharAt(0); + } + MIValue[] values = valueList.toArray(new MIValue[valueList.size()]); + MIResult[] res = resultList.toArray(new MIResult[resultList.size()]); + list.setMIValues(values); + list.setMIResults(res); + return list; + } + + /* + * MI C-String rather MICOnst values are enclose in double quotes + * and any double quotes or backslash in the string are escaped. + * Assuming the starting double quote was removed. + * This method will stop at the closing double quote remove the extra + * backslach escaping and return the string __without__ the enclosing double quotes + * The orignal StringBuffer will move forward. + */ + private String translateCString(FSB buffer) { + boolean escape = false; + boolean closingQuotes = false; + + StringBuffer sb = new StringBuffer(); + + int index = 0; + for (; index < buffer.length() && !closingQuotes; index++) { + char c = buffer.charAt(index); + if (c == '\\') { + if (escape) { + sb.append(c); + sb.append(c); + escape = false; + } else { + escape = true; + } + } else if (c == '"') { + if (escape) { + sb.append(c); + escape = false; + } else { + // Bail out. + closingQuotes = true; + } + } else { + if (escape) { + sb.append('\\'); + } + sb.append(c); + escape = false; + } + } + buffer.delete(0, index); + return sb.toString(); + } + + /** + * Tests if this string starts with the specified prefix beginning + * a specified index. + * + * @param value the string. + * @param prefix the prefix. + * @return <code>true</code> if prefix starts value. + */ + public boolean startsWith(StringBuffer value, String prefix) { + int vlen = value.length(); + int plen = prefix.length(); + + if (vlen < plen) { + return false; + } + for (int i = 0; i < plen; i++) { + if (value.charAt(i) != prefix.charAt(i)) { + return false; + } + } + return true; + } + + /** + * Fast String Buffer class. MIParser does a lot + * of deleting off the front of a string, that's clearly + * an order N operation for StringBuffer which makes + * the MIParser an order N^2 operation. There are "issues" + * with this for large arrays. Use of FSB rather than String + * Buffer makes MIParser N rather than N^2 because FSB can + * delete from the front in constant time. + */ + public class FSB { + StringBuffer buf; + int pos; + boolean shared; + + public FSB(StringBuffer buf) { + this.buf = buf; + pos = 0; + shared = false; + } + + public FSB(FSB fbuf) { + pos = fbuf.pos; + buf = fbuf.buf; + shared = true; + } + + public int length() { + int res = buf.length() - pos; + if (res < 0) + return 0; + + return res; + } + + public char charAt(int index) { + return buf.charAt(index + pos); + } + + private void resolveCopy() { + if (shared) { + buf = new StringBuffer(buf.toString()); + shared = false; + } + } + + public FSB deleteCharAt(int index) { + if (index == 0) { + pos++; + } else { + resolveCopy(); + buf = buf.deleteCharAt(pos + index); + } + + return this; + } + + public FSB delete(int start, int end) { + if (start == 0) { + pos = pos + end - start; + } else { + resolveCopy(); + buf.delete(start + pos, end + pos); + } + + return this; + } + + public void setLength(int a) { + if (a == 0) + pos = buf.length(); + else { + // panic! fortunately we don't do this. + } + } + + public String substring(int start, int end) { + return buf.substring(start + pos, end + pos); + } + + @Override + public String toString() { + return buf.substring(pos, buf.length()); + } + + int indexOf(char c) { + int len = buf.length(); + for (int i = pos; i < len; i++) { + if (buf.charAt(i) == c) + return i - pos; + } + + return -1; + } + + boolean startsWith(String s) { + int len = Math.min(s.length(), length()); + if (len < s.length()) + return false; + + for (int i = 0; i < len; i++) { + if (s.charAt(i) != buf.charAt(pos + i)) + return false; + } + + return true; + } + } +} |