Skip to main content

mib_rs/lexer/
token.rs

1//! Token types produced by the SMI/MIB [`Lexer`](super::Lexer).
2//!
3//! Contains the [`Token`] struct (pairing a [`TokenKind`] with a [`Span`])
4//! and the [`TokenKind`] enum covering all SMIv1/SMIv2 token categories:
5//! identifiers, literals, punctuation, and keywords.
6
7use crate::types::Span;
8
9/// A single lexed token with its classification and source location.
10///
11/// Use [`TokenKind`] to determine what the token represents, and
12/// [`Span`] to index back into the original source bytes.
13#[derive(Debug, Clone, Copy, PartialEq, Eq)]
14pub struct Token {
15    /// What kind of token this is (keyword, identifier, literal, etc.).
16    pub kind: TokenKind,
17    /// Byte range in the source text that produced this token.
18    pub span: Span,
19}
20
21/// Classification of a [`Token`].
22///
23/// Variants are grouped into special tokens, identifiers, literals,
24/// punctuation, and several keyword categories (structural, clause,
25/// macro, type, tag, status/access).
26#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
27#[repr(u8)]
28pub enum TokenKind {
29    // -- Special --
30    /// Unrecognized or malformed input.
31    Error,
32    /// End of input. Always the last token in a stream.
33    Eof,
34    /// A reserved ASN.1 keyword (e.g. `TRUE`, `FALSE`, `NULL`) that must
35    /// not appear as an identifier in MIB files.
36    ForbiddenKeyword,
37    /// An `--`-delimited comment.
38    Comment,
39
40    // -- Identifiers --
41    /// An identifier starting with an uppercase letter (type or module name).
42    UppercaseIdent,
43    /// An identifier starting with a lowercase letter (value reference).
44    LowercaseIdent,
45
46    // -- Literals --
47    /// A non-negative decimal integer, e.g. `42`.
48    Number,
49    /// A negative decimal integer, e.g. `-1`.
50    NegativeNumber,
51    /// A double-quoted string literal, e.g. `"hello"`.
52    QuotedString,
53    /// A hex string literal, e.g. `'0A1B'H`.
54    HexString,
55    /// A binary string literal, e.g. `'01010101'B`.
56    BinString,
57
58    // -- Single-character punctuation --
59    /// `[`
60    LBracket,
61    /// `]`
62    RBracket,
63    /// `{`
64    LBrace,
65    /// `}`
66    RBrace,
67    /// `(`
68    LParen,
69    /// `)`
70    RParen,
71    /// `:`
72    Colon,
73    /// `;`
74    Semicolon,
75    /// `,`
76    Comma,
77    /// `.`
78    Dot,
79    /// `|`
80    Pipe,
81    /// `-` (standalone, not part of a negative number)
82    Minus,
83
84    // -- Multi-character operators --
85    /// `..` (range separator in SIZE/value constraints)
86    DotDot,
87    /// `::=` (assignment operator)
88    ColonColonEqual,
89
90    // -- Structural keywords (first keyword range) --
91    /// `DEFINITIONS`
92    KwDefinitions,
93    /// `BEGIN`
94    KwBegin,
95    /// `END`
96    KwEnd,
97    /// `IMPORTS`
98    KwImports,
99    /// `EXPORTS` - triggers body-skipping in the lexer.
100    KwExports,
101    /// `FROM`
102    KwFrom,
103    /// `OBJECT`
104    KwObject,
105    /// `IDENTIFIER`
106    KwIdentifier,
107    /// `SEQUENCE`
108    KwSequence,
109    /// `OF`
110    KwOf,
111    /// `CHOICE`
112    KwChoice,
113    /// `MACRO` - triggers body-skipping in the lexer.
114    KwMacro,
115
116    // -- Clause keywords --
117    /// `SYNTAX`
118    KwSyntax,
119    /// `MAX-ACCESS`
120    KwMaxAccess,
121    /// `MIN-ACCESS`
122    KwMinAccess,
123    /// `ACCESS` (SMIv1)
124    KwAccess,
125    /// `STATUS`
126    KwStatus,
127    /// `DESCRIPTION`
128    KwDescription,
129    /// `REFERENCE`
130    KwReference,
131    /// `INDEX`
132    KwIndex,
133    /// `DEFVAL`
134    KwDefval,
135    /// `AUGMENTS`
136    KwAugments,
137    /// `UNITS`
138    KwUnits,
139    /// `DISPLAY-HINT`
140    KwDisplayHint,
141    /// `OBJECTS`
142    KwObjects,
143    /// `NOTIFICATIONS`
144    KwNotifications,
145    /// `MODULE`
146    KwModule,
147    /// `MANDATORY-GROUPS`
148    KwMandatoryGroups,
149    /// `GROUP`
150    KwGroup,
151    /// `WRITE-SYNTAX`
152    KwWriteSyntax,
153    /// `PRODUCT-RELEASE`
154    KwProductRelease,
155    /// `SUPPORTS`
156    KwSupports,
157    /// `INCLUDES`
158    KwIncludes,
159    /// `VARIATION`
160    KwVariation,
161    /// `CREATION-REQUIRES`
162    KwCreationRequires,
163    /// `REVISION`
164    KwRevision,
165    /// `LAST-UPDATED`
166    KwLastUpdated,
167    /// `ORGANIZATION`
168    KwOrganization,
169    /// `CONTACT-INFO`
170    KwContactInfo,
171    /// `IMPLIED`
172    KwImplied,
173    /// `SIZE`
174    KwSize,
175    /// `ENTERPRISE` (SMIv1 TRAP-TYPE)
176    KwEnterprise,
177    /// `VARIABLES` (SMIv1 TRAP-TYPE)
178    KwVariables,
179
180    // -- MACRO invocation keywords --
181    /// `MODULE-IDENTITY` (SMIv2)
182    KwModuleIdentity,
183    /// `MODULE-COMPLIANCE` (SMIv2)
184    KwModuleCompliance,
185    /// `OBJECT-GROUP` (SMIv2)
186    KwObjectGroup,
187    /// `NOTIFICATION-GROUP` (SMIv2)
188    KwNotificationGroup,
189    /// `AGENT-CAPABILITIES` (SMIv2)
190    KwAgentCapabilities,
191    /// `OBJECT-TYPE` (SMIv1/v2)
192    KwObjectType,
193    /// `OBJECT-IDENTITY` (SMIv2)
194    KwObjectIdentity,
195    /// `NOTIFICATION-TYPE` (SMIv2)
196    KwNotificationType,
197    /// `TEXTUAL-CONVENTION` (SMIv2)
198    KwTextualConvention,
199    /// `TRAP-TYPE` (SMIv1)
200    KwTrapType,
201
202    // -- Type keywords --
203    /// `INTEGER` or `Integer`
204    KwInteger,
205    /// `Unsigned32`
206    KwUnsigned32,
207    /// `Counter32`
208    KwCounter32,
209    /// `Counter64`
210    KwCounter64,
211    /// `Gauge32`
212    KwGauge32,
213    /// `IpAddress`
214    KwIpAddress,
215    /// `Opaque`
216    KwOpaque,
217    /// `TimeTicks`
218    KwTimeTicks,
219    /// `BITS`
220    KwBits,
221    /// `OCTET` (first half of `OCTET STRING`)
222    KwOctet,
223    /// `STRING` (second half of `OCTET STRING`)
224    KwString,
225
226    // -- SMIv1 type aliases --
227    /// `Counter` (SMIv1 alias for Counter32)
228    KwCounter,
229    /// `Gauge` (SMIv1 alias for Gauge32)
230    KwGauge,
231    /// `NetworkAddress` (SMIv1)
232    KwNetworkAddress,
233
234    // -- ASN.1 tag keywords --
235    /// `APPLICATION`
236    KwApplication,
237    /// `IMPLICIT`
238    KwImplicit,
239    /// `UNIVERSAL`
240    KwUniversal,
241
242    // -- Status/Access value keywords (last keyword range) --
243    /// `current`
244    KwCurrent,
245    /// `deprecated`
246    KwDeprecated,
247    /// `obsolete`
248    KwObsolete,
249    /// `mandatory` (SMIv1)
250    KwMandatory,
251    /// `optional` (SMIv1)
252    KwOptional,
253    /// `read-only`
254    KwReadOnly,
255    /// `read-write`
256    KwReadWrite,
257    /// `read-create`
258    KwReadCreate,
259    /// `write-only` (deprecated)
260    KwWriteOnly,
261    /// `not-accessible`
262    KwNotAccessible,
263    /// `accessible-for-notify`
264    KwAccessibleForNotify,
265    /// `not-implemented` (AGENT-CAPABILITIES)
266    KwNotImplemented,
267}
268
269impl TokenKind {
270    /// Returns `true` if this is any keyword token (structural, clause, macro,
271    /// type, tag, or status/access).
272    pub fn is_keyword(self) -> bool {
273        self.is_structural_keyword()
274            || self.is_clause_keyword()
275            || self.is_macro_keyword()
276            || self.is_type_keyword()
277            || self.is_tag_keyword()
278            || self.is_status_access_keyword()
279    }
280
281    /// Returns `true` for [`UppercaseIdent`](Self::UppercaseIdent) or
282    /// [`LowercaseIdent`](Self::LowercaseIdent).
283    pub fn is_identifier(self) -> bool {
284        matches!(self, TokenKind::UppercaseIdent | TokenKind::LowercaseIdent)
285    }
286
287    /// Returns `true` for built-in SMI type keywords (`INTEGER`, `Counter32`,
288    /// `OCTET`, `STRING`, `NetworkAddress`, etc.).
289    pub fn is_type_keyword(self) -> bool {
290        matches!(
291            self,
292            TokenKind::KwInteger
293                | TokenKind::KwUnsigned32
294                | TokenKind::KwCounter32
295                | TokenKind::KwCounter64
296                | TokenKind::KwGauge32
297                | TokenKind::KwIpAddress
298                | TokenKind::KwOpaque
299                | TokenKind::KwTimeTicks
300                | TokenKind::KwBits
301                | TokenKind::KwOctet
302                | TokenKind::KwString
303                | TokenKind::KwCounter
304                | TokenKind::KwGauge
305                | TokenKind::KwNetworkAddress
306        )
307    }
308
309    /// Returns `true` for SMI macro invocation keywords (`MODULE-IDENTITY`,
310    /// `OBJECT-TYPE`, `TRAP-TYPE`, etc.).
311    pub fn is_macro_keyword(self) -> bool {
312        matches!(
313            self,
314            TokenKind::KwModuleIdentity
315                | TokenKind::KwModuleCompliance
316                | TokenKind::KwObjectGroup
317                | TokenKind::KwNotificationGroup
318                | TokenKind::KwAgentCapabilities
319                | TokenKind::KwObjectType
320                | TokenKind::KwObjectIdentity
321                | TokenKind::KwNotificationType
322                | TokenKind::KwTextualConvention
323                | TokenKind::KwTrapType
324        )
325    }
326
327    /// Returns `true` for clause keywords used within macro bodies (`SYNTAX`,
328    /// `STATUS`, `DESCRIPTION`, `INDEX`, `DEFVAL`, etc.).
329    pub fn is_clause_keyword(self) -> bool {
330        matches!(
331            self,
332            TokenKind::KwSyntax
333                | TokenKind::KwMaxAccess
334                | TokenKind::KwMinAccess
335                | TokenKind::KwAccess
336                | TokenKind::KwStatus
337                | TokenKind::KwDescription
338                | TokenKind::KwReference
339                | TokenKind::KwIndex
340                | TokenKind::KwDefval
341                | TokenKind::KwAugments
342                | TokenKind::KwUnits
343                | TokenKind::KwDisplayHint
344                | TokenKind::KwObjects
345                | TokenKind::KwNotifications
346                | TokenKind::KwModule
347                | TokenKind::KwMandatoryGroups
348                | TokenKind::KwGroup
349                | TokenKind::KwWriteSyntax
350                | TokenKind::KwProductRelease
351                | TokenKind::KwSupports
352                | TokenKind::KwIncludes
353                | TokenKind::KwVariation
354                | TokenKind::KwCreationRequires
355                | TokenKind::KwRevision
356                | TokenKind::KwLastUpdated
357                | TokenKind::KwOrganization
358                | TokenKind::KwContactInfo
359                | TokenKind::KwImplied
360                | TokenKind::KwSize
361                | TokenKind::KwEnterprise
362                | TokenKind::KwVariables
363        )
364    }
365
366    /// Returns `true` for ASN.1 tag keywords (`APPLICATION`, `IMPLICIT`,
367    /// `UNIVERSAL`).
368    pub fn is_tag_keyword(self) -> bool {
369        matches!(
370            self,
371            TokenKind::KwApplication | TokenKind::KwImplicit | TokenKind::KwUniversal
372        )
373    }
374
375    /// Returns `true` for status or access value keywords (`current`,
376    /// `deprecated`, `read-only`, `not-accessible`, etc.).
377    pub fn is_status_access_keyword(self) -> bool {
378        matches!(
379            self,
380            TokenKind::KwCurrent
381                | TokenKind::KwDeprecated
382                | TokenKind::KwObsolete
383                | TokenKind::KwMandatory
384                | TokenKind::KwOptional
385                | TokenKind::KwReadOnly
386                | TokenKind::KwReadWrite
387                | TokenKind::KwReadCreate
388                | TokenKind::KwWriteOnly
389                | TokenKind::KwNotAccessible
390                | TokenKind::KwAccessibleForNotify
391                | TokenKind::KwNotImplemented
392        )
393    }
394
395    /// Returns `true` for structural keywords that frame the module
396    /// (`DEFINITIONS`, `BEGIN`, `END`, `IMPORTS`, `FROM`, `MACRO`, etc.).
397    pub fn is_structural_keyword(self) -> bool {
398        matches!(
399            self,
400            TokenKind::KwDefinitions
401                | TokenKind::KwBegin
402                | TokenKind::KwEnd
403                | TokenKind::KwImports
404                | TokenKind::KwExports
405                | TokenKind::KwFrom
406                | TokenKind::KwObject
407                | TokenKind::KwIdentifier
408                | TokenKind::KwSequence
409                | TokenKind::KwOf
410                | TokenKind::KwChoice
411                | TokenKind::KwMacro
412        )
413    }
414
415    /// Returns a human-readable name suitable for use in error messages.
416    ///
417    /// Punctuation tokens are shown as quoted characters (e.g. `'{'`),
418    /// keywords use their [`libsmi_name`](Self::libsmi_name), and other
419    /// tokens use descriptive labels like `"identifier"` or `"number"`.
420    pub fn display_name(self) -> &'static str {
421        match self {
422            TokenKind::Error => "<error>",
423            TokenKind::Eof => "end of file",
424            TokenKind::ForbiddenKeyword => "reserved keyword",
425            TokenKind::Comment => "comment",
426            TokenKind::UppercaseIdent => "identifier",
427            TokenKind::LowercaseIdent => "identifier",
428            TokenKind::Number => "number",
429            TokenKind::NegativeNumber => "negative number",
430            TokenKind::QuotedString => "quoted string",
431            TokenKind::HexString => "hex string",
432            TokenKind::BinString => "binary string",
433            TokenKind::LBracket => "'['",
434            TokenKind::RBracket => "']'",
435            TokenKind::LBrace => "'{'",
436            TokenKind::RBrace => "'}'",
437            TokenKind::LParen => "'('",
438            TokenKind::RParen => "')'",
439            TokenKind::Colon => "':'",
440            TokenKind::Semicolon => "';'",
441            TokenKind::Comma => "','",
442            TokenKind::Dot => "'.'",
443            TokenKind::Pipe => "'|'",
444            TokenKind::Minus => "'-'",
445            TokenKind::DotDot => "'..'",
446            TokenKind::ColonColonEqual => "'::='",
447            _ => self.libsmi_name(),
448        }
449    }
450
451    /// Returns the libsmi-compatible uppercase name for this token kind.
452    ///
453    /// These names match the token naming conventions used by the libsmi
454    /// library, with hyphens replaced by underscores (e.g. `OBJECT_TYPE`,
455    /// `READ_ONLY`, `COLON_COLON_EQUAL`).
456    pub fn libsmi_name(self) -> &'static str {
457        match self {
458            TokenKind::Error => "ERROR",
459            TokenKind::Eof => "EOF",
460            TokenKind::ForbiddenKeyword => "FORBIDDEN_KEYWORD",
461            TokenKind::Comment => "COMMENT",
462            TokenKind::UppercaseIdent => "UPPERCASE_IDENTIFIER",
463            TokenKind::LowercaseIdent => "LOWERCASE_IDENTIFIER",
464            TokenKind::Number => "NUMBER",
465            TokenKind::NegativeNumber => "NEGATIVENUMBER",
466            TokenKind::QuotedString => "QUOTED_STRING",
467            TokenKind::HexString => "HEX_STRING",
468            TokenKind::BinString => "BIN_STRING",
469            TokenKind::LBracket => "LBRACKET",
470            TokenKind::RBracket => "RBRACKET",
471            TokenKind::LBrace => "LBRACE",
472            TokenKind::RBrace => "RBRACE",
473            TokenKind::LParen => "LPAREN",
474            TokenKind::RParen => "RPAREN",
475            TokenKind::Colon => "COLON",
476            TokenKind::Semicolon => "SEMICOLON",
477            TokenKind::Comma => "COMMA",
478            TokenKind::Dot => "DOT",
479            TokenKind::Pipe => "PIPE",
480            TokenKind::Minus => "MINUS",
481            TokenKind::DotDot => "DOT_DOT",
482            TokenKind::ColonColonEqual => "COLON_COLON_EQUAL",
483            TokenKind::KwDefinitions => "DEFINITIONS",
484            TokenKind::KwBegin => "BEGIN",
485            TokenKind::KwEnd => "END",
486            TokenKind::KwImports => "IMPORTS",
487            TokenKind::KwExports => "EXPORTS",
488            TokenKind::KwFrom => "FROM",
489            TokenKind::KwObject => "OBJECT",
490            TokenKind::KwIdentifier => "IDENTIFIER",
491            TokenKind::KwSequence => "SEQUENCE",
492            TokenKind::KwOf => "OF",
493            TokenKind::KwChoice => "CHOICE",
494            TokenKind::KwMacro => "MACRO",
495            TokenKind::KwSyntax => "SYNTAX",
496            TokenKind::KwMaxAccess => "MAX_ACCESS",
497            TokenKind::KwMinAccess => "MIN_ACCESS",
498            TokenKind::KwAccess => "ACCESS",
499            TokenKind::KwStatus => "STATUS",
500            TokenKind::KwDescription => "DESCRIPTION",
501            TokenKind::KwReference => "REFERENCE",
502            TokenKind::KwIndex => "INDEX",
503            TokenKind::KwDefval => "DEFVAL",
504            TokenKind::KwAugments => "AUGMENTS",
505            TokenKind::KwUnits => "UNITS",
506            TokenKind::KwDisplayHint => "DISPLAY_HINT",
507            TokenKind::KwObjects => "OBJECTS",
508            TokenKind::KwNotifications => "NOTIFICATIONS",
509            TokenKind::KwModule => "MODULE",
510            TokenKind::KwMandatoryGroups => "MANDATORY_GROUPS",
511            TokenKind::KwGroup => "GROUP",
512            TokenKind::KwWriteSyntax => "WRITE_SYNTAX",
513            TokenKind::KwProductRelease => "PRODUCT_RELEASE",
514            TokenKind::KwSupports => "SUPPORTS",
515            TokenKind::KwIncludes => "INCLUDES",
516            TokenKind::KwVariation => "VARIATION",
517            TokenKind::KwCreationRequires => "CREATION_REQUIRES",
518            TokenKind::KwRevision => "REVISION",
519            TokenKind::KwLastUpdated => "LAST_UPDATED",
520            TokenKind::KwOrganization => "ORGANIZATION",
521            TokenKind::KwContactInfo => "CONTACT_INFO",
522            TokenKind::KwImplied => "IMPLIED",
523            TokenKind::KwSize => "SIZE",
524            TokenKind::KwEnterprise => "ENTERPRISE",
525            TokenKind::KwVariables => "VARIABLES",
526            TokenKind::KwModuleIdentity => "MODULE_IDENTITY",
527            TokenKind::KwModuleCompliance => "MODULE_COMPLIANCE",
528            TokenKind::KwObjectGroup => "OBJECT_GROUP",
529            TokenKind::KwNotificationGroup => "NOTIFICATION_GROUP",
530            TokenKind::KwAgentCapabilities => "AGENT_CAPABILITIES",
531            TokenKind::KwObjectType => "OBJECT_TYPE",
532            TokenKind::KwObjectIdentity => "OBJECT_IDENTITY",
533            TokenKind::KwNotificationType => "NOTIFICATION_TYPE",
534            TokenKind::KwTextualConvention => "TEXTUAL_CONVENTION",
535            TokenKind::KwTrapType => "TRAP_TYPE",
536            TokenKind::KwInteger => "INTEGER",
537            TokenKind::KwUnsigned32 => "UNSIGNED32",
538            TokenKind::KwCounter32 => "COUNTER32",
539            TokenKind::KwCounter64 => "COUNTER64",
540            TokenKind::KwGauge32 => "GAUGE32",
541            TokenKind::KwIpAddress => "IPADDRESS",
542            TokenKind::KwOpaque => "OPAQUE",
543            TokenKind::KwTimeTicks => "TIMETICKS",
544            TokenKind::KwBits => "BITS",
545            TokenKind::KwOctet => "OCTET",
546            TokenKind::KwString => "STRING",
547            TokenKind::KwCounter => "COUNTER",
548            TokenKind::KwGauge => "GAUGE",
549            TokenKind::KwNetworkAddress => "NETWORKADDRESS",
550            TokenKind::KwApplication => "APPLICATION",
551            TokenKind::KwImplicit => "IMPLICIT",
552            TokenKind::KwUniversal => "UNIVERSAL",
553            TokenKind::KwCurrent => "CURRENT",
554            TokenKind::KwDeprecated => "DEPRECATED",
555            TokenKind::KwObsolete => "OBSOLETE",
556            TokenKind::KwMandatory => "MANDATORY",
557            TokenKind::KwOptional => "OPTIONAL",
558            TokenKind::KwReadOnly => "READ_ONLY",
559            TokenKind::KwReadWrite => "READ_WRITE",
560            TokenKind::KwReadCreate => "READ_CREATE",
561            TokenKind::KwWriteOnly => "WRITE_ONLY",
562            TokenKind::KwNotAccessible => "NOT_ACCESSIBLE",
563            TokenKind::KwAccessibleForNotify => "ACCESSIBLE_FOR_NOTIFY",
564            TokenKind::KwNotImplemented => "NOT_IMPLEMENTED",
565        }
566    }
567}
568
569impl std::fmt::Display for TokenKind {
570    /// Formats using the [`libsmi_name`](TokenKind::libsmi_name) representation.
571    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
572        f.write_str(self.libsmi_name())
573    }
574}
575
576#[cfg(test)]
577mod tests {
578    use super::*;
579
580    #[test]
581    fn keyword_classification() {
582        assert!(TokenKind::KwDefinitions.is_keyword());
583        assert!(TokenKind::KwNotImplemented.is_keyword());
584        assert!(TokenKind::KwMacro.is_structural_keyword());
585        assert!(!TokenKind::KwSyntax.is_structural_keyword());
586        assert!(TokenKind::KwSyntax.is_clause_keyword());
587        assert!(TokenKind::KwVariables.is_clause_keyword());
588        assert!(!TokenKind::KwModuleIdentity.is_clause_keyword());
589        assert!(TokenKind::KwModuleIdentity.is_macro_keyword());
590        assert!(TokenKind::KwTrapType.is_macro_keyword());
591        assert!(TokenKind::KwInteger.is_type_keyword());
592        assert!(TokenKind::KwNetworkAddress.is_type_keyword());
593        assert!(TokenKind::KwApplication.is_tag_keyword());
594        assert!(TokenKind::KwUniversal.is_tag_keyword());
595        assert!(TokenKind::KwCurrent.is_status_access_keyword());
596        assert!(TokenKind::KwNotImplemented.is_status_access_keyword());
597    }
598
599    #[test]
600    fn non_keywords_are_not_keywords() {
601        assert!(!TokenKind::Error.is_keyword());
602        assert!(!TokenKind::UppercaseIdent.is_keyword());
603        assert!(!TokenKind::Number.is_keyword());
604        assert!(!TokenKind::LBrace.is_keyword());
605        assert!(!TokenKind::ColonColonEqual.is_keyword());
606    }
607
608    #[test]
609    fn identifier_classification() {
610        assert!(TokenKind::UppercaseIdent.is_identifier());
611        assert!(TokenKind::LowercaseIdent.is_identifier());
612        assert!(!TokenKind::KwObject.is_identifier());
613        assert!(!TokenKind::Number.is_identifier());
614    }
615
616    #[test]
617    fn libsmi_names() {
618        assert_eq!(TokenKind::Eof.libsmi_name(), "EOF");
619        assert_eq!(TokenKind::KwObjectType.libsmi_name(), "OBJECT_TYPE");
620        assert_eq!(
621            TokenKind::ColonColonEqual.libsmi_name(),
622            "COLON_COLON_EQUAL"
623        );
624        assert_eq!(TokenKind::KwReadOnly.libsmi_name(), "READ_ONLY");
625    }
626}