Регулярное выражение для Лексера.

Post Reply
zqg43035
Posts: 3
Joined: 28.01.2019 20:44

Регулярное выражение для Лексера.

Post by zqg43035 »

Я не так давно описывал, что создал и дорабатываю тяжелый самодельный лексер для ECMA6.
Функционал стандартных доступных лексеров для JS мне показался слабо выраженным.
Потому я решил создать структурно-типизированый лексер.
Под структурной типизацией я подразумеваю, определение не только типов данных и ключевых слов,
но и градацию этих данных и слов по областям применения в коде.
На текущий момент уже определены все доступные Объекты для самой спецификации, а так же все доступные объекты для Клиентских DOM, BOM.
Это же касается и всех доступных методов и пропсов. Так же проработаны отображения в древе всех объявлений переменных, функций (как явных так и анонимных), классов и т.д.
Планирую внести еще подсветку для конструкций REACT и несколько других фреймворков.

Столкнулся с проблемой, из-за которой не могу доработать Шаблоны и части фреймворков.
Суть проблемы - правильная подсветка сложных шаблонов.
Сейчас приведу пример, решение которого позволит закончить работу над лексером.
Инструкции и рекомендации все прочитаны (даже EControl в разрезе этого вопроса поднят).
Просто не хватает креативности для создания регулярки. С чем и прошу помочь.


В новых спецификациях ecma есть новый вид строки через обратные кавычки `str` с применением шаблонов по типу ${value}
Вот пример разбираемого текста:

Code: Select all

class SomeClass1 {'some_prop : some_value'};
let problem_place =  `problem_text`;

` solo_text `

` left_text ${left_inside} centr_text ${right_inside} right_text`

` start_text ${top_inside}
  midle_text ${bottom_inside}
  end_text`

class SomeClass2 {'some_prop : some_value'};
let problem_place =  `problem_text`;
В этом примере нужно подсветить только текстовую составляющую , а значения паттерна ${value} (не сам паттерн а только _inside части), подсвечивать остальными правилами лексера.

Если я правильно понимаю работу парсера лексеров, то подсветку можно задавать двумя способами:
1. Применить стиль на парсер строки (закладка Парсер).
2. Применить стиль на Шаблон правил (закладка Правила).
Причем следующий парсер или правило переназначает стиль, что был наложен предыдущим Парсером (правилом).
Если использовать простой парсер / (?s)(`) (.)*? (\1|\Z) / для выделения всего блока текста,
то он перезапишет подсветку паттерна ${value} и все в пределах `` будет засвечно одним стилем.
и наоборот, если поставить парсер строки приоритетом ниже то паттерн ${value} перезапишет парсер строки,
что приведет к тому, что покрашен будет только само ${value} , а текст вокруг потеряет подсветку и станет цветом по умолчанию.

Значит, что бы внутренние значения шаблонов могли подсвечиваться по ECMA,
нам нужно составить такое регулярное выражение, что бы строка парсилась
от знака ` до сочетания ${ и продолжала краситься после } и аж до закрывающего `
Получается у нас есть 4 определения для регулярного выражения:

Code: Select all

1)  ` txt `   == /(`)  (.*?) (`)/
2)  ` txt ${  == /(`)  (.*?) (\$\{)/
3)  } txt `   == /(\}) (.*?) (`)/
4)  } txt ${  == /(\}) (.*?) (\$\{)/
видно что у нас есть по два открывающих и два закрывающих определения, их можно сгруппировать
а так же для мульти-строк добавим ключ (?s) что бы точка искала и переводы строк.

Code: Select all

(?s)
( (`) | (\}) )
(.*?) 
( (\$\{) | (`) )
Такой шаблон работает хорошо, но из-за (\}) текст начинает парситься также после определения классов (в примере это "problem_place = `problem_text`;" ). Так как в классе есть этот же символ.
значит для открывающего (\}) определения нужно подставить (?<=) проверку на принадлежность знака } к паттерну ${value}
В проверку вставляем начало паттерна и получается (?<=\$\{.*?) (\})

Code: Select all

(?s)
( (`) | (?<=\$\{.*)\} )
(.*?) 
( (\$\{) | (`) )
Отлично, теперь после SomeClass1 текст не парсится, но вот после SomeClass2 текст начинает парситься, так как из-за ключа (?s) , парсер в тексте находит паттерн ${multiline_bottom} и правило срабатывает в TRUE и подсвечивает стилем код, идущий после ...some_value'}. Корень проблемы в том, что мы используем (.*?) для определения текста. Это необходимо потому, что внутри ${value} могут быть не только название переменной, но так же вычисляемые выражения с разными знаками и переводами строки.

Вот это и есть проблема.
Помогите додумать регулярку.
Либо если есть возможность, дать ссылку на Лексер, с решением этой проблемы через Правила.
Я уже 4 дня шаманю, не получается никак. Пробовал уже костыль делать, СабЛексер от ` до ` но там свои проблемы и нюансы.
Решение этой проблемы позволит применить аналогию для решения Шаблонов для остальных фреймворков.
п.с. В идеале нужно ставить еще проверку на весь патерн перед закрывающим ` и явно указывать пробельные символы до и после фигурных скобок, но я решил свести пример к минимальному значению.
zqg43035
Posts: 3
Joined: 28.01.2019 20:44

Post by zqg43035 »

Для полной проверки приведу код со всеми возможными вариантами.

Code: Select all

 
 yyy = {aaa};
`zzz `
`zzz ${aaa.bbb} 	  zzz ${aaa.bbb }zzz
 zzz ${aaa.bbb+bbb }.zzz ${aaa.bbb}.zzz 
 zzz ${aaa.bbb[ccc]}=zzz 
 zzz ${aaa.bbb['c']}:zzz
 zzz ${aaa('inner')} zzz
 zzz ${{"foo: boo,
         boo: foo"}} zzz 
 zzz ` 
 yyy = {aaa};
 yyy = 'aaa';
`zzz z zzz`
Каждая строка представляет отдельную ситуацию.
Парсер справится с задачей ЕСЛИ сможет найти и покрасить только строки где есть zzz при этом игнорировать паттерн / (\$\{.*?\}\s*) /
Самая проблемная ситуация это ${{"foo: boo"}} с двумя } на конце.
Это вторая проблема, с которой я столкнулся при регулярных выражениях в лексере.
Как правильно составить шаблон для ${ .. {...} .. } да еще и мультистрочный,
и как прикрутить его к обычному шаблону ${ ... }
что бы парсер мог понимать когда у шаблона есть несколько пар скобок.

Буду благодарен любой помощи.
Alexey
Posts: 1633
Joined: 05.10.2012 22:10

Post by Alexey »

слишком длинно. еще не прочитал.
но понятно что вы хотите подсветить внутри ${...} но и внутри Тик-символов тоже хотите.

есть 3 способа.
а. использовать плагин CudaText - Hilite Vars. он есть костыль к тому чтобы светить что-то внутри токена (токен это все от Тика до Тика `...` )

б. задать сублексер для того что внутри тиков. сублексер делать от тика до тика.
в. задать в вкладке Rules диапазон от тика до тика и дать ему стиль - в стиле надо поставить Background Only color. это не заменит покраску того что внутри тиков - оно покрасится само а новый Rule покрасит все внутри тиков _фоном_. так уже умеет делать лексер Bash для $(..) или ${...}.
Alexey
Posts: 1633
Joined: 05.10.2012 22:10

Post by Alexey »

и еще хинт для вашего способа (поиск 4 регулярками). при открывании тика, поставьте parser state = 1. при закрывании тика, убирайте state=1. для правил середины, проверяйте что есть state=1.
states описаны в одном из доков readme/tutorials/*.
zqg43035
Posts: 3
Joined: 28.01.2019 20:44

Post by zqg43035 »

Кому то будет полезно.
Таки смог разобрать как правильно составить выражение.
Получилось три ответа.

Красим включительно с внешними операторами ${}

Code: Select all

((?<=\$\{(.)*?)\}+|`)
(\r|\n|.)*?
((\$[\{\s]+)(?=(.)*\})|`)
Красим исключая знаки, что бы можно было ${ .. } парсить в древо навигации

Code: Select all

((?<=\$\{.*?\}+\s*)|`)
(?!\}+)(\r|\n|.)*?
((?=\$\{.*?\}+\s*)|`)
У предыдущих методов есть недостаток - внутри шаблонов VALUE объектов нужно печатать inline. Случается multiline очень редко, но все же - недостаток его не использовать.

Оптимальное Решение - через state=
Получилось Три Парсера, используем биты номер 1 и 2 (что бы в итоге выключить их оба суммой) :

Code: Select all

(?s)(`)(.*?)((`)|(\$[\{\s]*))
state= 1+

Code: Select all

state= 1?
(?s)(\}{1}?((.)|[^`]*?)(\$[\{\s]*))
state= 2+

Code: Select all

state= 2?
(?s)\}(.*?)(`)
state= 3-
Все работает, ничего не виснет, нигде лишней подсветки кода нет.
.
Alexey
Posts: 1633
Joined: 05.10.2012 22:10

Post by Alexey »

Для code tree можно и так-
- убрать все из лексера (чистое древо)
- написать TreeHelper (дока == readme from CudaTree plugin)
Alexey
Posts: 1633
Joined: 05.10.2012 22:10

Post by Alexey »

Если все доделано, хочу выложить в Addon manager.
- нужно красивое имя лекса- правильное имя ECMAScript в таком регистре
- нужно ваше имя и контакт в Notes вкладке
Alexey
Posts: 1633
Joined: 05.10.2012 22:10

Post by Alexey »

Вот вам работы на неделю- проверять баги JS lexer in Atom
https://github.com/atom/language-javascript/issues

67 открытых
250 закрытых

я у себя повторил-поправил много.
Developer
Posts: 23
Joined: 13.08.2018 13:14

Post by Developer »

Прошу прощения,
довольно долго не мог заняться лексером, не было возможности.
Лексер практически готов, осталось только убрать лишнее из него, что бы он был минимальным и без мусора.
Но прежде чем приступать к работе, нужно решить одну большую проблему с редактором.
БОльшая часть лексера попросту не будет работать из-за функций динамических подсветок.
Я проделал целое исследование в области восприятия цветов.
Подобрал нужные палитры и выделения разных цветов для различных типов данных, таким образом,
что бы в коде было наименьшее количество слитных цветовых комбинаций.
И к сожалению часть функционала в КудаТекст не доведена до конца.
По этой причине, приходиться по прежнему работать через СинВрайт.

Проблему описал тут
viewtopic.php?f=6&t=2278
Без решения этой проблемы, лексер практически ничем не отличается от идущего в комплекте.
Когда победим проблему с динамическими подсветками то, скорее всего, создам у нас на форуме тему,
где подробно опишу о чем лексер.
Attachments
JS_ES6
JS_ES6
Alexey
Posts: 1633
Joined: 05.10.2012 22:10

Post by Alexey »

кажется, проблему уже решили.
Post Reply