Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Rework internal TokenStream representation #1699

Open
wants to merge 1 commit into
base: dev
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 2 additions & 8 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

34 changes: 21 additions & 13 deletions plugins/cairo-lang-macro-stable/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,30 @@
use crate::ffi::{StableOption, StableSlice};
use std::ffi::CStr;
use std::num::NonZeroU8;
use std::os::raw::c_char;
use std::ptr::NonNull;

pub mod ffi;

#[repr(C)]
#[derive(Debug)]
pub struct StableToken {
pub span: StableTextSpan,
pub content: *mut c_char,
}

#[repr(C)]
#[derive(Debug)]
pub struct StableTextSpan {
pub start: usize,
pub end: usize,
}

#[repr(C)]
#[derive(Debug)]
pub enum StableTokenTree {
Ident(StableToken),
}

#[repr(C)]
#[derive(Debug)]
pub struct StableExpansion {
Expand All @@ -23,7 +42,7 @@ pub type StableExpansionsList = StableSlice<StableExpansion>;
#[repr(C)]
#[derive(Debug)]
pub struct StableTokenStream {
pub value: *mut c_char,
pub tokens: StableSlice<StableTokenTree>,
pub metadata: StableTokenStreamMetadata,
}

Expand Down Expand Up @@ -76,17 +95,6 @@ pub struct StableResultWrapper {
pub output: StableProcMacroResult,
}

impl StableTokenStream {
/// Convert to String.
///
/// # Safety
pub unsafe fn to_string(&self) -> String {
// Note that this does not deallocate the c-string.
// The memory must still be freed with `CString::from_raw`.
CStr::from_ptr(self.value).to_string_lossy().to_string()
}
}

#[repr(C)]
pub struct StablePostProcessContext {
pub aux_data: StableSlice<StableAuxData>,
Expand Down
2 changes: 1 addition & 1 deletion plugins/cairo-lang-macro/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ repository.workspace = true

[dependencies]
cairo-lang-macro-attributes = "0.1"
cairo-lang-macro-stable = "1"
cairo-lang-macro-stable = { path = "../cairo-lang-macro-stable" }
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just a note to change it during the release

linkme.workspace = true
serde = { workspace = true, optional = true }

Expand Down
129 changes: 122 additions & 7 deletions plugins/cairo-lang-macro/src/types/conversion.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
use crate::{
AuxData, Diagnostic, ExpansionDefinition, FullPathMarker, PostProcessContext, ProcMacroResult,
Severity, TokenStream, TokenStreamMetadata,
Severity, TextSpan, Token, TokenStream, TokenStreamMetadata, TokenTree,
};
use cairo_lang_macro_stable::ffi::StableSlice;
use cairo_lang_macro_stable::{
StableAuxData, StableDiagnostic, StableExpansion, StableFullPathMarker,
StablePostProcessContext, StableProcMacroResult, StableSeverity, StableTokenStream,
StableTokenStreamMetadata,
StablePostProcessContext, StableProcMacroResult, StableSeverity, StableTextSpan, StableToken,
StableTokenStream, StableTokenStreamMetadata, StableTokenTree,
};
use std::ffi::{c_char, CStr, CString};
use std::num::NonZeroU8;
Expand Down Expand Up @@ -90,15 +90,120 @@ impl ProcMacroResult {
}
}

impl TextSpan {
/// Convert to FFI-safe representation.
#[doc(hidden)]
pub fn into_stable(self) -> StableTextSpan {
StableTextSpan {
start: self.start,
end: self.end,
}
}

#[doc(hidden)]
pub fn from_stable(span: &StableTextSpan) -> Self {
Self {
start: span.start,
end: span.end,
}
}

#[doc(hidden)]
pub fn from_owned_stable(span: StableTextSpan) -> Self {
Self {
start: span.start,
end: span.end,
}
}
}

impl Token {
/// Convert to FFI-safe representation.
#[doc(hidden)]
pub fn into_stable(self) -> StableToken {
let cstr = CString::new(self.content.as_bytes()).unwrap();
StableToken {
span: self.span.into_stable(),
content: cstr.into_raw(),
}
}

/// Convert to native Rust representation, without taking the ownership of the string.
///
/// Note that you still need to free the memory by calling `from_owned_stable`.
///
/// # Safety
#[doc(hidden)]
pub unsafe fn from_stable(token: &StableToken) -> Self {
Self {
content: from_raw_cstr(token.content),
span: TextSpan::from_stable(&token.span),
}
}

/// Convert to native Rust representation, with taking the ownership of the string.
///
/// Useful when you need to free the allocated memory.
/// Only use on the same side of FFI-barrier, where the memory has been allocated.
///
/// # Safety
#[doc(hidden)]
pub unsafe fn from_owned_stable(token: StableToken) -> Self {
Self {
content: from_raw_cstring(token.content),
span: TextSpan::from_owned_stable(token.span),
}
}
}

impl TokenTree {
/// Convert to FFI-safe representation.
#[doc(hidden)]
pub fn into_stable(self) -> StableTokenTree {
match self {
Self::Ident(token) => StableTokenTree::Ident(token.into_stable()),
}
}

/// Convert to native Rust representation, without taking the ownership of the string.
///
/// Note that you still need to free the memory by calling `from_owned_stable`.
///
/// # Safety
#[doc(hidden)]
pub unsafe fn from_stable(token_tree: &StableTokenTree) -> Self {
match token_tree {
StableTokenTree::Ident(token) => Self::Ident(Token::from_stable(token)),
}
}

/// Convert to native Rust representation, with taking the ownership of the string.
///
/// Useful when you need to free the allocated memory.
/// Only use on the same side of FFI-barrier, where the memory has been allocated.
///
/// # Safety
#[doc(hidden)]
pub unsafe fn from_owned_stable(token_tree: StableTokenTree) -> Self {
match token_tree {
StableTokenTree::Ident(token) => Self::Ident(Token::from_owned_stable(token)),
}
}
}

impl TokenStream {
/// Convert to FFI-safe representation.
///
/// # Safety
#[doc(hidden)]
pub fn into_stable(self) -> StableTokenStream {
let cstr = CString::new(self.value).unwrap();
let tokens = self
.tokens
.into_iter()
.map(|token| token.into_stable())
.collect::<Vec<_>>();
StableTokenStream {
value: cstr.into_raw(),
tokens: StableSlice::new(tokens),
metadata: self.metadata.into_stable(),
}
}
Expand All @@ -110,8 +215,13 @@ impl TokenStream {
/// # Safety
#[doc(hidden)]
pub unsafe fn from_stable(token_stream: &StableTokenStream) -> Self {
let (ptr, n) = token_stream.tokens.raw_parts();
let tokens = slice::from_raw_parts(ptr, n)
.iter()
.map(|token_tree| TokenTree::from_stable(token_tree))
.collect::<Vec<_>>();
Self {
value: from_raw_cstr(token_stream.value),
tokens,
metadata: TokenStreamMetadata::from_stable(&token_stream.metadata),
}
}
Expand All @@ -124,8 +234,13 @@ impl TokenStream {
/// # Safety
#[doc(hidden)]
pub unsafe fn from_owned_stable(token_stream: StableTokenStream) -> Self {
let tokens = token_stream.tokens.into_owned();
let tokens = tokens
.into_iter()
.map(|token_tree| TokenTree::from_owned_stable(token_tree))
.collect::<Vec<_>>();
Self {
value: from_raw_cstring(token_stream.value),
tokens,
metadata: TokenStreamMetadata::from_owned_stable(token_stream.metadata),
}
}
Expand Down
92 changes: 13 additions & 79 deletions plugins/cairo-lang-macro/src/types/mod.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
use std::fmt::Display;
use std::vec::IntoIter;

mod conversion;
mod expansions;
mod token;

pub use expansions::*;
pub use token::*;

/// Result of procedural macro code generation.
#[derive(Debug, Clone)]
Expand All @@ -15,78 +16,6 @@ pub struct ProcMacroResult {
pub full_path_markers: Vec<String>,
}

/// An abstract stream of Cairo tokens.
///
/// This is both input and part of an output of a procedural macro.
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[derive(Debug, Default, Clone, PartialEq, Eq, Hash)]
pub struct TokenStream {
value: String,
metadata: TokenStreamMetadata,
}

/// Metadata of [`TokenStream`].
///
/// This struct can be used to describe the origin of the [`TokenStream`].
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[derive(Debug, Default, Clone, PartialEq, Eq, Hash)]
pub struct TokenStreamMetadata {
/// The path to the file from which the [`TokenStream`] has been created.
pub original_file_path: Option<String>,
/// ID of the file from which the [`TokenStream`] has been created.
///
/// It is guaranteed, that the `file_id` will be unique for each file.
pub file_id: Option<String>,
}

impl TokenStream {
#[doc(hidden)]
pub fn new(value: String) -> Self {
Self {
value,
metadata: TokenStreamMetadata::default(),
}
}

#[doc(hidden)]
pub fn empty() -> Self {
Self::new("".to_string())
}

#[doc(hidden)]
pub fn with_metadata(mut self, metadata: TokenStreamMetadata) -> Self {
self.metadata = metadata;
self
}

/// Get `[TokenStreamMetadata`] associated with this [`TokenStream`].
///
/// The metadata struct can be used to describe the [`TokenStream`] origin.
pub fn metadata(&self) -> &TokenStreamMetadata {
&self.metadata
}

pub fn is_empty(&self) -> bool {
self.to_string().is_empty()
}
}

impl Display for TokenStream {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.value)
}
}

impl TokenStreamMetadata {
#[doc(hidden)]
pub fn new(file_path: impl ToString, file_id: impl ToString) -> Self {
Self {
original_file_path: Some(file_path.to_string()),
file_id: Some(file_id.to_string()),
}
}
}

/// **Auxiliary data** returned by procedural macro code generation.
///
/// This struct can be used to collect additional information from the Cairo source code of
Expand All @@ -101,7 +30,7 @@ impl TokenStreamMetadata {
/// For instance, auxiliary data can be serialized as JSON.
///
/// ```
/// use cairo_lang_macro::{AuxData, ProcMacroResult, TokenStream, attribute_macro, post_process, PostProcessContext};
/// use cairo_lang_macro::{AuxData, ProcMacroResult, TokenStream, TokenTree, Token, TextSpan, attribute_macro, post_process, PostProcessContext};
/// use serde::{Serialize, Deserialize};
/// #[derive(Debug, Serialize, Deserialize)]
/// struct SomeAuxDataFormat {
Expand All @@ -110,11 +39,16 @@ impl TokenStreamMetadata {
///
/// #[attribute_macro]
/// pub fn some_macro(_attr: TokenStream, token_stream: TokenStream) -> ProcMacroResult {
/// let token_stream = TokenStream::new(
/// token_stream.to_string()
/// // Remove macro call to avoid infinite loop.
/// .replace("#[some]", "")
/// );
/// // Remove macro call to avoid infinite loop.
/// let code = token_stream.to_string().replace("#[some]", "");
/// let token_stream = TokenStream::new(vec![
/// TokenTree::Ident(
/// Token::new(
/// code.clone(),
/// TextSpan::new(0, code.len())
/// )
/// )
/// ]);
/// let value = SomeAuxDataFormat { some_message: "Hello from some macro!".to_string() };
/// let value = serde_json::to_string(&value).unwrap();
/// let value: Vec<u8> = value.into_bytes();
Expand Down
Loading
Loading