书中举了一个例子来说明语法分析中的下降递归,还是计算器。
0、准备工作
token.h文件
#ifndef TOKEN_H
#define TOKEN_H
typedef enum {
BAD_TOKEN,
NUMBER_TOKEN,
ADD,
SUB,
MUL,
DIV,
END
}TokenKind; // 词的类型
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; // 词节点的信息
TokenKind kind;
double value; // 具体值
char str[MAX_TOKEN_LENG]; // 原始字符
}Token; // 词节点的信息
void get_token(Token *token);
#endif // !TOKEN_H
#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;
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;
{
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");
}
{
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);
}
}
}
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).用前看符号指导产生式规则的选择
2).用前看符号指导产生式规则的选择
static Token st_look_ahead_token;
static int st_look_ahead_token_exists;
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);
}
}
{
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;
}
{
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;
}
{
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 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;
}
{
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;
#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);
}
}
{
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;
}
{
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;
}
{
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 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;
}
{
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;
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;
{
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;
}
{
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");
}
{
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';
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);
}
}
}
{
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);
}
{
char line[1024];
double value;
while (fgets(line, 1024, stdin) != NULL)
{
set_line(line);
value = parse_line();
printf(">>%f\n", value);
}
return 0;
}
}
——————————————————————我是分界线———————————————