欢迎来到 嗅灵易学

零基础也能上手的脚本技术课,一对一答疑带你入门

更新6-28

更新6-28

下面有一段代码,我们的目标是把这段代码翻译成机器码,首先把它分解成记号:
int ig;
void Fun(int ix,int iy)
{
  if(ix>iy)
  {
    if(10==iy)
    {
      iy=ix+ig;
    }
    ix=iy;
  }
}
void Fun2()
{
}
分解成的记号如下:

 
接下来我们要分析这些记号了.
看上面的源码,我用一块灰布把函数体挡住了,也就是说看不到函数体了,那就假装它不存在.发现只有变量定义和函数头,如果想支持更多的话可能还有”define”,”include”,结构体定义...但是决不可能有表达式,if之类的.接下来就开始分析可见部分:
BOOL Parse()
{
  _TOKEN  *pEnd=NULL;
  for(_TOKEN *p=”记号”表头指针;p!=NULL;p=p->pNext){
    if(p->iTTypex==TTEX_DT){// 数据类型
      if(VarDefine(p,&pEnd)==TRUE){// 是变量定义吗?
        p=pEnd; continue;
}
if(FunDeclare(p,&pEnd)==TRUE){// 是函数申明吗?
  p=pEnd; continue;
}
if(FunDefine(p,&pEnd)==TRUE){// 是函数定义吗?
  p=pEnd; continue;
}
...
return FALSE;
}
else if(...){...}
else return FALSE;
}
return TRUE;
}
VarDefine,FunDeclare,FunDefine三个函数都没具体实现,但是它们的意图是很明显的,就拿VarDefine来说,从p指向的记号节点开始分析,如果确实是变量定义就将变量定义的结束记号也就是”;”的指针填写在pEnd中,并且返回TRUE.如果不是变量定义返回FALSE,把机会让给FunDeclare函数...
注:
1.我总结了某些书看的累的原因:战线拉的太长.读者遇到一个问题可能要看十几页甚至几十页才能找到答案,是跳过去找答案还是继续往下看?如果跳过去,找到了答案,但是往往又有新的问题或者其他原因导致你找答案也不应看懂,只好来来回回只到把该熟悉的都熟悉,该记住的都记住了才能看懂.在没看懂的时候还有可能种种原因不能继续看下去,第2天再看的时候发现前面有东西忘记了,这种书你必须长期看下去了.看到熟透了,基本上才有看懂的希望.
在实现VarDefine等函数之前要明确一下目标:把”记号”翻译成中间代码,取名为”记号码”,用链表存放.下面是”记号码”表的节点定义:
struct _TCODE
{
  int    iTCType;  // 记号码类型
  union        // 可选成员
{
    char *pFunName; // 函数名(翻译成机器码的时候需要)
    _EXP *pExp;    // 表达式链表头指针(先不管它)
}Option;
_TCODE  *pNext;    // 下一个记号码
};
“记号码”的种类也是有限的,所以也用宏来表示它的类型.
#define    TCT_FUN_ENTRY      1  // 函数入口
#define    TCT_FUN_EXIT      2  // 函数出口
#define    TCT_IF_CONDITION    3  // if条件
#define    TCT_ELSE_IF_CONDITION  4  // else if条件
#define    TCT_ELSE        5  // else
#define    TCT_IF_END        6  // if结束
#define    TCT_ELSE_IF_END      7  // else if结束
#define    TCT_ELSE_END      8  // else 结束
#define    TCT_LOOP_ENTRY      9  // 循环入口
#define    TCT_LOOP_CONDITION    10 // 循环条件
#define    TCT_LOOP_EXTI      11 // 循环出口
#define    TCT_BREAK        12 // break
#define    TCT_CONTINUE      13 // continue
#define    TCT_JMP_LOOP_ENTRY    14 // 跳转到循环入口
#define    TCT_JMP_LOOP_EXIT    15 // 跳转到循环出口
#define    TCT_JMP_ELSE_END    16 // 跳转到else结束
现在就来实现VarDefine等函数,挑选FunDefine函数来实现:
BOOL FunDefine(_TOKEN *pSt,_TOKEN **pEnd)
{
_TOKEN *pEnd=NULL;
_TCODE *pTcode=NULL;
  int iRetype=pSt->iTType; // 函数返回值类型
  int iRetgrade=0; // 函数返回值指针层数,比如这种函数:int ******Fun()...
  int iCall=TT_STDCALL; // 函数调用约定,让他默认为_stdcall
  char *pName=NULL;
  for(_TOKEN *p=pSt;p->iTType==TT_PS;p=p->pNext) iRetgrade++;//TT_PS:”*”
  if(p->iTTypex==TTEX_CALL){iCall=p->iTType; p=p->pNext;}//如果有调用约定,取之
  if(p->iTType==TT_NAME){
    pName=new char[strlen(p->pName)+1];
    strcpy(pName,p->pName);
    p=p->pNext;
  }else return FLASE;
  if(p->iTType==TT_LPAR) p=p->pNext; else {delete []pName;return FALSE;}
  pTcode=AddTcode(&记号码头指针,TCT_FUN_ENTRY,pName); // 添加记号码
if(GetParam(p,pTcode,&pEnd)==FALSE){
delete[]pName;returnFALSE;}else p=p->pNext;
  if(Match(p,pName,&pEnd)==FALSE){delete []pName; return FALSE;}
  AddTcode(&记号码头指针,TCT_FUN_EXIT,pName); // 添加记号码
  delete []pName;
return TRUE;
}
注:
1.  这种分析方法很山寨,不过容易理解,先通过这种分析法来了解总体思路,正则表达式方法后面再说
2.  代码写的太挤可读性差,只是是希望能在一页之内写完,遇到问题查找也快
3.  看上对错误检查不够仔细,某些错误是可以留到后面的,最主要的是指针是否为NULL没有检查,没关系,山寨版将会用异常处理中报错然后修改指令指针

注意:上传附件及图片大小不得大于30M。

⚠️ 版权声明:
本博客所有内容(含教程、源码、工具)仅供个人技术学习与研究交流使用,严禁商用、倒卖、二次分发及非法用途。
未经作者书面授权,任何组织或个人不得转载、复制或用于其他平台,违者将追究相关责任。

0 0 0 举报
复制成功