TextMateLib 1.0
Modern C++ implementation of the TextMate syntax highlighting engine
Loading...
Searching...
No Matches
grammar.h
1#ifndef TEXTMATELIB_GRAMMAR_H
2#define TEXTMATELIB_GRAMMAR_H
3
4#include "types.h"
5#include "rule.h"
6#include "theme.h"
7#include "onigLib.h"
8#include "rawGrammar.h"
9#include "registry.h"
10#include "basicScopesAttributeProvider.h"
11#include "matcher.h"
12#include <string>
13#include <vector>
14#include <memory>
15#include <cstdint>
16
17namespace tml {
18
19// Forward declarations
20class StateStackImpl;
21class AttributedScopeStack;
22class LineTokens;
23struct TokenTypeMatcher;
24
25// IToken interface
26struct IToken {
27 int startIndex;
28 int endIndex;
29 std::vector<std::string> scopes;
30
31 IToken() : startIndex(0), endIndex(0) {}
32};
33
34// ITokenizeLineResult interface
35struct ITokenizeLineResult {
36 std::vector<IToken> tokens;
37 StateStack* ruleStack;
38 bool stoppedEarly;
39
40 ITokenizeLineResult() : ruleStack(nullptr), stoppedEarly(false) {}
41};
42
43// ITokenizeLineResult2 interface
44struct ITokenizeLineResult2 {
45 std::vector<uint32_t> tokens; // Uint32Array equivalent
46 StateStack* ruleStack;
47 bool stoppedEarly;
48
49 ITokenizeLineResult2() : ruleStack(nullptr), stoppedEarly(false) {}
50};
51
52// IGrammar interface
53class IGrammar {
54public:
55 virtual ~IGrammar() {}
56
57 virtual ITokenizeLineResult tokenizeLine(
58 const std::string& lineText,
59 StateStack* prevState,
60 int timeLimit = 0
61 ) = 0;
62
63 virtual ITokenizeLineResult2 tokenizeLine2(
64 const std::string& lineText,
65 StateStack* prevState,
66 int timeLimit = 0
67 ) = 0;
68};
69
70// Injection structure
71struct Injection {
72 std::string debugSelector;
73 Matcher<std::vector<std::string>> matcher;
74 int priority; // -1, 0, or 1
75 RuleId ruleId;
76 IRawGrammar* grammar;
77
78 Injection() : priority(0), ruleId(ruleIdFromNumber(-1)), grammar(nullptr) {}
79};
80
81// TokenTypeMatcher structure
82struct TokenTypeMatcher {
83 Matcher<std::vector<std::string>> matcher;
85
86 TokenTypeMatcher() : type(StandardTokenType::Other) {}
87};
88
89// BalancedBracketSelectors class
90class BalancedBracketSelectors {
91private:
92 std::vector<Matcher<std::vector<std::string>>> _balancedBracketMatchers;
93 std::vector<Matcher<std::vector<std::string>>> _unbalancedBracketMatchers;
94 bool _allowAny;
95
96public:
97 BalancedBracketSelectors(
98 const std::vector<std::string>& balancedBracketSelectors,
99 const std::vector<std::string>& unbalancedBracketSelectors
100 );
101
102 bool matchesAlways() const;
103 bool matchesNever() const;
104 bool match(const std::vector<std::string>& scopes) const;
105};
106
107// AttributedScopeStack class
108class AttributedScopeStack {
109public:
110 AttributedScopeStack* parent;
111 ScopeName scopeName;
112 EncodedTokenAttributes tokenAttributes;
113
114 AttributedScopeStack(
115 AttributedScopeStack* parent_,
116 const ScopeName& scopeName_,
117 EncodedTokenAttributes tokenAttributes_
118 );
119 ~AttributedScopeStack();
120
121 static AttributedScopeStack* createRoot(
122 const std::string& scopeName,
123 EncodedTokenAttributes tokenAttributes
124 );
125
126 static AttributedScopeStack* createRootAndLookUpScopeName(
127 const std::string& scopeName,
128 EncodedTokenAttributes tokenAttributes,
129 Grammar* grammar
130 );
131
132 AttributedScopeStack* push(
133 Grammar* grammar,
134 const std::string& scopeName
135 );
136
137 AttributedScopeStack* pushAttributed(
138 const std::string& scopePath,
139 Grammar* grammar
140 );
141
142 std::vector<std::string> getScopeNames() const;
143
144 static bool equals(AttributedScopeStack* a, AttributedScopeStack* b);
145
146private:
147 static AttributedScopeStack* _pushAttributed(
148 AttributedScopeStack* target,
149 const std::string& scopeName,
150 Grammar* grammar
151 );
152};
153
154// StateStackImpl class (StateStack implementation)
155class StateStackImpl : public StateStack {
156private:
157 int _enterPos;
158 int _anchorPos;
159
160public:
161 static StateStackImpl* NULL_STATE;
162
163 StateStackImpl* parent;
164 RuleId ruleId;
165 bool beginRuleCapturedEOL;
166 std::string* endRule;
167 AttributedScopeStack* nameScopesList;
168 AttributedScopeStack* contentNameScopesList;
169
170 StateStackImpl(
171 StateStackImpl* parent_,
172 RuleId ruleId_,
173 int enterPos_,
174 int anchorPos_,
175 bool beginRuleCapturedEOL_,
176 const std::string* endRule_,
177 AttributedScopeStack* nameScopesList_,
178 AttributedScopeStack* contentNameScopesList_
179 );
180
181 ~StateStackImpl();
182
183 // StateStack interface implementation
184 int depth;
185 int getDepth() const override { return depth; }
186 StateStack* clone() override;
187 bool equals(StateStack* other) override;
188
189 void reset();
190
191 // Stack manipulation
192 StateStackImpl* push(
193 RuleId ruleId,
194 int enterPos,
195 int anchorPos,
196 bool beginRuleCapturedEOL,
197 const std::string* endRule,
198 AttributedScopeStack* nameScopesList,
199 AttributedScopeStack* contentNameScopesList
200 );
201
202 StateStackImpl* pop();
203 StateStackImpl* safePop();
204
205 // Accessors
206 int getEnterPos() const { return _enterPos; }
207 int getAnchorPos() const { return _anchorPos; }
208 Rule* getRule(Grammar* grammar);
209
210 // State modification
211 StateStackImpl* withContentNameScopesList(AttributedScopeStack* contentNameScopesList);
212 StateStackImpl* withEndRule(const std::string& endRule);
213
214 // Comparison
215 bool hasSameRuleAs(StateStackImpl* other);
216
217 std::string toString() const;
218};
219
220// StackNodeArena: opt-in reclaimer for the structurally-shared StateStackImpl /
221// AttributedScopeStack node graph.
222//
223// These nodes are immutable, persistent, and share parent prefixes across lines; they are
224// allocated with raw `new` and have no reference counting, so left to themselves they leak.
225// Direct C++/C-API callers keep that leak-free-of-overhead behavior: when no arena is active
226// (the default), construction registers nothing and nothing is ever auto-freed, so existing
227// callers and tests are completely unaffected.
228//
229// A caller that owns a bounded tokenization episode (e.g. the WASM bindings) can install an
230// arena for the duration of that episode. Every node constructed while the arena is active is
231// recorded. At the episode boundary the owner either frees everything (clear(), for batch
232// paths where no node escapes) or mark-sweeps from the surviving roots (sweepKeeping(), for
233// per-line paths where the returned rule stack must outlive the call). Sweeping is safe only
234// after results have been fully consumed/copied, because survivors' parent chains are walked.
235class StackNodeArena {
236public:
237 std::vector<StateStackImpl*> stacks;
238 std::vector<AttributedScopeStack*> scopes;
239
240 // Free every node recorded in this arena. Use when nothing escapes the episode.
241 void clear();
242
243 // Free every recorded node except those reachable (via parent chains) from `roots`,
244 // then compact the arena down to the survivors. Use when some rule stacks must live on.
245 void sweepKeeping(const std::vector<StateStackImpl*>& roots);
246};
247
248// Active-arena hooks. When set, StateStackImpl / AttributedScopeStack constructors register
249// themselves with the arena. nullptr (the default) means "record nothing" — identical to the
250// historical behavior. Not thread-safe; intended for single-threaded WASM use.
251StackNodeArena* tmlGetActiveArena();
252void tmlSetActiveArena(StackNodeArena* arena);
253
254// LineTokens class
255class LineTokens {
256private:
257 bool _emitBinaryTokens;
258 std::string _lineText;
259 std::vector<TokenTypeMatcher> _tokenTypeMatchers;
260 BalancedBracketSelectors* _balancedBracketSelectors;
261
262 std::vector<IToken> _tokens;
263 std::vector<uint32_t> _binaryTokens;
264 int _lastTokenEndIndex;
265
266public:
267 LineTokens(
268 bool emitBinaryTokens,
269 const std::string& lineText,
270 const std::vector<TokenTypeMatcher>& tokenTypeMatchers,
271 BalancedBracketSelectors* balancedBracketSelectors
272 );
273
274 void produce(StateStackImpl* stack, int endIndex);
275 void produceFromScopes(AttributedScopeStack* scopesList, int endIndex);
276
277 std::vector<IToken> getResult(StateStackImpl* stack, int lineLength);
278 std::vector<uint32_t> getBinaryResult(StateStackImpl* stack, int lineLength);
279};
280
281// Grammar class
282class Grammar : public IGrammar, public IRuleFactoryHelper, public IOnigLib {
283private:
284 ScopeName _rootScopeName;
285 RuleId _rootId;
286 int _lastRuleId;
287 std::vector<Rule*> _ruleId2desc;
288 std::map<std::string, IRawGrammar*> _includedGrammars;
289 IGrammarRepository* _grammarRepository;
290 IThemeProvider* _themeProvider;
291 IRawGrammar* _grammar;
292 std::vector<Injection>* _injections;
293 BasicScopeAttributesProvider* _basicScopeAttributesProvider;
294 std::vector<TokenTypeMatcher> _tokenTypeMatchers;
295 IOnigLib* _onigLib;
296
297public:
298 BalancedBracketSelectors* balancedBracketSelectors;
299
300 Grammar(
301 const ScopeName& rootScopeName,
302 IRawGrammar* grammar,
303 int initialLanguage,
304 const EmbeddedLanguagesMap* embeddedLanguages,
305 const TokenTypeMap* tokenTypes,
306 BalancedBracketSelectors* balancedBracketSelectors_,
307 IGrammarRepository* grammarRepository,
308 IThemeProvider* themeProvider,
309 IOnigLib* onigLib
310 );
311
312 ~Grammar();
313
314 void dispose();
315
316 IThemeProvider* getThemeProvider() const { return _themeProvider; }
317
318 size_t getRuleCount() const { return _ruleId2desc.size(); }
319
320 // IOnigLib implementation
321 OnigScanner* createOnigScanner(const std::vector<std::string>& sources) override;
322 OnigString* createOnigString(const std::string& str) override;
323
324 // IRuleRegistry implementation
325 Rule* getRule(RuleId ruleId) override;
326 RuleId registerRule(Rule* rule) override;
327
328 // IRuleFactoryHelper implementation (new methods)
329 RuleId allocateRuleId() override;
330 void setRule(RuleId ruleId, Rule* rule) override;
331
332 // IGrammarRegistry implementation
333 IRawGrammar* getExternalGrammar(const std::string& scopeName, IRawRepository* repository) override;
334
335 // Get metadata for scope
336 BasicScopeAttributes getMetadataForScope(const std::string& scope);
337
338 // Get injections
339 std::vector<Injection> getInjections();
340
341 // IGrammar implementation
342 ITokenizeLineResult tokenizeLine(
343 const std::string& lineText,
344 StateStack* prevState,
345 int timeLimit = 0
346 ) override;
347
348 ITokenizeLineResult2 tokenizeLine2(
349 const std::string& lineText,
350 StateStack* prevState,
351 int timeLimit = 0
352 ) override;
353
354 // Get the root scope name of this grammar
355 ScopeName getScopeName() const { return _rootScopeName; }
356
357private:
358 std::vector<Injection> _collectInjections();
359
360 struct TokenizeResult {
361 int lineLength;
362 LineTokens* lineTokens;
363 StateStackImpl* ruleStack;
364 bool stoppedEarly;
365 };
366
367 TokenizeResult _tokenize(
368 const std::string& lineText,
369 StateStackImpl* prevState,
370 bool emitBinaryTokens,
371 int timeLimit
372 );
373};
374
375// Helper function to create grammar
376Grammar* createGrammar(
377 const ScopeName& scopeName,
378 IRawGrammar* grammar,
379 int initialLanguage,
380 const EmbeddedLanguagesMap* embeddedLanguages,
381 const TokenTypeMap* tokenTypes,
382 BalancedBracketSelectors* balancedBracketSelectors,
383 IGrammarRepository* grammarRepository,
384 IThemeProvider* themeProvider,
385 IOnigLib* onigLib
386);
387
388// Initialize grammar (merge with base if needed)
389IRawGrammar* initGrammar(IRawGrammar* grammar, IRawRule* base);
390
391} // namespace tml
392
393#endif // TEXTMATELIB_GRAMMAR_H
std::string ScopeName
Semantic name identifying a scope (e.g., "source.javascript", "comment.line")
Definition types.h:20
std::map< std::string, int > EmbeddedLanguagesMap
Map from embedded language name to token type ID.
Definition types.h:175
std::map< std::string, StandardTokenType > TokenTypeMap
Map from scope pattern to standard token type.
Definition types.h:179
RuleId ruleIdFromNumber(int id)
Convert an integer to a RuleId.
Definition types.h:109
StandardTokenType
Standard TextMate token type for syntax classification.
Definition types.h:136
@ Other
Not a recognized standard type.
int32_t EncodedTokenAttributes
Compact 32-bit encoding of a token's attributes.
Definition types.h:128
Core type definitions and interfaces for TextMateLib.