Home Backend Development Python Tutorial Make a calculator with 50 lines of Python code

Make a calculator with 50 lines of Python code

Oct 18, 2016 pm 01:21 PM
python code calculator

Introduction

In this article, I will show you how to parse and calculate a four-arithmetic expression like a general-purpose calculator. When we're finished, we'll have a calculator that can handle expressions like 1+2*-(-3+2)/5.6+3. Of course, you can also expand it to be more powerful.

My original intention is to provide a simple and interesting course to explain grammatical analysis and formal grammar (compilation principle content). At the same time, I would like to introduce PlyPlus, a syntax parsing interface that I have been improving intermittently for several years. As an add-on to this course, we'll end up with a safe replacement for eval().

If you want to try the examples given in this article on your own computer, you should install PlyPlus first, using the command pip install plyplus . (Translator’s note: pip is a package management system, used to install software packages written in python. You can find the specific usage on Baidu or Google, so I won’t go into details.)

This article requires the inheritance of python Use to understand.

Grammar


For those of you who don’t understand how parsing and formal grammar work, here’s a quick overview: Formal grammar is a set of rules at different levels used to parse text. Each rule describes how the corresponding portion of the input text is composed.展 Here is an example to show how to analyze 1+2+3+4:


rule #1 -Add is Made of Add+Number

or Number

add: add'+'number

| number'+'number

;

The parser will look for add+number or number+number every time, and when it finds one, it will convert it to add. Basically, the goal of every parser is to find the highest level of expression abstraction possible.

Here are each step of the parser:

number + number + number + number

The first conversion turns all Numbers into "number" rules

[number + number] + number + number

Parse The processor found its first matching pattern!

[add + number] + number

After converting into a pattern, it starts looking for the next

[add + number]

add

These ordered symbols become two simple ones on one level Rules: number+number and add+number. This way, you only need to tell the computer if you solve these two problems, and it can parse the entire expression. In fact, no matter how long the addition sequence is, it can be solved! This is the power of formal grammar.

Operator precedence

Arithmetic expressions are not just a linear growth of symbols, operators create an implicit hierarchy, which is very suitable for representation in a formal grammar:


1 + 2 * 3 / 4 - 5 + 6

This is equivalent to:

1 + (2 * 3 / 4) - 5 + 6

We can represent the structure in this grammar through nested rules:

add: add+mul

| mul'+'mul

;

mul: mul '*; number

| number'*'number

;

By setting add to operate on mul instead of number, we get multiplication-first rule.

Let us simulate in our mind the process of using this magical parser to analyze 1+2*3*4:

number + number * number * number

number + [number * number] * number

parsing The parser doesn't know the result of number+number, so here's another option for it (the parser)

number + [mul * number]

number + mul

Now we're in a bit of a bind! The parser doesn't know what to do with it number+mul. We can distinguish this situation, but if we continue to explore, we will find that there are many different possibilities that were not considered, such as mul+number, add+number, add+add, etc.

So what should we do?

Fortunately, we can do a little "trick": we can think of a number itself as a product, and a product itself as a sum!

This idea may seem a little weird at first, but it does make sense:

add: add'+'mul

| mul'+'mul

| mul

;

mul: mul' *'number

| number'*'number

| number

;

But if mul can become add, and number can become mul, the contents of some lines will become redundant. Discarding them, we get:

add: add'+'mul

| mul

;

mul: mul'*'number

| number

;

Let's use this new one Let’s simulate running 1+2*3*4 using the grammar:

number + number * number * number

Now there is no rule corresponding to number*number, but the parser can "get creative"

number + [number] * number * number

number + [mul * number] * number

number + [mul * number]

[number] + mul

[mul] + mul

[add + mul]

add

successful! ! !

If you think this is amazing, then try to simulate it with another arithmetic expression, and then see how the expression can solve the problem step by step in the correct way. Or wait and read the next section to see how the computer works step by step!

Run the parser

Now that we have a pretty good idea of ​​how to make our grammar work, let’s write an actual grammar to apply:

start: add;

add: add add_symbol mul | mul;

mul: mul mul_symbol number | number;

number:'[d.]+'; // Regular expression of decimal number

mul_symbol:'*'|'/ ';// Match * or /

add_symbol:'+'|'-';// Match + or -

You might want to brush up on regular expressions, but regardless, the syntax is pretty straightforward. Let’s test it with an expression:

>>>fromplyplusimportGrammar

>>> g=Grammar("""...""")

>>>printg.parse('1+2* 3-5').pretty()

start

add

                                                                    add_symbol

                                                                                                                                mul_symbol

                                                                                                           ​ 5

Great job!

Take a closer look at the tree and see what level the parser chose.

If you wish to run this parser yourself and use your own expressions, all you need is Python. After installing Pip and PlyPlus, paste the above command into Python (remember to replace '...' with the actual syntax~).

Shape the tree

Plyplus will automatically create a tree, but it is not necessarily optimal. Putting number into mul and mul into add is great for creating a hierarchy, but now that we have a hierarchy they become a burden. We tell Plyplus to "expand" (i.e. delete) the rules by prefixing them.

An @ will often expand a rule, a # will flatten it, and a ? will expand it if it has a child node. In this case, ? is what we need.

start: add;

?add: add add_symbol mul | mul; '[d.]+';

mul_symbol:'*'|'/';

add_symbol:'+'|'-';

The tree looks like this under the new syntax:

>>> g= Grammar("""...""")

>>>printg.parse('1+2*3-5').pretty()

start

add

add

number

1

                add_symbol

                                                     

Number

3

add_symbol

- -

number

5

Oh, this is much simpler, Dare I say, it's very good.

Bracket processing and other features

So far, we are still obviously missing some necessary features: brackets, unit operators (-(1+2)), and allowing null characters in the middle of expressions. In fact, these features are very easy to implement. Let’s try them below.

An important concept needs to be introduced first: atoms. All operations that occur within an atom (in parentheses and unit operations) take precedence over all addition or multiplication operations (including bitwise operations). Since the atom is just a priority constructor and has no grammatical meaning, help us add the "@" symbol to ensure that it can be expanded during compilation.

The simplest way to allow spaces to appear in expressions is to use this explanation: add SPACE add_symbol SPACE mul | mul; But this explanation results in verbosity and poor readability. So, we need to make Plyplus always ignore spaces.

The following is the complete syntax, including the above features:

start: add;

?add: (add add_symbol)? mul;

?mul: (mul mul_symbol)? atom;

@atom: neg | number |'('add')';

neg:'-'atom;

number:'[d.]+';

mul_symbol:'*'|'/';

add_symbol:' +'|'-';

WHITESPACE:'[ t]+'(%ignore);

Please make sure you understand this syntax before proceeding to the next step: calculation!

Operation


Now, we can convert an expression into a hierarchical tree. We only need to scan the tree branch by branch to get the final result.

We are now going to start writing code. Before that, I need to explain two things about this tree:

1. Each branch is an instance containing the following two attributes:

Head: Rules Names (such as add or number); tail: contains a list of all sub -rules that match it.

2.Plyplus will delete unnecessary tags by default. In this example, '( ' , ')' and '-' are removed. But add and mul will have their own rules, and Plyplus will know that they are necessary and will not delete them. If you need to retain these tags, you can manually turn off this feature, but from my experience, it is better not to do this, but to manually modify the relevant syntax for better results.

Back to business, now we start writing code. We'll use a very simple converter to scan this tree. It starts scanning from the outermost branch until it reaches the root node, and our job is to tell it how to scan. If all goes well, it will always start scanning from the outermost layer! Let's see how it works.

>>>importoperator as op

>>>fromplyplusimportSTransformer


classCalc(STransformer):


def_bin_operator(self, exp):

arg1, operator_ symbol, arg2=exp.tail

F Operator_func = {'+': OP.ADD,


'-': OP.Sub,

'*': OP.mul,

'/': OP.DIV} [Operator_symbol]

O Returnoperator_func (ARG1, ARG2)


number = lambdaseld, exp: float (exp.tail [0])

neg = lambdaseld, exp: -exp.tail [0] __Default _ Lambdas ELF, EXP: exp.tail[0]

add=_bin_operator

mul=_bin_operator

Each method corresponds to a rule. If the method does not exist, the __default__ method will be called. We have omitted start, add_symbol and mul_symbol because they will only return their own branches.

I used float() to parse the numbers, which is a lazy method, but I could also use a parser to do it.

To make the statements neat, I used the operator module. For example add is basically 'lambda x,y: x+y' or something like that.

OK, now let’s run this code to check the results.

>>> Calc().transform( g.parse('1 + 2 * -(-3+2) / 5.6 + 30'))

31.357142857142858

What about eval()? 7

>>>eval('1 + 2 * -(-3+2) / 5.6 + 30')

31.357142857142858

Successful:)

Last step: REPL

For the sake of beauty, we Wrap it into a nice calculator REPL:

defmain():

calc=Calc()

whileTrue:

try:

s=raw_input('> ')

exceptEOFError :

break

IFS == '':

Break

Tree = Calc_grammar.parse (s)

PrintCalc.transform (Tree)

The complete code can be obtained from here:

https: //github.com/erezsh// plyplus/blob/master/examples/calc.py

Statement of this Website
The content of this article is voluntarily contributed by netizens, and the copyright belongs to the original author. This site does not assume corresponding legal responsibility. If you find any content suspected of plagiarism or infringement, please contact admin@php.cn

Hot AI Tools

Undresser.AI Undress

Undresser.AI Undress

AI-powered app for creating realistic nude photos

AI Clothes Remover

AI Clothes Remover

Online AI tool for removing clothes from photos.

Undress AI Tool

Undress AI Tool

Undress images for free

Clothoff.io

Clothoff.io

AI clothes remover

Video Face Swap

Video Face Swap

Swap faces in any video effortlessly with our completely free AI face swap tool!

Hot Tools

Notepad++7.3.1

Notepad++7.3.1

Easy-to-use and free code editor

SublimeText3 Chinese version

SublimeText3 Chinese version

Chinese version, very easy to use

Zend Studio 13.0.1

Zend Studio 13.0.1

Powerful PHP integrated development environment

Dreamweaver CS6

Dreamweaver CS6

Visual web development tools

SublimeText3 Mac version

SublimeText3 Mac version

God-level code editing software (SublimeText3)

Can vs code run in Windows 8 Can vs code run in Windows 8 Apr 15, 2025 pm 07:24 PM

VS Code can run on Windows 8, but the experience may not be great. First make sure the system has been updated to the latest patch, then download the VS Code installation package that matches the system architecture and install it as prompted. After installation, be aware that some extensions may be incompatible with Windows 8 and need to look for alternative extensions or use newer Windows systems in a virtual machine. Install the necessary extensions to check whether they work properly. Although VS Code is feasible on Windows 8, it is recommended to upgrade to a newer Windows system for a better development experience and security.

How to run programs in terminal vscode How to run programs in terminal vscode Apr 15, 2025 pm 06:42 PM

In VS Code, you can run the program in the terminal through the following steps: Prepare the code and open the integrated terminal to ensure that the code directory is consistent with the terminal working directory. Select the run command according to the programming language (such as Python's python your_file_name.py) to check whether it runs successfully and resolve errors. Use the debugger to improve debugging efficiency.

Can visual studio code be used in python Can visual studio code be used in python Apr 15, 2025 pm 08:18 PM

VS Code can be used to write Python and provides many features that make it an ideal tool for developing Python applications. It allows users to: install Python extensions to get functions such as code completion, syntax highlighting, and debugging. Use the debugger to track code step by step, find and fix errors. Integrate Git for version control. Use code formatting tools to maintain code consistency. Use the Linting tool to spot potential problems ahead of time.

Is the vscode extension malicious? Is the vscode extension malicious? Apr 15, 2025 pm 07:57 PM

VS Code extensions pose malicious risks, such as hiding malicious code, exploiting vulnerabilities, and masturbating as legitimate extensions. Methods to identify malicious extensions include: checking publishers, reading comments, checking code, and installing with caution. Security measures also include: security awareness, good habits, regular updates and antivirus software.

Python: Automation, Scripting, and Task Management Python: Automation, Scripting, and Task Management Apr 16, 2025 am 12:14 AM

Python excels in automation, scripting, and task management. 1) Automation: File backup is realized through standard libraries such as os and shutil. 2) Script writing: Use the psutil library to monitor system resources. 3) Task management: Use the schedule library to schedule tasks. Python's ease of use and rich library support makes it the preferred tool in these areas.

What is vscode What is vscode for? What is vscode What is vscode for? Apr 15, 2025 pm 06:45 PM

VS Code is the full name Visual Studio Code, which is a free and open source cross-platform code editor and development environment developed by Microsoft. It supports a wide range of programming languages ​​and provides syntax highlighting, code automatic completion, code snippets and smart prompts to improve development efficiency. Through a rich extension ecosystem, users can add extensions to specific needs and languages, such as debuggers, code formatting tools, and Git integrations. VS Code also includes an intuitive debugger that helps quickly find and resolve bugs in your code.

Python vs. JavaScript: The Learning Curve and Ease of Use Python vs. JavaScript: The Learning Curve and Ease of Use Apr 16, 2025 am 12:12 AM

Python is more suitable for beginners, with a smooth learning curve and concise syntax; JavaScript is suitable for front-end development, with a steep learning curve and flexible syntax. 1. Python syntax is intuitive and suitable for data science and back-end development. 2. JavaScript is flexible and widely used in front-end and server-side programming.

Golang vs. Python: Concurrency and Multithreading Golang vs. Python: Concurrency and Multithreading Apr 17, 2025 am 12:20 AM

Golang is more suitable for high concurrency tasks, while Python has more advantages in flexibility. 1.Golang efficiently handles concurrency through goroutine and channel. 2. Python relies on threading and asyncio, which is affected by GIL, but provides multiple concurrency methods. The choice should be based on specific needs.

See all articles