commit 9be542c93ce71d84dddc11bbcf1864d3095a176a Author: Zander Thannhauser Date: Sat Jul 26 19:35:56 2025 -0500 first commit 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 $< $@ + +