This commit is contained in:
Guillem George 2025-10-25 21:56:43 +02:00
parent ad6f81afc0
commit e106a4ca66
12 changed files with 355 additions and 328 deletions

View file

@ -1,27 +0,0 @@
#include <stddef.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include "hash_map.h"
/*
** Hash the key using FNV-1a 32 bits hash algorithm.
*/
size_t hash(const char *key)
{
if (!key)
return 0;
uint32_t hash = 2166136261; // FNV offset basis
uint32_t prime = 16777619; // FNV prime
while (*key)
{
hash ^= *key;
hash *= prime;
key++;
}
return hash;
}

View file

@ -1,133 +1,172 @@
#include "hash_map.h"
#include <stddef.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
struct hash_map *hashMapInit(size_t size) {
struct pair_list **data = calloc(size, sizeof(struct pair_list));
struct hash_map *map = malloc(sizeof(struct hash_map));
map->data = data;
map->size = size;
return map;
/*
** Hash the key using FNV-1a 32 bits hash algorithm.
*/
size_t hash(const char *key)
{
if (!key)
return 0;
uint32_t hash = 2166136261; // FNV offset basis
uint32_t prime = 16777619; // FNV prime
while (*key)
{
hash ^= *key;
hash *= prime;
key++;
}
return hash;
}
struct hash_map *hashMapInit(size_t size)
{
struct pair_list **data = calloc(size, sizeof(struct pair_list));
struct hash_map *map = malloc(sizeof(struct hash_map));
map->data = data;
map->size = size;
return map;
}
bool hashMapInsert(struct hash_map *hash_map, const char *key, char *value,
bool *updated) {
if (hash_map == NULL || hash_map->data == NULL || hash_map->size == 0) {
bool *updated)
{
if (hash_map == NULL || hash_map->data == NULL || hash_map->size == 0)
{
if (updated)
*updated = false;
return false;
}
// Insert into hashmap (head)
size_t entry_hash = hash(key) % hash_map->size;
// Search if key is already present
struct pair_list *elt = hash_map->data[entry_hash];
while (elt != NULL)
{
if (strcmp(elt->key, key) == 0)
{
elt->value = value;
if (updated)
*updated = true;
return true;
}
elt = elt->next;
}
// Create entry
struct pair_list *new_entry = malloc(sizeof(struct pair_list));
if (new_entry == NULL)
return false;
new_entry->key = key;
new_entry->value = value;
new_entry->next = hash_map->data[entry_hash];
hash_map->data[entry_hash] = new_entry;
if (updated)
*updated = false;
return false;
}
*updated = false;
// Insert into hashmap (head)
size_t entry_hash = hash(key) % hash_map->size;
// Search if key is already present
struct pair_list *elt = hash_map->data[entry_hash];
while (elt != NULL) {
if (strcmp(elt->key, key) == 0) {
elt->value = value;
if (updated)
*updated = true;
return true;
}
elt = elt->next;
}
// Create entry
struct pair_list *new_entry = malloc(sizeof(struct pair_list));
if (new_entry == NULL)
return false;
new_entry->key = key;
new_entry->value = value;
new_entry->next = hash_map->data[entry_hash];
hash_map->data[entry_hash] = new_entry;
if (updated)
*updated = false;
return true;
}
void hashMapFree(struct hash_map *hash_map) {
if (hash_map == NULL)
return;
if (hash_map->data == NULL)
return;
for (size_t i = 0; i < hash_map->size; i++) {
struct pair_list *entry = hash_map->data[i];
while (entry != NULL) {
struct pair_list *next = entry->next;
free(entry);
entry = next;
}
}
free(hash_map->data);
free(hash_map);
}
void hashMapDump(struct hash_map *hash_map) {
for (size_t i = 0; i < hash_map->size; i++) {
struct pair_list *entry = hash_map->data[i];
bool is_null = true;
if (entry != NULL) {
is_null = false;
printf("%s: %s", entry->key, entry->value);
entry = entry->next;
}
while (entry != NULL) {
printf(", %s: %s", entry->key, entry->value);
entry = entry->next;
}
if (!is_null)
putchar('\n');
}
}
const char *hashMapGet(const struct hash_map *hash_map, const char *key) {
if (hash_map == NULL || hash_map->data == NULL || hash_map->size == 0)
return NULL;
size_t entry_hash = hash(key) % hash_map->size;
struct pair_list *entry = hash_map->data[entry_hash];
while (entry != NULL && strcmp(entry->key, key) != 0) {
entry = entry->next;
}
if (entry == NULL)
return NULL;
return entry->value;
}
bool hashMapRemove(struct hash_map *hash_map, const char *key) {
if (hash_map == NULL || hash_map->size == 0 || hash_map->data == NULL)
return false;
size_t entry_hash = hash(key) % hash_map->size;
struct pair_list *entry = hash_map->data[entry_hash];
if (entry == NULL)
return false;
if (strcmp(entry->key, key) == 0) {
hash_map->data[entry_hash] = entry->next;
free(entry);
return true;
}
if (entry->next == NULL)
return false;
while (strcmp(entry->next->key, key) != 0) {
entry = entry->next;
if (entry->next == NULL)
return false;
}
struct pair_list *elt_to_free = entry->next;
entry->next = entry->next->next;
free(elt_to_free);
return true;
}
void hashMapFree(struct hash_map *hash_map)
{
if (hash_map == NULL)
return;
if (hash_map->data == NULL)
return;
for (size_t i = 0; i < hash_map->size; i++)
{
struct pair_list *entry = hash_map->data[i];
while (entry != NULL)
{
struct pair_list *next = entry->next;
free(entry);
entry = next;
}
}
free(hash_map->data);
free(hash_map);
}
void hashMapDump(struct hash_map *hash_map)
{
for (size_t i = 0; i < hash_map->size; i++)
{
struct pair_list *entry = hash_map->data[i];
bool is_null = true;
if (entry != NULL)
{
is_null = false;
printf("%s: %s", entry->key, entry->value);
entry = entry->next;
}
while (entry != NULL)
{
printf(", %s: %s", entry->key, entry->value);
entry = entry->next;
}
if (!is_null)
putchar('\n');
}
}
const char *hashMapGet(const struct hash_map *hash_map, const char *key)
{
if (hash_map == NULL || hash_map->data == NULL || hash_map->size == 0)
return NULL;
size_t entry_hash = hash(key) % hash_map->size;
struct pair_list *entry = hash_map->data[entry_hash];
while (entry != NULL && strcmp(entry->key, key) != 0)
{
entry = entry->next;
}
if (entry == NULL)
return NULL;
return entry->value;
}
bool hashMapRemove(struct hash_map *hash_map, const char *key)
{
if (hash_map == NULL || hash_map->size == 0 || hash_map->data == NULL)
return false;
size_t entry_hash = hash(key) % hash_map->size;
struct pair_list *entry = hash_map->data[entry_hash];
if (entry == NULL)
return false;
if (strcmp(entry->key, key) == 0)
{
hash_map->data[entry_hash] = entry->next;
free(entry);
return true;
}
if (entry->next == NULL)
return false;
while (strcmp(entry->next->key, key) != 0)
{
entry = entry->next;
if (entry->next == NULL)
return false;
}
struct pair_list *elt_to_free = entry->next;
entry->next = entry->next->next;
free(elt_to_free);
return true;
}

View file

@ -0,0 +1,68 @@
#include "lines.h"
#include <ctype.h>
#include <err.h>
#include <stddef.h>
#include <stdlib.h>
#include <unistd.h>
// Helps to match a string (excludes blanks and special characters)
int isChar(char c)
{
return c != '\0' && !isblank(c) && c != ':' && c != '=' && c != '#';
}
int skipBlanks(char *buf, size_t buf_len)
{
size_t i = 0;
while (i < buf_len && isblank(buf[i]))
i++;
return i;
}
// Returns 1 if line is blank
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] == '#') // Comments
return 1;
i++;
}
return 1;
}
// 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
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] = '\0';
*read_chars = i;
return str_buf;
}

View file

@ -0,0 +1,25 @@
#ifndef LINES_H
#define LINES_H
#define STRING_BUFFER_SIZE 32
#include <stddef.h>
#include <stdio.h>
// Holds line information
// Exists only because of EPITA's annoying 4 parameters limit
// WARNING buffer must be freed after use
struct line
{
char* buffer; // Line content
size_t length; // Line length
size_t number; // Line number in file
FILE* file_stream; // Full file stream
};
int isChar(char c);
int isBlankLine(struct line *l);
int skipBlanks(char *buf, size_t buf_len);
char *readWord(char *buf, size_t buf_len, size_t *read_chars);
#endif // LINES_H

View file

@ -5,7 +5,7 @@
struct list
{
int data;
void* data;
struct list *next;
};
@ -13,7 +13,7 @@ struct list
** 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);
struct list *list_prepend(struct list *list, void* value);
/*
** Return the lenght of the list.
@ -38,7 +38,7 @@ void list_destroy(struct list *list);
** Return `NULL` if an error occured.
*/
// START PROTO list_append
struct list *list_append(struct list *list, int value);
struct list *list_append(struct list *list, void* value);
// END PROTO list_append
/*
@ -48,7 +48,7 @@ struct list *list_append(struct list *list, int value);
** Return `NULL` if an error occured.
*/
// START PROTO list_insert
struct list *list_insert(struct list *list, int value, size_t index);
struct list *list_insert(struct list *list, void* value, size_t index);
// END PROTO list_insert
/*
@ -64,7 +64,7 @@ struct list *list_remove(struct list *list, size_t index);
** Return `-1` if nothing is found.
*/
// START PROTO list_find
int list_find(struct list *list, int value);
int list_find(struct list *list, void* value);
// END PROTO list_find
/*

View file

@ -4,7 +4,7 @@
#include "list.h"
struct list *list_insert(struct list *list, int value, size_t index)
struct list *list_insert(struct list *list, void *value, size_t index)
{
if (list == NULL || index == 0)
{
@ -65,7 +65,7 @@ struct list *list_remove(struct list *list, size_t index)
return list;
}
int list_find(struct list *list, int value)
int list_find(struct list *list, void *value)
{
if (list == NULL)
{

View file

@ -3,7 +3,7 @@
#include "list.h"
struct list *list_prepend(struct list *list, int value)
struct list *list_prepend(struct list *list, void *value)
{
struct list *new_elt = malloc(sizeof(struct list));
if (new_elt == NULL)
@ -27,26 +27,26 @@ size_t list_length(struct list *list)
return len;
}
void list_print(struct list *list)
{
if (list == NULL)
{
return;
}
// 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;
}
}
// while (list != NULL)
// {
// if (list->next != NULL)
// {
// printf("%p ", list->data);
// }
// else
// {
// printf("%p\n", list->data);
// }
// list = list->next;
// }
// }
void list_destroy(struct list *list)
{
@ -60,7 +60,7 @@ void list_destroy(struct list *list)
}
}
struct list *list_append(struct list *list, int value)
struct list *list_append(struct list *list, void *value)
{
if (list == NULL)
{

View file

@ -5,7 +5,7 @@
void swap_next(struct list *elt)
{
int c = elt->next->data;
void *c = elt->next->data;
elt->next->data = elt->data;
elt->data = c;
}

View file

@ -1,46 +0,0 @@
#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

@ -1,31 +0,0 @@
#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,18 +1,16 @@
#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 <ctype.h>
#include <err.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "hash_map/hash_map.h"
#include "minimake.h"
#include "lines/lines.h"
#include "list/list.h"
// Static variables
struct hash_map *variables = NULL;
@ -20,49 +18,6 @@ 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 != '#';
}
// 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");
}
str_buf[i] = buf[i];
i++;
}
str_buf[i] = '\0';
*read_chars = i;
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;
}
// Registers a new variable and returns its pointer
// WARNING Allocates memory on the heap,
// the variables hashmap should be freed before exit
@ -115,8 +70,11 @@ static struct list *readDependencies(char *buf, size_t buf_size)
res = list_append(res, dep_name);
i += skipped_chars;
// TODO handle comments here
// Comments
if (buf[i] == '#')
return res;
// Unknown chars
if (!isChar(buf[i]) && !isblank(buf[i]) && buf[i] != '\0')
errx(2, "Unexpected character '%c'", buf[i]);
}
@ -124,27 +82,70 @@ static struct list *readDependencies(char *buf, size_t buf_size)
return res;
}
static int isBlankLine(struct line *l)
// Searches the following lines for recipes and returns them in the form of a
// list
// WARNING begins to read the following line
static struct list *readRecipe(struct line *l)
{
size_t line_size = l->length;
FILE *stream = l->file_stream;
char *buf = l->buffer;
size_t buf_size = BUFFER_SIZE;
size_t i = 0;
while (i < line_size)
struct list *res = NULL;
// Getline
while ((l->length = getline(&buf, &buf_size, stream)))
{
if (!isblank(buf[i]))
return 0;
if (buf[i] == '#') // Comment
return 1;
l->number++;
// Skip blank lines and comments
if (isBlankLine(l))
continue;
else if (buf[0] != '\t') // Not a recipe
{
l->buffer = buf; // Update buffer !
return res;
}
else // Add recipe to list
{
char *command = strdup(buf + 1);
if (command == NULL)
errx(1, "Internal error: couldn't duplicate string (%lu:1)",
l->number);
res = list_append(res, command);
}
}
return 1;
l->buffer = buf;
return res;
}
static struct list *readRecipe()
// Reads the value after a variable declaration
static char *readValue(char *buf, size_t buf_size)
{
// Getline
// Skip blank lines and comments
//
size_t i = 0;
size_t str_buf_size = STRING_BUFFER_SIZE;
char *str_buf = malloc(sizeof(char) * str_buf_size);
while (i < buf_size && buf[i] != '\0' && 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] = '\0';
return str_buf;
}
// Takes a buffer containing the line to parse and it length
@ -175,14 +176,16 @@ static void parseLine(struct line *current_line)
// Variable
case '=':
new_variable = createVariable(name);
// Read value
new_variable->value = readValue(buf + i, line_size);
break;
// Rule
case ':':
new_rule = createRule(name);
// Read dependencies
// Read recipe
new_rule->dependencies = readDependencies(buf + i, line_size);
new_rule->recipe = readRecipe(current_line);
parseLine(current_line);
// TODO: check for loooooops
break;
// Blank line
@ -226,6 +229,7 @@ void makeParse(char *path)
ssize_t nread;
struct line current_line;
current_line.number = 1;
current_line.file_stream = stream;
while ((nread = getline(&buf, &buf_size, stream)) != -1)
{
if (nread == -1)

View file

@ -1,6 +1,11 @@
#ifndef MINIMAKE_H
#define MINIMAKE_H
#define BUFFER_SIZE 1024
#define HASHMAP_SIZE 32
#include <stdio.h>
#include "list/list.h"
// Holds variable information
@ -20,16 +25,6 @@ struct rule
struct list *recipe;
};
// 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