UNPKG

@foxglove/omgidl-parser

Version:

Parse OMG IDL to flattened definitions for serialization

2,055 lines (2,034 loc) 65.9 kB
import { parseIDL as parse, parseIDL } from "./parseIDL"; describe("omgidl parser tests", () => { it("parses a struct", () => { const schema = ` struct A { int32 num; }; `; const types = parse(schema); expect(types).toEqual([ { name: "A", aggregatedKind: "struct", definitions: [ { isComplex: false, name: "num", type: "int32", }, ], }, ]); }); it("parses a struct with a member that references another struct", () => { const schema = ` struct B { A complexRef; }; struct A { int32 num; }; `; const types = parse(schema); expect(types).toEqual([ { name: "B", aggregatedKind: "struct", definitions: [ { isComplex: true, name: "complexRef", type: "A", }, ], }, { name: "A", aggregatedKind: "struct", definitions: [ { isComplex: false, name: "num", type: "int32", }, ], }, ]); }); it("parses a struct with multiple primitive definitions", () => { const schema = ` struct A { int32 num; float vec3[3]; sequence<uint8, 10> seq; string str; }; `; const types = parse(schema); expect(types).toEqual([ { name: "A", aggregatedKind: "struct", definitions: [ { isComplex: false, name: "num", type: "int32", }, { isComplex: false, name: "vec3", type: "float32", isArray: true, arrayLengths: [3], }, { isComplex: false, name: "seq", type: "uint8", isArray: true, arrayUpperBound: 10, }, { isComplex: false, name: "str", type: "string", }, ], }, ]); }); it("parses a module with an enclosed struct", () => { const types = parse( ` module rosidl_parser { module action { struct MyAction_Goal { int32 input_value; }; }; }; `, ); expect(types).toEqual([ { name: "rosidl_parser::action::MyAction_Goal", aggregatedKind: "struct", definitions: [ { isComplex: false, name: "input_value", type: "int32", }, ], }, ]); }); it("parses a module with an enclosed struct and module", () => { const types = parse( ` module rosidl_parser { module action { module MyAction_Goal_Constants { const short SHORT_CONSTANT = -23; }; struct MyAction_Goal { int32 input_value; }; }; }; `, ); expect(types).toEqual([ { aggregatedKind: "module", definitions: [ { isConstant: true, value: -23, valueText: "-23", isComplex: false, name: "SHORT_CONSTANT", type: "int16", }, ], name: "rosidl_parser::action::MyAction_Goal_Constants", }, { aggregatedKind: "struct", definitions: [ { isComplex: false, name: "input_value", type: "int32", }, ], name: "rosidl_parser::action::MyAction_Goal", }, ]); }); it("parses typedefs of complex types", () => { const types = parse( `module msg { typedef Point Point2D; struct PointCollection { sequence<Point2D> points; }; }; struct Point { float x; float y; };`, ); expect(types).toEqual([ { name: "msg::PointCollection", aggregatedKind: "struct", definitions: [ { name: "points", type: "Point", isComplex: true, isArray: true, arrayUpperBound: undefined, }, ], }, { name: "Point", aggregatedKind: "struct", definitions: [ { name: "x", type: "float32", isComplex: false, }, { name: "y", type: "float32", isComplex: false, }, ], }, ]); }); it("parses nested typedefs in modules and their usage", () => { const types = parse( `module msg { typedef float coord[2]; }; struct Point { msg::coord loc; };`, ); expect(types).toEqual([ { name: "Point", aggregatedKind: "struct", definitions: [ { name: "loc", type: "float32", isArray: true, arrayLengths: [2], isComplex: false, }, ], }, ]); }); it("parses typedefs by local and global names", () => { const types = parse( `module msg { typedef float coord[2]; struct Point { msg::coord loc; coord loc2; }; }; `, ); expect(types).toEqual([ { name: "msg::Point", aggregatedKind: "struct", definitions: [ { name: "loc", type: "float32", isArray: true, arrayLengths: [2], isComplex: false, }, { name: "loc2", type: "float32", isArray: true, arrayLengths: [2], isComplex: false, }, ], }, ]); }); it("parses typedefs by local and global names many levels deep into module", () => { const types = parse( `module layer1 { typedef float L1[1]; module layer2 { typedef float L2[2]; module layer3 { typedef float L3[3]; struct Point { layer1::L1 layer1L1; L1 lyr1; layer1::layer2::L2 layer1Layer2L2; layer2::L2 layer2L2; L2 lyr2; layer1::layer2::layer3::L3 layer1Layer2Layer3L3; layer2::layer3::L3 layer2Layer3L3; layer3::L3 layer3L3; L3 lyr3; }; }; }; }; `, ); expect(types).toEqual([ { name: "layer1::layer2::layer3::Point", aggregatedKind: "struct", definitions: [ { name: "layer1L1", type: "float32", isArray: true, arrayLengths: [1], isComplex: false, }, { name: "lyr1", type: "float32", isArray: true, arrayLengths: [1], isComplex: false, }, { name: "layer1Layer2L2", type: "float32", isArray: true, arrayLengths: [2], isComplex: false, }, { name: "layer2L2", type: "float32", isArray: true, arrayLengths: [2], isComplex: false, }, { name: "lyr2", type: "float32", isArray: true, arrayLengths: [2], isComplex: false, }, { name: "layer1Layer2Layer3L3", type: "float32", isArray: true, arrayLengths: [3], isComplex: false, }, { name: "layer2Layer3L3", type: "float32", isArray: true, arrayLengths: [3], isComplex: false, }, { name: "layer3L3", type: "float32", isArray: true, arrayLengths: [3], isComplex: false, }, { name: "lyr3", type: "float32", isArray: true, arrayLengths: [3], isComplex: false, }, ], }, ]); }); it("parses a module with a typedefs used in a struct", () => { const types = parse( ` module rosidl_parser { module action { typedef sequence<int32, 10> int32arr; @default (value=5) typedef short shortWithDefault; struct MyAction_Goal { int32arr intArray; shortWithDefault short5; }; }; }; `, ); expect(types).toEqual([ { aggregatedKind: "struct", definitions: [ { isComplex: false, name: "intArray", type: "int32", isArray: true, arrayUpperBound: 10, }, { isComplex: false, name: "short5", type: "int16", defaultValue: 5, annotations: { default: { name: "default", type: "named-params", namedParams: { value: 5, }, }, }, }, ], name: "rosidl_parser::action::MyAction_Goal", }, ]); }); it("parses a module with an multiple enclosed structs and modules", () => { const types = parse( ` module rosidl_parser { module action { module MyAction_Goal_Constants { const short SHORT_CONSTANT = -23; }; struct MyAction_Goal { int32 input_value; }; module MyAction_Result_Constants { const unsigned long UNSIGNED_LONG_CONSTANT = 42; }; struct MyAction_Result { uint32 output_value; }; module MyAction_Feedback_Constants { const float FLOAT_CONSTANT = 1.25; }; struct MyAction_Feedback { float progress_value; }; }; }; `, ); expect(types).toEqual([ { name: "rosidl_parser::action::MyAction_Goal_Constants", aggregatedKind: "module", definitions: [ { isConstant: true, type: "int16", name: "SHORT_CONSTANT", valueText: "-23", value: -23, isComplex: false, }, ], }, { name: "rosidl_parser::action::MyAction_Goal", aggregatedKind: "struct", definitions: [ { type: "int32", name: "input_value", isComplex: false, }, ], }, { name: "rosidl_parser::action::MyAction_Result_Constants", aggregatedKind: "module", definitions: [ { isConstant: true, type: "uint32", name: "UNSIGNED_LONG_CONSTANT", valueText: "42", value: 42, isComplex: false, }, ], }, { name: "rosidl_parser::action::MyAction_Result", aggregatedKind: "struct", definitions: [ { type: "uint32", name: "output_value", isComplex: false, }, ], }, { name: "rosidl_parser::action::MyAction_Feedback_Constants", aggregatedKind: "module", definitions: [ { isConstant: true, type: "float32", name: "FLOAT_CONSTANT", valueText: "1.25", value: 1.25, isComplex: false, }, ], }, { name: "rosidl_parser::action::MyAction_Feedback", aggregatedKind: "struct", definitions: [ { type: "float32", name: "progress_value", isComplex: false, }, ], }, ]); }); it("ignore #include statements in AST", () => { const types = parse( ` #include "OtherMessage.idl" #include <pkgname::msg::OtherMessage.idl> module rosidl_parser { module action { module MyAction_Goal_Constants { const short SHORT_CONSTANT = -23; }; struct MyAction_Goal { int32 input_value; }; }; }; `, ); // same as above expect(types).toEqual([ { aggregatedKind: "module", definitions: [ { isConstant: true, value: -23, valueText: "-23", isComplex: false, name: "SHORT_CONSTANT", type: "int16", }, ], name: "rosidl_parser::action::MyAction_Goal_Constants", }, { aggregatedKind: "struct", definitions: [ { isComplex: false, name: "input_value", type: "int32", }, ], name: "rosidl_parser::action::MyAction_Goal", }, ]); }); it("parses a module full of string constants", () => { const types = parse( ` module rosidl_parser { module msg { module MyMessage_Constants { const string STRING_CONSTANT = "string_value"; const wstring WSTRING_CONSTANT = "wstring_value_\u2122"; const string EMPTY_STRING_CONSTANT = ""; const string COMBINED_STRING_CONSTANT = "part1 " "part2" " part3"; }; }; }; `, ); expect(types).toEqual([ { aggregatedKind: "module", definitions: [ { isConstant: true, value: "string_value", valueText: "string_value", isComplex: false, name: "STRING_CONSTANT", type: "string", }, { isConstant: true, value: "wstring_value_\u2122", valueText: "wstring_value_\u2122", isComplex: false, name: "WSTRING_CONSTANT", type: "wstring", }, { isConstant: true, value: "", valueText: "", isComplex: false, name: "EMPTY_STRING_CONSTANT", type: "string", }, { isConstant: true, value: "part1 part2 part3", valueText: "part1 part2 part3", isComplex: false, name: "COMBINED_STRING_CONSTANT", type: "string", }, ], name: "rosidl_parser::msg::MyMessage_Constants", }, ]); }); it("parses all non-array type declarations", () => { const types = parse( ` module rosidl_parser { module msg { struct MyMessage { unsigned short unsigned_short_value; long long_value; unsigned long unsigned_long_value; long long long_long_value; unsigned long long unsigned_long_long_value; float float_value; double double_value; char char_value; wchar wchar_value; boolean boolean_value; octet octet_value; int8 int8_value; uint8 uint8_value; int16 int16_value; uint16 uint16_value; int32 int32_value; uint32 uint32_value; int64 int64_value; uint64 uint64_value; string string_value; }; }; }; `, ); expect(types).toEqual([ { name: "rosidl_parser::msg::MyMessage", aggregatedKind: "struct", definitions: [ { type: "uint16", name: "unsigned_short_value", isComplex: false, }, { type: "int32", name: "long_value", isComplex: false, }, { type: "uint32", name: "unsigned_long_value", isComplex: false, }, { type: "int64", name: "long_long_value", isComplex: false, }, { type: "uint64", name: "unsigned_long_long_value", isComplex: false, }, { type: "float32", name: "float_value", isComplex: false, }, { type: "float64", name: "double_value", isComplex: false, }, { type: "uint8", name: "char_value", isComplex: false, }, { type: "wchar", name: "wchar_value", isComplex: false, }, { type: "bool", name: "boolean_value", isComplex: false, }, { type: "uint8", name: "octet_value", isComplex: false, }, { type: "int8", name: "int8_value", isComplex: false, }, { type: "uint8", name: "uint8_value", isComplex: false, }, { type: "int16", name: "int16_value", isComplex: false, }, { type: "uint16", name: "uint16_value", isComplex: false, }, { type: "int32", name: "int32_value", isComplex: false, }, { type: "uint32", name: "uint32_value", isComplex: false, }, { type: "int64", name: "int64_value", isComplex: false, }, { type: "uint64", name: "uint64_value", isComplex: false, }, { type: "string", name: "string_value", isComplex: false, }, ], }, ]); }); it("parses a module of all array types", () => { const types = parse( ` const unsigned long UNSIGNED_LONG_CONSTANT = 42; module rosidl_parser { module msg { struct MyMessage { string<5> bounded_string_value; wstring wstring_value; wstring<23> bounded_wstring_value; wstring<UNSIGNED_LONG_CONSTANT> constant_bounded_wstring_value; sequence<short> unbounded_short_values; sequence<short, 5> bounded_short_values; sequence<string> unbounded_values_of_unbounded_strings; sequence<string<3>> unbounded_values_of_bounded_strings; sequence<string<3>, 4> bounded_values_of_bounded_strings; short array_short_values[23]; }; }; }; `, ); expect(types).toEqual([ { name: "rosidl_parser::msg::MyMessage", aggregatedKind: "struct", definitions: [ { type: "string", upperBound: 5, name: "bounded_string_value", isComplex: false, }, { type: "wstring", name: "wstring_value", isComplex: false, }, { type: "wstring", upperBound: 23, name: "bounded_wstring_value", isComplex: false, }, { type: "wstring", upperBound: 42, name: "constant_bounded_wstring_value", isComplex: false, }, { type: "int16", isArray: true, name: "unbounded_short_values", isComplex: false, }, { type: "int16", isArray: true, arrayUpperBound: 5, name: "bounded_short_values", isComplex: false, }, { type: "string", isArray: true, name: "unbounded_values_of_unbounded_strings", isComplex: false, }, { type: "string", upperBound: 3, isArray: true, name: "unbounded_values_of_bounded_strings", isComplex: false, }, { type: "string", upperBound: 3, isArray: true, arrayUpperBound: 4, name: "bounded_values_of_bounded_strings", isComplex: false, }, { type: "int16", name: "array_short_values", isArray: true, arrayLengths: [23], isComplex: false, }, ], }, ]); }); it("parses a module with arbitrary annotations including default values", () => { const types = parse( ` module rosidl_parser { module msg { @verbatim ( language="comment", text="Documentation of MyMessage." "Adjacent string literal." ) @transfer_mode(SHMEM_REF) struct MyMessage { @default ( value=123 ) unsigned short unsigned_short_value; @key @range ( min=-10, max=10 ) long long_value; @verbatim (language="comment", text="") @arbitrary_annotation ( key1="value1", key2=TRUE, key3=0.0, key4=10 ) @key unsigned long unsigned_long_value; @id(100) @default(200) uint32 uint32_with_default; }; }; }; `, ); expect(types).toEqual([ { name: "rosidl_parser::msg::MyMessage", annotations: { verbatim: { name: "verbatim", type: "named-params", namedParams: { language: "comment", text: "Documentation of MyMessage.Adjacent string literal.", }, }, transfer_mode: { name: "transfer_mode", type: "const-param", value: { usesConstant: true, name: "SHMEM_REF", }, }, }, aggregatedKind: "struct", definitions: [ { defaultValue: 123, type: "uint16", name: "unsigned_short_value", isComplex: false, annotations: { default: { name: "default", namedParams: { value: 123, }, type: "named-params", }, }, }, { type: "int32", name: "long_value", isComplex: false, annotations: { key: { name: "key", type: "no-params", }, range: { name: "range", type: "named-params", namedParams: { max: 10, min: -10, }, }, }, }, { type: "uint32", name: "unsigned_long_value", isComplex: false, annotations: { arbitrary_annotation: { name: "arbitrary_annotation", namedParams: { key1: "value1", key2: true, key3: 0, key4: 10, }, type: "named-params", }, key: { name: "key", type: "no-params", }, verbatim: { name: "verbatim", namedParams: { language: "comment", text: "", }, type: "named-params", }, }, }, { type: "uint32", name: "uint32_with_default", isComplex: false, defaultValue: 200, annotations: { id: { name: "id", type: "const-param", value: 100, }, default: { name: "default", type: "const-param", value: 200, }, }, }, ], }, ]); }); it('parses a module with customTypes and properly replaces "::"', () => { const types = parse( ` module rosidl_parser { module msg { struct MyMessage { geometry::msg::Point single_point; geometry::msg::Point points_with_length[10]; sequence<geometry::msg::Point> points_with_length_sequence; }; }; }; module geometry { module msg { struct Point { float x; }; }; }; `, ); expect(types).toEqual([ { name: "rosidl_parser::msg::MyMessage", aggregatedKind: "struct", definitions: [ { type: "geometry::msg::Point", name: "single_point", isComplex: true, }, { type: "geometry::msg::Point", name: "points_with_length", isArray: true, arrayLengths: [10], isComplex: true, }, { type: "geometry::msg::Point", name: "points_with_length_sequence", arrayUpperBound: undefined, isArray: true, isComplex: true, }, ], }, { name: "geometry::msg::Point", aggregatedKind: "struct", definitions: [ { name: "x", type: "float32", isComplex: false, }, ], }, ]); }); it("parses a module with various floating point default values", () => { const types = parse( ` module rosidl_parser { module msg { struct MyMessage { @default ( value=1.9e10 ) float int_and_frac_with_positive_scientific; @default ( value=1.9e+10 ) float int_and_frac_with_explicit_positive_scientific; @default ( value=1.1e-10) float int_and_frac_with_negative_scientific; @default ( value=0.00009 ) float int_and_frac; @default ( value = 1. ) float int_with_empty_frac; @default ( value = .1 ) float frac_only; @default ( value=9e05 ) float int_with_positive_scientific; @default ( value=9e+05 ) float int_with_explicit_positive_scientific; @default ( value=9e-05 ) float int_with_negative_scientific; @default ( value=8.7d ) float fixed_int_and_frac; @default ( value=4.d ) float fixed_int_with_dot_only; @default ( value=.3d ) float fixed_frac_only; @default ( value=7d ) float fixed_int_only; }; }; }; `, ); expect(types).toEqual([ { aggregatedKind: "struct", definitions: [ { annotations: { default: { name: "default", namedParams: { value: 19000000000, }, type: "named-params", }, }, defaultValue: 19000000000, isComplex: false, name: "int_and_frac_with_positive_scientific", type: "float32", }, { annotations: { default: { name: "default", namedParams: { value: 19000000000, }, type: "named-params", }, }, defaultValue: 19000000000, isComplex: false, name: "int_and_frac_with_explicit_positive_scientific", type: "float32", }, { annotations: { default: { name: "default", namedParams: { value: 1.1e-10, }, type: "named-params", }, }, defaultValue: 1.1e-10, isComplex: false, name: "int_and_frac_with_negative_scientific", type: "float32", }, { annotations: { default: { name: "default", namedParams: { value: 0.00009, }, type: "named-params", }, }, defaultValue: 0.00009, isComplex: false, name: "int_and_frac", type: "float32", }, { annotations: { default: { name: "default", namedParams: { value: 1, }, type: "named-params", }, }, defaultValue: 1, isComplex: false, name: "int_with_empty_frac", type: "float32", }, { annotations: { default: { name: "default", namedParams: { value: 0.1, }, type: "named-params", }, }, defaultValue: 0.1, isComplex: false, name: "frac_only", type: "float32", }, { annotations: { default: { name: "default", namedParams: { value: 900000, }, type: "named-params", }, }, defaultValue: 900000, isComplex: false, name: "int_with_positive_scientific", type: "float32", }, { annotations: { default: { name: "default", namedParams: { value: 900000, }, type: "named-params", }, }, defaultValue: 900000, isComplex: false, name: "int_with_explicit_positive_scientific", type: "float32", }, { annotations: { default: { name: "default", namedParams: { value: 0.00009, }, type: "named-params", }, }, defaultValue: 0.00009, isComplex: false, name: "int_with_negative_scientific", type: "float32", }, { annotations: { default: { name: "default", namedParams: { value: 8.7, }, type: "named-params", }, }, defaultValue: 8.7, isComplex: false, name: "fixed_int_and_frac", type: "float32", }, { annotations: { default: { name: "default", namedParams: { value: 4, }, type: "named-params", }, }, defaultValue: 4, isComplex: false, name: "fixed_int_with_dot_only", type: "float32", }, { annotations: { default: { name: "default", namedParams: { value: 0.3, }, type: "named-params", }, }, defaultValue: 0.3, isComplex: false, name: "fixed_frac_only", type: "float32", }, { annotations: { default: { name: "default", namedParams: { value: 7, }, type: "named-params", }, }, defaultValue: 7, isComplex: false, name: "fixed_int_only", type: "float32", }, ], name: "rosidl_parser::msg::MyMessage", }, ]); }); it("parses a module full of numeric constants", () => { const types = parse( ` module rosidl_parser { module msg { module MyMessage_Constants { const short SHORT_CONSTANT = -23; const unsigned long UNSIGNED_LONG_CONSTANT = 42; const float FLOAT_CONSTANT = 1.25; const double EXP_DOUBLE_CONSTANT = 1.25e-3; }; }; }; `, ); expect(types).toEqual([ { aggregatedKind: "module", definitions: [ { isConstant: true, value: -23, valueText: "-23", isComplex: false, name: "SHORT_CONSTANT", type: "int16", }, { isConstant: true, value: 42, valueText: "42", isComplex: false, name: "UNSIGNED_LONG_CONSTANT", type: "uint32", }, { isConstant: true, value: 1.25, valueText: "1.25", isComplex: false, name: "FLOAT_CONSTANT", type: "float32", }, { isConstant: true, value: 1.25e-3, valueText: "1.25e-3", isComplex: false, name: "EXP_DOUBLE_CONSTANT", type: "float64", }, ], name: "rosidl_parser::msg::MyMessage_Constants", }, ]); }); it("can parse comments", () => { const msgDef = ` // All of these comments should be ignored module action { /** another comment */ module MyAction_Goal_Constants /** maybe a sneaky one here */ { // two here of the same type // _another one_ const string tricky = "/** is this a comment? */ // hopefully not"; // like I'm not even here }; /** a multi * line * comment */ struct MyAction_Goal { // two here of different types /** maybe one more that */ int32 input_value; /** inline */ }; }; `; const types = parse(msgDef); expect(types).toEqual([ { aggregatedKind: "module", definitions: [ { isConstant: true, value: "/** is this a comment? */ // hopefully not", valueText: "/** is this a comment? */ // hopefully not", upperBound: undefined, isComplex: false, name: "tricky", type: "string", }, ], name: "action::MyAction_Goal_Constants", }, { aggregatedKind: "struct", definitions: [ { isComplex: false, name: "input_value", type: "int32", }, ], name: "action::MyAction_Goal", }, ]); }); it("can parse multiple forward declarations on same line with default annotation", () => { const msgDef = ` module action { struct MyAction_Goal { @default(value=5) int32 int1, int2; }; }; `; const types = parse(msgDef); expect(types).toEqual([ { aggregatedKind: "struct", definitions: [ { defaultValue: 5, isComplex: false, name: "int1", type: "int32", annotations: { default: { name: "default", type: "named-params", namedParams: { value: 5, }, }, }, }, { defaultValue: 5, isComplex: false, name: "int2", type: "int32", annotations: { default: { name: "default", type: "named-params", namedParams: { value: 5, }, }, }, }, ], name: "action::MyAction_Goal", }, ]); }); it("parses enums", () => { const msgDef = ` enum COLORS { RED, GREEN, BLUE }; `; const types = parse(msgDef); expect(types).toEqual([ { name: "COLORS", aggregatedKind: "module", definitions: [ { name: "RED", type: "uint32", isComplex: false, isConstant: true, value: 0, }, { name: "GREEN", type: "uint32", isComplex: false, isConstant: true, value: 1, }, { name: "BLUE", type: "uint32", isComplex: false, isConstant: true, value: 2, }, ], }, ]); }); it("parses enums with only explicit values", () => { const msgDef = ` enum COLORS { @value(50) RED, @value(100) BLUE, @value(80) YELLOW }; `; const types = parse(msgDef); expect(types).toEqual([ { name: "COLORS", aggregatedKind: "module", definitions: [ { name: "RED", type: "uint32", isComplex: false, isConstant: true, value: 50, }, { name: "BLUE", type: "uint32", isComplex: false, isConstant: true, value: 100, }, { name: "YELLOW", type: "uint32", isComplex: false, isConstant: true, value: 80, }, ], }, ]); }); it("parses enums with implicit and explicit values", () => { const msgDef = ` enum COLORS { @value(50) RED, GREEN, @value(100) BLUE, CYAN, MAGENTA, @value(80) YELLOW }; `; const types = parse(msgDef); expect(types).toEqual([ { name: "COLORS", aggregatedKind: "module", definitions: [ { name: "RED", type: "uint32", isComplex: false, isConstant: true, value: 50, }, { name: "GREEN", type: "uint32", isComplex: false, isConstant: true, value: 51, }, { name: "BLUE", type: "uint32", isComplex: false, isConstant: true, value: 100, }, { name: "CYAN", type: "uint32", isComplex: false, isConstant: true, value: 101, }, { name: "MAGENTA", type: "uint32", isComplex: false, isConstant: true, value: 102, }, { name: "YELLOW", type: "uint32", isComplex: false, isConstant: true, value: 80, }, ], }, ]); }); it("parses enums in modules", () => { const msgDef = ` module Scene { enum COLORS { RED, GREEN, BLUE }; }; `; const types = parse(msgDef); expect(types).toEqual([ { name: "Scene::COLORS", aggregatedKind: "module", definitions: [ { name: "RED", type: "uint32", isComplex: false, isConstant: true, value: 0, }, { name: "GREEN", type: "uint32", isComplex: false, isConstant: true, value: 1, }, { name: "BLUE", type: "uint32", isComplex: false, isConstant: true, value: 2, }, ], }, ]); }); it("parses enums used as type", () => { const msgDef = ` enum COLORS { RED, GREEN, BLUE }; struct Line { COLORS color; }; `; const types = parse(msgDef); expect(types).toEqual([ { name: "COLORS", aggregatedKind: "module", definitions: [ { name: "RED", type: "uint32", isComplex: false, isConstant: true, value: 0, }, { name: "GREEN", type: "uint32", isComplex: false, isConstant: true, value: 1, }, { name: "BLUE", type: "uint32", isComplex: false, isConstant: true, value: 2, }, ], }, { name: "Line", aggregatedKind: "struct", definitions: [ { name: "color", type: "uint32", isComplex: false, enumType: "COLORS", }, ], }, ]); }); it("parses enums used as constants from both enum and parent module namespaces", () => { const msgDef = ` module Test { enum COLORS { RED, GREEN, BLUE }; }; module Scene { module DefaultColors { const Test::COLORS red = Test::COLORS::RED; // enum namespace const Test::COLORS red2 = Test::RED; // parent module namespace }; struct Line { @default(value=Test::COLORS::GREEN) Test::COLORS color; }; }; `; const types = parse(msgDef); expect(types).toEqual([ { name: "Test::COLORS", aggregatedKind: "module", definitions: [ { name: "RED", type: "uint32", isComplex: false, isConstant: true, value: 0, }, { name: "GREEN", type: "uint32", isComplex: false, isConstant: true, value: 1, }, { name: "BLUE", type: "uint32", isComplex: false, isConstant: true, value: 2, }, ], }, { name: "Scene::DefaultColors", aggregatedKind: "module", definitions: [ { isConstant: true, name: "red", type: "uint32", isComplex: false, value: 0, valueText: "Test::COLORS::RED", }, { isConstant: true, name: "red2", type: "uint32", isComplex: false, value: 0, valueText: "Test::RED", }, ], }, { name: "Scene::Line", aggregatedKind: "struct", definitions: [ { name: "color", type: "uint32", isComplex: false, enumType: "Test::COLORS", defaultValue: 1, annotations: { default: { name: "default", type: "named-params", namedParams: { value: { usesConstant: true, name: "Test::COLORS::GREEN" }, }, }, }, }, ], }, ]); }); it("prioritizes typedef usage annotations over typedef declaration annotations", () => { const msgDef = ` @default(value=2) typedef uint8 byteWithDefault; struct JustACoupleNumbers { byteWithDefault byteWithSameDefault; @default(value=4) byteWithDefault byteWithDifferentDefault; }; `; const types = parse(msgDef); expect(types).toEqual([ { aggregatedKind: "struct", definitions: [ { name: "byteWithSameDefault", type: "uint8", defaultValue: 2, isComplex: false, annotations: { default: { name: "default", namedParams: { value: 2, }, type: "named-params", }, }, }, { name: "byteWithDifferentDefault", type: "uint8", defaultValue: 4, isComplex: false, annotations: { default: { name: "default", namedParams: { value: 4, }, type: "named-params", }, }, }, ], name: "JustACoupleNumbers", }, ]); }); it("resolves arrayLengths of typedefs used in structs", () => { const msgDef = ` typedef float grid45[4][5]; struct BigGrid { grid45 gridLine[1][2][3]; };`; const types = parse(msgDef); expect(types).toEqual([ { aggregatedKind: "struct", definitions: [ { name: "gridLine", arrayLengths: [1, 2, 3, 4, 5], isArray: true, isComplex: false, type: "float32", }, ], name: "BigGrid", }, ]); }); it("resolves multi-dimensional array constant usage", () => { const msgDef = ` const uint16 rows = 4; const uint16 cols = 5; struct GridBoard { float grid[rows][cols]; };`; const types = parse(msgDef); expect(types).toEqual([ { name: "GridBoard", aggregatedKind: "struct", definitions: [ { name: "grid", arrayLengths: [4, 5], isArray: true, isComplex: false, type: "float32", }, ], }, ]); }); it("can parse typedefs that reference other typedefs", () => { const msgDef = ` typedef sequence<int32, 10> int32arr; typedef int32arr int32arr2; struct ArrStruct { int32arr2 intArray; }; `; const types = parse(msgDef); expect(types).toEqual([ { name: "ArrStruct", aggregatedKind: "struct", definitions: [ { arrayUpperBound: 10, isArray: true, isComplex: false, name: "intArray", type: "int32", }, ], }, ]); }); it("can parse union that uses enums", () => { const msgDef = ` enum ColorMode { GRAY, RGBA, RGB }; module Limited { union Color switch (ColorMode) { case ColorMode::GRAY: uint8 gray; case ColorMode::RGBA: uint8 rgba[4]; default: uint8 rgb[3]; }; }; struct ColorSettings { Limited::Color chosenColor; }; `; const ast = parse(msgDef); expect(ast).toEqual([ { name: "ColorMode", aggregatedKind: "module", definitions: [ { name: "GRAY", value: 0, type: "uint32", isConstant: true, isComplex: false }, { name: "RGBA", value: 1, type: "uint32", isConstant: true, isComplex: false }, { name: "RGB", value: 2, type: "uint32", isConstant: true, isComplex: false }, ], }, { name: "Limited::Color", aggregatedKind: "union", switchType: "uint32", cases: [ { predicates: [0], type: { isComplex: false, name: "gray", type: "uint8", }, }, { predicates: [1], type: { arrayLengths: [4], isArray: true, isComplex: false, name: "rgba", type: "uint8", }, }, ], defaultCase: { arrayLengths: [3], isArray: true, isComplex: false, name: "rgb", type: "uint8", }, }, { name: "ColorSettings", aggregatedKind: "struct", definitions: [ { name: "chosenColor", type: "Limited::Color", isComplex: true, }, ], }, ]); }); it("can parse union that uses unscoped-local enum values from the switch enum", () => { const msgDef = ` enum ColorMode { GRAY, RGBA, RGB }; union Color switch (ColorMode) { case GRAY: uint8 gray; case RGBA: uint8 rgba[4]; default: uint8 rgb[3]; }; struct ColorSettings { Color chosenColor; }; `; const ast = parse(msgDef); expect(ast).toEqual([ { name: "ColorMode", aggregatedKind: "module", definitions: [ { name: "GRAY", value: 0, type: "uint32", isConstant: true, isComplex: false }, { name: "RGBA", value: 1, type: "uint32", isConstant: true, isComplex: false }, { name: "RGB", value: 2, type: "uint32", isConstant: true, isComplex: false }, ], }, { name: "Color", aggregatedKind: "union", switchType: "uint32", cases: [ { predicates: [0], type: { isComplex: false, name: "gray", type: "uint8", }, }, { predicates: [1], type: { arrayLengths: [4], isArray: true, isComplex: false, name: "rgba", type: "uint8", }, }, ], defaultCase: { arrayLengths: [3],