From eb7a901d094466b0bd123db00e84c4d67c6b27b6 Mon Sep 17 00:00:00 2001 From: Soispha Date: Tue, 26 Mar 2024 10:38:14 +0100 Subject: [PATCH] refactor(treewide): Rework file structure in `src/macros` This might be a big diff, but I _hope_ that it does not change much functionally (hopefully it changes nothing generation specific). What has changed? ----------------- - I had to merge the three crates into one, to allow `macros` to impl functions on Types defined in `parser`. - As mentioned in the point above, the conversion function are now inherent to the type they convert (i. e. `r#type.to_rust()` instead of `type_to_rust(r#type)`). - The conversion function have been sorted, by what they convert to: - `to_rust()` converts a type to be used in rust *host* code. - `to_c()` converts a type to be used in c *host* code. - `to_auxiliary_c()` converts a type to be used in c *auxiliary* code. - The top-most `generate` method of `TrixyConfig` now returns a `FileTree` instead of writing the files directly. The `FileTree` can still be materialize with the `materialize` method. But this change facilitates moving non-generation focused code out of the `generate` method. What is the difference between _host_ and _auxiliary_ code? ----------------------------------------------------------- Auxiliary code is always written in the language it is generated for. So auxiliary code for c would be written in c (or at least in a subset of c), as it represents c header files. Host code is always written in rust. This is the code that is responsible for implementing the actual ffi stuff. In our case these are the `extern "C"` functions and the types, defined in trixy. The rust host code is generating the native rust representations of these types. --- Cargo.toml | 31 +- {trixy-parser => docs}/generate_docs | 4 +- {trixy-parser/docs => docs}/grammar.ebnf | 0 {trixy-parser/docs => docs}/grammar.pdf | Bin src/lib.rs | 12 +- src/macros/config/file_tree.rs | 127 ++++++ src/macros/config/mod.rs | 2 + .../mod.rs => src/macros/config/trixy.rs | 87 ++-- .../macros}/generate.old/command_enum/mod.rs | 0 .../lua_functions_to_globals/mod.rs | 0 .../macros}/generate.old/lua_wrapper/mod.rs | 0 .../lua_wrapper/rust_wrapper_functions/mod.rs | 0 .../src => src/macros}/generate.old/mod.rs | 0 .../macros/generate/auxiliary/c}/mod.rs | 24 +- .../generate/auxiliary/c/pure_header.rs | 35 +- .../generate/auxiliary/c/structs_init.rs | 20 +- .../macros/generate/auxiliary/c/typedef.rs | 19 +- src/macros/generate/auxiliary/mod.rs | 47 +++ .../convert/auxiliary/c/attribute/mod.rs | 11 + .../convert/auxiliary/c/enumeration/mod.rs | 36 ++ .../convert/auxiliary/c/function/mod.rs | 77 ++++ .../auxiliary/c/identifier/doc_identifier.rs | 13 + .../convert/auxiliary/c/identifier/mod.rs | 42 ++ .../generate/convert/auxiliary/c/mod.rs | 16 + .../convert/auxiliary/c/namespace/mod.rs | 122 ++++++ .../convert/auxiliary/c/structure/mod.rs | 25 ++ .../auxiliary/c/type/doc_named_type.rs | 14 + .../generate/convert/auxiliary/c/type/mod.rs | 52 +++ .../convert/auxiliary/c/type/named_type.rs | 14 + src/macros/generate/convert/auxiliary/mod.rs | 1 + .../convert/host/c/enumeration/mod.rs | 47 +++ .../generate/convert/host/c/function/mod.rs | 146 +++++++ .../generate/convert/host/c/identifier/mod.rs | 26 ++ src/macros/generate/convert/host/c/mod.rs | 15 + .../generate/convert/host/c/namespace/mod.rs | 36 ++ .../generate/convert/host/c/structure/mod.rs | 28 ++ .../convert/host/c/type/doc_named_type.rs | 19 + .../generate/convert/host/c/type/mod.rs | 100 +++++ .../convert/host/c/type/named_type.rs | 33 ++ .../generate/convert/host/c/variant/mod.rs | 36 ++ src/macros/generate/convert/host/mod.rs | 2 + .../convert/host/rust/attribute/mod.rs | 22 + .../convert/host/rust/derive/enumeration.rs | 48 +++ .../generate/convert/host/rust/derive/mod.rs | 2 + .../convert/host/rust/derive/structure.rs | 68 ++++ .../convert/host/rust/enumeration/mod.rs | 45 +++ .../convert/host/rust/function/mod.rs | 61 +++ .../host/rust/identifier/doc_identifier.rs | 20 + .../convert/host/rust/identifier/mod.rs | 29 ++ src/macros/generate/convert/host/rust/mod.rs | 19 + .../convert/host/rust/namespace/mod.rs | 2 + .../convert/host/rust/namespace/module.rs | 59 +++ .../convert/host/rust/namespace/path.rs | 65 +++ .../convert/host/rust/structure/mod.rs | 43 ++ .../convert/host/rust/type/doc_named_type.rs | 19 + .../generate/convert/host/rust/type/mod.rs | 67 ++++ .../convert/host/rust/type/named_type.rs | 14 + .../generate/convert/host/rust/variant/mod.rs | 25 ++ src/macros/generate/convert/mod.rs | 2 + src/macros/generate/host/c/host/mod.rs | 74 ++++ .../macros/generate/host/c/mod.rs | 33 +- src/macros/generate/host/mod.rs | 34 ++ src/macros/generate/host/rust/host/mod.rs | 114 ++++++ .../macros/generate/host/rust/mod.rs | 30 +- src/macros/generate/mod.rs | 3 + src/macros/mod.rs | 123 ++++++ .../parser}/command_spec/checked.rs | 2 +- .../src => src/parser}/command_spec/mod.rs | 0 .../parser}/command_spec/unchecked.rs | 8 +- {trixy-parser/src => src/parser}/error.rs | 4 +- .../src => src/parser}/lexing/error.rs | 4 +- .../src => src/parser}/lexing/mod.rs | 82 ++-- .../src => src/parser}/lexing/test.rs | 0 .../src => src/parser}/lexing/tokenizer.rs | 2 +- trixy-parser/src/lib.rs => src/parser/mod.rs | 2 +- .../parser}/parsing/checked/error.rs | 4 +- .../src => src/parser}/parsing/checked/mod.rs | 8 +- .../parser}/parsing/checked/test.rs | 0 .../src => src/parser}/parsing/mod.rs | 0 .../parser}/parsing/unchecked/error.rs | 4 +- .../parser}/parsing/unchecked/mod.rs | 12 +- .../parser}/parsing/unchecked/test.rs | 0 .../src => src/types}/c_headers/errno.h | 0 .../src => src/types}/c_headers/string.h | 0 .../src => src/types}/c_headers/vec.h | 0 {trixy-types/src => src/types}/error/mod.rs | 0 trixy-types/src/lib.rs => src/types/mod.rs | 0 .../src => src/types}/traits/convert_trait.rs | 6 +- .../src => src/types}/traits/errno.rs | 2 +- {trixy-types/src => src/types}/traits/mod.rs | 2 +- .../src => src/types}/traits/try_from_impl.rs | 4 +- {trixy-types/src => src/types}/types_list.rs | 0 trixy-macros/.gitignore | 6 - trixy-macros/Cargo.toml | 32 -- .../src/generate/c_api/header/pure_header.rs | 194 --------- .../src/generate/c_api/header/structs_init.rs | 85 ---- .../src/generate/c_api/header/typedef.rs | 114 ------ trixy-macros/src/generate/c_api/host.rs | 375 ------------------ trixy-macros/src/generate/c_api/mod.rs | 226 ----------- .../src/generate/convertible_derive.rs | 148 ------- trixy-macros/src/generate/host/mod.rs | 315 --------------- trixy-macros/src/generate/mod.rs | 244 ------------ trixy-macros/src/lib.rs | 150 ------- trixy-parser/.gitignore | 6 - trixy-parser/Cargo.toml | 42 -- trixy-parser/README.md | 13 - trixy-parser/example/derives.tri | 54 --- trixy-parser/example/derives_minimal.tri | 6 - trixy-parser/example/empty.tri | 26 -- trixy-parser/example/failing_derives.tri | 55 --- trixy-parser/example/failing_enum_name.tri | 35 -- .../example/failing_types_generic.tri | 30 -- trixy-parser/example/full.tri | 157 -------- trixy-parser/example/functions.tri | 31 -- trixy-parser/example/simple.tri | 29 -- trixy-parser/example/trinitrix_api.tri | 117 ------ trixy-parser/src/bin/trixy-parser.rs | 167 -------- trixy-types/.gitignore | 6 - trixy-types/Cargo.toml | 32 -- 119 files changed, 2280 insertions(+), 2901 deletions(-) rename {trixy-parser => docs}/generate_docs (92%) rename {trixy-parser/docs => docs}/grammar.ebnf (100%) rename {trixy-parser/docs => docs}/grammar.pdf (100%) create mode 100644 src/macros/config/file_tree.rs create mode 100644 src/macros/config/mod.rs rename trixy-macros/src/config/mod.rs => src/macros/config/trixy.rs (62%) rename {trixy-macros/src => src/macros}/generate.old/command_enum/mod.rs (100%) rename {trixy-macros/src => src/macros}/generate.old/lua_wrapper/lua_functions_to_globals/mod.rs (100%) rename {trixy-macros/src => src/macros}/generate.old/lua_wrapper/mod.rs (100%) rename {trixy-macros/src => src/macros}/generate.old/lua_wrapper/rust_wrapper_functions/mod.rs (100%) rename {trixy-macros/src => src/macros}/generate.old/mod.rs (100%) rename {trixy-macros/src/generate/c_api/header => src/macros/generate/auxiliary/c}/mod.rs (80%) rename trixy-parser/example/comments.tri => src/macros/generate/auxiliary/c/pure_header.rs (52%) rename trixy-parser/example/multiple.tri => src/macros/generate/auxiliary/c/structs_init.rs (70%) rename trixy-parser/example/failing_types.tri => src/macros/generate/auxiliary/c/typedef.rs (72%) create mode 100644 src/macros/generate/auxiliary/mod.rs create mode 100644 src/macros/generate/convert/auxiliary/c/attribute/mod.rs create mode 100644 src/macros/generate/convert/auxiliary/c/enumeration/mod.rs create mode 100644 src/macros/generate/convert/auxiliary/c/function/mod.rs create mode 100644 src/macros/generate/convert/auxiliary/c/identifier/doc_identifier.rs create mode 100644 src/macros/generate/convert/auxiliary/c/identifier/mod.rs create mode 100644 src/macros/generate/convert/auxiliary/c/mod.rs create mode 100644 src/macros/generate/convert/auxiliary/c/namespace/mod.rs create mode 100644 src/macros/generate/convert/auxiliary/c/structure/mod.rs create mode 100644 src/macros/generate/convert/auxiliary/c/type/doc_named_type.rs create mode 100644 src/macros/generate/convert/auxiliary/c/type/mod.rs create mode 100644 src/macros/generate/convert/auxiliary/c/type/named_type.rs create mode 100644 src/macros/generate/convert/auxiliary/mod.rs create mode 100644 src/macros/generate/convert/host/c/enumeration/mod.rs create mode 100644 src/macros/generate/convert/host/c/function/mod.rs create mode 100644 src/macros/generate/convert/host/c/identifier/mod.rs create mode 100644 src/macros/generate/convert/host/c/mod.rs create mode 100644 src/macros/generate/convert/host/c/namespace/mod.rs create mode 100644 src/macros/generate/convert/host/c/structure/mod.rs create mode 100644 src/macros/generate/convert/host/c/type/doc_named_type.rs create mode 100644 src/macros/generate/convert/host/c/type/mod.rs create mode 100644 src/macros/generate/convert/host/c/type/named_type.rs create mode 100644 src/macros/generate/convert/host/c/variant/mod.rs create mode 100644 src/macros/generate/convert/host/mod.rs create mode 100644 src/macros/generate/convert/host/rust/attribute/mod.rs create mode 100644 src/macros/generate/convert/host/rust/derive/enumeration.rs create mode 100644 src/macros/generate/convert/host/rust/derive/mod.rs create mode 100644 src/macros/generate/convert/host/rust/derive/structure.rs create mode 100644 src/macros/generate/convert/host/rust/enumeration/mod.rs create mode 100644 src/macros/generate/convert/host/rust/function/mod.rs create mode 100644 src/macros/generate/convert/host/rust/identifier/doc_identifier.rs create mode 100644 src/macros/generate/convert/host/rust/identifier/mod.rs create mode 100644 src/macros/generate/convert/host/rust/mod.rs create mode 100644 src/macros/generate/convert/host/rust/namespace/mod.rs create mode 100644 src/macros/generate/convert/host/rust/namespace/module.rs create mode 100644 src/macros/generate/convert/host/rust/namespace/path.rs create mode 100644 src/macros/generate/convert/host/rust/structure/mod.rs create mode 100644 src/macros/generate/convert/host/rust/type/doc_named_type.rs create mode 100644 src/macros/generate/convert/host/rust/type/mod.rs create mode 100644 src/macros/generate/convert/host/rust/type/named_type.rs create mode 100644 src/macros/generate/convert/host/rust/variant/mod.rs create mode 100644 src/macros/generate/convert/mod.rs create mode 100644 src/macros/generate/host/c/host/mod.rs rename trixy-parser/example/other_comments.tri => src/macros/generate/host/c/mod.rs (68%) create mode 100644 src/macros/generate/host/mod.rs create mode 100644 src/macros/generate/host/rust/host/mod.rs rename trixy-parser/example/types.tri => src/macros/generate/host/rust/mod.rs (68%) create mode 100644 src/macros/generate/mod.rs create mode 100644 src/macros/mod.rs rename {trixy-parser/src => src/parser}/command_spec/checked.rs (99%) rename {trixy-parser/src => src/parser}/command_spec/mod.rs (100%) rename {trixy-parser/src => src/parser}/command_spec/unchecked.rs (96%) rename {trixy-parser/src => src/parser}/error.rs (99%) rename {trixy-parser/src => src/parser}/lexing/error.rs (95%) rename {trixy-parser/src => src/parser}/lexing/mod.rs (77%) rename {trixy-parser/src => src/parser}/lexing/test.rs (100%) rename {trixy-parser/src => src/parser}/lexing/tokenizer.rs (99%) rename trixy-parser/src/lib.rs => src/parser/mod.rs (97%) rename {trixy-parser/src => src/parser}/parsing/checked/error.rs (98%) rename {trixy-parser/src => src/parser}/parsing/checked/mod.rs (98%) rename {trixy-parser/src => src/parser}/parsing/checked/test.rs (100%) rename {trixy-parser/src => src/parser}/parsing/mod.rs (100%) rename {trixy-parser/src => src/parser}/parsing/unchecked/error.rs (98%) rename {trixy-parser/src => src/parser}/parsing/unchecked/mod.rs (97%) rename {trixy-parser/src => src/parser}/parsing/unchecked/test.rs (100%) rename {trixy-types/src => src/types}/c_headers/errno.h (100%) rename {trixy-types/src => src/types}/c_headers/string.h (100%) rename {trixy-types/src => src/types}/c_headers/vec.h (100%) rename {trixy-types/src => src/types}/error/mod.rs (100%) rename trixy-types/src/lib.rs => src/types/mod.rs (100%) rename {trixy-types/src => src/types}/traits/convert_trait.rs (97%) rename {trixy-types/src => src/types}/traits/errno.rs (98%) rename {trixy-types/src => src/types}/traits/mod.rs (96%) rename {trixy-types/src => src/types}/traits/try_from_impl.rs (94%) rename {trixy-types/src => src/types}/types_list.rs (100%) delete mode 100644 trixy-macros/.gitignore delete mode 100644 trixy-macros/Cargo.toml delete mode 100644 trixy-macros/src/generate/c_api/header/pure_header.rs delete mode 100644 trixy-macros/src/generate/c_api/header/structs_init.rs delete mode 100644 trixy-macros/src/generate/c_api/header/typedef.rs delete mode 100644 trixy-macros/src/generate/c_api/host.rs delete mode 100644 trixy-macros/src/generate/c_api/mod.rs delete mode 100644 trixy-macros/src/generate/convertible_derive.rs delete mode 100644 trixy-macros/src/generate/host/mod.rs delete mode 100644 trixy-macros/src/generate/mod.rs delete mode 100644 trixy-macros/src/lib.rs delete mode 100644 trixy-parser/.gitignore delete mode 100644 trixy-parser/Cargo.toml delete mode 100644 trixy-parser/README.md delete mode 100644 trixy-parser/example/derives.tri delete mode 100644 trixy-parser/example/derives_minimal.tri delete mode 100644 trixy-parser/example/empty.tri delete mode 100644 trixy-parser/example/failing_derives.tri delete mode 100644 trixy-parser/example/failing_enum_name.tri delete mode 100644 trixy-parser/example/failing_types_generic.tri delete mode 100644 trixy-parser/example/full.tri delete mode 100644 trixy-parser/example/functions.tri delete mode 100644 trixy-parser/example/simple.tri delete mode 100644 trixy-parser/example/trinitrix_api.tri delete mode 100644 trixy-parser/src/bin/trixy-parser.rs delete mode 100644 trixy-types/.gitignore delete mode 100644 trixy-types/Cargo.toml diff --git a/Cargo.toml b/Cargo.toml index 9ead302..af4dc6c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -23,7 +23,30 @@ version = "0.1.0" edition = "2021" [dependencies] -trixy-parser = { path = "./trixy-parser" } -trixy-macros = { path = "./trixy-macros" } -trixy-types = { path = "./trixy-types" } -thiserror = "1.0.57" +clap = { version = "4.5.4", features = ["derive"], optional = true } +convert_case = {version = "0.6.0", optional = true} +proc-macro2 = {version = "1.0.79", optional = true} +quote = {version = "1.0.35", optional = true} +syn = { version = "2.0.55", features = ["extra-traits", "full", "parsing"], optional = true } +thiserror = { version = "1.0.58", optional = true} + +# macros +prettyplease = {version = "0.2.17", optional = true} + +# parser +regex = {version = "1.10.4", optional = true} + +# types +libc ={ version = "0.2.153", optional = true} +log = { version = "0.4.21", optional = true} + +[dev-dependencies] +# parser +pretty_assertions = "1.4.0" + +[features] +default = ["parser", "types", "macros"] + +parser = [ "regex", "thiserror", "convert_case" ] +types = [ "parser", "libc", "log", "proc-macro2", "quote", "syn", "thiserror", "convert_case" ] +macros = [ "parser", "types", "prettyplease", "proc-macro2", "quote", "syn", "convert_case" ] diff --git a/trixy-parser/generate_docs b/docs/generate_docs similarity index 92% rename from trixy-parser/generate_docs rename to docs/generate_docs index b486c21..5b7e0b0 100755 --- a/trixy-parser/generate_docs +++ b/docs/generate_docs @@ -18,7 +18,7 @@ # and the Lesser GNU General Public License along with this program. # If not, see . -ebnf2pdf make "./docs/grammar.ebnf" -mv grammar.ebnf.pdf ./docs/grammar.pdf +ebnf2pdf make "./grammar.ebnf" +mv grammar.ebnf.pdf ./grammar.pdf # vim: ft=sh diff --git a/trixy-parser/docs/grammar.ebnf b/docs/grammar.ebnf similarity index 100% rename from trixy-parser/docs/grammar.ebnf rename to docs/grammar.ebnf diff --git a/trixy-parser/docs/grammar.pdf b/docs/grammar.pdf similarity index 100% rename from trixy-parser/docs/grammar.pdf rename to docs/grammar.pdf diff --git a/src/lib.rs b/src/lib.rs index 9be5fae..d9a07b4 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -19,13 +19,13 @@ * If not, see . */ -pub mod types { - pub use trixy_types::*; -} +#[cfg(feature = "macros")] +pub mod macros; +#[cfg(feature = "types")] +pub mod types; +#[cfg(feature = "parser")] +pub mod parser; -pub mod macros { - pub use trixy_macros::*; -} pub mod __private { //! This module contains crates needed for the generated code, it should not be used by humans. pub mod thiserror { diff --git a/src/macros/config/file_tree.rs b/src/macros/config/file_tree.rs new file mode 100644 index 0000000..8ac7949 --- /dev/null +++ b/src/macros/config/file_tree.rs @@ -0,0 +1,127 @@ +//! [`FileTree`]s are the fundamental data structure used by trixy to present generated data to +//! you. + +use std::{ + fmt::Display, + fs, io, + path::{Path, PathBuf}, +}; + +use super::trixy::Language; + +/// A file tree containing all files that were generated. These are separated into host and +/// auxiliary files. See their respective descriptions about what differentiates them. +#[derive(Default, Debug)] +pub struct FileTree { + /// Files, that are supposed to be included in the compiled crate. + pub host_files: Vec, + + /// Files, that should be shared with the consumer (e. g. c headers). + pub auxiliary_files: Vec, +} + +/// A generated files +#[derive(Default, Debug)] +pub struct GeneratedFile { + /// The path this generated file would like to be placed at. + /// This path is relative to the crate root. + pub path: PathBuf, + + /// The content of this file. + /// + /// This should already be formatted and ready to be used. + pub value: String, + + /// The language this file is written in. + /// + /// # Note + /// This can also be a associated language, + /// like having this set to [`Language::C`] for a c header file. + pub language: Language, +} + +impl GeneratedFile { + pub fn new(path: PathBuf, value: String, language: Language) -> Self { + Self { + path, + value, + language, + } + } + pub fn new_in_out_dir(name: String, value: String, language: Language, out_dir: &Path) -> Self { + let path = out_dir.join(name); + Self { + path, + value, + language, + } + } + + pub fn materialize(self) -> io::Result<()> { + fs::create_dir_all(self.path.parent().expect("This path should have a parent"))?; + fs::write(self.path, self.value.as_bytes())?; + Ok(()) + } +} + +impl FileTree { + pub fn new() -> Self { + Self::default() + } + + pub fn add_host_file(&mut self, file: GeneratedFile) { + self.host_files.push(file) + } + + pub fn add_auxiliary_file(&mut self, file: GeneratedFile) { + self.auxiliary_files.push(file) + } + + pub fn materialize(self) -> io::Result<()> { + self.host_files + .into_iter() + .map(|file| -> io::Result<()> { file.materialize() }) + .collect::>()?; + self.auxiliary_files + .into_iter() + .map(|file| -> io::Result<()> { file.materialize() }) + .collect::>()?; + Ok(()) + } +} + +impl Display for Language { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match &self { + Language::Rust => f.write_str("rust"), + Language::C => f.write_str("c"), + Language::Lua => f.write_str("lua"), + Language::All => unreachable!("The `all` language variant should never be displayed"), + } + } +} + +impl Display for GeneratedFile { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.write_fmt(format_args!("File path: `{}`\n", self.path.display()))?; + f.write_fmt(format_args!("```{}\n", self.language))?; + f.write_fmt(format_args!("{}\r", &self.value))?; + f.write_str("```\n\n") + } +} + +impl Display for FileTree { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.write_str("# Host files\n")?; + self.host_files + .iter() + .map(|file| -> std::fmt::Result { f.write_str(&file.to_string()) }) + .collect::()?; + + f.write_str("# Auxiliary files\n")?; + self.auxiliary_files + .iter() + .map(|file| -> std::fmt::Result { f.write_str(&file.to_string()) }) + .collect::() + } +} diff --git a/src/macros/config/mod.rs b/src/macros/config/mod.rs new file mode 100644 index 0000000..5e85912 --- /dev/null +++ b/src/macros/config/mod.rs @@ -0,0 +1,2 @@ +pub mod file_tree; +pub mod trixy; diff --git a/trixy-macros/src/config/mod.rs b/src/macros/config/trixy.rs similarity index 62% rename from trixy-macros/src/config/mod.rs rename to src/macros/config/trixy.rs index 71848af..864c325 100644 --- a/trixy-macros/src/config/mod.rs +++ b/src/macros/config/trixy.rs @@ -21,64 +21,72 @@ //! This module is responsible for the config passed to trixy. //! It works using the popular builder syntax: -//! ```no_run -//! use trixy_macros::config::{Language, TrixyConfig}; +//! ``` +//! use crate::macros::config::{Language, TrixyConfig}; //!# fn main() { //! let config = TrixyConfig::new() -//! .set_input_path("path/to/trixy/api.tri") -//! .set_output_path("api.rs") -//! .set_languages(vec![Language::Rust, Language::C]) -//! .set_generate_debug(false); +//! .trixy_path("path/to/trixy/api.tri") +//! .output_path("api.rs") +//! .languages(vec![Language::Rust, Language::C]) //!# } //! ``` -use std::path::PathBuf; +use std::{env, path::PathBuf}; -#[derive(Debug)] +#[derive(Debug, Default)] pub enum Language { Rust, C, Lua, + #[default] + All, } #[derive(Default, Debug)] pub struct TrixyConfig { - /// The Path to the base command interface config file + /// The Path to the base command interface config file. pub trixy_path: PathBuf, - /// The name of the outputted host code (rust and c bindings) - /// This file is written in $OUT_DIR - pub host_code_name: PathBuf, - /// The name of the c header - /// This file is written in $OUT_DIR - pub c_header_name: PathBuf, + /// The name of the outputted host code (rust and c bindings). + /// This file is written in $OUT_DIR> + pub host_code_name: String, + /// The name of the c header. + /// This file is written in $OUT_DIR. + pub c_header_name: String, /// The path from the root to the distribution directory. - /// Things like the c headers are copied in this dir - /// When this is [None] no dist dir will be generated + /// Things like the c headers are copied in this dir. + /// When this is [None] no dist dir will be generated. pub dist_dir_path: Option, - /// Whether to check if the dist dir is empty before writing something to it - pub check_dist_dir: bool, - - /// Should the macro generate Debug trait implementation for each enum - /// These are very useful but completely obscure the `cargo expand` output - pub generate_debug: bool, - + // /// Whether to check if the dist dir is empty before writing something to it. + // pub check_dist_dir: bool, /// This function is executed whenever an API function is called. /// The only argument is the command (encoded as the `Commands`) enum - /// which is represented by this function - /// Because of rust limitations this is supposed to be the name of a macro acting as said - /// function. This macro must be in scope of the generated code + /// which is represented by this function. pub callback_function: String, + + /// This path is used to place the outputted host code files. + /// Normally this would be the `$OUT_DIR` environment variable set by cargo at build time. + /// You have to set this, if you want to use trixy in an other context. + pub out_dir: PathBuf, } impl TrixyConfig { pub fn new>(callback_function: T) -> Self { + let out_dir; + if let Ok(out_dir_new) = env::var("OUT_DIR") { + let out_path = PathBuf::from(out_dir_new); + out_dir = out_path; + } else { + out_dir = PathBuf::default(); + }; + Self { callback_function: callback_function.into(), host_code_name: "api.rs".into(), c_header_name: "interface.h".into(), + out_dir, ..Default::default() } } @@ -90,13 +98,6 @@ impl TrixyConfig { } } - pub fn generate_debug(self, generate_debug: bool) -> Self { - Self { - generate_debug, - ..self - } - } - pub fn dist_dir_path>(self, dist_dir_path: T) -> Self { Self { dist_dir_path: Some(dist_dir_path.into()), @@ -104,21 +105,21 @@ impl TrixyConfig { } } - pub fn check_dist_dir(self, check_dist_dir: bool) -> Self { - Self { - check_dist_dir, - ..self - } - } - - pub fn host_code_name>(self, output_path: T) -> Self { + pub fn host_code_name>(self, output_path: T) -> Self { Self { host_code_name: output_path.into(), ..self } } - pub fn c_header_name>(self, output_path: T) -> Self { + pub fn out_dir>(self, out_dir: T) -> Self { + Self { + out_dir: out_dir.into(), + ..self + } + } + + pub fn c_header_name>(self, output_path: T) -> Self { Self { c_header_name: output_path.into(), ..self diff --git a/trixy-macros/src/generate.old/command_enum/mod.rs b/src/macros/generate.old/command_enum/mod.rs similarity index 100% rename from trixy-macros/src/generate.old/command_enum/mod.rs rename to src/macros/generate.old/command_enum/mod.rs diff --git a/trixy-macros/src/generate.old/lua_wrapper/lua_functions_to_globals/mod.rs b/src/macros/generate.old/lua_wrapper/lua_functions_to_globals/mod.rs similarity index 100% rename from trixy-macros/src/generate.old/lua_wrapper/lua_functions_to_globals/mod.rs rename to src/macros/generate.old/lua_wrapper/lua_functions_to_globals/mod.rs diff --git a/trixy-macros/src/generate.old/lua_wrapper/mod.rs b/src/macros/generate.old/lua_wrapper/mod.rs similarity index 100% rename from trixy-macros/src/generate.old/lua_wrapper/mod.rs rename to src/macros/generate.old/lua_wrapper/mod.rs diff --git a/trixy-macros/src/generate.old/lua_wrapper/rust_wrapper_functions/mod.rs b/src/macros/generate.old/lua_wrapper/rust_wrapper_functions/mod.rs similarity index 100% rename from trixy-macros/src/generate.old/lua_wrapper/rust_wrapper_functions/mod.rs rename to src/macros/generate.old/lua_wrapper/rust_wrapper_functions/mod.rs diff --git a/trixy-macros/src/generate.old/mod.rs b/src/macros/generate.old/mod.rs similarity index 100% rename from trixy-macros/src/generate.old/mod.rs rename to src/macros/generate.old/mod.rs diff --git a/trixy-macros/src/generate/c_api/header/mod.rs b/src/macros/generate/auxiliary/c/mod.rs similarity index 80% rename from trixy-macros/src/generate/c_api/header/mod.rs rename to src/macros/generate/auxiliary/c/mod.rs index 4ff5d38..f7ee0ff 100644 --- a/trixy-macros/src/generate/c_api/header/mod.rs +++ b/src/macros/generate/auxiliary/c/mod.rs @@ -23,14 +23,8 @@ //! It works by firstly listing the functions and then by grouping them into structures, effectively //! simulating namespaces in c. -use proc_macro2::TokenStream as TokenStream2; -use quote::quote; -use trixy_parser::command_spec::{Attribute, CommandSpec, NamedType}; -use trixy_types::header_names; - use crate::{ - config::TrixyConfig, - generate::{c_api::type_to_c, identifier_to_rust}, + macros::config::trixy::TrixyConfig, parser::command_spec::CommandSpec, types::header_names, }; mod pure_header; @@ -109,19 +103,3 @@ pub fn generate(trixy: &CommandSpec, _config: &TrixyConfig) -> String { END_HEADER_GUARD ) } - -fn attribute_to_doc_comment(attribute: &Attribute) -> String { - if let Attribute::doc(doc_comment) = attribute { - format!("/** {}\n*/", doc_comment) - } else { - "".to_owned() - } -} - -fn named_type_to_c(named_type: &NamedType) -> TokenStream2 { - let ident = identifier_to_rust(&named_type.name); - let c_type = type_to_c(&named_type.r#type, false); - quote! { - #c_type #ident - } -} diff --git a/trixy-parser/example/comments.tri b/src/macros/generate/auxiliary/c/pure_header.rs similarity index 52% rename from trixy-parser/example/comments.tri rename to src/macros/generate/auxiliary/c/pure_header.rs index b99a2af..fe7a795 100644 --- a/trixy-parser/example/comments.tri +++ b/src/macros/generate/auxiliary/c/pure_header.rs @@ -19,14 +19,31 @@ * If not, see . */ -fn print(message: String); +use crate::parser::command_spec::{CommandSpec, Enumeration, Structure}; -/// First doc comment -// Some more text -mod trinitrix { - /// Second doc comment - fn hi(name: String) -> String; +pub fn generate(trixy: &CommandSpec) -> String { + let functions: String = trixy + .functions + .iter() + .map(|r#fn| r#fn.to_auxiliary_c(&[])) + .collect(); + let namespaces: String = trixy + .namespaces + .iter() + .map(|nasp| nasp.to_auxiliary_c(&vec![])) + .collect(); + let structures: String = trixy + .structures + .iter() + .map(Structure::to_auxiliary_c) + .collect(); + let enumerations: String = trixy + .enumerations + .iter() + .map(Enumeration::to_auxiliary_c) + .collect(); + format!( + "{}\n{}\n{}\n{}", + enumerations, structures, functions, namespaces + ) } - -// That's a flat out lie, but it results in a rather nice syntax highlight compared to nothing: -// vim: syntax=rust diff --git a/trixy-parser/example/multiple.tri b/src/macros/generate/auxiliary/c/structs_init.rs similarity index 70% rename from trixy-parser/example/multiple.tri rename to src/macros/generate/auxiliary/c/structs_init.rs index ff59b64..c9b851b 100644 --- a/trixy-parser/example/multiple.tri +++ b/src/macros/generate/auxiliary/c/structs_init.rs @@ -19,15 +19,15 @@ * If not, see . */ -fn print(message: CommandTransferValue); +use proc_macro2::TokenStream as TokenStream2; -mod trinitrix { - fn hi(name: String) -> String; +use crate::parser::command_spec::CommandSpec; + +pub fn generate(trixy: &CommandSpec) -> String { + let struct_initializer: TokenStream2 = trixy + .namespaces + .iter() + .map(|nasp| nasp.to_auxiliary_c_full_struct_init(&vec![])) + .collect(); + struct_initializer.to_string() } - -mod trinitrix { - fn ho(name: String, name2: String) -> String; -} - -// That's a flat out lie, but it results in a rather nice syntax highlight compared to nothing: -// vim: syntax=rust diff --git a/trixy-parser/example/failing_types.tri b/src/macros/generate/auxiliary/c/typedef.rs similarity index 72% rename from trixy-parser/example/failing_types.tri rename to src/macros/generate/auxiliary/c/typedef.rs index a4c5ca2..37b233b 100644 --- a/trixy-parser/example/failing_types.tri +++ b/src/macros/generate/auxiliary/c/typedef.rs @@ -19,12 +19,15 @@ * If not, see . */ -struct Callback { - func: Function, - timeout: Integer, +use crate::parser::command_spec::{CommandSpec, Namespace}; + +pub fn generate(trixy: &CommandSpec) -> String { + let type_defs: String = trixy + .namespaces + .iter() + .rev() + .map(Namespace::to_auxiliary_c_full_typedef) + .collect::>() + .join("\n"); + type_defs.to_string() } - -fn execute_callback(callback: Name); - -// That's a flat out lie, but it results in a rather nice syntax highlight compared to nothing: -// vim: syntax=rust diff --git a/src/macros/generate/auxiliary/mod.rs b/src/macros/generate/auxiliary/mod.rs new file mode 100644 index 0000000..614991f --- /dev/null +++ b/src/macros/generate/auxiliary/mod.rs @@ -0,0 +1,47 @@ +use std::io::Write; +use std::process::{Command, Stdio}; + +use crate::{ + macros::{config::trixy::TrixyConfig, VIM_LINE_C}, + parser::command_spec::CommandSpec, +}; + +pub mod c; + +// FIXME(@soispha): We made to promise to not panic (outside of the toplevel generate function). +// Therefore these panics here should be avoided. <2024-03-25> +pub fn format_c(input: String) -> String { + let mut clang_format = Command::new("clang-format") + .stdin(Stdio::piped()) + .stdout(Stdio::piped()) + .args(["--style", "GNU"]) + .spawn() + .expect("`clang-format` is a dependency and should be provided by the user"); + + let mut stdin = clang_format + .stdin + .take() + .expect("We should be able to take stdin"); + std::thread::spawn(move || { + stdin + .write_all(input.as_bytes()) + .expect("Should be able to write to stdin"); + }); + + let output = clang_format + .wait_with_output() + .expect("Should be able to read stdout"); + String::from_utf8(output.stdout).expect("The input was utf8, it should not have changed") +} + +pub fn generate(trixy: &CommandSpec, config: &TrixyConfig) -> String { + let c_code = c::generate(trixy, config); + let c_code = format_c(c_code); + + format!( + " +{}\n{} +", + c_code, VIM_LINE_C + ) +} diff --git a/src/macros/generate/convert/auxiliary/c/attribute/mod.rs b/src/macros/generate/convert/auxiliary/c/attribute/mod.rs new file mode 100644 index 0000000..9ed3ede --- /dev/null +++ b/src/macros/generate/convert/auxiliary/c/attribute/mod.rs @@ -0,0 +1,11 @@ +use crate::parser::command_spec::Attribute; + +impl Attribute { + pub fn to_auxiliary_c(&self) -> String { + if let Attribute::doc(doc_comment) = self { + format!("/**\n {}\n*/", doc_comment) + } else { + "".to_owned() + } + } +} diff --git a/src/macros/generate/convert/auxiliary/c/enumeration/mod.rs b/src/macros/generate/convert/auxiliary/c/enumeration/mod.rs new file mode 100644 index 0000000..3a28455 --- /dev/null +++ b/src/macros/generate/convert/auxiliary/c/enumeration/mod.rs @@ -0,0 +1,36 @@ +use crate::parser::command_spec::{Attribute, DocIdentifier, Enumeration}; + +impl Enumeration { + pub fn to_auxiliary_c(&self) -> String { + let doc_comments: String = self + .attributes + .iter() + .map(Attribute::to_auxiliary_c) + .collect::(); + let ident = &self.identifier.to_auxiliary_c(); + let states = self + .states + .iter() + .map(DocIdentifier::to_auxiliary_c) + .collect::(); + let states = if self.states.is_empty() { + "/** + * This enum does not have variants on the rust side + * to work around c limitiation with variant-less enums + * we added a `__never` variant: + */ + __never," + .to_owned() + } else { + states + }; + format!( + " + {} + {} {{ + {} + }};", + doc_comments, ident, states + ) + } +} diff --git a/src/macros/generate/convert/auxiliary/c/function/mod.rs b/src/macros/generate/convert/auxiliary/c/function/mod.rs new file mode 100644 index 0000000..7bed115 --- /dev/null +++ b/src/macros/generate/convert/auxiliary/c/function/mod.rs @@ -0,0 +1,77 @@ +use proc_macro2::TokenStream as TokenStream2; +use quote::quote; + +use crate::parser::command_spec::{Attribute, Function, Identifier, NamedType}; + +impl Function { + pub fn to_auxiliary_c(&self, namespaces: &[&Identifier]) -> String { + let doc_comments: String = self + .attributes + .iter() + .map(Attribute::to_auxiliary_c) + .collect::(); + let ident = self.identifier.to_c_with_path(namespaces); + let inputs: Vec = self.inputs.iter().map(NamedType::to_auxiliary_c).collect(); + + let function_output = if let Some(out) = &self.output { + let type_name = &out.to_auxiliary_c(true); + let comma = if !inputs.is_empty() { + quote! { + , + } + } else { + TokenStream2::default() + }; + quote! { + #type_name trixy_output #comma + } + } else { + TokenStream2::default() + }; + + let output = quote! { + extern int #ident(#function_output #(#inputs),*); + }; + format!("{}{}\n", doc_comments, output) + } + + pub fn to_auxiliary_c_namespace_init(&self, namespaces: &[&Identifier]) -> TokenStream2 { + let ident = &self.identifier.to_rust(); + let full_ident = &self.identifier.to_c_with_path(namespaces); + + quote! { + . #ident = #full_ident, + } + } + + pub fn to_auxiliary_c_typedef(&self) -> TokenStream2 { + let ident = self.identifier.to_rust(); + + let (output, output_comma) = if let Some(output) = &self.output { + let output = output.to_auxiliary_c(true); + (quote! { #output }, quote! {,}) + } else { + (TokenStream2::default(), TokenStream2::default()) + }; + + let inputs: TokenStream2 = if self.inputs.is_empty() && output.is_empty() { + quote! { void } + } else if !self.inputs.is_empty() && !output.is_empty() { + let inputs: Vec = self + .inputs + .iter() + .map(|named_type| &named_type.r#type) + .map(|r#type| r#type.to_auxiliary_c(false)) + .collect(); + quote! { + #output_comma #(#inputs),* + } + } else { + TokenStream2::default() + }; + + quote! { + int (* #ident ) (#output #inputs); + } + } +} diff --git a/src/macros/generate/convert/auxiliary/c/identifier/doc_identifier.rs b/src/macros/generate/convert/auxiliary/c/identifier/doc_identifier.rs new file mode 100644 index 0000000..8901839 --- /dev/null +++ b/src/macros/generate/convert/auxiliary/c/identifier/doc_identifier.rs @@ -0,0 +1,13 @@ +use crate::parser::command_spec::{Attribute, DocIdentifier}; + +impl DocIdentifier { + pub fn to_auxiliary_c(&self) -> String { + let doc_comments: String = self + .attributes + .iter() + .map(Attribute::to_auxiliary_c) + .collect::(); + let ident = &self.name; + format!("{}{},", doc_comments, ident) + } +} diff --git a/src/macros/generate/convert/auxiliary/c/identifier/mod.rs b/src/macros/generate/convert/auxiliary/c/identifier/mod.rs new file mode 100644 index 0000000..321bded --- /dev/null +++ b/src/macros/generate/convert/auxiliary/c/identifier/mod.rs @@ -0,0 +1,42 @@ +use convert_case::{Case, Casing}; +use proc_macro2::TokenStream as TokenStream2; +use quote::quote; + +use crate::parser::command_spec::{Identifier, Variant}; + +mod doc_identifier; + +// +// pub use doc_identifier::*; + +impl Identifier { + pub fn to_auxiliary_c(&self) -> TokenStream2 { + let ident = self.to_rust_pascalized(); + match &self.variant { + Variant::Structure { .. } => { + let ident = self.to_rust_pascalized(); + quote! { + struct #ident + } + } + Variant::Enumeration { .. } => { + quote! { + enum #ident + } + } + Variant::Primitive => match self.name.to_case(Case::Snake).as_str() { + "string" => { + quote! { + const char* + } + } + other => { + todo!("'{}' is not yet supported", other) + } + }, + other => { + unimplemented!("{:#?}", other) + } + } + } +} diff --git a/src/macros/generate/convert/auxiliary/c/mod.rs b/src/macros/generate/convert/auxiliary/c/mod.rs new file mode 100644 index 0000000..1dee304 --- /dev/null +++ b/src/macros/generate/convert/auxiliary/c/mod.rs @@ -0,0 +1,16 @@ +mod attribute; +mod enumeration; +mod function; +mod identifier; +mod namespace; +mod structure; +mod r#type; + + +// pub use attribute::*; +// pub use enumeration::*; +// pub use function::*; +// pub use identifier::*; +// pub use namespace::*; +// pub use structure::*; +// pub use r#type::*; diff --git a/src/macros/generate/convert/auxiliary/c/namespace/mod.rs b/src/macros/generate/convert/auxiliary/c/namespace/mod.rs new file mode 100644 index 0000000..2361a87 --- /dev/null +++ b/src/macros/generate/convert/auxiliary/c/namespace/mod.rs @@ -0,0 +1,122 @@ +use proc_macro2::TokenStream as TokenStream2; +use quote::{format_ident, quote}; + +use crate::parser::command_spec::{ + Attribute, Enumeration, Function, Identifier, Namespace, Structure, +}; + +impl Namespace { + pub fn to_auxiliary_c(&self, namespaces: &Vec<&Identifier>) -> String { + let mut nasps = namespaces.clone(); + nasps.push(&self.name); + + let structures: String = self + .structures + .iter() + .map(Structure::to_auxiliary_c) + .collect::>() + .join("\n"); + let enumerations: String = self + .enumerations + .iter() + .map(Enumeration::to_auxiliary_c) + .collect::>() + .join("\n"); + let functions: String = self + .functions + .iter() + .map(|r#fn| r#fn.to_auxiliary_c(&nasps)) + .collect::>() + .join("\n"); + let namespaces: String = self + .namespaces + .iter() + .map(|nasp| nasp.to_auxiliary_c(&nasps)) + .collect(); + + format! {"{}\n{}\n{}\n{}", enumerations, structures, functions, namespaces} + } + + pub fn to_auxiliary_c_full_struct_init(&self, namespaces: &Vec<&Identifier>) -> TokenStream2 { + let mut input_namespaces = namespaces.clone(); + input_namespaces.push(&self.name); + + let ident = &self.name.to_rust(); + let type_ident = ident.clone(); + + let functions: TokenStream2 = self + .functions + .iter() + .map(|r#fn| r#fn.to_auxiliary_c_namespace_init(&input_namespaces)) + .collect(); + let namespaces: TokenStream2 = self + .namespaces + .iter() + .map(Namespace::to_auxiliary_c_namespace_init) + .collect(); + + let next_namespace: TokenStream2 = self + .namespaces + .iter() + .map(|nasp| nasp.to_auxiliary_c_full_struct_init(&input_namespaces)) + .collect(); + + quote! { + #next_namespace + + const struct #type_ident #ident = { + #functions + #namespaces + }; + } + } + pub fn to_auxiliary_c_typedef(&self) -> TokenStream2 { + let ident = &self.name.to_rust(); + let type_ident = ident.clone(); + + quote! { + struct #type_ident #ident; + } + } + pub fn to_auxiliary_c_full_typedef(&self) -> String { + let ident = format_ident!("{}", self.name.name); + let doc_comments = self + .attributes + .iter() + .map(Attribute::to_auxiliary_c) + .collect::(); + + let functions: TokenStream2 = self + .functions + .iter() + .map(Function::to_auxiliary_c_typedef) + .collect(); + let namespaces: TokenStream2 = self + .namespaces + .iter() + .map(Namespace::to_auxiliary_c_typedef) + .collect(); + let next_namespace: String = self + .namespaces + .iter() + .map(Namespace::to_auxiliary_c_full_typedef) + .collect::>() + .join("\n"); + + let namespace = quote! { + struct #ident { + #functions + #namespaces + }; + }; + format! {"{}\n{}{}\n", next_namespace, doc_comments, namespace} + } + + pub fn to_auxiliary_c_namespace_init(&self) -> TokenStream2 { + let ident = self.name.to_rust(); + + quote! { + . #ident = #ident , + } + } +} diff --git a/src/macros/generate/convert/auxiliary/c/structure/mod.rs b/src/macros/generate/convert/auxiliary/c/structure/mod.rs new file mode 100644 index 0000000..f533b45 --- /dev/null +++ b/src/macros/generate/convert/auxiliary/c/structure/mod.rs @@ -0,0 +1,25 @@ +use crate::parser::command_spec::{Attribute, DocNamedType, Structure}; + +impl Structure { + pub fn to_auxiliary_c(&self) -> String { + let doc_comments: String = self + .attributes + .iter() + .map(Attribute::to_auxiliary_c) + .collect::(); + let ident = self.identifier.to_rust(); + let contents = self + .contents + .iter() + .map(DocNamedType::to_auxiliary_c) + .collect::(); + format!( + " + {} + {} {{ + {} + }};", + doc_comments, ident, contents + ) + } +} diff --git a/src/macros/generate/convert/auxiliary/c/type/doc_named_type.rs b/src/macros/generate/convert/auxiliary/c/type/doc_named_type.rs new file mode 100644 index 0000000..21deaf4 --- /dev/null +++ b/src/macros/generate/convert/auxiliary/c/type/doc_named_type.rs @@ -0,0 +1,14 @@ +use crate::parser::command_spec::{Attribute, DocNamedType}; + +impl DocNamedType { + pub fn to_auxiliary_c(&self) -> String { + let doc_comments: String = self + .attributes + .iter() + .map(Attribute::to_auxiliary_c) + .collect::(); + let ident = &self.name.to_rust(); + let r#type = self.r#type.to_auxiliary_c(false); + format!("{}{} {};", doc_comments, r#type, ident) + } +} diff --git a/src/macros/generate/convert/auxiliary/c/type/mod.rs b/src/macros/generate/convert/auxiliary/c/type/mod.rs new file mode 100644 index 0000000..6ee7989 --- /dev/null +++ b/src/macros/generate/convert/auxiliary/c/type/mod.rs @@ -0,0 +1,52 @@ +use proc_macro2::TokenStream as TokenStream2; +use quote::quote; + +use crate::parser::command_spec::{NamedType, Type}; + +mod doc_named_type; +mod named_type; + +// pub use doc_named_type::*; +// pub use named_type::*; + +impl Type { + pub fn to_auxiliary_c(&self, is_output: bool) -> TokenStream2 { + match self { + Type::Typical { + identifier, + generic_args: _, + } => { + let ident = identifier.to_auxiliary_c(); + let output = if is_output { + quote! { + * + } + } else { + TokenStream2::default() + }; + quote! { + #ident #output + } + } + Type::Function { inputs, output } => { + // We cannot return a function pointer as of yet, thus guard against it + assert_eq!(is_output, false); + + let output = if let Some(output) = output { + output.to_auxiliary_c(false) + } else { + quote! { + void + } + }; + + let inputs: Vec = + inputs.iter().map(NamedType::to_auxiliary_c).collect(); + + quote! { + #output (*name) (#(#inputs),*) + } + } + } + } +} diff --git a/src/macros/generate/convert/auxiliary/c/type/named_type.rs b/src/macros/generate/convert/auxiliary/c/type/named_type.rs new file mode 100644 index 0000000..85a506b --- /dev/null +++ b/src/macros/generate/convert/auxiliary/c/type/named_type.rs @@ -0,0 +1,14 @@ +use proc_macro2::TokenStream as TokenStream2; +use quote::quote; + +use crate::parser::command_spec::NamedType; + +impl NamedType { + pub fn to_auxiliary_c(&self) -> TokenStream2 { + let ident = self.name.to_rust(); + let c_type = self.r#type.to_auxiliary_c(false); + quote! { + #c_type #ident + } + } +} diff --git a/src/macros/generate/convert/auxiliary/mod.rs b/src/macros/generate/convert/auxiliary/mod.rs new file mode 100644 index 0000000..ca8f836 --- /dev/null +++ b/src/macros/generate/convert/auxiliary/mod.rs @@ -0,0 +1 @@ +mod c; diff --git a/src/macros/generate/convert/host/c/enumeration/mod.rs b/src/macros/generate/convert/host/c/enumeration/mod.rs new file mode 100644 index 0000000..768fe46 --- /dev/null +++ b/src/macros/generate/convert/host/c/enumeration/mod.rs @@ -0,0 +1,47 @@ +use proc_macro2::TokenStream as TokenStream2; +use quote::quote; + +use crate::parser::command_spec::{DocIdentifier, Enumeration}; + +impl Enumeration { + pub fn to_c(&self) -> TokenStream2 { + let ident = &self.identifier.to_c(); + let doc_comments: TokenStream2 = self + .attributes + .iter() + .map(|attr| attr.to_rust(&self.identifier)) + .collect(); + + let states: Vec = self.states.iter().map(DocIdentifier::to_rust).collect(); + let paired_type = { + let path = &self + .identifier + .variant + .to_rust_path() + .expect("This should always be some for enums"); + + let ident = &self.identifier.to_rust(); + if path.is_empty() { + quote! { + crate :: #ident + } + } else { + quote! { + #path :: #ident + } + } + }; + + let convertible = &self.derive_from_c_paried_rust(&paired_type); + quote! { + #doc_comments + #[allow(non_camel_case_types)] + #[repr(C)] + #[derive(Debug)] + pub enum #ident { + #(#states),* + } + #convertible + } + } +} diff --git a/src/macros/generate/convert/host/c/function/mod.rs b/src/macros/generate/convert/host/c/function/mod.rs new file mode 100644 index 0000000..8a14131 --- /dev/null +++ b/src/macros/generate/convert/host/c/function/mod.rs @@ -0,0 +1,146 @@ +use proc_macro2::TokenStream as TokenStream2; +use quote::{format_ident, quote}; + +use crate::{ + macros::config::trixy::TrixyConfig, + parser::command_spec::{Function, Identifier, NamedType, Namespace}, +}; + +impl Function { + pub fn to_c(&self, config: &TrixyConfig, namespaces: &Vec<&Identifier>) -> TokenStream2 { + let ident = self.identifier.to_c_with_path(namespaces); + let inputs: Vec = self + .inputs + .iter() + // FIXME(@soispha): This was `named_type_to_rust_trixy` <2024-03-25> + .map(NamedType::to_rust) + .collect(); + + let callback_function = format_ident!("{}", config.callback_function); + + let command_value: TokenStream2 = self.identifier.to_c_with_path(&namespaces); + + if let Some(r#type) = &self.output { + let output_ident = r#type.to_c(); + quote! { + #[no_mangle] + pub extern "C" fn #ident(output: *mut #output_ident, #(#inputs),*) -> core::ffi::c_int { + let output_val: #output_ident = { + let (tx, rx) = trixy::oneshot::channel(); + #callback_function (#command_value); + let recv = rx.recv().expect("The channel should not be closed until this value is received"); + recv.into() + }; + unsafe { + std::ptr::write(output, output_val); + } + return 1; + } + } + } else { + quote! { + #[no_mangle] + pub extern "C" fn #ident(#(#inputs),*) -> core::ffi::c_int { + #callback_function (#command_value); + return 1; + } + } + } + } + + /// Turns a function in namespaces to the generated host enum path: + /// *trixy code:* + /// ```text + /// fn fn_alone(); + /// mod one { + /// fn fn_one(); + /// mod two { + /// fn fn_two(input: String); + /// } + /// } + /// ``` + /// *rust enum path for fn_alone:* + /// ``` + /// Commands::fn_alone + /// ``` + /// *rust enum path for fn_one:* + /// ``` + /// // `Commands` is just the name for the top-level namespace + /// Commands::One(one::One( + /// One::fn_one + /// )) + /// ``` + /// *rust enum path for fn_two:* + /// ``` + /// Commands::One(one::One( + /// one::two::Two(one::two::Two( + /// Two::fn_two {input: String} + /// )))) + /// ``` + pub fn to_rust_path(&self, namespaces: &Vec<&Identifier>) -> TokenStream2 { + let function_ident = self.to_rust_identifier(NamedType::to_rust_assignment, |_| { + quote! { + // This is defined in the outer call + tx + } + }); + + if namespaces.is_empty() { + quote! { + Commands:: #function_ident + } + } else { + let nasp_pascal_ident = namespaces.last().expect("We checked").to_rust_pascalized(); + + let namespace_path = Namespace::to_rust_path(namespaces); + + let function_call = quote! { + #namespace_path :: #nasp_pascal_ident :: #function_ident + }; + + let output: TokenStream2 = namespaces + .iter() + .enumerate() + .rev() + .fold(function_call, |acc, (index, nasp)| { + nasp_path_one_part(nasp, &acc, &namespaces, index) + }); + + output + } + } +} +/// This function add a namespace component to the [input] value like so: +/// (taking the example from the [function_path_to_rust] function) +/// ```text +/// one::two::Two::fn_two [= ] +/// -> +/// one::One::Two() [= ] +/// -> +/// Commands::One() [= ] +/// ``` +fn nasp_path_one_part( + current_nasp: &Identifier, + input: &TokenStream2, + namespaces: &Vec<&Identifier>, + index: usize, +) -> TokenStream2 { + let namespaces_to_do = &namespaces[..index]; + + let ident_pascal = current_nasp.to_rust_pascalized(); + + if index == 0 { + quote! { + Commands :: #ident_pascal ( #input ) + } + } else { + let ident_pascal_next = namespaces_to_do + .last() + .expect("We checked the index") + .to_rust_pascalized(); + let namespace_path = Namespace::to_rust_path(namespaces_to_do); + quote! { + #namespace_path :: #ident_pascal_next :: #ident_pascal ( #input ) + } + } +} diff --git a/src/macros/generate/convert/host/c/identifier/mod.rs b/src/macros/generate/convert/host/c/identifier/mod.rs new file mode 100644 index 0000000..4fcc9cc --- /dev/null +++ b/src/macros/generate/convert/host/c/identifier/mod.rs @@ -0,0 +1,26 @@ +use proc_macro2::TokenStream as TokenStream2; +use quote::{format_ident, ToTokens}; + +use crate::parser::command_spec::Identifier; + +impl Identifier { + pub fn to_c_with_path(&self, namespaces: &[&Identifier]) -> TokenStream2 { + let namespace_str = namespaces.iter().fold(String::default(), |acc, nasp| { + if acc.is_empty() { + nasp.name.clone() + } else { + format!("{}_{}", acc, nasp.name) + } + }); + + if namespace_str.is_empty() { + format_ident!("{}", self.name).to_token_stream() + } else { + format_ident!("{}_{}", namespace_str, self.name).to_token_stream() + } + } + + pub fn to_c(&self) -> TokenStream2 { + format_ident!("{}_c", self.name).to_token_stream() + } +} diff --git a/src/macros/generate/convert/host/c/mod.rs b/src/macros/generate/convert/host/c/mod.rs new file mode 100644 index 0000000..894a7c3 --- /dev/null +++ b/src/macros/generate/convert/host/c/mod.rs @@ -0,0 +1,15 @@ +mod enumeration; +mod function; +mod identifier; +mod namespace; +mod structure; +mod r#type; +mod variant; + +// pub use enumeration::*; +// pub use function::*; +// pub use identifier::*; +// pub use namespace::*; +// pub use r#type::*; +// pub use structure::*; +// pub use variant::*; diff --git a/src/macros/generate/convert/host/c/namespace/mod.rs b/src/macros/generate/convert/host/c/namespace/mod.rs new file mode 100644 index 0000000..e990d19 --- /dev/null +++ b/src/macros/generate/convert/host/c/namespace/mod.rs @@ -0,0 +1,36 @@ +use proc_macro2::TokenStream as TokenStream2; +use quote::quote; + +use crate::{ + macros::config::trixy::TrixyConfig, + parser::command_spec::{Enumeration, Identifier, Namespace, Structure}, +}; + +impl Namespace { + pub fn to_c(&self, config: &TrixyConfig, namespaces: &Vec<&Identifier>) -> TokenStream2 { + let ident = &self.name.to_c(); + let mut namespaces = namespaces.clone(); + namespaces.push(&self.name); + + let functions: TokenStream2 = self + .functions + .iter() + .map(|r#fn| r#fn.to_c(&config, &namespaces)) + .collect(); + let additional_functions: TokenStream2 = self + .namespaces + .iter() + .map(|nasp| nasp.to_c(&config, &namespaces)) + .collect(); + let structures: TokenStream2 = self.structures.iter().map(Structure::to_c).collect(); + let enumerations: TokenStream2 = self.enumerations.iter().map(Enumeration::to_c).collect(); + quote! { + pub mod #ident { + #enumerations + #structures + } + #functions + #additional_functions + } + } +} diff --git a/src/macros/generate/convert/host/c/structure/mod.rs b/src/macros/generate/convert/host/c/structure/mod.rs new file mode 100644 index 0000000..5932da5 --- /dev/null +++ b/src/macros/generate/convert/host/c/structure/mod.rs @@ -0,0 +1,28 @@ +use proc_macro2::TokenStream as TokenStream2; +use quote::quote; + +use crate::parser::command_spec::{DocNamedType, Structure}; + +impl Structure { + pub fn to_c(&self) -> TokenStream2 { + let ident = &self.identifier.to_c(); + let doc_comments: TokenStream2 = self + .attributes + .iter() + .map(|attr| attr.to_rust(&self.identifier)) + .collect(); + + let contents: Vec = self.contents.iter().map(DocNamedType::to_c).collect(); + + // TODO: Convertible <2024-03-08> + quote! { + #doc_comments + #[allow(non_camel_case_types)] + #[repr(C)] + #[derive(Debug)] + pub struct #ident { + #(#contents),* + } + } + } +} diff --git a/src/macros/generate/convert/host/c/type/doc_named_type.rs b/src/macros/generate/convert/host/c/type/doc_named_type.rs new file mode 100644 index 0000000..4a0a8ef --- /dev/null +++ b/src/macros/generate/convert/host/c/type/doc_named_type.rs @@ -0,0 +1,19 @@ +use proc_macro2::TokenStream as TokenStream2; +use quote::quote; + +use crate::parser::command_spec::{DocNamedType, NamedType}; + +impl DocNamedType { + pub fn to_c(&self) -> TokenStream2 { + let doc_comments: TokenStream2 = self + .attributes + .iter() + .map(|attr| attr.to_rust(&self.name)) + .collect(); + let named_type = &Into::::into(self).to_c(); + quote! { + #doc_comments + pub #named_type + } + } +} diff --git a/src/macros/generate/convert/host/c/type/mod.rs b/src/macros/generate/convert/host/c/type/mod.rs new file mode 100644 index 0000000..26854dd --- /dev/null +++ b/src/macros/generate/convert/host/c/type/mod.rs @@ -0,0 +1,100 @@ +use proc_macro2::TokenStream as TokenStream2; +use quote::quote; + +use crate::parser::command_spec::{Identifier, NamedType, Type}; + +mod doc_named_type; +mod named_type; + +// pub use doc_named_type::*; +// pub use named_type::*; + +impl Type { + pub fn to_c(&self) -> TokenStream2 { + match self { + Type::Typical { + identifier, + generic_args, + } => Type::to_c_typical(&identifier, &generic_args), + Type::Function { inputs, output } => Type::to_c_function(&inputs, &output), + } + } + + pub fn to_c_function(inputs: &[NamedType], output: &Option>) -> TokenStream2 { + Type::to_rust_function(&inputs, &output) + } + + fn to_c_typical(identifier: &Identifier, generic_args: &Vec) -> TokenStream2 { + let trixy_build_in_types: Vec<&str> = crate::types::BASE_TYPES + .iter() + .filter_map(|(name, _)| { + if name == &identifier.name { + Some(*name) + } else { + None + } + }) + .collect(); + + if trixy_build_in_types.is_empty() { + // The types was specified in the api.tri file + + let ident = identifier.to_c(); + + let namespaces_path = &identifier.variant.to_c_path(); + let nasp_path = if namespaces_path.is_empty() { + quote! { + crate :: + } + } else { + let path = namespaces_path; + quote! { + #path :: + } + }; + quote! { + #nasp_path #ident + } + } else { + debug_assert_eq!(trixy_build_in_types.len(), 1); + + let type_name = trixy_build_in_types + .first() + .expect("The names should not be dublicated, this should be the only value"); + + match *type_name { + "Result" => { + let ident_ok = &generic_args.first().expect("This is a result").to_c(); + let ident_err = &generic_args.last().expect("This is a result").to_c(); + quote! { + // eg: as Convertible>::Ptr, + as Convertible>::Ptr + } + } + "Option" => { + let value = generic_args + .first() + .expect("An option does only have one arg") + .to_rust(); + quote! { + *const #value + } + } + _ => { + let ident = &identifier.to_rust(); + let generics: TokenStream2 = { + let generics: Vec = + generic_args.iter().map(|val| val.to_c()).collect(); + quote! { + <#(#generics),*> + } + }; + + quote! { + trixy::types:: #ident #generics + } + } + } + } + } +} diff --git a/src/macros/generate/convert/host/c/type/named_type.rs b/src/macros/generate/convert/host/c/type/named_type.rs new file mode 100644 index 0000000..7d98870 --- /dev/null +++ b/src/macros/generate/convert/host/c/type/named_type.rs @@ -0,0 +1,33 @@ +use proc_macro2::TokenStream as TokenStream2; +use quote::quote; + +use crate::parser::command_spec::NamedType; + +impl NamedType { + pub fn to_c(&self) -> TokenStream2 { + let ident = self.name.to_rust(); + let r#type = self.r#type.to_c(); + quote! { + #ident : #r#type + } + } + pub fn to_rust_c_assignment(&self) -> TokenStream2 { + let ident = self.name.to_rust(); + let type_ident = self.r#type.to_c(); + quote! { + #ident : #type_ident + } + } + pub fn to_rust_assignment(&self) -> TokenStream2 { + let ident = self.name.to_rust(); + quote! { + #ident : match #ident.try_into() { + Ok(ok) => ok, + Err(err) => { + trixy::types::traits::errno::set(err); + return 0; + } + } + } + } +} diff --git a/src/macros/generate/convert/host/c/variant/mod.rs b/src/macros/generate/convert/host/c/variant/mod.rs new file mode 100644 index 0000000..c2ee82e --- /dev/null +++ b/src/macros/generate/convert/host/c/variant/mod.rs @@ -0,0 +1,36 @@ +use proc_macro2::TokenStream as TokenStream2; + +use crate::parser::command_spec::{Identifier, Namespace, Variant}; + +impl Variant { + pub fn to_c_path(&self) -> TokenStream2 { + fn mangle_namespace_name(vec: &Vec) -> Vec { + vec.into_iter() + .map(|ident| { + if "" == &ident.name && ident.variant == Variant::RootNamespace { + // The [`namespaces_to_path_expansive`] function already deals with + // [`RootNamespace`] variants, so we just leave that as is + ident.clone() + } else { + Identifier { + name: ident.to_c().to_string(), + variant: ident.variant.clone(), + } + } + }) + .collect() + } + + let main_namespace; + match self { + Variant::Structure { namespace } => { + main_namespace = mangle_namespace_name(namespace); + } + Variant::Enumeration { namespace } => { + main_namespace = mangle_namespace_name(namespace); + } + _ => unreachable!("This should never be called"), + } + Namespace::to_rust_path_owned(&main_namespace[..]) + } +} diff --git a/src/macros/generate/convert/host/mod.rs b/src/macros/generate/convert/host/mod.rs new file mode 100644 index 0000000..65d76bf --- /dev/null +++ b/src/macros/generate/convert/host/mod.rs @@ -0,0 +1,2 @@ +pub mod rust; +pub mod c; diff --git a/src/macros/generate/convert/host/rust/attribute/mod.rs b/src/macros/generate/convert/host/rust/attribute/mod.rs new file mode 100644 index 0000000..a486fc7 --- /dev/null +++ b/src/macros/generate/convert/host/rust/attribute/mod.rs @@ -0,0 +1,22 @@ +use proc_macro2::TokenStream as TokenStream2; +use quote::quote; + +use crate::parser::command_spec::{Attribute, Identifier}; + +impl Attribute { + pub fn to_rust(&self, _target: &Identifier) -> TokenStream2 { + match self { + Attribute::doc(comment) => quote! { + #[doc = #comment] + }, + Attribute::error => quote! { + // We simply use thiserror here + #[derive(trixy::__private::thiserror::Error)] + }, + Attribute::msg(msg) => quote! { + #[error(#msg)] + }, + Attribute::derive(_) => unimplemented!("Derive is not used as of now"), + } + } +} diff --git a/src/macros/generate/convert/host/rust/derive/enumeration.rs b/src/macros/generate/convert/host/rust/derive/enumeration.rs new file mode 100644 index 0000000..111198a --- /dev/null +++ b/src/macros/generate/convert/host/rust/derive/enumeration.rs @@ -0,0 +1,48 @@ +use proc_macro2::TokenStream as TokenStream2; +use quote::{quote, ToTokens}; + +use crate::parser::command_spec::{Enumeration, Identifier}; + +impl Enumeration { + fn derive_from_paired( + &self, + ident_self: &TokenStream2, + ident_paired: &TokenStream2, + paired_type: &TokenStream2, + ) -> TokenStream2 { + let match_lines: TokenStream2 = self + .states + .iter() + .map(|state| { + let name = Into::::into(state).to_rust(); + quote! { + #ident_paired :: #name => Self :: #name, + } + }) + .collect(); + quote! { + impl From<#paired_type> for #ident_self { + fn from(value: #paired_type) -> Self { + match value { + #match_lines + } + } + } + } + } + /// This function generates the `From` trait implementation for a given c enumeration + pub fn derive_from_c_paried_rust(&self, paired_type: &TokenStream2) -> TokenStream2 { + let c_ident = &self.identifier.to_c(); + let ident = &self.identifier.to_rust(); + + self.derive_from_paired(&c_ident.into_token_stream(), &ident, &paired_type) + } + + /// This function generates the `From` trait implementation for a given rust enumeration + pub fn derive_from_rust_paried_c(&self, paired_type: &TokenStream2) -> TokenStream2 { + let c_ident = &self.identifier.to_c(); + let ident = &self.identifier.to_rust(); + + self.derive_from_paired(&ident, &c_ident.into_token_stream(), &paired_type) + } +} diff --git a/src/macros/generate/convert/host/rust/derive/mod.rs b/src/macros/generate/convert/host/rust/derive/mod.rs new file mode 100644 index 0000000..51b50c3 --- /dev/null +++ b/src/macros/generate/convert/host/rust/derive/mod.rs @@ -0,0 +1,2 @@ +pub mod structure; +pub mod enumeration; diff --git a/src/macros/generate/convert/host/rust/derive/structure.rs b/src/macros/generate/convert/host/rust/derive/structure.rs new file mode 100644 index 0000000..bf653a9 --- /dev/null +++ b/src/macros/generate/convert/host/rust/derive/structure.rs @@ -0,0 +1,68 @@ +use proc_macro2::TokenStream as TokenStream2; +use quote::quote; + +use crate::parser::command_spec::Structure; + +impl Structure { + /// This function generates the `Convertible` implementation for a structure + // was: `structure_convertable_derive` + pub fn derive_convertible(&self, paired_type: &TokenStream2) -> TokenStream2 { + let ident = self.identifier.to_rust(); + + let into_fields: TokenStream2 = self + .contents + .iter() + .map(|con| { + let ident = &con.name.to_rust(); + quote! { + #ident: self.#ident.into(), + } + }) + .collect(); + + quote! { + impl trixy::types::traits::convert_trait::Convertible for #ident { + type Ptr = #paired_type; + + fn into_ptr(self) -> Self::Ptr { + Self::Ptr { + #into_fields + } + } + + fn from_ptr(ptr: Self::Ptr) -> Result { + todo!() + } + } + } + } + + /// This function generates the `TryFrom` trait implementation for a given structure + // was: `structure_into_impl` + pub fn derive_into_impl(&self, paired_type: &TokenStream2) -> TokenStream2 { + let ident = self.identifier.to_rust(); + + let try_into_fields: TokenStream2 = self + .contents + .iter() + .map(|con| { + let ident = &con.name.to_rust(); + quote! { + #ident: value.#ident.try_into()?, + } + }) + .collect(); + + quote! { + impl TryFrom<#paired_type> for #ident { + type Error = trixy::types::error::TypeConversionError; + + fn try_from(value: #paired_type) -> Result { + Ok(Self { + #try_into_fields + }) + } + } + } + } +} diff --git a/src/macros/generate/convert/host/rust/enumeration/mod.rs b/src/macros/generate/convert/host/rust/enumeration/mod.rs new file mode 100644 index 0000000..061d167 --- /dev/null +++ b/src/macros/generate/convert/host/rust/enumeration/mod.rs @@ -0,0 +1,45 @@ +use proc_macro2::TokenStream as TokenStream2; +use quote::quote; + +use crate::parser::command_spec::{DocIdentifier, Enumeration}; + +impl Enumeration { + pub fn to_rust(&self) -> TokenStream2 { + let doc_comments: TokenStream2 = self + .attributes + .iter() + .map(|attr| attr.to_rust(&self.identifier)) + .collect(); + + let ident = self.identifier.to_rust(); + let states: Vec = self.states.iter().map(DocIdentifier::to_rust).collect(); + + let paired_type = { + let path = self.identifier.variant.to_c_path(); + + let ident = self.identifier.to_c(); + if path.is_empty() { + quote! { + crate :: #ident + } + } else { + quote! { + #path :: #ident + } + } + }; + + // was: `rust_enumeration_into_impl` + let convertible = self.derive_from_rust_paried_c(&paired_type); + + quote! { + #doc_comments + #[allow(non_camel_case_types)] + #[derive(Debug)] + pub enum #ident { + #(#states),* + } + #convertible + } + } +} diff --git a/src/macros/generate/convert/host/rust/function/mod.rs b/src/macros/generate/convert/host/rust/function/mod.rs new file mode 100644 index 0000000..13f087a --- /dev/null +++ b/src/macros/generate/convert/host/rust/function/mod.rs @@ -0,0 +1,61 @@ +use proc_macro2::TokenStream as TokenStream2; +use quote::quote; + +use crate::parser::command_spec::{Function, Identifier, NamedType, Type}; + +impl Function { + // was called function_identifier_to_rust + pub fn to_rust_identifier( + &self, + input_fmt_fn: fn(&NamedType) -> TokenStream2, + output_fmt_fn: F, + ) -> TokenStream2 + where + F: Fn(&Type) -> TokenStream2, + { + let ident = self.identifier.to_rust(); + let inputs: Vec = self.inputs.iter().map(input_fmt_fn).collect(); + let output = &self.output; + + if inputs.is_empty() && output.is_none() { + quote! { + #ident + } + } else if output.is_some() { + let output = output_fmt_fn(&output.as_ref().expect("We checked")); + quote! { + #ident { + trixy_output: #output , + #(#inputs),* + } + } + } else if output.is_none() && !inputs.is_empty() { + quote! { + #ident { #(#inputs),* } + } + } else { + unreachable!("All other conditions should be met") + } + } + + pub fn to_rust(&self, _namespaces: &[&Identifier]) -> TokenStream2 { + let doc_comments: TokenStream2 = self + .attributes + .iter() + .map(|attr| attr.to_rust(&self.identifier)) + .collect(); + + let function_ident = self.to_rust_identifier(NamedType::to_rust, move |r#type| { + let ident = r#type.to_c(); + quote! { + trixy::oneshot::Sender<#ident> + } + }); + + quote! { + #doc_comments + #[allow(non_camel_case_types)] + #function_ident + } + } +} diff --git a/src/macros/generate/convert/host/rust/identifier/doc_identifier.rs b/src/macros/generate/convert/host/rust/identifier/doc_identifier.rs new file mode 100644 index 0000000..10a5ffa --- /dev/null +++ b/src/macros/generate/convert/host/rust/identifier/doc_identifier.rs @@ -0,0 +1,20 @@ +use proc_macro2::TokenStream as TokenStream2; +use quote::quote; + +use crate::parser::command_spec::{DocIdentifier, Identifier}; + +impl DocIdentifier { + pub fn to_rust(&self) -> TokenStream2 { + let attributes: TokenStream2 = self + .attributes + .iter() + .map(|attr| attr.to_rust(&Into::::into(self))) + .collect(); + let identifier = Into::::into(self).to_rust(); + + quote! { + #attributes + #identifier + } + } +} diff --git a/src/macros/generate/convert/host/rust/identifier/mod.rs b/src/macros/generate/convert/host/rust/identifier/mod.rs new file mode 100644 index 0000000..5d8b182 --- /dev/null +++ b/src/macros/generate/convert/host/rust/identifier/mod.rs @@ -0,0 +1,29 @@ +use convert_case::{Case, Casing}; +use proc_macro2::TokenStream as TokenStream2; +use quote::{format_ident, quote}; + +use crate::parser::command_spec::Identifier; + +mod doc_identifier; +// pub use doc_identifier::*; + +impl Identifier { + pub fn to_rust(&self) -> TokenStream2 { + if self.name == "()" { + quote! { + () + } + } else { + let ident = format_ident!("{}", self.name); + quote! { + #ident + } + } + } + pub fn to_rust_pascalized(&self) -> TokenStream2 { + let pascal_ident = format_ident!("{}", self.name.to_case(Case::Pascal)); + quote! { + #pascal_ident + } + } +} diff --git a/src/macros/generate/convert/host/rust/mod.rs b/src/macros/generate/convert/host/rust/mod.rs new file mode 100644 index 0000000..27e1e00 --- /dev/null +++ b/src/macros/generate/convert/host/rust/mod.rs @@ -0,0 +1,19 @@ +mod attribute; +mod enumeration; +mod function; +mod identifier; +mod namespace; +mod structure; +mod r#type; +mod variant; + +// pub use attribute::*; +// pub use enumeration::*; +// pub use function::*; +// pub use identifier::*; +// pub use namespace::*; +// pub use r#type::*; +// pub use structure::*; +// pub use variant::*; + +pub mod derive; diff --git a/src/macros/generate/convert/host/rust/namespace/mod.rs b/src/macros/generate/convert/host/rust/namespace/mod.rs new file mode 100644 index 0000000..9c719bd --- /dev/null +++ b/src/macros/generate/convert/host/rust/namespace/mod.rs @@ -0,0 +1,2 @@ +pub mod path; +pub mod module; diff --git a/src/macros/generate/convert/host/rust/namespace/module.rs b/src/macros/generate/convert/host/rust/namespace/module.rs new file mode 100644 index 0000000..37eb5a2 --- /dev/null +++ b/src/macros/generate/convert/host/rust/namespace/module.rs @@ -0,0 +1,59 @@ +use proc_macro2::TokenStream as TokenStream2; +use quote::quote; + +use crate::parser::command_spec::{Enumeration, Identifier, Namespace, Structure}; + +impl Namespace { + pub fn to_rust_module(&self, namespaces: &Vec<&Identifier>) -> TokenStream2 { + let mut namespaces = namespaces.clone(); + namespaces.push(&self.name); + let ident = self.name.to_rust(); + let enum_ident = self.name.to_rust_pascalized(); + + let doc_comments: TokenStream2 = self + .attributes + .iter() + .map(|attr| attr.to_rust(&self.name)) + .collect(); + let structures: TokenStream2 = self.structures.iter().map(Structure::to_rust).collect(); + let enumerations: TokenStream2 = + self.enumerations.iter().map(Enumeration::to_rust).collect(); + let functions: Vec = self + .functions + .iter() + .map(|r#fn| r#fn.to_rust(&namespaces)) + .collect(); + let namespace_modules: Vec = self + .namespaces + .iter() + .map(Self::to_rust_module_enum) + .collect(); + let namespaces: TokenStream2 = self + .namespaces + .iter() + .map(|nasp| nasp.to_rust_module(&namespaces)) + .collect(); + + quote! { + #doc_comments + pub mod #ident { + #structures + #enumerations + #[derive(Debug)] + pub enum #enum_ident { + #(#functions,)* + #(#namespace_modules),* + } + #namespaces + } + } + } + + pub fn to_rust_module_enum(&self) -> TokenStream2 { + let pascal_ident = self.name.to_rust_pascalized(); + let ident = self.name.to_rust(); + quote! { + #pascal_ident(#ident :: #pascal_ident) + } + } +} diff --git a/src/macros/generate/convert/host/rust/namespace/path.rs b/src/macros/generate/convert/host/rust/namespace/path.rs new file mode 100644 index 0000000..cbdff2e --- /dev/null +++ b/src/macros/generate/convert/host/rust/namespace/path.rs @@ -0,0 +1,65 @@ +use std::ops::Deref; + +use proc_macro2::TokenStream as TokenStream2; +use quote::{format_ident, quote}; + +use crate::parser::command_spec::{Identifier, Namespace, Variant}; + +impl Namespace { + pub fn to_rust_path>(namespaces: &[T]) -> TokenStream2 { + namespaces + .iter() + .fold(TokenStream2::default(), |acc, nasp| { + if nasp.variant == Variant::RootNamespace && nasp.name == "" { + if acc.is_empty() { + quote! { + crate + } + } else { + unreachable!("An root namespace can never come, after another namespcae") + } + } else { + let ident = format_ident!("{}", nasp.name); + if acc.is_empty() { + quote! { + crate :: #ident + } + } else { + quote! { + #acc :: #ident + } + } + } + }) + } + pub fn to_rust_path_owned(namespaces: &[Identifier]) -> TokenStream2 { + // PERFORMANCE(@soispha): Reallocating a new vector here can't really be the most efficient + // way to do this <2024-03-25> + let vec: Vec<&Identifier> = namespaces.iter().map(|val| val).collect(); + Namespace::to_rust_path(&vec) + // namespaces + // .iter() + // .fold(TokenStream2::default(), |acc, nasp| { + // if nasp.variant == Variant::RootNamespace && nasp.name == "" { + // if acc.is_empty() { + // quote! { + // crate + // } + // } else { + // unreachable!("An root namespace can never come, after another namespcae") + // } + // } else { + // let ident = format_ident!("{}", nasp.name); + // if acc.is_empty() { + // quote! { + // crate :: #ident + // } + // } else { + // quote! { + // #acc :: #ident + // } + // } + // } + // }) + } +} diff --git a/src/macros/generate/convert/host/rust/structure/mod.rs b/src/macros/generate/convert/host/rust/structure/mod.rs new file mode 100644 index 0000000..db84fac --- /dev/null +++ b/src/macros/generate/convert/host/rust/structure/mod.rs @@ -0,0 +1,43 @@ +use proc_macro2::TokenStream as TokenStream2; +use quote::quote; + +use crate::parser::command_spec::{DocNamedType, Structure}; + +impl Structure { + pub fn to_rust(&self) -> TokenStream2 { + let doc_comments: TokenStream2 = self + .attributes + .iter() + .map(|attr| attr.to_rust(&self.identifier)) + .collect(); + + let ident = &self.identifier.to_rust(); + let c_ident = { + let path = &self.identifier.variant.to_c_path(); + let ident = &self.identifier.to_c(); + if path.is_empty() { + quote! { + crate :: #ident + } + } else { + quote! { + #path :: #ident + } + } + }; + let contents: Vec = self.contents.iter().map(DocNamedType::to_rust).collect(); + let convertible = self.derive_convertible(&c_ident); + let into_impl = self.derive_into_impl(&c_ident); + + quote! { + #doc_comments + #[allow(non_camel_case_types)] + #[derive(Debug)] + pub struct #ident { + #(#contents),* + } + #convertible + #into_impl + } + } +} diff --git a/src/macros/generate/convert/host/rust/type/doc_named_type.rs b/src/macros/generate/convert/host/rust/type/doc_named_type.rs new file mode 100644 index 0000000..0531cab --- /dev/null +++ b/src/macros/generate/convert/host/rust/type/doc_named_type.rs @@ -0,0 +1,19 @@ +use proc_macro2::TokenStream as TokenStream2; +use quote::quote; + +use crate::parser::command_spec::{DocNamedType, NamedType}; + +impl DocNamedType { + pub fn to_rust(&self) -> TokenStream2 { + let attributes: TokenStream2 = self + .attributes + .iter() + .map(|attr| attr.to_rust(&self.name)) + .collect(); + let named_type = Into::::into(self).to_rust(); + quote! { + #attributes + pub #named_type + } + } +} diff --git a/src/macros/generate/convert/host/rust/type/mod.rs b/src/macros/generate/convert/host/rust/type/mod.rs new file mode 100644 index 0000000..f6141ef --- /dev/null +++ b/src/macros/generate/convert/host/rust/type/mod.rs @@ -0,0 +1,67 @@ +use proc_macro2::TokenStream as TokenStream2; +use quote::quote; + +use crate::parser::command_spec::{Identifier, NamedType, Type, Variant}; + +mod doc_named_type; +mod named_type; + +// pub use doc_named_type::*; +// pub use named_type::*; + +impl Type { + pub fn to_rust(&self) -> TokenStream2 { + match self { + Type::Typical { + identifier, + generic_args, + } => Self::to_rust_typical(&identifier, &generic_args), + Type::Function { inputs, output } => Self::to_rust_function(&inputs, &output), + } + } + pub fn to_rust_function(inputs: &[NamedType], output: &Option>) -> TokenStream2 { + let inputs: Vec = inputs.iter().map(NamedType::to_rust).collect(); + let output = if let Some(output) = output { + let output = output.to_rust(); + quote! { + -> #output + } + } else { + TokenStream2::default() + }; + + quote! { + fn(#(#inputs),*) #output + } + } + pub fn to_rust_typical(identifier: &Identifier, generic_args: &Vec) -> TokenStream2 { + let ident = identifier.to_rust(); + let namespaces_path = Variant::to_rust_path(&identifier.variant); + + let nasp_path = if let Some(nasp_path) = Variant::to_rust_path(&identifier.variant) { + if nasp_path.is_empty() { + quote! { + crate :: + } + } else { + let path = namespaces_path; + quote! { + #path :: + } + } + } else { + quote! {} + }; + + if generic_args.is_empty() { + quote! { + #nasp_path #ident + } + } else { + let generics: Vec = generic_args.iter().map(Type::to_rust).collect(); + quote! { + #nasp_path #ident <#(#generics),*> + } + } + } +} diff --git a/src/macros/generate/convert/host/rust/type/named_type.rs b/src/macros/generate/convert/host/rust/type/named_type.rs new file mode 100644 index 0000000..dd17d8a --- /dev/null +++ b/src/macros/generate/convert/host/rust/type/named_type.rs @@ -0,0 +1,14 @@ +use proc_macro2::TokenStream as TokenStream2; +use quote::quote; + +use crate::parser::command_spec::NamedType; + +impl NamedType { + pub fn to_rust(&self) -> TokenStream2 { + let ident = self.name.to_rust(); + let r#type = self.r#type.to_rust(); + quote! { + #ident : #r#type + } + } +} diff --git a/src/macros/generate/convert/host/rust/variant/mod.rs b/src/macros/generate/convert/host/rust/variant/mod.rs new file mode 100644 index 0000000..dc6b8b7 --- /dev/null +++ b/src/macros/generate/convert/host/rust/variant/mod.rs @@ -0,0 +1,25 @@ +use proc_macro2::TokenStream as TokenStream2; + +use crate::parser::command_spec::{Identifier, Namespace, Variant}; + +impl Variant { + // was named `type_variant_rust_path` + pub fn to_rust_path(&self) -> Option { + fn namespace_to_borrowed(vec: &Vec) -> Vec<&Identifier> { + let vec_2: Vec<&Identifier> = vec.iter().collect(); + vec_2 + } + + let main_namespace; + match self { + Variant::Structure { namespace } => { + main_namespace = namespace_to_borrowed(namespace); + } + Variant::Enumeration { namespace } => { + main_namespace = namespace_to_borrowed(namespace); + } + _ => return None, + } + Some(Namespace::to_rust_path(&main_namespace[..])) + } +} diff --git a/src/macros/generate/convert/mod.rs b/src/macros/generate/convert/mod.rs new file mode 100644 index 0000000..50c2842 --- /dev/null +++ b/src/macros/generate/convert/mod.rs @@ -0,0 +1,2 @@ +pub mod host; +pub mod auxiliary; diff --git a/src/macros/generate/host/c/host/mod.rs b/src/macros/generate/host/c/host/mod.rs new file mode 100644 index 0000000..a22a74b --- /dev/null +++ b/src/macros/generate/host/c/host/mod.rs @@ -0,0 +1,74 @@ +/* +* Copyright (C) 2023 - 2024: +* The Trinitrix Project +* +* This file is part of the Trixy crate for Trinitrix. +* +* Trixy is free software: you can redistribute it and/or modify +* it under the terms of the Lesser GNU General Public License as +* published by the Free Software Foundation, either version 3 of +* the License, or (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* and the Lesser GNU General Public License along with this program. +* If not, see . +*/ + +use proc_macro2::TokenStream as TokenStream2; +use quote::quote; + +use crate::{ + macros::config::trixy::TrixyConfig, + parser::command_spec::{CommandSpec, Enumeration, Structure}, +}; + +/// This function generates the main c API provided by Trixy. +/// This works for example like this: +/// Turning this: +/// ```text +/// nasp trinitrix { +/// struct Callback { +/// func: String, +/// timeout: String, +/// }; +/// +/// enum CallbackPriority { +/// High, +/// Medium, +/// Low, +/// }; +/// +/// fn execute_callback(callback: Callback, priority: CallbackPriority); +/// } +/// ``` +/// to this: +/// ```no_run +/// pub extern "C" fn exectute_callback(callback: Callback, priority: CallbackPriority) { +/// /* Here we simply call your handler function, with the command of the function */ +/// } +/// ``` +pub fn generate(trixy: &CommandSpec, config: &TrixyConfig) -> TokenStream2 { + let functions: TokenStream2 = trixy + .functions + .iter() + .map(|r#fn| r#fn.to_c(&config, &vec![])) + .collect(); + let namespaced_functions: TokenStream2 = trixy + .namespaces + .iter() + .map(|nasp| nasp.to_c(&config, &vec![])) + .collect(); + let structures: TokenStream2 = trixy.structures.iter().map(Structure::to_c).collect(); + let enumerations: TokenStream2 = trixy.enumerations.iter().map(Enumeration::to_c).collect(); + quote! { + #enumerations + #structures + #functions + #namespaced_functions + } +} diff --git a/trixy-parser/example/other_comments.tri b/src/macros/generate/host/c/mod.rs similarity index 68% rename from trixy-parser/example/other_comments.tri rename to src/macros/generate/host/c/mod.rs index 8454713..caf7f6c 100644 --- a/trixy-parser/example/other_comments.tri +++ b/src/macros/generate/host/c/mod.rs @@ -19,20 +19,23 @@ * If not, see . */ -/// other doc comment -fn hi(name: String) -> String; +use crate::{ + macros::{config::trixy::TrixyConfig, generate::host::format_rust}, + parser::command_spec::CommandSpec, +}; -/// struct comment -struct ho { - ident: String, - /// also a doc comment - codebase: Vec, +pub mod host; + +pub fn generate(trixy: &CommandSpec, config: &TrixyConfig) -> String { + let host_rust_code = host::generate(&trixy, &config); + + let rust_code = format_rust(host_rust_code); + + format!( + " +/* c API */ +{} +", + rust_code + ) } - -/// Some doc comment -mod trinitrix { - fn hi(name: String) -> String; -} - -// That's a flat out lie, but it results in a rather nice syntax highlight compared to nothing: -// vim: syntax=rust diff --git a/src/macros/generate/host/mod.rs b/src/macros/generate/host/mod.rs new file mode 100644 index 0000000..f19f6ca --- /dev/null +++ b/src/macros/generate/host/mod.rs @@ -0,0 +1,34 @@ +//! Host files that are included in the final binary + +use proc_macro2::TokenStream as TokenStream2; + +use crate::{ + macros::{config::trixy::TrixyConfig, VIM_LINE_RUST}, + parser::command_spec::CommandSpec, +}; + +pub mod c; +pub mod rust; + +pub fn format_rust(input: TokenStream2) -> String { + prettyplease::unparse( + &syn::parse2(input).expect("This code was generated, it should also be parsable"), + ) +} + +pub fn generate(trixy: &CommandSpec, config: &TrixyConfig) -> String { + let rust_host = rust::generate(trixy, config); + let c_host = c::generate(trixy, config); + + format!( + " +// Host code {{{{{{ +{} +/* C API */ +{} +// }}}}}} +{} + ", + rust_host, c_host, VIM_LINE_RUST + ) +} diff --git a/src/macros/generate/host/rust/host/mod.rs b/src/macros/generate/host/rust/host/mod.rs new file mode 100644 index 0000000..62f5720 --- /dev/null +++ b/src/macros/generate/host/rust/host/mod.rs @@ -0,0 +1,114 @@ +/* +* Copyright (C) 2023 - 2024: +* The Trinitrix Project +* +* This file is part of the Trixy crate for Trinitrix. +* +* Trixy is free software: you can redistribute it and/or modify +* it under the terms of the Lesser GNU General Public License as +* published by the Free Software Foundation, either version 3 of +* the License, or (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* and the Lesser GNU General Public License along with this program. +* If not, see . +*/ + +//! This module is responsible for generating the rust code used to interface with the api. +//! That includes the structs and enums declared in the trixy file and the enum used to describe the +//! command being executed. + +use proc_macro2::TokenStream as TokenStream2; +use quote::quote; + +use crate::{ + macros::config::trixy::TrixyConfig, + parser::command_spec::{CommandSpec, Enumeration, Namespace, Structure}, +}; + +/// This function turns, for example, the following trixy input into this rust code: +/// ```text +/// nasp trinitrix { +/// struct Callback { +/// func: String, +/// timeout: String, +/// }; +/// +/// enum CallbackPriority { +/// High, +/// Medium, +/// Low, +/// }; +/// +/// fn execute_callback(callback: Callback, priority: CallbackPriority) -> String; +/// } +/// ``` +/// ```no_run +/// #[derive(Debug)] +/// pub enum Commands { +/// Trinitrix(trinitrix::Trinitrix), +/// } +/// pub mod trinitrix { +/// #[allow(non_camel_case_types)] +/// #[derive(Debug)] +/// struct Callback { +/// func: String, +/// timeout: String, +/// } +/// #[allow(non_camel_case_types)] +/// #[derive(Debug)] +/// enum CallbackPriority { +/// High, +/// Medium, +/// Low, +/// } +/// #[derive(Debug)] +/// pub enum Trinitrix { +/// #[allow(non_camel_case_types)] +/// execute_callback { +/// callback: Callback, +/// priority: CallbackPriority, +/// trixy_output: trixy::oneshot::channel +/// }, +/// } +/// } +/// ``` +pub fn generate(trixy: &CommandSpec, _config: &TrixyConfig) -> TokenStream2 { + let modules: TokenStream2 = trixy + .namespaces + .iter() + .map(|nasp| nasp.to_rust_module(&vec![])) + .collect(); + let structures: TokenStream2 = trixy.structures.iter().map(Structure::to_rust).collect(); + let enumerations: TokenStream2 = trixy + .enumerations + .iter() + .map(Enumeration::to_rust) + .collect(); + let functions: Vec = trixy + .functions + .iter() + .map(|r#fn| r#fn.to_rust(&[])) + .collect(); + let namespace_modules: Vec = trixy + .namespaces + .iter() + .map(Namespace::to_rust_module_enum) + .collect(); + + quote! { + #structures + #enumerations + #[derive(Debug)] + pub enum Commands { + #(#functions,)* + #(#namespace_modules),* + } + #modules + } +} diff --git a/trixy-parser/example/types.tri b/src/macros/generate/host/rust/mod.rs similarity index 68% rename from trixy-parser/example/types.tri rename to src/macros/generate/host/rust/mod.rs index 1a135c5..5fc0b12 100644 --- a/trixy-parser/example/types.tri +++ b/src/macros/generate/host/rust/mod.rs @@ -19,20 +19,22 @@ * If not, see . */ -mod trinitrix { - struct Callback { - func: Function, - timeout: Integer, - } +use crate::parser::command_spec::CommandSpec; - enum CallbackPriority { - High, - Medium, - Low, - } +use crate::macros::{config::trixy::TrixyConfig, generate::host::format_rust}; - fn execute_callback(callback: Callback, priority: CallbackPriority); +pub mod host; + +pub fn generate(trixy: &CommandSpec, config: &TrixyConfig) -> String { + let host_rust_code = host::generate(&trixy, &config); + + let rust_code = format_rust(host_rust_code); + + format!( + " +/* Rust API */ +{} +", + rust_code + ) } - -// That's a flat out lie, but it results in a rather nice syntax highlight compared to nothing: -// vim: syntax=rust diff --git a/src/macros/generate/mod.rs b/src/macros/generate/mod.rs new file mode 100644 index 0000000..779d053 --- /dev/null +++ b/src/macros/generate/mod.rs @@ -0,0 +1,3 @@ +pub mod host; +pub mod auxiliary; +pub mod convert; diff --git a/src/macros/mod.rs b/src/macros/mod.rs new file mode 100644 index 0000000..9d31784 --- /dev/null +++ b/src/macros/mod.rs @@ -0,0 +1,123 @@ +/* +* Copyright (C) 2023 - 2024: +* The Trinitrix Project +* +* This file is part of the Trixy crate for Trinitrix. +* +* Trixy is free software: you can redistribute it and/or modify +* it under the terms of the Lesser GNU General Public License as +* published by the Free Software Foundation, either version 3 of +* the License, or (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* and the Lesser GNU General Public License along with this program. +* If not, see . +*/ + +use std::{ + fs, iter, + path::{Path, PathBuf}, +}; + +use crate::parser::parse_trixy_lang; +use crate::types::C_TYPE_HEADER; +use config::{ + file_tree::{FileTree, GeneratedFile}, + trixy::Language, +}; + +use self::config::trixy::TrixyConfig; + +pub mod config; +mod generate; + +const VIM_LINE_RUST: &'static str = "// vim: filetype=rust\n"; +const VIM_LINE_C: &'static str = "// vim: filetype=c\n"; + +impl TrixyConfig { + /// This is the heart of the trixy crate. + /// It's main purpose is to generate, what you have asked for in the [`TrixyConfig`] and to + /// return this generated code as a [`FileTree`]. + /// + /// # Panic + /// + /// Beware that this function will panic on errors, as it is written with the expectation that + /// it's run from a `build.rs` file. If you want to avoid that, usage of the raw generation + /// functions is guaranteed to never panic. + /// + /// # Examples + /// + /// ``` + /// use crate::macros::TrixyConfig; + /// + /// let trixy_config = TrixyConfig::new("some_callback_function") + /// .trixy_path("./path/to/api/file.tri") + /// /* other config settings */; + /// trixy_config.generate(); + /// ``` + pub fn generate(&self) -> FileTree { + let mut file_tree = FileTree::new(); + + let source_code = fs::read_to_string(&self.trixy_path).unwrap_or_else(|err| { + panic! {"Can't read file at path: '{}'. The Error was: '{}'", self.trixy_path.display(), err}; + }); + + let trixy_code = parse_trixy_lang(&source_code).unwrap_or_else(|err| { + panic! {"Parsing of the trixy file failed: \n{}", err} + }); + + // host code + let host_code = generate::host::generate(&trixy_code, &self); + file_tree.add_host_file(GeneratedFile::new_in_out_dir( + self.host_code_name.clone(), + host_code, + Language::Rust, + &self.out_dir, + )); + + // auxiliary code + let c_header = generate::auxiliary::generate(&trixy_code, &self); + + if let Some(dist_dir) = &self.dist_dir_path { + let c_header_dist = + PathBuf::from(format!("{}/{}", dist_dir.display(), &self.c_header_name)); + file_tree.add_auxiliary_file(GeneratedFile::new(c_header_dist, c_header, Language::C)); + + // TODO(@soispha): Is this even necessary? <2024-03-25> + let (interface_name, interface_content) = { + let interface_header = format!( + "\ + /* This file is automatcially generated by Trixy */ \n\ + #ifndef TRIXY_INTERFACE_H \n\ + #define TRIXY_INTERFACE_H \n\ + #include \"{}\" \n\ + #endif // TRIXY_INTERFACE_H \n\ + ", + &self.c_header_name + ); + ("interface.h", interface_header) + }; + + C_TYPE_HEADER + .iter() + .chain(iter::once(&(interface_name, &interface_content[..]))) + .for_each(|(name, content)| { + let path: &Path = &Path::new(name); + + let header_path = + PathBuf::from(format!("{}/{}", dist_dir.display(), path.display())); + file_tree.add_auxiliary_file(GeneratedFile::new( + header_path, + content.to_string(), + Language::C, + )); + }); + } + file_tree + } +} diff --git a/trixy-parser/src/command_spec/checked.rs b/src/parser/command_spec/checked.rs similarity index 99% rename from trixy-parser/src/command_spec/checked.rs rename to src/parser/command_spec/checked.rs index 36ff31f..c001662 100644 --- a/trixy-parser/src/command_spec/checked.rs +++ b/src/parser/command_spec/checked.rs @@ -23,7 +23,7 @@ use std::fmt::{Display, Write}; -use crate::lexing::TokenKind; +use crate::parser::lexing::TokenKind; use super::unchecked::{self, DeriveValue}; diff --git a/trixy-parser/src/command_spec/mod.rs b/src/parser/command_spec/mod.rs similarity index 100% rename from trixy-parser/src/command_spec/mod.rs rename to src/parser/command_spec/mod.rs diff --git a/trixy-parser/src/command_spec/unchecked.rs b/src/parser/command_spec/unchecked.rs similarity index 96% rename from trixy-parser/src/command_spec/unchecked.rs rename to src/parser/command_spec/unchecked.rs index 4d45b4d..d3d113f 100644 --- a/trixy-parser/src/command_spec/unchecked.rs +++ b/src/parser/command_spec/unchecked.rs @@ -25,9 +25,9 @@ use std::fmt::{Display, Write}; -use crate::lexing::{Token, TokenKind, TokenSpan}; +use crate::parser::lexing::{Token, TokenKind, TokenSpan}; -#[derive(Debug, Default, PartialEq, Eq, PartialOrd, Ord)] +#[derive(Debug, Default, PartialEq, Eq, PartialOrd, Ord, Clone)] pub struct CommandSpec { pub structures: Vec, pub enumerations: Vec, @@ -189,7 +189,7 @@ pub enum Type { impl Display for NamedType { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { let ident = match self.name.kind() { - crate::lexing::TokenKind::Identifier(ident) => ident, + crate::parser::lexing::TokenKind::Identifier(ident) => ident, _ => panic!("Tried to display a non identifier token in the Type display implementation. This is a bug"), }; f.write_str(ident)?; @@ -206,7 +206,7 @@ impl Display for Type { generic_args, } => { let ident = match identifier.kind() { - crate::lexing::TokenKind::Identifier(ident) => ident, + crate::parser::lexing::TokenKind::Identifier(ident) => ident, _ => panic!("Tried to display a non identifier token in the Type display implementation. This is a bug"), }; diff --git a/trixy-parser/src/error.rs b/src/parser/error.rs similarity index 99% rename from trixy-parser/src/error.rs rename to src/parser/error.rs index e75cb0b..fdc619d 100644 --- a/trixy-parser/src/error.rs +++ b/src/parser/error.rs @@ -23,9 +23,9 @@ use core::fmt; use thiserror::Error; -use crate::{ +use crate::parser::{ lexing::{error::SpannedLexingError, TokenSpan}, - parsing::{self}, + parsing, }; #[derive(Error, Debug)] diff --git a/trixy-parser/src/lexing/error.rs b/src/parser/lexing/error.rs similarity index 95% rename from trixy-parser/src/lexing/error.rs rename to src/parser/lexing/error.rs index c9bcf54..b7f5ee6 100644 --- a/trixy-parser/src/lexing/error.rs +++ b/src/parser/lexing/error.rs @@ -22,7 +22,7 @@ use std::{error::Error, fmt::Display}; use thiserror::Error; -use crate::error::{AdditionalHelp, ErrorContext, ErrorContextDisplay}; +use crate::parser::error::{AdditionalHelp, ErrorContext, ErrorContextDisplay}; #[derive(Error, Debug)] pub enum LexingError { @@ -67,7 +67,7 @@ impl Error for SpannedLexingError { impl ErrorContextDisplay for SpannedLexingError { type Error = LexingError; - fn context(&self) -> &crate::error::ErrorContext { + fn context(&self) -> &crate::parser::error::ErrorContext { &self.context } diff --git a/trixy-parser/src/lexing/mod.rs b/src/parser/lexing/mod.rs similarity index 77% rename from trixy-parser/src/lexing/mod.rs rename to src/parser/lexing/mod.rs index 1c84dd9..89bae0e 100644 --- a/trixy-parser/src/lexing/mod.rs +++ b/src/parser/lexing/mod.rs @@ -31,7 +31,7 @@ mod tokenizer; #[cfg(test)] mod test; -#[derive(Debug, PartialEq, PartialOrd, Ord, Eq)] +#[derive(Debug, PartialEq, PartialOrd, Ord, Eq, Clone)] pub struct TokenStream { pub original_file: String, tokens: Vec, @@ -308,7 +308,7 @@ impl Display for AttributeKeyword { /// # Examples /// /// ``` -/// use trixy_parser::token; +/// use crate::parser::token; ///# fn main() { /// token![mod]; /// token![;]; @@ -317,56 +317,56 @@ impl Display for AttributeKeyword { /// ``` #[macro_export] macro_rules! token { - [Semicolon] => { $crate::lexing::TokenKind::Semicolon }; - [;] => { $crate::lexing::TokenKind::Semicolon }; - [Colon] => { $crate::lexing::TokenKind::Colon }; - [:] => { $crate::lexing::TokenKind::Colon }; - [Comma] => { $crate::lexing::TokenKind::Comma }; - [,] => { $crate::lexing::TokenKind::Comma }; - [Arrow] => { $crate::lexing::TokenKind::Arrow }; - [->] => { $crate::lexing::TokenKind::Arrow }; - [PoundSign] => { $crate::lexing::TokenKind::PoundSign }; - [#] => { $crate::lexing::TokenKind::PoundSign }; - [EqualsSign] => { $crate::lexing::TokenKind::EqualsSign }; - [=] => { $crate::lexing::TokenKind::EqualsSign }; + [Semicolon] => { $crate::parser::lexing::TokenKind::Semicolon }; + [;] => { $crate::parser::lexing::TokenKind::Semicolon }; + [Colon] => { $crate::parser::lexing::TokenKind::Colon }; + [:] => { $crate::parser::lexing::TokenKind::Colon }; + [Comma] => { $crate::parser::lexing::TokenKind::Comma }; + [,] => { $crate::parser::lexing::TokenKind::Comma }; + [Arrow] => { $crate::parser::lexing::TokenKind::Arrow }; + [->] => { $crate::parser::lexing::TokenKind::Arrow }; + [PoundSign] => { $crate::parser::lexing::TokenKind::PoundSign }; + [#] => { $crate::parser::lexing::TokenKind::PoundSign }; + [EqualsSign] => { $crate::parser::lexing::TokenKind::EqualsSign }; + [=] => { $crate::parser::lexing::TokenKind::EqualsSign }; - [AngledBracketOpen] => { $crate::lexing::TokenKind::AngledBracketOpen }; - [<] => { $crate::lexing::TokenKind::AngledBracketOpen }; + [AngledBracketOpen] => { $crate::parser::lexing::TokenKind::AngledBracketOpen }; + [<] => { $crate::parser::lexing::TokenKind::AngledBracketOpen }; - [AngledBracketClose] => { $crate::lexing::TokenKind::AngledBracketClose }; - [>] => { $crate::lexing::TokenKind::AngledBracketClose }; + [AngledBracketClose] => { $crate::parser::lexing::TokenKind::AngledBracketClose }; + [>] => { $crate::parser::lexing::TokenKind::AngledBracketClose }; - [CurlyBracketOpen] => { $crate::lexing::TokenKind::CurlyBracketOpen}; - // [{] => { $crate::lexing::TokenKind::CurlyBracketOpen }; - [CurlyBracketClose] => { $crate::lexing::TokenKind::CurlyBracketClose}; - // [}] => { $crate::lexing::TokenKind::CurlyBracketClose }; - [CurvedBracketOpen] => { $crate::lexing::TokenKind::CurvedBracketOpen}; - // [(] => { $crate::lexing::TokenKind::ParenthesisOpen }; - [CurvedBracketClose] => { $crate::lexing::TokenKind::CurvedBracketClose}; - // [)] => { $crate::lexing::TokenKind::ParenthesisClose }; - [SquareBracketOpen] => { $crate::lexing::TokenKind::SquareBracketOpen}; - // [[] => { $crate::lexing::TokenKind::ParenthesisOpen }; - [SquareBracketClose] => { $crate::lexing::TokenKind::SquareBracketClose}; - // []] => { $crate::lexing::TokenKind::ParenthesisClose }; + [CurlyBracketOpen] => { $crate::parser::lexing::TokenKind::CurlyBracketOpen}; + // [{] => { $crate::parser::lexing::TokenKind::CurlyBracketOpen }; + [CurlyBracketClose] => { $crate::parser::lexing::TokenKind::CurlyBracketClose}; + // [}] => { $crate::parser::lexing::TokenKind::CurlyBracketClose }; + [CurvedBracketOpen] => { $crate::parser::lexing::TokenKind::CurvedBracketOpen}; + // [(] => { $crate::parser::lexing::TokenKind::ParenthesisOpen }; + [CurvedBracketClose] => { $crate::parser::lexing::TokenKind::CurvedBracketClose}; + // [)] => { $crate::parser::lexing::TokenKind::ParenthesisClose }; + [SquareBracketOpen] => { $crate::parser::lexing::TokenKind::SquareBracketOpen}; + // [[] => { $crate::parser::lexing::TokenKind::ParenthesisOpen }; + [SquareBracketClose] => { $crate::parser::lexing::TokenKind::SquareBracketClose}; + // []] => { $crate::parser::lexing::TokenKind::ParenthesisClose }; - [mod] => { $crate::lexing::TokenKind::Keyword($crate::lexing::Keyword::r#mod) }; - [fn] => { $crate::lexing::TokenKind::Keyword($crate::lexing::Keyword::r#fn) }; - [struct] => { $crate::lexing::TokenKind::Keyword($crate::lexing::Keyword::r#struct) }; - [enum] => { $crate::lexing::TokenKind::Keyword($crate::lexing::Keyword::r#enum) }; + [mod] => { $crate::parser::lexing::TokenKind::Keyword($crate::parser::lexing::Keyword::r#mod) }; + [fn] => { $crate::parser::lexing::TokenKind::Keyword($crate::parser::lexing::Keyword::r#fn) }; + [struct] => { $crate::parser::lexing::TokenKind::Keyword($crate::parser::lexing::Keyword::r#struct) }; + [enum] => { $crate::parser::lexing::TokenKind::Keyword($crate::parser::lexing::Keyword::r#enum) }; // The `derive` here is completely arbitrary. It is only for comparisons (see `same_kind`) - [AttributeKeyword] => { $crate::lexing::TokenKind::AttributeKeyword($crate::lexing::AttributeKeyword::derive) }; + [AttributeKeyword] => { $crate::parser::lexing::TokenKind::AttributeKeyword($crate::parser::lexing::AttributeKeyword::derive) }; // This is only works for checking for a identifier or comment // see the `same_kind` method on TokenKind - [Ident] => { $crate::lexing::TokenKind::Identifier("".to_owned()) }; - [Identifier] => { $crate::lexing::TokenKind::Identifier("".to_owned()) }; + [Ident] => { $crate::parser::lexing::TokenKind::Identifier("".to_owned()) }; + [Identifier] => { $crate::parser::lexing::TokenKind::Identifier("".to_owned()) }; - [StringLiteral] => { $crate::lexing::TokenKind::StringLiteral("".to_owned()) }; + [StringLiteral] => { $crate::parser::lexing::TokenKind::StringLiteral("".to_owned()) }; - [Comment] => { $crate::lexing::TokenKind::Comment("".to_owned()) }; + [Comment] => { $crate::parser::lexing::TokenKind::Comment("".to_owned()) }; } #[cfg(test)] @@ -397,6 +397,6 @@ mod tests { token_macro_test!(tok_expands_to_arrow, ->, => TokenKind::Arrow); token_macro_test!(tok_expands_to_semicolon, Semicolon, => TokenKind::Semicolon); - token_macro_test!(tok_expands_to_mod, mod, => TokenKind::Keyword(crate::lexing::Keyword::r#mod)); - token_macro_test!(tok_expands_to_fn, fn, => TokenKind::Keyword(crate::lexing::Keyword::r#fn)); + token_macro_test!(tok_expands_to_mod, mod, => TokenKind::Keyword(crate::parser::lexing::Keyword::r#mod)); + token_macro_test!(tok_expands_to_fn, fn, => TokenKind::Keyword(crate::parser::lexing::Keyword::r#fn)); } diff --git a/trixy-parser/src/lexing/test.rs b/src/parser/lexing/test.rs similarity index 100% rename from trixy-parser/src/lexing/test.rs rename to src/parser/lexing/test.rs diff --git a/trixy-parser/src/lexing/tokenizer.rs b/src/parser/lexing/tokenizer.rs similarity index 99% rename from trixy-parser/src/lexing/tokenizer.rs rename to src/parser/lexing/tokenizer.rs index 3904911..bca7dee 100644 --- a/trixy-parser/src/lexing/tokenizer.rs +++ b/src/parser/lexing/tokenizer.rs @@ -21,7 +21,7 @@ // This code is heavily inspired by: https://michael-f-bryan.github.io/static-analyser-in-rust/book/lex.html -use crate::{ +use crate::parser::{ error::ErrorContext, lexing::{Keyword, TokenSpan}, }; diff --git a/trixy-parser/src/lib.rs b/src/parser/mod.rs similarity index 97% rename from trixy-parser/src/lib.rs rename to src/parser/mod.rs index 493b40b..3b92749 100644 --- a/trixy-parser/src/lib.rs +++ b/src/parser/mod.rs @@ -21,7 +21,7 @@ use error::TrixyError; -use crate::lexing::TokenStream; +use crate::parser::lexing::TokenStream; use self::command_spec::checked::CommandSpec; diff --git a/trixy-parser/src/parsing/checked/error.rs b/src/parser/parsing/checked/error.rs similarity index 98% rename from trixy-parser/src/parsing/checked/error.rs rename to src/parser/parsing/checked/error.rs index bb6bafc..4d6dd67 100644 --- a/trixy-parser/src/parsing/checked/error.rs +++ b/src/parser/parsing/checked/error.rs @@ -23,7 +23,7 @@ use thiserror::Error; use std::{error::Error, fmt::Display}; -use crate::{ +use crate::parser::{ command_spec::{checked::Identifier, unchecked::Attribute}, error::{AdditionalHelp, ErrorContext, ErrorContextDisplay}, lexing::TokenSpan, @@ -133,7 +133,7 @@ impl Display for SpannedParsingError { impl ErrorContextDisplay for SpannedParsingError { type Error = ParsingError; - fn context(&self) -> &crate::error::ErrorContext { + fn context(&self) -> &crate::parser::error::ErrorContext { &self.context } diff --git a/trixy-parser/src/parsing/checked/mod.rs b/src/parser/parsing/checked/mod.rs similarity index 98% rename from trixy-parser/src/parsing/checked/mod.rs rename to src/parser/parsing/checked/mod.rs index 5a76b2c..8557d03 100644 --- a/trixy-parser/src/parsing/checked/mod.rs +++ b/src/parser/parsing/checked/mod.rs @@ -21,10 +21,10 @@ use std::{iter, mem}; +use crate::types::BASE_TYPES; use convert_case::{Case, Casing}; -use trixy_types::BASE_TYPES; -use crate::{ +use crate::parser::{ command_spec::{ checked::{ self, CommandSpec, DocIdentifier, DocNamedType, Enumeration, Function, Identifier, @@ -68,13 +68,13 @@ macro_rules! take_attrs { } }; (@process_val $iden:ident, $val:ident) => { - if let $crate::command_spec::unchecked::Attribute::$val{..} = $iden { + if let $crate::parser::command_spec::unchecked::Attribute::$val{..} = $iden { return Ok($iden.into()); }; take_attrs!{@process_val_last $iden} }; (@process_val $iden:ident, $val:ident, $($other:tt),+ $(,)*) => { - if let $crate::command_spec::unchecked::Attribute::$val{..} = $iden { + if let $crate::parser::command_spec::unchecked::Attribute::$val{..} = $iden { return Ok($iden.into()); }; take_attrs!{@process_val $iden, $($other),*} diff --git a/trixy-parser/src/parsing/checked/test.rs b/src/parser/parsing/checked/test.rs similarity index 100% rename from trixy-parser/src/parsing/checked/test.rs rename to src/parser/parsing/checked/test.rs diff --git a/trixy-parser/src/parsing/mod.rs b/src/parser/parsing/mod.rs similarity index 100% rename from trixy-parser/src/parsing/mod.rs rename to src/parser/parsing/mod.rs diff --git a/trixy-parser/src/parsing/unchecked/error.rs b/src/parser/parsing/unchecked/error.rs similarity index 98% rename from trixy-parser/src/parsing/unchecked/error.rs rename to src/parser/parsing/unchecked/error.rs index 80b2342..34c6fe0 100644 --- a/trixy-parser/src/parsing/unchecked/error.rs +++ b/src/parser/parsing/unchecked/error.rs @@ -22,7 +22,7 @@ use std::{error::Error, fmt::Display}; use thiserror::Error; -use crate::{ +use crate::parser::{ command_spec::unchecked::{Attribute, StringLiteral}, error::{AdditionalHelp, ErrorContext, ErrorContextDisplay}, lexing::{TokenKind, TokenSpan}, @@ -113,7 +113,7 @@ impl Display for SpannedParsingError { impl ErrorContextDisplay for SpannedParsingError { type Error = ParsingError; - fn context(&self) -> &crate::error::ErrorContext { + fn context(&self) -> &crate::parser::error::ErrorContext { &self.context } diff --git a/trixy-parser/src/parsing/unchecked/mod.rs b/src/parser/parsing/unchecked/mod.rs similarity index 97% rename from trixy-parser/src/parsing/unchecked/mod.rs rename to src/parser/parsing/unchecked/mod.rs index c362b1e..8201349 100644 --- a/trixy-parser/src/parsing/unchecked/mod.rs +++ b/src/parser/parsing/unchecked/mod.rs @@ -22,12 +22,14 @@ use std::{iter::once, mem}; use crate::{ - command_spec::unchecked::{ - Attribute, CommandSpec, Declaration, DeriveValue, DocNamedType, DocToken, Enumeration, - Function, NamedType, Namespace, StringLiteral, Structure, Type, + parser::{ + command_spec::unchecked::{ + Attribute, CommandSpec, Declaration, DeriveValue, DocNamedType, DocToken, Enumeration, + Function, NamedType, Namespace, StringLiteral, Structure, Type, + }, + error::ErrorContext, + lexing::{AttributeKeyword, Token, TokenKind, TokenSpan, TokenStream}, }, - error::ErrorContext, - lexing::{AttributeKeyword, Token, TokenKind, TokenSpan, TokenStream}, token, }; diff --git a/trixy-parser/src/parsing/unchecked/test.rs b/src/parser/parsing/unchecked/test.rs similarity index 100% rename from trixy-parser/src/parsing/unchecked/test.rs rename to src/parser/parsing/unchecked/test.rs diff --git a/trixy-types/src/c_headers/errno.h b/src/types/c_headers/errno.h similarity index 100% rename from trixy-types/src/c_headers/errno.h rename to src/types/c_headers/errno.h diff --git a/trixy-types/src/c_headers/string.h b/src/types/c_headers/string.h similarity index 100% rename from trixy-types/src/c_headers/string.h rename to src/types/c_headers/string.h diff --git a/trixy-types/src/c_headers/vec.h b/src/types/c_headers/vec.h similarity index 100% rename from trixy-types/src/c_headers/vec.h rename to src/types/c_headers/vec.h diff --git a/trixy-types/src/error/mod.rs b/src/types/error/mod.rs similarity index 100% rename from trixy-types/src/error/mod.rs rename to src/types/error/mod.rs diff --git a/trixy-types/src/lib.rs b/src/types/mod.rs similarity index 100% rename from trixy-types/src/lib.rs rename to src/types/mod.rs diff --git a/trixy-types/src/traits/convert_trait.rs b/src/types/traits/convert_trait.rs similarity index 97% rename from trixy-types/src/traits/convert_trait.rs rename to src/types/traits/convert_trait.rs index 1521911..aca5fc5 100644 --- a/trixy-types/src/traits/convert_trait.rs +++ b/src/types/traits/convert_trait.rs @@ -19,7 +19,7 @@ * If not, see . */ -use crate::error::{self, TypeConversionError}; +use crate::types::error::{self, TypeConversionError}; use log::warn; use std::{ error::Error, @@ -180,7 +180,7 @@ impl Convertible for Result { } impl Convertible for Vec { - type Ptr = crate::Vec<::Ptr>; + type Ptr = crate::types::Vec<::Ptr>; fn into_ptr(self) -> Self::Ptr { let data_vec: Vec<_> = self.into_iter().map(|val| val.into_ptr()).collect(); @@ -222,7 +222,7 @@ impl Convertible for Vec { macro_rules! make_vec_free { ($value:ident, $name:ident) => { #[no_mangle] - pub extern "C" fn $name(ptr: crate::Vec<<$value as Convertible>::Ptr>) { + pub extern "C" fn $name(ptr: crate::types::Vec<<$value as Convertible>::Ptr>) { Vec::<$value>::drop_ptr(ptr); } }; diff --git a/trixy-types/src/traits/errno.rs b/src/types/traits/errno.rs similarity index 98% rename from trixy-types/src/traits/errno.rs rename to src/types/traits/errno.rs index 985d70a..49a3fee 100644 --- a/trixy-types/src/traits/errno.rs +++ b/src/types/traits/errno.rs @@ -29,7 +29,7 @@ use std::{ use log::{error, warn}; -use crate::error::TypeConversionError; +use crate::types::error::TypeConversionError; // This code is heavily inspired by: https://michael-f-bryan.github.io/rust-ffi-guide/errors/return_types.html thread_local! { diff --git a/trixy-types/src/traits/mod.rs b/src/types/traits/mod.rs similarity index 96% rename from trixy-types/src/traits/mod.rs rename to src/types/traits/mod.rs index b06ca91..964d07b 100644 --- a/trixy-types/src/traits/mod.rs +++ b/src/types/traits/mod.rs @@ -22,4 +22,4 @@ pub mod convert_trait; pub mod errno; mod try_from_impl; -pub use try_from_impl::*; +// pub use try_from_impl::*; diff --git a/trixy-types/src/traits/try_from_impl.rs b/src/types/traits/try_from_impl.rs similarity index 94% rename from trixy-types/src/traits/try_from_impl.rs rename to src/types/traits/try_from_impl.rs index f84d3dc..db5ad87 100644 --- a/trixy-types/src/traits/try_from_impl.rs +++ b/src/types/traits/try_from_impl.rs @@ -19,7 +19,7 @@ * If not, see . */ -use crate::String; +use crate::types::String; use std::ffi::CString; use super::convert_trait::Convertible; @@ -34,7 +34,7 @@ impl From for String { } impl TryFrom for std::string::String { - type Error = crate::error::TypeConversionError; + type Error = crate::types::error::TypeConversionError; fn try_from(value: String) -> Result { let cstring = CString::from_ptr(value.0)?; diff --git a/trixy-types/src/types_list.rs b/src/types/types_list.rs similarity index 100% rename from trixy-types/src/types_list.rs rename to src/types/types_list.rs diff --git a/trixy-macros/.gitignore b/trixy-macros/.gitignore deleted file mode 100644 index 20c0ba9..0000000 --- a/trixy-macros/.gitignore +++ /dev/null @@ -1,6 +0,0 @@ -# build -/target -/result - -# This crate is a library -Cargo.lock diff --git a/trixy-macros/Cargo.toml b/trixy-macros/Cargo.toml deleted file mode 100644 index 53eb334..0000000 --- a/trixy-macros/Cargo.toml +++ /dev/null @@ -1,32 +0,0 @@ -# Copyright (C) 2023 - 2024: -# The Trinitrix Project -# -# This file is part of the Trixy crate for Trinitrix. -# -# Trixy is free software: you can redistribute it and/or modify -# it under the terms of the Lesser GNU General Public License as -# published by the Free Software Foundation, either version 3 of -# the License, or (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# and the Lesser GNU General Public License along with this program. -# If not, see . - -[package] -name = "trixy-macros" -version = "0.1.0" -edition = "2021" - -[dependencies] -convert_case = "0.6.0" -prettyplease = "0.2.15" -proc-macro2 = {version = "1.0.70", features = [ ]} -quote = "1.0.33" -syn = { version = "2.0.41", features = ["extra-traits", "full", "parsing"] } -trixy-parser = { path = "../trixy-parser" } -trixy-types = { path = "../trixy-types" } diff --git a/trixy-macros/src/generate/c_api/header/pure_header.rs b/trixy-macros/src/generate/c_api/header/pure_header.rs deleted file mode 100644 index 7885019..0000000 --- a/trixy-macros/src/generate/c_api/header/pure_header.rs +++ /dev/null @@ -1,194 +0,0 @@ -/* -* Copyright (C) 2023 - 2024: -* The Trinitrix Project -* -* This file is part of the Trixy crate for Trinitrix. -* -* Trixy is free software: you can redistribute it and/or modify -* it under the terms of the Lesser GNU General Public License as -* published by the Free Software Foundation, either version 3 of -* the License, or (at your option) any later version. -* -* This program is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU General Public License for more details. -* -* You should have received a copy of the GNU General Public License -* and the Lesser GNU General Public License along with this program. -* If not, see . -*/ - -use proc_macro2::TokenStream as TokenStream2; -use quote::quote; -use trixy_parser::command_spec::{ - CommandSpec, DocIdentifier, DocNamedType, Enumeration, Function, Identifier, Namespace, - Structure, -}; - -use crate::generate::{ - c_api::{ - header::{attribute_to_doc_comment, named_type_to_c, type_to_c}, - identifier_to_c, mangle_c_function_identifier, - }, - identifier_to_rust, -}; - -pub fn generate(trixy: &CommandSpec) -> String { - let functions: String = trixy - .functions - .iter() - .map(|r#fn| function_to_header(r#fn, &[])) - .collect(); - let namespaces: String = trixy - .namespaces - .iter() - .map(|nasp| namespace_to_header(nasp, &vec![])) - .collect(); - let structures: String = trixy - .structures - .iter() - .map(|r#struct| structure_to_header(r#struct)) - .collect(); - let enumerations: String = trixy - .enumerations - .iter() - .map(|r#enum| enumeration_to_header(r#enum)) - .collect(); - format!( - "{}\n{}\n{}\n{}", - enumerations, structures, functions, namespaces - ) -} -fn structure_to_header(structure: &Structure) -> String { - let doc_comments: String = structure - .attributes - .iter() - .map(attribute_to_doc_comment) - .collect::(); - let ident = identifier_to_c(&structure.identifier); - let contents = structure - .contents - .iter() - .map(doc_named_type_to_header) - .collect::(); - format!( - " - {} - {} {{ - {} - }};", - doc_comments, ident, contents - ) -} -fn doc_named_type_to_header(doc_named_type: &DocNamedType) -> String { - let doc_comments: String = doc_named_type - .attributes - .iter() - .map(attribute_to_doc_comment) - .collect::(); - let ident = identifier_to_rust(&doc_named_type.name); - let r#type = type_to_c(&doc_named_type.r#type, false); - format!("{}{} {};", doc_comments, r#type, ident) -} -fn enumeration_to_header(enumeration: &Enumeration) -> String { - let doc_comments: String = enumeration - .attributes - .iter() - .map(attribute_to_doc_comment) - .collect::(); - let ident = identifier_to_c(&enumeration.identifier); - let states = enumeration - .states - .iter() - .map(doc_identifier_to_header) - .collect::(); - let states = if enumeration.states.is_empty() { - "/** - * This enum does not have variants on the rust side - * to work around c limitiation with variant-less enums - * we added a `__never` variant: - */ - __never," - .to_owned() - } else { - states - }; - format!( - " - {} - {} {{ - {} - }};", - doc_comments, ident, states - ) -} -fn doc_identifier_to_header(doc_identifier: &DocIdentifier) -> String { - let doc_comments: String = doc_identifier - .attributes - .iter() - .map(attribute_to_doc_comment) - .collect::(); - let ident = &doc_identifier.name; - format!("{}{},", doc_comments, ident) -} -fn function_to_header(function: &Function, namespaces: &[&Identifier]) -> String { - let doc_comments: String = function - .attributes - .iter() - .map(attribute_to_doc_comment) - .collect::(); - let ident = mangle_c_function_identifier(&function.identifier, namespaces); - let inputs: Vec = function.inputs.iter().map(named_type_to_c).collect(); - - let function_output = if let Some(out) = &function.output { - let type_name = type_to_c(&out, true); - let comma = if !inputs.is_empty() { - quote! { - , - } - } else { - TokenStream2::default() - }; - quote! { - #type_name trixy_output #comma - } - } else { - TokenStream2::default() - }; - - let output = quote! { - extern int #ident(#function_output #(#inputs),*); - }; - format!("{}{}\n", doc_comments, output) -} -fn namespace_to_header(nasp: &Namespace, namespaces: &Vec<&Identifier>) -> String { - let mut nasps = namespaces.clone(); - nasps.push(&nasp.name); - - let structures: String = nasp - .structures - .iter() - .map(|r#fn| structure_to_header(r#fn)) - .collect::>() - .join("\n"); - let enumerations: String = nasp - .enumerations - .iter() - .map(|r#fn| enumeration_to_header(r#fn)) - .collect::>() - .join("\n"); - let functions: String = nasp - .functions - .iter() - .map(|r#fn| function_to_header(r#fn, &nasps)) - .collect::>() - .join("\n"); - let namespaces: String = nasp - .namespaces - .iter() - .map(|nasp| namespace_to_header(nasp, &nasps)) - .collect(); - - format! {"{}\n{}\n{}\n{}", enumerations, structures, functions, namespaces} -} diff --git a/trixy-macros/src/generate/c_api/header/structs_init.rs b/trixy-macros/src/generate/c_api/header/structs_init.rs deleted file mode 100644 index 8aa1482..0000000 --- a/trixy-macros/src/generate/c_api/header/structs_init.rs +++ /dev/null @@ -1,85 +0,0 @@ -/* -* Copyright (C) 2023 - 2024: -* The Trinitrix Project -* -* This file is part of the Trixy crate for Trinitrix. -* -* Trixy is free software: you can redistribute it and/or modify -* it under the terms of the Lesser GNU General Public License as -* published by the Free Software Foundation, either version 3 of -* the License, or (at your option) any later version. -* -* This program is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU General Public License for more details. -* -* You should have received a copy of the GNU General Public License -* and the Lesser GNU General Public License along with this program. -* If not, see . -*/ - -use proc_macro2::TokenStream as TokenStream2; -use quote::format_ident; -use quote::quote; -use trixy_parser::command_spec::{CommandSpec, Function, Identifier, Namespace}; - -use crate::generate::{c_api::mangle_c_function_identifier, identifier_to_rust}; - -pub fn generate(trixy: &CommandSpec) -> String { - let struct_initializer: TokenStream2 = trixy - .namespaces - .iter() - .map(|nasp| namespace_to_full_struct_init(nasp, &vec![])) - .collect(); - struct_initializer.to_string() -} - -fn namespace_to_full_struct_init(nasp: &Namespace, namespaces: &Vec<&Identifier>) -> TokenStream2 { - let mut input_namespaces = namespaces.clone(); - input_namespaces.push(&nasp.name); - - let ident = identifier_to_rust(&nasp.name); - let type_ident = format_ident!("{}", ident.to_string()); - let functions: TokenStream2 = nasp - .functions - .iter() - .map(|r#fn| function_to_struct_init(r#fn, &input_namespaces)) - .collect(); - let namespaces: TokenStream2 = nasp - .namespaces - .iter() - .map(namespace_to_struct_init) - .collect(); - - let next_namespace: TokenStream2 = nasp - .namespaces - .iter() - .map(|nasp| namespace_to_full_struct_init(nasp, &input_namespaces)) - .collect(); - - quote! { - #next_namespace - - const struct #type_ident #ident = { - #functions - #namespaces - }; - } -} -fn function_to_struct_init(function: &Function, namespaces: &[&Identifier]) -> TokenStream2 { - let ident = identifier_to_rust(&function.identifier); - let full_ident = mangle_c_function_identifier(&function.identifier, namespaces); - - quote! { - . #ident = #full_ident, - } -} - -fn namespace_to_struct_init(namespace: &Namespace) -> TokenStream2 { - let ident = identifier_to_rust(&namespace.name); - - quote! { - . #ident = #ident , - } -} diff --git a/trixy-macros/src/generate/c_api/header/typedef.rs b/trixy-macros/src/generate/c_api/header/typedef.rs deleted file mode 100644 index 67854e4..0000000 --- a/trixy-macros/src/generate/c_api/header/typedef.rs +++ /dev/null @@ -1,114 +0,0 @@ -/* -* Copyright (C) 2023 - 2024: -* The Trinitrix Project -* -* This file is part of the Trixy crate for Trinitrix. -* -* Trixy is free software: you can redistribute it and/or modify -* it under the terms of the Lesser GNU General Public License as -* published by the Free Software Foundation, either version 3 of -* the License, or (at your option) any later version. -* -* This program is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU General Public License for more details. -* -* You should have received a copy of the GNU General Public License -* and the Lesser GNU General Public License along with this program. -* If not, see . -*/ - -use proc_macro2::TokenStream as TokenStream2; -use quote::{format_ident, quote}; -use trixy_parser::command_spec::{CommandSpec, Function, Namespace}; - -use crate::generate::{ - c_api::{header::attribute_to_doc_comment, type_to_c}, - identifier_to_rust, -}; - -pub fn generate(trixy: &CommandSpec) -> String { - let type_defs: String = trixy - .namespaces - .iter() - .rev() - .map(|nasp| namespace_to_full_typedef(nasp)) - .collect::>() - .join("\n"); - type_defs.to_string() -} - -fn function_to_typedef(function: &Function) -> TokenStream2 { - let ident = identifier_to_rust(&function.identifier); - - let (output, output_comma) = if let Some(output) = &function.output { - let output = type_to_c(output, true); - (quote! { #output }, quote! {,}) - } else { - (TokenStream2::default(), TokenStream2::default()) - }; - - let inputs: TokenStream2 = if function.inputs.is_empty() && output.is_empty() { - quote! { void } - } else if !function.inputs.is_empty() && !output.is_empty() { - let inputs: Vec = function - .inputs - .iter() - .map(|named_type| &named_type.r#type) - .map(|r#type| type_to_c(&r#type, false)) - .collect(); - quote! { - #output_comma #(#inputs),* - } - } else { - TokenStream2::default() - }; - - quote! { - int (* #ident ) (#output #inputs); - } -} - -fn namespace_to_typedef(namespace: &Namespace) -> TokenStream2 { - let ident = identifier_to_rust(&namespace.name); - let type_ident = format_ident!("{}", ident.to_string()); - - quote! { - struct #type_ident #ident; - } -} - -fn namespace_to_full_typedef(nasp: &Namespace) -> String { - let ident = format_ident!("{}", nasp.name.name); - let doc_comments = nasp - .attributes - .iter() - .map(attribute_to_doc_comment) - .collect::(); - - let functions: TokenStream2 = nasp - .functions - .iter() - .map(|r#fn| function_to_typedef(r#fn)) - .collect(); - let namespaces: TokenStream2 = nasp - .namespaces - .iter() - .map(|nasp| namespace_to_typedef(nasp)) - .collect(); - let next_namespace: String = nasp - .namespaces - .iter() - .map(|nasp| namespace_to_full_typedef(nasp)) - .collect::>() - .join("\n"); - - let namespace = quote! { - struct #ident { - #functions - #namespaces - }; - }; - format! {"{}\n{}{}\n", next_namespace, doc_comments, namespace} -} diff --git a/trixy-macros/src/generate/c_api/host.rs b/trixy-macros/src/generate/c_api/host.rs deleted file mode 100644 index 95385c1..0000000 --- a/trixy-macros/src/generate/c_api/host.rs +++ /dev/null @@ -1,375 +0,0 @@ -/* -* Copyright (C) 2023 - 2024: -* The Trinitrix Project -* -* This file is part of the Trixy crate for Trinitrix. -* -* Trixy is free software: you can redistribute it and/or modify -* it under the terms of the Lesser GNU General Public License as -* published by the Free Software Foundation, either version 3 of -* the License, or (at your option) any later version. -* -* This program is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU General Public License for more details. -* -* You should have received a copy of the GNU General Public License -* and the Lesser GNU General Public License along with this program. -* If not, see . -*/ - -use convert_case::{Case, Casing}; -use proc_macro2::TokenStream as TokenStream2; -use quote::{format_ident, quote}; -use trixy_parser::command_spec::{ - CommandSpec, Enumeration, Function, Identifier, NamedType, Namespace, Structure, -}; - -use crate::{ - config::TrixyConfig, - generate::{ - attribute_to_rust, - c_api::{ - doc_named_type_to_c_equivalent, mangle_c_function_identifier, mangle_c_type_identifier, - type_to_c_equivalent, - }, - convertible_derive::c_enumeration_into_impl, - doc_identifier_to_rust, function_identifier_to_rust, identifier_to_rust, - namespaces_to_path, type_variant_rust_path, - }, -}; - -/// This function generates the main c API provided by Trixy. -/// This works for example like this: -/// Turning this: -/// ```text -/// nasp trinitrix { -/// struct Callback { -/// func: String, -/// timeout: String, -/// }; -/// -/// enum CallbackPriority { -/// High, -/// Medium, -/// Low, -/// }; -/// -/// fn execute_callback(callback: Callback, priority: CallbackPriority); -/// } -/// ``` -/// to this: -/// ```no_run -/// pub extern "C" fn exectute_callback(callback: Callback, priority: CallbackPriority) { -/// /* Here we simply call your handler function, with the command of the function */ -/// } -/// ``` -pub fn generate(trixy: &CommandSpec, config: &TrixyConfig) -> TokenStream2 { - let functions: TokenStream2 = trixy - .functions - .iter() - .map(|r#fn| function_to_c(r#fn, &config, &vec![])) - .collect(); - let namespaced_functions: TokenStream2 = trixy - .namespaces - .iter() - .map(|nasp| namespace_to_c(&nasp, &config, &vec![])) - .collect(); - let structures: TokenStream2 = trixy - .structures - .iter() - .map(|nasp| structure_to_c(&nasp)) - .collect(); - let enumerations: TokenStream2 = trixy - .enumerations - .iter() - .map(|nasp| enumeration_to_c(&nasp)) - .collect(); - quote! { - #enumerations - #structures - #functions - #namespaced_functions - } -} - -fn enumeration_to_c(enumeration: &Enumeration) -> TokenStream2 { - let ident = mangle_c_type_identifier(&enumeration.identifier); - let doc_comments: TokenStream2 = enumeration - .attributes - .iter() - .map(|attr| attribute_to_rust(&enumeration.identifier, attr)) - .collect(); - - let states: Vec = enumeration - .states - .iter() - .map(doc_identifier_to_rust) - .collect(); - let paired_type = { - let path = type_variant_rust_path(&enumeration.identifier.variant) - .expect("This should always be some for enums"); - - let ident = identifier_to_rust(&enumeration.identifier); - if path.is_empty() { - quote! { - crate :: #ident - } - } else { - quote! { - #path :: #ident - } - } - }; - - let convertible = c_enumeration_into_impl(&enumeration, &paired_type); - quote! { - #doc_comments - #[allow(non_camel_case_types)] - #[repr(C)] - #[derive(Debug)] - pub enum #ident { - #(#states),* - } - #convertible - } -} - -fn structure_to_c(structure: &Structure) -> TokenStream2 { - let ident = mangle_c_type_identifier(&structure.identifier); - let doc_comments: TokenStream2 = structure - .attributes - .iter() - .map(|attr| attribute_to_rust(&structure.identifier, attr)) - .collect(); - - let contents: Vec = structure - .contents - .iter() - .map(doc_named_type_to_c_equivalent) - .collect(); - - // TODO: Convertible <2024-03-08> - quote! { - #doc_comments - #[allow(non_camel_case_types)] - #[repr(C)] - #[derive(Debug)] - pub struct #ident { - #(#contents),* - } - } -} - -fn namespace_to_c( - namespace: &Namespace, - config: &TrixyConfig, - namespaces: &Vec<&Identifier>, -) -> TokenStream2 { - let ident = mangle_c_type_identifier(&namespace.name); - let mut namespaces = namespaces.clone(); - namespaces.push(&namespace.name); - - let functions: TokenStream2 = namespace - .functions - .iter() - .map(|r#fn| function_to_c(&r#fn, &config, &namespaces)) - .collect(); - let additional_functions: TokenStream2 = namespace - .namespaces - .iter() - .map(|nasp| namespace_to_c(&nasp, &config, &namespaces)) - .collect(); - let structures: TokenStream2 = namespace - .structures - .iter() - .map(|nasp| structure_to_c(&nasp)) - .collect(); - let enumerations: TokenStream2 = namespace - .enumerations - .iter() - .map(|nasp| enumeration_to_c(&nasp)) - .collect(); - quote! { - pub mod #ident { - #enumerations - #structures - } - #functions - #additional_functions - } -} - -fn function_to_c( - function: &Function, - config: &TrixyConfig, - namespaces: &Vec<&Identifier>, -) -> TokenStream2 { - let ident = mangle_c_function_identifier(&function.identifier, namespaces); - let inputs: Vec = function - .inputs - .iter() - .map(|named_type| named_type_to_rust_trixy(named_type)) - .collect(); - - let callback_function = format_ident!("{}", config.callback_function); - - let command_value: TokenStream2 = function_path_to_rust(&namespaces, &function); - - if let Some(r#type) = &function.output { - let output_ident = type_to_c_equivalent(&r#type); - quote! { - #[no_mangle] - pub extern "C" fn #ident(output: *mut #output_ident, #(#inputs),*) -> core::ffi::c_int { - let output_val: #output_ident = { - let (tx, rx) = trixy::oneshot::channel(); - #callback_function (#command_value); - let recv = rx.recv().expect("The channel should not be closed until this value is received"); - recv.into() - }; - unsafe { - std::ptr::write(output, output_val); - } - return 1; - } - } - } else { - quote! { - #[no_mangle] - pub extern "C" fn #ident(#(#inputs),*) -> core::ffi::c_int { - #callback_function (#command_value); - return 1; - } - } - } -} - -fn named_type_to_rust_trixy(named_type: &NamedType) -> TokenStream2 { - let ident = identifier_to_rust(&named_type.name); - let type_ident = type_to_c_equivalent(&named_type.r#type); - quote! { - #ident : #type_ident - } -} - -/// Turns a function in namespaces to the generated host enum path: -/// *trixy code:* -/// ```text -/// fn fn_alone(); -/// nasp one { -/// fn fn_one(); -/// nasp two { -/// fn fn_two(input: String); -/// } -/// } -/// ``` -/// *rust enum path for fn_alone:* -/// ```no_run -/// Commands::fn_alone -/// ``` -/// *rust enum path for fn_one:* -/// ```no_run -/// // `Commands` is just the name for the top-level namespace -/// Commands::One(one::One( -/// One::fn_one -/// )) -/// ``` -/// *rust enum path for fn_two:* -/// ```no_run -/// Commands::One(one::One( -/// one::two::Two(one::two::Two( -/// Two::fn_two {input: String} -/// )))) -/// ``` -fn function_path_to_rust(namespaces: &Vec<&Identifier>, function: &Function) -> TokenStream2 { - let function_ident = - function_identifier_to_rust(&function, named_type_to_rust_assignment, |_| { - quote! { - // This is defined in the outer call - tx - } - }); - if namespaces.is_empty() { - quote! { - Commands:: #function_ident - } - } else { - let nasp_pascal_ident = format_ident!( - "{}", - namespaces - .last() - .expect("We checked") - .name - .to_case(Case::Pascal) - ); - - let namespace_path = namespaces_to_path(namespaces); - - let function_call = quote! { - #namespace_path :: #nasp_pascal_ident :: #function_ident - }; - - let output: TokenStream2 = namespaces - .iter() - .enumerate() - .rev() - .fold(function_call, |acc, (index, nasp)| { - nasp_path_one_part(nasp, &acc, &namespaces, index) - }); - - output - } -} - -fn named_type_to_rust_assignment(named_type: &NamedType) -> TokenStream2 { - let ident = identifier_to_rust(&named_type.name); - quote! { - #ident : match #ident.try_into() { - Ok(ok) => ok, - Err(err) => { - trixy::types::traits::errno::set(err); - return 0; - } - } - } -} - -/// This function add a namespace component to the [input] value like so: -/// (taking the example from the [function_path_to_rust] function) -/// ```text -/// one::two::Two::fn_two [= ] -/// -> -/// one::One::Two() [= ] -/// -> -/// Commands::One() [= ] -/// ``` -fn nasp_path_one_part( - current_nasp: &Identifier, - input: &TokenStream2, - namespaces: &Vec<&Identifier>, - index: usize, -) -> TokenStream2 { - let namespaces_to_do = &namespaces[..index]; - - let ident_pascal = format_ident!("{}", current_nasp.name.to_case(Case::Pascal)); - - if index == 0 { - quote! { - Commands :: #ident_pascal ( #input ) - } - } else { - let ident_pascal_next = format_ident!( - "{}", - namespaces_to_do - .last() - .expect("We checked the index") - .name - .to_case(Case::Pascal) - ); - let namespace_path = namespaces_to_path(namespaces_to_do); - quote! { - #namespace_path :: #ident_pascal_next :: #ident_pascal ( #input ) - } - } -} diff --git a/trixy-macros/src/generate/c_api/mod.rs b/trixy-macros/src/generate/c_api/mod.rs deleted file mode 100644 index b94135a..0000000 --- a/trixy-macros/src/generate/c_api/mod.rs +++ /dev/null @@ -1,226 +0,0 @@ -/* -* Copyright (C) 2023 - 2024: -* The Trinitrix Project -* -* This file is part of the Trixy crate for Trinitrix. -* -* Trixy is free software: you can redistribute it and/or modify -* it under the terms of the Lesser GNU General Public License as -* published by the Free Software Foundation, either version 3 of -* the License, or (at your option) any later version. -* -* This program is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU General Public License for more details. -* -* You should have received a copy of the GNU General Public License -* and the Lesser GNU General Public License along with this program. -* If not, see . -*/ - -use convert_case::{Case, Casing}; -use proc_macro2::{Ident, TokenStream as TokenStream2}; -use quote::{format_ident, quote}; -use trixy_parser::command_spec::{DocNamedType, Identifier, NamedType, Type, Variant}; - -use crate::generate::{attribute_to_rust, namespaces_to_path_expansive, type_to_rust}; - -use super::identifier_to_rust; - -pub mod header; -pub mod host; - -pub fn mangle_c_function_identifier(identifier: &Identifier, namespaces: &[&Identifier]) -> Ident { - let namespace_str = namespaces.iter().fold(String::default(), |acc, nasp| { - if acc.is_empty() { - nasp.name.clone() - } else { - format!("{}_{}", acc, nasp.name) - } - }); - - if namespace_str.is_empty() { - format_ident!("{}", &identifier.name) - } else { - format_ident!("{}_{}", namespace_str, &identifier.name) - } -} -pub fn mangle_c_type_identifier(identifier: &Identifier) -> Ident { - format_ident!("{}_c", &identifier.name) -} - -pub fn type_to_c(r#type: &Type, is_output: bool) -> TokenStream2 { - let ident = identifier_to_c(&r#type.identifier); - let output = if is_output { - quote! { - * - } - } else { - TokenStream2::default() - }; - quote! { - #ident #output - } -} -pub fn identifier_to_c(identifier: &Identifier) -> TokenStream2 { - match &identifier.variant { - Variant::Structure { .. } => { - let ident = format_ident!("{}", identifier.name.to_case(Case::Pascal)); - quote! { - struct #ident - } - } - Variant::Enumeration { .. } => { - let ident = format_ident!("{}", identifier.name.to_case(Case::Pascal)); - quote! { - enum #ident - } - } - Variant::Primitive => match identifier.name.to_case(Case::Snake).as_str() { - "string" => { - quote! { - const char* - } - } - other => { - todo!("'{}' is not yet supported", other) - } - }, - other => { - unimplemented!("{:#?}", other) - } - } -} - -pub fn type_to_c_equivalent(r#type: &Type) -> TokenStream2 { - let trixy_build_in_types: Vec<&str> = trixy_types::BASE_TYPES - .iter() - .filter_map(|(name, _)| { - if name == &r#type.identifier.name { - Some(*name) - } else { - None - } - }) - .collect(); - - if trixy_build_in_types.is_empty() { - // The types was specified in the api.tri file - - let ident = mangle_c_type_identifier(&r#type.identifier); - - let namespaces_path = type_variant_c_path(&r#type.identifier.variant); - let nasp_path = if namespaces_path.is_empty() { - quote! { - crate :: - } - } else { - let path = namespaces_path; - quote! { - #path :: - } - }; - quote! { - #nasp_path #ident - } - } else { - debug_assert_eq!(trixy_build_in_types.len(), 1); - - let type_name = trixy_build_in_types - .first() - .expect("The names should not be dublicated, this should be the only value"); - - match *type_name { - "Result" => { - let ident_ok = - type_to_c_equivalent(&r#type.generic_args.first().expect("This is a result")); - let ident_err = - type_to_c_equivalent(&r#type.generic_args.last().expect("This is a result")); - quote! { - // as Convertible>::Ptr, - as Convertible>::Ptr - } - } - "Option" => { - let value = type_to_rust( - r#type - .generic_args - .first() - .expect("An option does only have one arg"), - ); - quote! { - *const #value - } - } - _ => { - let ident = identifier_to_rust(&r#type.identifier); - let generics: TokenStream2 = { - let generics: Vec = r#type - .generic_args - .iter() - .map(|val| type_to_c_equivalent(val)) - .collect(); - quote! { - <#(#generics),*> - } - }; - - quote! { - trixy::types:: #ident #generics - } - } - } - } -} -fn doc_named_type_to_c_equivalent(doc_named_type: &DocNamedType) -> TokenStream2 { - let doc_comments: TokenStream2 = doc_named_type - .attributes - .iter() - .map(|attr| attribute_to_rust(&&doc_named_type.name, attr)) - .collect(); - let named_type = named_type_to_c_equivalent(&doc_named_type.into()); - quote! { - #doc_comments - pub #named_type - } -} -fn named_type_to_c_equivalent(named_type: &NamedType) -> TokenStream2 { - let ident = identifier_to_rust(&named_type.name); - let r#type = type_to_c_equivalent(&named_type.r#type); - quote! { - #ident : #r#type - } -} -pub fn type_variant_c_path(variant: &Variant) -> TokenStream2 { - fn mangle_namespace_name(vec: &Vec) -> Vec { - vec.into_iter() - .map(|ident| { - if "" == &ident.name && ident.variant == Variant::RootNamespace { - // The [`namespaces_to_path_expansive`] function already deals with - // [`RootNamespace`] variants, so we just leave that as is - ident.clone() - } else { - Identifier { - // TODO(@soispha): This should use [`mangle_c_type_name`] - // to ensure same mangling as the others <2024-03-05> - name: format!("{}_c", ident.name), - variant: ident.variant.clone(), - } - } - }) - .collect() - } - - let main_namespace; - match variant { - Variant::Structure { namespace } => { - main_namespace = mangle_namespace_name(namespace); - } - Variant::Enumeration { namespace } => { - main_namespace = mangle_namespace_name(namespace); - } - _ => unreachable!("This should never be called"), - } - namespaces_to_path_expansive(&main_namespace[..]) -} diff --git a/trixy-macros/src/generate/convertible_derive.rs b/trixy-macros/src/generate/convertible_derive.rs deleted file mode 100644 index 77a62ae..0000000 --- a/trixy-macros/src/generate/convertible_derive.rs +++ /dev/null @@ -1,148 +0,0 @@ -/* -* Copyright (C) 2023 - 2024: -* The Trinitrix Project -* -* This file is part of the Trixy crate for Trinitrix. -* -* Trixy is free software: you can redistribute it and/or modify -* it under the terms of the Lesser GNU General Public License as -* published by the Free Software Foundation, either version 3 of -* the License, or (at your option) any later version. -* -* This program is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU General Public License for more details. -* -* You should have received a copy of the GNU General Public License -* and the Lesser GNU General Public License along with this program. -* If not, see . -*/ - -use proc_macro2::TokenStream; -use quote::{quote, ToTokens}; -use trixy_parser::command_spec::{Enumeration, Structure}; - -use crate::generate::{c_api::mangle_c_type_identifier, identifier_to_rust}; - -/// This function generates the `Convertible` implementation for a structure -pub fn structure_convertable_derive( - structure: &Structure, - paired_type: &TokenStream, -) -> TokenStream { - let ident = identifier_to_rust(&structure.identifier); - - let into_fields: TokenStream = structure - .contents - .iter() - .map(|con| { - let ident = identifier_to_rust(&con.name); - quote! { - #ident: self.#ident.into(), - } - }) - .collect(); - - quote! { - impl trixy::types::traits::convert_trait::Convertible for #ident { - type Ptr = #paired_type; - - fn into_ptr(self) -> Self::Ptr { - Self::Ptr { - #into_fields - } - } - - fn from_ptr(ptr: Self::Ptr) -> Result { - todo!() - } - } - } -} - -/// This function generates the `TryFrom` trait implementation for a given structure -pub fn structure_into_impl(structure: &Structure, paired_type: &TokenStream) -> TokenStream { - let ident = identifier_to_rust(&structure.identifier); - - let try_into_fields: TokenStream = structure - .contents - .iter() - .map(|con| { - let ident = identifier_to_rust(&con.name); - quote! { - #ident: value.#ident.try_into()?, - } - }) - .collect(); - - quote! { - impl TryFrom<#paired_type> for #ident { - type Error = trixy::types::error::TypeConversionError; - - fn try_from(value: #paired_type) -> Result { - Ok(Self { - #try_into_fields - }) - } - } - } -} - -/// This function generates the `From` trait implementation for a given c enumeration -pub fn c_enumeration_into_impl( - enumeration: &Enumeration, - paired_type: &TokenStream, -) -> TokenStream { - let c_ident = mangle_c_type_identifier(&enumeration.identifier); - let ident = identifier_to_rust(&enumeration.identifier); - - enumeration_into_impl( - enumeration, - &c_ident.into_token_stream(), - &ident, - &paired_type, - ) -} - -/// This function generates the `From` trait implementation for a given rust enumeration -pub fn rust_enumeration_into_impl( - enumeration: &Enumeration, - paired_type: &TokenStream, -) -> TokenStream { - let c_ident = mangle_c_type_identifier(&enumeration.identifier); - let ident = identifier_to_rust(&enumeration.identifier); - - enumeration_into_impl( - enumeration, - &ident, - &c_ident.into_token_stream(), - &paired_type, - ) -} - -fn enumeration_into_impl( - enumeration: &Enumeration, - ident_main: &TokenStream, - ident_client: &TokenStream, - paired_type: &TokenStream, -) -> TokenStream { - let match_lines: TokenStream = enumeration - .states - .iter() - .map(|state| { - let name = identifier_to_rust(&(state.into())); - quote! { - #ident_client :: #name => Self :: #name, - } - }) - .collect(); - quote! { - impl From<#paired_type> for #ident_main { - fn from(value: #paired_type) -> Self { - match value { - #match_lines - } - } - } - } -} diff --git a/trixy-macros/src/generate/host/mod.rs b/trixy-macros/src/generate/host/mod.rs deleted file mode 100644 index 548c051..0000000 --- a/trixy-macros/src/generate/host/mod.rs +++ /dev/null @@ -1,315 +0,0 @@ -/* -* Copyright (C) 2023 - 2024: -* The Trinitrix Project -* -* This file is part of the Trixy crate for Trinitrix. -* -* Trixy is free software: you can redistribute it and/or modify -* it under the terms of the Lesser GNU General Public License as -* published by the Free Software Foundation, either version 3 of -* the License, or (at your option) any later version. -* -* This program is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU General Public License for more details. -* -* You should have received a copy of the GNU General Public License -* and the Lesser GNU General Public License along with this program. -* If not, see . -*/ - -//! This module is responsible for generating the rust code used to interface with the api. -//! That includes the structs and enums declared in the trixy file and the enum used to describe the -//! command being executed. - -use std::cell::OnceCell; - -use convert_case::{Case, Casing}; -use proc_macro2::TokenStream as TokenStream2; -use quote::{format_ident, quote}; -use trixy_parser::command_spec::{ - CommandSpec, Enumeration, Function, Identifier, Namespace, Structure, -}; - -use crate::{ - config::TrixyConfig, - generate::{ - attribute_to_rust, - c_api::{mangle_c_type_identifier, type_variant_c_path}, - convertible_derive::{ - rust_enumeration_into_impl, structure_convertable_derive, structure_into_impl, - }, - doc_identifier_to_rust, doc_named_type_to_rust, identifier_to_rust, named_type_to_rust, - type_variant_rust_path, - }, -}; - -use super::{c_api::type_to_c_equivalent, function_identifier_to_rust}; - -thread_local! {static DEBUG: OnceCell = OnceCell::new();} - -/// This function turns, for example, the following trixy input into this rust code: -/// ```text -/// nasp trinitrix { -/// struct Callback { -/// func: String, -/// timeout: String, -/// }; -/// -/// enum CallbackPriority { -/// High, -/// Medium, -/// Low, -/// }; -/// -/// fn execute_callback(callback: Callback, priority: CallbackPriority) -> String; -/// } -/// ``` -/// ```no_run -/// #[derive(Debug)] -/// pub enum Commands { -/// Trinitrix(trinitrix::Trinitrix), -/// } -/// pub mod trinitrix { -/// #[allow(non_camel_case_types)] -/// #[derive(Debug)] -/// struct Callback { -/// func: String, -/// timeout: String, -/// } -/// #[allow(non_camel_case_types)] -/// #[derive(Debug)] -/// enum CallbackPriority { -/// High, -/// Medium, -/// Low, -/// } -/// #[derive(Debug)] -/// pub enum Trinitrix { -/// #[allow(non_camel_case_types)] -/// execute_callback { -/// callback: Callback, -/// priority: CallbackPriority, -/// trixy_output: trixy::oneshot::channel -/// }, -/// } -/// } -/// ``` -pub fn generate(trixy: &CommandSpec, config: &TrixyConfig) -> TokenStream2 { - DEBUG.with(|d| { - d.set(if config.generate_debug { - quote! { - #[derive(Debug)] - } - } else { - TokenStream2::default() - }) - .expect("The cell should always be empty at this point"); - }); - - let modules: TokenStream2 = trixy - .namespaces - .iter() - .map(|nasp| namespace_to_module(nasp, &vec![])) - .collect(); - let structures: TokenStream2 = trixy.structures.iter().map(structure_to_rust).collect(); - let enumerations: TokenStream2 = trixy.enumerations.iter().map(enumeration_to_rust).collect(); - let functions: Vec = trixy - .functions - .iter() - .map(|r#fn| function_to_rust(r#fn, &[])) - .collect(); - let namespace_modules: Vec = trixy - .namespaces - .iter() - .map(namespace_to_module_enum) - .collect(); - - let debug = get_debug_sate(); - - quote! { - #structures - #enumerations - #debug - pub enum Commands { - #(#functions,)* - #(#namespace_modules),* - } - #modules - } -} - -fn namespace_to_module(namespace: &Namespace, namespaces: &Vec<&Identifier>) -> TokenStream2 { - let mut namespaces = namespaces.clone(); - namespaces.push(&namespace.name); - let ident = identifier_to_rust(&namespace.name); - let enum_ident = format_ident!("{}", &namespace.name.name.to_case(Case::Pascal)); - - let doc_comments: TokenStream2 = namespace - .attributes - .iter() - .map(|attr| attribute_to_rust(&namespace.name, attr)) - .collect(); - let structures: TokenStream2 = namespace.structures.iter().map(structure_to_rust).collect(); - let enumerations: TokenStream2 = namespace - .enumerations - .iter() - .map(enumeration_to_rust) - .collect(); - let functions: Vec = namespace - .functions - .iter() - .map(|r#fn| function_to_rust(r#fn, &namespaces)) - .collect(); - let namespace_modules: Vec = namespace - .namespaces - .iter() - .map(namespace_to_module_enum) - .collect(); - let namespaces: TokenStream2 = namespace - .namespaces - .iter() - .map(|nasp| namespace_to_module(nasp, &namespaces)) - .collect(); - - let debug = get_debug_sate(); - quote! { - #doc_comments - pub mod #ident { - #structures - #enumerations - #debug - pub enum #enum_ident { - #(#functions,)* - #(#namespace_modules),* - } - #namespaces - } - } -} - -fn namespace_to_module_enum(namespace: &Namespace) -> TokenStream2 { - let pascal_ident = format_ident!("{}", namespace.name.name.to_case(Case::Pascal)); - let ident = identifier_to_rust(&namespace.name); - quote! { - #pascal_ident(#ident :: #pascal_ident) - } -} - -fn get_debug_sate() -> TokenStream2 { - let debug = DEBUG.with(|d| { - d.get() - .expect("The cell should contain something at this point") - .clone() - }); - debug -} - -fn function_to_rust(function: &Function, namespaces: &[&Identifier]) -> TokenStream2 { - let doc_comments: TokenStream2 = function - .attributes - .iter() - .map(|attr| attribute_to_rust(&function.identifier, attr)) - .collect(); - let function_ident = - function_identifier_to_rust(&function, named_type_to_rust, move |r#type| { - let ident = type_to_c_equivalent(r#type); - quote! { - trixy::oneshot::Sender<#ident> - } - }); - - quote! { - #doc_comments - #[allow(non_camel_case_types)] - #function_ident - } -} - -fn enumeration_to_rust(enumeration: &Enumeration) -> TokenStream2 { - let doc_comments: TokenStream2 = enumeration - .attributes - .iter() - .map(|attr| attribute_to_rust(&enumeration.identifier, attr)) - .collect(); - - let ident = identifier_to_rust(&enumeration.identifier); - let states: Vec = enumeration - .states - .iter() - .map(doc_identifier_to_rust) - .collect(); - - let debug = get_debug_sate(); - - let paired_type = { - let path = type_variant_c_path(&enumeration.identifier.variant); - - let ident = mangle_c_type_identifier(&enumeration.identifier); - if path.is_empty() { - quote! { - crate :: #ident - } - } else { - quote! { - #path :: #ident - } - } - }; - - let convertible = rust_enumeration_into_impl(&enumeration, &paired_type); - - quote! { - #doc_comments - #[allow(non_camel_case_types)] - #debug - pub enum #ident { - #(#states),* - } - #convertible - } -} - -fn structure_to_rust(structure: &Structure) -> TokenStream2 { - let doc_comments: TokenStream2 = structure - .attributes - .iter() - .map(|attr| attribute_to_rust(&structure.identifier, attr)) - .collect(); - - let ident = identifier_to_rust(&structure.identifier); - let c_ident = { - let path = type_variant_c_path(&structure.identifier.variant); - let ident = mangle_c_type_identifier(&structure.identifier); - if path.is_empty() { - quote! { - crate :: #ident - } - } else { - quote! { - #path :: #ident - } - } - }; - let contents: Vec = structure - .contents - .iter() - .map(doc_named_type_to_rust) - .collect(); - let convertible = structure_convertable_derive(&structure, &c_ident); - let into_impl = structure_into_impl(&structure, &c_ident); - - let debug = get_debug_sate(); - - quote! { - #doc_comments - #[allow(non_camel_case_types)] - #debug - pub struct #ident { - #(#contents),* - } - #convertible - #into_impl - } -} diff --git a/trixy-macros/src/generate/mod.rs b/trixy-macros/src/generate/mod.rs deleted file mode 100644 index 2b429f6..0000000 --- a/trixy-macros/src/generate/mod.rs +++ /dev/null @@ -1,244 +0,0 @@ -/* -* Copyright (C) 2023 - 2024: -* The Trinitrix Project -* -* This file is part of the Trixy crate for Trinitrix. -* -* Trixy is free software: you can redistribute it and/or modify -* it under the terms of the Lesser GNU General Public License as -* published by the Free Software Foundation, either version 3 of -* the License, or (at your option) any later version. -* -* This program is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU General Public License for more details. -* -* You should have received a copy of the GNU General Public License -* and the Lesser GNU General Public License along with this program. -* If not, see . -*/ - -use std::ops::Deref; - -use proc_macro2::TokenStream as TokenStream2; -use quote::{format_ident, quote}; -use trixy_parser::command_spec::{ - Attribute, CommandSpec, DocIdentifier, DocNamedType, Function, Identifier, NamedType, Type, - Variant, -}; - -use crate::config::TrixyConfig; - -pub mod c_api; -pub mod convertible_derive; -pub mod host; - -pub fn generate(trixy: &CommandSpec, config: &TrixyConfig) -> TokenStream2 { - // Build the language wrappers - let c_host_api = c_api::host::generate(&trixy, &config); - - // Build the final rust hosting code - let host_rust_code = host::generate(&trixy, &config); - - quote! { - #host_rust_code - /* C api */ - #c_host_api - } -} - -fn identifier_to_rust(identifier: &Identifier) -> TokenStream2 { - if &identifier.name == "()" { - quote! { - () - } - } else { - let ident = format_ident!("{}", &identifier.name); - quote! { - #ident - } - } -} - -fn function_identifier_to_rust( - function: &Function, - input_fmt_fn: fn(&NamedType) -> TokenStream2, - output_fmt_fn: F, -) -> TokenStream2 -where - F: Fn(&Type) -> TokenStream2, -{ - let ident = identifier_to_rust(&function.identifier); - let inputs: Vec = function.inputs.iter().map(input_fmt_fn).collect(); - let output = &function.output; - - if inputs.is_empty() && output.is_none() { - quote! { - #ident - } - } else if output.is_some() { - let output = output_fmt_fn(&output.as_ref().expect("We checked")); - quote! { - #ident { - trixy_output: #output , - #(#inputs),* - } - } - } else if output.is_none() && !inputs.is_empty() { - quote! { - #ident { #(#inputs),* } - } - } else { - unreachable!("All other conditions should be met") - } -} - -fn named_type_to_rust(named_type: &NamedType) -> TokenStream2 { - let ident = identifier_to_rust(&named_type.name); - let r#type = type_to_rust(&named_type.r#type); - quote! { - #ident : #r#type - } -} -fn type_to_rust(r#type: &Type) -> TokenStream2 { - let ident = identifier_to_rust(&r#type.identifier); - let namespaces_path = type_variant_rust_path(&r#type.identifier.variant); - - let nasp_path = if let Some(nasp_path) = type_variant_rust_path(&r#type.identifier.variant) { - if nasp_path.is_empty() { - quote! { - crate :: - } - } else { - let path = namespaces_path; - quote! { - #path :: - } - } - } else { - quote! {} - }; - - if r#type.generic_args.is_empty() { - quote! { - #nasp_path #ident - } - } else { - let generics: Vec = r#type.generic_args.iter().map(type_to_rust).collect(); - quote! { - #nasp_path #ident <#(#generics),*> - } - } -} -fn doc_identifier_to_rust(doc_identifier: &DocIdentifier) -> TokenStream2 { - let doc_comments: TokenStream2 = doc_identifier - .attributes - .iter() - .map(|attr| attribute_to_rust(&doc_identifier.into(), attr)) - .collect(); - let identifier = identifier_to_rust(&doc_identifier.into()); - - quote! { - #doc_comments - #identifier - } -} - -fn doc_named_type_to_rust(doc_named_type: &DocNamedType) -> TokenStream2 { - let doc_comments: TokenStream2 = doc_named_type - .attributes - .iter() - .map(|attr| attribute_to_rust(&&doc_named_type.name, attr)) - .collect(); - let named_type = named_type_to_rust(&doc_named_type.into()); - quote! { - #doc_comments - pub #named_type - } -} -fn attribute_to_rust(_target: &Identifier, attribute: &Attribute) -> TokenStream2 { - match attribute { - Attribute::doc(comment) => quote! { - #[doc = #comment] - }, - Attribute::error => quote! { - // We simply use thiserror here - #[derive(trixy::__private::thiserror::Error)] - }, - Attribute::msg(msg) => quote! { - #[error(#msg)] - }, - Attribute::derive(_) => unimplemented!("Derive is not used as of now"), - } -} -pub fn type_variant_rust_path(variant: &Variant) -> Option { - fn namespace_to_borrowed(vec: &Vec) -> Vec<&Identifier> { - let vec_2: Vec<&Identifier> = vec.iter().collect(); - vec_2 - } - - let main_namespace; - match variant { - Variant::Structure { namespace } => { - main_namespace = namespace_to_borrowed(namespace); - } - Variant::Enumeration { namespace } => { - main_namespace = namespace_to_borrowed(namespace); - } - _ => return None, - } - Some(namespaces_to_path(&main_namespace[..])) -} -fn namespaces_to_path>(namespaces: &[T]) -> TokenStream2 { - namespaces - .iter() - .fold(TokenStream2::default(), |acc, nasp| { - if nasp.variant == Variant::RootNamespace && nasp.name == "" { - if acc.is_empty() { - quote! { - crate - } - } else { - unreachable!("An root namespace can never come, after another namespcae") - } - } else { - let ident = format_ident!("{}", nasp.name); - if acc.is_empty() { - quote! { - crate :: #ident - } - } else { - quote! { - #acc :: #ident - } - } - } - }) -} -fn namespaces_to_path_expansive(namespaces: &[Identifier]) -> TokenStream2 { - namespaces - .iter() - .fold(TokenStream2::default(), |acc, nasp| { - if nasp.variant == Variant::RootNamespace && nasp.name == "" { - if acc.is_empty() { - quote! { - crate - } - } else { - unreachable!("An root namespace can never come, after another namespcae") - } - } else { - let ident = format_ident!("{}", nasp.name); - if acc.is_empty() { - quote! { - crate :: #ident - } - } else { - quote! { - #acc :: #ident - } - } - } - }) -} diff --git a/trixy-macros/src/lib.rs b/trixy-macros/src/lib.rs deleted file mode 100644 index 8ab6668..0000000 --- a/trixy-macros/src/lib.rs +++ /dev/null @@ -1,150 +0,0 @@ -/* -* Copyright (C) 2023 - 2024: -* The Trinitrix Project -* -* This file is part of the Trixy crate for Trinitrix. -* -* Trixy is free software: you can redistribute it and/or modify -* it under the terms of the Lesser GNU General Public License as -* published by the Free Software Foundation, either version 3 of -* the License, or (at your option) any later version. -* -* This program is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU General Public License for more details. -* -* You should have received a copy of the GNU General Public License -* and the Lesser GNU General Public License along with this program. -* If not, see . -*/ - -use std::{ - env, - fs::{self, File}, - io::Write, - iter, - path::{Path, PathBuf}, - process::Command, -}; - -use trixy_parser::parse_trixy_lang; -use trixy_types::C_TYPE_HEADER; - -use crate::config::TrixyConfig; - -pub mod config; -mod generate; - -const VIM_LINE_RUST: &'static str = "// vim: filetype=rust\n"; -const VIM_LINE_C: &'static str = "// vim: filetype=c\n"; - -impl TrixyConfig { - /// This is the heart of Trixy - /// It mainly does one thing: - /// - Generate a tree of modules from the input trixy file - /// - pub fn generate(&self) { - let source_code = fs::read_to_string(&self.trixy_path).unwrap_or_else(|err| { - panic! {"Can't read file at path: '{}'. The Error is: '{}'", - self.trixy_path.display(), err}; - }); - - let trixy_code = parse_trixy_lang(&source_code).unwrap_or_else(|err| { - panic! {"Parsing of the trixy file failed: \n{}", err} - }); - - // host code - let tokens = generate::generate(&trixy_code, &self); - eprintln!("{}", tokens); - let host_code = prettyplease::unparse( - &syn::parse2(tokens).expect("This code was generated, it should also be parsable"), - ); - let mut host_code_out = fs::File::create(PathBuf::from(format!( - "{}/{}", - env::var("OUT_DIR").expect("The build script should have this define"), - &self.host_code_name.display() - ))) - .expect("This file should always be free to use"); - write!(host_code_out, "{}\n{}", host_code, VIM_LINE_RUST).expect("Write should work"); - - // c header - let c_header = generate::c_api::header::generate(&trixy_code, &self); - let c_header_path = PathBuf::from(format!( - "{}/{}", - env::var("OUT_DIR").expect("The build script should have this define"), - &self.c_header_name.display() - )); - let mut c_header_out = - fs::File::create(&c_header_path).expect("This file should always be free to use"); - write!(c_header_out, "{}\n{}", c_header, VIM_LINE_C).expect("Write should work"); - - Command::new("clang-format") - .args(["--style", "GNU"]) - .args(["-i", &c_header_path.to_str().unwrap()]) - .status() - .unwrap_or_else(|err| { - panic!( - "Failed to format the c header file with `clang-format`; Error: `{}`", - err - ) - }); - - if let Some(dist_dir) = &self.dist_dir_path { - if !dist_dir.is_dir() { - fs::create_dir(dist_dir).unwrap_or_else(|err| { - panic! { - "Failed to create the dist directory ('{}') because of: `{}`", - dist_dir.display(), err} - }); - } - let c_header_dist = PathBuf::from(format!("{}/{}", dist_dir.display(), "generated.h")); - fs::copy(c_header_path, c_header_dist).unwrap_or_else( - |err| panic! {"Failed to copy the c header ('generated.h') to the dist dir because of: `{}`", err}, - ); - let (interface_name, interface_content) = { - let interface_header = format!( - "\ - /* This file is automatcially generated by Trixy */ \n\ - #ifndef TRIXY_INTERFACE_H \n\ - #define TRIXY_INTERFACE_H \n\ - #include \"generated.h\" \n\ - #endif // TRIXY_INTERFACE_H \n\ - " - ); - ("interface.h", interface_header) - }; - - C_TYPE_HEADER - .iter() - .chain(iter::once(&(interface_name, &interface_content[..]))) - .for_each(|(name, content)| { - let path: &Path = &Path::new(name); - if self.check_dist_dir { - if path.exists() { - panic! { - "The file ('{}') already exists in your dist dir ('{}')! - If you want to silence this check set `check_dist_dir` to false", - path.display(), dist_dir.display() - } - } - } - let header_path = - PathBuf::from(format!("{}/{}", dist_dir.display(), path.display())); - let mut file = File::create(&header_path).unwrap_or_else(|err| { - panic! { - "Failed to create the file at '{}' because of: '{}'", - header_path.display(), - err - } - }); - write!(file, "{}", content).unwrap_or_else(|err| { - panic! { - "Failed to copy the c header ('{}') to the dist dir because of: `{}`", - path.display(), - err} - }); - }); - } - } -} diff --git a/trixy-parser/.gitignore b/trixy-parser/.gitignore deleted file mode 100644 index 20c0ba9..0000000 --- a/trixy-parser/.gitignore +++ /dev/null @@ -1,6 +0,0 @@ -# build -/target -/result - -# This crate is a library -Cargo.lock diff --git a/trixy-parser/Cargo.toml b/trixy-parser/Cargo.toml deleted file mode 100644 index c9b4b87..0000000 --- a/trixy-parser/Cargo.toml +++ /dev/null @@ -1,42 +0,0 @@ -# Copyright (C) 2023 - 2024: -# The Trinitrix Project -# -# This file is part of the Trixy crate for Trinitrix. -# -# Trixy is free software: you can redistribute it and/or modify -# it under the terms of the Lesser GNU General Public License as -# published by the Free Software Foundation, either version 3 of -# the License, or (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# and the Lesser GNU General Public License along with this program. -# If not, see . - -[package] -name = "trixy-parser" -version = "0.1.0" -edition = "2021" - -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html - -[dependencies] -clap = { version = "4.4.11", features = ["derive"], optional = true } -convert_case = "0.6.0" -regex = "1.10.3" -thiserror = "1.0.50" -trixy-types = { path = "../trixy-types" } - -[dev-dependencies] -pretty_assertions = "1.4.0" - -[features] -build-binary = ["clap"] - -[[bin]] -name = "trixy-parser" -required-features = ["build-binary"] diff --git a/trixy-parser/README.md b/trixy-parser/README.md deleted file mode 100644 index 9d8c341..0000000 --- a/trixy-parser/README.md +++ /dev/null @@ -1,13 +0,0 @@ -# trixy-lang_parser - -This crate contains a parser (and lexer) for the Trixy language. -The corresponding grammar is in the grammar file [here](./docs/grammar.ebnf) encoded in [Extended Backus-Naur Form](https://en.wikipedia.org/wiki/Extended_Backus%E2%80%93Naur_form). - -## Testing - -A binary (`trixy-parser`) exists, which provides easy access to the different library -parsing steps - -## Docs - -Run `./generate_docs` to turn the grammar file into railroad diagrams. diff --git a/trixy-parser/example/derives.tri b/trixy-parser/example/derives.tri deleted file mode 100644 index dfa2f5e..0000000 --- a/trixy-parser/example/derives.tri +++ /dev/null @@ -1,54 +0,0 @@ -/* -* Copyright (C) 2023 - 2024: -* The Trinitrix Project -* -* This file is part of the Trixy crate for Trinitrix. -* -* Trixy is free software: you can redistribute it and/or modify -* it under the terms of the Lesser GNU General Public License as -* published by the Free Software Foundation, either version 3 of -* the License, or (at your option) any later version. -* -* This program is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU General Public License for more details. -* -* You should have received a copy of the GNU General Public License -* and the Lesser GNU General Public License along with this program. -* If not, see . -*/ - -/// Call out an outstanding person -fn outstanding(name: String); - -enum DogTraining { - Sheep, - Wolf, - Blind, -} - -#[derive("Error")] -enum TrainingMistake { - GotBitten, -} - -struct Dog { - name: String, -} - -struct TrainedDog { - name: String, - training: DogTraining, -} - -mod one { - /// Say hi to a name - fn hi(name: String) -> String; - - /// Train a dog (if it is there, otherwise do nothing) - fn train_dog(dog: Option) -> Result; -} - -// Trixy is a subset of Rust -// vim: syntax=rust cms=//%s diff --git a/trixy-parser/example/derives_minimal.tri b/trixy-parser/example/derives_minimal.tri deleted file mode 100644 index 35ed596..0000000 --- a/trixy-parser/example/derives_minimal.tri +++ /dev/null @@ -1,6 +0,0 @@ -#[derive("Error")] -enum A {} - -// This derive should fail (and produce a wrong spanned error) -#[derive("Error")] -mod B {} diff --git a/trixy-parser/example/empty.tri b/trixy-parser/example/empty.tri deleted file mode 100644 index 1776d65..0000000 --- a/trixy-parser/example/empty.tri +++ /dev/null @@ -1,26 +0,0 @@ -/* -* Copyright (C) 2023 - 2024: -* The Trinitrix Project -* -* This file is part of the Trixy crate for Trinitrix. -* -* Trixy is free software: you can redistribute it and/or modify -* it under the terms of the Lesser GNU General Public License as -* published by the Free Software Foundation, either version 3 of -* the License, or (at your option) any later version. -* -* This program is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU General Public License for more details. -* -* You should have received a copy of the GNU General Public License -* and the Lesser GNU General Public License along with this program. -* If not, see . -*/ - -// an empty comment: -// -// an empty doc comment: -/// -mod test {} diff --git a/trixy-parser/example/failing_derives.tri b/trixy-parser/example/failing_derives.tri deleted file mode 100644 index df22ea3..0000000 --- a/trixy-parser/example/failing_derives.tri +++ /dev/null @@ -1,55 +0,0 @@ -/* -* Copyright (C) 2023 - 2024: -* The Trinitrix Project -* -* This file is part of the Trixy crate for Trinitrix. -* -* Trixy is free software: you can redistribute it and/or modify -* it under the terms of the Lesser GNU General Public License as -* published by the Free Software Foundation, either version 3 of -* the License, or (at your option) any later version. -* -* This program is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU General Public License for more details. -* -* You should have received a copy of the GNU General Public License -* and the Lesser GNU General Public License along with this program. -* If not, see . -*/ - -/// Call out an outstanding person -fn outstanding(name: String); - -enum DogTraining { - Sheep, - Wolf, - Blind, -} - -#[derive("Error")] -enum TrainingMistake { - GotBitten, -} - -struct Dog { - name: String, -} - -struct TrainedDog { - name: String, - training: DogTraining, -} - -#[derive("Error")] -mod one { - /// Say hi to a name - fn hi(name: String) -> String; - - /// Train a dog (if it is there, otherwise do nothing) - fn train_dog(dog: Option) -> Result; -} - -// Trixy is a subset of Rust -// vim: syntax=rust cms=//%s diff --git a/trixy-parser/example/failing_enum_name.tri b/trixy-parser/example/failing_enum_name.tri deleted file mode 100644 index 15b3909..0000000 --- a/trixy-parser/example/failing_enum_name.tri +++ /dev/null @@ -1,35 +0,0 @@ -/* -* Copyright (C) 2023 - 2024: -* The Trinitrix Project -* -* This file is part of the Trixy crate for Trinitrix. -* -* Trixy is free software: you can redistribute it and/or modify -* it under the terms of the Lesser GNU General Public License as -* published by the Free Software Foundation, either version 3 of -* the License, or (at your option) any later version. -* -* This program is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU General Public License for more details. -* -* You should have received a copy of the GNU General Public License -* and the Lesser GNU General Public License along with this program. -* If not, see . -*/ - -mod trinitrix { - /// This enum can't be called Trinitrix, as that's already the name of the namespace - /// (if it's in Pascal case) - enum Trinitrix { - High, - Medium, - Low, - } - - fn execute_callback(priority: Trinitrix); -} - -// That's a flat out lie, but it results in a rather nice syntax highlight compared to nothing: -// vim: syntax=rust diff --git a/trixy-parser/example/failing_types_generic.tri b/trixy-parser/example/failing_types_generic.tri deleted file mode 100644 index 7e7d1c4..0000000 --- a/trixy-parser/example/failing_types_generic.tri +++ /dev/null @@ -1,30 +0,0 @@ -/* -* Copyright (C) 2023 - 2024: -* The Trinitrix Project -* -* This file is part of the Trixy crate for Trinitrix. -* -* Trixy is free software: you can redistribute it and/or modify -* it under the terms of the Lesser GNU General Public License as -* published by the Free Software Foundation, either version 3 of -* the License, or (at your option) any later version. -* -* This program is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU General Public License for more details. -* -* You should have received a copy of the GNU General Public License -* and the Lesser GNU General Public License along with this program. -* If not, see . -*/ - -struct A {} -struct B {} - -enum Error {} - -fn execute_callback(callback: String) -> Error; - -// That's a flat out lie, but it results in a rather nice syntax highlight compared to nothing: -// vim: syntax=rust diff --git a/trixy-parser/example/full.tri b/trixy-parser/example/full.tri deleted file mode 100644 index ee30e2f..0000000 --- a/trixy-parser/example/full.tri +++ /dev/null @@ -1,157 +0,0 @@ -/* -* Copyright (C) 2023 - 2024: -* The Trinitrix Project -* -* This file is part of the Trixy crate for Trinitrix. -* -* Trixy is free software: you can redistribute it and/or modify -* it under the terms of the Lesser GNU General Public License as -* published by the Free Software Foundation, either version 3 of -* the License, or (at your option) any later version. -* -* This program is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU General Public License for more details. -* -* You should have received a copy of the GNU General Public License -* and the Lesser GNU General Public License along with this program. -* If not, see . -*/ - -/// Prints to the output, with a newline. -// HACK(@soispha): The stdlib Lua `print()` function has stdout as output hardcoded, -// redirecting stdout seems too much like a hack thus we are just redefining the print function -// to output to a controlled output. <2023-09-09> -//fn print(input: CommandTransferValue); - -mod trinitrix { - /// Language specific functions, which mirror the `trinitrix.api` namespace. - /// That is, if you have to choose between a `std` and a `api` function choose the `std` - /// one as it will most likely be more high-level and easier to use (as it isn't abstracted - /// over multiple languages). Feel free to drop down to the lower level api, if you feel - /// like that more, it should be as stable and user-oriented as the `std` functions - mod std {} - - /// Debug only functions, these are effectively useless - mod debug { - enum UserGreet { - Friendly, - Angrily, - Hastly, - } - struct GreetedUser { - names: Vec, - new: GreetedUser, - state: UserGreet, - } - /// Greets the user - fn greet(input: String) -> String; - - /// Returns a table of greeted users - fn greet_multiple() -> GreetedUser; - } - - /// General API to change stuff in Trinitrix - mod api { - /// Closes the application - fn exit(); - - /// Send a message to the current room - /// The send message is interpreted literally. - fn room_message_send(msg: String); - - /// Open the help pages at the first occurrence of - /// the input string if it is Some, otherwise open - /// the help pages at the start - fn help(input: Option); - - // Register a function to be used with the Trinitrix API - // (This function is actually implemented in the std namespace) - /* fn register_function(function: RawFunction); */ - - /// Function that change the UI, or UI state - mod ui { - /// Shows the command line - fn command_line_show(); - - /// Hides the command line - fn command_line_hide(); - - /// Go to the next plane - fn cycle_planes(); - /// Go to the previous plane - fn cycle_planes_rev(); - - /// Sets the current app mode to Normal / navigation mode - fn set_mode_normal(); - /// Sets the current app mode to Insert / editing mode - fn set_mode_insert(); - } - - /// Manipulate keymappings, the mode is specified as a String build up of all mode - /// the keymapping should be active in. The mapping works as follows: - /// n => normal Mode - /// c => command Mode - /// i => insert Mode - /// - /// The key works in a similar matter, specifying the required keypresses to trigger the - /// callback. For example "aba" for require the user to press "a" then "b" then "a" again - /// to trigger the mapping. Special characters are encoded as follows: - /// "ba" => "Ctrl+a" then "b" then "a" - /// "" => "A" or "Shift+a" - /// "A" => "A" - /// " " => "Alt+a" () or "Meta+a"() (most terminals can't really differentiate between these characters) - /// "a" => "a" then "Ctrl+b" then "Ctrl+a" (also works for Shift, Alt and Super) - /// "" => "Ctrl+Shift+Alt+b" (the ordering doesn't matter) - /// "a " => "a" then a literal space (" ") - /// "å🙂" => "å" then "🙂" (full Unicode support!) - /// "" => escape key - /// "" => F3 key - /// "" => backspace key (and so forth) - /// "" => a literal "-" - /// "" or "" => a literal "<" - /// "" or "" => a literal ">" - /// - /// The callback MUST be registered first by calling - /// `trinitrix.api.register_function()` the returned value can than be used to - /// set the keymap. - mod keymaps { - /// Add a new keymapping - fn add(mode: String, key: String, callback: Function); - - /// Remove a keymapping - /// - /// Does nothing, if the keymapping doesn't exists - fn remove(mode: String, key: String); - - /// List declared keymappings - fn get(mode: String); - } - - /// Functions only used internally within Trinitrix - mod raw { - /// Send an error to the default error output - fn raise_error(input: String); - - /// Send output to the default output - /// This is mainly used to display the final - /// output of evaluated lua commands. - fn display_output(input: String); - - /// Input a character without checking for possible keymaps - /// If the current state does not expect input, this character is ignored - /// The encoding is the same as in the `trinitrix.api.keymaps` commands - fn send_input_unprocessed(input: String); - - /// This namespace is used to store some command specific data (like functions, as - /// ensuring memory locations stay allocated in garbage collected language is hard) - /// - /// Treat it as an implementation detail - mod __private {} - } - } -} - -// That's a flat out lie, but it results in a rather nice syntax highlight compared to nothing: -// vim: syntax=rust diff --git a/trixy-parser/example/functions.tri b/trixy-parser/example/functions.tri deleted file mode 100644 index b99b4e5..0000000 --- a/trixy-parser/example/functions.tri +++ /dev/null @@ -1,31 +0,0 @@ -/* -* Copyright (C) 2023 - 2024: -* The Trinitrix Project -* -* This file is part of the Trixy crate for Trinitrix. -* -* Trixy is free software: you can redistribute it and/or modify -* it under the terms of the Lesser GNU General Public License as -* published by the Free Software Foundation, either version 3 of -* the License, or (at your option) any later version. -* -* This program is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU General Public License for more details. -* -* You should have received a copy of the GNU General Public License -* and the Lesser GNU General Public License along with this program. -* If not, see . -*/ - -/// Call out an outstanding person -fn call_me_back_outstanding(callback: fn(name: String) -> String); - -mod one { - /// Call out a person - fn call_me_back(callback: fn(age: u32)); -} - -// Trixy is a subset of Rust -// vim: syntax=rust cms=//%s diff --git a/trixy-parser/example/simple.tri b/trixy-parser/example/simple.tri deleted file mode 100644 index eff99fe..0000000 --- a/trixy-parser/example/simple.tri +++ /dev/null @@ -1,29 +0,0 @@ -/* -* Copyright (C) 2023 - 2024: -* The Trinitrix Project -* -* This file is part of the Trixy crate for Trinitrix. -* -* Trixy is free software: you can redistribute it and/or modify -* it under the terms of the Lesser GNU General Public License as -* published by the Free Software Foundation, either version 3 of -* the License, or (at your option) any later version. -* -* This program is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU General Public License for more details. -* -* You should have received a copy of the GNU General Public License -* and the Lesser GNU General Public License along with this program. -* If not, see . -*/ - -fn print(message: CommandTransferValue); - -mod trinitrix { - fn hi(name: String) -> String; -} - -// That's a flat out lie, but it results in a rather nice syntax highlight compared to nothing: -// vim: syntax=rust diff --git a/trixy-parser/example/trinitrix_api.tri b/trixy-parser/example/trinitrix_api.tri deleted file mode 100644 index 885e471..0000000 --- a/trixy-parser/example/trinitrix_api.tri +++ /dev/null @@ -1,117 +0,0 @@ -//// Prints to the output, with a newline. -// HACK(@soispha): The stdlib Lua `print()` function has stdout as output hardcoded, -// redirecting stdout seems too much like a hack thus we are just redefining the print function -// to output to a controlled output. <2023-09-09> -// This is implemented only for lua -/* fn print(CommandTransferValue); */ - -mod trinitrix { - /// Language specific functions, which mirror the `trinitrix.api` namespace. - /// That is, if you have to choose between a `std` and a `api` function choose the `std` - /// one as it will most likely be more high-level and easier to use (as it isn't abstracted - /// over multiple languages). Feel free to drop down to the lower level api, if you feel - /// like that more, it should be as stable and user-oriented as the `std` functions - mod std {} - - /// General API to change stuff in Trinitrix - mod api { - /// Closes the application - fn exit(); - - /// Send a message to the current room - /// The send message is interpreted literally. - fn room_message_send(message: String); - - //// Open the help pages at the first occurrence of - //// the input string if it is Some, otherwise open - //// the help pages at the start - // TODO(@soispha): To be implemented <2024-03-09> - // fn help(Option); - - //// Register a function to be used with the Trinitrix api - // (This function is not actually implemented here) - /* declare register_function: false, */ - - /// Function that change the UI, or UI state - mod ui { - /// Shows the command line - fn command_line_show(); - - /// Hides the command line - fn command_line_hide(); - - /// Go to the next plane - fn cycle_planes(); - /// Go to the previous plane - fn cycle_planes_rev(); - - /// Sets the current app mode to Normal / navigation mode - fn set_mode_normal(); - /// Sets the current app mode to Insert / editing mode - fn set_mode_insert(); - } - - /// Manipulate keymappings, the mode is specified as a String build up of all mode - /// the keymapping should be active in. The mapping works as follows: - /// n => normal Mode - /// c => command Mode - /// i => insert Mode - /// - /// The key works in a similar matter, specifying the required keypresses to trigger the - /// callback. For example "aba" for require the user to press "a" then "b" then "a" again - /// to trigger the mapping. Special characters are encoded as follows: - /// "ba" => "Ctrl+a" then "b" then "a" - /// "" => "A" or "Shift+a" - /// "A" => "A" - /// " " => "Alt+a" () or "Meta+a"() (most terminals can't really differentiate between these characters) - /// "a" => "a" then "Ctrl+b" then "Ctrl+a" (also works for Shift, Alt and Super) - /// "" => "Ctrl+Shift+Alt+b" (the ordering doesn't matter) - /// "a " => "a" then a literal space (" ") - /// "å🙂" => "å" then "🙂" (full Unicode support!) - /// "" => escape key - /// "" => F3 key - /// "" => backspace key (and so forth) - /// "" => a literal "-" - /// "" or "" => a literal "<" - /// "" or "" => a literal ">" - /// - /// The callback MUST be registered first by calling - /// `trinitrix.api.register_function()` the returned value can than be used to - /// set the keymap. - mod keymaps { - /// Add a new keymapping - fn add(mode: String, key: String, callback: fn()); - - /// Remove a keymapping - /// - /// Does nothing, if the keymapping doesn't exists yet - fn remove(mode: String, key: String); - - /// List declared keymappings - fn get(mode: String); - } - - /// Functions only used internally within Trinitrix - mod raw { - /// Send an error to the default error output - fn raise_error(error_message: String); - - /// Send output to the default output - /// This is mainly used to display the final - /// output of evaluated lua commands. - fn display_output(output_message: String); - - /// Input a character without checking for possible keymaps - /// If the current state does not expect input, this character is ignored - /// The encoding is the same as in the `trinitrix.api.keymaps` commands - fn send_input_unprocessed(input: String); - - /// This namespace is used to store some command specific data (like functions, as - /// ensuring memory locations stay allocated in garbage collected language is hard) - /// - /// Treat it as an implementation detail - mod __private {} - } - } -} -// vim: syntax=rust diff --git a/trixy-parser/src/bin/trixy-parser.rs b/trixy-parser/src/bin/trixy-parser.rs deleted file mode 100644 index 3545150..0000000 --- a/trixy-parser/src/bin/trixy-parser.rs +++ /dev/null @@ -1,167 +0,0 @@ -/* -* Copyright (C) 2023 - 2024: -* The Trinitrix Project -* -* This file is part of the Trixy crate for Trinitrix. -* -* Trixy is free software: you can redistribute it and/or modify -* it under the terms of the Lesser GNU General Public License as -* published by the Free Software Foundation, either version 3 of -* the License, or (at your option) any later version. -* -* This program is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU General Public License for more details. -* -* You should have received a copy of the GNU General Public License -* and the Lesser GNU General Public License along with this program. -* If not, see . -*/ - -use std::{fs, path::PathBuf, process::exit}; - -use clap::{Parser, Subcommand}; - -use trixy_parser::{lexing::TokenStream, parse_trixy_lang}; - -/// A helper command for the trixy-lang_parser crate -#[derive(Parser, Debug)] -#[clap(author, version, about, long_about = None)] -pub struct Args { - #[command(subcommand)] - /// The subcommand to execute - pub subcommand: Command, -} - -#[derive(Subcommand, Debug)] -pub enum Command { - #[clap(value_parser)] - /// Only replace the regex replacements in the file - Replace { - #[clap(value_parser)] - /// The file containing the trixy code to replace - file: PathBuf, - }, - - #[clap(value_parser)] - /// Only try to tokenize the file - Tokenize { - #[clap(value_parser)] - /// The file containing the trixy code to tokenize - file: PathBuf, - }, - #[clap(value_parser)] - /// Only try to tokenize the file - Lex { - #[clap(value_parser)] - /// The file containing the trixy code to tokenize - file: PathBuf, - }, - - /// Check syntax, without type checking - Parse { - #[clap(value_parser)] - /// The file containing the trixy code to parse - file: PathBuf, - }, - /// Type check - Process { - #[clap(value_parser)] - /// The file containing the trixy code to process - file: PathBuf, - }, - - /// Act on the file as the library would do it - Library { - #[clap(value_parser)] - /// The file containing the trixy code to process - file: PathBuf, - }, -} - -pub fn main() { - let args = Args::parse(); - match args.subcommand { - Command::Tokenize { file } | Command::Lex { file } => { - let input = fs::read_to_string(file).unwrap(); - - let input_tokens = match TokenStream::lex(&input) { - Ok(err) => err, - Err(ok) => { - eprintln!("{}", ok); - exit(1); - } - }; - - println!("{:#?}", input_tokens); - } - Command::Parse { file } => { - let input = fs::read_to_string(file).unwrap(); - - let input_tokens = match TokenStream::lex(&input) { - Ok(ok) => ok, - Err(err) => { - eprintln!("Error while tokenizing:"); - eprintln!("{}", err); - exit(1); - } - }; - - let parsed = match input_tokens.parse_unchecked() { - Ok(ok) => ok, - Err(err) => { - eprintln!("Error while doing the first (unchecked) parsing run:"); - eprintln!("{}", err); - exit(1) - } - }; - println!("{:#?}", parsed); - } - Command::Process { file } => { - let input = fs::read_to_string(file).unwrap(); - - let input_tokens = match TokenStream::lex(&input) { - Ok(ok) => ok, - Err(err) => { - eprintln!("Error while tokenizing:"); - eprintln!("{}", err); - exit(1); - } - }; - - let parsed = match input_tokens.parse_unchecked() { - Ok(ok) => ok, - Err(err) => { - eprintln!("Error while doing the first (unchecked) parsing run:"); - eprintln!("{}", err); - exit(1) - } - }; - - let processed = match parsed.process(input) { - Ok(ok) => ok, - Err(err) => { - eprintln!("Error while doing the second (checked) parsing run:"); - eprintln!("{}", err); - exit(1) - } - }; - println!("{:#?}", processed); - } - Command::Library { file } => { - let input = fs::read_to_string(file).unwrap(); - let parsed = parse_trixy_lang(&input).unwrap_or_else(|err| { - eprintln!("{}", err); - exit(1) - }); - println!("{:#?}", parsed); - } - - Command::Replace { file } => { - let input = fs::read_to_string(file).unwrap(); - let parsed = TokenStream::replace(&input); - println!("{}", parsed); - } - } -} diff --git a/trixy-types/.gitignore b/trixy-types/.gitignore deleted file mode 100644 index 20c0ba9..0000000 --- a/trixy-types/.gitignore +++ /dev/null @@ -1,6 +0,0 @@ -# build -/target -/result - -# This crate is a library -Cargo.lock diff --git a/trixy-types/Cargo.toml b/trixy-types/Cargo.toml deleted file mode 100644 index 7c455dd..0000000 --- a/trixy-types/Cargo.toml +++ /dev/null @@ -1,32 +0,0 @@ -# Copyright (C) 2023 - 2024: -# The Trinitrix Project -# -# This file is part of the Trixy crate for Trinitrix. -# -# Trixy is free software: you can redistribute it and/or modify -# it under the terms of the Lesser GNU General Public License as -# published by the Free Software Foundation, either version 3 of -# the License, or (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# and the Lesser GNU General Public License along with this program. -# If not, see . - -[package] -name = "trixy-types" -version = "0.1.0" -edition = "2021" - -[dependencies] -convert_case = "0.6.0" -libc = "0.2.151" -log = "0.4.20" -proc-macro2 = "1.0.70" -quote = "1.0.33" -syn = { version = "2.0.41", features = ["extra-traits", "full", "parsing"] } -thiserror = "1.0.51"