C K&;的练习1-24;R-基本语法检查

C K&;的练习1-24;R-基本语法检查,c,coding-style,kr-c,C,Coding Style,Kr C,练习内容为“编写一个程序,检查C程序是否存在基本语法错误,如不平衡的括号、方括号和大括号。不要忘记单引号和双引号、转义序列和注释。” 我选择通过在堆栈上放置括号、方括号和大括号来解决问题,并确保所有内容都是后进先出,以及各种计数器,用于标记我们是否在评论、引用等中 问题是,我觉得我的代码虽然可以工作,但结构很差,不是特别地道。我尝试在结构中实现状态变量(堆栈、转义、插入,等等),并将测试分解为子例程。这没有多大帮助。有没有一种方法可以在正确处理转义字符等的同时以更干净的方式解决此问题 #incl

练习内容为“编写一个程序,检查C程序是否存在基本语法错误,如不平衡的括号、方括号和大括号。不要忘记单引号和双引号、转义序列和注释。”

我选择通过在堆栈上放置括号、方括号和大括号来解决问题,并确保所有内容都是后进先出,以及各种计数器,用于标记我们是否在评论、引用等中

问题是,我觉得我的代码虽然可以工作,但结构很差,不是特别地道。我尝试在结构中实现状态变量(堆栈、
转义
插入
,等等),并将测试分解为子例程。这没有多大帮助。有没有一种方法可以在正确处理转义字符等的同时以更干净的方式解决此问题

#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];
  }
}
#包括
#包括
#定义初始堆栈8
#定义FALSE 0
#定义真1
类型定义结构{
内部位置;
int最大长度;
字符*数组;
}堆叠;
int匹配(char,char);
堆栈创建();
作废删除(堆栈*);
无效推送(堆栈*,字符);
字符pop(堆栈*);
int main(){
字符c,out;
stack elemStack=create();
int转义、inString、inChar、inComment、startComment、i、lineNum;
返回值;
逸出=仪表=英寸=不协调=起始电流=0;
lineNum=1;
而((c=getchar())!=EOF){
如果(c=='\n')
lineNum++;
/*测试是否处于转义状态或转义字符*/
如果(转义){
逃逸=假;
}
else if(c=='\\'){
逃逸=真;
}
/*测试当前是否在双引号/单引号或注释中*/
否则,如果(安装){
如果(c==“”&&&!转义){
inString=FALSE;
}
}
否则,如果(英寸){
如果(转义)
逃逸=假;
else if(c=='\''&&!转义){
英寸=假;
}
}
否则,如果(不一致){
如果(c=='*')
startComment=真;
else if(c=='/'&&startComment)
不一致=错误;
其他的
startComment=FALSE;
}
/*测试是否应该开始注释、引用或转义字符*/
else if(c=='*'&&startComment)
不协调=正确;
else如果(c=='/'))
startComment=真;
else如果(c==“”){
inString=TRUE;
}
else if(c=='\''){
英寸=真;
}
/*接受字符并检查堆栈上的大括号*/
否则{
startComment=FALSE;
如果(c=='('| | c=='['| | c=='{')
推送(和elemStack,c);
else如果(c=')'| | c=']'| | c='}'){
out=弹出(&elemStack);
如果(out==-1 | |!匹配(out,c)){
printf(“第%d行的语法错误:%c与%c匹配\n.”,lineNum,out,c);
返回-1;
}
}
}
}
if(仪表| |英寸){
printf(“语法错误:引号未在文件末尾终止。\n”);
返回值=-1;
}
如果(!elemStack.position),则为else{
printf(“语法检查在%d行上通过。\n”,lineNum);
返回值=0;
}
否则{
printf(“语法错误:到达文件末尾时有%d个不匹配的元素。\n”,
elemStack.位置);
对于(i=0;i数组);
堆栈->数组=NULL;
}
无效推送(堆栈*堆栈,字符元素){
如果(堆栈->位置>=堆栈->最大长度){
char*newArray=malloc(2*(堆栈->最大长度)*sizeof(char));
int i;
对于(i=0;imaxLength;++i)
newArray[i]=堆栈->数组[i];
自由(堆栈->数组);
堆栈->数组=新数组;
}
堆栈->数组[堆栈->位置]=元素;
(堆栈->位置)+;
}
字符弹出(堆栈*堆栈){
如果(!(堆栈->位置)){
printf(“在空堆栈上尝试弹出。\n”);
返回-1;
}
否则{
(堆栈->位置)--;
返回堆栈->数组[堆栈->位置];
}
}

我可能会以完全不同的方式来处理这个问题,使用解析器生成器,比如,结合lexer生成器,比如

对于ANSI C,您可以基于这些工具的现有输入文件。这和例如可以作为一个起点。或者,在附录a中,K&R也包含一个与yacc兼容的C语法,或者您当然可以直接使用C标准中的语法

在本练习中,您将只使用您感兴趣的语法部分,而忽略其余部分。语法将确保语法正确(所有大括号匹配等),lex/yacc将负责所有代码生成。这使得您只需指定一些粘合代码,在本例中,这些代码主要是错误消息


这将是对代码的完全重写,但可能会让您更好地理解C语法,而且至少您已经学会了使用伟大的工具lex/yacc,这不会有什么坏处。

您的解决方案没有那么糟糕。这很直截了当,这是一件好事。为了从这个练习中学到更多,我可能会用状态机实现它。例如,您有几种状态,如:
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
   }

}