Внутренний мир Razor. Часть 1–Рекурсивный пинг-понг
Это первая статья о новом ASP.NET парсере – Razor. Над которым мы работали достаточно долго, и я хотел бы рассказать читателям, как же он работает.
Razor-парсер сильно отличается от существующего ASPX-парсера. Фактически ASPX-парсер, почти полностью, построен на регулярных выражениях, потому что синтаксис достаточно простой для разбора. Razor-парсер же разделен на три компонента:
- Парсер разметки, который имеет базовое представление о HTML-синтаксисе.
- Парсер кода, который имеет базовое представление C# или VB.
- И главный “дирижер”, которые знает, как соединить два парсера вместе.
Когда я говорю “базовое представление” я подразумеваю именно основы, мы не говори о полностью самостоятельном C# и HTML парсере. У себя в команде мы шутим, называя их “Опознователь разметки” и “Осмыслитель кода” :)
Итого на сцене Razor играет три “актера”: Парсер ядра, Парсер разметки и Парсер кода. Все трое работают вместе, чтобы разобрать документ Razor. Теперь давайте возьмем файл Razor и проведем полный обзор процедуры парсинга с использованием данных актеров. Использовать будем следующий пример:
<ul>@foreach(var p in Model.Products) { <li>@p.Name ($@p.Price)</li> <br> } </ul>
Итак начнем сверху. Фактически парсер Razor находится в одном из состояний в любой момент парсинга: разбор разметки документа, разбор разметки блока или разбор блока кода. Первые два обрабатываются парсером разметки, а последний парсером кода. Когда парсер ядра запускается первый раз, он вызывает парсер разметки и просит его разобрать разметку документа и вернуть результат. Сейчас парсер находится в состоянии разбора разметки документа. При таком состоянии, он просто ищет символ “@”, ему не важно какие теги ему попадаются и все, что касается HTML, главная цель – “@”. Когда же он нашел @, то решает – это переключение на код или email-адрес? Данное решение основывается на символах до и после @, проверяя на валидность email-адрес. Это всего лишь стандартная процедура, присутствует последовательность проверок, чтобы произвести переключение на режим кода.
В данном случае, когда мы видим первый символ “@”, ему предшествует пробел, что не валидно для email-адреса. Так что мы точно знаем,что нужно переключится на режим кода. Парсер разметки вызывает внутри парсер кода и просит разобрать блок кода. Блок, в определении парсера Razor, в основном является единым куском кода или разметки с четким началом и завершением. Так что “foreach” в нашем случае является примером блока кода. Он начинается с символа “f” и заканчивается “}”. Парсер кода знает достаточно о С#, что бы понять это, соответственно он начинает разбор кода. Парсер кода проделывает некоторое простое отслеживание C# операторов, так что когда он доберется до “
- HtmlMarkupParser.ParseDocument()
- CSharpCodeParser.ParseBlock()
- HtmlMarkupParser.ParseBlock()
- CSharpCodeParser.ParseBlock()
- HtmlMarkupParser.ParseBlock()
- CSharpCodeParser.ParseBlock()
(Понятное дело, я исключил из списка множество вспомогательных методов :).
Это проливает свет на фундаментальное отличие между ASPX и Razor. В ASPX файлах, вы можете думать о коде и разметке, как о двух параллельных потоках. Вы пишете разметку, потом перепрыгиваете и пишете код, потом обратно возвращаетесь и пишете разметку и т.д. Razor же файлы, как дерево. Вы пишете разметку, далее вкладываете в нее код, далее помещаете разметку в код и т.д.
Итак мы только что вызвали парсер разметки для разбора блока разметки, блок начинается с “
Во время разбора “
- HtmlMarkupParser.ParseDocument()
- CSharpCodeParser.ParseBlock()
- HtmlMarkupParser.ParseBlock()
- CSharpCodeParser.ParseBlock()
- HtmlMarkupParser.ParseBlock()
- CSharpCodeParser.ParseBlock()
Я углублюсь в детали обработки блоков позже, потому что процесс немного сложен, в итоге мы закончили с данными блоками кодами и вернулись в блок “
Я надеюсь общая структура алгоритма разбора ясна. Главное – это перестать думать, что парсер кода и разметки работают в отдельных потоках, а вместо этого конструкции располагаются одна в другой. Намекну, вдохновние мы черпали из PowerShell ;).
Источник – VibrantCode