当前位置:网站首页>How to use PHP to implement lexical analyzer and custom language
How to use PHP to implement lexical analyzer and custom language
2022-08-02 17:32:00 【Million speed cloud】
如何利用PHPImplement a lexer with a custom language
这篇“如何利用PHPImplement a lexer with a custom language”文章的知识点大部分人都不太理解,所以小编给大家总结了以下内容,内容详细,步骤清晰,具有一定的借鉴价值,希望大家阅读完这篇文章能有所收获,下面我们一起来看看这篇“如何利用PHPImplement a lexer with a custom language”文章吧.
一、分析需求
Enter Chinese formula,返回结果,比如:
current salary=10000;
个税起点=3000;
当前年份=2021;
如果(当前年份=2022){
个税起点=5000;
}
返回 (current salary-个税起点) * 0.2;
二、实现需求
The original idea was to use the way of string substitution,Replace Chinese keywords with php 的关键字,然后调用 eval 执行,This is indeed possible,But it doesn't always feel beautiful,And can not achieve dynamic parsing.I just wanted to implement a simple lexical analysis myself,然后结合 ast Convert lexical to php 代码执行,岂不快哉.The current version does not use abstract syntax trees to generate code,All use string concatenation.
<?php/** * Class Lexer * @package Sett\OaLang * 词法分析器 */ class Lexer { // A collection of built-in keywords public $keywordList = []; // A collection of built-in operators 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"; /** * Lexer constructor. * @param string $input */ public function __construct(string $input) { $this->input = $input; $this->currChar = mb_substr($this->input, $this->currCharPos, 1); } /** * @param array $keywordList */ public function setKeywordList($keywordList) { $this->keywordList = $keywordList; } /** * @return array * @throws Exception */ 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; } /** * @return array */ 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); } // Not all keywords are classified as variables 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); } /** * @param string $input * @return string */ private function matchVar(string $input = "") { $word = $input ?: ''; while ($this->isVar()) { $word .= $this->currChar; $this->nextChar(); } return $word; } /** * @return bool * Is it a normal variable */ 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(); } } /** * @param string $type * @param $word * @return array * 记录当前token和下一个字符 */ private function currToken(string $type, $word) { $token = $this->makeToken($type, $word); $this->nextChar(); return $token; } /** * @param string $type * @param string $char * @return array */ private function makeToken(string $type, string $char) { return ["type" => $type, "char" => $char, "pos" => $this->currCharPos]; } /** * @return bool * 判断是否是英文字符 */ 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; } /** * @return false|int * Whether Chinese characters */ private function isCnLetter() { return preg_match("/^[\x{4e00}-\x{9fa5}]+$/u", $this->currChar); } /** * @return bool * 是否为数字 */ private function isNumber() { return is_numeric($this->currChar); } /** * @return bool * 是否是字符串 */ private function isStr() { return $this->matchCompleteStr(); } /** * @return string * Match the full string */ private function matchCompleteStr() { $char = ""; if ($this->currChar == "\"") { $this->nextChar(); while ($this->currChar != "\"") { if ($this->currChar != "\"") { $char .= $this->currChar; } $this->nextChar(); } return $char; } return $char; } /** * @return bool * is an operator */ private function isOperator() { return in_array($this->currChar, $this->operatorList); } /** * @return string * 匹配中文字符 */ private function matchUntilNextCharIsNotCn() { $char = ""; while ($this->isCnLetter()) { $char .= $this->currChar; $this->nextChar(); } return $char; } /** * @return void 获取下一个字符 * 获取下一个字符 */ private function nextChar() { $this->currCharPos += 1; $this->currChar = mb_substr($this->input, $this->currCharPos, 1); if ($this->currChar == "") { $this->currChar = $this->eof; } } /** * @param string $input * @return bool * 是否是关键字 */ 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); } /** * @param array $tokens * @return \Generator */ 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);// Customize your keywords$kwMap = [ "如果" => "if", "否则" => "else", "返回" => "return", "否则如果" => "elseif"];$lexer->setKeywordList($kwMap);// Here are the generated words$tokens = $lexer->parseInput();// Convert the generated words to php,Of course you can also try itphp-parse转ast再转成php,Just simple stitching herevar_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 $姓名.$年龄;
以上就是关于“如何利用PHPImplement a lexer with a custom language”这篇文章的内容,相信大家都有了一定的了解,希望小编分享的内容对大家有帮助,若想了解更多相关的知识内容,请关注亿速云行业资讯频道.
边栏推荐
猜你喜欢
[email protected]设计"/>
NXP i.MX 8M Mini工业核心板B2B版本,4核ARM [email protected]设计
李开复花上千万投的缝纫机器人,团队出自大疆
How to check the WeChat applet server domain name and modify it
基于mobileNet实现狗的品种分类(迁移学习)
SSRF(服务器端请求伪造)
矩阵的特征值与特征向量
阿里面试败北:5种微服务注册中心如何选型?这几个维度告诉你
【无标题】
Servlet基础详解
【 Leetcode string, the string transform/hexadecimal conversion 】 HJ1. The length of the string last word HJ2. Calculation of a certain number of characters appear HJ30. String merging processing
随机推荐
祝蔡徐坤生日快乐!
MySQL (2)
MySQL 视图(详解)
剑指Offer 49.丑数 动态规划
codeforces k-Tree (dp仍然不会耶)
PAT Grade A 1143 Lowest Common Ancestor
Qt | 关于 Qt Creator 打开项目编译不过的问题
《数字经济全景白皮书》银行业智能风控科技应用专题分析 发布
阿里面试败北:5种微服务注册中心如何选型?这几个维度告诉你
MySQL 高级(进阶) SQL 语句 (一)
Based on mobileNet dog breed classification (migration)
OneFlow源码解析:Op、Kernel与解释器
"No title"
go——协程调度
马甲包接入过程记录
【无标题】
MySQL查询
form的编辑与展示的切换(输入框,单选多选框,上传图片,颜色选择器)适用个人信息的展示与修改
A status code, and access baidu process
HDU1561 树形背包dp+边界优化 0ms过题