中文字幕av专区_日韩电影在线播放_精品国产精品久久一区免费式_av在线免费观看网站

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

如何利用PHP實現詞法分析器與自定義語言

發布時間:2022-08-02 11:09:18 來源:億速云 閱讀:195 作者:iii 欄目:開發技術

這篇“如何利用PHP實現詞法分析器與自定義語言”文章的知識點大部分人都不太理解,所以小編給大家總結了以下內容,內容詳細,步驟清晰,具有一定的借鑒價值,希望大家閱讀完這篇文章能有所收獲,下面我們一起來看看這篇“如何利用PHP實現詞法分析器與自定義語言”文章吧。

一、分析需求

輸入中文公式,返回結果,比如:

現有薪資=10000;
個稅起點=3000;
當前年份=2021;
如果(當前年份=2022){
    個稅起點=5000;
}
返回 (現有薪資-個稅起點) * 0.2;

二、實現需求

最初的想法是使用字符串替換的方式,將中文關鍵字替換成 php 的關鍵字,然后調用 eval 執行,這樣確實也是可以的,但是總覺得不是很美麗,并且不能實現動態解析。就想著自己實現一個簡單的詞法分析,然后結合 ast 將詞法轉換成 php 代碼執行,豈不快哉。當前版本沒有用到抽象語法樹來生成代碼,全部使用字符串拼接。

<?php

/**

 * Class Lexer

 * @package Sett\OaLang

 * 詞法分析器
   */
   class 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";


    /**
     * 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);
            }
            // 不是關鍵字的全部歸為變量
            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
     * 是否為普通變量
     */
    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
     * 是否中文字符
     */
    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
     * 匹配完整字符串
     */
    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
     * 是否是操作符
     */
    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);
// 自定義你的關鍵字
$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 $姓名.$年齡;

以上就是關于“如何利用PHP實現詞法分析器與自定義語言”這篇文章的內容,相信大家都有了一定的了解,希望小編分享的內容對大家有幫助,若想了解更多相關的知識內容,請關注億速云行業資訊頻道。

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

php
AI

九龙坡区| 舞阳县| 江北区| 姜堰市| 麻城市| 迁西县| 西藏| 敦煌市| 双柏县| 平远县| 舟山市| 孟村| 平湖市| 哈巴河县| 高要市| 沧源| 丰镇市| 安化县| 灵武市| 濉溪县| 渝北区| 波密县| 泊头市| 汶上县| 岑溪市| 那曲县| 从江县| 峨眉山市| 尼勒克县| 宁夏| 和林格尔县| 石台县| 平湖市| 甘南县| 安平县| 商都县| 嘉义市| 南江县| 宣城市| 崇礼县| 南丹县|