rl0 automaton, lib
This commit is contained in:
parent
686e43448a
commit
46ca5ecc50
10 changed files with 764 additions and 519 deletions
|
@ -2,6 +2,14 @@
|
||||||
name = "rcompiler"
|
name = "rcompiler"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
|
default-run = "main"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
regex = "1.11.1"
|
regex = "1.11.1"
|
||||||
|
|
||||||
|
[[bin]]
|
||||||
|
name = "main"
|
||||||
|
path = "src/main.rs"
|
||||||
|
|
||||||
|
[[bin]]
|
||||||
|
name = "book"
|
||||||
|
|
82
src/bin/book.rs
Normal file
82
src/bin/book.rs
Normal file
|
@ -0,0 +1,82 @@
|
||||||
|
use ll_grammar::Skippable;
|
||||||
|
use rcompiler::prelude::*;
|
||||||
|
use regex::Match;
|
||||||
|
use std::collections::HashMap;
|
||||||
|
|
||||||
|
double_enum!(
|
||||||
|
BareTokens, Tokens {
|
||||||
|
WhiteSpace,
|
||||||
|
Assign,
|
||||||
|
Add,
|
||||||
|
LBrace,
|
||||||
|
RBrace,
|
||||||
|
Ident(String),
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
token_scanner!(
|
||||||
|
Tokens,
|
||||||
|
r"^\s|\t|\n|\r" : |_,_| {
|
||||||
|
Some(WhiteSpace)
|
||||||
|
}
|
||||||
|
r"^\+" : |_,_| {
|
||||||
|
Some(Add)
|
||||||
|
}
|
||||||
|
r"^=" : |_,_| {
|
||||||
|
Some(Assign)
|
||||||
|
}
|
||||||
|
r"^\(" : |_,_| {
|
||||||
|
Some(LBrace)
|
||||||
|
}
|
||||||
|
r"^\)" : |_,_| {
|
||||||
|
Some(RBrace)
|
||||||
|
}
|
||||||
|
r"^[a-zA-Z](\w)*" : |_, m: Match<'_>| {
|
||||||
|
Some(Ident(String::from(m.as_str())))
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq, Eq, Hash, Clone, PartialOrd, Ord)]
|
||||||
|
enum NoneTerminals {
|
||||||
|
S,
|
||||||
|
E,
|
||||||
|
P,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<N> From<NoneTerminals> for Sentential<NoneTerminals, N> {
|
||||||
|
fn from(value: NoneTerminals) -> Self {
|
||||||
|
Sentential::NoneTerminal(value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> From<BareTokens> for Sentential<T, BareTokens> {
|
||||||
|
fn from(value: BareTokens) -> Self {
|
||||||
|
Sentential::Terminal(value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn grammer() -> Grammar<NoneTerminals, BareTokens> {
|
||||||
|
use BareTokens::*;
|
||||||
|
use NoneTerminals::*;
|
||||||
|
cfg_grammar![
|
||||||
|
start: S;
|
||||||
|
S -> Ident, Assign, E;
|
||||||
|
E -> E, Add, P;
|
||||||
|
E -> P;
|
||||||
|
P -> Ident;
|
||||||
|
P -> LBrace, E, RBrace;
|
||||||
|
P -> Ident, LBrace, E, RBrace;
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
//let code = String::from("a = b()+c+(d+e())");
|
||||||
|
//let mut m = Scanner::<Tokens>::new(code).with_skipping(Tokens::WhiteSpace);
|
||||||
|
|
||||||
|
let mut grammar = grammer();
|
||||||
|
grammar.gen_follow();
|
||||||
|
println!("first: {:?}", grammar.first);
|
||||||
|
println!("follow: {:?}", grammar.follow);
|
||||||
|
grammar.gen_lr0_automaton();
|
||||||
|
println!("automaton: {:?}", grammar.lr0_automaton);
|
||||||
|
}
|
261
src/cfg/ll_grammar.rs
Normal file
261
src/cfg/ll_grammar.rs
Normal file
|
@ -0,0 +1,261 @@
|
||||||
|
use std::{collections::HashMap, fmt::Debug, hash::Hash};
|
||||||
|
|
||||||
|
use super::{Grammar, Sentential};
|
||||||
|
|
||||||
|
impl<N: PartialEq + Eq + Hash + Clone, T: PartialEq + Eq + Hash + Clone> Grammar<N, T> {
|
||||||
|
pub fn gen_ll_parse_table(&mut self) -> bool {
|
||||||
|
if self.follow.is_none() {
|
||||||
|
self.gen_follow();
|
||||||
|
}
|
||||||
|
if self.ll_parse_table.is_some() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
let mut conflict = false;
|
||||||
|
let mut parse_table: HashMap<(N, Option<T>), usize> = HashMap::new();
|
||||||
|
for (from, to) in self.rules.iter() {
|
||||||
|
for (id, to) in to.iter().enumerate() {
|
||||||
|
// rule is A -> al
|
||||||
|
// terminal == None means epsilon
|
||||||
|
for terminal in self.first(to) {
|
||||||
|
match terminal {
|
||||||
|
// let a be in First(al) -> add to T[A,a] = A->al (using the index of al)
|
||||||
|
Some(terminal) => {
|
||||||
|
conflict |= parse_table
|
||||||
|
.insert((from.clone(), Some(terminal.clone())), id)
|
||||||
|
.is_some();
|
||||||
|
}
|
||||||
|
// if first contains epsilon then
|
||||||
|
// let b be in Follow(A) -> add to T[A,b] = A->al (using the index of al)
|
||||||
|
None => {
|
||||||
|
for terminal in self.follow(from).iter() {
|
||||||
|
conflict |= parse_table
|
||||||
|
.insert((from.clone(), terminal.clone()), id)
|
||||||
|
.is_some()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
self.ll_parse_table = Some(parse_table);
|
||||||
|
conflict
|
||||||
|
}
|
||||||
|
|
||||||
|
/// get parse_table rule
|
||||||
|
/// None means error.
|
||||||
|
pub fn ll_parse_table(
|
||||||
|
&self,
|
||||||
|
none_terminal: &N,
|
||||||
|
terminal: &Option<T>,
|
||||||
|
) -> Option<(usize, &Vec<Sentential<N,T>>)> {
|
||||||
|
assert!(
|
||||||
|
self.ll_parse_table.is_some(),
|
||||||
|
"Please call gen_parse_table before this!"
|
||||||
|
);
|
||||||
|
self.ll_parse_table
|
||||||
|
.as_ref()
|
||||||
|
.unwrap()
|
||||||
|
.get(&(none_terminal.clone(), terminal.clone()))
|
||||||
|
.and_then(|f| {
|
||||||
|
self.rules
|
||||||
|
.get(none_terminal)
|
||||||
|
.and_then(|rule| rule.get(*f))
|
||||||
|
.map(|rule| (*f, rule))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn ll_parser<'a, S: Into<T> + PartialEq<T> + Clone>(
|
||||||
|
&'a self,
|
||||||
|
iter: &'a mut dyn Iterator<Item = Result<S, String>>,
|
||||||
|
) -> LLTabelParser<N, T, S> {
|
||||||
|
assert!(
|
||||||
|
self.ll_parse_table.is_some(),
|
||||||
|
"Please call gen_parse_table before this!"
|
||||||
|
);
|
||||||
|
LLTabelParser {
|
||||||
|
input: iter,
|
||||||
|
grammar: self,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Just checks a program. Does not generates output.
|
||||||
|
pub struct LLTabelParser<
|
||||||
|
'a,
|
||||||
|
N: PartialEq + Eq + Hash + Clone,
|
||||||
|
T: PartialEq + Eq + Hash + Clone,
|
||||||
|
S: Into<T> + PartialEq<T> + Clone,
|
||||||
|
> {
|
||||||
|
grammar: &'a Grammar<N, T>,
|
||||||
|
input: &'a mut dyn Iterator<Item = Result<S, String>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<
|
||||||
|
'a,
|
||||||
|
N: PartialEq + Eq + Hash + Clone + Debug,
|
||||||
|
T: PartialEq + Eq + Hash + Clone + Debug,
|
||||||
|
S: Into<T> + PartialEq<T> + Clone + Debug,
|
||||||
|
> LLTabelParser<'a, N, T, S>
|
||||||
|
{
|
||||||
|
pub fn parse(&mut self) -> Result<ParseTree<N, S>, String> {
|
||||||
|
// stack of table driven parser
|
||||||
|
// content of the vec:
|
||||||
|
// - first element: all of them combined represent the complete stack, of the parser.
|
||||||
|
// - secount element: rule has to able to derive the code defined, by its inner childs and the unparsed code from the accompanying first element.
|
||||||
|
let mut stack: Vec<(Vec<Sentential<N,T>>, ParseTree<N, S>)> = vec![(
|
||||||
|
vec![Sentential::NoneTerminal(self.grammar.start.clone())],
|
||||||
|
ParseTree::new(None),
|
||||||
|
)];
|
||||||
|
|
||||||
|
let mut next = match self.input.next() {
|
||||||
|
Some(Ok(d)) => Some(d),
|
||||||
|
Some(Err(err)) => return Err(format!("Invalid token: {}", err)),
|
||||||
|
None => None,
|
||||||
|
};
|
||||||
|
|
||||||
|
loop {
|
||||||
|
// look at current state
|
||||||
|
let mut state = stack.pop();
|
||||||
|
match state.as_mut() {
|
||||||
|
// processing inner state, of tracked rules
|
||||||
|
Some((inner_stack, rule)) => {
|
||||||
|
let inner_state = inner_stack.pop();
|
||||||
|
match inner_state {
|
||||||
|
// match terminal, check if equal
|
||||||
|
Some(Sentential::Terminal(terminal)) => match (next, terminal) {
|
||||||
|
// actual vs. expected input
|
||||||
|
(Some(inn), expect) if inn == expect => {
|
||||||
|
next = match self.input.next() {
|
||||||
|
Some(Ok(n)) => Some(n),
|
||||||
|
Some(Err(err)) => {
|
||||||
|
return Err(format!("Invalid token: {}", err))
|
||||||
|
}
|
||||||
|
None => None,
|
||||||
|
};
|
||||||
|
rule.childs.push(NodeChild::Data(inn));
|
||||||
|
stack.push(state.unwrap());
|
||||||
|
}
|
||||||
|
(a, b) => {
|
||||||
|
return Err(format!("found: {:?} expected: {:?}", a, b));
|
||||||
|
}
|
||||||
|
},
|
||||||
|
// take next none terminal and apply rule from parse table.
|
||||||
|
Some(Sentential::NoneTerminal(none_term)) => {
|
||||||
|
// load rule
|
||||||
|
let Some((id, new_rule)) = self
|
||||||
|
.grammar
|
||||||
|
.ll_parse_table(&none_term, &next.as_ref().map(|f| f.clone().into()))
|
||||||
|
else {
|
||||||
|
// no rule
|
||||||
|
return Err(format!(
|
||||||
|
"Unexpected token: {}",
|
||||||
|
next.map(|f| format!("{f:?}"))
|
||||||
|
.unwrap_or("end of file".to_string())
|
||||||
|
));
|
||||||
|
};
|
||||||
|
|
||||||
|
// reverse rule: because, uses vec as stack, but reversed
|
||||||
|
let new_rule_rev =
|
||||||
|
new_rule.iter().rev().map(|f| f.clone()).collect::<Vec<_>>();
|
||||||
|
// memorize current state/rule for later
|
||||||
|
stack.push(state.unwrap());
|
||||||
|
// process next rule
|
||||||
|
stack.push((
|
||||||
|
new_rule_rev,
|
||||||
|
ParseTree {
|
||||||
|
rule: Some((none_term, id)),
|
||||||
|
childs: Vec::new(),
|
||||||
|
},
|
||||||
|
));
|
||||||
|
}
|
||||||
|
// inner state is empty: current rule is finished
|
||||||
|
None => {
|
||||||
|
// if stack is empty, this is the initial state: finish or err
|
||||||
|
let Some(last) = stack.last_mut() else {
|
||||||
|
// ok: input has ended
|
||||||
|
if next.is_none() {
|
||||||
|
return Ok(state.unwrap().1);
|
||||||
|
}
|
||||||
|
// still code left, but not excepted
|
||||||
|
return Err(format!("Expected end of file."));
|
||||||
|
};
|
||||||
|
last.1.childs.push(NodeChild::Child(state.unwrap().1));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// should not be possible, because every other path pushes to the stack back or returns
|
||||||
|
None => {
|
||||||
|
return Err(format!("Err: EOS"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
|
||||||
|
pub trait Skippable {
|
||||||
|
fn skippable(&self) -> bool {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub enum NodeChild<N, S> {
|
||||||
|
Child(ParseTree<N, S>),
|
||||||
|
Data(S),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
#[allow(dead_code)]
|
||||||
|
pub struct ParseTree<N, S> {
|
||||||
|
pub rule: Option<(N, usize)>,
|
||||||
|
pub childs: Vec<NodeChild<N, S>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<N, S> ParseTree<N, S> {
|
||||||
|
pub fn new(rule: Option<(N, usize)>) -> Self {
|
||||||
|
Self {
|
||||||
|
rule,
|
||||||
|
childs: Vec::new(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<N: Skippable + Debug, S: Debug> ParseTree<N, S> {
|
||||||
|
/// cleanup the parse tree
|
||||||
|
/// does not work on a subtree
|
||||||
|
pub fn clean(self) -> Self {
|
||||||
|
self.clean_internal()
|
||||||
|
.expect("Clean only works on the main tree.")
|
||||||
|
}
|
||||||
|
|
||||||
|
/// internal clean
|
||||||
|
/// main node must not have a rule.
|
||||||
|
fn clean_internal(self) -> Result<Self, Vec<NodeChild<N, S>>> {
|
||||||
|
let childs = self
|
||||||
|
.childs
|
||||||
|
.into_iter()
|
||||||
|
.flat_map(|elem| match elem {
|
||||||
|
NodeChild::Child(parse_tree) => match parse_tree.clean_internal() {
|
||||||
|
Ok(tree) => [NodeChild::Child(tree)].into(),
|
||||||
|
Err(content) => content,
|
||||||
|
},
|
||||||
|
NodeChild::Data(d) => [NodeChild::Data(d)].into(),
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
if let Some((rule, _)) = &self.rule {
|
||||||
|
if rule.skippable() {
|
||||||
|
return Err(childs);
|
||||||
|
}
|
||||||
|
|
||||||
|
if childs.is_empty() {
|
||||||
|
return Err(childs);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(Self {
|
||||||
|
rule: self.rule,
|
||||||
|
childs,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
104
src/cfg/lr0.rs
Normal file
104
src/cfg/lr0.rs
Normal file
|
@ -0,0 +1,104 @@
|
||||||
|
use std::{
|
||||||
|
collections::{HashMap, HashSet},
|
||||||
|
hash::{Hash, Hasher},
|
||||||
|
rc::{Rc, Weak},
|
||||||
|
};
|
||||||
|
|
||||||
|
use super::{Grammar, Sentential};
|
||||||
|
|
||||||
|
#[derive(Debug, Eq, PartialEq)]
|
||||||
|
pub struct LR0State<N: Hash + Eq, T: Hash + Eq>(HashSet<(N, Vec<Sentential<N, T>>, usize)>);
|
||||||
|
|
||||||
|
impl<N: Hash + Eq + Clone, T: Hash + Eq + Clone> LR0State<N, T> {
|
||||||
|
pub fn next_kernel(&self, read: &Sentential<N, T>) -> Self {
|
||||||
|
let mut next_state: LR0State<N, T> = LR0State(HashSet::new());
|
||||||
|
for (from, to, dot) in self.0.iter() {
|
||||||
|
if to.get(*dot).map(|elem| *elem == *read).unwrap_or(false) {
|
||||||
|
next_state.0.insert((from.clone(), to.clone(), dot + 1));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
next_state
|
||||||
|
}
|
||||||
|
pub fn readable(&self) -> HashSet<Sentential<N, T>> {
|
||||||
|
let mut readbles = HashSet::new();
|
||||||
|
for (_, to, dot) in self.0.iter() {
|
||||||
|
if let Some(l) = to.get(*dot) {
|
||||||
|
readbles.insert(l.clone());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
readbles
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl<N: Hash + Eq + Ord, T: Hash + Eq + Ord> Hash for LR0State<N, T> {
|
||||||
|
fn hash<H: Hasher>(&self, state: &mut H) {
|
||||||
|
let mut a: Vec<&(N, Vec<Sentential<N, T>>, usize)> = self.0.iter().collect();
|
||||||
|
a.sort();
|
||||||
|
for s in a.iter() {
|
||||||
|
s.hash(state);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<N, T> Grammar<N, T>
|
||||||
|
where
|
||||||
|
N: PartialEq + Eq + Hash + Clone + Ord,
|
||||||
|
T: PartialEq + Eq + Hash + Clone + Ord,
|
||||||
|
{
|
||||||
|
pub fn lr0_clozure(&self, mut state: LR0State<N, T>) -> LR0State<N, T> {
|
||||||
|
loop {
|
||||||
|
let mut change = false;
|
||||||
|
let nt = state
|
||||||
|
.0
|
||||||
|
.iter()
|
||||||
|
.filter_map(|(_, to, dot)| to.get(*dot).cloned())
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
for n in nt {
|
||||||
|
if let Sentential::NoneTerminal(n) = n {
|
||||||
|
if let Some(rule) = self.rules.get(&n) {
|
||||||
|
for to in rule {
|
||||||
|
change |= state.0.insert((n.clone(), to.clone(), 0));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if !change {
|
||||||
|
return state;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn gen_lr0_automaton(&mut self) {
|
||||||
|
let mut out: HashMap<Rc<LR0State<N, T>>, Vec<(Sentential<N, T>, Weak<LR0State<N, T>>)>> =
|
||||||
|
HashMap::new();
|
||||||
|
let mut start_state = LR0State(HashSet::new());
|
||||||
|
if let Some(rule) = self.rules.get(&self.start) {
|
||||||
|
for to in rule {
|
||||||
|
start_state.0.insert((self.start.clone(), to.clone(), 0));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let rc = Rc::new(self.lr0_clozure(start_state));
|
||||||
|
let mut todo = vec![Rc::downgrade(&rc)];
|
||||||
|
out.insert(rc, Vec::new());
|
||||||
|
while let Some(elem) = todo.pop() {
|
||||||
|
if let Some(elem) = elem.upgrade() {
|
||||||
|
let mut vec = Vec::new();
|
||||||
|
for none_terminal in elem.readable() {
|
||||||
|
let next_state = self.lr0_clozure(elem.next_kernel(&none_terminal));
|
||||||
|
let rc = Rc::new(next_state);
|
||||||
|
if let Some((k, _)) = out.get_key_value(&rc) {
|
||||||
|
vec.push((none_terminal, Rc::downgrade(k)));
|
||||||
|
} else {
|
||||||
|
todo.push(Rc::downgrade(&rc));
|
||||||
|
vec.push((none_terminal, Rc::downgrade(&rc)));
|
||||||
|
out.insert(rc, Vec::new());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
out.entry(elem).and_modify(|elem| {
|
||||||
|
elem.extend(vec);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
self.lr0_automaton = Some(out);
|
||||||
|
}
|
||||||
|
}
|
282
src/cfg/mod.rs
Normal file
282
src/cfg/mod.rs
Normal file
|
@ -0,0 +1,282 @@
|
||||||
|
use std::{
|
||||||
|
collections::{HashMap, HashSet},
|
||||||
|
hash::Hash,
|
||||||
|
rc::{Rc, Weak},
|
||||||
|
};
|
||||||
|
|
||||||
|
use lr0::LR0State;
|
||||||
|
|
||||||
|
pub mod ll_grammar;
|
||||||
|
pub mod lr0;
|
||||||
|
|
||||||
|
#[macro_export]
|
||||||
|
macro_rules! cfg_grammar {
|
||||||
|
(
|
||||||
|
start: $start:ident;
|
||||||
|
$(
|
||||||
|
$left:ident -> $(
|
||||||
|
$right:ident
|
||||||
|
),*
|
||||||
|
);* $(;)?
|
||||||
|
) => {
|
||||||
|
{
|
||||||
|
let mut map = HashMap::new();
|
||||||
|
$({
|
||||||
|
if !map.contains_key(&$left) {
|
||||||
|
map.insert($left, Vec::new());
|
||||||
|
}
|
||||||
|
map.get_mut(&$left).unwrap().push(vec![$($right.into()),*]);
|
||||||
|
})*
|
||||||
|
$crate::cfg::Grammar {
|
||||||
|
start: $start,
|
||||||
|
rules: map,
|
||||||
|
first: None,
|
||||||
|
follow: None,
|
||||||
|
ll_parse_table: None,
|
||||||
|
lr0_automaton: None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||||
|
pub enum Sentential<N, T> {
|
||||||
|
Terminal(T),
|
||||||
|
NoneTerminal(N),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: PartialOrd, N: PartialOrd> PartialOrd for Sentential<N, T> {
|
||||||
|
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
|
||||||
|
match (self, other) {
|
||||||
|
(Self::Terminal(_), Self::NoneTerminal(_)) => Some(std::cmp::Ordering::Less),
|
||||||
|
(Self::NoneTerminal(_), Self::Terminal(_)) => Some(std::cmp::Ordering::Greater),
|
||||||
|
(Self::Terminal(a), Self::Terminal(b)) => a.partial_cmp(b),
|
||||||
|
(Self::NoneTerminal(a), Self::NoneTerminal(b)) => a.partial_cmp(b),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: Ord, N: Ord> Ord for Sentential<N, T> {
|
||||||
|
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
|
||||||
|
match (self, other) {
|
||||||
|
(Self::Terminal(_), Self::NoneTerminal(_)) => std::cmp::Ordering::Less,
|
||||||
|
(Self::NoneTerminal(_), Self::Terminal(_)) => std::cmp::Ordering::Greater,
|
||||||
|
(Self::Terminal(a), Self::Terminal(b)) => a.cmp(b),
|
||||||
|
(Self::NoneTerminal(a), Self::NoneTerminal(b)) => a.cmp(b),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Grammar<N: PartialEq + Eq + Hash + Clone, T: PartialEq + Eq + Hash + Clone> {
|
||||||
|
pub start: N,
|
||||||
|
pub rules: HashMap<N, Vec<Vec<Sentential<N, T>>>>,
|
||||||
|
/// none is epsilon
|
||||||
|
pub first: Option<HashMap<N, HashSet<Option<T>>>>,
|
||||||
|
/// none is $
|
||||||
|
pub follow: Option<HashMap<N, HashSet<Option<T>>>>,
|
||||||
|
|
||||||
|
// When in State N and reading T, then apply the usize'th rule of N.
|
||||||
|
/// none is $
|
||||||
|
pub ll_parse_table: Option<HashMap<(N, Option<T>), usize>>,
|
||||||
|
|
||||||
|
/// is a lr0 automaton
|
||||||
|
/// Graph, defined throw this adjacent list.
|
||||||
|
/// - key: states
|
||||||
|
/// - value: list with read symbol and linked node.
|
||||||
|
pub lr0_automaton:
|
||||||
|
Option<HashMap<Rc<LR0State<N, T>>, Vec<(Sentential<N, T>, Weak<LR0State<N, T>>)>>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<N: PartialEq + Eq + Hash + Clone, T: PartialEq + Eq + Hash + Clone> Grammar<N, T> {
|
||||||
|
pub fn can_produce_epsilon(&self, rule: &Sentential<N, T>) -> bool {
|
||||||
|
match rule {
|
||||||
|
Sentential::Terminal(_) => false,
|
||||||
|
Sentential::NoneTerminal(nt) => self
|
||||||
|
.rules
|
||||||
|
.get(&nt)
|
||||||
|
.map(|f| f.iter().any(|v| v.is_empty()))
|
||||||
|
.unwrap_or(false),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn gen_first(&mut self) {
|
||||||
|
let mut first: HashMap<N, HashSet<Option<T>>> = HashMap::new();
|
||||||
|
loop {
|
||||||
|
let mut change = false;
|
||||||
|
for (from, to) in self.rules.iter() {
|
||||||
|
'rule: for to in to.iter() {
|
||||||
|
// for each rule from -> to = X -> Y1...Yk
|
||||||
|
// add First(Yn) to First(X) if Y1...Yn-1 => e // n can be 1, disregarding the if
|
||||||
|
// add e to First(X) if Y1...Yk => e
|
||||||
|
for symbol in to {
|
||||||
|
match symbol {
|
||||||
|
Sentential::Terminal(a) => {
|
||||||
|
first
|
||||||
|
.entry(from.clone())
|
||||||
|
.and_modify(|e| {
|
||||||
|
change |= e.insert(Some(a.clone()));
|
||||||
|
})
|
||||||
|
.or_insert_with(|| {
|
||||||
|
change = true;
|
||||||
|
HashSet::from([Some(a.clone())])
|
||||||
|
});
|
||||||
|
}
|
||||||
|
Sentential::NoneTerminal(nt) => {
|
||||||
|
if let Some(set) = first.get(nt).cloned() {
|
||||||
|
first
|
||||||
|
.entry(from.clone())
|
||||||
|
.and_modify(|e| {
|
||||||
|
for val in set.iter() {
|
||||||
|
change |= e.insert(val.clone());
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.or_insert_with(|| {
|
||||||
|
change = true;
|
||||||
|
set
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !self.can_produce_epsilon(symbol) {
|
||||||
|
continue 'rule;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
first
|
||||||
|
.entry(from.clone())
|
||||||
|
.and_modify(|e| {
|
||||||
|
change |= e.insert(None);
|
||||||
|
})
|
||||||
|
.or_insert_with(|| {
|
||||||
|
change = true;
|
||||||
|
HashSet::from([None])
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !change {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
self.first = Some(first);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn first(&self, sent: &Vec<Sentential<N, T>>) -> HashSet<Option<T>> {
|
||||||
|
assert!(self.first.is_some(), "Please call gen_first before this!");
|
||||||
|
let mut out = HashSet::<Option<T>>::new();
|
||||||
|
|
||||||
|
// Y1Y2...Yk = al
|
||||||
|
// add First(Yn) to First(al) if Y1...Yn-1 => e // n can be 1, disregarding the if
|
||||||
|
// add e to First(al) if Y1...Yk => e
|
||||||
|
'rule: {
|
||||||
|
for symbol in sent {
|
||||||
|
match symbol {
|
||||||
|
Sentential::Terminal(a) => {
|
||||||
|
out.insert(Some(a.clone()));
|
||||||
|
}
|
||||||
|
Sentential::NoneTerminal(nt) => {
|
||||||
|
if let Some(set) = self.first.as_ref().unwrap().get(nt).cloned() {
|
||||||
|
out.extend(set);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !self.can_produce_epsilon(symbol) {
|
||||||
|
break 'rule;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
out.insert(None);
|
||||||
|
}
|
||||||
|
out
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn gen_follow(&mut self) {
|
||||||
|
if self.first == None {
|
||||||
|
self.gen_first();
|
||||||
|
}
|
||||||
|
let mut follow: HashMap<N, HashSet<Option<T>>> = HashMap::new();
|
||||||
|
follow.insert(self.start.clone(), HashSet::from([None]));
|
||||||
|
loop {
|
||||||
|
let mut change = false;
|
||||||
|
|
||||||
|
for (from, to) in self.rules.iter() {
|
||||||
|
for to in to.iter() {
|
||||||
|
// a
|
||||||
|
// if A -> aBb then add First(b) - {e} to Follow(B)
|
||||||
|
// and if A -> aBb and e in First(b) add Follow(A) to Follow(B)
|
||||||
|
if to.len() >= 2 {
|
||||||
|
for i in 0..(to.len() - 1) {
|
||||||
|
let slice = to[i + 1..].iter().map(|f| f.clone()).collect::<Vec<_>>();
|
||||||
|
match to.get(i) {
|
||||||
|
Some(Sentential::NoneTerminal(b)) => {
|
||||||
|
let mut set = self.first(&slice);
|
||||||
|
if set.contains(&None) {
|
||||||
|
if let Some(set) = follow.get(from).cloned() {
|
||||||
|
follow
|
||||||
|
.entry(b.clone())
|
||||||
|
.and_modify(|e| {
|
||||||
|
for val in set.iter() {
|
||||||
|
change |= e.insert(val.clone());
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.or_insert_with(|| {
|
||||||
|
change = true;
|
||||||
|
set
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
set.remove(&None);
|
||||||
|
follow
|
||||||
|
.entry(b.clone())
|
||||||
|
.and_modify(|e| {
|
||||||
|
for val in set.iter() {
|
||||||
|
change |= e.insert(val.clone());
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.or_insert_with(|| {
|
||||||
|
change = true;
|
||||||
|
set
|
||||||
|
});
|
||||||
|
}
|
||||||
|
_ => (),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// b
|
||||||
|
// and if A -> aB add Follow(A) to Follow(B)
|
||||||
|
match to.last() {
|
||||||
|
Some(Sentential::NoneTerminal(b)) => {
|
||||||
|
if let Some(set) = follow.get(from).cloned() {
|
||||||
|
follow
|
||||||
|
.entry(b.clone())
|
||||||
|
.and_modify(|e| {
|
||||||
|
for val in set.iter() {
|
||||||
|
change |= e.insert(val.clone());
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.or_insert_with(|| {
|
||||||
|
change = true;
|
||||||
|
set
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => (),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if !change {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
self.follow = Some(follow);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn follow(&self, none_termianl: &N) -> HashSet<Option<T>> {
|
||||||
|
assert!(self.follow.is_some(), "Please call gen_follow before this!");
|
||||||
|
self.follow
|
||||||
|
.as_ref()
|
||||||
|
.unwrap()
|
||||||
|
.get(&none_termianl)
|
||||||
|
.cloned()
|
||||||
|
.unwrap_or(HashSet::new())
|
||||||
|
}
|
||||||
|
}
|
|
@ -13,7 +13,7 @@ macro_rules! double_enum {
|
||||||
( $($args),+ )
|
( $($args),+ )
|
||||||
)?
|
)?
|
||||||
),*}
|
),*}
|
||||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
#[derive(Debug, Clone, PartialEq, Eq, Hash, Ord, PartialOrd)]
|
||||||
pub enum $bare_name {$($variant),*}
|
pub enum $bare_name {$($variant),*}
|
||||||
|
|
||||||
impl PartialEq<$name> for $bare_name {
|
impl PartialEq<$name> for $bare_name {
|
||||||
|
|
13
src/lib.rs
Normal file
13
src/lib.rs
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
pub mod cfg;
|
||||||
|
pub mod double_enum;
|
||||||
|
pub mod scanner;
|
||||||
|
|
||||||
|
pub mod prelude {
|
||||||
|
pub use crate::cfg::*;
|
||||||
|
pub use crate::cfg::ll_grammar::*;
|
||||||
|
pub use crate::cfg::lr0::*;
|
||||||
|
pub use crate::cfg_grammar;
|
||||||
|
pub use crate::double_enum;
|
||||||
|
pub use crate::scanner::*;
|
||||||
|
pub use crate::token_scanner;
|
||||||
|
}
|
|
@ -1,503 +0,0 @@
|
||||||
use std::{
|
|
||||||
collections::{HashMap, HashSet},
|
|
||||||
fmt::Debug,
|
|
||||||
hash::Hash,
|
|
||||||
};
|
|
||||||
|
|
||||||
#[macro_export]
|
|
||||||
macro_rules! ll_grammar {
|
|
||||||
(
|
|
||||||
start: $start:ident;
|
|
||||||
$(
|
|
||||||
$left:ident -> $(
|
|
||||||
$right:ident
|
|
||||||
),*
|
|
||||||
);* $(;)?
|
|
||||||
) => {
|
|
||||||
{
|
|
||||||
let mut map = HashMap::new();
|
|
||||||
$({
|
|
||||||
if !map.contains_key(&$left) {
|
|
||||||
map.insert($left, Vec::new());
|
|
||||||
}
|
|
||||||
map.get_mut(&$left).unwrap().push(vec![$($right.into()),*]);
|
|
||||||
})*
|
|
||||||
$crate::ll_grammar::LLGrammar {
|
|
||||||
start: $start,
|
|
||||||
rules: map,
|
|
||||||
first: None,
|
|
||||||
follow: None,
|
|
||||||
parse_table: None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
|
||||||
pub enum Sentential<T, N> {
|
|
||||||
Terminal(T),
|
|
||||||
NoneTerminal(N),
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct LLGrammar<N: PartialEq + Eq + Hash + Clone, T: PartialEq + Eq + Hash + Clone> {
|
|
||||||
pub start: N,
|
|
||||||
pub rules: HashMap<N, Vec<Vec<Sentential<T, N>>>>,
|
|
||||||
/// none is epsilon
|
|
||||||
pub first: Option<HashMap<N, HashSet<Option<T>>>>,
|
|
||||||
/// none is $
|
|
||||||
pub follow: Option<HashMap<N, HashSet<Option<T>>>>,
|
|
||||||
|
|
||||||
// When in State N and reading T, then apply the usize'th rule of N.
|
|
||||||
/// none is $
|
|
||||||
pub parse_table: Option<HashMap<(N, Option<T>), usize>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<N: PartialEq + Eq + Hash + Clone, T: PartialEq + Eq + Hash + Clone> LLGrammar<N, T> {
|
|
||||||
pub fn can_produce_epsilon(&self, rule: &Sentential<T, N>) -> bool {
|
|
||||||
match rule {
|
|
||||||
Sentential::Terminal(_) => false,
|
|
||||||
Sentential::NoneTerminal(nt) => self
|
|
||||||
.rules
|
|
||||||
.get(&nt)
|
|
||||||
.map(|f| f.iter().any(|v| v.is_empty()))
|
|
||||||
.unwrap_or(false),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn gen_first(&mut self) {
|
|
||||||
let mut first: HashMap<N, HashSet<Option<T>>> = HashMap::new();
|
|
||||||
loop {
|
|
||||||
let mut change = false;
|
|
||||||
for (from, to) in self.rules.iter() {
|
|
||||||
'rule: for to in to.iter() {
|
|
||||||
// for each rule from -> to = X -> Y1...Yk
|
|
||||||
// add First(Yn) to First(X) if Y1...Yn-1 => e // n can be 1, disregarding the if
|
|
||||||
// add e to First(X) if Y1...Yk => e
|
|
||||||
for symbol in to {
|
|
||||||
match symbol {
|
|
||||||
Sentential::Terminal(a) => {
|
|
||||||
first
|
|
||||||
.entry(from.clone())
|
|
||||||
.and_modify(|e| {
|
|
||||||
change |= e.insert(Some(a.clone()));
|
|
||||||
})
|
|
||||||
.or_insert_with(|| {
|
|
||||||
change = true;
|
|
||||||
HashSet::from([Some(a.clone())])
|
|
||||||
});
|
|
||||||
}
|
|
||||||
Sentential::NoneTerminal(nt) => {
|
|
||||||
if let Some(set) = first.get(nt).cloned() {
|
|
||||||
first
|
|
||||||
.entry(from.clone())
|
|
||||||
.and_modify(|e| {
|
|
||||||
for val in set.iter() {
|
|
||||||
change |= e.insert(val.clone());
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.or_insert_with(|| {
|
|
||||||
change = true;
|
|
||||||
set
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if !self.can_produce_epsilon(symbol) {
|
|
||||||
continue 'rule;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
first
|
|
||||||
.entry(from.clone())
|
|
||||||
.and_modify(|e| {
|
|
||||||
change |= e.insert(None);
|
|
||||||
})
|
|
||||||
.or_insert_with(|| {
|
|
||||||
change = true;
|
|
||||||
HashSet::from([None])
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if !change {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
self.first = Some(first);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn first(&self, sent: &Vec<Sentential<T, N>>) -> HashSet<Option<T>> {
|
|
||||||
assert!(self.first.is_some(), "Please call gen_first before this!");
|
|
||||||
let mut out = HashSet::<Option<T>>::new();
|
|
||||||
|
|
||||||
// Y1Y2...Yk = al
|
|
||||||
// add First(Yn) to First(al) if Y1...Yn-1 => e // n can be 1, disregarding the if
|
|
||||||
// add e to First(al) if Y1...Yk => e
|
|
||||||
'rule: {
|
|
||||||
for symbol in sent {
|
|
||||||
match symbol {
|
|
||||||
Sentential::Terminal(a) => {
|
|
||||||
out.insert(Some(a.clone()));
|
|
||||||
}
|
|
||||||
Sentential::NoneTerminal(nt) => {
|
|
||||||
if let Some(set) = self.first.as_ref().unwrap().get(nt).cloned() {
|
|
||||||
out.extend(set);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if !self.can_produce_epsilon(symbol) {
|
|
||||||
break 'rule;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
out.insert(None);
|
|
||||||
}
|
|
||||||
out
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn gen_follow(&mut self) {
|
|
||||||
if self.first == None {
|
|
||||||
self.gen_first();
|
|
||||||
}
|
|
||||||
let mut follow: HashMap<N, HashSet<Option<T>>> = HashMap::new();
|
|
||||||
follow.insert(self.start.clone(), HashSet::from([None]));
|
|
||||||
loop {
|
|
||||||
let mut change = false;
|
|
||||||
|
|
||||||
for (from, to) in self.rules.iter() {
|
|
||||||
for to in to.iter() {
|
|
||||||
// a
|
|
||||||
// if A -> aBb then add First(b) - {e} to Follow(B)
|
|
||||||
// and if A -> aBb and e in First(b) add Follow(A) to Follow(B)
|
|
||||||
if to.len() >= 2 {
|
|
||||||
for i in 0..(to.len() - 1) {
|
|
||||||
let slice = to[i + 1..].iter().map(|f| f.clone()).collect::<Vec<_>>();
|
|
||||||
match to.get(i) {
|
|
||||||
Some(Sentential::NoneTerminal(b)) => {
|
|
||||||
let mut set = self.first(&slice);
|
|
||||||
if set.contains(&None) {
|
|
||||||
if let Some(set) = follow.get(from).cloned() {
|
|
||||||
follow
|
|
||||||
.entry(b.clone())
|
|
||||||
.and_modify(|e| {
|
|
||||||
for val in set.iter() {
|
|
||||||
change |= e.insert(val.clone());
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.or_insert_with(|| {
|
|
||||||
change = true;
|
|
||||||
set
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
set.remove(&None);
|
|
||||||
follow
|
|
||||||
.entry(b.clone())
|
|
||||||
.and_modify(|e| {
|
|
||||||
for val in set.iter() {
|
|
||||||
change |= e.insert(val.clone());
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.or_insert_with(|| {
|
|
||||||
change = true;
|
|
||||||
set
|
|
||||||
});
|
|
||||||
}
|
|
||||||
_ => (),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// b
|
|
||||||
// and if A -> aB add Follow(A) to Follow(B)
|
|
||||||
match to.last() {
|
|
||||||
Some(Sentential::NoneTerminal(b)) => {
|
|
||||||
if let Some(set) = follow.get(from).cloned() {
|
|
||||||
follow
|
|
||||||
.entry(b.clone())
|
|
||||||
.and_modify(|e| {
|
|
||||||
for val in set.iter() {
|
|
||||||
change |= e.insert(val.clone());
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.or_insert_with(|| {
|
|
||||||
change = true;
|
|
||||||
set
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_ => (),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if !change {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
self.follow = Some(follow);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn follow(&self, none_termianl: &N) -> HashSet<Option<T>> {
|
|
||||||
assert!(self.follow.is_some(), "Please call gen_follow before this!");
|
|
||||||
self.follow
|
|
||||||
.as_ref()
|
|
||||||
.unwrap()
|
|
||||||
.get(&none_termianl)
|
|
||||||
.cloned()
|
|
||||||
.unwrap_or(HashSet::new())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn gen_parse_table(&mut self) -> bool {
|
|
||||||
if self.follow.is_none() {
|
|
||||||
self.gen_follow();
|
|
||||||
}
|
|
||||||
if self.parse_table.is_some() {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
let mut conflict = false;
|
|
||||||
let mut parse_table: HashMap<(N, Option<T>), usize> = HashMap::new();
|
|
||||||
for (from, to) in self.rules.iter() {
|
|
||||||
for (id, to) in to.iter().enumerate() {
|
|
||||||
// rule is A -> al
|
|
||||||
// terminal == None means epsilon
|
|
||||||
for terminal in self.first(to) {
|
|
||||||
match terminal {
|
|
||||||
// let a be in First(al) -> add to T[A,a] = A->al (using the index of al)
|
|
||||||
Some(terminal) => {
|
|
||||||
conflict |= parse_table
|
|
||||||
.insert((from.clone(), Some(terminal.clone())), id)
|
|
||||||
.is_some();
|
|
||||||
}
|
|
||||||
// if first contains epsilon then
|
|
||||||
// let b be in Follow(A) -> add to T[A,b] = A->al (using the index of al)
|
|
||||||
None => {
|
|
||||||
for terminal in self.follow(from).iter() {
|
|
||||||
conflict |= parse_table
|
|
||||||
.insert((from.clone(), terminal.clone()), id)
|
|
||||||
.is_some()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
self.parse_table = Some(parse_table);
|
|
||||||
conflict
|
|
||||||
}
|
|
||||||
|
|
||||||
/// get parse_table rule
|
|
||||||
/// None means error.
|
|
||||||
pub fn parse_table(
|
|
||||||
&self,
|
|
||||||
none_terminal: &N,
|
|
||||||
terminal: &Option<T>,
|
|
||||||
) -> Option<(usize, &Vec<Sentential<T, N>>)> {
|
|
||||||
assert!(
|
|
||||||
self.parse_table.is_some(),
|
|
||||||
"Please call gen_parse_table before this!"
|
|
||||||
);
|
|
||||||
self.parse_table
|
|
||||||
.as_ref()
|
|
||||||
.unwrap()
|
|
||||||
.get(&(none_terminal.clone(), terminal.clone()))
|
|
||||||
.and_then(|f| {
|
|
||||||
self.rules
|
|
||||||
.get(none_terminal)
|
|
||||||
.and_then(|rule| rule.get(*f))
|
|
||||||
.map(|rule| (*f, rule))
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn parser<'a, S: Into<T> + PartialEq<T> + Clone>(
|
|
||||||
&'a self,
|
|
||||||
iter: &'a mut dyn Iterator<Item = Result<S, String>>,
|
|
||||||
) -> LLTabelParser<N, T, S> {
|
|
||||||
assert!(
|
|
||||||
self.parse_table.is_some(),
|
|
||||||
"Please call gen_parse_table before this!"
|
|
||||||
);
|
|
||||||
LLTabelParser {
|
|
||||||
input: iter,
|
|
||||||
grammar: self,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Just checks a program. Does not generates output.
|
|
||||||
pub struct LLTabelParser<
|
|
||||||
'a,
|
|
||||||
N: PartialEq + Eq + Hash + Clone,
|
|
||||||
T: PartialEq + Eq + Hash + Clone,
|
|
||||||
S: Into<T> + PartialEq<T> + Clone,
|
|
||||||
> {
|
|
||||||
grammar: &'a LLGrammar<N, T>,
|
|
||||||
input: &'a mut dyn Iterator<Item = Result<S, String>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<
|
|
||||||
'a,
|
|
||||||
N: PartialEq + Eq + Hash + Clone + Debug,
|
|
||||||
T: PartialEq + Eq + Hash + Clone + Debug,
|
|
||||||
S: Into<T> + PartialEq<T> + Clone + Debug,
|
|
||||||
> LLTabelParser<'a, N, T, S>
|
|
||||||
{
|
|
||||||
pub fn parse(&mut self) -> Result<ParseTree<N, S>, String> {
|
|
||||||
// stack of table driven parser
|
|
||||||
// content of the vec:
|
|
||||||
// - first element: all of them combined represent the complete stack, of the parser.
|
|
||||||
// - secount element: rule has to able to derive the code defined, by its inner childs and the unparsed code from the accompanying first element.
|
|
||||||
let mut stack: Vec<(Vec<Sentential<T, N>>, ParseTree<N, S>)> = vec![(
|
|
||||||
vec![Sentential::NoneTerminal(self.grammar.start.clone())],
|
|
||||||
ParseTree::new(None),
|
|
||||||
)];
|
|
||||||
|
|
||||||
let mut next = match self.input.next() {
|
|
||||||
Some(Ok(d)) => Some(d),
|
|
||||||
Some(Err(err)) => return Err(format!("Invalid token: {}", err)),
|
|
||||||
None => None,
|
|
||||||
};
|
|
||||||
|
|
||||||
loop {
|
|
||||||
// look at current state
|
|
||||||
let mut state = stack.pop();
|
|
||||||
match state.as_mut() {
|
|
||||||
// processing inner state, of tracked rules
|
|
||||||
Some((inner_stack, rule)) => {
|
|
||||||
let inner_state = inner_stack.pop();
|
|
||||||
match inner_state {
|
|
||||||
// match terminal, check if equal
|
|
||||||
Some(Sentential::Terminal(terminal)) => match (next, terminal) {
|
|
||||||
// actual vs. expected input
|
|
||||||
(Some(inn), expect) if inn == expect => {
|
|
||||||
next = match self.input.next() {
|
|
||||||
Some(Ok(n)) => Some(n),
|
|
||||||
Some(Err(err)) => {
|
|
||||||
return Err(format!("Invalid token: {}", err))
|
|
||||||
}
|
|
||||||
None => None,
|
|
||||||
};
|
|
||||||
rule.childs.push(NodeChild::Data(inn));
|
|
||||||
stack.push(state.unwrap());
|
|
||||||
}
|
|
||||||
(a, b) => {
|
|
||||||
return Err(format!("found: {:?} expected: {:?}", a, b));
|
|
||||||
}
|
|
||||||
},
|
|
||||||
// take next none terminal and apply rule from parse table.
|
|
||||||
Some(Sentential::NoneTerminal(none_term)) => {
|
|
||||||
// load rule
|
|
||||||
let Some((id, new_rule)) = self
|
|
||||||
.grammar
|
|
||||||
.parse_table(&none_term, &next.as_ref().map(|f| f.clone().into()))
|
|
||||||
else {
|
|
||||||
// no rule
|
|
||||||
return Err(format!(
|
|
||||||
"Unexpected token: {}",
|
|
||||||
next.map(|f| format!("{f:?}"))
|
|
||||||
.unwrap_or("end of file".to_string())
|
|
||||||
));
|
|
||||||
};
|
|
||||||
|
|
||||||
// reverse rule: because, uses vec as stack, but reversed
|
|
||||||
let new_rule_rev =
|
|
||||||
new_rule.iter().rev().map(|f| f.clone()).collect::<Vec<_>>();
|
|
||||||
// memorize current state/rule for later
|
|
||||||
stack.push(state.unwrap());
|
|
||||||
// process next rule
|
|
||||||
stack.push((
|
|
||||||
new_rule_rev,
|
|
||||||
ParseTree {
|
|
||||||
rule: Some((none_term, id)),
|
|
||||||
childs: Vec::new(),
|
|
||||||
},
|
|
||||||
));
|
|
||||||
}
|
|
||||||
// inner state is empty: current rule is finished
|
|
||||||
None => {
|
|
||||||
// if stack is empty, this is the initial state: finish or err
|
|
||||||
let Some(last) = stack.last_mut() else {
|
|
||||||
// ok: input has ended
|
|
||||||
if next.is_none() {
|
|
||||||
return Ok(state.unwrap().1);
|
|
||||||
}
|
|
||||||
// still code left, but not excepted
|
|
||||||
return Err(format!("Expected end of file."));
|
|
||||||
};
|
|
||||||
last.1.childs.push(NodeChild::Child(state.unwrap().1));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// should not be possible, because every other path pushes to the stack back or returns
|
|
||||||
None => {
|
|
||||||
return Err(format!("Err: EOS"));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//
|
|
||||||
|
|
||||||
pub trait Skippable {
|
|
||||||
fn skippable(&self) -> bool {
|
|
||||||
false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
|
||||||
pub enum NodeChild<N, S> {
|
|
||||||
Child(ParseTree<N, S>),
|
|
||||||
Data(S),
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
|
||||||
#[allow(dead_code)]
|
|
||||||
pub struct ParseTree<N, S> {
|
|
||||||
pub rule: Option<(N, usize)>,
|
|
||||||
pub childs: Vec<NodeChild<N, S>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<N, S> ParseTree<N, S> {
|
|
||||||
pub fn new(rule: Option<(N, usize)>) -> Self {
|
|
||||||
Self {
|
|
||||||
rule,
|
|
||||||
childs: Vec::new(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<N: Skippable + Debug, S: Debug> ParseTree<N, S> {
|
|
||||||
/// cleanup the parse tree
|
|
||||||
/// does not work on a subtree
|
|
||||||
pub fn clean(self) -> Self {
|
|
||||||
self.clean_internal()
|
|
||||||
.expect("Clean only works on the main tree.")
|
|
||||||
}
|
|
||||||
|
|
||||||
/// internal clean
|
|
||||||
/// main node must not have a rule.
|
|
||||||
fn clean_internal(self) -> Result<Self, Vec<NodeChild<N, S>>> {
|
|
||||||
let childs = self
|
|
||||||
.childs
|
|
||||||
.into_iter()
|
|
||||||
.flat_map(|elem| match elem {
|
|
||||||
NodeChild::Child(parse_tree) => match parse_tree.clean_internal() {
|
|
||||||
Ok(tree) => [NodeChild::Child(tree)].into(),
|
|
||||||
Err(content) => content,
|
|
||||||
},
|
|
||||||
NodeChild::Data(d) => [NodeChild::Data(d)].into(),
|
|
||||||
})
|
|
||||||
.collect();
|
|
||||||
if let Some((rule, _)) = &self.rule {
|
|
||||||
if rule.skippable() {
|
|
||||||
return Err(childs);
|
|
||||||
}
|
|
||||||
|
|
||||||
if childs.is_empty() {
|
|
||||||
return Err(childs);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Ok(Self {
|
|
||||||
rule: self.rule,
|
|
||||||
childs,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
25
src/main.rs
25
src/main.rs
|
@ -1,9 +1,6 @@
|
||||||
mod double_enum;
|
use ll_grammar::Skippable;
|
||||||
mod ll_grammar;
|
use rcompiler::prelude::*;
|
||||||
mod scanner;
|
use regex::Match;
|
||||||
use ll_grammar::{LLGrammar, Sentential, Skippable};
|
|
||||||
use regex::{Match, Regex};
|
|
||||||
use scanner::Scanner;
|
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
|
||||||
double_enum!(
|
double_enum!(
|
||||||
|
@ -34,7 +31,7 @@ double_enum!(
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
scanner!(
|
token_scanner!(
|
||||||
Tokens,
|
Tokens,
|
||||||
r"^\s|\t|\n|\r" : |_,_| {
|
r"^\s|\t|\n|\r" : |_,_| {
|
||||||
Some(WhiteSpace)
|
Some(WhiteSpace)
|
||||||
|
@ -134,22 +131,22 @@ impl Skippable for NoneTerminals {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T> From<NoneTerminals> for Sentential<T, NoneTerminals> {
|
impl<N> From<NoneTerminals> for Sentential<NoneTerminals, N> {
|
||||||
fn from(value: NoneTerminals) -> Self {
|
fn from(value: NoneTerminals) -> Self {
|
||||||
Sentential::NoneTerminal(value)
|
Sentential::NoneTerminal(value)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<N> From<BareTokens> for Sentential<BareTokens, N> {
|
impl<T> From<BareTokens> for Sentential<T, BareTokens> {
|
||||||
fn from(value: BareTokens) -> Self {
|
fn from(value: BareTokens) -> Self {
|
||||||
Sentential::Terminal(value)
|
Sentential::Terminal(value)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn grammer() -> LLGrammar<NoneTerminals, BareTokens> {
|
fn grammer() -> Grammar<NoneTerminals, BareTokens> {
|
||||||
use BareTokens::*;
|
use BareTokens::*;
|
||||||
use NoneTerminals::*;
|
use NoneTerminals::*;
|
||||||
ll_grammar![
|
cfg_grammar![
|
||||||
start: P;
|
start: P;
|
||||||
P -> L,P;
|
P -> L,P;
|
||||||
P -> ;
|
P -> ;
|
||||||
|
@ -201,14 +198,14 @@ fn main() {
|
||||||
grammar.gen_follow();
|
grammar.gen_follow();
|
||||||
println!("first: {:?}", grammar.first);
|
println!("first: {:?}", grammar.first);
|
||||||
println!("follow: {:?}", grammar.follow);
|
println!("follow: {:?}", grammar.follow);
|
||||||
let conflict = grammar.gen_parse_table();
|
let conflict = grammar.gen_ll_parse_table();
|
||||||
println!("conflict: {conflict}");
|
println!("conflict: {conflict}");
|
||||||
println!("prase table: {:?}", grammar.parse_table);
|
println!("prase table: {:?}", grammar.ll_parse_table);
|
||||||
println!("parse\n\n");
|
println!("parse\n\n");
|
||||||
println!(
|
println!(
|
||||||
"parsed: {:?}",
|
"parsed: {:?}",
|
||||||
grammar
|
grammar
|
||||||
.parser(&mut m.iter_mut())
|
.ll_parser(&mut m.iter_mut())
|
||||||
.parse()
|
.parse()
|
||||||
.map(|tree| tree.clean())
|
.map(|tree| tree.clean())
|
||||||
)
|
)
|
||||||
|
|
|
@ -1,13 +1,14 @@
|
||||||
use std::marker::PhantomData;
|
use std::marker::PhantomData;
|
||||||
|
|
||||||
#[macro_export]
|
#[macro_export]
|
||||||
macro_rules! scanner {
|
macro_rules! token_scanner {
|
||||||
($name:ident,$(
|
($name:ident,$(
|
||||||
$regex:tt : $code:expr
|
$regex:tt : $code:expr
|
||||||
)*) => {
|
)*) => {
|
||||||
impl $crate::scanner::MatchNext<$name> for $name {
|
impl $crate::scanner::MatchNext<$name> for $name {
|
||||||
fn match_next(code: &String) -> Option<(Self, usize)> {
|
fn match_next(code: &String) -> Option<(Self, usize)> {
|
||||||
use $name::*;
|
use $name::*;
|
||||||
|
use regex::Regex;
|
||||||
$(
|
$(
|
||||||
if let Some(capture) = Regex::new($regex).unwrap().captures(&code) {
|
if let Some(capture) = Regex::new($regex).unwrap().captures(&code) {
|
||||||
if let Some(main_capture) = capture.get(0) {
|
if let Some(main_capture) = capture.get(0) {
|
||||||
|
|
Loading…
Reference in a new issue