Skip to content

Commit

Permalink
Merge branch 'release/1.0'
Browse files Browse the repository at this point in the history
  • Loading branch information
tessarin committed Oct 2, 2017
2 parents db831d0 + 556723a commit e61bee3
Show file tree
Hide file tree
Showing 5 changed files with 352 additions and 0 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
cdecl
*.1
20 changes: 20 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
@@ -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
59 changes: 59 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -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*<sup>[1]</sup>. 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

---

<sup>[1]</sup> **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
94 changes: 94 additions & 0 deletions cdecl.pod
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
=head1 NAME

B<cdecl> -- Translate C declarations to English

=head1 SYNOPSIS

B<cdecl>

=head1 DESCRIPTION

B<cdecl> 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<cdecl> 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<Arrays>

Interpreted with or without any size.

=item - I<Functions>

Interpreted with arguments ignored.

=item - I<Qualifiers>

Both C<volatite> and C<const> (which is translated as I<readonly>)

=item - I<Pointers>

=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<cdecl> 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<Expert C Programming - Deep C Secrets>, 1st edition

Peter van der Linden
(C) 1994 Sun Microsystems, Inc.
ISBN:978-0-131-77429-2

=cut
177 changes: 177 additions & 0 deletions main.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,177 @@
#include <stdio.h>
#include <string.h>
#include <ctype.h>

#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;
}

0 comments on commit e61bee3

Please sign in to comment.