diff --git a/minimake/src/list/.gitignore b/minimake/src/list/.gitignore new file mode 100644 index 0000000..3309b20 --- /dev/null +++ b/minimake/src/list/.gitignore @@ -0,0 +1 @@ +list.c diff --git a/minimake/src/list/list.h b/minimake/src/list/list.h new file mode 100644 index 0000000..03a03ca --- /dev/null +++ b/minimake/src/list/list.h @@ -0,0 +1,104 @@ +#ifndef LIST_H +#define LIST_H + +#include + +struct list +{ + int data; + struct list *next; +}; + +/* +** Insert a node containing `value` at the beginning of the list. +** Return `NULL` if an error occured. +*/ +struct list *list_prepend(struct list *list, int value); + +/* +** Return the lenght of the list. +** Return `0` if the list is empty. +*/ +size_t list_length(struct list *list); + +/* +** Display the list contents on `stdout`. +** Nothing is displayed if the list is empty. +*/ +void list_print(struct list *list); + +/* +** Release the memory used by the list. +** Does nothing if `list` is `NULL`. +*/ +void list_destroy(struct list *list); + +/* +** Append a node containing `value` at the end of the list. +** Return `NULL` if an error occured. +*/ +// START PROTO list_append +struct list *list_append(struct list *list, int value); +// END PROTO list_append + +/* +** Insert a node containing `value` at the index `index` in the list. +** If the index is greater than the length of the list, the behaviour is the +** same as `list_append`. +** Return `NULL` if an error occured. +*/ +// START PROTO list_insert +struct list *list_insert(struct list *list, int value, size_t index); +// END PROTO list_insert + +/* +** Remove the element at the index `index`. +** Return `NULL` if an error occured. +*/ +// START PROTO list_remove +struct list *list_remove(struct list *list, size_t index); +// END PROTO list_remove + +/* +** Return the position of the first node containing `value`. +** Return `-1` if nothing is found. +*/ +// START PROTO list_find +int list_find(struct list *list, int value); +// END PROTO list_find + +/* +** Concatenate the list `list2` at the end of the list `list`. +** Return `list2` if `list` is `NULL`. +*/ +// START PROTO list_concat +struct list *list_concat(struct list *list, struct list *list2); +// END PROTO list_concat + +/* +** Sort the elements of the list in ascending order. +** Return the new list. +*/ +// START PROTO list_sort +struct list *list_sort(struct list *list); +// END PROTO list_sort + +/* +** Invert the order of the elements of the list. +** Return the new list. +*/ +// START PROTO list_reverse +struct list *list_reverse(struct list *list); +// END PROTO list_reverse + +/* +** Split the list at index `index`. +** First part goes in `list` and contains the element at `index`. +** Second part is returned. +** Return `NULL` if `list` is `NULL` or `index` is invalid. +*/ +// START PROTO list_split +struct list *list_split(struct list *list, size_t index); +// END PROTO list_split + +#endif /* ! LIST_H */ diff --git a/minimake/src/list/list_advanced.c b/minimake/src/list/list_advanced.c new file mode 100644 index 0000000..75b401a --- /dev/null +++ b/minimake/src/list/list_advanced.c @@ -0,0 +1,101 @@ +#include +#include +#include + +#include "list.h" + +struct list *list_insert(struct list *list, int value, size_t index) +{ + if (list == NULL || index == 0) + { + struct list *new_elt = malloc(sizeof(struct list)); + new_elt->data = value; + new_elt->next = list; + return new_elt; + } + + struct list *elt = list; + + for (size_t i = 0; i < index - 1; i++) + { + if (elt->next == NULL) + { + break; + } + elt = elt->next; + } + + struct list *new_elt = malloc(sizeof(struct list)); + new_elt->data = value; + new_elt->next = elt->next; + elt->next = new_elt; + + return list; +} + +struct list *list_remove(struct list *list, size_t index) +{ + struct list *elt = list; + struct list *prev_elt; + + if (index == 0) + { + struct list *res = elt->next; + free(elt); + return res; + } + + for (size_t i = 0; i < index; i++) + { + if (elt == NULL) + { + return list; + } + prev_elt = elt; + elt = elt->next; + } + if (elt == NULL) + { + return list; + } + + prev_elt->next = elt->next; + free(elt); + + return list; +} + +int list_find(struct list *list, int value) +{ + if (list == NULL) + { + return -1; + } + + int res = 0; + while (list->data != value) + { + list = list->next; + res++; + if (list == NULL) + { + return -1; + } + } + return res; +} + +struct list *list_concat(struct list *list, struct list *list2) +{ + if (list == NULL) + { + return list2; + } + struct list *elt = list; + while (elt->next != NULL) + { + elt = elt->next; + } + elt->next = list2; + return list; +} diff --git a/minimake/src/list/list_basic.c b/minimake/src/list/list_basic.c new file mode 100644 index 0000000..094924b --- /dev/null +++ b/minimake/src/list/list_basic.c @@ -0,0 +1,86 @@ +#include +#include + +#include "list.h" + +struct list *list_prepend(struct list *list, int value) +{ + struct list *new_elt = malloc(sizeof(struct list)); + if (new_elt == NULL) + { + return NULL; + } + new_elt->next = list; + new_elt->data = value; + + return new_elt; +} + +size_t list_length(struct list *list) +{ + size_t len = 0; + while (list != NULL) + { + len++; + list = list->next; + } + return len; +} + +void list_print(struct list *list) +{ + if (list == NULL) + { + return; + } + + while (list != NULL) + { + if (list->next != NULL) + { + printf("%d ", list->data); + } + else + { + printf("%d\n", list->data); + } + list = list->next; + } +} + +void list_destroy(struct list *list) +{ + struct list *elt = list; + struct list *next_elt; + while (elt != NULL) + { + next_elt = elt->next; + free(elt); + elt = next_elt; + } +} + +struct list *list_append(struct list *list, int value) +{ + if (list == NULL) + { + struct list *new_elt = malloc(sizeof(struct list)); + new_elt->data = value; + new_elt->next = NULL; + return new_elt; + } + + struct list *elt = list; + + while (elt->next != NULL) + { + elt = elt->next; + } + + struct list *new_elt = malloc(sizeof(struct list)); + new_elt->data = value; + new_elt->next = NULL; + elt->next = new_elt; + + return list; +} diff --git a/minimake/src/list/list_ultimate.c b/minimake/src/list/list_ultimate.c new file mode 100644 index 0000000..6207398 --- /dev/null +++ b/minimake/src/list/list_ultimate.c @@ -0,0 +1,138 @@ +#include +#include + +#include "list.h" + +void swap_next(struct list *elt) +{ + int c = elt->next->data; + elt->next->data = elt->data; + elt->data = c; +} +struct list *list_sort(struct list *list) +{ + // Bubble sort go ! + if (list == NULL) + { + return list; + } + struct list *elt = list; + int len = 0; + while (elt->next != NULL) + { + if (elt->data > elt->next->data) + { + swap_next(elt); + } + elt = elt->next; + len++; + } + + for (int i = 1; i < len; i++) + { + elt = list; + while (elt->next != NULL) + { + if (elt->data > elt->next->data) + { + swap_next(elt); + } + elt = elt->next; + } + } + + return list; +} + +// Old proto +// WARNING no malloc/free allowed (moulinette issue) +struct list *list_reverse(struct list *list) +{ + if (list == NULL) + { + return list; + } + + // Get len + struct list *elt = list; + int len = 0; + while (elt->next != NULL) + { + len++; + elt = elt->next; + } + elt = list; + + // Bring each elt to end + for (int i = 0; i < len; i++) + { + elt = list; + for (int j = 0; j < len - i; j++) + { + swap_next(elt); + elt = elt->next; + } + } + + return list; +} + +// Doesn't work neither +// struct list *list_reverse(struct list *list) +// { +// struct list *new_list = NULL; +// struct list *elt = list; +// while (elt != NULL) +// { +// struct list *new_elt = malloc(sizeof(struct list)); +// if (new_elt == NULL) +// { +// return NULL; +// } +// new_elt->data = elt->data; +// new_elt->next = new_list; + +// struct list *next_elt = elt->next; +// free(elt); +// elt = next_elt; +// } +// return new_list; +// } + +// Moulinette issue +// struct list *list_reverse(struct list *list) +// { +// if (list == NULL) +// { +// return list; +// } + +// struct list *new_list = NULL; +// while (list != NULL) +// { +// new_list = list_prepend(new_list, list->data); +// list = list_remove(list, 0); +// } + +// return new_list; +// } + +struct list *list_split(struct list *list, size_t index) +{ + struct list *elt = list; + for (size_t i = 0; i < index; i++) + { + if (elt == NULL) + { + return NULL; + } + elt = elt->next; + } + if (elt == NULL) + { + return NULL; + } + struct list *res = elt->next; + elt->next = NULL; + return res; +} diff --git a/minimake/src/list_void/list.c b/minimake/src/list_void/list.c new file mode 100644 index 0000000..24b462e --- /dev/null +++ b/minimake/src/list_void/list.c @@ -0,0 +1,46 @@ +#include "list.h" + +#include +#include + +struct list *list_prepend(struct list *list, const void *value, + size_t data_size) +{ + struct list *new_node = malloc(sizeof(struct list)); + if (new_node == NULL) + { + return NULL; + } + void *node_data = malloc(data_size); + if (node_data == NULL) + { + return NULL; + } + new_node->data = node_data; + memcpy(node_data, value, data_size); + new_node->next = list; + return new_node; +} + +size_t list_length(struct list *list) +{ + size_t res = 0; + while (list != NULL) + { + list = list->next; + res++; + } + return res; +} + +void list_destroy(struct list *list) +{ + struct list *next; + while (list != NULL) + { + next = list->next; + free(list->data); + free(list); + list = next; + } +} diff --git a/minimake/src/list_void/list.h b/minimake/src/list_void/list.h new file mode 100644 index 0000000..b129adc --- /dev/null +++ b/minimake/src/list_void/list.h @@ -0,0 +1,31 @@ +#ifndef LIST_H +#define LIST_H + +#include + +struct list +{ + void *data; + struct list *next; +}; + +/* +** Insert a node containing `value` at the beginning of the list. +** Return `NULL` if an error occurred. +*/ +struct list *list_prepend(struct list *list, const void *value, + size_t data_size); + +/* +** Return the length of the list. +** Return `0` if the list is empty. +*/ +size_t list_length(struct list *list); + +/* +** Release the memory used by the list. +** Does nothing if `list` is `NULL`. +*/ +void list_destroy(struct list *list); + +#endif /* ! LIST_H */ diff --git a/minimake/src/minimake.c b/minimake/src/minimake.c index 3aa5f2e..8db5234 100644 --- a/minimake/src/minimake.c +++ b/minimake/src/minimake.c @@ -1,137 +1,247 @@ +#include "list/list.h" #define _POSIX_C_SOURCE 200809L +// Default values #define BUFFER_SIZE 1024 #define STRING_BUFFER_SIZE 32 #define HASHMAP_SIZE 32 -#include "minimake.h" -#include "hash_map/hash_map.h" - #include #include #include #include +#include "hash_map/hash_map.h" +#include "minimake.h" + +// Static variables +struct hash_map *variables = NULL; +struct hash_map *rules = NULL; + // ==== Parsing ==== // Helps to match a string (excludes blanks and special characters) -static int isChar(char c) { - return c != '\0' && !isblank(c) && c != ':' && c != '=' && c != '#'; +static int isChar(char c) +{ + return c != '\0' && !isblank(c) && c != ':' && c != '=' && c != '#'; } // Returns the next word from buf until buf_len, // and the numbers of read characters in *read_chars // WARNING allocates the result on the heap -static char *readWord(char *buf, size_t buf_len, size_t *read_chars) { - size_t i = 0; - size_t str_buf_size = STRING_BUFFER_SIZE; - char *str_buf = malloc(sizeof(char) * str_buf_size); - while (i < buf_len && isChar(buf[i])) { - // Reallocate more space if necessary - if (i >= str_buf_size - 1) { - str_buf_size += STRING_BUFFER_SIZE; - str_buf = realloc(str_buf, str_buf_size); - if (str_buf == NULL) - errx(2, "Could not realloc"); +static char *readWord(char *buf, size_t buf_len, size_t *read_chars) +{ + size_t i = 0; + size_t str_buf_size = STRING_BUFFER_SIZE; + char *str_buf = malloc(sizeof(char) * str_buf_size); + while (i < buf_len && isChar(buf[i])) + { + // Reallocate more space if necessary + if (i >= str_buf_size - 1) + { + str_buf_size += STRING_BUFFER_SIZE; + str_buf = realloc(str_buf, str_buf_size); + if (str_buf == NULL) + errx(2, "Could not realloc"); + } + + str_buf[i] = buf[i]; + i++; } - str_buf[i] = buf[i]; - i++; - } + str_buf[i] = '\0'; + *read_chars = i; - str_buf[i] = '\0'; - *read_chars = i; - - return str_buf; + return str_buf; } -static int skipBlanks(char *buf, size_t buf_len) { - size_t i = 0; - while (i < buf_len && isblank(buf[i])) - i++; - return i; +static int skipBlanks(char *buf, size_t buf_len) +{ + size_t i = 0; + while (i < buf_len && isblank(buf[i])) + i++; + return i; +} + +// Registers a new variable and returns its pointer +// WARNING Allocates memory on the heap, +// the variables hashmap should be freed before exit +static struct variable *createVariable(char *name) +{ + struct variable *res = malloc(sizeof(struct variable)); + res->name = name; + res->value = NULL; + + int err = hashMapInsert(variables, name, NULL, NULL); + if (!err) + errx(1, "Internal Error: Couln't add entry for '%s' in the hashmap", + name); + + return res; +} + +// Registers a new rule and returns its pointer +// WARNING Allocates memory on the heap, +// the rules hashmap should be freed before exit +static struct rule *createRule(char *name) +{ + struct rule *res = malloc(sizeof(struct rule)); + res->name = name; + res->dependencies = NULL; + res->recipe = NULL; + + int err = hashMapInsert(variables, name, NULL, NULL); + if (!err) + errx(1, "Internal Error: Couln't add entry for '%s' in the hashmap", + name); + + return res; +} + +// Parse dependencies from buf and returns them inside a chained list +static struct list *readDependencies(char *buf, size_t buf_size) +{ + size_t i = 0; + struct list *res = NULL; + while (i < buf_size) + { + i += skipBlanks(buf + i, buf_size - i); + + // Read word + size_t skipped_chars = 0; + char *dep_name = readWord(buf + i, buf_size - i, &skipped_chars); + if (skipped_chars != 0) + // Add to list + res = list_append(res, dep_name); + i += skipped_chars; + + // TODO handle comments here + + if (!isChar(buf[i]) && !isblank(buf[i]) && buf[i] != '\0') + errx(2, "Unexpected character '%c'", buf[i]); + } + + return res; +} + +static int isBlankLine(struct line *l) +{ + size_t line_size = l->length; + char *buf = l->buffer; + + size_t i = 0; + while (i < line_size) + { + if (!isblank(buf[i])) + return 0; + if (buf[i] == '#') // Comment + return 1; + } + return 1; +} + +static struct list *readRecipe() +{ + // Getline + // Skip blank lines and comments + // } // Takes a buffer containing the line to parse and it length // As well as the line number in the file for error handling -static void parseLine(char *buf, size_t line_number, size_t line_size) { +static void parseLine(struct line *current_line) +{ + char *buf = current_line->buffer; + size_t line_size = current_line->length; - size_t i = 0; - - i += skipBlanks(buf + i, line_size - i); - - // Read name - size_t skipped_chars = 0; - char *name = readWord(buf + i, line_size - i, &skipped_chars); - i += skipped_chars; - - i += skipBlanks(buf + i, line_size - i); - - // Definition type - switch (buf[i]) - { - // Variable - case '=': - return; - // Rule - case ':': - return; - default: - errx(2, "Unexpected character '%c' after declaration '%s'", buf[i], name); - - } - if (buf[i] != ':') - i++; - - // Read deps - while (i < line_size) { + size_t i = 0; i += skipBlanks(buf + i, line_size - i); - // Read word + // Read name size_t skipped_chars = 0; - char *dep_name = readWord(buf + i, line_size - i, &skipped_chars); - if (skipped_chars != 0) - printf(" %s", dep_name); - free(dep_name); + char *name = readWord(buf + i, line_size - i, &skipped_chars); i += skipped_chars; - if (!isChar(buf[i]) && !isblank(buf[i]) && buf[i] != '\0') - errx(2, "Unexpected character '%c'", buf[i]); - } + i += skipBlanks(buf + i, line_size - i); - free(name); + // Potential elements + struct rule *new_rule; + struct variable *new_variable; + + // Definition type + switch (buf[i]) + { + // Variable + case '=': + new_variable = createVariable(name); + // Read value + break; + + // Rule + case ':': + new_rule = createRule(name); + // Read dependencies + // Read recipe + break; + + // Blank line + case '\0': + case '#': + if (name != NULL) + errx(2, + "Unexpected character '%c' after declaration '%s' at line %lu", + buf[i], name, current_line->number); + else + break; + + default: + errx(2, "Unexpected character '%c' after declaration '%s' at line %lu", + buf[i], name, current_line->number); + } + + free(name); } -void make_parse(char *path) { +void makeParse(char *path) +{ + // Open file + FILE *stream = fopen(path, "r"); + if (stream == 0) + errx(2, "Could not open file: %s", path); - // Open file - FILE *stream = fopen(path, "r"); - if (stream == 0) - errx(2, "Could not open file: %s", path); + // Init hash maps + variables = hashMapInit(HASHMAP_SIZE); + rules = hashMapInit(HASHMAP_SIZE); + if (variables == NULL || rules == NULL) + errx(1, "Internal error: Failed to initiate hash maps"); - // Init hash maps - struct hash_map *variables = hashMapInit(HASHMAP_SIZE); - struct hash_map *rules = hashMapInit(HASHMAP_SIZE); + // Allocate line buffer + size_t buf_size = BUFFER_SIZE; + char *buf = malloc(sizeof(char) * buf_size); + if (buf == NULL) + errx(2, "Could not allocate more memory"); - // Allocate line buffer - size_t buf_size = BUFFER_SIZE; - char *buf = malloc(sizeof(char) * buf_size); - if (buf == NULL) - errx(2, "Could not allocate more memory"); + // Parse line by line + ssize_t nread; + struct line current_line; + current_line.number = 1; + while ((nread = getline(&buf, &buf_size, stream)) != -1) + { + if (nread == -1) + errx(1, "Could not get line %lu", current_line.number); - // Parse line by line - ssize_t nread; - size_t line_nb = 0; - while ((nread = getline(&buf, &buf_size, stream)) != -1) { - if (nread == -1) - errx(1, "Could not get line %lu", line_nb); - parseLine(buf, line_nb, nread); - } + current_line.buffer = buf; + current_line.length = nread; - free(buf); - fclose(stream); - return; + parseLine(¤t_line); + + current_line.number += 1; + } + + free(buf); + fclose(stream); + return; } // ==== Runtime ==== diff --git a/minimake/src/minimake.h b/minimake/src/minimake.h index 2cc08f9..07a560e 100644 --- a/minimake/src/minimake.h +++ b/minimake/src/minimake.h @@ -1,21 +1,35 @@ #ifndef MINIMAKE_H #define MINIMAKE_H +#include "list/list.h" + // Holds variable information // WARNING its values must be freed after use -struct variable { - char* name; - char* value; +struct variable +{ + char *name; + char *value; }; + // Holds rule information // WARNING its values must be freed after use -struct rule { +struct rule +{ char* name; - struct list* dependencies; - struct list* commands; + struct list *dependencies; + struct list *recipe; }; -int make(char* path, int flags); -void make_parse(char* path); +// Holds line information +// Exists only because of EPITA's annoying 4 parameters limit +// WARNING buffer must be freed after use +struct line +{ + char* buffer; + size_t number; + size_t length; +}; + +int make(char *path, int flags); #endif // ! MINIMAKE_H diff --git a/minimake/tests/Makefile.syntax-test b/minimake/tests/Makefile.syntax-test new file mode 100644 index 0000000..fff0427 --- /dev/null +++ b/minimake/tests/Makefile.syntax-test @@ -0,0 +1,35 @@ +SIMPLE_VAR = coucou +SIMPLE_VAR_COMMENT = the comment is gone # comment + +# the following line starts with a space then a tab + SPACES_BEFORE_TAB = var_beginning var_end + +sparse_rule: depa depb + + command 1 + + command 2 + + B = B_var_beginning B_var_end + +packed_rule: depa depb + command 1 + command 2 + +silent_rule: depa depb + @ command 1 + @command 2 + +rule_comment: depa depb # comment + +command_space_rule: depa depb + echo spaces before + echo spaces after + echo this is a # comment + +simple_rule: simple_dep + +no_dep_rule: + +variable_rule: beginning $(SIMPLE_VAR) end + echo "shouldn't be expanded: $(SIMPLE_VAR)"