aboutsummaryrefslogtreecommitdiff
path: root/ch1/1-24_syntax-checker.c
diff options
context:
space:
mode:
Diffstat (limited to 'ch1/1-24_syntax-checker.c')
-rw-r--r--ch1/1-24_syntax-checker.c163
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;
+}