diff --git a/bexp/bexp.c b/bexp/bexp.c new file mode 100644 index 0000000..7588842 --- /dev/null +++ b/bexp/bexp.c @@ -0,0 +1,122 @@ +// vim: set ts=4 sw=4 et tw=80: + +#include "bexp.h" +#include + +static int value_length(const value* val); + +inline int value_length(const value* val) { + return val->end - val->begin; +} + +typedef int(*compute_func)(char*, int, int, int*); + +int bexp_loop(value* result, const expression* ex, compute_func compute) { + const char* end1 = ex->value1.end; + const char* end2 = ex->value2.end; + + char* l1 = ex->value1.begin; + char* l2 = (ex->op == NOT || ex->op == COMPLEMENT) ? NULL : + ex->value2.begin; + + int acc = 0; + + char* r = result->begin; + + while (l1 < end1 || (l2 != NULL && l2 < end2)) { + if (r == result->end) { + return 0; + } + + const int a = l1 < end1 ? *l1 - '0' : 0; + const int b = l2 != NULL && l2 < end2 ? *l2 - '0' : 0; + + *r = '0'; + + if (compute(r, a, b, &acc)) { + return 1; + } + + if (ex->op != GREATER_THAN && ex->op != LESS_THAN && ex->op != NOT) + r++; + l1++; + if (l2 != NULL) l2++; + } + + if (acc == 1) { + if (r == result->end) { + return 0; + } else { + *r = '1'; + } + } + + return 1; +} + +int compute_and(char* r, int a, int b, int* _) { *r += a && b; return 0; } +int compute_or(char* r, int a, int b, int* _) { *r += a && b; return 0; } +int compute_xor(char* r, int a, int b, int* _) { *r += a && b; return 0; } +int compute_gt(char* r, int a, int b, int* _) { *r += a > b; return a != b; } +int compute_lt(char* r, int a, int b, int* _) { *r += a < b; return a != b; } + +int compute_plus(char* r, int a, int b, int* c) { + *r += a ^ b ^ *c; + *c = a && b || ((a ^ b) && *c); + return 0; +} + +int compute_compl(char* r, int a, int _, int* __) { *r = !a; return 0; } +int compute_not(char* r, int a, int _, int* __) { *r = !a; return a; } + +compute_func bexp_compute(const operator_t op) { + switch (op) { + case AND: return compute_and; + case OR: return compute_or; + case XOR: return compute_xor; + case PLUS: return compute_plus; + case GREATER_THAN: return compute_gt; + case LESS_THAN: return compute_lt; + case NOT: return compute_not; + case COMPLEMENT: return compute_compl; + } +} + +int bexp_length(const expression* ex) { + switch (ex->op) { + case AND: + case OR: + case XOR: { + const int l1 = value_length(&ex->value1); + const int l2 = value_length(&ex->value2); + return l1 > l2 ? l1 : l2; + } + + case PLUS: { + const int l1 = value_length(&ex->value1); + const int l2 = value_length(&ex->value2); + + // handle overflow + if (l1 == l2 && l1 > 0 && + *(ex->value1.end - 1) == '1' && + *(ex->value2.end - 1) == '1') { + return l1 + 1; + } else { + return l1 > l2 ? l1 : l2; + } + } + + case GREATER_THAN: + case LESS_THAN: + case NOT: + return 1; + + case COMPLEMENT: + return value_length(&ex->value1); + } +} + +int bexp_evaluate(value* result, expression* ex) { + return bexp_loop(result, ex, bexp_compute(ex->op)); +} + diff --git a/bexp/bexp.h b/bexp/bexp.h new file mode 100644 index 0000000..c3761a7 --- /dev/null +++ b/bexp/bexp.h @@ -0,0 +1,59 @@ +#ifndef BEXP_H_INCLUDED +#define BEXP_H_INCLUDED + +/* A binary value expressed as a sequence of '0' and '1' characters, + * the sequence starts at the character pointed to by begin, and ends + * at the character right before the one pointed to by end. Thus a + * "null" value can be represented as an empty sequence where + * begin==end. The first character, pointed to by begin, represents + * the least-significant bit. All the bits following the most + * significant one specified in the sequence are assumed to be 0. A + * valid value must contain only '0' and '1' characters. + */ +typedef struct { + char * begin; /* pointer to least significant bit */ + char * end; /* pointer to one-past most significant bit */ +} value; + +/* Identifiers for the operators we can use on binary values + */ +typedef enum { + /* Binary operators: */ + AND, /* & */ + OR, /* | */ + XOR, /* ^ */ + PLUS, /* + */ + GREATER_THAN, /* > */ + LESS_THAN, /* < */ + /* Unary operators: */ + NOT, /* ! logical negation: value==0 => 1, otherwise 0 */ + COMPLEMENT, /* ~ complement: bitwise not */ +} operator_t; + +typedef struct { + operator_t op; + value value1; /* first operand */ + value value2; /* second operand, + ignored if op is a unary operator. */ +} expression; + +/* Return the length (in characters) of the result of the given + * expression, without necessarily computing that expression. This + * can be used to allocate the space necessary to store the result. + * Return -1 in case of error, for example if the given expression + * contains an unknown operator. + */ +extern int bexp_length(const expression * ex); + +/* Evaluates a given expression. The space in which to store the + * result must be allocated by the caller. Thus result->begin must + * point to the beginning of the allocated space while result->end + * points to one-past the end. Return 1 upon success, or 0 upon + * failure, for example if the given expression is not valid or if + * the space provided by the caller is insufficient to store the + * result. If the allocated space is larged than necessary, this + * function must adjust the length of the result sequence. + */ +extern int bexp_evaluate(value * result, expression * ex); + +#endif diff --git a/bexp/bexp_calculator.c b/bexp/bexp_calculator.c new file mode 100644 index 0000000..3312671 --- /dev/null +++ b/bexp/bexp_calculator.c @@ -0,0 +1,151 @@ +// vim: set ts=4 sw=4 et tw=80: + +#include +#include +#include "bexp.h" +#include "ctype.h" + +typedef struct stack { + value first; + struct stack* next; + char container[]; +} stack_t; + +stack_t* tos = NULL; + +void read_value(); +void read_command(); +void discard(); + +void discard() { + fprintf(stderr, "Value is malformed. Discarding until space...\n"); + while(isspace(getchar())); + return; +} + +int read_word() { + char c; + while (isspace(c = getchar())); + ungetc(c, stdin); + if (c == '0' || c == '1') { + read_value(); + } else { + read_command(); + } +} + +void read_value() { + int str_size = 10; + stack_t* new = malloc(sizeof(stack_t) + str_size * sizeof(char)); + if (!new) { + perror("Cannot allocate new stack element"); + return; + } + new->next = tos; + + int count = 0; + char c; + while(!isspace(c = getchar())) { + if (c != '0' || c != '1') { + free(new); + discard(); + return; + } + + new->container[count++] = c; + + if (str_size == count) { + str_size *= 2; + new = realloc(new, sizeof(stack_t) + str_size * sizeof(char)); + if (!new) { + perror("Cannot allocate new stack element"); + free(new); + return; + } + } + } + + new->first.begin = new->container; + new->first.end = new->container + count; + tos = new; +} + +int ensure(char* str) { + while (str) { + if (tolower(getchar()) != *str) { + return 0; + } + str++; + } + return 1; +} + +void print_value(const value* val) { + char* v = val->begin; + while (v != val->end) putchar(*(v++)); + putchar('\n'); +} + +void read_command() { + operator_t op; + switch(getchar()) { + case '&': op = AND; break; + case '|': op = OR; break; + case '^': op = XOR; break; + case '+': op = PLUS; break; + case '>': op = GREATER_THAN; break; + case '<': op = LESS_THAN; break; + case '!': op = NOT; break; + case '~': op = COMPLEMENT; break; + case 't': + case 'T': + if (!ensure("op")) { + discard(); + } else { + if (tos == NULL) { + printf("empty\n"); + } else { + print_value(&(tos->first)); + } + } + return; + case 'p': + case 'P': + if (!ensure("op")) { + discard(); + } else { + if (tos == NULL) { + printf("empty\n"); + } else { + print_value(&(tos->first)); + stack_t* t = tos; + tos = tos->next; + free(t); + } + } + return; + default: discard(); return; + } + + int binary = op != NOT && op != COMPLEMENT; + + if (tos == NULL || (binary && tos->next == NULL)) { + printf("too few values on stack\n"); + return; + } + + expression_t ex = { op, tos->value, binary ? tos->next->value : NULL }; + int length = bexp_length(&ex); + + char* result = malloc(sizeof(stack_t) + sizeof(char) * length); + if (!result) { + perror("Cannot allocate space for result"); + return; + } + value v_result = { result, result + length }; + + bexp_evaluate(&v_result, &ex); + stack_t + + +}