/******************************************************************************* * Copyright (c) 2010, 2016 Wind River 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: * Wind River Systems - initial implementation * Anders Dahlberg (Ericsson) - Need additional API to extend support for memory spaces (Bug 431627) * Alvaro Sanchez-Leon (Ericsson) - Need additional API to extend support for memory spaces (Bug 431627) * Matthew Khouzam (Ericsson) - Minor code cleanup and privatization of variables *******************************************************************************/ package org.eclipse.cdt.dsf.gdb; import java.util.regex.Pattern; /** * GDB Type Parser (duplicate of org.eclipse.cdt.debug.mi.core.GDBTypeParser) * The code was lifted from: The C Programming Language * B. W. Kernighan and D. Ritchie * * @since 3.0 */ public class GDBTypeParser { // GDB type parsing from whatis command // declarator: type dcl // type: (name)+ // dcl: ('*' | '&')* direct-decl // direct-dcl: '(' dcl ')' // direct-dcl '(' ')' // direct-dcl '[' integer ']' // name: ([a-zA-z][0-9])+ // integer ([0-9)+ private static final int EOF = -1; private static final int NAME = 0; private static final int PARENS = 1; private static final int BRACKETS = 2; private String line; private int index; private int tokenType; private String token; private String dataType; private String name; private GDBDerivedType gdbDerivedType; private GDBType genericType; public GDBType getGDBType() { if (gdbDerivedType != null) { return gdbDerivedType; } return genericType; } public String getVariableName() { return name; } public GDBType parse(String gdbTypeString) { // Sanity. String s = (gdbTypeString == null) ? "" : gdbTypeString; //$NON-NLS-1$ s = Pattern.compile("\\bconst\\b").matcher(s).replaceAll(""); //$NON-NLS-1$//$NON-NLS-2$ s = Pattern.compile("\\bvolatile\\b").matcher(s).replaceAll(""); //$NON-NLS-1$//$NON-NLS-2$ s = s.trim(); // Initialize. line = s; index = 0; tokenType = -1; token = ""; //$NON-NLS-1$ dataType = ""; //$NON-NLS-1$ name = ""; //$NON-NLS-1$ gdbDerivedType = null; genericType = null; // Fetch the datatype. while (getToken() == NAME) { dataType += " " + token; //$NON-NLS-1$ } // Hack for GDB, the typename can be something like // class A : public B, C { ... } * // We are only interested in "class A" // Carefull for class A::data or class ns::A<ns::data> int column = dataType.indexOf(':'); while (column > 0) { if ((column + 2) < dataType.length() && dataType.charAt(column + 1) == ':') { column = dataType.indexOf(':', column+2); continue; } dataType = dataType.substring(0, column); break; } genericType = new GDBType(dataType); // Start the recursive parser. dcl(tokenType); return getGDBType(); } public static String unParse (GDBType gdbParentType) { StringBuilder sb = new StringBuilder(); GDBType gdbType = gdbParentType; // Fetch the datatype. while (gdbType != null) { if (gdbType instanceof GDBDerivedType) { GDBDerivedType derived = (GDBDerivedType)gdbType; int type = derived.getType(); gdbType = derived.getChild(); switch (type) { case GDBType.FUNCTION: sb.append("()"); //$NON-NLS-1$ break; case GDBType.ARRAY: sb.append('[').append(derived.getDimension()).append(']'); break; case GDBType.POINTER: handlePointer(gdbType, sb); break; case GDBType.REFERENCE: handleReference(gdbType, sb); break; } } else { sb.insert(0, ' '); sb.insert(0, gdbType.getTypeName()); break; } } return sb.toString().trim(); } private static void handleReference(GDBType gdbType, StringBuilder sb) { handleReferenceOrPointer(gdbType, sb, '&'); } private static void handlePointer(GDBType gdbType, StringBuilder sb) { handleReferenceOrPointer(gdbType, sb, '*'); } private static void handleReferenceOrPointer(GDBType gdbType, StringBuilder sb, char prefix) { switch (getChildType(gdbType)) { case GDBType.POINTER: case GDBType.REFERENCE: sb.append(prefix); break; case GDBType.GENERIC: sb.insert(0, prefix); break; default: sb.insert(0, "("+prefix).append(')'); //$NON-NLS-1$ break; } } private static int getChildType(GDBType gdbType) { return (gdbType != null) ? gdbType.getType() : GDBType.GENERIC; } public class GDBType { public static final int GENERIC = 0; public static final int POINTER = 1; public static final int REFERENCE = 2; public static final int ARRAY = 3; public static final int FUNCTION = 4; String nameType; int type; public GDBType(String n) { this(n, 0); } public GDBType(int t) { this("", t); //$NON-NLS-1$ } public GDBType(String n, int t) { nameType = n; type = t; } @Override public String toString() { return unParse(this); } public String verbose() { return nameType; } public int getType() { return type; } public String getTypeName() { return nameType; } } public class GDBDerivedType extends GDBType { int dimension; GDBType child; public GDBDerivedType(GDBType c, int i) { this(c, i, 0); } public GDBDerivedType(GDBType c, int t, int dim) { super(t); setChild(c); dimension = dim; } public int getDimension() { return dimension; } public void setChild(GDBType c) { child = c; } public GDBType getChild() { return child; } public boolean hasChild() { return child != null; } @Override public String verbose() { StringBuilder sb = new StringBuilder(); switch (getType()) { case FUNCTION : sb.append(" function returning ").append(hasChild() ? child.verbose() : ""); //$NON-NLS-1$//$NON-NLS-2$ break; case ARRAY : sb.append(" array[").append(dimension).append("] of ").append(hasChild() ? child.verbose() : ""); //$NON-NLS-1$//$NON-NLS-2$ //$NON-NLS-3$ break; case REFERENCE : sb.append(" reference to ").append(hasChild() ? child.verbose() : ""); //$NON-NLS-1$//$NON-NLS-2$ break; case POINTER : sb.append(" pointer to ").append(hasChild() ? child.verbose() : ""); //$NON-NLS-1$//$NON-NLS-2$ break; } return sb.toString(); } } int getch() { if (index >= line.length() || index < 0) { return EOF; } return line.charAt(index++); } void ungetch() { if (index > 0) { index--; } } // check if the character is an alphabet boolean isCIdentifierStart(int c) { if ((c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') || c == '_' || c == ':' || c == ',') { return true; } return false; } // check is the character is alpha numeric // [a-zA-Z0-9] // GDB hack accept ':' ',' part of the GDB hacks // when doing ptype gdb returns "class A : public C { ..}" boolean isCIdentifierPart(int c) { if ((c >= '0' && c <= '9') || (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') || c == '_' || c == ':') { return true; } return false; } boolean isCSpace(int c) { if (c == ' ' || c == '\t' || c == '\f' || c == '\n') { return true; } return false; } void insertingChild(int kind) { insertingChild(kind, 0); } /** * @since 4.4 */ protected void insertingChild(int kind, int d) { if (gdbDerivedType == null) { gdbDerivedType = createGDBDerivedType(genericType, kind, d); } else { GDBDerivedType dType = gdbDerivedType; GDBType gdbType = gdbDerivedType.getChild(); while (gdbType instanceof GDBDerivedType) { dType = (GDBDerivedType)gdbType; gdbType = dType.getChild(); } gdbType = createGDBDerivedType(gdbType, kind, d); dType.setChild(gdbType); } } // method returns the next token int getToken() { token = ""; //$NON-NLS-1$ int c = getch(); // Skip over any space while (isCSpace(c)) { c = getch(); } //char character = (char) c; if (c == '(') { c = getch(); if (c == ')') { token = "()"; //$NON-NLS-1$ tokenType = PARENS; } else if (isCIdentifierStart(c)) { int i = 0; token += (char)c; while (i == 0 && c != ')') { if (c == EOF) { // Unbalanced parantheses. break; } c = getch(); token += (char)c; if (c == '(') { ++i; } else if (c == ')') { --i; } } tokenType = PARENS; } else { ungetch(); tokenType = '('; } } else if (c == '[') { while ((c = getch()) != ']' && c != EOF) { token += (char) c; } tokenType = BRACKETS; } else if (isCIdentifierStart(c)) { StringBuilder sb = new StringBuilder(); sb.append((char) c); while (isCIdentifierPart((c = getch())) && c != EOF) { sb.append((char) c); } if (c == '<') { // Swallow template args in types like "class foobar<A,B> : public C {..} *" // FIXME: if the bracket is not terminate do we throw exception? sb.append((char) c); int count = 1; do { c = getch(); if (c == '<') { count++; } else if (c == '>') { count--; } if (c != ' ') { sb.append((char)c); } } while (count > 0 && c != EOF); } else if (c != EOF) { ungetch(); } token = sb.toString(); tokenType = NAME; } else if (c == '{') { // Swallow gdb sends things like "struct foobar {..} *" // FIXME: if the bracket is not terminate do we throw exception? int count = 1; do { c = getch(); if (c == '{') { count++; } else if (c == '}') { count--; } } while (count > 0 && c != EOF); } else { tokenType = c; } return tokenType; } void dcl() { dcl(getToken()); } // parse a declarator void dcl(int c) { int nstar = 0; int namp = 0; if (c == '*') { nstar++; for (; getToken() == '*'; nstar++) { } } else if (c == '&') { namp++; for (; getToken() == '&'; namp++) { } } dirdcl(); while (nstar-- > 0) { insertingChild(GDBType.POINTER); } while (namp-- > 0) { insertingChild(GDBType.REFERENCE); } } // parse a direct declarator void dirdcl() { int type; if (tokenType == '(') { dcl(); if (tokenType != ')' /*&& !name.isEmpty()*/) { // Do we throw an exception on unterminated parentheses // It should have been handle by getToken() return; } } else if (tokenType == NAME) { // Useless we do not need the name of the variable name = " " + token; //$NON-NLS-1$ } else if (tokenType == PARENS) { insertingChild(GDBType.FUNCTION); } else if (tokenType == BRACKETS) { int len = 0; if (!token.isEmpty()) { try { len = Integer.parseInt(token); } catch (NumberFormatException e) { } } insertingChild(GDBType.ARRAY, len); } else if (tokenType == '&') { insertingChild(GDBType.REFERENCE); } else { // oops bad declaration ? return; } while ((type = getToken()) == PARENS || type == BRACKETS) { if (type == PARENS) { insertingChild(GDBType.FUNCTION); } else { /* BRACKETS */ int len = 0; if (!token.isEmpty()) { try { len = Integer.parseInt(token); } catch (NumberFormatException e) { } } insertingChild(GDBType.ARRAY, len); } } } /** * @since 4.4 */ protected GDBDerivedType createGDBDerivedType(GDBType c, int t, int dim) { return new GDBDerivedType(c, t, dim); } public static void main(String[] args) { GDBTypeParser parser = new GDBTypeParser(); System.out.println("int *&"); //$NON-NLS-1$ parser.parse("int *&"); //$NON-NLS-1$ System.out.println(GDBTypeParser.unParse(parser.getGDBType())); System.out.println(parser.getGDBType().verbose()); System.out.println(); System.out.println("int (&rg)(int)"); //$NON-NLS-1$ parser.parse("int (&rg)(int)"); //$NON-NLS-1$ System.out.println(GDBTypeParser.unParse(parser.getGDBType())); System.out.println(parser.getGDBType().verbose()); System.out.println(); System.out.println("int (&ra)[3]"); //$NON-NLS-1$ parser.parse("int (&rg)[3]"); //$NON-NLS-1$ System.out.println(GDBTypeParser.unParse(parser.getGDBType())); System.out.println(parser.getGDBType().verbose()); System.out.println(); System.out.println("struct link { int i; int j; struct link * next;} *"); //$NON-NLS-1$ parser.parse("struct link { int i; int j; struct link * next} *"); //$NON-NLS-1$ System.out.println(GDBTypeParser.unParse(parser.getGDBType())); System.out.println(parser.getGDBType().verbose()); System.out.println(); System.out.println("class ns::link<8, ns::A> : public ns::B { int i; int j; struct link * next;} *"); //$NON-NLS-1$ parser.parse("class ns::link<8, ns::A> : public ns::B { int i; int j; struct link * next;} *"); //$NON-NLS-1$ System.out.println(GDBTypeParser.unParse(parser.getGDBType())); System.out.println(parser.getGDBType().verbose()); System.out.println(); System.out.println("char **argv"); //$NON-NLS-1$ parser.parse("char **argv"); //$NON-NLS-1$ System.out.println(GDBTypeParser.unParse(parser.getGDBType())); System.out.println(parser.getGDBType().verbose()); System.out.println(); System.out.println("int (*daytab)[13]"); //$NON-NLS-1$ parser.parse("int (*daytab)[13]"); //$NON-NLS-1$ System.out.println(GDBTypeParser.unParse(parser.getGDBType())); System.out.println(parser.getGDBType().verbose()); System.out.println(); System.out.println("int *daytab[13]"); //$NON-NLS-1$ parser.parse("int *daytab[13]"); //$NON-NLS-1$ System.out.println(GDBTypeParser.unParse(parser.getGDBType())); System.out.println(parser.getGDBType().verbose()); System.out.println(); System.out.println("void *comp()"); //$NON-NLS-1$ parser.parse("void *comp()"); //$NON-NLS-1$ System.out.println(GDBTypeParser.unParse(parser.getGDBType())); System.out.println(parser.getGDBType().verbose()); System.out.println(); System.out.println("void (*comp)()"); //$NON-NLS-1$ parser.parse("void (*comp)()"); //$NON-NLS-1$ System.out.println(GDBTypeParser.unParse(parser.getGDBType())); System.out.println(parser.getGDBType().verbose()); System.out.println(); System.out.println("int (*func[15])()"); //$NON-NLS-1$ parser.parse("int (*func[15])()"); //$NON-NLS-1$ System.out.println(GDBTypeParser.unParse(parser.getGDBType())); System.out.println(parser.getGDBType().verbose()); System.out.println(); System.out.println("char (*(*x())[])()"); //$NON-NLS-1$ parser.parse("char (*(*x())[])()"); //$NON-NLS-1$ System.out.println(GDBTypeParser.unParse(parser.getGDBType())); System.out.println(parser.getGDBType().verbose()); System.out.println(); System.out.println("char (*(*x[3])())[5]"); //$NON-NLS-1$ parser.parse("char (*(*x[3])())[5]"); //$NON-NLS-1$ System.out.println(GDBTypeParser.unParse(parser.getGDBType())); System.out.println(parser.getGDBType().verbose()); System.out.println(); System.out.println("char *[5]"); //$NON-NLS-1$ parser.parse("char *[5]"); //$NON-NLS-1$ System.out.println(GDBTypeParser.unParse(parser.getGDBType())); System.out.println(parser.getGDBType().verbose()); System.out.println(); System.out.println("int [2][3]"); //$NON-NLS-1$ parser.parse("int [2][3]"); //$NON-NLS-1$ System.out.println(GDBTypeParser.unParse(parser.getGDBType())); System.out.println(parser.getGDBType().verbose()); System.out.println(); System.out.println("int (int, char **)"); //$NON-NLS-1$ parser.parse("int (int, char **)"); //$NON-NLS-1$ System.out.println(GDBTypeParser.unParse(parser.getGDBType())); System.out.println(parser.getGDBType().verbose()); System.out.println(); System.out.println("int (int)"); //$NON-NLS-1$ parser.parse("int (int)"); //$NON-NLS-1$ System.out.println(GDBTypeParser.unParse(parser.getGDBType())); System.out.println(parser.getGDBType().verbose()); System.out.println(); System.out.println("int (void)"); //$NON-NLS-1$ parser.parse("int (void)"); //$NON-NLS-1$ System.out.println(GDBTypeParser.unParse(parser.getGDBType())); System.out.println(parser.getGDBType().verbose()); System.out.println(); System.out.println("int ()"); //$NON-NLS-1$ parser.parse("int ()"); //$NON-NLS-1$ System.out.println(GDBTypeParser.unParse(parser.getGDBType())); System.out.println(parser.getGDBType().verbose()); System.out.println(); } }