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