From 3735e825d81463e22ee9f76d4d0f5b7bc9ec323e Mon Sep 17 00:00:00 2001 From: hlorenzi Date: Sat, 7 Oct 2023 19:05:53 -0300 Subject: [PATCH] fix #181: fix optimized matcher algorithm to query for all prefixes --- src/asm/defs/ruledef_map.rs | 79 ++++++++++++++++++++----------------- src/asm/matcher/mod.rs | 5 +-- tests/issue181/ok.asm | 17 ++++++++ 3 files changed, 61 insertions(+), 40 deletions(-) create mode 100644 tests/issue181/ok.asm diff --git a/src/asm/defs/ruledef_map.rs b/src/asm/defs/ruledef_map.rs index 40f0bf48..7c10d062 100644 --- a/src/asm/defs/ruledef_map.rs +++ b/src/asm/defs/ruledef_map.rs @@ -1,21 +1,20 @@ use crate::*; -pub const RULEDEF_MAP_PREFIX_SIZE: usize = 4; +const MAX_PREFIX_SIZE: usize = 4; -pub type RuledefMapPrefix = [char; RULEDEF_MAP_PREFIX_SIZE]; +pub type RuledefMapPrefix = [char; MAX_PREFIX_SIZE]; #[derive(Debug)] pub struct RuledefMap { - /// Instructions that start with a mnemonic - prefixed: std::collections::HashMap>, - - /// All other instructions, e.g. the ones that start - /// with a parameter - unprefixed: Vec, + /// Each rule is considered to have a prefix + /// consisting of the first few "exact" pattern-part characters, + /// stopping before parameter and subrule slots, and also + /// cut off by the constant maximum prefix size declared above. + prefixes_to_rules: std::collections::HashMap>, } @@ -32,8 +31,7 @@ impl RuledefMap pub fn new() -> RuledefMap { RuledefMap { - prefixed: std::collections::HashMap::new(), - unprefixed: Vec::new(), + prefixes_to_rules: std::collections::HashMap::new(), } } @@ -71,12 +69,12 @@ impl RuledefMap rule_ref: util::ItemRef, rule: &asm::Rule) { - let mut prefix: RuledefMapPrefix = ['\0'; RULEDEF_MAP_PREFIX_SIZE]; + let mut prefix: RuledefMapPrefix = ['\0'; MAX_PREFIX_SIZE]; let mut prefix_index = 0; for part in &rule.pattern { - if prefix_index >= RULEDEF_MAP_PREFIX_SIZE + if prefix_index >= MAX_PREFIX_SIZE { break; } @@ -96,17 +94,10 @@ impl RuledefMap rule_ref, }; - if prefix_index > 0 - { - self.prefixed - .entry(prefix) - .or_insert_with(|| Vec::new()) - .push(entry); - } - else - { - self.unprefixed.push(entry); - } + self.prefixes_to_rules + .entry(prefix) + .or_insert_with(|| Vec::new()) + .push(entry); } @@ -114,12 +105,12 @@ impl RuledefMap walker: &syntax::TokenWalker) -> RuledefMapPrefix { - let mut prefix: RuledefMapPrefix = ['\0'; RULEDEF_MAP_PREFIX_SIZE]; + let mut prefix: RuledefMapPrefix = ['\0'; MAX_PREFIX_SIZE]; let mut prefix_index = 0; let mut walker_index = 0; - while prefix_index < RULEDEF_MAP_PREFIX_SIZE + while prefix_index < MAX_PREFIX_SIZE { let token = walker.next_nth(walker_index); walker_index += 1; @@ -128,7 +119,7 @@ impl RuledefMap { for c in token.text().chars() { - if prefix_index >= RULEDEF_MAP_PREFIX_SIZE + if prefix_index >= MAX_PREFIX_SIZE { break; } @@ -150,20 +141,34 @@ impl RuledefMap pub fn query_prefixed( &self, prefix: RuledefMapPrefix) - -> &[RuledefMapEntry] + -> [&[RuledefMapEntry]; MAX_PREFIX_SIZE + 1] { - match self.prefixed.get(&prefix) + // Try querying for every possible prefix, + // including the empty prefix, + // i.e. "abcde" will query for "", "a", "ab", "abc", and "abcd". + let mut results: [&[RuledefMapEntry]; MAX_PREFIX_SIZE + 1] = + [&[]; MAX_PREFIX_SIZE + 1]; + + for i in 0..(MAX_PREFIX_SIZE + 1) { - Some(entries) => entries, - None => &[], - } - } + let mut subprefix = prefix; + for j in i..MAX_PREFIX_SIZE + { + subprefix[j] = '\0'; + } + if let Some(entries) = self.prefixes_to_rules.get(&subprefix) + { + results[i] = entries; + } - pub fn query_unprefixed( - &self) - -> &[RuledefMapEntry] - { - &self.unprefixed + if i < MAX_PREFIX_SIZE && + prefix[i] == '\0' + { + break; + } + } + + results } } \ No newline at end of file diff --git a/src/asm/matcher/mod.rs b/src/asm/matcher/mod.rs index 169bf65a..f0b8b4aa 100644 --- a/src/asm/matcher/mod.rs +++ b/src/asm/matcher/mod.rs @@ -422,10 +422,9 @@ fn match_with_ruledef_map<'tokens>( let mut matches = WorkingMatches::new(); let prefix = asm::RuledefMap::parse_prefix(&walker); - let prefixed_entries = defs.ruledef_map.query_prefixed(prefix); - let unprefixed_entries = defs.ruledef_map.query_unprefixed(); + let entries = defs.ruledef_map.query_prefixed(prefix); - for entry in prefixed_entries.iter().chain(unprefixed_entries) + for entry in entries.iter().flat_map(|e| e.iter()) { let ruledef = defs.ruledefs.get(entry.ruledef_ref); let rule = ruledef.get_rule(entry.rule_ref); diff --git a/tests/issue181/ok.asm b/tests/issue181/ok.asm new file mode 100644 index 00000000..d1ba422f --- /dev/null +++ b/tests/issue181/ok.asm @@ -0,0 +1,17 @@ +#subruledef SELECT +{ + C => 0xc +} + +#ruledef +{ + A{sel:SELECT} => 0x1 @ sel + AB{sel:SELECT} => 0x2 @ sel + ABB{sel:SELECT} => 0x3 @ sel + ABBB{sel:SELECT} => 0x4 @ sel +} + +AC ; = 0x1c +ABC ; = 0x2c +ABBC ; = 0x3c +ABBBC ; = 0x4c \ No newline at end of file