1use std::fmt;
14
15#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
17pub enum SyntaxCategory {
18 Special,
20 Trivia,
22 Identifier,
24 Literal,
26 Punctuation,
28 Keyword,
30 Node,
32}
33
34#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
36pub enum KeywordCategory {
37 Structural,
39 Clause,
41 Macro,
43 Type,
45 Tag,
47 StatusAccess,
49}
50
51macro_rules! define_syntax_kinds {
52 (
53 special { $( $special:ident => ($special_libsmi:literal, $special_display:literal); )* }
54 trivia { $( $trivia:ident => ($trivia_libsmi:literal, $trivia_display:literal); )* }
55 identifiers { $( $identifier:ident => ($identifier_libsmi:literal, $identifier_display:literal); )* }
56 literals { $( $literal:ident => ($literal_libsmi:literal, $literal_display:literal); )* }
57 punctuation { $( $punctuation:ident => ($byte:literal, $spelling:literal, $punctuation_libsmi:literal, $punctuation_display:literal); )* }
58 operators { $( $operator:ident => ($operator_spelling:literal, $operator_libsmi:literal, $operator_display:literal); )* }
59 keywords { $( $keyword:ident => ($keyword_category:ident, $canonical:literal, [$($alias:literal),* $(,)?], $keyword_libsmi:literal); )* }
60 nodes { $( $node:ident => ($node_libsmi:literal, $node_display:literal); )* }
61 forbidden { $( $forbidden:literal ),* $(,)? }
62 ) => {
63 #[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
68 #[repr(u16)]
69 pub enum SyntaxKind {
70 $( #[doc = concat!("`", $special_display, "`")] $special, )*
71 $( #[doc = $trivia_display] $trivia, )*
72 $( #[doc = $identifier_display] $identifier, )*
73 $( #[doc = $literal_display] $literal, )*
74 $( #[doc = $punctuation_display] $punctuation, )*
75 $( #[doc = $operator_display] $operator, )*
76 $( #[doc = $canonical] $keyword, )*
77 $( #[doc = $node_display] $node, )*
78 }
79
80 impl SyntaxKind {
81 pub const ALL: &'static [Self] = &[
83 $( Self::$special, )*
84 $( Self::$trivia, )*
85 $( Self::$identifier, )*
86 $( Self::$literal, )*
87 $( Self::$punctuation, )*
88 $( Self::$operator, )*
89 $( Self::$keyword, )*
90 $( Self::$node, )*
91 ];
92
93 pub const fn from_raw(raw: u16) -> Option<Self> {
95 match raw {
96 $( value if value == Self::$special as u16 => Some(Self::$special), )*
97 $( value if value == Self::$trivia as u16 => Some(Self::$trivia), )*
98 $( value if value == Self::$identifier as u16 => Some(Self::$identifier), )*
99 $( value if value == Self::$literal as u16 => Some(Self::$literal), )*
100 $( value if value == Self::$punctuation as u16 => Some(Self::$punctuation), )*
101 $( value if value == Self::$operator as u16 => Some(Self::$operator), )*
102 $( value if value == Self::$keyword as u16 => Some(Self::$keyword), )*
103 $( value if value == Self::$node as u16 => Some(Self::$node), )*
104 _ => None,
105 }
106 }
107
108 pub const fn to_raw(self) -> u16 {
110 self as u16
111 }
112
113 pub const fn category(self) -> SyntaxCategory {
115 match self {
116 $( Self::$special => SyntaxCategory::Special, )*
117 $( Self::$trivia => SyntaxCategory::Trivia, )*
118 $( Self::$identifier => SyntaxCategory::Identifier, )*
119 $( Self::$literal => SyntaxCategory::Literal, )*
120 $( Self::$punctuation => SyntaxCategory::Punctuation, )*
121 $( Self::$operator => SyntaxCategory::Punctuation, )*
122 $( Self::$keyword => SyntaxCategory::Keyword, )*
123 $( Self::$node => SyntaxCategory::Node, )*
124 }
125 }
126
127 pub const fn keyword_category(self) -> Option<KeywordCategory> {
129 match self {
130 $( Self::$keyword => Some(KeywordCategory::$keyword_category), )*
131 _ => None,
132 }
133 }
134
135 pub const fn is_token(self) -> bool {
137 !self.is_node()
138 }
139
140 pub const fn is_node(self) -> bool {
142 matches!(self.category(), SyntaxCategory::Node)
143 }
144
145 pub const fn is_trivia(self) -> bool {
147 matches!(self.category(), SyntaxCategory::Trivia)
148 }
149
150 pub const fn is_identifier(self) -> bool {
152 matches!(self.category(), SyntaxCategory::Identifier)
153 }
154
155 pub const fn is_literal(self) -> bool {
157 matches!(self.category(), SyntaxCategory::Literal)
158 }
159
160 pub const fn is_punctuation(self) -> bool {
162 matches!(self.category(), SyntaxCategory::Punctuation)
163 }
164
165 pub const fn is_keyword(self) -> bool {
167 matches!(self.category(), SyntaxCategory::Keyword)
168 }
169
170 pub const fn is_structural_keyword(self) -> bool {
172 matches!(self.keyword_category(), Some(KeywordCategory::Structural))
173 }
174
175 pub const fn is_clause_keyword(self) -> bool {
177 matches!(self.keyword_category(), Some(KeywordCategory::Clause))
178 }
179
180 pub const fn is_macro_keyword(self) -> bool {
182 matches!(self.keyword_category(), Some(KeywordCategory::Macro))
183 }
184
185 pub const fn is_type_keyword(self) -> bool {
187 matches!(self.keyword_category(), Some(KeywordCategory::Type))
188 }
189
190 pub const fn is_tag_keyword(self) -> bool {
192 matches!(self.keyword_category(), Some(KeywordCategory::Tag))
193 }
194
195 pub const fn is_status_access_keyword(self) -> bool {
197 matches!(self.keyword_category(), Some(KeywordCategory::StatusAccess))
198 }
199
200 pub const fn fixed_text(self) -> Option<&'static str> {
202 match self {
203 $( Self::$punctuation => Some($spelling), )*
204 $( Self::$operator => Some($operator_spelling), )*
205 $( Self::$keyword => Some($canonical), )*
206 _ => None,
207 }
208 }
209
210 pub const fn keyword_spellings(self) -> &'static [&'static str] {
212 match self {
213 $( Self::$keyword => &[$canonical, $($alias),*], )*
214 _ => &[],
215 }
216 }
217
218 pub fn from_keyword(text: &str) -> Option<Self> {
220 match text {
221 $( $canonical $(| $alias)* => Some(Self::$keyword), )*
222 _ => None,
223 }
224 }
225
226 pub fn from_fixed_text(text: &str) -> Option<Self> {
228 match text {
229 $( $spelling => Some(Self::$punctuation), )*
230 $( $operator_spelling => Some(Self::$operator), )*
231 $( $canonical => Some(Self::$keyword), )*
232 _ => None,
233 }
234 }
235
236 pub const fn from_punctuation_byte(byte: u8) -> Option<Self> {
238 match byte {
239 $( $byte => Some(Self::$punctuation), )*
240 _ => None,
241 }
242 }
243
244 pub const fn display_name(self) -> &'static str {
246 match self {
247 $( Self::$special => $special_display, )*
248 $( Self::$trivia => $trivia_display, )*
249 $( Self::$identifier => $identifier_display, )*
250 $( Self::$literal => $literal_display, )*
251 $( Self::$punctuation => $punctuation_display, )*
252 $( Self::$operator => $operator_display, )*
253 $( Self::$keyword => $keyword_libsmi, )*
254 $( Self::$node => $node_display, )*
255 }
256 }
257
258 pub const fn libsmi_name(self) -> &'static str {
260 match self {
261 $( Self::$special => $special_libsmi, )*
262 $( Self::$trivia => $trivia_libsmi, )*
263 $( Self::$identifier => $identifier_libsmi, )*
264 $( Self::$literal => $literal_libsmi, )*
265 $( Self::$punctuation => $punctuation_libsmi, )*
266 $( Self::$operator => $operator_libsmi, )*
267 $( Self::$keyword => $keyword_libsmi, )*
268 $( Self::$node => $node_libsmi, )*
269 }
270 }
271 }
272
273 pub const FORBIDDEN_KEYWORDS: &[&str] = &[$($forbidden),*];
275
276 pub fn lookup_keyword(text: &str) -> Option<SyntaxKind> {
278 SyntaxKind::from_keyword(text)
279 }
280
281 pub fn is_forbidden_keyword(text: &str) -> bool {
283 matches!(text, $($forbidden)|*)
284 }
285 };
286}
287
288define_syntax_kinds! {
289 special {
290 ErrorToken => ("ERROR", "<error>");
291 EofToken => ("EOF", "end of file");
292 ForbiddenKeyword => ("FORBIDDEN_KEYWORD", "reserved keyword");
293 OpaqueText => ("OPAQUE_TEXT", "opaque text");
294 }
295 trivia {
296 Whitespace => ("WHITESPACE", "whitespace");
297 Comment => ("COMMENT", "comment");
298 }
299 identifiers {
300 UppercaseIdent => ("UPPERCASE_IDENTIFIER", "identifier");
301 LowercaseIdent => ("LOWERCASE_IDENTIFIER", "identifier");
302 }
303 literals {
304 Number => ("NUMBER", "number");
305 NegativeNumber => ("NEGATIVENUMBER", "negative number");
306 QuotedString => ("QUOTED_STRING", "quoted string");
307 HexString => ("HEX_STRING", "hex string");
308 BinString => ("BIN_STRING", "binary string");
309 }
310 punctuation {
311 LBracket => (b'[', "[", "LBRACKET", "'['");
312 RBracket => (b']', "]", "RBRACKET", "']'");
313 LBrace => (b'{', "{", "LBRACE", "'{'");
314 RBrace => (b'}', "}", "RBRACE", "'}'");
315 LParen => (b'(', "(", "LPAREN", "'('");
316 RParen => (b')', ")", "RPAREN", "')'");
317 Colon => (b':', ":", "COLON", "':'");
318 Semicolon => (b';', ";", "SEMICOLON", "';'");
319 Comma => (b',', ",", "COMMA", "','");
320 Dot => (b'.', ".", "DOT", "'.'");
321 Pipe => (b'|', "|", "PIPE", "'|'");
322 Minus => (b'-', "-", "MINUS", "'-'");
323 }
324 operators {
325 DotDot => ("..", "DOT_DOT", "'..'");
326 ColonColonEqual => ("::=", "COLON_COLON_EQUAL", "'::='");
327 }
328 keywords {
329 KwDefinitions => (Structural, "DEFINITIONS", [], "DEFINITIONS");
330 KwBegin => (Structural, "BEGIN", [], "BEGIN");
331 KwEnd => (Structural, "END", [], "END");
332 KwImports => (Structural, "IMPORTS", [], "IMPORTS");
333 KwExports => (Structural, "EXPORTS", [], "EXPORTS");
334 KwFrom => (Structural, "FROM", [], "FROM");
335 KwObject => (Structural, "OBJECT", [], "OBJECT");
336 KwIdentifier => (Structural, "IDENTIFIER", [], "IDENTIFIER");
337 KwSequence => (Structural, "SEQUENCE", [], "SEQUENCE");
338 KwOf => (Structural, "OF", [], "OF");
339 KwChoice => (Structural, "CHOICE", [], "CHOICE");
340 KwMacro => (Structural, "MACRO", [], "MACRO");
341
342 KwSyntax => (Clause, "SYNTAX", [], "SYNTAX");
343 KwMaxAccess => (Clause, "MAX-ACCESS", [], "MAX_ACCESS");
344 KwMinAccess => (Clause, "MIN-ACCESS", [], "MIN_ACCESS");
345 KwAccess => (Clause, "ACCESS", [], "ACCESS");
346 KwStatus => (Clause, "STATUS", [], "STATUS");
347 KwDescription => (Clause, "DESCRIPTION", [], "DESCRIPTION");
348 KwReference => (Clause, "REFERENCE", [], "REFERENCE");
349 KwIndex => (Clause, "INDEX", [], "INDEX");
350 KwDefval => (Clause, "DEFVAL", [], "DEFVAL");
351 KwAugments => (Clause, "AUGMENTS", [], "AUGMENTS");
352 KwUnits => (Clause, "UNITS", [], "UNITS");
353 KwDisplayHint => (Clause, "DISPLAY-HINT", [], "DISPLAY_HINT");
354 KwObjects => (Clause, "OBJECTS", [], "OBJECTS");
355 KwNotifications => (Clause, "NOTIFICATIONS", [], "NOTIFICATIONS");
356 KwModule => (Clause, "MODULE", [], "MODULE");
357 KwMandatoryGroups => (Clause, "MANDATORY-GROUPS", [], "MANDATORY_GROUPS");
358 KwGroup => (Clause, "GROUP", [], "GROUP");
359 KwWriteSyntax => (Clause, "WRITE-SYNTAX", [], "WRITE_SYNTAX");
360 KwProductRelease => (Clause, "PRODUCT-RELEASE", [], "PRODUCT_RELEASE");
361 KwSupports => (Clause, "SUPPORTS", [], "SUPPORTS");
362 KwIncludes => (Clause, "INCLUDES", [], "INCLUDES");
363 KwVariation => (Clause, "VARIATION", [], "VARIATION");
364 KwCreationRequires => (Clause, "CREATION-REQUIRES", [], "CREATION_REQUIRES");
365 KwRevision => (Clause, "REVISION", [], "REVISION");
366 KwLastUpdated => (Clause, "LAST-UPDATED", [], "LAST_UPDATED");
367 KwOrganization => (Clause, "ORGANIZATION", [], "ORGANIZATION");
368 KwContactInfo => (Clause, "CONTACT-INFO", [], "CONTACT_INFO");
369 KwImplied => (Clause, "IMPLIED", [], "IMPLIED");
370 KwSize => (Clause, "SIZE", [], "SIZE");
371 KwEnterprise => (Clause, "ENTERPRISE", [], "ENTERPRISE");
372 KwVariables => (Clause, "VARIABLES", [], "VARIABLES");
373
374 KwModuleIdentity => (Macro, "MODULE-IDENTITY", [], "MODULE_IDENTITY");
375 KwModuleCompliance => (Macro, "MODULE-COMPLIANCE", [], "MODULE_COMPLIANCE");
376 KwObjectGroup => (Macro, "OBJECT-GROUP", [], "OBJECT_GROUP");
377 KwNotificationGroup => (Macro, "NOTIFICATION-GROUP", [], "NOTIFICATION_GROUP");
378 KwAgentCapabilities => (Macro, "AGENT-CAPABILITIES", [], "AGENT_CAPABILITIES");
379 KwObjectType => (Macro, "OBJECT-TYPE", [], "OBJECT_TYPE");
380 KwObjectIdentity => (Macro, "OBJECT-IDENTITY", [], "OBJECT_IDENTITY");
381 KwNotificationType => (Macro, "NOTIFICATION-TYPE", [], "NOTIFICATION_TYPE");
382 KwTextualConvention => (Macro, "TEXTUAL-CONVENTION", [], "TEXTUAL_CONVENTION");
383 KwTrapType => (Macro, "TRAP-TYPE", [], "TRAP_TYPE");
384
385 KwInteger => (Type, "INTEGER", ["Integer"], "INTEGER");
386 KwUnsigned32 => (Type, "Unsigned32", [], "UNSIGNED32");
387 KwCounter32 => (Type, "Counter32", [], "COUNTER32");
388 KwCounter64 => (Type, "Counter64", [], "COUNTER64");
389 KwGauge32 => (Type, "Gauge32", [], "GAUGE32");
390 KwIpAddress => (Type, "IpAddress", [], "IPADDRESS");
391 KwOpaque => (Type, "Opaque", [], "OPAQUE");
392 KwTimeTicks => (Type, "TimeTicks", [], "TIMETICKS");
393 KwBits => (Type, "BITS", [], "BITS");
394 KwOctet => (Type, "OCTET", [], "OCTET");
395 KwString => (Type, "STRING", [], "STRING");
396 KwCounter => (Type, "Counter", [], "COUNTER");
397 KwGauge => (Type, "Gauge", [], "GAUGE");
398 KwNetworkAddress => (Type, "NetworkAddress", [], "NETWORKADDRESS");
399
400 KwApplication => (Tag, "APPLICATION", [], "APPLICATION");
401 KwImplicit => (Tag, "IMPLICIT", [], "IMPLICIT");
402 KwUniversal => (Tag, "UNIVERSAL", [], "UNIVERSAL");
403
404 KwCurrent => (StatusAccess, "current", [], "CURRENT");
405 KwDeprecated => (StatusAccess, "deprecated", [], "DEPRECATED");
406 KwObsolete => (StatusAccess, "obsolete", [], "OBSOLETE");
407 KwMandatory => (StatusAccess, "mandatory", [], "MANDATORY");
408 KwOptional => (StatusAccess, "optional", [], "OPTIONAL");
409 KwReadOnly => (StatusAccess, "read-only", [], "READ_ONLY");
410 KwReadWrite => (StatusAccess, "read-write", [], "READ_WRITE");
411 KwReadCreate => (StatusAccess, "read-create", [], "READ_CREATE");
412 KwWriteOnly => (StatusAccess, "write-only", [], "WRITE_ONLY");
413 KwNotAccessible => (StatusAccess, "not-accessible", [], "NOT_ACCESSIBLE");
414 KwAccessibleForNotify => (StatusAccess, "accessible-for-notify", [], "ACCESSIBLE_FOR_NOTIFY");
415 KwNotImplemented => (StatusAccess, "not-implemented", [], "NOT_IMPLEMENTED");
416 }
417 nodes {
418 SourceFile => ("SOURCE_FILE", "source file");
419 Module => ("MODULE", "module");
420 ModuleHeader => ("MODULE_HEADER", "module header");
421 Imports => ("IMPORTS_NODE", "imports");
422 ImportGroup => ("IMPORT_GROUP", "import group");
423 UnparsedRegion => ("UNPARSED_REGION", "unparsed region");
424
425 ValueAssignment => ("VALUE_ASSIGNMENT", "value assignment");
426 TypeAssignment => ("TYPE_ASSIGNMENT", "type assignment");
427 TextualConventionDefinition => ("TEXTUAL_CONVENTION_DEFINITION", "TEXTUAL-CONVENTION definition");
428 ObjectTypeDefinition => ("OBJECT_TYPE_DEFINITION", "OBJECT-TYPE definition");
429 ModuleIdentityDefinition => ("MODULE_IDENTITY_DEFINITION", "MODULE-IDENTITY definition");
430 ObjectIdentityDefinition => ("OBJECT_IDENTITY_DEFINITION", "OBJECT-IDENTITY definition");
431 NotificationTypeDefinition => ("NOTIFICATION_TYPE_DEFINITION", "NOTIFICATION-TYPE definition");
432 TrapTypeDefinition => ("TRAP_TYPE_DEFINITION", "TRAP-TYPE definition");
433 MacroDefinition => ("MACRO_DEFINITION", "MACRO definition");
434 ObjectGroupDefinition => ("OBJECT_GROUP_DEFINITION", "OBJECT-GROUP definition");
435 NotificationGroupDefinition => ("NOTIFICATION_GROUP_DEFINITION", "NOTIFICATION-GROUP definition");
436 ModuleComplianceDefinition => ("MODULE_COMPLIANCE_DEFINITION", "MODULE-COMPLIANCE definition");
437 AgentCapabilitiesDefinition => ("AGENT_CAPABILITIES_DEFINITION", "AGENT-CAPABILITIES definition");
438
439 ComplianceModule => ("COMPLIANCE_MODULE", "MODULE compliance section");
440 MandatoryGroupsClause => ("MANDATORY_GROUPS_CLAUSE", "MANDATORY-GROUPS clause");
441 ComplianceGroup => ("COMPLIANCE_GROUP", "GROUP compliance refinement");
442 ComplianceObject => ("COMPLIANCE_OBJECT", "OBJECT compliance refinement");
443 WriteSyntaxClause => ("WRITE_SYNTAX_CLAUSE", "WRITE-SYNTAX clause");
444 SupportsModule => ("SUPPORTS_MODULE", "SUPPORTS capability section");
445 IncludesClause => ("INCLUDES_CLAUSE", "INCLUDES clause");
446 VariationClause => ("VARIATION_CLAUSE", "VARIATION clause");
447 CreationRequiresClause => ("CREATION_REQUIRES_CLAUSE", "CREATION-REQUIRES clause");
448
449 SyntaxClause => ("SYNTAX_CLAUSE", "SYNTAX clause");
450 AccessClause => ("ACCESS_CLAUSE", "access clause");
451 StatusClause => ("STATUS_CLAUSE", "STATUS clause");
452 DescriptionClause => ("DESCRIPTION_CLAUSE", "DESCRIPTION clause");
453 ReferenceClause => ("REFERENCE_CLAUSE", "REFERENCE clause");
454 UnitsClause => ("UNITS_CLAUSE", "UNITS clause");
455 DisplayHintClause => ("DISPLAY_HINT_CLAUSE", "DISPLAY-HINT clause");
456 IndexClause => ("INDEX_CLAUSE", "INDEX clause");
457 IndexItem => ("INDEX_ITEM", "index item");
458 AugmentsClause => ("AUGMENTS_CLAUSE", "AUGMENTS clause");
459 DefvalClause => ("DEFVAL_CLAUSE", "DEFVAL clause");
460 DefvalContent => ("DEFVAL_CONTENT", "DEFVAL content");
461 ObjectsClause => ("OBJECTS_CLAUSE", "OBJECTS clause");
462 NotificationsClause => ("NOTIFICATIONS_CLAUSE", "NOTIFICATIONS clause");
463 RevisionClause => ("REVISION_CLAUSE", "REVISION clause");
464 LastUpdatedClause => ("LAST_UPDATED_CLAUSE", "LAST-UPDATED clause");
465 OrganizationClause => ("ORGANIZATION_CLAUSE", "ORGANIZATION clause");
466 ContactInfoClause => ("CONTACT_INFO_CLAUSE", "CONTACT-INFO clause");
467 EnterpriseClause => ("ENTERPRISE_CLAUSE", "ENTERPRISE clause");
468 VariablesClause => ("VARIABLES_CLAUSE", "VARIABLES clause");
469 ProductReleaseClause => ("PRODUCT_RELEASE_CLAUSE", "PRODUCT-RELEASE clause");
470
471 OidAssignment => ("OID_ASSIGNMENT", "OID assignment");
472 OidComponent => ("OID_COMPONENT", "OID component");
473
474 TypeRefSyntax => ("TYPE_REF_SYNTAX", "type reference syntax");
475 IntegerEnumSyntax => ("INTEGER_ENUM_SYNTAX", "integer enumeration syntax");
476 BitsSyntax => ("BITS_SYNTAX", "BITS syntax");
477 ConstrainedSyntax => ("CONSTRAINED_SYNTAX", "constrained type syntax");
478 Constraint => ("CONSTRAINT", "constraint");
479 Range => ("RANGE", "constraint range");
480 NamedNumber => ("NAMED_NUMBER", "named number");
481 SequenceOfSyntax => ("SEQUENCE_OF_SYNTAX", "SEQUENCE OF syntax");
482 SequenceSyntax => ("SEQUENCE_SYNTAX", "SEQUENCE syntax");
483 SequenceField => ("SEQUENCE_FIELD", "SEQUENCE or CHOICE field");
484 ChoiceSyntax => ("CHOICE_SYNTAX", "CHOICE syntax");
485 TaggedSyntax => ("TAGGED_SYNTAX", "tagged syntax");
486 OctetStringSyntax => ("OCTET_STRING_SYNTAX", "OCTET STRING syntax");
487 ObjectIdentifierSyntax => ("OBJECT_IDENTIFIER_SYNTAX", "OBJECT IDENTIFIER syntax");
488 Error => ("ERROR_NODE", "error node");
489 }
490 forbidden {
491 "ABSENT", "ANY", "BIT", "BOOLEAN", "BY", "COMPONENT", "COMPONENTS",
492 "DEFAULT", "DEFINED", "ENUMERATED", "EXPLICIT", "EXTERNAL", "FALSE",
493 "MAX", "MIN", "MINUS-INFINITY", "NULL", "OPTIONAL", "PLUS-INFINITY",
494 "PRESENT", "PRIVATE", "REAL", "SET", "TAGS", "TRUE", "WITH",
495 }
496}
497
498impl fmt::Display for SyntaxKind {
499 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
500 f.write_str(self.libsmi_name())
501 }
502}
503
504#[cfg(test)]
505mod tests {
506 use std::collections::{HashMap, HashSet};
507
508 use super::*;
509
510 #[test]
511 fn raw_discriminants_round_trip_exhaustively() {
512 for (raw, kind) in SyntaxKind::ALL.iter().copied().enumerate() {
513 assert_eq!(usize::from(kind.to_raw()), raw);
514 assert_eq!(SyntaxKind::from_raw(kind.to_raw()), Some(kind));
515 }
516 assert_eq!(
517 SyntaxKind::from_raw(u16::try_from(SyntaxKind::ALL.len()).unwrap()),
518 None
519 );
520 assert_eq!(SyntaxKind::from_raw(u16::MAX), None);
521 }
522
523 #[test]
524 fn inventory_and_categories_are_exhaustive() {
525 assert_eq!(SyntaxKind::ALL.len(), 175);
526 assert_eq!(
527 SyntaxKind::ALL.iter().filter(|kind| kind.is_node()).count(),
528 66
529 );
530 assert_eq!(
531 SyntaxKind::ALL
532 .iter()
533 .filter(|kind| kind.is_token())
534 .count(),
535 109
536 );
537 assert_eq!(
538 SyntaxKind::ALL
539 .iter()
540 .filter(|kind| kind.is_trivia())
541 .count(),
542 2
543 );
544 assert_eq!(
545 SyntaxKind::ALL
546 .iter()
547 .filter(|kind| kind.is_identifier())
548 .count(),
549 2
550 );
551 assert_eq!(
552 SyntaxKind::ALL
553 .iter()
554 .filter(|kind| kind.is_literal())
555 .count(),
556 5
557 );
558 assert_eq!(
559 SyntaxKind::ALL
560 .iter()
561 .filter(|kind| kind.is_punctuation())
562 .count(),
563 14
564 );
565 assert_eq!(
566 SyntaxKind::ALL
567 .iter()
568 .filter(|kind| kind.is_keyword())
569 .count(),
570 82
571 );
572 assert_eq!(SyntaxKind::SourceFile.category(), SyntaxCategory::Node);
573 assert_eq!(SyntaxKind::Error.category(), SyntaxCategory::Node);
574 assert!(SyntaxKind::Whitespace.is_trivia());
575 assert!(SyntaxKind::Comment.is_trivia());
576 assert!(!SyntaxKind::OpaqueText.is_trivia());
577 }
578
579 #[test]
580 fn keyword_and_fixed_spelling_round_trips_are_exhaustive() {
581 for kind in SyntaxKind::ALL.iter().copied() {
582 for spelling in kind.keyword_spellings() {
583 assert_eq!(SyntaxKind::from_keyword(spelling), Some(kind));
584 }
585 if let Some(text) = kind.fixed_text() {
586 assert_eq!(SyntaxKind::from_fixed_text(text), Some(kind));
587 }
588 }
589 assert_eq!(SyntaxKind::from_keyword("integer"), None);
590 assert_eq!(SyntaxKind::from_fixed_text("Integer"), None);
591 }
592
593 #[test]
594 fn single_byte_punctuation_round_trips_exhaustively() {
595 for kind in SyntaxKind::ALL
596 .iter()
597 .copied()
598 .filter(|kind| kind.is_punctuation())
599 {
600 let spelling = kind.fixed_text().unwrap();
601 if let [byte] = spelling.as_bytes() {
602 assert_eq!(SyntaxKind::from_punctuation_byte(*byte), Some(kind));
603 } else {
604 assert!(matches!(
605 kind,
606 SyntaxKind::DotDot | SyntaxKind::ColonColonEqual
607 ));
608 }
609 }
610 }
611
612 #[test]
613 fn declared_spellings_are_unique() {
614 let mut keywords = HashMap::new();
615 let mut fixed = HashMap::new();
616 for kind in SyntaxKind::ALL.iter().copied() {
617 for spelling in kind.keyword_spellings() {
618 assert_eq!(
619 keywords.insert(*spelling, kind),
620 None,
621 "duplicate {spelling}"
622 );
623 }
624 if let Some(spelling) = kind.fixed_text() {
625 assert_eq!(fixed.insert(spelling, kind), None, "duplicate {spelling}");
626 }
627 }
628 let forbidden: HashSet<_> = FORBIDDEN_KEYWORDS.iter().copied().collect();
629 assert_eq!(forbidden.len(), FORBIDDEN_KEYWORDS.len());
630 assert!(forbidden.is_disjoint(&keywords.keys().copied().collect()));
631 }
632
633 #[test]
634 fn forbidden_keyword_lookup_matches_the_declared_inventory() {
635 for keyword in FORBIDDEN_KEYWORDS {
636 assert!(is_forbidden_keyword(keyword));
637 assert_eq!(SyntaxKind::from_keyword(keyword), None);
638 }
639 assert!(!is_forbidden_keyword("optional"));
640 assert_eq!(
641 SyntaxKind::from_keyword("optional"),
642 Some(SyntaxKind::KwOptional)
643 );
644 }
645
646 #[test]
647 fn keyword_subcategories_cover_exactly_all_keywords() {
648 let counts = SyntaxKind::ALL.iter().copied().fold(
649 HashMap::<KeywordCategory, usize>::new(),
650 |mut counts, kind| {
651 if let Some(category) = kind.keyword_category() {
652 *counts.entry(category).or_default() += 1;
653 }
654 counts
655 },
656 );
657 assert_eq!(counts.values().sum::<usize>(), 82);
658 assert_eq!(counts[&KeywordCategory::Structural], 12);
659 assert_eq!(counts[&KeywordCategory::Clause], 31);
660 assert_eq!(counts[&KeywordCategory::Macro], 10);
661 assert_eq!(counts[&KeywordCategory::Type], 14);
662 assert_eq!(counts[&KeywordCategory::Tag], 3);
663 assert_eq!(counts[&KeywordCategory::StatusAccess], 12);
664 }
665
666 #[test]
667 fn legacy_display_and_libsmi_names_are_preserved() {
668 assert_eq!(SyntaxKind::EofToken.display_name(), "end of file");
669 assert_eq!(SyntaxKind::LBrace.display_name(), "'{'");
670 assert_eq!(SyntaxKind::KwObjectType.display_name(), "OBJECT_TYPE");
671 assert_eq!(SyntaxKind::KwObjectType.libsmi_name(), "OBJECT_TYPE");
672 assert_eq!(SyntaxKind::NegativeNumber.libsmi_name(), "NEGATIVENUMBER");
673 assert_eq!(SyntaxKind::ColonColonEqual.fixed_text(), Some("::="));
674 }
675}