compiler_course_helper/
lib.rs1extern 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#[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}