From d7150994e37dd5f5f73b8c3a94784cda60b5748a Mon Sep 17 00:00:00 2001 From: Cesar Tessarin Date: Sun, 1 Oct 2017 11:10:15 -0300 Subject: [PATCH 01/12] Add initial files --- .gitignore | 2 ++ Makefile | 13 +++++++++++++ main.c | 5 +++++ 3 files changed, 20 insertions(+) create mode 100644 .gitignore create mode 100644 Makefile create mode 100644 main.c 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..c1344e5 --- /dev/null +++ b/Makefile @@ -0,0 +1,13 @@ +CFLAGS = -Wall + +all: cdecl + +cdecl: main.c + $(CC) $(CFLAGS) -o $@ $^ + +debug: CFLAGS += -g +debug: cdecl + +.PHONY: clean +clean: + -rm -f cdecl diff --git a/main.c b/main.c new file mode 100644 index 0000000..e840c8e --- /dev/null +++ b/main.c @@ -0,0 +1,5 @@ +int +main(int argc, char **argv) +{ + return 0; +} From 1361cfa401b41fd6e5dea2ba49a52f27693de644 Mon Sep 17 00:00:00 2001 From: Cesar Tessarin Date: Sun, 1 Oct 2017 11:35:29 -0300 Subject: [PATCH 02/12] Add type declarations and first function --- main.c | 53 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 53 insertions(+) diff --git a/main.c b/main.c index e840c8e..ddd944d 100644 --- a/main.c +++ b/main.c @@ -1,3 +1,56 @@ +#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]; + +/* 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; +} + int main(int argc, char **argv) { From 4a2cae3ed1640172771b835ad6146f934347d3e6 Mon Sep 17 00:00:00 2001 From: Cesar Tessarin Date: Sun, 1 Oct 2017 13:58:28 -0300 Subject: [PATCH 03/12] Add utility and parsing routines --- main.c | 112 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 112 insertions(+) diff --git a/main.c b/main.c index ddd944d..0fc5f90 100644 --- a/main.c +++ b/main.c @@ -1,4 +1,6 @@ +#include #include +#include #define MAXTOKENLEN 64 #define MAXTOKENS 128 @@ -16,6 +18,11 @@ enum token_type { /* 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; @@ -51,6 +58,111 @@ classify_string() return IDENTIFIER; } +/* reads the next token into `this` and classify it */ +void +get_token() +{ + char *str = this.string; + + /* discard whitespace */ + while (isspace(*str = getchar())) ; + + 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 a ", 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("size %s ", this.string); + get_token(); /* read `]` */ + } + + printf("of "); + get_token(); +} + +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(); + + deal_with_pointers(); + + /* while there is content on the stack */ + while (stack_index >= 0) { + 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) { From 55ce69ba3bee938703e711b602d0f2e3445e4e4a Mon Sep 17 00:00:00 2001 From: Cesar Tessarin Date: Sun, 1 Oct 2017 14:14:31 -0300 Subject: [PATCH 04/12] Complete main and improve parsing The parsing routines are now called on `main`. Also, only spaces and tabs are now considered whitespace, since that allows the new line to terminate the program (instead of waiting for EOD). Parsing routine `deal_with_declarator` had the call to `deal_with_pointers` moved to inside the stack loop. This differs from the original algorithm, however it is necessary here since pointers do not have special treatment in `get_token` (as in the original). Thus, without repeatedly analysing for them in the stack, they would be printed as `*` only. --- main.c | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/main.c b/main.c index 0fc5f90..ee0efdb 100644 --- a/main.c +++ b/main.c @@ -65,7 +65,9 @@ get_token() char *str = this.string; /* discard whitespace */ - while (isspace(*str = getchar())) ; + do { + *str = getchar(); + } while (*str == ' ' || *str == '\t'); if (isalnum(*str)) { /* read the rest */ @@ -130,6 +132,7 @@ deal_with_arrays() get_token(); } +/* prints pointers on stack, if any */ void deal_with_pointers() { @@ -148,10 +151,9 @@ deal_with_declarator() if (this.type == '(') deal_with_function_args(); - deal_with_pointers(); - /* while there is content on the stack */ while (stack_index >= 0) { + deal_with_pointers(); if (current.type == '(') { /* simple parenthesis */ pop; @@ -166,5 +168,10 @@ deal_with_declarator() int main(int argc, char **argv) { + read_to_first_identifier(); + deal_with_declarator(); + + printf("\n"); + return 0; } From 2d6faa9a21325dabf9b88b614dc79be2a224fcce Mon Sep 17 00:00:00 2001 From: Cesar Tessarin Date: Sun, 1 Oct 2017 14:47:05 -0300 Subject: [PATCH 05/12] Add target to generate documentation --- Makefile | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/Makefile b/Makefile index c1344e5..0786982 100644 --- a/Makefile +++ b/Makefile @@ -1,6 +1,6 @@ CFLAGS = -Wall -all: cdecl +all: cdecl doc cdecl: main.c $(CC) $(CFLAGS) -o $@ $^ @@ -8,6 +8,13 @@ cdecl: main.c 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 From 033a387cceb6e40b80ae20508e3c8bd1789eeeb1 Mon Sep 17 00:00:00 2001 From: Cesar Tessarin Date: Sun, 1 Oct 2017 17:17:29 -0300 Subject: [PATCH 06/12] Remove article from generated text --- main.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/main.c b/main.c index ee0efdb..3633b20 100644 --- a/main.c +++ b/main.c @@ -94,7 +94,7 @@ read_to_first_identifier() push(this); } while (this.type != IDENTIFIER); - printf("%s is a ", this.string); + printf("%s is ", this.string); pop; get_token(); From d9556f6fafe72ed219511d657dbd0d3f08da55c1 Mon Sep 17 00:00:00 2001 From: Cesar Tessarin Date: Sun, 1 Oct 2017 17:17:56 -0300 Subject: [PATCH 07/12] Add man page --- cdecl.pod | 94 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 94 insertions(+) create mode 100644 cdecl.pod diff --git a/cdecl.pod b/cdecl.pod new file mode 100644 index 0000000..8b4af33 --- /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 given by +Peter van der Linden on his book "Expert C Programming - Deep C Secrets". It +was given as a programming challenge 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 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 an 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 From 1103490bd8c2b6684fd42a7c35cd82c1253d8076 Mon Sep 17 00:00:00 2001 From: Cesar Tessarin Date: Sun, 1 Oct 2017 17:25:23 -0300 Subject: [PATCH 08/12] Improve manual text --- cdecl.pod | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/cdecl.pod b/cdecl.pod index 8b4af33..a1d8c42 100644 --- a/cdecl.pod +++ b/cdecl.pod @@ -11,9 +11,9 @@ B 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 given by -Peter van der Linden on his book "Expert C Programming - Deep C Secrets". It -was given as a programming challenge in chapter 3. +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 @@ -76,7 +76,7 @@ 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 an implement and practice an easy algorithm. +rather implement and practice an easy algorithm. =head1 AUTHORS & HISTORY From 6ac66f1f838d244d438d874d7fcaa4e7c2deaba6 Mon Sep 17 00:00:00 2001 From: Cesar Tessarin Date: Sun, 1 Oct 2017 22:48:12 -0300 Subject: [PATCH 09/12] Add README file --- README.md | 59 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 59 insertions(+) create mode 100644 README.md diff --git a/README.md b/README.md new file mode 100644 index 0000000..0813400 --- /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 location on your system: + + $ make + $ mv dither ~/bin + $ mv dither.1 ~/man/man1 + +## Usage + +Call `cdecl` passing the desired declaration or write 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 From 76ce5d259bd8444eab593ad6da86510508689624 Mon Sep 17 00:00:00 2001 From: Cesar Tessarin Date: Sun, 1 Oct 2017 22:50:15 -0300 Subject: [PATCH 10/12] Improve array translation and examples --- cdecl.pod | 2 +- main.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/cdecl.pod b/cdecl.pod index a1d8c42..247cd18 100644 --- a/cdecl.pod +++ b/cdecl.pod @@ -67,7 +67,7 @@ produce, for example: $ cdecl long *(*(*x)[10])() - x is pointer to array size 10 of pointer to function returning pointer to long + x is pointer to array with size 10 of pointer to function returning pointer to long =head1 CAVEATS diff --git a/main.c b/main.c index 3633b20..27f560c 100644 --- a/main.c +++ b/main.c @@ -124,7 +124,7 @@ deal_with_arrays() /* print size if given */ if (isdigit(*this.string)) { - printf("size %s ", this.string); + printf("with size %s ", this.string); get_token(); /* read `]` */ } From 2d5852d828e253f5fca28b5800411359d62c63a7 Mon Sep 17 00:00:00 2001 From: Cesar Tessarin Date: Sun, 1 Oct 2017 22:54:53 -0300 Subject: [PATCH 11/12] Improve README text and formatting --- README.md | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/README.md b/README.md index 0813400..378861b 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,7 @@ `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. +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. @@ -17,17 +17,17 @@ program is given as an exercise in his book. #### Manual Installation Run `make` to compile the program and generate the manual page. Then, move the -files to your desired and appropriate location on your system: +files to your desired and appropriate locations on your system: $ make - $ mv dither ~/bin - $ mv dither.1 ~/man/man1 + $ mv cdecl ~/bin + $ mv cdecl.1 ~/man/man1 ## Usage -Call `cdecl` passing the desired declaration or write directly in your -terminal. The program will process the declaration and exit at the end of the -first line. +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 @@ -50,10 +50,10 @@ program check the [manual][man]. --- -[1]*Expert C Programming - Deep C Secrets*, 1st edition -  Peter van der Linden -  © 1994 Sun Microsystems, Inc. -  ISBN 978-0-131-77429-2 +[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 From 556723adc421cc2b930bb0fd0d62660fb1112a24 Mon Sep 17 00:00:00 2001 From: Cesar Tessarin Date: Sun, 1 Oct 2017 22:57:28 -0300 Subject: [PATCH 12/12] Improve README formatting --- README.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 378861b..b7d43cf 100644 --- a/README.md +++ b/README.md @@ -50,10 +50,10 @@ program check the [manual][man]. --- -[1]**Expert C Programming - Deep C Secrets**, 1st edition -  Peter van der Linden -  © 1994 Sun Microsystems, Inc. -  ISBN 978-0-131-77429-2 +[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