Skip to content

Commit

Permalink
Support Union definition processing (#63)
Browse files Browse the repository at this point in the history
Support for resolving references in Union cases, switchType, or
defaultCase.

FG-4329
  • Loading branch information
snosenzo authored Aug 30, 2023
1 parent 80a951f commit 7fe4a3a
Show file tree
Hide file tree
Showing 16 changed files with 1,318 additions and 52 deletions.
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -49,9 +49,9 @@
"eslint-plugin-import": "^2.27.5",
"eslint-plugin-jest": "^27.2.2",
"eslint-plugin-prettier": "^4.2.1",
"jest": "28.1.3",
"jest": "29.6.4",
"prettier": "2.7.1",
"ts-jest": "28.0.8",
"ts-jest": "29.1.1",
"typescript": "4.8.3"
}
}
2 changes: 1 addition & 1 deletion packages/omgidl-parser/src/IDLNodes/IDLNode.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import { BaseASTNode } from "../astTypes";
*/
export abstract class IDLNode<T extends BaseASTNode = BaseASTNode> implements IIDLNode<T> {
/** Map of all IDLNodes in a schema definition */
private map: Map<string, AnyIDLNode>;
protected map: Map<string, AnyIDLNode>;
/** Unresolved node parsed directly from schema */
protected readonly astNode: T;
/** Array of strings that represent namespace scope that astNode is contained within. */
Expand Down
18 changes: 14 additions & 4 deletions packages/omgidl-parser/src/IDLNodes/ReferenceTypeIDLNode.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
import { IDLNode } from "./IDLNode";
import { StructIDLNode } from "./StructIDLNode";
import { AnyIDLNode, IEnumIDLNode, IReferenceTypeIDLNode, IStructIDLNode } from "./interfaces";
import {
AnyIDLNode,
IEnumIDLNode,
IReferenceTypeIDLNode,
IStructIDLNode,
IUnionIDLNode,
} from "./interfaces";
import {
BaseASTNode,
StructMemberASTNode,
Expand All @@ -9,7 +14,11 @@ import {
} from "../astTypes";
import { SIMPLE_TYPES } from "../primitiveTypes";

type PossibleTypeRefNode = IStructIDLNode | IReferenceTypeIDLNode<TypedefASTNode> | IEnumIDLNode;
type PossibleTypeRefNode =
| IStructIDLNode
| IReferenceTypeIDLNode<TypedefASTNode>
| IEnumIDLNode
| IUnionIDLNode;

/** Class used for struct members and typedefs because they can reference each other and other types (enum and struct)
* This class resolves the fields of these types to their final values.
Expand Down Expand Up @@ -50,7 +59,7 @@ export abstract class ReferenceTypeIDLNode<T extends TypedefASTNode | StructMemb
if (parent.declarator === "typedef") {
return parent.isComplex;
}
return parent instanceof StructIDLNode;
return parent.declarator === "struct" || parent.declarator === "union";
}

get isArray(): boolean | undefined {
Expand Down Expand Up @@ -159,6 +168,7 @@ export abstract class ReferenceTypeIDLNode<T extends TypedefASTNode | StructMemb
if (
!(maybeValidParent.declarator === "struct") &&
!(maybeValidParent.declarator === "typedef") &&
!(maybeValidParent.declarator === "union") &&
!(maybeValidParent.declarator === "enum")
) {
throw new Error(
Expand Down
86 changes: 86 additions & 0 deletions packages/omgidl-parser/src/IDLNodes/UnionIDLNode.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
import { IDLNode } from "./IDLNode";
import { StructMemberIDLNode } from "./StructMemberIDLNode";
import { AnyIDLNode, IUnionIDLNode } from "./interfaces";
import { UnionASTNode } from "../astTypes";
import { INTEGER_TYPES, SIMPLE_TYPES } from "../primitiveTypes";
import { Case, IDLMessageDefinition, IDLMessageDefinitionField } from "../types";

export class UnionIDLNode extends IDLNode<UnionASTNode> implements IUnionIDLNode {
constructor(scopePath: string[], astNode: UnionASTNode, idlMap: Map<string, AnyIDLNode>) {
super(scopePath, astNode, idlMap);
}

get type(): string {
return this.astNode.name;
}

get isComplex(): boolean {
return true;
}

get switchType(): string {
let switchType = this.astNode.switchType;
if (!SIMPLE_TYPES.has(this.astNode.switchType)) {
const typeNode = this.getNode(this.scopePath, this.astNode.switchType);
if (typeNode.declarator === "enum" || typeNode.declarator === "typedef") {
switchType = typeNode.type;
}
}
if (!isValidSwitchType(switchType)) {
throw new Error(`Invalid resolved switch type ${switchType} in ${this.scopedIdentifier}`);
}
return switchType;
}

get cases(): Case[] {
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).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 {
return {
name: this.scopedIdentifier,
switchType: this.switchType,
cases: this.cases,
aggregatedKind: "union",
...(this.astNode.defaultCase ? { defaultCase: this.defaultCase } : undefined),
...(this.astNode.annotations ? { annotations: this.astNode.annotations } : undefined),
};
}
}

function isValidSwitchType(type: string): boolean {
return INTEGER_TYPES.has(type) || type === "bool";
}
12 changes: 11 additions & 1 deletion packages/omgidl-parser/src/IDLNodes/interfaces.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,9 @@ import {
StructASTNode,
StructMemberASTNode,
TypedefASTNode,
UnionASTNode,
} from "../astTypes";
import { IDLMessageDefinition, IDLMessageDefinitionField } from "../types";
import { Case, IDLMessageDefinition, IDLMessageDefinitionField } from "../types";

export interface IIDLNode<T extends BaseASTNode = BaseASTNode> {
readonly scopePath: string[];
Expand Down Expand Up @@ -57,10 +58,19 @@ export interface IStructMemberIDLNode extends IReferenceTypeIDLNode<StructMember

export type ITypedefIDLNode = IReferenceTypeIDLNode<TypedefASTNode>;

export interface IUnionIDLNode extends IIDLNode<UnionASTNode> {
isComplex: boolean;
switchType: string;
cases: Case[];
defaultCase: IDLMessageDefinitionField | undefined;
toIDLMessageDefinition(): IDLMessageDefinition;
}

export type AnyIDLNode =
| IConstantIDLNode
| IEnumIDLNode
| IModuleIDLNode
| IStructIDLNode
| IStructMemberIDLNode
| IUnionIDLNode
| ITypedefIDLNode;
1 change: 1 addition & 0 deletions packages/omgidl-parser/src/idl.ne
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,7 @@ union -> "union" fieldName "switch" "(" switchTypedef ")" "{" switchBody "}" {%
const defaultCase = allCases.find(c => "default" in c);
const cases = allCases.filter(c => ("predicates" in c));
const unionNode = {
declarator: "union",
name,
switchType,
cases,
Expand Down
Loading

0 comments on commit 7fe4a3a

Please sign in to comment.