compiler_course_helper/
lib.rs

1extern crate wasm_bindgen;
2
3use serde::{Deserialize, Serialize};
4use wasm_bindgen::prelude::*;
5
6mod grammar;
7pub use grammar::lr_fsm::LRFSMType;
8pub use grammar::Grammar;
9
10#[derive(Serialize, Deserialize)]
11pub struct WasmArgs {
12    pub grammar: String,
13    pub actions: Vec<Action>,
14    pub outputs: Vec<Output>,
15}
16
17// This function is intended to be called from JavaScript.
18// Example:
19// {
20//     "grammar": "E -> E + T | T\nT -> T * F | F\nF -> ( E ) | id",
21//     "actions": ["EliminateLeftRecursion"],
22//     "outputs": [
23//         {"NonTerminal": "JSON"},
24//         {"Production": "JSON"},
25//         {"LL1ParsingTable": "JSON"},
26//         {"LRParsingTable": ["LR0", "JSON"]}
27//     ]
28// }
29#[wasm_bindgen]
30pub fn wasm_grammar_to_output(json: &str) -> String {
31    let args: WasmArgs = serde_json::from_str(json).unwrap();
32    let result = grammar_to_output(&args.grammar, &args.actions, &args.outputs);
33    serde_json::to_string(&result).unwrap()
34}
35
36#[derive(Clone, Copy, Serialize, Deserialize)]
37pub enum Action {
38    EliminateLeftRecursion,
39}
40
41#[derive(Clone, Copy, Serialize, Deserialize)]
42pub enum Format {
43    Plain,
44    LaTeX,
45    JSON,
46}
47
48#[derive(Clone, Copy, Serialize, Deserialize)]
49pub enum Output {
50    Production(Format),
51    NonTerminal(Format),
52    LL1ParsingTable(Format),
53    LRFSM(LRFSMType, Format),
54    LRParsingTable(LRFSMType, Format),
55}
56
57impl Output {
58    pub fn format(&mut self, f: Format) {
59        match self {
60            Output::Production(format) => *format = f,
61            Output::NonTerminal(format) => *format = f,
62            Output::LL1ParsingTable(format) => *format = f,
63            Output::LRFSM(_, format) => *format = f,
64            Output::LRParsingTable(_, format) => *format = f,
65        }
66    }
67}
68
69pub fn grammar_to_output(
70    grammar: &str,
71    actions: &[Action],
72    outputs: &[Output],
73) -> Result<Vec<Result<String, String>>, String> {
74    let mut ret: Vec<Result<String, String>> = Vec::new();
75
76    let mut g = match Grammar::parse(grammar) {
77        Ok(g) => g,
78        Err(e) => {
79            return Err(e);
80        }
81    };
82
83    for action in actions {
84        match action {
85            Action::EliminateLeftRecursion => g.eliminate_left_recursion(),
86        }
87    }
88
89    for output in outputs {
90        match output {
91            Output::Production(format) => {
92                let t = g.to_production_output_vec();
93                ret.push(Ok(match format {
94                    Format::Plain => t.to_plaintext(),
95                    Format::LaTeX => t.to_latex(),
96                    Format::JSON => serde_json::to_string(&t).unwrap(),
97                }));
98            }
99            Output::NonTerminal(format) => {
100                let t = g.to_non_terminal_output_vec();
101                ret.push(Ok(match format {
102                    Format::Plain => t.to_plaintext(),
103                    Format::LaTeX => t.to_latex(),
104                    Format::JSON => serde_json::to_string(&t).unwrap(),
105                }));
106            }
107            Output::LL1ParsingTable(format) => {
108                let t = g.generate_ll1_parsing_table();
109                ret.push(Ok(match format {
110                    Format::Plain => t.to_plaintext(),
111                    Format::LaTeX => t.to_latex(),
112                    Format::JSON => serde_json::to_string(&t).unwrap(),
113                }));
114            }
115            Output::LRFSM(typ, format) => ret.push(g.to_lr_fsm(*typ).and_then(|t| {
116                Ok(match format {
117                    Format::Plain => t.to_plaintext(),
118                    Format::LaTeX => t.to_latex(),
119                    Format::JSON => serde_json::to_string(&t).unwrap(),
120                })
121            })),
122            Output::LRParsingTable(typ, format) => ret.push(g.to_lr_fsm(*typ).and_then(|t| {
123                let t = t.to_parsing_table();
124                Ok(match format {
125                    Format::Plain => t.to_plaintext(),
126                    Format::LaTeX => t.to_latex(),
127                    Format::JSON => serde_json::to_string(&t).unwrap(),
128                })
129            })),
130        }
131    }
132
133    Ok(ret)
134}
135
136#[cfg(test)]
137mod parse_tests {
138    use crate::grammar::EPSILON;
139
140    #[test]
141    fn simple_parse() {
142        let g = crate::Grammar::parse("S -> a").unwrap();
143
144        let s = g.symbol_table.get("S").unwrap().clone();
145        let a = g.symbol_table.get("a").unwrap().clone();
146        let epsilon = g.symbol_table.get(EPSILON).unwrap().clone();
147
148        assert_eq!(g.get_symbol_name(s), "S");
149        assert_eq!(g.get_symbol_name(a), "a");
150
151        assert_eq!(g.symbols[epsilon].non_terminal().unwrap().nullable, true);
152
153        assert_eq!(g.symbols[s].non_terminal().unwrap().productions[0], vec![a]);
154    }
155
156    #[test]
157    fn simple_parse_with_space() {
158        let g = crate::Grammar::parse("  S -> a ").unwrap();
159
160        let s = g.symbol_table.get("S").unwrap().clone();
161        let a = g.symbol_table.get("a").unwrap().clone();
162
163        assert_eq!(g.get_symbol_name(s), "S");
164        assert_eq!(g.get_symbol_name(a), "a");
165
166        assert_eq!(g.symbols[s].non_terminal().unwrap().productions[0], vec![a]);
167    }
168
169    #[test]
170    fn simple_parse_with_space_and_newline() {
171        let g = crate::Grammar::parse("  S -> a \n | b c").unwrap();
172
173        let s = g.symbol_table.get("S").unwrap().clone();
174        let a = g.symbol_table.get("a").unwrap().clone();
175        let b = g.symbol_table.get("b").unwrap().clone();
176        let c = g.symbol_table.get("c").unwrap().clone();
177
178        assert_eq!(g.get_symbol_name(s), "S");
179        assert_eq!(g.get_symbol_name(a), "a");
180        assert_eq!(g.get_symbol_name(b), "b");
181        assert_eq!(g.get_symbol_name(c), "c");
182        assert_eq!(g.symbols[s].non_terminal().unwrap().productions[0], vec![a]);
183        assert_eq!(
184            g.symbols[s].non_terminal().unwrap().productions[1],
185            vec![b, c]
186        );
187    }
188
189    #[test]
190    fn empty_parse() {
191        let _g = crate::Grammar::parse("  \n  ").unwrap();
192    }
193
194    #[test]
195    #[should_panic]
196    fn two_rightarrows_parse() {
197        let _g = crate::Grammar::parse("S -> a -> b").unwrap();
198    }
199
200    #[test]
201    #[should_panic]
202    fn no_left_parse() {
203        let _g = crate::Grammar::parse("-> a -> b").unwrap();
204    }
205
206    #[test]
207    #[should_panic]
208    fn no_previous_left_parse() {
209        let _g = crate::Grammar::parse("| a b\n S -> a").unwrap();
210    }
211
212    #[test]
213    #[should_panic]
214    fn left_contain_space() {
215        let _g = crate::Grammar::parse("S a S -> x").unwrap();
216    }
217}
218
219#[cfg(test)]
220mod nullable_first_follow_test {}
221
222#[cfg(test)]
223mod generate_ll1_parsing_table_test {
224    #[test]
225    fn expression_test() {
226        let mut g = crate::Grammar::parse(
227            "
228            E -> T E'
229            E' -> + T E' | ε
230            T -> F T'
231            T' -> * F T' | ε
232            F -> ( E ) | id
233            ",
234        )
235        .unwrap();
236
237        g.calculate_nullable_first_follow();
238        let result = g.generate_ll1_parsing_table();
239        println!("{}", result.to_plaintext());
240    }
241}