【手搓一个脚本语言】十、用C语言抽象语法树AST实现变量的引用
1、变量的保存
- 变量数量是零到多个,解决这种不确定性,最好的办法是链表!!!
- 变量相关的两个重要元素,变量名和变量值,封装到一个数据结构Var中!
- 再将数据结构Var做为数据保存到链表的节点中!
- 最后实现链表的相关功能:创建、追加、释放等!
1.1 定义Var数据类型
typedef struct _Variable Var;
struct _Variable {
char name[32];
Token value;
};
1.2 定义VarNode数据类型
typedef struct _VarNode *VarNode;
struct _VarNode {
Var var;
VarNode next;
};
1.3 定义函数指针,测试遍历链表时使用
typedef void (*VarFunc) (VarNode node);
1.4 创建链表节点
VarNode
varnode_new (Var x)
{
VarNode node = (VarNode) malloc (sizeof(struct _VarNode));
node->var = x; node->next = NULL;
return node;
}
1.5 释放链表至尾节点
void
varnode_free (VarNode node)
{
while (node != NULL)
{
VarNode tmp = node->next;
free (node);
node = tmp;
}
}
1.6 追加节点至链表
VarNode
varnode_append (VarNode node, VarNode vnt)
{
VarNode tmp = node;
if (tmp == NULL) return vnt;
while (tmp->next != NULL)
tmp = tmp->next;
tmp->next = vnt;
return node;
}
1.7 取链表尾节点
VarNode
varnode_last (VarNode node)
{
VarNode tmp = node;
if (tmp == NULL) return NULL;
while (tmp->next != NULL)
tmp = tmp->next;
return tmp;
}
1.8 遍历链表,调用函数指针,处理链表节点
void
varnode_foreach (VarNode node, VarFunc vf)
{
while (node != NULL)
{
VarNode tmp = node->next;
vf (node);
node = tmp;
}
}
2、测试变量定义
- 在test_eval函数中测试表达式:a=9+10,结果是19,符合预期!
- 如此只要将变量名a和值19保存到链表中即可!
EXPR: a=9+10
--------------------
IDNT: a
ASGN: =
NUMB: 9
OP: +
NUMB: 10
--------------------
MID: a = 9 + 10
--------------------
PREV: ( = a ( + 9 10 ) )
--------------------
<[a]
[=]
<[9]
>[+]
>[10]
--------------------
POST: a 9 10 + =
--------------------
Node count: 5
Node index: 4
PUSH 9, SP: 0
PUSH 10, SP: 1
ADD 9 10 ==> 19
--------------------
RESULT: 19
--------------------
3、运算赋值表达式
- 定义函数eval_varast,参数一为parse_string函数返回的AstNode,参数二为变量链表首节点
- 返回值为链表的首节点
VarNode
eval_varast (AstNode root, VarNode vlst)
{
Token tk;
Var var;
VarNode vnode;
memset (var.name, 0, 32);
memcpy (var.name, root->left->token.V.sval, strlen (root->left->token.V.sval));
vnode = varnode_new (var);
tk = eval_astnode (root);
vnode->var.value = tk;
PF("RESULT: %ld\n", tk.V.ival);
if (vlst == NULL)
return vnode;
else
vlst = varnode_append (vlst, vnode);
return vlst;
}
4、测试函数test_eval_var
- 可以定义一个字符串数组来存储表达式字符串,然后循环读取并运行表达式!
static void
out_var (VarNode node)
{
PF("VAR NAME: [%s], VALUE: [%ld]\n", node->var.name, node->var.value.V.ival);
}
void
test_eval_var (void)
{
VarNode varlist = NULL;
AstNode root = NULL;
Token tk;
char *st[1] = {
"width=9+2*5" };
for (int i = 0; i < 1; i++)
{
PF("expression: %s\n", st[i]);
PF("--------------------\n");
root = parse_string (st[i]);
if (root->token.type == T_OPER && root->token.V.oval[0] == '=')
{
PF("--------------------\n");
varlist = eval_varast (root, varlist);
if (varlist == NULL) PF("VARNODE IS NULL!\n");
PF("--------------------\n");
varnode_foreach (varlist, out_var);
PF("--------------------\n");
}
else
{
PF("Not assignment expression!\n");
}
astnode_free (root);
}
varnode_free (varlist);
}
5、编译运行
expression: width=9+2*5
--------------------
IDNT: width
ASGN: =
NUMB: 9
OP: +
NUMB: 2
OP: *
NUMB: 5
--------------------
PUSH 9, SP: 0
PUSH 2, SP: 1
PUSH 5, SP: 2
MUL 2 5 ==> 10
ADD 9 10 ==> 19
RESULT: 19
--------------------
VAR NAME: [width], VALUE: [19]
--------------------
6、检查内存分配情况
- 提示出错,将eval_astnode函数中的malloc函数分配的tks数组初始化一下,值都置为0!
for (int i = 0; i < count; i++)
{
tks[i].type = 0; tks[i].V.nval = 0; }
==3718== Memcheck, a memory error detector
==3718== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
==3718== Using Valgrind-3.13.0 and LibVEX; rerun with -h for copyright info
==3718== Command: ./gt
==3718==
expression: a=10
--------------------
IDNT: a
ASGN: =
NUMB: 10
--------------------
PUSH 10, SP: 0
RESULT: 10
--------------------
VAR NAME: [a], VALUE: [10]
--------------------
expression: b=9+10
--------------------
IDNT: b
ASGN: =
NUMB: 9
OP: +
NUMB: 10
--------------------
PUSH 9, SP: 0
PUSH 10, SP: 1
ADD 9 10 ==> 19
RESULT: 19
--------------------
VAR NAME: [a], VALUE: [10]
VAR NAME: [b], VALUE: [19]
--------------------
expression: c=(2+8)*9
--------------------
IDNT: c
ASGN: =
LPAR: (
NUMB: 2
OP: +
NUMB: 8
RPAR: )
OP: *
NUMB: 9
--------------------
PUSH 2, SP: 0
PUSH 8, SP: 1
ADD 2 8 ==> 10
PUSH 9, SP: 1
MUL 10 9 ==> 90
RESULT: 90
--------------------
VAR NAME: [a], VALUE: [10]
VAR NAME: [b], VALUE: [19]
VAR NAME: [c], VALUE: [90]
--------------------
==3718==
==3718== HEAP SUMMARY:
==3718== in use at exit: 0 bytes in 0 blocks
==3718== total heap usage: 25 allocs, 25 frees, 926 bytes allocated
==3718==
==3718== All heap blocks were freed -- no leaks are possible
==3718==
==3718== For counts of detected and suppressed errors, rerun with: -v
==3718== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)
7、完整代码
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define GT_DEBUG 1
#if GT_DEBUG
#define PFI(...) fprintf (stderr, __VA_ARGS__)
#else
#define PFI(...)
#endif
#define PF