Здравствуй, дорогой друг!
Если перед тобой, как и передо мной, когда-либо стояла задача написания своего синтаксического анализатора - то нам с тобой по пути!
Не будем глубоко вдаваться в теорию, но интересующимся - могу посоветовать книгу, которую можно без зазрения совести назвать классикой: Книга дракона.
А тем, кто хочет перейти к делу прямо сейчас - прошу за мной!
Постановка задачи
Задача, которую мы будем решать тоже можно назвать классической, так как она одновременно проста, но при этом демонстрирует основные этапы создания синтаксического анализатора - создание калькулятора выражений. Более формально, мы будем создавать калькулятор, который:- является однострочным (каждая строка - законченное выражение, перенос строк не поддерживается);
- поддерживает операции сложения, умножения, деления и вычитания;
- поддерживает приоритет операций;
- поддерживает скобки;
Немного теории
Вычисление любого выражения формального языка (а наш язык именно формальный) проходит несколько фаз. Чаще всего это следующие фазы:
- Лексический анализ;
- Синтаксический анализ;
- Семантический анализ;
К счастью, наш язык будет очень простой, поэтому составим какой-то план и будем ему следовать=)
Какой-то план
- Описать грамматику языка;
- Написать лексический анализатор;
- Написать синтаксический анализатор;
- Вычислить выражение, описанное абстрактным синтаксическим деревом;
Грамматика языка
Грамматика языка - это формальное описание структуры языка. Часто для описания грамматики языка используется так называемая форма Бэкуса-Наура или некое ее подобие.
Грамматика языка состоит из нескольких "уравнений", которые показывают как составные части языка (нетерминалы) можно разложить на другие составные и атомарные (терминалы) части языка. Терминалы - являются лексемами - неделимыми с точки зрения грамматики частями языка. В конечном счете любой терминал является последовательностью лексем.
Итак, начнем. Грамматика нашего языка будет выглядеть так:
Итак, начнем. Грамматика нашего языка будет выглядеть так:
- expression = multExpression (('+'|'-') multExpression)*
- multExpression = atom (('*'|'/') atom)*
- atom = NUMBER | '(' expression ')'
Разберем подробнее описанную грамматику. Любое выражение нашего калькулятора представляет собой expression. Сам expression представляет собой один или более выражений multExpression, разделенных знаками '+ или '-'.
multExpression в свою очередь представляет собой один или более выражений atom, разделенных знаками '*' или '/'.
И наконец, atom - это или число - NUMBER, или expression в круглых скобках.
Терминалами в данной грамматике являются:
На примере реального математического выражения, рассмотрим, как будет происходить синтаксический анализ.
Допустим есть выражение: 12 - (1+23) / 56;
На этом фаза лексического анализа заканчивается.
В виде формул процесс синтаксического анализа можно изобразить так:
12 - (1+23) / 56 = expression1 = multExpression1 '-' multExpression2
multExpression1 = atom1 = NUMBER = '12'
multExpression2 = atom2 '/' atom3
atom2 = '(' expression2 ')'
expression2 = multExpression3 '+' multExpression4
multExpression3= atom4 = NUMBER = '1'
multExpression4= atom5 = NUMBER = '23'
atom3 = NUMBER = '56'
Абстрактное синтаксическое дерево, получившееся из нашего выражения, можно изобразить так:
Шаг 3. Вычисление выражения
Теперь, когда все выражение разложено до элементарных составляющих - не составляет труда вычислить значение выражения.
Производя арифметические вычисления в порядке, обратном направлению синтаксического анализа - мы получим значение выражения expression1.
На этом хочу закончить статью.
multExpression в свою очередь представляет собой один или более выражений atom, разделенных знаками '*' или '/'.
И наконец, atom - это или число - NUMBER, или expression в круглых скобках.
Терминалами в данной грамматике являются:
- NUMBER - число с плавающей точкой;
- '(' - открывающая круглая скобка;
- ')' - закрывающая круглая скобка;
- '+' - знак сложения;
- '-' - знак вычитания;
- '*'- знак умножения;
- '/' - знак деления;
- NUMBER - дробное число;
- BRACKET - '(' или ')';
- OPERATOR - '+', '-', '*' или '/';
На примере реального математического выражения, рассмотрим, как будет происходить синтаксический анализ.
Допустим есть выражение: 12 - (1+23) / 56;
Шаг 1: лексический анализ
Целью лексического анализа - разбиение входного потока символов на последовательность лексем. В нашем случае получится следующая последовательность:- NUMBER ('12')
- OPERATOR ('-')
- BRACKET('(')
- NUMBER ('1')
- OPERATOR ('+')
- NUMBER ('23')
- BRACKET (')')
- OPERATOR ('/')
- NUMBER ('56')
- EOF
На этом фаза лексического анализа заканчивается.
Шаг 2: синтаксический анализ
Целью синтаксического анализа является преобразование входного потока лексем в абстрактное синтаксическое дерево, структура которого соответствует грамматике. В виде формул процесс синтаксического анализа можно изобразить так:
12 - (1+23) / 56 = expression1 = multExpression1 '-' multExpression2
multExpression1 = atom1 = NUMBER = '12'
multExpression2 = atom2 '/' atom3
atom2 = '(' expression2 ')'
expression2 = multExpression3 '+' multExpression4
multExpression3= atom4 = NUMBER = '1'
multExpression4= atom5 = NUMBER = '23'
atom3 = NUMBER = '56'
Абстрактное синтаксическое дерево, получившееся из нашего выражения, можно изобразить так:
Видно, что корнем дерева является нетерминал expression, а листьями - терминалы NUMBER, BRACKET и OPERATOR.
Если программа не может построить дерево для конкретного выражения - возникает ошибка синтаксического анализа, что означает, что выражение не соответствует грамматике языка.
Производя арифметические вычисления в порядке, обратном направлению синтаксического анализа - мы получим значение выражения expression1.
На этом хочу закончить статью.
В следующих частях мы реализуем лексический анализатор, синтаксический анализатор и вычисление выражений.
Ждите известий в следующей серии=)
Комментариев нет:
Отправить комментарий