blob: 6b027d8912059943cf8734dd4f5651f99272f12e [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2010, 2016 Wind River Systems and others.
*
* This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* 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();
}
}