Важкий шлях
Ви хочете рекурсивний аналізатор спуску .
Щоб отримати перевагу, потрібно думати рекурсивно, наприклад, використовуючи зразок рядка,
1+11*5
щоб зробити це вручну, вам доведеться прочитати 1
, а потім побачити плюс і почати цілком новий рекурсивний "сеанс" розбору, починаючи з 11
... і переконайтесь, що він проаналізував 11 * 5
власний фактор, отримавши дерево розбору 1 + (11 * 5)
.
Це все відчуває настільки болісно навіть для спроб пояснення, особливо з додатковою безсильністю C. Дивіться, після розбору 11, якщо * насправді було +, замість цього вам доведеться відмовитися від спроби ввести термін і замість цього розібрати 11
себе як фактор. Голова в мене вже вибухає. Це можливо за допомогою рекурсивної гідної стратегії, але є кращий спосіб ...
Простий (правильний) спосіб
Якщо ви використовуєте такий інструмент GPL, як Bison, вам, ймовірно, не потрібно турбуватися про проблеми з ліцензуванням, оскільки код C, породжений зубрами, не охоплюється GPL (IANAL, але я впевнений, що інструменти GPL не змушують GPL вмикати згенерований код / двійкові файли; наприклад, Apple компілює код, як, скажімо, Aperture with GCC, і вони продають його без необхідності GPL-коду).
Завантажте Bison (або щось еквівалентне, ANTLR тощо).
Зазвичай існує зразок коду, на який можна просто запустити зубр і отримати потрібний код С, який демонструє цей чотири функціональний калькулятор:
http://www.gnu.org/software/bison/manual/html_node/Infix-Calc.html
Подивіться на згенерований код і побачите, що це не так просто, як це звучить. Крім того , переваги використання такого інструменту , як Bison є : 1) ви дізнаєтеся що - то (особливо якщо ви читали книгу Дракон і дізнатися про граматику), 2) уникнути НИЗ намагається винайти колесо. Завдяки справжньому інструменту генератора аналізаторів, ви насправді маєте надію на збільшення масштабів пізніше, показуючи іншим людям, яким ви знаєте, що парсери - це область інструментів розбору.
Оновлення:
Люди тут пропонували багато обгрунтованих порад. Моє єдине застереження від пропуску інструментів розбору або просто використання алгоритму 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