Как я перенёс сборку лексера в compile-time

Stefanio 2 часа назад Как я перенёс сборку лексера в compile-time Простой 7 мин 1.4K Компиляторы * Программирование * Open source * C# * .NET * Туториал Я написал source generator для лексера HydraScript. Он собирал...
<5 — 2026'da uzaya kaç SpaceX Starship fırlatması ulaşacak?
Вот важная новость с фронта ИИ: Stefanio 2 часа назад Как я перенёс сборку лексера в compile-time Простой 7 мин 1. 4K Компиляторы * Программирование * Open source * C# * . NET * Туториал Я написал source generator для лексера HydraScript.
Он собирал регулярное выражение из описаний токенов. После этого мне оставалось скопировать регулярку и руками вставить её в другой файл. Даже тест на забытое копирование пришлось завести.
Технические детали
Довольно много усилий, чтобы поддерживать одну строку в актуальном состоянии. Проблема была в том, что результат моего генератора требовался другому генератору — тому, который стоит за атрибутом в . Хотелось описать токен один раз и поручить остальное сборке.
Для этого пришлось разобраться, в каком порядке Roslyn вообще выполняет генераторы. Как устроен лексический анализ HydraScriptМой лексер нарезает исходный код на токены с помощью одного регулярного выражения. Определения лежат в TokenTypes.
Stream:public record struct Dto( string Tag, string Pattern, int Priority, bool CanIgnore = false); С большинством полей вопросов не возникает. А вот за Priority прячется возможность сломать язык довольно безобидным изменением. Возьмём такую программу:let x = 12.
Отраслевые последствия
5 x += 1 >>> x >= 13 Начало 12. 5 подходит как под число с дробью, так и последовательность “целое-точка-целое”. Если раньше сработает соответствующая альтернатива, лексер заберёт 12 и оставит точку следующему токену.
По той же причине += нужно сохранить целиком, а оператору вывода >>> дать шанс до того, как правило сравнения заберёт первый >. Числовые литералы описаны так:yield return new( Tag: "IntegerLiteral", Pattern: "+", Priority: 3); yield return new( Tag: "FloatLiteral", Pattern: "++", Priority: 2); Дробное число стоит позже в файле, но раньше в регулярке. Я храню этот порядок рядом с определениями, потому что это правило языка: паттерн + сам не догадается, что 12.
5 нужно оставить в покое. Здесь всё держится на порядке альтернатив регулярного выражения. Отдельного алгоритма maximal munch, который сравнивает всех кандидатов и выбирает самое длинное совпадение, нет.
Этот прогресс даёт важные сигналы о будущем отрасли, и технологический мир внимательно наблюдает.




