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 codespan_reporting::diagnostic;
16 use codespan_reporting::files;
17 use serde::Serialize;
18 use std::fmt;
19 use std::ops;
20 
21 /// File identifier.
22 /// References a source file in the source database.
23 pub type FileId = usize;
24 
25 /// Source database.
26 /// Stores the source file contents for reference.
27 pub type SourceDatabase = files::SimpleFiles<String, String>;
28 
29 #[derive(Debug, Default, Copy, Clone, Serialize, PartialEq, Eq, PartialOrd, Ord)]
30 pub struct SourceLocation {
31     /// Byte offset into the file (counted from zero).
32     pub offset: usize,
33     /// Line number (counted from zero).
34     pub line: usize,
35     /// Column number (counted from zero)
36     pub column: usize,
37 }
38 
39 #[derive(Default, Copy, Clone, PartialEq, Eq, Serialize)]
40 pub struct SourceRange {
41     pub file: FileId,
42     pub start: SourceLocation,
43     pub end: SourceLocation,
44 }
45 
46 #[derive(Debug, Serialize, Clone)]
47 #[serde(tag = "kind", rename = "comment")]
48 pub struct Comment {
49     pub loc: SourceRange,
50     pub text: String,
51 }
52 
53 #[derive(Debug, Copy, Clone, PartialEq, Eq, Serialize)]
54 #[serde(rename_all = "snake_case")]
55 pub enum EndiannessValue {
56     LittleEndian,
57     BigEndian,
58 }
59 
60 #[derive(Debug, Copy, Clone, Serialize)]
61 #[serde(tag = "kind", rename = "endianness_declaration")]
62 pub struct Endianness {
63     pub loc: SourceRange,
64     pub value: EndiannessValue,
65 }
66 
67 #[derive(Debug, Clone, Serialize)]
68 #[serde(tag = "kind", rename = "tag")]
69 pub struct TagValue {
70     pub id: String,
71     pub loc: SourceRange,
72     pub value: usize,
73 }
74 
75 #[derive(Debug, Clone, Serialize)]
76 #[serde(tag = "kind", rename = "tag")]
77 pub struct TagRange {
78     pub id: String,
79     pub loc: SourceRange,
80     pub range: ops::RangeInclusive<usize>,
81     pub tags: Vec<TagValue>,
82 }
83 
84 #[derive(Debug, Clone, Serialize)]
85 #[serde(tag = "kind", rename = "tag")]
86 pub struct TagOther {
87     pub id: String,
88     pub loc: SourceRange,
89 }
90 
91 #[derive(Debug, Serialize, Clone, PartialEq, Eq)]
92 #[serde(untagged)]
93 pub enum Tag {
94     Value(TagValue),
95     Range(TagRange),
96     Other(TagOther),
97 }
98 
99 #[derive(Debug, Serialize, Clone)]
100 #[serde(tag = "kind", rename = "constraint")]
101 pub struct Constraint {
102     pub id: String,
103     pub loc: SourceRange,
104     pub value: Option<usize>,
105     pub tag_id: Option<String>,
106 }
107 
108 #[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
109 pub struct FieldKey(pub usize);
110 
111 #[derive(Debug, Serialize, Clone, PartialEq, Eq)]
112 #[serde(tag = "kind")]
113 pub enum FieldDesc {
114     #[serde(rename = "checksum_field")]
115     Checksum { field_id: String },
116     #[serde(rename = "padding_field")]
117     Padding { size: usize },
118     #[serde(rename = "size_field")]
119     Size { field_id: String, width: usize },
120     #[serde(rename = "count_field")]
121     Count { field_id: String, width: usize },
122     #[serde(rename = "elementsize_field")]
123     ElementSize { field_id: String, width: usize },
124     #[serde(rename = "body_field")]
125     Body,
126     #[serde(rename = "payload_field")]
127     Payload { size_modifier: Option<String> },
128     #[serde(rename = "fixed_field")]
129     FixedScalar { width: usize, value: usize },
130     #[serde(rename = "fixed_field")]
131     FixedEnum { enum_id: String, tag_id: String },
132     #[serde(rename = "reserved_field")]
133     Reserved { width: usize },
134     #[serde(rename = "array_field")]
135     Array {
136         id: String,
137         width: Option<usize>,
138         type_id: Option<String>,
139         size_modifier: Option<String>,
140         size: Option<usize>,
141     },
142     #[serde(rename = "scalar_field")]
143     Scalar { id: String, width: usize },
144     /// Special case of Scalar for fields used as condition for
145     /// optional fields. The width is always 1.
146     #[serde(rename = "flag_field")]
147     Flag { id: String, optional_field_id: String, set_value: usize },
148     #[serde(rename = "typedef_field")]
149     Typedef { id: String, type_id: String },
150     #[serde(rename = "group_field")]
151     Group { group_id: String, constraints: Vec<Constraint> },
152 }
153 
154 #[derive(Debug, Serialize, Clone)]
155 pub struct Field {
156     pub loc: SourceRange,
157     /// Unique identifier used to refer to the AST node in
158     /// compilation environments.
159     #[serde(skip_serializing)]
160     pub key: FieldKey,
161     #[serde(flatten)]
162     pub desc: FieldDesc,
163     pub cond: Option<Constraint>,
164 }
165 
166 #[derive(Debug, Serialize, Clone)]
167 #[serde(tag = "kind", rename = "test_case")]
168 pub struct TestCase {
169     pub loc: SourceRange,
170     pub input: String,
171 }
172 
173 #[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
174 pub struct DeclKey(pub usize);
175 
176 #[derive(Debug, Serialize, Clone, PartialEq, Eq)]
177 #[serde(tag = "kind")]
178 pub enum DeclDesc {
179     #[serde(rename = "checksum_declaration")]
180     Checksum { id: String, function: String, width: usize },
181     #[serde(rename = "custom_field_declaration")]
182     CustomField { id: String, width: Option<usize>, function: String },
183     #[serde(rename = "enum_declaration")]
184     Enum { id: String, tags: Vec<Tag>, width: usize },
185     #[serde(rename = "packet_declaration")]
186     Packet {
187         id: String,
188         constraints: Vec<Constraint>,
189         fields: Vec<Field>,
190         parent_id: Option<String>,
191     },
192     #[serde(rename = "struct_declaration")]
193     Struct {
194         id: String,
195         constraints: Vec<Constraint>,
196         fields: Vec<Field>,
197         parent_id: Option<String>,
198     },
199     #[serde(rename = "group_declaration")]
200     Group { id: String, fields: Vec<Field> },
201     #[serde(rename = "test_declaration")]
202     Test { type_id: String, test_cases: Vec<TestCase> },
203 }
204 
205 #[derive(Debug, Serialize, Clone)]
206 pub struct Decl {
207     pub loc: SourceRange,
208     /// Unique identifier used to refer to the AST node in
209     /// compilation environments.
210     #[serde(skip_serializing)]
211     pub key: DeclKey,
212     #[serde(flatten)]
213     pub desc: DeclDesc,
214 }
215 
216 #[derive(Debug, Serialize, Clone)]
217 pub struct File {
218     pub version: String,
219     pub file: FileId,
220     pub comments: Vec<Comment>,
221     pub endianness: Endianness,
222     pub declarations: Vec<Decl>,
223     #[serde(skip_serializing)]
224     pub max_key: usize,
225 }
226 
227 impl SourceLocation {
228     /// Construct a new source location.
229     ///
230     /// The `line_starts` indicates the byte offsets where new lines
231     /// start in the file. The first element should thus be `0` since
232     /// every file has at least one line starting at offset `0`.
new(offset: usize, line_starts: &[usize]) -> SourceLocation233     pub fn new(offset: usize, line_starts: &[usize]) -> SourceLocation {
234         let mut loc = SourceLocation { offset, line: 0, column: offset };
235         for (line, start) in line_starts.iter().enumerate() {
236             if *start > offset {
237                 break;
238             }
239             loc = SourceLocation { offset, line, column: offset - start };
240         }
241         loc
242     }
243 }
244 
245 impl SourceRange {
primary(&self) -> diagnostic::Label<FileId>246     pub fn primary(&self) -> diagnostic::Label<FileId> {
247         diagnostic::Label::primary(self.file, self.start.offset..self.end.offset)
248     }
secondary(&self) -> diagnostic::Label<FileId>249     pub fn secondary(&self) -> diagnostic::Label<FileId> {
250         diagnostic::Label::secondary(self.file, self.start.offset..self.end.offset)
251     }
252 }
253 
254 impl fmt::Display for SourceRange {
fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result255     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
256         if self.start.line == self.end.line {
257             write!(f, "{}:{}-{}", self.start.line, self.start.column, self.end.column)
258         } else {
259             write!(
260                 f,
261                 "{}:{}-{}:{}",
262                 self.start.line, self.start.column, self.end.line, self.end.column
263             )
264         }
265     }
266 }
267 
268 impl fmt::Debug for SourceRange {
fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result269     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
270         f.debug_struct("SourceRange").finish_non_exhaustive()
271     }
272 }
273 
274 impl ops::Add<SourceRange> for SourceRange {
275     type Output = SourceRange;
276 
add(self, rhs: SourceRange) -> SourceRange277     fn add(self, rhs: SourceRange) -> SourceRange {
278         assert_eq!(self.file, rhs.file);
279         SourceRange {
280             file: self.file,
281             start: self.start.min(rhs.start),
282             end: self.end.max(rhs.end),
283         }
284     }
285 }
286 
287 impl Eq for Endianness {}
288 impl PartialEq for Endianness {
eq(&self, other: &Self) -> bool289     fn eq(&self, other: &Self) -> bool {
290         // Implement structural equality, leave out loc.
291         self.value == other.value
292     }
293 }
294 
295 impl Eq for TagValue {}
296 impl PartialEq for TagValue {
eq(&self, other: &Self) -> bool297     fn eq(&self, other: &Self) -> bool {
298         // Implement structural equality, leave out loc.
299         self.id == other.id && self.value == other.value
300     }
301 }
302 
303 impl Eq for TagRange {}
304 impl PartialEq for TagRange {
eq(&self, other: &Self) -> bool305     fn eq(&self, other: &Self) -> bool {
306         // Implement structural equality, leave out loc.
307         self.id == other.id && self.range == other.range && self.tags == other.tags
308     }
309 }
310 
311 impl Eq for TagOther {}
312 impl PartialEq for TagOther {
eq(&self, other: &Self) -> bool313     fn eq(&self, other: &Self) -> bool {
314         // Implement structual equality, leave out loc.
315         self.id == other.id
316     }
317 }
318 
319 impl Tag {
id(&self) -> &str320     pub fn id(&self) -> &str {
321         match self {
322             Tag::Value(TagValue { id, .. })
323             | Tag::Range(TagRange { id, .. })
324             | Tag::Other(TagOther { id, .. }) => id,
325         }
326     }
327 
loc(&self) -> &SourceRange328     pub fn loc(&self) -> &SourceRange {
329         match self {
330             Tag::Value(TagValue { loc, .. })
331             | Tag::Range(TagRange { loc, .. })
332             | Tag::Other(TagOther { loc, .. }) => loc,
333         }
334     }
335 
value(&self) -> Option<usize>336     pub fn value(&self) -> Option<usize> {
337         match self {
338             Tag::Value(TagValue { value, .. }) => Some(*value),
339             Tag::Range(_) | Tag::Other(_) => None,
340         }
341     }
342 }
343 
344 impl Eq for Constraint {}
345 impl PartialEq for Constraint {
eq(&self, other: &Self) -> bool346     fn eq(&self, other: &Self) -> bool {
347         // Implement structural equality, leave out loc.
348         self.id == other.id && self.value == other.value && self.tag_id == other.tag_id
349     }
350 }
351 
352 impl Eq for TestCase {}
353 impl PartialEq for TestCase {
eq(&self, other: &Self) -> bool354     fn eq(&self, other: &Self) -> bool {
355         // Implement structural equality, leave out loc.
356         self.input == other.input
357     }
358 }
359 
360 impl Eq for File {}
361 impl PartialEq for File {
eq(&self, other: &Self) -> bool362     fn eq(&self, other: &Self) -> bool {
363         // Implement structural equality, leave out comments and PDL
364         // version information.
365         self.endianness == other.endianness && self.declarations == other.declarations
366     }
367 }
368 
369 impl File {
new(file: FileId) -> File370     pub fn new(file: FileId) -> File {
371         File {
372             version: "1,0".to_owned(),
373             comments: vec![],
374             // The endianness is mandatory, so this default value will
375             // be updated while parsing.
376             endianness: Endianness {
377                 loc: SourceRange::default(),
378                 value: EndiannessValue::LittleEndian,
379             },
380             declarations: vec![],
381             file,
382             max_key: 0,
383         }
384     }
385 
386     /// Iterate over the children of the selected declaration.
387     /// /!\ This method is unsafe to use if the file contains cyclic
388     /// declarations, use with caution.
iter_children<'d>(&'d self, decl: &'d Decl) -> impl Iterator<Item = &'d Decl>389     pub fn iter_children<'d>(&'d self, decl: &'d Decl) -> impl Iterator<Item = &'d Decl> {
390         self.declarations.iter().filter(|other_decl| other_decl.parent_id() == decl.id())
391     }
392 }
393 
394 impl Eq for Decl {}
395 impl PartialEq for Decl {
eq(&self, other: &Self) -> bool396     fn eq(&self, other: &Self) -> bool {
397         // Implement structural equality, leave out loc and key.
398         self.desc == other.desc
399     }
400 }
401 
402 impl Decl {
id(&self) -> Option<&str>403     pub fn id(&self) -> Option<&str> {
404         match &self.desc {
405             DeclDesc::Test { .. } => None,
406             DeclDesc::Checksum { id, .. }
407             | DeclDesc::CustomField { id, .. }
408             | DeclDesc::Enum { id, .. }
409             | DeclDesc::Packet { id, .. }
410             | DeclDesc::Struct { id, .. }
411             | DeclDesc::Group { id, .. } => Some(id),
412         }
413     }
414 
parent_id(&self) -> Option<&str>415     pub fn parent_id(&self) -> Option<&str> {
416         match &self.desc {
417             DeclDesc::Packet { parent_id, .. } | DeclDesc::Struct { parent_id, .. } => {
418                 parent_id.as_deref()
419             }
420             _ => None,
421         }
422     }
423 
constraints(&self) -> std::slice::Iter<'_, Constraint>424     pub fn constraints(&self) -> std::slice::Iter<'_, Constraint> {
425         match &self.desc {
426             DeclDesc::Packet { constraints, .. } | DeclDesc::Struct { constraints, .. } => {
427                 constraints.iter()
428             }
429             _ => [].iter(),
430         }
431     }
432 
fields(&self) -> std::slice::Iter<'_, Field>433     pub fn fields(&self) -> std::slice::Iter<'_, Field> {
434         match &self.desc {
435             DeclDesc::Packet { fields, .. }
436             | DeclDesc::Struct { fields, .. }
437             | DeclDesc::Group { fields, .. } => fields.iter(),
438             _ => [].iter(),
439         }
440     }
441 
442     /// Return the reference to the payload or body field in a declaration,
443     /// if present.
payload(&self) -> Option<&Field>444     pub fn payload(&self) -> Option<&Field> {
445         self.fields()
446             .find(|field| matches!(&field.desc, FieldDesc::Payload { .. } | FieldDesc::Body { .. }))
447     }
448 
449     /// Return the reference to the payload or body size field in a declaration,
450     /// if present.
payload_size(&self) -> Option<&Field>451     pub fn payload_size(&self) -> Option<&Field> {
452         self.fields().find(|field| match &field.desc {
453             FieldDesc::Size { field_id, .. } => field_id == "_payload_" || field_id == "_body_",
454             _ => false,
455         })
456     }
457 
458     /// Return the reference to the array size or count field in a declaration,
459     /// if present.
array_size(&self, id: &str) -> Option<&Field>460     pub fn array_size(&self, id: &str) -> Option<&Field> {
461         self.fields().find(|field| match &field.desc {
462             FieldDesc::Size { field_id, .. } | FieldDesc::Count { field_id, .. } => field_id == id,
463             _ => false,
464         })
465     }
466 
kind(&self) -> &str467     pub fn kind(&self) -> &str {
468         match &self.desc {
469             DeclDesc::Checksum { .. } => "checksum",
470             DeclDesc::CustomField { .. } => "custom field",
471             DeclDesc::Enum { .. } => "enum",
472             DeclDesc::Packet { .. } => "packet",
473             DeclDesc::Struct { .. } => "struct",
474             DeclDesc::Group { .. } => "group",
475             DeclDesc::Test { .. } => "test",
476         }
477     }
478 }
479 
480 impl Eq for Field {}
481 impl PartialEq for Field {
eq(&self, other: &Self) -> bool482     fn eq(&self, other: &Self) -> bool {
483         // Implement structural equality, leave out loc and annot.
484         self.desc == other.desc
485     }
486 }
487 
488 impl Field {
id(&self) -> Option<&str>489     pub fn id(&self) -> Option<&str> {
490         match &self.desc {
491             FieldDesc::Checksum { .. }
492             | FieldDesc::Padding { .. }
493             | FieldDesc::Size { .. }
494             | FieldDesc::Count { .. }
495             | FieldDesc::ElementSize { .. }
496             | FieldDesc::Body
497             | FieldDesc::Payload { .. }
498             | FieldDesc::FixedScalar { .. }
499             | FieldDesc::FixedEnum { .. }
500             | FieldDesc::Reserved { .. }
501             | FieldDesc::Group { .. } => None,
502             FieldDesc::Array { id, .. }
503             | FieldDesc::Scalar { id, .. }
504             | FieldDesc::Flag { id, .. }
505             | FieldDesc::Typedef { id, .. } => Some(id),
506         }
507     }
508 
kind(&self) -> &str509     pub fn kind(&self) -> &str {
510         match &self.desc {
511             FieldDesc::Checksum { .. } => "payload",
512             FieldDesc::Padding { .. } => "padding",
513             FieldDesc::Size { .. } => "size",
514             FieldDesc::Count { .. } => "count",
515             FieldDesc::ElementSize { .. } => "elementsize",
516             FieldDesc::Body { .. } => "body",
517             FieldDesc::Payload { .. } => "payload",
518             FieldDesc::FixedScalar { .. } | FieldDesc::FixedEnum { .. } => "fixed",
519             FieldDesc::Reserved { .. } => "reserved",
520             FieldDesc::Group { .. } => "group",
521             FieldDesc::Array { .. } => "array",
522             FieldDesc::Scalar { .. } => "scalar",
523             FieldDesc::Flag { .. } => "scalar",
524             FieldDesc::Typedef { .. } => "typedef",
525         }
526     }
527 }
528 
529 #[cfg(test)]
530 mod tests {
531     use super::*;
532 
533     #[test]
source_location_new()534     fn source_location_new() {
535         let line_starts = &[0, 20, 80, 120, 150];
536         assert_eq!(
537             SourceLocation::new(0, line_starts),
538             SourceLocation { offset: 0, line: 0, column: 0 }
539         );
540         assert_eq!(
541             SourceLocation::new(10, line_starts),
542             SourceLocation { offset: 10, line: 0, column: 10 }
543         );
544         assert_eq!(
545             SourceLocation::new(50, line_starts),
546             SourceLocation { offset: 50, line: 1, column: 30 }
547         );
548         assert_eq!(
549             SourceLocation::new(100, line_starts),
550             SourceLocation { offset: 100, line: 2, column: 20 }
551         );
552         assert_eq!(
553             SourceLocation::new(1000, line_starts),
554             SourceLocation { offset: 1000, line: 4, column: 850 }
555         );
556     }
557 
558     #[test]
source_location_new_no_crash_with_empty_line_starts()559     fn source_location_new_no_crash_with_empty_line_starts() {
560         let loc = SourceLocation::new(100, &[]);
561         assert_eq!(loc, SourceLocation { offset: 100, line: 0, column: 100 });
562     }
563 }
564