mirror of
https://github.com/vosen/ZLUDA.git
synced 2025-07-18 09:46:21 +03:00
1024 lines
38 KiB
Rust
1024 lines
38 KiB
Rust
use either::Either;
|
|
use ptx_parser_macros_impl::parser;
|
|
use proc_macro2::{Span, TokenStream};
|
|
use quote::{format_ident, quote, ToTokens};
|
|
use rustc_hash::{FxHashMap, FxHashSet};
|
|
use std::{collections::hash_map, hash::Hash, iter, rc::Rc};
|
|
use syn::{
|
|
parse_macro_input, parse_quote, punctuated::Punctuated, Ident, ItemEnum, Token, Type, TypePath,
|
|
Variant,
|
|
};
|
|
|
|
// https://docs.nvidia.com/cuda/parallel-thread-execution/index.html#vectors
|
|
// https://docs.nvidia.com/cuda/parallel-thread-execution/index.html#fundamental-types
|
|
// https://docs.nvidia.com/cuda/parallel-thread-execution/index.html#alternate-floating-point-data-formats
|
|
// https://docs.nvidia.com/cuda/parallel-thread-execution/index.html#packed-floating-point-data-types
|
|
// https://docs.nvidia.com/cuda/parallel-thread-execution/index.html#packed-integer-data-types
|
|
#[rustfmt::skip]
|
|
static POSTFIX_MODIFIERS: &[&str] = &[
|
|
".v2", ".v4", ".v8",
|
|
".s8", ".s16", ".s16x2", ".s32", ".s64",
|
|
".u8", ".u16", ".u16x2", ".u32", ".u64",
|
|
".f16", ".f16x2", ".f32", ".f64",
|
|
".b8", ".b16", ".b32", ".b64", ".b128",
|
|
".pred",
|
|
".bf16", ".bf16x2", ".e4m3", ".e5m2", ".tf32",
|
|
];
|
|
|
|
static POSTFIX_TYPES: &[&str] = &["ScalarType", "VectorPrefix"];
|
|
|
|
struct OpcodeDefinitions {
|
|
definitions: Vec<SingleOpcodeDefinition>,
|
|
block_selection: Vec<Vec<(Option<Vec<parser::DotModifier>>, usize)>>,
|
|
}
|
|
|
|
impl OpcodeDefinitions {
|
|
fn new(opcode: &Ident, definitions: Vec<SingleOpcodeDefinition>) -> Self {
|
|
let mut selections = vec![None; definitions.len()];
|
|
let mut generation = 0usize;
|
|
loop {
|
|
let mut selected_something = false;
|
|
let unselected = selections
|
|
.iter()
|
|
.enumerate()
|
|
.filter_map(|(idx, s)| if s.is_none() { Some(idx) } else { None })
|
|
.collect::<Vec<_>>();
|
|
match &*unselected {
|
|
[] => break,
|
|
[remaining] => {
|
|
selections[*remaining] = Some((None, generation));
|
|
break;
|
|
}
|
|
_ => {}
|
|
}
|
|
'check_definitions: for i in unselected.iter().copied() {
|
|
let mut candidates = definitions[i]
|
|
.unordered_modifiers
|
|
.iter()
|
|
.chain(definitions[i].ordered_modifiers.iter())
|
|
.filter(|modifier| match modifier {
|
|
DotModifierRef::Direct {
|
|
optional: false, ..
|
|
}
|
|
| DotModifierRef::Indirect {
|
|
optional: false, ..
|
|
} => true,
|
|
_ => false,
|
|
})
|
|
.collect::<Vec<_>>();
|
|
candidates.sort_by_key(|modifier| match modifier {
|
|
DotModifierRef::Direct { .. } => 1,
|
|
DotModifierRef::Indirect { value, .. } => value.alternatives.len(),
|
|
});
|
|
// Attempt every modifier
|
|
'check_candidates: for candidate_modifier in candidates {
|
|
// check all other unselected patterns
|
|
for j in unselected.iter().copied() {
|
|
if i == j {
|
|
continue;
|
|
}
|
|
let candidate_set = match candidate_modifier {
|
|
DotModifierRef::Direct { value, .. } => Either::Left(iter::once(value)),
|
|
DotModifierRef::Indirect { value, .. } => {
|
|
Either::Right(value.alternatives.iter())
|
|
}
|
|
};
|
|
for candidate_value in candidate_set {
|
|
if definitions[j].possible_modifiers.contains(candidate_value) {
|
|
continue 'check_candidates;
|
|
}
|
|
}
|
|
}
|
|
// it's unique
|
|
let candidate_vec = match candidate_modifier {
|
|
DotModifierRef::Direct { value, .. } => vec![value.clone()],
|
|
DotModifierRef::Indirect { value, .. } => {
|
|
value.alternatives.iter().cloned().collect::<Vec<_>>()
|
|
}
|
|
};
|
|
selections[i] = Some((Some(candidate_vec), generation));
|
|
selected_something = true;
|
|
continue 'check_definitions;
|
|
}
|
|
}
|
|
if !selected_something {
|
|
panic!(
|
|
"Failed to generate pattern selection for `{}`. State: {:?}",
|
|
opcode,
|
|
selections.into_iter().rev().collect::<Vec<_>>()
|
|
);
|
|
}
|
|
generation += 1;
|
|
}
|
|
let mut block_selection = Vec::new();
|
|
for current_generation in 0usize.. {
|
|
let mut current_generation_definitions = Vec::new();
|
|
for (idx, selection) in selections.iter_mut().enumerate() {
|
|
match selection {
|
|
Some((modifier_set, generation)) => {
|
|
if *generation == current_generation {
|
|
current_generation_definitions.push((modifier_set.clone(), idx));
|
|
*selection = None;
|
|
}
|
|
}
|
|
None => {}
|
|
}
|
|
}
|
|
if current_generation_definitions.is_empty() {
|
|
break;
|
|
}
|
|
block_selection.push(current_generation_definitions);
|
|
}
|
|
#[cfg(debug_assertions)]
|
|
{
|
|
let selected = block_selection
|
|
.iter()
|
|
.map(|x| x.len())
|
|
.reduce(|x, y| x + y)
|
|
.unwrap();
|
|
if selected != definitions.len() {
|
|
panic!(
|
|
"Internal error when generating pattern selection for `{}`: {:?}",
|
|
opcode, &block_selection
|
|
);
|
|
}
|
|
}
|
|
Self {
|
|
definitions,
|
|
block_selection,
|
|
}
|
|
}
|
|
|
|
fn get_enum_types(
|
|
parse_definitions: &[parser::OpcodeDefinition],
|
|
) -> FxHashMap<syn::Type, FxHashSet<parser::DotModifier>> {
|
|
let mut result = FxHashMap::default();
|
|
for parser::OpcodeDefinition(_, rules) in parse_definitions.iter() {
|
|
for rule in rules {
|
|
let type_ = match rule.type_ {
|
|
Some(ref type_) => type_.clone(),
|
|
None => continue,
|
|
};
|
|
let insert_values = |set: &mut FxHashSet<_>| {
|
|
for value in rule.alternatives.iter().cloned() {
|
|
set.insert(value);
|
|
}
|
|
};
|
|
match result.entry(type_) {
|
|
hash_map::Entry::Occupied(mut entry) => insert_values(entry.get_mut()),
|
|
hash_map::Entry::Vacant(entry) => {
|
|
insert_values(entry.insert(FxHashSet::default()))
|
|
}
|
|
};
|
|
}
|
|
}
|
|
result
|
|
}
|
|
}
|
|
|
|
struct SingleOpcodeDefinition {
|
|
possible_modifiers: FxHashSet<parser::DotModifier>,
|
|
unordered_modifiers: Vec<DotModifierRef>,
|
|
ordered_modifiers: Vec<DotModifierRef>,
|
|
arguments: parser::Arguments,
|
|
code_block: parser::CodeBlock,
|
|
}
|
|
|
|
impl SingleOpcodeDefinition {
|
|
fn function_arguments_declarations(&self) -> impl Iterator<Item = TokenStream> + '_ {
|
|
self.unordered_modifiers
|
|
.iter()
|
|
.chain(self.ordered_modifiers.iter())
|
|
.filter_map(|modf| {
|
|
let type_ = modf.type_of();
|
|
type_.map(|t| {
|
|
let name = modf.ident();
|
|
quote! { #name : #t }
|
|
})
|
|
})
|
|
.chain(self.arguments.0.iter().map(|arg| {
|
|
let name = &arg.ident;
|
|
let arg_type = if arg.unified {
|
|
quote! { (ParsedOperandStr<'input>, bool) }
|
|
} else if arg.can_be_negated {
|
|
quote! { (bool, ParsedOperandStr<'input>) }
|
|
} else {
|
|
quote! { ParsedOperandStr<'input> }
|
|
};
|
|
if arg.optional {
|
|
quote! { #name : Option<#arg_type> }
|
|
} else {
|
|
quote! { #name : #arg_type }
|
|
}
|
|
}))
|
|
}
|
|
|
|
fn function_arguments(&self) -> impl Iterator<Item = TokenStream> + '_ {
|
|
self.unordered_modifiers
|
|
.iter()
|
|
.chain(self.ordered_modifiers.iter())
|
|
.filter_map(|modf| {
|
|
let type_ = modf.type_of();
|
|
type_.map(|_| {
|
|
let name = modf.ident();
|
|
quote! { #name }
|
|
})
|
|
})
|
|
.chain(self.arguments.0.iter().map(|arg| {
|
|
let name = &arg.ident;
|
|
quote! { #name }
|
|
}))
|
|
}
|
|
|
|
fn extract_and_insert(
|
|
definitions: &mut FxHashMap<Ident, Vec<SingleOpcodeDefinition>>,
|
|
special_definitions: &mut FxHashMap<Ident, proc_macro2::Group>,
|
|
parser::OpcodeDefinition(pattern_seq, rules): parser::OpcodeDefinition,
|
|
) {
|
|
let (mut named_rules, mut unnamed_rules) = gather_rules(rules);
|
|
let mut last_opcode = pattern_seq.0.last().unwrap().0 .0.name.clone();
|
|
for (opcode_decl, code_block) in pattern_seq.0.into_iter().rev() {
|
|
let current_opcode = opcode_decl.0.name.clone();
|
|
if last_opcode != current_opcode {
|
|
named_rules = FxHashMap::default();
|
|
unnamed_rules = FxHashMap::default();
|
|
}
|
|
let parser::OpcodeDecl(instruction, arguments) = opcode_decl;
|
|
if code_block.special {
|
|
if !instruction.modifiers.is_empty() || !arguments.0.is_empty() {
|
|
panic!(
|
|
"`{}`: no modifiers or arguments are allowed in parser definition.",
|
|
instruction.name
|
|
);
|
|
}
|
|
special_definitions.insert(instruction.name, code_block.code);
|
|
continue;
|
|
}
|
|
let mut possible_modifiers = FxHashSet::default();
|
|
let mut unordered_modifiers = instruction
|
|
.modifiers
|
|
.into_iter()
|
|
.map(|parser::MaybeDotModifier { optional, modifier }| {
|
|
match named_rules.get(&modifier) {
|
|
Some(alts) => {
|
|
possible_modifiers.extend(alts.alternatives.iter().cloned());
|
|
if alts.alternatives.len() == 1 && alts.type_.is_none() {
|
|
DotModifierRef::Direct {
|
|
optional,
|
|
value: alts.alternatives[0].clone(),
|
|
name: modifier,
|
|
type_: alts.type_.clone(),
|
|
}
|
|
} else {
|
|
DotModifierRef::Indirect {
|
|
optional,
|
|
value: alts.clone(),
|
|
name: modifier,
|
|
}
|
|
}
|
|
}
|
|
None => {
|
|
let type_ = unnamed_rules.get(&modifier).cloned();
|
|
possible_modifiers.insert(modifier.clone());
|
|
DotModifierRef::Direct {
|
|
optional,
|
|
value: modifier.clone(),
|
|
name: modifier,
|
|
type_,
|
|
}
|
|
}
|
|
}
|
|
})
|
|
.collect::<Vec<_>>();
|
|
let ordered_modifiers = Self::extract_ordered_modifiers(&mut unordered_modifiers);
|
|
let entry = Self {
|
|
possible_modifiers,
|
|
unordered_modifiers,
|
|
ordered_modifiers,
|
|
arguments,
|
|
code_block,
|
|
};
|
|
multihash_extend(definitions, current_opcode.clone(), entry);
|
|
last_opcode = current_opcode;
|
|
}
|
|
}
|
|
|
|
fn extract_ordered_modifiers(
|
|
unordered_modifiers: &mut Vec<DotModifierRef>,
|
|
) -> Vec<DotModifierRef> {
|
|
let mut result = Vec::new();
|
|
loop {
|
|
let is_ordered = match unordered_modifiers.last() {
|
|
Some(DotModifierRef::Direct { value, .. }) => {
|
|
let name = value.to_string();
|
|
POSTFIX_MODIFIERS.contains(&&*name)
|
|
}
|
|
Some(DotModifierRef::Indirect { value, .. }) => {
|
|
let type_ = value.type_.to_token_stream().to_string();
|
|
//panic!("{} {}", type_, POSTFIX_TYPES.contains(&&*type_));
|
|
POSTFIX_TYPES.contains(&&*type_)
|
|
}
|
|
None => break,
|
|
};
|
|
if is_ordered {
|
|
result.push(unordered_modifiers.pop().unwrap());
|
|
} else {
|
|
break;
|
|
}
|
|
}
|
|
if unordered_modifiers.len() == 1 {
|
|
result.push(unordered_modifiers.pop().unwrap());
|
|
}
|
|
result.reverse();
|
|
result
|
|
}
|
|
}
|
|
|
|
fn gather_rules(
|
|
rules: Vec<parser::Rule>,
|
|
) -> (
|
|
FxHashMap<parser::DotModifier, Rc<parser::Rule>>,
|
|
FxHashMap<parser::DotModifier, Type>,
|
|
) {
|
|
let mut named = FxHashMap::default();
|
|
let mut unnamed = FxHashMap::default();
|
|
for rule in rules {
|
|
match rule.modifier {
|
|
Some(ref modifier) => {
|
|
named.insert(modifier.clone(), Rc::new(rule));
|
|
}
|
|
None => unnamed.extend(
|
|
rule.alternatives
|
|
.into_iter()
|
|
.map(|alt| (alt, rule.type_.as_ref().unwrap().clone())),
|
|
),
|
|
}
|
|
}
|
|
(named, unnamed)
|
|
}
|
|
|
|
#[proc_macro]
|
|
pub fn derive_parser(tokens: proc_macro::TokenStream) -> proc_macro::TokenStream {
|
|
let parse_definitions = parse_macro_input!(tokens as ptx_parser_macros_impl::parser::ParseDefinitions);
|
|
let mut definitions = FxHashMap::default();
|
|
let mut special_definitions = FxHashMap::default();
|
|
let types = OpcodeDefinitions::get_enum_types(&parse_definitions.definitions);
|
|
let enum_types_tokens = emit_enum_types(types, parse_definitions.additional_enums);
|
|
for definition in parse_definitions.definitions.into_iter() {
|
|
SingleOpcodeDefinition::extract_and_insert(
|
|
&mut definitions,
|
|
&mut special_definitions,
|
|
definition,
|
|
);
|
|
}
|
|
let definitions = definitions
|
|
.into_iter()
|
|
.map(|(k, v)| {
|
|
let v = OpcodeDefinitions::new(&k, v);
|
|
(k, v)
|
|
})
|
|
.collect::<FxHashMap<_, _>>();
|
|
let mut token_enum = parse_definitions.token_type;
|
|
let (all_opcode, all_modifier) = write_definitions_into_tokens(
|
|
&definitions,
|
|
special_definitions.keys(),
|
|
&mut token_enum.variants,
|
|
);
|
|
let token_impl = emit_parse_function(&token_enum.ident, &definitions, &special_definitions, all_opcode, all_modifier);
|
|
let tokens = quote! {
|
|
#enum_types_tokens
|
|
|
|
#token_enum
|
|
|
|
#token_impl
|
|
};
|
|
tokens.into()
|
|
}
|
|
|
|
fn emit_enum_types(
|
|
types: FxHashMap<syn::Type, FxHashSet<parser::DotModifier>>,
|
|
mut existing_enums: FxHashMap<Ident, ItemEnum>,
|
|
) -> TokenStream {
|
|
let token_types = types.into_iter().filter_map(|(type_, variants)| {
|
|
match type_ {
|
|
syn::Type::Path(TypePath {
|
|
qself: None,
|
|
ref path,
|
|
}) => {
|
|
if let Some(ident) = path.get_ident() {
|
|
if let Some(enum_) = existing_enums.get_mut(ident) {
|
|
enum_.variants.extend(variants.into_iter().map(|modifier| {
|
|
let ident = modifier.variant_capitalized();
|
|
let variant: syn::Variant = syn::parse_quote! {
|
|
#ident
|
|
};
|
|
variant
|
|
}));
|
|
return None;
|
|
}
|
|
}
|
|
}
|
|
_ => {}
|
|
}
|
|
let variants = variants.iter().map(|v| v.variant_capitalized());
|
|
Some(quote! {
|
|
#[derive(Copy, Clone, PartialEq, Eq, Hash)]
|
|
enum #type_ {
|
|
#(#variants),*
|
|
}
|
|
})
|
|
});
|
|
let mut result = TokenStream::new();
|
|
for tokens in token_types {
|
|
tokens.to_tokens(&mut result);
|
|
}
|
|
for (_, enum_) in existing_enums {
|
|
quote! { #enum_ }.to_tokens(&mut result);
|
|
}
|
|
result
|
|
}
|
|
|
|
fn emit_parse_function(
|
|
type_name: &Ident,
|
|
defs: &FxHashMap<Ident, OpcodeDefinitions>,
|
|
special_defs: &FxHashMap<Ident, proc_macro2::Group>,
|
|
all_opcode: Vec<&Ident>,
|
|
all_modifier: FxHashSet<&parser::DotModifier>,
|
|
) -> TokenStream {
|
|
use std::fmt::Write;
|
|
let fns_ = defs
|
|
.iter()
|
|
.map(|(opcode, defs)| {
|
|
defs.definitions.iter().enumerate().map(|(idx, def)| {
|
|
let mut fn_name = opcode.to_string();
|
|
write!(&mut fn_name, "_{}", idx).ok();
|
|
let fn_name = Ident::new(&fn_name, Span::call_site());
|
|
let code_block = &def.code_block.code;
|
|
let args = def.function_arguments_declarations();
|
|
quote! {
|
|
fn #fn_name<'input>(state: &mut PtxParserState, #(#args),* ) -> Instruction<ParsedOperandStr<'input>> #code_block
|
|
}
|
|
})
|
|
})
|
|
.flatten();
|
|
let selectors = defs.iter().map(|(opcode, def)| {
|
|
let opcode_variant = Ident::new(&capitalize(&opcode.to_string()), opcode.span());
|
|
let mut result = TokenStream::new();
|
|
let mut selectors = TokenStream::new();
|
|
quote! {
|
|
if false {
|
|
unsafe { std::hint::unreachable_unchecked() }
|
|
}
|
|
}
|
|
.to_tokens(&mut selectors);
|
|
let mut has_default_selector = false;
|
|
for selection_layer in def.block_selection.iter() {
|
|
for (selection_key, selected_definition) in selection_layer {
|
|
let def_parser = emit_definition_parser(type_name, (opcode,*selected_definition), &def.definitions[*selected_definition]);
|
|
match selection_key {
|
|
Some(selection_keys) => {
|
|
let selection_keys = selection_keys.iter().map(|k| k.dot_capitalized());
|
|
quote! {
|
|
else if false #(|| modifiers.iter().any(|(t, _)| *t == #type_name :: #selection_keys))* {
|
|
#def_parser
|
|
}
|
|
}
|
|
.to_tokens(&mut selectors);
|
|
}
|
|
None => {
|
|
has_default_selector = true;
|
|
quote! {
|
|
else {
|
|
#def_parser
|
|
}
|
|
}
|
|
.to_tokens(&mut selectors);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if !has_default_selector {
|
|
quote! {
|
|
else {
|
|
return Err(winnow::error::ErrMode::from_error_kind(stream, winnow::error::ErrorKind::Token))
|
|
}
|
|
}
|
|
.to_tokens(&mut selectors);
|
|
}
|
|
quote! {
|
|
#opcode_variant => {
|
|
let modifers_start = stream.checkpoint();
|
|
let modifiers = take_while(0.., |(t,_)| Token::modifier(t)).parse_next(stream)?;
|
|
#selectors
|
|
}
|
|
}
|
|
.to_tokens(&mut result);
|
|
result
|
|
}).chain(special_defs.iter().map(|(opcode, code)| {
|
|
let opcode_variant = Ident::new(&capitalize(&opcode.to_string()), opcode.span());
|
|
quote! {
|
|
#opcode_variant => { #code? }
|
|
}
|
|
}));
|
|
let opcodes = all_opcode.into_iter().map(|op_ident| {
|
|
let op = op_ident.to_string();
|
|
let variant = Ident::new(&capitalize(&op), op_ident.span());
|
|
let value = op;
|
|
quote! {
|
|
#type_name :: #variant => Some(#value),
|
|
}
|
|
});
|
|
let modifier_names = iter::once(Ident::new("DotUnified", Span::call_site()))
|
|
.chain(all_modifier.iter().map(|m| m.dot_capitalized()));
|
|
quote! {
|
|
impl<'input> #type_name<'input> {
|
|
fn opcode_text(self) -> Option<&'static str> {
|
|
match self {
|
|
#(#opcodes)*
|
|
_ => None
|
|
}
|
|
}
|
|
|
|
fn modifier(self) -> bool {
|
|
match self {
|
|
#(
|
|
#type_name :: #modifier_names => true,
|
|
)*
|
|
_ => false
|
|
}
|
|
}
|
|
}
|
|
|
|
#(#fns_)*
|
|
|
|
fn parse_instruction<'a, 'input>(stream: &mut PtxParser<'a, 'input>) -> winnow::error::PResult<Instruction<ParsedOperandStr<'input>>>
|
|
{
|
|
use winnow::Parser;
|
|
use winnow::token::*;
|
|
use winnow::combinator::*;
|
|
let opcode = any.parse_next(stream)?.0;
|
|
let modifiers_start = stream.checkpoint();
|
|
Ok(match opcode {
|
|
#(
|
|
#type_name :: #selectors
|
|
)*
|
|
_ => return Err(winnow::error::ErrMode::from_error_kind(stream, winnow::error::ErrorKind::Token))
|
|
})
|
|
}
|
|
}
|
|
}
|
|
|
|
fn emit_definition_parser(
|
|
token_type: &Ident,
|
|
(opcode, fn_idx): (&Ident, usize),
|
|
definition: &SingleOpcodeDefinition,
|
|
) -> TokenStream {
|
|
let return_error_ref = quote! {
|
|
return Err(winnow::error::ErrMode::from_error_kind(&stream, winnow::error::ErrorKind::Token))
|
|
};
|
|
let return_error = quote! {
|
|
return Err(winnow::error::ErrMode::from_error_kind(stream, winnow::error::ErrorKind::Token))
|
|
};
|
|
let ordered_parse_declarations = definition.ordered_modifiers.iter().map(|modifier| {
|
|
modifier.type_of().map(|type_| {
|
|
let name = modifier.ident();
|
|
quote! {
|
|
let #name : #type_;
|
|
}
|
|
})
|
|
});
|
|
let ordered_parse = definition.ordered_modifiers.iter().rev().map(|modifier| {
|
|
let arg_name = modifier.ident();
|
|
match modifier {
|
|
DotModifierRef::Direct { optional, value, type_: None, .. } => {
|
|
let variant = value.dot_capitalized();
|
|
if *optional {
|
|
quote! {
|
|
#arg_name = opt(any.verify(|(t, _)| *t == #token_type :: #variant)).parse_next(&mut stream)?.is_some();
|
|
}
|
|
} else {
|
|
quote! {
|
|
any.verify(|(t, _)| *t == #token_type :: #variant).parse_next(&mut stream)?;
|
|
}
|
|
}
|
|
}
|
|
DotModifierRef::Direct { optional: false, type_: Some(type_), name, value } => {
|
|
let variable = name.ident();
|
|
let variant = value.dot_capitalized();
|
|
let parsed_variant = value.variant_capitalized();
|
|
quote! {
|
|
any.verify(|(t, _)| *t == #token_type :: #variant).parse_next(&mut stream)?;
|
|
#variable = #type_ :: #parsed_variant;
|
|
}
|
|
}
|
|
DotModifierRef::Direct { optional: true, type_: Some(_), .. } => { todo!() }
|
|
DotModifierRef::Indirect { optional, value, .. } => {
|
|
let variants = value.alternatives.iter().map(|alt| {
|
|
let type_ = value.type_.as_ref().unwrap();
|
|
let token_variant = alt.dot_capitalized();
|
|
let parsed_variant = alt.variant_capitalized();
|
|
quote! {
|
|
#token_type :: #token_variant => #type_ :: #parsed_variant,
|
|
}
|
|
});
|
|
if *optional {
|
|
quote! {
|
|
#arg_name = opt(any.verify_map(|(tok, _)| {
|
|
Some(match tok {
|
|
#(#variants)*
|
|
_ => return None
|
|
})
|
|
})).parse_next(&mut stream)?;
|
|
}
|
|
} else {
|
|
quote! {
|
|
#arg_name = any.verify_map(|(tok, _)| {
|
|
Some(match tok {
|
|
#(#variants)*
|
|
_ => return None
|
|
})
|
|
}).parse_next(&mut stream)?;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
});
|
|
let unordered_parse_declarations = definition.unordered_modifiers.iter().map(|modifier| {
|
|
let name = modifier.ident();
|
|
let type_ = modifier.type_of_check();
|
|
quote! {
|
|
let mut #name : #type_ = std::default::Default::default();
|
|
}
|
|
});
|
|
let unordered_parse = definition
|
|
.unordered_modifiers
|
|
.iter()
|
|
.map(|modifier| match modifier {
|
|
DotModifierRef::Direct {
|
|
name,
|
|
value,
|
|
type_: None,
|
|
..
|
|
} => {
|
|
let name = name.ident();
|
|
let token_variant = value.dot_capitalized();
|
|
quote! {
|
|
#token_type :: #token_variant => {
|
|
if #name {
|
|
#return_error_ref;
|
|
}
|
|
#name = true;
|
|
}
|
|
}
|
|
}
|
|
DotModifierRef::Direct {
|
|
name,
|
|
value,
|
|
type_: Some(type_),
|
|
..
|
|
} => {
|
|
let variable = name.ident();
|
|
let token_variant = value.dot_capitalized();
|
|
let enum_variant = value.variant_capitalized();
|
|
quote! {
|
|
#token_type :: #token_variant => {
|
|
if #variable.is_some() {
|
|
#return_error_ref;
|
|
}
|
|
#variable = Some(#type_ :: #enum_variant);
|
|
}
|
|
}
|
|
}
|
|
DotModifierRef::Indirect { value, name, .. } => {
|
|
let variable = name.ident();
|
|
let type_ = value.type_.as_ref().unwrap();
|
|
let alternatives = value.alternatives.iter().map(|alt| {
|
|
let token_variant = alt.dot_capitalized();
|
|
let enum_variant = alt.variant_capitalized();
|
|
quote! {
|
|
#token_type :: #token_variant => {
|
|
if #variable.is_some() {
|
|
#return_error_ref;
|
|
}
|
|
#variable = Some(#type_ :: #enum_variant);
|
|
}
|
|
}
|
|
});
|
|
quote! {
|
|
#(#alternatives)*
|
|
}
|
|
}
|
|
});
|
|
let unordered_parse_validations =
|
|
definition
|
|
.unordered_modifiers
|
|
.iter()
|
|
.map(|modifier| match modifier {
|
|
DotModifierRef::Direct {
|
|
optional: false,
|
|
name,
|
|
type_: None,
|
|
..
|
|
} => {
|
|
let variable = name.ident();
|
|
quote! {
|
|
if !#variable {
|
|
#return_error;
|
|
}
|
|
}
|
|
}
|
|
DotModifierRef::Direct {
|
|
optional: false,
|
|
name,
|
|
type_: Some(_),
|
|
..
|
|
} => {
|
|
let variable = name.ident();
|
|
quote! {
|
|
let #variable = match #variable {
|
|
Some(x) => x,
|
|
None => #return_error
|
|
};
|
|
}
|
|
}
|
|
DotModifierRef::Indirect {
|
|
optional: false,
|
|
name,
|
|
..
|
|
} => {
|
|
let variable = name.ident();
|
|
quote! {
|
|
let #variable = match #variable {
|
|
Some(x) => x,
|
|
None => #return_error
|
|
};
|
|
}
|
|
}
|
|
DotModifierRef::Direct { optional: true, .. }
|
|
| DotModifierRef::Indirect { optional: true, .. } => TokenStream::new(),
|
|
});
|
|
let arguments_parse = definition.arguments.0.iter().enumerate().map(|(idx, arg)| {
|
|
let comma = if idx == 0 || arg.pre_pipe {
|
|
quote! { empty }
|
|
} else {
|
|
quote! { any.verify(|(t, _)| *t == #token_type::Comma).void() }
|
|
};
|
|
let pre_bracket = if arg.pre_bracket {
|
|
quote! {
|
|
any.verify(|(t, _)| *t == #token_type::LBracket).void()
|
|
}
|
|
} else {
|
|
quote! {
|
|
empty
|
|
}
|
|
};
|
|
let pre_pipe = if arg.pre_pipe {
|
|
quote! {
|
|
any.verify(|(t, _)| *t == #token_type::Pipe).void()
|
|
}
|
|
} else {
|
|
quote! {
|
|
empty
|
|
}
|
|
};
|
|
let can_be_negated = if arg.can_be_negated {
|
|
quote! {
|
|
opt(any.verify(|(t, _)| *t == #token_type::Not)).map(|o| o.is_some())
|
|
}
|
|
} else {
|
|
quote! {
|
|
empty
|
|
}
|
|
};
|
|
let operand = {
|
|
quote! {
|
|
ParsedOperandStr::parse
|
|
}
|
|
};
|
|
let post_bracket = if arg.post_bracket {
|
|
quote! {
|
|
any.verify(|(t, _)| *t == #token_type::RBracket).void()
|
|
}
|
|
} else {
|
|
quote! {
|
|
empty
|
|
}
|
|
};
|
|
let unified = if arg.unified {
|
|
quote! {
|
|
opt(any.verify(|(t, _)| *t == #token_type::DotUnified).void()).map(|u| u.is_some())
|
|
}
|
|
} else {
|
|
quote! {
|
|
empty
|
|
}
|
|
};
|
|
let pattern = quote! {
|
|
(#comma, #pre_bracket, #pre_pipe, #can_be_negated, #operand, #post_bracket, #unified)
|
|
};
|
|
let arg_name = &arg.ident;
|
|
if arg.unified && arg.can_be_negated {
|
|
panic!("TODO: argument can't be both prefixed by `!` and suffixed by `.unified`")
|
|
}
|
|
let inner_parser = if arg.unified {
|
|
quote! {
|
|
#pattern.map(|(_, _, _, _, name, _, unified)| (name, unified))
|
|
}
|
|
} else if arg.can_be_negated {
|
|
quote! {
|
|
#pattern.map(|(_, _, _, negated, name, _, _)| (negated, name))
|
|
}
|
|
} else {
|
|
quote! {
|
|
#pattern.map(|(_, _, _, _, name, _, _)| name)
|
|
}
|
|
};
|
|
if arg.optional {
|
|
quote! {
|
|
let #arg_name = opt(#inner_parser).parse_next(stream)?;
|
|
}
|
|
} else {
|
|
quote! {
|
|
let #arg_name = #inner_parser.parse_next(stream)?;
|
|
}
|
|
}
|
|
});
|
|
let fn_args = definition.function_arguments();
|
|
let fn_name = format_ident!("{}_{}", opcode, fn_idx);
|
|
let fn_call = quote! {
|
|
#fn_name(&mut stream.state, #(#fn_args),* )
|
|
};
|
|
quote! {
|
|
#(#unordered_parse_declarations)*
|
|
#(#ordered_parse_declarations)*
|
|
{
|
|
let mut stream = ReverseStream(modifiers);
|
|
#(#ordered_parse)*
|
|
let mut stream: &[_] = stream.0;
|
|
for (token, _) in stream.iter().cloned() {
|
|
match token {
|
|
#(#unordered_parse)*
|
|
_ => #return_error_ref
|
|
}
|
|
}
|
|
}
|
|
#(#unordered_parse_validations)*
|
|
#(#arguments_parse)*
|
|
#fn_call
|
|
}
|
|
}
|
|
|
|
fn write_definitions_into_tokens<'a>(
|
|
defs: &'a FxHashMap<Ident, OpcodeDefinitions>,
|
|
special_definitions: impl Iterator<Item = &'a Ident>,
|
|
variants: &mut Punctuated<Variant, Token![,]>,
|
|
) -> (Vec<&'a Ident>, FxHashSet<&'a parser::DotModifier>) {
|
|
let mut all_opcodes = Vec::new();
|
|
let mut all_modifiers = FxHashSet::default();
|
|
for (opcode, definitions) in defs.iter() {
|
|
all_opcodes.push(opcode);
|
|
let opcode_as_string = opcode.to_string();
|
|
let variant_name = Ident::new(&capitalize(&opcode_as_string), opcode.span());
|
|
let arg: Variant = syn::parse_quote! {
|
|
#[token(#opcode_as_string)]
|
|
#variant_name
|
|
};
|
|
variants.push(arg);
|
|
for definition in definitions.definitions.iter() {
|
|
for modifier in definition.possible_modifiers.iter() {
|
|
all_modifiers.insert(modifier);
|
|
}
|
|
}
|
|
}
|
|
for opcode in special_definitions {
|
|
all_opcodes.push(opcode);
|
|
let opcode_as_string = opcode.to_string();
|
|
let variant_name = Ident::new(&capitalize(&opcode_as_string), opcode.span());
|
|
let arg: Variant = syn::parse_quote! {
|
|
#[token(#opcode_as_string)]
|
|
#variant_name
|
|
};
|
|
variants.push(arg);
|
|
}
|
|
for modifier in all_modifiers.iter() {
|
|
let modifier_as_string = modifier.to_string();
|
|
let variant_name = modifier.dot_capitalized();
|
|
let arg: Variant = syn::parse_quote! {
|
|
#[token(#modifier_as_string)]
|
|
#variant_name
|
|
};
|
|
variants.push(arg);
|
|
}
|
|
variants.push(parse_quote! {
|
|
#[token(".unified")]
|
|
DotUnified
|
|
});
|
|
(all_opcodes, all_modifiers)
|
|
}
|
|
|
|
fn capitalize(s: &str) -> String {
|
|
let mut c = s.chars();
|
|
match c.next() {
|
|
None => String::new(),
|
|
Some(f) => f.to_uppercase().collect::<String>() + c.as_str(),
|
|
}
|
|
}
|
|
|
|
fn multihash_extend<K: Eq + Hash, V>(multimap: &mut FxHashMap<K, Vec<V>>, k: K, v: V) {
|
|
match multimap.entry(k) {
|
|
hash_map::Entry::Occupied(mut entry) => entry.get_mut().push(v),
|
|
hash_map::Entry::Vacant(entry) => {
|
|
entry.insert(vec![v]);
|
|
}
|
|
}
|
|
}
|
|
|
|
enum DotModifierRef {
|
|
Direct {
|
|
optional: bool,
|
|
value: parser::DotModifier,
|
|
name: parser::DotModifier,
|
|
type_: Option<Type>,
|
|
},
|
|
Indirect {
|
|
optional: bool,
|
|
name: parser::DotModifier,
|
|
value: Rc<parser::Rule>,
|
|
},
|
|
}
|
|
|
|
impl DotModifierRef {
|
|
fn ident(&self) -> Ident {
|
|
match self {
|
|
DotModifierRef::Direct { name, .. } => name.ident(),
|
|
DotModifierRef::Indirect { name, .. } => name.ident(),
|
|
}
|
|
}
|
|
|
|
fn type_of(&self) -> Option<syn::Type> {
|
|
Some(match self {
|
|
DotModifierRef::Direct {
|
|
optional: true,
|
|
type_: None,
|
|
..
|
|
} => syn::parse_quote! { bool },
|
|
DotModifierRef::Direct {
|
|
optional: false,
|
|
type_: None,
|
|
..
|
|
} => return None,
|
|
DotModifierRef::Direct {
|
|
optional: true,
|
|
type_: Some(type_),
|
|
..
|
|
} => syn::parse_quote! { Option<#type_> },
|
|
DotModifierRef::Direct {
|
|
optional: false,
|
|
type_: Some(type_),
|
|
..
|
|
} => type_.clone(),
|
|
DotModifierRef::Indirect {
|
|
optional, value, ..
|
|
} => {
|
|
let type_ = value
|
|
.type_
|
|
.as_ref()
|
|
.expect("Indirect modifer must have a type");
|
|
if *optional {
|
|
syn::parse_quote! { Option<#type_> }
|
|
} else {
|
|
type_.clone()
|
|
}
|
|
}
|
|
})
|
|
}
|
|
|
|
fn type_of_check(&self) -> syn::Type {
|
|
match self {
|
|
DotModifierRef::Direct { type_: None, .. } => syn::parse_quote! { bool },
|
|
DotModifierRef::Direct {
|
|
type_: Some(type_), ..
|
|
} => syn::parse_quote! { Option<#type_> },
|
|
DotModifierRef::Indirect { value, .. } => {
|
|
let type_ = value
|
|
.type_
|
|
.as_ref()
|
|
.expect("Indirect modifer must have a type");
|
|
syn::parse_quote! { Option<#type_> }
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
#[proc_macro]
|
|
pub fn generate_instruction_type(tokens: proc_macro::TokenStream) -> proc_macro::TokenStream {
|
|
let input = parse_macro_input!(tokens as ptx_parser_macros_impl::GenerateInstructionType);
|
|
let mut result = proc_macro2::TokenStream::new();
|
|
input.emit_arg_types(&mut result);
|
|
input.emit_instruction_type(&mut result);
|
|
input.emit_visit(&mut result);
|
|
input.emit_visit_mut(&mut result);
|
|
input.emit_visit_map(&mut result);
|
|
result.into()
|
|
}
|