Возможны ли языки программирования с подсветкой синтаксиса с использованием регулярных выражений? - PullRequest
11 голосов
/ 31 марта 2012

Мы все уже знаем, что синтаксический анализ HTML с использованием регулярных выражений вообще невозможен, так как это будет синтаксический анализ контекстно-зависимой грамматики, тогда как регулярные выражения могут анализировать только регулярные грамматики. То же самое верно и для других языков программирования.

Теперь, недавно, было объявлено о выделении синтаксиса Rainbow.js . Его предпосылка описана как очень простая:

Радуга сама по себе очень проста. Он проходит через блоки кода, обрабатывает шаблоны регулярных выражений и упаковывает соответствующие шаблоны в теги.

Я подумал, что подсветка синтаксиса - это, по сути, задача такой же сложности, что и синтаксический анализ языка, если мы предположим, что она должна быть и хорошей, и подходящей для многих языков. Тем не менее, несмотря на то, что есть критика этой библиотеки, ни в этом, ни в обсуждении HackerNews (взятом в качестве примера для обсуждения технически склонными) не упоминается, что выделение синтаксиса использование регулярных выражений в общем случае невозможно в общем случае, который я бы назвал серьезным недостатком, вызывающим остановку шоу.

Теперь вопрос: что-то мне не хватает? В частности:

  1. Возможно ли вообще подсветка синтаксиса с помощью регулярных выражений?
  2. Является ли это примером применяемого правила 80/20, где достаточно просто регулярных выражений, чтобы быть полезным?

Ответы [ 4 ]

13 голосов
/ 31 марта 2012

Подсветка синтаксиса с использованием регулярных выражений является древним искусством. Я думаю, что даже Emacs и vi начали этот путь.

Я полагал, что подсветка синтаксиса, по сути, такая же сложность, как и синтаксический анализ языка, [...]

Нет. Разница в том, что компилятору нужен настоящий анализ, потому что он должен понять всю программу и также должен генерировать вещи из этого понимания. Подсветка синтаксиса с другой стороны не требует понимания кода. Нужно просто понять общую структуру языка - что такое строковые литералы - что такое ключевые слова ... и так далее. Побочным эффектом этого различия является то, что вы можете выделить код, который синтаксически неверен, но вы не можете его проанализировать.

Немного другой подход к этому: синтаксический анализ языка часто представляет собой двухэтапный процесс: лексирование (разбиение потока байтов на поток «токенов») и реальный синтаксический анализ (перевод потока токенов в некоторую сложную структуру - часто Абстрактное синтаксическое дерево). Лексирование обычно выполняется с использованием ---- регулярных выражений. Посмотрите гибкие документы для этого. И это в основном все, что нужно понять базовому инструменту подсветки синтаксиса.

Конечно, есть угловые случаи, которые одно регулярное выражение не может поймать. Типичный пример:

foo(bla, bar);

Здесь foo может быть вызовом статического метода или метода экземпляра, или макроса, или чего-то еще. Но ваш маркер регулярных выражений не может вывести это. Он может добавлять цвета только для «общего вызова».

Итак: это правило на 100/0 процентов, если ваши требования низкоуровневые (т.е. без приведенного выше примера), и обычно правило 90/10 для реальных вещей.

3 голосов
/ 31 марта 2012

Вы можете сделать подсветку синтаксиса с помощью регулярных выражений как часть решения.Более конкретно, как часть «лексера», который разбивает входной текст на символы.Это на самом деле так, как работает большинство компиляторов / интерпретаторов.

Чтобы сделать это, используя только регулярное выражение , все же возникает проблема.Рассмотрим случай соответствия строки в Python.Python позволяет разделять строки либо одинарными кавычками ', либо двойными кавычками ".Кроме того, он допускает многострочные строки («синтаксис heredoc») с использованием тройных кавычек, ''' или """.

Итак, какие части следующего являются строками, а какие нет?Можете ли вы создать регулярное выражение, которое правильно идентифицирует строковые литералы str1 - str6?

str1 = "hello, world!"

str2 = 'hello, world!'

str3 = "The canonical test program is 'Hello World'."

str4 = '"Why," Peter said, "That\'s ludicrous. Who would do that?"'

str5 = """The heredoc syntax is handy for cases where you don't want to escape strings. "Very convenient."
"""

str6 = """Code sample:
s1 = "hi!"
s2 = 'Hi!'
S3 = '''
- apples
- oranges
- bananas
'''
"""

Аргумент, что «вы не можете (анализировать программы HTML | процессов) с регулярным выражением, потому что (HTML |языки программирования) имеют вложенные структуры - они не регулярные", не совсем верно - современные регулярные выражения (особенно в Perl) обладают большей выразительной силой, чем строго регулярные выражения в компьютереСмысл науки.Но то, что вы можете использовать регулярные выражения , не означает, что вы должны .


Редактировать: приведенная выше проблема с совпадением строк не так уж и плоха, еслиВаш регулярный выражение поддерживает обратные ссылки в шаблоне поиска.Многострочное регулярное выражение, подобное ('|"|'''|""").+?\1, вероятно, подойдет.


Редактировать 2: В качестве примера угловых случаев подсветки синтаксиса смотрите не более, чем в синтаксической подсветке StackOverflow приведенного выше кода.

2 голосов
/ 31 марта 2012

В основном, нет.

Вам нужен анализатор / токенизатор, который понимает язык, чтобы выбрать, какие биты выделять.

Регекс не режет горчицу для такой задачи.

1 голос
/ 31 марта 2012

Хороший пример, на который стоит обратить внимание, - реализация подсветки синтаксиса в Vim.Он использует шаблоны, основанные на регулярных выражениях.Однако шаблоны используются для распознавания иерархических структур содержания в документе, а не просто для токенизации входных данных.

Вы можете объявить области, которые начинаются и заканчиваются при сопоставлении с шаблоном регулярных выражений (плюс другой шаблон, который помогает пропускатьсредний материал).Эти регионы могут объявить, что они содержат другие регионы или простые шаблоны.Сдерживание может быть рекурсивным.Vim все это работает.Таким образом, это по существу форма синтаксического анализа без контекста.

Этот подход может работать с языками, которые имеют различные уровни встраивания, с различными лексическими свойствами.

Например, у меня есть язык, на которомПо сути, существует два набора ключевых слов (из-за происходящего встраивания языка домена).Написанные мной правила подсветки синтаксиса Vim правильно распознают контекст и по-разному раскрашивают ключевые слова.Обратите внимание, что существует несколько совпадений между этими наборами ключевых слов: одно и то же слово, другое значение в другом контексте.

Пример этого см. В http://www.kylheku.com/cgit/txr/tree/genman.txr. Если вы ищете синтаксис (doвы обнаружите, что один экземпляр окрашен в фиолетовый цвет, а другой - в зеленый.Они разные: один на языке извлечения текста, а другой на встроенном диалекте Лиспа.Подсветка синтаксиса Vim достаточно мощна, чтобы обрабатывать смесь языков с разными наборами ключевых слов.(Да, хотя это происходит через Интернет, на самом деле это процесс Vim, выполняющий подсветку синтаксиса.)

Или рассмотрите что-то вроде оболочки, где вы можете иметь синтаксис строкового литерала типа, например "foo bar",но внутри вы можете использовать подстановку команд, внутри которой вы должны рекурсивно распознать и раскрасить синтаксис оболочки: "foo $(for x in *; do ...; done) bar".

Так что нет, вы не можете делать полезную и точную подсветку синтаксиса только с помощью токенизации регулярных выражений, но регулярные выражения с иерархическим разбором могут хорошо работать.

...