Bootstrap

C语言之为波兰表达式增加函数调用功能

C语言之为波兰表达式增加函数调用功能

增加比较运算的不等于功能,首先解析不等于运算符

  • 在parse_expr_string函数的switch语句中加入:case ‘!’:
	case '>': case '<': case '=': case '!':
  • 当字符串为"!="时,代码如下:
	    else if (c == '!') //op !
	      {
		op[0] = c;
		if (n == '=')  //op !=
		  { op[1] = n; tmp = expr_node_new_op (op); idx = idx + 1; }
		else
		  { printf ("Error: Syntax error! !???"); exit(0); }
	      }
  • 在test_parse函数中加入测试字符串
  char *str = "(!= 99 100)";

编译运行,达到预期,效果如下:

gwsong@ubuntu:~/works/notes/lisp$ gcc xe.c -o xe
gwsong@ubuntu:~/works/notes/lisp$ ./xe
 express : (!= 99 100)
-------------------------
  OP: !=
 INT: 99
 INT: 100
-------------------------
full expr: ( != 99 100 )
-------------------------
Result : 0
gwsong@ubuntu:~/works/notes/lisp$ 

运算不等于表达式,输出结果

  • 定义不等于比较类型:CMP_NE,not equal
...
#define CMP_NE 5   //not equal '!='
...
  • 在expr_node_opcmp函数中加入代码如下,实现运算功能
...
    case CMP_NE: if (lta != ltb) ex.V.bval = BOOL_T; break;
...
  • 在expr_node_compute函数中加入代码如下,达到准确调用expr_node_opcmp函数,完成运算
    case '!':
      {
	if (tmp->expr.V.oval[1] == '=')
	  rb = expr_node_opcmp (tmp->next, CMP_NE);
	return rb;
      }
  • 在test_parse函数中加入两个测试表达式
  //char *str = "(!= 99 100)";
  char *str = "(!= 199 199)";

编译运行,达到预期

  • 表达式:char *str = “(!= 99 100)”;的运算结果如下:
gwsong@ubuntu:~/works/notes/lisp$ gcc xe.c -o xe
gwsong@ubuntu:~/works/notes/lisp$ ./xe
 express : (!= 99 100)
-------------------------
  OP: !=
 INT: 99
 INT: 100
-------------------------
full expr: ( != 99 100 )
-------------------------
Result : #t
gwsong@ubuntu:~/works/notes/lisp$ 
  • 表达式:char *str = “(!= 199 199)”;的运算结果如下:
gwsong@ubuntu:~/works/notes/lisp$ gcc xe.c -o xe
gwsong@ubuntu:~/works/notes/lisp$ ./xe
 express : (!= 199 199)
-------------------------
  OP: !=
 INT: 199
 INT: 199
-------------------------
full expr: ( != 199 199 )
-------------------------
Result : #f
gwsong@ubuntu:~/works/notes/lisp$ 

定义Procedure数据结构

  • 在xe.h头文件中加入代码如下:
/* define Procedure data struct */
typedef struct _Procedure Procedure;
struct _Procedure {
  char *name;     //procedure name
  void* (*fp) (); //function pointer
  int  reqs;      //request args
};

/* define function pointer to uese */
typedef void* (*_FP) ();

/* add a function pointer to table */
void
regist_subr (Procedure *pt, char *name, _FP fp, int reqs);

实现函数表相关功能,让表达式调用函数

  • 函数(function),回调函数(callback function),过程(procedure),子程序(subroutine),这些是同一功能的多种不同角度的称呼,要注意!!!
  • 自定义两个函数:fn_newline和fn_display,用于表达式调用
  • regist_subr函数用于向函数表添加自定义函数
  • init_proc_table函数调用regist_subr来实现函数表的初始化
  • 在xe.c文件中加入代码如下:
/* the newline function */
static void* fn_newline (void)
{
  printf ("\n"); return NULL;
}

/* the display function */
static void* fn_display (Expr ex)
{
  out_expr_value (ex); return NULL;
}

/* add a function pointer to table */
void
regist_subr (Procedure *pt, char *name, _FP fp, int reqs)
{
  pt->name = strdup(name);
  pt->fp = fp;
  pt->reqs = reqs;
}

/* init procedure table */
static void
init_proc_table (Procedure *ptab, int *plen)
{
  regist_subr (&(ptab[0]), "newline", fn_newline, 0);
  regist_subr (&(ptab[1]), "display", fn_display, 1);
  *plen = 2;
}

编码测试

  • 编写test_proc函数,测试函数表,输出表长度,输出表中的函数名,参数数量
/* test procedure function */
void
test_proc (void)
{
  int len = 0;
  Procedure *ptable = NULL;

  ptable = (Procedure*) malloc (32 * sizeof(Procedure));
  memset (ptable, 0, 32*sizeof(Procedure));

  init_proc_table (ptable, &len);

  printf ("Procedure table length is %d\n", len);

  for (int i = 0; i < 32; i++)
    {
      if (ptable[i].name != NULL)
	{
	  printf ("Func%d : [%s], args: %d\n", i, ptable[i].name, ptable[i].reqs);
	  free (ptable[i].name);
	}
    }

  free (ptable);
}

编译运行,检查一下内存情况,效果如下:

gwsong@ubuntu:~/works/notes/lisp$ gcc xe.c -o xe
gwsong@ubuntu:~/works/notes/lisp$ ./xe
Procedure table length is 2
Func0 : [newline], args: 0
Func1 : [display], args: 1
gwsong@ubuntu:~/works/notes/lisp$ valgrind --leak-check=yes ./xe
==32893== Memcheck, a memory error detector
==32893== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
==32893== Using Valgrind-3.13.0 and LibVEX; rerun with -h for copyright info
==32893== Command: ./xe
==32893== 
Procedure table length is 2
Func0 : [newline], args: 0
Func1 : [display], args: 1
==32893== 
==32893== HEAP SUMMARY:
==32893==     in use at exit: 0 bytes in 0 blocks
==32893==   total heap usage: 4 allocs, 4 frees, 1,808 bytes allocated
==32893== 
==32893== All heap blocks were freed -- no leaks are possible
==32893== 
==32893== For counts of detected and suppressed errors, rerun with: -v
==32893== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)
gwsong@ubuntu:~/works/notes/lisp$ 

实现函数调用功能

  • 定义expr_node_evaluate函数,实现调用函数功能
  • expr_node_evaluate函数需要两个参数:函数表和函数表的长度
  • 当节点类型为运算符(DT_OPERATOR)时,调用运算函数,返回结果!
  • 当节点类型为子节点(DT_SUBEXPR)时,递归调用自身!
  • 当节点类型为标识符(DT_IDENTIF)时,按标识符的字符串在函数表中查找函数指针!
  • 如果标识符不在函数表中,显示出错信息,退出程序!!!
  • 如果在则根据函数的参数数量调用函数指针,并返回运算结果!!!
  • 代码如下:
/* evaluate express node with procedure table */
Expr
expr_node_evaluate (ExprNode *node, Procedure *ptab, int plen)
{
  Expr ex = {0, 0};
  if (node->expr.dt == DT_SUBEXPR)
    {
      ex = expr_node_evaluate (node->expr.V.eval, ptab, plen);
    }
  else if (node->expr.dt == DT_IDENTIF)
    {
      char *tname = node->expr.V.sval;
      Procedure *subr = NULL;
      for (int i = 0; i < plen; i++)
	{ if (0 == strcmp (tname, ptab[i].name)) { subr = &(ptab[i]); break; } }
      if (subr == NULL)
	{ printf ("Error: [%s] Procedure not found!\n", tname); exit (0); }
      else
	{
	  if (subr->reqs == 0)
	    { void *rp = subr->fp (); if (rp == NULL) ex.V.vval = NULL; }
	  else if (subr->reqs == 1)
	    {
	      Expr ex = node->next->expr;
	      void *rp = subr->fp (ex);
	      if (rp == NULL) ex.V.vval = NULL;
	    }
	  else
	    {} //todo
	}
    }
  else if (node->expr.dt == DT_OPERATOR)
    {
      ex = expr_node_compute (node);
    }
  else
    {} //todo
  return ex;
}

测试函数调用

  • 表达式:char *str = “((display 2025) (newline))”;的功能是输出2025然后换行!
  • 在上面的test_proc函数基础上,加入解析表达式,循环计算表达式功能!
  • 代码如下:
/* test procedure function */
void
test_proc (void)
{
  int len = 0;
  Procedure *ptable = NULL;
  //char *str = "((display 2025) (newline))";
  //char *str = "((display 2025) (newline) (display 2026) (newline))";
  //char *str = "(;aha\n(display 2025) ;oho\n(newline))";
  //char *str = "((display 2025) (newline) (display #t) (newline))";
  char *str = "((display 2025) (newline) (display 2025) (newline) (+ 2025 2025))";
  Expr rs = { DT_INTEGER, 0};
  ExprNode *head = NULL, *tmp = NULL;

  /* create a length 32 procedure table */
  ptable = (Procedure*) malloc (32 * sizeof(Procedure));
  memset (ptable, 0, 32*sizeof(Procedure));

  /* init the procedure table */
  init_proc_table (ptable, &len);

  printf ("Procedure table length is %d\n", len);

  //parse express
  head = parse_expr_string (str);

  printf (" express : %s\n", str);
  printf ("-------------------------\n");
  out_expinfo (head);
  printf ("-------------------------\n");
  printf ("full expr:\n");
  out_express (head);
  printf ("\n-------------------------\n");

  //eval express node
  tmp = head;
  while (tmp != NULL)
    {
      rs = expr_node_evaluate (tmp, ptable, 32);
      if (rs.V.vval != NULL) //
	{
	  out_expr_value (rs); printf ("\n");
	}
      tmp = tmp->next;
    }

  printf ("-------------------------\n");

  expr_node_free (head); // free the node
  for (int i = 0; i < 32; i++) // free the procedure name
    {
      if (ptable[i].name != NULL)
	{
	  printf ("Func%d : [%s], args: %d\n", i, ptable[i].name, ptable[i].reqs);
	  free (ptable[i].name);
	}
    }
  free (ptable); //free the procedure table
}

编译运行,检查内存情况,基本达到预期,效果如下:

gwsong@ubuntu:~/works/notes/lisp$ gcc xe.c -o xe
gwsong@ubuntu:~/works/notes/lisp$ ./xe
Procedure table length is 2
 express : ((display 2025) (newline) (display 2025) (newline) (+ 2025 2025))
-------------------------
 SUB:
   IDNT: display
    INT: 2025
 SUB:
   IDNT: newline
 SUB:
   IDNT: display
    INT: 2025
 SUB:
   IDNT: newline
 SUB:
     OP: +
    INT: 2025
    INT: 2025
-------------------------
full expr:
 ( ( display 2025 )
 ( newline )
 ( display 2025 )
 ( newline )
 ( + 2025 2025 )
 )
-------------------------
 2025
 2025
 4050
-------------------------
Func0 : [newline], args: 0
Func1 : [display], args: 1
gwsong@ubuntu:~/works/notes/lisp$ valgrind --leak-check=yes ./xe
==33146== Memcheck, a memory error detector
==33146== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
==33146== Using Valgrind-3.13.0 and LibVEX; rerun with -h for copyright info
==33146== Command: ./xe
==33146== 
Procedure table length is 2
 express : ((display 2025) (newline) (display 2025) (newline) (+ 2025 2025))
-------------------------
 SUB:
   IDNT: display
    INT: 2025
 SUB:
   IDNT: newline
 SUB:
   IDNT: display
    INT: 2025
 SUB:
   IDNT: newline
 SUB:
     OP: +
    INT: 2025
    INT: 2025
-------------------------
full expr:
 ( ( display 2025 )
 ( newline )
 ( display 2025 )
 ( newline )
 ( + 2025 2025 )
 )
-------------------------
 2025
 2025
 4050
-------------------------
Func0 : [newline], args: 0
Func1 : [display], args: 1
==33146== 
==33146== HEAP SUMMARY:
==33146==     in use at exit: 0 bytes in 0 blocks
==33146==   total heap usage: 22 allocs, 22 frees, 2,176 bytes allocated
==33146== 
==33146== All heap blocks were freed -- no leaks are possible
==33146== 
==33146== For counts of detected and suppressed errors, rerun with: -v
==33146== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)
gwsong@ubuntu:~/works/notes/lisp$ 

将源代码重构成两个文件,头文件xe.h代码如下:

/* filename: xe.h */
#ifndef XE_HEADER
#define XE_HEADER

/* define boolean value */
#define BOOL_F 0  //#f , false
#define BOOL_T 1  //#t , true

/* define compare type */
#define CMP_BT 0   //big than  '>'
#define CMP_BE 1   //big equal '>='
#define CMP_LT 2   //less than '<'
#define CMP_LE 3   //less equal '<='
#define CMP_EQ 4   //equal '='
#define CMP_NE 5   //not equal '!='

/* define compute type */
#define OPR_ADD 0  //add +
#define OPR_SUB 1  //sub -
#define OPR_MUL 2  //mul *
#define OPR_DIV 3  //div /

/* define express datatype */
#define DT_OPERATOR 0X01
#define DT_INTEGER  0X02
#define DT_SUBEXPR  0X03
#define DT_BOOLEAN  0X04
#define DT_IDENTIF  0X05
#define DT_FUNCTION 0X06 //todo

/* define express node struct */
typedef struct _ExpressNode ExprNode;

/* define express struct */
typedef struct _Express Expr;

struct _Express {
  //char name[7]; //todo save variable name
  char dt; //express datatype
  union {
        void *vval; //pointer for return result
        char  oval[8]; //operator
        long  ival; //integer
    ExprNode *eval; //subexpr
        char  bval; //boolean
        char *sval; //identifiers
  } V;
};

/* define express node struct */
struct _ExpressNode {
  Expr expr;
  ExprNode *next;
};

/* define function pointer for express node */
typedef void (*EPFunc) (Expr expr);

/* create a new operator express node */
ExprNode* expr_node_new_op (char *val);

/* create a new integer express node */
ExprNode* expr_node_new_int (long val);

/* create a new sub express node */
ExprNode* expr_node_new_sub (ExprNode *val);

/* create a new boolean express node */
ExprNode* expr_node_new_bool (char val);

/* create a new identifier express node */
ExprNode* expr_node_new_idnt (char *val);

/* free the express node list */
void expr_node_free (ExprNode *node);

/* append node to head */
ExprNode* expr_node_append (ExprNode *head, ExprNode *node);

/* foreach node call epfunc from head to end */
void expr_node_foreach (ExprNode *head, EPFunc epfunc);

/* compute express list */
Expr expr_node_compute (ExprNode *node);

/* output express info */
void out_expinfo (ExprNode *head);

/* output express value */
void out_express (ExprNode *head);

/* output full express info */
void out_expinfo (ExprNode *head);

/* output full express value */
void out_express (ExprNode *head);

/* parse express string to express node */
ExprNode* parse_expr_string (char *estr);

/* define Procedure data struct */
typedef struct _Procedure Procedure;
struct _Procedure {
  char *name;     //procedure name
  void* (*fp) (); //function pointer
  int  reqs;      //request args
};

/* define function pointer to uese */
typedef void* (*_FP) ();

/* add a function pointer to table */
void
regist_subr (Procedure *pt, char *name, _FP fp, int reqs);

/* evaluate express node with procedure table */
Expr
expr_node_evaluate (ExprNode *node, Procedure *ptab, int plen);

//todo

#endif/*XE_HEADER*/

功能实现源码文件xe.c完整代码如下:

/* filename: xe.c */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include "xe.h"

/* compile : gcc xe.c -o xe */
/*     run : ./xe           */

/* create a new operator express node */
ExprNode *
expr_node_new_op (char *val)
{
  ExprNode *node = (ExprNode*) malloc (sizeof(ExprNode));
  node->expr.dt = DT_OPERATOR;
  memset (node->expr.V.oval, 0, 8);
  for (int i = 0; i < 8; i++)
    node->expr.V.oval[i] = val[i];
  node->next = NULL;
  return node;
}

/* create a new integer express node */
ExprNode *
expr_node_new_int (long val)
{
  ExprNode *node = (ExprNode*) malloc (sizeof(ExprNode));
  node->expr.dt = DT_INTEGER;
  node->expr.V.ival = val;
  node->next = NULL;
  return node;
}

/* create a new sub express node */
ExprNode *
expr_node_new_sub (ExprNode *val)
{
  ExprNode *node = (ExprNode*) malloc (sizeof(ExprNode));
  node->expr.dt = DT_SUBEXPR;
  node->expr.V.eval = val;
  node->next = NULL;
  return node;
}

/* create a new boolean express node */
ExprNode *
expr_node_new_bool (char val)
{
  ExprNode *node = (ExprNode*) malloc (sizeof(ExprNode));
  node->expr.dt = DT_BOOLEAN;
  node->expr.V.bval = val;
  node->next = NULL;
  return node;
}

/* create a new identifier express node */
ExprNode *
expr_node_new_idnt (char *val)
{
  ExprNode *node = (ExprNode*) malloc (sizeof(ExprNode));
  node->expr.dt = DT_IDENTIF;
  node->expr.V.sval = strdup(val); //todo
  node->next = NULL;
  return node;
}

/* free the express node list */
void
expr_node_free (ExprNode *node)
{
  while (node != NULL)
    {
      ExprNode *tmp = node->next;
      if (node->expr.dt == DT_SUBEXPR)
	expr_node_free (node->expr.V.eval);
      if (node->expr.dt == DT_IDENTIF)
	free (node->expr.V.sval);
      free (node);
      node = tmp;
    }
}

/* append node to head */
ExprNode *
expr_node_append (ExprNode *head, ExprNode *node)
{
  ExprNode *tmp = head;
  if (head == NULL) return node;
  while (tmp->next != NULL)
    tmp = tmp->next;
  tmp->next = node;
  return head;
}

/* foreach node call epfunc from head to end */
void
expr_node_foreach (ExprNode *head, EPFunc epfunc)
{
  ExprNode *tmp = head;
  while (tmp != NULL)
    {
      epfunc (tmp->expr);
      tmp = tmp->next;
    }
}

/*----------------------------------------*/

/* compute express node by operator */
static Expr
expr_node_opcmpt (ExprNode *node, char opt)
{
  Expr rs;
  long lt = 1;
  ExprNode *tmp = node;
  rs.dt = DT_INTEGER; rs.V.ival = lt;
  if (tmp == NULL)  //(oper)
    {
      switch (opt)
	{
	case OPR_ADD: //(+) return 0
	  rs.V.ival = 0; return rs;
	case OPR_SUB: //(-) out err, need args, todo
	  rs.V.ival = 0; return rs;
	case OPR_MUL: //(*) return 1
	  rs.V.ival = 1; return rs;
	case OPR_DIV: //(/) out err, need args, todo
	  rs.V.ival = 1; return rs;
	}
    }
  if (tmp->expr.dt == DT_SUBEXPR)
    { rs = expr_node_compute (tmp->expr.V.eval); lt = rs.V.ival; }
  else if (tmp->expr.dt == DT_INTEGER)
    { lt = tmp->expr.V.ival; }
  tmp = tmp->next;
  while (tmp != NULL)
    {
      if (tmp->expr.dt == DT_SUBEXPR)
	{
	  rs = expr_node_compute (tmp->expr.V.eval);
	  switch (opt)
	    {
	    case OPR_ADD: lt = lt + rs.V.ival; break;
	    case OPR_SUB: lt = lt - rs.V.ival; break;
	    case OPR_MUL: lt = lt * rs.V.ival; break;
	    case OPR_DIV: lt = lt / rs.V.ival; break;
	    }
	}
      else if (tmp->expr.dt == DT_INTEGER)
	{
	  switch (opt)
	    {
	    case OPR_ADD: lt = lt + tmp->expr.V.ival; break;
	    case OPR_SUB: lt = lt - tmp->expr.V.ival; break;
	    case OPR_MUL: lt = lt * tmp->expr.V.ival; break;
	    case OPR_DIV: lt = lt / tmp->expr.V.ival; break;
	    }
	}
      tmp = tmp->next;
    }
  rs.V.ival = lt;
  /*
  switch (opt)
    {
    case OPR_ADD: printf ("OP add, RS : %ld\n", lt); break;
    case OPR_SUB: printf ("OP sub, RS : %ld\n", lt); break;
    case OPR_MUL: printf ("OP mul, RS : %ld\n", lt); break;
    case OPR_DIV: printf ("OP div, RS : %ld\n", lt); break;
    }
  */
  return rs;
}

/* compare express node by operator */
static Expr
expr_node_opcmp (ExprNode *node, char ct)
{
  Expr ex = { .dt = DT_BOOLEAN, .V.bval = BOOL_F };
  ExprNode *ta = node, *tb = node->next;
  long lta = 0, ltb = 0;

  if (ta->expr.dt == DT_SUBEXPR)
    { Expr et = expr_node_compute (ta->expr.V.eval); lta = et.V.ival; }
  else if (ta->expr.dt == DT_INTEGER)
    lta = ta->expr.V.ival;
  else {} //todo out error msg

  if (tb->expr.dt == DT_SUBEXPR)
    { Expr et = expr_node_compute (tb->expr.V.eval); ltb = et.V.ival; }
  else if (tb->expr.dt == DT_INTEGER)
    ltb = tb->expr.V.ival;
  else {} //todo out error msg

  switch (ct)
    {
    case CMP_BT: if (lta >  ltb) ex.V.bval = BOOL_T; break;
    case CMP_BE: if (lta >= ltb) ex.V.bval = BOOL_T; break;
    case CMP_LT: if (lta <  ltb) ex.V.bval = BOOL_T; break;
    case CMP_LE: if (lta <= ltb) ex.V.bval = BOOL_T; break;
    case CMP_EQ: if (lta == ltb) ex.V.bval = BOOL_T; break;
    case CMP_NE: if (lta != ltb) ex.V.bval = BOOL_T; break;
    }
  return ex;
}

/* compute express list */
Expr
expr_node_compute (ExprNode *node)
{
  Expr rs, rb;
  ExprNode *tmp = node;
  rs.dt = DT_INTEGER; rs.V.ival = 0;
  rb.dt = DT_BOOLEAN; rs.V.bval = BOOL_F;
  switch (tmp->expr.V.oval[0])
    {
    case '+': rs = expr_node_opcmpt (tmp->next, OPR_ADD); break;
    case '-': rs = expr_node_opcmpt (tmp->next, OPR_SUB); break;
    case '*': rs = expr_node_opcmpt (tmp->next, OPR_MUL); break;
    case '/': rs = expr_node_opcmpt (tmp->next, OPR_DIV); break;
    case '>':
      {
	if (tmp->expr.V.oval[1] == '=')
	  rb = expr_node_opcmp (tmp->next, CMP_BE);
	else
	  rb = expr_node_opcmp (tmp->next, CMP_BT);
	return rb;
      }
    case '<':
      {
	if (tmp->expr.V.oval[1] == '=')
	  rb = expr_node_opcmp (tmp->next, CMP_LE);
	else
	  rb = expr_node_opcmp (tmp->next, CMP_LT);
	return rb;
      }
    case '=': rb = expr_node_opcmp (tmp->next, CMP_EQ); return rb;
    case '!':
      {
	if (tmp->expr.V.oval[1] == '=')
	  rb = expr_node_opcmp (tmp->next, CMP_NE);
	return rb;
      }
    }
  return rs;
}

/* evaluate express node with procedure table */
Expr
expr_node_evaluate (ExprNode *node, Procedure *ptab, int plen)
{
  Expr ex = {0, 0};
  if (node->expr.dt == DT_SUBEXPR)
    {
      ex = expr_node_evaluate (node->expr.V.eval, ptab, plen);
    }
  else if (node->expr.dt == DT_IDENTIF)
    {
      char *tname = node->expr.V.sval;
      Procedure *subr = NULL;
      for (int i = 0; i < plen; i++)
	{ if (0 == strcmp (tname, ptab[i].name)) { subr = &(ptab[i]); break; } }
      if (subr == NULL)
	{ printf ("Error: [%s] Procedure not found!\n", tname); exit (0); }
      else
	{
	  if (subr->reqs == 0)
	    { void *rp = subr->fp (); if (rp == NULL) ex.V.vval = NULL; }
	  else if (subr->reqs == 1)
	    {
	      Expr ex = node->next->expr;
	      void *rp = subr->fp (ex);
	      if (rp == NULL) ex.V.vval = NULL;
	    }
	  else
	    {} //todo
	}
    }
  else if (node->expr.dt == DT_OPERATOR)
    {
      ex = expr_node_compute (node);
    }
  else
    {} //todo
  return ex;
}

/*----------------------------------------*/

/* define output express info level */
static int exprlv = 0;

/* output express info */
void
out_expr_info (Expr expr)
{
  for (int i = 0; i < exprlv; i++) printf ("   ");
  switch (expr.dt)
    {
    case DT_OPERATOR: printf ("  OP: %s\n",  expr.V.oval); break;
    case DT_INTEGER : printf (" INT: %ld\n", expr.V.ival); break;
    case DT_SUBEXPR : printf (" SUB:\n");
      exprlv++; out_expinfo (expr.V.eval); exprlv--; break;
    case DT_BOOLEAN : printf ("BOOL: %s\n",
			      ((expr.V.bval == BOOL_F) ? "false" : "true")); break;
    case DT_IDENTIF : printf ("IDNT: %s\n", expr.V.sval); break;
    }
}

/* output express value */
void
out_expr_value (Expr expr)
{
  switch (expr.dt)
    {
    case DT_OPERATOR: printf (" %s",  expr.V.oval); break;
    case DT_INTEGER : printf (" %ld", expr.V.ival); break;
    case DT_SUBEXPR : out_express (expr.V.eval); printf ("\n"); break;
    case DT_BOOLEAN : printf (" %s", ((expr.V.bval == BOOL_F) ? "#f" : "#t")); break;
    case DT_IDENTIF : printf (" %s", expr.V.sval); break;
    }
}

/* output full express info */
void
out_expinfo (ExprNode *head)
{
  expr_node_foreach (head, out_expr_info);
}

/* output full express value */
void
out_express (ExprNode *head)
{
  printf (" (");
  expr_node_foreach (head, out_expr_value);
  printf (" )");
}

/*----------------------------------------*/

/* eat comment line */
static void
peat_comment_line (char *st, int *edx)
{
  int idx = *edx;
  char c = st[idx];
  while (c != 0)
    {
      if (c == '\n') break;
      idx++; c = st[idx];
    }
  *edx = idx;
}

/* define identifier length */
#define IDTSIZE 64

/* define number length */
#define NBSIZE 32

/* define stack height */
#define STSIZE 32

/* parse express string to express node */
ExprNode *
parse_expr_string (char *estr)
{
  ExprNode *root = NULL;
  ExprNode *st[STSIZE] = {0};
  char nbuf[NBSIZE] = {0};
  int idx = 0, ndx = 1, lv = 0;
  int elen = strlen (estr);
  char ibuf[IDTSIZE] = {0};
  int tdx = 0, tflag = 0;
  char c;

  nbuf[0] = '+'; //default +
  c = estr[idx];
  while (c != 0)
    {
      switch (c)
	{
	case ';': //comment line
	  peat_comment_line (estr, &idx);
	  break;

	case 'A'...'Z': case 'a'...'z': //identifier
	  {
	    char n = estr[idx+1];
	    ibuf[tdx] = c; tdx++;
	    if (n == ' ' || n == ')' || n == ';')
	      {
		ExprNode *tmp = expr_node_new_idnt (ibuf);
		st[lv-1] = expr_node_append (st[lv-1], tmp);
		memset (ibuf, 0, IDTSIZE); tdx = 0;
	      }
	  }
	  break;

	case '#': //sharp
	  {
	    char n = estr[idx+1];
	    char m = estr[idx+2];
	    if (m == ' ' || m == ')' || m == ';')
	      {
		char b = -1;
		if      (n == 'f') b = BOOL_F;
		else if (n == 't') b = BOOL_T;
		else break; //todo
		ExprNode *tmp = expr_node_new_bool (b);
		st[lv-1] = expr_node_append (st[lv-1], tmp);
		idx = idx + 1;
	      }
	    //todo
	  }
	  break;

	case '0'...'9': //number
	  {
	    char n = estr[idx+1];
	    nbuf[ndx] = c; ndx++;
	    if (n == ' ' || n == ')')
	      {
		long lt = strtol (nbuf, NULL, 10);
		ExprNode *tmp = expr_node_new_int (lt);
		st[lv-1] = expr_node_append (st[lv-1], tmp);
		memset (nbuf, 0, NBSIZE); nbuf[0] = '+'; ndx = 1;
	      }
	  }
	  break;

	case '+': case '-': case '*': case '/': //operator
	  {
	    if (c == '+' || c == '-')
	      {
		char n = estr[idx+1];
		if (n >= '0' && n <= '9')
		  { nbuf[0] = c; break; }
	      }
	    char op[8] = {0}; op[0] = c;
	    ExprNode *tmp = expr_node_new_op (op);
	    st[lv-1] = expr_node_append (st[lv-1], tmp);
	  }
	  break;

	case '>': case '<': case '=': case '!':
	  {
	    ExprNode *tmp;
	    char op[8] = {0};
	    char n = estr[idx+1];
	    op[0] = c;
	    if (c == '>' || c == '<')
	      {
		if (n == '=')
		  {
		    char m = estr[idx+2];
		    if (m == ' ') //for '>=', '<='
		      { op[1] = n; tmp = expr_node_new_op (op); idx = idx + 1; }
		    else
		      { printf ("Error: Syntax error! =???"); exit(0); }
		  }
		else if (n == ' ')
		  { tmp = expr_node_new_op (op); } //for '>', '<'
		else
		  { printf ("Error: Syntax error! =???"); exit(0); }
	      }
	    else if (c == '!') //op !
	      {
		op[0] = c;
		if (n == '=')  //op !=
		  { op[1] = n; tmp = expr_node_new_op (op); idx = idx + 1; }
		else
		  { printf ("Error: Syntax error! !???"); exit(0); }
	      }
	    else if (c == '=')
	      { tmp = expr_node_new_op (op); } //for '='
	    else
	      {} //todo err msg
	    st[lv-1] = expr_node_append (st[lv-1], tmp);
	  }
	  break;

	case '(':
	  {
	    lv++; //todo
	    if (lv > STSIZE) printf ("Error: Syntax error, Stack overflow!\n");
	  }
	  break;

	case ')':
	  {
	    lv--;
	    if (lv == 0)
	      {
		root = st[lv]; return root; //todo
	      }
	    else if (lv > 0)
	      {
		ExprNode *sub = expr_node_new_sub (st[lv]);
		st[lv-1] = expr_node_append (st[lv-1], sub); st[lv] = NULL;
	      }
	    else
	      { printf ("Error: Syntax error! Stack overflow!\n"); exit(0); } //todo
	  }
	  break;

	case ' ': //space
	  break;

	defualt:
	  printf ("Error: Syntax error!\n"); exit(0);
	}

      if (idx == elen) break;
      idx++; c = estr[idx];
    }

  return root;
}

/*----------------------------------------*/

/* test parse function */
void
test_parse (void)
{
  //char *str = "(!= 99 100)";
  char *str = "(!= 199 199)";
  Expr rs = { DT_INTEGER, 0};
  ExprNode *head;
  head = parse_expr_string (str);

  if (head == NULL) return;
  printf (" express : %s\n", str);
  printf ("-------------------------\n");
  out_expinfo (head);
  printf ("-------------------------\n");
  printf ("full expr:");
  out_express (head);
  printf ("\n-------------------------\n");
  printf ("Result : ");
  rs = expr_node_compute (head);
  switch (rs.dt)
    {
    case DT_INTEGER: printf ("%ld\n", rs.V.ival); break;
    case DT_BOOLEAN: printf ("%s\n", ((rs.V.bval == BOOL_F) ? "#f" : "#t")); break;
    }
  expr_node_free (head);
}

/*----------------------------------------*/

/* the newline function */
static void* fn_newline (void)
{
  printf ("\n"); return NULL;
}

/* the display function */
static void* fn_display (Expr ex)
{
  out_expr_value (ex); return NULL;
}

/* add a function pointer to table */
void
regist_subr (Procedure *pt, char *name, _FP fp, int reqs)
{
  pt->name = strdup(name);
  pt->fp = fp;
  pt->reqs = reqs;
}

/* init procedure table */
static void
init_proc_table (Procedure *ptab, int *plen)
{
  regist_subr (&(ptab[0]), "newline", fn_newline, 0);
  regist_subr (&(ptab[1]), "display", fn_display, 1);
  *plen = 2;
}

/*----------------------------------------*/

/* test procedure function */
void
test_proc (void)
{
  int len = 0;
  Procedure *ptable = NULL;
  //char *str = "((display 2025) (newline))";
  //char *str = "((display 2025) (newline) (display 2026) (newline))";
  //char *str = "(;aha\n(display 2025) ;oho\n(newline))";
  //char *str = "((display 2025) (newline) (display #t) (newline))";
  char *str = "((display 2025) (newline) (display 2025) (newline) (+ 2025 2025))";
  Expr rs = { DT_INTEGER, 0};
  ExprNode *head = NULL, *tmp = NULL;

  /* create a length 32 procedure table */
  ptable = (Procedure*) malloc (32 * sizeof(Procedure));
  memset (ptable, 0, 32*sizeof(Procedure));

  /* init the procedure table */
  init_proc_table (ptable, &len);

  printf ("Procedure table length is %d\n", len);

  //parse express
  head = parse_expr_string (str);

  printf (" express : %s\n", str);
  printf ("-------------------------\n");
  out_expinfo (head);
  printf ("-------------------------\n");
  printf ("full expr:\n");
  out_express (head);
  printf ("\n-------------------------\n");

  //eval express node
  tmp = head;
  while (tmp != NULL)
    {
      rs = expr_node_evaluate (tmp, ptable, 32);
      if (rs.V.vval != NULL) //
	{
	  out_expr_value (rs); printf ("\n");
	}
      tmp = tmp->next;
    }

  printf ("-------------------------\n");

  expr_node_free (head); // free the node
  for (int i = 0; i < 32; i++) // free the procedure name
    {
      if (ptable[i].name != NULL)
	{
	  printf ("Func%d : [%s], args: %d\n", i, ptable[i].name, ptable[i].reqs);
	  free (ptable[i].name);
	}
    }
  free (ptable); //free the procedure table
}

/*----------------------------------------*/

/* output debug info on/off */
#define MG_DEBUG 1

/* interactive evalute express */
void
eval_express (void)
{
  ExprNode *elist = NULL;
  char buf[1024] = {0};
  int idx = 0;
  char c;

  printf ("REPL:> ");
  c = fgetc (stdin);
  while (c != EOF)
    {
      if (c == '\n')
	{
	  Expr ex = {0, 0};
	  elist = parse_expr_string (buf);
	  #if MG_DEBUG
	  printf ("-------------------------\n");
	  out_expinfo (elist);
	  printf ("-------------------------\n");
	  printf ("full expr:");
	  out_express (elist);
	  printf ("\n-------------------------\n");
	  printf ("Result : ");
	  #endif
	  ex = expr_node_compute (elist);
	  switch (ex.dt)
	    {
	    case DT_INTEGER: printf ("%ld\n", ex.V.ival); break;
	    case DT_BOOLEAN: printf ("%s\n", ((ex.V.bval == BOOL_F) ? "#f" : "#t")); break;
	    }
	  memset (buf, 0, 1024);
	  expr_node_free (elist); elist = NULL;
	  printf ("REPL:> ");
	  idx = 0; c = fgetc (stdin);
	}
      else
	{
	  buf[idx] = c;
	  idx++; c = fgetc (stdin);
	}
    }
}

/**/
int
main (int argc, char *argv[])
{
  test_proc ();
  //test_parse ();
  //eval_express ();
  return 0;
}
//-----(not #f)-----//
  • 感觉分成两个文件还不够,找函数还是不太方便,下一步分成三个文件,将测试部分代码再分成一个文件!
  • 目前已经能解析整数、运算符、标识符、还不能解析字符串,下一步完成解析字符串,输出一个Hello world!试试!!!
;