Skip to main content

logicaffeine_language/
token_class.rs

1//! The one classification truth for LOGOS tokens: parts of speech ARE the
2//! syntax — verbs paint as functions, nouns as types, adjectives as
3//! modifiers. Every highlighting surface (LSP semantic tokens, the terminal
4//! REPL, any future renderer) derives its colors from THIS mapping, so they
5//! can never disagree.
6//!
7//! The match is deliberately wildcard-free: adding a `TokenType` variant
8//! does not compile until someone decides its class — the decision lives
9//! here, next to the enum, not in an editor plugin.
10
11use crate::token::TokenType;
12
13/// What a token IS, presentation-wise. Mirrors the LSP semantic-token
14/// vocabulary (the LSP legend maps 1:1; the REPL maps to ANSI colors).
15#[derive(Debug, Clone, Copy, PartialEq, Eq)]
16pub enum TokenClass {
17    Keyword,
18    Type,
19    Function,
20    Variable,
21    String,
22    Number,
23    Operator,
24    Namespace,
25    Modifier,
26    Property,
27    Comment,
28    Parameter,
29    EnumMember,
30}
31
32/// Classify a token by its part of speech. `None` = structural tokens
33/// (indent/dedent/newline/EOF) that no surface paints.
34pub fn classify(kind: &TokenType) -> Option<TokenClass> {
35    classify_inner(kind, 0)
36}
37
38const MAX_AMBIGUOUS_DEPTH: usize = 3;
39
40fn classify_inner(kind: &TokenType, depth: usize) -> Option<TokenClass> {
41    match kind {
42        // Quantifiers → keyword
43        TokenType::All | TokenType::No | TokenType::Some | TokenType::Any
44        | TokenType::Both | TokenType::Most | TokenType::Few | TokenType::Many
45        | TokenType::Cardinal(_) | TokenType::AtLeast(_) | TokenType::AtMost(_) => {
46            Some(TokenClass::Keyword) // keyword
47        }
48
49        // Logical connectives → operator
50        TokenType::And | TokenType::Or | TokenType::Not
51        | TokenType::If | TokenType::Then | TokenType::Iff | TokenType::Because
52        | TokenType::Although
53        | TokenType::Until | TokenType::Release | TokenType::WeakUntil
54        | TokenType::Implies => {
55            Some(TokenClass::Operator) // operator
56        }
57
58        // Modal operators → keyword
59        TokenType::Must | TokenType::Shall | TokenType::Should | TokenType::Can
60        | TokenType::May | TokenType::Cannot | TokenType::Would | TokenType::Could
61        | TokenType::Might | TokenType::Had => {
62            Some(TokenClass::Keyword) // keyword
63        }
64
65        // Imperative keywords → keyword
66        TokenType::Let | TokenType::Set | TokenType::Return | TokenType::Break | TokenType::Be
67        | TokenType::While | TokenType::Repeat | TokenType::For | TokenType::In
68        | TokenType::From | TokenType::Assert | TokenType::Trust
69        | TokenType::Require | TokenType::Requires | TokenType::Ensures
70        | TokenType::Otherwise | TokenType::Else | TokenType::Elif
71        | TokenType::Call | TokenType::New | TokenType::Either | TokenType::Inspect
72        | TokenType::Native | TokenType::Escape | TokenType::Given | TokenType::Prove
73        | TokenType::Auto | TokenType::Read | TokenType::Write | TokenType::Console
74        | TokenType::File | TokenType::Give | TokenType::Show | TokenType::Push
75        | TokenType::Pop | TokenType::Copy | TokenType::Through | TokenType::Length
76        | TokenType::At | TokenType::Add | TokenType::Remove | TokenType::Contains
77        | TokenType::Union | TokenType::Intersection | TokenType::Inside
78        | TokenType::Zone | TokenType::Called | TokenType::Size | TokenType::Mapped
79        | TokenType::Attempt | TokenType::Following | TokenType::Simultaneously
80        | TokenType::Spawn | TokenType::Send | TokenType::Await | TokenType::Portable
81        | TokenType::Manifest | TokenType::Chunk | TokenType::Shared | TokenType::Merge
82        | TokenType::Increase | TokenType::Decrease | TokenType::Tally
83        | TokenType::SharedSet | TokenType::SharedSequence | TokenType::CollaborativeSequence
84        | TokenType::SharedMap | TokenType::Divergent | TokenType::Append
85        | TokenType::Resolve | TokenType::RemoveWins | TokenType::AddWins
86        | TokenType::YATA | TokenType::Values | TokenType::Check | TokenType::Listen
87        | TokenType::NetConnect | TokenType::Sleep | TokenType::Sync | TokenType::Mount
88        | TokenType::Persistent | TokenType::Combined | TokenType::Followed | TokenType::Launch | TokenType::Task
89        | TokenType::Pipe | TokenType::Receive | TokenType::Stop | TokenType::Try
90        | TokenType::Into | TokenType::First | TokenType::After | TokenType::Mut => {
91            Some(TokenClass::Keyword) // keyword
92        }
93
94        // Nouns → type
95        TokenType::Noun(_) => Some(TokenClass::Type),
96
97        // Verbs → function
98        TokenType::Verb { .. } => Some(TokenClass::Function),
99
100        // Adjectives → modifier
101        TokenType::Adjective(_) | TokenType::NonIntersectiveAdjective(_) => {
102            Some(TokenClass::Modifier) // modifier
103        }
104
105        // Proper names → variable with declaration modifier
106        TokenType::ProperName(_) => Some(TokenClass::Variable), // variable + declaration
107
108        // Pronouns → variable
109        TokenType::Pronoun { .. } => Some(TokenClass::Variable),
110
111        // Articles → keyword
112        TokenType::Article(_) => Some(TokenClass::Keyword),
113
114        // Copula → keyword
115        TokenType::Is | TokenType::Are | TokenType::Was | TokenType::Were => {
116            Some(TokenClass::Keyword)
117        }
118
119        // Wh-words → keyword
120        TokenType::That | TokenType::Who | TokenType::Whose | TokenType::What | TokenType::Where
121        | TokenType::When | TokenType::Why | TokenType::Does | TokenType::Do => {
122            Some(TokenClass::Keyword)
123        }
124
125        // Identity/reflexive → keyword
126        TokenType::Identity | TokenType::Equals | TokenType::Reflexive
127        | TokenType::Reciprocal | TokenType::Respectively => {
128            Some(TokenClass::Keyword)
129        }
130
131        // String literals → string
132        TokenType::StringLiteral(_) | TokenType::InterpolatedString(_) | TokenType::CharLiteral(_) => Some(TokenClass::String),
133
134        // Number literals → number
135        TokenType::Number(_) | TokenType::MoneyLiteral { .. } | TokenType::DurationLiteral { .. }
136        | TokenType::DateLiteral { .. } | TokenType::TimeLiteral { .. } => {
137            Some(TokenClass::Number)
138        }
139
140        // Operators → operator
141        TokenType::Plus | TokenType::Minus | TokenType::Star | TokenType::Slash
142        | TokenType::Percent | TokenType::Lt | TokenType::Gt | TokenType::LtEq
143        | TokenType::GtEq | TokenType::EqEq | TokenType::NotEq | TokenType::Arrow
144        | TokenType::Assign | TokenType::Xor | TokenType::Shifted
145        | TokenType::Amp | TokenType::VBar | TokenType::Tilde | TokenType::Caret
146        | TokenType::Dot | TokenType::StarStar | TokenType::SlashSlash
147        | TokenType::PlusEq | TokenType::MinusEq | TokenType::StarEq
148        | TokenType::SlashEq | TokenType::PercentEq => {
149            Some(TokenClass::Operator)
150        }
151
152        // Block headers → namespace
153        TokenType::BlockHeader { .. } => Some(TokenClass::Namespace),
154
155        // Prepositions → keyword
156        TokenType::Preposition(_) => Some(TokenClass::Keyword),
157
158        // Generic identifier → variable
159        TokenType::Identifier => Some(TokenClass::Variable),
160
161        // Comparatives/superlatives → modifier
162        TokenType::Comparative(_) | TokenType::Superlative(_) | TokenType::Than => {
163            Some(TokenClass::Modifier)
164        }
165
166        // Calendar/temporal → keyword
167        TokenType::CalendarUnit(_) | TokenType::Ago | TokenType::Hence
168        | TokenType::Before | TokenType::TemporalAdverb(_) => {
169            Some(TokenClass::Keyword)
170        }
171
172        // Item/Items → keyword
173        TokenType::Item | TokenType::Items => Some(TokenClass::Keyword),
174
175        // Adverbs → modifier
176        TokenType::Adverb(_) | TokenType::ScopalAdverb(_) => Some(TokenClass::Modifier),
177
178        // Focus/presup → keyword
179        TokenType::Focus(_) | TokenType::PresupTrigger(_) | TokenType::Measure(_) => {
180            Some(TokenClass::Keyword)
181        }
182
183        // Auxiliary → keyword
184        TokenType::Auxiliary(_) => Some(TokenClass::Keyword),
185
186        // Escape blocks → string (raw code)
187        TokenType::EscapeBlock(_) => Some(TokenClass::String),
188
189        // Performative → keyword
190        TokenType::Performative(_) => Some(TokenClass::Keyword),
191        TokenType::Exclamation => Some(TokenClass::Keyword),
192
193        // NPIs → keyword
194        TokenType::Anything | TokenType::Anyone | TokenType::Nothing
195        | TokenType::Nobody | TokenType::NoOne | TokenType::Nowhere
196        | TokenType::Ever | TokenType::Never => {
197            Some(TokenClass::Keyword)
198        }
199
200        // Ambiguous → try primary classification with depth guard
201        TokenType::Ambiguous { primary, .. } => {
202            if depth >= MAX_AMBIGUOUS_DEPTH {
203                return None;
204            }
205            classify_inner(primary, depth + 1)
206        }
207
208        // Particles → keyword
209        TokenType::Particle(_) => Some(TokenClass::Keyword),
210
211        // Control → keyword
212        TokenType::To => Some(TokenClass::Keyword),
213
214        // Possessive → operator
215        TokenType::Possessive => Some(TokenClass::Operator),
216
217        // Punctuation → skip (or operator for comma/period)
218        TokenType::Period | TokenType::Comma | TokenType::Colon => Some(TokenClass::Operator),
219        TokenType::LParen | TokenType::RParen | TokenType::LBracket | TokenType::RBracket
220        | TokenType::LBrace | TokenType::RBrace => {
221            Some(TokenClass::Operator)
222        }
223
224        // Structural tokens → skip
225        TokenType::Indent | TokenType::Dedent | TokenType::Newline | TokenType::EOF => {
226            None
227        }
228    }
229}