Compare commits
9 Commits
2d8b7a262c
...
339c666ea6
Author | SHA1 | Date |
---|---|---|
David Mak | 339c666ea6 | |
David Mak | d6b884664e | |
David Mak | bb18ace08f | |
David Mak | 7796dd5e4f | |
David Mak | 373f4307c9 | |
David Mak | b469de51bc | |
lyken | a9afa92cea | |
lyken | a14c00434c | |
David Mak | 423b26ea81 |
|
@ -1,10 +1,9 @@
|
||||||
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, Data, DataStruct, Expr, ExprPath, GenericArgument, Ident, LitStr,
|
parse_macro_input, spanned::Spanned, Data, DataStruct, Expr, ExprField, ExprMethodCall,
|
||||||
PathArguments, Type, TypePath,
|
ExprPath, GenericArgument, Ident, LitStr, Path, PathArguments, Type, TypePath,
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Extracts all generic arguments of a [`Type`] into a [`Vec`].
|
/// Extracts all generic arguments of a [`Type`] into a [`Vec`].
|
||||||
|
@ -34,24 +33,74 @@ 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, .. }) => {
|
||||||
let Ok(ident) = path.require_ident() else {
|
if let Some(ident) = map_path_to_ident(path, &["usize", "size_t"], "llvm_usize") {
|
||||||
abort!(
|
quote! { #ident }
|
||||||
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,
|
||||||
|
@ -63,10 +112,44 @@ fn normalize_value_expr(expr: &Expr) -> proc_macro2::TokenStream {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Expr::Call(_) | Expr::MethodCall(_) => {
|
Expr::Call(_) => {
|
||||||
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,
|
||||||
|
@ -96,9 +179,13 @@ 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 either an expression returning an instance of `inkwell::types::BasicType` without the
|
/// accepts one of the following:
|
||||||
/// `inkwell::context::Context` instance prefix, or the reserved identifiers `usize` and `size_t` referring to an
|
///
|
||||||
/// `inkwell::types::IntType` of the platform-dependent integer size.
|
/// - An expression returning an instance of `inkwell::types::BasicType` (with or without the receiver `ctx`/`context`).
|
||||||
|
/// 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
|
||||||
///
|
///
|
||||||
|
@ -118,6 +205,8 @@ 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>>,
|
||||||
///
|
///
|
||||||
|
|
|
@ -0,0 +1,20 @@
|
||||||
|
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() {}
|
|
@ -0,0 +1,18 @@
|
||||||
|
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() {}
|
|
@ -0,0 +1,18 @@
|
||||||
|
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() {}
|
|
@ -0,0 +1,18 @@
|
||||||
|
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,4 +3,8 @@ 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,14 +1,12 @@
|
||||||
use inkwell::context::ContextRef;
|
|
||||||
use inkwell::{
|
use inkwell::{
|
||||||
context::{AsContextRef, Context},
|
context::Context,
|
||||||
types::{AnyTypeEnum, BasicType, BasicTypeEnum, IntType, PointerType},
|
types::{AnyTypeEnum, BasicType, BasicTypeEnum, IntType, PointerType},
|
||||||
values::{IntValue, PointerValue},
|
values::{IntValue, PointerValue},
|
||||||
AddressSpace,
|
AddressSpace,
|
||||||
};
|
};
|
||||||
use itertools::Itertools;
|
use nac3core_derive::StructFields;
|
||||||
|
|
||||||
use super::{
|
use super::{
|
||||||
structure::{FieldIndexCounter, StructField, StructFields},
|
structure::StructField,
|
||||||
ProxyType,
|
ProxyType,
|
||||||
};
|
};
|
||||||
use crate::codegen::{
|
use crate::codegen::{
|
||||||
|
@ -24,38 +22,16 @@ pub struct NDArrayType<'ctx> {
|
||||||
llvm_usize: IntType<'ctx>,
|
llvm_usize: IntType<'ctx>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(PartialEq, Eq, Clone, Copy)]
|
#[derive(PartialEq, Eq, Clone, Copy, StructFields)]
|
||||||
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