@foxglove/omgidl-parser
Version:
Parse OMG IDL to flattened definitions for serialization
113 lines (99 loc) • 4.01 kB
text/typescript
import { IDLNode } from "./IDLNode";
import { StructMemberIDLNode } from "./StructMemberIDLNode";
import { AnyIDLNode, IEnumIDLNode, ITypedefIDLNode, IUnionIDLNode } from "./interfaces";
import { UnionASTNode } from "../astTypes";
import { INTEGER_TYPES, SIMPLE_TYPES, normalizeType } from "../primitiveTypes";
import { Case, IDLMessageDefinition, IDLMessageDefinitionField } from "../types";
export class UnionIDLNode extends IDLNode<UnionASTNode> implements IUnionIDLNode {
private switchTypeNeedsResolution = false;
constructor(scopePath: string[], astNode: UnionASTNode, idlMap: Map<string, AnyIDLNode>) {
super(scopePath, astNode, idlMap);
if (!SIMPLE_TYPES.has(this.astNode.switchType)) {
this.switchTypeNeedsResolution = true;
}
}
get type(): string {
return this.astNode.name;
}
// eslint-disable-next-line @typescript-eslint/class-literal-property-style
get isComplex(): boolean {
return true;
}
private _switchTypeNode?: IEnumIDLNode | ITypedefIDLNode;
switchTypeNode(): IEnumIDLNode | ITypedefIDLNode {
if (this._switchTypeNode) {
return this._switchTypeNode;
}
const typeNode = this.getNode(this.scopePath, this.astNode.switchType);
if (typeNode.declarator !== "enum" && typeNode.declarator !== "typedef") {
throw new Error(
`Invalid switch type "${typeNode.scopedIdentifier}" ${this.astNode.switchType} in ${this.scopedIdentifier}`,
);
}
this._switchTypeNode = typeNode;
return typeNode;
}
get switchType(): string {
let switchType = this.astNode.switchType;
if (this.switchTypeNeedsResolution) {
switchType = this.switchTypeNode().type;
}
if (!isValidSwitchType(switchType)) {
throw new Error(`Invalid resolved switch type ${switchType} in ${this.scopedIdentifier}`);
}
return switchType;
}
get cases(): Case[] {
// If the switch type is an enum that means the case predicate values can be just the enumerator name
// So we need to search the scope of the enum for the enumerator and get its value
const isEnumSwitchType =
this.switchTypeNeedsResolution && this.switchTypeNode().declarator === "enum";
const predicateScopePath = isEnumSwitchType
? this.switchTypeNode().scopedIdentifier.split("::")
: this.scopePath;
return this.astNode.cases.map((def) => {
// These are not referenced anywhere so not necessary having in the map
const typeNode = new StructMemberIDLNode(
[...this.scopePath, this.name],
{ ...def.type, declarator: "struct-member" }, // unfortunate shoehorning for struct-member node
this.map,
);
const resolvedPredicates = def.predicates.map((predicate) => {
if (typeof predicate === "object") {
return this.getConstantNode(predicate.name, predicateScopePath).value as number | boolean;
}
return predicate;
});
const resolvedType = typeNode.toIDLMessageDefinitionField();
return {
type: resolvedType,
predicates: resolvedPredicates,
};
});
}
get defaultCase(): IDLMessageDefinitionField | undefined {
if (!this.astNode.defaultCase) {
return undefined;
}
const typeNode = new StructMemberIDLNode(
[...this.scopePath, this.name],
{ ...this.astNode.defaultCase, declarator: "struct-member" }, // unfortunate shoehorning for struct-member node
this.map,
);
return typeNode.toIDLMessageDefinitionField();
}
toIDLMessageDefinition(): IDLMessageDefinition {
const annotations = this.annotations;
return {
name: this.scopedIdentifier,
switchType: normalizeType(this.switchType),
cases: this.cases,
aggregatedKind: "union",
...(this.astNode.defaultCase ? { defaultCase: this.defaultCase } : undefined),
...(annotations ? { annotations } : undefined),
};
}
}
function isValidSwitchType(type: string): boolean {
return INTEGER_TYPES.has(type) || type === "bool";
}