上文展示了简易计算器的词法分析,获得了表达式中的每个记号。本文继续计算器的语法分析。
parser.h
#ifndef _PARSER_H
#define _PARSER_H
#include <stdio.h>
#include <stdlib.h>
#include "lex.h"
#define LINE_BUF_SIZE (1024)
double ParseLine();
double ParseExp();
#endif
parser.c
#include "parser.h"
static Token stLookAheadTok;
static int stLookAheadTokExist;
static void MyGetToken(Token *tok)
{
if (stLookAheadTokExist) {
*tok = stLookAheadTok;
stLookAheadTokExist = 0;
} else {
GetToken(tok);
}
}
static void UngetToken(Token *tok)
{
stLookAheadTok = *tok;
stLookAheadTokExist = 1;
}
static double ParsePrimaryExp()
{
Token tok;
MyGetToken(&tok);
if (tok.kind == TOKEN_NUM) {
return tok.value;
}
fprintf(stderr, "syntax error. \n");
exit(1);
return 0.0; // make compiler happy
}
static double ParseTerm()
{
double v1;
double v2;
Token tok;
v1 = ParsePrimaryExp();
for (;;) {
MyGetToken(&tok);
if (tok.kind != TOKEN_OP_MUL
&& tok.kind != TOKEN_OP_DIV) {
UngetToken(&tok);
break;
}
v2 = ParsePrimaryExp();
if (tok.kind == TOKEN_OP_MUL) {
v1 *= v2;
} else if (tok.kind == TOKEN_OP_DIV) {
v1 /= v2;
}
}
return v1;
}
double ParseExp()
{
double v1;
double v2;
Token tok;
v1 = ParseTerm();
for (;;) {
MyGetToken(&tok);
if (tok.kind != TOKEN_OP_ADD
&& tok.kind != TOKEN_OP_SUB) {
UngetToken(&tok);
break;
}
v2 = ParseTerm();
if (tok.kind == TOKEN_OP_ADD) {
v1 += v2;
} else if (tok.kind == TOKEN_OP_SUB) {
v1 -= v2;
} else {
UngetToken(&tok);
}
}
return v1;
}
double ParseLine()
{
double val;
stLookAheadTokExist = 0;
val = ParseExp();
return val;
}
加上上文的词法分析Code:
lex.h
#ifndef _LEX_H
#define _LEX_H
typedef enum {
TOKEN_BAD,
TOKEN_NUM,
TOKEN_OP_ADD,
TOKEN_OP_SUB,
TOKEN_OP_MUL,
TOKEN_OP_DIV,
TOKEN_LINE_END,
} TokenKind;
#define MAX_TOKEN_SIZE (100)
typedef struct {
TokenKind kind;
double value;
char str[MAX_TOKEN_SIZE];
} Token;
void SetLine(char *file);
void GetToken(Token *token);
#endif
lex.c
#include "lex.h"
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
static char *sLine;
static int sLinePos;
typedef enum {
INIT_ST,
INT_PART_ST,
DOT_ST,
FRAC_PART_ST,
} LexerStatus;
void GetToken(Token *token)
{
int pos = 0;
LexerStatus st = INIT_ST;
char currChar;
token->kind = TOKEN_BAD;
while (sLine[sLinePos] != '\0') {
currChar = sLine[sLinePos];
// extract number
if ((st == INT_PART_ST || st == FRAC_PART_ST)
&& !isdigit(currChar) && currChar != '.') {
token->kind = TOKEN_NUM;
sscanf(token->str, "%lf", &token->value);
return ;
}
// skip space
if (isspace(currChar)) {
if (currChar == '\n') {
token->kind = TOKEN_LINE_END;
return ;
}
sLinePos++;
continue;
}
if (sLinePos >= MAX_TOKEN_SIZE - 1) {
fprintf(stderr, "token too long \n");
}
token->str[pos] = sLine[sLinePos];
sLinePos++;
pos++;
token->str[pos] = '\0';
if (currChar == '+')
{
token->kind = TOKEN_OP_ADD;
return ;
} else if (currChar == '-') {
token->kind = TOKEN_OP_SUB;
return ;
} else if (currChar == '*') {
token->kind = TOKEN_OP_MUL;
return ;
} else if (currChar == '/') {
token->kind = TOKEN_OP_DIV;
return ;
} else if (isdigit(currChar)) {
if (st == INIT_ST) {
st = INT_PART_ST;
} else if (st == DOT_ST) {
st = FRAC_PART_ST;
}
} else if (currChar == '.') {
if (st == INT_PART_ST) {
st = DOT_ST;
} else {
fprintf(stderr, "syntax error. \n");
exit(1);
}
} else {
fprintf(stderr, "bad charactor(%c)\n", currChar);
exit(1);
}
}
}
void SetLine(char *line)
{
sLine = line;
sLinePos = 0;
}
// test code
// void ParseLine(char *buf)
// {
// Token token;
// SetLine(buf);
// while (1) {
// GetToken(&token);
// if (token.kind == TOKEN_LINE_END) {
// break;
// } else {
// printf("kind : %d -- %s \n", token.kind, token.str);
// }
// }
// }
// int main(int argc, char **argv)
// {
// char buf[1024];
// while (fgets(buf, 1024, stdin) != NULL) {
// printf("Parse String: %s", buf);
// ParseLine(buf);
// }
// return 0;
// }
main.c
#include "parser.h"
int main(int argc, char **argv)
{
char line[LINE_BUF_SIZE];
double val;
while (fgets(line, LINE_BUF_SIZE, stdin) != NULL) {
SetLine(line);
val = ParseLine();
printf(">>%f\n", val);
}
return 0;
}
但不支持括号和负数。