Рекурсивный восходящий парсер - Recursive ascent parser - Wikipedia

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

Рекурсивное восхождение впервые описал Томас Пенелло в своей статье. "Очень быстрый парсинг LR". в 1986 году. Он не намеревался создать редактируемую вручную реализацию LR-анализатора, а скорее поддерживаемый и эффективный парсер, реализованный в язык ассемблера. Позже эта техника была изложена Г. Робертс[2] в 1988 г., а также в статье Leermakers, Augusteijn, Kruseman Aretz[3] в 1992 г. в журнале Теоретическая информатика. Чрезвычайно удобочитаемое описание техники было написано Мореллом и Миддлтоном.[4] в 2003 году. Хорошее изложение можно также найти в статье TOPLAS Спербера и Тимана.[5]

Рекурсивный подъем также был объединен с рекурсивным спуском, в результате чего получился метод, известный как рекурсивный подъем / спуск. Этот метод реализации, вероятно, легче редактировать вручную из-за уменьшения количества состояний и того факта, что некоторые из этих состояний более интуитивно понятны сверху вниз, чем снизу вверх. Он также может дать некоторые минимальные улучшения производительности по сравнению с обычным рекурсивным восхождением.[6]

Резюме

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

  • Сдвиг - Закодирован как вызов функции, эффективно переходящий в новое состояние автомата.
  • Уменьшать - Кодируется по-разному в зависимости от программа семантического действия для соответствующих производство. Результат этой процедуры заключен в ADT который возвращается вызывающему. Действие уменьшения также должно записывать количество сдвинутых токенов. прежний для уменьшения, передав это значение обратно вызывающей стороне вместе со значением уменьшения. Этот счетчик сдвига определяет, в какой точке стека вызовов следует обработать сокращение.

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

Пример

Рассмотрим следующую грамматику в зубр синтаксис:

expr: expr '+' термин {$$ = 1 $ + 3 $; } | expr '-' термин {$$ = $ 1 - $ 3; } | термин {$$ = $ 1; }; срок: '(' выражение ')' {$$ = $ 2; } | число {$$ = $ 1; }; число: '0' {$$ = 0; } | '1' {$$ = 1; };

Эта грамматика LR (0) в том, что он леворекурсивный (в expr нетерминальный), но не требует предварительного просмотра. Рекурсивное восхождение также способно обрабатывать грамматики, которые являются LALR (1), во многом так же, как управляемые таблицами синтаксические анализаторы обрабатывают такие случаи (путем предварительного вычисления разрешения конфликтов на основе возможного просмотра вперед).

Ниже приводится Scala реализация рекурсивного восходящего парсера на основе приведенной выше грамматики:

объект ExprParser {  частный тип Результат = (Нетерминальный, Int)    частный запечатанный черта Нетерминальный {    вал v: Int  }    частный дело учебный класс NTexpr(v: Int, в: Транслировать[Char]) расширяет Нетерминальный  частный дело учебный класс NTterm(v: Int, в: Транслировать[Char]) расширяет Нетерминальный  частный дело учебный класс NTnum(v: Int, в: Транслировать[Char]) расширяет Нетерминальный    учебный класс ParseException(сообщение: Нить) расширяет RuntimeException(сообщение) {    def это() = это("")        def это(c: Char) = это(c.нанизывать)  }    def разбирать(в: Транслировать[Char]) = состояние0(в)._1.v    /*   * 0 $ принять:. expr $ end   *   * '(' сдвинуть и перейти в состояние 1   * Сдвиг '0' и переход в состояние 2   * Сдвинуть '1' и перейти в состояние 3   *   * expr перейти в состояние 4   * срок перейти в состояние 5   * число перейти в состояние 6   */  частный def состояние0(в: Транслировать[Char]) = в матч {    дело дворняга #:: хвост => {      def петля(кортеж: Результат): Результат = {        вал (res, идти к) = кортеж                если (идти к == 0) {          петля(res матч {            дело NTexpr(v, в) => состояние4(в, v)            дело NTterm(v, в) => состояние5(в, v)            дело NTnum(v, в) => состояние6(в, v)          })        } еще (res, идти к - 1)      }            петля(дворняга матч {        дело '(' => состояние1(хвост)        дело '0' => состояние2(хвост)        дело '1' => состояние3(хвост)        дело c => бросать новый ParseException(c)      })    }        дело Транслировать() => бросать новый ParseException  }    /*   * 4 термин: '('. Expr ')'   *   * '(' сдвинуть и перейти в состояние 1   * Сдвиг '0' и переход в состояние 2   * Сдвинуть '1' и перейти в состояние 3   *   * expr перейти в состояние 7   * срок перейти в состояние 5   * число перейти в состояние 6   */  частный def состояние1(в: Транслировать[Char]): Результат = в матч {    дело дворняга #:: хвост => {      def петля(кортеж: Результат): Результат = {        вал (res, идти к) = кортеж                если (идти к == 0) {          петля(res матч {            дело NTexpr(v, в) => состояние7(в, v)            дело NTterm(v, в) => состояние5(в, v)            дело NTnum(v, в) => состояние6(в, v)          })        } еще (res, идти к - 1)      }            петля(дворняга матч {        дело '(' => состояние1(хвост)        дело '0' => состояние2(хвост)        дело '1' => состояние3(хвост)        дело c => бросать новый ParseException(c)      })    }        дело Транслировать() => бросать новый ParseException  }    /*   * 6 число: '0'.   *   * $ по умолчанию уменьшить с использованием правила 6 (число)   */  частный def состояние2(в: Транслировать[Char]) = (NTnum(0, в), 0)    /*   * 7 число: '1'.   *   * $ по умолчанию уменьшить с использованием правила 7 (число)   */  частный def состояние3(в: Транслировать[Char]) = (NTnum(1, в), 0)    /*   * 0 $ accept: expr. $ конец   * 1 выражение: выражение. '+' термин   * 2 | выраж. '-' срок   *   * $ end shift и перейти к состоянию 8   * сдвиг '+' и переход в состояние 9   * '-' сдвинуть и перейти в состояние 10   */  частный def состояние4(в: Транслировать[Char], arg1: Int): Результат = в матч {    дело дворняга #:: хвост => {      декремент(дворняга матч {        дело '+' => состояние9(хвост, arg1)        дело '-' => состояние10(хвост, arg1)        дело c => бросать новый ParseException(c)      })    }        дело Транслировать() => состояние8(arg1)  }    /*   * 3 expr: термин.   *   * $ по умолчанию уменьшить с использованием правила 3 ​​(expr)   */  частный def состояние5(в: Транслировать[Char], arg1: Int) = (NTexpr(arg1, в), 0)    /*   * 5 член: кол.   *   * $ по умолчанию уменьшить с помощью правила 5 (срок)   */  частный def состояние6(в: Транслировать[Char], arg1: Int) = (NTterm(arg1, в), 0)    /*   * 1 выражение: выражение. '+' термин   * 2 | выраж. '-' срок   * 4 термин: '(' выражение ')'   *   * сдвиг '+' и переход в состояние 9   * '-' сдвинуть и перейти в состояние 10   * ')' сдвиг и перейти в состояние 11   */  частный def состояние7(в: Транслировать[Char], arg1: Int): Результат = в матч {    дело дворняга #:: хвост => {      декремент(дворняга матч {        дело '+' => состояние9(хвост, arg1)        дело '-' => состояние10(хвост, arg1)        дело ')' => состояние11(хвост, arg1)        дело c => бросать новый ParseException(c)      })    }        дело Транслировать() => бросать новый ParseException  }    /*   * 0 $ accept: expr $ end.   *   * $ default принять   */  частный def состояние8(arg1: Int) = (NTexpr(arg1, Транслировать()), 1)    /*   * 1 выражение: выражение '+'. срок   *   * '(' сдвинуть и перейти в состояние 1   * Сдвиг '0' и переход в состояние 2   * Сдвинуть '1' и перейти в состояние 3   *   * срок перейти в состояние 12   * число перейти в состояние 6   */  частный def состояние9(в: Транслировать[Char], arg1: Int) = в матч {    дело дворняга #:: хвост => {      def петля(кортеж: Результат): Результат = {        вал (res, идти к) = кортеж                если (идти к == 0) {          петля(res матч {            дело NTterm(v, в) => состояние12(в, arg1, v)            дело NTnum(v, в) => состояние6(в, v)            дело _ => бросать новый AssertionError          })        } еще (res, идти к - 1)      }            петля(дворняга матч {        дело '(' => состояние1(хвост)        дело '0' => состояние2(хвост)        дело '1' => состояние3(хвост)        дело c => бросать новый ParseException(c)      })    }        дело Транслировать() => бросать новый ParseException  }    /*   * 2 expr: expr '-'. срок   *   * '(' сдвинуть и перейти в состояние 1   * Сдвиг '0' и переход в состояние 2   * Сдвинуть '1' и перейти в состояние 3   *   * срок перейти в состояние 13   * число перейти в состояние 6   */  частный def состояние10(в: Транслировать[Char], arg1: Int) = в матч {    дело дворняга #:: хвост => {      def петля(кортеж: Результат): Результат = {        вал (res, идти к) = кортеж                если (идти к == 0) {          петля(res матч {            дело NTterm(v, в) => состояние13(в, arg1, v)            дело NTnum(v, в) => состояние6(в, v)            дело _ => бросать новый AssertionError          })        } еще (res, идти к - 1)      }            петля(дворняга матч {        дело '(' => состояние1(хвост)        дело '0' => состояние2(хвост)        дело '1' => состояние3(хвост)        дело c => бросать новый ParseException(c)      })    }        дело Транслировать() => бросать новый ParseException  }    /*   * 4 термин: '(' expr ')'.   *   * $ по умолчанию уменьшить с использованием правила 4 (срок)   */  частный def состояние11(в: Транслировать[Char], arg1: Int) = (NTterm(arg1, в), 2)    /*   * 1 выражение: выражение '+' термин.   *   * Уменьшение по умолчанию с использованием правила 1 (выражение)   */  частный def состояние12(в: Транслировать[Char], arg1: Int, arg2: Int) = (NTexpr(arg1 + arg2, в), 2)    /*   * 2 expr: expr '-' термин.   *   * $ по умолчанию уменьшить с использованием правила 2 (expr)   */  частный def состояние13(в: Транслировать[Char], arg1: Int, arg2: Int) = (NTexpr(arg1 - arg2, в), 2)    частный def декремент(кортеж: Результат) = {    вал (res, идти к) = кортеж    утверждать(идти к != 0)    (res, идти к - 1)  }}

Смотрите также

Рекомендации

  1. ^ Томас Дж. Пенелло (1986). "Очень быстрый парсинг LR".
  2. ^ G.H. Робертс (1988). «Рекурсивный подъем: аналог LR рекурсивного спуска».
  3. ^ Leermakers, Augusteijn, Kruseman Aretz (1992). «Функциональный парсер LR».CS1 maint: несколько имен: список авторов (связь)
  4. ^ Ларри Морелл и Дэвид Миддлтон (2003). "Рекурсивный восходящий синтаксический анализ". Журнал компьютерных наук в колледжах. 18 (6). С. 186–201.
  5. ^ Спербер и Тиман (2000). «Генерация парсеров LR путем частичной оценки».
  6. ^ Джон Бойленд и Дэниел Спивак (2009). "Рекурсивный генератор синтаксического анализа восхождения-спуска ScalaBison" (PDF).