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"
|
||||
|
||||
[dependencies]
|
||||
trixy-parser = { path = "./trixy-parser" }
|
||||
trixy-macros = { path = "./trixy-macros" }
|
||||
trixy-types = { path = "./trixy-types" }
|
||||
thiserror = "1.0.57"
|
||||
clap = { version = "4.5.4", features = ["derive"], optional = true }
|
||||
convert_case = {version = "0.6.0", optional = true}
|
||||
proc-macro2 = {version = "1.0.79", optional = true}
|
||||
quote = {version = "1.0.35", optional = true}
|
||||
syn = { version = "2.0.55", features = ["extra-traits", "full", "parsing"], optional = true }
|
||||
thiserror = { version = "1.0.58", optional = true}
|
||||
|
||||
# macros
|
||||
prettyplease = {version = "0.2.17", optional = true}
|
||||
|
||||
# parser
|
||||
regex = {version = "1.10.4", optional = true}
|
||||
|
||||
# types
|
||||
libc ={ version = "0.2.153", optional = true}
|
||||
log = { version = "0.4.21", optional = true}
|
||||
|
||||
[dev-dependencies]
|
||||
# parser
|
||||
pretty_assertions = "1.4.0"
|
||||
|
||||
[features]
|
||||
default = ["parser", "types", "macros"]
|
||||
|
||||
parser = [ "regex", "thiserror", "convert_case" ]
|
||||
types = [ "parser", "libc", "log", "proc-macro2", "quote", "syn", "thiserror", "convert_case" ]
|
||||
macros = [ "parser", "types", "prettyplease", "proc-macro2", "quote", "syn", "convert_case" ]
|
||||
|
|
|
@ -18,7 +18,7 @@
|
|||
# and the Lesser GNU General Public License along with this program.
|
||||
# If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
ebnf2pdf make "./docs/grammar.ebnf"
|
||||
mv grammar.ebnf.pdf ./docs/grammar.pdf
|
||||
ebnf2pdf make "./grammar.ebnf"
|
||||
mv grammar.ebnf.pdf ./grammar.pdf
|
||||
|
||||
# vim: ft=sh
|
12
src/lib.rs
12
src/lib.rs
|
@ -19,13 +19,13 @@
|
|||
* If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
pub mod types {
|
||||
pub use trixy_types::*;
|
||||
}
|
||||
#[cfg(feature = "macros")]
|
||||
pub mod macros;
|
||||
#[cfg(feature = "types")]
|
||||
pub mod types;
|
||||
#[cfg(feature = "parser")]
|
||||
pub mod parser;
|
||||
|
||||
pub mod macros {
|
||||
pub use trixy_macros::*;
|
||||
}
|
||||
pub mod __private {
|
||||
//! This module contains crates needed for the generated code, it should not be used by humans.
|
||||
pub mod thiserror {
|
||||
|
|
|
@ -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.
|
||||
//! It works using the popular builder syntax:
|
||||
//! ```no_run
|
||||
//! use trixy_macros::config::{Language, TrixyConfig};
|
||||
//! ```
|
||||
//! use crate::macros::config::{Language, TrixyConfig};
|
||||
//!# fn main() {
|
||||
//! let config = TrixyConfig::new()
|
||||
//! .set_input_path("path/to/trixy/api.tri")
|
||||
//! .set_output_path("api.rs")
|
||||
//! .set_languages(vec![Language::Rust, Language::C])
|
||||
//! .set_generate_debug(false);
|
||||
//! .trixy_path("path/to/trixy/api.tri")
|
||||
//! .output_path("api.rs")
|
||||
//! .languages(vec![Language::Rust, Language::C])
|
||||
//!# }
|
||||
//! ```
|
||||
|
||||
use std::path::PathBuf;
|
||||
use std::{env, path::PathBuf};
|
||||
|
||||
#[derive(Debug)]
|
||||
#[derive(Debug, Default)]
|
||||
pub enum Language {
|
||||
Rust,
|
||||
C,
|
||||
Lua,
|
||||
#[default]
|
||||
All,
|
||||
}
|
||||
|
||||
#[derive(Default, Debug)]
|
||||
pub struct TrixyConfig {
|
||||
/// The Path to the base command interface config file
|
||||
/// The Path to the base command interface config file.
|
||||
pub trixy_path: PathBuf,
|
||||
|
||||
/// The name of the outputted host code (rust and c bindings)
|
||||
/// This file is written in $OUT_DIR
|
||||
pub host_code_name: PathBuf,
|
||||
/// The name of the c header
|
||||
/// This file is written in $OUT_DIR
|
||||
pub c_header_name: PathBuf,
|
||||
/// The name of the outputted host code (rust and c bindings).
|
||||
/// This file is written in $OUT_DIR>
|
||||
pub host_code_name: String,
|
||||
/// The name of the c header.
|
||||
/// This file is written in $OUT_DIR.
|
||||
pub c_header_name: String,
|
||||
|
||||
/// The path from the root to the distribution directory.
|
||||
/// Things like the c headers are copied in this dir
|
||||
/// When this is [None] no dist dir will be generated
|
||||
/// Things like the c headers are copied in this dir.
|
||||
/// When this is [None] no dist dir will be generated.
|
||||
pub dist_dir_path: Option<PathBuf>,
|
||||
|
||||
/// Whether to check if the dist dir is empty before writing something to it
|
||||
pub check_dist_dir: bool,
|
||||
|
||||
/// Should the macro generate Debug trait implementation for each enum
|
||||
/// These are very useful but completely obscure the `cargo expand` output
|
||||
pub generate_debug: bool,
|
||||
|
||||
// /// Whether to check if the dist dir is empty before writing something to it.
|
||||
// pub check_dist_dir: bool,
|
||||
/// This function is executed whenever an API function is called.
|
||||
/// The only argument is the command (encoded as the `Commands`) enum
|
||||
/// which is represented by this function
|
||||
/// Because of rust limitations this is supposed to be the name of a macro acting as said
|
||||
/// function. This macro must be in scope of the generated code
|
||||
/// which is represented by this function.
|
||||
pub callback_function: String,
|
||||
|
||||
/// This path is used to place the outputted host code files.
|
||||
/// Normally this would be the `$OUT_DIR` environment variable set by cargo at build time.
|
||||
/// You have to set this, if you want to use trixy in an other context.
|
||||
pub out_dir: PathBuf,
|
||||
}
|
||||
|
||||
impl TrixyConfig {
|
||||
pub fn new<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 {
|
||||
callback_function: callback_function.into(),
|
||||
host_code_name: "api.rs".into(),
|
||||
c_header_name: "interface.h".into(),
|
||||
out_dir,
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
|
@ -90,13 +98,6 @@ impl TrixyConfig {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn generate_debug(self, generate_debug: bool) -> Self {
|
||||
Self {
|
||||
generate_debug,
|
||||
..self
|
||||
}
|
||||
}
|
||||
|
||||
pub fn dist_dir_path<T: Into<PathBuf>>(self, dist_dir_path: T) -> Self {
|
||||
Self {
|
||||
dist_dir_path: Some(dist_dir_path.into()),
|
||||
|
@ -104,21 +105,21 @@ impl TrixyConfig {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn check_dist_dir(self, check_dist_dir: bool) -> Self {
|
||||
Self {
|
||||
check_dist_dir,
|
||||
..self
|
||||
}
|
||||
}
|
||||
|
||||
pub fn host_code_name<T: Into<PathBuf>>(self, output_path: T) -> Self {
|
||||
pub fn host_code_name<T: Into<String>>(self, output_path: T) -> Self {
|
||||
Self {
|
||||
host_code_name: output_path.into(),
|
||||
..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 {
|
||||
c_header_name: output_path.into(),
|
||||
..self
|
|
@ -23,14 +23,8 @@
|
|||
//! It works by firstly listing the functions and then by grouping them into structures, effectively
|
||||
//! simulating namespaces in c.
|
||||
|
||||
use proc_macro2::TokenStream as TokenStream2;
|
||||
use quote::quote;
|
||||
use trixy_parser::command_spec::{Attribute, CommandSpec, NamedType};
|
||||
use trixy_types::header_names;
|
||||
|
||||
use crate::{
|
||||
config::TrixyConfig,
|
||||
generate::{c_api::type_to_c, identifier_to_rust},
|
||||
macros::config::trixy::TrixyConfig, parser::command_spec::CommandSpec, types::header_names,
|
||||
};
|
||||
|
||||
mod pure_header;
|
||||
|
@ -109,19 +103,3 @@ pub fn generate(trixy: &CommandSpec, _config: &TrixyConfig) -> String {
|
|||
END_HEADER_GUARD
|
||||
)
|
||||
}
|
||||
|
||||
fn attribute_to_doc_comment(attribute: &Attribute) -> String {
|
||||
if let Attribute::doc(doc_comment) = attribute {
|
||||
format!("/** {}\n*/", doc_comment)
|
||||
} else {
|
||||
"".to_owned()
|
||||
}
|
||||
}
|
||||
|
||||
fn named_type_to_c(named_type: &NamedType) -> TokenStream2 {
|
||||
let ident = identifier_to_rust(&named_type.name);
|
||||
let c_type = type_to_c(&named_type.r#type, false);
|
||||
quote! {
|
||||
#c_type #ident
|
||||
}
|
||||
}
|
|
@ -19,14 +19,31 @@
|
|||
* If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
fn print(message: String);
|
||||
use crate::parser::command_spec::{CommandSpec, Enumeration, Structure};
|
||||
|
||||
/// First doc comment
|
||||
// Some more text
|
||||
mod trinitrix {
|
||||
/// Second doc comment
|
||||
fn hi(name: String) -> String;
|
||||
pub fn generate(trixy: &CommandSpec) -> String {
|
||||
let functions: String = trixy
|
||||
.functions
|
||||
.iter()
|
||||
.map(|r#fn| r#fn.to_auxiliary_c(&[]))
|
||||
.collect();
|
||||
let namespaces: String = trixy
|
||||
.namespaces
|
||||
.iter()
|
||||
.map(|nasp| nasp.to_auxiliary_c(&vec![]))
|
||||
.collect();
|
||||
let structures: String = trixy
|
||||
.structures
|
||||
.iter()
|
||||
.map(Structure::to_auxiliary_c)
|
||||
.collect();
|
||||
let enumerations: String = trixy
|
||||
.enumerations
|
||||
.iter()
|
||||
.map(Enumeration::to_auxiliary_c)
|
||||
.collect();
|
||||
format!(
|
||||
"{}\n{}\n{}\n{}",
|
||||
enumerations, structures, functions, namespaces
|
||||
)
|
||||
}
|
||||
|
||||
// That's a flat out lie, but it results in a rather nice syntax highlight compared to nothing:
|
||||
// vim: syntax=rust
|
|
@ -19,15 +19,15 @@
|
|||
* If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
fn print(message: CommandTransferValue);
|
||||
use proc_macro2::TokenStream as TokenStream2;
|
||||
|
||||
mod trinitrix {
|
||||
fn hi(name: String) -> String;
|
||||
use crate::parser::command_spec::CommandSpec;
|
||||
|
||||
pub fn generate(trixy: &CommandSpec) -> String {
|
||||
let struct_initializer: TokenStream2 = trixy
|
||||
.namespaces
|
||||
.iter()
|
||||
.map(|nasp| nasp.to_auxiliary_c_full_struct_init(&vec![]))
|
||||
.collect();
|
||||
struct_initializer.to_string()
|
||||
}
|
||||
|
||||
mod trinitrix {
|
||||
fn ho(name: String, name2: String) -> String;
|
||||
}
|
||||
|
||||
// That's a flat out lie, but it results in a rather nice syntax highlight compared to nothing:
|
||||
// vim: syntax=rust
|
|
@ -19,12 +19,15 @@
|
|||
* If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
struct Callback {
|
||||
func: Function,
|
||||
timeout: Integer,
|
||||
use crate::parser::command_spec::{CommandSpec, Namespace};
|
||||
|
||||
pub fn generate(trixy: &CommandSpec) -> String {
|
||||
let type_defs: String = trixy
|
||||
.namespaces
|
||||
.iter()
|
||||
.rev()
|
||||
.map(Namespace::to_auxiliary_c_full_typedef)
|
||||
.collect::<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/>.
|
||||
*/
|
||||
|
||||
/// other doc comment
|
||||
fn hi(name: String) -> String;
|
||||
use crate::{
|
||||
macros::{config::trixy::TrixyConfig, generate::host::format_rust},
|
||||
parser::command_spec::CommandSpec,
|
||||
};
|
||||
|
||||
/// struct comment
|
||||
struct ho {
|
||||
ident: String,
|
||||
/// also a doc comment
|
||||
codebase: Vec<String>,
|
||||
pub mod host;
|
||||
|
||||
pub fn generate(trixy: &CommandSpec, config: &TrixyConfig) -> String {
|
||||
let host_rust_code = host::generate(&trixy, &config);
|
||||
|
||||
let rust_code = format_rust(host_rust_code);
|
||||
|
||||
format!(
|
||||
"
|
||||
/* c API */
|
||||
{}
|
||||
",
|
||||
rust_code
|
||||
)
|
||||
}
|
||||
|
||||
/// Some doc comment
|
||||
mod trinitrix {
|
||||
fn hi(name: String) -> String;
|
||||
}
|
||||
|
||||
// That's a flat out lie, but it results in a rather nice syntax highlight compared to nothing:
|
||||
// vim: syntax=rust
|
|
@ -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/>.
|
||||
*/
|
||||
|
||||
mod trinitrix {
|
||||
struct Callback {
|
||||
func: Function,
|
||||
timeout: Integer,
|
||||
}
|
||||
use crate::parser::command_spec::CommandSpec;
|
||||
|
||||
enum CallbackPriority {
|
||||
High,
|
||||
Medium,
|
||||
Low,
|
||||
}
|
||||
use crate::macros::{config::trixy::TrixyConfig, generate::host::format_rust};
|
||||
|
||||
fn execute_callback(callback: Callback, priority: CallbackPriority);
|
||||
pub mod host;
|
||||
|
||||
pub fn generate(trixy: &CommandSpec, config: &TrixyConfig) -> String {
|
||||
let host_rust_code = host::generate(&trixy, &config);
|
||||
|
||||
let rust_code = format_rust(host_rust_code);
|
||||
|
||||
format!(
|
||||
"
|
||||
/* Rust API */
|
||||
{}
|
||||
",
|
||||
rust_code
|
||||
)
|
||||
}
|
||||
|
||||
// That's a flat out lie, but it results in a rather nice syntax highlight compared to nothing:
|
||||
// vim: syntax=rust
|
|
@ -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 crate::lexing::TokenKind;
|
||||
use crate::parser::lexing::TokenKind;
|
||||
|
||||
use super::unchecked::{self, DeriveValue};
|
||||
|
|
@ -25,9 +25,9 @@
|
|||
|
||||
use std::fmt::{Display, Write};
|
||||
|
||||
use crate::lexing::{Token, TokenKind, TokenSpan};
|
||||
use crate::parser::lexing::{Token, TokenKind, TokenSpan};
|
||||
|
||||
#[derive(Debug, Default, PartialEq, Eq, PartialOrd, Ord)]
|
||||
#[derive(Debug, Default, PartialEq, Eq, PartialOrd, Ord, Clone)]
|
||||
pub struct CommandSpec {
|
||||
pub structures: Vec<Structure>,
|
||||
pub enumerations: Vec<Enumeration>,
|
||||
|
@ -189,7 +189,7 @@ pub enum Type {
|
|||
impl Display for NamedType {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
let ident = match self.name.kind() {
|
||||
crate::lexing::TokenKind::Identifier(ident) => ident,
|
||||
crate::parser::lexing::TokenKind::Identifier(ident) => ident,
|
||||
_ => panic!("Tried to display a non identifier token in the Type display implementation. This is a bug"),
|
||||
};
|
||||
f.write_str(ident)?;
|
||||
|
@ -206,7 +206,7 @@ impl Display for Type {
|
|||
generic_args,
|
||||
} => {
|
||||
let ident = match identifier.kind() {
|
||||
crate::lexing::TokenKind::Identifier(ident) => ident,
|
||||
crate::parser::lexing::TokenKind::Identifier(ident) => ident,
|
||||
_ => panic!("Tried to display a non identifier token in the Type display implementation. This is a bug"),
|
||||
};
|
||||
|
|
@ -23,9 +23,9 @@ use core::fmt;
|
|||
|
||||
use thiserror::Error;
|
||||
|
||||
use crate::{
|
||||
use crate::parser::{
|
||||
lexing::{error::SpannedLexingError, TokenSpan},
|
||||
parsing::{self},
|
||||
parsing,
|
||||
};
|
||||
|
||||
#[derive(Error, Debug)]
|
|
@ -22,7 +22,7 @@
|
|||
use std::{error::Error, fmt::Display};
|
||||
use thiserror::Error;
|
||||
|
||||
use crate::error::{AdditionalHelp, ErrorContext, ErrorContextDisplay};
|
||||
use crate::parser::error::{AdditionalHelp, ErrorContext, ErrorContextDisplay};
|
||||
|
||||
#[derive(Error, Debug)]
|
||||
pub enum LexingError {
|
||||
|
@ -67,7 +67,7 @@ impl Error for SpannedLexingError {
|
|||
impl ErrorContextDisplay for SpannedLexingError {
|
||||
type Error = LexingError;
|
||||
|
||||
fn context(&self) -> &crate::error::ErrorContext {
|
||||
fn context(&self) -> &crate::parser::error::ErrorContext {
|
||||
&self.context
|
||||
}
|
||||
|
|
@ -31,7 +31,7 @@ mod tokenizer;
|
|||
#[cfg(test)]
|
||||
mod test;
|
||||
|
||||
#[derive(Debug, PartialEq, PartialOrd, Ord, Eq)]
|
||||
#[derive(Debug, PartialEq, PartialOrd, Ord, Eq, Clone)]
|
||||
pub struct TokenStream {
|
||||
pub original_file: String,
|
||||
tokens: Vec<Token>,
|
||||
|
@ -308,7 +308,7 @@ impl Display for AttributeKeyword {
|
|||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use trixy_parser::token;
|
||||
/// use crate::parser::token;
|
||||
///# fn main() {
|
||||
/// token![mod];
|
||||
/// token![;];
|
||||
|
@ -317,56 +317,56 @@ impl Display for AttributeKeyword {
|
|||
/// ```
|
||||
#[macro_export]
|
||||
macro_rules! token {
|
||||
[Semicolon] => { $crate::lexing::TokenKind::Semicolon };
|
||||
[;] => { $crate::lexing::TokenKind::Semicolon };
|
||||
[Colon] => { $crate::lexing::TokenKind::Colon };
|
||||
[:] => { $crate::lexing::TokenKind::Colon };
|
||||
[Comma] => { $crate::lexing::TokenKind::Comma };
|
||||
[,] => { $crate::lexing::TokenKind::Comma };
|
||||
[Arrow] => { $crate::lexing::TokenKind::Arrow };
|
||||
[->] => { $crate::lexing::TokenKind::Arrow };
|
||||
[PoundSign] => { $crate::lexing::TokenKind::PoundSign };
|
||||
[#] => { $crate::lexing::TokenKind::PoundSign };
|
||||
[EqualsSign] => { $crate::lexing::TokenKind::EqualsSign };
|
||||
[=] => { $crate::lexing::TokenKind::EqualsSign };
|
||||
[Semicolon] => { $crate::parser::lexing::TokenKind::Semicolon };
|
||||
[;] => { $crate::parser::lexing::TokenKind::Semicolon };
|
||||
[Colon] => { $crate::parser::lexing::TokenKind::Colon };
|
||||
[:] => { $crate::parser::lexing::TokenKind::Colon };
|
||||
[Comma] => { $crate::parser::lexing::TokenKind::Comma };
|
||||
[,] => { $crate::parser::lexing::TokenKind::Comma };
|
||||
[Arrow] => { $crate::parser::lexing::TokenKind::Arrow };
|
||||
[->] => { $crate::parser::lexing::TokenKind::Arrow };
|
||||
[PoundSign] => { $crate::parser::lexing::TokenKind::PoundSign };
|
||||
[#] => { $crate::parser::lexing::TokenKind::PoundSign };
|
||||
[EqualsSign] => { $crate::parser::lexing::TokenKind::EqualsSign };
|
||||
[=] => { $crate::parser::lexing::TokenKind::EqualsSign };
|
||||
|
||||
|
||||
[AngledBracketOpen] => { $crate::lexing::TokenKind::AngledBracketOpen };
|
||||
[<] => { $crate::lexing::TokenKind::AngledBracketOpen };
|
||||
[AngledBracketOpen] => { $crate::parser::lexing::TokenKind::AngledBracketOpen };
|
||||
[<] => { $crate::parser::lexing::TokenKind::AngledBracketOpen };
|
||||
|
||||
[AngledBracketClose] => { $crate::lexing::TokenKind::AngledBracketClose };
|
||||
[>] => { $crate::lexing::TokenKind::AngledBracketClose };
|
||||
[AngledBracketClose] => { $crate::parser::lexing::TokenKind::AngledBracketClose };
|
||||
[>] => { $crate::parser::lexing::TokenKind::AngledBracketClose };
|
||||
|
||||
[CurlyBracketOpen] => { $crate::lexing::TokenKind::CurlyBracketOpen};
|
||||
// [{] => { $crate::lexing::TokenKind::CurlyBracketOpen };
|
||||
[CurlyBracketClose] => { $crate::lexing::TokenKind::CurlyBracketClose};
|
||||
// [}] => { $crate::lexing::TokenKind::CurlyBracketClose };
|
||||
[CurvedBracketOpen] => { $crate::lexing::TokenKind::CurvedBracketOpen};
|
||||
// [(] => { $crate::lexing::TokenKind::ParenthesisOpen };
|
||||
[CurvedBracketClose] => { $crate::lexing::TokenKind::CurvedBracketClose};
|
||||
// [)] => { $crate::lexing::TokenKind::ParenthesisClose };
|
||||
[SquareBracketOpen] => { $crate::lexing::TokenKind::SquareBracketOpen};
|
||||
// [[] => { $crate::lexing::TokenKind::ParenthesisOpen };
|
||||
[SquareBracketClose] => { $crate::lexing::TokenKind::SquareBracketClose};
|
||||
// []] => { $crate::lexing::TokenKind::ParenthesisClose };
|
||||
[CurlyBracketOpen] => { $crate::parser::lexing::TokenKind::CurlyBracketOpen};
|
||||
// [{] => { $crate::parser::lexing::TokenKind::CurlyBracketOpen };
|
||||
[CurlyBracketClose] => { $crate::parser::lexing::TokenKind::CurlyBracketClose};
|
||||
// [}] => { $crate::parser::lexing::TokenKind::CurlyBracketClose };
|
||||
[CurvedBracketOpen] => { $crate::parser::lexing::TokenKind::CurvedBracketOpen};
|
||||
// [(] => { $crate::parser::lexing::TokenKind::ParenthesisOpen };
|
||||
[CurvedBracketClose] => { $crate::parser::lexing::TokenKind::CurvedBracketClose};
|
||||
// [)] => { $crate::parser::lexing::TokenKind::ParenthesisClose };
|
||||
[SquareBracketOpen] => { $crate::parser::lexing::TokenKind::SquareBracketOpen};
|
||||
// [[] => { $crate::parser::lexing::TokenKind::ParenthesisOpen };
|
||||
[SquareBracketClose] => { $crate::parser::lexing::TokenKind::SquareBracketClose};
|
||||
// []] => { $crate::parser::lexing::TokenKind::ParenthesisClose };
|
||||
|
||||
|
||||
[mod] => { $crate::lexing::TokenKind::Keyword($crate::lexing::Keyword::r#mod) };
|
||||
[fn] => { $crate::lexing::TokenKind::Keyword($crate::lexing::Keyword::r#fn) };
|
||||
[struct] => { $crate::lexing::TokenKind::Keyword($crate::lexing::Keyword::r#struct) };
|
||||
[enum] => { $crate::lexing::TokenKind::Keyword($crate::lexing::Keyword::r#enum) };
|
||||
[mod] => { $crate::parser::lexing::TokenKind::Keyword($crate::parser::lexing::Keyword::r#mod) };
|
||||
[fn] => { $crate::parser::lexing::TokenKind::Keyword($crate::parser::lexing::Keyword::r#fn) };
|
||||
[struct] => { $crate::parser::lexing::TokenKind::Keyword($crate::parser::lexing::Keyword::r#struct) };
|
||||
[enum] => { $crate::parser::lexing::TokenKind::Keyword($crate::parser::lexing::Keyword::r#enum) };
|
||||
|
||||
// The `derive` here is completely arbitrary. It is only for comparisons (see `same_kind`)
|
||||
[AttributeKeyword] => { $crate::lexing::TokenKind::AttributeKeyword($crate::lexing::AttributeKeyword::derive) };
|
||||
[AttributeKeyword] => { $crate::parser::lexing::TokenKind::AttributeKeyword($crate::parser::lexing::AttributeKeyword::derive) };
|
||||
|
||||
// This is only works for checking for a identifier or comment
|
||||
// see the `same_kind` method on TokenKind
|
||||
[Ident] => { $crate::lexing::TokenKind::Identifier("".to_owned()) };
|
||||
[Identifier] => { $crate::lexing::TokenKind::Identifier("".to_owned()) };
|
||||
[Ident] => { $crate::parser::lexing::TokenKind::Identifier("".to_owned()) };
|
||||
[Identifier] => { $crate::parser::lexing::TokenKind::Identifier("".to_owned()) };
|
||||
|
||||
[StringLiteral] => { $crate::lexing::TokenKind::StringLiteral("".to_owned()) };
|
||||
[StringLiteral] => { $crate::parser::lexing::TokenKind::StringLiteral("".to_owned()) };
|
||||
|
||||
[Comment] => { $crate::lexing::TokenKind::Comment("".to_owned()) };
|
||||
[Comment] => { $crate::parser::lexing::TokenKind::Comment("".to_owned()) };
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
@ -397,6 +397,6 @@ mod tests {
|
|||
|
||||
token_macro_test!(tok_expands_to_arrow, ->, => TokenKind::Arrow);
|
||||
token_macro_test!(tok_expands_to_semicolon, Semicolon, => TokenKind::Semicolon);
|
||||
token_macro_test!(tok_expands_to_mod, mod, => TokenKind::Keyword(crate::lexing::Keyword::r#mod));
|
||||
token_macro_test!(tok_expands_to_fn, fn, => TokenKind::Keyword(crate::lexing::Keyword::r#fn));
|
||||
token_macro_test!(tok_expands_to_mod, mod, => TokenKind::Keyword(crate::parser::lexing::Keyword::r#mod));
|
||||
token_macro_test!(tok_expands_to_fn, fn, => TokenKind::Keyword(crate::parser::lexing::Keyword::r#fn));
|
||||
}
|
|
@ -21,7 +21,7 @@
|
|||
|
||||
// This code is heavily inspired by: https://michael-f-bryan.github.io/static-analyser-in-rust/book/lex.html
|
||||
|
||||
use crate::{
|
||||
use crate::parser::{
|
||||
error::ErrorContext,
|
||||
lexing::{Keyword, TokenSpan},
|
||||
};
|
|
@ -21,7 +21,7 @@
|
|||
|
||||
use error::TrixyError;
|
||||
|
||||
use crate::lexing::TokenStream;
|
||||
use crate::parser::lexing::TokenStream;
|
||||
|
||||
use self::command_spec::checked::CommandSpec;
|
||||
|
|
@ -23,7 +23,7 @@ use thiserror::Error;
|
|||
|
||||
use std::{error::Error, fmt::Display};
|
||||
|
||||
use crate::{
|
||||
use crate::parser::{
|
||||
command_spec::{checked::Identifier, unchecked::Attribute},
|
||||
error::{AdditionalHelp, ErrorContext, ErrorContextDisplay},
|
||||
lexing::TokenSpan,
|
||||
|
@ -133,7 +133,7 @@ impl Display for SpannedParsingError {
|
|||
impl ErrorContextDisplay for SpannedParsingError {
|
||||
type Error = ParsingError;
|
||||
|
||||
fn context(&self) -> &crate::error::ErrorContext {
|
||||
fn context(&self) -> &crate::parser::error::ErrorContext {
|
||||
&self.context
|
||||
}
|
||||
|
|
@ -21,10 +21,10 @@
|
|||
|
||||
use std::{iter, mem};
|
||||
|
||||
use crate::types::BASE_TYPES;
|
||||
use convert_case::{Case, Casing};
|
||||
use trixy_types::BASE_TYPES;
|
||||
|
||||
use crate::{
|
||||
use crate::parser::{
|
||||
command_spec::{
|
||||
checked::{
|
||||
self, CommandSpec, DocIdentifier, DocNamedType, Enumeration, Function, Identifier,
|
||||
|
@ -68,13 +68,13 @@ macro_rules! take_attrs {
|
|||
}
|
||||
};
|
||||
(@process_val $iden:ident, $val:ident) => {
|
||||
if let $crate::command_spec::unchecked::Attribute::$val{..} = $iden {
|
||||
if let $crate::parser::command_spec::unchecked::Attribute::$val{..} = $iden {
|
||||
return Ok($iden.into());
|
||||
};
|
||||
take_attrs!{@process_val_last $iden}
|
||||
};
|
||||
(@process_val $iden:ident, $val:ident, $($other:tt),+ $(,)*) => {
|
||||
if let $crate::command_spec::unchecked::Attribute::$val{..} = $iden {
|
||||
if let $crate::parser::command_spec::unchecked::Attribute::$val{..} = $iden {
|
||||
return Ok($iden.into());
|
||||
};
|
||||
take_attrs!{@process_val $iden, $($other),*}
|
|
@ -22,7 +22,7 @@
|
|||
use std::{error::Error, fmt::Display};
|
||||
use thiserror::Error;
|
||||
|
||||
use crate::{
|
||||
use crate::parser::{
|
||||
command_spec::unchecked::{Attribute, StringLiteral},
|
||||
error::{AdditionalHelp, ErrorContext, ErrorContextDisplay},
|
||||
lexing::{TokenKind, TokenSpan},
|
||||
|
@ -113,7 +113,7 @@ impl Display for SpannedParsingError {
|
|||
impl ErrorContextDisplay for SpannedParsingError {
|
||||
type Error = ParsingError;
|
||||
|
||||
fn context(&self) -> &crate::error::ErrorContext {
|
||||
fn context(&self) -> &crate::parser::error::ErrorContext {
|
||||
&self.context
|
||||
}
|
||||
|
|
@ -22,12 +22,14 @@
|
|||
use std::{iter::once, mem};
|
||||
|
||||
use crate::{
|
||||
parser::{
|
||||
command_spec::unchecked::{
|
||||
Attribute, CommandSpec, Declaration, DeriveValue, DocNamedType, DocToken, Enumeration,
|
||||
Function, NamedType, Namespace, StringLiteral, Structure, Type,
|
||||
},
|
||||
error::ErrorContext,
|
||||
lexing::{AttributeKeyword, Token, TokenKind, TokenSpan, TokenStream},
|
||||
},
|
||||
token,
|
||||
};
|
||||
|
|
@ -19,7 +19,7 @@
|
|||
* If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
use crate::error::{self, TypeConversionError};
|
||||
use crate::types::error::{self, TypeConversionError};
|
||||
use log::warn;
|
||||
use std::{
|
||||
error::Error,
|
||||
|
@ -180,7 +180,7 @@ impl<T: Convertible, E: Error> Convertible for Result<T, E> {
|
|||
}
|
||||
|
||||
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 {
|
||||
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 {
|
||||
($value:ident, $name:ident) => {
|
||||
#[no_mangle]
|
||||
pub extern "C" fn $name(ptr: crate::Vec<<$value as Convertible>::Ptr>) {
|
||||
pub extern "C" fn $name(ptr: crate::types::Vec<<$value as Convertible>::Ptr>) {
|
||||
Vec::<$value>::drop_ptr(ptr);
|
||||
}
|
||||
};
|
|
@ -29,7 +29,7 @@ use std::{
|
|||
|
||||
use log::{error, warn};
|
||||
|
||||
use crate::error::TypeConversionError;
|
||||
use crate::types::error::TypeConversionError;
|
||||
|
||||
// This code is heavily inspired by: https://michael-f-bryan.github.io/rust-ffi-guide/errors/return_types.html
|
||||
thread_local! {
|
|
@ -22,4 +22,4 @@
|
|||
pub mod convert_trait;
|
||||
pub mod errno;
|
||||
mod try_from_impl;
|
||||
pub use try_from_impl::*;
|
||||
// pub use try_from_impl::*;
|
|
@ -19,7 +19,7 @@
|
|||
* If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
use crate::String;
|
||||
use crate::types::String;
|
||||
use std::ffi::CString;
|
||||
|
||||
use super::convert_trait::Convertible;
|
||||
|
@ -34,7 +34,7 @@ impl From<std::string::String> for 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> {
|
||||
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