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