use std::collections::HashMap;
use hime_sdk::grammars::{Grammar, SymbolRef};
use hime_sdk::InputReference;
/*******************************************************************************
* Copyright (c) 2021 Association Cénotélie (cenotelie.fr)
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation, either version 3
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General
* Public License along with this program.
* If not, see .
******************************************************************************/
/// A symbol in the registry
#[derive(Debug, Clone)]
pub struct SymbolRegistryElement {
/// The index of the defining grammar
pub grammar_index: usize,
/// Reference to the symbol
pub symbol_ref: SymbolRef,
/// The input reference for its definition
pub definitions: Vec,
/// All the usage references
pub references: Vec,
}
impl SymbolRegistryElement {
/// Gets whether this symbol is at a location
#[must_use]
pub fn is_at_location(&self, location: &InputReference) -> bool {
self.definitions
.iter()
.chain(self.references.iter())
.any(|input_ref| input_ref.overlaps_with(location))
}
/// Gets the full reference to this symbol at a location
#[must_use]
pub fn get_full_reference(&self, location: &InputReference) -> Option {
self.definitions
.iter()
.chain(self.references.iter())
.find(|r| r.overlaps_with(location))
.copied()
}
}
/// A registry of symbols and their references
#[derive(Debug, Clone, Default)]
pub struct SymbolRegistry {
/// The symbols for each grammar
pub grammars: Vec>,
}
impl SymbolRegistry {
/// Initializes a registry from grammars
#[must_use]
pub fn from(grammars: &[Grammar]) -> SymbolRegistry {
SymbolRegistry {
grammars: grammars
.iter()
.enumerate()
.map(|(grammar_index, grammar)| {
let mut map = HashMap::new();
for terminal in &grammar.terminals {
map.insert(
SymbolRef::Terminal(terminal.id),
SymbolRegistryElement {
grammar_index,
symbol_ref: SymbolRef::Terminal(terminal.id),
definitions: vec![terminal.input_ref],
references: terminal
.terminal_references
.iter()
.map(|term_ref| term_ref.input_ref)
.collect(),
},
);
}
for variable in grammar.variables.iter().filter(|var| var.generated_for.is_none()) {
map.insert(
SymbolRef::Variable(variable.id),
SymbolRegistryElement {
grammar_index,
symbol_ref: SymbolRef::Variable(variable.id),
definitions: variable.rules.iter().map(|rule| rule.head_input_ref).collect(),
references: Vec::new(),
},
);
}
for symbol in &grammar.virtuals {
map.insert(
SymbolRef::Virtual(symbol.id),
SymbolRegistryElement {
grammar_index,
symbol_ref: SymbolRef::Virtual(symbol.id),
definitions: Vec::new(),
references: Vec::new(),
},
);
}
for symbol in &grammar.actions {
map.insert(
SymbolRef::Action(symbol.id),
SymbolRegistryElement {
grammar_index,
symbol_ref: SymbolRef::Action(symbol.id),
definitions: Vec::new(),
references: Vec::new(),
},
);
}
// retrieve all references in syntactic rules
for variable in &grammar.variables {
for rule in &variable.rules {
for element in &rule.body.elements {
if let Some(input_ref) = element.input_ref {
if let Some(entry) = map.get_mut(&element.symbol) {
entry.references.push(input_ref);
}
}
}
}
}
map
})
.collect(),
}
}
}