Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

WIP: Binary expression tree calculator #647

Draft
wants to merge 5 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
289 changes: 289 additions & 0 deletions src/calculator_exptree.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,289 @@
// calculator_exptree.c
// TODO: file description

#include <bstrlib.h>
#include <bstrlib_helper.h>
#include "calculator_exptree.h"
#include <perfgroup.h> /* CounterList */
#include <ctype.h> /* isspace */
#include <errno.h> /* errno */
#include <math.h> /* NAN / isnan */
#include <stdio.h> /* printf / fprintf */
#include <stdlib.h> /* malloc / free */
#include <string.h> /* strncmp / strlen */

#ifndef NAN
# define NAN (0.0/0.0)
#endif

#define NODE_NULL_VALUE NAN
#define NODE_NULL_OPERATOR '\0'

struct exptree_node {
struct exptree_node *left; // Left child
struct exptree_node *right; // Right child

double value; // Operand value (if it's a number)
char *counter_name;
char operator; // Operator: '+', '-', '*', '/'
};

// Forward declarations
static struct exptree_node *_make_expression_tree(const char **expr);
static struct exptree_node *_make_term_tree(const char **expr);
static struct exptree_node *_make_factor_tree(const char **expr);

static void _skip_spaces(const char **expr)
{
while (isspace(**expr)) {
(*expr)++;
}
}

// Set value and create a leaf node
static struct exptree_node *_make_value_node(double value)
{
struct exptree_node *node = malloc(sizeof(struct exptree_node));
if (!node) {
return NULL;
}
*node = (struct exptree_node){.left = NULL,
.right = NULL,
.value = value,
.counter_name = NULL,
.operator= NODE_NULL_OPERATOR };
return node;
}

// Set counter and create a leaf node
static struct exptree_node *_make_counter_node(char *counter)
{
struct exptree_node *node = malloc(sizeof(struct exptree_node));
if (!node) {
return NULL;
}
*node = (struct exptree_node){.left = NULL,
.right = NULL,
.value = NODE_NULL_VALUE,
.counter_name = counter,
.operator = NODE_NULL_OPERATOR };
return node;
}

// Parse an operator and create an operator node
static struct exptree_node *
_make_operator_node(char operator, struct exptree_node *left, struct exptree_node *right)
{
struct exptree_node *node = malloc(sizeof(struct exptree_node));
if (!node) {
return NULL;
}
*node = (struct exptree_node){.left = left,
.right = right,
.value = NODE_NULL_VALUE,
.counter_name = NULL,
.operator = operator};
return node;
}

// Parse factors: numbers or subexpressions in parentheses
static struct exptree_node *_make_factor_tree(const char **expr)
{
_skip_spaces(expr);
if (**expr == '(') {
(*expr)++; // Skip '('
// Recursively parse the subexpression:
struct exptree_node *subtree = _make_expression_tree(expr);
_skip_spaces(expr);
if (**expr == ')') {
(*expr)++; // Skip ')'
} else {
fprintf(stderr, "Error: Mismatched parentheses\n");
exit(EXIT_FAILURE);
}
return subtree;
} else {
char *endptr;
errno = 0;
double value = strtod(*expr, &endptr);
if (*expr == endptr) {
// No conversion performed
char *counter_name;
if (sscanf(*expr, " %m[^()+-*/ \n] %*s", &counter_name) == 1) {
*expr += strlen(counter_name);
return _make_counter_node(counter_name);
} else {
fprintf(stderr, "Error: Could not parse: %s\n", *expr);
exit(EXIT_FAILURE);
}
} else if (errno) {
/* Underflow or Overflow */
/* value is DBL_MIN / HUGE_VAL (TODO: is this ok?) */
}
*expr = endptr;
return _make_value_node(value);
}
}

// Parse terms: handles multiplication and division
static struct exptree_node *_make_term_tree(const char **expr)
{
struct exptree_node *left = _make_factor_tree(expr);
while (1) {
_skip_spaces(expr);
if (**expr == '*' || **expr == '/') {
char operator = **expr;
(*expr)++;
struct exptree_node *right = _make_factor_tree(expr);
left = _make_operator_node(operator, left, right);
} else {
break;
}
}
return left;
}

// Parse expressions: handles addition and subtraction
static struct exptree_node *_make_expression_tree(const char **expr)
{
struct exptree_node *left = _make_term_tree(expr);
while (1) {
_skip_spaces(expr);
if (**expr == '+' || **expr == '-') {
char operator = **expr;
(*expr)++;
struct exptree_node *right = _make_term_tree(expr);
left = _make_operator_node(operator, left, right);
} else {
break;
}
}
return left;
}

struct exptree_node *make_expression_tree(const char *expr)
{
return _make_expression_tree(&expr);
}

// Print the expression tree in in-order traversal
static void _print_expression_tree(const struct exptree_node *node)
{
if (!node) {
return;
}
if (node->operator) {
printf("(");
}
_print_expression_tree(node->left);
if (node->operator) {
printf(" %c ", node->operator);
} else if (node->counter_name) {
printf("%s", node->counter_name);
} else {
printf("%g", node->value);
}
_print_expression_tree(node->right);
if (node->operator) {
printf(")");
}
}

// Print the expression tree in in-order traversal
void print_expression_tree(const struct exptree_node *node)
{
if (!node) {
printf("Empty expression tree\n");
return;
}
_print_expression_tree(node);
printf("\n");
}

// Free the memory used by the tree
void free_expression_tree(struct exptree_node *node)
{
if (!node) {
return;
}
free_expression_tree(node->left);
free_expression_tree(node->right);
free(node->counter_name);
free(node);
}

// Gets node's value (returns NAN on error)
static double _get_value(const struct exptree_node *node, const CounterList *clist)
{
if (!node->counter_name) {
return node->value;
}

size_t len = strlen(node->counter_name);

/* TODO: set counter index when making the counter node to avoid redundant search */
/* only ok if order does not change */
for (int ctr = 0; clist->counters; ++ctr) {
const char *cname = bdata(clist->cnames->entry[ctr]);

if (len == strlen(cname) && !strncmp(node->counter_name, cname, len)) {
const char *value_str = bdata(clist->cvalues->entry[ctr]);
/* TODO: why are counter values stored as strings instead of
* unsigned long long ? */

char *endptr;
errno = 0;
double value = strtod(value_str, &endptr);
if (value_str == endptr) {
// no conversion performed
fprintf(stderr, "Error: no conversion performed on %s\n", value_str);
return NODE_NULL_VALUE;
} else if (errno) {
/* Underflow or Overflow */
/* value is DBL_MIN / HUGE_VAL (TODO: is this ok?) */
}
return value;
}
}

fprintf(stderr, "Error: counter not found: %s\n", node->counter_name);
return NODE_NULL_VALUE;
}

// Evaluates the expression tree recursively (returns NAN on error)
double evaluate_expression_tree(const struct exptree_node *node, const CounterList *clist)
{
if (!node) {
return NODE_NULL_VALUE;
}

// If it's a leaf node (number/counter), return its value
if (node->operator == NODE_NULL_OPERATOR) {
return _get_value(node, clist);
}

// If it's not a leaf node, it must have two child (may change in case of unary operators)

// Recursively evaluate left and right subtrees
double val_left = evaluate_expression_tree(node->left, clist);
double val_right = evaluate_expression_tree(node->right, clist);

// Apply the operator
switch (node->operator) {
case '+':
return val_left + val_right;
case '-':
return val_left - val_right;
case '*':
return val_left * val_right;
case '/':
if (val_right == 0.0) {
fprintf(stderr, "Error: Division by zero\n");
return NODE_NULL_VALUE;
}
return val_left / val_right;
default:
fprintf(stderr, "Error: Unknown operator '%c'\n", node->operator);
return NODE_NULL_VALUE;
}
}
1 change: 1 addition & 0 deletions src/cpustring.c
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
#include <stdio.h>
#include <math.h>

#include <bstrlib.h>
#include <likwid.h>

#define MAX(a, b) (((a) > (b)) ? (a) : (b))
Expand Down
55 changes: 55 additions & 0 deletions src/includes/calculator_exptree.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
// calculator_exptree.h
// TODO: file description

#ifndef CALCULATOR_EXPTREE_H_INCLUDED
#define CALCULATOR_EXPTREE_H_INCLUDED

/**
* @brief Forward declaration of struct exptree_node
*
*/
struct exptree_node;

// cannot fwd declare CounterList because it was an anonymous struct (changed)
// thus we named CounterList to avoid unnecessary inclusion dependency with:
// #include "perfgroup.h"
/**
* @brief Forward declaration of struct CounterList
*
*/
struct CounterList;

// TODO: do we want "print_expression_tree"?

/**
* @brief Builds a binary expression tree from expression expr
*
* @param expr The expression
* @return struct exptree_node* Expression tree node on success, NULL on error
*/
extern struct exptree_node *make_expression_tree(const char *expr);

/**
* @brief Deallocates the binary expression tree
*
* @param root The root node
*/
extern void free_expression_tree(struct exptree_node *root);

/**
* @brief Evaluates the expression tree with values from counter list
*
* @param root The root node
* @param clist The counter list
* @return double The value of the expression tree evaluation on success, NAN on error
*/
extern double evaluate_expression_tree(const struct exptree_node *root, const struct CounterList *clist);

/**
* @brief Prints the expression tree in infix order
*
* @param root The root node
*/
extern void print_expression_tree(const struct exptree_node *root);

#endif /* CALCULATOR_EXPTREE_H_INCLUDED */
3 changes: 3 additions & 0 deletions src/includes/likwid.h
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,8 @@
#include <stdint.h>
#include <string.h>

#include "calculator_exptree.h" // fwd declaration of struct exptree_node (maybe move to tree_types.h ?)

#define DEBUGLEV_ONLY_ERROR 0
#define DEBUGLEV_INFO 1
#define DEBUGLEV_DETAIL 2
Expand Down Expand Up @@ -1284,6 +1286,7 @@ typedef struct {
int nmetrics; /*!< \brief Number of metrics */
char **metricnames; /*!< \brief Metric names */
char **metricformulas; /*!< \brief Metric formulas */
struct exptree_node **metrictrees; /*!< \brief Metric expression trees */
char *longinfo; /*!< \brief Descriptive text about the group or empty */
} GroupInfo;

Expand Down
6 changes: 3 additions & 3 deletions src/includes/perfgroup.h
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
#include <bstrlib_helper.h>

#include <likwid.h>
#include "calculator_exptree.h"

typedef enum {
GROUP_NONE = 0,
Expand All @@ -54,7 +55,7 @@ static char* groupFileSectionNames[MAX_GROUP_FILE_SECTIONS] = {
"LUA"
};

typedef struct {
typedef struct CounterList {
int counters; /*!< \brief Number of entries in the list */
struct bstrList* cnames; /*!< \brief List of counter names */
struct bstrList* cvalues; /*!< \brief List of counter values */
Expand All @@ -79,8 +80,7 @@ extern int update_clist(CounterList* clist, char* counter, double result);
extern void destroy_clist(CounterList* clist);

extern int calc_metric(char* formula, CounterList* clist, double *result);


extern int calc_metric_new(const struct exptree_node* tree, const CounterList* clist, double *result);


#endif /* PERFGROUP_H */
Loading
Loading