Jump to content

Парсер рекурсивного спуска

(Перенаправлено с Рекурсивного спуска )

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

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

Рекурсивный спуск с обратным отслеживанием — это метод, который определяет, какую продукцию использовать, путем последовательной проверки каждой продукции. Рекурсивный спуск с обратным отслеживанием не ограничивается грамматиками LL( k ), но не гарантируется его завершение, если только грамматика не является LL( k ). Даже после завершения работы синтаксическим анализаторам, использующим рекурсивный спуск с возвратом, может потребоваться экспоненциальное время .

Хотя прогнозные парсеры широко используются и часто выбираются при написании парсера вручную, программисты часто предпочитают использовать парсер на основе таблиц, создаваемый генератором синтаксического анализатора . [ нужна ссылка ] либо для языка LL( k ), либо с использованием альтернативного синтаксического анализатора, такого как LALR или LR . Это особенно актуально, если грамматика не находится в форме LL( k ) , поскольку требуется преобразование грамматики в LL, чтобы сделать ее пригодной для прогнозного анализа. Прогнозирующие парсеры также можно создавать автоматически с помощью таких инструментов, как ANTLR .

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

Пример парсера

[ редактировать ]

Следующая EBNF -подобная грамматика (для Никлауса Вирта из книги PL/0 языка программирования «Алгоритмы + структуры данных = программы ») представлена ​​в LL(1) форме :

 program = block "." .
 
 block =
     ["const" ident "=" number {"," ident "=" number} ";"]
     ["var" ident {"," ident} ";"]
     {"procedure" ident ";" block ";"} statement .
 
 statement =
     ident ":=" expression
     | "call" ident
     | "begin" statement {";" statement } "end"
     | "if" condition "then" statement
     | "while" condition "do" statement .
 
 condition =
     "odd" expression
     | expression ("="|"#"|"<"|"<="|">"|">=") expression .
 
 expression = ["+"|"-"] term {("+"|"-") term} .
 
 term = factor {("*"|"/") factor} .
 
 factor =
     ident
     | number
     | "(" expression ")" .

Терминалы выражаются в кавычках. Каждый нетерминал определяется правилом грамматики, за исключением ident и Number , которые считаются определенными неявно.

C-реализация

[ редактировать ]

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

Обратите внимание, насколько точно предиктивный парсер ниже отражает грамматику выше. Для каждого нетерминала в грамматике существует процедура. Анализ осуществляется сверху вниз до тех пор, пока не будет обработан последний нетерминал. Фрагмент программы зависит от глобальной переменной sym , которая содержит текущий входной символ, и функции nextsym , которая обновляет sym при вызове.

Реализации функций nextsym и error для простоты опущены.

typedef enum {ident, number, lparen, rparen, times, slash, plus,
    minus, eql, neq, lss, leq, gtr, geq, callsym, beginsym, semicolon,
    endsym, ifsym, whilesym, becomes, thensym, dosym, constsym, comma,
    varsym, procsym, period, oddsym} Symbol;

Symbol sym;
void nextsym(void);
void error(const char msg[]);

int accept(Symbol s) {
    if (sym == s) {
        nextsym();
        return 1;
    }
    return 0;
}

int expect(Symbol s) {
    if (accept(s))
        return 1;
    error("expect: unexpected symbol");
    return 0;
}

void factor(void) {
    if (accept(ident)) {
        ;
    } else if (accept(number)) {
        ;
    } else if (accept(lparen)) {
        expression();
        expect(rparen);
    } else {
        error("factor: syntax error");
        nextsym();
    }
}

void term(void) {
    factor();
    while (sym == times || sym == slash) {
        nextsym();
        factor();
    }
}

void expression(void) {
    if (sym == plus || sym == minus)
        nextsym();
    term();
    while (sym == plus || sym == minus) {
        nextsym();
        term();
    }
}

void condition(void) {
    if (accept(oddsym)) {
        expression();
    } else {
        expression();
        if (sym == eql || sym == neq || sym == lss || sym == leq || sym == gtr || sym == geq) {
            nextsym();
            expression();
        } else {
            error("condition: invalid operator");
            nextsym();
        }
    }
}

void statement(void) {
    if (accept(ident)) {
        expect(becomes);
        expression();
    } else if (accept(callsym)) {
        expect(ident);
    } else if (accept(beginsym)) {
        do {
            statement();
        } while (accept(semicolon));
        expect(endsym);
    } else if (accept(ifsym)) {
        condition();
        expect(thensym);
        statement();
    } else if (accept(whilesym)) {
        condition();
        expect(dosym);
        statement();
    } else {
        error("statement: syntax error");
        nextsym();
    }
}

void block(void) {
    if (accept(constsym)) {
        do {
            expect(ident);
            expect(eql);
            expect(number);
        } while (accept(comma));
        expect(semicolon);
    }
    if (accept(varsym)) {
        do {
            expect(ident);
        } while (accept(comma));
        expect(semicolon);
    }
    while (accept(procsym)) {
        expect(ident);
        expect(semicolon);
        block();
        expect(semicolon);
    }
    statement();
}

void program(void) {
    nextsym();
    block();
    expect(period);
}

Некоторые генераторы парсеров рекурсивного спуска:

См. также

[ редактировать ]
  1. ^ Эта статья основана на материалах, взятых из Recursive+descent+parser в Бесплатном онлайн-словаре вычислительной техники до 1 ноября 2008 г. и включенных в соответствии с условиями «повторного лицензирования» GFDL версии 1.3 или более поздней.
  2. ^ Бердж, штат Вашингтон (1975). Методы рекурсивного программирования . ISBN  0-201-14450-6 .
  3. ^ Уотсон, Дес (22 марта 2017 г.). Практический подход к построению компилятора . Спрингер. ISBN  978-3-319-52789-5 .
  4. ^ Ахо, Альфред В .; Сетхи, Рави; Ульман, Джеффри (1986). Составители: принципы, методы и инструменты (первое изд.). Эддисон Уэсли. п. 183 .

Общие ссылки

[ редактировать ]
  • Составители: Принципы, методы и инструменты , первое издание, Альфред В. Ахо, Рави Сетхи и Джеффри Д. Ульман, в частности раздел 4.4.
  • Современная реализация компилятора на Java, второе издание , Эндрю Аппель, 2002 г., ISBN   0-521-82060-X .
  • Методы рекурсивного программирования , WH Burge, 1975, ISBN   0-201-14450-6
  • Создание компилятора на языке C , Чарльз Н. Фишер и Ричард Дж. Леблан-младший, 1991 г., ISBN   0-8053-2166-7 .
  • Компиляция с помощью C# и Java , Пэт Терри, 2005 г., ISBN   0-321-26360-X , 624
  • Алгоритмы + Структуры данных = Программы , Никлаус Вирт, 1975, ISBN   0-13-022418-9
  • «Строительство компилятора» , Никлаус Вирт, 1996 г., ISBN   0-201-40353-6
[ редактировать ]
Arc.Ask3.Ru: конец переведенного документа.
Arc.Ask3.Ru
Номер скриншота №: eb27c82281c1d69ae093b5943ed5560d__1722192120
URL1:https://arc.ask3.ru/arc/aa/eb/0d/eb27c82281c1d69ae093b5943ed5560d.html
Заголовок, (Title) документа по адресу, URL1:
Recursive descent parser - Wikipedia
Данный printscreen веб страницы (снимок веб страницы, скриншот веб страницы), визуально-программная копия документа расположенного по адресу URL1 и сохраненная в файл, имеет: квалифицированную, усовершенствованную (подтверждены: метки времени, валидность сертификата), открепленную ЭЦП (приложена к данному файлу), что может быть использовано для подтверждения содержания и факта существования документа в этот момент времени. Права на данный скриншот принадлежат администрации Ask3.ru, использование в качестве доказательства только с письменного разрешения правообладателя скриншота. Администрация Ask3.ru не несет ответственности за информацию размещенную на данном скриншоте. Права на прочие зарегистрированные элементы любого права, изображенные на снимках принадлежат их владельцам. Качество перевода предоставляется как есть. Любые претензии, иски не могут быть предъявлены. Если вы не согласны с любым пунктом перечисленным выше, вы не можете использовать данный сайт и информация размещенную на нем (сайте/странице), немедленно покиньте данный сайт. В случае нарушения любого пункта перечисленного выше, штраф 55! (Пятьдесят пять факториал, Денежную единицу (имеющую самостоятельную стоимость) можете выбрать самостоятельно, выплаичвается товарами в течение 7 дней с момента нарушения.)