compiler_course_helper/grammar/
parse.rs

1use crate::Grammar;
2
3impl Grammar {
4    pub fn parse(grammar: &str) -> Result<Self, String> {
5        let mut g = Self::new();
6
7        let mut raw_productions: Vec<(usize, &str)> = Vec::new();
8
9        let mut previous_left: Option<usize> = None;
10        for (i, line) in grammar.lines().enumerate() {
11            if line.chars().all(|c| c.is_whitespace()) {
12                continue;
13            }
14            let parts: Vec<&str> = line.split("->").collect();
15            if parts.len() > 2 {
16                return Err(format!("Line {}: too many \"->\"", i + 1));
17            }
18            let (left, rights): (usize, &str) = if parts.len() == 2 {
19                let left_str = parts[0].trim();
20                if left_str.split_whitespace().count() != 1 {
21                    return Err(format!("Line {}: left side contains whitespace", i + 1));
22                } else if left_str.is_empty() {
23                    return Err(format!("Line {}: empty left side", i + 1));
24                } else {
25                    (
26                        if let Some(idx) = g.get_symbol_index(left_str) {
27                            idx
28                        } else {
29                            g.add_non_terminal(left_str)
30                        },
31                        parts[1].trim(),
32                    )
33                }
34            } else {
35                if let Some(idx) = previous_left {
36                    (idx, parts[0].trim()[1..].trim())
37                } else {
38                    return Err(format!("Line {}: cannot find left side", i + 1));
39                }
40            };
41
42            previous_left = Some(left);
43
44            raw_productions.push((left, rights));
45        }
46
47        for (left, rights) in raw_productions {
48            for right in rights.split("|") {
49                let symbols = right
50                    .split_whitespace()
51                    .map(|s| {
52                        if let Some(idx) = g.get_symbol_index(s) {
53                            idx
54                        } else {
55                            g.add_terminal(s.to_string())
56                        }
57                    })
58                    .collect();
59                g.add_production(left, symbols);
60            }
61        }
62
63        let start_symbol: Option<usize> = if let Some(nt) = g.non_terminal_iter().next() {
64            Some(g.symbol_table[&nt.name])
65        } else {
66            None
67        };
68        g.start_symbol = start_symbol;
69
70        Ok(g)
71    }
72}