feat(trixy-macros): Add rust host code generation
This commit is contained in:
parent
1d49bdf2cf
commit
ed96a50bd4
|
@ -30,3 +30,5 @@ convert_case = "0.6.0"
|
||||||
proc-macro2 = "1.0.70"
|
proc-macro2 = "1.0.70"
|
||||||
quote = "1.0.33"
|
quote = "1.0.33"
|
||||||
syn = { version = "2.0.41", features = ["extra-traits", "full", "parsing"] }
|
syn = { version = "2.0.41", features = ["extra-traits", "full", "parsing"] }
|
||||||
|
trixy-lang_parser = { path = "./trixy-lang_parser" }
|
||||||
|
trixy-macros = { path = "./trixy-macros" }
|
||||||
|
|
118
src/lib.rs
118
src/lib.rs
|
@ -1,118 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (C) 2023 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 command_enum_parsing::DataCommandEnum;
|
|
||||||
use config::TrixyConfig;
|
|
||||||
use proc_macro::TokenStream;
|
|
||||||
use proc_macro2::TokenStream as TokenStream2;
|
|
||||||
use quote::quote;
|
|
||||||
use syn::parse_macro_input;
|
|
||||||
|
|
||||||
use crate::trixy_lang::parse_trixy_lang;
|
|
||||||
|
|
||||||
mod command_enum_parsing;
|
|
||||||
mod config;
|
|
||||||
mod generate;
|
|
||||||
mod trixy_lang;
|
|
||||||
|
|
||||||
/// This is the heart of the command api
|
|
||||||
/// It mainly does two things:
|
|
||||||
/// - Generate a command enum
|
|
||||||
/// - Wrap the enum in all supported languages (only lua for now)
|
|
||||||
/// - Generate wrapper lua function for each command
|
|
||||||
/// - Generate a `add_lua_functions_to_globals` function, which adds
|
|
||||||
/// the rust wrapper functions to the lua globals.
|
|
||||||
///
|
|
||||||
/// The input and output values of the wrapped functions are derived from the values specified in
|
|
||||||
/// the input to the `parse_command_enum` proc macro.
|
|
||||||
///
|
|
||||||
/// For example this rust code:
|
|
||||||
/// ```no_run
|
|
||||||
/// parse_command_enum! {
|
|
||||||
/// /// Greets the user
|
|
||||||
/// greet: fn(String) -> String,
|
|
||||||
/// }
|
|
||||||
/// ```
|
|
||||||
/// results in this expanded code:
|
|
||||||
/// ```no_run
|
|
||||||
/// #[derive(Debug)]
|
|
||||||
/// pub enum Command {
|
|
||||||
/// Greet(String),
|
|
||||||
/// }
|
|
||||||
/// pub fn add_lua_functions_to_globals(
|
|
||||||
/// lua: mlua::Lua,
|
|
||||||
/// tx: tokio::sync::mpsc::Sender<Event>,
|
|
||||||
/// ) -> mlua::Lua {
|
|
||||||
/// lua.set_app_data(tx);
|
|
||||||
/// let globals = lua.globals();
|
|
||||||
/// {
|
|
||||||
/// let wrapped_lua_function_greet = lua
|
|
||||||
/// .create_async_function(greet)
|
|
||||||
/// .expect(
|
|
||||||
/// format!(
|
|
||||||
/// "The function: `{}` should be defined",
|
|
||||||
/// "greet",
|
|
||||||
/// )
|
|
||||||
/// );
|
|
||||||
/// globals
|
|
||||||
/// .set("greet", wrapped_lua_function_greet)
|
|
||||||
/// .expect("Setting a static global value should work");
|
|
||||||
/// }
|
|
||||||
/// drop(globals);
|
|
||||||
/// lua
|
|
||||||
/// }
|
|
||||||
/// async fn greet(lua: &mlua::Lua, input: String) -> Result<String, mlua::Error> {
|
|
||||||
/// let (callback_tx, callback_rx) = tokio::sync::oneshot::channel::<String>();
|
|
||||||
/// let tx: core::cell::Ref<tokio::sync::mpsc::Sender<Event>> = lua
|
|
||||||
/// .app_data_ref()
|
|
||||||
/// .expect("This should exist, it was set before");
|
|
||||||
/// (*tx)
|
|
||||||
/// .send(Event::CommandEvent(Command::Greet(input.clone()), Some(callback_tx)))
|
|
||||||
/// .await
|
|
||||||
/// .expect("This should work, as the receiver is not dropped");
|
|
||||||
/// match callback_rx.await {
|
|
||||||
/// Ok(output) => {
|
|
||||||
/// return Ok(output);
|
|
||||||
/// }
|
|
||||||
/// Err(err) => {
|
|
||||||
/// return Err(mlua::Error::ExternalError(std::sync::Arc::new(err)));
|
|
||||||
/// }
|
|
||||||
/// };
|
|
||||||
/// }
|
|
||||||
/// ```
|
|
||||||
#[proc_macro]
|
|
||||||
pub fn trixy_generate(input: TokenStream) -> TokenStream {
|
|
||||||
let input = parse_macro_input!(input as TrixyConfig);
|
|
||||||
|
|
||||||
let trixy_code = parse_trixy_lang(input.get_path());
|
|
||||||
todo!()
|
|
||||||
// // Build the language wrappers
|
|
||||||
// let lua_wrapper: TokenStream2 = generate::lua_wrapper(&input);
|
|
||||||
//
|
|
||||||
// // Build the final enum
|
|
||||||
// let command_enum = generate::command_enum(&input);
|
|
||||||
|
|
||||||
// let output = quote! {
|
|
||||||
// #command_enum
|
|
||||||
// #lua_wrapper
|
|
||||||
// };
|
|
||||||
// output.into()
|
|
||||||
}
|
|
|
@ -26,5 +26,6 @@ edition = "2021"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
clap = { version = "4.4.11", features = ["derive"] }
|
clap = { version = "4.4.11", features = ["derive"] }
|
||||||
|
convert_case = "0.6.0"
|
||||||
pretty_assertions = "1.4.0"
|
pretty_assertions = "1.4.0"
|
||||||
thiserror = "1.0.50"
|
thiserror = "1.0.50"
|
||||||
|
|
|
@ -0,0 +1,36 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2023 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/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
nasp trinitrix {
|
||||||
|
/// This enum can't be called Trinitrix, as that's already the name of the namespace
|
||||||
|
/// (if it's in Pascal case)
|
||||||
|
enum Trinitrix {
|
||||||
|
High,
|
||||||
|
Medium,
|
||||||
|
Low,
|
||||||
|
};
|
||||||
|
|
||||||
|
fn execute_callback(priority: Trinitrix);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// That's a flat out lie, but it results in a rather nice syntax highlight compared to nothing:
|
||||||
|
// vim: syntax=rust
|
|
@ -0,0 +1,39 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2023 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/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
/// other doc comment
|
||||||
|
fn hi(name: String) -> String;
|
||||||
|
|
||||||
|
/// struct comment
|
||||||
|
struct ho {
|
||||||
|
ident: String,
|
||||||
|
/// also a doc comment
|
||||||
|
codebase: Vec<String>,
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Some doc comment
|
||||||
|
nasp 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
|
|
@ -18,7 +18,6 @@
|
||||||
* If not, see <https://www.gnu.org/licenses/>.
|
* If not, see <https://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
||||||
//! This module contains the already type checked types.
|
//! This module contains the already type checked types.
|
||||||
|
|
||||||
use std::fmt::Display;
|
use std::fmt::Display;
|
||||||
|
@ -91,7 +90,7 @@ pub struct Function {
|
||||||
pub attributes: Vec<Attribute>,
|
pub attributes: Vec<Attribute>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord)]
|
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone)]
|
||||||
pub struct Type {
|
pub struct Type {
|
||||||
pub identifier: Identifier,
|
pub identifier: Identifier,
|
||||||
pub generic_args: Vec<Type>,
|
pub generic_args: Vec<Type>,
|
||||||
|
@ -110,6 +109,12 @@ pub struct DocNamedType {
|
||||||
pub attributes: Vec<Attribute>,
|
pub attributes: Vec<Attribute>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl From<&DocNamedType> for NamedType {
|
||||||
|
fn from(value: &DocNamedType) -> Self {
|
||||||
|
Self { name: value.name.to_owned(), r#type: value.r#type.to_owned() }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl From<TokenKind> for Identifier {
|
impl From<TokenKind> for Identifier {
|
||||||
fn from(value: TokenKind) -> Self {
|
fn from(value: TokenKind) -> Self {
|
||||||
match value {
|
match value {
|
||||||
|
@ -156,6 +161,14 @@ pub struct DocIdentifier {
|
||||||
pub attributes: Vec<Attribute>,
|
pub attributes: Vec<Attribute>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl From<&DocIdentifier> for Identifier {
|
||||||
|
fn from(value: &DocIdentifier) -> Self {
|
||||||
|
Self {
|
||||||
|
name: value.name.to_owned(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// A const version of [Identifier]
|
/// A const version of [Identifier]
|
||||||
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord)]
|
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord)]
|
||||||
pub struct ConstIdentifier {
|
pub struct ConstIdentifier {
|
||||||
|
|
|
@ -21,3 +21,5 @@
|
||||||
|
|
||||||
pub mod checked;
|
pub mod checked;
|
||||||
pub mod unchecked;
|
pub mod unchecked;
|
||||||
|
|
||||||
|
pub use checked::*;
|
||||||
|
|
|
@ -18,22 +18,25 @@
|
||||||
* If not, see <https://www.gnu.org/licenses/>.
|
* If not, see <https://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
||||||
use core::fmt;
|
use core::fmt;
|
||||||
|
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
lexing::{error::SpannedLexingError, TokenSpan},
|
lexing::{error::SpannedLexingError, TokenSpan},
|
||||||
parsing::checked::error::SpannedParsingError,
|
parsing::{self},
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(Error, Debug)]
|
#[derive(Error, Debug)]
|
||||||
pub enum TrixyError {
|
pub enum TrixyError {
|
||||||
#[error(transparent)]
|
#[error(transparent)]
|
||||||
Lexing(#[from] SpannedLexingError),
|
Lexing(#[from] SpannedLexingError),
|
||||||
|
|
||||||
#[error(transparent)]
|
#[error(transparent)]
|
||||||
Parsing(#[from] SpannedParsingError),
|
Parsing(#[from] parsing::unchecked::error::SpannedParsingError),
|
||||||
|
|
||||||
|
#[error(transparent)]
|
||||||
|
Processing(#[from] parsing::checked::error::SpannedParsingError),
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The context of an Error.
|
/// The context of an Error.
|
||||||
|
|
|
@ -18,22 +18,23 @@
|
||||||
* If not, see <https://www.gnu.org/licenses/>.
|
* If not, see <https://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
||||||
use error::TrixyError;
|
use error::TrixyError;
|
||||||
|
|
||||||
use crate::lexing::TokenStream;
|
use crate::lexing::TokenStream;
|
||||||
|
|
||||||
use self::command_spec::checked::CommandSpec;
|
use self::command_spec::checked::CommandSpec;
|
||||||
|
|
||||||
mod command_spec;
|
pub mod command_spec;
|
||||||
pub mod error;
|
pub mod error;
|
||||||
pub mod lexing;
|
pub mod lexing;
|
||||||
pub mod parsing;
|
pub mod parsing;
|
||||||
|
|
||||||
pub fn parse_trixy_lang(input: &str) -> Result<CommandSpec, Box<TrixyError>> {
|
pub fn parse_trixy_lang(input: &str) -> Result<CommandSpec, Box<TrixyError>> {
|
||||||
let input_tokens = TokenStream::lex(input)
|
let input_tokens = TokenStream::lex(input)
|
||||||
.map_err(|err| Box::new(err.into()))?
|
.map_err(Into::<TrixyError>::into)?
|
||||||
.parse()
|
.parse_unchecked()
|
||||||
|
.map_err(Into::<TrixyError>::into)?
|
||||||
|
.process(input.to_owned())
|
||||||
.map_err(Into::<TrixyError>::into)?;
|
.map_err(Into::<TrixyError>::into)?;
|
||||||
Ok(input_tokens)
|
Ok(input_tokens)
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,10 +18,9 @@
|
||||||
* If not, see <https://www.gnu.org/licenses/>.
|
* If not, see <https://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
||||||
use std::{fs, process::exit};
|
use std::{fs, process::exit};
|
||||||
|
|
||||||
use trixy_lang_parser::lexing::TokenStream;
|
use trixy_lang_parser::{lexing::TokenStream, parse_trixy_lang};
|
||||||
|
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
|
|
||||||
|
@ -56,6 +55,13 @@ pub enum Command {
|
||||||
/// The file containing the trixy code to process
|
/// The file containing the trixy code to process
|
||||||
file: PathBuf,
|
file: PathBuf,
|
||||||
},
|
},
|
||||||
|
|
||||||
|
/// Act on the file as the library would do it
|
||||||
|
Library {
|
||||||
|
#[clap(value_parser)]
|
||||||
|
/// The file containing the trixy code to process
|
||||||
|
file: PathBuf,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn main() {
|
pub fn main() {
|
||||||
|
@ -127,5 +133,13 @@ pub fn main() {
|
||||||
};
|
};
|
||||||
println!("{:#?}", processed);
|
println!("{:#?}", processed);
|
||||||
}
|
}
|
||||||
|
Command::Library { file } => {
|
||||||
|
let input = fs::read_to_string(file).unwrap();
|
||||||
|
let parsed = parse_trixy_lang(&input).unwrap_or_else(|err| {
|
||||||
|
eprintln!("{}", err);
|
||||||
|
exit(1)
|
||||||
|
});
|
||||||
|
println!("{:#?}", parsed);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,7 +18,6 @@
|
||||||
* If not, see <https://www.gnu.org/licenses/>.
|
* If not, see <https://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
|
|
||||||
use std::{error::Error, fmt::Display};
|
use std::{error::Error, fmt::Display};
|
||||||
|
@ -36,6 +35,18 @@ pub enum ParsingError {
|
||||||
TypeNotDeclared { r#type: Identifier, span: TokenSpan },
|
TypeNotDeclared { r#type: Identifier, span: TokenSpan },
|
||||||
#[error(transparent)]
|
#[error(transparent)]
|
||||||
PreParseError(#[from] OldSpannedParsingError),
|
PreParseError(#[from] OldSpannedParsingError),
|
||||||
|
#[error("The enum ('{name}') has the same name as it's namespace")]
|
||||||
|
EnumWithNamespaceName {
|
||||||
|
name: Identifier,
|
||||||
|
enum_span: TokenSpan,
|
||||||
|
namespace_span: TokenSpan,
|
||||||
|
},
|
||||||
|
#[error("The enum ('{name}') has the same name as it's namespace if it's in Pascal case")]
|
||||||
|
EnumWithNamespaceNamePascal {
|
||||||
|
name: Identifier,
|
||||||
|
enum_span: TokenSpan,
|
||||||
|
namespace_span: TokenSpan,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ParsingError {
|
impl ParsingError {
|
||||||
|
@ -43,6 +54,8 @@ impl ParsingError {
|
||||||
match self {
|
match self {
|
||||||
ParsingError::TypeNotDeclared { span, .. } => span,
|
ParsingError::TypeNotDeclared { span, .. } => span,
|
||||||
ParsingError::PreParseError(err) => err.source.span(),
|
ParsingError::PreParseError(err) => err.source.span(),
|
||||||
|
ParsingError::EnumWithNamespaceName { enum_span, .. } => enum_span,
|
||||||
|
ParsingError::EnumWithNamespaceNamePascal { enum_span, .. } => enum_span,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -52,6 +65,8 @@ impl AdditionalHelp for ParsingError {
|
||||||
match self {
|
match self {
|
||||||
ParsingError::TypeNotDeclared { .. } => "This type should have been mentioned in the namespaces above, or in the namespace of this type usage".to_owned(),
|
ParsingError::TypeNotDeclared { .. } => "This type should have been mentioned in the namespaces above, or in the namespace of this type usage".to_owned(),
|
||||||
ParsingError::PreParseError(err) => ErrorContextDisplay::source(err).additional_help(),
|
ParsingError::PreParseError(err) => ErrorContextDisplay::source(err).additional_help(),
|
||||||
|
ParsingError::EnumWithNamespaceNamePascal {..}
|
||||||
|
| ParsingError::EnumWithNamespaceName {..} => "Change the name of this Enumeration as the generation process in trixy-macros needs to use this name".to_owned(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,9 +18,10 @@
|
||||||
* If not, see <https://www.gnu.org/licenses/>.
|
* If not, see <https://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
||||||
use std::mem;
|
use std::mem;
|
||||||
|
|
||||||
|
use convert_case::{Case, Casing};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
command_spec::{
|
command_spec::{
|
||||||
checked::{
|
checked::{
|
||||||
|
@ -35,7 +36,7 @@ use crate::{
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
error::ErrorContext,
|
error::ErrorContext,
|
||||||
lexing::{TokenKind, TokenStream},
|
lexing::{TokenKind, TokenSpan},
|
||||||
};
|
};
|
||||||
|
|
||||||
use self::error::{ParsingError, SpannedParsingError};
|
use self::error::{ParsingError, SpannedParsingError};
|
||||||
|
@ -51,29 +52,6 @@ struct Parser {
|
||||||
original_file: String,
|
original_file: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TokenStream {
|
|
||||||
pub fn parse(mut self) -> Result<CommandSpec, SpannedParsingError> {
|
|
||||||
let original_file = mem::take(&mut self.original_file);
|
|
||||||
|
|
||||||
let unchecked = self.parse_unchecked().map_err(|err| {
|
|
||||||
let span = *err.source.span();
|
|
||||||
SpannedParsingError {
|
|
||||||
source: Box::new(ParsingError::from(err)),
|
|
||||||
context: ErrorContext::from_span(span, &original_file),
|
|
||||||
}
|
|
||||||
})?;
|
|
||||||
|
|
||||||
let checked = Parser {
|
|
||||||
command_spec: unchecked,
|
|
||||||
structures: vec![],
|
|
||||||
enumerations: vec![],
|
|
||||||
original_file,
|
|
||||||
}
|
|
||||||
.parse()?;
|
|
||||||
Ok(checked)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl UncheckedCommandSpec {
|
impl UncheckedCommandSpec {
|
||||||
pub fn process(self, original_file: String) -> Result<CommandSpec, SpannedParsingError> {
|
pub fn process(self, original_file: String) -> Result<CommandSpec, SpannedParsingError> {
|
||||||
let checked = Parser {
|
let checked = Parser {
|
||||||
|
@ -111,6 +89,7 @@ impl Parser {
|
||||||
&mut self,
|
&mut self,
|
||||||
namespace: UncheckedNamespace,
|
namespace: UncheckedNamespace,
|
||||||
) -> Result<Namespace, ParsingError> {
|
) -> Result<Namespace, ParsingError> {
|
||||||
|
let namespace_span = namespace.name.span;
|
||||||
let name = match namespace.name.kind {
|
let name = match namespace.name.kind {
|
||||||
TokenKind::Identifier(ident) => Identifier { name: ident },
|
TokenKind::Identifier(ident) => Identifier { name: ident },
|
||||||
// This is not really used, so the value put here does not matter
|
// This is not really used, so the value put here does not matter
|
||||||
|
@ -123,7 +102,7 @@ impl Parser {
|
||||||
let mut enumerations = vec![];
|
let mut enumerations = vec![];
|
||||||
let mut enumerations_counter = 0;
|
let mut enumerations_counter = 0;
|
||||||
for enumeration in namespace.enumerations {
|
for enumeration in namespace.enumerations {
|
||||||
enumerations.push(self.process_enumeration(enumeration)?);
|
enumerations.push(self.process_enumeration(enumeration, &name, namespace_span)?);
|
||||||
enumerations_counter += 1;
|
enumerations_counter += 1;
|
||||||
}
|
}
|
||||||
let mut structures = vec![];
|
let mut structures = vec![];
|
||||||
|
@ -186,10 +165,27 @@ impl Parser {
|
||||||
fn process_enumeration(
|
fn process_enumeration(
|
||||||
&mut self,
|
&mut self,
|
||||||
mut enumeration: UncheckedEnumeration,
|
mut enumeration: UncheckedEnumeration,
|
||||||
|
namespace_name: &Identifier,
|
||||||
|
namespace_span: TokenSpan,
|
||||||
) -> Result<Enumeration, ParsingError> {
|
) -> Result<Enumeration, ParsingError> {
|
||||||
self.enumerations.push(enumeration.clone());
|
self.enumerations.push(enumeration.clone());
|
||||||
|
|
||||||
let identifier = mem::take(&mut enumeration.identifier.kind).into();
|
let enum_span = enumeration.identifier.span;
|
||||||
|
let identifier: Identifier = mem::take(&mut enumeration.identifier.kind).into();
|
||||||
|
if &identifier == namespace_name {
|
||||||
|
return Err(ParsingError::EnumWithNamespaceName {
|
||||||
|
name: identifier.clone(),
|
||||||
|
enum_span,
|
||||||
|
namespace_span,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if identifier.name == namespace_name.name.to_case(Case::Pascal) {
|
||||||
|
return Err(ParsingError::EnumWithNamespaceNamePascal {
|
||||||
|
name: identifier.clone(),
|
||||||
|
enum_span,
|
||||||
|
namespace_span,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
let mut states = vec![];
|
let mut states = vec![];
|
||||||
for mut state in enumeration.states {
|
for mut state in enumeration.states {
|
||||||
|
|
|
@ -20,4 +20,4 @@
|
||||||
|
|
||||||
|
|
||||||
pub mod checked;
|
pub mod checked;
|
||||||
mod unchecked;
|
pub mod unchecked;
|
||||||
|
|
|
@ -254,7 +254,7 @@ impl Parser {
|
||||||
}
|
}
|
||||||
while self.expect_peek(token![Comma]) {
|
while self.expect_peek(token![Comma]) {
|
||||||
self.expect(token![Comma])?;
|
self.expect(token![Comma])?;
|
||||||
if self.expect_peek(token![Ident]) {
|
if self.expect_peek(token![Ident]) || self.expect_peek(token![DocComment]) {
|
||||||
contents.push(self.parse_doc_named_type()?);
|
contents.push(self.parse_doc_named_type()?);
|
||||||
} else {
|
} else {
|
||||||
break;
|
break;
|
||||||
|
|
|
@ -0,0 +1,6 @@
|
||||||
|
# build
|
||||||
|
/target
|
||||||
|
/result
|
||||||
|
|
||||||
|
# This crate is a library
|
||||||
|
Cargo.lock
|
|
@ -0,0 +1,33 @@
|
||||||
|
# Copyright (C) 2023 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"
|
||||||
|
|
||||||
|
[lib]
|
||||||
|
proc-macro = true
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
convert_case = "0.6.0"
|
||||||
|
proc-macro2 = "1.0.70"
|
||||||
|
quote = "1.0.33"
|
||||||
|
syn = { version = "2.0.41", features = ["extra-traits", "full", "parsing"] }
|
||||||
|
trixy-lang_parser = { path = "../trixy-lang_parser" }
|
|
@ -0,0 +1 @@
|
||||||
|
api_correct.tri
|
|
@ -0,0 +1,40 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2023 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/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
nasp trinitrix {
|
||||||
|
struct Callback {
|
||||||
|
func: String,
|
||||||
|
timeout: String,
|
||||||
|
};
|
||||||
|
|
||||||
|
enum CallbackPriority {
|
||||||
|
High,
|
||||||
|
Medium,
|
||||||
|
Low,
|
||||||
|
};
|
||||||
|
|
||||||
|
fn execute_callback(callback: Callback, priority: CallbackPriority);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// That's a flat out lie, but it results in a rather nice syntax highlight compared to nothing:
|
||||||
|
// vim: syntax=rust
|
|
@ -0,0 +1,30 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2023 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/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
fn print(message: CommandTransferValue);
|
||||||
|
|
||||||
|
nasp trinitrix { {}
|
||||||
|
fn hi honner(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,9 @@
|
||||||
|
use trixy_macros::trixy_generate;
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
trixy_generate! {
|
||||||
|
path: "./examples/main/api.tri"
|
||||||
|
languages: rust, lua, c
|
||||||
|
generate_debug: false
|
||||||
|
}
|
||||||
|
}
|
|
@ -18,24 +18,25 @@
|
||||||
* If not, see <https://www.gnu.org/licenses/>.
|
* If not, see <https://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
||||||
//! This module is responsible for parsing the config passed to the macro call:
|
//! This module is responsible for parsing the config passed to the macro call:
|
||||||
//! For example:
|
//! For example:
|
||||||
//! ```no_run
|
//! ```no_run
|
||||||
//! trixy_generate! {
|
//! trixy_generate! {
|
||||||
//! path: ./trintrix_command_interface.tri
|
//! path: "./path/to/trixy/file.tri"
|
||||||
//! languages: rust, lua, c
|
//! languages: rust, lua, c
|
||||||
|
//! generate_debug: false
|
||||||
//! }
|
//! }
|
||||||
//! ```
|
//! ```
|
||||||
|
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
|
|
||||||
use proc_macro2::Ident;
|
use proc_macro2::Ident;
|
||||||
use syn::{parse::Parse, punctuated::Punctuated, LitStr, Result, Token};
|
use syn::{parse::Parse, punctuated::Punctuated, LitBool, LitStr, Result, Token};
|
||||||
|
|
||||||
mod kw {
|
mod kw {
|
||||||
syn::custom_keyword!(path);
|
syn::custom_keyword!(path);
|
||||||
syn::custom_keyword!(languages);
|
syn::custom_keyword!(languages);
|
||||||
|
syn::custom_keyword!(generate_debug);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
|
@ -63,6 +64,15 @@ struct Path {
|
||||||
raw: PathBuf,
|
raw: PathBuf,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
struct GenerateDebug {
|
||||||
|
#[allow(dead_code)]
|
||||||
|
generate_debug: kw::generate_debug,
|
||||||
|
#[allow(dead_code)]
|
||||||
|
colon: Token![:],
|
||||||
|
raw: bool,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct TrixyConfig {
|
pub struct TrixyConfig {
|
||||||
/// The Path to the base command interface config file
|
/// The Path to the base command interface config file
|
||||||
|
@ -70,10 +80,17 @@ pub struct TrixyConfig {
|
||||||
|
|
||||||
/// The languages the commands should be exposed in
|
/// The languages the commands should be exposed in
|
||||||
languages: Languages,
|
languages: Languages,
|
||||||
|
|
||||||
|
/// Should the macro generate Debug trait implementation for each enum
|
||||||
|
/// These are very useful but completely obscure the `cargo expand` ouput
|
||||||
|
generate_debug: GenerateDebug,
|
||||||
}
|
}
|
||||||
impl TrixyConfig {
|
impl TrixyConfig {
|
||||||
pub fn get_path(&self) -> PathBuf {
|
pub fn get_path(&self) -> &std::path::Path {
|
||||||
self.path.raw
|
&self.path.raw
|
||||||
|
}
|
||||||
|
pub fn generate_debug(&self) -> bool {
|
||||||
|
self.generate_debug.raw
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -82,6 +99,20 @@ impl Parse for TrixyConfig {
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
path: input.parse()?,
|
path: input.parse()?,
|
||||||
languages: input.parse()?,
|
languages: input.parse()?,
|
||||||
|
generate_debug: input.parse()?,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Parse for GenerateDebug {
|
||||||
|
fn parse(input: syn::parse::ParseStream) -> Result<Self> {
|
||||||
|
let kw: kw::generate_debug = input.parse()?;
|
||||||
|
let colon: Token![:] = input.parse()?;
|
||||||
|
let raw = input.parse::<LitBool>()?.value();
|
||||||
|
Ok(Self {
|
||||||
|
generate_debug: kw,
|
||||||
|
colon,
|
||||||
|
raw,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -0,0 +1,280 @@
|
||||||
|
//! This module is responsible for generating the rust code used to interface with the api.
|
||||||
|
//! That includes the structs and enums declared in the trixy file and the enum used to describe the
|
||||||
|
//! command being executed.
|
||||||
|
|
||||||
|
use std::cell::OnceCell;
|
||||||
|
|
||||||
|
use convert_case::{Case, Casing};
|
||||||
|
use proc_macro2::TokenStream as TokenStream2;
|
||||||
|
use quote::{format_ident, quote};
|
||||||
|
use trixy_lang_parser::command_spec::{
|
||||||
|
Attribute, CommandSpec, DocIdentifier, DocNamedType, Enumeration, Function, Identifier,
|
||||||
|
NamedType, Namespace, Structure, Type,
|
||||||
|
};
|
||||||
|
|
||||||
|
use crate::config::TrixyConfig;
|
||||||
|
|
||||||
|
thread_local! {static DEBUG: OnceCell<TokenStream2> = OnceCell::new();}
|
||||||
|
|
||||||
|
/// This function turns, for example, the following trixy input into this rust code:
|
||||||
|
/// ```rust
|
||||||
|
/// nasp trinitrix {
|
||||||
|
/// struct Callback {
|
||||||
|
/// func: Function,
|
||||||
|
/// timeout: Integer,
|
||||||
|
/// };
|
||||||
|
///
|
||||||
|
/// enum CallbackPriority {
|
||||||
|
/// High,
|
||||||
|
/// Medium,
|
||||||
|
/// Low,
|
||||||
|
/// };
|
||||||
|
///
|
||||||
|
/// fn execute_callback(callback: Callback, priority: CallbackPriority);
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
|
/// ```rust
|
||||||
|
/// #[derive(Debug)]
|
||||||
|
/// pub enum Commands {
|
||||||
|
/// Trinitrix(trinitrix::Trinitrix),
|
||||||
|
/// }
|
||||||
|
/// pub mod trinitrix {
|
||||||
|
/// #[allow(non_camel_case)]
|
||||||
|
/// #[derive(Debug)]
|
||||||
|
/// struct Callback {
|
||||||
|
/// func: Function,
|
||||||
|
/// timeout: Integer,
|
||||||
|
/// }
|
||||||
|
/// #[allow(non_camel_case)]
|
||||||
|
/// #[derive(Debug)]
|
||||||
|
/// enum CallbackPriority {
|
||||||
|
/// High,
|
||||||
|
/// Medium,
|
||||||
|
/// Low,
|
||||||
|
/// }
|
||||||
|
/// #[derive(Debug)]
|
||||||
|
/// pub enum Trinitrix {
|
||||||
|
/// #[allow(non_camel_case)]
|
||||||
|
/// execute_callback { callback: Callback, priority: CallbackPriority },
|
||||||
|
/// }
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
|
pub fn generate(trixy: &CommandSpec, config: &TrixyConfig) -> TokenStream2 {
|
||||||
|
DEBUG.with(|d| {
|
||||||
|
d.set(if config.generate_debug() {
|
||||||
|
quote! {
|
||||||
|
#[derive(Debug)]
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
TokenStream2::default()
|
||||||
|
})
|
||||||
|
.expect("The cell should always be empty at this point");
|
||||||
|
});
|
||||||
|
|
||||||
|
let modules: TokenStream2 = trixy.namespaces.iter().map(namespace_to_module).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(function_to_rust).collect();
|
||||||
|
let namespace_modules: Vec<TokenStream2> = trixy
|
||||||
|
.namespaces
|
||||||
|
.iter()
|
||||||
|
.map(namespace_to_module_enum)
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
let debug = get_debug_sate();
|
||||||
|
|
||||||
|
quote! {
|
||||||
|
#structures
|
||||||
|
#enumerations
|
||||||
|
#debug
|
||||||
|
pub enum Commands {
|
||||||
|
#(#functions,)*
|
||||||
|
#(#namespace_modules),*
|
||||||
|
}
|
||||||
|
#modules
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn namespace_to_module(namespace: &Namespace) -> TokenStream2 {
|
||||||
|
let ident = identifier_to_rust(&namespace.name);
|
||||||
|
let enum_ident = format_ident!("{}", &namespace.name.name.to_case(Case::Pascal));
|
||||||
|
|
||||||
|
let doc_comments: TokenStream2 = namespace
|
||||||
|
.attributes
|
||||||
|
.iter()
|
||||||
|
.map(attribute_to_doc_comment)
|
||||||
|
.collect();
|
||||||
|
let structures: TokenStream2 = namespace.structures.iter().map(structure_to_rust).collect();
|
||||||
|
let enumerations: TokenStream2 = namespace
|
||||||
|
.enumerations
|
||||||
|
.iter()
|
||||||
|
.map(enumeration_to_rust)
|
||||||
|
.collect();
|
||||||
|
let functions: Vec<TokenStream2> = namespace.functions.iter().map(function_to_rust).collect();
|
||||||
|
let namespace_modules: Vec<TokenStream2> = namespace
|
||||||
|
.namespaces
|
||||||
|
.iter()
|
||||||
|
.map(namespace_to_module_enum)
|
||||||
|
.collect();
|
||||||
|
let namespaces: TokenStream2 = namespace
|
||||||
|
.namespaces
|
||||||
|
.iter()
|
||||||
|
.map(namespace_to_module)
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
let debug = get_debug_sate();
|
||||||
|
quote! {
|
||||||
|
#doc_comments
|
||||||
|
pub mod #ident {
|
||||||
|
#structures
|
||||||
|
#enumerations
|
||||||
|
#debug
|
||||||
|
pub enum #enum_ident {
|
||||||
|
#(#functions,)*
|
||||||
|
#(#namespace_modules),*
|
||||||
|
}
|
||||||
|
#namespaces
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn namespace_to_module_enum(namespace: &Namespace) -> TokenStream2 {
|
||||||
|
let pascal_ident = format_ident!("{}", namespace.name.name.to_case(Case::Pascal));
|
||||||
|
let ident = identifier_to_rust(&namespace.name);
|
||||||
|
quote! {
|
||||||
|
#pascal_ident(#ident :: #pascal_ident)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn attribute_to_doc_comment(attribute: &Attribute) -> TokenStream2 {
|
||||||
|
let Attribute::doc(doc_comment) = attribute;
|
||||||
|
|
||||||
|
quote! {
|
||||||
|
#[doc = #doc_comment]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_debug_sate() -> TokenStream2 {
|
||||||
|
let debug = DEBUG.with(|d| {
|
||||||
|
d.get()
|
||||||
|
.expect("The cell should contain something at this point")
|
||||||
|
.clone()
|
||||||
|
});
|
||||||
|
debug
|
||||||
|
}
|
||||||
|
|
||||||
|
fn function_to_rust(function: &Function) -> TokenStream2 {
|
||||||
|
let doc_comments: TokenStream2 = function
|
||||||
|
.attributes
|
||||||
|
.iter()
|
||||||
|
.map(attribute_to_doc_comment)
|
||||||
|
.collect();
|
||||||
|
let ident = identifier_to_rust(&function.identifier);
|
||||||
|
let inputs: Vec<TokenStream2> = function.inputs.iter().map(named_type_to_rust).collect();
|
||||||
|
|
||||||
|
quote! {
|
||||||
|
#doc_comments
|
||||||
|
#[allow(non_camel_case_types)]
|
||||||
|
#ident {#(#inputs),*}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn enumeration_to_rust(enumeration: &Enumeration) -> TokenStream2 {
|
||||||
|
let doc_comments: TokenStream2 = enumeration
|
||||||
|
.attributes
|
||||||
|
.iter()
|
||||||
|
.map(attribute_to_doc_comment)
|
||||||
|
.collect();
|
||||||
|
let ident = identifier_to_rust(&enumeration.identifier);
|
||||||
|
let states: Vec<TokenStream2> = enumeration
|
||||||
|
.states
|
||||||
|
.iter()
|
||||||
|
.map(doc_identifier_to_rust)
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
let debug = get_debug_sate();
|
||||||
|
|
||||||
|
quote! {
|
||||||
|
#doc_comments
|
||||||
|
#[allow(non_camel_case)]
|
||||||
|
#debug
|
||||||
|
enum #ident {
|
||||||
|
#(#states),*
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn structure_to_rust(structure: &Structure) -> TokenStream2 {
|
||||||
|
let doc_comments: TokenStream2 = structure
|
||||||
|
.attributes
|
||||||
|
.iter()
|
||||||
|
.map(attribute_to_doc_comment)
|
||||||
|
.collect();
|
||||||
|
let ident = identifier_to_rust(&structure.identifier);
|
||||||
|
let contents: Vec<TokenStream2> = structure
|
||||||
|
.contents
|
||||||
|
.iter()
|
||||||
|
.map(doc_named_type_to_rust)
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
let debug = get_debug_sate();
|
||||||
|
|
||||||
|
quote! {
|
||||||
|
#doc_comments
|
||||||
|
#[allow(non_camel_case)]
|
||||||
|
#debug
|
||||||
|
struct #ident {
|
||||||
|
#(#contents),*
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn doc_identifier_to_rust(doc_identifier: &DocIdentifier) -> TokenStream2 {
|
||||||
|
let doc_comments: TokenStream2 = doc_identifier
|
||||||
|
.attributes
|
||||||
|
.iter()
|
||||||
|
.map(attribute_to_doc_comment)
|
||||||
|
.collect();
|
||||||
|
let identifier = identifier_to_rust(&doc_identifier.into());
|
||||||
|
|
||||||
|
quote! {
|
||||||
|
#doc_comments
|
||||||
|
#identifier
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn identifier_to_rust(identifier: &Identifier) -> TokenStream2 {
|
||||||
|
let ident = format_ident!("{}", &identifier.name);
|
||||||
|
quote! {
|
||||||
|
#ident
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn doc_named_type_to_rust(doc_named_type: &DocNamedType) -> TokenStream2 {
|
||||||
|
let doc_comments: TokenStream2 = doc_named_type
|
||||||
|
.attributes
|
||||||
|
.iter()
|
||||||
|
.map(attribute_to_doc_comment)
|
||||||
|
.collect();
|
||||||
|
let named_type = named_type_to_rust(&doc_named_type.into());
|
||||||
|
quote! {
|
||||||
|
#doc_comments
|
||||||
|
#named_type
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn named_type_to_rust(named_type: &NamedType) -> TokenStream2 {
|
||||||
|
let ident = identifier_to_rust(&named_type.name);
|
||||||
|
let r#type = type_to_rust(&named_type.r#type);
|
||||||
|
quote! {
|
||||||
|
#ident : #r#type
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn type_to_rust(r#type: &Type) -> TokenStream2 {
|
||||||
|
let ident = identifier_to_rust(&r#type.identifier);
|
||||||
|
let generics: Vec<TokenStream2> = r#type.generic_args.iter().map(type_to_rust).collect();
|
||||||
|
quote! {
|
||||||
|
#ident <#(#generics),*>
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1 @@
|
||||||
|
pub mod host;
|
|
@ -0,0 +1,56 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2023 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;
|
||||||
|
|
||||||
|
use proc_macro::TokenStream;
|
||||||
|
use quote::quote;
|
||||||
|
use syn::parse_macro_input;
|
||||||
|
use trixy_lang_parser::parse_trixy_lang;
|
||||||
|
|
||||||
|
use crate::config::TrixyConfig;
|
||||||
|
|
||||||
|
mod config;
|
||||||
|
mod generate;
|
||||||
|
|
||||||
|
/// This is the heart of Trixy
|
||||||
|
/// It mainly does one thing:
|
||||||
|
/// - Generate a tree of modules from the input trixy file
|
||||||
|
///
|
||||||
|
#[proc_macro]
|
||||||
|
pub fn trixy_generate(input: TokenStream) -> TokenStream {
|
||||||
|
let input = parse_macro_input!(input as TrixyConfig);
|
||||||
|
|
||||||
|
let source_code = fs::read_to_string(input.get_path()).unwrap_or_else(|err| {
|
||||||
|
panic! {"Can't read file at path: '{}'. The Error is: '{}'", input.get_path().display(), err};
|
||||||
|
});
|
||||||
|
|
||||||
|
let trixy_code = parse_trixy_lang(&source_code).unwrap_or_else(|err| {
|
||||||
|
panic! {"Parsing of the trixy file failed: \n{}", err}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Build the final rust hosting code
|
||||||
|
let host_rust_code = generate::host::generate(&trixy_code, &input);
|
||||||
|
|
||||||
|
let output = quote! {
|
||||||
|
#host_rust_code
|
||||||
|
};
|
||||||
|
output.into()
|
||||||
|
}
|
Reference in New Issue