From 8d27f4fbd40eb1ccb58338f4f17f7f9e4c5b8e59 Mon Sep 17 00:00:00 2001 From: Alexandre Bourquelot Date: Fri, 2 Feb 2024 10:35:11 +0800 Subject: [PATCH 01/47] atdcpp: copy files from atdd --- CHANGES.md | 1 + Makefile | 6 + README.md | 1 + atdcpp/.gitignore | 1 + atdcpp/Makefile | 33 + atdcpp/README.md | 47 + atdcpp/src/bin/Atdcpp_main.ml | 134 +++ atdcpp/src/bin/dune | 10 + atdcpp/src/lib/Codegen.ml | 1154 +++++++++++++++++++++ atdcpp/src/lib/Codegen.mli | 7 + atdcpp/src/lib/Dlang_annot.ml | 91 ++ atdcpp/src/lib/Dlang_annot.mli | 42 + atdcpp/src/lib/Indent.ml | 51 + atdcpp/src/lib/Indent.mli | 33 + atdcpp/src/lib/Version.ml | 3 + atdcpp/src/lib/dune | 7 + atdcpp/test/.gitignore | 2 + atdcpp/test/atd-input/everything.atd | 86 ++ atdcpp/test/cpp-expected/everything_atd.d | 786 ++++++++++++++ atdcpp/test/cpp-tests/dune | 26 + atdcpp/test/dune | 17 + dune | 1 + dune-project | 11 + scripts/install-opam-dependencies | 2 +- 24 files changed, 2551 insertions(+), 1 deletion(-) create mode 100644 atdcpp/.gitignore create mode 100644 atdcpp/Makefile create mode 100644 atdcpp/README.md create mode 100644 atdcpp/src/bin/Atdcpp_main.ml create mode 100644 atdcpp/src/bin/dune create mode 100644 atdcpp/src/lib/Codegen.ml create mode 100644 atdcpp/src/lib/Codegen.mli create mode 100644 atdcpp/src/lib/Dlang_annot.ml create mode 100644 atdcpp/src/lib/Dlang_annot.mli create mode 100644 atdcpp/src/lib/Indent.ml create mode 100644 atdcpp/src/lib/Indent.mli create mode 100644 atdcpp/src/lib/Version.ml create mode 100644 atdcpp/src/lib/dune create mode 100644 atdcpp/test/.gitignore create mode 100644 atdcpp/test/atd-input/everything.atd create mode 100644 atdcpp/test/cpp-expected/everything_atd.d create mode 100644 atdcpp/test/cpp-tests/dune create mode 100644 atdcpp/test/dune diff --git a/CHANGES.md b/CHANGES.md index 1327a3ccc..f57191790 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -5,6 +5,7 @@ Unreleased * atdd: Workaround d compiler bug regarding declaration order when using aliases (#393) Algebraic data types (SumType) now uses `alias this` syntax. * atdgen: Add support for `` in Melange (#401) +* atdcpp: Initial Release 2.15.0 (2023-10-26) ------------------- diff --git a/Makefile b/Makefile index 7afe41dfd..474e47bc3 100644 --- a/Makefile +++ b/Makefile @@ -11,6 +11,7 @@ DUNE ?= dune all: $(MAKE) -C atdpy clean-for-dune $(MAKE) -C atdd clean-for-dune + $(MAKE) -C atdcpp clean-for-dune $(MAKE) -C atdts clean-for-dune $(DUNE) build @@ -94,6 +95,11 @@ test-d: $(MAKE) test-common $(MAKE) -C atdd test +.PHONY: test-cpp +test-cpp: + $(MAKE) test-common + $(MAKE) -C atdcpp test + ############################################################################ .PHONY: js diff --git a/README.md b/README.md index 8b23267c6..63748396d 100644 --- a/README.md +++ b/README.md @@ -8,6 +8,7 @@ cross-language data types. It is used as input to generate efficient and type-safe serializers, deserializers and validators. Target programming languages currently supported: +* C++: [atdcpp](atdcpp) * DLang: [atdd](atdd) * Java: [atdj](atdj) * OCaml, Melange: [atdgen](atdgen) diff --git a/atdcpp/.gitignore b/atdcpp/.gitignore new file mode 100644 index 000000000..5e56e040e --- /dev/null +++ b/atdcpp/.gitignore @@ -0,0 +1 @@ +/bin diff --git a/atdcpp/Makefile b/atdcpp/Makefile new file mode 100644 index 000000000..5a2794690 --- /dev/null +++ b/atdcpp/Makefile @@ -0,0 +1,33 @@ +# +# Dlang/JSON backend +# + +DUNE ?= dune + +.PHONY: build +build: + rm -f bin/atdcpp + $(MAKE) clean-for-dune + $(DUNE) build @all + mkdir -p bin + ln -s ../../_build/install/default/bin/atdcpp bin/atdcpp + +# The symlink facilitates the development of test code that depends on the +# generated code. +.PHONY: test +test: + $(MAKE) clean-for-dune + $(DUNE) runtest -f; status=$$?; \ + ln -s ../../../_build/default/atdcpp/test/cpp-tests/everything.d \ + test/cpp-tests/everything.d && \ + exit "$$status" + +.PHONY: clean-for-dune +clean-for-dune: + rm -f test/cpp-tests/everything.d + +.PHONY: clean +clean: + $(MAKE) clean-for-dune + $(DUNE) clean + rm -rf bin diff --git a/atdcpp/README.md b/atdcpp/README.md new file mode 100644 index 000000000..4e17fd375 --- /dev/null +++ b/atdcpp/README.md @@ -0,0 +1,47 @@ +atdcpp +== + +atdcpp takes type definitions in the ATD format and derives `C++` +classes that can read and write JSON data. This saves the developer the +labor writing boilerplate that converts between dicts and classes. + +This allows safe interoperability with other languages supported by +ATD such as OCaml, Java, Python or Scala. + +See the sample input type definitions +[everything.atd](test/atd-input/everything.atd) and +the C++ output [everything.hpp](test/cpp-expected/everything.hpp). + +Requirements +-- + +Requirements for building and testing `atdcpp`: +* Opam and dependencies installed from the [`atd` project root](..) + with `make setup`. +* gcc / clang + +Requirements for generating C++ code: +* the `atdcpp` executable + +Requirements for compiling the generated C++ code: +* A working C++ compiler (gcc / clang) + +Documentation +-- + +* TODO + +Development notes +-- + +Build or rebuild with `make`. Test with `make test`. This requires +gtest. + +Running the tests is done from the `atdcpp/` main folder with `make +test`. + +We have two kinds of tests for atdcpp: +* code generation and C++ tests: + * they generate C++ code from ATD files and compare the C++ output + against the [expectations](cpp-expected). + * the generated code is executed by some tests. diff --git a/atdcpp/src/bin/Atdcpp_main.ml b/atdcpp/src/bin/Atdcpp_main.ml new file mode 100644 index 000000000..2457fb320 --- /dev/null +++ b/atdcpp/src/bin/Atdcpp_main.ml @@ -0,0 +1,134 @@ +(* + Entry point to the Atdcpp command. +*) + +open Printf +open Cmdliner + +type conf = { + input_files: string list; + version: bool; +} + +let run conf = + if conf.version then ( + print_endline Atdcpp.Version.version; + exit 0 + ) + else + conf.input_files + |> List.iter (fun atd_file -> + Atdcpp.Codegen.run_file atd_file + ) + +(***************************************************************************) +(* Command-line processing *) +(***************************************************************************) + +let error msg = + eprintf "Error: %s\n%!" msg; + exit 1 + +let input_files_term = + let info = + Arg.info [] (* list must be empty for anonymous arguments *) + ~docv:"PATH" + ~doc:"Input file in the ATD format with the '.atd' extension" + in + let default = [] in + Arg.value (Arg.pos_all Arg.file default info) + +let version_term = + let info = + Arg.info ["version"] + ~doc:"Prints the version of Atdcpp and exits" + in + Arg.value (Arg.flag info) + +let doc = + "Type-safe JSON serializers for D" + +(* + The structure of the help page. +*) +let man = [ + (* 'NAME' and 'SYNOPSIS' sections are inserted here by cmdliner. *) + + `S Manpage.s_description; (* standard 'DESCRIPTION' section *) + `P "Atdcpp turns a file containing type definitions into D classes \ + that read, write, and validate JSON data. The generated code \ + can be type-checked statically upon compilation to ensure user code agrees \ + with the ATD interface."; + + (* 'ARGUMENTS' and 'OPTIONS' sections are inserted here by cmdliner. *) + + `S Manpage.s_examples; (* standard 'EXAMPLES' section *) + `P "The following is a sample ATD file. 'sample.atd' becomes 'sample.d' \ + with the command 'Atdcpp sample.atd'."; + `Pre "\ +(* Sample ATD file sample.atd *) + +type foo = { + name: string; (* required field *) + ?description: string option; (* optional field *) + ~tags: string list; (* optional with implicit default *) + ~price : float; (* explicit default *) + items: bar list; +} + +(* sum type *) +type bar = [ + | Thing of int + | Nothing +] +"; + + `S Manpage.s_authors; + `P "Martin Jambon "; + + `S Manpage.s_bugs; + `P "Report issues at https://github.com/ahrefs/atd"; + + `S Manpage.s_see_also; + `P "atdgen, atdj, atds, atdts" +] + +let cmdline_term run = + let combine input_files version = + run { + input_files; + version; + } + in + Term.(const combine + $ input_files_term + $ version_term + ) + +let parse_command_line_and_run run = + let info = + Cmd.info + ~doc + ~man + "Atdcpp" + in + Cmd.v info (cmdline_term run) |> Cmd.eval |> exit + +let safe_run conf = + try run conf + with + (* for other exceptions, we show a backtrace *) + | Failure msg -> error msg + | Atd.Ast.Atd_error msg -> error msg + | e -> + let trace = Printexc.get_backtrace () in + eprintf "Error: exception %s\n%s%!" + (Printexc.to_string e) + trace + +let main () = + Printexc.record_backtrace true; + let conf = parse_command_line_and_run safe_run in + safe_run conf + +let () = main () diff --git a/atdcpp/src/bin/dune b/atdcpp/src/bin/dune new file mode 100644 index 000000000..f854622b6 --- /dev/null +++ b/atdcpp/src/bin/dune @@ -0,0 +1,10 @@ +(executable + (name atdcpp_main) + (public_name atdcpp) + (package atdcpp) + (libraries + cmdliner + atdcpp + atd + ) +) diff --git a/atdcpp/src/lib/Codegen.ml b/atdcpp/src/lib/Codegen.ml new file mode 100644 index 000000000..f65c041ef --- /dev/null +++ b/atdcpp/src/lib/Codegen.ml @@ -0,0 +1,1154 @@ +(* + Dlang code generation for JSON support (no biniou support) + + Takes the contents of a .atd file and translates it to a .d file. + Look into the tests to see what generated code looks like. +*) + +open Printf +open Atd.Ast +open Indent +module A = Atd.Ast +module B = Indent + +(* Mutable environment holding hash tables and such to avoid + naming conflicts. *) +type env = { + (* Global *) + create_variable: string -> string; + translate_variable: string -> string; + (* Local to a struct: instance variables, including method names *) + translate_inst_variable: unit -> (string -> string); +} + + +let annot_schema_dlang : Atd.Annot.schema_section = + { + section = "cpp"; + fields = [ + Type_expr, "t"; + Type_expr, "repr"; + Type_expr, "unwrap"; + Type_expr, "wrap"; + Field, "default"; + Module_head, "import"; + ] + } + +let annot_schema : Atd.Annot.schema = + annot_schema_dlang :: Atd.Json.annot_schema_json + +(* Translate a preferred variable name into an available Dlang identifier. *) +let trans env id = + env.translate_variable id + +(* + Convert an ascii string to CamelCase. + Note that this gets rid of leading and trailing underscores. +*) +let to_camel_case s = + let buf = Buffer.create (String.length s) in + let start_word = ref true in + for i = 0 to String.length s - 1 do + match s.[i] with + | '_' -> + start_word := true + | 'a'..'z' as c when !start_word -> + Buffer.add_char buf (Char.uppercase_ascii c); + start_word := false + | c -> + Buffer.add_char buf c; + start_word := false + done; + let name = Buffer.contents buf in + if name = "" then "X" + else + (* Make sure we don't start with a digit. This happens with + generated identifiers like '_42'. *) + match name.[0] with + | 'A'..'Z' | 'a'..'z' | '_' -> name + | _ -> "X" ^ name + +(* Use CamelCase *) +let struct_name env id = + trans env (to_camel_case id) + +(* + Create a struct identifier that hasn't been seen yet. + This is for internal disambiguation and still must translated using + the 'trans' function ('struct_name' will not work due to trailing + underscores being added for disambiguation). +*) +let create_struct_name env name = + let preferred_id = to_camel_case name in + env.create_variable preferred_id + +let init_env () : env = + let keywords = [ + (* Keywords + https://cpp.org/spec/lex.html#keywords + *) + "abstract";"alias";"align";"asm";"assert";"auto";"body";"bool"; + "break";"byte";"case";"cast";"catch";"cdouble";"cent";"cfloat"; + "char";"class";"const";"continue";"creal";"dchar";"debug";"default"; + "delegate";"delete";"deprecated";"do";"double";"else";"enum";"export"; + "extern";"false";"final";"finally";"float";"for";"foreach";"foreach_reverse"; + "function";"goto";"idouble";"if";"ifloat";"immutable";"import";"in"; + "inout";"int";"interface";"invariant";"ireal";"is";"lazy";"long";"macro"; + "mixin";"module";"new";"nothrow";"null";"out";"override";"package";"pragma"; + "private";"protected";"public";"pure";"real";"ref";"return";"scope";"shared"; + "short";"static";"struct";"super";"switch";"synchronized";"template";"this"; + "throw";"true";"try";"typeid";"typeof";"ubyte";"ucent";"uint";"ulong";"union"; + "unittest";"ushort";"version";"void";"wchar";"while";"with";"__FILE__";"__FILE_FULL_PATH__"; + "__MODULE__";"__LINE__";"__FUNCTION__";"__PRETTY_FUNCTION__";"__gshared"; + "__traits";"__vector";"__parameters"; + ] + in + (* Various variables used in the generated code. + Lowercase variables in this list are superfluous as long as all generated + variables either start with '_', 'atd_', or an uppercase letter. + *) + let reserved_variables = [ + (* from typing *) + "Any"; "Callable"; "Dict"; "List"; "Optional"; "Tuple"; + + (* for use in json.dumps, json.loads etc. *) + "json"; + + (* exceptions *) + "ValueError"; + + (* used to check JSON node type *) + "isinstance"; + "bool"; "int"; "float"; "str"; "dict"; "list"; "tuple"; + + (* other built-in variables *) + "self"; "cls"; "repr"; + ] in + let variables = + Atd.Unique_name.init + ~reserved_identifiers:(reserved_variables @ keywords) + ~reserved_prefixes:["atd_"; "_atd_"] + ~safe_prefix:"x_" + in + let method_names () = + Atd.Unique_name.init + ~reserved_identifiers:( + ["fromJson"; "toJson"; + "fromJsonString"; "toJsonString"] + @ keywords + ) + ~reserved_prefixes:["__"] + ~safe_prefix:"x_" + in + let create_variable name = + Atd.Unique_name.create variables name + in + let translate_variable id = + Atd.Unique_name.translate variables id + in + let translate_inst_variable () = + let u = method_names () in + fun id -> Atd.Unique_name.translate u id + in + { + create_variable; + translate_variable; + translate_inst_variable; + } + +type quote_kind = Single | Double + +(* Escape a string fragment to be placed in single quotes or double quotes. + https://docs.python.org/3/reference/lexical_analysis.html#string-and-bytes-literals +*) +let escape_string_content quote_kind s = + let buf = Buffer.create (String.length s + 2) in + for i = 0 to String.length s - 1 do + match s.[i], quote_kind with + | '\n', _ -> Buffer.add_string buf "\\n" + | '\\', _ -> Buffer.add_string buf "\\\\" + | '\'', Single -> Buffer.add_string buf "\\'" + | '"', Double -> Buffer.add_string buf "\\\"" + | c, (Single | Double) -> Buffer.add_char buf c + done; + Buffer.contents buf + +let single_esc s = + escape_string_content Single s + +let _double_esc s = + escape_string_content Double s + +let fixed_size_preamble atd_filename = + sprintf {| +// Generated by atdcpp from type definitions in %s. +// This implements classes for the types defined in '%s', providing +// methods and functions to convert data from/to JSON. + +// ############################################################################ +// # Private functions +// ############################################################################ + +module %s; + +import std.algorithm : map; +import std.array : array; +import std.conv; +import std.format; +import std.json; +import std.sumtype; +import std.traits : isCallable, ReturnType; +import std.typecons : nullable, Nullable, tuple, Tuple; + +private +{ + + class AtdException : Exception + { + @safe this(string msg, string file = __FILE__, size_t line = __LINE__) + { + super(msg, file, line); + } + } + + // workaround to make toDelegate callable from safe + @trusted auto toDelegate(F)(auto ref F fp) if (isCallable!F) + { + import std.functional; + return std.functional.toDelegate(fp); + } + + + @trusted T _atd_missing_json_field(T)(string typeName, string jsonFieldName) + { + throw new AtdException("missing field %%s in JSON object of type %%s".format(jsonFieldName, typeName)); + } + + auto _atd_bad_json(T)(string expectedType, T jsonValue) + { + string valueStr = jsonValue.to!string; + if (valueStr.length > 200) + { + valueStr = valueStr[0 .. 200]; + } + + return new AtdException( + "incompatible JSON value where type '%%s' was expected: %%s".format( + expectedType, valueStr + )); + } + + auto _atd_bad_d(T)(string expectedType, T jsonValue) + { + string valueStr = jsonValue.to!string; + if (valueStr.length > 200) + { + valueStr = valueStr[0 .. 200]; + } + + return new AtdException( + "incompatible D value where type '%%s' was expected: %%s".format( + expectedType, valueStr + )); + } + + auto _atd_read_unit(JSONValue x) + { + if (x.isNull) + return null; + else + throw _atd_bad_json("unit", x); + } + + auto _atd_read_bool(JSONValue x) + { + try + return x.boolean; + catch (JSONException e) + throw _atd_bad_json("bool", x); + } + + auto _atd_read_int(JSONValue x) + { + try + return cast(int) x.integer; + catch (JSONException e) + throw _atd_bad_json("int", x); + } + + auto _atd_read_float(JSONValue x) + { + try + return cast(float) x.floating; + catch (JSONException e) + throw _atd_bad_json("float", x); + } + + auto _atd_read_string(JSONValue x) + { + try + return x.str; + catch (JSONException e) + throw _atd_bad_json("string", x); + } + + template _atd_read_list(alias readElements) + { + auto _atd_read_list(JSONValue jsonVal) + { + if (jsonVal.type != JSONType.array) + throw _atd_bad_json("array", jsonVal); + auto list = jsonVal.array; + return array(list.map!readElements()); + } + } + + template _atd_read_object_to_assoc_array(alias readValue) + { + auto _atd_read_object_to_assoc_array(JSONValue jsonVal) + { + alias T = ReturnType!readValue; + + if (jsonVal.type != JSONType.object) + throw _atd_bad_json("object", jsonVal); + T[string] ret; + foreach (key, val; jsonVal.object) + ret[key] = readValue(val); + return ret; + } + } + + template _atd_read_array_to_assoc_dict(alias readKey, alias readValue) + { + auto _atd_read_array_to_assoc_dict(JSONValue jsonVal) + { + alias K = ReturnType!readKey; + alias V = ReturnType!readValue; + + if (jsonVal.type != JSONType.array) + throw _atd_bad_json("list", jsonVal); + V[K] ret; + foreach (jsonInnerVal; jsonVal.array) + { + if (jsonInnerVal.type != JSONType.array) + throw _atd_bad_json("list", jsonInnerVal); + ret[readKey(jsonInnerVal[0])] = readValue(jsonInnerVal[1]); + } + return ret; + } + } + + template _atd_read_object_to_tuple_list(alias readValue) + { + auto _atd_read_object_to_tuple_list(JSONValue jsonVal) + { + alias T = ReturnType!readValue; + + if (jsonVal.type != JSONType.object) + throw _atd_bad_json("object", jsonVal); + auto tupList = new Tuple!(string, T)[](jsonVal.object.length); + int i = 0; + foreach (key, val; jsonVal.object) + tupList[i++] = tuple(key, readValue(val)); + return tupList; + } + } + + template _atd_read_nullable(alias readElm) + { + auto _atd_read_nullable(JSONValue e) + { + alias T = ReturnType!readElm; + + if (e.isNull) + return Nullable!T.init; + else + return Nullable!T(readElm(e)); + } + } + + template _atd_read_option(alias readElm) + { + auto _atd_read_option(JSONValue e) + { + alias T = ReturnType!readElm; + + if (e.type == JSONType.string && e.str == "None") + return Nullable!T.init; + else if (e.type == JSONType.array && e.array.length == 2 && e[0].type == JSONType.string && e[0].str == "Some") + return Nullable!T(readElm(e[1])); + else + throw _atd_bad_json("option", e); + } + } + + template _atd_read_wrap(alias readElm, alias wrap) + { + auto _atd_read_wrap(JSONValue e) + { + return wrap(readElm(e)); + } + } + + // this whole set of function could be remplaced by one templated _atd_write_value function + // not sure it is what we want though + + auto _atd_write_unit(typeof(null) n) + { + return JSONValue(null); + } + + auto _atd_write_bool(bool b) + { + return JSONValue(b); + } + + auto _atd_write_int(int i) + { + return JSONValue(i); + } + + auto _atd_write_float(float f) + { + return JSONValue(f); + } + + auto _atd_write_string(string s) + { + return JSONValue(s); + } + + template _atd_write_list(alias writeElm) + { + auto _atd_write_list(T)(T[] list) + { + return JSONValue(array(list.map!writeElm())); + } + } + + template _atd_write_assoc_array_to_object(alias writeValue) + { + auto _atd_write_assoc_array_to_object(T)(T[string] assocArr) + { + JSONValue[string] ret; + foreach (key, val; assocArr) + ret[key] = writeValue(val); + return JSONValue(ret); + } + } + + template _atd_write_assoc_dict_to_array(alias writeKey, alias writeValue) + { + auto _atd_write_assoc_dict_to_array(K, V)(V[K] assocArr) + { + JSONValue[] ret; + foreach (key, val; assocArr) + ret ~= JSONValue([writeKey(key), writeValue(val)]); + return JSONValue(ret); + } + } + + template _atd_write_tuple_list_to_object(alias writeValue) + { + auto _atd_write_tuple_list_to_object(T)(Tuple!(string, T)[] tupList) + { + JSONValue[string] ret; + foreach (tup; tupList) + ret[tup[0]] = writeValue(tup[1]); + return JSONValue(ret); + } + } + + template _atd_write_nullable(alias writeElm) + { + auto _atd_write_nullable(T)(Nullable!T elm) + { + if (elm.isNull) + return JSONValue(null); + else + return writeElm(elm.get); + } + } + + template _atd_write_option(alias writeElm) + { + auto _atd_write_option(T)(Nullable!T elm) + { + if (elm.isNull) + return JSONValue("None"); + else + return JSONValue([JSONValue("Some"), writeElm(elm.get)]); + } + } + + template _atd_write_wrap(alias writeElm, alias unwrap) + { + auto _atd_write_wrap(Wrapped)(Wrapped e) + { + return writeElm(unwrap(e)); + } + } +} + +// ############################################################################ +// # Public classes +// ############################################################################ + +auto fromJsonString(T)(string s) +{ + JSONValue res = parseJSON(s); + return res.fromJson!T; +} + +auto toJsonString(T)(T obj) +{ + JSONValue res = obj.toJson!T; + return res.toString; +} + + |} + atd_filename + atd_filename + (sprintf "%s_atd" (Filename.remove_extension atd_filename)) + + +let not_implemented loc msg = + A.error_at loc ("not implemented in atdcpp: " ^ msg) + +let spaced ?(spacer = [Line ""]) (blocks : B.node list) : B.node list = + let rec spaced xs = + match List.filter (fun x -> not (B.is_empty_node x)) xs with + | [] + | [_] as xs -> xs + | a :: rest -> a :: spacer @ spaced rest + in + spaced blocks + +let double_spaced blocks = + spaced ~spacer:[Line ""; Line ""] blocks + +(* + Representations of ATD type '(string * value) list' in JSON and Dlang. + Key type or value type are provided when it's useful. +*) +type assoc_kind = + | Array_list (* default representation; possibly not even a list of pairs *) + | Array_dict of type_expr * type_expr (* key type, value type *) + (* Keys in JSON objects are always of type string. *) + | Object_dict of type_expr (* value type *) + | Object_list of type_expr (* value type *) + +let assoc_kind loc (e : type_expr) an : assoc_kind = + let json_repr = Atd.Json.get_json_list an in + let dlang_repr = Dlang_annot.get_dlang_assoc_repr an in + match e, json_repr, dlang_repr with + | Tuple (loc, [(_, key, _); (_, value, _)], an2), Array, Dict -> + Array_dict (key, value) + | Tuple (loc, + [(_, Name (_, (_, "string", _), _), _); (_, value, _)], an2), + Object, Dict -> + Object_dict value + | Tuple (loc, + [(_, Name (_, (_, "string", _), _), _); (_, value, _)], an2), + Object, List -> Object_list value + | _, Array, List -> Array_list + | _, Object, _ -> error_at loc "not a (string * _) list" + | _, Array, _ -> error_at loc "not a (_ * _) list" + +(* Map ATD built-in types to built-in Dlang types *) +let dlang_type_name env (name : string) = + match name with + | "unit" -> "void" + | "bool" -> "bool" + | "int" -> "int" + | "float" -> "float" + | "string" -> "string" + | "abstract" -> "JSONValue" + | user_defined -> + let typename = (struct_name env user_defined) in + typename + +let rec type_name_of_expr env (e : type_expr) : string = + match e with + | Sum (loc, _, _) -> not_implemented loc "inline sum types" + | Record (loc, _, _) -> not_implemented loc "inline records" + | Tuple (loc, xs, an) -> + let type_names = + xs + |> List.map (fun (loc, x, an) -> type_name_of_expr env x) + in + sprintf "Tuple!(%s)" (String.concat ", " type_names) + | List (loc, e, an) -> + (match assoc_kind loc e an with + | Array_list + | Object_list _ -> + sprintf "%s[]" + (type_name_of_expr env e) + | Array_dict (key, value) -> + sprintf "%s[%s]" + (type_name_of_expr env value) + (type_name_of_expr env key) + | Object_dict value -> + sprintf "%s[string]" + (type_name_of_expr env value) + ) + | Option (loc, e, an) -> sprintf "Nullable!%s" (type_name_of_expr env e) + | Nullable (loc, e, an) -> sprintf "Nullable!%s" (type_name_of_expr env e) + | Shared (loc, e, an) -> not_implemented loc "shared" (* TODO *) + | Wrap (loc, e, an) -> + (match Dlang_annot.get_dlang_wrap loc an with + | None -> error_at loc "wrap type declared, but no cpp annotation found" + | Some { dlang_wrap_t ; _ } -> dlang_wrap_t + ) + | Name (loc, (loc2, name, []), an) -> dlang_type_name env name + | Name (loc, (_, name, _::_), _) -> assert false + | Tvar (loc, _) -> not_implemented loc "type variables" + +let rec get_default_default (e : type_expr) : string option = + match e with + | Sum _ + | Record _ + | Tuple _ (* a default tuple could be possible but we're lazy *) -> None + | List _ -> Some "[]" + | Option _ + | Nullable _ -> None + | Shared (loc, e, an) -> get_default_default e + | Wrap (loc, e, an) -> get_default_default e + | Name (loc, (loc2, name, []), an) -> + (match name with + | "unit" -> None + | "bool" -> Some "false" + | "int" -> Some "0" + | "float" -> Some "0.0" + | "string" -> Some {|""|} + | "abstract" -> None + | _ -> None + ) + | Name _ -> None + | Tvar _ -> None + +let get_dlang_default (e : type_expr) (an : annot) : string option = + let user_default = Dlang_annot.get_dlang_default an in + match user_default with + | Some s -> Some s + | None -> get_default_default e + +(* If the field is '?foo: bar option', its cpp or json value has type + 'bar' rather than 'bar option'. *) +let unwrap_field_type loc field_name kind e = (* todo : dubious for cpp*) + match kind with + | Required + | With_default -> e + | Optional -> + match e with + | Option (loc, e, an) -> e + | _ -> + A.error_at loc + (sprintf "the type of optional field '%s' should be of \ + the form 'xxx option'" field_name) + +(* + Instance variable that's really the name of the getter method created + by @dataclass. It can't start with '__' as those are reserved for + internal magic. The 'trans_meth' translator must take care of this. +*) +let inst_var_name trans_meth field_name = + trans_meth field_name + +let rec json_writer ?(nested=false) env e = + match e with + | Sum (loc, _, _) -> not_implemented loc "inline sum types" + | Record (loc, _, _) -> not_implemented loc "inline records" + | Tuple (loc, cells, an) -> tuple_writer env (loc, cells, an) + | List (loc, e, an) -> + (match assoc_kind loc e an with + | Array_list -> + sprintf "_atd_write_list!(%s)" (json_writer ~nested:true env e) + | Array_dict (key, value) -> + sprintf "_atd_write_assoc_dict_to_array!(%s, %s)" + (json_writer ~nested:true env key) (json_writer ~nested:true env value) + | Object_dict value -> + sprintf "_atd_write_assoc_array_to_object!(%s)" + (json_writer ~nested:true env value) + | Object_list value -> + sprintf "_atd_write_tuple_list_to_object!(%s)" + (json_writer ~nested:true env value) + ) + | Option (loc, e, an) -> + sprintf "_atd_write_option!(%s)"(json_writer ~nested:true env e) + | Nullable (loc, e, an) -> + sprintf "_atd_write_nullable!(%s)" (json_writer ~nested:true env e) + | Shared (loc, e, an) -> not_implemented loc "shared" + | Wrap (loc, e, an) -> + (match Dlang_annot.get_dlang_wrap loc an with + | None -> error_at loc "wrap type declared, but no cpp annotation found" + | Some { dlang_wrap_t; dlang_unwrap ; _ } -> + sprintf "_atd_write_wrap!(%s, (%s e) => %s(e))" (json_writer ~nested:true env e) dlang_wrap_t dlang_unwrap + ) + | Name (loc, (loc2, name, []), an) -> + (match name with + | "bool" | "int" | "float" | "string" -> sprintf "_atd_write_%s" name + | "abstract" -> "(JSONValue x) => x" + | _ -> let dtype_name = (dlang_type_name env name) in + sprintf "((%s x) => x.toJson!(%s))" dtype_name dtype_name) + | Name (loc, _, _) -> not_implemented loc "parametrized types" + | Tvar (loc, _) -> not_implemented loc "type variables" + +and tuple_writer env (loc, cells, an) = + let tuple_body = + List.mapi (fun i (loc, e, an) -> + sprintf "%s(x[%i])" (json_writer env e) i + ) cells + |> String.concat ", " + in + sprintf "((%s x) => JSONValue([%s]))" + (type_name_of_expr env (Tuple (loc, cells, an))) + tuple_body + +let construct_json_field env trans_meth + ((loc, (name, kind, an), e) : simple_field) = + let unwrapped_type = unwrap_field_type loc name kind e in + let writer_function = json_writer env unwrapped_type in + let assignment = + [ + Line (sprintf "res[\"%s\"] = %s(obj.%s);" + (Atd.Json.get_json_fname name an |> single_esc) + writer_function + (inst_var_name trans_meth name)) + ] + in + match kind with + | Required + | With_default -> assignment + | Optional -> + [ + Line (sprintf "if (!obj.%s.isNull)" + (inst_var_name trans_meth name)); + Block [ Line(sprintf "res[\"%s\"] = %s(%s)(obj.%s);" + (Atd.Json.get_json_fname name an |> single_esc) + "_atd_write_option!" + (json_writer ~nested:true env unwrapped_type) + (inst_var_name trans_meth name))]; + ] + +(* + Function value that can be applied to a JSON node, converting it + to the desired value. +*) +let rec json_reader ?(nested=false) env (e : type_expr) = + match e with + | Sum (loc, _, _) -> not_implemented loc "inline sum types" + | Record (loc, _, _) -> not_implemented loc "inline records" + | Tuple (loc, cells, an) -> tuple_reader env cells + | List (loc, e, an) -> + (* ATD lists of pairs can be represented as objects in JSON or + as dicts in Python. All 4 combinations are supported. + The default is to use JSON arrays and Python lists. *) + (match assoc_kind loc e an with + | Array_list -> + sprintf "_atd_read_list!(%s)" + (json_reader ~nested:true env e) + | Array_dict (key, value) -> + sprintf "_atd_read_array_to_assoc_dict!(%s, %s)" + (json_reader ~nested:true env key) (json_reader ~nested:true env value) + | Object_dict value -> + sprintf "_atd_read_object_to_assoc_array!(%s)" + (json_reader ~nested:true env value) + | Object_list value -> + sprintf "_atd_read_object_to_tuple_list!(%s)" + (json_reader ~nested:true env value) + ) + | Option (loc, e, an) -> + sprintf "_atd_read_option!(%s)" (json_reader ~nested:true env e) + | Nullable (loc, e, an) -> + sprintf "_atd_read_nullable!(%s)" (json_reader ~nested:true env e) + | Shared (loc, e, an) -> not_implemented loc "shared" + | Wrap (loc, e, an) -> + (match Dlang_annot.get_dlang_wrap loc an with + | None -> error_at loc "wrap type declared, but no cpp annotation found" + | Some { dlang_wrap ; _ } -> + sprintf "_atd_read_wrap!(%s, (%s e) => %s(e))" (json_reader ~nested:true env e) (type_name_of_expr env e) dlang_wrap + ) + | Name (loc, (loc2, name, []), an) -> + (match name with + | "bool" | "int" | "float" | "string" -> sprintf "_atd_read_%s" name + | "abstract" -> "((JSONValue x) => x)" + | _ -> sprintf "fromJson!%s" + (struct_name env name) + ) + | Name (loc, _, _) -> not_implemented loc "parametrized types" + | Tvar (loc, _) -> not_implemented loc "type variables" + +and tuple_reader env cells = + let tuple_body = + List.mapi (fun i (loc, e, an) -> + sprintf "%s(x[%i])" (json_reader env e) i + ) cells + |> String.concat ", " + in + sprintf "((JSONValue x) @trusted { + if (x.type != JSONType.array || x.array.length != %d) + throw _atd_bad_json(\"Tuple of size %d\", x); + return tuple(%s); + })" (List.length cells) (List.length cells) tuple_body + +let from_json_class_argument + env trans_meth dlang_struct_name ((loc, (name, kind, an), e) : simple_field) = + let dlang_name = inst_var_name trans_meth name in + let json_name = Atd.Json.get_json_fname name an in + let else_body = + match kind with + | Required -> + sprintf "_atd_missing_json_field!(typeof(obj.%s))(\"%s\", \"%s\")" + dlang_name + (single_esc dlang_struct_name) + (single_esc json_name) + | Optional -> (sprintf "typeof(obj.%s).init" dlang_name) + | With_default -> + match get_dlang_default e an with + | Some x -> x + | None -> + A.error_at loc + (sprintf "missing default Dlang value for field '%s'" + name) + in + sprintf "obj.%s = (\"%s\" in x) ? %s(x[\"%s\"]) : %s;" + dlang_name + (single_esc json_name) + (json_reader env e) + (single_esc json_name) + else_body + +let inst_var_declaration + env trans_meth ((loc, (name, kind, an), e) : simple_field) = + let var_name = inst_var_name trans_meth name in + let type_name = type_name_of_expr env e in + let unwrapped_e = unwrap_field_type loc name kind e in + let default = + match kind with + | Required + | Optional -> "" + | With_default -> + match get_dlang_default unwrapped_e an with + | None -> "" + | Some x -> sprintf " = %s" x + in + [ + Line (sprintf "%s %s%s;" type_name var_name default) + ] + +let record env loc name (fields : field list) an = + let dlang_struct_name = struct_name env name in + let trans_meth = env.translate_inst_variable () in + let fields = + List.map (function + | `Field x -> x + | `Inherit _ -> (* expanded at loading time *) assert false) + fields + in + let inst_var_declarations = + List.map (fun x -> Inline (inst_var_declaration env trans_meth x)) fields + in + let json_object_body = + List.map (fun x -> + Inline (construct_json_field env trans_meth x)) fields in + let from_json_class_arguments = + List.map (fun x -> + Line (from_json_class_argument env trans_meth dlang_struct_name x) + ) fields in + let from_json = + [ + Line (sprintf "@trusted %s fromJson(T : %s)(JSONValue x) {" + (single_esc dlang_struct_name) (single_esc dlang_struct_name)); + Block [ + Line (sprintf "%s obj;" dlang_struct_name); + Inline from_json_class_arguments; + Line "return obj;"; + ]; + Line "}"; + ] + in + let to_json = + [ + Line (sprintf "@trusted JSONValue toJson(T : %s)(T obj) {" (single_esc dlang_struct_name)); + Block [ + Line ("JSONValue res;"); + Inline json_object_body; + Line "return res;" + ]; + Line "}"; + ] + in + [ + Line (sprintf "struct %s {" dlang_struct_name); + Block (spaced [ + Inline inst_var_declarations; + ]); + Line ("}"); + Line ""; + Inline from_json; + Inline to_json; + ] + +let alias_wrapper env name type_expr = + let dlang_struct_name = struct_name env name in + let value_type = type_name_of_expr env type_expr in + let optional_constructor = match type_expr with + | Tuple (_, _, _) -> + Line(sprintf "this(T...)(T args) @safe {_data = tuple(args);}"); + | _ -> Line(""); in + [ + Line (sprintf "struct %s{%s _data; alias _data this;" dlang_struct_name value_type); + Line (sprintf "this(%s init) @safe {_data = init;}" value_type ); + Line (sprintf "this(%s init) @safe {_data = init._data;}" dlang_struct_name); + Inline [optional_constructor]; + Line ("}"); + Line (sprintf "@trusted JSONValue toJson(T : %s)(%s e) {" dlang_struct_name dlang_struct_name); + Block [Line(sprintf "return %s(e);" (json_writer env type_expr))]; + Line("}"); + Line (sprintf "@trusted %s fromJson(T : %s)(JSONValue e) {" dlang_struct_name dlang_struct_name); + Block [Line(sprintf "return %s(%s(e));" dlang_struct_name (json_reader env type_expr))]; + Line("}"); + ] + +let case_class env type_name + (loc, orig_name, unique_name, an, opt_e) = + let json_name = Atd.Json.get_json_cons orig_name an in + match opt_e with + | None -> + [ + Line (sprintf {|// Original type: %s = [ ... | %s | ... ]|} + type_name + orig_name); + Line (sprintf "struct %s {}" (trans env unique_name)); + Line (sprintf "@trusted JSONValue toJson(T : %s)(T e) {" (trans env unique_name)); + Block [Line(sprintf "return JSONValue(\"%s\");" (single_esc json_name))]; + Line("}"); + ] + | Some e -> + [ + Line (sprintf {|// Original type: %s = [ ... | %s of ... | ... ]|} + type_name + orig_name); + Line (sprintf "struct %s { %s value; }" (trans env unique_name) (type_name_of_expr env e)); (* TODO : very dubious*) + Line (sprintf "@trusted JSONValue toJson(T : %s)(T e) {" (trans env unique_name)); + Block [Line(sprintf "return JSONValue([JSONValue(\"%s\"), %s(e.value)]);" (single_esc json_name) (json_writer env e))]; + Line("}"); + ] + + +let read_cases0 env loc name cases0 = + let ifs = + cases0 + |> List.map (fun (loc, orig_name, unique_name, an, opt_e) -> + let json_name = Atd.Json.get_json_cons orig_name an in + Inline [ + Line (sprintf "if (x.str == \"%s\") " (single_esc json_name)); + Block [ + Line (sprintf "return %s(%s());" (struct_name env name) (trans env unique_name)) + ]; + ] + ) + in + [ + Inline ifs; + Line (sprintf "throw _atd_bad_json(\"%s\", x);" + (struct_name env name |> single_esc)) + ] + +let read_cases1 env loc name cases1 = + let ifs = + cases1 + |> List.map (fun (loc, orig_name, unique_name, an, opt_e) -> + let e = + match opt_e with + | None -> assert false + | Some x -> x + in + let json_name = Atd.Json.get_json_cons orig_name an in + Inline [ + Line (sprintf "if (cons == \"%s\")" (single_esc json_name)); + Block [ + Line (sprintf "return %s(%s(%s(x[1])));" + (struct_name env name) + (trans env unique_name) + (json_reader env e)) + ] + ] + ) + in + [ + Inline ifs; + Line (sprintf "throw _atd_bad_json(\"%s\", x);" + (struct_name env name |> single_esc)) + ] + +let sum_container env loc name cases = + let dlang_struct_name = struct_name env name in + let type_list = + List.map (fun (loc, orig_name, unique_name, an, opt_e) -> + trans env unique_name + ) cases + |> String.concat ", " + in + let cases0, cases1 = + List.partition (fun (loc, orig_name, unique_name, an, opt_e) -> + opt_e = None + ) cases + in + let cases0_block = + if cases0 <> [] then + [ + Line "if (x.type == JSONType.string) {"; + Block (read_cases0 env loc name cases0); + Line "}"; + ] + else + [] + in + let cases1_block = + if cases1 <> [] then + [ + Line "if (x.type == JSONType.array && x.array.length == 2 && x[0].type == JSONType.string) {"; + Block [ + Line "string cons = x[0].str;"; + Inline (read_cases1 env loc name cases1) + ]; + Line "}"; + ] + else + [] + in + [ + Line (sprintf "struct %s{ %s _data; alias _data this;" dlang_struct_name (sprintf "SumType!(%s)" type_list) ); + Line (sprintf "@safe this(T)(T init) {_data = init;} @safe this(%s init) {_data = init._data;}}" dlang_struct_name); + Line ""; + Line (sprintf "@trusted %s fromJson(T : %s)(JSONValue x) {" + (single_esc dlang_struct_name) (single_esc dlang_struct_name)); + Block [ + Inline cases0_block; + Inline cases1_block; + Line (sprintf "throw _atd_bad_json(\"%s\", x);" + (single_esc (struct_name env name))) + ]; + Line "}"; + Line ""; + Line (sprintf "@trusted JSONValue toJson(T : %s)(T x) {" (dlang_struct_name)); + Block [ + Line "return x.match!("; + Line ( + List.map (fun (loc, orig_name, unique_name, an, opt_e) -> + sprintf "(%s v) => v.toJson!(%s)" (trans env unique_name) (trans env unique_name) + ) cases + |> String.concat ",\n"); + Line ");" + ]; + Line "}"; + ] + + +let sum env loc name cases = + let cases = + List.map (fun (x : variant) -> + match x with + | Variant (loc, (orig_name, an), opt_e) -> + let unique_name = create_struct_name env orig_name in + (loc, orig_name, unique_name, an, opt_e) + | Inherit _ -> assert false + ) cases + in + let case_classes = + List.map (fun x -> Inline (case_class env name x)) cases + |> double_spaced + in + let container_class = sum_container env loc name cases in + [ + Inline case_classes; + Inline container_class; + ] + |> double_spaced + +let type_def env ((loc, (name, param, an), e) : A.type_def) : B.t = + if param <> [] then + not_implemented loc "parametrized type"; + let unwrap e = + match e with + | Sum (loc, cases, an) -> + sum env loc name cases + | Record (loc, fields, an) -> + record env loc name fields an + | Tuple _ + | List _ + | Option _ + | Nullable _ + | Wrap _ + | Name _ -> alias_wrapper env name e + | Shared _ -> not_implemented loc "cyclic references" + | Tvar _ -> not_implemented loc "parametrized type" + in + unwrap e + +let module_body env x = + List.fold_left (fun acc (Type x) -> Inline (type_def env x) :: acc) [] x + |> List.rev + |> spaced + +let definition_group ~atd_filename env + (is_recursive, (items: A.module_body)) : B.t = + [ + Inline (module_body env items); + ] + +(* + Make sure that the types as defined in the atd file get a good name. + For example, type 'foo' should become struct 'Foo'. + We do this because each case constructor of sum types will also + translate to a struct in the same namespace. For example, + there may be a type like 'type bar = [ Foo | Bleep ]'. + We want to ensure that the type 'foo' gets the name 'Foo' and that only + later the case 'Foo' gets a lesser name like 'Foo_' or 'Foo2'. +*) +let reserve_good_struct_names env (items: A.module_body) = + List.iter + (fun (Type (loc, (name, param, an), e)) -> ignore (struct_name env name)) + items + +let to_file ~atd_filename ~head (items : A.module_body) dst_path = + let env = init_env () in + reserve_good_struct_names env items; + let head = List.map (fun s -> Line s) head in + let dlang_defs = + Atd.Util.tsort items + |> List.map (fun x -> Inline (definition_group ~atd_filename env x)) + in + Line (fixed_size_preamble atd_filename) :: Inline head :: dlang_defs + |> double_spaced + |> Indent.to_file ~indent:4 dst_path + +let run_file src_path = + let src_name = Filename.basename src_path in + let dst_name = + (if Filename.check_suffix src_name ".atd" then + Filename.chop_suffix src_name ".atd" + else + src_name) ^ "_atd.d" + |> String.lowercase_ascii + in + let dst_path = dst_name in + let full_module, _original_types = + Atd.Util.load_file + ~annot_schema + ~expand:true (* monomorphization = eliminate parametrized type defs *) + ~keep_builtins:true + ~inherit_fields:true + ~inherit_variants:true + src_path + in + let full_module = Atd.Ast.use_only_specific_variants full_module in + let (atd_head, atd_module) = full_module in + let head = + Dlang_annot.get_dlang_import (snd atd_head) + |> List.map (sprintf "import %s;") + in + to_file ~atd_filename:src_name ~head atd_module dst_path diff --git a/atdcpp/src/lib/Codegen.mli b/atdcpp/src/lib/Codegen.mli new file mode 100644 index 000000000..180fb5efb --- /dev/null +++ b/atdcpp/src/lib/Codegen.mli @@ -0,0 +1,7 @@ +(* + Dlang code generation for JSON support (no biniou support) +*) + +(** Take ATD type definitions and translate them to Dlang, writing + them out to a file which should have the '.d' extension. *) +val run_file : string -> unit diff --git a/atdcpp/src/lib/Dlang_annot.ml b/atdcpp/src/lib/Dlang_annot.ml new file mode 100644 index 000000000..2395d0a77 --- /dev/null +++ b/atdcpp/src/lib/Dlang_annot.ml @@ -0,0 +1,91 @@ +(* + ATD annotations to be interpreted specifically by atdcpp. + + atdcpp also honors json-related annotations defined in Atd.Json. +*) + +type assoc_repr = + | List + | Dict + +type atd_dlang_wrap = { + dlang_wrap_t : string; + dlang_wrap : string; + dlang_unwrap : string; +} + +let get_dlang_default an : string option = + Atd.Annot.get_opt_field + ~parse:(fun s -> Some s) + ~sections:["cpp"] + ~field:"default" + an + +let get_dlang_assoc_repr an : assoc_repr = + Atd.Annot.get_field + ~parse:(function + | "list" -> Some List + | "dict" -> Some Dict + | _ -> None + ) + ~default:List + ~sections:["cpp"] + ~field:"repr" + an + +(* imports etc. *) +let get_dlang_import an : string list = + Atd.Annot.get_fields + ~parse:(fun s -> Some s) + ~sections:["cpp"] + ~field:"import" + an + +let get_dlang_wrap loc an = + let path = ["cpp"] in + let module_ = + Atd.Annot.get_opt_field + ~parse:(fun s -> Some s) + ~sections:path + ~field:"module" + an + in + let open Printf in + let default field = + Option.map (fun s -> + sprintf "%s.%s" s field) module_ + in + let default_t field = + Option.map (fun s -> + sprintf "%s.%s" s field) module_ + in + let t = + Atd.Annot.get_field + ~parse:(fun s -> Some (Some s)) + ~default:(default_t "t") + ~sections:path + ~field:"t" + an + in + let wrap = + Atd.Annot.get_field + ~parse:(fun s -> Some (Some s)) + ~default:(default "wrap") + ~sections:path + ~field:"wrap" + an + in + let unwrap = + Atd.Annot.get_field + ~parse:(fun s -> Some (Some s)) + ~default:(default "unwrap") + ~sections:path + ~field:"unwrap" + an + in + match t, wrap, unwrap with + None, None, None -> None + | Some t, Some wrap, Some unwrap -> + Some { dlang_wrap_t = t; dlang_wrap = wrap; dlang_unwrap = unwrap } + | _ -> + Atd.Ast.error_at loc "Incomplete annotation. Missing t, wrap or unwrap" diff --git a/atdcpp/src/lib/Dlang_annot.mli b/atdcpp/src/lib/Dlang_annot.mli new file mode 100644 index 000000000..55e8c8cd7 --- /dev/null +++ b/atdcpp/src/lib/Dlang_annot.mli @@ -0,0 +1,42 @@ +(** + Dlang-specific ATD annotations. + + This interface serves as a reference of which Dlang-specific + ATD annotations are supported. atdcpp also honors JSON-related annotations + defined in [Atd.Json]. +*) + +(** Extract ["42"] from []. + The provided default must be a well-formed Dlang immutable expression. +*) +val get_dlang_default : Atd.Annot.t -> string option + +(** Whether an association list of ATD type [(string * foo) list] + must be represented in Dlang as a list of pairs or as a dictionary. + This is independent of the JSON representation. +*) +type assoc_repr = + | List + | Dict + +(** Inspect annotations placed on lists of pairs such as + [(string * foo) list ]. + Permissible values for the [repr] field are ["dict"] and ["list"]. + The default is ["list"]. +*) +val get_dlang_assoc_repr : Atd.Annot.t -> assoc_repr + +(** Returns text the user wants to be inserted at the beginning of the + Dlang file such as imports. *) +val get_dlang_import : Atd.Annot.t -> string list + + + +type atd_dlang_wrap = { + dlang_wrap_t : string; + dlang_wrap : string; + dlang_unwrap : string; +} + +val get_dlang_wrap : Atd.Ast.loc -> + Atd.Annot.t -> atd_dlang_wrap option diff --git a/atdcpp/src/lib/Indent.ml b/atdcpp/src/lib/Indent.ml new file mode 100644 index 000000000..f07cce716 --- /dev/null +++ b/atdcpp/src/lib/Indent.ml @@ -0,0 +1,51 @@ +(* + Simple indentation utility for code generators + + Something similar is found in atdgen/src but this API is simpler. +*) + +type node = + | Line of string + | Block of node list + | Inline of node list + +type t = node list + +let rec is_empty_node = function + | Line "" -> true + | Line _ -> false + | Block xs -> List.for_all is_empty_node xs + | Inline xs -> List.for_all is_empty_node xs + +let to_buffer ?(offset = 0) ?(indent = 2) buf l = + let rec print n = function + | Block l -> List.iter (print (n + indent)) l + | Inline l -> List.iter (print n) l + | Line "" -> Buffer.add_char buf '\n' + | Line s -> + for _ = 1 to n do + Buffer.add_char buf ' ' + done; + Buffer.add_string buf s; + Buffer.add_char buf '\n'; + in + List.iter (print offset) l + +let to_string ?offset ?indent l = + let buf = Buffer.create 1000 in + to_buffer ?offset ?indent buf l; + Buffer.contents buf + +let to_channel ?offset ?indent oc l = + let buf = Buffer.create 1000 in + to_buffer ?offset ?indent buf l; + Buffer.output_buffer oc buf + +let to_stdout ?offset ?indent l = + to_channel ?offset ?indent stdout l + +let to_file ?indent path l = + let oc = open_out path in + Fun.protect + ~finally:(fun () -> close_out_noerr oc) + (fun () -> to_channel ?indent oc l) diff --git a/atdcpp/src/lib/Indent.mli b/atdcpp/src/lib/Indent.mli new file mode 100644 index 000000000..ff0ed76d7 --- /dev/null +++ b/atdcpp/src/lib/Indent.mli @@ -0,0 +1,33 @@ +(** Simple indentation utility for code generators *) + +type node = + | Line of string (** single line (not indented) **) + | Block of node list (** indented sequence **) + | Inline of node list (** in-line sequence (not indented) **) + +type t = node list + +val is_empty_node : node -> bool + +val to_buffer : ?offset:int -> ?indent:int -> Buffer.t -> t -> unit + (** Write to a buffer. + + @param offset defines the number of space characters + to use for the left margin. Default: 0. + + @param indent defines the number of space characters to use for + indenting blocks. Default: 2. + *) + +val to_string : ?offset:int -> ?indent:int -> t -> string + (** Write to a string. See [to_buffer] for the options. *) + +val to_channel : ?offset:int -> ?indent:int -> out_channel -> t -> unit + (** Write to a channel. See [to_buffer] for the options. *) + +val to_stdout : ?offset:int -> ?indent:int -> t -> unit + (** Write to [stdout]. See [to_buffer] for the options. *) + +val to_file : ?indent:int -> string -> t -> unit + (** Write to a file, overwriting it if it was already there. + See [to_buffer] for the options. *) diff --git a/atdcpp/src/lib/Version.ml b/atdcpp/src/lib/Version.ml new file mode 100644 index 000000000..4cfa85ac8 --- /dev/null +++ b/atdcpp/src/lib/Version.ml @@ -0,0 +1,3 @@ +(* The version string is replaced by the actual version at release time + by 'dune release'. *) +let version = "%%VERSION%%" diff --git a/atdcpp/src/lib/dune b/atdcpp/src/lib/dune new file mode 100644 index 000000000..75e003e2b --- /dev/null +++ b/atdcpp/src/lib/dune @@ -0,0 +1,7 @@ +(library + (name atdcpp) + (libraries + re + atd + ) +) diff --git a/atdcpp/test/.gitignore b/atdcpp/test/.gitignore new file mode 100644 index 000000000..dfb825d7d --- /dev/null +++ b/atdcpp/test/.gitignore @@ -0,0 +1,2 @@ +!cpp-expected/ +cpp-tests/*.d diff --git a/atdcpp/test/atd-input/everything.atd b/atdcpp/test/atd-input/everything.atd new file mode 100644 index 000000000..6ea2ca857 --- /dev/null +++ b/atdcpp/test/atd-input/everything.atd @@ -0,0 +1,86 @@ + + +type kind = [ + | Root (* class name conflict *) + | Thing of int + | WOW + | Amaze of string list +] + +type frozen = [ + | A + | B of int +] + +type ('a, 'b) parametrized_record = { + field_a: 'a; + ~field_b: 'b list; +} + +type 'a parametrized_tuple = ('a * 'a * int) + +type root = { + id : string; + await: bool; + integer: int; + __init__ : float; + ~float_with_auto_default: float; + ~float_with_default : float; + items: int list list; + ?maybe: int option; + ~extras: int list; + ~answer : int; + aliased: alias; + point: (float * float); + kind: kind; + kinds: kind list; + assoc1: (float * int) list; + assoc2: (string * int) list ; + assoc3: (float * int) list ; + assoc4: (string * int) list ; + nullables: int nullable list; + options: int option list; + untyped_things: abstract list; + parametrized_record: (int, float) parametrized_record; + parametrized_tuple: kind parametrized_tuple; + wrapped: st wrap ; + aaa: alias_of_alias_of_alias; +} + +type st = int +type alias = int list +type alias2 = int list +type alias3 = int wrap +type alias_of_alias = alias3 wrap +type alias_of_alias_not_Wrapped = alias3 +type alias_of_alias_of_alias = alias_of_alias_not_Wrapped + +type password = int wrap + +type credential = { + name: string; + password: password; +} + +type credentials = credential list + + +type pair = (string * int) + +type require_field = { + req: string; +} + +type recursive_class = { + id: int; + flag: bool; + children: recursive_class list; +} + +type default_list = { + ~items: int list; +} + +type record_with_wrapped_type = { + item: string wrap ; +} diff --git a/atdcpp/test/cpp-expected/everything_atd.d b/atdcpp/test/cpp-expected/everything_atd.d new file mode 100644 index 000000000..97ea1d952 --- /dev/null +++ b/atdcpp/test/cpp-expected/everything_atd.d @@ -0,0 +1,786 @@ + +// Generated by atdcpp from type definitions in everything.atd. +// This implements classes for the types defined in 'everything.atd', providing +// methods and functions to convert data from/to JSON. + +// ############################################################################ +// # Private functions +// ############################################################################ + +module everything_atd; + +import std.algorithm : map; +import std.array : array; +import std.conv; +import std.format; +import std.json; +import std.sumtype; +import std.traits : isCallable, ReturnType; +import std.typecons : nullable, Nullable, tuple, Tuple; + +private +{ + + class AtdException : Exception + { + @safe this(string msg, string file = __FILE__, size_t line = __LINE__) + { + super(msg, file, line); + } + } + + // workaround to make toDelegate callable from safe + @trusted auto toDelegate(F)(auto ref F fp) if (isCallable!F) + { + import std.functional; + return std.functional.toDelegate(fp); + } + + + @trusted T _atd_missing_json_field(T)(string typeName, string jsonFieldName) + { + throw new AtdException("missing field %s in JSON object of type %s".format(jsonFieldName, typeName)); + } + + auto _atd_bad_json(T)(string expectedType, T jsonValue) + { + string valueStr = jsonValue.to!string; + if (valueStr.length > 200) + { + valueStr = valueStr[0 .. 200]; + } + + return new AtdException( + "incompatible JSON value where type '%s' was expected: %s".format( + expectedType, valueStr + )); + } + + auto _atd_bad_d(T)(string expectedType, T jsonValue) + { + string valueStr = jsonValue.to!string; + if (valueStr.length > 200) + { + valueStr = valueStr[0 .. 200]; + } + + return new AtdException( + "incompatible D value where type '%s' was expected: %s".format( + expectedType, valueStr + )); + } + + auto _atd_read_unit(JSONValue x) + { + if (x.isNull) + return null; + else + throw _atd_bad_json("unit", x); + } + + auto _atd_read_bool(JSONValue x) + { + try + return x.boolean; + catch (JSONException e) + throw _atd_bad_json("bool", x); + } + + auto _atd_read_int(JSONValue x) + { + try + return cast(int) x.integer; + catch (JSONException e) + throw _atd_bad_json("int", x); + } + + auto _atd_read_float(JSONValue x) + { + try + return cast(float) x.floating; + catch (JSONException e) + throw _atd_bad_json("float", x); + } + + auto _atd_read_string(JSONValue x) + { + try + return x.str; + catch (JSONException e) + throw _atd_bad_json("string", x); + } + + template _atd_read_list(alias readElements) + { + auto _atd_read_list(JSONValue jsonVal) + { + if (jsonVal.type != JSONType.array) + throw _atd_bad_json("array", jsonVal); + auto list = jsonVal.array; + return array(list.map!readElements()); + } + } + + template _atd_read_object_to_assoc_array(alias readValue) + { + auto _atd_read_object_to_assoc_array(JSONValue jsonVal) + { + alias T = ReturnType!readValue; + + if (jsonVal.type != JSONType.object) + throw _atd_bad_json("object", jsonVal); + T[string] ret; + foreach (key, val; jsonVal.object) + ret[key] = readValue(val); + return ret; + } + } + + template _atd_read_array_to_assoc_dict(alias readKey, alias readValue) + { + auto _atd_read_array_to_assoc_dict(JSONValue jsonVal) + { + alias K = ReturnType!readKey; + alias V = ReturnType!readValue; + + if (jsonVal.type != JSONType.array) + throw _atd_bad_json("list", jsonVal); + V[K] ret; + foreach (jsonInnerVal; jsonVal.array) + { + if (jsonInnerVal.type != JSONType.array) + throw _atd_bad_json("list", jsonInnerVal); + ret[readKey(jsonInnerVal[0])] = readValue(jsonInnerVal[1]); + } + return ret; + } + } + + template _atd_read_object_to_tuple_list(alias readValue) + { + auto _atd_read_object_to_tuple_list(JSONValue jsonVal) + { + alias T = ReturnType!readValue; + + if (jsonVal.type != JSONType.object) + throw _atd_bad_json("object", jsonVal); + auto tupList = new Tuple!(string, T)[](jsonVal.object.length); + int i = 0; + foreach (key, val; jsonVal.object) + tupList[i++] = tuple(key, readValue(val)); + return tupList; + } + } + + template _atd_read_nullable(alias readElm) + { + auto _atd_read_nullable(JSONValue e) + { + alias T = ReturnType!readElm; + + if (e.isNull) + return Nullable!T.init; + else + return Nullable!T(readElm(e)); + } + } + + template _atd_read_option(alias readElm) + { + auto _atd_read_option(JSONValue e) + { + alias T = ReturnType!readElm; + + if (e.type == JSONType.string && e.str == "None") + return Nullable!T.init; + else if (e.type == JSONType.array && e.array.length == 2 && e[0].type == JSONType.string && e[0].str == "Some") + return Nullable!T(readElm(e[1])); + else + throw _atd_bad_json("option", e); + } + } + + template _atd_read_wrap(alias readElm, alias wrap) + { + auto _atd_read_wrap(JSONValue e) + { + return wrap(readElm(e)); + } + } + + // this whole set of function could be remplaced by one templated _atd_write_value function + // not sure it is what we want though + + auto _atd_write_unit(typeof(null) n) + { + return JSONValue(null); + } + + auto _atd_write_bool(bool b) + { + return JSONValue(b); + } + + auto _atd_write_int(int i) + { + return JSONValue(i); + } + + auto _atd_write_float(float f) + { + return JSONValue(f); + } + + auto _atd_write_string(string s) + { + return JSONValue(s); + } + + template _atd_write_list(alias writeElm) + { + auto _atd_write_list(T)(T[] list) + { + return JSONValue(array(list.map!writeElm())); + } + } + + template _atd_write_assoc_array_to_object(alias writeValue) + { + auto _atd_write_assoc_array_to_object(T)(T[string] assocArr) + { + JSONValue[string] ret; + foreach (key, val; assocArr) + ret[key] = writeValue(val); + return JSONValue(ret); + } + } + + template _atd_write_assoc_dict_to_array(alias writeKey, alias writeValue) + { + auto _atd_write_assoc_dict_to_array(K, V)(V[K] assocArr) + { + JSONValue[] ret; + foreach (key, val; assocArr) + ret ~= JSONValue([writeKey(key), writeValue(val)]); + return JSONValue(ret); + } + } + + template _atd_write_tuple_list_to_object(alias writeValue) + { + auto _atd_write_tuple_list_to_object(T)(Tuple!(string, T)[] tupList) + { + JSONValue[string] ret; + foreach (tup; tupList) + ret[tup[0]] = writeValue(tup[1]); + return JSONValue(ret); + } + } + + template _atd_write_nullable(alias writeElm) + { + auto _atd_write_nullable(T)(Nullable!T elm) + { + if (elm.isNull) + return JSONValue(null); + else + return writeElm(elm.get); + } + } + + template _atd_write_option(alias writeElm) + { + auto _atd_write_option(T)(Nullable!T elm) + { + if (elm.isNull) + return JSONValue("None"); + else + return JSONValue([JSONValue("Some"), writeElm(elm.get)]); + } + } + + template _atd_write_wrap(alias writeElm, alias unwrap) + { + auto _atd_write_wrap(Wrapped)(Wrapped e) + { + return writeElm(unwrap(e)); + } + } +} + +// ############################################################################ +// # Public classes +// ############################################################################ + +auto fromJsonString(T)(string s) +{ + JSONValue res = parseJSON(s); + return res.fromJson!T; +} + +auto toJsonString(T)(T obj) +{ + JSONValue res = obj.toJson!T; + return res.toString; +} + + + + +import std.stdint : uint32_t, uint16_t; + + +struct RecursiveClass { + int id; + bool flag; + RecursiveClass[] children; +} + +@trusted RecursiveClass fromJson(T : RecursiveClass)(JSONValue x) { + RecursiveClass obj; + obj.id = ("id" in x) ? _atd_read_int(x["id"]) : _atd_missing_json_field!(typeof(obj.id))("RecursiveClass", "id"); + obj.flag = ("flag" in x) ? _atd_read_bool(x["flag"]) : _atd_missing_json_field!(typeof(obj.flag))("RecursiveClass", "flag"); + obj.children = ("children" in x) ? _atd_read_list!(fromJson!RecursiveClass)(x["children"]) : _atd_missing_json_field!(typeof(obj.children))("RecursiveClass", "children"); + return obj; +} +@trusted JSONValue toJson(T : RecursiveClass)(T obj) { + JSONValue res; + res["id"] = _atd_write_int(obj.id); + res["flag"] = _atd_write_bool(obj.flag); + res["children"] = _atd_write_list!(((RecursiveClass x) => x.toJson!(RecursiveClass)))(obj.children); + return res; +} + + +struct St{int _data; alias _data this; +this(int init) @safe {_data = init;} +this(St init) @safe {_data = init._data;} + +} +@trusted JSONValue toJson(T : St)(St e) { + return _atd_write_int(e); +} +@trusted St fromJson(T : St)(JSONValue e) { + return St(_atd_read_int(e)); +} + + +// Original type: kind = [ ... | Root | ... ] +struct Root_ {} +@trusted JSONValue toJson(T : Root_)(T e) { + return JSONValue("Root"); +} + + +// Original type: kind = [ ... | Thing of ... | ... ] +struct Thing { int value; } +@trusted JSONValue toJson(T : Thing)(T e) { + return JSONValue([JSONValue("Thing"), _atd_write_int(e.value)]); +} + + +// Original type: kind = [ ... | WOW | ... ] +struct WOW {} +@trusted JSONValue toJson(T : WOW)(T e) { + return JSONValue("wow"); +} + + +// Original type: kind = [ ... | Amaze of ... | ... ] +struct Amaze { string[] value; } +@trusted JSONValue toJson(T : Amaze)(T e) { + return JSONValue([JSONValue("!!!"), _atd_write_list!(_atd_write_string)(e.value)]); +} + + +struct Kind{ SumType!(Root_, Thing, WOW, Amaze) _data; alias _data this; +@safe this(T)(T init) {_data = init;} @safe this(Kind init) {_data = init._data;}} + +@trusted Kind fromJson(T : Kind)(JSONValue x) { + if (x.type == JSONType.string) { + if (x.str == "Root") + return Kind(Root_()); + if (x.str == "wow") + return Kind(WOW()); + throw _atd_bad_json("Kind", x); + } + if (x.type == JSONType.array && x.array.length == 2 && x[0].type == JSONType.string) { + string cons = x[0].str; + if (cons == "Thing") + return Kind(Thing(_atd_read_int(x[1]))); + if (cons == "!!!") + return Kind(Amaze(_atd_read_list!(_atd_read_string)(x[1]))); + throw _atd_bad_json("Kind", x); + } + throw _atd_bad_json("Kind", x); +} + +@trusted JSONValue toJson(T : Kind)(T x) { + return x.match!( + (Root_ v) => v.toJson!(Root_), +(Thing v) => v.toJson!(Thing), +(WOW v) => v.toJson!(WOW), +(Amaze v) => v.toJson!(Amaze) + ); +} + + +struct Alias3{uint32_t _data; alias _data this; +this(uint32_t init) @safe {_data = init;} +this(Alias3 init) @safe {_data = init._data;} + +} +@trusted JSONValue toJson(T : Alias3)(Alias3 e) { + return _atd_write_wrap!(_atd_write_int, (uint32_t e) => to!int(e))(e); +} +@trusted Alias3 fromJson(T : Alias3)(JSONValue e) { + return Alias3(_atd_read_wrap!(_atd_read_int, (int e) => to!uint32_t(e))(e)); +} + + +struct AliasOfAliasNotWrapped{Alias3 _data; alias _data this; +this(Alias3 init) @safe {_data = init;} +this(AliasOfAliasNotWrapped init) @safe {_data = init._data;} + +} +@trusted JSONValue toJson(T : AliasOfAliasNotWrapped)(AliasOfAliasNotWrapped e) { + return ((Alias3 x) => x.toJson!(Alias3))(e); +} +@trusted AliasOfAliasNotWrapped fromJson(T : AliasOfAliasNotWrapped)(JSONValue e) { + return AliasOfAliasNotWrapped(fromJson!Alias3(e)); +} + + +struct AliasOfAliasOfAlias{AliasOfAliasNotWrapped _data; alias _data this; +this(AliasOfAliasNotWrapped init) @safe {_data = init;} +this(AliasOfAliasOfAlias init) @safe {_data = init._data;} + +} +@trusted JSONValue toJson(T : AliasOfAliasOfAlias)(AliasOfAliasOfAlias e) { + return ((AliasOfAliasNotWrapped x) => x.toJson!(AliasOfAliasNotWrapped))(e); +} +@trusted AliasOfAliasOfAlias fromJson(T : AliasOfAliasOfAlias)(JSONValue e) { + return AliasOfAliasOfAlias(fromJson!AliasOfAliasNotWrapped(e)); +} + + +struct Alias{int[] _data; alias _data this; +this(int[] init) @safe {_data = init;} +this(Alias init) @safe {_data = init._data;} + +} +@trusted JSONValue toJson(T : Alias)(Alias e) { + return _atd_write_list!(_atd_write_int)(e); +} +@trusted Alias fromJson(T : Alias)(JSONValue e) { + return Alias(_atd_read_list!(_atd_read_int)(e)); +} + + +struct KindParametrizedTuple{Tuple!(Kind, Kind, int) _data; alias _data this; +this(Tuple!(Kind, Kind, int) init) @safe {_data = init;} +this(KindParametrizedTuple init) @safe {_data = init._data;} +this(T...)(T args) @safe {_data = tuple(args);} +} +@trusted JSONValue toJson(T : KindParametrizedTuple)(KindParametrizedTuple e) { + return ((Tuple!(Kind, Kind, int) x) => JSONValue([((Kind x) => x.toJson!(Kind))(x[0]), ((Kind x) => x.toJson!(Kind))(x[1]), _atd_write_int(x[2])]))(e); +} +@trusted KindParametrizedTuple fromJson(T : KindParametrizedTuple)(JSONValue e) { + return KindParametrizedTuple(((JSONValue x) @trusted { + if (x.type != JSONType.array || x.array.length != 3) + throw _atd_bad_json("Tuple of size 3", x); + return tuple(fromJson!Kind(x[0]), fromJson!Kind(x[1]), _atd_read_int(x[2])); + })(e)); +} + + +struct IntFloatParametrizedRecord { + int field_a; + float[] field_b = []; +} + +@trusted IntFloatParametrizedRecord fromJson(T : IntFloatParametrizedRecord)(JSONValue x) { + IntFloatParametrizedRecord obj; + obj.field_a = ("field_a" in x) ? _atd_read_int(x["field_a"]) : _atd_missing_json_field!(typeof(obj.field_a))("IntFloatParametrizedRecord", "field_a"); + obj.field_b = ("field_b" in x) ? _atd_read_list!(_atd_read_float)(x["field_b"]) : []; + return obj; +} +@trusted JSONValue toJson(T : IntFloatParametrizedRecord)(T obj) { + JSONValue res; + res["field_a"] = _atd_write_int(obj.field_a); + res["field_b"] = _atd_write_list!(_atd_write_float)(obj.field_b); + return res; +} + + +struct Root { + string id; + bool await; + int integer; + float x___init__; + float float_with_auto_default = 0.0; + float float_with_default = 0.1; + int[][] items; + Nullable!int maybe; + int[] extras = []; + int answer = 42; + Alias aliased; + Tuple!(float, float) point; + Kind kind; + Kind[] kinds; + Tuple!(float, int)[] assoc1; + Tuple!(string, int)[] assoc2; + int[float] assoc3; + int[string] assoc4; + Nullable!int[] nullables; + Nullable!int[] options; + JSONValue[] untyped_things; + IntFloatParametrizedRecord parametrized_record; + KindParametrizedTuple parametrized_tuple; + uint16_t wrapped; + AliasOfAliasOfAlias aaa; +} + +@trusted Root fromJson(T : Root)(JSONValue x) { + Root obj; + obj.id = ("ID" in x) ? _atd_read_string(x["ID"]) : _atd_missing_json_field!(typeof(obj.id))("Root", "ID"); + obj.await = ("await" in x) ? _atd_read_bool(x["await"]) : _atd_missing_json_field!(typeof(obj.await))("Root", "await"); + obj.integer = ("integer" in x) ? _atd_read_int(x["integer"]) : _atd_missing_json_field!(typeof(obj.integer))("Root", "integer"); + obj.x___init__ = ("__init__" in x) ? _atd_read_float(x["__init__"]) : _atd_missing_json_field!(typeof(obj.x___init__))("Root", "__init__"); + obj.float_with_auto_default = ("float_with_auto_default" in x) ? _atd_read_float(x["float_with_auto_default"]) : 0.0; + obj.float_with_default = ("float_with_default" in x) ? _atd_read_float(x["float_with_default"]) : 0.1; + obj.items = ("items" in x) ? _atd_read_list!(_atd_read_list!(_atd_read_int))(x["items"]) : _atd_missing_json_field!(typeof(obj.items))("Root", "items"); + obj.maybe = ("maybe" in x) ? _atd_read_option!(_atd_read_int)(x["maybe"]) : typeof(obj.maybe).init; + obj.extras = ("extras" in x) ? _atd_read_list!(_atd_read_int)(x["extras"]) : []; + obj.answer = ("answer" in x) ? _atd_read_int(x["answer"]) : 42; + obj.aliased = ("aliased" in x) ? fromJson!Alias(x["aliased"]) : _atd_missing_json_field!(typeof(obj.aliased))("Root", "aliased"); + obj.point = ("point" in x) ? ((JSONValue x) @trusted { + if (x.type != JSONType.array || x.array.length != 2) + throw _atd_bad_json("Tuple of size 2", x); + return tuple(_atd_read_float(x[0]), _atd_read_float(x[1])); + })(x["point"]) : _atd_missing_json_field!(typeof(obj.point))("Root", "point"); + obj.kind = ("kind" in x) ? fromJson!Kind(x["kind"]) : _atd_missing_json_field!(typeof(obj.kind))("Root", "kind"); + obj.kinds = ("kinds" in x) ? _atd_read_list!(fromJson!Kind)(x["kinds"]) : _atd_missing_json_field!(typeof(obj.kinds))("Root", "kinds"); + obj.assoc1 = ("assoc1" in x) ? _atd_read_list!(((JSONValue x) @trusted { + if (x.type != JSONType.array || x.array.length != 2) + throw _atd_bad_json("Tuple of size 2", x); + return tuple(_atd_read_float(x[0]), _atd_read_int(x[1])); + }))(x["assoc1"]) : _atd_missing_json_field!(typeof(obj.assoc1))("Root", "assoc1"); + obj.assoc2 = ("assoc2" in x) ? _atd_read_object_to_tuple_list!(_atd_read_int)(x["assoc2"]) : _atd_missing_json_field!(typeof(obj.assoc2))("Root", "assoc2"); + obj.assoc3 = ("assoc3" in x) ? _atd_read_array_to_assoc_dict!(_atd_read_float, _atd_read_int)(x["assoc3"]) : _atd_missing_json_field!(typeof(obj.assoc3))("Root", "assoc3"); + obj.assoc4 = ("assoc4" in x) ? _atd_read_object_to_assoc_array!(_atd_read_int)(x["assoc4"]) : _atd_missing_json_field!(typeof(obj.assoc4))("Root", "assoc4"); + obj.nullables = ("nullables" in x) ? _atd_read_list!(_atd_read_nullable!(_atd_read_int))(x["nullables"]) : _atd_missing_json_field!(typeof(obj.nullables))("Root", "nullables"); + obj.options = ("options" in x) ? _atd_read_list!(_atd_read_option!(_atd_read_int))(x["options"]) : _atd_missing_json_field!(typeof(obj.options))("Root", "options"); + obj.untyped_things = ("untyped_things" in x) ? _atd_read_list!(((JSONValue x) => x))(x["untyped_things"]) : _atd_missing_json_field!(typeof(obj.untyped_things))("Root", "untyped_things"); + obj.parametrized_record = ("parametrized_record" in x) ? fromJson!IntFloatParametrizedRecord(x["parametrized_record"]) : _atd_missing_json_field!(typeof(obj.parametrized_record))("Root", "parametrized_record"); + obj.parametrized_tuple = ("parametrized_tuple" in x) ? fromJson!KindParametrizedTuple(x["parametrized_tuple"]) : _atd_missing_json_field!(typeof(obj.parametrized_tuple))("Root", "parametrized_tuple"); + obj.wrapped = ("wrapped" in x) ? _atd_read_wrap!(fromJson!St, (St e) => ((St st) => st.to!int.to!uint16_t)(e))(x["wrapped"]) : _atd_missing_json_field!(typeof(obj.wrapped))("Root", "wrapped"); + obj.aaa = ("aaa" in x) ? fromJson!AliasOfAliasOfAlias(x["aaa"]) : _atd_missing_json_field!(typeof(obj.aaa))("Root", "aaa"); + return obj; +} +@trusted JSONValue toJson(T : Root)(T obj) { + JSONValue res; + res["ID"] = _atd_write_string(obj.id); + res["await"] = _atd_write_bool(obj.await); + res["integer"] = _atd_write_int(obj.integer); + res["__init__"] = _atd_write_float(obj.x___init__); + res["float_with_auto_default"] = _atd_write_float(obj.float_with_auto_default); + res["float_with_default"] = _atd_write_float(obj.float_with_default); + res["items"] = _atd_write_list!(_atd_write_list!(_atd_write_int))(obj.items); + if (!obj.maybe.isNull) + res["maybe"] = _atd_write_option!(_atd_write_int)(obj.maybe); + res["extras"] = _atd_write_list!(_atd_write_int)(obj.extras); + res["answer"] = _atd_write_int(obj.answer); + res["aliased"] = ((Alias x) => x.toJson!(Alias))(obj.aliased); + res["point"] = ((Tuple!(float, float) x) => JSONValue([_atd_write_float(x[0]), _atd_write_float(x[1])]))(obj.point); + res["kind"] = ((Kind x) => x.toJson!(Kind))(obj.kind); + res["kinds"] = _atd_write_list!(((Kind x) => x.toJson!(Kind)))(obj.kinds); + res["assoc1"] = _atd_write_list!(((Tuple!(float, int) x) => JSONValue([_atd_write_float(x[0]), _atd_write_int(x[1])])))(obj.assoc1); + res["assoc2"] = _atd_write_tuple_list_to_object!(_atd_write_int)(obj.assoc2); + res["assoc3"] = _atd_write_assoc_dict_to_array!(_atd_write_float, _atd_write_int)(obj.assoc3); + res["assoc4"] = _atd_write_assoc_array_to_object!(_atd_write_int)(obj.assoc4); + res["nullables"] = _atd_write_list!(_atd_write_nullable!(_atd_write_int))(obj.nullables); + res["options"] = _atd_write_list!(_atd_write_option!(_atd_write_int))(obj.options); + res["untyped_things"] = _atd_write_list!((JSONValue x) => x)(obj.untyped_things); + res["parametrized_record"] = ((IntFloatParametrizedRecord x) => x.toJson!(IntFloatParametrizedRecord))(obj.parametrized_record); + res["parametrized_tuple"] = ((KindParametrizedTuple x) => x.toJson!(KindParametrizedTuple))(obj.parametrized_tuple); + res["wrapped"] = _atd_write_wrap!(((St x) => x.toJson!(St)), (uint16_t e) => ((uint16_t e) => St(e.to!int))(e))(obj.wrapped); + res["aaa"] = ((AliasOfAliasOfAlias x) => x.toJson!(AliasOfAliasOfAlias))(obj.aaa); + return res; +} + + +struct RequireField { + string req; +} + +@trusted RequireField fromJson(T : RequireField)(JSONValue x) { + RequireField obj; + obj.req = ("req" in x) ? _atd_read_string(x["req"]) : _atd_missing_json_field!(typeof(obj.req))("RequireField", "req"); + return obj; +} +@trusted JSONValue toJson(T : RequireField)(T obj) { + JSONValue res; + res["req"] = _atd_write_string(obj.req); + return res; +} + + +struct RecordWithWrappedType { + int item; +} + +@trusted RecordWithWrappedType fromJson(T : RecordWithWrappedType)(JSONValue x) { + RecordWithWrappedType obj; + obj.item = ("item" in x) ? _atd_read_wrap!(_atd_read_string, (string e) => to!int(e))(x["item"]) : _atd_missing_json_field!(typeof(obj.item))("RecordWithWrappedType", "item"); + return obj; +} +@trusted JSONValue toJson(T : RecordWithWrappedType)(T obj) { + JSONValue res; + res["item"] = _atd_write_wrap!(_atd_write_string, (int e) => to!string(e))(obj.item); + return res; +} + + +struct Password{uint32_t _data; alias _data this; +this(uint32_t init) @safe {_data = init;} +this(Password init) @safe {_data = init._data;} + +} +@trusted JSONValue toJson(T : Password)(Password e) { + return _atd_write_wrap!(_atd_write_int, (uint32_t e) => to!int(e))(e); +} +@trusted Password fromJson(T : Password)(JSONValue e) { + return Password(_atd_read_wrap!(_atd_read_int, (int e) => to!uint32_t(e))(e)); +} + + +struct Pair{Tuple!(string, int) _data; alias _data this; +this(Tuple!(string, int) init) @safe {_data = init;} +this(Pair init) @safe {_data = init._data;} +this(T...)(T args) @safe {_data = tuple(args);} +} +@trusted JSONValue toJson(T : Pair)(Pair e) { + return ((Tuple!(string, int) x) => JSONValue([_atd_write_string(x[0]), _atd_write_int(x[1])]))(e); +} +@trusted Pair fromJson(T : Pair)(JSONValue e) { + return Pair(((JSONValue x) @trusted { + if (x.type != JSONType.array || x.array.length != 2) + throw _atd_bad_json("Tuple of size 2", x); + return tuple(_atd_read_string(x[0]), _atd_read_int(x[1])); + })(e)); +} + + +// Original type: frozen = [ ... | A | ... ] +struct A {} +@trusted JSONValue toJson(T : A)(T e) { + return JSONValue("A"); +} + + +// Original type: frozen = [ ... | B of ... | ... ] +struct B { int value; } +@trusted JSONValue toJson(T : B)(T e) { + return JSONValue([JSONValue("B"), _atd_write_int(e.value)]); +} + + +struct Frozen{ SumType!(A, B) _data; alias _data this; +@safe this(T)(T init) {_data = init;} @safe this(Frozen init) {_data = init._data;}} + +@trusted Frozen fromJson(T : Frozen)(JSONValue x) { + if (x.type == JSONType.string) { + if (x.str == "A") + return Frozen(A()); + throw _atd_bad_json("Frozen", x); + } + if (x.type == JSONType.array && x.array.length == 2 && x[0].type == JSONType.string) { + string cons = x[0].str; + if (cons == "B") + return Frozen(B(_atd_read_int(x[1]))); + throw _atd_bad_json("Frozen", x); + } + throw _atd_bad_json("Frozen", x); +} + +@trusted JSONValue toJson(T : Frozen)(T x) { + return x.match!( + (A v) => v.toJson!(A), +(B v) => v.toJson!(B) + ); +} + + +struct DefaultList { + int[] items = []; +} + +@trusted DefaultList fromJson(T : DefaultList)(JSONValue x) { + DefaultList obj; + obj.items = ("items" in x) ? _atd_read_list!(_atd_read_int)(x["items"]) : []; + return obj; +} +@trusted JSONValue toJson(T : DefaultList)(T obj) { + JSONValue res; + res["items"] = _atd_write_list!(_atd_write_int)(obj.items); + return res; +} + + +struct Credential { + string name; + Password password; +} + +@trusted Credential fromJson(T : Credential)(JSONValue x) { + Credential obj; + obj.name = ("name" in x) ? _atd_read_string(x["name"]) : _atd_missing_json_field!(typeof(obj.name))("Credential", "name"); + obj.password = ("password" in x) ? fromJson!Password(x["password"]) : _atd_missing_json_field!(typeof(obj.password))("Credential", "password"); + return obj; +} +@trusted JSONValue toJson(T : Credential)(T obj) { + JSONValue res; + res["name"] = _atd_write_string(obj.name); + res["password"] = ((Password x) => x.toJson!(Password))(obj.password); + return res; +} + + +struct Credentials{Credential[] _data; alias _data this; +this(Credential[] init) @safe {_data = init;} +this(Credentials init) @safe {_data = init._data;} + +} +@trusted JSONValue toJson(T : Credentials)(Credentials e) { + return _atd_write_list!(((Credential x) => x.toJson!(Credential)))(e); +} +@trusted Credentials fromJson(T : Credentials)(JSONValue e) { + return Credentials(_atd_read_list!(fromJson!Credential)(e)); +} + + +struct AliasOfAlias{uint16_t _data; alias _data this; +this(uint16_t init) @safe {_data = init;} +this(AliasOfAlias init) @safe {_data = init._data;} + +} +@trusted JSONValue toJson(T : AliasOfAlias)(AliasOfAlias e) { + return _atd_write_wrap!(((Alias3 x) => x.toJson!(Alias3)), (uint16_t e) => to!uint32_t(e))(e); +} +@trusted AliasOfAlias fromJson(T : AliasOfAlias)(JSONValue e) { + return AliasOfAlias(_atd_read_wrap!(fromJson!Alias3, (Alias3 e) => to!uint16_t(e))(e)); +} + + +struct Alias2{int[] _data; alias _data this; +this(int[] init) @safe {_data = init;} +this(Alias2 init) @safe {_data = init._data;} + +} +@trusted JSONValue toJson(T : Alias2)(Alias2 e) { + return _atd_write_list!(_atd_write_int)(e); +} +@trusted Alias2 fromJson(T : Alias2)(JSONValue e) { + return Alias2(_atd_read_list!(_atd_read_int)(e)); +} diff --git a/atdcpp/test/cpp-tests/dune b/atdcpp/test/cpp-tests/dune new file mode 100644 index 000000000..a46149b60 --- /dev/null +++ b/atdcpp/test/cpp-tests/dune @@ -0,0 +1,26 @@ +; +; Convert ATD -> Dlang +; +(rule + (targets + everything_atd.d + ) + (deps + ../atd-input/everything.atd + ) + (action + (run %{bin:atdcpp} %{deps}))) + +; +; Compile and run the tests on the generated Dlang code. +; +(rule + (alias runtest) + (package atdcpp) + (deps + (glob_files *.d)) + (action + (progn + (bash "ldc2 %{deps} --of test") + (bash ./test) + ))) diff --git a/atdcpp/test/dune b/atdcpp/test/dune new file mode 100644 index 000000000..692301273 --- /dev/null +++ b/atdcpp/test/dune @@ -0,0 +1,17 @@ +; +; We test in two phases: +; +; 1. Check that the generated Dlang code is what we expect. +; + +(rule + (alias runtest) + (package atdcpp) + (action + (diff cpp-expected/everything_atd.d + cpp-tests/everything_atd.d))) + +; 2. Run the generated Dlang code and check that is reads or writes JSON +; data as expected. +; +; See cpp-tests/dune diff --git a/dune b/dune index 49ce9dd08..1c9b39d17 100644 --- a/dune +++ b/dune @@ -12,3 +12,4 @@ (rule (copy atd.opam.template atds.opam.template)) (rule (copy atd.opam.template atdts.opam.template)) (rule (copy atd.opam.template atdd.opam.template)) +(rule (copy atd.opam.template atdcpp.opam.template)) diff --git a/dune-project b/dune-project index 665c5c0a3..6b6c5356d 100644 --- a/dune-project +++ b/dune-project @@ -210,3 +210,14 @@ Melange backend") (cmdliner (>= 1.1.0)) re (odoc :with-doc))) + +(package + (name atdcpp) + (synopsis "C++ code generation for ATD APIs") + (description "C++ code generation for ATD APIs") + (depends + (ocaml (>= 4.08)) + (atd (>= 2.11.0)) + (cmdliner (>= 1.1.0)) + re + (odoc :with-doc))) diff --git a/scripts/install-opam-dependencies b/scripts/install-opam-dependencies index fc6893944..6195d7d95 100755 --- a/scripts/install-opam-dependencies +++ b/scripts/install-opam-dependencies @@ -12,7 +12,7 @@ mkdir -p "$workspace" # Remove the dependencies on packages provided by this very project. for x in *.opam; do grep -v \ - '"atd"\|"atdgen"\|"atdgen-codec-runtime"\|"atdgen-runtime"\|"atdj"\|"atdpy"\|"atds"\|"atdts"\|"atdd"' "$x" \ + '"atd"\|"atdgen"\|"atdgen-codec-runtime"\|"atdgen-runtime"\|"atdj"\|"atdpy"\|"atds"\|"atdts"\|"atdd"\|"atdcpp"' "$x" \ > "$workspace"/"$x" done From c275fb7649cb1f0379d8b1342455da1742e7a9f8 Mon Sep 17 00:00:00 2001 From: Alexandre Bourquelot Date: Fri, 2 Feb 2024 17:23:38 +0800 Subject: [PATCH 02/47] atdcpp: iterate on serializer generation --- atdcpp/everything_atd.cpp | 158 ++++++++++++++++++++++++++++ atdcpp/everything_atd.hpp | 128 ++++++++++++++++++++++ atdcpp/src/lib/Codegen.ml | 2 +- atdcpp/test/cpp-tests/test_atdd.cpp | 111 +++++++++++++++++++ 4 files changed, 398 insertions(+), 1 deletion(-) create mode 100644 atdcpp/everything_atd.cpp create mode 100644 atdcpp/everything_atd.hpp create mode 100644 atdcpp/test/cpp-tests/test_atdd.cpp diff --git a/atdcpp/everything_atd.cpp b/atdcpp/everything_atd.cpp new file mode 100644 index 000000000..3a59aee12 --- /dev/null +++ b/atdcpp/everything_atd.cpp @@ -0,0 +1,158 @@ +#include "everything_atd.hpp" + +#include +#include +#include +#include // Only for rapidjson::ParseResult +#include + +IntFloatParametrizedRecord IntFloatParametrizedRecord::from_json(const rapidjson::Document &doc) +{ + IntFloatParametrizedRecord record; + if (doc.IsObject()) + { + const rapidjson::Value &fieldA = doc["field_a"]; + + record.field_a = _atd_read_int(doc["field_a"]); + record.field_b = _atd_read_array(doc["field_b"]); + } + + return record; +} + +IntFloatParametrizedRecord IntFloatParametrizedRecord::from_json_string(const std::string &json) +{ + rapidjson::Document doc; + doc.Parse(json.c_str()); + + if (doc.HasParseError()) + { + throw AtdException("Error parsing JSON: " + std::string(rapidjson::GetParseError_En(doc.GetParseError()))); + } + + return from_json(doc); +} + + +std::string IntFloatParametrizedRecord::to_json_string() const +{ + rapidjson::StringBuffer buffer; + rapidjson::Writer writer(buffer); + + writer.StartObject(); + + writer.Key("field_a"); + _atd_write_int(field_a, writer); + writer.Key("field_b"); + _atd_write_array(field_b, writer); + + writer.EndObject(); + + return buffer.GetString(); +} + + +namespace +{ + void write(std::vector obj, rapidjson::Writer &writer) + { + _atd_write_array(obj, writer); + } +} + +std::string NestedNestedIntListRecord::to_json_string() const +{ + rapidjson::StringBuffer buffer; + rapidjson::Writer writer(buffer); + + writer.StartObject(); + + writer.Key("field_a"); + + _atd_write_array, write>(field_a, writer); + + writer.EndObject(); + + return buffer.GetString(); +} + +namespace __NNNIntListRecord +{ + typedef std::vector> T; + + void write_0(std::vector obj, rapidjson::Writer &writer) + { + _atd_write_array(obj, writer); + } + + void write(T obj, rapidjson::Writer &writer) + { + _atd_write_array, write_0>(obj, writer); + } + + std::vector read_0(const rapidjson::Value &val) + { + return _atd_read_array(val); + } + + T read(const rapidjson::Value &val) + { + return _atd_read_array, read_0>(val); + } +} + +std::string NNNIntListRecord::to_json_string() const +{ + rapidjson::StringBuffer buffer; + rapidjson::Writer writer(buffer); + + writer.StartObject(); + + writer.Key("field_a"); + + _atd_write_array>, __NNNIntListRecord::write>(field_a, writer); + + writer.EndObject(); + + return buffer.GetString(); +} + + + +NNNIntListRecord NNNIntListRecord::from_json(const rapidjson::Document &doc) +{ + NNNIntListRecord record; + if (doc.IsObject()) + { + const rapidjson::Value &fieldA = doc["field_a"]; + + record.field_a = _atd_read_array>, __NNNIntListRecord::read>(doc["field_a"]); + } + + return record; +} + +int main() +{ + std::string json = R"({"field_a":1134,"field_b":[1.1,2.2,7]})"; + + try + { + IntFloatParametrizedRecord recordFromJson = IntFloatParametrizedRecord::from_json_string(json); + std::cout << "IntFloatParametrizedRecord: " << IntFloatParametrizedRecord{123, {1.1, 2.2, 3.3}}.to_json_string() << std::endl; + + std::cout << "field_a: " << recordFromJson.field_a << std::endl; + std::cout << "field_b: "; + for (auto &val : recordFromJson.field_b) + { + std::cout << val << " "; + } + std::cout << std::endl; + } + catch(const std::exception& e) + { + std::cerr << e.what() << '\n'; + } + + return 0; +} \ No newline at end of file diff --git a/atdcpp/everything_atd.hpp b/atdcpp/everything_atd.hpp new file mode 100644 index 000000000..79504a50d --- /dev/null +++ b/atdcpp/everything_atd.hpp @@ -0,0 +1,128 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +class AtdException : public std::exception +{ +public: + AtdException(const std::string &message) : msg_(message) {} + + const char *what() const throw() override + { + return msg_.c_str(); + } + +private: + std::string msg_; +}; + +// Forward declaration for utility functions using snake case +template +T read_from_json(const rapidjson::Value &val); + +template +rapidjson::Value write_to_json(const T &value, rapidjson::Document::AllocatorType &allocator); + +// Reading an integer from JSON +int _atd_read_int(const rapidjson::Value &val) +{ + if (!val.IsInt()) + { + throw AtdException("Expected an integer"); + } + return val.GetInt(); +} + +// Reading a float from JSON +float _atd_read_float(const rapidjson::Value &val) +{ + if (val.IsInt()) + { + return static_cast(val.GetInt()); + } + else if (val.IsUint()) + { + return static_cast(val.GetUint()); + } + if (!val.IsFloat()) + { + throw AtdException("Expected a float"); + } + + return val.GetFloat(); +} + +template +std::vector _atd_read_array(const rapidjson::Value &val) +{ + if (!val.IsArray()) + { + throw std::runtime_error("Expected an array"); // Or your specific exception type + } + + std::vector result; + for (rapidjson::SizeType i = 0; i < val.Size(); i++) + { + result.push_back(read_func(val[i])); + } + + return result; +} + +void _atd_write_int(int value, rapidjson::Writer& writer) +{ + writer.Int(value); +} + +void _atd_write_float(float value, rapidjson::Writer& writer) +{ + writer.Double(value); +} + +template &)> +void _atd_write_array(const std::vector& values, rapidjson::Writer& writer) +{ + writer.StartArray(); + for (const auto& value : values) + { + write_func(value, writer); + } + writer.EndArray(); +} + + +struct IntFloatParametrizedRecord +{ + int field_a; + std::vector field_b; + + std::string to_json_string() const; + static IntFloatParametrizedRecord from_json(const rapidjson::Document &doc); + static IntFloatParametrizedRecord from_json_string(const std::string &json); +}; + +struct NestedNestedIntListRecord +{ + std::vector> field_a; + + std::string to_json_string() const; + static NestedNestedIntListRecord from_json(const rapidjson::Document &doc); + static NestedNestedIntListRecord from_json_string(const std::string &json); +}; + +struct NNNIntListRecord +{ + std::vector>> field_a; + + std::string to_json_string() const; + static NNNIntListRecord from_json(const rapidjson::Document &doc); + static NNNIntListRecord from_json_string(const std::string &json); +}; \ No newline at end of file diff --git a/atdcpp/src/lib/Codegen.ml b/atdcpp/src/lib/Codegen.ml index f65c041ef..0e691ffe9 100644 --- a/atdcpp/src/lib/Codegen.ml +++ b/atdcpp/src/lib/Codegen.ml @@ -1132,7 +1132,7 @@ let run_file src_path = (if Filename.check_suffix src_name ".atd" then Filename.chop_suffix src_name ".atd" else - src_name) ^ "_atd.d" + src_name) ^ "_atd.hpp" |> String.lowercase_ascii in let dst_path = dst_name in diff --git a/atdcpp/test/cpp-tests/test_atdd.cpp b/atdcpp/test/cpp-tests/test_atdd.cpp new file mode 100644 index 000000000..26096442b --- /dev/null +++ b/atdcpp/test/cpp-tests/test_atdd.cpp @@ -0,0 +1,111 @@ +#include +#include +#include +#include +#include +#include + +struct IntFloatParametrizedRecord { + int field_a; + std::vector field_b; + + IntFloatParametrizedRecord() = default; + + IntFloatParametrizedRecord(int a, const std::vector& b) + : field_a(a), field_b(b) {} + + std::string toJson() const { + // Convert record to JSON string + // Implementation omitted for brevity + return ""; + } + + bool operator==(const IntFloatParametrizedRecord& other) const { + return field_a == other.field_a && field_b == other.field_b; + } + + bool operator!=(const IntFloatParametrizedRecord& other) const { + return !(*this == other); + } +}; + +struct Pair { + Pair() = default; + std::string field_a; + int field_b; +}; + +template +T fromJson(const std::string& json) { + // Convert JSON string to object of type T + // Implementation omitted for brevity + return T(); +} + +template +std::string toJsonString(const T& obj) { + // Convert object to JSON string + // Implementation omitted for brevity + return ""; +} + +int main() { + std::map> tests; + + tests["simpleRecord"] = []() { + IntFloatParametrizedRecord record(32, {5.4, 3.3}); + + std::string json = record.toJson(); + IntFloatParametrizedRecord recordFromJson = fromJson(json); + + if (record == recordFromJson) { + std::cout << "Test passed: simpleRecord" << std::endl; + } else { + std::cout << "Test failed: simpleRecord" << std::endl; + } + }; + + tests["simpleRecordMissingInt"] = []() { + std::string json = "{\"field_b\":[5.40000009536743164,3.29999995231628418]}"; + try { + fromJson(json); + std::cout << "Test failed: simpleRecordMissingInt" << std::endl; + } catch (const std::exception& e) { + std::cout << "Test passed: simpleRecordMissingInt" << std::endl; + } + }; + + tests["validPair"] = []() { + std::string str = "[\"hello\",2]"; + Pair p = fromJson(str); + + if (str == toJsonString(p)) { + std::cout << "Test passed: validPair" << std::endl; + } else { + std::cout << "Test failed: validPair" << std::endl; + } + }; + + // Add more test cases... + + std::cout << "Running tests..." << std::endl; + + int passed = 0; + for (const auto& test : tests) { + try { + test.second(); + passed++; + std::cout << "✅ Test " << test.first << std::endl; + } catch (const std::exception& e) { + std::cout << "❌ Test " << test.first << " with: " << e.what() << std::endl; + } + } + + if (passed == tests.size()) { + std::cout << "All tests passed" << std::endl; + } else { + std::cout << "Failure, " << passed << "/" << tests.size() << " tests passed" << std::endl; + } + + return 0; +} From 05a4975dcc90f4d7d3c100715687eba4a2e214b1 Mon Sep 17 00:00:00 2001 From: Alexandre Bourquelot Date: Fri, 2 Feb 2024 17:34:36 +0800 Subject: [PATCH 03/47] rename --- ...ything_atd.cpp => everything_atd_goal.cpp} | 86 +++++++++++-------- ...ything_atd.hpp => everything_atd_goal.hpp} | 0 2 files changed, 48 insertions(+), 38 deletions(-) rename atdcpp/{everything_atd.cpp => everything_atd_goal.cpp} (60%) rename atdcpp/{everything_atd.hpp => everything_atd_goal.hpp} (100%) diff --git a/atdcpp/everything_atd.cpp b/atdcpp/everything_atd_goal.cpp similarity index 60% rename from atdcpp/everything_atd.cpp rename to atdcpp/everything_atd_goal.cpp index 3a59aee12..e1f96f453 100644 --- a/atdcpp/everything_atd.cpp +++ b/atdcpp/everything_atd_goal.cpp @@ -12,7 +12,7 @@ IntFloatParametrizedRecord IntFloatParametrizedRecord::from_json(const rapidjson if (doc.IsObject()) { const rapidjson::Value &fieldA = doc["field_a"]; - + record.field_a = _atd_read_int(doc["field_a"]); record.field_b = _atd_read_array(doc["field_b"]); } @@ -33,7 +33,6 @@ IntFloatParametrizedRecord IntFloatParametrizedRecord::from_json_string(const st return from_json(doc); } - std::string IntFloatParametrizedRecord::to_json_string() const { rapidjson::StringBuffer buffer; @@ -51,7 +50,6 @@ std::string IntFloatParametrizedRecord::to_json_string() const return buffer.GetString(); } - namespace { void write(std::vector obj, rapidjson::Writer &writer) @@ -78,26 +76,29 @@ std::string NestedNestedIntListRecord::to_json_string() const namespace __NNNIntListRecord { - typedef std::vector> T; - - void write_0(std::vector obj, rapidjson::Writer &writer) + namespace field_0 { - _atd_write_array(obj, writer); - } + typedef std::vector> T; - void write(T obj, rapidjson::Writer &writer) - { - _atd_write_array, write_0>(obj, writer); - } + void write_0(std::vector obj, rapidjson::Writer &writer) + { + _atd_write_array(obj, writer); + } - std::vector read_0(const rapidjson::Value &val) - { - return _atd_read_array(val); - } + void write(T obj, rapidjson::Writer &writer) + { + _atd_write_array, write_0>(obj, writer); + } - T read(const rapidjson::Value &val) - { - return _atd_read_array, read_0>(val); + std::vector read_0(const rapidjson::Value &val) + { + return _atd_read_array(val); + } + + T read(const rapidjson::Value &val) + { + return _atd_read_array, read_0>(val); + } } } @@ -110,49 +111,58 @@ std::string NNNIntListRecord::to_json_string() const writer.Key("field_a"); - _atd_write_array>, __NNNIntListRecord::write>(field_a, writer); + _atd_write_array>, __NNNIntListRecord::field_0::write>(field_a, writer); writer.EndObject(); return buffer.GetString(); } - - NNNIntListRecord NNNIntListRecord::from_json(const rapidjson::Document &doc) { NNNIntListRecord record; if (doc.IsObject()) { const rapidjson::Value &fieldA = doc["field_a"]; - - record.field_a = _atd_read_array>, __NNNIntListRecord::read>(doc["field_a"]); + + record.field_a = _atd_read_array>, __NNNIntListRecord::field_0::read>(doc["field_a"]); } return record; } -int main() +const rapidjson::Document doc_from_json(const std::string &json) { - std::string json = R"({"field_a":1134,"field_b":[1.1,2.2,7]})"; + rapidjson::Document doc; + doc.Parse(json.c_str()); - try + if (doc.HasParseError()) { - IntFloatParametrizedRecord recordFromJson = IntFloatParametrizedRecord::from_json_string(json); - std::cout << "IntFloatParametrizedRecord: " << IntFloatParametrizedRecord{123, {1.1, 2.2, 3.3}}.to_json_string() << std::endl; + throw AtdException("Error parsing JSON: " + std::string(rapidjson::GetParseError_En(doc.GetParseError()))); + } - std::cout << "field_a: " << recordFromJson.field_a << std::endl; - std::cout << "field_b: "; - for (auto &val : recordFromJson.field_b) + return doc; +} + +int main() +{ + std::string NNNIntListRecord_json = R"({"field_a":[[[1,2,3],[4,4,6]],[[7,8,9],[10,11,12]]]})"; + + NNNIntListRecord recordFromJson = NNNIntListRecord::from_json(doc_from_json(NNNIntListRecord_json)); + /// iterate through all vectors to print all values (3 level of nesting) + for (auto &vec1 : recordFromJson.field_a) + { + for (auto &vec2 : vec1) { - std::cout << val << " "; + for (auto &val : vec2) + { + std::cout << val << " "; + } + std::cout << std::endl; } - std::cout << std::endl; - } - catch(const std::exception& e) - { - std::cerr << e.what() << '\n'; } + std::cout << "NNNIntListRecord: " << NNNIntListRecord{{{{1, 2, 3}, {4, 5, 6}}, {{7, 8, 9}, {10, 18, 12}}}}.to_json_string() << std::endl; + return 0; } \ No newline at end of file diff --git a/atdcpp/everything_atd.hpp b/atdcpp/everything_atd_goal.hpp similarity index 100% rename from atdcpp/everything_atd.hpp rename to atdcpp/everything_atd_goal.hpp From 9e436c299a3dfcf9303e55339a23b9f6ca895f57 Mon Sep 17 00:00:00 2001 From: Alexandre Bourquelot Date: Fri, 2 Feb 2024 22:17:52 +0800 Subject: [PATCH 04/47] atdcpp: handle basic struct --- atdcpp/everything_atd_goal.cpp | 11 +- atdcpp/everything_atd_goal.hpp | 7 - atdcpp/src/lib/Codegen.ml | 454 ++++++++++----------------------- 3 files changed, 143 insertions(+), 329 deletions(-) diff --git a/atdcpp/everything_atd_goal.cpp b/atdcpp/everything_atd_goal.cpp index e1f96f453..9c8d5a76b 100644 --- a/atdcpp/everything_atd_goal.cpp +++ b/atdcpp/everything_atd_goal.cpp @@ -1,4 +1,4 @@ -#include "everything_atd.hpp" +#include "everything_atd_goal.hpp" #include #include @@ -102,6 +102,15 @@ namespace __NNNIntListRecord } } +typedef NNNIntListRecord TT; + +TT TT::from_json(const rapidjson::Document &doc) +{ + TT record; + + return record; +} + std::string NNNIntListRecord::to_json_string() const { rapidjson::StringBuffer buffer; diff --git a/atdcpp/everything_atd_goal.hpp b/atdcpp/everything_atd_goal.hpp index 79504a50d..e3239b741 100644 --- a/atdcpp/everything_atd_goal.hpp +++ b/atdcpp/everything_atd_goal.hpp @@ -24,13 +24,6 @@ class AtdException : public std::exception std::string msg_; }; -// Forward declaration for utility functions using snake case -template -T read_from_json(const rapidjson::Value &val); - -template -rapidjson::Value write_to_json(const T &value, rapidjson::Document::AllocatorType &allocator); - // Reading an integer from JSON int _atd_read_int(const rapidjson::Value &val) { diff --git a/atdcpp/src/lib/Codegen.ml b/atdcpp/src/lib/Codegen.ml index 0e691ffe9..c5dfa3d60 100644 --- a/atdcpp/src/lib/Codegen.ml +++ b/atdcpp/src/lib/Codegen.ml @@ -1,5 +1,5 @@ (* - Dlang code generation for JSON support (no biniou support) + cpp code generation for JSON support (no biniou support) Takes the contents of a .atd file and translates it to a .d file. Look into the tests to see what generated code looks like. @@ -38,7 +38,7 @@ let annot_schema_dlang : Atd.Annot.schema_section = let annot_schema : Atd.Annot.schema = annot_schema_dlang :: Atd.Json.annot_schema_json -(* Translate a preferred variable name into an available Dlang identifier. *) +(* Translate a preferred variable name into an available cpp identifier. *) let trans env id = env.translate_variable id @@ -190,327 +190,124 @@ let fixed_size_preamble atd_filename = // # Private functions // ############################################################################ -module %s; +// filename %s +#pragma once -import std.algorithm : map; -import std.array : array; -import std.conv; -import std.format; -import std.json; -import std.sumtype; -import std.traits : isCallable, ReturnType; -import std.typecons : nullable, Nullable, tuple, Tuple; +#include +#include +#include +#include +#include +#include +#include +#include +#include -private +class AtdException : public std::exception { +public: + AtdException(const std::string &message) : msg_(message) {} - class AtdException : Exception - { - @safe this(string msg, string file = __FILE__, size_t line = __LINE__) - { - super(msg, file, line); - } - } - - // workaround to make toDelegate callable from safe - @trusted auto toDelegate(F)(auto ref F fp) if (isCallable!F) - { - import std.functional; - return std.functional.toDelegate(fp); - } - - - @trusted T _atd_missing_json_field(T)(string typeName, string jsonFieldName) - { - throw new AtdException("missing field %%s in JSON object of type %%s".format(jsonFieldName, typeName)); - } - - auto _atd_bad_json(T)(string expectedType, T jsonValue) - { - string valueStr = jsonValue.to!string; - if (valueStr.length > 200) - { - valueStr = valueStr[0 .. 200]; - } - - return new AtdException( - "incompatible JSON value where type '%%s' was expected: %%s".format( - expectedType, valueStr - )); - } - - auto _atd_bad_d(T)(string expectedType, T jsonValue) - { - string valueStr = jsonValue.to!string; - if (valueStr.length > 200) - { - valueStr = valueStr[0 .. 200]; - } - - return new AtdException( - "incompatible D value where type '%%s' was expected: %%s".format( - expectedType, valueStr - )); - } - - auto _atd_read_unit(JSONValue x) - { - if (x.isNull) - return null; - else - throw _atd_bad_json("unit", x); - } - - auto _atd_read_bool(JSONValue x) - { - try - return x.boolean; - catch (JSONException e) - throw _atd_bad_json("bool", x); - } - - auto _atd_read_int(JSONValue x) - { - try - return cast(int) x.integer; - catch (JSONException e) - throw _atd_bad_json("int", x); - } - - auto _atd_read_float(JSONValue x) - { - try - return cast(float) x.floating; - catch (JSONException e) - throw _atd_bad_json("float", x); - } - - auto _atd_read_string(JSONValue x) - { - try - return x.str; - catch (JSONException e) - throw _atd_bad_json("string", x); - } - - template _atd_read_list(alias readElements) - { - auto _atd_read_list(JSONValue jsonVal) - { - if (jsonVal.type != JSONType.array) - throw _atd_bad_json("array", jsonVal); - auto list = jsonVal.array; - return array(list.map!readElements()); - } - } - - template _atd_read_object_to_assoc_array(alias readValue) - { - auto _atd_read_object_to_assoc_array(JSONValue jsonVal) - { - alias T = ReturnType!readValue; - - if (jsonVal.type != JSONType.object) - throw _atd_bad_json("object", jsonVal); - T[string] ret; - foreach (key, val; jsonVal.object) - ret[key] = readValue(val); - return ret; - } - } - - template _atd_read_array_to_assoc_dict(alias readKey, alias readValue) - { - auto _atd_read_array_to_assoc_dict(JSONValue jsonVal) - { - alias K = ReturnType!readKey; - alias V = ReturnType!readValue; - - if (jsonVal.type != JSONType.array) - throw _atd_bad_json("list", jsonVal); - V[K] ret; - foreach (jsonInnerVal; jsonVal.array) - { - if (jsonInnerVal.type != JSONType.array) - throw _atd_bad_json("list", jsonInnerVal); - ret[readKey(jsonInnerVal[0])] = readValue(jsonInnerVal[1]); - } - return ret; - } - } - - template _atd_read_object_to_tuple_list(alias readValue) - { - auto _atd_read_object_to_tuple_list(JSONValue jsonVal) - { - alias T = ReturnType!readValue; - - if (jsonVal.type != JSONType.object) - throw _atd_bad_json("object", jsonVal); - auto tupList = new Tuple!(string, T)[](jsonVal.object.length); - int i = 0; - foreach (key, val; jsonVal.object) - tupList[i++] = tuple(key, readValue(val)); - return tupList; - } - } - - template _atd_read_nullable(alias readElm) - { - auto _atd_read_nullable(JSONValue e) - { - alias T = ReturnType!readElm; - - if (e.isNull) - return Nullable!T.init; - else - return Nullable!T(readElm(e)); - } - } - - template _atd_read_option(alias readElm) - { - auto _atd_read_option(JSONValue e) - { - alias T = ReturnType!readElm; - - if (e.type == JSONType.string && e.str == "None") - return Nullable!T.init; - else if (e.type == JSONType.array && e.array.length == 2 && e[0].type == JSONType.string && e[0].str == "Some") - return Nullable!T(readElm(e[1])); - else - throw _atd_bad_json("option", e); - } - } - - template _atd_read_wrap(alias readElm, alias wrap) + const char *what() const throw() override { - auto _atd_read_wrap(JSONValue e) - { - return wrap(readElm(e)); - } + return msg_.c_str(); } - // this whole set of function could be remplaced by one templated _atd_write_value function - // not sure it is what we want though +private: + std::string msg_; +}; - auto _atd_write_unit(typeof(null) n) - { - return JSONValue(null); - } - - auto _atd_write_bool(bool b) - { - return JSONValue(b); - } +template +T _atd_missing_json_field(const std::string &type, const std::string &field) +{ + throw AtdException("Missing JSON field '" + field + "' in " + type); +} - auto _atd_write_int(int i) +// Reading an integer from JSON +int _atd_read_int(const rapidjson::Value &val) +{ + if (!val.IsInt()) { - return JSONValue(i); + throw AtdException("Expected an integer"); } + return val.GetInt(); +} - auto _atd_write_float(float f) +// Reading a float from JSON +float _atd_read_float(const rapidjson::Value &val) +{ + if (val.IsInt()) { - return JSONValue(f); + return static_cast(val.GetInt()); } - - auto _atd_write_string(string s) + else if (val.IsUint()) { - return JSONValue(s); + return static_cast(val.GetUint()); } - - template _atd_write_list(alias writeElm) + if (!val.IsFloat()) { - auto _atd_write_list(T)(T[] list) - { - return JSONValue(array(list.map!writeElm())); - } + throw AtdException("Expected a float"); } - template _atd_write_assoc_array_to_object(alias writeValue) - { - auto _atd_write_assoc_array_to_object(T)(T[string] assocArr) - { - JSONValue[string] ret; - foreach (key, val; assocArr) - ret[key] = writeValue(val); - return JSONValue(ret); - } - } + return val.GetFloat(); +} - template _atd_write_assoc_dict_to_array(alias writeKey, alias writeValue) +std::string _atd_read_string(const rapidjson::Value &val) +{ + if (!val.IsString()) { - auto _atd_write_assoc_dict_to_array(K, V)(V[K] assocArr) - { - JSONValue[] ret; - foreach (key, val; assocArr) - ret ~= JSONValue([writeKey(key), writeValue(val)]); - return JSONValue(ret); - } + throw AtdException("Expected a string"); } + return val.GetString(); +} - template _atd_write_tuple_list_to_object(alias writeValue) +template +std::vector _atd_read_array(const rapidjson::Value &val) +{ + if (!val.IsArray()) { - auto _atd_write_tuple_list_to_object(T)(Tuple!(string, T)[] tupList) - { - JSONValue[string] ret; - foreach (tup; tupList) - ret[tup[0]] = writeValue(tup[1]); - return JSONValue(ret); - } + throw std::runtime_error("Expected an array"); // Or your specific exception type } - template _atd_write_nullable(alias writeElm) + std::vector result; + for (rapidjson::SizeType i = 0; i < val.Size(); i++) { - auto _atd_write_nullable(T)(Nullable!T elm) - { - if (elm.isNull) - return JSONValue(null); - else - return writeElm(elm.get); - } + result.push_back(read_func(val[i])); } - template _atd_write_option(alias writeElm) - { - auto _atd_write_option(T)(Nullable!T elm) - { - if (elm.isNull) - return JSONValue("None"); - else - return JSONValue([JSONValue("Some"), writeElm(elm.get)]); - } - } + return result; +} - template _atd_write_wrap(alias writeElm, alias unwrap) - { - auto _atd_write_wrap(Wrapped)(Wrapped e) - { - return writeElm(unwrap(e)); - } - } +void _atd_write_int(int value, rapidjson::Writer& writer) +{ + writer.Int(value); } -// ############################################################################ -// # Public classes -// ############################################################################ +void _atd_write_float(float value, rapidjson::Writer& writer) +{ + writer.Double(value); +} -auto fromJsonString(T)(string s) +void _atd_write_string(const std::string &value, rapidjson::Writer& writer) { - JSONValue res = parseJSON(s); - return res.fromJson!T; + writer.String(value.c_str()); } -auto toJsonString(T)(T obj) +template &)> +void _atd_write_array(const std::vector& values, rapidjson::Writer& writer) { - JSONValue res = obj.toJson!T; - return res.toString; + writer.StartArray(); + for (const auto& value : values) + { + write_func(value, writer); + } + writer.EndArray(); } |} atd_filename atd_filename - (sprintf "%s_atd" (Filename.remove_extension atd_filename)) + atd_filename let not_implemented loc msg = @@ -529,7 +326,7 @@ let double_spaced blocks = spaced ~spacer:[Line ""; Line ""] blocks (* - Representations of ATD type '(string * value) list' in JSON and Dlang. + Representations of ATD type '(string * value) list' in JSON and cpp. Key type or value type are provided when it's useful. *) type assoc_kind = @@ -556,15 +353,15 @@ let assoc_kind loc (e : type_expr) an : assoc_kind = | _, Object, _ -> error_at loc "not a (string * _) list" | _, Array, _ -> error_at loc "not a (_ * _) list" -(* Map ATD built-in types to built-in Dlang types *) +(* Map ATD built-in types to built-in cpp types *) let dlang_type_name env (name : string) = match name with | "unit" -> "void" | "bool" -> "bool" | "int" -> "int" | "float" -> "float" - | "string" -> "string" - | "abstract" -> "JSONValue" + | "string" -> "std::string" + | "abstract" -> "rapidjson::Value" | user_defined -> let typename = (struct_name env user_defined) in typename @@ -583,18 +380,18 @@ let rec type_name_of_expr env (e : type_expr) : string = (match assoc_kind loc e an with | Array_list | Object_list _ -> - sprintf "%s[]" + sprintf "std::vector<%s>" (type_name_of_expr env e) | Array_dict (key, value) -> - sprintf "%s[%s]" + sprintf "std::map<%s, %s>" (type_name_of_expr env value) (type_name_of_expr env key) | Object_dict value -> - sprintf "%s[string]" + sprintf "std::map" (type_name_of_expr env value) ) - | Option (loc, e, an) -> sprintf "Nullable!%s" (type_name_of_expr env e) - | Nullable (loc, e, an) -> sprintf "Nullable!%s" (type_name_of_expr env e) + | Option (loc, e, an) -> sprintf "std::optional<%s>" (type_name_of_expr env e) + | Nullable (loc, e, an) -> sprintf "std::optional<%s>" (type_name_of_expr env e) | Shared (loc, e, an) -> not_implemented loc "shared" (* TODO *) | Wrap (loc, e, an) -> (match Dlang_annot.get_dlang_wrap loc an with @@ -610,7 +407,7 @@ let rec get_default_default (e : type_expr) : string option = | Sum _ | Record _ | Tuple _ (* a default tuple could be possible but we're lazy *) -> None - | List _ -> Some "[]" + | List _ -> Some "{}" | Option _ | Nullable _ -> None | Shared (loc, e, an) -> get_default_default e @@ -664,21 +461,21 @@ let rec json_writer ?(nested=false) env e = | List (loc, e, an) -> (match assoc_kind loc e an with | Array_list -> - sprintf "_atd_write_list!(%s)" (json_writer ~nested:true env e) + sprintf "_atd_write_list<%s>" (json_writer ~nested:true env e) | Array_dict (key, value) -> - sprintf "_atd_write_assoc_dict_to_array!(%s, %s)" + sprintf "_atd_write_assoc_dict_to_array<%s, %s>" (json_writer ~nested:true env key) (json_writer ~nested:true env value) | Object_dict value -> - sprintf "_atd_write_assoc_array_to_object!(%s)" + sprintf "_atd_write_assoc_array_to_object<%s>" (json_writer ~nested:true env value) | Object_list value -> - sprintf "_atd_write_tuple_list_to_object!(%s)" + sprintf "_atd_write_tuple_list_to_object<%s>" (json_writer ~nested:true env value) ) | Option (loc, e, an) -> - sprintf "_atd_write_option!(%s)"(json_writer ~nested:true env e) + sprintf "_atd_write_option<%s>"(json_writer ~nested:true env e) | Nullable (loc, e, an) -> - sprintf "_atd_write_nullable!(%s)" (json_writer ~nested:true env e) + sprintf "_atd_write_nullable<%s>" (json_writer ~nested:true env e) | Shared (loc, e, an) -> not_implemented loc "shared" | Wrap (loc, e, an) -> (match Dlang_annot.get_dlang_wrap loc an with @@ -712,10 +509,9 @@ let construct_json_field env trans_meth let writer_function = json_writer env unwrapped_type in let assignment = [ - Line (sprintf "res[\"%s\"] = %s(obj.%s);" - (Atd.Json.get_json_fname name an |> single_esc) - writer_function - (inst_var_name trans_meth name)) + Line (sprintf "writer.Key(\"%s\");" + (Atd.Json.get_json_fname name an |> single_esc)); + Line (sprintf "%s(%s, writer);" writer_function (inst_var_name trans_meth name)); ] in match kind with @@ -747,16 +543,16 @@ let rec json_reader ?(nested=false) env (e : type_expr) = The default is to use JSON arrays and Python lists. *) (match assoc_kind loc e an with | Array_list -> - sprintf "_atd_read_list!(%s)" + sprintf "_atd_read_list<%s>" (json_reader ~nested:true env e) | Array_dict (key, value) -> - sprintf "_atd_read_array_to_assoc_dict!(%s, %s)" + sprintf "_atd_read_array_to_assoc_dict<%s, %s>" (json_reader ~nested:true env key) (json_reader ~nested:true env value) | Object_dict value -> - sprintf "_atd_read_object_to_assoc_array!(%s)" + sprintf "_atd_read_object_to_assoc_array<%s>" (json_reader ~nested:true env value) | Object_list value -> - sprintf "_atd_read_object_to_tuple_list!(%s)" + sprintf "_atd_read_object_to_tuple_list<%s>" (json_reader ~nested:true env value) ) | Option (loc, e, an) -> @@ -800,25 +596,34 @@ let from_json_class_argument let else_body = match kind with | Required -> - sprintf "_atd_missing_json_field!(typeof(obj.%s))(\"%s\", \"%s\")" + sprintf "_atd_missing_json_field(\"%s\", \"%s\")" dlang_name (single_esc dlang_struct_name) (single_esc json_name) - | Optional -> (sprintf "typeof(obj.%s).init" dlang_name) + | Optional -> (sprintf "decltype(record.%s).init" dlang_name) | With_default -> match get_dlang_default e an with | Some x -> x | None -> A.error_at loc - (sprintf "missing default Dlang value for field '%s'" + (sprintf "missing default cpp value for field '%s'" name) in - sprintf "obj.%s = (\"%s\" in x) ? %s(x[\"%s\"]) : %s;" + Inline [ + Line (sprintf "if (doc.HasMember(\"%s\"))" (single_esc json_name)); + Block [ + Line (sprintf "record.%s = %s(doc[\"%s\"]);" + dlang_name + (json_reader env e) + (single_esc json_name)); + ]; + Line (sprintf "else record.%s = %s;" dlang_name else_body);] + (* sprintf "obj.%s = (\"%s\" in x) ? %s(x[\"%s\"]) : %s;" dlang_name (single_esc json_name) (json_reader env e) (single_esc json_name) - else_body + else_body *) let inst_var_declaration env trans_meth ((loc, (name, kind, an), e) : simple_field) = @@ -855,27 +660,33 @@ let record env loc name (fields : field list) an = Inline (construct_json_field env trans_meth x)) fields in let from_json_class_arguments = List.map (fun x -> - Line (from_json_class_argument env trans_meth dlang_struct_name x) + (from_json_class_argument env trans_meth dlang_struct_name x) ) fields in let from_json = [ - Line (sprintf "@trusted %s fromJson(T : %s)(JSONValue x) {" - (single_esc dlang_struct_name) (single_esc dlang_struct_name)); + Line (sprintf "static %s from_json(const rapidjson::Document & doc) {" + (single_esc dlang_struct_name)); Block [ - Line (sprintf "%s obj;" dlang_struct_name); + Line (sprintf "%s record;" dlang_struct_name); + Line "if (!doc.IsObject()) {"; + Block [Line "throw AtdException(\"Expected an object\");"]; + Line "}"; Inline from_json_class_arguments; - Line "return obj;"; + Line "return record;"; ]; Line "}"; ] in let to_json = [ - Line (sprintf "@trusted JSONValue toJson(T : %s)(T obj) {" (single_esc dlang_struct_name)); + Line (sprintf "std::string to_json_string() {"); Block [ - Line ("JSONValue res;"); + Line ("rapidjson::StringBuffer buffer;"); + Line ("rapidjson::Writer writer(buffer);"); + Line ("writer.StartObject();"); Inline json_object_body; - Line "return res;" + Line ("writer.EndObject();"); + Line "return buffer.GetString();" ]; Line "}"; ] @@ -884,11 +695,12 @@ let record env loc name (fields : field list) an = Line (sprintf "struct %s {" dlang_struct_name); Block (spaced [ Inline inst_var_declarations; + Inline from_json; + Inline to_json; ]); - Line ("}"); + Line ("};"); Line ""; - Inline from_json; - Inline to_json; + ] let alias_wrapper env name type_expr = From fb16edf19446d43bbc14bfce39b0293aab960a22 Mon Sep 17 00:00:00 2001 From: Alexandre Bourquelot Date: Fri, 2 Feb 2024 22:46:16 +0800 Subject: [PATCH 05/47] atdcpp: fix compilation --- atdcpp/src/lib/Codegen.ml | 10 ++++++---- atdcpp/test/atd-input/everything.atd | 18 +++++++++++------- atdcpp/test/cpp-tests/dune | 4 ++-- 3 files changed, 19 insertions(+), 13 deletions(-) diff --git a/atdcpp/src/lib/Codegen.ml b/atdcpp/src/lib/Codegen.ml index c5dfa3d60..1cb269304 100644 --- a/atdcpp/src/lib/Codegen.ml +++ b/atdcpp/src/lib/Codegen.ml @@ -461,7 +461,7 @@ let rec json_writer ?(nested=false) env e = | List (loc, e, an) -> (match assoc_kind loc e an with | Array_list -> - sprintf "_atd_write_list<%s>" (json_writer ~nested:true env e) + sprintf "_atd_write_array<%s>" (json_writer ~nested:true env e) | Array_dict (key, value) -> sprintf "_atd_write_assoc_dict_to_array<%s, %s>" (json_writer ~nested:true env key) (json_writer ~nested:true env value) @@ -543,7 +543,7 @@ let rec json_reader ?(nested=false) env (e : type_expr) = The default is to use JSON arrays and Python lists. *) (match assoc_kind loc e an with | Array_list -> - sprintf "_atd_read_list<%s>" + sprintf "_atd_read_array<%s>" (json_reader ~nested:true env e) | Array_dict (key, value) -> sprintf "_atd_read_array_to_assoc_dict<%s, %s>" @@ -642,6 +642,7 @@ let inst_var_declaration [ Line (sprintf "%s %s%s;" type_name var_name default) ] + let record env loc name (fields : field list) an = let dlang_struct_name = struct_name env name in @@ -706,7 +707,7 @@ let record env loc name (fields : field list) an = let alias_wrapper env name type_expr = let dlang_struct_name = struct_name env name in let value_type = type_name_of_expr env type_expr in - let optional_constructor = match type_expr with + (* let optional_constructor = match type_expr with | Tuple (_, _, _) -> Line(sprintf "this(T...)(T args) @safe {_data = tuple(args);}"); | _ -> Line(""); in @@ -722,7 +723,8 @@ let alias_wrapper env name type_expr = Line (sprintf "@trusted %s fromJson(T : %s)(JSONValue e) {" dlang_struct_name dlang_struct_name); Block [Line(sprintf "return %s(%s(e));" dlang_struct_name (json_reader env type_expr))]; Line("}"); - ] + ] *) + [Line (sprintf "typedef %s %s;" value_type dlang_struct_name)] let case_class env type_name (loc, orig_name, unique_name, an, opt_e) = diff --git a/atdcpp/test/atd-input/everything.atd b/atdcpp/test/atd-input/everything.atd index 6ea2ca857..65a047588 100644 --- a/atdcpp/test/atd-input/everything.atd +++ b/atdcpp/test/atd-input/everything.atd @@ -1,6 +1,6 @@ - +(* *) -type kind = [ +(* type kind = [ | Root (* class name conflict *) | Thing of int | WOW @@ -10,13 +10,14 @@ type kind = [ type frozen = [ | A | B of int -] +] *) type ('a, 'b) parametrized_record = { field_a: 'a; ~field_b: 'b list; } +(* type 'a parametrized_tuple = ('a * 'a * int) type root = { @@ -56,15 +57,18 @@ type alias_of_alias_not_Wrapped = alias3 type alias_of_alias_of_alias = alias_of_alias_not_Wrapped type password = int wrap - +*) type credential = { name: string; - password: password; + password: int; } type credentials = credential list - +type three_level_nested_list_record = { + field_a: int list list list +} +(* type pair = (string * int) type require_field = { @@ -83,4 +87,4 @@ type default_list = { type record_with_wrapped_type = { item: string wrap ; -} +} *) diff --git a/atdcpp/test/cpp-tests/dune b/atdcpp/test/cpp-tests/dune index a46149b60..8d30e596e 100644 --- a/atdcpp/test/cpp-tests/dune +++ b/atdcpp/test/cpp-tests/dune @@ -3,7 +3,7 @@ ; (rule (targets - everything_atd.d + everything_atd.hpp ) (deps ../atd-input/everything.atd @@ -18,7 +18,7 @@ (alias runtest) (package atdcpp) (deps - (glob_files *.d)) + (glob_files *.cpp)) (action (progn (bash "ldc2 %{deps} --of test") From 1ad50131fa73a6c6d2568cc3f3af8444b65f0371 Mon Sep 17 00:00:00 2001 From: Alexandre Bourquelot Date: Fri, 2 Feb 2024 22:46:35 +0800 Subject: [PATCH 06/47] atdcpp: add test_main --- atdcpp/test_main.cpp | 45 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) create mode 100644 atdcpp/test_main.cpp diff --git a/atdcpp/test_main.cpp b/atdcpp/test_main.cpp new file mode 100644 index 000000000..addc54ddb --- /dev/null +++ b/atdcpp/test_main.cpp @@ -0,0 +1,45 @@ +#include "everything_atd.hpp" + +#include +#include + + +const rapidjson::Document doc_from_json(const std::string &json) +{ + rapidjson::Document doc; + doc.Parse(json.c_str()); + + if (doc.HasParseError()) + { + } + + return doc; +} + +int main() +{ + + Credential credential = Credential::from_json(doc_from_json(R"({"name":"user","password":1234})")); + std::cout << "Credential: " << credential.to_json_string() << std::endl; + + + // std::string NNNIntListRecord_json = R"({"field_a":[[[1,2,3],[4,4,6]],[[7,8,9],[10,11,12]]]})"; + + // NNNIntListRecord recordFromJson = NNNIntListRecord::from_json(doc_from_json(NNNIntListRecord_json)); + // /// iterate through all vectors to print all values (3 level of nesting) + // for (auto &vec1 : recordFromJson.field_a) + // { + // for (auto &vec2 : vec1) + // { + // for (auto &val : vec2) + // { + // std::cout << val << " "; + // } + // std::cout << std::endl; + // } + // } + + // std::cout << "NNNIntListRecord: " << NNNIntListRecord{{{{1, 2, 3}, {4, 5, 6}}, {{7, 8, 9}, {10, 18, 12}}}}.to_json_string() << std::endl; + + // return 0; +} \ No newline at end of file From d91a4b86df6739922d0bda1b98eed5b56db30df0 Mon Sep 17 00:00:00 2001 From: Alexandre Bourquelot Date: Mon, 26 Feb 2024 17:12:44 +0100 Subject: [PATCH 07/47] atdcpp: get multiple level of list to work with function pointer instead of template --- atdcpp/src/lib/Codegen.ml | 56 +++++++++++++++++++--------- atdcpp/test/atd-input/everything.atd | 12 ++++-- atdcpp/test_main.cpp | 10 ++++- 3 files changed, 57 insertions(+), 21 deletions(-) diff --git a/atdcpp/src/lib/Codegen.ml b/atdcpp/src/lib/Codegen.ml index 1cb269304..cd3f122b9 100644 --- a/atdcpp/src/lib/Codegen.ml +++ b/atdcpp/src/lib/Codegen.ml @@ -261,15 +261,17 @@ std::string _atd_read_string(const rapidjson::Value &val) return val.GetString(); } -template -std::vector _atd_read_array(const rapidjson::Value &val) +template +auto _atd_read_array(F read_func, const rapidjson::Value &val) { + using ResultType = typename std::invoke_result::type; + if (!val.IsArray()) { throw std::runtime_error("Expected an array"); // Or your specific exception type } - std::vector result; + std::vector result; for (rapidjson::SizeType i = 0; i < val.Size(); i++) { result.push_back(read_func(val[i])); @@ -293,8 +295,8 @@ void _atd_write_string(const std::string &value, rapidjson::Writer&)> -void _atd_write_array(const std::vector& values, rapidjson::Writer& writer) +template +void _atd_write_array(F write_func, const V& values, rapidjson::Writer& writer) { writer.StartArray(); for (const auto& value : values) @@ -461,7 +463,7 @@ let rec json_writer ?(nested=false) env e = | List (loc, e, an) -> (match assoc_kind loc e an with | Array_list -> - sprintf "_atd_write_array<%s>" (json_writer ~nested:true env e) + sprintf "_atd_write_array([](auto v, auto &w){%s(v, w);}, " (json_writer ~nested:true env e) | Array_dict (key, value) -> sprintf "_atd_write_assoc_dict_to_array<%s, %s>" (json_writer ~nested:true env key) (json_writer ~nested:true env value) @@ -485,10 +487,10 @@ let rec json_writer ?(nested=false) env e = ) | Name (loc, (loc2, name, []), an) -> (match name with - | "bool" | "int" | "float" | "string" -> sprintf "_atd_write_%s" name + | "bool" | "int" | "float" | "string" -> sprintf "_atd_write_%s(" name | "abstract" -> "(JSONValue x) => x" | _ -> let dtype_name = (dlang_type_name env name) in - sprintf "((%s x) => x.toJson!(%s))" dtype_name dtype_name) + sprintf "%s::to_json" dtype_name) | Name (loc, _, _) -> not_implemented loc "parametrized types" | Tvar (loc, _) -> not_implemented loc "type variables" @@ -511,7 +513,7 @@ let construct_json_field env trans_meth [ Line (sprintf "writer.Key(\"%s\");" (Atd.Json.get_json_fname name an |> single_esc)); - Line (sprintf "%s(%s, writer);" writer_function (inst_var_name trans_meth name)); + Line (sprintf "%st.%s, writer);" writer_function (inst_var_name trans_meth name)); ] in match kind with @@ -543,7 +545,7 @@ let rec json_reader ?(nested=false) env (e : type_expr) = The default is to use JSON arrays and Python lists. *) (match assoc_kind loc e an with | Array_list -> - sprintf "_atd_read_array<%s>" + sprintf "_atd_read_array([](const rapidjson::Value &val){return %s(val);}, " (json_reader ~nested:true env e) | Array_dict (key, value) -> sprintf "_atd_read_array_to_assoc_dict<%s, %s>" @@ -568,9 +570,9 @@ let rec json_reader ?(nested=false) env (e : type_expr) = ) | Name (loc, (loc2, name, []), an) -> (match name with - | "bool" | "int" | "float" | "string" -> sprintf "_atd_read_%s" name + | "bool" | "int" | "float" | "string" -> sprintf "_atd_read_%s(" name | "abstract" -> "((JSONValue x) => x)" - | _ -> sprintf "fromJson!%s" + | _ -> sprintf "%s::from_json" (struct_name env name) ) | Name (loc, _, _) -> not_implemented loc "parametrized types" @@ -612,7 +614,7 @@ let from_json_class_argument Inline [ Line (sprintf "if (doc.HasMember(\"%s\"))" (single_esc json_name)); Block [ - Line (sprintf "record.%s = %s(doc[\"%s\"]);" + Line (sprintf "record.%s = %sdoc[\"%s\"]);" dlang_name (json_reader env e) (single_esc json_name)); @@ -665,7 +667,7 @@ let record env loc name (fields : field list) an = ) fields in let from_json = [ - Line (sprintf "static %s from_json(const rapidjson::Document & doc) {" + Line (sprintf "static %s from_json(const rapidjson::Value & doc) {" (single_esc dlang_struct_name)); Block [ Line (sprintf "%s record;" dlang_struct_name); @@ -680,24 +682,44 @@ let record env loc name (fields : field list) an = in let to_json = [ - Line (sprintf "std::string to_json_string() {"); + Line (sprintf "static void to_json(%s t, rapidjson::Writer &writer) {" (single_esc dlang_struct_name)); Block [ - Line ("rapidjson::StringBuffer buffer;"); - Line ("rapidjson::Writer writer(buffer);"); Line ("writer.StartObject();"); Inline json_object_body; Line ("writer.EndObject();"); + ]; + Line "}"; + ] + in + let to_json_string_static = + [ + Line (sprintf "static std::string to_json_string(%s t) {" (single_esc dlang_struct_name)); + Block [ + Line ("rapidjson::StringBuffer buffer;"); + Line ("rapidjson::Writer writer(buffer);"); + Line ("to_json(t, writer);"); Line "return buffer.GetString();" ]; Line "}"; ] in + let to_json_string = + [ + Line (sprintf "std::string to_json_string() {" ); + Block [ + Line ("return to_json_string(*this);"); + ]; + Line "}"; + ] + in [ Line (sprintf "struct %s {" dlang_struct_name); Block (spaced [ Inline inst_var_declarations; Inline from_json; Inline to_json; + Inline to_json_string_static; + Inline to_json_string; ]); Line ("};"); Line ""; diff --git a/atdcpp/test/atd-input/everything.atd b/atdcpp/test/atd-input/everything.atd index 65a047588..03a0bfe9e 100644 --- a/atdcpp/test/atd-input/everything.atd +++ b/atdcpp/test/atd-input/everything.atd @@ -63,11 +63,17 @@ type credential = { password: int; } -type credentials = credential list +type credentials = { + credentials: credential list; +} + + -type three_level_nested_list_record = { +(* type credentials = credential list *) + +(* type three_level_nested_list_record = { field_a: int list list list -} +} *) (* type pair = (string * int) diff --git a/atdcpp/test_main.cpp b/atdcpp/test_main.cpp index addc54ddb..8f7452d4b 100644 --- a/atdcpp/test_main.cpp +++ b/atdcpp/test_main.cpp @@ -3,7 +3,6 @@ #include #include - const rapidjson::Document doc_from_json(const std::string &json) { rapidjson::Document doc; @@ -22,6 +21,15 @@ int main() Credential credential = Credential::from_json(doc_from_json(R"({"name":"user","password":1234})")); std::cout << "Credential: " << credential.to_json_string() << std::endl; + Credentials credentials = Credentials::from_json(doc_from_json(R"({"credentials": + [{"name":"user1","password":1234},{"name":"user2","password":5678}]})")); + + for (auto &credential : credentials.credentials) + { + std::cout << "Credential: " << credential.to_json_string() << std::endl; + } + + std::cout << "Credentials: " << credentials.to_json_string() << std::endl; // std::string NNNIntListRecord_json = R"({"field_a":[[[1,2,3],[4,4,6]],[[7,8,9],[10,11,12]]]})"; From 30e7ea46da35da0059889ce25eef476ecc21d6ca Mon Sep 17 00:00:00 2001 From: Alexandre Bourquelot Date: Mon, 26 Feb 2024 17:23:09 +0100 Subject: [PATCH 08/47] atdcpp: support recursive lists --- atdcpp/src/lib/Codegen.ml | 8 ++++---- atdcpp/test/atd-input/everything.atd | 5 +++-- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/atdcpp/src/lib/Codegen.ml b/atdcpp/src/lib/Codegen.ml index cd3f122b9..0e1a69048 100644 --- a/atdcpp/src/lib/Codegen.ml +++ b/atdcpp/src/lib/Codegen.ml @@ -463,7 +463,7 @@ let rec json_writer ?(nested=false) env e = | List (loc, e, an) -> (match assoc_kind loc e an with | Array_list -> - sprintf "_atd_write_array([](auto v, auto &w){%s(v, w);}, " (json_writer ~nested:true env e) + sprintf "_atd_write_array([](auto v, auto &w){%sv, w);}, " (json_writer ~nested:true env e) | Array_dict (key, value) -> sprintf "_atd_write_assoc_dict_to_array<%s, %s>" (json_writer ~nested:true env key) (json_writer ~nested:true env value) @@ -490,7 +490,7 @@ let rec json_writer ?(nested=false) env e = | "bool" | "int" | "float" | "string" -> sprintf "_atd_write_%s(" name | "abstract" -> "(JSONValue x) => x" | _ -> let dtype_name = (dlang_type_name env name) in - sprintf "%s::to_json" dtype_name) + sprintf "%s::to_json(" dtype_name) | Name (loc, _, _) -> not_implemented loc "parametrized types" | Tvar (loc, _) -> not_implemented loc "type variables" @@ -545,7 +545,7 @@ let rec json_reader ?(nested=false) env (e : type_expr) = The default is to use JSON arrays and Python lists. *) (match assoc_kind loc e an with | Array_list -> - sprintf "_atd_read_array([](const rapidjson::Value &val){return %s(val);}, " + sprintf "_atd_read_array([](const auto &v){return %sv);}, " (json_reader ~nested:true env e) | Array_dict (key, value) -> sprintf "_atd_read_array_to_assoc_dict<%s, %s>" @@ -572,7 +572,7 @@ let rec json_reader ?(nested=false) env (e : type_expr) = (match name with | "bool" | "int" | "float" | "string" -> sprintf "_atd_read_%s(" name | "abstract" -> "((JSONValue x) => x)" - | _ -> sprintf "%s::from_json" + | _ -> sprintf "%s::from_json(" (struct_name env name) ) | Name (loc, _, _) -> not_implemented loc "parametrized types" diff --git a/atdcpp/test/atd-input/everything.atd b/atdcpp/test/atd-input/everything.atd index 03a0bfe9e..25b4a729f 100644 --- a/atdcpp/test/atd-input/everything.atd +++ b/atdcpp/test/atd-input/everything.atd @@ -71,9 +71,10 @@ type credentials = { (* type credentials = credential list *) -(* type three_level_nested_list_record = { +type three_level_nested_list_record = { field_a: int list list list -} *) +} + (* type pair = (string * int) From a788edb03061b6adf1b6431e490bd2b0e7ebfaf7 Mon Sep 17 00:00:00 2001 From: Alexandre Bourquelot Date: Mon, 26 Feb 2024 18:05:01 +0100 Subject: [PATCH 09/47] atdcpp: support type definitions --- atdcpp/src/lib/Codegen.ml | 48 +++++++++++++++++++++++-- atdcpp/test/atd-input/everything.atd | 10 +++--- atdcpp/test_main.cpp | 53 +++++++++++++++++++++++----- 3 files changed, 96 insertions(+), 15 deletions(-) diff --git a/atdcpp/src/lib/Codegen.ml b/atdcpp/src/lib/Codegen.ml index 0e1a69048..70985655b 100644 --- a/atdcpp/src/lib/Codegen.ml +++ b/atdcpp/src/lib/Codegen.ml @@ -233,6 +233,15 @@ int _atd_read_int(const rapidjson::Value &val) return val.GetInt(); } +bool _atd_read_bool(const rapidjson::Value &val) +{ + if (!val.IsBool()) + { + throw AtdException("Expected a boolean"); + } + return val.GetBool(); +} + // Reading a float from JSON float _atd_read_float(const rapidjson::Value &val) { @@ -285,6 +294,11 @@ void _atd_write_int(int value, rapidjson::Writer& write writer.Int(value); } +void _atd_write_bool(bool value, rapidjson::Writer& writer) +{ + writer.Bool(value); +} + void _atd_write_float(float value, rapidjson::Writer& writer) { writer.Double(value); @@ -682,7 +696,7 @@ let record env loc name (fields : field list) an = in let to_json = [ - Line (sprintf "static void to_json(%s t, rapidjson::Writer &writer) {" (single_esc dlang_struct_name)); + Line (sprintf "static void to_json(const %s &t, rapidjson::Writer &writer) {" (single_esc dlang_struct_name)); Block [ Line ("writer.StartObject();"); Inline json_object_body; @@ -693,7 +707,7 @@ let record env loc name (fields : field list) an = in let to_json_string_static = [ - Line (sprintf "static std::string to_json_string(%s t) {" (single_esc dlang_struct_name)); + Line (sprintf "static std::string to_json_string(const %s &t) {" (single_esc dlang_struct_name)); Block [ Line ("rapidjson::StringBuffer buffer;"); Line ("rapidjson::Writer writer(buffer);"); @@ -746,7 +760,35 @@ let alias_wrapper env name type_expr = Block [Line(sprintf "return %s(%s(e));" dlang_struct_name (json_reader env type_expr))]; Line("}"); ] *) - [Line (sprintf "typedef %s %s;" value_type dlang_struct_name)] + [ + Line (sprintf "namespace typedefs {"); + Block [ + Line (sprintf "typedef %s %s;" value_type dlang_struct_name) + ]; + Line "}"; + Line (sprintf "namespace %s {" dlang_struct_name); + Block [ + Line (sprintf "auto from_json(const rapidjson::Value &doc) {"); + Block [ + Line (sprintf "return %sdoc);" (json_reader env type_expr)); + ]; + Line "}"; + Line (sprintf "auto to_json(const typedefs::%s &t, rapidjson::Writer &writer) {" dlang_struct_name); + Block [ + Line (sprintf "%st, writer);" (json_writer env type_expr)); + ]; + Line "}"; + Line (sprintf "std::string to_json_string(const typedefs::%s &t) {" dlang_struct_name); + Block [ + Line ("rapidjson::StringBuffer buffer;"); + Line ("rapidjson::Writer writer(buffer);"); + Line (sprintf "to_json(t, writer);"); + Line "return buffer.GetString();" + ]; + Line "}"; + ]; + Line "}"; + ] let case_class env type_name (loc, orig_name, unique_name, an, opt_e) = diff --git a/atdcpp/test/atd-input/everything.atd b/atdcpp/test/atd-input/everything.atd index 25b4a729f..16e8755ed 100644 --- a/atdcpp/test/atd-input/everything.atd +++ b/atdcpp/test/atd-input/everything.atd @@ -69,18 +69,18 @@ type credentials = { -(* type credentials = credential list *) +type credentials2 = credential list type three_level_nested_list_record = { field_a: int list list list } -(* -type pair = (string * int) + +(* type pair = (string * int) type require_field = { req: string; -} +} *) type recursive_class = { id: int; @@ -88,6 +88,8 @@ type recursive_class = { children: recursive_class list; } +(* + type default_list = { ~items: int list; } diff --git a/atdcpp/test_main.cpp b/atdcpp/test_main.cpp index 8f7452d4b..0476906b1 100644 --- a/atdcpp/test_main.cpp +++ b/atdcpp/test_main.cpp @@ -18,20 +18,57 @@ const rapidjson::Document doc_from_json(const std::string &json) int main() { - Credential credential = Credential::from_json(doc_from_json(R"({"name":"user","password":1234})")); - std::cout << "Credential: " << credential.to_json_string() << std::endl; + // Credential credential = Credential::from_json(doc_from_json(R"({"name":"user","password":1234})")); + // std::cout << "Credential: " << credential.to_json_string() << std::endl; - Credentials credentials = Credentials::from_json(doc_from_json(R"({"credentials": - [{"name":"user1","password":1234},{"name":"user2","password":5678}]})")); + // Credentials credentials = Credentials::from_json(doc_from_json(R"({"credentials": + // [{"name":"user1","password":1234},{"name":"user2","password":5678}]})")); - for (auto &credential : credentials.credentials) + // for (auto &credential : credentials.credentials) + // { + // std::cout << "Credential: " << credential.to_json_string() << std::endl; + // } + + // std::cout << "Credentials: " << credentials.to_json_string() << std::endl; + + // std::string NNNIntListRecord_json = R"({"field_a":[[[1,2,3],[4,4,6]],[[7,8,9],[10,11,12]]]})"; + + // ThreeLevelNestedListRecord recordFromJson = ThreeLevelNestedListRecord::from_json(doc_from_json(NNNIntListRecord_json)); + + // for (auto &vec1 : recordFromJson.field_a) + // { + // std::cout << "vec1: " << std::endl; + // for (auto &vec2 : vec1) + // { + // std::cout << "vec2: " << std::endl; + // for (auto &val : vec2) + // { + // std::cout << val << " "; + // } + // std::cout << std::endl; + // } + // } + + // std::cout << "ThreeLevelNestedListRecord: " << ThreeLevelNestedListRecord{{{{1, 2, 3}, {4, 5, 6}}, {{7, 8, 9}, {10, 18, 12}}}}.to_json_string() << std::endl; + // std::cout << "Three..." << recordFromJson.to_json_string() << std::endl; + + + // std::string RecursiveClass_json = R"({"id":1,"flag":true,"children":[{"id":2,"flag":false,"children":[]},{"id":3,"flag":true,"children":[{"id":4,"flag":false,"children":[]}]}]})"; + + // RecursiveClass recordFromJson2 = RecursiveClass::from_json(doc_from_json(RecursiveClass_json)); + + // std::cout << "RecursiveClass: " << recordFromJson2.to_json_string() << std::endl; + + + typedefs::Credentials2 credentials2 = Credentials2::from_json(doc_from_json(R"( + [{"name":"user1","password":1234},{"name":"user2","password":5678}])")); + + for (auto &credential : credentials2) { std::cout << "Credential: " << credential.to_json_string() << std::endl; } - std::cout << "Credentials: " << credentials.to_json_string() << std::endl; - - // std::string NNNIntListRecord_json = R"({"field_a":[[[1,2,3],[4,4,6]],[[7,8,9],[10,11,12]]]})"; + std::cout << "Credentials: " << Credentials2::to_json_string(credentials2) << std::endl; // NNNIntListRecord recordFromJson = NNNIntListRecord::from_json(doc_from_json(NNNIntListRecord_json)); // /// iterate through all vectors to print all values (3 level of nesting) From 75bd2ba7566415c274f438c77ab657358b6fe6e8 Mon Sep 17 00:00:00 2001 From: Alexandre Bourquelot Date: Tue, 27 Feb 2024 10:19:14 +0100 Subject: [PATCH 10/47] atdcpp: tuple --- atdcpp/src/lib/Codegen.ml | 45 ++++++++++------------------ atdcpp/test/atd-input/everything.atd | 9 +++--- atdcpp/test_main.cpp | 8 +++++ 3 files changed, 28 insertions(+), 34 deletions(-) diff --git a/atdcpp/src/lib/Codegen.ml b/atdcpp/src/lib/Codegen.ml index 70985655b..fab6815a6 100644 --- a/atdcpp/src/lib/Codegen.ml +++ b/atdcpp/src/lib/Codegen.ml @@ -391,7 +391,7 @@ let rec type_name_of_expr env (e : type_expr) : string = xs |> List.map (fun (loc, x, an) -> type_name_of_expr env x) in - sprintf "Tuple!(%s)" (String.concat ", " type_names) + sprintf "std::tuple<%s>" (String.concat ", " type_names) | List (loc, e, an) -> (match assoc_kind loc e an with | Array_list @@ -511,12 +511,15 @@ let rec json_writer ?(nested=false) env e = and tuple_writer env (loc, cells, an) = let tuple_body = List.mapi (fun i (loc, e, an) -> - sprintf "%s(x[%i])" (json_writer env e) i + sprintf "%sstd::get<%i>(t), writer)" (json_writer env e) i ) cells - |> String.concat ", " + |> String.concat "; " in - sprintf "((%s x) => JSONValue([%s]))" - (type_name_of_expr env (Tuple (loc, cells, an))) + sprintf "[](const auto &t, auto &writer){ + writer.StartArray(); + %s; + writer.EndArray(); + }(" tuple_body let construct_json_field env trans_meth @@ -595,15 +598,16 @@ let rec json_reader ?(nested=false) env (e : type_expr) = and tuple_reader env cells = let tuple_body = List.mapi (fun i (loc, e, an) -> - sprintf "%s(x[%i])" (json_reader env e) i + sprintf "%sv[%i])" (json_reader env e) i ) cells |> String.concat ", " in - sprintf "((JSONValue x) @trusted { - if (x.type != JSONType.array || x.array.length != %d) - throw _atd_bad_json(\"Tuple of size %d\", x); - return tuple(%s); - })" (List.length cells) (List.length cells) tuple_body + sprintf "[](auto &v){ + if (!v.IsArray() || v.Size() != %d) + throw AtdException(\"Tuple of size %d\"); + return std::make_tuple(%s); + }(" + (List.length cells) (List.length cells) tuple_body let from_json_class_argument env trans_meth dlang_struct_name ((loc, (name, kind, an), e) : simple_field) = @@ -743,23 +747,6 @@ let record env loc name (fields : field list) an = let alias_wrapper env name type_expr = let dlang_struct_name = struct_name env name in let value_type = type_name_of_expr env type_expr in - (* let optional_constructor = match type_expr with - | Tuple (_, _, _) -> - Line(sprintf "this(T...)(T args) @safe {_data = tuple(args);}"); - | _ -> Line(""); in - [ - Line (sprintf "struct %s{%s _data; alias _data this;" dlang_struct_name value_type); - Line (sprintf "this(%s init) @safe {_data = init;}" value_type ); - Line (sprintf "this(%s init) @safe {_data = init._data;}" dlang_struct_name); - Inline [optional_constructor]; - Line ("}"); - Line (sprintf "@trusted JSONValue toJson(T : %s)(%s e) {" dlang_struct_name dlang_struct_name); - Block [Line(sprintf "return %s(e);" (json_writer env type_expr))]; - Line("}"); - Line (sprintf "@trusted %s fromJson(T : %s)(JSONValue e) {" dlang_struct_name dlang_struct_name); - Block [Line(sprintf "return %s(%s(e));" dlang_struct_name (json_reader env type_expr))]; - Line("}"); - ] *) [ Line (sprintf "namespace typedefs {"); Block [ @@ -773,7 +760,7 @@ let alias_wrapper env name type_expr = Line (sprintf "return %sdoc);" (json_reader env type_expr)); ]; Line "}"; - Line (sprintf "auto to_json(const typedefs::%s &t, rapidjson::Writer &writer) {" dlang_struct_name); + Line (sprintf "void to_json(const typedefs::%s &t, rapidjson::Writer &writer) {" dlang_struct_name); Block [ Line (sprintf "%st, writer);" (json_writer env type_expr)); ]; diff --git a/atdcpp/test/atd-input/everything.atd b/atdcpp/test/atd-input/everything.atd index 16e8755ed..e332c700e 100644 --- a/atdcpp/test/atd-input/everything.atd +++ b/atdcpp/test/atd-input/everything.atd @@ -10,7 +10,7 @@ type frozen = [ | A | B of int -] *) +] type ('a, 'b) parametrized_record = { field_a: 'a; @@ -58,6 +58,7 @@ type alias_of_alias_of_alias = alias_of_alias_not_Wrapped type password = int wrap *) +*) type credential = { name: string; password: int; @@ -67,8 +68,6 @@ type credentials = { credentials: credential list; } - - type credentials2 = credential list type three_level_nested_list_record = { @@ -76,11 +75,11 @@ type three_level_nested_list_record = { } -(* type pair = (string * int) +type pair = (string * int) type require_field = { req: string; -} *) +} type recursive_class = { id: int; diff --git a/atdcpp/test_main.cpp b/atdcpp/test_main.cpp index 0476906b1..135274139 100644 --- a/atdcpp/test_main.cpp +++ b/atdcpp/test_main.cpp @@ -70,6 +70,14 @@ int main() std::cout << "Credentials: " << Credentials2::to_json_string(credentials2) << std::endl; + + std::string pair_json = R"(["stringb", 1234])"; + typedefs::Pair pair = Pair::from_json(doc_from_json(pair_json)); + + std::cout << "pair first: " << std::get(pair) << std::endl; + std::cout << "pair second: " << std::get(pair) << std::endl; + + std::cout << "Pair: " << Pair::to_json_string(pair) << std::endl; // NNNIntListRecord recordFromJson = NNNIntListRecord::from_json(doc_from_json(NNNIntListRecord_json)); // /// iterate through all vectors to print all values (3 level of nesting) // for (auto &vec1 : recordFromJson.field_a) From 4331819f018ce35b6d402ee0c6180ac55d96a726 Mon Sep 17 00:00:00 2001 From: Alexandre Bourquelot Date: Tue, 27 Feb 2024 12:40:24 +0100 Subject: [PATCH 11/47] atdcpp: handle variants --- atdcpp/src/lib/Codegen.ml | 103 ++++++++++++++++++--------- atdcpp/test/atd-input/everything.atd | 7 +- atdcpp/test_main.cpp | 34 +++++---- 3 files changed, 97 insertions(+), 47 deletions(-) diff --git a/atdcpp/src/lib/Codegen.ml b/atdcpp/src/lib/Codegen.ml index fab6815a6..3a0555ee8 100644 --- a/atdcpp/src/lib/Codegen.ml +++ b/atdcpp/src/lib/Codegen.ml @@ -223,6 +223,11 @@ T _atd_missing_json_field(const std::string &type, const std::string &field) throw AtdException("Missing JSON field '" + field + "' in " + type); } +auto _atd_bad_json(const std::string &type, const rapidjson::Value &x) +{ + return AtdException("Bad JSON for " + type); +} + // Reading an integer from JSON int _atd_read_int(const rapidjson::Value &val) { @@ -786,20 +791,33 @@ let case_class env type_name Line (sprintf {|// Original type: %s = [ ... | %s | ... ]|} type_name orig_name); - Line (sprintf "struct %s {}" (trans env unique_name)); - Line (sprintf "@trusted JSONValue toJson(T : %s)(T e) {" (trans env unique_name)); - Block [Line(sprintf "return JSONValue(\"%s\");" (single_esc json_name))]; - Line("}"); + Line (sprintf "struct %s {" (trans env orig_name)); + Line (sprintf "static void to_json(const %s &e, rapidjson::Writer &writer){" (trans env unique_name)); + Block [ + Line (sprintf "writer.String(\"%s\");" (single_esc json_name)); + ]; + Line("}"); + Line (sprintf "};"); ] | Some e -> [ Line (sprintf {|// Original type: %s = [ ... | %s of ... | ... ]|} type_name orig_name); - Line (sprintf "struct %s { %s value; }" (trans env unique_name) (type_name_of_expr env e)); (* TODO : very dubious*) - Line (sprintf "@trusted JSONValue toJson(T : %s)(T e) {" (trans env unique_name)); - Block [Line(sprintf "return JSONValue([JSONValue(\"%s\"), %s(e.value)]);" (single_esc json_name) (json_writer env e))]; - Line("}"); + Line (sprintf "struct %s" (trans env orig_name)); + Line(sprintf "{"); + Block [ + Line (sprintf "%s value;" (type_name_of_expr env e)); + Line (sprintf "static void to_json(const %s &e, rapidjson::Writer &writer){" (trans env unique_name)); + Block [ + Line (sprintf "writer.StartArray();"); + Line (sprintf "writer.String(\"%s\");" (single_esc json_name)); + Line (sprintf "%se.value, writer);" (json_writer env e)); + Line (sprintf "writer.EndArray();"); + ]; + Line("}"); + ]; + Line(sprintf "};"); ] @@ -809,9 +827,9 @@ let read_cases0 env loc name cases0 = |> List.map (fun (loc, orig_name, unique_name, an, opt_e) -> let json_name = Atd.Json.get_json_cons orig_name an in Inline [ - Line (sprintf "if (x.str == \"%s\") " (single_esc json_name)); + Line (sprintf "if (std::string_view(x.GetString()) == \"%s\") " (single_esc json_name)); Block [ - Line (sprintf "return %s(%s());" (struct_name env name) (trans env unique_name)) + Line (sprintf "return Types::%s();" (trans env unique_name)) ]; ] ) @@ -835,8 +853,7 @@ let read_cases1 env loc name cases1 = Inline [ Line (sprintf "if (cons == \"%s\")" (single_esc json_name)); Block [ - Line (sprintf "return %s(%s(%s(x[1])));" - (struct_name env name) + Line (sprintf "return Types::%s({%sx[1])});" (trans env unique_name) (json_reader env e)) ] @@ -853,7 +870,7 @@ let sum_container env loc name cases = let dlang_struct_name = struct_name env name in let type_list = List.map (fun (loc, orig_name, unique_name, an, opt_e) -> - trans env unique_name + (sprintf "::%s::Types::%s" (dlang_struct_name) (trans env unique_name)) ) cases |> String.concat ", " in @@ -865,7 +882,7 @@ let sum_container env loc name cases = let cases0_block = if cases0 <> [] then [ - Line "if (x.type == JSONType.string) {"; + Line "if (x.IsString()) {"; Block (read_cases0 env loc name cases0); Line "}"; ] @@ -875,9 +892,9 @@ let sum_container env loc name cases = let cases1_block = if cases1 <> [] then [ - Line "if (x.type == JSONType.array && x.array.length == 2 && x[0].type == JSONType.string) {"; + Line "if (x.IsArray() && x.Size() == 2 && x[0].IsString()) {"; Block [ - Line "string cons = x[0].str;"; + Line "std::string cons = x[0].GetString();"; Inline (read_cases1 env loc name cases1) ]; Line "}"; @@ -886,30 +903,48 @@ let sum_container env loc name cases = [] in [ - Line (sprintf "struct %s{ %s _data; alias _data this;" dlang_struct_name (sprintf "SumType!(%s)" type_list) ); - Line (sprintf "@safe this(T)(T init) {_data = init;} @safe this(%s init) {_data = init._data;}}" dlang_struct_name); - Line ""; - Line (sprintf "@trusted %s fromJson(T : %s)(JSONValue x) {" - (single_esc dlang_struct_name) (single_esc dlang_struct_name)); + Line (sprintf "namespace typedefs {"); + Block [ Line(sprintf "typedef %s %s;" (sprintf "std::variant<%s>" type_list) (dlang_struct_name))]; + Line "}"; + + Line (sprintf "namespace %s {" (dlang_struct_name)); + Block [ + Line (sprintf "static ::typedefs::%s from_json(const rapidjson::Value &x) {" (dlang_struct_name)); Block [ Inline cases0_block; Inline cases1_block; Line (sprintf "throw _atd_bad_json(\"%s\", x);" (single_esc (struct_name env name))) ]; - Line "}"; - Line ""; - Line (sprintf "@trusted JSONValue toJson(T : %s)(T x) {" (dlang_struct_name)); + Line "}"; + ]; + Block [ - Line "return x.match!("; - Line ( - List.map (fun (loc, orig_name, unique_name, an, opt_e) -> - sprintf "(%s v) => v.toJson!(%s)" (trans env unique_name) (trans env unique_name) - ) cases - |> String.concat ",\n"); - Line ");" + Line (sprintf "static void to_json(const ::typedefs::%s &x, rapidjson::Writer &writer) {" (dlang_struct_name)); + Block [ + Line "std::visit([&writer](auto &&arg) {"; + Block [ + Line "using T = std::decay_t;"; + Line ( + List.map (fun (loc, orig_name, unique_name, an, opt_e) -> + sprintf "if constexpr (std::is_same_v) Types::%s::to_json(arg, writer);" (trans env unique_name) (trans env unique_name) + ) cases + |> String.concat "\n"); + ]; + Line ("}, x);"); + ]; + Line ("}"); ]; - Line "}"; + + Line (sprintf "std::string to_json_string(const ::typedefs::%s &x) {" (dlang_struct_name)); + Block [ + Line ("rapidjson::StringBuffer buffer;"); + Line ("rapidjson::Writer writer(buffer);"); + Line ("to_json(x, writer);"); + Line "return buffer.GetString();" + ]; + Line "}"; + Line ("}"); ] @@ -929,7 +964,9 @@ let sum env loc name cases = in let container_class = sum_container env loc name cases in [ - Inline case_classes; + Line (sprintf "namespace %s::Types {" (struct_name env name)); + Block case_classes; + Line (sprintf "}"); Inline container_class; ] |> double_spaced diff --git a/atdcpp/test/atd-input/everything.atd b/atdcpp/test/atd-input/everything.atd index e332c700e..39ea22879 100644 --- a/atdcpp/test/atd-input/everything.atd +++ b/atdcpp/test/atd-input/everything.atd @@ -7,11 +7,15 @@ | Amaze of string list ] +*) + type frozen = [ | A | B of int ] +(* + type ('a, 'b) parametrized_record = { field_a: 'a; ~field_b: 'b list; @@ -87,12 +91,13 @@ type recursive_class = { children: recursive_class list; } -(* type default_list = { ~items: int list; } +(* + type record_with_wrapped_type = { item: string wrap ; } *) diff --git a/atdcpp/test_main.cpp b/atdcpp/test_main.cpp index 135274139..b8c17032b 100644 --- a/atdcpp/test_main.cpp +++ b/atdcpp/test_main.cpp @@ -60,25 +60,33 @@ int main() // std::cout << "RecursiveClass: " << recordFromJson2.to_json_string() << std::endl; - typedefs::Credentials2 credentials2 = Credentials2::from_json(doc_from_json(R"( - [{"name":"user1","password":1234},{"name":"user2","password":5678}])")); + // typedefs::Credentials2 credentials2 = Credentials2::from_json(doc_from_json(R"( + // [{"name":"user1","password":1234},{"name":"user2","password":5678}])")); + + // for (auto &credential : credentials2) + // { + // std::cout << "Credential: " << credential.to_json_string() << std::endl; + // } + + // std::cout << "Credentials: " << Credentials2::to_json_string(credentials2) << std::endl; - for (auto &credential : credentials2) - { - std::cout << "Credential: " << credential.to_json_string() << std::endl; - } - std::cout << "Credentials: " << Credentials2::to_json_string(credentials2) << std::endl; + // std::string pair_json = R"(["stringb", 1234])"; + // typedefs::Pair pair = Pair::from_json(doc_from_json(pair_json)); + // std::cout << "pair first: " << std::get(pair) << std::endl; + // std::cout << "pair second: " << std::get(pair) << std::endl; - std::string pair_json = R"(["stringb", 1234])"; - typedefs::Pair pair = Pair::from_json(doc_from_json(pair_json)); + // std::cout << "Pair: " << Pair::to_json_string(pair) << std::endl; + + // typedefs::Frozen frozen = Frozen::Types::A(); + // std::cout << "Frozen: " << Frozen::to_json_string(frozen) << std::endl; - std::cout << "pair first: " << std::get(pair) << std::endl; - std::cout << "pair second: " << std::get(pair) << std::endl; + // std::string frozen_json = R"(["B", 1234])"; + // frozen = Frozen::from_json(doc_from_json(frozen_json)); + // std::cout << "Frozen: " << Frozen::to_json_string(frozen) << std::endl; - std::cout << "Pair: " << Pair::to_json_string(pair) << std::endl; - // NNNIntListRecord recordFromJson = NNNIntListRecord::from_json(doc_from_json(NNNIntListRecord_json)); + // NNNIntListRecord recordFromJson = NNNIntListRecord::from_json(doc_from_json(NNNIntListRecord_json)); // /// iterate through all vectors to print all values (3 level of nesting) // for (auto &vec1 : recordFromJson.field_a) // { From 31c25316820e3eb2d18d1e9dd42eed01c5d055a8 Mon Sep 17 00:00:00 2001 From: Alexandre Bourquelot Date: Tue, 27 Feb 2024 13:03:09 +0100 Subject: [PATCH 12/47] atdcpp: fix name clashing with variants --- atdcpp/src/lib/Codegen.ml | 12 ++++++------ atdcpp/test/atd-input/everything.atd | 7 +++++-- 2 files changed, 11 insertions(+), 8 deletions(-) diff --git a/atdcpp/src/lib/Codegen.ml b/atdcpp/src/lib/Codegen.ml index 3a0555ee8..b98a150c2 100644 --- a/atdcpp/src/lib/Codegen.ml +++ b/atdcpp/src/lib/Codegen.ml @@ -792,7 +792,7 @@ let case_class env type_name type_name orig_name); Line (sprintf "struct %s {" (trans env orig_name)); - Line (sprintf "static void to_json(const %s &e, rapidjson::Writer &writer){" (trans env unique_name)); + Line (sprintf "static void to_json(const %s &e, rapidjson::Writer &writer){" (trans env orig_name)); Block [ Line (sprintf "writer.String(\"%s\");" (single_esc json_name)); ]; @@ -808,7 +808,7 @@ let case_class env type_name Line(sprintf "{"); Block [ Line (sprintf "%s value;" (type_name_of_expr env e)); - Line (sprintf "static void to_json(const %s &e, rapidjson::Writer &writer){" (trans env unique_name)); + Line (sprintf "static void to_json(const %s &e, rapidjson::Writer &writer){" (trans env orig_name)); Block [ Line (sprintf "writer.StartArray();"); Line (sprintf "writer.String(\"%s\");" (single_esc json_name)); @@ -829,7 +829,7 @@ let read_cases0 env loc name cases0 = Inline [ Line (sprintf "if (std::string_view(x.GetString()) == \"%s\") " (single_esc json_name)); Block [ - Line (sprintf "return Types::%s();" (trans env unique_name)) + Line (sprintf "return Types::%s();" (trans env orig_name)) ]; ] ) @@ -854,7 +854,7 @@ let read_cases1 env loc name cases1 = Line (sprintf "if (cons == \"%s\")" (single_esc json_name)); Block [ Line (sprintf "return Types::%s({%sx[1])});" - (trans env unique_name) + (trans env orig_name) (json_reader env e)) ] ] @@ -870,7 +870,7 @@ let sum_container env loc name cases = let dlang_struct_name = struct_name env name in let type_list = List.map (fun (loc, orig_name, unique_name, an, opt_e) -> - (sprintf "::%s::Types::%s" (dlang_struct_name) (trans env unique_name)) + (sprintf "::%s::Types::%s" (dlang_struct_name) (trans env orig_name)) ) cases |> String.concat ", " in @@ -927,7 +927,7 @@ let sum_container env loc name cases = Line "using T = std::decay_t;"; Line ( List.map (fun (loc, orig_name, unique_name, an, opt_e) -> - sprintf "if constexpr (std::is_same_v) Types::%s::to_json(arg, writer);" (trans env unique_name) (trans env unique_name) + sprintf "if constexpr (std::is_same_v) Types::%s::to_json(arg, writer);" (trans env orig_name) (trans env orig_name) ) cases |> String.concat "\n"); ]; diff --git a/atdcpp/test/atd-input/everything.atd b/atdcpp/test/atd-input/everything.atd index 39ea22879..5c66e1b98 100644 --- a/atdcpp/test/atd-input/everything.atd +++ b/atdcpp/test/atd-input/everything.atd @@ -1,13 +1,16 @@ (* *) -(* type kind = [ +type kind = [ | Root (* class name conflict *) | Thing of int | WOW | Amaze of string list ] -*) +type root = { + +} + type frozen = [ | A From 485491dfbb2b9e6dcf6e607abdd3815f0de6ed2a Mon Sep 17 00:00:00 2001 From: Alexandre Bourquelot Date: Tue, 27 Feb 2024 15:00:10 +0100 Subject: [PATCH 13/47] atdcpp: wrapping --- atdcpp/src/lib/Codegen.ml | 16 ++++++++++++++-- atdcpp/test/atd-input/everything.atd | 14 +++++++------- 2 files changed, 21 insertions(+), 9 deletions(-) diff --git a/atdcpp/src/lib/Codegen.ml b/atdcpp/src/lib/Codegen.ml index b98a150c2..12e92576a 100644 --- a/atdcpp/src/lib/Codegen.ml +++ b/atdcpp/src/lib/Codegen.ml @@ -294,6 +294,12 @@ auto _atd_read_array(F read_func, const rapidjson::Value &val) return result; } +template +auto _atd_read_wrap(F read_func, W unwrap_func, const rapidjson::Value &val) +{ + return unwrap_func(read_func(val)); +} + void _atd_write_int(int value, rapidjson::Writer& writer) { writer.Int(value); @@ -325,6 +331,12 @@ void _atd_write_array(F write_func, const V& values, rapidjson::Writer +void _atd_write_wrap(F write_func, W wrap_func, const T &val, rapidjson::Writer& writer) +{ + write_func(wrap_func(val), writer); +} + |} atd_filename atd_filename @@ -502,7 +514,7 @@ let rec json_writer ?(nested=false) env e = (match Dlang_annot.get_dlang_wrap loc an with | None -> error_at loc "wrap type declared, but no cpp annotation found" | Some { dlang_wrap_t; dlang_unwrap ; _ } -> - sprintf "_atd_write_wrap!(%s, (%s e) => %s(e))" (json_writer ~nested:true env e) dlang_wrap_t dlang_unwrap + sprintf "_atd_write_wrap([](const auto &v, auto &w){%sv, w);}, [](const auto &e){return %s(e);}, " (json_writer ~nested:true env e) dlang_unwrap ) | Name (loc, (loc2, name, []), an) -> (match name with @@ -588,7 +600,7 @@ let rec json_reader ?(nested=false) env (e : type_expr) = (match Dlang_annot.get_dlang_wrap loc an with | None -> error_at loc "wrap type declared, but no cpp annotation found" | Some { dlang_wrap ; _ } -> - sprintf "_atd_read_wrap!(%s, (%s e) => %s(e))" (json_reader ~nested:true env e) (type_name_of_expr env e) dlang_wrap + sprintf "_atd_read_wrap([](const auto& v){return %sv);}, [](const auto &e){return %s(e);}," (json_reader ~nested:true env e) dlang_wrap ) | Name (loc, (loc2, name, []), an) -> (match name with diff --git a/atdcpp/test/atd-input/everything.atd b/atdcpp/test/atd-input/everything.atd index 5c66e1b98..d766995a0 100644 --- a/atdcpp/test/atd-input/everything.atd +++ b/atdcpp/test/atd-input/everything.atd @@ -8,7 +8,7 @@ type kind = [ ] type root = { - + } @@ -24,7 +24,6 @@ type ('a, 'b) parametrized_record = { ~field_b: 'b list; } -(* type 'a parametrized_tuple = ('a * 'a * int) type root = { @@ -54,18 +53,19 @@ type root = { wrapped: st wrap ; aaa: alias_of_alias_of_alias; } +*) type st = int type alias = int list type alias2 = int list -type alias3 = int wrap -type alias_of_alias = alias3 wrap +type alias3 = int wrap +type alias_of_alias = alias3 wrap type alias_of_alias_not_Wrapped = alias3 type alias_of_alias_of_alias = alias_of_alias_not_Wrapped -type password = int wrap -*) -*) +type password = int wrap + + type credential = { name: string; password: int; From 2fd3e5c73edf4f05493adf578cc3ffa460af7559 Mon Sep 17 00:00:00 2001 From: Alexandre Bourquelot Date: Tue, 27 Feb 2024 15:53:52 +0100 Subject: [PATCH 14/47] atdcpp: solve alias and namespace issue --- atdcpp/src/lib/Codegen.ml | 128 +++++++++++++++++++++++---- atdcpp/test/atd-input/everything.atd | 11 +-- 2 files changed, 113 insertions(+), 26 deletions(-) diff --git a/atdcpp/src/lib/Codegen.ml b/atdcpp/src/lib/Codegen.ml index 12e92576a..b9d44372c 100644 --- a/atdcpp/src/lib/Codegen.ml +++ b/atdcpp/src/lib/Codegen.ml @@ -202,6 +202,7 @@ let fixed_size_preamble atd_filename = #include #include #include +#include class AtdException : public std::exception { @@ -282,7 +283,7 @@ auto _atd_read_array(F read_func, const rapidjson::Value &val) if (!val.IsArray()) { - throw std::runtime_error("Expected an array"); // Or your specific exception type + throw AtdException("Expected an array"); // Or your specific exception type } std::vector result; @@ -294,6 +295,89 @@ auto _atd_read_array(F read_func, const rapidjson::Value &val) return result; } +template +auto _atd_read_object_to_tuple_list(F read_func, const rapidjson::Value &val) +{ + using ResultType = typename std::invoke_result::type; + + if (!val.IsObject()) + { + throw AtdException("Expected an object"); // Or your specific exception type + } + + std::vector> result; + for (auto &m : val.GetObject()) + { + result.push_back(std::make_pair(m.name.GetString(), read_func(m.value))); + } + + return result; +} + +template +auto _atd_read_array_to_assoc_dict(RK read_key_func, RV read_value_func, const rapidjson::Value &val) +{ + using KeyType = typename std::invoke_result::type; + using ValueType = typename std::invoke_result::type; + + if (!val.IsArray()) + { + throw AtdException("Expected an array"); // Or your specific exception type + } + + std::map result; + for (rapidjson::SizeType i = 0; i < val.Size(); i++) + { + auto &pair = val[i]; + if (!pair.IsArray() || pair.Size() != 2) + { + throw AtdException("Expected an array of pairs"); + } + result[read_key_func(pair[0])] = read_value_func(pair[1]); + } + + return result; +} + +template +auto _atd_read_object_to_assoc_array(F read_func, const rapidjson::Value &val) +{ + using ResultType = typename std::invoke_result::type; + + if (!val.IsObject()) + { + throw AtdException("Expected an object"); // Or your specific exception type + } + + std::map result; + for (auto &m : val.GetObject()) + { + result[m.name.GetString()] = read_func(m.value); + } + + return result; +} + +template +auto _atd_read_nullable(F read_func, const rapidjson::Value &val) +{ + if (val.IsNull()) + { + return std::optional::type>(); + } + return std::optional::type>(read_func(val)); +} + +template +auto _atd_read_option(F read_func, const rapidjson::Value &val) +{ + if (val.IsNull()) + { + return std::optional::type>(); + } + return std::optional::type>(read_func(val)); +} + template auto _atd_read_wrap(F read_func, W unwrap_func, const rapidjson::Value &val) { @@ -399,6 +483,19 @@ let dlang_type_name env (name : string) = let typename = (struct_name env user_defined) in typename +let dlang_type_name_namespaced env (name : string) = + match name with + | "unit" -> "void" + | "bool" -> "bool" + | "int" -> "int" + | "float" -> "float" + | "string" -> "std::string" + | "abstract" -> "rapidjson::Value" + | user_defined -> + let typename = (struct_name env user_defined) in + sprintf "%s::%s" "typedefs" typename + + let rec type_name_of_expr env (e : type_expr) : string = match e with | Sum (loc, _, _) -> not_implemented loc "inline sum types" @@ -420,7 +517,7 @@ let rec type_name_of_expr env (e : type_expr) : string = (type_name_of_expr env value) (type_name_of_expr env key) | Object_dict value -> - sprintf "std::map" + sprintf "std::map" (type_name_of_expr env value) ) | Option (loc, e, an) -> sprintf "std::optional<%s>" (type_name_of_expr env e) @@ -431,7 +528,7 @@ let rec type_name_of_expr env (e : type_expr) : string = | None -> error_at loc "wrap type declared, but no cpp annotation found" | Some { dlang_wrap_t ; _ } -> dlang_wrap_t ) - | Name (loc, (loc2, name, []), an) -> dlang_type_name env name + | Name (loc, (loc2, name, []), an) -> dlang_type_name_namespaced env name | Name (loc, (_, name, _::_), _) -> assert false | Tvar (loc, _) -> not_implemented loc "type variables" @@ -582,19 +679,19 @@ let rec json_reader ?(nested=false) env (e : type_expr) = sprintf "_atd_read_array([](const auto &v){return %sv);}, " (json_reader ~nested:true env e) | Array_dict (key, value) -> - sprintf "_atd_read_array_to_assoc_dict<%s, %s>" + sprintf "_atd_read_array_to_assoc_dict([](const auto &k){return %sk);}, [](const auto &v){return %sv);}, " (json_reader ~nested:true env key) (json_reader ~nested:true env value) | Object_dict value -> - sprintf "_atd_read_object_to_assoc_array<%s>" + sprintf "_atd_read_object_to_assoc_array([](const auto &v){return %sv);}," (json_reader ~nested:true env value) | Object_list value -> - sprintf "_atd_read_object_to_tuple_list<%s>" + sprintf "_atd_read_object_to_tuple_list([](const auto &v){return %sv);}," (json_reader ~nested:true env value) ) | Option (loc, e, an) -> - sprintf "_atd_read_option!(%s)" (json_reader ~nested:true env e) + sprintf "_atd_read_option([](const auto &v){return %sv);}, " (json_reader ~nested:true env e) | Nullable (loc, e, an) -> - sprintf "_atd_read_nullable!(%s)" (json_reader ~nested:true env e) + sprintf "_atd_read_nullable([](const auto &v){return %sv);}, " (json_reader ~nested:true env e) | Shared (loc, e, an) -> not_implemented loc "shared" | Wrap (loc, e, an) -> (match Dlang_annot.get_dlang_wrap loc an with @@ -637,7 +734,7 @@ let from_json_class_argument dlang_name (single_esc dlang_struct_name) (single_esc json_name) - | Optional -> (sprintf "decltype(record.%s).init" dlang_name) + | Optional -> (sprintf "std::nullopt") | With_default -> match get_dlang_default e an with | Some x -> x @@ -655,12 +752,6 @@ let from_json_class_argument (single_esc json_name)); ]; Line (sprintf "else record.%s = %s;" dlang_name else_body);] - (* sprintf "obj.%s = (\"%s\" in x) ? %s(x[\"%s\"]) : %s;" - dlang_name - (single_esc json_name) - (json_reader env e) - (single_esc json_name) - else_body *) let inst_var_declaration env trans_meth ((loc, (name, kind, an), e) : simple_field) = @@ -757,8 +848,11 @@ let record env loc name (fields : field list) an = Inline to_json_string; ]); Line ("};"); - Line ""; - + Line (sprintf "namespace typedefs {"); + Block [ + Line (sprintf "typedef %s %s;" dlang_struct_name dlang_struct_name) + ]; + Line "}"; ] let alias_wrapper env name type_expr = diff --git a/atdcpp/test/atd-input/everything.atd b/atdcpp/test/atd-input/everything.atd index d766995a0..9d4ff7294 100644 --- a/atdcpp/test/atd-input/everything.atd +++ b/atdcpp/test/atd-input/everything.atd @@ -7,18 +7,11 @@ type kind = [ | Amaze of string list ] -type root = { - -} - - type frozen = [ | A | B of int ] -(* - type ('a, 'b) parametrized_record = { field_a: 'a; ~field_b: 'b list; @@ -50,10 +43,10 @@ type root = { untyped_things: abstract list; parametrized_record: (int, float) parametrized_record; parametrized_tuple: kind parametrized_tuple; - wrapped: st wrap ; + wrapped: st wrap ; aaa: alias_of_alias_of_alias; } -*) + type st = int type alias = int list From 4c45db7f2c56c528dbfcc4b775fc5a0fa908f024 Mon Sep 17 00:00:00 2001 From: Alexandre Bourquelot Date: Tue, 27 Feb 2024 17:07:29 +0100 Subject: [PATCH 15/47] atdcpp: make everything in root compile --- atdcpp/src/lib/Codegen.ml | 107 ++++++++++++++++++++++----- atdcpp/test/atd-input/everything.atd | 9 +-- 2 files changed, 92 insertions(+), 24 deletions(-) diff --git a/atdcpp/src/lib/Codegen.ml b/atdcpp/src/lib/Codegen.ml index b9d44372c..984a868ef 100644 --- a/atdcpp/src/lib/Codegen.ml +++ b/atdcpp/src/lib/Codegen.ml @@ -305,10 +305,10 @@ auto _atd_read_object_to_tuple_list(F read_func, const rapidjson::Value &val) throw AtdException("Expected an object"); // Or your specific exception type } - std::vector> result; + std::vector> result; for (auto &m : val.GetObject()) { - result.push_back(std::make_pair(m.name.GetString(), read_func(m.value))); + result.push_back(std::make_tuple(m.name.GetString(), read_func(m.value))); } return result; @@ -415,6 +415,71 @@ void _atd_write_array(F write_func, const V& values, rapidjson::Writer +void _atd_write_tuple_list_to_object(F write_func, const V &values, rapidjson::Writer& writer) +{ + writer.StartObject(); + for (const auto& value : values) + { + writer.Key(std::get<0>(value).c_str()); + write_func(std::get<1>(value), writer); + } + writer.EndObject(); +} + +template +void _atd_write_assoc_dict_to_array(const Wk write_key_func, const Wv write_value_func, const Map &value_map, rapidjson::Writer& writer) +{ + writer.StartArray(); + for (const auto& pair : value_map) + { + writer.StartArray(); + write_key_func(pair.first, writer); + write_value_func(pair.second, writer); + writer.EndArray(); + } + writer.EndArray(); +} + +template +void _atd_write_assoc_array_to_object(F write_func, const Map &value_map, rapidjson::Writer& writer) +{ + writer.StartObject(); + for (const auto& pair : value_map) + { + writer.Key(pair.first.c_str()); + write_func(pair.second, writer); + } + writer.EndObject(); +} + + +template +void _atd_write_option(F write_func, const O &val, rapidjson::Writer& writer) +{ + if (val) + { + write_func(*val, writer); + } + else + { + writer.Null(); + } +} + +template +void _atd_write_nullable(F write_func, const O &val, rapidjson::Writer& writer) +{ + if (val) + { + write_func(*val, writer); + } + else + { + writer.Null(); + } +} + template void _atd_write_wrap(F write_func, W wrap_func, const T &val, rapidjson::Writer& writer) { @@ -514,8 +579,8 @@ let rec type_name_of_expr env (e : type_expr) : string = (type_name_of_expr env e) | Array_dict (key, value) -> sprintf "std::map<%s, %s>" - (type_name_of_expr env value) (type_name_of_expr env key) + (type_name_of_expr env value) | Object_dict value -> sprintf "std::map" (type_name_of_expr env value) @@ -593,19 +658,19 @@ let rec json_writer ?(nested=false) env e = | Array_list -> sprintf "_atd_write_array([](auto v, auto &w){%sv, w);}, " (json_writer ~nested:true env e) | Array_dict (key, value) -> - sprintf "_atd_write_assoc_dict_to_array<%s, %s>" + sprintf "_atd_write_assoc_dict_to_array([](auto v, auto &w){%sv, w);}, [](auto v, auto &w){%sv, w);}, " (json_writer ~nested:true env key) (json_writer ~nested:true env value) | Object_dict value -> - sprintf "_atd_write_assoc_array_to_object<%s>" + sprintf "_atd_write_assoc_array_to_object([](auto v, auto &w){%sv, w);}, " (json_writer ~nested:true env value) | Object_list value -> - sprintf "_atd_write_tuple_list_to_object<%s>" + sprintf "_atd_write_tuple_list_to_object([](auto v, auto &w){%sv, w);}, " (json_writer ~nested:true env value) ) | Option (loc, e, an) -> - sprintf "_atd_write_option<%s>"(json_writer ~nested:true env e) + sprintf "_atd_write_option([](auto v, auto &w){%sv, w);}, "(json_writer ~nested:true env e) | Nullable (loc, e, an) -> - sprintf "_atd_write_nullable<%s>" (json_writer ~nested:true env e) + sprintf "_atd_write_nullable([](auto v, auto &w){%sv, w);}, " (json_writer ~nested:true env e) | Shared (loc, e, an) -> not_implemented loc "shared" | Wrap (loc, e, an) -> (match Dlang_annot.get_dlang_wrap loc an with @@ -616,7 +681,7 @@ let rec json_writer ?(nested=false) env e = | Name (loc, (loc2, name, []), an) -> (match name with | "bool" | "int" | "float" | "string" -> sprintf "_atd_write_%s(" name - | "abstract" -> "(JSONValue x) => x" + | "abstract" -> not_implemented loc "abstract" | _ -> let dtype_name = (dlang_type_name env name) in sprintf "%s::to_json(" dtype_name) | Name (loc, _, _) -> not_implemented loc "parametrized types" @@ -652,13 +717,16 @@ let construct_json_field env trans_meth | With_default -> assignment | Optional -> [ - Line (sprintf "if (!obj.%s.isNull)" + Line (sprintf "if (t.%s != std::nullopt) {" (inst_var_name trans_meth name)); - Block [ Line(sprintf "res[\"%s\"] = %s(%s)(obj.%s);" - (Atd.Json.get_json_fname name an |> single_esc) - "_atd_write_option!" + Block [ + Line (sprintf "writer.Key(\"%s\");" + (Atd.Json.get_json_fname name an |> single_esc)); + Line(sprintf "%s([](const auto &v, auto &w){%sv, w);}, t.%s, writer);" + "_atd_write_option" (json_writer ~nested:true env unwrapped_type) (inst_var_name trans_meth name))]; + Line "}"; ] (* @@ -702,7 +770,7 @@ let rec json_reader ?(nested=false) env (e : type_expr) = | Name (loc, (loc2, name, []), an) -> (match name with | "bool" | "int" | "float" | "string" -> sprintf "_atd_read_%s(" name - | "abstract" -> "((JSONValue x) => x)" + | "abstract" -> not_implemented loc "abstract" | _ -> sprintf "%s::from_json(" (struct_name env name) ) @@ -839,6 +907,12 @@ let record env loc name (fields : field list) an = ] in [ + Line (sprintf "struct %s;" dlang_struct_name); + Line (sprintf "namespace typedefs {"); + Block [ + Line (sprintf "typedef %s %s;" dlang_struct_name dlang_struct_name) + ]; + Line "}"; Line (sprintf "struct %s {" dlang_struct_name); Block (spaced [ Inline inst_var_declarations; @@ -848,11 +922,6 @@ let record env loc name (fields : field list) an = Inline to_json_string; ]); Line ("};"); - Line (sprintf "namespace typedefs {"); - Block [ - Line (sprintf "typedef %s %s;" dlang_struct_name dlang_struct_name) - ]; - Line "}"; ] let alias_wrapper env name type_expr = diff --git a/atdcpp/test/atd-input/everything.atd b/atdcpp/test/atd-input/everything.atd index 9d4ff7294..eaad1775d 100644 --- a/atdcpp/test/atd-input/everything.atd +++ b/atdcpp/test/atd-input/everything.atd @@ -40,10 +40,10 @@ type root = { assoc4: (string * int) list ; nullables: int nullable list; options: int option list; - untyped_things: abstract list; + (* untyped_things: abstract list; *) parametrized_record: (int, float) parametrized_record; parametrized_tuple: kind parametrized_tuple; - wrapped: st wrap ; + wrapped: st wrap ; aaa: alias_of_alias_of_alias; } @@ -92,8 +92,7 @@ type default_list = { ~items: int list; } -(* type record_with_wrapped_type = { - item: string wrap ; -} *) + item: string wrap ; +} From 26cb4499bbb71fd0198f4d36ad06d012c29c4968 Mon Sep 17 00:00:00 2001 From: Alexandre Bourquelot Date: Tue, 27 Feb 2024 17:28:30 +0100 Subject: [PATCH 16/47] atdcpp: test root --- atdcpp/test/atd-input/everything.atd | 1 + atdcpp/test_main.cpp | 43 ++++++++++++++++++++++++++++ 2 files changed, 44 insertions(+) diff --git a/atdcpp/test/atd-input/everything.atd b/atdcpp/test/atd-input/everything.atd index eaad1775d..d4afd44d1 100644 --- a/atdcpp/test/atd-input/everything.atd +++ b/atdcpp/test/atd-input/everything.atd @@ -45,6 +45,7 @@ type root = { parametrized_tuple: kind parametrized_tuple; wrapped: st wrap ; aaa: alias_of_alias_of_alias; + item: string wrap ; } diff --git a/atdcpp/test_main.cpp b/atdcpp/test_main.cpp index b8c17032b..52de8b175 100644 --- a/atdcpp/test_main.cpp +++ b/atdcpp/test_main.cpp @@ -2,6 +2,11 @@ #include #include +#include +#include +#include +#include +#include const rapidjson::Document doc_from_json(const std::string &json) { @@ -17,7 +22,45 @@ const rapidjson::Document doc_from_json(const std::string &json) int main() { + Root root; + + root.id = "id long"; + root.await = false; + root.integer = 43; + root.x___init__ = 3.14; + root.float_with_auto_default = 90.03; + root.float_with_default = 32.1; + root.items = {{1, 2}, {-1, -2}}; + root.maybe = 422; + root.extras = {34, 12}; + root.answer = 12; + root.aliased = {55, 44}; + root.point = {4.4, 1.1}; + root.kind = Kind::Types::Root(); + root.kinds = {Kind::Types::Amaze({{"one", "two"}}), Kind::Types::Root(), Kind::Types::Root(), Kind::Types::Thing({1})}; + root.assoc1 = {{4.12, 1},{2.2, 2}}; + root.assoc2 = {{"first", 1}, {"second", 2}}; + root.assoc3 = {{1.1, 1}, {2.2, 2}}; + root.assoc4 = {{"firstt", 1}, {"secondd", 2}}; + root.nullables = {1, std::nullopt, 3}; + root.options = {1, 2, std::nullopt}; + root.parametrized_record = {2, {1.0, 1.1}}; + root.parametrized_tuple = {Kind::Types::Root(), Kind::Types::WOW(), 9}; + root.wrapped = 1; + root.aaa = -90; + root.item = 45; + + std::string json = root.to_json_string(); + + // now you turn json into pretty json string + rapidjson::Document doc; + doc.Parse(json.c_str()); + rapidjson::StringBuffer buffer; + rapidjson::PrettyWriter writer(buffer); + doc.Accept(writer); + std::cout << "Root: " << buffer.GetString() << std::endl; + std::cout << "Root: " << json << std::endl; // Credential credential = Credential::from_json(doc_from_json(R"({"name":"user","password":1234})")); // std::cout << "Credential: " << credential.to_json_string() << std::endl; From c7fce0104f10f737aec5b32ee02f66cf227a5c17 Mon Sep 17 00:00:00 2001 From: Alexandre Bourquelot Date: Tue, 27 Feb 2024 17:41:08 +0100 Subject: [PATCH 17/47] atdcpp: change how we handle optional --- atdcpp/src/lib/Codegen.ml | 23 +++++++++++++++++------ 1 file changed, 17 insertions(+), 6 deletions(-) diff --git a/atdcpp/src/lib/Codegen.ml b/atdcpp/src/lib/Codegen.ml index 984a868ef..a5cf94a10 100644 --- a/atdcpp/src/lib/Codegen.ml +++ b/atdcpp/src/lib/Codegen.ml @@ -371,17 +371,25 @@ auto _atd_read_nullable(F read_func, const rapidjson::Value &val) template auto _atd_read_option(F read_func, const rapidjson::Value &val) { - if (val.IsNull()) + using ResultType = typename std::invoke_result::type; + if (val.IsString() && std::string_view(val.GetString()) == "None") { - return std::optional::type>(); + return std::optional(); + } + else if (val.IsArray() && val.Size() == 2 && val[0].IsString() && std::string_view(val[0].GetString()) == "Some") + { + return std::make_optional(read_func(val[1])); + } + else + { + throw AtdException("Expected an option"); } - return std::optional::type>(read_func(val)); } template -auto _atd_read_wrap(F read_func, W unwrap_func, const rapidjson::Value &val) +auto _atd_read_wrap(F read_func, W wrap_func, const rapidjson::Value &val) { - return unwrap_func(read_func(val)); + return wrap_func(read_func(val)); } void _atd_write_int(int value, rapidjson::Writer& writer) @@ -459,11 +467,14 @@ void _atd_write_option(F write_func, const O &val, rapidjson::Writer Date: Tue, 27 Feb 2024 17:52:19 +0100 Subject: [PATCH 18/47] atdcpp: fix the test situation --- atdcpp/test/cpp-expected/everything_atd.d | 786 ------------ atdcpp/test/cpp-expected/everything_atd.hpp | 1219 +++++++++++++++++++ atdcpp/test/dune | 4 +- 3 files changed, 1221 insertions(+), 788 deletions(-) delete mode 100644 atdcpp/test/cpp-expected/everything_atd.d create mode 100644 atdcpp/test/cpp-expected/everything_atd.hpp diff --git a/atdcpp/test/cpp-expected/everything_atd.d b/atdcpp/test/cpp-expected/everything_atd.d deleted file mode 100644 index 97ea1d952..000000000 --- a/atdcpp/test/cpp-expected/everything_atd.d +++ /dev/null @@ -1,786 +0,0 @@ - -// Generated by atdcpp from type definitions in everything.atd. -// This implements classes for the types defined in 'everything.atd', providing -// methods and functions to convert data from/to JSON. - -// ############################################################################ -// # Private functions -// ############################################################################ - -module everything_atd; - -import std.algorithm : map; -import std.array : array; -import std.conv; -import std.format; -import std.json; -import std.sumtype; -import std.traits : isCallable, ReturnType; -import std.typecons : nullable, Nullable, tuple, Tuple; - -private -{ - - class AtdException : Exception - { - @safe this(string msg, string file = __FILE__, size_t line = __LINE__) - { - super(msg, file, line); - } - } - - // workaround to make toDelegate callable from safe - @trusted auto toDelegate(F)(auto ref F fp) if (isCallable!F) - { - import std.functional; - return std.functional.toDelegate(fp); - } - - - @trusted T _atd_missing_json_field(T)(string typeName, string jsonFieldName) - { - throw new AtdException("missing field %s in JSON object of type %s".format(jsonFieldName, typeName)); - } - - auto _atd_bad_json(T)(string expectedType, T jsonValue) - { - string valueStr = jsonValue.to!string; - if (valueStr.length > 200) - { - valueStr = valueStr[0 .. 200]; - } - - return new AtdException( - "incompatible JSON value where type '%s' was expected: %s".format( - expectedType, valueStr - )); - } - - auto _atd_bad_d(T)(string expectedType, T jsonValue) - { - string valueStr = jsonValue.to!string; - if (valueStr.length > 200) - { - valueStr = valueStr[0 .. 200]; - } - - return new AtdException( - "incompatible D value where type '%s' was expected: %s".format( - expectedType, valueStr - )); - } - - auto _atd_read_unit(JSONValue x) - { - if (x.isNull) - return null; - else - throw _atd_bad_json("unit", x); - } - - auto _atd_read_bool(JSONValue x) - { - try - return x.boolean; - catch (JSONException e) - throw _atd_bad_json("bool", x); - } - - auto _atd_read_int(JSONValue x) - { - try - return cast(int) x.integer; - catch (JSONException e) - throw _atd_bad_json("int", x); - } - - auto _atd_read_float(JSONValue x) - { - try - return cast(float) x.floating; - catch (JSONException e) - throw _atd_bad_json("float", x); - } - - auto _atd_read_string(JSONValue x) - { - try - return x.str; - catch (JSONException e) - throw _atd_bad_json("string", x); - } - - template _atd_read_list(alias readElements) - { - auto _atd_read_list(JSONValue jsonVal) - { - if (jsonVal.type != JSONType.array) - throw _atd_bad_json("array", jsonVal); - auto list = jsonVal.array; - return array(list.map!readElements()); - } - } - - template _atd_read_object_to_assoc_array(alias readValue) - { - auto _atd_read_object_to_assoc_array(JSONValue jsonVal) - { - alias T = ReturnType!readValue; - - if (jsonVal.type != JSONType.object) - throw _atd_bad_json("object", jsonVal); - T[string] ret; - foreach (key, val; jsonVal.object) - ret[key] = readValue(val); - return ret; - } - } - - template _atd_read_array_to_assoc_dict(alias readKey, alias readValue) - { - auto _atd_read_array_to_assoc_dict(JSONValue jsonVal) - { - alias K = ReturnType!readKey; - alias V = ReturnType!readValue; - - if (jsonVal.type != JSONType.array) - throw _atd_bad_json("list", jsonVal); - V[K] ret; - foreach (jsonInnerVal; jsonVal.array) - { - if (jsonInnerVal.type != JSONType.array) - throw _atd_bad_json("list", jsonInnerVal); - ret[readKey(jsonInnerVal[0])] = readValue(jsonInnerVal[1]); - } - return ret; - } - } - - template _atd_read_object_to_tuple_list(alias readValue) - { - auto _atd_read_object_to_tuple_list(JSONValue jsonVal) - { - alias T = ReturnType!readValue; - - if (jsonVal.type != JSONType.object) - throw _atd_bad_json("object", jsonVal); - auto tupList = new Tuple!(string, T)[](jsonVal.object.length); - int i = 0; - foreach (key, val; jsonVal.object) - tupList[i++] = tuple(key, readValue(val)); - return tupList; - } - } - - template _atd_read_nullable(alias readElm) - { - auto _atd_read_nullable(JSONValue e) - { - alias T = ReturnType!readElm; - - if (e.isNull) - return Nullable!T.init; - else - return Nullable!T(readElm(e)); - } - } - - template _atd_read_option(alias readElm) - { - auto _atd_read_option(JSONValue e) - { - alias T = ReturnType!readElm; - - if (e.type == JSONType.string && e.str == "None") - return Nullable!T.init; - else if (e.type == JSONType.array && e.array.length == 2 && e[0].type == JSONType.string && e[0].str == "Some") - return Nullable!T(readElm(e[1])); - else - throw _atd_bad_json("option", e); - } - } - - template _atd_read_wrap(alias readElm, alias wrap) - { - auto _atd_read_wrap(JSONValue e) - { - return wrap(readElm(e)); - } - } - - // this whole set of function could be remplaced by one templated _atd_write_value function - // not sure it is what we want though - - auto _atd_write_unit(typeof(null) n) - { - return JSONValue(null); - } - - auto _atd_write_bool(bool b) - { - return JSONValue(b); - } - - auto _atd_write_int(int i) - { - return JSONValue(i); - } - - auto _atd_write_float(float f) - { - return JSONValue(f); - } - - auto _atd_write_string(string s) - { - return JSONValue(s); - } - - template _atd_write_list(alias writeElm) - { - auto _atd_write_list(T)(T[] list) - { - return JSONValue(array(list.map!writeElm())); - } - } - - template _atd_write_assoc_array_to_object(alias writeValue) - { - auto _atd_write_assoc_array_to_object(T)(T[string] assocArr) - { - JSONValue[string] ret; - foreach (key, val; assocArr) - ret[key] = writeValue(val); - return JSONValue(ret); - } - } - - template _atd_write_assoc_dict_to_array(alias writeKey, alias writeValue) - { - auto _atd_write_assoc_dict_to_array(K, V)(V[K] assocArr) - { - JSONValue[] ret; - foreach (key, val; assocArr) - ret ~= JSONValue([writeKey(key), writeValue(val)]); - return JSONValue(ret); - } - } - - template _atd_write_tuple_list_to_object(alias writeValue) - { - auto _atd_write_tuple_list_to_object(T)(Tuple!(string, T)[] tupList) - { - JSONValue[string] ret; - foreach (tup; tupList) - ret[tup[0]] = writeValue(tup[1]); - return JSONValue(ret); - } - } - - template _atd_write_nullable(alias writeElm) - { - auto _atd_write_nullable(T)(Nullable!T elm) - { - if (elm.isNull) - return JSONValue(null); - else - return writeElm(elm.get); - } - } - - template _atd_write_option(alias writeElm) - { - auto _atd_write_option(T)(Nullable!T elm) - { - if (elm.isNull) - return JSONValue("None"); - else - return JSONValue([JSONValue("Some"), writeElm(elm.get)]); - } - } - - template _atd_write_wrap(alias writeElm, alias unwrap) - { - auto _atd_write_wrap(Wrapped)(Wrapped e) - { - return writeElm(unwrap(e)); - } - } -} - -// ############################################################################ -// # Public classes -// ############################################################################ - -auto fromJsonString(T)(string s) -{ - JSONValue res = parseJSON(s); - return res.fromJson!T; -} - -auto toJsonString(T)(T obj) -{ - JSONValue res = obj.toJson!T; - return res.toString; -} - - - - -import std.stdint : uint32_t, uint16_t; - - -struct RecursiveClass { - int id; - bool flag; - RecursiveClass[] children; -} - -@trusted RecursiveClass fromJson(T : RecursiveClass)(JSONValue x) { - RecursiveClass obj; - obj.id = ("id" in x) ? _atd_read_int(x["id"]) : _atd_missing_json_field!(typeof(obj.id))("RecursiveClass", "id"); - obj.flag = ("flag" in x) ? _atd_read_bool(x["flag"]) : _atd_missing_json_field!(typeof(obj.flag))("RecursiveClass", "flag"); - obj.children = ("children" in x) ? _atd_read_list!(fromJson!RecursiveClass)(x["children"]) : _atd_missing_json_field!(typeof(obj.children))("RecursiveClass", "children"); - return obj; -} -@trusted JSONValue toJson(T : RecursiveClass)(T obj) { - JSONValue res; - res["id"] = _atd_write_int(obj.id); - res["flag"] = _atd_write_bool(obj.flag); - res["children"] = _atd_write_list!(((RecursiveClass x) => x.toJson!(RecursiveClass)))(obj.children); - return res; -} - - -struct St{int _data; alias _data this; -this(int init) @safe {_data = init;} -this(St init) @safe {_data = init._data;} - -} -@trusted JSONValue toJson(T : St)(St e) { - return _atd_write_int(e); -} -@trusted St fromJson(T : St)(JSONValue e) { - return St(_atd_read_int(e)); -} - - -// Original type: kind = [ ... | Root | ... ] -struct Root_ {} -@trusted JSONValue toJson(T : Root_)(T e) { - return JSONValue("Root"); -} - - -// Original type: kind = [ ... | Thing of ... | ... ] -struct Thing { int value; } -@trusted JSONValue toJson(T : Thing)(T e) { - return JSONValue([JSONValue("Thing"), _atd_write_int(e.value)]); -} - - -// Original type: kind = [ ... | WOW | ... ] -struct WOW {} -@trusted JSONValue toJson(T : WOW)(T e) { - return JSONValue("wow"); -} - - -// Original type: kind = [ ... | Amaze of ... | ... ] -struct Amaze { string[] value; } -@trusted JSONValue toJson(T : Amaze)(T e) { - return JSONValue([JSONValue("!!!"), _atd_write_list!(_atd_write_string)(e.value)]); -} - - -struct Kind{ SumType!(Root_, Thing, WOW, Amaze) _data; alias _data this; -@safe this(T)(T init) {_data = init;} @safe this(Kind init) {_data = init._data;}} - -@trusted Kind fromJson(T : Kind)(JSONValue x) { - if (x.type == JSONType.string) { - if (x.str == "Root") - return Kind(Root_()); - if (x.str == "wow") - return Kind(WOW()); - throw _atd_bad_json("Kind", x); - } - if (x.type == JSONType.array && x.array.length == 2 && x[0].type == JSONType.string) { - string cons = x[0].str; - if (cons == "Thing") - return Kind(Thing(_atd_read_int(x[1]))); - if (cons == "!!!") - return Kind(Amaze(_atd_read_list!(_atd_read_string)(x[1]))); - throw _atd_bad_json("Kind", x); - } - throw _atd_bad_json("Kind", x); -} - -@trusted JSONValue toJson(T : Kind)(T x) { - return x.match!( - (Root_ v) => v.toJson!(Root_), -(Thing v) => v.toJson!(Thing), -(WOW v) => v.toJson!(WOW), -(Amaze v) => v.toJson!(Amaze) - ); -} - - -struct Alias3{uint32_t _data; alias _data this; -this(uint32_t init) @safe {_data = init;} -this(Alias3 init) @safe {_data = init._data;} - -} -@trusted JSONValue toJson(T : Alias3)(Alias3 e) { - return _atd_write_wrap!(_atd_write_int, (uint32_t e) => to!int(e))(e); -} -@trusted Alias3 fromJson(T : Alias3)(JSONValue e) { - return Alias3(_atd_read_wrap!(_atd_read_int, (int e) => to!uint32_t(e))(e)); -} - - -struct AliasOfAliasNotWrapped{Alias3 _data; alias _data this; -this(Alias3 init) @safe {_data = init;} -this(AliasOfAliasNotWrapped init) @safe {_data = init._data;} - -} -@trusted JSONValue toJson(T : AliasOfAliasNotWrapped)(AliasOfAliasNotWrapped e) { - return ((Alias3 x) => x.toJson!(Alias3))(e); -} -@trusted AliasOfAliasNotWrapped fromJson(T : AliasOfAliasNotWrapped)(JSONValue e) { - return AliasOfAliasNotWrapped(fromJson!Alias3(e)); -} - - -struct AliasOfAliasOfAlias{AliasOfAliasNotWrapped _data; alias _data this; -this(AliasOfAliasNotWrapped init) @safe {_data = init;} -this(AliasOfAliasOfAlias init) @safe {_data = init._data;} - -} -@trusted JSONValue toJson(T : AliasOfAliasOfAlias)(AliasOfAliasOfAlias e) { - return ((AliasOfAliasNotWrapped x) => x.toJson!(AliasOfAliasNotWrapped))(e); -} -@trusted AliasOfAliasOfAlias fromJson(T : AliasOfAliasOfAlias)(JSONValue e) { - return AliasOfAliasOfAlias(fromJson!AliasOfAliasNotWrapped(e)); -} - - -struct Alias{int[] _data; alias _data this; -this(int[] init) @safe {_data = init;} -this(Alias init) @safe {_data = init._data;} - -} -@trusted JSONValue toJson(T : Alias)(Alias e) { - return _atd_write_list!(_atd_write_int)(e); -} -@trusted Alias fromJson(T : Alias)(JSONValue e) { - return Alias(_atd_read_list!(_atd_read_int)(e)); -} - - -struct KindParametrizedTuple{Tuple!(Kind, Kind, int) _data; alias _data this; -this(Tuple!(Kind, Kind, int) init) @safe {_data = init;} -this(KindParametrizedTuple init) @safe {_data = init._data;} -this(T...)(T args) @safe {_data = tuple(args);} -} -@trusted JSONValue toJson(T : KindParametrizedTuple)(KindParametrizedTuple e) { - return ((Tuple!(Kind, Kind, int) x) => JSONValue([((Kind x) => x.toJson!(Kind))(x[0]), ((Kind x) => x.toJson!(Kind))(x[1]), _atd_write_int(x[2])]))(e); -} -@trusted KindParametrizedTuple fromJson(T : KindParametrizedTuple)(JSONValue e) { - return KindParametrizedTuple(((JSONValue x) @trusted { - if (x.type != JSONType.array || x.array.length != 3) - throw _atd_bad_json("Tuple of size 3", x); - return tuple(fromJson!Kind(x[0]), fromJson!Kind(x[1]), _atd_read_int(x[2])); - })(e)); -} - - -struct IntFloatParametrizedRecord { - int field_a; - float[] field_b = []; -} - -@trusted IntFloatParametrizedRecord fromJson(T : IntFloatParametrizedRecord)(JSONValue x) { - IntFloatParametrizedRecord obj; - obj.field_a = ("field_a" in x) ? _atd_read_int(x["field_a"]) : _atd_missing_json_field!(typeof(obj.field_a))("IntFloatParametrizedRecord", "field_a"); - obj.field_b = ("field_b" in x) ? _atd_read_list!(_atd_read_float)(x["field_b"]) : []; - return obj; -} -@trusted JSONValue toJson(T : IntFloatParametrizedRecord)(T obj) { - JSONValue res; - res["field_a"] = _atd_write_int(obj.field_a); - res["field_b"] = _atd_write_list!(_atd_write_float)(obj.field_b); - return res; -} - - -struct Root { - string id; - bool await; - int integer; - float x___init__; - float float_with_auto_default = 0.0; - float float_with_default = 0.1; - int[][] items; - Nullable!int maybe; - int[] extras = []; - int answer = 42; - Alias aliased; - Tuple!(float, float) point; - Kind kind; - Kind[] kinds; - Tuple!(float, int)[] assoc1; - Tuple!(string, int)[] assoc2; - int[float] assoc3; - int[string] assoc4; - Nullable!int[] nullables; - Nullable!int[] options; - JSONValue[] untyped_things; - IntFloatParametrizedRecord parametrized_record; - KindParametrizedTuple parametrized_tuple; - uint16_t wrapped; - AliasOfAliasOfAlias aaa; -} - -@trusted Root fromJson(T : Root)(JSONValue x) { - Root obj; - obj.id = ("ID" in x) ? _atd_read_string(x["ID"]) : _atd_missing_json_field!(typeof(obj.id))("Root", "ID"); - obj.await = ("await" in x) ? _atd_read_bool(x["await"]) : _atd_missing_json_field!(typeof(obj.await))("Root", "await"); - obj.integer = ("integer" in x) ? _atd_read_int(x["integer"]) : _atd_missing_json_field!(typeof(obj.integer))("Root", "integer"); - obj.x___init__ = ("__init__" in x) ? _atd_read_float(x["__init__"]) : _atd_missing_json_field!(typeof(obj.x___init__))("Root", "__init__"); - obj.float_with_auto_default = ("float_with_auto_default" in x) ? _atd_read_float(x["float_with_auto_default"]) : 0.0; - obj.float_with_default = ("float_with_default" in x) ? _atd_read_float(x["float_with_default"]) : 0.1; - obj.items = ("items" in x) ? _atd_read_list!(_atd_read_list!(_atd_read_int))(x["items"]) : _atd_missing_json_field!(typeof(obj.items))("Root", "items"); - obj.maybe = ("maybe" in x) ? _atd_read_option!(_atd_read_int)(x["maybe"]) : typeof(obj.maybe).init; - obj.extras = ("extras" in x) ? _atd_read_list!(_atd_read_int)(x["extras"]) : []; - obj.answer = ("answer" in x) ? _atd_read_int(x["answer"]) : 42; - obj.aliased = ("aliased" in x) ? fromJson!Alias(x["aliased"]) : _atd_missing_json_field!(typeof(obj.aliased))("Root", "aliased"); - obj.point = ("point" in x) ? ((JSONValue x) @trusted { - if (x.type != JSONType.array || x.array.length != 2) - throw _atd_bad_json("Tuple of size 2", x); - return tuple(_atd_read_float(x[0]), _atd_read_float(x[1])); - })(x["point"]) : _atd_missing_json_field!(typeof(obj.point))("Root", "point"); - obj.kind = ("kind" in x) ? fromJson!Kind(x["kind"]) : _atd_missing_json_field!(typeof(obj.kind))("Root", "kind"); - obj.kinds = ("kinds" in x) ? _atd_read_list!(fromJson!Kind)(x["kinds"]) : _atd_missing_json_field!(typeof(obj.kinds))("Root", "kinds"); - obj.assoc1 = ("assoc1" in x) ? _atd_read_list!(((JSONValue x) @trusted { - if (x.type != JSONType.array || x.array.length != 2) - throw _atd_bad_json("Tuple of size 2", x); - return tuple(_atd_read_float(x[0]), _atd_read_int(x[1])); - }))(x["assoc1"]) : _atd_missing_json_field!(typeof(obj.assoc1))("Root", "assoc1"); - obj.assoc2 = ("assoc2" in x) ? _atd_read_object_to_tuple_list!(_atd_read_int)(x["assoc2"]) : _atd_missing_json_field!(typeof(obj.assoc2))("Root", "assoc2"); - obj.assoc3 = ("assoc3" in x) ? _atd_read_array_to_assoc_dict!(_atd_read_float, _atd_read_int)(x["assoc3"]) : _atd_missing_json_field!(typeof(obj.assoc3))("Root", "assoc3"); - obj.assoc4 = ("assoc4" in x) ? _atd_read_object_to_assoc_array!(_atd_read_int)(x["assoc4"]) : _atd_missing_json_field!(typeof(obj.assoc4))("Root", "assoc4"); - obj.nullables = ("nullables" in x) ? _atd_read_list!(_atd_read_nullable!(_atd_read_int))(x["nullables"]) : _atd_missing_json_field!(typeof(obj.nullables))("Root", "nullables"); - obj.options = ("options" in x) ? _atd_read_list!(_atd_read_option!(_atd_read_int))(x["options"]) : _atd_missing_json_field!(typeof(obj.options))("Root", "options"); - obj.untyped_things = ("untyped_things" in x) ? _atd_read_list!(((JSONValue x) => x))(x["untyped_things"]) : _atd_missing_json_field!(typeof(obj.untyped_things))("Root", "untyped_things"); - obj.parametrized_record = ("parametrized_record" in x) ? fromJson!IntFloatParametrizedRecord(x["parametrized_record"]) : _atd_missing_json_field!(typeof(obj.parametrized_record))("Root", "parametrized_record"); - obj.parametrized_tuple = ("parametrized_tuple" in x) ? fromJson!KindParametrizedTuple(x["parametrized_tuple"]) : _atd_missing_json_field!(typeof(obj.parametrized_tuple))("Root", "parametrized_tuple"); - obj.wrapped = ("wrapped" in x) ? _atd_read_wrap!(fromJson!St, (St e) => ((St st) => st.to!int.to!uint16_t)(e))(x["wrapped"]) : _atd_missing_json_field!(typeof(obj.wrapped))("Root", "wrapped"); - obj.aaa = ("aaa" in x) ? fromJson!AliasOfAliasOfAlias(x["aaa"]) : _atd_missing_json_field!(typeof(obj.aaa))("Root", "aaa"); - return obj; -} -@trusted JSONValue toJson(T : Root)(T obj) { - JSONValue res; - res["ID"] = _atd_write_string(obj.id); - res["await"] = _atd_write_bool(obj.await); - res["integer"] = _atd_write_int(obj.integer); - res["__init__"] = _atd_write_float(obj.x___init__); - res["float_with_auto_default"] = _atd_write_float(obj.float_with_auto_default); - res["float_with_default"] = _atd_write_float(obj.float_with_default); - res["items"] = _atd_write_list!(_atd_write_list!(_atd_write_int))(obj.items); - if (!obj.maybe.isNull) - res["maybe"] = _atd_write_option!(_atd_write_int)(obj.maybe); - res["extras"] = _atd_write_list!(_atd_write_int)(obj.extras); - res["answer"] = _atd_write_int(obj.answer); - res["aliased"] = ((Alias x) => x.toJson!(Alias))(obj.aliased); - res["point"] = ((Tuple!(float, float) x) => JSONValue([_atd_write_float(x[0]), _atd_write_float(x[1])]))(obj.point); - res["kind"] = ((Kind x) => x.toJson!(Kind))(obj.kind); - res["kinds"] = _atd_write_list!(((Kind x) => x.toJson!(Kind)))(obj.kinds); - res["assoc1"] = _atd_write_list!(((Tuple!(float, int) x) => JSONValue([_atd_write_float(x[0]), _atd_write_int(x[1])])))(obj.assoc1); - res["assoc2"] = _atd_write_tuple_list_to_object!(_atd_write_int)(obj.assoc2); - res["assoc3"] = _atd_write_assoc_dict_to_array!(_atd_write_float, _atd_write_int)(obj.assoc3); - res["assoc4"] = _atd_write_assoc_array_to_object!(_atd_write_int)(obj.assoc4); - res["nullables"] = _atd_write_list!(_atd_write_nullable!(_atd_write_int))(obj.nullables); - res["options"] = _atd_write_list!(_atd_write_option!(_atd_write_int))(obj.options); - res["untyped_things"] = _atd_write_list!((JSONValue x) => x)(obj.untyped_things); - res["parametrized_record"] = ((IntFloatParametrizedRecord x) => x.toJson!(IntFloatParametrizedRecord))(obj.parametrized_record); - res["parametrized_tuple"] = ((KindParametrizedTuple x) => x.toJson!(KindParametrizedTuple))(obj.parametrized_tuple); - res["wrapped"] = _atd_write_wrap!(((St x) => x.toJson!(St)), (uint16_t e) => ((uint16_t e) => St(e.to!int))(e))(obj.wrapped); - res["aaa"] = ((AliasOfAliasOfAlias x) => x.toJson!(AliasOfAliasOfAlias))(obj.aaa); - return res; -} - - -struct RequireField { - string req; -} - -@trusted RequireField fromJson(T : RequireField)(JSONValue x) { - RequireField obj; - obj.req = ("req" in x) ? _atd_read_string(x["req"]) : _atd_missing_json_field!(typeof(obj.req))("RequireField", "req"); - return obj; -} -@trusted JSONValue toJson(T : RequireField)(T obj) { - JSONValue res; - res["req"] = _atd_write_string(obj.req); - return res; -} - - -struct RecordWithWrappedType { - int item; -} - -@trusted RecordWithWrappedType fromJson(T : RecordWithWrappedType)(JSONValue x) { - RecordWithWrappedType obj; - obj.item = ("item" in x) ? _atd_read_wrap!(_atd_read_string, (string e) => to!int(e))(x["item"]) : _atd_missing_json_field!(typeof(obj.item))("RecordWithWrappedType", "item"); - return obj; -} -@trusted JSONValue toJson(T : RecordWithWrappedType)(T obj) { - JSONValue res; - res["item"] = _atd_write_wrap!(_atd_write_string, (int e) => to!string(e))(obj.item); - return res; -} - - -struct Password{uint32_t _data; alias _data this; -this(uint32_t init) @safe {_data = init;} -this(Password init) @safe {_data = init._data;} - -} -@trusted JSONValue toJson(T : Password)(Password e) { - return _atd_write_wrap!(_atd_write_int, (uint32_t e) => to!int(e))(e); -} -@trusted Password fromJson(T : Password)(JSONValue e) { - return Password(_atd_read_wrap!(_atd_read_int, (int e) => to!uint32_t(e))(e)); -} - - -struct Pair{Tuple!(string, int) _data; alias _data this; -this(Tuple!(string, int) init) @safe {_data = init;} -this(Pair init) @safe {_data = init._data;} -this(T...)(T args) @safe {_data = tuple(args);} -} -@trusted JSONValue toJson(T : Pair)(Pair e) { - return ((Tuple!(string, int) x) => JSONValue([_atd_write_string(x[0]), _atd_write_int(x[1])]))(e); -} -@trusted Pair fromJson(T : Pair)(JSONValue e) { - return Pair(((JSONValue x) @trusted { - if (x.type != JSONType.array || x.array.length != 2) - throw _atd_bad_json("Tuple of size 2", x); - return tuple(_atd_read_string(x[0]), _atd_read_int(x[1])); - })(e)); -} - - -// Original type: frozen = [ ... | A | ... ] -struct A {} -@trusted JSONValue toJson(T : A)(T e) { - return JSONValue("A"); -} - - -// Original type: frozen = [ ... | B of ... | ... ] -struct B { int value; } -@trusted JSONValue toJson(T : B)(T e) { - return JSONValue([JSONValue("B"), _atd_write_int(e.value)]); -} - - -struct Frozen{ SumType!(A, B) _data; alias _data this; -@safe this(T)(T init) {_data = init;} @safe this(Frozen init) {_data = init._data;}} - -@trusted Frozen fromJson(T : Frozen)(JSONValue x) { - if (x.type == JSONType.string) { - if (x.str == "A") - return Frozen(A()); - throw _atd_bad_json("Frozen", x); - } - if (x.type == JSONType.array && x.array.length == 2 && x[0].type == JSONType.string) { - string cons = x[0].str; - if (cons == "B") - return Frozen(B(_atd_read_int(x[1]))); - throw _atd_bad_json("Frozen", x); - } - throw _atd_bad_json("Frozen", x); -} - -@trusted JSONValue toJson(T : Frozen)(T x) { - return x.match!( - (A v) => v.toJson!(A), -(B v) => v.toJson!(B) - ); -} - - -struct DefaultList { - int[] items = []; -} - -@trusted DefaultList fromJson(T : DefaultList)(JSONValue x) { - DefaultList obj; - obj.items = ("items" in x) ? _atd_read_list!(_atd_read_int)(x["items"]) : []; - return obj; -} -@trusted JSONValue toJson(T : DefaultList)(T obj) { - JSONValue res; - res["items"] = _atd_write_list!(_atd_write_int)(obj.items); - return res; -} - - -struct Credential { - string name; - Password password; -} - -@trusted Credential fromJson(T : Credential)(JSONValue x) { - Credential obj; - obj.name = ("name" in x) ? _atd_read_string(x["name"]) : _atd_missing_json_field!(typeof(obj.name))("Credential", "name"); - obj.password = ("password" in x) ? fromJson!Password(x["password"]) : _atd_missing_json_field!(typeof(obj.password))("Credential", "password"); - return obj; -} -@trusted JSONValue toJson(T : Credential)(T obj) { - JSONValue res; - res["name"] = _atd_write_string(obj.name); - res["password"] = ((Password x) => x.toJson!(Password))(obj.password); - return res; -} - - -struct Credentials{Credential[] _data; alias _data this; -this(Credential[] init) @safe {_data = init;} -this(Credentials init) @safe {_data = init._data;} - -} -@trusted JSONValue toJson(T : Credentials)(Credentials e) { - return _atd_write_list!(((Credential x) => x.toJson!(Credential)))(e); -} -@trusted Credentials fromJson(T : Credentials)(JSONValue e) { - return Credentials(_atd_read_list!(fromJson!Credential)(e)); -} - - -struct AliasOfAlias{uint16_t _data; alias _data this; -this(uint16_t init) @safe {_data = init;} -this(AliasOfAlias init) @safe {_data = init._data;} - -} -@trusted JSONValue toJson(T : AliasOfAlias)(AliasOfAlias e) { - return _atd_write_wrap!(((Alias3 x) => x.toJson!(Alias3)), (uint16_t e) => to!uint32_t(e))(e); -} -@trusted AliasOfAlias fromJson(T : AliasOfAlias)(JSONValue e) { - return AliasOfAlias(_atd_read_wrap!(fromJson!Alias3, (Alias3 e) => to!uint16_t(e))(e)); -} - - -struct Alias2{int[] _data; alias _data this; -this(int[] init) @safe {_data = init;} -this(Alias2 init) @safe {_data = init._data;} - -} -@trusted JSONValue toJson(T : Alias2)(Alias2 e) { - return _atd_write_list!(_atd_write_int)(e); -} -@trusted Alias2 fromJson(T : Alias2)(JSONValue e) { - return Alias2(_atd_read_list!(_atd_read_int)(e)); -} diff --git a/atdcpp/test/cpp-expected/everything_atd.hpp b/atdcpp/test/cpp-expected/everything_atd.hpp new file mode 100644 index 000000000..11fadb878 --- /dev/null +++ b/atdcpp/test/cpp-expected/everything_atd.hpp @@ -0,0 +1,1219 @@ + +// Generated by atdcpp from type definitions in everything.atd. +// This implements classes for the types defined in 'everything.atd', providing +// methods and functions to convert data from/to JSON. + +// ############################################################################ +// # Private functions +// ############################################################################ + +// filename everything.atd +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +class AtdException : public std::exception +{ +public: + AtdException(const std::string &message) : msg_(message) {} + + const char *what() const throw() override + { + return msg_.c_str(); + } + +private: + std::string msg_; +}; + +template +T _atd_missing_json_field(const std::string &type, const std::string &field) +{ + throw AtdException("Missing JSON field '" + field + "' in " + type); +} + +auto _atd_bad_json(const std::string &type, const rapidjson::Value &x) +{ + return AtdException("Bad JSON for " + type); +} + +// Reading an integer from JSON +int _atd_read_int(const rapidjson::Value &val) +{ + if (!val.IsInt()) + { + throw AtdException("Expected an integer"); + } + return val.GetInt(); +} + +bool _atd_read_bool(const rapidjson::Value &val) +{ + if (!val.IsBool()) + { + throw AtdException("Expected a boolean"); + } + return val.GetBool(); +} + +// Reading a float from JSON +float _atd_read_float(const rapidjson::Value &val) +{ + if (val.IsInt()) + { + return static_cast(val.GetInt()); + } + else if (val.IsUint()) + { + return static_cast(val.GetUint()); + } + if (!val.IsFloat()) + { + throw AtdException("Expected a float"); + } + + return val.GetFloat(); +} + +std::string _atd_read_string(const rapidjson::Value &val) +{ + if (!val.IsString()) + { + throw AtdException("Expected a string"); + } + return val.GetString(); +} + +template +auto _atd_read_array(F read_func, const rapidjson::Value &val) +{ + using ResultType = typename std::invoke_result::type; + + if (!val.IsArray()) + { + throw AtdException("Expected an array"); // Or your specific exception type + } + + std::vector result; + for (rapidjson::SizeType i = 0; i < val.Size(); i++) + { + result.push_back(read_func(val[i])); + } + + return result; +} + +template +auto _atd_read_object_to_tuple_list(F read_func, const rapidjson::Value &val) +{ + using ResultType = typename std::invoke_result::type; + + if (!val.IsObject()) + { + throw AtdException("Expected an object"); // Or your specific exception type + } + + std::vector> result; + for (auto &m : val.GetObject()) + { + result.push_back(std::make_tuple(m.name.GetString(), read_func(m.value))); + } + + return result; +} + +template +auto _atd_read_array_to_assoc_dict(RK read_key_func, RV read_value_func, const rapidjson::Value &val) +{ + using KeyType = typename std::invoke_result::type; + using ValueType = typename std::invoke_result::type; + + if (!val.IsArray()) + { + throw AtdException("Expected an array"); // Or your specific exception type + } + + std::map result; + for (rapidjson::SizeType i = 0; i < val.Size(); i++) + { + auto &pair = val[i]; + if (!pair.IsArray() || pair.Size() != 2) + { + throw AtdException("Expected an array of pairs"); + } + result[read_key_func(pair[0])] = read_value_func(pair[1]); + } + + return result; +} + +template +auto _atd_read_object_to_assoc_array(F read_func, const rapidjson::Value &val) +{ + using ResultType = typename std::invoke_result::type; + + if (!val.IsObject()) + { + throw AtdException("Expected an object"); // Or your specific exception type + } + + std::map result; + for (auto &m : val.GetObject()) + { + result[m.name.GetString()] = read_func(m.value); + } + + return result; +} + +template +auto _atd_read_nullable(F read_func, const rapidjson::Value &val) +{ + if (val.IsNull()) + { + return std::optional::type>(); + } + return std::optional::type>(read_func(val)); +} + +template +auto _atd_read_option(F read_func, const rapidjson::Value &val) +{ + using ResultType = typename std::invoke_result::type; + if (val.IsString() && std::string_view(val.GetString()) == "None") + { + return std::optional(); + } + else if (val.IsArray() && val.Size() == 2 && val[0].IsString() && std::string_view(val[0].GetString()) == "Some") + { + return std::make_optional(read_func(val[1])); + } + else + { + throw AtdException("Expected an option"); + } +} + +template +auto _atd_read_wrap(F read_func, W wrap_func, const rapidjson::Value &val) +{ + return wrap_func(read_func(val)); +} + +void _atd_write_int(int value, rapidjson::Writer& writer) +{ + writer.Int(value); +} + +void _atd_write_bool(bool value, rapidjson::Writer& writer) +{ + writer.Bool(value); +} + +void _atd_write_float(float value, rapidjson::Writer& writer) +{ + writer.Double(value); +} + +void _atd_write_string(const std::string &value, rapidjson::Writer& writer) +{ + writer.String(value.c_str()); +} + +template +void _atd_write_array(F write_func, const V& values, rapidjson::Writer& writer) +{ + writer.StartArray(); + for (const auto& value : values) + { + write_func(value, writer); + } + writer.EndArray(); +} + +template +void _atd_write_tuple_list_to_object(F write_func, const V &values, rapidjson::Writer& writer) +{ + writer.StartObject(); + for (const auto& value : values) + { + writer.Key(std::get<0>(value).c_str()); + write_func(std::get<1>(value), writer); + } + writer.EndObject(); +} + +template +void _atd_write_assoc_dict_to_array(const Wk write_key_func, const Wv write_value_func, const Map &value_map, rapidjson::Writer& writer) +{ + writer.StartArray(); + for (const auto& pair : value_map) + { + writer.StartArray(); + write_key_func(pair.first, writer); + write_value_func(pair.second, writer); + writer.EndArray(); + } + writer.EndArray(); +} + +template +void _atd_write_assoc_array_to_object(F write_func, const Map &value_map, rapidjson::Writer& writer) +{ + writer.StartObject(); + for (const auto& pair : value_map) + { + writer.Key(pair.first.c_str()); + write_func(pair.second, writer); + } + writer.EndObject(); +} + + +template +void _atd_write_option(F write_func, const O &val, rapidjson::Writer& writer) +{ + if (val) + { + writer.StartArray(); + writer.String("Some"); + write_func(*val, writer); + writer.EndArray(); + } + else + { + writer.String("None"); + } +} + +template +void _atd_write_nullable(F write_func, const O &val, rapidjson::Writer& writer) +{ + if (val) + { + write_func(*val, writer); + } + else + { + writer.Null(); + } +} + +template +void _atd_write_wrap(F write_func, W wrap_func, const T &val, rapidjson::Writer& writer) +{ + write_func(wrap_func(val), writer); +} + + + + +struct RecursiveClass; +namespace typedefs { + typedef RecursiveClass RecursiveClass; +} +struct RecursiveClass { + int id; + bool flag; + std::vector children; + + static RecursiveClass from_json(const rapidjson::Value & doc) { + RecursiveClass record; + if (!doc.IsObject()) { + throw AtdException("Expected an object"); + } + if (doc.HasMember("id")) + record.id = _atd_read_int(doc["id"]); + else record.id = _atd_missing_json_field("RecursiveClass", "id"); + if (doc.HasMember("flag")) + record.flag = _atd_read_bool(doc["flag"]); + else record.flag = _atd_missing_json_field("RecursiveClass", "flag"); + if (doc.HasMember("children")) + record.children = _atd_read_array([](const auto &v){return RecursiveClass::from_json(v);}, doc["children"]); + else record.children = _atd_missing_json_field("RecursiveClass", "children"); + return record; + } + + static void to_json(const RecursiveClass &t, rapidjson::Writer &writer) { + writer.StartObject(); + writer.Key("id"); + _atd_write_int(t.id, writer); + writer.Key("flag"); + _atd_write_bool(t.flag, writer); + writer.Key("children"); + _atd_write_array([](auto v, auto &w){RecursiveClass::to_json(v, w);}, t.children, writer); + writer.EndObject(); + } + + static std::string to_json_string(const RecursiveClass &t) { + rapidjson::StringBuffer buffer; + rapidjson::Writer writer(buffer); + to_json(t, writer); + return buffer.GetString(); + } + + std::string to_json_string() { + return to_json_string(*this); + } +}; + + +struct ThreeLevelNestedListRecord; +namespace typedefs { + typedef ThreeLevelNestedListRecord ThreeLevelNestedListRecord; +} +struct ThreeLevelNestedListRecord { + std::vector>> field_a; + + static ThreeLevelNestedListRecord from_json(const rapidjson::Value & doc) { + ThreeLevelNestedListRecord record; + if (!doc.IsObject()) { + throw AtdException("Expected an object"); + } + if (doc.HasMember("field_a")) + record.field_a = _atd_read_array([](const auto &v){return _atd_read_array([](const auto &v){return _atd_read_array([](const auto &v){return _atd_read_int(v);}, v);}, v);}, doc["field_a"]); + else record.field_a = _atd_missing_json_field("ThreeLevelNestedListRecord", "field_a"); + return record; + } + + static void to_json(const ThreeLevelNestedListRecord &t, rapidjson::Writer &writer) { + writer.StartObject(); + writer.Key("field_a"); + _atd_write_array([](auto v, auto &w){_atd_write_array([](auto v, auto &w){_atd_write_array([](auto v, auto &w){_atd_write_int(v, w);}, v, w);}, v, w);}, t.field_a, writer); + writer.EndObject(); + } + + static std::string to_json_string(const ThreeLevelNestedListRecord &t) { + rapidjson::StringBuffer buffer; + rapidjson::Writer writer(buffer); + to_json(t, writer); + return buffer.GetString(); + } + + std::string to_json_string() { + return to_json_string(*this); + } +}; + + +namespace typedefs { + typedef int St; +} +namespace St { + auto from_json(const rapidjson::Value &doc) { + return _atd_read_int(doc); + } + void to_json(const typedefs::St &t, rapidjson::Writer &writer) { + _atd_write_int(t, writer); + } + std::string to_json_string(const typedefs::St &t) { + rapidjson::StringBuffer buffer; + rapidjson::Writer writer(buffer); + to_json(t, writer); + return buffer.GetString(); + } +} + + +namespace Kind::Types { + + + // Original type: kind = [ ... | Root | ... ] + struct Root { + static void to_json(const Root &e, rapidjson::Writer &writer){ + writer.String("Root"); + } + }; + + + // Original type: kind = [ ... | Thing of ... | ... ] + struct Thing + { + int value; + static void to_json(const Thing &e, rapidjson::Writer &writer){ + writer.StartArray(); + writer.String("Thing"); + _atd_write_int(e.value, writer); + writer.EndArray(); + } + }; + + + // Original type: kind = [ ... | WOW | ... ] + struct WOW { + static void to_json(const WOW &e, rapidjson::Writer &writer){ + writer.String("wow"); + } + }; + + + // Original type: kind = [ ... | Amaze of ... | ... ] + struct Amaze + { + std::vector value; + static void to_json(const Amaze &e, rapidjson::Writer &writer){ + writer.StartArray(); + writer.String("!!!"); + _atd_write_array([](auto v, auto &w){_atd_write_string(v, w);}, e.value, writer); + writer.EndArray(); + } + }; + + +} + + +namespace typedefs { + typedef std::variant<::Kind::Types::Root, ::Kind::Types::Thing, ::Kind::Types::WOW, ::Kind::Types::Amaze> Kind; +} +namespace Kind { + static ::typedefs::Kind from_json(const rapidjson::Value &x) { + if (x.IsString()) { + if (std::string_view(x.GetString()) == "Root") + return Types::Root(); + if (std::string_view(x.GetString()) == "wow") + return Types::WOW(); + throw _atd_bad_json("Kind", x); + } + if (x.IsArray() && x.Size() == 2 && x[0].IsString()) { + std::string cons = x[0].GetString(); + if (cons == "Thing") + return Types::Thing({_atd_read_int(x[1])}); + if (cons == "!!!") + return Types::Amaze({_atd_read_array([](const auto &v){return _atd_read_string(v);}, x[1])}); + throw _atd_bad_json("Kind", x); + } + throw _atd_bad_json("Kind", x); + } + static void to_json(const ::typedefs::Kind &x, rapidjson::Writer &writer) { + std::visit([&writer](auto &&arg) { + using T = std::decay_t; + if constexpr (std::is_same_v) Types::Root::to_json(arg, writer); +if constexpr (std::is_same_v) Types::Thing::to_json(arg, writer); +if constexpr (std::is_same_v) Types::WOW::to_json(arg, writer); +if constexpr (std::is_same_v) Types::Amaze::to_json(arg, writer); + }, x); + } +std::string to_json_string(const ::typedefs::Kind &x) { + rapidjson::StringBuffer buffer; + rapidjson::Writer writer(buffer); + to_json(x, writer); + return buffer.GetString(); +} +} + + +namespace typedefs { + typedef uint32_t Alias3; +} +namespace Alias3 { + auto from_json(const rapidjson::Value &doc) { + return _atd_read_wrap([](const auto& v){return _atd_read_int(v);}, [](const auto &e){return static_cast(e);},doc); + } + void to_json(const typedefs::Alias3 &t, rapidjson::Writer &writer) { + _atd_write_wrap([](const auto &v, auto &w){_atd_write_int(v, w);}, [](const auto &e){return static_cast(e);}, t, writer); + } + std::string to_json_string(const typedefs::Alias3 &t) { + rapidjson::StringBuffer buffer; + rapidjson::Writer writer(buffer); + to_json(t, writer); + return buffer.GetString(); + } +} + + +namespace typedefs { + typedef typedefs::Alias3 AliasOfAliasNotWrapped; +} +namespace AliasOfAliasNotWrapped { + auto from_json(const rapidjson::Value &doc) { + return Alias3::from_json(doc); + } + void to_json(const typedefs::AliasOfAliasNotWrapped &t, rapidjson::Writer &writer) { + Alias3::to_json(t, writer); + } + std::string to_json_string(const typedefs::AliasOfAliasNotWrapped &t) { + rapidjson::StringBuffer buffer; + rapidjson::Writer writer(buffer); + to_json(t, writer); + return buffer.GetString(); + } +} + + +namespace typedefs { + typedef typedefs::AliasOfAliasNotWrapped AliasOfAliasOfAlias; +} +namespace AliasOfAliasOfAlias { + auto from_json(const rapidjson::Value &doc) { + return AliasOfAliasNotWrapped::from_json(doc); + } + void to_json(const typedefs::AliasOfAliasOfAlias &t, rapidjson::Writer &writer) { + AliasOfAliasNotWrapped::to_json(t, writer); + } + std::string to_json_string(const typedefs::AliasOfAliasOfAlias &t) { + rapidjson::StringBuffer buffer; + rapidjson::Writer writer(buffer); + to_json(t, writer); + return buffer.GetString(); + } +} + + +namespace typedefs { + typedef std::vector Alias; +} +namespace Alias { + auto from_json(const rapidjson::Value &doc) { + return _atd_read_array([](const auto &v){return _atd_read_int(v);}, doc); + } + void to_json(const typedefs::Alias &t, rapidjson::Writer &writer) { + _atd_write_array([](auto v, auto &w){_atd_write_int(v, w);}, t, writer); + } + std::string to_json_string(const typedefs::Alias &t) { + rapidjson::StringBuffer buffer; + rapidjson::Writer writer(buffer); + to_json(t, writer); + return buffer.GetString(); + } +} + + +namespace typedefs { + typedef std::tuple KindParametrizedTuple; +} +namespace KindParametrizedTuple { + auto from_json(const rapidjson::Value &doc) { + return [](auto &v){ + if (!v.IsArray() || v.Size() != 3) + throw AtdException("Tuple of size 3"); + return std::make_tuple(Kind::from_json(v[0]), Kind::from_json(v[1]), _atd_read_int(v[2])); + }(doc); + } + void to_json(const typedefs::KindParametrizedTuple &t, rapidjson::Writer &writer) { + [](const auto &t, auto &writer){ + writer.StartArray(); + Kind::to_json(std::get<0>(t), writer); Kind::to_json(std::get<1>(t), writer); _atd_write_int(std::get<2>(t), writer); + writer.EndArray(); + }(t, writer); + } + std::string to_json_string(const typedefs::KindParametrizedTuple &t) { + rapidjson::StringBuffer buffer; + rapidjson::Writer writer(buffer); + to_json(t, writer); + return buffer.GetString(); + } +} + + +struct IntFloatParametrizedRecord; +namespace typedefs { + typedef IntFloatParametrizedRecord IntFloatParametrizedRecord; +} +struct IntFloatParametrizedRecord { + int field_a; + std::vector field_b = {}; + + static IntFloatParametrizedRecord from_json(const rapidjson::Value & doc) { + IntFloatParametrizedRecord record; + if (!doc.IsObject()) { + throw AtdException("Expected an object"); + } + if (doc.HasMember("field_a")) + record.field_a = _atd_read_int(doc["field_a"]); + else record.field_a = _atd_missing_json_field("IntFloatParametrizedRecord", "field_a"); + if (doc.HasMember("field_b")) + record.field_b = _atd_read_array([](const auto &v){return _atd_read_float(v);}, doc["field_b"]); + else record.field_b = {}; + return record; + } + + static void to_json(const IntFloatParametrizedRecord &t, rapidjson::Writer &writer) { + writer.StartObject(); + writer.Key("field_a"); + _atd_write_int(t.field_a, writer); + writer.Key("field_b"); + _atd_write_array([](auto v, auto &w){_atd_write_float(v, w);}, t.field_b, writer); + writer.EndObject(); + } + + static std::string to_json_string(const IntFloatParametrizedRecord &t) { + rapidjson::StringBuffer buffer; + rapidjson::Writer writer(buffer); + to_json(t, writer); + return buffer.GetString(); + } + + std::string to_json_string() { + return to_json_string(*this); + } +}; + + +struct Root; +namespace typedefs { + typedef Root Root; +} +struct Root { + std::string id; + bool await; + int integer; + float x___init__; + float float_with_auto_default = 0.0; + float float_with_default = 0.1; + std::vector> items; + std::optional maybe; + std::vector extras = {}; + int answer = 42; + typedefs::Alias aliased; + std::tuple point; + typedefs::Kind kind; + std::vector kinds; + std::vector> assoc1; + std::vector> assoc2; + std::map assoc3; + std::map assoc4; + std::vector> nullables; + std::vector> options; + typedefs::IntFloatParametrizedRecord parametrized_record; + typedefs::KindParametrizedTuple parametrized_tuple; + uint16_t wrapped; + typedefs::AliasOfAliasOfAlias aaa; + int item; + + static Root from_json(const rapidjson::Value & doc) { + Root record; + if (!doc.IsObject()) { + throw AtdException("Expected an object"); + } + if (doc.HasMember("ID")) + record.id = _atd_read_string(doc["ID"]); + else record.id = _atd_missing_json_field("Root", "ID"); + if (doc.HasMember("await")) + record.await = _atd_read_bool(doc["await"]); + else record.await = _atd_missing_json_field("Root", "await"); + if (doc.HasMember("integer")) + record.integer = _atd_read_int(doc["integer"]); + else record.integer = _atd_missing_json_field("Root", "integer"); + if (doc.HasMember("__init__")) + record.x___init__ = _atd_read_float(doc["__init__"]); + else record.x___init__ = _atd_missing_json_field("Root", "__init__"); + if (doc.HasMember("float_with_auto_default")) + record.float_with_auto_default = _atd_read_float(doc["float_with_auto_default"]); + else record.float_with_auto_default = 0.0; + if (doc.HasMember("float_with_default")) + record.float_with_default = _atd_read_float(doc["float_with_default"]); + else record.float_with_default = 0.1; + if (doc.HasMember("items")) + record.items = _atd_read_array([](const auto &v){return _atd_read_array([](const auto &v){return _atd_read_int(v);}, v);}, doc["items"]); + else record.items = _atd_missing_json_field("Root", "items"); + if (doc.HasMember("maybe")) + record.maybe = _atd_read_option([](const auto &v){return _atd_read_int(v);}, doc["maybe"]); + else record.maybe = std::nullopt; + if (doc.HasMember("extras")) + record.extras = _atd_read_array([](const auto &v){return _atd_read_int(v);}, doc["extras"]); + else record.extras = {}; + if (doc.HasMember("answer")) + record.answer = _atd_read_int(doc["answer"]); + else record.answer = 42; + if (doc.HasMember("aliased")) + record.aliased = Alias::from_json(doc["aliased"]); + else record.aliased = _atd_missing_json_field("Root", "aliased"); + if (doc.HasMember("point")) + record.point = [](auto &v){ + if (!v.IsArray() || v.Size() != 2) + throw AtdException("Tuple of size 2"); + return std::make_tuple(_atd_read_float(v[0]), _atd_read_float(v[1])); + }(doc["point"]); + else record.point = _atd_missing_json_field("Root", "point"); + if (doc.HasMember("kind")) + record.kind = Kind::from_json(doc["kind"]); + else record.kind = _atd_missing_json_field("Root", "kind"); + if (doc.HasMember("kinds")) + record.kinds = _atd_read_array([](const auto &v){return Kind::from_json(v);}, doc["kinds"]); + else record.kinds = _atd_missing_json_field("Root", "kinds"); + if (doc.HasMember("assoc1")) + record.assoc1 = _atd_read_array([](const auto &v){return [](auto &v){ + if (!v.IsArray() || v.Size() != 2) + throw AtdException("Tuple of size 2"); + return std::make_tuple(_atd_read_float(v[0]), _atd_read_int(v[1])); + }(v);}, doc["assoc1"]); + else record.assoc1 = _atd_missing_json_field("Root", "assoc1"); + if (doc.HasMember("assoc2")) + record.assoc2 = _atd_read_object_to_tuple_list([](const auto &v){return _atd_read_int(v);},doc["assoc2"]); + else record.assoc2 = _atd_missing_json_field("Root", "assoc2"); + if (doc.HasMember("assoc3")) + record.assoc3 = _atd_read_array_to_assoc_dict([](const auto &k){return _atd_read_float(k);}, [](const auto &v){return _atd_read_int(v);}, doc["assoc3"]); + else record.assoc3 = _atd_missing_json_field("Root", "assoc3"); + if (doc.HasMember("assoc4")) + record.assoc4 = _atd_read_object_to_assoc_array([](const auto &v){return _atd_read_int(v);},doc["assoc4"]); + else record.assoc4 = _atd_missing_json_field("Root", "assoc4"); + if (doc.HasMember("nullables")) + record.nullables = _atd_read_array([](const auto &v){return _atd_read_nullable([](const auto &v){return _atd_read_int(v);}, v);}, doc["nullables"]); + else record.nullables = _atd_missing_json_field("Root", "nullables"); + if (doc.HasMember("options")) + record.options = _atd_read_array([](const auto &v){return _atd_read_option([](const auto &v){return _atd_read_int(v);}, v);}, doc["options"]); + else record.options = _atd_missing_json_field("Root", "options"); + if (doc.HasMember("parametrized_record")) + record.parametrized_record = IntFloatParametrizedRecord::from_json(doc["parametrized_record"]); + else record.parametrized_record = _atd_missing_json_field("Root", "parametrized_record"); + if (doc.HasMember("parametrized_tuple")) + record.parametrized_tuple = KindParametrizedTuple::from_json(doc["parametrized_tuple"]); + else record.parametrized_tuple = _atd_missing_json_field("Root", "parametrized_tuple"); + if (doc.HasMember("wrapped")) + record.wrapped = _atd_read_wrap([](const auto& v){return St::from_json(v);}, [](const auto &e){return [](typedefs::St st){return st - 1;}(e);},doc["wrapped"]); + else record.wrapped = _atd_missing_json_field("Root", "wrapped"); + if (doc.HasMember("aaa")) + record.aaa = AliasOfAliasOfAlias::from_json(doc["aaa"]); + else record.aaa = _atd_missing_json_field("Root", "aaa"); + if (doc.HasMember("item")) + record.item = _atd_read_wrap([](const auto& v){return _atd_read_string(v);}, [](const auto &e){return std::stoi(e);},doc["item"]); + else record.item = _atd_missing_json_field("Root", "item"); + return record; + } + + static void to_json(const Root &t, rapidjson::Writer &writer) { + writer.StartObject(); + writer.Key("ID"); + _atd_write_string(t.id, writer); + writer.Key("await"); + _atd_write_bool(t.await, writer); + writer.Key("integer"); + _atd_write_int(t.integer, writer); + writer.Key("__init__"); + _atd_write_float(t.x___init__, writer); + writer.Key("float_with_auto_default"); + _atd_write_float(t.float_with_auto_default, writer); + writer.Key("float_with_default"); + _atd_write_float(t.float_with_default, writer); + writer.Key("items"); + _atd_write_array([](auto v, auto &w){_atd_write_array([](auto v, auto &w){_atd_write_int(v, w);}, v, w);}, t.items, writer); + if (t.maybe != std::nullopt) { + writer.Key("maybe"); + _atd_write_option([](const auto &v, auto &w){_atd_write_int(v, w);}, t.maybe, writer); + } + writer.Key("extras"); + _atd_write_array([](auto v, auto &w){_atd_write_int(v, w);}, t.extras, writer); + writer.Key("answer"); + _atd_write_int(t.answer, writer); + writer.Key("aliased"); + Alias::to_json(t.aliased, writer); + writer.Key("point"); + [](const auto &t, auto &writer){ + writer.StartArray(); + _atd_write_float(std::get<0>(t), writer); _atd_write_float(std::get<1>(t), writer); + writer.EndArray(); + }(t.point, writer); + writer.Key("kind"); + Kind::to_json(t.kind, writer); + writer.Key("kinds"); + _atd_write_array([](auto v, auto &w){Kind::to_json(v, w);}, t.kinds, writer); + writer.Key("assoc1"); + _atd_write_array([](auto v, auto &w){[](const auto &t, auto &writer){ + writer.StartArray(); + _atd_write_float(std::get<0>(t), writer); _atd_write_int(std::get<1>(t), writer); + writer.EndArray(); + }(v, w);}, t.assoc1, writer); + writer.Key("assoc2"); + _atd_write_tuple_list_to_object([](auto v, auto &w){_atd_write_int(v, w);}, t.assoc2, writer); + writer.Key("assoc3"); + _atd_write_assoc_dict_to_array([](auto v, auto &w){_atd_write_float(v, w);}, [](auto v, auto &w){_atd_write_int(v, w);}, t.assoc3, writer); + writer.Key("assoc4"); + _atd_write_assoc_array_to_object([](auto v, auto &w){_atd_write_int(v, w);}, t.assoc4, writer); + writer.Key("nullables"); + _atd_write_array([](auto v, auto &w){_atd_write_nullable([](auto v, auto &w){_atd_write_int(v, w);}, v, w);}, t.nullables, writer); + writer.Key("options"); + _atd_write_array([](auto v, auto &w){_atd_write_option([](auto v, auto &w){_atd_write_int(v, w);}, v, w);}, t.options, writer); + writer.Key("parametrized_record"); + IntFloatParametrizedRecord::to_json(t.parametrized_record, writer); + writer.Key("parametrized_tuple"); + KindParametrizedTuple::to_json(t.parametrized_tuple, writer); + writer.Key("wrapped"); + _atd_write_wrap([](const auto &v, auto &w){St::to_json(v, w);}, [](const auto &e){return [](uint16_t e){return e + 1;}(e);}, t.wrapped, writer); + writer.Key("aaa"); + AliasOfAliasOfAlias::to_json(t.aaa, writer); + writer.Key("item"); + _atd_write_wrap([](const auto &v, auto &w){_atd_write_string(v, w);}, [](const auto &e){return std::to_string(e);}, t.item, writer); + writer.EndObject(); + } + + static std::string to_json_string(const Root &t) { + rapidjson::StringBuffer buffer; + rapidjson::Writer writer(buffer); + to_json(t, writer); + return buffer.GetString(); + } + + std::string to_json_string() { + return to_json_string(*this); + } +}; + + +struct RequireField; +namespace typedefs { + typedef RequireField RequireField; +} +struct RequireField { + std::string req; + + static RequireField from_json(const rapidjson::Value & doc) { + RequireField record; + if (!doc.IsObject()) { + throw AtdException("Expected an object"); + } + if (doc.HasMember("req")) + record.req = _atd_read_string(doc["req"]); + else record.req = _atd_missing_json_field("RequireField", "req"); + return record; + } + + static void to_json(const RequireField &t, rapidjson::Writer &writer) { + writer.StartObject(); + writer.Key("req"); + _atd_write_string(t.req, writer); + writer.EndObject(); + } + + static std::string to_json_string(const RequireField &t) { + rapidjson::StringBuffer buffer; + rapidjson::Writer writer(buffer); + to_json(t, writer); + return buffer.GetString(); + } + + std::string to_json_string() { + return to_json_string(*this); + } +}; + + +struct RecordWithWrappedType; +namespace typedefs { + typedef RecordWithWrappedType RecordWithWrappedType; +} +struct RecordWithWrappedType { + int item; + + static RecordWithWrappedType from_json(const rapidjson::Value & doc) { + RecordWithWrappedType record; + if (!doc.IsObject()) { + throw AtdException("Expected an object"); + } + if (doc.HasMember("item")) + record.item = _atd_read_wrap([](const auto& v){return _atd_read_string(v);}, [](const auto &e){return std::stoi(e);},doc["item"]); + else record.item = _atd_missing_json_field("RecordWithWrappedType", "item"); + return record; + } + + static void to_json(const RecordWithWrappedType &t, rapidjson::Writer &writer) { + writer.StartObject(); + writer.Key("item"); + _atd_write_wrap([](const auto &v, auto &w){_atd_write_string(v, w);}, [](const auto &e){return std::to_string(e);}, t.item, writer); + writer.EndObject(); + } + + static std::string to_json_string(const RecordWithWrappedType &t) { + rapidjson::StringBuffer buffer; + rapidjson::Writer writer(buffer); + to_json(t, writer); + return buffer.GetString(); + } + + std::string to_json_string() { + return to_json_string(*this); + } +}; + + +namespace typedefs { + typedef uint32_t Password; +} +namespace Password { + auto from_json(const rapidjson::Value &doc) { + return _atd_read_wrap([](const auto& v){return _atd_read_int(v);}, [](const auto &e){return static_cast(e);},doc); + } + void to_json(const typedefs::Password &t, rapidjson::Writer &writer) { + _atd_write_wrap([](const auto &v, auto &w){_atd_write_int(v, w);}, [](const auto &e){return static_cast(e);}, t, writer); + } + std::string to_json_string(const typedefs::Password &t) { + rapidjson::StringBuffer buffer; + rapidjson::Writer writer(buffer); + to_json(t, writer); + return buffer.GetString(); + } +} + + +namespace typedefs { + typedef std::tuple Pair; +} +namespace Pair { + auto from_json(const rapidjson::Value &doc) { + return [](auto &v){ + if (!v.IsArray() || v.Size() != 2) + throw AtdException("Tuple of size 2"); + return std::make_tuple(_atd_read_string(v[0]), _atd_read_int(v[1])); + }(doc); + } + void to_json(const typedefs::Pair &t, rapidjson::Writer &writer) { + [](const auto &t, auto &writer){ + writer.StartArray(); + _atd_write_string(std::get<0>(t), writer); _atd_write_int(std::get<1>(t), writer); + writer.EndArray(); + }(t, writer); + } + std::string to_json_string(const typedefs::Pair &t) { + rapidjson::StringBuffer buffer; + rapidjson::Writer writer(buffer); + to_json(t, writer); + return buffer.GetString(); + } +} + + +namespace Frozen::Types { + + + // Original type: frozen = [ ... | A | ... ] + struct A { + static void to_json(const A &e, rapidjson::Writer &writer){ + writer.String("A"); + } + }; + + + // Original type: frozen = [ ... | B of ... | ... ] + struct B + { + int value; + static void to_json(const B &e, rapidjson::Writer &writer){ + writer.StartArray(); + writer.String("B"); + _atd_write_int(e.value, writer); + writer.EndArray(); + } + }; + + +} + + +namespace typedefs { + typedef std::variant<::Frozen::Types::A, ::Frozen::Types::B> Frozen; +} +namespace Frozen { + static ::typedefs::Frozen from_json(const rapidjson::Value &x) { + if (x.IsString()) { + if (std::string_view(x.GetString()) == "A") + return Types::A(); + throw _atd_bad_json("Frozen", x); + } + if (x.IsArray() && x.Size() == 2 && x[0].IsString()) { + std::string cons = x[0].GetString(); + if (cons == "B") + return Types::B({_atd_read_int(x[1])}); + throw _atd_bad_json("Frozen", x); + } + throw _atd_bad_json("Frozen", x); + } + static void to_json(const ::typedefs::Frozen &x, rapidjson::Writer &writer) { + std::visit([&writer](auto &&arg) { + using T = std::decay_t; + if constexpr (std::is_same_v) Types::A::to_json(arg, writer); +if constexpr (std::is_same_v) Types::B::to_json(arg, writer); + }, x); + } +std::string to_json_string(const ::typedefs::Frozen &x) { + rapidjson::StringBuffer buffer; + rapidjson::Writer writer(buffer); + to_json(x, writer); + return buffer.GetString(); +} +} + + +struct DefaultList; +namespace typedefs { + typedef DefaultList DefaultList; +} +struct DefaultList { + std::vector items = {}; + + static DefaultList from_json(const rapidjson::Value & doc) { + DefaultList record; + if (!doc.IsObject()) { + throw AtdException("Expected an object"); + } + if (doc.HasMember("items")) + record.items = _atd_read_array([](const auto &v){return _atd_read_int(v);}, doc["items"]); + else record.items = {}; + return record; + } + + static void to_json(const DefaultList &t, rapidjson::Writer &writer) { + writer.StartObject(); + writer.Key("items"); + _atd_write_array([](auto v, auto &w){_atd_write_int(v, w);}, t.items, writer); + writer.EndObject(); + } + + static std::string to_json_string(const DefaultList &t) { + rapidjson::StringBuffer buffer; + rapidjson::Writer writer(buffer); + to_json(t, writer); + return buffer.GetString(); + } + + std::string to_json_string() { + return to_json_string(*this); + } +}; + + +struct Credential; +namespace typedefs { + typedef Credential Credential; +} +struct Credential { + std::string name; + int password; + + static Credential from_json(const rapidjson::Value & doc) { + Credential record; + if (!doc.IsObject()) { + throw AtdException("Expected an object"); + } + if (doc.HasMember("name")) + record.name = _atd_read_string(doc["name"]); + else record.name = _atd_missing_json_field("Credential", "name"); + if (doc.HasMember("password")) + record.password = _atd_read_int(doc["password"]); + else record.password = _atd_missing_json_field("Credential", "password"); + return record; + } + + static void to_json(const Credential &t, rapidjson::Writer &writer) { + writer.StartObject(); + writer.Key("name"); + _atd_write_string(t.name, writer); + writer.Key("password"); + _atd_write_int(t.password, writer); + writer.EndObject(); + } + + static std::string to_json_string(const Credential &t) { + rapidjson::StringBuffer buffer; + rapidjson::Writer writer(buffer); + to_json(t, writer); + return buffer.GetString(); + } + + std::string to_json_string() { + return to_json_string(*this); + } +}; + + +namespace typedefs { + typedef std::vector Credentials2; +} +namespace Credentials2 { + auto from_json(const rapidjson::Value &doc) { + return _atd_read_array([](const auto &v){return Credential::from_json(v);}, doc); + } + void to_json(const typedefs::Credentials2 &t, rapidjson::Writer &writer) { + _atd_write_array([](auto v, auto &w){Credential::to_json(v, w);}, t, writer); + } + std::string to_json_string(const typedefs::Credentials2 &t) { + rapidjson::StringBuffer buffer; + rapidjson::Writer writer(buffer); + to_json(t, writer); + return buffer.GetString(); + } +} + + +struct Credentials; +namespace typedefs { + typedef Credentials Credentials; +} +struct Credentials { + std::vector credentials; + + static Credentials from_json(const rapidjson::Value & doc) { + Credentials record; + if (!doc.IsObject()) { + throw AtdException("Expected an object"); + } + if (doc.HasMember("credentials")) + record.credentials = _atd_read_array([](const auto &v){return Credential::from_json(v);}, doc["credentials"]); + else record.credentials = _atd_missing_json_field("Credentials", "credentials"); + return record; + } + + static void to_json(const Credentials &t, rapidjson::Writer &writer) { + writer.StartObject(); + writer.Key("credentials"); + _atd_write_array([](auto v, auto &w){Credential::to_json(v, w);}, t.credentials, writer); + writer.EndObject(); + } + + static std::string to_json_string(const Credentials &t) { + rapidjson::StringBuffer buffer; + rapidjson::Writer writer(buffer); + to_json(t, writer); + return buffer.GetString(); + } + + std::string to_json_string() { + return to_json_string(*this); + } +}; + + +namespace typedefs { + typedef uint16_t AliasOfAlias; +} +namespace AliasOfAlias { + auto from_json(const rapidjson::Value &doc) { + return _atd_read_wrap([](const auto& v){return Alias3::from_json(v);}, [](const auto &e){return static_cast(e);},doc); + } + void to_json(const typedefs::AliasOfAlias &t, rapidjson::Writer &writer) { + _atd_write_wrap([](const auto &v, auto &w){Alias3::to_json(v, w);}, [](const auto &e){return static_cast(e);}, t, writer); + } + std::string to_json_string(const typedefs::AliasOfAlias &t) { + rapidjson::StringBuffer buffer; + rapidjson::Writer writer(buffer); + to_json(t, writer); + return buffer.GetString(); + } +} + + +namespace typedefs { + typedef std::vector Alias2; +} +namespace Alias2 { + auto from_json(const rapidjson::Value &doc) { + return _atd_read_array([](const auto &v){return _atd_read_int(v);}, doc); + } + void to_json(const typedefs::Alias2 &t, rapidjson::Writer &writer) { + _atd_write_array([](auto v, auto &w){_atd_write_int(v, w);}, t, writer); + } + std::string to_json_string(const typedefs::Alias2 &t) { + rapidjson::StringBuffer buffer; + rapidjson::Writer writer(buffer); + to_json(t, writer); + return buffer.GetString(); + } +} diff --git a/atdcpp/test/dune b/atdcpp/test/dune index 692301273..c71d379fc 100644 --- a/atdcpp/test/dune +++ b/atdcpp/test/dune @@ -8,8 +8,8 @@ (alias runtest) (package atdcpp) (action - (diff cpp-expected/everything_atd.d - cpp-tests/everything_atd.d))) + (diff cpp-expected/everything_atd.hpp + cpp-tests/everything_atd.hpp))) ; 2. Run the generated Dlang code and check that is reads or writes JSON ; data as expected. From c3c660e580b0866b79caed09b48c5e650d9f2a89 Mon Sep 17 00:00:00 2001 From: Alexandre Bourquelot Date: Tue, 27 Feb 2024 17:54:19 +0100 Subject: [PATCH 19/47] atdcpp: some cleanup --- atdcpp/everything_atd_goal.cpp | 177 --------------------------------- atdcpp/everything_atd_goal.hpp | 121 ---------------------- 2 files changed, 298 deletions(-) delete mode 100644 atdcpp/everything_atd_goal.cpp delete mode 100644 atdcpp/everything_atd_goal.hpp diff --git a/atdcpp/everything_atd_goal.cpp b/atdcpp/everything_atd_goal.cpp deleted file mode 100644 index 9c8d5a76b..000000000 --- a/atdcpp/everything_atd_goal.cpp +++ /dev/null @@ -1,177 +0,0 @@ -#include "everything_atd_goal.hpp" - -#include -#include -#include -#include // Only for rapidjson::ParseResult -#include - -IntFloatParametrizedRecord IntFloatParametrizedRecord::from_json(const rapidjson::Document &doc) -{ - IntFloatParametrizedRecord record; - if (doc.IsObject()) - { - const rapidjson::Value &fieldA = doc["field_a"]; - - record.field_a = _atd_read_int(doc["field_a"]); - record.field_b = _atd_read_array(doc["field_b"]); - } - - return record; -} - -IntFloatParametrizedRecord IntFloatParametrizedRecord::from_json_string(const std::string &json) -{ - rapidjson::Document doc; - doc.Parse(json.c_str()); - - if (doc.HasParseError()) - { - throw AtdException("Error parsing JSON: " + std::string(rapidjson::GetParseError_En(doc.GetParseError()))); - } - - return from_json(doc); -} - -std::string IntFloatParametrizedRecord::to_json_string() const -{ - rapidjson::StringBuffer buffer; - rapidjson::Writer writer(buffer); - - writer.StartObject(); - - writer.Key("field_a"); - _atd_write_int(field_a, writer); - writer.Key("field_b"); - _atd_write_array(field_b, writer); - - writer.EndObject(); - - return buffer.GetString(); -} - -namespace -{ - void write(std::vector obj, rapidjson::Writer &writer) - { - _atd_write_array(obj, writer); - } -} - -std::string NestedNestedIntListRecord::to_json_string() const -{ - rapidjson::StringBuffer buffer; - rapidjson::Writer writer(buffer); - - writer.StartObject(); - - writer.Key("field_a"); - - _atd_write_array, write>(field_a, writer); - - writer.EndObject(); - - return buffer.GetString(); -} - -namespace __NNNIntListRecord -{ - namespace field_0 - { - typedef std::vector> T; - - void write_0(std::vector obj, rapidjson::Writer &writer) - { - _atd_write_array(obj, writer); - } - - void write(T obj, rapidjson::Writer &writer) - { - _atd_write_array, write_0>(obj, writer); - } - - std::vector read_0(const rapidjson::Value &val) - { - return _atd_read_array(val); - } - - T read(const rapidjson::Value &val) - { - return _atd_read_array, read_0>(val); - } - } -} - -typedef NNNIntListRecord TT; - -TT TT::from_json(const rapidjson::Document &doc) -{ - TT record; - - return record; -} - -std::string NNNIntListRecord::to_json_string() const -{ - rapidjson::StringBuffer buffer; - rapidjson::Writer writer(buffer); - - writer.StartObject(); - - writer.Key("field_a"); - - _atd_write_array>, __NNNIntListRecord::field_0::write>(field_a, writer); - - writer.EndObject(); - - return buffer.GetString(); -} - -NNNIntListRecord NNNIntListRecord::from_json(const rapidjson::Document &doc) -{ - NNNIntListRecord record; - if (doc.IsObject()) - { - const rapidjson::Value &fieldA = doc["field_a"]; - - record.field_a = _atd_read_array>, __NNNIntListRecord::field_0::read>(doc["field_a"]); - } - - return record; -} - -const rapidjson::Document doc_from_json(const std::string &json) -{ - rapidjson::Document doc; - doc.Parse(json.c_str()); - - if (doc.HasParseError()) - { - throw AtdException("Error parsing JSON: " + std::string(rapidjson::GetParseError_En(doc.GetParseError()))); - } - - return doc; -} - -int main() -{ - std::string NNNIntListRecord_json = R"({"field_a":[[[1,2,3],[4,4,6]],[[7,8,9],[10,11,12]]]})"; - - NNNIntListRecord recordFromJson = NNNIntListRecord::from_json(doc_from_json(NNNIntListRecord_json)); - /// iterate through all vectors to print all values (3 level of nesting) - for (auto &vec1 : recordFromJson.field_a) - { - for (auto &vec2 : vec1) - { - for (auto &val : vec2) - { - std::cout << val << " "; - } - std::cout << std::endl; - } - } - - std::cout << "NNNIntListRecord: " << NNNIntListRecord{{{{1, 2, 3}, {4, 5, 6}}, {{7, 8, 9}, {10, 18, 12}}}}.to_json_string() << std::endl; - - return 0; -} \ No newline at end of file diff --git a/atdcpp/everything_atd_goal.hpp b/atdcpp/everything_atd_goal.hpp deleted file mode 100644 index e3239b741..000000000 --- a/atdcpp/everything_atd_goal.hpp +++ /dev/null @@ -1,121 +0,0 @@ -#pragma once - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -class AtdException : public std::exception -{ -public: - AtdException(const std::string &message) : msg_(message) {} - - const char *what() const throw() override - { - return msg_.c_str(); - } - -private: - std::string msg_; -}; - -// Reading an integer from JSON -int _atd_read_int(const rapidjson::Value &val) -{ - if (!val.IsInt()) - { - throw AtdException("Expected an integer"); - } - return val.GetInt(); -} - -// Reading a float from JSON -float _atd_read_float(const rapidjson::Value &val) -{ - if (val.IsInt()) - { - return static_cast(val.GetInt()); - } - else if (val.IsUint()) - { - return static_cast(val.GetUint()); - } - if (!val.IsFloat()) - { - throw AtdException("Expected a float"); - } - - return val.GetFloat(); -} - -template -std::vector _atd_read_array(const rapidjson::Value &val) -{ - if (!val.IsArray()) - { - throw std::runtime_error("Expected an array"); // Or your specific exception type - } - - std::vector result; - for (rapidjson::SizeType i = 0; i < val.Size(); i++) - { - result.push_back(read_func(val[i])); - } - - return result; -} - -void _atd_write_int(int value, rapidjson::Writer& writer) -{ - writer.Int(value); -} - -void _atd_write_float(float value, rapidjson::Writer& writer) -{ - writer.Double(value); -} - -template &)> -void _atd_write_array(const std::vector& values, rapidjson::Writer& writer) -{ - writer.StartArray(); - for (const auto& value : values) - { - write_func(value, writer); - } - writer.EndArray(); -} - - -struct IntFloatParametrizedRecord -{ - int field_a; - std::vector field_b; - - std::string to_json_string() const; - static IntFloatParametrizedRecord from_json(const rapidjson::Document &doc); - static IntFloatParametrizedRecord from_json_string(const std::string &json); -}; - -struct NestedNestedIntListRecord -{ - std::vector> field_a; - - std::string to_json_string() const; - static NestedNestedIntListRecord from_json(const rapidjson::Document &doc); - static NestedNestedIntListRecord from_json_string(const std::string &json); -}; - -struct NNNIntListRecord -{ - std::vector>> field_a; - - std::string to_json_string() const; - static NNNIntListRecord from_json(const rapidjson::Document &doc); - static NNNIntListRecord from_json_string(const std::string &json); -}; \ No newline at end of file From 62fb7ece0c667afaaa2860cda55aa9f4d0842fcf Mon Sep 17 00:00:00 2001 From: Alexandre Bourquelot Date: Tue, 27 Feb 2024 18:05:06 +0100 Subject: [PATCH 20/47] atdcpp: opam file --- atdcpp.opam | 84 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 84 insertions(+) create mode 100644 atdcpp.opam diff --git a/atdcpp.opam b/atdcpp.opam new file mode 100644 index 000000000..aee196f88 --- /dev/null +++ b/atdcpp.opam @@ -0,0 +1,84 @@ +# This file is generated by dune, edit dune-project instead +opam-version: "2.0" +synopsis: "C++ code generation for ATD APIs" +description: "C++ code generation for ATD APIs" +maintainer: [ + "Louis Roché " + "Martin Jambon " + "Rudi Grinberg " +] +authors: [ + "Martin Jambon " + "Rudi Grinberg " + "Martin Jambon " + "Martin Jambon " + "Ivan Jager " + "oleksiy " + "David Sheets " + "Rudi Grinberg " + "Martin Jambon " + "Jeff Meister " + "Caio Wakamatsu " + "Carmelo Piccione " + "Daniel Weil " + "Egor Chemokhonenko " + "Gabriel Scherer " + "Raman Varabets " + "tzm " + "Mathieu Baudet " + "Oleksiy Golovko " + "Rauan Mayemir " + "Carmelo Piccione " + "John Billings " + "Louis Roché " + "Brendan Long " + "Chris Yocum " + "Louis Roché (Ahrefs) " + "Louis Roché " + "Pavel Antoshkin " + "Pierre Boutillier " + "Shon Feder " + "Anurag Soni " + "Arjun Ravi Narayan " + "Asya-kawai " + "Christophe Troestler " + "Damien Doligez " + "Daniel M " + "Ding Xiang Fei " + "François Pottier " + "Javier Chavarri " + "Kate " + "Louis " + "Louis Roché " + "Raman Varabets " + "Stephane Legrand " + "Vincent Bernardoff " + "haoyang " + "pmundkur " + "ygrek " +] +license: "MIT" +homepage: "https://github.com/ahrefs/atd" +bug-reports: "https://github.com/ahrefs/atd/issues" +depends: [ + "dune" {>= "2.8"} + "ocaml" {>= "4.08"} + "atd" {>= "2.11.0"} + "cmdliner" {>= "1.1.0"} + "re" + "odoc" {with-doc} +] +dev-repo: "git+https://github.com/ahrefs/atd.git" +build: [ + ["dune" "subst"] {dev} + [ + "dune" + "build" + "-p" + name + "-j" + jobs + "@install" + "@doc" {with-doc} + ] +] From 2d5e5e6fdb0d08df5c206fb29857c4d2e3a6d1bf Mon Sep 17 00:00:00 2001 From: Alexandre Bourquelot Date: Tue, 27 Feb 2024 19:17:59 +0100 Subject: [PATCH 21/47] atdcpp: handle includes --- atdcpp/Makefile | 6 +- atdcpp/src/lib/Codegen.ml | 134 +++++++------ .../src/lib/{Dlang_annot.ml => Cpp_annot.ml} | 22 +- atdcpp/src/lib/Cpp_annot.mli | 42 ++++ atdcpp/src/lib/Dlang_annot.mli | 42 ---- atdcpp/test/atd-input/everything.atd | 15 +- atdcpp/test/cpp-expected/everything_atd.hpp | 188 ++++++++++++++++++ atdcpp/test/cpp-tests/dune | 2 +- atdcpp/test/cpp-tests/everything_atd.hpp | 1 + atdcpp/test/cpp-tests/test_atdd.cpp | 108 ++++------ atdcpp/test_main.cpp | 85 -------- 11 files changed, 373 insertions(+), 272 deletions(-) rename atdcpp/src/lib/{Dlang_annot.ml => Cpp_annot.ml} (81%) create mode 100644 atdcpp/src/lib/Cpp_annot.mli delete mode 100644 atdcpp/src/lib/Dlang_annot.mli create mode 120000 atdcpp/test/cpp-tests/everything_atd.hpp diff --git a/atdcpp/Makefile b/atdcpp/Makefile index 5a2794690..b021b6c6d 100644 --- a/atdcpp/Makefile +++ b/atdcpp/Makefile @@ -18,13 +18,13 @@ build: test: $(MAKE) clean-for-dune $(DUNE) runtest -f; status=$$?; \ - ln -s ../../../_build/default/atdcpp/test/cpp-tests/everything.d \ - test/cpp-tests/everything.d && \ + ln -s ../../../_build/default/atdcpp/test/cpp-tests/everything_atd.hpp \ + test/cpp-tests/everything_atd.hpp && \ exit "$$status" .PHONY: clean-for-dune clean-for-dune: - rm -f test/cpp-tests/everything.d + rm -f test/cpp-tests/everything_atd.hpp .PHONY: clean clean: diff --git a/atdcpp/src/lib/Codegen.ml b/atdcpp/src/lib/Codegen.ml index a5cf94a10..3c1417834 100644 --- a/atdcpp/src/lib/Codegen.ml +++ b/atdcpp/src/lib/Codegen.ml @@ -22,7 +22,7 @@ type env = { } -let annot_schema_dlang : Atd.Annot.schema_section = +let annot_schema_cpp : Atd.Annot.schema_section = { section = "cpp"; fields = [ @@ -31,12 +31,12 @@ let annot_schema_dlang : Atd.Annot.schema_section = Type_expr, "unwrap"; Type_expr, "wrap"; Field, "default"; - Module_head, "import"; + Module_head, "include"; ] } let annot_schema : Atd.Annot.schema = - annot_schema_dlang :: Atd.Json.annot_schema_json + annot_schema_cpp :: Atd.Json.annot_schema_json (* Translate a preferred variable name into an available cpp identifier. *) let trans env id = @@ -531,8 +531,8 @@ type assoc_kind = let assoc_kind loc (e : type_expr) an : assoc_kind = let json_repr = Atd.Json.get_json_list an in - let dlang_repr = Dlang_annot.get_dlang_assoc_repr an in - match e, json_repr, dlang_repr with + let cpp_repr = Cpp_annot.get_cpp_assoc_repr an in + match e, json_repr, cpp_repr with | Tuple (loc, [(_, key, _); (_, value, _)], an2), Array, Dict -> Array_dict (key, value) | Tuple (loc, @@ -547,7 +547,7 @@ let assoc_kind loc (e : type_expr) an : assoc_kind = | _, Array, _ -> error_at loc "not a (_ * _) list" (* Map ATD built-in types to built-in cpp types *) -let dlang_type_name env (name : string) = +let cpp_type_name env (name : string) = match name with | "unit" -> "void" | "bool" -> "bool" @@ -559,7 +559,7 @@ let dlang_type_name env (name : string) = let typename = (struct_name env user_defined) in typename -let dlang_type_name_namespaced env (name : string) = +let cpp_type_name_namespaced env (name : string) = match name with | "unit" -> "void" | "bool" -> "bool" @@ -600,11 +600,11 @@ let rec type_name_of_expr env (e : type_expr) : string = | Nullable (loc, e, an) -> sprintf "std::optional<%s>" (type_name_of_expr env e) | Shared (loc, e, an) -> not_implemented loc "shared" (* TODO *) | Wrap (loc, e, an) -> - (match Dlang_annot.get_dlang_wrap loc an with + (match Cpp_annot.get_cpp_wrap loc an with | None -> error_at loc "wrap type declared, but no cpp annotation found" - | Some { dlang_wrap_t ; _ } -> dlang_wrap_t + | Some { cpp_wrap_t ; _ } -> cpp_wrap_t ) - | Name (loc, (loc2, name, []), an) -> dlang_type_name_namespaced env name + | Name (loc, (loc2, name, []), an) -> cpp_type_name_namespaced env name | Name (loc, (_, name, _::_), _) -> assert false | Tvar (loc, _) -> not_implemented loc "type variables" @@ -631,8 +631,8 @@ let rec get_default_default (e : type_expr) : string option = | Name _ -> None | Tvar _ -> None -let get_dlang_default (e : type_expr) (an : annot) : string option = - let user_default = Dlang_annot.get_dlang_default an in +let get_cpp_default (e : type_expr) (an : annot) : string option = + let user_default = Cpp_annot.get_cpp_default an in match user_default with | Some s -> Some s | None -> get_default_default e @@ -684,16 +684,16 @@ let rec json_writer ?(nested=false) env e = sprintf "_atd_write_nullable([](auto v, auto &w){%sv, w);}, " (json_writer ~nested:true env e) | Shared (loc, e, an) -> not_implemented loc "shared" | Wrap (loc, e, an) -> - (match Dlang_annot.get_dlang_wrap loc an with + (match Cpp_annot.get_cpp_wrap loc an with | None -> error_at loc "wrap type declared, but no cpp annotation found" - | Some { dlang_wrap_t; dlang_unwrap ; _ } -> - sprintf "_atd_write_wrap([](const auto &v, auto &w){%sv, w);}, [](const auto &e){return %s(e);}, " (json_writer ~nested:true env e) dlang_unwrap + | Some { cpp_wrap_t; cpp_unwrap ; _ } -> + sprintf "_atd_write_wrap([](const auto &v, auto &w){%sv, w);}, [](const auto &e){return %s(e);}, " (json_writer ~nested:true env e) cpp_unwrap ) | Name (loc, (loc2, name, []), an) -> (match name with | "bool" | "int" | "float" | "string" -> sprintf "_atd_write_%s(" name | "abstract" -> not_implemented loc "abstract" - | _ -> let dtype_name = (dlang_type_name env name) in + | _ -> let dtype_name = (cpp_type_name env name) in sprintf "%s::to_json(" dtype_name) | Name (loc, _, _) -> not_implemented loc "parametrized types" | Tvar (loc, _) -> not_implemented loc "type variables" @@ -773,10 +773,10 @@ let rec json_reader ?(nested=false) env (e : type_expr) = sprintf "_atd_read_nullable([](const auto &v){return %sv);}, " (json_reader ~nested:true env e) | Shared (loc, e, an) -> not_implemented loc "shared" | Wrap (loc, e, an) -> - (match Dlang_annot.get_dlang_wrap loc an with + (match Cpp_annot.get_cpp_wrap loc an with | None -> error_at loc "wrap type declared, but no cpp annotation found" - | Some { dlang_wrap ; _ } -> - sprintf "_atd_read_wrap([](const auto& v){return %sv);}, [](const auto &e){return %s(e);}," (json_reader ~nested:true env e) dlang_wrap + | Some { cpp_wrap ; _ } -> + sprintf "_atd_read_wrap([](const auto& v){return %sv);}, [](const auto &e){return %s(e);}," (json_reader ~nested:true env e) cpp_wrap ) | Name (loc, (loc2, name, []), an) -> (match name with @@ -803,19 +803,19 @@ and tuple_reader env cells = (List.length cells) (List.length cells) tuple_body let from_json_class_argument - env trans_meth dlang_struct_name ((loc, (name, kind, an), e) : simple_field) = - let dlang_name = inst_var_name trans_meth name in + env trans_meth cpp_struct_name ((loc, (name, kind, an), e) : simple_field) = + let cpp_name = inst_var_name trans_meth name in let json_name = Atd.Json.get_json_fname name an in let else_body = match kind with | Required -> sprintf "_atd_missing_json_field(\"%s\", \"%s\")" - dlang_name - (single_esc dlang_struct_name) + cpp_name + (single_esc cpp_struct_name) (single_esc json_name) | Optional -> (sprintf "std::nullopt") | With_default -> - match get_dlang_default e an with + match get_cpp_default e an with | Some x -> x | None -> A.error_at loc @@ -826,11 +826,11 @@ let from_json_class_argument Line (sprintf "if (doc.HasMember(\"%s\"))" (single_esc json_name)); Block [ Line (sprintf "record.%s = %sdoc[\"%s\"]);" - dlang_name + cpp_name (json_reader env e) (single_esc json_name)); ]; - Line (sprintf "else record.%s = %s;" dlang_name else_body);] + Line (sprintf "else record.%s = %s;" cpp_name else_body);] let inst_var_declaration env trans_meth ((loc, (name, kind, an), e) : simple_field) = @@ -842,17 +842,30 @@ let inst_var_declaration | Required | Optional -> "" | With_default -> - match get_dlang_default unwrapped_e an with + match get_cpp_default unwrapped_e an with | None -> "" | Some x -> sprintf " = %s" x in [ Line (sprintf "%s %s%s;" type_name var_name default) ] - + +let from_json_string_block = + [ + Line "static auto from_json_string(const std::string &s) {"; + Block [ + Line "rapidjson::Document doc;"; + Line "doc.Parse(s.c_str());"; + Line "if (doc.HasParseError()) {"; + Block [Line "throw AtdException(\"Failed to parse JSON\");"]; + Line "}"; + Line "return from_json(doc);"; + ]; + Line "}"; + ] let record env loc name (fields : field list) an = - let dlang_struct_name = struct_name env name in + let cpp_struct_name = struct_name env name in let trans_meth = env.translate_inst_variable () in let fields = List.map (function @@ -868,14 +881,14 @@ let record env loc name (fields : field list) an = Inline (construct_json_field env trans_meth x)) fields in let from_json_class_arguments = List.map (fun x -> - (from_json_class_argument env trans_meth dlang_struct_name x) + (from_json_class_argument env trans_meth cpp_struct_name x) ) fields in let from_json = [ Line (sprintf "static %s from_json(const rapidjson::Value & doc) {" - (single_esc dlang_struct_name)); + (single_esc cpp_struct_name)); Block [ - Line (sprintf "%s record;" dlang_struct_name); + Line (sprintf "%s record;" cpp_struct_name); Line "if (!doc.IsObject()) {"; Block [Line "throw AtdException(\"Expected an object\");"]; Line "}"; @@ -887,7 +900,7 @@ let record env loc name (fields : field list) an = in let to_json = [ - Line (sprintf "static void to_json(const %s &t, rapidjson::Writer &writer) {" (single_esc dlang_struct_name)); + Line (sprintf "static void to_json(const %s &t, rapidjson::Writer &writer) {" (single_esc cpp_struct_name)); Block [ Line ("writer.StartObject();"); Inline json_object_body; @@ -898,7 +911,7 @@ let record env loc name (fields : field list) an = in let to_json_string_static = [ - Line (sprintf "static std::string to_json_string(const %s &t) {" (single_esc dlang_struct_name)); + Line (sprintf "static std::string to_json_string(const %s &t) {" (single_esc cpp_struct_name)); Block [ Line ("rapidjson::StringBuffer buffer;"); Line ("rapidjson::Writer writer(buffer);"); @@ -918,16 +931,17 @@ let record env loc name (fields : field list) an = ] in [ - Line (sprintf "struct %s;" dlang_struct_name); + Line (sprintf "struct %s;" cpp_struct_name); Line (sprintf "namespace typedefs {"); Block [ - Line (sprintf "typedef %s %s;" dlang_struct_name dlang_struct_name) + Line (sprintf "typedef %s %s;" cpp_struct_name cpp_struct_name) ]; Line "}"; - Line (sprintf "struct %s {" dlang_struct_name); + Line (sprintf "struct %s {" cpp_struct_name); Block (spaced [ Inline inst_var_declarations; Inline from_json; + Inline from_json_string_block; Inline to_json; Inline to_json_string_static; Inline to_json_string; @@ -936,27 +950,28 @@ let record env loc name (fields : field list) an = ] let alias_wrapper env name type_expr = - let dlang_struct_name = struct_name env name in + let cpp_struct_name = struct_name env name in let value_type = type_name_of_expr env type_expr in [ Line (sprintf "namespace typedefs {"); Block [ - Line (sprintf "typedef %s %s;" value_type dlang_struct_name) + Line (sprintf "typedef %s %s;" value_type cpp_struct_name) ]; Line "}"; - Line (sprintf "namespace %s {" dlang_struct_name); + Line (sprintf "namespace %s {" cpp_struct_name); Block [ Line (sprintf "auto from_json(const rapidjson::Value &doc) {"); Block [ Line (sprintf "return %sdoc);" (json_reader env type_expr)); ]; Line "}"; - Line (sprintf "void to_json(const typedefs::%s &t, rapidjson::Writer &writer) {" dlang_struct_name); + Inline from_json_string_block; + Line (sprintf "void to_json(const typedefs::%s &t, rapidjson::Writer &writer) {" cpp_struct_name); Block [ Line (sprintf "%st, writer);" (json_writer env type_expr)); ]; Line "}"; - Line (sprintf "std::string to_json_string(const typedefs::%s &t) {" dlang_struct_name); + Line (sprintf "std::string to_json_string(const typedefs::%s &t) {" cpp_struct_name); Block [ Line ("rapidjson::StringBuffer buffer;"); Line ("rapidjson::Writer writer(buffer);"); @@ -1053,18 +1068,18 @@ let read_cases1 env loc name cases1 = ] let sum_container env loc name cases = - let dlang_struct_name = struct_name env name in - let type_list = - List.map (fun (loc, orig_name, unique_name, an, opt_e) -> - (sprintf "::%s::Types::%s" (dlang_struct_name) (trans env orig_name)) - ) cases - |> String.concat ", " - in + let cpp_struct_name = struct_name env name in let cases0, cases1 = List.partition (fun (loc, orig_name, unique_name, an, opt_e) -> opt_e = None ) cases in + let type_list = + List.map (fun (loc, orig_name, unique_name, an, opt_e) -> + (sprintf "::%s::Types::%s" (cpp_struct_name) (trans env orig_name)) + ) cases + |> String.concat ", " + in let cases0_block = if cases0 <> [] then [ @@ -1090,12 +1105,11 @@ let sum_container env loc name cases = in [ Line (sprintf "namespace typedefs {"); - Block [ Line(sprintf "typedef %s %s;" (sprintf "std::variant<%s>" type_list) (dlang_struct_name))]; + Block [ Line(sprintf "typedef %s %s;" (sprintf "std::variant<%s>" type_list) (cpp_struct_name))]; Line "}"; - - Line (sprintf "namespace %s {" (dlang_struct_name)); + Line (sprintf "namespace %s {" (cpp_struct_name)); Block [ - Line (sprintf "static ::typedefs::%s from_json(const rapidjson::Value &x) {" (dlang_struct_name)); + Line (sprintf "static ::typedefs::%s from_json(const rapidjson::Value &x) {" (cpp_struct_name)); Block [ Inline cases0_block; Inline cases1_block; @@ -1104,9 +1118,9 @@ let sum_container env loc name cases = ]; Line "}"; ]; - + Block [Inline from_json_string_block;]; Block [ - Line (sprintf "static void to_json(const ::typedefs::%s &x, rapidjson::Writer &writer) {" (dlang_struct_name)); + Line (sprintf "static void to_json(const ::typedefs::%s &x, rapidjson::Writer &writer) {" (cpp_struct_name)); Block [ Line "std::visit([&writer](auto &&arg) {"; Block [ @@ -1122,7 +1136,7 @@ let sum_container env loc name cases = Line ("}"); ]; - Line (sprintf "std::string to_json_string(const ::typedefs::%s &x) {" (dlang_struct_name)); + Line (sprintf "std::string to_json_string(const ::typedefs::%s &x) {" (cpp_struct_name)); Block [ Line ("rapidjson::StringBuffer buffer;"); Line ("rapidjson::Writer writer(buffer);"); @@ -1206,11 +1220,11 @@ let to_file ~atd_filename ~head (items : A.module_body) dst_path = let env = init_env () in reserve_good_struct_names env items; let head = List.map (fun s -> Line s) head in - let dlang_defs = + let cpp_defs = Atd.Util.tsort items |> List.map (fun x -> Inline (definition_group ~atd_filename env x)) in - Line (fixed_size_preamble atd_filename) :: Inline head :: dlang_defs + Line (fixed_size_preamble atd_filename) :: Inline head :: cpp_defs |> double_spaced |> Indent.to_file ~indent:4 dst_path @@ -1236,7 +1250,7 @@ let run_file src_path = let full_module = Atd.Ast.use_only_specific_variants full_module in let (atd_head, atd_module) = full_module in let head = - Dlang_annot.get_dlang_import (snd atd_head) - |> List.map (sprintf "import %s;") + Cpp_annot.get_cpp_include (snd atd_head) + |> List.map (sprintf "#include %s") in to_file ~atd_filename:src_name ~head atd_module dst_path diff --git a/atdcpp/src/lib/Dlang_annot.ml b/atdcpp/src/lib/Cpp_annot.ml similarity index 81% rename from atdcpp/src/lib/Dlang_annot.ml rename to atdcpp/src/lib/Cpp_annot.ml index 2395d0a77..4095583ff 100644 --- a/atdcpp/src/lib/Dlang_annot.ml +++ b/atdcpp/src/lib/Cpp_annot.ml @@ -8,20 +8,20 @@ type assoc_repr = | List | Dict -type atd_dlang_wrap = { - dlang_wrap_t : string; - dlang_wrap : string; - dlang_unwrap : string; +type atd_cpp_wrap = { + cpp_wrap_t : string; + cpp_wrap : string; + cpp_unwrap : string; } -let get_dlang_default an : string option = +let get_cpp_default an : string option = Atd.Annot.get_opt_field ~parse:(fun s -> Some s) ~sections:["cpp"] ~field:"default" an -let get_dlang_assoc_repr an : assoc_repr = +let get_cpp_assoc_repr an : assoc_repr = Atd.Annot.get_field ~parse:(function | "list" -> Some List @@ -33,15 +33,15 @@ let get_dlang_assoc_repr an : assoc_repr = ~field:"repr" an -(* imports etc. *) -let get_dlang_import an : string list = +(* includes etc. *) +let get_cpp_include an : string list = Atd.Annot.get_fields ~parse:(fun s -> Some s) ~sections:["cpp"] - ~field:"import" + ~field:"include" an -let get_dlang_wrap loc an = +let get_cpp_wrap loc an = let path = ["cpp"] in let module_ = Atd.Annot.get_opt_field @@ -86,6 +86,6 @@ let get_dlang_wrap loc an = match t, wrap, unwrap with None, None, None -> None | Some t, Some wrap, Some unwrap -> - Some { dlang_wrap_t = t; dlang_wrap = wrap; dlang_unwrap = unwrap } + Some { cpp_wrap_t = t; cpp_wrap = wrap; cpp_unwrap = unwrap } | _ -> Atd.Ast.error_at loc "Incomplete annotation. Missing t, wrap or unwrap" diff --git a/atdcpp/src/lib/Cpp_annot.mli b/atdcpp/src/lib/Cpp_annot.mli new file mode 100644 index 000000000..ebafcdc76 --- /dev/null +++ b/atdcpp/src/lib/Cpp_annot.mli @@ -0,0 +1,42 @@ +(** + cpp-specific ATD annotations. + + This interface serves as a reference of which cpp-specific + ATD annotations are supported. atdcpp also honors JSON-related annotations + defined in [Atd.Json]. +*) + +(** Extract ["42"] from []. + The provided default must be a well-formed cpp immutable expression. +*) +val get_cpp_default : Atd.Annot.t -> string option + +(** Whether an association list of ATD type [(string * foo) list] + must be represented in cpp as a list of pairs or as a dictionary. + This is independent of the JSON representation. +*) +type assoc_repr = + | List + | Dict + +(** Inspect annotations placed on lists of pairs such as + [(string * foo) list ]. + Permissible values for the [repr] field are ["dict"] and ["list"]. + The default is ["list"]. +*) +val get_cpp_assoc_repr : Atd.Annot.t -> assoc_repr + +(** Returns text the user wants to be inserted at the beginning of the + cpp file such as include. *) +val get_cpp_include : Atd.Annot.t -> string list + + + +type atd_cpp_wrap = { + cpp_wrap_t : string; + cpp_wrap : string; + cpp_unwrap : string; +} + +val get_cpp_wrap : Atd.Ast.loc -> + Atd.Annot.t -> atd_cpp_wrap option diff --git a/atdcpp/src/lib/Dlang_annot.mli b/atdcpp/src/lib/Dlang_annot.mli deleted file mode 100644 index 55e8c8cd7..000000000 --- a/atdcpp/src/lib/Dlang_annot.mli +++ /dev/null @@ -1,42 +0,0 @@ -(** - Dlang-specific ATD annotations. - - This interface serves as a reference of which Dlang-specific - ATD annotations are supported. atdcpp also honors JSON-related annotations - defined in [Atd.Json]. -*) - -(** Extract ["42"] from []. - The provided default must be a well-formed Dlang immutable expression. -*) -val get_dlang_default : Atd.Annot.t -> string option - -(** Whether an association list of ATD type [(string * foo) list] - must be represented in Dlang as a list of pairs or as a dictionary. - This is independent of the JSON representation. -*) -type assoc_repr = - | List - | Dict - -(** Inspect annotations placed on lists of pairs such as - [(string * foo) list ]. - Permissible values for the [repr] field are ["dict"] and ["list"]. - The default is ["list"]. -*) -val get_dlang_assoc_repr : Atd.Annot.t -> assoc_repr - -(** Returns text the user wants to be inserted at the beginning of the - Dlang file such as imports. *) -val get_dlang_import : Atd.Annot.t -> string list - - - -type atd_dlang_wrap = { - dlang_wrap_t : string; - dlang_wrap : string; - dlang_unwrap : string; -} - -val get_dlang_wrap : Atd.Ast.loc -> - Atd.Annot.t -> atd_dlang_wrap option diff --git a/atdcpp/test/atd-input/everything.atd b/atdcpp/test/atd-input/everything.atd index d4afd44d1..af4691e58 100644 --- a/atdcpp/test/atd-input/everything.atd +++ b/atdcpp/test/atd-input/everything.atd @@ -1,4 +1,4 @@ -(* *) + type kind = [ | Root (* class name conflict *) @@ -88,12 +88,21 @@ type recursive_class = { children: recursive_class list; } - type default_list = { ~items: int list; } - type record_with_wrapped_type = { item: string wrap ; } + +(* type recursive_record2 = { + id: int; + flag: bool; + children: recursive_record2; +} + +type recursive_variant = [ + | A + | B of recursive_variant +] *) \ No newline at end of file diff --git a/atdcpp/test/cpp-expected/everything_atd.hpp b/atdcpp/test/cpp-expected/everything_atd.hpp index 11fadb878..95cb012a5 100644 --- a/atdcpp/test/cpp-expected/everything_atd.hpp +++ b/atdcpp/test/cpp-expected/everything_atd.hpp @@ -317,6 +317,9 @@ void _atd_write_wrap(F write_func, W wrap_func, const T &val, rapidjson::Writer< +#include + + struct RecursiveClass; namespace typedefs { typedef RecursiveClass RecursiveClass; @@ -343,6 +346,15 @@ struct RecursiveClass { return record; } + static auto from_json_string(const std::string &s) { + rapidjson::Document doc; + doc.Parse(s.c_str()); + if (doc.HasParseError()) { + throw AtdException("Failed to parse JSON"); + } + return from_json(doc); + } + static void to_json(const RecursiveClass &t, rapidjson::Writer &writer) { writer.StartObject(); writer.Key("id"); @@ -385,6 +397,15 @@ struct ThreeLevelNestedListRecord { return record; } + static auto from_json_string(const std::string &s) { + rapidjson::Document doc; + doc.Parse(s.c_str()); + if (doc.HasParseError()) { + throw AtdException("Failed to parse JSON"); + } + return from_json(doc); + } + static void to_json(const ThreeLevelNestedListRecord &t, rapidjson::Writer &writer) { writer.StartObject(); writer.Key("field_a"); @@ -412,6 +433,14 @@ namespace St { auto from_json(const rapidjson::Value &doc) { return _atd_read_int(doc); } + static auto from_json_string(const std::string &s) { + rapidjson::Document doc; + doc.Parse(s.c_str()); + if (doc.HasParseError()) { + throw AtdException("Failed to parse JSON"); + } + return from_json(doc); + } void to_json(const typedefs::St &t, rapidjson::Writer &writer) { _atd_write_int(t, writer); } @@ -494,6 +523,14 @@ namespace Kind { } throw _atd_bad_json("Kind", x); } + static auto from_json_string(const std::string &s) { + rapidjson::Document doc; + doc.Parse(s.c_str()); + if (doc.HasParseError()) { + throw AtdException("Failed to parse JSON"); + } + return from_json(doc); + } static void to_json(const ::typedefs::Kind &x, rapidjson::Writer &writer) { std::visit([&writer](auto &&arg) { using T = std::decay_t; @@ -519,6 +556,14 @@ namespace Alias3 { auto from_json(const rapidjson::Value &doc) { return _atd_read_wrap([](const auto& v){return _atd_read_int(v);}, [](const auto &e){return static_cast(e);},doc); } + static auto from_json_string(const std::string &s) { + rapidjson::Document doc; + doc.Parse(s.c_str()); + if (doc.HasParseError()) { + throw AtdException("Failed to parse JSON"); + } + return from_json(doc); + } void to_json(const typedefs::Alias3 &t, rapidjson::Writer &writer) { _atd_write_wrap([](const auto &v, auto &w){_atd_write_int(v, w);}, [](const auto &e){return static_cast(e);}, t, writer); } @@ -538,6 +583,14 @@ namespace AliasOfAliasNotWrapped { auto from_json(const rapidjson::Value &doc) { return Alias3::from_json(doc); } + static auto from_json_string(const std::string &s) { + rapidjson::Document doc; + doc.Parse(s.c_str()); + if (doc.HasParseError()) { + throw AtdException("Failed to parse JSON"); + } + return from_json(doc); + } void to_json(const typedefs::AliasOfAliasNotWrapped &t, rapidjson::Writer &writer) { Alias3::to_json(t, writer); } @@ -557,6 +610,14 @@ namespace AliasOfAliasOfAlias { auto from_json(const rapidjson::Value &doc) { return AliasOfAliasNotWrapped::from_json(doc); } + static auto from_json_string(const std::string &s) { + rapidjson::Document doc; + doc.Parse(s.c_str()); + if (doc.HasParseError()) { + throw AtdException("Failed to parse JSON"); + } + return from_json(doc); + } void to_json(const typedefs::AliasOfAliasOfAlias &t, rapidjson::Writer &writer) { AliasOfAliasNotWrapped::to_json(t, writer); } @@ -576,6 +637,14 @@ namespace Alias { auto from_json(const rapidjson::Value &doc) { return _atd_read_array([](const auto &v){return _atd_read_int(v);}, doc); } + static auto from_json_string(const std::string &s) { + rapidjson::Document doc; + doc.Parse(s.c_str()); + if (doc.HasParseError()) { + throw AtdException("Failed to parse JSON"); + } + return from_json(doc); + } void to_json(const typedefs::Alias &t, rapidjson::Writer &writer) { _atd_write_array([](auto v, auto &w){_atd_write_int(v, w);}, t, writer); } @@ -599,6 +668,14 @@ namespace KindParametrizedTuple { return std::make_tuple(Kind::from_json(v[0]), Kind::from_json(v[1]), _atd_read_int(v[2])); }(doc); } + static auto from_json_string(const std::string &s) { + rapidjson::Document doc; + doc.Parse(s.c_str()); + if (doc.HasParseError()) { + throw AtdException("Failed to parse JSON"); + } + return from_json(doc); + } void to_json(const typedefs::KindParametrizedTuple &t, rapidjson::Writer &writer) { [](const auto &t, auto &writer){ writer.StartArray(); @@ -637,6 +714,15 @@ struct IntFloatParametrizedRecord { return record; } + static auto from_json_string(const std::string &s) { + rapidjson::Document doc; + doc.Parse(s.c_str()); + if (doc.HasParseError()) { + throw AtdException("Failed to parse JSON"); + } + return from_json(doc); + } + static void to_json(const IntFloatParametrizedRecord &t, rapidjson::Writer &writer) { writer.StartObject(); writer.Key("field_a"); @@ -781,6 +867,15 @@ struct Root { return record; } + static auto from_json_string(const std::string &s) { + rapidjson::Document doc; + doc.Parse(s.c_str()); + if (doc.HasParseError()) { + throw AtdException("Failed to parse JSON"); + } + return from_json(doc); + } + static void to_json(const Root &t, rapidjson::Writer &writer) { writer.StartObject(); writer.Key("ID"); @@ -877,6 +972,15 @@ struct RequireField { return record; } + static auto from_json_string(const std::string &s) { + rapidjson::Document doc; + doc.Parse(s.c_str()); + if (doc.HasParseError()) { + throw AtdException("Failed to parse JSON"); + } + return from_json(doc); + } + static void to_json(const RequireField &t, rapidjson::Writer &writer) { writer.StartObject(); writer.Key("req"); @@ -915,6 +1019,15 @@ struct RecordWithWrappedType { return record; } + static auto from_json_string(const std::string &s) { + rapidjson::Document doc; + doc.Parse(s.c_str()); + if (doc.HasParseError()) { + throw AtdException("Failed to parse JSON"); + } + return from_json(doc); + } + static void to_json(const RecordWithWrappedType &t, rapidjson::Writer &writer) { writer.StartObject(); writer.Key("item"); @@ -942,6 +1055,14 @@ namespace Password { auto from_json(const rapidjson::Value &doc) { return _atd_read_wrap([](const auto& v){return _atd_read_int(v);}, [](const auto &e){return static_cast(e);},doc); } + static auto from_json_string(const std::string &s) { + rapidjson::Document doc; + doc.Parse(s.c_str()); + if (doc.HasParseError()) { + throw AtdException("Failed to parse JSON"); + } + return from_json(doc); + } void to_json(const typedefs::Password &t, rapidjson::Writer &writer) { _atd_write_wrap([](const auto &v, auto &w){_atd_write_int(v, w);}, [](const auto &e){return static_cast(e);}, t, writer); } @@ -965,6 +1086,14 @@ namespace Pair { return std::make_tuple(_atd_read_string(v[0]), _atd_read_int(v[1])); }(doc); } + static auto from_json_string(const std::string &s) { + rapidjson::Document doc; + doc.Parse(s.c_str()); + if (doc.HasParseError()) { + throw AtdException("Failed to parse JSON"); + } + return from_json(doc); + } void to_json(const typedefs::Pair &t, rapidjson::Writer &writer) { [](const auto &t, auto &writer){ writer.StartArray(); @@ -1026,6 +1155,14 @@ namespace Frozen { } throw _atd_bad_json("Frozen", x); } + static auto from_json_string(const std::string &s) { + rapidjson::Document doc; + doc.Parse(s.c_str()); + if (doc.HasParseError()) { + throw AtdException("Failed to parse JSON"); + } + return from_json(doc); + } static void to_json(const ::typedefs::Frozen &x, rapidjson::Writer &writer) { std::visit([&writer](auto &&arg) { using T = std::decay_t; @@ -1060,6 +1197,15 @@ struct DefaultList { return record; } + static auto from_json_string(const std::string &s) { + rapidjson::Document doc; + doc.Parse(s.c_str()); + if (doc.HasParseError()) { + throw AtdException("Failed to parse JSON"); + } + return from_json(doc); + } + static void to_json(const DefaultList &t, rapidjson::Writer &writer) { writer.StartObject(); writer.Key("items"); @@ -1102,6 +1248,15 @@ struct Credential { return record; } + static auto from_json_string(const std::string &s) { + rapidjson::Document doc; + doc.Parse(s.c_str()); + if (doc.HasParseError()) { + throw AtdException("Failed to parse JSON"); + } + return from_json(doc); + } + static void to_json(const Credential &t, rapidjson::Writer &writer) { writer.StartObject(); writer.Key("name"); @@ -1131,6 +1286,14 @@ namespace Credentials2 { auto from_json(const rapidjson::Value &doc) { return _atd_read_array([](const auto &v){return Credential::from_json(v);}, doc); } + static auto from_json_string(const std::string &s) { + rapidjson::Document doc; + doc.Parse(s.c_str()); + if (doc.HasParseError()) { + throw AtdException("Failed to parse JSON"); + } + return from_json(doc); + } void to_json(const typedefs::Credentials2 &t, rapidjson::Writer &writer) { _atd_write_array([](auto v, auto &w){Credential::to_json(v, w);}, t, writer); } @@ -1161,6 +1324,15 @@ struct Credentials { return record; } + static auto from_json_string(const std::string &s) { + rapidjson::Document doc; + doc.Parse(s.c_str()); + if (doc.HasParseError()) { + throw AtdException("Failed to parse JSON"); + } + return from_json(doc); + } + static void to_json(const Credentials &t, rapidjson::Writer &writer) { writer.StartObject(); writer.Key("credentials"); @@ -1188,6 +1360,14 @@ namespace AliasOfAlias { auto from_json(const rapidjson::Value &doc) { return _atd_read_wrap([](const auto& v){return Alias3::from_json(v);}, [](const auto &e){return static_cast(e);},doc); } + static auto from_json_string(const std::string &s) { + rapidjson::Document doc; + doc.Parse(s.c_str()); + if (doc.HasParseError()) { + throw AtdException("Failed to parse JSON"); + } + return from_json(doc); + } void to_json(const typedefs::AliasOfAlias &t, rapidjson::Writer &writer) { _atd_write_wrap([](const auto &v, auto &w){Alias3::to_json(v, w);}, [](const auto &e){return static_cast(e);}, t, writer); } @@ -1207,6 +1387,14 @@ namespace Alias2 { auto from_json(const rapidjson::Value &doc) { return _atd_read_array([](const auto &v){return _atd_read_int(v);}, doc); } + static auto from_json_string(const std::string &s) { + rapidjson::Document doc; + doc.Parse(s.c_str()); + if (doc.HasParseError()) { + throw AtdException("Failed to parse JSON"); + } + return from_json(doc); + } void to_json(const typedefs::Alias2 &t, rapidjson::Writer &writer) { _atd_write_array([](auto v, auto &w){_atd_write_int(v, w);}, t, writer); } diff --git a/atdcpp/test/cpp-tests/dune b/atdcpp/test/cpp-tests/dune index 8d30e596e..b3a1ea194 100644 --- a/atdcpp/test/cpp-tests/dune +++ b/atdcpp/test/cpp-tests/dune @@ -21,6 +21,6 @@ (glob_files *.cpp)) (action (progn - (bash "ldc2 %{deps} --of test") + (bash "clang++ -I../../lib/rapidjson/include -std=c++20 %{deps} -o test") (bash ./test) ))) diff --git a/atdcpp/test/cpp-tests/everything_atd.hpp b/atdcpp/test/cpp-tests/everything_atd.hpp new file mode 120000 index 000000000..868fa36e4 --- /dev/null +++ b/atdcpp/test/cpp-tests/everything_atd.hpp @@ -0,0 +1 @@ +../../../_build/default/atdcpp/test/cpp-tests/everything_atd.hpp \ No newline at end of file diff --git a/atdcpp/test/cpp-tests/test_atdd.cpp b/atdcpp/test/cpp-tests/test_atdd.cpp index 26096442b..54f682fa5 100644 --- a/atdcpp/test/cpp-tests/test_atdd.cpp +++ b/atdcpp/test/cpp-tests/test_atdd.cpp @@ -4,90 +4,64 @@ #include #include #include - -struct IntFloatParametrizedRecord { - int field_a; - std::vector field_b; - - IntFloatParametrizedRecord() = default; - - IntFloatParametrizedRecord(int a, const std::vector& b) - : field_a(a), field_b(b) {} - - std::string toJson() const { - // Convert record to JSON string - // Implementation omitted for brevity - return ""; - } - - bool operator==(const IntFloatParametrizedRecord& other) const { - return field_a == other.field_a && field_b == other.field_b; - } - - bool operator!=(const IntFloatParametrizedRecord& other) const { - return !(*this == other); - } -}; - -struct Pair { - Pair() = default; - std::string field_a; - int field_b; -}; - -template -T fromJson(const std::string& json) { - // Convert JSON string to object of type T - // Implementation omitted for brevity - return T(); -} - -template -std::string toJsonString(const T& obj) { - // Convert object to JSON string - // Implementation omitted for brevity - return ""; -} +#include "everything_atd.hpp" int main() { std::map> tests; tests["simpleRecord"] = []() { - IntFloatParametrizedRecord record(32, {5.4, 3.3}); + IntFloatParametrizedRecord record{32, {5.4, 3.3}}; - std::string json = record.toJson(); - IntFloatParametrizedRecord recordFromJson = fromJson(json); + std::string json = record.to_json_string(); + IntFloatParametrizedRecord recordFromJson = IntFloatParametrizedRecord::from_json_string(json); - if (record == recordFromJson) { + if (json == recordFromJson.to_json_string()) { std::cout << "Test passed: simpleRecord" << std::endl; } else { std::cout << "Test failed: simpleRecord" << std::endl; } }; - tests["simpleRecordMissingInt"] = []() { - std::string json = "{\"field_b\":[5.40000009536743164,3.29999995231628418]}"; - try { - fromJson(json); - std::cout << "Test failed: simpleRecordMissingInt" << std::endl; - } catch (const std::exception& e) { - std::cout << "Test passed: simpleRecordMissingInt" << std::endl; - } - }; - - tests["validPair"] = []() { - std::string str = "[\"hello\",2]"; - Pair p = fromJson(str); - - if (str == toJsonString(p)) { - std::cout << "Test passed: validPair" << std::endl; + // Add the test for the Root object serialization and deserialization + tests["rootObjectSerialization"] = []() { + Root root; + + root.id = "id long"; + root.await = false; + root.integer = 43; + root.x___init__ = 3.14; + root.float_with_auto_default = 90.03; + root.float_with_default = 32.1; + root.items = {{1, 2}, {-1, -2}}; + root.maybe = 422; + root.extras = {34, 12}; + root.answer = 12; + root.aliased = {55, 44}; + root.point = {4.4, 1.1}; + root.kind = Kind::Types::Root(); + root.kinds = {Kind::Types::Amaze({{"one", "two"}}), Kind::Types::Root(), Kind::Types::Root(), Kind::Types::Thing({1})}; + root.assoc1 = {{4.12, 1},{2.2, 2}}; + root.assoc2 = {{"first", 1}, {"second", 2}}; + root.assoc3 = {{1.1, 1}, {2.2, 2}}; + root.assoc4 = {{"firstt", 1}, {"secondd", 2}}; + root.nullables = {1, std::nullopt, 3}; + root.options = {1, 2, std::nullopt}; + root.parametrized_record = {2, {1.0, 1.1}}; + root.parametrized_tuple = {Kind::Types::Root(), Kind::Types::WOW(), 9}; + root.wrapped = 1; + root.aaa = -90; + root.item = 45; + + std::string json = root.to_json_string(); + Root rootFromJson = Root::from_json_string(json); + + if (json == rootFromJson.to_json_string()) { + std::cout << "Test passed: rootObjectSerialization" << std::endl; } else { - std::cout << "Test failed: validPair" << std::endl; + std::cout << "Test failed: rootObjectSerialization" << std::endl; } }; - // Add more test cases... - std::cout << "Running tests..." << std::endl; int passed = 0; diff --git a/atdcpp/test_main.cpp b/atdcpp/test_main.cpp index 52de8b175..f09d8333f 100644 --- a/atdcpp/test_main.cpp +++ b/atdcpp/test_main.cpp @@ -61,89 +61,4 @@ int main() std::cout << "Root: " << buffer.GetString() << std::endl; std::cout << "Root: " << json << std::endl; - // Credential credential = Credential::from_json(doc_from_json(R"({"name":"user","password":1234})")); - // std::cout << "Credential: " << credential.to_json_string() << std::endl; - - // Credentials credentials = Credentials::from_json(doc_from_json(R"({"credentials": - // [{"name":"user1","password":1234},{"name":"user2","password":5678}]})")); - - // for (auto &credential : credentials.credentials) - // { - // std::cout << "Credential: " << credential.to_json_string() << std::endl; - // } - - // std::cout << "Credentials: " << credentials.to_json_string() << std::endl; - - // std::string NNNIntListRecord_json = R"({"field_a":[[[1,2,3],[4,4,6]],[[7,8,9],[10,11,12]]]})"; - - // ThreeLevelNestedListRecord recordFromJson = ThreeLevelNestedListRecord::from_json(doc_from_json(NNNIntListRecord_json)); - - // for (auto &vec1 : recordFromJson.field_a) - // { - // std::cout << "vec1: " << std::endl; - // for (auto &vec2 : vec1) - // { - // std::cout << "vec2: " << std::endl; - // for (auto &val : vec2) - // { - // std::cout << val << " "; - // } - // std::cout << std::endl; - // } - // } - - // std::cout << "ThreeLevelNestedListRecord: " << ThreeLevelNestedListRecord{{{{1, 2, 3}, {4, 5, 6}}, {{7, 8, 9}, {10, 18, 12}}}}.to_json_string() << std::endl; - // std::cout << "Three..." << recordFromJson.to_json_string() << std::endl; - - - // std::string RecursiveClass_json = R"({"id":1,"flag":true,"children":[{"id":2,"flag":false,"children":[]},{"id":3,"flag":true,"children":[{"id":4,"flag":false,"children":[]}]}]})"; - - // RecursiveClass recordFromJson2 = RecursiveClass::from_json(doc_from_json(RecursiveClass_json)); - - // std::cout << "RecursiveClass: " << recordFromJson2.to_json_string() << std::endl; - - - // typedefs::Credentials2 credentials2 = Credentials2::from_json(doc_from_json(R"( - // [{"name":"user1","password":1234},{"name":"user2","password":5678}])")); - - // for (auto &credential : credentials2) - // { - // std::cout << "Credential: " << credential.to_json_string() << std::endl; - // } - - // std::cout << "Credentials: " << Credentials2::to_json_string(credentials2) << std::endl; - - - // std::string pair_json = R"(["stringb", 1234])"; - // typedefs::Pair pair = Pair::from_json(doc_from_json(pair_json)); - - // std::cout << "pair first: " << std::get(pair) << std::endl; - // std::cout << "pair second: " << std::get(pair) << std::endl; - - // std::cout << "Pair: " << Pair::to_json_string(pair) << std::endl; - - // typedefs::Frozen frozen = Frozen::Types::A(); - // std::cout << "Frozen: " << Frozen::to_json_string(frozen) << std::endl; - - // std::string frozen_json = R"(["B", 1234])"; - // frozen = Frozen::from_json(doc_from_json(frozen_json)); - // std::cout << "Frozen: " << Frozen::to_json_string(frozen) << std::endl; - - // NNNIntListRecord recordFromJson = NNNIntListRecord::from_json(doc_from_json(NNNIntListRecord_json)); - // /// iterate through all vectors to print all values (3 level of nesting) - // for (auto &vec1 : recordFromJson.field_a) - // { - // for (auto &vec2 : vec1) - // { - // for (auto &val : vec2) - // { - // std::cout << val << " "; - // } - // std::cout << std::endl; - // } - // } - - // std::cout << "NNNIntListRecord: " << NNNIntListRecord{{{{1, 2, 3}, {4, 5, 6}}, {{7, 8, 9}, {10, 18, 12}}}}.to_json_string() << std::endl; - - // return 0; } \ No newline at end of file From 663c7be09fa3425e883a1452adc5b62022f2c51d Mon Sep 17 00:00:00 2001 From: Alexandre Bourquelot Date: Wed, 28 Feb 2024 09:47:06 +0100 Subject: [PATCH 22/47] atdcpp: forward declare variant components --- atdcpp/src/lib/Codegen.ml | 13 ++- atdcpp/test/atd-input/everything.atd | 9 ++- atdcpp/test/cpp-expected/everything_atd.hpp | 89 +++++++++++++++++++++ atdcpp/test/cpp-tests/everything_atd.hpp | 1 - 4 files changed, 107 insertions(+), 5 deletions(-) delete mode 120000 atdcpp/test/cpp-tests/everything_atd.hpp diff --git a/atdcpp/src/lib/Codegen.ml b/atdcpp/src/lib/Codegen.ml index 3c1417834..9c969fc1e 100644 --- a/atdcpp/src/lib/Codegen.ml +++ b/atdcpp/src/lib/Codegen.ml @@ -1158,12 +1158,23 @@ let sum env loc name cases = | Inherit _ -> assert false ) cases in - let case_classes = + let cases_forward_declarations = + List.map (fun (loc, orig_name, unique_name, an, opt_e) -> Line (sprintf "struct %s;" orig_name)) cases + in + let cases_forward_declarations_in_namespace = + [ + Line (sprintf "namespace %s::Types {" (struct_name env name)); + Block cases_forward_declarations; + Line (sprintf "}"); + ] + in + let case_classes = List.map (fun x -> Inline (case_class env name x)) cases |> double_spaced in let container_class = sum_container env loc name cases in [ + Inline cases_forward_declarations_in_namespace; Line (sprintf "namespace %s::Types {" (struct_name env name)); Block case_classes; Line (sprintf "}"); diff --git a/atdcpp/test/atd-input/everything.atd b/atdcpp/test/atd-input/everything.atd index af4691e58..3780a7c65 100644 --- a/atdcpp/test/atd-input/everything.atd +++ b/atdcpp/test/atd-input/everything.atd @@ -96,13 +96,16 @@ type record_with_wrapped_type = { item: string wrap ; } +type 'a recursive = 'a list + (* type recursive_record2 = { id: int; flag: bool; children: recursive_record2; -} +} *) + type recursive_variant = [ | A - | B of recursive_variant -] *) \ No newline at end of file + | B of recursive_variant list +] \ No newline at end of file diff --git a/atdcpp/test/cpp-expected/everything_atd.hpp b/atdcpp/test/cpp-expected/everything_atd.hpp index 95cb012a5..2ec8e9300 100644 --- a/atdcpp/test/cpp-expected/everything_atd.hpp +++ b/atdcpp/test/cpp-expected/everything_atd.hpp @@ -320,6 +320,81 @@ void _atd_write_wrap(F write_func, W wrap_func, const T &val, rapidjson::Writer< #include +namespace RecursiveVariant::Types { + struct A; + struct B; +} + + +namespace RecursiveVariant::Types { + + + // Original type: recursive_variant = [ ... | A | ... ] + struct A { + static void to_json(const A &e, rapidjson::Writer &writer){ + writer.String("A"); + } + }; + + + // Original type: recursive_variant = [ ... | B of ... | ... ] + struct B + { + std::vector value; + static void to_json(const B &e, rapidjson::Writer &writer){ + writer.StartArray(); + writer.String("B"); + _atd_write_array([](auto v, auto &w){RecursiveVariant::to_json(v, w);}, e.value, writer); + writer.EndArray(); + } + }; + + +} + + +namespace typedefs { + typedef std::variant<::RecursiveVariant::Types::A, ::RecursiveVariant::Types::B> RecursiveVariant; +} +namespace RecursiveVariant { + static ::typedefs::RecursiveVariant from_json(const rapidjson::Value &x) { + if (x.IsString()) { + if (std::string_view(x.GetString()) == "A") + return Types::A(); + throw _atd_bad_json("RecursiveVariant", x); + } + if (x.IsArray() && x.Size() == 2 && x[0].IsString()) { + std::string cons = x[0].GetString(); + if (cons == "B") + return Types::B({_atd_read_array([](const auto &v){return RecursiveVariant::from_json(v);}, x[1])}); + throw _atd_bad_json("RecursiveVariant", x); + } + throw _atd_bad_json("RecursiveVariant", x); + } + static auto from_json_string(const std::string &s) { + rapidjson::Document doc; + doc.Parse(s.c_str()); + if (doc.HasParseError()) { + throw AtdException("Failed to parse JSON"); + } + return from_json(doc); + } + static void to_json(const ::typedefs::RecursiveVariant &x, rapidjson::Writer &writer) { + std::visit([&writer](auto &&arg) { + using T = std::decay_t; + if constexpr (std::is_same_v) Types::A::to_json(arg, writer); +if constexpr (std::is_same_v) Types::B::to_json(arg, writer); + }, x); + } +std::string to_json_string(const ::typedefs::RecursiveVariant &x) { + rapidjson::StringBuffer buffer; + rapidjson::Writer writer(buffer); + to_json(x, writer); + return buffer.GetString(); +} +} + + struct RecursiveClass; namespace typedefs { typedef RecursiveClass RecursiveClass; @@ -453,6 +528,14 @@ namespace St { } +namespace Kind::Types { + struct Root; + struct Thing; + struct WOW; + struct Amaze; +} + + namespace Kind::Types { @@ -1110,6 +1193,12 @@ namespace Pair { } +namespace Frozen::Types { + struct A; + struct B; +} + + namespace Frozen::Types { diff --git a/atdcpp/test/cpp-tests/everything_atd.hpp b/atdcpp/test/cpp-tests/everything_atd.hpp deleted file mode 120000 index 868fa36e4..000000000 --- a/atdcpp/test/cpp-tests/everything_atd.hpp +++ /dev/null @@ -1 +0,0 @@ -../../../_build/default/atdcpp/test/cpp-tests/everything_atd.hpp \ No newline at end of file From faaf32ac50b3f088a2e342f700d4134176fd5fc6 Mon Sep 17 00:00:00 2001 From: Alexandre Bourquelot Date: Wed, 28 Feb 2024 12:40:37 +0100 Subject: [PATCH 23/47] atdcpp: separate into header and source file --- atdcpp/src/lib/Codegen.ml | 216 +++++++++++++++++++++------ atdcpp/test/atd-input/everything.atd | 8 +- 2 files changed, 171 insertions(+), 53 deletions(-) diff --git a/atdcpp/src/lib/Codegen.ml b/atdcpp/src/lib/Codegen.ml index 9c969fc1e..56c216e1b 100644 --- a/atdcpp/src/lib/Codegen.ml +++ b/atdcpp/src/lib/Codegen.ml @@ -180,17 +180,14 @@ let single_esc s = let _double_esc s = escape_string_content Double s -let fixed_size_preamble atd_filename = - sprintf {| +let fixed_size_preamble_header atd_filename = + sprintf {| // Generated by atdcpp from type definitions in %s. // This implements classes for the types defined in '%s', providing // methods and functions to convert data from/to JSON. -// ############################################################################ -// # Private functions // ############################################################################ -// filename %s #pragma once #include @@ -204,6 +201,25 @@ let fixed_size_preamble atd_filename = #include #include +|} atd_filename atd_filename + +let fixed_size_preamble atd_filename = + sprintf {| +// Generated by atdcpp from type definitions in %s. +// This implements classes for the types defined in '%s', providing +// methods and functions to convert data from/to JSON. + +// ############################################################################ +// # Private functions +// ############################################################################ + +// filename %s + +#include "%s_atd.hpp" + +namespace atd +{ + class AtdException : public std::exception { public: @@ -501,6 +517,7 @@ void _atd_write_wrap(F write_func, W wrap_func, const T &val, rapidjson::Writer< atd_filename atd_filename atd_filename + (Filename.chop_extension atd_filename) let not_implemented loc msg = @@ -850,9 +867,14 @@ let inst_var_declaration Line (sprintf "%s %s%s;" type_name var_name default) ] -let from_json_string_block = +let from_json_string_declaration = + [ + Line "static auto from_json_string(const std::string &s);"; + ] + +let from_json_string_definition cpp_name = [ - Line "static auto from_json_string(const std::string &s) {"; + Line (sprintf "auto %sfrom_json_string(const std::string &s) {" (match cpp_name with | Some x -> sprintf "%s::" x | None -> "")); Block [ Line "rapidjson::Document doc;"; Line "doc.Parse(s.c_str());"; @@ -864,29 +886,27 @@ let from_json_string_block = Line "}"; ] -let record env loc name (fields : field list) an = + +let record_definition env loc name (fields : field list) an = let cpp_struct_name = struct_name env name in - let trans_meth = env.translate_inst_variable () in let fields = List.map (function | `Field x -> x | `Inherit _ -> (* expanded at loading time *) assert false) fields in - let inst_var_declarations = - List.map (fun x -> Inline (inst_var_declaration env trans_meth x)) fields - in - let json_object_body = - List.map (fun x -> - Inline (construct_json_field env trans_meth x)) fields in + let trans_meth = env.translate_inst_variable () in let from_json_class_arguments = List.map (fun x -> (from_json_class_argument env trans_meth cpp_struct_name x) ) fields in + let json_object_body = + List.map (fun x -> + Inline (construct_json_field env trans_meth x)) fields in let from_json = [ - Line (sprintf "static %s from_json(const rapidjson::Value & doc) {" - (single_esc cpp_struct_name)); + Line (sprintf "%s %s::from_json(const rapidjson::Value & doc) {" + (single_esc cpp_struct_name) (single_esc cpp_struct_name)); Block [ Line (sprintf "%s record;" cpp_struct_name); Line "if (!doc.IsObject()) {"; @@ -900,7 +920,7 @@ let record env loc name (fields : field list) an = in let to_json = [ - Line (sprintf "static void to_json(const %s &t, rapidjson::Writer &writer) {" (single_esc cpp_struct_name)); + Line (sprintf "void %s::to_json(const %s &t, rapidjson::Writer &writer) {" (single_esc cpp_struct_name) (single_esc cpp_struct_name)); Block [ Line ("writer.StartObject();"); Inline json_object_body; @@ -911,7 +931,7 @@ let record env loc name (fields : field list) an = in let to_json_string_static = [ - Line (sprintf "static std::string to_json_string(const %s &t) {" (single_esc cpp_struct_name)); + Line (sprintf "std::string %s::to_json_string(const %s &t) {" (single_esc cpp_struct_name) (single_esc cpp_struct_name)); Block [ Line ("rapidjson::StringBuffer buffer;"); Line ("rapidjson::Writer writer(buffer);"); @@ -923,13 +943,55 @@ let record env loc name (fields : field list) an = in let to_json_string = [ - Line (sprintf "std::string to_json_string() {" ); + Line (sprintf "std::string %s::to_json_string() {" (single_esc cpp_struct_name)); Block [ Line ("return to_json_string(*this);"); ]; Line "}"; ] in + [ + Inline from_json; + Inline (from_json_string_definition (Some cpp_struct_name)); + Inline to_json; + Inline to_json_string_static; + Inline to_json_string; + ] + + +let record env loc name (fields : field list) an = + let cpp_struct_name = struct_name env name in + let trans_meth = env.translate_inst_variable () in + let fields = + List.map (function + | `Field x -> x + | `Inherit _ -> (* expanded at loading time *) assert false) + fields + in + let inst_var_declarations = + List.map (fun x -> Inline (inst_var_declaration env trans_meth x)) fields + in + let from_json = + [ + Line (sprintf "static %s from_json(const rapidjson::Value & doc);" + (single_esc cpp_struct_name)); + ] + in + let to_json = + [ + Line (sprintf "static void to_json(const %s &t, rapidjson::Writer &writer);" (single_esc cpp_struct_name)); + ] + in + let to_json_string_static = + [ + Line (sprintf "static std::string to_json_string(const %s &t);" (single_esc cpp_struct_name)); + ] + in + let to_json_string = + [ + Line (sprintf "std::string to_json_string();" ); + ] + in [ Line (sprintf "struct %s;" cpp_struct_name); Line (sprintf "namespace typedefs {"); @@ -941,7 +1003,7 @@ let record env loc name (fields : field list) an = Block (spaced [ Inline inst_var_declarations; Inline from_json; - Inline from_json_string_block; + Inline from_json_string_declaration; Inline to_json; Inline to_json_string_static; Inline to_json_string; @@ -957,15 +1019,28 @@ let alias_wrapper env name type_expr = Block [ Line (sprintf "typedef %s %s;" value_type cpp_struct_name) ]; - Line "}"; + Line "}"; Line (sprintf "namespace %s {" cpp_struct_name); Block [ + Line (sprintf "auto from_json(const rapidjson::Value &doc);"); + Inline from_json_string_declaration; + Line (sprintf "void to_json(const typedefs::%s &t, rapidjson::Writer &writer);" cpp_struct_name); + Line (sprintf "std::string to_json_string(const typedefs::%s &t);" cpp_struct_name); + ]; + Line "}"; + ] + +let alias_wrapper_definition env name type_expr = + let cpp_struct_name = struct_name env name in + [ + Line (sprintf "namespace %s {" cpp_struct_name); + Block [ Line (sprintf "auto from_json(const rapidjson::Value &doc) {"); Block [ Line (sprintf "return %sdoc);" (json_reader env type_expr)); ]; Line "}"; - Inline from_json_string_block; + Inline (from_json_string_definition None); Line (sprintf "void to_json(const typedefs::%s &t, rapidjson::Writer &writer) {" cpp_struct_name); Block [ Line (sprintf "%st, writer);" (json_writer env type_expr)); @@ -978,9 +1053,8 @@ let alias_wrapper env name type_expr = Line (sprintf "to_json(t, writer);"); Line "return buffer.GetString();" ]; + Line "}"; ]; Line "}"; - ]; - Line "}"; ] let case_class env type_name @@ -1074,12 +1148,6 @@ let sum_container env loc name cases = opt_e = None ) cases in - let type_list = - List.map (fun (loc, orig_name, unique_name, an, opt_e) -> - (sprintf "::%s::Types::%s" (cpp_struct_name) (trans env orig_name)) - ) cases - |> String.concat ", " - in let cases0_block = if cases0 <> [] then [ @@ -1104,12 +1172,9 @@ let sum_container env loc name cases = [] in [ - Line (sprintf "namespace typedefs {"); - Block [ Line(sprintf "typedef %s %s;" (sprintf "std::variant<%s>" type_list) (cpp_struct_name))]; - Line "}"; Line (sprintf "namespace %s {" (cpp_struct_name)); Block [ - Line (sprintf "static ::typedefs::%s from_json(const rapidjson::Value &x) {" (cpp_struct_name)); + Line (sprintf "static atd::typedefs::%s from_json(const rapidjson::Value &x) {" (cpp_struct_name)); Block [ Inline cases0_block; Inline cases1_block; @@ -1118,9 +1183,9 @@ let sum_container env loc name cases = ]; Line "}"; ]; - Block [Inline from_json_string_block;]; + Block [Inline (from_json_string_definition (Some cpp_struct_name))]; Block [ - Line (sprintf "static void to_json(const ::typedefs::%s &x, rapidjson::Writer &writer) {" (cpp_struct_name)); + Line (sprintf "static void to_json(const atd::typedefs::%s &x, rapidjson::Writer &writer) {" (cpp_struct_name)); Block [ Line "std::visit([&writer](auto &&arg) {"; Block [ @@ -1136,7 +1201,7 @@ let sum_container env loc name cases = Line ("}"); ]; - Line (sprintf "std::string to_json_string(const ::typedefs::%s &x) {" (cpp_struct_name)); + Line (sprintf "std::string to_json_string(const atd::typedefs::%s &x) {" (cpp_struct_name)); Block [ Line ("rapidjson::StringBuffer buffer;"); Line ("rapidjson::Writer writer(buffer);"); @@ -1158,6 +1223,13 @@ let sum env loc name cases = | Inherit _ -> assert false ) cases in + let cpp_struct_name = struct_name env name in + let type_list = + List.map (fun (loc, orig_name, unique_name, an, opt_e) -> + (sprintf "atd::%s::Types::%s" (cpp_struct_name) (trans env orig_name)) + ) cases + |> String.concat ", " + in let cases_forward_declarations = List.map (fun (loc, orig_name, unique_name, an, opt_e) -> Line (sprintf "struct %s;" orig_name)) cases in @@ -1172,9 +1244,17 @@ let sum env loc name cases = List.map (fun x -> Inline (case_class env name x)) cases |> double_spaced in + let typedef_declaration = + [ + Line (sprintf "namespace typedefs {"); + Block [ Line(sprintf "typedef %s %s;" (sprintf "std::variant<%s>" type_list) (cpp_struct_name))]; + Line "}"; + ] + in let container_class = sum_container env loc name cases in [ Inline cases_forward_declarations_in_namespace; + Inline typedef_declaration; Line (sprintf "namespace %s::Types {" (struct_name env name)); Block case_classes; Line (sprintf "}"); @@ -1188,7 +1268,27 @@ let type_def env ((loc, (name, param, an), e) : A.type_def) : B.t = let unwrap e = match e with | Sum (loc, cases, an) -> - sum env loc name cases + sum env loc name cases + | Record (loc, fields, an) -> + record_definition env loc name fields an + | Tuple _ + | List _ + | Option _ + | Nullable _ + | Wrap _ + | Name _ -> alias_wrapper_definition env name e + | Shared _ -> not_implemented loc "cyclic references" + | Tvar _ -> not_implemented loc "parametrized type" + in + unwrap e + +let type_decl env ((loc, (name, param, an), e) : A.type_def) : B.t = + if param <> [] then + not_implemented loc "parametrized type"; + let unwrap e = + match e with + | Sum (loc, cases, an) -> + sum env loc name cases | Record (loc, fields, an) -> record env loc name fields an | Tuple _ @@ -1202,15 +1302,16 @@ let type_def env ((loc, (name, param, an), e) : A.type_def) : B.t = in unwrap e -let module_body env x = - List.fold_left (fun acc (Type x) -> Inline (type_def env x) :: acc) [] x +let module_body env x is_header = + let type_decl_or_def = if is_header then type_decl else type_def in + List.fold_left (fun acc (Type x) -> Inline (type_decl_or_def env x) :: acc) [] x |> List.rev |> spaced -let definition_group ~atd_filename env +let definition_group ~atd_filename env is_header (is_recursive, (items: A.module_body)) : B.t = [ - Inline (module_body env items); + Inline (module_body env items is_header); ] (* @@ -1227,15 +1328,30 @@ let reserve_good_struct_names env (items: A.module_body) = (fun (Type (loc, (name, param, an), e)) -> ignore (struct_name env name)) items -let to_file ~atd_filename ~head (items : A.module_body) dst_path = +let to_header_file ~atd_filename ~head (items : A.module_body) dst_path = let env = init_env () in reserve_good_struct_names env items; let head = List.map (fun s -> Line s) head in + let cpp_declarations = + Atd.Util.tsort items + |> List.map (fun x -> Inline (definition_group ~atd_filename env true x)) + in + let namespace_start = [Line (sprintf "namespace atd {")] in + let namespace_end = [Line "} // namespace atd"] in + Line (fixed_size_preamble_header atd_filename) :: Inline head :: namespace_start @ cpp_declarations @ namespace_end + |> double_spaced + |> Indent.to_file ~indent:4 dst_path + +let to_cpp_file ~atd_filename (items : A.module_body) dst_path = + let env = init_env () in + reserve_good_struct_names env items; let cpp_defs = Atd.Util.tsort items - |> List.map (fun x -> Inline (definition_group ~atd_filename env x)) + |> List.map (fun x -> Inline (definition_group ~atd_filename env false x)) in - Line (fixed_size_preamble atd_filename) :: Inline head :: cpp_defs + let head = [Line (sprintf "#include \"%s_atd.hpp\"" (Filename.chop_extension atd_filename))] in + let namespace_end = [Line "} // namespace atd"] in + Line (fixed_size_preamble atd_filename) :: head @ cpp_defs @ namespace_end |> double_spaced |> Indent.to_file ~indent:4 dst_path @@ -1245,10 +1361,11 @@ let run_file src_path = (if Filename.check_suffix src_name ".atd" then Filename.chop_suffix src_name ".atd" else - src_name) ^ "_atd.hpp" + src_name) ^ "_atd" |> String.lowercase_ascii in - let dst_path = dst_name in + let hpp_dst_path = dst_name ^ ".hpp" in + let cpp_dst_path = dst_name ^ ".cpp" in let full_module, _original_types = Atd.Util.load_file ~annot_schema @@ -1264,4 +1381,5 @@ let run_file src_path = Cpp_annot.get_cpp_include (snd atd_head) |> List.map (sprintf "#include %s") in - to_file ~atd_filename:src_name ~head atd_module dst_path + to_header_file ~atd_filename:src_name ~head atd_module hpp_dst_path; + to_cpp_file ~atd_filename:src_name atd_module cpp_dst_path diff --git a/atdcpp/test/atd-input/everything.atd b/atdcpp/test/atd-input/everything.atd index 3780a7c65..0606464fb 100644 --- a/atdcpp/test/atd-input/everything.atd +++ b/atdcpp/test/atd-input/everything.atd @@ -1,5 +1,5 @@ - +(* type kind = [ | Root (* class name conflict *) | Thing of int @@ -46,7 +46,7 @@ type root = { wrapped: st wrap ; aaa: alias_of_alias_of_alias; item: string wrap ; -} +} *) type st = int @@ -64,7 +64,7 @@ type credential = { name: string; password: int; } - +(* type credentials = { credentials: credential list; } @@ -108,4 +108,4 @@ type 'a recursive = 'a list type recursive_variant = [ | A | B of recursive_variant list -] \ No newline at end of file +] *) \ No newline at end of file From afb3f5bcedc71ca650431295c4db9dec430d9405 Mon Sep 17 00:00:00 2001 From: Alexandre Bourquelot Date: Wed, 28 Feb 2024 15:39:39 +0100 Subject: [PATCH 24/47] atdcpp: fix --- atdcpp/Makefile | 3 + atdcpp/src/lib/Codegen.ml | 124 +- atdcpp/test/atd-input/everything.atd | 8 +- atdcpp/test/cpp-expected/everything_atd.hpp | 1335 ++----------------- atdcpp/test/cpp-tests/dune | 3 +- atdcpp/test/cpp-tests/test_atdd.cpp | 2 + atdcpp/test_main.cpp | 1 + 7 files changed, 220 insertions(+), 1256 deletions(-) diff --git a/atdcpp/Makefile b/atdcpp/Makefile index b021b6c6d..f7c099bcb 100644 --- a/atdcpp/Makefile +++ b/atdcpp/Makefile @@ -20,11 +20,14 @@ test: $(DUNE) runtest -f; status=$$?; \ ln -s ../../../_build/default/atdcpp/test/cpp-tests/everything_atd.hpp \ test/cpp-tests/everything_atd.hpp && \ + ln -s ../../../_build/default/atdcpp/test/cpp-tests/everything_atd.cpp \ + test/cpp-tests/everything_atd.cpp && \ exit "$$status" .PHONY: clean-for-dune clean-for-dune: rm -f test/cpp-tests/everything_atd.hpp + rm -f test/cpp-tests/everything_atd.cpp .PHONY: clean clean: diff --git a/atdcpp/src/lib/Codegen.ml b/atdcpp/src/lib/Codegen.ml index 56c216e1b..c257c7ca7 100644 --- a/atdcpp/src/lib/Codegen.ml +++ b/atdcpp/src/lib/Codegen.ml @@ -867,14 +867,14 @@ let inst_var_declaration Line (sprintf "%s %s%s;" type_name var_name default) ] -let from_json_string_declaration = +let from_json_string_declaration cpp_name = [ - Line "static auto from_json_string(const std::string &s);"; + Line (sprintf "static %s from_json_string(const std::string &s);" cpp_name); ] -let from_json_string_definition cpp_name = +let from_json_string_definition cpp_name p = [ - Line (sprintf "auto %sfrom_json_string(const std::string &s) {" (match cpp_name with | Some x -> sprintf "%s::" x | None -> "")); + Line (sprintf "%s %sfrom_json_string(const std::string &s) {" cpp_name (match p with | Some x -> sprintf "%s::" x | None -> "")); Block [ Line "rapidjson::Document doc;"; Line "doc.Parse(s.c_str());"; @@ -952,7 +952,7 @@ let record_definition env loc name (fields : field list) an = in [ Inline from_json; - Inline (from_json_string_definition (Some cpp_struct_name)); + Inline (from_json_string_definition cpp_struct_name (Some cpp_struct_name)); Inline to_json; Inline to_json_string_static; Inline to_json_string; @@ -1000,10 +1000,11 @@ let record env loc name (fields : field list) an = ]; Line "}"; Line (sprintf "struct %s {" cpp_struct_name); - Block (spaced [ + Block ([ Inline inst_var_declarations; + Line (""); Inline from_json; - Inline from_json_string_declaration; + Inline (from_json_string_declaration cpp_struct_name); Inline to_json; Inline to_json_string_static; Inline to_json_string; @@ -1022,8 +1023,8 @@ let alias_wrapper env name type_expr = Line "}"; Line (sprintf "namespace %s {" cpp_struct_name); Block [ - Line (sprintf "auto from_json(const rapidjson::Value &doc);"); - Inline from_json_string_declaration; + Line (sprintf "typedefs::%s from_json(const rapidjson::Value &doc);" cpp_struct_name); + Inline (from_json_string_declaration (sprintf "typedefs::%s" cpp_struct_name)); Line (sprintf "void to_json(const typedefs::%s &t, rapidjson::Writer &writer);" cpp_struct_name); Line (sprintf "std::string to_json_string(const typedefs::%s &t);" cpp_struct_name); ]; @@ -1035,12 +1036,12 @@ let alias_wrapper_definition env name type_expr = [ Line (sprintf "namespace %s {" cpp_struct_name); Block [ - Line (sprintf "auto from_json(const rapidjson::Value &doc) {"); + Line (sprintf "typedefs::%s from_json(const rapidjson::Value &doc) {" cpp_struct_name); Block [ Line (sprintf "return %sdoc);" (json_reader env type_expr)); ]; Line "}"; - Inline (from_json_string_definition None); + Inline (from_json_string_definition (sprintf "typedefs::%s" cpp_struct_name) None); Line (sprintf "void to_json(const typedefs::%s &t, rapidjson::Writer &writer) {" cpp_struct_name); Block [ Line (sprintf "%st, writer);" (json_writer env type_expr)); @@ -1057,9 +1058,31 @@ let alias_wrapper_definition env name type_expr = Line "}"; ] +let case_class_definition env type_name (loc, orig_name, unique_name, an, opt_e) = + let json_name = Atd.Json.get_json_cons orig_name an in + match opt_e with + | None -> + [ + Line (sprintf "void %s::to_json(const %s &e, rapidjson::Writer &writer){" (trans env orig_name) (trans env orig_name)); + Block [ + Line (sprintf "writer.String(\"%s\");" (single_esc json_name)); + ]; + Line (sprintf "};"); + ] + | Some e -> + [ + Line (sprintf "void %s::to_json(const %s &e, rapidjson::Writer &writer){" (trans env orig_name) (trans env orig_name)); + Block [ + Line (sprintf "writer.StartArray();"); + Line (sprintf "writer.String(\"%s\");" (single_esc json_name)); + Line (sprintf "%se.value, writer);" (json_writer env e)); + Line (sprintf "writer.EndArray();"); + ]; + Line("}"); + ] + let case_class env type_name (loc, orig_name, unique_name, an, opt_e) = - let json_name = Atd.Json.get_json_cons orig_name an in match opt_e with | None -> [ @@ -1067,11 +1090,7 @@ let case_class env type_name type_name orig_name); Line (sprintf "struct %s {" (trans env orig_name)); - Line (sprintf "static void to_json(const %s &e, rapidjson::Writer &writer){" (trans env orig_name)); - Block [ - Line (sprintf "writer.String(\"%s\");" (single_esc json_name)); - ]; - Line("}"); + Block [Line (sprintf "static void to_json(const %s &e, rapidjson::Writer &writer);" (trans env orig_name));]; Line (sprintf "};"); ] | Some e -> @@ -1083,14 +1102,7 @@ let case_class env type_name Line(sprintf "{"); Block [ Line (sprintf "%s value;" (type_name_of_expr env e)); - Line (sprintf "static void to_json(const %s &e, rapidjson::Writer &writer){" (trans env orig_name)); - Block [ - Line (sprintf "writer.StartArray();"); - Line (sprintf "writer.String(\"%s\");" (single_esc json_name)); - Line (sprintf "%se.value, writer);" (json_writer env e)); - Line (sprintf "writer.EndArray();"); - ]; - Line("}"); + Line (sprintf "static void to_json(const %s &e, rapidjson::Writer &writer);" (trans env orig_name)); ]; Line(sprintf "};"); ] @@ -1141,7 +1153,7 @@ let read_cases1 env loc name cases1 = (struct_name env name |> single_esc)) ] -let sum_container env loc name cases = +let sum_container_definition env loc name cases = let cpp_struct_name = struct_name env name in let cases0, cases1 = List.partition (fun (loc, orig_name, unique_name, an, opt_e) -> @@ -1174,7 +1186,7 @@ let sum_container env loc name cases = [ Line (sprintf "namespace %s {" (cpp_struct_name)); Block [ - Line (sprintf "static atd::typedefs::%s from_json(const rapidjson::Value &x) {" (cpp_struct_name)); + Line (sprintf "typedefs::%s from_json(const rapidjson::Value &x) {" (cpp_struct_name)); Block [ Inline cases0_block; Inline cases1_block; @@ -1183,36 +1195,72 @@ let sum_container env loc name cases = ]; Line "}"; ]; - Block [Inline (from_json_string_definition (Some cpp_struct_name))]; + Block [Inline (from_json_string_definition (sprintf "typedefs::%s" cpp_struct_name) (None))]; Block [ - Line (sprintf "static void to_json(const atd::typedefs::%s &x, rapidjson::Writer &writer) {" (cpp_struct_name)); + Line (sprintf "void to_json(const atd::typedefs::%s &x, rapidjson::Writer &writer) {" (cpp_struct_name)); Block [ Line "std::visit([&writer](auto &&arg) {"; Block [ Line "using T = std::decay_t;"; - Line ( + Block ( List.map (fun (loc, orig_name, unique_name, an, opt_e) -> - sprintf "if constexpr (std::is_same_v) Types::%s::to_json(arg, writer);" (trans env orig_name) (trans env orig_name) + Line (sprintf "if constexpr (std::is_same_v) Types::%s::to_json(arg, writer);" (trans env orig_name) (trans env orig_name)) ) cases - |> String.concat "\n"); + ); ]; Line ("}, x);"); ]; Line ("}"); ]; - Line (sprintf "std::string to_json_string(const atd::typedefs::%s &x) {" (cpp_struct_name)); + Block [Line (sprintf "std::string to_json_string(const atd::typedefs::%s &x) {" (cpp_struct_name)); Block [ Line ("rapidjson::StringBuffer buffer;"); Line ("rapidjson::Writer writer(buffer);"); Line ("to_json(x, writer);"); Line "return buffer.GetString();" ]; - Line "}"; + Line "}";]; Line ("}"); ] +let sum_container env loc name cases = + let cpp_struct_name = struct_name env name in + [ + Line (sprintf "namespace %s {" (cpp_struct_name)); + Block [ + Line (sprintf "static atd::typedefs::%s from_json(const rapidjson::Value &x);" (cpp_struct_name)); + Inline (from_json_string_declaration (sprintf "atd::typedefs::%s" cpp_struct_name)); + Line (sprintf "static void to_json(const atd::typedefs::%s &x, rapidjson::Writer &writer);" (cpp_struct_name)); + Line (sprintf "std::string to_json_string(const atd::typedefs::%s &x);" (cpp_struct_name)); + ]; + Line ("}"); + ] + +let sum_definition env loc name cases = + let cases = + List.map (fun (x : variant) -> + match x with + | Variant (loc, (orig_name, an), opt_e) -> + let unique_name = create_struct_name env orig_name in + (loc, orig_name, unique_name, an, opt_e) + | Inherit _ -> assert false + ) cases + in + let case_classes = + List.map (fun x -> Inline (case_class_definition env name x)) cases + |> double_spaced + in + let container_class = sum_container_definition env loc name cases in + [ + Line (sprintf "namespace %s::Types {" (struct_name env name)); + Block case_classes; + Line (sprintf "}"); + Inline container_class; + ] + |> double_spaced + let sum env loc name cases = let cases = List.map (fun (x : variant) -> @@ -1242,7 +1290,6 @@ let sum env loc name cases = in let case_classes = List.map (fun x -> Inline (case_class env name x)) cases - |> double_spaced in let typedef_declaration = [ @@ -1260,7 +1307,6 @@ let sum env loc name cases = Line (sprintf "}"); Inline container_class; ] - |> double_spaced let type_def env ((loc, (name, param, an), e) : A.type_def) : B.t = if param <> [] then @@ -1268,7 +1314,7 @@ let type_def env ((loc, (name, param, an), e) : A.type_def) : B.t = let unwrap e = match e with | Sum (loc, cases, an) -> - sum env loc name cases + sum_definition env loc name cases | Record (loc, fields, an) -> record_definition env loc name fields an | Tuple _ @@ -1276,7 +1322,7 @@ let type_def env ((loc, (name, param, an), e) : A.type_def) : B.t = | Option _ | Nullable _ | Wrap _ - | Name _ -> alias_wrapper_definition env name e + | Name _ -> alias_wrapper_definition env name e | Shared _ -> not_implemented loc "cyclic references" | Tvar _ -> not_implemented loc "parametrized type" in @@ -1296,7 +1342,7 @@ let type_decl env ((loc, (name, param, an), e) : A.type_def) : B.t = | Option _ | Nullable _ | Wrap _ - | Name _ -> alias_wrapper env name e + | Name _ -> alias_wrapper env name e | Shared _ -> not_implemented loc "cyclic references" | Tvar _ -> not_implemented loc "parametrized type" in diff --git a/atdcpp/test/atd-input/everything.atd b/atdcpp/test/atd-input/everything.atd index 0606464fb..81bc87699 100644 --- a/atdcpp/test/atd-input/everything.atd +++ b/atdcpp/test/atd-input/everything.atd @@ -1,5 +1,5 @@ -(* + type kind = [ | Root (* class name conflict *) | Thing of int @@ -46,7 +46,7 @@ type root = { wrapped: st wrap ; aaa: alias_of_alias_of_alias; item: string wrap ; -} *) +} type st = int @@ -64,7 +64,7 @@ type credential = { name: string; password: int; } -(* + type credentials = { credentials: credential list; } @@ -108,4 +108,4 @@ type 'a recursive = 'a list type recursive_variant = [ | A | B of recursive_variant list -] *) \ No newline at end of file +] \ No newline at end of file diff --git a/atdcpp/test/cpp-expected/everything_atd.hpp b/atdcpp/test/cpp-expected/everything_atd.hpp index 2ec8e9300..88f4d50c9 100644 --- a/atdcpp/test/cpp-expected/everything_atd.hpp +++ b/atdcpp/test/cpp-expected/everything_atd.hpp @@ -3,11 +3,8 @@ // This implements classes for the types defined in 'everything.atd', providing // methods and functions to convert data from/to JSON. -// ############################################################################ -// # Private functions // ############################################################################ -// filename everything.atd #pragma once #include @@ -21,377 +18,39 @@ #include #include -class AtdException : public std::exception -{ -public: - AtdException(const std::string &message) : msg_(message) {} - - const char *what() const throw() override - { - return msg_.c_str(); - } - -private: - std::string msg_; -}; - -template -T _atd_missing_json_field(const std::string &type, const std::string &field) -{ - throw AtdException("Missing JSON field '" + field + "' in " + type); -} - -auto _atd_bad_json(const std::string &type, const rapidjson::Value &x) -{ - return AtdException("Bad JSON for " + type); -} - -// Reading an integer from JSON -int _atd_read_int(const rapidjson::Value &val) -{ - if (!val.IsInt()) - { - throw AtdException("Expected an integer"); - } - return val.GetInt(); -} - -bool _atd_read_bool(const rapidjson::Value &val) -{ - if (!val.IsBool()) - { - throw AtdException("Expected a boolean"); - } - return val.GetBool(); -} - -// Reading a float from JSON -float _atd_read_float(const rapidjson::Value &val) -{ - if (val.IsInt()) - { - return static_cast(val.GetInt()); - } - else if (val.IsUint()) - { - return static_cast(val.GetUint()); - } - if (!val.IsFloat()) - { - throw AtdException("Expected a float"); - } - - return val.GetFloat(); -} - -std::string _atd_read_string(const rapidjson::Value &val) -{ - if (!val.IsString()) - { - throw AtdException("Expected a string"); - } - return val.GetString(); -} - -template -auto _atd_read_array(F read_func, const rapidjson::Value &val) -{ - using ResultType = typename std::invoke_result::type; - - if (!val.IsArray()) - { - throw AtdException("Expected an array"); // Or your specific exception type - } - - std::vector result; - for (rapidjson::SizeType i = 0; i < val.Size(); i++) - { - result.push_back(read_func(val[i])); - } - - return result; -} - -template -auto _atd_read_object_to_tuple_list(F read_func, const rapidjson::Value &val) -{ - using ResultType = typename std::invoke_result::type; - - if (!val.IsObject()) - { - throw AtdException("Expected an object"); // Or your specific exception type - } - - std::vector> result; - for (auto &m : val.GetObject()) - { - result.push_back(std::make_tuple(m.name.GetString(), read_func(m.value))); - } - - return result; -} - -template -auto _atd_read_array_to_assoc_dict(RK read_key_func, RV read_value_func, const rapidjson::Value &val) -{ - using KeyType = typename std::invoke_result::type; - using ValueType = typename std::invoke_result::type; - - if (!val.IsArray()) - { - throw AtdException("Expected an array"); // Or your specific exception type - } - - std::map result; - for (rapidjson::SizeType i = 0; i < val.Size(); i++) - { - auto &pair = val[i]; - if (!pair.IsArray() || pair.Size() != 2) - { - throw AtdException("Expected an array of pairs"); - } - result[read_key_func(pair[0])] = read_value_func(pair[1]); - } - - return result; -} - -template -auto _atd_read_object_to_assoc_array(F read_func, const rapidjson::Value &val) -{ - using ResultType = typename std::invoke_result::type; - - if (!val.IsObject()) - { - throw AtdException("Expected an object"); // Or your specific exception type - } - - std::map result; - for (auto &m : val.GetObject()) - { - result[m.name.GetString()] = read_func(m.value); - } - - return result; -} - -template -auto _atd_read_nullable(F read_func, const rapidjson::Value &val) -{ - if (val.IsNull()) - { - return std::optional::type>(); - } - return std::optional::type>(read_func(val)); -} - -template -auto _atd_read_option(F read_func, const rapidjson::Value &val) -{ - using ResultType = typename std::invoke_result::type; - if (val.IsString() && std::string_view(val.GetString()) == "None") - { - return std::optional(); - } - else if (val.IsArray() && val.Size() == 2 && val[0].IsString() && std::string_view(val[0].GetString()) == "Some") - { - return std::make_optional(read_func(val[1])); - } - else - { - throw AtdException("Expected an option"); - } -} - -template -auto _atd_read_wrap(F read_func, W wrap_func, const rapidjson::Value &val) -{ - return wrap_func(read_func(val)); -} - -void _atd_write_int(int value, rapidjson::Writer& writer) -{ - writer.Int(value); -} - -void _atd_write_bool(bool value, rapidjson::Writer& writer) -{ - writer.Bool(value); -} -void _atd_write_float(float value, rapidjson::Writer& writer) -{ - writer.Double(value); -} -void _atd_write_string(const std::string &value, rapidjson::Writer& writer) -{ - writer.String(value.c_str()); -} -template -void _atd_write_array(F write_func, const V& values, rapidjson::Writer& writer) -{ - writer.StartArray(); - for (const auto& value : values) - { - write_func(value, writer); - } - writer.EndArray(); -} - -template -void _atd_write_tuple_list_to_object(F write_func, const V &values, rapidjson::Writer& writer) -{ - writer.StartObject(); - for (const auto& value : values) - { - writer.Key(std::get<0>(value).c_str()); - write_func(std::get<1>(value), writer); - } - writer.EndObject(); -} - -template -void _atd_write_assoc_dict_to_array(const Wk write_key_func, const Wv write_value_func, const Map &value_map, rapidjson::Writer& writer) -{ - writer.StartArray(); - for (const auto& pair : value_map) - { - writer.StartArray(); - write_key_func(pair.first, writer); - write_value_func(pair.second, writer); - writer.EndArray(); - } - writer.EndArray(); -} - -template -void _atd_write_assoc_array_to_object(F write_func, const Map &value_map, rapidjson::Writer& writer) -{ - writer.StartObject(); - for (const auto& pair : value_map) - { - writer.Key(pair.first.c_str()); - write_func(pair.second, writer); - } - writer.EndObject(); -} - - -template -void _atd_write_option(F write_func, const O &val, rapidjson::Writer& writer) -{ - if (val) - { - writer.StartArray(); - writer.String("Some"); - write_func(*val, writer); - writer.EndArray(); - } - else - { - writer.String("None"); - } -} - -template -void _atd_write_nullable(F write_func, const O &val, rapidjson::Writer& writer) -{ - if (val) - { - write_func(*val, writer); - } - else - { - writer.Null(); - } -} - -template -void _atd_write_wrap(F write_func, W wrap_func, const T &val, rapidjson::Writer& writer) -{ - write_func(wrap_func(val), writer); -} - - +#include -#include +namespace atd { namespace RecursiveVariant::Types { struct A; struct B; } - - +namespace typedefs { + typedef std::variant RecursiveVariant; +} namespace RecursiveVariant::Types { - - // Original type: recursive_variant = [ ... | A | ... ] struct A { - static void to_json(const A &e, rapidjson::Writer &writer){ - writer.String("A"); - } + static void to_json(const A &e, rapidjson::Writer &writer); }; - - // Original type: recursive_variant = [ ... | B of ... | ... ] struct B { std::vector value; - static void to_json(const B &e, rapidjson::Writer &writer){ - writer.StartArray(); - writer.String("B"); - _atd_write_array([](auto v, auto &w){RecursiveVariant::to_json(v, w);}, e.value, writer); - writer.EndArray(); - } + static void to_json(const B &e, rapidjson::Writer &writer); }; - - -} - - -namespace typedefs { - typedef std::variant<::RecursiveVariant::Types::A, ::RecursiveVariant::Types::B> RecursiveVariant; } namespace RecursiveVariant { - static ::typedefs::RecursiveVariant from_json(const rapidjson::Value &x) { - if (x.IsString()) { - if (std::string_view(x.GetString()) == "A") - return Types::A(); - throw _atd_bad_json("RecursiveVariant", x); - } - if (x.IsArray() && x.Size() == 2 && x[0].IsString()) { - std::string cons = x[0].GetString(); - if (cons == "B") - return Types::B({_atd_read_array([](const auto &v){return RecursiveVariant::from_json(v);}, x[1])}); - throw _atd_bad_json("RecursiveVariant", x); - } - throw _atd_bad_json("RecursiveVariant", x); - } - static auto from_json_string(const std::string &s) { - rapidjson::Document doc; - doc.Parse(s.c_str()); - if (doc.HasParseError()) { - throw AtdException("Failed to parse JSON"); - } - return from_json(doc); - } - static void to_json(const ::typedefs::RecursiveVariant &x, rapidjson::Writer &writer) { - std::visit([&writer](auto &&arg) { - using T = std::decay_t; - if constexpr (std::is_same_v) Types::A::to_json(arg, writer); -if constexpr (std::is_same_v) Types::B::to_json(arg, writer); - }, x); - } -std::string to_json_string(const ::typedefs::RecursiveVariant &x) { - rapidjson::StringBuffer buffer; - rapidjson::Writer writer(buffer); - to_json(x, writer); - return buffer.GetString(); -} + static atd::typedefs::RecursiveVariant from_json(const rapidjson::Value &x); + static atd::typedefs::RecursiveVariant from_json_string(const std::string &s); + static void to_json(const atd::typedefs::RecursiveVariant &x, rapidjson::Writer &writer); + std::string to_json_string(const atd::typedefs::RecursiveVariant &x); } @@ -404,53 +63,11 @@ struct RecursiveClass { bool flag; std::vector children; - static RecursiveClass from_json(const rapidjson::Value & doc) { - RecursiveClass record; - if (!doc.IsObject()) { - throw AtdException("Expected an object"); - } - if (doc.HasMember("id")) - record.id = _atd_read_int(doc["id"]); - else record.id = _atd_missing_json_field("RecursiveClass", "id"); - if (doc.HasMember("flag")) - record.flag = _atd_read_bool(doc["flag"]); - else record.flag = _atd_missing_json_field("RecursiveClass", "flag"); - if (doc.HasMember("children")) - record.children = _atd_read_array([](const auto &v){return RecursiveClass::from_json(v);}, doc["children"]); - else record.children = _atd_missing_json_field("RecursiveClass", "children"); - return record; - } - - static auto from_json_string(const std::string &s) { - rapidjson::Document doc; - doc.Parse(s.c_str()); - if (doc.HasParseError()) { - throw AtdException("Failed to parse JSON"); - } - return from_json(doc); - } - - static void to_json(const RecursiveClass &t, rapidjson::Writer &writer) { - writer.StartObject(); - writer.Key("id"); - _atd_write_int(t.id, writer); - writer.Key("flag"); - _atd_write_bool(t.flag, writer); - writer.Key("children"); - _atd_write_array([](auto v, auto &w){RecursiveClass::to_json(v, w);}, t.children, writer); - writer.EndObject(); - } - - static std::string to_json_string(const RecursiveClass &t) { - rapidjson::StringBuffer buffer; - rapidjson::Writer writer(buffer); - to_json(t, writer); - return buffer.GetString(); - } - - std::string to_json_string() { - return to_json_string(*this); - } + static RecursiveClass from_json(const rapidjson::Value & doc); + static RecursiveClass from_json_string(const std::string &s); + static void to_json(const RecursiveClass &t, rapidjson::Writer &writer); + static std::string to_json_string(const RecursiveClass &t); + std::string to_json_string(); }; @@ -461,43 +78,11 @@ namespace typedefs { struct ThreeLevelNestedListRecord { std::vector>> field_a; - static ThreeLevelNestedListRecord from_json(const rapidjson::Value & doc) { - ThreeLevelNestedListRecord record; - if (!doc.IsObject()) { - throw AtdException("Expected an object"); - } - if (doc.HasMember("field_a")) - record.field_a = _atd_read_array([](const auto &v){return _atd_read_array([](const auto &v){return _atd_read_array([](const auto &v){return _atd_read_int(v);}, v);}, v);}, doc["field_a"]); - else record.field_a = _atd_missing_json_field("ThreeLevelNestedListRecord", "field_a"); - return record; - } - - static auto from_json_string(const std::string &s) { - rapidjson::Document doc; - doc.Parse(s.c_str()); - if (doc.HasParseError()) { - throw AtdException("Failed to parse JSON"); - } - return from_json(doc); - } - - static void to_json(const ThreeLevelNestedListRecord &t, rapidjson::Writer &writer) { - writer.StartObject(); - writer.Key("field_a"); - _atd_write_array([](auto v, auto &w){_atd_write_array([](auto v, auto &w){_atd_write_array([](auto v, auto &w){_atd_write_int(v, w);}, v, w);}, v, w);}, t.field_a, writer); - writer.EndObject(); - } - - static std::string to_json_string(const ThreeLevelNestedListRecord &t) { - rapidjson::StringBuffer buffer; - rapidjson::Writer writer(buffer); - to_json(t, writer); - return buffer.GetString(); - } - - std::string to_json_string() { - return to_json_string(*this); - } + static ThreeLevelNestedListRecord from_json(const rapidjson::Value & doc); + static ThreeLevelNestedListRecord from_json_string(const std::string &s); + static void to_json(const ThreeLevelNestedListRecord &t, rapidjson::Writer &writer); + static std::string to_json_string(const ThreeLevelNestedListRecord &t); + std::string to_json_string(); }; @@ -505,26 +90,10 @@ namespace typedefs { typedef int St; } namespace St { - auto from_json(const rapidjson::Value &doc) { - return _atd_read_int(doc); - } - static auto from_json_string(const std::string &s) { - rapidjson::Document doc; - doc.Parse(s.c_str()); - if (doc.HasParseError()) { - throw AtdException("Failed to parse JSON"); - } - return from_json(doc); - } - void to_json(const typedefs::St &t, rapidjson::Writer &writer) { - _atd_write_int(t, writer); - } - std::string to_json_string(const typedefs::St &t) { - rapidjson::StringBuffer buffer; - rapidjson::Writer writer(buffer); - to_json(t, writer); - return buffer.GetString(); - } + typedefs::St from_json(const rapidjson::Value &doc); + static typedefs::St from_json_string(const std::string &s); + void to_json(const typedefs::St &t, rapidjson::Writer &writer); + std::string to_json_string(const typedefs::St &t); } @@ -534,101 +103,36 @@ namespace Kind::Types { struct WOW; struct Amaze; } - - +namespace typedefs { + typedef std::variant Kind; +} namespace Kind::Types { - - // Original type: kind = [ ... | Root | ... ] struct Root { - static void to_json(const Root &e, rapidjson::Writer &writer){ - writer.String("Root"); - } + static void to_json(const Root &e, rapidjson::Writer &writer); }; - - // Original type: kind = [ ... | Thing of ... | ... ] struct Thing { int value; - static void to_json(const Thing &e, rapidjson::Writer &writer){ - writer.StartArray(); - writer.String("Thing"); - _atd_write_int(e.value, writer); - writer.EndArray(); - } + static void to_json(const Thing &e, rapidjson::Writer &writer); }; - - // Original type: kind = [ ... | WOW | ... ] struct WOW { - static void to_json(const WOW &e, rapidjson::Writer &writer){ - writer.String("wow"); - } + static void to_json(const WOW &e, rapidjson::Writer &writer); }; - - // Original type: kind = [ ... | Amaze of ... | ... ] struct Amaze { std::vector value; - static void to_json(const Amaze &e, rapidjson::Writer &writer){ - writer.StartArray(); - writer.String("!!!"); - _atd_write_array([](auto v, auto &w){_atd_write_string(v, w);}, e.value, writer); - writer.EndArray(); - } + static void to_json(const Amaze &e, rapidjson::Writer &writer); }; - - -} - - -namespace typedefs { - typedef std::variant<::Kind::Types::Root, ::Kind::Types::Thing, ::Kind::Types::WOW, ::Kind::Types::Amaze> Kind; } namespace Kind { - static ::typedefs::Kind from_json(const rapidjson::Value &x) { - if (x.IsString()) { - if (std::string_view(x.GetString()) == "Root") - return Types::Root(); - if (std::string_view(x.GetString()) == "wow") - return Types::WOW(); - throw _atd_bad_json("Kind", x); - } - if (x.IsArray() && x.Size() == 2 && x[0].IsString()) { - std::string cons = x[0].GetString(); - if (cons == "Thing") - return Types::Thing({_atd_read_int(x[1])}); - if (cons == "!!!") - return Types::Amaze({_atd_read_array([](const auto &v){return _atd_read_string(v);}, x[1])}); - throw _atd_bad_json("Kind", x); - } - throw _atd_bad_json("Kind", x); - } - static auto from_json_string(const std::string &s) { - rapidjson::Document doc; - doc.Parse(s.c_str()); - if (doc.HasParseError()) { - throw AtdException("Failed to parse JSON"); - } - return from_json(doc); - } - static void to_json(const ::typedefs::Kind &x, rapidjson::Writer &writer) { - std::visit([&writer](auto &&arg) { - using T = std::decay_t; - if constexpr (std::is_same_v) Types::Root::to_json(arg, writer); -if constexpr (std::is_same_v) Types::Thing::to_json(arg, writer); -if constexpr (std::is_same_v) Types::WOW::to_json(arg, writer); -if constexpr (std::is_same_v) Types::Amaze::to_json(arg, writer); - }, x); - } -std::string to_json_string(const ::typedefs::Kind &x) { - rapidjson::StringBuffer buffer; - rapidjson::Writer writer(buffer); - to_json(x, writer); - return buffer.GetString(); -} + static atd::typedefs::Kind from_json(const rapidjson::Value &x); + static atd::typedefs::Kind from_json_string(const std::string &s); + static void to_json(const atd::typedefs::Kind &x, rapidjson::Writer &writer); + std::string to_json_string(const atd::typedefs::Kind &x); } @@ -636,26 +140,10 @@ namespace typedefs { typedef uint32_t Alias3; } namespace Alias3 { - auto from_json(const rapidjson::Value &doc) { - return _atd_read_wrap([](const auto& v){return _atd_read_int(v);}, [](const auto &e){return static_cast(e);},doc); - } - static auto from_json_string(const std::string &s) { - rapidjson::Document doc; - doc.Parse(s.c_str()); - if (doc.HasParseError()) { - throw AtdException("Failed to parse JSON"); - } - return from_json(doc); - } - void to_json(const typedefs::Alias3 &t, rapidjson::Writer &writer) { - _atd_write_wrap([](const auto &v, auto &w){_atd_write_int(v, w);}, [](const auto &e){return static_cast(e);}, t, writer); - } - std::string to_json_string(const typedefs::Alias3 &t) { - rapidjson::StringBuffer buffer; - rapidjson::Writer writer(buffer); - to_json(t, writer); - return buffer.GetString(); - } + typedefs::Alias3 from_json(const rapidjson::Value &doc); + static typedefs::Alias3 from_json_string(const std::string &s); + void to_json(const typedefs::Alias3 &t, rapidjson::Writer &writer); + std::string to_json_string(const typedefs::Alias3 &t); } @@ -663,26 +151,10 @@ namespace typedefs { typedef typedefs::Alias3 AliasOfAliasNotWrapped; } namespace AliasOfAliasNotWrapped { - auto from_json(const rapidjson::Value &doc) { - return Alias3::from_json(doc); - } - static auto from_json_string(const std::string &s) { - rapidjson::Document doc; - doc.Parse(s.c_str()); - if (doc.HasParseError()) { - throw AtdException("Failed to parse JSON"); - } - return from_json(doc); - } - void to_json(const typedefs::AliasOfAliasNotWrapped &t, rapidjson::Writer &writer) { - Alias3::to_json(t, writer); - } - std::string to_json_string(const typedefs::AliasOfAliasNotWrapped &t) { - rapidjson::StringBuffer buffer; - rapidjson::Writer writer(buffer); - to_json(t, writer); - return buffer.GetString(); - } + typedefs::AliasOfAliasNotWrapped from_json(const rapidjson::Value &doc); + static typedefs::AliasOfAliasNotWrapped from_json_string(const std::string &s); + void to_json(const typedefs::AliasOfAliasNotWrapped &t, rapidjson::Writer &writer); + std::string to_json_string(const typedefs::AliasOfAliasNotWrapped &t); } @@ -690,26 +162,10 @@ namespace typedefs { typedef typedefs::AliasOfAliasNotWrapped AliasOfAliasOfAlias; } namespace AliasOfAliasOfAlias { - auto from_json(const rapidjson::Value &doc) { - return AliasOfAliasNotWrapped::from_json(doc); - } - static auto from_json_string(const std::string &s) { - rapidjson::Document doc; - doc.Parse(s.c_str()); - if (doc.HasParseError()) { - throw AtdException("Failed to parse JSON"); - } - return from_json(doc); - } - void to_json(const typedefs::AliasOfAliasOfAlias &t, rapidjson::Writer &writer) { - AliasOfAliasNotWrapped::to_json(t, writer); - } - std::string to_json_string(const typedefs::AliasOfAliasOfAlias &t) { - rapidjson::StringBuffer buffer; - rapidjson::Writer writer(buffer); - to_json(t, writer); - return buffer.GetString(); - } + typedefs::AliasOfAliasOfAlias from_json(const rapidjson::Value &doc); + static typedefs::AliasOfAliasOfAlias from_json_string(const std::string &s); + void to_json(const typedefs::AliasOfAliasOfAlias &t, rapidjson::Writer &writer); + std::string to_json_string(const typedefs::AliasOfAliasOfAlias &t); } @@ -717,26 +173,10 @@ namespace typedefs { typedef std::vector Alias; } namespace Alias { - auto from_json(const rapidjson::Value &doc) { - return _atd_read_array([](const auto &v){return _atd_read_int(v);}, doc); - } - static auto from_json_string(const std::string &s) { - rapidjson::Document doc; - doc.Parse(s.c_str()); - if (doc.HasParseError()) { - throw AtdException("Failed to parse JSON"); - } - return from_json(doc); - } - void to_json(const typedefs::Alias &t, rapidjson::Writer &writer) { - _atd_write_array([](auto v, auto &w){_atd_write_int(v, w);}, t, writer); - } - std::string to_json_string(const typedefs::Alias &t) { - rapidjson::StringBuffer buffer; - rapidjson::Writer writer(buffer); - to_json(t, writer); - return buffer.GetString(); - } + typedefs::Alias from_json(const rapidjson::Value &doc); + static typedefs::Alias from_json_string(const std::string &s); + void to_json(const typedefs::Alias &t, rapidjson::Writer &writer); + std::string to_json_string(const typedefs::Alias &t); } @@ -744,34 +184,10 @@ namespace typedefs { typedef std::tuple KindParametrizedTuple; } namespace KindParametrizedTuple { - auto from_json(const rapidjson::Value &doc) { - return [](auto &v){ - if (!v.IsArray() || v.Size() != 3) - throw AtdException("Tuple of size 3"); - return std::make_tuple(Kind::from_json(v[0]), Kind::from_json(v[1]), _atd_read_int(v[2])); - }(doc); - } - static auto from_json_string(const std::string &s) { - rapidjson::Document doc; - doc.Parse(s.c_str()); - if (doc.HasParseError()) { - throw AtdException("Failed to parse JSON"); - } - return from_json(doc); - } - void to_json(const typedefs::KindParametrizedTuple &t, rapidjson::Writer &writer) { - [](const auto &t, auto &writer){ - writer.StartArray(); - Kind::to_json(std::get<0>(t), writer); Kind::to_json(std::get<1>(t), writer); _atd_write_int(std::get<2>(t), writer); - writer.EndArray(); - }(t, writer); - } - std::string to_json_string(const typedefs::KindParametrizedTuple &t) { - rapidjson::StringBuffer buffer; - rapidjson::Writer writer(buffer); - to_json(t, writer); - return buffer.GetString(); - } + typedefs::KindParametrizedTuple from_json(const rapidjson::Value &doc); + static typedefs::KindParametrizedTuple from_json_string(const std::string &s); + void to_json(const typedefs::KindParametrizedTuple &t, rapidjson::Writer &writer); + std::string to_json_string(const typedefs::KindParametrizedTuple &t); } @@ -783,48 +199,11 @@ struct IntFloatParametrizedRecord { int field_a; std::vector field_b = {}; - static IntFloatParametrizedRecord from_json(const rapidjson::Value & doc) { - IntFloatParametrizedRecord record; - if (!doc.IsObject()) { - throw AtdException("Expected an object"); - } - if (doc.HasMember("field_a")) - record.field_a = _atd_read_int(doc["field_a"]); - else record.field_a = _atd_missing_json_field("IntFloatParametrizedRecord", "field_a"); - if (doc.HasMember("field_b")) - record.field_b = _atd_read_array([](const auto &v){return _atd_read_float(v);}, doc["field_b"]); - else record.field_b = {}; - return record; - } - - static auto from_json_string(const std::string &s) { - rapidjson::Document doc; - doc.Parse(s.c_str()); - if (doc.HasParseError()) { - throw AtdException("Failed to parse JSON"); - } - return from_json(doc); - } - - static void to_json(const IntFloatParametrizedRecord &t, rapidjson::Writer &writer) { - writer.StartObject(); - writer.Key("field_a"); - _atd_write_int(t.field_a, writer); - writer.Key("field_b"); - _atd_write_array([](auto v, auto &w){_atd_write_float(v, w);}, t.field_b, writer); - writer.EndObject(); - } - - static std::string to_json_string(const IntFloatParametrizedRecord &t) { - rapidjson::StringBuffer buffer; - rapidjson::Writer writer(buffer); - to_json(t, writer); - return buffer.GetString(); - } - - std::string to_json_string() { - return to_json_string(*this); - } + static IntFloatParametrizedRecord from_json(const rapidjson::Value & doc); + static IntFloatParametrizedRecord from_json_string(const std::string &s); + static void to_json(const IntFloatParametrizedRecord &t, rapidjson::Writer &writer); + static std::string to_json_string(const IntFloatParametrizedRecord &t); + std::string to_json_string(); }; @@ -859,181 +238,11 @@ struct Root { typedefs::AliasOfAliasOfAlias aaa; int item; - static Root from_json(const rapidjson::Value & doc) { - Root record; - if (!doc.IsObject()) { - throw AtdException("Expected an object"); - } - if (doc.HasMember("ID")) - record.id = _atd_read_string(doc["ID"]); - else record.id = _atd_missing_json_field("Root", "ID"); - if (doc.HasMember("await")) - record.await = _atd_read_bool(doc["await"]); - else record.await = _atd_missing_json_field("Root", "await"); - if (doc.HasMember("integer")) - record.integer = _atd_read_int(doc["integer"]); - else record.integer = _atd_missing_json_field("Root", "integer"); - if (doc.HasMember("__init__")) - record.x___init__ = _atd_read_float(doc["__init__"]); - else record.x___init__ = _atd_missing_json_field("Root", "__init__"); - if (doc.HasMember("float_with_auto_default")) - record.float_with_auto_default = _atd_read_float(doc["float_with_auto_default"]); - else record.float_with_auto_default = 0.0; - if (doc.HasMember("float_with_default")) - record.float_with_default = _atd_read_float(doc["float_with_default"]); - else record.float_with_default = 0.1; - if (doc.HasMember("items")) - record.items = _atd_read_array([](const auto &v){return _atd_read_array([](const auto &v){return _atd_read_int(v);}, v);}, doc["items"]); - else record.items = _atd_missing_json_field("Root", "items"); - if (doc.HasMember("maybe")) - record.maybe = _atd_read_option([](const auto &v){return _atd_read_int(v);}, doc["maybe"]); - else record.maybe = std::nullopt; - if (doc.HasMember("extras")) - record.extras = _atd_read_array([](const auto &v){return _atd_read_int(v);}, doc["extras"]); - else record.extras = {}; - if (doc.HasMember("answer")) - record.answer = _atd_read_int(doc["answer"]); - else record.answer = 42; - if (doc.HasMember("aliased")) - record.aliased = Alias::from_json(doc["aliased"]); - else record.aliased = _atd_missing_json_field("Root", "aliased"); - if (doc.HasMember("point")) - record.point = [](auto &v){ - if (!v.IsArray() || v.Size() != 2) - throw AtdException("Tuple of size 2"); - return std::make_tuple(_atd_read_float(v[0]), _atd_read_float(v[1])); - }(doc["point"]); - else record.point = _atd_missing_json_field("Root", "point"); - if (doc.HasMember("kind")) - record.kind = Kind::from_json(doc["kind"]); - else record.kind = _atd_missing_json_field("Root", "kind"); - if (doc.HasMember("kinds")) - record.kinds = _atd_read_array([](const auto &v){return Kind::from_json(v);}, doc["kinds"]); - else record.kinds = _atd_missing_json_field("Root", "kinds"); - if (doc.HasMember("assoc1")) - record.assoc1 = _atd_read_array([](const auto &v){return [](auto &v){ - if (!v.IsArray() || v.Size() != 2) - throw AtdException("Tuple of size 2"); - return std::make_tuple(_atd_read_float(v[0]), _atd_read_int(v[1])); - }(v);}, doc["assoc1"]); - else record.assoc1 = _atd_missing_json_field("Root", "assoc1"); - if (doc.HasMember("assoc2")) - record.assoc2 = _atd_read_object_to_tuple_list([](const auto &v){return _atd_read_int(v);},doc["assoc2"]); - else record.assoc2 = _atd_missing_json_field("Root", "assoc2"); - if (doc.HasMember("assoc3")) - record.assoc3 = _atd_read_array_to_assoc_dict([](const auto &k){return _atd_read_float(k);}, [](const auto &v){return _atd_read_int(v);}, doc["assoc3"]); - else record.assoc3 = _atd_missing_json_field("Root", "assoc3"); - if (doc.HasMember("assoc4")) - record.assoc4 = _atd_read_object_to_assoc_array([](const auto &v){return _atd_read_int(v);},doc["assoc4"]); - else record.assoc4 = _atd_missing_json_field("Root", "assoc4"); - if (doc.HasMember("nullables")) - record.nullables = _atd_read_array([](const auto &v){return _atd_read_nullable([](const auto &v){return _atd_read_int(v);}, v);}, doc["nullables"]); - else record.nullables = _atd_missing_json_field("Root", "nullables"); - if (doc.HasMember("options")) - record.options = _atd_read_array([](const auto &v){return _atd_read_option([](const auto &v){return _atd_read_int(v);}, v);}, doc["options"]); - else record.options = _atd_missing_json_field("Root", "options"); - if (doc.HasMember("parametrized_record")) - record.parametrized_record = IntFloatParametrizedRecord::from_json(doc["parametrized_record"]); - else record.parametrized_record = _atd_missing_json_field("Root", "parametrized_record"); - if (doc.HasMember("parametrized_tuple")) - record.parametrized_tuple = KindParametrizedTuple::from_json(doc["parametrized_tuple"]); - else record.parametrized_tuple = _atd_missing_json_field("Root", "parametrized_tuple"); - if (doc.HasMember("wrapped")) - record.wrapped = _atd_read_wrap([](const auto& v){return St::from_json(v);}, [](const auto &e){return [](typedefs::St st){return st - 1;}(e);},doc["wrapped"]); - else record.wrapped = _atd_missing_json_field("Root", "wrapped"); - if (doc.HasMember("aaa")) - record.aaa = AliasOfAliasOfAlias::from_json(doc["aaa"]); - else record.aaa = _atd_missing_json_field("Root", "aaa"); - if (doc.HasMember("item")) - record.item = _atd_read_wrap([](const auto& v){return _atd_read_string(v);}, [](const auto &e){return std::stoi(e);},doc["item"]); - else record.item = _atd_missing_json_field("Root", "item"); - return record; - } - - static auto from_json_string(const std::string &s) { - rapidjson::Document doc; - doc.Parse(s.c_str()); - if (doc.HasParseError()) { - throw AtdException("Failed to parse JSON"); - } - return from_json(doc); - } - - static void to_json(const Root &t, rapidjson::Writer &writer) { - writer.StartObject(); - writer.Key("ID"); - _atd_write_string(t.id, writer); - writer.Key("await"); - _atd_write_bool(t.await, writer); - writer.Key("integer"); - _atd_write_int(t.integer, writer); - writer.Key("__init__"); - _atd_write_float(t.x___init__, writer); - writer.Key("float_with_auto_default"); - _atd_write_float(t.float_with_auto_default, writer); - writer.Key("float_with_default"); - _atd_write_float(t.float_with_default, writer); - writer.Key("items"); - _atd_write_array([](auto v, auto &w){_atd_write_array([](auto v, auto &w){_atd_write_int(v, w);}, v, w);}, t.items, writer); - if (t.maybe != std::nullopt) { - writer.Key("maybe"); - _atd_write_option([](const auto &v, auto &w){_atd_write_int(v, w);}, t.maybe, writer); - } - writer.Key("extras"); - _atd_write_array([](auto v, auto &w){_atd_write_int(v, w);}, t.extras, writer); - writer.Key("answer"); - _atd_write_int(t.answer, writer); - writer.Key("aliased"); - Alias::to_json(t.aliased, writer); - writer.Key("point"); - [](const auto &t, auto &writer){ - writer.StartArray(); - _atd_write_float(std::get<0>(t), writer); _atd_write_float(std::get<1>(t), writer); - writer.EndArray(); - }(t.point, writer); - writer.Key("kind"); - Kind::to_json(t.kind, writer); - writer.Key("kinds"); - _atd_write_array([](auto v, auto &w){Kind::to_json(v, w);}, t.kinds, writer); - writer.Key("assoc1"); - _atd_write_array([](auto v, auto &w){[](const auto &t, auto &writer){ - writer.StartArray(); - _atd_write_float(std::get<0>(t), writer); _atd_write_int(std::get<1>(t), writer); - writer.EndArray(); - }(v, w);}, t.assoc1, writer); - writer.Key("assoc2"); - _atd_write_tuple_list_to_object([](auto v, auto &w){_atd_write_int(v, w);}, t.assoc2, writer); - writer.Key("assoc3"); - _atd_write_assoc_dict_to_array([](auto v, auto &w){_atd_write_float(v, w);}, [](auto v, auto &w){_atd_write_int(v, w);}, t.assoc3, writer); - writer.Key("assoc4"); - _atd_write_assoc_array_to_object([](auto v, auto &w){_atd_write_int(v, w);}, t.assoc4, writer); - writer.Key("nullables"); - _atd_write_array([](auto v, auto &w){_atd_write_nullable([](auto v, auto &w){_atd_write_int(v, w);}, v, w);}, t.nullables, writer); - writer.Key("options"); - _atd_write_array([](auto v, auto &w){_atd_write_option([](auto v, auto &w){_atd_write_int(v, w);}, v, w);}, t.options, writer); - writer.Key("parametrized_record"); - IntFloatParametrizedRecord::to_json(t.parametrized_record, writer); - writer.Key("parametrized_tuple"); - KindParametrizedTuple::to_json(t.parametrized_tuple, writer); - writer.Key("wrapped"); - _atd_write_wrap([](const auto &v, auto &w){St::to_json(v, w);}, [](const auto &e){return [](uint16_t e){return e + 1;}(e);}, t.wrapped, writer); - writer.Key("aaa"); - AliasOfAliasOfAlias::to_json(t.aaa, writer); - writer.Key("item"); - _atd_write_wrap([](const auto &v, auto &w){_atd_write_string(v, w);}, [](const auto &e){return std::to_string(e);}, t.item, writer); - writer.EndObject(); - } - - static std::string to_json_string(const Root &t) { - rapidjson::StringBuffer buffer; - rapidjson::Writer writer(buffer); - to_json(t, writer); - return buffer.GetString(); - } - - std::string to_json_string() { - return to_json_string(*this); - } + static Root from_json(const rapidjson::Value & doc); + static Root from_json_string(const std::string &s); + static void to_json(const Root &t, rapidjson::Writer &writer); + static std::string to_json_string(const Root &t); + std::string to_json_string(); }; @@ -1044,43 +253,11 @@ namespace typedefs { struct RequireField { std::string req; - static RequireField from_json(const rapidjson::Value & doc) { - RequireField record; - if (!doc.IsObject()) { - throw AtdException("Expected an object"); - } - if (doc.HasMember("req")) - record.req = _atd_read_string(doc["req"]); - else record.req = _atd_missing_json_field("RequireField", "req"); - return record; - } - - static auto from_json_string(const std::string &s) { - rapidjson::Document doc; - doc.Parse(s.c_str()); - if (doc.HasParseError()) { - throw AtdException("Failed to parse JSON"); - } - return from_json(doc); - } - - static void to_json(const RequireField &t, rapidjson::Writer &writer) { - writer.StartObject(); - writer.Key("req"); - _atd_write_string(t.req, writer); - writer.EndObject(); - } - - static std::string to_json_string(const RequireField &t) { - rapidjson::StringBuffer buffer; - rapidjson::Writer writer(buffer); - to_json(t, writer); - return buffer.GetString(); - } - - std::string to_json_string() { - return to_json_string(*this); - } + static RequireField from_json(const rapidjson::Value & doc); + static RequireField from_json_string(const std::string &s); + static void to_json(const RequireField &t, rapidjson::Writer &writer); + static std::string to_json_string(const RequireField &t); + std::string to_json_string(); }; @@ -1091,43 +268,11 @@ namespace typedefs { struct RecordWithWrappedType { int item; - static RecordWithWrappedType from_json(const rapidjson::Value & doc) { - RecordWithWrappedType record; - if (!doc.IsObject()) { - throw AtdException("Expected an object"); - } - if (doc.HasMember("item")) - record.item = _atd_read_wrap([](const auto& v){return _atd_read_string(v);}, [](const auto &e){return std::stoi(e);},doc["item"]); - else record.item = _atd_missing_json_field("RecordWithWrappedType", "item"); - return record; - } - - static auto from_json_string(const std::string &s) { - rapidjson::Document doc; - doc.Parse(s.c_str()); - if (doc.HasParseError()) { - throw AtdException("Failed to parse JSON"); - } - return from_json(doc); - } - - static void to_json(const RecordWithWrappedType &t, rapidjson::Writer &writer) { - writer.StartObject(); - writer.Key("item"); - _atd_write_wrap([](const auto &v, auto &w){_atd_write_string(v, w);}, [](const auto &e){return std::to_string(e);}, t.item, writer); - writer.EndObject(); - } - - static std::string to_json_string(const RecordWithWrappedType &t) { - rapidjson::StringBuffer buffer; - rapidjson::Writer writer(buffer); - to_json(t, writer); - return buffer.GetString(); - } - - std::string to_json_string() { - return to_json_string(*this); - } + static RecordWithWrappedType from_json(const rapidjson::Value & doc); + static RecordWithWrappedType from_json_string(const std::string &s); + static void to_json(const RecordWithWrappedType &t, rapidjson::Writer &writer); + static std::string to_json_string(const RecordWithWrappedType &t); + std::string to_json_string(); }; @@ -1135,26 +280,10 @@ namespace typedefs { typedef uint32_t Password; } namespace Password { - auto from_json(const rapidjson::Value &doc) { - return _atd_read_wrap([](const auto& v){return _atd_read_int(v);}, [](const auto &e){return static_cast(e);},doc); - } - static auto from_json_string(const std::string &s) { - rapidjson::Document doc; - doc.Parse(s.c_str()); - if (doc.HasParseError()) { - throw AtdException("Failed to parse JSON"); - } - return from_json(doc); - } - void to_json(const typedefs::Password &t, rapidjson::Writer &writer) { - _atd_write_wrap([](const auto &v, auto &w){_atd_write_int(v, w);}, [](const auto &e){return static_cast(e);}, t, writer); - } - std::string to_json_string(const typedefs::Password &t) { - rapidjson::StringBuffer buffer; - rapidjson::Writer writer(buffer); - to_json(t, writer); - return buffer.GetString(); - } + typedefs::Password from_json(const rapidjson::Value &doc); + static typedefs::Password from_json_string(const std::string &s); + void to_json(const typedefs::Password &t, rapidjson::Writer &writer); + std::string to_json_string(const typedefs::Password &t); } @@ -1162,34 +291,10 @@ namespace typedefs { typedef std::tuple Pair; } namespace Pair { - auto from_json(const rapidjson::Value &doc) { - return [](auto &v){ - if (!v.IsArray() || v.Size() != 2) - throw AtdException("Tuple of size 2"); - return std::make_tuple(_atd_read_string(v[0]), _atd_read_int(v[1])); - }(doc); - } - static auto from_json_string(const std::string &s) { - rapidjson::Document doc; - doc.Parse(s.c_str()); - if (doc.HasParseError()) { - throw AtdException("Failed to parse JSON"); - } - return from_json(doc); - } - void to_json(const typedefs::Pair &t, rapidjson::Writer &writer) { - [](const auto &t, auto &writer){ - writer.StartArray(); - _atd_write_string(std::get<0>(t), writer); _atd_write_int(std::get<1>(t), writer); - writer.EndArray(); - }(t, writer); - } - std::string to_json_string(const typedefs::Pair &t) { - rapidjson::StringBuffer buffer; - rapidjson::Writer writer(buffer); - to_json(t, writer); - return buffer.GetString(); - } + typedefs::Pair from_json(const rapidjson::Value &doc); + static typedefs::Pair from_json_string(const std::string &s); + void to_json(const typedefs::Pair &t, rapidjson::Writer &writer); + std::string to_json_string(const typedefs::Pair &t); } @@ -1197,74 +302,26 @@ namespace Frozen::Types { struct A; struct B; } - - +namespace typedefs { + typedef std::variant Frozen; +} namespace Frozen::Types { - - // Original type: frozen = [ ... | A | ... ] struct A { - static void to_json(const A &e, rapidjson::Writer &writer){ - writer.String("A"); - } + static void to_json(const A &e, rapidjson::Writer &writer); }; - - // Original type: frozen = [ ... | B of ... | ... ] struct B { int value; - static void to_json(const B &e, rapidjson::Writer &writer){ - writer.StartArray(); - writer.String("B"); - _atd_write_int(e.value, writer); - writer.EndArray(); - } + static void to_json(const B &e, rapidjson::Writer &writer); }; - - -} - - -namespace typedefs { - typedef std::variant<::Frozen::Types::A, ::Frozen::Types::B> Frozen; } namespace Frozen { - static ::typedefs::Frozen from_json(const rapidjson::Value &x) { - if (x.IsString()) { - if (std::string_view(x.GetString()) == "A") - return Types::A(); - throw _atd_bad_json("Frozen", x); - } - if (x.IsArray() && x.Size() == 2 && x[0].IsString()) { - std::string cons = x[0].GetString(); - if (cons == "B") - return Types::B({_atd_read_int(x[1])}); - throw _atd_bad_json("Frozen", x); - } - throw _atd_bad_json("Frozen", x); - } - static auto from_json_string(const std::string &s) { - rapidjson::Document doc; - doc.Parse(s.c_str()); - if (doc.HasParseError()) { - throw AtdException("Failed to parse JSON"); - } - return from_json(doc); - } - static void to_json(const ::typedefs::Frozen &x, rapidjson::Writer &writer) { - std::visit([&writer](auto &&arg) { - using T = std::decay_t; - if constexpr (std::is_same_v) Types::A::to_json(arg, writer); -if constexpr (std::is_same_v) Types::B::to_json(arg, writer); - }, x); - } -std::string to_json_string(const ::typedefs::Frozen &x) { - rapidjson::StringBuffer buffer; - rapidjson::Writer writer(buffer); - to_json(x, writer); - return buffer.GetString(); -} + static atd::typedefs::Frozen from_json(const rapidjson::Value &x); + static atd::typedefs::Frozen from_json_string(const std::string &s); + static void to_json(const atd::typedefs::Frozen &x, rapidjson::Writer &writer); + std::string to_json_string(const atd::typedefs::Frozen &x); } @@ -1275,43 +332,11 @@ namespace typedefs { struct DefaultList { std::vector items = {}; - static DefaultList from_json(const rapidjson::Value & doc) { - DefaultList record; - if (!doc.IsObject()) { - throw AtdException("Expected an object"); - } - if (doc.HasMember("items")) - record.items = _atd_read_array([](const auto &v){return _atd_read_int(v);}, doc["items"]); - else record.items = {}; - return record; - } - - static auto from_json_string(const std::string &s) { - rapidjson::Document doc; - doc.Parse(s.c_str()); - if (doc.HasParseError()) { - throw AtdException("Failed to parse JSON"); - } - return from_json(doc); - } - - static void to_json(const DefaultList &t, rapidjson::Writer &writer) { - writer.StartObject(); - writer.Key("items"); - _atd_write_array([](auto v, auto &w){_atd_write_int(v, w);}, t.items, writer); - writer.EndObject(); - } - - static std::string to_json_string(const DefaultList &t) { - rapidjson::StringBuffer buffer; - rapidjson::Writer writer(buffer); - to_json(t, writer); - return buffer.GetString(); - } - - std::string to_json_string() { - return to_json_string(*this); - } + static DefaultList from_json(const rapidjson::Value & doc); + static DefaultList from_json_string(const std::string &s); + static void to_json(const DefaultList &t, rapidjson::Writer &writer); + static std::string to_json_string(const DefaultList &t); + std::string to_json_string(); }; @@ -1323,48 +348,11 @@ struct Credential { std::string name; int password; - static Credential from_json(const rapidjson::Value & doc) { - Credential record; - if (!doc.IsObject()) { - throw AtdException("Expected an object"); - } - if (doc.HasMember("name")) - record.name = _atd_read_string(doc["name"]); - else record.name = _atd_missing_json_field("Credential", "name"); - if (doc.HasMember("password")) - record.password = _atd_read_int(doc["password"]); - else record.password = _atd_missing_json_field("Credential", "password"); - return record; - } - - static auto from_json_string(const std::string &s) { - rapidjson::Document doc; - doc.Parse(s.c_str()); - if (doc.HasParseError()) { - throw AtdException("Failed to parse JSON"); - } - return from_json(doc); - } - - static void to_json(const Credential &t, rapidjson::Writer &writer) { - writer.StartObject(); - writer.Key("name"); - _atd_write_string(t.name, writer); - writer.Key("password"); - _atd_write_int(t.password, writer); - writer.EndObject(); - } - - static std::string to_json_string(const Credential &t) { - rapidjson::StringBuffer buffer; - rapidjson::Writer writer(buffer); - to_json(t, writer); - return buffer.GetString(); - } - - std::string to_json_string() { - return to_json_string(*this); - } + static Credential from_json(const rapidjson::Value & doc); + static Credential from_json_string(const std::string &s); + static void to_json(const Credential &t, rapidjson::Writer &writer); + static std::string to_json_string(const Credential &t); + std::string to_json_string(); }; @@ -1372,26 +360,10 @@ namespace typedefs { typedef std::vector Credentials2; } namespace Credentials2 { - auto from_json(const rapidjson::Value &doc) { - return _atd_read_array([](const auto &v){return Credential::from_json(v);}, doc); - } - static auto from_json_string(const std::string &s) { - rapidjson::Document doc; - doc.Parse(s.c_str()); - if (doc.HasParseError()) { - throw AtdException("Failed to parse JSON"); - } - return from_json(doc); - } - void to_json(const typedefs::Credentials2 &t, rapidjson::Writer &writer) { - _atd_write_array([](auto v, auto &w){Credential::to_json(v, w);}, t, writer); - } - std::string to_json_string(const typedefs::Credentials2 &t) { - rapidjson::StringBuffer buffer; - rapidjson::Writer writer(buffer); - to_json(t, writer); - return buffer.GetString(); - } + typedefs::Credentials2 from_json(const rapidjson::Value &doc); + static typedefs::Credentials2 from_json_string(const std::string &s); + void to_json(const typedefs::Credentials2 &t, rapidjson::Writer &writer); + std::string to_json_string(const typedefs::Credentials2 &t); } @@ -1402,43 +374,11 @@ namespace typedefs { struct Credentials { std::vector credentials; - static Credentials from_json(const rapidjson::Value & doc) { - Credentials record; - if (!doc.IsObject()) { - throw AtdException("Expected an object"); - } - if (doc.HasMember("credentials")) - record.credentials = _atd_read_array([](const auto &v){return Credential::from_json(v);}, doc["credentials"]); - else record.credentials = _atd_missing_json_field("Credentials", "credentials"); - return record; - } - - static auto from_json_string(const std::string &s) { - rapidjson::Document doc; - doc.Parse(s.c_str()); - if (doc.HasParseError()) { - throw AtdException("Failed to parse JSON"); - } - return from_json(doc); - } - - static void to_json(const Credentials &t, rapidjson::Writer &writer) { - writer.StartObject(); - writer.Key("credentials"); - _atd_write_array([](auto v, auto &w){Credential::to_json(v, w);}, t.credentials, writer); - writer.EndObject(); - } - - static std::string to_json_string(const Credentials &t) { - rapidjson::StringBuffer buffer; - rapidjson::Writer writer(buffer); - to_json(t, writer); - return buffer.GetString(); - } - - std::string to_json_string() { - return to_json_string(*this); - } + static Credentials from_json(const rapidjson::Value & doc); + static Credentials from_json_string(const std::string &s); + static void to_json(const Credentials &t, rapidjson::Writer &writer); + static std::string to_json_string(const Credentials &t); + std::string to_json_string(); }; @@ -1446,26 +386,10 @@ namespace typedefs { typedef uint16_t AliasOfAlias; } namespace AliasOfAlias { - auto from_json(const rapidjson::Value &doc) { - return _atd_read_wrap([](const auto& v){return Alias3::from_json(v);}, [](const auto &e){return static_cast(e);},doc); - } - static auto from_json_string(const std::string &s) { - rapidjson::Document doc; - doc.Parse(s.c_str()); - if (doc.HasParseError()) { - throw AtdException("Failed to parse JSON"); - } - return from_json(doc); - } - void to_json(const typedefs::AliasOfAlias &t, rapidjson::Writer &writer) { - _atd_write_wrap([](const auto &v, auto &w){Alias3::to_json(v, w);}, [](const auto &e){return static_cast(e);}, t, writer); - } - std::string to_json_string(const typedefs::AliasOfAlias &t) { - rapidjson::StringBuffer buffer; - rapidjson::Writer writer(buffer); - to_json(t, writer); - return buffer.GetString(); - } + typedefs::AliasOfAlias from_json(const rapidjson::Value &doc); + static typedefs::AliasOfAlias from_json_string(const std::string &s); + void to_json(const typedefs::AliasOfAlias &t, rapidjson::Writer &writer); + std::string to_json_string(const typedefs::AliasOfAlias &t); } @@ -1473,24 +397,11 @@ namespace typedefs { typedef std::vector Alias2; } namespace Alias2 { - auto from_json(const rapidjson::Value &doc) { - return _atd_read_array([](const auto &v){return _atd_read_int(v);}, doc); - } - static auto from_json_string(const std::string &s) { - rapidjson::Document doc; - doc.Parse(s.c_str()); - if (doc.HasParseError()) { - throw AtdException("Failed to parse JSON"); - } - return from_json(doc); - } - void to_json(const typedefs::Alias2 &t, rapidjson::Writer &writer) { - _atd_write_array([](auto v, auto &w){_atd_write_int(v, w);}, t, writer); - } - std::string to_json_string(const typedefs::Alias2 &t) { - rapidjson::StringBuffer buffer; - rapidjson::Writer writer(buffer); - to_json(t, writer); - return buffer.GetString(); - } + typedefs::Alias2 from_json(const rapidjson::Value &doc); + static typedefs::Alias2 from_json_string(const std::string &s); + void to_json(const typedefs::Alias2 &t, rapidjson::Writer &writer); + std::string to_json_string(const typedefs::Alias2 &t); } + + +} // namespace atd diff --git a/atdcpp/test/cpp-tests/dune b/atdcpp/test/cpp-tests/dune index b3a1ea194..f901f6354 100644 --- a/atdcpp/test/cpp-tests/dune +++ b/atdcpp/test/cpp-tests/dune @@ -4,6 +4,7 @@ (rule (targets everything_atd.hpp + everything_atd.cpp ) (deps ../atd-input/everything.atd @@ -21,6 +22,6 @@ (glob_files *.cpp)) (action (progn - (bash "clang++ -I../../lib/rapidjson/include -std=c++20 %{deps} -o test") + (bash "g++ -I../../lib/rapidjson/include -std=c++20 %{deps} -o test") (bash ./test) ))) diff --git a/atdcpp/test/cpp-tests/test_atdd.cpp b/atdcpp/test/cpp-tests/test_atdd.cpp index 54f682fa5..f23c1efbc 100644 --- a/atdcpp/test/cpp-tests/test_atdd.cpp +++ b/atdcpp/test/cpp-tests/test_atdd.cpp @@ -7,6 +7,8 @@ #include "everything_atd.hpp" int main() { + using namespace atd; + std::map> tests; tests["simpleRecord"] = []() { diff --git a/atdcpp/test_main.cpp b/atdcpp/test_main.cpp index f09d8333f..a054d9037 100644 --- a/atdcpp/test_main.cpp +++ b/atdcpp/test_main.cpp @@ -22,6 +22,7 @@ const rapidjson::Document doc_from_json(const std::string &json) int main() { + using namespace atd; Root root; root.id = "id long"; From 2ef87a78ae269f3f89b8cb30b1c845fc7e8ac83b Mon Sep 17 00:00:00 2001 From: Alexandre Bourquelot Date: Mon, 4 Mar 2024 18:28:47 +0800 Subject: [PATCH 25/47] atdcpp: add cpp expected file --- atdcpp/test/cpp-expected/everything_atd.cpp | 1262 +++++++++++++++++++ 1 file changed, 1262 insertions(+) create mode 100644 atdcpp/test/cpp-expected/everything_atd.cpp diff --git a/atdcpp/test/cpp-expected/everything_atd.cpp b/atdcpp/test/cpp-expected/everything_atd.cpp new file mode 100644 index 000000000..458519e5c --- /dev/null +++ b/atdcpp/test/cpp-expected/everything_atd.cpp @@ -0,0 +1,1262 @@ + +// Generated by atdcpp from type definitions in everything.atd. +// This implements classes for the types defined in 'everything.atd', providing +// methods and functions to convert data from/to JSON. + +// ############################################################################ +// # Private functions +// ############################################################################ + +// filename everything.atd + +#include "everything_atd.hpp" + +namespace atd +{ + +class AtdException : public std::exception +{ +public: + AtdException(const std::string &message) : msg_(message) {} + + const char *what() const throw() override + { + return msg_.c_str(); + } + +private: + std::string msg_; +}; + +template +T _atd_missing_json_field(const std::string &type, const std::string &field) +{ + throw AtdException("Missing JSON field '" + field + "' in " + type); +} + +auto _atd_bad_json(const std::string &type, const rapidjson::Value &x) +{ + return AtdException("Bad JSON for " + type); +} + +// Reading an integer from JSON +int _atd_read_int(const rapidjson::Value &val) +{ + if (!val.IsInt()) + { + throw AtdException("Expected an integer"); + } + return val.GetInt(); +} + +bool _atd_read_bool(const rapidjson::Value &val) +{ + if (!val.IsBool()) + { + throw AtdException("Expected a boolean"); + } + return val.GetBool(); +} + +// Reading a float from JSON +float _atd_read_float(const rapidjson::Value &val) +{ + if (val.IsInt()) + { + return static_cast(val.GetInt()); + } + else if (val.IsUint()) + { + return static_cast(val.GetUint()); + } + if (!val.IsFloat()) + { + throw AtdException("Expected a float"); + } + + return val.GetFloat(); +} + +std::string _atd_read_string(const rapidjson::Value &val) +{ + if (!val.IsString()) + { + throw AtdException("Expected a string"); + } + return val.GetString(); +} + +template +auto _atd_read_array(F read_func, const rapidjson::Value &val) +{ + using ResultType = typename std::invoke_result::type; + + if (!val.IsArray()) + { + throw AtdException("Expected an array"); // Or your specific exception type + } + + std::vector result; + for (rapidjson::SizeType i = 0; i < val.Size(); i++) + { + result.push_back(read_func(val[i])); + } + + return result; +} + +template +auto _atd_read_object_to_tuple_list(F read_func, const rapidjson::Value &val) +{ + using ResultType = typename std::invoke_result::type; + + if (!val.IsObject()) + { + throw AtdException("Expected an object"); // Or your specific exception type + } + + std::vector> result; + for (auto &m : val.GetObject()) + { + result.push_back(std::make_tuple(m.name.GetString(), read_func(m.value))); + } + + return result; +} + +template +auto _atd_read_array_to_assoc_dict(RK read_key_func, RV read_value_func, const rapidjson::Value &val) +{ + using KeyType = typename std::invoke_result::type; + using ValueType = typename std::invoke_result::type; + + if (!val.IsArray()) + { + throw AtdException("Expected an array"); // Or your specific exception type + } + + std::map result; + for (rapidjson::SizeType i = 0; i < val.Size(); i++) + { + auto &pair = val[i]; + if (!pair.IsArray() || pair.Size() != 2) + { + throw AtdException("Expected an array of pairs"); + } + result[read_key_func(pair[0])] = read_value_func(pair[1]); + } + + return result; +} + +template +auto _atd_read_object_to_assoc_array(F read_func, const rapidjson::Value &val) +{ + using ResultType = typename std::invoke_result::type; + + if (!val.IsObject()) + { + throw AtdException("Expected an object"); // Or your specific exception type + } + + std::map result; + for (auto &m : val.GetObject()) + { + result[m.name.GetString()] = read_func(m.value); + } + + return result; +} + +template +auto _atd_read_nullable(F read_func, const rapidjson::Value &val) +{ + if (val.IsNull()) + { + return std::optional::type>(); + } + return std::optional::type>(read_func(val)); +} + +template +auto _atd_read_option(F read_func, const rapidjson::Value &val) +{ + using ResultType = typename std::invoke_result::type; + if (val.IsString() && std::string_view(val.GetString()) == "None") + { + return std::optional(); + } + else if (val.IsArray() && val.Size() == 2 && val[0].IsString() && std::string_view(val[0].GetString()) == "Some") + { + return std::make_optional(read_func(val[1])); + } + else + { + throw AtdException("Expected an option"); + } +} + +template +auto _atd_read_wrap(F read_func, W wrap_func, const rapidjson::Value &val) +{ + return wrap_func(read_func(val)); +} + +void _atd_write_int(int value, rapidjson::Writer& writer) +{ + writer.Int(value); +} + +void _atd_write_bool(bool value, rapidjson::Writer& writer) +{ + writer.Bool(value); +} + +void _atd_write_float(float value, rapidjson::Writer& writer) +{ + writer.Double(value); +} + +void _atd_write_string(const std::string &value, rapidjson::Writer& writer) +{ + writer.String(value.c_str()); +} + +template +void _atd_write_array(F write_func, const V& values, rapidjson::Writer& writer) +{ + writer.StartArray(); + for (const auto& value : values) + { + write_func(value, writer); + } + writer.EndArray(); +} + +template +void _atd_write_tuple_list_to_object(F write_func, const V &values, rapidjson::Writer& writer) +{ + writer.StartObject(); + for (const auto& value : values) + { + writer.Key(std::get<0>(value).c_str()); + write_func(std::get<1>(value), writer); + } + writer.EndObject(); +} + +template +void _atd_write_assoc_dict_to_array(const Wk write_key_func, const Wv write_value_func, const Map &value_map, rapidjson::Writer& writer) +{ + writer.StartArray(); + for (const auto& pair : value_map) + { + writer.StartArray(); + write_key_func(pair.first, writer); + write_value_func(pair.second, writer); + writer.EndArray(); + } + writer.EndArray(); +} + +template +void _atd_write_assoc_array_to_object(F write_func, const Map &value_map, rapidjson::Writer& writer) +{ + writer.StartObject(); + for (const auto& pair : value_map) + { + writer.Key(pair.first.c_str()); + write_func(pair.second, writer); + } + writer.EndObject(); +} + + +template +void _atd_write_option(F write_func, const O &val, rapidjson::Writer& writer) +{ + if (val) + { + writer.StartArray(); + writer.String("Some"); + write_func(*val, writer); + writer.EndArray(); + } + else + { + writer.String("None"); + } +} + +template +void _atd_write_nullable(F write_func, const O &val, rapidjson::Writer& writer) +{ + if (val) + { + write_func(*val, writer); + } + else + { + writer.Null(); + } +} + +template +void _atd_write_wrap(F write_func, W wrap_func, const T &val, rapidjson::Writer& writer) +{ + write_func(wrap_func(val), writer); +} + + + + +#include "everything_atd.hpp" + + +namespace RecursiveVariant::Types { + + + void A::to_json(const A &e, rapidjson::Writer &writer){ + writer.String("A"); + }; + + + void B::to_json(const B &e, rapidjson::Writer &writer){ + writer.StartArray(); + writer.String("B"); + _atd_write_array([](auto v, auto &w){RecursiveVariant::to_json(v, w);}, e.value, writer); + writer.EndArray(); + } + + +} + + +namespace RecursiveVariant { + typedefs::RecursiveVariant from_json(const rapidjson::Value &x) { + if (x.IsString()) { + if (std::string_view(x.GetString()) == "A") + return Types::A(); + throw _atd_bad_json("RecursiveVariant", x); + } + if (x.IsArray() && x.Size() == 2 && x[0].IsString()) { + std::string cons = x[0].GetString(); + if (cons == "B") + return Types::B({_atd_read_array([](const auto &v){return RecursiveVariant::from_json(v);}, x[1])}); + throw _atd_bad_json("RecursiveVariant", x); + } + throw _atd_bad_json("RecursiveVariant", x); + } + typedefs::RecursiveVariant from_json_string(const std::string &s) { + rapidjson::Document doc; + doc.Parse(s.c_str()); + if (doc.HasParseError()) { + throw AtdException("Failed to parse JSON"); + } + return from_json(doc); + } + void to_json(const atd::typedefs::RecursiveVariant &x, rapidjson::Writer &writer) { + std::visit([&writer](auto &&arg) { + using T = std::decay_t; + if constexpr (std::is_same_v) Types::A::to_json(arg, writer); + if constexpr (std::is_same_v) Types::B::to_json(arg, writer); + }, x); + } + std::string to_json_string(const atd::typedefs::RecursiveVariant &x) { + rapidjson::StringBuffer buffer; + rapidjson::Writer writer(buffer); + to_json(x, writer); + return buffer.GetString(); + } +} + + +RecursiveClass RecursiveClass::from_json(const rapidjson::Value & doc) { + RecursiveClass record; + if (!doc.IsObject()) { + throw AtdException("Expected an object"); + } + if (doc.HasMember("id")) + record.id = _atd_read_int(doc["id"]); + else record.id = _atd_missing_json_field("RecursiveClass", "id"); + if (doc.HasMember("flag")) + record.flag = _atd_read_bool(doc["flag"]); + else record.flag = _atd_missing_json_field("RecursiveClass", "flag"); + if (doc.HasMember("children")) + record.children = _atd_read_array([](const auto &v){return RecursiveClass::from_json(v);}, doc["children"]); + else record.children = _atd_missing_json_field("RecursiveClass", "children"); + return record; +} +RecursiveClass RecursiveClass::from_json_string(const std::string &s) { + rapidjson::Document doc; + doc.Parse(s.c_str()); + if (doc.HasParseError()) { + throw AtdException("Failed to parse JSON"); + } + return from_json(doc); +} +void RecursiveClass::to_json(const RecursiveClass &t, rapidjson::Writer &writer) { + writer.StartObject(); + writer.Key("id"); + _atd_write_int(t.id, writer); + writer.Key("flag"); + _atd_write_bool(t.flag, writer); + writer.Key("children"); + _atd_write_array([](auto v, auto &w){RecursiveClass::to_json(v, w);}, t.children, writer); + writer.EndObject(); +} +std::string RecursiveClass::to_json_string(const RecursiveClass &t) { + rapidjson::StringBuffer buffer; + rapidjson::Writer writer(buffer); + to_json(t, writer); + return buffer.GetString(); +} +std::string RecursiveClass::to_json_string() { + return to_json_string(*this); +} + + +ThreeLevelNestedListRecord ThreeLevelNestedListRecord::from_json(const rapidjson::Value & doc) { + ThreeLevelNestedListRecord record; + if (!doc.IsObject()) { + throw AtdException("Expected an object"); + } + if (doc.HasMember("field_a")) + record.field_a = _atd_read_array([](const auto &v){return _atd_read_array([](const auto &v){return _atd_read_array([](const auto &v){return _atd_read_int(v);}, v);}, v);}, doc["field_a"]); + else record.field_a = _atd_missing_json_field("ThreeLevelNestedListRecord", "field_a"); + return record; +} +ThreeLevelNestedListRecord ThreeLevelNestedListRecord::from_json_string(const std::string &s) { + rapidjson::Document doc; + doc.Parse(s.c_str()); + if (doc.HasParseError()) { + throw AtdException("Failed to parse JSON"); + } + return from_json(doc); +} +void ThreeLevelNestedListRecord::to_json(const ThreeLevelNestedListRecord &t, rapidjson::Writer &writer) { + writer.StartObject(); + writer.Key("field_a"); + _atd_write_array([](auto v, auto &w){_atd_write_array([](auto v, auto &w){_atd_write_array([](auto v, auto &w){_atd_write_int(v, w);}, v, w);}, v, w);}, t.field_a, writer); + writer.EndObject(); +} +std::string ThreeLevelNestedListRecord::to_json_string(const ThreeLevelNestedListRecord &t) { + rapidjson::StringBuffer buffer; + rapidjson::Writer writer(buffer); + to_json(t, writer); + return buffer.GetString(); +} +std::string ThreeLevelNestedListRecord::to_json_string() { + return to_json_string(*this); +} + + +namespace St { + typedefs::St from_json(const rapidjson::Value &doc) { + return _atd_read_int(doc); + } + typedefs::St from_json_string(const std::string &s) { + rapidjson::Document doc; + doc.Parse(s.c_str()); + if (doc.HasParseError()) { + throw AtdException("Failed to parse JSON"); + } + return from_json(doc); + } + void to_json(const typedefs::St &t, rapidjson::Writer &writer) { + _atd_write_int(t, writer); + } + std::string to_json_string(const typedefs::St &t) { + rapidjson::StringBuffer buffer; + rapidjson::Writer writer(buffer); + to_json(t, writer); + return buffer.GetString(); + } +} + + +namespace Kind::Types { + + + void Root::to_json(const Root &e, rapidjson::Writer &writer){ + writer.String("Root"); + }; + + + void Thing::to_json(const Thing &e, rapidjson::Writer &writer){ + writer.StartArray(); + writer.String("Thing"); + _atd_write_int(e.value, writer); + writer.EndArray(); + } + + + void WOW::to_json(const WOW &e, rapidjson::Writer &writer){ + writer.String("wow"); + }; + + + void Amaze::to_json(const Amaze &e, rapidjson::Writer &writer){ + writer.StartArray(); + writer.String("!!!"); + _atd_write_array([](auto v, auto &w){_atd_write_string(v, w);}, e.value, writer); + writer.EndArray(); + } + + +} + + +namespace Kind { + typedefs::Kind from_json(const rapidjson::Value &x) { + if (x.IsString()) { + if (std::string_view(x.GetString()) == "Root") + return Types::Root(); + if (std::string_view(x.GetString()) == "wow") + return Types::WOW(); + throw _atd_bad_json("Kind", x); + } + if (x.IsArray() && x.Size() == 2 && x[0].IsString()) { + std::string cons = x[0].GetString(); + if (cons == "Thing") + return Types::Thing({_atd_read_int(x[1])}); + if (cons == "!!!") + return Types::Amaze({_atd_read_array([](const auto &v){return _atd_read_string(v);}, x[1])}); + throw _atd_bad_json("Kind", x); + } + throw _atd_bad_json("Kind", x); + } + typedefs::Kind from_json_string(const std::string &s) { + rapidjson::Document doc; + doc.Parse(s.c_str()); + if (doc.HasParseError()) { + throw AtdException("Failed to parse JSON"); + } + return from_json(doc); + } + void to_json(const atd::typedefs::Kind &x, rapidjson::Writer &writer) { + std::visit([&writer](auto &&arg) { + using T = std::decay_t; + if constexpr (std::is_same_v) Types::Root::to_json(arg, writer); + if constexpr (std::is_same_v) Types::Thing::to_json(arg, writer); + if constexpr (std::is_same_v) Types::WOW::to_json(arg, writer); + if constexpr (std::is_same_v) Types::Amaze::to_json(arg, writer); + }, x); + } + std::string to_json_string(const atd::typedefs::Kind &x) { + rapidjson::StringBuffer buffer; + rapidjson::Writer writer(buffer); + to_json(x, writer); + return buffer.GetString(); + } +} + + +namespace Alias3 { + typedefs::Alias3 from_json(const rapidjson::Value &doc) { + return _atd_read_wrap([](const auto& v){return _atd_read_int(v);}, [](const auto &e){return static_cast(e);},doc); + } + typedefs::Alias3 from_json_string(const std::string &s) { + rapidjson::Document doc; + doc.Parse(s.c_str()); + if (doc.HasParseError()) { + throw AtdException("Failed to parse JSON"); + } + return from_json(doc); + } + void to_json(const typedefs::Alias3 &t, rapidjson::Writer &writer) { + _atd_write_wrap([](const auto &v, auto &w){_atd_write_int(v, w);}, [](const auto &e){return static_cast(e);}, t, writer); + } + std::string to_json_string(const typedefs::Alias3 &t) { + rapidjson::StringBuffer buffer; + rapidjson::Writer writer(buffer); + to_json(t, writer); + return buffer.GetString(); + } +} + + +namespace AliasOfAliasNotWrapped { + typedefs::AliasOfAliasNotWrapped from_json(const rapidjson::Value &doc) { + return Alias3::from_json(doc); + } + typedefs::AliasOfAliasNotWrapped from_json_string(const std::string &s) { + rapidjson::Document doc; + doc.Parse(s.c_str()); + if (doc.HasParseError()) { + throw AtdException("Failed to parse JSON"); + } + return from_json(doc); + } + void to_json(const typedefs::AliasOfAliasNotWrapped &t, rapidjson::Writer &writer) { + Alias3::to_json(t, writer); + } + std::string to_json_string(const typedefs::AliasOfAliasNotWrapped &t) { + rapidjson::StringBuffer buffer; + rapidjson::Writer writer(buffer); + to_json(t, writer); + return buffer.GetString(); + } +} + + +namespace AliasOfAliasOfAlias { + typedefs::AliasOfAliasOfAlias from_json(const rapidjson::Value &doc) { + return AliasOfAliasNotWrapped::from_json(doc); + } + typedefs::AliasOfAliasOfAlias from_json_string(const std::string &s) { + rapidjson::Document doc; + doc.Parse(s.c_str()); + if (doc.HasParseError()) { + throw AtdException("Failed to parse JSON"); + } + return from_json(doc); + } + void to_json(const typedefs::AliasOfAliasOfAlias &t, rapidjson::Writer &writer) { + AliasOfAliasNotWrapped::to_json(t, writer); + } + std::string to_json_string(const typedefs::AliasOfAliasOfAlias &t) { + rapidjson::StringBuffer buffer; + rapidjson::Writer writer(buffer); + to_json(t, writer); + return buffer.GetString(); + } +} + + +namespace Alias { + typedefs::Alias from_json(const rapidjson::Value &doc) { + return _atd_read_array([](const auto &v){return _atd_read_int(v);}, doc); + } + typedefs::Alias from_json_string(const std::string &s) { + rapidjson::Document doc; + doc.Parse(s.c_str()); + if (doc.HasParseError()) { + throw AtdException("Failed to parse JSON"); + } + return from_json(doc); + } + void to_json(const typedefs::Alias &t, rapidjson::Writer &writer) { + _atd_write_array([](auto v, auto &w){_atd_write_int(v, w);}, t, writer); + } + std::string to_json_string(const typedefs::Alias &t) { + rapidjson::StringBuffer buffer; + rapidjson::Writer writer(buffer); + to_json(t, writer); + return buffer.GetString(); + } +} + + +namespace KindParametrizedTuple { + typedefs::KindParametrizedTuple from_json(const rapidjson::Value &doc) { + return [](auto &v){ + if (!v.IsArray() || v.Size() != 3) + throw AtdException("Tuple of size 3"); + return std::make_tuple(Kind::from_json(v[0]), Kind::from_json(v[1]), _atd_read_int(v[2])); + }(doc); + } + typedefs::KindParametrizedTuple from_json_string(const std::string &s) { + rapidjson::Document doc; + doc.Parse(s.c_str()); + if (doc.HasParseError()) { + throw AtdException("Failed to parse JSON"); + } + return from_json(doc); + } + void to_json(const typedefs::KindParametrizedTuple &t, rapidjson::Writer &writer) { + [](const auto &t, auto &writer){ + writer.StartArray(); + Kind::to_json(std::get<0>(t), writer); Kind::to_json(std::get<1>(t), writer); _atd_write_int(std::get<2>(t), writer); + writer.EndArray(); + }(t, writer); + } + std::string to_json_string(const typedefs::KindParametrizedTuple &t) { + rapidjson::StringBuffer buffer; + rapidjson::Writer writer(buffer); + to_json(t, writer); + return buffer.GetString(); + } +} + + +IntFloatParametrizedRecord IntFloatParametrizedRecord::from_json(const rapidjson::Value & doc) { + IntFloatParametrizedRecord record; + if (!doc.IsObject()) { + throw AtdException("Expected an object"); + } + if (doc.HasMember("field_a")) + record.field_a = _atd_read_int(doc["field_a"]); + else record.field_a = _atd_missing_json_field("IntFloatParametrizedRecord", "field_a"); + if (doc.HasMember("field_b")) + record.field_b = _atd_read_array([](const auto &v){return _atd_read_float(v);}, doc["field_b"]); + else record.field_b = {}; + return record; +} +IntFloatParametrizedRecord IntFloatParametrizedRecord::from_json_string(const std::string &s) { + rapidjson::Document doc; + doc.Parse(s.c_str()); + if (doc.HasParseError()) { + throw AtdException("Failed to parse JSON"); + } + return from_json(doc); +} +void IntFloatParametrizedRecord::to_json(const IntFloatParametrizedRecord &t, rapidjson::Writer &writer) { + writer.StartObject(); + writer.Key("field_a"); + _atd_write_int(t.field_a, writer); + writer.Key("field_b"); + _atd_write_array([](auto v, auto &w){_atd_write_float(v, w);}, t.field_b, writer); + writer.EndObject(); +} +std::string IntFloatParametrizedRecord::to_json_string(const IntFloatParametrizedRecord &t) { + rapidjson::StringBuffer buffer; + rapidjson::Writer writer(buffer); + to_json(t, writer); + return buffer.GetString(); +} +std::string IntFloatParametrizedRecord::to_json_string() { + return to_json_string(*this); +} + + +Root Root::from_json(const rapidjson::Value & doc) { + Root record; + if (!doc.IsObject()) { + throw AtdException("Expected an object"); + } + if (doc.HasMember("ID")) + record.id = _atd_read_string(doc["ID"]); + else record.id = _atd_missing_json_field("Root", "ID"); + if (doc.HasMember("await")) + record.await = _atd_read_bool(doc["await"]); + else record.await = _atd_missing_json_field("Root", "await"); + if (doc.HasMember("integer")) + record.integer = _atd_read_int(doc["integer"]); + else record.integer = _atd_missing_json_field("Root", "integer"); + if (doc.HasMember("__init__")) + record.x___init__ = _atd_read_float(doc["__init__"]); + else record.x___init__ = _atd_missing_json_field("Root", "__init__"); + if (doc.HasMember("float_with_auto_default")) + record.float_with_auto_default = _atd_read_float(doc["float_with_auto_default"]); + else record.float_with_auto_default = 0.0; + if (doc.HasMember("float_with_default")) + record.float_with_default = _atd_read_float(doc["float_with_default"]); + else record.float_with_default = 0.1; + if (doc.HasMember("items")) + record.items = _atd_read_array([](const auto &v){return _atd_read_array([](const auto &v){return _atd_read_int(v);}, v);}, doc["items"]); + else record.items = _atd_missing_json_field("Root", "items"); + if (doc.HasMember("maybe")) + record.maybe = _atd_read_option([](const auto &v){return _atd_read_int(v);}, doc["maybe"]); + else record.maybe = std::nullopt; + if (doc.HasMember("extras")) + record.extras = _atd_read_array([](const auto &v){return _atd_read_int(v);}, doc["extras"]); + else record.extras = {}; + if (doc.HasMember("answer")) + record.answer = _atd_read_int(doc["answer"]); + else record.answer = 42; + if (doc.HasMember("aliased")) + record.aliased = Alias::from_json(doc["aliased"]); + else record.aliased = _atd_missing_json_field("Root", "aliased"); + if (doc.HasMember("point")) + record.point = [](auto &v){ + if (!v.IsArray() || v.Size() != 2) + throw AtdException("Tuple of size 2"); + return std::make_tuple(_atd_read_float(v[0]), _atd_read_float(v[1])); + }(doc["point"]); + else record.point = _atd_missing_json_field("Root", "point"); + if (doc.HasMember("kind")) + record.kind = Kind::from_json(doc["kind"]); + else record.kind = _atd_missing_json_field("Root", "kind"); + if (doc.HasMember("kinds")) + record.kinds = _atd_read_array([](const auto &v){return Kind::from_json(v);}, doc["kinds"]); + else record.kinds = _atd_missing_json_field("Root", "kinds"); + if (doc.HasMember("assoc1")) + record.assoc1 = _atd_read_array([](const auto &v){return [](auto &v){ + if (!v.IsArray() || v.Size() != 2) + throw AtdException("Tuple of size 2"); + return std::make_tuple(_atd_read_float(v[0]), _atd_read_int(v[1])); + }(v);}, doc["assoc1"]); + else record.assoc1 = _atd_missing_json_field("Root", "assoc1"); + if (doc.HasMember("assoc2")) + record.assoc2 = _atd_read_object_to_tuple_list([](const auto &v){return _atd_read_int(v);},doc["assoc2"]); + else record.assoc2 = _atd_missing_json_field("Root", "assoc2"); + if (doc.HasMember("assoc3")) + record.assoc3 = _atd_read_array_to_assoc_dict([](const auto &k){return _atd_read_float(k);}, [](const auto &v){return _atd_read_int(v);}, doc["assoc3"]); + else record.assoc3 = _atd_missing_json_field("Root", "assoc3"); + if (doc.HasMember("assoc4")) + record.assoc4 = _atd_read_object_to_assoc_array([](const auto &v){return _atd_read_int(v);},doc["assoc4"]); + else record.assoc4 = _atd_missing_json_field("Root", "assoc4"); + if (doc.HasMember("nullables")) + record.nullables = _atd_read_array([](const auto &v){return _atd_read_nullable([](const auto &v){return _atd_read_int(v);}, v);}, doc["nullables"]); + else record.nullables = _atd_missing_json_field("Root", "nullables"); + if (doc.HasMember("options")) + record.options = _atd_read_array([](const auto &v){return _atd_read_option([](const auto &v){return _atd_read_int(v);}, v);}, doc["options"]); + else record.options = _atd_missing_json_field("Root", "options"); + if (doc.HasMember("parametrized_record")) + record.parametrized_record = IntFloatParametrizedRecord::from_json(doc["parametrized_record"]); + else record.parametrized_record = _atd_missing_json_field("Root", "parametrized_record"); + if (doc.HasMember("parametrized_tuple")) + record.parametrized_tuple = KindParametrizedTuple::from_json(doc["parametrized_tuple"]); + else record.parametrized_tuple = _atd_missing_json_field("Root", "parametrized_tuple"); + if (doc.HasMember("wrapped")) + record.wrapped = _atd_read_wrap([](const auto& v){return St::from_json(v);}, [](const auto &e){return [](typedefs::St st){return st - 1;}(e);},doc["wrapped"]); + else record.wrapped = _atd_missing_json_field("Root", "wrapped"); + if (doc.HasMember("aaa")) + record.aaa = AliasOfAliasOfAlias::from_json(doc["aaa"]); + else record.aaa = _atd_missing_json_field("Root", "aaa"); + if (doc.HasMember("item")) + record.item = _atd_read_wrap([](const auto& v){return _atd_read_string(v);}, [](const auto &e){return std::stoi(e);},doc["item"]); + else record.item = _atd_missing_json_field("Root", "item"); + return record; +} +Root Root::from_json_string(const std::string &s) { + rapidjson::Document doc; + doc.Parse(s.c_str()); + if (doc.HasParseError()) { + throw AtdException("Failed to parse JSON"); + } + return from_json(doc); +} +void Root::to_json(const Root &t, rapidjson::Writer &writer) { + writer.StartObject(); + writer.Key("ID"); + _atd_write_string(t.id, writer); + writer.Key("await"); + _atd_write_bool(t.await, writer); + writer.Key("integer"); + _atd_write_int(t.integer, writer); + writer.Key("__init__"); + _atd_write_float(t.x___init__, writer); + writer.Key("float_with_auto_default"); + _atd_write_float(t.float_with_auto_default, writer); + writer.Key("float_with_default"); + _atd_write_float(t.float_with_default, writer); + writer.Key("items"); + _atd_write_array([](auto v, auto &w){_atd_write_array([](auto v, auto &w){_atd_write_int(v, w);}, v, w);}, t.items, writer); + if (t.maybe != std::nullopt) { + writer.Key("maybe"); + _atd_write_option([](const auto &v, auto &w){_atd_write_int(v, w);}, t.maybe, writer); + } + writer.Key("extras"); + _atd_write_array([](auto v, auto &w){_atd_write_int(v, w);}, t.extras, writer); + writer.Key("answer"); + _atd_write_int(t.answer, writer); + writer.Key("aliased"); + Alias::to_json(t.aliased, writer); + writer.Key("point"); + [](const auto &t, auto &writer){ + writer.StartArray(); + _atd_write_float(std::get<0>(t), writer); _atd_write_float(std::get<1>(t), writer); + writer.EndArray(); + }(t.point, writer); + writer.Key("kind"); + Kind::to_json(t.kind, writer); + writer.Key("kinds"); + _atd_write_array([](auto v, auto &w){Kind::to_json(v, w);}, t.kinds, writer); + writer.Key("assoc1"); + _atd_write_array([](auto v, auto &w){[](const auto &t, auto &writer){ + writer.StartArray(); + _atd_write_float(std::get<0>(t), writer); _atd_write_int(std::get<1>(t), writer); + writer.EndArray(); + }(v, w);}, t.assoc1, writer); + writer.Key("assoc2"); + _atd_write_tuple_list_to_object([](auto v, auto &w){_atd_write_int(v, w);}, t.assoc2, writer); + writer.Key("assoc3"); + _atd_write_assoc_dict_to_array([](auto v, auto &w){_atd_write_float(v, w);}, [](auto v, auto &w){_atd_write_int(v, w);}, t.assoc3, writer); + writer.Key("assoc4"); + _atd_write_assoc_array_to_object([](auto v, auto &w){_atd_write_int(v, w);}, t.assoc4, writer); + writer.Key("nullables"); + _atd_write_array([](auto v, auto &w){_atd_write_nullable([](auto v, auto &w){_atd_write_int(v, w);}, v, w);}, t.nullables, writer); + writer.Key("options"); + _atd_write_array([](auto v, auto &w){_atd_write_option([](auto v, auto &w){_atd_write_int(v, w);}, v, w);}, t.options, writer); + writer.Key("parametrized_record"); + IntFloatParametrizedRecord::to_json(t.parametrized_record, writer); + writer.Key("parametrized_tuple"); + KindParametrizedTuple::to_json(t.parametrized_tuple, writer); + writer.Key("wrapped"); + _atd_write_wrap([](const auto &v, auto &w){St::to_json(v, w);}, [](const auto &e){return [](uint16_t e){return e + 1;}(e);}, t.wrapped, writer); + writer.Key("aaa"); + AliasOfAliasOfAlias::to_json(t.aaa, writer); + writer.Key("item"); + _atd_write_wrap([](const auto &v, auto &w){_atd_write_string(v, w);}, [](const auto &e){return std::to_string(e);}, t.item, writer); + writer.EndObject(); +} +std::string Root::to_json_string(const Root &t) { + rapidjson::StringBuffer buffer; + rapidjson::Writer writer(buffer); + to_json(t, writer); + return buffer.GetString(); +} +std::string Root::to_json_string() { + return to_json_string(*this); +} + + +RequireField RequireField::from_json(const rapidjson::Value & doc) { + RequireField record; + if (!doc.IsObject()) { + throw AtdException("Expected an object"); + } + if (doc.HasMember("req")) + record.req = _atd_read_string(doc["req"]); + else record.req = _atd_missing_json_field("RequireField", "req"); + return record; +} +RequireField RequireField::from_json_string(const std::string &s) { + rapidjson::Document doc; + doc.Parse(s.c_str()); + if (doc.HasParseError()) { + throw AtdException("Failed to parse JSON"); + } + return from_json(doc); +} +void RequireField::to_json(const RequireField &t, rapidjson::Writer &writer) { + writer.StartObject(); + writer.Key("req"); + _atd_write_string(t.req, writer); + writer.EndObject(); +} +std::string RequireField::to_json_string(const RequireField &t) { + rapidjson::StringBuffer buffer; + rapidjson::Writer writer(buffer); + to_json(t, writer); + return buffer.GetString(); +} +std::string RequireField::to_json_string() { + return to_json_string(*this); +} + + +RecordWithWrappedType RecordWithWrappedType::from_json(const rapidjson::Value & doc) { + RecordWithWrappedType record; + if (!doc.IsObject()) { + throw AtdException("Expected an object"); + } + if (doc.HasMember("item")) + record.item = _atd_read_wrap([](const auto& v){return _atd_read_string(v);}, [](const auto &e){return std::stoi(e);},doc["item"]); + else record.item = _atd_missing_json_field("RecordWithWrappedType", "item"); + return record; +} +RecordWithWrappedType RecordWithWrappedType::from_json_string(const std::string &s) { + rapidjson::Document doc; + doc.Parse(s.c_str()); + if (doc.HasParseError()) { + throw AtdException("Failed to parse JSON"); + } + return from_json(doc); +} +void RecordWithWrappedType::to_json(const RecordWithWrappedType &t, rapidjson::Writer &writer) { + writer.StartObject(); + writer.Key("item"); + _atd_write_wrap([](const auto &v, auto &w){_atd_write_string(v, w);}, [](const auto &e){return std::to_string(e);}, t.item, writer); + writer.EndObject(); +} +std::string RecordWithWrappedType::to_json_string(const RecordWithWrappedType &t) { + rapidjson::StringBuffer buffer; + rapidjson::Writer writer(buffer); + to_json(t, writer); + return buffer.GetString(); +} +std::string RecordWithWrappedType::to_json_string() { + return to_json_string(*this); +} + + +namespace Password { + typedefs::Password from_json(const rapidjson::Value &doc) { + return _atd_read_wrap([](const auto& v){return _atd_read_int(v);}, [](const auto &e){return static_cast(e);},doc); + } + typedefs::Password from_json_string(const std::string &s) { + rapidjson::Document doc; + doc.Parse(s.c_str()); + if (doc.HasParseError()) { + throw AtdException("Failed to parse JSON"); + } + return from_json(doc); + } + void to_json(const typedefs::Password &t, rapidjson::Writer &writer) { + _atd_write_wrap([](const auto &v, auto &w){_atd_write_int(v, w);}, [](const auto &e){return static_cast(e);}, t, writer); + } + std::string to_json_string(const typedefs::Password &t) { + rapidjson::StringBuffer buffer; + rapidjson::Writer writer(buffer); + to_json(t, writer); + return buffer.GetString(); + } +} + + +namespace Pair { + typedefs::Pair from_json(const rapidjson::Value &doc) { + return [](auto &v){ + if (!v.IsArray() || v.Size() != 2) + throw AtdException("Tuple of size 2"); + return std::make_tuple(_atd_read_string(v[0]), _atd_read_int(v[1])); + }(doc); + } + typedefs::Pair from_json_string(const std::string &s) { + rapidjson::Document doc; + doc.Parse(s.c_str()); + if (doc.HasParseError()) { + throw AtdException("Failed to parse JSON"); + } + return from_json(doc); + } + void to_json(const typedefs::Pair &t, rapidjson::Writer &writer) { + [](const auto &t, auto &writer){ + writer.StartArray(); + _atd_write_string(std::get<0>(t), writer); _atd_write_int(std::get<1>(t), writer); + writer.EndArray(); + }(t, writer); + } + std::string to_json_string(const typedefs::Pair &t) { + rapidjson::StringBuffer buffer; + rapidjson::Writer writer(buffer); + to_json(t, writer); + return buffer.GetString(); + } +} + + +namespace Frozen::Types { + + + void A::to_json(const A &e, rapidjson::Writer &writer){ + writer.String("A"); + }; + + + void B::to_json(const B &e, rapidjson::Writer &writer){ + writer.StartArray(); + writer.String("B"); + _atd_write_int(e.value, writer); + writer.EndArray(); + } + + +} + + +namespace Frozen { + typedefs::Frozen from_json(const rapidjson::Value &x) { + if (x.IsString()) { + if (std::string_view(x.GetString()) == "A") + return Types::A(); + throw _atd_bad_json("Frozen", x); + } + if (x.IsArray() && x.Size() == 2 && x[0].IsString()) { + std::string cons = x[0].GetString(); + if (cons == "B") + return Types::B({_atd_read_int(x[1])}); + throw _atd_bad_json("Frozen", x); + } + throw _atd_bad_json("Frozen", x); + } + typedefs::Frozen from_json_string(const std::string &s) { + rapidjson::Document doc; + doc.Parse(s.c_str()); + if (doc.HasParseError()) { + throw AtdException("Failed to parse JSON"); + } + return from_json(doc); + } + void to_json(const atd::typedefs::Frozen &x, rapidjson::Writer &writer) { + std::visit([&writer](auto &&arg) { + using T = std::decay_t; + if constexpr (std::is_same_v) Types::A::to_json(arg, writer); + if constexpr (std::is_same_v) Types::B::to_json(arg, writer); + }, x); + } + std::string to_json_string(const atd::typedefs::Frozen &x) { + rapidjson::StringBuffer buffer; + rapidjson::Writer writer(buffer); + to_json(x, writer); + return buffer.GetString(); + } +} + + +DefaultList DefaultList::from_json(const rapidjson::Value & doc) { + DefaultList record; + if (!doc.IsObject()) { + throw AtdException("Expected an object"); + } + if (doc.HasMember("items")) + record.items = _atd_read_array([](const auto &v){return _atd_read_int(v);}, doc["items"]); + else record.items = {}; + return record; +} +DefaultList DefaultList::from_json_string(const std::string &s) { + rapidjson::Document doc; + doc.Parse(s.c_str()); + if (doc.HasParseError()) { + throw AtdException("Failed to parse JSON"); + } + return from_json(doc); +} +void DefaultList::to_json(const DefaultList &t, rapidjson::Writer &writer) { + writer.StartObject(); + writer.Key("items"); + _atd_write_array([](auto v, auto &w){_atd_write_int(v, w);}, t.items, writer); + writer.EndObject(); +} +std::string DefaultList::to_json_string(const DefaultList &t) { + rapidjson::StringBuffer buffer; + rapidjson::Writer writer(buffer); + to_json(t, writer); + return buffer.GetString(); +} +std::string DefaultList::to_json_string() { + return to_json_string(*this); +} + + +Credential Credential::from_json(const rapidjson::Value & doc) { + Credential record; + if (!doc.IsObject()) { + throw AtdException("Expected an object"); + } + if (doc.HasMember("name")) + record.name = _atd_read_string(doc["name"]); + else record.name = _atd_missing_json_field("Credential", "name"); + if (doc.HasMember("password")) + record.password = _atd_read_int(doc["password"]); + else record.password = _atd_missing_json_field("Credential", "password"); + return record; +} +Credential Credential::from_json_string(const std::string &s) { + rapidjson::Document doc; + doc.Parse(s.c_str()); + if (doc.HasParseError()) { + throw AtdException("Failed to parse JSON"); + } + return from_json(doc); +} +void Credential::to_json(const Credential &t, rapidjson::Writer &writer) { + writer.StartObject(); + writer.Key("name"); + _atd_write_string(t.name, writer); + writer.Key("password"); + _atd_write_int(t.password, writer); + writer.EndObject(); +} +std::string Credential::to_json_string(const Credential &t) { + rapidjson::StringBuffer buffer; + rapidjson::Writer writer(buffer); + to_json(t, writer); + return buffer.GetString(); +} +std::string Credential::to_json_string() { + return to_json_string(*this); +} + + +namespace Credentials2 { + typedefs::Credentials2 from_json(const rapidjson::Value &doc) { + return _atd_read_array([](const auto &v){return Credential::from_json(v);}, doc); + } + typedefs::Credentials2 from_json_string(const std::string &s) { + rapidjson::Document doc; + doc.Parse(s.c_str()); + if (doc.HasParseError()) { + throw AtdException("Failed to parse JSON"); + } + return from_json(doc); + } + void to_json(const typedefs::Credentials2 &t, rapidjson::Writer &writer) { + _atd_write_array([](auto v, auto &w){Credential::to_json(v, w);}, t, writer); + } + std::string to_json_string(const typedefs::Credentials2 &t) { + rapidjson::StringBuffer buffer; + rapidjson::Writer writer(buffer); + to_json(t, writer); + return buffer.GetString(); + } +} + + +Credentials Credentials::from_json(const rapidjson::Value & doc) { + Credentials record; + if (!doc.IsObject()) { + throw AtdException("Expected an object"); + } + if (doc.HasMember("credentials")) + record.credentials = _atd_read_array([](const auto &v){return Credential::from_json(v);}, doc["credentials"]); + else record.credentials = _atd_missing_json_field("Credentials", "credentials"); + return record; +} +Credentials Credentials::from_json_string(const std::string &s) { + rapidjson::Document doc; + doc.Parse(s.c_str()); + if (doc.HasParseError()) { + throw AtdException("Failed to parse JSON"); + } + return from_json(doc); +} +void Credentials::to_json(const Credentials &t, rapidjson::Writer &writer) { + writer.StartObject(); + writer.Key("credentials"); + _atd_write_array([](auto v, auto &w){Credential::to_json(v, w);}, t.credentials, writer); + writer.EndObject(); +} +std::string Credentials::to_json_string(const Credentials &t) { + rapidjson::StringBuffer buffer; + rapidjson::Writer writer(buffer); + to_json(t, writer); + return buffer.GetString(); +} +std::string Credentials::to_json_string() { + return to_json_string(*this); +} + + +namespace AliasOfAlias { + typedefs::AliasOfAlias from_json(const rapidjson::Value &doc) { + return _atd_read_wrap([](const auto& v){return Alias3::from_json(v);}, [](const auto &e){return static_cast(e);},doc); + } + typedefs::AliasOfAlias from_json_string(const std::string &s) { + rapidjson::Document doc; + doc.Parse(s.c_str()); + if (doc.HasParseError()) { + throw AtdException("Failed to parse JSON"); + } + return from_json(doc); + } + void to_json(const typedefs::AliasOfAlias &t, rapidjson::Writer &writer) { + _atd_write_wrap([](const auto &v, auto &w){Alias3::to_json(v, w);}, [](const auto &e){return static_cast(e);}, t, writer); + } + std::string to_json_string(const typedefs::AliasOfAlias &t) { + rapidjson::StringBuffer buffer; + rapidjson::Writer writer(buffer); + to_json(t, writer); + return buffer.GetString(); + } +} + + +namespace Alias2 { + typedefs::Alias2 from_json(const rapidjson::Value &doc) { + return _atd_read_array([](const auto &v){return _atd_read_int(v);}, doc); + } + typedefs::Alias2 from_json_string(const std::string &s) { + rapidjson::Document doc; + doc.Parse(s.c_str()); + if (doc.HasParseError()) { + throw AtdException("Failed to parse JSON"); + } + return from_json(doc); + } + void to_json(const typedefs::Alias2 &t, rapidjson::Writer &writer) { + _atd_write_array([](auto v, auto &w){_atd_write_int(v, w);}, t, writer); + } + std::string to_json_string(const typedefs::Alias2 &t) { + rapidjson::StringBuffer buffer; + rapidjson::Writer writer(buffer); + to_json(t, writer); + return buffer.GetString(); + } +} + + +} // namespace atd From dcee9814b2b1dc1077ba7eebc5fca3618739e481 Mon Sep 17 00:00:00 2001 From: Alexandre Bourquelot Date: Mon, 4 Mar 2024 18:35:14 +0800 Subject: [PATCH 26/47] atdcpp: small edit readme --- atdcpp/README.md | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/atdcpp/README.md b/atdcpp/README.md index 4e17fd375..f917558a2 100644 --- a/atdcpp/README.md +++ b/atdcpp/README.md @@ -12,6 +12,8 @@ See the sample input type definitions [everything.atd](test/atd-input/everything.atd) and the C++ output [everything.hpp](test/cpp-expected/everything.hpp). +This implementation makes use of the RapidJson C++ library. + Requirements -- @@ -19,12 +21,14 @@ Requirements for building and testing `atdcpp`: * Opam and dependencies installed from the [`atd` project root](..) with `make setup`. * gcc / clang +* librapidjson Requirements for generating C++ code: * the `atdcpp` executable Requirements for compiling the generated C++ code: * A working C++ compiler (gcc / clang) +* The rapidjson library Documentation -- @@ -34,8 +38,7 @@ Documentation Development notes -- -Build or rebuild with `make`. Test with `make test`. This requires -gtest. +Build or rebuild with `make`. Test with `make test`. Running the tests is done from the `atdcpp/` main folder with `make test`. From c4ae9f5fe4d800dd09c80422b4d5dcd52b843fd5 Mon Sep 17 00:00:00 2001 From: Alexandre Bourquelot Date: Wed, 6 Mar 2024 02:18:07 +0800 Subject: [PATCH 27/47] atdcpp: properly handle recursion via wrapping with pointer --- atdcpp/src/lib/Codegen.ml | 23 +++- atdcpp/src/lib/Cpp_annot.ml | 9 +- atdcpp/src/lib/Cpp_annot.mli | 1 + atdcpp/test/atd-input/everything.atd | 22 ++-- atdcpp/test/cpp-expected/everything_atd.cpp | 126 +++++++++++++++++--- atdcpp/test/cpp-expected/everything_atd.hpp | 54 +++++++-- atdcpp/test/cpp-tests/test_atdd.cpp | 44 ++++++- atdcpp/test/dune | 3 +- 8 files changed, 242 insertions(+), 40 deletions(-) diff --git a/atdcpp/src/lib/Codegen.ml b/atdcpp/src/lib/Codegen.ml index c257c7ca7..5a4d1ccb8 100644 --- a/atdcpp/src/lib/Codegen.ml +++ b/atdcpp/src/lib/Codegen.ml @@ -30,6 +30,7 @@ let annot_schema_cpp : Atd.Annot.schema_section = Type_expr, "repr"; Type_expr, "unwrap"; Type_expr, "wrap"; + Type_expr, "templatize"; Field, "default"; Module_head, "include"; ] @@ -408,6 +409,22 @@ auto _atd_read_wrap(F read_func, W wrap_func, const rapidjson::Value &val) return wrap_func(read_func(val)); } +template +std::shared_ptr _atd_wrap_shared_ptr(T val) +{ + return std::make_shared(val); +} + +template +T _atd_unwrap_shared_ptr(const std::shared_ptr val) +{ + if (!val) + { + throw AtdException("Expected a shared_ptr but got nullptr"); + } + return *val; +} + void _atd_write_int(int value, rapidjson::Writer& writer) { writer.Int(value); @@ -586,8 +603,7 @@ let cpp_type_name_namespaced env (name : string) = | "abstract" -> "rapidjson::Value" | user_defined -> let typename = (struct_name env user_defined) in - sprintf "%s::%s" "typedefs" typename - + sprintf "%s::%s" "typedefs" typename let rec type_name_of_expr env (e : type_expr) : string = match e with @@ -619,7 +635,8 @@ let rec type_name_of_expr env (e : type_expr) : string = | Wrap (loc, e, an) -> (match Cpp_annot.get_cpp_wrap loc an with | None -> error_at loc "wrap type declared, but no cpp annotation found" - | Some { cpp_wrap_t ; _ } -> cpp_wrap_t + | Some { cpp_wrap_t ; cpp_templatize=false; _ } -> cpp_wrap_t + | Some { cpp_wrap_t ; cpp_templatize=true; _ } -> sprintf "%s<%s>" cpp_wrap_t (type_name_of_expr env e) ) | Name (loc, (loc2, name, []), an) -> cpp_type_name_namespaced env name | Name (loc, (_, name, _::_), _) -> assert false diff --git a/atdcpp/src/lib/Cpp_annot.ml b/atdcpp/src/lib/Cpp_annot.ml index 4095583ff..0b63c2fe6 100644 --- a/atdcpp/src/lib/Cpp_annot.ml +++ b/atdcpp/src/lib/Cpp_annot.ml @@ -12,6 +12,7 @@ type atd_cpp_wrap = { cpp_wrap_t : string; cpp_wrap : string; cpp_unwrap : string; + cpp_templatize : bool; } let get_cpp_default an : string option = @@ -83,9 +84,15 @@ let get_cpp_wrap loc an = ~field:"unwrap" an in + let templatize = + Atd.Annot.get_flag + ~sections:path + ~field:"templatize" + an + in match t, wrap, unwrap with None, None, None -> None | Some t, Some wrap, Some unwrap -> - Some { cpp_wrap_t = t; cpp_wrap = wrap; cpp_unwrap = unwrap } + Some { cpp_wrap_t = t; cpp_wrap = wrap; cpp_unwrap = unwrap; cpp_templatize = templatize} | _ -> Atd.Ast.error_at loc "Incomplete annotation. Missing t, wrap or unwrap" diff --git a/atdcpp/src/lib/Cpp_annot.mli b/atdcpp/src/lib/Cpp_annot.mli index ebafcdc76..dc3b238c5 100644 --- a/atdcpp/src/lib/Cpp_annot.mli +++ b/atdcpp/src/lib/Cpp_annot.mli @@ -36,6 +36,7 @@ type atd_cpp_wrap = { cpp_wrap_t : string; cpp_wrap : string; cpp_unwrap : string; + cpp_templatize : bool; } val get_cpp_wrap : Atd.Ast.loc -> diff --git a/atdcpp/test/atd-input/everything.atd b/atdcpp/test/atd-input/everything.atd index 81bc87699..1eb2c8503 100644 --- a/atdcpp/test/atd-input/everything.atd +++ b/atdcpp/test/atd-input/everything.atd @@ -98,14 +98,18 @@ type record_with_wrapped_type = { type 'a recursive = 'a list -(* type recursive_record2 = { - id: int; - flag: bool; - children: recursive_record2; -} *) +(* This only works if the recursive member is always present. If it can optionally not be present, we need extra wrapping in an optional as below *) +type recursive_variant = [ + | Integer of int + | Rec of recursive_variant wrap +] +type struct_with_recursive_variant = { + variant: recursive_variant; +} -type recursive_variant = [ - | A - | B of recursive_variant list -] \ No newline at end of file +type recursive_record2 = { + id: int; + flag: bool; + children: recursive_record2 nullable wrap ; +} \ No newline at end of file diff --git a/atdcpp/test/cpp-expected/everything_atd.cpp b/atdcpp/test/cpp-expected/everything_atd.cpp index 458519e5c..c29dcb0d1 100644 --- a/atdcpp/test/cpp-expected/everything_atd.cpp +++ b/atdcpp/test/cpp-expected/everything_atd.cpp @@ -202,6 +202,22 @@ auto _atd_read_wrap(F read_func, W wrap_func, const rapidjson::Value &val) return wrap_func(read_func(val)); } +template +std::shared_ptr _atd_wrap_shared_ptr(T val) +{ + return std::make_shared(val); +} + +template +T _atd_unwrap_shared_ptr(const std::shared_ptr val) +{ + if (!val) + { + throw AtdException("Expected a shared_ptr but got nullptr"); + } + return *val; +} + void _atd_write_int(int value, rapidjson::Writer& writer) { writer.Int(value); @@ -316,15 +332,18 @@ void _atd_write_wrap(F write_func, W wrap_func, const T &val, rapidjson::Writer< namespace RecursiveVariant::Types { - void A::to_json(const A &e, rapidjson::Writer &writer){ - writer.String("A"); - }; + void Integer::to_json(const Integer &e, rapidjson::Writer &writer){ + writer.StartArray(); + writer.String("Integer"); + _atd_write_int(e.value, writer); + writer.EndArray(); + } - void B::to_json(const B &e, rapidjson::Writer &writer){ + void Rec::to_json(const Rec &e, rapidjson::Writer &writer){ writer.StartArray(); - writer.String("B"); - _atd_write_array([](auto v, auto &w){RecursiveVariant::to_json(v, w);}, e.value, writer); + writer.String("Rec"); + _atd_write_wrap([](const auto &v, auto &w){RecursiveVariant::to_json(v, w);}, [](const auto &e){return _atd_unwrap_shared_ptr(e);}, e.value, writer); writer.EndArray(); } @@ -334,15 +353,12 @@ namespace RecursiveVariant::Types { namespace RecursiveVariant { typedefs::RecursiveVariant from_json(const rapidjson::Value &x) { - if (x.IsString()) { - if (std::string_view(x.GetString()) == "A") - return Types::A(); - throw _atd_bad_json("RecursiveVariant", x); - } if (x.IsArray() && x.Size() == 2 && x[0].IsString()) { std::string cons = x[0].GetString(); - if (cons == "B") - return Types::B({_atd_read_array([](const auto &v){return RecursiveVariant::from_json(v);}, x[1])}); + if (cons == "Integer") + return Types::Integer({_atd_read_int(x[1])}); + if (cons == "Rec") + return Types::Rec({_atd_read_wrap([](const auto& v){return RecursiveVariant::from_json(v);}, [](const auto &e){return _atd_wrap_shared_ptr(e);},x[1])}); throw _atd_bad_json("RecursiveVariant", x); } throw _atd_bad_json("RecursiveVariant", x); @@ -358,8 +374,8 @@ namespace RecursiveVariant { void to_json(const atd::typedefs::RecursiveVariant &x, rapidjson::Writer &writer) { std::visit([&writer](auto &&arg) { using T = std::decay_t; - if constexpr (std::is_same_v) Types::A::to_json(arg, writer); - if constexpr (std::is_same_v) Types::B::to_json(arg, writer); + if constexpr (std::is_same_v) Types::Integer::to_json(arg, writer); + if constexpr (std::is_same_v) Types::Rec::to_json(arg, writer); }, x); } std::string to_json_string(const atd::typedefs::RecursiveVariant &x) { @@ -371,6 +387,51 @@ namespace RecursiveVariant { } +RecursiveRecord2 RecursiveRecord2::from_json(const rapidjson::Value & doc) { + RecursiveRecord2 record; + if (!doc.IsObject()) { + throw AtdException("Expected an object"); + } + if (doc.HasMember("id")) + record.id = _atd_read_int(doc["id"]); + else record.id = _atd_missing_json_field("RecursiveRecord2", "id"); + if (doc.HasMember("flag")) + record.flag = _atd_read_bool(doc["flag"]); + else record.flag = _atd_missing_json_field("RecursiveRecord2", "flag"); + if (doc.HasMember("children")) + record.children = _atd_read_wrap([](const auto& v){return _atd_read_nullable([](const auto &v){return RecursiveRecord2::from_json(v);}, v);}, [](const auto &e){return _atd_wrap_shared_ptr(e);},doc["children"]); + else record.children = _atd_missing_json_field("RecursiveRecord2", "children"); + return record; +} +RecursiveRecord2 RecursiveRecord2::from_json_string(const std::string &s) { + rapidjson::Document doc; + doc.Parse(s.c_str()); + if (doc.HasParseError()) { + throw AtdException("Failed to parse JSON"); + } + return from_json(doc); +} +void RecursiveRecord2::to_json(const RecursiveRecord2 &t, rapidjson::Writer &writer) { + writer.StartObject(); + writer.Key("id"); + _atd_write_int(t.id, writer); + writer.Key("flag"); + _atd_write_bool(t.flag, writer); + writer.Key("children"); + _atd_write_wrap([](const auto &v, auto &w){_atd_write_nullable([](auto v, auto &w){RecursiveRecord2::to_json(v, w);}, v, w);}, [](const auto &e){return _atd_unwrap_shared_ptr(e);}, t.children, writer); + writer.EndObject(); +} +std::string RecursiveRecord2::to_json_string(const RecursiveRecord2 &t) { + rapidjson::StringBuffer buffer; + rapidjson::Writer writer(buffer); + to_json(t, writer); + return buffer.GetString(); +} +std::string RecursiveRecord2::to_json_string() { + return to_json_string(*this); +} + + RecursiveClass RecursiveClass::from_json(const rapidjson::Value & doc) { RecursiveClass record; if (!doc.IsObject()) { @@ -451,6 +512,41 @@ std::string ThreeLevelNestedListRecord::to_json_string() { } +StructWithRecursiveVariant StructWithRecursiveVariant::from_json(const rapidjson::Value & doc) { + StructWithRecursiveVariant record; + if (!doc.IsObject()) { + throw AtdException("Expected an object"); + } + if (doc.HasMember("variant")) + record.variant = RecursiveVariant::from_json(doc["variant"]); + else record.variant = _atd_missing_json_field("StructWithRecursiveVariant", "variant"); + return record; +} +StructWithRecursiveVariant StructWithRecursiveVariant::from_json_string(const std::string &s) { + rapidjson::Document doc; + doc.Parse(s.c_str()); + if (doc.HasParseError()) { + throw AtdException("Failed to parse JSON"); + } + return from_json(doc); +} +void StructWithRecursiveVariant::to_json(const StructWithRecursiveVariant &t, rapidjson::Writer &writer) { + writer.StartObject(); + writer.Key("variant"); + RecursiveVariant::to_json(t.variant, writer); + writer.EndObject(); +} +std::string StructWithRecursiveVariant::to_json_string(const StructWithRecursiveVariant &t) { + rapidjson::StringBuffer buffer; + rapidjson::Writer writer(buffer); + to_json(t, writer); + return buffer.GetString(); +} +std::string StructWithRecursiveVariant::to_json_string() { + return to_json_string(*this); +} + + namespace St { typedefs::St from_json(const rapidjson::Value &doc) { return _atd_read_int(doc); diff --git a/atdcpp/test/cpp-expected/everything_atd.hpp b/atdcpp/test/cpp-expected/everything_atd.hpp index 88f4d50c9..f8b8881bf 100644 --- a/atdcpp/test/cpp-expected/everything_atd.hpp +++ b/atdcpp/test/cpp-expected/everything_atd.hpp @@ -28,22 +28,24 @@ namespace atd { namespace RecursiveVariant::Types { - struct A; - struct B; + struct Integer; + struct Rec; } namespace typedefs { - typedef std::variant RecursiveVariant; + typedef std::variant RecursiveVariant; } namespace RecursiveVariant::Types { - // Original type: recursive_variant = [ ... | A | ... ] - struct A { - static void to_json(const A &e, rapidjson::Writer &writer); + // Original type: recursive_variant = [ ... | Integer of ... | ... ] + struct Integer + { + int value; + static void to_json(const Integer &e, rapidjson::Writer &writer); }; - // Original type: recursive_variant = [ ... | B of ... | ... ] - struct B + // Original type: recursive_variant = [ ... | Rec of ... | ... ] + struct Rec { - std::vector value; - static void to_json(const B &e, rapidjson::Writer &writer); + std::shared_ptr value; + static void to_json(const Rec &e, rapidjson::Writer &writer); }; } namespace RecursiveVariant { @@ -54,6 +56,23 @@ namespace RecursiveVariant { } +struct RecursiveRecord2; +namespace typedefs { + typedef RecursiveRecord2 RecursiveRecord2; +} +struct RecursiveRecord2 { + int id; + bool flag; + std::shared_ptr> children; + + static RecursiveRecord2 from_json(const rapidjson::Value & doc); + static RecursiveRecord2 from_json_string(const std::string &s); + static void to_json(const RecursiveRecord2 &t, rapidjson::Writer &writer); + static std::string to_json_string(const RecursiveRecord2 &t); + std::string to_json_string(); +}; + + struct RecursiveClass; namespace typedefs { typedef RecursiveClass RecursiveClass; @@ -86,6 +105,21 @@ struct ThreeLevelNestedListRecord { }; +struct StructWithRecursiveVariant; +namespace typedefs { + typedef StructWithRecursiveVariant StructWithRecursiveVariant; +} +struct StructWithRecursiveVariant { + typedefs::RecursiveVariant variant; + + static StructWithRecursiveVariant from_json(const rapidjson::Value & doc); + static StructWithRecursiveVariant from_json_string(const std::string &s); + static void to_json(const StructWithRecursiveVariant &t, rapidjson::Writer &writer); + static std::string to_json_string(const StructWithRecursiveVariant &t); + std::string to_json_string(); +}; + + namespace typedefs { typedef int St; } diff --git a/atdcpp/test/cpp-tests/test_atdd.cpp b/atdcpp/test/cpp-tests/test_atdd.cpp index f23c1efbc..8b89643df 100644 --- a/atdcpp/test/cpp-tests/test_atdd.cpp +++ b/atdcpp/test/cpp-tests/test_atdd.cpp @@ -60,7 +60,49 @@ int main() { if (json == rootFromJson.to_json_string()) { std::cout << "Test passed: rootObjectSerialization" << std::endl; } else { - std::cout << "Test failed: rootObjectSerialization" << std::endl; + throw std::runtime_error("check is failed"); + } + }; + + tests["recursiveVariant"] = []() { + typedefs::RecursiveVariant recursiveVariant = RecursiveVariant::Types::Integer{42}; + + typedefs::RecursiveVariant recursiveVariant2 = RecursiveVariant::Types::Rec{std::make_shared(recursiveVariant)}; + typedefs::StructWithRecursiveVariant structWithRecursiveVariant = {recursiveVariant2}; + + std::string json = structWithRecursiveVariant.to_json_string(); + typedefs::StructWithRecursiveVariant structWithRecursiveVariantFromJson = StructWithRecursiveVariant::from_json_string(json); + + if (json == R"({"variant":["Rec",["Integer",42]]})" && json == structWithRecursiveVariantFromJson.to_json_string()) { + std::cout << "Test passed: recursiveVariant" << std::endl; + } else { + throw std::runtime_error("check is failed"); + } + }; + + tests["recursive record"] = []() { + using T = std::optional; + + auto optional = std::make_optional( + {2, + false, + std::make_shared(std::nullopt)} + ); + + typedefs::RecursiveRecord2 record{}; + record.id = 1; + record.flag = true; + record.children = std::make_shared(optional); + + std::string json = record.to_json_string(); + + auto target_json = R"({"id":1,"flag":true,"children":{"id":2,"flag":false,"children":null}})"; + RecursiveRecord2 recordFromJson = RecursiveRecord2::from_json_string(target_json); + + if (json == target_json && json == recordFromJson.to_json_string()) { + std::cout << "Test passed: recursive record" << std::endl; + } else { + throw std::runtime_error("check is failed"); } }; diff --git a/atdcpp/test/dune b/atdcpp/test/dune index c71d379fc..9d7df7d79 100644 --- a/atdcpp/test/dune +++ b/atdcpp/test/dune @@ -9,7 +9,8 @@ (package atdcpp) (action (diff cpp-expected/everything_atd.hpp - cpp-tests/everything_atd.hpp))) + cpp-tests/everything_atd.hpp) + )) ; 2. Run the generated Dlang code and check that is reads or writes JSON ; data as expected. From f7417a80446c784adfecb9e55ddbcfef0cf66589 Mon Sep 17 00:00:00 2001 From: Alexandre Bourquelot Date: Wed, 6 Mar 2024 12:22:59 +0800 Subject: [PATCH 28/47] atdcpp: forward declaration and typedefs at top of file --- atdcpp/src/lib/Codegen.ml | 260 +++++++++++++----- atdcpp/test/cpp-expected/everything_atd.cpp | 12 +- atdcpp/test/cpp-expected/everything_atd.hpp | 275 ++++++++------------ atdcpp/test/cpp-tests/test_atdd.cpp | 5 +- 4 files changed, 322 insertions(+), 230 deletions(-) diff --git a/atdcpp/src/lib/Codegen.ml b/atdcpp/src/lib/Codegen.ml index 5a4d1ccb8..e581905ae 100644 --- a/atdcpp/src/lib/Codegen.ml +++ b/atdcpp/src/lib/Codegen.ml @@ -1010,12 +1010,6 @@ let record env loc name (fields : field list) an = ] in [ - Line (sprintf "struct %s;" cpp_struct_name); - Line (sprintf "namespace typedefs {"); - Block [ - Line (sprintf "typedef %s %s;" cpp_struct_name cpp_struct_name) - ]; - Line "}"; Line (sprintf "struct %s {" cpp_struct_name); Block ([ Inline inst_var_declarations; @@ -1031,13 +1025,7 @@ let record env loc name (fields : field list) an = let alias_wrapper env name type_expr = let cpp_struct_name = struct_name env name in - let value_type = type_name_of_expr env type_expr in [ - Line (sprintf "namespace typedefs {"); - Block [ - Line (sprintf "typedef %s %s;" value_type cpp_struct_name) - ]; - Line "}"; Line (sprintf "namespace %s {" cpp_struct_name); Block [ Line (sprintf "typedefs::%s from_json(const rapidjson::Value &doc);" cpp_struct_name); @@ -1214,7 +1202,7 @@ let sum_container_definition env loc name cases = ]; Block [Inline (from_json_string_definition (sprintf "typedefs::%s" cpp_struct_name) (None))]; Block [ - Line (sprintf "void to_json(const atd::typedefs::%s &x, rapidjson::Writer &writer) {" (cpp_struct_name)); + Line (sprintf "void to_json(const typedefs::%s &x, rapidjson::Writer &writer) {" (cpp_struct_name)); Block [ Line "std::visit([&writer](auto &&arg) {"; Block [ @@ -1230,7 +1218,7 @@ let sum_container_definition env loc name cases = Line ("}"); ]; - Block [Line (sprintf "std::string to_json_string(const atd::typedefs::%s &x) {" (cpp_struct_name)); + Block [Line (sprintf "std::string to_json_string(const typedefs::%s &x) {" (cpp_struct_name)); Block [ Line ("rapidjson::StringBuffer buffer;"); Line ("rapidjson::Writer writer(buffer);"); @@ -1245,15 +1233,12 @@ let sum_container_definition env loc name cases = let sum_container env loc name cases = let cpp_struct_name = struct_name env name in [ - Line (sprintf "namespace %s {" (cpp_struct_name)); - Block [ - Line (sprintf "static atd::typedefs::%s from_json(const rapidjson::Value &x);" (cpp_struct_name)); - Inline (from_json_string_declaration (sprintf "atd::typedefs::%s" cpp_struct_name)); - Line (sprintf "static void to_json(const atd::typedefs::%s &x, rapidjson::Writer &writer);" (cpp_struct_name)); - Line (sprintf "std::string to_json_string(const atd::typedefs::%s &x);" (cpp_struct_name)); - ]; - Line ("}"); + Line (sprintf "static typedefs::%s from_json(const rapidjson::Value &x);" (cpp_struct_name)); + Inline (from_json_string_declaration (sprintf "typedefs::%s" cpp_struct_name)); + Line (sprintf "static void to_json(const typedefs::%s &x, rapidjson::Writer &writer);" (cpp_struct_name)); + Line (sprintf "std::string to_json_string(const typedefs::%s &x);" (cpp_struct_name)); ] + let sum_definition env loc name cases = let cases = @@ -1288,43 +1273,27 @@ let sum env loc name cases = | Inherit _ -> assert false ) cases in - let cpp_struct_name = struct_name env name in - let type_list = - List.map (fun (loc, orig_name, unique_name, an, opt_e) -> - (sprintf "atd::%s::Types::%s" (cpp_struct_name) (trans env orig_name)) - ) cases - |> String.concat ", " - in - let cases_forward_declarations = - List.map (fun (loc, orig_name, unique_name, an, opt_e) -> Line (sprintf "struct %s;" orig_name)) cases - in - let cases_forward_declarations_in_namespace = - [ - Line (sprintf "namespace %s::Types {" (struct_name env name)); - Block cases_forward_declarations; - Line (sprintf "}"); - ] - in let case_classes = List.map (fun x -> Inline (case_class env name x)) cases in - let typedef_declaration = - [ - Line (sprintf "namespace typedefs {"); - Block [ Line(sprintf "typedef %s %s;" (sprintf "std::variant<%s>" type_list) (cpp_struct_name))]; - Line "}"; - ] - in let container_class = sum_container env loc name cases in [ - Inline cases_forward_declarations_in_namespace; - Inline typedef_declaration; - Line (sprintf "namespace %s::Types {" (struct_name env name)); - Block case_classes; + Line (sprintf "namespace %s {" (struct_name env name)); + Block [ + Line (sprintf "namespace Types {"); + Block case_classes; + Line (sprintf "}"); + Line (""); + Inline container_class; + ]; Line (sprintf "}"); - Inline container_class; ] +type codegen_type = + | Type_decl | Type_def + | Forward_decl | Struct_typedef | Alias_typedef | Variant_typedef + + let type_def env ((loc, (name, param, an), e) : A.type_def) : B.t = if param <> [] then not_implemented loc "parametrized type"; @@ -1365,16 +1334,153 @@ let type_decl env ((loc, (name, param, an), e) : A.type_def) : B.t = in unwrap e -let module_body env x is_header = - let type_decl_or_def = if is_header then type_decl else type_def in - List.fold_left (fun acc (Type x) -> Inline (type_decl_or_def env x) :: acc) [] x + +let sum_forward_decl env loc name cases = + let cases = + List.map (fun (x : variant) -> + match x with + | Variant (loc, (orig_name, an), opt_e) -> + let unique_name = create_struct_name env orig_name in + (loc, orig_name, unique_name, an, opt_e) + | Inherit _ -> assert false + ) cases + in + let cases_forward_declarations = + List.map (fun (loc, orig_name, unique_name, an, opt_e) -> Line (sprintf "struct %s;" orig_name)) cases + in + let cases_forward_declarations_in_namespace = + [ + Line (sprintf "namespace %s::Types {" (struct_name env name)); + Block cases_forward_declarations; + Line (sprintf "}"); + ] + in + cases_forward_declarations_in_namespace + +let record_forward_decl env loc name (fields : field list) an = + let cpp_struct_name = struct_name env name in + Line (sprintf "struct %s;" cpp_struct_name) + + +let record_typedef env loc name (fields : field list) an = + let cpp_struct_name = struct_name env name in + Line (sprintf "typedef %s %s;" cpp_struct_name cpp_struct_name) + +let forward_decl env ((loc, (name, param, an), e) : A.type_def) : B.t = + if param <> [] then + not_implemented loc "parametrized type"; + let unwrap e = + match e with + | Sum (loc, cases, an) -> + sum_forward_decl env loc name cases + | Record (loc, fields, an) -> + [record_forward_decl env loc name fields an] + | Tuple _ | List _ | Option _ | Nullable _ | Wrap _ | Name _ -> [] + | Shared _ -> not_implemented loc "cyclic references" + | Tvar _ -> not_implemented loc "parametrized type" + in + unwrap e + +let sum_typedef env loc name cases = + let cases = + List.map (fun (x : variant) -> + match x with + | Variant (loc, (orig_name, an), opt_e) -> + let unique_name = create_struct_name env orig_name in + (loc, orig_name, unique_name, an, opt_e) + | Inherit _ -> assert false + ) cases + in + let cpp_struct_name = struct_name env name in + let type_list = + List.map (fun (loc, orig_name, unique_name, an, opt_e) -> + (sprintf "atd::%s::Types::%s" (cpp_struct_name) (trans env orig_name)) + ) cases + |> String.concat ", " + in + let typedef_declaration = + Line(sprintf "typedef %s %s;" (sprintf "std::variant<%s>" type_list) (cpp_struct_name)); + in + typedef_declaration + +let alias_typedef env name type_expr = + let cpp_struct_name = struct_name env name in + let value_type = type_name_of_expr env type_expr in + Line (sprintf "typedef %s %s;" value_type cpp_struct_name) + + +let typedefs env ((loc, (name, param, an), e) : A.type_def) : B.t = + if param <> [] then + not_implemented loc "parametrized type"; + let unwrap e = + match e with + | Sum (loc, cases, an) -> + [] + | Record (loc, fields, an) -> + [record_typedef env loc name fields an] + | Tuple _ + | List _ + | Option _ + | Nullable _ + | Wrap _ + | Name _ -> [] + | Shared _ -> not_implemented loc "cyclic references" + | Tvar _ -> not_implemented loc "parametrized type" + in + unwrap e + +let typedefs2 env ((loc, (name, param, an), e) : A.type_def) : B.t = + if param <> [] then + not_implemented loc "parametrized type"; + let unwrap e = + match e with + | Sum (loc, cases, an) -> + [] + | Record (loc, fields, an) -> + [] + | Tuple _ + | List _ + | Option _ + | Nullable _ + | Wrap _ + | Name _ -> [alias_typedef env name e] + | Shared _ -> not_implemented loc "cyclic references" + | Tvar _ -> not_implemented loc "parametrized type" + in + unwrap e + + let typedefs3 env ((loc, (name, param, an), e) : A.type_def) : B.t = + if param <> [] then + not_implemented loc "parametrized type"; + let unwrap e = + match e with + | Sum (loc, cases, an) -> + [sum_typedef env loc name cases] + | _ -> [] + in + unwrap e + +let module_body env x codegen_type = + let fn = match codegen_type with + | Type_decl -> type_decl + | Type_def -> type_def + | Forward_decl -> forward_decl + | Struct_typedef -> typedefs + | Variant_typedef -> typedefs3 + | Alias_typedef -> typedefs2 + in + let gen = + List.fold_left (fun acc (Type x) -> Inline (fn env x) :: acc) [] x |> List.rev - |> spaced + in + match codegen_type with + | Type_decl | Type_def -> gen |> spaced + | _ -> gen -let definition_group ~atd_filename env is_header +let definition_group ~atd_filename env codegen_type (is_recursive, (items: A.module_body)) : B.t = [ - Inline (module_body env items is_header); + Inline (module_body env items codegen_type); ] (* @@ -1395,14 +1501,46 @@ let to_header_file ~atd_filename ~head (items : A.module_body) dst_path = let env = init_env () in reserve_good_struct_names env items; let head = List.map (fun s -> Line s) head in + let cpp_forward_declarations = + Atd.Util.tsort items + |> List.map (fun x -> Inline (definition_group ~atd_filename env Forward_decl x)) + in + let cpp_forward_declarations = [Line "// forward declarations"] @ cpp_forward_declarations @ [Line ""; Line ""] in + let cpp_typedefs = + Atd.Util.tsort items + |> List.map (fun x -> Inline (definition_group ~atd_filename env Struct_typedef x)) + in + let cpp_v_typedefs = + Atd.Util.tsort items + |> List.map (fun x -> Inline (definition_group ~atd_filename env Variant_typedef x)) + in + let cpp_true_typedefs = + Atd.Util.tsort items + |> List.map (fun x -> Inline (definition_group ~atd_filename env Alias_typedef x)) + in + let cpp_typedefs = + [Line "namespace typedefs {"] + @ + [Block + [Inline cpp_typedefs; + Line ""; + Inline cpp_v_typedefs; + Line ""; + Inline cpp_true_typedefs;] + ] @ [Line "} // namespace typedefs"; Line ""] in let cpp_declarations = Atd.Util.tsort items - |> List.map (fun x -> Inline (definition_group ~atd_filename env true x)) + |> List.map (fun x -> Inline (definition_group ~atd_filename env Type_decl x)) in let namespace_start = [Line (sprintf "namespace atd {")] in let namespace_end = [Line "} // namespace atd"] in - Line (fixed_size_preamble_header atd_filename) :: Inline head :: namespace_start @ cpp_declarations @ namespace_end - |> double_spaced + Line (fixed_size_preamble_header atd_filename) + :: Inline head + :: (namespace_start |> double_spaced) + @ cpp_forward_declarations + @ cpp_typedefs + @ (cpp_declarations |> double_spaced) + @ namespace_end |> Indent.to_file ~indent:4 dst_path let to_cpp_file ~atd_filename (items : A.module_body) dst_path = @@ -1410,7 +1548,7 @@ let to_cpp_file ~atd_filename (items : A.module_body) dst_path = reserve_good_struct_names env items; let cpp_defs = Atd.Util.tsort items - |> List.map (fun x -> Inline (definition_group ~atd_filename env false x)) + |> List.map (fun x -> Inline (definition_group ~atd_filename env Type_def x)) in let head = [Line (sprintf "#include \"%s_atd.hpp\"" (Filename.chop_extension atd_filename))] in let namespace_end = [Line "} // namespace atd"] in diff --git a/atdcpp/test/cpp-expected/everything_atd.cpp b/atdcpp/test/cpp-expected/everything_atd.cpp index c29dcb0d1..ea89007e3 100644 --- a/atdcpp/test/cpp-expected/everything_atd.cpp +++ b/atdcpp/test/cpp-expected/everything_atd.cpp @@ -371,14 +371,14 @@ namespace RecursiveVariant { } return from_json(doc); } - void to_json(const atd::typedefs::RecursiveVariant &x, rapidjson::Writer &writer) { + void to_json(const typedefs::RecursiveVariant &x, rapidjson::Writer &writer) { std::visit([&writer](auto &&arg) { using T = std::decay_t; if constexpr (std::is_same_v) Types::Integer::to_json(arg, writer); if constexpr (std::is_same_v) Types::Rec::to_json(arg, writer); }, x); } - std::string to_json_string(const atd::typedefs::RecursiveVariant &x) { + std::string to_json_string(const typedefs::RecursiveVariant &x) { rapidjson::StringBuffer buffer; rapidjson::Writer writer(buffer); to_json(x, writer); @@ -630,7 +630,7 @@ namespace Kind { } return from_json(doc); } - void to_json(const atd::typedefs::Kind &x, rapidjson::Writer &writer) { + void to_json(const typedefs::Kind &x, rapidjson::Writer &writer) { std::visit([&writer](auto &&arg) { using T = std::decay_t; if constexpr (std::is_same_v) Types::Root::to_json(arg, writer); @@ -639,7 +639,7 @@ namespace Kind { if constexpr (std::is_same_v) Types::Amaze::to_json(arg, writer); }, x); } - std::string to_json_string(const atd::typedefs::Kind &x) { + std::string to_json_string(const typedefs::Kind &x) { rapidjson::StringBuffer buffer; rapidjson::Writer writer(buffer); to_json(x, writer); @@ -1157,14 +1157,14 @@ namespace Frozen { } return from_json(doc); } - void to_json(const atd::typedefs::Frozen &x, rapidjson::Writer &writer) { + void to_json(const typedefs::Frozen &x, rapidjson::Writer &writer) { std::visit([&writer](auto &&arg) { using T = std::decay_t; if constexpr (std::is_same_v) Types::A::to_json(arg, writer); if constexpr (std::is_same_v) Types::B::to_json(arg, writer); }, x); } - std::string to_json_string(const atd::typedefs::Frozen &x) { + std::string to_json_string(const typedefs::Frozen &x) { rapidjson::StringBuffer buffer; rapidjson::Writer writer(buffer); to_json(x, writer); diff --git a/atdcpp/test/cpp-expected/everything_atd.hpp b/atdcpp/test/cpp-expected/everything_atd.hpp index f8b8881bf..c7e2d4245 100644 --- a/atdcpp/test/cpp-expected/everything_atd.hpp +++ b/atdcpp/test/cpp-expected/everything_atd.hpp @@ -19,47 +19,89 @@ #include - - #include - - namespace atd { - - +// forward declarations namespace RecursiveVariant::Types { struct Integer; struct Rec; } -namespace typedefs { - typedef std::variant RecursiveVariant; -} -namespace RecursiveVariant::Types { - // Original type: recursive_variant = [ ... | Integer of ... | ... ] - struct Integer - { - int value; - static void to_json(const Integer &e, rapidjson::Writer &writer); - }; - // Original type: recursive_variant = [ ... | Rec of ... | ... ] - struct Rec - { - std::shared_ptr value; - static void to_json(const Rec &e, rapidjson::Writer &writer); - }; +struct RecursiveRecord2; +struct RecursiveClass; +struct ThreeLevelNestedListRecord; +struct StructWithRecursiveVariant; +namespace Kind::Types { + struct Root; + struct Thing; + struct WOW; + struct Amaze; } -namespace RecursiveVariant { - static atd::typedefs::RecursiveVariant from_json(const rapidjson::Value &x); - static atd::typedefs::RecursiveVariant from_json_string(const std::string &s); - static void to_json(const atd::typedefs::RecursiveVariant &x, rapidjson::Writer &writer); - std::string to_json_string(const atd::typedefs::RecursiveVariant &x); +struct IntFloatParametrizedRecord; +struct Root; +struct RequireField; +struct RecordWithWrappedType; +namespace Frozen::Types { + struct A; + struct B; } +struct DefaultList; +struct Credential; +struct Credentials; -struct RecursiveRecord2; namespace typedefs { typedef RecursiveRecord2 RecursiveRecord2; + typedef RecursiveClass RecursiveClass; + typedef ThreeLevelNestedListRecord ThreeLevelNestedListRecord; + typedef StructWithRecursiveVariant StructWithRecursiveVariant; + typedef IntFloatParametrizedRecord IntFloatParametrizedRecord; + typedef Root Root; + typedef RequireField RequireField; + typedef RecordWithWrappedType RecordWithWrappedType; + typedef DefaultList DefaultList; + typedef Credential Credential; + typedef Credentials Credentials; + + typedef std::variant RecursiveVariant; + typedef std::variant Kind; + typedef std::variant Frozen; + + typedef int St; + typedef uint32_t Alias3; + typedef typedefs::Alias3 AliasOfAliasNotWrapped; + typedef typedefs::AliasOfAliasNotWrapped AliasOfAliasOfAlias; + typedef std::vector Alias; + typedef std::tuple KindParametrizedTuple; + typedef uint32_t Password; + typedef std::tuple Pair; + typedef std::vector Credentials2; + typedef uint16_t AliasOfAlias; + typedef std::vector Alias2; +} // namespace typedefs + +namespace RecursiveVariant { + namespace Types { + // Original type: recursive_variant = [ ... | Integer of ... | ... ] + struct Integer + { + int value; + static void to_json(const Integer &e, rapidjson::Writer &writer); + }; + // Original type: recursive_variant = [ ... | Rec of ... | ... ] + struct Rec + { + std::shared_ptr value; + static void to_json(const Rec &e, rapidjson::Writer &writer); + }; + } + + static typedefs::RecursiveVariant from_json(const rapidjson::Value &x); + static typedefs::RecursiveVariant from_json_string(const std::string &s); + static void to_json(const typedefs::RecursiveVariant &x, rapidjson::Writer &writer); + std::string to_json_string(const typedefs::RecursiveVariant &x); } + + struct RecursiveRecord2 { int id; bool flag; @@ -73,10 +115,6 @@ struct RecursiveRecord2 { }; -struct RecursiveClass; -namespace typedefs { - typedef RecursiveClass RecursiveClass; -} struct RecursiveClass { int id; bool flag; @@ -90,10 +128,6 @@ struct RecursiveClass { }; -struct ThreeLevelNestedListRecord; -namespace typedefs { - typedef ThreeLevelNestedListRecord ThreeLevelNestedListRecord; -} struct ThreeLevelNestedListRecord { std::vector>> field_a; @@ -105,10 +139,6 @@ struct ThreeLevelNestedListRecord { }; -struct StructWithRecursiveVariant; -namespace typedefs { - typedef StructWithRecursiveVariant StructWithRecursiveVariant; -} struct StructWithRecursiveVariant { typedefs::RecursiveVariant variant; @@ -120,9 +150,6 @@ struct StructWithRecursiveVariant { }; -namespace typedefs { - typedef int St; -} namespace St { typedefs::St from_json(const rapidjson::Value &doc); static typedefs::St from_json_string(const std::string &s); @@ -131,48 +158,37 @@ namespace St { } -namespace Kind::Types { - struct Root; - struct Thing; - struct WOW; - struct Amaze; -} -namespace typedefs { - typedef std::variant Kind; -} -namespace Kind::Types { - // Original type: kind = [ ... | Root | ... ] - struct Root { - static void to_json(const Root &e, rapidjson::Writer &writer); - }; - // Original type: kind = [ ... | Thing of ... | ... ] - struct Thing - { - int value; - static void to_json(const Thing &e, rapidjson::Writer &writer); - }; - // Original type: kind = [ ... | WOW | ... ] - struct WOW { - static void to_json(const WOW &e, rapidjson::Writer &writer); - }; - // Original type: kind = [ ... | Amaze of ... | ... ] - struct Amaze - { - std::vector value; - static void to_json(const Amaze &e, rapidjson::Writer &writer); - }; -} namespace Kind { - static atd::typedefs::Kind from_json(const rapidjson::Value &x); - static atd::typedefs::Kind from_json_string(const std::string &s); - static void to_json(const atd::typedefs::Kind &x, rapidjson::Writer &writer); - std::string to_json_string(const atd::typedefs::Kind &x); + namespace Types { + // Original type: kind = [ ... | Root | ... ] + struct Root { + static void to_json(const Root &e, rapidjson::Writer &writer); + }; + // Original type: kind = [ ... | Thing of ... | ... ] + struct Thing + { + int value; + static void to_json(const Thing &e, rapidjson::Writer &writer); + }; + // Original type: kind = [ ... | WOW | ... ] + struct WOW { + static void to_json(const WOW &e, rapidjson::Writer &writer); + }; + // Original type: kind = [ ... | Amaze of ... | ... ] + struct Amaze + { + std::vector value; + static void to_json(const Amaze &e, rapidjson::Writer &writer); + }; + } + + static typedefs::Kind from_json(const rapidjson::Value &x); + static typedefs::Kind from_json_string(const std::string &s); + static void to_json(const typedefs::Kind &x, rapidjson::Writer &writer); + std::string to_json_string(const typedefs::Kind &x); } -namespace typedefs { - typedef uint32_t Alias3; -} namespace Alias3 { typedefs::Alias3 from_json(const rapidjson::Value &doc); static typedefs::Alias3 from_json_string(const std::string &s); @@ -181,9 +197,6 @@ namespace Alias3 { } -namespace typedefs { - typedef typedefs::Alias3 AliasOfAliasNotWrapped; -} namespace AliasOfAliasNotWrapped { typedefs::AliasOfAliasNotWrapped from_json(const rapidjson::Value &doc); static typedefs::AliasOfAliasNotWrapped from_json_string(const std::string &s); @@ -192,9 +205,6 @@ namespace AliasOfAliasNotWrapped { } -namespace typedefs { - typedef typedefs::AliasOfAliasNotWrapped AliasOfAliasOfAlias; -} namespace AliasOfAliasOfAlias { typedefs::AliasOfAliasOfAlias from_json(const rapidjson::Value &doc); static typedefs::AliasOfAliasOfAlias from_json_string(const std::string &s); @@ -203,9 +213,6 @@ namespace AliasOfAliasOfAlias { } -namespace typedefs { - typedef std::vector Alias; -} namespace Alias { typedefs::Alias from_json(const rapidjson::Value &doc); static typedefs::Alias from_json_string(const std::string &s); @@ -214,9 +221,6 @@ namespace Alias { } -namespace typedefs { - typedef std::tuple KindParametrizedTuple; -} namespace KindParametrizedTuple { typedefs::KindParametrizedTuple from_json(const rapidjson::Value &doc); static typedefs::KindParametrizedTuple from_json_string(const std::string &s); @@ -225,10 +229,6 @@ namespace KindParametrizedTuple { } -struct IntFloatParametrizedRecord; -namespace typedefs { - typedef IntFloatParametrizedRecord IntFloatParametrizedRecord; -} struct IntFloatParametrizedRecord { int field_a; std::vector field_b = {}; @@ -241,10 +241,6 @@ struct IntFloatParametrizedRecord { }; -struct Root; -namespace typedefs { - typedef Root Root; -} struct Root { std::string id; bool await; @@ -280,10 +276,6 @@ struct Root { }; -struct RequireField; -namespace typedefs { - typedef RequireField RequireField; -} struct RequireField { std::string req; @@ -295,10 +287,6 @@ struct RequireField { }; -struct RecordWithWrappedType; -namespace typedefs { - typedef RecordWithWrappedType RecordWithWrappedType; -} struct RecordWithWrappedType { int item; @@ -310,9 +298,6 @@ struct RecordWithWrappedType { }; -namespace typedefs { - typedef uint32_t Password; -} namespace Password { typedefs::Password from_json(const rapidjson::Value &doc); static typedefs::Password from_json_string(const std::string &s); @@ -321,9 +306,6 @@ namespace Password { } -namespace typedefs { - typedef std::tuple Pair; -} namespace Pair { typedefs::Pair from_json(const rapidjson::Value &doc); static typedefs::Pair from_json_string(const std::string &s); @@ -332,37 +314,27 @@ namespace Pair { } -namespace Frozen::Types { - struct A; - struct B; -} -namespace typedefs { - typedef std::variant Frozen; -} -namespace Frozen::Types { - // Original type: frozen = [ ... | A | ... ] - struct A { - static void to_json(const A &e, rapidjson::Writer &writer); - }; - // Original type: frozen = [ ... | B of ... | ... ] - struct B - { - int value; - static void to_json(const B &e, rapidjson::Writer &writer); - }; -} namespace Frozen { - static atd::typedefs::Frozen from_json(const rapidjson::Value &x); - static atd::typedefs::Frozen from_json_string(const std::string &s); - static void to_json(const atd::typedefs::Frozen &x, rapidjson::Writer &writer); - std::string to_json_string(const atd::typedefs::Frozen &x); + namespace Types { + // Original type: frozen = [ ... | A | ... ] + struct A { + static void to_json(const A &e, rapidjson::Writer &writer); + }; + // Original type: frozen = [ ... | B of ... | ... ] + struct B + { + int value; + static void to_json(const B &e, rapidjson::Writer &writer); + }; + } + + static typedefs::Frozen from_json(const rapidjson::Value &x); + static typedefs::Frozen from_json_string(const std::string &s); + static void to_json(const typedefs::Frozen &x, rapidjson::Writer &writer); + std::string to_json_string(const typedefs::Frozen &x); } -struct DefaultList; -namespace typedefs { - typedef DefaultList DefaultList; -} struct DefaultList { std::vector items = {}; @@ -374,10 +346,6 @@ struct DefaultList { }; -struct Credential; -namespace typedefs { - typedef Credential Credential; -} struct Credential { std::string name; int password; @@ -390,9 +358,6 @@ struct Credential { }; -namespace typedefs { - typedef std::vector Credentials2; -} namespace Credentials2 { typedefs::Credentials2 from_json(const rapidjson::Value &doc); static typedefs::Credentials2 from_json_string(const std::string &s); @@ -401,10 +366,6 @@ namespace Credentials2 { } -struct Credentials; -namespace typedefs { - typedef Credentials Credentials; -} struct Credentials { std::vector credentials; @@ -416,9 +377,6 @@ struct Credentials { }; -namespace typedefs { - typedef uint16_t AliasOfAlias; -} namespace AliasOfAlias { typedefs::AliasOfAlias from_json(const rapidjson::Value &doc); static typedefs::AliasOfAlias from_json_string(const std::string &s); @@ -427,15 +385,10 @@ namespace AliasOfAlias { } -namespace typedefs { - typedef std::vector Alias2; -} namespace Alias2 { typedefs::Alias2 from_json(const rapidjson::Value &doc); static typedefs::Alias2 from_json_string(const std::string &s); void to_json(const typedefs::Alias2 &t, rapidjson::Writer &writer); std::string to_json_string(const typedefs::Alias2 &t); } - - } // namespace atd diff --git a/atdcpp/test/cpp-tests/test_atdd.cpp b/atdcpp/test/cpp-tests/test_atdd.cpp index 8b89643df..979d6e308 100644 --- a/atdcpp/test/cpp-tests/test_atdd.cpp +++ b/atdcpp/test/cpp-tests/test_atdd.cpp @@ -68,12 +68,13 @@ int main() { typedefs::RecursiveVariant recursiveVariant = RecursiveVariant::Types::Integer{42}; typedefs::RecursiveVariant recursiveVariant2 = RecursiveVariant::Types::Rec{std::make_shared(recursiveVariant)}; - typedefs::StructWithRecursiveVariant structWithRecursiveVariant = {recursiveVariant2}; + typedefs::RecursiveVariant recursiveVariant3 = RecursiveVariant::Types::Rec{std::make_shared(recursiveVariant2)}; + typedefs::StructWithRecursiveVariant structWithRecursiveVariant = {recursiveVariant3}; std::string json = structWithRecursiveVariant.to_json_string(); typedefs::StructWithRecursiveVariant structWithRecursiveVariantFromJson = StructWithRecursiveVariant::from_json_string(json); - if (json == R"({"variant":["Rec",["Integer",42]]})" && json == structWithRecursiveVariantFromJson.to_json_string()) { + if (json == R"({"variant":["Rec",["Rec",["Integer",42]]]})" && json == structWithRecursiveVariantFromJson.to_json_string()) { std::cout << "Test passed: recursiveVariant" << std::endl; } else { throw std::runtime_error("check is failed"); From cf107c921b5c302df7ec52631c44d7492a55aa61 Mon Sep 17 00:00:00 2001 From: Alexandre Bourquelot Date: Wed, 6 Mar 2024 13:02:27 +0800 Subject: [PATCH 29/47] atdcpp: refactor code to remove copied code --- atdcpp/src/lib/Codegen.ml | 439 +++++++++++++------------------------- 1 file changed, 143 insertions(+), 296 deletions(-) diff --git a/atdcpp/src/lib/Codegen.ml b/atdcpp/src/lib/Codegen.ml index e581905ae..6dfa758d5 100644 --- a/atdcpp/src/lib/Codegen.ml +++ b/atdcpp/src/lib/Codegen.ml @@ -903,6 +903,13 @@ let from_json_string_definition cpp_name p = Line "}"; ] +type codegen_type = + | Declaration + | Definition + | Forward_decl + | Struct_typedef + | Alias_typedef + | Variant_typedef let record_definition env loc name (fields : field list) an = let cpp_struct_name = struct_name env name in @@ -976,17 +983,17 @@ let record_definition env loc name (fields : field list) an = ] -let record env loc name (fields : field list) an = +let record env loc name (fields : field list) an codegen_type = let cpp_struct_name = struct_name env name in let trans_meth = env.translate_inst_variable () in - let fields = + let fields_l = List.map (function | `Field x -> x | `Inherit _ -> (* expanded at loading time *) assert false) fields in let inst_var_declarations = - List.map (fun x -> Inline (inst_var_declaration env trans_meth x)) fields + List.map (fun x -> Inline (inst_var_declaration env trans_meth x)) fields_l in let from_json = [ @@ -1009,6 +1016,8 @@ let record env loc name (fields : field list) an = Line (sprintf "std::string to_json_string();" ); ] in + match codegen_type with + | Declaration -> [ Line (sprintf "struct %s {" cpp_struct_name); Block ([ @@ -1022,23 +1031,27 @@ let record env loc name (fields : field list) an = ]); Line ("};"); ] + | Definition -> record_definition env loc name fields an + | Forward_decl -> [Line (sprintf "struct %s;" cpp_struct_name)] + | Struct_typedef -> [Line (sprintf "typedef %s %s;" cpp_struct_name cpp_struct_name)] + | _ -> [] -let alias_wrapper env name type_expr = +let alias_wrapper env name type_expr codegen_type = let cpp_struct_name = struct_name env name in - [ - Line (sprintf "namespace %s {" cpp_struct_name); - Block [ - Line (sprintf "typedefs::%s from_json(const rapidjson::Value &doc);" cpp_struct_name); - Inline (from_json_string_declaration (sprintf "typedefs::%s" cpp_struct_name)); - Line (sprintf "void to_json(const typedefs::%s &t, rapidjson::Writer &writer);" cpp_struct_name); - Line (sprintf "std::string to_json_string(const typedefs::%s &t);" cpp_struct_name); - ]; - Line "}"; - ] - -let alias_wrapper_definition env name type_expr = - let cpp_struct_name = struct_name env name in - [ + match codegen_type with + | Declaration -> + [ + Line (sprintf "namespace %s {" cpp_struct_name); + Block [ + Line (sprintf "typedefs::%s from_json(const rapidjson::Value &doc);" cpp_struct_name); + Inline (from_json_string_declaration (sprintf "typedefs::%s" cpp_struct_name)); + Line (sprintf "void to_json(const typedefs::%s &t, rapidjson::Writer &writer);" cpp_struct_name); + Line (sprintf "std::string to_json_string(const typedefs::%s &t);" cpp_struct_name); + ]; + Line "}"; + ] + | Definition -> + [ Line (sprintf "namespace %s {" cpp_struct_name); Block [ Line (sprintf "typedefs::%s from_json(const rapidjson::Value &doc) {" cpp_struct_name); @@ -1062,55 +1075,58 @@ let alias_wrapper_definition env name type_expr = Line "}"; ]; Line "}"; ] + | Alias_typedef -> [Line (sprintf "typedef %s %s;" (type_name_of_expr env type_expr) cpp_struct_name)] + | _ -> [] -let case_class_definition env type_name (loc, orig_name, unique_name, an, opt_e) = - let json_name = Atd.Json.get_json_cons orig_name an in - match opt_e with - | None -> - [ - Line (sprintf "void %s::to_json(const %s &e, rapidjson::Writer &writer){" (trans env orig_name) (trans env orig_name)); - Block [ - Line (sprintf "writer.String(\"%s\");" (single_esc json_name)); - ]; - Line (sprintf "};"); - ] - | Some e -> - [ - Line (sprintf "void %s::to_json(const %s &e, rapidjson::Writer &writer){" (trans env orig_name) (trans env orig_name)); - Block [ - Line (sprintf "writer.StartArray();"); - Line (sprintf "writer.String(\"%s\");" (single_esc json_name)); - Line (sprintf "%se.value, writer);" (json_writer env e)); - Line (sprintf "writer.EndArray();"); - ]; - Line("}"); - ] -let case_class env type_name - (loc, orig_name, unique_name, an, opt_e) = - match opt_e with - | None -> - [ - Line (sprintf {|// Original type: %s = [ ... | %s | ... ]|} - type_name - orig_name); - Line (sprintf "struct %s {" (trans env orig_name)); - Block [Line (sprintf "static void to_json(const %s &e, rapidjson::Writer &writer);" (trans env orig_name));]; - Line (sprintf "};"); +let case_class env type_name (loc, orig_name, unique_name, an, opt_e) case_classes = + let json_name = Atd.Json.get_json_cons orig_name an in + match case_classes with + | Declaration -> (match opt_e with + | None -> + [ + Line (sprintf {|// Original type: %s = [ ... | %s | ... ]|} + type_name + orig_name); + Line (sprintf "struct %s {" (trans env orig_name)); + Block [Line (sprintf "static void to_json(const %s &e, rapidjson::Writer &writer);" (trans env orig_name));]; + Line (sprintf "};"); ] - | Some e -> - [ - Line (sprintf {|// Original type: %s = [ ... | %s of ... | ... ]|} - type_name - orig_name); - Line (sprintf "struct %s" (trans env orig_name)); - Line(sprintf "{"); - Block [ - Line (sprintf "%s value;" (type_name_of_expr env e)); - Line (sprintf "static void to_json(const %s &e, rapidjson::Writer &writer);" (trans env orig_name)); - ]; - Line(sprintf "};"); + | Some e -> + [ + Line (sprintf {|// Original type: %s = [ ... | %s of ... | ... ]|} + type_name + orig_name); + Line (sprintf "struct %s" (trans env orig_name)); + Line(sprintf "{"); + Block [ + Line (sprintf "%s value;" (type_name_of_expr env e)); + Line (sprintf "static void to_json(const %s &e, rapidjson::Writer &writer);" (trans env orig_name)); + ]; + Line(sprintf "};"); + ]) + | Definition -> (match opt_e with + | None -> + [ + Line (sprintf "void %s::to_json(const %s &e, rapidjson::Writer &writer){" (trans env orig_name) (trans env orig_name)); + Block [ + Line (sprintf "writer.String(\"%s\");" (single_esc json_name)); + ]; + Line (sprintf "};"); ] + | Some e -> + [ + Line (sprintf "void %s::to_json(const %s &e, rapidjson::Writer &writer){" (trans env orig_name) (trans env orig_name)); + Block [ + Line (sprintf "writer.StartArray();"); + Line (sprintf "writer.String(\"%s\");" (single_esc json_name)); + Line (sprintf "%se.value, writer);" (json_writer env e)); + Line (sprintf "writer.EndArray();"); + ]; + Line("}"); + ]) + | _ -> [] + let read_cases0 env loc name cases0 = @@ -1158,7 +1174,7 @@ let read_cases1 env loc name cases1 = (struct_name env name |> single_esc)) ] -let sum_container_definition env loc name cases = +let sum_container env loc name cases codegen_type = let cpp_struct_name = struct_name env name in let cases0, cases1 = List.partition (fun (loc, orig_name, unique_name, an, opt_e) -> @@ -1188,6 +1204,15 @@ let sum_container_definition env loc name cases = else [] in + match codegen_type with + | Declaration -> + [ + Line (sprintf "static typedefs::%s from_json(const rapidjson::Value &x);" (cpp_struct_name)); + Inline (from_json_string_declaration (sprintf "typedefs::%s" cpp_struct_name)); + Line (sprintf "static void to_json(const typedefs::%s &x, rapidjson::Writer &writer);" (cpp_struct_name)); + Line (sprintf "std::string to_json_string(const typedefs::%s &x);" (cpp_struct_name)); + ] + | Definition -> [ Line (sprintf "namespace %s {" (cpp_struct_name)); Block [ @@ -1217,7 +1242,6 @@ let sum_container_definition env loc name cases = ]; Line ("}"); ]; - Block [Line (sprintf "std::string to_json_string(const typedefs::%s &x) {" (cpp_struct_name)); Block [ Line ("rapidjson::StringBuffer buffer;"); @@ -1228,42 +1252,9 @@ let sum_container_definition env loc name cases = Line "}";]; Line ("}"); ] - - -let sum_container env loc name cases = - let cpp_struct_name = struct_name env name in - [ - Line (sprintf "static typedefs::%s from_json(const rapidjson::Value &x);" (cpp_struct_name)); - Inline (from_json_string_declaration (sprintf "typedefs::%s" cpp_struct_name)); - Line (sprintf "static void to_json(const typedefs::%s &x, rapidjson::Writer &writer);" (cpp_struct_name)); - Line (sprintf "std::string to_json_string(const typedefs::%s &x);" (cpp_struct_name)); - ] + | _ -> [] - -let sum_definition env loc name cases = - let cases = - List.map (fun (x : variant) -> - match x with - | Variant (loc, (orig_name, an), opt_e) -> - let unique_name = create_struct_name env orig_name in - (loc, orig_name, unique_name, an, opt_e) - | Inherit _ -> assert false - ) cases - in - let case_classes = - List.map (fun x -> Inline (case_class_definition env name x)) cases - |> double_spaced - in - let container_class = sum_container_definition env loc name cases in - [ - Line (sprintf "namespace %s::Types {" (struct_name env name)); - Block case_classes; - Line (sprintf "}"); - Inline container_class; - ] - |> double_spaced - -let sum env loc name cases = +let sum env loc name cases codegen_type = let cases = List.map (fun (x : variant) -> match x with @@ -1274,207 +1265,75 @@ let sum env loc name cases = ) cases in let case_classes = - List.map (fun x -> Inline (case_class env name x)) cases + List.map (fun x -> Inline (case_class env name x codegen_type)) cases in - let container_class = sum_container env loc name cases in - [ - Line (sprintf "namespace %s {" (struct_name env name)); - Block [ - Line (sprintf "namespace Types {"); - Block case_classes; + let container_class = sum_container env loc name cases codegen_type in + match codegen_type with + | Declaration -> + [ + Line (sprintf "namespace %s {" (struct_name env name)); + Block [ + Line (sprintf "namespace Types {"); + Block case_classes; + Line (sprintf "}"); + Line (""); + Inline container_class; + ]; + Line (sprintf "}"); + ] + | Definition -> + [ + Line (sprintf "namespace %s::Types {" (struct_name env name)); + Block (case_classes |> double_spaced); Line (sprintf "}"); - Line (""); Inline container_class; - ]; - Line (sprintf "}"); - ] - -type codegen_type = - | Type_decl | Type_def - | Forward_decl | Struct_typedef | Alias_typedef | Variant_typedef - - -let type_def env ((loc, (name, param, an), e) : A.type_def) : B.t = - if param <> [] then - not_implemented loc "parametrized type"; - let unwrap e = - match e with - | Sum (loc, cases, an) -> - sum_definition env loc name cases - | Record (loc, fields, an) -> - record_definition env loc name fields an - | Tuple _ - | List _ - | Option _ - | Nullable _ - | Wrap _ - | Name _ -> alias_wrapper_definition env name e - | Shared _ -> not_implemented loc "cyclic references" - | Tvar _ -> not_implemented loc "parametrized type" - in - unwrap e - -let type_decl env ((loc, (name, param, an), e) : A.type_def) : B.t = - if param <> [] then - not_implemented loc "parametrized type"; - let unwrap e = - match e with - | Sum (loc, cases, an) -> - sum env loc name cases - | Record (loc, fields, an) -> - record env loc name fields an - | Tuple _ - | List _ - | Option _ - | Nullable _ - | Wrap _ - | Name _ -> alias_wrapper env name e - | Shared _ -> not_implemented loc "cyclic references" - | Tvar _ -> not_implemented loc "parametrized type" - in - unwrap e - - -let sum_forward_decl env loc name cases = - let cases = - List.map (fun (x : variant) -> - match x with - | Variant (loc, (orig_name, an), opt_e) -> - let unique_name = create_struct_name env orig_name in - (loc, orig_name, unique_name, an, opt_e) - | Inherit _ -> assert false - ) cases - in - let cases_forward_declarations = - List.map (fun (loc, orig_name, unique_name, an, opt_e) -> Line (sprintf "struct %s;" orig_name)) cases - in - let cases_forward_declarations_in_namespace = + ] |> double_spaced + | Forward_decl -> + let cases_forward_declarations = + List.map (fun (loc, orig_name, unique_name, an, opt_e) -> Line (sprintf "struct %s;" orig_name)) cases + in [ Line (sprintf "namespace %s::Types {" (struct_name env name)); Block cases_forward_declarations; Line (sprintf "}"); ] - in - cases_forward_declarations_in_namespace - -let record_forward_decl env loc name (fields : field list) an = - let cpp_struct_name = struct_name env name in - Line (sprintf "struct %s;" cpp_struct_name) - - -let record_typedef env loc name (fields : field list) an = - let cpp_struct_name = struct_name env name in - Line (sprintf "typedef %s %s;" cpp_struct_name cpp_struct_name) - -let forward_decl env ((loc, (name, param, an), e) : A.type_def) : B.t = - if param <> [] then - not_implemented loc "parametrized type"; - let unwrap e = - match e with - | Sum (loc, cases, an) -> - sum_forward_decl env loc name cases - | Record (loc, fields, an) -> - [record_forward_decl env loc name fields an] - | Tuple _ | List _ | Option _ | Nullable _ | Wrap _ | Name _ -> [] - | Shared _ -> not_implemented loc "cyclic references" - | Tvar _ -> not_implemented loc "parametrized type" - in - unwrap e - -let sum_typedef env loc name cases = - let cases = - List.map (fun (x : variant) -> - match x with - | Variant (loc, (orig_name, an), opt_e) -> - let unique_name = create_struct_name env orig_name in - (loc, orig_name, unique_name, an, opt_e) - | Inherit _ -> assert false - ) cases - in - let cpp_struct_name = struct_name env name in - let type_list = - List.map (fun (loc, orig_name, unique_name, an, opt_e) -> - (sprintf "atd::%s::Types::%s" (cpp_struct_name) (trans env orig_name)) - ) cases - |> String.concat ", " - in - let typedef_declaration = - Line(sprintf "typedef %s %s;" (sprintf "std::variant<%s>" type_list) (cpp_struct_name)); - in - typedef_declaration - -let alias_typedef env name type_expr = - let cpp_struct_name = struct_name env name in - let value_type = type_name_of_expr env type_expr in - Line (sprintf "typedef %s %s;" value_type cpp_struct_name) - + | Variant_typedef -> + let type_list = + List.map (fun (loc, orig_name, unique_name, an, opt_e) -> + (sprintf "atd::%s::Types::%s" (struct_name env name) (trans env orig_name)) + ) cases + |> String.concat ", " + in + [Line(sprintf "typedef %s %s;" (sprintf "std::variant<%s>" type_list) (struct_name env name))] + | _ -> [] -let typedefs env ((loc, (name, param, an), e) : A.type_def) : B.t = +let type_def env ((loc, (name, param, an), e) : A.type_def) codegen_type : B.t = if param <> [] then not_implemented loc "parametrized type"; let unwrap e = match e with | Sum (loc, cases, an) -> - [] + sum env loc name cases codegen_type | Record (loc, fields, an) -> - [record_typedef env loc name fields an] + record env loc name fields an codegen_type | Tuple _ | List _ | Option _ | Nullable _ | Wrap _ - | Name _ -> [] + | Name _ -> alias_wrapper env name e codegen_type | Shared _ -> not_implemented loc "cyclic references" | Tvar _ -> not_implemented loc "parametrized type" in unwrap e -let typedefs2 env ((loc, (name, param, an), e) : A.type_def) : B.t = - if param <> [] then - not_implemented loc "parametrized type"; - let unwrap e = - match e with - | Sum (loc, cases, an) -> - [] - | Record (loc, fields, an) -> - [] - | Tuple _ - | List _ - | Option _ - | Nullable _ - | Wrap _ - | Name _ -> [alias_typedef env name e] - | Shared _ -> not_implemented loc "cyclic references" - | Tvar _ -> not_implemented loc "parametrized type" - in - unwrap e - - let typedefs3 env ((loc, (name, param, an), e) : A.type_def) : B.t = - if param <> [] then - not_implemented loc "parametrized type"; - let unwrap e = - match e with - | Sum (loc, cases, an) -> - [sum_typedef env loc name cases] - | _ -> [] - in - unwrap e - let module_body env x codegen_type = - let fn = match codegen_type with - | Type_decl -> type_decl - | Type_def -> type_def - | Forward_decl -> forward_decl - | Struct_typedef -> typedefs - | Variant_typedef -> typedefs3 - | Alias_typedef -> typedefs2 - in let gen = - List.fold_left (fun acc (Type x) -> Inline (fn env x) :: acc) [] x + List.fold_left (fun acc (Type x) -> Inline (type_def env x codegen_type) :: acc) [] x |> List.rev in match codegen_type with - | Type_decl | Type_def -> gen |> spaced + | Declaration | Definition -> gen |> spaced | _ -> gen let definition_group ~atd_filename env codegen_type @@ -1501,37 +1360,25 @@ let to_header_file ~atd_filename ~head (items : A.module_body) dst_path = let env = init_env () in reserve_good_struct_names env items; let head = List.map (fun s -> Line s) head in - let cpp_forward_declarations = + let get_lines_for codegen_type = Atd.Util.tsort items - |> List.map (fun x -> Inline (definition_group ~atd_filename env Forward_decl x)) + |> List.map (fun x -> Inline (definition_group ~atd_filename env codegen_type x)) in + let cpp_forward_declarations = get_lines_for Forward_decl in let cpp_forward_declarations = [Line "// forward declarations"] @ cpp_forward_declarations @ [Line ""; Line ""] in - let cpp_typedefs = - Atd.Util.tsort items - |> List.map (fun x -> Inline (definition_group ~atd_filename env Struct_typedef x)) - in - let cpp_v_typedefs = - Atd.Util.tsort items - |> List.map (fun x -> Inline (definition_group ~atd_filename env Variant_typedef x)) - in - let cpp_true_typedefs = - Atd.Util.tsort items - |> List.map (fun x -> Inline (definition_group ~atd_filename env Alias_typedef x)) - in + let cpp_structs_typedefs = get_lines_for Struct_typedef in + let cpp_variant_typedefs = get_lines_for Variant_typedef in + let cpp_alias_typedefs = get_lines_for Alias_typedef in let cpp_typedefs = [Line "namespace typedefs {"] @ - [Block - [Inline cpp_typedefs; - Line ""; - Inline cpp_v_typedefs; - Line ""; - Inline cpp_true_typedefs;] + [(Block ([ + Inline cpp_structs_typedefs; + Inline cpp_variant_typedefs; + Inline cpp_alias_typedefs; + ] |> spaced)) ] @ [Line "} // namespace typedefs"; Line ""] in - let cpp_declarations = - Atd.Util.tsort items - |> List.map (fun x -> Inline (definition_group ~atd_filename env Type_decl x)) - in + let cpp_declarations = get_lines_for Declaration in let namespace_start = [Line (sprintf "namespace atd {")] in let namespace_end = [Line "} // namespace atd"] in Line (fixed_size_preamble_header atd_filename) @@ -1548,7 +1395,7 @@ let to_cpp_file ~atd_filename (items : A.module_body) dst_path = reserve_good_struct_names env items; let cpp_defs = Atd.Util.tsort items - |> List.map (fun x -> Inline (definition_group ~atd_filename env Type_def x)) + |> List.map (fun x -> Inline (definition_group ~atd_filename env Definition x)) in let head = [Line (sprintf "#include \"%s_atd.hpp\"" (Filename.chop_extension atd_filename))] in let namespace_end = [Line "} // namespace atd"] in From ea993a763159c6473c0213bb4feb4d5a58980816 Mon Sep 17 00:00:00 2001 From: Alexandre Bourquelot Date: Wed, 6 Mar 2024 17:06:05 +0800 Subject: [PATCH 30/47] atdcpp: remove static from namespaces --- atdcpp/src/lib/Codegen.ml | 15 ++++---- atdcpp/test/cpp-expected/everything_atd.hpp | 40 ++++++++++----------- 2 files changed, 28 insertions(+), 27 deletions(-) diff --git a/atdcpp/src/lib/Codegen.ml b/atdcpp/src/lib/Codegen.ml index 6dfa758d5..f06667ee1 100644 --- a/atdcpp/src/lib/Codegen.ml +++ b/atdcpp/src/lib/Codegen.ml @@ -884,9 +884,10 @@ let inst_var_declaration Line (sprintf "%s %s%s;" type_name var_name default) ] -let from_json_string_declaration cpp_name = +let from_json_string_declaration cpp_name static = + let static_val = match static with | true -> "static " | false -> "" in [ - Line (sprintf "static %s from_json_string(const std::string &s);" cpp_name); + Line (sprintf "%s%s from_json_string(const std::string &s);" static_val cpp_name); ] let from_json_string_definition cpp_name p = @@ -1024,7 +1025,7 @@ let record env loc name (fields : field list) an codegen_type = Inline inst_var_declarations; Line (""); Inline from_json; - Inline (from_json_string_declaration cpp_struct_name); + Inline (from_json_string_declaration cpp_struct_name true); Inline to_json; Inline to_json_string_static; Inline to_json_string; @@ -1044,7 +1045,7 @@ let alias_wrapper env name type_expr codegen_type = Line (sprintf "namespace %s {" cpp_struct_name); Block [ Line (sprintf "typedefs::%s from_json(const rapidjson::Value &doc);" cpp_struct_name); - Inline (from_json_string_declaration (sprintf "typedefs::%s" cpp_struct_name)); + Inline (from_json_string_declaration (sprintf "typedefs::%s" cpp_struct_name) false); Line (sprintf "void to_json(const typedefs::%s &t, rapidjson::Writer &writer);" cpp_struct_name); Line (sprintf "std::string to_json_string(const typedefs::%s &t);" cpp_struct_name); ]; @@ -1207,9 +1208,9 @@ let sum_container env loc name cases codegen_type = match codegen_type with | Declaration -> [ - Line (sprintf "static typedefs::%s from_json(const rapidjson::Value &x);" (cpp_struct_name)); - Inline (from_json_string_declaration (sprintf "typedefs::%s" cpp_struct_name)); - Line (sprintf "static void to_json(const typedefs::%s &x, rapidjson::Writer &writer);" (cpp_struct_name)); + Line (sprintf "typedefs::%s from_json(const rapidjson::Value &x);" (cpp_struct_name)); + Inline (from_json_string_declaration (sprintf "typedefs::%s" cpp_struct_name) false); + Line (sprintf "void to_json(const typedefs::%s &x, rapidjson::Writer &writer);" (cpp_struct_name)); Line (sprintf "std::string to_json_string(const typedefs::%s &x);" (cpp_struct_name)); ] | Definition -> diff --git a/atdcpp/test/cpp-expected/everything_atd.hpp b/atdcpp/test/cpp-expected/everything_atd.hpp index c7e2d4245..7e5b6f0c2 100644 --- a/atdcpp/test/cpp-expected/everything_atd.hpp +++ b/atdcpp/test/cpp-expected/everything_atd.hpp @@ -95,9 +95,9 @@ namespace RecursiveVariant { }; } - static typedefs::RecursiveVariant from_json(const rapidjson::Value &x); - static typedefs::RecursiveVariant from_json_string(const std::string &s); - static void to_json(const typedefs::RecursiveVariant &x, rapidjson::Writer &writer); + typedefs::RecursiveVariant from_json(const rapidjson::Value &x); + typedefs::RecursiveVariant from_json_string(const std::string &s); + void to_json(const typedefs::RecursiveVariant &x, rapidjson::Writer &writer); std::string to_json_string(const typedefs::RecursiveVariant &x); } @@ -152,7 +152,7 @@ struct StructWithRecursiveVariant { namespace St { typedefs::St from_json(const rapidjson::Value &doc); - static typedefs::St from_json_string(const std::string &s); + typedefs::St from_json_string(const std::string &s); void to_json(const typedefs::St &t, rapidjson::Writer &writer); std::string to_json_string(const typedefs::St &t); } @@ -182,16 +182,16 @@ namespace Kind { }; } - static typedefs::Kind from_json(const rapidjson::Value &x); - static typedefs::Kind from_json_string(const std::string &s); - static void to_json(const typedefs::Kind &x, rapidjson::Writer &writer); + typedefs::Kind from_json(const rapidjson::Value &x); + typedefs::Kind from_json_string(const std::string &s); + void to_json(const typedefs::Kind &x, rapidjson::Writer &writer); std::string to_json_string(const typedefs::Kind &x); } namespace Alias3 { typedefs::Alias3 from_json(const rapidjson::Value &doc); - static typedefs::Alias3 from_json_string(const std::string &s); + typedefs::Alias3 from_json_string(const std::string &s); void to_json(const typedefs::Alias3 &t, rapidjson::Writer &writer); std::string to_json_string(const typedefs::Alias3 &t); } @@ -199,7 +199,7 @@ namespace Alias3 { namespace AliasOfAliasNotWrapped { typedefs::AliasOfAliasNotWrapped from_json(const rapidjson::Value &doc); - static typedefs::AliasOfAliasNotWrapped from_json_string(const std::string &s); + typedefs::AliasOfAliasNotWrapped from_json_string(const std::string &s); void to_json(const typedefs::AliasOfAliasNotWrapped &t, rapidjson::Writer &writer); std::string to_json_string(const typedefs::AliasOfAliasNotWrapped &t); } @@ -207,7 +207,7 @@ namespace AliasOfAliasNotWrapped { namespace AliasOfAliasOfAlias { typedefs::AliasOfAliasOfAlias from_json(const rapidjson::Value &doc); - static typedefs::AliasOfAliasOfAlias from_json_string(const std::string &s); + typedefs::AliasOfAliasOfAlias from_json_string(const std::string &s); void to_json(const typedefs::AliasOfAliasOfAlias &t, rapidjson::Writer &writer); std::string to_json_string(const typedefs::AliasOfAliasOfAlias &t); } @@ -215,7 +215,7 @@ namespace AliasOfAliasOfAlias { namespace Alias { typedefs::Alias from_json(const rapidjson::Value &doc); - static typedefs::Alias from_json_string(const std::string &s); + typedefs::Alias from_json_string(const std::string &s); void to_json(const typedefs::Alias &t, rapidjson::Writer &writer); std::string to_json_string(const typedefs::Alias &t); } @@ -223,7 +223,7 @@ namespace Alias { namespace KindParametrizedTuple { typedefs::KindParametrizedTuple from_json(const rapidjson::Value &doc); - static typedefs::KindParametrizedTuple from_json_string(const std::string &s); + typedefs::KindParametrizedTuple from_json_string(const std::string &s); void to_json(const typedefs::KindParametrizedTuple &t, rapidjson::Writer &writer); std::string to_json_string(const typedefs::KindParametrizedTuple &t); } @@ -300,7 +300,7 @@ struct RecordWithWrappedType { namespace Password { typedefs::Password from_json(const rapidjson::Value &doc); - static typedefs::Password from_json_string(const std::string &s); + typedefs::Password from_json_string(const std::string &s); void to_json(const typedefs::Password &t, rapidjson::Writer &writer); std::string to_json_string(const typedefs::Password &t); } @@ -308,7 +308,7 @@ namespace Password { namespace Pair { typedefs::Pair from_json(const rapidjson::Value &doc); - static typedefs::Pair from_json_string(const std::string &s); + typedefs::Pair from_json_string(const std::string &s); void to_json(const typedefs::Pair &t, rapidjson::Writer &writer); std::string to_json_string(const typedefs::Pair &t); } @@ -328,9 +328,9 @@ namespace Frozen { }; } - static typedefs::Frozen from_json(const rapidjson::Value &x); - static typedefs::Frozen from_json_string(const std::string &s); - static void to_json(const typedefs::Frozen &x, rapidjson::Writer &writer); + typedefs::Frozen from_json(const rapidjson::Value &x); + typedefs::Frozen from_json_string(const std::string &s); + void to_json(const typedefs::Frozen &x, rapidjson::Writer &writer); std::string to_json_string(const typedefs::Frozen &x); } @@ -360,7 +360,7 @@ struct Credential { namespace Credentials2 { typedefs::Credentials2 from_json(const rapidjson::Value &doc); - static typedefs::Credentials2 from_json_string(const std::string &s); + typedefs::Credentials2 from_json_string(const std::string &s); void to_json(const typedefs::Credentials2 &t, rapidjson::Writer &writer); std::string to_json_string(const typedefs::Credentials2 &t); } @@ -379,7 +379,7 @@ struct Credentials { namespace AliasOfAlias { typedefs::AliasOfAlias from_json(const rapidjson::Value &doc); - static typedefs::AliasOfAlias from_json_string(const std::string &s); + typedefs::AliasOfAlias from_json_string(const std::string &s); void to_json(const typedefs::AliasOfAlias &t, rapidjson::Writer &writer); std::string to_json_string(const typedefs::AliasOfAlias &t); } @@ -387,7 +387,7 @@ namespace AliasOfAlias { namespace Alias2 { typedefs::Alias2 from_json(const rapidjson::Value &doc); - static typedefs::Alias2 from_json_string(const std::string &s); + typedefs::Alias2 from_json_string(const std::string &s); void to_json(const typedefs::Alias2 &t, rapidjson::Writer &writer); std::string to_json_string(const typedefs::Alias2 &t); } From 19fb4b790c1d58fb3867d7d4dfbd7edd97d052d2 Mon Sep 17 00:00:00 2001 From: Alexandre Bourquelot Date: Thu, 7 Mar 2024 17:53:07 +0800 Subject: [PATCH 31/47] atdcpp: fix optional and nullable serialization --- atdcpp/src/lib/Codegen.ml | 60 +++++---- atdcpp/test/atd-input/everything.atd | 14 +- atdcpp/test/cpp-expected/everything_atd.cpp | 141 +++++++++++++++++--- atdcpp/test/cpp-expected/everything_atd.hpp | 35 ++++- atdcpp/test/cpp-tests/test_atdd.cpp | 36 +++++ 5 files changed, 241 insertions(+), 45 deletions(-) diff --git a/atdcpp/src/lib/Codegen.ml b/atdcpp/src/lib/Codegen.ml index f06667ee1..ecd0bbbd2 100644 --- a/atdcpp/src/lib/Codegen.ml +++ b/atdcpp/src/lib/Codegen.ml @@ -201,6 +201,7 @@ let fixed_size_preamble_header atd_filename = #include #include #include +#include |} atd_filename atd_filename @@ -243,6 +244,7 @@ T _atd_missing_json_field(const std::string &type, const std::string &field) auto _atd_bad_json(const std::string &type, const rapidjson::Value &x) { + (void) x; return AtdException("Bad JSON for " + type); } @@ -649,7 +651,7 @@ let rec get_default_default (e : type_expr) : string option = | Tuple _ (* a default tuple could be possible but we're lazy *) -> None | List _ -> Some "{}" | Option _ - | Nullable _ -> None + | Nullable _ -> Some "std::nullopt" | Shared (loc, e, an) -> get_default_default e | Wrap (loc, e, an) -> get_default_default e | Name (loc, (loc2, name, []), an) -> @@ -657,7 +659,7 @@ let rec get_default_default (e : type_expr) : string option = | "unit" -> None | "bool" -> Some "false" | "int" -> Some "0" - | "float" -> Some "0.0" + | "float" -> Some "0.0f" | "string" -> Some {|""|} | "abstract" -> None | _ -> None @@ -749,29 +751,34 @@ and tuple_writer env (loc, cells, an) = let construct_json_field env trans_meth ((loc, (name, kind, an), e) : simple_field) = let unwrapped_type = unwrap_field_type loc name kind e in - let writer_function = json_writer env unwrapped_type in - let assignment = + let writer_function n = json_writer ~nested:n env unwrapped_type in + let cpp_var_name = inst_var_name trans_meth name in + let cpp_type_name = type_name_of_expr env unwrapped_type in + let cpp_default = match (get_cpp_default e an) with | Some x -> x | None -> "" in + let assignement = [ Line (sprintf "writer.Key(\"%s\");" (Atd.Json.get_json_fname name an |> single_esc)); - Line (sprintf "%st.%s, writer);" writer_function (inst_var_name trans_meth name)); + Line (sprintf "%st.%s, writer);" (writer_function false) (inst_var_name trans_meth name)); ] in match kind with - | Required - | With_default -> assignment + | Required -> assignement + | With_default -> + [ + Line (sprintf "if (t.%s != %s(%s)) {" cpp_var_name cpp_type_name cpp_default); + Block assignement; + Line "}" + ] | Optional -> [ - Line (sprintf "if (t.%s != std::nullopt) {" - (inst_var_name trans_meth name)); - Block [ - Line (sprintf "writer.Key(\"%s\");" - (Atd.Json.get_json_fname name an |> single_esc)); - Line(sprintf "%s([](const auto &v, auto &w){%sv, w);}, t.%s, writer);" - "_atd_write_option" - (json_writer ~nested:true env unwrapped_type) - (inst_var_name trans_meth name))]; - Line "}"; + Line (sprintf "if (t.%s != std::nullopt) {" cpp_var_name); + Block [ + Line (sprintf "writer.Key(\"%s\");" + (Atd.Json.get_json_fname name an |> single_esc)); + Line (sprintf "%st.%s.value(), writer);" (writer_function true) cpp_var_name) + ]; + Line "}"; ] (* @@ -856,12 +863,19 @@ let from_json_class_argument (sprintf "missing default cpp value for field '%s'" name) in + let reader = + match kind with + | Optional -> (match e with + | Option(_, e, _) -> (json_reader env e) + | _ -> A.error_at loc "optional field must be of type 'xxx option'") + | _ -> (json_reader env e) + in Inline [ Line (sprintf "if (doc.HasMember(\"%s\"))" (single_esc json_name)); Block [ Line (sprintf "record.%s = %sdoc[\"%s\"]);" cpp_name - (json_reader env e) + reader (single_esc json_name)); ]; Line (sprintf "else record.%s = %s;" cpp_name else_body);] @@ -912,14 +926,8 @@ type codegen_type = | Alias_typedef | Variant_typedef -let record_definition env loc name (fields : field list) an = +let record_definition env loc name fields an = let cpp_struct_name = struct_name env name in - let fields = - List.map (function - | `Field x -> x - | `Inherit _ -> (* expanded at loading time *) assert false) - fields - in let trans_meth = env.translate_inst_variable () in let from_json_class_arguments = List.map (fun x -> @@ -1032,7 +1040,7 @@ let record env loc name (fields : field list) an codegen_type = ]); Line ("};"); ] - | Definition -> record_definition env loc name fields an + | Definition -> record_definition env loc name fields_l an | Forward_decl -> [Line (sprintf "struct %s;" cpp_struct_name)] | Struct_typedef -> [Line (sprintf "typedef %s %s;" cpp_struct_name cpp_struct_name)] | _ -> [] diff --git a/atdcpp/test/atd-input/everything.atd b/atdcpp/test/atd-input/everything.atd index 1eb2c8503..20331c8f5 100644 --- a/atdcpp/test/atd-input/everything.atd +++ b/atdcpp/test/atd-input/everything.atd @@ -25,7 +25,7 @@ type root = { integer: int; __init__ : float; ~float_with_auto_default: float; - ~float_with_default : float; + ~float_with_default : float; items: int list list; ?maybe: int option; ~extras: int list; @@ -112,4 +112,16 @@ type recursive_record2 = { id: int; flag: bool; children: recursive_record2 nullable wrap ; +} + +type null_opt = { + a : int; + b : int option; + c : int nullable; + ?f : int option; + ~h :int option; + ~i : int nullable; +} + +type empty_record = { } \ No newline at end of file diff --git a/atdcpp/test/cpp-expected/everything_atd.cpp b/atdcpp/test/cpp-expected/everything_atd.cpp index ea89007e3..dd19e53c6 100644 --- a/atdcpp/test/cpp-expected/everything_atd.cpp +++ b/atdcpp/test/cpp-expected/everything_atd.cpp @@ -36,6 +36,7 @@ T _atd_missing_json_field(const std::string &type, const std::string &field) auto _atd_bad_json(const std::string &type, const rapidjson::Value &x) { + (void) x; return AtdException("Bad JSON for " + type); } @@ -801,8 +802,10 @@ void IntFloatParametrizedRecord::to_json(const IntFloatParametrizedRecord &t, ra writer.StartObject(); writer.Key("field_a"); _atd_write_int(t.field_a, writer); - writer.Key("field_b"); - _atd_write_array([](auto v, auto &w){_atd_write_float(v, w);}, t.field_b, writer); + if (t.field_b != std::vector({})) { + writer.Key("field_b"); + _atd_write_array([](auto v, auto &w){_atd_write_float(v, w);}, t.field_b, writer); + } writer.EndObject(); } std::string IntFloatParametrizedRecord::to_json_string(const IntFloatParametrizedRecord &t) { @@ -835,15 +838,15 @@ Root Root::from_json(const rapidjson::Value & doc) { else record.x___init__ = _atd_missing_json_field("Root", "__init__"); if (doc.HasMember("float_with_auto_default")) record.float_with_auto_default = _atd_read_float(doc["float_with_auto_default"]); - else record.float_with_auto_default = 0.0; + else record.float_with_auto_default = 0.0f; if (doc.HasMember("float_with_default")) record.float_with_default = _atd_read_float(doc["float_with_default"]); - else record.float_with_default = 0.1; + else record.float_with_default = 0.1f; if (doc.HasMember("items")) record.items = _atd_read_array([](const auto &v){return _atd_read_array([](const auto &v){return _atd_read_int(v);}, v);}, doc["items"]); else record.items = _atd_missing_json_field("Root", "items"); if (doc.HasMember("maybe")) - record.maybe = _atd_read_option([](const auto &v){return _atd_read_int(v);}, doc["maybe"]); + record.maybe = _atd_read_int(doc["maybe"]); else record.maybe = std::nullopt; if (doc.HasMember("extras")) record.extras = _atd_read_array([](const auto &v){return _atd_read_int(v);}, doc["extras"]); @@ -924,20 +927,28 @@ void Root::to_json(const Root &t, rapidjson::Writer &wr _atd_write_int(t.integer, writer); writer.Key("__init__"); _atd_write_float(t.x___init__, writer); - writer.Key("float_with_auto_default"); - _atd_write_float(t.float_with_auto_default, writer); - writer.Key("float_with_default"); - _atd_write_float(t.float_with_default, writer); + if (t.float_with_auto_default != float(0.0f)) { + writer.Key("float_with_auto_default"); + _atd_write_float(t.float_with_auto_default, writer); + } + if (t.float_with_default != float(0.1f)) { + writer.Key("float_with_default"); + _atd_write_float(t.float_with_default, writer); + } writer.Key("items"); _atd_write_array([](auto v, auto &w){_atd_write_array([](auto v, auto &w){_atd_write_int(v, w);}, v, w);}, t.items, writer); if (t.maybe != std::nullopt) { writer.Key("maybe"); - _atd_write_option([](const auto &v, auto &w){_atd_write_int(v, w);}, t.maybe, writer); + _atd_write_int(t.maybe.value(), writer); + } + if (t.extras != std::vector({})) { + writer.Key("extras"); + _atd_write_array([](auto v, auto &w){_atd_write_int(v, w);}, t.extras, writer); + } + if (t.answer != int(42)) { + writer.Key("answer"); + _atd_write_int(t.answer, writer); } - writer.Key("extras"); - _atd_write_array([](auto v, auto &w){_atd_write_int(v, w);}, t.extras, writer); - writer.Key("answer"); - _atd_write_int(t.answer, writer); writer.Key("aliased"); Alias::to_json(t.aliased, writer); writer.Key("point"); @@ -1115,6 +1126,72 @@ namespace Pair { } +NullOpt NullOpt::from_json(const rapidjson::Value & doc) { + NullOpt record; + if (!doc.IsObject()) { + throw AtdException("Expected an object"); + } + if (doc.HasMember("a")) + record.a = _atd_read_int(doc["a"]); + else record.a = _atd_missing_json_field("NullOpt", "a"); + if (doc.HasMember("b")) + record.b = _atd_read_option([](const auto &v){return _atd_read_int(v);}, doc["b"]); + else record.b = _atd_missing_json_field("NullOpt", "b"); + if (doc.HasMember("c")) + record.c = _atd_read_nullable([](const auto &v){return _atd_read_int(v);}, doc["c"]); + else record.c = _atd_missing_json_field("NullOpt", "c"); + if (doc.HasMember("f")) + record.f = _atd_read_int(doc["f"]); + else record.f = std::nullopt; + if (doc.HasMember("h")) + record.h = _atd_read_option([](const auto &v){return _atd_read_int(v);}, doc["h"]); + else record.h = 3; + if (doc.HasMember("i")) + record.i = _atd_read_nullable([](const auto &v){return _atd_read_int(v);}, doc["i"]); + else record.i = 3; + return record; +} +NullOpt NullOpt::from_json_string(const std::string &s) { + rapidjson::Document doc; + doc.Parse(s.c_str()); + if (doc.HasParseError()) { + throw AtdException("Failed to parse JSON"); + } + return from_json(doc); +} +void NullOpt::to_json(const NullOpt &t, rapidjson::Writer &writer) { + writer.StartObject(); + writer.Key("a"); + _atd_write_int(t.a, writer); + writer.Key("b"); + _atd_write_option([](auto v, auto &w){_atd_write_int(v, w);}, t.b, writer); + writer.Key("c"); + _atd_write_nullable([](auto v, auto &w){_atd_write_int(v, w);}, t.c, writer); + if (t.f != std::nullopt) { + writer.Key("f"); + _atd_write_int(t.f.value(), writer); + } + if (t.h != std::optional(3)) { + writer.Key("h"); + _atd_write_option([](auto v, auto &w){_atd_write_int(v, w);}, t.h, writer); + } + if (t.i != std::optional(3)) { + writer.Key("i"); + _atd_write_nullable([](auto v, auto &w){_atd_write_int(v, w);}, t.i, writer); + } + writer.EndObject(); +} +std::string NullOpt::to_json_string(const NullOpt &t) { + rapidjson::StringBuffer buffer; + rapidjson::Writer writer(buffer); + to_json(t, writer); + return buffer.GetString(); +} +std::string NullOpt::to_json_string() { + return to_json_string(*this); +} + + namespace Frozen::Types { @@ -1173,6 +1250,36 @@ namespace Frozen { } +EmptyRecord EmptyRecord::from_json(const rapidjson::Value & doc) { + EmptyRecord record; + if (!doc.IsObject()) { + throw AtdException("Expected an object"); + } + return record; +} +EmptyRecord EmptyRecord::from_json_string(const std::string &s) { + rapidjson::Document doc; + doc.Parse(s.c_str()); + if (doc.HasParseError()) { + throw AtdException("Failed to parse JSON"); + } + return from_json(doc); +} +void EmptyRecord::to_json(const EmptyRecord &t, rapidjson::Writer &writer) { + writer.StartObject(); + writer.EndObject(); +} +std::string EmptyRecord::to_json_string(const EmptyRecord &t) { + rapidjson::StringBuffer buffer; + rapidjson::Writer writer(buffer); + to_json(t, writer); + return buffer.GetString(); +} +std::string EmptyRecord::to_json_string() { + return to_json_string(*this); +} + + DefaultList DefaultList::from_json(const rapidjson::Value & doc) { DefaultList record; if (!doc.IsObject()) { @@ -1193,8 +1300,10 @@ DefaultList DefaultList::from_json_string(const std::string &s) { } void DefaultList::to_json(const DefaultList &t, rapidjson::Writer &writer) { writer.StartObject(); - writer.Key("items"); - _atd_write_array([](auto v, auto &w){_atd_write_int(v, w);}, t.items, writer); + if (t.items != std::vector({})) { + writer.Key("items"); + _atd_write_array([](auto v, auto &w){_atd_write_int(v, w);}, t.items, writer); + } writer.EndObject(); } std::string DefaultList::to_json_string(const DefaultList &t) { diff --git a/atdcpp/test/cpp-expected/everything_atd.hpp b/atdcpp/test/cpp-expected/everything_atd.hpp index 7e5b6f0c2..74b5196cf 100644 --- a/atdcpp/test/cpp-expected/everything_atd.hpp +++ b/atdcpp/test/cpp-expected/everything_atd.hpp @@ -17,6 +17,7 @@ #include #include #include +#include #include @@ -40,10 +41,12 @@ struct IntFloatParametrizedRecord; struct Root; struct RequireField; struct RecordWithWrappedType; +struct NullOpt; namespace Frozen::Types { struct A; struct B; } +struct EmptyRecord; struct DefaultList; struct Credential; struct Credentials; @@ -58,6 +61,8 @@ namespace typedefs { typedef Root Root; typedef RequireField RequireField; typedef RecordWithWrappedType RecordWithWrappedType; + typedef NullOpt NullOpt; + typedef EmptyRecord EmptyRecord; typedef DefaultList DefaultList; typedef Credential Credential; typedef Credentials Credentials; @@ -246,8 +251,8 @@ struct Root { bool await; int integer; float x___init__; - float float_with_auto_default = 0.0; - float float_with_default = 0.1; + float float_with_auto_default = 0.0f; + float float_with_default = 0.1f; std::vector> items; std::optional maybe; std::vector extras = {}; @@ -314,6 +319,22 @@ namespace Pair { } +struct NullOpt { + int a; + std::optional b; + std::optional c; + std::optional f; + std::optional h = 3; + std::optional i = 3; + + static NullOpt from_json(const rapidjson::Value & doc); + static NullOpt from_json_string(const std::string &s); + static void to_json(const NullOpt &t, rapidjson::Writer &writer); + static std::string to_json_string(const NullOpt &t); + std::string to_json_string(); +}; + + namespace Frozen { namespace Types { // Original type: frozen = [ ... | A | ... ] @@ -335,6 +356,16 @@ namespace Frozen { } +struct EmptyRecord { + + static EmptyRecord from_json(const rapidjson::Value & doc); + static EmptyRecord from_json_string(const std::string &s); + static void to_json(const EmptyRecord &t, rapidjson::Writer &writer); + static std::string to_json_string(const EmptyRecord &t); + std::string to_json_string(); +}; + + struct DefaultList { std::vector items = {}; diff --git a/atdcpp/test/cpp-tests/test_atdd.cpp b/atdcpp/test/cpp-tests/test_atdd.cpp index 979d6e308..d88525c54 100644 --- a/atdcpp/test/cpp-tests/test_atdd.cpp +++ b/atdcpp/test/cpp-tests/test_atdd.cpp @@ -107,6 +107,42 @@ int main() { } }; + tests["optional nullable"] = []() { + std::optional x{3}; + std::optional xx{std::nullopt}; + + auto somes = typedefs::NullOpt{.a = 3, .b = x, .c = x, .f = x, .h = x, .i = x}; + auto nones = typedefs::NullOpt{.a = 0, .b = xx, .c = xx, .f = xx, .h = xx, .i = xx}; + + auto some_j = R"({"a":3,"b":["Some",3],"c":3,"f":3})"; + auto nones_j = R"({"a":0,"b":"None","c":null,"h":"None","i":null})"; + + if (NullOpt::from_json_string(some_j).to_json_string() == somes.to_json_string() && some_j == somes.to_json_string()) { + std::cout << "Test passed: optional nullable some" << std::endl; + } else { + throw std::runtime_error("check 1 is failed"); + } + + + if (NullOpt::from_json_string(nones_j).to_json_string() == nones.to_json_string() && nones_j == nones.to_json_string()) { + std::cout << "Test passed: optional nullable none" << std::endl; + } else { + throw std::runtime_error("check 2 is failed"); + } + }; + + tests["empty record"] = []() { + typedefs::EmptyRecord emptyRecord; + std::string json = "{}"; + typedefs::EmptyRecord emptyRecordFromJson = EmptyRecord::from_json_string(json); + + if (emptyRecord.to_json_string() == emptyRecordFromJson.to_json_string()) { + std::cout << "Test passed: empty record" << std::endl; + } else { + throw std::runtime_error("check is failed"); + } + }; + std::cout << "Running tests..." << std::endl; int passed = 0; From 3fd0e1559adb15e04b713e0adc545ffd883a711d Mon Sep 17 00:00:00 2001 From: Alexandre Bourquelot Date: Fri, 5 Apr 2024 15:52:16 +0800 Subject: [PATCH 32/47] atdcpp: add maybe unused to helper function and put them in anonymous namespace --- atdcpp/src/lib/Codegen.ml | 60 ++++++++++++++++++++------------------- 1 file changed, 31 insertions(+), 29 deletions(-) diff --git a/atdcpp/src/lib/Codegen.ml b/atdcpp/src/lib/Codegen.ml index ecd0bbbd2..233429bbe 100644 --- a/atdcpp/src/lib/Codegen.ml +++ b/atdcpp/src/lib/Codegen.ml @@ -222,6 +222,9 @@ let fixed_size_preamble atd_filename = namespace atd { +namespace // anonymous +{ + class AtdException : public std::exception { public: @@ -237,19 +240,19 @@ private: }; template -T _atd_missing_json_field(const std::string &type, const std::string &field) +[[maybe_unused]] T _atd_missing_json_field(const std::string &type, const std::string &field) { throw AtdException("Missing JSON field '" + field + "' in " + type); } -auto _atd_bad_json(const std::string &type, const rapidjson::Value &x) +[[maybe_unused]] auto _atd_bad_json(const std::string &type, const rapidjson::Value &x) { (void) x; return AtdException("Bad JSON for " + type); } // Reading an integer from JSON -int _atd_read_int(const rapidjson::Value &val) +[[maybe_unused]] int _atd_read_int(const rapidjson::Value &val) { if (!val.IsInt()) { @@ -258,7 +261,7 @@ int _atd_read_int(const rapidjson::Value &val) return val.GetInt(); } -bool _atd_read_bool(const rapidjson::Value &val) +[[maybe_unused]] bool _atd_read_bool(const rapidjson::Value &val) { if (!val.IsBool()) { @@ -268,7 +271,7 @@ bool _atd_read_bool(const rapidjson::Value &val) } // Reading a float from JSON -float _atd_read_float(const rapidjson::Value &val) +[[maybe_unused]] float _atd_read_float(const rapidjson::Value &val) { if (val.IsInt()) { @@ -286,7 +289,7 @@ float _atd_read_float(const rapidjson::Value &val) return val.GetFloat(); } -std::string _atd_read_string(const rapidjson::Value &val) +[[maybe_unused]] std::string _atd_read_string(const rapidjson::Value &val) { if (!val.IsString()) { @@ -296,7 +299,7 @@ std::string _atd_read_string(const rapidjson::Value &val) } template -auto _atd_read_array(F read_func, const rapidjson::Value &val) +[[maybe_unused]] auto _atd_read_array(F read_func, const rapidjson::Value &val) { using ResultType = typename std::invoke_result::type; @@ -315,7 +318,7 @@ auto _atd_read_array(F read_func, const rapidjson::Value &val) } template -auto _atd_read_object_to_tuple_list(F read_func, const rapidjson::Value &val) +[[maybe_unused]] auto _atd_read_object_to_tuple_list(F read_func, const rapidjson::Value &val) { using ResultType = typename std::invoke_result::type; @@ -334,7 +337,7 @@ auto _atd_read_object_to_tuple_list(F read_func, const rapidjson::Value &val) } template -auto _atd_read_array_to_assoc_dict(RK read_key_func, RV read_value_func, const rapidjson::Value &val) +[[maybe_unused]] auto _atd_read_array_to_assoc_dict(RK read_key_func, RV read_value_func, const rapidjson::Value &val) { using KeyType = typename std::invoke_result::type; using ValueType = typename std::invoke_result::type; @@ -359,7 +362,7 @@ auto _atd_read_array_to_assoc_dict(RK read_key_func, RV read_value_func, const r } template -auto _atd_read_object_to_assoc_array(F read_func, const rapidjson::Value &val) +[[maybe_unused]] auto _atd_read_object_to_assoc_array(F read_func, const rapidjson::Value &val) { using ResultType = typename std::invoke_result::type; @@ -378,7 +381,7 @@ auto _atd_read_object_to_assoc_array(F read_func, const rapidjson::Value &val) } template -auto _atd_read_nullable(F read_func, const rapidjson::Value &val) +[[maybe_unused]] auto _atd_read_nullable(F read_func, const rapidjson::Value &val) { if (val.IsNull()) { @@ -388,7 +391,7 @@ auto _atd_read_nullable(F read_func, const rapidjson::Value &val) } template -auto _atd_read_option(F read_func, const rapidjson::Value &val) +[[maybe_unused]] auto _atd_read_option(F read_func, const rapidjson::Value &val) { using ResultType = typename std::invoke_result::type; if (val.IsString() && std::string_view(val.GetString()) == "None") @@ -406,19 +409,19 @@ auto _atd_read_option(F read_func, const rapidjson::Value &val) } template -auto _atd_read_wrap(F read_func, W wrap_func, const rapidjson::Value &val) +[[maybe_unused]] auto _atd_read_wrap(F read_func, W wrap_func, const rapidjson::Value &val) { return wrap_func(read_func(val)); } template -std::shared_ptr _atd_wrap_shared_ptr(T val) +[[maybe_unused]] std::shared_ptr _atd_wrap_shared_ptr(T val) { return std::make_shared(val); } template -T _atd_unwrap_shared_ptr(const std::shared_ptr val) +[[maybe_unused]] T _atd_unwrap_shared_ptr(const std::shared_ptr val) { if (!val) { @@ -427,28 +430,28 @@ T _atd_unwrap_shared_ptr(const std::shared_ptr val) return *val; } -void _atd_write_int(int value, rapidjson::Writer& writer) +[[maybe_unused]] void _atd_write_int(int value, rapidjson::Writer& writer) { writer.Int(value); } -void _atd_write_bool(bool value, rapidjson::Writer& writer) +[[maybe_unused]] void _atd_write_bool(bool value, rapidjson::Writer& writer) { writer.Bool(value); } -void _atd_write_float(float value, rapidjson::Writer& writer) +[[maybe_unused]] void _atd_write_float(float value, rapidjson::Writer& writer) { writer.Double(value); } -void _atd_write_string(const std::string &value, rapidjson::Writer& writer) +[[maybe_unused]] void _atd_write_string(const std::string &value, rapidjson::Writer& writer) { writer.String(value.c_str()); } template -void _atd_write_array(F write_func, const V& values, rapidjson::Writer& writer) +[[maybe_unused]] void _atd_write_array(F write_func, const V& values, rapidjson::Writer& writer) { writer.StartArray(); for (const auto& value : values) @@ -459,7 +462,7 @@ void _atd_write_array(F write_func, const V& values, rapidjson::Writer -void _atd_write_tuple_list_to_object(F write_func, const V &values, rapidjson::Writer& writer) +[[maybe_unused]] void _atd_write_tuple_list_to_object(F write_func, const V &values, rapidjson::Writer& writer) { writer.StartObject(); for (const auto& value : values) @@ -471,7 +474,7 @@ void _atd_write_tuple_list_to_object(F write_func, const V &values, rapidjson::W } template -void _atd_write_assoc_dict_to_array(const Wk write_key_func, const Wv write_value_func, const Map &value_map, rapidjson::Writer& writer) +[[maybe_unused]] void _atd_write_assoc_dict_to_array(const Wk write_key_func, const Wv write_value_func, const Map &value_map, rapidjson::Writer& writer) { writer.StartArray(); for (const auto& pair : value_map) @@ -485,7 +488,7 @@ void _atd_write_assoc_dict_to_array(const Wk write_key_func, const Wv write_valu } template -void _atd_write_assoc_array_to_object(F write_func, const Map &value_map, rapidjson::Writer& writer) +[[maybe_unused]] void _atd_write_assoc_array_to_object(F write_func, const Map &value_map, rapidjson::Writer& writer) { writer.StartObject(); for (const auto& pair : value_map) @@ -498,7 +501,7 @@ void _atd_write_assoc_array_to_object(F write_func, const Map &value_map, rapidj template -void _atd_write_option(F write_func, const O &val, rapidjson::Writer& writer) +[[maybe_unused]] void _atd_write_option(F write_func, const O &val, rapidjson::Writer& writer) { if (val) { @@ -514,7 +517,7 @@ void _atd_write_option(F write_func, const O &val, rapidjson::Writer -void _atd_write_nullable(F write_func, const O &val, rapidjson::Writer& writer) +[[maybe_unused]] void _atd_write_nullable(F write_func, const O &val, rapidjson::Writer& writer) { if (val) { @@ -527,11 +530,11 @@ void _atd_write_nullable(F write_func, const O &val, rapidjson::Writer -void _atd_write_wrap(F write_func, W wrap_func, const T &val, rapidjson::Writer& writer) +[[maybe_unused]] void _atd_write_wrap(F write_func, W wrap_func, const T &val, rapidjson::Writer& writer) { write_func(wrap_func(val), writer); } - +} // anonymous namespace |} atd_filename atd_filename @@ -1406,9 +1409,8 @@ let to_cpp_file ~atd_filename (items : A.module_body) dst_path = Atd.Util.tsort items |> List.map (fun x -> Inline (definition_group ~atd_filename env Definition x)) in - let head = [Line (sprintf "#include \"%s_atd.hpp\"" (Filename.chop_extension atd_filename))] in let namespace_end = [Line "} // namespace atd"] in - Line (fixed_size_preamble atd_filename) :: head @ cpp_defs @ namespace_end + Line (fixed_size_preamble atd_filename) :: cpp_defs @ namespace_end |> double_spaced |> Indent.to_file ~indent:4 dst_path From 2526270f042f37de03cc5550f5c4dcd2fe3135be Mon Sep 17 00:00:00 2001 From: Alexandre Bourquelot Date: Fri, 5 Apr 2024 16:26:12 +0800 Subject: [PATCH 33/47] atdcpp: add better exception logging --- atdcpp/src/lib/Codegen.ml | 27 +++++++++++++++++++++++++-- 1 file changed, 25 insertions(+), 2 deletions(-) diff --git a/atdcpp/src/lib/Codegen.ml b/atdcpp/src/lib/Codegen.ml index 233429bbe..5b07088b1 100644 --- a/atdcpp/src/lib/Codegen.ml +++ b/atdcpp/src/lib/Codegen.ml @@ -225,6 +225,29 @@ namespace atd namespace // anonymous { +std::string _rapid_json_type_to_string(rapidjson::Type type) +{ + switch (type) + { + case rapidjson::kNullType: + return "null"; + case rapidjson::kFalseType: + return "false"; + case rapidjson::kTrueType: + return "true"; + case rapidjson::kObjectType: + return "object"; + case rapidjson::kArrayType: + return "array"; + case rapidjson::kStringType: + return "string"; + case rapidjson::kNumberType: + return "number"; + default: + return "unknown"; + } +} + class AtdException : public std::exception { public: @@ -247,8 +270,8 @@ template [[maybe_unused]] auto _atd_bad_json(const std::string &type, const rapidjson::Value &x) { - (void) x; - return AtdException("Bad JSON for " + type); + auto x_type = x.GetType(); + return AtdException("Bad JSON for " + type + " got type " + _rapid_json_type_to_string(x_type)); } // Reading an integer from JSON From f0a492eb3218c71cde7e20bae242e0fec7f890a9 Mon Sep 17 00:00:00 2001 From: Alexandre Bourquelot Date: Fri, 5 Apr 2024 16:48:40 +0800 Subject: [PATCH 34/47] atdcpp: dont forget to include optional in header --- atdcpp/src/lib/Codegen.ml | 1 + 1 file changed, 1 insertion(+) diff --git a/atdcpp/src/lib/Codegen.ml b/atdcpp/src/lib/Codegen.ml index 5b07088b1..c865ad3be 100644 --- a/atdcpp/src/lib/Codegen.ml +++ b/atdcpp/src/lib/Codegen.ml @@ -202,6 +202,7 @@ let fixed_size_preamble_header atd_filename = #include #include #include +#include |} atd_filename atd_filename From 398ff80c89fa75ca99762fae91c399c0c4032235 Mon Sep 17 00:00:00 2001 From: Alexandre Bourquelot Date: Fri, 5 Apr 2024 18:56:11 +0800 Subject: [PATCH 35/47] atdcpp: support cpp namespace definition --- atdcpp/src/lib/Codegen.ml | 25 ++++++++++++++----------- atdcpp/src/lib/Cpp_annot.ml | 7 +++++++ atdcpp/src/lib/Cpp_annot.mli | 3 +++ 3 files changed, 24 insertions(+), 11 deletions(-) diff --git a/atdcpp/src/lib/Codegen.ml b/atdcpp/src/lib/Codegen.ml index c865ad3be..c2aa60cc1 100644 --- a/atdcpp/src/lib/Codegen.ml +++ b/atdcpp/src/lib/Codegen.ml @@ -33,6 +33,7 @@ let annot_schema_cpp : Atd.Annot.schema_section = Type_expr, "templatize"; Field, "default"; Module_head, "include"; + Module_head, "namespace"; ] } @@ -206,7 +207,7 @@ let fixed_size_preamble_header atd_filename = |} atd_filename atd_filename -let fixed_size_preamble atd_filename = +let fixed_size_preamble atd_filename namespace_name = sprintf {| // Generated by atdcpp from type definitions in %s. // This implements classes for the types defined in '%s', providing @@ -220,7 +221,7 @@ let fixed_size_preamble atd_filename = #include "%s_atd.hpp" -namespace atd +namespace %s { namespace // anonymous @@ -564,6 +565,7 @@ template atd_filename atd_filename (Filename.chop_extension atd_filename) + namespace_name let not_implemented loc msg = @@ -1336,7 +1338,7 @@ let sum env loc name cases codegen_type = | Variant_typedef -> let type_list = List.map (fun (loc, orig_name, unique_name, an, opt_e) -> - (sprintf "atd::%s::Types::%s" (struct_name env name) (trans env orig_name)) + (sprintf "%s::Types::%s" (struct_name env name) (trans env orig_name)) ) cases |> String.concat ", " in @@ -1392,7 +1394,7 @@ let reserve_good_struct_names env (items: A.module_body) = (fun (Type (loc, (name, param, an), e)) -> ignore (struct_name env name)) items -let to_header_file ~atd_filename ~head (items : A.module_body) dst_path = +let to_header_file ~atd_filename ~head (items : A.module_body) dst_path namespace_name = let env = init_env () in reserve_good_struct_names env items; let head = List.map (fun s -> Line s) head in @@ -1415,8 +1417,8 @@ let to_header_file ~atd_filename ~head (items : A.module_body) dst_path = ] |> spaced)) ] @ [Line "} // namespace typedefs"; Line ""] in let cpp_declarations = get_lines_for Declaration in - let namespace_start = [Line (sprintf "namespace atd {")] in - let namespace_end = [Line "} // namespace atd"] in + let namespace_start = [Line (sprintf "namespace %s {" namespace_name)] in + let namespace_end = [Line (sprintf "} // namespace %s" namespace_name)] in Line (fixed_size_preamble_header atd_filename) :: Inline head :: (namespace_start |> double_spaced) @@ -1426,15 +1428,15 @@ let to_header_file ~atd_filename ~head (items : A.module_body) dst_path = @ namespace_end |> Indent.to_file ~indent:4 dst_path -let to_cpp_file ~atd_filename (items : A.module_body) dst_path = +let to_cpp_file ~atd_filename (items : A.module_body) dst_path namespace_name = let env = init_env () in reserve_good_struct_names env items; let cpp_defs = Atd.Util.tsort items |> List.map (fun x -> Inline (definition_group ~atd_filename env Definition x)) in - let namespace_end = [Line "} // namespace atd"] in - Line (fixed_size_preamble atd_filename) :: cpp_defs @ namespace_end + let namespace_end = [Line (sprintf "} // namespace %s" namespace_name)] in + Line (fixed_size_preamble atd_filename namespace_name) :: cpp_defs @ namespace_end |> double_spaced |> Indent.to_file ~indent:4 dst_path @@ -1464,5 +1466,6 @@ let run_file src_path = Cpp_annot.get_cpp_include (snd atd_head) |> List.map (sprintf "#include %s") in - to_header_file ~atd_filename:src_name ~head atd_module hpp_dst_path; - to_cpp_file ~atd_filename:src_name atd_module cpp_dst_path + let namespace_name = match Cpp_annot.get_cpp_namespace (snd atd_head) with | Some v -> sprintf "atd::%s" v | None -> "atd" in + to_header_file ~atd_filename:src_name ~head atd_module hpp_dst_path namespace_name; + to_cpp_file ~atd_filename:src_name atd_module cpp_dst_path namespace_name diff --git a/atdcpp/src/lib/Cpp_annot.ml b/atdcpp/src/lib/Cpp_annot.ml index 0b63c2fe6..fbb37156b 100644 --- a/atdcpp/src/lib/Cpp_annot.ml +++ b/atdcpp/src/lib/Cpp_annot.ml @@ -42,6 +42,13 @@ let get_cpp_include an : string list = ~field:"include" an +let get_cpp_namespace an : string option = + Atd.Annot.get_opt_field + ~parse:(fun s -> Some s) + ~sections:["cpp"] + ~field:"namespace" + an + let get_cpp_wrap loc an = let path = ["cpp"] in let module_ = diff --git a/atdcpp/src/lib/Cpp_annot.mli b/atdcpp/src/lib/Cpp_annot.mli index dc3b238c5..50a94cf77 100644 --- a/atdcpp/src/lib/Cpp_annot.mli +++ b/atdcpp/src/lib/Cpp_annot.mli @@ -30,6 +30,9 @@ val get_cpp_assoc_repr : Atd.Annot.t -> assoc_repr cpp file such as include. *) val get_cpp_include : Atd.Annot.t -> string list +(** Return the namespace that needs to be used for the types defined in the + ATD file. *) +val get_cpp_namespace : Atd.Annot.t -> string option type atd_cpp_wrap = { From e6e57584140353f415fe08ba7ae662a04f685fd8 Mon Sep 17 00:00:00 2001 From: Alexandre Bourquelot Date: Sat, 6 Apr 2024 17:29:06 +0800 Subject: [PATCH 36/47] atdcpp: update test expected --- atdcpp/test/cpp-expected/everything_atd.cpp | 87 +++++++++++++-------- atdcpp/test/cpp-expected/everything_atd.hpp | 7 +- atdcpp/test/cpp-tests/everything_atd.cpp | 1 + atdcpp/test/cpp-tests/everything_atd.hpp | 1 + 4 files changed, 61 insertions(+), 35 deletions(-) create mode 120000 atdcpp/test/cpp-tests/everything_atd.cpp create mode 120000 atdcpp/test/cpp-tests/everything_atd.hpp diff --git a/atdcpp/test/cpp-expected/everything_atd.cpp b/atdcpp/test/cpp-expected/everything_atd.cpp index dd19e53c6..c11fffdd2 100644 --- a/atdcpp/test/cpp-expected/everything_atd.cpp +++ b/atdcpp/test/cpp-expected/everything_atd.cpp @@ -14,6 +14,32 @@ namespace atd { +namespace // anonymous +{ + +std::string _rapid_json_type_to_string(rapidjson::Type type) +{ + switch (type) + { + case rapidjson::kNullType: + return "null"; + case rapidjson::kFalseType: + return "false"; + case rapidjson::kTrueType: + return "true"; + case rapidjson::kObjectType: + return "object"; + case rapidjson::kArrayType: + return "array"; + case rapidjson::kStringType: + return "string"; + case rapidjson::kNumberType: + return "number"; + default: + return "unknown"; + } +} + class AtdException : public std::exception { public: @@ -29,19 +55,19 @@ class AtdException : public std::exception }; template -T _atd_missing_json_field(const std::string &type, const std::string &field) +[[maybe_unused]] T _atd_missing_json_field(const std::string &type, const std::string &field) { throw AtdException("Missing JSON field '" + field + "' in " + type); } -auto _atd_bad_json(const std::string &type, const rapidjson::Value &x) +[[maybe_unused]] auto _atd_bad_json(const std::string &type, const rapidjson::Value &x) { - (void) x; - return AtdException("Bad JSON for " + type); + auto x_type = x.GetType(); + return AtdException("Bad JSON for " + type + " got type " + _rapid_json_type_to_string(x_type)); } // Reading an integer from JSON -int _atd_read_int(const rapidjson::Value &val) +[[maybe_unused]] int _atd_read_int(const rapidjson::Value &val) { if (!val.IsInt()) { @@ -50,7 +76,7 @@ int _atd_read_int(const rapidjson::Value &val) return val.GetInt(); } -bool _atd_read_bool(const rapidjson::Value &val) +[[maybe_unused]] bool _atd_read_bool(const rapidjson::Value &val) { if (!val.IsBool()) { @@ -60,7 +86,7 @@ bool _atd_read_bool(const rapidjson::Value &val) } // Reading a float from JSON -float _atd_read_float(const rapidjson::Value &val) +[[maybe_unused]] float _atd_read_float(const rapidjson::Value &val) { if (val.IsInt()) { @@ -78,7 +104,7 @@ float _atd_read_float(const rapidjson::Value &val) return val.GetFloat(); } -std::string _atd_read_string(const rapidjson::Value &val) +[[maybe_unused]] std::string _atd_read_string(const rapidjson::Value &val) { if (!val.IsString()) { @@ -88,7 +114,7 @@ std::string _atd_read_string(const rapidjson::Value &val) } template -auto _atd_read_array(F read_func, const rapidjson::Value &val) +[[maybe_unused]] auto _atd_read_array(F read_func, const rapidjson::Value &val) { using ResultType = typename std::invoke_result::type; @@ -107,7 +133,7 @@ auto _atd_read_array(F read_func, const rapidjson::Value &val) } template -auto _atd_read_object_to_tuple_list(F read_func, const rapidjson::Value &val) +[[maybe_unused]] auto _atd_read_object_to_tuple_list(F read_func, const rapidjson::Value &val) { using ResultType = typename std::invoke_result::type; @@ -126,7 +152,7 @@ auto _atd_read_object_to_tuple_list(F read_func, const rapidjson::Value &val) } template -auto _atd_read_array_to_assoc_dict(RK read_key_func, RV read_value_func, const rapidjson::Value &val) +[[maybe_unused]] auto _atd_read_array_to_assoc_dict(RK read_key_func, RV read_value_func, const rapidjson::Value &val) { using KeyType = typename std::invoke_result::type; using ValueType = typename std::invoke_result::type; @@ -151,7 +177,7 @@ auto _atd_read_array_to_assoc_dict(RK read_key_func, RV read_value_func, const r } template -auto _atd_read_object_to_assoc_array(F read_func, const rapidjson::Value &val) +[[maybe_unused]] auto _atd_read_object_to_assoc_array(F read_func, const rapidjson::Value &val) { using ResultType = typename std::invoke_result::type; @@ -170,7 +196,7 @@ auto _atd_read_object_to_assoc_array(F read_func, const rapidjson::Value &val) } template -auto _atd_read_nullable(F read_func, const rapidjson::Value &val) +[[maybe_unused]] auto _atd_read_nullable(F read_func, const rapidjson::Value &val) { if (val.IsNull()) { @@ -180,7 +206,7 @@ auto _atd_read_nullable(F read_func, const rapidjson::Value &val) } template -auto _atd_read_option(F read_func, const rapidjson::Value &val) +[[maybe_unused]] auto _atd_read_option(F read_func, const rapidjson::Value &val) { using ResultType = typename std::invoke_result::type; if (val.IsString() && std::string_view(val.GetString()) == "None") @@ -198,19 +224,19 @@ auto _atd_read_option(F read_func, const rapidjson::Value &val) } template -auto _atd_read_wrap(F read_func, W wrap_func, const rapidjson::Value &val) +[[maybe_unused]] auto _atd_read_wrap(F read_func, W wrap_func, const rapidjson::Value &val) { return wrap_func(read_func(val)); } template -std::shared_ptr _atd_wrap_shared_ptr(T val) +[[maybe_unused]] std::shared_ptr _atd_wrap_shared_ptr(T val) { return std::make_shared(val); } template -T _atd_unwrap_shared_ptr(const std::shared_ptr val) +[[maybe_unused]] T _atd_unwrap_shared_ptr(const std::shared_ptr val) { if (!val) { @@ -219,28 +245,28 @@ T _atd_unwrap_shared_ptr(const std::shared_ptr val) return *val; } -void _atd_write_int(int value, rapidjson::Writer& writer) +[[maybe_unused]] void _atd_write_int(int value, rapidjson::Writer& writer) { writer.Int(value); } -void _atd_write_bool(bool value, rapidjson::Writer& writer) +[[maybe_unused]] void _atd_write_bool(bool value, rapidjson::Writer& writer) { writer.Bool(value); } -void _atd_write_float(float value, rapidjson::Writer& writer) +[[maybe_unused]] void _atd_write_float(float value, rapidjson::Writer& writer) { writer.Double(value); } -void _atd_write_string(const std::string &value, rapidjson::Writer& writer) +[[maybe_unused]] void _atd_write_string(const std::string &value, rapidjson::Writer& writer) { writer.String(value.c_str()); } template -void _atd_write_array(F write_func, const V& values, rapidjson::Writer& writer) +[[maybe_unused]] void _atd_write_array(F write_func, const V& values, rapidjson::Writer& writer) { writer.StartArray(); for (const auto& value : values) @@ -251,7 +277,7 @@ void _atd_write_array(F write_func, const V& values, rapidjson::Writer -void _atd_write_tuple_list_to_object(F write_func, const V &values, rapidjson::Writer& writer) +[[maybe_unused]] void _atd_write_tuple_list_to_object(F write_func, const V &values, rapidjson::Writer& writer) { writer.StartObject(); for (const auto& value : values) @@ -263,7 +289,7 @@ void _atd_write_tuple_list_to_object(F write_func, const V &values, rapidjson::W } template -void _atd_write_assoc_dict_to_array(const Wk write_key_func, const Wv write_value_func, const Map &value_map, rapidjson::Writer& writer) +[[maybe_unused]] void _atd_write_assoc_dict_to_array(const Wk write_key_func, const Wv write_value_func, const Map &value_map, rapidjson::Writer& writer) { writer.StartArray(); for (const auto& pair : value_map) @@ -277,7 +303,7 @@ void _atd_write_assoc_dict_to_array(const Wk write_key_func, const Wv write_valu } template -void _atd_write_assoc_array_to_object(F write_func, const Map &value_map, rapidjson::Writer& writer) +[[maybe_unused]] void _atd_write_assoc_array_to_object(F write_func, const Map &value_map, rapidjson::Writer& writer) { writer.StartObject(); for (const auto& pair : value_map) @@ -290,7 +316,7 @@ void _atd_write_assoc_array_to_object(F write_func, const Map &value_map, rapidj template -void _atd_write_option(F write_func, const O &val, rapidjson::Writer& writer) +[[maybe_unused]] void _atd_write_option(F write_func, const O &val, rapidjson::Writer& writer) { if (val) { @@ -306,7 +332,7 @@ void _atd_write_option(F write_func, const O &val, rapidjson::Writer -void _atd_write_nullable(F write_func, const O &val, rapidjson::Writer& writer) +[[maybe_unused]] void _atd_write_nullable(F write_func, const O &val, rapidjson::Writer& writer) { if (val) { @@ -319,17 +345,14 @@ void _atd_write_nullable(F write_func, const O &val, rapidjson::Writer -void _atd_write_wrap(F write_func, W wrap_func, const T &val, rapidjson::Writer& writer) +[[maybe_unused]] void _atd_write_wrap(F write_func, W wrap_func, const T &val, rapidjson::Writer& writer) { write_func(wrap_func(val), writer); } - +} // anonymous namespace -#include "everything_atd.hpp" - - namespace RecursiveVariant::Types { diff --git a/atdcpp/test/cpp-expected/everything_atd.hpp b/atdcpp/test/cpp-expected/everything_atd.hpp index 74b5196cf..2dfc229d3 100644 --- a/atdcpp/test/cpp-expected/everything_atd.hpp +++ b/atdcpp/test/cpp-expected/everything_atd.hpp @@ -18,6 +18,7 @@ #include #include #include +#include #include @@ -67,9 +68,9 @@ namespace typedefs { typedef Credential Credential; typedef Credentials Credentials; - typedef std::variant RecursiveVariant; - typedef std::variant Kind; - typedef std::variant Frozen; + typedef std::variant RecursiveVariant; + typedef std::variant Kind; + typedef std::variant Frozen; typedef int St; typedef uint32_t Alias3; diff --git a/atdcpp/test/cpp-tests/everything_atd.cpp b/atdcpp/test/cpp-tests/everything_atd.cpp new file mode 120000 index 000000000..38fc1cfcd --- /dev/null +++ b/atdcpp/test/cpp-tests/everything_atd.cpp @@ -0,0 +1 @@ +../../../_build/default/atdcpp/test/cpp-tests/everything_atd.cpp \ No newline at end of file diff --git a/atdcpp/test/cpp-tests/everything_atd.hpp b/atdcpp/test/cpp-tests/everything_atd.hpp new file mode 120000 index 000000000..868fa36e4 --- /dev/null +++ b/atdcpp/test/cpp-tests/everything_atd.hpp @@ -0,0 +1 @@ +../../../_build/default/atdcpp/test/cpp-tests/everything_atd.hpp \ No newline at end of file From 5ad2f51052965704de29e76b8078faea2865ea0f Mon Sep 17 00:00:00 2001 From: Alexandre Bourquelot Date: Mon, 8 Apr 2024 11:58:48 +0800 Subject: [PATCH 37/47] atdcpp: add enum storage --- atdcpp/src/lib/Codegen.ml | 122 +++++++++++++++++++- atdcpp/src/lib/Cpp_annot.ml | 16 +++ atdcpp/src/lib/Cpp_annot.mli | 15 +++ atdcpp/test/atd-input/everything.atd | 8 ++ atdcpp/test/cpp-expected/everything_atd.cpp | 46 +++++++- atdcpp/test/cpp-expected/everything_atd.hpp | 20 +++- atdcpp/test/cpp-tests/everything_atd.cpp | 1 - atdcpp/test/cpp-tests/everything_atd.hpp | 1 - atdcpp/test/cpp-tests/test_atdd.cpp | 3 +- 9 files changed, 221 insertions(+), 11 deletions(-) delete mode 120000 atdcpp/test/cpp-tests/everything_atd.cpp delete mode 120000 atdcpp/test/cpp-tests/everything_atd.hpp diff --git a/atdcpp/src/lib/Codegen.ml b/atdcpp/src/lib/Codegen.ml index c2aa60cc1..fc08bb896 100644 --- a/atdcpp/src/lib/Codegen.ml +++ b/atdcpp/src/lib/Codegen.ml @@ -1167,7 +1167,7 @@ let case_class env type_name (loc, orig_name, unique_name, an, opt_e) case_clas -let read_cases0 env loc name cases0 = +let read_cases0 env loc name cases0 sum_repr = let ifs = cases0 |> List.map (fun (loc, orig_name, unique_name, an, opt_e) -> @@ -1175,7 +1175,7 @@ let read_cases0 env loc name cases0 = Inline [ Line (sprintf "if (std::string_view(x.GetString()) == \"%s\") " (single_esc json_name)); Block [ - Line (sprintf "return Types::%s();" (trans env orig_name)) + Line (sprintf "return Types::%s%s;" (trans env orig_name) (match sum_repr with | Cpp_annot.Variant -> "()" | Cpp_annot.Enum -> "")) ]; ] ) @@ -1223,7 +1223,7 @@ let sum_container env loc name cases codegen_type = if cases0 <> [] then [ Line "if (x.IsString()) {"; - Block (read_cases0 env loc name cases0); + Block (read_cases0 env loc name cases0 Cpp_annot.Variant); Line "}"; ] else @@ -1345,13 +1345,127 @@ let sum env loc name cases codegen_type = [Line(sprintf "typedef %s %s;" (sprintf "std::variant<%s>" type_list) (struct_name env name))] | _ -> [] + +let enum_container env loc name cases codegen_type = + let cpp_struct_name = struct_name env name in + let cases0, cases1 = + List.partition (fun (loc, orig_name, unique_name, an, opt_e) -> + opt_e = None + ) cases + in + let cases0_block = + if cases0 <> [] then + [ + Line "if (x.IsString()) {"; + Block (read_cases0 env loc name cases0 Cpp_annot.Enum); + Line "}"; + ] + else + [] + in + let cases1_block = + if cases1 <> [] then + error_at loc "enums with parameters are not supported" + else + [] + in + match codegen_type with + | Declaration -> + [ + Line (sprintf "typedefs::%s from_json(const rapidjson::Value &x);" (cpp_struct_name)); + Inline (from_json_string_declaration (sprintf "typedefs::%s" cpp_struct_name) false); + Line (sprintf "void to_json(const typedefs::%s &x, rapidjson::Writer &writer);" (cpp_struct_name)); + Line (sprintf "std::string to_json_string(const typedefs::%s &x);" (cpp_struct_name)); + ] + | Definition -> + [ + Line (sprintf "namespace %s {" (cpp_struct_name)); + Block [ + Line (sprintf "typedefs::%s from_json(const rapidjson::Value &x) {" (cpp_struct_name)); + Block [ + Inline cases0_block; + Inline cases1_block; + Line (sprintf "throw _atd_bad_json(\"%s\", x);" + (single_esc (struct_name env name))) + ]; + Line "}"; + ]; + Block [Inline (from_json_string_definition (sprintf "typedefs::%s" cpp_struct_name) (None))]; + Block [ + Line (sprintf "void to_json(const typedefs::%s &x, rapidjson::Writer &writer) {" (cpp_struct_name)); + Block [ + Line "switch (x) {"; + Block ( + List.map (fun (loc, orig_name, unique_name, an, opt_e) -> + Line (sprintf "case Types::%s: _atd_write_string(\"%s\", writer); break;" (trans env orig_name) (trans env orig_name)) + ) cases + ); + Line ("}"); + ]; + Line ("}"); + ]; + Block [Line (sprintf "std::string to_json_string(const typedefs::%s &x) {" (cpp_struct_name)); + Block [ + Line ("rapidjson::StringBuffer buffer;"); + Line ("rapidjson::Writer writer(buffer);"); + Line ("to_json(x, writer);"); + Line "return buffer.GetString();" + ]; + Line "}";]; + Line ("}"); + ] + | _ -> [] + +let enum env loc name cases codegen_type = + let cases = + List.map (fun (x : variant) -> + match x with + | Variant (loc, (orig_name, an), opt_e) -> + let unique_name = create_struct_name env orig_name in + (loc, orig_name, unique_name, an, opt_e) + | Inherit _ -> assert false + ) cases + in + (* let case_classes = + List.map (fun x -> Inline (case_class env name x codegen_type)) cases + in *) + let container_class = enum_container env loc name cases codegen_type in + match codegen_type with + | Declaration -> + [ + Line (sprintf "namespace %s {" (struct_name env name)); + Block [ + Line (sprintf "typedef typedefs::%s Types;" (struct_name env name)); + Line (""); + Inline container_class; + ]; + Line (sprintf "}"); + ] + | Definition -> + [ + Inline container_class; + ] |> double_spaced + | Struct_typedef -> + let type_list = + List.map (fun (loc, orig_name, unique_name, an, opt_e) -> + Line (sprintf "%s," (trans env orig_name))) cases in + [ + Line (sprintf "enum class %s {" (struct_name env name)); + Block type_list; + Line (sprintf "};"); + ] + | _ -> [] + + let type_def env ((loc, (name, param, an), e) : A.type_def) codegen_type : B.t = if param <> [] then not_implemented loc "parametrized type"; let unwrap e = match e with | Sum (loc, cases, an) -> - sum env loc name cases codegen_type + (match (Cpp_annot.get_cpp_sumtype_repr an) with + | Variant -> sum env loc name cases codegen_type + | Enum -> enum env loc name cases codegen_type) | Record (loc, fields, an) -> record env loc name fields an codegen_type | Tuple _ diff --git a/atdcpp/src/lib/Cpp_annot.ml b/atdcpp/src/lib/Cpp_annot.ml index fbb37156b..7edf05050 100644 --- a/atdcpp/src/lib/Cpp_annot.ml +++ b/atdcpp/src/lib/Cpp_annot.ml @@ -8,6 +8,10 @@ type assoc_repr = | List | Dict +type sumtype_repr = + | Variant + | Enum + type atd_cpp_wrap = { cpp_wrap_t : string; cpp_wrap : string; @@ -22,6 +26,18 @@ let get_cpp_default an : string option = ~field:"default" an +let get_cpp_sumtype_repr an : sumtype_repr = + Atd.Annot.get_field + ~parse:(function + | "variant" -> Some Variant + | "enum" -> Some Enum + | _ -> None + ) + ~default:Variant + ~sections:["cpp"] + ~field:"repr" + an + let get_cpp_assoc_repr an : assoc_repr = Atd.Annot.get_field ~parse:(function diff --git a/atdcpp/src/lib/Cpp_annot.mli b/atdcpp/src/lib/Cpp_annot.mli index 50a94cf77..152c1d90f 100644 --- a/atdcpp/src/lib/Cpp_annot.mli +++ b/atdcpp/src/lib/Cpp_annot.mli @@ -19,6 +19,21 @@ type assoc_repr = | List | Dict +(** Whether a sum type must be represented in cpp as a variant or as an enum. + This is independent of the JSON representation. +*) +type sumtype_repr = + | Variant + | Enum + +(** Inspection of annotations placed on sum types such as + [type foo = A | B | C ]. + Permissible values for the [repr] field are ["enum"] and ["variant"]. + The default is ["variant"]. +*) +val get_cpp_sumtype_repr : Atd.Annot.t -> sumtype_repr + + (** Inspect annotations placed on lists of pairs such as [(string * foo) list ]. Permissible values for the [repr] field are ["dict"] and ["list"]. diff --git a/atdcpp/test/atd-input/everything.atd b/atdcpp/test/atd-input/everything.atd index 20331c8f5..b7a720b75 100644 --- a/atdcpp/test/atd-input/everything.atd +++ b/atdcpp/test/atd-input/everything.atd @@ -1,4 +1,5 @@ + type kind = [ | Root (* class name conflict *) @@ -12,6 +13,12 @@ type frozen = [ | B of int ] +type enum_sumtype = [ + | A + | B + | C +] + type ('a, 'b) parametrized_record = { field_a: 'a; ~field_b: 'b list; @@ -46,6 +53,7 @@ type root = { wrapped: st wrap ; aaa: alias_of_alias_of_alias; item: string wrap ; + ee : enum_sumtype; } diff --git a/atdcpp/test/cpp-expected/everything_atd.cpp b/atdcpp/test/cpp-expected/everything_atd.cpp index c11fffdd2..61d655e04 100644 --- a/atdcpp/test/cpp-expected/everything_atd.cpp +++ b/atdcpp/test/cpp-expected/everything_atd.cpp @@ -11,7 +11,7 @@ #include "everything_atd.hpp" -namespace atd +namespace atd::my::custom::ns { namespace // anonymous @@ -672,6 +672,43 @@ namespace Kind { } +namespace EnumSumtype { + typedefs::EnumSumtype from_json(const rapidjson::Value &x) { + if (x.IsString()) { + if (std::string_view(x.GetString()) == "A") + return Types::A; + if (std::string_view(x.GetString()) == "B") + return Types::B; + if (std::string_view(x.GetString()) == "C") + return Types::C; + throw _atd_bad_json("EnumSumtype", x); + } + throw _atd_bad_json("EnumSumtype", x); + } + typedefs::EnumSumtype from_json_string(const std::string &s) { + rapidjson::Document doc; + doc.Parse(s.c_str()); + if (doc.HasParseError()) { + throw AtdException("Failed to parse JSON"); + } + return from_json(doc); + } + void to_json(const typedefs::EnumSumtype &x, rapidjson::Writer &writer) { + switch (x) { + case Types::A: _atd_write_string("A", writer); break; + case Types::B: _atd_write_string("B", writer); break; + case Types::C: _atd_write_string("C", writer); break; + } + } + std::string to_json_string(const typedefs::EnumSumtype &x) { + rapidjson::StringBuffer buffer; + rapidjson::Writer writer(buffer); + to_json(x, writer); + return buffer.GetString(); + } +} + + namespace Alias3 { typedefs::Alias3 from_json(const rapidjson::Value &doc) { return _atd_read_wrap([](const auto& v){return _atd_read_int(v);}, [](const auto &e){return static_cast(e);},doc); @@ -930,6 +967,9 @@ Root Root::from_json(const rapidjson::Value & doc) { if (doc.HasMember("item")) record.item = _atd_read_wrap([](const auto& v){return _atd_read_string(v);}, [](const auto &e){return std::stoi(e);},doc["item"]); else record.item = _atd_missing_json_field("Root", "item"); + if (doc.HasMember("ee")) + record.ee = EnumSumtype::from_json(doc["ee"]); + else record.ee = _atd_missing_json_field("Root", "ee"); return record; } Root Root::from_json_string(const std::string &s) { @@ -1010,6 +1050,8 @@ void Root::to_json(const Root &t, rapidjson::Writer &wr AliasOfAliasOfAlias::to_json(t.aaa, writer); writer.Key("item"); _atd_write_wrap([](const auto &v, auto &w){_atd_write_string(v, w);}, [](const auto &e){return std::to_string(e);}, t.item, writer); + writer.Key("ee"); + EnumSumtype::to_json(t.ee, writer); writer.EndObject(); } std::string Root::to_json_string(const Root &t) { @@ -1487,4 +1529,4 @@ namespace Alias2 { } -} // namespace atd +} // namespace atd::my::custom::ns diff --git a/atdcpp/test/cpp-expected/everything_atd.hpp b/atdcpp/test/cpp-expected/everything_atd.hpp index 2dfc229d3..99fea3f01 100644 --- a/atdcpp/test/cpp-expected/everything_atd.hpp +++ b/atdcpp/test/cpp-expected/everything_atd.hpp @@ -22,7 +22,7 @@ #include -namespace atd { +namespace atd::my::custom::ns { // forward declarations namespace RecursiveVariant::Types { struct Integer; @@ -58,6 +58,11 @@ namespace typedefs { typedef RecursiveClass RecursiveClass; typedef ThreeLevelNestedListRecord ThreeLevelNestedListRecord; typedef StructWithRecursiveVariant StructWithRecursiveVariant; + enum class EnumSumtype { + A, + B, + C, + }; typedef IntFloatParametrizedRecord IntFloatParametrizedRecord; typedef Root Root; typedef RequireField RequireField; @@ -195,6 +200,16 @@ namespace Kind { } +namespace EnumSumtype { + typedef typedefs::EnumSumtype Types; + + typedefs::EnumSumtype from_json(const rapidjson::Value &x); + typedefs::EnumSumtype from_json_string(const std::string &s); + void to_json(const typedefs::EnumSumtype &x, rapidjson::Writer &writer); + std::string to_json_string(const typedefs::EnumSumtype &x); +} + + namespace Alias3 { typedefs::Alias3 from_json(const rapidjson::Value &doc); typedefs::Alias3 from_json_string(const std::string &s); @@ -273,6 +288,7 @@ struct Root { uint16_t wrapped; typedefs::AliasOfAliasOfAlias aaa; int item; + typedefs::EnumSumtype ee; static Root from_json(const rapidjson::Value & doc); static Root from_json_string(const std::string &s); @@ -423,4 +439,4 @@ namespace Alias2 { void to_json(const typedefs::Alias2 &t, rapidjson::Writer &writer); std::string to_json_string(const typedefs::Alias2 &t); } -} // namespace atd +} // namespace atd::my::custom::ns diff --git a/atdcpp/test/cpp-tests/everything_atd.cpp b/atdcpp/test/cpp-tests/everything_atd.cpp deleted file mode 120000 index 38fc1cfcd..000000000 --- a/atdcpp/test/cpp-tests/everything_atd.cpp +++ /dev/null @@ -1 +0,0 @@ -../../../_build/default/atdcpp/test/cpp-tests/everything_atd.cpp \ No newline at end of file diff --git a/atdcpp/test/cpp-tests/everything_atd.hpp b/atdcpp/test/cpp-tests/everything_atd.hpp deleted file mode 120000 index 868fa36e4..000000000 --- a/atdcpp/test/cpp-tests/everything_atd.hpp +++ /dev/null @@ -1 +0,0 @@ -../../../_build/default/atdcpp/test/cpp-tests/everything_atd.hpp \ No newline at end of file diff --git a/atdcpp/test/cpp-tests/test_atdd.cpp b/atdcpp/test/cpp-tests/test_atdd.cpp index d88525c54..7450c26a4 100644 --- a/atdcpp/test/cpp-tests/test_atdd.cpp +++ b/atdcpp/test/cpp-tests/test_atdd.cpp @@ -7,7 +7,7 @@ #include "everything_atd.hpp" int main() { - using namespace atd; + using namespace atd::my::custom::ns; std::map> tests; @@ -53,6 +53,7 @@ int main() { root.wrapped = 1; root.aaa = -90; root.item = 45; + root.ee = EnumSumtype::Types::B; std::string json = root.to_json_string(); Root rootFromJson = Root::from_json_string(json); From 877dabaf772648e41013c88d81a1f3b784cbc2d3 Mon Sep 17 00:00:00 2001 From: Alexandre Bourquelot Date: Tue, 9 Apr 2024 10:41:42 +0800 Subject: [PATCH 38/47] atdcpp: remove dlang mentions --- atdcpp/src/lib/Codegen.mli | 4 ++-- atdcpp/test/cpp-tests/dune | 5 +++-- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/atdcpp/src/lib/Codegen.mli b/atdcpp/src/lib/Codegen.mli index 180fb5efb..fc58e2554 100644 --- a/atdcpp/src/lib/Codegen.mli +++ b/atdcpp/src/lib/Codegen.mli @@ -1,7 +1,7 @@ (* - Dlang code generation for JSON support (no biniou support) + C++ code generation for JSON support (no biniou support) *) -(** Take ATD type definitions and translate them to Dlang, writing +(** Take ATD type definitions and translate them to C++, writing them out to a file which should have the '.d' extension. *) val run_file : string -> unit diff --git a/atdcpp/test/cpp-tests/dune b/atdcpp/test/cpp-tests/dune index f901f6354..5dfe283ed 100644 --- a/atdcpp/test/cpp-tests/dune +++ b/atdcpp/test/cpp-tests/dune @@ -1,5 +1,5 @@ ; -; Convert ATD -> Dlang +; Convert ATD -> C++ ; (rule (targets @@ -13,7 +13,8 @@ (run %{bin:atdcpp} %{deps}))) ; -; Compile and run the tests on the generated Dlang code. +; Compile and run the tests on the generated C++ code. +; Linking with rapidjson library ; (rule (alias runtest) From 7476df38daf7da3cef21eb3b46745fe79bc3c7ae Mon Sep 17 00:00:00 2001 From: Alexandre Bourquelot Date: Tue, 9 Apr 2024 17:42:40 +0800 Subject: [PATCH 39/47] atdcpp: call .empty() for lists when no user default is set --- atdcpp/src/lib/Codegen.ml | 34 +++++++++++++++------ atdcpp/test/cpp-expected/everything_atd.cpp | 6 ++-- 2 files changed, 27 insertions(+), 13 deletions(-) diff --git a/atdcpp/src/lib/Codegen.ml b/atdcpp/src/lib/Codegen.ml index fc08bb896..e541075fb 100644 --- a/atdcpp/src/lib/Codegen.ml +++ b/atdcpp/src/lib/Codegen.ml @@ -696,11 +696,16 @@ let rec get_default_default (e : type_expr) : string option = | Name _ -> None | Tvar _ -> None -let get_cpp_default (e : type_expr) (an : annot) : string option = + +type cpp_default = | User_default of string | Default_default of string | No_default + +let get_cpp_default (e : type_expr) (an : annot) : cpp_default = let user_default = Cpp_annot.get_cpp_default an in match user_default with - | Some s -> Some s - | None -> get_default_default e + | Some s -> User_default s + | None -> (match get_default_default e with + | Some s -> Default_default s + | None -> No_default) (* If the field is '?foo: bar option', its cpp or json value has type 'bar' rather than 'bar option'. *) @@ -777,13 +782,22 @@ and tuple_writer env (loc, cells, an) = }(" tuple_body +let get_is_set_to_default env an e field = + let cpp_default = get_cpp_default e an in + let cpp_default_str = match cpp_default with | User_default x | Default_default x -> x | No_default -> "" in + let default_format = sprintf "%s != %s(%s)" field (type_name_of_expr env e) cpp_default_str in + match e with + | List _ -> (match cpp_default with + | User_default _ -> default_format + | Default_default _ | No_default -> sprintf "!%s.empty()" field + ) + | _ -> default_format + let construct_json_field env trans_meth ((loc, (name, kind, an), e) : simple_field) = let unwrapped_type = unwrap_field_type loc name kind e in let writer_function n = json_writer ~nested:n env unwrapped_type in let cpp_var_name = inst_var_name trans_meth name in - let cpp_type_name = type_name_of_expr env unwrapped_type in - let cpp_default = match (get_cpp_default e an) with | Some x -> x | None -> "" in let assignement = [ Line (sprintf "writer.Key(\"%s\");" @@ -795,7 +809,7 @@ let construct_json_field env trans_meth | Required -> assignement | With_default -> [ - Line (sprintf "if (t.%s != %s(%s)) {" cpp_var_name cpp_type_name cpp_default); + Line (sprintf "if (%s) {" (get_is_set_to_default env an e (sprintf "t.%s" cpp_var_name))); Block assignement; Line "}" ] @@ -886,8 +900,8 @@ let from_json_class_argument | Optional -> (sprintf "std::nullopt") | With_default -> match get_cpp_default e an with - | Some x -> x - | None -> + | User_default x | Default_default x -> x + | No_default -> A.error_at loc (sprintf "missing default cpp value for field '%s'" name) @@ -920,8 +934,8 @@ let inst_var_declaration | Optional -> "" | With_default -> match get_cpp_default unwrapped_e an with - | None -> "" - | Some x -> sprintf " = %s" x + | No_default -> "" + | User_default x | Default_default x -> sprintf " = %s" x in [ Line (sprintf "%s %s%s;" type_name var_name default) diff --git a/atdcpp/test/cpp-expected/everything_atd.cpp b/atdcpp/test/cpp-expected/everything_atd.cpp index 61d655e04..e82607661 100644 --- a/atdcpp/test/cpp-expected/everything_atd.cpp +++ b/atdcpp/test/cpp-expected/everything_atd.cpp @@ -862,7 +862,7 @@ void IntFloatParametrizedRecord::to_json(const IntFloatParametrizedRecord &t, ra writer.StartObject(); writer.Key("field_a"); _atd_write_int(t.field_a, writer); - if (t.field_b != std::vector({})) { + if (!t.field_b.empty()) { writer.Key("field_b"); _atd_write_array([](auto v, auto &w){_atd_write_float(v, w);}, t.field_b, writer); } @@ -1004,7 +1004,7 @@ void Root::to_json(const Root &t, rapidjson::Writer &wr writer.Key("maybe"); _atd_write_int(t.maybe.value(), writer); } - if (t.extras != std::vector({})) { + if (!t.extras.empty()) { writer.Key("extras"); _atd_write_array([](auto v, auto &w){_atd_write_int(v, w);}, t.extras, writer); } @@ -1365,7 +1365,7 @@ DefaultList DefaultList::from_json_string(const std::string &s) { } void DefaultList::to_json(const DefaultList &t, rapidjson::Writer &writer) { writer.StartObject(); - if (t.items != std::vector({})) { + if (!t.items.empty()) { writer.Key("items"); _atd_write_array([](auto v, auto &w){_atd_write_int(v, w);}, t.items, writer); } From cefe4384041d7e612eb9aff17f966c2de457c314 Mon Sep 17 00:00:00 2001 From: Alexandre Bourquelot Date: Thu, 11 Apr 2024 10:49:54 +0800 Subject: [PATCH 40/47] atdcpp: better parsing error reporting --- atdcpp/src/lib/Codegen.ml | 20 +++++----- atdcpp/test/cpp-expected/everything_atd.cpp | 42 ++++++++++----------- 2 files changed, 32 insertions(+), 30 deletions(-) diff --git a/atdcpp/src/lib/Codegen.ml b/atdcpp/src/lib/Codegen.ml index e541075fb..2b2684df5 100644 --- a/atdcpp/src/lib/Codegen.ml +++ b/atdcpp/src/lib/Codegen.ml @@ -281,7 +281,7 @@ template { if (!val.IsInt()) { - throw AtdException("Expected an integer"); + throw AtdException("Expected an integer but got" + _rapid_json_type_to_string(val.GetType())); } return val.GetInt(); } @@ -290,7 +290,7 @@ template { if (!val.IsBool()) { - throw AtdException("Expected a boolean"); + throw AtdException("Expected a boolean but got" + _rapid_json_type_to_string(val.GetType())); } return val.GetBool(); } @@ -308,7 +308,7 @@ template } if (!val.IsFloat()) { - throw AtdException("Expected a float"); + throw AtdException("Expected a float but got" + _rapid_json_type_to_string(val.GetType())); } return val.GetFloat(); @@ -318,7 +318,7 @@ template { if (!val.IsString()) { - throw AtdException("Expected a string"); + throw AtdException("Expected a string but got" + _rapid_json_type_to_string(val.GetType())); } return val.GetString(); } @@ -330,7 +330,7 @@ template if (!val.IsArray()) { - throw AtdException("Expected an array"); // Or your specific exception type + throw AtdException("Expected an array but got" + _rapid_json_type_to_string(val.GetType())); } std::vector result; @@ -349,7 +349,7 @@ template if (!val.IsObject()) { - throw AtdException("Expected an object"); // Or your specific exception type + throw AtdException("Expected an object but got" + _rapid_json_type_to_string(val.GetType())); } std::vector> result; @@ -369,7 +369,7 @@ template if (!val.IsArray()) { - throw AtdException("Expected an array"); // Or your specific exception type + throw AtdException("Expected an array but got" + _rapid_json_type_to_string(val.GetType())); } std::map result; @@ -393,7 +393,7 @@ template if (!val.IsObject()) { - throw AtdException("Expected an object"); // Or your specific exception type + throw AtdException("Expected an object but got" + _rapid_json_type_to_string(val.GetType())); } std::map result; @@ -986,7 +986,9 @@ let record_definition env loc name fields an = Block [ Line (sprintf "%s record;" cpp_struct_name); Line "if (!doc.IsObject()) {"; - Block [Line "throw AtdException(\"Expected an object\");"]; + Block [ + Line (sprintf "throw AtdException(\"atdtype: %s, expected an object but got\" + _rapid_json_type_to_string(doc.GetType()));" name); + ]; Line "}"; Inline from_json_class_arguments; Line "return record;"; diff --git a/atdcpp/test/cpp-expected/everything_atd.cpp b/atdcpp/test/cpp-expected/everything_atd.cpp index e82607661..f07f412a2 100644 --- a/atdcpp/test/cpp-expected/everything_atd.cpp +++ b/atdcpp/test/cpp-expected/everything_atd.cpp @@ -71,7 +71,7 @@ template { if (!val.IsInt()) { - throw AtdException("Expected an integer"); + throw AtdException("Expected an integer but got" + _rapid_json_type_to_string(val.GetType())); } return val.GetInt(); } @@ -80,7 +80,7 @@ template { if (!val.IsBool()) { - throw AtdException("Expected a boolean"); + throw AtdException("Expected a boolean but got" + _rapid_json_type_to_string(val.GetType())); } return val.GetBool(); } @@ -98,7 +98,7 @@ template } if (!val.IsFloat()) { - throw AtdException("Expected a float"); + throw AtdException("Expected a float but got" + _rapid_json_type_to_string(val.GetType())); } return val.GetFloat(); @@ -108,7 +108,7 @@ template { if (!val.IsString()) { - throw AtdException("Expected a string"); + throw AtdException("Expected a string but got" + _rapid_json_type_to_string(val.GetType())); } return val.GetString(); } @@ -120,7 +120,7 @@ template if (!val.IsArray()) { - throw AtdException("Expected an array"); // Or your specific exception type + throw AtdException("Expected an array but got" + _rapid_json_type_to_string(val.GetType())); } std::vector result; @@ -139,7 +139,7 @@ template if (!val.IsObject()) { - throw AtdException("Expected an object"); // Or your specific exception type + throw AtdException("Expected an object but got" + _rapid_json_type_to_string(val.GetType())); } std::vector> result; @@ -159,7 +159,7 @@ template if (!val.IsArray()) { - throw AtdException("Expected an array"); // Or your specific exception type + throw AtdException("Expected an array but got" + _rapid_json_type_to_string(val.GetType())); } std::map result; @@ -183,7 +183,7 @@ template if (!val.IsObject()) { - throw AtdException("Expected an object"); // Or your specific exception type + throw AtdException("Expected an object but got" + _rapid_json_type_to_string(val.GetType())); } std::map result; @@ -414,7 +414,7 @@ namespace RecursiveVariant { RecursiveRecord2 RecursiveRecord2::from_json(const rapidjson::Value & doc) { RecursiveRecord2 record; if (!doc.IsObject()) { - throw AtdException("Expected an object"); + throw AtdException("atdtype: recursive_record2, expected an object but got" + _rapid_json_type_to_string(doc.GetType())); } if (doc.HasMember("id")) record.id = _atd_read_int(doc["id"]); @@ -459,7 +459,7 @@ std::string RecursiveRecord2::to_json_string() { RecursiveClass RecursiveClass::from_json(const rapidjson::Value & doc) { RecursiveClass record; if (!doc.IsObject()) { - throw AtdException("Expected an object"); + throw AtdException("atdtype: recursive_class, expected an object but got" + _rapid_json_type_to_string(doc.GetType())); } if (doc.HasMember("id")) record.id = _atd_read_int(doc["id"]); @@ -504,7 +504,7 @@ std::string RecursiveClass::to_json_string() { ThreeLevelNestedListRecord ThreeLevelNestedListRecord::from_json(const rapidjson::Value & doc) { ThreeLevelNestedListRecord record; if (!doc.IsObject()) { - throw AtdException("Expected an object"); + throw AtdException("atdtype: three_level_nested_list_record, expected an object but got" + _rapid_json_type_to_string(doc.GetType())); } if (doc.HasMember("field_a")) record.field_a = _atd_read_array([](const auto &v){return _atd_read_array([](const auto &v){return _atd_read_array([](const auto &v){return _atd_read_int(v);}, v);}, v);}, doc["field_a"]); @@ -539,7 +539,7 @@ std::string ThreeLevelNestedListRecord::to_json_string() { StructWithRecursiveVariant StructWithRecursiveVariant::from_json(const rapidjson::Value & doc) { StructWithRecursiveVariant record; if (!doc.IsObject()) { - throw AtdException("Expected an object"); + throw AtdException("atdtype: struct_with_recursive_variant, expected an object but got" + _rapid_json_type_to_string(doc.GetType())); } if (doc.HasMember("variant")) record.variant = RecursiveVariant::from_json(doc["variant"]); @@ -840,7 +840,7 @@ namespace KindParametrizedTuple { IntFloatParametrizedRecord IntFloatParametrizedRecord::from_json(const rapidjson::Value & doc) { IntFloatParametrizedRecord record; if (!doc.IsObject()) { - throw AtdException("Expected an object"); + throw AtdException("atdtype: _int_float_parametrized_record, expected an object but got" + _rapid_json_type_to_string(doc.GetType())); } if (doc.HasMember("field_a")) record.field_a = _atd_read_int(doc["field_a"]); @@ -882,7 +882,7 @@ std::string IntFloatParametrizedRecord::to_json_string() { Root Root::from_json(const rapidjson::Value & doc) { Root record; if (!doc.IsObject()) { - throw AtdException("Expected an object"); + throw AtdException("atdtype: root, expected an object but got" + _rapid_json_type_to_string(doc.GetType())); } if (doc.HasMember("ID")) record.id = _atd_read_string(doc["ID"]); @@ -1068,7 +1068,7 @@ std::string Root::to_json_string() { RequireField RequireField::from_json(const rapidjson::Value & doc) { RequireField record; if (!doc.IsObject()) { - throw AtdException("Expected an object"); + throw AtdException("atdtype: require_field, expected an object but got" + _rapid_json_type_to_string(doc.GetType())); } if (doc.HasMember("req")) record.req = _atd_read_string(doc["req"]); @@ -1103,7 +1103,7 @@ std::string RequireField::to_json_string() { RecordWithWrappedType RecordWithWrappedType::from_json(const rapidjson::Value & doc) { RecordWithWrappedType record; if (!doc.IsObject()) { - throw AtdException("Expected an object"); + throw AtdException("atdtype: record_with_wrapped_type, expected an object but got" + _rapid_json_type_to_string(doc.GetType())); } if (doc.HasMember("item")) record.item = _atd_read_wrap([](const auto& v){return _atd_read_string(v);}, [](const auto &e){return std::stoi(e);},doc["item"]); @@ -1194,7 +1194,7 @@ namespace Pair { NullOpt NullOpt::from_json(const rapidjson::Value & doc) { NullOpt record; if (!doc.IsObject()) { - throw AtdException("Expected an object"); + throw AtdException("atdtype: null_opt, expected an object but got" + _rapid_json_type_to_string(doc.GetType())); } if (doc.HasMember("a")) record.a = _atd_read_int(doc["a"]); @@ -1318,7 +1318,7 @@ namespace Frozen { EmptyRecord EmptyRecord::from_json(const rapidjson::Value & doc) { EmptyRecord record; if (!doc.IsObject()) { - throw AtdException("Expected an object"); + throw AtdException("atdtype: empty_record, expected an object but got" + _rapid_json_type_to_string(doc.GetType())); } return record; } @@ -1348,7 +1348,7 @@ std::string EmptyRecord::to_json_string() { DefaultList DefaultList::from_json(const rapidjson::Value & doc) { DefaultList record; if (!doc.IsObject()) { - throw AtdException("Expected an object"); + throw AtdException("atdtype: default_list, expected an object but got" + _rapid_json_type_to_string(doc.GetType())); } if (doc.HasMember("items")) record.items = _atd_read_array([](const auto &v){return _atd_read_int(v);}, doc["items"]); @@ -1385,7 +1385,7 @@ std::string DefaultList::to_json_string() { Credential Credential::from_json(const rapidjson::Value & doc) { Credential record; if (!doc.IsObject()) { - throw AtdException("Expected an object"); + throw AtdException("atdtype: credential, expected an object but got" + _rapid_json_type_to_string(doc.GetType())); } if (doc.HasMember("name")) record.name = _atd_read_string(doc["name"]); @@ -1449,7 +1449,7 @@ namespace Credentials2 { Credentials Credentials::from_json(const rapidjson::Value & doc) { Credentials record; if (!doc.IsObject()) { - throw AtdException("Expected an object"); + throw AtdException("atdtype: credentials, expected an object but got" + _rapid_json_type_to_string(doc.GetType())); } if (doc.HasMember("credentials")) record.credentials = _atd_read_array([](const auto &v){return Credential::from_json(v);}, doc["credentials"]); From 580e6fcd5cf3f7e951471e63cb0273963ad3c784 Mon Sep 17 00:00:00 2001 From: Alexandre Bourquelot Date: Fri, 3 May 2024 19:24:53 +0800 Subject: [PATCH 41/47] atdcpp: get rapidjson from apt --- .circleci/setup-system | 4 ++++ atdcpp/test/cpp-tests/dune | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/.circleci/setup-system b/.circleci/setup-system index 1eaab0b77..27ef2a5f6 100755 --- a/.circleci/setup-system +++ b/.circleci/setup-system @@ -25,6 +25,10 @@ sudo apt-get install -y \ # For JSON Schema and atdpy testing pip install jsonschema pytest mypy flake8 +# For atdcpp testing +sudo apt install -y \ + rapidjson-dev + # To get latest ldc version source $(curl https://dlang.org/install.sh | bash -s ldc -a) echo "source $(~/dlang/install.sh ldc -a)" >> "$BASH_ENV" diff --git a/atdcpp/test/cpp-tests/dune b/atdcpp/test/cpp-tests/dune index 5dfe283ed..70c6f72ef 100644 --- a/atdcpp/test/cpp-tests/dune +++ b/atdcpp/test/cpp-tests/dune @@ -23,6 +23,6 @@ (glob_files *.cpp)) (action (progn - (bash "g++ -I../../lib/rapidjson/include -std=c++20 %{deps} -o test") + (bash "g++ -I../../lib/rapidjson/include -I/usr/include/rapidjson -std=c++20 %{deps} -o test") (bash ./test) ))) From 6a987b5972e0e70b3ee00cfe5b240913970eaf7a Mon Sep 17 00:00:00 2001 From: Alexandre Bourquelot Date: Fri, 3 May 2024 19:28:20 +0800 Subject: [PATCH 42/47] atdcpp: add tests to makefile --- Makefile | 1 + 1 file changed, 1 insertion(+) diff --git a/Makefile b/Makefile index 474e47bc3..2dfb68b5c 100644 --- a/Makefile +++ b/Makefile @@ -51,6 +51,7 @@ test: $(MAKE) test-python $(MAKE) test-ts $(MAKE) test-d + $(MAKE) test-cpp # Test the OCaml code used by all the backends From 28153957e6067fb48c7ef7624cc32b2670e0116f Mon Sep 17 00:00:00 2001 From: Alexandre Bourquelot Date: Fri, 3 May 2024 19:40:05 +0800 Subject: [PATCH 43/47] atdcpp: tests on std=c++17 --- atdcpp/test/cpp-tests/dune | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/atdcpp/test/cpp-tests/dune b/atdcpp/test/cpp-tests/dune index 70c6f72ef..f131ca04e 100644 --- a/atdcpp/test/cpp-tests/dune +++ b/atdcpp/test/cpp-tests/dune @@ -23,6 +23,6 @@ (glob_files *.cpp)) (action (progn - (bash "g++ -I../../lib/rapidjson/include -I/usr/include/rapidjson -std=c++20 %{deps} -o test") + (bash "g++ -I../../lib/rapidjson/include -I/usr/include/rapidjson -std=c++17 %{deps} -o test") (bash ./test) ))) From ea577a6887d446d2f6be641adf4d07909dff92da Mon Sep 17 00:00:00 2001 From: Alexandre Bourquelot Date: Fri, 3 May 2024 19:59:28 +0800 Subject: [PATCH 44/47] atdcpp: add memory include --- atdcpp/src/lib/Codegen.ml | 1 + atdcpp/test/cpp-expected/everything_atd.hpp | 1 + atdcpp/test/cpp-tests/dune | 2 +- 3 files changed, 3 insertions(+), 1 deletion(-) diff --git a/atdcpp/src/lib/Codegen.ml b/atdcpp/src/lib/Codegen.ml index 2b2684df5..ac1a9fd88 100644 --- a/atdcpp/src/lib/Codegen.ml +++ b/atdcpp/src/lib/Codegen.ml @@ -203,6 +203,7 @@ let fixed_size_preamble_header atd_filename = #include #include #include +#include #include |} atd_filename atd_filename diff --git a/atdcpp/test/cpp-expected/everything_atd.hpp b/atdcpp/test/cpp-expected/everything_atd.hpp index 99fea3f01..ecca746ef 100644 --- a/atdcpp/test/cpp-expected/everything_atd.hpp +++ b/atdcpp/test/cpp-expected/everything_atd.hpp @@ -18,6 +18,7 @@ #include #include #include +#include #include diff --git a/atdcpp/test/cpp-tests/dune b/atdcpp/test/cpp-tests/dune index f131ca04e..15786cbc1 100644 --- a/atdcpp/test/cpp-tests/dune +++ b/atdcpp/test/cpp-tests/dune @@ -23,6 +23,6 @@ (glob_files *.cpp)) (action (progn - (bash "g++ -I../../lib/rapidjson/include -I/usr/include/rapidjson -std=c++17 %{deps} -o test") + (bash "g++ -I../../lib/rapidjson/include -std=c++17 %{deps} -o test") (bash ./test) ))) From 0c2e9e12a76e07cfd23e383185222ee5ffa5573d Mon Sep 17 00:00:00 2001 From: Alexandre Bourquelot Date: Fri, 3 May 2024 20:06:57 +0800 Subject: [PATCH 45/47] atdcpp: add required imports in test --- atdcpp/test/cpp-tests/test_atdd.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/atdcpp/test/cpp-tests/test_atdd.cpp b/atdcpp/test/cpp-tests/test_atdd.cpp index 7450c26a4..486705c90 100644 --- a/atdcpp/test/cpp-tests/test_atdd.cpp +++ b/atdcpp/test/cpp-tests/test_atdd.cpp @@ -4,6 +4,11 @@ #include #include #include +#include +#include +#include +#include + #include "everything_atd.hpp" int main() { From b62feb9ac91d353358e773cb604ea9dbb5ae8823 Mon Sep 17 00:00:00 2001 From: Alexandre Bourquelot Date: Fri, 3 May 2024 20:41:39 +0800 Subject: [PATCH 46/47] atdcpp: handle untyped things --- atdcpp/src/lib/Codegen.ml | 25 +++++++++++++++++++------ atdcpp/test/atd-input/everything.atd | 2 +- atdcpp/test/cpp-tests/test_atdd.cpp | 1 + 3 files changed, 21 insertions(+), 7 deletions(-) diff --git a/atdcpp/src/lib/Codegen.ml b/atdcpp/src/lib/Codegen.ml index ac1a9fd88..07c4fed50 100644 --- a/atdcpp/src/lib/Codegen.ml +++ b/atdcpp/src/lib/Codegen.ml @@ -324,6 +324,15 @@ template return val.GetString(); } +[[maybe_unused]] std::string _atd_read_abstract(const rapidjson::Value &val) +{ + // will convert val to string + rapidjson::StringBuffer buffer; + rapidjson::Writer writer(buffer); + val.Accept(writer); + return buffer.GetString(); +} + template [[maybe_unused]] auto _atd_read_array(F read_func, const rapidjson::Value &val) { @@ -476,6 +485,12 @@ template writer.String(value.c_str()); } +[[maybe_unused]] void _atd_write_abstract(const std::string &value, rapidjson::Writer& writer) +{ + // writes string value as raw json + writer.RawValue(value.c_str(), value.size(), rapidjson::kStringType); +} + template [[maybe_unused]] void _atd_write_array(F write_func, const V& values, rapidjson::Writer& writer) { @@ -620,7 +635,7 @@ let cpp_type_name env (name : string) = | "int" -> "int" | "float" -> "float" | "string" -> "std::string" - | "abstract" -> "rapidjson::Value" + | "abstract" -> "std::string" | user_defined -> let typename = (struct_name env user_defined) in typename @@ -632,7 +647,7 @@ let cpp_type_name_namespaced env (name : string) = | "int" -> "int" | "float" -> "float" | "string" -> "std::string" - | "abstract" -> "rapidjson::Value" + | "abstract" -> "std::string" | user_defined -> let typename = (struct_name env user_defined) in sprintf "%s::%s" "typedefs" typename @@ -762,8 +777,7 @@ let rec json_writer ?(nested=false) env e = ) | Name (loc, (loc2, name, []), an) -> (match name with - | "bool" | "int" | "float" | "string" -> sprintf "_atd_write_%s(" name - | "abstract" -> not_implemented loc "abstract" + | "bool" | "int" | "float" | "string" | "abstract" -> sprintf "_atd_write_%s(" name | _ -> let dtype_name = (cpp_type_name env name) in sprintf "%s::to_json(" dtype_name) | Name (loc, _, _) -> not_implemented loc "parametrized types" @@ -865,8 +879,7 @@ let rec json_reader ?(nested=false) env (e : type_expr) = ) | Name (loc, (loc2, name, []), an) -> (match name with - | "bool" | "int" | "float" | "string" -> sprintf "_atd_read_%s(" name - | "abstract" -> not_implemented loc "abstract" + | "bool" | "int" | "float" | "string" | "abstract" -> sprintf "_atd_read_%s(" name | _ -> sprintf "%s::from_json(" (struct_name env name) ) diff --git a/atdcpp/test/atd-input/everything.atd b/atdcpp/test/atd-input/everything.atd index b7a720b75..3717d8649 100644 --- a/atdcpp/test/atd-input/everything.atd +++ b/atdcpp/test/atd-input/everything.atd @@ -47,7 +47,7 @@ type root = { assoc4: (string * int) list ; nullables: int nullable list; options: int option list; - (* untyped_things: abstract list; *) + untyped_things: abstract list; parametrized_record: (int, float) parametrized_record; parametrized_tuple: kind parametrized_tuple; wrapped: st wrap ; diff --git a/atdcpp/test/cpp-tests/test_atdd.cpp b/atdcpp/test/cpp-tests/test_atdd.cpp index 486705c90..5eb976d82 100644 --- a/atdcpp/test/cpp-tests/test_atdd.cpp +++ b/atdcpp/test/cpp-tests/test_atdd.cpp @@ -59,6 +59,7 @@ int main() { root.aaa = -90; root.item = 45; root.ee = EnumSumtype::Types::B; + root.untyped_things = {R"({"objec1t":"value"})", R"({"object":[1,2,3]})"}; std::string json = root.to_json_string(); Root rootFromJson = Root::from_json_string(json); From 426eff755e724df6ef9ed907a0a18dda7ebb27c6 Mon Sep 17 00:00:00 2001 From: Alexandre Bourquelot Date: Fri, 3 May 2024 20:50:02 +0800 Subject: [PATCH 47/47] atdcpp: update expected files --- atdcpp/test/cpp-expected/everything_atd.cpp | 20 ++++++++++++++++++++ atdcpp/test/cpp-expected/everything_atd.hpp | 1 + 2 files changed, 21 insertions(+) diff --git a/atdcpp/test/cpp-expected/everything_atd.cpp b/atdcpp/test/cpp-expected/everything_atd.cpp index f07f412a2..72722a931 100644 --- a/atdcpp/test/cpp-expected/everything_atd.cpp +++ b/atdcpp/test/cpp-expected/everything_atd.cpp @@ -113,6 +113,15 @@ template return val.GetString(); } +[[maybe_unused]] std::string _atd_read_abstract(const rapidjson::Value &val) +{ + // will convert val to string + rapidjson::StringBuffer buffer; + rapidjson::Writer writer(buffer); + val.Accept(writer); + return buffer.GetString(); +} + template [[maybe_unused]] auto _atd_read_array(F read_func, const rapidjson::Value &val) { @@ -265,6 +274,12 @@ template writer.String(value.c_str()); } +[[maybe_unused]] void _atd_write_abstract(const std::string &value, rapidjson::Writer& writer) +{ + // writes string value as raw json + writer.RawValue(value.c_str(), value.size(), rapidjson::kStringType); +} + template [[maybe_unused]] void _atd_write_array(F write_func, const V& values, rapidjson::Writer& writer) { @@ -952,6 +967,9 @@ Root Root::from_json(const rapidjson::Value & doc) { if (doc.HasMember("options")) record.options = _atd_read_array([](const auto &v){return _atd_read_option([](const auto &v){return _atd_read_int(v);}, v);}, doc["options"]); else record.options = _atd_missing_json_field("Root", "options"); + if (doc.HasMember("untyped_things")) + record.untyped_things = _atd_read_array([](const auto &v){return _atd_read_abstract(v);}, doc["untyped_things"]); + else record.untyped_things = _atd_missing_json_field("Root", "untyped_things"); if (doc.HasMember("parametrized_record")) record.parametrized_record = IntFloatParametrizedRecord::from_json(doc["parametrized_record"]); else record.parametrized_record = _atd_missing_json_field("Root", "parametrized_record"); @@ -1040,6 +1058,8 @@ void Root::to_json(const Root &t, rapidjson::Writer &wr _atd_write_array([](auto v, auto &w){_atd_write_nullable([](auto v, auto &w){_atd_write_int(v, w);}, v, w);}, t.nullables, writer); writer.Key("options"); _atd_write_array([](auto v, auto &w){_atd_write_option([](auto v, auto &w){_atd_write_int(v, w);}, v, w);}, t.options, writer); + writer.Key("untyped_things"); + _atd_write_array([](auto v, auto &w){_atd_write_abstract(v, w);}, t.untyped_things, writer); writer.Key("parametrized_record"); IntFloatParametrizedRecord::to_json(t.parametrized_record, writer); writer.Key("parametrized_tuple"); diff --git a/atdcpp/test/cpp-expected/everything_atd.hpp b/atdcpp/test/cpp-expected/everything_atd.hpp index ecca746ef..3d0862a57 100644 --- a/atdcpp/test/cpp-expected/everything_atd.hpp +++ b/atdcpp/test/cpp-expected/everything_atd.hpp @@ -284,6 +284,7 @@ struct Root { std::map assoc4; std::vector> nullables; std::vector> options; + std::vector untyped_things; typedefs::IntFloatParametrizedRecord parametrized_record; typedefs::KindParametrizedTuple parametrized_tuple; uint16_t wrapped;