aboutsummaryrefslogtreecommitdiff
path: root/ch5/5-20_expanded-dcl.c
diff options
context:
space:
mode:
authorzlg <zlg@zlg.space>2015-02-20 22:35:43 -0800
committerzlg <zlg@zlg.space>2015-02-20 22:35:43 -0800
commite859c083a97edf6be14b17c652835f1e27b75210 (patch)
tree1a48df7728bee2e0c1630c4cd0a0f169c1503214 /ch5/5-20_expanded-dcl.c
parentSolve Exercise 5-19: Omit extra parens in `undcl` (diff)
downloadknr-e859c083a97edf6be14b17c652835f1e27b75210.tar.gz
knr-e859c083a97edf6be14b17c652835f1e27b75210.tar.bz2
knr-e859c083a97edf6be14b17c652835f1e27b75210.tar.xz
knr-e859c083a97edf6be14b17c652835f1e27b75210.zip
Solve Exercise 5-20: Expanded `dcl`
This marks the end of chapter 5. Admittedly, the solution for 5-20 is terrible, but it does what it's supposed to do. I'm just excited to move onto chapter 6 and start learning structures!
Diffstat (limited to 'ch5/5-20_expanded-dcl.c')
-rw-r--r--ch5/5-20_expanded-dcl.c215
1 files changed, 215 insertions, 0 deletions
diff --git a/ch5/5-20_expanded-dcl.c b/ch5/5-20_expanded-dcl.c
new file mode 100644
index 0000000..d7d4c5c
--- /dev/null
+++ b/ch5/5-20_expanded-dcl.c
@@ -0,0 +1,215 @@
+#include <stdio.h>
+#include <string.h>
+#include <ctype.h>
+
+/* The C Programming Language: 2nd Edition
+ *
+ * Exercise 5-20: Expand `dcl` to handle declarations with function
+ * argument types, qualifiers like `const`, and so on.
+ *
+ * Notes: Basically, you need a qualifier string and an accepting
+ * type string. This solution is nowhere near robust, and is in great
+ * need of a refactor, but honestly I don't know where to begin fixing
+ * this mess.
+ *
+ * The robust way to do this would be to have a list of valid tokens, their
+ * semantic meaning, and a sort of "test string" that could be verified for
+ * integrity before attempting to output. Once it's been checked for sanity,
+ * the gathered details should be added to the final sentence in grammatical
+ * order. That requires more work than this exercise is worth, however. If I
+ * find myself bored with C, I'll come back to this, as it could be done much
+ * better than what's here. Patches welcome!
+ */
+
+#define MAXTOKEN 100
+#define BUFSIZE 100
+
+enum { NAME, PARENS, BRACKETS, TYPE, QUAL };
+
+void dcl(void);
+void dirdcl(void);
+int gettoken(void);
+char buf[BUFSIZE];
+int bufp = 0;
+int tokentype;
+int accept = 0;
+
+char qual[MAXTOKEN];
+char token[MAXTOKEN];
+char name[MAXTOKEN];
+char datatype[MAXTOKEN];
+char clause[MAXTOKEN];
+char out[1000];
+int get_line(char *s, int lim);
+
+void dcl(void) {
+ int ns;
+ for (ns = 0; gettoken() == '*'; ns++) {
+ }
+ dirdcl();
+ while (ns-- > 0) {
+ strcat(out, " pointer to");
+ }
+}
+
+void dirdcl(void) {
+ int type;
+ if (tokentype == '(') {
+ dcl();
+ if (tokentype != ')') {
+ printf("error: missing )\n");
+ }
+ } else if (tokentype == QUAL) {
+ strcat(qual, token);
+ gettoken();
+ } else if (tokentype == TYPE) {
+ strcat(datatype, " ");
+ strcat(datatype, token);
+ } else if (tokentype == NAME) {
+ strcpy(name, token);
+ } else {
+ printf("error: expected name or (dcl)\n");
+ }
+ while ((type = gettoken()) == PARENS || type == BRACKETS) {
+ if (type == PARENS) {
+ strcat(out, " function returning");
+ if (accept == 1) {
+ strcpy(clause, " accepting ");
+ while ((type = gettoken()) != '\n' && type != ')') {
+ if (type == '(') {
+ continue;
+ }
+ if (type == QUAL) {
+ strcat(clause, token);
+ strcat(clause, " ");
+ continue;
+ } else if (type == TYPE) {
+ strcat(clause, token);
+ return;
+ } else {
+ printf("Error: invalid argument type: %s\n", token);
+ }
+ }
+ strcat(clause, "void");
+ return;
+ }
+ } else {
+ strcat(out, " array");
+ strcat(out, token);
+ strcat(out, " of");
+ }
+ }
+}
+
+int gettoken(void) {
+ char *p = token;
+ while (buf[bufp] == ' ' || buf[bufp] == '\t') {
+ bufp++;
+ }
+ if (buf[bufp] == '/') {
+ if (buf[bufp+1] == '/' || buf[bufp+1] == '*') {
+ while (buf[bufp] != '\n') {
+ bufp++;
+ }
+ }
+ return tokentype = buf[bufp];
+ } else if (buf[bufp] == '(') {
+ if (buf[++bufp] == ')') {
+ strcpy(token, "()");
+ bufp++;
+ accept = 1;
+ return tokentype = PARENS;
+ } else {
+ return tokentype = '(';
+ }
+ } else if (buf[bufp] == '[') {
+ while (buf[bufp] != ']') {
+ if (buf[bufp + 1] == '[') {
+ return tokentype = buf[bufp + 1];
+ }
+ if (isspace(buf[bufp])) {
+ bufp++;
+ continue;
+ }
+ *p++ = buf[bufp++];
+ }
+ *p++ = buf[bufp++];
+ *p = '\0';
+ return tokentype = BRACKETS;
+ } else if (isalpha(buf[bufp])) {
+ *p++ = buf[bufp++];
+ while (isalnum(buf[bufp])) {
+ *p++ = buf[bufp++];
+ }
+ *p = '\0';
+ /* check types */
+ if (strcmp(token, "const") == 0 ||
+ strcmp(token, "static") == 0 ||
+ strcmp(token, "long") == 0 ||
+ strcmp(token, "short") == 0) {
+ return tokentype = QUAL;
+ }
+ if (strcmp(token, "int") == 0 ||
+ strcmp(token, "double") == 0 ||
+ strcmp(token, "unsigned") == 0 ||
+ strcmp(token, "signed") == 0 ||
+ strcmp(token, "void") == 0 ||
+ strcmp(token, "char") == 0) {
+ return tokentype = TYPE;
+ }
+ return tokentype = NAME;
+ } else {
+ return tokentype = buf[bufp++];
+ }
+}
+
+/* Returns length of line including \n
+ */
+int get_line(char *s, int lim) {
+ int c, i;
+ for (i = 0; i < lim - 1 && (c=getchar()) != EOF && c != '\n'; ++i) {
+ *s++ = c;
+ }
+ if (c == '\n') {
+ *s++ = c;
+ i++;
+ }
+ *s = '\0';
+ return i;
+}
+
+int main(void) {
+ int len = 0;
+ while ((len = get_line(buf, BUFSIZE)) > 0 && buf[len - 1] != EOF) {
+ if (len == 1 && buf[bufp] == '\n') {
+ continue;
+ }
+ bufp = 0;
+ datatype[0] = 0;
+ qual[0] = 0;
+ gettoken();
+ if (tokentype == QUAL) {
+ strcpy(qual, token);
+ gettoken();
+ }
+ if (tokentype == TYPE) {
+ strcpy(datatype, token);
+ }
+ out[0] = '\0';
+ name[0] = '\0';
+ dcl();
+ if (tokentype == ')') {
+ printf("error: too many closing parens\n\n");
+ } else if (tokentype == ']') {
+ printf("error: too many closing brackets\n\n");
+ } else if (tokentype == '(') {
+ printf("error: too many opening parens\n\n");
+ } else if (tokentype == '[') {
+ printf("error: too many opening brackets\n\n");
+ } else {
+ printf("%s: %s %s %s %s\n\n", name, out, qual, datatype, clause);
+ }
+ accept = 0;
+ }
+ return 0;
+}