aboutsummaryrefslogtreecommitdiff
path: root/src/parse.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/parse.rs')
-rw-r--r--src/parse.rs200
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 @@
1use crate::{lex::Stanza, Dict};
2
3pub struct ParseState {
4 dict: Dict,
5 status: Status,
6 current_entry: EntryBuilder,
7 errors: Vec<ParseError>,
8}
9
10#[derive(Debug)]
11enum ParseError {
12 Build(EntryBuilderError),
13 UndefinedState(Status, EntryBuilder, Stanza),
14}
15
16impl 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)]
114enum 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)]
131struct Entry {
132 name: &'static str,
133 defn: Vec<&'static str>,
134 note: Option<&'static str>,
135 synonym: Option<&'static str>,
136}
137
138impl 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)]
149struct 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)]
157enum EntryBuilderError {
158 MissingField(&'static str),
159}
160
161impl 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}