P06 lab code added
This commit is contained in:
parent
2f61cf20b1
commit
c26659919d
|
@ -0,0 +1,9 @@
|
||||||
|
SNP_SHARED_MAKEFILE := $(if $(SNP_SHARED_MAKEFILE),$(SNP_SHARED_MAKEFILE),"~/snp/shared.mk")
|
||||||
|
|
||||||
|
TARGET := bin/integer-rechner
|
||||||
|
MODULES := src/calc.c src/scan.c src/stack.c src/eval.c src/error.c
|
||||||
|
SOURCES := src/main.c $(MODULES)
|
||||||
|
TSTSOURCES := tests/tests.c $(MODULES)
|
||||||
|
|
||||||
|
include $(SNP_SHARED_MAKEFILE)
|
||||||
|
|
|
@ -0,0 +1,8 @@
|
||||||
|
/**
|
||||||
|
* @mainpage SNP - P06 Integer Rechner
|
||||||
|
*
|
||||||
|
* @section Purpose
|
||||||
|
*
|
||||||
|
* This is a lab on usage of arrays.
|
||||||
|
*
|
||||||
|
*/
|
|
@ -0,0 +1,168 @@
|
||||||
|
/* ----------------------------------------------------------------------------
|
||||||
|
* -- _____ ______ _____ -
|
||||||
|
* -- |_ _| | ____|/ ____| -
|
||||||
|
* -- | | _ __ | |__ | (___ Institute of Embedded Systems -
|
||||||
|
* -- | | | '_ \| __| \___ \ Zuercher Hochschule Winterthur -
|
||||||
|
* -- _| |_| | | | |____ ____) | (University of Applied Sciences) -
|
||||||
|
* -- |_____|_| |_|______|_____/ 8401 Winterthur, Switzerland -
|
||||||
|
* ----------------------------------------------------------------------------
|
||||||
|
*/
|
||||||
|
/**
|
||||||
|
* @file
|
||||||
|
* @brief Lab implementation
|
||||||
|
*/
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <assert.h>
|
||||||
|
#include "calc.h"
|
||||||
|
#include "eval.h"
|
||||||
|
#include "error.h"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief The details of the otherwise opaque type.
|
||||||
|
*/
|
||||||
|
struct calc {
|
||||||
|
scan_t *scan; ///< DI: the scanner (this instance does not take over ownership)
|
||||||
|
eval_t *eval; ///< DI: the evaluator (this instance does not take over ownership)
|
||||||
|
};
|
||||||
|
|
||||||
|
static void parse_expression(calc_t *instance);
|
||||||
|
|
||||||
|
static eval_op_t unary_tk2op(int tk) {
|
||||||
|
switch(tk) {
|
||||||
|
case '~': return OP_INV;
|
||||||
|
case '+': return OP_NOP;
|
||||||
|
case '-': return OP_CHS;
|
||||||
|
default:
|
||||||
|
error_fatal("not supported unary op");
|
||||||
|
return OP_NOP;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static eval_op_t binary_tk2op(int tk) {
|
||||||
|
switch(tk) {
|
||||||
|
case '|': return OP_BIT_OR;
|
||||||
|
case '^': return OP_BIT_XOR;
|
||||||
|
case '&': return OP_BIT_AND;
|
||||||
|
case TK_LSHIFT: return OP_BIT_LEFT;
|
||||||
|
case TK_RSHIFT: return OP_BIT_RIGHT;
|
||||||
|
case '+': return OP_ADD;
|
||||||
|
case '-': return OP_SUB;
|
||||||
|
case '*': return OP_MUL;
|
||||||
|
case '/': return OP_DIV;
|
||||||
|
case '%': return OP_MOD;
|
||||||
|
default:
|
||||||
|
error_fatal("not supported binary op");
|
||||||
|
return OP_NOP;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static int parse_optional_op(calc_t *instance, const int ops[])
|
||||||
|
{
|
||||||
|
assert(instance);
|
||||||
|
assert(ops);
|
||||||
|
const int *op = ops;
|
||||||
|
while(*op && *op != scan_curr_token(instance->scan)) op++;
|
||||||
|
if (*op) scan_next_token(instance->scan);
|
||||||
|
return *op;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void parse_unary_op(calc_t *instance, void (*parse)(calc_t *), const int ops[])
|
||||||
|
{
|
||||||
|
assert(instance);
|
||||||
|
int op = parse_optional_op(instance, ops);
|
||||||
|
if (op) {
|
||||||
|
parse_unary_op(instance, parse, ops);
|
||||||
|
eval_op(instance->eval, unary_tk2op(op));
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
(*parse)(instance);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void parse_binary_op(calc_t *instance, void (*parse)(calc_t *), const int ops[])
|
||||||
|
{
|
||||||
|
assert(instance);
|
||||||
|
(*parse)(instance);
|
||||||
|
int op = parse_optional_op(instance, ops);
|
||||||
|
while (op) {
|
||||||
|
(*parse)(instance);
|
||||||
|
eval_op(instance->eval, binary_tk2op(op));
|
||||||
|
op = parse_optional_op(instance, ops);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void parse_nested_expression(calc_t *instance)
|
||||||
|
{
|
||||||
|
assert(instance);
|
||||||
|
// printf("parse_nested\n");
|
||||||
|
if ('(' != scan_curr_token(instance->scan)) error_fatal("number or '(' ... ')' expected");
|
||||||
|
if (scan_next_token(instance->scan) == TK_EOT) error_fatal("expression expected");
|
||||||
|
parse_expression(instance);
|
||||||
|
if (scan_curr_token(instance->scan) != ')') error_fatal("closing ')' expected");
|
||||||
|
scan_next_token(instance->scan);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void parse_number(calc_t *instance)
|
||||||
|
{
|
||||||
|
assert(instance);
|
||||||
|
// printf("parse_number\n");
|
||||||
|
if (scan_curr_token(instance->scan) != TK_NUM) error_fatal("number expected");
|
||||||
|
eval_number(instance->eval, scan_curr_value(instance->scan));
|
||||||
|
scan_next_token(instance->scan);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void parse_primary(calc_t *instance)
|
||||||
|
{
|
||||||
|
assert(instance);
|
||||||
|
// printf("parse_primary(%d)\n", scan_curr_token(instance->scan));
|
||||||
|
int t = scan_curr_token(instance->scan);
|
||||||
|
if (t == TK_NUM) parse_number(instance);
|
||||||
|
else if (t == '(') parse_nested_expression(instance);
|
||||||
|
else error_fatal("number or '(' ... ')' expected");
|
||||||
|
}
|
||||||
|
|
||||||
|
static void parse_unary(calc_t *instance) { parse_unary_op(instance, parse_primary, (const int[]){ '+', '-', '~', 0 }); }
|
||||||
|
|
||||||
|
static void parse_mul(calc_t *instance) { parse_binary_op(instance, parse_unary, (const int[]){ '*', '/', '%', 0 }); }
|
||||||
|
|
||||||
|
static void parse_sum(calc_t *instance) { parse_binary_op(instance, parse_mul, (const int[]){ '+', '-', 0 }); }
|
||||||
|
|
||||||
|
static void parse_bit_shift(calc_t *instance) { parse_binary_op(instance, parse_sum, (const int[]){ TK_LSHIFT, TK_RSHIFT, 0 }); }
|
||||||
|
|
||||||
|
static void parse_bit_and(calc_t *instance) { parse_binary_op(instance, parse_bit_shift, (const int[]){ '&', 0 }); }
|
||||||
|
|
||||||
|
static void parse_bit_xor(calc_t *instance) { parse_binary_op(instance, parse_bit_and, (const int[]){ '^', 0 }); }
|
||||||
|
|
||||||
|
static void parse_bit_or(calc_t *instance) { parse_binary_op(instance, parse_bit_xor, (const int[]){ '|', 0 }); }
|
||||||
|
|
||||||
|
static void parse_expression(calc_t *instance) { parse_bit_or(instance); }
|
||||||
|
|
||||||
|
|
||||||
|
calc_t *calc_new(scan_t *scan, eval_t *eval)
|
||||||
|
{
|
||||||
|
assert(scan);
|
||||||
|
assert(eval);
|
||||||
|
calc_t *instance = malloc(sizeof(calc_t));
|
||||||
|
if (!instance) error_fatal_errno("calc_new: instance");
|
||||||
|
instance->scan = scan;
|
||||||
|
instance->eval = eval;
|
||||||
|
return instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
void calc_destroy(calc_t *instance)
|
||||||
|
{
|
||||||
|
assert(instance);
|
||||||
|
free(instance);
|
||||||
|
}
|
||||||
|
|
||||||
|
void calc_execute(calc_t *instance)
|
||||||
|
{
|
||||||
|
assert(instance);
|
||||||
|
if (scan_next_token(instance->scan) == TK_EOT) error_fatal("missing expression text");
|
||||||
|
parse_expression(instance);
|
||||||
|
// printf("excess = %d\n", scan_curr_token(instance->scan));
|
||||||
|
if (scan_curr_token(instance->scan) != TK_EOT) error_fatal("excess expression text found");
|
||||||
|
|
||||||
|
eval_result(instance->eval);
|
||||||
|
}
|
|
@ -0,0 +1,50 @@
|
||||||
|
/* ----------------------------------------------------------------------------
|
||||||
|
* -- _____ ______ _____ -
|
||||||
|
* -- |_ _| | ____|/ ____| -
|
||||||
|
* -- | | _ __ | |__ | (___ Institute of Embedded Systems -
|
||||||
|
* -- | | | '_ \| __| \___ \ Zuercher Hochschule Winterthur -
|
||||||
|
* -- _| |_| | | | |____ ____) | (University of Applied Sciences) -
|
||||||
|
* -- |_____|_| |_|______|_____/ 8401 Winterthur, Switzerland -
|
||||||
|
* ----------------------------------------------------------------------------
|
||||||
|
*/
|
||||||
|
/**
|
||||||
|
* @file
|
||||||
|
* @brief Infix calculator
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _CALC_H_
|
||||||
|
#define _CALC_H_
|
||||||
|
|
||||||
|
#include "scan.h"
|
||||||
|
#include "eval.h"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Opaque type - detailed in the implementation only.
|
||||||
|
*/
|
||||||
|
typedef struct calc calc_t;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Construcor.
|
||||||
|
* @param scan [IN] Dependency Injection: the serving scanner.
|
||||||
|
* @param eval [IN] Dependency Injection: the serving evaluator.
|
||||||
|
* @return The newly created and initialized instance
|
||||||
|
* @remark In case of error, the program terminates with EXIT_FAILURE and an appropriate error message.
|
||||||
|
* @remark Does not take over ownership of the injected dependencies.
|
||||||
|
*/
|
||||||
|
calc_t *calc_new(scan_t *scan, eval_t *eval);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Frees the instances memory and the data it owns.
|
||||||
|
* @param instance [INOUT] The affected instance.
|
||||||
|
* @remark assert(instance) is performed, i.e. it is considered a programming error if instance is NULL.
|
||||||
|
*/
|
||||||
|
void calc_destroy(calc_t *instance);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Entry point for processing the expression from stdin.
|
||||||
|
* @param instance [INOUT] The affected instance.
|
||||||
|
* @remark assert(instance) is performed, i.e. it is considered a programming error if instance is NULL.
|
||||||
|
*/
|
||||||
|
void calc_execute(calc_t *instance);
|
||||||
|
|
||||||
|
#endif // _CALC_H_
|
|
@ -0,0 +1,36 @@
|
||||||
|
/* ----------------------------------------------------------------------------
|
||||||
|
* -- _____ ______ _____ -
|
||||||
|
* -- |_ _| | ____|/ ____| -
|
||||||
|
* -- | | _ __ | |__ | (___ Institute of Embedded Systems -
|
||||||
|
* -- | | | '_ \| __| \___ \ Zuercher Hochschule Winterthur -
|
||||||
|
* -- _| |_| | | | |____ ____) | (University of Applied Sciences) -
|
||||||
|
* -- |_____|_| |_|______|_____/ 8401 Winterthur, Switzerland -
|
||||||
|
* ----------------------------------------------------------------------------
|
||||||
|
*/
|
||||||
|
/**
|
||||||
|
* @file
|
||||||
|
* @brief Errors
|
||||||
|
*/
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include "error.h"
|
||||||
|
|
||||||
|
static size_t context = 0;
|
||||||
|
void error_set_pos(size_t pos)
|
||||||
|
{
|
||||||
|
context = pos;
|
||||||
|
}
|
||||||
|
|
||||||
|
void error_fatal(const char message[])
|
||||||
|
{
|
||||||
|
fprintf(stderr, "ERROR: at or near position %zd: %s\n", context, message);
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
|
||||||
|
void error_fatal_errno(const char message[])
|
||||||
|
{
|
||||||
|
fprintf(stderr, "ERROR: at or near position %zd: ", context);
|
||||||
|
perror(message);
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,36 @@
|
||||||
|
/* ----------------------------------------------------------------------------
|
||||||
|
* -- _____ ______ _____ -
|
||||||
|
* -- |_ _| | ____|/ ____| -
|
||||||
|
* -- | | _ __ | |__ | (___ Institute of Embedded Systems -
|
||||||
|
* -- | | | '_ \| __| \___ \ Zuercher Hochschule Winterthur -
|
||||||
|
* -- _| |_| | | | |____ ____) | (University of Applied Sciences) -
|
||||||
|
* -- |_____|_| |_|______|_____/ 8401 Winterthur, Switzerland -
|
||||||
|
* ----------------------------------------------------------------------------
|
||||||
|
*/
|
||||||
|
/**
|
||||||
|
* @file
|
||||||
|
* @brief Error handling
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _ERROR_H_
|
||||||
|
#define _ERROR_H_
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Sets the context of the current token for potential error reports.
|
||||||
|
* @param pos [IN] The current input start position of the token which is currently processed.
|
||||||
|
*/
|
||||||
|
void error_set_pos(size_t pos);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Issues on stderr an error message and terminates the program with EXIT_FAILURE.
|
||||||
|
* @param message [IN] Context information.
|
||||||
|
*/
|
||||||
|
void error_fatal(const char message[]);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Issues on stderr an error message (includeing the errno text) and terminates the program with EXIT_FAILURE.
|
||||||
|
* @param message [IN] Context information.
|
||||||
|
*/
|
||||||
|
void error_fatal_errno(const char message[]);
|
||||||
|
|
||||||
|
#endif // _ERROR_H_
|
|
@ -0,0 +1,244 @@
|
||||||
|
/* ----------------------------------------------------------------------------
|
||||||
|
* -- _____ ______ _____ -
|
||||||
|
* -- |_ _| | ____|/ ____| -
|
||||||
|
* -- | | _ __ | |__ | (___ Institute of Embedded Systems -
|
||||||
|
* -- | | | '_ \| __| \___ \ Zuercher Hochschule Winterthur -
|
||||||
|
* -- _| |_| | | | |____ ____) | (University of Applied Sciences) -
|
||||||
|
* -- |_____|_| |_|______|_____/ 8401 Winterthur, Switzerland -
|
||||||
|
* ----------------------------------------------------------------------------
|
||||||
|
*/
|
||||||
|
/**
|
||||||
|
* @file
|
||||||
|
* @brief RPN Evaluator
|
||||||
|
*/
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <assert.h>
|
||||||
|
#include "eval.h"
|
||||||
|
#include "error.h"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief The details of the otherwise opaque type.
|
||||||
|
*/
|
||||||
|
struct eval {
|
||||||
|
stack_t *stack; ///< DI: the stack (this instance does not take over ownership)
|
||||||
|
int verbose; ///< verbosity control (0 = silent, 1 = verbose)
|
||||||
|
};
|
||||||
|
|
||||||
|
static void write_op(FILE *fd, eval_op_t op)
|
||||||
|
{
|
||||||
|
switch(op) {
|
||||||
|
case OP_NOP: fprintf(fd, "OP_NOP\n"); break;
|
||||||
|
case OP_PRINT_HEX: fprintf(fd, "OP_PRINT_HEX\n"); break;
|
||||||
|
case OP_PRINT_DEC: fprintf(fd, "OP_PRINT_DEC\n"); break;
|
||||||
|
case OP_PRINT_OCT: fprintf(fd, "OP_PRINT_OCT\n"); break;
|
||||||
|
case OP_PRINT_BIN: fprintf(fd, "OP_PRINT_BIN\n"); break;
|
||||||
|
case OP_BIT_OR: fprintf(fd, "OP_BIT_OR\n"); break;
|
||||||
|
case OP_BIT_XOR: fprintf(fd, "OP_BIT_XOR\n"); break;
|
||||||
|
case OP_BIT_AND: fprintf(fd, "OP_BIT_AND\n"); break;
|
||||||
|
case OP_BIT_LEFT: fprintf(fd, "OP_BIT_LEFT\n"); break;
|
||||||
|
case OP_BIT_RIGHT: fprintf(fd, "OP_BIT_RIGHT\n"); break;
|
||||||
|
case OP_ADD: fprintf(fd, "OP_ADD\n"); break;
|
||||||
|
case OP_SUB: fprintf(fd, "OP_SUB\n"); break;
|
||||||
|
case OP_MUL: fprintf(fd, "OP_MUL\n"); break;
|
||||||
|
case OP_DIV: fprintf(fd, "OP_DIV\n"); break;
|
||||||
|
case OP_MOD: fprintf(fd, "OP_MOD\n"); break;
|
||||||
|
case OP_CHS: fprintf(fd, "OP_CHS\n"); break;
|
||||||
|
case OP_INV: fprintf(fd, "OP_INV\n"); break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void write_value(FILE *fd, eval_op_t op, unsigned int value)
|
||||||
|
{
|
||||||
|
const unsigned int top_bit = 1 << (sizeof(unsigned int) * 8 - 1);
|
||||||
|
switch(op) {
|
||||||
|
case OP_PRINT_HEX:
|
||||||
|
fprintf(fd, "hex=0x%0*x\n", (int)(sizeof(unsigned int)*2), value);
|
||||||
|
break;
|
||||||
|
case OP_PRINT_DEC:
|
||||||
|
fprintf(fd, "dec=%d\n", value);
|
||||||
|
break;
|
||||||
|
case OP_PRINT_OCT:
|
||||||
|
fprintf(fd, "oct=0%0*o\n", (int)((sizeof(unsigned int)*8+2)/3), value);
|
||||||
|
break;
|
||||||
|
case OP_PRINT_BIN:
|
||||||
|
fprintf(fd, "bin=0b");
|
||||||
|
for(size_t i = 0; i < sizeof(unsigned int)*8; i++) {
|
||||||
|
fputc((value & top_bit) ? '1' : '0', fd);
|
||||||
|
value <<= 1;
|
||||||
|
}
|
||||||
|
fputc('\n', fd);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static int eval_top(eval_t *instance, eval_op_t op)
|
||||||
|
{
|
||||||
|
assert(instance);
|
||||||
|
assert(instance->stack);
|
||||||
|
|
||||||
|
switch(op) {
|
||||||
|
case OP_NOP:
|
||||||
|
return 1;
|
||||||
|
case OP_PRINT_HEX:
|
||||||
|
case OP_PRINT_DEC:
|
||||||
|
case OP_PRINT_OCT:
|
||||||
|
case OP_PRINT_BIN:
|
||||||
|
write_value(stdout, op, stack_top(instance->stack));
|
||||||
|
return 1;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int eval_unary(eval_t *instance, eval_op_t op)
|
||||||
|
{
|
||||||
|
assert(instance);
|
||||||
|
assert(instance->stack);
|
||||||
|
|
||||||
|
unsigned int v;
|
||||||
|
switch(op) {
|
||||||
|
case OP_CHS:
|
||||||
|
v = stack_pop(instance->stack);
|
||||||
|
stack_push(instance->stack, -v);
|
||||||
|
return 1;
|
||||||
|
case OP_INV:
|
||||||
|
// 1. implement the ~ operator analogous to the - sign operator above
|
||||||
|
// BEGIN-STUDENTS-TO-ADD-CODE
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// END-STUDENTS-TO-ADD-CODE
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int eval_binary(eval_t *instance, eval_op_t op)
|
||||||
|
{
|
||||||
|
assert(instance);
|
||||||
|
assert(instance->stack);
|
||||||
|
|
||||||
|
unsigned int a;
|
||||||
|
unsigned int b;
|
||||||
|
|
||||||
|
switch(op) {
|
||||||
|
case OP_ADD:
|
||||||
|
b = stack_pop(instance->stack);
|
||||||
|
a = stack_pop(instance->stack);
|
||||||
|
stack_push(instance->stack, a + b);
|
||||||
|
return 1;
|
||||||
|
case OP_SUB:
|
||||||
|
b = stack_pop(instance->stack);
|
||||||
|
a = stack_pop(instance->stack);
|
||||||
|
stack_push(instance->stack, a - b);
|
||||||
|
return 1;
|
||||||
|
case OP_MUL:
|
||||||
|
b = stack_pop(instance->stack);
|
||||||
|
a = stack_pop(instance->stack);
|
||||||
|
stack_push(instance->stack, a * b);
|
||||||
|
return 1;
|
||||||
|
case OP_DIV:
|
||||||
|
b = stack_pop(instance->stack);
|
||||||
|
a = stack_pop(instance->stack);
|
||||||
|
stack_push(instance->stack, a / b);
|
||||||
|
return 1;
|
||||||
|
case OP_MOD:
|
||||||
|
b = stack_pop(instance->stack);
|
||||||
|
a = stack_pop(instance->stack);
|
||||||
|
stack_push(instance->stack, a % b);
|
||||||
|
return 1;
|
||||||
|
case OP_BIT_OR:
|
||||||
|
// 1. implement the | operator analogous to the * operator above
|
||||||
|
// BEGIN-STUDENTS-TO-ADD-CODE
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// END-STUDENTS-TO-ADD-CODE
|
||||||
|
case OP_BIT_XOR:
|
||||||
|
// 1. implement the ^ operator analogous to the * operator above
|
||||||
|
// BEGIN-STUDENTS-TO-ADD-CODE
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// END-STUDENTS-TO-ADD-CODE
|
||||||
|
case OP_BIT_AND:
|
||||||
|
// 1. implement the & operator analogous to the * operator above
|
||||||
|
// BEGIN-STUDENTS-TO-ADD-CODE
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// END-STUDENTS-TO-ADD-CODE
|
||||||
|
case OP_BIT_LEFT:
|
||||||
|
// 1. implement the << operator analogous to the * operator above
|
||||||
|
// BEGIN-STUDENTS-TO-ADD-CODE
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// END-STUDENTS-TO-ADD-CODE
|
||||||
|
case OP_BIT_RIGHT:
|
||||||
|
// 1. implement the >> operator analogous to the * operator above
|
||||||
|
// BEGIN-STUDENTS-TO-ADD-CODE
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// END-STUDENTS-TO-ADD-CODE
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
eval_t *eval_new(stack_t *stack, int verbose)
|
||||||
|
{
|
||||||
|
assert(stack);
|
||||||
|
eval_t *instance = malloc(sizeof(eval_t));
|
||||||
|
if (!instance) error_fatal_errno("eval_new: instance");
|
||||||
|
instance->stack = stack;
|
||||||
|
instance->verbose = verbose;
|
||||||
|
return instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
void eval_destroy(eval_t *instance)
|
||||||
|
{
|
||||||
|
assert(instance);
|
||||||
|
free(instance);
|
||||||
|
}
|
||||||
|
|
||||||
|
void eval_number(eval_t *instance, unsigned int value)
|
||||||
|
{
|
||||||
|
assert(instance);
|
||||||
|
stack_push(instance->stack, value);
|
||||||
|
if (instance->verbose) write_value(stderr, OP_PRINT_HEX, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
void eval_op(eval_t *instance, eval_op_t op)
|
||||||
|
{
|
||||||
|
if (eval_top(instance, op) || eval_unary(instance, op) || eval_binary(instance, op)) {
|
||||||
|
// successfully evaluated
|
||||||
|
if (instance->verbose) write_op(stderr, op);
|
||||||
|
} else {
|
||||||
|
error_fatal("eval_op: unknown op");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void eval_result(eval_t *instance)
|
||||||
|
{
|
||||||
|
assert(instance);
|
||||||
|
unsigned int v = stack_top(instance->stack);
|
||||||
|
printf("--- RESULT ---\n");
|
||||||
|
write_value(stdout, OP_PRINT_DEC, v);
|
||||||
|
write_value(stdout, OP_PRINT_HEX, v);
|
||||||
|
write_value(stdout, OP_PRINT_OCT, v);
|
||||||
|
write_value(stdout, OP_PRINT_BIN, v);
|
||||||
|
}
|
|
@ -0,0 +1,87 @@
|
||||||
|
/* ----------------------------------------------------------------------------
|
||||||
|
* -- _____ ______ _____ -
|
||||||
|
* -- |_ _| | ____|/ ____| -
|
||||||
|
* -- | | _ __ | |__ | (___ Institute of Embedded Systems -
|
||||||
|
* -- | | | '_ \| __| \___ \ Zuercher Hochschule Winterthur -
|
||||||
|
* -- _| |_| | | | |____ ____) | (University of Applied Sciences) -
|
||||||
|
* -- |_____|_| |_|______|_____/ 8401 Winterthur, Switzerland -
|
||||||
|
* ----------------------------------------------------------------------------
|
||||||
|
*/
|
||||||
|
/**
|
||||||
|
* @file
|
||||||
|
* @brief RPN Evaluator
|
||||||
|
*/
|
||||||
|
#ifndef _EVAL_H_
|
||||||
|
#define _EVAL_H_
|
||||||
|
|
||||||
|
#include "stack.h"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief All RPN Operators.
|
||||||
|
*/
|
||||||
|
typedef enum {
|
||||||
|
OP_NOP, ///< no operation
|
||||||
|
OP_PRINT_HEX, ///< print as hex
|
||||||
|
OP_PRINT_DEC, ///< print as dec
|
||||||
|
OP_PRINT_OCT, ///< print as oct
|
||||||
|
OP_PRINT_BIN, ///< print as bin
|
||||||
|
OP_BIT_OR, ///< unsigned integer bit or
|
||||||
|
OP_BIT_XOR, ///< unsigned integer bit xor
|
||||||
|
OP_BIT_AND, ///< unsigned integer bit and
|
||||||
|
OP_BIT_LEFT, ///< unsigned integer shift-left
|
||||||
|
OP_BIT_RIGHT, ///< unsigned integer shift-right
|
||||||
|
OP_ADD, ///< unsigned integer addition
|
||||||
|
OP_SUB, ///< unsigned integer subtraction
|
||||||
|
OP_MUL, ///< unsigned integer multiplication
|
||||||
|
OP_DIV, ///< unsigned integer division
|
||||||
|
OP_MOD, ///< unsigned integer modulo
|
||||||
|
OP_CHS, ///< unsigned integer twos-complement
|
||||||
|
OP_INV, ///< unsigned integer ones-complement
|
||||||
|
} eval_op_t;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Opaque type - detailed in the implementation only.
|
||||||
|
*/
|
||||||
|
typedef struct eval eval_t;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Construcor.
|
||||||
|
* @param stack [IN] Dependency Injection: the serving stack.
|
||||||
|
* @param verbose [IN] Cointrol debug output (stderr): 0 = no output, 1 = additional output.
|
||||||
|
* @return The newly created and initialized instance
|
||||||
|
* @remark In case of error, the program terminates with EXIT_FAILURE and an appropriate error message.
|
||||||
|
* @remark Does not take over ownership of the injected dependencies.
|
||||||
|
*/
|
||||||
|
eval_t *eval_new(stack_t *stack, int verbose);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Frees the instances memory and the data it owns.
|
||||||
|
* @param instance [INOUT] The affected instance.
|
||||||
|
* @remark assert(instance) is performed, i.e. it is considered a programming error if instance is NULL.
|
||||||
|
*/
|
||||||
|
void eval_destroy(eval_t *instance);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Feeds the value in RPN sequence to the evaluator.
|
||||||
|
* @param instance [INOUT] The affected instance.
|
||||||
|
* @param value [IN] The value to stack on the RPN calculator.
|
||||||
|
* @remark assert(instance) is performed, i.e. it is considered a programming error if instance is NULL.
|
||||||
|
*/
|
||||||
|
void eval_number(eval_t *instance, unsigned int value);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Feeds the operation in RPN sequence to the evaluator.
|
||||||
|
* @param instance [INOUT] The affected instance.
|
||||||
|
* @param op [IN] The operator to evaluate.
|
||||||
|
* @remark assert(instance) is performed, i.e. it is considered a programming error if instance is NULL.
|
||||||
|
*/
|
||||||
|
void eval_op(eval_t *instance, eval_op_t op);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Prints the top entry of the stack.
|
||||||
|
* @param instance [INOUT] The affected instance.
|
||||||
|
* @remark assert(instance) is performed, i.e. it is considered a programming error if instance is NULL.
|
||||||
|
*/
|
||||||
|
void eval_result(eval_t *instance);
|
||||||
|
|
||||||
|
#endif // _EVAL_H_
|
|
@ -0,0 +1,42 @@
|
||||||
|
/* ----------------------------------------------------------------------------
|
||||||
|
* -- _____ ______ _____ -
|
||||||
|
* -- |_ _| | ____|/ ____| -
|
||||||
|
* -- | | _ __ | |__ | (___ Institute of Embedded Systems -
|
||||||
|
* -- | | | '_ \| __| \___ \ Zuercher Hochschule Winterthur -
|
||||||
|
* -- _| |_| | | | |____ ____) | (University of Applied Sciences) -
|
||||||
|
* -- |_____|_| |_|______|_____/ 8401 Winterthur, Switzerland -
|
||||||
|
* ----------------------------------------------------------------------------
|
||||||
|
*/
|
||||||
|
/**
|
||||||
|
* @file
|
||||||
|
* @brief Lab implementation
|
||||||
|
*/
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include "calc.h"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Main entry point.
|
||||||
|
* @param[in] argc The size of the argv array.
|
||||||
|
* @param[in] argv The command line arguments...
|
||||||
|
* @returns Returns EXIT_SUCCESS (=0) on success, EXIT_FAILURE (=1) there is an expression syntax error.
|
||||||
|
*/
|
||||||
|
int main(int argc, char* argv[])
|
||||||
|
{
|
||||||
|
int verbose = argc > 1 && strtol(argv[1], NULL, 10);
|
||||||
|
|
||||||
|
const size_t stack_size = 10;
|
||||||
|
stack_t *stack = stack_new(stack_size);
|
||||||
|
eval_t *eval = eval_new(stack, verbose);
|
||||||
|
scan_t *scan = scan_new();
|
||||||
|
calc_t *calc = calc_new(scan, eval);
|
||||||
|
|
||||||
|
calc_execute(calc);
|
||||||
|
|
||||||
|
calc_destroy(calc);
|
||||||
|
scan_destroy(scan);
|
||||||
|
eval_destroy(eval);
|
||||||
|
stack_destroy(stack);
|
||||||
|
|
||||||
|
return EXIT_SUCCESS;
|
||||||
|
}
|
|
@ -0,0 +1,204 @@
|
||||||
|
/* ----------------------------------------------------------------------------
|
||||||
|
* -- _____ ______ _____ -
|
||||||
|
* -- |_ _| | ____|/ ____| -
|
||||||
|
* -- | | _ __ | |__ | (___ Institute of Embedded Systems -
|
||||||
|
* -- | | | '_ \| __| \___ \ Zuercher Hochschule Winterthur -
|
||||||
|
* -- _| |_| | | | |____ ____) | (University of Applied Sciences) -
|
||||||
|
* -- |_____|_| |_|______|_____/ 8401 Winterthur, Switzerland -
|
||||||
|
* ----------------------------------------------------------------------------
|
||||||
|
*/
|
||||||
|
/**
|
||||||
|
* @file
|
||||||
|
* @brief Infix calculator tokenizer
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <ctype.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <assert.h>
|
||||||
|
#include "scan.h"
|
||||||
|
#include "error.h"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief The details of the otherwise opaque type.
|
||||||
|
*/
|
||||||
|
struct scan {
|
||||||
|
size_t count; ///< counts the read characters (used for error reporting)
|
||||||
|
int la; ///< look-ahead-1 character
|
||||||
|
int token; ///< the currently recognized token
|
||||||
|
unsigned int value; ///< the current TK_NUM value
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
static int next_char(scan_t *instance)
|
||||||
|
{
|
||||||
|
assert(instance);
|
||||||
|
instance->la = getc(stdin);
|
||||||
|
if (instance->la != EOF) instance->count++;
|
||||||
|
return instance->la;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int curr_char(scan_t *instance)
|
||||||
|
{
|
||||||
|
assert(instance);
|
||||||
|
return instance->la;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int skip_chars(scan_t *instance)
|
||||||
|
{
|
||||||
|
assert(instance);
|
||||||
|
while(instance->la != EOF && isspace(instance->la)) next_char(instance);
|
||||||
|
return instance->la;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int eof(scan_t *instance)
|
||||||
|
{
|
||||||
|
assert(instance);
|
||||||
|
// printf("eof?\n");
|
||||||
|
if (curr_char(instance) == EOF) {
|
||||||
|
instance->token = TK_EOT;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int op1(scan_t *instance, const char ops[])
|
||||||
|
{
|
||||||
|
assert(instance);
|
||||||
|
// printf("op1?\n");
|
||||||
|
assert(ops);
|
||||||
|
if (strchr(ops, curr_char(instance))) {
|
||||||
|
instance->token = curr_char(instance);
|
||||||
|
next_char(instance);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int op2(scan_t *instance, const char *op, int t)
|
||||||
|
{
|
||||||
|
assert(instance);
|
||||||
|
// printf("op2?\n");
|
||||||
|
assert(op);
|
||||||
|
assert(strlen(op) == 2);
|
||||||
|
if (curr_char(instance) == op[0] && next_char(instance) == op[1]) {
|
||||||
|
instance->token = t;
|
||||||
|
next_char(instance);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int number(scan_t *instance)
|
||||||
|
{
|
||||||
|
assert(instance);
|
||||||
|
// printf("number\n");
|
||||||
|
int c = curr_char(instance);
|
||||||
|
if (c == '0') {
|
||||||
|
instance->value = 0;
|
||||||
|
instance->token = TK_NUM;
|
||||||
|
c = next_char(instance);
|
||||||
|
if (strchr("xX", c)) {
|
||||||
|
instance->token = TK_ERROR;
|
||||||
|
c = next_char(instance);
|
||||||
|
while(isxdigit(c) || c == '_') {
|
||||||
|
if (c != '_') {
|
||||||
|
instance->value *= 16;
|
||||||
|
instance->token = TK_NUM;
|
||||||
|
if (isdigit(c)) {
|
||||||
|
instance->value += c - '0';
|
||||||
|
} else {
|
||||||
|
instance->value += 10 + c - (isupper(c) ? 'A' : 'a');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
c = next_char(instance);
|
||||||
|
}
|
||||||
|
} else if (strchr("bB", c)) {
|
||||||
|
instance->token = TK_ERROR;
|
||||||
|
c = next_char(instance);
|
||||||
|
while(strchr("01_", c)) {
|
||||||
|
if (c != '_') {
|
||||||
|
instance->value *= 2;
|
||||||
|
instance->value += c - '0';
|
||||||
|
instance->token = TK_NUM;
|
||||||
|
}
|
||||||
|
c = next_char(instance);
|
||||||
|
}
|
||||||
|
} else if (strchr("01234567_", c)) {
|
||||||
|
while(strchr("01234567_", c)) {
|
||||||
|
if (c != '_') {
|
||||||
|
instance->value *= 8;
|
||||||
|
instance->value += c - '0';
|
||||||
|
}
|
||||||
|
c = next_char(instance);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 1;
|
||||||
|
} else if (isdigit(c)) {
|
||||||
|
instance->value = 0;
|
||||||
|
while(isdigit(c) || c == '_') {
|
||||||
|
if (c != '_') {
|
||||||
|
instance->value *= 10;
|
||||||
|
instance->value += c - '0';
|
||||||
|
c = next_char(instance);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
instance->token = TK_NUM;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
scan_t *scan_new()
|
||||||
|
{
|
||||||
|
scan_t *instance = malloc(sizeof(scan_t));
|
||||||
|
if (!instance) error_fatal_errno("scan_new: instance");
|
||||||
|
instance->count = 0;
|
||||||
|
instance->la = EOF;
|
||||||
|
instance->token = TK_EOT;
|
||||||
|
instance->value = 0;
|
||||||
|
return instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
void scan_destroy(scan_t *instance)
|
||||||
|
{
|
||||||
|
assert(instance);
|
||||||
|
free(instance);
|
||||||
|
}
|
||||||
|
|
||||||
|
int scan_next_token(scan_t *instance)
|
||||||
|
{
|
||||||
|
assert(instance);
|
||||||
|
// printf("next...\n");
|
||||||
|
int c = curr_char(instance);
|
||||||
|
if (c == EOF) c = next_char(instance); // initial (or final) situation
|
||||||
|
c = skip_chars(instance);
|
||||||
|
error_set_pos(instance->count);
|
||||||
|
|
||||||
|
instance->token = TK_ERROR;
|
||||||
|
instance->value = 0;
|
||||||
|
|
||||||
|
if (eof(instance) || op1(instance, "+-*%/|^&~()") || op2(instance, "<<", TK_LSHIFT) || op2(instance, ">>", TK_RSHIFT) || number(instance)) {
|
||||||
|
// one of the above was found
|
||||||
|
skip_chars(instance);
|
||||||
|
}
|
||||||
|
|
||||||
|
// printf("next = %d\n", token);
|
||||||
|
return instance->token;
|
||||||
|
}
|
||||||
|
|
||||||
|
int scan_curr_token(scan_t *instance)
|
||||||
|
{
|
||||||
|
assert(instance);
|
||||||
|
return instance->token;
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned int scan_curr_value(scan_t *instance)
|
||||||
|
{
|
||||||
|
assert(instance);
|
||||||
|
if (scan_curr_token(instance) != TK_NUM) error_fatal_errno("scan_curr_value: wrong token");
|
||||||
|
return instance->value;
|
||||||
|
}
|
|
@ -0,0 +1,75 @@
|
||||||
|
/* ----------------------------------------------------------------------------
|
||||||
|
* -- _____ ______ _____ -
|
||||||
|
* -- |_ _| | ____|/ ____| -
|
||||||
|
* -- | | _ __ | |__ | (___ Institute of Embedded Systems -
|
||||||
|
* -- | | | '_ \| __| \___ \ Zuercher Hochschule Winterthur -
|
||||||
|
* -- _| |_| | | | |____ ____) | (University of Applied Sciences) -
|
||||||
|
* -- |_____|_| |_|______|_____/ 8401 Winterthur, Switzerland -
|
||||||
|
* ----------------------------------------------------------------------------
|
||||||
|
*/
|
||||||
|
/**
|
||||||
|
* @file
|
||||||
|
* @brief Infix calculator tokenizer
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _SCAN_H_
|
||||||
|
#define _SCAN_H_
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Opaque type - detailed in the implementation only.
|
||||||
|
*/
|
||||||
|
typedef struct scan scan_t;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Specific token enum values beside the ones provided as plain characters.
|
||||||
|
* @remark The last enum value must be below printable ascii code
|
||||||
|
*/
|
||||||
|
typedef enum {
|
||||||
|
TK_ERROR = 0, ///< error or end mark for arrays of tokens (must be 0)
|
||||||
|
TK_EOT, ///< end of text, i.e. when EOF is recognized
|
||||||
|
TK_LSHIFT, ///< left-shift operator (2-char token)
|
||||||
|
TK_RSHIFT, ///< right-shift operator (2-char token)
|
||||||
|
TK_NUM, ///< number token (e.g. 3, 0x1F, etc.)
|
||||||
|
} scan_tk_t;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Construcor.
|
||||||
|
* @return The newly created and initialized instance
|
||||||
|
* @remark In case of error, the program terminates with EXIT_FAILURE and an appropriate error message.
|
||||||
|
*/
|
||||||
|
scan_t *scan_new();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Frees the instances memory and the data it owns.
|
||||||
|
* @param instance [INOUT] The affected instance.
|
||||||
|
* @remark assert(instance) is performed, i.e. it is considered a programming error if instance is NULL.
|
||||||
|
*/
|
||||||
|
void scan_destroy(scan_t *instance);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Moves to the next token.
|
||||||
|
* @param instance [INOUT] The affected instance.
|
||||||
|
* @return Returns the next recognized token (single character token or scan_tk_t enum value).
|
||||||
|
* @remark assert(instance) is performed, i.e. it is considered a programming error if instance is NULL.
|
||||||
|
* @remark If the token is a TK_NUM, the scan_curr_value() function returns the associated value.
|
||||||
|
*/
|
||||||
|
int scan_next_token(scan_t *instance);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Gets the token which mas found with the last scan_next_token() call.
|
||||||
|
* @param instance [INOUT] The affected instance.
|
||||||
|
* @return Returns the currently recognized token (single character token or scan_tk_t enum value).
|
||||||
|
* @remark assert(instance) is performed, i.e. it is considered a programming error if instance is NULL.
|
||||||
|
*/
|
||||||
|
int scan_curr_token(scan_t *instance);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Gets the value associated with the TK_NUM token.
|
||||||
|
* @param instance [INOUT] The affected instance.
|
||||||
|
* @return Returns the value associated with the current TK_NUM.
|
||||||
|
* @remark assert(instance) is performed, i.e. it is considered a programming error if instance is NULL.
|
||||||
|
* @remark This only gives valid values if the scan_curr_token() equals TK_NUM.
|
||||||
|
*/
|
||||||
|
unsigned int scan_curr_value(scan_t *instance);
|
||||||
|
|
||||||
|
#endif // _SCAN_H_
|
|
@ -0,0 +1,132 @@
|
||||||
|
/* ----------------------------------------------------------------------------
|
||||||
|
* -- _____ ______ _____ -
|
||||||
|
* -- |_ _| | ____|/ ____| -
|
||||||
|
* -- | | _ __ | |__ | (___ Institute of Embedded Systems -
|
||||||
|
* -- | | | '_ \| __| \___ \ Zuercher Hochschule Winterthur -
|
||||||
|
* -- _| |_| | | | |____ ____) | (University of Applied Sciences) -
|
||||||
|
* -- |_____|_| |_|______|_____/ 8401 Winterthur, Switzerland -
|
||||||
|
* ----------------------------------------------------------------------------
|
||||||
|
*/
|
||||||
|
/**
|
||||||
|
* @file
|
||||||
|
* @brief Stack
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <assert.h>
|
||||||
|
#include "stack.h"
|
||||||
|
#include "error.h"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief The details of the otherwise opaque type.
|
||||||
|
*/
|
||||||
|
struct stack {
|
||||||
|
stack_value_t *stack; ///< the data container (the instance owns this)
|
||||||
|
stack_value_t *top; ///< the pointer into the container where the current top data resides (has to point initially to stack-1)
|
||||||
|
stack_value_t *full; ///< the pointer to the last entry of the conatainer
|
||||||
|
};
|
||||||
|
|
||||||
|
stack_t *stack_new(size_t max_elements)
|
||||||
|
{
|
||||||
|
stack_t *instance = NULL;
|
||||||
|
// 1. allocate a stack_t instance on the heap and set the instance variable to it
|
||||||
|
// 2. call error_fatal_errno("stack_new: instance"); if failed to allocate the memory on the heap
|
||||||
|
// 3. allocate an array of max_elements value_t's on the heap and store its address in the stack member of the stack_t instance
|
||||||
|
// 4. call error_fatal_errno("stack_new: stack"); if failed to allocate the memory on the heap
|
||||||
|
// 5. set the top member of the stack_t instance to the address of the "element" before the first stack array element
|
||||||
|
// 6. set the full member of the stack_t instance to the address of the last element of the stack array
|
||||||
|
// BEGIN-STUDENTS-TO-ADD-CODE
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// END-STUDENTS-TO-ADD-CODE
|
||||||
|
return instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
void stack_destroy(stack_t *instance)
|
||||||
|
{
|
||||||
|
assert(instance);
|
||||||
|
// 1. free the stack array
|
||||||
|
// 2. free the stack_t instance
|
||||||
|
// BEGIN-STUDENTS-TO-ADD-CODE
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// END-STUDENTS-TO-ADD-CODE
|
||||||
|
}
|
||||||
|
|
||||||
|
void stack_push(stack_t *instance, stack_value_t value)
|
||||||
|
{
|
||||||
|
assert(instance);
|
||||||
|
if (stack_is_full(instance)) error_fatal("stack_push: full");
|
||||||
|
// 1. increment by one element the address stored in the top member of the stack_t instance
|
||||||
|
// 2. store the received value at the new top location
|
||||||
|
// BEGIN-STUDENTS-TO-ADD-CODE
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// END-STUDENTS-TO-ADD-CODE
|
||||||
|
}
|
||||||
|
|
||||||
|
stack_value_t stack_pop(stack_t *instance)
|
||||||
|
{
|
||||||
|
assert(instance);
|
||||||
|
if (stack_is_empty(instance)) error_fatal("stack_pop: empty");
|
||||||
|
stack_value_t value = 0;
|
||||||
|
// 1. set the variable value to the value from the address the top member points to
|
||||||
|
// 2. decrement by one element the address stored in the top member of the stack_t instance
|
||||||
|
// BEGIN-STUDENTS-TO-ADD-CODE
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// END-STUDENTS-TO-ADD-CODE
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
stack_value_t stack_top(stack_t *instance)
|
||||||
|
{
|
||||||
|
assert(instance);
|
||||||
|
if (stack_is_empty(instance)) error_fatal("stack_top: empty");
|
||||||
|
stack_value_t value = 0;
|
||||||
|
// 1. set the variable value to the value from the address the top member points to
|
||||||
|
// BEGIN-STUDENTS-TO-ADD-CODE
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// END-STUDENTS-TO-ADD-CODE
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
int stack_is_empty(stack_t *instance)
|
||||||
|
{
|
||||||
|
assert(instance);
|
||||||
|
int is_empty = 1;
|
||||||
|
// 1. set is_empty to 1 if the top equals the initial condition as done in stack_new(), otherwise to 0
|
||||||
|
// BEGIN-STUDENTS-TO-ADD-CODE
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// END-STUDENTS-TO-ADD-CODE
|
||||||
|
return is_empty;
|
||||||
|
}
|
||||||
|
|
||||||
|
int stack_is_full(stack_t *instance)
|
||||||
|
{
|
||||||
|
assert(instance);
|
||||||
|
int is_full = 1;
|
||||||
|
// 1. set is_full to 1 if the top equals the full pointer as set in the stack_new() function, otherwise 0
|
||||||
|
// BEGIN-STUDENTS-TO-ADD-CODE
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// END-STUDENTS-TO-ADD-CODE
|
||||||
|
return is_full;
|
||||||
|
}
|
|
@ -0,0 +1,86 @@
|
||||||
|
/* ----------------------------------------------------------------------------
|
||||||
|
* -- _____ ______ _____ -
|
||||||
|
* -- |_ _| | ____|/ ____| -
|
||||||
|
* -- | | _ __ | |__ | (___ Institute of Embedded Systems -
|
||||||
|
* -- | | | '_ \| __| \___ \ Zuercher Hochschule Winterthur -
|
||||||
|
* -- _| |_| | | | |____ ____) | (University of Applied Sciences) -
|
||||||
|
* -- |_____|_| |_|______|_____/ 8401 Winterthur, Switzerland -
|
||||||
|
* ----------------------------------------------------------------------------
|
||||||
|
*/
|
||||||
|
/**
|
||||||
|
* @file
|
||||||
|
* @brief Stack
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _STACK_H_
|
||||||
|
#define _STACK_H_
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Convenience alias.
|
||||||
|
*/
|
||||||
|
typedef unsigned int stack_value_t;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Opaque type - detailed in the implementation only.
|
||||||
|
*/
|
||||||
|
typedef struct stack stack_t;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Construcor.
|
||||||
|
* @param max_elements [IN] Gives the size of the stack.
|
||||||
|
* @return The newly created and initialized instance
|
||||||
|
* @remark In case of error, the program terminates with EXIT_FAILURE and an appropriate error message.
|
||||||
|
*/
|
||||||
|
stack_t *stack_new(size_t max_elements);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Frees the instances memory and the data it owns (i.e. the stack container).
|
||||||
|
* @param instance [INOUT] The affected instance.
|
||||||
|
* @remark assert(instance) is performed, i.e. it is considered a programming error if instance is NULL.
|
||||||
|
*/
|
||||||
|
void stack_destroy(stack_t *instance);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Pushes the value to the stack.
|
||||||
|
* @param instance [INOUT] The affected instance.
|
||||||
|
* @param value [IN] The value to pus.
|
||||||
|
* @remark In case of error, the program terminates with EXIT_FAILURE and an appropriate error message.
|
||||||
|
* @remark assert(instance) is performed, i.e. it is considered a programming error if instance is NULL.
|
||||||
|
*/
|
||||||
|
void stack_push(stack_t *instance, stack_value_t value);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Removes the top stack entry and returns it.
|
||||||
|
* @param instance [INOUT] The affected instance.
|
||||||
|
* @return Returns the top most entry.
|
||||||
|
* @remark In case of error, the program terminates with EXIT_FAILURE and an appropriate error message.
|
||||||
|
* @remark assert(instance) is performed, i.e. it is considered a programming error if instance is NULL.
|
||||||
|
*/
|
||||||
|
stack_value_t stack_pop(stack_t *instance);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Gets the top stack entry.
|
||||||
|
* @param instance [INOUT] The affected instance.
|
||||||
|
* @return Returns the top most entry.
|
||||||
|
* @remark In case of error, the program terminates with EXIT_FAILURE and an appropriate error message.
|
||||||
|
* @remark assert(instance) is performed, i.e. it is considered a programming error if instance is NULL.
|
||||||
|
*/
|
||||||
|
stack_value_t stack_top(stack_t *instance);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Queries if the stack is empty - i.e. if a pop operation is possible.
|
||||||
|
* @param instance [INOUT] The affected instance.
|
||||||
|
* @return Returns 1 if empry, 0 otherwise.
|
||||||
|
* @remark assert(instance) is performed, i.e. it is considered a programming error if instance is NULL.
|
||||||
|
*/
|
||||||
|
int stack_is_empty(stack_t *instance);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Queries if the stack is full - i.e. if a push operation is possible.
|
||||||
|
* @param instance [INOUT] The affected instance.
|
||||||
|
* @return Returns 1 if full, 0 otherwise.
|
||||||
|
* @remark assert(instance) is performed, i.e. it is considered a programming error if instance is NULL.
|
||||||
|
*/
|
||||||
|
int stack_is_full(stack_t *instance);
|
||||||
|
|
||||||
|
#endif // _STACK_H_
|
|
@ -0,0 +1,263 @@
|
||||||
|
/* ----------------------------------------------------------------------------
|
||||||
|
* -- _____ ______ _____ -
|
||||||
|
* -- |_ _| | ____|/ ____| -
|
||||||
|
* -- | | _ __ | |__ | (___ Institute of Embedded Systems -
|
||||||
|
* -- | | | '_ \| __| \___ \ Zuercher Hochschule Winterthur -
|
||||||
|
* -- _| |_| | | | |____ ____) | (University of Applied Sciences) -
|
||||||
|
* -- |_____|_| |_|______|_____/ 8401 Winterthur, Switzerland -
|
||||||
|
* ----------------------------------------------------------------------------
|
||||||
|
*/
|
||||||
|
/**
|
||||||
|
* @file
|
||||||
|
* @brief Test suite for the given package.
|
||||||
|
*/
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <sys/wait.h>
|
||||||
|
#include <time.h>
|
||||||
|
#include <assert.h>
|
||||||
|
#include <CUnit/Basic.h>
|
||||||
|
#include "test_utils.h"
|
||||||
|
#include "stack.h"
|
||||||
|
|
||||||
|
#ifndef TARGET // must be given by the make file --> see test target
|
||||||
|
#error missing TARGET define
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/// @brief alias for EXIT_SUCCESS
|
||||||
|
#define OK EXIT_SUCCESS
|
||||||
|
/// @brief alias for EXIT_FAILURE
|
||||||
|
#define FAIL EXIT_FAILURE
|
||||||
|
|
||||||
|
/// @brief The name of the STDOUT text file.
|
||||||
|
#define OUTFILE "stdout.txt"
|
||||||
|
/// @brief The name of the STDERR text file.
|
||||||
|
#define ERRFILE "stderr.txt"
|
||||||
|
|
||||||
|
#define TRACE_INDENT "\n " ///< allow for better stdout formatting in case of error
|
||||||
|
|
||||||
|
// setup & cleanup
|
||||||
|
static int setup(void)
|
||||||
|
{
|
||||||
|
remove_file_if_exists(OUTFILE);
|
||||||
|
remove_file_if_exists(ERRFILE);
|
||||||
|
return 0; // success
|
||||||
|
}
|
||||||
|
|
||||||
|
static int teardown(void)
|
||||||
|
{
|
||||||
|
// Do nothing.
|
||||||
|
// Especially: do not remove result files - they are removed in int setup(void) *before* running a test.
|
||||||
|
return 0; // success
|
||||||
|
}
|
||||||
|
|
||||||
|
// tests
|
||||||
|
static void test_stack_new(void)
|
||||||
|
{
|
||||||
|
// arrange
|
||||||
|
const size_t size = 2;
|
||||||
|
// act
|
||||||
|
stack_t *instance = stack_new(size);
|
||||||
|
// assert
|
||||||
|
CU_ASSERT_PTR_NOT_NULL_FATAL(instance);
|
||||||
|
CU_ASSERT_EQUAL(stack_is_empty(instance), 1);
|
||||||
|
// cleanup
|
||||||
|
stack_destroy(instance);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void test_stack_push(void)
|
||||||
|
{
|
||||||
|
// arrange
|
||||||
|
const size_t size = 2;
|
||||||
|
stack_t *instance = stack_new(size);
|
||||||
|
CU_ASSERT_PTR_NOT_NULL_FATAL(instance);
|
||||||
|
// act & assert
|
||||||
|
for(size_t i = 0; i < size; i++) {
|
||||||
|
stack_push(instance, i);
|
||||||
|
CU_ASSERT_EQUAL(stack_top(instance), i);
|
||||||
|
}
|
||||||
|
// cleanup
|
||||||
|
stack_destroy(instance);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void test_stack_top(void)
|
||||||
|
{
|
||||||
|
// arrange
|
||||||
|
const size_t size = 2;
|
||||||
|
stack_t *instance = stack_new(size);
|
||||||
|
CU_ASSERT_PTR_NOT_NULL_FATAL(instance);
|
||||||
|
// act & assert
|
||||||
|
CU_ASSERT_EQUAL(stack_is_empty(instance), 1);
|
||||||
|
for(size_t i = 0; i < size; i++) {
|
||||||
|
CU_ASSERT_EQUAL(stack_is_full(instance), 0);
|
||||||
|
stack_push(instance, i);
|
||||||
|
CU_ASSERT_EQUAL(stack_is_empty(instance), 0);
|
||||||
|
CU_ASSERT_EQUAL(stack_top(instance), i);
|
||||||
|
}
|
||||||
|
CU_ASSERT_EQUAL(stack_is_full(instance), 1);
|
||||||
|
// cleanup
|
||||||
|
stack_destroy(instance);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void test_stack_pop(void)
|
||||||
|
{
|
||||||
|
// arrange
|
||||||
|
const size_t size = 2;
|
||||||
|
stack_t *instance = stack_new(size);
|
||||||
|
CU_ASSERT_PTR_NOT_NULL_FATAL(instance);
|
||||||
|
for(size_t i = 0; i < size; i++) {
|
||||||
|
stack_push(instance, i);
|
||||||
|
}
|
||||||
|
// act & assert
|
||||||
|
for(size_t i = size; i > 0; i--) {
|
||||||
|
CU_ASSERT_EQUAL(stack_is_empty(instance), 0);
|
||||||
|
CU_ASSERT_EQUAL(stack_pop(instance), i-1);
|
||||||
|
CU_ASSERT_EQUAL(stack_is_full(instance), 0);
|
||||||
|
}
|
||||||
|
// cleanup
|
||||||
|
stack_destroy(instance);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void test_stack_is_empty(void)
|
||||||
|
{
|
||||||
|
// arrange
|
||||||
|
const size_t size = 2;
|
||||||
|
stack_t *instance = stack_new(size);
|
||||||
|
CU_ASSERT_PTR_NOT_NULL_FATAL(instance);
|
||||||
|
// act & assert
|
||||||
|
CU_ASSERT_EQUAL(stack_is_empty(instance), 1);
|
||||||
|
for(size_t i = 0; i < size; i++) {
|
||||||
|
stack_push(instance, i);
|
||||||
|
CU_ASSERT_EQUAL(stack_is_empty(instance), 0);
|
||||||
|
}
|
||||||
|
for(size_t i = size; i > 0; i--) {
|
||||||
|
CU_ASSERT_EQUAL(stack_is_empty(instance), 0);
|
||||||
|
stack_pop(instance);
|
||||||
|
}
|
||||||
|
CU_ASSERT_EQUAL(stack_is_empty(instance), 1);
|
||||||
|
// cleanup
|
||||||
|
stack_destroy(instance);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void test_stack_is_full(void)
|
||||||
|
{
|
||||||
|
// arrange
|
||||||
|
const size_t size = 2;
|
||||||
|
stack_t *instance = stack_new(size);
|
||||||
|
CU_ASSERT_PTR_NOT_NULL_FATAL(instance);
|
||||||
|
// act & assert
|
||||||
|
CU_ASSERT_EQUAL(stack_is_full(instance), 0);
|
||||||
|
for(size_t i = 0; i < size; i++) {
|
||||||
|
CU_ASSERT_EQUAL(stack_is_full(instance), 0);
|
||||||
|
stack_push(instance, i);
|
||||||
|
}
|
||||||
|
CU_ASSERT_EQUAL(stack_is_full(instance), 1);
|
||||||
|
for(size_t i = size; i > 0; i--) {
|
||||||
|
stack_pop(instance);
|
||||||
|
CU_ASSERT_EQUAL(stack_is_full(instance), 0);
|
||||||
|
}
|
||||||
|
CU_ASSERT_EQUAL(stack_is_full(instance), 0);
|
||||||
|
// cleanup
|
||||||
|
stack_destroy(instance);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void test_silent(void)
|
||||||
|
{
|
||||||
|
const char *out_txt[] = {
|
||||||
|
"--- RESULT ---\n",
|
||||||
|
"dec=3\n",
|
||||||
|
"hex=0x00000003\n",
|
||||||
|
"oct=000000000003\n",
|
||||||
|
"bin=0b00000000000000000000000000000011\n",
|
||||||
|
};
|
||||||
|
const char *err_txt[] = {
|
||||||
|
NULL,
|
||||||
|
};
|
||||||
|
|
||||||
|
// arrange & act & assert
|
||||||
|
CU_ASSERT_EQUAL_FATAL(WEXITSTATUS(system("echo '1 + 2' | " XSTR(TARGET) " 0 >" OUTFILE " 2>" ERRFILE)), OK);
|
||||||
|
assert_lines(OUTFILE, out_txt, sizeof(out_txt)/sizeof(*out_txt));
|
||||||
|
assert_lines(ERRFILE, err_txt, sizeof(err_txt)/sizeof(*err_txt));
|
||||||
|
// arrange & act & assert
|
||||||
|
CU_ASSERT_EQUAL_FATAL(WEXITSTATUS(system("echo '1 + 2' | " XSTR(TARGET) " >" OUTFILE " 2>" ERRFILE)), OK);
|
||||||
|
assert_lines(OUTFILE, out_txt, sizeof(out_txt)/sizeof(*out_txt));
|
||||||
|
assert_lines(ERRFILE, err_txt, sizeof(err_txt)/sizeof(*err_txt));
|
||||||
|
}
|
||||||
|
|
||||||
|
static void test_verbose(void)
|
||||||
|
{
|
||||||
|
const char *out_txt[] = {
|
||||||
|
"--- RESULT ---\n",
|
||||||
|
"dec=3\n",
|
||||||
|
"hex=0x00000003\n",
|
||||||
|
"oct=000000000003\n",
|
||||||
|
"bin=0b00000000000000000000000000000011\n",
|
||||||
|
};
|
||||||
|
const char *err_txt[] = {
|
||||||
|
"hex=0x00000001\n",
|
||||||
|
"hex=0x00000002\n",
|
||||||
|
"OP_ADD\n",
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
// arrange & act & assert
|
||||||
|
CU_ASSERT_EQUAL_FATAL(WEXITSTATUS(system("echo '1 + 2' | " XSTR(TARGET) " 1 >" OUTFILE " 2>" ERRFILE)), OK);
|
||||||
|
assert_lines(OUTFILE, out_txt, sizeof(out_txt)/sizeof(*out_txt));
|
||||||
|
assert_lines(ERRFILE, err_txt, sizeof(err_txt)/sizeof(*err_txt));
|
||||||
|
}
|
||||||
|
|
||||||
|
static void test_all(void)
|
||||||
|
{
|
||||||
|
const char *out_txt[] = {
|
||||||
|
"--- RESULT ---\n",
|
||||||
|
"dec=8\n",
|
||||||
|
"hex=0x00000008\n",
|
||||||
|
"oct=000000000010\n",
|
||||||
|
"bin=0b00000000000000000000000000001000\n",
|
||||||
|
};
|
||||||
|
const char *err_txt[] = {
|
||||||
|
NULL,
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
// arrange & act & assert
|
||||||
|
CU_ASSERT_EQUAL_FATAL(WEXITSTATUS(system("echo '(8 | 7 ^ 5) & 4 << 3 + 2 * (~1+(1>>0))' | " XSTR(TARGET) " >" OUTFILE " 2>" ERRFILE)), OK);
|
||||||
|
assert_lines(OUTFILE, out_txt, sizeof(out_txt)/sizeof(*out_txt));
|
||||||
|
assert_lines(ERRFILE, err_txt, sizeof(err_txt)/sizeof(*err_txt));
|
||||||
|
}
|
||||||
|
|
||||||
|
static void test_error(void)
|
||||||
|
{
|
||||||
|
const char *out_txt[] = {
|
||||||
|
NULL,
|
||||||
|
};
|
||||||
|
const char *err_txt[] = {
|
||||||
|
"ERROR: at or near position 5: number or '(' ... ')' expected\n",
|
||||||
|
};
|
||||||
|
|
||||||
|
// arrange & act & assert
|
||||||
|
CU_ASSERT_EQUAL_FATAL(WEXITSTATUS(system("echo '1 + ' | " XSTR(TARGET) " >" OUTFILE " 2>" ERRFILE)), FAIL);
|
||||||
|
assert_lines(OUTFILE, out_txt, sizeof(out_txt)/sizeof(*out_txt));
|
||||||
|
assert_lines(ERRFILE, err_txt, sizeof(err_txt)/sizeof(*err_txt));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Registers and runs the tests.
|
||||||
|
* @returns success (0) or one of the CU_ErrorCode (>0)
|
||||||
|
*/
|
||||||
|
int main(void)
|
||||||
|
{
|
||||||
|
// setup, run, teardown
|
||||||
|
TestMainBasic("lab test", setup, teardown
|
||||||
|
, test_stack_new
|
||||||
|
, test_stack_push
|
||||||
|
, test_stack_top
|
||||||
|
, test_stack_pop
|
||||||
|
, test_stack_is_empty
|
||||||
|
, test_stack_is_full
|
||||||
|
, test_silent
|
||||||
|
, test_verbose
|
||||||
|
, test_all
|
||||||
|
, test_error
|
||||||
|
);
|
||||||
|
}
|
Loading…
Reference in New Issue