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.
This commit is contained in:
parent
c898c48de2
commit
eb7a901d09
31
Cargo.toml
31
Cargo.toml
|
@ -23,7 +23,30 @@ version = "0.1.0"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
trixy-parser = { path = "./trixy-parser" }
|
clap = { version = "4.5.4", features = ["derive"], optional = true }
|
||||||
trixy-macros = { path = "./trixy-macros" }
|
convert_case = {version = "0.6.0", optional = true}
|
||||||
trixy-types = { path = "./trixy-types" }
|
proc-macro2 = {version = "1.0.79", optional = true}
|
||||||
thiserror = "1.0.57"
|
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" ]
|
||||||
|
|
|
@ -18,7 +18,7 @@
|
||||||
# and the Lesser GNU General Public License along with this program.
|
# and the Lesser GNU General Public License along with this program.
|
||||||
# If not, see <https://www.gnu.org/licenses/>.
|
# If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
ebnf2pdf make "./docs/grammar.ebnf"
|
ebnf2pdf make "./grammar.ebnf"
|
||||||
mv grammar.ebnf.pdf ./docs/grammar.pdf
|
mv grammar.ebnf.pdf ./grammar.pdf
|
||||||
|
|
||||||
# vim: ft=sh
|
# vim: ft=sh
|
12
src/lib.rs
12
src/lib.rs
|
@ -19,13 +19,13 @@
|
||||||
* If not, see <https://www.gnu.org/licenses/>.
|
* If not, see <https://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
pub mod types {
|
#[cfg(feature = "macros")]
|
||||||
pub use trixy_types::*;
|
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 {
|
pub mod __private {
|
||||||
//! This module contains crates needed for the generated code, it should not be used by humans.
|
//! This module contains crates needed for the generated code, it should not be used by humans.
|
||||||
pub mod thiserror {
|
pub mod thiserror {
|
||||||
|
|
|
@ -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<GeneratedFile>,
|
||||||
|
|
||||||
|
/// Files, that should be shared with the consumer (e. g. c headers).
|
||||||
|
pub auxiliary_files: Vec<GeneratedFile>,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 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::<io::Result<()>>()?;
|
||||||
|
self.auxiliary_files
|
||||||
|
.into_iter()
|
||||||
|
.map(|file| -> io::Result<()> { file.materialize() })
|
||||||
|
.collect::<io::Result<()>>()?;
|
||||||
|
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::<std::fmt::Result>()?;
|
||||||
|
|
||||||
|
f.write_str("# Auxiliary files\n")?;
|
||||||
|
self.auxiliary_files
|
||||||
|
.iter()
|
||||||
|
.map(|file| -> std::fmt::Result { f.write_str(&file.to_string()) })
|
||||||
|
.collect::<std::fmt::Result>()
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,2 @@
|
||||||
|
pub mod file_tree;
|
||||||
|
pub mod trixy;
|
|
@ -21,64 +21,72 @@
|
||||||
|
|
||||||
//! This module is responsible for the config passed to trixy.
|
//! This module is responsible for the config passed to trixy.
|
||||||
//! It works using the popular builder syntax:
|
//! It works using the popular builder syntax:
|
||||||
//! ```no_run
|
//! ```
|
||||||
//! use trixy_macros::config::{Language, TrixyConfig};
|
//! use crate::macros::config::{Language, TrixyConfig};
|
||||||
//!# fn main() {
|
//!# fn main() {
|
||||||
//! let config = TrixyConfig::new()
|
//! let config = TrixyConfig::new()
|
||||||
//! .set_input_path("path/to/trixy/api.tri")
|
//! .trixy_path("path/to/trixy/api.tri")
|
||||||
//! .set_output_path("api.rs")
|
//! .output_path("api.rs")
|
||||||
//! .set_languages(vec![Language::Rust, Language::C])
|
//! .languages(vec![Language::Rust, Language::C])
|
||||||
//! .set_generate_debug(false);
|
|
||||||
//!# }
|
//!# }
|
||||||
//! ```
|
//! ```
|
||||||
|
|
||||||
use std::path::PathBuf;
|
use std::{env, path::PathBuf};
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug, Default)]
|
||||||
pub enum Language {
|
pub enum Language {
|
||||||
Rust,
|
Rust,
|
||||||
C,
|
C,
|
||||||
Lua,
|
Lua,
|
||||||
|
#[default]
|
||||||
|
All,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Default, Debug)]
|
#[derive(Default, Debug)]
|
||||||
pub struct TrixyConfig {
|
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,
|
pub trixy_path: PathBuf,
|
||||||
|
|
||||||
/// The name of the outputted host code (rust and c bindings)
|
/// The name of the outputted host code (rust and c bindings).
|
||||||
/// This file is written in $OUT_DIR
|
/// This file is written in $OUT_DIR>
|
||||||
pub host_code_name: PathBuf,
|
pub host_code_name: String,
|
||||||
/// The name of the c header
|
/// The name of the c header.
|
||||||
/// This file is written in $OUT_DIR
|
/// This file is written in $OUT_DIR.
|
||||||
pub c_header_name: PathBuf,
|
pub c_header_name: String,
|
||||||
|
|
||||||
/// The path from the root to the distribution directory.
|
/// The path from the root to the distribution directory.
|
||||||
/// Things like the c headers are copied in this dir
|
/// Things like the c headers are copied in this dir.
|
||||||
/// When this is [None] no dist dir will be generated
|
/// When this is [None] no dist dir will be generated.
|
||||||
pub dist_dir_path: Option<PathBuf>,
|
pub dist_dir_path: Option<PathBuf>,
|
||||||
|
|
||||||
/// Whether to check if the dist dir is empty before writing something to it
|
// /// Whether to check if the dist dir is empty before writing something to it.
|
||||||
pub check_dist_dir: bool,
|
// 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,
|
|
||||||
|
|
||||||
/// This function is executed whenever an API function is called.
|
/// This function is executed whenever an API function is called.
|
||||||
/// The only argument is the command (encoded as the `Commands`) enum
|
/// The only argument is the command (encoded as the `Commands`) enum
|
||||||
/// which is represented by this function
|
/// 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
|
|
||||||
pub callback_function: String,
|
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 {
|
impl TrixyConfig {
|
||||||
pub fn new<T: Into<String>>(callback_function: T) -> Self {
|
pub fn new<T: Into<String>>(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 {
|
Self {
|
||||||
callback_function: callback_function.into(),
|
callback_function: callback_function.into(),
|
||||||
host_code_name: "api.rs".into(),
|
host_code_name: "api.rs".into(),
|
||||||
c_header_name: "interface.h".into(),
|
c_header_name: "interface.h".into(),
|
||||||
|
out_dir,
|
||||||
..Default::default()
|
..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<T: Into<PathBuf>>(self, dist_dir_path: T) -> Self {
|
pub fn dist_dir_path<T: Into<PathBuf>>(self, dist_dir_path: T) -> Self {
|
||||||
Self {
|
Self {
|
||||||
dist_dir_path: Some(dist_dir_path.into()),
|
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 {
|
pub fn host_code_name<T: Into<String>>(self, output_path: T) -> Self {
|
||||||
Self {
|
|
||||||
check_dist_dir,
|
|
||||||
..self
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn host_code_name<T: Into<PathBuf>>(self, output_path: T) -> Self {
|
|
||||||
Self {
|
Self {
|
||||||
host_code_name: output_path.into(),
|
host_code_name: output_path.into(),
|
||||||
..self
|
..self
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn c_header_name<T: Into<PathBuf>>(self, output_path: T) -> Self {
|
pub fn out_dir<T: Into<PathBuf>>(self, out_dir: T) -> Self {
|
||||||
|
Self {
|
||||||
|
out_dir: out_dir.into(),
|
||||||
|
..self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn c_header_name<T: Into<String>>(self, output_path: T) -> Self {
|
||||||
Self {
|
Self {
|
||||||
c_header_name: output_path.into(),
|
c_header_name: output_path.into(),
|
||||||
..self
|
..self
|
|
@ -23,14 +23,8 @@
|
||||||
//! It works by firstly listing the functions and then by grouping them into structures, effectively
|
//! It works by firstly listing the functions and then by grouping them into structures, effectively
|
||||||
//! simulating namespaces in c.
|
//! 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::{
|
use crate::{
|
||||||
config::TrixyConfig,
|
macros::config::trixy::TrixyConfig, parser::command_spec::CommandSpec, types::header_names,
|
||||||
generate::{c_api::type_to_c, identifier_to_rust},
|
|
||||||
};
|
};
|
||||||
|
|
||||||
mod pure_header;
|
mod pure_header;
|
||||||
|
@ -109,19 +103,3 @@ pub fn generate(trixy: &CommandSpec, _config: &TrixyConfig) -> String {
|
||||||
END_HEADER_GUARD
|
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
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -19,14 +19,31 @@
|
||||||
* If not, see <https://www.gnu.org/licenses/>.
|
* If not, see <https://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
fn print(message: String);
|
use crate::parser::command_spec::{CommandSpec, Enumeration, Structure};
|
||||||
|
|
||||||
/// First doc comment
|
pub fn generate(trixy: &CommandSpec) -> String {
|
||||||
// Some more text
|
let functions: String = trixy
|
||||||
mod trinitrix {
|
.functions
|
||||||
/// Second doc comment
|
.iter()
|
||||||
fn hi(name: String) -> String;
|
.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
|
|
|
@ -19,15 +19,15 @@
|
||||||
* If not, see <https://www.gnu.org/licenses/>.
|
* If not, see <https://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
fn print(message: CommandTransferValue);
|
use proc_macro2::TokenStream as TokenStream2;
|
||||||
|
|
||||||
mod trinitrix {
|
use crate::parser::command_spec::CommandSpec;
|
||||||
fn hi(name: String) -> String;
|
|
||||||
|
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
|
|
|
@ -19,12 +19,15 @@
|
||||||
* If not, see <https://www.gnu.org/licenses/>.
|
* If not, see <https://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
struct Callback {
|
use crate::parser::command_spec::{CommandSpec, Namespace};
|
||||||
func: Function,
|
|
||||||
timeout: Integer,
|
pub fn generate(trixy: &CommandSpec) -> String {
|
||||||
|
let type_defs: String = trixy
|
||||||
|
.namespaces
|
||||||
|
.iter()
|
||||||
|
.rev()
|
||||||
|
.map(Namespace::to_auxiliary_c_full_typedef)
|
||||||
|
.collect::<Vec<String>>()
|
||||||
|
.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
|
|
|
@ -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
|
||||||
|
)
|
||||||
|
}
|
|
@ -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()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -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::<String>();
|
||||||
|
let ident = &self.identifier.to_auxiliary_c();
|
||||||
|
let states = self
|
||||||
|
.states
|
||||||
|
.iter()
|
||||||
|
.map(DocIdentifier::to_auxiliary_c)
|
||||||
|
.collect::<String>();
|
||||||
|
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
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
|
@ -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::<String>();
|
||||||
|
let ident = self.identifier.to_c_with_path(namespaces);
|
||||||
|
let inputs: Vec<TokenStream2> = 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<TokenStream2> = 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -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::<String>();
|
||||||
|
let ident = &self.name;
|
||||||
|
format!("{}{},", doc_comments, ident)
|
||||||
|
}
|
||||||
|
}
|
|
@ -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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -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::*;
|
|
@ -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::<Vec<String>>()
|
||||||
|
.join("\n");
|
||||||
|
let enumerations: String = self
|
||||||
|
.enumerations
|
||||||
|
.iter()
|
||||||
|
.map(Enumeration::to_auxiliary_c)
|
||||||
|
.collect::<Vec<String>>()
|
||||||
|
.join("\n");
|
||||||
|
let functions: String = self
|
||||||
|
.functions
|
||||||
|
.iter()
|
||||||
|
.map(|r#fn| r#fn.to_auxiliary_c(&nasps))
|
||||||
|
.collect::<Vec<String>>()
|
||||||
|
.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::<String>();
|
||||||
|
|
||||||
|
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::<Vec<String>>()
|
||||||
|
.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 ,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -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::<String>();
|
||||||
|
let ident = self.identifier.to_rust();
|
||||||
|
let contents = self
|
||||||
|
.contents
|
||||||
|
.iter()
|
||||||
|
.map(DocNamedType::to_auxiliary_c)
|
||||||
|
.collect::<String>();
|
||||||
|
format!(
|
||||||
|
"
|
||||||
|
{}
|
||||||
|
{} {{
|
||||||
|
{}
|
||||||
|
}};",
|
||||||
|
doc_comments, ident, contents
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
|
@ -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::<String>();
|
||||||
|
let ident = &self.name.to_rust();
|
||||||
|
let r#type = self.r#type.to_auxiliary_c(false);
|
||||||
|
format!("{}{} {};", doc_comments, r#type, ident)
|
||||||
|
}
|
||||||
|
}
|
|
@ -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<TokenStream2> =
|
||||||
|
inputs.iter().map(NamedType::to_auxiliary_c).collect();
|
||||||
|
|
||||||
|
quote! {
|
||||||
|
#output (*name) (#(#inputs),*)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1 @@
|
||||||
|
mod c;
|
|
@ -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<TokenStream2> = 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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -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<TokenStream2> = 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 [= <input>]
|
||||||
|
/// ->
|
||||||
|
/// one::One::Two(<input>) [= <input>]
|
||||||
|
/// ->
|
||||||
|
/// Commands::One(<input>) [= <input>]
|
||||||
|
/// ```
|
||||||
|
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 )
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -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()
|
||||||
|
}
|
||||||
|
}
|
|
@ -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::*;
|
|
@ -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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -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<TokenStream2> = 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),*
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -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::<NamedType>::into(self).to_c();
|
||||||
|
quote! {
|
||||||
|
#doc_comments
|
||||||
|
pub #named_type
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -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<Box<Type>>) -> TokenStream2 {
|
||||||
|
Type::to_rust_function(&inputs, &output)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn to_c_typical(identifier: &Identifier, generic_args: &Vec<Type>) -> 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: <Result<TrainedDog, TrainingMistake> as Convertible>::Ptr,
|
||||||
|
<Result<#ident_ok, #ident_err> 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<TokenStream2> =
|
||||||
|
generic_args.iter().map(|val| val.to_c()).collect();
|
||||||
|
quote! {
|
||||||
|
<#(#generics),*>
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
quote! {
|
||||||
|
trixy::types:: #ident #generics
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -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<Identifier>) -> Vec<Identifier> {
|
||||||
|
vec.into_iter()
|
||||||
|
.map(|ident| {
|
||||||
|
if "<root>" == &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[..])
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,2 @@
|
||||||
|
pub mod rust;
|
||||||
|
pub mod c;
|
|
@ -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"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -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::<Identifier>::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<paired_type>` 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<paired_type>` 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)
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,2 @@
|
||||||
|
pub mod structure;
|
||||||
|
pub mod enumeration;
|
|
@ -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<Self, trixy::types::error::TypeConversionError> {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// This function generates the `TryFrom<paired_type>` 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<Self, Self::Error> {
|
||||||
|
Ok(Self {
|
||||||
|
#try_into_fields
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -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<TokenStream2> = 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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -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<F>(
|
||||||
|
&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<TokenStream2> = 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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -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::<Identifier>::into(self)))
|
||||||
|
.collect();
|
||||||
|
let identifier = Into::<Identifier>::into(self).to_rust();
|
||||||
|
|
||||||
|
quote! {
|
||||||
|
#attributes
|
||||||
|
#identifier
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -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;
|
|
@ -0,0 +1,2 @@
|
||||||
|
pub mod path;
|
||||||
|
pub mod module;
|
|
@ -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<TokenStream2> = self
|
||||||
|
.functions
|
||||||
|
.iter()
|
||||||
|
.map(|r#fn| r#fn.to_rust(&namespaces))
|
||||||
|
.collect();
|
||||||
|
let namespace_modules: Vec<TokenStream2> = 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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -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<T: Deref<Target = Identifier>>(namespaces: &[T]) -> TokenStream2 {
|
||||||
|
namespaces
|
||||||
|
.iter()
|
||||||
|
.fold(TokenStream2::default(), |acc, nasp| {
|
||||||
|
if nasp.variant == Variant::RootNamespace && nasp.name == "<root>" {
|
||||||
|
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 == "<root>" {
|
||||||
|
// 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
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// })
|
||||||
|
}
|
||||||
|
}
|
|
@ -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<TokenStream2> = 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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -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::<NamedType>::into(self).to_rust();
|
||||||
|
quote! {
|
||||||
|
#attributes
|
||||||
|
pub #named_type
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -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<Box<Type>>) -> TokenStream2 {
|
||||||
|
let inputs: Vec<TokenStream2> = 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<Type>) -> 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<TokenStream2> = generic_args.iter().map(Type::to_rust).collect();
|
||||||
|
quote! {
|
||||||
|
#nasp_path #ident <#(#generics),*>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -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<TokenStream2> {
|
||||||
|
fn namespace_to_borrowed(vec: &Vec<Identifier>) -> 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[..]))
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,2 @@
|
||||||
|
pub mod host;
|
||||||
|
pub mod auxiliary;
|
|
@ -0,0 +1,74 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2023 - 2024:
|
||||||
|
* The Trinitrix Project <soispha@vhack.eu, antifallobst@systemausfall.org>
|
||||||
|
*
|
||||||
|
* 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 <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
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
|
||||||
|
}
|
||||||
|
}
|
|
@ -19,20 +19,23 @@
|
||||||
* If not, see <https://www.gnu.org/licenses/>.
|
* If not, see <https://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/// other doc comment
|
use crate::{
|
||||||
fn hi(name: String) -> String;
|
macros::{config::trixy::TrixyConfig, generate::host::format_rust},
|
||||||
|
parser::command_spec::CommandSpec,
|
||||||
|
};
|
||||||
|
|
||||||
/// struct comment
|
pub mod host;
|
||||||
struct ho {
|
|
||||||
ident: String,
|
pub fn generate(trixy: &CommandSpec, config: &TrixyConfig) -> String {
|
||||||
/// also a doc comment
|
let host_rust_code = host::generate(&trixy, &config);
|
||||||
codebase: Vec<String>,
|
|
||||||
|
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
|
|
|
@ -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
|
||||||
|
)
|
||||||
|
}
|
|
@ -0,0 +1,114 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2023 - 2024:
|
||||||
|
* The Trinitrix Project <soispha@vhack.eu, antifallobst@systemausfall.org>
|
||||||
|
*
|
||||||
|
* 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 <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
//! 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<String>
|
||||||
|
/// },
|
||||||
|
/// }
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
|
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<TokenStream2> = trixy
|
||||||
|
.functions
|
||||||
|
.iter()
|
||||||
|
.map(|r#fn| r#fn.to_rust(&[]))
|
||||||
|
.collect();
|
||||||
|
let namespace_modules: Vec<TokenStream2> = trixy
|
||||||
|
.namespaces
|
||||||
|
.iter()
|
||||||
|
.map(Namespace::to_rust_module_enum)
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
quote! {
|
||||||
|
#structures
|
||||||
|
#enumerations
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum Commands {
|
||||||
|
#(#functions,)*
|
||||||
|
#(#namespace_modules),*
|
||||||
|
}
|
||||||
|
#modules
|
||||||
|
}
|
||||||
|
}
|
|
@ -19,20 +19,22 @@
|
||||||
* If not, see <https://www.gnu.org/licenses/>.
|
* If not, see <https://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
mod trinitrix {
|
use crate::parser::command_spec::CommandSpec;
|
||||||
struct Callback {
|
|
||||||
func: Function,
|
|
||||||
timeout: Integer,
|
|
||||||
}
|
|
||||||
|
|
||||||
enum CallbackPriority {
|
use crate::macros::{config::trixy::TrixyConfig, generate::host::format_rust};
|
||||||
High,
|
|
||||||
Medium,
|
|
||||||
Low,
|
|
||||||
}
|
|
||||||
|
|
||||||
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
|
|
|
@ -0,0 +1,3 @@
|
||||||
|
pub mod host;
|
||||||
|
pub mod auxiliary;
|
||||||
|
pub mod convert;
|
|
@ -0,0 +1,123 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2023 - 2024:
|
||||||
|
* The Trinitrix Project <soispha@vhack.eu, antifallobst@systemausfall.org>
|
||||||
|
*
|
||||||
|
* 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 <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
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
|
||||||
|
}
|
||||||
|
}
|
|
@ -23,7 +23,7 @@
|
||||||
|
|
||||||
use std::fmt::{Display, Write};
|
use std::fmt::{Display, Write};
|
||||||
|
|
||||||
use crate::lexing::TokenKind;
|
use crate::parser::lexing::TokenKind;
|
||||||
|
|
||||||
use super::unchecked::{self, DeriveValue};
|
use super::unchecked::{self, DeriveValue};
|
||||||
|
|
|
@ -25,9 +25,9 @@
|
||||||
|
|
||||||
use std::fmt::{Display, Write};
|
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 struct CommandSpec {
|
||||||
pub structures: Vec<Structure>,
|
pub structures: Vec<Structure>,
|
||||||
pub enumerations: Vec<Enumeration>,
|
pub enumerations: Vec<Enumeration>,
|
||||||
|
@ -189,7 +189,7 @@ pub enum Type {
|
||||||
impl Display for NamedType {
|
impl Display for NamedType {
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
let ident = match self.name.kind() {
|
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"),
|
_ => panic!("Tried to display a non identifier token in the Type display implementation. This is a bug"),
|
||||||
};
|
};
|
||||||
f.write_str(ident)?;
|
f.write_str(ident)?;
|
||||||
|
@ -206,7 +206,7 @@ impl Display for Type {
|
||||||
generic_args,
|
generic_args,
|
||||||
} => {
|
} => {
|
||||||
let ident = match identifier.kind() {
|
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"),
|
_ => panic!("Tried to display a non identifier token in the Type display implementation. This is a bug"),
|
||||||
};
|
};
|
||||||
|
|
|
@ -23,9 +23,9 @@ use core::fmt;
|
||||||
|
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
|
|
||||||
use crate::{
|
use crate::parser::{
|
||||||
lexing::{error::SpannedLexingError, TokenSpan},
|
lexing::{error::SpannedLexingError, TokenSpan},
|
||||||
parsing::{self},
|
parsing,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(Error, Debug)]
|
#[derive(Error, Debug)]
|
|
@ -22,7 +22,7 @@
|
||||||
use std::{error::Error, fmt::Display};
|
use std::{error::Error, fmt::Display};
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
|
|
||||||
use crate::error::{AdditionalHelp, ErrorContext, ErrorContextDisplay};
|
use crate::parser::error::{AdditionalHelp, ErrorContext, ErrorContextDisplay};
|
||||||
|
|
||||||
#[derive(Error, Debug)]
|
#[derive(Error, Debug)]
|
||||||
pub enum LexingError {
|
pub enum LexingError {
|
||||||
|
@ -67,7 +67,7 @@ impl Error for SpannedLexingError {
|
||||||
impl ErrorContextDisplay for SpannedLexingError {
|
impl ErrorContextDisplay for SpannedLexingError {
|
||||||
type Error = LexingError;
|
type Error = LexingError;
|
||||||
|
|
||||||
fn context(&self) -> &crate::error::ErrorContext {
|
fn context(&self) -> &crate::parser::error::ErrorContext {
|
||||||
&self.context
|
&self.context
|
||||||
}
|
}
|
||||||
|
|
|
@ -31,7 +31,7 @@ mod tokenizer;
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod test;
|
mod test;
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, PartialOrd, Ord, Eq)]
|
#[derive(Debug, PartialEq, PartialOrd, Ord, Eq, Clone)]
|
||||||
pub struct TokenStream {
|
pub struct TokenStream {
|
||||||
pub original_file: String,
|
pub original_file: String,
|
||||||
tokens: Vec<Token>,
|
tokens: Vec<Token>,
|
||||||
|
@ -308,7 +308,7 @@ impl Display for AttributeKeyword {
|
||||||
/// # Examples
|
/// # Examples
|
||||||
///
|
///
|
||||||
/// ```
|
/// ```
|
||||||
/// use trixy_parser::token;
|
/// use crate::parser::token;
|
||||||
///# fn main() {
|
///# fn main() {
|
||||||
/// token![mod];
|
/// token![mod];
|
||||||
/// token![;];
|
/// token![;];
|
||||||
|
@ -317,56 +317,56 @@ impl Display for AttributeKeyword {
|
||||||
/// ```
|
/// ```
|
||||||
#[macro_export]
|
#[macro_export]
|
||||||
macro_rules! token {
|
macro_rules! token {
|
||||||
[Semicolon] => { $crate::lexing::TokenKind::Semicolon };
|
[Semicolon] => { $crate::parser::lexing::TokenKind::Semicolon };
|
||||||
[;] => { $crate::lexing::TokenKind::Semicolon };
|
[;] => { $crate::parser::lexing::TokenKind::Semicolon };
|
||||||
[Colon] => { $crate::lexing::TokenKind::Colon };
|
[Colon] => { $crate::parser::lexing::TokenKind::Colon };
|
||||||
[:] => { $crate::lexing::TokenKind::Colon };
|
[:] => { $crate::parser::lexing::TokenKind::Colon };
|
||||||
[Comma] => { $crate::lexing::TokenKind::Comma };
|
[Comma] => { $crate::parser::lexing::TokenKind::Comma };
|
||||||
[,] => { $crate::lexing::TokenKind::Comma };
|
[,] => { $crate::parser::lexing::TokenKind::Comma };
|
||||||
[Arrow] => { $crate::lexing::TokenKind::Arrow };
|
[Arrow] => { $crate::parser::lexing::TokenKind::Arrow };
|
||||||
[->] => { $crate::lexing::TokenKind::Arrow };
|
[->] => { $crate::parser::lexing::TokenKind::Arrow };
|
||||||
[PoundSign] => { $crate::lexing::TokenKind::PoundSign };
|
[PoundSign] => { $crate::parser::lexing::TokenKind::PoundSign };
|
||||||
[#] => { $crate::lexing::TokenKind::PoundSign };
|
[#] => { $crate::parser::lexing::TokenKind::PoundSign };
|
||||||
[EqualsSign] => { $crate::lexing::TokenKind::EqualsSign };
|
[EqualsSign] => { $crate::parser::lexing::TokenKind::EqualsSign };
|
||||||
[=] => { $crate::lexing::TokenKind::EqualsSign };
|
[=] => { $crate::parser::lexing::TokenKind::EqualsSign };
|
||||||
|
|
||||||
|
|
||||||
[AngledBracketOpen] => { $crate::lexing::TokenKind::AngledBracketOpen };
|
[AngledBracketOpen] => { $crate::parser::lexing::TokenKind::AngledBracketOpen };
|
||||||
[<] => { $crate::lexing::TokenKind::AngledBracketOpen };
|
[<] => { $crate::parser::lexing::TokenKind::AngledBracketOpen };
|
||||||
|
|
||||||
[AngledBracketClose] => { $crate::lexing::TokenKind::AngledBracketClose };
|
[AngledBracketClose] => { $crate::parser::lexing::TokenKind::AngledBracketClose };
|
||||||
[>] => { $crate::lexing::TokenKind::AngledBracketClose };
|
[>] => { $crate::parser::lexing::TokenKind::AngledBracketClose };
|
||||||
|
|
||||||
[CurlyBracketOpen] => { $crate::lexing::TokenKind::CurlyBracketOpen};
|
[CurlyBracketOpen] => { $crate::parser::lexing::TokenKind::CurlyBracketOpen};
|
||||||
// [{] => { $crate::lexing::TokenKind::CurlyBracketOpen };
|
// [{] => { $crate::parser::lexing::TokenKind::CurlyBracketOpen };
|
||||||
[CurlyBracketClose] => { $crate::lexing::TokenKind::CurlyBracketClose};
|
[CurlyBracketClose] => { $crate::parser::lexing::TokenKind::CurlyBracketClose};
|
||||||
// [}] => { $crate::lexing::TokenKind::CurlyBracketClose };
|
// [}] => { $crate::parser::lexing::TokenKind::CurlyBracketClose };
|
||||||
[CurvedBracketOpen] => { $crate::lexing::TokenKind::CurvedBracketOpen};
|
[CurvedBracketOpen] => { $crate::parser::lexing::TokenKind::CurvedBracketOpen};
|
||||||
// [(] => { $crate::lexing::TokenKind::ParenthesisOpen };
|
// [(] => { $crate::parser::lexing::TokenKind::ParenthesisOpen };
|
||||||
[CurvedBracketClose] => { $crate::lexing::TokenKind::CurvedBracketClose};
|
[CurvedBracketClose] => { $crate::parser::lexing::TokenKind::CurvedBracketClose};
|
||||||
// [)] => { $crate::lexing::TokenKind::ParenthesisClose };
|
// [)] => { $crate::parser::lexing::TokenKind::ParenthesisClose };
|
||||||
[SquareBracketOpen] => { $crate::lexing::TokenKind::SquareBracketOpen};
|
[SquareBracketOpen] => { $crate::parser::lexing::TokenKind::SquareBracketOpen};
|
||||||
// [[] => { $crate::lexing::TokenKind::ParenthesisOpen };
|
// [[] => { $crate::parser::lexing::TokenKind::ParenthesisOpen };
|
||||||
[SquareBracketClose] => { $crate::lexing::TokenKind::SquareBracketClose};
|
[SquareBracketClose] => { $crate::parser::lexing::TokenKind::SquareBracketClose};
|
||||||
// []] => { $crate::lexing::TokenKind::ParenthesisClose };
|
// []] => { $crate::parser::lexing::TokenKind::ParenthesisClose };
|
||||||
|
|
||||||
|
|
||||||
[mod] => { $crate::lexing::TokenKind::Keyword($crate::lexing::Keyword::r#mod) };
|
[mod] => { $crate::parser::lexing::TokenKind::Keyword($crate::parser::lexing::Keyword::r#mod) };
|
||||||
[fn] => { $crate::lexing::TokenKind::Keyword($crate::lexing::Keyword::r#fn) };
|
[fn] => { $crate::parser::lexing::TokenKind::Keyword($crate::parser::lexing::Keyword::r#fn) };
|
||||||
[struct] => { $crate::lexing::TokenKind::Keyword($crate::lexing::Keyword::r#struct) };
|
[struct] => { $crate::parser::lexing::TokenKind::Keyword($crate::parser::lexing::Keyword::r#struct) };
|
||||||
[enum] => { $crate::lexing::TokenKind::Keyword($crate::lexing::Keyword::r#enum) };
|
[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`)
|
// 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
|
// This is only works for checking for a identifier or comment
|
||||||
// see the `same_kind` method on TokenKind
|
// see the `same_kind` method on TokenKind
|
||||||
[Ident] => { $crate::lexing::TokenKind::Identifier("".to_owned()) };
|
[Ident] => { $crate::parser::lexing::TokenKind::Identifier("".to_owned()) };
|
||||||
[Identifier] => { $crate::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)]
|
#[cfg(test)]
|
||||||
|
@ -397,6 +397,6 @@ mod tests {
|
||||||
|
|
||||||
token_macro_test!(tok_expands_to_arrow, ->, => TokenKind::Arrow);
|
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_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_mod, mod, => TokenKind::Keyword(crate::parser::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_fn, fn, => TokenKind::Keyword(crate::parser::lexing::Keyword::r#fn));
|
||||||
}
|
}
|
|
@ -21,7 +21,7 @@
|
||||||
|
|
||||||
// This code is heavily inspired by: https://michael-f-bryan.github.io/static-analyser-in-rust/book/lex.html
|
// 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,
|
error::ErrorContext,
|
||||||
lexing::{Keyword, TokenSpan},
|
lexing::{Keyword, TokenSpan},
|
||||||
};
|
};
|
|
@ -21,7 +21,7 @@
|
||||||
|
|
||||||
use error::TrixyError;
|
use error::TrixyError;
|
||||||
|
|
||||||
use crate::lexing::TokenStream;
|
use crate::parser::lexing::TokenStream;
|
||||||
|
|
||||||
use self::command_spec::checked::CommandSpec;
|
use self::command_spec::checked::CommandSpec;
|
||||||
|
|
|
@ -23,7 +23,7 @@ use thiserror::Error;
|
||||||
|
|
||||||
use std::{error::Error, fmt::Display};
|
use std::{error::Error, fmt::Display};
|
||||||
|
|
||||||
use crate::{
|
use crate::parser::{
|
||||||
command_spec::{checked::Identifier, unchecked::Attribute},
|
command_spec::{checked::Identifier, unchecked::Attribute},
|
||||||
error::{AdditionalHelp, ErrorContext, ErrorContextDisplay},
|
error::{AdditionalHelp, ErrorContext, ErrorContextDisplay},
|
||||||
lexing::TokenSpan,
|
lexing::TokenSpan,
|
||||||
|
@ -133,7 +133,7 @@ impl Display for SpannedParsingError {
|
||||||
impl ErrorContextDisplay for SpannedParsingError {
|
impl ErrorContextDisplay for SpannedParsingError {
|
||||||
type Error = ParsingError;
|
type Error = ParsingError;
|
||||||
|
|
||||||
fn context(&self) -> &crate::error::ErrorContext {
|
fn context(&self) -> &crate::parser::error::ErrorContext {
|
||||||
&self.context
|
&self.context
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,10 +21,10 @@
|
||||||
|
|
||||||
use std::{iter, mem};
|
use std::{iter, mem};
|
||||||
|
|
||||||
|
use crate::types::BASE_TYPES;
|
||||||
use convert_case::{Case, Casing};
|
use convert_case::{Case, Casing};
|
||||||
use trixy_types::BASE_TYPES;
|
|
||||||
|
|
||||||
use crate::{
|
use crate::parser::{
|
||||||
command_spec::{
|
command_spec::{
|
||||||
checked::{
|
checked::{
|
||||||
self, CommandSpec, DocIdentifier, DocNamedType, Enumeration, Function, Identifier,
|
self, CommandSpec, DocIdentifier, DocNamedType, Enumeration, Function, Identifier,
|
||||||
|
@ -68,13 +68,13 @@ macro_rules! take_attrs {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
(@process_val $iden:ident, $val:ident) => {
|
(@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());
|
return Ok($iden.into());
|
||||||
};
|
};
|
||||||
take_attrs!{@process_val_last $iden}
|
take_attrs!{@process_val_last $iden}
|
||||||
};
|
};
|
||||||
(@process_val $iden:ident, $val:ident, $($other:tt),+ $(,)*) => {
|
(@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());
|
return Ok($iden.into());
|
||||||
};
|
};
|
||||||
take_attrs!{@process_val $iden, $($other),*}
|
take_attrs!{@process_val $iden, $($other),*}
|
|
@ -22,7 +22,7 @@
|
||||||
use std::{error::Error, fmt::Display};
|
use std::{error::Error, fmt::Display};
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
|
|
||||||
use crate::{
|
use crate::parser::{
|
||||||
command_spec::unchecked::{Attribute, StringLiteral},
|
command_spec::unchecked::{Attribute, StringLiteral},
|
||||||
error::{AdditionalHelp, ErrorContext, ErrorContextDisplay},
|
error::{AdditionalHelp, ErrorContext, ErrorContextDisplay},
|
||||||
lexing::{TokenKind, TokenSpan},
|
lexing::{TokenKind, TokenSpan},
|
||||||
|
@ -113,7 +113,7 @@ impl Display for SpannedParsingError {
|
||||||
impl ErrorContextDisplay for SpannedParsingError {
|
impl ErrorContextDisplay for SpannedParsingError {
|
||||||
type Error = ParsingError;
|
type Error = ParsingError;
|
||||||
|
|
||||||
fn context(&self) -> &crate::error::ErrorContext {
|
fn context(&self) -> &crate::parser::error::ErrorContext {
|
||||||
&self.context
|
&self.context
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,12 +22,14 @@
|
||||||
use std::{iter::once, mem};
|
use std::{iter::once, mem};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
|
parser::{
|
||||||
command_spec::unchecked::{
|
command_spec::unchecked::{
|
||||||
Attribute, CommandSpec, Declaration, DeriveValue, DocNamedType, DocToken, Enumeration,
|
Attribute, CommandSpec, Declaration, DeriveValue, DocNamedType, DocToken, Enumeration,
|
||||||
Function, NamedType, Namespace, StringLiteral, Structure, Type,
|
Function, NamedType, Namespace, StringLiteral, Structure, Type,
|
||||||
},
|
},
|
||||||
error::ErrorContext,
|
error::ErrorContext,
|
||||||
lexing::{AttributeKeyword, Token, TokenKind, TokenSpan, TokenStream},
|
lexing::{AttributeKeyword, Token, TokenKind, TokenSpan, TokenStream},
|
||||||
|
},
|
||||||
token,
|
token,
|
||||||
};
|
};
|
||||||
|
|
|
@ -19,7 +19,7 @@
|
||||||
* If not, see <https://www.gnu.org/licenses/>.
|
* If not, see <https://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
use crate::error::{self, TypeConversionError};
|
use crate::types::error::{self, TypeConversionError};
|
||||||
use log::warn;
|
use log::warn;
|
||||||
use std::{
|
use std::{
|
||||||
error::Error,
|
error::Error,
|
||||||
|
@ -180,7 +180,7 @@ impl<T: Convertible, E: Error> Convertible for Result<T, E> {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: Convertible> Convertible for Vec<T> {
|
impl<T: Convertible> Convertible for Vec<T> {
|
||||||
type Ptr = crate::Vec<<T as Convertible>::Ptr>;
|
type Ptr = crate::types::Vec<<T as Convertible>::Ptr>;
|
||||||
|
|
||||||
fn into_ptr(self) -> Self::Ptr {
|
fn into_ptr(self) -> Self::Ptr {
|
||||||
let data_vec: Vec<_> = self.into_iter().map(|val| val.into_ptr()).collect();
|
let data_vec: Vec<_> = self.into_iter().map(|val| val.into_ptr()).collect();
|
||||||
|
@ -222,7 +222,7 @@ impl<T: Convertible> Convertible for Vec<T> {
|
||||||
macro_rules! make_vec_free {
|
macro_rules! make_vec_free {
|
||||||
($value:ident, $name:ident) => {
|
($value:ident, $name:ident) => {
|
||||||
#[no_mangle]
|
#[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);
|
Vec::<$value>::drop_ptr(ptr);
|
||||||
}
|
}
|
||||||
};
|
};
|
|
@ -29,7 +29,7 @@ use std::{
|
||||||
|
|
||||||
use log::{error, warn};
|
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
|
// This code is heavily inspired by: https://michael-f-bryan.github.io/rust-ffi-guide/errors/return_types.html
|
||||||
thread_local! {
|
thread_local! {
|
|
@ -22,4 +22,4 @@
|
||||||
pub mod convert_trait;
|
pub mod convert_trait;
|
||||||
pub mod errno;
|
pub mod errno;
|
||||||
mod try_from_impl;
|
mod try_from_impl;
|
||||||
pub use try_from_impl::*;
|
// pub use try_from_impl::*;
|
|
@ -19,7 +19,7 @@
|
||||||
* If not, see <https://www.gnu.org/licenses/>.
|
* If not, see <https://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
use crate::String;
|
use crate::types::String;
|
||||||
use std::ffi::CString;
|
use std::ffi::CString;
|
||||||
|
|
||||||
use super::convert_trait::Convertible;
|
use super::convert_trait::Convertible;
|
||||||
|
@ -34,7 +34,7 @@ impl From<std::string::String> for String {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TryFrom<String> for std::string::String {
|
impl TryFrom<String> for std::string::String {
|
||||||
type Error = crate::error::TypeConversionError;
|
type Error = crate::types::error::TypeConversionError;
|
||||||
|
|
||||||
fn try_from(value: String) -> Result<Self, Self::Error> {
|
fn try_from(value: String) -> Result<Self, Self::Error> {
|
||||||
let cstring = CString::from_ptr(value.0)?;
|
let cstring = CString::from_ptr(value.0)?;
|
|
@ -1,6 +0,0 @@
|
||||||
# build
|
|
||||||
/target
|
|
||||||
/result
|
|
||||||
|
|
||||||
# This crate is a library
|
|
||||||
Cargo.lock
|
|
|
@ -1,32 +0,0 @@
|
||||||
# Copyright (C) 2023 - 2024:
|
|
||||||
# The Trinitrix Project <soispha@vhack.eu, antifallobst@systemausfall.org>
|
|
||||||
#
|
|
||||||
# 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 <https://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
[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" }
|
|
|
@ -1,194 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (C) 2023 - 2024:
|
|
||||||
* The Trinitrix Project <soispha@vhack.eu, antifallobst@systemausfall.org>
|
|
||||||
*
|
|
||||||
* 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 <https://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
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::<String>();
|
|
||||||
let ident = identifier_to_c(&structure.identifier);
|
|
||||||
let contents = structure
|
|
||||||
.contents
|
|
||||||
.iter()
|
|
||||||
.map(doc_named_type_to_header)
|
|
||||||
.collect::<String>();
|
|
||||||
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::<String>();
|
|
||||||
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::<String>();
|
|
||||||
let ident = identifier_to_c(&enumeration.identifier);
|
|
||||||
let states = enumeration
|
|
||||||
.states
|
|
||||||
.iter()
|
|
||||||
.map(doc_identifier_to_header)
|
|
||||||
.collect::<String>();
|
|
||||||
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::<String>();
|
|
||||||
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::<String>();
|
|
||||||
let ident = mangle_c_function_identifier(&function.identifier, namespaces);
|
|
||||||
let inputs: Vec<TokenStream2> = 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::<Vec<String>>()
|
|
||||||
.join("\n");
|
|
||||||
let enumerations: String = nasp
|
|
||||||
.enumerations
|
|
||||||
.iter()
|
|
||||||
.map(|r#fn| enumeration_to_header(r#fn))
|
|
||||||
.collect::<Vec<String>>()
|
|
||||||
.join("\n");
|
|
||||||
let functions: String = nasp
|
|
||||||
.functions
|
|
||||||
.iter()
|
|
||||||
.map(|r#fn| function_to_header(r#fn, &nasps))
|
|
||||||
.collect::<Vec<String>>()
|
|
||||||
.join("\n");
|
|
||||||
let namespaces: String = nasp
|
|
||||||
.namespaces
|
|
||||||
.iter()
|
|
||||||
.map(|nasp| namespace_to_header(nasp, &nasps))
|
|
||||||
.collect();
|
|
||||||
|
|
||||||
format! {"{}\n{}\n{}\n{}", enumerations, structures, functions, namespaces}
|
|
||||||
}
|
|
|
@ -1,85 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (C) 2023 - 2024:
|
|
||||||
* The Trinitrix Project <soispha@vhack.eu, antifallobst@systemausfall.org>
|
|
||||||
*
|
|
||||||
* 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 <https://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
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 ,
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,114 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (C) 2023 - 2024:
|
|
||||||
* The Trinitrix Project <soispha@vhack.eu, antifallobst@systemausfall.org>
|
|
||||||
*
|
|
||||||
* 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 <https://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
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::<Vec<String>>()
|
|
||||||
.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<TokenStream2> = 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::<String>();
|
|
||||||
|
|
||||||
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::<Vec<String>>()
|
|
||||||
.join("\n");
|
|
||||||
|
|
||||||
let namespace = quote! {
|
|
||||||
struct #ident {
|
|
||||||
#functions
|
|
||||||
#namespaces
|
|
||||||
};
|
|
||||||
};
|
|
||||||
format! {"{}\n{}{}\n", next_namespace, doc_comments, namespace}
|
|
||||||
}
|
|
|
@ -1,375 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (C) 2023 - 2024:
|
|
||||||
* The Trinitrix Project <soispha@vhack.eu, antifallobst@systemausfall.org>
|
|
||||||
*
|
|
||||||
* 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 <https://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
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<TokenStream2> = 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<TokenStream2> = 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<TokenStream2> = 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 [= <input>]
|
|
||||||
/// ->
|
|
||||||
/// one::One::Two(<input>) [= <input>]
|
|
||||||
/// ->
|
|
||||||
/// Commands::One(<input>) [= <input>]
|
|
||||||
/// ```
|
|
||||||
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 )
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,226 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (C) 2023 - 2024:
|
|
||||||
* The Trinitrix Project <soispha@vhack.eu, antifallobst@systemausfall.org>
|
|
||||||
*
|
|
||||||
* 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 <https://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
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! {
|
|
||||||
// <Result<TrainedDog, TrainingMistake> as Convertible>::Ptr,
|
|
||||||
<Result<#ident_ok, #ident_err> 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<TokenStream2> = 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<Identifier>) -> Vec<Identifier> {
|
|
||||||
vec.into_iter()
|
|
||||||
.map(|ident| {
|
|
||||||
if "<root>" == &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[..])
|
|
||||||
}
|
|
|
@ -1,148 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (C) 2023 - 2024:
|
|
||||||
* The Trinitrix Project <soispha@vhack.eu, antifallobst@systemausfall.org>
|
|
||||||
*
|
|
||||||
* 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 <https://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
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<Self, trixy::types::error::TypeConversionError> {
|
|
||||||
todo!()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// This function generates the `TryFrom<paired_type>` 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<Self, Self::Error> {
|
|
||||||
Ok(Self {
|
|
||||||
#try_into_fields
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// This function generates the `From<paired_type>` 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<paired_type>` 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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue