P06 lab code added

This commit is contained in:
Andreas Gieriet 2020-03-29 23:46:39 +02:00
parent 2f61cf20b1
commit c26659919d
14 changed files with 1440 additions and 0 deletions

View File

@ -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)

View File

@ -0,0 +1,8 @@
/**
* @mainpage SNP - P06 Integer Rechner
*
* @section Purpose
*
* This is a lab on usage of arrays.
*
*/

View File

@ -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);
}

View File

@ -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_

View File

@ -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);
}

View File

@ -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_

View File

@ -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);
}

View File

@ -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_

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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_

View File

@ -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;
}

View File

@ -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_

View File

@ -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
);
}