Здравствуйте, Marty, Вы писали:
M>Это вообще не проблема. Цепочка символов же одна? Значит, и айди будет один, просто в зависимости от контекста по разному будет обрабатываться.
Если в одном контексте a --i означает "вызвать функцию a с аргументом декремент от i", а в другом — "вычесть результат применения унарного минуса к i из a", то лексер не может оперировать лексемой "minusminus".
Он должен порождать две лексемы minus. Это делает смысл лексера эфемерным, потому что нафига он такой нужен? Проще сразу писать парсер.
Композитная лексема — просто очередной нетерминал. Прекрасно работает для контекстно-специфичных ключевых слов. Те же get и set в C#, которые являются ключевыми словами исключительно внутри property definition.
M>У меня, кстати, есть обратная связь, я её обдумывал, когда вспоминал о проблеме >> vs > и > в плюсовых шаблонах.
Я верю, что вы придумали, что с этим делать. С моей точки зрения, это ненужная сложность. Вот вам лексер зачем, собственно, нужен?
M>У меня задумывается как универсальное средство: 1) для разбора сорцов и построения AST, с выдачей диагностических сообщений с указанием места ошибки/предупреждения — в этом варианте доп нагрузку на токенах можно дропать, когда она больше не требуется; 2) хочется иметь возможность репарсинга с определенной позиции, для реализации хотя бы подсветки синтаксиса в редакторе
Ну вот PEG как раз репарсинг прекрасно автоматизирует, т.к. в нём не нужно думать о том, как получить корректный поток лексем для нового представления. Там всего один кэш с простой стратегией вытеснения.
Идеального решения я пока так и не нашёл, но PEG ближе всего к тому, что мне нужно. Судя по всему, я в ближайшем будущем посажу дипломников пилить нормальный фреймворк, а то, скажем, инкрементальный ohm-js сделан крайне странно в плане семантических действий; и в нём нет error autorecovery. При этом статьи про доработку PEG для восстановления после неудачного парсинга есть — надо просто перенести их к продакшн.
С подсветкой синтаксиса всё нетривиально.
Та же VS Code сдалась; в ней красить синтаксис парсером считается неэффективным. Применяют двухуровневую раскраску — основа красится движком на основе регекспов, а парсеру оставляют семантическую раскраску, которой должно быть мало по сравнению с синтаксической. Типа там покрасить в жёлтый переменные функционального типа, затенить константы или недостижимый код, и прочие мелочи.
Но это заставляет решать задачу разбора минимум дважды — один раз грамматикой, второй раз регекспами; особенно бесит при работе над нестабилизированным языком, где синтаксис, грамматика и семантика правятся по нескольку раз в неделю.
А по-другому получается плохо, т.к. проблема курицы и яйца. Чтобы понять, насколько проектируемый язык удобен и хорош, нужно написать на нём приличный корпус текстов. Писать тексты без IDE Assistance крайне утомительно.
Поэтому примеров выходит мало, и на их основе трудно сделать какие-то выводы.
В идеале хочется иметь возможность в наскетчить язык в несколько десятков строчек, и сразу получить все плюшки вроде онлайн диагностики и раскраски синтаксиса; и при этом ещё иметь возможность резко сказать "ой, что-то у нас фигня выходит, давайте теперь переходы будут не просто "номер метки", а goto <labelName>, а labelName — это не номер, а строка, начинающаяся с $. И получить новый extension сразу после перекомпиляции, а не после трёхдневного переписывания лексера, парсера, и textMate раскрасчика синтаксиса.
Как устроена подсветка в настоящей VS, я не в курсе — но явно лучше, чем в VS Code, т.к. последняя красит тот же C# значительно хуже.
Подозреваю, что там какой-то хардкор, трудновоспроизводимый в домашних условиях.
M>Как минимум, номер строки тоже надо хранить
Это всё легко пересчитывать туда-обратно по мере необходимости. Хранить ли позицию в виде "строка/колонка" или просто в виде индекса в строке — дело вкуса.
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Здравствуйте, Sinclair, Вы писали:
S>Здравствуйте, Marty, Вы писали:
M>>Это вообще не проблема. Цепочка символов же одна? Значит, и айди будет один, просто в зависимости от контекста по разному будет обрабатываться. S>Если в одном контексте a --i означает "вызвать функцию a с аргументом декремент от i", а в другом — "вычесть результат применения унарного минуса к i из a", то лексер не может оперировать лексемой "minusminus".
Это очень странный язык. Никто так не делает.
M>>У меня, кстати, есть обратная связь, я её обдумывал, когда вспоминал о проблеме >> vs > и > в плюсовых шаблонах. S>Я верю, что вы придумали, что с этим делать. С моей точки зрения, это ненужная сложность. Вот вам лексер зачем, собственно, нужен?
Сейчас просто синтаксис раскрашиваю, тоже нужная мне штука. Без особых проблем можно настроить под любой язык. Для PHP/Perl, и прочих, где есть такие штуки, как Document Here, надо немного доделать, но пока не нужно было
S>С подсветкой синтаксиса всё нетривиально. S>Та же VS Code сдалась; в ней красить синтаксис парсером считается неэффективным. Применяют двухуровневую раскраску — основа красится движком на основе регекспов, а парсеру оставляют семантическую раскраску, которой должно быть мало по сравнению с синтаксической. Типа там покрасить в жёлтый переменные функционального типа, затенить константы или недостижимый код, и прочие мелочи. S>Но это заставляет решать задачу разбора минимум дважды — один раз грамматикой, второй раз регекспами; особенно бесит при работе над нестабилизированным языком, где синтаксис, грамматика и семантика правятся по нескольку раз в неделю.
Не очень понятно, зачем там регеэксы. Я как-то для себя делал тулзу, которая выдирает комент /*! \file \brief bla-bla */ из плюсовых файлов — это доксиген-компатибл описание. Было на регэксах — жутко тормозило (да, я знаю, в плюсиках сейчас они тормозные), и выдирало кривовато, потому что не осилил совсем всё правильно сделать. Переделал через свой токенизатор, обрабатывают только коменты — летает, и выдирает то, что нужно.
По мелочам можно простенькие инструменты по-быстрому лепить.
S>В идеале хочется иметь возможность в наскетчить язык в несколько десятков строчек, и сразу получить все плюшки вроде онлайн диагностики и раскраски синтаксиса; и при этом ещё иметь возможность резко сказать "ой, что-то у нас фигня выходит, давайте теперь переходы будут не просто "номер метки", а goto <labelName>, а labelName — это не номер, а строка, начинающаяся с $. И получить новый extension сразу после перекомпиляции, а не после трёхдневного переписывания лексера, парсера, и textMate раскрасчика синтаксиса.
Ну, теоретически такое наверное осилю
M>>Как минимум, номер строки тоже надо хранить S>Это всё легко пересчитывать туда-обратно по мере необходимости. Хранить ли позицию в виде "строка/колонка" или просто в виде индекса в строке — дело вкуса.
Номер строки пересчитывать не слишком быстро, это надо с начала файла бежать каждый раз
Здравствуйте, Marty, Вы писали:
M>Сейчас просто синтаксис раскрашиваю, тоже нужная мне штука. Без особых проблем можно настроить под любой язык. Для PHP/Perl, и прочих, где есть такие штуки, как Document Here, надо немного доделать, но пока не нужно было
Лексером? Мало какой язык можно раскрасить лексером.
M>Не очень понятно, зачем там регеэксы. Я как-то для себя делал тулзу, которая выдирает комент /*! \file \brief bla-bla */ из плюсовых файлов — это доксиген-компатибл описание. Было на регэксах — жутко тормозило (да, я знаю, в плюсиках сейчас они тормозные), и выдирало кривовато, потому что не осилил совсем всё правильно сделать. Переделал через свой токенизатор, обрабатывают только коменты — летает, и выдирает то, что нужно. M>По мелочам можно простенькие инструменты по-быстрому лепить.
Регекспы там затем, что они работают быстрее, чем обращение по протоколу LSP.
M>Номер строки пересчитывать не слишком быстро, это надо с начала файла бежать каждый раз
Так никто не делает. При работе в IDE файл хранится в виде дерева строк, по нему преобразование — O(logN) в любую сторону.
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Здравствуйте, Marty, Вы писали: M>Это очень странный язык. Никто так не делает.
Контекстных историй — овердохрена. Вы же сами приводите пример с >>, и тут же пишете "никто так не делает".
Или, скажем, string interpolation. Токенизатор создаёт существенные проблемы, т.к. шаблон строки плохо укладывается в идею "тут у нас просто строковая константа".
var q = $@"Hello, {
getHelloTarget("simple")}";
Ну, ок, давайте скажем, что у нас тут токены VarKeyword, Identifier('q'), InterpolationStringFragment('Hello, '), а дальше-то что? Нельзя просто искать следующую }, потому что а) в коде выражения фигурные скобки вполне себе могут встречаться и б) она может вовсе не встретиться. Вот такой вот кусок кода должен быть токенизирован совершенно по другому:
var q = $@"Hello, {
getHelloTarget(";
Безлексерному парсеру — всё равно. Ему нет проблемы включить режим "разбираем ескейпы и прочую чухню", потом "разбираем expression", потом "а теперь снова ескейпы и ждём конца строки".
M>Ну, теоретически такое наверное осилю
Это было бы замечательно. На удивление, довольно мало production-ready вещей. Примерно все едят кактус. Вот, например, state-of-the-art у самих авторов концепции LSP: https://github.com/microsoft/tolerant-php-parser/blob/main/docs/HowItWorks.md
Читаем внимательно:
— Incremental parsing (Note: not yet implemented)
— Error Tokens: There are also some instances, where the aforementioned error handling wouldn't be appropriate, and special-casing based on certain heuristics, such as whitespace, would be required.
— Open Questions: Мы всё ещё не знаем, как нам правильно это пилить, присылайте свои идеи
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Здравствуйте, Sinclair, Вы писали:
M>>Сейчас просто синтаксис раскрашиваю, тоже нужная мне штука. Без особых проблем можно настроить под любой язык. Для PHP/Perl, и прочих, где есть такие штуки, как Document Here, надо немного доделать, но пока не нужно было S>Лексером? Мало какой язык можно раскрасить лексером.
Я плюсики раскрашиваю вроде норм.
Отвечая также на соседнее сообщение, скажу, что у меня наверное не совсем честный лексер. Так, у меня строковые символьные литералы парсятся отдельным классом, который надо "проинсталлить" в лексер. проблема с PHP/PERL в общем-то только с тем, что для DOCUMENT HERE надо написать отдельный парсер этого литерала, мне пока не до этого.
Далее, выхлоп токенизера отправляется в пользовательский обработчик, завернутый в std::function. Потом мне показалось этого мало, и я сделал возможность добавлять "фильтры" на выхлоп. Сильно переделывать уже не хотелось, и у меня немножко странно получилось — фильтры устанавливаются в голову, и получается, что раньше отрабатывают фильтры, установленные позже.
Ф фильтрах я при необходимости делаю небольшие автоматики, которые делают что-то полезное. Так, например, для числовых литералов, если за ними сразу, без каких-либо пробелов и чего-то другого, следует идентификатор — я это склеиваю, и получаю новомодные плюсовые пользовательские литерали, или как оно там называется. Или препроцессор. Сначала было всё в конечном обработчике, потом сделал фильтр, который умеет посылать токены PP_START/PP_END, по которым я устанавливаю другой цвет фонта при раскраске.
S>Регекспы там затем, что они работают быстрее, чем обращение по протоколу LSP.
Ну, вот тут не уверен, что это всегда так. В любом случае, мой токенизер гораздо быстрее работает.
M>>Номер строки пересчитывать не слишком быстро, это надо с начала файла бежать каждый раз S>Так никто не делает. При работе в IDE файл хранится в виде дерева строк, по нему преобразование — O(logN) в любую сторону.
Я пока с IDE не работаю, у меня текст в одной строке, и все ссылки на исходный текст идут как индекс в этой строке
ЗЫ Заметил баг — съедает закрывающий символ "*/" в многострочных коментариях
Здравствуйте, Marty, Вы писали: M>Я плюсики раскрашиваю вроде норм.
Скорее всего ключевое слово тут "вроде". В частности, лексером невозможно корректно раскрасить код с макросами, т.к. вплоть до раскрытия макроопределений неизвестно даже, является ли код частью юнита компиляции.
Ну, то есть вопрос в постановке задачи. Если хочется просто красить лексемы — то "кому и кобыла невеста".
А вот делать полноценную современную семантическую разметку — увы.
M>Отвечая также на соседнее сообщение, скажу, что у меня наверное не совсем честный лексер. Так, у меня строковые символьные литералы парсятся отдельным классом, который надо "проинсталлить" в лексер. проблема с PHP/PERL в общем-то только с тем, что для DOCUMENT HERE надо написать отдельный парсер этого литерала, мне пока не до этого.
Ну вот я вам и объясняю, что традиционное деление на лексер и парсер — это просто историческое заблуждение.
Вот у вас уже внутри лексера используется парсер, который, надо полагать, внутри использует лексер. Ну и нафига это всё, спрашивается, когда можно просто перестать себя обманывать и притворяться, что существует какой-то отдельный от "парсера" "токенизатор"?
M>Далее, выхлоп токенизера отправляется в пользовательский обработчик, завернутый в std::function. Потом мне показалось этого мало, и я сделал возможность добавлять "фильтры" на выхлоп. Сильно переделывать уже не хотелось, и у меня немножко странно получилось — фильтры устанавливаются в голову, и получается, что раньше отрабатывают фильтры, установленные позже.
Не, если задача — поразвлекаться на плюсах, то вы движетесь к успеху.
Проблема всех таких решений — write-only code, в котором совершенно невозможно понять, что и как разбирает результат всех этих применений фильтров к фильтрам и каскадов парсеров.
M>Ф фильтрах я при необходимости делаю небольшие автоматики, которые делают что-то полезное. Так, например, для числовых литералов, если за ними сразу, без каких-либо пробелов и чего-то другого, следует идентификатор — я это склеиваю, и получаю новомодные плюсовые пользовательские литерали, или как оно там называется. Или препроцессор. Сначала было всё в конечном обработчике, потом сделал фильтр, который умеет посылать токены PP_START/PP_END, по которым я устанавливаю другой цвет фонта при раскраске.
Во-во, об этом я и говорю. В теории вы даже можете сделать фильтр, который проверяет, попадает ли выхлоп IFDEF в компиляцию, или нет. Но зачем называть это токенизатором?
M>Вот так у меня выглядит создание токенизатора: http://files.rsdn.org/2511/cpp.html M>А так — использование: http://files.rsdn.org/2511/tokenizer_test_011.html
M>Эти файлы раскрашены этим тестом
Ну, если вы пишете токенизатор для конкретного языка — то да, можно делать примерно всё, что угодно. Всё равно это write-only код, который потом никто не будет проверять или поддерживать.
S>>Регекспы там затем, что они работают быстрее, чем обращение по протоколу LSP. M>Ну, вот тут не уверен, что это всегда так. В любом случае, мой токенизер гораздо быстрее работает.
Скорость работы токенизатора не очень важна, когда узким местом является передача его результатов в IDE.
M>Я пока с IDE не работаю, у меня текст в одной строке, и все ссылки на исходный текст идут как индекс в этой строке
Когда начнёте работать с инкрементальностью, неизбежно перейдёте на древовидное хранение. Аккурат потому, что O(N) операции вас задолбают.
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Здравствуйте, Sinclair, Вы писали:
M>>Я плюсики раскрашиваю вроде норм. S>Скорее всего ключевое слово тут "вроде". В частности, лексером невозможно корректно раскрасить код с макросами, т.к. вплоть до раскрытия макроопределений неизвестно даже, является ли код частью юнита компиляции.
Да. Но в большинстве случаев это не нужно. Например, у меня в перспективе маячит раскраска кода листингов в документации, того, который в маркдауне ограничен тремя бэктиками, а на кывте обрамляется тэгом code.
S>Ну, то есть вопрос в постановке задачи. Если хочется просто красить лексемы — то "кому и кобыла невеста".
Часто большее и не нужно
S>А вот делать полноценную современную семантическую разметку — увы.
Да, это не самое простое.
S>Вот у вас уже внутри лексера используется парсер, который, надо полагать, внутри использует лексер. Ну и нафига это всё, спрашивается, когда можно просто перестать себя обманывать и притворяться, что существует какой-то отдельный от "парсера" "токенизатор"?
Есть токенизатор, есть ручки, которые можно дергать. К токенизатору можно прикрутить надстройки, и да, он становится не совсем честным. И что?
Внутри мой токенизатор устроен как куча говнокода, там без тонких материй
Да, я до возможностей IDE сильно не дотягиваю, но у меня уже гораздо лучше, чем том же notepad++.
Когда я на говне запилил свой первый DSL, который локально хорошо взлетел (но не первый мой DSL вообще), то был вопрос, а где его редактировать, чтобы красиво. Получилось в NPPP и Кейле. В NPPP — через его настройки кстомной раскраски, в Кейле — через правки конфигов какого-то движка, который я перепутал с тем, что наша местная терраинформатика делает. И там и там всё было на пределе возможностей, приходилось извращаться. А сейчас у меня уже гораздо лучше.
S>Проблема всех таких решений — write-only code, в котором совершенно невозможно понять, что и как разбирает результат всех этих применений фильтров к фильтрам и каскадов парсеров.
Моё решение предлагает задание операторов оптом, задание различных типов скобок, и наборы фильтров. Всё это можно варьировать при настройке и получать результат лучше, чем в NPP. Уже годно для тупого лексера.
write-only code... Ну, хз...
Так-то, почти любое решение — дерьмо. С начала ты трескаешь то, что тебе дали разработчики решения, и тебя всё устраивает, и ты не паришься, что там под капотом. Потом ты упираешься в ограничения этого решения, но ты ничего не можешь изменить...
S>Во-во, об этом я и говорю. В теории вы даже можете сделать фильтр, который проверяет, попадает ли выхлоп IFDEF в компиляцию, или нет. Но зачем называть это токенизатором?
Это уже не совсем токенизатор, конечно, это доп фичи, на него навешанные. Последовательность установки фильтров и их настройки можно сохранять как конфиги, и это гораздо гибче, чем раскраска в NPP
S>Ну, если вы пишете токенизатор для конкретного языка — то да, можно делать примерно всё, что угодно.
Мой "токенизатор" писался с упором на то, чтобы можно было в первую очередь распарсить существующие языки. Что может придумать воспалённый мозг, я не знаю. Например, что a--b, это не полная хрень, а a минус b с унарным минусом. Если нужно такое — это не ко мне. У меня жадный алгоритм токенизации. И он выплюнет a, --, b, для примитивной раскраски этого достаточно, как в прочем, и для дальнейшего разбора.
S>Всё равно это write-only код, который потом никто не будет проверять или поддерживать.
Не очень понятно, что ты хотел сказать.
M>>Я пока с IDE не работаю, у меня текст в одной строке, и все ссылки на исходный текст идут как индекс в этой строке S>Когда начнёте работать с инкрементальностью, неизбежно перейдёте на древовидное хранение. Аккурат потому, что O(N) операции вас задолбают.
Мне до этого ещё далеко. Но не вижу особых проблем. Не уверен, что на каждое нажатие клавиши надо делать вставки в единственный глобальный string с текстом. На вскидку — будет глобальный текст, и список строк, которые ссылаются индексами на глобальный текст. Если пошло редактирование строки, то глобальный текст не перестраивается при каждом вводе, просто для строки выделяется отдельный буфер и строка маркируется признаком своего буфера. Потом вставляем, пробегаем и обновляем индексы после нашей строки, переделываем раскарску. Раскраску при вводе/удалении можно сдвигать/удалять по месту, на базе старой, без перескана, при паузах и в фоне можно всё перестраивать/пересканивать как угодно. В общем, что-то не вижу особых проблем. Мои варианты решений конечно далеки от дедовских, которые шуршали даже на XT, но, как минимум не хуже того, что сейчас в тренде.
Здравствуйте, Marty, Вы писали: M>Да. Но в большинстве случаев это не нужно. Например, у меня в перспективе маячит раскраска кода листингов в документации, того, который в маркдауне ограничен тремя бэктиками, а на кывте обрамляется тэгом code.
Ну, тогда я не совсем понимаю, чего вы хотите. В начале вы сослались на Гвидо, который объясняет причины, по которым он предпочёл бы переехать на PEG. Но PEG — это не лексер, это безлексерный парсер. M>Часто большее и не нужно
Я ж не спорю — если ваши планы заканчиваются на лексической раскраске, то ваше решение прекрасно. M>Есть токенизатор, есть ручки, которые можно дергать. К токенизатору можно прикрутить надстройки, и да, он становится не совсем честным. И что?
То, что теряется смысл выделять отдельно токенизатор, т.к. это затрудняет разработку полного решения. M>Да, я до возможностей IDE сильно не дотягиваю, но у меня уже гораздо лучше, чем том же notepad++. M>Когда я на говне запилил свой первый DSL, который локально хорошо взлетел (но не первый мой DSL вообще), то был вопрос, а где его редактировать, чтобы красиво. Получилось в NPPP и Кейле. В NPPP — через его настройки кстомной раскраски, в Кейле — через правки конфигов какого-то движка, который я перепутал с тем, что наша местная терраинформатика делает. И там и там всё было на пределе возможностей, приходилось извращаться. А сейчас у меня уже гораздо лучше.
Стандарт де-факто сейчас — это VS Code, а точнее — протокол LSP.
M>Моё решение предлагает задание операторов оптом, задание различных типов скобок, и наборы фильтров. Всё это можно варьировать при настройке и получать результат лучше, чем в NPP. Уже годно для тупого лексера.
Ну так и замечательно. Просто если вы хотите что-то куда-то развивать, то тупой лексер, с моей точки зрения — тупик.
M>Мой "токенизатор" писался с упором на то, чтобы можно было в первую очередь распарсить существующие языки. Что может придумать воспалённый мозг, я не знаю.
А что тут не знать? M>Например, что a--b, это не полная хрень, а a минус b с унарным минусом. Если нужно такое — это не ко мне. У меня жадный алгоритм токенизации. И он выплюнет a, --, b, для примитивной раскраски этого достаточно, как в прочем, и для дальнейшего разбора.
Ну, интерполированные строки он сможет разобрать?
M>Не очень понятно, что ты хотел сказать.
Что поддерживать парсер языка, в котором собственно грамматика размазана по токенизатору, парсеру, и пачке "фильтров", которые меняют поведение токенизатора и парсера — то ещё занятие.
M>Мне до этого ещё далеко. Но не вижу особых проблем. Не уверен, что на каждое нажатие клавиши надо делать вставки в единственный глобальный string с текстом. На вскидку — будет глобальный текст, и список строк, которые ссылаются индексами на глобальный текст. Если пошло редактирование строки, то глобальный текст не перестраивается при каждом вводе, просто для строки выделяется отдельный буфер и строка маркируется признаком своего буфера. Потом вставляем, пробегаем и обновляем индексы после нашей строки, переделываем раскарску. Раскраску при вводе/удалении можно сдвигать/удалять по месту, на базе старой, без перескана, при паузах и в фоне можно всё перестраивать/пересканивать как угодно. В общем, что-то не вижу особых проблем. Мои варианты решений конечно далеки от дедовских, которые шуршали даже на XT, но, как минимум не хуже того, что сейчас в тренде.
Очень хорошо.
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Здравствуйте, Sinclair, Вы писали:
M>>Да. Но в большинстве случаев это не нужно. Например, у меня в перспективе маячит раскраска кода листингов в документации, того, который в маркдауне ограничен тремя бэктиками, а на кывте обрамляется тэгом code. S>Ну, тогда я не совсем понимаю, чего вы хотите. В начале вы сослались на Гвидо, который объясняет причины, по которым он предпочёл бы переехать на PEG. Но PEG — это не лексер, это безлексерный парсер.
Гвидо там же говорит, что у него есть свой офигенский лексер, который он не хочет выкидывать. Я в такой же ситуации. Вот и всё. Для разминки думаю набросать пару языков на ручном рекурсивном спуске с использованием своего "лексера", а там дальше видно будет.
Packet Diagram выглядит хорошим кандидатом, мне как раз надо как-то по-простому описать кастомную раскладку памяти в прошивке, чтобы её не компилятор генерил, а я своей тулзой (хочу прошить во флешку инфу по железке раз и навсегда, а потом бутлоадер будет проверять, соответствует ли фирмварь хардварной ревизии), и этот кусочек прошивки хочу описать в виде Packet Diagram, а тулзе значения переменных отдавать, и она бы HEX генерила по такому описанию. Должно получится на имеющемся быстро и не слишком сложно
M>>Часто большее и не нужно S>Я ж не спорю — если ваши планы заканчиваются на лексической раскраске, то ваше решение прекрасно.
Мои планы на этом только начинаются
S>Стандарт де-факто сейчас — это VS Code, а точнее — протокол LSP.
Вы (ничего, что я на Вы?) сами же говорите, что это полное говно, нет?
И что там двухстадийная раскраска, и первая фаза на регэксах? У меня работает гораздо быстрее регэксов
M>>Моё решение предлагает задание операторов оптом, задание различных типов скобок, и наборы фильтров. Всё это можно варьировать при настройке и получать результат лучше, чем в NPP. Уже годно для тупого лексера. S>Ну так и замечательно. Просто если вы хотите что-то куда-то развивать, то тупой лексер, с моей точки зрения — тупик.
Посмотрю, как пойдёт. Пока так, что есть малой кровью
M>>Например, что a--b, это не полная хрень, а a минус b с унарным минусом. Если нужно такое — это не ко мне. У меня жадный алгоритм токенизации. И он выплюнет a, --, b, для примитивной раскраски этого достаточно, как в прочем, и для дальнейшего разбора. S>Ну, интерполированные строки он сможет разобрать?
Ну, надо будет плагин-парсер запилить для интерполированных строк, как и для любых нетривиальных литералов
M>>Не очень понятно, что ты хотел сказать. S>Что поддерживать парсер языка, в котором собственно грамматика размазана по токенизатору, парсеру, и пачке "фильтров", которые меняют поведение токенизатора и парсера — то ещё занятие.
Ну, если бы был хороший инструмент, который позволяет не размазывать, то я бы на него посмотрел. Может и приду к чему-то такому, может сам запилю. Пока ничего не нравится
M>>Мне до этого ещё далеко. Но не вижу особых проблем. Не уверен, что на каждое нажатие клавиши надо делать вставки...
S>Очень хорошо.
Здравствуйте, Sinclair, Вы писали:
M>>Не очень понятно, что ты хотел сказать. S>Что поддерживать парсер языка, в котором собственно грамматика размазана по токенизатору, парсеру, и пачке "фильтров", которые меняют поведение токенизатора и парсера — то ещё занятие.
А тут, мне кажется, есть вполне себе плюсы. В конфигах можно задавать свои операторы (алисами к существующим, или заменой), ключевые слова, и тд и тп. Без кодирования можно переделать под себя правкой джейсонов
Здравствуйте, Marty, Вы писали:
M>Гвидо там же говорит, что у него есть свой офигенский лексер, который он не хочет выкидывать.
Где вы там такое нашли? Всё, что там есть — длинное перечисление проблем в существующем парсере, как объяснение его интереса к PEG.
M>Я в такой же ситуации. Вот и всё. Для разминки думаю набросать пару языков на ручном рекурсивном спуске с использованием своего "лексера", а там дальше видно будет.
Кто ж вам запретит, делайте.
Меня собственно запиливание ручного рекурсивного спуска не интересует уже очень давно.
Интересует как можно более компактное решение типовых проблем современных парсеров:
— внятная диагностика синтаксических ошибок (у PEG с этим всё очень хорошо)
— внятная поддержка инкрементального парсинга (у PEG с этим всё очень хорошо; неплохо бы ещё автоматизировать и следующие стадии — скажем, корректировка таблиц имён и связанных с ними референсов без полного повтора семантического анализа, но это уже не про грамматики)
— внятная поддержка отката после ошибок (у PEG с этим всё неплохо — есть теоретические наработки, но не воплощены на практике).
Делать всё это вручную и каждый раз с нуля как-то не очень хочется.
M>Packet Diagram выглядит хорошим кандидатом, мне как раз надо как-то по-простому описать кастомную раскладку памяти в прошивке, чтобы её не компилятор генерил, а я своей тулзой (хочу прошить во флешку инфу по железке раз и навсегда, а потом бутлоадер будет проверять, соответствует ли фирмварь хардварной ревизии), и этот кусочек прошивки хочу описать в виде Packet Diagram, а тулзе значения переменных отдавать, и она бы HEX генерила по такому описанию. Должно получится на имеющемся быстро и не слишком сложно
Ну, тут формат примитивный, для него PEG делается в несколько строчек.
M>Вы (ничего, что я на Вы?) сами же говорите, что это полное говно, нет?
Ну да, а чему это противоречит? Неговна незавезли. M>И что там двухстадийная раскраска, и первая фаза на регэксах? У меня работает гораздо быстрее регэксов
:sigh: Что именно работает быстрее регекспов?
Предположим, что вы придумали новый язык описания чего-нибудь. Неважно — программного кода, расположения прошивок в памяти, диаграмм, или дерева эффектов в фильме.
Современные пользователи избалованы — они хотят не просто тул, который можно вызывать из командной строки с разнообразными флагами.
Они хотят полноценный интерактивный экспириенс — чтобы файлики эти можно было открывать там же, где они редактируют остальной контент; чтобы там раскраска синтаксиса работала, сворачивание регионов, навигация по коду, чтоб ошибки были и в списке project problems, и подчёркнуты прямо в редакторе, чтобы были quick fixes для этих ошибок, чтобы работал автокомплит, всякие go to definition и find all usages. И чтобы всё это — прямо на лету, интерактивно, и без please wait for project reindexing.
Можно начать колхозить свою IDE с блэкджеком и С++, но это — очень большая работа. В современной IDE уже наколбашено огромное количество функций, как встроенных, так и при помощи плагинов.
Даже если отказаться от собственно интегрированности, то даже "простой редактор кода" — это уже овердофига всяких фич, которые дорого писать с нуля. Поэтому берут за основу, скажем, тот же Monaco.
И вот теперь возникает вопрос — а как этому редактору обмениваться с вашим супербыстрым лексером? Ах, если бы он был написан на чистых плюсах, то ваш код можно было бы добавить к коду этого редактора, и собрать всё одним компилятором. Но нет — не написан он на плюсах. Поэтому поток маркеров стиля вы будете ему отдавать в виде JSON. И в итоге сколько бы вы времени ни сэкономили на переходах в конечном автомате, это просто потеряется на фоне стоимости сериализации/десериализации в JSON.
Именно поэтому современные IDE красят текст регекспами. Регекспы грузятся в память основного редактора, компилируются там, и работают приемлемо быстро, не создавая гигабайтного трафика между редактором и LSP.
M>Ну, надо будет плагин-парсер запилить для интерполированных строк, как и для любых нетривиальных литералов
Отож. Любую проблему можно решить допиливанием костыля для костыля. Кроме проблемы избытка костылей.
M>Ну, если бы был хороший инструмент, который позволяет не размазывать, то я бы на него посмотрел. Может и приду к чему-то такому, может сам запилю. Пока ничего не нравится
Хороший инструмент — это PEG.
M>Вы, наверное, забыли тут вставить
Нет, зачем. Я искренне рад за вас — сам люблю баловаться всякими велосипедами. Вот, в нынешнем проекте я из любви к искусству запилил VS Code extension с парсером и семантическим анализатором для нашего языка программирования. Пока остальная команда делала компилятор по-старинке — через bison и yacc. Когда есть возможность сравнить — сразу видно, насколько PEG выигрывает у классики.
Очень много тавтологичного бойлерплейта требуется для написания парсера традиционным методом.
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Здравствуйте, Marty, Вы писали: M>А тут, мне кажется, есть вполне себе плюсы. В конфигах можно задавать свои операторы (алиасами к существующим, или заменой), ключевые слова, и тд и тп. Без кодирования можно переделать под себя правкой джейсонов
Да, для решения конкретной ограниченной задачи такое подойдёт. Я помню, клиппер лет 30 назад изобретал что-то подобное — у него был конфигурируемый набор алиасов для команд; кто-то развлекался созданием русифицированного языка на его основе.
А вообще, вы не первый, кто пытается сделать конфигурируемую систему раскраски синтаксиса. Примерно все остановились на регекспах как наиболее универсальном способе.
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Здравствуйте, Sinclair, Вы писали:
M>>Гвидо там же говорит, что у него есть свой офигенский лексер, который он не хочет выкидывать. S>Где вы там такое нашли? Всё, что там есть — длинное перечисление проблем в существующем парсере, как объяснение его интереса к PEG.
Там цикл статей, во второй статье:
Токенизация Python достаточно сложна, поэтому я не хочу реализовывать её на правилах PEG. Например, вы должны отслеживать отступы (для этого требуется стек внутри токенизатора); интересна также и обработка новых строк в Python (они значимы, кроме заключенных в соответствующие скобки). Многие типы строк также вызывают некоторую сложность. Короче говоря, у меня нет никаких претензий к уже существующему токенизатору Python, поэтому я хочу оставить его как есть. Кстати, у CPython есть два токенизатора: внутренний, который используется парсером, он написан на C, и стандартный библиотечный, который является точной копией, реализованной на чистом Python. Это пригодится в моём проекте.
S>Кто ж вам запретит, делайте. S>Меня собственно запиливание ручного рекурсивного спуска не интересует уже очень давно. S>Интересует как можно более компактное решение типовых проблем современных парсеров: S>- внятная диагностика синтаксических ошибок (у PEG с этим всё очень хорошо) S>- внятная поддержка инкрементального парсинга (у PEG с этим всё очень хорошо; неплохо бы ещё автоматизировать и следующие стадии — скажем, корректировка таблиц имён и связанных с ними референсов без полного повтора семантического анализа, но это уже не про грамматики) S>- внятная поддержка отката после ошибок (у PEG с этим всё неплохо — есть теоретические наработки, но не воплощены на практике).
Да, это всё очень замечательно, кто же спорит
S>Ну, тут формат примитивный, для него PEG делается в несколько строчек.
Можно будет сравнить, если вам не лень будет этим заняться. Я, когда запилю, напомню, на таком простом примере наверное не сложно будет уделать рукопашный парсер?
M>>Вы (ничего, что я на Вы?) сами же говорите, что это полное говно, нет? S>Ну да, а чему это противоречит? Неговна незавезли.
Не говно — это когда функционал LSP доступен не через пайп, а через inproc плагин.
M>>И что там двухстадийная раскраска, и первая фаза на регэксах? У меня работает гораздо быстрее регэксов S>:sigh: Что именно работает быстрее регекспов?
Мой токенизатор.
S>Предположим, что вы придумали новый язык описания чего-нибудь. Неважно — программного кода, расположения прошивок в памяти, диаграмм, или дерева эффектов в фильме. S>Современные пользователи избалованы — они хотят не просто тул, который можно вызывать из командной строки с разнообразными флагами. S>Они хотят полноценный интерактивный экспириенс — чтобы файлики эти можно было открывать там же, где они редактируют остальной контент; чтобы там раскраска синтаксиса работала, сворачивание регионов, навигация по коду, чтоб ошибки были и в списке project problems, и подчёркнуты прямо в редакторе, чтобы были quick fixes для этих ошибок, чтобы работал автокомплит, всякие go to definition и find all usages. И чтобы всё это — прямо на лету, интерактивно, и без please wait for project reindexing. S>Можно начать колхозить свою IDE с блэкджеком и С++, но это — очень большая работа. В современной IDE уже наколбашено огромное количество функций, как встроенных, так и при помощи плагинов. S>Даже если отказаться от собственно интегрированности, то даже "простой редактор кода" — это уже овердофига всяких фич, которые дорого писать с нуля. Поэтому берут за основу, скажем, тот же Monaco. S>И вот теперь возникает вопрос — а как этому редактору обмениваться с вашим супербыстрым лексером? Ах, если бы он был написан на чистых плюсах, то ваш код можно было бы добавить к коду этого редактора, и собрать всё одним компилятором. Но нет — не написан он на плюсах. Поэтому поток маркеров стиля вы будете ему отдавать в виде JSON. И в итоге сколько бы вы времени ни сэкономили на переходах в конечном автомате, это просто потеряется на фоне стоимости сериализации/десериализации в JSON. S>Именно поэтому современные IDE красят текст регекспами. Регекспы грузятся в память основного редактора, компилируются там, и работают приемлемо быстро, не создавая гигабайтного трафика между редактором и LSP.
Тут надо разбираться, как сделать плагин для какой-нибудь IDE, это самое сложное. У меня вообще в тудушке есть пункт собрать некоторые свои плюсовые проекты в вебассемблю. Может появится побочный эффект, что я смогу на плюсиках делать плагины для ВСКода, но не факт.
M>>Ну, надо будет плагин-парсер запилить для интерполированных строк, как и для любых нетривиальных литералов S>Отож. Любую проблему можно решить допиливанием костыля для костыля. Кроме проблемы избытка костылей.
Не костыля, а плагина
M>>Ну, если бы был хороший инструмент, который позволяет не размазывать, то я бы на него посмотрел. Может и приду к чему-то такому, может сам запилю. Пока ничего не нравится S>Хороший инструмент — это PEG.
А насколько он хорош для плюсов?
M>>Вы, наверное, забыли тут вставить S>Нет, зачем. Я искренне рад за вас — сам люблю баловаться всякими велосипедами. Вот, в нынешнем проекте я из любви к искусству запилил VS Code extension с парсером и семантическим анализатором для нашего языка программирования. Пока остальная команда делала компилятор по-старинке — через bison и yacc. Когда есть возможность сравнить — сразу видно, насколько PEG выигрывает у классики. S>Очень много тавтологичного бойлерплейта требуется для написания парсера традиционным методом.
bison и yacc
Вы хотели сказать, наверное, lex+yacc/flex+bison?
Я когда-то flex+bison тыкал, тоже не помню для чего, может те же плюсики раскрашивал. Это ужасно
Здравствуйте, Sinclair, Вы писали:
M>>А тут, мне кажется, есть вполне себе плюсы. В конфигах можно задавать свои операторы (алиасами к существующим, или заменой), ключевые слова, и тд и тп. Без кодирования можно переделать под себя правкой джейсонов S>Да, для решения конкретной ограниченной задачи такое подойдёт. Я помню, клиппер лет 30 назад изобретал что-то подобное — у него был конфигурируемый набор алиасов для команд; кто-то развлекался созданием русифицированного языка на его основе. S>А вообще, вы не первый, кто пытается сделать конфигурируемую систему раскраски синтаксиса. Примерно все остановились на регекспах как наиболее универсальном способе.
То-то современные IDE такие жирные и тормозные. То ли дело — старикашка кейл, у него раскраска на базе scintilla, летает, голубчик
SciTE — текстовый редактор, который первоначально был создан для демонстрации возможностей Scintilla.
Notepad++ — текстовый редактор для Microsoft Windows.
Geany — легковесная кроссплатформенная интегрированная среда разработки.
FbEdit — среда разработки под Windows для языка FreeBasic.
Code::Blocks — кроссплатформенная интегрированная среда разработки с открытым исходным кодом.
CodeLite — свободная кроссплатформенная среда разработки программного обеспечения для языка C/C++ с открытым исходным кодом.
FlashDevelop — открытая среда разработки RIA Flex, Flash, Haxe для Microsoft Windows.
Aegisub[5] — кроссплатформенный редактор субтитров.
Notepad2 — текстовый редактор для Microsoft Windows, как замена стандартному приложению Блокнот.
PureBasic IDE — кроссплатформенная интегрированная среда разработки для языка PureBasic.
MySQL Workbench — инструмент для визуального проектирования баз данных.
Visual Prolog — Visual Prolog.
µVision (начиная с версии 4.53.0.4) — интегрированная среда разработки для микроконтроллеров.
TortoiseGit — визуальный клиент системы управления исходными кодами программ git.
Сам юзал µVision (он же кейл), Notepad++, Code::Blocks — все они весьма шустры, по сравнению c например Eclipse, или IDE от JetBrains (IDEA, CLion). VSCode в принципе шустр, но с подсветкой тоже подтупливает
Здравствуйте, Marty, Вы писали: M>Там цикл статей, во второй статье: M>
M>Токенизация Python достаточно сложна, поэтому я не хочу реализовывать её на правилах PEG. Например, вы должны отслеживать отступы (для этого требуется стек внутри токенизатора); интересна также и обработка новых строк в Python (они значимы, кроме заключенных в соответствующие скобки). Многие типы строк также вызывают некоторую сложность. Короче говоря, у меня нет никаких претензий к уже существующему токенизатору Python, поэтому я хочу оставить его как есть. Кстати, у CPython есть два токенизатора: внутренний, который используется парсером, он написан на C, и стандартный библиотечный, который является точной копией, реализованной на чистом Python. Это пригодится в моём проекте.
А, спасибо, посмотрю. Да, indent-based синтаксисы традиционно трудны для вообще всех грамматических парсеров. У меня есть идея, как это можно было бы изящно сделать в PEG, не встраивая в него костыли. Но её надо проверять.
S>>Ну, тут формат примитивный, для него PEG делается в несколько строчек. M>Можно будет сравнить, если вам не лень будет этим заняться. Я, когда запилю, напомню, на таком простом примере наверное не сложно будет уделать рукопашный парсер?
Что значит "уделать"? Опередить по компактности и читаемости кода — 100%.
M>Не говно — это когда функционал LSP доступен не через пайп, а через inproc плагин.
Ну так его-то и не завезли. Собственно, за систему inproc-плагинов люди и не любили старую студию.
M>Мой токенизатор.
При отдаче результата в dev>null? Или он у вас там порождает стилизированный HTML?
M>Тут надо разбираться, как сделать плагин для какой-нибудь IDE, это самое сложное. У меня вообще в тудушке есть пункт собрать некоторые свои плюсовые проекты в вебассемблю. Может появится побочный эффект, что я смогу на плюсиках делать плагины для ВСКода, но не факт.
Во-первых, на плюсиках плагины для ВСКода и так можно делать. Их уже делают кто во что горазд.
Во-вторых, сборка в wasm упростит деплоймент, но вряд ли улучшит быстродействие. Потому что узким местом LSP всё равно является JSON-RPC.
Встроить раскраску синтаксиса во встроенный редактор можно только через LSP либо TextMate2. Если вы захотите делать inproc-раскраску, вам придётся напилить свой custom editor.
M>Не костыля, а плагина А какой смысл тогда называть "это" токенизатором, если у него внутри полноценный парсер?
M>А насколько он хорош для плюсов?
Для разбора плюсов или для порождения плюсового кода? Для разбора — нормально; для порождения — надо смотреть на инструменты.
Есть сразу несколько библиотек с реализацией PEG для плюсов. Но я на них не смотрел — не моя специализация.
M>Вы хотели сказать, наверное, lex+yacc/flex+bison?
Наверное. Но глядя на примеры "PEG" для плюсов (https://github.com/taocpp/PEGTL/blob/main/src/example/pegtl/calculator.cpp) я вижу, что проблема не столько в конкретном выбранном виде парсеров, сколько в твёрдом убеждении их авторов в неизбежности страданий. Типа, "нельзя просто написать грамматику с действиями и разбирать ей тексты!". Надо получить обязательную порцию унижений в виде пачки бойлерплейта. В итоге, калькулятор с 18 операциями превращается в 360 строк кода . При этом, собственно, от грамматики в исходнике ничего не остаётся — всё размазано по вспомогательным структурам и рукопашным методам match.
Вот этот вариант выглядит получше, но всё делает в рантайме. Из этого мы сразу выводим что
а) косяки в грамматике тоже обнаружатся в рантайме, а в компайл-тайме нам ничего не скажут. То есть "приходи, дорогой друг, завтра, когда отработают тесты найтли билда".
б) быстродействие, скорее всего, хуже чем у плюсовых регекспов и аналогов на Javascript.
В общем, беглым взглядом я ничего не нашёл. Скорее всего, можно навелосипедить приличное решение, которое умеет разбирать текст DSL, который содержит грамматику и семантические действия, и порождать код на плюсах. Вот это, собственно, и даст возможность быстро выполнять как прототипирование, так и собственно разбор текста получившимся кодом.
M>Я когда-то flex+bison тыкал, тоже не помню для чего, может те же плюсики раскрашивал. Это ужасно
Отож.
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
S>>>Ну, тут формат примитивный, для него PEG делается в несколько строчек. M>>Можно будет сравнить, если вам не лень будет этим заняться. Я, когда запилю, напомню, на таком простом примере наверное не сложно будет уделать рукопашный парсер? S>Что значит "уделать"? Опередить по компактности и читаемости кода — 100%.
Я пока не очень разобрался, как всё это работает в конкретных инструментах, но наверное на каждое правило должно быть навешено какое-то действие, типа, добавить элемент в AST. Можно считать, что код по навешиванию действий в PEG варианте не входит в парсер, а во втором варианте считать наоборот
M>>Не говно — это когда функционал LSP доступен не через пайп, а через inproc плагин. S>Ну так его-то и не завезли. Собственно, за систему inproc-плагинов люди и не любили старую студию.
То есть, inproc-плагины — это говно, а по пайпу туда-сюда гонять джейсон — это офигенно?
M>>Мой токенизатор. S>При отдаче результата в dev>null? Или он у вас там порождает стилизированный HTML?
Не очень понял. Мой токенизатор может порождать стилизированный HTML, я уже приводил этот пример. И я могу без проблем какому-то хосту отдавать информацию, что и с какой позиции надо красить каким стилем.
M>>Тут надо разбираться, как сделать плагин для какой-нибудь IDE, это самое сложное. У меня вообще в тудушке есть пункт собрать некоторые свои плюсовые проекты в вебассемблю. Может появится побочный эффект, что я смогу на плюсиках делать плагины для ВСКода, но не факт. S>Во-первых, на плюсиках плагины для ВСКода и так можно делать. Их уже делают кто во что горазд.
Это интересно, но не на столько, чтобы я кинулся сейчас сразу искать информацию на эту тему. Если есть под рукой годные ссылки, то буду благодарен.
S>Во-вторых, сборка в wasm упростит деплоймент, но вряд ли улучшит быстродействие. Потому что узким местом LSP всё равно является JSON-RPC.
Ну, я очень мало знаю о том, как устроен VSCode, и пока не копал. Слышал, что он весь из себя на JS сделанный, и плагины тоже как-то так делаются. Про web assembly я тоже очень мало знаю, но, теоретически, это вроде как единственный путь собирать плюсовый код, чтобы он работал на жиэсе.
S>Встроить раскраску синтаксиса во встроенный редактор можно только через LSP либо TextMate2. Если вы захотите делать inproc-раскраску, вам придётся напилить свой custom editor.
LSP — понятно, а TextMate/2 — что за зверь? Какие с ним проблемы? (Да, я бегло погуглил, но ничего не понял )
M>>Не костыля, а плагина S> А какой смысл тогда называть "это" токенизатором, если у него внутри полноценный парсер?
Так не полноценный же. Сам по себе, без надстроек — он вполне себе только токенизатор.
У меня ещё есть разделение на TokenizerBuilder и Tokenizer. Сейчас TokenizerBuilder умеет создавать Tokenizer, но, теоретически, TokenizerBuilder будет уметь сериализовать Tokenizer в плюсовый код, из которого можно создать Tokenizer. А этот Tokenizer можно будет запихать без билдера в STM32, чтобы он там что-то простецкое разбирал. Но я пока эту ветку не прорабатывал.
M>>Вы хотели сказать, наверное, lex+yacc/flex+bison? S>Наверное. Но глядя на примеры "PEG" для плюсов (https://github.com/taocpp/PEGTL/blob/main/src/example/pegtl/calculator.cpp) я вижу, что проблема не столько в конкретном выбранном виде парсеров, сколько в твёрдом убеждении их авторов в неизбежности страданий. Типа, "нельзя просто написать грамматику с действиями и разбирать ей тексты!". Надо получить обязательную порцию унижений в виде пачки бойлерплейта. В итоге, калькулятор с 18 операциями превращается в 360 строк кода . При этом, собственно, от грамматики в исходнике ничего не остаётся — всё размазано по вспомогательным структурам и рукопашным методам match.
Глянул, что-то такое себе
S>Вот этот вариант выглядит получше, но всё делает в рантайме. Из этого мы сразу выводим что S>а) косяки в грамматике тоже обнаружатся в рантайме, а в компайл-тайме нам ничего не скажут. То есть "приходи, дорогой друг, завтра, когда отработают тесты найтли билда". S>б) быстродействие, скорее всего, хуже чем у плюсовых регекспов и аналогов на Javascript. S>В общем, беглым взглядом я ничего не нашёл. Скорее всего, можно навелосипедить приличное решение, которое умеет разбирать текст DSL, который содержит грамматику и семантические действия, и порождать код на плюсах. Вот это, собственно, и даст возможность быстро выполнять как прототипирование, так и собственно разбор текста получившимся кодом.
Здравствуйте, Marty, Вы писали: M>Я пока не очень разобрался, как всё это работает в конкретных инструментах, но наверное на каждое правило должно быть навешено какое-то действие, типа, добавить элемент в AST. Можно считать, что код по навешиванию действий в PEG варианте не входит в парсер, а во втором варианте считать наоборот
Ну, вообще на эту тему чуть больше мнений, чем есть реализаций PEG. Но более-менее традиционным всё же считается подход, в котором semantic actions вписаны прямо в грамматику.
Можно и по-другому, но тогда начинаются всякие мелкие неприятности при написании, да и читаемость страдает.
M>То есть, inproc-плагины — это говно, а по пайпу туда-сюда гонять джейсон — это офигенно?
Конечно. Основная причина — криворукий плагин не роняет студию, а просто отпадает, ни на что не влияя.
А то, что там гигабайты под капотом летают — ну и хрен с ними, пока это не мешает пользоваться студией. Многочисленные преимущества вот так вот взяли да и перевесили недостатки.
M>Не очень понял.
А что тут не понять? Бенчмаркать "чистый токенизатор", который отдаёт результат в dev/null, не имеет никакого смысла. Это всё равно, что мерить скорость какого-нибудь "i++".
Интересна скорость прикладной задачи с использованием различных решений. M>Мой токенизатор может порождать стилизированный HTML, я уже приводил этот пример. И я могу без проблем какому-то хосту отдавать информацию, что и с какой позиции надо красить каким стилем.
Всё упирается в то, каким способом это отдаётся этому хосту. Понимаете, лексический анализ при правильной реализации — штука феерически дешёвая. Ну, то есть понятно, что ваш код теоретически можно разогнать ещё в три-пять раз, но даже уже и так он работает настолько эффективно, что становятся заметны вызовы getNext. Любое использование результата вызова getNext будет ещё заметнее. M>Это интересно, но не на столько, чтобы я кинулся сейчас сразу искать информацию на эту тему. Если есть под рукой годные ссылки, то буду благодарен.
Годные ссылки лежат прямо на сайте VS Code — с пошаговыми тьюториалами и примерами. На данном этапе важно понять принцип: собственно JSON-RPC был там придуман ровно для того, чтобы LSP можно было реализовать на чём угодно, хоть даже и на плюсах. Поэтому, к примеру, сервер для Rust написан на Rust, а сервер для Java — на Java.
M>Ну, я очень мало знаю о том, как устроен VSCode, и пока не копал. Слышал, что он весь из себя на JS сделанный, и плагины тоже как-то так делаются. Про web assembly я тоже очень мало знаю, но, теоретически, это вроде как единственный путь собирать плюсовый код, чтобы он работал на жиэсе.
VS Code совершенно всё равно, на чём работает ваш код. Да, работа на JS упрощает деплоймент, потому что не нужно думать о том, как запустить ваш код на конкретной платформе. А в остальном стандартный способ — стартовать процесс на том, на чём он написан, и работать с ним через локальные веб-вызовы.
И даже это не единственный способ — поскольку VS Code умеет работать в веб (zero-deployment), ваш LSP сервер вообще может торчать в интернете, и VS Code будет с ним работать на лету через честное сетевое соединение.
Но я эту возможность пока что мало изучал, по ряду административных причин.
M>LSP — понятно, а TextMate/2 — что за зверь? Какие с ним проблемы? (Да, я бегло погуглил, но ничего не понял )
Это и есть раскраска на регекспах. Она полностью декларативна в том смысле, что никакого кода вы не пишете. Пишете более-менее развесистый набор правил, и отдаёте их примерно в любой редактор, который поддерживает этот стандарт.
M>Так не полноценный же. Сам по себе, без надстроек — он вполне себе только токенизатор.
M>У меня ещё есть разделение на TokenizerBuilder и Tokenizer. Сейчас TokenizerBuilder умеет создавать Tokenizer, но, теоретически, TokenizerBuilder будет уметь сериализовать Tokenizer в плюсовый код, из которого можно создать Tokenizer. А этот Tokenizer можно будет запихать без билдера в STM32, чтобы он там что-то простецкое разбирал. Но я пока эту ветку не прорабатывал.
Ну, в целом это и есть самый конструктивный подход. Потому что если делать "только конфигурацию в рантайме", то вы пропустите оптимизации. Будет примерно то же самое по производительности, что и регекспы на плюсах (я же правильно понял, что там они всё ещё интерпретируются?). А если делать рукопашное написание токенизатора, то код становится громоздким и плохо поддерживаемым.
Так что да — берём код грамматики на удобном DSL, по нему строим код на целевом языке и алга. Ну, там дальше возможны варианты — не обязательно же сначала порождать код C++, чтобы потом его какой-нибудь Clang или ICC превращал в LLVM IR. Можно и сразу LLVM IR порождать, минуя лишний этап.
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Здравствуйте, Sinclair, Вы писали:
M>>Я пока не очень разобрался, как всё это работает в конкретных инструментах, но наверное на каждое правило должно быть навешено какое-то действие, типа, добавить элемент в AST. Можно считать, что код по навешиванию действий в PEG варианте не входит в парсер, а во втором варианте считать наоборот S>Ну, вообще на эту тему чуть больше мнений, чем есть реализаций PEG. Но более-менее традиционным всё же считается подход, в котором semantic actions вписаны прямо в грамматику. S>Можно и по-другому, но тогда начинаются всякие мелкие неприятности при написании, да и читаемость страдает.
Это интересный вопрос, на самом деле: имхо было бы удобно описывать правила отдельно, а действия, которые производятся при матче правил — отдельно. Тогда грамматику языка можно описать один раз, а построение AST можно пилить для target языков отдельно, и биндить к правилам при выводе в целевой язык.
M>>То есть, inproc-плагины — это говно, а по пайпу туда-сюда гонять джейсон — это офигенно? S>Конечно. Основная причина — криворукий плагин не роняет студию, а просто отпадает, ни на что не влияя. S>А то, что там гигабайты под капотом летают — ну и хрен с ними, пока это не мешает пользоваться студией. Многочисленные преимущества вот так вот взяли да и перевесили недостатки.
Криворукий плагин не роняет фар, а просто выгружается. Хотя в фаре плагины inproc. По-моему, это тривиально делается. В винде для этого есть SEH, в других системах наверняка должно быть что-то подобное.
S>Годные ссылки лежат прямо на сайте VS Code — с пошаговыми тьюториалами и примерами. На данном этапе важно понять принцип: собственно JSON-RPC был там придуман ровно для того, чтобы LSP можно было реализовать на чём угодно, хоть даже и на плюсах. Поэтому, к примеру, сервер для Rust написан на Rust, а сервер для Java — на Java.
Спс, я не интересовался настолько, чтобы что-то копать. LSP это конечно интересно, но вообще для VSCode есть куча плагинов на все случаи жизни, а не только для подсветки кода. Или LSP настолько всеобъемлющ, что позволяет реализовать любые типы плагинов для VSCode? Но да, в контексте раскраски синтаксиса и интеллисенса от LSP наверное никуда не деться.
Здравствуйте, Marty, Вы писали:
M>Это интересный вопрос, на самом деле: имхо было бы удобно описывать правила отдельно, а действия, которые производятся при матче правил — отдельно. Тогда грамматику языка можно описать один раз, а построение AST можно пилить для target языков отдельно, и биндить к правилам при выводе в целевой язык.
Это имеет смысл в ограниченном количестве случаев. Ситуации, когда одна и та же грамматика применяется для разных целевых языков, лично мне в жизни не встречались и ожиданий таковых нет.
И даже если такое встретится, мне, скорее всего, будет проще просто склонировать грамматику и переписать правила, чем мучиться с выписыванием отдельного решения.
Впрочем, если хочется попрактиковаться — именно так устроен Ohm-js. В реальности получается не особо удобно, несмотря на офигенную гибкость TypeScript. Основная проблема — безымянные части правил.
В Lingo, к примеру, я могу писать семантическия действия прямо по месту, ещё и выбирая для байндинга только нужные мне части правил:
В Ohm у меня справа от = стоит безымянная альтернатива; чтобы забиндить к ней какое-то действие, нужно дать ей имя. И аргументами для действия будут все ноды, независимо от их полезности, а тип результата будет каким-то общим супертипом для всех узлов. Средства для всего этого в библиотеке есть, но выглядит костыльно:
Примерно то же самое придётся делать в более-менее любом подходе, где действия отделены от семантики. Причём ценность этого равна примерно нулю, потому что, повторюсь, целевой язык компилятора обычно известен к тому моменту, когда пишется грамматика. И лучше помочь тем, кто пилит в этом направлении, чем портить жизнь остальным 99.9% пользователей. Авторы Ohm.js приводят вырожденные примеры — типа "а давайте мы забабахаем калькулятор на семантических правилах; а теперь давайте на той же грамматике забабахаем претти-принтер". Ну вот в реальной жизни наиболее практичный способ — это не вычислять грамматикой значение выражения, а породить AST, из которого уже легко, непринуждённо, и типобезопасно получается и "вычисление значения", и pretty print, и вообще примерно всё остальное.
Особенно с учётом того, что реальные языки (в отличие от школьных примеров типа целочисленной арифметики) используют разные типы узлов в AST, и крайне полезно статически проверять корректность сборки родителей из детей.
M>Криворукий плагин не роняет фар, а просто выгружается. Хотя в фаре плагины inproc. По-моему, это тривиально делается. В винде для этого есть SEH, в других системах наверняка должно быть что-то подобное.
По-моему, вы фантазируете. Никакой SEH не поможет вам от плагина, который просто срёт в общее адресное пространство потому, что кто-то не освоил адресную арифметику, или отлаживался в дебаг-режиме, а в релизе у него неинициализированный указатель показывает в космос, а не в null.
M>Спс, я не интересовался настолько, чтобы что-то копать. LSP это конечно интересно, но вообще для VSCode есть куча плагинов на все случаи жизни, а не только для подсветки кода. Или LSP настолько всеобъемлющ, что позволяет реализовать любые типы плагинов для VSCode?
Нет, LSP — только для языковых инструментов. Плагины, которые расширяют саму студию — всякие альтернативные редакторы, интеграция юнит-тестирования или системы контроля версий пишутся на тайпскрипте и, в некотором смысле, работают in-proc. Но вас-то интересуют не они. Ну, по крайней мере мне как раз интересно, как с минимально возможными усилиями завести IDE для нового языка, а не новый пункт меню в одной случайно выбранной IDE. M>Но да, в контексте раскраски синтаксиса и интеллисенса от LSP наверное никуда не деться.
Да, вся навигация по коду, автодополнение, signature hint, рефакторинги и всё прочее — это LSP.
Уйдемте отсюда, Румата! У вас слишком богатые погреба.