diff options
Diffstat (limited to 'ch1/1-24_syntax-checker.c')
-rw-r--r-- | ch1/1-24_syntax-checker.c | 163 |
1 files changed, 163 insertions, 0 deletions
diff --git a/ch1/1-24_syntax-checker.c b/ch1/1-24_syntax-checker.c new file mode 100644 index 0000000..019b9bc --- /dev/null +++ b/ch1/1-24_syntax-checker.c @@ -0,0 +1,163 @@ +#include <stdio.h> + +/* The C Programming Language: 2nd Edition + * Exercise 1-24: + * "Write a program to check a C program for rudimentary syntax errors like + * unbalanced parentheses, brackets, and braces. Don't forget about quotes, both + * single and double, escape sequences, and comments. (This program is hard if + * you do it in full generality.)" + * + * Proksima from Freenode's ##c helped me understand full generality where + * Ixquick, Wikipedia, and StackOverflow all failed: a program that has full + * generality handles all use cases. In this case, my program should report no + * errors from a well formed C source file, and return correct errors for every + * non-valid C source file. + * + * I can tackle this one the same way I tackled the previous exercise: with a + * FSM. The trick is in catching the mismatched levels. + * + * Post-solution note: switch() would've made this MUCH shorter... + */ + +char c; + +/* Set the state/count variables */ +int linenr, parens, brackets, braces, singqs, dubqs, escapes, mcomms, scomms = 0; + +int main() { + linenr = 1; + // Begin streaming! + while ((c = getchar()) != EOF) { + if (scomms == 1) { + if (c == '\n') { + scomms--; + } else { + continue; + } + } else if (mcomms == 1) { + if (c == '*') { + if (getchar() == '/') { + mcomms--; + } + } + } else { + // Check for escape sequences + if (c == '\\') { + escapes++; + c = getchar(); + // This does not detect all sequences; just the ones covered in Chapter 1. + if (c != '\\' && c != 't' && c != '\'' && c != '"' && c != 'n' && c != 'b' && c != '0') { + break; + } else { + escapes--; + } + } + // Newline behavior + if (c == '\n') { + if (singqs > 0 || dubqs > 0) { + break; + } + linenr++; + } + // Parentheses + if (c == '(') { + parens++; + } + if (c == ')') { + parens -= 1; + if (parens < 0) { + break; + } + } + // Brackets + if (c == '[') { + brackets++; + } + if (c == ']') { + brackets--; + if (brackets < 0) { + break; + } + } + // Braces + if (c == '{') { + braces++; + } + if (c == '}') { + braces--; + if (braces < 0) { + break; + } + } + // Double quotes + if (c == '"') { + if (dubqs == 0) { + dubqs++; + } else { + dubqs--; + } + } + // Single quotes, which are only checked outside of doubles. + if (c == '\'' && dubqs == 0) { + if (singqs == 0) { + singqs++; + } else { + singqs--; + } + } + // Comment detection + if (c == '/') { + if (getchar() == '*') { + mcomms++; + } + if (getchar() == '/') { + scomms++; + } + } + } + } + + if (escapes > 0) { + printf("Invalid escape sequence on line %d!\n", linenr); + return 1; + } + if (singqs > 0) { + printf("Unclosed single quote on line %d!\n", linenr); + return 1; + } + if (dubqs > 0) { + printf("Unclosed double quote on line %d!\n", linenr); + return 1; + } + if (brackets > 0) { + printf("Unclosed brackets on line %d!\n", linenr); + return 1; + } + if (brackets < 0) { + printf("Too many close brackets on line %d!\n", linenr); + return 1; + } + if (parens > 0) { + printf("Unclosed parenthesis on line %d!\n", linenr); + return 1; + } + if (parens < 0) { + printf("Too many close parentheses on line %d!\n", linenr); + return 1; + } + if (braces > 0) { + printf("Unclosed braces on line %d!\n", linenr); + return 1; + } + if (braces < 0) { + printf("Too many close braces on line %d!\n", linenr); + return 1; + } + if (mcomms == 1) { + printf("Unclosed comment at end of file!\n"); + return 1; + } + + printf("All clean.\n"); + return 0; +} |