diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..40acb59 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +cdecl +*.1 diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..0786982 --- /dev/null +++ b/Makefile @@ -0,0 +1,20 @@ +CFLAGS = -Wall + +all: cdecl doc + +cdecl: main.c + $(CC) $(CFLAGS) -o $@ $^ + +debug: CFLAGS += -g +debug: cdecl + +doc: cdecl.1 + +cdecl.1: cdecl.pod + @echo -- Generating manpage from POD: + pod2man --section=1 --center="cdecl" $? $@ + +.PHONY: clean +clean: + -rm -f cdecl + -rm -f *.1 diff --git a/README.md b/README.md new file mode 100644 index 0000000..b7d43cf --- /dev/null +++ b/README.md @@ -0,0 +1,59 @@ +# cdecl - C Declarations Translator + +`cdecl` translates C declarations to plain English. + +Throughout the history of the C language, this program has been written many +times in different versions with varying levels of complexity and features. +This one in particular uses an algorithm developed by Peter van der Linden, +presented in his book *Expert C Programming*[1]. Writing the +program is given as an exercise in his book. + +## Installing + +#### On macOS with [Homebrew][brw] + + $ brew install tessarin/core/cdecl + +#### Manual Installation + +Run `make` to compile the program and generate the manual page. Then, move the +files to your desired and appropriate locations on your system: + + $ make + $ mv cdecl ~/bin + $ mv cdecl.1 ~/man/man1 + +## Usage + +Call `cdecl` passing the declaration or write it directly in your terminal. +The program will process the declaration and exit at the end of the first +line. + + $ cdecl + +For more information including the types and features recognized by the +program check the [manual][man]. + +## Examples + + $ cdecl + long * const c + c is readonly pointer to long + + $ cdecl + int *(*fn())() + fn is function returning pointer to function returning pointer to int + + $ cdecl + short *(*(* const x)[3])() + x is readonly pointer to array with size 3 of pointer to function returning pointer to short + +--- + +[1] **Expert C Programming - Deep C Secrets**, 1st edition +  Peter van der Linden +  © 1994 Sun Microsystems, Inc. +  ISBN 978-0-131-77429-2 + + [brw]: https://brew.sh + [man]: cdecl.pod diff --git a/cdecl.pod b/cdecl.pod new file mode 100644 index 0000000..247cd18 --- /dev/null +++ b/cdecl.pod @@ -0,0 +1,94 @@ +=head1 NAME + +B -- Translate C declarations to English + +=head1 SYNOPSIS + +B + +=head1 DESCRIPTION + +B reads a C declaration from its input and translates it to English +terms which are sent as output. + +This program was written following the recommendations and algorithm presented +by Peter van der Linden on his book "Expert C Programming - Deep C Secrets". +Writing this program was a programming challenge given in Chapter 3. + +There have been many C versions written along history. This one in +particular -- although very simple -- has an easy to understand parsing algorithm +and is relatively complete. Recognized types include: + + void + char + short + int + long + signed + unsigned + float + double + struct + union + enum + +And the following language features: + +=over 6 + +=item - I + +Interpreted with or without any size. + +=item - I + +Interpreted with arguments ignored. + +=item - I + +Both C and C (which is translated as I) + +=item - I + +=back + +=head1 EXAMPLES + +Calling the program and writing the declaration followed by a new line will +produce, for example: + + $ cdecl + int * const n + n is readonly pointer to int + + $ cdecl + char *f() + f is function returning pointer to char + + $ cdecl + long *(*(*x)[10])() + x is pointer to array with size 10 of pointer to function returning pointer to long + +=head1 CAVEATS + +Due to the way it is constructed, this version of C won't produce any +warning or error if bad syntax is encountered. There are no checks for invalid +constructs either, such as an array of functions. + +The goal (as described in the book) was not to build a complete parser but +rather implement and practice an easy algorithm. + +=head1 AUTHORS & HISTORY + +This software and manual were written by Cesar Tessarin originally on July 5th +2011, rewritten partially and updated on Oct. 2017. However, all credit of the +internal algorithm and design goes to Peter van der Linden since (as stated +above) this was an exercise from his book: + +B, 1st edition + +Peter van der Linden +(C) 1994 Sun Microsystems, Inc. +ISBN:978-0-131-77429-2 + +=cut diff --git a/main.c b/main.c new file mode 100644 index 0000000..27f560c --- /dev/null +++ b/main.c @@ -0,0 +1,177 @@ +#include +#include +#include + +#define MAXTOKENLEN 64 +#define MAXTOKENS 128 + +struct token { + char type; + char string[MAXTOKENLEN]; +}; + +enum token_type { + TYPE, + QUALIFIER, + IDENTIFIER +}; + +/* main token stack */ +struct token stack[MAXTOKENS]; +int stack_index = -1; + +#define push(A) stack[++stack_index] = (A) +#define pop stack[stack_index--] +#define current stack[stack_index] + +/* current token */ +struct token this; + +/* returns the type of the current token */ +enum token_type +classify_string() +{ + char *str = this.string; + + if ( !strcmp(str, "void") + || !strcmp(str, "char") + || !strcmp(str, "short") + || !strcmp(str, "int") + || !strcmp(str, "long") + || !strcmp(str, "signed") + || !strcmp(str, "unsigned") + || !strcmp(str, "float") + || !strcmp(str, "double") + || !strcmp(str, "struct") + || !strcmp(str, "union") + || !strcmp(str, "enum") + ) return TYPE; + + if (!strcmp(str, "volatile")) + return QUALIFIER; + + if (!strcmp(str, "const")) { + strcpy(str, "readonly"); + return QUALIFIER; + } + + return IDENTIFIER; +} + +/* reads the next token into `this` and classify it */ +void +get_token() +{ + char *str = this.string; + + /* discard whitespace */ + do { + *str = getchar(); + } while (*str == ' ' || *str == '\t'); + + if (isalnum(*str)) { + /* read the rest */ + while (isalnum(*++str = getchar())) ; + ungetc(*str, stdin); + *str = '\0'; + + this.type = classify_string(); + return; + } + + /* for other not alnum the token type is itself */ + this.type = *str; + *++str = '\0'; + + return; +} + +void +read_to_first_identifier() +{ + do { + get_token(); + push(this); + } while (this.type != IDENTIFIER); + + printf("%s is ", this.string); + pop; + + get_token(); +} + +/* --- Parsing Routines --- + * + * Work on the current token and always + * read the next one at the end. + */ + +void +deal_with_function_args() +{ + /* read past closing `)` */ + while (this.type != ')') get_token(); + get_token(); + + printf("function returning "); +} + +void +deal_with_arrays() +{ + printf("array "); + get_token(); /* read size or `]` */ + + /* print size if given */ + if (isdigit(*this.string)) { + printf("with size %s ", this.string); + get_token(); /* read `]` */ + } + + printf("of "); + get_token(); +} + +/* prints pointers on stack, if any */ +void +deal_with_pointers() +{ + while (current.type == '*') { + printf("pointer to "); + pop; + } +} + +void +deal_with_declarator() +{ + if (this.type == '[') + deal_with_arrays(); + + if (this.type == '(') + deal_with_function_args(); + + /* while there is content on the stack */ + while (stack_index >= 0) { + deal_with_pointers(); + if (current.type == '(') { + /* simple parenthesis */ + pop; + get_token(); /* should be the matching `)` */ + deal_with_declarator(); + } else { + printf("%s ", pop.string); + } + } +} + +int +main(int argc, char **argv) +{ + read_to_first_identifier(); + deal_with_declarator(); + + printf("\n"); + + return 0; +}