TextMateLib 1.0
Modern C++ implementation of the TextMate syntax highlighting engine
Loading...
Searching...
No Matches
rule.cpp
1#include "rule.h"
2#include "grammarDependencies.h"
3#include <stdexcept>
4#include <iostream>
5
6namespace tml {
7
8// CompiledRule implementation
9
10CompiledRule::CompiledRule() : scanner(nullptr) {}
11
12CompiledRule::~CompiledRule() {
13 dispose();
14}
15
16void CompiledRule::dispose() {
17 if (scanner != nullptr) {
18 scanner->dispose();
19 delete scanner;
20 scanner = nullptr;
21 }
22}
23
24// RegExpSourceList implementation
25
26RegExpSourceList::RegExpSourceList()
27 : _hasAnchors(false), _cached(nullptr),
28 _anchorCache_A0_G0(nullptr), _anchorCache_A0_G1(nullptr),
29 _anchorCache_A1_G0(nullptr), _anchorCache_A1_G1(nullptr) {
30}
31
32RegExpSourceList::~RegExpSourceList() {
33 dispose();
34}
35
36void RegExpSourceList::push(RegexSource* item) {
37 _items.push_back(item);
38 if (item->hasAnchor) {
39 _hasAnchors = true;
40 }
41}
42
43void RegExpSourceList::unshift(RegexSource* item) {
44 _items.insert(_items.begin(), item);
45 if (item->hasAnchor) {
46 _hasAnchors = true;
47 }
48}
49
50void RegExpSourceList::setSource(int index, const std::string& newSource) {
51 if (index >= 0 && index < static_cast<int>(_items.size())) {
52 if (_items[index]->source != newSource) {
53 // Bust the cache when source changes
54 dispose();
55 _items[index]->source = newSource;
56 }
57 }
58}
59
60int RegExpSourceList::length() const {
61 return _items.size();
62}
63
64CompiledRule* RegExpSourceList::compile(IOnigLib* onigLib) {
65 if (!_cached) {
66 std::vector<std::string> sources;
67 for (auto* item : _items) {
68 sources.push_back(item->source);
69 }
70
71 _cached = new CompiledRule();
72 _cached->scanner = onigLib->createOnigScanner(sources);
73
74 for (auto* item : _items) {
75 _cached->rules.push_back(item->ruleId);
76 }
77 }
78 return _cached;
79}
80
81CompiledRule* RegExpSourceList::compileAG(IOnigLib* onigLib, bool allowA, bool allowG) {
82 // Check if we need to resolve anchors
83 if (!_hasAnchors) {
84 // No anchors, use the cached compile
85 return compile(onigLib);
86 }
87
88 // Cache the compiled rules for different allowA/allowG combinations
89 CompiledRule** cacheSlot = nullptr;
90 if (allowA) {
91 if (allowG) {
92 cacheSlot = &_anchorCache_A1_G1;
93 } else {
94 cacheSlot = &_anchorCache_A1_G0;
95 }
96 } else {
97 if (allowG) {
98 cacheSlot = &_anchorCache_A0_G1;
99 } else {
100 cacheSlot = &_anchorCache_A0_G0;
101 }
102 }
103
104 if (*cacheSlot != nullptr) {
105 return *cacheSlot;
106 }
107
108 // Create and cache the compiled rule
109 std::vector<std::string> sources;
110 for (auto* item : _items) {
111 sources.push_back(item->resolveAnchors(allowA, allowG));
112 }
113
114 CompiledRule* result = new CompiledRule();
115 result->scanner = onigLib->createOnigScanner(sources);
116
117 for (auto* item : _items) {
118 result->rules.push_back(item->ruleId);
119 }
120
121 *cacheSlot = result;
122 return result;
123}
124
125void RegExpSourceList::dispose() {
126 if (_cached) {
127 _cached->dispose();
128 delete _cached;
129 _cached = nullptr;
130 }
131 if (_anchorCache_A0_G0) {
132 _anchorCache_A0_G0->dispose();
133 delete _anchorCache_A0_G0;
134 _anchorCache_A0_G0 = nullptr;
135 }
136 if (_anchorCache_A0_G1) {
137 _anchorCache_A0_G1->dispose();
138 delete _anchorCache_A0_G1;
139 _anchorCache_A0_G1 = nullptr;
140 }
141 if (_anchorCache_A1_G0) {
142 _anchorCache_A1_G0->dispose();
143 delete _anchorCache_A1_G0;
144 _anchorCache_A1_G0 = nullptr;
145 }
146 if (_anchorCache_A1_G1) {
147 _anchorCache_A1_G1->dispose();
148 delete _anchorCache_A1_G1;
149 _anchorCache_A1_G1 = nullptr;
150 }
151}
152
153// Rule base class implementation
154
155Rule::Rule(ILocation* location_, RuleId id_,
156 const std::string* name, const std::string* contentName)
157 : location(location_), id(id_),
158 _name(name ? new std::string(*name) : nullptr),
159 _contentName(contentName ? new std::string(*contentName) : nullptr) {
160
161 _nameIsCapturing = RegexSource::hasCaptures(_name);
162 _contentNameIsCapturing = RegexSource::hasCaptures(_contentName);
163}
164
165Rule::~Rule() {
166 delete location;
167 delete _name;
168 delete _contentName;
169}
170
171std::string Rule::getDebugName() const {
172 std::string loc = location ? (basename(location->filename) + ":" + std::to_string(location->line)) : "unknown";
173 return "Rule#" + std::to_string(ruleIdToNumber(id)) + " @ " + loc;
174}
175
176std::string* Rule::getName(const std::string* lineText,
177 const std::vector<IOnigCaptureIndex>* captureIndices) const {
178 if (!_nameIsCapturing || _name == nullptr || lineText == nullptr || captureIndices == nullptr) {
179 return _name ? new std::string(*_name) : nullptr;
180 }
181 return new std::string(RegexSource::replaceCaptures(*_name, *lineText, *captureIndices));
182}
183
184std::string* Rule::getContentName(const std::string& lineText,
185 const std::vector<IOnigCaptureIndex>& captureIndices) const {
186 if (!_contentNameIsCapturing || _contentName == nullptr) {
187 return _contentName ? new std::string(*_contentName) : nullptr;
188 }
189 return new std::string(RegexSource::replaceCaptures(*_contentName, lineText, captureIndices));
190}
191
192// CaptureRule implementation
193
194CaptureRule::CaptureRule(ILocation* location_, RuleId id_,
195 const std::string* name, const std::string* contentName,
196 RuleId retokenizeCapturedWithRuleId_)
197 : Rule(location_, id_, name, contentName),
198 retokenizeCapturedWithRuleId(retokenizeCapturedWithRuleId_) {
199}
200
201void CaptureRule::dispose() {
202 // Nothing specific to dispose
203}
204
205void CaptureRule::collectPatterns(IRuleRegistry* grammar, RegExpSourceList* out) {
206 throw std::runtime_error("Not supported!");
207}
208
209CompiledRule* CaptureRule::compile(IRuleRegistry* grammar, IOnigLib* onigLib,
210 const std::string* endRegexSource) {
211 throw std::runtime_error("Not supported!");
212}
213
214CompiledRule* CaptureRule::compileAG(IRuleRegistry* grammar, IOnigLib* onigLib,
215 const std::string* endRegexSource,
216 bool allowA, bool allowG) {
217 throw std::runtime_error("Not supported!");
218}
219
220// MatchRule implementation
221
222MatchRule::MatchRule(ILocation* location_, RuleId id_,
223 const std::string* name, const std::string& match,
224 const std::vector<CaptureRule*>& captures_)
225 : Rule(location_, id_, name, nullptr),
226 _match(match, id_),
227 captures(captures_),
228 _cachedCompiledPatterns(nullptr) {
229}
230
231MatchRule::~MatchRule() {
232 // Note: CaptureRules are now registered with the Grammar and will be
233 // deleted by the Grammar's dispose() method. We should NOT delete them here
234 // to avoid double-free.
235}
236
237void MatchRule::dispose() {
238 if (_cachedCompiledPatterns) {
239 _cachedCompiledPatterns->dispose();
240 delete _cachedCompiledPatterns;
241 _cachedCompiledPatterns = nullptr;
242 }
243}
244
245std::string MatchRule::getDebugMatchRegExp() const {
246 return _match.source;
247}
248
249void MatchRule::collectPatterns(IRuleRegistry* grammar, RegExpSourceList* out) {
250 out->push(&_match);
251}
252
253CompiledRule* MatchRule::compile(IRuleRegistry* grammar, IOnigLib* onigLib,
254 const std::string* endRegexSource) {
255 return _getCachedCompiledPatterns(grammar)->compile(onigLib);
256}
257
258CompiledRule* MatchRule::compileAG(IRuleRegistry* grammar, IOnigLib* onigLib,
259 const std::string* endRegexSource,
260 bool allowA, bool allowG) {
261 return _getCachedCompiledPatterns(grammar)->compileAG(onigLib, allowA, allowG);
262}
263
264RegExpSourceList* MatchRule::_getCachedCompiledPatterns(IRuleRegistry* grammar) {
265 if (!_cachedCompiledPatterns) {
266 _cachedCompiledPatterns = new RegExpSourceList();
267 collectPatterns(grammar, _cachedCompiledPatterns);
268 }
269 return _cachedCompiledPatterns;
270}
271
272// IncludeOnlyRule implementation
273
274IncludeOnlyRule::IncludeOnlyRule(ILocation* location_, RuleId id_,
275 const std::string* name, const std::string* contentName,
276 const ICompilePatternsResult& patterns_)
277 : Rule(location_, id_, name, contentName),
278 patterns(patterns_.patterns),
279 hasMissingPatterns(patterns_.hasMissingPatterns),
280 _cachedCompiledPatterns(nullptr) {
281}
282
283void IncludeOnlyRule::dispose() {
284 if (_cachedCompiledPatterns) {
285 _cachedCompiledPatterns->dispose();
286 delete _cachedCompiledPatterns;
287 _cachedCompiledPatterns = nullptr;
288 }
289}
290
291void IncludeOnlyRule::collectPatterns(IRuleRegistry* grammar, RegExpSourceList* out) {
292 for (const auto& pattern : patterns) {
293 Rule* rule = grammar->getRule(pattern);
294 rule->collectPatterns(grammar, out);
295 }
296}
297
298CompiledRule* IncludeOnlyRule::compile(IRuleRegistry* grammar, IOnigLib* onigLib,
299 const std::string* endRegexSource) {
300 return _getCachedCompiledPatterns(grammar)->compile(onigLib);
301}
302
303CompiledRule* IncludeOnlyRule::compileAG(IRuleRegistry* grammar, IOnigLib* onigLib,
304 const std::string* endRegexSource,
305 bool allowA, bool allowG) {
306 return _getCachedCompiledPatterns(grammar)->compileAG(onigLib, allowA, allowG);
307}
308
309RegExpSourceList* IncludeOnlyRule::_getCachedCompiledPatterns(IRuleRegistry* grammar) {
310 if (!_cachedCompiledPatterns) {
311 _cachedCompiledPatterns = new RegExpSourceList();
312 collectPatterns(grammar, _cachedCompiledPatterns);
313 }
314 return _cachedCompiledPatterns;
315}
316
317// BeginEndRule implementation
318
319BeginEndRule::BeginEndRule(ILocation* location_, RuleId id_,
320 const std::string* name, const std::string* contentName,
321 const std::string& begin, const std::vector<CaptureRule*>& beginCaptures_,
322 const std::string& end, const std::vector<CaptureRule*>& endCaptures_,
323 bool applyEndPatternLast_, const ICompilePatternsResult& patterns_)
324 : Rule(location_, id_, name, contentName),
325 _begin(begin, id_),
326 _end(end.empty() ? "\uFFFF" : end, END_RULE_ID),
327 beginCaptures(beginCaptures_),
328 endHasBackReferences(_end.hasBackReferences),
329 endCaptures(endCaptures_),
330 applyEndPatternLast(applyEndPatternLast_),
331 patterns(patterns_.patterns),
332 hasMissingPatterns(patterns_.hasMissingPatterns),
333 _cachedCompiledPatterns(nullptr) {
334}
335
336BeginEndRule::~BeginEndRule() {
337 // Note: CaptureRules are now registered with the Grammar and will be
338 // deleted by the Grammar's dispose() method. We should NOT delete them here
339 // to avoid double-free.
340 // The beginCaptures and endCaptures vectors just hold pointers, not ownership.
341}
342
343void BeginEndRule::dispose() {
344 if (_cachedCompiledPatterns) {
345 _cachedCompiledPatterns->dispose();
346 delete _cachedCompiledPatterns;
347 _cachedCompiledPatterns = nullptr;
348 }
349}
350
351std::string BeginEndRule::getDebugBeginRegExp() const {
352 return _begin.source;
353}
354
355std::string BeginEndRule::getDebugEndRegExp() const {
356 return _end.source;
357}
358
359std::string BeginEndRule::getEndWithResolvedBackReferences(const std::string& lineText,
360 const std::vector<IOnigCaptureIndex>& captureIndices) {
361 return _end.resolveBackReferences(lineText, captureIndices);
362}
363
364void BeginEndRule::collectPatterns(IRuleRegistry* grammar, RegExpSourceList* out) {
365 out->push(&_begin);
366}
367
368CompiledRule* BeginEndRule::compile(IRuleRegistry* grammar, IOnigLib* onigLib,
369 const std::string* endRegexSource) {
370 std::string endSource = endRegexSource ? *endRegexSource : _end.source;
371 return _getCachedCompiledPatterns(grammar, endSource)->compile(onigLib);
372}
373
374CompiledRule* BeginEndRule::compileAG(IRuleRegistry* grammar, IOnigLib* onigLib,
375 const std::string* endRegexSource,
376 bool allowA, bool allowG) {
377 std::string endSource = endRegexSource ? *endRegexSource : _end.source;
378 return _getCachedCompiledPatterns(grammar, endSource)->compileAG(onigLib, allowA, allowG);
379}
380
381RegExpSourceList* BeginEndRule::_getCachedCompiledPatterns(IRuleRegistry* grammar,
382 const std::string& endRegexSource) {
383 if (!_cachedCompiledPatterns) {
384 _cachedCompiledPatterns = new RegExpSourceList();
385
386 for (const auto& pattern : patterns) {
387 Rule* rule = grammar->getRule(pattern);
388 rule->collectPatterns(grammar, _cachedCompiledPatterns);
389 }
390
391 // Clone the end pattern if it has back-references to avoid sharing
392 // the same RegexSource across multiple concurrent rule instances
393 RegexSource* endPattern = _end.hasBackReferences ? _end.clone() : new RegexSource(_end.source, _end.ruleId);
394 if (applyEndPatternLast) {
395 _cachedCompiledPatterns->push(endPattern);
396 } else {
397 _cachedCompiledPatterns->unshift(endPattern);
398 }
399 }
400
401 if (_end.hasBackReferences) {
402 int index = applyEndPatternLast ? (_cachedCompiledPatterns->length() - 1) : 0;
403 _cachedCompiledPatterns->setSource(index, endRegexSource);
404 }
405
406 return _cachedCompiledPatterns;
407}
408
409// BeginWhileRule implementation
410
411BeginWhileRule::BeginWhileRule(ILocation* location_, RuleId id_,
412 const std::string* name, const std::string* contentName,
413 const std::string& begin, const std::vector<CaptureRule*>& beginCaptures_,
414 const std::string& whilePattern, const std::vector<CaptureRule*>& whileCaptures_,
415 const ICompilePatternsResult& patterns_)
416 : Rule(location_, id_, name, contentName),
417 _begin(begin, id_),
418 _while(whilePattern, WHILE_RULE_ID),
419 beginCaptures(beginCaptures_),
420 whileCaptures(whileCaptures_),
421 whileHasBackReferences(_while.hasBackReferences),
422 patterns(patterns_.patterns),
423 hasMissingPatterns(patterns_.hasMissingPatterns),
424 _cachedCompiledPatterns(nullptr),
425 _cachedCompiledWhilePatterns(nullptr) {
426}
427
428BeginWhileRule::~BeginWhileRule() {
429 // Note: CaptureRules are now registered with the Grammar and will be
430 // deleted by the Grammar's dispose() method. We should NOT delete them here
431 // to avoid double-free.
432}
433
434void BeginWhileRule::dispose() {
435 if (_cachedCompiledPatterns) {
436 _cachedCompiledPatterns->dispose();
437 delete _cachedCompiledPatterns;
438 _cachedCompiledPatterns = nullptr;
439 }
440 if (_cachedCompiledWhilePatterns) {
441 _cachedCompiledWhilePatterns->dispose();
442 delete _cachedCompiledWhilePatterns;
443 _cachedCompiledWhilePatterns = nullptr;
444 }
445}
446
447std::string BeginWhileRule::getDebugBeginRegExp() const {
448 return _begin.source;
449}
450
451std::string BeginWhileRule::getDebugWhileRegExp() const {
452 return _while.source;
453}
454
455std::string BeginWhileRule::getWhileWithResolvedBackReferences(const std::string& lineText,
456 const std::vector<IOnigCaptureIndex>& captureIndices) {
457 return _while.resolveBackReferences(lineText, captureIndices);
458}
459
460void BeginWhileRule::collectPatterns(IRuleRegistry* grammar, RegExpSourceList* out) {
461 out->push(&_begin);
462}
463
464CompiledRule* BeginWhileRule::compile(IRuleRegistry* grammar, IOnigLib* onigLib,
465 const std::string* endRegexSource) {
466 return _getCachedCompiledPatterns(grammar)->compile(onigLib);
467}
468
469CompiledRule* BeginWhileRule::compileAG(IRuleRegistry* grammar, IOnigLib* onigLib,
470 const std::string* endRegexSource,
471 bool allowA, bool allowG) {
472 return _getCachedCompiledPatterns(grammar)->compileAG(onigLib, allowA, allowG);
473}
474
475CompiledRule* BeginWhileRule::compileWhile(IOnigLib* onigLib, const std::string* endRegexSource) {
476 std::string whileSource = endRegexSource ? *endRegexSource : _while.source;
477 return _getCachedCompiledWhilePatterns(onigLib, whileSource)->compile(onigLib);
478}
479
480CompiledRule* BeginWhileRule::compileWhileAG(IOnigLib* onigLib, const std::string* endRegexSource,
481 bool allowA, bool allowG) {
482 std::string whileSource = endRegexSource ? *endRegexSource : _while.source;
483 return _getCachedCompiledWhilePatterns(onigLib, whileSource)->compileAG(onigLib, allowA, allowG);
484}
485
486RegExpSourceList* BeginWhileRule::_getCachedCompiledPatterns(IRuleRegistry* grammar) {
487 if (!_cachedCompiledPatterns) {
488 _cachedCompiledPatterns = new RegExpSourceList();
489
490 for (const auto& pattern : patterns) {
491 Rule* rule = grammar->getRule(pattern);
492 rule->collectPatterns(grammar, _cachedCompiledPatterns);
493 }
494 }
495 return _cachedCompiledPatterns;
496}
497
498RegExpSourceList* BeginWhileRule::_getCachedCompiledWhilePatterns(IOnigLib* onigLib,
499 const std::string& whileRegexSource) {
500 if (!_cachedCompiledWhilePatterns) {
501 _cachedCompiledWhilePatterns = new RegExpSourceList();
502 // Clone the while pattern if it has back-references to avoid sharing
503 // the same RegexSource across multiple concurrent rule instances
504 RegexSource* whilePattern = _while.hasBackReferences ? _while.clone() : new RegexSource(_while.source, _while.ruleId);
505 _cachedCompiledWhilePatterns->push(whilePattern);
506 }
507
508 if (_while.hasBackReferences) {
509 _cachedCompiledWhilePatterns->setSource(0, whileRegexSource);
510 }
511
512 return _cachedCompiledWhilePatterns;
513}
514
515// RuleFactory implementation
516
517Rule* RuleFactory::createCaptureRule(IRuleFactoryHelper* helper, ILocation* location,
518 const std::string* name, const std::string* contentName,
519 RuleId retokenizeCapturedWithRuleId) {
520 CaptureRule* rule = new CaptureRule(location, ruleIdFromNumber(-1), name, contentName, retokenizeCapturedWithRuleId);
521 RuleId registeredId = helper->registerRule(rule);
522 rule->id = registeredId; // Update the rule's ID with the registered ID
523 return rule;
524}
525
526RuleId RuleFactory::getCompiledRuleId(IRawRule* desc, IRuleFactoryHelper* helper, IRawRepository* repository) {
527 if (!desc) {
528 return ruleIdFromNumber(-1);
529 }
530
531 if (desc->id != nullptr) {
532 return *desc->id;
533 }
534
535 // ✅ PHASE 2 FIX: Allocate ID first, build rule, then store it
536 RuleId ruleId = helper->allocateRuleId();
537 desc->id = new RuleId(ruleId);
538
539 Rule* rule = nullptr;
540
541 if (desc->match) {
542 rule = new MatchRule(
543 desc->tmlLocation,
544 ruleId,
545 desc->name,
546 *desc->match,
547 _compileCaptures(desc->captures, helper, repository)
548 );
549 } else if (desc->begin == nullptr) {
550 // Note: In TypeScript, repositories are merged here using mergeObjects.
551 // In C++, repository resolution happens during pattern compilation via
552 // the passed repository parameter, so explicit merging isn't needed.
553 std::vector<IRawRule*>* patterns = desc->patterns;
554 if (!patterns && desc->include) {
555 patterns = new std::vector<IRawRule*>();
556 IRawRule* includeRule = new IRawRule();
557 includeRule->include = new std::string(*desc->include);
558 patterns->push_back(includeRule);
559 }
560
561 rule = new IncludeOnlyRule(
562 desc->tmlLocation,
563 ruleId,
564 desc->name,
565 desc->contentName,
566 _compilePatterns(patterns, helper, repository)
567 );
568 } else if (desc->whilePattern) {
569 rule = new BeginWhileRule(
570 desc->tmlLocation,
571 ruleId,
572 desc->name,
573 desc->contentName,
574 *desc->begin,
575 _compileCaptures(desc->beginCaptures ? desc->beginCaptures : desc->captures, helper, repository),
576 *desc->whilePattern,
577 _compileCaptures(desc->whileCaptures ? desc->whileCaptures : desc->captures, helper, repository),
578 _compilePatterns(desc->patterns, helper, repository)
579 );
580 } else {
581 rule = new BeginEndRule(
582 desc->tmlLocation,
583 ruleId,
584 desc->name,
585 desc->contentName,
586 *desc->begin,
587 _compileCaptures(desc->beginCaptures ? desc->beginCaptures : desc->captures, helper, repository),
588 desc->end ? *desc->end : "",
589 _compileCaptures(desc->endCaptures ? desc->endCaptures : desc->captures, helper, repository),
590 desc->applyEndPatternLast ? *desc->applyEndPatternLast : false,
591 _compilePatterns(desc->patterns, helper, repository)
592 );
593 }
594
595 // ✅ PHASE 2 FIX: Actually store the rule in the registry!
596 if (rule) {
597 helper->setRule(ruleId, rule);
598 }
599
600 return ruleId;
601}
602
603std::vector<CaptureRule*> RuleFactory::_compileCaptures(IRawCaptures* captures,
604 IRuleFactoryHelper* helper,
605 IRawRepository* repository) {
606 std::vector<CaptureRule*> result;
607
608 if (!captures) {
609 return result;
610 }
611
612 // Find maximum capture id
613 int maximumCaptureId = 0;
614 for (const auto& pair : captures->captures) {
615 int numericCaptureId = std::stoi(pair.first);
616 if (numericCaptureId > maximumCaptureId) {
617 maximumCaptureId = numericCaptureId;
618 }
619 }
620
621 // Initialize result
622 result.resize(maximumCaptureId + 1, nullptr);
623
624 // Fill out result
625 for (const auto& pair : captures->captures) {
626 int numericCaptureId = std::stoi(pair.first);
627 RuleId retokenizeCapturedWithRuleId = ruleIdFromNumber(0);
628
629 if (pair.second->patterns) {
630 retokenizeCapturedWithRuleId = getCompiledRuleId(pair.second, helper, repository);
631 }
632
633 // Use createCaptureRule to properly register the capture rule
634 Rule* captureRule = createCaptureRule(
635 helper,
636 pair.second->tmlLocation,
637 pair.second->name,
638 pair.second->contentName,
639 retokenizeCapturedWithRuleId
640 );
641 result[numericCaptureId] = dynamic_cast<CaptureRule*>(captureRule);
642 }
643
644 return result;
645}
646
647ICompilePatternsResult RuleFactory::_compilePatterns(std::vector<IRawRule*>* patterns,
648 IRuleFactoryHelper* helper,
649 IRawRepository* repository) {
650 ICompilePatternsResult result;
651
652 if (!patterns) {
653 return result;
654 }
655
656 for (auto* pattern : *patterns) {
657 RuleId ruleId = ruleIdFromNumber(-1);
658
659 if (pattern->include) {
660 IncludeReference reference = parseInclude(*pattern->include);
661
662 switch (reference.kind) {
663 case IncludeReferenceKind::Base:
664 case IncludeReferenceKind::Self: {
665 // Look up in repository using the include string
666 IRawRule* repoRule = repository->getRule(*pattern->include);
667 if (repoRule) {
668 ruleId = getCompiledRuleId(repoRule, helper, repository);
669 }
670 break;
671 }
672
673 case IncludeReferenceKind::RelativeReference: {
674 // Local include found in `repository` (e.g., #ruleName)
675 IRawRule* localIncludedRule = repository->getRule(reference.ruleName);
676 if (localIncludedRule) {
677 ruleId = getCompiledRuleId(localIncludedRule, helper, repository);
678 }
679 break;
680 }
681
682 case IncludeReferenceKind::TopLevelReference:
683 case IncludeReferenceKind::TopLevelRepositoryReference: {
684 const std::string& externalGrammarName = reference.scopeName;
685 const std::string* externalGrammarInclude =
686 (reference.kind == IncludeReferenceKind::TopLevelRepositoryReference)
687 ? &reference.ruleName
688 : nullptr;
689
690 // External include - get the external grammar
691 IRawGrammar* externalGrammar = helper->getExternalGrammar(externalGrammarName, repository);
692
693 if (externalGrammar) {
694 if (externalGrammarInclude) {
695 IRawRule* externalIncludedRule = externalGrammar->repository->getRule(*externalGrammarInclude);
696 if (externalIncludedRule) {
697 ruleId = getCompiledRuleId(externalIncludedRule, helper, externalGrammar->repository);
698 }
699 } else {
700 // Reference to top-level rule
701 IRawRule* selfRule = externalGrammar->repository->getRule("$self");
702 if (selfRule) {
703 ruleId = getCompiledRuleId(selfRule, helper, externalGrammar->repository);
704 }
705 }
706 }
707 break;
708 }
709 }
710 } else {
711 ruleId = getCompiledRuleId(pattern, helper, repository);
712 }
713
714 if (ruleIdToNumber(ruleId) != -1) {
715 Rule* rule = helper->getRule(ruleId);
716
717 bool skipRule = false;
718
719 // Check if rule should be skipped (has missing patterns and no actual patterns)
720 IncludeOnlyRule* includeOnlyRule = dynamic_cast<IncludeOnlyRule*>(rule);
721 BeginEndRule* beginEndRule = dynamic_cast<BeginEndRule*>(rule);
722 BeginWhileRule* beginWhileRule = dynamic_cast<BeginWhileRule*>(rule);
723
724 if (includeOnlyRule) {
725 if (includeOnlyRule->hasMissingPatterns && includeOnlyRule->patterns.empty()) {
726 skipRule = true;
727 }
728 } else if (beginEndRule) {
729 if (beginEndRule->hasMissingPatterns && beginEndRule->patterns.empty()) {
730 skipRule = true;
731 }
732 } else if (beginWhileRule) {
733 if (beginWhileRule->hasMissingPatterns && beginWhileRule->patterns.empty()) {
734 skipRule = true;
735 }
736 }
737
738 if (!skipRule) {
739 result.patterns.push_back(ruleId);
740 }
741 }
742 }
743
744 result.hasMissingPatterns = (patterns->size() != result.patterns.size());
745 return result;
746}
747
748} // namespace tml
RuleId ruleIdFromNumber(int id)
Convert an integer to a RuleId.
Definition types.h:109
const RuleId END_RULE_ID(-1)
Special rule ID indicating the end of a matched region.
int ruleIdToNumber(RuleId id)
Convert a RuleId to its integer value.
Definition types.h:116
const RuleId WHILE_RULE_ID(-2)
Special rule ID for 'while' rule matching.