Compare commits
9 Commits
339c666ea6
...
2d8b7a262c
Author | SHA1 | Date |
---|---|---|
David Mak | 2d8b7a262c | |
David Mak | c50ed30f75 | |
David Mak | 23a8bcb711 | |
David Mak | 900150def3 | |
David Mak | 4df1ec3282 | |
David Mak | f276547800 | |
lyken | 1787c9cfc9 | |
lyken | e6296d5e32 | |
David Mak | 6b24ef2a11 |
|
@ -1,9 +1,10 @@
|
||||||
use proc_macro::TokenStream;
|
use proc_macro::TokenStream;
|
||||||
use proc_macro_error::{abort, proc_macro_error};
|
use proc_macro_error::{abort, proc_macro_error};
|
||||||
use quote::quote;
|
use quote::quote;
|
||||||
|
use syn::spanned::Spanned;
|
||||||
use syn::{
|
use syn::{
|
||||||
parse_macro_input, spanned::Spanned, Data, DataStruct, Expr, ExprField, ExprMethodCall,
|
parse_macro_input, Data, DataStruct, Expr, ExprPath, GenericArgument, Ident, LitStr,
|
||||||
ExprPath, GenericArgument, Ident, LitStr, Path, PathArguments, Type, TypePath,
|
PathArguments, Type, TypePath,
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Extracts all generic arguments of a [`Type`] into a [`Vec`].
|
/// Extracts all generic arguments of a [`Type`] into a [`Vec`].
|
||||||
|
@ -33,74 +34,24 @@ fn extract_generic_args(expected_ty_name: &'static str, ty: &Type) -> Option<Vec
|
||||||
Some(args.iter().cloned().collect::<Vec<_>>())
|
Some(args.iter().cloned().collect::<Vec<_>>())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Maps a `path` matching one of the `target_idents` into the `replacement` [`Ident`].
|
|
||||||
fn map_path_to_ident(path: &Path, target_idents: &[&str], replacement: &str) -> Option<Ident> {
|
|
||||||
path.require_ident()
|
|
||||||
.ok()
|
|
||||||
.filter(|ident| target_idents.iter().any(|target| ident == target))
|
|
||||||
.map(|ident| Ident::new(replacement, ident.span()))
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Extracts the left-hand side of a dot-expression.
|
|
||||||
fn extract_dot_operand(expr: &Expr) -> Option<&Expr> {
|
|
||||||
match expr {
|
|
||||||
Expr::MethodCall(ExprMethodCall { receiver: operand, .. })
|
|
||||||
| Expr::Field(ExprField { base: operand, .. }) => Some(operand),
|
|
||||||
_ => None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Replaces the top-level receiver of a dot-expression with an [`Ident`], returning `Some(&mut expr)` if the
|
|
||||||
/// replacement is performed.
|
|
||||||
///
|
|
||||||
/// The top-level receiver is the left-most receiver expression, e.g. the top-level receiver of `a.b.c.foo()` is `a`.
|
|
||||||
fn replace_top_level_receiver(expr: &mut Expr, ident: Ident) -> Option<&mut Expr> {
|
|
||||||
if let Expr::MethodCall(ExprMethodCall { receiver: operand, .. })
|
|
||||||
| Expr::Field(ExprField { base: operand, .. }) = expr
|
|
||||||
{
|
|
||||||
return if extract_dot_operand(operand).is_some() {
|
|
||||||
if replace_top_level_receiver(operand, ident).is_some() {
|
|
||||||
Some(expr)
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
*operand = Box::new(Expr::Path(ExprPath {
|
|
||||||
attrs: Vec::default(),
|
|
||||||
qself: None,
|
|
||||||
path: ident.into(),
|
|
||||||
}));
|
|
||||||
|
|
||||||
Some(expr)
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
None
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Iterates all operands to the left-hand side of the `.` of an [expression][`Expr`], i.e. the container operand of all
|
|
||||||
/// [`Expr::Field`] and the receiver operand of all [`Expr::MethodCall`].
|
|
||||||
///
|
|
||||||
/// The iterator will return the operand expressions in reverse order of appearance. For example, `a.b.c.func()` will
|
|
||||||
/// return `vec![c, b, a]`.
|
|
||||||
fn iter_dot_operands(expr: &Expr) -> impl Iterator<Item = &Expr> {
|
|
||||||
let mut o = extract_dot_operand(expr);
|
|
||||||
|
|
||||||
std::iter::from_fn(move || {
|
|
||||||
let this = o;
|
|
||||||
o = o.as_ref().and_then(|o| extract_dot_operand(o));
|
|
||||||
|
|
||||||
this
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Normalizes a value expression for use when creating an instance of this structure, returning a
|
/// Normalizes a value expression for use when creating an instance of this structure, returning a
|
||||||
/// [`proc_macro2::TokenStream`] of tokens representing the normalized expression.
|
/// [`proc_macro2::TokenStream`] of tokens representing the normalized expression.
|
||||||
fn normalize_value_expr(expr: &Expr) -> proc_macro2::TokenStream {
|
fn normalize_value_expr(expr: &Expr) -> proc_macro2::TokenStream {
|
||||||
match &expr {
|
match &expr {
|
||||||
Expr::Path(ExprPath { qself: None, path, .. }) => {
|
Expr::Path(ExprPath { qself: None, path, .. }) => {
|
||||||
if let Some(ident) = map_path_to_ident(path, &["usize", "size_t"], "llvm_usize") {
|
let Ok(ident) = path.require_ident() else {
|
||||||
quote! { #ident }
|
abort!(
|
||||||
|
path,
|
||||||
|
format!(
|
||||||
|
"Expected one of `size_t`, `usize`, or an implicit call expression in #[value_type(...)], found {}",
|
||||||
|
quote!(#expr).to_string(),
|
||||||
|
)
|
||||||
|
)
|
||||||
|
};
|
||||||
|
|
||||||
|
if ident == "usize" || ident == "size_t" {
|
||||||
|
let llvm_usize = Ident::new("llvm_usize", ident.span());
|
||||||
|
quote! { #llvm_usize }
|
||||||
} else {
|
} else {
|
||||||
abort!(
|
abort!(
|
||||||
path,
|
path,
|
||||||
|
@ -112,44 +63,10 @@ fn normalize_value_expr(expr: &Expr) -> proc_macro2::TokenStream {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Expr::Call(_) => {
|
Expr::Call(_) | Expr::MethodCall(_) => {
|
||||||
quote! { ctx.#expr }
|
quote! { ctx.#expr }
|
||||||
}
|
}
|
||||||
|
|
||||||
Expr::MethodCall(_) => {
|
|
||||||
let base_receiver = iter_dot_operands(expr).last();
|
|
||||||
|
|
||||||
match base_receiver {
|
|
||||||
// `usize.{...}`, `size_t.{...}` -> Rewrite the identifiers to `llvm_usize`
|
|
||||||
Some(Expr::Path(ExprPath { qself: None, path, .. }))
|
|
||||||
if map_path_to_ident(path, &["usize", "size_t"], "llvm_usize").is_some() =>
|
|
||||||
{
|
|
||||||
let ident =
|
|
||||||
map_path_to_ident(path, &["usize", "size_t"], "llvm_usize").unwrap();
|
|
||||||
|
|
||||||
let mut expr = expr.clone();
|
|
||||||
let expr = replace_top_level_receiver(&mut expr, ident).unwrap();
|
|
||||||
|
|
||||||
quote!(#expr)
|
|
||||||
}
|
|
||||||
|
|
||||||
// `ctx.{...}`, `context.{...}` -> Rewrite the identifiers to `ctx`
|
|
||||||
Some(Expr::Path(ExprPath { qself: None, path, .. }))
|
|
||||||
if map_path_to_ident(path, &["ctx", "context"], "ctx").is_some() =>
|
|
||||||
{
|
|
||||||
let ident = map_path_to_ident(path, &["ctx", "context"], "ctx").unwrap();
|
|
||||||
|
|
||||||
let mut expr = expr.clone();
|
|
||||||
let expr = replace_top_level_receiver(&mut expr, ident).unwrap();
|
|
||||||
|
|
||||||
quote!(#expr)
|
|
||||||
}
|
|
||||||
|
|
||||||
// No reserved identifier prefix -> Prepend `ctx.` to the entire expression
|
|
||||||
_ => quote! { ctx.#expr },
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
_ => {
|
_ => {
|
||||||
abort!(
|
abort!(
|
||||||
expr,
|
expr,
|
||||||
|
@ -179,13 +96,9 @@ fn normalize_value_expr(expr: &Expr) -> proc_macro2::TokenStream {
|
||||||
/// # Attributes for [`StructFields`]
|
/// # Attributes for [`StructFields`]
|
||||||
///
|
///
|
||||||
/// Each `StructField` field must be declared with the `#[value_type(...)]` attribute. The argument of `value_type`
|
/// Each `StructField` field must be declared with the `#[value_type(...)]` attribute. The argument of `value_type`
|
||||||
/// accepts one of the following:
|
/// accepts either an expression returning an instance of `inkwell::types::BasicType` without the
|
||||||
///
|
/// `inkwell::context::Context` instance prefix, or the reserved identifiers `usize` and `size_t` referring to an
|
||||||
/// - An expression returning an instance of `inkwell::types::BasicType` (with or without the receiver `ctx`/`context`).
|
/// `inkwell::types::IntType` of the platform-dependent integer size.
|
||||||
/// For example, `context.i8_type()`, `ctx.i8_type()`, and `i8_type()` all refer to `i8`.
|
|
||||||
/// - The reserved identifiers `usize` and `size_t` referring to an `inkwell::types::IntType` of the platform-dependent
|
|
||||||
/// integer size. `usize` and `size_t` can also be used as the receiver to other method calls, e.g.
|
|
||||||
/// `usize.array_type(3)`.
|
|
||||||
///
|
///
|
||||||
/// # Example
|
/// # Example
|
||||||
///
|
///
|
||||||
|
@ -205,8 +118,6 @@ fn normalize_value_expr(expr: &Expr) -> proc_macro2::TokenStream {
|
||||||
/// #[derive(PartialEq, Eq, Clone, Copy, StructFields)]
|
/// #[derive(PartialEq, Eq, Clone, Copy, StructFields)]
|
||||||
/// pub struct SliceValue<'ctx> {
|
/// pub struct SliceValue<'ctx> {
|
||||||
/// // Declares ptr have a value type of i8*
|
/// // Declares ptr have a value type of i8*
|
||||||
/// //
|
|
||||||
/// // Can also be written as `ctx.i8_type().ptr_type(...)` or `context.i8_type().ptr_type(...)`
|
|
||||||
/// #[value_type(i8_type().ptr_type(AddressSpace::default()))]
|
/// #[value_type(i8_type().ptr_type(AddressSpace::default()))]
|
||||||
/// ptr: StructField<'ctx, PointerValue<'ctx>>,
|
/// ptr: StructField<'ctx, PointerValue<'ctx>>,
|
||||||
///
|
///
|
||||||
|
|
|
@ -1,20 +0,0 @@
|
||||||
use nac3core::{
|
|
||||||
codegen::types::structure::StructField,
|
|
||||||
inkwell::{
|
|
||||||
values::{IntValue, PointerValue},
|
|
||||||
AddressSpace,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
use nac3core_derive::StructFields;
|
|
||||||
|
|
||||||
#[derive(PartialEq, Eq, Clone, Copy, StructFields)]
|
|
||||||
pub struct NDArrayValue<'ctx> {
|
|
||||||
#[value_type(usize)]
|
|
||||||
ndims: StructField<'ctx, IntValue<'ctx>>,
|
|
||||||
#[value_type(usize.ptr_type(AddressSpace::default()))]
|
|
||||||
shape: StructField<'ctx, PointerValue<'ctx>>,
|
|
||||||
#[value_type(i8_type().ptr_type(AddressSpace::default()))]
|
|
||||||
data: StructField<'ctx, PointerValue<'ctx>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
fn main() {}
|
|
|
@ -1,18 +0,0 @@
|
||||||
use nac3core::{
|
|
||||||
codegen::types::structure::StructField,
|
|
||||||
inkwell::{
|
|
||||||
values::{IntValue, PointerValue},
|
|
||||||
AddressSpace,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
use nac3core_derive::StructFields;
|
|
||||||
|
|
||||||
#[derive(PartialEq, Eq, Clone, Copy, StructFields)]
|
|
||||||
pub struct SliceValue<'ctx> {
|
|
||||||
#[value_type(context.i8_type().ptr_type(AddressSpace::default()))]
|
|
||||||
ptr: StructField<'ctx, PointerValue<'ctx>>,
|
|
||||||
#[value_type(usize)]
|
|
||||||
len: StructField<'ctx, IntValue<'ctx>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
fn main() {}
|
|
|
@ -1,18 +0,0 @@
|
||||||
use nac3core::{
|
|
||||||
codegen::types::structure::StructField,
|
|
||||||
inkwell::{
|
|
||||||
values::{IntValue, PointerValue},
|
|
||||||
AddressSpace,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
use nac3core_derive::StructFields;
|
|
||||||
|
|
||||||
#[derive(PartialEq, Eq, Clone, Copy, StructFields)]
|
|
||||||
pub struct SliceValue<'ctx> {
|
|
||||||
#[value_type(ctx.i8_type().ptr_type(AddressSpace::default()))]
|
|
||||||
ptr: StructField<'ctx, PointerValue<'ctx>>,
|
|
||||||
#[value_type(usize)]
|
|
||||||
len: StructField<'ctx, IntValue<'ctx>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
fn main() {}
|
|
|
@ -1,18 +0,0 @@
|
||||||
use nac3core::{
|
|
||||||
codegen::types::structure::StructField,
|
|
||||||
inkwell::{
|
|
||||||
values::{IntValue, PointerValue},
|
|
||||||
AddressSpace,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
use nac3core_derive::StructFields;
|
|
||||||
|
|
||||||
#[derive(PartialEq, Eq, Clone, Copy, StructFields)]
|
|
||||||
pub struct SliceValue<'ctx> {
|
|
||||||
#[value_type(i8_type().ptr_type(AddressSpace::default()))]
|
|
||||||
ptr: StructField<'ctx, PointerValue<'ctx>>,
|
|
||||||
#[value_type(size_t)]
|
|
||||||
len: StructField<'ctx, IntValue<'ctx>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
fn main() {}
|
|
|
@ -3,8 +3,4 @@ fn test_parse_empty() {
|
||||||
let t = trybuild::TestCases::new();
|
let t = trybuild::TestCases::new();
|
||||||
t.pass("tests/structfields_empty.rs");
|
t.pass("tests/structfields_empty.rs");
|
||||||
t.pass("tests/structfields_slice.rs");
|
t.pass("tests/structfields_slice.rs");
|
||||||
t.pass("tests/structfields_slice_ctx.rs");
|
|
||||||
t.pass("tests/structfields_slice_context.rs");
|
|
||||||
t.pass("tests/structfields_slice_sizet.rs");
|
|
||||||
t.pass("tests/structfields_ndarray.rs");
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,12 +1,14 @@
|
||||||
|
use inkwell::context::ContextRef;
|
||||||
use inkwell::{
|
use inkwell::{
|
||||||
context::Context,
|
context::{AsContextRef, Context},
|
||||||
types::{AnyTypeEnum, BasicType, BasicTypeEnum, IntType, PointerType},
|
types::{AnyTypeEnum, BasicType, BasicTypeEnum, IntType, PointerType},
|
||||||
values::{IntValue, PointerValue},
|
values::{IntValue, PointerValue},
|
||||||
AddressSpace,
|
AddressSpace,
|
||||||
};
|
};
|
||||||
use nac3core_derive::StructFields;
|
use itertools::Itertools;
|
||||||
|
|
||||||
use super::{
|
use super::{
|
||||||
structure::StructField,
|
structure::{FieldIndexCounter, StructField, StructFields},
|
||||||
ProxyType,
|
ProxyType,
|
||||||
};
|
};
|
||||||
use crate::codegen::{
|
use crate::codegen::{
|
||||||
|
@ -22,16 +24,38 @@ pub struct NDArrayType<'ctx> {
|
||||||
llvm_usize: IntType<'ctx>,
|
llvm_usize: IntType<'ctx>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(PartialEq, Eq, Clone, Copy, StructFields)]
|
#[derive(PartialEq, Eq, Clone, Copy)]
|
||||||
pub struct NDArrayStructFields<'ctx> {
|
pub struct NDArrayStructFields<'ctx> {
|
||||||
#[value_type(usize)]
|
|
||||||
pub ndims: StructField<'ctx, IntValue<'ctx>>,
|
pub ndims: StructField<'ctx, IntValue<'ctx>>,
|
||||||
#[value_type(usize.ptr_type(AddressSpace::default()))]
|
|
||||||
pub shape: StructField<'ctx, PointerValue<'ctx>>,
|
pub shape: StructField<'ctx, PointerValue<'ctx>>,
|
||||||
#[value_type(i8_type().ptr_type(AddressSpace::default()))]
|
|
||||||
pub data: StructField<'ctx, PointerValue<'ctx>>,
|
pub data: StructField<'ctx, PointerValue<'ctx>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<'ctx> StructFields<'ctx> for NDArrayStructFields<'ctx> {
|
||||||
|
fn new(ctx: impl AsContextRef<'ctx>, llvm_usize: IntType<'ctx>) -> Self {
|
||||||
|
let ctx = unsafe { ContextRef::new(ctx.as_ctx_ref()) };
|
||||||
|
let mut counter = FieldIndexCounter::default();
|
||||||
|
|
||||||
|
NDArrayStructFields {
|
||||||
|
ndims: StructField::create(&mut counter, "ndims", llvm_usize),
|
||||||
|
shape: StructField::create(
|
||||||
|
&mut counter,
|
||||||
|
"shape",
|
||||||
|
llvm_usize.ptr_type(AddressSpace::default()),
|
||||||
|
),
|
||||||
|
data: StructField::create(
|
||||||
|
&mut counter,
|
||||||
|
"data",
|
||||||
|
ctx.i8_type().ptr_type(AddressSpace::default()),
|
||||||
|
),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn to_vec(&self) -> Vec<(&'static str, BasicTypeEnum<'ctx>)> {
|
||||||
|
vec![self.ndims.into(), self.shape.into(), self.data.into()]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl<'ctx> NDArrayType<'ctx> {
|
impl<'ctx> NDArrayType<'ctx> {
|
||||||
/// Checks whether `llvm_ty` represents a `ndarray` type, returning [Err] if it does not.
|
/// Checks whether `llvm_ty` represents a `ndarray` type, returning [Err] if it does not.
|
||||||
pub fn is_representable(
|
pub fn is_representable(
|
||||||
|
|
Loading…
Reference in New Issue