Merge remote-tracking branch 'origin/main' into 90-implement-the-struct-tree-for-the-parser
This commit is contained in:
commit
64c637b860
|
@ -0,0 +1,3 @@
|
||||||
|
[submodule "dep/tomlc99"]
|
||||||
|
path = dep/tomlc99
|
||||||
|
url = https://github.com/cktan/tomlc99.git
|
|
@ -4,8 +4,6 @@ cmake_minimum_required(VERSION 3.15...3.25)
|
||||||
# Header must be included this way: #include <module/header.h>
|
# Header must be included this way: #include <module/header.h>
|
||||||
#
|
#
|
||||||
# ├─ res
|
# ├─ res
|
||||||
# ├─ dep
|
|
||||||
# │ └─ klib
|
|
||||||
# ├─ src
|
# ├─ src
|
||||||
# │ ├─ lex
|
# │ ├─ lex
|
||||||
# │ │ └─ lexer.l
|
# │ │ └─ lexer.l
|
||||||
|
@ -29,6 +27,8 @@ set(CMAKE_C_STANDARD_REQUIRED TRUE)
|
||||||
set(GEMSTONE_TEST_DIR ${PROJECT_SOURCE_DIR}/tests)
|
set(GEMSTONE_TEST_DIR ${PROJECT_SOURCE_DIR}/tests)
|
||||||
set(GEMSTONE_BINARY_DIR ${PROJECT_SOURCE_DIR}/bin)
|
set(GEMSTONE_BINARY_DIR ${PROJECT_SOURCE_DIR}/bin)
|
||||||
|
|
||||||
|
add_compile_definitions(GSC_VERSION="${PROJECT_VERSION}")
|
||||||
|
|
||||||
include(CTest)
|
include(CTest)
|
||||||
|
|
||||||
if(BUILD_TESTING)
|
if(BUILD_TESTING)
|
||||||
|
@ -63,10 +63,8 @@ set(YACC_SOURCE_FILE ${PROJECT_SOURCE_DIR}/src/yacc/parser.y)
|
||||||
set(YACC_GENERATED_SOURCE_FILE ${PROJECT_SOURCE_DIR}/src/yacc/parser.tab.c)
|
set(YACC_GENERATED_SOURCE_FILE ${PROJECT_SOURCE_DIR}/src/yacc/parser.tab.c)
|
||||||
|
|
||||||
add_custom_command(OUTPUT ${YACC_GENERATED_SOURCE_FILE}
|
add_custom_command(OUTPUT ${YACC_GENERATED_SOURCE_FILE}
|
||||||
|
|
||||||
COMMAND bison
|
COMMAND bison
|
||||||
ARGS -Wno-yacc -Wcounterexamples -d -o ${YACC_GENERATED_SOURCE_FILE} ${YACC_SOURCE_FILE}
|
ARGS -Wno-yacc -Wcounterexamples -d -o ${YACC_GENERATED_SOURCE_FILE} ${YACC_SOURCE_FILE}
|
||||||
|
|
||||||
COMMENT "generate C source file for parser"
|
COMMENT "generate C source file for parser"
|
||||||
VERBATIM)
|
VERBATIM)
|
||||||
|
|
||||||
|
@ -76,7 +74,28 @@ add_custom_command(OUTPUT ${YACC_GENERATED_SOURCE_FILE}
|
||||||
|
|
||||||
find_package(PkgConfig REQUIRED)
|
find_package(PkgConfig REQUIRED)
|
||||||
pkg_search_module(GLIB REQUIRED IMPORTED_TARGET glib-2.0)
|
pkg_search_module(GLIB REQUIRED IMPORTED_TARGET glib-2.0)
|
||||||
|
|
||||||
|
# ------------------------------------------------ #
|
||||||
|
# TOML-C99 #
|
||||||
|
# ------------------------------------------------ #
|
||||||
|
|
||||||
|
execute_process(COMMAND git submodule init -- dep/tomlc99)
|
||||||
|
|
||||||
|
add_custom_command(OUTPUT ${PROJECT_SOURCE_DIR}/dep/tomlc99/toml.c
|
||||||
|
COMMAND git
|
||||||
|
ARGS submodule update --init --recursive --checkout
|
||||||
|
COMMENT "checkout dependency TOML-C99"
|
||||||
|
VERBATIM)
|
||||||
|
|
||||||
|
add_library(tomlc99 ${PROJECT_SOURCE_DIR}/dep/tomlc99/toml.c)
|
||||||
|
set_target_properties(tomlc99
|
||||||
|
PROPERTIES
|
||||||
|
OUTPUT_NAME "toml"
|
||||||
|
ARCHIVE_OUTPUT_DIRECTORY ${GEMSTONE_BINARY_DIR}/dep)
|
||||||
|
|
||||||
|
include_directories(${PROJECT_SOURCE_DIR}/dep/tomlc99)
|
||||||
|
|
||||||
|
|
||||||
# ------------------------------------------------ #
|
# ------------------------------------------------ #
|
||||||
# Source #
|
# Source #
|
||||||
# ------------------------------------------------ #
|
# ------------------------------------------------ #
|
||||||
|
@ -109,6 +128,8 @@ set_target_properties(release
|
||||||
|
|
||||||
target_link_libraries(release PkgConfig::GLIB)
|
target_link_libraries(release PkgConfig::GLIB)
|
||||||
|
|
||||||
|
target_link_libraries(release tomlc99)
|
||||||
|
|
||||||
# FIXME: cannot compile with /O2 because of /RTC1 flag
|
# FIXME: cannot compile with /O2 because of /RTC1 flag
|
||||||
if (MSVC)
|
if (MSVC)
|
||||||
set(RELEASE_FLAGS)
|
set(RELEASE_FLAGS)
|
||||||
|
@ -127,6 +148,9 @@ target_compile_options(release PUBLIC ${FLAGS} ${RELEASE_FLAGS})
|
||||||
# add src directory as include path
|
# add src directory as include path
|
||||||
target_include_directories(release PUBLIC src)
|
target_include_directories(release PUBLIC src)
|
||||||
|
|
||||||
|
# disable assertions
|
||||||
|
target_compile_definitions(release PUBLIC NDEBUG="1")
|
||||||
|
|
||||||
# ------------------------------------------------ #
|
# ------------------------------------------------ #
|
||||||
# Target DEBUG #
|
# Target DEBUG #
|
||||||
# ------------------------------------------------ #
|
# ------------------------------------------------ #
|
||||||
|
@ -143,6 +167,8 @@ set_target_properties(debug
|
||||||
|
|
||||||
target_link_libraries(debug PkgConfig::GLIB)
|
target_link_libraries(debug PkgConfig::GLIB)
|
||||||
|
|
||||||
|
target_link_libraries(debug tomlc99)
|
||||||
|
|
||||||
if (MSVC)
|
if (MSVC)
|
||||||
set(DEBUG_FLAGS /DEBUG)
|
set(DEBUG_FLAGS /DEBUG)
|
||||||
else()
|
else()
|
||||||
|
@ -174,6 +200,8 @@ set_target_properties(check
|
||||||
|
|
||||||
target_link_libraries(check PkgConfig::GLIB)
|
target_link_libraries(check PkgConfig::GLIB)
|
||||||
|
|
||||||
|
target_link_libraries(check tomlc99)
|
||||||
|
|
||||||
if (MSVC)
|
if (MSVC)
|
||||||
set(CHECK_FLAGS /DEBUG /WX)
|
set(CHECK_FLAGS /DEBUG /WX)
|
||||||
else()
|
else()
|
||||||
|
|
|
@ -10,5 +10,7 @@ COPY --chown=lorang CMakeLists.txt /home/lorang/
|
||||||
COPY --chown=lorang run-check-test.sh /home/lorang/
|
COPY --chown=lorang run-check-test.sh /home/lorang/
|
||||||
COPY --chown=lorang .env /home/lorang/
|
COPY --chown=lorang .env /home/lorang/
|
||||||
COPY --chown=lorang run-docker-build.sh /home/lorang/
|
COPY --chown=lorang run-docker-build.sh /home/lorang/
|
||||||
|
COPY --chown=lorang dep /home/lorang/dep
|
||||||
|
COPY --chown=lorang .git /home/lorang/.git
|
||||||
|
|
||||||
RUN cmake .
|
RUN cmake .
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
Subproject commit 5221b3d3d66c25a1dc6f0372b4f824f1202fe398
|
|
@ -5,7 +5,7 @@
|
||||||
#include <sys/log.h>
|
#include <sys/log.h>
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
|
|
||||||
struct AST_Node_t *AST_new_node(enum AST_SyntaxElement_t kind, const char* value) {
|
struct AST_Node_t *AST_new_node(TokenLocation location, enum AST_SyntaxElement_t kind, const char* value) {
|
||||||
DEBUG("creating new AST node: %d \"%s\"", kind, value);
|
DEBUG("creating new AST node: %d \"%s\"", kind, value);
|
||||||
assert(kind < AST_ELEMENT_COUNT);
|
assert(kind < AST_ELEMENT_COUNT);
|
||||||
|
|
||||||
|
@ -23,6 +23,7 @@ struct AST_Node_t *AST_new_node(enum AST_SyntaxElement_t kind, const char* value
|
||||||
node->child_count = 0;
|
node->child_count = 0;
|
||||||
node->kind = kind;
|
node->kind = kind;
|
||||||
node->value = value;
|
node->value = value;
|
||||||
|
node->location = location;
|
||||||
|
|
||||||
return node;
|
return node;
|
||||||
}
|
}
|
||||||
|
@ -83,6 +84,9 @@ void AST_init() {
|
||||||
lookup_table[AST_Negate] = "-";
|
lookup_table[AST_Negate] = "-";
|
||||||
lookup_table[AST_Parameter] = "parameter";
|
lookup_table[AST_Parameter] = "parameter";
|
||||||
lookup_table[AST_ParamDecl] = "parameter-declaration";
|
lookup_table[AST_ParamDecl] = "parameter-declaration";
|
||||||
|
lookup_table[AST_AddressOf] = "address of";
|
||||||
|
lookup_table[AST_Dereference] = "deref";
|
||||||
|
lookup_table[AST_Reference] = "ref";
|
||||||
}
|
}
|
||||||
|
|
||||||
const char* AST_node_to_string(const struct AST_Node_t* node) {
|
const char* AST_node_to_string(const struct AST_Node_t* node) {
|
||||||
|
@ -114,6 +118,14 @@ const char* AST_node_to_string(const struct AST_Node_t* node) {
|
||||||
return string;
|
return string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static inline unsigned long int min(unsigned long int a, unsigned long int b) {
|
||||||
|
return a > b ? b : a;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline unsigned long int max(unsigned long int a, unsigned long int b) {
|
||||||
|
return a > b ? a : b;
|
||||||
|
}
|
||||||
|
|
||||||
void AST_push_node(struct AST_Node_t *owner, struct AST_Node_t *child) {
|
void AST_push_node(struct AST_Node_t *owner, struct AST_Node_t *child) {
|
||||||
DEBUG("Adding new node %p to %p", child, owner);
|
DEBUG("Adding new node %p to %p", child, owner);
|
||||||
assert(owner != NULL);
|
assert(owner != NULL);
|
||||||
|
@ -134,6 +146,12 @@ void AST_push_node(struct AST_Node_t *owner, struct AST_Node_t *child) {
|
||||||
PANIC("failed to allocate children array of AST node");
|
PANIC("failed to allocate children array of AST node");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
owner->location.col_end = max(owner->location.col_end, child->location.col_end);
|
||||||
|
owner->location.line_end = max(owner->location.line_end, child->location.line_end);
|
||||||
|
|
||||||
|
owner->location.col_start = min(owner->location.col_start, child->location.col_start);
|
||||||
|
owner->location.line_start = min(owner->location.line_start, child->location.line_start);
|
||||||
|
|
||||||
assert(owner->children != NULL);
|
assert(owner->children != NULL);
|
||||||
|
|
||||||
owner->children[owner->child_count++] = child;
|
owner->children[owner->child_count++] = child;
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
#define _AST_H_
|
#define _AST_H_
|
||||||
|
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
|
#include <io/files.h>
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief The type of a AST node
|
* @brief The type of a AST node
|
||||||
|
@ -75,6 +76,9 @@ enum AST_SyntaxElement_t {
|
||||||
AST_Parameter,
|
AST_Parameter,
|
||||||
AST_Qualifyier,
|
AST_Qualifyier,
|
||||||
AST_ParamDecl,
|
AST_ParamDecl,
|
||||||
|
AST_AddressOf,
|
||||||
|
AST_Dereference,
|
||||||
|
AST_Reference,
|
||||||
AST_ELEMENT_COUNT
|
AST_ELEMENT_COUNT
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -94,6 +98,8 @@ struct AST_Node_t {
|
||||||
// optional value: integer literal, string literal, ...
|
// optional value: integer literal, string literal, ...
|
||||||
const char* value;
|
const char* value;
|
||||||
|
|
||||||
|
TokenLocation location;
|
||||||
|
|
||||||
// number of child nodes ownd by this node
|
// number of child nodes ownd by this node
|
||||||
// length of children array
|
// length of children array
|
||||||
size_t child_count;
|
size_t child_count;
|
||||||
|
@ -133,7 +139,7 @@ const char* AST_node_to_string(const struct AST_Node_t* node);
|
||||||
[[maybe_unused]]
|
[[maybe_unused]]
|
||||||
[[nodiscard("pointer must be freed")]]
|
[[nodiscard("pointer must be freed")]]
|
||||||
[[gnu::returns_nonnull]]
|
[[gnu::returns_nonnull]]
|
||||||
struct AST_Node_t *AST_new_node(enum AST_SyntaxElement_t kind, const char* value);
|
struct AST_Node_t *AST_new_node(TokenLocation location, enum AST_SyntaxElement_t kind, const char* value);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Deallocate this node and all of its children.
|
* @brief Deallocate this node and all of its children.
|
||||||
|
|
|
@ -0,0 +1,419 @@
|
||||||
|
//
|
||||||
|
// Created by servostar on 5/31/24.
|
||||||
|
//
|
||||||
|
|
||||||
|
#include <cfg/opt.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <sys/log.h>
|
||||||
|
#include <io/files.h>
|
||||||
|
#include <assert.h>
|
||||||
|
#include <toml.h>
|
||||||
|
|
||||||
|
static GHashTable* args = NULL;
|
||||||
|
|
||||||
|
static void clean(void) {
|
||||||
|
GHashTableIter iter;
|
||||||
|
gpointer key, value;
|
||||||
|
g_hash_table_iter_init(&iter, args);
|
||||||
|
|
||||||
|
while (g_hash_table_iter_next(&iter, &key, &value)) {
|
||||||
|
free(value);
|
||||||
|
free(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
g_hash_table_destroy(args);
|
||||||
|
}
|
||||||
|
|
||||||
|
void parse_options(int argc, char* argv[]) {
|
||||||
|
args = g_hash_table_new(g_str_hash, g_str_equal);
|
||||||
|
|
||||||
|
atexit(clean);
|
||||||
|
|
||||||
|
for (int i = 0; i < argc; i++) {
|
||||||
|
Option* option = malloc(sizeof(Option));
|
||||||
|
option->is_opt = g_str_has_prefix(argv[i], "--");
|
||||||
|
option->string = strdup(argv[i] + (option->is_opt ? 2 : 0));
|
||||||
|
option->index = i;
|
||||||
|
option->value = NULL;
|
||||||
|
|
||||||
|
char* equals = strchr(option->string, '=');
|
||||||
|
if (equals != NULL) {
|
||||||
|
option->value = equals + 1;
|
||||||
|
*equals = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
g_hash_table_insert(args, (gpointer) option->string, (gpointer) option);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool is_option_set(const char* option) {
|
||||||
|
assert(option != NULL);
|
||||||
|
assert(args != NULL);
|
||||||
|
return g_hash_table_contains(args, option);
|
||||||
|
}
|
||||||
|
|
||||||
|
const Option* get_option(const char* option) {
|
||||||
|
if (g_hash_table_contains(args, option)) {
|
||||||
|
return g_hash_table_lookup(args, option);
|
||||||
|
}
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
GArray* get_non_options_after(const char* command) {
|
||||||
|
const Option* command_option = get_option(command);
|
||||||
|
|
||||||
|
if (command_option == NULL) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
GArray* array = g_array_new(FALSE, FALSE, sizeof(const char*));
|
||||||
|
|
||||||
|
GHashTableIter iter;
|
||||||
|
gpointer key, value;
|
||||||
|
g_hash_table_iter_init(&iter, args);
|
||||||
|
|
||||||
|
while (g_hash_table_iter_next(&iter, &key, &value)) {
|
||||||
|
Option* option = (Option*) value;
|
||||||
|
if (!option->is_opt && command_option->index < option->index) {
|
||||||
|
g_array_append_val(array, option->string);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (array->len == 0) {
|
||||||
|
g_array_free(array, FALSE);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return array;
|
||||||
|
}
|
||||||
|
|
||||||
|
TargetConfig* default_target_config() {
|
||||||
|
DEBUG("generating default target config...");
|
||||||
|
|
||||||
|
TargetConfig* config = malloc(sizeof(TargetConfig));
|
||||||
|
|
||||||
|
config->name = strdup("out");
|
||||||
|
config->print_ast = false;
|
||||||
|
config->print_asm = false;
|
||||||
|
config->print_ir = false;
|
||||||
|
config->mode = Application;
|
||||||
|
config->archive_directory = strdup("archive");
|
||||||
|
config->output_directory = strdup("bin");
|
||||||
|
config->optimization_level = 1;
|
||||||
|
config->root_module = NULL;
|
||||||
|
|
||||||
|
return config;
|
||||||
|
}
|
||||||
|
|
||||||
|
TargetConfig* default_target_config_from_args() {
|
||||||
|
DEBUG("generating default target from command line...");
|
||||||
|
|
||||||
|
TargetConfig* config = default_target_config();
|
||||||
|
|
||||||
|
if (is_option_set("print-ast")) {
|
||||||
|
config->print_ast = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (is_option_set("print-asm")) {
|
||||||
|
config->print_asm = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (is_option_set("print-ir")) {
|
||||||
|
config->print_ir = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (is_option_set("mode")) {
|
||||||
|
const Option* opt = get_option("mode");
|
||||||
|
|
||||||
|
if (opt->value != NULL) {
|
||||||
|
if (strcmp(opt->value, "app") == 0) {
|
||||||
|
config->mode = Application;
|
||||||
|
} else if (strcmp(opt->value, "lib") == 0) {
|
||||||
|
config->mode = Library;
|
||||||
|
} else {
|
||||||
|
print_message(Warning, "Invalid compilation mode: %s", opt->value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (is_option_set("output")) {
|
||||||
|
const Option* opt = get_option("output");
|
||||||
|
|
||||||
|
if (opt->value != NULL) {
|
||||||
|
config->name = strdup(opt->value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
GArray* files = get_non_options_after("compile");
|
||||||
|
|
||||||
|
if (files == NULL) {
|
||||||
|
print_message(Error, "No input file provided.");
|
||||||
|
} else {
|
||||||
|
|
||||||
|
if (files->len > 1) {
|
||||||
|
print_message(Warning, "Got more than one file to compile, using first, ignoring others.");
|
||||||
|
}
|
||||||
|
|
||||||
|
config->root_module = strdup( ((char**) files->data) [0]);
|
||||||
|
|
||||||
|
g_array_free(files, TRUE);
|
||||||
|
}
|
||||||
|
|
||||||
|
return config;
|
||||||
|
}
|
||||||
|
|
||||||
|
void print_help(void) {
|
||||||
|
DEBUG("printing help dialog...");
|
||||||
|
|
||||||
|
const char *lines[] = {
|
||||||
|
"Gemstone Compiler (c) GPL-2.0",
|
||||||
|
"Build a project target: gsc build [target]|all",
|
||||||
|
"Compile non-project file: gsc compile <target-options> [file]",
|
||||||
|
"Output information: gsc <option>",
|
||||||
|
"Target options:",
|
||||||
|
" --print-ast print resulting abstract syntax tree to a file",
|
||||||
|
" --print-asm print resulting assembly language to a file",
|
||||||
|
" --print-ir print resulting LLVM-IR to a file",
|
||||||
|
" --mode=[app|lib] set the compilation mode to either application or library",
|
||||||
|
" --output=name name of output files without extension",
|
||||||
|
"Options:",
|
||||||
|
" --verbose print logs with level information or higher",
|
||||||
|
" --debug print debug logs (if not disabled at compile time)",
|
||||||
|
" --version print the version",
|
||||||
|
" --help print this hel dialog",
|
||||||
|
};
|
||||||
|
|
||||||
|
for (unsigned int i = 0; i < sizeof(lines) / sizeof(const char *); i++) {
|
||||||
|
printf("%s\n", lines[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void get_bool(bool* boolean, const toml_table_t *table, const char* name) {
|
||||||
|
DEBUG("retrieving boolean %s", name);
|
||||||
|
|
||||||
|
const toml_datum_t datum = toml_bool_in(table, name);
|
||||||
|
|
||||||
|
if (datum.ok) {
|
||||||
|
*boolean = datum.u.b;
|
||||||
|
DEBUG("boolean has value: %d", datum.u.b);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void get_str(char** string, const toml_table_t *table, const char* name) {
|
||||||
|
DEBUG("retrieving string %s", name);
|
||||||
|
|
||||||
|
const toml_datum_t datum = toml_string_in(table, name);
|
||||||
|
|
||||||
|
if (datum.ok) {
|
||||||
|
*string = datum.u.s;
|
||||||
|
DEBUG("string has value: %s", datum.u.s);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void get_int(int* integer, const toml_table_t *table, const char* name) {
|
||||||
|
DEBUG("retrieving integer %s", name);
|
||||||
|
|
||||||
|
const toml_datum_t datum = toml_int_in(table, name);
|
||||||
|
|
||||||
|
if (datum.ok) {
|
||||||
|
*integer = (int) datum.u.i;
|
||||||
|
DEBUG("integer has value: %ld", datum.u.i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static int parse_project_table(ProjectConfig *config, const toml_table_t *project_table) {
|
||||||
|
DEBUG("parsing project table...");
|
||||||
|
|
||||||
|
// project name
|
||||||
|
get_str(&config->name, project_table, "version");
|
||||||
|
if (config->name == NULL) {
|
||||||
|
printf("Invalid project configuration: project must have a name\n\n");
|
||||||
|
return PROJECT_SEMANTIC_ERR;
|
||||||
|
}
|
||||||
|
|
||||||
|
// project version
|
||||||
|
get_str(&config->name, project_table, "name");
|
||||||
|
|
||||||
|
// author names
|
||||||
|
toml_array_t *authors = toml_array_in(project_table, "authors");
|
||||||
|
if (authors) {
|
||||||
|
config->authors = g_array_new(FALSE, FALSE, sizeof(char *));
|
||||||
|
|
||||||
|
for (int i = 0;; i++) {
|
||||||
|
toml_datum_t author = toml_string_at(authors, i);
|
||||||
|
if (!author.ok)
|
||||||
|
break;
|
||||||
|
|
||||||
|
g_array_append_val(config->authors, author.u.s);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// project description
|
||||||
|
get_str(&config->desc, project_table, "description");
|
||||||
|
// project license
|
||||||
|
get_str(&config->license, project_table, "license");
|
||||||
|
|
||||||
|
return PROJECT_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int get_mode_from_str(TargetCompilationMode* mode, const char* name) {
|
||||||
|
if (mode == NULL) {
|
||||||
|
return PROJECT_SEMANTIC_ERR;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (strcmp(name, "application") == 0) {
|
||||||
|
*mode = Application;
|
||||||
|
return PROJECT_OK;
|
||||||
|
} else if (strcmp(name, "library") == 0) {
|
||||||
|
*mode = Library;
|
||||||
|
return PROJECT_OK;
|
||||||
|
}
|
||||||
|
printf("Invalid project configuration, mode is invalid: %s\n\n", name);
|
||||||
|
return PROJECT_SEMANTIC_ERR;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int parse_target(const ProjectConfig *config, const toml_table_t *target_table, const char* name) {
|
||||||
|
DEBUG("parsing target table...");
|
||||||
|
|
||||||
|
TargetConfig* target_config = default_target_config();
|
||||||
|
|
||||||
|
target_config->name = (char*) name;
|
||||||
|
|
||||||
|
get_bool(&target_config->print_ast, target_table, "print_ast");
|
||||||
|
get_bool(&target_config->print_asm, target_table, "print_asm");
|
||||||
|
get_bool(&target_config->print_ir, target_table, "print_ir");
|
||||||
|
|
||||||
|
get_str(&target_config->root_module, target_table, "root");
|
||||||
|
get_str(&target_config->output_directory, target_table, "output");
|
||||||
|
get_str(&target_config->archive_directory, target_table, "archive");
|
||||||
|
|
||||||
|
get_int(&target_config->optimization_level, target_table, "opt");
|
||||||
|
|
||||||
|
char* mode = NULL;
|
||||||
|
get_str(&mode, target_table, "mode");
|
||||||
|
int err = get_mode_from_str(&target_config->mode, mode);
|
||||||
|
if (err != PROJECT_OK) {
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
g_hash_table_insert(config->targets, target_config->name, target_config);
|
||||||
|
|
||||||
|
return PROJECT_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int parse_targets(ProjectConfig *config, const toml_table_t *root) {
|
||||||
|
DEBUG("parsing targets of project \"%s\"", config->name);
|
||||||
|
|
||||||
|
toml_table_t *targets = toml_table_in(root, "target");
|
||||||
|
if (targets == NULL) {
|
||||||
|
print_message(Warning, "Project has no targets");
|
||||||
|
return PROJECT_SEMANTIC_ERR;
|
||||||
|
}
|
||||||
|
|
||||||
|
config->targets = g_hash_table_new(g_str_hash, g_str_equal);
|
||||||
|
|
||||||
|
for (int i = 0; i < MAX_TARGETS_PER_PROJECT; i++) {
|
||||||
|
const char *key = toml_key_in(targets, i);
|
||||||
|
|
||||||
|
if (key == NULL)
|
||||||
|
break;
|
||||||
|
|
||||||
|
toml_table_t *target = toml_table_in(targets, key);
|
||||||
|
parse_target(config, target, key);
|
||||||
|
}
|
||||||
|
|
||||||
|
return PROJECT_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
int load_project_config(ProjectConfig *config) {
|
||||||
|
DEBUG("loading project config...");
|
||||||
|
|
||||||
|
FILE *config_file = fopen(PROJECT_CONFIG_FILE, "r");
|
||||||
|
if (config_file == NULL) {
|
||||||
|
print_message(Error, "Cannot open file %s: %s", PROJECT_CONFIG_FILE, strerror(errno));
|
||||||
|
INFO("project file not found");
|
||||||
|
return PROJECT_TOML_ERR;
|
||||||
|
}
|
||||||
|
|
||||||
|
char err_buf[TOML_ERROR_MSG_BUF];
|
||||||
|
|
||||||
|
toml_table_t *conf = toml_parse_file(config_file, err_buf, sizeof(err_buf));
|
||||||
|
fclose(config_file);
|
||||||
|
|
||||||
|
if (conf == NULL) {
|
||||||
|
print_message(Error, "Invalid project configuration: %s", err_buf);
|
||||||
|
return PROJECT_SEMANTIC_ERR;
|
||||||
|
}
|
||||||
|
|
||||||
|
toml_table_t *project = toml_table_in(conf, "project");
|
||||||
|
if (project == NULL) {
|
||||||
|
print_message(Error, "Invalid project configuration: missing project table.");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (parse_project_table(config, project) == PROJECT_OK) {
|
||||||
|
return parse_targets(config, conf);
|
||||||
|
}
|
||||||
|
|
||||||
|
toml_free(conf);
|
||||||
|
return PROJECT_SEMANTIC_ERR;
|
||||||
|
}
|
||||||
|
|
||||||
|
void delete_target_config(TargetConfig* config) {
|
||||||
|
if (config->root_module != NULL) {
|
||||||
|
free(config->root_module);
|
||||||
|
}
|
||||||
|
if (config->archive_directory != NULL) {
|
||||||
|
free(config->archive_directory);
|
||||||
|
}
|
||||||
|
if (config->name != NULL) {
|
||||||
|
free(config->name);
|
||||||
|
}
|
||||||
|
if (config->output_directory != NULL) {
|
||||||
|
free(config->output_directory);
|
||||||
|
}
|
||||||
|
free(config);
|
||||||
|
}
|
||||||
|
|
||||||
|
void delete_project_config(ProjectConfig* config) {
|
||||||
|
if (config->name != NULL) {
|
||||||
|
free(config->name);
|
||||||
|
}
|
||||||
|
if (config->authors != NULL) {
|
||||||
|
g_array_free(config->authors, TRUE);
|
||||||
|
}
|
||||||
|
if (config->desc != NULL) {
|
||||||
|
free(config->desc);
|
||||||
|
}
|
||||||
|
if (config->license != NULL) {
|
||||||
|
free(config->license);
|
||||||
|
}
|
||||||
|
if (config->targets != NULL) {
|
||||||
|
GHashTableIter iter;
|
||||||
|
|
||||||
|
g_hash_table_iter_init(&iter, config->targets);
|
||||||
|
|
||||||
|
char* key;
|
||||||
|
TargetConfig* val;
|
||||||
|
while (g_hash_table_iter_next(&iter, (gpointer) &key, (gpointer) &val)) {
|
||||||
|
delete_target_config(val);
|
||||||
|
}
|
||||||
|
|
||||||
|
g_hash_table_destroy(config->targets);
|
||||||
|
}
|
||||||
|
|
||||||
|
free(config);
|
||||||
|
}
|
||||||
|
|
||||||
|
ProjectConfig* default_project_config() {
|
||||||
|
ProjectConfig* config = malloc(sizeof(ProjectConfig));
|
||||||
|
|
||||||
|
config->authors = NULL;
|
||||||
|
config->name = NULL;
|
||||||
|
config->targets = NULL;
|
||||||
|
config->license = NULL;
|
||||||
|
config->version = NULL;
|
||||||
|
config->desc = NULL;
|
||||||
|
|
||||||
|
return config;
|
||||||
|
}
|
|
@ -0,0 +1,168 @@
|
||||||
|
//
|
||||||
|
// Created by servostar on 5/31/24.
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifndef GEMSTONE_OPT_H
|
||||||
|
#define GEMSTONE_OPT_H
|
||||||
|
|
||||||
|
#include <glib.h>
|
||||||
|
|
||||||
|
#define MAX_TARGETS_PER_PROJECT 100
|
||||||
|
#define PROJECT_CONFIG_FILE "build.toml"
|
||||||
|
|
||||||
|
#define PROJECT_OK 0
|
||||||
|
#define PROJECT_TOML_ERR 1
|
||||||
|
#define PROJECT_SEMANTIC_ERR 2
|
||||||
|
|
||||||
|
#define TOML_ERROR_MSG_BUF 256
|
||||||
|
|
||||||
|
typedef enum TargetCompilationMode_t {
|
||||||
|
// output an executable binary
|
||||||
|
Application,
|
||||||
|
// output a binary object file
|
||||||
|
Library
|
||||||
|
} TargetCompilationMode;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief A target defines a source file which is to be compiled into a specific
|
||||||
|
* format. Additionally properties such as output folders can be set.
|
||||||
|
* Intermediate representations can be printed as well.
|
||||||
|
*/
|
||||||
|
typedef struct TargetConfig_t {
|
||||||
|
char* name;
|
||||||
|
bool print_ast;
|
||||||
|
bool print_asm;
|
||||||
|
bool print_ir;
|
||||||
|
// root module file which imports all submodules
|
||||||
|
// if this is NULL use the first commandline argument as root module
|
||||||
|
char* root_module;
|
||||||
|
// output directory for binaries
|
||||||
|
char* output_directory;
|
||||||
|
// output directory for intermediate representations (LLVM-IR, Assembly, ...)
|
||||||
|
char* archive_directory;
|
||||||
|
// mode of compilation
|
||||||
|
TargetCompilationMode mode;
|
||||||
|
// number between 1 and 3
|
||||||
|
int optimization_level;
|
||||||
|
} TargetConfig;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief A project is a collection of targets. Each target can point to
|
||||||
|
* very different sources. The project binds these targets together
|
||||||
|
* and specifies metadata about the authors and others.
|
||||||
|
*/
|
||||||
|
typedef struct ProjectConfig_t {
|
||||||
|
// name of the project
|
||||||
|
char* name;
|
||||||
|
// description
|
||||||
|
char* desc;
|
||||||
|
// version
|
||||||
|
char* version;
|
||||||
|
// license
|
||||||
|
char* license;
|
||||||
|
// list of authors
|
||||||
|
GArray* authors;
|
||||||
|
GHashTable* targets;
|
||||||
|
} ProjectConfig;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Represents a command line option.
|
||||||
|
*/
|
||||||
|
typedef struct Option_t {
|
||||||
|
// index in which the option appeared in the argument array
|
||||||
|
int index;
|
||||||
|
// identifier of the option
|
||||||
|
const char* string;
|
||||||
|
// option if format is equals to --option=value
|
||||||
|
const char* value;
|
||||||
|
// whether or not this option has a value
|
||||||
|
bool is_opt;
|
||||||
|
} Option;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Create the default configuration for targets.
|
||||||
|
* @return A pointer to a new target configuration.
|
||||||
|
*/
|
||||||
|
[[nodiscard("must be freed")]]
|
||||||
|
TargetConfig* default_target_config();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Create the default configuration for a project.
|
||||||
|
* Contains no targets.
|
||||||
|
* @return A pointer to a new project configuration.
|
||||||
|
*/
|
||||||
|
[[nodiscard("must be freed")]]
|
||||||
|
ProjectConfig* default_project_config();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Create a new default target configuration an write command line
|
||||||
|
* option onto the default configuration.
|
||||||
|
* @return A default config with user specified values.
|
||||||
|
*/
|
||||||
|
[[nodiscard("must be freed")]]
|
||||||
|
TargetConfig* default_target_config_from_args();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Load a project configuration from a TOML file with the name
|
||||||
|
* PROJECT_CONFIG_FILE.
|
||||||
|
* @param config Configuration to fill values from file into.
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
[[gnu::nonnull(1)]]
|
||||||
|
int load_project_config(ProjectConfig *config);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Print a help dialog to stdout.
|
||||||
|
*/
|
||||||
|
void print_help(void);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Delete a project config by deallocation.
|
||||||
|
* @param config The config to free.
|
||||||
|
*/
|
||||||
|
[[gnu::nonnull(1)]]
|
||||||
|
void delete_project_config(ProjectConfig* config);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Delete a target configuration by deallocation.
|
||||||
|
*/
|
||||||
|
[[gnu::nonnull(1)]]
|
||||||
|
void delete_target_config(TargetConfig*);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Parse the given command line arguments so that calls to
|
||||||
|
* is_option_set() and get_option() can be made safely.
|
||||||
|
* @param argc Number of arguments
|
||||||
|
* @param argv Array of arguments
|
||||||
|
*/
|
||||||
|
void parse_options(int argc, char* argv[]);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Tests whether an option was set as argument.
|
||||||
|
* @attention Requires a previous call to parse_options()
|
||||||
|
* @param option Name of option to check for.
|
||||||
|
* @return 1 if the options was set, 0 otherwise
|
||||||
|
*/
|
||||||
|
[[gnu::nonnull(1)]]
|
||||||
|
bool is_option_set(const char* option);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Returns the options information if present
|
||||||
|
* @attention Requires a previous call to parse_options()
|
||||||
|
* @param option
|
||||||
|
* @return A valid option struct or NULL if not found.
|
||||||
|
*/
|
||||||
|
[[gnu::nonnull(1)]]
|
||||||
|
const Option* get_option(const char* option);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Put a copy of all options whos index is greather than the index
|
||||||
|
* of the option specified by command.
|
||||||
|
* @param command
|
||||||
|
* @return an array of options that followed command.
|
||||||
|
*/
|
||||||
|
[[gnu::nonnull(1)]]
|
||||||
|
[[nodiscard("must be freed")]]
|
||||||
|
GArray* get_non_options_after(const char* command);
|
||||||
|
|
||||||
|
#endif //GEMSTONE_OPT_H
|
|
@ -0,0 +1,248 @@
|
||||||
|
//
|
||||||
|
// Created by servostar on 6/2/24.
|
||||||
|
//
|
||||||
|
|
||||||
|
#include <compiler.h>
|
||||||
|
#include <ast/ast.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <sys/log.h>
|
||||||
|
#include <yacc/parser.tab.h>
|
||||||
|
#include <lex/util.h>
|
||||||
|
#include <io/files.h>
|
||||||
|
#include <assert.h>
|
||||||
|
#include <cfg/opt.h>
|
||||||
|
|
||||||
|
extern void yyrestart(FILE *);
|
||||||
|
|
||||||
|
// Module AST node used by the parser for AST construction.
|
||||||
|
[[maybe_unused]]
|
||||||
|
AST_NODE_PTR root;
|
||||||
|
// Current file which gets compiled the parser.
|
||||||
|
// NOTE: due to global state no concurrent compilation is possible
|
||||||
|
// on parser level.
|
||||||
|
[[maybe_unused]]
|
||||||
|
ModuleFile *current_file;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Compile the specified file into AST
|
||||||
|
* @param ast Initialized AST module node to build program rules
|
||||||
|
* @param file The file to be processed
|
||||||
|
* @return EXIT_SUCCESS in case the parsing was success full anything lese if not
|
||||||
|
*/
|
||||||
|
[[nodiscard("AST may be in invalid state")]]
|
||||||
|
[[gnu::nonnull(1), gnu::nonnull(1)]]
|
||||||
|
static int compile_file_to_ast(AST_NODE_PTR ast, ModuleFile *file) {
|
||||||
|
assert(file->path != NULL);
|
||||||
|
assert(ast != NULL);
|
||||||
|
|
||||||
|
file->handle = fopen(file->path, "r");
|
||||||
|
|
||||||
|
if (file->handle == NULL) {
|
||||||
|
INFO("unable to open file: %s", file->path);
|
||||||
|
print_message(Error, "Cannot open file %s: %s", file->path, strerror(errno));
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
|
DEBUG("parsing file: %s", file->path);
|
||||||
|
// setup global state
|
||||||
|
root = ast;
|
||||||
|
current_file = file;
|
||||||
|
yyin = file->handle;
|
||||||
|
yyrestart(yyin);
|
||||||
|
lex_reset();
|
||||||
|
|
||||||
|
yyparse();
|
||||||
|
|
||||||
|
// clean up global state
|
||||||
|
// current_file = NULL;
|
||||||
|
root = NULL;
|
||||||
|
yyin = NULL;
|
||||||
|
|
||||||
|
return EXIT_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Setup the environment of the target.
|
||||||
|
* @param target
|
||||||
|
* @return EXIT_SUCCESS if successful EXIT_FAILURE otherwise
|
||||||
|
*/
|
||||||
|
static int setup_target_environment(const TargetConfig *target) {
|
||||||
|
DEBUG("setting up environment for target: %s", target->name);
|
||||||
|
|
||||||
|
assert(target->output_directory != NULL);
|
||||||
|
assert(target->archive_directory != NULL);
|
||||||
|
|
||||||
|
int result = create_directory(target->archive_directory);
|
||||||
|
if (result != 0 && errno != EEXIST) {
|
||||||
|
const char *message = get_last_error();
|
||||||
|
assert(message != NULL);
|
||||||
|
|
||||||
|
print_message(Error, "Unable to create directory: %s: %s", target->archive_directory, message);
|
||||||
|
free((void *) message);
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
|
result = create_directory(target->output_directory);
|
||||||
|
if (result != 0 && errno != EEXIST) {
|
||||||
|
const char *message = get_last_error();
|
||||||
|
assert(message != NULL);
|
||||||
|
|
||||||
|
print_message(Error, "Unable to create directory: %s: %s", target->output_directory, message);
|
||||||
|
free((void *) message);
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
|
return EXIT_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Print the supplied AST of the specified target to a graphviz ".gv" file
|
||||||
|
* @param ast
|
||||||
|
* @param target
|
||||||
|
*/
|
||||||
|
static void print_ast_to_file(const AST_NODE_PTR ast, const TargetConfig *target) {
|
||||||
|
assert(ast != NULL);
|
||||||
|
assert(target != NULL);
|
||||||
|
|
||||||
|
if (!target->print_ast)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// create file path to write graphviz to
|
||||||
|
const char *path = make_file_path(target->name, ".gv", 1, target->archive_directory);
|
||||||
|
|
||||||
|
FILE *output = fopen((const char *) path, "w");
|
||||||
|
if (output == NULL) {
|
||||||
|
const char *message = get_last_error();
|
||||||
|
print_message(Error, "Unable to open file for syntax tree at: %s: %s", path, message);
|
||||||
|
free((void *) message);
|
||||||
|
} else {
|
||||||
|
|
||||||
|
AST_fprint_graphviz(output, ast);
|
||||||
|
|
||||||
|
fclose(output);
|
||||||
|
}
|
||||||
|
|
||||||
|
free((void *) path);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Build the given target
|
||||||
|
* @param unit
|
||||||
|
* @param target
|
||||||
|
*/
|
||||||
|
static void build_target(ModuleFileStack *unit, const TargetConfig *target) {
|
||||||
|
print_message(Info, "Building target: %s", target->name);
|
||||||
|
|
||||||
|
AST_NODE_PTR ast = AST_new_node(empty_location(), AST_Module, NULL);
|
||||||
|
ModuleFile *file = push_file(unit, target->root_module);
|
||||||
|
|
||||||
|
if (compile_file_to_ast(ast, file) == EXIT_SUCCESS) {
|
||||||
|
if (setup_target_environment(target) == 0) {
|
||||||
|
|
||||||
|
print_ast_to_file(ast, target);
|
||||||
|
|
||||||
|
// TODO: parse AST to semantic values
|
||||||
|
// TODO: backend codegen
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
AST_delete_node(ast);
|
||||||
|
|
||||||
|
print_file_statistics(file);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Compile a single file.
|
||||||
|
* Creates a single target by the given command line arguments.
|
||||||
|
* @param unit
|
||||||
|
*/
|
||||||
|
static void compile_file(ModuleFileStack *unit) {
|
||||||
|
INFO("compiling basic files...");
|
||||||
|
|
||||||
|
TargetConfig *target = default_target_config_from_args();
|
||||||
|
|
||||||
|
if (target->root_module == NULL) {
|
||||||
|
print_message(Error, "No input file specified.");
|
||||||
|
delete_target_config(target);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
build_target(unit, target);
|
||||||
|
|
||||||
|
delete_target_config(target);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Build all project targets specified by the command line arguments.
|
||||||
|
* @param unit
|
||||||
|
* @param config
|
||||||
|
*/
|
||||||
|
static void build_project_targets(ModuleFileStack *unit, const ProjectConfig *config) {
|
||||||
|
if (is_option_set("all")) {
|
||||||
|
// build all targets in the project
|
||||||
|
GHashTableIter iter;
|
||||||
|
|
||||||
|
g_hash_table_iter_init(&iter, config->targets);
|
||||||
|
|
||||||
|
char *key;
|
||||||
|
TargetConfig *val;
|
||||||
|
while (g_hash_table_iter_next(&iter, (gpointer) &key, (gpointer) &val)) {
|
||||||
|
build_target(unit, val);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// build all targets given in the arguments
|
||||||
|
GArray* targets = get_non_options_after("build");
|
||||||
|
|
||||||
|
if (targets != NULL) {
|
||||||
|
for (guint i = 0; i < targets->len; i++) {
|
||||||
|
const char *target_name = (((Option*) targets->data) + i)->string;
|
||||||
|
|
||||||
|
if (g_hash_table_contains(config->targets, target_name)) {
|
||||||
|
build_target(unit, g_hash_table_lookup(config->targets, target_name));
|
||||||
|
} else {
|
||||||
|
print_message(Error, "Unknown target: %s", target_name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
g_array_free(targets, FALSE);
|
||||||
|
} else {
|
||||||
|
print_message(Error, "No targets specified.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Build targets from project. Configuration is provided by command line arguments.
|
||||||
|
* @param unit File storage
|
||||||
|
*/
|
||||||
|
static void build_project(ModuleFileStack *unit) {
|
||||||
|
ProjectConfig *config = default_project_config();
|
||||||
|
int err = load_project_config(config);
|
||||||
|
|
||||||
|
if (err == PROJECT_OK) {
|
||||||
|
build_project_targets(unit, config);
|
||||||
|
}
|
||||||
|
|
||||||
|
delete_project_config(config);
|
||||||
|
}
|
||||||
|
|
||||||
|
void run_compiler() {
|
||||||
|
ModuleFileStack files = new_file_stack();
|
||||||
|
|
||||||
|
if (is_option_set("build")) {
|
||||||
|
build_project(&files);
|
||||||
|
} else if (is_option_set("compile")) {
|
||||||
|
compile_file(&files);
|
||||||
|
} else {
|
||||||
|
print_message(Error, "Invalid mode of operation. Rerun with --help.");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (files.files == NULL) {
|
||||||
|
print_message(Error, "No input files, nothing to do.");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
print_unit_statistics(&files);
|
||||||
|
delete_files(&files);
|
||||||
|
}
|
|
@ -0,0 +1,13 @@
|
||||||
|
//
|
||||||
|
// Created by servostar on 6/2/24.
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifndef GEMSTONE_COMPILER_H
|
||||||
|
#define GEMSTONE_COMPILER_H
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Run the gemstone compiler with the provided command arguments.
|
||||||
|
*/
|
||||||
|
void run_compiler();
|
||||||
|
|
||||||
|
#endif //GEMSTONE_COMPILER_H
|
|
@ -0,0 +1,356 @@
|
||||||
|
//
|
||||||
|
// Created by servostar on 5/30/24.
|
||||||
|
//
|
||||||
|
|
||||||
|
#include <io/files.h>
|
||||||
|
#include <sys/log.h>
|
||||||
|
#include <assert.h>
|
||||||
|
#include <sys/col.h>
|
||||||
|
|
||||||
|
#ifdef __unix__
|
||||||
|
|
||||||
|
#include <sys/stat.h>
|
||||||
|
|
||||||
|
#define MAX_PATH_BYTES PATH_MAX
|
||||||
|
|
||||||
|
#elif defined(_WIN32) || defined(WIN32)
|
||||||
|
|
||||||
|
#include <Windows.h>
|
||||||
|
|
||||||
|
#define MAX_PATH_BYTES _MAX_PATH
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
ModuleFileStack new_file_stack() {
|
||||||
|
ModuleFileStack stack;
|
||||||
|
stack.files = NULL;
|
||||||
|
|
||||||
|
return stack;
|
||||||
|
}
|
||||||
|
|
||||||
|
ModuleFile *push_file(ModuleFileStack *stack, const char *path) {
|
||||||
|
assert(stack != NULL);
|
||||||
|
|
||||||
|
// lazy init of heap stack
|
||||||
|
if (stack->files == NULL) {
|
||||||
|
stack->files = g_array_new(FALSE, FALSE, sizeof(ModuleFile));
|
||||||
|
}
|
||||||
|
|
||||||
|
ModuleFile new_file = {
|
||||||
|
.path = path,
|
||||||
|
.handle = NULL
|
||||||
|
};
|
||||||
|
|
||||||
|
g_array_append_val(stack->files, new_file);
|
||||||
|
|
||||||
|
return ((ModuleFile *) stack->files->data) + stack->files->len - 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
void delete_files(ModuleFileStack *stack) {
|
||||||
|
for (size_t i = 0; i < stack->files->len; i++) {
|
||||||
|
const ModuleFile *file = (ModuleFile *) stack->files->data + i;
|
||||||
|
|
||||||
|
if (file->handle != NULL) {
|
||||||
|
DEBUG("closing file: %s", file->path);
|
||||||
|
fclose(file->handle);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
g_array_free(stack->files, TRUE);
|
||||||
|
DEBUG("deleted module file stack");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Number of bytes to read at once whilest
|
||||||
|
// seeking the current line in print_diagnostic()
|
||||||
|
#define SEEK_BUF_BYTES 256
|
||||||
|
|
||||||
|
static inline unsigned long int min(unsigned long int a, unsigned long int b) {
|
||||||
|
return a > b ? b : a;
|
||||||
|
}
|
||||||
|
|
||||||
|
// behaves like fgets except that it has defined behavior when n == 1
|
||||||
|
static void custom_fgets(char *buffer, size_t n, FILE *stream) {
|
||||||
|
if (n == 1) {
|
||||||
|
buffer[0] = (char) fgetc(stream);
|
||||||
|
buffer[1] = 0;
|
||||||
|
} else {
|
||||||
|
fgets(buffer, (int) n, stream);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void print_diagnostic(ModuleFile *file, TokenLocation *location, Message kind, const char *message) {
|
||||||
|
assert(file->handle != NULL);
|
||||||
|
assert(location != NULL);
|
||||||
|
assert(message != NULL);
|
||||||
|
|
||||||
|
// reset to start
|
||||||
|
rewind(file->handle);
|
||||||
|
|
||||||
|
char *buffer = alloca(SEEK_BUF_BYTES);
|
||||||
|
unsigned long int line_count = 1;
|
||||||
|
|
||||||
|
// seek to first line
|
||||||
|
while (line_count < location->line_start && fgets(buffer, SEEK_BUF_BYTES, file->handle) != NULL) {
|
||||||
|
line_count += strchr(buffer, '\n') != NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
const char *accent_color = RESET;
|
||||||
|
const char *kind_text = "unknown";
|
||||||
|
switch (kind) {
|
||||||
|
case Info:
|
||||||
|
kind_text = "info";
|
||||||
|
accent_color = CYAN;
|
||||||
|
file->statistics.info_count++;
|
||||||
|
break;
|
||||||
|
case Warning:
|
||||||
|
kind_text = "warning";
|
||||||
|
accent_color = YELLOW;
|
||||||
|
file->statistics.warning_count++;
|
||||||
|
break;
|
||||||
|
case Error:
|
||||||
|
kind_text = "error";
|
||||||
|
accent_color = RED;
|
||||||
|
file->statistics.error_count++;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
const char *absolute_path = get_absolute_path(file->path);
|
||||||
|
|
||||||
|
printf("%s%s:%ld:%s %s%s:%s %s\n", BOLD, absolute_path, location->line_start, RESET, accent_color, kind_text, RESET,
|
||||||
|
message);
|
||||||
|
|
||||||
|
free((void *) absolute_path);
|
||||||
|
|
||||||
|
const size_t lines = location->line_end - location->line_start + 1;
|
||||||
|
|
||||||
|
for (size_t l = 0; l < lines; l++) {
|
||||||
|
printf(" %4ld | ", location->line_start + l);
|
||||||
|
|
||||||
|
size_t chars = 0;
|
||||||
|
|
||||||
|
// print line before token group start
|
||||||
|
size_t limit = min(location->col_start, SEEK_BUF_BYTES);
|
||||||
|
while (limit > 1) {
|
||||||
|
custom_fgets(buffer, (int) limit, file->handle);
|
||||||
|
chars += printf("%s", buffer);
|
||||||
|
limit = min(location->col_start - chars, SEEK_BUF_BYTES);
|
||||||
|
|
||||||
|
if (strchr(buffer, '\n') != NULL) {
|
||||||
|
goto cont;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
printf("%s", accent_color);
|
||||||
|
|
||||||
|
chars = 0;
|
||||||
|
limit = min(location->col_end - location->col_start + 1, SEEK_BUF_BYTES);
|
||||||
|
while (limit > 0) {
|
||||||
|
custom_fgets(buffer, (int) limit, file->handle);
|
||||||
|
chars += printf("%s", buffer);
|
||||||
|
limit = min(location->col_end - location->col_start + 1 - chars, SEEK_BUF_BYTES);
|
||||||
|
|
||||||
|
if (strchr(buffer, '\n') != NULL) {
|
||||||
|
goto cont;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
printf("%s", RESET);
|
||||||
|
|
||||||
|
// print rest of the line
|
||||||
|
do {
|
||||||
|
custom_fgets(buffer, SEEK_BUF_BYTES, file->handle);
|
||||||
|
printf("%s", buffer);
|
||||||
|
} while (strchr(buffer, '\n') == NULL);
|
||||||
|
|
||||||
|
cont:
|
||||||
|
printf("%s", RESET);
|
||||||
|
}
|
||||||
|
|
||||||
|
printf(" | ");
|
||||||
|
for (size_t i = 1; i < location->col_start; i++) {
|
||||||
|
printf(" ");
|
||||||
|
}
|
||||||
|
|
||||||
|
printf("%s", accent_color);
|
||||||
|
printf("^");
|
||||||
|
for (size_t i = 0; i < location->col_end - location->col_start; i++) {
|
||||||
|
printf("~");
|
||||||
|
}
|
||||||
|
|
||||||
|
printf("%s\n\n", RESET);
|
||||||
|
}
|
||||||
|
|
||||||
|
TokenLocation new_location(unsigned long int line_start, unsigned long int col_start, unsigned long int line_end,
|
||||||
|
unsigned long int col_end) {
|
||||||
|
TokenLocation location;
|
||||||
|
|
||||||
|
location.line_start = line_start;
|
||||||
|
location.line_end = line_end;
|
||||||
|
location.col_start = col_start;
|
||||||
|
location.col_end = col_end;
|
||||||
|
|
||||||
|
return location;
|
||||||
|
}
|
||||||
|
|
||||||
|
TokenLocation empty_location(void) {
|
||||||
|
TokenLocation location;
|
||||||
|
|
||||||
|
location.line_start = 0;
|
||||||
|
location.line_end = 0;
|
||||||
|
location.col_start = 0;
|
||||||
|
location.col_end = 0;
|
||||||
|
|
||||||
|
return location;
|
||||||
|
}
|
||||||
|
|
||||||
|
void print_file_statistics(ModuleFile *file) {
|
||||||
|
if (file->statistics.info_count + file->statistics.warning_count + file->statistics.error_count < 1) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
printf("File %s generated ", file->path);
|
||||||
|
|
||||||
|
if (file->statistics.info_count > 0) {
|
||||||
|
printf("%ld notice(s) ", file->statistics.info_count);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (file->statistics.warning_count > 0) {
|
||||||
|
printf("%ld warning(s) ", file->statistics.warning_count);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (file->statistics.error_count > 0) {
|
||||||
|
printf("%ld error(s) ", file->statistics.error_count);
|
||||||
|
}
|
||||||
|
|
||||||
|
printf("\n\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
void print_unit_statistics(ModuleFileStack *file_stack) {
|
||||||
|
FileDiagnosticStatistics stats;
|
||||||
|
stats.info_count = 0;
|
||||||
|
stats.warning_count = 0;
|
||||||
|
stats.error_count = 0;
|
||||||
|
|
||||||
|
for (size_t i = 0; i < file_stack->files->len; i++) {
|
||||||
|
ModuleFile *file = (ModuleFile *) file_stack->files->data;
|
||||||
|
|
||||||
|
stats.info_count += file->statistics.warning_count;
|
||||||
|
stats.warning_count += file->statistics.warning_count;
|
||||||
|
stats.error_count += file->statistics.error_count;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (stats.info_count + stats.warning_count + stats.error_count < 1) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
printf("%d file(s) generated ", file_stack->files->len);
|
||||||
|
|
||||||
|
if (stats.info_count > 0) {
|
||||||
|
printf("%ld notice(s) ", stats.info_count);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (stats.warning_count > 0) {
|
||||||
|
printf("%ld warning(s) ", stats.warning_count);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (stats.error_count > 0) {
|
||||||
|
printf("%ld error(s) ", stats.error_count);
|
||||||
|
}
|
||||||
|
|
||||||
|
printf("\n\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
void print_message(Message kind, const char *fmt, ...) {
|
||||||
|
const char *accent_color = RESET;
|
||||||
|
const char *kind_text = "unknown";
|
||||||
|
switch (kind) {
|
||||||
|
case Info:
|
||||||
|
kind_text = "info";
|
||||||
|
accent_color = CYAN;
|
||||||
|
break;
|
||||||
|
case Warning:
|
||||||
|
kind_text = "warning";
|
||||||
|
accent_color = YELLOW;
|
||||||
|
break;
|
||||||
|
case Error:
|
||||||
|
kind_text = "error";
|
||||||
|
accent_color = RED;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
va_list args;
|
||||||
|
va_start(args, fmt);
|
||||||
|
|
||||||
|
printf("%s%s:%s ", accent_color, kind_text, RESET);
|
||||||
|
vprintf(fmt, args);
|
||||||
|
printf("\n");
|
||||||
|
|
||||||
|
va_end(args);
|
||||||
|
}
|
||||||
|
|
||||||
|
int create_directory(const char *path) {
|
||||||
|
assert(path != NULL);
|
||||||
|
|
||||||
|
DEBUG("creating directory: %s", path);
|
||||||
|
|
||||||
|
int result;
|
||||||
|
#ifdef __unix__
|
||||||
|
result = mkdir(path, S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH);
|
||||||
|
#elif defined(_WIN32) || defined(WIN32)
|
||||||
|
result = _mkdir(path);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
const char *get_last_error() {
|
||||||
|
return strdup(strerror(errno));
|
||||||
|
}
|
||||||
|
|
||||||
|
const char *get_absolute_path(const char *path) {
|
||||||
|
assert(path != NULL);
|
||||||
|
|
||||||
|
DEBUG("resolving absolute path of: %s", path);
|
||||||
|
|
||||||
|
#ifdef __unix__
|
||||||
|
// use unix specific function
|
||||||
|
char absolute_path[MAX_PATH_BYTES];
|
||||||
|
realpath(path, absolute_path);
|
||||||
|
#elif defined(_WIN32) || defined(WIN32)
|
||||||
|
// use Windows CRT specific function
|
||||||
|
char absolute_path[MAX_PATH_BYTES];
|
||||||
|
_fullpath(path, absolute_path, _MAX_PATH);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
return strdup(absolute_path);
|
||||||
|
}
|
||||||
|
|
||||||
|
const char* make_file_path(const char* name, const char* ext, int count, ...) {
|
||||||
|
DEBUG("building file path...");
|
||||||
|
|
||||||
|
va_list args;
|
||||||
|
va_start(args, count); // Initialize the va_list with the first variadic argument
|
||||||
|
|
||||||
|
char* path = calloc(MAX_PATH_BYTES, sizeof(char));
|
||||||
|
|
||||||
|
for (int i = 0; i < count; i++) {
|
||||||
|
const char* arg = va_arg(args, const char*);
|
||||||
|
assert(arg != NULL);
|
||||||
|
|
||||||
|
strcat(path, arg);
|
||||||
|
strcat(path, PATH_SEPARATOR);
|
||||||
|
}
|
||||||
|
|
||||||
|
va_end(args); // Clean up the va_list
|
||||||
|
|
||||||
|
if (name != NULL) {
|
||||||
|
strcat(path, name);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (name != NULL) {
|
||||||
|
strcat(path, ext);
|
||||||
|
}
|
||||||
|
|
||||||
|
return path;
|
||||||
|
}
|
|
@ -0,0 +1,156 @@
|
||||||
|
//
|
||||||
|
// Created by servostar on 5/30/24.
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifndef GEMSTONE_FILES_H
|
||||||
|
#define GEMSTONE_FILES_H
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <glib.h>
|
||||||
|
|
||||||
|
#if defined(WIN32) || defined(_WIN32)
|
||||||
|
#define PATH_SEPARATOR "\\"
|
||||||
|
#else
|
||||||
|
#define PATH_SEPARATOR "/"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
typedef struct FileDiagnosticStatistics_t {
|
||||||
|
size_t error_count;
|
||||||
|
size_t warning_count;
|
||||||
|
size_t info_count;
|
||||||
|
} FileDiagnosticStatistics;
|
||||||
|
|
||||||
|
typedef struct ModuleFile_t {
|
||||||
|
const char *path;
|
||||||
|
FILE *handle;
|
||||||
|
FileDiagnosticStatistics statistics;
|
||||||
|
} ModuleFile;
|
||||||
|
|
||||||
|
typedef struct ModuleFileStack_t {
|
||||||
|
GArray *files;
|
||||||
|
} ModuleFileStack;
|
||||||
|
|
||||||
|
typedef enum Message_t {
|
||||||
|
Info,
|
||||||
|
Warning,
|
||||||
|
Error
|
||||||
|
} Message;
|
||||||
|
|
||||||
|
typedef struct TokenLocation_t {
|
||||||
|
unsigned long int line_start;
|
||||||
|
unsigned long int col_start;
|
||||||
|
unsigned long int line_end;
|
||||||
|
unsigned long int col_end;
|
||||||
|
} TokenLocation;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Create a new, empty file stack.
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
ModuleFileStack new_file_stack();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Add a new file to the file stack.
|
||||||
|
* @attention The file handle returned will be invalid
|
||||||
|
* @param stack
|
||||||
|
* @param path
|
||||||
|
* @return A new file module
|
||||||
|
*/
|
||||||
|
[[gnu::nonnull(1), gnu::nonnull(2)]]
|
||||||
|
ModuleFile *push_file(ModuleFileStack *stack, const char *path);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Delete all files in the stack and the stack itself
|
||||||
|
* @param stack
|
||||||
|
*/
|
||||||
|
[[gnu::nonnull(1)]]
|
||||||
|
void delete_files(ModuleFileStack *stack);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new token location
|
||||||
|
* @param line_start
|
||||||
|
* @param col_start
|
||||||
|
* @param line_end
|
||||||
|
* @param col_end
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
TokenLocation new_location(unsigned long int line_start, unsigned long int col_start, unsigned long int line_end,
|
||||||
|
unsigned long int col_end);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Create a new empty location with all of its contents set to zero
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
TokenLocation empty_location(void);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Prints some diagnostic message to stdout.
|
||||||
|
* This also print the token group and the attached source as context.
|
||||||
|
* @param file
|
||||||
|
* @param location
|
||||||
|
* @param kind
|
||||||
|
* @param message
|
||||||
|
*/
|
||||||
|
[[gnu::nonnull(1), gnu::nonnull(2)]]
|
||||||
|
void print_diagnostic(ModuleFile *file, TokenLocation *location, Message kind, const char *message);
|
||||||
|
|
||||||
|
[[gnu::nonnull(2)]]
|
||||||
|
/**
|
||||||
|
* @brief Print a general message to stdout. Provides no source context like print_diagnostic()
|
||||||
|
* @param kind
|
||||||
|
* @param fmt
|
||||||
|
* @param ...
|
||||||
|
*/
|
||||||
|
void print_message(Message kind, const char *fmt, ...);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Print statistics about a specific file.
|
||||||
|
* Will print the amount of infos, warning and errors emitted during compilation.
|
||||||
|
* @param file
|
||||||
|
*/
|
||||||
|
[[gnu::nonnull(1)]]
|
||||||
|
void print_file_statistics(ModuleFile *file);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Print statistics of all files in the module stack.
|
||||||
|
* @param file_stack
|
||||||
|
*/
|
||||||
|
[[gnu::nonnull(1)]]
|
||||||
|
void print_unit_statistics(ModuleFileStack *file_stack);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Create a new directory. Will return EEXISTS in case the directory already exists.
|
||||||
|
* @param path
|
||||||
|
* @return 0 if successful, anything else otherwise
|
||||||
|
*/
|
||||||
|
[[gnu::nonnull(1)]]
|
||||||
|
int create_directory(const char* path);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Get a string describing the last error set by errno.
|
||||||
|
* @return a string that must be freed
|
||||||
|
*/
|
||||||
|
[[nodiscard("pointer must be freed")]]
|
||||||
|
const char* get_last_error();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Resolve the absolute path from a given relative path.
|
||||||
|
* @param path
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
[[gnu::nonnull(1)]]
|
||||||
|
[[nodiscard("pointer must be freed")]]
|
||||||
|
const char* get_absolute_path(const char* path);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Create a file path from a base name, extension a variable amount of directory path segments.
|
||||||
|
* @param count Amount of path segments to prepend to the basename
|
||||||
|
* @param name Basename of the file
|
||||||
|
* @param ext Extension of the file
|
||||||
|
* @param ... Path segments without path separator
|
||||||
|
* @return A relative path of a file
|
||||||
|
*/
|
||||||
|
[[nodiscard("pointer must be freed")]]
|
||||||
|
const char* make_file_path(const char* name, const char* ext, int count, ...);
|
||||||
|
|
||||||
|
#endif //GEMSTONE_FILES_H
|
|
@ -52,6 +52,7 @@
|
||||||
"float" {DEBUG("\"%s\" tokenized with \'KeyFloat\'", yytext); return(KeyFloat);};
|
"float" {DEBUG("\"%s\" tokenized with \'KeyFloat\'", yytext); return(KeyFloat);};
|
||||||
"self" {DEBUG("\"%s\" tokenized with \'KeySelf\'", yytext); return(KeySelf);};
|
"self" {DEBUG("\"%s\" tokenized with \'KeySelf\'", yytext); return(KeySelf);};
|
||||||
"as" {DEBUG("\"%s\" tokenized with \'KeyAs'", yytext); return (KeyAs);};
|
"as" {DEBUG("\"%s\" tokenized with \'KeyAs'", yytext); return (KeyAs);};
|
||||||
|
"to" {DEBUG("\"%s\" tokenized with \'KeyTo'", yytext); return (KeyTo);};
|
||||||
"short" {DEBUG("\"%s\" tokenized with \'KeyShort\'", yytext); return(KeyShort);};
|
"short" {DEBUG("\"%s\" tokenized with \'KeyShort\'", yytext); return(KeyShort);};
|
||||||
"long" {DEBUG("\"%s\" tokenized with \'KeyLong\'", yytext); return(KeyLong);};
|
"long" {DEBUG("\"%s\" tokenized with \'KeyLong\'", yytext); return(KeyLong);};
|
||||||
"half" {DEBUG("\"%s\" tokenized with \'KeyHalf\'", yytext); return(KeyHalf);};
|
"half" {DEBUG("\"%s\" tokenized with \'KeyHalf\'", yytext); return(KeyHalf);};
|
||||||
|
@ -97,7 +98,7 @@
|
||||||
yytext[yyleng - 2] = 0;
|
yytext[yyleng - 2] = 0;
|
||||||
|
|
||||||
DEBUG("\"%s\" tokenized with \'ValStr\'", yytext); yylval.string = strdup(yytext); return(ValStr);};
|
DEBUG("\"%s\" tokenized with \'ValStr\'", yytext); yylval.string = strdup(yytext); return(ValStr);};
|
||||||
\"\"\"([^\"\n]|\\\n)*\"\"\" {
|
\"\"\"[^\"]*\"\"\" {
|
||||||
yytext = yytext +3;
|
yytext = yytext +3;
|
||||||
yytext[yyleng - 6] = 0;
|
yytext[yyleng - 6] = 0;
|
||||||
|
|
||||||
|
|
|
@ -25,6 +25,16 @@ void lex_init(void) {
|
||||||
atexit(lex_deinit);
|
atexit(lex_deinit);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void lex_reset(void) {
|
||||||
|
eof = 0;
|
||||||
|
nRow = 0;
|
||||||
|
nBuffer = 0;
|
||||||
|
lBuffer = 0;
|
||||||
|
nTokenStart = 0;
|
||||||
|
nTokenLength = 0;
|
||||||
|
nTokenNextStart = 0;
|
||||||
|
}
|
||||||
|
|
||||||
void beginToken(char *t) {
|
void beginToken(char *t) {
|
||||||
nTokenStart = nTokenNextStart;
|
nTokenStart = nTokenNextStart;
|
||||||
nTokenLength = (int) strlen(t);
|
nTokenLength = (int) strlen(t);
|
||||||
|
@ -41,7 +51,7 @@ int nextChar(char *dst) {
|
||||||
|
|
||||||
if (eof)
|
if (eof)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
while (nBuffer >= lBuffer) {
|
while (nBuffer >= lBuffer) {
|
||||||
frc = getNextLine();
|
frc = getNextLine();
|
||||||
if (frc != 0) {
|
if (frc != 0) {
|
||||||
|
@ -57,7 +67,7 @@ int nextChar(char *dst) {
|
||||||
|
|
||||||
int getNextLine(void) {
|
int getNextLine(void) {
|
||||||
char *p;
|
char *p;
|
||||||
|
|
||||||
nBuffer = 0;
|
nBuffer = 0;
|
||||||
nTokenStart = -1;
|
nTokenStart = -1;
|
||||||
nTokenNextStart = 1;
|
nTokenNextStart = 1;
|
||||||
|
|
|
@ -16,6 +16,8 @@ extern char* buffer;
|
||||||
*/
|
*/
|
||||||
void lex_init(void);
|
void lex_init(void);
|
||||||
|
|
||||||
|
void lex_reset(void);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Begin counting a new token. This will fill the global struct yylloc.
|
* @brief Begin counting a new token. This will fill the global struct yylloc.
|
||||||
* @param t the text of the token. Must be null terminated
|
* @param t the text of the token. Must be null terminated
|
||||||
|
|
81
src/main.c
81
src/main.c
|
@ -1,15 +1,10 @@
|
||||||
#include <ast/ast.h>
|
#include <ast/ast.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <sys/log.h>
|
#include <sys/log.h>
|
||||||
#include <yacc/parser.tab.h>
|
|
||||||
#include <sys/col.h>
|
#include <sys/col.h>
|
||||||
#include <lex/util.h>
|
#include <lex/util.h>
|
||||||
#include <set/set.h>
|
#include <cfg/opt.h>
|
||||||
|
#include <compiler.h>
|
||||||
#define LOG_LEVEL LOG_LEVEL_DEBUG
|
|
||||||
|
|
||||||
extern FILE *yyin;
|
|
||||||
AST_NODE_PTR root;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Log a debug message to inform about beginning exit procedures
|
* @brief Log a debug message to inform about beginning exit procedures
|
||||||
|
@ -17,68 +12,50 @@ AST_NODE_PTR root;
|
||||||
*/
|
*/
|
||||||
void notify_exit(void) { DEBUG("Exiting gemstone..."); }
|
void notify_exit(void) { DEBUG("Exiting gemstone..."); }
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Closes File after compiling.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
void close_file(void) {
|
|
||||||
if (NULL != yyin) {
|
|
||||||
fclose(yyin);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Run compiler setup here
|
* @brief Run compiler setup here
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
void setup(void) {
|
void setup(int argc, char *argv[]) {
|
||||||
// setup preample
|
// setup preample
|
||||||
|
parse_options(argc, argv);
|
||||||
|
|
||||||
log_init();
|
log_init();
|
||||||
DEBUG("starting gemstone...");
|
DEBUG("starting gemstone...");
|
||||||
|
|
||||||
#if LOG_LEVEL <= LOG_LEVEL_DEBUG
|
#if LOG_LEVEL <= LOG_LEVEL_DEBUG
|
||||||
atexit(¬ify_exit);
|
atexit(¬ify_exit);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// actual setup
|
// actual setup
|
||||||
AST_init();
|
AST_init();
|
||||||
|
|
||||||
col_init();
|
col_init();
|
||||||
|
|
||||||
lex_init();
|
lex_init();
|
||||||
|
|
||||||
DEBUG("finished starting up gemstone...");
|
DEBUG("finished starting up gemstone...");
|
||||||
}
|
}
|
||||||
|
|
||||||
int main(int argc, char *argv[]) {
|
int main(int argc, char *argv[]) {
|
||||||
|
if (argc <= 1) {
|
||||||
|
print_help();
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
setup();
|
setup(argc, argv);
|
||||||
atexit(close_file);
|
|
||||||
|
|
||||||
// Check for file input as argument
|
if (is_option_set("help")) {
|
||||||
if (2 != argc) {
|
print_help();
|
||||||
INFO("Usage: %s <filename>\n", argv[0]);
|
exit(0);
|
||||||
PANIC("No File could be found");
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// filename as first argument
|
if (is_option_set("version")) {
|
||||||
char *filename = argv[1];
|
printf("Running GSC version %s\n", GSC_VERSION);
|
||||||
|
exit(0);
|
||||||
|
}
|
||||||
|
|
||||||
FILE *file = fopen(filename, "r");
|
run_compiler();
|
||||||
|
|
||||||
if (NULL == file) {
|
return 0;
|
||||||
PANIC("File couldn't be opened!");
|
|
||||||
}
|
|
||||||
yyin = file;
|
|
||||||
|
|
||||||
root = AST_new_node(AST_Module, NULL);
|
|
||||||
yyparse();
|
|
||||||
create_set(root);
|
|
||||||
FILE *output = fopen("test.txt", "w");
|
|
||||||
AST_fprint_graphviz(output, root);
|
|
||||||
fclose(output);
|
|
||||||
AST_delete_node(root);
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,14 +4,29 @@
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <sys/log.h>
|
#include <sys/log.h>
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <cfg/opt.h>
|
||||||
|
|
||||||
static struct Logger_t {
|
static struct Logger_t {
|
||||||
FILE** streams;
|
FILE** streams;
|
||||||
size_t stream_count;
|
size_t stream_count;
|
||||||
} GlobalLogger;
|
} GlobalLogger;
|
||||||
|
|
||||||
void log_init(void)
|
int runtime_log_level = LOG_LEVEL_WARNING;
|
||||||
|
|
||||||
|
void set_log_level(int level)
|
||||||
{
|
{
|
||||||
|
runtime_log_level = level;
|
||||||
|
}
|
||||||
|
|
||||||
|
void log_init()
|
||||||
|
{
|
||||||
|
if (is_option_set("verbose")) {
|
||||||
|
set_log_level(LOG_LEVEL_INFORMATION);
|
||||||
|
} else if (is_option_set("debug")) {
|
||||||
|
set_log_level(LOG_LEVEL_DEBUG);
|
||||||
|
}
|
||||||
|
|
||||||
assert(LOG_DEFAULT_STREAM != NULL);
|
assert(LOG_DEFAULT_STREAM != NULL);
|
||||||
log_register_stream(LOG_DEFAULT_STREAM);
|
log_register_stream(LOG_DEFAULT_STREAM);
|
||||||
}
|
}
|
||||||
|
@ -30,7 +45,7 @@ void log_register_stream(FILE* restrict stream)
|
||||||
if (GlobalLogger.streams == NULL)
|
if (GlobalLogger.streams == NULL)
|
||||||
{
|
{
|
||||||
PANIC("failed to allocate stream buffer");
|
PANIC("failed to allocate stream buffer");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|
|
@ -53,12 +53,22 @@ will not print.
|
||||||
#define INFO(format, ...) __LOG(LOG_STRING_INFORMATION, LOG_LEVEL_INFORMATION, format"\n", ##__VA_ARGS__)
|
#define INFO(format, ...) __LOG(LOG_STRING_INFORMATION, LOG_LEVEL_INFORMATION, format"\n", ##__VA_ARGS__)
|
||||||
#define DEBUG(format, ...) __LOG(LOG_STRING_DEBUG, LOG_LEVEL_DEBUG, format"\n", ##__VA_ARGS__)
|
#define DEBUG(format, ...) __LOG(LOG_STRING_DEBUG, LOG_LEVEL_DEBUG, format"\n", ##__VA_ARGS__)
|
||||||
|
|
||||||
|
extern int runtime_log_level;
|
||||||
|
|
||||||
#define __LOG(level, priority, format, ...) \
|
#define __LOG(level, priority, format, ...) \
|
||||||
do { \
|
do { \
|
||||||
if (LOG_LEVEL <= priority) \
|
if (LOG_LEVEL <= priority) \
|
||||||
syslog_logf(level, __FILE_NAME__, __LINE__, __func__, format, ##__VA_ARGS__); \
|
if (runtime_log_level <= priority) \
|
||||||
|
syslog_logf(level, __FILE_NAME__, __LINE__, __func__, format, ##__VA_ARGS__); \
|
||||||
} while(0)
|
} while(0)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Set the runtime log level. Must be one of: LOG_LEVEL_ERROR, LOG_LEVEL_WARNING,
|
||||||
|
* LOG_LEVEL_INFORMATION, LOG_LEVEL_DEBUG
|
||||||
|
* @param level the new log level
|
||||||
|
*/
|
||||||
|
void set_log_level(int level);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Log a message into all registered streams
|
* @brief Log a message into all registered streams
|
||||||
*
|
*
|
||||||
|
|
|
@ -5,9 +5,9 @@
|
||||||
#include <sys/log.h>
|
#include <sys/log.h>
|
||||||
#include <ast/ast.h>
|
#include <ast/ast.h>
|
||||||
#include <sys/col.h>
|
#include <sys/col.h>
|
||||||
|
#include <io/files.h>
|
||||||
extern int yylineno;
|
extern int yylineno;
|
||||||
|
extern ModuleFile* current_file;
|
||||||
|
|
||||||
int yyerror(const char*);
|
int yyerror(const char*);
|
||||||
|
|
||||||
|
@ -16,7 +16,8 @@
|
||||||
|
|
||||||
extern int yylex();
|
extern int yylex();
|
||||||
extern AST_NODE_PTR root;
|
extern AST_NODE_PTR root;
|
||||||
|
|
||||||
|
#define new_loc() new_location(yylloc.first_line, yylloc.first_column, yylloc.last_line, yylloc.last_column)
|
||||||
}
|
}
|
||||||
|
|
||||||
%union {
|
%union {
|
||||||
|
@ -73,6 +74,7 @@
|
||||||
%token KeyFloat
|
%token KeyFloat
|
||||||
%token KeySelf
|
%token KeySelf
|
||||||
%token KeyAs
|
%token KeyAs
|
||||||
|
%token KeyTo
|
||||||
%token <string> ValInt
|
%token <string> ValInt
|
||||||
%token <string> Ident
|
%token <string> Ident
|
||||||
%token <string> ValFloat
|
%token <string> ValFloat
|
||||||
|
@ -129,8 +131,8 @@
|
||||||
%left '+' '-'
|
%left '+' '-'
|
||||||
%left '*' '/'
|
%left '*' '/'
|
||||||
%left OpNot OpBitnot
|
%left OpNot OpBitnot
|
||||||
%left KeyAs
|
%left KeyAs KeyTo
|
||||||
%left '(' ')'
|
%left '(' ')' '[' ']'
|
||||||
|
|
||||||
%%
|
%%
|
||||||
program: program programbody {AST_push_node(root, $2);
|
program: program programbody {AST_push_node(root, $2);
|
||||||
|
@ -146,35 +148,43 @@ programbody: moduleimport {$$ = $1;}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
expr: ValFloat {$$ = AST_new_node(AST_Float, $1);}
|
expr: ValFloat {$$ = AST_new_node(new_loc(), AST_Float, $1);}
|
||||||
| ValInt {$$ = AST_new_node(AST_Int, $1);}
|
| ValInt {$$ = AST_new_node(new_loc(), AST_Int, $1);}
|
||||||
| ValMultistr {$$ = AST_new_node(AST_String, $1);}
|
| ValMultistr {$$ = AST_new_node(new_loc(), AST_String, $1);}
|
||||||
| ValStr {$$ = AST_new_node(AST_String, $1);}
|
| ValStr {$$ = AST_new_node(new_loc(), AST_String, $1);}
|
||||||
| Ident {$$ = AST_new_node(AST_Ident, $1);}
|
| Ident {$$ = AST_new_node(new_loc(), AST_Ident, $1);}
|
||||||
| operation {$$ = $1;}
|
| operation {$$ = $1;}
|
||||||
| boxaccess {$$ = $1;}
|
| boxaccess {$$ = $1;}
|
||||||
| boxselfaccess{$$ = $1;}
|
| boxselfaccess{$$ = $1;}
|
||||||
| typecast{$$ = $1;}
|
| typecast{$$ = $1;}
|
||||||
| reinterpretcast{$$ = $1;}
|
| reinterpretcast{$$ = $1;}
|
||||||
|
| '(' expr ')' {$$=$2;}
|
||||||
|
| KeyRef Ident {AST_NODE_PTR addrof = AST_new_node(new_loc(), AST_AddressOf, NULL);
|
||||||
|
AST_push_node(addrof, AST_new_node(new_loc(), AST_Ident, $2));
|
||||||
|
$$ = addrof;}
|
||||||
|
| expr '[' expr ']' {AST_NODE_PTR deref = AST_new_node(new_loc(), AST_Dereference, NULL);
|
||||||
|
AST_push_node(deref, $1);
|
||||||
|
AST_push_node(deref, $3);
|
||||||
|
$$ = deref;};
|
||||||
|
|
||||||
exprlist: expr ',' exprlist {AST_push_node($3, $1);
|
exprlist: expr ',' exprlist {AST_push_node($3, $1);
|
||||||
$$ = $3;}
|
$$ = $3;}
|
||||||
| expr {AST_NODE_PTR list = AST_new_node(AST_ExprList, NULL);
|
| expr {AST_NODE_PTR list = AST_new_node(new_loc(), AST_ExprList, NULL);
|
||||||
AST_push_node(list, $1);
|
AST_push_node(list, $1);
|
||||||
$$ = list;};
|
$$ = list;};
|
||||||
|
|
||||||
argumentlist: argumentlist '(' exprlist ')' {AST_push_node($1, $3);
|
argumentlist: argumentlist '(' exprlist ')' {AST_push_node($1, $3);
|
||||||
$$ = $1;}
|
$$ = $1;}
|
||||||
| '(' exprlist ')'{AST_NODE_PTR list = AST_new_node(AST_ArgList, NULL);
|
| '(' exprlist ')'{AST_NODE_PTR list = AST_new_node(new_loc(), AST_ArgList, NULL);
|
||||||
AST_push_node(list, $2);
|
AST_push_node(list, $2);
|
||||||
$$ = list;}
|
$$ = list;}
|
||||||
| argumentlist '(' ')'
|
| argumentlist '(' ')'
|
||||||
| '(' ')'{AST_NODE_PTR list = AST_new_node(AST_ArgList, NULL);
|
| '(' ')'{AST_NODE_PTR list = AST_new_node(new_loc(), AST_ArgList, NULL);
|
||||||
$$ = list;};
|
$$ = list;};
|
||||||
|
|
||||||
|
|
||||||
fundef: KeyFun Ident paramlist '{' statementlist'}' {AST_NODE_PTR fun = AST_new_node(AST_Fun, NULL);
|
fundef: KeyFun Ident paramlist '{' statementlist'}' {AST_NODE_PTR fun = AST_new_node(new_loc(), AST_Fun, NULL);
|
||||||
AST_NODE_PTR ident = AST_new_node(AST_Ident, $2);
|
AST_NODE_PTR ident = AST_new_node(new_loc(), AST_Ident, $2);
|
||||||
AST_push_node(fun, ident);
|
AST_push_node(fun, ident);
|
||||||
AST_push_node(fun, $3);
|
AST_push_node(fun, $3);
|
||||||
AST_push_node(fun, $5);
|
AST_push_node(fun, $5);
|
||||||
|
@ -184,66 +194,66 @@ fundef: KeyFun Ident paramlist '{' statementlist'}' {AST_NODE_PTR fun = AST_new_
|
||||||
paramlist: paramlist '(' params ')' {AST_push_node($1, $3);
|
paramlist: paramlist '(' params ')' {AST_push_node($1, $3);
|
||||||
$$ = $1;}
|
$$ = $1;}
|
||||||
| paramlist '(' ')'{$$ = $1;}
|
| paramlist '(' ')'{$$ = $1;}
|
||||||
| '(' params ')' {AST_NODE_PTR list = AST_new_node(AST_List, NULL);
|
| '(' params ')' {AST_NODE_PTR list = AST_new_node(new_loc(), AST_List, NULL);
|
||||||
AST_push_node(list, $2);
|
AST_push_node(list, $2);
|
||||||
$$ = list;}
|
$$ = list;}
|
||||||
| '(' ')' {$$ = AST_new_node(AST_List, NULL);};
|
| '(' ')' {$$ = AST_new_node(new_loc(), AST_List, NULL);};
|
||||||
|
|
||||||
params: IOqualifyier paramdecl ',' params {AST_NODE_PTR parameter = AST_new_node(AST_Parameter, NULL);
|
params: IOqualifyier paramdecl ',' params {AST_NODE_PTR parameter = AST_new_node(new_loc(), AST_Parameter, NULL);
|
||||||
AST_push_node(parameter, $1);
|
AST_push_node(parameter, $1);
|
||||||
AST_push_node(parameter, $2);
|
AST_push_node(parameter, $2);
|
||||||
AST_push_node($4, parameter);
|
AST_push_node($4, parameter);
|
||||||
$$ = $4;}
|
$$ = $4;}
|
||||||
| IOqualifyier paramdecl {AST_NODE_PTR list = AST_new_node(AST_ParamList, NULL);
|
| IOqualifyier paramdecl {AST_NODE_PTR list = AST_new_node(new_loc(), AST_ParamList, NULL);
|
||||||
AST_NODE_PTR parameter = AST_new_node(AST_Parameter, NULL);
|
AST_NODE_PTR parameter = AST_new_node(new_loc(), AST_Parameter, NULL);
|
||||||
AST_push_node(parameter, $1);
|
AST_push_node(parameter, $1);
|
||||||
AST_push_node(parameter, $2);
|
AST_push_node(parameter, $2);
|
||||||
AST_push_node(list, parameter);
|
AST_push_node(list, parameter);
|
||||||
$$ = list;};
|
$$ = list;};
|
||||||
|
|
||||||
IOqualifyier: KeyIn { AST_NODE_PTR in = AST_new_node(AST_Qualifyier, "in");
|
IOqualifyier: KeyIn { AST_NODE_PTR in = AST_new_node(new_loc(), AST_Qualifyier, "in");
|
||||||
AST_NODE_PTR list = AST_new_node(AST_List, NULL);
|
AST_NODE_PTR list = AST_new_node(new_loc(), AST_List, NULL);
|
||||||
AST_push_node(list, in);
|
AST_push_node(list, in);
|
||||||
$$ = list;}
|
$$ = list;}
|
||||||
| KeyOut{ AST_NODE_PTR out = AST_new_node(AST_Qualifyier, "out");
|
| KeyOut{ AST_NODE_PTR out = AST_new_node(new_loc(), AST_Qualifyier, "out");
|
||||||
AST_NODE_PTR list = AST_new_node(AST_List, NULL);
|
AST_NODE_PTR list = AST_new_node(new_loc(), AST_List, NULL);
|
||||||
AST_push_node(list, out);
|
AST_push_node(list, out);
|
||||||
$$ = list;}
|
$$ = list;}
|
||||||
| KeyIn KeyOut{ AST_NODE_PTR in = AST_new_node(AST_Qualifyier, "in");
|
| KeyIn KeyOut{ AST_NODE_PTR in = AST_new_node(new_loc(), AST_Qualifyier, "in");
|
||||||
AST_NODE_PTR out = AST_new_node(AST_Qualifyier, "out");
|
AST_NODE_PTR out = AST_new_node(new_loc(), AST_Qualifyier, "out");
|
||||||
AST_NODE_PTR list = AST_new_node(AST_List, NULL);
|
AST_NODE_PTR list = AST_new_node(new_loc(), AST_List, NULL);
|
||||||
AST_push_node(list, in);
|
AST_push_node(list, in);
|
||||||
AST_push_node(list, out);
|
AST_push_node(list, out);
|
||||||
$$ = list;}
|
$$ = list;}
|
||||||
| KeyOut KeyIn{ AST_NODE_PTR in = AST_new_node(AST_Qualifyier, "in");
|
| KeyOut KeyIn{ AST_NODE_PTR in = AST_new_node(new_loc(), AST_Qualifyier, "in");
|
||||||
AST_NODE_PTR out = AST_new_node(AST_Qualifyier, "out");
|
AST_NODE_PTR out = AST_new_node(new_loc(), AST_Qualifyier, "out");
|
||||||
AST_NODE_PTR list = AST_new_node(AST_List, NULL);
|
AST_NODE_PTR list = AST_new_node(new_loc(), AST_List, NULL);
|
||||||
AST_push_node(list, in);
|
AST_push_node(list, in);
|
||||||
AST_push_node(list, out);
|
AST_push_node(list, out);
|
||||||
$$ = list;}
|
$$ = list;}
|
||||||
| {$$ = AST_new_node(AST_List, NULL);};
|
| {$$ = AST_new_node(new_loc(), AST_List, NULL);};
|
||||||
|
|
||||||
paramdecl: type ':' Ident { AST_NODE_PTR paramdecl = AST_new_node(AST_ParamDecl, NULL);
|
paramdecl: type ':' Ident { AST_NODE_PTR paramdecl = AST_new_node(new_loc(), AST_ParamDecl, NULL);
|
||||||
AST_push_node(paramdecl, $1);
|
AST_push_node(paramdecl, $1);
|
||||||
AST_NODE_PTR ident = AST_new_node(AST_Ident, $3);
|
AST_NODE_PTR ident = AST_new_node(new_loc(), AST_Ident, $3);
|
||||||
AST_push_node(paramdecl, ident);
|
AST_push_node(paramdecl, ident);
|
||||||
$$ = paramdecl;
|
$$ = paramdecl;
|
||||||
DEBUG("Param-Declaration"); };
|
DEBUG("Param-Declaration"); };
|
||||||
|
|
||||||
box: KeyType KeyBox ':' Ident '{' boxbody '}' {AST_NODE_PTR box = AST_new_node(AST_Box, NULL);
|
box: KeyType KeyBox ':' Ident '{' boxbody '}' {AST_NODE_PTR box = AST_new_node(new_loc(), AST_Box, NULL);
|
||||||
AST_NODE_PTR ident = AST_new_node(AST_Ident, $4);
|
AST_NODE_PTR ident = AST_new_node(new_loc(), AST_Ident, $4);
|
||||||
AST_push_node(box, ident);
|
AST_push_node(box, ident);
|
||||||
AST_push_node(box, $6);
|
AST_push_node(box, $6);
|
||||||
$$ = box;
|
$$ = box;
|
||||||
DEBUG("Box"); }
|
DEBUG("Box"); }
|
||||||
| KeyType KeyBox ':' Ident '{' '}' {AST_NODE_PTR box = AST_new_node(AST_Box, NULL);
|
| KeyType KeyBox ':' Ident '{' '}' {AST_NODE_PTR box = AST_new_node(new_loc(), AST_Box, NULL);
|
||||||
AST_NODE_PTR ident = AST_new_node(AST_Ident, $4);
|
AST_NODE_PTR ident = AST_new_node(new_loc(), AST_Ident, $4);
|
||||||
AST_push_node(box, ident);
|
AST_push_node(box, ident);
|
||||||
$$ = box;};
|
$$ = box;};
|
||||||
|
|
||||||
boxbody: boxbody boxcontent {AST_push_node($1, $2);
|
boxbody: boxbody boxcontent {AST_push_node($1, $2);
|
||||||
$$ = $1;}
|
$$ = $1;}
|
||||||
| boxcontent {AST_NODE_PTR list = AST_new_node(AST_List, NULL);
|
| boxcontent {AST_NODE_PTR list = AST_new_node(new_loc(), AST_List, NULL);
|
||||||
AST_push_node(list, $1);
|
AST_push_node(list, $1);
|
||||||
$$ = list;};
|
$$ = list;};
|
||||||
|
|
||||||
|
@ -251,66 +261,66 @@ boxcontent: decl { $$ = $1;DEBUG("Box decl Content"); }
|
||||||
| definition { $$ = $1;DEBUG("Box def Content"); }
|
| definition { $$ = $1;DEBUG("Box def Content"); }
|
||||||
| fundef { $$ = $1;DEBUG("Box fun Content"); };
|
| fundef { $$ = $1;DEBUG("Box fun Content"); };
|
||||||
|
|
||||||
boxselfaccess: KeySelf '.' Ident {AST_NODE_PTR boxselfaccess = AST_new_node(AST_List, NULL);
|
boxselfaccess: KeySelf '.' Ident {AST_NODE_PTR boxselfaccess = AST_new_node(new_loc(), AST_List, NULL);
|
||||||
AST_NODE_PTR self = AST_new_node(AST_Ident, "self");
|
AST_NODE_PTR self = AST_new_node(new_loc(), AST_Ident, "self");
|
||||||
AST_push_node(boxselfaccess, self);
|
AST_push_node(boxselfaccess, self);
|
||||||
AST_NODE_PTR identlist = AST_new_node(AST_IdentList, NULL);
|
AST_NODE_PTR identlist = AST_new_node(new_loc(), AST_IdentList, NULL);
|
||||||
AST_NODE_PTR ident = AST_new_node(AST_Ident, $3);
|
AST_NODE_PTR ident = AST_new_node(new_loc(), AST_Ident, $3);
|
||||||
AST_push_node(identlist,ident);
|
AST_push_node(identlist,ident);
|
||||||
AST_push_node(boxselfaccess, identlist);
|
AST_push_node(boxselfaccess, identlist);
|
||||||
$$ = boxselfaccess;}
|
$$ = boxselfaccess;}
|
||||||
| KeySelf '.' boxaccess {AST_NODE_PTR boxselfaccess = AST_new_node(AST_List, NULL);
|
| KeySelf '.' boxaccess {AST_NODE_PTR boxselfaccess = AST_new_node(new_loc(), AST_List, NULL);
|
||||||
AST_NODE_PTR self = AST_new_node(AST_Ident, "self");
|
AST_NODE_PTR self = AST_new_node(new_loc(), AST_Ident, "self");
|
||||||
AST_push_node(boxselfaccess, self);
|
AST_push_node(boxselfaccess, self);
|
||||||
AST_push_node(boxselfaccess, $3);
|
AST_push_node(boxselfaccess, $3);
|
||||||
$$ = boxselfaccess;};
|
$$ = boxselfaccess;};
|
||||||
|
|
||||||
boxaccess: Ident '.' Ident {AST_NODE_PTR identlist = AST_new_node(AST_IdentList, NULL);
|
boxaccess: Ident '.' Ident {AST_NODE_PTR identlist = AST_new_node(new_loc(), AST_IdentList, NULL);
|
||||||
AST_NODE_PTR ident1 = AST_new_node(AST_Ident, $1);
|
AST_NODE_PTR ident1 = AST_new_node(new_loc(), AST_Ident, $1);
|
||||||
AST_NODE_PTR ident2 = AST_new_node(AST_Ident, $3);
|
AST_NODE_PTR ident2 = AST_new_node(new_loc(), AST_Ident, $3);
|
||||||
AST_push_node(identlist,ident1);
|
AST_push_node(identlist,ident1);
|
||||||
AST_push_node(identlist,ident2);
|
AST_push_node(identlist,ident2);
|
||||||
$$ = identlist;}
|
$$ = identlist;}
|
||||||
| Ident '.' boxaccess {AST_NODE_PTR ident = AST_new_node(AST_Ident, $1);
|
| Ident '.' boxaccess {AST_NODE_PTR ident = AST_new_node(new_loc(), AST_Ident, $1);
|
||||||
AST_push_node($3,ident);
|
AST_push_node($3,ident);
|
||||||
$$ = $3;};
|
$$ = $3;};
|
||||||
|
|
||||||
boxcall: boxaccess argumentlist {AST_NODE_PTR boxcall = AST_new_node(AST_Call, NULL);
|
boxcall: boxaccess argumentlist {AST_NODE_PTR boxcall = AST_new_node(new_loc(), AST_Call, NULL);
|
||||||
AST_push_node(boxcall, $1);
|
AST_push_node(boxcall, $1);
|
||||||
AST_push_node(boxcall, $2);
|
AST_push_node(boxcall, $2);
|
||||||
$$ = boxcall;}
|
$$ = boxcall;}
|
||||||
| boxselfaccess argumentlist {AST_NODE_PTR boxcall = AST_new_node(AST_Call, NULL);
|
| boxselfaccess argumentlist {AST_NODE_PTR boxcall = AST_new_node(new_loc(), AST_Call, NULL);
|
||||||
AST_push_node(boxcall, $1);
|
AST_push_node(boxcall, $1);
|
||||||
AST_push_node(boxcall, $2);
|
AST_push_node(boxcall, $2);
|
||||||
$$ = boxcall;};
|
$$ = boxcall;};
|
||||||
|
|
||||||
|
|
||||||
typecast: expr KeyAs type %prec KeyAs {AST_NODE_PTR cast = AST_new_node(AST_Typecast, NULL);
|
typecast: expr KeyAs type %prec KeyAs {AST_NODE_PTR cast = AST_new_node(new_loc(), AST_Typecast, NULL);
|
||||||
AST_push_node(cast, $1);
|
AST_push_node(cast, $1);
|
||||||
AST_push_node(cast, $3);
|
AST_push_node(cast, $3);
|
||||||
$$ = cast;
|
$$ = cast;
|
||||||
DEBUG("Type-Cast"); };
|
DEBUG("Type-Cast"); };
|
||||||
|
|
||||||
reinterpretcast: '(' type ')' expr { AST_NODE_PTR cast = AST_new_node(AST_Transmute, NULL);
|
reinterpretcast: expr KeyTo type %prec KeyTo { AST_NODE_PTR cast = AST_new_node(new_loc(), AST_Transmute, NULL);
|
||||||
AST_push_node(cast, $4);
|
AST_push_node(cast, $1);
|
||||||
AST_push_node(cast, $2);
|
AST_push_node(cast, $3);
|
||||||
$$ = cast;
|
$$ = cast;
|
||||||
DEBUG("Reinterpret-Cast"); };
|
DEBUG("Reinterpret-Cast"); };
|
||||||
|
|
||||||
|
|
||||||
funcall: Ident argumentlist {AST_NODE_PTR funcall = AST_new_node(AST_Call, NULL);
|
funcall: Ident argumentlist {AST_NODE_PTR funcall = AST_new_node(new_loc(), AST_Call, NULL);
|
||||||
AST_NODE_PTR ident = AST_new_node(AST_Ident, $1);
|
AST_NODE_PTR ident = AST_new_node(new_loc(), AST_Ident, $1);
|
||||||
AST_push_node(funcall, ident);
|
AST_push_node(funcall, ident);
|
||||||
AST_push_node(funcall, $2);
|
AST_push_node(funcall, $2);
|
||||||
$$ = funcall;
|
$$ = funcall;
|
||||||
DEBUG("Function call"); };
|
DEBUG("Function call"); };
|
||||||
|
|
||||||
moduleimport: KeyImport ValStr {$$ = AST_new_node(AST_Import, $2);
|
moduleimport: KeyImport ValStr {$$ = AST_new_node(new_loc(), AST_Import, $2);
|
||||||
DEBUG("Module-Import"); };
|
DEBUG("Module-Import"); };
|
||||||
|
|
||||||
statementlist: statementlist statement {AST_push_node($1, $2);
|
statementlist: statementlist statement {AST_push_node($1, $2);
|
||||||
$$ = $1;}
|
$$ = $1;}
|
||||||
| statement {AST_NODE_PTR list = AST_new_node(AST_StmtList, NULL);
|
| statement {AST_NODE_PTR list = AST_new_node(new_loc(), AST_StmtList, NULL);
|
||||||
AST_push_node(list, $1);
|
AST_push_node(list, $1);
|
||||||
$$ = list;};
|
$$ = list;};
|
||||||
|
|
||||||
|
@ -322,16 +332,16 @@ statement: assign {$$ = $1;}
|
||||||
| funcall {$$ = $1;}
|
| funcall {$$ = $1;}
|
||||||
| boxcall{$$ = $1;};
|
| boxcall{$$ = $1;};
|
||||||
|
|
||||||
branchif: KeyIf expr '{' statementlist '}' { AST_NODE_PTR branch = AST_new_node(AST_If, NULL);
|
branchif: KeyIf expr '{' statementlist '}' { AST_NODE_PTR branch = AST_new_node(new_loc(), AST_If, NULL);
|
||||||
AST_push_node(branch, $2);
|
AST_push_node(branch, $2);
|
||||||
AST_push_node(branch, $4);
|
AST_push_node(branch, $4);
|
||||||
$$ = branch; };
|
$$ = branch; };
|
||||||
|
|
||||||
branchelse: KeyElse '{' statementlist '}' { AST_NODE_PTR branch = AST_new_node(AST_Else, NULL);
|
branchelse: KeyElse '{' statementlist '}' { AST_NODE_PTR branch = AST_new_node(new_loc(), AST_Else, NULL);
|
||||||
AST_push_node(branch, $3);
|
AST_push_node(branch, $3);
|
||||||
$$ = branch; };
|
$$ = branch; };
|
||||||
|
|
||||||
branchelseif: KeyElse KeyIf expr '{' statementlist '}' { AST_NODE_PTR branch = AST_new_node(AST_IfElse, NULL);
|
branchelseif: KeyElse KeyIf expr '{' statementlist '}' { AST_NODE_PTR branch = AST_new_node(new_loc(), AST_IfElse, NULL);
|
||||||
AST_push_node(branch, $3);
|
AST_push_node(branch, $3);
|
||||||
AST_push_node(branch, $5);
|
AST_push_node(branch, $5);
|
||||||
$$ = branch; };
|
$$ = branch; };
|
||||||
|
@ -339,49 +349,49 @@ branchelseif: KeyElse KeyIf expr '{' statementlist '}' { AST_NODE_PTR branch = A
|
||||||
branchfull: branchhalf { $$ = $1;};
|
branchfull: branchhalf { $$ = $1;};
|
||||||
|branchhalf branchelse { AST_push_node($1 , $2);
|
|branchhalf branchelse { AST_push_node($1 , $2);
|
||||||
$$ = $1; }
|
$$ = $1; }
|
||||||
branchhalf: branchif { AST_NODE_PTR branch = AST_new_node(AST_Stmt, NULL);
|
branchhalf: branchif { AST_NODE_PTR branch = AST_new_node(new_loc(), AST_Stmt, NULL);
|
||||||
AST_push_node(branch, $1);
|
AST_push_node(branch, $1);
|
||||||
$$ = branch; }
|
$$ = branch; }
|
||||||
| branchhalf branchelseif { AST_push_node($1 , $2);
|
| branchhalf branchelseif { AST_push_node($1 , $2);
|
||||||
$$ = $1; };
|
$$ = $1; };
|
||||||
|
|
||||||
|
|
||||||
while: KeyWhile expr '{' statementlist '}' {AST_NODE_PTR whilenode = AST_new_node(AST_While, NULL);
|
while: KeyWhile expr '{' statementlist '}' {AST_NODE_PTR whilenode = AST_new_node(new_loc(), AST_While, NULL);
|
||||||
AST_push_node(whilenode, $2);
|
AST_push_node(whilenode, $2);
|
||||||
AST_push_node(whilenode, $4);
|
AST_push_node(whilenode, $4);
|
||||||
$$ = whilenode;};
|
$$ = whilenode;};
|
||||||
|
|
||||||
identlist: Ident ',' identlist {AST_NODE_PTR ident = AST_new_node(AST_Ident, $1);
|
identlist: Ident ',' identlist {AST_NODE_PTR ident = AST_new_node(new_loc(), AST_Ident, $1);
|
||||||
AST_push_node($3, ident);
|
AST_push_node($3, ident);
|
||||||
$$ = $3;}
|
$$ = $3;}
|
||||||
| Ident {AST_NODE_PTR list = AST_new_node(AST_IdentList, NULL);
|
| Ident {AST_NODE_PTR list = AST_new_node(new_loc(), AST_IdentList, NULL);
|
||||||
AST_NODE_PTR ident = AST_new_node(AST_Ident, $1);
|
AST_NODE_PTR ident = AST_new_node(new_loc(), AST_Ident, $1);
|
||||||
AST_push_node(list, ident);
|
AST_push_node(list, ident);
|
||||||
$$ = list;};
|
$$ = list;};
|
||||||
|
|
||||||
decl: type ':' identlist {AST_NODE_PTR decl = AST_new_node(AST_Decl, NULL);
|
decl: type ':' identlist {AST_NODE_PTR decl = AST_new_node(new_loc(), AST_Decl, NULL);
|
||||||
AST_push_node(decl, $1);
|
AST_push_node(decl, $1);
|
||||||
AST_push_node(decl, $3);
|
AST_push_node(decl, $3);
|
||||||
$$ = decl;}
|
$$ = decl;}
|
||||||
| storagequalifier type ':' identlist {AST_NODE_PTR decl = AST_new_node(AST_Decl, NULL);
|
| storagequalifier type ':' identlist {AST_NODE_PTR decl = AST_new_node(new_loc(), AST_Decl, NULL);
|
||||||
AST_push_node(decl, $1);
|
AST_push_node(decl, $1);
|
||||||
AST_push_node(decl, $2);
|
AST_push_node(decl, $2);
|
||||||
AST_push_node(decl, $4);
|
AST_push_node(decl, $4);
|
||||||
$$ = decl;};
|
$$ = decl;};
|
||||||
|
|
||||||
|
|
||||||
definition: decl '=' expr { AST_NODE_PTR def = AST_new_node(AST_Def, NULL);
|
definition: decl '=' expr { AST_NODE_PTR def = AST_new_node(new_loc(), AST_Def, NULL);
|
||||||
AST_push_node(def, $1);
|
AST_push_node(def, $1);
|
||||||
AST_push_node(def, $3);
|
AST_push_node(def, $3);
|
||||||
$$ = def;
|
$$ = def;
|
||||||
DEBUG("Definition"); };
|
DEBUG("Definition"); };
|
||||||
|
|
||||||
storagequalifier: KeyGlobal {$$ = AST_new_node(AST_Storage, "global");}
|
storagequalifier: KeyGlobal {$$ = AST_new_node(new_loc(), AST_Storage, "global");}
|
||||||
| KeyStatic {$$ = AST_new_node(AST_Storage, "static");}
|
| KeyStatic {$$ = AST_new_node(new_loc(), AST_Storage, "static");}
|
||||||
| KeyLocal {$$ = AST_new_node(AST_Storage, "local");};
|
| KeyLocal {$$ = AST_new_node(new_loc(), AST_Storage, "local");};
|
||||||
|
|
||||||
assign: Ident '=' expr { AST_NODE_PTR assign = AST_new_node(AST_Assign, NULL);
|
assign: Ident '=' expr { AST_NODE_PTR assign = AST_new_node(new_loc(), AST_Assign, NULL);
|
||||||
AST_NODE_PTR ident = AST_new_node(AST_Ident, $1);
|
AST_NODE_PTR ident = AST_new_node(new_loc(), AST_Ident, $1);
|
||||||
AST_push_node(assign, ident);
|
AST_push_node(assign, ident);
|
||||||
AST_push_node(assign, $3);
|
AST_push_node(assign, $3);
|
||||||
$$ = assign;
|
$$ = assign;
|
||||||
|
@ -390,200 +400,141 @@ assign: Ident '=' expr { AST_NODE_PTR assign = AST_new_node(AST_Assign, NULL);
|
||||||
| boxaccess '=' expr
|
| boxaccess '=' expr
|
||||||
| boxselfaccess '=' expr ;
|
| boxselfaccess '=' expr ;
|
||||||
|
|
||||||
sign: KeySigned {$$ = AST_new_node(AST_Sign, "signed");}
|
sign: KeySigned {$$ = AST_new_node(new_loc(), AST_Sign, "signed");}
|
||||||
| KeyUnsigned{$$ = AST_new_node(AST_Sign, "unsigned");};
|
| KeyUnsigned{$$ = AST_new_node(new_loc(), AST_Sign, "unsigned");};
|
||||||
|
|
||||||
typedef: KeyType type':' Ident {AST_NODE_PTR typeDef = AST_new_node(AST_Typedef, NULL);
|
typedef: KeyType type':' Ident {AST_NODE_PTR typeDef = AST_new_node(new_loc(), AST_Typedef, NULL);
|
||||||
AST_push_node(typeDef, $2);
|
AST_push_node(typeDef, $2);
|
||||||
AST_NODE_PTR ident = AST_new_node(AST_Ident, $4);
|
AST_NODE_PTR ident = AST_new_node(new_loc(), AST_Ident, $4);
|
||||||
AST_push_node(typeDef, ident);
|
AST_push_node(typeDef, ident);
|
||||||
$$ = typeDef;};
|
$$ = typeDef;};
|
||||||
|
|
||||||
scale: scale KeyShort {AST_NODE_PTR shortnode = AST_new_node(AST_Scale, "short");
|
scale: scale KeyShort {AST_NODE_PTR shortnode = AST_new_node(new_loc(), AST_Scale, "short");
|
||||||
AST_push_node($1, shortnode);
|
AST_push_node($1, shortnode);
|
||||||
$$ = $1;}
|
$$ = $1;}
|
||||||
| scale KeyHalf {AST_NODE_PTR shortnode = AST_new_node(AST_Scale, "half");
|
| scale KeyHalf {AST_NODE_PTR shortnode = AST_new_node(new_loc(), AST_Scale, "half");
|
||||||
AST_push_node($1, shortnode);
|
AST_push_node($1, shortnode);
|
||||||
$$ = $1;}
|
$$ = $1;}
|
||||||
| scale KeyLong {AST_NODE_PTR shortnode = AST_new_node(AST_Scale, "long");
|
| scale KeyLong {AST_NODE_PTR shortnode = AST_new_node(new_loc(), AST_Scale, "long");
|
||||||
AST_push_node($1, shortnode);
|
AST_push_node($1, shortnode);
|
||||||
$$ = $1;}
|
$$ = $1;}
|
||||||
| scale KeyDouble {AST_NODE_PTR shortnode = AST_new_node(AST_Scale, "double");
|
| scale KeyDouble {AST_NODE_PTR shortnode = AST_new_node(new_loc(), AST_Scale, "double");
|
||||||
AST_push_node($1, shortnode);
|
AST_push_node($1, shortnode);
|
||||||
$$ = $1;}
|
$$ = $1;}
|
||||||
| KeyShort {AST_NODE_PTR scale = AST_new_node(AST_List, NULL);
|
| KeyShort {AST_NODE_PTR scale = AST_new_node(new_loc(), AST_List, NULL);
|
||||||
AST_NODE_PTR shortnode = AST_new_node(AST_Scale, "short");
|
AST_NODE_PTR shortnode = AST_new_node(new_loc(), AST_Scale, "short");
|
||||||
AST_push_node(scale, shortnode);
|
AST_push_node(scale, shortnode);
|
||||||
$$ = scale;}
|
$$ = scale;}
|
||||||
| KeyHalf {AST_NODE_PTR scale = AST_new_node(AST_List, NULL);
|
| KeyHalf {AST_NODE_PTR scale = AST_new_node(new_loc(), AST_List, NULL);
|
||||||
AST_NODE_PTR shortnode = AST_new_node(AST_Scale, "half");
|
AST_NODE_PTR shortnode = AST_new_node(new_loc(), AST_Scale, "half");
|
||||||
AST_push_node(scale, shortnode);
|
AST_push_node(scale, shortnode);
|
||||||
$$ = scale;}
|
$$ = scale;}
|
||||||
| KeyLong {AST_NODE_PTR scale = AST_new_node(AST_List, NULL);
|
| KeyLong {AST_NODE_PTR scale = AST_new_node(new_loc(), AST_List, NULL);
|
||||||
AST_NODE_PTR shortnode = AST_new_node(AST_Scale, "long");
|
AST_NODE_PTR shortnode = AST_new_node(new_loc(), AST_Scale, "long");
|
||||||
AST_push_node(scale, shortnode);
|
AST_push_node(scale, shortnode);
|
||||||
$$ = scale;}
|
$$ = scale;}
|
||||||
| KeyDouble {AST_NODE_PTR scale = AST_new_node(AST_List, NULL);
|
| KeyDouble {AST_NODE_PTR scale = AST_new_node(new_loc(), AST_List, NULL);
|
||||||
AST_NODE_PTR shortnode = AST_new_node(AST_Scale, "double");
|
AST_NODE_PTR shortnode = AST_new_node(new_loc(), AST_Scale, "double");
|
||||||
AST_push_node(scale, shortnode);
|
AST_push_node(scale, shortnode);
|
||||||
$$ = scale;};
|
$$ = scale;};
|
||||||
|
|
||||||
typekind: Ident {$$ = AST_new_node(AST_Typekind, $1);}
|
typekind: Ident {$$ = AST_new_node(new_loc(), AST_Typekind, $1);}
|
||||||
| KeyInt {$$ = AST_new_node(AST_Typekind, "int");}
|
| KeyInt {$$ = AST_new_node(new_loc(), AST_Typekind, "int");}
|
||||||
| KeyFloat {$$ = AST_new_node(AST_Typekind, "float");};
|
| KeyFloat {$$ = AST_new_node(new_loc(), AST_Typekind, "float");};
|
||||||
|
|
||||||
type: typekind {AST_NODE_PTR type = AST_new_node(AST_Type, NULL);
|
type: typekind {AST_NODE_PTR type = AST_new_node(new_loc(), AST_Type, NULL);
|
||||||
AST_push_node(type, $1);
|
AST_push_node(type, $1);
|
||||||
$$ = type;}
|
$$ = type;}
|
||||||
| scale typekind {AST_NODE_PTR type = AST_new_node(AST_Type, NULL);
|
| scale typekind {AST_NODE_PTR type = AST_new_node(new_loc(), AST_Type, NULL);
|
||||||
AST_push_node(type, $1);
|
AST_push_node(type, $1);
|
||||||
AST_push_node(type, $2);
|
AST_push_node(type, $2);
|
||||||
$$ = type;}
|
$$ = type;}
|
||||||
| sign typekind {AST_NODE_PTR type = AST_new_node(AST_Type, NULL);
|
| sign typekind {AST_NODE_PTR type = AST_new_node(new_loc(), AST_Type, NULL);
|
||||||
AST_push_node(type, $1);
|
AST_push_node(type, $1);
|
||||||
AST_push_node(type, $2);
|
AST_push_node(type, $2);
|
||||||
$$ = type;}
|
$$ = type;}
|
||||||
| sign scale typekind {AST_NODE_PTR type = AST_new_node(AST_Type, NULL);
|
| sign scale typekind {AST_NODE_PTR type = AST_new_node(new_loc(), AST_Type, NULL);
|
||||||
AST_push_node(type, $1);
|
AST_push_node(type, $1);
|
||||||
AST_push_node(type, $2);
|
AST_push_node(type, $2);
|
||||||
AST_push_node(type, $3);
|
AST_push_node(type, $3);
|
||||||
$$ = type;};
|
$$ = type;}
|
||||||
|
| KeyRef type {AST_NODE_PTR reftype = AST_new_node(new_loc(), AST_Reference, NULL);
|
||||||
|
AST_push_node(reftype, $2);
|
||||||
|
$$ = reftype; };
|
||||||
|
|
||||||
operation: oparith {$$ = $1;}
|
operation: oparith {$$ = $1;}
|
||||||
| oplogic {$$ = $1;}
|
| oplogic {$$ = $1;}
|
||||||
| opbool {$$ = $1;}
|
| opbool {$$ = $1;}
|
||||||
| opbit {$$ = $1;};
|
| opbit {$$ = $1;};
|
||||||
|
|
||||||
oparith: expr '+' expr {AST_NODE_PTR add = AST_new_node(AST_Add, NULL);
|
oparith: expr '+' expr {AST_NODE_PTR add = AST_new_node(new_loc(), AST_Add, NULL);
|
||||||
AST_push_node(add, $1);
|
AST_push_node(add, $1);
|
||||||
AST_push_node(add, $3);
|
AST_push_node(add, $3);
|
||||||
$$ = add;}
|
$$ = add;}
|
||||||
| expr '-' expr {AST_NODE_PTR subtract = AST_new_node(AST_Sub, NULL);
|
| expr '-' expr {AST_NODE_PTR subtract = AST_new_node(new_loc(), AST_Sub, NULL);
|
||||||
AST_push_node(subtract, $1);
|
AST_push_node(subtract, $1);
|
||||||
AST_push_node(subtract, $3);
|
AST_push_node(subtract, $3);
|
||||||
$$ = subtract;}
|
$$ = subtract;}
|
||||||
| expr '*' expr {AST_NODE_PTR mul = AST_new_node(AST_Mul, NULL);
|
| expr '*' expr {AST_NODE_PTR mul = AST_new_node(new_loc(), AST_Mul, NULL);
|
||||||
AST_push_node(mul, $1);
|
AST_push_node(mul, $1);
|
||||||
AST_push_node(mul, $3);
|
AST_push_node(mul, $3);
|
||||||
$$ = mul;}
|
$$ = mul;}
|
||||||
| expr '/' expr {AST_NODE_PTR div = AST_new_node(AST_Div, NULL);
|
| expr '/' expr {AST_NODE_PTR div = AST_new_node(new_loc(), AST_Div, NULL);
|
||||||
AST_push_node(div, $1);
|
AST_push_node(div, $1);
|
||||||
AST_push_node(div, $3);
|
AST_push_node(div, $3);
|
||||||
$$ = div;}
|
$$ = div;}
|
||||||
| '-' expr %prec '*'{AST_NODE_PTR negator = AST_new_node(AST_Negate, NULL);
|
| '-' expr %prec '*'{AST_NODE_PTR negator = AST_new_node(new_loc(), AST_Negate, NULL);
|
||||||
AST_push_node(negator, $2);
|
AST_push_node(negator, $2);
|
||||||
$$ = negator;};
|
$$ = negator;};
|
||||||
|
|
||||||
oplogic: expr OpEquals expr {AST_NODE_PTR equals = AST_new_node(AST_Eq, NULL);
|
oplogic: expr OpEquals expr {AST_NODE_PTR equals = AST_new_node(new_loc(), AST_Eq, NULL);
|
||||||
AST_push_node(equals, $1);
|
AST_push_node(equals, $1);
|
||||||
AST_push_node(equals, $3);
|
AST_push_node(equals, $3);
|
||||||
$$ = equals;}
|
$$ = equals;}
|
||||||
| expr '<' expr {AST_NODE_PTR less = AST_new_node(AST_Less, NULL);
|
| expr '<' expr {AST_NODE_PTR less = AST_new_node(new_loc(), AST_Less, NULL);
|
||||||
AST_push_node(less, $1);
|
AST_push_node(less, $1);
|
||||||
AST_push_node(less, $3);
|
AST_push_node(less, $3);
|
||||||
$$ = less;}
|
$$ = less;}
|
||||||
| expr '>' expr{AST_NODE_PTR greater = AST_new_node(AST_Greater, NULL);
|
| expr '>' expr{AST_NODE_PTR greater = AST_new_node(new_loc(), AST_Greater, NULL);
|
||||||
AST_push_node(greater, $1);
|
AST_push_node(greater, $1);
|
||||||
AST_push_node(greater, $3);
|
AST_push_node(greater, $3);
|
||||||
$$ = greater;};
|
$$ = greater;};
|
||||||
|
|
||||||
opbool: expr OpAnd expr {AST_NODE_PTR and = AST_new_node(AST_BoolAnd, NULL);
|
opbool: expr OpAnd expr {AST_NODE_PTR and = AST_new_node(new_loc(), AST_BoolAnd, NULL);
|
||||||
AST_push_node(and, $1);
|
AST_push_node(and, $1);
|
||||||
AST_push_node(and, $3);
|
AST_push_node(and, $3);
|
||||||
$$ = and;}
|
$$ = and;}
|
||||||
| expr OpOr expr{AST_NODE_PTR or = AST_new_node(AST_BoolOr, NULL);
|
| expr OpOr expr{AST_NODE_PTR or = AST_new_node(new_loc(), AST_BoolOr, NULL);
|
||||||
AST_push_node(or, $1);
|
AST_push_node(or, $1);
|
||||||
AST_push_node(or, $3);
|
AST_push_node(or, $3);
|
||||||
$$ = or;}
|
$$ = or;}
|
||||||
| expr OpXor expr{AST_NODE_PTR xor = AST_new_node(AST_BoolXor, NULL);
|
| expr OpXor expr{AST_NODE_PTR xor = AST_new_node(new_loc(), AST_BoolXor, NULL);
|
||||||
AST_push_node(xor, $1);
|
AST_push_node(xor, $1);
|
||||||
AST_push_node(xor, $3);
|
AST_push_node(xor, $3);
|
||||||
$$ = xor;}
|
$$ = xor;}
|
||||||
| OpNot expr %prec OpAnd{AST_NODE_PTR not = AST_new_node(AST_BoolNot, NULL);
|
| OpNot expr %prec OpAnd{AST_NODE_PTR not = AST_new_node(new_loc(), AST_BoolNot, NULL);
|
||||||
AST_push_node(not, $2);
|
AST_push_node(not, $2);
|
||||||
$$ = not;};
|
$$ = not;};
|
||||||
|
|
||||||
opbit: expr OpBitand expr {AST_NODE_PTR and = AST_new_node(AST_BitAnd, NULL);
|
opbit: expr OpBitand expr {AST_NODE_PTR and = AST_new_node(new_loc(), AST_BitAnd, NULL);
|
||||||
AST_push_node(and, $1);
|
AST_push_node(and, $1);
|
||||||
AST_push_node(and, $3);
|
AST_push_node(and, $3);
|
||||||
$$ = and;}
|
$$ = and;}
|
||||||
| expr OpBitor expr{AST_NODE_PTR or = AST_new_node(AST_BitOr, NULL);
|
| expr OpBitor expr{AST_NODE_PTR or = AST_new_node(new_loc(), AST_BitOr, NULL);
|
||||||
AST_push_node(or, $1);
|
AST_push_node(or, $1);
|
||||||
AST_push_node(or, $3);
|
AST_push_node(or, $3);
|
||||||
$$ = or;}
|
$$ = or;}
|
||||||
| expr OpBitxor expr{AST_NODE_PTR xor = AST_new_node(AST_BitXor, NULL);
|
| expr OpBitxor expr{AST_NODE_PTR xor = AST_new_node(new_loc(), AST_BitXor, NULL);
|
||||||
AST_push_node(xor, $1);
|
AST_push_node(xor, $1);
|
||||||
AST_push_node(xor, $3);
|
AST_push_node(xor, $3);
|
||||||
$$ = xor;}
|
$$ = xor;}
|
||||||
| OpBitnot expr %prec OpBitand{AST_NODE_PTR not = AST_new_node(AST_BitNot, NULL);
|
| OpBitnot expr %prec OpBitand{AST_NODE_PTR not = AST_new_node(new_loc(), AST_BitNot, NULL);
|
||||||
AST_push_node(not, $2);
|
AST_push_node(not, $2);
|
||||||
$$ = not;};
|
$$ = not;};
|
||||||
%%
|
%%
|
||||||
|
|
||||||
|
|
||||||
const char* ERROR = "error";
|
|
||||||
const char* WARNING = "warning";
|
|
||||||
const char* NOTE = "note";
|
|
||||||
|
|
||||||
int print_message(const char* kind, const char* message) {
|
|
||||||
// number of characters written
|
|
||||||
int char_count = 0;
|
|
||||||
// highlight to use
|
|
||||||
char* HIGHLIGHT = CYAN;
|
|
||||||
|
|
||||||
// convert message kind into color
|
|
||||||
if (kind == ERROR) {
|
|
||||||
HIGHLIGHT = RED;
|
|
||||||
} else if (kind == WARNING) {
|
|
||||||
HIGHLIGHT = YELLOW;
|
|
||||||
}
|
|
||||||
|
|
||||||
// print message
|
|
||||||
char_count += printf("%sfilename:%d:%d%s:%s%s %s: %s%s\n", BOLD, yylloc.first_line, yylloc.first_column, RESET, HIGHLIGHT, BOLD, kind, RESET, message);
|
|
||||||
|
|
||||||
// print line in which error occurred
|
|
||||||
|
|
||||||
char_count += printf(" %4d | ", yylloc.first_line);
|
|
||||||
|
|
||||||
for (int i = 0; i < yylloc.first_column - 1; i++) {
|
|
||||||
if (buffer[i] == '\n') {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
printf("%c", buffer[i]);
|
|
||||||
}
|
|
||||||
|
|
||||||
char_count += printf("%s%s", BOLD, HIGHLIGHT);
|
|
||||||
|
|
||||||
for (int i = yylloc.first_column - 1; i < yylloc.last_column; i++) {
|
|
||||||
if (buffer[i] == '\n') {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
char_count += printf("%c", buffer[i]);
|
|
||||||
}
|
|
||||||
|
|
||||||
char_count += printf("%s", RESET);
|
|
||||||
|
|
||||||
for (int i = yylloc.last_column; buffer[i] != '\0' && buffer[i] != '\n'; i++) {
|
|
||||||
printf("%c", buffer[i]);
|
|
||||||
}
|
|
||||||
|
|
||||||
char_count += printf("\n | ");
|
|
||||||
|
|
||||||
for (int i = 0; i < yylloc.first_column - 1; i++) {
|
|
||||||
char_count += printf(" ");
|
|
||||||
}
|
|
||||||
|
|
||||||
char_count += printf("%s^", HIGHLIGHT);
|
|
||||||
|
|
||||||
for (int i = 0; i < yylloc.last_column - yylloc.first_column; i++) {
|
|
||||||
printf("~");
|
|
||||||
}
|
|
||||||
|
|
||||||
char_count += printf("%s\n\n", RESET);
|
|
||||||
|
|
||||||
return char_count;
|
|
||||||
}
|
|
||||||
|
|
||||||
int yyerror(const char *s) {
|
int yyerror(const char *s) {
|
||||||
return print_message(ERROR, s);
|
TokenLocation location = new_loc();
|
||||||
|
print_diagnostic(current_file, &location, Error, s);
|
||||||
|
return 0;
|
||||||
}
|
}
|
|
@ -9,4 +9,5 @@ set(CTEST_BINARY_DIRECTORY ${PROJECT_BINARY_DIR}/tests)
|
||||||
add_subdirectory(logging)
|
add_subdirectory(logging)
|
||||||
add_subdirectory(input_file)
|
add_subdirectory(input_file)
|
||||||
add_subdirectory(ast)
|
add_subdirectory(ast)
|
||||||
add_subdirectory(glib)
|
add_subdirectory(glib)
|
||||||
|
add_subdirectory(project)
|
|
@ -2,6 +2,20 @@ include(CTest)
|
||||||
|
|
||||||
include_directories(${PROJECT_SOURCE_DIR}/src)
|
include_directories(${PROJECT_SOURCE_DIR}/src)
|
||||||
|
|
||||||
|
# ------------------------------------------------ #
|
||||||
|
# Setup Glib 2.0 #
|
||||||
|
# ------------------------------------------------ #
|
||||||
|
|
||||||
|
find_package(PkgConfig REQUIRED)
|
||||||
|
pkg_search_module(GLIB REQUIRED IMPORTED_TARGET glib-2.0)
|
||||||
|
include_directories(PRIVATE ${GLIB_INCLUDE_DIRS})
|
||||||
|
|
||||||
|
# ------------------------------------------------ #
|
||||||
|
# Setup TOML-C99 #
|
||||||
|
# ------------------------------------------------ #
|
||||||
|
|
||||||
|
include_directories(${PROJECT_SOURCE_DIR}/dep/tomlc99)
|
||||||
|
|
||||||
# ------------------------------------------------------- #
|
# ------------------------------------------------------- #
|
||||||
# CTEST 1
|
# CTEST 1
|
||||||
# test building the syntax tree
|
# test building the syntax tree
|
||||||
|
@ -9,11 +23,16 @@ include_directories(${PROJECT_SOURCE_DIR}/src)
|
||||||
add_executable(ast_build_tree
|
add_executable(ast_build_tree
|
||||||
${PROJECT_SOURCE_DIR}/src/ast/ast.c
|
${PROJECT_SOURCE_DIR}/src/ast/ast.c
|
||||||
${PROJECT_SOURCE_DIR}/src/sys/log.c
|
${PROJECT_SOURCE_DIR}/src/sys/log.c
|
||||||
|
${PROJECT_SOURCE_DIR}/src/io/files.c
|
||||||
|
${PROJECT_SOURCE_DIR}/src/sys/col.c
|
||||||
|
${PROJECT_SOURCE_DIR}/src/cfg/opt.c
|
||||||
|
${PROJECT_SOURCE_DIR}/dep/tomlc99/toml.c
|
||||||
build_tree.c)
|
build_tree.c)
|
||||||
set_target_properties(ast_build_tree
|
set_target_properties(ast_build_tree
|
||||||
PROPERTIES
|
PROPERTIES
|
||||||
OUTPUT_NAME "build_tree"
|
OUTPUT_NAME "build_tree"
|
||||||
RUNTIME_OUTPUT_DIRECTORY ${GEMSTONE_BINARY_DIR}/tests/ast)
|
RUNTIME_OUTPUT_DIRECTORY ${GEMSTONE_BINARY_DIR}/tests/ast)
|
||||||
|
target_link_libraries(ast_build_tree PkgConfig::GLIB)
|
||||||
add_test(NAME ast_build_tree
|
add_test(NAME ast_build_tree
|
||||||
WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}
|
WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}
|
||||||
COMMAND python ${GEMSTONE_TEST_DIR}/ast/test_ast.py check_build_tree)
|
COMMAND python ${GEMSTONE_TEST_DIR}/ast/test_ast.py check_build_tree)
|
||||||
|
@ -25,11 +44,16 @@ add_test(NAME ast_build_tree
|
||||||
add_executable(ast_print_node
|
add_executable(ast_print_node
|
||||||
${PROJECT_SOURCE_DIR}/src/ast/ast.c
|
${PROJECT_SOURCE_DIR}/src/ast/ast.c
|
||||||
${PROJECT_SOURCE_DIR}/src/sys/log.c
|
${PROJECT_SOURCE_DIR}/src/sys/log.c
|
||||||
|
${PROJECT_SOURCE_DIR}/src/io/files.c
|
||||||
|
${PROJECT_SOURCE_DIR}/src/sys/col.c
|
||||||
|
${PROJECT_SOURCE_DIR}/src/cfg/opt.c
|
||||||
|
${PROJECT_SOURCE_DIR}/dep/tomlc99/toml.c
|
||||||
print_node.c)
|
print_node.c)
|
||||||
set_target_properties(ast_print_node
|
set_target_properties(ast_print_node
|
||||||
PROPERTIES
|
PROPERTIES
|
||||||
OUTPUT_NAME "print_node"
|
OUTPUT_NAME "print_node"
|
||||||
RUNTIME_OUTPUT_DIRECTORY ${GEMSTONE_BINARY_DIR}/tests/ast)
|
RUNTIME_OUTPUT_DIRECTORY ${GEMSTONE_BINARY_DIR}/tests/ast)
|
||||||
|
target_link_libraries(ast_print_node PkgConfig::GLIB)
|
||||||
add_test(NAME ast_print_node
|
add_test(NAME ast_print_node
|
||||||
WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}
|
WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}
|
||||||
COMMAND python ${GEMSTONE_TEST_DIR}/ast/test_ast.py check_print_node)
|
COMMAND python ${GEMSTONE_TEST_DIR}/ast/test_ast.py check_print_node)
|
||||||
|
@ -41,11 +65,16 @@ add_test(NAME ast_print_node
|
||||||
add_executable(ast_graphviz
|
add_executable(ast_graphviz
|
||||||
${PROJECT_SOURCE_DIR}/src/ast/ast.c
|
${PROJECT_SOURCE_DIR}/src/ast/ast.c
|
||||||
${PROJECT_SOURCE_DIR}/src/sys/log.c
|
${PROJECT_SOURCE_DIR}/src/sys/log.c
|
||||||
|
${PROJECT_SOURCE_DIR}/src/io/files.c
|
||||||
|
${PROJECT_SOURCE_DIR}/src/sys/col.c
|
||||||
|
${PROJECT_SOURCE_DIR}/src/cfg/opt.c
|
||||||
|
${PROJECT_SOURCE_DIR}/dep/tomlc99/toml.c
|
||||||
print_graphviz.c)
|
print_graphviz.c)
|
||||||
set_target_properties(ast_graphviz
|
set_target_properties(ast_graphviz
|
||||||
PROPERTIES
|
PROPERTIES
|
||||||
OUTPUT_NAME "print_graphviz"
|
OUTPUT_NAME "print_graphviz"
|
||||||
RUNTIME_OUTPUT_DIRECTORY ${GEMSTONE_BINARY_DIR}/tests/ast)
|
RUNTIME_OUTPUT_DIRECTORY ${GEMSTONE_BINARY_DIR}/tests/ast)
|
||||||
|
target_link_libraries(ast_graphviz PkgConfig::GLIB)
|
||||||
add_test(NAME ast_graphviz
|
add_test(NAME ast_graphviz
|
||||||
WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}
|
WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}
|
||||||
COMMAND python ${GEMSTONE_TEST_DIR}/ast/test_ast.py check_print_graphviz)
|
COMMAND python ${GEMSTONE_TEST_DIR}/ast/test_ast.py check_print_graphviz)
|
||||||
|
|
|
@ -6,22 +6,22 @@
|
||||||
#include <sys/log.h>
|
#include <sys/log.h>
|
||||||
|
|
||||||
void generate_statement(const AST_NODE_PTR stmt) {
|
void generate_statement(const AST_NODE_PTR stmt) {
|
||||||
const AST_NODE_PTR add = AST_new_node(AST_Add, NULL);
|
const AST_NODE_PTR add = AST_new_node(empty_location(), AST_Add, NULL);
|
||||||
|
|
||||||
AST_push_node(add, AST_new_node(AST_Int, "3"));
|
AST_push_node(add, AST_new_node(empty_location(), AST_Int, "3"));
|
||||||
AST_push_node(add, AST_new_node(AST_Int, "6"));
|
AST_push_node(add, AST_new_node(empty_location(), AST_Int, "6"));
|
||||||
|
|
||||||
AST_push_node(stmt, add);
|
AST_push_node(stmt, add);
|
||||||
}
|
}
|
||||||
|
|
||||||
void generate_branch(const AST_NODE_PTR stmt) {
|
void generate_branch(const AST_NODE_PTR stmt) {
|
||||||
const AST_NODE_PTR branch = AST_new_node(AST_If, NULL);
|
const AST_NODE_PTR branch = AST_new_node(empty_location(), AST_If, NULL);
|
||||||
const AST_NODE_PTR gt = AST_new_node(AST_Greater, NULL);
|
const AST_NODE_PTR gt = AST_new_node(empty_location(), AST_Greater, NULL);
|
||||||
|
|
||||||
AST_push_node(branch, gt);
|
AST_push_node(branch, gt);
|
||||||
|
|
||||||
AST_push_node(gt, AST_new_node(AST_Float, "2.3"));
|
AST_push_node(gt, AST_new_node(empty_location(), AST_Float, "2.3"));
|
||||||
AST_push_node(gt, AST_new_node(AST_Float, "0.79"));
|
AST_push_node(gt, AST_new_node(empty_location(), AST_Float, "0.79"));
|
||||||
|
|
||||||
AST_push_node(stmt, branch);
|
AST_push_node(stmt, branch);
|
||||||
|
|
||||||
|
@ -30,7 +30,7 @@ void generate_branch(const AST_NODE_PTR stmt) {
|
||||||
|
|
||||||
int main(void) {
|
int main(void) {
|
||||||
|
|
||||||
const AST_NODE_PTR root = AST_new_node(AST_Stmt, NULL);
|
const AST_NODE_PTR root = AST_new_node(empty_location(), AST_Stmt, NULL);
|
||||||
|
|
||||||
generate_branch(root);
|
generate_branch(root);
|
||||||
|
|
||||||
|
|
|
@ -7,15 +7,15 @@
|
||||||
|
|
||||||
int main(void) {
|
int main(void) {
|
||||||
|
|
||||||
struct AST_Node_t* node = AST_new_node(AST_If, NULL);
|
struct AST_Node_t* node = AST_new_node(empty_location(), AST_If, NULL);
|
||||||
|
|
||||||
struct AST_Node_t* child = AST_new_node(AST_Add, NULL);
|
struct AST_Node_t* child = AST_new_node(empty_location(), AST_Add, NULL);
|
||||||
AST_push_node(child, AST_new_node(AST_Int, "43"));
|
AST_push_node(child, AST_new_node(empty_location(), AST_Int, "43"));
|
||||||
AST_push_node(child, AST_new_node(AST_Int, "9"));
|
AST_push_node(child, AST_new_node(empty_location(), AST_Int, "9"));
|
||||||
|
|
||||||
AST_push_node(node, child);
|
AST_push_node(node, child);
|
||||||
AST_push_node(node, AST_new_node(AST_Expr, NULL));
|
AST_push_node(node, AST_new_node(empty_location(), AST_Expr, NULL));
|
||||||
AST_push_node(node, AST_new_node(AST_Expr, NULL));
|
AST_push_node(node, AST_new_node(empty_location(), AST_Expr, NULL));
|
||||||
|
|
||||||
FILE* out = fopen("ast.gv", "w+");
|
FILE* out = fopen("ast.gv", "w+");
|
||||||
// convert this file ^^^^^^
|
// convert this file ^^^^^^
|
||||||
|
|
|
@ -6,22 +6,22 @@
|
||||||
#include <sys/log.h>
|
#include <sys/log.h>
|
||||||
|
|
||||||
void generate_statement(const AST_NODE_PTR stmt) {
|
void generate_statement(const AST_NODE_PTR stmt) {
|
||||||
const AST_NODE_PTR add = AST_new_node(AST_Add, NULL);
|
const AST_NODE_PTR add = AST_new_node(empty_location(), AST_Add, NULL);
|
||||||
|
|
||||||
AST_push_node(add, AST_new_node(AST_Int, "3"));
|
AST_push_node(add, AST_new_node(empty_location(), AST_Int, "3"));
|
||||||
AST_push_node(add, AST_new_node(AST_Int, "6"));
|
AST_push_node(add, AST_new_node(empty_location(), AST_Int, "6"));
|
||||||
|
|
||||||
AST_push_node(stmt, add);
|
AST_push_node(stmt, add);
|
||||||
}
|
}
|
||||||
|
|
||||||
void generate_branch(const AST_NODE_PTR stmt) {
|
void generate_branch(const AST_NODE_PTR stmt) {
|
||||||
const AST_NODE_PTR branch = AST_new_node(AST_If, NULL);
|
const AST_NODE_PTR branch = AST_new_node(empty_location(), AST_If, NULL);
|
||||||
const AST_NODE_PTR gt = AST_new_node(AST_Greater, NULL);
|
const AST_NODE_PTR gt = AST_new_node(empty_location(), AST_Greater, NULL);
|
||||||
|
|
||||||
AST_push_node(branch, gt);
|
AST_push_node(branch, gt);
|
||||||
|
|
||||||
AST_push_node(gt, AST_new_node(AST_Float, "2.3"));
|
AST_push_node(gt, AST_new_node(empty_location(), AST_Float, "2.3"));
|
||||||
AST_push_node(gt, AST_new_node(AST_Float, "0.79"));
|
AST_push_node(gt, AST_new_node(empty_location(), AST_Float, "0.79"));
|
||||||
|
|
||||||
AST_push_node(stmt, branch);
|
AST_push_node(stmt, branch);
|
||||||
|
|
||||||
|
@ -32,7 +32,7 @@ int main(void) {
|
||||||
|
|
||||||
AST_init();
|
AST_init();
|
||||||
|
|
||||||
const AST_NODE_PTR root = AST_new_node(AST_Stmt, NULL);
|
const AST_NODE_PTR root = AST_new_node(empty_location(), AST_Stmt, NULL);
|
||||||
|
|
||||||
generate_branch(root);
|
generate_branch(root);
|
||||||
|
|
||||||
|
|
|
@ -8,7 +8,7 @@ int main(void) {
|
||||||
|
|
||||||
AST_init();
|
AST_init();
|
||||||
|
|
||||||
const AST_NODE_PTR node = AST_new_node(0, "value");
|
const AST_NODE_PTR node = AST_new_node(empty_location(), 0, "value");
|
||||||
|
|
||||||
for (size_t i = 0; i < AST_ELEMENT_COUNT; i++) {
|
for (size_t i = 0; i < AST_ELEMENT_COUNT; i++) {
|
||||||
// set kind
|
// set kind
|
||||||
|
|
|
@ -81,6 +81,9 @@ def run_check_print_node():
|
||||||
50 parameter
|
50 parameter
|
||||||
51 value
|
51 value
|
||||||
52 parameter-declaration
|
52 parameter-declaration
|
||||||
|
53 address of
|
||||||
|
54 deref
|
||||||
|
55 ref
|
||||||
""" == p.stdout
|
""" == p.stdout
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -12,7 +12,7 @@ def check_accept():
|
||||||
|
|
||||||
test_file_name = sys.argv[1]
|
test_file_name = sys.argv[1]
|
||||||
|
|
||||||
p = subprocess.run(["./gsc", test_file_name], capture_output=True, text=True)
|
p = subprocess.run(["./gsc", "compile", test_file_name], capture_output=True, text=True)
|
||||||
|
|
||||||
assert p.returncode == 0
|
assert p.returncode == 0
|
||||||
|
|
||||||
|
@ -22,7 +22,7 @@ def check_abort():
|
||||||
|
|
||||||
logging.basicConfig(level=logging.INFO)
|
logging.basicConfig(level=logging.INFO)
|
||||||
|
|
||||||
p = subprocess.run("./gsc", capture_output=True, text=True)
|
p = subprocess.run(["./gsc", "compile"], capture_output=True, text=True)
|
||||||
|
|
||||||
assert p.returncode == 1
|
assert p.returncode == 1
|
||||||
|
|
||||||
|
|
|
@ -2,17 +2,36 @@ include(CTest)
|
||||||
|
|
||||||
include_directories(${PROJECT_SOURCE_DIR}/src)
|
include_directories(${PROJECT_SOURCE_DIR}/src)
|
||||||
|
|
||||||
|
# ------------------------------------------------ #
|
||||||
|
# Setup Glib 2.0 #
|
||||||
|
# ------------------------------------------------ #
|
||||||
|
|
||||||
|
find_package(PkgConfig REQUIRED)
|
||||||
|
pkg_search_module(GLIB REQUIRED IMPORTED_TARGET glib-2.0)
|
||||||
|
include_directories(PRIVATE ${GLIB_INCLUDE_DIRS})
|
||||||
|
|
||||||
|
# ------------------------------------------------ #
|
||||||
|
# Setup TOML-C99 #
|
||||||
|
# ------------------------------------------------ #
|
||||||
|
|
||||||
|
include_directories(${PROJECT_SOURCE_DIR}/dep/tomlc99)
|
||||||
|
|
||||||
# ------------------------------------------------------- #
|
# ------------------------------------------------------- #
|
||||||
# CTEST 1
|
# CTEST 1
|
||||||
# test the default output of the logger
|
# test the default output of the logger
|
||||||
|
|
||||||
add_executable(logging_output
|
add_executable(logging_output
|
||||||
${PROJECT_SOURCE_DIR}/src/sys/log.c
|
${PROJECT_SOURCE_DIR}/src/sys/log.c
|
||||||
|
${PROJECT_SOURCE_DIR}/src/sys/col.c
|
||||||
|
${PROJECT_SOURCE_DIR}/src/cfg/opt.c
|
||||||
|
${PROJECT_SOURCE_DIR}/src/io/files.c
|
||||||
output.c)
|
output.c)
|
||||||
set_target_properties(logging_output
|
set_target_properties(logging_output
|
||||||
PROPERTIES
|
PROPERTIES
|
||||||
OUTPUT_NAME "output"
|
OUTPUT_NAME "output"
|
||||||
RUNTIME_OUTPUT_DIRECTORY ${GEMSTONE_BINARY_DIR}/tests/logging)
|
RUNTIME_OUTPUT_DIRECTORY ${GEMSTONE_BINARY_DIR}/tests/logging)
|
||||||
|
target_link_libraries(logging_output PkgConfig::GLIB)
|
||||||
|
target_link_libraries(logging_output tomlc99)
|
||||||
add_test(NAME logging_output
|
add_test(NAME logging_output
|
||||||
WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}
|
WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}
|
||||||
COMMAND python ${GEMSTONE_TEST_DIR}/logging/test_logging.py check_output)
|
COMMAND python ${GEMSTONE_TEST_DIR}/logging/test_logging.py check_output)
|
||||||
|
@ -23,11 +42,16 @@ add_test(NAME logging_output
|
||||||
|
|
||||||
add_executable(logging_panic
|
add_executable(logging_panic
|
||||||
${PROJECT_SOURCE_DIR}/src/sys/log.c
|
${PROJECT_SOURCE_DIR}/src/sys/log.c
|
||||||
|
${PROJECT_SOURCE_DIR}/src/sys/col.c
|
||||||
|
${PROJECT_SOURCE_DIR}/src/cfg/opt.c
|
||||||
|
${PROJECT_SOURCE_DIR}/src/io/files.c
|
||||||
panic.c)
|
panic.c)
|
||||||
set_target_properties(logging_panic
|
set_target_properties(logging_panic
|
||||||
PROPERTIES
|
PROPERTIES
|
||||||
OUTPUT_NAME "panic"
|
OUTPUT_NAME "panic"
|
||||||
RUNTIME_OUTPUT_DIRECTORY ${GEMSTONE_BINARY_DIR}/tests/logging)
|
RUNTIME_OUTPUT_DIRECTORY ${GEMSTONE_BINARY_DIR}/tests/logging)
|
||||||
|
target_link_libraries(logging_panic PkgConfig::GLIB)
|
||||||
|
target_link_libraries(logging_panic tomlc99)
|
||||||
add_test(NAME logging_panic
|
add_test(NAME logging_panic
|
||||||
WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}
|
WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}
|
||||||
COMMAND python ${GEMSTONE_TEST_DIR}/logging/test_logging.py check_panic)
|
COMMAND python ${GEMSTONE_TEST_DIR}/logging/test_logging.py check_panic)
|
||||||
|
@ -38,11 +62,16 @@ add_test(NAME logging_panic
|
||||||
|
|
||||||
add_executable(logging_streams
|
add_executable(logging_streams
|
||||||
${PROJECT_SOURCE_DIR}/src/sys/log.c
|
${PROJECT_SOURCE_DIR}/src/sys/log.c
|
||||||
|
${PROJECT_SOURCE_DIR}/src/sys/col.c
|
||||||
|
${PROJECT_SOURCE_DIR}/src/cfg/opt.c
|
||||||
|
${PROJECT_SOURCE_DIR}/src/io/files.c
|
||||||
streams.c)
|
streams.c)
|
||||||
set_target_properties(logging_streams
|
set_target_properties(logging_streams
|
||||||
PROPERTIES
|
PROPERTIES
|
||||||
OUTPUT_NAME "stream"
|
OUTPUT_NAME "stream"
|
||||||
RUNTIME_OUTPUT_DIRECTORY ${GEMSTONE_BINARY_DIR}/tests/logging)
|
RUNTIME_OUTPUT_DIRECTORY ${GEMSTONE_BINARY_DIR}/tests/logging)
|
||||||
|
target_link_libraries(logging_streams PkgConfig::GLIB)
|
||||||
|
target_link_libraries(logging_streams tomlc99)
|
||||||
add_test(NAME logging_streams
|
add_test(NAME logging_streams
|
||||||
WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}
|
WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}
|
||||||
COMMAND python ${GEMSTONE_TEST_DIR}/logging/test_logging.py check_stream)
|
COMMAND python ${GEMSTONE_TEST_DIR}/logging/test_logging.py check_stream)
|
||||||
|
@ -53,11 +82,16 @@ add_test(NAME logging_streams
|
||||||
|
|
||||||
add_executable(logging_level
|
add_executable(logging_level
|
||||||
${PROJECT_SOURCE_DIR}/src/sys/log.c
|
${PROJECT_SOURCE_DIR}/src/sys/log.c
|
||||||
|
${PROJECT_SOURCE_DIR}/src/sys/col.c
|
||||||
|
${PROJECT_SOURCE_DIR}/src/cfg/opt.c
|
||||||
|
${PROJECT_SOURCE_DIR}/src/io/files.c
|
||||||
level.c)
|
level.c)
|
||||||
set_target_properties(logging_level
|
set_target_properties(logging_level
|
||||||
PROPERTIES
|
PROPERTIES
|
||||||
OUTPUT_NAME "level"
|
OUTPUT_NAME "level"
|
||||||
RUNTIME_OUTPUT_DIRECTORY ${GEMSTONE_BINARY_DIR}/tests/logging)
|
RUNTIME_OUTPUT_DIRECTORY ${GEMSTONE_BINARY_DIR}/tests/logging)
|
||||||
|
target_link_libraries(logging_level PkgConfig::GLIB)
|
||||||
|
target_link_libraries(logging_level tomlc99)
|
||||||
add_test(NAME logging_level
|
add_test(NAME logging_level
|
||||||
WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}
|
WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}
|
||||||
COMMAND python ${GEMSTONE_TEST_DIR}/logging/test_logging.py check_level)
|
COMMAND python ${GEMSTONE_TEST_DIR}/logging/test_logging.py check_level)
|
||||||
|
|
|
@ -3,11 +3,16 @@
|
||||||
//
|
//
|
||||||
|
|
||||||
#include "sys/log.h"
|
#include "sys/log.h"
|
||||||
|
#include <sys/col.h>
|
||||||
|
#include <cfg/opt.h>
|
||||||
|
|
||||||
#define LOG_LEVEL LOG_LEVEL_WARNING
|
#define LOG_LEVEL LOG_LEVEL_WARNING
|
||||||
|
|
||||||
int main(void) {
|
int main(int argc, char* argv[]) {
|
||||||
|
parse_options(argc, argv);
|
||||||
log_init();
|
log_init();
|
||||||
|
set_log_level(LOG_LEVEL_DEBUG);
|
||||||
|
col_init();
|
||||||
|
|
||||||
DEBUG("logging some debug...");
|
DEBUG("logging some debug...");
|
||||||
INFO("logging some info...");
|
INFO("logging some info...");
|
||||||
|
|
|
@ -3,9 +3,14 @@
|
||||||
//
|
//
|
||||||
|
|
||||||
#include "sys/log.h"
|
#include "sys/log.h"
|
||||||
|
#include <sys/col.h>
|
||||||
|
#include <cfg/opt.h>
|
||||||
|
|
||||||
int main(void) {
|
int main(int argc, char* argv[]) {
|
||||||
|
parse_options(argc, argv);
|
||||||
log_init();
|
log_init();
|
||||||
|
set_log_level(LOG_LEVEL_DEBUG);
|
||||||
|
col_init();
|
||||||
|
|
||||||
DEBUG("logging some debug...");
|
DEBUG("logging some debug...");
|
||||||
INFO("logging some info...");
|
INFO("logging some info...");
|
||||||
|
|
|
@ -3,9 +3,12 @@
|
||||||
//
|
//
|
||||||
|
|
||||||
#include "sys/log.h"
|
#include "sys/log.h"
|
||||||
|
#include <cfg/opt.h>
|
||||||
|
|
||||||
int main(void) {
|
int main(int argc, char* argv[]) {
|
||||||
|
parse_options(argc, argv);
|
||||||
log_init();
|
log_init();
|
||||||
|
set_log_level(LOG_LEVEL_DEBUG);
|
||||||
|
|
||||||
// this should appear in stderr
|
// this should appear in stderr
|
||||||
INFO("before exit");
|
INFO("before exit");
|
||||||
|
|
|
@ -4,6 +4,7 @@
|
||||||
|
|
||||||
#include "sys/log.h"
|
#include "sys/log.h"
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
#include <cfg/opt.h>
|
||||||
|
|
||||||
static FILE* file;
|
static FILE* file;
|
||||||
|
|
||||||
|
@ -13,8 +14,10 @@ void close_file(void) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int main(void) {
|
int main(int argc, char* argv[]) {
|
||||||
|
parse_options(argc, argv);
|
||||||
log_init();
|
log_init();
|
||||||
|
set_log_level(LOG_LEVEL_DEBUG);
|
||||||
|
|
||||||
// this should appear in stderr
|
// this should appear in stderr
|
||||||
INFO("should only be in stderr");
|
INFO("should only be in stderr");
|
||||||
|
|
|
@ -0,0 +1,9 @@
|
||||||
|
include(CTest)
|
||||||
|
|
||||||
|
# ------------------------------------------------------- #
|
||||||
|
# CTEST 1
|
||||||
|
# test if the program successfully reads the project config
|
||||||
|
|
||||||
|
add_test(NAME project_build
|
||||||
|
WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}/tests/project
|
||||||
|
COMMAND python ${GEMSTONE_TEST_DIR}/project/test_project.py)
|
|
@ -0,0 +1,15 @@
|
||||||
|
[project]
|
||||||
|
authors = [ "Sven Vogel" ]
|
||||||
|
license = "GPL-2.0"
|
||||||
|
version = "0.1.0"
|
||||||
|
description = "This is a test project"
|
||||||
|
|
||||||
|
[target.debug]
|
||||||
|
root = "src/main.txt"
|
||||||
|
output = "bin"
|
||||||
|
archive = "archive"
|
||||||
|
opt = 1
|
||||||
|
print_ast = true
|
||||||
|
print_asm = true
|
||||||
|
print_ir = true
|
||||||
|
mode = "application"
|
|
@ -0,0 +1,4 @@
|
||||||
|
|
||||||
|
fun main(out int: e) {
|
||||||
|
e = 0
|
||||||
|
}
|
|
@ -0,0 +1,15 @@
|
||||||
|
import subprocess
|
||||||
|
import logging
|
||||||
|
from logging import info, error
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
info("started check output...")
|
||||||
|
|
||||||
|
p = subprocess.run([ "../../bin/debug/gsc", "build", "all" ], capture_output=True, text=True)
|
||||||
|
|
||||||
|
info("checking exit code...")
|
||||||
|
|
||||||
|
print(p.stdout)
|
||||||
|
|
||||||
|
# check exit code
|
||||||
|
assert p.returncode == 0
|
Loading…
Reference in New Issue