当前位置:网站首页>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”这篇文章的内容,相信大家都有了一定的了解,希望小编分享的内容对大家有帮助,若想了解更多相关的知识内容,请关注亿速云行业资讯频道.
边栏推荐
猜你喜欢

使用 docker 搭建 redis-cluster 集群

暴力破解美团最新JVM面试题:无限执行

TMS320C6678开发板( DSP+Zynq )RTOS综合功能案例,嵌入式必看!

How to check the WeChat applet server domain name and modify it

IPtables and binlog

23.支持向量机的使用

两分钟录音就可秒变语言通!火山语音音色复刻技术如何修炼而成?

2022年安全员-A证考试试题及模拟考试

【 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

servlet交互过程图详解,servlet的常见问题,创建web项目(一)
随机推荐
Qt | 关于对象树和元对象的相关问题
Qt | 事件系统 QEvent
软件代码签名证书怎么申请
兆骑科创双创服务平台,创业赛事活动,投融资对接平台
Qt | 关于 Qt Creator 打开项目编译不过的问题
codeforces Linova and Kingdom
ShardingSphere基本介绍及核心概念
【深度学习】关于处理过拟合的一点心得
Qt | 模态对话框和非模态对话框 QDialog
23.支持向量机的使用
QT基础第四天(4)qt事件机制:事件基础概念,常见事件机制,事件处理以及事件的重写
setTimeout与setInterval的区别
Vest bag access process record
PAT Grade A 1143 Lowest Common Ancestor
PAT甲级 1145 哈希 - 平均查找时间
leetcode 504. Base 7 七进制数 (简单)
祝蔡徐坤生日快乐!
剑指Offer 49.丑数 动态规划
面试了个阿里P7大佬,他让我见识到什么才是“精通高并发与调优”
CNN flower classification