mirror of
https://github.com/vosen/ZLUDA.git
synced 2025-04-28 13:37:57 +03:00
PTX parser rewrite (#267)
Replaces traditional LALRPOP-based parser with winnow-based parser to handle out-of-order instruction modifer. Generate instruction type and instruction visitor from a macro instead of writing by hand. Add separate compilation path using the new parser that only works in tests for now
This commit is contained in:
parent
872054ae40
commit
193eb29be8
34 changed files with 14776 additions and 55 deletions
13
ptx_parser_macros_impl/Cargo.toml
Normal file
13
ptx_parser_macros_impl/Cargo.toml
Normal file
|
@ -0,0 +1,13 @@
|
|||
[package]
|
||||
name = "ptx_parser_macros_impl"
|
||||
version = "0.0.0"
|
||||
authors = ["Andrzej Janik <vosen@vosen.pl>"]
|
||||
edition = "2021"
|
||||
|
||||
[lib]
|
||||
|
||||
[dependencies]
|
||||
syn = { version = "2.0.67", features = ["extra-traits", "full"] }
|
||||
quote = "1.0"
|
||||
proc-macro2 = "1.0.86"
|
||||
rustc-hash = "2.0.0"
|
881
ptx_parser_macros_impl/src/lib.rs
Normal file
881
ptx_parser_macros_impl/src/lib.rs
Normal file
|
@ -0,0 +1,881 @@
|
|||
use proc_macro2::TokenStream;
|
||||
use quote::{format_ident, quote, ToTokens};
|
||||
use syn::{
|
||||
braced, parse::Parse, punctuated::Punctuated, token, Expr, Ident, LitBool, PathSegment, Token,
|
||||
Type, TypeParam, Visibility,
|
||||
};
|
||||
|
||||
pub mod parser;
|
||||
|
||||
pub struct GenerateInstructionType {
|
||||
pub visibility: Option<Visibility>,
|
||||
pub name: Ident,
|
||||
pub type_parameters: Punctuated<TypeParam, Token![,]>,
|
||||
pub short_parameters: Punctuated<Ident, Token![,]>,
|
||||
pub variants: Punctuated<InstructionVariant, Token![,]>,
|
||||
}
|
||||
|
||||
impl GenerateInstructionType {
|
||||
pub fn emit_arg_types(&self, tokens: &mut TokenStream) {
|
||||
for v in self.variants.iter() {
|
||||
v.emit_type(&self.visibility, tokens);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn emit_instruction_type(&self, tokens: &mut TokenStream) {
|
||||
let vis = &self.visibility;
|
||||
let type_name = &self.name;
|
||||
let type_parameters = &self.type_parameters;
|
||||
let variants = self.variants.iter().map(|v| v.emit_variant());
|
||||
quote! {
|
||||
#vis enum #type_name<#type_parameters> {
|
||||
#(#variants),*
|
||||
}
|
||||
}
|
||||
.to_tokens(tokens);
|
||||
}
|
||||
|
||||
pub fn emit_visit(&self, tokens: &mut TokenStream) {
|
||||
self.emit_visit_impl(VisitKind::Ref, tokens, InstructionVariant::emit_visit)
|
||||
}
|
||||
|
||||
pub fn emit_visit_mut(&self, tokens: &mut TokenStream) {
|
||||
self.emit_visit_impl(
|
||||
VisitKind::RefMut,
|
||||
tokens,
|
||||
InstructionVariant::emit_visit_mut,
|
||||
)
|
||||
}
|
||||
|
||||
pub fn emit_visit_map(&self, tokens: &mut TokenStream) {
|
||||
self.emit_visit_impl(VisitKind::Map, tokens, InstructionVariant::emit_visit_map)
|
||||
}
|
||||
|
||||
fn emit_visit_impl(
|
||||
&self,
|
||||
kind: VisitKind,
|
||||
tokens: &mut TokenStream,
|
||||
mut fn_: impl FnMut(&InstructionVariant, &Ident, &mut TokenStream),
|
||||
) {
|
||||
let type_name = &self.name;
|
||||
let type_parameters = &self.type_parameters;
|
||||
let short_parameters = &self.short_parameters;
|
||||
let mut inner_tokens = TokenStream::new();
|
||||
for v in self.variants.iter() {
|
||||
fn_(v, type_name, &mut inner_tokens);
|
||||
}
|
||||
let visit_ref = kind.reference();
|
||||
let visitor_type = format_ident!("Visitor{}", kind.type_suffix());
|
||||
let visit_fn = format_ident!("visit{}", kind.fn_suffix());
|
||||
let (type_parameters, visitor_parameters, return_type) = if kind == VisitKind::Map {
|
||||
(
|
||||
quote! { <#type_parameters, To: Operand, Err> },
|
||||
quote! { <#short_parameters, To, Err> },
|
||||
quote! { std::result::Result<#type_name<To>, Err> },
|
||||
)
|
||||
} else {
|
||||
(
|
||||
quote! { <#type_parameters, Err> },
|
||||
quote! { <#short_parameters, Err> },
|
||||
quote! { std::result::Result<(), Err> },
|
||||
)
|
||||
};
|
||||
quote! {
|
||||
pub fn #visit_fn #type_parameters (i: #visit_ref #type_name<#short_parameters>, visitor: &mut impl #visitor_type #visitor_parameters ) -> #return_type {
|
||||
Ok(match i {
|
||||
#inner_tokens
|
||||
})
|
||||
}
|
||||
}.to_tokens(tokens);
|
||||
if kind == VisitKind::Map {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, PartialEq, Eq)]
|
||||
enum VisitKind {
|
||||
Ref,
|
||||
RefMut,
|
||||
Map,
|
||||
}
|
||||
|
||||
impl VisitKind {
|
||||
fn fn_suffix(self) -> &'static str {
|
||||
match self {
|
||||
VisitKind::Ref => "",
|
||||
VisitKind::RefMut => "_mut",
|
||||
VisitKind::Map => "_map",
|
||||
}
|
||||
}
|
||||
|
||||
fn type_suffix(self) -> &'static str {
|
||||
match self {
|
||||
VisitKind::Ref => "",
|
||||
VisitKind::RefMut => "Mut",
|
||||
VisitKind::Map => "Map",
|
||||
}
|
||||
}
|
||||
|
||||
fn reference(self) -> Option<proc_macro2::TokenStream> {
|
||||
match self {
|
||||
VisitKind::Ref => Some(quote! { & }),
|
||||
VisitKind::RefMut => Some(quote! { &mut }),
|
||||
VisitKind::Map => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Parse for GenerateInstructionType {
|
||||
fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
|
||||
let visibility = if !input.peek(Token![enum]) {
|
||||
Some(input.parse::<Visibility>()?)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
input.parse::<Token![enum]>()?;
|
||||
let name = input.parse::<Ident>()?;
|
||||
input.parse::<Token![<]>()?;
|
||||
let type_parameters = Punctuated::parse_separated_nonempty(input)?;
|
||||
let short_parameters = type_parameters
|
||||
.iter()
|
||||
.map(|p: &TypeParam| p.ident.clone())
|
||||
.collect();
|
||||
input.parse::<Token![>]>()?;
|
||||
let variants_buffer;
|
||||
braced!(variants_buffer in input);
|
||||
let variants = variants_buffer.parse_terminated(InstructionVariant::parse, Token![,])?;
|
||||
Ok(Self {
|
||||
visibility,
|
||||
name,
|
||||
type_parameters,
|
||||
short_parameters,
|
||||
variants,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
pub struct InstructionVariant {
|
||||
pub name: Ident,
|
||||
pub type_: Option<Option<Expr>>,
|
||||
pub space: Option<Expr>,
|
||||
pub data: Option<Type>,
|
||||
pub arguments: Option<Arguments>,
|
||||
pub visit: Option<Expr>,
|
||||
pub visit_mut: Option<Expr>,
|
||||
pub map: Option<Expr>,
|
||||
}
|
||||
|
||||
impl InstructionVariant {
|
||||
fn args_name(&self) -> Ident {
|
||||
format_ident!("{}Args", self.name)
|
||||
}
|
||||
|
||||
fn emit_variant(&self) -> TokenStream {
|
||||
let name = &self.name;
|
||||
let data = match &self.data {
|
||||
None => {
|
||||
quote! {}
|
||||
}
|
||||
Some(data_type) => {
|
||||
quote! {
|
||||
data: #data_type,
|
||||
}
|
||||
}
|
||||
};
|
||||
let arguments = match &self.arguments {
|
||||
None => {
|
||||
quote! {}
|
||||
}
|
||||
Some(args) => {
|
||||
let args_name = self.args_name();
|
||||
match &args {
|
||||
Arguments::Def(InstructionArguments { generic: None, .. }) => {
|
||||
quote! {
|
||||
arguments: #args_name,
|
||||
}
|
||||
}
|
||||
Arguments::Def(InstructionArguments {
|
||||
generic: Some(generics),
|
||||
..
|
||||
}) => {
|
||||
quote! {
|
||||
arguments: #args_name <#generics>,
|
||||
}
|
||||
}
|
||||
Arguments::Decl(type_) => quote! {
|
||||
arguments: #type_,
|
||||
},
|
||||
}
|
||||
}
|
||||
};
|
||||
quote! {
|
||||
#name { #data #arguments }
|
||||
}
|
||||
}
|
||||
|
||||
fn emit_visit(&self, enum_: &Ident, tokens: &mut TokenStream) {
|
||||
self.emit_visit_impl(&self.visit, enum_, tokens, InstructionArguments::emit_visit)
|
||||
}
|
||||
|
||||
fn emit_visit_mut(&self, enum_: &Ident, tokens: &mut TokenStream) {
|
||||
self.emit_visit_impl(
|
||||
&self.visit_mut,
|
||||
enum_,
|
||||
tokens,
|
||||
InstructionArguments::emit_visit_mut,
|
||||
)
|
||||
}
|
||||
|
||||
fn emit_visit_impl(
|
||||
&self,
|
||||
visit_fn: &Option<Expr>,
|
||||
enum_: &Ident,
|
||||
tokens: &mut TokenStream,
|
||||
mut fn_: impl FnMut(&InstructionArguments, &Option<Option<Expr>>, &Option<Expr>) -> TokenStream,
|
||||
) {
|
||||
let name = &self.name;
|
||||
let arguments = match &self.arguments {
|
||||
None => {
|
||||
quote! {
|
||||
#enum_ :: #name { .. } => { }
|
||||
}
|
||||
.to_tokens(tokens);
|
||||
return;
|
||||
}
|
||||
Some(Arguments::Decl(_)) => {
|
||||
quote! {
|
||||
#enum_ :: #name { data, arguments } => { #visit_fn }
|
||||
}
|
||||
.to_tokens(tokens);
|
||||
return;
|
||||
}
|
||||
Some(Arguments::Def(args)) => args,
|
||||
};
|
||||
let data = &self.data.as_ref().map(|_| quote! { data,});
|
||||
let arg_calls = fn_(arguments, &self.type_, &self.space);
|
||||
quote! {
|
||||
#enum_ :: #name { #data arguments } => {
|
||||
#arg_calls
|
||||
}
|
||||
}
|
||||
.to_tokens(tokens);
|
||||
}
|
||||
|
||||
fn emit_visit_map(&self, enum_: &Ident, tokens: &mut TokenStream) {
|
||||
let name = &self.name;
|
||||
let data = &self.data.as_ref().map(|_| quote! { data,});
|
||||
let arguments = match self.arguments {
|
||||
None => None,
|
||||
Some(Arguments::Decl(_)) => {
|
||||
let map = self.map.as_ref().unwrap();
|
||||
quote! {
|
||||
#enum_ :: #name { #data arguments } => {
|
||||
#map
|
||||
}
|
||||
}
|
||||
.to_tokens(tokens);
|
||||
return;
|
||||
}
|
||||
Some(Arguments::Def(ref def)) => Some(def),
|
||||
};
|
||||
let arguments_ident = &self.arguments.as_ref().map(|_| quote! { arguments,});
|
||||
let mut arg_calls = None;
|
||||
let arguments_init = arguments.as_ref().map(|arguments| {
|
||||
let arg_type = self.args_name();
|
||||
arg_calls = Some(arguments.emit_visit_map(&self.type_, &self.space));
|
||||
let arg_names = arguments.fields.iter().map(|arg| &arg.name);
|
||||
quote! {
|
||||
arguments: #arg_type { #(#arg_names),* }
|
||||
}
|
||||
});
|
||||
quote! {
|
||||
#enum_ :: #name { #data #arguments_ident } => {
|
||||
#arg_calls
|
||||
#enum_ :: #name { #data #arguments_init }
|
||||
}
|
||||
}
|
||||
.to_tokens(tokens);
|
||||
}
|
||||
|
||||
fn emit_type(&self, vis: &Option<Visibility>, tokens: &mut TokenStream) {
|
||||
let arguments = match self.arguments {
|
||||
Some(Arguments::Def(ref a)) => a,
|
||||
Some(Arguments::Decl(_)) => return,
|
||||
None => return,
|
||||
};
|
||||
let name = self.args_name();
|
||||
let type_parameters = if arguments.generic.is_some() {
|
||||
Some(quote! { <T> })
|
||||
} else {
|
||||
None
|
||||
};
|
||||
let fields = arguments.fields.iter().map(|f| f.emit_field(vis));
|
||||
quote! {
|
||||
#vis struct #name #type_parameters {
|
||||
#(#fields),*
|
||||
}
|
||||
}
|
||||
.to_tokens(tokens);
|
||||
}
|
||||
}
|
||||
|
||||
impl Parse for InstructionVariant {
|
||||
fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
|
||||
let name = input.parse::<Ident>()?;
|
||||
let properties_buffer;
|
||||
braced!(properties_buffer in input);
|
||||
let properties = properties_buffer.parse_terminated(VariantProperty::parse, Token![,])?;
|
||||
let mut type_ = None;
|
||||
let mut space = None;
|
||||
let mut data = None;
|
||||
let mut arguments = None;
|
||||
let mut visit = None;
|
||||
let mut visit_mut = None;
|
||||
let mut map = None;
|
||||
for property in properties {
|
||||
match property {
|
||||
VariantProperty::Type(t) => type_ = Some(t),
|
||||
VariantProperty::Space(s) => space = Some(s),
|
||||
VariantProperty::Data(d) => data = Some(d),
|
||||
VariantProperty::Arguments(a) => arguments = Some(a),
|
||||
VariantProperty::Visit(e) => visit = Some(e),
|
||||
VariantProperty::VisitMut(e) => visit_mut = Some(e),
|
||||
VariantProperty::Map(e) => map = Some(e),
|
||||
}
|
||||
}
|
||||
Ok(Self {
|
||||
name,
|
||||
type_,
|
||||
space,
|
||||
data,
|
||||
arguments,
|
||||
visit,
|
||||
visit_mut,
|
||||
map,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
enum VariantProperty {
|
||||
Type(Option<Expr>),
|
||||
Space(Expr),
|
||||
Data(Type),
|
||||
Arguments(Arguments),
|
||||
Visit(Expr),
|
||||
VisitMut(Expr),
|
||||
Map(Expr),
|
||||
}
|
||||
|
||||
impl VariantProperty {
|
||||
pub fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
|
||||
let lookahead = input.lookahead1();
|
||||
Ok(if lookahead.peek(Token![type]) {
|
||||
input.parse::<Token![type]>()?;
|
||||
input.parse::<Token![:]>()?;
|
||||
VariantProperty::Type(if input.peek(Token![!]) {
|
||||
input.parse::<Token![!]>()?;
|
||||
None
|
||||
} else {
|
||||
Some(input.parse::<Expr>()?)
|
||||
})
|
||||
} else if lookahead.peek(Ident) {
|
||||
let key = input.parse::<Ident>()?;
|
||||
match &*key.to_string() {
|
||||
"data" => {
|
||||
input.parse::<Token![:]>()?;
|
||||
VariantProperty::Data(input.parse::<Type>()?)
|
||||
}
|
||||
"space" => {
|
||||
input.parse::<Token![:]>()?;
|
||||
VariantProperty::Space(input.parse::<Expr>()?)
|
||||
}
|
||||
"arguments" => {
|
||||
let generics = if input.peek(Token![<]) {
|
||||
input.parse::<Token![<]>()?;
|
||||
let gen_params =
|
||||
Punctuated::<PathSegment, syn::token::PathSep>::parse_separated_nonempty(input)?;
|
||||
input.parse::<Token![>]>()?;
|
||||
Some(gen_params)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
input.parse::<Token![:]>()?;
|
||||
if input.peek(token::Brace) {
|
||||
let fields;
|
||||
braced!(fields in input);
|
||||
VariantProperty::Arguments(Arguments::Def(InstructionArguments::parse(
|
||||
generics, &fields,
|
||||
)?))
|
||||
} else {
|
||||
VariantProperty::Arguments(Arguments::Decl(input.parse::<Type>()?))
|
||||
}
|
||||
}
|
||||
"visit" => {
|
||||
input.parse::<Token![:]>()?;
|
||||
VariantProperty::Visit(input.parse::<Expr>()?)
|
||||
}
|
||||
"visit_mut" => {
|
||||
input.parse::<Token![:]>()?;
|
||||
VariantProperty::VisitMut(input.parse::<Expr>()?)
|
||||
}
|
||||
"map" => {
|
||||
input.parse::<Token![:]>()?;
|
||||
VariantProperty::Map(input.parse::<Expr>()?)
|
||||
}
|
||||
x => {
|
||||
return Err(syn::Error::new(
|
||||
key.span(),
|
||||
format!(
|
||||
"Unexpected key `{}`. Expected `type`, `data`, `arguments`, `visit, `visit_mut` or `map`.",
|
||||
x
|
||||
),
|
||||
))
|
||||
}
|
||||
}
|
||||
} else {
|
||||
return Err(lookahead.error());
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
pub enum Arguments {
|
||||
Decl(Type),
|
||||
Def(InstructionArguments),
|
||||
}
|
||||
|
||||
pub struct InstructionArguments {
|
||||
pub generic: Option<Punctuated<PathSegment, syn::token::PathSep>>,
|
||||
pub fields: Punctuated<ArgumentField, Token![,]>,
|
||||
}
|
||||
|
||||
impl InstructionArguments {
|
||||
pub fn parse(
|
||||
generic: Option<Punctuated<PathSegment, syn::token::PathSep>>,
|
||||
input: syn::parse::ParseStream,
|
||||
) -> syn::Result<Self> {
|
||||
let fields = Punctuated::<ArgumentField, Token![,]>::parse_terminated_with(
|
||||
input,
|
||||
ArgumentField::parse,
|
||||
)?;
|
||||
Ok(Self { generic, fields })
|
||||
}
|
||||
|
||||
fn emit_visit(
|
||||
&self,
|
||||
parent_type: &Option<Option<Expr>>,
|
||||
parent_space: &Option<Expr>,
|
||||
) -> TokenStream {
|
||||
self.emit_visit_impl(parent_type, parent_space, ArgumentField::emit_visit)
|
||||
}
|
||||
|
||||
fn emit_visit_mut(
|
||||
&self,
|
||||
parent_type: &Option<Option<Expr>>,
|
||||
parent_space: &Option<Expr>,
|
||||
) -> TokenStream {
|
||||
self.emit_visit_impl(parent_type, parent_space, ArgumentField::emit_visit_mut)
|
||||
}
|
||||
|
||||
fn emit_visit_map(
|
||||
&self,
|
||||
parent_type: &Option<Option<Expr>>,
|
||||
parent_space: &Option<Expr>,
|
||||
) -> TokenStream {
|
||||
self.emit_visit_impl(parent_type, parent_space, ArgumentField::emit_visit_map)
|
||||
}
|
||||
|
||||
fn emit_visit_impl(
|
||||
&self,
|
||||
parent_type: &Option<Option<Expr>>,
|
||||
parent_space: &Option<Expr>,
|
||||
mut fn_: impl FnMut(&ArgumentField, &Option<Option<Expr>>, &Option<Expr>, bool) -> TokenStream,
|
||||
) -> TokenStream {
|
||||
let is_ident = if let Some(ref generic) = self.generic {
|
||||
generic.len() > 1
|
||||
} else {
|
||||
false
|
||||
};
|
||||
let field_calls = self
|
||||
.fields
|
||||
.iter()
|
||||
.map(|f| fn_(f, parent_type, parent_space, is_ident));
|
||||
quote! {
|
||||
#(#field_calls)*
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct ArgumentField {
|
||||
pub name: Ident,
|
||||
pub is_dst: bool,
|
||||
pub repr: Type,
|
||||
pub space: Option<Expr>,
|
||||
pub type_: Option<Expr>,
|
||||
pub relaxed_type_check: bool,
|
||||
}
|
||||
|
||||
impl ArgumentField {
|
||||
fn parse_block(
|
||||
input: syn::parse::ParseStream,
|
||||
) -> syn::Result<(Type, Option<Expr>, Option<Expr>, Option<bool>, bool)> {
|
||||
let content;
|
||||
braced!(content in input);
|
||||
let all_fields =
|
||||
Punctuated::<ExprOrPath, Token![,]>::parse_terminated_with(&content, |content| {
|
||||
let lookahead = content.lookahead1();
|
||||
Ok(if lookahead.peek(Token![type]) {
|
||||
content.parse::<Token![type]>()?;
|
||||
content.parse::<Token![:]>()?;
|
||||
ExprOrPath::Type(content.parse::<Expr>()?)
|
||||
} else if lookahead.peek(Ident) {
|
||||
let name_ident = content.parse::<Ident>()?;
|
||||
content.parse::<Token![:]>()?;
|
||||
match &*name_ident.to_string() {
|
||||
"relaxed_type_check" => {
|
||||
ExprOrPath::RelaxedTypeCheck(content.parse::<LitBool>()?.value)
|
||||
}
|
||||
"repr" => ExprOrPath::Repr(content.parse::<Type>()?),
|
||||
"space" => ExprOrPath::Space(content.parse::<Expr>()?),
|
||||
"dst" => {
|
||||
let ident = content.parse::<LitBool>()?;
|
||||
ExprOrPath::Dst(ident.value)
|
||||
}
|
||||
name => {
|
||||
return Err(syn::Error::new(
|
||||
name_ident.span(),
|
||||
format!("Unexpected key `{}`, expected `repr` or `space", name),
|
||||
))
|
||||
}
|
||||
}
|
||||
} else {
|
||||
return Err(lookahead.error());
|
||||
})
|
||||
})?;
|
||||
let mut repr = None;
|
||||
let mut type_ = None;
|
||||
let mut space = None;
|
||||
let mut is_dst = None;
|
||||
let mut relaxed_type_check = false;
|
||||
for exp_or_path in all_fields {
|
||||
match exp_or_path {
|
||||
ExprOrPath::Repr(r) => repr = Some(r),
|
||||
ExprOrPath::Type(t) => type_ = Some(t),
|
||||
ExprOrPath::Space(s) => space = Some(s),
|
||||
ExprOrPath::Dst(x) => is_dst = Some(x),
|
||||
ExprOrPath::RelaxedTypeCheck(relaxed) => relaxed_type_check = relaxed,
|
||||
}
|
||||
}
|
||||
Ok((repr.unwrap(), type_, space, is_dst, relaxed_type_check))
|
||||
}
|
||||
|
||||
fn parse_basic(input: &syn::parse::ParseBuffer) -> syn::Result<Type> {
|
||||
input.parse::<Type>()
|
||||
}
|
||||
|
||||
fn emit_visit(
|
||||
&self,
|
||||
parent_type: &Option<Option<Expr>>,
|
||||
parent_space: &Option<Expr>,
|
||||
is_ident: bool,
|
||||
) -> TokenStream {
|
||||
self.emit_visit_impl(parent_type, parent_space, is_ident, false)
|
||||
}
|
||||
|
||||
fn emit_visit_mut(
|
||||
&self,
|
||||
parent_type: &Option<Option<Expr>>,
|
||||
parent_space: &Option<Expr>,
|
||||
is_ident: bool,
|
||||
) -> TokenStream {
|
||||
self.emit_visit_impl(parent_type, parent_space, is_ident, true)
|
||||
}
|
||||
|
||||
fn emit_visit_impl(
|
||||
&self,
|
||||
parent_type: &Option<Option<Expr>>,
|
||||
parent_space: &Option<Expr>,
|
||||
is_ident: bool,
|
||||
is_mut: bool,
|
||||
) -> TokenStream {
|
||||
let (is_typeless, type_) = match (self.type_.as_ref(), parent_type) {
|
||||
(Some(type_), _) => (false, Some(type_)),
|
||||
(None, None) => panic!("No type set"),
|
||||
(None, Some(None)) => (true, None),
|
||||
(None, Some(Some(type_))) => (false, Some(type_)),
|
||||
};
|
||||
let space = self
|
||||
.space
|
||||
.as_ref()
|
||||
.or(parent_space.as_ref())
|
||||
.map(|space| quote! { #space })
|
||||
.unwrap_or_else(|| quote! { StateSpace::Reg });
|
||||
let is_dst = self.is_dst;
|
||||
let relaxed_type_check = self.relaxed_type_check;
|
||||
let name = &self.name;
|
||||
let type_space = if is_typeless {
|
||||
quote! {
|
||||
let type_space = None;
|
||||
}
|
||||
} else {
|
||||
quote! {
|
||||
let type_ = #type_;
|
||||
let space = #space;
|
||||
let type_space = Some((std::borrow::Borrow::<Type>::borrow(&type_), space));
|
||||
}
|
||||
};
|
||||
if is_ident {
|
||||
if is_mut {
|
||||
quote! {
|
||||
{
|
||||
#type_space
|
||||
visitor.visit_ident(&mut arguments.#name, type_space, #is_dst, #relaxed_type_check)?;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
quote! {
|
||||
{
|
||||
#type_space
|
||||
visitor.visit_ident(& arguments.#name, type_space, #is_dst, #relaxed_type_check)?;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
let (operand_fn, arguments_name) = if is_mut {
|
||||
(
|
||||
quote! {
|
||||
VisitOperand::visit_mut
|
||||
},
|
||||
quote! {
|
||||
&mut arguments.#name
|
||||
},
|
||||
)
|
||||
} else {
|
||||
(
|
||||
quote! {
|
||||
VisitOperand::visit
|
||||
},
|
||||
quote! {
|
||||
& arguments.#name
|
||||
},
|
||||
)
|
||||
};
|
||||
quote! {{
|
||||
#type_space
|
||||
#operand_fn(#arguments_name, |x| visitor.visit(x, type_space, #is_dst, #relaxed_type_check))?;
|
||||
}}
|
||||
}
|
||||
}
|
||||
|
||||
fn emit_visit_map(
|
||||
&self,
|
||||
parent_type: &Option<Option<Expr>>,
|
||||
parent_space: &Option<Expr>,
|
||||
is_ident: bool,
|
||||
) -> TokenStream {
|
||||
let (is_typeless, type_) = match (self.type_.as_ref(), parent_type) {
|
||||
(Some(type_), _) => (false, Some(type_)),
|
||||
(None, None) => panic!("No type set"),
|
||||
(None, Some(None)) => (true, None),
|
||||
(None, Some(Some(type_))) => (false, Some(type_)),
|
||||
};
|
||||
let space = self
|
||||
.space
|
||||
.as_ref()
|
||||
.or(parent_space.as_ref())
|
||||
.map(|space| quote! { #space })
|
||||
.unwrap_or_else(|| quote! { StateSpace::Reg });
|
||||
let is_dst = self.is_dst;
|
||||
let relaxed_type_check = self.relaxed_type_check;
|
||||
let name = &self.name;
|
||||
let type_space = if is_typeless {
|
||||
quote! {
|
||||
let type_space = None;
|
||||
}
|
||||
} else {
|
||||
quote! {
|
||||
let type_ = #type_;
|
||||
let space = #space;
|
||||
let type_space = Some((std::borrow::Borrow::<Type>::borrow(&type_), space));
|
||||
}
|
||||
};
|
||||
let map_call = if is_ident {
|
||||
quote! {
|
||||
visitor.visit_ident(arguments.#name, type_space, #is_dst, #relaxed_type_check)?
|
||||
}
|
||||
} else {
|
||||
quote! {
|
||||
MapOperand::map(arguments.#name, |x| visitor.visit(x, type_space, #is_dst, #relaxed_type_check))?
|
||||
}
|
||||
};
|
||||
quote! {
|
||||
let #name = {
|
||||
#type_space
|
||||
#map_call
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
fn is_dst(name: &Ident) -> syn::Result<bool> {
|
||||
if name.to_string().starts_with("dst") {
|
||||
Ok(true)
|
||||
} else if name.to_string().starts_with("src") {
|
||||
Ok(false)
|
||||
} else {
|
||||
return Err(syn::Error::new(
|
||||
name.span(),
|
||||
format!(
|
||||
"Could not guess if `{}` is a read or write argument. Name should start with `dst` or `src`",
|
||||
name
|
||||
),
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
fn emit_field(&self, vis: &Option<Visibility>) -> TokenStream {
|
||||
let name = &self.name;
|
||||
let type_ = &self.repr;
|
||||
quote! {
|
||||
#vis #name: #type_
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Parse for ArgumentField {
|
||||
fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
|
||||
let name = input.parse::<Ident>()?;
|
||||
|
||||
input.parse::<Token![:]>()?;
|
||||
let lookahead = input.lookahead1();
|
||||
let (repr, type_, space, is_dst, relaxed_type_check) = if lookahead.peek(token::Brace) {
|
||||
Self::parse_block(input)?
|
||||
} else if lookahead.peek(syn::Ident) {
|
||||
(Self::parse_basic(input)?, None, None, None, false)
|
||||
} else {
|
||||
return Err(lookahead.error());
|
||||
};
|
||||
let is_dst = match is_dst {
|
||||
Some(x) => x,
|
||||
None => Self::is_dst(&name)?,
|
||||
};
|
||||
Ok(Self {
|
||||
name,
|
||||
is_dst,
|
||||
repr,
|
||||
type_,
|
||||
space,
|
||||
relaxed_type_check
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
enum ExprOrPath {
|
||||
Repr(Type),
|
||||
Type(Expr),
|
||||
Space(Expr),
|
||||
Dst(bool),
|
||||
RelaxedTypeCheck(bool),
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use proc_macro2::Span;
|
||||
use quote::{quote, ToTokens};
|
||||
|
||||
fn to_string(x: impl ToTokens) -> String {
|
||||
quote! { #x }.to_string()
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_argument_field_basic() {
|
||||
let input = quote! {
|
||||
dst: P::Operand
|
||||
};
|
||||
let arg = syn::parse2::<ArgumentField>(input).unwrap();
|
||||
assert_eq!("dst", arg.name.to_string());
|
||||
assert_eq!("P :: Operand", to_string(arg.repr));
|
||||
assert!(matches!(arg.type_, None));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_argument_field_block() {
|
||||
let input = quote! {
|
||||
dst: {
|
||||
type: ScalarType::U32,
|
||||
space: StateSpace::Global,
|
||||
repr: P::Operand,
|
||||
}
|
||||
};
|
||||
let arg = syn::parse2::<ArgumentField>(input).unwrap();
|
||||
assert_eq!("dst", arg.name.to_string());
|
||||
assert_eq!("ScalarType :: U32", to_string(arg.type_.unwrap()));
|
||||
assert_eq!("StateSpace :: Global", to_string(arg.space.unwrap()));
|
||||
assert_eq!("P :: Operand", to_string(arg.repr));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_argument_field_block_untyped() {
|
||||
let input = quote! {
|
||||
dst: {
|
||||
repr: P::Operand,
|
||||
}
|
||||
};
|
||||
let arg = syn::parse2::<ArgumentField>(input).unwrap();
|
||||
assert_eq!("dst", arg.name.to_string());
|
||||
assert_eq!("P :: Operand", to_string(arg.repr));
|
||||
assert!(matches!(arg.type_, None));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_variant_complex() {
|
||||
let input = quote! {
|
||||
Ld {
|
||||
type: ScalarType::U32,
|
||||
space: StateSpace::Global,
|
||||
data: LdDetails,
|
||||
arguments<P>: {
|
||||
dst: {
|
||||
repr: P::Operand,
|
||||
type: ScalarType::U32,
|
||||
space: StateSpace::Shared,
|
||||
},
|
||||
src: P::Operand,
|
||||
},
|
||||
}
|
||||
};
|
||||
let variant = syn::parse2::<InstructionVariant>(input).unwrap();
|
||||
assert_eq!("Ld", variant.name.to_string());
|
||||
assert_eq!("ScalarType :: U32", to_string(variant.type_.unwrap()));
|
||||
assert_eq!("StateSpace :: Global", to_string(variant.space.unwrap()));
|
||||
assert_eq!("LdDetails", to_string(variant.data.unwrap()));
|
||||
let arguments = if let Some(Arguments::Def(a)) = variant.arguments {
|
||||
a
|
||||
} else {
|
||||
panic!()
|
||||
};
|
||||
assert_eq!("P", to_string(arguments.generic));
|
||||
let mut fields = arguments.fields.into_iter();
|
||||
let dst = fields.next().unwrap();
|
||||
assert_eq!("P :: Operand", to_string(dst.repr));
|
||||
assert_eq!("ScalarType :: U32", to_string(dst.type_));
|
||||
assert_eq!("StateSpace :: Shared", to_string(dst.space));
|
||||
let src = fields.next().unwrap();
|
||||
assert_eq!("P :: Operand", to_string(src.repr));
|
||||
assert!(matches!(src.type_, None));
|
||||
assert!(matches!(src.space, None));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn visit_variant_empty() {
|
||||
let input = quote! {
|
||||
Ret {
|
||||
data: RetData
|
||||
}
|
||||
};
|
||||
let variant = syn::parse2::<InstructionVariant>(input).unwrap();
|
||||
let mut output = TokenStream::new();
|
||||
variant.emit_visit(&Ident::new("Instruction", Span::call_site()), &mut output);
|
||||
assert_eq!(output.to_string(), "Instruction :: Ret { .. } => { }");
|
||||
}
|
||||
}
|
844
ptx_parser_macros_impl/src/parser.rs
Normal file
844
ptx_parser_macros_impl/src/parser.rs
Normal file
|
@ -0,0 +1,844 @@
|
|||
use proc_macro2::Span;
|
||||
use proc_macro2::TokenStream;
|
||||
use quote::quote;
|
||||
use quote::ToTokens;
|
||||
use rustc_hash::FxHashMap;
|
||||
use std::fmt::Write;
|
||||
use syn::bracketed;
|
||||
use syn::parse::Peek;
|
||||
use syn::punctuated::Punctuated;
|
||||
use syn::spanned::Spanned;
|
||||
use syn::LitInt;
|
||||
use syn::Type;
|
||||
use syn::{braced, parse::Parse, token, Ident, ItemEnum, Token};
|
||||
|
||||
pub struct ParseDefinitions {
|
||||
pub token_type: ItemEnum,
|
||||
pub additional_enums: FxHashMap<Ident, ItemEnum>,
|
||||
pub definitions: Vec<OpcodeDefinition>,
|
||||
}
|
||||
|
||||
impl Parse for ParseDefinitions {
|
||||
fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
|
||||
let token_type = input.parse::<ItemEnum>()?;
|
||||
let mut additional_enums = FxHashMap::default();
|
||||
while input.peek(Token![#]) {
|
||||
let enum_ = input.parse::<ItemEnum>()?;
|
||||
additional_enums.insert(enum_.ident.clone(), enum_);
|
||||
}
|
||||
let mut definitions = Vec::new();
|
||||
while !input.is_empty() {
|
||||
definitions.push(input.parse::<OpcodeDefinition>()?);
|
||||
}
|
||||
Ok(Self {
|
||||
token_type,
|
||||
additional_enums,
|
||||
definitions,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
pub struct OpcodeDefinition(pub Patterns, pub Vec<Rule>);
|
||||
|
||||
impl Parse for OpcodeDefinition {
|
||||
fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
|
||||
let patterns = input.parse::<Patterns>()?;
|
||||
let mut rules = Vec::new();
|
||||
while Rule::peek(input) {
|
||||
rules.push(input.parse::<Rule>()?);
|
||||
input.parse::<Token![;]>()?;
|
||||
}
|
||||
Ok(Self(patterns, rules))
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Patterns(pub Vec<(OpcodeDecl, CodeBlock)>);
|
||||
|
||||
impl Parse for Patterns {
|
||||
fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
|
||||
let mut result = Vec::new();
|
||||
loop {
|
||||
if !OpcodeDecl::peek(input) {
|
||||
break;
|
||||
}
|
||||
let decl = input.parse::<OpcodeDecl>()?;
|
||||
let code_block = input.parse::<CodeBlock>()?;
|
||||
result.push((decl, code_block))
|
||||
}
|
||||
Ok(Self(result))
|
||||
}
|
||||
}
|
||||
|
||||
pub struct OpcodeDecl(pub Instruction, pub Arguments);
|
||||
|
||||
impl OpcodeDecl {
|
||||
fn peek(input: syn::parse::ParseStream) -> bool {
|
||||
Instruction::peek(input) && !input.peek2(Token![=])
|
||||
}
|
||||
}
|
||||
|
||||
impl Parse for OpcodeDecl {
|
||||
fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
|
||||
Ok(Self(
|
||||
input.parse::<Instruction>()?,
|
||||
input.parse::<Arguments>()?,
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
pub struct CodeBlock {
|
||||
pub special: bool,
|
||||
pub code: proc_macro2::Group,
|
||||
}
|
||||
|
||||
impl Parse for CodeBlock {
|
||||
fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
|
||||
let lookahead = input.lookahead1();
|
||||
let (special, code) = if lookahead.peek(Token![<]) {
|
||||
input.parse::<Token![<]>()?;
|
||||
input.parse::<Token![=]>()?;
|
||||
//input.parse::<Token![>]>()?;
|
||||
(true, input.parse::<proc_macro2::Group>()?)
|
||||
} else if lookahead.peek(Token![=]) {
|
||||
input.parse::<Token![=]>()?;
|
||||
input.parse::<Token![>]>()?;
|
||||
(false, input.parse::<proc_macro2::Group>()?)
|
||||
} else {
|
||||
return Err(lookahead.error());
|
||||
};
|
||||
Ok(Self { special, code })
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Rule {
|
||||
pub modifier: Option<DotModifier>,
|
||||
pub type_: Option<Type>,
|
||||
pub alternatives: Vec<DotModifier>,
|
||||
}
|
||||
|
||||
impl Rule {
|
||||
fn peek(input: syn::parse::ParseStream) -> bool {
|
||||
DotModifier::peek(input)
|
||||
|| (input.peek(Ident) && input.peek2(Token![=]) && !input.peek3(Token![>]))
|
||||
}
|
||||
|
||||
fn parse_alternatives(input: syn::parse::ParseStream) -> syn::Result<Vec<DotModifier>> {
|
||||
let mut result = Vec::new();
|
||||
Self::parse_with_alternative(input, &mut result)?;
|
||||
loop {
|
||||
if !input.peek(Token![,]) {
|
||||
break;
|
||||
}
|
||||
input.parse::<Token![,]>()?;
|
||||
Self::parse_with_alternative(input, &mut result)?;
|
||||
}
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
fn parse_with_alternative(
|
||||
input: &syn::parse::ParseBuffer,
|
||||
result: &mut Vec<DotModifier>,
|
||||
) -> Result<(), syn::Error> {
|
||||
input.parse::<Token![.]>()?;
|
||||
let part1 = input.parse::<IdentLike>()?;
|
||||
if input.peek(token::Brace) {
|
||||
result.push(DotModifier {
|
||||
part1: part1.clone(),
|
||||
part2: None,
|
||||
});
|
||||
let suffix_content;
|
||||
braced!(suffix_content in input);
|
||||
let suffixes = Punctuated::<IdentOrTypeSuffix, Token![,]>::parse_separated_nonempty(
|
||||
&suffix_content,
|
||||
)?;
|
||||
for part2 in suffixes {
|
||||
result.push(DotModifier {
|
||||
part1: part1.clone(),
|
||||
part2: Some(part2),
|
||||
});
|
||||
}
|
||||
} else if IdentOrTypeSuffix::peek(input) {
|
||||
let part2 = Some(IdentOrTypeSuffix::parse(input)?);
|
||||
result.push(DotModifier { part1, part2 });
|
||||
} else {
|
||||
result.push(DotModifier { part1, part2: None });
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Eq, Hash, Clone)]
|
||||
struct IdentOrTypeSuffix(IdentLike);
|
||||
|
||||
impl IdentOrTypeSuffix {
|
||||
fn span(&self) -> Span {
|
||||
self.0.span()
|
||||
}
|
||||
|
||||
fn peek(input: syn::parse::ParseStream) -> bool {
|
||||
input.peek(Token![::])
|
||||
}
|
||||
}
|
||||
|
||||
impl ToTokens for IdentOrTypeSuffix {
|
||||
fn to_tokens(&self, tokens: &mut TokenStream) {
|
||||
let ident = &self.0;
|
||||
quote! { :: #ident }.to_tokens(tokens)
|
||||
}
|
||||
}
|
||||
|
||||
impl Parse for IdentOrTypeSuffix {
|
||||
fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
|
||||
input.parse::<Token![::]>()?;
|
||||
Ok(Self(input.parse::<IdentLike>()?))
|
||||
}
|
||||
}
|
||||
|
||||
impl Parse for Rule {
|
||||
fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
|
||||
let (modifier, type_) = if DotModifier::peek(input) {
|
||||
let modifier = Some(input.parse::<DotModifier>()?);
|
||||
if input.peek(Token![:]) {
|
||||
input.parse::<Token![:]>()?;
|
||||
(modifier, Some(input.parse::<Type>()?))
|
||||
} else {
|
||||
(modifier, None)
|
||||
}
|
||||
} else {
|
||||
(None, Some(input.parse::<Type>()?))
|
||||
};
|
||||
input.parse::<Token![=]>()?;
|
||||
let content;
|
||||
braced!(content in input);
|
||||
let alternatives = Self::parse_alternatives(&content)?;
|
||||
Ok(Self {
|
||||
modifier,
|
||||
type_,
|
||||
alternatives,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Instruction {
|
||||
pub name: Ident,
|
||||
pub modifiers: Vec<MaybeDotModifier>,
|
||||
}
|
||||
impl Instruction {
|
||||
fn peek(input: syn::parse::ParseStream) -> bool {
|
||||
input.peek(Ident)
|
||||
}
|
||||
}
|
||||
|
||||
impl Parse for Instruction {
|
||||
fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
|
||||
let instruction = input.parse::<Ident>()?;
|
||||
let mut modifiers = Vec::new();
|
||||
loop {
|
||||
if !MaybeDotModifier::peek(input) {
|
||||
break;
|
||||
}
|
||||
modifiers.push(MaybeDotModifier::parse(input)?);
|
||||
}
|
||||
Ok(Self {
|
||||
name: instruction,
|
||||
modifiers,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
pub struct MaybeDotModifier {
|
||||
pub optional: bool,
|
||||
pub modifier: DotModifier,
|
||||
}
|
||||
|
||||
impl MaybeDotModifier {
|
||||
fn peek(input: syn::parse::ParseStream) -> bool {
|
||||
input.peek(token::Brace) || DotModifier::peek(input)
|
||||
}
|
||||
}
|
||||
|
||||
impl Parse for MaybeDotModifier {
|
||||
fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
|
||||
Ok(if input.peek(token::Brace) {
|
||||
let content;
|
||||
braced!(content in input);
|
||||
let modifier = DotModifier::parse(&content)?;
|
||||
Self {
|
||||
modifier,
|
||||
optional: true,
|
||||
}
|
||||
} else {
|
||||
let modifier = DotModifier::parse(input)?;
|
||||
Self {
|
||||
modifier,
|
||||
optional: false,
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Eq, Hash, Clone)]
|
||||
pub struct DotModifier {
|
||||
part1: IdentLike,
|
||||
part2: Option<IdentOrTypeSuffix>,
|
||||
}
|
||||
|
||||
impl std::fmt::Display for DotModifier {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, ".")?;
|
||||
self.part1.fmt(f)?;
|
||||
if let Some(ref part2) = self.part2 {
|
||||
write!(f, "::")?;
|
||||
part2.0.fmt(f)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Debug for DotModifier {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
std::fmt::Display::fmt(&self, f)
|
||||
}
|
||||
}
|
||||
|
||||
impl DotModifier {
|
||||
pub fn span(&self) -> Span {
|
||||
let part1 = self.part1.span();
|
||||
if let Some(ref part2) = self.part2 {
|
||||
part1.join(part2.span()).unwrap_or(part1)
|
||||
} else {
|
||||
part1
|
||||
}
|
||||
}
|
||||
|
||||
pub fn ident(&self) -> Ident {
|
||||
let mut result = String::new();
|
||||
write!(&mut result, "{}", self.part1).unwrap();
|
||||
if let Some(ref part2) = self.part2 {
|
||||
write!(&mut result, "_{}", part2.0).unwrap();
|
||||
} else {
|
||||
match self.part1 {
|
||||
IdentLike::Type(_) | IdentLike::Const(_) => result.push('_'),
|
||||
IdentLike::Ident(_) | IdentLike::Integer(_) => {}
|
||||
}
|
||||
}
|
||||
Ident::new(&result.to_ascii_lowercase(), self.span())
|
||||
}
|
||||
|
||||
pub fn variant_capitalized(&self) -> Ident {
|
||||
self.capitalized_impl(String::new())
|
||||
}
|
||||
|
||||
pub fn dot_capitalized(&self) -> Ident {
|
||||
self.capitalized_impl("Dot".to_string())
|
||||
}
|
||||
|
||||
fn capitalized_impl(&self, prefix: String) -> Ident {
|
||||
let mut temp = String::new();
|
||||
write!(&mut temp, "{}", &self.part1).unwrap();
|
||||
if let Some(IdentOrTypeSuffix(ref part2)) = self.part2 {
|
||||
write!(&mut temp, "_{}", part2).unwrap();
|
||||
}
|
||||
let mut result = prefix;
|
||||
let mut capitalize = true;
|
||||
for c in temp.chars() {
|
||||
if c == '_' {
|
||||
capitalize = true;
|
||||
continue;
|
||||
}
|
||||
// Special hack to emit `BF16`` instead of `Bf16``
|
||||
let c = if capitalize || c == 'f' && result.ends_with('B') {
|
||||
capitalize = false;
|
||||
c.to_ascii_uppercase()
|
||||
} else {
|
||||
c
|
||||
};
|
||||
result.push(c);
|
||||
}
|
||||
Ident::new(&result, self.span())
|
||||
}
|
||||
|
||||
pub fn tokens(&self) -> TokenStream {
|
||||
let part1 = &self.part1;
|
||||
let part2 = &self.part2;
|
||||
match self.part2 {
|
||||
None => quote! { . #part1 },
|
||||
Some(_) => quote! { . #part1 #part2 },
|
||||
}
|
||||
}
|
||||
|
||||
fn peek(input: syn::parse::ParseStream) -> bool {
|
||||
input.peek(Token![.])
|
||||
}
|
||||
}
|
||||
|
||||
impl Parse for DotModifier {
|
||||
fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
|
||||
input.parse::<Token![.]>()?;
|
||||
let part1 = input.parse::<IdentLike>()?;
|
||||
if IdentOrTypeSuffix::peek(input) {
|
||||
let part2 = Some(IdentOrTypeSuffix::parse(input)?);
|
||||
Ok(Self { part1, part2 })
|
||||
} else {
|
||||
Ok(Self { part1, part2: None })
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Eq, Hash, Clone)]
|
||||
enum IdentLike {
|
||||
Type(Token![type]),
|
||||
Const(Token![const]),
|
||||
Ident(Ident),
|
||||
Integer(LitInt),
|
||||
}
|
||||
|
||||
impl IdentLike {
|
||||
fn span(&self) -> Span {
|
||||
match self {
|
||||
IdentLike::Type(c) => c.span(),
|
||||
IdentLike::Const(t) => t.span(),
|
||||
IdentLike::Ident(i) => i.span(),
|
||||
IdentLike::Integer(l) => l.span(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Display for IdentLike {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
IdentLike::Type(_) => f.write_str("type"),
|
||||
IdentLike::Const(_) => f.write_str("const"),
|
||||
IdentLike::Ident(ident) => write!(f, "{}", ident),
|
||||
IdentLike::Integer(integer) => write!(f, "{}", integer),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ToTokens for IdentLike {
|
||||
fn to_tokens(&self, tokens: &mut TokenStream) {
|
||||
match self {
|
||||
IdentLike::Type(_) => quote! { type }.to_tokens(tokens),
|
||||
IdentLike::Const(_) => quote! { const }.to_tokens(tokens),
|
||||
IdentLike::Ident(ident) => quote! { #ident }.to_tokens(tokens),
|
||||
IdentLike::Integer(int) => quote! { #int }.to_tokens(tokens),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Parse for IdentLike {
|
||||
fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
|
||||
let lookahead = input.lookahead1();
|
||||
Ok(if lookahead.peek(Token![const]) {
|
||||
IdentLike::Const(input.parse::<Token![const]>()?)
|
||||
} else if lookahead.peek(Token![type]) {
|
||||
IdentLike::Type(input.parse::<Token![type]>()?)
|
||||
} else if lookahead.peek(Ident) {
|
||||
IdentLike::Ident(input.parse::<Ident>()?)
|
||||
} else if lookahead.peek(LitInt) {
|
||||
IdentLike::Integer(input.parse::<LitInt>()?)
|
||||
} else {
|
||||
return Err(lookahead.error());
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// Arguments decalaration can loook like this:
|
||||
// a{, b}
|
||||
// That's why we don't parse Arguments as Punctuated<Argument, Token![,]>
|
||||
#[derive(PartialEq, Eq)]
|
||||
pub struct Arguments(pub Vec<Argument>);
|
||||
|
||||
impl Parse for Arguments {
|
||||
fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
|
||||
let mut result = Vec::new();
|
||||
loop {
|
||||
if input.peek(Token![,]) {
|
||||
input.parse::<Token![,]>()?;
|
||||
}
|
||||
let mut optional = false;
|
||||
let mut can_be_negated = false;
|
||||
let mut pre_pipe = false;
|
||||
let ident;
|
||||
let lookahead = input.lookahead1();
|
||||
if lookahead.peek(token::Brace) {
|
||||
let content;
|
||||
braced!(content in input);
|
||||
let lookahead = content.lookahead1();
|
||||
if lookahead.peek(Token![!]) {
|
||||
content.parse::<Token![!]>()?;
|
||||
can_be_negated = true;
|
||||
ident = input.parse::<Ident>()?;
|
||||
} else if lookahead.peek(Token![,]) {
|
||||
optional = true;
|
||||
content.parse::<Token![,]>()?;
|
||||
ident = content.parse::<Ident>()?;
|
||||
} else {
|
||||
return Err(lookahead.error());
|
||||
}
|
||||
} else if lookahead.peek(token::Bracket) {
|
||||
let bracketed;
|
||||
bracketed!(bracketed in input);
|
||||
if bracketed.peek(Token![|]) {
|
||||
optional = true;
|
||||
bracketed.parse::<Token![|]>()?;
|
||||
pre_pipe = true;
|
||||
ident = bracketed.parse::<Ident>()?;
|
||||
} else {
|
||||
let mut sub_args = Self::parse(&bracketed)?;
|
||||
sub_args.0.first_mut().unwrap().pre_bracket = true;
|
||||
sub_args.0.last_mut().unwrap().post_bracket = true;
|
||||
if peek_brace_token(input, Token![.]) {
|
||||
let optional_suffix;
|
||||
braced!(optional_suffix in input);
|
||||
optional_suffix.parse::<Token![.]>()?;
|
||||
let unified_ident = optional_suffix.parse::<Ident>()?;
|
||||
if unified_ident.to_string() != "unified" {
|
||||
return Err(syn::Error::new(
|
||||
unified_ident.span(),
|
||||
format!("Exptected `unified`, got `{}`", unified_ident),
|
||||
));
|
||||
}
|
||||
for a in sub_args.0.iter_mut() {
|
||||
a.unified = true;
|
||||
}
|
||||
}
|
||||
result.extend(sub_args.0);
|
||||
continue;
|
||||
}
|
||||
} else if lookahead.peek(Ident) {
|
||||
ident = input.parse::<Ident>()?;
|
||||
} else if lookahead.peek(Token![|]) {
|
||||
input.parse::<Token![|]>()?;
|
||||
pre_pipe = true;
|
||||
ident = input.parse::<Ident>()?;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
result.push(Argument {
|
||||
optional,
|
||||
pre_pipe,
|
||||
can_be_negated,
|
||||
pre_bracket: false,
|
||||
ident,
|
||||
post_bracket: false,
|
||||
unified: false,
|
||||
});
|
||||
}
|
||||
Ok(Self(result))
|
||||
}
|
||||
}
|
||||
|
||||
// This is effectively input.peek(token::Brace) && input.peek2(Token![.])
|
||||
// input.peek2 is supposed to skip over next token, but it skips over whole
|
||||
// braced token group. Not sure if it's a bug
|
||||
fn peek_brace_token<T: Peek>(input: syn::parse::ParseStream, _t: T) -> bool {
|
||||
use syn::token::Token;
|
||||
let cursor = input.cursor();
|
||||
cursor
|
||||
.group(proc_macro2::Delimiter::Brace)
|
||||
.map_or(false, |(content, ..)| T::Token::peek(content))
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Eq)]
|
||||
pub struct Argument {
|
||||
pub optional: bool,
|
||||
pub pre_bracket: bool,
|
||||
pub pre_pipe: bool,
|
||||
pub can_be_negated: bool,
|
||||
pub ident: Ident,
|
||||
pub post_bracket: bool,
|
||||
pub unified: bool,
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::{Arguments, DotModifier, MaybeDotModifier};
|
||||
use quote::{quote, ToTokens};
|
||||
|
||||
#[test]
|
||||
fn parse_modifier_complex() {
|
||||
let input = quote! {
|
||||
.level::eviction_priority
|
||||
};
|
||||
let modifier = syn::parse2::<DotModifier>(input).unwrap();
|
||||
assert_eq!(
|
||||
". level :: eviction_priority",
|
||||
modifier.tokens().to_string()
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_modifier_optional() {
|
||||
let input = quote! {
|
||||
{ .level::eviction_priority }
|
||||
};
|
||||
let maybe_modifider = syn::parse2::<MaybeDotModifier>(input).unwrap();
|
||||
assert_eq!(
|
||||
". level :: eviction_priority",
|
||||
maybe_modifider.modifier.tokens().to_string()
|
||||
);
|
||||
assert!(maybe_modifider.optional);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_type_token() {
|
||||
let input = quote! {
|
||||
. type
|
||||
};
|
||||
let maybe_modifier = syn::parse2::<MaybeDotModifier>(input).unwrap();
|
||||
assert_eq!(". type", maybe_modifier.modifier.tokens().to_string());
|
||||
assert!(!maybe_modifier.optional);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn arguments_memory() {
|
||||
let input = quote! {
|
||||
[a], b
|
||||
};
|
||||
let arguments = syn::parse2::<Arguments>(input).unwrap();
|
||||
let a = &arguments.0[0];
|
||||
assert!(!a.optional);
|
||||
assert_eq!("a", a.ident.to_string());
|
||||
assert!(a.pre_bracket);
|
||||
assert!(!a.pre_pipe);
|
||||
assert!(a.post_bracket);
|
||||
assert!(!a.can_be_negated);
|
||||
let b = &arguments.0[1];
|
||||
assert!(!b.optional);
|
||||
assert_eq!("b", b.ident.to_string());
|
||||
assert!(!b.pre_bracket);
|
||||
assert!(!b.pre_pipe);
|
||||
assert!(!b.post_bracket);
|
||||
assert!(!b.can_be_negated);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn arguments_optional() {
|
||||
let input = quote! {
|
||||
b{, cache_policy}
|
||||
};
|
||||
let arguments = syn::parse2::<Arguments>(input).unwrap();
|
||||
let b = &arguments.0[0];
|
||||
assert!(!b.optional);
|
||||
assert_eq!("b", b.ident.to_string());
|
||||
assert!(!b.pre_bracket);
|
||||
assert!(!b.pre_pipe);
|
||||
assert!(!b.post_bracket);
|
||||
assert!(!b.can_be_negated);
|
||||
let cache_policy = &arguments.0[1];
|
||||
assert!(cache_policy.optional);
|
||||
assert_eq!("cache_policy", cache_policy.ident.to_string());
|
||||
assert!(!cache_policy.pre_bracket);
|
||||
assert!(!cache_policy.pre_pipe);
|
||||
assert!(!cache_policy.post_bracket);
|
||||
assert!(!cache_policy.can_be_negated);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn arguments_optional_pred() {
|
||||
let input = quote! {
|
||||
p[|q], a
|
||||
};
|
||||
let arguments = syn::parse2::<Arguments>(input).unwrap();
|
||||
assert_eq!(arguments.0.len(), 3);
|
||||
let p = &arguments.0[0];
|
||||
assert!(!p.optional);
|
||||
assert_eq!("p", p.ident.to_string());
|
||||
assert!(!p.pre_bracket);
|
||||
assert!(!p.pre_pipe);
|
||||
assert!(!p.post_bracket);
|
||||
assert!(!p.can_be_negated);
|
||||
let q = &arguments.0[1];
|
||||
assert!(q.optional);
|
||||
assert_eq!("q", q.ident.to_string());
|
||||
assert!(!q.pre_bracket);
|
||||
assert!(q.pre_pipe);
|
||||
assert!(!q.post_bracket);
|
||||
assert!(!q.can_be_negated);
|
||||
let a = &arguments.0[2];
|
||||
assert!(!a.optional);
|
||||
assert_eq!("a", a.ident.to_string());
|
||||
assert!(!a.pre_bracket);
|
||||
assert!(!a.pre_pipe);
|
||||
assert!(!a.post_bracket);
|
||||
assert!(!a.can_be_negated);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn arguments_optional_with_negate() {
|
||||
let input = quote! {
|
||||
b, {!}c
|
||||
};
|
||||
let arguments = syn::parse2::<Arguments>(input).unwrap();
|
||||
assert_eq!(arguments.0.len(), 2);
|
||||
let b = &arguments.0[0];
|
||||
assert!(!b.optional);
|
||||
assert_eq!("b", b.ident.to_string());
|
||||
assert!(!b.pre_bracket);
|
||||
assert!(!b.pre_pipe);
|
||||
assert!(!b.post_bracket);
|
||||
assert!(!b.can_be_negated);
|
||||
let c = &arguments.0[1];
|
||||
assert!(!c.optional);
|
||||
assert_eq!("c", c.ident.to_string());
|
||||
assert!(!c.pre_bracket);
|
||||
assert!(!c.pre_pipe);
|
||||
assert!(!c.post_bracket);
|
||||
assert!(c.can_be_negated);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn arguments_tex() {
|
||||
let input = quote! {
|
||||
d[|p], [a{, b}, c], dpdx, dpdy {, e}
|
||||
};
|
||||
let arguments = syn::parse2::<Arguments>(input).unwrap();
|
||||
assert_eq!(arguments.0.len(), 8);
|
||||
{
|
||||
let d = &arguments.0[0];
|
||||
assert!(!d.optional);
|
||||
assert_eq!("d", d.ident.to_string());
|
||||
assert!(!d.pre_bracket);
|
||||
assert!(!d.pre_pipe);
|
||||
assert!(!d.post_bracket);
|
||||
assert!(!d.can_be_negated);
|
||||
}
|
||||
{
|
||||
let p = &arguments.0[1];
|
||||
assert!(p.optional);
|
||||
assert_eq!("p", p.ident.to_string());
|
||||
assert!(!p.pre_bracket);
|
||||
assert!(p.pre_pipe);
|
||||
assert!(!p.post_bracket);
|
||||
assert!(!p.can_be_negated);
|
||||
}
|
||||
{
|
||||
let a = &arguments.0[2];
|
||||
assert!(!a.optional);
|
||||
assert_eq!("a", a.ident.to_string());
|
||||
assert!(a.pre_bracket);
|
||||
assert!(!a.pre_pipe);
|
||||
assert!(!a.post_bracket);
|
||||
assert!(!a.can_be_negated);
|
||||
}
|
||||
{
|
||||
let b = &arguments.0[3];
|
||||
assert!(b.optional);
|
||||
assert_eq!("b", b.ident.to_string());
|
||||
assert!(!b.pre_bracket);
|
||||
assert!(!b.pre_pipe);
|
||||
assert!(!b.post_bracket);
|
||||
assert!(!b.can_be_negated);
|
||||
}
|
||||
{
|
||||
let c = &arguments.0[4];
|
||||
assert!(!c.optional);
|
||||
assert_eq!("c", c.ident.to_string());
|
||||
assert!(!c.pre_bracket);
|
||||
assert!(!c.pre_pipe);
|
||||
assert!(c.post_bracket);
|
||||
assert!(!c.can_be_negated);
|
||||
}
|
||||
{
|
||||
let dpdx = &arguments.0[5];
|
||||
assert!(!dpdx.optional);
|
||||
assert_eq!("dpdx", dpdx.ident.to_string());
|
||||
assert!(!dpdx.pre_bracket);
|
||||
assert!(!dpdx.pre_pipe);
|
||||
assert!(!dpdx.post_bracket);
|
||||
assert!(!dpdx.can_be_negated);
|
||||
}
|
||||
{
|
||||
let dpdy = &arguments.0[6];
|
||||
assert!(!dpdy.optional);
|
||||
assert_eq!("dpdy", dpdy.ident.to_string());
|
||||
assert!(!dpdy.pre_bracket);
|
||||
assert!(!dpdy.pre_pipe);
|
||||
assert!(!dpdy.post_bracket);
|
||||
assert!(!dpdy.can_be_negated);
|
||||
}
|
||||
{
|
||||
let e = &arguments.0[7];
|
||||
assert!(e.optional);
|
||||
assert_eq!("e", e.ident.to_string());
|
||||
assert!(!e.pre_bracket);
|
||||
assert!(!e.pre_pipe);
|
||||
assert!(!e.post_bracket);
|
||||
assert!(!e.can_be_negated);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn rule_multi() {
|
||||
let input = quote! {
|
||||
.ss: StateSpace = { .global, .local, .param{::func}, .shared{::cta, ::cluster} }
|
||||
};
|
||||
let rule = syn::parse2::<super::Rule>(input).unwrap();
|
||||
assert_eq!(". ss", rule.modifier.unwrap().tokens().to_string());
|
||||
assert_eq!(
|
||||
"StateSpace",
|
||||
rule.type_.unwrap().to_token_stream().to_string()
|
||||
);
|
||||
let alts = rule
|
||||
.alternatives
|
||||
.iter()
|
||||
.map(|m| m.tokens().to_string())
|
||||
.collect::<Vec<_>>();
|
||||
assert_eq!(
|
||||
vec![
|
||||
". global",
|
||||
". local",
|
||||
". param",
|
||||
". param :: func",
|
||||
". shared",
|
||||
". shared :: cta",
|
||||
". shared :: cluster"
|
||||
],
|
||||
alts
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn rule_multi2() {
|
||||
let input = quote! {
|
||||
.cop: StCacheOperator = { .wb, .cg, .cs, .wt }
|
||||
};
|
||||
let rule = syn::parse2::<super::Rule>(input).unwrap();
|
||||
assert_eq!(". cop", rule.modifier.unwrap().tokens().to_string());
|
||||
assert_eq!(
|
||||
"StCacheOperator",
|
||||
rule.type_.unwrap().to_token_stream().to_string()
|
||||
);
|
||||
let alts = rule
|
||||
.alternatives
|
||||
.iter()
|
||||
.map(|m| m.tokens().to_string())
|
||||
.collect::<Vec<_>>();
|
||||
assert_eq!(vec![". wb", ". cg", ". cs", ". wt",], alts);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn args_unified() {
|
||||
let input = quote! {
|
||||
d, [a]{.unified}{, cache_policy}
|
||||
};
|
||||
let args = syn::parse2::<super::Arguments>(input).unwrap();
|
||||
let a = &args.0[1];
|
||||
assert!(!a.optional);
|
||||
assert_eq!("a", a.ident.to_string());
|
||||
assert!(a.pre_bracket);
|
||||
assert!(!a.pre_pipe);
|
||||
assert!(a.post_bracket);
|
||||
assert!(!a.can_be_negated);
|
||||
assert!(a.unified);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn special_block() {
|
||||
let input = quote! {
|
||||
bra <= { bra(stream) }
|
||||
};
|
||||
syn::parse2::<super::OpcodeDefinition>(input).unwrap();
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue