阅读《自制编程语言》之递归下降

 
书中举了一个例子来说明语法分析中的下降递归,还是计算器。

0、准备工作

token.h文件

#ifndef TOKEN_H
#define  TOKEN_H
typedef enum {
 BAD_TOKEN,
 NUMBER_TOKEN,
 ADD,
 SUB,
 MUL,
 DIV,
 END
}TokenKind;           // 词的类型
#define MAX_TOKEN_LENG     1000
typedef struct {
 TokenKind kind;
 double value;   // 具体值
 char str[MAX_TOKEN_LENG];  // 原始字符
}Token;      // 词节点的信息
void get_token(Token *token);
#endif // !TOKEN_H

1、词法分析

分割单词,主要是要识别出数值和加减乘除。这个应该比较容易,就直接上代码了。
static char *st_line;        // 读取一行
static int st_line_pos;      // 游标,表示解析到的位置

typedef enum {
 INITIAK_STATUS,          // 数值类型初始的标记
 IN_INT_PART_STATUS,      // 整数
 DOT_STATUS,              // 小数点
 IN_FRAC_PART_STATUS      // 小数
} LexerStatus;
void get_token(Token * token)
{
 char ch;
 int status             = INITIAK_STATUS;
 int token_str_length  = 0;
 token->kind     = BAD_TOKEN;
 while (st_line[st_line_pos] != '\0')    // 未读取完整个字符串
 {
  ch                                             = st_line[st_line_pos];
  if ((status == IN_FRAC_PART_STATUS || status == IN_INT_PART_STATUS) &&
   !isdigit(st_line[st_line_pos]) && st_line[st_line_pos] != '.')
  {
   sscanf(token->str, "%lf", &token->value);           // 确定是数值
   token->kind = NUMBER_TOKEN;
   return;
  }
  if(isspace(st_line[st_line_pos] ))
  {
   if (st_line[st_line_pos] == '\n')     // 读取到换行符
   {
    token->kind = END;
    return;
   }
   st_line_pos++;
   continue;
  }
  
  if (token_str_length >= (MAX_TOKEN_LENG - 1)) /* 如果词的大小等于大于最大的设定大小 */
  {
   printf("word over length");
  }
  token->str[token_str_length] = st_line[st_line_pos];
  token_str_length++;
  st_line_pos++;
  token->str[token_str_length] = '\0';
  
  if (ch == '+')
  {
   token->kind = ADD;
   return;
  }
  else if (ch == '-')
  {
   token->kind = SUB;
   return;
  }
  else if (ch == '*')
  {
   token->kind = MUL;
   return;
  }
  else if (ch == '/')
  {
   token->kind = DIV;
   return;
  }
  else if (isdigit(ch))    // 是数字
  {
   if (status == INITIAK_STATUS)
   {
    status = IN_INT_PART_STATUS;
   }
   else if (status == DOT_STATUS)
   {
    status = IN_FRAC_PART_STATUS;
   }
  }
  else if (ch == '.')
  {
   if (status == IN_INT_PART_STATUS)
   {
    status = DOT_STATUS;
   }
   else
   {
    printf("syntax error\n");       // 非法的输入
    exit(1);
   }
  }
  else
  {
    printf("bad ch\n");       // 非法的输入
    exit(1);
  }
 }
}

void set_line(char *line)
{
 st_line    = line;
 st_line_pos   = 0;
}

2、语法分析
这里使用了递归下降的分析方法。yacc版的计算器曾经使用下面算法


exp    // 表达式规则
:term

|exp ADD term
|exp SUB term

;
term //表达式规则

:primary_exp
|term MUL primary_exp

|term DIV primary_exp
;

primary_exp    // 一元表达式的规则
:DOUBLE_LITERAL

1).每个非终结符构造一个分析函数
2).用前看符号指导产生式规则的选择
static Token st_look_ahead_token;
static int st_look_ahead_token_exists;
static void my_get_token(Token * token)
{
 if (st_look_ahead_token_exists)   /* 退回上一个 */
 {
  *token = st_look_ahead_token;
  st_look_ahead_token_exists = 0;
 }
 else                                 /* 获得新的词 */
 {
  get_token(token);
 }
}
static void unget_token(Token *token)
{
 st_look_ahead_token = *token;
 st_look_ahead_token_exists = 1;
}
static double parse_primary_expression()
{
 Token token;
 my_get_token(&token);
 if(token.kind == NUMBER_TOKEN)
 {
  return token.value;
 }
 fprintf(stderr, "syntax error");
 exit(1);
 return 0.0;
}
static double parse_term()
{
 double v1,v2;
 Token token;
 v1 = parse_primary_expression();
 do
 {
  my_get_token(&token);
  if (token.kind != MUL && token.kind != DIV)  // 非乘除
  {
   unget_token(&token);
   break;
  }
  v2 = parse_primary_expression();   /* 获得乘除的词 */
  if (token.kind == MUL) 
  {
   v1 *= v2;
  }
  else if (token.kind == DIV)
  {
   v1 /= v2;
  }
 }while (1);
 return v1;
}
 double parse_expression()
{
 double v1, v2;
 Token token;
 v1 = parse_term();
 do
 {
  my_get_token(&token);
  if (token.kind != ADD && token.kind != SUB)  // 非加减
  {
   unget_token(&token);
   break;
  }
  v2 = parse_term();   /* 获得加减的词 */
  if (token.kind == ADD)
  {
   v1 += v2;
  }
  else if (token.kind == SUB)
  {
   v1 -= v2;
  }
  else
  {
   unget_token(&token);
  }
 } while (1);
 return v1;
}
 double parse_line()
 {
  double value;
  st_look_ahead_token_exists = 0;
  value                      = parse_expression();
  return value;
 }

3、整合代码 文件analyse.c
#include<stdlib.h>
#include<ctype.h>
#include"token.h"
#include<stdio.h>
static char *st_line;        // 读取一行
static int st_line_pos;      // 游标,表示解析到的位置
static Token st_look_ahead_token;
static int st_look_ahead_token_exists;
static void my_get_token(Token * token)
{
 if (st_look_ahead_token_exists)   /* 退回上一个 */
 {
  *token = st_look_ahead_token;
  st_look_ahead_token_exists = 0;
 }
 else                                 /* 获得新的词 */
 {
  get_token(token);
 }
}
static void unget_token(Token *token)
{
 st_look_ahead_token = *token;
 st_look_ahead_token_exists = 1;
}
static double parse_primary_expression()
{
 Token token;
 my_get_token(&token);
 if(token.kind == NUMBER_TOKEN)
 {
  return token.value;
 }
 fprintf(stderr, "syntax error");
 exit(1);
 return 0.0;
}
static double parse_term()
{
 double v1,v2;
 Token token;
 v1 = parse_primary_expression();
 do
 {
  my_get_token(&token);
  if (token.kind != MUL && token.kind != DIV)  // 非乘除
  {
   unget_token(&token);
   break;
  }
  v2 = parse_primary_expression();   /* 获得乘除的词 */
  if (token.kind == MUL) 
  {
   v1 *= v2;
  }
  else if (token.kind == DIV)
  {
   v1 /= v2;
  }
 }while (1);
 return v1;
}
 double parse_expression()
{
 double v1, v2;
 Token token;
 v1 = parse_term();
 do
 {
  my_get_token(&token);
  if (token.kind != ADD && token.kind != SUB)  // 非加减
  {
   unget_token(&token);
   break;
  }
  v2 = parse_term();   /* 获得加减的词 */
  if (token.kind == ADD)
  {
   v1 += v2;
  }
  else if (token.kind == SUB)
  {
   v1 -= v2;
  }
  else
  {
   unget_token(&token);
  }
 } while (1);
 return v1;
}
 double parse_line()
 {
  double value;
  st_look_ahead_token_exists = 0;
  value                      = parse_expression();
  return value;
 }
typedef enum {
 INITIAK_STATUS,          // 数值类型初始的标记
 IN_INT_PART_STATUS,      // 整数
 DOT_STATUS,              // 小数点
 IN_FRAC_PART_STATUS      // 小数
} LexerStatus;
void get_token(Token * token)
{
 char ch;
 int status = INITIAK_STATUS;
 int token_str_length = 0;
 token->kind = BAD_TOKEN;
 while (st_line[st_line_pos] != '\0')    // 未读取完整个字符串
 {
  ch = st_line[st_line_pos];
  if ((status == IN_FRAC_PART_STATUS || status == IN_INT_PART_STATUS) &&
   !isdigit(st_line[st_line_pos]) && st_line[st_line_pos] != '.')
  {
   sscanf(token->str, "%lf", &token->value);           // 确定是数值
   token->kind = NUMBER_TOKEN;
   return;
  }
  if (isspace(st_line[st_line_pos]))
  {
   if (st_line[st_line_pos] == '\n')     // 读取到换行符
   {
    token->kind = END;
    return;
   }
   st_line_pos++;
   continue;
  }
  if (token_str_length >= (MAX_TOKEN_LENG - 1)) /* 如果词的大小等于大于最大的设定大小 */
  {
   printf("word over length");
  }
  token->str[token_str_length] = st_line[st_line_pos];
  token_str_length++;
  st_line_pos++;
  token->str[token_str_length] = '\0';
  if (ch == '+')
  {
   token->kind = ADD;
   return;
  }
  else if (ch == '-')
  {
   token->kind = SUB;
   return;
  }
  else if (ch == '*')
  {
   token->kind = MUL;
   return;
  }
  else if (ch == '/')
  {
   token->kind = DIV;
   return;
  }
  else if (isdigit(ch))    // 是数字
  {
   if (status == INITIAK_STATUS)
   {
    status = IN_INT_PART_STATUS;
   }
   else if (status == DOT_STATUS)
   {
    status = IN_FRAC_PART_STATUS;
   }
  }
  else if (ch == '.')
  {
   if (status == IN_INT_PART_STATUS)
   {
    status = DOT_STATUS;
   }
   else
   {
    printf("syntax error\n");       // 非法的输入
    exit(1);
   }
  }
  else
  {
   printf("bad ch\n");       // 非法的输入
   exit(1);
  }
 }
}

void set_line(char *line)
{
 st_line = line;
 st_line_pos = 0;
}

int main()
{
 char line[1024];
 double value;
 while (fgets(line, 1024, stdin) != NULL)
 {
  set_line(line);
  value = parse_line();
  printf(">>%f\n", value);
 }
 return 0;
}
——————————————————————我是分界线———————————————

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值