A small calculator that can calculate the value of a simple mathematical expression is not a compiler, but a knowledge of the compiler is used. I have been reading some compiler stuff recently. Therefore, writing this simplest calculator is an image of the abstract compiler knowledge, it also lays the foundation for adding complicated things-statements. This calculator is written for reference by the tiny compiler implemented in the compilation principles and practices. Tiny is a compiler worth studying. It can be said that it is small and dirty. From lexical analysis to code production, the code is very clear and easy to understand. I think to understand the compiler, you can start with tiny and run it and analyze it. Not to mention anything. Start recording this small calculator.

Let's talk about the following requirements:

1. Only the simplest +-*/operation is supported.

2. support nested brackets

3. Only positive numbers are supported.

The three simple requirements can focus on compiler-related knowledge. For example, you can calculate the expression (5 + 3) * 2 and obtain the value 16. Or calculate 7-5*3 to get the value-3.

Next, let's talk about how to implement it. Before that, I first talked about a method that I learned in college. When I learned the data structure stack, the instructor proposed a method to use the stack to calculate the value of a simple mathematical expression. The method is to scan this expression in sequence, determine the order of its entry to the stack based on the operator's priority, get a suffix expression of the expression, and finally use this suffix expression to evaluate the value.

The method to be introduced here is a bit similar to the above method. It also needs to scan the expression first. However, what we get after scanning is not a suffix expression, but a syntax tree, then, recursively solve the syntax tree. Below is the syntax tree corresponding to expression (5 + 3) * 2. This syntax tree can also be obtained through post-order traversal, so the two methods are still the same.

*

/\

+ 2

/\

5 3

Anyone who has learned how to compile knows that to implement a language, you must first define its syntax. The syntax is used to define what the language looks like (nonsense). For example, the calculator syntax defines the priority and combination of operations. If the expression does not conform to the syntax definition during the scan, the expression is considered invalid. For example, the expression "5 + 3-" is invalid because it is not followed by the subtraction. The syntax is shown below:

Expr-> term | term + term | term-term

Term-> factor | factor * factor | factor/factor

Factor-> Number | (expr)

Number-> (0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 )*

I think the definition syntax is a very interesting thing. At least this syntax is not made by me and is obtained from the tiny compiler. This syntax looks clear. The order in which the three syntaxes appear indicates the priority of the calculation. The parentheses are the highest, the multiplication and division are the second, and the addition and subtraction are the lowest. A factor represents the smallest computational factor. It can be a number or an expression enclosed in parentheses. Term represents a multiplication or division expression, and exprt represents an addition or subtraction expression. Term and expr can also be num or (expr)

The definition syntax should consider eliminating the left recursion problem, for example, if the first syntax expr-> term | term + term | term-term is written as expr-> term | expr + term | expr-term, this shows that addition and subtraction have a left combination, however, if this syntax is directly written as a recursive function, there will be an endless loop. This Syntax of tiny eliminates the left recursion and throws the left concatenation problem of addition and subtraction to the function corresponding to expr for processing.

Next, let's start with the Code. First, let's look at the functions that process expr.

TreeNode *exp(){TreeNode *node;TreeNode *lnode, *rnode;node = term();/*如果下一个符号是+ 或者-，那么就以操作符作为根节点两个操作数作为子节点*/while ((ADD == token) || (MINUS == token)){/*左节点*/lnode = node;/*操作符节点，即根节点*/node = newNode();node->attr.e = OpK;node->val.tt = token;node->child[0] = lnode;match(token);/*右节点*/rnode = term();node->child[1] = rnode;}return node;}

This code first calls the term () function to process a multiplication or division expression, and then determines whether the next token is a addition or subtraction operator. If yes, the operator is used as the root node, use the node returned by the term () function as the left node of the root node, and then call the term () function to return an expression node as the right node. The left and right nodes represent the two operands of the operator.

The following is the code of the term function, which is very similar to the exp function and does not need to be described in detail.

TreeNode *term(){TreeNode *node;TreeNode *lnode, *rnode;node = factor();/*将乘法或者除法操作符作为根节点，并得到左右节点*/while ((MUL == token) || (DIV == token)){lnode = node;node = newNode();node->attr.e = OpK;node->val.tt = token;node->child[0] = lnode;match(token);rnode = factor();node->child[1] = rnode;}return node;}

The next step is the factor function, which represents a minimum computing factor.

TreeNode *factor(){TreeNode *node;switch (token){/*一个数字*/case NUM:/*生成并返回一个节点，节点类型就是常数*/node = newNode();node->attr.e = ConstK;node->val.num = atoi(tval);match(NUM);break;/*左括号*/case LPAREN:match(LPAREN);/*调用exp来解析一个表达式*/node = exp();match(RPAREN);break;default:printf("<Error>factor: Token cann't handled.\n");exit(1);break;}return node;}

The factor function determines whether the next token is a number or a bracket. If it is a number, it directly returns the node represented by the number. If it is a left bracket, it is an expression in the brackets, call exp to analyze the expression and then return the node of the expression.

Through the above functions, we can return a syntax tree. The following calc function uses this syntax tree to recursively evaluate values.

int calc(TreeNode *node){int val;int val1, val2;if (NULL == node){printf("<Error>calc: syntax error.\n");exit(1);}/*根据节点的属性返回相应的值，目前节点有两种属性:数字或者操作符*/switch (node->attr.e){/*数字属性节点直接返回值*/case ConstK:return node->val.num;break;/*操作符属性节点值需要先计算两个操作数的值，再根据操作符来计算最后的结果*/case OpK:val1 = calc(node->child[0]);val2 = calc(node->child[1]);switch (node->val.tt){case ADD:val = val1 + val2;break;case MINUS:val = val1 - val2;break;case MUL:val = val1 * val2;break;case DIV:val = val1 / val2;break;default:printf("<Error>cal: Unknown operation.\n");exit(1);break;}break;default:printf("<Error>calc: Unknown expression type.\n");exit(1);break;}return val;}

The main code of this small calculator has been introduced, and other supporting functions are left. For example, the gettoken function is used to obtain a token, the match function is used to determine whether the obtained tokens are consistent with the token required by the current syntax. Otherwise, a syntax error occurs.

Compile the Code:

Gcc-fno-builtin mycomplier. C-o mycomplier

Create an exprtest file with the content of the expression to be calculated, such as 3 + (10-2) * 5

After saving the exprtest file, input mycomplier exprtest to output the result is 43.

All right, this small calculator is over. It also has many numeric functions to be improved, such as supporting negative numbers and other operators. But as I said at the beginning, I focus on compiler knowledge. I am an acute child, so I don't have to spend much time dealing with these numeric operations. This calculator will be improved later, and the processing statement function will be added to it to make it more like compiling a language.

Principle of the complete code download path compiler-a small calculator

If you have any questions, please send an email to us for further study :)

Email: [email protected]

Compiler-simple mathematical expression calculator