Проблема с оператором switch в лексическом анализаторе - PullRequest
0 голосов
/ 23 февраля 2019

Я пытаюсь сделать лексический анализатор на C, и я попытался пошагово пройти код, чтобы увидеть, в чем проблема, но я не вижу ее.Этот код читает строку из файла, предполагая, что первая строка и ничего больше не находится в файле.Поэтому я протестировал его с "a = (b + 2) * c".

. Он работает и печатает a, =, но затем ничего.Я понял, что это проблема с оператором switch в моей функции lookup(), потому что он, кажется, работает нормально с чем-то, что не UNKNOWN в lex().Любое понимание будет полезно и ценится.

#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>

int charClass;
char lexeme[100];
char nextChar;
int lexLen;
int token;
int nextToken;
FILE *fp;

void addChar();
void getChar();
void getNonBlank();
int lex();

#define LETTER 0
#define DIGIT 1
#define UNKNOWN 99

#define INT_LIT 10
#define IDENT 11
#define ASSIGN_OP 20
#define ADD_OP 21
#define SUB_OP 22
#define MULT_OP 23
#define DIV_OP 24
#define LEFT_PARENT 25
#define RIGHT_PARENT 26

int main(int argc, char *argv[])
{
    fp = fopen(argv[1], "r");

    if (fopen == NULL)
        printf("File can not be opened");
    else {
        getChar();

        while (nextToken != EOF) {
            lex();
        }
    }
    return 0;
}

int lookup(char ch) {
    switch (ch) {
    case '(':
        addChar();
        nextToken = LEFT_PARENT;
        break;
    case ')':
        addChar();
        nextToken = RIGHT_PARENT;
        break;
    case '+':
        addChar();
        nextToken = ADD_OP;
        break;
    case '-':
       addChar();
        nextToken = SUB_OP;
        break;
    case '*':
        addChar();
        nextToken = MULT_OP;
        break;
    case '/':
        addChar();
        nextToken = DIV_OP;
        break;
    default:
        addChar();
        nextToken = EOF;
    }
    return nextToken;
}

void addChar() {
    if (lexLen <= 98) {
        lexeme[lexLen++] = nextChar;
        lexeme[lexLen] = 0;
    } else
        printf("Error- lexele is too long...\n");
}

void getChar() {
    if ((nextChar = getc(fp)) != EOF) {
        if (isalpha(nextChar))
            charClass = LETTER;
        else if(isdigit(nextChar))
             charClass = DIGIT;
        else
            charClass = UNKNOWN;
    } else
        charClass =EOF;
}

void getNonBlank() {
    while (isspace(nextChar))
        getChar();
}

int lex() {
    lexLen = 0;
    getNonBlank();
    switch (charClass) {
    case LETTER:
        addChar();
        getChar();
        while (charClass == LETTER || charClass == DIGIT) {
            addChar();
            getChar();
        }
        nextToken = IDENT;
        break;
    case DIGIT:
        addChar();
        getChar();
        while (charClass == DIGIT) {
            addChar();
            getChar();
        }
        nextToken = INT_LIT;
        break;
    case UNKNOWN:
        lookup(nextChar);
        getChar();
        break;
    case EOF:
        nextToken = EOF;
        lexeme[0] = 'E';
        lexeme[1] = 'O';
        lexeme[2] = 'F';
        lexeme[3] = 0;
    }
    printf("Next token is :%d, next lexeme is %s\n", nextToken, lexeme);
    return nextToken;
}

1 Ответ

0 голосов
/ 23 февраля 2019

Первое замечание: когда вы получаете символ и хотите сравнить его с EOF, вы должны сохранить символ в int , а не в символе, поэтому

void getChar()
{
    if((nextChar=getc(fp))!=EOF)
        {
        if(isalpha(nextChar))
            charClass=LETTER;
            else if(isdigit(nextChar))
                charClass=DIGIT;
        else
            charClass=UNKNOWN;
        }   
    else
        charClass=EOF;
}

не очень хорошо управляет делом EOF , потому что nextChar является char , может быть:

void getChar()
{
    if((charClass=getc(fp))!=EOF)
        {
      nextChar = charClass;
        if(isalpha(nextChar))
            charClass=LETTER;
            else if(isdigit(nextChar))
                charClass=DIGIT;
        else
            charClass=UNKNOWN;
    }
}

Второе замечание: вы забылиуправляйте делом '=', поэтому я думаю, что в lookup(char ch) вы должны добавить регистр:

case '=':addChar();
       nextToken=ASSIGN_OP;
  break;

, поэтому вы останавливаетесь после прочтения '='.

Если явыполните следующие изменения:

pi@raspberrypi:/tmp $ gcc -g -Wextra q.c
pi@raspberrypi:/tmp $ cat in
a = (b + 2) * c
pi@raspberrypi:/tmp $ ./a.out in
Next token is :11, next lexeme is a
Next token is :20, next lexeme is =
Next token is :25, next lexeme is (
Next token is :11, next lexeme is b
Next token is :21, next lexeme is +
Next token is :10, next lexeme is 2
Next token is :26, next lexeme is )
Next token is :23, next lexeme is *
Next token is :11, next lexeme is c
^C

Я должен завершить выполнение, потому что программа зацикливается, это потому, что в getNonBlank() случае EOF не управляют, поэтому:

void getNonBlank()
{
    while((charClass != EOF) && isspace(nextChar))
        getChar();
}

После этого изменения:

pi@raspberrypi:/tmp $ ./a.out in
Next token is :11, next lexeme is a
Next token is :20, next lexeme is =
Next token is :25, next lexeme is (
Next token is :11, next lexeme is b
Next token is :21, next lexeme is +
Next token is :10, next lexeme is 2
Next token is :26, next lexeme is )
Next token is :23, next lexeme is *
Next token is :11, next lexeme is c
Next token is :-1, next lexeme is EOF

Как говорит chqrlie в замечании, также замените if(fopen == NULL) на if (fp == NULL)

...