Является ли C ++ контекстно-зависимым или контекстно-зависимым?

405

Я часто слышу заявления о том, что C ++ является контекстно-зависимым языком. Возьмите следующий пример:

a b(c);

Это определение переменной или объявление функции? Это зависит от значения символа c. Если cэто переменная , то a b(c);определяет переменную с именем bтипа a. Это напрямую инициализируется с c. Но если cэто тип , то a b(c);объявляет функцию с именем, bкоторая принимает cи возвращает a.

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

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

Итак, является ли C ++ контекстно-зависимым или контекстно-зависимым?

fredoverflow
источник
12
@CarlNorum Пожалуйста, покажите мне одно грамматическое правило C ++, которое не состоит из единственного нетерминального символа в левой части, и я сразу же поверю вам.
fredoverflow
9
IIUC, это немного зависит от того, где вы проводите черту для чувствительности к контексту. Я думаю, что я видел, как люди утверждают, что почти все статически типизированные языки программирования являются контекстно-зависимыми, не потому, что вы не можете создать для них практический компилятор с помощью инструментов синтаксического анализа CFG, а потому, что такие реализации «обманывают», анализируя некоторые недопустимые программы и только отклонив их позже, во время проверки типа. Таким образом, если вы считаете, что плохо напечатанные программы не на языке (в смысле CS, то есть на наборе строк), парсер должен принять, больше языков, чем C ++, являются контекстно-зависимыми.
6
@DeadMG: Нет, вы не правы. В теории формального языка вообще нет «разбора» или «семантики», только «язык», представляющий собой набор строк.
jpalecek
27
Пока нет ответов, которые действительно касались вашего определения «контекстно-свободной грамматики». На мой взгляд, правильный ответ на этот вопрос либо ссылается на продукцию в приложении А, которая не соответствует вашему определению, либо демонстрирует, что ваше определение неверно или недостаточно. Стоять на своем!
Гонки легкости на орбите
8
См. Действительно ли грамматика D не зависит от контекста? , На самом деле, я думаю, что все здесь должны прочитать этот вопрос и его ответы!
Гонки легкости на орбите

Ответы:

341

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

Поэтому я утверждаю, что C ++ не является ни контекстно-зависимым, ни контекстно-зависимым .

Если вы разрешаете произвольные последовательности символов на обеих сторонах любого производства, вы создаете грамматику типа 0 («неограниченную») в иерархии Хомского , которая является более мощной, чем контекстно-зависимая грамматика; неограниченные грамматики полны по Тьюрингу. Контекстно-зависимая (Тип-1) грамматика допускает несколько символов контекста в левой части продукции, но тот же контекст должен появляться в правой части продукции (отсюда и название «контекстно-зависимая»). [1] Контекстно-зависимые грамматики эквивалентны линейно ограниченным машинам Тьюринга .

В примере программы простое вычисление может быть выполнено с помощью машины Тьюринга с линейными ограничениями, поэтому она не вполне доказывает эквивалентность Тьюринга, но важная часть заключается в том, что синтаксическому анализатору необходимо выполнить вычисления для выполнения синтаксического анализа. Это могли быть любые вычисления, выражаемые как создание экземпляра шаблона, и есть все основания полагать, что создание экземпляра шаблона C ++ является Turing-complete. См., Например, статью Тодда Л. Вельдхуйзена за 2003 год .

В любом случае, C ++ может быть проанализирован компьютером, поэтому он может быть проанализирован машиной Тьюринга. Следовательно, неограниченная грамматика может распознать это. На самом деле написание такой грамматики было бы непрактичным, поэтому стандарт не пытается это сделать. (См. ниже.)

Проблема с «неоднозначностью» некоторых выражений - это в основном красная сельдь. Начнем с того, что двусмысленность - это особенность определенной грамматики, а не языка. Даже если можно доказать, что в языке нет однозначных грамматик, если его можно распознать по контекстно-свободной грамматике, он не зависит от контекста. Точно так же, если он не может быть распознан контекстно-свободной грамматикой, но он может быть распознан контекстно-зависимой грамматикой, он является контекстно-зависимым. Неоднозначность не актуальна.

Но в любом случае, как строка 21 (т.е. auto b = foo<IsPrime<234799>>::typen<1>();) в программе ниже, выражения вовсе не являются двусмысленными; они просто анализируются по-разному в зависимости от контекста. В самом простом выражении проблемы синтаксическая категория определенных идентификаторов зависит от того, как они были объявлены (например, типы и функции), что означает, что формальному языку придется признать тот факт, что две строки произвольной длины в одна и та же программа идентична (декларация и использование). Это может быть смоделировано грамматикой «копирования», которая является грамматикой, которая распознает две последовательные точные копии одного и того же слова. Это легко доказать с помощью леммы прокачкичто этот язык не является контекстно-свободным. Для этого языка возможна контекстно-зависимая грамматика, а в ответе на этот вопрос приведена грамматика типа 0: /math/163830/context-sensitive-grammar-for-the- язык копирования .

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

То, что выглядит как формальная грамматика в стандарте C ++, не является полным формальным определением синтаксиса языка C ++. Это даже не полное формальное определение языка после предварительной обработки, которое может быть легче формализовать. (Однако это не был бы язык: язык C ++, как определено стандартом, включает в себя препроцессор, и операция препроцессора описывается алгоритмически, поскольку это было бы чрезвычайно трудно описать в любом грамматическом формализме. Это в этом разделе стандарта, где описывается лексическое разложение, включая правила, в которых оно должно применяться более одного раза.)

Различные грамматики (две накладывающиеся грамматики для лексического анализа, одна из которых имеет место перед предварительной обработкой, а другая, если необходимо, впоследствии, плюс «синтаксическая» грамматика), собраны в Приложении A с этим важным примечанием (выделение добавлено):

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

Наконец, вот обещанная программа. Строка 21 синтаксически правильна тогда и только тогда, когда N in IsPrime<N>простое. Иначе, typenявляется целым числом, а не шаблоном, поэтому typen<1>()анализируется как (typen<1)>()синтаксически неверный, поскольку ()не является синтаксически допустимым выражением.

template<bool V> struct answer { answer(int) {} bool operator()(){return V;}};

template<bool no, bool yes, int f, int p> struct IsPrimeHelper
  : IsPrimeHelper<p % f == 0, f * f >= p, f + 2, p> {};
template<bool yes, int f, int p> struct IsPrimeHelper<true, yes, f, p> { using type = answer<false>; };
template<int f, int p> struct IsPrimeHelper<false, true, f, p> { using type = answer<true>; };

template<int I> using IsPrime = typename IsPrimeHelper<!(I&1), false, 3, I>::type;
template<int I>
struct X { static const int i = I; int a[i]; }; 

template<typename A> struct foo;
template<>struct foo<answer<true>>{
  template<int I> using typen = X<I>;
};
template<> struct foo<answer<false>>{
  static const int typen = 0;
};

int main() {
  auto b = foo<IsPrime<234799>>::typen<1>(); // Syntax error if not prime
  return 0;
}

[1] Чтобы выразить это более технически, каждое производство в контекстно-зависимой грамматике должно иметь форму:

αAβ → αγβ

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

Это можно прочитать A → γтолько в контексте [α, β]. В контекстно-свободной (тип 2) грамматике αи βдолжен быть пустым.

Оказывается, вы также можете ограничить грамматику с помощью «монотонного» ограничения, где каждое произведение должно иметь форму:

α → βгде |α| ≥ |β| > 0  ( |α|означает "длина α")

Можно доказать, что набор языков, распознаваемых монотонными грамматиками, точно такой же, как набор языков, распознаваемых контекстно-зависимыми грамматиками, и часто бывает проще основывать доказательства на монотонных грамматиках. Следовательно, довольно часто можно увидеть, что «контекстно-зависимый» используется, как если бы он означал «монотонный».

RICi
источник
27
Таким образом, он не только зависит от контекста, но также может зависеть от любого контекста, который вы можете выразить в шаблонах, которые являются полными по Тьюрингу.
щенок
7
@mehrdad, ОП говорит «контекстно-зависимый язык», а не контекстно-зависимая грамматика. Неоднозначность - это особенность грамматики, а не языка. Язык действительно контекстно-зависимый, но не потому, что конкретная грамматика для него неоднозначна.
Ричи
2
Обратите внимание, что мой пример не является двусмысленным. Это однозначное выражение действительной программы. Если вы измените значение в строке 21, оно может стать неправильным. Но ни в том, ни в другом случае это не является двусмысленным.
Ричи
5
У меня есть одно сомнение: как вы показываете, результат оценки шаблона может иметь значение между правильно сформированной и плохо сформированной программой. Оценка шаблона завершена. Таким образом, для правильного определения того, является ли строка в языке (C ++), требуется полнота по Тьюрингу? Как вы говорите, контекстно-зависимый язык - это «просто» линейный ограниченный автомат, который не является полным по Тьюрингу AFAIK. Или ваш аргумент использует ограничения, которые стандарт C ++ накладывает на некоторые вещи, включая глубину оценки шаблона?
4
@AntonGolov: Моя оригинальная версия этого примера сделала именно это (вы можете добиться этого, вставив 0внутрь (), для простого), но я думаю, что это более интересно, потому что это демонстрирует, что вам нужно создание экземпляра шаблона даже для распознавания, если строка является синтаксически правильной программой C ++. Если обе ветви компилируются, то мне пришлось бы работать усерднее, чтобы оспорить аргумент, что разница "семантическая". Любопытно, что хотя мне часто приходится давать определение «синтаксическому», никто никогда не предлагал определения «семантического», кроме «материала, который я не считаю синтаксическим» :)
rici
115

Во-первых, вы правильно заметили, что в грамматике в конце стандарта C ++ нет контекстно-зависимых правил, поэтому грамматика не зависит от контекста.

Однако эта грамматика точно не описывает язык C ++, потому что она производит программы не на C ++, такие как

int m() { m++; }

или

typedef static int int;

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

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

  • двусмысленный
  • нельзя разобрать с бизоном
  • не LL (k), LR (k), LALR (k) или любой другой языковой класс, определенный парсером, который они выбрали

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

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

jpalecek
источник
7
ambiguity doesn't have anything to do with context-sensitivityЭто тоже была моя интуиция, поэтому я рад видеть кого-то (а) согласного и (б) объяснить, где я не мог. Я полагаю, что это дисквалифицирует все аргументы, которые основаны a b(c);, и частично удовлетворяет первоначальному вопросу, предпосылкой которого были «часто слышимые» утверждения о чувствительности к контексту из-за неоднозначности ... особенно, когда для грамматики фактически нет двусмысленности даже в MVP.
Гонки легкости на орбите
6
@KonradRudolph: Стандарт гласит: «Существует количество, определяемое реализацией, которое определяет ограничение общей глубины рекурсивных реализаций, которое может включать более одного шаблона. Результат бесконечной рекурсии в экземплярах не определен». (14.7.1p15) Я понимаю, что это означает, что реализация не обязана понимать каждую действительную программу на С ++, а не то, что программы с слишком большой глубиной рекурсии недопустимы. Единственными, которые помечены как недействительные, являются те, которые имеют бесконечную глубину рекурсии.
Ричи
3
@KonradRudolph: я спорю, что это «общая справка». Тот факт, что я прочитал эту довольно сложную статью и недостаточно разбираюсь в ней, чтобы разобраться в этом небольшом факте, должен быть достаточным для демонстрации этого. Это не так, как если бы вы сказали что-то вроде «компьютеры обычно используют электричество» или «биты могут быть истинными или ложными».
Гонки легкости на орбите
3
Если бы этот факт действительно был настолько широко известен, я думаю, что было бы намного легче найти ссылку на него, чем долго спорить о том, нужно ли это предоставлять. Не говоря уже о конструктивном.
Сэмюэль Эдвин Уорд
5
Насколько я могу судить, @Konrad ошибся, когда сказал, что «Чувствительный к контексту эквивалентен завершению по Тьюрингу». (по крайней мере, он был, если он обозначал «Рекурсивно перечислимый» словом «Тьюринг завершен»), и с тех пор не смог распознать эту ошибку. Вот ссылка на правильное отношение включения набора, включенное здесь: en.wikipedia.org/wiki/Chomsky_hierarchy
pnkfelix
61

Да. Следующее выражение имеет различный порядок операций в зависимости от типа разрешенного контекста :

Редактировать: Когда фактический порядок операций меняется, это делает невероятно трудным использование «обычного» компилятора, который анализирует недекорированный AST перед его украшением (распространение информации о типе). Другие упомянутые контекстно-зависимые вещи являются «довольно простыми» по сравнению с этим (не то, чтобы оценка шаблона вообще была легкой).

#if FIRST_MEANING
   template<bool B>
   class foo
   { };
#else
   static const int foo = 0;
   static const int bar = 15;
#endif

С последующим:

static int foobar( foo < 2 ? 1 < 1 : 0 > & bar );
Сэм Харвелл
источник
Почему эта проблема не может быть решена так же, как для C, если вспомнить, какие определения типов находятся в области видимости?
Blaisorblade
1
@Blaisorblade: Один из способов сделать компилятор «чистым» - это разделить задачи на независимые шаги в цепочке, такие как создание дерева разбора из входных данных, за которым следует шаг, выполняющий анализ типов. C ++ вынуждает вас либо: 1) объединить эти шаги в один или 2) проанализировать документ в соответствии с обоими / всеми возможными интерпретациями и позволить этапам разрешения типов сузить его до правильной интерпретации.
Сэм Харвелл
@ 280Z28: согласился, но это касается и C; Я думаю, что хороший ответ на этот вопрос должен показать, почему C ++ хуже, чем C. Кандидатская диссертация, связанная здесь, делает это: stackoverflow.com/a/243447/53974
Blaisorblade
26

Чтобы ответить на ваш вопрос, вам нужно выделить два разных вопроса.

  1. Простой синтаксис почти каждого языка программирования не зависит от контекста. Как правило, это дается в виде расширенной формы Бэкуса-Наура или грамматики без контекста.

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

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

Дэн
источник
5
Интересно отметить, что вам часто приходится размещать уровень «простого синтаксиса» ниже, чем вы ожидаете, чтобы получить CFG для вашего языка программирования. Возьмите C, например. Вы можете подумать, что правило грамматики для простого объявления переменной в C будет таким VARDECL : TYPENAME IDENTIFIER, но у вас его не будет, потому что вы не можете отличить имена типов от других идентификаторов на уровне CF. Другой пример: на уровне CF вы не можете решить, анализировать ли это a*bкак объявление переменной ( bс указателем типа на a) или как умножение.
LaC
2
@LaC: Да, спасибо за указание на это! Кстати, я уверен, что есть более часто используемый технический термин для простого синтаксиса . У кого-нибудь правильный термин?
Дан
4
@Dan: то, о чем вы говорите, является приближением языка, заданного некоторой контекстно-свободной грамматикой. Конечно, такое приближение по определению не имеет контекста. В этом смысле «синтаксис» часто используется при обсуждении языков программирования.
reinierpost
13

Возможно, вы захотите взглянуть на дизайн и эволюцию C ++ , Бьярн Страуструп. В нем он описывает свои проблемы, пытаясь использовать yacc (или аналогичный) для анализа ранней версии C ++, и желая, чтобы он использовал вместо этого рекурсивный спуск.

Матиас Вейлер
источник
Вау, спасибо. Интересно, имеет ли смысл подумать об использовании чего-то более мощного, чем CFG, для разбора любого искусственного языка.
Дервин Танк
Отличная книга для понимания причин C ++. Я рекомендую это и Lippman's Inside the C ++ Object Model для понимания того, как работает C ++. Хотя оба немного устарели, они все еще хорошо читаются.
Мэтт Прайс
«Meta-S» - это контекстно-зависимый движок Quinn Tyler Jackson. Я не использовал это, но он рассказывает впечатляющую историю. Проверьте его комментарии в comp.compilers, и посмотрите rnaparse.com/MetaS%20defined.htm
Ира Бакстер
@IraBaxter: ваш x-ref сегодня MIA - и твердые ссылки на программное обеспечение кажутся неуловимыми (поиск в Google не дает хороших ссылок, ни с «site: rnaparse.com meta-s», ни с «quinn jackson meta- s '; есть кусочки, но meta-s.com ведет к неинформативному веб-сайту, например).
Джонатан Леффлер
@Jonathan: некоторое время только что заметил вашу жалобу. Не знаю, почему ссылка плохая, я думал, что это было хорошо, когда я ее написал. Куинн был довольно активным в comp.compilers. Похоже, что Google начинает работать плохо, это все, что я могу найти: groups.google.com/group/comp.compilers/browse_thread/thread/… IIRC, он передал права на MetaS какой-то на Гавайях компании для перепродажи. Учитывая, как странно это было странно, ИМХО это подписание смертного приговора. Похоже, очень умная схема.
Ира Бакстер
12

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

Первый пример:

A*B;

Это выражение умножения?

ИЛИ

Это объявление Bпеременной, чтобы быть указателем типа A?

Если A - переменная, то это выражение, если A - тип, это объявление указателя.

Второй пример:

A B(bar);

Это прототип функции, принимающий аргумент barтипа?

ИЛИ

Является ли это объявить переменную Bтипа Aи вызывает конструктор А с barконстантой в качестве инициализатора?

Вы должны снова знать, barявляется ли переменная или тип из таблицы символов.

Третий пример:

class Foo
{
public:
    void fn(){x*y;}
    int x, y;
};

Это тот случай, когда построение таблицы символов во время синтаксического анализа не помогает, потому что объявление x и y идет после определения функции. Поэтому вам нужно сначала просмотреть определение класса и просмотреть определения методов во втором проходе, чтобы сказать, что x * y - это выражение, а не объявление указателя или что-то еще.

Calmarius
источник
1
A B();является объявлением функции даже в определении функции. Ищите самый неприятный анализ ...
AProgrammer
"Вы не можете построить синтаксическое дерево, просто проанализировав файл" FALSE. Смотри мой ответ.
Ира Бакстер
10

C ++ анализируется с помощью анализатора GLR. Это означает, что во время синтаксического анализа исходного кода анализатор может столкнуться с неоднозначностью, но он должен продолжить и решить, какое грамматическое правило использовать позже .

смотри также,

Почему C ++ не может быть проанализирован с помощью анализатора LR (1)?


Помните, что контекстно-свободная грамматика не может описывать ВСЕ правила синтаксиса языка программирования. Например, грамматика атрибута используется для проверки правильности типа выражения.

int x;
x = 9 + 1.0;

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

арак
источник
4
Большинство синтаксических анализаторов C ++ не используют технологию синтаксического анализа GLR. GCC нет. Некоторые делают. См. Semanticdesigns.com/Products/FrontEnds/CppFrontEnd.html для того, который делает.
Ира Бакстер
10

У меня есть ощущение, что есть некоторая путаница между формальным определением «контекстно-зависимым» и неформальным использованием «контекстно-зависимым». Первый имеет четко определенный смысл. Последний используется для выражения «вам нужен контекст для анализа входных данных».

Это также спрашивается здесь: Контекст-чувствительность против неоднозначности .

Вот контекстно-свободная грамматика:

<a> ::= <b> | <c>
<b> ::= "x"
<c> ::= "x"

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

Омри Барел
источник
Как наличие нескольких символов на левой стороне производства решает эту проблему? Я не думаю, что этот ответ отвечает на вопрос.
user541686
1
Мой ответ в ответ на первое предложение: «Я часто слышу утверждения, что C ++ является контекстно-зависимым языком». Если в этих утверждениях неформально используется выражение «контекстно-зависимое», то проблем нет. Я не думаю, что C ++ формально является контекстно-зависимым.
Омри Барел
Я думаю , что C ++ является формально контекстно-зависимым, но проблема у меня в том , что я не понимаю , как контекстно-зависимая грамматика будет иметь больше успеха при анализе C ++ , чем бы CFG.
user541686
6

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

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

C ++ выходит далеко за рамки этого благодаря своей полной шаблонной системе Turing. См. Вопрос переполнения стека 794015 .

Джеймс Джонс
источник
5

Он является контекстно-зависимым, так как a b(c);имеет два допустимых синтаксических анализа - объявление и переменную. Когда вы говорите «Если cэто тип», это контекст, прямо здесь, и вы точно описали, как C ++ чувствителен к нему. Если у вас не было этого контекста "Что есть c?" Вы не могли бы разобрать это однозначно.

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

Изменить: Есть, конечно, больше вопросов чувствительности к контексту, я просто сосредоточился на том, что вы показали. Шаблоны особенно противны для этого.

щенок
источник
1
Кроме того a<b<c>>d, верно? (Ваш пример на самом деле является классическим из C , где он является единственным препятствием для отсутствия контекста.)
Kerrek SB
Думаю, это скорее проблема лексизма. Но, безусловно, в той же категории, да.
щенок
2
Спрашивающий не спрашивает, насколько он более контекстно-зависимый, чем C, только чтобы показать, что он контекстно-зависимый.
Щенок
Итак ... является ли C ++ более контекстно-зависимым, чем C?
Керрек С.Б.
2
@DeadMG Я не думаю, что вы отвечаете на вопрос (я не думаю, что я тоже). Как наличие терминалов на левой стороне производства решает эту проблему?
user541686
5

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

Для наиболее наглядного примера, давайте рассмотрим раздосадовать Разбор: int f(X);. Если Xэто значение, то оно определяется fкак переменная, которая будет инициализирована X. Если Xэто тип, он определяется fкак функция, принимающая единственный параметр типа X.

Глядя на это с грамматической точки зрения, мы можем рассмотреть это так:

A variable_decl ::= <type> <identifier> '(' initializer ')' ';'

B function_decl ::= <type> <identifier> '(' param_decl ')' ';'

A ::= [declaration of X as value]
B ::= [declaration of X as type]

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

Это все еще довольно сильно отличается от типичной CSG (или, по крайней мере, то, что я о них помню). Это зависит от создаваемой таблицы символов - той части, которая определенно распознает Xкак тип или значение, а не просто некоторый тип оператора, предшествующего этому, но правильный тип оператора для правильного символа / идентификатора.

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

Джерри Гроб
источник
Продукция (без контекста) достаточно хорошо определяет наиболее неприятный синтаксический анализ, чтобы его можно было проанализировать с помощью механизма синтаксического анализа. Это отодвигает проблему принятия решения о том, какая из нескольких интерпретаций действительна до тех пор, пока анализ не будет завершен, но это только упрощает разработку синтаксического анализатора и преобразователя имен, поскольку они являются модульными, а не запутанными, как в обычных синтаксических анализаторах C ++. См. AST для наиболее неприятного анализа: stackoverflow.com/questions/17388771/…
Ира Бакстер
5

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

a<b<c>()

Это можно разобрать как

template
   |
   a < expr > ()
        |
        <
      /   \
     b     c

Или

 expr
   |
   <
 /   \
a   template
     |
     b < expr > ()
          |
          c

Для устранения двух неоднозначных значений AST можно использовать только объявление «a» - первое AST, если «a» является шаблоном, или второе, если нет.

Аарон
источник
Я полагаю, что C ++ 11 требует последней интерпретации, и вы должны добавить парены, чтобы подписаться на первую.
Джозеф Гарвин
1
@ ДжозефГарвин, нет. C ++ обязывает, <если это может быть скобка (например, она следует за идентификатором, который называет шаблон). В C ++ 11 добавлено требование, чтобы >и первый символ >>интерпретировался как закрывающие скобки, если такое использование правдоподобно. Это влияет на синтаксический анализ, a<b>c>где aнаходится шаблон, но не влияет на a<b<c>.
Ричи
@aaron: как это проще чем a();(что либо expr.callили expr.type.conv)?
Ричи
@rici: Ой, я не поняла, что это было асимметрично.
Джозеф Гарвин
5
Вы описываете двусмысленность или контекстную чувствительность?
corazza
4

Было показано, что шаблоны C ++ являются мощными по Тьюрингу. Хотя это и не официальная ссылка, здесь есть место, чтобы взглянуть на это:

http://cpptruths.blogspot.com/2005/11/c-templates-are-turing-complete.html

Я рискну предположить (столь же старый, как фольклорное и краткое доказательство CACM, показывающее, что ALGOL в 60-х годах не может быть представлен CFG) и скажу, что C ++ не может быть правильно проанализирован только CFG. CFGs в сочетании с различными механизмами TP на проходе дерева или во время редукционных событий - это другая история. В общем смысле, из-за проблемы остановки, существует некоторая программа на C ++, которая не может быть показана правильной / неправильной, но тем не менее правильной / неправильной.

{PS - Как автор Meta-S (упомянутый несколькими людьми выше) - я могу с уверенностью сказать, что Thothic не является более не существующей, и программное обеспечение не доступно бесплатно. Возможно, я сформулировал эту версию своего ответа так, чтобы меня не удаляли и не голосовали до -3.}

Куинн Тайлер Джексон
источник
3

C ++ не является контекстно-свободным. Я узнал это некоторое время назад в лекции компиляторов. Быстрый поиск дал эту ссылку, где раздел «Синтаксис или семантика» объясняет, почему C и C ++ не являются контекстно-свободными:

Обсуждение Википедии: Грамматика без контекста

С уважением,
Ованес

Ованес
источник
2

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

Нужно знать, является ли идентификатор именем типа (именем класса, именем, введенным typedef, параметром шаблона typename), именем шаблона или каким-либо другим именем, чтобы можно было правильно использовать идентификатор. Например:

x = (name)(expression);

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

Эта трудность привела к необходимости typenameи templateс зависимыми именами. Насколько я знаю, остальная часть C ++ не является контекстно-зависимой (то есть для нее можно написать контекстно-свободную грамматику).

AProgrammer
источник
2

Meta-S "является контекстно-зависимым механизмом синтаксического анализа Куинн Тайлер Джексон. Я не использовал его, но он рассказывает впечатляющую историю. Посмотрите его комментарии в comp.compilers и посмотрите rnaparse.com/MetaS%20defined.htm - Ира Бакстер 25 июля в 10:42

Правильная ссылка на разбор enigines

Meta-S был собственностью несуществующей компании под названием Thothic. Я могу отправить бесплатную копию Meta-S всем, кто интересуется ею, и я использовал ее в исследованиях анализа РНА. Обратите внимание, что «грамматика псевдоузла», включенная в папки с примерами, была написана программистом, не занимающимся биоинформатикой, и в основном не работает. Мои грамматики используют другой подход и работают довольно хорошо.

STH
источник
На самом деле это интересная находка.
Дервин Танк
0

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

В C / ++ оператор if разрешен только внутри тела функции. Казалось бы, это делает его контекстно-зависимым, верно? Ну нет. Для контекстно-свободных грамматик фактически не требуется свойство, в котором вы можете извлечь некоторую строку кода и определить, является ли она действительной. Это не совсем то, что означает контекстно-свободный. Это действительно просто лейбл, который в некоторой степени подразумевает что-то вроде того, на что это похоже.

Теперь, если оператор в теле функции анализируется по-разному в зависимости от того, что определено вне непосредственных грамматических предков (например, описывает ли идентификатор тип или переменную), как в a * b;случае, то он, на самом деле, является контекстно-зависимым. Здесь нет никакой реальной двусмысленности; он будет проанализирован как объявление указателя, если aтип и умножение в противном случае.

Быть чувствительным к контексту не обязательно означает «трудно разобрать». C на самом деле не так уж и сложен, потому что печально известная a * b;«неоднозначность» может быть решена с помощью таблицы символов, содержащей typedefs, с которой встречались ранее. Для решения этого случая не требуется каких-либо произвольных реализаций шаблонов (которые, как было доказано, выполнено с помощью Turing Complete), как это иногда делает C ++. На самом деле невозможно написать программу на C, которая не будет компилироваться за конечное время, даже если она имеет ту же чувствительность к контексту, что и C ++.

Python (и другие языки, чувствительные к пробелам) также зависит от контекста, так как он требует состояния в лексере для генерирования маркеров отступа и отступа, но это не усложняет анализ, чем обычная грамматика LL-1. На самом деле он использует генератор синтаксических анализаторов, что является частью того, почему в Python есть такие неинформативные сообщения об ошибках синтаксиса. Здесь также важно отметить, что a * b;в Python нет «неоднозначности», подобной проблеме, дающей хороший конкретный пример контекстно-зависимого языка без «неоднозначной» грамматики (как упоминалось в первом абзаце).

Beefster
источник
-4

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

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

Чтобы поставить вопрос о синтаксическом разборе на отдых, я предлагаю эмпирическое доказательство того, что существуют неконтекстные грамматики для C ++, которые можно использовать для создания AST для неконтекстного анализа исходного текста, фактически анализируя его с помощью существующего GLR. -parser-инструмент, основанный на явной грамматике.

Да, это удается, «принимая слишком много»; не все, что она принимает, является допустимой программой на C ++, поэтому за ней следуют дополнительные проверки (проверки типов). И да, проверка типов может столкнуться с проблемами вычислимости. На практике инструменты не имеют этой проблемы; если бы люди писали такие программы, ни одна из них не скомпилировалась бы. (Я думаю, что стандарт на самом деле накладывает ограничение на количество вычислений, которые вы можете выполнить, разворачивая шаблон, поэтому на самом деле вычисления на самом деле конечны, но, вероятно, довольно большие).

Если вы имеете в виду, определите , является ли исходная программа членом набора допустимых исходных программ на C ++ , тогда я соглашусь, что проблема намного сложнее. Но проблема не в разборе .

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

Запуск средства определения имени / типа инструмента по AST с исходным значением 234799 завершается успешно. При значении 234797 преобразователь имен завершается неудачно (как и ожидалось) с сообщением об ошибке «typen не является типом». и, следовательно, эта версия не является допустимой программой C ++.

967 tree nodes in tree.
15 ambiguity nodes in tree.
(translation_unit@Cpp~GCC5=2#6b11a20^0 Line 1 Column 1 File C:/temp/prime_with_templates.cpp
 (declaration_seq@Cpp~GCC5=1021#6b06640^1#6b11a20:1 {10} Line 1 Column 1 File C:/temp/prime_with_templates.cpp
  (pp_declaration_seq@Cpp~GCC5=1022#6b049a0^1#6b06640:1 Line 1 Column 1 File C:/temp/prime_with_templates.cpp
   (declaration@Cpp~GCC5=1036#6b04980^1#6b049a0:1 Line 1 Column 1 File C:/temp/prime_with_templates.cpp
   |(template_declaration@Cpp~GCC5=2079#6b04960^1#6b04980:1 Line 1 Column 1 File C:/temp/prime_with_templates.cpp
   | (template_parameter_list@Cpp~GCC5=2082#6afbde0^1#6b04960:1 Line 1 Column 10 File C:/temp/prime_with_templates.cpp
   |  (template_parameter@Cpp~GCC5=2085#6afbd80^1#6afbde0:1 Line 1 Column 10 File C:/temp/prime_with_templates.cpp
   |   (parameter_declaration@Cpp~GCC5=1611#6afbd40^1#6afbd80:1 Line 1 Column 10 File C:/temp/prime_with_templates.cpp
   |   |(basic_decl_specifier_seq@Cpp~GCC5=1070#6afb880^1#6afbd40:1 Line 1 Column 10 File C:/temp/prime_with_templates.cpp
   |   | (decl_specifier@Cpp~GCC5=1073#6afb840^1#6afb880:1 Line 1 Column 10 File C:/temp/prime_with_templates.cpp
   |   |  (trailing_type_specifier@Cpp~GCC5=1118#6afb7e0^1#6afb840:1 Line 1 Column 10 File C:/temp/prime_with_templates.cpp
   |   |   (simple_type_specifier@Cpp~GCC5=1138#6afb7a0^1#6afb7e0:1 Line 1 Column 10 File C:/temp/prime_with_templates.cpp)simple_type_specifier
   |   |  )trailing_type_specifier#6afb7e0
   |   | )decl_specifier#6afb840
   |   |)basic_decl_specifier_seq#6afb880
   |   |(ptr_declarator@Cpp~GCC5=1417#6afbc40^1#6afbd40:2 Line 1 Column 15 File C:/temp/prime_with_templates.cpp
   |   | (noptr_declarator@Cpp~GCC5=1421#6afbba0^1#6afbc40:1 Line 1 Column 15 File C:/temp/prime_with_templates.cpp
   |   |  (declarator_id@Cpp~GCC5=1487#6afbb80^1#6afbba0:1 Line 1 Column 15 File C:/temp/prime_with_templates.cpp
   |   |   (id_expression@Cpp~GCC5=317#6afbaa0^1#6afbb80:1 Line 1 Column 15 File C:/temp/prime_with_templates.cpp
   |   |   |(unqualified_id@Cpp~GCC5=319#6afb9c0^1#6afbaa0:1 Line 1 Column 15 File C:/temp/prime_with_templates.cpp
   |   |   | (IDENTIFIER@Cpp~GCC5=3368#6afb780^1#6afb9c0:1[`V'] Line 1 Column 15 File C:/temp/prime_with_templates.cpp)IDENTIFIER
   |   |   |)unqualified_id#6afb9c0
   |   |   )id_expression#6afbaa0
   |   |  )declarator_id#6afbb80
   |   | )noptr_declarator#6afbba0
   |   |)ptr_declarator#6afbc40
   |   )parameter_declaration#6afbd40
   |  )template_parameter#6afbd80
   | )template_parameter_list#6afbde0
   | (declaration@Cpp~GCC5=1033#6b04940^1#6b04960:2 Line 1 Column 18 File C:/temp/prime_with_templates.cpp
   |  (block_declaration@Cpp~GCC5=1050#6b04920^1#6b04940:1 Line 1 Column 18 File C:/temp/prime_with_templates.cpp
   |   (simple_declaration@Cpp~GCC5=1060#6b04900^1#6b04920:1 Line 1 Column 18 File C:/temp/prime_with_templates.cpp
   |   |(basic_decl_specifier_seq@Cpp~GCC5=1070#6b048e0^1#6b04900:1 Line 1 Column 18 File C:/temp/prime_with_templates.cpp
   |   | (decl_specifier@Cpp~GCC5=1073#6b048c0^1#6b048e0:1 Line 1 Column 18 File C:/temp/prime_with_templates.cpp
   |   |  (type_specifier@Cpp~GCC5=1110#6b048a0^1#6b048c0:1 Line 1 Column 18 File C:/temp/prime_with_templates.cpp
   |   |   (class_specifier@Cpp~GCC5=1761#6b04880^1#6b048a0:1 Line 1 Column 18 File C:/temp/prime_with_templates.cpp
   |   |   |(class_head@Cpp~GCC5=1763#6afb980^1#6b04880:1 Line 1 Column 18 File C:/temp/prime_with_templates.cpp
   |   |   | (class_key@Cpp~GCC5=1791#6afbca0^1#6afb980:1 Line 1 Column 18 File C:/temp/prime_with_templates.cpp)class_key
   |   |   | (IDENTIFIER@Cpp~GCC5=3368#6afbcc0^1#6afb980:2[`answer'] Line 1 Column 25 File C:/temp/prime_with_templates.cpp)IDENTIFIER
   |   |   | (optional_base_clause@Cpp~GCC5=1872#6afba60^1#6afb980:3 Line 1 Column 32 File C:/temp/prime_with_templates.cpp)optional_base_clause
   |   |   |)class_head#6afb980
   |   |   |(member_specification@Cpp~GCC5=1794#6b042e0^1#6b04880:2 {2} Line 1 Column 34 File C:/temp/prime_with_templates.cpp
   |   |   | (member_declaration_or_access_specifier@Cpp~GCC5=1806#6b04060^1#6b042e0:1 Line 1 Column 34 File C:/temp/prime_with_templates.cpp
   |   |   |  (member_declaration@Cpp~GCC5=1822#6b04040^1#6b04060:1 Line 1 Column 34 File C:/temp/prime_with_templates.cpp
   |   |   |   (function_definition@Cpp~GCC5=1632#6b04020^1#6b04040:1 Line 1 Column 34 File C:/temp/prime_with_templates.cpp
   |   |   |   |(function_head@Cpp~GCC5=1673#6afbec0^1#6b04020:1 Line 1 Column 34 File C:/temp/prime_with_templates.cpp
   |   |   |   | (ptr_declarator@Cpp~GCC5=1417#6afbfe0^1#6afbec0:1 Line 1 Column 34 File C:/temp/prime_with_templates.cpp
   |   |   |   |  (noptr_declarator@Cpp~GCC5=1422#6afbf80^1#6afbfe0:1 Line 1 Column 34 File C:/temp/prime_with_templates.cpp
   |   |   |   |   (noptr_declarator@Cpp~GCC5=1421#6afbf60^1#6afbf80:1 Line 1 Column 34 File C:/temp/prime_with_templates.cpp
   |   |   |   |   |(declarator_id@Cpp~GCC5=1487#6afbea0^1#6afbf60:1 Line 1 Column 34 File C:/temp/prime_with_templates.cpp
   |   |   |   |   | (id_expression@Cpp~GCC5=317#6afbb40^1#6afbea0:1 Line 1 Column 34 File C:/temp/prime_with_templates.cpp
   |   |   |   |   |  (unqualified_id@Cpp~GCC5=319#6afbc80^1#6afbb40:1 Line 1 Column 34 File C:/temp/prime_with_templates.cpp
   |   |   |   |   |   (IDENTIFIER@Cpp~GCC5=3368#6afbc20^1#6afbc80:1[`answer'] Line 1 Column 34 File C:/temp/prime_with_templates.cpp)IDENTIFIER
   |   |   |   |   |  )unqualified_id#6afbc80
   |   |   |   |   | )id_expression#6afbb40
   |   |   |   |   |)declarator_id#6afbea0
   |   |   |   |   )noptr_declarator#6afbf60
   |   |   |   |   (parameter_declaration_clause@Cpp~GCC5=1559#6afbd00^1#6afbf80:2 Line 1 Column 41 File C:/temp/prime_with_templates.cpp
   |   |   |   |   |(pp_parameter_declaration_list@Cpp~GCC5=1570#6afb940^1#6afbd00:1 Line 1 Column 41 File C:/temp/prime_with_templates.cpp
   |   |   |   |   | (pp_parameter_declaration_seq@Cpp~GCC5=1574#6afb800^1#6afb940:1 Line 1 Column 41 File C:/temp/prime_with_templates.cpp
   |   |   |   |   |  (parameter_declaration@Cpp~GCC5=1610#6afb9a0^1#6afb800:1 Line 1 Column 41 File C:/temp/prime_with_templates.cpp
   |   |   |   |   |   (basic_decl_specifier_seq@Cpp~GCC5=1070#6afbf40^1#6afb9a0:1 Line 1 Column 41 File C:/temp/prime_with_templates.cpp
   |   |   |   |   |   |(decl_specifier@Cpp~GCC5=1073#6afbfa0^1#6afbf40:1 Line 1 Column 41 File C:/temp/prime_with_templates.cpp
   |   |   |   |   |   | (trailing_type_specifier@Cpp~GCC5=1118#6afbfc0^1#6afbfa0:1 Line 1 Column 41 File C:/temp/prime_with_templates.cpp
   |   |   |   |   |   |  (simple_type_specifier@Cpp~GCC5=1140#6afb860^1#6afbfc0:1 Line 1 Column 41 File C:/temp/prime_with_templates.cpp)simple_type_specifier
   |   |   |   |   |   | )trailing_type_specifier#6afbfc0
   |   |   |   |   |   |)decl_specifier#6afbfa0
   |   |   |   |   |   )basic_decl_specifier_seq#6afbf40
   |   |   |   |   |  )parameter_declaration#6afb9a0
   |   |   |   |   | )pp_parameter_declaration_seq#6afb800
   |   |   |   |   |)pp_parameter_declaration_list#6afb940
   |   |   |   |   )parameter_declaration_clause#6afbd00
   |   |   |   |   (function_qualifiers@Cpp~GCC5=1438#6afbce0^1#6afbf80:3 Line 1 Column 46 File C:/temp/prime_with_templates.cpp)function_qualifiers
   |   |   |   |  )noptr_declarator#6afbf80
   |   |   |   | )ptr_declarator#6afbfe0
   |   |   |   |)function_head#6afbec0
   |   |   |   |(function_body@Cpp~GCC5=1680#6b04000^1#6b04020:2 Line 1 Column 46 File C:/temp/prime_with_templates.cpp
   |   |   |   | (compound_statement@Cpp~GCC5=888#6afbee0^1#6b04000:1 Line 1 Column 46 File C:/temp/prime_with_templates.cpp)compound_statement
   |   |   |   |)function_body#6b04000
   |   |   |   )function_definition#6b04020
   |   |   |  )member_declaration#6b04040
   |   |   | )member_declaration_or_access_specifier#6b04060
   |   |   | (member_declaration_or_access_specifier@Cpp~GCC5=1806#6b042c0^1#6b042e0:2 Line 1 Column 49 File C:/temp/prime_with_templates.cpp
   |   |   |  (member_declaration@Cpp~GCC5=1822#6b04820^1#6b042c0:1 Line 1 Column 49 File C:/temp/prime_with_templates.cpp
   |   |   |   (function_definition@Cpp~GCC5=1632#6b04280^1#6b04820:1 Line 1 Column 49 File C:/temp/prime_with_templates.cpp
   |   |   |   |(function_head@Cpp~GCC5=1674#6b04220^1#6b04280:1 Line 1 Column 49 File C:/temp/prime_with_templates.cpp
   |   |   |   | (basic_decl_specifier_seq@Cpp~GCC5=1070#6b040e0^1#6b04220:1 Line 1 Column 49 File C:/temp/prime_with_templates.cpp
   |   |   |   |  (decl_specifier@Cpp~GCC5=1073#6b040c0^1#6b040e0:1 Line 1 Column 49 File C:/temp/prime_with_templates.cpp
   |   |   |   |   (trailing_type_specifier@Cpp~GCC5=1118#6b040a0^1#6b040c0:1 Line 1 Column 49 File C:/temp/prime_with_templates.cpp
   |   |   |   |   |(simple_type_specifier@Cpp~GCC5=1138#6b04080^1#6b040a0:1 Line 1 Column 49 File C:/temp/prime_with_templates.cpp)simple_type_specifier
   |   |   |   |   )trailing_type_specifier#6b040a0
   |   |   |   |  )decl_specifier#6b040c0
   |   |   |   | )basic_decl_specifier_seq#6b040e0
   |   |   |   | (ptr_declarator@Cpp~GCC5=1417#6b04200^1#6b04220:2 Line 1 Column 54 File C:/temp/prime_with_templates.cpp
   |   |   |   |  (noptr_declarator@Cpp~GCC5=1422#6b041e0^1#6b04200:1 Line 1 Column 54 File C:/temp/prime_with_templates.cpp
   |   |   |   |   (noptr_declarator@Cpp~GCC5=1421#6b041a0^1#6b041e0:1 Line 1 Column 54 File C:/temp/prime_with_templates.cpp
   |   |   |   |   |(declarator_id@Cpp~GCC5=1487#6b04180^1#6b041a0:1 Line 1 Column 54 File C:/temp/prime_with_templates.cpp
   |   |   |   |   | (id_expression@Cpp~GCC5=317#6b04160^1#6b04180:1 Line 1 Column 54 File C:/temp/prime_with_templates.cpp
   |   |   |   |   |  (unqualified_id@Cpp~GCC5=320#6b04140^1#6b04160:1 Line 1 Column 54 File C:/temp/prime_with_templates.cpp
   |   |   |   |   |   (operator_function_id@Cpp~GCC5=2027#6b04120^1#6b04140:1 Line 1 Column 54 File C:/temp/prime_with_templates.cpp
   |   |   |   |   |   |(operator@Cpp~GCC5=2070#6b04100^1#6b04120:1 Line 1 Column 62 File C:/temp/prime_with_templates.cpp)operator
   |   |   |   |   |   )operator_function_id#6b04120
   |   |   |   |   |  )unqualified_id#6b04140
   |   |   |   |   | )id_expression#6b04160
   |   |   |   |   |)declarator_id#6b04180
   |   |   |   |   )noptr_declarator#6b041a0
   |   |   |   |   (parameter_declaration_clause@Cpp~GCC5=1558#6afba40^1#6b041e0:2 Line 1 Column 65 File C:/temp/prime_with_templates.cpp)parameter_declaration_clause
   |   |   |   |   (function_qualifiers@Cpp~GCC5=1438#6b041c0^1#6b041e0:3 Line 1 Column 66 File C:/temp/prime_with_templates.cpp)function_qualifiers
   |   |   |   |  )noptr_declarator#6b041e0
   |   |   |   | )ptr_declarator#6b04200
   |   |   |   |)function_head#6b04220
   |   |   |   |(function_body@Cpp~GCC5=1680#6b04300^1#6b04280:2 Line 1 Column 66 File C:/temp/prime_with_templates.cpp
   |   |   |   | (compound_statement@Cpp~GCC5=889#6b04760^1#6b04300:1 Line 1 Column 66 File C:/temp/prime_with_templates.cpp
   |   |   |   |  (pp_statement_seq@Cpp~GCC5=894#6b04780^1#6b04760:1 Line 1 Column 67 File C:/temp/prime_with_templates.cpp
   |   |   |   |   (statement@Cpp~GCC5=857#6b04440^1#6b04780:1 Line 1 Column 67 File C:/temp/prime_with_templates.cpp
   |   |   |   |   |(jump_statement@Cpp~GCC5=1011#6afba80^1#6b04440:1 Line 1 Column 67 File C:/temp/prime_with_templates.cpp
   |   |   |   |   | (pm_expression@Cpp~GCC5=551#6b04380^1#6afba80:1 Line 1 Column 74 File C:/temp/prime_with_templates.cpp
   |   |   |   |   |  (cast_expression@Cpp~GCC5=543#6b04360^1#6b04380:1 Line 1 Column 74 File C:/temp/prime_with_templates.cpp
   |   |   |   |   |   (unary_expression@Cpp~GCC5=465#6b04340^1#6b04360:1 Line 1 Column 74 File C:/temp/prime_with_templates.cpp
   |   |   |   |   |   |(primary_expression@Cpp~GCC5=307#6b04320^1#6b04340:1 Line 1 Column 74 File C:/temp/prime_with_templates.cpp
   |   |   |   |   |   | (id_expression@Cpp~GCC5=317#6b042a0^1#6b04320:1 Line 1 Column 74 File C:/temp/prime_with_templates.cpp
   |   |   |   |   |   |  (unqualified_id@Cpp~GCC5=319#6b04260^1#6b042a0:1 Line 1 Column 74 File C:/temp/prime_with_templates.cpp
   |   |   |   |   |   |   (IDENTIFIER@Cpp~GCC5=3368#6b04240^1#6b04260:1[`V'] Line 1 Column 74 File C:/temp/prime_with_templates.cpp)IDENTIFIER
   |   |   |   |   |   |  )unqualified_id#6b04260
   |   |   |   |   |   | )id_expression#6b042a0
   |   |   |   |   |   |)primary_expression#6b04320
   |   |   |   |   |   )unary_expression#6b04340
   |   |   |   |   |  )cast_expression#6b04360
   |   |   |   |   | )pm_expression#6b04380
   |   |   |   |   |)jump_statement#6afba80
   |   |   |   |   )statement#6b04440
   |   |   |   |  )pp_statement_seq#6b04780
   |   |   |   | )compound_statement#6b04760
   |   |   |   |)function_body#6b04300
   |   |   |   )function_definition#6b04280
   |   |   |  )member_declaration#6b04820
   |   |   | )member_declaration_or_access_specifier#6b042c0
   |   |   |)member_specification#6b042e0
   |   |   )class_specifier#6b04880
   |   |  )type_specifier#6b048a0
   |   | )decl_specifier#6b048c0
   |   |)basic_decl_specifier_seq#6b048e0
   |   )simple_declaration#6b04900
   |  )block_declaration#6b04920
   | )declaration#6b04940
   |)template_declaration#6b04960
   )declaration#6b04980
  )pp_declaration_seq#6b049a0
  (pp_declaration_seq@Cpp~GCC5=1022#6b06620^1#6b06640:2 Line 3 Column 1 File C:/temp/prime_with_templates.cpp
   (declaration@Cpp~GCC5=1036#6b06600^1#6b06620:1 Line 3 Column 1 File C:/temp/prime_with_templates.cpp
   |(template_declaration@Cpp~GCC5=2079#6b065e0^1#6b06600:1 Line 3 Column 1 File C:/temp/prime_with_templates.cpp
   | (template_parameter_list@Cpp~GCC5=2083#6b05460^1#6b065e0:1 Line 3 Column 10 File C:/temp/prime_with_templates.cpp
   |  (template_parameter_list@Cpp~GCC5=2083#6b05140^1#6b05460:1 Line 3 Column 10 File C:/temp/prime_with_templates.cpp
   |   (template_parameter_list@Cpp~GCC5=2083#6b04ee0^1#6b05140:1 Line 3 Column 10 File C:/temp/prime_with_templates.cpp
   |   |(template_parameter_list@Cpp~GCC5=2082#6b04cc0^1#6b04ee0:1 Line 3 Column 10 File C:/temp/prime_with_templates.cpp
   |   | (template_parameter@Cpp~GCC5=2085#6b04ca0^1#6b04cc0:1 Line 3 Column 10 File C:/temp/prime_with_templates.cpp
   |   |  (parameter_declaration@Cpp~GCC5=1611#6b04c80^1#6b04ca0:1 Line 3 Column 10 File C:/temp/prime_with_templates.cpp
   |   |   (basic_decl_specifier_seq@Cpp~GCC5=1070#6b04a40^1#6b04c80:1 Line 3 Column 10 File C:/temp/prime_with_templates.cpp
   |   |   |(decl_specifier@Cpp~GCC5=1073#6b04a20^1#6b04a40:1 Line 3 Column 10 File C:/temp/prime_with_templates.cpp
   |   |   | (trailing_type_specifier@Cpp~GCC5=1118#6b04a00^1#6b04a20:1 Line 3 Column 10 File C:/temp/prime_with_templates.cpp
   |   |   |  (simple_type_specifier@Cpp~GCC5=1138#6b049e0^1#6b04a00:1 Line 3 Column 10 File C:/temp/prime_with_templates.cpp)simple_type_specifier
   |   |   | )trailing_type_specifier#6b04a00
   |   |   |)decl_specifier#6b04a20
   |   |   )basic_decl_specifier_seq#6b04a40
   |   |   (ptr_declarator@Cpp~GCC5=1417#6b04c40^1#6b04c80:2 Line 3 Column 15 File C:/temp/prime_with_templates.cpp
   |   |   |(noptr_declarator@Cpp~GCC5=1421#6b04be0^1#6b04c40:1 Line 3 Column 15 File C:/temp/prime_with_templates.cpp
   |   |   | (declarator_id@Cpp~GCC5=1487#6b04bc0^1#6b04be0:1 Line 3 Column 15 File C:/temp/prime_with_templates.cpp
   |   |   |  (id_expression@Cpp~GCC5=317#6b04b60^1#6b04bc0:1 Line 3 Column 15 File C:/temp/prime_with_templates.cpp
   |   |   |   (unqualified_id@Cpp~GCC5=319#6b04ac0^1#6b04b60:1 Line 3 Column 15 File C:/temp/prime_with_templates.cpp
   |   |   |   |(IDENTIFIER@Cpp~GCC5=3368#6b049c0^1#6b04ac0:1[`no'] Line 3 Column 15 File C:/temp/prime_with_templates.cpp)IDENTIFIER
   |   |   |   )unqualified_id#6b04ac0
   |   |   |  )id_expression#6b04b60
   |   |   | )declarator_id#6b04bc0
   |   |   |)noptr_declarator#6b04be0
   |   |   )ptr_declarator#6b04c40
   |   |  )parameter_declaration#6b04c80
   |   | )template_parameter#6b04ca0
   |   |)template_parameter_list#6b04cc0
   |   |(template_parameter@Cpp~GCC5=2085#6b04ec0^1#6b04ee0:2 Line 3 Column 19 File C:/temp/prime_with_templates.cpp
   |   | (parameter_declaration@Cpp~GCC5=1611#6b04ea0^1#6b04ec0:1 Line 3 Column 19 File C:/temp/prime_with_templates.cpp
   |   |  (basic_decl_specifier_seq@Cpp~GCC5=1070#6b04b40^1#6b04ea0:1 Line 3 Column 19 File C:/temp/prime_with_templates.cpp
   |   |   (decl_specifier@Cpp~GCC5=1073#6b04ba0^1#6b04b40:1 Line 3 Column 19 File C:/temp/prime_with_templates.cpp
   |   |   |(trailing_type_specifier@Cpp~GCC5=1118#6b04c60^1#6b04ba0:1 Line 3 Column 19 File C:/temp/prime_with_templates.cpp
   |   |   | (simple_type_specifier@Cpp~GCC5=1138#6b04580^1#6b04c60:1 Line 3 Column 19 File C:/temp/prime_with_templates.cpp)simple_type_specifier
   |   |   |)trailing_type_specifier#6b04c60
   |   |   )decl_specifier#6b04ba0
   |   |  )basic_decl_specifier_seq#6b04b40
   |   |  (ptr_declarator@Cpp~GCC5=1417#6b04e60^1#6b04ea0:2 Line 3 Column 24 File C:/temp/prime_with_templates.cpp
   |   |   (noptr_declarator@Cpp~GCC5=1421#6b04e40^1#6b04e60:1 Line 3 Column 24 File C:/temp/prime_with_templates.cpp
   |   |   |(declarator_id@Cpp~GCC5=1487#6b04de0^1#6b04e40:1 Line 3 Column 24 File C:/temp/prime_with_templates.cpp
   |   |   | (id_expression@Cpp~GCC5=317#6b04d80^1#6b04de0:1 Line 3 Column 24 File C:/temp/prime_with_templates.cpp
   |   |   |  (unqualified_id@Cpp~GCC5=319#6b04ce0^1#6b04d80:1 Line 3 Column 24 File C:/temp/prime_with_templates.cpp
   |   |   |   (IDENTIFIER@Cpp~GCC5=3368#6b04560^1#6b04ce0:1[`yes'] Line 3 Column 24 File C:/temp/prime_with_templates.cpp)IDENTIFIER
   |   |   |  )unqualified_id#6b04ce0
   |   |   | )id_expression#6b04d80
   |   |   |)declarator_id#6b04de0
   |   |   )noptr_declarator#6b04e40
   |   |  )ptr_declarator#6b04e60
   |   | )parameter_declaration#6b04ea0
   |   |)template_parameter#6b04ec0
   |   )template_parameter_list#6b04ee0
   |   (template_parameter@Cpp~GCC5=2085#6b05120^1#6b05140:2 Line 3 Column 29 File C:/temp/prime_with_templates.cpp
   |   |(parameter_declaration@Cpp~GCC5=1611#6b05100^1#6b05120:1 Line 3 Column 29 File C:/temp/prime_with_templates.cpp
   |   | (basic_decl_specifier_seq@Cpp~GCC5=1070#6b04d20^1#6b05100:1 Line 3 Column 29 File C:/temp/prime_with_templates.cpp
   |   |  (decl_specifier@Cpp~GCC5=1073#6b04dc0^1#6b04d20:1 Line 3 Column 29 File C:/temp/prime_with_templates.cpp
   |   |   (trailing_type_specifier@Cpp~GCC5=1118#6b04e80^1#6b04dc0:1 Line 3 Column 29 File C:/temp/prime_with_templates.cpp
   |   |   |(simple_type_specifier@Cpp~GCC5=1140#6b046e0^1#6b04e80:1 Line 3 Column 29 File C:/temp/prime_with_templates.cpp)simple_type_specifier
   |   |   )trailing_type_specifier#6b04e80
   |   |  )decl_specifier#6b04dc0
   |   | )basic_decl_specifier_seq#6b04d20
   |   | (ptr_declarator@Cpp~GCC5=1417#6b05080^1#6b05100:2 Line 3 Column 33 File C:/temp/prime_with_templates.cpp
   |   |  (noptr_declarator@Cpp~GCC5=1421#6b05020^1#6b05080:1 Line 3 Column 33 File C:/temp/prime_with_templates.cpp
   |   |   (declarator_id@Cpp~GCC5=1487#6b05000^1#6b05020:1 Line 3 Column 33 File C:/temp/prime_with_templates.cpp
   |   |   |(id_expression@Cpp~GCC5=317#6b04fa0^1#6b05000:1 Line 3 Column 33 File C:/temp/prime_with_templates.cpp
   |   |   | (unqualified_id@Cpp~GCC5=319#6b04f00^1#6b04fa0:1 Line 3 Column 33 File C:/temp/prime_with_templates.cpp
   |   |   |  (IDENTIFIER@Cpp~GCC5=3368#6b046c0^1#6b04f00:1[`f'] Line 3 Column 33 File C:/temp/prime_with_templates.cpp)IDENTIFIER
   |   |   | )unqualified_id#6b04f00
   |   |   |)id_expression#6b04fa0
   |   |   )declarator_id#6b05000
   |   |  )noptr_declarator#6b05020
   |   | )ptr_declarator#6b05080
   |   |)parameter_declaration#6b05100
   |   )template_parameter#6b05120
   |  )template_parameter_list#6b05140
   |  (template_parameter@Cpp~GCC5=2085#6b05440^1#6b05460:2 Line 3 Column 36 File C:/temp/prime_with_templates.cpp
   |   (parameter_declaration@Cpp~GCC5=1611#6b05420^1#6b05440:1 Line 3 Column 36 File C:/temp/prime_with_templates.cpp
   |   |(basic_decl_specifier_seq@Cpp~GCC5=1070#6b05160^1#6b05420:1 Line 3 Column 36 File C:/temp/prime_with_templates.cpp
   |   | (decl_specifier@Cpp~GCC5=1073#6b04fe0^1#6b05160:1 Line 3 Column 36 File C:/temp/prime_with_templates.cpp
   |   |  (trailing_type_specifier@Cpp~GCC5=1118#6b050e0^1#6b04fe0:1 Line 3 Column 36 File C:/temp/prime_with_templates.cpp
   |   |   (simple_type_specifier@Cpp~GCC5=1140#6b050c0^1#6b050e0:1 Line 3 Column 36 File C:/temp/prime_with_templates.cpp)simple_type_specifier
   |   |  )trailing_type_specifier#6b050e0
   |   | )decl_specifier#6b04fe0
   |   |)basic_decl_specifier_seq#6b05160
   |   |(ptr_declarator@Cpp~GCC5=1417#6b053e0^1#6b05420:2 Line 3 Column 40 File C:/temp/prime_with_templates.cpp
   |   | (noptr_declarator@Cpp~GCC5=1421#6b053c0^1#6b053e0:1 Line 3 Column 40 File C:/temp/prime_with_templates.cpp
   |   |  (declarator_id@Cpp~GCC5=1487#6b05360^1#6b053c0:1 Line 3 Column 40 File C:/temp/prime_with_templates.cpp
   |   |   (id_expression@Cpp~GCC5=317#6b05280^1#6b05360:1 Line 3 Column 40 File C:/temp/prime_with_templates.cpp
   |   |   |(unqualified_id@Cpp~GCC5=319#6b051a0^1#6b05280:1 Line 3 Column 40 File C:/temp/prime_with_templates.cpp
   |   |   | (IDENTIFIER@Cpp~GCC5=3368#6b046a0^1#6b051a0:1[`p'] Line 3 Column 40 File C:/temp/prime_with_templates.cpp)IDENTIFIER
   |   |   |)unqualified_id#6b051a0
   |   |   )id_expression#6b05280
   |   |  )declarator_id#6b05360
   |   | )noptr_declarator#6b053c0
   |   |)ptr_declarator#6b053e0
   |   )parameter_declaration#6b05420
   |  )template_parameter#6b05440
   | )template_parameter_list#6b05460
Ира Бакстер
источник
Определение, является ли это объявлением переменной или умножением, не является функцией проверки типа. Также я должен был вычеркнуть твой ответ из этого материала саморекламы ... снова.
Щенок
@Puppy: вы можете сказать, что вам нравится, но так работает инструмент. Удаление имени инструмента, вероятно, просто заставит людей спросить, как называется инструмент.
Ира Бакстер
То, как работает этот инструмент, не имеет значения, поскольку вопрос не касается работы инструмента. Кроме того, я думаю, что мы можем спокойно ждать, когда это действительно произойдет.
щенок