/
EnumParser.java
265 lines (233 loc) · 8.94 KB
/
EnumParser.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
/*******************************************************************************
* Copyright (c) 2015, 2023 Ericsson
*
* All rights reserved. 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
*******************************************************************************/
package org.eclipse.tracecompass.internal.ctf.core.event.metadata.tsdl.enumeration;
import static org.eclipse.tracecompass.internal.ctf.core.event.metadata.tsdl.TsdlUtils.childTypeError;
import java.util.List;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.eclipse.tracecompass.ctf.core.event.metadata.DeclarationScope;
import org.eclipse.tracecompass.ctf.core.event.types.EnumDeclaration;
import org.eclipse.tracecompass.ctf.core.event.types.IDeclaration;
import org.eclipse.tracecompass.ctf.core.event.types.IntegerDeclaration;
import org.eclipse.tracecompass.ctf.core.trace.CTFTrace;
import org.eclipse.tracecompass.ctf.parser.CTFParser;
import org.eclipse.tracecompass.internal.ctf.core.event.metadata.AbstractScopedCommonTreeParser;
import org.eclipse.tracecompass.internal.ctf.core.event.metadata.ParseException;
import org.eclipse.tracecompass.internal.ctf.core.event.types.ICTFMetadataNode;
/**
*
* Enumerations are a mapping between an integer type and a table of strings.
* The numerical representation of the enumeration follows the integer type
* specified by the metadata. The enumeration mapping table is detailed in the
* enumeration description within the metadata. The mapping table maps inclusive
* value ranges (or single values) to strings. Instead of being limited to
* simple `value -> string` mappings, these enumerations map `[ start_value ...
* end_value ] -> string`, which map inclusive ranges of values to strings. An
* enumeration from the C language can be represented in this format by having
* the same `start_value` and `end_value` for each mapping, which is in fact a
* range of size 1. This single-value range is supported without repeating the
* start and end values with the `value = string` declaration. Enumerations need
* to contain at least one entry. <br>
*
* <pre>
enum name : integer_type {
somestring = start_value1 ... end_value1 ,
"other string" = start_value2 ... end_value2 ,
yet_another_string, will be assigned to end_value2 + 1 ,
"some other string" = value,
//...
}
* </pre>
*
* If the values are omitted, the enumeration starts at 0 and increment of 1 for
* each entry. An entry with omitted value that follows a range entry takes as
* value the `end_value` of the previous range + 1:
*
* <pre>
enum name : unsigned int {
ZERO,
ONE,
TWO,
TEN = 10,
ELEVEN,
}
* </pre>
*
* Overlapping ranges within a single enumeration are implementation defined.
* <br>
* A nameless enumeration can be declared as a field type or as part of a
* `typedef`:
*
* <pre>
enum : integer_type {
// ...
}
* </pre>
*
* Enumerations omitting the container type`:integer_type`use the`int`type(for
* compatibility with C99).The`int`type _must be_ previously declared,e.g.:
*
* <pre>
* typealias integer{size=32;align=32;signed=true;}:=int;
enum{
// ...
}
* </pre>
*
* @author Matthew Khouzam
* @author Efficios - Description
*
*/
public final class EnumParser extends AbstractScopedCommonTreeParser {
/**
* Parameter Object
*
* @author Matthew Khouzam
*
*/
@NonNullByDefault
public static final class Param implements ICommonTreeParserParameter {
private final DeclarationScope fCurrentScope;
private final CTFTrace fTrace;
/**
* Parameter object constructor
*
* @param trace
* Trace to populate
* @param currentScope
* the current scope
*/
public Param(CTFTrace trace, DeclarationScope currentScope) {
fTrace = trace;
fCurrentScope = currentScope;
}
}
/**
* Instance
*/
public static final EnumParser INSTANCE = new EnumParser();
private EnumParser() {
}
/**
* Parses an enum declaration and returns the corresponding declaration.
*
* @param theEnum
* An ENUM node.
*
* @return The corresponding enum declaration.
* @throws ParseException
* there was an error parsing the enum. Probably a mal-formed
* tree.
*/
@Override
public EnumDeclaration parse(ICTFMetadataNode theEnum, ICommonTreeParserParameter param) throws ParseException {
if (!(param instanceof Param)) {
throw new IllegalArgumentException("Param must be a " + Param.class.getCanonicalName()); //$NON-NLS-1$
}
Param parameter = (Param) param;
DeclarationScope scope = parameter.fCurrentScope;
List<ICTFMetadataNode> children = theEnum.getChildren();
/* The return value */
EnumDeclaration enumDeclaration = null;
/* Name */
@Nullable String enumName = null;
/* Body */
ICTFMetadataNode enumBody = null;
/* Container type */
IntegerDeclaration containerTypeDeclaration = null;
/* Loop on all children and identify what we have to work with. */
for (ICTFMetadataNode child : children) {
switch (child.getType()) {
case CTFParser.ENUM_NAME: {
ICTFMetadataNode enumNameIdentifier = child.getChild(0);
enumName = enumNameIdentifier.getText();
break;
}
case CTFParser.ENUM_BODY: {
enumBody = child;
break;
}
case CTFParser.ENUM_CONTAINER_TYPE: {
CTFTrace trace = ((Param) param).fTrace;
containerTypeDeclaration = EnumContainerParser.INSTANCE.parse(child, new EnumContainerParser.Param(trace, scope));
break;
}
default:
throw childTypeError(child);
}
}
/*
* If the container type has not been defined explicitly, we assume it
* is "int".
*/
if (containerTypeDeclaration == null) {
IDeclaration enumDecl;
/*
* it could be because the enum was already declared.
*/
if (enumName != null) {
scope.setName(enumName);
enumDecl = scope.lookupEnumRecursive(enumName);
if (enumDecl != null) {
return (EnumDeclaration) enumDecl;
}
}
IDeclaration decl = scope.lookupTypeRecursive("int"); //$NON-NLS-1$
if (decl == null) {
throw new ParseException("enum container type implicit and type int not defined"); //$NON-NLS-1$
} else if (!(decl instanceof IntegerDeclaration)) {
throw new ParseException("enum container type implicit and type int not an integer"); //$NON-NLS-1$
}
containerTypeDeclaration = (IntegerDeclaration) decl;
}
/*
* If it has a body, it's a new declaration, otherwise it's a reference
* to an existing declaration. Same logic as struct.
*/
if (enumBody != null) {
/*
* If enum has a name, check if already defined in the current
* scope.
*/
if ((enumName != null)
&& (scope.lookupEnum(enumName) != null)) {
throw new ParseException("enum " + enumName //$NON-NLS-1$
+ " already defined"); //$NON-NLS-1$
}
/* Create the declaration */
enumDeclaration = new EnumDeclaration(containerTypeDeclaration);
/* Parse the body */
EnumBodyParser.INSTANCE.parse(enumBody, new EnumBodyParser.Param(enumDeclaration));
/* If the enum has name, add it to the current scope. */
if (enumName != null) {
scope.registerEnum(enumName, enumDeclaration);
}
} else {
if (enumName != null) {
/* Name and !body */
/* Lookup the name in the current scope. */
enumDeclaration = scope.lookupEnumRecursive(enumName);
/*
* If not found, it means that an enum with such name has not
* been defined
*/
if (enumDeclaration == null) {
throw new ParseException("enum " + enumName //$NON-NLS-1$
+ " is not defined"); //$NON-NLS-1$
}
} else {
/* !Name and !body */
throw new ParseException("enum with no name and no body"); //$NON-NLS-1$
}
}
return enumDeclaration;
}
}