Действительно ли грамматика D контекстно-свободна?

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


Грамматика языка D явно контекстно-свободна.

Однако грамматика C ++ (даже без макросов ). (Прочтите внимательно!)

Разрешено: Я ничего не знаю (официально) о компиляторах, лексерах и синтаксических анализаторах. Все, что я знаю, это то, что я узнал в сети.
И вот что (я думаю) я понял относительно контекста, на не очень техническом жаргоне:

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

Или, даже с меньшей строгостью:

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

Так, например, C ++ не проходит контекстно-свободный тест, потому что значение confusing<sizeof(x)>::q < 3 > (2) зависит от значения q.

Все идет нормально.

Теперь мой вопрос: можно ли то же самое сказать о D?

В D хэш-таблицы могут быть созданы с помощью объявления Value[Key], например

int[string] peoplesAges;   // Maps names to ages

Статические массивы можно определять с помощью аналогичного синтаксиса:

int[3] ages;   // Array of 3 elements

А шаблоны можно использовать, чтобы запутать их:

template Test1(T...)
{
    alias int[T[0]] Test;
}

template Test2(U...)
{
    alias int[U] Test2;  // LGTM
}

Test1!(5) foo;
Test1!(int) bar;
Test2!(int) baz;  // Guess what? It's invalid code.

Это означает, что я не могу определить значение T[0] или U, просто взглянув на него (т. Е. Это может быть число, это может быть тип данных или это может быть кортеж Бог знает- Какие). Я даже не могу сказать, является ли выражение грамматически правильным (поскольку int[U] определенно не является - у вас не может быть хэш-таблицы с кортежами в качестве ключей или значений).

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

Учитывая это, действительно ли D не зависит от контекста, или я неправильно понимаю концепцию?

Почему / почему нет?


Обновлять:

Я просто подумал, что прокомментирую: действительно интересно увидеть ответы, так как:

  • В некоторых ответах утверждается, что C ++ и D не могут быть контекстно-зависимыми.
  • В некоторых ответах утверждается, что C ++ и D оба контекстно-независимы.
  • Некоторые ответы подтверждают утверждение, что C ++ контекстно-зависимый, а D - нет.
  • Никто еще не утверждал, что C ++ контекстно-независимый, а D контекстно-зависимый :-)

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


person user541686    schedule 08.08.2011    source источник
comment
Итак, какое отношение это имеет к c ++?   -  person BЈовић    schedule 08.08.2011
comment
@VJo: сравнивает грамматику D с грамматикой C ++ (контекстно-зависимая и контекстно-зависимая). Я пометил C ++ вместо другого языка, потому что сайт D сравнивает себя с C ++.   -  person user541686    schedule 08.08.2011
comment
А, напоминает мне формальные языки и теорию автоматов в колледже.   -  person    schedule 08.08.2011
comment
Я не думаю, что int [T] и int [0] неоднозначны - идентификаторы, вероятно, не могут начинаться с цифр, и вы, вероятно, не можете использовать там переменные, поэтому либо тип [тип (идентификатор)], либо тип [размер (постоянный литерал )], разрешимый с просмотром вперед по одному токену. Имеет ли это смысл применительно к конкретным типам, анализатор не должен определять.   -  person Cat Plus Plus    schedule 08.08.2011
comment
Мы говорим о шаблонах или о синтаксисе в целом? Я думаю, что в D есть и указатели, и умножение, так что же такое a * b?   -  person Bo Persson    schedule 08.08.2011
comment
@Cat Plus Plus: в языке D тип может использовать любые вычисляемые выражения во время компиляции.   -  person BCS    schedule 08.08.2011
comment
Шаблоны и макросы C ++ полезны, но значительно затрудняют реализацию компилятора.   -  person umlcat    schedule 08.08.2011
comment
@ Бо Перссон; В качестве операторов a*b; и a*b=exp; оба анализируются как объявления указателя. Если вы поместите a*b в позицию r-значения, это станет выражением умножения. Хотя это не отражено в документированной грамматике, оно четко определено и может быть решено на уровне грамматики.   -  person BCS    schedule 08.08.2011
comment
@BCS - Хорошо, значит, грамматика не зависит от контекста, вы просто не можете отличить это от грамматики? Я понимаю, почему был задан этот вопрос. :-)   -  person Bo Persson    schedule 08.08.2011
comment
@Bo Persson: этого не скажешь по документированной грамматике. : b   -  person BCS    schedule 08.08.2011
comment
весело, как здесь каждый представляет свою собственную теорию информатики. Я очень хочу знать, кто прав. Я понятия не имею. Лучше не гадать.   -  person Johannes Schaub - litb    schedule 09.08.2011
comment
Нам нужно, чтобы Уолтер ответил на этот вопрос.   -  person Arlen    schedule 10.08.2011
comment
Проблема здесь в том, что неформальное определение контекста в OP и идея о том, что такое грамматика, недостаточно строги. Мы также не можем ввести «смысл». Формальные грамматики - это математические концепции, которые абсолютно ничего не знают о значении, а определение языка программирования имеет гораздо большее значение, чем формальная грамматика. Грамматика, рассматриваемая с одной стороны, - это набор правил, которые позволяют вам решить, является ли длинная строка (текст программы) синтаксически допустимой. Если он действителен, он все еще может не иметь смысла, может не компилироваться, но если он недействителен, это определенно чушь.   -  person Cecil Ward    schedule 07.03.2017
comment
Если смотреть в противоположном направлении, формальная грамматика говорит вам, как сгенерировать каждую синтаксически правильную программу. Для этого вы берете абстрактные символы, упомянутые в левой части одного из правил грамматики, и заменяете его некоторым расширенным элементом по вашему выбору, который соответствует правилу расширения в правой части правила, и есть некоторые символ как знак равенства между ними. Замена похожа на поиск и замену в текстовом редакторе. Текст для замены может быть любым, если он следует шаблону, который является правой частью этого правила.   -  person Cecil Ward    schedule 07.03.2017
comment
Если вы знакомы с регулярными выражениями при поиске и замене в вашем любимом текстовом редакторе или другом редакторе или в языках программирования, то вы это уже видели. Теперь вы знаете все, что нужно знать о формальных грамматиках. Контекстно-свободная грамматика - это грамматика, в которой правила могут быть только этого очень простого типа замены, и нет разрешенных условий, когда вы могли бы сказать `` заменить таким образом, если lhs встречается в этом синтаксическом контексте '', но заменить что-то другое, если в другой синтаксический контекст.   -  person Cecil Ward    schedule 07.03.2017
comment
Пример: неконтекстное правило всегда будет выглядеть как X - ›Y, что означает, что всякий раз, когда вы видите X, вы можете заменить его на Y при создании программы, неконтекстное правило может выглядеть примерно как X b -› Y но c X d - ›Z может сказать вам, как раскрыть X, то есть« чем заменить X », при создании действующей программы, и он говорит, что в одном контексте, когда X имеет перед ним a и ab после него, затем замените его на Y, но во втором контексте замените его на Z. Итак, когда вы видите X, вам нужно осмотреться, чтобы знать, что делать.   -  person Cecil Ward    schedule 07.03.2017
comment
Теперь, когда вы анализируете программу, как при компиляции, вы должны как-то работать с этим в другом направлении, чтобы посмотреть на свой текст, посмотреть, выглядит ли он как потенциальная правая часть в конкретном правиле, если она соответствует этой правой стороне, вы заменяете свой текст с левым. В неконтекстной грамматике вам нужно беспокоиться о том, чтобы lhs были в правильном контексте, поэтому обработка более трудна. Бесконтекстные грамматики популярны, потому что их легко реализовать.   -  person Cecil Ward    schedule 07.03.2017
comment
Я должен был указать раньше, что правая струны могут быть более длинными. Всегда должны быть какие-то правила, называемые «терминалами». Они говорят, что some lhs равняется одной буквальной строке. При создании программы вы закончили, когда вы заменили все терминалами. При анализе вы начинаете с сопоставления вещей в тексте вашей программы с каждым из терминалов, а затем выполняете обратные замены rhs- ›lhs против правил, пока не дойдете до lhs некоторого« корневого »правила, если хотите, это lhs можно было бы назвать например ПРОГРАММА. Тогда вы закончили анализ, и программа действительна.   -  person Cecil Ward    schedule 07.03.2017
comment
Если на каком-то этапе вы не можете найти ни одного правила, правая часть которого соответствует вашему тексту, то вы мертвое мясо. В вашей программе есть синтаксическая ошибка, и она недействительна в соответствии с формальной грамматикой. В случае неконтекстной грамматики контекст, указанный в lhs, также должен соответствовать ситуации. У вас могут быть грамматики со все большей и большей общностью в том смысле, что они допускают более мощные, сложные и выразительные правила. Это называется Иерархией Хомского. Повышение уровня мерзости с точки зрения проблемы «синтаксического анализа» (выполнение синтаксического анализа), в результате чего автор компилятора становится сплошной свиньей.   -  person Cecil Ward    schedule 07.03.2017
comment
Мои извинения, как полегчало. Надеюсь, что это все равно будет кому-нибудь полезно. Википедия - ваш друг. Надеюсь, я понял все правильно, но упустил много деталей.   -  person Cecil Ward    schedule 07.03.2017


Ответы (7)


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

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

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

Говоря, что C ++ не является контекстно-независимым, люди имеют в виду, что это разделение невозможно удобным способом (с удобным включением в качестве критериев "почти соответствует описанию официального языка" и "мой инструмент генератора синтаксического анализатора поддерживает такое разделение "; разрешение грамматики быть неоднозначным, а неоднозначность разрешена семантической проверкой - это относительно простой способ сделать сокращение для C ++ и полностью следовать стандарту C ++, но это неудобно, когда вы полагаетесь на инструменты, которые не допускают неоднозначной грамматики, когда у вас есть такие инструменты, это удобно).

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

person AProgrammer    schedule 08.08.2011
comment
+1 за то, что разделение между проверками чистого синтаксиса и семантической проверкой является несколько произвольным выбором. - person BCS; 08.08.2011
comment
Извините, но я не знаю, что означают терминальное и генеративное. Но в любом случае я действительно смущен вашим утверждением: На практике ни один язык программирования не является контекстно-свободным, потому что такие вещи, как «переменная должна быть объявлена ​​до того, как она будет использована», не могут проверяться с помощью контекстно-свободной грамматики. Разве это не семантическая проблема (неразрешенная ссылка) по сравнению с синтаксической проблемой (я не могу понять, что это попытка умножить две вещи, даже если одна из них не существует)? - person user541686; 08.08.2011
comment
Грамматики - это способ описания набора строк терминалов (набор терминалов - это алфавит). Генеративная грамматика - это разновидность грамматики, с которой вы знакомы. Одна из моих точек зрения заключалась в том, что разделение на лексическое, синтаксическое и семантическое оставляют большую часть на усмотрение и в основном обусловлено простотой описания и доступностью инструментов. И второй основной момент заключался в том, что контекстно-свободный язык имеет формальное определение грамматики и, не указывая точно, что вы готовы продвигать на лексической и семантической фазе, вы находитесь в области, которую вы знаете, что я имею в виду. - person AProgrammer; 08.08.2011
comment
Не тот ответ, который я искал, но, похоже, пока лучший, +1. - person user541686; 09.08.2011
comment
Хотя этот ответ правильный, некоторых он может сбить с толку, потому что AProgrammer использует язык Хомского, где грамматика генерирует строку (строка создается грамматикой) вместо более естественного понятия синтаксического анализа: в компиляторе мы не заботимся о генерации строк, мы заботимся только об обратном процессе интерпретации строки. - person Qwertie; 09.06.2012

Свойство быть контекстно-свободным - очень формальная концепция; вы можете найти определение здесь. Обратите внимание, что это применимо к грамматикам: язык считается контекстно-свободным, если существует хотя бы одна контекстно-свободная грамматика, которая его распознает. Обратите внимание, что могут быть и другие грамматики, возможно, не связанные с контекстом, которые распознают тот же язык.

В основном это означает, что определение языкового элемента не может меняться в зависимости от того, какие элементы его окружают. Под элементами языка я имею в виду такие понятия, как выражения и идентификаторы, а не конкретные экземпляры этих понятий внутри программ, такие как a + b или count.

Попробуем построить конкретный пример. Рассмотрим этот простой оператор COBOL:

   01 my-field PICTURE 9.9 VALUE 9.9.

Здесь я определяю поле, то есть переменную, размер которой рассчитан на хранение одной целой цифры, десятичной точки и одной десятичной цифры с начальным значением 9.9. Неполная грамматика для этого может быть:

field-declaration ::= level-number identifier 'PICTURE' expression 'VALUE' expression '.'
expression ::= digit+ ( '.' digit+ )

К сожалению, допустимые выражения, которые могут следовать за PICTURE, не являются теми же действительными выражениями, которые могут следовать за VALUE. Я мог бы переписать вторую продукцию в своей грамматике следующим образом:

'PICTURE' expression ::= digit+ ( '.' digit+ ) | 'A'+ | 'X'+
'VALUE' expression ::= digit+ ( '.' digit+ )

Это сделает мою грамматику контекстно-зависимой, потому что expression будет другим, в зависимости от того, был ли он найден после 'PICTURE' или после 'VALUE'. Однако, как было указано, это ничего не говорит о базовом языке. Лучшей альтернативой было бы:

field-declaration ::= level-number identifier 'PICTURE' format 'VALUE' expression '.'
format ::= digit+ ( '.' digit+ ) | 'A'+ | 'X'+
expression ::= digit+ ( '.' digit+ )

который не зависит от контекста.

Как видите, это сильно отличается от вашего понимания. Учитывать:

a = b + c;

Вы очень мало можете сказать об этом утверждении, не просматривая объявления a, b и c, на любом из языков, для которых это утверждение является допустимым, однако это само по себе не означает, что какой-либо из этих языков не является контекст свободный. Возможно, вас сбивает с толку тот факт, что свобода контекста отличается от двусмысленности. Это упрощенная версия вашего примера на C ++:

a < b > (c)

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

identifier assignment identifier binary-operator identifier semi-colon

В некоторых случаях вы можете разрешить двусмысленность, введя контекстную чувствительность на уровне грамматики. Я не думаю, что это относится к приведенному выше неоднозначному примеру: в этом случае вы не можете устранить двусмысленность, не зная, является ли a шаблоном или нет. Обратите внимание: когда такая информация недоступна, например, когда она зависит от конкретной специализации шаблона, язык предоставляет способы устранения неоднозначности: вот почему вам иногда приходится использовать typename для ссылки на определенные типы в шаблонах или использовать template, когда вы вызовите шаблоны функций-членов.

person Nicola Musatti    schedule 08.08.2011
comment
Я не вижу, что в вашем коде cobol не является контекстно-зависимым (то, что следует за PICTURE, является предложением изображения, то, что следует за VALUE, является арифметическим выражением, но связь недостаточно велика, чтобы быть возможной в бесконтекстной грамматика). a<b>(c) с другой стороны, чтобы иметь другое дерево синтаксического анализа в зависимости от объявления a, такие вещи болезненны, когда это возможно, вставлять CFG. - person AProgrammer; 08.08.2011
comment
Ваш пример на COBOL неверен. Это совершенно законно в контекстно-свободной грамматике. Тот факт, что синтаксический элемент может иметь несколько интерпретаций, не означает, что язык контекстно-зависимый. Посмотрите определение или прочтите некоторые другие ответы здесь для получения более подробной информации. - person hammar; 08.08.2011
comment
This is ambiguous [...] however it isn't a proof that C++ is not context free ... ты как бы оставил меня в подвешенном состоянии в своем заключении. : \ Кроме того, я не совсем понимаю, что вы пытаетесь сказать, когда вы говорите a program construct cannot have a different meaning according to where it appears - ›Разве мое использование int[T[0]] не имеет другого значения (массив или карта) в зависимости от того, где оно используется? Они по-разному определены в синтаксисе D, не так ли? - person user541686; 08.08.2011
comment
@Mehrdad: Смысл (он же семантика) не имеет ничего общего с контекстной чувствительностью языка, которая является чисто синтаксической концепцией. - person hammar; 08.08.2011
comment
@hammar: Но это действительно похоже на синтаксическую проблему. Взгляните на BasicType2 в разделе декларации грамматики D: it заявляет, что DeclaratorSuffix может быть либо [ AssignExpression ], либо [ Type ]. Когда вы прослеживаете возможности AssignExpression (просто щелкайте первый элемент каждый раз ... для этого требуется около дюжины щелчков), вы в конечном итоге попадаете на _ 6_ - в котором есть Identifier, что неоднозначно с _8 _... no? - person user541686; 08.08.2011
comment
@Mehrdad: Грамматика, не зависящая от контекста, действительно может быть неоднозначной, поскольку строка типа int[T[0]] может быть проанализирована несколькими способами. Вы можете определить, что это правильный синтаксис, не глядя на контекст, а не на то, что он означает. В контекстно-зависимом языке даже достоверность такого синтаксического элемента зависит от его контекста. - person hammar; 08.08.2011
comment
@hammar: Не могли бы вы привести пример на C ++, где выражение становится синтаксически недействительным из-за контекста? Я думаю, это очень поможет. - person user541686; 08.08.2011
comment
@Mehrdad: Такой пример продемонстрировал бы, что C ++ контекстно-зависимый, чего я не думаю. Я не знаю таких примеров. - person hammar; 08.08.2011
comment
@hammar: Как насчет stackoverflow.com/q/1172939/541686? Кстати, забавно, как вы говорите, что оба C ++ и D контекстно-независимы, тогда как AProgrammer говорит, что no язык контекстно-свободный ... - person user541686; 08.08.2011
comment
@Mehrdad позвольте нам продолжить обсуждение в чате - person hammar; 08.08.2011
comment
@hammar: lol Я вообще-то на работе, к сожалению, сейчас не могу болтать. : \ - person user541686; 08.08.2011
comment
Кстати, я склонен согласиться с комментариями тех, кто проголосовал против :-(. Я постараюсь исправить свой ответ позже. - person Nicola Musatti; 08.08.2011
comment
@Mehrdad: Этот пример показывает только двусмысленность, а не достоверность или недействительность. семантика того, что только что сделал этот вызов, не меняет того факта, что он действителен для C ++. - person Puppy; 08.08.2011
comment
Надеюсь, я ответил на (действительные!) Возражения, высказанные в некоторых комментариях, не убирая того, что заставило некоторых по достоинству оценить мой первоначальный ответ. - person Nicola Musatti; 09.08.2011

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

Во-первых, понятие грамматики довольно интуитивно понятно. Представьте себе набор правил:

S -> a T
T -> b G t
T -> Y d
b G -> a Y b
Y -> c
Y -> lambda (nothing)

И представьте, что вы начинаете с S. Заглавные буквы не являются терминалами, а маленькие буквы - терминалами. Это означает, что если вы получите предложение всех терминалов, вы можете сказать, что грамматика сгенерировала это предложение как «слово» в языке. Представьте себе такие замены с приведенной выше грамматикой (заменяется фраза между * фразами *):

*S* -> a *T* -> a *b G* t -> a a *Y* b t -> a a b t

Итак, я мог создать aabt с этой грамматикой.

Хорошо, вернемся к основной строке.

Предположим, простой язык. У вас есть числа, два типа (int и string) и переменные. Вы можете выполнять умножение целых чисел и сложение строк, но не наоборот.

Первое, что вам понадобится, это лексер. Это обычно обычная грамматика (или равнозначная ей, DFA или, в равной степени, регулярное выражение), которая соответствует программным токенам. Их принято выражать в регулярных выражениях. В нашем примере:

(Я придумываю эти синтаксисы)

number: [1-9][0-9]*    // One digit from 1 to 9, followed by any number
                       // of digits from 0-9
variable: [a-zA-Z_][a-zA-Z_0-9]*  // You get the idea. First a-z or A-Z or _
                                  // then as many a-z or A-Z or _ or 0-9
                                  // this is similar to C
int: 'i' 'n' 't'
string: 's' 't' 'r' 'i' 'n' 'g'
equal: '='
plus: '+'
multiply: '*'

whitespace: (' ' or '\n' or '\t' or '\r')*   // to ignore this type of token

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

Тогда вам понадобится парсер. Синтаксический анализатор обычно является контекстно-свободной грамматикой. Грамматика без контекста означает, что в грамматике есть только один нетерминал в левой части правил грамматики. В примере в начале этого ответа правило

b G -> a Y b

делает грамматику контекстно чувствительной, потому что слева у вас b G, а не только G. Что это значит?

Что ж, когда вы пишете грамматику, каждый из нетерминалов имеет значение. Напишем для нашего примера неконтекстную грамматику (| означает или. Как если бы в одной строке было написано много правил):

program -> statement program | lambda
statement -> declaration | executable
declaration -> int variable | string variable
executable -> variable equal expression
expression -> integer_type | string_type
integer_type -> variable multiply variable |
                variable multiply number |
                number multiply variable |
                number multiply number
string_type -> variable plus variable

Теперь эта грамматика может принять этот код:

x = 1*y
int x
string y
z = x+y

Грамматически это правильный код. Итак, вернемся к тому, что означает «контекстно-свободный». Как вы можете видеть в приведенном выше примере, когда вы раскрываете executable, вы генерируете один оператор формы variable = operand operator operand без какого-либо учета того, в какой части кода вы находитесь. Будь то самое начало или середина, определены ли переменные или нет, или совпадают ли типы, вы не знаете, и вам все равно.

Далее вам нужна семантика. Здесь в игру вступили контекстно-зависимые грамматики. Во-первых, позвольте мне сказать вам, что на самом деле никто не пишет контекстно-зависимую грамматику (потому что ее анализ слишком сложен), а скорее битовые фрагменты кода, которые синтаксический анализатор вызывает при синтаксическом анализе ввода (называемые подпрограммами действий. Хотя это не единственный способ). Формально, однако, вы можете определить все, что вам нужно. Например, чтобы убедиться, что вы определяете переменную перед ее использованием, вместо этого

executable -> variable equal expression

у вас должно быть что-то вроде:

declaration some_code executable -> declaration some_code variable equal expression

однако более сложно убедиться, что variable в объявлении совпадает с вычисляемым.

В любом случае, я просто хотел дать вам идею. Итак, все эти вещи контекстно-зависимы:

  • Проверка типа
  • Количество аргументов функции
  • значение по умолчанию для работы
  • если member существует в obj в коде: obj.member
  • Почти все, что не похоже: отсутствует ; или }

Надеюсь, вы поняли, в чем разница (если нет, я буду более чем счастлив объяснить).

Итак, в итоге:

  • Lexer использует обычную грамматику для токенизации ввода
  • Parser использует контекстно-свободную грамматику, чтобы убедиться, что программа имеет правильную структуру.
  • Семантический анализатор использует контекстно-зависимую грамматику для проверки типов, сопоставления параметров и т. Д.

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

Например, один язык, который я не помню, использовал подписку на массив и вызов функции как в круглых скобках, и поэтому он требовал, чтобы синтаксический анализатор искал тип (контекстно-зависимый материал) переменной и определял, какое правило (function_call или array_substitution) взять.

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

Чтобы перейти к вашему вопросу! В приведенном вами примере ясно, что грамматика C ++ не является контекстно-зависимой. Я понятия не имею о языке D, но теперь вы можете рассуждать о нем. Подумайте об этом так: в контекстно-свободной грамматике нетерминал может расширяться, не принимая во внимание ничего, НО структуру языка. Подобно тому, что вы сказали, оно расширяется, никуда не «глядя».

Знакомый пример - естественные языки. Например, на английском вы скажете:

sentence -> subject verb object clause
clause -> .... | lambda

Ну, sentence и clause здесь нетерминалы. С помощью этой грамматики вы можете составить следующие предложения:

I go there because I want to

or

I jump you that I is air

Как видите, второй имеет правильную структуру, но не имеет смысла. Если речь идет о грамматике, свободной от контекста, значение не имеет значения. Он просто заменяется verb на любой глагол, не «глядя» на остальную часть предложения.

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

person Shahbaz    schedule 01.11.2011
comment
показать, в чем разница между контекстно-свободной грамматикой и контекстно-зависимой, всего 3 строчки: D. Проголосуйте за усилия по созданию стены текста. Контекстно-зависимая грамматика - это грамматика, которая расширяет конкретную комбинацию терминальных / нетерминальных до другой последовательности, контекстно-зависимая грамматика просто расширяет 1 нетерминал до последовательности - person CoffeDeveloper; 01.02.2015
comment
@Dariooo, это может быть даже одна строка, если вы выразите это математически, но большинству людей все еще нужно объяснение, чтобы понять. ;) спасибо за голос - person Shahbaz; 02.02.2015

В лексере D есть конструкция:

string ::= q" Delim1 Chars newline Delim2 "

где Delim1 и Delim2 - совпадающие идентификаторы, а Chars не содержит новую строку Delim2.

Эта конструкция зависит от контекста, поэтому грамматика лексера D зависит от контекста.

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

person Ellery Newcomer    schedule 16.08.2011
comment
Факт, который я помню из теории языка, заключается в том, что идентификаторы sameidentifier в целом не зависят от контекста. Перед публикацией я на всякий случай пропустил приведенное выше правило производства через лемму о перекачке. - person Ellery Newcomer; 16.08.2011
comment
Проголосовали, потому что это единственная реальная попытка ответить на вопрос. - person johncip; 06.06.2013

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

Нет, это совершенно неправильно. Грамматика не может быть контекстно-свободной, если вы не можете определить, является ли она выражением, просто взглянув на нее и текущее состояние парсера (нахожусь ли я в функции, в пространстве имен и т. Д.).

Тип выражения, однако, является семантическим значением, а не синтаксическим, и синтаксический анализатор и грамматика не дают ни цента о типах или семантической достоверности или о том, можете ли вы иметь кортежи в качестве значений или ключей в хэш-картах, или если вы определил этот идентификатор перед его использованием.

Грамматике все равно, что это значит, и имеет ли это смысл. Его волнует только то, что это есть.

person Puppy    schedule 08.08.2011
comment
@Hans: Совершенно уверен, что context - это только такие вещи, как «Какие идентификаторы были объявлены до сих пор?». Грамматики LALR не зависят от контекста, но они по-прежнему используют состояние синтаксического анализатора для определения значения. - person Puppy; 08.08.2011
comment
Ничто не мешает генерировать строку двумя разными нетерминалами в CFG. Таким образом, простой взгляд на эту строку не будет указывать на то, из какого нетерминального она сгенерирована. Вам нужен какой-то контекст. Но контекст, который следует учитывать, более локализован в CFG, чем в более мощной грамматике. - person AProgrammer; 08.08.2011
comment
@DeadMG: The grammar doesn't care what it means, or if that makes sense. It only cares about what it is. - ›Я запуталась. Значит, вы имеете в виду, что omg(!) следует синтаксису D? Все в этой строке выглядит правильно, за исключением того, что восклицательный знак не является идентификатором или числом ... - person user541686; 08.08.2011
comment
Я не знаю достаточно о синтаксисе D, чтобы знать о данной строке. - person Puppy; 08.08.2011

Чтобы ответить на вопрос, является ли язык программирования контекстно-свободным, вы должны сначала решить, где провести грань между синтаксисом и семантикой. В качестве крайнего примера, в C недопустимо, чтобы программа использовала значения некоторых видов целых чисел после того, как им было разрешено переполнение. Ясно, что это нельзя проверить во время компиляции, не говоря уже о времени синтаксического анализа:

void Fn() {
  int i = INT_MAX;
  FnThatMightNotReturn();  // halting problem?
  i++;
  if(Test(i)) printf("Weeee!\n");
}

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

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

Учитывая, что наиболее правильной спецификацией для синтаксиса D является синтаксический анализатор (IIRC - синтаксический анализатор LL), я сильно подозреваю, что он на самом деле контекстно-свободный по определению, которое я предложил.


Примечание: приведенное выше ничего не говорит о том, какую грамматику использует языковая документация или данный парсер, только если существует контекстно-свободная грамматика. Кроме того, единственной полной документацией на языке D является исходный код компилятора DMD.

person BCS    schedule 08.08.2011
comment
+1 интересный момент о синтаксисе и семантике. Однако: Можете ли вы правильно и однозначно определить дерево синтаксического анализа всех правильных программ, используя контекстно-свободную грамматику - ›Поскольку ассоциативные массивы и массивы статического размера не являются частью одного и того же дерева синтаксического анализа (они определены с другим синтаксисом), они требуют потенциальных преобразований при использовании в шаблоне, в зависимости от контекста. Конечно, компилятор все компилирует правильно, но исходное дерево разбора некорректно. Это вне контекста? Не для меня, но я не уверен. - person user541686; 08.08.2011
comment
Что касается моего примера переполнения; Мне пришлось бы проверить, но может быть даже незаконным допускать переполнение, даже если его результат никогда не используется. OTOH, который не меняет сути примера. - person BCS; 08.08.2011
comment
@Mehrdad: все, что подразумевает, - это то, что грамматика документа (которая заведомо неверна: например, a*b=c; неоднозначна в этой грамматике, но однозначно анализируется DMD) не является контекстно-свободной. С некоторыми тривиальными поправками к грамматике вы можете объединить произведения, чтобы они были однозначными. Вы не обязательно будете знать, что это за [] , но вы правильно его проанализируете. - person BCS; 08.08.2011
comment
@BCS В D a*b=c; - это объявление переменной b как указателя на a, инициализированного выражением c. Это так по определению; если a не разрешается к какому-либо типу, программа имеет неправильный формат, даже если a * b в качестве выражения умножения вернет что-то, что можно было бы присвоить c. Для назначенного выражения умножения вам понадобятся круглые скобки. Может быть, это все еще не контекстно-зависимое, но a*b=c; не является неопределенным утверждением. - person Bolpat; 10.12.2019
comment
@Bolpat IIRC, когда я опубликовал этот комментарий ~ 9 лет назад, грамматика в документации технически позволяла синтаксический анализ MulExp = Exp;. Что касается того, как компилятор интерпретировал это, вы, тем не менее, правы; это однозначно. Что, собственно, и было моей точкой зрения; грамматика в документации и грамматика в исходном коде не совпадали. - person BCS; 30.01.2020

От этих ответов у меня болит голова.

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

В C ++ (порядок может быть отключен, но это не должно отменять мою точку зрения):

  1. он должен обрабатывать макросы и другие вещи препроцессора
  2. он должен интерпретировать шаблоны
  3. наконец он интерпретирует ваш код.

Поскольку первый шаг может изменить контекст второго шага, а второй шаг может изменить контекст третьего шага, язык, на котором ВЫ пишете (включая все эти шаги), зависит от контекста.

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

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

person Jason McCarrell    schedule 08.08.2011
comment
Хотел бы я видеть этот вопрос 3 года назад, пройдя курсы по компиляторам и т. Д. Я помню, как хорошо разбирался в этом в школе, но без особого интереса к компиляторам, боюсь, я не изучал их много лет. - person Jason McCarrell; 08.08.2011