我的编程空间,编程开发者的网络收藏夹
学习永远不晚

PHP怎么实现词法分析与自定义语言

短信预约 -IT技能 免费直播动态提醒
省份

北京

  • 北京
  • 上海
  • 天津
  • 重庆
  • 河北
  • 山东
  • 辽宁
  • 黑龙江
  • 吉林
  • 甘肃
  • 青海
  • 河南
  • 江苏
  • 湖北
  • 湖南
  • 江西
  • 浙江
  • 广东
  • 云南
  • 福建
  • 海南
  • 山西
  • 四川
  • 陕西
  • 贵州
  • 安徽
  • 广西
  • 内蒙
  • 西藏
  • 新疆
  • 宁夏
  • 兵团
手机号立即预约

请填写图片验证码后获取短信验证码

看不清楚,换张图片

免费获取短信验证码

PHP怎么实现词法分析与自定义语言

本文小编为大家详细介绍“PHP怎么实现词法分析与自定义语言”,内容详细,步骤清晰,细节处理妥当,希望这篇“PHP怎么实现词法分析与自定义语言”文章能帮助大家解决疑惑,下面跟着小编的思路慢慢深入,一起来学习新知识吧。

                           

之前项目有一个需求,业务人员使用中文编写一些自定义公式,然后需要我们后台执行将结果返回到界面上,于是就基于有限状态机写了这个词法分析器,比较简单,希望能够抛砖引玉。

一、分析需求

输入中文公式,返回结果,比如:

现有薪资=10000;个税起点=3000;当前年份=2021;如果(当前年份=2022){    个税起点=5000;}返回 (现有薪资-个税起点) * 0.2;

二、实现需求

最初的想法是使用字符串替换的方式,将中文关键字替换成php的关键字,然后调用eval执行,这样确实也是可以的,但是总觉得不是很美丽,并且不能实现动态解析。就想着自己实现一个简单的词法分析,然后结合ast将词法转换成php代码执行,岂不快哉。当前版本没有用到抽象语法树来生成代码,全部使用字符串拼接。

<?phpclass Lexer {    // 内置关键字集合    public $keywordList = [];    // 内置操作符集合    public $operatorList = [        "+", "-", "*", "/", "=", ">", "<", "!", "(", ")", "{", "}", ",", ";"    ];    // 源代码    private $input;    // 当前的字符    private $currChar;    // 当前字符位置    private $currCharPos = 0;    // 结束符    private $eof = "eof";    // 当前编码    private $currEncode  = "UTF-8";    // 内置关键字    public const VAR = "variable";    public const STR = "string";    public const KW  = "keyword";    public const OPR = "operator";    public const INT = "integer";    public const NIL = "null";        public function __construct(string $input) {        $this->input    = $input;        $this->currChar = mb_substr($this->input, $this->currCharPos, 1);    }        public function setKeywordList($keywordList) {        $this->keywordList = $keywordList;    }        public function parseInput() {        if ($this->input == "") {            throw new Exception("code can not be empty");        }        $tokens = [];        do {            $token = $this->nextToken();            if ($token["type"] != "eof") {                $tokens[] = $token;            }            if ($token["type"] == self::KW) {                $tokens[] = $this->makeToken(self::NIL, " ");            }        } while ($token["type"] != "eof");        return $tokens;    }        public function nextToken() {        $this->skipBlankChar();        $this->currChar == "" && $this->currChar = $this->eof;        if ($this->isCnLetter()) {            $word = $this->matchUntilNextCharIsNotCn();            if ($this->isKeyword($word)) {                $this->currCharPos -= 1;                return $this->currToken(static::KW, $word);            }            // 不是关键字的全部归为变量            return $this->makeToken(static::VAR, $word);        }        // 如果是操作符        if ($this->isOperator()) {            return $this->currToken(static::OPR, $this->currChar);        }        // 如果是数字        if ($this->isNumber()) {            return $this->currToken(static::INT, $this->currChar);        }        // 如果是字符串        if ($str = $this->isStr()) {            return $this->currToken(static::STR, $str);        }        // 如果是变量        if ($this->isVar()) {            $word = $this->matchVar();            if ($this->isKeyword($word)) {                return $this->currToken(static::KW, $word);            }            return $this->makeToken(static::VAR, $word);        }        if ($this->currChar == $this->eof) {            return $this->currToken('eof', $this->currChar);        }        return $this->currToken(static::VAR, $this->currChar);    }        private function matchVar(string $input = "") {        $word = $input ?: '';        while ($this->isVar()) {            $word .= $this->currChar;            $this->nextChar();        }        return $word;    }        private function isVar() {        return $this->isCnLetter() || $this->isEnLetter();    }        private function skipBlankChar() {        while (ord($this->currChar) == 10 ||            ord($this->currChar) == 13 ||            ord($this->currChar) == 32) {            $this->nextChar();        }    }        private function currToken(string $type, $word) {        $token = $this->makeToken($type, $word);        $this->nextChar();        return $token;    }        private function makeToken(string $type, string $char) {        return ["type" => $type, "char" => $char, "pos" => $this->currCharPos];    }        private function isEnLetter() {        if ($this->currChar == "" || $this->currChar == $this->eof) {            return false;        }        $ord = mb_ord($this->currChar, $this->currEncode);        if ($ord > ord('a') && $ord < ord('z')) {            return true;        }        return false;    }        private function isCnLetter() {        return preg_match("/^[\x{4e00}-\x{9fa5}]+$/u", $this->currChar);    }        private function isNumber() {        return is_numeric($this->currChar);    }        private function isStr() {        return $this->matchCompleteStr();    }        private function matchCompleteStr() {        $char = "";        if ($this->currChar == "\"") {            $this->nextChar();            while ($this->currChar != "\"") {                if ($this->currChar != "\"") {                    $char .= $this->currChar;                }                $this->nextChar();            }            return $char;        }        return $char;    }        private function isOperator() {        return in_array($this->currChar, $this->operatorList);    }        private function matchUntilNextCharIsNotCn() {        $char = "";        while ($this->isCnLetter()) {            $char .= $this->currChar;            $this->nextChar();        }        return $char;    }        private function nextChar() {        $this->currCharPos += 1;        $this->currChar    = mb_substr($this->input, $this->currCharPos, 1);        if ($this->currChar == "") {            $this->currChar = $this->eof;        }    }        private function isKeyword(string $input) {        return ($this->keywordList[$input] ?? "") != "";    }    public function convert(array $tokens) {        $code = "";        foreach ($this->lexerIterator($tokens) as $generator) {            switch ($generator["type"]) {                case static::KW:                    $code .= $this->keywordList[$generator["char"]];                    break;                case static::VAR:                    $code .= sprintf("$%s", $generator["char"]);                    break;                case static::OPR:                    $code .= $this->replace($generator["char"]);                    break;                case static::INT:                    $code .= $generator["char"];                    break;                case static::STR:                    $code .= sprintf("\"%s\"", $generator["char"]);                    break;                default:                    $code .= $generator["char"];            }        }        return $code;    }    private function replace(string $char) {        return str_replace("+", ".", $char);    }        private function lexerIterator(array $tokens) {        foreach ($tokens as $index => $token) {            yield $token;        }    }}

三、如何使用

require __DIR__ . "/vendor/autoload.php";// 定义一段代码$code = <<<EOF姓名="腕豪";问候="你好啊";地址=(1+2) * 3;如果(地址 > 3){    地址=1;}否则{    地址="艾欧尼亚"}说话 = ("我"+"爱")+"你";返回 姓名+年龄;EOF;$lexer = new Lexer($code);// 自定义你的关键字$kwMap = [    "如果" => "if", "否则" => "else", "返回" => "return", "否则如果" => "elseif"];$lexer->setKeywordList($kwMap);// 这里是生成的词$tokens = $lexer->parseInput();// 将生成的词转成php,当然你也可以尝试用php-parse转ast再转成php,这里只是简单的拼接var_dump($lexer->convert($tokens));

生成词

[{    "type": "variable",    "char": "姓名",    "pos": 2}, {    "type": "operator",    "char": "=",    "pos": 2}, {    "type": "string",    "char": "腕豪",    "pos": 7}, {    "type": "operator",    "char": ";",    "pos": 8}, {    "type": "variable",    "char": "问候",    "pos": 13}, {    "type": "operator",    "char": "=",    "pos": 13}, {    "typ e": "string",    "char": "你好啊",    "pos": 17}, {    "type": "operator",    "char": ";",    "pos": 18}, {    "type": "variable",    "char": "地址",    "pos": 23}, {    "type": "operator",    "char": "=",    "pos": 23}, {    "type": "operator",    "char": "(",    "pos": 24}, {    "type": "integer",    "char": "1",    "pos": 25}, {    "type": "operator",    "char": " +",    "pos": 26}, {    "type": "integer",    "char": "2",    "pos": 27}, {    "type": "operator",    "char": ")",    "pos": 28}, {    "type": "operator",    "char": "*",    "pos": 30}, {    "type": "integer",    "char": "3",    "pos": 32}, {    "type": "operator",    "char": ";",    "pos": 33}, {    "type": "keyword",    "char": "如果",    "pos": 37}, {    "type": "nul l",    "char": " ",    "pos": 38}, {    "type": "operator",    "char": "(",    "pos": 38}, {    "type": "variable",    "char": "地址",    "pos": 41}, {    "type": "operator",    "char": ">",    "pos": 42}, {    "type": "integer",    "char": "3",    "pos": 44}, {    "type": "operator",    "char": ")",    "pos": 45}, {    "type": "operator",    "char": "{",    "pos": 46}, {    "type": "variable",    "char": "地址",    "pos": 55}, {    "type": "operator",    "char": "=",    "pos": 55}, {    "type": "integer",    "char": "1",    "pos": 56}, {    "type": "operator",    "char": ";",    "pos": 57}, {    "type": "operator",    "char": "}",    "pos": 60}, {    "type": "keyword",    "char": "否则",    "pos": 62}, {    "type": "null",    "char ": " ",    "pos": 63}, {    "type": "operator",    "char": "{",    "pos": 63}, {    "type": "variable",    "char": "地址",    "pos": 72}, {    "type": "operator",    "char": "=",    "pos": 72}, {    "type": "string",    "char": "艾欧尼亚",    "pos": 78}, {    "type": "operator",    "char": ";",    "pos": 79}, {    "type": "operator",    "char": "}",    "pos": 82}, {    "type": "variable",    "char": "说话",    "pos": 87}, {    "type": "operator",    "char": "=",    "pos": 88}, {    "type": "operator",    "char": "(",    "pos": 90}, {    "type": "string",    "char": "我",    "pos": 93}, {    "type": "operator",    "char": "+",    "pos": 94}, {    "type": "string",    "char": "爱",    "pos": 97}, {    "type": "operator",    "char": ")",    "pos": 98}, {    "type": "operator",    "char": "+",    "pos": 99}, {    "type": "string",    "char": "你",    "pos": 102}, {    "type": "operator",    "char": ";",    "pos": 103}, {    "type": "keyword",    "char": "返回",    "pos": 107}, {    "type": "null",    "char": " ",    "pos": 108}, {    "type": "variable",    "char": "姓名",    "pos": 111}, {    "typ e": "operator",    "char": "+",    "pos": 111}, {    "type": "variable",    "char": "年龄",    "pos": 114}, {    "type": "operator",    "char": ";",    "pos": 114}]

输出:

$姓名="腕豪";$问候="你好啊";$地址=(1.2)*3;if ($地址>3){$地址=1;}else {$地址="艾欧尼亚";}$说话=("我"."爱")."你";return $姓名.$年龄;

能执行吗?当然能。还存在一些小bug,不想改了。

四、使用场景

什么,居然有人说没什么用?oa系统总有用到的时候。

读到这里,这篇“PHP怎么实现词法分析与自定义语言”文章已经介绍完毕,想要掌握这篇文章的知识点还需要大家自己动手实践使用过才能领会,如果想了解更多相关内容的文章,欢迎关注编程网行业资讯频道。

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

PHP怎么实现词法分析与自定义语言

下载Word文档到电脑,方便收藏和打印~

下载Word文档

猜你喜欢

PHP怎么实现词法分析与自定义语言

本文小编为大家详细介绍“PHP怎么实现词法分析与自定义语言”,内容详细,步骤清晰,细节处理妥当,希望这篇“PHP怎么实现词法分析与自定义语言”文章能帮助大家解决疑惑,下面跟着小编的思路慢慢深入,一起来学习新知识吧。
2023-06-26

利用PHP实现词法分析器与自定义语言

这篇文章主要为大家详细介绍了润滑利用PHP实现词法分析器与自定义语言,文中的示例代码讲解详细,感兴趣的小伙伴可以动手尝试一下
2022-11-13

wxpython中自定义事件的实现与使用方法分析

本文实例讲述了wxpython中自定义事件的实现与使用方法。分享给大家供大家参考,具体如下: 创建自定义事件的步骤: ① 定义事件类,该事件类必须继承自wx.PyCommandEvent,并定义get和set方法来获取和设置事件参数。 ②
2022-06-04

SAPGUI里怎么实现自定义的语法检查

SAPGUI里怎么实现自定义的语法检查,很多新手对此不是很清楚,为了帮助大家解决这个难题,下面小编将为大家详细讲解,有这方面需求的人可以来学习下,希望你能有所收获。需求:在SAPGUI里点击这个语法检查的小图标或者直接按快捷键Ctrl+F2
2023-06-04

Go语言自定义linter静态检查工具怎么实现

今天小编给大家分享一下Go语言自定义linter静态检查工具怎么实现的相关知识点,内容详细,逻辑清晰,相信大部分人都还太了解这方面的知识,所以分享这篇文章给大家参考一下,希望大家阅读完这篇文章后有所收获,下面我们一起来了解一下吧。Go语言中
2023-06-30

C语言数据结构之队列怎么定义与实现

今天小编给大家分享一下C语言数据结构之队列怎么定义与实现的相关知识点,内容详细,逻辑清晰,相信大部分人都还太了解这方面的知识,所以分享这篇文章给大家参考一下,希望大家阅读完这篇文章后有所收获,下面我们一起来了解一下吧。一、队列的性质上次我们
2023-07-02

R语言中怎么实现PCA分析与可视化

这期内容当中小编将会给大家带来有关R语言中怎么实现PCA分析与可视化,文章内容丰富且以专业的角度为大家分析和叙述,阅读完这篇文章希望大家可以有所收获。1. 常用术语(1)标准化(Scale)如果不对数据进行scale处理,本身数值大的基因对
2023-06-08

编程热搜

  • Python 学习之路 - Python
    一、安装Python34Windows在Python官网(https://www.python.org/downloads/)下载安装包并安装。Python的默认安装路径是:C:\Python34配置环境变量:【右键计算机】--》【属性】-
    Python 学习之路 - Python
  • chatgpt的中文全称是什么
    chatgpt的中文全称是生成型预训练变换模型。ChatGPT是什么ChatGPT是美国人工智能研究实验室OpenAI开发的一种全新聊天机器人模型,它能够通过学习和理解人类的语言来进行对话,还能根据聊天的上下文进行互动,并协助人类完成一系列
    chatgpt的中文全称是什么
  • C/C++中extern函数使用详解
  • C/C++可变参数的使用
    可变参数的使用方法远远不止以下几种,不过在C,C++中使用可变参数时要小心,在使用printf()等函数时传入的参数个数一定不能比前面的格式化字符串中的’%’符号个数少,否则会产生访问越界,运气不好的话还会导致程序崩溃
    C/C++可变参数的使用
  • css样式文件该放在哪里
  • php中数组下标必须是连续的吗
  • Python 3 教程
    Python 3 教程 Python 的 3.0 版本,常被称为 Python 3000,或简称 Py3k。相对于 Python 的早期版本,这是一个较大的升级。为了不带入过多的累赘,Python 3.0 在设计的时候没有考虑向下兼容。 Python
    Python 3 教程
  • Python pip包管理
    一、前言    在Python中, 安装第三方模块是通过 setuptools 这个工具完成的。 Python有两个封装了 setuptools的包管理工具: easy_install  和  pip , 目前官方推荐使用 pip。    
    Python pip包管理
  • ubuntu如何重新编译内核
  • 改善Java代码之慎用java动态编译

目录