c
复制代码
/* filename gt.c */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
/* compile: gcc gt.c -o gt */
/* run: ./gt */
/* memcheck: valgrind --leak-check=yes ./gt */
/* debug on|off */
#define GT_DEBUG 1
#if GT_DEBUG
#define PFI(...) fprintf (stderr, __VA_ARGS__)
#else
#define PFI(...)
#endif
#define PF(...) fprintf (stderr, __VA_ARGS__)
/* define token type */
#define T_OPER 0x21
#define T_NUMB 0x22
#define T_SUBA 0x23
/* define astnode datatype */
typedef struct _AstNode *AstNode;
/* define token datatype */
typedef struct _Token Token;
struct _Token {
union {
void *nval; //null
char oval[8]; //operator
long ival; //integer
AstNode aobj; //sub astnode
} V;
char type; //value type
char fill[7]; //unused
};
/* define astnode data struct */
struct _AstNode {
Token token;
AstNode left, right;
};
/* create a new astnode */
AstNode
astnode_new (Token tk)
{
AstNode node = (AstNode) malloc (sizeof(struct _AstNode));
node->token = tk; node->left = NULL; node->right = NULL;
return node;
}
/* free the astnode */
void
astnode_free (AstNode node)
{
if (node != NULL)
{
astnode_free (node->left);
astnode_free (node->right);
if (node->token.type == T_SUBA)
astnode_free (node->token.V.aobj);
free (node);
}
}
/* count all astnode */
static void
astnode_count_all (AstNode node, int *cnt)
{
if (node != NULL)
{
astnode_count_all (node->left, cnt);
astnode_count_all (node->right, cnt);
if (node->token.type == T_SUBA)
astnode_count_all (node->token.V.aobj, cnt);
else
(*cnt)++;
}
}
/* count astnode */
int
astnode_count (AstNode node)
{
int count = 0;
astnode_count_all (node, &count);
return count;
}
/* visual traverse, by level, by left or right */
static void
astnode_visual_trav_lv (AstNode node, int lv, char lr)
{
if (node != NULL)
{
astnode_visual_trav_lv (node->left, lv+1, 'L');
for (int i = 0; i < lv; i++) PFI(" ");
if (lr == 'L') PFI("<");
if (lr == 'R') PFI(">");
switch (node->token.type)
{
case T_OPER: PFI("[%s]\n", node->token.V.oval); break;
case T_NUMB: PFI("[%ld]\n", node->token.V.ival); break;
case T_SUBA: PFI("\n"); astnode_visual_trav_lv (node->token.V.aobj, lv, 'N'); break;
}
astnode_visual_trav_lv (node->right, lv+1, 'R');
}
}
/* visual traverse, looks like a tree */
void
astnode_visual_trav (AstNode node)
{
astnode_visual_trav_lv (node, 0, 'N');
}
/* prevorder traverse ast */
void
astnode_prevorder_trav (AstNode node)
{
if (node != NULL)
{
if (node->token.type == T_OPER) PFI(" (");
switch (node->token.type)
{
case T_OPER: PFI( " %s", node->token.V.oval); break;
case T_NUMB: PFI(" %ld", node->token.V.ival); break;
case T_SUBA: astnode_prevorder_trav (node->token.V.aobj); break;
}
astnode_prevorder_trav (node->left);
astnode_prevorder_trav (node->right);
if (node->token.type == T_OPER) PFI(" )");
}
}
/* inorder traverse ast */
void
astnode_inorder_trav (AstNode node)
{
if (node != NULL)
{
astnode_inorder_trav (node->left);
switch (node->token.type)
{
case T_OPER: PFI( " %s", node->token.V.oval); break;
case T_NUMB: PFI(" %ld", node->token.V.ival); break;
case T_SUBA: PFI(" ("); astnode_inorder_trav (node->token.V.aobj); PFI(" )"); break;
}
astnode_inorder_trav (node->right);
}
}
/* postorder traverse ast */
void
astnode_postorder_trav (AstNode node)
{
if (node != NULL)
{
astnode_postorder_trav (node->left);
astnode_postorder_trav (node->right);
switch (node->token.type)
{
case T_OPER: PFI( " %s", node->token.V.oval); break;
case T_NUMB: PFI(" %ld", node->token.V.ival); break;
case T_SUBA: astnode_postorder_trav (node->token.V.aobj); break;
}
}
}
/* postorder traverse ast save to array */
void
astnode_postorder_trav_toa (AstNode node, Token tks[], int *idx)
{
if (node != NULL)
{
astnode_postorder_trav_toa (node->left, tks, idx);
astnode_postorder_trav_toa (node->right, tks, idx);
switch (node->token.type)
{
case T_OPER: tks[*idx] = node->token; (*idx)++; break;
case T_NUMB: tks[*idx] = node->token; (*idx)++; break;
case T_SUBA: astnode_postorder_trav_toa (node->token.V.aobj, tks, idx); break;
}
}
}
/* get operator priority */
static int
get_priority (char *op)
{
if (op[0] == '+') return 1;
if (op[0] == '-') return 1;
if (op[0] == '*') return 2;
if (op[0] == '/') return 2;
}
/* define stack height */
#define STCKSIZE 16
/* define number buffer length */
#define NUMBSIZE 20
/* parse expression string to ast */
AstNode
parse_string (char *estr)
{
AstNode head = NULL;
AstNode st[STCKSIZE] = {0};
AstNode tp[STCKSIZE] = {0};
int lv = 0;
char nbuf[NUMBSIZE] = {0};
int ndx = 0;
int idx = 0;
char c;
c = estr[idx];
while (c != '\0')
{
switch (c)
{
case '0'...'9': //number
{
char n = estr[idx + 1]; //next char
nbuf[ndx] = c; ndx++;
if (!(n >= '0' && n <= '9')) //not a number
{
long lt = strtol (nbuf, NULL, 10);
Token tk = { .type = T_NUMB, .V.ival = lt };
AstNode node = astnode_new (tk);
if (st[lv] == NULL)
{
if (tp[lv] == NULL) tp[lv] = node;
else
PFI("Info: At index %d, Maybe Syntax error!\n", idx);
}
else
{
AstNode tmp = st[lv]->right;
if (tmp == NULL)
{
st[lv]->right = node;
}
else
{
while (tmp->right != NULL)
tmp = tmp->right;
tmp->right = node;
}
}
PFI("NUMB: %s\n", nbuf);
memset (nbuf, 0, NUMBSIZE); ndx = 0; //init nbuf
}
}
break;
case '+': case '-': case '*': case '/': //operator
{
Token tk = { .type = T_OPER, .V.oval[0] = c };
AstNode node = astnode_new (tk);
int opr = get_priority (tk.V.oval);
if (st[lv] == NULL)
{
if (tp[lv] == NULL)
{
st[lv] = node;
}
else
{
node->left = tp[lv];
st[lv] = node;
}
}
else
{
int ppr = get_priority (st[lv]->token.V.oval);
if (opr > ppr)
{
node->left = st[lv]->right;
st[lv]->right = node;
}
else
{
node->left = st[lv];
st[lv] = node;
}
}
PFI(" OP: %c\n", c);
}
break;
case '(': //left parentheses
{
lv++; PFI("LPAR: %c\n", c);
}
break;
case ')': //right parentheses
{
AstNode node = NULL;
Token tk = { .type = T_SUBA, .V.aobj = st[lv] };
if (st[lv] == NULL) tk.V.aobj = tp[lv];
node = astnode_new (tk);
if (st[lv-1] == NULL)
{
st[lv-1] = node; st[lv] = NULL; tp[lv] = NULL;
}
else
{
if (st[lv-1]->right == NULL)
{
st[lv-1]->right = node;
}
else
{
AstNode tmp = st[lv-1]->right;
while (tmp->right != NULL)
tmp = tmp->right;
tmp->right = node;
}
st[lv] = NULL; tp[lv] = NULL;
}
lv--; PFI("RPAR: %c\n", c);
}
break;
case ' ': break; //space do nothing
default:
PFI("Error: At index %d, [%c], Syntax error!\n", idx, c); exit (0);
}
idx++; c = estr[idx];
}
if (st[lv] == NULL)
{
if (tp[lv] == NULL) {} //return NULL
else head = tp[lv];
}
else
{
head = st[lv];
}
return head;
}
/* evaluate astnode */
Token
eval_astnode (AstNode node)
{
int index = 0, count = 0;
Token *tks, tk, stk[16] = {0};
int sp = 0;
count = astnode_count (node);
tks = (Token*) malloc (count * sizeof(Token));
astnode_postorder_trav_toa (node, tks, &index);
PF("Node count: %d\n", count);
PF("Node index: %d\n", index);
for (int i = 0; i < count; i++)
{
switch (tks[i].type)
{
case T_NUMB: //number
{
PFI("PUSH %ld, SP: %d\n", tks[i].V.ival, sp);
stk[sp] = tks[i]; sp = sp + 1;
}
break;
case T_OPER: //operator
{
switch (tks[i].V.oval[0])
{
case '+': //add
PFI("ADD %ld %ld", stk[sp-2].V.ival, stk[sp-1].V.ival);
stk[sp-2].V.ival = stk[sp-2].V.ival + stk[sp-1].V.ival;
PFI(" ==> %ld\n", stk[sp-2].V.ival);
stk[sp-1].type = 0; stk[sp-1].V.nval = 0; sp = sp - 1;
break;
case '-': //sub
if (sp-2 < 0)
{
PFI("SUB %ld", stk[sp-1].V.ival);
stk[sp-1].V.ival = -(stk[sp-1].V.ival);
PFI(" ==> %ld\n", stk[sp-1].V.ival);
break;
}
PFI("SUB %ld %ld", stk[sp-2].V.ival, stk[sp-1].V.ival);
stk[sp-2].V.ival = stk[sp-2].V.ival - stk[sp-1].V.ival;
PFI(" ==> %ld\n", stk[sp-2].V.ival);
stk[sp-1].type = 0; stk[sp-1].V.nval = 0; sp = sp - 1;
break;
case '*': //mul
PFI("MUL %ld %ld", stk[sp-2].V.ival, stk[sp-1].V.ival);
stk[sp-2].V.ival = stk[sp-2].V.ival * stk[sp-1].V.ival;
PFI(" ==> %ld\n", stk[sp-2].V.ival);
stk[sp-1].type = 0; stk[sp-1].V.nval = 0; sp = sp - 1;
break;
case '/': //div
if (stk[sp-1].V.ival == 0)
{
PF("Error: The divisor cannot be zero!\n");
break;
}
PFI("DIV %ld %ld", stk[sp-2].V.ival, stk[sp-1].V.ival);
stk[sp-2].V.ival = stk[sp-2].V.ival / stk[sp-1].V.ival;
PFI(" ==> %ld\n", stk[sp-2].V.ival);
stk[sp-1].type = 0; stk[sp-1].V.nval = 0; sp = sp - 1;
break;
}
}
break;
}
}
free (tks);
tk = stk[0];
return tk;
}
/* define expression buffer length */
#define EXPRSIZE 1024
/* run a expression shell */
void
expr_shell (void)
{
char buf[EXPRSIZE] = {0};
int idx = 0;
char c;
PF("EXPR:> ");
c = fgetc (stdin);
while (c != EOF)
{
if (c == '\n')
{
Token tk = { .type = 0, .V.nval = 0 };
AstNode root = NULL;
PF("------------------------------\n");
root = parse_string (buf);
PF("------------------------------\n MID: ");
astnode_inorder_trav (root);
PF("\n------------------------------\n");
tk = eval_astnode (root);
PF("------------------------------\n");
PF("RESULT: %ld\n", tk.V.ival);
PF("------------------------------\n");
astnode_free (root);
memset (buf, 0, EXPRSIZE); idx = 0;
PF("EXPR:> ");
}
else
{
buf[idx] = c; idx++;
}
c = fgetc (stdin);
}
PF("\nBye!\n");
}
/**************************************************/
/* test eval astnode function */
void
test_eval (void)
{
AstNode root = NULL;
Token tk;
//char *st = "1+2*3";
//char *st = "1+2*3+4";
//char *st = "1*2+3*4";
//char *st = "1*2*3+5*6*7";
//char *st = "(10+20)*30";
//char *st = "10+20+-30";
//char *st = "1000";
//char *st = "((1001)+2)";
//char *st = "-(1000-900)";
char *st = "1001/(90-90)";
PF("EXPR: %s\n", st);
PF("--------------------\n");
root = parse_string (st);
PF("--------------------\n");
PF(" MID: "); astnode_inorder_trav (root); PF("\n");
PF("--------------------\n");
PF("PREV: "); astnode_prevorder_trav (root); PF("\n");
PF("--------------------\n");
astnode_visual_trav (root);
PF("--------------------\n");
PF("POST: "); astnode_postorder_trav (root); PF("\n");
PF("--------------------\n");
tk = eval_astnode (root);
PF("--------------------\n");
PF("RESULT: %ld\n", tk.V.ival);
PF("--------------------\n");
astnode_free (root);
}
/* test parse string function */
void
test_parse (void)
{
AstNode root = NULL;
//char *st = "1+2-3";
//char *st = "1+2-3+4";
//char *st = "1+2-3+4-5";
//char *st = "100-200+300+400-500";
//char *st = "1+2*3";
//char *st = "1*2+3";
//char *st = "1+2*3+4";
//char *st = "1+2+3*4+5+6";
//char *st = "1*2+3*4";
//char *st = "1*2*3+4*5*6+7*8*9";
//char *st = "1*(2+3)";
//char *st = "(1+2)*3";
//char *st = "1*(2+3+4)*5";
//char *st = "1+(2-(3+4+5)-6)+7";
//char *st = "(((1+2)-3)+4)-5";
char *st = "1+(2-(3+(4-5)))";
PF("EXPR: %s\n", st);
PF("--------------------\n");
root = parse_string (st);
PF("--------------------\n");
PF(" MID: "); astnode_inorder_trav (root); PF("\n");
PF("--------------------\n");
PF("PREV: "); astnode_prevorder_trav (root); PF("\n");
PF("--------------------\n");
PF("POST: "); astnode_postorder_trav (root); PF("\n");
PF("--------------------\n");
astnode_visual_trav (root);
PF("--------------------\n");
astnode_free (root);
}
/* test astnode function */
void
test_astnode (void)
{
AstNode root = NULL, tpl = NULL, tpr = NULL;
Token tka = { .type = T_NUMB, .V.ival = 1 };
Token tkb = { .type = T_NUMB, .V.ival = 2 };
Token tkc = { .type = T_OPER, .V.oval[0] = '+' };
Token tkd = { .type = T_NUMB, .V.ival = 3 };
Token tke = { .type = T_OPER, .V.oval[0] = '-' };
tpl = astnode_new (tka); //1
tpr = astnode_new (tkb); //2
root = astnode_new (tkc); //+
root->left = tpl;
root->right = tpr;
tpl = root;
tpr = astnode_new (tkd); //3
root = astnode_new (tke); //-
root->left = tpl;
root->right = tpr;
PF(" MID: ");
astnode_inorder_trav (root); PF("\n");
PF("PREV: ");
astnode_prevorder_trav (root); PF("\n");
PF("POST: ");
astnode_postorder_trav (root); PF("\n");
astnode_free (root);
}
/* main function */
int
main (int argc, char *argv[])
{
//test_astnode ();
//test_parse ();
//test_eval ();
expr_shell ();
return 0;
}
//-----GT-----//