Подтвердить что ты не робот

Как работает простой калькулятор с круглыми скобками?

Я хочу узнать, как работают калькуляторы. Например, скажем, что у нас есть входы в инфиксной нотации:

1 + 2 x 10 - 2

Анализатор должен соблюдать общие правила в математике. В приведенном выше примере это означает:

1 + (2 x 10) - 2 = 19 (вместо 3 x 10 - 2 = 28)

И затем рассмотрим следующее:

1 + 2 x ((2/9) + 7) - 2

Включает ли это абстрактное дерево синтаксиса? Бинарное дерево? Каким образом порядок операций должен быть математически правильным? Должен ли я использовать алгоритм шунтирования, чтобы преобразовать его в постфиксную нотацию? И потом, как бы я разобрал его в постфиксной нотации? Зачем конвертировать в первую очередь?

Есть ли учебник, который показывает, как построены эти относительно простые калькуляторы? Или может кто-нибудь объяснить?

4b9b3361

Ответ 1

Один из способов оценить выражение - это рекурсивный синтаксический анализатор спуска. http://en.wikipedia.org/wiki/Recursive_descent_parser

Здесь пример грамматики в форме BNF: http://en.wikipedia.org/wiki/Backus-Naur_form

Expr ::= Term ('+' Term | '-' Term)*
Term ::= Factor ('*' Factor | '/' Factor)*

Factor ::= ['-'] (Number | '(' Expr ')')

Number ::= Digit+

Здесь * означает, что предыдущий элемент повторяется ноль или более раз, + означает один или несколько повторений, квадратные скобки означают необязательный.

Грамматика гарантирует, что элементы наивысшего приоритета сначала собираются вместе или в этом случае оцениваются в первую очередь. Когда вы посещаете каждый node в грамматике, вместо создания абстрактного дерева синтаксиса вы оцениваете текущий node и возвращаете значение.

Пример кода (не идеальный, но должен дать вам представление о том, как сопоставить BNF для кода):

def parse_expr():
  term = parse_term()
  while 1:
    if match('+'):
      term = term + parse_term()
    elif match('-'):
      term = term - parse_term()
    else: return term

def parse_term():
  factor = parse_factor()
  while 1:
    if match('*'):
      factor = factor * parse_factor()
    elif match('/'):
      factor = factor / parse_factor()
    else: return factor

def parse_factor():
  if match('-'):
    negate = -1
  else: negate = 1
  if peek_digit():
    return negate * parse_number()
  if match('('):
    expr = parse_expr()
    if not match(')'): error...
    return negate * expr
  error...

def parse_number():
  num = 0
  while peek_digit():
    num = num * 10 + read_digit()
  return num

Чтобы показать, как будет оцениваться ваш пример 1 + 2 * 10 - 2:

call parse_expr                              stream is 1 + 2 * 10 - 2
  call parse term
    call parse factor
      call parse number which returns 1      stream is now + 2 * 10 - 2
    match '+'                                stream is now 2 * 10 - 2
    call parse factor
      call parse number which returns 2      stream is now * 10 - 2
      match '*'                              stream is now 10 - 2
      call parse number which returns 10     stream is now - 2
      computes 2 * 10, return 20
    compute 1 + 20 -> 21
    match '-'                                stream is now 2
    call parse factor
      call parse number which returns 2      stream is empty
    compute 21 - 2, return 19
  return 19

Ответ 2

Попробуйте посмотреть Antlr. Это то, что я использовал для создания пользовательского компилятора/парсера... и мог легко связать с калькулятором, который будет очень простой задачей для создания.