2011-08-10 3 views
2

연습 문제는 "C 프로그램에서 불균형 괄호, 대괄호 및 중괄호와 같은 기본 구문 오류가 있는지 프로그램을 작성하십시오. 단일 및 이중 따옴표, 이스케이프 시퀀스 및 주석. " K & R 연습 문제 1-24 - 기본 구문 검사

내가 스택에 괄호, 대괄호, 중괄호를 넣고 확인 모든함으로써 문제를 해결하는 이동하기로 결정했습니다 LIFO 등, 우리는 주석, 인용에있어 여부

마킹을위한 다양한 카운터와 함께했다

문제는 내 코드가 제대로 작동하지만 구조가 제대로되어 있지 않으며 특히 관용적이지 않다는 느낌이 들기 때문입니다. 구조체 내에서 상태 변수 (스택, escaped, inString 등)를 구현하고 테스트를 서브 루틴으로 분해하려고 시도했습니다. 그것은별로 도움이되지 못했습니다. 이스케이프 된 문자 등을 올바르게 처리하면서이 문제를 더욱 명확하게 해결할 수있는 방법이 있습니까?

#include <stdio.h> 
#include <stdlib.h> 
#define INITIALSTACK 8 
#define FALSE 0 
#define TRUE 1 

typedef struct { 
    int position; 
    int maxLength; 
    char* array; 
} stack; 

int match(char, char); 

stack create(); 
void delete(stack*); 
void push(stack*, char); 
char pop(stack*); 

int main() { 
    char c, out; 
    stack elemStack = create(); 

    int escaped, inString, inChar, inComment, startComment, i, lineNum; 
    int returnValue; 

    escaped = inString = inChar = inComment = startComment = 0; 
    lineNum = 1; 

    while ((c = getchar()) != EOF) { 
    if (c == '\n') 
     lineNum++; 

    /* Test if in escaped state or for escape character */ 
    if (escaped) { 
     escaped = FALSE; 
    } 
    else if (c == '\\') { 
     escaped = TRUE; 
    } 

    /* Test if currently in double/single quote or a comment */ 
    else if (inString) { 
     if (c == '"' && !escaped) { 
     inString = FALSE; 
     } 
    } 
    else if (inChar) { 
     if (escaped) 
     escaped = FALSE; 
     else if (c == '\'' && !escaped) { 
     inChar = FALSE; 
     } 
    } 
    else if (inComment) { 
     if (c == '*') 
     startComment = TRUE; 
     else if (c == '/' && startComment) 
     inComment = FALSE; 
     else 
     startComment = FALSE; 
    } 

    /* Test if we should be starting a comment, quote, or escaped character */ 
    else if (c == '*' && startComment) 
     inComment = TRUE; 
    else if (c == '/') 
     startComment = TRUE; 
    else if (c == '"') { 
     inString = TRUE; 
    } 
    else if (c == '\'') { 
     inChar = TRUE; 
    } 

    /* Accept the character and check braces on the stack */ 
    else { 
     startComment = FALSE; 

     if (c == '(' || c == '[' || c == '{') 
     push(&elemStack, c); 
     else if (c == ')' || c == ']' || c == '}') { 
     out = pop(&elemStack); 
     if (out == -1 || !match(out, c)) { 
      printf("Syntax error on line %d: %c matched with %c\n.", lineNum, out, c); 
      return -1; 
     } 
     } 
    } 
    } 

    if (inString || inChar) { 
    printf("Syntax error: Quote not terminated by end of file.\n"); 
    returnValue = -1; 
    } 
    else if (!elemStack.position) { 
    printf("Syntax check passed on %d line(s).\n", lineNum); 
    returnValue = 0; 
    } 
    else { 
    printf("Syntax error: Reached end of file with %d unmatched elements.\n ", 
      elemStack.position); 
    for(i = 0; i < elemStack.position; ++i) 
     printf(" %c", elemStack.array[i]); 
    printf("\n"); 
    returnValue = -1; 
    } 

    delete(&elemStack); 
    return returnValue; 
} 

int match(char left, char right) { 
    return ((left == '{' && right == '}') || 
      (left == '(' && right == ')') || 
      (left == '[' && right == ']')); 
} 

stack create() { 
    stack newStack; 
    newStack.array = malloc(INITIALSTACK * sizeof(char)); 
    newStack.maxLength = INITIALSTACK; 
    newStack.position = 0; 
    return newStack; 
} 

void delete(stack* stack) { 
    free(stack -> array); 
    stack -> array = NULL; 
} 

void push(stack* stack, char elem) { 
    if (stack -> position >= stack -> maxLength) { 
    char* newArray = malloc(2 * (stack -> maxLength) * sizeof(char)); 
    int i; 

    for (i = 0; i < stack -> maxLength; ++i) 
     newArray[i] = stack -> array[i]; 

    free(stack -> array); 
    stack -> array = newArray; 
    } 

    stack -> array[stack -> position] = elem; 
    (stack -> position)++; 
} 

char pop(stack* stack) { 
    if (!(stack -> position)) { 
    printf("Pop attempted on empty stack.\n"); 
    return -1; 
    } 
    else { 
    (stack -> position)--; 
    return stack -> array[stack -> position]; 
    } 
} 

답변

3

당신의 해결책은 그렇게 나쁘지 않습니다. 그것은 매우 똑바르다. 그것은 좋은 것이다. 이 훈련에서 좀 더 배우려면 상태 머신으로 이것을 구현할 것입니다. 예 : 당신은 다음과 같은 몇 가지 상태를 가지고 있습니다 : code, comment, string 등등. 그러면 그들 사이에 전환을 정의 할 수 있습니다. 상태에 따라 논리로 끝나기 때문에 훨씬 더 쉽게 얻을 수 있습니다 (그래서 주 기능과 같이 코드 블롭이 없습니다). 그런 다음 상태에 따라 코드를 구문 분석 할 수 있습니다. 예를 들어, 주석 상태에있는 경우 끝 주석 문자가 나타날 때까지 모든 것을 무시합니다. 그런 다음 상태를 code으로 변경하는 등의 작업을 수행합니다.

의사 코드에서는이 같은 볼 수 있었다 : 이것은 예를 들어, 수행 할 수 있습니다 물론

current_state = CODE 

while(...) { 

    switch(current_state) { 
     case CODE: 
     if(input == COMMENT_START) { 
      current_state = COMMENT 
      break 
     } 

     if(input == STRING_START) { 
      current_state = STRING 
      break 
     } 

     // handle your {, [, (stuff... 

     break 

     case COMMENT: 
     if(input == COMMENT_END) { 
      current_state = CODE 
      break 
     } 

     // handle comment.. i.e. ignore everything 

     break 
     case STRING: 
     // ... string stuff like above with state transitions.. 
     break 
    } 

} 

yacc. 그러나 내가 코멘트에 언급했듯이, 당신이 그것을 사용하도록 제안하지는 않을 것입니다. 어쩌면 당신은 충분한 시간을 갖고 가능한 한 많이 배우고 싶다면 그렇게 할 수 있습니다.하지만 먼저 그것을 "어려운 방법"으로 구현할 것입니다.

+0

감사합니다. 계속해서 상태 변수를 상태 시스템의 한 형태로 추상화 해 봅니다. –

2

나는 아마 lex처럼 렉서 발전기와 결합 yacc처럼, 파서 생성기를 이용하여 매우 다르게이 접근 것이다.

이러한 도구의 기존 입력 파일 인 ANSI C를 기반으로 할 수 있습니다. lex specificationyacc grammar 예. 출발점이 될 수 있습니다. 또는 K & R에는 부록 A에있는 yacc 호환 C 문법이 포함되어 있거나 물론 C 표준의 문법으로 직접 작업 할 수도 있습니다.

이 연습에서는 관심있는 부분 만 문법에 사용하고 나머지는 무시합니다. 문법은 문법이 정확하다는 것을 보증 할 것이고 (모든 중괄호는 일치된다.) lex/yacc는 모든 코드 생성을 처리 할 것이다. 따라서 접착제 코드를 지정하지 않아도됩니다.이 코드는 대부분이 경우 오류 메시지입니다.

코드를 완전히 다시 작성하지만 C 문법을 더 잘 이해할 수 있으며, 적어도 lex/yacc라는 훌륭한 도구로 작업하는 방법을 배웠을 것입니다. 결코 상처를 입지 않습니다.

+2

본인은 의견을 말하지 않습니다. IMHO 이것은 lex/yacc을 배우기위한 훈련이 아닙니다. 해결책은 문제를 해결하는 좋은/빠른 방법이지만 운동에는 적합하지 않습니다. 나는 lex/yacc가 아니라 C를 배우는 것이라고 생각한다. 그리고 당신의 관심사가 가장 쉬운 방법으로 해결된다면 gcc를 사용하여 컴파일하고 확인하십시오. 분명히 이것은이 훈련의 핵심은 아닙니다. – duedl0r

+0

@ duedl0r : 공정한 지적.C 코드를 작성하는 것보다 C 문법에 익숙해지는 것에 대해 더 많이 연습한다고 생각합니다 (C 텍스트를 구문 분석하는 일부 텍스트를 작성하는 경우 C 문법을 사용할 필요가 없습니다). 따라서 나의 접근법은 운동의 문법 측면에 초점을 맞 춥니 다. 자신의 yacc 문법을 작성하는 것이 사전에 만들어진 yacc 문법을 사용하는 것보다 더 많이 가르쳐 주겠지 만 동의 할 것입니다. –