This commit is contained in:
Guillem George 2025-10-25 16:16:07 +02:00
parent 750bf77846
commit ad6f81afc0
10 changed files with 765 additions and 99 deletions

1
minimake/src/list/.gitignore vendored Normal file
View file

@ -0,0 +1 @@
list.c

104
minimake/src/list/list.h Normal file
View file

@ -0,0 +1,104 @@
#ifndef LIST_H
#define LIST_H
#include <stddef.h>
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 */

View file

@ -0,0 +1,101 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#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;
}

View file

@ -0,0 +1,86 @@
#include <stdio.h>
#include <stdlib.h>
#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;
}

View file

@ -0,0 +1,138 @@
#include <stddef.h>
#include <stdlib.h>
#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;
}

View file

@ -0,0 +1,46 @@
#include "list.h"
#include <stdlib.h>
#include <string.h>
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;
}
}

View file

@ -0,0 +1,31 @@
#ifndef LIST_H
#define LIST_H
#include <stddef.h>
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 */

View file

@ -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 <ctype.h>
#include <err.h>
#include <stdio.h>
#include <stdlib.h>
#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(&current_line);
current_line.number += 1;
}
free(buf);
fclose(stream);
return;
}
// ==== Runtime ====

View file

@ -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

View file

@ -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)"