asn1_compiler/parser/asn/
module.rs1use std::collections::HashMap;
3
4use crate::tokenizer::Token;
5use anyhow::Result;
6
7use crate::parser::utils::{expect_keyword, expect_one_of_keywords, expect_token};
8
9use super::{
10 defs::parse_definition,
11 oid::parse_object_identifier,
12 structs::{
13 defs::Asn1Definition,
14 module::{Asn1Module, Asn1ModuleName, Asn1ModuleTag},
15 oid::ObjectIdentifier,
16 },
17};
18
19impl Asn1Module {
20 pub(crate) fn resolve_object_classes(
21 &mut self,
22 object_classes: &HashMap<String, Asn1Definition>,
23 ) -> Result<()> {
24 for def in self.definitions.values_mut() {
25 if !def.is_object_or_object_set() {
26 continue;
27 }
28 if let Some(class) = def.get_object_class() {
29 let classdef = object_classes.get(&class);
30 if classdef.is_none() {
31 return Err(parse_error!(
32 "Error Resolving Class '{}' for Definition '{}'",
33 class,
34 def.id()
35 )
36 .into());
37 }
38 let class = classdef.unwrap();
39 def.resolve_object_class(class)?;
40 }
41 }
42 Ok(())
43 }
44}
45
46pub(in crate::parser) fn parse_module(tokens: &[Token]) -> Result<(Asn1Module, usize)>
47where
48{
49 let mut consumed = 0;
50
51 let (name, name_consumed) = parse_module_name(&tokens[consumed..])?;
53 consumed += name_consumed;
54 log::trace!(
55 "Parsed module name '{}'. Consumed {} tokens",
56 name.name,
57 consumed
58 );
59
60 if expect_keyword(&tokens[consumed..], "DEFINITIONS")? {
62 consumed += 1;
63 } else {
64 return Err(unexpected_token!("DEFINITIONS", tokens[consumed]).into());
65 }
66
67 let (tags, tags_consumed) = maybe_parse_header_tags(&tokens[consumed..])?;
68 consumed += tags_consumed;
69 log::trace!(
70 "Parsed Header Tags '{:?}'. Consumed {} tokens",
71 tags,
72 consumed
73 );
74
75 if expect_token(&tokens[consumed..], Token::is_assignment)? {
78 consumed += 1;
79 } else {
80 return Err(unexpected_token!("::=", tokens[consumed]).into());
81 }
82 if expect_keyword(&tokens[consumed..], "BEGIN")? {
83 consumed += 1;
84 } else {
85 return Err(unexpected_token!("BEGIN", tokens[consumed]).into());
86 }
87
88 let (_, exports_consumed) = parse_module_maybe_exports(&tokens[consumed..])?;
90 consumed += exports_consumed;
91 log::trace!("Parsed EXPORTS. Consumed {} tokens.", consumed);
92
93 let (imports, imports_consumed) = parse_module_imports(&tokens[consumed..])?;
94 consumed += imports_consumed;
95 log::trace!(
96 "Parsed IMPORTS. Consumed {} tokens. Parsing Definitions Now",
97 consumed
98 );
99
100 let mut definitions = HashMap::new();
101 while !expect_keyword(&tokens[consumed..], "END")? {
102 let (def, definition_consumed) = parse_definition(&tokens[consumed..])?;
103 consumed += definition_consumed;
104 log::trace!(
105 "Parsed '{}' Definition. Consumed {} tokens so far",
106 def.id(),
107 consumed
108 );
109 definitions.insert(def.id(), def);
110 }
111
112 consumed += 1;
115
116 let module = Asn1Module::default()
117 .name(name)
118 .tags(tags)
119 .imports(imports)
120 .definitions(definitions);
121 Ok((module, consumed))
122}
123
124fn parse_module_maybe_exports(tokens: &[Token]) -> Result<((), usize)> {
125 let mut consumed = 0;
126 if expect_keyword(&tokens[consumed..], "EXPORTS")? {
127 consumed += 1;
128 loop {
129 if expect_token(&tokens[consumed..], Token::is_semicolon)? {
130 consumed += 1;
131 break;
132 }
133
134 if expect_token(&tokens[consumed..], Token::is_identifier)? {
135 consumed += 1;
136 }
137
138 if expect_token(&tokens[consumed..], Token::is_comma)? {
139 consumed += 1;
140 }
141 }
142 }
143 Ok(((), consumed))
144}
145
146fn parse_module_imports(tokens: &[Token]) -> Result<(HashMap<String, Asn1ModuleName>, usize)> {
147 let mut consumed = 0;
148
149 let mut imports = HashMap::new();
150 if expect_keyword(&tokens[consumed..], "IMPORTS")? {
151 consumed += 1;
152
153 loop {
154 let mut imported_defs = vec![];
155 while !expect_keyword(&tokens[consumed..], "FROM")? {
156 if expect_token(&tokens[consumed..], Token::is_identifier)? {
157 let definition = tokens[consumed].text.clone();
158 imported_defs.push(definition);
159 }
160 consumed += 1;
161 if expect_token(&tokens[consumed..], Token::is_comma)? {
162 consumed += 1;
163 }
164 }
165 consumed += 1;
166 let (module_name, module_name_consumed) = parse_module_name(&tokens[consumed..])?;
167 consumed += module_name_consumed;
168
169 for d in imported_defs {
170 if imports.contains_key(&d) {
171 return Err(parse_error!("Definition '{}' is imported twice", d).into());
172 }
173 let _ = imports.insert(d, module_name.clone());
174 }
175
176 if expect_token(&tokens[consumed..], Token::is_semicolon)? {
177 consumed += 1;
178 break;
179 }
180 }
181 }
182
183 Ok((imports, consumed))
184}
185
186fn maybe_parse_header_tags(tokens: &[Token]) -> Result<(Asn1ModuleTag, usize)> {
187 let mut consumed = 0;
188
189 let tag =
190 if expect_one_of_keywords(&tokens[consumed..], &["EXPLICIT", "IMPLICIT", "AUTOMATIC"])? {
191 let tag: Asn1ModuleTag = match tokens[consumed].text.as_str() {
192 "EXPLICIT" => Asn1ModuleTag::Explicit,
193 "IMPLICIT" => Asn1ModuleTag::Implicit,
194 "AUTOMATIC" => Asn1ModuleTag::Automatic,
195 _ => {
196 return Err(parse_error!("Should Never Reach").into());
198 }
199 };
200 consumed += 1;
201 if expect_keyword(&tokens[consumed..], "TAGS")? {
202 consumed += 1
203 } else {
204 return Err(unexpected_token!("TAGS", tokens[consumed]).into());
205 }
206 tag
207 } else {
208 Asn1ModuleTag::Explicit
209 };
210 Ok((tag, consumed))
211}
212
213fn parse_module_name(tokens: &[Token]) -> Result<(Asn1ModuleName, usize)> {
214 let mut consumed = 0;
215 let name = if expect_token(&tokens[consumed..], Token::is_module_reference)? {
218 tokens[consumed].text.clone()
219 } else {
220 return Err(parse_error!(
221 "Module Name '{}' is not a valid Module Reference",
222 tokens[consumed].text
223 )
224 .into());
225 };
226 consumed += 1;
227
228 let (oid, oid_consumed) = maybe_parse_object_identifer(&tokens[consumed..])?;
231 consumed += oid_consumed;
232
233 Ok((Asn1ModuleName::new(name, oid), consumed))
234}
235
236fn maybe_parse_object_identifer(tokens: &[Token]) -> Result<(Option<ObjectIdentifier>, usize)> {
237 match expect_token(tokens, Token::is_curly_begin) {
238 Ok(success) => {
239 if success {
240 match parse_object_identifier(tokens) {
241 Ok((oid, consumed)) => Ok((Some(oid), consumed)),
242 Err(e) => Err(e),
243 }
244 } else {
245 Ok((None, 0))
246 }
247 }
248 Err(_) => Ok((None, 0)),
251 }
252}
253
254#[cfg(test)]
255mod tests {
256
257 use super::*;
258 use crate::tokenizer::tokenize;
259
260 #[test]
261 fn empty_module_success() {
262 let input = "ModuleFoo DEFINITIONS ::= BEGIN END";
263 let reader = std::io::BufReader::new(std::io::Cursor::new(input));
264 let tokens = tokenize(reader);
265 assert!(tokens.is_ok());
266
267 let mut tokens = tokens.unwrap();
268 let module = parse_module(&mut tokens);
269 assert!(module.is_ok(), "{}: {:#?}", input, module.err().unwrap());
270
271 let (module, consumed) = module.unwrap();
272 assert_eq!(consumed, 5);
273
274 assert!(module.definitions.is_empty());
275 assert!(module.imports.is_empty());
276 assert_eq!(module._tags, Asn1ModuleTag::Explicit);
277 }
278 #[test]
282 fn parse_module_name_tests() {
283 struct ParseModuleNameTestCase<'tc> {
284 input: &'tc str,
285 success: bool,
286 consumed: usize,
287 oid_present: bool,
288 }
289
290 let test_cases = vec![
291 ParseModuleNameTestCase {
292 input: "ModuleFoo",
293 success: true,
294 consumed: 1,
295 oid_present: false,
296 },
297 ParseModuleNameTestCase {
298 input: "moduleFoo",
299 success: false,
300 consumed: 0,
301 oid_present: false,
302 },
303 ParseModuleNameTestCase {
304 input: "ModuleFoo { iso }",
305 success: true,
306 consumed: 4,
307 oid_present: true,
308 },
309 ParseModuleNameTestCase {
310 input: "ModuleFoo { iso ",
311 success: false,
312 consumed: 0,
313 oid_present: false,
314 },
315 ParseModuleNameTestCase {
316 input: "ModuleFoo iso ", success: true,
318 consumed: 1,
319 oid_present: false,
320 },
321 ParseModuleNameTestCase {
322 input: "NGAP-CommonDataTypes { itu-t (0) identified-organization (4) etsi (0) mobileDomain (0) ngran-Access (22) modules (3) ngap (1) version1 (1) ngap-CommonDataTypes (3) }", success: true, consumed: 39, oid_present: true,
323 }
324 ];
325
326 for tc in test_cases {
327 let reader = std::io::BufReader::new(std::io::Cursor::new(tc.input));
328 let tokens = tokenize(reader);
329 assert!(tokens.is_ok());
330
331 let mut tokens = tokens.unwrap();
332 let module = parse_module_name(&mut tokens);
333 assert_eq!(module.is_ok(), tc.success, "{}", tc.input);
334
335 if tc.success {
336 let (module, consumed) = module.unwrap();
337 assert_eq!(consumed, tc.consumed);
338 assert_eq!(module.oid.is_some(), tc.oid_present);
339 }
340 }
341 }
342}