From a77c29ffbf07c6ac9b34d527568ee73682340bff Mon Sep 17 00:00:00 2001 From: Marek Misik Date: Wed, 5 Jun 2024 03:12:05 +0200 Subject: [PATCH] Erpcgen reimplementation in rust --- erpcgen_rust/.gitignore | 1 + erpcgen_rust/Cargo.lock | 900 +++++++++++++++ erpcgen_rust/Cargo.toml | 21 + erpcgen_rust/README.md | 30 + erpcgen_rust/askama.toml | 3 + erpcgen_rust/src/generator/escapers.rs | 44 + erpcgen_rust/src/generator/mod.rs | 11 + erpcgen_rust/src/generator/python/mod.rs | 11 + .../src/generator/python/python_generator.rs | 255 +++++ .../src/generator/python/symbols/mod.rs | 15 + .../generator/python/symbols/py_callback.rs | 20 + .../src/generator/python/symbols/py_const.rs | 27 + .../src/generator/python/symbols/py_enum.rs | 47 + .../generator/python/symbols/py_function.rs | 104 ++ .../generator/python/symbols/py_interface.rs | 31 + .../src/generator/python/symbols/py_struct.rs | 154 +++ .../generator/python/symbols/py_typedef.rs | 22 + .../src/generator/python/symbols/py_union.rs | 115 ++ .../src/generator/python/templates/common.rs | 116 ++ .../src/generator/python/templates/init.rs | 62 + .../generator/python/templates/init_global.rs | 71 ++ .../src/generator/python/templates/mod.rs | 24 + .../generator/python/templates/py_client.rs | 95 ++ .../generator/python/templates/py_const.rs | 16 + .../python/templates/py_en_union_read.rs | 17 + .../python/templates/py_en_union_write.rs | 17 + .../python/templates/py_encapsulated.rs | 17 + .../src/generator/python/templates/py_enum.rs | 16 + .../python/templates/py_interface.rs | 62 + .../generator/python/templates/py_server.rs | 104 ++ .../generator/python/templates/py_struct.rs | 16 + .../generator/python/templates/py_typedef.rs | 15 + .../generator/python/templates/py_union.rs | 16 + .../python/templates/py_union_read.rs | 17 + .../python/templates/py_union_write.rs | 17 + .../src/generator/python/templates/util.rs | 310 +++++ erpcgen_rust/src/generator/python/util.rs | 404 +++++++ erpcgen_rust/src/generator/util.rs | 163 +++ erpcgen_rust/src/grammar/idl_grammar.pest | 238 ++++ erpcgen_rust/src/main.rs | 76 ++ erpcgen_rust/src/options.rs | 98 ++ erpcgen_rust/src/parser/erpc_parser.rs | 978 ++++++++++++++++ .../src/parser/expressions/expression.rs | 119 ++ .../parser/expressions/expression_parser.rs | 26 + erpcgen_rust/src/parser/expressions/mod.rs | 11 + .../src/parser/expressions/operation.rs | 30 + .../src/parser/forward_definition_record.rs | 27 + erpcgen_rust/src/parser/grammar_parser.rs | 12 + erpcgen_rust/src/parser/mod.rs | 12 + erpcgen_rust/src/parser/util/annotations.rs | 921 +++++++++++++++ erpcgen_rust/src/parser/util/common.rs | 274 +++++ .../src/parser/util/const_definition.rs | 17 + .../src/parser/util/enum_definition.rs | 28 + erpcgen_rust/src/parser/util/expression.rs | 412 +++++++ .../src/parser/util/interface_definition.rs | 192 ++++ erpcgen_rust/src/parser/util/mod.rs | 16 + .../src/parser/util/struct_definition.rs | 87 ++ .../src/parser/util/type_definition.rs | 24 + .../src/parser/util/union_definition.rs | 21 + erpcgen_rust/src/symbols/annotations.rs | 118 ++ erpcgen_rust/src/symbols/const_definition.rs | 198 ++++ erpcgen_rust/src/symbols/doxygen_comment.rs | 13 + erpcgen_rust/src/symbols/enum_definition.rs | 239 ++++ erpcgen_rust/src/symbols/enum_member.rs | 217 ++++ .../src/symbols/function_definition.rs | 523 +++++++++ .../src/symbols/function_parameter.rs | 217 ++++ .../src/symbols/interface_definition.rs | 313 +++++ erpcgen_rust/src/symbols/language.rs | 15 + erpcgen_rust/src/symbols/member_options.rs | 13 + erpcgen_rust/src/symbols/mod.rs | 30 + erpcgen_rust/src/symbols/param_direction.rs | 27 + erpcgen_rust/src/symbols/pending_ident.rs | 65 ++ erpcgen_rust/src/symbols/program.rs | 1013 +++++++++++++++++ .../src/symbols/struct_data_member.rs | 237 ++++ erpcgen_rust/src/symbols/struct_definition.rs | 289 +++++ erpcgen_rust/src/symbols/struct_member.rs | 30 + erpcgen_rust/src/symbols/type_definition.rs | 212 ++++ erpcgen_rust/src/symbols/types.rs | 356 ++++++ erpcgen_rust/src/symbols/union_case.rs | 202 ++++ erpcgen_rust/src/symbols/union_case_member.rs | 242 ++++ erpcgen_rust/src/symbols/union_definition.rs | 309 +++++ erpcgen_rust/src/symbols/util.rs | 108 ++ erpcgen_rust/src/symbols/value.rs | 873 ++++++++++++++ .../templates/python/py_client.askama | 82 ++ .../templates/python/py_common.askama | 54 + erpcgen_rust/templates/python/py_const.askama | 4 + .../templates/python/py_en_union_read.askama | 49 + .../templates/python/py_en_union_write.askama | 53 + .../templates/python/py_encapsulated.askama | 12 + erpcgen_rust/templates/python/py_enum.askama | 15 + erpcgen_rust/templates/python/py_init.askama | 22 + .../templates/python/py_init_global.askama | 13 + .../templates/python/py_interface.askama | 31 + .../templates/python/py_macros.askama | 22 + .../templates/python/py_server.askama | 87 ++ .../templates/python/py_struct.askama | 100 ++ .../templates/python/py_typedef.askama | 1 + erpcgen_rust/templates/python/py_union.askama | 22 + .../templates/python/py_union_read.askama | 49 + .../templates/python/py_union_write.askama | 53 + 100 files changed, 13533 insertions(+) create mode 100644 erpcgen_rust/.gitignore create mode 100644 erpcgen_rust/Cargo.lock create mode 100644 erpcgen_rust/Cargo.toml create mode 100644 erpcgen_rust/README.md create mode 100644 erpcgen_rust/askama.toml create mode 100644 erpcgen_rust/src/generator/escapers.rs create mode 100644 erpcgen_rust/src/generator/mod.rs create mode 100644 erpcgen_rust/src/generator/python/mod.rs create mode 100644 erpcgen_rust/src/generator/python/python_generator.rs create mode 100644 erpcgen_rust/src/generator/python/symbols/mod.rs create mode 100644 erpcgen_rust/src/generator/python/symbols/py_callback.rs create mode 100644 erpcgen_rust/src/generator/python/symbols/py_const.rs create mode 100644 erpcgen_rust/src/generator/python/symbols/py_enum.rs create mode 100644 erpcgen_rust/src/generator/python/symbols/py_function.rs create mode 100644 erpcgen_rust/src/generator/python/symbols/py_interface.rs create mode 100644 erpcgen_rust/src/generator/python/symbols/py_struct.rs create mode 100644 erpcgen_rust/src/generator/python/symbols/py_typedef.rs create mode 100644 erpcgen_rust/src/generator/python/symbols/py_union.rs create mode 100644 erpcgen_rust/src/generator/python/templates/common.rs create mode 100644 erpcgen_rust/src/generator/python/templates/init.rs create mode 100644 erpcgen_rust/src/generator/python/templates/init_global.rs create mode 100644 erpcgen_rust/src/generator/python/templates/mod.rs create mode 100644 erpcgen_rust/src/generator/python/templates/py_client.rs create mode 100644 erpcgen_rust/src/generator/python/templates/py_const.rs create mode 100644 erpcgen_rust/src/generator/python/templates/py_en_union_read.rs create mode 100644 erpcgen_rust/src/generator/python/templates/py_en_union_write.rs create mode 100644 erpcgen_rust/src/generator/python/templates/py_encapsulated.rs create mode 100644 erpcgen_rust/src/generator/python/templates/py_enum.rs create mode 100644 erpcgen_rust/src/generator/python/templates/py_interface.rs create mode 100644 erpcgen_rust/src/generator/python/templates/py_server.rs create mode 100644 erpcgen_rust/src/generator/python/templates/py_struct.rs create mode 100644 erpcgen_rust/src/generator/python/templates/py_typedef.rs create mode 100644 erpcgen_rust/src/generator/python/templates/py_union.rs create mode 100644 erpcgen_rust/src/generator/python/templates/py_union_read.rs create mode 100644 erpcgen_rust/src/generator/python/templates/py_union_write.rs create mode 100644 erpcgen_rust/src/generator/python/templates/util.rs create mode 100644 erpcgen_rust/src/generator/python/util.rs create mode 100644 erpcgen_rust/src/generator/util.rs create mode 100644 erpcgen_rust/src/grammar/idl_grammar.pest create mode 100644 erpcgen_rust/src/main.rs create mode 100644 erpcgen_rust/src/options.rs create mode 100644 erpcgen_rust/src/parser/erpc_parser.rs create mode 100644 erpcgen_rust/src/parser/expressions/expression.rs create mode 100644 erpcgen_rust/src/parser/expressions/expression_parser.rs create mode 100644 erpcgen_rust/src/parser/expressions/mod.rs create mode 100644 erpcgen_rust/src/parser/expressions/operation.rs create mode 100644 erpcgen_rust/src/parser/forward_definition_record.rs create mode 100644 erpcgen_rust/src/parser/grammar_parser.rs create mode 100644 erpcgen_rust/src/parser/mod.rs create mode 100644 erpcgen_rust/src/parser/util/annotations.rs create mode 100644 erpcgen_rust/src/parser/util/common.rs create mode 100644 erpcgen_rust/src/parser/util/const_definition.rs create mode 100644 erpcgen_rust/src/parser/util/enum_definition.rs create mode 100644 erpcgen_rust/src/parser/util/expression.rs create mode 100644 erpcgen_rust/src/parser/util/interface_definition.rs create mode 100644 erpcgen_rust/src/parser/util/mod.rs create mode 100644 erpcgen_rust/src/parser/util/struct_definition.rs create mode 100644 erpcgen_rust/src/parser/util/type_definition.rs create mode 100644 erpcgen_rust/src/parser/util/union_definition.rs create mode 100644 erpcgen_rust/src/symbols/annotations.rs create mode 100644 erpcgen_rust/src/symbols/const_definition.rs create mode 100644 erpcgen_rust/src/symbols/doxygen_comment.rs create mode 100644 erpcgen_rust/src/symbols/enum_definition.rs create mode 100644 erpcgen_rust/src/symbols/enum_member.rs create mode 100644 erpcgen_rust/src/symbols/function_definition.rs create mode 100644 erpcgen_rust/src/symbols/function_parameter.rs create mode 100644 erpcgen_rust/src/symbols/interface_definition.rs create mode 100644 erpcgen_rust/src/symbols/language.rs create mode 100644 erpcgen_rust/src/symbols/member_options.rs create mode 100644 erpcgen_rust/src/symbols/mod.rs create mode 100644 erpcgen_rust/src/symbols/param_direction.rs create mode 100644 erpcgen_rust/src/symbols/pending_ident.rs create mode 100644 erpcgen_rust/src/symbols/program.rs create mode 100644 erpcgen_rust/src/symbols/struct_data_member.rs create mode 100644 erpcgen_rust/src/symbols/struct_definition.rs create mode 100644 erpcgen_rust/src/symbols/struct_member.rs create mode 100644 erpcgen_rust/src/symbols/type_definition.rs create mode 100644 erpcgen_rust/src/symbols/types.rs create mode 100644 erpcgen_rust/src/symbols/union_case.rs create mode 100644 erpcgen_rust/src/symbols/union_case_member.rs create mode 100644 erpcgen_rust/src/symbols/union_definition.rs create mode 100644 erpcgen_rust/src/symbols/util.rs create mode 100644 erpcgen_rust/src/symbols/value.rs create mode 100644 erpcgen_rust/templates/python/py_client.askama create mode 100644 erpcgen_rust/templates/python/py_common.askama create mode 100644 erpcgen_rust/templates/python/py_const.askama create mode 100644 erpcgen_rust/templates/python/py_en_union_read.askama create mode 100644 erpcgen_rust/templates/python/py_en_union_write.askama create mode 100644 erpcgen_rust/templates/python/py_encapsulated.askama create mode 100644 erpcgen_rust/templates/python/py_enum.askama create mode 100644 erpcgen_rust/templates/python/py_init.askama create mode 100644 erpcgen_rust/templates/python/py_init_global.askama create mode 100644 erpcgen_rust/templates/python/py_interface.askama create mode 100644 erpcgen_rust/templates/python/py_macros.askama create mode 100644 erpcgen_rust/templates/python/py_server.askama create mode 100644 erpcgen_rust/templates/python/py_struct.askama create mode 100644 erpcgen_rust/templates/python/py_typedef.askama create mode 100644 erpcgen_rust/templates/python/py_union.askama create mode 100644 erpcgen_rust/templates/python/py_union_read.askama create mode 100644 erpcgen_rust/templates/python/py_union_write.askama diff --git a/erpcgen_rust/.gitignore b/erpcgen_rust/.gitignore new file mode 100644 index 00000000..ea8c4bf7 --- /dev/null +++ b/erpcgen_rust/.gitignore @@ -0,0 +1 @@ +/target diff --git a/erpcgen_rust/Cargo.lock b/erpcgen_rust/Cargo.lock new file mode 100644 index 00000000..2af75c57 --- /dev/null +++ b/erpcgen_rust/Cargo.lock @@ -0,0 +1,900 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "android-tzdata" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0" + +[[package]] +name = "android_system_properties" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" +dependencies = [ + "libc", +] + +[[package]] +name = "ansi_term" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d52a9bb7ec0cf484c551830a7ce27bd20d67eac647e1befb56b0be4ee39a55d2" +dependencies = [ + "winapi", +] + +[[package]] +name = "anstream" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d664a92ecae85fd0a7392615844904654d1d5f5514837f471ddef4a057aba1b6" +dependencies = [ + "anstyle", + "anstyle-parse", + "anstyle-query", + "anstyle-wincon", + "colorchoice", + "utf8parse", +] + +[[package]] +name = "anstyle" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7079075b41f533b8c61d2a4d073c4676e1f8b249ff94a393b0595db304e0dd87" + +[[package]] +name = "anstyle-parse" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c75ac65da39e5fe5ab759307499ddad880d724eed2f6ce5b5e8a26f4f387928c" +dependencies = [ + "utf8parse", +] + +[[package]] +name = "anstyle-query" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e28923312444cdd728e4738b3f9c9cac739500909bb3d3c94b43551b16517648" +dependencies = [ + "windows-sys", +] + +[[package]] +name = "anstyle-wincon" +version = "3.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1cd54b81ec8d6180e24654d0b371ad22fc3dd083b6ff8ba325b72e00c87660a7" +dependencies = [ + "anstyle", + "windows-sys", +] + +[[package]] +name = "askama" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b79091df18a97caea757e28cd2d5fda49c6cd4bd01ddffd7ff01ace0c0ad2c28" +dependencies = [ + "askama_derive", + "askama_escape", + "humansize", + "num-traits", + "percent-encoding", +] + +[[package]] +name = "askama_derive" +version = "0.12.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19fe8d6cb13c4714962c072ea496f3392015f0989b1a2847bb4b2d9effd71d83" +dependencies = [ + "askama_parser", + "basic-toml", + "mime", + "mime_guess", + "proc-macro2", + "quote", + "serde", + "syn 2.0.48", +] + +[[package]] +name = "askama_escape" +version = "0.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "619743e34b5ba4e9703bba34deac3427c72507c7159f5fd030aea8cac0cfe341" + +[[package]] +name = "askama_parser" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "acb1161c6b64d1c3d83108213c2a2533a342ac225aabd0bda218278c2ddb00c0" +dependencies = [ + "nom", +] + +[[package]] +name = "atty" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" +dependencies = [ + "hermit-abi", + "libc", + "winapi", +] + +[[package]] +name = "autocfg" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1fdabc7756949593fe60f30ec81974b613357de856987752631dea1e3394c80" + +[[package]] +name = "basic-toml" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "823388e228f614e9558c6804262db37960ec8821856535f5c3f59913140558f8" +dependencies = [ + "serde", +] + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "block-buffer" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" +dependencies = [ + "generic-array", +] + +[[package]] +name = "bumpalo" +version = "3.15.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ff69b9dd49fd426c69a0db9fc04dd934cdb6645ff000864d98f7e2af8830eaa" + +[[package]] +name = "cc" +version = "1.0.90" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8cd6604a82acf3039f1144f54b8eb34e91ffba622051189e71b781822d5ee1f5" + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "chrono" +version = "0.4.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a0d04d43504c61aa6c7531f1871dd0d418d91130162063b789da00fd7057a5e" +dependencies = [ + "android-tzdata", + "iana-time-zone", + "js-sys", + "num-traits", + "wasm-bindgen", + "windows-targets", +] + +[[package]] +name = "clap" +version = "2.34.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a0610544180c38b88101fecf2dd634b174a62eef6946f84dfc6a7127512b381c" +dependencies = [ + "ansi_term", + "atty", + "bitflags", + "strsim 0.8.0", + "textwrap", + "unicode-width", + "vec_map", +] + +[[package]] +name = "clap" +version = "4.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dcfab8ba68f3668e89f6ff60f5b205cea56aa7b769451a59f34b8682f51c056d" +dependencies = [ + "clap_builder", + "clap_derive", +] + +[[package]] +name = "clap_builder" +version = "4.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fb7fb5e4e979aec3be7791562fcba452f94ad85e954da024396433e0e25a79e9" +dependencies = [ + "anstream", + "anstyle", + "clap_lex", + "strsim 0.10.0", +] + +[[package]] +name = "clap_derive" +version = "4.4.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf9804afaaf59a91e75b022a30fb7229a7901f60c755489cc61c9b423b836442" +dependencies = [ + "heck 0.4.1", + "proc-macro2", + "quote", + "syn 2.0.48", +] + +[[package]] +name = "clap_lex" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "702fc72eb24e5a1e48ce58027a675bc24edd52096d5397d4aea7c6dd9eca0bd1" + +[[package]] +name = "colorchoice" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7" + +[[package]] +name = "core-foundation-sys" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06ea2b9bc92be3c2baa9334a323ebca2d6f074ff852cd1d7b11064035cd3868f" + +[[package]] +name = "cpufeatures" +version = "0.2.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53fe5e26ff1b7aef8bca9c6080520cfb8d9333c7568e1829cef191a9723e5504" +dependencies = [ + "libc", +] + +[[package]] +name = "crc" +version = "3.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2b432c56615136f8dba245fed7ec3d5518c500a31108661067e61e72fe7e6bc" +dependencies = [ + "crc-catalog", +] + +[[package]] +name = "crc-catalog" +version = "2.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19d374276b40fb8bbdee95aef7c7fa6b5316ec764510eb64b8dd0e2ed0d7e7f5" + +[[package]] +name = "crypto-common" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" +dependencies = [ + "generic-array", + "typenum", +] + +[[package]] +name = "digest" +version = "0.10.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" +dependencies = [ + "block-buffer", + "crypto-common", +] + +[[package]] +name = "erpcgen" +version = "0.1.0" +dependencies = [ + "askama", + "askama_escape", + "chrono", + "clap 4.4.12", + "crc", + "fern", + "lazy_static", + "log", + "pest", + "pest_derive", + "structopt", +] + +[[package]] +name = "fern" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9f0c14694cbd524c8720dd69b0e3179344f04ebb5f90f2e4a440c6ea3b2f1ee" +dependencies = [ + "log", +] + +[[package]] +name = "generic-array" +version = "0.14.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" +dependencies = [ + "typenum", + "version_check", +] + +[[package]] +name = "heck" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d621efb26863f0e9924c6ac577e8275e5e6b77455db64ffa6c65c904e9e132c" +dependencies = [ + "unicode-segmentation", +] + +[[package]] +name = "heck" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" + +[[package]] +name = "hermit-abi" +version = "0.1.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" +dependencies = [ + "libc", +] + +[[package]] +name = "humansize" +version = "2.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6cb51c9a029ddc91b07a787f1d86b53ccfa49b0e86688c946ebe8d3555685dd7" +dependencies = [ + "libm", +] + +[[package]] +name = "iana-time-zone" +version = "0.1.60" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7ffbb5a1b541ea2561f8c41c087286cc091e21e556a4f09a8f6cbf17b69b141" +dependencies = [ + "android_system_properties", + "core-foundation-sys", + "iana-time-zone-haiku", + "js-sys", + "wasm-bindgen", + "windows-core", +] + +[[package]] +name = "iana-time-zone-haiku" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" +dependencies = [ + "cc", +] + +[[package]] +name = "js-sys" +version = "0.3.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29c15563dc2726973df627357ce0c9ddddbea194836909d655df6a75d2cf296d" +dependencies = [ + "wasm-bindgen", +] + +[[package]] +name = "lazy_static" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" + +[[package]] +name = "libc" +version = "0.2.151" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "302d7ab3130588088d277783b1e2d2e10c9e9e4a16dd9050e6ec93fb3e7048f4" + +[[package]] +name = "libm" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ec2a862134d2a7d32d7983ddcdd1c4923530833c9f2ea1a44fc5fa473989058" + +[[package]] +name = "log" +version = "0.4.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f" + +[[package]] +name = "memchr" +version = "2.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "523dc4f511e55ab87b694dc30d0f820d60906ef06413f93d4d7a1385599cc149" + +[[package]] +name = "mime" +version = "0.3.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" + +[[package]] +name = "mime_guess" +version = "2.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4192263c238a5f0d0c6bfd21f336a313a4ce1c450542449ca191bb657b4642ef" +dependencies = [ + "mime", + "unicase", +] + +[[package]] +name = "minimal-lexical" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" + +[[package]] +name = "nom" +version = "7.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" +dependencies = [ + "memchr", + "minimal-lexical", +] + +[[package]] +name = "num-traits" +version = "0.2.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da0df0e5185db44f69b44f26786fe401b6c293d1907744beaa7fa62b2e5a517a" +dependencies = [ + "autocfg", +] + +[[package]] +name = "once_cell" +version = "1.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" + +[[package]] +name = "percent-encoding" +version = "2.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" + +[[package]] +name = "pest" +version = "2.7.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "311fb059dee1a7b802f036316d790138c613a4e8b180c822e3925a662e9f0c95" +dependencies = [ + "memchr", + "thiserror", + "ucd-trie", +] + +[[package]] +name = "pest_derive" +version = "2.7.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f73541b156d32197eecda1a4014d7f868fd2bcb3c550d5386087cfba442bf69c" +dependencies = [ + "pest", + "pest_generator", +] + +[[package]] +name = "pest_generator" +version = "2.7.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c35eeed0a3fab112f75165fdc026b3913f4183133f19b49be773ac9ea966e8bd" +dependencies = [ + "pest", + "pest_meta", + "proc-macro2", + "quote", + "syn 2.0.48", +] + +[[package]] +name = "pest_meta" +version = "2.7.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2adbf29bb9776f28caece835398781ab24435585fe0d4dc1374a61db5accedca" +dependencies = [ + "once_cell", + "pest", + "sha2", +] + +[[package]] +name = "proc-macro-error" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" +dependencies = [ + "proc-macro-error-attr", + "proc-macro2", + "quote", + "syn 1.0.109", + "version_check", +] + +[[package]] +name = "proc-macro-error-attr" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" +dependencies = [ + "proc-macro2", + "quote", + "version_check", +] + +[[package]] +name = "proc-macro2" +version = "1.0.76" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95fc56cda0b5c3325f5fbbd7ff9fda9e02bb00bb3dac51252d2f1bfa1cb8cc8c" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "serde" +version = "1.0.197" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3fb1c873e1b9b056a4dc4c0c198b24c3ffa059243875552b2bd0933b1aee4ce2" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.197" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7eb0b34b42edc17f6b7cac84a52a1c5f0e1bb2227e997ca9011ea3dd34e8610b" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.48", +] + +[[package]] +name = "sha2" +version = "0.10.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest", +] + +[[package]] +name = "strsim" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a" + +[[package]] +name = "strsim" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" + +[[package]] +name = "structopt" +version = "0.3.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c6b5c64445ba8094a6ab0c3cd2ad323e07171012d9c98b0b15651daf1787a10" +dependencies = [ + "clap 2.34.0", + "lazy_static", + "structopt-derive", +] + +[[package]] +name = "structopt-derive" +version = "0.4.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dcb5ae327f9cc13b68763b5749770cb9e048a99bd9dfdfa58d0cf05d5f64afe0" +dependencies = [ + "heck 0.3.3", + "proc-macro-error", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "syn" +version = "1.0.109" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "syn" +version = "2.0.48" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0f3531638e407dfc0814761abb7c00a5b54992b849452a0646b7f65c9f770f3f" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "textwrap" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060" +dependencies = [ + "unicode-width", +] + +[[package]] +name = "thiserror" +version = "1.0.56" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d54378c645627613241d077a3a79db965db602882668f9136ac42af9ecb730ad" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.56" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa0faa943b50f3db30a20aa7e265dbc66076993efed8463e8de414e5d06d3471" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.48", +] + +[[package]] +name = "typenum" +version = "1.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" + +[[package]] +name = "ucd-trie" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed646292ffc8188ef8ea4d1e0e0150fb15a5c2e12ad9b8fc191ae7a8a7f3c4b9" + +[[package]] +name = "unicase" +version = "2.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f7d2d4dafb69621809a81864c9c1b864479e1235c0dd4e199924b9742439ed89" +dependencies = [ + "version_check", +] + +[[package]] +name = "unicode-ident" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" + +[[package]] +name = "unicode-segmentation" +version = "1.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1dd624098567895118886609431a7c3b8f516e41d30e0643f03d94592a147e36" + +[[package]] +name = "unicode-width" +version = "0.1.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e51733f11c9c4f72aa0c160008246859e340b00807569a0da0e7a1079b27ba85" + +[[package]] +name = "utf8parse" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a" + +[[package]] +name = "vec_map" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191" + +[[package]] +name = "version_check" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" + +[[package]] +name = "wasm-bindgen" +version = "0.2.92" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4be2531df63900aeb2bca0daaaddec08491ee64ceecbee5076636a3b026795a8" +dependencies = [ + "cfg-if", + "wasm-bindgen-macro", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.92" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "614d787b966d3989fa7bb98a654e369c762374fd3213d212cfc0251257e747da" +dependencies = [ + "bumpalo", + "log", + "once_cell", + "proc-macro2", + "quote", + "syn 2.0.48", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.92" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1f8823de937b71b9460c0c34e25f3da88250760bec0ebac694b49997550d726" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.92" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.48", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.92" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af190c94f2773fdb3729c55b007a722abb5384da03bc0986df4c289bf5567e96" + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "windows-core" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-sys" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-targets" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a18201040b24831fbb9e4eb208f8892e1f50a37feb53cc7ff887feb8f50e7cd" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb7764e35d4db8a7921e09562a0304bf2f93e0a51bfccee0bd0bb0b666b015ea" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbaa0368d4f1d2aaefc55b6fcfee13f41544ddf36801e793edbbfd7d7df075ef" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a28637cb1fa3560a16915793afb20081aba2c92ee8af57b4d5f28e4b3e7df313" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ffe5e8e31046ce6230cc7215707b816e339ff4d4d67c65dffa206fd0f7aa7b9a" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d6fa32db2bc4a2f5abeacf2b69f7992cd09dca97498da74a151a3132c26befd" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a657e1e9d3f514745a572a6846d3c7aa7dbe1658c056ed9c3344c4109a6949e" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dff9641d1cd4be8d1a070daf9e3773c5f67e78b4d9d42263020c057706765c04" diff --git a/erpcgen_rust/Cargo.toml b/erpcgen_rust/Cargo.toml new file mode 100644 index 00000000..18696eca --- /dev/null +++ b/erpcgen_rust/Cargo.toml @@ -0,0 +1,21 @@ +[package] +name = "erpcgen" +version = "0.1.0" +edition = "2021" + + +[dependencies] +pest = "2.7.9" +pest_derive = { version = "2.7.9", features = ["grammar-extras"] } +clap = { version = "4.4.12", features = ["derive"] } +structopt = "0.3.26" +fern = "0.6.2" +log = "0.4.20" +lazy_static = "1.4.0" +askama = "0.12.1" +askama_escape = "0.10.3" +crc = "3.0.1" +chrono = "0.4.37" + +[build-dependencies] +askama = "0.12.1" diff --git a/erpcgen_rust/README.md b/erpcgen_rust/README.md new file mode 100644 index 00000000..085486fd --- /dev/null +++ b/erpcgen_rust/README.md @@ -0,0 +1,30 @@ +# Erpcgen rust + +A code generator for the eRPC library in written in Rust. + +## Prerequisites + +- [Rust](https://www.rust-lang.org/tools/install) +- [Cargo](https://doc.rust-lang.org/cargo/getting-started/installation.html) (Comes with Rust) + +## Building the Project + +1. Navigate to the project directory: + + ``` + cd path/to/project + ``` +2. Build the project: + + ``` + cargo build + ``` + +## Running the Project + +1. After building the project, you can run it with: + + ``` + cargo run -- path/to/input.erpc + ``` + \ No newline at end of file diff --git a/erpcgen_rust/askama.toml b/erpcgen_rust/askama.toml new file mode 100644 index 00000000..18707e50 --- /dev/null +++ b/erpcgen_rust/askama.toml @@ -0,0 +1,3 @@ +[[escaper]] +extensions = ["askama"] +path = "generator::escapers::Python" \ No newline at end of file diff --git a/erpcgen_rust/src/generator/escapers.rs b/erpcgen_rust/src/generator/escapers.rs new file mode 100644 index 00000000..c03e5194 --- /dev/null +++ b/erpcgen_rust/src/generator/escapers.rs @@ -0,0 +1,44 @@ +/* + * Copyright 2024 Marek Mišík(nxf76107) + * Copyright 2024 NXP + * + * SPDX-License-Identifier: BSD-3-Clause + */ + + +use std::fmt::Write; +use askama_escape::Escaper; + +/// Escapes the given string for Python +/// +/// # Arguments +/// +/// * `input` - The string to escape +/// +/// # Returns +/// +/// The escaped string +fn py_escaper(input: &str) -> String { + // Perform the escaping here. + input.replace("\\", "\\\\") +} + +pub struct Python; + +/// Implementation of Escaper for Python +impl Escaper for Python { + + /// Writes the escaped string to the given writer + /// + /// # Arguments + /// + /// * `fmt` - The writer to write the escaped string to + /// * `string` - The string to escape + /// + /// # Returns + /// + /// The result of writing the escaped string to the writer + fn write_escaped(&self, mut fmt: W, string: &str) -> std::fmt::Result where W: Write { + write!(fmt, "{}", py_escaper(string)) + } +} diff --git a/erpcgen_rust/src/generator/mod.rs b/erpcgen_rust/src/generator/mod.rs new file mode 100644 index 00000000..a10036d3 --- /dev/null +++ b/erpcgen_rust/src/generator/mod.rs @@ -0,0 +1,11 @@ +/* + * Copyright 2024 Marek Mišík(nxf76107) + * Copyright 2024 NXP + * + * SPDX-License-Identifier: BSD-3-Clause + */ + + +pub(crate) mod python; +pub(super) mod util; +pub mod escapers; \ No newline at end of file diff --git a/erpcgen_rust/src/generator/python/mod.rs b/erpcgen_rust/src/generator/python/mod.rs new file mode 100644 index 00000000..8bea341b --- /dev/null +++ b/erpcgen_rust/src/generator/python/mod.rs @@ -0,0 +1,11 @@ +/* + * Copyright 2024 Marek Mišík(nxf76107) + * + * SPDX-License-Identifier: BSD-3-Clause + */ + + +pub(crate) mod python_generator; +pub(crate) mod templates; +pub mod util; +pub(crate) mod symbols; \ No newline at end of file diff --git a/erpcgen_rust/src/generator/python/python_generator.rs b/erpcgen_rust/src/generator/python/python_generator.rs new file mode 100644 index 00000000..a57292da --- /dev/null +++ b/erpcgen_rust/src/generator/python/python_generator.rs @@ -0,0 +1,255 @@ +/* + * Copyright 2024 Marek Mišík(nxf76107) + * + * SPDX-License-Identifier: BSD-3-Clause + */ + + +use std::collections::{HashMap, HashSet}; +use std::path::PathBuf; +use log::{info, trace}; +use crate::generator::python::templates::common::CommonFileTemplateBuilder; +use crate::generator::python::templates::init::InitFileTemplateBuilder; +use crate::generator::python::templates::init_global::GlobalInitFileTemplateBuilder; +use crate::generator::python::templates::py_client::PyClientTemplateBuilder; +use crate::generator::python::templates::py_interface::PyInterfaceTemplateBuilder; +use crate::generator::python::templates::py_server::PyServerTemplateBuilder; +use crate::generator::python::templates::util::remove_string_quotes; +use crate::generator::util::{extract_groups, extract_includes, find_output_dir_annotation}; +use crate::symbols::annotations::Annotation; +use crate::symbols::const_definition::ConstDefinition; +use crate::symbols::enum_definition::EnumDefinition; +use crate::symbols::interface_definition::InterfaceDefinition; +use crate::symbols::language::Language; +use crate::symbols::program::Program; +use crate::symbols::struct_definition::StructDefinition; +use crate::symbols::type_definition::TypeDefinition; +use crate::symbols::union_definition::UnionDefinition; + + +/// Python code generator +pub(crate) struct PythonGenerator { + program: Box, + output_dir: PathBuf, + time: String, + crc: Option, + groups: HashMap>, +} + +/// Python code generator implementation +impl PythonGenerator { + pub(crate) fn new(mut program: Box, crc: Option, mut base_path: PathBuf) -> Self { + let _ = find_output_dir_annotation(&program.annotations, &Language::Python).is_some_and(|a| { + match a { + Annotation::OutputDir { path, .. } => { + base_path.push(&remove_string_quotes(path.as_str())); + true + }, + _ => panic!("Unexpected annotation") + } + }); + Self::prepare_interface_ids(&mut program.interface_definitions); // Assign IDs to interfaces + let groups = extract_groups(program.interface_definitions.clone(), &program.name.clone().unwrap_or_default()); + PythonGenerator { + program, + output_dir: base_path, + time: chrono::Local::now().to_string(), + crc, + groups, + } + } + + /// Assigns IDs to interfaces that don't have one + /// + /// # Arguments + /// + /// * `interfaces` - A mutable reference to a vector of interface definitions + fn prepare_interface_ids(interfaces: &mut Vec) { + let mut interface_ids = interfaces.iter().filter_map(|i| { + i.id + }).collect::>(); + let mut current_id = 1; + for interface in interfaces { + if interface.id.is_some() { + continue; // Skip interfaces with ID + } + while interface_ids.contains(¤t_id) { // Find the first available ID + current_id += 1; + } + interface.id = Some(current_id); + interface_ids.insert(current_id); // Mark the ID as used + } + } + + /// Generates Python code + pub(crate) fn generate(&self) { + info!("Generating Python code"); + + if self.output_dir.components().next().is_some() { + self.create_group_folder(self.output_dir.clone()); + } + + self.generate_global_init_file(); + + + let groups = extract_groups(self.program.interface_definitions.clone(), &self.program.name.clone().unwrap_or_default()); + trace!("Groups: {:?}", groups); + trace!("Output dir: {:?}", self.output_dir); + for (group_name, interfaces) in groups { + self.generate_group(&group_name, &interfaces); + } + } + + /// Generates Python code for a group + /// + /// # Arguments + /// + /// * `group_path` - A reference to a string representing the group path + /// * `interfaces` - A reference to a vector of interface definitions + pub(crate) fn generate_group(&self, group_path: &String, interfaces: &Vec) { + info!("Generating Python code for group {}", group_path); + + let mut all_types = HashSet::new(); + let mut includes = HashSet::new(); + + // Gather all required types + for interface in interfaces { + all_types.extend(self.program.get_interface_types(interface)); + includes.extend(extract_includes(&self.program.annotations, interface)); + } + + + // Resolve gathered types + let consts: Vec = self.program.const_definitions.clone(); + trace!("Consts: {:?}", consts); + let enums: Vec = self.program.enum_definitions.clone(); + trace!("Enums: {:?}", enums); + let structs: Vec = self.program.struct_definitions.clone(); + trace!("Structs: {:?}", structs); + let unions: Vec = self.program.union_definitions.clone(); + trace!("Unions: {:?}", unions); + let typedefs: Vec = self.program.type_definitions.clone(); + trace!("Typedefs: {:?}", typedefs); + println!("Base path: {:?}", self.output_dir); + let folder_path = self.output_dir.join(group_path); + println!("Folder path: {:?}", folder_path); + + self.create_group_folder(folder_path.clone()); + + self.generate_init_file(folder_path.clone()); + + self.generate_common_file(folder_path.clone(), includes.into_iter().collect(), consts, enums, structs, unions, typedefs); + + self.generate_interface_file(folder_path.clone(), interfaces); + + self.generate_client_file(folder_path.clone(), group_path.clone(), interfaces); + + self.generate_server_file(folder_path.clone(), group_path.clone(), interfaces); + } + + /// Generates __init__.py file + /// + /// # Arguments + /// + /// * `path_buf` - A path buffer + fn generate_init_file(&self, path_buf: PathBuf) { + info!("Generating __init__.py file"); + InitFileTemplateBuilder::new(self.time.clone(), path_buf).with_preceding_comment( + self.program.doxygen_preceding_comment.clone() + ).create(); + } + + /// Generates global __init__.py file + fn generate_global_init_file(&self) { + info!("Generating global __init__.py file"); + GlobalInitFileTemplateBuilder::new(self.time.clone(), self.output_dir.clone()) + .with_preceding_comment(self.program.doxygen_preceding_comment.clone()) + .with_crc(self.crc.clone()) + .create(); + } + + /// Creates a group folder + fn create_group_folder(&self, folder_path: PathBuf) { + trace!("Creating group folder: {:?}", folder_path); + std::fs::create_dir_all(folder_path).expect("Unable to create group folder"); + } + + /// Generates interface.py file + /// + /// # Arguments + /// + /// * `path_buf` - A path buffer + /// * `interfaces` - A reference to a vector of interface definitions + fn generate_interface_file(&self, path_buf: PathBuf, interfaces: &Vec) { + info!("Generating interface.py file"); + PyInterfaceTemplateBuilder::new(self.time.clone(), path_buf.join("interface.py")) + .with_preceding_comment(self.program.doxygen_preceding_comment.clone()) + .with_interfaces(interfaces) + .create(); + } + + /// Generates common.py file + /// + /// # Arguments + /// + /// * `path_buf` - A path buffer + /// * `includes` - A vector of strings representing includes + /// * `consts` - A vector of constant definitions + /// * `enums` - A vector of enum definitions + /// * `structs` - A vector of struct definitions + /// * `unions` - A vector of union definitions + /// * `typedefs` - A vector of type definitions + fn generate_common_file(&self, path_buf: PathBuf, includes: Vec, consts: Vec, enums: Vec, structs: Vec, unions: Vec, typedefs: Vec) { + info!("Generating common.py file"); + CommonFileTemplateBuilder::new(self.time.clone(), path_buf.join("common.py")) + .with_preceding_comment(self.program.doxygen_preceding_comment.clone()) + .with_includes(includes) + .with_consts(&consts) + .with_enums(&enums) + .with_structs(&structs) + .with_unions(&unions) + .with_typedefs(&typedefs) + .create(); + } + + /// Generates client.py file + /// + /// # Arguments + /// + /// * `path_buf` - A path buffer + /// * `group` - A string representing the group + /// * `interfaces` - A reference to a vector of interface definitions + fn generate_client_file(&self, path_buf: PathBuf, group: String, interfaces: &Vec) { + info!("Generating client.py file"); + PyClientTemplateBuilder::new(self.time.clone(), group, path_buf.join("client.py")) + .with_preceding_comment(self.program.doxygen_preceding_comment.clone()) + .with_interfaces(interfaces) + .with_groups(self.groups.keys().cloned().collect()) + .with_callbacks(interfaces, &self.groups) + .create(); + } + + /// Generates server.py file + /// + /// # Arguments + /// + /// * `path_buf` - A path buffer + /// * `group` - A string representing the group + /// * `interfaces` - A reference to a vector of interface definitions + fn generate_server_file(&self, path_buf: PathBuf, group: String, interfaces: &Vec) { + info!("Generating server.py file"); + let mut includes = HashSet::new(); + + for interface in interfaces { + includes.extend(extract_includes(&self.program.annotations, interface)); + } + + PyServerTemplateBuilder::new(self.time.clone(), group, path_buf.join("server.py")) + .with_preceding_comment(self.program.doxygen_preceding_comment.clone()) + .with_interfaces(interfaces) + .with_groups(self.groups.keys().cloned().collect()) + .with_includes(includes.iter().cloned().collect()) + .create(); + } +} + diff --git a/erpcgen_rust/src/generator/python/symbols/mod.rs b/erpcgen_rust/src/generator/python/symbols/mod.rs new file mode 100644 index 00000000..b0d81358 --- /dev/null +++ b/erpcgen_rust/src/generator/python/symbols/mod.rs @@ -0,0 +1,15 @@ +/* + * Copyright 2024 Marek Mišík(nxf76107) + * Copyright 2024 NXP + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +pub(crate) mod py_enum; +pub(crate) mod py_const; +pub(crate) mod py_struct; +pub(crate) mod py_union; +pub(crate) mod py_typedef; +pub(crate) mod py_interface; +pub(crate) mod py_function; +pub(crate) mod py_callback; \ No newline at end of file diff --git a/erpcgen_rust/src/generator/python/symbols/py_callback.rs b/erpcgen_rust/src/generator/python/symbols/py_callback.rs new file mode 100644 index 00000000..acf0f87b --- /dev/null +++ b/erpcgen_rust/src/generator/python/symbols/py_callback.rs @@ -0,0 +1,20 @@ +/* + * Copyright 2024 Marek Mišík(nxf76107) + * Copyright 2024 NXP + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +pub struct PyCallback { + pub name: String, + pub functions: Vec +} + +impl PyCallback { + pub fn new(name: &str, functions: Vec) -> Self { + Self { + name: name.to_string(), + functions + } + } +} \ No newline at end of file diff --git a/erpcgen_rust/src/generator/python/symbols/py_const.rs b/erpcgen_rust/src/generator/python/symbols/py_const.rs new file mode 100644 index 00000000..9bb02652 --- /dev/null +++ b/erpcgen_rust/src/generator/python/symbols/py_const.rs @@ -0,0 +1,27 @@ +/* + * Copyright 2024 Marek Mišík(nxf76107) + * Copyright 2024 NXP + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +use crate::generator::python::util::{format_comments, value_to_string}; +use crate::symbols::const_definition::ConstDefinition; + +pub(crate) struct PyConst { + pub name: String, + pub value: String, + pub preceding_comment: String, + pub trailing_comment: String, +} + +impl PyConst { + pub(crate) fn from(const_def: &ConstDefinition) -> Self { + Self { + name: const_def.name.clone(), + value: value_to_string(const_def.value.clone()), + preceding_comment: format_comments(const_def.doxygen_preceding_comment.clone()), + trailing_comment: format_comments(const_def.doxygen_trailing_comment.clone()), + } + } +} \ No newline at end of file diff --git a/erpcgen_rust/src/generator/python/symbols/py_enum.rs b/erpcgen_rust/src/generator/python/symbols/py_enum.rs new file mode 100644 index 00000000..2a6b559c --- /dev/null +++ b/erpcgen_rust/src/generator/python/symbols/py_enum.rs @@ -0,0 +1,47 @@ +/* + * Copyright 2024 Marek Mišík(nxf76107) + * Copyright 2024 NXP + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +use crate::generator::python::util::format_comments; +use crate::symbols::enum_definition::EnumDefinition; +use crate::symbols::enum_member::EnumMember; + +pub struct PyEnum { + pub name: Option, + pub members: Vec, + pub preceding_comment: String, + pub trailing_comment: String, +} + +impl PyEnum { + pub(crate) fn from(definition: &EnumDefinition) -> Self { + Self { + name: definition.name.clone(), + members: definition.members.iter().map(PyEnumMember::from).collect(), + preceding_comment: format_comments(definition.doxygen_preceding_comment.clone()), + trailing_comment: format_comments(definition.doxygen_trailing_comment.clone()), + } + } +} + +pub struct PyEnumMember { + pub name: String, + pub value: i32, + pub preceding_comment: String, + pub trailing_comment: String, +} + +impl PyEnumMember { + pub(crate) fn from(enum_member: &EnumMember) -> Self { + Self { + name: enum_member.name.clone(), + value: enum_member.value.to_i32(), + preceding_comment: format_comments(enum_member.doxygen_preceding_comment.clone()), + trailing_comment: format_comments(enum_member.doxygen_trailing_comment.clone()), + } + } +} + diff --git a/erpcgen_rust/src/generator/python/symbols/py_function.rs b/erpcgen_rust/src/generator/python/symbols/py_function.rs new file mode 100644 index 00000000..954c82bc --- /dev/null +++ b/erpcgen_rust/src/generator/python/symbols/py_function.rs @@ -0,0 +1,104 @@ +/* + * Copyright 2024 Marek Mišík(nxf76107) + * Copyright 2024 NXP + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +use std::rc::Rc; + +use crate::generator::python::util::{ + find_discriminator_name, find_external_annotation, format_comments, + is_discriminator_for_function, is_length_for_function, +}; +use crate::generator::util::find_nullable_annotation; +use crate::symbols::function_definition::FunctionDefinition; +use crate::symbols::function_parameter::FunctionParameter; +use crate::symbols::param_direction::ParamDirection; +use crate::symbols::types::Type; + +pub struct PyFunction { + pub name: String, + pub id: usize, + pub return_type: Option>, + pub is_oneway: bool, + pub external: bool, + pub parameters: Vec, + pub preceding_comment: String, + pub trailing_comment: String, +} + +impl PyFunction { + pub(crate) fn from(function: &FunctionDefinition) -> Self { + Self { + name: function.name.clone(), + id: function.id.expect("Function ID must be set"), + return_type: function.return_type.clone(), + is_oneway: function.is_oneway, + external: find_external_annotation(&function.annotations), + parameters: function + .parameters + .iter() + .map(|p| { + PyFunctionParameter::from( + p, + is_length_for_function( + function, + &p.clone().name.expect("Parameter must have a name"), + ), + is_discriminator_for_function( + function, + &p.clone().name.expect("Parameter must have a name"), + ), + ) + }) + .collect(), + preceding_comment: format_comments(function.doxygen_preceding_comment.clone()), + trailing_comment: format_comments(function.doxygen_trailing_comment.clone()), + } + } + + pub fn has_out_parameters(&self) -> bool { + self.parameters.iter().any(|p| p.is_out()) + } +} + +pub struct PyFunctionParameter { + pub name: String, + pub type_: Rc, + pub direction: ParamDirection, + pub is_length_for: Option, + pub is_discriminator: Option, + pub discriminator_name: Option, + pub is_nullable: bool, +} + +impl PyFunctionParameter { + pub(crate) fn from( + parameter: &FunctionParameter, + is_length_for: Option, + is_discriminator_for: Option, + ) -> Self { + Self { + name: parameter.name.clone().expect("Parameter name must be set"), + type_: parameter.type_.clone().resolve_original_type(), + direction: parameter.direction, + is_length_for: is_length_for.clone(), + is_discriminator: is_discriminator_for.clone(), + discriminator_name: find_discriminator_name(¶meter.annotations), + is_nullable: find_nullable_annotation(¶meter.annotations) + } + } + + pub(crate) fn is_out(&self) -> bool { + self.direction == ParamDirection::Out || self.direction == ParamDirection::InOut + } + + pub(crate) fn is_in(&self) -> bool { + self.direction == ParamDirection::In || self.direction == ParamDirection::InOut + } + + pub(crate) fn is_inout(&self) -> bool { + self.direction == ParamDirection::InOut + } +} diff --git a/erpcgen_rust/src/generator/python/symbols/py_interface.rs b/erpcgen_rust/src/generator/python/symbols/py_interface.rs new file mode 100644 index 00000000..31f0ac30 --- /dev/null +++ b/erpcgen_rust/src/generator/python/symbols/py_interface.rs @@ -0,0 +1,31 @@ +/* + * Copyright 2024 Marek Mišík(nxf76107) + * Copyright 2024 NXP + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +use crate::generator::python::symbols::py_function::PyFunction; +//use crate::generator::python::symbols::py_function::PyFunction; +use crate::generator::python::util::format_comments; +use crate::symbols::interface_definition::InterfaceDefinition; + +pub struct PyInterface { + pub name: String, + pub id: usize, + pub functions: Vec, + pub preceding_comment: String, + pub trailing_comment: String, +} + +impl PyInterface { + pub(crate) fn from(interface: &InterfaceDefinition) -> Self { + Self { + name: interface.name.clone(), + id: interface.id.expect("Interface ID must be set"), + functions: interface.functions.iter().map(PyFunction::from).collect(), + preceding_comment: format_comments(interface.doxygen_preceding_comment.clone()), + trailing_comment: format_comments(interface.doxygen_trailing_comment.clone()), + } + } +} \ No newline at end of file diff --git a/erpcgen_rust/src/generator/python/symbols/py_struct.rs b/erpcgen_rust/src/generator/python/symbols/py_struct.rs new file mode 100644 index 00000000..901a8882 --- /dev/null +++ b/erpcgen_rust/src/generator/python/symbols/py_struct.rs @@ -0,0 +1,154 @@ +/* + * Copyright 2024 Marek Mišík(nxf76107) + * Copyright 2024 NXP + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +use std::rc::Rc; +use crate::generator::python::symbols::py_union::PyUnionCase; +use crate::generator::python::util::{find_discriminator_name, format_comments, is_discriminator_for, is_length_for, reorder_cases_default_last}; +use crate::generator::util::find_nullable_annotation; +use crate::symbols::struct_data_member::StructDataMember; +use crate::symbols::struct_definition::StructDefinition; +use crate::symbols::struct_member::StructMember; +use crate::symbols::types::Type; +use crate::symbols::union_definition::UnionDefinition; +use crate::symbols::value::Value; + +pub struct PyStruct { + pub name: String, + pub members: Vec, + pub unions: Vec, + pub data_members: Vec, + pub preceding_comment: String, + pub trailing_comment: String, +} + +impl PyStruct { + pub(crate) fn from(definition: &StructDefinition) -> Self { + let members: Vec = definition.members.iter().map(|m| PyStructMember::from(m, definition)).collect(); + + Self { + name: definition.name.clone(), + members: members.clone(), + unions: members.clone().into_iter().filter_map(|m| { + if m.is_encapsulated_union() { + Some(m.to_encapsulated_union()) + } else { + None + } + }).collect(), + data_members: members.into_iter().filter_map(|m| { + if m.is_data_member() { + Some(m.to_data_member()) + } else { + None + } + }).collect(), + preceding_comment: format_comments(definition.doxygen_preceding_comment.clone()), + trailing_comment: format_comments(definition.doxygen_trailing_comment.clone()), + } + } +} + +#[derive(Debug, Clone, PartialEq)] +pub struct PyStructDataMember { + pub name: String, + pub type_: Rc, + pub is_length_for: Option, + pub is_discriminator: Option, + pub discriminator_name: Option, + pub is_nullable: bool, + pub preceding_comment: String, + pub trailing_comment: String, +} + +impl PyStructDataMember { + pub(crate) fn from(struct_member: &StructDataMember, is_length_for: Option, is_discriminator: Option) -> Self { + Self { + name: struct_member.name.clone(), + type_: struct_member.member_type.clone(), + is_length_for, + is_discriminator, + discriminator_name: find_discriminator_name(&struct_member.annotations), + is_nullable: find_nullable_annotation(&struct_member.annotations), + preceding_comment: format_comments(struct_member.doxygen_preceding_comment.clone()), + trailing_comment: format_comments(struct_member.doxygen_trailing_comment.clone()), + } + } +} + +#[derive(Debug, Clone, PartialEq)] +pub struct PyEncapsulatedUnion { + pub name: String, + pub type_: Rc, + pub cases: Vec, + pub discriminator_name: String, + pub preceding_comment: String, + pub trailing_comment: String, +} + +impl PyEncapsulatedUnion { + pub(crate) fn from(union: &UnionDefinition, discriminator: &Value) -> Self { + let discriminator_name = match discriminator { + Value::RuntimeValue { name } => name.clone(), + _ => panic!("Unexpected discriminator value"), + }; + + Self { + name: union.name.clone(), + type_: Rc::new(Type::Union { name: union.name.clone() }), + cases: reorder_cases_default_last(union.cases.iter().map(PyUnionCase::from).collect()), + discriminator_name, + preceding_comment: format_comments(union.doxygen_preceding_comment.clone()), + trailing_comment: format_comments(union.doxygen_trailing_comment.clone()), + } + } + + pub fn has_default_case(&self) -> bool { + self.cases.iter().any(|c| c.case_values.is_empty()) + } + + pub fn has_only_default_case(&self) -> bool { + self.cases.iter().all(|c| c.case_values.is_empty()) + } +} + +#[derive(Debug, Clone, PartialEq)] +pub enum PyStructMember { + DataMember(PyStructDataMember), + EncapsulatedUnion(PyEncapsulatedUnion), +} + +impl PyStructMember { + + pub fn from(member: &StructMember, definition: &StructDefinition) -> Self { + match member { + StructMember::DataMember(d) => PyStructMember::DataMember(PyStructDataMember::from(d, is_length_for(definition, &d.name), is_discriminator_for(definition, &d.name))), + StructMember::UnionDefinition { definition: u, discriminator: d } => PyStructMember::EncapsulatedUnion(PyEncapsulatedUnion::from(u, d)), + } + } + pub fn is_data_member(&self) -> bool { + matches!(self, PyStructMember::DataMember(_)) + } + + pub fn is_encapsulated_union(&self) -> bool { + matches!(self, PyStructMember::EncapsulatedUnion(_)) + } + + pub fn to_data_member(self) -> PyStructDataMember { + match self { + PyStructMember::DataMember(d) => d, + _ => panic!("Expected data member"), + } + } + + pub fn to_encapsulated_union(self) -> PyEncapsulatedUnion { + match self { + PyStructMember::EncapsulatedUnion(u) => u, + _ => panic!("Expected encapsulated union"), + } + } +} + diff --git a/erpcgen_rust/src/generator/python/symbols/py_typedef.rs b/erpcgen_rust/src/generator/python/symbols/py_typedef.rs new file mode 100644 index 00000000..e4aa39df --- /dev/null +++ b/erpcgen_rust/src/generator/python/symbols/py_typedef.rs @@ -0,0 +1,22 @@ +/* + * Copyright 2024 Marek Mišík(nxf76107) + * Copyright 2024 NXP + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +use crate::symbols::type_definition::TypeDefinition; + +pub struct PyTypeDef { + pub name: String, + pub type_: String, +} + +impl PyTypeDef { + pub(crate) fn from(type_definition: &TypeDefinition) -> Self { + Self { + name: type_definition.name.clone(), + type_: type_definition.referenced_type.get_name() + } + } +} \ No newline at end of file diff --git a/erpcgen_rust/src/generator/python/symbols/py_union.rs b/erpcgen_rust/src/generator/python/symbols/py_union.rs new file mode 100644 index 00000000..5aeeacaf --- /dev/null +++ b/erpcgen_rust/src/generator/python/symbols/py_union.rs @@ -0,0 +1,115 @@ +/* + * Copyright 2024 Marek Mišík(nxf76107) + * Copyright 2024 NXP + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +use crate::generator::python::util::{ + find_discriminator_name, format_comments, is_discriminator_for_union_case_member, + is_length_for_union_case_member, reorder_cases_default_last, +}; +use crate::generator::util::find_nullable_annotation; +use crate::symbols::types::Type; +use crate::symbols::union_case::UnionCase; +use crate::symbols::union_case_member::UnionCaseMember; +use crate::symbols::union_definition::UnionDefinition; +use crate::symbols::value::Value; +use std::rc::Rc; +pub struct PyUnion { + pub name: String, + pub cases: Vec, + pub preceding_comment: String, + pub trailing_comment: String, +} + +impl PyUnion { + pub(crate) fn from(definition: &UnionDefinition) -> Self { + let mut cases: Vec = definition.cases.iter().map(PyUnionCase::from).collect(); + for i in 0..cases.len() - 1 { + if cases[i].case_members.is_empty() { + let values = cases[i].clone().case_values.clone(); + cases[i + 1].case_values.extend(values); + cases[i].case_values.clear(); + } + } + Self { + name: definition.name.clone(), + cases: reorder_cases_default_last( + cases.into_iter().filter(|c| !c.case_members.is_empty()).collect(), + ), + preceding_comment: format_comments(definition.doxygen_preceding_comment.clone()), + trailing_comment: format_comments(definition.doxygen_trailing_comment.clone()), + } + } + + pub fn has_default_case(&self) -> bool { + self.cases.iter().any(|c| c.case_values.is_empty()) + } + + pub fn has_only_default_case(&self) -> bool { + self.cases.iter().all(|c| c.case_values.is_empty()) + } +} + +#[derive(Debug, Clone, PartialEq)] +pub struct PyUnionCase { + pub case_values: Vec, + pub case_members: Vec, +} + +impl PyUnionCase { + pub(crate) fn from(union_case: &UnionCase) -> Self { + Self { + case_values: union_case.case_values.clone(), + case_members: union_case + .members + .iter() + .map(|c| { + PyUnionCaseMember::from( + c, + is_length_for_union_case_member(union_case, &c.name), + is_discriminator_for_union_case_member(union_case, &c.name), + ) + }) + .collect(), + } + } + + pub fn is_default(&self) -> bool { + self.case_values.is_empty() + } +} + +#[derive(Debug, Clone, PartialEq)] +pub struct PyUnionCaseMember { + pub name: String, + pub type_: Rc, + pub is_length_for: Option, + pub is_discriminator: Option, + pub discriminator_name: Option, + pub is_nullable: bool, + pub preceding_comment: String, + pub trailing_comment: String, +} + +impl PyUnionCaseMember { + pub(crate) fn from( + union_case_member: &UnionCaseMember, + is_length_for: Option, + is_discriminator: Option, + ) -> Self { + Self { + name: union_case_member.name.clone(), + type_: union_case_member.member_type.clone(), + is_length_for, + is_discriminator, + discriminator_name: find_discriminator_name(&union_case_member.annotations), + is_nullable: find_nullable_annotation( + &union_case_member.annotations, + ), + preceding_comment: format_comments(union_case_member.doxygen_preceding_comment.clone()), + trailing_comment: format_comments(union_case_member.doxygen_trailing_comment.clone()), + } + } +} diff --git a/erpcgen_rust/src/generator/python/templates/common.rs b/erpcgen_rust/src/generator/python/templates/common.rs new file mode 100644 index 00000000..8307a073 --- /dev/null +++ b/erpcgen_rust/src/generator/python/templates/common.rs @@ -0,0 +1,116 @@ +/* + * Copyright 2024 Marek Mišík(nxf76107) + * Copyright 2024 NXP + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +use std::path::PathBuf; +use askama::Template; +use crate::generator; +use crate::generator::python::symbols::py_const::PyConst; +use crate::generator::python::symbols::py_enum::PyEnum; +use crate::generator::python::symbols::py_struct::PyStruct; +use crate::generator::python::symbols::py_typedef::PyTypeDef; +use crate::generator::python::symbols::py_union::PyUnion; +use crate::generator::python::util::format_comments; +use crate::generator::util::write_to_file; +use crate::symbols::const_definition::ConstDefinition; +use crate::symbols::doxygen_comment::DoxygenComment; +use crate::symbols::enum_definition::EnumDefinition; +use crate::symbols::struct_definition::StructDefinition; +use crate::generator::python::templates::util::{format_case_values_py, decode_type, encode_type, format_class_init_py, format_type_name_py, format_values_py, optional_indent}; +use crate::symbols::type_definition::TypeDefinition; +use crate::symbols::union_definition::UnionDefinition; + + +#[derive(Template)] +#[template(path = "python/py_common.askama")] +pub(crate) struct CommonFileTemplate { + pub date: String, + pub preceding_comment: String, + pub includes: Vec, + pub consts: Vec, + pub enums: Vec, + pub structs: Vec, + pub unions: Vec, + pub typedefs: Vec, +} + +pub(crate) struct CommonFileTemplateBuilder { + date: String, + preceding_comment: String, + includes: Vec, + consts: Vec, + enums: Vec, + structs: Vec, + unions: Vec, + typedefs: Vec, + path_buf: PathBuf, +} + +impl CommonFileTemplateBuilder { + pub(crate) fn new(date: String, output_path: PathBuf) -> Self { + Self { + date, + preceding_comment: String::default(), + includes: vec![], + consts: vec![], + enums: vec![], + structs: vec![], + unions: vec![], + typedefs: vec![], + path_buf: output_path, + } + } + + pub(crate) fn with_preceding_comment(mut self, comments: Vec) -> Self { + let concatenated_comments = format_comments(comments); + self.preceding_comment = concatenated_comments; + self + } + + pub(crate) fn with_includes(mut self, includes: Vec) -> Self { + self.includes = includes; + self + } + + pub(crate) fn with_consts(mut self, consts: &[ConstDefinition]) -> Self { + self.consts = consts.iter().map(PyConst::from).collect(); + self + } + + pub(crate) fn with_enums(mut self, enums: &[EnumDefinition]) -> Self { + self.enums = enums.iter().map(PyEnum::from).collect(); + self + } + + pub(crate) fn with_structs(mut self, structs: &[StructDefinition]) -> Self { + self.structs = structs.iter().map(PyStruct::from).collect(); + self + } + + pub(crate) fn with_unions(mut self, unions: &[UnionDefinition]) -> Self { + self.unions = unions.iter().map(PyUnion::from).collect(); + self + } + + pub(crate) fn with_typedefs(mut self, typedefs: &[TypeDefinition]) -> Self { + self.typedefs = typedefs.iter().map(PyTypeDef::from).collect(); + self + } + + pub(crate) fn create(self) { + let content = CommonFileTemplate { + date: self.date, + preceding_comment: self.preceding_comment, + includes: self.includes, + consts: self.consts, + enums: self.enums, + structs: self.structs, + unions: self.unions, + typedefs: self.typedefs, + }.render().expect("Failed to render CommonFileTemplate"); + write_to_file(&self.path_buf, content.as_str()); + } +} \ No newline at end of file diff --git a/erpcgen_rust/src/generator/python/templates/init.rs b/erpcgen_rust/src/generator/python/templates/init.rs new file mode 100644 index 00000000..ac2be220 --- /dev/null +++ b/erpcgen_rust/src/generator/python/templates/init.rs @@ -0,0 +1,62 @@ +/* + * Copyright 2024 Marek Mišík(nxf76107) + * Copyright 2024 NXP + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +use std::path::PathBuf; +use askama::Template; +use crate::generator; +use crate::generator::python::util::format_comments; +use crate::generator::util::write_to_file; +use crate::symbols::doxygen_comment::DoxygenComment; + + +#[derive(Template)] +#[template(path = "python/py_init.askama")] +pub struct InitFileTemplate { + pub date: String, + pub preceding_comment: String, +} + +impl InitFileTemplate { + pub(crate) fn new(date: String, preceding_comment: String) -> Self { + Self { + date, + preceding_comment + } + } +} + +pub(crate) struct InitFileTemplateBuilder { + date: String, + preceding_comment: String, + path_buf: PathBuf, +} + +impl InitFileTemplateBuilder { + pub(crate) fn new(date: String, output_path: PathBuf) -> Self { + Self { + date, + preceding_comment: String::default(), + path_buf: output_path, + } + } + + pub(crate) fn with_preceding_comment(mut self, comments: Vec) -> Self { + let concatenated_comments = format_comments(comments); + self.preceding_comment = concatenated_comments; + self + } + + pub(crate) fn create(mut self) { + let content = InitFileTemplate { + date: self.date, + preceding_comment: self.preceding_comment, + }.render().expect("Failed to render InitFileTemplate"); + self.path_buf.push("__init__.py"); + + write_to_file(&self.path_buf, content.as_str()) + } +} \ No newline at end of file diff --git a/erpcgen_rust/src/generator/python/templates/init_global.rs b/erpcgen_rust/src/generator/python/templates/init_global.rs new file mode 100644 index 00000000..d4cfd354 --- /dev/null +++ b/erpcgen_rust/src/generator/python/templates/init_global.rs @@ -0,0 +1,71 @@ +/* + * Copyright 2024 Marek Mišík(nxf76107) + * Copyright 2024 NXP + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +use std::path::PathBuf; +use askama::Template; +use crate::generator; +use crate::generator::python::util::format_comments; +use crate::generator::util::write_to_file; +use crate::symbols::doxygen_comment::DoxygenComment; + +#[derive(Template)] +#[template(path = "python/py_init_global.askama")] +pub struct GlobalInitFileTemplate { + pub date: String, + pub preceding_comment: String, + pub crc16: Option +} + +impl GlobalInitFileTemplate { + pub(crate) fn new(date: String, preceding_comment: String, crc16: Option) -> Self { + Self { + date, + preceding_comment, + crc16 + } + } +} + +pub(crate) struct GlobalInitFileTemplateBuilder { + date: String, + preceding_comment: String, + crc16: Option, + path_buf: PathBuf, +} + +impl GlobalInitFileTemplateBuilder { + pub(crate) fn new(date: String, output_path: PathBuf) -> Self { + Self { + date, + preceding_comment: String::default(), + crc16: None, + path_buf: output_path, + } + } + + pub(crate) fn with_preceding_comment(mut self, comments: Vec) -> Self { + let concatenated_comments = format_comments(comments); + self.preceding_comment = concatenated_comments; + self + } + + pub(crate) fn with_crc(mut self, crc: Option) -> Self { + self.crc16 = crc; + self + } + + pub(crate) fn create(mut self) { + let content = GlobalInitFileTemplate { + date: self.date, + preceding_comment: self.preceding_comment, + crc16: self.crc16 + }.render().expect("Failed to render GlobalInitFileTemplate"); + self.path_buf.push("__init__.py"); + + write_to_file(&self.path_buf, content.as_str()) + } +} \ No newline at end of file diff --git a/erpcgen_rust/src/generator/python/templates/mod.rs b/erpcgen_rust/src/generator/python/templates/mod.rs new file mode 100644 index 00000000..b7741ca5 --- /dev/null +++ b/erpcgen_rust/src/generator/python/templates/mod.rs @@ -0,0 +1,24 @@ +/* + * Copyright 2024 Marek Mišík(nxf76107) + * Copyright 2024 NXP + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +pub mod init; +pub mod init_global; +pub mod common; +pub mod py_enum; +pub mod py_const; +pub mod util; +pub mod py_struct; +pub mod py_encapsulated; +pub mod py_en_union_read; +pub mod py_en_union_write; +pub mod py_union; +pub mod py_union_read; +pub mod py_union_write; +pub mod py_typedef; +pub mod py_interface; +pub mod py_client; +pub mod py_server; \ No newline at end of file diff --git a/erpcgen_rust/src/generator/python/templates/py_client.rs b/erpcgen_rust/src/generator/python/templates/py_client.rs new file mode 100644 index 00000000..24ab1daf --- /dev/null +++ b/erpcgen_rust/src/generator/python/templates/py_client.rs @@ -0,0 +1,95 @@ +/* + * Copyright 2024 Marek Mišík(nxf76107) + * Copyright 2024 NXP + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +use std::collections::HashMap; +use std::path::PathBuf; +use askama::Template; +use crate::generator::python::symbols::py_callback::PyCallback; +use crate::generator::python::symbols::py_interface::PyInterface; +use crate::generator::python::templates::util::{ decode_type, encode_type, format_function_prototype_py, format_param_name, optional_indent }; +use crate::generator; +use crate::generator::python::util::{find_used_callbacks_for_group, format_comments}; +use crate::generator::util::write_to_file; +use crate::symbols::doxygen_comment::DoxygenComment; +use crate::symbols::interface_definition::InterfaceDefinition; + +#[derive(Template)] +#[template(path = "python/py_client.askama")] +pub struct PyClientTemplate { + pub date: String, + pub preceding_comment: String, + pub interfaces: Vec, + pub callbacks: Vec, + pub group: String, + pub groups: Vec, + pub output: String, +} + +pub struct PyClientTemplateBuilder { + date: String, + preceding_comment: String, + interfaces: Vec, + callbacks: Vec, + group: String, + groups: Vec, + output: String, + output_path: PathBuf, +} + +impl PyClientTemplateBuilder { + pub(crate) fn new(date: String, group: String, output_path: PathBuf)-> Self { + Self { + date, + preceding_comment: String::default(), + interfaces: vec![], + callbacks: vec![], + group, + groups: vec![], + output: String::default(), + output_path, + } + } + + pub(crate) fn with_preceding_comment(mut self, comments: Vec) -> Self { + let concatenated_comments = format_comments(comments); + self.preceding_comment = concatenated_comments; + self + } + + pub(crate) fn with_interfaces(mut self, interfaces: &Vec) -> Self { + self.interfaces = interfaces.iter().map(PyInterface::from).collect(); + self + } + + pub(crate) fn with_callbacks(mut self, interfaces: &Vec, groups: &HashMap> ) -> Self { + self.callbacks = find_used_callbacks_for_group(&self.group, interfaces, groups); + self + } + + pub(crate) fn with_groups(mut self, groups: Vec) -> Self { + self.groups = groups; + self + } + + pub(crate) fn with_output(mut self, output: String) -> Self { + self.output = output; + self + } + + pub(crate) fn create(self) { + let content = PyClientTemplate { + date: self.date, + preceding_comment: self.preceding_comment, + interfaces: self.interfaces, + callbacks: self.callbacks, + group: self.group, + groups: self.groups, + output: self.output, + }.render().expect("Failed to render PyClientTemplate"); + write_to_file(&self.output_path, content.as_str()); + } +} \ No newline at end of file diff --git a/erpcgen_rust/src/generator/python/templates/py_const.rs b/erpcgen_rust/src/generator/python/templates/py_const.rs new file mode 100644 index 00000000..6af9521b --- /dev/null +++ b/erpcgen_rust/src/generator/python/templates/py_const.rs @@ -0,0 +1,16 @@ +/* + * Copyright 2024 Marek Mišík(nxf76107) + * Copyright 2024 NXP + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +use askama::Template; +use crate::generator::python::symbols::py_const::PyConst; +use crate::generator; + +#[derive(Template)] +#[template(path = "python/py_const.askama")] +pub(crate) struct ConstTemplate { + pub const_: PyConst +} \ No newline at end of file diff --git a/erpcgen_rust/src/generator/python/templates/py_en_union_read.rs b/erpcgen_rust/src/generator/python/templates/py_en_union_read.rs new file mode 100644 index 00000000..8d8a3478 --- /dev/null +++ b/erpcgen_rust/src/generator/python/templates/py_en_union_read.rs @@ -0,0 +1,17 @@ +/* + * Copyright 2024 Marek Mišík(nxf76107) + * Copyright 2024 NXP + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +use askama::Template; +use crate::generator::python::symbols::py_struct::PyEncapsulatedUnion; +use crate::generator::python::templates::util::{format_case_values_py, decode_type, optional_indent}; +use crate::generator; + +#[derive(Template)] +#[template(path = "python/py_en_union_read.askama")] +pub struct EnUnionReadTemplate { + pub encapsulated: PyEncapsulatedUnion, +} \ No newline at end of file diff --git a/erpcgen_rust/src/generator/python/templates/py_en_union_write.rs b/erpcgen_rust/src/generator/python/templates/py_en_union_write.rs new file mode 100644 index 00000000..70bbf4ca --- /dev/null +++ b/erpcgen_rust/src/generator/python/templates/py_en_union_write.rs @@ -0,0 +1,17 @@ +/* + * Copyright 2024 Marek Mišík(nxf76107) + * Copyright 2024 NXP + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +use askama::Template; +use crate::generator; +use crate::generator::python::symbols::py_struct::PyEncapsulatedUnion; +use crate::generator::python::templates::util::{format_case_values_py, encode_type, optional_indent}; + +#[derive(Template)] +#[template(path = "python/py_en_union_write.askama")] +pub struct EnUnionWriteTemplate { + pub encapsulated: PyEncapsulatedUnion, +} \ No newline at end of file diff --git a/erpcgen_rust/src/generator/python/templates/py_encapsulated.rs b/erpcgen_rust/src/generator/python/templates/py_encapsulated.rs new file mode 100644 index 00000000..67962269 --- /dev/null +++ b/erpcgen_rust/src/generator/python/templates/py_encapsulated.rs @@ -0,0 +1,17 @@ +/* + * Copyright 2024 Marek Mišík(nxf76107) + * Copyright 2024 NXP + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +use askama::Template; +use crate::generator::python::symbols::py_struct::{PyEncapsulatedUnion }; +use crate::generator::python::templates::util::{format_class_init_py, format_type_name_py, format_values_py}; // Keep for the template +use crate::generator; + +#[derive(Template)] +#[template(path = "python/py_encapsulated.askama")] +pub struct StructTemplate { + pub encapsulated: PyEncapsulatedUnion +} \ No newline at end of file diff --git a/erpcgen_rust/src/generator/python/templates/py_enum.rs b/erpcgen_rust/src/generator/python/templates/py_enum.rs new file mode 100644 index 00000000..191a3f6c --- /dev/null +++ b/erpcgen_rust/src/generator/python/templates/py_enum.rs @@ -0,0 +1,16 @@ +/* + * Copyright 2024 Marek Mišík(nxf76107) + * Copyright 2024 NXP + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +use askama::Template; +use crate::generator::python::symbols::py_enum::PyEnum; +use crate::generator; + +#[derive(Template)] +#[template(path = "python/py_enum.askama")] +pub(crate) struct EnumTemplate { + enum_: PyEnum +} \ No newline at end of file diff --git a/erpcgen_rust/src/generator/python/templates/py_interface.rs b/erpcgen_rust/src/generator/python/templates/py_interface.rs new file mode 100644 index 00000000..a8d6a669 --- /dev/null +++ b/erpcgen_rust/src/generator/python/templates/py_interface.rs @@ -0,0 +1,62 @@ +/* + * Copyright 2024 Marek Mišík(nxf76107) + * Copyright 2024 NXP + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +use std::path::PathBuf; +use askama::Template; +use crate::generator::python::symbols::py_interface::PyInterface; +use crate::generator::python::templates::util::{format_function_prototype_py}; +use crate::generator; +use crate::generator::python::util::format_comments; +use crate::generator::util::write_to_file; +use crate::symbols::doxygen_comment::DoxygenComment; +use crate::symbols::interface_definition::InterfaceDefinition; + +#[derive(Template)] +#[template(path = "python/py_interface.askama")] +pub struct PyInterfaceTemplate { + pub date: String, + pub preceding_comment: String, + pub interfaces: Vec +} + +pub struct PyInterfaceTemplateBuilder { + date: String, + preceding_comment: String, + interfaces: Vec, + path_buf: PathBuf, +} + +impl PyInterfaceTemplateBuilder { + pub(crate) fn new(date: String, output_path: PathBuf) -> Self { + Self { + date, + preceding_comment: String::default(), + interfaces: vec![], + path_buf: output_path, + } + } + + pub(crate) fn with_preceding_comment(mut self, comments: Vec) -> Self { + let concatenated_comments = format_comments(comments); + self.preceding_comment = concatenated_comments; + self + } + + pub(crate) fn with_interfaces(mut self, interfaces: &Vec) -> Self { + self.interfaces = interfaces.iter().map(PyInterface::from).collect(); + self + } + + pub(crate) fn create(self) { + let content = PyInterfaceTemplate { + date: self.date, + preceding_comment: self.preceding_comment, + interfaces: self.interfaces, + }.render().expect("Failed to render InterfaceFileTemplate"); + write_to_file(&self.path_buf, content.as_str()); + } +} diff --git a/erpcgen_rust/src/generator/python/templates/py_server.rs b/erpcgen_rust/src/generator/python/templates/py_server.rs new file mode 100644 index 00000000..b763a64c --- /dev/null +++ b/erpcgen_rust/src/generator/python/templates/py_server.rs @@ -0,0 +1,104 @@ +/* + * Copyright 2024 Marek Mišík(nxf76107) + * Copyright 2024 NXP + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +use std::collections::HashMap; +use std::path::PathBuf; +use askama::Template; +use crate::generator::python::symbols::py_callback::PyCallback; +use crate::generator::python::symbols::py_interface::PyInterface; +use crate::generator::python::templates::util::{ decode_type, encode_type, format_function_prototype_py, format_param_name, optional_indent, format_inner_parameters_py }; +use crate::generator; +use crate::generator::python::util::{find_used_callbacks_for_group, format_comments}; +use crate::generator::util::write_to_file; +use crate::symbols::doxygen_comment::DoxygenComment; +use crate::symbols::interface_definition::InterfaceDefinition; + +#[derive(Template)] +#[template(path = "python/py_server.askama")] +pub struct PyServerTemplate { + pub date: String, + pub preceding_comment: String, + pub interfaces: Vec, + pub callbacks: Vec, + pub group: String, + pub groups: Vec, + pub includes: Vec, + pub output: String, +} + +pub struct PyServerTemplateBuilder { + date: String, + preceding_comment: String, + interfaces: Vec, + callbacks: Vec, + group: String, + groups: Vec, + output: String, + includes: Vec, + output_path: PathBuf, +} + +impl PyServerTemplateBuilder { + pub(crate) fn new(date: String, group: String, output_path: PathBuf)-> Self { + Self { + date, + preceding_comment: String::default(), + interfaces: vec![], + callbacks: vec![], + group, + groups: vec![], + includes: vec![], + output: String::default(), + output_path, + } + } + + pub(crate) fn with_preceding_comment(mut self, comments: Vec) -> Self { + let concatenated_comments = format_comments(comments); + self.preceding_comment = concatenated_comments; + self + } + + pub(crate) fn with_interfaces(mut self, interfaces: &Vec) -> Self { + self.interfaces = interfaces.iter().map(PyInterface::from).collect(); + self + } + + pub(crate) fn with_callbacks(mut self, interfaces: &Vec, groups: &HashMap> ) -> Self { + self.callbacks = find_used_callbacks_for_group(&self.group, interfaces, groups); + self + } + + pub(crate) fn with_groups(mut self, groups: Vec) -> Self { + self.groups = groups; + self + } + + pub(crate) fn with_output(mut self, output: String) -> Self { + self.output = output; + self + } + + pub(crate) fn with_includes(mut self, includes: Vec) -> Self { + self.includes = includes; + self + } + + pub(crate) fn create(self) { + let content = PyServerTemplate { + date: self.date, + preceding_comment: self.preceding_comment, + interfaces: self.interfaces, + callbacks: self.callbacks, + group: self.group, + groups: self.groups, + includes: self.includes, + output: self.output, + }.render().expect("Failed to render PyClientTemplate"); + write_to_file(&self.output_path, content.as_str()); + } +} \ No newline at end of file diff --git a/erpcgen_rust/src/generator/python/templates/py_struct.rs b/erpcgen_rust/src/generator/python/templates/py_struct.rs new file mode 100644 index 00000000..1e519ac2 --- /dev/null +++ b/erpcgen_rust/src/generator/python/templates/py_struct.rs @@ -0,0 +1,16 @@ +/* + * Copyright 2024 Marek Mišík(nxf76107) + * Copyright 2024 NXP + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +use askama::Template; +use crate::generator::python::symbols::py_struct::{PyStruct}; +use crate::generator::python::templates::util::{format_case_values_py, encode_type, format_class_init_py, format_type_name_py, format_values_py, get_current_array_dim, shed_array_dimension, optional_indent, decode_type}; +use crate::generator; +#[derive(Template)] +#[template(path = "python/py_struct.askama")] +pub struct StructTemplate { + pub struct_: PyStruct +} diff --git a/erpcgen_rust/src/generator/python/templates/py_typedef.rs b/erpcgen_rust/src/generator/python/templates/py_typedef.rs new file mode 100644 index 00000000..411c1193 --- /dev/null +++ b/erpcgen_rust/src/generator/python/templates/py_typedef.rs @@ -0,0 +1,15 @@ +/* + * Copyright 2024 Marek Mišík(nxf76107) + * Copyright 2024 NXP + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +use askama::Template; +use crate::generator::python::symbols::py_typedef::PyTypeDef; +use crate::generator; +#[derive(Template)] +#[template(path = "python/py_typedef.askama")] +pub struct PyTypeDefTemplate { + pub typedef_: PyTypeDef +} \ No newline at end of file diff --git a/erpcgen_rust/src/generator/python/templates/py_union.rs b/erpcgen_rust/src/generator/python/templates/py_union.rs new file mode 100644 index 00000000..c15744c4 --- /dev/null +++ b/erpcgen_rust/src/generator/python/templates/py_union.rs @@ -0,0 +1,16 @@ +/* + * Copyright 2024 Marek Mišík(nxf76107) + * Copyright 2024 NXP + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +use askama::Template; +use crate::generator::python::symbols::py_union::PyUnion; +use crate::generator::python::templates::util::{format_type_name_py, format_values_py ,format_case_values_py, decode_type, encode_type, optional_indent}; +use crate::generator; +#[derive(Template)] +#[template(path = "python/py_union.askama")] +pub struct PyUnionTemplate { + pub union_: PyUnion, +} \ No newline at end of file diff --git a/erpcgen_rust/src/generator/python/templates/py_union_read.rs b/erpcgen_rust/src/generator/python/templates/py_union_read.rs new file mode 100644 index 00000000..d66c41ca --- /dev/null +++ b/erpcgen_rust/src/generator/python/templates/py_union_read.rs @@ -0,0 +1,17 @@ +/* + * Copyright 2024 Marek Mišík(nxf76107) + * Copyright 2024 NXP + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +use askama::Template; +use crate::generator::python::templates::util::{format_case_values_py, decode_type, optional_indent}; +use crate::generator; +use crate::generator::python::symbols::py_union::PyUnion; + +#[derive(Template)] +#[template(path = "python/py_union_read.askama")] +pub struct UnionReadTemplate { + pub union_: PyUnion, +} \ No newline at end of file diff --git a/erpcgen_rust/src/generator/python/templates/py_union_write.rs b/erpcgen_rust/src/generator/python/templates/py_union_write.rs new file mode 100644 index 00000000..94feb843 --- /dev/null +++ b/erpcgen_rust/src/generator/python/templates/py_union_write.rs @@ -0,0 +1,17 @@ +/* + * Copyright 2024 Marek Mišík(nxf76107) + * Copyright 2024 NXP + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +use askama::Template; +use crate::generator::python::templates::util::{format_case_values_py, encode_type, optional_indent}; +use crate::generator; +use crate::generator::python::symbols::py_union::PyUnion; + +#[derive(Template)] +#[template(path = "python/py_union_write.askama")] +pub struct UnionReadTemplate { + pub union_: PyUnion, +} \ No newline at end of file diff --git a/erpcgen_rust/src/generator/python/templates/util.rs b/erpcgen_rust/src/generator/python/templates/util.rs new file mode 100644 index 00000000..e1e22842 --- /dev/null +++ b/erpcgen_rust/src/generator/python/templates/util.rs @@ -0,0 +1,310 @@ +/* + * Copyright 2024 Marek Mišík(nxf76107) + * Copyright 2024 NXP + * + * SPDX-License-Identifier: BSD-3-Clause + */ + + +use crate::generator::python::symbols::py_struct::PyStructDataMember; +use crate::symbols::types::Type; +use crate::symbols::value::Value; +use std::ops::Deref; +use std::rc::Rc; +use crate::generator::python::symbols::py_function::PyFunction; +use crate::symbols::param_direction::ParamDirection; + +fn value_to_string_py(value: &Value) -> String { + match value { + Value::Bool(bool) => bool.to_string(), + Value::Int8(n) => n.to_string(), + Value::Int16(n) => n.to_string(), + Value::Int32(n) => n.to_string(), + Value::Int64(n) => n.to_string(), + Value::Uint8(n) => n.to_string(), + Value::Uint16(n) => n.to_string(), + Value::Uint32(n) => n.to_string(), + Value::UInt64(n) => n.to_string(), + Value::Float(n) => n.to_string(), + Value::Double(n) => n.to_string(), + Value::String(s) => s.clone(), + Value::ReferencedValue { name, prefix, .. } => if let Some(p) = prefix{ p.clone() + "." + name.as_str() } else { name.clone() }, + _ => "".to_string(), + } +} + +pub fn format_values_py(values: &[Value]) -> String { + values + .iter() + .map(value_to_string_py) + .collect::>() + .join(", ") +} + +pub fn format_type_name_py(type_: Rc) -> String { + match type_.deref() { + Type::List { element_type } => format!( + "list<{}>", + format_type_name_py(element_type.clone()) + ), + Type::Array { + element_type, + dimension_sizes, + } => { + let mut result = format_type_name_py(element_type.clone()); + for size in dimension_sizes { + result.push_str(&format!("[{}]", size)); + } + result + } + Type::TypeDef { name, .. } => name.clone(), + Type::Bool => "bool".to_string(), + Type::Int8 => "int8".to_string(), + Type::Int16 => "int16".to_string(), + Type::Int32 => "int32".to_string(), + Type::Int64 => "int64".to_string(), + Type::UInt8 => "uint8".to_string(), + Type::UInt16 => "uint16".to_string(), + Type::UInt32 => "uint32".to_string(), + Type::UInt64 => "uint64".to_string(), + Type::Float => "float".to_string(), + Type::Double => "double".to_string(), + Type::String => "string".to_string(), + Type::Struct { name, .. } => name.clone(), + Type::Enum { name, .. } => name.clone(), + Type::Union { name, .. } => name.clone(), + _ => "".to_string(), + } +} + +pub fn format_class_init_py(members: &[PyStructDataMember]) -> String { + members + .iter() + .filter_map(|m| { + if m.is_length_for.is_none() && !m.type_.is_union() { + Some(format!("{}=None", m.name)) + } else { + None + } + }) + .collect::>() + .join(", ") +} + +pub fn shed_array_dimension(array_type: Rc) -> Rc { + if let Type::List { element_type } = array_type.deref() { + return element_type.clone() + } + if let Type::Array { element_type, dimension_sizes } = array_type.deref() { + if dimension_sizes.len() > 1 { + let tail = dimension_sizes.clone().split_off(1); + Rc::new(Type::Array { + element_type: element_type.clone(), + dimension_sizes: tail, + }) + } else { + element_type.clone() + } + } else { + array_type + } +} + +pub fn get_current_array_dim(array_type: Rc) -> usize { + if let Type::Array { dimension_sizes, .. } = array_type.deref() { + if !dimension_sizes.is_empty() { + dimension_sizes[0] + } else { + 0 + } + } else { + 0 + } +} + +pub fn optional_indent(indent: &bool) -> String { + if *indent { + " ".to_string() + } else { + "".to_string() + } +} + +pub fn format_case_values_py(values: &[Value], discriminator: &String ) -> String { + values.iter().map(|v| format!("{} == {}", discriminator, value_to_string_py(v))).collect::>().join(" or ") +} + +pub fn decode_type(py_type: Rc, name: &str, prefix: &str, discriminator: Option, codec: &str, indent: &str, depth: usize) -> String { + match py_type.as_ref() { + Type::Struct { .. } => { + format!( + "{} = {}{}()._read({})", + name, + prefix, + py_type.get_name(), + codec + ) + } + Type::Union { ..} => { + format!( + "{},{} = {}{}()._read({})", + name, + discriminator.clone().unwrap_or_else(|| "discriminator".to_string()), + prefix, + py_type.get_name(), + codec + ) + } + Type::Enum { .. } => { + format!( + "{} = {}.read_int32()", + name, + codec, + ) + } + Type::String => { + format!( + "{}{} = {}.read_string()", + indent, + name, + codec, + ) + } + Type::Binary => { + format!( + "{}{} = {}.read_binary()", + indent, + name, + codec, + ) + } + Type::Array { dimension_sizes, .. } => { + let mut res = String::new(); + let current_dim = dimension_sizes[0]; + let next_ident = if dimension_sizes.len() > 1 { format!("{} ", indent) } else { indent.to_string() }; + res += format!("{}{} = []\n", indent, name).as_str(); + res += format!("{}for _i{} in range({}):\n", indent, depth, current_dim).as_str(); + res += format!("{}{}\n", indent, decode_type(shed_array_dimension(py_type), format!("_v{}", depth).as_str(), "", None, codec, next_ident.as_str(), depth+1)).as_str(); + res += format!(" {}{}.append(_v{})", indent, name, depth).as_str(); + res + } + Type::List { element_type} => { + let mut res = String::new(); + res += format!("{}_n{} = {}.start_read_list()\n", indent, depth, codec).as_str(); + res += format!("{}{} = []\n", indent, name).as_str(); + res += format!("{}for _i{} in range(_n{}):\n", indent, depth, depth).as_str(); + res += format!("{}{}\n", indent, decode_type(element_type.clone(), format!("_v{}", depth).as_str(), "", None, codec, format!("{} ", indent).as_str(), depth+1)).as_str(); + res += format!(" {}{}.append(_v{})", indent, name, depth).as_str(); + res + } + Type::Callback { name: cb_name, ..} => { + format!( + "{}{} = {}[{}.read_int8()]", + indent, + name, + cb_name, + codec + ) + } + _ => format!("{}{} = {}.read_{}()", indent, name, codec, py_type.get_name()) + } +} + +pub fn encode_type(py_type: Rc, name: &str, prefix: &str, discriminator: Option, codec: &str, indent: &str, depth: usize) -> String { + match py_type.as_ref() { + Type::Struct { .. } => { + format!( + "{}._write({})", + name, + codec + ) + } + Type::Union { ..} => { + format!( + "{}{}.write({}, {})", + indent, + name, + codec, + discriminator.clone().unwrap_or_else(|| "discriminator".to_string()), + ) + } + Type::Enum { .. } => { + format!( + "{}{}.write_int32({})", + indent, + codec, + name, + ) + } + Type::String => { + format!( + "{}{}.write_string({})", + indent, + codec, + name, + ) + } + Type::Binary => { + format!( + "{}{}.write_binary({})", + indent, + codec, + name, + ) + } + Type::Array { dimension_sizes, .. } => { + let mut res = String::new(); + let next_ident = if dimension_sizes.len() > 1 { format!("{} ", indent) } else { indent.to_string() }; + res += format!("{}for _i{} in {}:\n", indent, depth, name).as_str(); + res += format!("{}{}", indent, encode_type(shed_array_dimension(py_type), format!("_i{}", depth).as_str(), "", discriminator, codec, next_ident.as_str(), depth+1)).as_str(); + res + } + Type::List { element_type} => { + let mut res = String::new(); + res += format!("{}{}.start_write_list(len({}))\n", indent, codec, name).as_str(); + res += format!("{}for _i{} in {}:\n", indent, depth, name).as_str(); + res += format!("{}{}", indent, encode_type(element_type.clone(), format!("_i{}", depth).as_str(), "", discriminator, codec, format!("{} ", indent).as_str(), depth+1)).as_str(); + res + } + Type::Callback { name: cb_name, ..} => { // {$codec}.write_int8({$ info.tableName}.index({$name})){%>%} + format!( + "{}{}.write_int8({}.index({}))", + indent, + codec, + cb_name, + name + ) + } + _ => format!("{}{}.write_{}({})", indent, codec, py_type.get_name(), name) + } +} + +pub fn format_function_prototype_py(func: &PyFunction) -> String { + let mut res = String::new(); + res += format!("{}(self, ", func.name).as_str(); + res += format_inner_parameters_py(func).as_str(); + res += ")"; + res +} + +pub fn format_inner_parameters_py(func: &PyFunction) -> String { + func.parameters.iter().filter_map(|p| { + if p.is_length_for.is_some() { + None + } else { + Some(p.name.clone()) + } + } ).collect::>().join(", ") +} + +pub fn format_param_name(name: &str, param_direction: &ParamDirection) -> String { + match param_direction { + ParamDirection::In => name.to_string(), + ParamDirection::Out => format!("{}.value", name), + ParamDirection::InOut => format!("{}.value", name), + } +} + +pub fn remove_string_quotes(s: &str) -> String { + s.replace('"', "") +} diff --git a/erpcgen_rust/src/generator/python/util.rs b/erpcgen_rust/src/generator/python/util.rs new file mode 100644 index 00000000..f7832a33 --- /dev/null +++ b/erpcgen_rust/src/generator/python/util.rs @@ -0,0 +1,404 @@ +use std::collections::{HashMap, HashSet}; +use std::ops::Deref; +use std::rc::Rc; +use crate::generator::python::symbols::py_callback::PyCallback; +use crate::generator::python::symbols::py_union::PyUnionCase; +use crate::symbols::annotations::Annotation; +use crate::symbols::doxygen_comment::DoxygenComment; +use crate::symbols::function_definition::FunctionDefinition; +use crate::symbols::interface_definition::InterfaceDefinition; +use crate::symbols::struct_definition::StructDefinition; +use crate::symbols::struct_member::StructMember; +use crate::symbols::types::Type; +use crate::symbols::union_case::UnionCase; +use crate::symbols::value::Value; + +/// Formats a list of doxygen comments into a single string +/// +/// # Arguments +/// +/// * `doxygen_comments` - A list of doxygen comments +/// +/// # Returns +/// +/// A string containing the formatted comments +pub(crate) fn format_comments(doxygen_comments: Vec) -> String { + let mut formatted_comments = String::new(); + for comment in doxygen_comments { + match comment { + DoxygenComment::SingleLine(c) => { + formatted_comments.push_str(&format!("# {}", c)); + }, + DoxygenComment::MultiLine(c) => { + formatted_comments.push_str("'''\n"); + formatted_comments.push_str(&format!("{}\n", c)); + formatted_comments.push_str("'''\n"); + }, + } + } + formatted_comments +} + +/// Formats a list of values into a single string +/// +/// # Arguments +/// +/// * `value` - A list of annotations +/// +/// # Returns +/// +/// A string containing the formatted values +pub(crate) fn value_to_string(value: Value) -> String { + //use every type of value defined + match value { + Value::Bool(b) => b.to_string(), + Value::Int8(i) => i.to_string(), + Value::Int16(i) => i.to_string(), + Value::Int32(i) => i.to_string(), + Value::Int64(i) => i.to_string(), + Value::Uint8(i) => i.to_string(), + Value::Uint16(i) => i.to_string(), + Value::Uint32(i) => i.to_string(), + Value::UInt64(i) => i.to_string(), + Value::Float(f) => f.to_string(), + Value::Double(d) => d.to_string(), + Value::Enum { value } => value.to_string(), + Value::ReferencedValue { name, prefix, .. } => if let Some(p) = prefix { format!("{}.{}", p, name) } else { name }, + Value::String(s) => s, + _ => String::new(), // EXTENSION POINT: Add more types + } +} + + +/// Checks if a member is a length for a struct +/// +/// # Arguments +/// +/// * `struct_definition` - The struct definition +/// * `member_name` - The member name +/// +/// # Returns +/// +/// The name of the member that the length is for +pub(crate) fn is_length_for(struct_definition: &StructDefinition, member_name: &String) -> Option { + struct_definition.members.iter().find_map(|m| { + if let StructMember::DataMember(d) = m { + d.annotations.iter().find_map(|a| { + match a { + Annotation::Length { length, .. } => if let Value::RuntimeValue { name } = length { + if name == member_name { + Some(d.name.clone()) + } else { + None + } + } else { + None + }, + _ => None + } + }) + } else { + None + } + }) +} + +/// Checks if a member is a length for a union case member +/// +/// # Arguments +/// +/// * `union_case_member` - The union case member +/// * `member_name` - The member name +/// +/// # Returns +/// +/// The name of the member that the length is for +pub fn is_length_for_union_case_member(union_case_member: &UnionCase, member_name: &String) -> Option { + union_case_member.members.iter().find_map(|m| { + m.annotations.iter().find_map(|a| { + match a { + Annotation::Length { length, .. } => if let Value::RuntimeValue { name } = length { + if name == member_name { + Some(m.name.clone()) + } else { + None + } + } else { + None + }, + _ => None + } + }) + }) +} + +/// Checks if a member is a length for a function +/// +/// # Arguments +/// +/// * `function` - The function definition +/// * `member_name` - The member name +/// +/// # Returns +/// +/// The name of the member that the length is for +pub fn is_length_for_function(function: &FunctionDefinition, member_name: &String) -> Option { + function.parameters.iter().find_map(|p| { + p.annotations.iter().find_map(|a| { + match a { + Annotation::Length { length, .. } => if let Value::RuntimeValue { name } = length { + if name == member_name { + Some(p.name.clone().expect("Parameter name must be set")) + } else { + None + } + } else { + None + }, + _ => None + } + }) + }) +} + +/// Checks if a member is a discriminator for a struct +/// +/// # Arguments +/// +/// * `struct_definition` - The struct definition +/// * `member_name` - The member name +/// +/// # Returns +/// +/// The name of the member that the discriminator is for +pub(crate) fn is_discriminator_for(struct_definition: &StructDefinition, member_name: &String) -> Option { + // Look for encapsulated unions + let res = struct_definition.members.iter().find_map(|m| { + if let StructMember::UnionDefinition { discriminator: Value::RuntimeValue { name}, .. } = m { + if name == member_name { + Some(name.clone()) + } else { + None + } + } else { + None + } + }); + + if res.is_some() { + return res; + } + // Look for unions + struct_definition.members.iter().find_map(|m| { + if let StructMember::DataMember(d) = m { + d.annotations.iter().find_map(|a| { + match a { + Annotation::Discriminator { value, .. } => if let Value::RuntimeValue { name } = value { + if name == member_name { + Some(name.clone()) + } else { + None + } + } else { + None + }, + _ => None + } + }) + } else { + None + } + }) +} + +/// Checks if a member is a discriminator for a union case member +/// +/// # Arguments +/// +/// * `union_case_member` - The union case member +/// * `member_name` - The member name +/// +/// # Returns +/// +/// The name of the member that the discriminator is for +pub(crate) fn is_discriminator_for_union_case_member(union_case_member: &UnionCase, member_name: &String) -> Option { + union_case_member.members.iter().find_map(|m| { + m.annotations.iter().find_map(|a| { + match a { + Annotation::Discriminator { value, .. } => if let Value::RuntimeValue { name } = value { + if name == member_name { + Some(m.name.clone()) + } else { + None + } + } else { + None + }, + _ => None + } + }) + }) +} + +/// Checks if a member is a discriminator for a function +/// +/// # Arguments +/// +/// * `function` - The function definition +/// * `member_name` - The member name +/// +/// # Returns +/// +/// The name of the member that the discriminator is for +pub(crate) fn is_discriminator_for_function(function: &FunctionDefinition, member_name: &String) -> Option { + function.parameters.iter().find_map(|p| { + p.annotations.iter().find_map(|a| { + match a { + Annotation::Discriminator { value, .. } => if let Value::RuntimeValue { name } = value { + if name == member_name { + Some(p.name.clone().expect("Parameter name must be set")) + } else { + None + } + } else { + None + }, + _ => None + } + }) + }) +} + +/// Finds the name of the discriminator for a struct +/// +/// # Arguments +/// +/// * `annotations` - A list of annotations +/// +/// # Returns +/// +/// The name of the discriminator +pub(crate) fn find_discriminator_name(annotations: &[Annotation]) -> Option { + annotations.iter().find_map(|a| { + match a { + Annotation::Discriminator { value, .. } => if let Value::RuntimeValue {name} = value { + Some(name.clone()) + } else { + None + }, + _ => None + } + }) +} + +/// Finds includes in a list of interfaces and program annotations +/// +/// # Arguments +/// +/// * `interfaces` - A list of interfaces +/// * `program_annotations` - A list of annotations +/// +/// # Returns +/// +/// * A list of include paths +pub(crate) fn find_includes(interfaces: &Vec, program_annotations: &Vec) -> Vec { + let mut includes = vec![]; + for interface in interfaces { + for annotation in &interface.annotations { + if let Annotation::Include { path, .. } = annotation { + includes.push(path.clone()); + } + } + } + for annotation in program_annotations { + if let Annotation::Include { path, .. } = annotation { + includes.push(path.clone()); + } + } + includes +} + +/// Finds external annotations in a list of annotations +/// +/// # Arguments +/// +/// * `annotations` - A list of annotations +/// +/// # Returns +/// +/// * True if an external annotation is found, false otherwise +pub(crate) fn find_external_annotation(annotations: &[Annotation]) -> bool { + annotations.iter().any(|a| { + matches!(a, Annotation::External {..}) + }) +} + +/// Reorders cases so that the default case is last +/// +/// # Arguments +/// +/// * `list` - A list of union cases +/// +/// # Returns +/// +/// * A list of union cases with the default case last +pub(crate) fn reorder_cases_default_last(list: Vec) -> Vec { + let default_case = list.iter().find(|c| c.case_values.is_empty()).cloned(); + let mut rest = list.iter().filter(|c| !c.case_values.is_empty()).cloned().collect::>(); + if let Some(default_case) = default_case { + rest.push(default_case); + } + rest +} + +/// Finds used callbacks for a group +/// +/// # Arguments +/// +/// * `group_name` - The name of the group +/// * `interfaces` - A list of interfaces +/// * `groups` - A map of groups +/// +/// # Returns +/// +/// * A list of used callbacks +pub(crate) fn find_used_callbacks_for_group(group_name: &String, interfaces: &Vec, groups: &HashMap>) -> Vec { + let mut used_callbacks: HashSet> = HashSet::new(); + for interface in interfaces { + for function in &interface.functions { + if let Some(prototype) = &function.prototype { + used_callbacks.insert(prototype.clone()); + } + for parameter in &function.parameters { + if let Type::Callback {..} = ¶meter.type_.deref() { + used_callbacks.insert(parameter.type_.clone()); + } + } + } + } + + let mut callbacks: Vec = vec![]; + for callback in used_callbacks { + let mut callback_functions: Vec = vec![]; + for (name, interfaces) in groups { + for interface in interfaces { + for function in &interface.functions { + if let Some(prototype) = &function.prototype { + if prototype == &callback { + if group_name == name { + callback_functions.push(format!("interface.I{}Service.{}", interface.name, function.name)); + } else { + callback_functions.push(format!("interface_{}.I{}Service.{}", group_name, interface.name, function.name)); + } + } + } + } + } + } + let Type::Callback {name, ..} = &callback.deref() else { + panic!("Expected callback type") + }; + callbacks.push(PyCallback::new(name, callback_functions)); + } + callbacks +} diff --git a/erpcgen_rust/src/generator/util.rs b/erpcgen_rust/src/generator/util.rs new file mode 100644 index 00000000..23af2783 --- /dev/null +++ b/erpcgen_rust/src/generator/util.rs @@ -0,0 +1,163 @@ +/* + * Copyright 2024 Marek Mišík(nxf76107) + * Copyright 2024 NXP + * + * SPDX-License-Identifier: BSD-3-Clause + */ + + +use std::collections::{HashMap, HashSet}; +use std::fs::OpenOptions; +use std::io::Write; +use std::path::{PathBuf}; +use log::trace; +use crate::generator::python::templates::util::remove_string_quotes; +use crate::symbols::annotations::Annotation; +use crate::symbols::interface_definition::InterfaceDefinition; +use crate::symbols::language::Language; + +/// Extracts the interface definition rule from the given rules +/// +/// # Arguments +/// +/// * `list` - The list of annotations to search +/// * `language` - The language to search for +/// +/// # Returns +/// +/// The output directory annotation if found, otherwise None +pub(crate) fn find_output_dir_annotation(list: &[Annotation], language: &Language) -> Option { + list.iter().find(|a| { + match a { + Annotation::OutputDir { language_specifier, .. } => + language_specifier.is_none() || language_specifier.as_ref().is_some_and(|l| l == language), + _ => false + } + }).cloned() +} + +/// Finds the group annotation +/// +/// # Arguments +/// +/// * `list` - The list of annotations to search +/// +/// # Returns +/// +/// The group annotation if found, otherwise None +fn find_group_annotation(list: &[Annotation]) -> Option { + list.iter().find(|a| { + matches!(a, Annotation::Group { .. }) + }).cloned() +} + +/// Checks if the given list of annotations contains a CRC annotation +/// +/// # Arguments +/// +/// * `list` - The list of annotations to search +/// * `language` - The language to search for +/// +/// # Returns +/// +/// True if the list contains a CRC annotation, otherwise false +fn has_crc_annotation(list: &[Annotation], language: &Language) -> bool { + list.iter().any(|a| { + match a { + Annotation::Crc { language_specifier, .. } => + language_specifier.is_none() || language_specifier.as_ref().is_some_and(|l| l == language), + _ => false + } + }) +} + +/// Extracts groups from the given list of interface definitions +/// +/// # Arguments +/// +/// * `list` - The list of interface definitions to extract groups from +/// * `base_name` - The base name to use for the group +/// +/// # Returns +/// +/// A map of groups to interface definitions +pub(crate) fn extract_groups(list: Vec, base_name: &str) -> HashMap> { + let mut groups: HashMap> = HashMap::new(); + if list.is_empty() { + groups.insert(base_name.to_string(), list.clone()); + } + for interface in list { + let group = find_group_annotation(&interface.annotations).map(|a| { + match a { + Annotation::Group { group, .. } => remove_string_quotes(&group) + "_" + base_name, + _ => panic!("Unexpected annotation") + } + }).unwrap_or_else(|| base_name.to_string()); + if let Some(vec) = groups.get_mut(&group) { + vec.push(interface); + } else { + groups.insert(group, vec![interface]); + }; + } + groups +} + +/// Writes the given content to the given file +/// +/// # Arguments +/// +/// * `path` - The path to write the content to +/// * `content` - The content to write +pub(crate) fn write_to_file(path: &PathBuf, content: &str) { + trace!("Writing to file: {:?}", path); + let mut file = OpenOptions::new() + .create(true) + .write(true) + .truncate(true) + .open(path) + .unwrap(); + + file.write_all(content.as_bytes()).expect("Unable to write data"); +} + +/// Extracts the includes from the given list of annotations +/// +/// # Arguments +/// +/// * `program_annotations` - The list of program annotations to extract includes from +/// * `interface_definitions` - The interface definitions to extract includes from +/// +/// # Returns +/// +/// A set of includes +pub(crate) fn extract_includes(program_annotations: &Vec, interface_definitions: &InterfaceDefinition) -> HashSet { + let mut includes = HashSet::new(); + for annotation in program_annotations { + if let Annotation::Include { path, .. } = annotation { + includes.insert(path.clone()); + } + } + for annotation in interface_definitions.annotations.iter() { // TODO check whether this works correctly + if let Annotation::Include { path, .. } = annotation { + includes.insert(path.clone()); + } + } + includes +} + +/// Finds nullable annotations +/// +/// # Arguments +/// +/// * `list` - The list of annotations to search +/// * `language` - The language to search for +/// +/// # Returns +/// +/// The nullable annotation if found, otherwise None +pub(crate) fn find_nullable_annotation(list: &[Annotation]) -> bool { + list.iter().any(|a| { + matches!(a, Annotation::Nullable { .. }) + }) +} + diff --git a/erpcgen_rust/src/grammar/idl_grammar.pest b/erpcgen_rust/src/grammar/idl_grammar.pest new file mode 100644 index 00000000..1d925b26 --- /dev/null +++ b/erpcgen_rust/src/grammar/idl_grammar.pest @@ -0,0 +1,238 @@ +// +// Created by intellij-pest on 2023-11-12 +// grammar +// Author: marek +// + +program = _{ SOI ~ definition* ~ EOI } + +definition = _{ import | definition_base } + +definition_base = { #pcd = doxygenPrecedingCommentsOpt ~ #def = (programDefinition | constDefinition | enumDefinition | unionDefinition | unionForwardDefinition | structDefinition | structForwardDefinition | typedefDefinition | interfaceDefinition | callbackDefinitionWithParams) ~ ";"? ~ #tcd = doxygenTrailingCommentsOpt } + +annotation = _{ "@" ~ (typesHeader | sharedMemoryEnd | sharedMemoryBegin | shared | retain | outputDir | nullable | noInfraErrors | noConstParam | noAllocErrors | name | maxLength | length | include | id | group | external | errorReturn | discriminator | crc) } + typesHeader = { (#ls = languageSpecifier ~ ":")? ~ "types_header" ~ lrb ~ #str = string ~ rrb } + sharedMemoryEnd = { (#ls = languageSpecifier ~ ":")? ~ "shared_memory_end" ~ lrb ~ #exp = expression ~ rrb } + sharedMemoryBegin = { (#ls = languageSpecifier ~ ":")? ~ "shared_memory_begin" ~ lrb ~ #exp = expression ~ rrb } + shared = { (#ls = languageSpecifier ~ ":")? ~ "shared" } + retain = { (#ls = languageSpecifier ~ ":")? ~ "retain" } + outputDir = { (#ls = languageSpecifier ~ ":")? ~ "output_dir" ~ lrb ~ #str = string ~ rrb } + nullable = { (#ls = languageSpecifier ~ ":")? ~ "nullable" } + noInfraErrors = { (#ls = languageSpecifier ~ ":")? ~ "no_infra_errors" } + noConstParam = { (#ls = languageSpecifier ~ ":")?~ "no_const_params" } + noAllocErrors = { (#ls = languageSpecifier ~ ":")? ~ "no_alloc_errors" } + name = { (#ls = languageSpecifier ~ ":")? ~ "name" ~ lrb ~ #str = (string | ident) ~ rrb } + maxLength = { (#ls = languageSpecifier ~ ":")?~ "max_length" ~ lrb ~ #exp = expression ~ rrb } + length = { (#ls = languageSpecifier ~ ":")? ~ "length" ~ lrb ~ #exp = expression ~ rrb } + include = { (#ls = languageSpecifier ~ ":")? ~ "include" ~ lrb ~ #str = string ~ rrb } + id = { (#ls = languageSpecifier ~ ":")? ~ "id" ~ lrb ~ decimal ~ rrb } + group = { (#ls = languageSpecifier ~ ":")? ~ "group" ~ lrb ~ #str = string ~ rrb } + external = { (#ls = languageSpecifier ~ ":")? ~ "external" } + errorReturn = { (#ls = languageSpecifier ~ ":")? ~ "error_return" ~ lrb ~ #exp = constExpression ~ rrb } + discriminator = { (#ls = languageSpecifier ~ ":")? ~ "discriminator" ~ lrb ~ #name = ident ~ rrb } + crc = { (#ls = languageSpecifier ~ ":")? ~ "crc" } + +languageSpecifier = { "c" | "py" } + +annotationList = { annotation* } + +programDefinition = { #al = annotationList ~ "program" ~ #name = ident } + +import = { "import" ~ #str = string } + +typedefDefinition = { #al = annotationList ~ "type" ~ #name = ident ~ eq ~ #dt = dataType } + +enumDefinition = { #al = annotationList ~ "enum" ~ #opt_name = ident? ~ lcb ~ #ml = enumMemberList ~ rcb } + +enumMember = { #pc = doxygenPrecedingCommentsOpt ~ #name = ident ~ (eq ~ #exp = expression)? ~ #al = annotationList? ~ ","? ~ #tc = doxygenTrailingCommentsOpt } + +enumMemberList = { enumMember+ } + +structForwardDefinition = { "struct" ~ #name = ident } + +structDefinition = { #al = annotationList ~"struct" ~ #name = ident ~ lcb ~ #ml = structMemberList ~ rcb } + +structMemberList = { (structUnionMember | structDataMember)* } + +structDataMember = { #pc = doxygenPrecedingCommentsOpt ~ #ol = structMemberOptionList ~ #sdt = simpleDataType ~ #name = ident ~ #al = annotationList ~ ";"? ~ #tc = doxygenTrailingCommentsOpt } + +structUnionMember = { #pc = doxygenPrecedingCommentsOpt ~ "union" ~ lrb ~ #disc = ident ~ rrb ~ lcb ~ #cl = unionCaseList ~ rcb ~ #member_name = ident ~ #al = annotationList ~ ";"? ~ #tc = doxygenTrailingCommentsOpt } + +structMemberOptionList = { (byref | optional)* } + +byref = { "byref" } + +optional = { "optional" } + +unionForwardDefinition = { "union" ~ #name = ident } + +unionDefinition = { #al = annotationList ~ "union" ~ #name = ident ~ lcb ~ #cl = unionCaseList ~ rcb } + +unionCaseList = { (unionCase | unionDefaultCase)* } + +unionCase = { case ~ #cl = unionCaseExprList ~ ":" ~ #ml = unionMemberList } + +case = { "case" } + +unionDefaultCase = { #dc = default ~ ":" ~ #ml = unionMemberList } + +default = { "default" } + +unionCaseExprList = { #exp = expression ~ (comma ~ #exp = expression)* } + +unionMemberList = { unionMember* } + +unionMember = { (!"case") ~ #pc = doxygenPrecedingCommentsOpt ~ #ol = structMemberOptionList ~ #sdt = simpleDataType ~ #name = ident ~ #al = annotationList? ~ ";"? ~ #tc = doxygenTrailingCommentsOpt } + +interfaceDefinition = { #al = annotationList ~ "interface" ~ #name = ident ~ lcb ~ #cl = callbackTypeDefinitionList ~ #fl = functionDefinitionList ~ rcb} + +functionDefinitionList = { (functionDefinition | callbackDefinition )* } + +callbackTypeDefinitionList = { callbackTypeDefinition* } + +callbackTypeDefinition = { #pc = doxygenPrecedingCommentsOpt ~ #al = annotationList ~ "type" ~ #def = functionTypeDefinition ~ ";"? ~ #tc = doxygenTrailingCommentsOpt } + +functionDefinition = { #pc = doxygenPrecedingCommentsOpt ~ #al = annotationList ~ #def = functionTypeDefinition ~ ";"? ~ #tc = doxygenTrailingCommentsOpt } + +callbackDefinition = { #pc = doxygenPrecedingCommentsOpt ~ #al = annotationList ~ #def = callbackDefinitionBase ~ ";"? ~ #tc = doxygenTrailingCommentsOpt } + +functionTypeDefinition = { oneWayFunction | returningFunction } + +oneWayFunction = _{ #ow = oneWay ~ #name = ident ~ "(" ~ #pl = paramDefinitionList ~ ")" } + +returningFunction = _{ #name = ident ~ "(" ~ #pl = paramDefinitionList ~ ")" ~ #rt = functionReturnType } + +oneWay = { "oneway" } + +functionReturnType = { "->" ~ (#vo = void | (#al = annotationList ~ #sdt = simpleDataType)) } + +void = { "void" } + +paramDefinitionList = { void | ( #pdf = paramDefinition ~ (comma + ~ #pdf = paramDefinition)*)? } + +paramDefinition = { #pd = paramDirection? ~ #sds = simpleDataTypeScope ~ #name = ident? ~ #al = annotationList } + +paramDirection = _{ paramDirectionIn | paramDirectionOut | paramDirectionInout } + +paramDirectionIn = @{ "in" ~ WHITESPACE } + +paramDirectionOut = @{ "out" ~ WHITESPACE } + +paramDirectionInout = @{ "inout" ~ WHITESPACE } + +callbackDefinitionBase = _{ callbackDefinitionWithParams | callback } + +callbackDefinitionWithParams = { #type = simpleDataTypeScope ~ #name = ident ~ lrb ~ #pl = callbackParamList ~ rrb } + +callback = { #type = simpleDataTypeScope ~ #name = ident } + +callbackParamList = { ( #cbp = ident ~ (comma ~ #cbp = ident)*)? } + +dataType = _{ structDefinition | enumDefinition | simpleDataType } + +simpleDataTypeScope = { (#scp = ident ~ "::")? ~ #sdt = simpleDataType } + +simpleDataType = { arrayType | listType | ident } + +arrayType = { #type = ident ~ #dim = arrayDimension+ } + +listType = { "list" ~ lab ~ #type = ident ~ rab } + +arrayDimension = { lsb ~ #exp = expression ~ rsb } + +constDefinition = { #al = annotationList ~ "const" ~ #sdt = simpleDataType ~ #name = ident ~ eq ~ #ce = constExpression } + +constExpression = { expression | string } + +expression = { primary ~ (binaryOperation ~ expression)* } + +atom = _{ unaryOperation? ~ primary } + +binaryOperation = _{ add | sub | mul | div | md | and | or | xor | shl | shr } + add = { "+" } + sub = { "-" } + mul = { "*" } + div = { "/" } + md = { "%" } + and = { "&" } + or = { "|" } + xor = { "^" } + shl = { "<<" } + shr = { ">>" } + +unaryOperation = _{ nop | neg | inv } + nop = { "+" } + neg = { "-" } + inv = { "~" } + +ident = @{ ASCII_ALPHA ~ (ASCII_ALPHANUMERIC | "_" )* } + +primary = _{ decimal | hexadecimal | binary | floatingPoint | ident | bracedExpression } + +bracedExpression = { lrb ~ #exp = expression ~ rrb } + +decimal = @{ ASCII_DIGIT+ ~ integerSuffix? } + +hexadecimal = @{ "0x" ~ ASCII_HEX_DIGIT+ ~ integerSuffix? } + +binary = @{ "0b" ~ ASCII_BIN_DIGIT+ ~ integerSuffix? } + + +floatingPoint = @{ + decimal_sequence ~ "." ~ decimal_sequence? ~ exponent? + | "." ~ decimal_sequence ~ exponent? +} + +exponent = @{ + ^"e" ~ ("+" | "-")? ~ ASCII_DIGIT+ ~ decimal_sequence +} + +decimal_sequence = @{ ASCII_DIGIT+ } + +integerSuffix = @{ ull | ul | u } + u = { ^"u" } + ul = { ^"ul" } + ull = { ^"ull" } + +string = @{ "\"" ~ ( "\"\"" | (!"\"" ~ ANY) )* ~ "\"" } + +doxygenPrecedingCommentsOpt = { (doxygenMultilineCommentPreceding | doxygenSingleLineCommentPreceding)* } + +doxygenMultilineCommentPreceding = _{ ("/**" | "/*!" ) ~ #ct = multilineCommentContent ~ "*/" } + +doxygenMultilineCommentTrailing = _{ ("/**<" | "/*!<" ) ~ #ct = multilineCommentContent ~ "*/" } + +multilineCommentContent = { (!"*/" ~ ANY)* } + +doxygenTrailingCommentsOpt = { (doxygenMultilineCommentTrailing | doxygenSingleLineCommentTrailing)* } + +doxygenSingleLineCommentPreceding = _{ ("///" | "//!") ~ #ct = singleLineCommentContent } + +doxygenSingleLineCommentTrailing = _{ ("///<" | "//!<") ~ #ct = singleLineCommentContent } + +singleLineCommentContent = @{ (!NEWLINE ~ ANY)* ~ NEWLINE } + +lcb = { "{" } + +rcb = { "}" } + +lsb = { "[" } + +rsb = { "]" } + +lrb = { "(" } + +rrb = { ")" } + +eq = { "=" } + +lab = { "<" } + +rab = { ">" } + +comma = { "," } + +WHITESPACE = _{ " " | "\t" | "\r\n" | "\n" } + +COMMENT = _{ (( !"//!" ~ !"///" ~ "//") ~ (!NEWLINE ~ ANY)*) | ( !"/**" ~ !"/*!" ~ "/*" ~ (!"*/" ~ ANY)* ~ "*/") } \ No newline at end of file diff --git a/erpcgen_rust/src/main.rs b/erpcgen_rust/src/main.rs new file mode 100644 index 00000000..8571fd50 --- /dev/null +++ b/erpcgen_rust/src/main.rs @@ -0,0 +1,76 @@ +/* + * Copyright 2024 Marek Mišík(nxf76107) + * Copyright 2024 NXP + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +use std::path::PathBuf; +use std::str::FromStr; +use structopt::StructOpt; +use crate::generator::python::python_generator::PythonGenerator; +use crate::options::Options; +use crate::symbols::annotations::Annotation; +use crate::symbols::language::Language; + +pub(crate) mod options; +pub(crate) mod parser; +pub(crate) mod symbols; +pub mod generator; + +/// eRPC version +pub const ERPC_VERSION_STR: &str = "1.11.0"; + +/// Main function +fn main() { + let options = Options::from_args(); + + let loglevel = match options.verbose { + 0 => log::LevelFilter::Error, + 1 => log::LevelFilter::Info, + 2 => log::LevelFilter::Debug, + _ => log::LevelFilter::Trace, + }; + + fern::Dispatch::new() + .format(|out, message, _| { + out.finish(format_args!( + "{}", + message + )) + }) + .level(loglevel) + .chain(std::io::stdout()) + .apply() + .unwrap(); + + let language = options.language.unwrap_or(Language::Python); + + if language == Language::C || language == Language::Java { + log::error!("Language not supported yet"); + return; + } + + let mut parser = parser::erpc_parser::ErpcParser::new(language.clone()); + if options.strict { + parser.strict(); + } + + parser.parse_idl_file(options.input.as_os_str().to_str().unwrap_or_default()); + + let program = parser.program; + let crc = if program.annotations.iter().any(|a| { + matches!(a, Annotation::Crc { .. }) + }) { + Some(parser.crc16.finalize()) + } else { + None + }; + + let generator = match language { + Language::Python => PythonGenerator::new(program, crc, PathBuf::from_str(&options.output.unwrap_or_default()).unwrap()), + _ => panic!("Language not supported yet") + }; + + generator.generate(); +} diff --git a/erpcgen_rust/src/options.rs b/erpcgen_rust/src/options.rs new file mode 100644 index 00000000..8959d203 --- /dev/null +++ b/erpcgen_rust/src/options.rs @@ -0,0 +1,98 @@ +/* + * Copyright 2024 Marek Mišík(nxf76107) + * Copyright 2024 NXP + * + * SPDX-License-Identifier: BSD-3-Clause + */ +use std::ffi::OsString; +use std::path::PathBuf; +use std::str::FromStr; +use structopt::StructOpt; +use structopt::clap::AppSettings:: {ArgRequiredElseHelp}; +use crate::symbols::language::Language; + + +/// Implementation of FromStr for Language +impl FromStr for Language { + type Err = &'static str; + + fn from_str(s: &str) -> Result { + match s { + "C" => Ok(Language::C), + "Rust" | "rs" => Ok(Language::Rust), + "Python" | "py" => Ok(Language::Python), + "Java" => Ok(Language::Java), + _ => Err("no match"), + } + } +} + +/// Codec type +#[derive(Debug, PartialEq)] +pub enum Codec { + BasicCodec +} + +/// Implementation of FromStr for Codec +impl FromStr for Codec { + type Err = &'static str; + + fn from_str(s: &str) -> Result { + match s { + "basic" => Ok(Codec::BasicCodec), + _ => Err("no match"), + } + } +} + + +/// Command line options +#[derive(StructOpt, Debug)] +#[structopt( + version="0.0.1", + settings = &[ArgRequiredElseHelp], + after_help = r#" + Available languages (use with -g option): + c C/C++ + py Python + Available codecs (use with --c option): + basic BasicCodec + ; + "# +)] +pub(crate) struct Options { + /*#[structopt( + short = "h", long = "help", + help = "Show this custom help message", + takes_value = false + )] + pub(crate) help: bool,*/ + + /// Set output directory path prefix + #[structopt(short = "o", long = "output", value_name = "filePath")] + pub(crate) output: Option, + + /// Print extra detailed log information + #[structopt(short = "v", long = "verbose", parse(from_occurrences))] + pub(crate) verbose: u8, + + /// Strict mode (treat warnings as errors) + #[structopt(short = "s", long = "strict", parse(from_flag))] + pub(crate) strict: bool, + + /// Add search path for imports + #[structopt(short = "I", long = "path", value_name = "filePath", parse(from_os_str), number_of_values = 1, multiple = true)] + pub(crate) path: Vec, + + /// Select the output language (default is C) + #[structopt(short = "g", long = "generate")] + pub(crate) language: Option, + + /// Specify used codec type + #[structopt(short = "c", long = "codec")] + pub(crate) codec: Option, + + /// The input file to use + #[structopt(parse(from_os_str), hidden = true)] + pub(crate) input: PathBuf, +} \ No newline at end of file diff --git a/erpcgen_rust/src/parser/erpc_parser.rs b/erpcgen_rust/src/parser/erpc_parser.rs new file mode 100644 index 00000000..481df89e --- /dev/null +++ b/erpcgen_rust/src/parser/erpc_parser.rs @@ -0,0 +1,978 @@ +/* + * Copyright 2024 Marek Mišík(nxf76107) + * Copyright 2024 NXP + * + * SPDX-License-Identifier: BSD-3-Clause + */ + + +use std::path::PathBuf; + +use crate::parser::grammar_parser::{IDLParser, Rule}; + +use crate::parser::util::common::{extract_annotations_list_rule_tag, extract_cases_list, extract_doxygen_definition_comments_preceding, extract_doxygen_definition_comments_trailing, extract_doxygen_member_comments_preceding, extract_doxygen_member_comments_trailing, extract_ident_name_tag, extract_ident_opt_name_tag, extract_ident_type_tag, extract_member_list, extract_member_name_tag, extract_simple_data_type, extract_string_rule}; +use crate::parser::util::const_definition::{ + extract_const_expression_rule, +}; +use crate::parser::util::expression::{ extract_expression_rule_tag, +}; +use crate::parser::util::interface_definition::{ + extract_callback_type_definition_list_rule, extract_definition, + extract_function_definition_list_rule, + extract_oneway_rule, extract_param_direction, extract_parameter_definition_list, extract_parameter_list_rule, extract_return_type_rule, + extract_simple_data_type_scope, +}; +use crate::parser::util::struct_definition::{ + extract_discriminator_rule, extract_options_list, +}; + +use crate::symbols::const_definition::ConstDefinitionBuilder; +use crate::symbols::enum_definition::EnumDefinitionBuilder; +use crate::symbols::enum_member::{EnumMember, EnumMemberBuilder}; +use crate::symbols::function_definition::{FunctionDefinition, FunctionDefinitionBuilder}; +use crate::symbols::function_parameter::{FunctionParameter, FunctionParameterBuilder}; +use crate::symbols::interface_definition::InterfaceDefinitionBuilder; + +use crate::symbols::program::{Program, ProgramBuilder}; +use crate::symbols::struct_data_member::{StructDataMemberBuilder}; +use crate::symbols::struct_definition::{StructDefinitionBuilder}; +use crate::symbols::struct_member::StructMember; +use crate::symbols::type_definition::TypeDefinitionBuilder; + + +use crate::symbols::union_case::{UnionCase, UnionCaseBuilder}; +use crate::symbols::union_case_member::{UnionCaseMember, UnionCaseMemberBuilder}; +use crate::symbols::union_definition::{UnionDefinitionBuilder}; + + + +use log::{debug, error, info, trace}; +use pest::error::{Error, ErrorVariant}; +use pest::iterators::{Pair, Pairs}; +use pest::{Parser, Position}; + +use crate::parser::forward_definition_record::ForwardDefinitionRecord; +use crate::parser::util::type_definition::extract_data_type_rule; +use crate::symbols::doxygen_comment::DoxygenComment; +use crate::symbols::language::Language; +use crate::symbols::pending_ident::{PendingError, PendingIdent}; +use crc::{Crc, Digest, Algorithm}; +use std::process::exit; +use crate::parser::util::union_definition::find_default_rule; + +type ParserResult<'a> = Result, Error>; + + +const ALTERED_KERMIT : Algorithm = Algorithm {width: 16, poly: 0x1021, init: 0xEF4A, xorout: 0x0000, refin: true, refout: false, check: 35244, residue: 0x0000}; +const CRC: Crc = Crc::::new(&ALTERED_KERMIT); + +pub(crate) struct ErpcParser { + pub(crate) program: Box, + pub(crate) file_path_stack: Vec, + pub(crate) crc16: Digest<'static, u16>, + forward_declarations: Vec, + strict: bool, +} + +impl<'a> ErpcParser { + pub(crate) fn new(language: Language) -> Self { + Self { + program: Box::new(Program::new(language)), + file_path_stack: vec![], + crc16: CRC.digest(), + forward_declarations: vec![], + strict: false, + } + } + + pub(crate) fn strict(&mut self) { + self.strict = true; + } + + pub(crate) fn parse_idl_file(&mut self, file_path: &str) { + let content = self.read_file_into_string(file_path); + self.crc16.update(content.as_bytes()); + if self.file_path_stack.is_empty() { // Set the name of the program preemptively + let path = PathBuf::from(file_path); + self.program.name = Some(path + .file_name() + .unwrap_or_default() + .to_string_lossy() + .strip_suffix(".erpc") + .unwrap_or_default() + .to_string() + ); + } + self.file_path_stack.push(file_path.to_string()); + let parser_result = self.handle_parser_error(IDLParser::parse(Rule::program, &content)); + for pair in parser_result { + self.parse_definition_or_import(pair); + } + if self.file_path_stack.len() == 1 { + self.check_forward_declarations_satisfied(); + self.program.apply_renames(); + } + self.file_path_stack.pop(); + } + + pub(crate) fn check_forward_declarations_satisfied(&self) { + let Some(unresolved) = self.forward_declarations.first() else { + return; + }; + let error = self.restore_error_from_unresolved_forward_declaration(unresolved); + self.handle_error(error); + } + + fn parse_definition_or_import(&mut self, pair: Pair) { + match pair.as_rule() { + Rule::definition_base => { + self.parse_definition(pair); + } + Rule::import => { + if let Err(e) = self.parse_import(pair) { + self.handle_error(e); + } + } + Rule::EOI => { + info!("End of input file: {:?}", self.file_path_stack.last()); + } + _ => { + error!("Error: {:?} is not expected", pair); + panic!("Unexpected rule"); + } + } + } + + fn parse_definition(&mut self, pair: Pair<'_, Rule>) { + let rules = pair.into_inner(); + + let doxygen_preceding_comment = extract_doxygen_definition_comments_preceding(&rules); + let doxygen_trailing_comment = extract_doxygen_definition_comments_trailing(&rules); + let definition_rule = extract_definition(&rules); + + let unwrapped = definition_rule.expect("Expected definition rule"); + + let res: Result<(), Box>> = match unwrapped.as_rule() { + Rule::programDefinition => self.parse_program_definition( + unwrapped, + doxygen_preceding_comment, + doxygen_trailing_comment, + ), + Rule::constDefinition => self.parse_const_definition( + unwrapped, + doxygen_preceding_comment, + doxygen_trailing_comment, + ), + Rule::structDefinition => self.parse_struct_definition( + unwrapped, + doxygen_preceding_comment, + doxygen_trailing_comment, + ), + Rule::structForwardDefinition => self.parse_struct_forward_definition(unwrapped), + Rule::enumDefinition => self.parse_enum_definition( + unwrapped, + doxygen_preceding_comment, + doxygen_trailing_comment, + ), + Rule::unionDefinition => self.parse_union_definition( + unwrapped, + doxygen_preceding_comment, + doxygen_trailing_comment, + ), + Rule::unionForwardDefinition => self.parse_union_forward_definition(unwrapped), + Rule::typedefDefinition => self.parse_type_definition( + unwrapped, + doxygen_preceding_comment, + doxygen_trailing_comment, + ), + Rule::interfaceDefinition => self.parse_interface_definition( + unwrapped, + doxygen_preceding_comment, + doxygen_trailing_comment, + ), + _ => { + error!("Error: {:?}", unwrapped); + panic!("Unexpected rule"); + } + }; + + match res { + Ok(_) => {} + Err(e) => self.handle_error(e), + } + } + + fn parse_import(&mut self, pair: Pair) -> Result<(), Box>> { + match pair.as_rule() { + Rule::import => self.handle_import(pair), + _ => { + error!("Error: {:?}", pair); + panic!("Unexpected rule"); + } + } + } + + fn handle_import(&mut self, path: Pair<'_, Rule>) -> Result<(), Box>> { + info!("Parsing import"); + let rules = path.into_inner(); + let path = extract_string_rule(&rules).expect("Expected path").as_str(); + self.parse_idl_file(path); + Ok(()) + } + + fn parse_program_definition( + &mut self, + program_definition_rule: Pair<'_, Rule>, + doxygen_preceding_comment: Vec, + doxygen_trailing_comment: Vec, + ) -> Result<(), Box>> { + info!("Parsing program definition"); + let program_definition_rules = program_definition_rule.into_inner(); + + ProgramBuilder::new(&mut self.program, self.strict) + .with_doxygen_preceding_comment(doxygen_preceding_comment) + .with_doxygen_trailing_comment(doxygen_trailing_comment) + .with_annotations( + extract_annotations_list_rule_tag(&program_definition_rules) + .expect("Expected annotations"), + ) + .and_then(|b| { + b.with_name( + extract_ident_name_tag(&program_definition_rules) + .expect("Expected program name"), + ) + }) + .map(|b| b.build())? + } + + fn parse_const_definition( + &mut self, + const_definition_rule: Pair<'_, Rule>, + doxygen_preceding_comment: Vec, + doxygen_trailing_comment: Vec, + ) -> Result<(), Box>> { + info!("Parsing const definition"); + + let rules = const_definition_rule.into_inner(); + + let definition = ConstDefinitionBuilder::new(&self.program, self.strict) + .with_doxygen_preceding_comment(doxygen_preceding_comment) + .with_doxygen_trailing_comment(doxygen_trailing_comment) + .with_type(extract_simple_data_type(&rules).expect("Expected simple data type")) + .and_then(|b| { + b.with_annotations( + extract_annotations_list_rule_tag(&rules).expect("Expected annotations"), + ) + }) + .and_then(|b| b.with_name(extract_ident_name_tag(&rules).expect("Expected const name"))) + .and_then(|b| { + b.with_value( + extract_const_expression_rule(&rules).expect("Expected const expression"), + ) + }) + .map(|b| b.build())?; + + self.program.add_const_definition(definition); + Ok(()) + } + + fn parse_type_definition( + &mut self, + type_definition_rule: Pair<'_, Rule>, + doxygen_preceding_comment: Vec, + doxygen_trailing_comment: Vec, + ) -> Result<(), Box>> { + info!("Parsing type definition"); + let rules = type_definition_rule.into_inner(); + + let data_type = extract_data_type_rule(&rules).expect("Expected data type"); + + match data_type.as_rule() { + Rule::structDefinition => { + self.parse_struct_definition(data_type, vec![], vec![])?; + } + Rule::enumDefinition => { + self.parse_enum_definition(data_type, vec![], vec![])?; + } + _ => { + // Do nothing + } + } + + let definition = TypeDefinitionBuilder::new(&self.program, self.strict) + .with_doxygen_preceding_comment(doxygen_preceding_comment) + .with_doxygen_trailing_comment(doxygen_trailing_comment) + .with_annotations( + extract_annotations_list_rule_tag(&rules).expect("Expected annotations"), + ) + .and_then(|b| b.with_name(extract_ident_name_tag(&rules).expect("Expected type name"))) + .and_then(|b| { + let data_type = extract_data_type_rule(&rules).expect("Expected data type"); + match data_type.as_rule() { + Rule::simpleDataType => b.with_simple_type(data_type), + Rule::structDefinition => b.with_latest_struct_definition(), + Rule::enumDefinition => b.with_latest_enum_definition(), + _ => { + error!("Error: {:?}", data_type); + panic!("Unexpected rule"); + } + } + }) + .map(|b| b.build())?; + + self.program.add_type_definition(definition); + Ok(()) + } + + fn parse_struct_forward_definition( + &mut self, + struct_forward_definition_rule: Pair<'_, Rule>, + ) -> Result<(), Box>> { + info!("Parsing struct forward definition"); + let rules = struct_forward_definition_rule.into_inner(); + let ident = extract_ident_name_tag(&rules).expect("Expected struct name"); + self.handle_forward_declaration(ident.clone()); + self.program.add_struct_forward_definition(ident) + } + + fn parse_struct_definition( + &mut self, + struct_definition_rule: Pair<'_, Rule>, + doxygen_preceding_comment: Vec, + doxygen_trailing_comment: Vec, + ) -> Result<(), Box>> { + info!("Parsing struct definition"); + + let rules = struct_definition_rule.into_inner(); + + let mut definition = StructDefinitionBuilder::new(&self.program, self.strict) + .with_doxygen_preceding_comment(doxygen_preceding_comment) + .with_doxygen_trailing_comment(doxygen_trailing_comment) + .with_annotations( + extract_annotations_list_rule_tag(&rules).expect("Expected annotations"), + ) + .and_then(|b| { + b.with_name(extract_ident_name_tag(&rules).expect("Expected struct name")) + }) + .and_then(|mut b| { + let members_list_rule = + extract_member_list(&rules).expect("Expected struct members."); + let member_rules = members_list_rule.into_inner(); + for member_rule in member_rules { + match member_rule.as_rule() { + Rule::structDataMember => { + let member_builder = b.new_member_builder(self.strict); + let (member, pending) = + self.parse_struct_data_member(member_rule, member_builder)?; + b.add_member(member, pending); + } + Rule::structUnionMember => { + let (member, pending) = self.parse_encapsulated_union(member_rule, &b)?; + b.add_member(member, pending); + } + _ => { + error!("Error: {:?}", member_rule); + panic!("Unexpected rule"); + } + } + } + Ok(b) + })?; + + match definition.resolve_pending_idents() { + Ok(_) => {} + Err(e) => return Err(self.restore_error_from_unresolved_ident(e)), + } + + let definition = definition.build()?; + + self.handle_resolve_forward_declaration(&definition.name); + self.program.add_struct_definition(definition); + + Ok(()) + } + + fn parse_struct_data_member( + &self, + struct_member_rule: Pair<'_, Rule>, + builder: StructDataMemberBuilder<'a>, + ) -> Result<(StructMember, Vec), Box>> { + info!("Parsing struct member"); + let rules = struct_member_rule.into_inner(); + builder + .with_doxygen_preceding_comment(extract_doxygen_member_comments_preceding(&rules)) + .with_doxygen_trailing_comment(extract_doxygen_member_comments_trailing(&rules)) + .with_options(extract_options_list(&rules).expect("Expected options")) + .and_then(|b| { + b.with_type(extract_simple_data_type(&rules).expect("Expected simple data type")) + }) + .and_then(|b| { + b.with_name(extract_ident_name_tag(&rules).expect("Expected struct member name")) + }) + .and_then(|b| { + b.with_annotations( + extract_annotations_list_rule_tag(&rules).expect("Expected annotations"), + ) + }) + .map(|b| b.build()) + } + + fn parse_union_forward_definition( + &mut self, + struct_forward_definition_rule: Pair<'_, Rule>, + ) -> Result<(), Box>> { + info!("Parsing union forward definition"); + let rules = struct_forward_definition_rule.into_inner(); + let ident = extract_ident_name_tag(&rules).expect("Expected union name"); + self.handle_forward_declaration(ident.clone()); + self.program.add_union_forward_definition(ident) + } + + fn parse_encapsulated_union( + &self, + struct_union_member_rule: Pair, + struct_builder: &StructDefinitionBuilder, + ) -> Result<(StructMember, Vec), Box>> { + info!("Parsing encapsulated union"); + + let rules = struct_union_member_rule.into_inner(); + + let union_builder = struct_builder.new_union_builder(self.strict); + + let (disc, pending) = struct_builder.get_discriminator_by_ident( + extract_discriminator_rule(&rules).expect("Expected discriminator ident"), + )?; + + let builder = union_builder + .with_doxygen_preceding_comment(extract_doxygen_member_comments_preceding(&rules)) + .with_doxygen_trailing_comment(extract_doxygen_member_comments_trailing(&rules)) + .with_name(extract_member_name_tag(&rules).expect("Expected union name")) + .and_then(|mut b| { + let cases_list_rule = extract_cases_list(&rules).expect("Expected union cases."); + let case_rules = cases_list_rule.into_inner(); + for case_rule in case_rules { + let case_builder = b.new_case_builder(); + match self.parse_union_case(case_rule, case_builder) { + Ok(case) => { + b.with_case(case); + } + Err(e) => return Err(e), + }; + } + Ok(b) + }) + .and_then(|b| { + b.with_annotations( + extract_annotations_list_rule_tag(&rules).expect("Expected annotations"), + ) + })?; + + Ok((builder.build_encapsulated(disc), pending)) + } + + fn parse_enum_definition( + &'_ mut self, + enum_definition_rule: Pair<'_, Rule>, + doxygen_preceding_comment: Vec, + doxygen_trailing_comment: Vec, + ) -> Result<(), Box>> { + info!("Parsing enum definition"); + + let rules = enum_definition_rule.into_inner(); + let definition = EnumDefinitionBuilder::new(&self.program, self.strict) + .with_doxygen_preceding_comment(doxygen_preceding_comment) + .with_doxygen_trailing_comment(doxygen_trailing_comment) + .annotations(extract_annotations_list_rule_tag(&rules).expect("Expected annotations")) + .and_then(|b| b.with_name(extract_ident_opt_name_tag(&rules))) + .and_then(|mut b| { + let members_list_rule = + extract_member_list(&rules).expect("Expected enum members."); + let member_rules = members_list_rule.into_inner(); + for member_rule in member_rules { + let member_builder = b.new_member_builder(); + match self.parse_enum_member(member_rule, member_builder) { + Ok(member) => { + b.add_enum_member(member); + } + Err(e) => return Err(e), + }; + } + Ok(b) + }) + .map(|b| b.build())?; + + self.program.add_enum_definition(definition); + Ok(()) + } + + fn parse_enum_member( + &self, + enum_member_rule: Pair<'_, Rule>, + builder: EnumMemberBuilder<'a>, + ) -> Result>> { + info!("Parsing enum member"); + let rules = enum_member_rule.into_inner(); + builder + .with_doxygen_preceding_comment(extract_doxygen_member_comments_preceding(&rules)) + .with_doxygen_trailing_comment(extract_doxygen_member_comments_trailing(&rules)) + .with_name(extract_ident_name_tag(&rules).expect("Expected enum member name")) + .and_then(|b| { + if let Some(r) = extract_expression_rule_tag(&rules) { + b.with_value(r) + } else { + Ok(b) + } + }) + .and_then(|b| { + b.annotations( + extract_annotations_list_rule_tag(&rules).expect("Expected annotations"), + ) + }) + .map(|b| b.build()) + } + + fn parse_union_definition( + &'_ mut self, + union_definition_rule: Pair<'_, Rule>, + doxygen_preceding_comment: Vec, + doxygen_trailing_comment: Vec, + ) -> Result<(), Box>> { + info!("Parsing union definition"); + + let rules = union_definition_rule.into_inner(); + let definition = UnionDefinitionBuilder::new(&self.program, self.strict) + .with_doxygen_preceding_comment(doxygen_preceding_comment) + .with_doxygen_trailing_comment(doxygen_trailing_comment) + .with_annotations( + extract_annotations_list_rule_tag(&rules).expect("Expected annotations"), + ) + .and_then(|b| b.with_name(extract_ident_name_tag(&rules).expect("Expected union name"))) + .and_then(|mut b| { + let cases_list_rule = extract_cases_list(&rules).expect("Expected union cases."); + let case_rules = cases_list_rule.into_inner(); + for case_rule in case_rules { + let case_builder = b.new_case_builder(); + match self.parse_union_case(case_rule, case_builder) { + Ok(case) => { + b.with_case(case); + } + Err(e) => return Err(e), + }; + } + Ok(b) + }) + .map(|b| b.build())?; + + self.handle_resolve_forward_declaration(&definition.name); + self.program.add_union_definition(definition); + Ok(()) + } + + fn parse_union_case( + &self, + union_case_rule: Pair<'_, Rule>, + builder: UnionCaseBuilder<'a>, + ) -> Result>> { + info!("Parsing union case"); + let rules = union_case_rule.into_inner(); + let mut builder_unwrapped = builder + .check_if_default_case(find_default_rule(&rules)) + .and_then(|b | b.with_case_values(extract_cases_list(&rules))) + .and_then(|mut b| { + let members_list_rule = + extract_member_list(&rules).expect("Expected union case members."); + let member_rules = members_list_rule.into_inner(); + for member_rule in member_rules { + let member_builder = b.new_member_builder(); + let (member, pending) = + self.parse_union_case_member(member_rule, member_builder)?; + b.with_member(member, pending); + } + Ok(b) + })?; + + match builder_unwrapped.resolve_pending_idents() { + Ok(_) => {} + Err(e) => return Err(self.restore_error_from_unresolved_ident(e)), + } + + builder_unwrapped.build() + } + + fn parse_union_case_member( + &self, + union_case_member_rule: Pair<'_, Rule>, + builder: UnionCaseMemberBuilder<'a>, + ) -> Result<(UnionCaseMember, Vec), Box>> { + info!("Parsing enum member"); + let rules = union_case_member_rule.into_inner(); + builder + .with_doxygen_preceding_comment(extract_doxygen_member_comments_preceding(&rules)) + .with_doxygen_trailing_comment(extract_doxygen_member_comments_trailing(&rules)) + .with_options(extract_options_list(&rules).expect("Expected options")) + .and_then(|b| { + b.with_type(extract_simple_data_type(&rules).expect("Expected simple data type")) + }) + .and_then(|b| { + b.with_name( + extract_ident_name_tag(&rules).expect("Expected union case member name"), + ) + }) + .and_then(|b| { + b.with_annotations( + extract_annotations_list_rule_tag(&rules).expect("Expected annotations"), + ) + }) + .map(|b| b.build()) + } + + fn parse_interface_definition( + &'_ mut self, + interface_definition_rule: Pair<'_, Rule>, + doxygen_preceding_comment: Vec, + doxygen_trailing_comment: Vec, + ) -> Result<(), Box>> { + info!("Parsing interface definition"); + + let rules = interface_definition_rule.into_inner(); + + let builder = InterfaceDefinitionBuilder::new(&self.program, self.strict) + .with_doxygen_preceding_comment(doxygen_preceding_comment) + .with_doxygen_trailing_comment(doxygen_trailing_comment) + .with_annotations( + extract_annotations_list_rule_tag(&rules).expect("Expected annotations"), + ) + .and_then(|b| { + b.with_name(extract_ident_name_tag(&rules).expect("Expected interface name")) + }) + .and_then(|mut b| { + let callback_definitions_list_rule = + extract_callback_type_definition_list_rule(&rules) + .expect("Expected callback definitions."); + let callback_definition_rules = callback_definitions_list_rule.into_inner(); + for callback_definition_rule in callback_definition_rules { + let callback_builder = b.new_function_definition_builder(); + let callback_definition = + self.parse_function_definition(callback_definition_rule, callback_builder, true)?; + b.add_callback_definition(callback_definition); + } + Ok(b) + }) + .and_then(|mut b| { + let functions_definitions_list_rule = extract_function_definition_list_rule(&rules) + .expect("Expected function definitions."); + let function_definition_rules = functions_definitions_list_rule.into_inner(); + for function_definition_rule in function_definition_rules { + b.add_function(self.parse_function_or_callback(function_definition_rule, &b)?) + } + Ok(b) + })?; + + self.program.add_interface_definition(builder.build()); + + Ok(()) + } + + fn parse_function_or_callback( + &self, + function_rule: Pair<'_, Rule>, + builder: &InterfaceDefinitionBuilder<'a>, + ) -> Result>> { + match function_rule.as_rule() { + Rule::functionDefinition => { + let function_builder = builder.new_function_definition_builder(); + self.parse_function_definition(function_rule, function_builder, false) + } + Rule::callbackDefinition => { + let callback_builder = builder.new_function_definition_builder(); + self.parse_callback(function_rule, callback_builder) + } + _ => { + error!("Error: {:?}", function_rule); + panic!("Unexpected rule"); + } + } + } + + fn parse_function_inner( + &self, + function_type_definition_rule: Pair<'_, Rule>, + builder: FunctionDefinitionBuilder<'a>, + is_definition: bool, + ) -> Result>> { + info!("Parsing function"); + let cloned_rule = function_type_definition_rule.clone(); + let rules = function_type_definition_rule.into_inner(); + let mut function = builder + .with_oneway(extract_oneway_rule(&rules).is_some()) + .with_name(extract_ident_name_tag(&rules).expect("Expected function type name")) + .and_then(|mut b| { + let parameters_list_rule = + extract_parameter_list_rule(&rules).expect("Expected parameters list."); + let parameter_rules = + extract_parameter_definition_list(parameters_list_rule.into_inner()); + trace!("Found {} parameters", parameter_rules.len()); + for parameter_rule in parameter_rules { + let parameter_builder = b.new_parameter_builder(); + let (parameter, pending) = + self.parse_function_parameter(parameter_rule, parameter_builder, is_definition)?; + b.add_parameter(parameter, pending); + } + Ok(b) + }) + .and_then(|b| { + b.with_return_type( + extract_return_type_rule(&rules), + cloned_rule.as_span().end_pos(), + ) + })?; + + match function.resolve_pending_idents() { + Ok(_) => {} + Err(e) => return Err(self.restore_error_from_unresolved_ident(e)), + } + + Ok(function.build()) + } + + fn parse_function_parameter( + &self, + function_parameter_rule: Pair<'_, Rule>, + builder: FunctionParameterBuilder<'a>, + is_definition: bool, + ) -> Result<(FunctionParameter, Vec), Box>> { + info!("Parsing function parameter"); + let rules = function_parameter_rule.into_inner(); + let name_rule = extract_ident_name_tag(&rules); + + if !is_definition && name_rule.is_none() { + let type_rule = extract_simple_data_type_scope(&rules).expect("Expected simple data type"); + return Err(Box::new(Error::new_from_pos( + ErrorVariant::CustomError { + message: "Parameter name is required".to_string() + }, + type_rule.as_span().end_pos(), + ))); + } + + builder + .with_name(name_rule) + .and_then(|b| { + b.with_type( + extract_simple_data_type_scope(&rules) + .expect("Expected simple data type scope"), + ) + }) + .map(|b| { + if let Some(direction) = extract_param_direction(&rules) { + b.with_direction(direction) + } else { + b + } + }) + .and_then(|b| { + b.with_annotations( + extract_annotations_list_rule_tag(&rules).expect("Expected annotations"), + ) + }) + .map(|b| b.build()) + } + + fn parse_function_definition( + &self, + function_definition_rule: Pair<'_, Rule>, + builder: FunctionDefinitionBuilder<'a>, + is_definition: bool, + ) -> Result>> { + info!("Parsing function definition"); + let rules = function_definition_rule.into_inner(); + let builder = builder + .with_doxygen_preceding_comment(extract_doxygen_member_comments_preceding(&rules)) + .with_doxygen_trailing_comment(extract_doxygen_member_comments_trailing(&rules)) + .with_annotations( + extract_annotations_list_rule_tag(&rules).expect("Expected annotations"), + )?; + + self.parse_function_inner( + rules + .find_first_tagged("def") + .expect("Expected function type definition"), + builder, is_definition, + ) + } + + fn parse_callback( + &self, + callback_definition_rule: Pair<'_, Rule>, + builder: FunctionDefinitionBuilder<'a>, + ) -> Result>> { + info!("Parsing callback"); + info!("Rule: {:?}", callback_definition_rule.as_rule()); + + let rules = callback_definition_rule.into_inner(); + let unnamed = extract_definition(&rules).is_some_and(|r| r.as_rule() == Rule::callback); + + builder + .with_doxygen_preceding_comment(extract_doxygen_member_comments_preceding(&rules)) + .with_doxygen_trailing_comment(extract_doxygen_member_comments_trailing(&rules)) + .with_annotations( + extract_annotations_list_rule_tag(&rules).expect("Expected annotations"), + ) + .and_then(|b| { + b.from_prototype( + extract_ident_type_tag(&rules).expect("Expected callback type"), + ) + }) + .and_then(|b| { + b.with_name_cb(extract_ident_name_tag(&rules).expect("Expected callback name"), unnamed) + }) + .and_then(|b| b.with_renamed_parameters(extract_parameter_list_rule(&rules))) + .map(|b| b.build()) + } + + fn handle_forward_declaration(&mut self, ident: Pair<'_, Rule>) { + let ident_name = ident.as_str().to_string(); + info!("Forward declaration for {}", ident_name); + let position = ident.as_span().start_pos().pos(); + let file = self + .file_path_stack + .last() + .expect("Expected file path") + .clone(); + self.forward_declarations + .push(ForwardDefinitionRecord::new(ident_name, position, file)); + } + + fn handle_resolve_forward_declaration(&mut self, name: &str) { + self.forward_declarations.retain(|f| f.name != name); + debug!("Resolved forward declaration for {}", name); + } + + fn restore_error_from_unresolved_forward_declaration( + &self, + forward_definition_record: &ForwardDefinitionRecord, + ) -> Box> { + let file_content = self.read_file_into_string(&forward_definition_record.file_name); + Box::new(Error::new_from_pos( + ErrorVariant::CustomError { + message: format!( + "Unresolved forward declaration for {}", + forward_definition_record.name + ) + .to_string(), + }, + Position::new(&file_content, forward_definition_record.position) + .expect("Expected position"), + )) + } + + fn restore_error_from_unresolved_ident(&self, pending_error: PendingError) -> Box> { + let file_content = + self.read_file_into_string(self.file_path_stack.last().expect("Expected file path")); + let position = pending_error.position; + Box::new(Error::new_from_pos( + ErrorVariant::CustomError { + message: pending_error.error, + }, + Position::new(&file_content, position).expect("Expected position"), + )) + } + + fn handle_parser_error<'b>(&mut self, parser_result: ParserResult<'b>) -> Pairs<'b, Rule> { + match parser_result { + Ok(pairs) => pairs, + Err(mut e) => { + e = e + .with_path(self.file_path_stack.last().expect("Expected file path")) + .renamed_rules(|rule| match *rule { + Rule::definition_base => "definition".to_string(), + Rule::import => "import".to_string(), + // Annotations + Rule::typesHeader => "@types_header".to_string(), + Rule::sharedMemoryEnd => "@shared_memory_end".to_string(), + Rule::sharedMemoryBegin => "@shared_memory_begin".to_string(), + Rule::outputDir => "@output_dir".to_string(), + Rule::nullable => "@nullable".to_string(), + Rule::noInfraErrors => "@no_infra_errors".to_string(), + Rule::noConstParam => "@no_const_params".to_string(), + Rule::name => "@name".to_string(), + Rule::maxLength => "@max_length".to_string(), + Rule::length => "@length".to_string(), + Rule::include => "@include".to_string(), + Rule::id => "@id".to_string(), + Rule::group => "@group".to_string(), + Rule::external => "@external".to_string(), + Rule::errorReturn => "@error_return".to_string(), + Rule::discriminator => "@discriminator".to_string(), + Rule::crc => "@crc".to_string(), + // Symbols + Rule::programDefinition => "program definition".to_string(), + Rule::typedefDefinition => "type definition".to_string(), + Rule::enumDefinition => "enum definition".to_string(), + Rule::structForwardDefinition => "struct forward definition".to_string(), + Rule::structDefinition => "struct definition".to_string(), + Rule::structDataMember => "struct member".to_string(), + Rule::structUnionMember => "union definition".to_string(), + Rule::unionForwardDefinition => "union forward definition".to_string(), + Rule::unionDefinition => "union definition".to_string(), + Rule::unionCase => "union case".to_string(), + Rule::unionMember => "union case member".to_string(), + Rule::constDefinition => "const definition".to_string(), + Rule::interfaceDefinition => "interface definition".to_string(), + Rule::functionDefinition => "function definition".to_string(), + Rule::callbackDefinition => "callback definition".to_string(), + Rule::paramDefinition => "parameter definition".to_string(), + Rule::paramDefinitionList => "parameter list".to_string(), + Rule::oneWay => "oneway".to_string(), + Rule::functionReturnType => "return type".to_string(), + Rule::paramDirectionInout => "inout".to_string(), + Rule::paramDirectionIn => "in".to_string(), + Rule::paramDirectionOut => "out".to_string(), + Rule::callback => "callback".to_string(), + Rule::dataType => "type".to_string(), + Rule::simpleDataType => "type".to_string(), + Rule::arrayType => "array type".to_string(), + Rule::listType => "list type".to_string(), + Rule::constExpression => "const expression".to_string(), + Rule::expression => "expression".to_string(), + Rule::ident => "identifier".to_string(), + Rule::lcb => "{".to_string(), + Rule::rcb => "}".to_string(), + Rule::lsb => "[".to_string(), + Rule::rsb => "]".to_string(), + Rule::lrb => "(".to_string(), + Rule::rrb => ")".to_string(), + Rule::lab => "<".to_string(), + Rule::rab => ">".to_string(), + Rule::comma => ",".to_string(), + _ => { + format!("{:?}", rule) + } + }); + error!("Error:\n {}", e); + exit(1); + } + } + } + + fn handle_error(&self, error: Box>) { + let error = error.with_path(self.file_path_stack.last().expect("Expected file path")); + error!("Error:\n {}", error); + exit(1) + } + + fn read_file_into_string(&self, file_path: &str) -> String { + info!("Reading file: {}", file_path); + let file_content = std::fs::read_to_string(file_path); + match file_content { + Ok(content) => { + debug!("Successfully read file: {}", file_path); + content + } + Err(e) => { + error!("Error: {:?}", e); + panic!("Opening file was unsuccessful"); + } + } + } +} diff --git a/erpcgen_rust/src/parser/expressions/expression.rs b/erpcgen_rust/src/parser/expressions/expression.rs new file mode 100644 index 00000000..857e9393 --- /dev/null +++ b/erpcgen_rust/src/parser/expressions/expression.rs @@ -0,0 +1,119 @@ +/* + * Copyright 2024 Marek Mišík(nxf76107) + * Copyright 2024 NXP + * + * SPDX-License-Identifier: BSD-3-Clause + */ + + +use pest::error::Error; +use pest::iterators::Pairs; +use crate::parser::expressions::expression_parser::EXPRESSION_PARSER; +use crate::parser::expressions::operation::{BinaryOperation, UnaryOperation}; +use crate::parser::grammar_parser::Rule; +use crate::parser::util::expression::{map_infix, map_prefix, map_primary}; +use crate::symbols::const_definition::ConstDefinition; +use crate::symbols::enum_definition::EnumDefinition; +use crate::symbols::value::Value; + +/// Represents an expression tree +#[derive(Clone, Debug)] +pub(crate) enum Expression { + Operand(Value), + UnaryExpression(UnaryOperation, Box), + BinaryExpression(Box, BinaryOperation, Box) +} + +/// Implementation of Expression +impl Expression { + /// Creates a new expression tree from a list of pairs + /// + /// # Arguments + /// + /// * `pairs` - A list of pairs + /// * `consts` - A list of constant definitions + /// * `enums` - A list of enum definitions + /// + /// # Returns + /// + /// * A new expression tree. + pub(crate) fn new_expression_tree(pairs: Pairs, consts: &Vec, enums: &Vec) -> Result>> { + let tree = EXPRESSION_PARSER + .map_primary(|primary| + map_primary(primary, consts, enums) + ) + .map_infix(|left, infix, right| + map_infix(infix, left, right) + ) + .map_prefix(|prefix, expr| + map_prefix(prefix, expr) + ) + .parse(pairs); + tree + } + + /// Evaluates the expression tree + /// + /// # Returns + /// + /// * The result of the expression. + pub(crate) fn eval(&mut self) -> Value { + match self { + Expression::Operand(value) => value.clone(), + Expression::UnaryExpression(op, operand) => { + let operand = operand.eval(); + Expression::eval_unary(op, operand) + } + Expression::BinaryExpression(left, op, right) => { + let left = left.eval(); + let right = right.eval(); + Expression::eval_binary(op, left, right) + } + } + } + + /// Evaluates a unary operation + /// + /// # Arguments + /// + /// * `unary_op` - The unary operation + /// * `operand` - The operand + /// + /// # Returns + /// + /// * The result of the operation. + fn eval_unary(unary_op: &UnaryOperation, operand: Value) -> Value { + match unary_op { + UnaryOperation::Neg => -operand, + UnaryOperation::Inv => !operand, + UnaryOperation::Nop => operand, + } + } + + /// Evaluates a binary operation + /// + /// # Arguments + /// + /// * `binary_op` - The binary operation + /// * `left` - The left operand + /// * `right` - The right operand + /// + /// # Returns + /// + /// * The result of the operation. + fn eval_binary(binary_op: &BinaryOperation, left: Value, right: Value) -> Value { + match binary_op { + BinaryOperation::Add => left + right, + BinaryOperation::Sub => left - right, + BinaryOperation::Mul => left * right, + BinaryOperation::Div => left / right, + BinaryOperation::Mod => left % right, + BinaryOperation::Shl => left << right, + BinaryOperation::Shr => left >> right, + BinaryOperation::Xor => left ^ right, + BinaryOperation::And => left & right, + BinaryOperation::Or => left | right, + } + } + +} diff --git a/erpcgen_rust/src/parser/expressions/expression_parser.rs b/erpcgen_rust/src/parser/expressions/expression_parser.rs new file mode 100644 index 00000000..41b5c1f1 --- /dev/null +++ b/erpcgen_rust/src/parser/expressions/expression_parser.rs @@ -0,0 +1,26 @@ +/* + * Copyright 2024 Marek Mišík(nxf76107) + * Copyright 2024 NXP + * + * SPDX-License-Identifier: BSD-3-Clause + */ + + +use lazy_static::lazy_static; +use pest::pratt_parser::PrattParser; +use crate::parser::grammar_parser::Rule; +use pest::pratt_parser::{Assoc::*, Op}; + +/// The expression parser +lazy_static!{ + pub(crate) static ref EXPRESSION_PARSER: PrattParser = { + PrattParser::new() + .op(Op::prefix(Rule::nop) | Op::prefix(Rule::neg) | Op::prefix(Rule::inv)) + .op(Op::infix(Rule::mul, Left) | Op::infix(Rule::div, Left) | Op::infix(Rule::md, Left)) + .op(Op::infix(Rule::add, Left) | Op::infix(Rule::sub, Left)) + .op(Op::infix(Rule::shl, Left) | Op::infix(Rule::shr, Left)) + .op(Op::infix(Rule::and, Left)) + .op(Op::infix(Rule::xor, Left)) + .op(Op::infix(Rule::or, Left)) + }; +} diff --git a/erpcgen_rust/src/parser/expressions/mod.rs b/erpcgen_rust/src/parser/expressions/mod.rs new file mode 100644 index 00000000..95ae940e --- /dev/null +++ b/erpcgen_rust/src/parser/expressions/mod.rs @@ -0,0 +1,11 @@ +/* + * Copyright 2024 Marek Mišík(nxf76107) + * Copyright 2024 NXP + * + * SPDX-License-Identifier: BSD-3-Clause + */ + + +pub(crate) mod expression; +pub(crate) mod operation; +pub(crate) mod expression_parser; diff --git a/erpcgen_rust/src/parser/expressions/operation.rs b/erpcgen_rust/src/parser/expressions/operation.rs new file mode 100644 index 00000000..87b4210e --- /dev/null +++ b/erpcgen_rust/src/parser/expressions/operation.rs @@ -0,0 +1,30 @@ +/* + * Copyright 2024 Marek Mišík(nxf76107) + * Copyright 2024 NXP + * + * SPDX-License-Identifier: BSD-3-Clause + */ + + +/// Enumeration for binary operations. +#[derive(Debug, Copy, Clone)] +pub(crate) enum BinaryOperation { + Add, + Sub, + Mul, + Div, + Mod, + Shl, + Shr, + Xor, + And, + Or +} + +/// Enumeration for unary operations. +#[derive(Debug, Copy, Clone)] +pub(crate) enum UnaryOperation { + Nop, + Neg, + Inv +} \ No newline at end of file diff --git a/erpcgen_rust/src/parser/forward_definition_record.rs b/erpcgen_rust/src/parser/forward_definition_record.rs new file mode 100644 index 00000000..abc36a14 --- /dev/null +++ b/erpcgen_rust/src/parser/forward_definition_record.rs @@ -0,0 +1,27 @@ +/* + * Copyright 2024 Marek Mišík(nxf76107) + * Copyright 2024 NXP + * + * SPDX-License-Identifier: BSD-3-Clause + */ + + +/// ForwardDefinitionRecord is a struct that holds the name of the forward definition, +/// the position of the forward definition in the file, and the file name of the file that the forward definition is in. +#[derive(Debug, Clone)] +pub(crate) struct ForwardDefinitionRecord { + pub(crate) name: String, + pub(crate) position: usize, + pub(crate) file_name: String, +} + +/// Implementation of ForwardDefinitionRecord +impl ForwardDefinitionRecord { + pub(crate) fn new(name: String, position: usize, file_name: String) -> Self { + Self { + name, + position, + file_name, + } + } +} \ No newline at end of file diff --git a/erpcgen_rust/src/parser/grammar_parser.rs b/erpcgen_rust/src/parser/grammar_parser.rs new file mode 100644 index 00000000..08c9bd7f --- /dev/null +++ b/erpcgen_rust/src/parser/grammar_parser.rs @@ -0,0 +1,12 @@ +/* + * Copyright 2024 Marek Mišík(nxf76107) + * Copyright 2024 NXP + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +use pest_derive::Parser; + +#[derive(Parser)] +#[grammar = "grammar/idl_grammar.pest"] +pub struct IDLParser; \ No newline at end of file diff --git a/erpcgen_rust/src/parser/mod.rs b/erpcgen_rust/src/parser/mod.rs new file mode 100644 index 00000000..cbe9cf6f --- /dev/null +++ b/erpcgen_rust/src/parser/mod.rs @@ -0,0 +1,12 @@ +/* + * Copyright 2024 Marek Mišík(nxf76107) + * Copyright 2024 NXP + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +pub(crate) mod grammar_parser; +pub(crate) mod erpc_parser; +mod expressions; +pub(crate) mod util; +mod forward_definition_record; \ No newline at end of file diff --git a/erpcgen_rust/src/parser/util/annotations.rs b/erpcgen_rust/src/parser/util/annotations.rs new file mode 100644 index 00000000..c1cda5f4 --- /dev/null +++ b/erpcgen_rust/src/parser/util/annotations.rs @@ -0,0 +1,921 @@ +/* + * Copyright 2024 Marek Mišík(nxf76107) + * + * SPDX-License-Identifier: BSD-3-Clause + */ + + +use pest::iterators::{Pair, Pairs}; +use pest::error::{Error, ErrorVariant}; +use crate::parser::grammar_parser::Rule; +use crate::parser::util::common::{extract_ident_name_tag, extract_string_rule}; +use crate::parser::util::expression::extract_expression_rule_tag; +use crate::symbols::annotations::Annotation; +use crate::symbols::interface_definition::InterfaceDefinitionBuilder; +use crate::symbols::language::Language; +use crate::symbols::pending_ident::{CHECK_SCALAR, PendingIdent}; +use crate::symbols::program::Program; +use crate::symbols::types::Type; +use crate::symbols::value::Value; + +/// Extracts the annotations rule from the given rules. +/// +/// # Arguments +/// +/// * `pair` - The pair containing the annotations rule. +/// +/// # Returns +/// +/// The annotations rule. +pub(crate) fn parse_include_annotation(pair: Pair) -> Annotation { + let rules = pair.into_inner(); + let language_specifier: Option = extract_language_specifier(&rules); + let include_path: Option = extract_string_rule(&rules).map(|rule| rule.as_str().to_string()); + Annotation::Include { + language_specifier, + path: include_path.unwrap_or_default(), + } +} + +/// Extracts the annotations rule from the given rules. +/// +/// # Arguments +/// +/// * `pair` - The pair containing the annotations rule. +/// +/// # Returns +/// +/// The annotations rule. +pub(crate) fn parse_no_alloc_errors_annotation(pair: Pair) -> Annotation { + let rules = pair.into_inner(); + let language_specifier: Option = extract_language_specifier(&rules); + Annotation::NoAllocErrors { language_specifier } +} + +/// Extracts the annotations rule from the given rules. +/// +/// # Arguments +/// +/// * `pair` - The pair containing the annotations rule. +/// +/// # Returns +/// +/// The annotations rule. +pub(crate) fn parse_no_const_param_annotation(pair: Pair) -> Annotation { + let rules = pair.into_inner(); + let language_specifier: Option = extract_language_specifier(&rules); + Annotation::NoConstParam { language_specifier } +} + +/// Extracts the annotations rule from the given rules. +/// +/// # Arguments +/// +/// * `pair` - The pair containing the annotations rule. +/// +/// # Returns +/// +/// The annotations rule. +pub(crate) fn parse_no_infra_errors_annotation(pair: Pair) -> Annotation { + let rules = pair.into_inner(); + let language_specifier: Option = extract_language_specifier(& rules); + Annotation::NoInfraErrors { language_specifier } +} + +/// Extracts the annotations rule from the given rules. +/// +/// # Arguments +/// +/// * `pair` - The pair containing the annotations rule. +/// +/// # Returns +/// +/// The annotations rule. +pub(crate) fn parse_output_dir_annotation(pair: Pair) -> Annotation { + let rules = pair.into_inner(); + let language_specifier: Option = extract_language_specifier(&rules); + let output_dir: Option = extract_string_rule(&rules).map(|rule| rule.as_str().to_string()); + Annotation::OutputDir { + language_specifier, + path: output_dir.unwrap_or_default(), + } +} + +/// Extracts the annotations rule from the given rules. +/// +/// # Arguments +/// +/// * `pair` - The pair containing the annotations rule. +/// +/// # Returns +/// +/// The annotations rule. +pub(crate) fn parse_types_header_annotation(pair: Pair) -> Annotation { + let rules = pair.into_inner(); + let language_specifier: Option = extract_language_specifier(&rules); + Annotation::TypesHeader { language_specifier } +} + +/// Extracts annotations rule from the given rules. +/// +/// # Arguments +/// +/// * `pair` - The pair containing the annotations rule. +/// +/// # Returns +/// +/// The annotations rule. +pub(crate) fn parse_nullable_annotation(pair: Pair) -> Annotation { + let rules = pair.into_inner(); + let language_specifier: Option = extract_language_specifier(&rules); + Annotation::Nullable { + language_specifier + } +} + +/// Extracts annotations rule from the given rules. +/// +/// # Arguments +/// +/// * `pair` - The pair containing the annotations rule. +/// +/// # Returns +/// +/// The annotations rule. +pub(crate) fn parse_retain_annotation(pair: Pair) -> Annotation { + let rules = pair.into_inner(); + let language_specifier: Option = extract_language_specifier(&rules); + Annotation::Retain { + language_specifier + } +} + +/// Extracts annotations rule from the given rules. +/// +/// # Arguments +/// +/// * `pair` - The pair containing the annotations rule. +/// +/// # Returns +/// +/// The annotations rule. +pub(crate) fn parse_name_annotation(pair: Pair) -> Annotation { + let rules = pair.into_inner(); + let language_specifier: Option = extract_language_specifier(&rules); + let name: Option = extract_string_rule(&rules).map(|rule| rule.as_str().trim_start_matches('"').trim_end_matches('"').to_string()); + Annotation::Name { + language_specifier, + name: name.expect("Name annotation must have a name."), + } +} + +/// Extracts language specifier from the given string. +/// +/// # Arguments +/// +/// * `str` - The string containing the language specifier. +/// +/// # Returns +/// +/// The language specifier. +pub(crate) fn resolve_language_specifier(str: &str) -> Option { + match str { + "c" => Some(Language::C), + "py" => Some(Language::Python), + "java" => Some(Language::Java), + "rust" => Some(Language::Rust), + _ => None, + } +} + +/// Extracts annotations rule from the given rules. +/// +/// # Arguments +/// +/// * `pair` - The pair containing the annotations rule. +/// +/// # Returns +/// +/// The annotations rule. +pub(crate) fn parse_crc_annotation(pair: Pair) -> Annotation { + let rules = pair.into_inner(); + let language_specifier: Option = extract_language_specifier(&rules); + Annotation::Crc { + language_specifier, + } +} + +/// Extracts annotations rule from the given rules. +/// +/// # Arguments +/// +/// * `pair` - The pair containing the annotations rule. +/// +/// # Returns +/// +/// The annotations rule. +pub(crate) fn parse_group_annotation(pair: Pair) -> Annotation { + let rules = pair.into_inner(); + let language_specifier: Option = extract_language_specifier(&rules); + let group: Option = extract_string_rule(&rules).map(|rule| rule.as_str().to_string()); + Annotation::Group { + language_specifier, + group: group.expect("Group annotation must have a group."), + } +} + +/// Extracts language specifier from the given rules. +/// +/// # Arguments +/// +/// * `rules` - The rules containing the language specifier. +/// +/// # Returns +/// +/// The language specifier. +pub(crate) fn extract_language_specifier<'a>(rules: &'a Pairs<'a, Rule>) -> Option { + rules.find_first_tagged("ls").map(|language| { + resolve_language_specifier(language.as_str()).expect("Invalid language specifier") + }) +} + +/// Extracts annotations rule from the given rules. +/// +/// # Arguments +/// +/// * `pair` - The pair containing the annotations rule. +/// +/// # Returns +/// +/// The annotations rule. +pub(crate) fn parse_external_annotation(pair: Pair) -> Annotation { + let rules = pair.into_inner(); + let language_specifier: Option = extract_language_specifier(&rules); + Annotation::External { + language_specifier, + } +} + +/// Extracts annotations rule from the given rules. +/// +/// # Arguments +/// +/// * `pair` - The pair containing the annotations rule. +/// +/// # Returns +/// +/// The annotations rule. +pub(crate) fn parse_shared_annotation(pair: Pair) -> Annotation { + let rules = pair.into_inner(); + let language_specifier: Option = extract_language_specifier(&rules); + Annotation::Shared { + language_specifier, + } +} + +/// Extracts annotations rule from the given rules. +/// +/// # Arguments +/// +/// * `program` - The program containing the annotations rule. +/// * `rule` - The pair containing the annotations rule. +/// +/// # Returns +/// +/// The annotations rule. +fn parse_shared_memory_begin_annotation(program: &Program, rule: Pair<'_, Rule>) -> Result>> { + let rules = rule.into_inner(); + let language_specifier: Option = extract_language_specifier(&rules); + let address = program.resolve_value_from_expression(extract_expression_rule_tag(&rules).expect("Expected expression"), &Type::UInt32, false)?; + + Ok(Annotation::SharedMemoryBegin { + language_specifier, + address: address.to_u64() as usize, + }) +} + +/// Extracts annotations rule from the given rules. +/// +/// # Arguments +/// +/// * `program` - The program containing the annotations rule. +/// * `rule` - The pair containing the annotations rule. +/// +/// # Returns +/// +/// The annotations rule. +fn parse_shared_memory_end_annotation(program: &Program, rule: Pair<'_, Rule>) -> Result>> { + let rules = rule.into_inner(); + let language_specifier: Option = extract_language_specifier(&rules); + let address = program.resolve_value_from_expression(extract_expression_rule_tag(&rules).expect("Expected expression"), &Type::UInt32, false)?; + + Ok(Annotation::SharedMemoryEnd { + language_specifier, + address: address.to_u64() as usize, + }) +} + +/// Extracts annotations rule from the given rules. +/// +/// # Arguments +/// +/// * `program` - The program containing the annotations rule. +/// * `pair` - The pair containing the annotations rule. +/// * `check_id_is_free` - The function to check if the ID is free. +/// +/// # Returns +/// +/// The annotations rule. +pub(crate) fn parse_id_annotation(program: &Program, pair: Pair, check_id_is_free: impl Fn(usize) -> bool) -> Result>> { + let rules = pair.into_inner(); + let language_specifier: Option = extract_language_specifier(&rules); + let id_rule = extract_expression_rule_tag(&rules).expect("Expected expression"); + let id = program.resolve_value_from_expression(id_rule.clone(), &Type::UInt64, false)?; + if !check_id_is_free(id.to_u64() as usize) { + return Err( + Box::new(Error::new_from_span( + ErrorVariant::CustomError { + message: format!("ID {} is already in use", id.to_u64() as usize), + }, + id_rule.as_span(), + )) + ) + } + Ok(Annotation::Id { + language_specifier, + id: id.to_u64() as usize, + }) +} + +/// Extracts annotations rule from the given rules. +/// +/// # Arguments +/// +/// * `pair` - The pair containing the annotations rule. +/// +/// # Returns +/// +/// The annotations rule. +pub(crate) fn parse_discriminator_annotation(pair: Pair) -> (Annotation, PendingIdent) { + let rules = pair.clone().into_inner(); + let language_specifier: Option = extract_language_specifier(&rules); + let discriminator_rule = extract_ident_name_tag(&rules).expect("Expected identifier"); + let discriminator: String = discriminator_rule.as_str().to_string(); + (Annotation::Discriminator { + language_specifier, + value: Value::RuntimeValue { name: discriminator.clone() }, + }, PendingIdent::new(discriminator, discriminator_rule.as_span().start_pos().pos(), Box::new(CHECK_SCALAR))) +} + +/// Extracts annotations rule from the given rules. +/// +/// # Arguments +/// +/// * `program` - The program containing the annotations rule. +/// * `pair` - The pair containing the annotations rule. +/// +/// # Returns +/// +/// The annotations rule. +pub(crate) fn parse_error_return_annotation(program: &Program, pair: Pair) -> Annotation { + let rules = pair.into_inner(); + let language_specifier: Option = extract_language_specifier(&rules); + let value = program.resolve_value_from_expression(extract_expression_rule_tag(&rules).expect("Expected expression"), &Type::Int32, false).expect("Expected integer value"); + Annotation::ErrorReturn { + language_specifier, + value: value.to_i32(), // TODO: This is a temporary solution + } +} + +/// Extracts annotations rule from the given rules. +/// +/// # Arguments +/// +/// * `program` - The program containing the annotations rule. +/// * `pair` - The pair containing the annotations rule. +/// +/// # Returns +/// +/// The annotations rule. +pub(crate) fn parse_length_annotation(program: &Program, pair: Pair) -> Result<(Annotation, Option), Box>> { + let rules = pair.into_inner(); + let language_specifier: Option = extract_language_specifier(&rules); + let expr = extract_expression_rule_tag(&rules).expect("Expected expression"); + if let Ok(length ) = program.resolve_value_from_expression(expr.clone(), &Type::UInt32, false) { + return Ok((Annotation::Length { + language_specifier, + length, + }, None)); + }; + let Some(Rule::ident) = expr.clone().into_inner().next().map(|rule| rule.as_rule()) else { + return Err(Box::new(Error::new_from_span(ErrorVariant::CustomError { + message: format!("Expected an identifier, found {:?}", expr.as_str()).to_string(), + }, + expr.as_span(), + ))); + }; + Ok((Annotation::Length { + language_specifier, + length: Value::RuntimeValue { name: expr.as_str().to_string() }, + }, Some(PendingIdent::new(expr.as_str().to_string(), expr.as_span().start_pos().pos(), Box::new(CHECK_SCALAR))))) +} + +/// Extracts annotations rule from the given rules. +/// +/// # Arguments +/// +/// * `program` - The program containing the annotations rule. +/// * `pair` - The pair containing the annotations rule. +/// +/// # Returns +/// +/// The annotations rule. +pub(crate) fn parse_max_length_annotation(program: &Program, pair: Pair) -> Result<(Annotation, Option), Box>> { + let rules = pair.into_inner(); + let language_specifier: Option = extract_language_specifier(&rules); + let expr = extract_expression_rule_tag(&rules).expect("Expected expression"); + if let Ok(length ) = program.resolve_value_from_expression(expr.clone(), &Type::UInt32, false) { + return Ok((Annotation::MaxLength { + language_specifier, + length, + }, None)); + }; + let Some(Rule::ident) = expr.clone().into_inner().next().map(|rule| rule.as_rule()) else { + return Err(Box::new(Error::new_from_span( + ErrorVariant::CustomError { + message: format!("Expected an identifier, found {:?}", expr.as_str()).to_string(), + }, + expr.as_span(), + ))); + }; + Ok((Annotation::MaxLength { + language_specifier, + length: Value::RuntimeValue { name: expr.as_str().to_string() }, + }, Some(PendingIdent::new(expr.as_str().to_string(), expr.as_span().start_pos().pos(), Box::new(CHECK_SCALAR))))) +} + +/// Extracts annotations rule from the given rules. +/// +/// # Arguments +/// +/// * `struct_annotations_rule` - The pair containing the annotations rule. +/// * `language` - The language of the annotations. +/// * `strict` - The flag to indicate if the parsing should be strict. +/// +/// # Returns +/// +/// The annotations rule. +pub(crate) fn parse_struct_annotations(struct_annotations_rule: Pair,language: &Language, strict: bool) -> Result, Box>> { + let mut annotations = vec![]; + let annotation_rules = struct_annotations_rule.into_inner(); + for annotation in annotation_rules { + let ann = match annotation.as_rule() { + Rule::nullable => parse_nullable_annotation(annotation), + Rule::retain => parse_retain_annotation(annotation), + Rule::name => parse_name_annotation(annotation), + _ => { + if strict { + return Err(Box::new(Error::new_from_span( + ErrorVariant::CustomError { + message: format!("Unexpected annotation: {}", annotation.as_str()).to_string(), + }, + annotation.as_span(), + ))); + } else { + continue; + } + }, + }; + if !ann.get_language_specifier().is_some_and(|sp| &sp != language) { + annotations.push(ann); // Filter out annotations that are not for the current language + } + } + Ok(annotations) +} + +/// Extracts annotations rule from the given rules. +/// +/// # Arguments +/// +/// * `type_annotations_rule` - The pair containing the annotations rule. +/// * `language` - The language of the annotations. +/// * `strict` - The flag to indicate if the parsing should be strict. +/// +/// # Returns +/// +/// The annotations rule. +pub(crate) fn parse_typedef_annotations(type_annotations_rule: Pair,language: &Language, strict: bool) -> Result, Box>> { + let mut annotations = vec![]; + let annotation_rules = type_annotations_rule.into_inner(); + for annotation in annotation_rules { + let ann = match annotation.as_rule() { + Rule::external => parse_external_annotation(annotation), + Rule::name => parse_name_annotation(annotation), + Rule::noConstParam => parse_no_const_param_annotation(annotation), + Rule::shared => parse_shared_annotation(annotation), + _ => { + if strict { + return Err(Box::new(Error::new_from_span( + ErrorVariant::CustomError { + message: format!("Unexpected annotation: {}", annotation.as_str()), + }, + annotation.as_span(), + ))); + } else { + continue; + } + }, + }; + if !ann.get_language_specifier().is_some_and(|sp| &sp != language) { + annotations.push(ann); // Filter out annotations that are not for the current language + } + } + Ok(annotations) +} + +/// Extracts annotations rule from the given rules. +/// +/// # Arguments +/// +/// * `enum_annotations_rule` - The pair containing the annotations rule. +/// * `language` - The language of the annotations. +/// * `strict` - The flag to indicate if the parsing should be strict. +/// +/// # Returns +/// +/// The annotations rule. +pub(crate) fn parse_enum_annotations(enum_annotations_rule: Pair, language: &Language, strict: bool) -> Result, Box>> { + let mut annotations = vec![]; + let annotation_rules = enum_annotations_rule.into_inner(); + for annotation in annotation_rules { + let ann = match annotation.as_rule() { + Rule::name => parse_name_annotation(annotation), + _ => { + if strict { + return Err(Box::new(Error::new_from_span( + ErrorVariant::CustomError { + message: format!("Unexpected annotation: {}", annotation.as_str()).to_string(), + }, + annotation.as_span(), + ))); + } else { + continue; + } + }, + }; + if !ann.get_language_specifier().is_some_and(|sp| &sp != language) { + annotations.push(ann); // Filter out annotations that are not for the current language + } + } + Ok(annotations) +} + +pub(crate) fn parse_union_annotations(union_annotations_rule: Pair,language: &Language, strict: bool) -> Result<(Vec, Vec), Box>> { //TODO + let mut annotations = vec![]; + let mut pending_idents = vec![]; + let annotation_rules = union_annotations_rule.into_inner(); + for annotation in annotation_rules { + let anno = match annotation.as_rule() { + Rule::name => parse_name_annotation(annotation), + Rule::discriminator => { + let (ann, pend) = parse_discriminator_annotation(annotation); + pending_idents.push(pend); + ann + }, + Rule::nullable => parse_nullable_annotation(annotation), + Rule::retain => parse_retain_annotation(annotation), + _ => { + if strict { + return Err(Box::new(Error::new_from_span( + pest::error::ErrorVariant::CustomError { + message: format!("Unexpected annotation: {}", annotation.as_str()).to_string(), + }, + annotation.as_span(), + ))); + } else { + continue; + } + }, + }; + if !anno.get_language_specifier().is_some_and(|sp| &sp != language) { + annotations.push(anno); // Filter out annotations that are not for the current language + } + } + Ok((annotations, pending_idents)) +} + +/// Extracts annotations rule from the given rules. +/// +/// # Arguments +/// +/// * `program` - The program containing the annotations rule. +/// * `list_annotations_rule` - The pair containing the annotations rule. +/// * `language` - The language of the annotations. +/// * `strict` - The flag to indicate if the parsing should be strict. +/// +/// # Returns +/// +/// The annotations rule. +pub(crate) fn parse_list_annotations(program: &Program, list_annotations_rule: Pair,language: &Language, strict: bool) -> Result<(Vec, Vec), Box>>{ + let mut annotations = vec![]; + let mut pending_idents = vec![]; + let annotation_rules = list_annotations_rule.into_inner(); + for annotation in annotation_rules { + let ann = match annotation.as_rule() { + Rule::length => { + let (ann, pend) = parse_length_annotation(program, annotation)?; + if let Some(p) = pend { + pending_idents.push(p); + } + ann + }, + Rule::maxLength => { + let (ann, pend) = parse_max_length_annotation(program, annotation)?; + if let Some(p) = pend { + pending_idents.push(p); + } + ann + }, + Rule::nullable => parse_nullable_annotation(annotation), + Rule::retain => parse_retain_annotation(annotation), + Rule::name => parse_name_annotation(annotation), + _ => { + if strict { + return Err(Box::new(Error::new_from_span( + ErrorVariant::CustomError { + message: format!("Unexpected annotation: {}", annotation.as_str()).to_string(), + }, + annotation.as_span(), + ))); + } else { + continue; + } + }, + }; + if !ann.get_language_specifier().is_some_and(|sp| &sp != language) { + annotations.push(ann); // Filter out annotations that are not for the current language + } + } + Ok((annotations, pending_idents)) +} + +/// Extracts annotations rule from the given rules. +/// +/// # Arguments +/// +/// * `array_annotations` - The pair containing the annotations rule. +/// * `language` - The language of the annotations. +/// * `strict` - The flag to indicate if the parsing should be strict. +/// +/// # Returns +/// +/// The annotations rule. +pub(crate) fn parse_array_annotations(array_annotations: Pair<'_, Rule>,language: &Language, strict: bool) -> Result, Box>> { + let mut annotations = vec![]; + let annotation_rules = array_annotations.into_inner(); + for annotation in annotation_rules { + let ann = match annotation.as_rule() { + Rule::retain => parse_retain_annotation(annotation), + Rule::name => parse_name_annotation(annotation), + _ => { + if strict { + return Err(Box::new(Error::new_from_span( + ErrorVariant::CustomError { + message: format!("Unexpected annotation: {}", annotation.as_str()).to_string(), + }, + annotation.as_span(), + ))); + } else { + continue; + } + }, + }; + if !ann.get_language_specifier().is_some_and(|sp| &sp != language) { + annotations.push(ann); // Filter out annotations that are not for the current language + } + } + Ok(annotations) +} + + +/// Extracts annotations rule from the given rules. +/// +/// # Arguments +/// +/// * `program` - The program containing the annotations rule. +/// * `program_annotations_rule` - The pair containing the annotations rule. +/// * `language` - The language of the annotations. +/// * `strict` - The flag to indicate if the parsing should be strict. +/// +/// # Returns +/// +/// The annotations rule. +pub(crate) fn parse_program_annotations( + program: &Program, + program_annotations_rule: Pair<'_, Rule>, + language: &Language, + strict: bool, +) -> Result, Box>> { + let mut annotations = vec![]; + let annotation_rules = program_annotations_rule.into_inner(); + for annotation in annotation_rules { + let ann = match annotation.as_rule() { + Rule::crc => parse_crc_annotation(annotation), + Rule::include => parse_include_annotation(annotation), + Rule::noAllocErrors => { + parse_no_alloc_errors_annotation(annotation) + } + Rule::noConstParam => parse_no_const_param_annotation(annotation), + Rule::noInfraErrors => { + parse_no_infra_errors_annotation(annotation) + } + Rule::outputDir => parse_output_dir_annotation(annotation), + Rule::sharedMemoryBegin => { + parse_shared_memory_begin_annotation(program, annotation)? + } + Rule::sharedMemoryEnd => { + parse_shared_memory_end_annotation(program, annotation)? + } + Rule::typesHeader => parse_types_header_annotation(annotation), + _ => { + if strict { + return Err(Box::new(Error::new_from_span( + ErrorVariant::CustomError { + message: format!("Unexpected annotation: {}", annotation.as_str()).to_string(), + }, + annotation.as_span(), + ))); + } else { + continue; + } + } + }; + if !ann.get_language_specifier().is_some_and(|sp| &sp != language) { + annotations.push(ann); // Filter out annotations that are not for the current language + } + } + Ok(annotations) +} + +/// Extracts annotations rule from the given rules. +/// +/// # Arguments +/// +/// * `program` - The program containing the annotations rule. +/// * `interface_annotations_rule` - The pair containing the annotations rule. +/// * `language` - The language of the annotations. +/// * `strict` - The flag to indicate if the parsing should be strict. +/// +/// # Returns +/// +/// The annotations rule. +pub(crate) fn parse_interface_annotations(program: &Program, interface_annotations_rule: Pair,language: &Language, strict: bool) -> Result, Box>> { + let mut annotations = vec![]; + let check_id = |id| program.check_is_interface_id_free(id); + let annotation_rules = interface_annotations_rule.into_inner(); + for annotation in annotation_rules { + let ann = match annotation.as_rule() { + Rule::group => parse_group_annotation(annotation), + Rule::include => parse_include_annotation(annotation), + Rule::id => parse_id_annotation(program, annotation, check_id)?, + Rule::name => parse_name_annotation(annotation), + _ => { + if strict { + return Err(Box::new(Error::new_from_span( + ErrorVariant::CustomError { + message: format!("Unexpected annotation: {}", annotation.as_str()).to_string(), + }, + annotation.as_span(), + ))); + } else { + continue; + } + }, + }; + if !ann.get_language_specifier().is_some_and(|sp| &sp != language) { + annotations.push(ann); // Filter out annotations that are not for the current language + } + } + Ok(annotations) +} + + +/// Extracts annotations rule from the given rules. +/// +/// # Arguments +/// +/// * `program` - The program containing the annotations rule. +/// * `interface_definition_builder` - The interface definition builder. +/// * `interface_annotations_rule` - The pair containing the annotations rule. +/// * `language` - The language of the annotations. +/// * `strict` - The flag to indicate if the parsing should be strict. +/// +/// # Returns +/// +/// The annotations rule. +pub(crate) fn parse_function_annotations(program: &Program, interface_definition_builder: &InterfaceDefinitionBuilder, interface_annotations_rule: Pair,language: &Language, strict: bool) -> Result, Box>> { + let mut annotations = vec![]; + let annotation_rules = interface_annotations_rule.into_inner(); + let check_id = |id| interface_definition_builder.check_function_id_is_free(id); + for annotation in annotation_rules { + let ann = match annotation.as_rule() { + Rule::errorReturn => parse_error_return_annotation(program, annotation), + Rule::external => parse_external_annotation(annotation), + Rule::id => parse_id_annotation(program, annotation, check_id)?, + Rule::name => parse_name_annotation(annotation), + Rule::noConstParam => parse_no_const_param_annotation(annotation), + _ => { + if strict { + return Err(Box::new(Error::new_from_span( + pest::error::ErrorVariant::CustomError { + message: format!("Unexpected annotation: {}", annotation.as_str()).to_string(), + }, + annotation.as_span(), + ))); + } else { + continue; + } + }, + }; + if !ann.get_language_specifier().is_some_and(|sp| &sp != language) { + annotations.push(ann); // Filter out annotations that are not for the current language + } + } + Ok(annotations) +} + +/// Parses the member annotations from the given rule. +/// +/// This function iterates over the annotations in the given rule and parses them. +/// If the annotation is a name annotation, it is parsed using the `parse_name_annotation` function. +/// If the annotation is not recognized and the `strict` parameter is set to true, an error is returned. +/// If the annotation is not recognized and the `strict` parameter is set to false, the annotation is skipped. +/// After parsing an annotation, if the language specifier of the annotation does not match the provided language, +/// the annotation is added to the list of annotations to be returned. +/// +/// # Arguments +/// +/// * `array_annotations` - The pair containing the annotations rule. +/// * `language` - The language of the annotations. +/// * `strict` - The flag to indicate if the parsing should be strict. +/// +/// # Returns +/// +/// The annotations rule. +pub(crate) fn parse_member_annotations(array_annotations: Pair<'_, Rule>,language: &Language, strict: bool) -> Result, Box>> { + let mut annotations = vec![]; + let annotation_rules = array_annotations.into_inner(); + for annotation in annotation_rules { + let ann = match annotation.as_rule() { + Rule::name => parse_name_annotation(annotation), + _ => { + if strict { + return Err(Box::new(Error::new_from_span( + ErrorVariant::CustomError { + message: format!("Unexpected annotation: {}", annotation.as_str()).to_string(), + }, + annotation.as_span(), + ))); + } else { + continue; + } + }, + }; + if !ann.get_language_specifier().is_some_and(|sp| &sp != language) { + annotations.push(ann); // Filter out annotations that are not for the current language + } + } + Ok(annotations) +} + + +/// Checks if the given annotations rule has a discriminator annotation. +/// +/// # Arguments +/// +/// * `annotations` - The annotations rule. +/// +/// # Returns +/// +/// * `true` if the annotations rule has a discriminator annotation, otherwise `false`. +pub(crate) fn has_discriminator_annotation(annotations: &[Annotation]) -> bool { + annotations.iter().any(|ann| matches!(ann, Annotation::Discriminator { .. })) +} + +/// Finds name annotation in the given annotations rule. +/// +/// # Arguments +/// +/// * `annotations` - The annotations rule. +/// +/// # Returns +/// +/// The name annotation. +pub(crate) fn find_name_annotation(annotations: &[Annotation]) -> Option<&Annotation> { + annotations.iter().find(|ann| matches!(ann, Annotation::Name { .. })) +} + + + + + diff --git a/erpcgen_rust/src/parser/util/common.rs b/erpcgen_rust/src/parser/util/common.rs new file mode 100644 index 00000000..dc143659 --- /dev/null +++ b/erpcgen_rust/src/parser/util/common.rs @@ -0,0 +1,274 @@ +/* + * Copyright 2024 Marek Mišík(nxf76107) + * + * SPDX-License-Identifier: BSD-3-Clause + */ + + +use crate::parser::grammar_parser::Rule; +use pest::iterators::{Pair, Pairs}; +use log::error; +use crate::symbols::doxygen_comment::DoxygenComment; + + +/// Extract a rule from a list of rules +/// +/// # Arguments +/// +/// * `rules` - A list of rules +/// +/// # Returns +/// +/// An optional rule +pub(crate) fn extract_doxygen_trailing_comment_tag( + rules: &Pairs, +) -> Option { + if let Some(rule) = rules.find_first_tagged("tc") { + return Some(rule.as_str().to_string()); + } + None +} + +/// Extract a rule from a list of rules +/// +/// # Arguments +/// +/// * `rules` - A list of rules +/// +/// # Returns +/// +/// An optional rule +pub(crate) fn extract_annotations_list_rule_tag<'a>( + rules: &'a Pairs<'a, Rule>, +) -> Option> { + if let Some(rule) = rules.find_first_tagged("al") { + return Some(rule); + } + None +} +/// Extract a rule from a list of rules +/// +/// # Arguments +/// +/// * `rules` - A list of rules +/// +/// # Returns +/// +/// An optional rule +pub(crate) fn extract_ident_name_tag<'a>( + rules: &'a Pairs, +) -> Option> { + rules.find_first_tagged("name") +} + +/// Extract a rule from a list of rules +/// +/// # Arguments +/// +/// * `rules` - A list of rules +/// +/// # Returns +/// +/// An optional rule +pub(crate) fn extract_ident_opt_name_tag<'a>( + rules: &'a Pairs, +) -> Option> { + rules.find_first_tagged("opt_name") +} + +/// Extract a rule from a list of rules +/// +/// # Arguments +/// +/// * `rules` - A list of rules +/// +/// # Returns +/// +/// An optional rule +pub(crate) fn extract_member_name_tag<'a>( + rules: &'a Pairs, +) -> Option> { + rules.find_first_tagged("member_name") +} + +/// Extract a rule from a list of rules +/// +/// # Arguments +/// +/// * `rules` - A list of rules +/// +/// # Returns +/// +/// An optional rule +pub(crate) fn extract_ident_type_tag<'a>( + rules: &'a Pairs, +) -> Option> { + if let Some(rule) = rules.find_first_tagged("type") { + return Some(rule); + } + None +} + +/// Extract a rule from a list of rules +/// +/// # Arguments +/// +/// * `rules` - A list of rules +/// +/// # Returns +/// +/// An optional rule +pub(crate) fn extract_string_rule<'a>( + rules: &'a Pairs<'a, Rule> +) -> Option> { + rules.find_first_tagged("str") +} + +/// Extract a rule from a list of rules +/// +/// # Arguments +/// +/// * `rules` - A list of rules +/// +/// # Returns +/// +/// An optional rule +pub(crate) fn extract_doxygen_definition_comments_preceding<'a>( + rules: &'a Pairs<'a, Rule> +) -> Vec { + let Some(comments) = rules.find_first_tagged("pcd") else { + return vec![] + }; + parse_comments(comments) +} + +/// Extract a rule from a list of rules +/// +/// # Arguments +/// +/// * `rules` - A list of rules +/// +/// # Returns +/// +/// An optional rule +pub(crate) fn extract_doxygen_member_comments_preceding<'a>( + rules: &'a Pairs<'a, Rule> +) -> Vec { + let Some(comments) = rules.find_first_tagged("pc") else { + return vec![] + }; + parse_comments(comments) +} + +/// Extract a rule from a list of rules +/// +/// # Arguments +/// +/// * `rules` - A list of rules +/// +/// # Returns +/// +/// An optional rule +pub(crate) fn extract_doxygen_definition_comments_trailing<'a>( + rules: &'a Pairs<'a, Rule> +) -> Vec { + let Some(comments) = rules.find_first_tagged("tcd") else { + return vec![] + }; + parse_comments(comments) +} + +/// Extract a rule from a list of rules +/// +/// # Arguments +/// +/// * `rules` - A list of rules +/// +/// # Returns +/// +/// An optional rule +pub(crate) fn extract_doxygen_member_comments_trailing<'a>( + rules: &'a Pairs<'a, Rule> +) -> Vec { + let Some(comments) = rules.find_first_tagged("tc") else { + return vec![] + }; + parse_comments(comments) +} + +/// Parse comments +/// +/// # Arguments +/// +/// * `comments` - A list of comments +/// +/// # Returns +/// +/// A list of doxygen comments +fn parse_comments( + comments: Pair +) -> Vec { + let comments_list = comments.into_inner(); + let mut comments_vec = vec![]; + comments_list.for_each(|comment| { + match comment.as_rule() { + Rule::multilineCommentContent => { + comments_vec.push(DoxygenComment::MultiLine(comment.as_str().to_string())); + + }, + Rule::singleLineCommentContent => { + comments_vec.push(DoxygenComment::SingleLine(comment.as_str().trim_end_matches('\n').trim_end_matches('\r').to_string())); + }, + _ => { + error!("{:?}", comment.as_rule()); + panic!("Unexpected rule"); + } + } + }); + comments_vec +} + +/// Extract a rule from a list of rules +/// +/// # Arguments +/// +/// * `rules` - A list of rules +/// +/// # Returns +/// +/// An optional rule +pub(crate) fn extract_simple_data_type<'a>( + rules: &'a Pairs<'a, Rule> +) -> Option> { + rules.find_first_tagged("sdt") +} + +/// Extract a rule from a list of rules +/// +/// # Arguments +/// +/// * `rules` - A list of rules +/// +/// # Returns +/// +/// An optional rule +pub(crate) fn extract_cases_list<'a>( + rules: &'a Pairs<'a, Rule> +) -> Option> { + rules.find_first_tagged("cl") +} + +/// Extract a rule from a list of rules +/// +/// # Arguments +/// +/// * `rules` - A list of rules +/// +/// # Returns +/// +/// An optional rule +pub(crate) fn extract_member_list<'a>( + rules: &'a Pairs<'a, Rule> +) -> Option> { + rules.find_first_tagged("ml") +} diff --git a/erpcgen_rust/src/parser/util/const_definition.rs b/erpcgen_rust/src/parser/util/const_definition.rs new file mode 100644 index 00000000..3820969d --- /dev/null +++ b/erpcgen_rust/src/parser/util/const_definition.rs @@ -0,0 +1,17 @@ +use pest::iterators::{Pair, Pairs}; +use crate::parser::grammar_parser::Rule; + +/// Extracts the const definition rule from the given rules +/// +/// # Arguments +/// +/// * `rules` - The rules to extract the const definition rule from +/// +/// # Returns +/// +/// The const definition rule if found, otherwise None +pub(crate) fn extract_const_expression_rule<'a>( + rules: &'a Pairs<'a, Rule>, +) -> Option> { + rules.find_first_tagged("ce") +} diff --git a/erpcgen_rust/src/parser/util/enum_definition.rs b/erpcgen_rust/src/parser/util/enum_definition.rs new file mode 100644 index 00000000..dba9b6ea --- /dev/null +++ b/erpcgen_rust/src/parser/util/enum_definition.rs @@ -0,0 +1,28 @@ +/* + * Copyright 2024 Marek Mišík(nxf76107) + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +use crate::symbols::enum_member::EnumMember; +use crate::symbols::value::Value; + +/// Resolves the value of the enum member +/// +/// # Arguments +/// +/// * `preceding_enum_member` - The preceding enum member +/// +/// # Returns +/// +/// The value of the enum member +pub(crate) fn resolve_enum_member_value(preceding_enum_member: Option<&EnumMember>) -> Value { + match preceding_enum_member { + Some(member) => { + Value::Int64(member.value.to_i64() + 1) + } + None => { + Value::Int64(0) + } + } +} diff --git a/erpcgen_rust/src/parser/util/expression.rs b/erpcgen_rust/src/parser/util/expression.rs new file mode 100644 index 00000000..44568c41 --- /dev/null +++ b/erpcgen_rust/src/parser/util/expression.rs @@ -0,0 +1,412 @@ +/* + * Copyright 2024 Marek Mišík(nxf76107) + * + * SPDX-License-Identifier: BSD-3-Clause + */ + + +use log::{info, trace}; +use pest::error::{Error, ErrorVariant}; +use pest::iterators::{Pair, Pairs}; +use std::num::{ParseFloatError, ParseIntError}; +use std::str::FromStr; +use pest::Span; +use crate::parser::expressions::expression::Expression; +use crate::parser::expressions::operation::{BinaryOperation, UnaryOperation}; +use crate::parser::grammar_parser::Rule; +use crate::symbols::const_definition::ConstDefinition; +use crate::symbols::enum_definition::EnumDefinition; +use crate::symbols::types::Type; +use crate::symbols::value::Value; + +/// Represents the expression parser +/// +/// # Arguments +/// +/// * `pair` - The pair to parse +/// * `consts` - The list of constant definitions +/// * `enums` - The list of enum definitions +/// +/// # Returns +/// +/// The parsed expression +pub(crate) fn map_primary(pair: Pair, consts: &Vec, enums: &Vec) -> Result>> { + let pos = pair.as_span().start_pos(); + let literal = pair.as_str(); + if Rule::bracedExpression == pair.as_rule() { + let inner = pair.into_inner(); + let expr_rule = extract_expression_rule_tag(&inner).expect("Braced expression has inner expression"); + return Expression::new_expression_tree(expr_rule.into_inner(), consts, enums); + } + if Rule::expression == pair.as_rule() { + return Expression::new_expression_tree(pair.into_inner(), consts, enums); + } + let res = match pair.as_rule() { + Rule::decimal => { + parse_decimal(literal) + .map_err(|e| Box::new(Error::new_from_pos(ErrorVariant::CustomError { + message: e.to_string(), + }, pos))) + } + Rule::hexadecimal => { + parse_hexadecimal(literal) + .map_err(|e| Box::new(Error::new_from_pos(ErrorVariant::CustomError { + message: e.to_string(), + }, pos))) + } + Rule::binary => { + parse_binary(literal) + .map_err(|e| Box::new(Error::new_from_pos(ErrorVariant::CustomError { + message: e.to_string(), + }, pos))) + } + Rule::floatingPoint => { + parse_floating_point(literal) + .map_err(|e| Box::new(Error::new_from_pos(ErrorVariant::CustomError { + message: e.to_string(), + }, pos))) + } + Rule::ident => { + resolve_value(literal, pair.as_span(), consts, enums, false) + } + _ => { + println!("Error: {:?}", pair.as_rule()); + unreachable!("Unexpected rule") + } + }?; + Ok(Expression::Operand(res)) +} + + +/// Resolves the value of the given identifier +/// +/// # Arguments +/// +/// * `ident` - The identifier to resolve +/// * `span` - The span of the identifier +/// * `consts` - The list of constant definitions +/// * `enums` - The list of enum definitions +/// * `keep_reference` - Whether to keep the reference +/// +/// # Returns +/// +/// The value of the identifier +pub(crate) fn resolve_value(ident: &str, span: Span, consts: &[ConstDefinition], enums: &[EnumDefinition], keep_reference: bool ) -> Result>> { + let const_value = consts + .iter() + .find(|c| c.name == ident) + .map(|c| Value::ReferencedValue { name: c.name.clone(), prefix: None, value: Box::new(c.value.clone()) }); + if let Some(v) = const_value { + return Ok(v) + } + + let enum_value = enums + .iter() + .flat_map(|ed| ed.members.iter().map(move |m| (ed, m))) + .find(|(_, m)| m.name == ident) + .map(|(ed, m)| { + if keep_reference { + Value::ReferencedValue { name: m.name.clone(), prefix: ed.name.clone(), value: Box::new(m.value.clone()) } + } else { + m.value.clone() + } + }); + + return match enum_value { + Some(v) => { + Ok(v) + } + _ => { + Err(Box::new(Error::new_from_pos(ErrorVariant::CustomError { + message: format!("Value {} not is not defined", ident).to_string(), + }, span.start_pos()))) + } + } +} + +/// Maps the infix expression +/// +/// # Arguments +/// +/// * `pair` - The pair to map +/// * `lhs` - The left hand side expression +/// * `rhs` - The right hand side expression +/// +/// # Returns +/// +/// The mapped expression +pub(crate) fn map_infix(pair: Pair, lhs: Result>>, rhs: Result>>) -> Result>> { + let left = lhs?; + let right = rhs?; + match pair.as_rule() { + Rule::add => { + Ok(Expression::BinaryExpression(Box::new(left), BinaryOperation::Add, Box::new(right))) + } + Rule::sub => { + Ok(Expression::BinaryExpression(Box::new(left), BinaryOperation::Sub, Box::new(right))) + } + Rule::mul => { + Ok(Expression::BinaryExpression(Box::new(left), BinaryOperation::Mul, Box::new(right))) + } + Rule::div => { + Ok(Expression::BinaryExpression(Box::new(left), BinaryOperation::Div, Box::new(right))) + } + Rule::md => { + Ok(Expression::BinaryExpression(Box::new(left), BinaryOperation::Mod, Box::new(right))) + } + Rule::shl => { + Ok(Expression::BinaryExpression(Box::new(left), BinaryOperation::Shl, Box::new(right))) + } + Rule::shr => { + Ok(Expression::BinaryExpression(Box::new(left), BinaryOperation::Shr, Box::new(right))) + } + Rule::xor => { + Ok(Expression::BinaryExpression(Box::new(left), BinaryOperation::Xor, Box::new(right))) + } + Rule::and => { + Ok(Expression::BinaryExpression(Box::new(left), BinaryOperation::And, Box::new(right))) + } + Rule::or => { + Ok(Expression::BinaryExpression(Box::new(left), BinaryOperation::Or, Box::new(right))) + } + _ => { + unreachable!("Unexpected rule") + } + } +} + +/// Maps the prefix expression +/// +/// # Arguments +/// +/// * `pair` - The pair to map +/// * `expression` - The expression to map +/// +/// # Returns +/// +/// The mapped expression +pub(crate) fn map_prefix(pair: Pair, expression: Result>>) -> Result>> { + if let Ok(e) = expression { + let res = match pair.as_rule() { + Rule::nop => { + e + } + Rule::neg => { + Expression::UnaryExpression(UnaryOperation::Neg, Box::new(e)) + } + Rule::inv => { + Expression::UnaryExpression(UnaryOperation::Inv, Box::new(e)) + } + _ => { + unreachable!("Unexpected rule") + } + }; + return Ok(res); + } + expression +} + + +/// Evaluates the given expression +/// +/// # Arguments +/// +/// * `rule` - The rule to evaluate +/// * `target_type` - The target type +/// * `consts` - The list of constant definitions +/// * `enums` - The list of enum definitions +/// * `allow_reference` - Whether to allow reference +/// * `strict` - Whether to cast strictly +/// +/// # Returns +/// +/// The evaluated value +pub(crate) fn evaluate_expression(rule: Pair, target_type: &Type, consts: &Vec, enums: &Vec, allow_reference: bool , strict: bool) -> Result>> { + let pos = rule.as_span().start_pos(); + info!("Evaluating expression {} with target type {:?}", rule.as_str(), target_type); + let mut pairs = rule.into_inner(); + + if pairs.peek().is_some_and(|p| p.as_rule() == Rule::string) { + if target_type != &Type::String { + return Err(Box::new(Error::new_from_pos(ErrorVariant::CustomError { + message: format!("Cannot cast string to {:?}", target_type).to_string(), + }, pos))) + } + let string = pairs.next().expect("Expected string").as_str(); + return Ok(Value::String(string.to_string())); + } + + // In case of union, we want to get Value::ReferencedValue if possible + if allow_reference && pairs.len() == 1 && pairs.peek().is_some_and(|pair| pair.as_rule() == Rule::ident) { + info!("Resolving reference"); + let ident_rule = pairs.next().expect("Expected ident"); + return resolve_value(ident_rule.as_str(), ident_rule.as_span(), consts, enums, allow_reference); + } + + let mut expression_tree = Expression::new_expression_tree(pairs, consts, enums)?; + info!("Expression tree: {:?}", expression_tree); + + let value = expression_tree.eval(); + info!("Value: {:?}", value); + let value_type = value.determine_type(); + let casted_value = cast_to(value, target_type, strict); + trace!("Casted value: {:?}", casted_value); + match casted_value { + Some(v) => { + Ok(v) + } + None => { + Err(Box::new(Error::new_from_pos(ErrorVariant::CustomError { + message: format!("Cannot cast {:?} to {:?}", value_type, target_type).to_string(), + }, pos))) + } + } +} + + +/// Parses the decimal value +/// +/// # Arguments +/// +/// * `str` - The string to parse +/// +/// # Returns +/// +/// The parsed value +pub(crate) fn parse_decimal(str: &str) -> Result { + let pos = str.find(|c: char| c.is_alphabetic()); + let (value, suffix) = match pos { + Some(pos) => str.split_at(pos), + None => (str, ""), + }; + if suffix.is_empty() { + return Ok(Value::Int64(value.parse::()?)); + } + Ok(Value::UInt64(value.parse::()?)) +} + +/// Parses the hexadecimal value +/// +/// # Arguments +/// +/// * `str` - The string to parse +/// +/// # Returns +/// +/// The parsed value +pub(crate) fn parse_hexadecimal(str: &str) -> Result { + let value = str.trim_start_matches("0x"); + let pos = value.find(|c: char| c.is_alphabetic()); + let (value, suffix) = match pos { + Some(pos) => value.split_at(pos), + None => (value, ""), + }; + if suffix.is_empty() { + return Ok(Value::Int64(i64::from_str_radix(value, 16)?)); + } + Ok(Value::UInt64(u64::from_str_radix(value, 16)?)) +} + +/// Parses the binary value +/// +/// # Arguments +/// +/// * `str` - The string to parse +/// +/// # Returns +/// +/// The parsed value +pub(crate) fn parse_binary(str: &str) -> Result { + let value = str.trim_start_matches("0b"); + let pos = value.find(|c: char| c.is_alphabetic()); + let (value, suffix) = match pos { + Some(pos) => value.split_at(pos), + None => (value, ""), + }; + if suffix.is_empty() { + return Ok(Value::Int64(i64::from_str_radix(value, 2)?)); + } + Ok(Value::UInt64(u64::from_str_radix(value, 2)?)) +} + +/// Parses the floating point value +/// +/// # Arguments +/// +/// * `str` - The string to parse +/// +/// # Returns +/// +/// The parsed value +pub(crate) fn parse_floating_point(str: &str) -> Result { + Ok(Value::Double(f64::from_str(str)?)) +} + +/// Casts the given value to the target type +/// +/// # Arguments +/// +/// * `value` - The value to cast +/// * `target_type` - The target type +/// * `strict` - Whether to cast strictly +/// +/// # Returns +/// +/// The casted value if possible, otherwise None +pub(crate) fn cast_to(value: Value, target_type: &Type, strict: bool) -> Option { + if let Value::ReferencedValue { value, .. } = value { + return cast_to(*value, target_type, strict); + } + if strict && value.is_integer() { + return match target_type { + Type::Int8 => Some(Value::Int8(value.to_i8())), + Type::Int16 => Some(Value::Int16(value.to_i16())), + Type::Int32 => Some(Value::Int32(value.to_i32())), + Type::Int64 => Some(Value::Int64(value.to_i64())), + Type::UInt8 => Some(Value::Uint8(value.to_u8())), + Type::UInt16 => Some(Value::Uint16(value.to_u16())), + Type::UInt32 => Some(Value::Uint32(value.to_u32())), + _ => None, + }; + } + if strict && !value.is_integer() { + return match target_type { + Type::Float => Some(Value::Float(value.to_f32())), + Type::Double => Some(Value::Double(value.to_f64())), + _ => None, + }; + } + + match target_type { + Type::Int8 => Some(Value::Int8(value.to_i8())), + Type::Int16 => Some(Value::Int16(value.to_i16())), + Type::Int32 => Some(Value::Int32(value.to_i32())), + Type::Int64 => Some(Value::Int64(value.to_i64())), + Type::UInt8 => Some(Value::Uint8(value.to_u8())), + Type::UInt16 => Some(Value::Uint16(value.to_u16())), + Type::UInt32 => Some(Value::Uint32(value.to_u32())), + Type::UInt64 => Some(Value::UInt64(value.to_u64())), + Type::Float => Some(Value::Float(value.to_f32())), + Type::Double => Some(Value::Double(value.to_f64())), + _ => None, + } +} + +/// Extracts the expression rule from the given rules +/// +/// # Arguments +/// +/// * `rules` - The rules to extract the expression rule from +/// +/// # Returns +/// +/// The expression rule if found, otherwise None +pub(crate) fn extract_expression_rule_tag<'a>( + rules: &'a Pairs<'a, Rule>, +) -> Option> { + if let Some(rule) = rules.find_first_tagged("exp") { + return Some(rule); + } + None +} + diff --git a/erpcgen_rust/src/parser/util/interface_definition.rs b/erpcgen_rust/src/parser/util/interface_definition.rs new file mode 100644 index 00000000..d3db4ccd --- /dev/null +++ b/erpcgen_rust/src/parser/util/interface_definition.rs @@ -0,0 +1,192 @@ +/* + * Copyright 2024 Marek Mišík(nxf76107) + * + * SPDX-License-Identifier: BSD-3-Clause + */ + + +use pest::iterators::{Pair, Pairs}; +use crate::parser::grammar_parser::Rule; + + +/// Extracts the interface definition rule from the given rules +/// +/// # Arguments +/// +/// * `rules` - The rules to extract the interface definition rule from +/// +/// # Returns +/// +/// The interface definition rule if found, otherwise None +pub(crate) fn extract_oneway_rule<'a>( + rules: &'a Pairs, +) -> Option> { + rules.find_first_tagged("ow") +} + + +/// Extracts the interface definition rule from the given rules +/// +/// # Arguments +/// +/// * `rules` - The rules to extract the interface definition rule from +/// +/// # Returns +/// +/// The interface definition rule if found, otherwise None +pub(crate) fn extract_return_type_rule<'a>( + rules: &'a Pairs, +) -> Option> { + rules.find_first_tagged("rt") +} + + +/// Extracts the interface definition rule from the given rules +/// +/// # Arguments +/// +/// * `rules` - The rules to extract the interface definition rule from +/// +/// # Returns +/// +/// The interface definition rule if found, otherwise None +pub(crate) fn extract_parameter_list_rule<'a>( + rules: &'a Pairs, +) -> Option> { + rules.find_first_tagged("pl") +} + +/// Extracts the interface definition rule from the given rules +/// +/// # Arguments +/// +/// * `rules` - The rules to extract the interface definition rule from +/// +/// # Returns +/// +/// The interface definition rule if found, otherwise None +pub(crate) fn extract_callback_type_definition_list_rule<'a>( + rules: &'a Pairs<'a, Rule> +) -> Option> { + rules.find_first_tagged("cl") +} + +/// Extracts the interface definition rule from the given rules +/// +/// # Arguments +/// +/// * `rules` - The rules to extract the interface definition rule from +/// +/// # Returns +/// +/// The interface definition rule if found, otherwise None +pub(crate) fn extract_function_definition_list_rule<'a>( + rules: &'a Pairs, +) -> Option> { + rules.find_first_tagged("fl") +} + +/// Extracts the interface definition rule from the given rules +/// +/// # Arguments +/// +/// * `rules` - The rules to extract the interface definition rule from +/// +/// # Returns +/// +/// The interface definition rule if found, otherwise None +pub(crate) fn extract_definition<'a>( + rules: &'a Pairs, +) -> Option> { + rules.find_first_tagged("def") +} + +/// Extracts the interface definition rule from the given rules +/// +/// # Arguments +/// +/// * `rules` - The rules to extract the interface definition rule from +/// +/// # Returns +/// +/// The interface definition rule if found, otherwise None +pub(crate) fn extract_param_direction<'a>( + rules: &'a Pairs, +) -> Option> { + rules.find_first_tagged("pd") +} + +/// Extracts the interface definition rule from the given rules +/// +/// # Arguments +/// +/// * `rules` - The rules to extract the interface definition rule from +/// +/// # Returns +/// +/// The interface definition rule if found, otherwise None +pub(crate) fn extract_scope_ident<'a>( + rules: &'a Pairs, +) -> Option> { + rules.find_first_tagged("scp") +} + +/// Extracts the interface definition rule from the given rules +/// +/// # Arguments +/// +/// * `rules` - The rules to extract the interface definition rule from +/// +/// # Returns +/// +/// The interface definition rule if found, otherwise None +pub(crate) fn extract_simple_data_type_scope<'a>( + rules: &'a Pairs, +) -> Option> { + rules.find_first_tagged("sds") +} + +/// Extracts the interface definition rule from the given rules +/// +/// # Arguments +/// +/// * `rules` - The rules to extract the interface definition rule from +/// +/// # Returns +/// +/// The interface definition rule if found, otherwise None +pub(crate) fn extract_parameter_definition_list( + rules: Pairs, +) -> Vec> { + rules.find_tagged("pdf").collect() +} + +/// Extracts the interface definition rule from the given rules +/// +/// # Arguments +/// +/// * `rules` - The rules to extract the interface definition rule from +/// +/// # Returns +/// +/// The interface definition rule if found, otherwise None +pub(crate) fn extract_void_rule<'a>( + rules: &'a Pairs, +) -> Option> { + rules.find_first_tagged("vo") +} + +/// Extracts the interface definition rule from the given rules +/// +/// # Arguments +/// +/// * `rules` - The rules to extract the interface definition rule from +/// +/// # Returns +/// +/// The interface definition rule if found, otherwise None +pub(crate) fn extract_callback_parameters( + rules: Pairs, +) -> Vec> { + rules.find_tagged("cbp").collect() +} diff --git a/erpcgen_rust/src/parser/util/mod.rs b/erpcgen_rust/src/parser/util/mod.rs new file mode 100644 index 00000000..e00a9914 --- /dev/null +++ b/erpcgen_rust/src/parser/util/mod.rs @@ -0,0 +1,16 @@ +/* + * Copyright 2024 Marek Mišík(nxf76107) + * + * SPDX-License-Identifier: BSD-3-Clause + */ + + +pub(crate) mod const_definition; +pub(crate) mod common; +pub(crate) mod expression; +pub(crate) mod annotations; +pub(crate) mod struct_definition; +pub(crate) mod enum_definition; +pub(crate) mod union_definition; +pub(crate) mod interface_definition; +pub(crate) mod type_definition; \ No newline at end of file diff --git a/erpcgen_rust/src/parser/util/struct_definition.rs b/erpcgen_rust/src/parser/util/struct_definition.rs new file mode 100644 index 00000000..493eba47 --- /dev/null +++ b/erpcgen_rust/src/parser/util/struct_definition.rs @@ -0,0 +1,87 @@ +/* + * Copyright 2024 Marek Mišík(nxf76107) + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +use pest::error::{Error, ErrorVariant}; +use pest::iterators::{Pair, Pairs}; +use crate::parser::grammar_parser::Rule; +use crate::symbols::member_options::MemberOptions; + + +/// Extracts the struct definition rule from the given rules +/// +/// # Arguments +/// +/// * `rules` - The rules to extract the struct definition rule from +/// +/// # Returns +/// +/// The struct definition rule if found, otherwise None +pub(crate) fn extract_struct_member_options<'a>(rules: &'a Pairs) -> Option> { + rules.find_first_tagged("ol") +} + +/// Parses the member options +/// +/// # Arguments +/// +/// * `member_option_rules` - The member option rules to parse +/// * `strict` - Whether to enforce strict parsing +/// +/// # Returns +/// +/// The parsed member options +pub(crate) fn parse_member_options(member_option_rules: Pairs, strict: bool) -> Result, Box>> { + let mut member_options = vec![]; + for member_option_rule in member_option_rules { + match member_option_rule.as_rule() { + Rule::byref => { + if strict && member_options.contains(&MemberOptions::Byref) { + return Err(Box::new(Error::new_from_span(ErrorVariant::CustomError { + message: "Duplicate byref member option".to_string() + }, member_option_rule.as_span()))); + } + member_options.push(MemberOptions::Byref); + } + Rule::optional => { + if strict && member_options.contains(&MemberOptions::Byref) { + return Err(Box::new(Error::new_from_span(ErrorVariant::CustomError { + message: "Duplicate nullable member option".to_string() + }, member_option_rule.as_span()))); + } + member_options.push(MemberOptions::Optional); + } + _ => unreachable!(), + } + } + Ok(member_options) +} + +/// Extracts the struct definition rule from the given rules +/// +/// # Arguments +/// +/// * `rules` - The rules to extract the struct definition rule from +/// +/// # Returns +/// +/// The struct definition rule if found, otherwise None +pub(crate) fn extract_discriminator_rule<'a>(rules: &'a Pairs) -> Option> { + rules.find_first_tagged("disc") +} + +/// Extracts the struct definition rule from the given rules +/// +/// # Arguments +/// +/// * `rules` - The rules to extract the struct definition rule from +/// +/// # Returns +/// +/// The struct definition rule if found, otherwise None +pub(crate) fn extract_options_list<'a>(rules: &'a Pairs) -> Option> { + rules.find_first_tagged("ol") +} + diff --git a/erpcgen_rust/src/parser/util/type_definition.rs b/erpcgen_rust/src/parser/util/type_definition.rs new file mode 100644 index 00000000..c4e49c52 --- /dev/null +++ b/erpcgen_rust/src/parser/util/type_definition.rs @@ -0,0 +1,24 @@ +/* + * Copyright 2024 Marek Mišík(nxf76107) + * + * SPDX-License-Identifier: BSD-3-Clause + */ + + +use pest::iterators::{Pair, Pairs}; +use crate::parser::grammar_parser::Rule; + +/// Extracts the data type rule from the given rules +/// +/// # Arguments +/// +/// * `rules` - The rules to extract the data type rule from +/// +/// # Returns +/// +/// The data type rule if found, otherwise None +pub(crate) fn extract_data_type_rule<'a>( + rules: &'a Pairs<'a, Rule>, +) -> Option> { + rules.find_first_tagged("dt") +} \ No newline at end of file diff --git a/erpcgen_rust/src/parser/util/union_definition.rs b/erpcgen_rust/src/parser/util/union_definition.rs new file mode 100644 index 00000000..0c73f4ff --- /dev/null +++ b/erpcgen_rust/src/parser/util/union_definition.rs @@ -0,0 +1,21 @@ +/* + * Copyright 2024 Marek Mišík(nxf76107) + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +use pest::iterators::{Pair, Pairs}; +use crate::parser::grammar_parser::Rule; + +/// Finds the default rule +/// +/// # Arguments +/// +/// * `rules` - The rules to find the default rule from +/// +/// # Returns +/// +/// The default rule if found, otherwise None +pub(crate) fn find_default_rule<'a>(rules: &Pairs<'a, Rule>) -> Option> { + rules.find_first_tagged("dc") +} \ No newline at end of file diff --git a/erpcgen_rust/src/symbols/annotations.rs b/erpcgen_rust/src/symbols/annotations.rs new file mode 100644 index 00000000..218db4fc --- /dev/null +++ b/erpcgen_rust/src/symbols/annotations.rs @@ -0,0 +1,118 @@ +/* + * Copyright 2024 Marek Mišík(nxf76107) + * Copyright 2024 NXP + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +use crate::symbols::language::Language; +use crate::symbols::value::Value; + +/// Represents an annotation in the IDL +#[derive(Debug, Clone, PartialEq, Hash)] +pub enum Annotation { + TypesHeader { + language_specifier: Option, + }, + SharedMemoryBegin { + language_specifier: Option, + address: usize, + }, + SharedMemoryEnd { + language_specifier: Option, + address: usize, + }, + Shared { + language_specifier: Option, + }, + Retain { + language_specifier: Option, + }, + OutputDir { + language_specifier: Option, + path: String, + }, + Nullable { + language_specifier: Option, + }, + NoInfraErrors { + language_specifier: Option, + }, + NoConstParam { + language_specifier: Option, + }, + NoAllocErrors { + language_specifier: Option, + }, + Name { + language_specifier: Option, + name: String, + }, + MaxLength { + language_specifier: Option, + length: Value, + }, + Length { + language_specifier: Option, + length: Value, + }, + Include { + language_specifier: Option, + path: String, + }, + Id { + language_specifier: Option, + id: usize, + }, + Group { + language_specifier: Option, + group: String, + }, + External { + language_specifier: Option, + }, + ErrorReturn { + language_specifier: Option, + value: i32, + }, + Discriminator { + language_specifier: Option, + value: Value, + }, + Crc { + language_specifier: Option, + }, +} + +impl Annotation { + /// Get the language specifier of the annotation + /// + /// # Returns + /// + /// The language specifier of the annotation + pub(crate) fn get_language_specifier(&self) -> Option { + match self { + Annotation::TypesHeader { language_specifier } => language_specifier.clone(), + Annotation::SharedMemoryBegin { language_specifier, .. } => language_specifier.clone(), + Annotation::SharedMemoryEnd { language_specifier, .. } => language_specifier.clone(), + Annotation::Shared { language_specifier } => language_specifier.clone(), + Annotation::Retain { language_specifier } => language_specifier.clone(), + Annotation::OutputDir { language_specifier, .. } => language_specifier.clone(), + Annotation::Nullable { language_specifier } => language_specifier.clone(), + Annotation::NoInfraErrors { language_specifier } => language_specifier.clone(), + Annotation::NoConstParam { language_specifier } => language_specifier.clone(), + Annotation::NoAllocErrors { language_specifier } => language_specifier.clone(), + Annotation::Name { language_specifier, .. } => language_specifier.clone(), + Annotation::MaxLength { language_specifier, .. } => language_specifier.clone(), + Annotation::Length { language_specifier, .. } => language_specifier.clone(), + Annotation::Include { language_specifier, .. } => language_specifier.clone(), + Annotation::Id { language_specifier, .. } => language_specifier.clone(), + Annotation::Group { language_specifier, .. } => language_specifier.clone(), + Annotation::External { language_specifier } => language_specifier.clone(), + Annotation::ErrorReturn { language_specifier, .. } => language_specifier.clone(), + Annotation::Discriminator { language_specifier, .. } => language_specifier.clone(), + Annotation::Crc { language_specifier } => language_specifier.clone(), + } + } +} + diff --git a/erpcgen_rust/src/symbols/const_definition.rs b/erpcgen_rust/src/symbols/const_definition.rs new file mode 100644 index 00000000..7aa7f610 --- /dev/null +++ b/erpcgen_rust/src/symbols/const_definition.rs @@ -0,0 +1,198 @@ +/* + * Copyright 2024 Marek Mišík(nxf76107) + * Copyright 2024 NXP + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +use std::collections::HashMap; +use std::rc::Rc; + +use pest::error::{Error, ErrorVariant}; +use pest::iterators::Pair; + +use crate::parser::grammar_parser::Rule; +use crate::symbols::annotations::Annotation; +use crate::symbols::doxygen_comment::DoxygenComment; +use crate::symbols::program::Program; +use crate::symbols::types::Type; +use crate::symbols::util::apply_rename_for_type; +use crate::symbols::value::Value; + +/// A constant definition +#[derive(Debug, Clone)] +pub(crate) struct ConstDefinition{ + pub(crate) name: String, + pub(crate) annotations: Vec, + pub(crate) const_type: Rc, + pub(crate) value: Value, + pub(crate) doxygen_preceding_comment: Vec, + pub(crate) doxygen_trailing_comment: Vec, +} + +impl ConstDefinition { + pub(crate) fn apply_rename(&mut self, renames: &HashMap) { + self.name = renames.get(&self.name).unwrap_or(&self.name).to_string(); + self.const_type = apply_rename_for_type(self.const_type.clone(), renames); + } +} + +/// A builder for a constant definition +pub(crate) struct ConstDefinitionBuilder<'a> { + program: &'a Program, + strict: bool, + name: Option, + annotations: Vec, + const_type: Option>, + value: Option, + doxygen_preceding_comment: Vec, + doxygen_trailing_comment: Vec, +} + +/// Implementation of the builder +impl<'a> ConstDefinitionBuilder<'a> { + pub fn new(program: &'a Program, strict: bool) -> Self { + Self { + program, + strict, + name: None, + annotations: Vec::new(), + const_type: None, + value: None, + doxygen_preceding_comment: vec![], + doxygen_trailing_comment: vec![], + } + } + + /// Add annotations to the constant definition + /// + /// # Arguments + /// + /// * `comment` - The annotations to add + /// + /// # Returns + /// + /// * The builder + pub(crate) fn with_doxygen_preceding_comment(mut self, comment: Vec) -> Self { + self.doxygen_preceding_comment = comment; + self + } + + /// Add annotations to the constant definition + /// + /// # Arguments + /// + /// * `comment` - The annotations to add + /// + /// # Returns + /// + /// * The builder + pub(crate) fn with_doxygen_trailing_comment(mut self, comment: Vec) -> Self { + self.doxygen_trailing_comment = comment; + self + } + + /// Add annotations to the constant definition + /// + /// # Arguments + /// + /// * `rule` - The annotations to add + /// + /// # Returns + /// + /// * The builder + pub(crate) fn with_annotations(self, rule: Pair) -> Result>> { + // There are no annotations currently supported for const definitions + Ok(self) + } + + /// Add annotations to the constant definition + /// + /// # Arguments + /// + /// * `type_rule` - The annotations to add + /// + /// # Returns + /// + /// * The builder + pub(crate) fn with_type(mut self, type_rule: Pair) -> Result>> { + let const_type = self.program.resolve_simple_data_type(type_rule)?; + self.const_type = Some(const_type); + Ok(self) + } + + /// Sets the name of the constant definition. + /// + /// This method takes a `Pair` as input, which represents the parsed name of the constant definition. + /// It checks if the name is already in use in the program. If the name is already in use, it returns an error. + /// If the name is not in use, it sets the name of the constant definition and returns the builder. + /// + /// # Arguments + /// + /// * `rule` - The parsed name of the constant definition. This is a `Pair` because the name is parsed from the input using the Pest parsing library. + /// + /// # Returns + /// + /// A result containing either the builder (if the name was successfully set) or an error (if the name is already in use). + pub(crate) fn with_name(mut self, rule: Pair) -> Result>> { + let location = rule.as_span(); + let name = rule.as_str().to_string(); + if !self.program.check_symbol_name_is_free(&name) { + return Err(Box::new(Error::new_from_span( + ErrorVariant::CustomError { + message: format!("Symbol name {} is already in use", name).to_string(), + }, + location, + ))); + } + self.name = Some(name); + Ok(self) + } + + /// Sets the value of the constant definition. + /// + /// This method takes an expression as input and resolves it to a value that is set as the value of the constant definition. + /// The type of the constant definition must be defined before the value can be set. If the type is not defined, an error is returned. + /// The value is resolved using the `resolve_value_from_expression` method of the program. This method takes the expression, the type of the constant definition, and a boolean indicating whether the resolution should be strict as input, and returns the resolved value. + /// + /// # Arguments + /// + /// * `expression` - The expression that should be resolved to a value. This is a Pair because the expression is parsed from the input using the Pest parsing library. + /// + /// # Returns + /// + /// A result containing either the builder (if the value was successfully set) or an error (if the type of the constant definition was not defined or if the value could not be resolved). + pub fn with_value(mut self, expression: Pair) -> Result>> { + let span = expression.as_span(); + + let Some(const_type) = self.const_type.clone() else { + return Err(Box::new(Error::new_from_span( + ErrorVariant::CustomError { + message: "Const type must be defined before value".to_string(), + }, + span, + ))); + }; + + let value = self.program.resolve_value_from_expression(expression, const_type.as_ref(), self.strict)?; + + self.value = Some(value); + Ok(self) + } + + /// Build the constant definition + /// + /// # Returns + /// + /// * The constant definition + pub(crate) fn build(self) -> ConstDefinition { + ConstDefinition { + name: self.name.unwrap(), + annotations: self.annotations, + const_type: self.const_type.unwrap(), + value: self.value.unwrap(), + doxygen_preceding_comment: self.doxygen_preceding_comment, + doxygen_trailing_comment: self.doxygen_trailing_comment, + } + } +} \ No newline at end of file diff --git a/erpcgen_rust/src/symbols/doxygen_comment.rs b/erpcgen_rust/src/symbols/doxygen_comment.rs new file mode 100644 index 00000000..0187f512 --- /dev/null +++ b/erpcgen_rust/src/symbols/doxygen_comment.rs @@ -0,0 +1,13 @@ +/* + * Copyright 2024 Marek Mišík(nxf76107) + * Copyright 2024 NXP + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +/// Represents a doxygen comment. +#[derive(Debug, PartialEq, Clone)] +pub(crate) enum DoxygenComment { + SingleLine(String), + MultiLine(String), +} \ No newline at end of file diff --git a/erpcgen_rust/src/symbols/enum_definition.rs b/erpcgen_rust/src/symbols/enum_definition.rs new file mode 100644 index 00000000..6904aaa6 --- /dev/null +++ b/erpcgen_rust/src/symbols/enum_definition.rs @@ -0,0 +1,239 @@ +/* + * Copyright 2024 Marek Mišík(nxf76107) + * Copyright 2024 NXP + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +use std::collections::HashMap; +use std::hash::{Hash, Hasher}; +use log::{debug, trace}; +use crate::parser::grammar_parser::Rule; +use crate::parser::util::annotations::{find_name_annotation, parse_enum_annotations}; +use crate::symbols::annotations::Annotation; +use crate::symbols::enum_member::{EnumMember, EnumMemberBuilder}; +use crate::symbols::program::Program; +use pest::error::Error; +use pest::iterators::Pair; +use crate::symbols::doxygen_comment::DoxygenComment; + +/// An enumeration definition +#[derive(Debug, PartialEq, Clone)] +pub(crate) struct EnumDefinition { + pub(crate) name: Option, + pub(crate) members: Vec, + pub(crate) annotations: Vec, + pub(crate) doxygen_preceding_comment: Vec, + pub(crate) doxygen_trailing_comment: Vec, +} + + +/// Implementation of the equality for the enumeration definition +impl Eq for EnumDefinition { } + +/// Implementation of the enumeration definition +impl EnumDefinition { + pub fn new( + name: Option, + members: Vec, + annotations: Vec, + doxygen_preceding_comment: Vec, + doxygen_trailing_comment: Vec, + ) -> Self { + Self { + name, + members, + annotations, + doxygen_preceding_comment, + doxygen_trailing_comment, + } + } +} + +/// Hash implementation for the enumeration definition +impl Hash for EnumDefinition { + fn hash(&self, state: &mut H) { + self.name.hash(state); + } +} + +impl EnumDefinition { + pub fn apply_rename(&mut self, renames: &HashMap) { + if let Some(name) = &self.name { + self.name = Some(renames.get(name).unwrap_or(name).to_string()); + } + self.members.iter_mut().for_each(|m| m.apply_rename(renames)); + } +} + +/// A builder for an enumeration definition +pub(crate) struct EnumDefinitionBuilder<'a> { + pub(super) program: &'a Program, + strict: bool, + name: Option, + pub(super) members: Vec, + annotations: Vec, + doxygen_preceding_comment: Vec, + doxygen_trailing_comment: Vec, +} + +/// Implementation of the builder +impl<'a> EnumDefinitionBuilder<'a> { + pub fn new(program: &'a Program, strict: bool) -> Self { + Self { + program, + strict, + name: None, + members: vec![], + annotations: vec![], + doxygen_preceding_comment: vec![], + doxygen_trailing_comment: vec![], + } + } + + /// Add a name to the enumeration definition + /// + /// # Arguments + /// + /// * `rule_opt` - The name to add + /// + /// # Returns + /// + /// * The builder + /// + /// # Errors + /// + /// * If the name is already in use + pub(crate) fn with_name(mut self, rule_opt: Option>) -> Result>> { + // We look for the name in the annotations + let name_from_annotation = find_name_annotation(&self.annotations); + if let Some(Annotation::Name { name, ..}) = name_from_annotation { + self.name = Some(name.clone()); + return Ok(self); + } + + let Some(rule) = rule_opt else { + return Ok(self) + }; + let location = rule.as_span(); + let name = rule.as_str().to_string(); + debug!("With name: {}", name); + if !self.program.check_symbol_name_is_free(&name) { + return Err(Box::new(Error::new_from_span( + pest::error::ErrorVariant::CustomError { + message: format!("Symbol name {} is already in use", name).to_string(), + }, + location, + ))); + } + self.name = Some(name); + Ok(self) + } + + /// Check if the enumeration member name is free + /// + /// # Arguments + /// + /// * `name` - The name to check + /// + /// # Returns + /// + /// * True if the name is free + pub(super) fn check_enum_member_name_is_free(&self, name: &str) -> bool { + self.members.iter().all(|member| member.name != name) + } + + /// Add a member to the enumeration definition + /// + /// # Arguments + /// + /// * `member` - The member to add + /// + /// # Returns + /// + /// * The builder + pub(crate) fn add_member(mut self, member: EnumMember) -> Self { + self.members.push(member); // We assume that has valid name from the builder of enum member + self + } + + /// Add annotations to the enumeration definition + /// + /// # Arguments + /// + /// * `annotations_list_rule` - The annotations to add + /// + /// # Returns + /// + /// * The builder + pub(crate) fn annotations( + mut self, + annotations_list_rule: Pair, + ) -> Result>> { + self.annotations = parse_enum_annotations(annotations_list_rule, &self.program.language, self.strict)?; + Ok(self) + } + + /// Add doxygen preceding comment to the enumeration definition + /// + /// # Arguments + /// + /// * `comment` - The comment to add + /// + /// # Returns + /// + /// * The builder + pub(crate) fn with_doxygen_preceding_comment(mut self, comment: Vec) -> Self { + trace!("With doxygen preceding comment: {:?}", comment); + self.doxygen_preceding_comment = comment; + self + } + + /// Add doxygen trailing comment to the enumeration definition + /// + /// # Arguments + /// + /// * `comment` - The comment to add + /// + /// # Returns + /// + /// * The builder + pub(crate) fn with_doxygen_trailing_comment(mut self, comment: Vec) -> Self { + trace!("With doxygen trailing comment: {:?}", comment); + self.doxygen_trailing_comment = comment; + self + } + + /// Add an enumeration member + /// + /// # Arguments + /// + /// * `member` - The member to add + /// + /// # Returns + /// + /// * The builder + pub(crate) fn add_enum_member(&mut self, member: EnumMember) { + self.members.push(member); + } + + /// Create a new enumeration member builder + /// + /// # Returns + /// + /// * The builder + pub(crate) fn new_member_builder(&'a self) -> EnumMemberBuilder<'a> { + EnumMemberBuilder::new(self, true) + } + + /// Build the enumeration definition + pub(crate) fn build(self) -> EnumDefinition { + EnumDefinition::new( + self.name, + self.members, + self.annotations, + self.doxygen_preceding_comment, + self.doxygen_trailing_comment, + ) + } +} diff --git a/erpcgen_rust/src/symbols/enum_member.rs b/erpcgen_rust/src/symbols/enum_member.rs new file mode 100644 index 00000000..770593dd --- /dev/null +++ b/erpcgen_rust/src/symbols/enum_member.rs @@ -0,0 +1,217 @@ +/* + * Copyright 2024 Marek Mišík(nxf76107) + * Copyright 2024 NXP + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +use std::collections::HashMap; +use log::{debug, trace}; +use crate::parser::grammar_parser::Rule; +use crate::parser::util::annotations::parse_enum_annotations; +use crate::parser::util::enum_definition::resolve_enum_member_value; +use crate::symbols::annotations::Annotation; +use crate::symbols::enum_definition::EnumDefinitionBuilder; +use crate::symbols::types::Type; +use crate::symbols::value::Value; +use pest::error::{Error, ErrorVariant}; +use pest::iterators::Pair; +use crate::symbols::doxygen_comment::DoxygenComment; + +/// An enumeration member +#[derive(Debug, PartialEq, Clone)] +pub(crate) struct EnumMember { + pub(crate) name: String, + pub(crate) value: Value, + pub(crate) annotations: Vec, + pub(crate) doxygen_preceding_comment: Vec, + pub(crate) doxygen_trailing_comment: Vec, +} + +/// A builder for an enumeration member +impl EnumMember { + pub fn new( + name: String, + value: Value, + annotations: Vec, + doxygen_preceding_comment: Vec, + doxygen_trailing_comment: Vec, + ) -> Self { + Self { + name, + value, + annotations, + doxygen_preceding_comment, + doxygen_trailing_comment, + } + } + + pub fn apply_rename(&mut self, renames: &HashMap) { + self.name = renames.get(&self.name).unwrap_or(&self.name).to_string(); + } +} + +/// A builder for an enumeration member +pub(crate) struct EnumMemberBuilder<'a> { + enum_builder: &'a EnumDefinitionBuilder<'a>, + strict: bool, + name: Option, + value: Option, + annotations: Vec, + doxygen_preceding_comment: Vec, + doxygen_trailing_comment: Vec, +} + +/// Implementation of the builder +impl<'a> EnumMemberBuilder<'a> { + pub(super) fn new( + enum_builder: &'a EnumDefinitionBuilder<'a>, + strict: bool, + ) -> EnumMemberBuilder<'a> { + Self { + enum_builder, + strict, + name: None, + value: None, + annotations: vec![], + doxygen_preceding_comment: vec![], + doxygen_trailing_comment: vec![], + } + } + + /// Set the name of the enumeration member + /// + /// # Arguments + /// + /// * `rule` - The rule containing the name of the enumeration member + /// + /// # Returns + /// + /// * The enumeration member builder + /// + /// # Errors + /// + /// * If the name is already in use + pub fn with_name(mut self, rule: Pair) -> Result>> { + let location = rule.as_span(); + let name = rule.as_str().to_string(); + debug!("With name: {}", name); + if !self.enum_builder.program.check_symbol_name_is_free(&name) + || !self.enum_builder.check_enum_member_name_is_free(&name) + { + return Err(Box::new(Error::new_from_span( + ErrorVariant::CustomError { + message: format!("Symbol name {} is already in use", name).to_string(), + }, + location, + ))); + } + + self.name = Some(name); + Ok(self) + } + + /// Set the value of the enumeration member + /// + /// # Arguments + /// + /// * `expression` - The rule containing the value of the enumeration member + /// + /// # Returns + /// + /// * The enumeration member builder + /// + /// # Errors + /// + /// * If the value is not an integer + pub fn with_value(mut self, expression: Pair) -> Result>> { + let span = expression.as_span(); + let value = self.enum_builder.program.resolve_value_from_expression(expression, &Type::Int32, false)?; + debug!("With value: {:?}", value); + if !value.is_integer() { + return Err(Box::new(Error::new_from_span( + ErrorVariant::CustomError { + message: "Enum member value must be an integer".to_string(), + }, + span, + ))); + } + + self.value = Some(value); + Ok(self) + } + + /// Add annotations to the enumeration member + /// + /// # Arguments + /// + /// * `annotations_list_rule` - The rule containing the annotations + /// + /// # Returns + /// + /// * The enumeration member builder + pub(crate) fn annotations( + mut self, + annotations_list_rule: Pair, + ) -> Result>> { + self.annotations = parse_enum_annotations(annotations_list_rule, &self.enum_builder.program.language, self.strict)?; + Ok(self) + } + + /// Add doxygen preceding comment to the enumeration member + /// + /// # Arguments + /// + /// * `comment` - The doxygen comment to add + /// + /// # Returns + /// + /// * The enumeration member builder + pub(crate) fn with_doxygen_preceding_comment(mut self, comment: Vec) -> Self { + trace!("With doxygen preceding comment: {:?}", comment); + self.doxygen_preceding_comment = comment; + self + } + + /// Add doxygen trailing comment to the enumeration member + /// + /// # Arguments + /// + /// * `comment` - The doxygen comment to add + /// + /// # Returns + /// + /// * The enumeration member builder + pub(crate) fn with_doxygen_trailing_comment(mut self, comment: Vec) -> Self { + trace!("With doxygen trailing comment: {:?}", comment); + self.doxygen_trailing_comment = comment; + self + } + + /// Build the enumeration member + /// + /// # Returns + /// + /// * The enumeration member + /// + /// # Panics + /// + /// * If the enumeration member name is not set + pub fn build(self) -> EnumMember { + let name = self.name.expect("Enum member name not set"); + let value = match self.value { + Some(v) => v, + None => { + let member = self.enum_builder.members.iter().last(); + resolve_enum_member_value(member) + } + }; + EnumMember::new( + name, + value, + self.annotations, + self.doxygen_preceding_comment, + self.doxygen_trailing_comment, + ) + } +} diff --git a/erpcgen_rust/src/symbols/function_definition.rs b/erpcgen_rust/src/symbols/function_definition.rs new file mode 100644 index 00000000..63d5dfce --- /dev/null +++ b/erpcgen_rust/src/symbols/function_definition.rs @@ -0,0 +1,523 @@ +/* + * Copyright 2024 Marek Mišík(nxf76107) + * Copyright 2024 NXP + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +use std::collections::HashMap; +use crate::parser::grammar_parser::Rule; +use crate::parser::util::annotations::{find_name_annotation, parse_function_annotations}; +use crate::parser::util::common::extract_simple_data_type; +use crate::parser::util::interface_definition::{ + extract_callback_parameters, extract_scope_ident, extract_void_rule, +}; +use crate::symbols::annotations::Annotation; +use crate::symbols::doxygen_comment::DoxygenComment; +use crate::symbols::function_parameter::{FunctionParameter, FunctionParameterBuilder}; +use crate::symbols::interface_definition::InterfaceDefinitionBuilder; +use crate::symbols::pending_ident::{PendingError, PendingIdent}; +use crate::symbols::types::Type; +use log::info; +use pest::error::{Error, ErrorVariant}; +use pest::iterators::Pair; +use pest::Position; +use std::hash::{Hash, Hasher}; +use std::rc::Rc; + +/// A function definition +#[derive(Debug, Clone)] +pub(crate) struct FunctionDefinition { + pub(crate) name: String, + pub(crate) id: Option, + pub(crate) prototype: Option>, + pub(crate) renames: HashMap, + pub(crate) is_oneway: bool, + pub(crate) return_type: Option>, + pub(crate) parameters: Vec, + pub(crate) annotations: Vec, + pub(crate) doxygen_preceding_comment: Vec, + pub(crate) doxygen_trailing_comment: Vec, +} + +/// Implementation of the equality for the function definition +impl Hash for FunctionDefinition { + fn hash(&self, state: &mut H) { + self.name.hash(state); + } +} + +impl FunctionDefinition { + pub fn apply_rename(&mut self, interface_renames: &HashMap, program_renames: &HashMap) { + self.name = interface_renames.get(&self.name).unwrap_or(&self.name).to_string(); + self.parameters.iter_mut().for_each(|m| m.apply_rename(&self.renames, program_renames)); + } +} + +/// A builder for a function definition +pub(crate) struct FunctionDefinitionBuilder<'a> { + pub(crate) interface_builder: &'a InterfaceDefinitionBuilder<'a>, + strict: bool, + name: Option, + id: Option, + prototype: Option>, + renames: HashMap, + is_oneway: bool, + return_type: Option>, + parameters: Vec, + annotations: Vec, + doxygen_preceding_comment: Vec, + doxygen_trailing_comment: Vec, + pending_idents: Vec, +} + +/// Implementation of the builder +impl<'a> FunctionDefinitionBuilder<'a> { + pub fn new(interface_builder: &'a InterfaceDefinitionBuilder<'a>, strict: bool) -> Self { + Self { + interface_builder, + strict, + name: None, + id: None, + prototype: None, + renames: HashMap::new(), + is_oneway: false, + return_type: None, + parameters: vec![], + annotations: vec![], + doxygen_preceding_comment: vec![], + doxygen_trailing_comment: vec![], + pending_idents: vec![], + } + } + + /// Creates a new function definition builder from a prototype + /// + /// # Arguments + /// + /// * `rule` - The rule containing the prototype + /// + /// # Returns + /// + /// * The function definition builder + pub(crate) fn from_prototype(mut self, rule: Pair) -> Result>> { + let rules = rule.into_inner(); + let scope_ident = extract_scope_ident(&rules).map(|ident| ident.as_str().to_string()); + let simple_data_type = + extract_simple_data_type(&rules).expect("Simple data type is required"); + let res = self + .interface_builder + .resolve_scoped_type(simple_data_type, &scope_ident)?; + self.prototype = Some(res.clone()); + self.copy_prototype(res); + Ok(self) + } + + /// Copies the prototype of the function + /// + /// # Arguments + /// + /// * `prototype` - The prototype to copy + fn copy_prototype(&mut self, prototype: Rc) { + if let Some(prototype) = self + .interface_builder + .get_callback_definition(prototype.clone()) + { + self.copy_prototype_properties(prototype); + return; + } + if let Some(prototype) = self + .interface_builder + .program + .resolve_function_definition(prototype.clone()) + { + self.copy_prototype_properties(prototype); + } + } + + /// Copies the properties of the prototype + /// + /// # Arguments + /// + /// * `prototype` - The prototype to copy + pub(crate) fn copy_prototype_properties(&mut self, prototype: FunctionDefinition) { + self.is_oneway = prototype.is_oneway; + self.return_type = prototype.return_type; + self.parameters = prototype.parameters; + } + + /// Adds a parameter to the function definition + /// + /// This method adds a parameter to the function definition. + /// It first checks if the parameter name is free, then it checks if the parameter type is valid. + /// If the parameter is valid, it adds it to the function definition. + /// + /// # Arguments + /// + /// * `rule` - The rule containing the parameter + /// + /// # Returns + /// + /// * The builder + /// + /// # Errors + /// + /// * Too much parameters for function + /// * Expected more parameter names + /// * Parameter must exist + + pub(crate) fn with_renamed_parameters( + mut self, + rule: Option>, + ) -> Result>> { + let Some(rule) = rule else { + return Ok(self); + }; + let rules = rule.clone().into_inner(); + let idents = extract_callback_parameters(rules); // This statement is true, because it is handled by with_type + for (idx, ident) in idents.iter().enumerate() { + if idx > self.parameters.len() { + return Err(Box::new(Error::new_from_span( + ErrorVariant::CustomError { + message: "Too much parameters for function".to_string(), + }, + ident.as_span(), + ))); + } + let parameter = self.parameters.get_mut(idx).expect("Parameter must exist"); + parameter.name = Some(ident.as_str().to_string()); + } + let all_named = self.check_all_parameters_have_names(); + if !all_named { + return Err(Box::new(Error::new_from_pos( + ErrorVariant::CustomError { + message: "Expected more parameter names".to_string(), + }, + rule.as_span().end_pos(), + ))); + } + + Ok(self) + } + + /// Adds a parameter to the function definition + /// + /// # Arguments + /// + /// * `comment` - The comment to add + /// + /// # Returns + /// + /// * The builder + pub(crate) fn with_doxygen_preceding_comment(mut self, comment: Vec) -> Self { + self.doxygen_preceding_comment = comment; + self + } + + /// Adds a parameter to the function definition + /// + /// # Arguments + /// + /// * `comment` - The comment to add + /// + /// # Returns + /// + /// * The builder + pub(crate) fn with_doxygen_trailing_comment(mut self, comment: Vec) -> Self { + self.doxygen_trailing_comment = comment; + self + } + + /// Adds a parameter to the function definition + /// + /// # Arguments + /// + /// * `is_oneway` - The flag to set + /// + /// # Returns + /// + /// * The builder + pub(crate) fn with_oneway(mut self, is_oneway: bool) -> Self { + self.is_oneway = is_oneway; + self + } + + /// Adds a parameter to the function definition + /// + /// # Arguments + /// + /// * `rule` - The rule containing the name + /// + /// # Returns + /// + /// * The builder + pub(crate) fn with_name(mut self, rule: Pair) -> Result>> { + let location = rule.as_span(); + let name = rule.as_str().to_string(); + if !self.interface_builder.check_function_name_is_free(&name) { + return Err(Box::new(Error::new_from_span( + ErrorVariant::CustomError { + message: format!("Symbol name {} is already in use", name).to_string(), + }, + location, + ))); + } + self.name = Some(name); + Ok(self) + } + + /// Adds a parameter to the function definition + /// + /// # Arguments + /// + /// * `rule` - The rule containing the name + /// * `unnamed` - The flag to set + /// + /// # Returns + /// + /// * The builder + pub(crate) fn with_name_cb( + mut self, + rule: Pair, + unnamed: bool, + ) -> Result>> { + let location = rule.as_span(); + let name = rule.as_str().to_string(); + if !self.interface_builder.check_function_name_is_free(&name) { + return Err(Box::new(Error::new_from_span( + ErrorVariant::CustomError { + message: format!("Symbol name {} is already in use", name).to_string(), + }, + location, + ))); + } + if unnamed { + let ok = self.check_all_parameters_have_names(); + if !ok { + return Err(Box::new(Error::new_from_pos( + ErrorVariant::CustomError { + message: "Expected parameter name".to_string(), + }, + location.end_pos(), + ))); + } + } + + self.name = Some(name); + Ok(self) + } + + /// Checks if all parameters of the function have names. + /// + /// This method iterates over all parameters of the function and checks if they have a name. + /// It returns `true` if all parameters have a name, and `false` otherwise. + /// + /// # Returns + /// + /// * `bool` - `true` if all parameters have names, `false` otherwise. + fn check_all_parameters_have_names(&self) -> bool { + self.parameters.iter().all(|p| p.name.is_some()) + } + + /// Adds a parameter to the function definition + /// + /// # Arguments + /// + /// * `type_rule` - The rule containing the type + /// * `pos` - The position of the rule + /// + /// # Returns + /// + /// * The builder + pub(crate) fn with_return_type( + mut self, + type_rule: Option>, + pos: Position, + ) -> Result>> { + let Some(pair) = type_rule else { + return if self.is_oneway { + Ok(self) + } else { + Err(Box::new(Error::new_from_pos( + ErrorVariant::CustomError { + message: "Return type is required".to_string(), + }, + pos, + ))) + } + }; + if self.is_oneway { + return Err(Box::new(Error::new_from_span( + ErrorVariant::CustomError { + message: "Oneway functions cannot have a return type".to_string(), + }, + pair.as_span(), + ))); + } + let rules = pair.clone().into_inner(); + + if extract_void_rule(&rules).is_some() { + self.is_oneway = false; + return Ok(self); + }; + + let Some(sdt) = extract_simple_data_type(&rules) else { + return Err(Box::new(Error::new_from_span( + ErrorVariant::CustomError { + message: "Invalid return type".to_string(), + }, + pair.as_span(), + ))); + }; + + let return_type = self + .interface_builder + .program + .resolve_simple_data_type(sdt)?; + + self.return_type = Some(return_type); + Ok(self) + } + + /// Checks if a parameter name is free + /// + /// # Arguments + /// + /// * `name` - The name to check + /// + /// # Returns + /// + /// * `bool` - `true` if the name is free, `false` otherwise + pub(crate) fn check_parameter_name_is_free(&self, name: &str) -> bool { + self.parameters + .iter() + .filter(|p| p.name.is_some()) + .all(|p| p.name.as_ref().unwrap() != name) + } + + /// Creates a new parameter builder + /// + /// # Returns + /// + /// * The parameter builder + pub(crate) fn new_parameter_builder(&self) -> FunctionParameterBuilder { + FunctionParameterBuilder::new(self, self.strict) + } + + /// Adds a parameter to the function definition + /// + /// # Arguments + /// + /// * `parameter` - The parameter to add + /// + /// # Returns + /// + /// * The builder + pub(crate) fn add_parameter( + &mut self, + parameter: FunctionParameter, + pending_idents: Vec, + ) { + info!("Adding parameter {:?}", parameter); + self.check_name_annotation(parameter.name.as_ref().unwrap(), ¶meter.annotations); + self.parameters.push(parameter); + self.pending_idents.extend(pending_idents); + } + + /// Adds annotations to the function definition + /// + /// # Arguments + /// + /// * `annotation_rule` - The rule containing the annotations + /// + /// # Returns + /// + /// * The builder + pub(crate) fn with_annotations( + mut self, + annotation_rule: Pair, + ) -> Result>> { + let annotations = parse_function_annotations( + self.interface_builder.program, + self.interface_builder, + annotation_rule, + &self.interface_builder.program.language, + self.strict, + )?; + if let Some(Annotation::Id { id, .. }) = annotations + .iter() + .find(|a| matches!(a, Annotation::Id { .. })) + { + self.id = Some(*id); + } + self.annotations = annotations; + Ok(self) + } + + /// Resolves pending identifiers + /// + /// # Returns + /// + /// * The result of the resolution + /// + /// # Errors + /// + /// * Symbol name is not a member of the function + pub(crate) fn resolve_pending_idents(&mut self) -> Result<(), PendingError> { + for pending in &self.pending_idents { + let member = self + .parameters + .iter() + .find(|m| m.name.clone().is_some_and(|n| n == pending.name)); + if let Some(m) = member { + if let Some(e) = (pending.check)(m.type_.clone().as_ref()) { + return Err(PendingError::new(pending.name.clone(), pending.position, e)); + } + } else { + return Err(PendingError::new( + pending.name.clone(), + pending.position, + format!( + "Symbol name {} is not a member of the function", + pending.name + ), + )); + } + } + Ok(()) + } + /// Adds a rename mapping to the program. + /// + /// This function inserts a new rename mapping into the `renames` HashMap of the program. + /// The `renames` HashMap is used to map from one name to another. + /// + /// # Arguments + /// + /// * `from` - A string slice that holds the old name. + /// * `annotations` - A vector of annotations. + pub(crate) fn check_name_annotation(&mut self, from: &str, annotations: &Vec) { + if let Some(Annotation::Name {name, ..}) = find_name_annotation(annotations) { + self.renames.insert(from.to_string(), name.clone()); + } + } + + /// Builds the function definition + /// + /// # Returns + /// + /// * The function definition + pub(crate) fn build(self) -> FunctionDefinition { + FunctionDefinition { + name: self.name.expect("Function name not set"), + id: self.id, + prototype: self.prototype, + renames: self.renames, + is_oneway: self.is_oneway, + return_type: self.return_type, + parameters: self.parameters, + annotations: self.annotations, + doxygen_preceding_comment: self.doxygen_preceding_comment, + doxygen_trailing_comment: self.doxygen_trailing_comment, + } + } +} diff --git a/erpcgen_rust/src/symbols/function_parameter.rs b/erpcgen_rust/src/symbols/function_parameter.rs new file mode 100644 index 00000000..dc878361 --- /dev/null +++ b/erpcgen_rust/src/symbols/function_parameter.rs @@ -0,0 +1,217 @@ +/* + * Copyright 2024 Marek Mišík(nxf76107) + * Copyright 2024 NXP + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +use std::collections::HashMap; +use std::rc::Rc; +use log::{debug, info}; +use pest::error::{Error, ErrorVariant}; +use pest::iterators::Pair; +use crate::parser::grammar_parser::Rule; +use crate::parser::util::annotations::{find_name_annotation, has_discriminator_annotation, parse_array_annotations, parse_enum_annotations, parse_list_annotations, parse_member_annotations, parse_struct_annotations, parse_union_annotations}; +use crate::parser::util::common::{extract_simple_data_type}; +use crate::parser::util::interface_definition::extract_scope_ident; +use crate::symbols::annotations::Annotation; +use crate::symbols::function_definition::FunctionDefinitionBuilder; +use crate::symbols::param_direction::ParamDirection; +use crate::symbols::pending_ident::PendingIdent; +use crate::symbols::types::Type; +use crate::symbols::util::{apply_rename_for_annotation, apply_rename_for_type}; + +/// A function parameter +#[derive(Debug, Clone)] +pub(crate) struct FunctionParameter { + pub(crate) name: Option, + pub(crate) type_: Rc, + pub(crate) direction: ParamDirection, + pub(crate) annotations: Vec, +} + +impl FunctionParameter { + pub fn apply_rename(&mut self, renames: &HashMap, program_renames: &HashMap) { + if let Some(name) = &self.name { + self.name = Some(renames.get(name).unwrap_or(name).to_string()); + } + self.annotations = self.annotations.iter().map(|a| apply_rename_for_annotation(a.clone(), renames, program_renames)).collect(); + self.type_ = apply_rename_for_type(self.type_.clone(), program_renames); + } +} + +/// A builder for a function parameter +pub(crate) struct FunctionParameterBuilder<'a> { + function_builder: &'a FunctionDefinitionBuilder<'a>, + strict: bool, + name: Option, + type_: Option>, + direction: ParamDirection, + annotations: Vec, + pending_idents: Vec +} + +/// Implementation of the builder +impl<'a> FunctionParameterBuilder<'a> { + pub(crate) fn new(function_builder: &'a FunctionDefinitionBuilder<'a>, strict: bool) -> Self { + Self { + function_builder, + strict, + name: None, + type_: None, + direction: ParamDirection::In, + annotations: vec![], + pending_idents: vec![] + } + } + + /// Set the name of the parameter + /// + /// # Arguments + /// + /// * `rule` - The rule to extract the name from + /// + /// # Returns + /// + /// * The builder + /// + /// # Errors + /// + /// * If the name is already in use + /// + pub(crate) fn with_name(mut self, rule: Option>) -> Result>> { + let Some(rule) = rule else { + return Ok(self) + }; + if self.name.is_some() { + return Ok(self) + } + let location = rule.as_span(); + let name = rule.as_str().to_string(); + debug!("With name: {}", name); + if !self.function_builder.check_parameter_name_is_free(&name) { + return Err(Box::new(Error::new_from_span( + ErrorVariant::CustomError { + message: format!("Symbol name {} is already in use", name.as_str()) + }, + location, + ))); + } + self.name = Some(name); + Ok(self) + } + + /// Set the type of the parameter + /// + /// # Arguments + /// + /// * `rule` - The rule to extract the type from + /// + /// # Returns + /// + /// * The builder + pub(crate) fn with_type(mut self, rule: Pair) -> Result>> { + let rules = rule.into_inner(); + let scope_ident = extract_scope_ident(&rules).map(|ident| ident.as_str().to_string()); + let simple_data_type = extract_simple_data_type(&rules).expect("Simple data type is required"); + let res = self.function_builder.interface_builder.resolve_scoped_type(simple_data_type, &scope_ident)?; + debug!("With type: {:?}", res); + self.type_ = Some(res); + Ok(self) + } + + /// Set the type of the parameter + /// + /// # Arguments + /// + /// * `annotations_rule` - The rule to extract the annotations from + /// + /// # Returns + /// + /// * The builder + /// + /// # Errors + /// + /// * Type must be defined + /// * Union member must have a discriminator annotation + pub(crate) fn with_annotations(mut self, annotations_rule: Pair) -> Result>> { + let cloned = annotations_rule.clone(); + self.annotations = match self.type_.clone().expect("Type must be defined").as_ref() { + Type::Struct { .. } => parse_struct_annotations(annotations_rule, &self.function_builder.interface_builder.program.language, self.strict)?, + Type::Enum { .. } => parse_enum_annotations(annotations_rule, &self.function_builder.interface_builder.program.language, self.strict)?, + Type::Union { .. } => { + let (ann, pend) = parse_union_annotations(annotations_rule,&self.function_builder.interface_builder.program.language , self.strict)?; + if !has_discriminator_annotation(&ann) { + return Err(Box::new(Error::new_from_pos( + ErrorVariant::CustomError { + message: "Union member must have a discriminator annotation".to_string(), + }, + cloned.as_span().start_pos(), + ))); + } + self.pending_idents.extend(pend); + ann + }, + Type::Array { .. } => parse_array_annotations(annotations_rule, &self.function_builder.interface_builder.program.language, self.strict)?, + Type::List { .. } => { + let (ann, pend) = parse_list_annotations(self.function_builder.interface_builder.program, annotations_rule, &self.function_builder.interface_builder.program.language, self.strict)?; + self.pending_idents.extend(pend); + ann + }, + Type::Binary => { + let (ann, pend) = parse_list_annotations(self.function_builder.interface_builder.program, annotations_rule, &self.function_builder.interface_builder.program.language, self.strict)?; + self.pending_idents.extend(pend); + ann + }, + _ => parse_member_annotations(annotations_rule.clone(), &self.function_builder.interface_builder.program.language, self.strict)? + }; + if self.type_.clone().is_some_and(|t| t.is_union()) && !has_discriminator_annotation(&self.annotations) { + return Err(Box::new(Error::new_from_pos( + ErrorVariant::CustomError { + message: "Union member must have a discriminator annotation".to_string(), + }, + cloned.as_span().start_pos(), + ))); + } + if let Some(Annotation::Name {name, ..}) = find_name_annotation(&self.annotations) { + self.name = Some(name.clone()); // Consider adding a check for name conflict + } + Ok(self) + } + + /// Set the direction of the parameter + /// + /// # Arguments + /// + /// * `direction` - The rule to extract the direction from + /// + /// # Returns + /// + /// * The builder + pub(crate) fn with_direction(mut self, direction: Pair) -> Self { + let direction = match direction.as_rule() { + Rule::paramDirectionIn => ParamDirection::In, + Rule::paramDirectionOut => ParamDirection::Out, + Rule::paramDirectionInout => ParamDirection::InOut, + _ => unreachable!() + }; + debug!("With direction: {:?}", direction); + self.direction = direction; + self + } + + /// Build the function parameter + /// + /// # Returns + /// + /// * The function parameter + pub(crate) fn build(self) -> (FunctionParameter, Vec) { + info!("Building function parameter {:?}", self.name); + (FunctionParameter { + name: self.name, + type_: self.type_.expect("Type is required"), + direction: self.direction, + annotations: self.annotations + }, self.pending_idents) + } +} \ No newline at end of file diff --git a/erpcgen_rust/src/symbols/interface_definition.rs b/erpcgen_rust/src/symbols/interface_definition.rs new file mode 100644 index 00000000..9c7e142f --- /dev/null +++ b/erpcgen_rust/src/symbols/interface_definition.rs @@ -0,0 +1,313 @@ +/* + * Copyright 2024 Marek Mišík(nxf76107) + * Copyright 2024 NXP + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +use std::collections::{HashMap, HashSet}; +use std::rc::Rc; +use pest::error::Error; +use pest::iterators::Pair; +use crate::parser::grammar_parser::Rule; +use crate::parser::util::annotations::{find_name_annotation, parse_interface_annotations}; +use crate::symbols::annotations::Annotation; +use crate::symbols::doxygen_comment::DoxygenComment; +use crate::symbols::function_definition::{FunctionDefinition, FunctionDefinitionBuilder}; +use crate::symbols::program::Program; +use crate::symbols::types::Type; + +/// Interface definition +#[derive(Debug, Clone)] +pub(crate) struct InterfaceDefinition { + pub(crate) name: String, + pub(crate) id: Option, + pub(crate) annotations: Vec, + pub(crate) renames: HashMap, + pub(crate) callback_definitions: Vec, + pub(crate) functions: Vec, + pub(crate) doxygen_preceding_comment: Vec, + pub(crate) doxygen_trailing_comment: Vec, +} + +impl InterfaceDefinition { + pub fn apply_rename(&mut self, renames: &HashMap) { + self.name = renames.get(&self.name).unwrap_or(&self.name).to_string(); + self.callback_definitions.iter_mut().for_each(|f| f.apply_rename(&self.renames, renames)); + self.functions.iter_mut().for_each(|f| f.apply_rename(&self.renames, renames)); + } +} + +/// Implementation of InterfaceDefinition +pub(crate) struct InterfaceDefinitionBuilder<'a> { + pub(super) program: &'a Program, + strict: bool, + pub(crate) name: Option, + pub(crate) id: Option, + pub(crate) function_ids: HashSet, + annotations: Vec, + renames: HashMap, + callback_definitions: Vec, + functions: Vec, + doxygen_preceding_comment: Vec, + doxygen_trailing_comment: Vec, +} + +/// Implementation of InterfaceDefinitionBuilder +impl<'a> InterfaceDefinitionBuilder<'a> { + pub fn new(program: &'a Program, strict: bool) -> Self { + Self { + program, + strict, + name: None, + id: None, + function_ids: HashSet::new(), + renames: HashMap::new(), + annotations: vec![], + callback_definitions: vec![], + functions: vec![], + doxygen_preceding_comment: vec![], + doxygen_trailing_comment: vec![], + } + } + + /// Create a new InterfaceDefinitionBuilder + /// + /// # Arguments + /// + /// * `comment` - A vector of DoxygenComment + /// + /// # Returns + /// + /// * The builder + pub(crate) fn with_doxygen_preceding_comment(mut self, comment: Vec) -> Self { + self.doxygen_preceding_comment = comment; + self + } + + + /// Create a new InterfaceDefinitionBuilder + /// + /// # Arguments + /// + /// * `comment` - A vector of DoxygenComment + /// + /// # Returns + /// + /// * The builder + pub(crate) fn with_doxygen_trailing_comment(mut self, comment: Vec) -> Self { + self.doxygen_trailing_comment = comment; + self + } + + /// Create a new InterfaceDefinitionBuilder + /// + /// # Arguments + /// + /// * `rule` - A Pair + /// * `scope` - An Option + /// + /// # Returns + /// + /// * The builder + pub(crate) fn resolve_scoped_type(&self, rule: Pair, scope: &Option) -> Result, Box>> { + let is_ident = rule.clone().into_inner().next().is_some_and(|r| r.as_rule() == Rule::ident); + let name = rule.as_str().to_string(); + if is_ident && scope.as_ref().is_none() { + let resolved = self.callback_definitions.iter().find(|f| f.name == name); + if let Some(f) = resolved { + return Ok(Rc::new(Type::Callback{ name: f.name.clone(), scope: self.name.clone().expect("Interface name must be set") })); + } + } + if let Some(s) = scope { + return self.program.resolve_scoped_type(name.as_str(), s.as_str()) + .ok_or(Box::new(Error::new_from_span( + pest::error::ErrorVariant::CustomError { + message: format!("Type {} not found", name).to_string(), + }, + rule.as_span(), + ))) + } + self.program.resolve_simple_data_type(rule) + + } + + /// Create a new InterfaceDefinitionBuilder + /// + /// # Arguments + /// + /// * `rule` - A Pair + /// + /// # Returns + /// + /// * The builder + pub(crate) fn with_name(mut self, rule: Pair) -> Result>> { + let location = rule.as_span(); + let name = rule.as_str().to_string(); + // Either the name is not in use or it is used in a forward declaration + if !self.program.check_symbol_name_is_free(&name) { + return Err(Box::new(Error::new_from_span( + pest::error::ErrorVariant::CustomError { + message: format!("Symbol name {} is already in use", name).to_string(), + }, + location, + ))); + } + self.name = Some(name); + Ok(self) + } + + /// Create a new InterfaceDefinitionBuilder + /// + /// # Arguments + /// + /// * `name` - A &str + /// + /// # Returns + /// + /// * The builder + pub(crate) fn check_function_name_is_free(&self, name: &str) -> bool { + self.callback_definitions.iter().all(|f| f.name != name) + && self.functions.iter().all(|f| f.name != name + ) && self.program.check_symbol_name_is_free(name) // Symbol name may not be necessary + } + + /// Create a new InterfaceDefinitionBuilder + /// + /// # Arguments + /// + /// * `id` - A usize + /// + /// # Returns + /// + /// * The builder + pub(crate) fn check_function_id_is_free(&self, id: usize) -> bool { + !self.function_ids.contains(&id) + } + + /// Create a new InterfaceDefinitionBuilder + /// + /// # Arguments + /// + /// * `callback_definition` - A FunctionDefinition + /// + /// # Returns + /// + /// * The builder + pub(crate) fn add_callback_definition(&mut self, callback_definition: FunctionDefinition) { + self.check_name_annotation(&callback_definition.name, &callback_definition.annotations); + self.callback_definitions.push(callback_definition); + } + + /// Create a new InterfaceDefinitionBuilder + /// + /// # Arguments + /// + /// * `function` - A FunctionDefinition + /// + /// # Returns + /// + /// * The builder + pub(crate) fn add_function(&mut self, function: FunctionDefinition) { + if let Some(id) = function.id { + self.function_ids.insert(id); + } + self.check_name_annotation(&function.name, &function.annotations); + self.functions.push(function); + } + + + /// Create a new InterfaceDefinitionBuilder + /// + /// # Arguments + /// + /// * `rule` - A Pair + /// + /// # Returns + /// + /// * The builder + pub(crate) fn with_annotations(mut self, rule: Pair) -> Result>> { + self.annotations = parse_interface_annotations(self.program, rule, &self.program.language, self.strict)?; + if let Some(Annotation::Id {id, .. }) = self.annotations.iter().find(|a| matches!(a, Annotation::Id {..})) { + self.id = Some(*id); + } + Ok(self) + } + + /// Create a new InterfaceDefinitionBuilder + /// + /// # Arguments + /// + /// * `name` - A Rc + /// + /// # Returns + /// + /// * The builder + pub(crate) fn get_callback_definition(&self, name: Rc) -> Option { + let Type::Callback { name, .. } = name.as_ref() else { + return None; + }; + self.callback_definitions.iter().find(|f| &f.name == name).cloned() + } + + /// Create a new InterfaceDefinitionBuilder + /// + /// # Returns + /// + /// * The builder + pub(crate) fn new_function_definition_builder(&self) -> FunctionDefinitionBuilder { + FunctionDefinitionBuilder::new(self, self.strict) + } + + /// Create a new InterfaceDefinitionBuilder + /// + /// # Returns + /// + /// * The builder + pub(crate) fn assign_function_ids(&mut self) { + let mut id = 1; + for function in self.functions.iter_mut() { + if function.id.is_none() { + while self.function_ids.contains(&id) { + id += 1; + } + function.id = Some(id); + id += 1; + } + } + } + + /// Adds a rename mapping to the program. + /// + /// This function inserts a new rename mapping into the `renames` HashMap of the program. + /// The `renames` HashMap is used to map from one name to another. + /// + /// # Arguments + /// + /// * `from` - A string slice that holds the old name. + /// * `annotations` - A vector of annotations. + pub(crate) fn check_name_annotation(&mut self, from: &str, annotations: &Vec) { + if let Some(Annotation::Name {name, ..}) = find_name_annotation(annotations) { + self.renames.insert(from.to_string(), name.clone()); + } + } + + /// Create a new InterfaceDefinitionBuilder + /// + /// # Returns + /// + /// * The InterfaceDefinition + pub(crate) fn build(mut self) -> InterfaceDefinition { + self.assign_function_ids(); + InterfaceDefinition { + name: self.name.expect("Interface name not set"), + id: self.id, + annotations: self.annotations, + renames: self.renames, + callback_definitions: self.callback_definitions, + functions: self.functions, + doxygen_preceding_comment: self.doxygen_preceding_comment, + doxygen_trailing_comment: self.doxygen_trailing_comment, + } + } +} diff --git a/erpcgen_rust/src/symbols/language.rs b/erpcgen_rust/src/symbols/language.rs new file mode 100644 index 00000000..344dd8c3 --- /dev/null +++ b/erpcgen_rust/src/symbols/language.rs @@ -0,0 +1,15 @@ +/* + * Copyright 2024 Marek Mišík(nxf76107) + * Copyright 2024 NXP + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +/// Language enum +#[derive(Debug, Clone, PartialEq, Hash)] +pub enum Language { + C, + Python, + Rust, + Java +} \ No newline at end of file diff --git a/erpcgen_rust/src/symbols/member_options.rs b/erpcgen_rust/src/symbols/member_options.rs new file mode 100644 index 00000000..caaf2e77 --- /dev/null +++ b/erpcgen_rust/src/symbols/member_options.rs @@ -0,0 +1,13 @@ +/* + * Copyright 2024 Marek Mišík(nxf76107) + * Copyright 2024 NXP + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +/// Member options +#[derive(Debug, Clone, Copy, PartialEq)] +pub(crate) enum MemberOptions{ + Byref, + Optional, +} \ No newline at end of file diff --git a/erpcgen_rust/src/symbols/mod.rs b/erpcgen_rust/src/symbols/mod.rs new file mode 100644 index 00000000..0c284dd2 --- /dev/null +++ b/erpcgen_rust/src/symbols/mod.rs @@ -0,0 +1,30 @@ +/* + * Copyright 2024 Marek Mišík(nxf76107) + * Copyright 2024 NXP + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +pub(crate) mod annotations; +pub(crate) mod language; +pub(crate) mod value; +pub(crate) mod program; +pub(crate) mod enum_definition; +pub(crate) mod union_definition; +pub(crate) mod interface_definition; +pub(crate) mod const_definition; +pub(crate) mod type_definition; +pub(crate) mod types; +pub(crate) mod struct_definition; +pub(crate) mod struct_data_member; +pub(crate) mod member_options; +pub(crate) mod enum_member; +pub(crate) mod struct_member; +pub(crate) mod union_case; +pub(crate) mod union_case_member; +pub(crate) mod function_definition; +pub(crate) mod param_direction; +pub(crate) mod function_parameter; +pub(crate) mod doxygen_comment; +pub(crate) mod pending_ident; +mod util; diff --git a/erpcgen_rust/src/symbols/param_direction.rs b/erpcgen_rust/src/symbols/param_direction.rs new file mode 100644 index 00000000..f5950805 --- /dev/null +++ b/erpcgen_rust/src/symbols/param_direction.rs @@ -0,0 +1,27 @@ +/* + * Copyright 2024 Marek Mišík(nxf76107) + * Copyright 2024 NXP + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +use std::fmt::Display; + +/// Parameter direction +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub(crate) enum ParamDirection { + In, + Out, + InOut, +} + +/// Display implementation for ParamDirection +impl Display for ParamDirection { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + ParamDirection::In => write!(f, "in"), + ParamDirection::Out => write!(f, "out"), + ParamDirection::InOut => write!(f, "inout"), + } + } +} \ No newline at end of file diff --git a/erpcgen_rust/src/symbols/pending_ident.rs b/erpcgen_rust/src/symbols/pending_ident.rs new file mode 100644 index 00000000..e3b96b2e --- /dev/null +++ b/erpcgen_rust/src/symbols/pending_ident.rs @@ -0,0 +1,65 @@ +/* + * Copyright 2024 Marek Mišík(nxf76107) + * Copyright 2024 NXP + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +use crate::symbols::types::Type; + +/// Pending identifier +pub(crate) struct PendingIdent { + pub(crate) name: String, + pub(crate) position: usize, + pub(crate) check: Box Option> // If error, return why, else return None +} + +/// Implementation of PendingIdent +impl PendingIdent { + pub(crate) fn new(name: String, position: usize, check: Box Option>) -> Self { + Self { + name, + position, + check + } + } +} + +/// Check if the type is a scalar +pub const CHECK_SCALAR: fn(&Type) -> Option = |arg| check_scalar(arg); + +/// Check if the type is a scalar +/// +/// # Arguments +/// +/// * `t` - The type to check +/// +/// # Returns +/// +/// * None if the type is a scalar, else a string with the error +fn check_scalar(t: &Type) -> Option { + if t.is_scalar() { + None + } else { + Some(format!("Type {:?} is not a scalar type", t)) + } +} + +/// Pending error +pub(crate) struct PendingError { + pub(crate) name: String, + pub(crate) position: usize, + pub(crate) error: String +} + +/// Implementation of PendingError +impl PendingError { + pub(crate) fn new(name: String, position: usize, error: String) -> Self { + Self { + name, + position, + error + } + } +} + diff --git a/erpcgen_rust/src/symbols/program.rs b/erpcgen_rust/src/symbols/program.rs new file mode 100644 index 00000000..2344461c --- /dev/null +++ b/erpcgen_rust/src/symbols/program.rs @@ -0,0 +1,1013 @@ +/* + * Copyright 2024 Marek Mišík(nxf76107) + * Copyright 2024 NXP + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +use std::collections::{HashMap, HashSet}; +use std::ops::Deref; +use std::rc::{Rc}; +use log::{error, info}; +use pest::error::{Error, ErrorVariant}; +use pest::iterators::Pair; +use crate::parser::grammar_parser::Rule; +use crate::parser::util::annotations::{find_name_annotation, parse_program_annotations}; +use crate::parser::util::common::{extract_ident_type_tag}; +use crate::parser::util::expression::evaluate_expression; +use crate::symbols::annotations::Annotation; +use crate::symbols::const_definition::ConstDefinition; +use crate::symbols::doxygen_comment::DoxygenComment; +use crate::symbols::enum_definition::EnumDefinition; +use crate::symbols::function_definition::FunctionDefinition; +use crate::symbols::interface_definition::InterfaceDefinition; +use crate::symbols::language::Language; +use crate::symbols::struct_definition::StructDefinition; +use crate::symbols::struct_member::StructMember; +use crate::symbols::type_definition::TypeDefinition; +use crate::symbols::types::{Type}; +use crate::symbols::union_definition::UnionDefinition; +use crate::symbols::value::Value; + +/// Program +/// +/// Represents a program. +pub(crate) struct Program { + pub(crate) name: Option, + pub(crate) language: Language, + pub(crate) symbol_names: HashSet, + pub(crate) value_names: HashSet, + pub(crate) renames: HashMap, + pub(crate) types: Vec>, + pub(crate) interface_ids: HashSet, + pub(crate) annotations: Vec, + pub(crate) doxygen_preceding_comment: Vec, + pub(crate) doxygen_trailing_comment: Vec, + pub(crate) const_definitions: Vec, + pub(crate) enum_definitions: Vec, + pub(crate) type_definitions: Vec, + pub(crate) struct_definitions: Vec, + pub(crate) union_definitions: Vec, + pub(crate) interface_definitions: Vec +} + +/// Implementation of Program +impl Program { + pub(crate) fn new(language: Language) -> Self { + Self { + name: None, + language, + symbol_names: HashSet::new(), + value_names: HashSet::new(), + renames: HashMap::new(), + types: vec![ + Rc::new(Type::Bool), + Rc::new(Type::Int8), + Rc::new(Type::Int16), + Rc::new(Type::Int32), + Rc::new(Type::Int64), + Rc::new(Type::UInt8), + Rc::new(Type::UInt16), + Rc::new(Type::UInt32), + Rc::new(Type::UInt64), + Rc::new(Type::Float), + Rc::new(Type::Double), + Rc::new(Type::String), + Rc::new(Type::Binary) + ], + interface_ids: HashSet::new(), + annotations: vec![], + doxygen_preceding_comment: vec![], + doxygen_trailing_comment: vec![], + const_definitions: vec![], + enum_definitions: vec![], + type_definitions: vec![], + struct_definitions: vec![], + union_definitions: vec![], + interface_definitions: vec![] + } + } + + /// Checks if a symbol name is free (not already used) in the program. + /// + /// # Arguments + /// + /// * `name` - A string slice that holds the name of the symbol. + /// + /// # Returns + /// + /// * `bool` - Returns `true` if the symbol name is free, `false` otherwise. + pub(crate) fn check_symbol_name_is_free(&self, name: &str) -> bool { + !self.symbol_names.contains(name) + } + + /// Adds a symbol name to the program. + /// + /// # Arguments + /// + /// * `name` - A string slice that holds the name of the symbol. + pub(crate) fn add_symbol_name(&mut self, name: &str) { + self.symbol_names.insert(name.to_string()); + } + + /// Checks if a value name is free (not already used) in the program. + /// + /// # Arguments + /// + /// * `name` - A string slice that holds the name of the value. + /// + /// # Returns + /// + /// * `bool` - Returns `true` if the value name is free, `false` otherwise. + pub(crate) fn check_value_name_is_free(&self, name: &str) -> bool { + !self.value_names.contains(name) + } + + + /// Adds a value name to the program. + /// + /// # Arguments + /// + /// * `name` - A string slice that holds the name of the value. + pub(crate) fn add_value_name(&mut self, name: &str) { + self.value_names.insert(name.to_string()); + } + + /// Resolves a simple data type. + /// + /// # Arguments + /// + /// * `simple_data_type_rule` - A pair of rule. + /// + /// # Returns + /// + /// * `Result, Box>>` - Returns a result with a reference counted type or an error. + /// + /// # Errors + /// + /// * Expected simple data type + /// * Type is not defined + /// + /// # Panics + /// + /// * Unexpected rule + pub(crate) fn resolve_simple_data_type(&self, simple_data_type_rule: Pair) -> Result, Box>> { + let inner_type = simple_data_type_rule.into_inner().next().expect("Expected simple data type"); + + match inner_type.as_rule() { + Rule::arrayType => self.resolve_array_type(inner_type), + Rule::listType => self.resolve_list_type(inner_type), + Rule::ident => self + .resolve_type_from_ident(inner_type.as_str()) + .ok_or_else(|| { + Box::new(Error::new_from_span( + ErrorVariant::CustomError { + message: format!("Type {} is not defined", inner_type.as_str()) + .to_string(), + }, + inner_type.as_span(), + )) + }), + _ => { + error!("Unexpected rule: {:?}", inner_type.as_rule()); + unreachable!("Unexpected rule") + } + } + } + + /// Resolves a type from an identifier. + /// + /// # Arguments + /// + /// * `list_type_rule` - A pair of rule. + /// + /// # Returns + /// + /// * `Result, Box>>` - Returns a result with a reference counted type or an error. + /// + /// # Errors + /// + /// * Primitive type not found + /// + /// # Panics + /// + /// * Expected ident + pub(crate) fn resolve_list_type(&self, list_type_rule: Pair) -> Result, Box>> { + let rules = list_type_rule.into_inner(); + let type_ident = extract_ident_type_tag(&rules).expect("Expected ident"); + let element_type = self.resolve_type_from_ident(&type_ident.as_str()).ok_or_else(|| Box::new(Error::new_from_span( + ErrorVariant::CustomError { + message: format!("Primitive type {} not found", type_ident.as_str()).to_string(), + }, + type_ident.as_span() + )))?; + let full_type = Rc::new(Type::List { element_type }); // TODO investigate adding a new list type to program + Ok(full_type) + } +/// Resolves a type from an identifier. + /// + /// # Arguments + /// + /// * `array_type_rule` - A pair of rule. + /// + /// # Returns + /// + /// * `Result, Box>>` - Returns a result with a reference counted type or an error. + /// + /// # Errors + /// + /// * Expected type ident + /// * Expected UInt32 value + pub(crate) fn resolve_array_type( + &self, + array_type_rule: Pair, + ) -> Result, Box>> { + let rules = array_type_rule.into_inner(); + let array_type = self.resolve_type_from_ident(extract_ident_type_tag(&rules).expect("Expected type ident").as_str()); + let mut dims: Vec = vec![]; + let dim_rules = rules.find_tagged("exp"); + for dim_rule in dim_rules { + let value = self.resolve_value_from_expression(dim_rule, &Type::UInt32, true)?; + match value { + Value::Uint32(v) => dims.push(v as usize), + _ => unreachable!("Expected UInt32 value") + } + } + Ok(Rc::new(Type::Array { element_type: array_type.unwrap(), dimension_sizes: dims })) + } +/// Resolves a type from an identifier. + /// + /// # Arguments + /// + /// * `ident` - A string slice that holds the identifier. + /// + /// # Returns + /// + /// * `Option>` - Returns an option with a reference counted type. + pub(crate) fn resolve_type_from_ident(&self, ident: &str) -> Option> { + if let Some(t) = Type::resolve_primitive_type(ident) { + return self.types.iter().find(|ty| ty.as_ref() == &t).cloned(); + } + self.resolve_complex_type(ident) + } +/// Resolves a complex type. + /// + /// # Arguments + /// + /// * `type_mame` - A string slice that holds the type name. + /// + /// # Returns + /// + /// * `Option>` - Returns an option with a reference counted type. + fn resolve_complex_type(&self, type_mame: &str) -> Option> { + self.types.iter().find(|ty| { + match ty.as_ref() { + Type::Struct { name } => type_mame == name, + Type::Union { name } => type_mame == name, + Type::Enum { name } => type_mame == name, + Type::TypeDef { name, .. } => type_mame == name, + _ => false + } + }).cloned() + } +/// Resolves a value from an expression. + /// + /// # Arguments + /// + /// * `expression` - A pair of rule. + /// * `value_type` - A reference counted type. + /// * `strict` - A boolean that indicates if the resolution is strict. + /// + /// # Returns + /// + /// * `Result>>` - Returns a result with a value or an error. + pub(crate) fn resolve_value_or_reference_from_expression( + &self, + expression: Pair, + value_type: &Type, + strict: bool, + ) -> Result>> { + evaluate_expression( + expression, + value_type, + &self.const_definitions, + &self.enum_definitions, + true, + strict, + ) + } +/// Resolves a value from an expression. + /// + /// # Arguments + /// + /// * `expression` - A pair of rule. + /// * `value_type` - A reference counted type. + /// * `strict` - A boolean that indicates if the resolution is strict. + /// + /// # Returns + /// + /// * `Result>>` - Returns a result with a value or an error. + pub(crate) fn resolve_value_from_expression( + &self, + expression: Pair, + value_type: &Type, + strict: bool, + ) -> Result>> { + evaluate_expression( + expression, + value_type, + &self.const_definitions, + &self.enum_definitions, + false, + strict, + ) + } + + /// Resolves a type from an identifier. + /// + /// Extension point: add a possibility for other types to be scoped. + /// + /// # Arguments + /// + /// * `type_name` - A string slice that holds the type name. + /// * `scope` - A string slice that holds the scope. + /// + /// # Returns + /// + /// * `Option>` - Returns an option with a reference counted type. + /// + pub(crate) fn resolve_scoped_type(&self, type_name: &str, scope: &str) -> Option> { + self.interface_definitions.iter().find(|i| i.name == scope).and_then(|i| { + i.callback_definitions.iter().find(|c| c.name == type_name).map(|c| Rc::new(Type::Callback { name: c.name.clone(), scope: scope.to_string() })) + }) + //EXTENSION POINT: add a possibility for other types to be scoped + } +/// Resolves a function definition. + /// + /// # Arguments + /// + /// * `func_type` - Function type. + /// + /// # Returns + /// + /// * `Option` - Returns an option with a function definition. + pub(crate) fn resolve_function_definition( + &self, + func_type: Rc, + ) -> Option { + let Type::Callback { scope, name } = func_type.deref() else { + return None; + }; + self.interface_definitions + .iter() + .find(|i| &i.name == scope) + .and_then(|i| { + i.callback_definitions + .iter() + .find(|c| &c.name == name) + .cloned() + }) + } +/// Add a enum definition. + /// + /// # Arguments + /// + /// * `definition` - Enum definition. + pub(crate) fn add_enum_definition(&mut self, definition: EnumDefinition) { + if let Some(name) = definition.name.clone() { + self.add_symbol_name(&name); + self.types.push(Rc::new(Type::Enum { name })); + } + + definition.members.iter().for_each(|m| { + self.check_name_annotation(&m.name, &m.annotations); + self.add_value_name(&m.name) + }); + self.enum_definitions.push(definition); + } + + /// Add a const definition. + /// + /// # Arguments + /// + /// * `definition` - Const definition. + pub(crate) fn add_const_definition(&mut self, definition: ConstDefinition) { + self.add_symbol_name(&definition.name); + self.check_name_annotation(&definition.name, &definition.annotations); + self.const_definitions.push(definition); + } +/// Add a union definition. + /// + /// # Arguments + /// + /// * `definition` - Union definition. + pub(crate) fn add_union_definition(&mut self, definition: UnionDefinition) { + self.add_symbol_name(&definition.name); + self.types.push(Rc::new(Type::Union { name: definition.name.clone() })); + self.check_name_annotation(&definition.name, &definition.annotations); + self.union_definitions.push(definition); + } +/// Add a struct definition. + /// + /// # Arguments + /// + /// * `definition` - Struct definition. + /// + /// # Returns + /// + /// * `Result<(), Box>>` - Returns a result with a unit or an error. + /// + /// # Panics + /// + /// * Struct name is already in use + pub(crate) fn add_union_forward_definition( + &mut self, + union_definition_ident: Pair, + ) -> Result<(), Box>> { + let name = union_definition_ident.as_str().to_string(); + if !self.check_symbol_name_is_free(&name) { + return Err(Box::new(Error::new_from_span( + ErrorVariant::CustomError { + message: format!("Symbol name {} is already in use", name).to_string(), + }, + union_definition_ident.as_span(), + ))); + } + let forward_definition = Rc::new(Type::Union { name }); + self.types.push(forward_definition); + Ok(()) + } + + /// Add a struct definition. + /// + /// # Arguments + /// + /// * `definition` - Struct definition. + pub(crate) fn add_struct_definition(&mut self, definition: StructDefinition) { + self.add_symbol_name(&definition.name); + self.types.push(Rc::new(Type::Struct { name: definition.name.clone() })); + self.check_name_annotation(&definition.name, &definition.annotations); + self.struct_definitions.push(definition); + } +/// Add a struct forward definition. + /// + /// # Arguments + /// + /// * `struct_definition_ident` - A Pair + /// + /// # Returns + /// + /// * `Result<(), Box>>` - Returns a result with a unit or an error. + /// + /// # Errors + /// + /// * Struct name is already in use + pub(crate) fn add_struct_forward_definition( + &mut self, + struct_definition_ident: Pair, + ) -> Result<(), Box>> { + let name = struct_definition_ident.as_str().to_string(); + if !self.check_symbol_name_is_free(&name) { + return Err(Box::new(Error::new_from_span( + ErrorVariant::CustomError { + message: format!("Symbol name {} is already in use", name).to_string(), + }, + struct_definition_ident.as_span(), + ))); + } + let forward_definition = Rc::new(Type::Struct { name }); + self.types.push(forward_definition); + Ok(()) + } +/// Add a type definition. + /// + /// # Arguments + /// + /// * `definition` - A type definition + pub(crate) fn add_type_definition(&mut self, definition: TypeDefinition) { + self.add_symbol_name(&definition.name); + self.types.push(Rc::new(Type::TypeDef { name: definition.name.clone(), referenced_type: definition.referenced_type.clone() })); + info!("Adding type definition: {}", definition.name); + self.check_name_annotation(&definition.name, &definition.annotations); + self.type_definitions.push(definition); + } + + /// Add an interface definition. + /// + /// # Arguments + /// + /// * `definition` - An interface definition + pub(crate) fn add_interface_definition(&mut self, definition: InterfaceDefinition) { + self.add_symbol_name(&definition.name); + for function in &definition.callback_definitions { + self.add_symbol_name(&function.name); // This may not be necessary + self.types.push(Rc::new(Type::Callback { + name: function.name.clone(), + scope: definition.name.clone(), + })); + } + if let Some(id) = definition.id { + self.interface_ids.insert(id); // Register used Id + } + self.check_name_annotation(&definition.name, &definition.annotations); + self.interface_definitions.push(definition); + } + + /// Get a struct definition. + /// + /// # Arguments + /// + /// * `name` - A string slice that holds the name of the struct + /// + /// # Returns + /// + /// * `Option<&StructDefinition>` - Returns an option with a reference to the struct definition + pub(crate) fn get_struct_definition(&self, name: &str) -> Option<&StructDefinition> { + self.struct_definitions.iter().find(|s| s.name == name) + } + + /// Get a union definition. + /// + /// # Arguments + /// + /// * `name` - A string slice that holds the name of the union + /// + /// # Returns + /// + /// * `Option<&UnionDefinition>` - Returns an option with a reference to the union definition + pub(crate) fn get_union_definition(&self, name: &str) -> Option<&UnionDefinition> { + self.union_definitions.iter().find(|u| u.name == name) + } + + pub(crate) fn get_enum_definition(&self, name: &str) -> Option<&EnumDefinition> { + self.enum_definitions.iter().find(|e| e.name == Some(name.to_string())) + } + + pub(crate) fn get_type_definition(&self, name: &str) -> Option<&TypeDefinition> { + self.type_definitions.iter().find(|t| t.name == name) + } +/// Get a function definition. + /// + /// # Arguments + /// + /// * `name` - A string slice that holds the name of the function + /// * `scope` - A string slice that holds the scope of the function + /// + /// # Returns + /// + /// * `Option<&FunctionDefinition>` - Returns an option with a reference to the function definition + pub(crate) fn get_function_definition( + &self, + name: &str, + scope: &str, + ) -> Option<&FunctionDefinition> { + self.interface_definitions + .iter() + .find(|i| i.name == scope) + .and_then(|i| i.callback_definitions.iter().find(|f| f.name == name)) + } + + /// Get a callback type definition. + /// + /// # Arguments + /// + /// * `type_` - Callback type + /// + /// # Returns + /// + /// * `Option<&TypeDefinition>` - Returns an option with a reference to the type definition + pub(crate) fn get_callback_type_definition( + &self, + type_: Rc, + ) -> Option<&FunctionDefinition> { + match type_.deref() { + Type::Callback { name, scope } => { + self.get_function_definition(name, scope) + }, + _ => None + } + } + + /// Get interface types + /// + /// # Arguments + /// + /// * `interface` - An interface definition + /// + /// # Returns + /// + /// * `HashSet>` - Returns a hash set with reference counted types + pub(crate) fn get_interface_types(&self, interface: &InterfaceDefinition) -> HashSet> { + let mut dependent_types: HashSet> = HashSet::new(); + interface.callback_definitions.iter().for_each(|c| { + dependent_types.extend(self.get_function_dependent_types(c)); + }); + interface.functions.iter().for_each(|f|{ + dependent_types.extend(self.get_function_dependent_types(f)); + }); + dependent_types + } + + /// Get function dependent types + /// + /// # Arguments + /// + /// * `function` - A function definition + /// + /// # Returns + /// + /// * `HashSet>` - Returns a hash set with reference counted types + pub(crate) fn get_function_dependent_types( + &self, + function: &FunctionDefinition, + ) -> HashSet> { + let mut dependent_types: HashSet> = HashSet::new(); + function.parameters.iter().for_each(|p| { + let pt = p.type_.clone(); + if !pt.is_primitive() || !dependent_types.contains(&pt) { + dependent_types.insert(pt.clone()); + let dt = self.get_dependent_types(pt); + dependent_types.extend(dt); + } + }); + if let Some(rt) = function.return_type.clone() { + if !rt.is_primitive() || !dependent_types.contains(&rt) { + dependent_types.insert(rt.clone()); + let dt = self.get_dependent_types(rt); + dependent_types.extend(dt); + } + }; + dependent_types + } + + /// Get dependent types + /// + /// # Arguments + /// + /// * `type_` - A type + /// + /// # Returns + /// + /// * `HashSet>` - Returns a hash set with reference counted types + /// + /// # Panics + /// + /// * Struct definition must exist for a defined type + /// * Union definition must exist for a defined type + /// * Type definition must exist for a defined type + /// * Interface definition must exist for a defined type + /// * Function definition must exist for a defined type + pub(crate) fn get_dependent_types(&self, type_: Rc) -> HashSet> { + let mut dependent_types: HashSet> = HashSet::new(); + if !type_.is_primitive() && !type_.is_array() { + dependent_types.insert(type_.clone()); + } + match type_.deref() { + Type::Struct { name } => { + let sd = self + .get_struct_definition(name) + .expect("Struct definition must exist for a defined type"); + sd.members.iter().for_each(|m| match m { + StructMember::DataMember(dm) => { + let mt = dm.member_type.clone(); + if !mt.is_primitive() || !dependent_types.contains(&mt) { + dependent_types.insert(mt.clone()); + let dt = self.get_dependent_types(mt); + dependent_types.extend(dt); + } + } + StructMember::UnionDefinition { definition: ud, .. } => { + ud.cases.iter().for_each(|c| { + c.members.iter().for_each(|m| { + let mt = m.member_type.clone(); + if !mt.is_primitive() || !dependent_types.contains(&mt) { + dependent_types.insert(mt.clone()); + let dt = self.get_dependent_types(mt); + dependent_types.extend(dt); + } + }) + }) + } + }); + } + Type::Union { name } => { + let ud = self + .get_union_definition(name) + .expect("Union definition must exist for a defined type"); + ud.cases.iter().for_each(|c| { + c.members.iter().for_each(|m| { + let mt = m.member_type.clone(); + if !mt.is_primitive() || !dependent_types.contains(&mt) { + dependent_types.insert(mt.clone()); + let dt = self.get_dependent_types(mt); + dependent_types.extend(dt); + } + }) + }) + } + Type::TypeDef { name, .. } => { + let td = self + .get_type_definition(name) + .expect("Type definition must exist for a defined type"); + let mt = td.referenced_type.clone(); + if !mt.is_primitive() || !dependent_types.contains(&mt) { + dependent_types.insert(mt.clone()); + let dt = self.get_dependent_types(mt); + dependent_types.extend(dt); + } + } + Type::List { element_type } => { + let et = element_type.clone(); + if !et.is_primitive() || !dependent_types.contains(&et) { + dependent_types.insert(et.clone()); + let dt = self.get_dependent_types(et); + dependent_types.extend(dt); + } + } + Type::Array { element_type, .. } => { + let et = element_type.clone(); + if !et.is_primitive() || !dependent_types.contains(&et) { + dependent_types.insert(et.clone()); + let dt = self.get_dependent_types(et); + dependent_types.extend(dt); + } + } + Type::Callback { name, scope } => { + let interface = self + .interface_definitions + .iter() + .find(|i| i.name == scope.as_ref()) + .expect("Interface definition must exist for a defined type"); + let function = interface + .callback_definitions + .iter() + .find(|f| f.name == name.as_ref()) + .expect("Function definition must exist for a defined type"); + function.parameters.iter().for_each(|p| { + let pt = p.type_.clone(); + if !pt.is_primitive() || !dependent_types.contains(&pt) { + let dt = self.get_dependent_types(pt); + dependent_types.extend(dt); + } + }); + } + _ => {} + }; + dependent_types + } +/// Get used structs from interface + /// + /// # Arguments + /// + /// * `dependent_types` - A hash set with reference counted types + /// + /// # Returns + /// + /// * `Vec<&StructDefinition>` - Returns a vector with references to struct definitions + pub(crate) fn used_structs_from_interface( + &mut self, + dependent_types: &HashSet>, + ) -> Vec<&StructDefinition> { + dependent_types + .iter() + .filter(|t| t.is_struct()) + .map(|t| { + let name = match t.as_ref() { + Type::Struct { name } => name, + _ => unreachable!(), + }; + self.get_struct_definition(name) + .expect("Struct definition must exist for a defined type") + }) + .collect() + } +/// Get used unions from interface + /// + /// # Arguments + /// + /// * `dependent_types` - A hash set with reference counted types + /// + /// # Returns + /// + /// * `Vec<&UnionDefinition>` - Returns a vector with references to union definitions + pub(crate) fn used_unions_from_interface( + &mut self, + dependent_types: &HashSet>, + ) -> Vec<&UnionDefinition> { + dependent_types + .iter() + .filter(|t| t.is_union()) + .map(|t| { + let name = match t.as_ref() { + Type::Union { name } => name, + _ => unreachable!(), + }; + self.get_union_definition(name) + .expect("Union definition must exist for a defined type") + }) + .collect() + } +/// Get used enums from interface + /// + /// # Arguments + /// + /// * `dependent_types` - A hash set with reference counted types + /// + /// # Returns + /// + /// * `Vec<&EnumDefinition>` - Returns a vector with references to enum definitions + pub(crate) fn used_enums_from_interface( + &mut self, + dependent_types: &HashSet>, + ) -> Vec<&EnumDefinition> { + let mut enums: Vec<&EnumDefinition> = dependent_types + .iter() + .filter(|t| t.is_enum()) + .map(|t| { + let name = match t.as_ref() { + Type::Enum { name } => name, + _ => unreachable!(), + }; + self.get_enum_definition(name) + .expect("Enum definition must exist for a defined type") + }) + .collect(); + let nameless_enums: Vec<&EnumDefinition> = self + .enum_definitions + .iter() + .filter(|e| e.name.is_none()) + .collect(); + enums.extend(nameless_enums); + enums + } +/// Get used types from interface + /// + /// # Arguments + /// + /// * `dependent_types` - A hash set with reference counted types + /// + /// # Returns + /// + /// * `Vec<&TypeDefinition>` - Returns a vector with references to type definitions + pub(crate) fn used_types_from_interface( + &mut self, + dependent_types: &HashSet>, + ) -> Vec<&TypeDefinition> { + self.type_definitions + .iter() + .filter(|t| dependent_types.contains(&t.referenced_type)) + .collect() // To keep the order + } + /// Check if an interface id is free + /// + /// # Arguments + /// + /// * `id` - An usize + /// + /// # Returns + /// + /// * `bool` - Returns true if the interface id is free, false otherwise + pub(crate) fn check_is_interface_id_free(&self, id: usize) -> bool { + !self.interface_ids.contains(&id) + } + /// Adds a rename mapping to the program. + /// + /// This function inserts a new rename mapping into the `renames` HashMap of the program. + /// The `renames` HashMap is used to map from one name to another. + /// + /// # Arguments + /// + /// * `from` - A string slice that holds the old name. + /// * `annotations` - A vector of annotations. + pub(crate) fn check_name_annotation(&mut self, from: &str, annotations: &Vec) { + if let Some(Annotation::Name {name, ..}) = find_name_annotation(annotations) { + self.renames.insert(from.to_string(), name.clone()); + } + } + + /// Applies the rename mappings to the program. + /// + /// This function iterates over the various definitions in the program (constants, enums, types, structs, unions, interfaces) + /// and applies the rename mappings stored in the `renames` HashMap of the program. + /// + /// The `renames` HashMap is used to map from one name to another. This is useful when a name in the source code needs to be + /// changed in the generated code, for example, to avoid naming conflicts or to adhere to the naming conventions of the target language. + /// + /// Each definition has an `apply_rename` method that takes care of renaming itself and its members (if applicable). + pub(crate) fn apply_renames(&mut self) { + self.const_definitions.iter_mut().for_each(|c| { + c.apply_rename(&self.renames); + }); + self.enum_definitions.iter_mut().for_each(|e| { + e.apply_rename(&self.renames); + }); + self.type_definitions.iter_mut().for_each(|t| { + t.apply_rename(&self.renames); + }); + self.struct_definitions.iter_mut().for_each(|s| { + s.apply_rename(&self.renames); + }); + self.union_definitions.iter_mut().for_each(|u| { + u.apply_rename(&self.renames); + }); + self.interface_definitions.iter_mut().for_each(|i| { + i.apply_rename(&self.renames); + }); + } +} + +pub(crate) struct ProgramBuilder<'a> { + program: &'a mut Program, + strict: bool, + name: Option, + annotations: Vec, + doxygen_preceding_comment: Vec, + doxygen_trailing_comment: Vec, +} + +/// Implementation of ProgramBuilder +impl<'a> ProgramBuilder<'a> { + pub(crate) fn new(program: &'a mut Program, strict: bool) -> Self { + Self { + program, + strict, + name: None, + annotations: vec![], + doxygen_preceding_comment: vec![], + doxygen_trailing_comment: vec![], + } + } +/// Create a new ProgramBuilder + /// + /// # Arguments + /// + /// * `name` - A pair of rule + /// + /// # Returns + /// + /// * `Result>>` - Returns a result with the builder or an error + pub(crate) fn with_name(mut self, name: Pair) -> Result>> { + self.name = Some(name.as_str().to_string()); + Ok(self) + } +/// Create a new ProgramBuilder + /// + /// # Arguments + /// + /// * `annotations` - A pair of rule + /// + /// # Returns + /// + /// * `Result>>` - Returns a result with the builder or an error + pub(crate) fn with_annotations( + mut self, + annotations: Pair, + ) -> Result>> { + self.annotations = parse_program_annotations( + self.program, + annotations, + &self.program.language, + self.strict, + )?; + Ok(self) + } +/// Create a new ProgramBuilder + /// + /// # Arguments + /// + /// * `comment` - Doxygen comment to add + /// + /// # Returns + /// + /// * The builder. + pub(crate) fn with_doxygen_preceding_comment(mut self, comment: Vec) -> Self { + self.doxygen_preceding_comment = comment; + self + } +/// Create a new ProgramBuilder + /// + /// # Arguments + /// + /// * `comment` - Doxygen comment to add + /// + /// # Returns + /// + /// * The builder. + pub(crate) fn with_doxygen_trailing_comment(mut self, comment: Vec) -> Self { + self.doxygen_trailing_comment = comment; + self + } +/// Build the program + /// + /// # Returns + /// + /// * `Result<(), Box>>` - Returns a result with a unit or an error + pub(crate) fn build(self) -> Result<(), Box>> { + self.program.name = self.name; + self.program.annotations = self.annotations; + self.program.doxygen_preceding_comment = self.doxygen_preceding_comment; + self.program.doxygen_trailing_comment = self.doxygen_trailing_comment; + Ok(()) + } + +} diff --git a/erpcgen_rust/src/symbols/struct_data_member.rs b/erpcgen_rust/src/symbols/struct_data_member.rs new file mode 100644 index 00000000..d43a2b18 --- /dev/null +++ b/erpcgen_rust/src/symbols/struct_data_member.rs @@ -0,0 +1,237 @@ +/* + * Copyright 2024 Marek Mišík(nxf76107) + * Copyright 2024 NXP + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +use std::collections::HashMap; +use std::rc::Rc; + +use pest::error::Error; +use pest::iterators::Pair; + +use crate::parser::grammar_parser::Rule; +use crate::parser::util::annotations::{find_name_annotation, has_discriminator_annotation, parse_array_annotations, parse_enum_annotations, parse_list_annotations, parse_member_annotations, parse_struct_annotations, parse_union_annotations}; +use crate::parser::util::struct_definition::{extract_options_list, parse_member_options}; +use crate::symbols::annotations::Annotation; +use crate::symbols::doxygen_comment::DoxygenComment; +use crate::symbols::member_options::MemberOptions; +use crate::symbols::pending_ident::PendingIdent; +use crate::symbols::struct_definition::StructDefinitionBuilder; +use crate::symbols::struct_member::StructMember; +use crate::symbols::types::Type; +use crate::symbols::util::{apply_rename_for_annotation, apply_rename_for_type}; + +/// Struct data member +#[derive(Debug, PartialEq, Clone)] +pub(crate) struct StructDataMember{ + pub(crate) member_options: Vec, + pub(crate) name: String, + pub(crate) member_type: Rc, + pub(crate) annotations: Vec, + pub(crate) doxygen_preceding_comment: Vec, + pub(crate) doxygen_trailing_comment: Vec, +} + +impl StructDataMember { + pub fn apply_rename(&mut self, struct_renames: &HashMap, program_renames: &HashMap) { + self.name = struct_renames.get(&self.name).unwrap_or(&self.name).to_string(); + self.member_type = apply_rename_for_type(self.member_type.clone(), program_renames); + self.annotations = self.annotations.iter().cloned().map(|a| apply_rename_for_annotation(a, struct_renames, program_renames)).collect(); + } +} + +/// Struct data member builder +pub(crate) struct StructDataMemberBuilder<'a> { + struct_definition_builder: &'a StructDefinitionBuilder<'a>, + strict: bool, + member_options: Vec, + name: Option, + member_type: Option>, + annotations: Vec, + doxygen_preceding_comment: Vec, + doxygen_trailing_comment: Vec, + pending_idents: Vec +} + +/// Implementation of StructDataMemberBuilder +impl <'a> StructDataMemberBuilder<'a> { + pub(super) fn new(struct_definition_builder: &'a StructDefinitionBuilder<'a>, strict: bool) -> Self { + Self { + struct_definition_builder, + strict, + member_options: vec![], + name: None, + member_type: None, + annotations: vec![], + doxygen_preceding_comment: vec![], + doxygen_trailing_comment: vec![], + pending_idents: vec![] + } + } + + /// Create a new StructDataMemberBuilder + /// + /// # Arguments + /// + /// * `rule` - Pair + /// + /// # Returns + /// + /// * The builder + pub(crate) fn with_name(mut self, rule: Pair) -> Result>> { + let location = rule.as_span(); + let name = rule.as_str().to_string(); + if !self.struct_definition_builder.check_member_name_is_free(&name) { + return Err(Box::new(Error::new_from_span( + pest::error::ErrorVariant::CustomError { + message: format!("Symbol name {} is already in use", name).to_string(), + }, + location, + ))); + } + self.name = Some(name); + Ok(self) + } + + /// Create a new StructDataMemberBuilder + /// + /// # Arguments + /// + /// * `type_rule` - Pair + /// + /// # Returns + /// + /// * The builder + pub(crate) fn with_type(mut self, type_rule: Pair) -> Result>> { + let member_type = self.struct_definition_builder.program.resolve_simple_data_type(type_rule)?; + self.member_type = Some(member_type); + Ok(self) + } + + /// Create a new StructDataMemberBuilder + /// + /// # Arguments + /// + /// * `rule` - Pair + /// + /// # Returns + /// + /// * The builder + pub(crate) fn with_options(mut self, mut rule: Pair) -> Result>>{ + let rules = rule.into_inner(); + let option_rules = extract_options_list(&rules); + let member_options = match option_rules { + Some(options) => { + parse_member_options(options.into_inner(), self.strict)? + } + None => { + vec![] + } + }; + self.member_options = member_options; + Ok(self) + } + + /// Create a new StructDataMemberBuilder + /// + /// # Arguments + /// + /// * `comment` - Vec + /// + /// # Returns + /// + /// * The builder + pub(crate) fn with_doxygen_preceding_comment(mut self, comment: Vec) -> Self { + self.doxygen_preceding_comment = comment; + self + } + + /// Create a new StructDataMemberBuilder + /// + /// # Arguments + /// + /// * `comment` - Vec + /// + /// # Returns + /// + /// * The builder + pub(crate) fn with_doxygen_trailing_comment(mut self, comment: Vec) -> Self { + self.doxygen_trailing_comment = comment; + self + } + + + /// Create a new StructDataMemberBuilder + /// + /// # Arguments + /// + /// * `rule` - Pair + /// + /// # Returns + /// + /// * The builder + /// + /// # Errors + /// + /// * Type must be defined + /// * Union member must have a discriminator annotation + pub(crate) fn with_annotations(mut self, rule: Pair) -> Result>> { + self.annotations = match self.member_type.clone().expect("Type must be defined").as_ref() { + Type::Struct { .. } => parse_struct_annotations(rule.clone(), &self.struct_definition_builder.program.language, self.strict.clone())?, + Type::Enum { .. } => parse_enum_annotations(rule.clone(), &self.struct_definition_builder.program.language, self.strict.clone())?, + Type::Union { .. } => { + let (ann, pend) = parse_union_annotations(rule.clone(),&self.struct_definition_builder.program.language , self.strict.clone())?; + if !has_discriminator_annotation(&ann) { + return Err(Box::new(Error::new_from_pos( + pest::error::ErrorVariant::CustomError { + message: "Union member must have a discriminator annotation".to_string(), + }, + rule.as_span().start_pos(), + ))); + } + self.pending_idents.extend(pend); + ann + }, + Type::Array { .. } => parse_array_annotations(rule.clone(), &self.struct_definition_builder.program.language, self.strict)?, + Type::List { .. } => { + let (ann, pend) = parse_list_annotations(&self.struct_definition_builder.program, rule.clone(), &self.struct_definition_builder.program.language, self.strict.clone())?; + self.pending_idents.extend(pend); + ann + }, + Type::Binary | Type::String => { + let (ann, pend) = parse_list_annotations(self.struct_definition_builder.program, rule.clone(), &self.struct_definition_builder.program.language, self.strict)?; + self.pending_idents.extend(pend); + ann + }, + _ => { + parse_member_annotations(rule.clone(), &self.struct_definition_builder.program.language, self.strict)? + } + }; + if self.member_type.clone().is_some_and(|t| t.is_union()) && !has_discriminator_annotation(&self.annotations) { + return Err(Box::new(Error::new_from_pos( + pest::error::ErrorVariant::CustomError { + message: "Union member must have a discriminator annotation".to_string(), + }, + rule.as_span().start_pos(), + ))); + } + if let Some(Annotation::Name {name, ..}) = find_name_annotation(&self.annotations) { + self.name = Some(name.clone()); // Consider adding a check for name conflict + } + Ok(self) + } + + /// Build the StructDataMember + pub(crate) fn build(self) -> (StructMember, Vec) { + (StructMember::DataMember(StructDataMember { + member_options: self.member_options, + name: self.name.expect("Name is required for struct member"), + member_type: self.member_type.expect("Type is required for struct member"), + annotations: self.annotations, + doxygen_preceding_comment: self.doxygen_preceding_comment, + doxygen_trailing_comment: self.doxygen_trailing_comment, + }), self.pending_idents) + } +} \ No newline at end of file diff --git a/erpcgen_rust/src/symbols/struct_definition.rs b/erpcgen_rust/src/symbols/struct_definition.rs new file mode 100644 index 00000000..00a1dd8a --- /dev/null +++ b/erpcgen_rust/src/symbols/struct_definition.rs @@ -0,0 +1,289 @@ +/* + * Copyright 2024 Marek Mišík(nxf76107) + * Copyright 2024 NXP + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +use std::collections::HashMap; +use std::hash::{Hash, Hasher}; +use pest::error::Error; +use pest::iterators::Pair; +use crate::parser::grammar_parser::Rule; +use crate::parser::util::annotations::{find_name_annotation, parse_struct_annotations}; +use crate::symbols::annotations::Annotation; +use crate::symbols::doxygen_comment::DoxygenComment; +use crate::symbols::pending_ident::{CHECK_SCALAR, PendingError, PendingIdent}; +use crate::symbols::program::Program; +use crate::symbols::struct_data_member::{StructDataMemberBuilder}; +use crate::symbols::struct_member::StructMember; +use crate::symbols::types::Type; +use crate::symbols::union_definition::UnionDefinitionBuilder; +use crate::symbols::value::Value; + +/// Struct definition +#[derive(Debug, PartialEq, Clone)] +pub(crate) struct StructDefinition { + pub(crate) name: String, + pub(crate) annotations: Vec, + pub(crate) renames: HashMap, + pub(crate) members: Vec, + pub(crate) doxygen_preceding_comment: Vec, + pub(crate) doxygen_trailing_comment: Vec, +} + +/// Hash implementation for StructDefinition +impl Hash for StructDefinition { + fn hash(&self, state: &mut H) { + self.name.hash(state); + } +} + +impl StructDefinition { + pub fn apply_rename(&mut self, renames: &HashMap) { + self.name = renames.get(&self.name).unwrap_or(&self.name).to_string(); + self.members.iter_mut().for_each(|m| m.apply_rename(&self.renames, renames)); + } +} + +/// Equality implementation for StructDefinition +impl Eq for StructDefinition {} + +/// Struct definition builder +pub(crate) struct StructDefinitionBuilder<'a> { + pub(super) program: &'a Program, + strict: bool, + name: Option, + renames: HashMap, + annotations: Vec, + members: Vec, + doxygen_preceding_comment: Vec, + doxygen_trailing_comment: Vec, + pending_idents: Vec +} + + +/// Implementation of StructDefinitionBuilder +impl<'a> StructDefinitionBuilder<'a> { + pub fn new(program: &'a Program, strict: bool) -> Self { + Self { + program, + strict, + name: None, + annotations: vec![], + renames: HashMap::new(), + members: vec![], + doxygen_preceding_comment: vec![], + doxygen_trailing_comment: vec![], + pending_idents: vec![] + } + } + + /// Create a new StructDefinitionBuilder + /// + /// # Arguments + /// + /// * `comment` - A vector of DoxygenComment + /// + /// # Returns + /// + /// * The builder + pub(crate) fn with_doxygen_preceding_comment(mut self, comment: Vec) -> Self { + self.doxygen_preceding_comment = comment; + self + } + + /// Create a new StructDefinitionBuilder + /// + /// # Arguments + /// + /// * `comment` - A vector of DoxygenComment + /// + /// # Returns + /// + /// * The builder + pub(crate) fn with_doxygen_trailing_comment(mut self, comment: Vec) -> Self { + self.doxygen_trailing_comment = comment; + self + } + + /// Check if the struct has a forward declaration + /// + /// # Arguments + /// + /// * `def_name` - The name of the struct + /// + /// # Returns + /// + /// * True if the struct has a forward declaration, else false + fn check_has_forward_declaration(&self, def_name: &str) -> bool { + let has_type = self.program.types.iter().any(|t| { + match t.as_ref() { + Type::Struct { name } => name.as_str() == def_name, + _ => false + } + }); + let has_definition = self.program.struct_definitions.iter().any(|d| d.name == def_name); + has_type && !has_definition + } + + /// Create a new StructDefinitionBuilder + /// + /// # Arguments + /// + /// * `rule` - Pair + /// + /// # Returns + /// + /// * The builder + /// + /// # Errors + /// + /// * Symbol name is already in use + pub(crate) fn with_name(mut self, rule: Pair) -> Result>> { + if self.name.is_some() { + return Ok(self) + } + let location = rule.as_span(); + let name = rule.as_str().to_string(); + // Either the name is not in use or it is used in a forward declaration + if !self.check_has_forward_declaration(&name) && !self.program.check_symbol_name_is_free(&name) { + return Err(Box::new(Error::new_from_span( + pest::error::ErrorVariant::CustomError { + message: format!("Symbol name {} is already in use", name).to_string(), + }, + location, + ))); + } + self.name = Some(name); + Ok(self) + } + + /// Create a new StructDefinitionBuilder + /// + /// # Arguments + /// + /// * `rule` - Pair + /// + /// # Returns + /// + /// * The builder + pub(crate) fn with_annotations(mut self, rule: Pair) -> Result>> { + self.annotations = parse_struct_annotations(rule, &self.program.language, self.strict)?; + Ok(self) + } + + /// Create a new StructDefinitionBuilder + /// + /// # Arguments + /// + /// * `name` - A &str + /// + /// # Returns + /// + /// * The builder + pub(crate) fn check_member_name_is_free(&self, name: &str) -> bool { + self.members.iter().all(|member| + match member { + StructMember::DataMember(f) => f.name != name, + StructMember::UnionDefinition{ definition: d,.. } => d.name != name, + } + ) + } + + /// Add a new member + /// + /// # Arguments + /// + /// * `member` - StructMember + /// * `pending_idents` - A vector of PendingIdent + pub(crate) fn add_member(&mut self, member: StructMember, pending_idents: Vec) { + self.pending_idents.extend(pending_idents); + self.check_member_name_annotations(&member); + self.members.push(member); + } + + /// Create a new StructDataMemberBuilder + pub(crate) fn new_member_builder(&self, strict: bool) -> StructDataMemberBuilder { + StructDataMemberBuilder::new(self, strict) + } + + /// Create a new UnionDefinitionBuilder + pub(crate) fn new_union_builder(&self, strict: bool) -> UnionDefinitionBuilder { + UnionDefinitionBuilder::new_encapsulated(self.program, strict, self) + } + + /// Get a discriminator by identifier + /// + /// # Arguments + /// + /// * `pair` - Pair + /// + /// # Returns + /// + /// * The value and a vector of PendingIdent + pub(crate) fn get_discriminator_by_ident(&self, pair: Pair) -> Result<(Value, Vec), Box>> { + let ident = pair.as_str().to_string(); + Ok((Value::RuntimeValue { name: ident.clone() }, vec![PendingIdent::new(ident, pair.as_span().start_pos().pos(), Box::new(CHECK_SCALAR))])) + } + + /// Resolve pending identifiers + /// + /// # Returns + /// + /// * Ok if successful, else an error + pub(crate) fn resolve_pending_idents(&mut self) -> Result<(), PendingError> { + for pending in &self.pending_idents { + let member = self.members.iter().find(|m| match m { + StructMember::DataMember(f) => f.name == pending.name, + StructMember::UnionDefinition { .. } => false + }); + if let Some(StructMember::DataMember(m)) = member { + if let Some(e) = (pending.check)(m.member_type.clone().as_ref()) { + return Err(PendingError::new(pending.name.clone(), pending.position, e)) + } + } else { + return Err(PendingError::new(pending.name.clone(), pending.position, format!("Symbol name {} is not a member of the struct", pending.name))); + } + } + Ok(()) + } + + /// Adds a rename mapping to the program. + /// + /// This function inserts a new rename mapping into the `renames` HashMap of the program. + /// The `renames` HashMap is used to map from one name to another. + /// + /// # Arguments + /// + /// * `from` - A string slice that holds the old name. + /// * `annotations` - A vector of annotations. + pub(crate) fn check_name_annotation(&mut self, from: &str, annotations: &Vec) { + if let Some(Annotation::Name {name, ..}) = find_name_annotation(annotations) { + self.renames.insert(from.to_string(), name.clone()); + } + } + + fn check_member_name_annotations(&mut self, member: &StructMember) { + match member { + StructMember::DataMember(f) => self.check_name_annotation(&f.name, &f.annotations), + StructMember::UnionDefinition{ definition: d, .. } => self.check_name_annotation(&d.name, &d.annotations), + } + } + + /// Build the StructDefinition + /// + /// # Returns + /// + /// * The struct definitions. + pub(crate) fn build(self) -> Result>> { + Ok(StructDefinition { + name: self.name.unwrap(), + annotations: self.annotations, + renames: self.renames, + members: self.members, + doxygen_preceding_comment: self.doxygen_preceding_comment, + doxygen_trailing_comment: self.doxygen_trailing_comment, + }) + } +} \ No newline at end of file diff --git a/erpcgen_rust/src/symbols/struct_member.rs b/erpcgen_rust/src/symbols/struct_member.rs new file mode 100644 index 00000000..62ec7d49 --- /dev/null +++ b/erpcgen_rust/src/symbols/struct_member.rs @@ -0,0 +1,30 @@ +/* + * Copyright 2024 Marek Mišík(nxf76107) + * Copyright 2024 NXP + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +use std::collections::HashMap; +use crate::symbols::struct_data_member::StructDataMember; +use crate::symbols::union_definition::UnionDefinition; +use crate::symbols::value::Value; + +/// Struct member +#[derive(Debug, PartialEq, Clone)] +pub(crate) enum StructMember { + DataMember(StructDataMember), + UnionDefinition { + definition: UnionDefinition, + discriminator: Value, // Can be Value::RuntimeValue + }, +} + +impl StructMember { + pub fn apply_rename(&mut self, struct_renames: &HashMap, program_renames: &HashMap) { + match self { + StructMember::DataMember(member) => member.apply_rename(struct_renames, program_renames), + StructMember::UnionDefinition { definition, .. } => definition.apply_rename_encapsulated(struct_renames, program_renames), + } + } +} \ No newline at end of file diff --git a/erpcgen_rust/src/symbols/type_definition.rs b/erpcgen_rust/src/symbols/type_definition.rs new file mode 100644 index 00000000..94063087 --- /dev/null +++ b/erpcgen_rust/src/symbols/type_definition.rs @@ -0,0 +1,212 @@ +/* + * Copyright 2024 Marek Mišík(nxf76107) + * Copyright 2024 NXP + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +use std::collections::HashMap; +use std::hash::Hash; +use std::rc::Rc; +use pest::error::{Error, ErrorVariant}; +use pest::iterators::Pair; +use crate::parser::grammar_parser::Rule; +use crate::parser::util::annotations::parse_typedef_annotations; +use crate::symbols::annotations::Annotation; +use crate::symbols::doxygen_comment::DoxygenComment; +use crate::symbols::program::Program; +use crate::symbols::types::Type; +use crate::symbols::util::apply_rename_for_type; + +/// Type definition +#[derive(Debug, Clone, PartialEq)] +pub(crate) struct TypeDefinition { + pub(crate) name: String, + pub(crate) annotations: Vec, + pub(crate) doxygen_preceding_comment: Vec, + pub(crate) doxygen_trailing_comment: Vec, + pub(crate) referenced_type: Rc +} + +/// Hash implementation for TypeDefinition +impl Hash for TypeDefinition { + fn hash(&self, state: &mut H) { + self.name.hash(state); + } +} + +/// Equality implementation for TypeDefinition +impl Eq for TypeDefinition {} + +/// Implementation of TypeDefinition + +impl TypeDefinition { + pub fn apply_rename(&mut self, renames: &HashMap) { + self.name = renames.get(&self.name).unwrap_or(&self.name).to_string(); + self.referenced_type = apply_rename_for_type(self.referenced_type.clone(), renames); + } +} + +/// Type definition builder +pub(crate) struct TypeDefinitionBuilder<'a> { + pub(super) program: &'a Program, + strict: bool, + name: Option, + annotations: Vec, + doxygen_preceding_comment: Vec, + doxygen_trailing_comment: Vec, + referenced_type: Option>, +} + +/// Implementation of TypeDefinitionBuilder +impl<'a> TypeDefinitionBuilder<'a> { + pub fn new(program: &'a Program, strict: bool) -> Self { + Self { + program, + strict, + name: None, + annotations: vec![], + doxygen_preceding_comment: vec![], + doxygen_trailing_comment: vec![], + referenced_type: None, + } + } + + /// Create a new TypeDefinitionBuilder + /// + /// # Arguments + /// + /// * `comment` - A vector of DoxygenComment + /// + /// # Returns + /// + /// * The builder + pub(crate) fn with_doxygen_preceding_comment(mut self, comment: Vec) -> Self { + self.doxygen_preceding_comment = comment; + self + } + + /// Create a new TypeDefinitionBuilder + /// + /// # Arguments + /// + /// * `comment` - A vector of DoxygenComment + /// + /// # Returns + /// + /// * The builder + pub(crate) fn with_doxygen_trailing_comment(mut self, comment: Vec) -> Self { + self.doxygen_trailing_comment = comment; + self + } + + /// Create a new TypeDefinitionBuilder + /// + /// # Arguments + /// + /// * `rule` - Pair + /// + /// # Returns + /// + /// * The builder + pub(crate) fn with_annotations(mut self, rule: Pair) -> Result>> { + self.annotations = parse_typedef_annotations(rule, &self.program.language, self.strict)?; + Ok(self) + } + + /// Create a new TypeDefinitionBuilder + /// + /// # Arguments + /// + /// * `type_rule` - Pair + /// + /// # Returns + /// + /// * The builder + pub(crate) fn with_simple_type(mut self, type_rule: Pair) -> Result>> { + let referenced_type = self.program.resolve_simple_data_type(type_rule)?; + self.referenced_type = Some(referenced_type); + Ok(self) + } + + /// Create a new TypeDefinitionBuilder + /// + /// # Returns + /// + /// * The builder + /// + /// # Errors + /// + /// * Struct definition must be defined before type definition + pub(crate) fn with_latest_struct_definition(mut self) -> Result>> { + let referenced_type = self.program.struct_definitions.last() + .expect("Struct definition must be defined before type definition"); + self.referenced_type = self.program.resolve_type_from_ident(referenced_type.name.as_str()); + Ok(self) + } + + /// Create a new TypeDefinitionBuilder + /// + /// # Returns + /// + /// * The builder + /// + /// # Errors + /// + /// * Enum definition must be defined before type definition + /// * Type definition must have a name + pub(crate) fn with_latest_enum_definition(mut self) -> Result>> { + let referenced_type = self.program.enum_definitions.last() + .expect("Enum definition must be defined before type definition"); + if let Some(name) = referenced_type.name.clone() { + self.referenced_type = self.program.resolve_type_from_ident(name.as_str()); + } else { + self.referenced_type = self.program.resolve_type_from_ident(self.name.clone().expect("Type definition must have a name").as_str()); + } + Ok(self) + } + + + /// Create a new TypeDefinitionBuilder + /// + /// # Arguments + /// + /// * `rule` - Pair + /// + /// # Returns + /// + /// * The builder + /// + /// # Errors + /// + /// * Symbol name is already in use + pub(crate) fn with_name(mut self, rule: Pair) -> Result>> { + let location = rule.as_span(); + let name = rule.as_str().to_string(); + if !self.program.check_value_name_is_free(&name) + { + return Err(Box::new(Error::new_from_span( + ErrorVariant::CustomError { + message: format!("Symbol name {} is already in use", name).to_string(), + }, + location, + ))); + } + + self.name = Some(name); + Ok(self) + } + + + /// Create a new TypeDefinitionBuilder + pub(crate) fn build(self) -> TypeDefinition { + TypeDefinition { + name: self.name.expect("Type definition must have a name"), + annotations: self.annotations, + doxygen_preceding_comment: self.doxygen_preceding_comment, + doxygen_trailing_comment: self.doxygen_trailing_comment, + referenced_type: self.referenced_type.expect("Type definition must have a type"), + } + } +} + \ No newline at end of file diff --git a/erpcgen_rust/src/symbols/types.rs b/erpcgen_rust/src/symbols/types.rs new file mode 100644 index 00000000..2a40c8e4 --- /dev/null +++ b/erpcgen_rust/src/symbols/types.rs @@ -0,0 +1,356 @@ +/* + * Copyright 2024 Marek Mišík(nxf76107) + * Copyright 2024 NXP + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +use std::rc::{Rc}; + +/// Type +#[derive(Debug, Clone, PartialEq, Hash)] +pub(crate) enum Type { + Bool, + Int8, + Int16, + Int32, + Int64, + UInt8, + UInt16, + UInt32, + UInt64, + Float, + Double, + String, + Binary, + Struct { + name: String, + }, + Enum { + name: String + }, + Union { + name: String, + }, + Array { + element_type: Rc, + dimension_sizes: Vec, + }, + List { + element_type: Rc, + }, + TypeDef { + name: String, + referenced_type: Rc, + }, + Callback { + scope: String, + name: String, + }, +} + +/// Symbol type +pub(crate) enum SymbolType { + Type, + Struct, + Enum, + Union, + Array, + List, + Callback, +} + +/// Hash implementation for Type +impl Eq for Type {} + +/// Type implementation +impl Type { + /// Checks if the type is a primitive + /// + /// # Returns + /// + /// * True if the type is a primitive, else false + pub(crate) fn is_primitive(&self) -> bool { + if let Type::TypeDef { referenced_type, .. } = self { + return referenced_type.is_primitive(); + } + matches!(self, Type::Bool | Type::Int8 | Type::Int16 | Type::Int32 | Type::Int64 | Type::UInt8 | Type::UInt16 | Type::UInt32 | Type::UInt64 | Type::Float | Type::Double | Type::String) + } + + + /// Checks if the type is a struct + /// + /// # Returns + /// + /// * True if the type is a struct, else false + pub(crate) fn is_struct(&self) -> bool { + match self { + Type::Struct { .. } => true, + Type::TypeDef { referenced_type, .. } => { + referenced_type.is_struct() + }, + _ => false + } + } + + + /// Checks if the type is an enum + /// + /// # Returns + /// + /// * True if the type is an enum, else false + pub(crate) fn is_enum(&self) -> bool { + match self { + Type::Enum { .. } => true, + Type::TypeDef { referenced_type, .. } => { + referenced_type.is_enum() + }, + _ => false + } + } + + /// Checks if the type is a union + /// + /// # Returns + /// + /// * True if the type is a union, else false + pub(crate) fn is_union(&self) -> bool { + match self { + Type::Union { .. } => true, + Type::TypeDef { referenced_type, .. } => { + referenced_type.is_union() + }, + _ => false + } + } + + /// Checks if the type is an array + /// + /// # Returns + /// + /// * True if the type is an array, else false + pub(crate) fn is_array(&self) -> bool { + match self { + Type::Array { .. } => true, + Type::TypeDef { referenced_type, .. } => { + referenced_type.is_array() + }, + _ => false + } + } + + /// Checks if the type is a list + /// + /// # Returns + /// + /// * True if the type is a list, else false + pub(crate) fn is_list(&self) -> bool { + match self { + Type::List { .. } => true, + Type::TypeDef { referenced_type, .. } => { + referenced_type.is_list() + }, + _ => false + } + } + + /// Checks if the type is a function + /// + /// # Returns + /// + /// * True if the type is a function, else false + pub(crate) fn is_function(&self) -> bool { + match self { + Type::Callback { .. } => true, + Type::TypeDef { referenced_type, .. } => { + referenced_type.is_function() + }, + _ => false + } + } + + /// Checks if the type is a typedef + /// + /// # Returns + /// + /// * True if the type is a typedef, else false + pub(crate) fn is_typedef(&self) -> bool { + matches!(self, Type::TypeDef { .. }) + } + + /// Resolve original type + /// + /// # Returns + /// + /// * The original type + pub(crate) fn resolve_original_type(&self) -> Rc { + match self { + Type::TypeDef { referenced_type, .. } => { + referenced_type.resolve_original_type() + }, + _ => Rc::from(self.clone()) + } + } + + /// Checks if the type is complex + /// + /// # Returns + /// + /// * True if the type is complex, else false + pub(crate) fn is_complex(&self) -> bool { + if let Type::TypeDef { referenced_type, .. } = self { + return referenced_type.is_complex(); + } + self.is_struct() || self.is_enum() || self.is_union() || self.is_array() || self.is_list() + } + + /// Checks if the type is simple + /// + /// # Returns + /// + /// * True if the type is simple, else false + pub(crate) fn is_simple(&self) -> bool { + if let Type::TypeDef { referenced_type, .. } = self { + return referenced_type.is_simple(); + } + self.is_primitive() + } + + /// Checks if the type is an integer + /// + /// # Returns + /// + /// * True if the type is an integer, else false + pub(crate) fn is_integer(&self) -> bool { + if let Type::TypeDef { referenced_type, .. } = self { + return referenced_type.is_integer(); + } + matches!(self, Type::Int8 | Type::Int16 | Type::Int32 | Type::Int64 | Type::UInt8 | Type::UInt16 | Type::UInt32 | Type::UInt64) + } + + /// Checks if the type is a boolean + /// + /// # Returns + /// + /// * True if the type is a boolean, else false + pub(crate) fn is_boolean(&self) -> bool { + if let Type::TypeDef { referenced_type, .. } = self { + return referenced_type.is_boolean(); + } + matches!(self, Type::Bool) + } + + /// Checks if the type is a scalar + /// + /// # Returns + /// + /// * True if the type is a scalar, else false + pub(crate) fn is_scalar(&self) -> bool { + if let Type::TypeDef { referenced_type, .. } = self { + return referenced_type.is_scalar(); + } + self.is_integer() || self.is_boolean() || self.is_enum() + } + + /// Checks if the type is a floating point + /// + /// # Returns + /// + /// * True if the type is a floating point, else false + pub(crate) fn is_string(&self) -> bool { + if let Type::TypeDef { referenced_type, .. } = self { + return referenced_type.is_string(); + } + matches!(self, Type::String) + } + + /// Checks if the type is binary + /// + /// # Returns + /// + /// * True if the type is binary, else false + pub(crate) fn is_binary(&self) -> bool { + if let Type::TypeDef { referenced_type, .. } = self { + return referenced_type.is_binary(); + } + matches!(self, Type::Binary) + } + + /// Get the name of the type + /// + /// # Returns + /// + /// * The name of the type + pub(crate) fn get_name(&self) -> String { + match self { + Type::Bool => "bool".to_string(), + Type::Int8 => "int8".to_string(), + Type::Int16 => "int16".to_string(), + Type::Int32 => "int32".to_string(), + Type::Int64 => "int64".to_string(), + Type::UInt8 => "uint8".to_string(), + Type::UInt16 => "uint16".to_string(), + Type::UInt32 => "uint32".to_string(), + Type::UInt64 => "uint64".to_string(), + Type::Float => "float".to_string(), + Type::Double => "double".to_string(), + Type::String => "string".to_string(), + Type::Binary => "binary".to_string(), + Type::Struct { name } => name.clone(), + Type::Enum { name } => name.clone(), + Type::Union { name } => name.clone(), + Type::Array { element_type, dimension_sizes } => { + let mut result = element_type.get_name(); + for size in dimension_sizes { + result.push_str(&format!("[{}]", size)); + } + result + }, + Type::List { element_type } => format!("list<{}>", element_type.get_name()), + Type::TypeDef { name, .. } => name.clone(), + Type::Callback { scope, name } => format!("{}::{}", scope, name), + } + } + + /// Get the inner type + /// + /// # Returns + /// + /// * The inner type + pub(crate) fn get_inner_type(&self) -> Rc { + match self { + Type::Array { element_type, .. } => element_type.clone(), + Type::List { element_type } => element_type.clone(), + Type::TypeDef { referenced_type, .. } => referenced_type.get_inner_type(), + _ => Rc::from(self.clone()) + } + } + + /// Resolve primitive type + /// + /// # Arguments + /// + /// * `ident` - The identifier + /// + /// # Returns + /// + /// * The type if found, else None + pub(crate) fn resolve_primitive_type(ident: &str) -> Option { + match ident { + "bool" => Some(Type::Bool), + "int8" => Some(Type::Int8), + "int16" => Some(Type::Int16), + "int32" => Some(Type::Int32), + "int64" => Some(Type::Int64), + "uint8" => Some(Type::UInt8), + "uint16" => Some(Type::UInt16), + "uint32" => Some(Type::UInt32), + "uint64" => Some(Type::UInt64), + "float" => Some(Type::Float), + "double" => Some(Type::Double), + "string" => Some(Type::String), + "binary" => Some(Type::Binary), + _ => None + } + } +} \ No newline at end of file diff --git a/erpcgen_rust/src/symbols/union_case.rs b/erpcgen_rust/src/symbols/union_case.rs new file mode 100644 index 00000000..8f7e9760 --- /dev/null +++ b/erpcgen_rust/src/symbols/union_case.rs @@ -0,0 +1,202 @@ +/* + * Copyright 2024 Marek Mišík(nxf76107) + * Copyright 2024 NXP + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +use std::collections::HashMap; +use std::rc::Rc; +use log::trace; +use pest::error::{Error, ErrorVariant}; +use pest::iterators::Pair; +use crate::parser::grammar_parser::Rule; +use crate::symbols::pending_ident::{PendingError, PendingIdent}; +use crate::symbols::struct_member::StructMember; +use crate::symbols::types::Type; +use crate::symbols::union_case_member::{UnionCaseMember, UnionCaseMemberBuilder}; +use crate::symbols::union_definition::UnionDefinitionBuilder; +use crate::symbols::value::Value; + +/// Union case +#[derive(Debug, PartialEq, Clone)] +pub(crate) struct UnionCase { + pub(crate) case_type: Rc, + pub(crate) case_values: Vec, // Empty signifies default case + pub(crate) members: Vec, +} + +/// Implementation of UnionCase +impl UnionCase { + /// Apply rename to the UnionCase + /// + /// # Arguments + /// + /// * `renames` - HashMap + /// * `program_renames` - HashMap + pub(crate) fn apply_rename(&mut self, union_renames: &HashMap, program_renames: &HashMap) { + self.case_values = self.case_values.iter().map(|v| + if let Value::ReferencedValue { name, prefix, value } = v { + let new_name = program_renames.get(name).unwrap_or(name).to_string(); + let new_prefix = prefix.clone().map(|p| program_renames.get(&p).unwrap_or(&p).to_string()); + Value::ReferencedValue { name: new_name, value: value.clone(), prefix: new_prefix } + } else { + v.clone() + } + ).collect(); + self.members.iter_mut().for_each(|m| m.apply_rename(union_renames, program_renames)); + } +} + +/// Union case builder +pub(crate) struct UnionCaseBuilder<'a> { + pub(super) union_builder: &'a UnionDefinitionBuilder<'a>, + strict: bool, + case_type: Option>, + case_values: Vec, + members: Vec, + pending_idents: Vec +} + +/// Implementation of UnionCaseBuilder +impl<'a> UnionCaseBuilder<'a> { + pub(super) fn new(union_builder: &'a UnionDefinitionBuilder<'a>, strict: bool) -> UnionCaseBuilder<'a> { + Self { + union_builder, + strict, + case_type: Some(Rc::new(Type::Int32)), + case_values: vec![], + members: vec![], + pending_idents: vec![] + } + } + + /// Create a new UnionCaseBuilder + /// + /// # Arguments + /// + /// * `union_case_values_rule` - Option> + /// + /// # Returns + /// + /// * The builder. + pub(crate) fn with_case_values(mut self, union_case_values_rule: Option>) -> Result>> { + let Some(union_case_values_rule) = union_case_values_rule else { + return Ok(self) + }; + let mut case_values = Vec::new(); + trace!("With case values {:?}", union_case_values_rule.as_str()); + let value_rules = union_case_values_rule.into_inner(); + for value_rule in value_rules.find_tagged("exp") { + let value = self.evaluate_case_value(value_rule)?; + case_values.push(value); + } + self.case_values = case_values; + Ok(self) + } + + /// Evaluate case value + /// + /// # Arguments + /// + /// * `expression_rule` - Pair + /// + /// # Returns + /// + /// * The value or error + /// + /// # Errors + /// + /// * If the case value is not an integer + fn evaluate_case_value(&self, expression_rule: Pair) -> Result>> { + let span = expression_rule.as_span(); + let value = self.union_builder.program.resolve_value_or_reference_from_expression(expression_rule, &Type::Int32, self.strict)?; + + if !value.is_integer() && !value.is_referenced() { + return Err(Box::new(Error::new_from_span( + ErrorVariant::CustomError { + message: "Case member value must be an integer".to_string(), + }, + span, + ))); + } + Ok(value) + } + + + /// Create a new UnionCaseBuilder + /// + /// # Arguments + /// + /// * `member` - UnionCaseMember + /// * `pending` - Vec + pub(crate) fn with_member(&mut self, member: UnionCaseMember, pending: Vec) { + self.pending_idents.extend(pending); + self.members.push(member); + } + + + /// Create a new UnionCaseBuilder + /// + /// # Arguments + /// + /// * `default_rule` - Option> + /// + /// # Returns + /// + /// * The builder + pub(crate) fn check_if_default_case(self, default_rule: Option>) -> Result>> { + if let Some(default_rule) = default_rule { + if self.union_builder.has_default_case { + return Err(Box::new(Error::new_from_span( + ErrorVariant::CustomError { + message: "Default case must not have case values".to_string(), + }, + default_rule.as_span(), + ))); + } + } + Ok(self) + } + + /// Resolve pending identifiers + /// + /// # Returns + /// + /// * Ok if all pending identifiers are resolved, else an error + pub(crate) fn resolve_pending_idents(&mut self) -> Result<(), PendingError> { + for pending in &self.pending_idents { + let member = self.members.iter().find(|m| m.name == pending.name); + if let Some(m) = member { + if let Some(e) = (pending.check)(m.member_type.clone().as_ref()) { + return Err(PendingError::new(pending.name.clone(), pending.position, e)) + } + } else { + return Err(PendingError::new(pending.name.clone(), pending.position, format!("Symbol name {} is not a member of the union", pending.name))); + } + } + Ok(()) + } + + /// Create a new UnionCaseMemberBuilder + /// + /// # Returns + /// + /// * The builder + pub(crate) fn new_member_builder(&'a self) -> UnionCaseMemberBuilder<'a> { + UnionCaseMemberBuilder::new(self, self.strict) + } + + /// Build the UnionCase + /// + /// # Returns + /// + /// * The UnionCase + pub(crate) fn build(mut self) -> Result>> { + Ok(UnionCase { + case_type: self.case_type.unwrap(), + case_values: self.case_values, + members: self.members, + }) + } +} \ No newline at end of file diff --git a/erpcgen_rust/src/symbols/union_case_member.rs b/erpcgen_rust/src/symbols/union_case_member.rs new file mode 100644 index 00000000..2a58fb5f --- /dev/null +++ b/erpcgen_rust/src/symbols/union_case_member.rs @@ -0,0 +1,242 @@ +/* + * Copyright 2024 Marek Mišík(nxf76107) + * Copyright 2024 NXP + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +use std::collections::HashMap; +use std::rc::Rc; +use log::debug; +use pest::error::{Error}; +use pest::iterators::Pair; +use crate::parser::grammar_parser::Rule; +use crate::parser::util::annotations::{find_name_annotation, has_discriminator_annotation, parse_array_annotations, parse_enum_annotations, parse_list_annotations, parse_struct_annotations, parse_union_annotations}; +use crate::parser::util::struct_definition::{extract_options_list, extract_struct_member_options, parse_member_options}; +use crate::symbols::annotations::Annotation; +use crate::symbols::doxygen_comment::DoxygenComment; +use crate::symbols::member_options::MemberOptions; +use crate::symbols::pending_ident::PendingIdent; +use crate::symbols::types::Type; +use crate::symbols::union_case::UnionCaseBuilder; +use crate::symbols::util::{apply_rename_for_annotation, apply_rename_for_type}; + +/// Union case member +#[derive(Debug, PartialEq, Clone)] +pub(crate) struct UnionCaseMember { + pub(crate) member_options: Vec, + pub(crate) name: String, + pub(crate) member_type: Rc, + pub(crate) annotations: Vec, + pub(crate) doxygen_preceding_comment: Vec, + pub(crate) doxygen_trailing_comment: Vec, +} + +impl UnionCaseMember { + pub(crate) fn apply_rename(&mut self, union_renames: &HashMap, program_renames: &HashMap) { + self.name = union_renames.get(&self.name).unwrap_or(&self.name).to_string(); + self.member_type = apply_rename_for_type(self.member_type.clone(), program_renames); + self.annotations = self.annotations.iter().map(|a| apply_rename_for_annotation(a.clone(), union_renames, program_renames)).collect(); + + } +} + +/// Union case member builder +pub(crate) struct UnionCaseMemberBuilder<'a> { + union_case_builder: &'a UnionCaseBuilder<'a>, + strict: bool, + member_options: Vec, + name: Option, + member_type: Option>, + annotations: Vec, + doxygen_preceding_comment: Vec, + doxygen_trailing_comment: Vec, + pending_idents: Vec +} + +/// Implementation of UnionCaseMemberBuilder +impl<'a> UnionCaseMemberBuilder<'a> { + pub(super) fn new(union_case_builder: &'a UnionCaseBuilder<'a>, strict: bool) -> Self { + Self { + union_case_builder, + strict, + member_options: vec![], + name: None, + member_type: Some(Rc::new(Type::Int32)), + annotations: vec![], + doxygen_preceding_comment: vec![], + doxygen_trailing_comment: vec![], + pending_idents: vec![] + } + } + + /// Create a new UnionCaseMemberBuilder + /// + /// # Arguments + /// + /// * `rule` - Pair + /// + /// # Returns + /// + /// * The builder. + pub(crate) fn with_name(mut self, rule: Pair) -> Result>> { + let location = rule.as_span(); + let name = rule.as_str().to_string(); + debug!("With name {}", name); + if !self.union_case_builder.union_builder.check_union_case_member_name_is_free(&name) { + return Err(Box::new(Error::new_from_span( + pest::error::ErrorVariant::CustomError { + message: format!("Symbol name {} is already in use", name).to_string(), + }, + location, + ))); + } + self.name = Some(name); + Ok(self) + } + + /// Create a new UnionCaseMemberBuilder + /// + /// # Arguments + /// + /// * `rule` - Pair + /// + /// # Returns + /// + /// * The builder. + /// + /// # Errors + /// + /// * Type must be defined + /// * Union member must have a discriminator annotation + pub(crate) fn with_annotations(mut self, rule: Pair) -> Result>> { + let cloned_rule = rule.clone(); + self.annotations = match self.member_type.clone().expect("Type must be defined").as_ref() { + Type::Struct { .. } => parse_struct_annotations(rule, &self.union_case_builder.union_builder.program.language, self.strict.clone())?, + Type::Enum { .. } => parse_enum_annotations(rule, &self.union_case_builder.union_builder.program.language, self.strict.clone())?, + Type::Union { .. } => { + let (ann, pend) = parse_union_annotations(rule,&self.union_case_builder.union_builder.program.language , self.strict.clone())?; + if !has_discriminator_annotation(&ann) { + return Err(Box::new(Error::new_from_pos( + pest::error::ErrorVariant::CustomError { + message: "Union member must have a discriminator annotation".to_string(), + }, + cloned_rule.as_span().start_pos(), + ))); + } + self.pending_idents.extend(pend); + ann + }, + Type::Array { .. } => parse_array_annotations(rule, &self.union_case_builder.union_builder.program.language, self.strict.clone())?, + Type::List { .. } => { + let (ann, pend) = parse_list_annotations(&self.union_case_builder.union_builder.program, rule, &self.union_case_builder.union_builder.program.language, self.strict.clone())?; + self.pending_idents.extend(pend); + ann + }, + _ => vec![] + }; + if self.member_type.clone().is_some_and(|t| t.is_union()) && !has_discriminator_annotation(&self.annotations) { + return Err(Box::new(Error::new_from_pos( + pest::error::ErrorVariant::CustomError { + message: "Union member must have a discriminator annotation".to_string(), + }, + cloned_rule.as_span().start_pos(), + ))); + } + if let Some(Annotation::Name {name, ..}) = find_name_annotation(&self.annotations) { + self.name = Some(name.clone()); // Consider adding a check for name conflict + } + Ok(self) + } + + /// Create a new UnionCaseMemberBuilder + /// + /// # Arguments + /// + /// * `comment` - Vec + /// + /// # Returns + /// + /// * The builder + pub(crate) fn with_doxygen_preceding_comment(mut self, comment: Vec) -> Self { + self.doxygen_preceding_comment = comment; + self + } + + /// Create a new UnionCaseMemberBuilder + /// + /// # Arguments + /// + /// * `comment` - Vec + /// + /// # Returns + /// + /// * The builder + pub(crate) fn with_doxygen_trailing_comment(mut self, comment: Vec) -> Self { + self.doxygen_trailing_comment = comment; + self + } + + + /// Create a new UnionCaseMemberBuilder + /// + /// # Arguments + /// + /// * `rule` - Pair + /// + /// # Returns + /// + /// * The builder + pub(crate) fn with_options(mut self, mut rule: Pair) -> Result>>{ + let rules = rule.into_inner(); + let option_rules = extract_options_list(&rules); + let member_options = match option_rules { + Some(options) => { + parse_member_options(options.into_inner(), self.strict)? + } + None => { + vec![] + } + }; + self.member_options = member_options; + Ok(self) + } + + + /// Create a new UnionCaseMemberBuilder + /// + /// # Arguments + /// + /// * `rule` - Pair + /// + /// # Returns + /// + /// * The builder + pub(crate) fn with_type(mut self, rule: Pair) -> Result>> { + let member_type = self.union_case_builder.union_builder.program.resolve_simple_data_type(rule)?; + self.member_type = Some(member_type); + Ok(self) + } + + + /// Create a new UnionCaseMemberBuilder + /// + /// # Returns + /// + /// * The UnionCaseMember, and a vector of PendingIdent + /// + /// # Panics + /// + /// * If the UnionCaseMember name is missing + /// * If the UnionCaseMember type is missing + pub(crate) fn build(self) -> (UnionCaseMember, Vec) { + (UnionCaseMember { + member_options: self.member_options, + name: self.name.expect("Union case member name is missing"), + member_type: self.member_type.expect("Union case member type is missing"), + annotations: self.annotations, + doxygen_preceding_comment: self.doxygen_preceding_comment, + doxygen_trailing_comment: self.doxygen_trailing_comment, + }, self.pending_idents) + } +} \ No newline at end of file diff --git a/erpcgen_rust/src/symbols/union_definition.rs b/erpcgen_rust/src/symbols/union_definition.rs new file mode 100644 index 00000000..44d537cb --- /dev/null +++ b/erpcgen_rust/src/symbols/union_definition.rs @@ -0,0 +1,309 @@ +/* + * Copyright 2024 Marek Mišík(nxf76107) + * Copyright 2024 NXP + * + * SPDX-License-Identifier: BSD-3-Clause + */ +use std::collections::HashMap; +use std::hash::{Hash, Hasher}; +use pest::error::{Error, ErrorVariant}; +use pest::iterators::Pair; +use crate::parser::grammar_parser::Rule; +use crate::parser::util::annotations::{find_name_annotation, parse_member_annotations, parse_union_annotations}; +use crate::symbols::annotations::Annotation; +use crate::symbols::doxygen_comment::DoxygenComment; +use crate::symbols::program::Program; +use crate::symbols::struct_definition::StructDefinitionBuilder; +use crate::symbols::struct_member::StructMember; +use crate::symbols::types::Type; +use crate::symbols::union_case::{UnionCase, UnionCaseBuilder}; +use crate::symbols::value::Value; + +/// Union definition +#[derive(Debug, PartialEq, Clone)] +pub(crate) struct UnionDefinition { + pub(crate) name: String, + pub(crate) annotations: Vec, + pub(crate) renames: HashMap, + pub(crate) cases: Vec, + pub(crate) doxygen_preceding_comment: Vec, + pub(crate) doxygen_trailing_comment: Vec, +} + +/// Hash implementation for UnionDefinition +impl Hash for UnionDefinition { + fn hash(&self, state: &mut H) { + self.name.hash(state); + } +} + +/// Equality implementation for UnionDefinition +impl Eq for UnionDefinition {} + +impl UnionDefinition { + pub fn apply_rename(&mut self, renames: &HashMap) { + self.name = renames.get(&self.name).unwrap_or(&self.name).to_string(); + self.cases.iter_mut().for_each(|c| c.apply_rename(&self.renames, renames)); + } + + pub fn apply_rename_encapsulated(&mut self, struct_renames: &HashMap, renames: &HashMap) { + self.name = struct_renames.get(&self.name).unwrap_or(&self.name).to_string(); + self.cases.iter_mut().for_each(|c| c.apply_rename(&self.renames, renames)); + } +} + +/// Union definition builder +pub(crate) struct UnionDefinitionBuilder<'a> { + pub(super) program: &'a Program, + pub(super) opt_struct_builder: Option<&'a StructDefinitionBuilder<'a>>, + strict: bool, + name: Option, + pub(super) has_default_case: bool, + annotations: Vec, + renames: HashMap, + doxygen_preceding_comment: Vec, + doxygen_trailing_comment: Vec, + cases: Vec, +} + +/// Implementation of UnionDefinitionBuilder +impl <'a>UnionDefinitionBuilder<'a> { + pub fn new(program: &'a Program, strict: bool) -> Self { + Self { + program, + opt_struct_builder: None, + strict, + name: None, + has_default_case: false, + annotations: vec![], + renames: HashMap::new(), + doxygen_preceding_comment: vec![], + doxygen_trailing_comment: vec![], + cases: vec![], + } + } + + /// Create a new UnionDefinitionBuilder + /// + /// # Arguments + /// + /// * `program` - A reference to the Program + /// * `strict` - A boolean indicating if strict mode is enabled + /// * `struct_builder` - A reference to the StructDefinitionBuilder + /// + /// # Returns + /// + /// * The builder + pub(crate) fn new_encapsulated(program: &'a Program, strict: bool, struct_builder: &'a StructDefinitionBuilder<'a>) -> Self { + Self { + program, + opt_struct_builder: Some(struct_builder), + strict, + name: None, + has_default_case: false, + renames: HashMap::new(), + annotations: vec![], + doxygen_preceding_comment: vec![], + doxygen_trailing_comment: vec![], + cases: vec![] + } + } + + /// Create a new UnionDefinitionBuilder + /// + /// # Arguments + /// + /// * `def_name` - A string slice + /// + /// # Returns + /// + /// * True if the union has a forward declaration, else false + fn check_has_forward_declaration(&self, def_name: &str) -> bool { + let has_type = self.program.types.iter().any(|t| { + match t.as_ref() { + Type::Union { name } => name.as_str() == def_name, + _ => false + } + }); + let has_definition = self.program.union_definitions.iter().any(|d| d.name == def_name); + has_type && !has_definition + } + + /// Create a new UnionDefinitionBuilder + /// + /// # Arguments + /// + /// * `rule` - Pair + /// + /// # Returns + /// + /// * The builder + /// + /// # Errors + /// + /// * Symbol name is already in use + pub(crate) fn with_name(mut self, rule: Pair) -> Result>> { + let location = rule.as_span(); + let name = rule.as_str().to_string(); + if !self.check_has_forward_declaration(&name) && (self.opt_struct_builder.is_some_and(|b| !b.check_member_name_is_free(&name)) || !self.program.check_symbol_name_is_free(&name)) { + return Err(Box::new(Error::new_from_span( + ErrorVariant::CustomError { + message: format!("Symbol name {} is already in use", name).to_string(), + }, + location, + ))); + } + self.name = Some(name); + Ok(self) + } + + /// Create a new UnionDefinitionBuilder + /// + /// # Arguments + /// + /// * `rule` - Pair + /// + /// # Returns + /// + /// * The builder + pub(crate) fn with_annotations(mut self, rule: Pair) -> Result>> { + if self.opt_struct_builder.is_some() { + self.annotations = parse_member_annotations(rule, &self.program.language, self.strict)?; + } else { + let (ann, _) = parse_union_annotations(rule, &self.program.language, self.strict)?; + self.annotations = ann; + } + Ok(self) + } + + /// Create a new UnionDefinitionBuilder + /// + /// # Arguments + /// + /// * `comment` - A vector of DoxygenComment + /// + /// # Returns + /// + /// * The builder + pub(crate) fn with_doxygen_preceding_comment(mut self, comment: Vec) -> Self { + self.doxygen_preceding_comment = comment; + self + } + + /// Create a new UnionDefinitionBuilder + /// + /// # Arguments + /// + /// * `comment` - A vector of DoxygenComment + /// + /// # Returns + /// + /// * The builder + pub(crate) fn with_doxygen_trailing_comment(mut self, comment: Vec) -> Self { + self.doxygen_trailing_comment = comment; + self + } + + /// Create a new UnionDefinitionBuilder + /// + /// # Arguments + /// + /// * `case` - A UnionCase + pub(crate) fn with_case(&mut self, case: UnionCase) { + if case.case_values.is_empty() { // Default case + self.has_default_case = true; + } + for member in case.members.iter() { + self.check_name_annotation(&member.name, &member.annotations); + } + self.cases.push(case); + } + + /// Check if the union case member name is free + /// + /// # Arguments + /// + /// * `name` - A string slice + /// + /// # Returns + /// + /// * True if the union case member name is free, else false + pub(super) fn check_union_case_member_name_is_free(&self, name: &str) -> bool { + self.cases.iter().all(|c| c.members.iter().all(|m| m.name != name)) + } + + + /// Check if the union has a default case + /// + /// # Returns + /// + /// * True if the union has a default case, else false + pub(super) fn check_has_default_case(&self) -> bool { + self.cases.iter().any(|c| c.case_values.is_empty()) + } + + + /// Create a new UnionDefinitionBuilder + /// + /// # Returns + /// + /// * The builder + pub(crate) fn new_case_builder(&'a self) -> UnionCaseBuilder<'a> { + UnionCaseBuilder::new(self, self.strict) + } + + /// Adds a rename mapping to the program. + /// + /// This function inserts a new rename mapping into the `renames` HashMap of the program. + /// The `renames` HashMap is used to map from one name to another. + /// + /// # Arguments + /// + /// * `from` - A string slice that holds the old name. + /// * `annotations` - A vector of annotations. + pub(crate) fn check_name_annotation(&mut self, from: &str, annotations: &Vec) { + if let Some(Annotation::Name {name, ..}) = find_name_annotation(annotations) { + self.renames.insert(from.to_string(), name.clone()); + } + } + + /// Build the UnionDefinition + /// + /// # Returns + /// + /// * The UnionDefinition + pub(crate) fn build(self) -> UnionDefinition { + UnionDefinition { + name: self.name.unwrap(), + annotations: self.annotations, + renames: self.renames, + doxygen_preceding_comment: self.doxygen_preceding_comment, + doxygen_trailing_comment: self.doxygen_trailing_comment, + cases: self.cases, + } + } + + + /// Build the UnionDefinition + /// + /// # Arguments + /// + /// * `discriminator` - A Value + /// + /// # Returns + /// + /// * The StructMember + pub(crate) fn build_encapsulated(self, discriminator: Value) -> StructMember { + StructMember::UnionDefinition { + definition: UnionDefinition { + name: self.name.unwrap(), + annotations: self.annotations, + renames: self.renames, + doxygen_preceding_comment: self.doxygen_preceding_comment, + doxygen_trailing_comment: self.doxygen_trailing_comment, + cases: self.cases, + }, + discriminator + } + } +} \ No newline at end of file diff --git a/erpcgen_rust/src/symbols/util.rs b/erpcgen_rust/src/symbols/util.rs new file mode 100644 index 00000000..074f09e1 --- /dev/null +++ b/erpcgen_rust/src/symbols/util.rs @@ -0,0 +1,108 @@ +/* + * Copyright 2024 Marek Mišík(nxf76107) + * Copyright 2024 NXP + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +use std::collections::HashMap; +use std::ops::Deref; +use std::rc::Rc; +use crate::symbols::annotations::Annotation; +use crate::symbols::types::Type; +use crate::symbols::value::Value; + +/// Applies the rename mappings to a given type. +/// +/// This function takes a type and a HashMap of renames as input, and returns a new type where the names have been replaced according to the rename mappings. +/// The rename mappings are applied to the name of the type itself (if applicable) and recursively to the names of any contained types (for composite types like arrays and lists). +/// If a type does not have a name or if its name is not in the rename mappings, it is returned unchanged. +/// +/// # Arguments +/// +/// * `type_` - The type to which the rename mappings should be applied. This is an Rc because types can be shared between different parts of the program. +/// * `renames` - The rename mappings. This is a reference to a HashMap where each key-value pair represents a rename mapping from an original name to a new name. +/// +/// # Returns +/// +/// A new type where the names have been replaced according to the rename mappings. This is returned as an Rc because types can be shared between different parts of the program. +pub(crate) fn apply_rename_for_type(type_: Rc, renames: &HashMap) -> Rc { + match type_.deref() { + Type::Enum { name, .. } => { + let new_name = renames.get(name).unwrap_or(name).to_string(); + Rc::new(Type::Enum { name: new_name }) + } + Type::Struct { name, .. } => { + let new_name = renames.get(name).unwrap_or(name).to_string(); + Rc::new(Type::Struct { name: new_name }) + } + Type::Union { name, .. } => { + let new_name = renames.get(name).unwrap_or(name).to_string(); + Rc::new(Type::Union { name: new_name }) + } + Type::Array { element_type, dimension_sizes } => { + let new_element_type = apply_rename_for_type(element_type.clone(), renames); + Rc::new(Type::Array { element_type: new_element_type, dimension_sizes: dimension_sizes.clone() }) + } + Type::List { element_type } => { + let new_element_type = apply_rename_for_type(element_type.clone(), renames); + Rc::new(Type::List { element_type: new_element_type }) + } + Type::TypeDef { name, referenced_type } => { + let new_name = renames.get(name).unwrap_or(name).to_string(); + let new_referenced_type = apply_rename_for_type(referenced_type.clone(), renames); + Rc::new(Type::TypeDef { name: new_name, referenced_type: new_referenced_type }) + } + Type::Callback { scope, name } => { + let new_scope = renames.get(scope).unwrap_or(scope).to_string(); + let new_name = renames.get(name).unwrap_or(name).to_string(); + Rc::new(Type::Callback { scope: new_scope, name: new_name }) + } + _ => type_.clone() + } +} + +/// Applies the rename mappings to a given annotation. +/// +/// This function takes an annotation and two HashMaps of renames as input, and returns a new annotation where the names have been replaced according to the rename mappings. +/// The rename mappings are applied to the name of the value in the annotation (if applicable). +/// If a value does not have a name or if its name is not in the rename mappings, it is returned unchanged. +/// +/// # Arguments +/// +/// * `annotation` - The annotation to which the rename mappings should be applied. +/// * `member_renames` - The rename mappings for members. This is a reference to a HashMap where each key-value pair represents a rename mapping from an original name to a new name. +/// * `program_renames` - The rename mappings for the program. This is a reference to a HashMap where each key-value pair represents a rename mapping from an original name to a new name. +/// +/// # Returns +/// +/// A new annotation where the names have been replaced according to the rename mappings. +pub fn apply_rename_for_annotation(annotation: Annotation, member_renames: &HashMap, program_renames: &HashMap,) -> Annotation { + match annotation { + Annotation::Discriminator { value, language_specifier } => { + if let Value::RuntimeValue { name } = value { + let new_name = member_renames.get(&name).unwrap_or(&name).to_string(); + Annotation::Discriminator { value: Value::RuntimeValue { name: new_name }, language_specifier } + } else if let Value::ReferencedValue { prefix, name, value} = value { + let new_name = program_renames.get(&name).unwrap_or(&name).to_string(); + let new_prefix = prefix.map(|p| program_renames.get(&p).unwrap_or(&p).to_string()); + Annotation::Discriminator { value: Value::ReferencedValue { name: new_name, value, prefix: new_prefix }, language_specifier } + } else { + Annotation::Discriminator { value, language_specifier } + } + } + Annotation::Length { length, language_specifier } => { + if let Value::RuntimeValue { name } = length { + let new_name = member_renames.get(&name).unwrap_or(&name).to_string(); + Annotation::Length { length: Value::RuntimeValue { name: new_name }, language_specifier } + } else if let Value::ReferencedValue { prefix, name, value} = length { + let new_name = program_renames.get(&name).unwrap_or(&name).to_string(); + let new_prefix = prefix.map(|p| program_renames.get(&p).unwrap_or(&p).to_string()); + Annotation::Length { length: Value::ReferencedValue { name: new_name, value, prefix: new_prefix }, language_specifier } + } else { + Annotation::Length { length, language_specifier } + } + } + _ => annotation + } +} \ No newline at end of file diff --git a/erpcgen_rust/src/symbols/value.rs b/erpcgen_rust/src/symbols/value.rs new file mode 100644 index 00000000..106357e2 --- /dev/null +++ b/erpcgen_rust/src/symbols/value.rs @@ -0,0 +1,873 @@ +/* + * Copyright 2024 Marek Mišík(nxf76107) + * Copyright 2024 NXP + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +use std::collections::HashMap; +use std::hash::{Hash, Hasher}; +use std::ops::{Add, BitAnd, BitOr, BitXor, Div, Mul, Neg, Not, Rem, Shl, Shr, Sub}; +use crate::symbols::types::Type; + +/// Value +#[derive(Debug, Clone, PartialEq)] +pub(crate) enum Value { + Bool(bool), + Int8(i8), + Int16(i16), + Int32(i32), + Int64(i64), + Uint8(u8), + Uint16(u16), + Uint32(u32), + UInt64(u64), + Float(f32), + Double(f64), + String(String), + Struct { + fields: HashMap, + }, + Enum { + value: i32, + }, + Union { + fields: HashMap, + }, + Array { + elements: Vec, + dimension_sizes: Vec, + }, + List { + elements: Vec, + }, + ReferencedValue { + name: String, + prefix: Option, + value: Box, + }, + RuntimeValue { // The type of the value needs to be checked during parsing + name: String, + } +} + +/// Hash implementation for Value +impl Hash for Value { + fn hash(&self, state: &mut H) { + match self { + Value::Bool(v) => v.hash(state), + Value::Int8(v) => v.hash(state), + Value::Int16(v) => v.hash(state), + Value::Int32(v) => v.hash(state), + Value::Int64(v) => v.hash(state), + Value::Uint8(v) => v.hash(state), + Value::Uint16(v) => v.hash(state), + Value::Uint32(v) => v.hash(state), + Value::UInt64(v) => v.hash(state), + Value::Float(v) => v.to_bits().hash(state), + Value::Double(v) => v.to_bits().hash(state), + Value::String(v) => v.hash(state), + Value::Struct { fields } => { + for (name, value) in fields { + name.hash(state); + value.hash(state); + } + } + Value::Enum { value } => value.hash(state), + Value::Union { fields } => { + for (name, value) in fields { + name.hash(state); + value.hash(state); + } + } + Value::Array { elements, dimension_sizes } => { + for value in elements { + value.hash(state); + } + for size in dimension_sizes { + size.hash(state); + } + } + Value::List { elements } => { + for value in elements { + value.hash(state); + } + } + Value::ReferencedValue { name, value, .. } => { + name.hash(state); + value.hash(state); + } + Value::RuntimeValue { name } => { + name.hash(state); + } + } + } +} + +/// Mathematical addition implementation for Value +impl Add for Value { + type Output = Self; + + /// Add two values + /// + /// # Arguments + /// + /// * `rhs` - The right hand side value + /// + /// # Returns + /// + /// * The result of the addition + fn add(self, rhs: Self) -> Self::Output { + match (self, rhs) { + (Value::Int8(lhs), Value::Int8(rhs)) => Value::Int8(lhs + rhs), + (Value::Int16(lhs), Value::Int16(rhs)) => Value::Int16(lhs + rhs), + (Value::Int32(lhs), Value::Int32(rhs)) => Value::Int32(lhs + rhs), + (Value::Int64(lhs), Value::Int64(rhs)) => Value::Int64(lhs + rhs), + (Value::Uint8(lhs), Value::Uint8(rhs)) => Value::Uint8(lhs + rhs), + (Value::Uint16(lhs), Value::Uint16(rhs)) => Value::Uint16(lhs + rhs), + (Value::Uint32(lhs), Value::Uint32(rhs)) => Value::Uint32(lhs + rhs), + (Value::UInt64(lhs), Value::UInt64(rhs)) => Value::UInt64(lhs + rhs), + (Value::Float(lhs), Value::Float(rhs)) => Value::Float(lhs + rhs), + (Value::Double(lhs), Value::Double(rhs)) => Value::Double(lhs + rhs), + (Value::UInt64(lhs), Value::Int64(rhs)) => Value::Int64(lhs as i64 + rhs), + (Value::Int64(lhs), Value::UInt64(rhs)) => Value::Int64(lhs + rhs as i64), + (Value::Int64(lhs), Value::Double(rhs)) => Value::Double(lhs as f64 + rhs), + (Value::Double(lhs), Value::Int64(rhs)) => Value::Double(lhs + rhs as f64), + (Value::UInt64(lhs), Value::Double(rhs)) => Value::Double(lhs as f64 + rhs), + (Value::Double(lhs), Value::UInt64(rhs)) => Value::Double(lhs + rhs as f64), + (lhs, rhs) => { + panic!( + "Bitwise XOR not supported for types: {:?} and {:?}", + lhs, rhs + ); + } + } + } +} + +/// Mathematical subtraction implementation for Value +impl Sub for Value { + type Output = Self; + + /// Subtract two values + /// + /// # Arguments + /// + /// * `rhs` - The right hand side value + /// + /// # Returns + /// + /// * The result of the subtraction + fn sub(self, rhs: Self) -> Self::Output { + match (self, rhs) { + (Value::Int8(lhs), Value::Int8(rhs)) => Value::Int8(lhs - rhs), + (Value::Int16(lhs), Value::Int16(rhs)) => Value::Int16(lhs - rhs), + (Value::Int32(lhs), Value::Int32(rhs)) => Value::Int32(lhs - rhs), + (Value::Int64(lhs), Value::Int64(rhs)) => Value::Int64(lhs - rhs), + (Value::Uint8(lhs), Value::Uint8(rhs)) => Value::Uint8(lhs - rhs), + (Value::Uint16(lhs), Value::Uint16(rhs)) => Value::Uint16(lhs - rhs), + (Value::Uint32(lhs), Value::Uint32(rhs)) => Value::Uint32(lhs - rhs), + (Value::UInt64(lhs), Value::UInt64(rhs)) => Value::UInt64(lhs - rhs), + (Value::Float(lhs), Value::Float(rhs)) => Value::Float(lhs - rhs), + (Value::Double(lhs), Value::Double(rhs)) => Value::Double(lhs - rhs), + (Value::UInt64(lhs), Value::Int64(rhs)) => Value::Int64(lhs as i64 - rhs), + (Value::Int64(lhs), Value::UInt64(rhs)) => Value::Int64(lhs - rhs as i64), + (Value::Int64(lhs), Value::Double(rhs)) => Value::Double(lhs as f64 - rhs), + (Value::Double(lhs), Value::Int64(rhs)) => Value::Double(lhs - rhs as f64), + (Value::UInt64(lhs), Value::Double(rhs)) => Value::Double(lhs as f64 - rhs), + (Value::Double(lhs), Value::UInt64(rhs)) => Value::Double(lhs - rhs as f64), + (lhs, rhs) => { + panic!( + "Bitwise XOR not supported for types: {:?} and {:?}", + lhs, rhs + ); + } + } + } +} + +/// Mathematical multiplication implementation for Value +impl Mul for Value { + type Output = Self; + + /// Multiply two values + /// + /// # Arguments + /// + /// * `rhs` - The right hand side value + /// + /// # Returns + /// + /// * The result of the multiplication + fn mul(self, rhs: Self) -> Self::Output { + match (self, rhs) { + (Value::Int8(lhs), Value::Int8(rhs)) => Value::Int8(lhs * rhs), + (Value::Int16(lhs), Value::Int16(rhs)) => Value::Int16(lhs * rhs), + (Value::Int32(lhs), Value::Int32(rhs)) => Value::Int32(lhs * rhs), + (Value::Int64(lhs), Value::Int64(rhs)) => Value::Int64(lhs * rhs), + (Value::Uint8(lhs), Value::Uint8(rhs)) => Value::Uint8(lhs * rhs), + (Value::Uint16(lhs), Value::Uint16(rhs)) => Value::Uint16(lhs * rhs), + (Value::Uint32(lhs), Value::Uint32(rhs)) => Value::Uint32(lhs * rhs), + (Value::UInt64(lhs), Value::UInt64(rhs)) => Value::UInt64(lhs * rhs), + (Value::Float(lhs), Value::Float(rhs)) => Value::Float(lhs * rhs), + (Value::Double(lhs), Value::Double(rhs)) => Value::Double(lhs * rhs), + (Value::UInt64(lhs), Value::Int64(rhs)) => Value::Int64(lhs as i64 * rhs), + (Value::Int64(lhs), Value::UInt64(rhs)) => Value::Int64(lhs * rhs as i64), + (Value::Int64(lhs), Value::Double(rhs)) => Value::Double(lhs as f64 * rhs), + (Value::Double(lhs), Value::Int64(rhs)) => Value::Double(lhs * rhs as f64), + (Value::UInt64(lhs), Value::Double(rhs)) => Value::Double(lhs as f64 * rhs), + (Value::Double(lhs), Value::UInt64(rhs)) => Value::Double(lhs * rhs as f64), + (lhs, rhs) => { + panic!( + "Bitwise XOR not supported for types: {:?} and {:?}", + lhs, rhs + ); + } + } + } +} + +/// Mathematical division implementation for Value +impl Div for Value { + type Output = Self; + + /// Divide two values + /// + /// # Arguments + /// + /// * `rhs` - The right hand side value + /// + /// # Returns + /// + /// * The result of the division + fn div(self, rhs: Self) -> Self::Output { + match (self, rhs) { + (Value::Int8(lhs), Value::Int8(rhs)) => Value::Int8(lhs / rhs), + (Value::Int16(lhs), Value::Int16(rhs)) => Value::Int16(lhs / rhs), + (Value::Int32(lhs), Value::Int32(rhs)) => Value::Int32(lhs / rhs), + (Value::Int64(lhs), Value::Int64(rhs)) => Value::Int64(lhs / rhs), + (Value::Uint8(lhs), Value::Uint8(rhs)) => Value::Uint8(lhs / rhs), + (Value::Uint16(lhs), Value::Uint16(rhs)) => Value::Uint16(lhs / rhs), + (Value::Uint32(lhs), Value::Uint32(rhs)) => Value::Uint32(lhs / rhs), + (Value::UInt64(lhs), Value::UInt64(rhs)) => Value::UInt64(lhs / rhs), + (Value::Float(lhs), Value::Float(rhs)) => Value::Float(lhs / rhs), + (Value::Double(lhs), Value::Double(rhs)) => Value::Double(lhs / rhs), + (Value::UInt64(lhs), Value::Int64(rhs)) => Value::Int64(lhs as i64 / rhs), + (Value::Int64(lhs), Value::UInt64(rhs)) => Value::Int64(lhs / rhs as i64), + (Value::Int64(lhs), Value::Double(rhs)) => Value::Double(lhs as f64 / rhs), + (Value::Double(lhs), Value::Int64(rhs)) => Value::Double(lhs / rhs as f64), + (Value::UInt64(lhs), Value::Double(rhs)) => Value::Double(lhs as f64 / rhs), + (Value::Double(lhs), Value::UInt64(rhs)) => Value::Double(lhs / rhs as f64), + (lhs, rhs) => { + panic!( + "Bitwise XOR not supported for types: {:?} and {:?}", + lhs, rhs + ); + } + } + } +} + +/// Mathematical remainder implementation for Value +impl Rem for Value { + type Output = Self; + + /// Calculate the remainder of two values + /// + /// # Arguments + /// + /// * `rhs` - The right hand side value + /// + /// # Returns + /// + /// * The result of the remainder + fn rem(self, rhs: Self) -> Self::Output { + match (self, rhs) { + (Value::Int8(lhs), Value::Int8(rhs)) => Value::Int8(lhs % rhs), + (Value::Int16(lhs), Value::Int16(rhs)) => Value::Int16(lhs % rhs), + (Value::Int32(lhs), Value::Int32(rhs)) => Value::Int32(lhs % rhs), + (Value::Int64(lhs), Value::Int64(rhs)) => Value::Int64(lhs % rhs), + (Value::Uint8(lhs), Value::Uint8(rhs)) => Value::Uint8(lhs % rhs), + (Value::Uint16(lhs), Value::Uint16(rhs)) => Value::Uint16(lhs % rhs), + (Value::Uint32(lhs), Value::Uint32(rhs)) => Value::Uint32(lhs % rhs), + (Value::UInt64(lhs), Value::UInt64(rhs)) => Value::UInt64(lhs % rhs), + (Value::Float(lhs), Value::Float(rhs)) => Value::Float(lhs % rhs), + (Value::Double(lhs), Value::Double(rhs)) => Value::Double(lhs % rhs), + (Value::UInt64(lhs), Value::Int64(rhs)) => Value::Int64(lhs as i64 % rhs), + (Value::Int64(lhs), Value::UInt64(rhs)) => Value::Int64(lhs % rhs as i64), + (Value::Int64(lhs), Value::Double(rhs)) => Value::Double(lhs as f64 % rhs), + (Value::Double(lhs), Value::Int64(rhs)) => Value::Double(lhs % rhs as f64), + (Value::UInt64(lhs), Value::Double(rhs)) => Value::Double(lhs as f64 % rhs), + (Value::Double(lhs), Value::UInt64(rhs)) => Value::Double(lhs % rhs as f64), + (lhs, rhs) => { + panic!( + "Bitwise XOR not supported for types: {:?} and {:?}", + lhs, rhs + ); + } + } + } +} + +/// Bitwise OR implementation for Value +impl BitOr for Value { + type Output = Self; + + /// Perform a bitwise OR operation on two values + /// + /// # Arguments + /// + /// * `rhs` - The right hand side value + /// + /// # Returns + /// + /// * The result of the bitwise OR operation + fn bitor(self, rhs: Self) -> Self::Output { + match (self, rhs) { + (Value::Int8(lhs), Value::Int8(rhs)) => Value::Int8(lhs | rhs), + (Value::Int16(lhs), Value::Int16(rhs)) => Value::Int16(lhs | rhs), + (Value::Int32(lhs), Value::Int32(rhs)) => Value::Int32(lhs | rhs), + (Value::Int64(lhs), Value::Int64(rhs)) => Value::Int64(lhs | rhs), + (Value::Uint8(lhs), Value::Uint8(rhs)) => Value::Uint8(lhs | rhs), + (Value::Uint16(lhs), Value::Uint16(rhs)) => Value::Uint16(lhs | rhs), + (Value::Uint32(lhs), Value::Uint32(rhs)) => Value::Uint32(lhs | rhs), + (Value::UInt64(lhs), Value::UInt64(rhs)) => Value::UInt64(lhs | rhs), + (Value::UInt64(lhs), Value::Int64(rhs)) => Value::Int64(lhs as i64 | rhs), + (Value::Int64(lhs), Value::UInt64(rhs)) => Value::Int64(lhs | rhs as i64), + (Value::Int64(lhs), Value::Double(rhs)) => Value::Int64(lhs | rhs as i64), + (Value::Double(lhs), Value::Int64(rhs)) => Value::Int64(lhs as i64 | rhs), + (Value::UInt64(lhs), Value::Double(rhs)) => Value::UInt64(lhs | rhs as u64), + (Value::Double(lhs), Value::UInt64(rhs)) => Value::UInt64(lhs as u64 | rhs), + (lhs, rhs) => { + panic!( + "Bitwise XOR not supported for types: {:?} and {:?}", + lhs, rhs + ); + } + } + } +} + +/// Bitwise AND implementation for Value +impl BitAnd for Value { + type Output = Self; + + /// Perform a bitwise AND operation on two values + /// + /// # Arguments + /// + /// * `rhs` - The right hand side value + /// + /// # Returns + /// + /// * The result of the bitwise AND operation + fn bitand(self, rhs: Self) -> Self::Output { + match (self, rhs) { + (Value::Int8(lhs), Value::Int8(rhs)) => Value::Int8(lhs & rhs), + (Value::Int16(lhs), Value::Int16(rhs)) => Value::Int16(lhs & rhs), + (Value::Int32(lhs), Value::Int32(rhs)) => Value::Int32(lhs & rhs), + (Value::Int64(lhs), Value::Int64(rhs)) => Value::Int64(lhs & rhs), + (Value::Uint8(lhs), Value::Uint8(rhs)) => Value::Uint8(lhs & rhs), + (Value::Uint16(lhs), Value::Uint16(rhs)) => Value::Uint16(lhs & rhs), + (Value::Uint32(lhs), Value::Uint32(rhs)) => Value::Uint32(lhs & rhs), + (Value::UInt64(lhs), Value::UInt64(rhs)) => Value::UInt64(lhs & rhs), + (Value::UInt64(lhs), Value::Int64(rhs)) => Value::UInt64(lhs & rhs as u64), + (Value::Int64(lhs), Value::UInt64(rhs)) => Value::UInt64(lhs as u64 & rhs), + (Value::Int64(lhs), Value::Double(rhs)) => Value::Int64(lhs as i64 & rhs as i64), + (Value::Double(lhs), Value::Int64(rhs)) => Value::Int64(lhs as i64 & rhs as i64), + (Value::UInt64(lhs), Value::Double(rhs)) => Value::UInt64(lhs & rhs as u64), + (Value::Double(lhs), Value::UInt64(rhs)) => Value::UInt64(lhs as u64 & rhs), + (lhs, rhs) => { + panic!( + "Bitwise XOR not supported for types: {:?} and {:?}", + lhs, rhs + ); + } + } + } +} + +/// Bitwise XOR implementation for Value +impl BitXor for Value { + type Output = Self; + + /// Perform a bitwise XOR operation on two values + /// + /// # Arguments + /// + /// * `rhs` - The right hand side value + /// + /// # Returns + /// + /// * The result of the bitwise XOR operation + fn bitxor(self, rhs: Self) -> Self::Output { + match (self, rhs) { + (Value::Int8(lhs), Value::Int8(rhs)) => Value::Int8(lhs ^ rhs), + (Value::Int16(lhs), Value::Int16(rhs)) => Value::Int16(lhs ^ rhs), + (Value::Int32(lhs), Value::Int32(rhs)) => Value::Int32(lhs ^ rhs), + (Value::Int64(lhs), Value::Int64(rhs)) => Value::Int64(lhs ^ rhs), + (Value::Uint8(lhs), Value::Uint8(rhs)) => Value::Uint8(lhs ^ rhs), + (Value::Uint16(lhs), Value::Uint16(rhs)) => Value::Uint16(lhs ^ rhs), + (Value::Uint32(lhs), Value::Uint32(rhs)) => Value::Uint32(lhs ^ rhs), + (Value::UInt64(lhs), Value::UInt64(rhs)) => Value::UInt64(lhs ^ rhs), + (Value::UInt64(lhs), Value::Int64(rhs)) => Value::UInt64(lhs ^ rhs as u64), + (Value::Int64(lhs), Value::UInt64(rhs)) => Value::UInt64(lhs as u64 ^ rhs), + (Value::Int64(lhs), Value::Double(rhs)) => Value::Int64(lhs ^ rhs as i64), + (Value::Double(lhs), Value::Int64(rhs)) => Value::Int64(lhs as i64 ^ rhs), + (Value::UInt64(lhs), Value::Double(rhs)) => Value::UInt64(lhs ^ rhs as u64), + (Value::Double(lhs), Value::UInt64(rhs)) => Value::UInt64(lhs as u64 ^ rhs), + (lhs, rhs) => { + panic!( + "Bitwise XOR not supported for types: {:?} and {:?}", + lhs, rhs + ); + } + } + } +} + +/// Bitwise shift left implementation for Value +impl Shl for Value { + type Output = Self; + + /// Perform a bitwise shift left operation on two values + /// + /// # Arguments + /// + /// * `rhs` - The right hand side value + /// + /// # Returns + /// + /// * The result of the bitwise shift left operation + fn shl(self, rhs: Self) -> Self::Output { + match (self, rhs) { + (Value::Int8(lhs), Value::Int8(rhs)) => Value::Int8(lhs << rhs), + (Value::Int16(lhs), Value::Int16(rhs)) => Value::Int16(lhs << rhs), + (Value::Int32(lhs), Value::Int32(rhs)) => Value::Int32(lhs << rhs), + (Value::Int64(lhs), Value::Int64(rhs)) => Value::Int64(lhs << rhs), + (Value::Uint8(lhs), Value::Uint8(rhs)) => Value::Uint8(lhs << rhs), + (Value::Uint16(lhs), Value::Uint16(rhs)) => Value::Uint16(lhs << rhs), + (Value::Uint32(lhs), Value::Uint32(rhs)) => Value::Uint32(lhs << rhs), + (Value::UInt64(lhs), Value::UInt64(rhs)) => Value::UInt64(lhs << rhs), + (Value::UInt64(lhs), Value::Int64(rhs)) => Value::UInt64(lhs << rhs as u64), + (Value::Int64(lhs), Value::UInt64(rhs)) => Value::UInt64((lhs as u64) << rhs), + (Value::Int64(lhs), Value::Double(rhs)) => Value::Int64(lhs << (rhs as i64)), + (Value::Double(lhs), Value::Int64(rhs)) => Value::Int64((lhs as i64) << rhs), + (Value::UInt64(lhs), Value::Double(rhs)) => Value::UInt64(lhs << rhs as u64), + (Value::Double(lhs), Value::UInt64(rhs)) => Value::UInt64((lhs as u64) << rhs), + (lhs, rhs) => { + panic!( + "Bitwise XOR not supported for types: {:?} and {:?}", + lhs, rhs + ); + } + } + } +} + +/// Bitwise shift right implementation for Value +impl Shr for Value { + type Output = Self; + + /// Perform a bitwise shift right operation on two values + /// + /// # Arguments + /// + /// * `rhs` - The right hand side value + /// + /// # Returns + /// + /// * The result of the bitwise shift right operation + fn shr(self, rhs: Self) -> Self::Output { + match (self, rhs) { + (Value::Int8(lhs), Value::Int8(rhs)) => Value::Int8(lhs >> rhs), + (Value::Int16(lhs), Value::Int16(rhs)) => Value::Int16(lhs >> rhs), + (Value::Int32(lhs), Value::Int32(rhs)) => Value::Int32(lhs >> rhs), + (Value::Int64(lhs), Value::Int64(rhs)) => Value::Int64(lhs >> rhs), + (Value::Uint8(lhs), Value::Uint8(rhs)) => Value::Uint8(lhs >> rhs), + (Value::Uint16(lhs), Value::Uint16(rhs)) => Value::Uint16(lhs >> rhs), + (Value::Uint32(lhs), Value::Uint32(rhs)) => Value::Uint32(lhs >> rhs), + (Value::UInt64(lhs), Value::UInt64(rhs)) => Value::UInt64(lhs >> rhs), + (Value::UInt64(lhs), Value::Int64(rhs)) => Value::UInt64(lhs >> rhs as u64), + (Value::Int64(lhs), Value::UInt64(rhs)) => Value::UInt64((lhs as u64) >> rhs), + (Value::Int64(lhs), Value::Double(rhs)) => Value::Int64(lhs >> (rhs as i64)), + (Value::Double(lhs), Value::Int64(rhs)) => Value::Int64((lhs as i64) >> rhs), + (Value::UInt64(lhs), Value::Double(rhs)) => Value::UInt64(lhs >> rhs as u64), + (Value::Double(lhs), Value::UInt64(rhs)) => Value::UInt64((lhs as u64) >> rhs), + (lhs, rhs) => { + panic!( + "Bitwise XOR not supported for types: {:?} and {:?}", + lhs, rhs + ); + } + } + } +} + +/// Negation implementation for Value +impl Neg for Value { + type Output = Self; + + /// Negate a value + /// + /// # Returns + /// + /// * The negated value + fn neg(self) -> Self::Output { + match self { + Value::Int8(v) => Value::Int8(-v), + Value::Int16(v) => Value::Int16(-v), + Value::Int32(v) => Value::Int32(-v), + Value::Int64(v) => Value::Int64(-v), + Value::Uint8(v) => Value::Int8(-(v as i8)), + Value::Uint16(v) => Value::Int16(-(v as i16)), + Value::Uint32(v) => Value::Int32(-(v as i32)), + Value::UInt64(v) => Value::Int64(-(v as i64)), + Value::Float(v) => Value::Float(-v), + Value::Double(v) => Value::Double(-v), + _ => { + panic!("Negation not supported for type: {:?}", self) + } + } + } +} + + +/// Logical NOT implementation for Value +impl Not for Value { + type Output = Self; + + /// Perform a logical NOT operation on a value + /// + /// # Returns + /// + /// * The result of the logical NOT operation + fn not(self) -> Self::Output { + match self { + Value::Bool(v) => Value::Bool(!v), + Value::Int8(v) => Value::Int8(!v), + Value::Int16(v) => Value::Int16(!v), + Value::Int32(v) => Value::Int32(!v), + Value::Int64(v) => Value::Int64(!v), + Value::Uint8(v) => Value::Int8(!(v as i8)), + Value::Uint16(v) => Value::Int16(!(v as i16)), + Value::Uint32(v) => Value::Int32(!(v as i32)), + Value::UInt64(v) => Value::Int64(!(v as i64)), + _ => { + panic!("Negation not supported for type: {:?}", self) + } + } + } +} + +/// Value implementation +impl Value { + + /// Checks if the value is a primitive + /// + /// # Returns + /// + /// * True if the value is a primitive, else false + pub fn is_referenced(&self) -> bool { + match self { + Value::ReferencedValue { .. } => true, + _ => false, + } + } + + /// Converts the value to 8-bit integer + /// + /// # Returns + /// + /// * The 8-bit integer value + pub fn to_i8(&self) -> i8 { + match self { + Value::Int8(v) => *v, + Value::Int16(v) => *v as i8, + Value::Int32(v) => *v as i8, + Value::Int64(v) => *v as i8, + Value::Uint8(v) => *v as i8, + Value::Uint16(v) => *v as i8, + Value::Uint32(v) => *v as i8, + Value::UInt64(v) => *v as i8, + Value::Float(v) => *v as i8, + Value::Double(v) => *v as i8, + _ => { + panic!("Cannot convert value to i8: {:?}", self) + } + } + } + + /// Converts the value to 16-bit integer + /// + /// # Returns + /// + /// * The 16-bit integer value + pub fn to_i16(&self) -> i16 { + match self { + Value::Int8(v) => *v as i16, + Value::Int16(v) => *v, + Value::Int32(v) => *v as i16, + Value::Int64(v) => *v as i16, + Value::Uint8(v) => *v as i16, + Value::Uint16(v) => *v as i16, + Value::Uint32(v) => *v as i16, + Value::UInt64(v) => *v as i16, + Value::Float(v) => *v as i16, + Value::Double(v) => *v as i16, + _ => { + panic!("Cannot convert value to i16: {:?}", self) + } + } + } + + /// Converts the value to 32-bit integer + /// + /// # Returns + /// + /// * The 32-bit integer value + pub fn to_i32(&self) -> i32 { + match self { + Value::Int8(v) => *v as i32, + Value::Int16(v) => *v as i32, + Value::Int32(v) => *v, + Value::Int64(v) => *v as i32, + Value::Uint8(v) => *v as i32, + Value::Uint16(v) => *v as i32, + Value::Uint32(v) => *v as i32, + Value::UInt64(v) => *v as i32, + Value::Float(v) => *v as i32, + Value::Double(v) => *v as i32, + _ => { + panic!("Cannot convert value to i32: {:?}", self) + } + } + } + + /// Converts the value to 64-bit integer + /// + /// # Returns + /// + /// * The 64-bit integer value + pub fn to_i64(&self) -> i64 { + match self { + Value::Int8(v) => *v as i64, + Value::Int16(v) => *v as i64, + Value::Int32(v) => *v as i64, + Value::Int64(v) => *v, + Value::Uint8(v) => *v as i64, + Value::Uint16(v) => *v as i64, + Value::Uint32(v) => *v as i64, + Value::UInt64(v) => *v as i64, + Value::Float(v) => *v as i64, + Value::Double(v) => *v as i64, + _ => { + panic!("Cannot convert value to i64: {:?}", self) + } + } + } + + /// Converts the value to 8-bit unsigned integer + /// + /// # Returns + /// + /// * The 8-bit unsigned integer value + pub fn to_u8(&self) -> u8 { + match self { + Value::Int8(v) => *v as u8, + Value::Int16(v) => *v as u8, + Value::Int32(v) => *v as u8, + Value::Int64(v) => *v as u8, + Value::Uint8(v) => *v, + Value::Uint16(v) => *v as u8, + Value::Uint32(v) => *v as u8, + Value::UInt64(v) => *v as u8, + Value::Float(v) => *v as u8, + Value::Double(v) => *v as u8, + _ => { + panic!("Cannot convert value to u8: {:?}", self) + } + } + } + + /// Converts the value to 16-bit unsigned integer + /// + /// # Returns + /// + /// * The 16-bit unsigned integer value + pub fn to_u16(&self) -> u16 { + match self { + Value::Int8(v) => *v as u16, + Value::Int16(v) => *v as u16, + Value::Int32(v) => *v as u16, + Value::Int64(v) => *v as u16, + Value::Uint8(v) => *v as u16, + Value::Uint16(v) => *v, + Value::Uint32(v) => *v as u16, + Value::UInt64(v) => *v as u16, + Value::Float(v) => *v as u16, + Value::Double(v) => *v as u16, + _ => { + panic!("Cannot convert value to u16: {:?}", self) + } + } + } + + /// Converts the value to 32-bit unsigned integer + /// + /// # Returns + /// + /// * The 32-bit unsigned integer value + pub fn to_u32(&self) -> u32 { + match self { + Value::Int8(v) => *v as u32, + Value::Int16(v) => *v as u32, + Value::Int32(v) => *v as u32, + Value::Int64(v) => *v as u32, + Value::Uint8(v) => *v as u32, + Value::Uint16(v) => *v as u32, + Value::Uint32(v) => *v, + Value::UInt64(v) => *v as u32, + Value::Float(v) => *v as u32, + Value::Double(v) => *v as u32, + _ => { + panic!("Cannot convert value to u32: {:?}", self) + } + } + } + + /// Converts the value to 64-bit unsigned integer + /// + /// # Returns + /// + /// * The 64-bit unsigned integer value + pub fn to_u64(&self) -> u64 { + match self { + Value::Int8(v) => *v as u64, + Value::Int16(v) => *v as u64, + Value::Int32(v) => *v as u64, + Value::Int64(v) => *v as u64, + Value::Uint8(v) => *v as u64, + Value::Uint16(v) => *v as u64, + Value::Uint32(v) => *v as u64, + Value::UInt64(v) => *v, + Value::Float(v) => *v as u64, + Value::Double(v) => *v as u64, + _ => { + panic!("Cannot convert value to u64: {:?}", self) + } + } + } + + /// Converts the value to 32-bit floating point number + /// + /// # Returns + /// + /// * The 32-bit floating point number value + pub fn to_f32(&self) -> f32 { + match self { + Value::Int8(v) => *v as f32, + Value::Int16(v) => *v as f32, + Value::Int32(v) => *v as f32, + Value::Int64(v) => *v as f32, + Value::Uint8(v) => *v as f32, + Value::Uint16(v) => *v as f32, + Value::Uint32(v) => *v as f32, + Value::UInt64(v) => *v as f32, + Value::Float(v) => *v, + Value::Double(v) => *v as f32, + _ => { + panic!("Cannot convert value to f32: {:?}", self) + } + } + } + + /// Converts the value to 64-bit floating point number + /// + /// # Returns + /// + /// * The 64-bit floating point number value + pub fn to_f64(self) -> f64 { + match self { + Value::Int8(v) => v as f64, + Value::Int16(v) => v as f64, + Value::Int32(v) => v as f64, + Value::Int64(v) => v as f64, + Value::Uint8(v) => v as f64, + Value::Uint16(v) => v as f64, + Value::Uint32(v) => v as f64, + Value::UInt64(v) => v as f64, + Value::Float(v) => v as f64, + Value::Double(v) => v, + _ => { + panic!("Cannot convert value to f64: {:?}", self) + } + } + } + + /// Checks if the value is a numeric type + /// + /// # Returns + /// + /// * True if the value is numeric, else false + pub fn is_numeric(&self) -> bool { + matches!( + self, + Value::Int8(_) + | Value::Int16(_) + | Value::Int32(_) + | Value::Int64(_) + | Value::Uint8(_) + | Value::Uint16(_) + | Value::Uint32(_) + | Value::UInt64(_) + | Value::Float(_) + | Value::Double(_) + ) + } + + /// Checks if the value is an integer type + /// + /// # Returns + /// + /// * True if the value is an integer, else false + pub fn is_integer(&self) -> bool { + matches!( + self, + Value::Int8(_) + | Value::Int16(_) + | Value::Int32(_) + | Value::Int64(_) + | Value::Uint8(_) + | Value::Uint16(_) + | Value::Uint32(_) + | Value::UInt64(_) + ) + } + + /// Determines the type of the current Value + /// + /// # Returns + /// + /// * The type of the value + pub fn determine_type(&self) -> Type { + match self { + Value::Bool(_) => Type::Bool, + Value::Int8(_) => Type::Int8, + Value::Int16(_) => Type::Int16, + Value::Int32(_) => Type::Int32, + Value::Int64(_) => Type::Int64, + Value::Uint8(_) => Type::UInt8, + Value::Uint16(_) => Type::UInt16, + Value::Uint32(_) => Type::UInt32, + Value::UInt64(_) => Type::UInt64, + Value::Float(_) => Type::Float, + Value::Double(_) => Type::Double, + Value::String(_) => Type::String, + Value::ReferencedValue { value: inner, .. } => inner.determine_type(), + _ => { + panic!("Cannot determine type for value: {:?}", self) + } + } + } +} diff --git a/erpcgen_rust/templates/python/py_client.askama b/erpcgen_rust/templates/python/py_client.askama new file mode 100644 index 00000000..0cb858a0 --- /dev/null +++ b/erpcgen_rust/templates/python/py_client.askama @@ -0,0 +1,82 @@ +{% if !preceding_comment.is_empty() -%} +{{ preceding_comment -}} +{% endif ~%} + +# +# Generated by erpcgen {{ crate::ERPC_VERSION_STR }} on {{ date }}. +# +# AUTOGENERATED - DO NOT EDIT +# + +import erpc +from . import common, interface +{% if groups.len() > 1 ~%} +# import callbacks declaration from other groups +{% for name in groups -%} +{% if name.clone() != group ~%} +from ..{% if !output.is_empty() %}{{ output }}_{% endif %}{{ name }} import interface as interface_{{ name }} +{%- endif -%} +{% endfor -%} +{% endif -%} + +{% if !callbacks.is_empty() -%} +{% for callback in callbacks ~%} +_{{ callback.name }} = [ {% for function in callback.functions %}{{ function }}{% if !loop.last %}, {% endif %}{% endfor %} ] +{% endfor -%} +{% endif -%} +{% for interface in interfaces %} +# Client for {{ interface.name }} +class {{ interface.name }}Client(interface.I{{ interface.name }}): + def __init__(self, manager): + super({{ interface.name }}Client, self).__init__() + self._clientManager = manager +{% for function in interface.functions %} + def {{ self::format_function_prototype_py(function) }}: +{%- for parameter in function.parameters -%} +{% if parameter.is_out() && parameter.is_length_for.is_none() && parameter.is_discriminator.is_none() %} + assert type({{ parameter.name }}) is erpc.Reference # {{ parameter.direction }} parameter must be a Reference object +{%- endif -%} +{% endfor %} + # Build remote function invocation message. + request = self._clientManager.create_request({% if function.is_oneway %}isOneway=True{% endif %}) + codec = request.codec + codec.start_write_message(erpc.codec.MessageInfo( + type=erpc.codec.MessageType.{% if function.is_oneway %}kOnewayMessage{% else %}kInvocationMessage{% endif %}, + service=self.SERVICE_ID, + request=self.{{ function.name|upper }}_ID, + sequence=request.sequence)) +{% for parameter in function.parameters -%} +{% if parameter.is_in() && parameter.is_length_for.is_none() && parameter.is_discriminator.is_none() -%} +{% let indent = self::optional_indent(parameter.is_nullable) -%} +{% let formatted_name = self::format_param_name(parameter.name, parameter.direction) -%} +{% if parameter.is_nullable %} + codec.write_null_flag({{ formatted_name }} is None) + if {{ formatted_name }} is not None: +{%- else %} + if {{ formatted_name }} is None: + raise ValueError("{{ formatted_name }} is None") +{%- endif %} + {{ indent }}{{ self::encode_type(parameter.type_.clone(), formatted_name, "common.", parameter.discriminator_name.clone(), "codec", indent, 0)|indent(8) -}} +{% endif -%} +{% endfor %} + + # Send request {% if !function.is_oneway %} and process reply{% endif %}. + self._clientManager.perform_request(request) +{%- if !function.is_oneway -%} +{% for parameter in function.parameters -%} +{% if parameter.is_out() && parameter.is_length_for.is_none() && parameter.is_discriminator.is_none() -%} +{% let indent = self::optional_indent(parameter.is_nullable) -%} +{% let formatted_name = self::format_param_name(parameter.name, parameter.direction) -%} +{% if parameter.is_nullable %} + if {{ formatted_name }} != None: +{%- endif %} + {{ indent }}{{ self::decode_type(parameter.type_.clone(), formatted_name, "common.", parameter.discriminator_name.clone(), "codec", indent, 0)|indent(8) -}} +{% endif -%} +{% endfor -%} +{% endif -%} +{% if function.return_type.is_some() %} + {{ self::decode_type(function.return_type.clone().expect("Return type must be set"), "_result", "common.", Some(String::from("").clone()), "codec", "", 0)|indent(8) }} + return _result +{%- endif %} +{% endfor -%} +{% endfor -%} \ No newline at end of file diff --git a/erpcgen_rust/templates/python/py_common.askama b/erpcgen_rust/templates/python/py_common.askama new file mode 100644 index 00000000..8a3a4c49 --- /dev/null +++ b/erpcgen_rust/templates/python/py_common.askama @@ -0,0 +1,54 @@ +{% if !preceding_comment.is_empty() -%} +{{ preceding_comment -}} +{% endif ~%} + +# +# Generated by erpcgen {{+ crate::ERPC_VERSION_STR +}} on {{+ date +}}. +# +# AUTOGENERATED - DO NOT EDIT +# + +{%~ for inc in includes -%} +import {{ inc -}} +{% endfor -%} + +{%- if !consts.is_empty() %} + +# Constant variable declarations +{%- for const_ in consts %} +{% include "py_const.askama" ~%} + +{%~ endfor -%} +{% endif -%} + +{%- if !enums.is_empty() %} +# Enumerators data types declarations +{% for enum_ in enums -%} +{% include "py_enum.askama" ~%} + +{%~ endfor -%} +{% endif -%} + +{%- if !structs.is_empty() -%} +# Structures data types declarations +{% for struct_ in structs -%} +{% include "py_struct.askama" ~%} + +{% endfor -%} +{% endif -%} + +{%- if !unions.is_empty() -%} +# Unions data types declarations +{% for union_ in unions -%} +{% include "py_union.askama" ~%} + +{% endfor -%} +{% endif -%} +{# +{%- if !unions.is_empty() -%} +# Data types declarations +{% for typedef_ in typedefs -%} +{% include "py_typedef.askama" ~%} + +{% endfor -%} +{% endif -%} #} \ No newline at end of file diff --git a/erpcgen_rust/templates/python/py_const.askama b/erpcgen_rust/templates/python/py_const.askama new file mode 100644 index 00000000..8a6f6f65 --- /dev/null +++ b/erpcgen_rust/templates/python/py_const.askama @@ -0,0 +1,4 @@ +{% if !const_.preceding_comment.is_empty() -%} +{{ const_.preceding_comment -}} +{% endif -%} +{{ const_.name }} = {{ const_.value }}{% if !const_.trailing_comment.is_empty() %}{{ const_.trailing_comment }}{% endif -%} \ No newline at end of file diff --git a/erpcgen_rust/templates/python/py_en_union_read.askama b/erpcgen_rust/templates/python/py_en_union_read.askama new file mode 100644 index 00000000..5dfb8ab0 --- /dev/null +++ b/erpcgen_rust/templates/python/py_en_union_read.askama @@ -0,0 +1,49 @@ +{% let self_e_name = "self.".to_string() + encapsulated.name.as_str() %} + self.{{ encapsulated.discriminator_name }} = codec.start_read_union() + {{ self_e_name }} = {{ self_e_name }}_union() +{%- if encapsulated.has_only_default_case() -%} {# case union has 1 or 0 cases#} +{%- for case in encapsulated.cases -%} +{% for member in case.case_members -%} +{% let self_m_name = self_e_name.clone() + "." + member.name.as_str() -%} +{% let indent = self::optional_indent(member.is_nullable) -%} +{% if member.is_nullable %} + if codec.read_null_flag(): + {{ self_m_name }} = None + else: +{%- endif -%} +{% if member.type_.is_union() %} + {{ indent }}{{ self_e_name }}.{{member.name}}, self.{{ member.discriminator_name.clone().unwrap() }} = {{ member.type_.clone().get_name() }}()._read(codec) +{%- else %} + {{ self::decode_type(member.type_.clone(), (self_e_name.clone() + "." + member.name.as_str()).as_str(), "", member.discriminator_name.clone(), "codec", indent, 0) }} +{%- endif -%} +{% endfor -%} +{% endfor -%} +{%- else -%} {# case union has 2 or more cases #} +{%- for case in encapsulated.cases -%} +{% if loop.first %} + if {{ self::format_case_values_py(case.case_values, encapsulated.discriminator_name) }}: +{%- else if loop.last %} + else: +{%- else %} + elif {{ self::format_case_values_py(case.case_values, encapsulated.discriminator_name) -}}: +{%- endif -%} +{% for member in case.case_members -%} +{% let self_m_name = self_e_name.clone() + "." + member.name.as_str() -%} +{% let indent = self::optional_indent(member.is_nullable) -%} +{% if member.is_nullable %} + if codec.read_null_flag(): + {{ self_m_name }} = None + else: +{%- endif -%} +{% if member.type_.is_union() %} + {{ indent }}{{ self_e_name }}.{{member.name}}, self.{{ member.discriminator_name.clone().unwrap() }} = {{ member.type_.clone().get_name() }}()._read(codec) +{%- else %} + {{ self::decode_type(member.type_.clone(), (self_e_name.clone() + "." + member.name.as_str()).as_str(), "", member.discriminator_name.clone(), "codec", indent, 0) }} +{%- endif -%} +{% endfor -%} +{% endfor -%} +{% endif %} {# case union has 2 or more cases end #} +{%- if !encapsulated.has_default_case() %} + else: + raise ValueError("invalid union discriminator value %s" % repr(self.{{ encapsulated.discriminator_name }})) +{%- endif %} \ No newline at end of file diff --git a/erpcgen_rust/templates/python/py_en_union_write.askama b/erpcgen_rust/templates/python/py_en_union_write.askama new file mode 100644 index 00000000..1ca9ffc5 --- /dev/null +++ b/erpcgen_rust/templates/python/py_en_union_write.askama @@ -0,0 +1,53 @@ +{% let self_e_name = "self.".to_string() + encapsulated.name.as_str() %} + self.{{ encapsulated.discriminator_name }} = codec.start_read_union() + {{ self_e_name }} = {{ self_e_name }}_union() +{%- if encapsulated.has_only_default_case() -%} {# case union has 1 or 0 cases#} +{%- for case in encapsulated.cases -%} +{% for member in case.case_members -%} +{% let self_m_name = self_e_name.clone() + "." + member.name.as_str() -%} +{% let indent = self::optional_indent(member.is_nullable) -%} +{%- if member.is_nullable %} + codec.write_null_flag({{ self_m_name }} is None) + if {{ self_m_name }} is not None: +{%- else %} + if {{ self_m_name }} is None: + raise ValueError("{{ member.name }} is None") +{%- endif -%} +{% if member.type_.is_union() %} + {{ indent }}{{ member.type_.clone().get_name() }}()._write(codec, self.{{ member.discriminator_name.clone().unwrap() }}) +{%- else %} + {{ self::encode_type(member.type_.clone(), self_m_name, "", None, "codec", indent, 0) -}} +{%- endif -%} +{% endfor -%} +{% endfor -%} +{%- else -%} {# case union has 2 or more cases #} +{%- for case in encapsulated.cases -%} +{% if loop.first %} + if {{ self::format_case_values_py(case.case_values, encapsulated.discriminator_name) }}: +{%- else if loop.last %} + else: +{%- else %} + elif {{ self::format_case_values_py(case.case_values, encapsulated.discriminator_name) -}}: +{%- endif -%} +{% for member in case.case_members -%} +{% let self_m_name = self_e_name.clone() + "." + member.name.as_str() -%} +{% let indent = self::optional_indent(member.is_nullable) -%} +{%- if member.is_nullable %} + codec.write_null_flag({{ self_m_name }} is None) + if {{ self_m_name }} is not None: +{%- else -%} + if {{ self_m_name }} is None: + raise ValueError("{{ member.name }} is None") +{%- endif -%} +{% if member.type_.is_union() %} + {{ indent }}{{ member.type_.clone().get_name() }}()._write(codec, self.{{ member.discriminator_name.clone().unwrap() }}) +{%- else %} + {{ self::encode_type(member.type_.clone(), self_m_name, "", None, "codec", indent, 0) -}} +{%- endif -%} +{% endfor -%} +{% endfor -%} +{% endif %} {# case union has 2 or more cases end #} +{%- if !encapsulated.has_default_case() %} + else: + raise ValueError("invalid union discriminator value %s" % repr(self.{{ encapsulated.discriminator_name }})) +{%- endif %} \ No newline at end of file diff --git a/erpcgen_rust/templates/python/py_encapsulated.askama b/erpcgen_rust/templates/python/py_encapsulated.askama new file mode 100644 index 00000000..3ac4159b --- /dev/null +++ b/erpcgen_rust/templates/python/py_encapsulated.askama @@ -0,0 +1,12 @@ +{% if !encapsulated.preceding_comment.is_empty() -%} +{{ encapsulated.preceding_comment -}} +{% endif %} + class {{~ encapsulated.name }}_union(object): +{%- for c in encapsulated.cases -%} +{% if !c.case_members.is_empty() %} + # {% if c.case_values.is_empty() %}default case{% else %}case {{ self::format_values_py(c.case_values) }}{% endif %} +{%- for cm in c.case_members %} + {{ cm.name }} = None # {{+ self::format_type_name_py(cm.type_.clone()) -}} +{% endfor -%} +{% endif -%} +{% endfor -%} \ No newline at end of file diff --git a/erpcgen_rust/templates/python/py_enum.askama b/erpcgen_rust/templates/python/py_enum.askama new file mode 100644 index 00000000..8dc23bff --- /dev/null +++ b/erpcgen_rust/templates/python/py_enum.askama @@ -0,0 +1,15 @@ +{% if !enum_.preceding_comment.is_empty() -%} +{{ enum_.preceding_comment -}} +{% endif -%} +{% if enum_.name.is_some() -%} +class {{ enum_.name.clone().unwrap() }}: +{% endif -%} +{% for member in enum_.members -%} +{% if !member.preceding_comment.is_empty() -%} + {{ member.preceding_comment -}} +{% endif -%} +{% if enum_.name.is_some() +%} {%+ endif %}{{ member.name +}} = {{+ member.value }} {% if !member.trailing_comment.is_empty() %} {{ member.trailing_comment -}}{% endif %} +{% endfor -%} +{%- if !enum_.trailing_comment.is_empty() -%} +{{ enum_.trailing_comment -}} +{% endif -%} \ No newline at end of file diff --git a/erpcgen_rust/templates/python/py_init.askama b/erpcgen_rust/templates/python/py_init.askama new file mode 100644 index 00000000..3b73d06f --- /dev/null +++ b/erpcgen_rust/templates/python/py_init.askama @@ -0,0 +1,22 @@ +{% if !preceding_comment.is_empty() %} +{{ preceding_comment }} +{% endif %} +# +# Generated by erpcgen {{ crate::ERPC_VERSION_STR }} on {{ date }}. +# +# AUTOGENERATED - DO NOT EDIT +# + +try: + from erpc import erpc_version + version = erpc_version.ERPC_VERSION +except ImportError: + version = "unknown" +if version != "{{ crate::ERPC_VERSION_STR }}": + raise ValueError("The generated shim code version ({{ crate::ERPC_VERSION_STR }}) is different to the rest of eRPC code (%s). \ +Install newer version by running \"python setup.py install\" in folder erpc/erpc_python/." % repr(version)) + +from . import common +from . import client +from . import server +from . import interface \ No newline at end of file diff --git a/erpcgen_rust/templates/python/py_init_global.askama b/erpcgen_rust/templates/python/py_init_global.askama new file mode 100644 index 00000000..c8c12825 --- /dev/null +++ b/erpcgen_rust/templates/python/py_init_global.askama @@ -0,0 +1,13 @@ +{% if !preceding_comment.is_empty() -%} +{{ preceding_comment -}} +{% endif ~%} + +# +# Generated by erpcgen {{ crate::ERPC_VERSION_STR }} on {{ date }}. +# +# AUTOGENERATED - DO NOT EDIT +# + +{% if crc16.is_some() -%} +ERPC_GENERATED_SHIM_CODE_CRC = {{ crc16.unwrap() }} +{% endif %} \ No newline at end of file diff --git a/erpcgen_rust/templates/python/py_interface.askama b/erpcgen_rust/templates/python/py_interface.askama new file mode 100644 index 00000000..eaf427eb --- /dev/null +++ b/erpcgen_rust/templates/python/py_interface.askama @@ -0,0 +1,31 @@ +{% if !preceding_comment.is_empty() -%} +{{ preceding_comment -}} +{% endif ~%} + +# +# Generated by erpcgen {{ crate::ERPC_VERSION_STR }} on {{ date }}. +# +# AUTOGENERATED - DO NOT EDIT +# + +{%~for interface in interfaces -%} +{% if !interface.preceding_comment.is_empty() -%} +{{ interface.preceding_comment -}} +{% else ~%} +# Abstract base class for {{ interface.name -}} +{% endif ~%} +class I{{ interface.name }}(object): + SERVICE_ID = {{ interface.id -}} +{% for function in interface.functions ~%} + {{ function.name|upper }}_ID = {{ function.id -}} +{% endfor -%} +{% for function in interface.functions ~%} +{% if !function.external -%} +{% if !function.preceding_comment.is_empty() -%} + {{ function.preceding_comment }} +{% endif %} + def {{ self::format_function_prototype_py(function) }}: + raise NotImplementedError() +{%- endif -%} +{% endfor %} +{% endfor -%} \ No newline at end of file diff --git a/erpcgen_rust/templates/python/py_macros.askama b/erpcgen_rust/templates/python/py_macros.askama new file mode 100644 index 00000000..8e5cac3a --- /dev/null +++ b/erpcgen_rust/templates/python/py_macros.askama @@ -0,0 +1,22 @@ +{% macro decode_type(py_type, name, prefix, codec, indent, depth) %} +{% if py_type.is_struct() %} {# struct #} +self.{{name}} = {{ name }}._read({{ codec }}) +{% else if py_type.is_union %} {# union in list for exampe #} +{{ member.name }}, discriminator = {{ member.name }}._read({{codec}}) +{% else if py_type.is_enum() %} {# enum #} +{{ name }} = {{ codec }}.read_int32() +{% else if py_type.is_string() %} {# string #} +{{ name }} = {{ codec }}.read_string() +{% else if py_type.is_binary() %} {# binary #} +{{ name }} = {{ codec }}.read_binary() +{% else if py_type.is_array() || py_type.is_list() %} +{% if py_type.is_list() %} +_n{{ depth }} = {{ codec }}.start_read_list(len({{ name }})) +{% endif %} +{% if py_type.is_list() %} {% endif %}{{ name }} = [] +for _i{{ depth }} in range({% if py_type.is_list() %}_n{{ depth }}{% else %}{{ get_current_array_dim(py_type) }}{% endif %}): + {% call decode_type(shed_array_dimension(py_type), concat_slices("_v", depth.as_str()), prefix, codec, indent, depth + 1) %} + {{ name }}.append(_v{{ depth }}) +{% endif %} +{% endmacro decode_type %} + diff --git a/erpcgen_rust/templates/python/py_server.askama b/erpcgen_rust/templates/python/py_server.askama new file mode 100644 index 00000000..9a3065c8 --- /dev/null +++ b/erpcgen_rust/templates/python/py_server.askama @@ -0,0 +1,87 @@ +{% if !preceding_comment.is_empty() -%} +{{ preceding_comment -}} +{% endif ~%} + +# +# Generated by erpcgen {{ crate::ERPC_VERSION_STR }} on {{ date }}. +# +# AUTOGENERATED - DO NOT EDIT +# + +import erpc +from . import common, interface +{%- if !includes.is_empty() -%} +{% for inc in includes ~%} +from . import {{ inc }} +{%- endfor %} +{% endif -%} + +{% if !callbacks.is_empty() ~%} +{% for callback in callbacks ~%} +_{{ callback.name }} = [ {% for function in callback.functions %}{{ function }}{% if !loop.last %}, {% endif %}{% endfor %} ] +{% endfor -%} +{% endif -%} +{% for interface in interfaces %} +# Client for {{ interface.name }} +class {{ interface.name }}Service(erpc.server.Service): + def __init__(self, handler): + super({{ interface.name }}Service, self).__init__(interface.I{{ interface.name }}.SERVICE_ID) + self._handler = handler + self._methods = { +{%- for function in interface.functions %} + interface.I{{ interface.name }}.{{ function.name|upper }}_ID: self._handle_{{ function.name }}, +{%- endfor %} + } +{% for function in interface.functions %} + def _handle_{{ function.name }}(self, sequence, codec): +{%- if function.has_out_parameters() %} + # Create reference objects to pass into handler for out/inout parameters. +{%- for parameter in function.parameters -%} +{% if parameter.is_out() && parameter.is_length_for.is_none() && parameter.is_discriminator.is_none() %} + {{ parameter.name }} = erpc.Reference() +{%- endif -%} +{% endfor -%} +{% endif %} +{% for parameter in function.parameters -%} +{% if parameter.is_in() && parameter.is_length_for.is_none() && parameter.is_discriminator.is_none() -%} +{% let indent = self::optional_indent(parameter.is_nullable) -%} +{% let formatted_name = self::format_param_name(parameter.name, parameter.direction) -%} +{% if parameter.is_nullable %} + if codec.read_null_flag(): + {{ parameter.name }} = None + else: + if {{ formatted_name }} is not None: +{%- endif %} + {{ indent }}{{ self::decode_type(parameter.type_.clone(), formatted_name, "common.", parameter.discriminator_name.clone(), "codec", indent, 0)|indent(8) -}} +{% endif -%} +{% endfor %} + # Invoke user implementation of remote function. + {% if function.return_type.is_some() %}_result = {% endif %}self._handler.{{ function.name }}({{ self::format_inner_parameters_py(function) }}) +{%- if !function.is_oneway %} + + # Prepare codec for reply message. + codec.reset() + + # Construct reply message. + codec.start_write_message(erpc.codec.MessageInfo( + type=erpc.codec.MessageType.kReplyMessage, + service=interface.I{{ interface.name }}.SERVICE_ID, + request=interface.I{{ interface.name }}.{{ function.name|upper }}_ID, + sequence=sequence)) +{% for parameter in function.parameters -%} +{% if parameter.is_out() && parameter.is_length_for.is_none() && parameter.is_discriminator.is_none() -%} +{% let indent = self::optional_indent(parameter.is_nullable) -%} +{% let formatted_name = self::format_param_name(parameter.name, parameter.direction) -%} +{% if parameter.is_nullable %} + if {{ formatted_name }} != None: +{%- endif %} + {{ indent }}{{ self::encode_type(parameter.type_.clone(), formatted_name, "common.", parameter.discriminator_name.clone() , "codec", indent, 0)|indent(8) -}} +{% endif -%} +{% endfor -%} +{% if function.return_type.is_some() %} + {{ self::encode_type(function.return_type.clone().expect("Return type must be set"), "_result", "common.", None, "codec", "", 0)|indent(8) }} + return _result +{%- endif %} +{% endif %} +{% endfor %} +{% endfor %} \ No newline at end of file diff --git a/erpcgen_rust/templates/python/py_struct.askama b/erpcgen_rust/templates/python/py_struct.askama new file mode 100644 index 00000000..c9509860 --- /dev/null +++ b/erpcgen_rust/templates/python/py_struct.askama @@ -0,0 +1,100 @@ +{% if !struct_.preceding_comment.is_empty() -%} +{{ struct_.preceding_comment -}} +{% endif -%} +class {{ struct_.name }}(object): +{%- for encapsulated in struct_.unions -%} + {% include "py_encapsulated.askama" %} +{% endfor %} + def __init__(self, {{ self::format_class_init_py(struct_.data_members) }}): +{%- for m in struct_.members -%} +{% if m.is_data_member() -%} +{% let data_member = m.clone().to_data_member() -%} +{% if data_member.is_length_for.is_none() %} + self.{{ data_member.name }} = {{ data_member.name +}} # {{ self::format_type_name_py(data_member.type_.clone()) -}} +{% endif -%} +{% else -%} +{% let union_member = m.clone().to_encapsulated_union() %} + self.{{ union_member.name }} = self.{{ union_member.name }}_union # {{ self::format_type_name_py(union_member.type_.clone() ) -}} +{% endif -%} +{% endfor -%} + +{# create read-only properties for @length counts #} +{% for m in struct_.data_members -%} +{% if m.is_length_for.is_some() %} + @property + def {{ m.name }}(self): + return len(self.{{ m.is_length_for.clone().unwrap() }}) +{%- endif -%} +{% endfor %} + def _read(self, codec): +{%- for m in struct_.members -%} +{% if m.is_data_member() -%} {# member variant #} +{%- let data_member = m.clone().to_data_member() -%} +{% let self_dm_name = "self.".to_string() + data_member.name.as_str() -%} +{% let indent = self::optional_indent(data_member.is_nullable) -%} +{% if data_member.is_length_for.is_none() && data_member.is_discriminator.is_none() -%} {# member variant #} +{%- if data_member.is_nullable %} + if codec.read_null_flag(): + {{ self_dm_name }} = None + else: +{%- endif -%} +{% if data_member.type_.is_union() %} + {{ indent }}{{ self_dm_name }}, self.{{ data_member.discriminator_name.clone().unwrap() }} = {{ data_member.type_.clone().get_name() }}()._read(codec) +{%- else %} + {{ self::decode_type(data_member.type_.clone(), self_dm_name, "", data_member.discriminator_name.clone(), "codec", indent, 0)|indent(8) -}} +{% endif -%} +{% endif -%} +{% else -%} {# union variant #} +{%- let encapsulated = m.clone().to_encapsulated_union() -%} +{% include "py_en_union_read.askama" -%} +{% endif -%} {# member variant #} +{%- endfor %} + return self + + def _write(self, codec): +{%- for m in struct_.members -%} +{% if m.is_data_member() -%} {# member variant #} +{%- let data_member = m.clone().to_data_member() -%} +{% let self_dm_name = "self.".to_string() + data_member.name.as_str() -%} +{% let indent = self::optional_indent(data_member.is_nullable) -%} +{% if data_member.is_length_for.is_none() && data_member.is_discriminator.is_none() -%} {# member variant #} +{%- if data_member.is_nullable %} + codec.write_null_flag({{ self_dm_name }} is None) + if {{ self_dm_name }} is not None: +{%- else %} + if {{ self_dm_name }} is None: + raise ValueError("{{ data_member.name }} is None") +{%- endif -%} +{% if data_member.type_.is_union() %} + {{ indent }}{{ data_member.type_.clone().get_name() }}()._write(codec, self.{{ data_member.discriminator_name.clone().unwrap() }}) +{%- else %} + {{ self::encode_type(data_member.type_.clone(), self_dm_name, "", None, "codec", indent, 0)|indent(8) -}} +{% endif -%} +{% endif -%} +{% else -%} {# union variant #} +{%- let encapsulated = m.clone().to_encapsulated_union() -%} +{% include "py_en_union_write.askama" -%} +{% endif -%} {# member variant #} +{%- endfor %} + + def __str__(self): + return "<%s@%x +{%- for m in struct_.members -%} +{%- if m.is_data_member() -%} +{%- let data_member = m.clone().to_data_member() %} +{%- if data_member.is_length_for.is_none() %} {{ data_member.name }}=%s {%- endif %} +{%- else %} +{%- let union_member = m.clone().to_encapsulated_union() %} {{ union_member.name }}=%s +{%- endif %} +{%- endfor %}>" % (self.__class__.__name__, id(self) +{%- for m in struct_.members -%} +{%- if m.is_data_member() -%} +{%- let data_member = m.clone().to_data_member() %} +{%- if data_member.is_length_for.is_none() %}, self.{{ data_member.name }} {%- endif %} +{%- else %} +{%- let union_member = m.clone().to_encapsulated_union() %} self.{{ union_member.name }} +{%- endif %} +{%- endfor %}) + + def __repr__(self): + return self.__str__() \ No newline at end of file diff --git a/erpcgen_rust/templates/python/py_typedef.askama b/erpcgen_rust/templates/python/py_typedef.askama new file mode 100644 index 00000000..52d2e2e7 --- /dev/null +++ b/erpcgen_rust/templates/python/py_typedef.askama @@ -0,0 +1 @@ +{{ typedef_.name }} = {{ typedef_.type_ }} \ No newline at end of file diff --git a/erpcgen_rust/templates/python/py_union.askama b/erpcgen_rust/templates/python/py_union.askama new file mode 100644 index 00000000..9b43da62 --- /dev/null +++ b/erpcgen_rust/templates/python/py_union.askama @@ -0,0 +1,22 @@ +{% if !union_.preceding_comment.is_empty() -%} +{{ union_.preceding_comment -}} +{% endif -%} + class {{ union_.name }}(object): +{%- for c in union_.cases -%} +{% if !c.case_members.is_empty() %} + # {% if c.case_values.is_empty() %}default case{% else %}case {{ self::format_values_py(c.case_values) }}{% endif %} +{%- for cm in c.case_members %} + {{ cm.name }} = None # {{+ self::format_type_name_py(cm.type_.clone()) -}} +{% endfor -%} +{% endif -%} +{% endfor %} + + def _read(self, codec): + {%- include "py_union_read.askama" %} + return self, discriminator + + def _write(self, codec, discriminator): + {%- include "py_union_write.askama" %} + + def __repr__(self): + return self.__str__() \ No newline at end of file diff --git a/erpcgen_rust/templates/python/py_union_read.askama b/erpcgen_rust/templates/python/py_union_read.askama new file mode 100644 index 00000000..563b38d8 --- /dev/null +++ b/erpcgen_rust/templates/python/py_union_read.askama @@ -0,0 +1,49 @@ +{% let self_e_name = "self.".to_string() -%} +{% let discr = "discriminator".to_string() %} + discriminator = codec.start_read_union() +{%- if union_.has_only_default_case() -%} {# case union has 1 or 0 cases#} +{%- for case in union_.cases -%} +{% for member in case.case_members -%} +{% let self_m_name = self_e_name.clone() + member.name.as_str() -%} +{% let indent = self::optional_indent(member.is_nullable) -%} +{% if member.is_nullable %} + if codec.read_null_flag(): + {{ self_m_name }} = None + else: +{%- endif -%} +{% if member.type_.is_union() %} + {{ indent }}{{ self_e_name }}.{{member.name}}, self.{{ member.discriminator_name.clone().unwrap() }} = {{ member.type_.clone().get_name() }}()._read(codec) +{%- else %} + {{ indent }}{{ self::decode_type(member.type_.clone(), (self_e_name.clone() + member.name.as_str()).as_str(), "", member.discriminator_name.clone(), "codec", indent, 0)|indent(12) }} +{%- endif -%} +{% endfor -%} +{% endfor -%} +{%- else -%} {# case union has 2 or more cases #} +{%- for case in union_.cases -%} +{% if loop.first %} + if {{ self::format_case_values_py(case.case_values, discr) }}: +{%- else if loop.last %} + else: +{%- else %} + elif {{ self::format_case_values_py(case.case_values, discr) -}}: +{%- endif -%} +{% for member in case.case_members -%} +{% let self_m_name = self_e_name.clone() + member.name.as_str() -%} +{% let indent = self::optional_indent(member.is_nullable) -%} +{% if member.is_nullable %} + if codec.read_null_flag(): + {{ self_m_name }} = None + else: +{%- endif -%} +{% if member.type_.is_union() %} + {{ indent }}{{ self_e_name }}.{{member.name}}, self.{{ member.discriminator_name.clone().unwrap() }} = {{ member.type_.clone().get_name() }}()._read(codec) +{%- else %} + {{ self::decode_type(member.type_.clone(), (self_e_name.clone() + member.name.as_str()).as_str(), "", member.discriminator_name.clone(), "codec", indent, 0)|indent(12) }} +{%- endif -%} +{% endfor -%} +{% endfor -%} +{% endif %} {# case union has 2 or more cases end #} +{%- if !union_.has_default_case() %} + else: + raise ValueError("invalid union discriminator value %s" % repr(discriminator)) +{%- endif %} \ No newline at end of file diff --git a/erpcgen_rust/templates/python/py_union_write.askama b/erpcgen_rust/templates/python/py_union_write.askama new file mode 100644 index 00000000..885c3c9f --- /dev/null +++ b/erpcgen_rust/templates/python/py_union_write.askama @@ -0,0 +1,53 @@ +{% let self_e_name = "self.".to_string() -%} +{% let discr = "discriminator".to_string() %} + codec.start_write_union(discriminator) +{%- if union_.has_only_default_case() -%} {# case union has 1 or 0 cases#} +{%- for case in union_.cases -%} +{% for member in case.case_members -%} +{% let self_m_name = self_e_name.clone() + member.name.as_str() -%} +{% let indent = self::optional_indent(member.is_nullable) -%} +{%- if member.is_nullable %} + codec.write_null_flag({{ self_m_name }} is None) + if {{ self_m_name }} is not None: +{%- else %} + if {{ self_m_name }} is None: + raise ValueError("{{ member.name }} is None") +{%- endif -%} +{% if member.type_.is_union() %} + {{ indent }}{{ member.type_.clone().get_name() }}()._write(codec, self.{{ member.discriminator_name.clone().unwrap() }}) +{%- else %} + {{ self::encode_type(member.type_.clone(), self_m_name, "", None, "codec", indent, 0)|indent(12) -}} +{%- endif -%} +{% endfor -%} +{% endfor -%} +{%- else -%} {# case union has 2 or more cases #} +{%- for case in union_.cases -%} +{% if loop.first %} + if {{ self::format_case_values_py(case.case_values, discr) }}: +{%- else if loop.last %} + else: +{%- else %} + elif {{ self::format_case_values_py(case.case_values, discr) }}: +{%- endif -%} +{% for member in case.case_members -%} +{% let self_m_name = self_e_name.clone() + member.name.as_str() -%} +{% let indent = self::optional_indent(member.is_nullable) -%} +{%- if member.is_nullable %} + codec.write_null_flag({{ self_m_name }} is None) + if {{ self_m_name }} is not None: +{%- else %} + if {{ self_m_name }} is None: + raise ValueError("{{ member.name }} is None") +{%- endif -%} +{% if member.type_.is_union() %} + {{ indent }}{{ member.type_.clone().get_name() }}()._write(codec, self.{{ member.discriminator_name.clone().unwrap() }}) +{%- else %} + {{ self::encode_type(member.type_.clone(), self_m_name, "", None, "codec", indent, 0)|indent(12) -}} +{%- endif -%} +{% endfor -%} +{% endfor -%} +{% endif %} {# case union has 2 or more cases end #} +{%- if !union_.has_default_case() %} + else: + raise ValueError("invalid union discriminator value %s" % repr(discriminator)) +{%- endif %} \ No newline at end of file