LINUX.ORG.RU

как же написать этот парсер?

 , , , ,


1

2

Мы предприняли титанические усилия и реализовали интерпретатор Common Lisp (ну, почти всего), способный в любой момент приостановить своё выполнение и вернуть состояние. Состояние можно скопировать и потом перезапустить несколько раз. При этом мы умеем что-то делать и с биндингами специальных переменных. Чтобы копировать состояния эффективно, мы начали делать «версионное состояние», которое копируется более дёшево, чем просто глубокая копия графа объектов. Его, правда, пока не используем, но планируем.

Ничего, что вся эта конструкция тормозная и привязана к SBCL. На этой основе реализована раскраска Яра (пока старой версии, но не суть).

Мы напускаем наш обычный парсер, который входит в транслятор, на текст в файле. Состояние парсера запоминаем в начале каждой строки, чтобы сэкономить усилия и не парсить файл целиком с начала до конца на каждое нажатие кнопки.

Смысл всех наворотов ровно в том, чтобы иметь один общий парсер, пригодный и для компиляции, и для среды.

Но на этом страдания не закончились. Оказывается (кто бы мог подумать?), современная среда должна ещё подсказывать, чем можно продолжить текст. И оказалось что это имеет прямое отношение к грамматике языка и к информации, которую накапливает парсер.

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

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

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

★★★★★

Есть вот такой чудо-комплетер: http://synthcode.com/wiki/scheme-complete

Умеет понимать, что "(string-ref (n" может быть дополнено только до (string-ref (number->string", а

(let ((len (string-length str)))
  (string-ref str (- 

только идентификатором len.

Public Domain.

monk ★★★★★
()

Completion для lisp это из области фантастики.

anonymous
()

Скажу по собственному опыту, хотя он и специфический. Я пилю IDE, но для разработки микросхем и в первую очередь процессоров, и там тоже пришлось делать свой, С++-подобный(в идеале 99%-совместимый) язык. Ну например для того, чтоб видеть какой wire и reg с какой стадии конвейера получает данные, и ожидаемую физическую latency(с точностью +-10% считается влёт методом квадратов).

Это моё частное мнение. Кроме того, оно относится к verilog- и с++-подобному языку, а не к Лиспу. Возможно, оно в корне не верно.

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

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

i36_zubov
()
Ответ на: комментарий от i36_zubov

Я пилю IDE, но для разработки микросхем и в первую очередь процессоров

Интересно, для какой компании? Не из РФ? Топология или уровень RTL?

I-Love-Microsoft ★★★★★
()
Ответ на: комментарий от I-Love-Microsoft

Интересно, для какой компании?

Для себя. У меня есть свой хобби-проект, а продукция каденса, которая попала волею судьбы ко мне в руки, оскорбляет мой взор говном на экране, мой разум - говном вместо инструментов и говном в виде результатов.

Был бы в конторе софт от mentor graphics, возможно всё было бы по другому. Но даже он мне не совсем подходит по некоторым причинам.

i36_zubov
()
Ответ на: комментарий от den73

180 кб, из них половина данных.

В случае CL данные можно сгенерировать из типов describe.

Не обещает лёгкой жизни :(

Прочитать 90Кб — это много?

monk ★★★★★
()
Ответ на: комментарий от monk

Вообще да, 90 кб - это много. Ты сам читал? Стоит ли мне читать?

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

Эту проблему я знаю. Моё решение - делать весь код начиная с момента поломки парсера «серым». Для начала это пойдёт. На будущее можно разбивать на параграфы. Скажем, границей параграфа может быть код с нулевым отступом. И инкрементную раскраску не делать дальше конца текущего параграфа.

den73 ★★★★★
() автор топика

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

какой

инкрементальный. то есть, что-то на основе prefix tree, например. чтобы параллельно разбору строился индекс с префиксом, которым искать уже в соответствии с «что сейчас ожидается» по возможным вариантам дерева разбора (в лесу деревьев)

либо

может быть есть идеи, как можно рекурсивно-спускающийся парсер приспособить для этого

TBPEG, tree based PEG . есть paper про С++0х реализацию tbpeg парсера chilon::parser, вот по аналогии что-то на CL изобразить.

PEG-парсер, который выкидивает пробелы вокруг токенов и параллельно строит AST, переписывая и выкидывая избыточные узлы (какие из них избыточны — задаётся TBPEG грамматикой)

Оказывается (кто бы мог подумать?), современная среда должна ещё подсказывать, чем можно продолжить текст. И оказалось что это имеет прямое отношение к грамматике языка и к информации, которую накапливает парсер.

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

почему не подходит? просто допустим есть допустимые варианты формы, из которых можно выбирать корректные варианты. prefix tree. «информация, которую накапливает» — это разобранные варианты, которые должны быть одним из допустимых. index.

ищем index в prefix tree, если повезёт, то инкрементально. если не находим — значит, недопустимый вариант (который инкрементально должен отсеяться как можно раньше).

При этом понятно, что список ожидаемого, вообще говоря, строится динамически. Например

тут надо расписать побольше примеров, для наглядности.

anonymous
()

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

почему не входит? вот в ANTLR парсере, например.

есть «синтаксические» предикаты и «семантические».

синтаксические — проверяют форму в целом, если нарушены -> форма не распознана по структуре самой грамматики

семантические проверяют что например в грамматике «object.methodcall(p1,p2,p3)» object — имеет тип переменная, methodcall — тип метода этой переменной, p1..3 — параметры-выражения.

то есть, семантические уже строят таблицу символов и проверяют по ней.

а синтаксические предикаты таки входят в расширенную tree grammar, которая строит AST.

для того же (defsystem — короткая форма, которая в docstring описана неформально (ты говоришь, как метаданные)

а должна быть описана как форма в виде предикатов для парсера.

Т.е. статически сгенерировать сам список продолжений невозможно.

future/promise, не?

anonymous
()
Ответ на: комментарий от anonymous

Efficient and Flexible Incremental Parsing TIM A. WAGNER and SUSAN L. GRAHAM University of California, Berkeley

на примере какой-то IDE из проекта про баяны (списочек)

папа карло — инкрементальный парсер напоиграться (тут надо понять, что происходит с tree grammar).

ряд ссылок на публикации по инкрементальным парсерам: 1 2 3

плюс, конечно то что в редакторе Yi на Хаскелле (там тоже что-то было про инкрементальный парсинг)

anonymous
()
Ответ на: комментарий от den73

ещё реквестирую

дай ссылок, плиз. где можно почитать про «парсер-линзу» ???

и как оно у тебя применяется (если применяется) ?

anonymous
()
Ответ на: комментарий от i36_zubov

Я пилю IDE, но для разработки микросхем и в первую очередь процессоров, и там тоже пришлось делать свой, С++-подобный(в идеале 99%-совместимый) язык.

Xilinx HLS?

mv ★★★★★
()
Ответ на: ещё реквестирую от anonymous

Где-то на ЛОРе прочитал этот термин. Смысл в том, что он даже комментарии собирает и потом по дереву можно в точности воспроизвести исходный текст.

Думается, он применяется в CLang. Как у меня применяется? Первое практическое применение было для 1С 7.7. В 1С 7.7 параметры по умолчанию передаются по имени (то, что в C по мутабельному указателю). Как правило, фактически они не присваиваются. Знание об этом помогает читать программу. Я написал тул, который разбирает код обработки на 1С 7.7 и добавляет слово «знач» к каждому параметру, который фактически не меняется, а потом собирает исходник обратно.

Второе применение - в 1С очень медленный отладчик. Я написал программу (правда, кривоватую), которая инструментирует код, чтобы в момент падения по ошибке напечатался стек. А потом обратно разинструментирует.

Также был сделан (более кривой) аналогичный инструментор-разинструментов для Firebird, где он тоже был по делу, там с отладчиком ещё намного хуже.

Вот применения из жизни. В Яре он пока не применяется за отсутствием синтаксиса в настоящий момент. Синтаксис в процессе переработки. У старого синтаксиса есть парсер-линза, или был. Можешь попробовать посмотреть исходники (хотя они страшные).

den73 ★★★★★
() автор топика
Последнее исправление: den73 (всего исправлений: 1)
Ответ на: комментарий от anonymous

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

den73 ★★★★★
() автор топика
Ответ на: комментарий от den73

то есть по твоим двум ссылкам из поста this : yar / src / versii / âåğñèè.lisp

А то я сегодня весь день пытаюсь понять, почему в офтопике иногда вместо кириллицы кракозяблы с клавиуатуры приходят.

вангую: потому что раскладка стоит EN, и копипастится русский текст. при EN раскладке он в клипборд попадает как CP-1252, европейская. а когда стоит RU, попадает как CP-1251 (хотя по идее должен быть уникод везде)

можно однострочник написать на iconv или enca, который детектит CP-1252 и если найдёт конвертирует в UTF-8 или уникод через CP-1251. то есть, два раза переконвертирует чтобы пофиксить кракозябры (подобрать последовательность).

anonymous
()
Ответ на: комментарий от anonymous

Нет, это что-то с TranslateMessage, WM_CHAR vs WM_KEYчто-то-там. Не суть.

то есть по твоим двум ссылкам из поста this : yar / src / versii / âåğñèè.lisp

Это вообще не распарсил. ЧТо значит «то есть»?

den73 ★★★★★
() автор топика

Что взять в качестве примера парсера, который может подсказать, что сейчас ожидается

С современными LALR-парсерами можно получать список ожидаемых токенов. Вот обсуждали на лоре что-то похожее: Syntax-aware autocomplete

Я там предлагал решения с bison и lemon. Но там простое туповатое решение, на лиспе или с PEG наверняка можно сделать красивее и лучше

Deleted
()
Ответ на: комментарий от den73

Ты сам читал? Стоит ли мне читать?

По диагонали. Я elisp не очень хорошо знаю.

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

monk ★★★★★
()

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

anonymous
()

Пацаны, если вкратце, что ТС сделал-то?

Из исходников типа yar / src / versii / âåğñèè.lisp не очень очевидно))

anonymous
()
Ответ на: комментарий от monk

Мне из этой ссылки не ясно, зачем нужен sbcl, если это новый интерпретатор. А также дружит ли оно с потоками, на каких архитектурах работает, короче, больше инфы

anonymous
()
Ответ на: комментарий от monk

Короче, лажа какая-то. Смесь из русского языка, английского и транслита)) Как я понял, это не интерпретатор и не компилятор вовсе, а какая-то надстройка над sbcl. Дескать, если вы будете интерпретировать в каком-то нашем окружении, то оно может и сработает, а может и нет.

родовых функциях (CLOS generic functions)

Позабавило.

Это не значит, что нельзя пользоваться этими возможностями в нашем интерпретаторе. Это значит, что ими нужно пользоваться осторожно.

Ага, перед наступанием на грабли воспользуйтесь поролоновой насадкой.

Притом, что в sbcl repl тоже работает по дефолту как компилятор, а компилятор ТС не менял, как я понял. Т.е. дефолтное более лучшее поведение придется менять в угоду этой системе. О сохранении состояния тредов тоже речи нет, как я понял.

anonymous
()
Ответ на: комментарий от monk

Короче, я написал не затем, чтобы поругать или что-то. Видно ведь, ТС тратит на это большие усилия, пишет там о «нас», т.е. он не один, как я понял. А вот результат какой-то невнятный и сомнительный. Стоит ли оно того?

Вообще, задумку ТС'а лучше всего делать на уровне OS, но и на уровне lisp-среды тоже можно попытаться. Но не так, как он, похоже

anonymous
()
Ответ на: комментарий от anonymous

Вообще тут не должно быть потоков. Ибо данныео структуре программы могут быть изменены где угодно плюс все это идет в связке в ui

i36_zubov
()
Ответ на: комментарий от i36_zubov

Вообще тут не должно быть потоков.

О, норм, задумал я многопоточное приложение, и тут мне: «Вообще тут не должно быть потоков». Это в 21 веке

anonymous
()
Ответ на: комментарий от monk

Примерно понял, спасибо.

den73 ★★★★★
() автор топика
Ответ на: комментарий от anonymous

ну делай, ёпт. в 21м веке же, значит надо потоки. это ж 21й век, ну как без потоков то, когда 21й век на дворе.

i36_zubov
()
Ответ на: комментарий от anonymous

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

i36_zubov
()
Ответ на: комментарий от anonymous

Задумка ТС, чтобы работало:

(setf state (my-eval '(let ((getter-and-setter 
                      (let ((local 1))
                        (list
                         (lambda () local)
                         (lambda (x) (setf local x) (suspend-request))))))
                 (let ((getter (first getter-and-setter))
                       (setter (second getter-and-setter)))
                   (funcall setter 2)
                   (values 1 (funcall getter)))))))
......
.....
......
(my-eval state) ; выполняется из места, где выполнен (suspend-request)

Можно считать, что это call/cc «для бедных» (нормальный, к сожалению, в CL не завезли).

monk ★★★★★
()
Ответ на: комментарий от monk

А, ясно, вы тут по схеме угараете)) Не завезли и не надо)

Честно говоря, городить такие костыли ради этого никто не будет

anonymous
()
Ответ на: комментарий от monk

Я тоже начинал со шланга. А нет. Ни хера. Рано или поздно приходим к страдалити и отсосалити

i36_zubov
()
Ответ на: комментарий от I-Love-Microsoft

Да. Хотя при чем тут это. У меня есть в лапах каденс, по знакомству могут дать их конкурентов.

Но лядь! Они застряли в 90х. Мне нужно свое тактирование(я придумал способ малочувствительный к перекосу фронтов) и т.д. а эти среды до кучи не понимают нихера. Каково получить netlist в виде таблицы(таблицы, карл!) Причем белые буквы на черном фоне. Они охерели так хамить. У них весь layout и так выглядит как пиксельная блевотина но видимо трэш должен поступать

i36_zubov
()
Ответ на: комментарий от i36_zubov

Раз уж я прознал про твое дело, то я благословляю тебя на эту работу и искренне желаю успехов :)

I-Love-Microsoft ★★★★★
()
Ответ на: комментарий от i36_zubov

Слушай, ты серьезно предлагаешь парсер на лиспе писать?

А есть разница, на чём писать? На лиспе будет медленней, но проще отлаживать (можно в любой точке посмотреть все структуры или исправить функцию прямо по ходу выполнения).

monk ★★★★★
()
Ответ на: комментарий от i36_zubov

Дак уже есть парсеров на лиспе куча, будет ещё один.

Я похоже понял, что надо вместо or и and завести my-or и my-and. Также будет примитив «ожидать Х», где X - это, скажем, ключевое слово или динамически вычисляемое множество ожидаемых слов, как в случае кейвордов.

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

den73 ★★★★★
() автор топика
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.