抽象语法树的定义(C语言版)

给定抽象语法:

   E -> n

     | E + E

     | E * E

构造出相应的抽象语法树

抽象语法树的数据结构及相关操作

enum kind {E_INT, E_ADD, E_TIMES};
struct Exp 
{
    enum kind kind;
};
struct Exp_Int   // 对应第一个右部
{
    enum kind kind;
    int n;
};
struct Exp_Add   // 对应第二个右部
{
    enum kind kind;
    struct Exp *left;
    struct Exp *right;
};
struct Exp_Times // 对应第三个右部
{
    enum kind kind;
    struct Exp *left;
    struct Exp *right;
};

// “构造函数”的定义
struct Exp_Int *Exp_Int_new(int n)
{
    struct Exp_Int *p = malloc(sizeof(*p));
    p->kind = E_ADD;
    p->n = n;
    return p;
}

struct Exp_Add *Exp_Add_new(struct Exp *left, struct Exp *right)
{
    struct Exp_Add *p = malloc(sizeof(*p));
    p->kind = E_ADD;
    p->left = left;
    p->right = right;
    return p;
}

struct Exp_Times *Exp_Times_new(struct Exp *left, struct Exp *right)
{
    struct Exp_Times *p = malloc(sizeof(*p));
    p->kind = E_TIMES;
    p->left = left;
    p->right = right;
    return p;
}

 AST上的常用操作

(1)优美打印

void pretty_print(e)
{
    switch (e->kind)
    {
    case E_INT:
        printf("%d", e->n);
        return;
    case E_ADD:
        printf("(");
        pretty_print(e->left);
        printf(")");
        printf(" + ");
        printf("(");
        pretty_print(e->right);
        printf(")");
    case E_TIMES:
        printf("(");
        pretty_print(e->left);
        printf(")");
        printf(" * ");
        printf("(");
        pretty_print(e->right);
        printf(")");
    default:
        break;
    }
}

(2)树的规模(节点个数)

int numNodes(E e)
{
    switch (e->kind)
    {
    case E_INT: return 1;
    case E_ADD: 
    case E_TIMES:
        return 1 + numNodes(e->left)
                 + numNodes(e->right);
    default:
        error("compiler bug");
    }
}

1、手工构造 

 例如,用上述数据结构来编码程序  2 + 3 * 4

e1 = Exp_Int_new(2);
e2 = Exp_Int_new(3);
e3 = Exp_Int_new(4);
e4 = Exp_Times_new(e2, e3);
e5 = Exp_Add_new(e1, e4);

2、自动生成

LR分析中生成抽象语法树

(1)在语法动作中,加入生成语法树的代码片段(片段一般是语法树的“构造函数”)

(2)在产生式归约的时候,会自底向上构造整棵树(从叶子到根)

比如,在yacc中,我们书写以下代码

   E -> E + E    {$$ = Exp_Add_new($1, $3);}
     |  E * E    {$$ = Exp_Times_new($1, $3);}
     |  n        {$$ = Exp_Int_new($1);}

源代码信息的保留和传播

抽象语法树是编译器前端和后端的接口,程序一旦被转换成抽象语法树,则源代码即被丢弃,后续的阶段只处理抽象语法树。

所以抽象语法树必须编码足够多的源代码信息,例如它必须编码每个语法结构在源代码中的位置(文件、行号、列号等),这样,后续的检查阶段才能精准的报错,或者获取程序的执行剖面。

示例:位置信息

struct position_t
{
    char *file;
    int line;
    int column;
};
struct Exp_Add
{
    enum kind kind;
    Exp *left;
    Exp *right;
    struct position_t from;
    struct position_t to;
};

上述程序可以表示我们识别了这样一个子树的结构,并且这棵子树对应的源代码是从(from)第几行第几列到(to)第几行第几列。

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

青衫客36

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值