A few days ago I started writing my own compiler-like program which has the goal to compile plain text into executable python code. After I let my first version review here, I made huge changes to the overall code and added the possibility of adding rectangles. I try to add all the suggested improvements as best as I could, please do not feel offended, if I missed one of your suggestions.
My final goal is to write a program which compiles text into executable python code, which then will display a 3D-Diagram containing all geometric shapes when executed.
At the moment, the user has to provide a text similar to this:
(1.2,45,6)
(7,8,5)
(10,77,88)
(99999,1,1)
R((1,1,1),(2,2,2),(1,3,4))
(5,7,6)
(1,2,3)
(4,5,6)
R((2,3,4),(9,9,9),(3,4,5))
P(2,1,1)
where R(...) symbolizes rectangles and P(...) is an acronym for a profane point. I added the formatting mistake of the last line, purposely, to show that the compiler does not care about spaces. This plain text will be compiled to a python-code similar (depending on the exact preferences of the code) like this.
points = [
(1.2,45,6),
(7,8,5),
(10,77,88),
(99999,1,1),
]
points.extend([
(5,7,6),
(1,2,3),
(4,5,6),
(2,1,1),
])
rectangels = [
((1,1,1),(2,2,2),(1,3,4)),
((2,3,4),(9,9,9),(3,4,5)),
]
print(points)
print(rectangels)
For the sake of me, you and all humankind I quickly realized that rewriting the code into a state machine is the proper way to go after suggested by @Edward.
So how could I further improve the following code?
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <errno.h>
#include <float.h>
#include <ctype.h>
#define SPACE_FOR_POINTS 4
#define SPACE_FOR_RECTANGLES 4
/*For some reason, maximum size of any double represetend as a string:
StackOverflow: What is the maximum length in chars needed to represent any double value?
*/
#define MAX_DIGITS_DOUBLE 3 + DBL_MANT_DIG - DBL_MIN_EXP
#define MAX_DIGITS_POINTS 5 + 3*MAX_DIGITS_DOUBLE
#define MAX_DIGITS_RECT 17+9*MAX_DIGITS_DOUBLE
void printPointList(char appened, char** points, int index);
void printRectangleList(char appened, char** rectangles, int index);
int main(int argc, char * argv[]) {
FILE* fp = fopen(argv[1], "r");
if(fp == NULL)return errno;
//Do parsing
char** points; //all future points will be stored here
points = malloc(SPACE_FOR_POINTS * sizeof(char*)); //for the moment, make space for SPACE_FOR_POINTS
if(points != NULL){
for(int i = 0; i < SPACE_FOR_POINTS; i++){
/*
This is a point to be stored:
(double,double,double)\0
Calloc is better, because we can later break
creating the list of points/rectangels, when first char is not '(',
which is not necessarily the case for malloc.
*/
points[i] = calloc(MAX_DIGITS_POINTS,1);
if(points[i] == NULL){
fprintf(stderr, "Could not allocate enough memory to perform compilation.");
return -1;
}
}
} else {
fprintf(stderr, "Could not allocate enough memory to perform compilation.");
return -1;
}
char** rectangles; //all future regtangles will be stored here
rectangles = malloc(SPACE_FOR_RECTANGLES*sizeof(char*)); /*for the moment, make space for
SPACE_FOR_RECTANGLES */
if(points != NULL){
for(int i = 0; i < SPACE_FOR_RECTANGLES; i++){
/*
This is a rectangle to be stored:
((double,double,double),(double,double,double),(double,double,double))\0
Calloc is better, because we can later break
creating the list of points/rectangels, when first char is not '(',
which is not necessarily the case for malloc.
*/
rectangles[i] = calloc(MAX_DIGITS_RECT, 1);
if(rectangles[i] == NULL){
fprintf(stderr, "Could not allocate enough memory to perform compilation.");
return -1;
}
}
} else {
fprintf(stderr, "Could not allocate enough memory to perform compilation.");
return -1;
}
char bool_appended_rectangle = 0; //Was a list already printed?
char bool_appended_points = 0; //Was a list already printed?
int numbers_written = 0; //counter
int points_index = -1;
int rectangels_index = -1;
size_t actual_index = 0; //counter
enum{name, openparen, comma, number, endparen, error} state = endparen;
enum{point, rectangle} shape = point;
//State machine
for (char ch = fgetc(fp); ch != EOF; ch = fgetc(fp)) {
if(isspace(ch)){
continue;
}
switch(state){
case name:
if(ch == '('){
state = openparen;
switch (shape) {
case rectangle:
rectangles[rectangels_index][actual_index++] = ch;
break;
case point:
points[points_index][actual_index++] = ch;
break;
default:
break;
}
} else {
state = error;
}
break;
case openparen:
if(ch == '('){
switch (shape) {
case rectangle:
rectangles[rectangels_index][actual_index++] = ch;
state = openparen;
break;
case point:
state = error;
break;
default:
break;
}
}
if(isdigit(ch)){
numbers_written++;
switch (shape) {
case rectangle:
rectangles[rectangels_index][actual_index++] = ch;
state = number;
break;
case point:
points[points_index][actual_index++] = ch;
state = number;
break;
default:
break;
}
}
break;
case number:
if(isdigit(ch)){
switch (shape) {
case rectangle:
rectangles[rectangels_index][actual_index++] = ch;
break;
case point:
points[points_index][actual_index++] = ch;
break;
default:
break;
}
break;
}
if(ch == ','){
switch (shape) {
case rectangle:
if(numbers_written <= 9){
rectangles[rectangels_index][actual_index++] = ch;
state = comma;
} else {
state = error;
}
break;
case point:
if(numbers_written <= 3){
points[points_index][actual_index++] = ch;
state = comma;
} else {
state = error;
}
default:
break;
}
break;
}
if(ch == ')'){
switch (shape) {
case rectangle:
if(numbers_written%3 == 0){
rectangles[rectangels_index][actual_index++] = ch;
state = endparen;
} else {
state = error;
}
break;
case point:
//printf("%d", numbers_written);
if(numbers_written == 3){
state = endparen;
points[points_index][actual_index++] = ch;
} else {
state = error;
}
break;
default:
break;
}
break;
}
if(ch == '.'){
points[points_index][actual_index++] = ch;
break;
}
state = error;
break;
case endparen:
switch (state) {
case point:
points[points_index][actual_index++] = '\0';
break;
case rectangle:
rectangles[rectangels_index][actual_index++] = '\0';
break;
default:
break;
}
if(ch == '(' || ch == 'P'){
if(++points_index >= SPACE_FOR_POINTS){
printPointList(bool_appended_points, points, points_index-1);
bool_appended_points = 1;
//clear all Strings
for(int i = 0; i < points_index; i++){
points[i] = calloc(MAX_DIGITS_POINTS, 1);
}
points_index = 0;
}
}
if(ch == '('){
actual_index = 0;
points[points_index][actual_index++] = ch;
shape = point;
state = openparen;
numbers_written = 0;
break;
}
if(ch == 'P'){
actual_index = 0;
state = name;
shape = point;
numbers_written = 0;
break;
}
if(ch == 'R'){
if(++rectangels_index >= SPACE_FOR_RECTANGLES){
printRectangleList(bool_appended_rectangle, rectangles, rectangels_index-1);
bool_appended_rectangle = 1;
//clear all strings
for(int i = 0; i < rectangels_index; i++){
rectangles[i] = calloc(MAX_DIGITS_RECT, 1);
}
rectangels_index = 0;
}
actual_index = 0;
state = name;
shape = rectangle;
numbers_written = 0;
break;
}
if(ch == ','){
if(shape == rectangle){
if(numbers_written < 9){
rectangles[rectangels_index][actual_index++] = ch;
state = comma;
} else {
state = error;
}
}
break;
}
if(ch == ')'){
if(shape == rectangle){
rectangles[rectangels_index][actual_index++] = ch;
rectangles[rectangels_index][actual_index++] = '\0';
break;
}
}
state = error;
break;
case comma:
switch (shape) {
case point:
if(isdigit(ch)){
numbers_written++;
state = number;
points[points_index][actual_index++] = ch;
break;
}
state = error;
break;
case rectangle:
if(isdigit(ch)){
numbers_written++;
rectangles[rectangels_index][actual_index++] = ch;
state = number;
break;
}
if(ch == '('){
rectangles[rectangels_index][actual_index++] = ch;
state = openparen;
break;
}
state = error;
break;
default:
break;
}
break;
default:
fprintf(stderr, "Error: Corrupted File.");
return -1;
}
}
printPointList(bool_appended_points, points, points_index);
printRectangleList(bool_appended_rectangle, rectangles, rectangels_index);
printf("print(points)\n");
printf("print(rectangels)\n");
fclose(stdout); //needed?
fclose(fp);
return 1;
}
void printPointList(char appended, char** points, int index){
if(!appended){
appended = 1;
printf("points = [\n");
} else {
printf("points.extend(\n");
}
for(int i = 0; i <= index; i++){
printf("%s,\n", points[i]);
}
if(!appended){
printf("]\n");
} else {
printf("])\n");
}
}
void printRectangleList(char appended, char** rectangles, int index){
if(!appended){
appended = 1;
printf("rectangle = [\n");
} else {
printf("rectangle.extend(\n");
}
for(int i = 0; i <= index; i++){
printf("%s,\n", rectangles[i]);
}
if(!appended){
printf("]\n");
} else {
printf("])\n");
}
}
points.extend
at all? \$\endgroup\$