Какие современные компьютерные языки относятся к LL(1)?

(Во время каникул я занимаюсь языковой теорией. Извините, если это наивный вопрос.)

Согласно здесь:

Грамматики LL, особенно грамматики LL(1), представляют большой практический интерес, поскольку синтаксические анализаторы для этих грамматик легко построить, и по этой причине многие компьютерные языки разработаны как LL(1).

Итак, из любопытства, какие современные компьютерные языки являются LL(1)? Попадают ли в эту категорию C, Java, C# или Python?


person smwikipedia    schedule 01.01.2017    source источник
comment
Я прочитал очень интересную статью именно на эту тему [и с использованием грамматик и регулярных выражений] и думал, что добавил ее в закладки, но, к сожалению, это не так. Кажется, я думаю, что Perl там наверху. (извините за этот бесполезный комментарий)   -  person Martin    schedule 01.01.2017
comment
@Martin Perl определенно не LL (1). Разбор Perl неразрешим.   -  person sepp2k    schedule 01.01.2017
comment
Тогда это обратная сторона, Perl находится внизу (LL4?). Интересно, что это единственный язык, написанный лингвистом, и поэтому на нем гораздо проще писать человеку, чем читать машине (сравнительно говоря).   -  person Martin    schedule 01.01.2017
comment
Лучше задать этот вопрос на Lambda the Ultimate. См. похожий вопрос Хорошие языки с простой грамматикой.   -  person Guy Coder    schedule 01.01.2017
comment
@martin: (k) в LL(k) относится к количеству прогнозных токенов, которые вам могут понадобиться, чтобы решить, что делать с текущим токеном. Многие языки вообще не являются LL, и на практике увеличение значения k редко помогает, хотя вы можете увеличить мощность синтаксического анализа LL, если разрешите k быть неограниченным (см., например, ANTLR). В этом случае синтаксический анализатор больше не работает с линейным временем, и вам может понадобиться более мощный алгоритм, такой как LR.   -  person rici    schedule 01.01.2017
comment
Ааа я совсем неправильно понял. Ранее я читал о сложности грамматики компьютерных языков, и они были по шкале от 1 до 4, и я думаю, что на них ссылается LL, это была интересная ссылка, которую я не мог сослаться. Извините за путаницу.   -  person Martin    schedule 01.01.2017


Ответы (2)


Думаю, у меня возникнет соблазн пометить эту цитату из Википедии с помощью [необходима цитата]; предположения, по крайней мере, сомнительны. Например, существует большое количество инструментов для создания компиляторов на основе yacc, которые чрезвычайно упрощают , на практике для создания синтаксического анализатора с использованием более мощного (и такого же быстрого) алгоритма LALR, а некоторые также реализуют еще более мощный (и почти такой же быстрый в большинстве практических грамматик) алгоритм GLR. Синтаксические анализаторы рукописного ввода не были нужны десятилетиями. [Примечание 1]

Пытаясь ответить на вопрос:

  1. Большинство компьютерных языков «технически» не являются LL, потому что они даже не являются контекстно-свободными. Сюда входят языки, требующие объявления идентификаторов (C, C++, C#, Java и т. д.), а также языки с препроцессорами и/или средствами макросов (среди прочих, C и C++), языки с неоднозначностями, которые могут быть только решена с использованием семантической информации (Perl был бы худшим преступником здесь, но C и C++ также находятся на первом месте). И, чтобы еще больше развеселить окружающих, он также включает в себя языки, поддерживающие компоновку наподобие Python (разумеется, Python, а также Haskell).

    Я ставлю пугающие кавычки вокруг «технически», потому что есть много людей, которые скажут, что эти исключения «не считаются». Это их мнение, и они имеют на него право (во всяком случае, я перестал с этим спорить, хотя и не разделяю этого мнения). Вы можете, например, исключить препроцессор из C/C++, предварительно обработав текст перед применением грамматики, или предварительно обработать языки, поддерживающие пробелы, вставив токены INDENT, NEWLINE и DEDENT вместо пробела, после чего вы можете сделать какое-то утверждение. о мистическом «основном языке». (Это гораздо сложнее применить к шаблонам C++, которые можно устранить только путем синтаксического анализа текста, поэтому вы не можете утверждать, что расширение может быть выполнено до синтаксического анализа.)

    Утверждение, что язык не является контекстно-свободным, поскольку требует объявления идентификаторов, возможно, немного более спорно. В некоторых языках (кроме C/C++, в которых требуется семантический анализ, чтобы избежать двусмысленности) вы можете утверждать, что дерево синтаксического анализа может быть построено без проверки правила объявления, что делает это правило «несинтаксическим». Но остается тот случай, когда вы можете применять правило объявления синтаксически; вы просто не можете сделать это с помощью контекстно-свободной грамматики (вы можете использовать грамматику Ван Вейнгардена, для пример).

    В любом случае распространенная стратегия синтаксического анализа состоит в том, чтобы распознать надмножество языка, а затем отклонить несоответствующие программы посредством последующего анализа результирующего дерева синтаксического анализа; в этом случае возникает вопрос, является ли надмножество LL. На мой взгляд, для того, чтобы это имело смысл, необходимо, чтобы каждую допустимую программу можно было разобрать в правильное синтаксическое дерево, что исключает использование семантического анализа для устранения неоднозначности.

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

    Например, вы можете распознавать алгебраические выражения, используя простой обычный язык:

    (PREFIX* OPERAND POSTFIX*) (INFIX PREFIX* OPERAND POSTFIX*)*
    

    где PREFIX — это набор префиксных операторов, а также (, POSTFIX — это набор постфиксных операторов (если они есть), а также ), INFIX — это набор инфиксных операторов (сложение, вычитание, умножение и т. д.), а OPERAND — это идентификатор или константа.

    Это регулярное выражение не будет корректно отклонять выражения с несбалансированными круглыми скобками, но мы уже согласились, что можно распознавать надмножество языка, верно? :-)

    При желании мы могли бы удалить круглые скобки из наборов PREFIX и POSTFIX и сделать OPERAND рекурсивным производством. Результирующая грамматика тривиально LL(1) [Примечание 2]:

    operand    => IDENTIFIER | CONSTANT | '(' expression ')'
    term       => operand | PREFIX operand | operand POSTFIX
    expression => term | term INFIX expression
    

    Проблема, однако, в том, что эта грамматика не фиксирует приоритет оператора. Он даже не пытается распознать тот факт, что a+b*c и a*b+c являются суммами, так что оператором верхнего уровня является + в обоих случаях, а не тот оператор, который стоит первым в выражении. (Если бы вы разбирали APL, это не было бы проблемой. Но большинство языков соответствуют обычным ожиданиям в отношении приоритета операторов.)

    Этот момент важен, потому что грамматика LL не может обрабатывать леворекурсивные продукции, а вам нужна леворекурсивная продукция, чтобы правильно анализировать левоассоциативный оператор. (То есть, чтобы правильно анализировать a-b-c как ((a-b)-c), а не (a-(b-c)), у которого было бы другое значение.) Опять же, вы могли бы возразить, что это придирка, поскольку вы могли бы постобработать дерево разбора, чтобы исправить ассоциативность. Это, безусловно, верно, но в результате грамматика, которую вы используете для анализа, отличается от грамматики, которую вы используете для объяснения синтаксиса языка. Это может или не может беспокоить вас.

    Здесь стоит добавить, что существуют языки (Haskell, Prolog и многие другие), в которых вы можете определять операторы и их приоритет в тексте программы. Это, очевидно, делает невозможным создание правильного синтаксического дерева без семантического анализа, и обычный подход к разбору таких языков состоит в том, чтобы использовать именно упрощенный язык «чередующихся операндов и операторов», описанный выше. Как только все приоритеты операторов известны, предположительно в конце синтаксического анализа, все выражения повторно анализируются с использованием чего-то вроде алгоритма маневровой станции, чтобы произвести правильный синтаксический анализ.

  3. Давайте предположим, что мы отбросим приведенные выше возражения и просто спросим: «Для каких широко используемых языков программирования можно использовать синтаксический анализатор LL?»

    Тем не менее, чтобы избежать мошенничества, мы действительно должны требовать, чтобы синтаксический анализатор LL имел фиксированный просмотр вперед и не требовал обратного отслеживания. Если вы разрешаете произвольный поиск вперед и назад, то вы значительно расширяете область анализируемых языков, но я утверждаю, что вы больше не находитесь в области ЯЛ. Это устранит, например, подмножество C++ без шаблонов и препроцессоров, даже несмотря на то, что общие реализации компиляторов используют синтаксические анализаторы рекурсивного спуска, поскольку для разрешения "Самый неприятный анализ" неоднозначность. (В любом случае, вы не можете удалить шаблоны из C++, а синтаксический анализ с помощью шаблонов действительно сложен. [Примечание 3])

    И Java, и Python были спроектированы так, чтобы LL (1) «псевдоразборчиво». Я считаю, что Haskell тоже попадает в эту категорию. C не может быть правильно проанализирован без семантической информации (классическая двусмысленность: является ли (x)*(y) приведением или умножением? - это зависит от того, было ли x определено типом или объявлено как переменная), но я почти уверен, что его можно захватить с помощью Парсер рекурсивного спуска без возврата. Я не изучал C# подробно, но этот ответ Эрика Липперта предполагает, что в некоторых случаях требуется откат.

Примечания

  1. Конечно, люди все еще делают это, и во многих случаях по уважительным причинам (например, для создания более качественных сообщений об ошибках). Но «создать анализатор LALR сложно» не веская причина, поскольку это не так.

  2. Это не совсем LL, потому что я не левый фактор. Но это достаточно легко сделать; Я оставлю это в качестве упражнения.

  3. См. раздел Является ли C++ контекстно-зависимым или контекстно-зависимым?. Также классическая работа Тодда Вельдхуизена Шаблоны C++ завершены по Тьюрингу

person rici    schedule 01.01.2017
comment
Утверждение, что язык не является контекстно-свободным, потому что он требует объявления идентификаторов, возможно, немного более спорно. Почему то, что тривиально верно, должно быть спорным? - person John Coleman; 01.01.2017
comment
@JohnColeman: я воздержусь от очевидной политической аналогии и оставлю вас читать ветки комментариев в моих сообщениях на эту тему. Я также был удивлен, что это было спорным. - person rici; 01.01.2017
comment
Для меня это похоже на тот факт, что физические компьютеры не являются универсальными машинами Тьюринга, потому что у них конечная память. Тривиально верно, но полностью согласующееся с тем фактом, что машины Тьюринга дают хорошее представление о физических компьютерах. Точно так же реальные языки программирования почти никогда не бывают контекстно-свободными, но эта тривиальная истина полностью согласуется с тем фактом, что контекстно-свободные грамматики дают хороший способ думать о многих таких языках. - person John Coleman; 01.01.2017
comment
@johncoleman: конечно, и синтаксический анализатор cfg является частью любого синтаксического анализатора C++. Но это далеко не вся история. (Python, который не требует объявлений, на самом деле является CF по модулю чувствительности макета. Но внимательное изучение грамматики, используемой для синтаксического анализа, по сравнению с грамматикой, используемой для объяснения, показывает некоторые ограничения синтаксического анализа LL. Возможно, мне следует добавить ссылка на это) - person rici; 02.01.2017
comment
Ваш технически кажется основанным на неспособности отличить язык от синтаксиса языка. Время летит как банан — это синтаксически правильное предложение, которое не имеет семантического смысла. То, что синтаксис языка - LL(1), не требует, чтобы все синтаксически допустимые тексты программ также были допустимыми программами. Кроме того, тот факт, что некоторые символы языка имеют специальное лексическое представление (отступы в Python), не означает, что язык не является LL(1). Вы упоминаете VW-грамматики. VW-грамматики различают произвольное представление и грамматические терминальные символы. - person LHP; 24.12.2019
comment
Кроме того, VW-грамматики довольно специфичны, поскольку они являются языками, эквивалентными Тьюрингу, и как таковые могут выражать полную семантику языка программирования. (как показано Кливлендом и Узгалисом в их книге «Грамматики языков программирования».) - person LHP; 24.12.2019
comment
@LHP Вот что я тоже задавался вопросом. Я постараюсь добавить еще немного пояснений. давайте иметь действительно простой язык, включающий определение целочисленных переменных и их вывод, каждая инструкция заканчивается символом ; как { вар myvar = 10; распечатать myvar2; }. Поскольку любая инструкция начинается либо с var, либо с print, это LL(1): каждый тип выражения может быть выведен по его первому токену. При построении AST мы не делаем, если переменная объявлена ​​при ее печати: PROG[DCL[myvar, 10], PRINT[myvar2]. Наконец, при анализе AST для сборки программы мы проверяем наличие переменных и возвращаем ошибку. - person Felix Bertoni; 21.08.2020
comment
Это похоже на расширенный комментарий, а не на ответ. Вы правы в том, что такие языки, как C и C++, являются контекстно-зависимыми, поскольку им требуется семантическая информация для разрешения синтаксических неоднозначностей. Но можно утверждать, что все языки, требующие объявления идентификаторов, являются контекстно-зависимыми. Например, Паскаль требует объявлений, но это семантическое требование — эти объявления не нужны ни для разрешения синтаксических неоднозначностей, ни для построения дерева синтаксического анализа. - person Adrian McCarthy; 30.06.2021
comment
@adrian: я мог бы добавить Pascal в список в пункте 3, если его можно разобрать вплоть до проверки объявлений с помощью грамматики LL (я сделал это только с помощью синтаксического анализатора LR, но, вероятно, он есть в списке). Но я настаиваю на том, что это не LL в формальном языковом смысле, потому что входные данные с необъявленными переменными неправильно сформированы. Вы построили дерево синтаксического анализа для псевдоанглийского *All mimsy was the borogoves но я думаю, что я не единственный, кто сказал бы, что это грамматическая, а не семантическая ошибка, не зная, что такое borogove или как его распознать лицемерие... - person rici; 30.06.2021
comment
Отчасти в этом смысл рифмы, а также то, что Хомский пытался выразить грамматически правильным высказыванием о бесцветных зеленых идеях. Точно так же мне на самом деле не нужно ничего знать о семантике программы на Паскале, чтобы заметить, что идентификатор не находится в области видимости. На мой взгляд, это больше похоже на несоответствие между подлежащим и глаголом (синтаксическое), чем на семантические нарушения вроде яростного сна. Как я уже сказал, я согласен с тем, что не каждый будет подходить к синтаксису с этой точки зрения, но именно так я выучил термины как математический лингвист, и я думаю... - person rici; 30.06.2021
comment
это правильный ответ. Но я написал пункт 3 с учетом вашего возражения. Ничто в ответе не подразумевает, что синтаксические анализаторы LL или LR бесполезны. Только то, что они не могут захватить все правильно сформированное (что не то же самое, что может взорваться во время выполнения с неправильным вводом). - person rici; 30.06.2021
comment
Fwiw, я думаю, что критика LL в пункте 2 должна быть достаточной независимо от грамматического соглашения. - person rici; 30.06.2021
comment
@rici: Возможно ли, чтобы только конечная генеративная грамматика охватывала все «правильное построение» для полного языка программирования по Тьюрингу? Стандарт C++, например, определяет правильно сформированную программу как программу C++, созданную в соответствии с правилами синтаксиса, диагностируемыми семантическими правилами и правилом одного определения. Возможно ли вообще выразить что-то вроде правила одного определения в грамматике? Или это изначально семантическое ограничение? (Кстати, я считаю предложения о борогровах и зеленых идеях грамматически правильной чушью.) - person Adrian McCarthy; 01.07.2021
comment
@adrian: конечно, генеративные грамматики завершены. Если вы ограничите их одним нетерминалом в левой части, то C++ будет невозможен, но вы сможете определить правильно сформированный awk без особых проблем. Что касается borogoves, по крайней мере, в моем идиолекте, версия, которую я предоставил, не была грамматической, как это предложение. Я думаю, что это общепринятый взгляд на английскую грамматику, и именно этого Хомский надеялся достичь. - person rici; 01.07.2021

Если я рассматриваю компьютерные языки шире, чем языки программирования, я считаю, что есть несколько декларативных языков, которые вы могли бы рассмотреть, хотя это также может зависеть от того, что вы считаете современным.

Возможные кандидаты:

  • XML
  • LLVM ИК
  • многие языки конфигурации, такие как файлы INI
  • некоторые сетевые протоколы
  • некоторые форматы данных, например файлы CSV

Большинство (все?) разновидностей языков, описывающих регулярные выражения, не являются регулярными выражениями, а являются LL(1).

Для реальных языков программирования все они, вероятно, слишком стары, чтобы считаться современными, и многие из них имеют общие расширения, которые, вероятно, нарушают LL(k).

  • языки ассемблера
  • БАЗОВЫЙ
  • ЛИСП
  • Языки вирта: Pascal, Modula, Oberon
  • Пролог

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

person Adrian McCarthy    schedule 30.06.2021