• 实现一个计算器


    产生原因:

    (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可以简单的跑起来:

    折起来吧,效果在前面。

  • 相关阅读:
    图形控件开发商十年之旅回顾
    图表控件,数据可视化,你真的选对了吗?
    话说LightningChart是最快最美的图表控件,到底先进在哪里?
    LayUI技巧总结
    解决EFCore缓存机制导致的数据查询错误问题
    关于反射的那些骚操作
    .NetCore借助AutoMapper实现全局数据字段按角色过滤
    记录一次EFCore CodeFirst迁移实践,解决多个项目表结构同步更新问题
    SQL查询整个数据库中某个特定值所在的表和字段的方法
    Visual Studio利用正则表达式进行查找和替换
  • 原文地址:https://www.cnblogs.com/chenliyang/p/6548182.html
Copyright © 2020-2023  润新知