《ooc》第三章针对动态链接举例,解析并计算算术表达式。《数据结构》中有关于这一块的描述,即算术表达式是某个二叉树的中序序列,示例中是将该算术表达式(中序序列)还原为一个二叉树,然后进行计算。当然该例的目的不是让我们重温数据结构,而是让我们通过该示例进一步加深对“动态链接”的理解。
首先从二叉树着手,抽象出结点类型,具体包括分支结点(包括根结点)、叶子;分支结点对应于二元操作符(+ - * /),叶子结点对应于数值或一元操作符(负值运算符);现在对于我们需要定义的类型有了大致的了解,我们需要针对所有类型定义计算方法,用于计算以该结点为根结点的树的值。和第二章类似,我们还需要针对对象资源的分配和回收作些文章。首先声明结点类型、树(对象)管理的方法:
/* * node types */ const void * Minus; const void * Value; const void * Mult; const void * Div; const void * Add; const void * Sub; /* * tree management */ void * new (const void * type, ...); void process (const void * tree); void delete (void * tree);
struct Type { void * (* new) (va_list ap); double (* exec) (const void * tree); void (* delete) (void * tree); };
void * new (const void * type, ...) { va_list ap; void * result; assert(type && ((struct Type *) type) -> new); va_start(ap, type); result = ((struct Type *) type) -> new(ap); * (const struct Type **) result = type; va_end(ap); return result; } // 树值计算 static double exec (const void * tree) { assert(tree && * (struct Type **) tree && (* (struct Type **) tree) -> exec); return (* (struct Type **) tree) -> exec(tree); } void process (const void * tree) { printf("\t%g\n", exec(tree)); } void delete (void * tree) { assert(tree && * (struct Type **) tree && (* (struct Type **) tree) -> delete); (* (struct Type **) tree) -> delete(tree); }
struct Val { const void * type; double value; }; static void * mkVal (va_list ap) { struct Val * node = malloc(sizeof(struct Val)); assert(node); node -> value = va_arg(ap, double); return node; } static double doVal (const void * tree) { return ((struct Val *) tree) -> value; } static struct Type _Value = { mkVal, doVal, free }; const void * Value = & _Value;
struct Un { const void * type; void * arg; }; static void * mkUn (va_list ap) { struct Un * node = malloc(sizeof(struct Un)); assert(node); node -> arg = va_arg(ap, void *); return node; } static double doMinus (const void * tree) { return - exec(((struct Un *) tree) -> arg); } static void freeUn (void * tree) { delete(((struct Un *) tree) -> arg); free(tree); } static struct Type _Minus = { mkUn, doMinus, freeUn }; const void * Minus = & _Minus;
struct Bin { const void * type; void * left, * right; }; static void * mkBin (va_list ap) { struct Bin * node = malloc(sizeof(struct Bin)); assert(node); node -> left = va_arg(ap, void *); node -> right = va_arg(ap, void *); return node; } static double doAdd (const void * tree) { return exec(((struct Bin *) tree) -> left) + exec(((struct Bin *) tree) -> right); } static double doSub (const void * tree) { return exec(((struct Bin *) tree) -> left) - exec(((struct Bin *) tree) -> right); } static double doMult (const void * tree) { return exec(((struct Bin *) tree) -> left) * exec(((struct Bin *) tree) -> right); } static double doDiv (const void * tree) { double left = exec(((struct Bin *) tree) -> left); double right = exec(((struct Bin *) tree) -> right); if (right == 0.0) error("division by zero"); return left / right; } static void freeBin (void * tree) { delete(((struct Bin *) tree) -> left); delete(((struct Bin *) tree) -> right); free(tree); } static struct Type _Add = { mkBin, doAdd, freeBin }; static struct Type _Sub = { mkBin, doSub, freeBin }; static struct Type _Mult = { mkBin, doMult, freeBin }; static struct Type _Div = { mkBin, doDiv, freeBin }; const void * Add = & _Add; const void * Sub = & _Sub; const void * Mult = & _Mult; const void * Div = & _Div;
我们在针对某个表达式解析时,需要判断当前的symbols是空格、数值还是字符(运算符、括号等);处理时首先忽略掉空格,这就需要标记当前的东东是数值还是字符,我们用枚举值NUMBER表示数值:
enum tokens { /* must not clash with operators */ NUMBER = 'n' /* literal constant */ };
static enum tokens token; /* current input symbol */ static double number; /* if NUMBER: numerical value */
static enum tokens scan (const char * buf) /* return token = next input symbol */ { static const char * bp; if (buf) bp = buf; /* new input line */ while (isspace(* bp & 0xff)) ++ bp; if (isdigit(* bp & 0xff) || * bp == '.') { errno = 0; token = NUMBER, number = strtod(bp, (char **) & bp); if (errno == ERANGE) error("bad value: %s", strerror(errno)); } else { token = * bp ? * bp ++ : 0; } return token; }
/* * factor : + factor * - factor * NUMBER * ( sum ) */ static void * sum (void); static void * factor (void) { void * result; switch (token) { case '+': scan(0); return factor(); case '-': scan(0); return new(Minus, factor()); default: error("bad factor: '%c' 0x%x", token, token); case NUMBER: result = new(Value, number); break; case '(': scan(0); result = sum(); if (token != ')') error("expecting )"); } scan(0); return result; } /* * product : factor { *|/ factor }... */ static void * product (void) { void * result = factor(); const void * type; for (;;) { switch (token) { case '*': type = Mult; break; case '/': type = Div; break; default: return result; } scan(0); result = new(type, result, factor()); } } /* * sum : product { +|- product }... */ static void * sum (void) { void * result = product(); const void * type; for (;;) { switch (token) { case '+': type = Add; break; case '-': type = Sub; break; default: return result; } scan(0); result = new(type, result, product()); } }
static jmp_buf onError; void error (const char * fmt, ...) { va_list ap; va_start(ap, fmt); vfprintf(stderr, fmt, ap), putc('\n', stderr); va_end(ap); longjmp(onError, 1); }
int main (void) { volatile int errors = 0; char buf [BUFSIZ]; if (setjmp(onError)) // 异常恢复断点 ++ errors; while (fgets(buf, sizeof buf, stdin)) if (scan(buf)) { void * e = sum(); // 创建树 if (token) error("trash after sum"); process(e); // 计算树值 delete(e); // 释放树 } return errors > 0; }