aboutsummaryrefslogtreecommitdiffhomepage
path: root/ptx_parser_macros_impl
diff options
context:
space:
mode:
Diffstat (limited to 'ptx_parser_macros_impl')
-rw-r--r--ptx_parser_macros_impl/Cargo.toml13
-rw-r--r--ptx_parser_macros_impl/src/lib.rs881
-rw-r--r--ptx_parser_macros_impl/src/parser.rs844
3 files changed, 1738 insertions, 0 deletions
diff --git a/ptx_parser_macros_impl/Cargo.toml b/ptx_parser_macros_impl/Cargo.toml
new file mode 100644
index 0000000..96f3b74
--- /dev/null
+++ b/ptx_parser_macros_impl/Cargo.toml
@@ -0,0 +1,13 @@
+[package]
+name = "ptx_parser_macros_impl"
+version = "0.0.0"
+authors = ["Andrzej Janik <[email protected]>"]
+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"
diff --git a/ptx_parser_macros_impl/src/lib.rs b/ptx_parser_macros_impl/src/lib.rs
new file mode 100644
index 0000000..2f2c87a
--- /dev/null
+++ b/ptx_parser_macros_impl/src/lib.rs
@@ -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 { .. } => { }");
+ }
+}
diff --git a/ptx_parser_macros_impl/src/parser.rs b/ptx_parser_macros_impl/src/parser.rs
new file mode 100644
index 0000000..f1cd738
--- /dev/null
+++ b/ptx_parser_macros_impl/src/parser.rs
@@ -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();
+ }
+}