ПО, ЭВМ и АСУ из Таможенного Союза

Информация о пользователе

Привет, Гость! Войдите или зарегистрируйтесь.


Вы здесь » ПО, ЭВМ и АСУ из Таможенного Союза » контекстно-свободные грамматики » Рекурсивный спуск (recursive descent), LL(k)


Рекурсивный спуск (recursive descent), LL(k)

Сообщений 1 страница 16 из 16

1

Читаем русскоязычную статью в википедии:
https://ru.wikipedia.org/wiki/Метод_рекурсивного_спуска

Как проверить, является ли некая грамматика LL(k)-грамматикой?
Как привести грамматику к нужному виду, если она пока не является LL(k) грамматикой, но может ей стать?
Что хотя бы прочитать, чтобы это выяснить?
Не отвечает нам русская википедия.

Английская посылает читать
Aho, Alfred V.; Sethi, Ravi; Ullman, Jeffrey (1986). Compilers: Principles, Techniques and Tools (first ed.). Addison Wesley. p. 183.

Там ещё есть ссылка на книгу Бердж "методы рекурсивного программирования".

https://en.wikipedia.org/wiki/Determini … ee_grammar

In 1965, Donald Knuth invented the LR(k) parser and proved that there exists an LR(k) grammar for every deterministic context-free language.

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

есть ли где-нибудь в интернете викиучебник под свободной лицензией?

https://ru.wikibooks.org/wiki/Реализаци … ого_спуска

это не то, что хотелось найти...

Словосочетания "рекурсивный спуск", "метод рекурсивного спуска" - это неудачные словосочетания, потому что они уводят мысль в сторону. Человек думает "ага, я понимаю что такое рекурсивный вызов функции (это когда она может прямо или непрямо вызвать саму себя), а значит я понимаю, как написать парсер этим способом. Тем более, что в книге дракона приведён скелет на псевдокоде", однако думает этот человек неправильно. Если бы этот метод назывался по-другому, впадения в эту иллюзию бы не было. Такое стало возможно благодаря "кривому"-кривому переводу. Если бы переводили полное словосочетания "метод РАЗБОРА при помощи рекурсивного спуска", может быть придумали бы другое словосочетание. Словосочетание "сентенциальная форма" тоже неудачное.

Отредактировано Лис (2019-01-09 13:03:02)

0

2

Как проверить, является ли некая грамматика LL(k)-грамматикой?

Это должно быть очевидно из рекурсии по отсутствию переборов глубоко вложенных (рекурсивных) вариантов.
Пример:
УровеньУ:
СлучайУ_1: УровеньУ_1
СлучайУ_2: УровеньУ_2
...
иначе ПолярныйЛис

Требуется, чтобы все возможные направления СлучайУ_1, СлучайУ_2,... и т.д. были сразу же взаимно однозначны и вычислимы суммарно за константное время, т.е. не уходили в рек. перебор.
Можно записать и как:
если СлучайУ_1 то УровеньУ_1
иначе если СлучайУ_2 то УровеньУ_2
...
но суть не меняется - переход к любому случаю за константное время.

Как привести грамматику к нужному виду, если она пока не является LL(k) грамматикой, но может ей стать??

Скобочками для всех случаев перебора с углублением.

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

Отредактировано MihalNik (2019-01-03 17:50:28)

0

3

MihalNik написал(а):

а не писать филькины грамматики.

Что написано в Кнуте? Сначала пишем грамматику, а потом проверяем, является ли она LR(k)-грамматикой, способом описанным в "Section II".

Но мне нужно проверить не LR(k), а LL(k), которые более ограниченные.
https://en.wikipedia.org/wiki/LL_grammar
    Every LL(k) grammar is also a LR(k) grammar.

Тут описана пара преобразований (устранение левой рекурсии и правого ветвления):
https://neerc.ifmo.ru/wiki/index.php?title=LL(k)-грамматики,_множества_FIRST_и_FOLLOW
но "После их устранения грамматика всё ещё может остаться не LL(1)-грамматикой."

https://en.wikipedia.org/wiki/LL_grammar
    It is also decidable if a given LR(k) grammar is also a LL(m) grammar for some m.

вот с этого места хотелось бы по-подробнее.

MihalNik написал(а):

можно сразу составлять

Нельзя. Это как в анекдоте "печатаю со скоростью 400 знаков в минуту, но такая фигня получается..."
Когда ты программируешь рекурсивным спуском - фактически программируешь некую грамматику. А не факт, что язык, который ты собрался этой программой разобрать, обладает нужными характеристиками.
Ситуация, когда есть программа-парсер, но нет формальной спецификации языка - неудовлетворительна.
Ведь если язык не специфицирован, то нет гарантий, что со временем не вылезут неоднозначности. И в истории языкостроения уже были инциденты, когда неоднозначности в языке обнаруживались на практике.
Поэтому возможность доказать, что всё хорошо, и что баги из спецификации языка не повылазят - важна.

Отредактировано Лис (2019-01-09 15:05:28)

0

4

Лис написал(а):

Что написано в Кнуте? Сначала пишем грамматику, а потом проверяем, является ли она LR(k)-грамматикой

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

relation  =  "=" | "#" | "<" | "<=" | ">" | ">=" | IN | IS.

Это строчка из синтаксиса КП.
В каком-нибудь любимом Лисом Паскале это можно записать как:

Код:
Relation_s: R:= S('=') or S('#') or S('<=') or S('<') or S('>=') or S('>') or W('IN') or W('IS');

И это будет работать правильно при соответствующем контексте. И сразу же очевидный порядок: вначале подстановка "<=" и ">=", а потом "<" и ">" соответственно.

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

Во-первых, для всех распространенных языков как правило уже известно, обладают они определенного класса грамматикой или нет. Во-вторых, нет ничего страшного, если где-то возникнет углубленный перебор, поэтому класс грамматики вообще не особо важен - парсер все равно ее разберет. В-третьих, не трудно и переделать и так, что парсер найдет любые неоднозначности для конкретного кода. Для примера выше это замена жадного оператора 'or' на добавление в список/массив с перебором всех последующих альтернатив независимо от совпадения предыдущих. Так что даже если грамматика допускает неоднозначности - все равно можно грамотно программировать на таком языке, неудовлетворящем фантазиям Кнута, если язык предоставляет механизм исправления любых неоднозначностей, например, скобками.

P.S.:

Лис написал(а):

Это как в анекдоте "печатаю со скоростью 400 знаков в минуту, но такая фигня получается..."

В анекдоте было 1000 или 1500. 400 - вполне достижимая средняя скорость набора обычных текстов.

Отредактировано MihalNik (2019-01-09 16:21:35)

0

5

Да, грамматика в рек. спуске при построении языка легко редактируется до очевидно однозначной. Берем еще пример из КП:

statement  =  [assignment | ProcedureCall | IfStatement | CaseStatement |
  WhileStatement | RepeatStatement | ForStatement].
assignment  =  designator ":=" expression.
ProcedureCall  =  designator [ActualParameters].

На самом деле IfStatement | CaseStatement | WhileStatement | RepeatStatement | ForStatement  начинаются с уникальных ключевых слов. Переносим их в statement:

statement  =  [assignment | ProcedureCall | IF IfStatement | CASE CaseStatement |
  WHILE WhileStatement | REPEAT RepeatStatement | FOR ForStatement].
assignment  =  designator ":=" expression.
ProcedureCall  =  designator [ActualParameters].

Аналогично переносим начала assignment и ProcedureCall:

statement  =  [designator ":=" | designator ProcedureCall | IF IfStatement | CASE CaseStatement |
  WHILE WhileStatement | REPEAT RepeatStatement | FOR ForStatement].
assignment  =  expression.
ProcedureCall  =  [ActualParameters].

Очевидно, разбор statement однозначен (знак присваивания и ключевые слова уникальны). Но если нам лениво разбирать ветвления/перетаскивать куски грамматики в свой язык, мы можем просто добавить пару ключевых слов, например,  LET и CALL.
Очевидную однозначность можно создать, например,3-мя след. способами:
1) Однозначностью выбора внутри каждого выражения без необходимости углубления в нетерминалы.
2) Уникальными ключевыми словами/знакосочетаниями в начале каждого выражения.
3) Простым программным кодом без трюков, который в отличие от грамматики будет в принципе всегда однозначен. IF|THEN|ELSE|WHILE|REPEAT и скобки.

Отредактировано MihalNik (2019-01-09 17:50:26)

0

6

http://вече.программирование-по-русски.рф/viewtopic.php?t=110&p=430#p435

MihalNik написал(а):

Лиса волнует русскоязычное программирование или Лиса волнует "некий способ перехода" к нему? Т.е. цель или путь?
Потому что то, что Лис предлагает (аналог YACC) - путь бесцельный. Когда можно напрямую вместо грамматики писать более точный, однозначый сразу исполняемый код на любом языке, а затем переписать на разрабатываемом.
Ах, да YACC же ничего в русском языке не умеет. Но, действительно, кого волнуют проблемы англоязычных авторов?

Итак, что предлагает MihalNik ?
1) написать код методом рекурсивного спуска на любом языке (например взять реализацию Павиа)
2) превести этот код на русский язык

Это не работающий метод. Потому что это "искусство". А должна быть "технология".
Технология от искусства отличается тем, что она объяснима.

По уму надо делать так:
1) писать грамматику
2) преобразовывать грамматику документированными способами, с сохранением всех этапов преобразований с целью объяснения почему оно так.
3) генерировать по результирующей грамматике парсер (можно и рекурсивным спуском)

Для преобразования грамматик нужна утилита, потому что преобразовывать руками - долго и муторно с точки зрения оформления.
Представим, что доказательство (путь преобразования) нужно выкладывать в HTML (для интернета) и в pdf (для электронных библиотек).
Любое изменение в синтаксисе и в ходе преобразований грамматики приведёт к большой ручной работе по преобразованию и HTML и pdf. Совершенно очевидно, что руками делать - смерти подобно.

Если же не подходить технологически, а методом "искусства", то получится кирпич-архив, которой годится только для распальцовок.

Отредактировано Лис (2019-02-13 17:16:44)

0

7

Зато если подходить технологически, получится очередной безликий язык, просто с кириллическим синтаксисом. Тогда проще всего ключевые слова в каком-нибудь tcc локализовать, получится кириллический си (ну и добавить поддержку кириллических идентификаторов). Технологии описаны, книжек полно, бери читай, пиши. YACC-bison-...-profit.

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

Отредактировано atzx (2019-06-22 16:24:05)

0

8

По уму надо делать так:
1) писать грамматику
2) преобразовывать грамматику документированными способами, с сохранением всех этапов преобразований с целью объяснения почему оно так.
3) генерировать по результирующей грамматике парсер (можно и рекурсивным спуском)

Сравниваем еще раз - символ грамматики:

relation  =  "=" | "#" | "<" | "<=" | ">" | ">=" | IN | IS.

и кусок оператора выбора:

Relation_s: R:= S('=') or S('#') or S('<=') or S('<') or S('>=') or S('>') or W('IN') or W('IS');

Что мешает добавить пояснения к программному коду и зачем тогда п.1 и п.3? А если бы Лис присмотрелся внимательно, то заметил бы, что код законченный, корректный и однозначный с точки зрения рекурсивного спуска.

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

И чем муторнее сразу написать указанный программный код по сравнению с соответствующей грамматикой?

Отредактировано MihalNik (2019-02-13 19:30:57)

0

9

atzx написал(а):

рекурсивный спуск и LL(k) грамматики - это достаточно разные вещи. Так что заголовок темы странный.

Мы можем (мне кажется, что можем) проверить, равно k единице или не равно.

далее цитирую:
https://studfiles.net/preview/2544556/page:4/

если удалось получить LL(1)-грамматику, то для построения анализатора можно использовать метод рекурсивного спуска без возвратов

0

10

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

Отредактировано atzx (2019-06-22 16:27:10)

0

11

atzx написал(а):

Вот о чём говорится в цитате

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

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

0

12

магическо-искуссным способом

Менее магическим, чем предлагает Лис.

каким образом и почему она именно такая

MihalNik взял кусочек готовой грамматики существующего языка, сопоставил ему программный код и не увидел никакого смысла записи грамматики в изначальном виде.

исходная грамматика, написанная понятным образом

И снова тот же вопрос: чем способ записи РБНФ понятнее записи кода?

0

13

Не знаю что имеет ввиду Лис, но сравнивать РБНФ и код парсера - всё равно что сравнивать код на С и соответствующий ассемблерный листинг.

Отредактировано atzx (2019-02-13 21:12:21)

0

14

Не знаю что имеет ввиду Лис, но сравнивать РБНФ и код парсера - всё равно что сравнивать код на С и соответствующий ассемблерный листинг.

Возвращаемся к #9 и сравниваем - аналогия не раскрыта. Как-то синтаксис Паскаля (в отличии от С) не намного сложнее РБНФ. Да и весь он не нужен (например, можно написать рекурсией без спец. операторов-циклов) и необходимое подмножество сопоставимо с РБНФ. Лис же предлагает решать проблемы однозначности, которых в коде просто не существует - компилятор гарантирует это. Паскаль, который практически все учат в средней школе - по сложности как ассемблер? Серьезно?

Отредактировано MihalNik (2019-02-13 22:05:45)

0

15

MihalNik написал(а):

аналогия не раскрыта

Всё там раскрыто. Аналогия здорого человека.

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

0

16

А РБНФ это русский, ага. Как будто "Паскалей" с русскими ключевыми словами - нет. Слабая отмазка и оффтоп.

Отредактировано MihalNik (2019-02-14 13:32:44)

0


Вы здесь » ПО, ЭВМ и АСУ из Таможенного Союза » контекстно-свободные грамматики » Рекурсивный спуск (recursive descent), LL(k)