From 9be542c93ce71d84dddc11bbcf1864d3095a176a Mon Sep 17 00:00:00 2001 From: Zander Thannhauser Date: Sat, 26 Jul 2025 19:35:56 -0500 Subject: [PATCH] first commit --- .envrc | 1 + .gitignore | 3 + avl.c | 651 +++++++++++++++++++ avl.h | 208 +++++++ flake.lock | 77 +++ flake.nix | 47 ++ main.c | 1767 ++++++++++++++++++++++++++++++++++++++++++++++++++++ makefile | 41 ++ 8 files changed, 2795 insertions(+) create mode 100644 .envrc create mode 100644 .gitignore create mode 100644 avl.c create mode 100644 avl.h create mode 100644 flake.lock create mode 100644 flake.nix create mode 100644 main.c create mode 100644 makefile diff --git a/.envrc b/.envrc new file mode 100644 index 0000000..3550a30 --- /dev/null +++ b/.envrc @@ -0,0 +1 @@ +use flake diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..451efb9 --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +.direnv/ +bin/ +outputs/ diff --git a/avl.c b/avl.c new file mode 100644 index 0000000..5cec1e5 --- /dev/null +++ b/avl.c @@ -0,0 +1,651 @@ +/***************************************************************************** + + avl.c - Source code for the AVL-tree library. + + Copyright (C) 1998 Michael H. Buselli + Copyright (C) 2000-2002 Wessel Dankers + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + Augmented AVL-tree. Original by Michael H. Buselli . + + Modified by Wessel Dankers to add a bunch of bloat to + the sourcecode, change the interface and squash a few bugs. + Mail him if you find new bugs. + +*****************************************************************************/ + +#include +#include +#include + +#include "avl.h" + +static void avl_rebalance(avl_tree_t *, avl_node_t *); + +#ifdef AVL_COUNT +#define NODE_COUNT(n) ((n) ? (n)->count : 0) +#define L_COUNT(n) (NODE_COUNT((n)->left)) +#define R_COUNT(n) (NODE_COUNT((n)->right)) +#define CALC_COUNT(n) (L_COUNT(n) + R_COUNT(n) + 1) +#endif + +#ifdef AVL_DEPTH +#define NODE_DEPTH(n) ((n) ? (n)->depth : 0) +#define L_DEPTH(n) (NODE_DEPTH((n)->left)) +#define R_DEPTH(n) (NODE_DEPTH((n)->right)) +#define CALC_DEPTH(n) ((L_DEPTH(n) > R_DEPTH(n) ? L_DEPTH(n) : R_DEPTH(n)) + 1) +#endif + +#ifndef AVL_DEPTH +/* Also known as ffs() (from BSD) */ +static int lg(unsigned int u) { + int r = 1; + if(!u) return 0; + if(u & 0xffff0000) { u >>= 16; r += 16; } + if(u & 0x0000ff00) { u >>= 8; r += 8; } + if(u & 0x000000f0) { u >>= 4; r += 4; } + if(u & 0x0000000c) { u >>= 2; r += 2; } + if(u & 0x00000002) r++; + return r; +} +#endif + +static int avl_check_balance(avl_node_t *avlnode) { +#ifdef AVL_DEPTH + int d; + d = R_DEPTH(avlnode) - L_DEPTH(avlnode); + return d<-1?-1:d>1?1:0; +#else +/* int d; + * d = lg(R_COUNT(avlnode)) - lg(L_COUNT(avlnode)); + * d = d<-1?-1:d>1?1:0; + */ +#ifdef AVL_COUNT + int pl, r; + + pl = lg(L_COUNT(avlnode)); + r = R_COUNT(avlnode); + + if(r>>pl+1) + return 1; + if(pl<2 || r>>pl-2) + return 0; + return -1; +#else +#error No balancing possible. +#endif +#endif +} + +#ifdef AVL_COUNT +unsigned int avl_count(const avl_tree_t *avltree) { + return NODE_COUNT(avltree->top); +} + +avl_node_t *avl_at(const avl_tree_t *avltree, unsigned int index) { + avl_node_t *avlnode; + unsigned int c; + + avlnode = avltree->top; + + while(avlnode) { + c = L_COUNT(avlnode); + + if(index < c) { + avlnode = avlnode->left; + } else if(index > c) { + avlnode = avlnode->right; + index -= c+1; + } else { + return avlnode; + } + } + return NULL; +} + +unsigned int avl_index(const avl_node_t *avlnode) { + avl_node_t *next; + unsigned int c; + + c = L_COUNT(avlnode); + + while((next = avlnode->parent)) { + if(avlnode == next->right) + c += L_COUNT(next) + 1; + avlnode = next; + } + + return c; +} +#endif + +int avl_search_closest(const avl_tree_t *avltree, const void *item, avl_compare_t cmp, avl_node_t **avlnode) { + avl_node_t *node; + int c; + + if (!avlnode) + avlnode = &node; + + if (!cmp) + { + cmp = avltree->cmp; + } + + node = avltree->top; + + if (!node) + return *avlnode = NULL, 0; + + for (;;) { + c = cmp(item, node->item); + + if(c < 0) { + if(node->left) + node = node->left; + else + return *avlnode = node, -1; + } else if(c > 0) { + if(node->right) + node = node->right; + else + return *avlnode = node, 1; + } else { + return *avlnode = node, 0; + } + } +} + +/* + * avl_search: + * Return a pointer to a node with the given item in the tree. + * If no such item is in the tree, then NULL is returned. + */ +avl_node_t *avl_search(const avl_tree_t *avltree, const void *item) { + avl_node_t *node; + return avl_search_closest(avltree, item, NULL, &node) ? NULL : node; +} + +avl_node_t *avl_search2(const avl_tree_t *avltree, const void *item, avl_compare_t cmp) { + avl_node_t *node; + return avl_search_closest(avltree, item, cmp, &node) ? NULL : node; +} + +avl_tree_t *avl_init_tree(avl_tree_t *rc, avl_compare_t cmp, avl_freeitem_t freeitem) { + if(rc) { + rc->head = NULL; + rc->tail = NULL; + rc->top = NULL; + rc->cmp = cmp; + rc->freeitem = freeitem; + } + return rc; +} + +avl_tree_t *avl_alloc_tree(avl_compare_t cmp, avl_freeitem_t freeitem) { + return avl_init_tree(malloc(sizeof(avl_tree_t)), cmp, freeitem); +} + +void avl_clear_tree(avl_tree_t *avltree) { + avltree->top = avltree->head = avltree->tail = NULL; +} + +void avl_free_nodes(avl_tree_t *avltree) { + avl_node_t *node, *next; + avl_freeitem_t freeitem; + + freeitem = avltree->freeitem; + + for(node = avltree->head; node; node = next) { + next = node->next; + if(freeitem) + freeitem(node->item); + free(node); + } + + avl_clear_tree(avltree); +} + +/* + * avl_free_tree: + * Free all memory used by this tree. If freeitem is not NULL, then + * it is assumed to be a destructor for the items referenced in the avl_ + * tree, and they are deleted as well. + */ +void avl_free_tree(avl_tree_t *avltree) { + if (avltree) { + avl_free_nodes(avltree); + free(avltree); + } +} + +static void avl_clear_node(avl_node_t *newnode) { + newnode->left = newnode->right = NULL; + #ifdef AVL_COUNT + newnode->count = 1; + #endif + #ifdef AVL_DEPTH + newnode->depth = 1; + #endif +} + +avl_node_t *avl_init_node(avl_node_t *newnode, void *item) { + if(newnode) { +/* avl_clear_node(newnode); */ + newnode->item = item; + } + return newnode; +} + +avl_node_t *avl_insert_top(avl_tree_t *avltree, avl_node_t *newnode) { + avl_clear_node(newnode); + newnode->prev = newnode->next = newnode->parent = NULL; + avltree->head = avltree->tail = avltree->top = newnode; + return newnode; +} + +avl_node_t *avl_insert_before(avl_tree_t *avltree, avl_node_t *node, avl_node_t *newnode) { + if(!node) + return avltree->tail + ? avl_insert_after(avltree, avltree->tail, newnode) + : avl_insert_top(avltree, newnode); + + if(node->left) + return avl_insert_after(avltree, node->prev, newnode); + + avl_clear_node(newnode); + + newnode->next = node; + newnode->parent = node; + + newnode->prev = node->prev; + if(node->prev) + node->prev->next = newnode; + else + avltree->head = newnode; + node->prev = newnode; + + node->left = newnode; + avl_rebalance(avltree, node); + return newnode; +} + +avl_node_t *avl_insert_after(avl_tree_t *avltree, avl_node_t *node, avl_node_t *newnode) { + if(!node) + return avltree->head + ? avl_insert_before(avltree, avltree->head, newnode) + : avl_insert_top(avltree, newnode); + + if(node->right) + return avl_insert_before(avltree, node->next, newnode); + + avl_clear_node(newnode); + + newnode->prev = node; + newnode->parent = node; + + newnode->next = node->next; + if(node->next) + node->next->prev = newnode; + else + avltree->tail = newnode; + node->next = newnode; + + node->right = newnode; + avl_rebalance(avltree, node); + return newnode; +} + +avl_node_t *avl_insert_node(avl_tree_t *avltree, avl_node_t *newnode) { + avl_node_t *node; + + if(!avltree->top) + return avl_insert_top(avltree, newnode); + + switch (avl_search_closest(avltree, newnode->item, avltree->cmp, &node)) + { + case -1: + return avl_insert_before(avltree, node, newnode); + + case 1: + return avl_insert_after(avltree, node, newnode); + + case 0: + break; + + default: + abort(); + break; + } + + return NULL; +} + +/* + * avl_insert: + * Create a new node and insert an item there. + * Returns the new node on success or NULL if no memory could be allocated. + */ +avl_node_t *avl_insert(avl_tree_t *avltree, void *item) { + avl_node_t *newnode; + + newnode = avl_init_node(malloc(sizeof(avl_node_t)), item); + if(newnode) { + if(avl_insert_node(avltree, newnode)) + return newnode; + free(newnode); + errno = EEXIST; + } + return NULL; +} + +/* + * avl_unlink_node: + * Removes the given node. Does not delete the item at that node. + * The item of the node may be freed before calling avl_unlink_node. + * (In other words, it is not referenced by this function.) + */ +void avl_unlink_node(avl_tree_t *avltree, avl_node_t *avlnode) { + avl_node_t *parent; + avl_node_t **superparent; + avl_node_t *subst, *left, *right; + avl_node_t *balnode; + + if(avlnode->prev) + avlnode->prev->next = avlnode->next; + else + avltree->head = avlnode->next; + + if(avlnode->next) + avlnode->next->prev = avlnode->prev; + else + avltree->tail = avlnode->prev; + + parent = avlnode->parent; + + superparent = parent + ? avlnode == parent->left ? &parent->left : &parent->right + : &avltree->top; + + left = avlnode->left; + right = avlnode->right; + if(!left) { + *superparent = right; + if(right) + right->parent = parent; + balnode = parent; + } else if(!right) { + *superparent = left; + left->parent = parent; + balnode = parent; + } else { + subst = avlnode->prev; + if(subst == left) { + balnode = subst; + } else { + balnode = subst->parent; + balnode->right = subst->left; + if(balnode->right) + balnode->right->parent = balnode; + subst->left = left; + left->parent = subst; + } + subst->right = right; + subst->parent = parent; + right->parent = subst; + *superparent = subst; + } + + avl_rebalance(avltree, balnode); +} + +void *avl_delete_node(avl_tree_t *avltree, avl_node_t *avlnode) { + void *item = NULL; + if(avlnode) { + item = avlnode->item; + avl_unlink_node(avltree, avlnode); + if(avltree->freeitem) + avltree->freeitem(item); + free(avlnode); + } + return item; +} + +void *avl_delete(avl_tree_t *avltree, const void *item) { + return avl_delete_node(avltree, avl_search(avltree, item)); +} + +avl_node_t *avl_fixup_node(avl_tree_t *avltree, avl_node_t *newnode) { + avl_node_t *oldnode = NULL, *node; + + if(!avltree || !newnode) + return NULL; + + node = newnode->prev; + if(node) { + oldnode = node->next; + node->next = newnode; + } else { + avltree->head = newnode; + } + + node = newnode->next; + if(node) { + oldnode = node->prev; + node->prev = newnode; + } else { + avltree->tail = newnode; + } + + node = newnode->parent; + if(node) { + if(node->left == oldnode) + node->left = newnode; + else + node->right = newnode; + } else { + oldnode = avltree->top; + avltree->top = newnode; + } + + return oldnode; +} + +/* + * avl_rebalance: + * Rebalances the tree if one side becomes too heavy. This function + * assumes that both subtrees are AVL-trees with consistant data. The + * function has the additional side effect of recalculating the count of + * the tree at this node. It should be noted that at the return of this + * function, if a rebalance takes place, the top of this subtree is no + * longer going to be the same node. + */ +void avl_rebalance(avl_tree_t *avltree, avl_node_t *avlnode) { + avl_node_t *child; + avl_node_t *gchild; + avl_node_t *parent; + avl_node_t **superparent; + + parent = avlnode; + + while(avlnode) { + parent = avlnode->parent; + + superparent = parent + ? avlnode == parent->left ? &parent->left : &parent->right + : &avltree->top; + + switch(avl_check_balance(avlnode)) { + case -1: + child = avlnode->left; + #ifdef AVL_DEPTH + if(L_DEPTH(child) >= R_DEPTH(child)) { + #else + #ifdef AVL_COUNT + if(L_COUNT(child) >= R_COUNT(child)) { + #else + #error No balancing possible. + #endif + #endif + avlnode->left = child->right; + if(avlnode->left) + avlnode->left->parent = avlnode; + child->right = avlnode; + avlnode->parent = child; + *superparent = child; + child->parent = parent; + #ifdef AVL_COUNT + avlnode->count = CALC_COUNT(avlnode); + child->count = CALC_COUNT(child); + #endif + #ifdef AVL_DEPTH + avlnode->depth = CALC_DEPTH(avlnode); + child->depth = CALC_DEPTH(child); + #endif + } else { + gchild = child->right; + avlnode->left = gchild->right; + if(avlnode->left) + avlnode->left->parent = avlnode; + child->right = gchild->left; + if(child->right) + child->right->parent = child; + gchild->right = avlnode; + if(gchild->right) + gchild->right->parent = gchild; + gchild->left = child; + if(gchild->left) + gchild->left->parent = gchild; + *superparent = gchild; + gchild->parent = parent; + #ifdef AVL_COUNT + avlnode->count = CALC_COUNT(avlnode); + child->count = CALC_COUNT(child); + gchild->count = CALC_COUNT(gchild); + #endif + #ifdef AVL_DEPTH + avlnode->depth = CALC_DEPTH(avlnode); + child->depth = CALC_DEPTH(child); + gchild->depth = CALC_DEPTH(gchild); + #endif + } + break; + case 1: + child = avlnode->right; + #ifdef AVL_DEPTH + if(R_DEPTH(child) >= L_DEPTH(child)) { + #else + #ifdef AVL_COUNT + if(R_COUNT(child) >= L_COUNT(child)) { + #else + #error No balancing possible. + #endif + #endif + avlnode->right = child->left; + if(avlnode->right) + avlnode->right->parent = avlnode; + child->left = avlnode; + avlnode->parent = child; + *superparent = child; + child->parent = parent; + #ifdef AVL_COUNT + avlnode->count = CALC_COUNT(avlnode); + child->count = CALC_COUNT(child); + #endif + #ifdef AVL_DEPTH + avlnode->depth = CALC_DEPTH(avlnode); + child->depth = CALC_DEPTH(child); + #endif + } else { + gchild = child->left; + avlnode->right = gchild->left; + if(avlnode->right) + avlnode->right->parent = avlnode; + child->left = gchild->right; + if(child->left) + child->left->parent = child; + gchild->left = avlnode; + if(gchild->left) + gchild->left->parent = gchild; + gchild->right = child; + if(gchild->right) + gchild->right->parent = gchild; + *superparent = gchild; + gchild->parent = parent; + #ifdef AVL_COUNT + avlnode->count = CALC_COUNT(avlnode); + child->count = CALC_COUNT(child); + gchild->count = CALC_COUNT(gchild); + #endif + #ifdef AVL_DEPTH + avlnode->depth = CALC_DEPTH(avlnode); + child->depth = CALC_DEPTH(child); + gchild->depth = CALC_DEPTH(gchild); + #endif + } + break; + default: + #ifdef AVL_COUNT + avlnode->count = CALC_COUNT(avlnode); + #endif + #ifdef AVL_DEPTH + avlnode->depth = CALC_DEPTH(avlnode); + #endif + } + avlnode = parent; + } +} + +void avl_foreach(avl_tree_t* tree, void (*callback)(void*)) +{ + for (avl_node_t* node = tree->head; node; node = node->next) + { + callback(node->item); + } +}; + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/avl.h b/avl.h new file mode 100644 index 0000000..851d8d3 --- /dev/null +++ b/avl.h @@ -0,0 +1,208 @@ +/***************************************************************************** + + avl.h - Source code for the AVL-tree library. + + Copyright (C) 1998 Michael H. Buselli + Copyright (C) 2000-2002 Wessel Dankers + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + Augmented AVL-tree. Original by Michael H. Buselli . + + Modified by Wessel Dankers to add a bunch of bloat to + the sourcecode, change the interface and squash a few bugs. + Mail him if you find new bugs. + +*****************************************************************************/ + +#ifndef _AVL_H +#define _AVL_H + +/* We need either depths, counts or both (the latter being the default) */ +#if !defined(AVL_DEPTH) && !defined(AVL_COUNT) +#define AVL_DEPTH +#define AVL_COUNT +#endif + +/* User supplied function to compare two items like strcmp() does. + * For example: cmp(a,b) will return: + * -1 if a < b + * 0 if a = b + * 1 if a > b + */ +typedef int (*avl_compare_t)(const void *, const void *); + +/* User supplied function to delete an item when a node is free()d. + * If NULL, the item is not free()d. + */ +typedef void (*avl_freeitem_t)(void *); + +typedef struct avl_node_t { + struct avl_node_t *next; + struct avl_node_t *prev; + struct avl_node_t *parent; + struct avl_node_t *left; + struct avl_node_t *right; + void *item; +#ifdef AVL_COUNT + unsigned int count; +#endif +#ifdef AVL_DEPTH + int depth; +#endif +} avl_node_t; + +typedef struct avl_tree_t { + avl_node_t *head; + avl_node_t *tail; + avl_node_t *top; + avl_compare_t cmp; + avl_freeitem_t freeitem; +} avl_tree_t; + +/* Initializes a new tree for elements that will be ordered using + * the supplied strcmp()-like function. + * Returns the value of avltree (even if it's NULL). + * O(1) */ +extern avl_tree_t *avl_init_tree(avl_tree_t *avltree, avl_compare_t, avl_freeitem_t); + +/* Allocates and initializes a new tree for elements that will be + * ordered using the supplied strcmp()-like function. + * Returns NULL if memory could not be allocated. + * O(1) */ +extern avl_tree_t *avl_alloc_tree(avl_compare_t, avl_freeitem_t); + +/* Frees the entire tree efficiently. Nodes will be free()d. + * If the tree's freeitem is not NULL it will be invoked on every item. + * O(n) */ +extern void avl_free_tree(avl_tree_t *); + +/* Reinitializes the tree structure for reuse. Nothing is free()d. + * Compare and freeitem functions are left alone. + * O(1) */ +extern void avl_clear_tree(avl_tree_t *); + +/* Free()s all nodes in the tree but leaves the tree itself. + * If the tree's freeitem is not NULL it will be invoked on every item. + * O(n) */ +extern void avl_free_nodes(avl_tree_t *); + +/* Initializes memory for use as a node. Returns NULL if avlnode is NULL. + * O(1) */ +extern avl_node_t *avl_init_node(avl_node_t *avlnode, void *item); + +/* Insert an item into the tree and return the new node. + * Returns NULL and sets errno if memory for the new node could not be + * allocated or if the node is already in the tree (EEXIST). + * O(lg n) */ +extern avl_node_t *avl_insert(avl_tree_t *, void *item); + +/* Insert a node into the tree and return it. + * Returns NULL if the node is already in the tree. + * O(lg n) */ +extern avl_node_t *avl_insert_node(avl_tree_t *, avl_node_t *); + +/* Insert a node in an empty tree. If avlnode is NULL, the tree will be + * cleared and ready for re-use. + * If the tree is not empty, the old nodes are left dangling. + * O(1) */ +extern avl_node_t *avl_insert_top(avl_tree_t *, avl_node_t *avlnode); + +/* Insert a node before another node. Returns the new node. + * If old is NULL, the item is appended to the tree. + * O(lg n) */ +extern avl_node_t *avl_insert_before(avl_tree_t *, avl_node_t *old, avl_node_t *new); + +/* Insert a node after another node. Returns the new node. + * If old is NULL, the item is prepended to the tree. + * O(lg n) */ +extern avl_node_t *avl_insert_after(avl_tree_t *, avl_node_t *old, avl_node_t *new); + +/* Deletes a node from the tree. Returns immediately if the node is NULL. + * The item will not be free()d regardless of the tree's freeitem handler. + * This function comes in handy if you need to update the search key. + * O(lg n) */ +extern void avl_unlink_node(avl_tree_t *, avl_node_t *); + +/* Deletes a node from the tree. Returns immediately if the node is NULL. + * If the tree's freeitem is not NULL, it is invoked on the item. + * If it is, returns the item. + * O(lg n) */ +extern void *avl_delete_node(avl_tree_t *, avl_node_t *); + +/* Searches for an item in the tree and deletes it if found. + * If the tree's freeitem is not NULL, it is invoked on the item. + * If it is, returns the item. + * O(lg n) */ +extern void *avl_delete(avl_tree_t *, const void *item); + +/* If exactly one node is moved in memory, this will fix the pointers + * in the tree that refer to it. It must be an exact shallow copy. + * Returns the pointer to the old position. + * O(1) */ +extern avl_node_t *avl_fixup_node(avl_tree_t *, avl_node_t *new); + +/* Searches for a node with the key closest (or equal) to the given item. + * If avlnode is not NULL, *avlnode will be set to the node found or NULL + * if the tree is empty. Return values: + * -1 if the returned node is smaller + * 0 if the returned node is equal or if the tree is empty + * 1 if the returned node is greater + * O(lg n) */ +int avl_search_closest(const avl_tree_t *avltree, const void *item, avl_compare_t cmp, avl_node_t **avlnode); + +/* Searches for the item in the tree and returns a matching node if found + * or NULL if not. + * O(lg n) */ +extern avl_node_t *avl_search(const avl_tree_t *, const void *item); + +avl_node_t *avl_search2(const avl_tree_t *avltree, const void *item, avl_compare_t cmp); + +void avl_foreach(avl_tree_t* tree, void (*callback)(void*)); + +#ifdef AVL_COUNT +/* Returns the number of nodes in the tree. + * O(1) */ +extern unsigned int avl_count(const avl_tree_t *); + +/* Searches a node by its rank in the list. Counting starts at 0. + * Returns NULL if the index exceeds the number of nodes in the tree. + * O(lg n) */ +extern avl_node_t *avl_at(const avl_tree_t *, unsigned int); + +/* Returns the rank of a node in the list. Counting starts at 0. + * O(lg n) */ +extern unsigned int avl_index(const avl_node_t *); +#endif + +#endif + + + + + + + + + + + + + + + + + + diff --git a/flake.lock b/flake.lock new file mode 100644 index 0000000..369b059 --- /dev/null +++ b/flake.lock @@ -0,0 +1,77 @@ +{ + "nodes": { + "flake-compat": { + "flake": false, + "locked": { + "lastModified": 1747046372, + "narHash": "sha256-CIVLLkVgvHYbgI2UpXvIIBJ12HWgX+fjA8Xf8PUmqCY=", + "owner": "edolstra", + "repo": "flake-compat", + "rev": "9100a0f413b0c601e0533d1d94ffd501ce2e7885", + "type": "github" + }, + "original": { + "owner": "edolstra", + "repo": "flake-compat", + "type": "github" + } + }, + "flake-utils": { + "inputs": { + "systems": "systems" + }, + "locked": { + "lastModified": 1731533236, + "narHash": "sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=", + "owner": "numtide", + "repo": "flake-utils", + "rev": "11707dc2f618dd54ca8739b309ec4fc024de578b", + "type": "github" + }, + "original": { + "owner": "numtide", + "repo": "flake-utils", + "type": "github" + } + }, + "nixpkgs": { + "locked": { + "lastModified": 1749664469, + "narHash": "sha256-Ar3r6cmQVKVoEy/TWAKq8WuGqZibRjMt5oZRvkn+2Bk=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "3fa008da69980bd61324ad9e3a825022f0b736f1", + "type": "github" + }, + "original": { + "owner": "NixOS", + "repo": "nixpkgs", + "type": "github" + } + }, + "root": { + "inputs": { + "flake-compat": "flake-compat", + "flake-utils": "flake-utils", + "nixpkgs": "nixpkgs" + } + }, + "systems": { + "locked": { + "lastModified": 1681028828, + "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", + "owner": "nix-systems", + "repo": "default", + "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", + "type": "github" + }, + "original": { + "owner": "nix-systems", + "repo": "default", + "type": "github" + } + } + }, + "root": "root", + "version": 7 +} diff --git a/flake.nix b/flake.nix new file mode 100644 index 0000000..e4bf75e --- /dev/null +++ b/flake.nix @@ -0,0 +1,47 @@ + +# vim: set sw=2 ts=2 et: # + +{ + description = "An example project using flutter"; + + inputs.nixpkgs = { url = "github:NixOS/nixpkgs"; }; + + inputs.flake-utils.url = "github:numtide/flake-utils"; + + inputs.flake-compat = { + url = "github:edolstra/flake-compat"; + flake = false; + }; + + outputs = { self, nixpkgs, flake-utils, ... }: + flake-utils.lib.eachDefaultSystem (system: + let pkgs = import nixpkgs { + inherit system; + + config.allowUnfree = true; + }; + in { + devShells.default = + pkgs.mkShell { + buildInputs = with pkgs; [ + gcc + gnumake + readline.dev + gmp.dev + python3 + gedit + valgrind + man-pages + man-pages-posix + ]; + }; + packages.default = pkgs.stdenv.mkDerivation rec { + pname = "qc"; + version = "1.0"; + src = ./.; + nativeBuildInputs = with pkgs; [ gcc gnumake ]; + buildInputs = with pkgs; [ readline.dev ]; + }; + }); +} + diff --git a/main.c b/main.c new file mode 100644 index 0000000..653ceee --- /dev/null +++ b/main.c @@ -0,0 +1,1767 @@ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include + +#include "avl.h" + +#define TODO assert(!"TODO"); + +#define argv0 program_invocation_name + +#ifdef ZDEBUG +#define zprintf(fmt, ...) \ + printf(fmt, ## __VA_ARGS__); +#else +#define zprintf(...); +#endif + +const char* command = NULL; + +static void parse_args(int argc, char* const* argv) +{ + for (int opt; (opt = getopt(argc, argv, "c:")) != -1; ) switch (opt) + { + case 'c': + command = optarg; + break; + + default: + { + printf("Unknown option '%c'!" "\n", opt); + exit(1); + break; + } + } +} + +struct string +{ + char* data; + unsigned refcount; +}; + +struct string* new_string(const char* data_ro) +{ + struct string* this = malloc(sizeof(*this)); + + this->data = strdup(data_ro); + this->refcount = 1; + + return this; +} + +struct string* new_string_from_fmt(const char* fmt, ...) +{ + struct string* this = malloc(sizeof(*this)); + + va_list ap; + + va_start(ap, fmt); + + int x = vasprintf(&this->data, fmt, ap); + + if (x < 0) + { + abort(); + } + + this->refcount = 1; + + va_end(ap); + + return this; +} + +struct string* inc_string(struct string* this) +{ + if (this) + this->refcount++; + + return this; +} + +void free_string(struct string* this) +{ + if (this && !--this->refcount) + { + free(this->data); + free(this); + } +} + +struct value; + +struct value* inc_value(struct value* this); + +void free_value(struct value* this); + +struct expression +{ + enum expression_kind { + ek_syntax_error, + + // primary: + ek_literal, + ek_variable, + + // arithmetic operators: + ek_positive, + ek_negative, + ek_add, + ek_subtract, + ek_multiply, + ek_divide, + + // comparision operators: + ek_greater_than, + ek_less_than, + ek_greater_than_equal_to, + ek_less_than_equal_to, + + // equality operators: + ek_equal_to, + ek_not_equal_to, + + // logical operators: + ek_logical_not, + ek_logical_and, + ek_logical_or, + + ek_ternary, + + ek_assign, + + ek_comma, + } kind; + + struct string* string; + + struct expression* center; + struct expression* left; + struct expression* right; + + struct value* value; + + unsigned refcount; +}; + +struct expression* inc_expression(struct expression* this); + +struct expression* new_syntax_error_expression( + struct string* error_message, + struct expression* subexpression) +{ + struct expression* this = malloc(sizeof(*this)); + + this->kind = ek_syntax_error; + this->string = inc_string(error_message); + this->center = inc_expression(subexpression); + this->left = this->right = NULL; + this->value = NULL; + + this->refcount = 1; + + return this; +} + +struct expression* new_literal_expression( + struct value* value) +{ + struct expression* this = malloc(sizeof(*this)); + + this->kind = ek_literal; + this->string = NULL;; + this->center = NULL; + this->left = this->right = NULL; + this->value = inc_value(value); + + this->refcount = 1; + + return this; +} + +struct expression* new_variable_expression( + struct string* variable) +{ + struct expression* this = malloc(sizeof(*this)); + + this->kind = ek_variable; + this->string = inc_string(variable); + this->center = NULL; + this->left = this->right = NULL; + this->value = NULL; + + this->refcount = 1; + + return this; +} + +struct expression* new_logical_not_expression( + struct expression* sub) +{ + struct expression* this = malloc(sizeof(*this)); + + this->kind = ek_logical_not; + this->string = NULL; + this->center = inc_expression(sub); + this->left = NULL; + this->right = NULL; + this->value = NULL; + + this->refcount = 1; + + return this; +} + +struct expression* new_positive_expression( + struct expression* sub) +{ + struct expression* this = malloc(sizeof(*this)); + + this->kind = ek_positive; + this->string = NULL; + this->center = inc_expression(sub); + this->left = NULL; + this->right = NULL; + this->value = NULL; + + this->refcount = 1; + + return this; +} + +struct expression* new_negative_expression( + struct expression* sub) +{ + struct expression* this = malloc(sizeof(*this)); + + this->kind = ek_negative; + this->string = NULL; + this->center = inc_expression(sub); + this->left = NULL; + this->right = NULL; + this->value = NULL; + + this->refcount = 1; + + return this; +} + +struct expression* new_binary_expression( + enum expression_kind kind, + struct expression* left, + struct expression* right) +{ + struct expression* this = malloc(sizeof(*this)); + + this->kind = kind; + this->string = NULL; + this->center = NULL; + this->left = inc_expression(left); + this->right = inc_expression(right); + this->value = NULL; + + this->refcount = 1; + + return this; +} + +struct expression* inc_expression(struct expression* this) +{ + if (this) + this->refcount++; + + return this; +} + +void free_expression(struct expression* this) +{ + if (this && !--this->refcount) + { + free_string(this->string); + + free_expression(this->center); + free_expression(this->left); + free_expression(this->right); + + free_value(this->value); + + free(this); + } +} + +struct value +{ + mpq_t mpq; + unsigned refcount; +}; + +struct value* new_value_from_int(int x) +{ + struct value* this = malloc(sizeof(*this)); + + mpq_init(this->mpq); + + mpq_set_si(this->mpq, x, 1); + + this->refcount = 1; + + return this; +} + +struct value* new_value(mpq_t mpq) +{ + struct value* this = malloc(sizeof(*this)); + + mpq_init(this->mpq); + + mpq_set(this->mpq, mpq); + + this->refcount = 1; + + return this; +} + +struct value* inc_value(struct value* this) +{ + if (this) + this->refcount++; + + return this; +} + +void free_value(struct value* this) +{ + if (this && !--this->refcount) + { + mpq_clear(this->mpq); + + free(this); + } +} + +struct value* scan(const char* text) +{ + const char* moving = text; + + mpq_t base; mpq_init(base); + mpq_t tmp; mpq_init(tmp); + mpq_t value; mpq_init(value); + + mpq_set_si(value, 0, 1); + mpq_set_si(base, 10, 1); + + while ('0' <= *moving && *moving <= '9') + { + mpq_set_si(tmp, *moving++ - '0', 1); + + mpq_mul(value, value, base); + mpq_add(value, value, tmp); + } + + if (*moving == '.') + { + mpq_t factor; mpq_init(factor); + + TODO; + + mpq_clear(factor); + } + + assert(!*moving); + + struct value* retval = new_value(value); + + mpq_clear(base); + mpq_clear(tmp); + mpq_clear(value); + + return retval; +} + +struct scope +{ + avl_tree_t* tree; +}; + +struct variable +{ + struct string* name; + struct value* value; +}; + +struct variable* new_variable(struct string* name) +{ + struct variable* this = malloc(sizeof(*this)); + + this->name = inc_string(name); + this->value = NULL; + + return this; +} + +int compare_variable(const void* a, const void* b) +{ + const struct variable* A = a, *B = b; + + return strcmp(A->name->data, B->name->data); +} + +void free_variable(void* ptr) +{ + struct variable* this = ptr; + + free_string(this->name); + + free_value(this->value); + + free(this); +} + +struct scope* new_scope(void) +{ + struct scope* this = malloc(sizeof(*this)); + + this->tree = avl_alloc_tree(compare_variable, free_variable); + + return this; +} + +struct value** scope_lookup( + struct scope* this, + struct string* name) +{ + struct avl_node_t* node = avl_search(this->tree, &name); + + if (!node) + { + struct variable* var = new_variable(name); + + node = avl_insert(this->tree, var); + } + + return &((struct variable*) node->item)->value; +} + +void free_scope(struct scope* this) +{ + avl_free_tree(this->tree); + + free(this); +} + +struct expression* parse(const char* text) +{ + enum { + tk_uninitialized, + + // primary: + tk_identifier, + tk_literal, + + // brakets: + tk_oparen, + tk_cparen, + + // arithmetic operators: + tk_plus, + tk_minus, + tk_asterisk, + tk_slash, + + // comparision operators: + tk_less_than, + tk_less_than_eq, + + tk_greater_than, + tk_greater_than_eq, + + tk_emarkequals, + tk_equalsequals, + + // logical operators: + tk_vbarvbar, + tk_ampersandampersand, + + // misc: + tk_emark, + tk_qmark, + tk_colon, + tk_equals, + tk_comma, + + tk_EOF, + + number_of_tokens, + } tokenkind = tk_uninitialized; + + static const char* const tokennames[number_of_tokens] = { + [tk_EOF] = "EOF", + + [tk_identifier] = "identifier", + [tk_literal] = "literal", + [tk_oparen] = "(", + [tk_cparen] = ")", + [tk_plus] = "+", + [tk_minus] = "-", + [tk_asterisk] = "*", + [tk_slash] = "/", + [tk_less_than] = "<", + [tk_less_than_eq] = "<=", + [tk_greater_than] = ">", + [tk_greater_than_eq] = ">=", + [tk_emarkequals] = "!=", + [tk_equalsequals] = "==", + [tk_vbarvbar] = "||", + [tk_ampersandampersand] = "&&", + [tk_emark] = "!", + [tk_qmark] = "?", + [tk_colon] = ":", + [tk_equals] = "=", + [tk_comma] = ",", + }; + + struct { + char* data; + size_t n, cap; + } buffer = {}; + + void append(char c) + { + if (buffer.n == buffer.cap) + { + buffer.cap = buffer.cap << 1 ?: 1; + buffer.data = realloc(buffer.data, sizeof(*buffer.data) * buffer.cap); + } + + buffer.data[buffer.n++] = c; + } + + const char* moving = text; + + void next_token(void) + { + while (*moving && *moving == ' ') + moving++; + + switch (*moving) + { + case 0: + tokenkind = tk_EOF; + break; + + case '0' ... '9': + { + buffer.n = 0; + + while (false + || *moving == '_' + || ('0' <= *moving && *moving <= '9')) + { + append(*moving++); + } + + append(0); + + tokenkind = tk_literal; + break; + } + + case '_': + case 'a' ... 'z': + case 'A' ... 'Z': + { + buffer.n = 0; + + while (false + || *moving == '_' + || ('a' <= *moving && *moving <= 'z') + || ('A' <= *moving && *moving <= 'Z')) + { + append(*moving++); + } + + append(0); + + tokenkind = tk_identifier; + break; + } + + case '+': tokenkind = tk_plus, moving++; break; + case '-': tokenkind = tk_minus, moving++; break; + case '*': tokenkind = tk_asterisk, moving++; break; + case '/': tokenkind = tk_slash, moving++; break; + + case '(': tokenkind = tk_oparen, moving++; break; + case ')': tokenkind = tk_cparen, moving++; break; + + case '?': tokenkind = tk_qmark, moving++; break; + case ':': tokenkind = tk_colon, moving++; break; + + case ',': tokenkind = tk_comma, moving++; break; + + case '|': + { + moving++; + + switch (*moving) + { + case '|': + tokenkind = tk_vbarvbar, moving++; + break; + + default: + { + TODO; + break; + } + } + + break; + } + + case '&': + { + moving++; + + switch (*moving) + { + case '&': + tokenkind = tk_ampersandampersand, moving++; + break; + + default: + { + TODO; + break; + } + } + + break; + } + + case '!': + { + moving++; + + switch (*moving) + { + case '=': + tokenkind = tk_emarkequals, moving++; + break; + + default: + tokenkind = tk_emark; + break; + } + + break; + } + + case '<': + { + moving++; + + switch (*moving) + { + case '=': + tokenkind = tk_less_than_eq, moving++; + break; + + default: + { + tokenkind = tk_less_than; + break; + } + } + + break; + } + + case '>': + { + moving++; + + switch (*moving) + { + case '=': + tokenkind = tk_greater_than_eq, moving++; + break; + + default: + { + tokenkind = tk_greater_than; + break; + } + } + + break; + } + + case '=': + { + moving++; + + switch (*moving) + { + case '=': + tokenkind = tk_equalsequals, moving++; + break; + + default: + tokenkind = tk_equals, moving++; + break; + } + + break; + } + + default: + assert(!"TODO"); + break; + } + } + + next_token(); + + struct expression* parse_root(void) + { + struct expression* parse_comma(void) + { + struct expression* parse_assign(void) + { + struct expression* parse_ternary(void) + { + struct expression* parse_logicals(void) + { + struct expression* parse_equality(void) + { + struct expression* parse_comparision(void) + { + struct expression* parse_arithmetic(void) + { + struct expression* parse_prefix(void) + { + struct expression* parse_primary(void) + { + struct expression* retval; + + switch (tokenkind) + { + case tk_identifier: + { + struct string* name = + new_string(buffer.data); + + retval = new_variable_expression(name); + next_token(); + + free_string(name); + break; + } + + case tk_literal: + { + struct value* value = + scan(buffer.data); + + retval = new_literal_expression(value); + + next_token(); + + free_value(value); + break; + } + + case tk_oparen: + { + next_token(); + + retval = parse_root(); + + if (tokenkind != tk_cparen) + { + struct string* message = + new_string_from_fmt( + "unexpected '%s'", + tokennames[tokenkind]); + + retval = new_syntax_error_expression(message, retval); + + free_string(message); + } + + next_token(); + break; + } + + default: + { + struct string* message = + new_string_from_fmt( + "unexpected '%s'", + tokennames[tokenkind]); + + retval = new_syntax_error_expression(message, NULL); + + free_string(message); + break; + } + } + + return retval; + } + + switch (tokenkind) + { + case tk_emark: + { + next_token(); + + struct expression* sub = + parse_prefix(); + + struct expression* retval = + new_logical_not_expression(sub); + + free_expression(sub); + + return retval; + } + + case tk_plus: + { + next_token(); + + struct expression* sub = + parse_prefix(); + + struct expression* retval = + new_positive_expression(sub); + + free_expression(sub); + + return retval; + } + + case tk_minus: + { + next_token(); + + struct expression* sub = + parse_prefix(); + + struct expression* retval = + new_negative_expression(sub); + + free_expression(sub); + + return retval; + } + + default: + return parse_primary(); + } + } + + struct expression* left = parse_prefix(); + + again: switch (tokenkind) + { + case tk_plus: + { + next_token(); + + struct expression* right = + parse_prefix(); + + struct expression* retval = + new_binary_expression(ek_add, left, right); + + free_expression(left); + free_expression(right); + + left = retval; + + goto again; + } + + case tk_minus: + { + next_token(); + + struct expression* right = + parse_prefix(); + + struct expression* retval = + new_binary_expression(ek_subtract, left, right); + + free_expression(left); + free_expression(right); + + left = retval; + + goto again; + } + + case tk_asterisk: + { + next_token(); + + struct expression* right = + parse_prefix(); + + struct expression* retval = + new_binary_expression(ek_multiply, left, right); + + free_expression(left); + free_expression(right); + + left = retval; + + goto again; + } + + case tk_slash: + { + next_token(); + + struct expression* right = + parse_prefix(); + + struct expression* retval = + new_binary_expression(ek_divide, left, right); + + free_expression(left); + free_expression(right); + + left = retval; + + goto again; + } + + default: + break; + } + + return left; + } + + struct expression* left = parse_arithmetic(); + + again: switch (tokenkind) + { + case tk_less_than: + { + next_token(); + + struct expression* right = + parse_arithmetic(); + + struct expression* retval = + new_binary_expression(ek_less_than, left, right); + + free_expression(left); + free_expression(right); + + left = retval; + + goto again; + } + + case tk_less_than_eq: + { + next_token(); + + struct expression* right = + parse_arithmetic(); + + struct expression* retval = + new_binary_expression(ek_less_than_equal_to, left, right); + + free_expression(left); + free_expression(right); + + left = retval; + + goto again; + } + + case tk_greater_than: + { + next_token(); + + struct expression* right = + parse_arithmetic(); + + struct expression* retval = + new_binary_expression(ek_greater_than, left, right); + + free_expression(left); + free_expression(right); + + left = retval; + + goto again; + } + + case tk_greater_than_eq: + { + next_token(); + + struct expression* right = + parse_arithmetic(); + + struct expression* retval = + new_binary_expression(ek_greater_than_equal_to, left, right); + + free_expression(left); + free_expression(right); + + left = retval; + + goto again; + } + + default: + break; + } + + return left; + } + + struct expression* left = parse_comparision(); + + again: switch (tokenkind) + { + case tk_equalsequals: + { + next_token(); + + struct expression* right = + parse_comparision(); + + struct expression* retval = + new_binary_expression(ek_equal_to, left, right); + + free_expression(left); + free_expression(right); + + left = retval; + + goto again; + } + + case tk_emarkequals: + { + next_token(); + + struct expression* right = + parse_comparision(); + + struct expression* retval = + new_binary_expression(ek_not_equal_to, left, right); + + free_expression(left); + free_expression(right); + + left = retval; + + goto again; + } + + default: + break; + } + + return left; + } + + struct expression* left = parse_equality(); + + again: switch (tokenkind) + { + case tk_vbarvbar: + { + next_token(); + + struct expression* right = + parse_equality(); + + struct expression* retval = + new_binary_expression(ek_logical_or, left, right); + + free_expression(left); + free_expression(right); + + left = retval; + + goto again; + } + + case tk_ampersandampersand: + { + next_token(); + + struct expression* right = + parse_equality(); + + struct expression* retval = + new_binary_expression(ek_logical_and, left, right); + + free_expression(left); + free_expression(right); + + left = retval; + + goto again; + } + + default: + break; + } + + return left; + } + + struct expression* center = parse_logicals(); + + if (tokenkind == tk_qmark) + { + next_token(); + + struct expression* left = parse_logicals(); + + next_token(); + + struct expression* right = parse_logicals(); + + TODO; + } + else + { + return center; + } + } + + struct expression* left = parse_ternary(); + + if (tokenkind == tk_equals) + { + next_token(); + + struct expression* right = parse_assign(); + + struct expression* retval = new_binary_expression(ek_assign, left, right); + + free_expression(left); + free_expression(right); + + return retval; + } + else + { + return left; + } + } + + struct expression* left = parse_assign(); + + again: switch (tokenkind) + { + case tk_comma: + { + next_token(); + + struct expression* right = parse_assign(); + + struct expression* retval = new_binary_expression(ek_comma, left, right); + + free_expression(left); + free_expression(right); + + left = retval; + + goto again; + } + + default: + break; + } + + return left; + } + + return parse_comma(); + } + + struct expression* root = parse_root(); + + if (tokenkind != tk_EOF) + { + struct string* message = new_string_from_fmt( + "expected EOF, unexpected '%s'", tokennames[tokenkind]); + + struct expression* error = new_syntax_error_expression(message, root); + + free_expression(root); + + root = error; + + free_string(message); + } + + free(buffer.data); + + return root; +} + +struct value** evaluate_lvalue( + struct expression* expression, + struct scope* scope) +{ + switch (expression->kind) + { + case ek_syntax_error: assert(0); break; + + case ek_variable: + { + return scope_lookup(scope, expression->string); + } + + case ek_literal: TODO; break; + + case ek_add: TODO; break; + case ek_subtract: TODO; break; + case ek_multiply: TODO; break; + case ek_divide: TODO; break; + + case ek_positive: TODO; break; + case ek_negative: TODO; break; + + case ek_greater_than: TODO; break; + case ek_greater_than_equal_to: TODO; break; + case ek_less_than: TODO; break; + case ek_less_than_equal_to: TODO; break; + + case ek_equal_to: TODO; break; + case ek_not_equal_to: TODO; break; + + case ek_logical_not: TODO; break; + case ek_logical_and: TODO; break; + case ek_logical_or: TODO; break; + + case ek_ternary: TODO; break; + + case ek_assign: + { + TODO; + break; + }; + + case ek_comma: + { + TODO; + break; + }; + } + + return NULL; +} + +struct value* evaluate( + struct expression* expression, + struct scope* scope) +{ + struct value* retval = NULL; + + switch (expression->kind) + { + case ek_syntax_error: assert(0); break; + + case ek_variable: + { + struct value* val = *scope_lookup(scope, expression->string); + + if (val) + { + retval = inc_value(val); + } + else + { + retval = new_value_from_int(0); + } + + break; + }; + + case ek_literal: + { + retval = inc_value(expression->value); + break; + } + + case ek_add: + { + struct value* left = evaluate(expression->left, scope); + struct value* right = evaluate(expression->right, scope); + + mpq_t q; + + mpq_init(q); + + mpq_add(q, left->mpq, right->mpq); + + retval = new_value(q); + + free_value(left), free_value(right); + + mpq_clear(q); + break; + } + + case ek_subtract: + { + struct value* left = evaluate(expression->left, scope); + struct value* right = evaluate(expression->right, scope); + + mpq_t q; + + mpq_init(q); + + mpq_sub(q, left->mpq, right->mpq); + + retval = new_value(q); + + free_value(left), free_value(right); + + mpq_clear(q); + break; + } + + case ek_multiply: + { + struct value* left = evaluate(expression->left, scope); + struct value* right = evaluate(expression->right, scope); + + mpq_t q; + + mpq_init(q); + + mpq_mul(q, left->mpq, right->mpq); + + retval = new_value(q); + + free_value(left), free_value(right); + + mpq_clear(q); + break; + } + + case ek_divide: + { + struct value* left = evaluate(expression->left, scope); + struct value* right = evaluate(expression->right, scope); + + mpq_t q; + + mpq_init(q); + + mpq_div(q, left->mpq, right->mpq); + + retval = new_value(q); + + free_value(left), free_value(right); + + mpq_clear(q); + break; + } + + case ek_positive: + { + retval = evaluate(expression->center, scope); + break; + } + + case ek_negative: + { + struct value* sub = evaluate(expression->center, scope); + + mpq_t q; + + mpq_init(q); + + mpq_neg(q, sub->mpq); + + retval = new_value(q); + + free_value(sub); + + mpq_clear(q); + break; + } + + case ek_greater_than: + { + struct value* left = evaluate(expression->left, scope); + struct value* right = evaluate(expression->right, scope); + + retval = new_value_from_int(mpq_cmp(left->mpq, right->mpq) > 0); + + free_value(left), free_value(right); + break; + } + + case ek_greater_than_equal_to: + { + struct value* left = evaluate(expression->left, scope); + struct value* right = evaluate(expression->right, scope); + + retval = new_value_from_int(mpq_cmp(left->mpq, right->mpq) >= 0); + + free_value(left), free_value(right); + break; + } + + case ek_less_than: + { + struct value* left = evaluate(expression->left, scope); + struct value* right = evaluate(expression->right, scope); + + retval = new_value_from_int(mpq_cmp(left->mpq, right->mpq) < 0); + + free_value(left), free_value(right); + break; + } + + case ek_less_than_equal_to: + { + struct value* left = evaluate(expression->left, scope); + struct value* right = evaluate(expression->right, scope); + + retval = new_value_from_int(mpq_cmp(left->mpq, right->mpq) <= 0); + + free_value(left), free_value(right); + break; + } + + case ek_equal_to: + { + struct value* left = evaluate(expression->left, scope); + struct value* right = evaluate(expression->right, scope); + + retval = new_value_from_int(mpq_cmp(left->mpq, right->mpq) == 0); + + free_value(left), free_value(right); + break; + } + + case ek_not_equal_to: + { + struct value* left = evaluate(expression->left, scope); + struct value* right = evaluate(expression->right, scope); + + retval = new_value_from_int(mpq_cmp(left->mpq, right->mpq) != 0); + + free_value(left), free_value(right); + break; + } + + case ek_logical_not: + { + struct value* sub = evaluate(expression->center, scope); + + retval = new_value_from_int(mpq_sgn(sub->mpq) == 0); + + free_value(sub); + break; + } + + case ek_logical_and: + { + struct value* left = evaluate(expression->left, scope); + struct value* right = evaluate(expression->right, scope); + + retval = new_value_from_int( + mpq_sgn(left->mpq) != 0 && mpq_sgn(right->mpq) != 0); + + free_value(left), free_value(right); + break; + } + + case ek_logical_or: + { + struct value* left = evaluate(expression->left, scope); + struct value* right = evaluate(expression->right, scope); + + retval = new_value_from_int( + mpq_sgn(left->mpq) != 0 || mpq_sgn(right->mpq) != 0); + + free_value(left), free_value(right); + break; + } + + case ek_ternary: TODO; break; + + case ek_assign: + { + struct value** lvalue = evaluate_lvalue(expression->left, scope); + + retval = evaluate(expression->right, scope); + + free_value(*lvalue), *lvalue = inc_value(retval); + + break; + }; + + case ek_comma: + { + free_value(evaluate(expression->left, scope)); + + retval = evaluate(expression->right, scope); + + break; + }; + } + + return retval; +} + +void print_mpz(const mpz_t x_ro, const char* digits[10]) +{ + assert(mpz_sgn(x_ro) > 0); + + mpz_t x, d, b; + + mpz_init(x), mpz_init(d), mpz_init(b); + + mpz_set(x, x_ro); + + mpz_set_ui(b, 10); + + size_t n = mpz_sizeinbase(x, 10); + + unsigned *buffer = malloc(sizeof(*buffer) * n); + unsigned *moving = buffer; + + while (mpz_sgn(x)) + { + mpz_fdiv_qr(x, d, x, b); + + assert(mpz_fits_uint_p(d)); + + unsigned int diu = mpz_get_ui(d); + + assert(diu < 10); + + *moving++ = diu; + } + + while (moving > buffer) + { + printf("%s", digits[*--moving]); + } + + free(buffer); + + mpz_clear(x), mpz_clear(d), mpz_clear(b); +} + +void print(struct value* this) +{ + mpq_t v; + + mpq_init(v); + + mpq_set(v, this->mpq); + + if (mpq_sgn(v) == 0) + { + putchar('0'); + } + else + { + if (mpq_sgn(v) < 0) + { + putchar('-'); + + mpq_abs(v, v); + } + + mpz_t q; + + mpz_init(q); + + mpz_fdiv_q(q, mpq_numref(v), mpq_denref(v)); + + if (mpz_sgn(q) > 0) + { + print_mpz(q, + (const char*[10]){"0", "1", "2", "3", "4", "5", "6", "7", "8", "9"}); + + putchar(' '); + } + + mpq_t qq; + + mpq_init(qq); + + mpq_set_z(qq, q); + + mpq_sub(v, v, qq); + + if (mpq_sgn(v) > 0) + { + print_mpz(mpq_numref(v), + (const char*[10]){"⁰", "¹", "²", "³", "⁴", "⁵", "⁶", "⁷", "⁸", "⁹"}); + + putchar('/'); + + print_mpz(mpq_denref(v), + (const char*[10]){"₀", "₁", "₂", "₃", "₄", "₅", "₆", "₇", "₈", "₉"}); + } + + mpq_clear(qq); + + mpz_clear(q); + } + + mpq_clear(v); + + puts(""); +} + +void print_syntax_error( + const struct expression* this) +{ + assert(this->kind == ek_syntax_error); + + printf("syntax error: %s\n", this->string->data); +} + +bool foreach_syntax_error( + struct expression* exp, + void (*callback)( + const struct expression*)) +{ + bool retval = false; + + if (exp->kind == ek_syntax_error) + { + callback(exp); + + retval = true; + } + + if (exp->center && foreach_syntax_error(exp->center, callback)) + { + retval = true; + } + + if (exp->left && foreach_syntax_error(exp->left, callback)) + { + retval = true; + } + + if (exp->right && foreach_syntax_error(exp->right, callback)) + { + retval = true; + } + + return retval; +} + +int main(int argc, char* const* argv) +{ + parse_args(argc, argv); + + if (command) + { + struct scope* scope = new_scope(); + + struct expression* expression = parse(command); + + bool any_syntax_errors = foreach_syntax_error(expression, + print_syntax_error); + + if (!any_syntax_errors) + { + struct value* value = evaluate(expression, scope); + + print(value); + + free_value(value); + } + + free_expression(expression); + + free_scope(scope); + } + else + { + struct scope* scope = new_scope(); + + for (char* line; (line = readline(">>> ")); free(line)) + { + if (!*line) continue; + + add_history(line); + + struct expression* expression = parse(line); + + struct value* value = evaluate(expression, scope); + + print(value); + + free_value(value); + + free_expression(expression); + } + + free_scope(scope); + } + + return 0; +} + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/makefile b/makefile new file mode 100644 index 0000000..49b79e0 --- /dev/null +++ b/makefile @@ -0,0 +1,41 @@ + +# vim: noexpandtab tabstop=4 : + +PREFIX ?= ${out} +PREFIX ?= ${HOME} + +args = + +cc = gcc + +cppflags += -D _GNU_SOURCE +#cppflags += -D ZDEBUG=1 + +cflags = -Werror -Wall -Wextra -Wstrict-prototypes -Wfatal-errors + +# cflags += -O3 +cflags += -g + +cflags += -Wno-unused + +ldflags += -lreadline -lgmp + +bin/qc: main.c avl.c | bin/ + $(cc) $(cppflags) $(cflags) $^ -o $@ $(ldflags) + +bin/: + mkdir -pv bin + +run: bin/qc + $< $(args) + +valrun: bin/qc + valgrind -- $< $(args) + +install: ${PREFIX}/bin/qc + +${PREFIX}/bin/qc: bin/qc + mkdir -pv ${PREFIX}/bin/ + cp -vau $< $@ + +