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