From 6dc9776263a4108b3c1837af362f6794b977be5d Mon Sep 17 00:00:00 2001 From: Igor Matuszewski Date: Fri, 9 Feb 2024 14:05:30 +0100 Subject: [PATCH] Use model::VersionSpecifier instead of VersionQualityRange --- .../codegen/runtime/generator/src/parser.rs | 56 ++++++++---- .../runtime/generator/src/parser/grammar.rs | 2 - .../src/parser/grammar/constructor.rs | 60 ++----------- .../src/parser/grammar/parser_definition.rs | 3 +- .../src/parser/grammar/scanner_definition.rs | 29 +++---- .../src/parser/grammar/version_quality.rs | 13 --- .../src/parser/keyword_scanner_definition.rs | 4 +- .../generator/src/parser/parser_definition.rs | 87 +++++++++---------- .../src/parser/scanner_definition.rs | 2 +- .../runtime/generator/src/parser/trie.rs | 21 +++-- 10 files changed, 117 insertions(+), 160 deletions(-) delete mode 100644 crates/codegen/runtime/generator/src/parser/grammar/version_quality.rs diff --git a/crates/codegen/runtime/generator/src/parser.rs b/crates/codegen/runtime/generator/src/parser.rs index 54c226f026..65b7bbde93 100644 --- a/crates/codegen/runtime/generator/src/parser.rs +++ b/crates/codegen/runtime/generator/src/parser.rs @@ -3,7 +3,7 @@ use std::collections::{BTreeMap, BTreeSet}; use std::rc::Rc; -use codegen_language_definition::model::{Identifier, Language}; +use codegen_language_definition::model::{Identifier, Language, VersionSpecifier}; use quote::{format_ident, quote}; use semver::Version; use serde::Serialize; @@ -254,13 +254,21 @@ impl GrammarVisitor for ParserAccumulatorState { for def in scanner.definitions() { let versions = def.enabled.iter().chain(def.reserved.iter()); - self.referenced_versions.extend( - versions - .map(|vqr| &vqr.from) - // "Removed from 0.0.0" is an alias for "never"; it's never directly checked - .filter(|v| *v != &Version::new(0, 0, 0)) - .cloned(), - ); + for version in versions { + match version { + VersionSpecifier::Never => {} + VersionSpecifier::From { from } => { + self.referenced_versions.insert(from.clone()); + } + VersionSpecifier::Till { till } => { + self.referenced_versions.insert(till.clone()); + } + VersionSpecifier::Range { from, till } => { + self.referenced_versions.insert(from.clone()); + self.referenced_versions.insert(till.clone()); + } + } + } } } @@ -338,20 +346,38 @@ impl GrammarVisitor for ParserAccumulatorState { } fn scanner_definition_node_enter(&mut self, node: &ScannerDefinitionNode) { - if let ScannerDefinitionNode::Versioned(_, version_quality_ranges) = node { - for vqr in version_quality_ranges { - self.referenced_versions.insert(vqr.from.clone()); + if let ScannerDefinitionNode::Versioned(_, version_specifier) = node { + match version_specifier { + VersionSpecifier::Never => {} + VersionSpecifier::From { from } => { + self.referenced_versions.insert(from.clone()); + } + VersionSpecifier::Till { till } => { + self.referenced_versions.insert(till.clone()); + } + VersionSpecifier::Range { from, till } => { + self.referenced_versions.insert(from.clone()); + self.referenced_versions.insert(till.clone()); + } } } } fn parser_definition_node_enter(&mut self, node: &ParserDefinitionNode) { match node { - ParserDefinitionNode::Versioned(_, version_quality_ranges) => { - for vqr in version_quality_ranges { - self.referenced_versions.insert(vqr.from.clone()); + ParserDefinitionNode::Versioned(_, version_specifier) => match version_specifier { + VersionSpecifier::Never => {} + VersionSpecifier::From { from } => { + self.referenced_versions.insert(from.clone()); } - } + VersionSpecifier::Till { till } => { + self.referenced_versions.insert(till.clone()); + } + VersionSpecifier::Range { from, till } => { + self.referenced_versions.insert(from.clone()); + self.referenced_versions.insert(till.clone()); + } + }, ParserDefinitionNode::ScannerDefinition(scanner) => { self.top_level_scanner_names.insert(scanner.name().clone()); self.terminal_kinds.insert(scanner.name().clone()); diff --git a/crates/codegen/runtime/generator/src/parser/grammar.rs b/crates/codegen/runtime/generator/src/parser/grammar.rs index da3d44a938..38e074f88b 100644 --- a/crates/codegen/runtime/generator/src/parser/grammar.rs +++ b/crates/codegen/runtime/generator/src/parser/grammar.rs @@ -13,13 +13,11 @@ pub mod constructor; pub mod parser_definition; pub mod precedence_parser_definition; pub mod scanner_definition; -pub mod version_quality; pub mod visitor; pub use parser_definition::*; pub use precedence_parser_definition::*; pub use scanner_definition::*; -pub use version_quality::*; pub use visitor::*; pub struct Grammar { diff --git a/crates/codegen/runtime/generator/src/parser/grammar/constructor.rs b/crates/codegen/runtime/generator/src/parser/grammar/constructor.rs index d77a977b84..0d7256e9ea 100644 --- a/crates/codegen/runtime/generator/src/parser/grammar/constructor.rs +++ b/crates/codegen/runtime/generator/src/parser/grammar/constructor.rs @@ -10,10 +10,10 @@ use indexmap::IndexMap; use once_cell::sync::Lazy; use crate::parser::grammar::{ - DelimitedRecoveryTokenThreshold, Grammar, GrammarElement, KeywordScannerDefinition, - KeywordScannerDefinitionVersionedNode, Labeled, ParserDefinition, ParserDefinitionNode, - PrecedenceParserDefinition, PrecedenceParserDefinitionNode, ScannerDefinition, - ScannerDefinitionNode, TriviaParserDefinition, VersionQuality, VersionQualityRange, + DelimitedRecoveryTokenThreshold, Grammar, GrammarElement, KeywordScannerDefinition, Labeled, + ParserDefinition, ParserDefinitionNode, PrecedenceParserDefinition, + PrecedenceParserDefinitionNode, ScannerDefinition, ScannerDefinitionNode, + TriviaParserDefinition, }; impl Grammar { @@ -133,7 +133,7 @@ impl ScannerDefinition for NamedScanner { struct NamedKeywordScanner { name: Identifier, identifier_scanner_name: Identifier, - defs: Vec, + defs: Vec, } impl KeywordScannerDefinition for NamedKeywordScanner { @@ -141,7 +141,7 @@ impl KeywordScannerDefinition for NamedKeywordScanner { &self.name } - fn definitions(&self) -> &[KeywordScannerDefinitionVersionedNode] { + fn definitions(&self) -> &[model::KeywordDefinition] { &self.defs } @@ -237,37 +237,6 @@ impl ParserThunk { } } -fn enabled_to_range(spec: impl Into>) -> Vec { - let Some(spec) = spec.into() else { - return vec![]; - }; - - match spec { - model::VersionSpecifier::Never => vec![VersionQualityRange { - from: semver::Version::new(0, 0, 0), - quality: VersionQuality::Removed, - }], - model::VersionSpecifier::From { from } => vec![VersionQualityRange { - from, - quality: VersionQuality::Introduced, - }], - model::VersionSpecifier::Till { till } => vec![VersionQualityRange { - from: till, - quality: VersionQuality::Removed, - }], - model::VersionSpecifier::Range { from, till } => vec![ - VersionQualityRange { - from, - quality: VersionQuality::Introduced, - }, - VersionQualityRange { - from: till, - quality: VersionQuality::Removed, - }, - ], - } -} - struct ResolveCtx<'a> { items: &'a HashMap, Item)>, resolved: &'a mut HashMap, @@ -384,21 +353,10 @@ fn resolve_grammar_element(ident: &Identifier, ctx: &mut ResolveCtx<'_>) -> Gram def: resolve_token(item.deref().clone(), ctx), }, Item::Keyword { item } => { - let defs: Vec<_> = item - .definitions - .iter() - .cloned() - .map(|def| KeywordScannerDefinitionVersionedNode { - value: def.value, - enabled: enabled_to_range(def.enabled), - reserved: enabled_to_range(def.reserved), - }) - .collect(); - let kw_scanner = NamedKeywordScanner { name: ident.clone(), identifier_scanner_name: item.identifier.clone(), - defs, + defs: item.definitions.clone(), }; // Keywords are special scanners and are handled separately @@ -812,7 +770,7 @@ trait VersionWrapped { impl VersionWrapped for ParserDefinitionNode { fn versioned(self, enabled: Option) -> Self { if let Some(enabled) = enabled { - Self::Versioned(Box::new(self), enabled_to_range(enabled)) + Self::Versioned(Box::new(self), enabled) } else { self } @@ -822,7 +780,7 @@ impl VersionWrapped for ParserDefinitionNode { impl VersionWrapped for ScannerDefinitionNode { fn versioned(self, enabled: Option) -> Self { if let Some(enabled) = enabled { - Self::Versioned(Box::new(self), enabled_to_range(enabled)) + Self::Versioned(Box::new(self), enabled) } else { self } diff --git a/crates/codegen/runtime/generator/src/parser/grammar/parser_definition.rs b/crates/codegen/runtime/generator/src/parser/grammar/parser_definition.rs index 7af8f9dcc0..e9c6985de6 100644 --- a/crates/codegen/runtime/generator/src/parser/grammar/parser_definition.rs +++ b/crates/codegen/runtime/generator/src/parser/grammar/parser_definition.rs @@ -6,7 +6,6 @@ use codegen_language_definition::model::{self, Identifier}; use crate::parser::grammar::visitor::{GrammarVisitor, Visitable}; use crate::parser::grammar::{ KeywordScannerDefinitionRef, PrecedenceParserDefinitionRef, ScannerDefinitionRef, - VersionQualityRange, }; /// A named wrapper, used to give a name to a [`ParserDefinitionNode`]. @@ -76,7 +75,7 @@ impl From for DelimitedRecoveryTokenThreshold { #[derive(Clone, Debug)] pub enum ParserDefinitionNode { - Versioned(Box, Vec), + Versioned(Box, model::VersionSpecifier), Optional(Box), ZeroOrMore(Labeled>), OneOrMore(Labeled>), diff --git a/crates/codegen/runtime/generator/src/parser/grammar/scanner_definition.rs b/crates/codegen/runtime/generator/src/parser/grammar/scanner_definition.rs index 8fd12dc2a1..b964c67b01 100644 --- a/crates/codegen/runtime/generator/src/parser/grammar/scanner_definition.rs +++ b/crates/codegen/runtime/generator/src/parser/grammar/scanner_definition.rs @@ -3,7 +3,7 @@ use std::rc::Rc; use codegen_language_definition::model::{self, Identifier}; -use crate::parser::grammar::{GrammarVisitor, VersionQualityRange, Visitable}; +use crate::parser::grammar::{GrammarVisitor, Visitable}; pub trait ScannerDefinition: Debug { fn name(&self) -> &Identifier; @@ -21,7 +21,7 @@ impl Visitable for ScannerDefinitionRef { #[derive(Clone, Debug)] pub enum ScannerDefinitionNode { - Versioned(Box, Vec), + Versioned(Box, model::VersionSpecifier), Optional(Box), ZeroOrMore(Box), OneOrMore(Box), @@ -71,7 +71,7 @@ impl Visitable for ScannerDefinitionNode { pub trait KeywordScannerDefinition: Debug { fn name(&self) -> &Identifier; fn identifier_scanner(&self) -> &Identifier; - fn definitions(&self) -> &[KeywordScannerDefinitionVersionedNode]; + fn definitions(&self) -> &[model::KeywordDefinition]; } pub type KeywordScannerDefinitionRef = Rc; @@ -82,16 +82,6 @@ impl Visitable for KeywordScannerDefinitionRef { } } -#[derive(Debug)] -pub struct KeywordScannerDefinitionVersionedNode { - // Underlying keyword scanner (i.e. identifier scanner) - pub value: model::KeywordValue, - /// When the keyword scanner is enabled - pub enabled: Vec, - /// When the keyword is reserved, i.e. can't be used in other position (e.g. as a name) - pub reserved: Vec, -} - impl From for ScannerDefinitionNode { fn from(val: model::KeywordValue) -> Self { match val { @@ -121,7 +111,7 @@ impl KeywordScannerAtomic { /// Wraps the keyword scanner definition if it is a single atom value. pub fn try_from_def(def: &KeywordScannerDefinitionRef) -> Option { match def.definitions() { - [KeywordScannerDefinitionVersionedNode { + [model::KeywordDefinition { value: model::KeywordValue::Atom { .. }, .. }] => Some(Self(Rc::clone(def))), @@ -139,13 +129,16 @@ impl std::ops::Deref for KeywordScannerAtomic { } impl KeywordScannerAtomic { - pub fn definition(&self) -> &KeywordScannerDefinitionVersionedNode { - let def = &self.0.definitions().first(); - def.expect("KeywordScannerAtomic should have exactly one definition") + pub fn definition(&self) -> &model::KeywordDefinition { + self.0 + .definitions() + .first() + .expect("KeywordScannerAtomic should have exactly one definition") } + pub fn value(&self) -> &str { match self.definition() { - KeywordScannerDefinitionVersionedNode { + model::KeywordDefinition { value: model::KeywordValue::Atom { atom }, .. } => atom, diff --git a/crates/codegen/runtime/generator/src/parser/grammar/version_quality.rs b/crates/codegen/runtime/generator/src/parser/grammar/version_quality.rs deleted file mode 100644 index 37b4210567..0000000000 --- a/crates/codegen/runtime/generator/src/parser/grammar/version_quality.rs +++ /dev/null @@ -1,13 +0,0 @@ -use semver::Version; - -#[derive(Clone, Debug, Copy, PartialEq, Eq, strum_macros::Display)] -pub enum VersionQuality { - Introduced, - Removed, -} - -#[derive(Clone, Debug)] -pub struct VersionQualityRange { - pub from: Version, - pub quality: VersionQuality, -} diff --git a/crates/codegen/runtime/generator/src/parser/keyword_scanner_definition.rs b/crates/codegen/runtime/generator/src/parser/keyword_scanner_definition.rs index 06c3f5a6d5..f5e721845e 100644 --- a/crates/codegen/runtime/generator/src/parser/keyword_scanner_definition.rs +++ b/crates/codegen/runtime/generator/src/parser/keyword_scanner_definition.rs @@ -20,8 +20,8 @@ impl KeywordScannerDefinitionExtensions for KeywordScannerDefinitionRef { .iter() .map(|versioned_kw| { let scanner = versioned_kw.value.to_scanner_code(); - let enabled_cond = versioned_kw.enabled.as_bool_expr(); - let reserved_cond = versioned_kw.reserved.as_bool_expr(); + let enabled_cond = versioned_kw.enabled.as_ref().as_bool_expr(); + let reserved_cond = versioned_kw.reserved.as_ref().as_bool_expr(); // Simplify the emitted code if we trivially know that reserved or enabled is true match (&*reserved_cond.to_string(), &*enabled_cond.to_string()) { diff --git a/crates/codegen/runtime/generator/src/parser/parser_definition.rs b/crates/codegen/runtime/generator/src/parser/parser_definition.rs index 94b4756627..097e4e015c 100644 --- a/crates/codegen/runtime/generator/src/parser/parser_definition.rs +++ b/crates/codegen/runtime/generator/src/parser/parser_definition.rs @@ -1,12 +1,11 @@ -use codegen_language_definition::model::Identifier; +use codegen_language_definition::model::{Identifier, VersionSpecifier}; use inflector::Inflector; use proc_macro2::TokenStream; use quote::{format_ident, quote}; use semver::Version; use crate::parser::grammar::{ - Labeled, ParserDefinitionNode, ParserDefinitionRef, TriviaParserDefinitionRef, VersionQuality, - VersionQualityRange, + Labeled, ParserDefinitionNode, ParserDefinitionRef, TriviaParserDefinitionRef, }; pub trait ParserDefinitionExtensions { @@ -30,7 +29,7 @@ impl ParserDefinitionExtensions for TriviaParserDefinitionRef { pub trait ParserDefinitionNodeExtensions { fn to_parser_code(&self, context_name: &Identifier, is_trivia: bool) -> TokenStream; - fn applicable_version_quality_ranges(&self) -> Vec; + fn applicable_version_quality_ranges(&self) -> Option<&VersionSpecifier>; } impl ParserDefinitionNodeExtensions for ParserDefinitionNode { @@ -300,11 +299,9 @@ impl ParserDefinitionNodeExtensions for ParserDefinitionNode { } } - fn applicable_version_quality_ranges(&self) -> Vec { + fn applicable_version_quality_ranges(&self) -> Option<&VersionSpecifier> { match self { - ParserDefinitionNode::Versioned(_, version_quality_ranges) => { - version_quality_ranges.clone() - } + ParserDefinitionNode::Versioned(_, version_specifier) => Some(version_specifier), ParserDefinitionNode::Optional(value) | ParserDefinitionNode::ZeroOrMore(Labeled { value, .. }) @@ -312,7 +309,7 @@ impl ParserDefinitionNodeExtensions for ParserDefinitionNode { value.applicable_version_quality_ranges() } - _ => vec![], + _ => None, } } } @@ -323,37 +320,9 @@ pub trait VersionQualityRangeVecExtensions { fn as_bool_expr(&self) -> TokenStream; } -impl VersionQualityRangeVecExtensions for Vec { - fn as_bool_expr(&self) -> TokenStream { - if self.is_empty() { - quote!(true) - } else { - // Optimize for legibility; return `false` for "never enabled" - match self.as_slice() { - [VersionQualityRange { - from, - quality: VersionQuality::Removed, - }] if from == &Version::new(0, 0, 0) => return quote!(false), - _ => {} - } - - let flags = self.iter().map(|vqr| { - let flag = format_ident!( - "version_is_at_least_{v}", - v = &vqr.from.to_string().replace('.', "_") - ); - if vqr.quality == VersionQuality::Introduced { - quote! { self.#flag } - } else { - quote! { !self.#flag } - } - }); - quote! { #(#flags)&&* } - } - } - +impl VersionQualityRangeVecExtensions for Option<&VersionSpecifier> { fn wrap_code(&self, if_true: TokenStream, if_false: Option) -> TokenStream { - if self.is_empty() { + if self.is_none() { if_true } else { let condition = self.as_bool_expr(); @@ -362,18 +331,46 @@ impl VersionQualityRangeVecExtensions for Vec { quote! { if #condition { #if_true } #else_part } } } + + fn as_bool_expr(&self) -> TokenStream { + let to_version_flag_name = |v: &Version| { + format_ident!( + "version_is_at_least_{v}", + v = &v.to_string().replace('.', "_") + ) + }; + + match self { + // No constraints imposed, so always enabled + None => quote!(true), + Some(VersionSpecifier::Never) => quote!(false), + Some(VersionSpecifier::From { from }) => { + let flag = to_version_flag_name(from); + quote! { self.#flag } + } + Some(VersionSpecifier::Till { till }) => { + let flag = to_version_flag_name(till); + quote! { ! self.#flag } + } + Some(VersionSpecifier::Range { from, till }) => { + let from_flag = to_version_flag_name(from); + let till_flag = to_version_flag_name(till); + quote! { self.#from_flag && ! self.#till_flag } + } + } + } } pub fn make_sequence(parsers: impl IntoIterator) -> TokenStream { make_sequence_versioned( parsers .into_iter() - .map(|parser| (parser, String::new(), vec![])), + .map(|parser| (parser, String::new(), None)), ) } -pub fn make_sequence_versioned( - parsers: impl IntoIterator)>, +pub fn make_sequence_versioned<'a>( + parsers: impl IntoIterator)>, ) -> TokenStream { let parsers = parsers .into_iter() @@ -398,11 +395,11 @@ pub fn make_sequence_versioned( } pub fn make_choice(parsers: impl IntoIterator) -> TokenStream { - make_choice_versioned(parsers.into_iter().map(|parser| (parser, vec![]))) + make_choice_versioned(parsers.into_iter().map(|parser| (parser, None))) } -fn make_choice_versioned( - parsers: impl IntoIterator)>, +fn make_choice_versioned<'a>( + parsers: impl IntoIterator)>, ) -> TokenStream { let parsers = parsers .into_iter() diff --git a/crates/codegen/runtime/generator/src/parser/scanner_definition.rs b/crates/codegen/runtime/generator/src/parser/scanner_definition.rs index f02f90b6eb..c7a2657621 100644 --- a/crates/codegen/runtime/generator/src/parser/scanner_definition.rs +++ b/crates/codegen/runtime/generator/src/parser/scanner_definition.rs @@ -51,7 +51,7 @@ impl ScannerDefinitionNodeExtensions for ScannerDefinitionNode { match self { ScannerDefinitionNode::Versioned(body, version_quality_ranges) => { let body = body.to_scanner_code(); - version_quality_ranges.wrap_code(body, Some(quote! { false })) + Some(version_quality_ranges).wrap_code(body, Some(quote! { false })) } ScannerDefinitionNode::Optional(node) => { diff --git a/crates/codegen/runtime/generator/src/parser/trie.rs b/crates/codegen/runtime/generator/src/parser/trie.rs index 59971c6e92..082bc61904 100644 --- a/crates/codegen/runtime/generator/src/parser/trie.rs +++ b/crates/codegen/runtime/generator/src/parser/trie.rs @@ -1,13 +1,12 @@ use std::collections::BTreeMap; use std::fmt::Debug; +// use crate::grammar::model::KeywordDefinition; +use codegen_language_definition::model::{self, VersionSpecifier}; use proc_macro2::TokenStream; use quote::{format_ident, quote}; -use crate::parser::grammar::{ - KeywordScannerAtomic, KeywordScannerDefinitionVersionedNode, ScannerDefinitionNode, - ScannerDefinitionRef, VersionQualityRange, -}; +use crate::parser::grammar::{KeywordScannerAtomic, ScannerDefinitionNode, ScannerDefinitionRef}; use crate::parser::parser_definition::VersionQualityRangeVecExtensions; #[derive(Clone, Debug, Default)] @@ -93,21 +92,21 @@ impl Trie { } trait VersionWrapped { - fn applicable_version_quality_ranges(&self) -> Vec; + fn applicable_version_quality_ranges(&self) -> Option<&VersionSpecifier>; } impl VersionWrapped for ScannerDefinitionNode { - fn applicable_version_quality_ranges(&self) -> Vec { + fn applicable_version_quality_ranges(&self) -> Option<&VersionSpecifier> { match self { ScannerDefinitionNode::Versioned(_, version_quality_ranges) => { - version_quality_ranges.clone() + Some(version_quality_ranges) } ScannerDefinitionNode::Optional(node) | ScannerDefinitionNode::ZeroOrMore(node) | ScannerDefinitionNode::OneOrMore(node) => node.applicable_version_quality_ranges(), - _ => vec![], + _ => None, } } } @@ -141,12 +140,12 @@ impl Payload for KeywordScannerAtomic { fn to_leaf_code(&self) -> TokenStream { let kind = format_ident!("{}", self.name()); - let KeywordScannerDefinitionVersionedNode { + let model::KeywordDefinition { enabled, reserved, .. } = self.definition(); - let enabled_cond = enabled.as_bool_expr(); - let reserved_cond = reserved.as_bool_expr(); + let enabled_cond = enabled.as_ref().as_bool_expr(); + let reserved_cond = reserved.as_ref().as_bool_expr(); // Simplify the emitted code if we trivially know that reserved or enabled is true match (&*reserved_cond.to_string(), &*enabled_cond.to_string()) {