编写一个简单的语法解析器 [英] Writing a simple syntax parser

查看:53
本文介绍了编写一个简单的语法解析器的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

这是我想要做的 - 在 PHP 中:给定一个字符串,得到如下结果:

Here's what I'd like to do - in Php: given a string, have a result like this:

  • (a()?b|c) a 是一个返回 true 或 false 的函数.这应该在调用 a()
  • 后给出 bc
  • (a()?(b()?d|e)|c).原理一样.最终结果应该是 d, ec
  • (a()?(b()?d|e)|(c()?f|g)).原理一样.最终结果应该是 d, e, fg
  • (a()?b|c) a is a function that returns true of false. This should give b or c after calling a()
  • (a()?(b()?d|e)|c). Same principle. The final result should be d, e or c
  • (a()?(b()?d|e)|(c()?f|g)). Same principle. The final result should be d, e, f or g

我面临的问题是 a(在我之前的例子中)也可以是一个表达式,像这样:

The problem I'm facing is that a (in my previous examples) can be an expression too, like this:

((h() ? a | i) ? (b() ? d | e) | (c() ? f | g))

我正在尝试使用正则表达式来实现,但这不起作用.

I'm trying to use a regexp, to do it but this doesn't work.

$res=preg_match_all('/\([^.\(]+\)/', $str, $matches);

所以最后我想像这样调用我的函数:

So in the end I would like do call my function like this:

$final_string=compute("(a(x(y(z()?o|p)))?(b()?d|e)|(c()?f|g))");

$final_string 中的最终结果应该是 defg

The final result in $final_string should be d, e, f or g

我很确定以前做过一些事情,但在谷歌上找不到.你会怎么做?

I'm pretty sure something has been done before but cant find it on google. How would you do?

更准确地说,这是我想要分析字符串的方式:

To be more precise, here's how I'd like to have the string to be analyzed:

$str =
    "
    (myfunction(12684444)
    ?   {* comment *}
        (
            myfunction(1)|
            myfunction(2)|
            myfunction(80)|
            myfunction(120)|
            myfunction(184)|
            myfunction(196)
        ?   {* comment *}
            AAAAA
            {* /comment *}
        |
            {* Ignore all other values: *}
            BBBBB
        ) {* /comment *}

    |   {* comment *}
        CCCC
    )";

推荐答案

这是我的基本"版本,可以正常工作并完成工作.它是半"递归(一个可以调用或不调用函数的循环)和我计划做的改进(处理+"分隔符以添加"两个函数的返回,并处理"=" 来设置变量以使返回函数值的短别名)在 _compute() 函数中似乎很容易实现......也许是因为我写了自己编写代码,也许是因为,就像 Paul Crovella 所说的,我没有使用 PCRE,因为它很容易变成一个无法维护的烂摊子......

Here's my "basic" version that works and does the job. It's "half" recursive (a loop that may call the function or not) and the improvements I'm planning to do (handle "+" separator to "add" returns of two functions, and handle "=" to set variables to make short aliases of the value of a return function) seem quite easy to implement in the _compute() function... maybe because I wrote the code myself, and maybe because, like Paul Crovella said, I'm not using PCRE, because it can very easily become an unmaintainable mess...

注意:这段代码很容易优化,它并不完美(在某些情况下它不像(a()+b()))...但如果有人愿意完成它,他/她不客气!

NB: this code can be easily optimized, and it's not perfect (there are some cases it doesnt work like (a()+b()))... but if someone is willing to finish it he/she's welcome!

class Parser
{
    private $ref = array(
        'a'         => array( 'type' => 'fn',  'val' => '_a'),
        'b'         => array( 'type' => 'fn',  'val' => '_b'),
        'c'         => array( 'type' => 'fn',  'val' => '_c'),
        'd'         => array( 'type' => 'fn',  'val' => '_d'),
        'e'         => array( 'type' => 'fn',  'val' => '_e'),
        'f'         => array( 'type' => 'fn',  'val' => '_f'),
        'intro'         => array( 'type' => 'fn',  'val' => '_getIntro'),
        'insist'        => array( 'type' => 'fn',  'val' => '_insist'),
        'summoner_name' => array( 'type' => 'fn',  'val' => '_getSummonerName'),
        'type'          => array( 'type' => 'fn',  'val' => '_getEtat'),
        ' '             => array( 'type' => 'str', 'val' => ' ')
    );
    private function _a($p)        { return 'valfnA'; }
    private function _b($p)        { return 'valfnB'; }
    private function _c($p)        { return 'valfnC'; }
    private function _d($p)        { return 'valfnD'; }
    private function _e($p)        { return 'valfnE'; }
    private function _f($p)        { return 'valfnF'; }
    private function _getIntro($p)        { return 'valGetIntro'; }
    private function _insist($p)          { return 'valInsist'; }
    private function _getSummonerName($p) { return 'valGetSqmmonerName'; }
    private function _getEtat($p)         { return 'valGetEtat'; }

    private function _convertKey($key, $params=false)
    {
        $retour = 'indéfini';
        if (isset($this->ref[$key])) {
            $val = $this->ref[$key];
            switch ($val['type']) {
                case 'fn':
                    $val=$val['val'];
                    if (method_exists($this, $val)) {
                        $retour = $this->$val($params);
                    }
                    break;

                default:
                    if (isset($this->val['val'])) {
                        $retour = $this->val['val'];
                    }
                    break;
            }
        }
        return $retour;
    }
    private function _compute($str)
    {
        $p=strpos($str, '?');
        if ($p===false) {
            $p=strpos($str, '=');
            if ($p===false) {
                return $str;
            }
        } else {
            $or=strpos($str, '|');
            if ($or===false) {
                return false;
            }
            $s=substr($str,0,$p);
            if (empty($s) || (strtolower($s)=='false')) {
                return substr($str, $or+1);
            }
            return substr($str, $p+1, ($or-$p)-1);
        }
        return $str;
    }
    private function _getTexte($str, $i, $level)
    {
        if (empty($str)) {
            return $str;
        }
        $level++;
        $f   = (strlen($str)-$i);
        $val = substr($str, $i);
        do {
            $d = $i;
            do {
                $p=$d;
                $d=strpos($str, '(', $p+1);
                if (($p==$i) && ($d===false)) {
                    $retour = $this->_compute($str);
                    return $retour;
                } elseif (($d===false) && ($p>$i)) {
                    $f=strpos($str, ')', $p+1);
                    if ($f===false) {
                        return false;
                    }
                    $d=$p;
                    while((--$d)>=$i) {
                        if (($str[$d]!=' ')
                            && ($str[$d]!='_')
                            && (!ctype_alnum($str[$d]))
                        ) {
                            break;
                        }
                    }
                    if ($d>=$i) {
                        $d++;
                    } else {
                        $d=$i;
                    }
                    $val=substr($str, $d, ($f-$d)+1);
                    $fn=substr($str, $d, $p-$d);
                    $param=$this->_getTexte(
                        substr($str, $p+1, ($f-$p)-1), 0, $level+1
                    );
                    if (!empty($fn)) {
                        $val = $this->_convertKey($fn, $param);
                    } else {
                        $val = $this->_compute($param);
                    }
                    $str = substr($str, 0, $d).$val.substr($str, $f+1);
                    break;
                } elseif ($d===false) {
                    break;
                }
            } while (true);
        } while (true);
    }
    public function parse($str)
    {
        $retour=preg_replace('/{\*[^.{]+\*}/', '', $str); //}
        $retour=str_replace("\n", "", $retour);
        $retour=str_replace("\r", "", $retour);
        while (strpos($retour, '  ')!==false) {
            $retour=str_replace("  ", " ", $retour);
        }
        return trim($this->_getTexte($retour, 0, 0));
    }
}

$p=new Parser();
$tests = [
    "a",
    "a()",
    "a(b)",
    "(a?b|c)",
    "(a()?(b()?d|e)|(c()?f|g))",
    "(a()?(b()?d|e)|(c()?f()|g))",
    "((h() ? a | i) ? (b() ? d | e) | (c() ? f | g))",
    "(a(d(f))?b(e(f))|c)",
    '(intro(intro(type(insist(poou))))?toutou|tutu)',
    'type()intro(intro(type(insist(poou))))?type()|tutu'
];
foreach ($tests as $test) {
    $res=$p->parse($test);
    echo $test.' = '.var_export($res,true)."\n";
}

这篇关于编写一个简单的语法解析器的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆