diff --git a/.github/labeler.yml b/.github/labeler.yml index 5de0c3a7aa7..b25608e95ec 100644 --- a/.github/labeler.yml +++ b/.github/labeler.yml @@ -20,6 +20,11 @@ swift: - tests/FlatBuffers.Test.Swift/**/* - src/idl_gen_swift.cpp +nim: + - '**/*.nim' + - nim/**/* + - src/idl_gen_nim.cpp + javascript: - '**/*.js' - src/idl_gen_ts.cpp @@ -63,7 +68,7 @@ rust: - '**/*.rs' - rust/**/* - src/idl_gen_rust.cpp - + dart: - '**/*.dart' - src/idl_gen_dart.cpp @@ -92,4 +97,4 @@ CI: grpc: - grpc/**/* - - src/idl_gen_grpc.cpp \ No newline at end of file + - src/idl_gen_grpc.cpp diff --git a/.gitignore b/.gitignore index 7e4268951f6..41e3ef2628e 100644 --- a/.gitignore +++ b/.gitignore @@ -148,4 +148,7 @@ flatbuffers.pc **/html/** **/latex/** # https://cmake.org/cmake/help/latest/module/FetchContent.html#variable:FETCHCONTENT_BASE_DIR -_deps/ \ No newline at end of file +_deps/ +nimcache/ +testresults/ +testament.db diff --git a/CMakeLists.txt b/CMakeLists.txt index 7e7ab8762b0..b4f9ecf3179 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -71,7 +71,7 @@ option(FLATBUFFERS_ENABLE_PCH Only work if CMake supports 'target_precompile_headers'. \" This can speed up compilation time." OFF) -option(FLATBUFFERS_SKIP_MONSTER_EXTRA +option(FLATBUFFERS_SKIP_MONSTER_EXTRA "Skip generating monster_extra.fbs that contains non-supported numerical\" types." OFF) @@ -156,6 +156,7 @@ set(FlatBuffers_Compiler_SRCS src/idl_gen_grpc.cpp src/idl_gen_json_schema.cpp src/idl_gen_swift.cpp + src/idl_gen_nim.cpp src/idl_namer.h src/namer.h src/flatc.cpp @@ -320,8 +321,8 @@ include_directories(grpc) # Creates an interface library that stores the configuration settings that each # target links too. This is a compromise between setting configuration globally -# with add_compile_options() and the more targetted target_compile_options(). -# This way each target in this file can share settings and override them if +# with add_compile_options() and the more targetted target_compile_options(). +# This way each target in this file can share settings and override them if # needed. add_library(ProjectConfig INTERFACE) target_compile_features(ProjectConfig @@ -335,9 +336,9 @@ set(CMAKE_CXX_STANDARD_REQUIRED ON) # We shouldn't rely on any compiler-extensions to make things work. set(CMAKE_CXX_EXTENSIONS OFF) -if(MSVC) +if(MSVC) target_compile_options(ProjectConfig - INTERFACE + INTERFACE /W4 /WX /wd4512 # C4512: assignment operator could not be generated @@ -352,9 +353,9 @@ else() INTERFACE -Wall -Werror - -pedantic - -Werror - -Wextra + -pedantic + -Werror + -Wextra -Wno-unused-parameter -Wold-style-cast -Wimplicit-fallthrough @@ -371,13 +372,13 @@ else() $<$: $<$,4.4>: - -Wunused-result - -Werror=unused-result - -Wunused-parameter + -Wunused-result + -Werror=unused-result + -Wunused-parameter -Werror=unused-parameter > $<$,7.0>: - -faligned-new + -faligned-new -Werror=implicit-fallthrough=2 > $<$,8.0>: @@ -405,7 +406,7 @@ if(FLATBUFFERS_BUILD_FLATLIB) add_library(flatbuffers STATIC ${FlatBuffers_Library_SRCS}) # Attach header directory for when build via add_subdirectory(). - target_include_directories(flatbuffers + target_include_directories(flatbuffers INTERFACE $ ) @@ -423,7 +424,7 @@ if(FLATBUFFERS_BUILD_FLATC) endif() target_link_libraries(flatc PRIVATE $) - target_compile_options(flatc + target_compile_options(flatc PUBLIC $<$,$>: /MT @@ -623,13 +624,13 @@ if(FLATBUFFERS_BUILD_GRPCTEST) find_package(gRPC CONFIG REQUIRED) add_executable(grpctest ${FlatBuffers_GRPCTest_SRCS}) add_dependencies(grpctest generated_code) - target_link_libraries(grpctext - PRIVATE + target_link_libraries(grpctext + PRIVATE $ - gRPC::grpc++_unsecure - gRPC::gpr + gRPC::grpc++_unsecure + gRPC::gpr pthread - dl + dl ) endif() @@ -747,4 +748,4 @@ add_library(FlatBuffers::FlatBuffers ALIAS FlatBuffers) target_include_directories( FlatBuffers INTERFACE $ - $) \ No newline at end of file + $) diff --git a/include/flatbuffers/idl.h b/include/flatbuffers/idl.h index 1803d68bb22..a06085033ce 100644 --- a/include/flatbuffers/idl.h +++ b/include/flatbuffers/idl.h @@ -44,26 +44,26 @@ namespace flatbuffers { // of type tokens. // clang-format off #define FLATBUFFERS_GEN_TYPES_SCALAR(TD) \ - TD(NONE, "", uint8_t, byte, byte, byte, uint8, u8, UByte, UInt8) \ - TD(UTYPE, "", uint8_t, byte, byte, byte, uint8, u8, UByte, UInt8) /* begin scalar/int */ \ - TD(BOOL, "bool", uint8_t, boolean,bool, bool, bool, bool, Boolean, Bool) \ - TD(CHAR, "byte", int8_t, byte, int8, sbyte, int8, i8, Byte, Int8) \ - TD(UCHAR, "ubyte", uint8_t, byte, byte, byte, uint8, u8, UByte, UInt8) \ - TD(SHORT, "short", int16_t, short, int16, short, int16, i16, Short, Int16) \ - TD(USHORT, "ushort", uint16_t, short, uint16, ushort, uint16, u16, UShort, UInt16) \ - TD(INT, "int", int32_t, int, int32, int, int32, i32, Int, Int32) \ - TD(UINT, "uint", uint32_t, int, uint32, uint, uint32, u32, UInt, UInt32) \ - TD(LONG, "long", int64_t, long, int64, long, int64, i64, Long, Int64) \ - TD(ULONG, "ulong", uint64_t, long, uint64, ulong, uint64, u64, ULong, UInt64) /* end int */ \ - TD(FLOAT, "float", float, float, float32, float, float32, f32, Float, Float32) /* begin float */ \ - TD(DOUBLE, "double", double, double, float64, double, float64, f64, Double, Double) /* end float/scalar */ + TD(NONE, "", uint8_t, byte, byte, byte, uint8, u8, UByte, UInt8, uint8) \ + TD(UTYPE, "", uint8_t, byte, byte, byte, uint8, u8, UByte, UInt8, uint8) /* begin scalar/int */ \ + TD(BOOL, "bool", uint8_t, boolean,bool, bool, bool, bool, Boolean, Bool, bool) \ + TD(CHAR, "byte", int8_t, byte, int8, sbyte, int8, i8, Byte, Int8, int8) \ + TD(UCHAR, "ubyte", uint8_t, byte, byte, byte, uint8, u8, UByte, UInt8, uint8) \ + TD(SHORT, "short", int16_t, short, int16, short, int16, i16, Short, Int16, int16) \ + TD(USHORT, "ushort", uint16_t, short, uint16, ushort, uint16, u16, UShort, UInt16, uint16) \ + TD(INT, "int", int32_t, int, int32, int, int32, i32, Int, Int32, int32) \ + TD(UINT, "uint", uint32_t, int, uint32, uint, uint32, u32, UInt, UInt32, uint32) \ + TD(LONG, "long", int64_t, long, int64, long, int64, i64, Long, Int64, int64) \ + TD(ULONG, "ulong", uint64_t, long, uint64, ulong, uint64, u64, ULong, UInt64, uint64) /* end int */ \ + TD(FLOAT, "float", float, float, float32, float, float32, f32, Float, Float32, float32) /* begin float */ \ + TD(DOUBLE, "double", double, double, float64, double, float64, f64, Double, Double, float64) /* end float/scalar */ #define FLATBUFFERS_GEN_TYPES_POINTER(TD) \ - TD(STRING, "string", Offset, int, int, StringOffset, int, unused, Int, Offset) \ - TD(VECTOR, "", Offset, int, int, VectorOffset, int, unused, Int, Offset) \ - TD(STRUCT, "", Offset, int, int, int, int, unused, Int, Offset) \ - TD(UNION, "", Offset, int, int, int, int, unused, Int, Offset) + TD(STRING, "string", Offset, int, int, StringOffset, int, unused, Int, Offset, uoffset) \ + TD(VECTOR, "", Offset, int, int, VectorOffset, int, unused, Int, Offset, uoffset) \ + TD(STRUCT, "", Offset, int, int, int, int, unused, Int, Offset, uoffset) \ + TD(UNION, "", Offset, int, int, int, int, unused, Int, Offset, uoffset) #define FLATBUFFERS_GEN_TYPE_ARRAY(TD) \ - TD(ARRAY, "", int, int, int, int, int, unused, Int, Offset) + TD(ARRAY, "", int, int, int, int, int, unused, Int, Offset, uoffset) // The fields are: // - enum // - FlatBuffers schema type. @@ -74,6 +74,7 @@ namespace flatbuffers { // - Python type. // - Kotlin type. // - Rust type. +// - Nim type. // using these macros, we can now write code dealing with types just once, e.g. @@ -623,6 +624,7 @@ struct IDLOptions { kRust = 1 << 14, kKotlin = 1 << 15, kSwift = 1 << 16, + kNim = 1 << 17, kMAX }; @@ -1163,6 +1165,10 @@ extern bool GenerateKotlin(const Parser &parser, const std::string &path, extern bool GenerateSwift(const Parser &parser, const std::string &path, const std::string &file_name); +// Generate Nim classes. +// See idl_gen_nim.cpp +extern bool GenerateNim(const Parser &parser, const std::string &path, + const std::string &file_name); // Generate a schema file from the internal representation, useful after // parsing a .proto schema. extern std::string GenerateFBS(const Parser &parser, diff --git a/nim/flatbuffers.nimble b/nim/flatbuffers.nimble new file mode 100644 index 00000000000..cc30478117b --- /dev/null +++ b/nim/flatbuffers.nimble @@ -0,0 +1,7 @@ +version = "1.0.0" +author = "flatbuffers" +description = "Flatbuffers" +license = "Apache 2.0" +srcDir = "flatbuffers" + +requires "nim >= 1.4.0" diff --git a/nim/flatbuffers/flatbuffers.nim b/nim/flatbuffers/flatbuffers.nim new file mode 100644 index 00000000000..b9532877e76 --- /dev/null +++ b/nim/flatbuffers/flatbuffers.nim @@ -0,0 +1,7 @@ +import + src/[ + builder, + struct, + table + ] +export flatbuffers.builder, flatbuffers.table, flatbuffers.struct diff --git a/nim/flatbuffers/src/builder.nim b/nim/flatbuffers/src/builder.nim new file mode 100644 index 00000000000..cceeba6977b --- /dev/null +++ b/nim/flatbuffers/src/builder.nim @@ -0,0 +1,263 @@ +import math +import table + + +const MAX_BUFFER_SIZE* = 2^31 + + +type Builder* = ref object of RootObj + bytes*: seq[byte] + minalign*: int + current_vtable*: seq[uoffset] + objectEnd*: uoffset + vtables*: seq[uoffset] #? + head*: uoffset + nested*: bool + finished*: bool + vectorNumElems*: uoffset + +using this: var Builder + +func newBuilder*(size: int): Builder = + result = new Builder + result.bytes.setLen(size) + result.minalign = 1 + result.head = size.uoffset + result.nested = false + result.finished = false + result.vectorNumElems = 0 + +proc FinishedBytes*(this): seq[byte] = + if not this.finished: + quit("Builder not finished, Incorrect use of FinishedBytes(): must call 'Finish' first.") + result = this.bytes[this.head..^1] + +proc Output*(this): seq[byte] = + if not this.finished: + quit("Builder not finished, Incorrect use of Output(): must call 'Finish' first.") + + result = this.bytes[this.head..^1] + +func Offset*(this): uoffset = + result = this.bytes.len.uoffset - this.head + +proc StartObject*(this; numfields: int) = + if this.nested: + quit("builder is nested") + + this.current_vtable.setLen(numfields) + for i in this.current_vtable.mitems(): + i = 0 + this.objectEnd = this.Offset() + this.nested = true + +proc GrowByteBuffer*(this) = + if this.bytes.len == MAX_BUFFER_SIZE: + quit("flatbuffers: cannot grow buffer beyond 2 gigabytes") + let oldLen = this.bytes.len + var newLen = min(this.bytes.len * 2, MAX_BUFFER_SIZE) + if newLen == 0: + newLen = 1 + this.bytes.setLen(newLen) + var j = this.bytes.len - 1 + while j >= 0: + if j >= newLen - oldLen: + this.bytes[j] = this.bytes[j - (newLen - oldLen)] + else: + this.bytes[j] = 0 + dec(j) + +proc Place*[T](this; x: T) = + this.head -= uoffset x.sizeof + WriteVal(this.bytes, this.head, x) + +func Pad*(this; n: int) = + for i in 0.. this.minalign: + this.minalign = size + var alignsize = (not (this.bytes.len - this.head.int + additionalBytes)) + 1 + alignsize = alignsize and (size - 1) + + while this.head.int < alignsize + size + additionalBytes: + let oldbufSize = this.bytes.len + this.GrowByteBuffer() + this.head = (this.head.int + this.bytes.len - oldbufSize).uoffset + this.Pad(alignsize) + +proc PrependOffsetRelative*[T: Offsets](this; off: T) = + when T is voffset: + this.Prep(T.sizeof, 0) + if not off.uoffset <= this.Offset: + quit("flatbuffers: Offset arithmetic error.") + this.Place(off) + else: + this.Prep(T.sizeof, 0) + if not off.uoffset <= this.Offset: + quit("flatbuffers: Offset arithmetic error.") + let off2: T = this.Offset.T - off + sizeof(T).T + this.Place(off2) + + +proc Prepend*[T](this; x: T) = + this.Prep(x.sizeof, 0) + this.Place(x) + +proc Slot*(this; slotnum: int) = + this.current_vtable[slotnum] = this.Offset + +proc PrependSlot*[T](this; o: int; x, d: T) = + if x != d: + when T is uoffset or T is soffset or T is voffset: + this.PrependOffsetRelative(x) + else: + this.Prepend(x) + this.Slot(o) + +proc AssertStuctInline(this; obj: uoffset) = + if obj != this.Offset: + quit("flatbuffers: Tried to write a Struct at an Offset that is different from the current Offset of the Builder.") + +proc PrependStructSlot*(this; o: int; x: uoffset; d: uoffset) = + if x != d: + this.AssertStuctInline(x) + this.Slot(o) + +proc Add*[T](this; n: T) = + this.Prep(T.sizeof, 0) + WriteVal(this.bytes, this.head, n) + +proc VtableEqual*(a: seq[uoffset]; objectStart: uoffset; b: seq[byte]): bool = + if a.len * voffset.sizeof != b.len: + return false + + var i = 0 + while i < a.len: + var seq = b[i * voffset.sizeof..<(i + 1) * voffset.sizeof] + let x = GetVal[voffset](addr seq) + + if x == 0 and a[i] == 0: + inc i + continue + + let y = objectStart.soffset - a[i].soffset + if x.soffset != y: + return false + inc i + return true + +proc WriteVtable*(this): uoffset = + this.PrependOffsetRelative(0.soffset) + + let objectOffset = this.Offset + var existingVtable = uoffset 0 + + var i = this.current_vtable.len - 1 + while i >= 0 and this.current_vtable[i] == 0: dec i + + this.current_vtable = this.current_vtable[0..i] + for i in countdown(this.vtables.len - 1, 0): + let + vt2Offset: uoffset = this.vtables[i] + vt2Start: int = this.bytes.len - int vt2Offset + + var seq = this.bytes[vt2Start.. +#include +#include + +#include "flatbuffers/code_generators.h" +#include "flatbuffers/flatbuffers.h" +#include "flatbuffers/idl.h" +#include "flatbuffers/util.h" +#include "idl_namer.h" + +namespace flatbuffers { +namespace nim { + +std::set NimKeywords() { + return { + "addr", "and", "as", "asm", "bind", "block", + "break", "case", "cast", "concept", "const", "continue", + "converter", "defer", "discard", "distinct", "div", "do", + "elif", "else", "end", "enum", "except", "export", + "finally", "for", "from", "func", "if", "import", + "in", "include", "interface", "is", "isnot", "iterator", + "let", "macro", "method", "mixin", "mod", "nil", + "not", "notin", "object", "of", "or", "out", + "proc", "ptr", "raise", "ref", "return", "shl", + "shr", "static", "template", "try", "tuple", "type", + "using", "var", "when", "while", "xor", "yield", + }; +} + +Namer::Config NimDefaultConfig() { + return { /*types=*/Case::kKeep, + /*constants=*/Case::kScreamingSnake, + /*methods=*/Case::kUpperCamel, + /*functions=*/Case::kUpperCamel, + /*fields=*/Case::kLowerCamel, + /*variable=*/Case::kLowerCamel, + /*variants=*/Case::kKeep, + /*enum_variant_seperator=*/".", + /*escape_keywords=*/Namer::Config::Escape::AfterConvertingCase, + /*namespaces=*/Case::kKeep, + /*namespace_seperator=*/"/", + /*object_prefix=*/"", + /*object_suffix=*/"T", + /*keyword_prefix=*/"", + /*keyword_suffix=*/"_", + /*filenames=*/Case::kKeep, + /*directories=*/Case::kKeep, + /*output_path=*/"", + /*filename_suffix=*/"", + /*filename_extension=*/".nim" }; +} +// Hardcode spaces per indentation. +const CommentConfig def_comment = { nullptr, "#", nullptr }; +const std::string Indent = " "; +const std::string Comment = "# "; +const std::string End = "\n"; +const std::string EndFunc = "\n"; +const std::string Export = "*"; +const std::string SelfData = "self.tab"; +const std::string SelfDataPos = "self.tab.Pos"; +const std::string SelfDataBytes = "self.tab.Bytes"; + +class NimGenerator : public BaseGenerator { + public: + NimGenerator(const Parser &parser, const std::string &path, + const std::string &file_name) + : BaseGenerator(parser, path, file_name, "" /* not used */, + "" /* not used */, "nim"), + namer_(WithFlagOptions(NimDefaultConfig(), parser.opts, path), + NimKeywords()) {} + + // Most field accessors need to retrieve and test the field offset first, + // this is the prefix code for that. + std::string OffsetPrefix(const FieldDef &field) { + return Indent + "let o = " + SelfData + ".Offset(" + + NumToString(field.value.offset) + ")\n" + Indent + "if o != 0:\n"; + } + + // Begin a class declaration. + void BeginClass(const StructDef &struct_def, std::string &code) { + code += + "type " + namer_.Type(struct_def) + Export + " = object of FlatObj\n"; + code += "\n"; + } + + // Begin enum code with a class declaration. + void BeginEnum(const std::string &class_name, std::string &code) { + code += "type " + class_name + Export + " {.pure.} = enum\n"; + } + + // Starts a new line and then indents. + std::string GenIndents(int num) const { + return "\n" + std::string(num * Indent.length(), ' '); + } + + // A single enum member. + void EnumMember(const EnumDef &enum_def, const EnumVal &ev, + std::string &code) { + code += Indent + namer_.Variant(ev) + " = " + enum_def.ToString(ev) + "." + + GenTypeBasic(enum_def.underlying_type) + ",\n"; + } + + // End enum code. + void EndEnum(std::string &code) { code += "\n"; } + + // Get the length of a vector. + void GetVectorLen(const StructDef &struct_def, const FieldDef &field, + std::string &code) { + code += "proc " + namer_.Field(field) + "Length" + Export + + "(self: " + namer_.Type(struct_def) + "): int =\n"; + code += OffsetPrefix(field); + code += Indent + Indent + "result = " + SelfData + ".Vectorlen(o)\n"; + code += EndFunc; + } + + void GetStructVector(const StructDef &struct_def, const FieldDef &field, + std::string &code) { + imports_.insert(GetImport(struct_def, field)); + code += "proc " + namer_.Field(field) + Export + + "(self: " + namer_.Type(struct_def) + "): seq[" + + ImportedName(struct_def, field) + "] =\n"; + code += Indent + "let len = self." + namer_.Field(field) + "Length\n"; + code += Indent + "for i in countup(0, len - 1):\n"; + code += + Indent + Indent + "result.add(self." + namer_.Field(field) + "(i))\n"; + code += EndFunc; + } + + void GetEnumVector(const StructDef &struct_def, const FieldDef &field, + std::string &code) { + imports_.insert(GetImport(struct_def, field)); + code += "proc " + namer_.Field(field) + Export + + "(self: " + namer_.Type(struct_def) + "): seq[" + + ImportedName(struct_def, field) + "] =\n"; + code += Indent + "let len = self." + namer_.Field(field) + "Length\n"; + code += Indent + "for i in countup(0, len - 1):\n"; + code += + Indent + Indent + "result.add(self." + namer_.Field(field) + "(i))\n"; + code += EndFunc; + } + + void GetMemberOfVectorOfEnum(const StructDef &struct_def, + const FieldDef &field, std::string &code) { + imports_.insert(GetImport(struct_def, field)); + code += "proc " + namer_.Field(field) + Export + + "(self: " + namer_.Type(struct_def) + + ", j: int): " + ImportedName(struct_def, field) + " =\n"; + code += OffsetPrefix(field); + code += Indent + Indent + "var x = " + SelfData + ".Vector(o)\n"; + code += Indent + Indent + "x += j.uoffset * " + + NumToString(InlineSize(field.value.type.VectorType())) + + ".uoffset\n"; + + code += Indent + Indent + "result = " + ImportedName(struct_def, field) + + "(" + GenGetter(field.value.type) + "x))\n"; + code += EndFunc; + } + + void GetNonStructVector(const StructDef &struct_def, const FieldDef &field, + std::string &code) { + code += "proc " + namer_.Field(field) + Export + + "(self: " + namer_.Type(struct_def) + "): seq[" + TypeName(field) + + "] =\n"; + code += Indent + "let len = self." + namer_.Field(field) + "Length\n"; + code += Indent + "for i in countup(0, len - 1):\n"; + code += + Indent + Indent + "result.add(self." + namer_.Field(field) + "(i))\n"; + code += EndFunc; + } + + // Get the value of a struct's scalar. + void GetScalarFieldOfStruct(const StructDef &struct_def, + const FieldDef &field, std::string &code) { + std::string field_type = GenTypeGet(field.value.type); + if (IsEnum(field.value.type)) { + imports_.insert(GetImport(struct_def, field)); + field_type = ImportedName(struct_def, field); + } + code += "proc " + namer_.Field(field) + Export + + "(self: " + namer_.Type(struct_def) + "): " + field_type + " =\n"; + code += Indent + "result = "; + if (IsEnum(field.value.type)) { code += field_type + "("; } + code += GenGetter(field.value.type) + SelfDataPos + " + " + + NumToString(field.value.offset) + ")"; + if (IsEnum(field.value.type)) { code += ")"; } + code += "\n"; + + if (parser_.opts.mutable_buffer) { + code += "proc `" + namer_.Field(field) + "=`" + Export + "(self: var " + + namer_.Type(struct_def) + ", n: " + GenTypeGet(field.value.type) + + "): bool =\n"; + code += Indent + "result = "; + code += SelfData + ".Mutate(" + SelfDataPos + " + " + + NumToString(field.value.offset) + ", n"; + if (IsEnum(field.value.type)) { + code += "." + GenTypeGet(field.value.type); + } + code += ")\n"; + } + code += EndFunc; + } + + // Get the value of a struct's struct. + void GetStructFieldOfStruct(const StructDef &struct_def, + const FieldDef &field, std::string &code) { + imports_.insert(GetImport(struct_def, field)); + code += "proc " + namer_.Field(field) + Export + + "(self: " + namer_.Type(struct_def) + + "): " + ImportedName(struct_def, field) + " =\n"; + code += Indent + "result.Init(" + SelfDataBytes + ", " + SelfDataPos + + " + " + NumToString(field.value.offset) + ")\n"; + + if (parser_.opts.mutable_buffer) { + code += "proc `" + namer_.Field(field) + "=`" + Export + "(self: var " + + namer_.Type(struct_def) + + ", n: " + ImportedName(struct_def, field) + ") =\n"; + code += Indent + "discard " + SelfData + ".Mutate(" + SelfDataPos + + " + " + NumToString(field.value.offset) + ", n)\n"; + } + code += EndFunc; + } + + // Get the value of a table's scalar. + void GetScalarFieldOfTable(const StructDef &struct_def, const FieldDef &field, + std::string &code) { + std::string field_type = GenTypeGet(field.value.type); + if (IsEnum(field.value.type)) { + imports_.insert(GetImport(struct_def, field)); + field_type = ImportedName(struct_def, field); + } + std::string returntype = field_type; + if (field.IsScalarOptional()) { + ImportOptions(); + returntype = "Option[" + field_type + "]"; + } + + code += "proc " + namer_.Field(field) + Export + + "(self: " + namer_.Type(struct_def) + "): " + returntype + " =\n"; + + code += OffsetPrefix(field); + code += Indent + Indent + "result = "; + if (field.IsScalarOptional()) { code += "some("; } + if (IsEnum(field.value.type)) { code += field_type + "("; } + code += GenGetter(field.value.type) + "o + " + SelfDataPos + ")"; + if (IsEnum(field.value.type)) { code += ")"; } + if (field.IsScalarOptional()) { code += ")"; } + code += "\n"; + if (!field.IsScalarOptional()) { + std::string default_value; + if (IsBool(field.value.type.base_type)) { + default_value = field.value.constant == "0" ? "false" : "true"; + } else { + default_value = field.value.constant; + } + code += Indent + "else:\n"; + code += Indent + Indent + "result = "; + if (IsEnum(field.value.type)) { code += field_type + "("; } + code += default_value; + if (IsEnum(field.value.type)) { code += ")"; } + code += "\n\n"; + } + + if (parser_.opts.mutable_buffer) { + code += "proc `" + namer_.Field(field) + "=`" + Export + "(self: var " + + namer_.Type(struct_def) + ", n: " + field_type + "): bool =\n"; + code += Indent + "result = " + SelfData + ".MutateSlot(" + + NumToString(field.value.offset) + ", n"; + if (IsEnum(field.value.type)) { + code += "." + GenTypeGet(field.value.type); + } + code += ")\n"; + } + code += EndFunc; + } + + // Get a struct by initializing an existing struct. + // Specific to Table. + void GetStructFieldOfTable(const StructDef &struct_def, const FieldDef &field, + std::string &code) { + ImportOptions(); + imports_.insert(GetImport(struct_def, field)); + code += "proc " + namer_.Field(field) + Export + + "(self: " + namer_.Type(struct_def) + "): Option[" + + ImportedName(struct_def, field) + "] =\n"; + code += OffsetPrefix(field); + // var x: Property.Property + // x.Init(self.tab.Bytes, o + self.tab.Pos) + // result = some(x) + code += + Indent + Indent + "var x: " + ImportedName(struct_def, field) + "\n"; + code += Indent + Indent + "x.Init(" + SelfDataBytes + ", o + " + + SelfDataPos + ")\n"; + code += Indent + Indent + "result = some(x)\n"; + code += EndFunc; + } + + // Get the value of a string. + void GetStringField(const StructDef &struct_def, const FieldDef &field, + std::string &code) { + ImportOptions(); + code += "proc " + namer_.Field(field) + Export + + "(self: " + namer_.Type(struct_def) + "): Option[" + + GenTypeGet(field.value.type) + "] =\n"; + code += OffsetPrefix(field); + code += Indent + Indent + "result = some(" + GenGetter(field.value.type) + + "o + " + SelfDataPos + "))\n"; + if (field.IsDefault()) { + code += Indent + "else:\n"; + code += + Indent + Indent + "result = some(\"" + field.value.constant + "\")\n"; + } + code += EndFunc; + } + + // Get the value of a union from an object. + void GetUnionField(const StructDef &struct_def, const FieldDef &field, + std::string &code) { + ImportOptions(); + code += "proc " + namer_.Field(field) + Export + + "(self: " + namer_.Type(struct_def) + "): Option[Vtable] =\n"; + code += OffsetPrefix(field); + code += Indent + Indent + "result = some(" + GenGetter(field.value.type) + + "o))\n"; + code += EndFunc; + } + + // Get the value of a vector's struct member. + void GetMemberOfVectorOfStruct(const StructDef &struct_def, + const FieldDef &field, std::string &code) { + imports_.insert(GetImport(struct_def, field)); + code += "proc " + namer_.Field(field) + Export + + "(self: " + namer_.Type(struct_def) + + ", j: int): " + ImportedName(struct_def, field) + " =\n"; + code += OffsetPrefix(field); + code += Indent + Indent + "var x = " + SelfData + ".Vector(o)\n"; + code += Indent + Indent + "x += j.uoffset * " + + NumToString(InlineSize(field.value.type.VectorType())) + + ".uoffset\n"; + + code += Indent + Indent + "result.Init(" + SelfDataBytes + ", x)\n"; + code += EndFunc; + } + + // Get the value of a vector's non-struct member. Uses a named return + // argument to conveniently set the zero value for the result. + void GetMemberOfVectorOfNonStruct(const StructDef &struct_def, + const FieldDef &field, std::string &code) { + code += "proc " + namer_.Field(field) + Export + + "(self: " + namer_.Type(struct_def) + + ", j: int): " + TypeName(field) + " =\n"; + code += OffsetPrefix(field); + code += Indent + Indent + "var x = " + SelfData + ".Vector(o)\n"; + code += Indent + Indent + "x += j.uoffset * " + + NumToString(InlineSize(field.value.type.VectorType())) + + ".uoffset\n"; + + code += + Indent + Indent + "result = " + GenGetter(field.value.type) + "x)\n"; + code += EndFunc; + } + + // Begin the creator function signature. + void BeginBuilderArgs(const StructDef &struct_def, std::string &code) { + code += "proc Create" + namer_.Type(struct_def) + Export + + "(self: var Builder, "; + } + + // Recursively generate arguments for a constructor, to deal with nested + // structs. + void StructBuilderArgs(const StructDef &struct_def, + const std::string nameprefix, + const std::string namesuffix, bool has_field_name, + bool with_type, const std::string fieldname_suffix, + std::string &code) { + for (auto it = struct_def.fields.vec.begin(); + it != struct_def.fields.vec.end(); ++it) { + auto &field = **it; + const auto &field_type = field.value.type; + const auto &type = + IsArray(field_type) ? field_type.VectorType() : field_type; + if (it != struct_def.fields.vec.begin()) { code += std::string(", "); } + if (IsStruct(type)) { + // Generate arguments for a struct inside a struct. To ensure names + // don't clash, and to make it obvious these arguments are constructing + // a nested struct, prefix the name with the field name. + auto subprefix = nameprefix; + if (has_field_name) { + subprefix += namer_.Field(field) + fieldname_suffix; + } + StructBuilderArgs(*field.value.type.struct_def, subprefix, namesuffix, + has_field_name, with_type, fieldname_suffix, code); + } else { + code += nameprefix; + if (has_field_name) { code += namer_.Field(field); } + code += namesuffix; + if (with_type) { + code += ": "; + + if (IsEnum(field.value.type)) { + imports_.insert(GetImport(struct_def, field)); + code += ImportedName(struct_def, field); + } else { + code += GenTypeBasic(field.value.type); + } + }; + } + } + } + + // End the creator function signature. + void EndBuilderArgs(std::string &code) { code += "): uoffset =\n"; } + + // Recursively generate struct construction statements and instert manual + // padding. + void StructBuilderBody(const StructDef &struct_def, const char *nameprefix, + std::string &code) { + code += Indent + "self.Prep(" + NumToString(struct_def.minalign) + ", "; + code += NumToString(struct_def.bytesize) + ")\n"; + for (auto it = struct_def.fields.vec.rbegin(); + it != struct_def.fields.vec.rend(); ++it) { + auto &field = **it; + if (field.padding) + code += Indent + "self.Pad(" + NumToString(field.padding) + ")\n"; + if (IsStruct(field.value.type)) { + StructBuilderBody(*field.value.type.struct_def, + (nameprefix + (namer_.Field(field) + "_")).c_str(), + code); + } else { + code += Indent + "self.Prepend" + GenMethod(field) + "("; + code += nameprefix + namer_.Field(field); + if (IsEnum(field.value.type)) { + code += "." + GenTypeGet(field.value.type); + } + code += ")\n"; + } + } + } + + void EndBuilderBody(std::string &code) { + code += Indent + "result = self.Offset()\n"; + code += EndFunc; + } + + // Get the value of a table's starting offset. + void GetStartOfTable(const StructDef &struct_def, std::string &code) { + code += "proc " + namer_.Type(struct_def) + "Start" + Export + + "(builder: var Builder) =\n"; + code += Indent + "builder.StartObject(" + + NumToString(struct_def.fields.vec.size()) + ")\n"; + } + + // Set the value of a table's field. + void BuildFieldOfTable(const StructDef &struct_def, const FieldDef &field, + const size_t offset, std::string &code) { + std::string field_type = GenTypeBasic(field.value.type); + std::string variable = namer_.Variable(field); + if (IsEnum(field.value.type)) { + imports_.insert(GetImport(struct_def, field)); + field_type = ImportedName(struct_def, field); + variable += "." + GenTypeBasic(field.value.type); + } + code += "proc " + namer_.Type(struct_def) + "Add" + namer_.Method(field) + + Export + "(builder: var Builder, " + namer_.Variable(field) + ": " + + field_type + ") =\n"; + code += Indent + "builder.Prepend" + GenMethod(field) + "Slot(" + + NumToString(offset) + ", " + variable + ", " + + GetDefaultValue(field) + ")\n"; + } + + // Set the value of one of the members of a table's vector. + void BuildVectorOfTable(const StructDef &struct_def, const FieldDef &field, + std::string &code) { + code += "proc " + namer_.Type(struct_def) + "Start" + namer_.Method(field) + + "Vector" + Export + + "(builder: var Builder, numElems: uoffset): uoffset =\n"; + code += Indent + "builder.StartVector(" + + NumToString(InlineSize(field.value.type.VectorType())) + + ", numElems, " + + NumToString(InlineAlignment(field.value.type.VectorType())) + ")\n"; + } + + // Get the offset of the end of a table. + void GetEndOffsetOnTable(const StructDef &struct_def, std::string &code) { + code += "proc " + namer_.Type(struct_def) + "End" + Export + + "(builder: var Builder): uoffset =\n"; + code += Indent + "result = builder.EndObject()\n"; + code += "\n"; + } + + // Generate a struct field, conditioned on its child type(s). + void GenStructAccessor(const StructDef &struct_def, const FieldDef &field, + std::string &code) { + GenComment(field.doc_comment, &code, &def_comment); + if (IsScalar(field.value.type.base_type)) { + if (struct_def.fixed) { + GetScalarFieldOfStruct(struct_def, field, code); + } else { + GetScalarFieldOfTable(struct_def, field, code); + } + } else { + switch (field.value.type.base_type) { + case BASE_TYPE_STRUCT: + if (struct_def.fixed) { + GetStructFieldOfStruct(struct_def, field, code); + } else { + GetStructFieldOfTable(struct_def, field, code); + } + break; + case BASE_TYPE_STRING: GetStringField(struct_def, field, code); break; + case BASE_TYPE_VECTOR: { + auto vectortype = field.value.type.VectorType(); + GetVectorLen(struct_def, field, code); + if (vectortype.base_type == BASE_TYPE_STRUCT) { + GetMemberOfVectorOfStruct(struct_def, field, code); + GetStructVector(struct_def, field, code); + } else if (IsEnum(vectortype) || + vectortype.base_type == BASE_TYPE_UNION) { + GetMemberOfVectorOfEnum(struct_def, field, code); + GetEnumVector(struct_def, field, code); + } else { + GetMemberOfVectorOfNonStruct(struct_def, field, code); + GetNonStructVector(struct_def, field, code); + } + break; + } + case BASE_TYPE_UNION: GetUnionField(struct_def, field, code); break; + default: FLATBUFFERS_ASSERT(0); + } + } + } + + // Generate table constructors, conditioned on its members' types. + void GenTableBuilders(const StructDef &struct_def, std::string &code) { + GetStartOfTable(struct_def, code); + + for (auto it = struct_def.fields.vec.begin(); + it != struct_def.fields.vec.end(); ++it) { + auto &field = **it; + if (field.deprecated) continue; + + auto offset = it - struct_def.fields.vec.begin(); + BuildFieldOfTable(struct_def, field, offset, code); + if (IsVector(field.value.type)) { + BuildVectorOfTable(struct_def, field, code); + } + } + + GetEndOffsetOnTable(struct_def, code); + } + + // Generate struct or table methods. + void GenStruct(const StructDef &struct_def, std::string &code) { + if (struct_def.generated) return; + + GenComment(struct_def.doc_comment, &code, &def_comment); + BeginClass(struct_def, code); + + // Generate the Init method that sets the field in a pre-existing + // accessor object. This is to allow object reuse. + for (auto it = struct_def.fields.vec.begin(); + it != struct_def.fields.vec.end(); ++it) { + auto &field = **it; + if (field.deprecated) continue; + + GenStructAccessor(struct_def, field, code); + } + + if (struct_def.fixed) { + // create a struct constructor function + GenStructBuilder(struct_def, code); + } else { + // Create a set of functions that allow table construction. + GenTableBuilders(struct_def, code); + } + } + + // Generate enum declarations. + void GenEnum(const EnumDef &enum_def, std::string &code) { + if (enum_def.generated) return; + + GenComment(enum_def.doc_comment, &code, &def_comment); + BeginEnum(namer_.Type(enum_def), code); + for (auto it = enum_def.Vals().begin(); it != enum_def.Vals().end(); ++it) { + auto &ev = **it; + GenComment(ev.doc_comment, &code, &def_comment, Indent.c_str()); + EnumMember(enum_def, ev, code); + } + EndEnum(code); + } + + // Returns the method name for use with add/put calls. + std::string GenMethod(const FieldDef &field) const { + if (IsStruct(field.value.type)) { + return "Struct"; + } else { + return ""; + } + } + + std::string GenTypeBasic(const Type &type) const { + // clang-format off + static const char *nimtypename[] = { + #define FLATBUFFERS_TD(ENUM, IDLTYPE, \ + CTYPE, JTYPE, GTYPE, NTYPE, PTYPE, RTYPE, KTYPE, STYPE, NIMTYPE) \ + #NIMTYPE, + FLATBUFFERS_GEN_TYPES(FLATBUFFERS_TD) + #undef FLATBUFFERS_TD + }; + // clang-format on + return nimtypename[type.base_type]; + } + + std::string GenTypePointer(const Type &type) const { + switch (type.base_type) { + case BASE_TYPE_STRING: return "string"; + case BASE_TYPE_VECTOR: return GenTypeGet(type.VectorType()); + case BASE_TYPE_STRUCT: return type.struct_def->name; + case BASE_TYPE_UNION: + // fall through + default: return "*flatbuffers.Table"; + } + } + + std::string GenTypeGet(const Type &type) const { + return IsScalar(type.base_type) ? GenTypeBasic(type) : GenTypePointer(type); + } + + // Returns the function name that is able to read a value of the given type. + std::string GenGetter(const Type &type) const { + switch (type.base_type) { + case BASE_TYPE_STRING: return SelfData + ".String("; + case BASE_TYPE_UNION: return SelfData + ".Union("; + case BASE_TYPE_VECTOR: return GenGetter(type.VectorType()); + default: return "Get[" + GenTypeGet(type) + "](" + SelfData + ", "; + } + } + + std::string TypeName(const FieldDef &field) const { + if (field.value.type.struct_def) { + return namer_.Type(*field.value.type.struct_def); + } else if (field.value.type.enum_def) { + return namer_.Type(*field.value.type.enum_def); + } else { + return GenTypeGet(field.value.type); + } + } + + std::string GetDefaultValue(const FieldDef &field) const { + BaseType base_type = field.value.type.base_type; + if (field.IsScalarOptional()) { + return "default(" + GenTypeBasic(field.value.type) + ")"; + } else if (IsBool(base_type)) { + return field.value.constant == "0" ? "false" : "true"; + } else if (IsFloat(base_type)) { + return field.value.constant; + } else if (IsInteger(base_type)) { + return field.value.constant; + } else { + // For string, struct, and table. + return "default(" + GenTypeBasic(field.value.type) + ")"; + } + } + + std::vector StringSplit(const std::string orig_str, + const std::string token) { + std::vector result; + std::string str = orig_str; + while (str.size()) { + size_t index = str.find(token); + if (index != std::string::npos) { + result.push_back(str.substr(0, index)); + str = str.substr(index + token.size()); + if (str.size() == 0) result.push_back(str); + } else { + result.push_back(str); + str = ""; + } + } + return result; + } + std::string GetRelativePathFromNamespace(const std::string &relative_to, + const std::string &str2) { + std::vector relative_to_vec = StringSplit(relative_to, "/"); + std::vector str2_vec = StringSplit(str2, "/"); + while (relative_to_vec.size() > 0 && str2_vec.size() > 0) { + if (relative_to_vec[0] == str2_vec[0]) { + relative_to_vec.erase(relative_to_vec.begin()); + str2_vec.erase(str2_vec.begin()); + } else { + break; + } + } + relative_to_vec.pop_back(); + for (size_t i = 0; i < relative_to_vec.size(); ++i) { + str2_vec.insert(str2_vec.begin(), std::string("..")); + } + + std::string new_path; + for (size_t i = 0; i < str2_vec.size(); ++i) { + new_path += str2_vec[i]; + if (i != str2_vec.size() - 1) { new_path += "/"; } + } + return new_path; + } + + std::string GenPackageReference(const StructDef &struct_def) const { + return namer_.NamespacedType(struct_def); + } + + std::string GenPackageReference(const EnumDef &enum_def) const { + return namer_.NamespacedType(enum_def); + } + + std::string GenPackageReference(const Type &type) const { + if (type.struct_def) { + return GenPackageReference(*type.struct_def); + } else if (type.enum_def) { + return GenPackageReference(*type.enum_def); + } else { + return GenTypeGet(type); + } + } + + std::string GenPackageReference(const EnumVal &val) const { + return GenPackageReference(val.union_type); + } + + std::string GenPackageReference(const FieldDef &field) const { + return GenPackageReference(field.value.type); + } + + std::string ImportName(const EnumVal &val) { + std::string package_name = GenPackageReference(val); + std::replace(package_name.begin(), package_name.end(), '/', '_'); + return package_name; + } + + std::string ImportName(const FieldDef &field) { + std::string package_name = GenPackageReference(field); + std::replace(package_name.begin(), package_name.end(), '/', '_'); + return package_name; + } + + std::string ImportedName(const EnumDef &enum_def, const EnumVal &val) { + return ImportName(val) + "." + namer_.Variant(val); + } + + std::string ImportedName(const StructDef &struct_def, const FieldDef &field) { + if (namer_.Type(struct_def) == TypeName(field)) { + return TypeName(field); + } else { + return ImportName(field) + "." + TypeName(field); + } + } + + std::string GetImport(const EnumDef &enum_def, const EnumVal &val) { + if (GenPackageReference(val) != GenPackageReference(enum_def)) { + return "import " + + GetRelativePathFromNamespace(GenPackageReference(enum_def), + GenPackageReference(val)) + + " as " + ImportName(val) + "\n"; + } else { + return std::string(); + } + } + + std::string GetImport(const StructDef &struct_def, const FieldDef &field) { + if (GenPackageReference(field) != GenPackageReference(struct_def)) { + return "import " + + GetRelativePathFromNamespace(GenPackageReference(struct_def), + GenPackageReference(field)) + + " as " + ImportName(field) + "\n"; + } else { + return std::string(); + } + } + + void ImportOptions() { import_options_ = true; } + + // Create a struct with a builder and the struct's arguments. + void GenStructBuilder(const StructDef &struct_def, std::string &code) { + BeginBuilderArgs(struct_def, code); + StructBuilderArgs(struct_def, + /* nameprefix = */ "", + /* namesuffix = */ "", + /* has_field_name = */ true, + /* with_type = */ true, + /* fieldname_suffix = */ "_", code); + EndBuilderArgs(code); + + StructBuilderBody(struct_def, "", code); + EndBuilderBody(code); + } + + void GenUnionInit(const StructDef &struct_def, const FieldDef &field, + std::string &field_type) { + field_type = TypeName(field); + if (parser_.opts.include_dependence_headers) { + imports_.insert(GetImport(struct_def, field)); + field_type = ImportedName(struct_def, field); + } + field_type += "T"; + } + + void GenStructInit(const StructDef &struct_def, const FieldDef &field, + std::string &output) { + const Type &type = field.value.type; + const std::string object_type = namer_.ObjectType(*type.struct_def); + if (parser_.opts.include_dependence_headers) { + imports_.insert(GetImport(struct_def, field)); + output = ImportedName(struct_def, field) + "T"; + } else { + output = object_type; + } + } + + void GenVectorInit(const StructDef &struct_def, const FieldDef &field, + std::string &field_type) { + const Type &vector_type = field.value.type.VectorType(); + const BaseType base_type = vector_type.base_type; + if (base_type == BASE_TYPE_STRUCT) { + const std::string object_type = + namer_.ObjectType(*vector_type.struct_def); + field_type = object_type; + if (parser_.opts.include_dependence_headers) { + imports_.insert(GetImport(struct_def, field)); + field_type = ImportedName(struct_def, field) + "T"; + } + field_type = "seq[" + field_type + "]"; + } else if (base_type == BASE_TYPE_STRING) { + field_type = "seq[string]"; + } else { + field_type = GenTypeBasic(vector_type); + if (IsEnum(vector_type)) { + imports_.insert(GetImport(struct_def, field)); + field_type = ImportedName(struct_def, field); + } + field_type = "seq[" + field_type + "]"; + } + } + + void GenInitialize(const StructDef &struct_def, std::string &code) { + std::string object_fields_code; + std::string init_fields_code; + for (auto it = struct_def.fields.vec.begin(); + it != struct_def.fields.vec.end(); ++it) { + auto &field = **it; + if (field.deprecated) continue; + + // Determines field type, default value, and typing imports. + auto base_type = field.value.type.base_type; + std::string field_type; + switch (base_type) { + case BASE_TYPE_UNION: { + GenUnionInit(struct_def, field, field_type); + break; + } + case BASE_TYPE_STRUCT: { + GenStructInit(struct_def, field, field_type); + break; + } + case BASE_TYPE_VECTOR: + case BASE_TYPE_ARRAY: { + GenVectorInit(struct_def, field, field_type); + break; + } + case BASE_TYPE_STRING: { + field_type = "string"; + break; + } + default: + if (IsEnum(field.value.type)) { + imports_.insert(GetImport(struct_def, field)); + field_type = ImportedName(struct_def, field); + } else { + field_type = GenTypeBasic(field.value.type); + } + // Scalar or sting fields. + break; + } + + if ((field.IsOptional() || base_type == BASE_TYPE_STRUCT || + base_type == BASE_TYPE_UNION || base_type == BASE_TYPE_STRING) && + !struct_def.fixed && !(base_type == BASE_TYPE_VECTOR)) { + ImportOptions(); + field_type = "Option[" + field_type + "]"; + } + + const auto field_field = namer_.Field(field); + std::string default_value; + if (field.IsScalarOptional()) { + default_value = "default(type(result." + field_field + "))"; + } else if (IsBool(base_type)) { + default_value = (field.value.constant == "0" ? "false" : "true"); + } else if (IsFloat(base_type) || IsInteger(base_type)) { + default_value = field.value.constant; + } else if (base_type == BASE_TYPE_STRING && field.IsDefault()) { + default_value = "some(\"" + field.value.constant + "\")"; + } else { + // For string, struct, and table. + default_value = "default(type(result." + field_field + "))"; + } + if (IsEnum(field.value.type)) { + default_value = field_type + "(" + default_value + ")"; + } + // Wrties the init statement. + object_fields_code += + GenIndents(1) + field_field + Export + ": " + field_type; + init_fields_code += + GenIndents(1) + "result." + field_field + " = " + default_value; + } + + // Writes Init method. + code += "type " + namer_.ObjectType(struct_def) + Export + " = ref object"; + code += object_fields_code; + code += "\n"; + code += "\n"; + + code += "proc new" + namer_.ObjectType(struct_def) + Export + + "(): " + namer_.ObjectType(struct_def) + " = "; + code += GenIndents(1) + "new(result)"; + + if (init_fields_code.empty()) { + code += GenIndents(1) + "discard\n"; + } else { + code += init_fields_code; + code += "\n"; + } + code += "\n"; + } + + void InitializeFromBuf(const StructDef &struct_def, std::string &code) const { + const auto struct_var = namer_.Variable(struct_def); + const auto struct_type = namer_.Type(struct_def); + + code += "proc InitFromBuf" + Export + "(self: var " + + namer_.ObjectType(struct_def) + ", buf: seq[byte], pos: uoffset) ="; + code += GenIndents(1) + "var " + struct_var + ": " + struct_type; + code += GenIndents(1) + struct_var + ".Init(buf, pos)"; + code += GenIndents(1) + "self.InitFromObj(" + struct_var + ")"; + code += "\n"; + code += "\n"; + } + + void InitializeFromObjForObject(const StructDef &struct_def, + std::string &code) const { + const auto struct_var = namer_.Variable(struct_def); + const auto struct_object = namer_.ObjectType(struct_def); + + code += "proc InitFromObj" + Export + "(self: var " + + namer_.ObjectType(struct_def) + ", " + struct_var + ": " + + namer_.Type(struct_def) + ") ="; + code += GenIndents(1) + "self.UnPack(" + struct_var + ")"; + code += "\n"; + code += "\n"; + } + + void GenUnPackForStruct(const StructDef &struct_def, const FieldDef &field, + std::string &code) { + const auto struct_var = namer_.Variable(struct_def); + const auto field_field = namer_.Field(field); + const auto field_var = namer_.Variable(field); + auto field_type = TypeName(field); + + if (parser_.opts.include_dependence_headers) { + imports_.insert(GetImport(struct_def, field)); + field_type = ImportedName(struct_def, field); + } + if (struct_def.fixed) { + // Struct fields are not optional + code += GenIndents(1) + "self." + field_field + ".InitFromObj(" + + struct_var + "." + field_field + ")"; + } else { + code += + GenIndents(1) + "if " + struct_var + "." + field_field + ".isSome:"; + code += GenIndents(2) + "var " + field_var + ": " + field_type + "T"; + code += GenIndents(2) + field_var + ".InitFromObj(" + struct_var + "." + + field_field + ".get())"; + code += + GenIndents(2) + "self." + field_field + " = some(" + field_var + ")"; + } + // A struct's accessor requires a struct buf instance. + // if (struct_def.fixed && + // field.value.type.base_type == BASE_TYPE_STRUCT) { + // code += field_type + "()"; + // } + // code += "))"; + } + + void GenUnPackForUnion(const StructDef &struct_def, const FieldDef &field, + std::string &code) { + const auto field_field = namer_.Field(field); + const auto field_method = namer_.Method(field); + const auto struct_var = namer_.Variable(struct_def); + + imports_.insert(GetImport(struct_def, field)); + const auto field_type = ImportedName(struct_def, field); + + code += GenIndents(1) + "if " + struct_var + "." + field_field + ".isSome:"; + code += GenIndents(2) + "self." + field_field + " = some(" + field_type + + "Creator(" + struct_var + "." + field_field + "Type, " + + struct_var + "." + field_field + ".get()))"; + } + + void GenUnPackForStructVector(const StructDef &struct_def, + const FieldDef &field, std::string &code) { + const auto field_field = namer_.Field(field); + const auto field_var = namer_.Variable(field); + const auto struct_var = namer_.Variable(struct_def); + auto field_type = TypeName(field); + + if (parser_.opts.include_dependence_headers) { + imports_.insert(GetImport(struct_def, field)); + field_type = ImportedName(struct_def, field); + } + + code += GenIndents(1) + "for " + field_var + " in " + struct_var + "." + + field_field + ":"; + code += GenIndents(2) + "var " + field_var + "t: " + field_type + "T"; + code += GenIndents(2) + field_var + "t.InitFromObj(" + field_var + ")"; + code += GenIndents(2) + "self." + field_field + ".add(" + field_var + "t)"; + } + + void GenUnPackForScalarVector(const StructDef &struct_def, + const FieldDef &field, std::string &code) { + const auto field_field = namer_.Field(field); + const auto struct_var = namer_.Variable(struct_def); + + code += GenIndents(1) + "self." + field_field + " = " + struct_var + "." + + field_field; + } + + void GenUnPackForScalar(const StructDef &struct_def, const FieldDef &field, + std::string &code) const { + const auto field_field = namer_.Field(field); + const auto struct_var = namer_.Variable(struct_def); + + code += GenIndents(1) + "self." + field_field + " = " + struct_var + "." + + field_field; + } + + // Generates the UnPack method for the object class. + void GenUnPack(const StructDef &struct_def, std::string &code_base) { + std::string code; + // Items that needs to be imported. No duplicate modules will be imported. + std::set import_list; + + for (auto it = struct_def.fields.vec.begin(); + it != struct_def.fields.vec.end(); ++it) { + auto &field = **it; + if (field.deprecated) continue; + + auto field_type = TypeName(field); + switch (field.value.type.base_type) { + case BASE_TYPE_STRUCT: { + GenUnPackForStruct(struct_def, field, code); + break; + } + case BASE_TYPE_UNION: { + GenUnPackForUnion(struct_def, field, code); + break; + } + case BASE_TYPE_VECTOR: { + auto vectortype = field.value.type.VectorType(); + if (vectortype.base_type == BASE_TYPE_STRUCT) { + GenUnPackForStructVector(struct_def, field, code); + } else { + GenUnPackForScalarVector(struct_def, field, code); + } + break; + } + case BASE_TYPE_ARRAY: { + GenUnPackForScalarVector(struct_def, field, code); + break; + } + default: GenUnPackForScalar(struct_def, field, code); + } + } + + // Writes import statements and code into the generated file. + const auto struct_var = namer_.Variable(struct_def); + + code_base += "proc UnPack" + Export + "(self: var " + + namer_.ObjectType(struct_def) + ", " + struct_var + ": " + + namer_.Type(struct_def) + ") ="; + if (code.empty()) { code += GenIndents(1) + "discard"; } + // Write the code. + code_base += code; + code_base += "\n"; + code_base += "\n"; + } + + void GenPackForStruct(const StructDef &struct_def, std::string &code) { + code += "proc Pack" + Export + "(self: " + namer_.ObjectType(struct_def) + + ", builder: var Builder): uoffset ="; + code += GenIndents(1) + "result = builder.Create" + + namer_.Type(struct_def) + "("; + + StructBuilderArgs(struct_def, + /* nameprefix = */ "self.", + /* namesuffix = */ "", + /* has_field_name = */ true, + /* with_type = */ false, + /* fieldname_suffix = */ ".", code); + code += ")\n"; + code += "\n"; + } + + void GenPackForStructVectorField(const StructDef &struct_def, + const FieldDef &field, + std::string &code_prefix, + std::string &code) { + const auto field_field = namer_.Field(field); + const auto struct_type = namer_.Type(struct_def); + const auto field_method = namer_.Method(field); + + // Creates the field. + code_prefix += GenIndents(1) + "var " + field_field + ": uoffset"; + code_prefix += GenIndents(1) + "if self." + field_field + ".len > 0:"; + if (field.value.type.struct_def->fixed) { + code_prefix += GenIndents(2) + "discard builder." + struct_type + + "Start" + field_method + "Vector(self." + field_field + + ".len.uoffset)"; + code_prefix += GenIndents(2) + "for i in countdown(self." + field_field + + ".len - 1, 0):"; + code_prefix += + GenIndents(3) + "discard self." + field_field + "[i].Pack(builder)"; + } else { + // If the vector is a struct vector, we need to first build accessor for + // each struct element. + code_prefix += + GenIndents(2) + "var " + field_field + "list: seq[uoffset] = @[]"; + code_prefix += GenIndents(2) + "for i in countup(0, self." + field_field + + ".len - 1):"; + code_prefix += GenIndents(3) + field_field + "list.add(self." + + field_field + "[i].Pack(builder))"; + + code_prefix += GenIndents(2) + "discard builder." + struct_type + + "Start" + field_method + "Vector(self." + field_field + + ".len.uoffset)"; + code_prefix += GenIndents(2) + "for i in countdown(" + field_field + + "list.len - 1, 0):"; + code_prefix += GenIndents(3) + "builder.PrependOffsetRelative" + "(" + + field_field + "list[i])"; + } + code_prefix += GenIndents(2) + field_field + " = builder.EndVector()"; + + // Adds the field into the struct. + code += GenIndents(1) + "if self." + field_field + ".len > 0:"; + code += GenIndents(2) + "builder." + struct_type + "Add" + field_method + + "(" + field_field + ")"; + } + + void GenPackForScalarVectorFieldHelper(const StructDef &struct_def, + const FieldDef &field, + std::string &code, int indents) const { + const auto field_field = namer_.Field(field); + const auto field_method = namer_.Method(field); + const auto struct_type = namer_.Type(struct_def); + const auto vectortype = field.value.type.VectorType(); + + code += GenIndents(indents) + "discard builder." + struct_type + "Start" + + field_method + "Vector(self." + field_field + ".len.uoffset)"; + code += GenIndents(indents) + "for i in countdown(self." + field_field + + ".len - 1, 0):"; + code += GenIndents(indents + 1) + "builder.Prepend"; + + std::string type_name; + switch (vectortype.base_type) { + case BASE_TYPE_BOOL: + case BASE_TYPE_CHAR: + case BASE_TYPE_UCHAR: + case BASE_TYPE_SHORT: + case BASE_TYPE_USHORT: + case BASE_TYPE_INT: + case BASE_TYPE_UINT: + case BASE_TYPE_LONG: + case BASE_TYPE_ULONG: + case BASE_TYPE_FLOAT: + case BASE_TYPE_DOUBLE: break; + case BASE_TYPE_STRING: type_name = "OffsetRelative"; break; + default: type_name = "OffsetRelative"; break; + } + code += type_name; + } + + void GenPackForScalarVectorField(const StructDef &struct_def, + const FieldDef &field, + std::string &code_prefix, + std::string &code) const { + const auto field_field = namer_.Field(field); + const auto field_method = namer_.Method(field); + const auto struct_type = namer_.Type(struct_def); + + // Adds the field into the struct. + code += GenIndents(1) + "if self." + field_field + ".len > 0:"; + code += GenIndents(2) + "builder." + struct_type + "Add" + field_method + + "(" + field_field + ")"; + + // Creates the field. + code_prefix += GenIndents(1) + "var " + field_field + ": uoffset "; + code_prefix += GenIndents(1) + "if self." + field_field + ".len > 0:"; + // If the vector is a string vector, we need to first build accessor for + // each string element. And this generated code, needs to be + // placed ahead of code_prefix. + auto vectortype = field.value.type.VectorType(); + if (IsString(vectortype)) { + code_prefix += + GenIndents(2) + "var " + field_field + "list: seq[uoffset] = @[]"; + code_prefix += + GenIndents(2) + "for i in countup(0, self." + field_field + ".len):"; + code_prefix += GenIndents(3) + field_field + + "list.add(builder.Create(self." + field_field + "[i]))"; + GenPackForScalarVectorFieldHelper(struct_def, field, code_prefix, 2); + code_prefix += "(" + field_field + "list[i])"; + code_prefix += GenIndents(2) + field_field + " = builder.EndVector()"; + } else { + GenPackForScalarVectorFieldHelper(struct_def, field, code_prefix, 2); + code_prefix += "(self." + field_field + "[i])"; + code_prefix += GenIndents(2) + field_field + " = builder.EndVector()"; + } + } + + void GenPackForStructField(const StructDef &struct_def, const FieldDef &field, + std::string &code_prefix, std::string &code) { + const auto field_field = namer_.Field(field); + const auto field_method = namer_.Method(field); + const auto struct_type = namer_.Type(struct_def); + + code_prefix += GenIndents(1) + "var " + field_field + ": uoffset"; + code_prefix += GenIndents(1) + "if self." + field_field + ".isSome:"; + code_prefix += GenIndents(2) + field_field + " = self." + field_field + + ".get().Pack(builder)"; + + code += GenIndents(1) + "if self." + field_field + ".isSome:"; + code += GenIndents(2) + "builder." + struct_type + "Add" + field_method + + "(" + field_field + ")"; + } + + void GenPackForUnionField(const StructDef &struct_def, const FieldDef &field, + std::string &code_prefix, std::string &code) { + const auto field_field = namer_.Field(field); + const auto field_method = namer_.Method(field); + const auto struct_type = namer_.Type(struct_def); + + code_prefix += GenIndents(1) + "var " + field_field + ": uoffset"; + code_prefix += GenIndents(1) + "if self." + field_field + ".isSome:"; + code_prefix += GenIndents(2) + field_field + " = self." + field_field + + ".get().Pack(self." + field_field + "Type, builder)"; + code += GenIndents(1) + "if self." + field_field + ".isSome:"; + code += GenIndents(2) + "builder." + struct_type + "Add" + field_method + + "(" + field_field + ")"; + } + + void GenPackForTable(const StructDef &struct_def, std::string &code_base) { + std::string code, code_prefix; + const auto struct_type = namer_.Type(struct_def); + + code_base += "proc Pack" + Export + + "(self: " + namer_.ObjectType(struct_def) + + ", builder: var Builder): uoffset ="; + code += GenIndents(1) + "builder." + struct_type + "Start()"; + for (auto it = struct_def.fields.vec.begin(); + it != struct_def.fields.vec.end(); ++it) { + auto &field = **it; + if (field.deprecated) continue; + + const auto field_method = namer_.Method(field); + const auto field_field = namer_.Field(field); + + switch (field.value.type.base_type) { + case BASE_TYPE_STRUCT: { + GenPackForStructField(struct_def, field, code_prefix, code); + break; + } + case BASE_TYPE_UNION: { + GenPackForUnionField(struct_def, field, code_prefix, code); + break; + } + case BASE_TYPE_VECTOR: { + auto vectortype = field.value.type.VectorType(); + if (vectortype.base_type == BASE_TYPE_STRUCT) { + GenPackForStructVectorField(struct_def, field, code_prefix, code); + } else { + GenPackForScalarVectorField(struct_def, field, code_prefix, code); + } + break; + } + case BASE_TYPE_ARRAY: { + GenPackForScalarVectorField(struct_def, field, code_prefix, code); + break; + } + case BASE_TYPE_STRING: { + code_prefix += GenIndents(1) + "var " + field_field + ": uoffset"; + code_prefix += GenIndents(1) + "if self." + field_field + ".isSome:"; + code_prefix += GenIndents(2) + field_field + + " = builder.Create(self." + field_field + ".get())"; + code += GenIndents(1) + "if self." + field_field + ".isSome:"; + code += GenIndents(2) + "builder." + struct_type + "Add" + + field_method + "(" + field_field + ")"; + break; + } + default: + // Generates code for scalar values. If the value equals to the + // default value, builder will automatically ignore it. So we don't + // need to check the value ahead. + code += GenIndents(1) + "builder." + struct_type + "Add" + + field_method + "(self." + field_field + ")"; + break; + } + } + + code += GenIndents(1) + "result = builder." + struct_type + "End()"; + + code_base += code_prefix + code; + code_base += "\n"; + code_base += "\n"; + } + void GenForwardDeclarations(const StructDef &struct_def, std::string &code) { + std::string forward_declare_comment = " # forward declaration\n"; + std::string type = namer_.Type(struct_def); + std::string object_type = namer_.ObjectType(struct_def); + std::string struct_var = namer_.Variable(struct_def); + + code += "proc Pack" + Export + "(self: " + object_type + + ", builder: var Builder): uoffset" + forward_declare_comment; + code += "proc UnPack" + Export + "(self: var " + object_type + ", " + + struct_var + ": " + type + ")" + forward_declare_comment; + code += "proc InitFromObj" + Export + "(self: var " + object_type + ", " + + struct_var + ": " + type + ")" + forward_declare_comment; + code += "proc InitFromBuf" + Export + "(self: var " + object_type + + ", buf: seq[byte], pos: uoffset)" + forward_declare_comment; + code += "\n"; + } + + void GenStructForObjectAPI(const StructDef &struct_def, + std::string &code_base) { + std::string code; + // Creates an object class for a struct or a table + GenInitialize(struct_def, code); + + GenForwardDeclarations(struct_def, code); + if (struct_def.fixed) { + GenPackForStruct(struct_def, code); + } else { + GenPackForTable(struct_def, code); + } + + GenUnPack(struct_def, code); + + InitializeFromObjForObject(struct_def, code); + + InitializeFromBuf(struct_def, code); + + // Adds the imports at top. + code_base += code; + } + + void GenUnionCreatorForStruct(const EnumDef &enum_def, const EnumVal &ev, + std::string &object_code, + std::string &creator_code, + std::string &pack_code) { + const std::string variant = namer_.Variant(ev); + const std::string field_var = namer_.Variable(*ev.union_type.struct_def); + const std::string enum_objectType = namer_.ObjectType(enum_def); + std::string field_type = namer_.ObjectType(*ev.union_type.struct_def); + if (parser_.opts.include_dependence_headers) { + imports_.insert(GetImport(enum_def, ev)); + field_type = ImportName(ev) + "." + field_type; + } + object_code += GenIndents(1) + "as" + variant + Export + ": " + field_type; + + creator_code += + GenIndents(1) + "of " + namer_.EnumVariant(enum_def, ev) + ":"; + creator_code += GenIndents(2) + "result.as" + variant + + ".InitFromBuf(table.Bytes, table.Pos)"; + + pack_code += GenIndents(1) + "of " + namer_.EnumVariant(enum_def, ev) + ":"; + pack_code += + GenIndents(2) + "result = self.as" + variant + ".Pack(builder)"; + } + + void GenUnionCreatorForString(const EnumDef &enum_def, const EnumVal &ev, + std::string &object_code, + std::string &creator_code, + std::string &pack_code) { + const auto union_type = namer_.Type(enum_def); + const auto variant = namer_.Variant(ev); + object_code += GenIndents(1) + "as" + variant + Export + ": string"; + + creator_code += + GenIndents(1) + "of " + namer_.EnumVariant(enum_def, ev) + ":"; + creator_code += + GenIndents(2) + "result.as" + variant + " = table.String(table.Pos)"; + + pack_code += GenIndents(1) + "of " + namer_.EnumVariant(enum_def, ev) + ":"; + pack_code += + GenIndents(2) + "result = builder.Create(self.as" + variant + ")"; + } + + // Creates an union object based on union type. + void GenUnionCreator(const EnumDef &enum_def, std::string &code) { + if (enum_def.generated) return; + std::string object_code; + std::string creator_code; + std::string pack_code; + + const auto enum_fn = namer_.Function(enum_def); + const auto enum_type = namer_.Type(enum_def); + const auto enum_objectType = namer_.ObjectType(enum_def); + + object_code += "\n"; + object_code += + "type " + enum_objectType + Export + " {.union.} = ref object"; + + creator_code += "proc " + enum_type + "Creator" + Export + + "(unionType: " + enum_type + + ", table: Vtable): " + enum_objectType + " ="; + creator_code += GenIndents(1) + "case unionType"; + + pack_code += "proc Pack" + Export + "(self: " + enum_objectType + + ", unionType: " + enum_type + + ", builder: var Builder): uoffset ="; + pack_code += GenIndents(1) + "case unionType"; + + for (auto it = enum_def.Vals().begin(); it != enum_def.Vals().end(); ++it) { + auto &ev = **it; + switch (ev.union_type.base_type) { + case BASE_TYPE_STRUCT: + GenUnionCreatorForStruct(enum_def, ev, object_code, creator_code, + pack_code); + break; + case BASE_TYPE_STRING: + GenUnionCreatorForString(enum_def, ev, object_code, creator_code, + pack_code); + break; + default: break; + } + } + creator_code += GenIndents(1) + "else:"; + creator_code += GenIndents(2) + "discard"; + pack_code += GenIndents(1) + "else:"; + pack_code += GenIndents(2) + "discard"; + code += object_code + "\n\n"; + code += creator_code + "\n\n"; + code += pack_code + "\n\n"; + } + + bool generate() { + std::string one_file_code; + if (!generateEnums(one_file_code)) return false; + if (!generateStructs(one_file_code)) return false; + if (parser_.opts.one_file) { + // Legacy file format uses keep casing. + return SaveType(file_name_ + "_generated.py", *parser_.current_namespace_, + one_file_code, true); + } + return true; + } + + private: + bool generateEnums(std::string &one_file_code) { + for (auto it = parser_.enums_.vec.begin(); it != parser_.enums_.vec.end(); + ++it) { + bool needs_imports = false; + auto &enum_def = **it; + std::string enumcode; + GenEnum(enum_def, enumcode); + if (parser_.opts.generate_object_based_api & enum_def.is_union) { + GenUnionCreator(enum_def, enumcode); + needs_imports = true; + } + + if (parser_.opts.one_file && !enumcode.empty()) { + one_file_code += enumcode + "\n\n"; + } else { + if (!SaveType(namer_.File(enum_def, SkipFile::Suffix), + *enum_def.defined_namespace, enumcode, needs_imports)) + return false; + } + } + return true; + } + + bool generateStructs(std::string &one_file_code) { + for (auto it = parser_.structs_.vec.begin(); + it != parser_.structs_.vec.end(); ++it) { + auto &struct_def = **it; + std::string declcode; + GenStruct(struct_def, declcode); + if (parser_.opts.generate_object_based_api && !struct_def.generated) { + GenStructForObjectAPI(struct_def, declcode); + } + + if (parser_.opts.one_file && !declcode.empty()) { + one_file_code += declcode + "\n\n"; + } else { + if (!SaveType(namer_.File(struct_def, SkipFile::Suffix), + *struct_def.defined_namespace, declcode, true)) + return false; + } + } + return true; + } + + // Begin by declaring namespace and imports. + void BeginFile(const std::string &name_space_name, const bool needs_imports, + std::string &code) { + code += "{.warning[HoleEnumConv]: off.}\n"; + code += Comment + FlatBuffersGeneratedWarning() + "\n\n"; + code += Comment + "namespace: " + name_space_name + "\n\n"; + if (import_options_) { + code += "import std/options\n"; + import_options_ = false; + } + if (needs_imports) { code += "import flatbuffers\n"; } + code += "\n"; + for (std::string import : imports_) { code += import; } + imports_.clear(); + code += "\n"; + } + + // Save out the generated code for a Table type. + bool SaveType(const std::string &defname, const Namespace &ns, + const std::string &classcode, bool needs_imports) { + if (!classcode.length()) return true; + + std::string code = ""; + BeginFile(LastNamespacePart(ns), needs_imports, code); + code += classcode; + + const std::string directories = + parser_.opts.one_file ? path_ : namer_.Directories(ns.components); + EnsureDirExists(directories); + + const std::string filename = directories + defname; + return SaveFile(filename.c_str(), code, false); + } + + private: + const IdlNamer namer_; + std::unordered_set imports_; + bool import_options_; +}; + +} // namespace nim + +bool GenerateNim(const Parser &parser, const std::string &path, + const std::string &file_name) { + nim::NimGenerator generator(parser, path, file_name); + return generator.generate(); +} + +} // namespace flatbuffers diff --git a/src/idl_gen_swift.cpp b/src/idl_gen_swift.cpp index ded8606aa5f..435c305162d 100644 --- a/src/idl_gen_swift.cpp +++ b/src/idl_gen_swift.cpp @@ -1908,7 +1908,7 @@ class SwiftGenerator : public BaseGenerator { // clang-format off static const char * const swift_type[] = { #define FLATBUFFERS_TD(ENUM, IDLTYPE, \ - CTYPE, JTYPE, GTYPE, NTYPE, PTYPE, RTYPE, KTYPE, STYPE) \ + CTYPE, JTYPE, GTYPE, NTYPE, PTYPE, RTYPE, KTYPE, STYPE, ...) \ #STYPE, FLATBUFFERS_GEN_TYPES(FLATBUFFERS_TD) #undef FLATBUFFERS_TD diff --git a/src/idl_parser.cpp b/src/idl_parser.cpp index 050698e13e5..1f047726c3d 100644 --- a/src/idl_parser.cpp +++ b/src/idl_parser.cpp @@ -2480,7 +2480,8 @@ bool Parser::SupportsOptionalScalars(const flatbuffers::IDLOptions &opts) { IDLOptions::kRust | IDLOptions::kSwift | IDLOptions::kLobster | IDLOptions::kKotlin | IDLOptions::kCpp | IDLOptions::kJava | IDLOptions::kCSharp | IDLOptions::kTs | IDLOptions::kBinary | - IDLOptions::kGo | IDLOptions::kPython | IDLOptions::kJson; + IDLOptions::kGo | IDLOptions::kPython | IDLOptions::kJson | + IDLOptions::kNim; unsigned long langs = opts.lang_to_generate; return (langs > 0 && langs < IDLOptions::kMAX) && !(langs & ~supported_langs); } @@ -2491,7 +2492,7 @@ bool Parser::SupportsOptionalScalars() const { bool Parser::SupportsDefaultVectorsAndStrings() const { static FLATBUFFERS_CONSTEXPR unsigned long supported_langs = - IDLOptions::kRust | IDLOptions::kSwift; + IDLOptions::kRust | IDLOptions::kSwift | IDLOptions::kNim; return !(opts.lang_to_generate & ~supported_langs); } @@ -2499,7 +2500,7 @@ bool Parser::SupportsAdvancedUnionFeatures() const { return (opts.lang_to_generate & ~(IDLOptions::kCpp | IDLOptions::kTs | IDLOptions::kPhp | IDLOptions::kJava | IDLOptions::kCSharp | IDLOptions::kKotlin | - IDLOptions::kBinary | IDLOptions::kSwift)) == 0; + IDLOptions::kBinary | IDLOptions::kSwift | IDLOptions::kNim)) == 0; } bool Parser::SupportsAdvancedArrayFeatures() const { diff --git a/tests/FlatBuffers.Test.Nim/NimTest.sh b/tests/FlatBuffers.Test.Nim/NimTest.sh new file mode 100755 index 00000000000..3c0a5e112c4 --- /dev/null +++ b/tests/FlatBuffers.Test.Nim/NimTest.sh @@ -0,0 +1,19 @@ +nim_dir=`pwd` +cd .. +test_dir=`pwd` +alias flatc='${test_dir}/../build/flatc' +shopt -s expand_aliases + +mkdir -p ${nim_dir}/generated +cd ${nim_dir}/generated/ +flatc --nim --gen-mutable -I ${test_dir}/include_test ${test_dir}/monster_test.fbs +# ${test_dir}/union_vector/union_vector.fbs +flatc --nim ${test_dir}/optional_scalars.fbs +flatc --nim --gen-object-api ${test_dir}/more_defaults.fbs +flatc --nim --gen-object-api ${test_dir}/recursive_imports.fbs +flatc --nim --gen-mutable --gen-object-api ${test_dir}/MutatingBool.fbs +cd ${nim_dir} + +testament --megatest:off all +# rm -r ${nim_dir}/generated +rm -r ${nim_dir}/tests/*/test diff --git a/tests/FlatBuffers.Test.Nim/tests/moredefaults/test.nim b/tests/FlatBuffers.Test.Nim/tests/moredefaults/test.nim new file mode 100644 index 00000000000..3872921290f --- /dev/null +++ b/tests/FlatBuffers.Test.Nim/tests/moredefaults/test.nim @@ -0,0 +1,58 @@ +discard """ + action: "run" + exitcode: 0 + timeout: 60.0 +""" +import std/unittest +import std/options +import flatbuffers +import ../../generated/MoreDefaults + +suite "TestMoreDefaults": + + test "testFlatbuffersObject": + var fbb = newBuilder(0) + fbb.MoreDefaultsStart() + let root = fbb.MoreDefaultsEnd() + fbb.Finish(root) + + var defaults: MoreDefaults + defaults.GetRootAs(fbb.FinishedBytes(), 0) + check(defaults.emptyString.isSome) + check(defaults.emptyString.get() == "") + check(defaults.someString.isSome) + check(defaults.someString.get() == "some") + check(defaults.ints == []) + check(defaults.floats == []) + check(defaults.bools == []) + check(defaults.intsLength == 0) + check(defaults.floatsLength == 0) + check(defaults.abcsLength == 0) + check(defaults.boolsLength == 0) + + test "testFlatbuffersObjectAPI": + let defaults = newMoreDefaultsT() + check(defaults.emptyString.isSome) + check(defaults.emptyString.get() == "") + check(defaults.someString.isSome) + check(defaults.someString.get() == "some") + check(defaults.ints == []) + check(defaults.floats == []) + check(defaults.abcs == []) + check(defaults.bools == []) + + var fbb = newBuilder(0) + fbb.Finish(defaults.Pack(fbb)) + var fDefaults: MoreDefaults + fDefaults.GetRootAs(fbb.FinishedBytes(), 0) + check(fDefaults.emptyString.isSome) + check(fDefaults.emptyString.get() == "") + check(fDefaults.someString.isSome) + check(fDefaults.someString.get() == "some") + check(fDefaults.ints == []) + check(fDefaults.floats == []) + check(fDefaults.bools == []) + check(fDefaults.intsLength == 0) + check(fDefaults.floatsLength == 0) + check(fDefaults.abcsLength == 0) + check(fDefaults.boolsLength == 0) diff --git a/tests/FlatBuffers.Test.Nim/tests/mutatingbool/test.nim b/tests/FlatBuffers.Test.Nim/tests/mutatingbool/test.nim new file mode 100644 index 00000000000..fdcabe71326 --- /dev/null +++ b/tests/FlatBuffers.Test.Nim/tests/mutatingbool/test.nim @@ -0,0 +1,46 @@ +discard """ + action: "run" + exitcode: 0 + timeout: 60.0 +""" +import std/unittest +import std/options +import flatbuffers +import ../../generated/TestMutatingBool +import ../../generated/Property + +proc get(): TestMutatingBoolT = + discard + +suite "TestMutatingBool": + + test "MutatingBool": + var builder = newBuilder(1024) + builder.TestMutatingBoolStart() + builder.TestMutatingBoolAddB(builder.CreateProperty(false)) + let root = builder.TestMutatingBoolEnd() + builder.Finish(root) + + var test_mutating_bool: TestMutatingBool + GetRootAs(test_mutating_bool, builder.FinishedBytes(), 0) + check(test_mutating_bool.b.isSome) + var prop2 = test_mutating_bool.b.get() + check(prop2.property == false) + discard (prop2.property = false) + check(prop2.property == false) + discard (prop2.property = true) + check(prop2.property == true) + + test "EmptyBool": + var builder = newBuilder(1024) + builder.TestMutatingBoolStart() + let root = builder.TestMutatingBoolEnd() + builder.Finish(root) + + var test_mutating_bool: TestMutatingBool + GetRootAs(test_mutating_bool, builder.FinishedBytes(), 0) + check(test_mutating_bool.b.isNone) + + test "defaultValue": + let mutatingbool = newTestMutatingBoolT() + check(mutatingbool.b.isNone) diff --git a/tests/FlatBuffers.Test.Nim/tests/mygame/test.nim b/tests/FlatBuffers.Test.Nim/tests/mygame/test.nim new file mode 100644 index 00000000000..1cd9085290e --- /dev/null +++ b/tests/FlatBuffers.Test.Nim/tests/mygame/test.nim @@ -0,0 +1,530 @@ +discard """ + action: "run" + exitcode: 0 + timeout: 60.0 +""" +import std/unittest +import std/options +import flatbuffers +import ../../generated/MyGame/Example/Test +import ../../generated/MyGame/Example/Monster +import ../../generated/MyGame/Example/Vec3 +import ../../generated/MyGame/Example/Color as ColorMod +import ../../generated/MyGame/Example/Any as AnyMod + +proc verifyMonster(monster: var Monster) = + check(monster.hp == 80) + check(monster.mana == 150) + check(monster.name.isSome) + check(monster.name.get() == "MyMonster") + check(monster.pos.isSome) + let pos = monster.pos.get() + check(pos.x == 1) + check(pos.y == 2) + check(pos.z == 3) + check(pos.test1 == 3) + check(pos.test2 == Color.Green) + check(pos.test3.a == 5) + check(pos.test3.b == 6) + check(monster.testType == Any.Monster) + check(monster.test.isSome) + let monster2 = Monster(tab: monster.test.get()) + check(monster2.name.isSome) + check(monster2.name.get() == "Fred") + check((monster.mana = 10) == false) + check(monster.mana == 150) + check(monster.inventoryLength == 5) + var sum: uint8 = 0 + for item in monster.inventory: + sum += item + check(sum == 10) + check(monster.test4Length == 2) + + let test0 = monster.test4(0) + let test1 = monster.test4(1) + var sum0 = test0.a + test0.b + var sum1 = test1.a + test1.b + check(sum0 + sum1 == 100) + + check(monster.testarrayofstringLength == 2) + check(monster.testarrayofstring(0) == "test1") + check(monster.testarrayofstring(1) == "test2") + check(monster.testbool == true) + + +suite "TestMyGame": + + test "testData": + let data: seq[byte] = @[byte(48), 0, 0, 0, 77, 79, 78, 83, 0, 0, 0, 0, 36, + 0, 72, 0, 40, 0, 0, 0, 38, 0, 32, 0, 0, 0, 28, 0, 0, 0, 27, 0, 20, 0, + 16, 0, 12, 0, 4, 0, 0, 0, 0, 0, 0, 0, 11, 0, 36, 0, 0, 0, 164, 0, 0, 0, + 0, 0, 0, 1, 60, 0, 0, 0, 68, 0, 0, 0, 76, 0, 0, 0, 0, 0, 0, 1, 88, 0, 0, + 0, 120, 0, 0, 0, 0, 0, 80, 0, 0, 0, 128, 63, 0, 0, 0, 64, 0, 0, 64, 64, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8, 64, 2, 0, 5, 0, 6, 0, 0, 0, 2, 0, 0, 0, + 64, 0, 0, 0, 48, 0, 0, 0, 2, 0, 0, 0, 30, 0, 40, 0, 10, 0, 20, 0, 152, + 255, 255, 255, 4, 0, 0, 0, 4, 0, 0, 0, 70, 114, 101, 100, 0, 0, 0, 0, 5, + 0, 0, 0, 0, 1, 2, 3, 4, 0, 0, 0, 5, 0, 0, 0, 116, 101, 115, 116, 50, 0, + 0, 0, 5, 0, 0, 0, 116, 101, 115, 116, 49, 0, 0, 0, 9, 0, 0, 0, 77, 121, + 77, 111, 110, 115, 116, 101, 114, 0, 0, 0, 3, 0, 0, 0, 20, 0, 0, 0, 36, + 0, 0, 0, 4, 0, 0, 0, 240, 255, 255, 255, 32, 0, 0, 0, 248, 255, 255, + 255, 36, 0, 0, 0, 12, 0, 8, 0, 0, 0, 0, 0, 0, 0, 4, 0, 12, 0, 0, 0, 28, + 0, 0, 0, 5, 0, 0, 0, 87, 105, 108, 109, 97, 0, 0, 0, 6, 0, 0, 0, 66, 97, + 114, 110, 101, 121, 0, 0, 5, 0, 0, 0, 70, 114, 111, 100, 111, 0, 0, 0] + var monster: Monster + GetRootAs(monster, data, 0) + verifyMonster(monster) + + test "testCreateString": + var fbb = newBuilder(0) + let name = fbb.Create("Frodo") + fbb.Finish(name) + check(fbb.FinishedBytes() == @[byte(4), 0, 0, 0, 5, 0, 0, 0, 70, 114, 111, + 100, 111, 0, 0, 0]) + + test "testCreateVector": + var fbb = newBuilder(0) + let vec = fbb.Create(@[byte(0), 1, 2, 3, 4]) + fbb.Finish(vec) + check(fbb.FinishedBytes() == @[byte(4), 0, 0, 0, 5, 0, 0, 0, 0, 1, 2, 3, 4, + 0, 0, 0]) + + test "createSimpleMonster": + var fbb = newBuilder(0) + let names = [ + fbb.Create("Frodo"), + fbb.Create("Barney"), + fbb.Create("Wilma"), + ] + fbb.MonsterStart() + fbb.MonsterAddName(names[0]) + let monster = fbb.MonsterEnd() + fbb.Finish(monster) + check(fbb.FinishedBytes() == @[byte(16), 0, 0, 0, 12, 0, 8, 0, 0, 0, 0, 0, + 0, 0, 4, 0, 12, 0, 0, 0, 28, 0, 0, 0, 5, 0, 0, 0, 87, 105, 108, 109, 97, + 0, 0, 0, 6, 0, 0, 0, 66, 97, 114, 110, 101, 121, 0, 0, 5, 0, 0, 0, 70, + 114, 111, 100, 111, 0, 0, 0]) + + test "testCreateTestVector": + var fbb = newBuilder(0) + discard fbb.MonsterStartTest4Vector(2) + discard fbb.CreateTest(a = 30, b = 40) + discard fbb.CreateTest(a = 10, b = 20) + let test4 = fbb.EndVector() + fbb.Finish(test4) + check(fbb.FinishedBytes() == @[byte(4), 0, 0, 0, 2, 0, 0, 0, 10, 0, 20, 0, + 30, 0, 40, 0]) + + test "testTableWithStruct": + var fbb = newBuilder(0) + fbb.MonsterStart() + fbb.MonsterAddPos(fbb.CreateVec3(x = 1, + y = 2, + z = 3, + test1 = 3, + test2 = Color.Green, + test3_a = 5, test3_b = 6)) + + let monster_end = fbb.MonsterEnd() + fbb.Finish(monster_end) + check(fbb.FinishedBytes() == @[byte(12), 0, 0, 0, 0, 0, 6, 0, 36, 0, 4, 0, + 6, 0, 0, 0, 0, 0, 128, 63, 0, 0, 0, 64, 0, 0, 64, 64, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 8, 64, 2, 0, 5, 0, 6, 0, 0, 0]) + + test "testCreateMonster": + var fbb = newBuilder(0) + let names = [ + fbb.Create("Frodo"), + fbb.Create("Barney"), + fbb.Create("Wilma"), + ] + + var offsets: seq[uoffset] = @[] + fbb.MonsterStart() + fbb.MonsterAddName(names[0]) + offsets.add(fbb.MonsterEnd()) + fbb.MonsterStart() + fbb.MonsterAddName(names[1]) + offsets.add(fbb.MonsterEnd()) + fbb.MonsterStart() + fbb.MonsterAddName(names[2]) + offsets.add(fbb.MonsterEnd()) + + let str = fbb.Create("MyMonster") + let test1 = fbb.Create("test1") + let test2 = fbb.Create("test2") + let inv = fbb.Create(@[byte(0), 1, 2, 3, 4]) + let fred = fbb.Create("Fred") + fbb.MonsterStart() + fbb.MonsterAddName(fred) + let mon2 = fbb.MonsterEnd() + + discard fbb.MonsterStartTest4Vector(2) + discard fbb.CreateTest(a = 30, b = 40) + discard fbb.CreateTest(a = 10, b = 20) + let test4 = fbb.EndVector() + + discard fbb.MonsterStartTestarrayofstringVector(2) + fbb.PrependOffsetRelative(test1) + fbb.PrependOffsetRelative(test2) + let stringTestVector = fbb.EndVector() + + discard fbb.MonsterStartTestarrayoftablesVector(3) + fbb.PrependOffsetRelative(offsets[0]) + fbb.PrependOffsetRelative(offsets[1]) + fbb.PrependOffsetRelative(offsets[2]) + let tableTestVector = fbb.EndVector() + + fbb.MonsterStart() + fbb.MonsterAddPos(fbb.CreateVec3(x = 1, + y = 2, + z = 3, + test1 = 3, + test2 = Color.Green, + test3_a = 5, test3_b = 6)) + fbb.MonsterAddHp(80) + fbb.MonsterAddName(str) + fbb.MonsterAddInventory(inv) + fbb.MonsterAddTestType(Any.Monster) + fbb.MonsterAddTest(mon2) + fbb.MonsterAddTest4(test4) + fbb.MonsterAddTestarrayofstring(stringTestVector) + fbb.MonsterAddTestbool(true) + fbb.MonsterAddTestarrayoftables(tableTestVector) + let monster_end = fbb.MonsterEnd() + fbb.Finish(monster_end) + check(fbb.FinishedBytes() == @[byte(40), 0, 0, 0, 36, 0, 72, 0, 40, 0, 0, 0, + 38, 0, 32, 0, 0, 0, 28, 0, 0, 0, 27, 0, 20, 0, 16, 0, 12, 0, 4, 0, 0, 0, + 0, 0, 0, 0, 11, 0, 36, 0, 0, 0, 68, 0, 0, 0, 0, 0, 0, 1, 76, 0, 0, 0, + 84, 0, 0, 0, 92, 0, 0, 0, 0, 0, 0, 1, 104, 0, 0, 0, 136, 0, 0, 0, 0, 0, + 80, 0, 0, 0, 128, 63, 0, 0, 0, 64, 0, 0, 64, 64, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 8, 64, 2, 0, 5, 0, 6, 0, 0, 0, 3, 0, 0, 0, 108, 0, 0, 0, 112, 0, + 0, 0, 128, 0, 0, 0, 2, 0, 0, 0, 52, 0, 0, 0, 60, 0, 0, 0, 2, 0, 0, 0, + 10, 0, 20, 0, 30, 0, 40, 0, 168, 255, 255, 255, 4, 0, 0, 0, 4, 0, 0, 0, + 70, 114, 101, 100, 0, 0, 0, 0, 5, 0, 0, 0, 0, 1, 2, 3, 4, 0, 0, 0, 5, 0, + 0, 0, 116, 101, 115, 116, 50, 0, 0, 0, 5, 0, 0, 0, 116, 101, 115, 116, + 49, 0, 0, 0, 9, 0, 0, 0, 77, 121, 77, 111, 110, 115, 116, 101, 114, 0, + 0, 0, 240, 255, 255, 255, 32, 0, 0, 0, 248, 255, 255, 255, 36, 0, 0, 0, + 12, 0, 8, 0, 0, 0, 0, 0, 0, 0, 4, 0, 12, 0, 0, 0, 28, 0, 0, 0, 5, 0, 0, + 0, 87, 105, 108, 109, 97, 0, 0, 0, 6, 0, 0, 0, 66, 97, 114, 110, 101, + 121, 0, 0, 5, 0, 0, 0, 70, 114, 111, 100, 111, 0, 0, 0]) + + + # var test_mutating_bool: TestMutatingBool + # GetRootAs(test_mutating_bool, builder.FinishedBytes(), 0) + # XCTAssertEqual(bytes.sizedByteArray, ) + # let monster = MyGame_Example_Monster.getRootAsMonster(bb: bytes.buffer) + # readMonster(monster: monster) + # mutateMonster(fb: bytes.buffer) + # readMonster(monster: monster) + + + # test "testReadFromOtherLanguages": + # let path = FileManager.default.currentDirectoryPath + # let url = URL(fileURLWithPath: path, isDirectory: true) + # .appendingPathComponent("monsterdata_test").appendingPathExtension("mon") + # guard let data = try? Data(contentsOf: url) else { return } + # let _data = ByteBuffer(data: data) + # readVerifiedMonster(fb: _data) + # } + + # test "testCreateMonster": + # let bytes = createMonster(withPrefix: false) + # // swiftformat:disable all + # check(bytes.sizedByteArray == [48, 0, 0, 0, 77, 79, 78, 83, 0, 0, 0, 0, 36, 0, 72, 0, 40, 0, 0, 0, 38, 0, 32, 0, 0, 0, 28, 0, 0, 0, 27, 0, 20, 0, 16, 0, 12, 0, 4, 0, 0, 0, 0, 0, 0, 0, 11, 0, 36, 0, 0, 0, 164, 0, 0, 0, 0, 0, 0, 1, 60, 0, 0, 0, 68, 0, 0, 0, 76, 0, 0, 0, 0, 0, 0, 1, 88, 0, 0, 0, 120, 0, 0, 0, 0, 0, 80, 0, 0, 0, 128, 63, 0, 0, 0, 64, 0, 0, 64, 64, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8, 64, 2, 0, 5, 0, 6, 0, 0, 0, 2, 0, 0, 0, 64, 0, 0, 0, 48, 0, 0, 0, 2, 0, 0, 0, 30, 0, 40, 0, 10, 0, 20, 0, 152, 255, 255, 255, 4, 0, 0, 0, 4, 0, 0, 0, 70, 114, 101, 100, 0, 0, 0, 0, 5, 0, 0, 0, 0, 1, 2, 3, 4, 0, 0, 0, 5, 0, 0, 0, 116, 101, 115, 116, 50, 0, 0, 0, 5, 0, 0, 0, 116, 101, 115, 116, 49, 0, 0, 0, 9, 0, 0, 0, 77, 121, 77, 111, 110, 115, 116, 101, 114, 0, 0, 0, 3, 0, 0, 0, 20, 0, 0, 0, 36, 0, 0, 0, 4, 0, 0, 0, 240, 255, 255, 255, 32, 0, 0, 0, 248, 255, 255, 255, 36, 0, 0, 0, 12, 0, 8, 0, 0, 0, 0, 0, 0, 0, 4, 0, 12, 0, 0, 0, 28, 0, 0, 0, 5, 0, 0, 0, 87, 105, 108, 109, 97, 0, 0, 0, 6, 0, 0, 0, 66, 97, 114, 110, 101, 121, 0, 0, 5, 0, 0, 0, 70, 114, 111, 100, 111, 0, 0, 0]) + # // swiftformat:enable all + # let monster = Monster.getRootAsMonster(bb: bytes.buffer) + # readMonster(monster: monster) + # mutateMonster(fb: bytes.buffer) + # readMonster(monster: monster) + # } + + # test "testCreateMonsterResizedBuffer": + # let bytes = createMonster(withPrefix: false) + # // swiftformat:disable all + # check(bytes.sizedByteArray == [48, 0, 0, 0, 77, 79, 78, 83, 0, 0, 0, 0, 36, 0, 72, 0, 40, 0, 0, 0, 38, 0, 32, 0, 0, 0, 28, 0, 0, 0, 27, 0, 20, 0, 16, 0, 12, 0, 4, 0, 0, 0, 0, 0, 0, 0, 11, 0, 36, 0, 0, 0, 164, 0, 0, 0, 0, 0, 0, 1, 60, 0, 0, 0, 68, 0, 0, 0, 76, 0, 0, 0, 0, 0, 0, 1, 88, 0, 0, 0, 120, 0, 0, 0, 0, 0, 80, 0, 0, 0, 128, 63, 0, 0, 0, 64, 0, 0, 64, 64, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8, 64, 2, 0, 5, 0, 6, 0, 0, 0, 2, 0, 0, 0, 64, 0, 0, 0, 48, 0, 0, 0, 2, 0, 0, 0, 30, 0, 40, 0, 10, 0, 20, 0, 152, 255, 255, 255, 4, 0, 0, 0, 4, 0, 0, 0, 70, 114, 101, 100, 0, 0, 0, 0, 5, 0, 0, 0, 0, 1, 2, 3, 4, 0, 0, 0, 5, 0, 0, 0, 116, 101, 115, 116, 50, 0, 0, 0, 5, 0, 0, 0, 116, 101, 115, 116, 49, 0, 0, 0, 9, 0, 0, 0, 77, 121, 77, 111, 110, 115, 116, 101, 114, 0, 0, 0, 3, 0, 0, 0, 20, 0, 0, 0, 36, 0, 0, 0, 4, 0, 0, 0, 240, 255, 255, 255, 32, 0, 0, 0, 248, 255, 255, 255, 36, 0, 0, 0, 12, 0, 8, 0, 0, 0, 0, 0, 0, 0, 4, 0, 12, 0, 0, 0, 28, 0, 0, 0, 5, 0, 0, 0, 87, 105, 108, 109, 97, 0, 0, 0, 6, 0, 0, 0, 66, 97, 114, 110, 101, 121, 0, 0, 5, 0, 0, 0, 70, 114, 111, 100, 111, 0, 0, 0]) + # // swiftformat:enable all + # readVerifiedMonster(fb: bytes.sizedBuffer) + # } + + # test "testCreateMonsterPrefixed": + # let bytes = createMonster(withPrefix: true) + # // swiftformat:disable all + # check(bytes.sizedByteArray, [44, 1, 0, 0, 44, 0, 0, 0, 77, 79, 78, 83, 36, 0, 72, 0, 40, 0, 0, 0, 38, 0, 32, 0, 0, 0, 28, 0, 0, 0, 27, 0, 20, 0, 16, 0, 12, 0, 4, 0, 0, 0, 0, 0, 0, 0, 11, 0, 36, 0, 0, 0, 164, 0, 0, 0, 0, 0, 0, 1, 60, 0, 0, 0, 68, 0, 0, 0, 76, 0, 0, 0, 0, 0, 0, 1, 88, 0, 0, 0, 120, 0, 0, 0, 0, 0, 80, 0, 0, 0, 128, 63, 0, 0, 0, 64, 0, 0, 64, 64, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8, 64, 2, 0, 5, 0, 6, 0, 0, 0, 2, 0, 0, 0, 64, 0, 0, 0, 48, 0, 0, 0, 2, 0, 0, 0, 30, 0, 40, 0, 10, 0, 20, 0, 152, 255, 255, 255, 4, 0, 0, 0, 4, 0, 0, 0, 70, 114, 101, 100, 0, 0, 0, 0, 5, 0, 0, 0, 0, 1, 2, 3, 4, 0, 0, 0, 5, 0, 0, 0, 116, 101, 115, 116, 50, 0, 0, 0, 5, 0, 0, 0, 116, 101, 115, 116, 49, 0, 0, 0, 9, 0, 0, 0, 77, 121, 77, 111, 110, 115, 116, 101, 114, 0, 0, 0, 3, 0, 0, 0, 20, 0, 0, 0, 36, 0, 0, 0, 4, 0, 0, 0, 240, 255, 255, 255, 32, 0, 0, 0, 248, 255, 255, 255, 36, 0, 0, 0, 12, 0, 8, 0, 0, 0, 0, 0, 0, 0, 4, 0, 12, 0, 0, 0, 28, 0, 0, 0, 5, 0, 0, 0, 87, 105, 108, 109, 97, 0, 0, 0, 6, 0, 0, 0, 66, 97, 114, 110, 101, 121, 0, 0, 5, 0, 0, 0, 70, 114, 111, 100, 111, 0, 0, ==0]) + # // swiftformat:enable all + + # var buffer = bytes.buffer + # readMonster(monster: getPrefixedSizeRoot(byteBuffer: &buffer)) + # } + + # test "testCreateMonsterUsingCreateMonsterMethodWithNilPos": + # var fbb = FlatBufferBuilder(initialSize: 1) + # let name = fbb.Create("Frodo") + # let mStart = fbb.MonsterStart() + # Monster.add(name: name, &fbb) + # let root = Monster.endMonster(&fbb, start: mStart) + # fbb.finish(offset: root) + # let newMonster = Monster.getRootAsMonster(bb: fbb.sizedBuffer) + # XCTAssertNil(newMonster.pos) + # check(newMonster.name, "Fro ==o") + # } + + # test "testCreateMonsterUsingCreateMonsterMethodWithPosX": + # var fbb = FlatBufferBuilder(initialSize: 1) + # let name = fbb.Create("Barney") + # let mStart = fbb.MonsterStart() + # Monster.add( + # pos: Vec3( + # x: 10, + # y: 0, + # z: 0, + # test1: 0, + # test2: .blue, + # test3: .init()), + # &fbb) + # Monster.add(name: name, &fbb) + # let root = Monster.endMonster(&fbb, start: mStart) + # fbb.finish(offset: root) + + # let newMonster = Monster.getRootAsMonster(bb: fbb.sizedBuffer) + # check(newMonster.pos!.x == 10) + # check(newMonster.name, "Barn ==y") + # } + + # test "testReadMonsterFromUnsafePointerWithoutCopying": + # // swiftformat:disable all + # var array: [UInt8] = [48, 0, 0, 0, 77, 79, 78, 83, 0, 0, 0, 0, 36, 0, 72, 0, 40, 0, 0, 0, 38, 0, 32, 0, 0, 0, 28, 0, 0, 0, 27, 0, 20, 0, 16, 0, 12, 0, 4, 0, 0, 0, 0, 0, 0, 0, 11, 0, 36, 0, 0, 0, 164, 0, 0, 0, 0, 0, 0, 1, 60, 0, 0, 0, 68, 0, 0, 0, 76, 0, 0, 0, 0, 0, 0, 1, 88, 0, 0, 0, 120, 0, 0, 0, 0, 0, 80, 0, 0, 0, 128, 63, 0, 0, 0, 64, 0, 0, 64, 64, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8, 64, 2, 0, 5, 0, 6, 0, 0, 0, 2, 0, 0, 0, 64, 0, 0, 0, 48, 0, 0, 0, 2, 0, 0, 0, 30, 0, 40, 0, 10, 0, 20, 0, 152, 255, 255, 255, 4, 0, 0, 0, 4, 0, 0, 0, 70, 114, 101, 100, 0, 0, 0, 0, 5, 0, 0, 0, 0, 1, 2, 3, 4, 0, 0, 0, 5, 0, 0, 0, 116, 101, 115, 116, 50, 0, 0, 0, 5, 0, 0, 0, 116, 101, 115, 116, 49, 0, 0, 0, 9, 0, 0, 0, 77, 121, 77, 111, 110, 115, 116, 101, 114, 0, 0, 0, 3, 0, 0, 0, 20, 0, 0, 0, 36, 0, 0, 0, 4, 0, 0, 0, 240, 255, 255, 255, 32, 0, 0, 0, 248, 255, 255, 255, 36, 0, 0, 0, 12, 0, 8, 0, 0, 0, 0, 0, 0, 0, 4, 0, 12, 0, 0, 0, 28, 0, 0, 0, 5, 0, 0, 0, 87, 105, 108, 109, 97, 0, 0, 0, 6, 0, 0, 0, 66, 97, 114, 110, 101, 121, 0, 0, 5, 0, 0, 0, 70, 114, 111, 100, 111, 0, 0, 0] + # // swiftformat:enable all + # let unpacked = array + # .withUnsafeMutableBytes { (memory) -> MonsterT in + # let bytes = ByteBuffer( + # assumingMemoryBound: memory.baseAddress!, + # capacity: memory.count) + # var monster = Monster.getRootAsMonster(bb: bytes) + # readFlatbufferMonster(monster: &monster) + # let unpacked = monster.unpack() + # return unpacked + # } + # readObjectApi(monster: unpacked) + # } + + # test "testArrayOfBools": + # let boolArray = [false, true, false, true, false, true, false] + # var fbb = FlatBufferBuilder(initialSize: 1) + # let name = fbb.Create("Frodo") + # let bools = fbb.createVector(boolArray) + # let root = Monster.createMonster( + # &fbb, + # nameOffset: name, + # testarrayofboolsVectorOffset: bools) + # fbb.finish(offset: root) + # let monster = Monster.getRootAsMonster(bb: fbb.sizedBuffer) + + # let values = monster.testarrayofbools + + # check(boolArray == values) + + # for i in 0.. FlatBufferBuild": + # var fbb = FlatBufferBuilder(initialSize: 1) + # let names = [ + # fbb.Create("Frodo"), + # fbb.Create("Barney"), + # fbb.Create("Wilma"), + # ] + # var offsets: [Offset] = [] + # let start1 = fbb.MonsterStart() + # Monster.add(name: names[0], &fbb) + # offsets.append(Monster.endMonster(&fbb, start: start1)) + # let start2 = fbb.MonsterStart() + # Monster.add(name: names[1], &fbb) + # offsets.append(Monster.endMonster(&fbb, start: start2)) + # let start3 = fbb.MonsterStart() + # Monster.add(name: names[2], &fbb) + # offsets.append(Monster.endMonster(&fbb, start: start3)) + + # let sortedArray = Monster.sortVectorOfMonster(offsets: offsets, &fbb) + + # let str = fbb.Create("MyMonster") + # let test1 = fbb.Create("test1") + # let test2 = fbb.Create("test2") + # let _inv: [Byte] = [0, 1, 2, 3, 4] + # let inv = fbb.createVector(_inv) + + # let fred = fbb.Create("Fred") + # let mon1Start = fbb.MonsterStart() + # Monster.add(name: fred, &fbb) + # let mon2 = Monster.endMonster(&fbb, start: mon1Start) + + # let test4 = fbb.createVector(ofStructs: [ + # Test(a: 30, b: 40), + # Test(a: 10, b: 20), + # ]) + + # let stringTestVector = fbb.createVector(ofOffsets: [test1, test2]) + # let mStart = fbb.MonsterStart() + # Monster.add( + # pos: Vec3( + # x: 1, + # y: 2, + # z: 3, + # test1: 3, + # test2: Color.Green, + # test3: .init(a: 5, b: 6)), + # &fbb) + # Monster.add(hp: 80, &fbb) + # Monster.add(name: str, &fbb) + # Monster.addVectorOf(inventory: inv, &fbb) + # Monster.add(testType: .monster, &fbb) + # Monster.add(test: mon2, &fbb) + # Monster.addVectorOf(test4: test4, &fbb) + # Monster.addVectorOf(testarrayofstring: stringTestVector, &fbb) + # Monster.add(testbool: true, &fbb) + # Monster.addVectorOf(testarrayoftables: sortedArray, &fbb) + # let end = Monster.endMonster(&fbb, start: mStart) + # Monster.finish(&fbb, end: end, prefix: prefix) + # return fbb + # } + + # test "mutateMonster(fb: ByteBuffe": + # let monster = Monster.getRootAsMonster(bb: fb) + # XCTAssertFalse(monster.mana = 10) + # check(monster.testarrayoftables(0).name, "Barn ==y") + # check(monster.testarrayoftables(1).name, "Fro ==o") + # check(monster.testarrayoftables(2).name, "Wil ==a") + + # // Example of searching for a table by the key + # XCTAssertNotNil(monster.testarrayoftablesBy(key: "Frodo")) + # XCTAssertNotNil(monster.testarrayoftablesBy(key: "Barney")) + # XCTAssertNotNil(monster.testarrayoftablesBy(key: "Wilma")) + + # check(monster.testType, ==.monster) + + # check(monster.inventory = 1 0) == true) + # check(monster.inventory = 2 1) == true) + # check(monster.inventory = 3 2) == true) + # check(monster.inventory = 4 3) == true) + # check(monster.inventory = 5 4) == true) + + # for i in 0..