From 77f966f89fcf76fa67485adf941ac6e90d28029c Mon Sep 17 00:00:00 2001 From: Wouter van Oortmerssen Date: Tue, 18 Aug 2020 14:00:02 -0700 Subject: [PATCH] [Lobster] optional scalars support --- lobster/flatbuffers.lobster | 17 +++ src/idl_gen_lobster.cpp | 7 +- src/idl_parser.cpp | 3 +- tests/generate_code.bat | 2 +- tests/generate_code.sh | 2 +- tests/lobstertest.lobster | 28 +++- tests/optional_scalars_generated.lobster | 184 +++++++++++++++++++++++ 7 files changed, 237 insertions(+), 6 deletions(-) create mode 100644 tests/optional_scalars_generated.lobster diff --git a/lobster/flatbuffers.lobster b/lobster/flatbuffers.lobster index 0f1c15dd2..8b04c87b3 100644 --- a/lobster/flatbuffers.lobster +++ b/lobster/flatbuffers.lobster @@ -257,6 +257,10 @@ class builder: f(x) Slot(o) + def PrependSlot(o:int, x, f): + f(x) + Slot(o) + def PrependBoolSlot(o, x, d): PrependSlot(o, x, d): PrependBool(_) def PrependByteSlot(o, x, d): PrependSlot(o, x, d): PrependByte(_) def PrependUint8Slot(o, x, d): PrependSlot(o, x, d): PrependUint8(_) @@ -270,6 +274,19 @@ class builder: def PrependFloat32Slot(o, x, d): PrependSlot(o, x, d): PrependFloat32(_) def PrependFloat64Slot(o, x, d): PrependSlot(o, x, d): PrependFloat64(_) + def PrependBoolSlot(o, x): PrependSlot(o, x): PrependBool(_) + def PrependByteSlot(o, x): PrependSlot(o, x): PrependByte(_) + def PrependUint8Slot(o, x): PrependSlot(o, x): PrependUint8(_) + def PrependUint16Slot(o, x): PrependSlot(o, x): PrependUint16(_) + def PrependUint32Slot(o, x): PrependSlot(o, x): PrependUint32(_) + def PrependUint64Slot(o, x): PrependSlot(o, x): PrependUint64(_) + def PrependInt8Slot(o, x): PrependSlot(o, x): PrependInt8(_) + def PrependInt16Slot(o, x): PrependSlot(o, x): PrependInt16(_) + def PrependInt32Slot(o, x): PrependSlot(o, x): PrependInt32(_) + def PrependInt64Slot(o, x): PrependSlot(o, x): PrependInt64(_) + def PrependFloat32Slot(o, x): PrependSlot(o, x): PrependFloat32(_) + def PrependFloat64Slot(o, x): PrependSlot(o, x): PrependFloat64(_) + def PrependUOffsetTRelativeSlot(o:int, x:offset): if x.o: PrependUOffsetTRelative(x) diff --git a/src/idl_gen_lobster.cpp b/src/idl_gen_lobster.cpp index 0c600a74f..09cdfffa9 100644 --- a/src/idl_gen_lobster.cpp +++ b/src/idl_gen_lobster.cpp @@ -110,11 +110,14 @@ class LobsterGenerator : public BaseGenerator { offsets + ")"; } else { + auto defval = field.nullable ? "0" : field.value.constant; acc = "buf_.flatbuffers_field_" + GenTypeName(field.value.type) + - "(pos_, " + offsets + ", " + field.value.constant + ")"; + "(pos_, " + offsets + ", " + defval + ")"; } if (field.value.type.enum_def) acc = NormalizedName(*field.value.type.enum_def) + "(" + acc + ")"; + if (field.nullable) + acc += ", buf_.flatbuffers_field_present(pos_, " + offsets + ")"; code += def + "():\n return " + acc + "\n"; return; } @@ -198,7 +201,7 @@ class LobsterGenerator : public BaseGenerator { NormalizedName(field) + ":" + LobsterType(field.value.type) + "):\n b_.Prepend" + GenMethod(field.value.type) + "Slot(" + NumToString(offset) + ", " + NormalizedName(field); - if (IsScalar(field.value.type.base_type)) + if (IsScalar(field.value.type.base_type) && !field.nullable) code += ", " + field.value.constant; code += ")\n return this\n"; } diff --git a/src/idl_parser.cpp b/src/idl_parser.cpp index 1c9303c98..633ce6caa 100644 --- a/src/idl_parser.cpp +++ b/src/idl_parser.cpp @@ -2258,7 +2258,8 @@ CheckedError Parser::CheckClash(std::vector &fields, bool Parser::SupportsNullableScalars() const { - return !(opts.lang_to_generate & ~(IDLOptions::kRust | IDLOptions::kSwift)); + return !(opts.lang_to_generate & + ~(IDLOptions::kRust | IDLOptions::kSwift | IDLOptions::kLobster)); } bool Parser::SupportsAdvancedUnionFeatures() const { diff --git a/tests/generate_code.bat b/tests/generate_code.bat index 05c58ba4c..20b98d056 100644 --- a/tests/generate_code.bat +++ b/tests/generate_code.bat @@ -54,7 +54,7 @@ set TEST_NOINCL_FLAGS=%TEST_BASE_FLAGS% --no-includes --no-fb-import ..\%buildtype%\flatc.exe --cpp %TEST_BASE_FLAGS% --cpp-ptr-type flatbuffers::unique_ptr native_type_test.fbs || goto FAIL @rem Generate the optional scalar code for tests. -..\%buildtype%\flatc.exe --rust optional_scalars.fbs || goto FAIL +..\%buildtype%\flatc.exe --rust --lobster optional_scalars.fbs || goto FAIL @rem Generate the schema evolution tests ..\%buildtype%\flatc.exe --cpp --scoped-enums %TEST_CPP_FLAGS% -o evolution_test ./evolution_test/evolution_v1.fbs ./evolution_test/evolution_v2.fbs || goto FAIL diff --git a/tests/generate_code.sh b/tests/generate_code.sh index 3e66cb0e7..32abfd2f0 100755 --- a/tests/generate_code.sh +++ b/tests/generate_code.sh @@ -52,7 +52,7 @@ $TEST_NOINCL_FLAGS $TEST_CPP_FLAGS $TEST_CS_FLAGS -o namespace_test namespace_te ../flatc --dart monster_extra.fbs # Generate optional scalar code for tests. -../flatc --rust optional_scalars.fbs +../flatc --rust --lobster optional_scalars.fbs # Generate the schema evolution tests ../flatc --cpp --scoped-enums $TEST_CPP_FLAGS -o evolution_test ./evolution_test/evolution_v*.fbs diff --git a/tests/lobstertest.lobster b/tests/lobstertest.lobster index 7ea9b3889..ac42c2e83 100644 --- a/tests/lobstertest.lobster +++ b/tests/lobstertest.lobster @@ -14,6 +14,7 @@ import from "../lobster/" import monster_test_generated +import optional_scalars_generated def check_read_buffer(buf): // CheckReadBuffer checks that the given buffer is evaluated correctly as the example Monster. @@ -108,6 +109,28 @@ def make_monster_from_generated_code(): return b.SizedCopy() +def test_optional_scalars(): + def build(add_fields): + let b = flatbuffers_builder {} + let ss = optional_scalars_ScalarStuffBuilder { b }.start() + if add_fields: + ss.add_just_i8(1) + ss.add_maybe_i8(1) + ss.add_default_i8(1) + b.Finish(ss.end()) + return optional_scalars_GetRootAsScalarStuff(b.SizedCopy()) + + var root = build(true) + assert root.just_i8() == 1 and root.default_i8() == 1 + var maybe_val, maybe_present = root.maybe_i8() + assert maybe_val == 1 and maybe_present == true + + root = build(false) + assert root.just_i8() == 0 and root.default_i8() == 42 + maybe_val, maybe_present = root.maybe_i8() + assert maybe_val == 0 and maybe_present == false + + // Verify that the canonical flatbuffer file (produced by the C++ implementation) // is readable by the generated Lobster code. let fb2 = read_file("monsterdata_test.mon") @@ -134,4 +157,7 @@ assert not err2 // Check the resulting binary again (full roundtrip test): check_read_buffer(fb3) -print "Lobster test succesful!" \ No newline at end of file +// Additional tests. +test_optional_scalars() + +print "Lobster test succesful!" diff --git a/tests/optional_scalars_generated.lobster b/tests/optional_scalars_generated.lobster new file mode 100644 index 000000000..0c4b928fd --- /dev/null +++ b/tests/optional_scalars_generated.lobster @@ -0,0 +1,184 @@ +// automatically generated by the FlatBuffers compiler, do not modify +import flatbuffers + +namespace optional_scalars + +class ScalarStuff + +class ScalarStuff : flatbuffers_handle + def just_i8(): + return buf_.flatbuffers_field_int8(pos_, 4, 0) + def maybe_i8(): + return buf_.flatbuffers_field_int8(pos_, 6, 0), buf_.flatbuffers_field_present(pos_, 6) + def default_i8(): + return buf_.flatbuffers_field_int8(pos_, 8, 42) + def just_u8(): + return buf_.flatbuffers_field_int8(pos_, 10, 0) + def maybe_u8(): + return buf_.flatbuffers_field_int8(pos_, 12, 0), buf_.flatbuffers_field_present(pos_, 12) + def default_u8(): + return buf_.flatbuffers_field_int8(pos_, 14, 42) + def just_i16(): + return buf_.flatbuffers_field_int16(pos_, 16, 0) + def maybe_i16(): + return buf_.flatbuffers_field_int16(pos_, 18, 0), buf_.flatbuffers_field_present(pos_, 18) + def default_i16(): + return buf_.flatbuffers_field_int16(pos_, 20, 42) + def just_u16(): + return buf_.flatbuffers_field_int16(pos_, 22, 0) + def maybe_u16(): + return buf_.flatbuffers_field_int16(pos_, 24, 0), buf_.flatbuffers_field_present(pos_, 24) + def default_u16(): + return buf_.flatbuffers_field_int16(pos_, 26, 42) + def just_i32(): + return buf_.flatbuffers_field_int32(pos_, 28, 0) + def maybe_i32(): + return buf_.flatbuffers_field_int32(pos_, 30, 0), buf_.flatbuffers_field_present(pos_, 30) + def default_i32(): + return buf_.flatbuffers_field_int32(pos_, 32, 42) + def just_u32(): + return buf_.flatbuffers_field_int32(pos_, 34, 0) + def maybe_u32(): + return buf_.flatbuffers_field_int32(pos_, 36, 0), buf_.flatbuffers_field_present(pos_, 36) + def default_u32(): + return buf_.flatbuffers_field_int32(pos_, 38, 42) + def just_i64(): + return buf_.flatbuffers_field_int64(pos_, 40, 0) + def maybe_i64(): + return buf_.flatbuffers_field_int64(pos_, 42, 0), buf_.flatbuffers_field_present(pos_, 42) + def default_i64(): + return buf_.flatbuffers_field_int64(pos_, 44, 42) + def just_u64(): + return buf_.flatbuffers_field_int64(pos_, 46, 0) + def maybe_u64(): + return buf_.flatbuffers_field_int64(pos_, 48, 0), buf_.flatbuffers_field_present(pos_, 48) + def default_u64(): + return buf_.flatbuffers_field_int64(pos_, 50, 42) + def just_f32(): + return buf_.flatbuffers_field_float32(pos_, 52, 0.0) + def maybe_f32(): + return buf_.flatbuffers_field_float32(pos_, 54, 0), buf_.flatbuffers_field_present(pos_, 54) + def default_f32(): + return buf_.flatbuffers_field_float32(pos_, 56, 42.0) + def just_f64(): + return buf_.flatbuffers_field_float64(pos_, 58, 0.0) + def maybe_f64(): + return buf_.flatbuffers_field_float64(pos_, 60, 0), buf_.flatbuffers_field_present(pos_, 60) + def default_f64(): + return buf_.flatbuffers_field_float64(pos_, 62, 42.0) + def just_bool(): + return buf_.flatbuffers_field_int8(pos_, 64, 0) + def maybe_bool(): + return buf_.flatbuffers_field_int8(pos_, 66, 0), buf_.flatbuffers_field_present(pos_, 66) + def default_bool(): + return buf_.flatbuffers_field_int8(pos_, 68, 1) + +def GetRootAsScalarStuff(buf:string): return ScalarStuff { buf, buf.flatbuffers_indirect(0) } + +struct ScalarStuffBuilder: + b_:flatbuffers_builder + def start(): + b_.StartObject(33) + return this + def add_just_i8(just_i8:int): + b_.PrependInt8Slot(0, just_i8, 0) + return this + def add_maybe_i8(maybe_i8:int): + b_.PrependInt8Slot(1, maybe_i8) + return this + def add_default_i8(default_i8:int): + b_.PrependInt8Slot(2, default_i8, 42) + return this + def add_just_u8(just_u8:int): + b_.PrependUint8Slot(3, just_u8, 0) + return this + def add_maybe_u8(maybe_u8:int): + b_.PrependUint8Slot(4, maybe_u8) + return this + def add_default_u8(default_u8:int): + b_.PrependUint8Slot(5, default_u8, 42) + return this + def add_just_i16(just_i16:int): + b_.PrependInt16Slot(6, just_i16, 0) + return this + def add_maybe_i16(maybe_i16:int): + b_.PrependInt16Slot(7, maybe_i16) + return this + def add_default_i16(default_i16:int): + b_.PrependInt16Slot(8, default_i16, 42) + return this + def add_just_u16(just_u16:int): + b_.PrependUint16Slot(9, just_u16, 0) + return this + def add_maybe_u16(maybe_u16:int): + b_.PrependUint16Slot(10, maybe_u16) + return this + def add_default_u16(default_u16:int): + b_.PrependUint16Slot(11, default_u16, 42) + return this + def add_just_i32(just_i32:int): + b_.PrependInt32Slot(12, just_i32, 0) + return this + def add_maybe_i32(maybe_i32:int): + b_.PrependInt32Slot(13, maybe_i32) + return this + def add_default_i32(default_i32:int): + b_.PrependInt32Slot(14, default_i32, 42) + return this + def add_just_u32(just_u32:int): + b_.PrependUint32Slot(15, just_u32, 0) + return this + def add_maybe_u32(maybe_u32:int): + b_.PrependUint32Slot(16, maybe_u32) + return this + def add_default_u32(default_u32:int): + b_.PrependUint32Slot(17, default_u32, 42) + return this + def add_just_i64(just_i64:int): + b_.PrependInt64Slot(18, just_i64, 0) + return this + def add_maybe_i64(maybe_i64:int): + b_.PrependInt64Slot(19, maybe_i64) + return this + def add_default_i64(default_i64:int): + b_.PrependInt64Slot(20, default_i64, 42) + return this + def add_just_u64(just_u64:int): + b_.PrependUint64Slot(21, just_u64, 0) + return this + def add_maybe_u64(maybe_u64:int): + b_.PrependUint64Slot(22, maybe_u64) + return this + def add_default_u64(default_u64:int): + b_.PrependUint64Slot(23, default_u64, 42) + return this + def add_just_f32(just_f32:float): + b_.PrependFloat32Slot(24, just_f32, 0.0) + return this + def add_maybe_f32(maybe_f32:float): + b_.PrependFloat32Slot(25, maybe_f32) + return this + def add_default_f32(default_f32:float): + b_.PrependFloat32Slot(26, default_f32, 42.0) + return this + def add_just_f64(just_f64:float): + b_.PrependFloat64Slot(27, just_f64, 0.0) + return this + def add_maybe_f64(maybe_f64:float): + b_.PrependFloat64Slot(28, maybe_f64) + return this + def add_default_f64(default_f64:float): + b_.PrependFloat64Slot(29, default_f64, 42.0) + return this + def add_just_bool(just_bool:int): + b_.PrependBoolSlot(30, just_bool, 0) + return this + def add_maybe_bool(maybe_bool:int): + b_.PrependBoolSlot(31, maybe_bool) + return this + def add_default_bool(default_bool:int): + b_.PrependBoolSlot(32, default_bool, 1) + return this + def end(): + return b_.EndObject() +