zoukankan      html  css  js  c++  java
  • 实现一个计算器

    产生原因:

    (1)一直以来,我都想写一门语言,但无从下手。
    (2)我找到了很多编译原理的教程,但始终觉得内容晦涩,理解不了,所以先尝试写一个简单的,比如:计算器。
    (3)网上有很多关于计算器的实现,但大多需要有编译原理的基础,对于我这种小白实在难以理解。
    (4)我决定采用暴力模拟的方式,需要用正则表达式,但我不想自己实现,所以用js。

    最终要实现什么效果

      

    计算器接受一串字符,处理后返回结果。

    我们来看一下要做什么:

    首先需要知道有哪些“元素”,比如“12+34×56"的元素有整数12,加号,整数34,乘号,整数56,这个过程称为词法分析

    然后根据符号的优先级进行组合,其过程相当于加括号,12+(34*56),这个过程称为语法分析

    借用正则表达式,可以简单暴力的实现词法分析。

    什么是正则表达式

    正则表达式的概念,和编译原理一样,都要费好大功夫来理解,当初也是各种查资料。

    尽量简单的讲一讲吧。

    定义:正则表达式是一种生成器,可以生成大量相同模式的字符串。

    字符串的概念大家都懂,来看一下正则表达式是怎么生成字符串的。

    例子:正则表达式 ab* 可以生成'a' , 'ab' , 'abb' , 'abbb' ...

    正则表达式有三种规则(并,或,闭包),其他规则都是由这三个规则组合而成。

    并,直接连在一起,表示相连,例如:abc 生成’abc'

    或,以符号|分隔,表示或,例如:a|b生成'a','b'

    闭包,加符号*,表示重复任意次,例如:a* 生成 '','a', 'aa', 'aaa'...

    一些常用的规则:

    [],例如:[abcd]等价于a|b|c|d

    +,例如:a+等价于aa*

    ?,例如:a?等价于 a|空

    d,等价于[0123456789],还可以用[0-9]

    w,等价于[A-Za-z0-9]

    W,非w

    ,表示w与W的交界处

    词法分析

    计算器用到的正则表达式:

    +:+

    -:-

    *:*

    /:/

    (:(

    ):)

    number:d+

    注意:规则中有的字符要转义,如:+

    不断用正则来匹配输入的字符串,就可以。

    使用js来实现:

    复制代码
     1 function get_words(buf){
     2     patterns = [
     3         ['(',        /(/],
     4         [')',        /)/],
     5         ['+',        /+/],
     6         ['-',        /-/],
     7         ['*',        /*/],
     8         ['/',        ///],
     9         ['number',     /d+(.d+)?/]
    10         ];
    11     
    12     words = [];
    13     flag = true;
    14     while (buf && flag) {
    15         flag = false;
    16         for (p in patterns) {
    17             buf = buf.trimLeft();
    18             ex = patterns[p][1].exec(buf);
    19             if (ex && ex['index'] == 0) {
    20                 str = ex[0];
    21                 flag = true;
    22                 buf = buf.slice(str.length,Infinity);
    23                 words.push([patterns[p][0], parseFloat(str)]);
    24                 break;
    25             }
    26         }
    27     }
    28     return words;
    29 }
    复制代码

    对于'12+34 * (78-56)',会得到:

    复制代码
    number,12
    +,NaN
    number,34
    *,NaN
    (,NaN
    number,78
    -,NaN
    number,56
    ),NaN
    复制代码

    至此,词法分析完成。

    语法分析

    我们采用类似于正则的方式来描述语法分析的过程,可称其为文法

    分析一波。

    括号优先级最高,所以遇见括号就要计算,文法为:

    <factor> => ( '(' <expr> ')' )  | 'number'

    引号的称终结符,尖括号的称非终结符

    非终结符表示可以继续推导,终结符表示推导终点。

    其中<expr>表示整个算式

    乘除优先级比加法高,所以遇见乘除就要计算,文法为:

    <term> => <factor> ( '*' <factor> ) | ( '/' <factor> *

    然后是加减:

    <expr> => <term> ( '+' <term> ) | ( '-' <term> *

     这些都可以用正则来理解。

    其中每个非终结符都做成一个函数,翻译过来就成。

    复制代码
     1 function parse(words) {
     2     // <expr>     => <term> (('+' <term>) | ('-' <term>))*
     3     // <term>     => <factor> (('*' <factor>) | ('/' <factor>))*
     4     // <factor> => ('(' <expr> ')')  | 'number'
     5     p = 0;  
     6     
     7     function type() {
     8         if (p >= words.length) return null;
     9         return words[p][0];
    10     }
    11     function match(sym) {
    12         if (words[p][0] == sym) {
    13             return words[p++][1];
    14         }
    15         console.log('
    error
    ');
    16     }
    17     function expr() {
    18         value = term();
    19         while (type() == '+' || type() == '-') {
    20             if (type() == '+') {
    21                 match('+');
    22                 value += term();
    23             } else {
    24                 match('-');
    25                 value -= term();
    26             }
    27         }
    28         return value;
    29     }
    30     function term() {
    31         value = factor();
    32         while (type() == '*' || type() == '/') {
    33             if (type() == '*') {
    34                 match('*');
    35                 value *= factor();
    36             } else {
    37                 match('/');
    38                 value /= factor();
    39             }
    40         }
    41         return value;
    42     }
    43     function factor() {
    44         if (type() == '(') {
    45             match('(');
    46             value = expr();
    47             match(')');
    48         } else if (type() == 'number') {
    49             value =  match('number');
    50         }
    51         return value;
    52     }
    53     
    54     return expr();
    55 }
    复制代码

    写完了,哈哈。

    总结

    用node.js可以简单的跑起来:

    折起来吧,效果在前面。

  • 相关阅读:
    codevs 1115 开心的金明
    POJ 1125 Stockbroker Grapevine
    POJ 2421 constructing roads
    codevs 1390 回文平方数 USACO
    codevs 1131 统计单词数 2011年NOIP全国联赛普及组
    codevs 1313 质因数分解
    洛谷 绕钉子的长绳子
    洛谷 P1276 校门外的树(增强版)
    codevs 2627 村村通
    codevs 1191 数轴染色
  • 原文地址:https://www.cnblogs.com/chenliyang/p/6548182.html
Copyright © 2011-2022 走看看