Compare commits
No commits in common. "7d1a41aca9e6a173ec502b595625bb1d3bc6548d" and "1d49bdf2cf95000faba92956d4615ce4a1b380e1" have entirely different histories.
7d1a41aca9
...
1d49bdf2cf
|
@ -2,10 +2,8 @@
|
||||||
/target
|
/target
|
||||||
/result
|
/result
|
||||||
|
|
||||||
# dev env
|
# direnv
|
||||||
.direnv
|
.direnv
|
||||||
.ccls-cache
|
|
||||||
|
|
||||||
|
|
||||||
# trixy is a library
|
# trixy is a library
|
||||||
Cargo.lock
|
Cargo.lock
|
||||||
|
|
|
@ -102,7 +102,7 @@ comments:
|
||||||
commenter:
|
commenter:
|
||||||
type: line
|
type: line
|
||||||
comment_char: "//"
|
comment_char: "//"
|
||||||
trailing_lines: 1
|
trailing_lines: 2
|
||||||
|
|
||||||
- extensions:
|
- extensions:
|
||||||
- rs
|
- rs
|
||||||
|
@ -124,7 +124,7 @@ comments:
|
||||||
start_block_char: "/*\n"
|
start_block_char: "/*\n"
|
||||||
end_block_char: "*/\n"
|
end_block_char: "*/\n"
|
||||||
per_line_char: "*"
|
per_line_char: "*"
|
||||||
trailing_lines: 1
|
trailing_lines: 2
|
||||||
|
|
||||||
# In this case extension is singular and a single string extension is provided.
|
# In this case extension is singular and a single string extension is provided.
|
||||||
- extension: html
|
- extension: html
|
||||||
|
@ -139,7 +139,7 @@ comments:
|
||||||
commenter:
|
commenter:
|
||||||
type: line
|
type: line
|
||||||
comment_char: ";;;"
|
comment_char: ";;;"
|
||||||
trailing_lines: 1
|
trailing_lines: 2
|
||||||
|
|
||||||
- extensions:
|
- extensions:
|
||||||
- ebnf
|
- ebnf
|
||||||
|
@ -148,7 +148,7 @@ comments:
|
||||||
start_block_char: "#(*\n"
|
start_block_char: "#(*\n"
|
||||||
end_block_char: "#*)\n"
|
end_block_char: "#*)\n"
|
||||||
per_line_char: "#"
|
per_line_char: "#"
|
||||||
trailing_lines: 1
|
trailing_lines: 2
|
||||||
|
|
||||||
# The extension string "any" is special and so will match any file
|
# The extension string "any" is special and so will match any file
|
||||||
# extensions. Commenter configurations are always checked in the
|
# extensions. Commenter configurations are always checked in the
|
||||||
|
@ -162,4 +162,4 @@ comments:
|
||||||
commenter:
|
commenter:
|
||||||
type: line
|
type: line
|
||||||
comment_char: '#'
|
comment_char: '#'
|
||||||
trailing_lines: 1
|
trailing_lines: 2
|
||||||
|
|
|
@ -22,12 +22,11 @@ name = "trixy"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
|
|
||||||
|
[lib]
|
||||||
|
proc-macro = true
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
convert_case = "0.6.0"
|
convert_case = "0.6.0"
|
||||||
log = "0.4.20"
|
|
||||||
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"] }
|
||||||
thiserror = "1.0.51"
|
|
||||||
# trixy-lang_parser = { path = "./trixy-lang_parser" }
|
|
||||||
# trixy-macros = { path = "./trixy-macros" }
|
|
||||||
|
|
|
@ -17,7 +17,6 @@
|
||||||
# If not, see <https://www.gnu.org/licenses/>.
|
# If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
{
|
{
|
||||||
description = "A rust crate used to generate multi-language apis for your
|
description = "A rust crate used to generate multi-language apis for your
|
||||||
application";
|
application";
|
||||||
|
@ -123,10 +122,6 @@
|
||||||
cargo-edit
|
cargo-edit
|
||||||
cargo-expand
|
cargo-expand
|
||||||
|
|
||||||
# Used to format the c header files
|
|
||||||
libclang
|
|
||||||
gdb
|
|
||||||
|
|
||||||
ebnf2pdf.outputs.packages."${system}".default
|
ebnf2pdf.outputs.packages."${system}".default
|
||||||
];
|
];
|
||||||
inherit nativeBuildInputs buildInputs;
|
inherit nativeBuildInputs buildInputs;
|
||||||
|
|
|
@ -0,0 +1,124 @@
|
||||||
|
/*
|
||||||
|
* 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/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
//! This module is responsible for parsing the config passed to the macro call:
|
||||||
|
//! For example:
|
||||||
|
//! ```no_run
|
||||||
|
//! trixy_generate! {
|
||||||
|
//! path: ./trintrix_command_interface.tri
|
||||||
|
//! languages: rust, lua, c
|
||||||
|
//! }
|
||||||
|
//! ```
|
||||||
|
|
||||||
|
use std::path::PathBuf;
|
||||||
|
|
||||||
|
use proc_macro2::Ident;
|
||||||
|
use syn::{parse::Parse, punctuated::Punctuated, LitStr, Result, Token};
|
||||||
|
|
||||||
|
mod kw {
|
||||||
|
syn::custom_keyword!(path);
|
||||||
|
syn::custom_keyword!(languages);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum Language {
|
||||||
|
Rust,
|
||||||
|
Lua,
|
||||||
|
C,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
struct Languages {
|
||||||
|
#[allow(dead_code)]
|
||||||
|
languages: kw::languages,
|
||||||
|
#[allow(dead_code)]
|
||||||
|
colon: Token![:],
|
||||||
|
raw: Punctuated<Language, Token![,]>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
struct Path {
|
||||||
|
#[allow(dead_code)]
|
||||||
|
path: kw::path,
|
||||||
|
#[allow(dead_code)]
|
||||||
|
colon: Token![:],
|
||||||
|
raw: PathBuf,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct TrixyConfig {
|
||||||
|
/// The Path to the base command interface config file
|
||||||
|
path: Path,
|
||||||
|
|
||||||
|
/// The languages the commands should be exposed in
|
||||||
|
languages: Languages,
|
||||||
|
}
|
||||||
|
impl TrixyConfig {
|
||||||
|
pub fn get_path(&self) -> PathBuf {
|
||||||
|
self.path.raw
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Parse for TrixyConfig {
|
||||||
|
fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
|
||||||
|
Ok(Self {
|
||||||
|
path: input.parse()?,
|
||||||
|
languages: input.parse()?,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Parse for Path {
|
||||||
|
fn parse(input: syn::parse::ParseStream) -> Result<Self> {
|
||||||
|
let path: kw::path = input.parse()?;
|
||||||
|
let colon: Token![:] = input.parse()?;
|
||||||
|
let raw = PathBuf::from(input.parse::<LitStr>()?.value());
|
||||||
|
Ok(Self { path, colon, raw })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Parse for Languages {
|
||||||
|
fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
|
||||||
|
let languages: kw::languages = input.parse()?;
|
||||||
|
let colon: Token![:] = input.parse()?;
|
||||||
|
let raw = Punctuated::<Language, Token![,]>::parse_separated_nonempty(input)?;
|
||||||
|
Ok(Self {
|
||||||
|
languages,
|
||||||
|
colon,
|
||||||
|
raw,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Parse for Language {
|
||||||
|
fn parse(input: syn::parse::ParseStream) -> Result<Self> {
|
||||||
|
let ident: Ident = input.parse()?;
|
||||||
|
match &ident.to_string()[..] {
|
||||||
|
"rust" | "Rust" => Ok(Self::Rust),
|
||||||
|
"lua" | "Lua" => Ok(Self::Lua),
|
||||||
|
"c" | "C" => Ok(Self::C),
|
||||||
|
other => Err(input.error(format!(
|
||||||
|
"The language: `{}` is not a registered language!",
|
||||||
|
other
|
||||||
|
))),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,32 +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 std::ffi::c_char;
|
|
||||||
|
|
||||||
use thiserror::Error;
|
|
||||||
|
|
||||||
#[derive(Error, Debug)]
|
|
||||||
pub enum TypeConversionError {
|
|
||||||
#[error("Can't convert this ('{got:#?}') string into a valid rust string; Remember that these must be valid utf-8")]
|
|
||||||
String { got: *const c_char },
|
|
||||||
|
|
||||||
#[error("You passed a null pointer to the conversion function!")]
|
|
||||||
NullPointer,
|
|
||||||
}
|
|
106
src/lib.rs
106
src/lib.rs
|
@ -18,27 +18,101 @@
|
||||||
* If not, see <https://www.gnu.org/licenses/>.
|
* If not, see <https://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
//! Trixy contains the types used by the [trixy-macros] crate to provide ffi safe types
|
|
||||||
use std::{ffi::c_char, ops::Deref};
|
|
||||||
|
|
||||||
use proc_macro2::TokenStream;
|
use command_enum_parsing::DataCommandEnum;
|
||||||
|
use config::TrixyConfig;
|
||||||
|
use proc_macro::TokenStream;
|
||||||
|
use proc_macro2::TokenStream as TokenStream2;
|
||||||
use quote::quote;
|
use quote::quote;
|
||||||
|
use syn::parse_macro_input;
|
||||||
|
|
||||||
pub mod error;
|
use crate::trixy_lang::parse_trixy_lang;
|
||||||
pub mod traits;
|
|
||||||
|
|
||||||
// NOTE(@soispha): All types specified here *MUST* be include in the BASE_TYPES constant, otherwise
|
mod command_enum_parsing;
|
||||||
// they are not usable from Trixy code <2023-12-25>
|
mod config;
|
||||||
|
mod generate;
|
||||||
|
mod trixy_lang;
|
||||||
|
|
||||||
#[repr(C)]
|
/// This is the heart of the command api
|
||||||
pub struct String(*const c_char);
|
/// 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);
|
||||||
|
|
||||||
/// These are the "primitive" types used in Trixy, you can use any of them to create new structures
|
let trixy_code = parse_trixy_lang(input.get_path());
|
||||||
pub const BASE_TYPES: [&'static str; 2] = ["String", "u8"];
|
todo!()
|
||||||
|
// // Build the language wrappers
|
||||||
|
// let lua_wrapper: TokenStream2 = generate::lua_wrapper(&input);
|
||||||
|
//
|
||||||
|
// // Build the final enum
|
||||||
|
// let command_enum = generate::command_enum(&input);
|
||||||
|
|
||||||
pub fn to_c_name<T: Deref<Target = str>>(rust_type: T) -> TokenStream {
|
// let output = quote! {
|
||||||
match &*rust_type {
|
// #command_enum
|
||||||
"String" => quote!(const char*),
|
// #lua_wrapper
|
||||||
other => panic! {"'{}' is not a vaild type name!", other},
|
// };
|
||||||
}
|
// output.into()
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,152 +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 core::slice;
|
|
||||||
use std::{
|
|
||||||
cell::RefCell,
|
|
||||||
error::Error,
|
|
||||||
ffi::{c_char, c_int},
|
|
||||||
ptr,
|
|
||||||
};
|
|
||||||
|
|
||||||
use log::{error, warn};
|
|
||||||
|
|
||||||
use crate::error::TypeConversionError;
|
|
||||||
|
|
||||||
#[macro_export]
|
|
||||||
macro_rules! convert {
|
|
||||||
($input:expr) => {
|
|
||||||
match $input.try_into() {
|
|
||||||
Ok(ok) => ok,
|
|
||||||
Err(err) => {
|
|
||||||
trixy::traits::errno::set(err);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
// This code is heavily inspired by: https://michael-f-bryan.github.io/rust-ffi-guide/errors/return_types.html
|
|
||||||
thread_local! {
|
|
||||||
static LAST_ERROR: RefCell<Option<Box<TypeConversionError>>> = RefCell::new(None);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Update the most recent error, clearing whatever may have been there before.
|
|
||||||
pub fn set(error: TypeConversionError) {
|
|
||||||
error!("Setting LAST_ERROR: {}", error);
|
|
||||||
|
|
||||||
{
|
|
||||||
// Print a pseudo-backtrace for this error, following back each error's
|
|
||||||
// cause until we reach the root error.
|
|
||||||
let mut cause = error.source();
|
|
||||||
while let Some(parent_err) = cause {
|
|
||||||
warn!("Caused by: {}", parent_err);
|
|
||||||
cause = parent_err.source();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
LAST_ERROR.with(|prev| {
|
|
||||||
*prev.borrow_mut() = Some(Box::new(error));
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Retrieve the most recent error, clearing it in the process.
|
|
||||||
pub fn take_last_error() -> Option<Box<TypeConversionError>> {
|
|
||||||
LAST_ERROR.with(|prev| prev.borrow_mut().take())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub const ERROR_FUNCTIONS: &'static str = r#"
|
|
||||||
/// Calculate the number of bytes in the last error's error message **not**
|
|
||||||
/// including any trailing `null` characters.
|
|
||||||
extern int last_error_length();
|
|
||||||
|
|
||||||
/// Write the most recent error message into a caller-provided buffer as a UTF-8
|
|
||||||
/// string, returning the number of bytes written.
|
|
||||||
///
|
|
||||||
/// # Note
|
|
||||||
///
|
|
||||||
/// This writes a **UTF-8** string into the buffer. Windows users may need to
|
|
||||||
/// convert it to a UTF-16 "unicode" afterwards.
|
|
||||||
///
|
|
||||||
/// If there are no recent errors then this returns `0` (because we wrote 0
|
|
||||||
/// bytes). `-1` is returned if there are any errors, for example when passed a
|
|
||||||
/// null pointer or a buffer of insufficient size.
|
|
||||||
extern int last_error_message(char* buffer, int length);
|
|
||||||
"#;
|
|
||||||
|
|
||||||
/// Calculate the number of bytes in the last error's error message **not**
|
|
||||||
/// including any trailing `null` characters.
|
|
||||||
#[no_mangle]
|
|
||||||
pub extern "C" fn last_error_length() -> c_int {
|
|
||||||
LAST_ERROR.with(|prev| match *prev.borrow() {
|
|
||||||
Some(ref err) => err.to_string().len() as c_int + 1,
|
|
||||||
None => 0,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Write the most recent error message into a caller-provided buffer as a UTF-8
|
|
||||||
/// string, returning the number of bytes written.
|
|
||||||
///
|
|
||||||
/// # Note
|
|
||||||
///
|
|
||||||
/// This writes a **UTF-8** string into the buffer. Windows users may need to
|
|
||||||
/// convert it to a UTF-16 "unicode" afterwards.
|
|
||||||
///
|
|
||||||
/// If there are no recent errors then this returns `0` (because we wrote 0
|
|
||||||
/// bytes). `-1` is returned if there are any errors, for example when passed a
|
|
||||||
/// null pointer or a buffer of insufficient size.
|
|
||||||
#[no_mangle]
|
|
||||||
pub unsafe extern "C" fn last_error_message(buffer: *mut c_char, length: c_int) -> c_int {
|
|
||||||
if buffer.is_null() {
|
|
||||||
warn!("Null pointer passed into last_error_message() as the buffer");
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
let last_error = match take_last_error() {
|
|
||||||
Some(err) => err,
|
|
||||||
None => return 0,
|
|
||||||
};
|
|
||||||
|
|
||||||
let error_message = last_error.to_string();
|
|
||||||
|
|
||||||
let buffer = slice::from_raw_parts_mut(buffer as *mut u8, length as usize);
|
|
||||||
|
|
||||||
if error_message.len() >= buffer.len() {
|
|
||||||
warn!("Buffer provided for writing the last error message is too small.");
|
|
||||||
warn!(
|
|
||||||
"Expected at least {} bytes but got {}",
|
|
||||||
error_message.len() + 1,
|
|
||||||
buffer.len()
|
|
||||||
);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
ptr::copy_nonoverlapping(
|
|
||||||
error_message.as_ptr(),
|
|
||||||
buffer.as_mut_ptr(),
|
|
||||||
error_message.len(),
|
|
||||||
);
|
|
||||||
|
|
||||||
// Add a trailing null so people using the string as a `char *` don't
|
|
||||||
// accidentally read into garbage.
|
|
||||||
buffer[error_message.len()] = 0;
|
|
||||||
|
|
||||||
error_message.len() as c_int
|
|
||||||
}
|
|
|
@ -1,55 +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 std::ffi::CStr;
|
|
||||||
|
|
||||||
use crate::error::TypeConversionError;
|
|
||||||
|
|
||||||
pub mod errno;
|
|
||||||
|
|
||||||
impl TryFrom<crate::String> for &str {
|
|
||||||
type Error = crate::error::TypeConversionError;
|
|
||||||
|
|
||||||
fn try_from(value: crate::String) -> Result<Self, Self::Error> {
|
|
||||||
let ptr = value.0;
|
|
||||||
if ptr.is_null() {
|
|
||||||
Err(TypeConversionError::NullPointer)
|
|
||||||
} else {
|
|
||||||
// SAFETY: We checked for null, which is a huge upside other things that we simply hope
|
|
||||||
// for:
|
|
||||||
// - null terminated
|
|
||||||
// - actually be a valid pointer (`(void*) 2` is *valid* c but not a *valid* pointer)
|
|
||||||
// - be contained in a single allocated object
|
|
||||||
// - the memory must obviously not be mutated, while this &str exists
|
|
||||||
// - the null terminator must be within `isize::MAX` from the start position
|
|
||||||
let str = unsafe { CStr::from_ptr(ptr) };
|
|
||||||
str.to_str()
|
|
||||||
.map_err(|_err| crate::error::TypeConversionError::String { got: value.0 })
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl TryFrom<crate::String> for String {
|
|
||||||
type Error = crate::error::TypeConversionError;
|
|
||||||
|
|
||||||
fn try_from(value: crate::String) -> Result<Self, Self::Error> {
|
|
||||||
let str: &str = value.try_into()?;
|
|
||||||
Ok(str.to_owned())
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -25,17 +25,6 @@ edition = "2021"
|
||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
clap = { version = "4.4.11", features = ["derive"], optional = true }
|
clap = { version = "4.4.11", features = ["derive"] }
|
||||||
convert_case = "0.6.0"
|
|
||||||
thiserror = "1.0.50"
|
|
||||||
trixy = { path = "../../trixy" }
|
|
||||||
|
|
||||||
[dev-dependencies]
|
|
||||||
pretty_assertions = "1.4.0"
|
pretty_assertions = "1.4.0"
|
||||||
|
thiserror = "1.0.50"
|
||||||
[features]
|
|
||||||
build-binary = ["clap"]
|
|
||||||
|
|
||||||
[[bin]]
|
|
||||||
name = "trixy-parser"
|
|
||||||
required-features = ["build-binary"]
|
|
||||||
|
|
|
@ -2,9 +2,5 @@
|
||||||
This crate contains a parser (and lexer) for the Trixy language.
|
This crate contains a parser (and lexer) for the Trixy language.
|
||||||
The corresponding grammar is in the grammar file [here](./docs/grammar.ebnf) encoded in [Extended Backus-Naur Form](https://en.wikipedia.org/wiki/Extended_Backus%E2%80%93Naur_form).
|
The corresponding grammar is in the grammar file [here](./docs/grammar.ebnf) encoded in [Extended Backus-Naur Form](https://en.wikipedia.org/wiki/Extended_Backus%E2%80%93Naur_form).
|
||||||
|
|
||||||
## Testing
|
|
||||||
A binary (`trixy-parser`) exists, which provides easy access to the different library
|
|
||||||
parsing steps
|
|
||||||
|
|
||||||
## Docs
|
## Docs
|
||||||
Run `./generate_docs` to turn the grammar file into railroad diagrams.
|
Run `./generate_docs` to turn the grammar file into railroad diagrams.
|
||||||
|
|
|
@ -19,9 +19,3 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// an empty comment:
|
|
||||||
//
|
|
||||||
|
|
||||||
//
|
|
||||||
|
|
|
@ -1,36 +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/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
|
|
||||||
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
|
|
|
@ -1,39 +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/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
|
|
||||||
/// 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
|
|
|
@ -19,16 +19,26 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//! This module contains the already type checked types.
|
//! This module contains the already type checked types.
|
||||||
|
|
||||||
use std::fmt::{Display, Write};
|
use std::fmt::Display;
|
||||||
|
|
||||||
use crate::lexing::TokenKind;
|
use crate::lexing::TokenKind;
|
||||||
|
|
||||||
use super::unchecked;
|
use super::unchecked;
|
||||||
|
|
||||||
|
/// These are the "primitive" types used in trixy, you can use any of them to create new structures
|
||||||
|
pub const BASE_TYPES: [ConstIdentifier; 8] = [
|
||||||
|
Identifier::from("Integer"),
|
||||||
|
Identifier::from("Float"),
|
||||||
|
Identifier::from("Decimal"),
|
||||||
|
Identifier::from("String"),
|
||||||
|
Identifier::from("Function"),
|
||||||
|
Identifier::from("Option"),
|
||||||
|
Identifier::from("Result"),
|
||||||
|
Identifier::from("Vec"),
|
||||||
|
];
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord)]
|
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord)]
|
||||||
pub struct Namespace {
|
pub struct Namespace {
|
||||||
pub name: Identifier,
|
pub name: Identifier,
|
||||||
|
@ -81,33 +91,11 @@ pub struct Function {
|
||||||
pub attributes: Vec<Attribute>,
|
pub attributes: Vec<Attribute>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone)]
|
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord)]
|
||||||
pub struct Type {
|
pub struct Type {
|
||||||
pub identifier: Identifier,
|
pub identifier: Identifier,
|
||||||
pub generic_args: Vec<Type>,
|
pub generic_args: Vec<Type>,
|
||||||
}
|
}
|
||||||
impl Display for Type {
|
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
||||||
let ident = &self.identifier.name;
|
|
||||||
|
|
||||||
f.write_str(ident)?;
|
|
||||||
if !self.generic_args.is_empty() {
|
|
||||||
f.write_char('<')?;
|
|
||||||
let mut first_run = true;
|
|
||||||
for arg in &self.generic_args {
|
|
||||||
if !first_run {
|
|
||||||
f.write_str(", ")?;
|
|
||||||
} else {
|
|
||||||
first_run = false;
|
|
||||||
}
|
|
||||||
write!(f, "{}", arg)?;
|
|
||||||
}
|
|
||||||
f.write_char('>')
|
|
||||||
} else {
|
|
||||||
f.write_str("")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord)]
|
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord)]
|
||||||
pub struct NamedType {
|
pub struct NamedType {
|
||||||
|
@ -122,15 +110,6 @@ 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 {
|
||||||
|
@ -177,14 +156,6 @@ 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 {
|
||||||
|
|
|
@ -19,9 +19,5 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
pub mod checked;
|
pub mod checked;
|
||||||
pub mod unchecked;
|
pub mod unchecked;
|
||||||
|
|
||||||
pub use checked::*;
|
|
||||||
|
|
|
@ -19,8 +19,6 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//! This module contains the not type checked types.
|
//! This module contains the not type checked types.
|
||||||
//! These are generated on the first pass of the parser, to be later converted into the checked
|
//! These are generated on the first pass of the parser, to be later converted into the checked
|
||||||
//! ones.
|
//! ones.
|
||||||
|
|
|
@ -19,27 +19,21 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
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::{self},
|
parsing::checked::error::SpannedParsingError,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[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] parsing::unchecked::error::SpannedParsingError),
|
Parsing(#[from] SpannedParsingError),
|
||||||
|
|
||||||
#[error(transparent)]
|
|
||||||
Processing(#[from] parsing::checked::error::SpannedParsingError),
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The context of an Error.
|
/// The context of an Error.
|
||||||
|
|
|
@ -19,8 +19,6 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
use std::{error::Error, fmt::Display};
|
use std::{error::Error, fmt::Display};
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
|
|
||||||
|
|
|
@ -19,8 +19,6 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
use std::fmt::Display;
|
use std::fmt::Display;
|
||||||
|
|
||||||
use self::{error::SpannedLexingError, tokenizer::Tokenizer};
|
use self::{error::SpannedLexingError, tokenizer::Tokenizer};
|
||||||
|
|
|
@ -19,8 +19,6 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
use crate::lexing::{Keyword, Token, TokenKind, TokenSpan};
|
use crate::lexing::{Keyword, Token, TokenKind, TokenSpan};
|
||||||
|
|
||||||
use super::TokenStream;
|
use super::TokenStream;
|
||||||
|
|
|
@ -19,8 +19,6 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// This code is heavily inspired by: https://michael-f-bryan.github.io/static-analyser-in-rust/book/lex.html
|
// This code is heavily inspired by: https://michael-f-bryan.github.io/static-analyser-in-rust/book/lex.html
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
|
@ -150,18 +148,6 @@ impl<'a> Tokenizer<'a> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// checks if the next char in the input str is a newline
|
|
||||||
fn end_of_line(text: &str) -> bool {
|
|
||||||
let next = text.chars().next();
|
|
||||||
if let Some('\n') = next {
|
|
||||||
true
|
|
||||||
} else if let Some('\r') = next {
|
|
||||||
true
|
|
||||||
} else {
|
|
||||||
false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn tokenize_comment(text: &str) -> Result<(TokenKind, usize), LexingError> {
|
fn tokenize_comment(text: &str) -> Result<(TokenKind, usize), LexingError> {
|
||||||
// every token starts with two slashes
|
// every token starts with two slashes
|
||||||
let slashes: &str = &text[..2];
|
let slashes: &str = &text[..2];
|
||||||
|
@ -170,9 +156,6 @@ fn tokenize_comment(text: &str) -> Result<(TokenKind, usize), LexingError> {
|
||||||
} else {
|
} else {
|
||||||
let text: &str = &text[2..];
|
let text: &str = &text[2..];
|
||||||
if let Some('/') = text.chars().next() {
|
if let Some('/') = text.chars().next() {
|
||||||
if end_of_line(&text) {
|
|
||||||
Ok((TokenKind::DocComment("".to_owned()), 1 + 3))
|
|
||||||
} else {
|
|
||||||
let text = &text[1..];
|
let text = &text[1..];
|
||||||
let (doc_comment, chars_read) = take_while(text, |ch| ch != '\n' && ch != '\r')?;
|
let (doc_comment, chars_read) = take_while(text, |ch| ch != '\n' && ch != '\r')?;
|
||||||
|
|
||||||
|
@ -180,15 +163,11 @@ fn tokenize_comment(text: &str) -> Result<(TokenKind, usize), LexingError> {
|
||||||
let doc_comment = doc_comment.trim_start();
|
let doc_comment = doc_comment.trim_start();
|
||||||
let doc_comment = doc_comment.trim_end();
|
let doc_comment = doc_comment.trim_end();
|
||||||
|
|
||||||
Ok((
|
return Ok((
|
||||||
TokenKind::DocComment(doc_comment.to_owned()),
|
TokenKind::DocComment(doc_comment.to_owned()),
|
||||||
chars_read + 3,
|
chars_read + 3,
|
||||||
))
|
));
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
if end_of_line(&text) {
|
|
||||||
Ok((TokenKind::Comment("".to_owned()), 1 + 2))
|
|
||||||
} else {
|
|
||||||
let (comment, chars_read) = take_while(text, |ch| ch != '\n' && ch != '\r')?;
|
let (comment, chars_read) = take_while(text, |ch| ch != '\n' && ch != '\r')?;
|
||||||
|
|
||||||
// trim whitespace
|
// trim whitespace
|
||||||
|
@ -198,8 +177,6 @@ fn tokenize_comment(text: &str) -> Result<(TokenKind, usize), LexingError> {
|
||||||
Ok((TokenKind::Comment(comment.to_owned()), chars_read + 2))
|
Ok((TokenKind::Comment(comment.to_owned()), chars_read + 2))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn tokenize_ident(text: &str) -> Result<(TokenKind, usize), LexingError> {
|
fn tokenize_ident(text: &str) -> Result<(TokenKind, usize), LexingError> {
|
||||||
let (got, chars_read) = take_while(text, |ch| ch == '_' || ch.is_alphanumeric())?;
|
let (got, chars_read) = take_while(text, |ch| ch == '_' || ch.is_alphanumeric())?;
|
||||||
|
|
|
@ -19,25 +19,21 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
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;
|
||||||
|
|
||||||
pub mod command_spec;
|
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(Into::<TrixyError>::into)?
|
.map_err(|err| Box::new(err.into()))?
|
||||||
.parse_unchecked()
|
.parse()
|
||||||
.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)
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,11 +19,9 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
use std::{fs, process::exit};
|
use std::{fs, process::exit};
|
||||||
|
|
||||||
use trixy_lang_parser::{lexing::TokenStream, parse_trixy_lang};
|
use trixy_lang_parser::lexing::TokenStream;
|
||||||
|
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
|
|
||||||
|
@ -58,13 +56,6 @@ 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() {
|
||||||
|
@ -136,13 +127,5 @@ 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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -19,8 +19,6 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
|
|
||||||
use std::{error::Error, fmt::Display};
|
use std::{error::Error, fmt::Display};
|
||||||
|
@ -38,18 +36,6 @@ 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 {
|
||||||
|
@ -57,8 +43,6 @@ 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,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -68,8 +52,6 @@ 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(),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,18 +19,13 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
use std::mem;
|
use std::mem;
|
||||||
|
|
||||||
use convert_case::{Case, Casing};
|
|
||||||
use trixy::BASE_TYPES;
|
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
command_spec::{
|
command_spec::{
|
||||||
checked::{
|
checked::{
|
||||||
CommandSpec, DocIdentifier, DocNamedType, Enumeration, Function, Identifier, NamedType,
|
CommandSpec, DocIdentifier, DocNamedType, Enumeration, Function, Identifier, NamedType,
|
||||||
Namespace, Structure, Type,
|
Namespace, Structure, Type, BASE_TYPES,
|
||||||
},
|
},
|
||||||
unchecked::{
|
unchecked::{
|
||||||
CommandSpec as UncheckedCommandSpec, DocNamedType as UncheckedDocNamedType,
|
CommandSpec as UncheckedCommandSpec, DocNamedType as UncheckedDocNamedType,
|
||||||
|
@ -40,7 +35,7 @@ use crate::{
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
error::ErrorContext,
|
error::ErrorContext,
|
||||||
lexing::{TokenKind, TokenSpan},
|
lexing::{TokenKind, TokenStream},
|
||||||
};
|
};
|
||||||
|
|
||||||
use self::error::{ParsingError, SpannedParsingError};
|
use self::error::{ParsingError, SpannedParsingError};
|
||||||
|
@ -56,6 +51,29 @@ 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 {
|
||||||
|
@ -93,7 +111,6 @@ 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
|
||||||
|
@ -106,7 +123,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, &name, namespace_span)?);
|
enumerations.push(self.process_enumeration(enumeration)?);
|
||||||
enumerations_counter += 1;
|
enumerations_counter += 1;
|
||||||
}
|
}
|
||||||
let mut structures = vec![];
|
let mut structures = vec![];
|
||||||
|
@ -169,27 +186,10 @@ 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 enum_span = enumeration.identifier.span;
|
let identifier = mem::take(&mut enumeration.identifier.kind).into();
|
||||||
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 {
|
||||||
|
@ -262,7 +262,7 @@ impl Parser {
|
||||||
.iter()
|
.iter()
|
||||||
.map(|r#enum| Into::<Identifier>::into(r#enum.identifier.kind.clone()))
|
.map(|r#enum| Into::<Identifier>::into(r#enum.identifier.kind.clone()))
|
||||||
.any(|ident| ident == identifier)
|
.any(|ident| ident == identifier)
|
||||||
&& !BASE_TYPES.iter().any(|ident| ident == &identifier.name)
|
&& !BASE_TYPES.iter().any(|ident| ident.name == identifier.name)
|
||||||
{
|
{
|
||||||
return Err(ParsingError::TypeNotDeclared {
|
return Err(ParsingError::TypeNotDeclared {
|
||||||
r#type: identifier,
|
r#type: identifier,
|
||||||
|
|
|
@ -19,8 +19,6 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
use crate::command_spec::checked::{
|
use crate::command_spec::checked::{
|
||||||
Attribute, CommandSpec, DocIdentifier, DocNamedType, Enumeration, Function, Identifier,
|
Attribute, CommandSpec, DocIdentifier, DocNamedType, Enumeration, Function, Identifier,
|
||||||
NamedType, Namespace, Structure, Type,
|
NamedType, Namespace, Structure, Type,
|
||||||
|
|
|
@ -19,7 +19,5 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
pub mod checked;
|
pub mod checked;
|
||||||
pub mod unchecked;
|
mod unchecked;
|
||||||
|
|
|
@ -19,8 +19,6 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
use std::{error::Error, fmt::Display};
|
use std::{error::Error, fmt::Display};
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
|
|
||||||
|
|
|
@ -19,8 +19,6 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
use std::mem;
|
use std::mem;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
|
@ -256,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]) || self.expect_peek(token![DocComment]) {
|
if self.expect_peek(token![Ident]) {
|
||||||
contents.push(self.parse_doc_named_type()?);
|
contents.push(self.parse_doc_named_type()?);
|
||||||
} else {
|
} else {
|
||||||
break;
|
break;
|
||||||
|
|
|
@ -19,8 +19,6 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
use pretty_assertions::assert_eq;
|
use pretty_assertions::assert_eq;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
|
|
|
@ -1,6 +0,0 @@
|
||||||
# build
|
|
||||||
/target
|
|
||||||
/result
|
|
||||||
|
|
||||||
# This crate is a library
|
|
||||||
Cargo.lock
|
|
|
@ -1,32 +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/>.
|
|
||||||
|
|
||||||
|
|
||||||
[package]
|
|
||||||
name = "trixy-macros"
|
|
||||||
version = "0.1.0"
|
|
||||||
edition = "2021"
|
|
||||||
|
|
||||||
[dependencies]
|
|
||||||
convert_case = "0.6.0"
|
|
||||||
prettyplease = "0.2.15"
|
|
||||||
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" }
|
|
||||||
trixy = { path = "../../trixy" }
|
|
|
@ -1,7 +0,0 @@
|
||||||
# build
|
|
||||||
/target
|
|
||||||
/result
|
|
||||||
/dist
|
|
||||||
|
|
||||||
# This crate is a library
|
|
||||||
Cargo.lock
|
|
|
@ -1,35 +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/>.
|
|
||||||
|
|
||||||
|
|
||||||
[package]
|
|
||||||
name = "main-example"
|
|
||||||
version = "0.0.0"
|
|
||||||
publish = false
|
|
||||||
edition = "2021"
|
|
||||||
|
|
||||||
[lib]
|
|
||||||
crate-type = ["cdylib"]
|
|
||||||
|
|
||||||
[dependencies]
|
|
||||||
trixy = { path = "../../../../trixy" }
|
|
||||||
env_logger = { version = "0.10.1" }
|
|
||||||
log = "0.4.20"
|
|
||||||
|
|
||||||
[build-dependencies]
|
|
||||||
trixy-macros = {path = "../../../trixy-macros"}
|
|
|
@ -1,31 +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 trixy_macros::config::TrixyConfig;
|
|
||||||
|
|
||||||
fn main() {
|
|
||||||
println!("cargo:rerun-if-changed=./src/api.tri");
|
|
||||||
TrixyConfig::new("callback")
|
|
||||||
.trixy_path("./src/api.tri")
|
|
||||||
.dist_dir_path("./dist")
|
|
||||||
.generate_debug(true)
|
|
||||||
.generate();
|
|
||||||
}
|
|
|
@ -1,50 +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/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
|
|
||||||
#include "../dist/interface.h"
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
int two;
|
|
||||||
} a_t;
|
|
||||||
|
|
||||||
int main(void) {
|
|
||||||
fn_alone("hi");
|
|
||||||
|
|
||||||
if (!fn_alone(0x0)) {
|
|
||||||
int error_length = last_error_length();
|
|
||||||
char *error = malloc(error_length);
|
|
||||||
last_error_message(error, error_length);
|
|
||||||
printf("Encountered error: %s\n", error);
|
|
||||||
free(error);
|
|
||||||
}
|
|
||||||
|
|
||||||
one.fn_one();
|
|
||||||
// one.two.fn_two();
|
|
||||||
//
|
|
||||||
// two_t two = one.two;
|
|
||||||
// two.three.fn_three();
|
|
||||||
// one.two.three.fn_three();
|
|
||||||
// one_two_fn_two();
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
|
@ -1,54 +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/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
|
|
||||||
// nasp trinitrix {
|
|
||||||
// struct Callback {
|
|
||||||
// func: String,
|
|
||||||
// timeout: String,
|
|
||||||
// };
|
|
||||||
//
|
|
||||||
// enum CallbackPriority {
|
|
||||||
// High,
|
|
||||||
// Medium,
|
|
||||||
// Low,
|
|
||||||
// };
|
|
||||||
//
|
|
||||||
// fn execute_callback(callback: Callback, priority: CallbackPriority);
|
|
||||||
// }
|
|
||||||
|
|
||||||
/// Some doc comment
|
|
||||||
fn fn_alone(input: String);
|
|
||||||
/// Some doc comment
|
|
||||||
nasp one {
|
|
||||||
/// Some doc comment
|
|
||||||
fn fn_one();
|
|
||||||
nasp two {
|
|
||||||
fn fn_two();
|
|
||||||
nasp three {
|
|
||||||
fn fn_three();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// That's a flat out lie, but it results in a rather nice syntax highlight compared to nothing:
|
|
||||||
// vim: syntax=rust
|
|
|
@ -1,30 +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/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
macro_rules! callback {
|
|
||||||
($cmd:expr) => {{
|
|
||||||
println!("{:#?}", $cmd);
|
|
||||||
}};
|
|
||||||
}
|
|
||||||
|
|
||||||
include!(concat!(env!("OUT_DIR"), "/api.rs"));
|
|
|
@ -1,25 +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/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
|
|
||||||
fn main() {
|
|
||||||
let input = include_str!(concat!(env!("OUT_DIR"), "/interface.h"));
|
|
||||||
println!("{}", input);
|
|
||||||
}
|
|
|
@ -1,136 +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/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//! 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};
|
|
||||||
//!# 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);
|
|
||||||
//!# }
|
|
||||||
//! ```
|
|
||||||
|
|
||||||
use std::path::{Path, PathBuf};
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub enum Language {
|
|
||||||
Rust,
|
|
||||||
C,
|
|
||||||
Lua,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Default, Debug)]
|
|
||||||
pub struct TrixyConfig {
|
|
||||||
/// 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 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
|
|
||||||
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,
|
|
||||||
|
|
||||||
/// 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
|
|
||||||
pub callback_function: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl TrixyConfig {
|
|
||||||
pub fn new<T: Into<String>>(callback_function: T) -> Self {
|
|
||||||
Self {
|
|
||||||
callback_function: callback_function.into(),
|
|
||||||
host_code_name: "api.rs".into(),
|
|
||||||
c_header_name: "interface.h".into(),
|
|
||||||
..Default::default()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn trixy_path<T: Into<PathBuf>>(self, input_path: T) -> Self {
|
|
||||||
Self {
|
|
||||||
trixy_path: input_path.into(),
|
|
||||||
..self
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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()),
|
|
||||||
..self
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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 {
|
|
||||||
Self {
|
|
||||||
host_code_name: output_path.into(),
|
|
||||||
..self
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn c_header_name<T: Into<PathBuf>>(self, output_path: T) -> Self {
|
|
||||||
Self {
|
|
||||||
c_header_name: output_path.into(),
|
|
||||||
..self
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn callback_function<T: Into<String>>(self, callback_function: T) -> Self {
|
|
||||||
Self {
|
|
||||||
callback_function: callback_function.into(),
|
|
||||||
..self
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,293 +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/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
|
|
||||||
//! This module generates the c header
|
|
||||||
//! It works by firstly listing the functions and then by grouping them into structures, effectively
|
|
||||||
//! simulating namespaces in c.
|
|
||||||
|
|
||||||
use proc_macro2::{Ident, Punct, Spacing, TokenStream as TokenStream2};
|
|
||||||
use quote::{format_ident, quote};
|
|
||||||
use trixy_lang_parser::command_spec::{
|
|
||||||
Attribute, CommandSpec, Function, Identifier, NamedType, Namespace,
|
|
||||||
};
|
|
||||||
|
|
||||||
use crate::{
|
|
||||||
config::TrixyConfig,
|
|
||||||
generate::{c_api::mangle_c_function_ident, identifier_to_rust, type_to_rust},
|
|
||||||
};
|
|
||||||
|
|
||||||
const BEGIN_HEADER_GUARD: &'static str = r"#ifndef TRIXY_MAIN_HEADER
|
|
||||||
#define TRIXY_MAIN_HEADER";
|
|
||||||
const END_HEADER_GUARD: &'static str = r"#endif // ifndef TRIXY_MAIN_HEADER";
|
|
||||||
|
|
||||||
/// This function acts as the core transformative aspect, turning this trixy code into the
|
|
||||||
/// following c header:
|
|
||||||
///
|
|
||||||
/// *Trixy:*
|
|
||||||
/// ```text
|
|
||||||
/// fn fn_alone();
|
|
||||||
/// nasp one {
|
|
||||||
/// fn fn_one();
|
|
||||||
/// nasp two {
|
|
||||||
/// fn fn_two();
|
|
||||||
/// nasp three {
|
|
||||||
/// fn fn_three();
|
|
||||||
/// }
|
|
||||||
/// }
|
|
||||||
/// }
|
|
||||||
/// ```
|
|
||||||
/// *C header:*
|
|
||||||
/// ```text
|
|
||||||
/// #ifndef TRIXY_MAIN_HEADER
|
|
||||||
/// #define TRIXY_MAIN_HEADER
|
|
||||||
///
|
|
||||||
/// extern int fn_alone();
|
|
||||||
/// extern int one_fn_one();
|
|
||||||
/// extern int one_two_fn_two();
|
|
||||||
/// extern int one_two_three_fn_three();
|
|
||||||
///
|
|
||||||
/// typedef struct {
|
|
||||||
/// void (*fn_three)(void);
|
|
||||||
/// } three_t;
|
|
||||||
/// typedef struct {
|
|
||||||
/// void (*fn_two)(void);
|
|
||||||
/// three_t three;
|
|
||||||
/// } two_t;
|
|
||||||
/// typedef struct {
|
|
||||||
/// void (*fn_one)(void);
|
|
||||||
/// two_t two;
|
|
||||||
/// } one_t;
|
|
||||||
///
|
|
||||||
/// const three_t three = {
|
|
||||||
/// .fn_three = one_two_three_fn_three,
|
|
||||||
/// };
|
|
||||||
/// const two_t two = {
|
|
||||||
/// .fn_two = one_two_fn_two,
|
|
||||||
/// .three = three,
|
|
||||||
/// };
|
|
||||||
/// const one_t one = {
|
|
||||||
/// .fn_one = one_fn_one,
|
|
||||||
/// .two = two,
|
|
||||||
/// };
|
|
||||||
/// #endif // ifndef TRIXY_MAIN_HEADER
|
|
||||||
/// ```
|
|
||||||
pub fn generate(trixy: &CommandSpec, _config: &TrixyConfig) -> 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 type_defs: String = trixy
|
|
||||||
.namespaces
|
|
||||||
.iter()
|
|
||||||
.rev()
|
|
||||||
.map(|nasp| namespace_to_full_typedef(nasp))
|
|
||||||
.collect::<Vec<String>>()
|
|
||||||
.join("\n");
|
|
||||||
|
|
||||||
let struct_initializer: TokenStream2 = trixy
|
|
||||||
.namespaces
|
|
||||||
.iter()
|
|
||||||
.map(|nasp| namespace_to_full_struct_init(nasp, &vec![]))
|
|
||||||
.collect();
|
|
||||||
|
|
||||||
let output = quote! {
|
|
||||||
#struct_initializer
|
|
||||||
}
|
|
||||||
.to_string();
|
|
||||||
format!(
|
|
||||||
"{}\n\n{}\n\n{}\n{}\n{}\n{}\n\n{}",
|
|
||||||
BEGIN_HEADER_GUARD,
|
|
||||||
trixy::traits::errno::ERROR_FUNCTIONS,
|
|
||||||
functions,
|
|
||||||
namespaces,
|
|
||||||
type_defs,
|
|
||||||
output,
|
|
||||||
END_HEADER_GUARD
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
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_ident(function, namespaces);
|
|
||||||
let inputs: Vec<TokenStream2> = function.inputs.iter().map(named_type_to_c).collect();
|
|
||||||
|
|
||||||
let output = quote! {
|
|
||||||
extern int #ident(#(#inputs),*);
|
|
||||||
};
|
|
||||||
format!("{}{}\n", doc_comments, output)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn attribute_to_doc_comment(attribute: &Attribute) -> String {
|
|
||||||
let Attribute::doc(doc_comment) = attribute;
|
|
||||||
format!("/// {}\n", doc_comment)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn named_type_to_c(named_type: &NamedType) -> TokenStream2 {
|
|
||||||
let ident = identifier_to_rust(&named_type.name);
|
|
||||||
let r#type = type_to_rust(&named_type.r#type);
|
|
||||||
let c_type = trixy::to_c_name(r#type.to_string());
|
|
||||||
quote! {
|
|
||||||
#c_type #ident
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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!("{}_t", 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 #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_ident(function, namespaces);
|
|
||||||
|
|
||||||
quote! {
|
|
||||||
. #ident = #full_ident,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn namespace_to_struct_init(namespace: &Namespace) -> TokenStream2 {
|
|
||||||
let ident = identifier_to_rust(&namespace.name);
|
|
||||||
|
|
||||||
quote! {
|
|
||||||
. #ident = #ident ,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn namespace_to_full_typedef(nasp: &Namespace) -> String {
|
|
||||||
let ident = format_ident!("{}_t", 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! {
|
|
||||||
typedef struct {
|
|
||||||
#functions
|
|
||||||
#namespaces
|
|
||||||
} #ident;
|
|
||||||
};
|
|
||||||
format! {"{}\n{}{}\n", next_namespace, doc_comments, namespace}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn function_to_typedef(function: &Function) -> TokenStream2 {
|
|
||||||
let ident = identifier_to_rust(&function.identifier);
|
|
||||||
|
|
||||||
let output = if let Some(output) = &function.output {
|
|
||||||
let output = output.to_string();
|
|
||||||
quote! { #output, }
|
|
||||||
} else {
|
|
||||||
TokenStream2::default()
|
|
||||||
};
|
|
||||||
|
|
||||||
let inputs: Vec<TokenStream2> = if function.inputs.is_empty() {
|
|
||||||
vec![quote! { void }]
|
|
||||||
} else {
|
|
||||||
todo!()
|
|
||||||
};
|
|
||||||
|
|
||||||
quote! {
|
|
||||||
int (* #ident ) (#output #(#inputs),*);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn namespace_to_typedef(namespace: &Namespace) -> TokenStream2 {
|
|
||||||
let ident = identifier_to_rust(&namespace.name);
|
|
||||||
let type_ident = format_ident!("{}_t", ident.to_string());
|
|
||||||
|
|
||||||
quote! {
|
|
||||||
#type_ident #ident ;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn namespace_to_header(nasp: &Namespace, namespaces: &Vec<&Identifier>) -> String {
|
|
||||||
let mut nasps = namespaces.clone();
|
|
||||||
nasps.push(&nasp.name);
|
|
||||||
|
|
||||||
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{}", functions, namespaces}
|
|
||||||
}
|
|
|
@ -1,291 +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 convert_case::{Case, Casing};
|
|
||||||
use proc_macro2::TokenStream as TokenStream2;
|
|
||||||
use quote::{format_ident, quote};
|
|
||||||
use trixy_lang_parser::command_spec::{CommandSpec, Function, Identifier, NamedType, Namespace};
|
|
||||||
|
|
||||||
use crate::{
|
|
||||||
config::TrixyConfig,
|
|
||||||
generate::{
|
|
||||||
c_api::mangle_c_function_ident, identifier_to_rust, named_type_to_rust, type_to_rust,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
/// 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();
|
|
||||||
quote! {
|
|
||||||
#functions
|
|
||||||
#namespaced_functions
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn namespace_to_c(
|
|
||||||
namespace: &Namespace,
|
|
||||||
config: &TrixyConfig,
|
|
||||||
namespaces: &Vec<&Identifier>,
|
|
||||||
) -> TokenStream2 {
|
|
||||||
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();
|
|
||||||
quote! {
|
|
||||||
#functions
|
|
||||||
#additional_functions
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn function_to_c(
|
|
||||||
function: &Function,
|
|
||||||
config: &TrixyConfig,
|
|
||||||
namespaces: &Vec<&Identifier>,
|
|
||||||
) -> TokenStream2 {
|
|
||||||
let ident = mangle_c_function_ident(function, namespaces);
|
|
||||||
let inputs: Vec<TokenStream2> = function
|
|
||||||
.inputs
|
|
||||||
.iter()
|
|
||||||
.map(named_type_to_rust_trixy)
|
|
||||||
.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 = type_to_rust(&r#type);
|
|
||||||
quote! {
|
|
||||||
#[no_mangle]
|
|
||||||
pub unsafe extern "C" fn #ident(output: *mut #output, #(#inputs),*) -> core::ffi::c_int {
|
|
||||||
#callback_function ! (#command_value)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} 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_rust(&named_type.r#type);
|
|
||||||
quote! {
|
|
||||||
#ident : trixy:: #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 = {
|
|
||||||
let ident = format_ident!("{}", function.identifier.name);
|
|
||||||
if function.inputs.is_empty() {
|
|
||||||
quote! {
|
|
||||||
#ident
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
let inputs: Vec<TokenStream2> = function
|
|
||||||
.inputs
|
|
||||||
.iter()
|
|
||||||
.map(named_type_to_rust_assignment)
|
|
||||||
.collect();
|
|
||||||
quote! {
|
|
||||||
#ident { #(#inputs),* }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
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 = nasps_to_path(namespaces);
|
|
||||||
|
|
||||||
let function_call = if !function.inputs.is_empty() {
|
|
||||||
let inputs: Vec<TokenStream2> =
|
|
||||||
function.inputs.iter().map(named_type_to_rust).collect();
|
|
||||||
quote! {
|
|
||||||
#namespace_path :: #nasp_pascal_ident :: #function_ident { #(#inputs),* }
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
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 : trixy::convert!(#ident)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// 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 = nasps_to_path(namespaces_to_do);
|
|
||||||
quote! {
|
|
||||||
#namespace_path :: #ident_pascal_next :: #ident_pascal ( #input )
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn nasps_to_path(namespaces: &[&Identifier]) -> TokenStream2 {
|
|
||||||
namespaces
|
|
||||||
.iter()
|
|
||||||
.fold(TokenStream2::default(), |acc, nasp| {
|
|
||||||
let ident = format_ident!("{}", nasp.name);
|
|
||||||
if acc.is_empty() {
|
|
||||||
quote! {
|
|
||||||
#ident
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
quote! {
|
|
||||||
#acc :: #ident
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
|
@ -1,43 +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 proc_macro2::Ident;
|
|
||||||
use quote::format_ident;
|
|
||||||
use trixy_lang_parser::command_spec::{Function, Identifier};
|
|
||||||
|
|
||||||
pub mod header;
|
|
||||||
pub mod host;
|
|
||||||
|
|
||||||
pub fn mangle_c_function_ident(function: &Function, 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!("{}", &function.identifier.name)
|
|
||||||
} else {
|
|
||||||
format_ident!("{}_{}", namespace_str, &function.identifier.name)
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,294 +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/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//! 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, Namespace,
|
|
||||||
Structure,
|
|
||||||
};
|
|
||||||
|
|
||||||
use crate::{
|
|
||||||
config::TrixyConfig,
|
|
||||||
generate::{identifier_to_rust, named_type_to_rust},
|
|
||||||
};
|
|
||||||
|
|
||||||
thread_local! {static DEBUG: OnceCell<TokenStream2> = OnceCell::new();}
|
|
||||||
|
|
||||||
/// This function turns, for example, the following trixy input into this rust code:
|
|
||||||
/// ```text
|
|
||||||
/// nasp trinitrix {
|
|
||||||
/// struct Callback {
|
|
||||||
/// func: String,
|
|
||||||
/// timeout: String,
|
|
||||||
/// };
|
|
||||||
///
|
|
||||||
/// enum CallbackPriority {
|
|
||||||
/// High,
|
|
||||||
/// Medium,
|
|
||||||
/// Low,
|
|
||||||
/// };
|
|
||||||
///
|
|
||||||
/// fn execute_callback(callback: Callback, priority: CallbackPriority);
|
|
||||||
/// }
|
|
||||||
/// // wrong but helps with syntax highlight:
|
|
||||||
/// // vim: syntax=rust
|
|
||||||
/// ```
|
|
||||||
/// ```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 },
|
|
||||||
/// }
|
|
||||||
/// }
|
|
||||||
/// ```
|
|
||||||
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();
|
|
||||||
|
|
||||||
if inputs.is_empty() {
|
|
||||||
quote! {
|
|
||||||
#doc_comments
|
|
||||||
#[allow(non_camel_case_types)]
|
|
||||||
#ident
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
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_types)]
|
|
||||||
#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_types)]
|
|
||||||
#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 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
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,72 +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 proc_macro2::TokenStream as TokenStream2;
|
|
||||||
use quote::{format_ident, quote};
|
|
||||||
use trixy_lang_parser::command_spec::{CommandSpec, Identifier, NamedType, Type};
|
|
||||||
|
|
||||||
use crate::config::TrixyConfig;
|
|
||||||
|
|
||||||
pub mod c_api;
|
|
||||||
pub mod host;
|
|
||||||
|
|
||||||
pub fn generate(trixy: &CommandSpec, config: &TrixyConfig) -> TokenStream2 {
|
|
||||||
// Build the language wrappers
|
|
||||||
let c_host_api = c_api::host::generate(&trixy, &config);
|
|
||||||
|
|
||||||
// Build the final rust hosting code
|
|
||||||
let host_rust_code = host::generate(&trixy, &config);
|
|
||||||
|
|
||||||
quote! {
|
|
||||||
#host_rust_code
|
|
||||||
/* C api */
|
|
||||||
#c_host_api
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn identifier_to_rust(identifier: &Identifier) -> TokenStream2 {
|
|
||||||
let ident = format_ident!("{}", &identifier.name);
|
|
||||||
quote! {
|
|
||||||
#ident
|
|
||||||
}
|
|
||||||
}
|
|
||||||
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);
|
|
||||||
if r#type.generic_args.is_empty() {
|
|
||||||
quote! {
|
|
||||||
#ident
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
let generics: Vec<TokenStream2> = r#type.generic_args.iter().map(type_to_rust).collect();
|
|
||||||
quote! {
|
|
||||||
#ident <#(#generics),*>
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,108 +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 std::{env, fs, io::Write, path::PathBuf, process::Command};
|
|
||||||
|
|
||||||
use trixy_lang_parser::parse_trixy_lang;
|
|
||||||
|
|
||||||
use crate::config::TrixyConfig;
|
|
||||||
|
|
||||||
pub mod config;
|
|
||||||
mod generate;
|
|
||||||
|
|
||||||
const VIM_LINE_RUST: &'static str = "// vim: filetype=rust\n";
|
|
||||||
const VIM_LINE_C: &'static str = "// vim: filetype=c\n";
|
|
||||||
|
|
||||||
impl TrixyConfig {
|
|
||||||
/// This is the heart of Trixy
|
|
||||||
/// It mainly does one thing:
|
|
||||||
/// - Generate a tree of modules from the input trixy file
|
|
||||||
///
|
|
||||||
pub fn generate(&self) {
|
|
||||||
let source_code = fs::read_to_string(&self.trixy_path).unwrap_or_else(|err| {
|
|
||||||
panic! {"Can't read file at path: '{}'. The Error is: '{}'",
|
|
||||||
self.trixy_path.display(), err};
|
|
||||||
});
|
|
||||||
|
|
||||||
let trixy_code = parse_trixy_lang(&source_code).unwrap_or_else(|err| {
|
|
||||||
panic! {"Parsing of the trixy file failed: \n{}", err}
|
|
||||||
});
|
|
||||||
|
|
||||||
// host code
|
|
||||||
let host_code = prettyplease::unparse(
|
|
||||||
&syn::parse2(generate::generate(&trixy_code, &self))
|
|
||||||
.expect("This code was generated, it should also be parsable"),
|
|
||||||
);
|
|
||||||
let mut host_code_out = fs::File::create(PathBuf::from(format!(
|
|
||||||
"{}/{}",
|
|
||||||
env::var("OUT_DIR").expect("The build script should have this define"),
|
|
||||||
&self.host_code_name.display()
|
|
||||||
)))
|
|
||||||
.expect("This file should always be free to use");
|
|
||||||
write!(host_code_out, "{}\n{}", host_code, VIM_LINE_RUST).expect("Write should work");
|
|
||||||
|
|
||||||
// c header
|
|
||||||
let c_header = generate::c_api::header::generate(&trixy_code, &self);
|
|
||||||
let c_header_path = PathBuf::from(format!(
|
|
||||||
"{}/{}",
|
|
||||||
env::var("OUT_DIR").expect("The build script should have this define"),
|
|
||||||
&self.c_header_name.display()
|
|
||||||
));
|
|
||||||
let mut c_header_out =
|
|
||||||
fs::File::create(&c_header_path).expect("This file should always be free to use");
|
|
||||||
write!(c_header_out, "{}\n{}", c_header, VIM_LINE_C).expect("Write should work");
|
|
||||||
|
|
||||||
Command::new("clang-format")
|
|
||||||
.args(["-i", &c_header_path.to_str().unwrap()])
|
|
||||||
.status()
|
|
||||||
.unwrap_or_else(|err| {
|
|
||||||
panic!(
|
|
||||||
"Failed to format the c header file with `clang-format`; Error: `{}`",
|
|
||||||
err
|
|
||||||
)
|
|
||||||
});
|
|
||||||
|
|
||||||
if let Some(dist_dir) = &self.dist_dir_path {
|
|
||||||
if !dist_dir.is_dir() {
|
|
||||||
fs::create_dir(dist_dir).unwrap_or_else(|err| {
|
|
||||||
panic! {
|
|
||||||
"Failed to create the dist directory ('{}') because of: `{}`",
|
|
||||||
dist_dir.display(), err}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
if self.check_dist_dir {
|
|
||||||
if dist_dir.read_dir().iter().count() != 1 {
|
|
||||||
panic!("Your specified dist dir already has something in it! Set `check_dist_dir` to `false` to override this check");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
let c_header_dist = PathBuf::from(format!(
|
|
||||||
"{}/{}",
|
|
||||||
dist_dir.display(),
|
|
||||||
self.c_header_name.display()
|
|
||||||
));
|
|
||||||
fs::copy(c_header_path, c_header_dist).unwrap_or_else(
|
|
||||||
|err| panic! {"Failed to copy the c header to the dist dir because of: `{}`", err},
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
Reference in New Issue