Re: По Antlr подскажите...
От: maxkar  
Дата: 03.12.15 23:40
Оценка: 61 (3)
Здравствуйте, Кондраций, Вы писали:

К>Вопрос первый: можно ли как-то использовать '=', а не '=='. Можно конечно парсером выдирать всё выражения и разбирать далее руками, но не хочется.

К>Вопрос второй на понимание (может смешной, но я в грамматиках лох): описанное выше проиходит от того, что ANTLR описывает контекстно-свободные грамматики?

Начну со второго вопроса. Описанное выше просиходит из-за того, что ANTLR не разбирает произвольные контекстные грамматики. Он разбирает LR(1) (или LALR, не помню уже) грамматики, которые являются подмножеством контекстно-свободных.

Вообще,контекстно-свободная грамматика не про возможность использования символов ('=', '==', 'var' — и т.п. — терминалы называются). Она про возможность использования правил из самой грамматики. Т.е. допустим мы пишем код. У нас есть правило
expr: int
expr: '(' expr '+' expr ')'
if: 'if' expr 'then' statement
assign: var '=' expr


В контекстно-свободной грамматике всегда можно написать '(1 + 2)' там, где по грамматике положен expr. Т.е. можно использовать сумму как в if, так и в assign. А вот в контекстно-зависимой грамматике это уже не правда. Например, можно сказать, что сложение можно использовать только после знака присваивания (но не в операторе if). При этом и в присваивании, и в if все равно будет "expr". Такую контекстно-зависимую грамматику можно выразить как
expr: int
var '=' expr: var '=' '(' expr '+' expr ')'
if: 'if' expr 'then' statement
assign: var '=' expr


Теперь к первому вопросу. ANTLR не разбирает произвольные грамматики. Он разбирает только грамматики определенного вида по строго определенному алгоритму. Детальное описание можно найти в книгах и конспектах лекций по компиляторам. Если на пальцах, он хранит "стек" состояний (начальные части выражений). И может заменять только "группу символов на вершине стека" на определенный литерал в левой части. На примере выражения выше (в середине выражения, первые шаги не показаны, стек растет слева на право):

var['x'] '=' '(' expr['3'] '+' '1'
// Ух ты, 1 - это же expr
var['x'] '=' '(' expr['3'] '+' expr['1']
// Ничего не понятно - нужно взять следующий токен
var['x'] '=' '(' expr['3'] '+' expr['1'] ')'
// О! Оно оказывается на expr кончается, надо заменить
var['x'] '=' expr['(3 + 3)']
// А это - assign!
assign['x = (3 + 3)']


Но так происходит только в простом случае. Для более сложной грамматики по текущему стеку не всегда можно определить, во что сворачивается (по какому правилу заменяется) терминал. Например:
variableName: string
functionName: string
call: functionName '(' ')'
assign: variableName '=' variableName


В этом случае выражение 'x()' все еще можно разобрать на стеке, если посмотреть на следующий символ:
'x'
// Строка!
string['x']
// Хм... Не понятно пока, функция это или переменная.
// Что там дальше? '('? Значит, это функция!
functionName['x']
// Пока все, берем следующий токен ...


А вот в более сложной грамматике такое может не получиться сделать.
id: string
token: string
filterDef: id '=' int
expr: token '=' string

Вот здесь при разборе выражения нельзя понять, что такое 'x' в выражении 'x = ...'. Это id (и идет определение фильтра) или token (и идет выражение)?

Т.е. парсер должен работать примерно так:
'x'
// Строка!
string['x']
// Хм... не понятно. id? token? Что там дальше?
// '=' ? не, все равно не понятно! это все еще может быть 'id=...' и 'token=...'
// Разбирайте сами свое выражение :(


Обнаружение подобных ситуаций происходит уже на этапе компиляции грамматики. Парсер не сможет определить, как трактовать текущий терминал ('x' в последнем примере) ни по текущему состоянию, ни с использованием следующего символа. И вот эта ситуация называется 'reduce/reduce conflict'. (кстати, ант на reduce/reduce жалуется? Если я не угадал и там shift/reduce, нужно писать другой пример ).

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

По неточному фрагменту грамматики полезное решение угадать нельзя. Хорошо бы было посмотреть всю грамматику целиком. Если она не секретная, может выложите куда-нибудь? Ну а если секретная, попробуйте сами теорию почитать. Там ничего сложного, просто более подробное описание процесса выше (и с описанием различных граничных случаев) со стандартными приемами решения разных конфликтов.
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.