Уже есть много хороших ответов, но, поскольку вы не знакомы с грамматиками, синтаксическими анализаторами, компиляторами и т. Д., Позвольте мне продемонстрировать это на примере.
Во-первых, понятие грамматики довольно интуитивно понятно. Представьте себе набор правил:
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
a * b
? - person Bo Persson   schedule 08.08.2011a*b;
иa*b=exp;
оба анализируются как объявления указателя. Если вы поместитеa*b
в позицию r-значения, это станет выражением умножения. Хотя это не отражено в документированной грамматике, оно четко определено и может быть решено на уровне грамматики. - person BCS   schedule 08.08.2011