diff options
Diffstat (limited to 'src/parse.rs')
-rw-r--r-- | src/parse.rs | 200 |
1 files changed, 200 insertions, 0 deletions
diff --git a/src/parse.rs b/src/parse.rs new file mode 100644 index 0000000..5b613ca --- /dev/null +++ b/src/parse.rs | |||
@@ -0,0 +1,200 @@ | |||
1 | use crate::{lex::Stanza, Dict}; | ||
2 | |||
3 | pub struct ParseState { | ||
4 | dict: Dict, | ||
5 | status: Status, | ||
6 | current_entry: EntryBuilder, | ||
7 | errors: Vec<ParseError>, | ||
8 | } | ||
9 | |||
10 | #[derive(Debug)] | ||
11 | enum ParseError { | ||
12 | Build(EntryBuilderError), | ||
13 | UndefinedState(Status, EntryBuilder, Stanza), | ||
14 | } | ||
15 | |||
16 | impl ParseState { | ||
17 | pub fn new() -> Self { | ||
18 | Self { | ||
19 | dict: Dict::new(), | ||
20 | status: Status::Start, | ||
21 | current_entry: EntryBuilder::new(), | ||
22 | errors: Vec::new(), | ||
23 | } | ||
24 | } | ||
25 | |||
26 | pub fn advance(mut self, stanza: Stanza) -> Self { | ||
27 | match (self.status, stanza) { | ||
28 | (Status::Start, Stanza::Entry(e)) => { | ||
29 | self.current_entry.set_name(e); | ||
30 | self.status = Status::ContainsName; | ||
31 | } | ||
32 | (Status::ContainsName, Stanza::Defn(d)) => { | ||
33 | self.current_entry.push_defn(d); | ||
34 | self.status = Status::ContainsOneDefn; | ||
35 | } | ||
36 | (Status::ContainsOneDefn | Status::ContainsMulDefn, Stanza::Defn(d)) => { | ||
37 | self.current_entry.push_defn(d); | ||
38 | self.status = Status::ContainsMulDefn; | ||
39 | } | ||
40 | ( | ||
41 | Status::ContainsOneDefn | Status::ContainsMulDefn | Status::ContainsSynonym, | ||
42 | Stanza::Note(n), | ||
43 | ) => { | ||
44 | self.current_entry.set_note(n); | ||
45 | self.status = Status::ContainsNote; | ||
46 | } | ||
47 | ( | ||
48 | Status::ContainsOneDefn | Status::ContainsMulDefn | Status::ContainsNote, | ||
49 | Stanza::Synonym(s), | ||
50 | ) => { | ||
51 | self.current_entry.set_synonym(s); | ||
52 | self.status = Status::ContainsSynonym; | ||
53 | } | ||
54 | ( | ||
55 | Status::ContainsOneDefn | ||
56 | | Status::ContainsMulDefn | ||
57 | | Status::ContainsNote | ||
58 | | Status::ContainsSynonym, | ||
59 | Stanza::Entry(e), | ||
60 | ) => { | ||
61 | // flush the current entry | ||
62 | match self.current_entry.build() { | ||
63 | Ok(entry) => self.dict.insert(entry.name, entry.into()), | ||
64 | Err(b) => self.register_error(ParseError::Build(b)), | ||
65 | }; | ||
66 | |||
67 | // begin with the new one | ||
68 | self.current_entry.clear(); | ||
69 | self.current_entry.set_name(e); | ||
70 | self.status = Status::ContainsName; | ||
71 | } | ||
72 | (Status::ContainsName, Stanza::Entry(e)) => { | ||
73 | // dump unfinished entry and enter new entry | ||
74 | self.current_entry.clear(); | ||
75 | self.current_entry.set_name(e); | ||
76 | self.status = Status::ContainsName; | ||
77 | } | ||
78 | (_, new_entry) => { | ||
79 | // any other states means our parser is entering undefined teritorry | ||
80 | // register an error if we have anything in current_entry | ||
81 | self.register_undefined_state_error(new_entry); | ||
82 | // and set the status to Start and fast forward to the next entry | ||
83 | self.current_entry.clear(); | ||
84 | self.status = Status::Start; | ||
85 | } | ||
86 | } | ||
87 | self | ||
88 | } | ||
89 | |||
90 | fn register_error(&mut self, error: ParseError) { | ||
91 | self.errors.push(error) | ||
92 | } | ||
93 | |||
94 | fn register_undefined_state_error(&mut self, new_entry: Stanza) { | ||
95 | self.register_error(ParseError::UndefinedState( | ||
96 | self.status, | ||
97 | self.current_entry.clone(), | ||
98 | new_entry, | ||
99 | )); | ||
100 | } | ||
101 | |||
102 | pub fn finish(self) -> Dict { | ||
103 | self.dict | ||
104 | } | ||
105 | |||
106 | pub fn dump(&self) { | ||
107 | for err in &self.errors { | ||
108 | eprintln!("{err:?}"); | ||
109 | } | ||
110 | } | ||
111 | } | ||
112 | |||
113 | #[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone, Copy)] | ||
114 | enum Status { | ||
115 | // ready to accept a new entry | ||
116 | Start, | ||
117 | // ready to accept a defn | ||
118 | ContainsName, | ||
119 | // can accept notes or synonyms, or flush this entry | ||
120 | ContainsOneDefn, | ||
121 | // can accept notes or synonyms, or flush this entry | ||
122 | ContainsMulDefn, | ||
123 | // can accept a synonym | ||
124 | ContainsNote, | ||
125 | // can accept a note | ||
126 | ContainsSynonym, | ||
127 | // mangled stanza, skip until the next entry occurs | ||
128 | } | ||
129 | |||
130 | #[derive(Debug, Clone)] | ||
131 | struct Entry { | ||
132 | name: &'static str, | ||
133 | defn: Vec<&'static str>, | ||
134 | note: Option<&'static str>, | ||
135 | synonym: Option<&'static str>, | ||
136 | } | ||
137 | |||
138 | impl From<Entry> for crate::DictValue { | ||
139 | fn from(entry: Entry) -> Self { | ||
140 | Self { | ||
141 | defn: entry.defn, | ||
142 | note: entry.note, | ||
143 | synonym: entry.synonym, | ||
144 | } | ||
145 | } | ||
146 | } | ||
147 | |||
148 | #[derive(Debug, Default, Clone)] | ||
149 | struct EntryBuilder { | ||
150 | name: Option<&'static str>, | ||
151 | defn: Vec<&'static str>, | ||
152 | note: Option<&'static str>, | ||
153 | synonym: Option<&'static str>, | ||
154 | } | ||
155 | |||
156 | #[derive(Debug)] | ||
157 | enum EntryBuilderError { | ||
158 | MissingField(&'static str), | ||
159 | } | ||
160 | |||
161 | impl EntryBuilder { | ||
162 | fn new() -> Self { | ||
163 | Self::default() | ||
164 | } | ||
165 | |||
166 | fn clear(&mut self) { | ||
167 | *self = Self::default(); | ||
168 | } | ||
169 | |||
170 | fn set_name(&mut self, name: &'static str) { | ||
171 | self.name = Some(name); | ||
172 | } | ||
173 | |||
174 | fn push_defn(&mut self, defn: &'static str) { | ||
175 | self.defn.push(defn); | ||
176 | } | ||
177 | |||
178 | fn set_note(&mut self, note: &'static str) { | ||
179 | self.note = Some(note); | ||
180 | } | ||
181 | |||
182 | fn set_synonym(&mut self, synonym: &'static str) { | ||
183 | self.synonym = Some(synonym); | ||
184 | } | ||
185 | |||
186 | fn build(&self) -> Result<Entry, EntryBuilderError> { | ||
187 | let name = self.name.ok_or(EntryBuilderError::MissingField("name"))?; | ||
188 | let defn = if self.defn.is_empty() { | ||
189 | return Err(EntryBuilderError::MissingField("defn")); | ||
190 | } else { | ||
191 | self.defn.clone() | ||
192 | }; | ||
193 | Ok(Entry { | ||
194 | name, | ||
195 | defn, | ||
196 | note: self.note, | ||
197 | synonym: self.synonym, | ||
198 | }) | ||
199 | } | ||
200 | } | ||