Трудный путь
Вы хотите парсер рекурсивного спуска .
Чтобы получить приоритет, вы должны мыслить рекурсивно, например, используя вашу строку образца,
1+11*5
чтобы сделать это вручную, вам нужно прочитать 1
, затем увидеть плюс и начать совершенно новый "сеанс" рекурсивного разбора, начинающийся с 11
... и убедиться, что парсинг 11 * 5
в свой собственный фактор, дающий дерево разбора с 1 + (11 * 5)
.
Это все так больно даже пытаться объяснить, особенно с добавленным бессилием C. Смотрите. После анализа 11, если бы * был на самом деле вместо +, вам пришлось бы отказаться от попытки составить термин и вместо этого проанализируйте сам 11
как фактор. Моя голова уже взрывается. Это возможно с рекурсивной приличной стратегией, но есть лучший способ ...
Легкий (правильный) способ
Если вы используете инструмент GPL, такой как Bison, вам, вероятно, не нужно беспокоиться о проблемах лицензирования, поскольку код C, сгенерированный bison, не покрывается GPL (IANAL, но я уверен, что инструменты GPL не требуют принудительного использования). GPL для сгенерированного кода / двоичных файлов, например, Apple компилирует код, например, Aperture, с помощью GCC, и они продают его без GPL указанного кода).
Скачать Bison (или что-то эквивалентное, ANTLR и т. Д.).
Обычно есть пример кода, на котором вы можете просто запустить bison и получить желаемый код C, демонстрирующий этот калькулятор с четырьмя функциями:
http://www.gnu.org/software/bison/manual/html_node/Infix-Calc.html
Посмотрите на сгенерированный код и убедитесь, что это не так просто, как кажется. Кроме того, преимущества использования такого инструмента, как Bison, заключаются в том, что: 1) вы чему-то учитесь (особенно если читаете книгу Дракона и изучаете грамматику), 2) избегаете NIH попыток изобретать велосипед. С реальным инструментом генератора синтаксических анализаторов у вас действительно есть надежда на дальнейшее расширение, показывающее другим людям, которых вы знаете, что парсеры являются областью инструментов синтаксического анализа.
Обновление:
Люди здесь предложили много здравых советов. Мое единственное предупреждение против того, чтобы пропускать инструменты синтаксического анализа или просто использовать алгоритм Shunting Yard или ручной рекурсивный приличный синтаксический анализатор, состоит в том, что маленькие игрушечные языки 1 могут когда-нибудь превратиться в большие настоящие языки с функциями ( sin, cos, log) и переменные, условия и для циклов.
Flex / Bison вполне может быть излишним для небольшого, простого интерпретатора, но однозначный анализатор + оценщик может вызвать проблемы в будущем, когда необходимо внести изменения или добавить функции. Ваша ситуация будет меняться, и вам нужно будет использовать свое суждение; только не наказывайте других людей за ваши грехи [2] и не создавайте менее чем адекватный инструмент.
Мой любимый инструмент для разбора
Лучший в мире инструмент для работы - это библиотека Parsec (для рекурсивных приличных парсеров), которая поставляется с языком программирования Haskell. Он очень похож на BNF или похож на какой-то специализированный инструмент или предметно-ориентированный язык для синтаксического анализа (пример кода [3]), но на самом деле это просто обычная библиотека в Haskell, что означает, что он компилируется в тот же шаг сборки, что и для остального кода на Haskell, и вы можете написать произвольный код на Haskell и вызвать его в своем анализаторе; вы можете смешивать и сопоставлять другие библиотеки , все в одном коде . (Кстати, встраивание подобного языка в язык, отличный от языка Haskell, приводит к множеству синтаксических искажений. Я сделал это в C #, и он работает довольно хорошо, но он не так хорош и лаконичен.)
Примечания:
1 Ричард Столлман говорит, в Почему вы не должны использовать Tcl
Главный урок Emacs заключается в том, что
язык для расширений не должен
быть просто "языком расширения". Это
должен быть реальным языком программирования,
предназначен для написания и ведения
содержательные программы. Потому что люди
захочет сделать это!
[2] Да, я вечно боюсь использовать этот "язык".
Также обратите внимание, что когда я отправлял эту запись, предварительный просмотр был правильным, но ТАК неадекватный синтаксический анализатор съел мой закрывающий тег привязки в первом абзаце , доказывая, что парсеры не являются чем-то, с чем надо шутить если вы используете регулярные выражения и одно хакерское , вы, вероятно, получите что-то тонкое и маленькое неправильное .
[3] Фрагмент синтаксического анализатора Haskell с использованием Parsec: калькулятор с четырьмя функциями, дополненный показателями, скобками, пробелами для умножения и константами (такими как pi и e).
aexpr = expr `chainl1` toOp
expr = optChainl1 term addop (toScalar 0)
term = factor `chainl1` mulop
factor = sexpr `chainr1` powop
sexpr = parens aexpr
<|> scalar
<|> ident
powop = sym "^" >>= return . (B Pow)
<|> sym "^-" >>= return . (\x y -> B Pow x (B Sub (toScalar 0) y))
toOp = sym "->" >>= return . (B To)
mulop = sym "*" >>= return . (B Mul)
<|> sym "/" >>= return . (B Div)
<|> sym "%" >>= return . (B Mod)
<|> return . (B Mul)
addop = sym "+" >>= return . (B Add)
<|> sym "-" >>= return . (B Sub)
scalar = number >>= return . toScalar
ident = literal >>= return . Lit
parens p = do
lparen
result <- p
rparen
return result