Трудный путь
Вам нужен синтаксический анализатор с рекурсивным спуском .
Чтобы получить приоритет, вам нужно думать рекурсивно, например, используя образец строки,
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] Да, я навсегда испорчен этим «языком».
Также обратите внимание, что когда я отправил эту запись, предварительный просмотр был правильным, но неадекватный парсер SO съел мой закрытый тег привязки в первом абзаце , доказывая, что синтаксические анализаторы не являются чем-то, с чем можно шутить, потому что если вы используете регулярные выражения и один раз взломает вас возможно будет что-то тонкое и мелкое неправильно .
[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