1 // Copyright 2023 Google LLC
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // https://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14
15 use crate::ast;
16 use codespan_reporting::diagnostic::Diagnostic;
17 use codespan_reporting::files;
18 use pest::iterators::{Pair, Pairs};
19 use pest::{Parser, Token};
20 use std::iter::{Filter, Peekable};
21
22 // Generate the PDL parser.
23 //
24 // TODO:
25 // - use #[grammar = "pdl.pest"]
26 // currently not possible because CARGO_MANIFEST_DIR is not set
27 // in soong environment.
28 // - use silent atomic rules for keywords like
29 // ENUM = @{ "enum" ~ WHITESPACE }
30 // currently not implemented in pest:
31 // https://github.com/pest-parser/pest/issues/520
32 #[derive(pest_derive::Parser)]
33 #[grammar_inline = r#"
34 WHITESPACE = _{ " " | "\n" | "\r" | "\t" }
35 COMMENT = { block_comment | line_comment }
36
37 block_comment = { "/*" ~ (!"*/" ~ ANY)* ~ "*/" }
38 line_comment = { "//" ~ (!"\n" ~ ANY)* }
39
40 alpha = { 'a'..'z' | 'A'..'Z' }
41 digit = { '0'..'9' }
42 hexdigit = { digit | 'a'..'f' | 'A'..'F' }
43 alphanum = { alpha | digit | "_" }
44
45 identifier = @{ alpha ~ alphanum* }
46 payload_identifier = @{ "_payload_" }
47 body_identifier = @{ "_body_" }
48 intvalue = @{ digit+ }
49 hexvalue = @{ ("0x"|"0X") ~ hexdigit+ }
50 integer = @{ hexvalue | intvalue }
51 string = @{ "\"" ~ (!"\"" ~ ANY)* ~ "\"" }
52 size_modifier = @{ "+" ~ intvalue }
53
54 ENUM = @{ "enum" ~ WHITESPACE }
55 PACKET = @{ "packet" ~ WHITESPACE }
56 STRUCT = @{ "struct" ~ WHITESPACE }
57 GROUP = @{ "group" ~ WHITESPACE }
58 CHECKSUM = @{ "checksum" ~ WHITESPACE }
59 CUSTOM_FIELD = @{ "custom_field" ~ WHITESPACE }
60 TEST = @{ "test" ~ WHITESPACE }
61
62 endianness_declaration = ${ ("little_endian_packets" | "big_endian_packets") ~ WHITESPACE }
63
64 enum_value = { identifier ~ "=" ~ integer }
65 enum_value_list = { enum_value ~ ("," ~ enum_value)* ~ ","? }
66 enum_range = {
67 identifier ~ "=" ~ integer ~ ".." ~ integer ~ ("{" ~
68 enum_value_list ~
69 "}")?
70 }
71 enum_other = { identifier ~ "=" ~ ".." }
72 enum_tag = { enum_range | enum_value | enum_other }
73 enum_tag_list = { enum_tag ~ ("," ~ enum_tag)* ~ ","? }
74 enum_declaration = {
75 ENUM ~ identifier ~ ":" ~ integer ~ "{" ~
76 enum_tag_list ~
77 "}"
78 }
79
80 constraint = { identifier ~ "=" ~ (identifier|integer) }
81 constraint_list = { constraint ~ ("," ~ constraint)* }
82
83 checksum_field = { "_checksum_start_" ~ "(" ~ identifier ~ ")" }
84 padding_field = { "_padding_" ~ "[" ~ integer ~ "]" }
85 size_field = { "_size_" ~ "(" ~ (identifier|payload_identifier|body_identifier) ~ ")" ~ ":" ~ integer }
86 count_field = { "_count_" ~ "(" ~ identifier ~ ")" ~ ":" ~ integer }
87 elementsize_field = { "_elementsize_" ~ "(" ~ identifier ~ ")" ~ ":" ~ integer }
88 body_field = @{ "_body_" }
89 payload_field = { "_payload_" ~ (":" ~ "[" ~ size_modifier ~ "]")? }
90 fixed_field = { "_fixed_" ~ "=" ~ (
91 (integer ~ ":" ~ integer) |
92 (identifier ~ ":" ~ identifier)
93 )}
94 reserved_field = { "_reserved_" ~ ":" ~ integer }
95 array_field = { identifier ~ ":" ~ (integer|identifier) ~
96 "[" ~ (size_modifier|integer)? ~ "]"
97 }
98 scalar_field = { identifier ~ ":" ~ integer }
99 typedef_field = { identifier ~ ":" ~ identifier }
100 group_field = { identifier ~ ("{" ~ constraint_list? ~ "}")? }
101
102 field_desc = _{
103 checksum_field |
104 padding_field |
105 size_field |
106 count_field |
107 elementsize_field |
108 body_field |
109 payload_field |
110 fixed_field |
111 reserved_field |
112 array_field |
113 scalar_field |
114 typedef_field |
115 group_field
116 }
117 field = { field_desc ~ ("if" ~ constraint)? }
118 field_list = { field ~ ("," ~ field)* ~ ","? }
119
120 packet_declaration = {
121 PACKET ~ identifier ~
122 (":" ~ identifier)? ~
123 ("(" ~ constraint_list ~ ")")? ~
124 "{" ~
125 field_list? ~
126 "}"
127 }
128
129 struct_declaration = {
130 STRUCT ~ identifier ~
131 (":" ~ identifier)? ~
132 ("(" ~ constraint_list ~ ")")? ~
133 "{" ~
134 field_list? ~
135 "}"
136 }
137
138 group_declaration = {
139 GROUP ~ identifier ~ "{" ~ field_list ~ "}"
140 }
141
142 checksum_declaration = {
143 CHECKSUM ~ identifier ~ ":" ~ integer ~ string
144 }
145
146 custom_field_declaration = {
147 CUSTOM_FIELD ~ identifier ~ (":" ~ integer)? ~ string
148 }
149
150 test_case = { string }
151 test_case_list = _{ test_case ~ ("," ~ test_case)* ~ ","? }
152 test_declaration = {
153 TEST ~ identifier ~ "{" ~
154 test_case_list ~
155 "}"
156 }
157
158 declaration = _{
159 enum_declaration |
160 packet_declaration |
161 struct_declaration |
162 group_declaration |
163 checksum_declaration |
164 custom_field_declaration |
165 test_declaration
166 }
167
168 file = {
169 SOI ~
170 endianness_declaration ~
171 declaration* ~
172 EOI
173 }
174 "#]
175 pub struct PDLParser;
176
177 type Node<'i> = Pair<'i, Rule>;
178 type NodeIterator<'i> = Peekable<Filter<Pairs<'i, Rule>, fn(&Node<'i>) -> bool>>;
179 struct Context<'a> {
180 file: ast::FileId,
181 line_starts: &'a Vec<usize>,
182 key: std::cell::Cell<usize>,
183 }
184
185 trait Helpers<'i> {
children(self) -> NodeIterator<'i>186 fn children(self) -> NodeIterator<'i>;
as_loc(&self, context: &Context) -> ast::SourceRange187 fn as_loc(&self, context: &Context) -> ast::SourceRange;
as_string(&self) -> String188 fn as_string(&self) -> String;
as_usize(&self) -> Result<usize, String>189 fn as_usize(&self) -> Result<usize, String>;
190 }
191
192 impl<'a> Context<'a> {
field_key(&self) -> ast::FieldKey193 fn field_key(&self) -> ast::FieldKey {
194 ast::FieldKey(self.key.replace(self.key.get() + 1))
195 }
196
decl_key(&self) -> ast::DeclKey197 fn decl_key(&self) -> ast::DeclKey {
198 ast::DeclKey(self.key.replace(self.key.get() + 1))
199 }
200 }
201
202 impl<'i> Helpers<'i> for Node<'i> {
children(self) -> NodeIterator<'i>203 fn children(self) -> NodeIterator<'i> {
204 self.into_inner().filter((|n| n.as_rule() != Rule::COMMENT) as fn(&Self) -> bool).peekable()
205 }
206
as_loc(&self, context: &Context) -> ast::SourceRange207 fn as_loc(&self, context: &Context) -> ast::SourceRange {
208 let span = self.as_span();
209 ast::SourceRange {
210 file: context.file,
211 start: ast::SourceLocation::new(span.start_pos().pos(), context.line_starts),
212 end: ast::SourceLocation::new(span.end_pos().pos(), context.line_starts),
213 }
214 }
215
as_string(&self) -> String216 fn as_string(&self) -> String {
217 self.as_str().to_owned()
218 }
219
as_usize(&self) -> Result<usize, String>220 fn as_usize(&self) -> Result<usize, String> {
221 let text = self.as_str();
222 if let Some(num) = text.strip_prefix("0x") {
223 usize::from_str_radix(num, 16)
224 .map_err(|_| format!("cannot convert '{}' to usize", self.as_str()))
225 } else {
226 #[allow(clippy::from_str_radix_10)]
227 usize::from_str_radix(text, 10)
228 .map_err(|_| format!("cannot convert '{}' to usize", self.as_str()))
229 }
230 }
231 }
232
err_unexpected_rule<T>(expected: Rule, found: Rule) -> Result<T, String>233 fn err_unexpected_rule<T>(expected: Rule, found: Rule) -> Result<T, String> {
234 Err(format!("expected rule {:?}, got {:?}", expected, found))
235 }
236
err_missing_rule<T>(expected: Rule) -> Result<T, String>237 fn err_missing_rule<T>(expected: Rule) -> Result<T, String> {
238 Err(format!("expected rule {:?}, got nothing", expected))
239 }
240
expect<'i>(iter: &mut impl Iterator<Item = Node<'i>>, rule: Rule) -> Result<Node<'i>, String>241 fn expect<'i>(iter: &mut impl Iterator<Item = Node<'i>>, rule: Rule) -> Result<Node<'i>, String> {
242 match iter.next() {
243 Some(node) if node.as_rule() == rule => Ok(node),
244 Some(node) => err_unexpected_rule(rule, node.as_rule()),
245 None => err_missing_rule(rule),
246 }
247 }
248
maybe<'i>(iter: &mut NodeIterator<'i>, rule: Rule) -> Option<Node<'i>>249 fn maybe<'i>(iter: &mut NodeIterator<'i>, rule: Rule) -> Option<Node<'i>> {
250 iter.next_if(|n| n.as_rule() == rule)
251 }
252
parse_identifier(iter: &mut NodeIterator<'_>) -> Result<String, String>253 fn parse_identifier(iter: &mut NodeIterator<'_>) -> Result<String, String> {
254 expect(iter, Rule::identifier).map(|n| n.as_string())
255 }
256
parse_integer(iter: &mut NodeIterator<'_>) -> Result<usize, String>257 fn parse_integer(iter: &mut NodeIterator<'_>) -> Result<usize, String> {
258 expect(iter, Rule::integer).and_then(|n| n.as_usize())
259 }
260
parse_identifier_opt(iter: &mut NodeIterator<'_>) -> Result<Option<String>, String>261 fn parse_identifier_opt(iter: &mut NodeIterator<'_>) -> Result<Option<String>, String> {
262 Ok(maybe(iter, Rule::identifier).map(|n| n.as_string()))
263 }
264
parse_integer_opt(iter: &mut NodeIterator<'_>) -> Result<Option<usize>, String>265 fn parse_integer_opt(iter: &mut NodeIterator<'_>) -> Result<Option<usize>, String> {
266 maybe(iter, Rule::integer).map(|n| n.as_usize()).transpose()
267 }
268
parse_identifier_or_integer( iter: &mut NodeIterator<'_>, ) -> Result<(Option<String>, Option<usize>), String>269 fn parse_identifier_or_integer(
270 iter: &mut NodeIterator<'_>,
271 ) -> Result<(Option<String>, Option<usize>), String> {
272 match iter.next() {
273 Some(n) if n.as_rule() == Rule::identifier => Ok((Some(n.as_string()), None)),
274 Some(n) if n.as_rule() == Rule::integer => Ok((None, Some(n.as_usize()?))),
275 Some(n) => Err(format!(
276 "expected rule {:?} or {:?}, got {:?}",
277 Rule::identifier,
278 Rule::integer,
279 n.as_rule()
280 )),
281 None => {
282 Err(format!("expected rule {:?} or {:?}, got nothing", Rule::identifier, Rule::integer))
283 }
284 }
285 }
286
parse_string<'i>(iter: &mut impl Iterator<Item = Node<'i>>) -> Result<String, String>287 fn parse_string<'i>(iter: &mut impl Iterator<Item = Node<'i>>) -> Result<String, String> {
288 expect(iter, Rule::string)
289 .map(|n| n.as_str())
290 .and_then(|s| s.strip_prefix('"').ok_or_else(|| "expected \" prefix".to_owned()))
291 .and_then(|s| s.strip_suffix('"').ok_or_else(|| "expected \" suffix".to_owned()))
292 .map(|s| s.to_owned())
293 }
294
parse_size_modifier_opt(iter: &mut NodeIterator<'_>) -> Option<String>295 fn parse_size_modifier_opt(iter: &mut NodeIterator<'_>) -> Option<String> {
296 maybe(iter, Rule::size_modifier).map(|n| n.as_string())
297 }
298
parse_endianness(node: Node<'_>, context: &Context) -> Result<ast::Endianness, String>299 fn parse_endianness(node: Node<'_>, context: &Context) -> Result<ast::Endianness, String> {
300 if node.as_rule() != Rule::endianness_declaration {
301 err_unexpected_rule(Rule::endianness_declaration, node.as_rule())
302 } else {
303 Ok(ast::Endianness {
304 loc: node.as_loc(context),
305 value: match node.as_str().trim() {
306 "little_endian_packets" => ast::EndiannessValue::LittleEndian,
307 "big_endian_packets" => ast::EndiannessValue::BigEndian,
308 _ => unreachable!(),
309 },
310 })
311 }
312 }
313
parse_constraint(node: Node<'_>, context: &Context) -> Result<ast::Constraint, String>314 fn parse_constraint(node: Node<'_>, context: &Context) -> Result<ast::Constraint, String> {
315 if node.as_rule() != Rule::constraint {
316 err_unexpected_rule(Rule::constraint, node.as_rule())
317 } else {
318 let loc = node.as_loc(context);
319 let mut children = node.children();
320 let id = parse_identifier(&mut children)?;
321 let (tag_id, value) = parse_identifier_or_integer(&mut children)?;
322 Ok(ast::Constraint { id, loc, value, tag_id })
323 }
324 }
325
parse_constraint_list_opt( iter: &mut NodeIterator<'_>, context: &Context, ) -> Result<Vec<ast::Constraint>, String>326 fn parse_constraint_list_opt(
327 iter: &mut NodeIterator<'_>,
328 context: &Context,
329 ) -> Result<Vec<ast::Constraint>, String> {
330 maybe(iter, Rule::constraint_list)
331 .map_or(Ok(vec![]), |n| n.children().map(|n| parse_constraint(n, context)).collect())
332 }
333
parse_enum_value(node: Node<'_>, context: &Context) -> Result<ast::TagValue, String>334 fn parse_enum_value(node: Node<'_>, context: &Context) -> Result<ast::TagValue, String> {
335 if node.as_rule() != Rule::enum_value {
336 err_unexpected_rule(Rule::enum_value, node.as_rule())
337 } else {
338 let loc = node.as_loc(context);
339 let mut children = node.children();
340 let id = parse_identifier(&mut children)?;
341 let value = parse_integer(&mut children)?;
342 Ok(ast::TagValue { id, loc, value })
343 }
344 }
345
parse_enum_value_list_opt( iter: &mut NodeIterator<'_>, context: &Context, ) -> Result<Vec<ast::TagValue>, String>346 fn parse_enum_value_list_opt(
347 iter: &mut NodeIterator<'_>,
348 context: &Context,
349 ) -> Result<Vec<ast::TagValue>, String> {
350 maybe(iter, Rule::enum_value_list)
351 .map_or(Ok(vec![]), |n| n.children().map(|n| parse_enum_value(n, context)).collect())
352 }
353
parse_enum_range(node: Node<'_>, context: &Context) -> Result<ast::TagRange, String>354 fn parse_enum_range(node: Node<'_>, context: &Context) -> Result<ast::TagRange, String> {
355 if node.as_rule() != Rule::enum_range {
356 err_unexpected_rule(Rule::enum_range, node.as_rule())
357 } else {
358 let loc = node.as_loc(context);
359 let mut children = node.children();
360 let id = parse_identifier(&mut children)?;
361 let start = parse_integer(&mut children)?;
362 let end = parse_integer(&mut children)?;
363 let tags = parse_enum_value_list_opt(&mut children, context)?;
364 Ok(ast::TagRange { id, loc, range: start..=end, tags })
365 }
366 }
367
parse_enum_other(node: Node<'_>, context: &Context) -> Result<ast::TagOther, String>368 fn parse_enum_other(node: Node<'_>, context: &Context) -> Result<ast::TagOther, String> {
369 if node.as_rule() != Rule::enum_other {
370 err_unexpected_rule(Rule::enum_other, node.as_rule())
371 } else {
372 let loc = node.as_loc(context);
373 let mut children = node.children();
374 let id = parse_identifier(&mut children)?;
375 Ok(ast::TagOther { id, loc })
376 }
377 }
378
parse_enum_tag(node: Node<'_>, context: &Context) -> Result<ast::Tag, String>379 fn parse_enum_tag(node: Node<'_>, context: &Context) -> Result<ast::Tag, String> {
380 if node.as_rule() != Rule::enum_tag {
381 err_unexpected_rule(Rule::enum_tag, node.as_rule())
382 } else {
383 match node.children().next() {
384 Some(node) if node.as_rule() == Rule::enum_value => {
385 Ok(ast::Tag::Value(parse_enum_value(node, context)?))
386 }
387 Some(node) if node.as_rule() == Rule::enum_range => {
388 Ok(ast::Tag::Range(parse_enum_range(node, context)?))
389 }
390 Some(node) if node.as_rule() == Rule::enum_other => {
391 Ok(ast::Tag::Other(parse_enum_other(node, context)?))
392 }
393 Some(node) => Err(format!(
394 "expected rule {:?} or {:?}, got {:?}",
395 Rule::enum_value,
396 Rule::enum_range,
397 node.as_rule()
398 )),
399 None => Err(format!(
400 "expected rule {:?} or {:?}, got nothing",
401 Rule::enum_value,
402 Rule::enum_range
403 )),
404 }
405 }
406 }
407
parse_enum_tag_list( iter: &mut NodeIterator<'_>, context: &Context, ) -> Result<Vec<ast::Tag>, String>408 fn parse_enum_tag_list(
409 iter: &mut NodeIterator<'_>,
410 context: &Context,
411 ) -> Result<Vec<ast::Tag>, String> {
412 expect(iter, Rule::enum_tag_list)
413 .and_then(|n| n.children().map(|n| parse_enum_tag(n, context)).collect())
414 }
415
parse_field(node: Node<'_>, context: &Context) -> Result<ast::Field, String>416 fn parse_field(node: Node<'_>, context: &Context) -> Result<ast::Field, String> {
417 let loc = node.as_loc(context);
418 let mut children = node.children();
419 let desc = children.next().unwrap();
420 let cond = children.next();
421 let rule = desc.as_rule();
422 let mut children = desc.children();
423 Ok(ast::Field {
424 loc,
425 key: context.field_key(),
426 cond: cond.map(|constraint| parse_constraint(constraint, context)).transpose()?,
427 desc: match rule {
428 Rule::checksum_field => {
429 let field_id = parse_identifier(&mut children)?;
430 ast::FieldDesc::Checksum { field_id }
431 }
432 Rule::padding_field => {
433 let size = parse_integer(&mut children)?;
434 ast::FieldDesc::Padding { size }
435 }
436 Rule::size_field => {
437 let field_id = match children.next() {
438 Some(n) if n.as_rule() == Rule::identifier => n.as_string(),
439 Some(n) if n.as_rule() == Rule::payload_identifier => n.as_string(),
440 Some(n) if n.as_rule() == Rule::body_identifier => n.as_string(),
441 Some(n) => err_unexpected_rule(Rule::identifier, n.as_rule())?,
442 None => err_missing_rule(Rule::identifier)?,
443 };
444 let width = parse_integer(&mut children)?;
445 ast::FieldDesc::Size { field_id, width }
446 }
447 Rule::count_field => {
448 let field_id = parse_identifier(&mut children)?;
449 let width = parse_integer(&mut children)?;
450 ast::FieldDesc::Count { field_id, width }
451 }
452 Rule::elementsize_field => {
453 let field_id = parse_identifier(&mut children)?;
454 let width = parse_integer(&mut children)?;
455 ast::FieldDesc::ElementSize { field_id, width }
456 }
457 Rule::body_field => ast::FieldDesc::Body,
458 Rule::payload_field => {
459 let size_modifier = parse_size_modifier_opt(&mut children);
460 ast::FieldDesc::Payload { size_modifier }
461 }
462 Rule::fixed_field => match children.next() {
463 Some(n) if n.as_rule() == Rule::integer => {
464 let value = n.as_usize()?;
465 let width = parse_integer(&mut children)?;
466 ast::FieldDesc::FixedScalar { width, value }
467 }
468 Some(n) if n.as_rule() == Rule::identifier => {
469 let tag_id = n.as_string();
470 let enum_id = parse_identifier(&mut children)?;
471 ast::FieldDesc::FixedEnum { enum_id, tag_id }
472 }
473 _ => unreachable!(),
474 },
475 Rule::reserved_field => {
476 let width = parse_integer(&mut children)?;
477 ast::FieldDesc::Reserved { width }
478 }
479 Rule::array_field => {
480 let id = parse_identifier(&mut children)?;
481 let (type_id, width) = parse_identifier_or_integer(&mut children)?;
482 let (size, size_modifier) = match children.next() {
483 Some(n) if n.as_rule() == Rule::integer => (Some(n.as_usize()?), None),
484 Some(n) if n.as_rule() == Rule::size_modifier => (None, Some(n.as_string())),
485 Some(n) => {
486 return Err(format!(
487 "expected rule {:?} or {:?}, got {:?}",
488 Rule::integer,
489 Rule::size_modifier,
490 n.as_rule()
491 ))
492 }
493 None => (None, None),
494 };
495 ast::FieldDesc::Array { id, type_id, width, size, size_modifier }
496 }
497 Rule::scalar_field => {
498 let id = parse_identifier(&mut children)?;
499 let width = parse_integer(&mut children)?;
500 ast::FieldDesc::Scalar { id, width }
501 }
502 Rule::typedef_field => {
503 let id = parse_identifier(&mut children)?;
504 let type_id = parse_identifier(&mut children)?;
505 ast::FieldDesc::Typedef { id, type_id }
506 }
507 Rule::group_field => {
508 let group_id = parse_identifier(&mut children)?;
509 let constraints = parse_constraint_list_opt(&mut children, context)?;
510 ast::FieldDesc::Group { group_id, constraints }
511 }
512 _ => return Err(format!("expected rule *_field, got {:?}", rule)),
513 },
514 })
515 }
516
parse_field_list(iter: &mut NodeIterator, context: &Context) -> Result<Vec<ast::Field>, String>517 fn parse_field_list(iter: &mut NodeIterator, context: &Context) -> Result<Vec<ast::Field>, String> {
518 expect(iter, Rule::field_list)
519 .and_then(|n| n.children().map(|n| parse_field(n, context)).collect())
520 }
521
parse_field_list_opt( iter: &mut NodeIterator, context: &Context, ) -> Result<Vec<ast::Field>, String>522 fn parse_field_list_opt(
523 iter: &mut NodeIterator,
524 context: &Context,
525 ) -> Result<Vec<ast::Field>, String> {
526 maybe(iter, Rule::field_list)
527 .map_or(Ok(vec![]), |n| n.children().map(|n| parse_field(n, context)).collect())
528 }
529
parse_toplevel(root: Node<'_>, context: &Context) -> Result<ast::File, String>530 fn parse_toplevel(root: Node<'_>, context: &Context) -> Result<ast::File, String> {
531 let mut toplevel_comments = vec![];
532 let mut file = ast::File::new(context.file);
533
534 let mut comment_start = vec![];
535 for token in root.clone().tokens() {
536 match token {
537 Token::Start { rule: Rule::COMMENT, pos } => comment_start.push(pos),
538 Token::End { rule: Rule::COMMENT, pos } => {
539 let start_pos = comment_start.pop().unwrap();
540 file.comments.push(ast::Comment {
541 loc: ast::SourceRange {
542 file: context.file,
543 start: ast::SourceLocation::new(start_pos.pos(), context.line_starts),
544 end: ast::SourceLocation::new(pos.pos(), context.line_starts),
545 },
546 text: start_pos.span(&pos).as_str().to_owned(),
547 })
548 }
549 _ => (),
550 }
551 }
552
553 for node in root.children() {
554 let loc = node.as_loc(context);
555 let rule = node.as_rule();
556 match rule {
557 Rule::endianness_declaration => file.endianness = parse_endianness(node, context)?,
558 Rule::checksum_declaration => {
559 let mut children = node.children();
560 expect(&mut children, Rule::CHECKSUM)?;
561 let id = parse_identifier(&mut children)?;
562 let width = parse_integer(&mut children)?;
563 let function = parse_string(&mut children)?;
564 file.declarations.push(ast::Decl {
565 loc,
566 key: context.decl_key(),
567 desc: ast::DeclDesc::Checksum { id, function, width },
568 })
569 }
570 Rule::custom_field_declaration => {
571 let mut children = node.children();
572 expect(&mut children, Rule::CUSTOM_FIELD)?;
573 let id = parse_identifier(&mut children)?;
574 let width = parse_integer_opt(&mut children)?;
575 let function = parse_string(&mut children)?;
576 file.declarations.push(ast::Decl {
577 loc,
578 key: context.decl_key(),
579 desc: ast::DeclDesc::CustomField { id, function, width },
580 })
581 }
582 Rule::enum_declaration => {
583 let mut children = node.children();
584 expect(&mut children, Rule::ENUM)?;
585 let id = parse_identifier(&mut children)?;
586 let width = parse_integer(&mut children)?;
587 let tags = parse_enum_tag_list(&mut children, context)?;
588 file.declarations.push(ast::Decl {
589 loc,
590 key: context.decl_key(),
591 desc: ast::DeclDesc::Enum { id, width, tags },
592 })
593 }
594 Rule::packet_declaration => {
595 let mut children = node.children();
596 expect(&mut children, Rule::PACKET)?;
597 let id = parse_identifier(&mut children)?;
598 let parent_id = parse_identifier_opt(&mut children)?;
599 let constraints = parse_constraint_list_opt(&mut children, context)?;
600 let fields = parse_field_list_opt(&mut children, context)?;
601 file.declarations.push(ast::Decl {
602 loc,
603 key: context.decl_key(),
604 desc: ast::DeclDesc::Packet { id, parent_id, constraints, fields },
605 })
606 }
607 Rule::struct_declaration => {
608 let mut children = node.children();
609 expect(&mut children, Rule::STRUCT)?;
610 let id = parse_identifier(&mut children)?;
611 let parent_id = parse_identifier_opt(&mut children)?;
612 let constraints = parse_constraint_list_opt(&mut children, context)?;
613 let fields = parse_field_list_opt(&mut children, context)?;
614 file.declarations.push(ast::Decl {
615 loc,
616 key: context.decl_key(),
617 desc: ast::DeclDesc::Struct { id, parent_id, constraints, fields },
618 })
619 }
620 Rule::group_declaration => {
621 let mut children = node.children();
622 expect(&mut children, Rule::GROUP)?;
623 let id = parse_identifier(&mut children)?;
624 let fields = parse_field_list(&mut children, context)?;
625 file.declarations.push(ast::Decl {
626 loc,
627 key: context.decl_key(),
628 desc: ast::DeclDesc::Group { id, fields },
629 })
630 }
631 Rule::test_declaration => {}
632 Rule::EOI => (),
633 _ => unreachable!(),
634 }
635 }
636 file.comments.append(&mut toplevel_comments);
637 file.max_key = context.key.get();
638 Ok(file)
639 }
640
641 /// Parse PDL source code from a string.
642 ///
643 /// The file is added to the compilation database under the provided
644 /// name.
parse_inline( sources: &mut ast::SourceDatabase, name: &str, source: String, ) -> Result<ast::File, Diagnostic<ast::FileId>>645 pub fn parse_inline(
646 sources: &mut ast::SourceDatabase,
647 name: &str,
648 source: String,
649 ) -> Result<ast::File, Diagnostic<ast::FileId>> {
650 let root = PDLParser::parse(Rule::file, &source)
651 .map_err(|e| {
652 Diagnostic::error()
653 .with_message(format!("failed to parse input file '{}': {}", name, e))
654 })?
655 .next()
656 .unwrap();
657 let line_starts: Vec<_> = files::line_starts(&source).collect();
658 let file = sources.add(name.to_owned(), source.clone());
659 parse_toplevel(root, &Context { file, line_starts: &line_starts, key: std::cell::Cell::new(0) })
660 .map_err(|e| Diagnostic::error().with_message(e))
661 }
662
663 /// Parse a new source file.
664 ///
665 /// The source file is fully read and added to the compilation
666 /// database. Returns the constructed AST, or a descriptive error
667 /// message in case of syntax error.
parse_file( sources: &mut ast::SourceDatabase, name: &str, ) -> Result<ast::File, Diagnostic<ast::FileId>>668 pub fn parse_file(
669 sources: &mut ast::SourceDatabase,
670 name: &str,
671 ) -> Result<ast::File, Diagnostic<ast::FileId>> {
672 let source = std::fs::read_to_string(name).map_err(|e| {
673 Diagnostic::error().with_message(format!("failed to read input file '{}': {}", name, e))
674 })?;
675 parse_inline(sources, name, source)
676 }
677
678 #[cfg(test)]
679 mod test {
680 use super::*;
681
682 #[test]
endianness_is_set()683 fn endianness_is_set() {
684 // The file starts out with a placeholder little-endian value.
685 // This tests that we update it while parsing.
686 let mut db = ast::SourceDatabase::new();
687 let file = parse_inline(&mut db, "stdin", String::from(" big_endian_packets ")).unwrap();
688 assert_eq!(file.endianness.value, ast::EndiannessValue::BigEndian);
689 assert_ne!(file.endianness.loc, ast::SourceRange::default());
690 }
691
692 #[test]
test_parse_string_bare()693 fn test_parse_string_bare() {
694 let mut pairs = PDLParser::parse(Rule::string, r#""test""#).unwrap();
695
696 assert_eq!(parse_string(&mut pairs).as_deref(), Ok("test"));
697 assert_eq!(pairs.next(), None, "pairs is empty");
698 }
699
700 #[test]
test_parse_string_space()701 fn test_parse_string_space() {
702 let mut pairs = PDLParser::parse(Rule::string, r#""test with space""#).unwrap();
703
704 assert_eq!(parse_string(&mut pairs).as_deref(), Ok("test with space"));
705 assert_eq!(pairs.next(), None, "pairs is empty");
706 }
707
708 #[test]
709 #[should_panic] /* This is not supported */
test_parse_string_escape()710 fn test_parse_string_escape() {
711 let mut pairs = PDLParser::parse(Rule::string, r#""\"test\"""#).unwrap();
712
713 assert_eq!(parse_string(&mut pairs).as_deref(), Ok(r#""test""#));
714 assert_eq!(pairs.next(), None, "pairs is empty");
715 }
716
717 #[test]
test_no_whitespace_between_keywords()718 fn test_no_whitespace_between_keywords() {
719 // Validate that the parser rejects inputs where whitespaces
720 // are not applied between alphabetical keywords and identifiers.
721 let mut db = ast::SourceDatabase::new();
722 assert!(parse_inline(
723 &mut db,
724 "test",
725 r#"
726 little_endian_packetsstructx{foo:8}
727 "#
728 .to_owned()
729 )
730 .is_err());
731
732 let result = parse_inline(
733 &mut db,
734 "test",
735 r#"
736 little_endian_packets
737 struct x { foo:8 }
738 "#
739 .to_owned(),
740 );
741 println!("{:?}", result);
742 assert!(result.is_ok());
743 }
744 }
745