~bl4ckb0ne/boson

27db08629f62284b9d011fb8db3689f8f986f0b8 — Simon Zeni 4 months ago 35c96ca
function: include_directories & executable
2 files changed, 219 insertions(+), 71 deletions(-)

M include/interpreter.h
M src/builtin.c
M include/interpreter.h => include/interpreter.h +33 -4
@@ 11,10 11,10 @@ struct ast_function;
struct ast_string;

enum object_type {
	OBJECT_TYPE_STRING = 0 << 0,
	OBJECT_TYPE_NUMBER = 0 << 1,
	OBJECT_TYPE_BOOLEAN = 0 << 2,
	OBJECT_TYPE_ARRAY = 0 << 3,
	OBJECT_TYPE_STRING = 1 << 0,
	OBJECT_TYPE_NUMBER = 1 << 1,
	OBJECT_TYPE_BOOLEAN = 1 << 2,
	OBJECT_TYPE_ARRAY = 1 << 3,
};

struct object {


@@ 36,6 36,30 @@ struct object {

char *object_to_str(struct object *);

enum build_target_type {
	BUILD_TARGET_EXECUTABLE = 1 << 0,
	BUILD_TARGET_SHARED = 1 << 1,
	BUILD_TARGET_STATIC = 1 << 2,
};

struct build_target {
	enum build_target_type type;
	struct {
		size_t n;
		char* data;
	} name;

	struct {
		size_t n;
		char **files;
	} source;

	struct {
		size_t n;
		char **paths;
	} include;
};

struct context {
	struct {
		size_t n;


@@ 49,6 73,11 @@ struct context {
		size_t n;
		char **data;
	} project_arguments;

	struct {
		size_t n;
		struct build_target **targets;
	} build;
};

struct object *eval_string(struct ast_string *string);

M src/builtin.c => src/builtin.c +186 -67
@@ 3,6 3,7 @@
#include "options.h"
#include "log.h"

#include <assert.h>
#include <stdlib.h>
#include <stddef.h>
#include <string.h>


@@ 14,70 15,6 @@
#endif

static struct object *
project(struct context *ctx, struct ast_arguments *args)
{
	if (args->args->expressions[0]->type != EXPRESSION_STRING) {
		fatal("project: first argument must be a string literal");
	}

	if (args->args->expressions[1]->type != EXPRESSION_STRING) {
		fatal("project: second argument must be a string literal");
	}

	const char *language = args->args->expressions[1]->data.string->data;
	if (strcmp(language, "c") != 0) {
		fatal("project: %s language not supported", language);
	}

	for (size_t i = 0; i < args->kwargs->n; ++i) {
		const char *key = args->kwargs->keys[i]->data;
		struct ast_expression *value = args->kwargs->values[i];
		if (strcmp(key, "version") == 0) {
			if (value->type != EXPRESSION_STRING) {
				fatal("version must be a string");
			}
			if (ctx->version.data) {
				fatal("version has already been specified");
			}
			ctx->version.data = calloc(value->data.string->n,
					sizeof(char));
			strncpy(ctx->version.data, value->data.string->data,
					value->data.string->n);
			ctx->version.n = value->data.string->n;
		} else if (strcmp(key, "license") == 0) {
			if (value->type == EXPRESSION_ARRAY) {
				fatal("multiple licenses not supported");
			} else if (value->type != EXPRESSION_STRING) {
				fatal("license must be a string");
			}
		} else if (strcmp(key, "default_options") == 0) {
			if (value->type != EXPRESSION_ARRAY) {
				fatal("default_options must be an array");
			}

			for(size_t j = 0; j < value->data.array->n; ++j) {
				struct ast_expression *option =
					value->data.array->expressions[j];

				if (option->type != EXPRESSION_STRING) {
					fatal("option must be a string");
				}

				char k[32] = {0}, v[32] = {0};
				sscanf(option->data.string->data, "%32[^=]=%s",
						k, v);
				if (!options_parse(ctx->options, k, v)) {
					fatal("failed to parse option '%s=%s'",
							k, v);
				}
			}
		}
	}

	return NULL;
}

static struct object *
add_project_arguments(struct context *ctx, struct ast_arguments *args)
{
	const char *language = NULL;


@@ 122,6 59,80 @@ add_project_arguments(struct context *ctx, struct ast_arguments *args)
}

static struct object *
executable(struct context *ctx, struct ast_arguments *args)
{
	if (args->args->n != 2) {
		fatal("function 'executable' requires at least 2 arguments");
	}

	struct build_target *target = calloc(1, sizeof(struct build_target));
	if (!target) {
		fatal("failed to allocate executable target");
	}
	target->type = BUILD_TARGET_EXECUTABLE;

	struct object *name = eval_expression(ctx, args->args->expressions[0]);
	if (name->type != OBJECT_TYPE_STRING) {
		fatal("executable name must be a string");
	}

	target->name.n = name->string.n;
	target->name.data = calloc(target->name.n, sizeof(char));
	strncpy(target->name.data, name->string.data, target->name.n);

	struct object *sources = eval_expression(ctx, args->args->expressions[1]);
	if (sources->type == OBJECT_TYPE_STRING) {
		fatal("todo handle single source file");
	} else if (sources->type == OBJECT_TYPE_ARRAY) {
		target->source.n = sources->array.n;
		target->source.files = calloc(target->source.n, sizeof(char*));

		for (size_t i = 0; i < sources->array.n; ++i) {
			struct object *file = sources->array.objects[i];
			assert(file->type == OBJECT_TYPE_STRING);
			target->source.files[i] = calloc(file->string.n,
					sizeof(char));
			strncpy(target->source.files[i], file->string.data,
					file->string.n);
		}
	} else {
		fatal("sources must be either a string or a list of string");
	}

	for (size_t i = 0; i < args->kwargs->n; ++i) {
		const char *key = args->kwargs->keys[i]->data;
		struct object *value = eval_expression(ctx,
				args->kwargs->values[i]);
		if (strcmp(key, "include_directories") == 0) {
			assert(value->type == OBJECT_TYPE_ARRAY);
			target->include.n = value->array.n;
			target->include.paths = calloc(target->include.n,
					sizeof(char*));

			for (size_t i = 0; i < value->array.n; ++i) {
				struct object *path = value->array.objects[i];
				assert(path->type == OBJECT_TYPE_STRING);
				target->include.paths[i] = calloc(
						path->string.n,
						sizeof(char));
				strncpy(target->include.paths[i],
						path->string.data,
						path->string.n);
			}
		} else {
			fatal("executable todo handle kwarg %s", key);
		}
	}
	const size_t new_size = ctx->build.n + 1;
	ctx->build.targets = realloc(ctx->build.targets,
			new_size * sizeof(struct build_target*));
	ctx->build.targets[ctx->build.n] = target;
	ctx->build.n = new_size;

	return NULL;
}

static struct object *
files(struct context *ctx, struct ast_arguments *args)
{
	if (args->kwargs->n != 0) {


@@ 167,6 178,115 @@ files(struct context *ctx, struct ast_arguments *args)
}

static struct object *
include_directories(struct context *ctx, struct ast_arguments *args)
{
	if (args->kwargs->n != 0) {
		fatal("function 'include_directories' takes no keyword arguments");
	}

	char *cwd = calloc(PATH_MAX, sizeof(char));
	getcwd(cwd, PATH_MAX);

	struct object *includes = calloc(1, sizeof(struct object));
	if (!includes) {
		fatal("failed to allocate include directories array");
	}

	includes->type = OBJECT_TYPE_ARRAY;

	for (size_t i = 0; i < args->args->n; ++i) {
		struct ast_expression *expr = args->args->expressions[i];
		if (expr->type != EXPRESSION_STRING) {
			fatal("function 'files' takes only string arguments");
		}
		struct object *path = eval_string(expr->data.string);

		char abs_path[PATH_MAX] = {0};
		snprintf(abs_path, PATH_MAX, "%s/%s", cwd, path->string.data);

		const size_t path_size = strlen(abs_path) + 1;
		path->string.data = realloc(path->string.data,
				path_size * sizeof(char));

		strncpy(path->string.data, abs_path, path_size);
		path->string.n = path_size;

		const size_t includes_size = includes->array.n + 1;
		includes->array.objects = realloc(includes->array.objects,
			includes_size * sizeof(struct object));
		includes->array.objects[includes->array.n] = path;
		includes->array.n = includes_size;
	}

	free(cwd);
	return includes;
}

static struct object *
project(struct context *ctx, struct ast_arguments *args)
{
	if (args->args->expressions[0]->type != EXPRESSION_STRING) {
		fatal("project: first argument must be a string literal");
	}

	if (args->args->expressions[1]->type != EXPRESSION_STRING) {
		fatal("project: second argument must be a string literal");
	}

	const char *language = args->args->expressions[1]->data.string->data;
	if (strcmp(language, "c") != 0) {
		fatal("project: %s language not supported", language);
	}

	for (size_t i = 0; i < args->kwargs->n; ++i) {
		const char *key = args->kwargs->keys[i]->data;
		struct ast_expression *value = args->kwargs->values[i];
		if (strcmp(key, "version") == 0) {
			if (value->type != EXPRESSION_STRING) {
				fatal("version must be a string");
			}
			if (ctx->version.data) {
				fatal("version has already been specified");
			}
			ctx->version.data = calloc(value->data.string->n,
					sizeof(char));
			strncpy(ctx->version.data, value->data.string->data,
					value->data.string->n);
			ctx->version.n = value->data.string->n;
		} else if (strcmp(key, "license") == 0) {
			if (value->type == EXPRESSION_ARRAY) {
				fatal("multiple licenses not supported");
			} else if (value->type != EXPRESSION_STRING) {
				fatal("license must be a string");
			}
		} else if (strcmp(key, "default_options") == 0) {
			if (value->type != EXPRESSION_ARRAY) {
				fatal("default_options must be an array");
			}

			for(size_t j = 0; j < value->data.array->n; ++j) {
				struct ast_expression *option =
					value->data.array->expressions[j];

				if (option->type != EXPRESSION_STRING) {
					fatal("option must be a string");
				}

				char k[32] = {0}, v[32] = {0};
				sscanf(option->data.string->data, "%32[^=]=%s",
						k, v);
				if (!options_parse(ctx->options, k, v)) {
					fatal("failed to parse option '%s=%s'",
							k, v);
				}
			}
		}
	}

	return NULL;
}

static struct object *
todo(struct context *ctx, struct ast_arguments *args)
{
	fatal("FUNCTION NOT IMPLEMENTED");


@@ 196,7 316,7 @@ static const struct {
	{"disabler", todo},
	{"environment", todo},
	{"error", todo},
	{"executable", todo},
	{"executable", executable},
	{"files", files},
	{"find_library", todo},
	{"find_program", todo},


@@ 205,7 325,7 @@ static const struct {
	{"get_variable", todo},
	{"gettext", todo},
	{"import", todo},
	{"include_directories", todo},
	{"include_directories", include_directories},
	{"install_data", todo},
	{"install_headers", todo},
	{"install_man", todo},


@@ 237,7 357,6 @@ struct object *
eval_function(struct context *ctx, struct ast_function *function)
{
	const char *name = function->left->data;
	info("builtin function '%s'", name);
	int low = 0, mid, cmp;
	int high = (sizeof(functions) / sizeof(functions[0])) - 1;
	while (low <= high) {