Merge pull request #66 from Servostar/concept/ast-design
Concept/ast design
This commit is contained in:
commit
730248595e
|
@ -2,7 +2,7 @@ name: "Build check gemstone in SDK"
|
||||||
run-name: SDK build check to ${{ inputs.deploy_target }} by @${{ github.actor }}
|
run-name: SDK build check to ${{ inputs.deploy_target }} by @${{ github.actor }}
|
||||||
on: [push, pull_request]
|
on: [push, pull_request]
|
||||||
env:
|
env:
|
||||||
SDK: 0.2.2-alpine-3.19.1
|
SDK: 0.2.3-alpine-3.19.1
|
||||||
jobs:
|
jobs:
|
||||||
build-check-sdk:
|
build-check-sdk:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
|
|
@ -21,6 +21,9 @@ project(gemstone
|
||||||
DESCRIPTION "programming language compiler"
|
DESCRIPTION "programming language compiler"
|
||||||
LANGUAGES C)
|
LANGUAGES C)
|
||||||
|
|
||||||
|
set(CMAKE_C_STANDARD 23)
|
||||||
|
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)
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
FROM servostar/gemstone:sdk-0.2.2-alpine-3.19.1
|
FROM servostar/gemstone:sdk-0.2.3-alpine-3.19.1
|
||||||
LABEL authors="servostar"
|
LABEL authors="servostar"
|
||||||
LABEL version="0.2.2"
|
LABEL version="0.2.3"
|
||||||
LABEL description="docker image for setting up the build pipeline on SDK"
|
LABEL description="docker image for setting up the build pipeline on SDK"
|
||||||
LABEL website="https://github.com/Servostar/gemstone"
|
LABEL website="https://github.com/Servostar/gemstone"
|
||||||
|
|
||||||
|
|
|
@ -1,11 +1,11 @@
|
||||||
FROM alpine:3.19.1
|
FROM alpine:3.19.1
|
||||||
LABEL authors="servostar"
|
LABEL authors="servostar"
|
||||||
LABEL version="0.2.2"
|
LABEL version="0.2.3"
|
||||||
LABEL description="base image for building the gemstone programming language compiler"
|
LABEL description="base image for building the gemstone programming language compiler"
|
||||||
LABEL website="https://github.com/Servostar/gemstone"
|
LABEL website="https://github.com/Servostar/gemstone"
|
||||||
|
|
||||||
# install dependencies
|
# install dependencies
|
||||||
RUN apk add build-base gcc make cmake bison flex git python3
|
RUN apk add build-base gcc make cmake bison flex git python3 graphviz
|
||||||
|
|
||||||
# create user for build
|
# create user for build
|
||||||
RUN adduser --disabled-password lorang
|
RUN adduser --disabled-password lorang
|
||||||
|
|
|
@ -0,0 +1,274 @@
|
||||||
|
|
||||||
|
#include <ast/ast.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <sys/log.h>
|
||||||
|
#include <assert.h>
|
||||||
|
|
||||||
|
struct AST_Node_t *AST_new_node(enum AST_SyntaxElement_t kind, const char* value) {
|
||||||
|
DEBUG("creating new AST node: %d \"%s\"", kind, value);
|
||||||
|
assert(kind < AST_ELEMENT_COUNT);
|
||||||
|
|
||||||
|
struct AST_Node_t *node = malloc(sizeof(struct AST_Node_t));
|
||||||
|
|
||||||
|
if (node == NULL) {
|
||||||
|
PANIC("failed to allocate AST node");
|
||||||
|
}
|
||||||
|
|
||||||
|
assert(node != NULL);
|
||||||
|
|
||||||
|
// init to discrete state
|
||||||
|
node->parent = NULL;
|
||||||
|
node->children = NULL;
|
||||||
|
node->child_count = 0;
|
||||||
|
node->kind = kind;
|
||||||
|
node->value = value;
|
||||||
|
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const char* lookup_table[AST_ELEMENT_COUNT] = { "__UNINIT__" };
|
||||||
|
|
||||||
|
void AST_init() {
|
||||||
|
DEBUG("initializing global syntax tree...");
|
||||||
|
|
||||||
|
INFO("filling lookup table...");
|
||||||
|
|
||||||
|
lookup_table[AST_Stmt] = "stmt";
|
||||||
|
lookup_table[AST_Expr] = "expr";
|
||||||
|
|
||||||
|
lookup_table[AST_Add] = "+";
|
||||||
|
lookup_table[AST_Sub] = "-";
|
||||||
|
lookup_table[AST_Mul] = "*";
|
||||||
|
lookup_table[AST_Div] = "/";
|
||||||
|
|
||||||
|
lookup_table[AST_BitAnd] = "&";
|
||||||
|
lookup_table[AST_BitOr] = "|";
|
||||||
|
lookup_table[AST_BitXor] = "^";
|
||||||
|
lookup_table[AST_BitNot] = "!";
|
||||||
|
|
||||||
|
lookup_table[AST_Eq] = "==";
|
||||||
|
lookup_table[AST_Less] = "<";
|
||||||
|
lookup_table[AST_Greater] = ">";
|
||||||
|
|
||||||
|
lookup_table[AST_BoolAnd] = "&&";
|
||||||
|
lookup_table[AST_BoolOr] = "||";
|
||||||
|
lookup_table[AST_BoolXor] = "^^";
|
||||||
|
lookup_table[AST_BoolNot] = "!!";
|
||||||
|
|
||||||
|
lookup_table[AST_While] = "while";
|
||||||
|
lookup_table[AST_If] = "if";
|
||||||
|
lookup_table[AST_IfElse] = "else if";
|
||||||
|
lookup_table[AST_Else] = "else";
|
||||||
|
|
||||||
|
lookup_table[AST_Decl] = "decl";
|
||||||
|
lookup_table[AST_Assign] = "assign";
|
||||||
|
lookup_table[AST_Def] = "def";
|
||||||
|
|
||||||
|
lookup_table[AST_Typedef] = "typedef";
|
||||||
|
lookup_table[AST_Box] = "box";
|
||||||
|
lookup_table[AST_Fun] = "fun";
|
||||||
|
|
||||||
|
lookup_table[AST_Typecast] = "cast";
|
||||||
|
lookup_table[AST_Transmute] = "as";
|
||||||
|
lookup_table[AST_Condition] = "condition";
|
||||||
|
}
|
||||||
|
|
||||||
|
const char* AST_node_to_string(const struct AST_Node_t* node) {
|
||||||
|
DEBUG("converting AST node to string: %p", node);
|
||||||
|
assert(node != NULL);
|
||||||
|
|
||||||
|
const char* string;
|
||||||
|
|
||||||
|
switch(node->kind) {
|
||||||
|
case AST_Int:
|
||||||
|
case AST_Float:
|
||||||
|
case AST_String:
|
||||||
|
case AST_Ident:
|
||||||
|
case AST_Macro:
|
||||||
|
case AST_Import:
|
||||||
|
case AST_Call:
|
||||||
|
string = node->value;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
string = lookup_table[node->kind];
|
||||||
|
}
|
||||||
|
|
||||||
|
assert(string != NULL);
|
||||||
|
|
||||||
|
return string;
|
||||||
|
}
|
||||||
|
|
||||||
|
void AST_push_node(struct AST_Node_t *owner, struct AST_Node_t *child) {
|
||||||
|
DEBUG("Adding new node %p to %p", child, owner);
|
||||||
|
assert(owner != NULL);
|
||||||
|
assert(child != NULL);
|
||||||
|
|
||||||
|
// if there are no children for now
|
||||||
|
if (owner->child_count == 0) {
|
||||||
|
DEBUG("Allocating new children array");
|
||||||
|
owner->children = malloc(sizeof(struct AST_Node_t *));
|
||||||
|
|
||||||
|
} else {
|
||||||
|
DEBUG("Rellocating old children array");
|
||||||
|
const size_t size = sizeof(struct AST_Node_t *) * (owner->child_count + 1);
|
||||||
|
owner->children = realloc(owner->children, size);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (owner->children == NULL) {
|
||||||
|
PANIC("failed to allocate children array of AST node");
|
||||||
|
}
|
||||||
|
|
||||||
|
assert(owner->children != NULL);
|
||||||
|
|
||||||
|
owner->children[owner->child_count++] = child;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct AST_Node_t *AST_get_node(struct AST_Node_t *owner, const size_t idx) {
|
||||||
|
DEBUG("retrvieng node %d from %p", idx, owner);
|
||||||
|
assert(owner != NULL);
|
||||||
|
assert(owner->children != NULL);
|
||||||
|
assert(idx < owner->child_count);
|
||||||
|
|
||||||
|
if (owner->children == NULL) {
|
||||||
|
PANIC("AST owner node has no children");
|
||||||
|
}
|
||||||
|
|
||||||
|
struct AST_Node_t *child = owner->children[idx];
|
||||||
|
|
||||||
|
if (child == NULL) {
|
||||||
|
PANIC("child node is NULL");
|
||||||
|
}
|
||||||
|
|
||||||
|
return child;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct AST_Node_t* AST_remove_child(struct AST_Node_t* owner, const size_t idx) {
|
||||||
|
assert(owner != NULL);
|
||||||
|
assert(owner->children != NULL);
|
||||||
|
assert(idx < owner->child_count);
|
||||||
|
|
||||||
|
struct AST_Node_t* child = owner->children[idx];
|
||||||
|
|
||||||
|
child->parent = NULL;
|
||||||
|
|
||||||
|
owner->child_count--;
|
||||||
|
|
||||||
|
// shift back every following element by one
|
||||||
|
for (size_t i = idx; i < owner->child_count; i++) {
|
||||||
|
owner->children[i] = owner->children[i + 1];
|
||||||
|
}
|
||||||
|
|
||||||
|
return child;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct AST_Node_t* AST_detach_child(struct AST_Node_t* owner, const struct AST_Node_t* child) {
|
||||||
|
assert(owner != NULL);
|
||||||
|
assert(child != NULL);
|
||||||
|
assert(owner->children != NULL);
|
||||||
|
|
||||||
|
for (size_t i = 0; i < owner->child_count; i++) {
|
||||||
|
if (owner->children[i] == child) {
|
||||||
|
return AST_remove_child(owner, i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
PANIC("Child to detach not a child of parent");
|
||||||
|
}
|
||||||
|
|
||||||
|
void AST_delete_node(struct AST_Node_t *node) {
|
||||||
|
assert(node != NULL);
|
||||||
|
|
||||||
|
DEBUG("Deleting AST node: %p", node);
|
||||||
|
|
||||||
|
if (node->children == NULL) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (node->parent != NULL) {
|
||||||
|
const struct AST_Node_t* child = AST_detach_child(node->parent, node);
|
||||||
|
assert(child == node);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (size_t i = 0; i < node->child_count; i++) {
|
||||||
|
// prevent detach of children node
|
||||||
|
node->children[i]->parent = NULL;
|
||||||
|
AST_delete_node(node->children[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
free(node->children);
|
||||||
|
free(node);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void AST_visit_nodes_recurse2(struct AST_Node_t *root,
|
||||||
|
void (*for_each)(struct AST_Node_t *node,
|
||||||
|
size_t depth),
|
||||||
|
const size_t depth) {
|
||||||
|
DEBUG("Recursive visit of %p at %d with %p", root, depth, for_each);
|
||||||
|
|
||||||
|
assert(root != NULL);
|
||||||
|
|
||||||
|
(for_each)(root, depth);
|
||||||
|
|
||||||
|
for (size_t i = 0; i < root->child_count; i++) {
|
||||||
|
AST_visit_nodes_recurse2(root->children[i], for_each, depth + 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void AST_visit_nodes_recurse(struct AST_Node_t *root,
|
||||||
|
void (*for_each)(struct AST_Node_t *node,
|
||||||
|
size_t depth)) {
|
||||||
|
DEBUG("Starting recursive visit of %p with %p", root, for_each);
|
||||||
|
|
||||||
|
assert(root != NULL);
|
||||||
|
assert(for_each != NULL);
|
||||||
|
|
||||||
|
AST_visit_nodes_recurse2(root, for_each, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void AST_fprint_graphviz_node_definition(FILE* stream, const struct AST_Node_t* node) {
|
||||||
|
DEBUG("Printing graphviz definition of %p", node);
|
||||||
|
|
||||||
|
assert(stream != NULL);
|
||||||
|
assert(node != NULL);
|
||||||
|
|
||||||
|
fprintf(stream, "\tnode%p [label=\"%s\"]\n", (void*) node, AST_node_to_string(node));
|
||||||
|
|
||||||
|
if (node->children == NULL) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (size_t i = 0; i < node->child_count; i++) {
|
||||||
|
AST_fprint_graphviz_node_definition(stream, node->children[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void AST_fprint_graphviz_node_connection(FILE* stream, const struct AST_Node_t* node) {
|
||||||
|
DEBUG("Printing graphviz connection of %p", node);
|
||||||
|
|
||||||
|
assert(stream != NULL);
|
||||||
|
assert(node != NULL);
|
||||||
|
|
||||||
|
if (node->children == NULL) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (size_t i = 0; i < node->child_count; i++) {
|
||||||
|
fprintf(stream, "\tnode%p -- node%p\n", (void*) node, (void*) node->children[i]);
|
||||||
|
AST_fprint_graphviz_node_connection(stream, node->children[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void AST_fprint_graphviz(FILE* stream, const struct AST_Node_t* root) {
|
||||||
|
DEBUG("Starting print of graphviz graph of %p", root);
|
||||||
|
|
||||||
|
assert(stream != NULL);
|
||||||
|
assert(root != NULL);
|
||||||
|
|
||||||
|
fprintf(stream, "graph {\n");
|
||||||
|
|
||||||
|
AST_fprint_graphviz_node_definition(stream, root);
|
||||||
|
AST_fprint_graphviz_node_connection(stream, root);
|
||||||
|
|
||||||
|
fprintf(stream, "}\n");
|
||||||
|
}
|
|
@ -0,0 +1,200 @@
|
||||||
|
|
||||||
|
#ifndef _AST_H_
|
||||||
|
#define _AST_H_
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief The type of a AST node
|
||||||
|
* @attention The last element is not to be used in the AST
|
||||||
|
* as it is used as a lazy way to get the total number of available
|
||||||
|
* variants of this enum.
|
||||||
|
*/
|
||||||
|
enum AST_SyntaxElement_t {
|
||||||
|
AST_Stmt = 0,
|
||||||
|
AST_Expr,
|
||||||
|
// Literals
|
||||||
|
AST_Int,
|
||||||
|
AST_Float,
|
||||||
|
AST_String,
|
||||||
|
// Control flow
|
||||||
|
AST_While,
|
||||||
|
AST_If,
|
||||||
|
AST_IfElse,
|
||||||
|
AST_Else,
|
||||||
|
AST_Condition,
|
||||||
|
// Variable management
|
||||||
|
AST_Decl,
|
||||||
|
AST_Assign,
|
||||||
|
AST_Def,
|
||||||
|
AST_Ident,
|
||||||
|
// Arithmetic operators
|
||||||
|
AST_Add,
|
||||||
|
AST_Sub,
|
||||||
|
AST_Mul,
|
||||||
|
AST_Div,
|
||||||
|
// Bitwise operators
|
||||||
|
AST_BitAnd,
|
||||||
|
AST_BitOr,
|
||||||
|
AST_BitXor,
|
||||||
|
AST_BitNot,
|
||||||
|
// Boolean operators
|
||||||
|
AST_BoolAnd,
|
||||||
|
AST_BoolOr,
|
||||||
|
AST_BoolXor,
|
||||||
|
AST_BoolNot,
|
||||||
|
// Logical operators
|
||||||
|
AST_Eq,
|
||||||
|
AST_Greater,
|
||||||
|
AST_Less,
|
||||||
|
// Casts
|
||||||
|
AST_Typecast, // type cast
|
||||||
|
AST_Transmute, // reinterpret cast
|
||||||
|
AST_Call, // function call
|
||||||
|
AST_Macro, // builtin functions: lineno(), filename(), ...
|
||||||
|
// Defintions
|
||||||
|
AST_Typedef,
|
||||||
|
AST_Box,
|
||||||
|
AST_Fun,
|
||||||
|
AST_Import,
|
||||||
|
// amount of variants
|
||||||
|
// in this enum
|
||||||
|
AST_ELEMENT_COUNT
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief A single node which can be joined with other nodes like a graph.
|
||||||
|
* Every node can have one ancestor (parent) but multiple (also none) children.
|
||||||
|
* Nodes have two properties:
|
||||||
|
* - kind: The type of the node. Such as AST_Expr, AST_Add, ...
|
||||||
|
* - value: A string representing an optional value. Can be a integer literal for kind AST_int
|
||||||
|
*/
|
||||||
|
struct AST_Node_t {
|
||||||
|
// parent node that owns this node
|
||||||
|
struct AST_Node_t *parent;
|
||||||
|
|
||||||
|
// type of AST node: if, declaration, ...
|
||||||
|
enum AST_SyntaxElement_t kind;
|
||||||
|
// optional value: integer literal, string literal, ...
|
||||||
|
const char* value;
|
||||||
|
|
||||||
|
// number of child nodes ownd by this node
|
||||||
|
// length of children array
|
||||||
|
size_t child_count;
|
||||||
|
// variable amount of child nodes
|
||||||
|
struct AST_Node_t **children;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Shorthand type for a single AST node
|
||||||
|
*/
|
||||||
|
typedef struct AST_Node_t* AST_NODE_PTR;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Initalize the global state of this module. Required for some functionality to work correctly.
|
||||||
|
*/
|
||||||
|
void AST_init(void);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Returns the string representation of the supplied node
|
||||||
|
* @attention The retuned pointer is not to be freed as it may either be a statically stored string or
|
||||||
|
* used by the node after this function call.
|
||||||
|
* @param node to return string representation of
|
||||||
|
* @return string represenation of the node
|
||||||
|
*/
|
||||||
|
[[maybe_unused]]
|
||||||
|
[[gnu::nonnull(1)]]
|
||||||
|
const char* AST_node_to_string(const struct AST_Node_t* node);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Create a new node struct on the system heap. Initializes the struct with the given values.
|
||||||
|
* All other fields are set to either NULL or 0. No allocation for children array is preformed.
|
||||||
|
* @attention parameter value can be NULL in case no value can be provided for the node
|
||||||
|
* @param kind the type of this node
|
||||||
|
* @param value an optional value for this node
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
[[maybe_unused]]
|
||||||
|
[[nodiscard("pointer must be freed")]]
|
||||||
|
[[gnu::returns_nonnull]]
|
||||||
|
struct AST_Node_t *AST_new_node(enum AST_SyntaxElement_t kind, const char* value);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Deallocate this node and all of its children.
|
||||||
|
* @attention This function will detach this node from its parent if one is present
|
||||||
|
* Use of the supplied node after this call is undefined behavior
|
||||||
|
* @param node The node to deallocate
|
||||||
|
*/
|
||||||
|
[[maybe_unused]]
|
||||||
|
[[gnu::nonnull(1)]]
|
||||||
|
void AST_delete_node(struct AST_Node_t * node);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Add a new child node to a parent node
|
||||||
|
* @attention This can reallocate the children array
|
||||||
|
* @param owner node to add a child to
|
||||||
|
* @param child node to be added as a child
|
||||||
|
*/
|
||||||
|
[[maybe_unused]]
|
||||||
|
[[gnu::nonnull(1), gnu::nonnull(2)]]
|
||||||
|
void AST_push_node(struct AST_Node_t *owner, struct AST_Node_t *child);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Remove the specified child from the owner.
|
||||||
|
* @attention The parent of the removed node is set to NULL.
|
||||||
|
* The returned pointer is still valid. It must be freed at some pointer later.
|
||||||
|
* @param owner Node to remove the child from
|
||||||
|
* @param idx the index of the child to remove
|
||||||
|
* @return a pointer to the child which was removed
|
||||||
|
*/
|
||||||
|
[[maybe_unused]]
|
||||||
|
[[nodiscard("pointer must be freed")]]
|
||||||
|
[[gnu::nonnull(1)]]
|
||||||
|
struct AST_Node_t* AST_remove_child(struct AST_Node_t* owner, size_t idx);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Detach a child from its parent. This involves removing the child from its parent
|
||||||
|
* and marking the parent of the child as NULL.
|
||||||
|
* @attention The returned pointer is still valid. It must be freed at some pointer later.
|
||||||
|
* @param owner the owner to remove the child from
|
||||||
|
* @param child the child to detach
|
||||||
|
* @return a pointer to child detached
|
||||||
|
*/
|
||||||
|
[[nodiscard("pointer must be freed")]]
|
||||||
|
[[gnu::nonnull(1), gnu::nonnull(1)]]
|
||||||
|
struct AST_Node_t* AST_detach_child(struct AST_Node_t* owner, const struct AST_Node_t* child);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Return a pointer to the n-th child of a node
|
||||||
|
* @attention Pointer to childen nodes will never change.
|
||||||
|
* However, the index a node is stored within a parent can change
|
||||||
|
* if a child of lower index is removed, thus reducing the childrens index by one.
|
||||||
|
* @param owner the parent node which owns the children
|
||||||
|
* @param idx the index of the child to get a pointer to
|
||||||
|
* @return a pointer to the n-th child of the owner node
|
||||||
|
*/
|
||||||
|
[[maybe_unused]]
|
||||||
|
[[gnu::nonnull(1)]]
|
||||||
|
struct AST_Node_t *AST_get_node(struct AST_Node_t *owner, size_t idx);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Execute a function for every child, grandchild, ... and the supplied node as topmost ancestor
|
||||||
|
* @param root the root to recursively execute a function for
|
||||||
|
* @param for_each the function to execute
|
||||||
|
*/
|
||||||
|
[[maybe_unused]]
|
||||||
|
[[gnu::nonnull(1), gnu::nonnull(2)]]
|
||||||
|
void AST_visit_nodes_recurse(struct AST_Node_t *root,
|
||||||
|
void (*for_each)(struct AST_Node_t *node,
|
||||||
|
size_t depth));
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Prints a graphviz graph of the node and all its ancestors.
|
||||||
|
* @param stream The stream to print to. Can be a file, stdout, ...
|
||||||
|
* @param node the topmost ancestor
|
||||||
|
*/
|
||||||
|
[[maybe_unused]]
|
||||||
|
[[gnu::nonnull(1), gnu::nonnull(2)]]
|
||||||
|
void AST_fprint_graphviz(FILE* stream, const struct AST_Node_t* node);
|
||||||
|
|
||||||
|
#endif
|
86
src/main.c
86
src/main.c
|
@ -1,77 +1,71 @@
|
||||||
|
#include <ast/ast.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <sys/log.h>
|
#include <sys/log.h>
|
||||||
#include <yacc/parser.tab.h>
|
#include <yacc/parser.tab.h>
|
||||||
|
|
||||||
#define LOG_LEVEL LOG_LEVEL_DEBUG
|
#define LOG_LEVEL LOG_LEVEL_DEBUG
|
||||||
|
|
||||||
extern FILE* yyin;
|
extern FILE *yyin;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Log a debug message to inform about beginning exit procedures
|
* @brief Log a debug message to inform about beginning exit procedures
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
void notify_exit(void)
|
void notify_exit(void) { DEBUG("Exiting gemstone..."); }
|
||||||
{
|
|
||||||
DEBUG("Exiting gemstone...");
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Closes File after compiling.
|
* @brief Closes File after compiling.
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
|
||||||
void close_file(void)
|
void close_file(void) {
|
||||||
{
|
if (NULL != yyin) {
|
||||||
if (NULL != yyin)
|
fclose(yyin);
|
||||||
{
|
}
|
||||||
fclose(yyin);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Run compiler setup here
|
* @brief Run compiler setup here
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
void setup(void)
|
void setup(void) {
|
||||||
{
|
// setup preample
|
||||||
// setup preample
|
|
||||||
|
|
||||||
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();
|
||||||
DEBUG("finished starting up gemstone...");
|
|
||||||
|
DEBUG("finished starting up gemstone...");
|
||||||
}
|
}
|
||||||
|
|
||||||
int main(int argc, char *argv[]) {
|
int main(int argc, char *argv[]) {
|
||||||
|
|
||||||
setup();
|
setup();
|
||||||
atexit(close_file);
|
atexit(close_file);
|
||||||
|
|
||||||
// Check for file input as argument
|
|
||||||
if (2 != argc)
|
|
||||||
{
|
|
||||||
INFO("Usage: %s <filename>\n", argv[0]);
|
|
||||||
PANIC("No File could be found");
|
|
||||||
}
|
|
||||||
|
|
||||||
// filename as first argument
|
|
||||||
char *filename = argv[1];
|
|
||||||
|
|
||||||
FILE *file = fopen(filename, "r");
|
// Check for file input as argument
|
||||||
|
if (2 != argc) {
|
||||||
|
INFO("Usage: %s <filename>\n", argv[0]);
|
||||||
|
PANIC("No File could be found");
|
||||||
|
}
|
||||||
|
|
||||||
if (NULL == file)
|
// filename as first argument
|
||||||
{
|
char *filename = argv[1];
|
||||||
PANIC("File couldn't be opened!");
|
|
||||||
}
|
|
||||||
yyin = file;
|
|
||||||
|
|
||||||
yyparse();
|
|
||||||
|
|
||||||
return 0;
|
FILE *file = fopen(filename, "r");
|
||||||
|
|
||||||
|
if (NULL == file) {
|
||||||
|
PANIC("File couldn't be opened!");
|
||||||
|
}
|
||||||
|
yyin = file;
|
||||||
|
|
||||||
|
yyparse();
|
||||||
|
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <sys/log.h>
|
#include <sys/log.h>
|
||||||
|
#include <assert.h>
|
||||||
|
|
||||||
static struct Logger_t {
|
static struct Logger_t {
|
||||||
FILE** streams;
|
FILE** streams;
|
||||||
|
@ -11,13 +12,15 @@ static struct Logger_t {
|
||||||
|
|
||||||
void log_init(void)
|
void log_init(void)
|
||||||
{
|
{
|
||||||
|
assert(LOG_DEFAULT_STREAM != NULL);
|
||||||
log_register_stream(LOG_DEFAULT_STREAM);
|
log_register_stream(LOG_DEFAULT_STREAM);
|
||||||
}
|
}
|
||||||
|
|
||||||
void log_register_stream(FILE* restrict stream)
|
void log_register_stream(FILE* restrict stream)
|
||||||
{
|
{
|
||||||
if (stream == NULL)
|
// replace runtime check with assertion
|
||||||
PANIC("stream to register is NULL");
|
// only to be used in debug target
|
||||||
|
assert(stream != NULL);
|
||||||
|
|
||||||
if (GlobalLogger.stream_count == 0)
|
if (GlobalLogger.stream_count == 0)
|
||||||
{
|
{
|
||||||
|
@ -57,7 +60,7 @@ static void vflogf(
|
||||||
vfprintf(stream, format, args);
|
vfprintf(stream, format, args);
|
||||||
}
|
}
|
||||||
|
|
||||||
void __logf(
|
void syslog_logf(
|
||||||
const char* restrict level,
|
const char* restrict level,
|
||||||
const char* restrict file,
|
const char* restrict file,
|
||||||
const unsigned long line,
|
const unsigned long line,
|
||||||
|
@ -78,7 +81,7 @@ void __logf(
|
||||||
va_end(args);
|
va_end(args);
|
||||||
}
|
}
|
||||||
|
|
||||||
void __panicf(
|
void syslog_panicf(
|
||||||
const char* restrict file,
|
const char* restrict file,
|
||||||
const unsigned long line,
|
const unsigned long line,
|
||||||
const char* restrict func,
|
const char* restrict func,
|
||||||
|
@ -95,7 +98,7 @@ void __panicf(
|
||||||
exit(EXIT_FAILURE);
|
exit(EXIT_FAILURE);
|
||||||
}
|
}
|
||||||
|
|
||||||
void __fatalf(
|
void syslog_fatalf(
|
||||||
const char* restrict file,
|
const char* restrict file,
|
||||||
const unsigned long line,
|
const unsigned long line,
|
||||||
const char* restrict func,
|
const char* restrict func,
|
||||||
|
|
|
@ -2,7 +2,6 @@
|
||||||
#define _SYS_ERR_H_
|
#define _SYS_ERR_H_
|
||||||
|
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <string.h>
|
|
||||||
|
|
||||||
#define LOG_DEFAULT_STREAM stderr
|
#define LOG_DEFAULT_STREAM stderr
|
||||||
|
|
||||||
|
@ -35,14 +34,14 @@
|
||||||
* This macro will print debug information to stderr and call abort() to
|
* This macro will print debug information to stderr and call abort() to
|
||||||
* performa a ungracefull exit. No clean up possible.
|
* performa a ungracefull exit. No clean up possible.
|
||||||
*/
|
*/
|
||||||
#define PANIC(format, ...) __panicf(__FILE_NAME__, __LINE__, __func__, format"\n", ##__VA_ARGS__)
|
#define PANIC(format, ...) syslog_panicf(__FILE_NAME__, __LINE__, __func__, format"\n", ##__VA_ARGS__)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Panic is used in cases where the process is in an invalid or undefined state.
|
* @brief Panic is used in cases where the process is in an invalid or undefined state.
|
||||||
* This macro will print debug information to stderr and call exit() to
|
* This macro will print debug information to stderr and call exit() to
|
||||||
* initiate a gracefull exit, giving the process the opportunity to clean up.
|
* initiate a gracefull exit, giving the process the opportunity to clean up.
|
||||||
*/
|
*/
|
||||||
#define FATAL(format, ...) __fatalf(__FILE_NAME__, __LINE__, __func__, format"\n", ##__VA_ARGS__)
|
#define FATAL(format, ...) syslog_fatalf(__FILE_NAME__, __LINE__, __func__, format"\n", ##__VA_ARGS__)
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Standard log macros. These will not terminate the application.
|
Standard log macros. These will not terminate the application.
|
||||||
|
@ -57,7 +56,7 @@ will not print.
|
||||||
#define __LOG(level, priority, format, ...) \
|
#define __LOG(level, priority, format, ...) \
|
||||||
do { \
|
do { \
|
||||||
if (LOG_LEVEL <= priority) \
|
if (LOG_LEVEL <= priority) \
|
||||||
__logf(level, __FILE_NAME__, __LINE__, __func__, format, ##__VA_ARGS__); \
|
syslog_logf(level, __FILE_NAME__, __LINE__, __func__, format, ##__VA_ARGS__); \
|
||||||
} while(0)
|
} while(0)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -70,10 +69,11 @@ will not print.
|
||||||
* @param format the format to print following args in
|
* @param format the format to print following args in
|
||||||
* @param ...
|
* @param ...
|
||||||
*/
|
*/
|
||||||
void __logf(
|
[[gnu::nonnull(1), gnu::nonnull(2), gnu::nonnull(4)]]
|
||||||
|
void syslog_logf(
|
||||||
const char* restrict level,
|
const char* restrict level,
|
||||||
const char* restrict file,
|
const char* restrict file,
|
||||||
const unsigned long line,
|
unsigned long line,
|
||||||
const char* restrict func,
|
const char* restrict func,
|
||||||
const char* restrict format,
|
const char* restrict format,
|
||||||
...);
|
...);
|
||||||
|
@ -87,9 +87,11 @@ void __logf(
|
||||||
* @param format the format to print following args in
|
* @param format the format to print following args in
|
||||||
* @param ...
|
* @param ...
|
||||||
*/
|
*/
|
||||||
void __panicf(
|
[[noreturn]]
|
||||||
|
[[gnu::nonnull(1), gnu::nonnull(3), gnu::nonnull(4)]]
|
||||||
|
void syslog_panicf(
|
||||||
const char* restrict file,
|
const char* restrict file,
|
||||||
const unsigned long line,
|
unsigned long line,
|
||||||
const char* restrict func,
|
const char* restrict func,
|
||||||
const char* restrict format,
|
const char* restrict format,
|
||||||
...);
|
...);
|
||||||
|
@ -103,9 +105,11 @@ void __panicf(
|
||||||
* @param format the format to print following args in
|
* @param format the format to print following args in
|
||||||
* @param ...
|
* @param ...
|
||||||
*/
|
*/
|
||||||
void __fatalf(
|
[[noreturn]]
|
||||||
|
[[gnu::nonnull(1), gnu::nonnull(3), gnu::nonnull(4)]]
|
||||||
|
void syslog_fatalf(
|
||||||
const char* restrict file,
|
const char* restrict file,
|
||||||
const unsigned long line,
|
unsigned long line,
|
||||||
const char* restrict func,
|
const char* restrict func,
|
||||||
const char* restrict format,
|
const char* restrict format,
|
||||||
...);
|
...);
|
||||||
|
@ -121,6 +125,7 @@ void log_init(void);
|
||||||
*
|
*
|
||||||
* @param stream
|
* @param stream
|
||||||
*/
|
*/
|
||||||
|
[[gnu::nonnull(1)]]
|
||||||
void log_register_stream(FILE* restrict stream);
|
void log_register_stream(FILE* restrict stream);
|
||||||
|
|
||||||
#endif /* _SYS_ERR_H_ */
|
#endif /* _SYS_ERR_H_ */
|
||||||
|
|
|
@ -7,4 +7,5 @@ set(CTEST_BINARY_DIRECTORY ${PROJECT_BINARY_DIR}/tests)
|
||||||
# Provide test to run here or include another CMakeLists.txt
|
# Provide test to run here or include another CMakeLists.txt
|
||||||
|
|
||||||
add_subdirectory(logging)
|
add_subdirectory(logging)
|
||||||
add_subdirectory(input_file)
|
add_subdirectory(input_file)
|
||||||
|
add_subdirectory(ast)
|
|
@ -0,0 +1,51 @@
|
||||||
|
include(CTest)
|
||||||
|
|
||||||
|
include_directories(${PROJECT_SOURCE_DIR}/src)
|
||||||
|
|
||||||
|
# ------------------------------------------------------- #
|
||||||
|
# CTEST 1
|
||||||
|
# test building the syntax tree
|
||||||
|
|
||||||
|
add_executable(ast_build_tree
|
||||||
|
${PROJECT_SOURCE_DIR}/src/ast/ast.c
|
||||||
|
${PROJECT_SOURCE_DIR}/src/sys/log.c
|
||||||
|
build_tree.c)
|
||||||
|
set_target_properties(ast_build_tree
|
||||||
|
PROPERTIES
|
||||||
|
OUTPUT_NAME "build_tree"
|
||||||
|
RUNTIME_OUTPUT_DIRECTORY ${GEMSTONE_BINARY_DIR}/tests/ast)
|
||||||
|
add_test(NAME ast_build_tree
|
||||||
|
WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}
|
||||||
|
COMMAND python ${GEMSTONE_TEST_DIR}/ast/test_ast.py check_build_tree)
|
||||||
|
|
||||||
|
# ------------------------------------------------------- #
|
||||||
|
# CTEST 2
|
||||||
|
# test node to string output
|
||||||
|
|
||||||
|
add_executable(ast_print_node
|
||||||
|
${PROJECT_SOURCE_DIR}/src/ast/ast.c
|
||||||
|
${PROJECT_SOURCE_DIR}/src/sys/log.c
|
||||||
|
print_node.c)
|
||||||
|
set_target_properties(ast_print_node
|
||||||
|
PROPERTIES
|
||||||
|
OUTPUT_NAME "print_node"
|
||||||
|
RUNTIME_OUTPUT_DIRECTORY ${GEMSTONE_BINARY_DIR}/tests/ast)
|
||||||
|
add_test(NAME ast_print_node
|
||||||
|
WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}
|
||||||
|
COMMAND python ${GEMSTONE_TEST_DIR}/ast/test_ast.py check_print_node)
|
||||||
|
|
||||||
|
# ------------------------------------------------------- #
|
||||||
|
# CTEST 3
|
||||||
|
# test graphviz output
|
||||||
|
|
||||||
|
add_executable(ast_graphviz
|
||||||
|
${PROJECT_SOURCE_DIR}/src/ast/ast.c
|
||||||
|
${PROJECT_SOURCE_DIR}/src/sys/log.c
|
||||||
|
print_graphviz.c)
|
||||||
|
set_target_properties(ast_graphviz
|
||||||
|
PROPERTIES
|
||||||
|
OUTPUT_NAME "print_graphviz"
|
||||||
|
RUNTIME_OUTPUT_DIRECTORY ${GEMSTONE_BINARY_DIR}/tests/ast)
|
||||||
|
add_test(NAME ast_graphviz
|
||||||
|
WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}
|
||||||
|
COMMAND python ${GEMSTONE_TEST_DIR}/ast/test_ast.py check_print_graphviz)
|
|
@ -0,0 +1,40 @@
|
||||||
|
//
|
||||||
|
// Created by servostar on 5/7/24.
|
||||||
|
//
|
||||||
|
|
||||||
|
#include <ast/ast.h>
|
||||||
|
#include <sys/log.h>
|
||||||
|
|
||||||
|
void generate_statement(const AST_NODE_PTR stmt) {
|
||||||
|
const AST_NODE_PTR add = AST_new_node(AST_Add, NULL);
|
||||||
|
|
||||||
|
AST_push_node(add, AST_new_node(AST_Int, "3"));
|
||||||
|
AST_push_node(add, AST_new_node(AST_Int, "6"));
|
||||||
|
|
||||||
|
AST_push_node(stmt, add);
|
||||||
|
}
|
||||||
|
|
||||||
|
void generate_branch(const AST_NODE_PTR stmt) {
|
||||||
|
const AST_NODE_PTR branch = AST_new_node(AST_If, NULL);
|
||||||
|
const AST_NODE_PTR gt = AST_new_node(AST_Greater, NULL);
|
||||||
|
|
||||||
|
AST_push_node(branch, gt);
|
||||||
|
|
||||||
|
AST_push_node(gt, AST_new_node(AST_Float, "2.3"));
|
||||||
|
AST_push_node(gt, AST_new_node(AST_Float, "0.79"));
|
||||||
|
|
||||||
|
AST_push_node(stmt, branch);
|
||||||
|
|
||||||
|
generate_statement(branch);
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(void) {
|
||||||
|
|
||||||
|
const AST_NODE_PTR root = AST_new_node(AST_Stmt, NULL);
|
||||||
|
|
||||||
|
generate_branch(root);
|
||||||
|
|
||||||
|
AST_delete_node(root);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
|
@ -0,0 +1,32 @@
|
||||||
|
//
|
||||||
|
// Created by servostar on 5/7/24.
|
||||||
|
//
|
||||||
|
|
||||||
|
#include <ast/ast.h>
|
||||||
|
#include <sys/log.h>
|
||||||
|
|
||||||
|
int main(void) {
|
||||||
|
|
||||||
|
struct AST_Node_t* node = AST_new_node(AST_If, NULL);
|
||||||
|
|
||||||
|
struct AST_Node_t* child = AST_new_node(AST_Add, NULL);
|
||||||
|
AST_push_node(child, AST_new_node(AST_Int, "43"));
|
||||||
|
AST_push_node(child, AST_new_node(AST_Int, "9"));
|
||||||
|
|
||||||
|
AST_push_node(node, child);
|
||||||
|
AST_push_node(node, AST_new_node(AST_Expr, NULL));
|
||||||
|
AST_push_node(node, AST_new_node(AST_Expr, NULL));
|
||||||
|
|
||||||
|
FILE* out = fopen("ast.gv", "w+");
|
||||||
|
// convert this file ^^^^^^
|
||||||
|
// to an svg with: `dot -Tsvg ast.gv > graph.svg`
|
||||||
|
|
||||||
|
AST_fprint_graphviz(out, node);
|
||||||
|
|
||||||
|
AST_delete_node(node);
|
||||||
|
|
||||||
|
fflush(out);
|
||||||
|
fclose(out);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
|
@ -0,0 +1,54 @@
|
||||||
|
//
|
||||||
|
// Created by servostar on 5/8/24.
|
||||||
|
//
|
||||||
|
|
||||||
|
#include <ast/ast.h>
|
||||||
|
#include <sys/log.h>
|
||||||
|
|
||||||
|
void generate_statement(const AST_NODE_PTR stmt) {
|
||||||
|
const AST_NODE_PTR add = AST_new_node(AST_Add, NULL);
|
||||||
|
|
||||||
|
AST_push_node(add, AST_new_node(AST_Int, "3"));
|
||||||
|
AST_push_node(add, AST_new_node(AST_Int, "6"));
|
||||||
|
|
||||||
|
AST_push_node(stmt, add);
|
||||||
|
}
|
||||||
|
|
||||||
|
void generate_branch(const AST_NODE_PTR stmt) {
|
||||||
|
const AST_NODE_PTR branch = AST_new_node(AST_If, NULL);
|
||||||
|
const AST_NODE_PTR gt = AST_new_node(AST_Greater, NULL);
|
||||||
|
|
||||||
|
AST_push_node(branch, gt);
|
||||||
|
|
||||||
|
AST_push_node(gt, AST_new_node(AST_Float, "2.3"));
|
||||||
|
AST_push_node(gt, AST_new_node(AST_Float, "0.79"));
|
||||||
|
|
||||||
|
AST_push_node(stmt, branch);
|
||||||
|
|
||||||
|
generate_statement(branch);
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(void) {
|
||||||
|
|
||||||
|
AST_init();
|
||||||
|
|
||||||
|
const AST_NODE_PTR root = AST_new_node(AST_Stmt, NULL);
|
||||||
|
|
||||||
|
generate_branch(root);
|
||||||
|
|
||||||
|
FILE* output = fopen("tmp/graph.gv", "w");
|
||||||
|
|
||||||
|
if (output == NULL) {
|
||||||
|
PANIC("unable to open file");
|
||||||
|
}
|
||||||
|
|
||||||
|
AST_fprint_graphviz(output, root);
|
||||||
|
|
||||||
|
fflush(output);
|
||||||
|
|
||||||
|
fclose(output);
|
||||||
|
|
||||||
|
AST_delete_node(root);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
|
@ -0,0 +1,23 @@
|
||||||
|
//
|
||||||
|
// Created by servostar on 5/7/24.
|
||||||
|
//
|
||||||
|
|
||||||
|
#include <ast/ast.h>
|
||||||
|
|
||||||
|
int main(void) {
|
||||||
|
|
||||||
|
AST_init();
|
||||||
|
|
||||||
|
const AST_NODE_PTR node = AST_new_node(0, "value");
|
||||||
|
|
||||||
|
for (size_t i = 0; i < AST_ELEMENT_COUNT; i++) {
|
||||||
|
// set kind
|
||||||
|
node->kind = i;
|
||||||
|
// print symbol
|
||||||
|
printf("%ld %s\n", i, AST_node_to_string(node));
|
||||||
|
}
|
||||||
|
|
||||||
|
AST_delete_node(node);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
|
@ -0,0 +1,126 @@
|
||||||
|
import subprocess
|
||||||
|
import sys
|
||||||
|
import logging
|
||||||
|
from logging import info, error
|
||||||
|
import os
|
||||||
|
|
||||||
|
BIN_DIR = "bin/tests/ast/"
|
||||||
|
|
||||||
|
|
||||||
|
def run_check_build_tree():
|
||||||
|
info("started check tree build...")
|
||||||
|
|
||||||
|
p = subprocess.run(BIN_DIR + "build_tree", capture_output=True, text=True)
|
||||||
|
|
||||||
|
info("checking exit code...")
|
||||||
|
|
||||||
|
# check exit code
|
||||||
|
assert p.returncode == 0
|
||||||
|
|
||||||
|
|
||||||
|
def run_check_print_node():
|
||||||
|
info("started check node print...")
|
||||||
|
|
||||||
|
p = subprocess.run(BIN_DIR + "print_node", capture_output=True, text=True)
|
||||||
|
|
||||||
|
info("checking exit code...")
|
||||||
|
|
||||||
|
# check exit code
|
||||||
|
assert p.returncode == 0
|
||||||
|
|
||||||
|
assert """0 stmt
|
||||||
|
1 expr
|
||||||
|
2 value
|
||||||
|
3 value
|
||||||
|
4 value
|
||||||
|
5 while
|
||||||
|
6 if
|
||||||
|
7 else if
|
||||||
|
8 else
|
||||||
|
9 condition
|
||||||
|
10 decl
|
||||||
|
11 assign
|
||||||
|
12 def
|
||||||
|
13 value
|
||||||
|
14 +
|
||||||
|
15 -
|
||||||
|
16 *
|
||||||
|
17 /
|
||||||
|
18 &
|
||||||
|
19 |
|
||||||
|
20 ^
|
||||||
|
21 !
|
||||||
|
22 &&
|
||||||
|
23 ||
|
||||||
|
24 ^^
|
||||||
|
25 !!
|
||||||
|
26 ==
|
||||||
|
27 >
|
||||||
|
28 <
|
||||||
|
29 cast
|
||||||
|
30 as
|
||||||
|
31 value
|
||||||
|
32 value
|
||||||
|
33 typedef
|
||||||
|
34 box
|
||||||
|
35 fun
|
||||||
|
36 value
|
||||||
|
""" == p.stdout
|
||||||
|
|
||||||
|
|
||||||
|
def run_check_print_graphviz():
|
||||||
|
info("started check print graphviz...")
|
||||||
|
|
||||||
|
info("creating temporary folder...")
|
||||||
|
|
||||||
|
if not os.path.exists("tmp"):
|
||||||
|
os.mkdir("tmp")
|
||||||
|
|
||||||
|
info("cleaning temporary folder...")
|
||||||
|
|
||||||
|
if os.path.exists("tmp/graph.gv"):
|
||||||
|
os.remove("tmp/graph.gv")
|
||||||
|
|
||||||
|
if os.path.exists("tmp/graph.svg"):
|
||||||
|
os.remove("tmp/graph.svg")
|
||||||
|
|
||||||
|
p = subprocess.run(BIN_DIR + "print_graphviz", capture_output=True, text=True)
|
||||||
|
|
||||||
|
info("checking exit code...")
|
||||||
|
|
||||||
|
# check exit code
|
||||||
|
assert p.returncode == 0
|
||||||
|
|
||||||
|
info("converting gv to svg...")
|
||||||
|
|
||||||
|
p = subprocess.run(["dot", "-Tsvg", "tmp/graph.gv", "-otmp/graph.svg"])
|
||||||
|
|
||||||
|
info("checking exit code...")
|
||||||
|
assert p.returncode == 0
|
||||||
|
|
||||||
|
info("checking svg output...")
|
||||||
|
with open("tmp/graph.svg", "r") as file:
|
||||||
|
string = "".join(file.readlines())
|
||||||
|
assert "2.3" in string
|
||||||
|
assert "0.79" in string
|
||||||
|
assert "stmt" in string
|
||||||
|
assert "if" in string
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
logging.basicConfig(level=logging.INFO)
|
||||||
|
|
||||||
|
target = sys.argv[1]
|
||||||
|
|
||||||
|
info(f"starting ast test suite with target: {target}")
|
||||||
|
|
||||||
|
match target:
|
||||||
|
case "check_build_tree":
|
||||||
|
run_check_build_tree()
|
||||||
|
case "check_print_node":
|
||||||
|
run_check_print_node()
|
||||||
|
case "check_print_graphviz":
|
||||||
|
run_check_print_graphviz()
|
||||||
|
case _:
|
||||||
|
error(f"unknown target: {target}")
|
||||||
|
exit(1)
|
Loading…
Reference in New Issue