简易计算器(2)- 语法分析(不使用Yacc)

上文展示了简易计算器的词法分析,获得了表达式中的每个记号。本文继续计算器的语法分析。

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;
}

但不支持括号和负数。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值