BoldCalc (Федосов Андрей)

Разберём идейную реализацию программы BoldCalc. Конспект рассчитан на то, что Вы в процессе прочтения сами реализуете работающую программу и закрепите тем самым материал основной лекции. Можно, конечно, не глядя скопировать соответствующие части решения (а можно и скачать само решение, но зачем?). Поскольку в решении не применяется сколь-нибудь сложный алгоритм, рекомендуется убедиться, что вы поняли разбор, закрыть его и решить по-своему.

Условие задачи

Отталкиваясь от пунктов нашего условия будем писать нашу функцию. На вход нам поступают строки, которые мы будем последовательно обрабатывать и некоторый словарь с переменными, о котором мы расскажем ниже. Перед обработкой выполним небольшое преобразование строки, а именно: удалим все пробелы методом replace(), после чего перейдём к основным этапам.

  1. Во-первых, нам необходимо определиться, нужно ли работать с нашей строчкой, или её обработку можно пропустить (в случае, если она является комментарием).
       1     if s.startswith("#"):
       2         return
    
  2. Затем, внимательно прочитав условие, мы можем понять, что в случае, если строка не является комментарием, то перед нами строка одного из двух типов: либо строка с присваиванием (“=”), либо обычное выражение. Определить это мы можем достаточно просто воспользовавшись методом split(). Также на этом шаге необходимо учесть, что знаков равно в такой строке должно быть не больше 1, иначе нам нужно выкинуть соответствующую ошибку.

       1     *V, H = s.split("=")
       2     if len(V)>1:
       3         return "Syntax error"
    
  3. Двигаясь дальше по этой ветке, мы должны проверить, что с левой стороны знака находится идентификатор. В этом нам поможет достаточно простое регулярное выражение, которое должно включать в себя (согласно условию) буквы латинского алфавита и символ нижнего подчёркивания. Иначе мы также выкидываем соответствующую ошибку.
       1     if V and not reVar.match(V[0]):
       2         return "Assignment error"
    
  4. Далее нам необходимо сделать проверку выражения, стоящего с правой стороны от знака “=”, этот этап будет универсальным и подойдёт и для обработки случая строки, не содержащей присваивания.

    • На этом этапе в первую очередь нам необходимо убедиться, что наше выражение не пустое,
    • Затем — что в нём присутствуют только допустимые символы, а именно: буквы и разрешённые операции.
    • Дополнительно мы проверяем, нет ли в нашем выражении запрещённых последовательностей, таких как: “//”, “**”, правильно ли написан идентификатор и ситуации, когда “()” сразу при имени идентификатора. Для чего мы также используем регулярные выражения. Иначе наше выражение составлено неверно, и нам нужно выкинуть ошибку синтаксиса.

       1     if all((H, reVal.match(H), not reInv.search(H))):
       2         pass
       3     else:
       4         return "Syntax error"
    
  5. После того, как мы проверим нашу строку, перейдём к её вычислению, обернув их блоками try-except. На данном этапе стоит обратить внимание на то, чтобы имена в обрабатываемом выражении должны быть уникальными, для чего мы с помощью регулярного выражения проходимся по нашей строке, заменяя методом “sub()” идентификаторы на уникальные (например, приписывая им некоторый префикс в начало).

  6. Также нам необходимо учитывать, что на следующем шаге мы будем работать с функцией eval(), для чего нам необходимо передать ей выражение, в котором все операции деления заменены на целочисленные. В этом нам поможет метод replace().

  7. После чего можем непосредственно переходить к вычислению выражения с использованием функции eval(), которая будем принимать наше выражение и некоторый словарь, в который мы будем записывать пары идентификатор: значение (слегка переименованные переменные калькулятора на время eval станут переменными python)

  8. В ходе вычисления блока try мы должны ловить исключение NameError и возвращать соответствующий вывод. И, соответственно, все остальные Exceptions, которые будем считать за Runtime error.

  9. Для надёжности встроим последнюю проверку, которая будет заключаться в том, что мы сверим тип результата с целочисленным типом “int” и, если это не так, выкинем ошибку синтаксиса.

       1         try:
       2             Hr = reNam.sub(rf"{SUF}\1", H).replace("/", "//")
       3             R = eval(Hr, {'__builtins__': {}}, Var)
       4         except NameError as E:
       5             return  "Name error"
       6         except Exception as E:
       7             return  "Runtime error"
       8         else:
       9             if type(R) is not int:
      10                 return  "Syntax error"
    
  10. После вычисления выражения в случае, если в нашей строке имелось присваивание, следующим шагом пополним наш словарь переменных соответствующей парой. Иначе просто вернём значение выражения.
       1     if V:
       2         Var[SUF+V[0]] = R
       3     else:
       4         return R
    
  11. В основном нашем цикле, который будет последовательно скармливать строки нашей функции обработки просто будем каждый раз выполнять печать результата, в случае, если он не пустой.
       1 s = input().strip().replace(' ', '')
       2 while s:
       3     R = calc(s, Var)
       4     if R != None:
       5         print(R)
       6     s = input().strip().replace(' ', '')
    

LecturesCMC/PythonIntro2020/Homework_BoldCalc/Review (последним исправлял пользователь FrBrGeorge 2020-12-05 17:52:56)