Loading [MathJax]/jax/output/CommonHTML/config.js
前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >PHP 使用 nikic/php-parser 处理 AST

PHP 使用 nikic/php-parser 处理 AST

作者头像
菜皮日记
发布于 2023-12-18 06:18:30
发布于 2023-12-18 06:18:30
31400
代码可运行
举报
文章被收录于专栏:菜皮日记菜皮日记
运行总次数:0
代码可运行
先来熟悉 php-parser 的 API

nikic/PHP-Parser 可以解析 PHP 代码并生成 AST,还支持修改 AST 再还原成PHP源码,从而实现元编程,可用来做 AOP 和静态代码检查等。Swoft 框架中 AOP 也是基于 PHP-parser 开发的。

https://github.com/nikic/PHP-Parser

首先使用 composer 安装 php-parser composer require nikic/php-parser

在代码中引入 autoload.php,开始测试代码

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制

<?php
require __DIR__ . '/vendor/autoload.php';

use PhpParser\Error;
use PhpParser\NodeDumper;
use PhpParser\ParserFactory;

// 定义一段PHP代码
$code = <<<'CODE'
<?php
function printLine($msg) {
    echo $msg, "\n";
}
printLine('Hello World!!!');
CODE;

// 创建一个解析器parser,需要指定优先版本
$parser = (new ParserFactory)->create(ParserFactory::PREFER_PHP7);
try {
		// 解析上面定义的PHP代码
    $ast = $parser->parse($code);
} catch (Error $error) {
    echo "Parse error: {$error->getMessage()}\n";
    return;
}

$dumper = new NodeDumper;
// 打印出生成的 AST
echo $dumper->dump($ast) . "\n========\n";

打印出结果:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制


array(
    0: Stmt_Function(
        attrGroups: array(
        )
        byRef: false
        name: Identifier(
            name: printLine
        )
        params: array(
            0: Param(
                attrGroups: array(
                )
                flags: 0
                type: null
                byRef: false
                variadic: false
                var: Expr_Variable(
                    name: msg
                )
                default: null
            )
        )
        returnType: null
        stmts: array(
            0: Stmt_Echo(
                exprs: array(
                    0: Expr_Variable(
                        name: msg
                    )
                    1: Scalar_String(
                        value:

                    )
                )
            )
        )
    )
    1: Stmt_Expression(
        expr: Expr_FuncCall(
            name: Name(
                parts: array(
                    0: printLine
                )
            )
            args: array(
                0: Arg(
                    name: null
                    value: Scalar_String(
                        value: Hello World!!!
                    )
                    byRef: false
                    unpack: false
                )
            )
        )
    )
)
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
AST 中各个结构说明可参见文档:https://github.com/nikic/PHP-Parser/blob/master/doc/2_Usage_of_basic_components.markdown#node-tree-structure

上面打印的数组中分别是:

  • Stmt_Function -> PhpParser\Node\Stmt\Function_
  • Stmt_Expression -> PhpParser\Node\Stmt\Expression

Function_ 有个 _ 后缀是因为 Function 本身是保留字,包中还有很多命名带有_ 也都是这个原因。

Node 的类型说明:

  • PhpParser\Node\Stmts are statement nodes, i.e. language constructs that do not return a value and can not occur in an expression. For example a class definition is a statement. It doesn't return a value and you can't write something like func(class A {});.
  • PhpParser\Node\Exprs are expression nodes, i.e. language constructs that return a value and thus can occur in other expressions. Examples of expressions are $var (PhpParser\Node\Expr\Variable) and func() (PhpParser\Node\Expr\FuncCall).
  • PhpParser\Node\Scalars are nodes representing scalar values, like 'string' (PhpParser\Node\Scalar\String_), 0 (PhpParser\Node\Scalar\LNumber) or magic constants like __FILE__ (PhpParser\Node\Scalar\MagicConst\File). All PhpParser\Node\Scalars extend PhpParser\Node\Expr, as scalars are expressions, too.
  • There are some nodes not in either of these groups, for example names (PhpParser\Node\Name) and call arguments (PhpParser\Node\Arg).

访问并修改 Node:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制


// 访问第0个元素 即Stmt_Function,一级一级向下访问,最后赋值
$ast[0]->stmts[0]->exprs[1]->value = '换行被替换了';
// 访问第1个元素 即Stmt_Expression
$ast[1]->expr->args[0]->value->value = 'Hello World被替换了';

echo $dumper->dump($ast) . "\n========\n";

打印结果:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制

array(
    0: Stmt_Function(
        attrGroups: array(
        )
        byRef: false
        name: Identifier(
            name: printLine
        )
        params: array(
            0: Param(
                attrGroups: array(
                )
                flags: 0
                type: null
                byRef: false
                variadic: false
                var: Expr_Variable(
                    name: msg
                )
                default: null
            )
        )
        returnType: null
        stmts: array(
            0: Stmt_Echo(
                exprs: array(
                    0: Expr_Variable(
                        name: msg
                    )
                    1: Scalar_String(
                        value: 换行被替换了  // 这里value被改变了
                    )
                )
            )
        )
    )
    1: Stmt_Expression(
        expr: Expr_FuncCall(
            name: Name(
                parts: array(
                    0: printLine
                )
            )
            args: array(
                0: Arg(
                    name: null
                    value: Scalar_String(
                        value: Hello World被替换了  // 这里value也被改了
                    )
                    byRef: false
                    unpack: false
                )
            )
        )
    )
)

遍历 AST 中的 Node:

遍历 AST 需要指定一个访问器,需实现几个方法,beforeTraverse 和 afterTraverse 是在开始遍历前和结束遍历后执行一次,enterNode 和 leaveNode 是每遍历到一个 Node 时执行一次。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制

interface NodeVisitor {
    public function beforeTraverse(array $nodes);
    public function enterNode(Node $node);
    public function leaveNode(Node $node);
    public function afterTraverse(array $nodes);
}

// NodeVisitorAbstract 是其抽象类
class NodeVisitorAbstract implements NodeVisitor
{
    public function beforeTraverse(array $nodes) {
        return null;
    }

    public function enterNode(Node $node) {
        return null;
    }

    public function leaveNode(Node $node) {
        return null;
    }

    public function afterTraverse(array $nodes) {
        return null;
    }
}
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制

use PhpParser\Node;
use PhpParser\Node\Stmt\Function_;
use PhpParser\NodeTraverser;
use PhpParser\NodeVisitorAbstract;

$traverser = new NodeTraverser();
$traverser->addVisitor(new class extends NodeVisitorAbstract {
    public function enterNode(Node $node) {
				// 如果node是Function_类型时
        if ($node instanceof Function_) {
            // Clean out the function body
						// 情况node的语句,即清空了函数体
            $node->stmts = [];

            // 或者返回一个新的node
            // return new Function_("new_func");
        }
    }
});

$ast = $traverser->traverse($ast);
echo $dumper->dump($ast) . "\n========\n";

输出:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制

array(
    0: Stmt_Function(
        attrGroups: array(
        )
        byRef: false
        name: Identifier(
            name: new_func
        )
        params: array(
        )
        returnType: null
        stmts: array(
        )  //  stmts 被清空了
    )
    1: Stmt_Expression(
        expr: Expr_FuncCall(
            name: Name(
                parts: array(
                    0: printLine
                )
            )
            args: array(
                0: Arg(
                    name: null
                    value: Scalar_String(
                        value: Hello World被替换了
                    )
                    byRef: false
                    unpack: false
                )
            )
        )
    )
)
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
输出修改后的 PHP 代码,即 Pretty Print
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制

use PhpParser\PrettyPrinter;

$prettyPrinter = new PrettyPrinter\Standard;
echo $prettyPrinter->prettyPrintFile($ast);

输出:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制

<?php

function printLine($msg)
{
}
printLine('Hello World被替换了');%

函数体被清空了,并且第二个语句 printLine 中的参数被替换了。

有了这种能力,结合一些注释标注等,就可以在 PHP 代码在执行之前动态修改带有指定特征的 PHP代码的行为。

使用 PHP-parser 重写 PHP 类代码实现AOP:

参考文章:https://learnku.com/articles/14387/aop-design-rewrite-the-php-class-using-php-parser

该 AOP 增强的效果是在字符串后面增加一个叹号 !

入口 aop.php:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制

<?php
require __DIR__ . '/vendor/autoload.php';
require __DIR__ . '/ProxyVisitor.php';
require __DIR__ . '/Test.php';

use PhpParser\ParserFactory;
use PhpParser\NodeTraverser;
use PhpParser\NodeDumper;
use PhpParser\PrettyPrinter\Standard;

$file = './Test.php';
$code = file_get_contents($file);

$parser = (new ParserFactory())->create(ParserFactory::PREFER_PHP7);
$ast = $parser->parse($code);


$dumper = new NodeDumper;
echo $dumper->dump($ast) . "\n========\n";



$traverser = new NodeTraverser();
$className = 'Test';
$proxyId = uniqid();
$visitor = new ProxyVisitor($className, $proxyId);

$traverser->addVisitor($visitor);
$proxyAst = $traverser->traverse($ast);
if (!$proxyAst) {
    throw new \Exception(sprintf('Class %s AST optimize failure', $className));
}
$printer = new Standard();
$proxyCode = $printer->prettyPrint($proxyAst);

echo $proxyCode;

eval($proxyCode);

$class = $visitor->getClassName();
$bean = new $class();

echo $bean->show();
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
PHP-Parser 的访问器 ProxyVisitor.php
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制

<?php
require __DIR__ . '/vendor/autoload.php';

use PhpParser\NodeVisitorAbstract;
use PhpParser\Node;
use PhpParser\Node\Expr\Closure;
use PhpParser\Node\Expr\FuncCall;
use PhpParser\Node\Expr\MethodCall;
use PhpParser\Node\Expr\Variable;
use PhpParser\Node\Name;
use PhpParser\Node\Param;
use PhpParser\Node\Scalar\String_;
use PhpParser\Node\Stmt\Class_;
use PhpParser\Node\Stmt\ClassMethod;
use PhpParser\Node\Stmt\Return_;
use PhpParser\Node\Stmt\TraitUse;
use PhpParser\NodeFinder;
use PhpParser\NodeDumper;


class ProxyVisitor extends NodeVisitorAbstract
{
    protected $className;

    protected $proxyId;

    public function __construct($className, $proxyId)
    {
        $this->className = $className;
        $this->proxyId = $proxyId;
    }

    public function getProxyClassName(): string
    {
        return \basename(str_replace('\\', '/', $this->className)) . '_' . $this->proxyId;
    }

    public function getClassName()
    {
        return '\\' . $this->className . '_' . $this->proxyId;
    }

    /**
     * @return \PhpParser\Node\Stmt\TraitUse
     */
    private function getAopTraitUseNode(): TraitUse
    {
        // Use AopTrait trait use node
        return new TraitUse([new Name('AopTrait')]);
    }



    public function leaveNode(Node $node)
    {
        echo "=====leaveNode=====\n";

        // Proxy Class
        if ($node instanceof Class_) {
            // Create proxy class base on parent class
            echo "Class_ instance";
            return new Class_($this->getProxyClassName(), [
                'flags' => $node->flags,
                'stmts' => $node->stmts,
                'extends' => new Name($this->className),
            ]);
        }
        // Rewrite public and protected methods, without static methods
        if ($node instanceof ClassMethod && !$node->isStatic() && ($node->isPublic() || $node->isProtected())) {
            $methodName = $node->name->toString();
            echo "classmethod name ", $methodName , "\n";
            // Rebuild closure uses, only variable
            $uses = [];
            foreach ($node->params as $key => $param) {
                if ($param instanceof Param) {
                    $uses[$key] = new Param($param->var, null, null, true);
                }
            }
            $params = [
                // Add method to an closure
                new Closure([
                    'static' => $node->isStatic(),
                    'uses' => $uses,
                    'stmts' => $node->stmts,
                ]),
                new String_($methodName),
                new FuncCall(new Name('func_get_args')),
            ];
            $stmts = [
                new Return_(new MethodCall(new Variable('this'), '__proxyCall', $params))
            ];
            $returnType = $node->getReturnType();
            if ($returnType instanceof Name && $returnType->toString() === 'self') {
                $returnType = new Name('\\' . $this->className);
            }
            return new ClassMethod($methodName, [
                'flags' => $node->flags,
                'byRef' => $node->byRef,
                'params' => $node->params,
                'returnType' => $returnType,
                'stmts' => $stmts,
            ]);
        }
    }

    public function afterTraverse(array $nodes)
    {
        echo "=====afterTraverse=====\n";



        $addEnhancementMethods = true;
        $nodeFinder = new NodeFinder();
        $nodeFinder->find($nodes, function (Node $node) use (
            &$addEnhancementMethods
        ) {
            if ($node instanceof TraitUse) {
                foreach ($node->traits as $trait) {
                    // Did AopTrait trait use ?
                    if ($trait instanceof Name && $trait->toString() === 'AopTrait') {
                        $addEnhancementMethods = false;
                        break;
                    }
                }
            }
        });
        // Find Class Node and then Add Aop Enhancement Methods nodes and getOriginalClassName() method
        $classNode = $nodeFinder->findFirstInstanceOf($nodes, Class_::class);
        $addEnhancementMethods && array_unshift($classNode->stmts, $this->getAopTraitUseNode());
        return $nodes;
    }
}

trait AopTrait
{
    /**
     * AOP proxy call method
     * 这个AOP加强就是往字符串后面加一个 !
     * @param \Closure $closure
     * @param string   $method
     * @param array    $params
     * @return mixed|null
     * @throws \Throwable
     */
    public function __proxyCall(\Closure $closure, string $method, array $params)
    {
        $res = $closure(...$params);
        if (is_string($res)) {
            $res .= '!';
        }
        return $res;
    }

}

被代理的类 Test.php

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制

<?php

class Test
{
    public function show()
    {
        return 'hello world';
    }
}

执行后,被增强的结果类为:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制

class Test_60b7bffeb7672 extends Test
{
    use AopTrait;
    public function show()
    {
        return $this->__proxyCall(function () {
            return 'hello world';
        }, 'show', func_get_args());
    }
}

执行结果:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制

hello world!
Swoft 框架中的 AOP 实现原理

swoft 的 aop 也是基于 php-parser 来实现的,由于懒的搞 phpunit,在本是 testcase 的类上直接改代码手动调试了:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制

<?php declare(strict_types=1);

namespace SwoftTest\Aop\Unit;

require_once './vendor/autoload.php';

use Swoft\Aop\Ast\Visitor\ProxyVisitor;
use Swoft\Aop\BeiAopClass;
use Swoft\Aop\Proxy;
use Swoft\Proxy\Ast\Parser;
use Swoft\Proxy\Exception\ProxyException;
use Swoft\Proxy\Proxy as BaseProxy;
use function class_exists;
use function sprintf;
use const PHP_EOL;

class AopTest
{
    public function testProxyClass(): void
    {
        /* 
				源码在 https://github.com/swoft-cloud/swoft-aop/blob/master/src/Ast/Visitor/ProxyVisitor.php
				实现了PhpParser的NodeVisitor接口,即定义了遍历ast的nodes时,处理每个node的具体方式。
				*/
        $visitor   = new ProxyVisitor();
        $className = BaseProxy::newClassName(BeiAopClass::class, $visitor);
        
        $o = new $className;
        var_dump($o->MethodNull(1,'2', 3.0, 'xxxx'));
       
    }
}

$a = new AopTest();
$a->testProxyClass();

newClassName 方法如下:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制

<?php

class Proxy{

public static function newClassName(string $className, Visitor $visitor, string $suffix = ''): string
    {
        echo "被aop的类名:$className \n";
        $cacheKey = $className . $suffix;
        if (isset(self::$caches[$cacheKey])) {
            return self::$caches[$cacheKey];
        }
        $parser = new Parser();
        // 给parser添加访问器,即默认的ProxyVisitor
        $parser->addNodeVisitor(get_class($visitor), $visitor);

        $proxyCode = $parser->parse($className);
        echo "代理后的代码:\n $proxyCode \n";
        $proxyName = $visitor->getProxyName();
        $newClassName = $visitor->getProxyClassName();

	      // 要想让代理后的代码生效,可以写入文件,之后require文件生效
        eval($proxyCode);

				// 或者直接eval这段代理后的代码
        $proxyFile = sprintf('%s/%s.php', Sys::getTempDir(), $proxyName);
        $proxyCode = sprintf('<?php %s %s', PHP_EOL, $proxyCode);

        // Generate proxy class
        $result = file_put_contents($proxyFile, $proxyCode);
        if ($result === false) {
            throw new ProxyException(sprintf('Proxy file(%s) generate fail', $proxyFile));
        }

        // Load new proxy class file.
        self::loadProxyClass($proxyFile);

        // Ensure proxy class is loaded
        if (!class_exists($newClassName)) {
            throw new ProxyException(sprintf('Proxy class(%s) is not exist!', $newClassName));
        }

        // Add cache, mark has been required.
        self::$caches[$cacheKey] = $newClassName;
        return $newClassName;
    }

得到类名,即可 new 之,按照原类的方法签名方式调用,即可得到代理后的效果。

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
Rc-lang开发周记12 部分Parser
由于是由换行来分句,我觉得一个头疼的点在于要想清楚哪里要换行,想清楚这个parser都是由什么组成,然后拼接在一起。但是写到这里的时候我才想到如果表达式有多行(这个也是非常常见的情况)就支持不了了…以后再做支持吧,这个或许可以对于表达式单独添加换行的支持。
AkemiHomura
2023/04/07
2120
Rc-lang开发周记12 部分Parser
Rc-lang开发周记14 重构与AST Visitor
本周先是解决了上周遗留下来的一个非常头疼的问题,之后重构了Token和AST的定义以及考虑了一下Visitor。之后也编写了建立符号表的代码以及一半转换到vm指令的代码,但是总觉得哪里不太对劲就先停了下来,后续确认无误了再一起拿出来讲。还学习了一些rust的实现方式,关于IR方面有更多了解以后有意向再单独出一篇文章讲解自己的一些了解
AkemiHomura
2023/04/07
3070
Rc-lang开发周记14 重构与AST Visitor
Python Ast介绍及应用
Abstract Syntax Trees即抽象语法树。Ast是python源码到字节码的一种中间产物,借助ast模块可以从语法树的角度分析源码结构。此外,我们不仅可以修改和执行语法树,还可以将Source生成的语法树unparse成python源码。因此ast给python源码检查、语法分析、修改代码以及代码调试等留下了足够的发挥空间。
py3study
2020/02/10
2.4K1
TiDB SQL Parser 的实现
其中,SQL Parser的功能是把SQL语句按照SQL语法规则进行解析,将文本转换成抽象语法树(AST),这部分功能需要些背景知识才能比较容易理解,我尝试做下相关知识的介绍,希望能对读懂这部分代码有点帮助。
mazhen
2023/11/24
7150
TiDB SQL Parser 的实现
(2)PHP内核 - 玩转php的编译与执行
注意这个的child[1],并不是表示是一个节点,类似于zval_string里面的val[1],节点地址连续分配在zend_ast结构末尾。根据 kind 类型转换为其他类型节点,具体的类型和对应的结构在/Zend/zend_ast.h里面定义。常用的下面两个节点类型
猿哥
2019/07/10
2.3K0
phpMyAdmin 中 sql-parser 组件的使用
腾讯云数据库团队
2016/11/09
4.3K0
PyTorch 源码解读之即时编译篇
来源丨https://zhuanlan.zhihu.com/p/361101354
BBuf
2021/07/01
1.3K0
PyTorch 源码解读之即时编译篇
TiDB 源码阅读系列文章(五)TiDB SQL Parser 的实现
PingCAP 发布了 TiDB 的源码阅读系列文章,让我们可以比较系统的去学习了解TiDB的内部实现。最近的一篇《SQL 的一生》,从整体上讲解了一条 SQL 语句的处理流程,从网络上接收数据,MySQL 协议解析和转换,SQL 语法解析,查询计划的制定和优化,查询计划执行,到最后返回结果。
PingCAP
2018/03/22
4.7K3
TiDB 源码阅读系列文章(五)TiDB SQL Parser 的实现
Go每日一库之125:ast(抽象语法树)
当你对GoLang AST感兴趣时,你会参考什么?文档还是源代码? 虽然阅读文档可以帮助你抽象地理解它,但你无法看到API之间的关系等等。 如果是阅读整个源代码,你会完全看懂,但你想看完整个代码我觉得您应该会很累。 因此,本着高效学习的原则,我写了此文,希望对您能有所帮助。 让我们轻松一点,通过AST来了解我们平时写的Go代码在内部是如何表示的。 本文不深入探讨如何解析源代码,先从AST建立后的描述开始。 如果您对代码如何转换为AST很好奇,请浏览深入挖掘分析Go代码。 让我们开始吧!
luckpunk
2023/09/30
1.2K0
CPython源码阅读笔记(1)
目前 CPython 的开发已经迁移到了 Github 上,可以直接去 Github clone 对应的分支。 我们将基于 Python 2.7.13 版本, Linux x86_64 环境进行接下来的工作。 下载好代码以后以
鱼塘小咸鱼
2018/11/06
4.6K0
(1)PHP内核 - 玩转php的编译与执行
曾几何时php一不小心闯入了我生活,php语法竟然和C语言那么莫名的相似,这是最初php给我的感受,当接触的php时间越来越多的时候,php也没有那般生涩难懂,但是偶尔一些的新的php 设计思想,也会思考许久,不知是从什么时候开始了php另一个世界。我想应该是从那次的类型转换开始的,"1e12"字符串类型在转化为数字类型变量时,不同的php版本下转换结果截然不同,有的就变成了数字1,有的却可以正常的识别为科学计数法10^12,在这个地方就已经悄悄的埋下了一枚种子。
猿哥
2019/07/10
1.9K0
通过v8 0.1.5源码分析js的编译、执行过程
我们主要关注Compile和Run这两个函数。这两个函数都属于Script这个类,我们看看定义。
theanarkh
2020/02/18
2.4K0
M语言编程_所有编程语言大全
一直对技术有很强的兴趣,终于,决定要写自己的语言(m语言)。那就先从最简单的开始:解释执行器。
全栈程序员站长
2022/09/20
14.4K0
TiDB 源码阅读系列文章(二十三)Prepare/Execute 请求处理
在之前的一篇文章《TiDB 源码阅读系列文章(三)SQL 的一生》中,我们介绍了 TiDB 在收到客户端请求包时,最常见的 Command --- COM_QUERY 的请求处理流程。本文我们将介绍另外一种大家经常使用的 Command --- Prepare/Execute 请求在 TiDB 中的处理过程。
PingCAP
2019/01/04
9990
Postgresql源码(37)plpgsql函数编译执行流程分析
1、编译过程主要是pl_gram.y做语法匹配的过程plpgsql_yyparse,整体匹配后的结果会作为PLpgSQL_stmt_block结构记录在plpgsql_parse_result中。
mingjie
2022/05/12
1.3K0
十分钟成为 Contributor 系列 | 支持 AST 还原为 SQL
SQL 语句发送到 TiDB 后首先会经过 parser,从文本 parse 成为 AST(抽象语法树),AST 节点与 SQL 文本结构是一一对应的,我们通过遍历整个 AST 树就可以拼接出一个与 AST 语义相同的 SQL 文本。
PingCAP
2018/12/21
1.7K0
golang源码分析(18)添加一个新语句到Golang编译器内部
在Go中添加while语句是简单的,因为只需要简单的将while翻译为for。所以我们选择了一个更具有挑战性的任务:添加until。until与while相似,只是将判定条件改为了否定,意为“直到……”。例如:
golangLeetcode
2022/08/02
3700
golang源码分析(18)添加一个新语句到Golang编译器内部
Cobar源码分析之AST
Cobar是阿里开源的数据库中间件,关于它的介绍这里不再赘述,可以参考之前的文章《Cobar SQL审计的设计与实现》
龟仙老人
2021/07/07
7530
Cobar源码分析之AST
DSL parser 和 PromQL
PromQL (Prometheus Query Language) 是 Prometheus 自己开发的数据查询 DSL 语言,语言表现力非常丰富,内置函数很多,在日常数据可视化以及rule 告警中都会使用到它。
王磊-字节跳动
2019/07/07
3.1K0
Rc-lang开发周记15 Rust源码学习之desugar
这周可以说几乎没写什么代码,都在学习别人的实现。在参考别人的做法之前自己写一版比较合适,这样会对整体有个了解(这样有利于阅读代码),知道哪些地方会有问题,看别人的代码后会发现哪里不一样并且去思考差异。不过我之前已经写过简易的实现了,因此直接来参考Rust的实现了
AkemiHomura
2023/04/07
4630
Rc-lang开发周记15 Rust源码学习之desugar
相关推荐
Rc-lang开发周记12 部分Parser
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
本文部分代码块支持一键运行,欢迎体验
本文部分代码块支持一键运行,欢迎体验