Initial commit

This commit is contained in:
Wroclaw 2022-05-15 21:47:33 +02:00
commit dabf8a22f6
14 changed files with 1072 additions and 0 deletions

1
.gitignore vendored Normal file
View file

@ -0,0 +1 @@
\build*

26
.vscode/launch.json vendored Normal file
View file

@ -0,0 +1,26 @@
{
"version": "0.2.0",
"configurations": [
{
"name": "Debug c++ project",
"type": "cppdbg",
"request": "launch",
"program": "${workspaceFolder}\\build\\build.exe",
"args": [],
"stopAtEntry": false,
"cwd": "${workspaceFolder}\\build\\",
"environment": [],
"externalConsole": true,
"MIMode": "gdb",
"miDebuggerPath": "gdb",
"setupCommands": [
{
"description": "Enable pretty-printing for gdb",
"text": "-enable-pretty-printing",
"ignoreFailures": true
}
],
"preLaunchTask": "Build c++ project"
}
]
}

8
.vscode/settings.json vendored Normal file
View file

@ -0,0 +1,8 @@
{
"files.associations": {
"*.tcc": "cpp",
"string": "cpp",
"new": "cpp",
"iosfwd": "cpp"
}
}

27
.vscode/tasks.json vendored Normal file
View file

@ -0,0 +1,27 @@
{
"tasks": [
{
"type": "cppbuild",
"label": "Build c++ project",
"command": "c++",
"args": [
"-fdiagnostics-color=always",
"-g",
"${workspaceFolder}\\src\\main.cpp",
"-o",
"${workspaceFolder}\\build\\build.exe"
],
"options": {
"cwd": "${fileDirname}"
},
"problemMatcher": [
"$gcc"
],
"group": {
"kind": "build",
"isDefault": true
},
}
],
"version": "2.0.0"
}

5
README.md Normal file
View file

@ -0,0 +1,5 @@
# calculator
Terminal-like calculator for simple arithmetic operations.
This is project for a college, given requirements are in (zadanie.txt)[./zadanie.txt].
_Inspired by vi keybinds :^)_

26
concept.txt Normal file
View file

@ -0,0 +1,26 @@
press esc or type :q to exit, type :h for help
> 2+5
7
> 2+2*2
6 <-- when typing it should change dynamically, it may be too ambitious though
> (2+2)*2
8
> :h
This is help, it should appear instantly after user presses :h
Type your equation to the terminal and get quick answers
commands:
:h - help - displays this message
:a - about program
:q - exit
> 2+2:h
It should not print equation result here
<display help message here>
It should not remove equation
> 2+2
4

417
src/calculator.cpp Normal file
View file

@ -0,0 +1,417 @@
#include <vector>
#include <string>
#include "stack.cpp"
namespace Calculator
{
enum SymbolType {
number,
add,
subract,
multiply,
divide,
left_bracket,
right_bracket,
};
/**
* @brief Represents one symbol.
*/
struct Symbol {
SymbolType type = SymbolType::number;
double value = 0;
};
void dbgPrint(const std::vector<Symbol>* _input) {
std::cout << "\033[31m";
for (auto it = _input->begin(); it != _input->end(); it++) {
auto type = it->type;
std::cout << "[";
if (type == SymbolType::add) std::cout << "+";
else if (type == SymbolType::subract) std::cout << "-";
else if (type == SymbolType::divide) std::cout << "/";
else if (type == SymbolType::multiply) std::cout << "*";
else if (type == SymbolType::number) std::cout << it->value;
else if (type == SymbolType::left_bracket) std::cout << "(";
else if (type == SymbolType::right_bracket) std::cout << ")";
std::cout << "]";
};
std::cout << "\033[0m";
}
void dbgPrint(const std::vector<unsigned int>* _input) {
std::cout << "\033[31m";
for (auto it = _input->begin(); it != _input->end(); it++) {
std::cout << "[" << *it << "]";
}
std::cout << "\033[31m";
}
/**
* @brief the result of parse function.
*/
struct parseResult {
std::vector<Symbol>* result;
short unmatchedParanthesis = 0;
};
parseResult* parse(const std::string& _input) {
parseResult* returnVal = new parseResult;
returnVal->result = new std::vector<Symbol>();
for (unsigned int i = 0; i < _input.size(); i++) {
switch (_input[i]) {
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
case '.': {
int length = 0;
auto j = i;
std::string textifiedValue = ""; // I don't know how to avoid duplication :<
while ( // while character is one of 0123456789. and it's not the end of _input
(
(_input[j] >= 0x30 && _input[j] <= 0x39) || // 0123456789
_input[j] == '.' // .
) &&
j < _input.size()
) {
textifiedValue += _input[j];
j++;
}
Symbol symbol;
symbol.value = std::atof(textifiedValue.c_str());
returnVal->result->push_back(symbol);
i = j - 1;
} break;
case '+': {
Symbol symbol;
symbol.type = SymbolType::add;
returnVal->result->push_back(symbol);
} break;
case '-': {
Symbol symbol;
symbol.type = SymbolType::subract;
returnVal->result->push_back(symbol);
} break;
case '*': {
Symbol symbol;
symbol.type = SymbolType::multiply;
returnVal->result->push_back(symbol);
} break;
case '/': {
Symbol symbol;
symbol.type = SymbolType::divide;
returnVal->result->push_back(symbol);
} break;
case '(': {
Symbol symbol;
symbol.type = SymbolType::left_bracket;
returnVal->result->push_back(symbol);
returnVal->unmatchedParanthesis++;
} break;
case ')': {
Symbol symbol;
symbol.type = SymbolType::right_bracket;
returnVal->result->push_back(symbol);
returnVal->unmatchedParanthesis--;
} break;
default:
// it should be impossible to get there
static_assert(true);
}
}
return returnVal;
}
/**
* @brief corrects negative signs in input
*
* @param _input what to correct
*/
void correctNegativeSign(parseResult* _input) {
auto parsed = _input->result;
for (auto it = parsed->begin(); it != parsed->end(); it++) {
auto jt = it + 1; //next element
if (it->type == SymbolType::subract && jt != parsed->end() && jt->type == SymbolType::number) {
it = parsed->erase(it);
it->value = it->value * (-1);
continue;
}
}
parsed->shrink_to_fit();
}
/**
* @brief validates parsation
*
* @param _input
* @return true if valid
* @return false if invalid
*/
bool validateParsation(const std::vector<Symbol>* _input) {
return true;
if (_input->size() == 0) return false;
for (auto it = _input->begin(); true /* Break argument 2 lines down */; it++) {
auto jt = it + 1;
auto kt = jt + 1;
if (jt == _input->end()) {
if (it->type == SymbolType::add) return false;
if (it->type == SymbolType::subract) return false;
if (it->type == SymbolType::multiply) return false;
if (it->type == SymbolType::divide) return false;
break;
}
if (kt == _input->end()) {
if (jt->type == SymbolType::left_bracket) {
if (it->type == SymbolType::add) return false;
if (it->type == SymbolType::subract) return false;
if (it->type == SymbolType::multiply) return false;
if (it->type == SymbolType::divide) return false;
}
}
switch (it->type)
{
case SymbolType::number:
if (jt->type == SymbolType::number) return false;
if (jt->type == SymbolType::left_bracket) return false;
break;
case SymbolType::left_bracket: // sign cannot be after left bracket, only number can be
case SymbolType::add:
case SymbolType::subract:
case SymbolType::multiply:
case SymbolType::divide:
if (jt->type == SymbolType::add) return false;
if (jt->type == SymbolType::subract) return false;
if (jt->type == SymbolType::multiply) return false;
if (jt->type == SymbolType::divide) return false;
break;
case SymbolType::right_bracket:
if (jt->type == SymbolType::number) return false;
break;
}
}
return true;
}
/**
* @brief finds maximum in _vector.
*
* @param _vector in which vector find max.
* @return unsigned int the position of maximum value.
*/
unsigned int findMaxPosition(const std::vector<unsigned int>& _vector) {
int maxPosition = 0;
int maxValue = 0;
for (int i = 0; i < _vector.size(); i++) {
if (_vector[i] > maxValue) {
maxPosition = i;
maxValue = _vector[i];
}
}
return maxPosition;
}
/**
* @brief Converts _expression to ONP
*
* @param _expression which expression to convert
* @return std::vector<Symbol>* expression in ONP
*/
std::vector<Symbol>* toONP(std::vector<Symbol>* _expression) {
std::vector<Symbol>* expression = new std::vector<Symbol>(); // will not contain brackets
std::vector<Symbol>* returnVal = new std::vector<Symbol>();
std::vector<unsigned int> priorities;
unsigned int bracketPriority = 0;
unsigned int size = _expression->size();
expression->reserve(size);
returnVal->reserve(size);
priorities.reserve(size);
// 1. Copy _expression to expression removing paranthesis
// NOTE: paranthesis should be matched before.
for (unsigned int i = 0; i < _expression->size(); i++) {
switch (_expression->at(i).type)
{
case SymbolType::number:
expression->push_back(_expression->at(i));
priorities.push_back((1<<0) + bracketPriority * (1<<3));
break;
case SymbolType::add:
case SymbolType::subract:
expression->push_back(_expression->at(i));
priorities.push_back((1<<1) + bracketPriority * (1<<3));
break;
case SymbolType::multiply:
case SymbolType::divide:
expression->push_back(_expression->at(i));
priorities.push_back((1<<2) + bracketPriority * (1<<3));
break;
case SymbolType::left_bracket:
bracketPriority++;
break;
case SymbolType::right_bracket:
bracketPriority--;
break;
}
}
size = expression->size();
// 2. Actually convert to ONP
if (size > 0) while (true) {
unsigned int maxOnPos = findMaxPosition(priorities);
if (priorities[maxOnPos] == 0) break;
if (maxOnPos >= 1 && maxOnPos - 1 < size && priorities[maxOnPos-1] != 0) { // left
returnVal->push_back(expression->at(maxOnPos-1));
priorities[maxOnPos-1] = 0;
}
if (maxOnPos >= 0 && maxOnPos + 1 < size && priorities[maxOnPos+1] != 0) { // right
returnVal->push_back(expression->at(maxOnPos+1));
priorities[maxOnPos+1] = 0;
}
if (maxOnPos >= 0 && maxOnPos < size && priorities[maxOnPos] != 0) { // center (maximum element)
returnVal->push_back(expression->at(maxOnPos));
priorities[maxOnPos] = 0;
}
}
delete expression;
return returnVal;
}
Symbol calculateUsingStack(std::vector<Symbol>& _stack) {
switch (_stack.back().type) {
case SymbolType::number: {
return Stack::take(_stack);
}
case SymbolType::add: {
Stack::take<Symbol>(_stack); //top symbol is add
Symbol toPut;
toPut.value = calculateUsingStack(_stack).value + calculateUsingStack(_stack).value;
Stack::put<Symbol>(_stack, toPut);
return toPut;
} break;
case SymbolType::subract: {
Stack::take<Symbol>(_stack); //top symbol...
Symbol toPut;
toPut.value = calculateUsingStack(_stack).value - calculateUsingStack(_stack).value;
Stack::put<Symbol>(_stack, toPut);
return toPut;
} break;
case SymbolType::multiply: {
Stack::take<Symbol>(_stack); //top symbol...
Symbol toPut;
toPut.value = calculateUsingStack(_stack).value * calculateUsingStack(_stack).value;
Stack::put<Symbol>(_stack, toPut);
return toPut;
} break;
case SymbolType::divide: {
Stack::take<Symbol>(_stack); //top symbol...
Symbol toPut;
auto val = calculateUsingStack(_stack).value;
toPut.value = calculateUsingStack(_stack).value / val;
Stack::put<Symbol>(_stack, toPut);
return toPut;
} break;
default:
throw "Impossible";
}
}
/**
* @brief the return value of parseAndCalculate
*/
struct parseAndCalculateResult {
int unmatchedParanthesis = 0;
bool valid = true;
double value = 0;
};
/**
* @brief Parses and calculates the value of math expression expressed in std::vector<char>.
*
* @param _input mathematical expression.
* @return parseAndCalculateResult the result of solving it.
*/
parseAndCalculateResult parseAndCaluclate(const std::string& _input, bool debug = false) {
parseAndCalculateResult returnVal;
// 0. return if _input is empty
if (_input.size() == 0) return returnVal;
// 1. Parse input
parseResult* parsed = parse(_input);
// 2. if unmatchedBrackes > 0, add unmatched brackets
returnVal.unmatchedParanthesis = parsed->unmatchedParanthesis; // Let's not forget that we still return it :^)
while (parsed->unmatchedParanthesis > 0) {
Symbol sym;
sym.type = SymbolType::right_bracket;
parsed->unmatchedParanthesis--;
parsed->result->push_back(sym);
}
// 3. if unmatchedBrackets < 0, throw something or... abort
// display amount of missing opening brackets in red at the end,
// if it's more than 3, display number, not individual ones
// IMPLEMENT DISPLAYING IT SOMEWHERE ELSE!!!
if (parsed->unmatchedParanthesis < 0) {
returnVal.unmatchedParanthesis = parsed->unmatchedParanthesis;
return returnVal;
};
// 4. correct minus sign
correctNegativeSign(parsed);
// 5. Validate parsation. If invalid, set appripraite flag and return.
returnVal.valid = validateParsation(parsed->result);
if (!returnVal.valid) {
delete parsed;
return returnVal;
}
// 6. Convet to Reversed Polish Adnotation (ONP)
auto* onped = toONP(parsed->result);
delete parsed;
// 7. if onped vector is length of 0, invalidate and return.
if (onped->size() == 0) {
delete onped;
return returnVal;
}
// 8. do a stacky wacky recursive onp magic to calculate the result
try {
returnVal.value = calculateUsingStack(*onped).value;
}
catch (char const* e) {
//everything is fine
returnVal.value = onped->end()->value;
}
delete onped;
return returnVal;
}
}; // namespace Calculator

79
src/commands.cpp Normal file
View file

@ -0,0 +1,79 @@
#include <iostream>
#include <string>
#include "cursor.cpp"
#include "terminal.cpp"
namespace commands {
/**
* @brief Prints keys in command like visual input in current line.
*
* @param keys the string to print
*/
void printInput(terminal::state keys) {
cursor::setX(0);
std::cout << "\033[2K:" << keys.keys;
}
/**
* @brief prints the desired output in command-like mode
*
* @param keys keys pressed
* @param string the output
*/
void printOutput(terminal::state keys, std::wstring string) {
cursor::setX(0);
cursor::up();
std::cout << "\033[2K\n\033[2K:" << keys.keys << "\n";
std::wcout << L"\033[2K" << string << "\n";
}
/**
* @brief main loop for commands input.
*
* @return true if we should quit program.
*/
bool loop() {
std::cout << "\n\033[2K";
terminal::state keys;
while(true) {
printInput(keys);
auto input = cursor::getchar();
if (input.code >= 'a' && input.code <= 'z' || input.code >= 'A' && input.code <= 'Z') {
terminal::key_insert(keys, input.code);
continue;
}
else if (input.code == 8) { // BACKSPACE
if (!terminal::key_backspace(keys)) return false;
}
else if (input.code == 13) { // ENTER
if (keys.keys.length() == 0) {
printOutput(keys, L"No command provided");
break;
}
if (keys.keys[0] == 'q') {
printOutput(keys, L"Exiting...");
return true;
}
else if (keys.keys[0] == 'a') {
printOutput(keys, L"calculator\nCopyright Rafał F.\nAll rights reserved.");
break;
}
else if (keys.keys[0] == 'h') {
printOutput(keys, L"Calculator\n:q - quit\n:a - about");
break;
}
else {
printOutput(keys, L"Command not found\n");
break;
}
}
}
return false;
}
} //namespace commands

145
src/cursor.cpp Normal file
View file

@ -0,0 +1,145 @@
#ifndef __CURSOR_CPP
#define __CURSOR_CPP
#include <iostream>
#include "getch.h"
/**
* @brief Set of tools to manipulate cursor in terminal.
*/
namespace cursor {
/**
* @brief Represents cursor coordinates.
*/
struct position {
short x = 0; // column
short y = 0; // line
};
/**
* @brief Get current cursor position.
*
* @return position the current position.
*/
position getPosition() {
position returnVal;
std::cout << "\033[6n";
bool readSemicolon = false;
bool stillReading = true;
while (stillReading) {
char read = getch();
// \033[{line};{column}R
switch (read)
{
case '\033':
case '[':
break;
case ';':
readSemicolon = true;
break;
case 'R':
stillReading = false;
break;
default:
// if character read is one of: 0123456789
if (read >= 0x30 & read <= 0x39) {
read -= 0x30;
if (readSemicolon) // if reading x pos
returnVal.x = 10 * returnVal.x + read;
else
returnVal.y = 10 * returnVal.y + read;
}
else {std::cout << (int)read << ",";}
break;
}
}
return returnVal;
}
/**
* @brief Set the position of a cursor in terminal.
*
* @param x the column to which to set.
* @param y the line to which to set.
*/
void setPosition(const short& x, const short& y) {
std::cout << "\033[" << y << ";" << x << "H";
}
/**
* @brief Set the position of a cursor in terminal.
*
* @param pos the position to which to set.
*/
void setPosition(const position& pos) {
std::cout << "\033[" << pos.y << ";" << pos.x << "H";
}
/**
* @brief Sets the column of a cursor.
*
* @param _x The column to which to set.
*/
void setX(const short& _x) { std::cout << "\033[" << _x << "G"; }
/**
* @brief Set the row of a cursor.
*
* @param _y The row to which to set.
*/
void setY(const short& _y) { setPosition(getPosition().x, _y); }
/**
* @brief Requests terminal to save position.
*/
void savePosition() { std::cout << "\0337"; }
/**
* @brief Requests termianl to restore saved position.
*/
void restorePosition() { std::cout << "\0338"; }
/**
* @brief moves cursor 1 line up
*/
void up() { std::cout << "\033[1A"; }
/**
* @brief represents pressed key.
*
*/
struct key {
unsigned char code = 0;
unsigned char special = 0;
};
/**
* @brief gets keyboard characker.
*
* @return key the pressed key.
*/
key getchar() {
key returnval;
unsigned char input = getch();
switch (input) {
case 0: // special
returnval.special = 1;
returnval.code = getch();
break;
case 224: // non numpad navigation keys
returnval.special = 2;
returnval.code = getch();
break;
default:
returnval.code = input;
break;
}
return returnval;
}
}; // namespace Cursor
#endif

12
src/getch.h Normal file
View file

@ -0,0 +1,12 @@
#ifndef __GETCH_H
#define __GETCH_H
#ifdef _WIN32
// FIXME: assuming that windows getch behaves the same as linux getch, this should be checked!
#include <conio.h>
#include <windows.h> // used only for virtual terminal configuring for windows default terminal
#else
#include <curses.h>
#endif
#endif

185
src/main.cpp Normal file
View file

@ -0,0 +1,185 @@
#include <iostream>
#include <sstream>
#include "calculator.cpp"
#include "commands.cpp"
#include "cursor.cpp"
#include "getch.h"
#include "terminal.cpp"
/**
* @brief Prints value of pressed key in top left corner.
* Additionally it can get offet to see more presses.
*
* @param _flags keypress flags.
* @param _key char that we got.
* @param offset how much column do you want to spare?
*/
void debugKey(cursor::key _key, short& offset) {
cursor::savePosition();
cursor::setPosition(1, 1);
// std::cout << " "; // clear debug corner :^)
cursor::setPosition(offset++ * 4 + 1, 1);
std::cout << "\033[31;1m";
// if control flag set
if (_key.special != 0)
std::cout << "^";
std::cout << int(_key.code);
std::cout << "\033[0m";
cursor::restorePosition();
}
/**
* @brief Prints entered keys to the console.
*
* @param keys currently typed keys.
*/
void writeInput(terminal::state keys) {
// FIXME: Do not duplicate lines when line overflows
// possible mitigation: only write one line
// FIXME: flickering
cursor::setX(1);
std::cout << "\033[0K" << "> ";
for(auto it = keys.keys.begin(); it != keys.keys.end(); it++) {
std::cout << *it;
}
cursor::setX(3+keys.cursorPos);
}
/**
* @brief Writes result line with the result while in typing mode
*
* @param string the result
* @param paranthesis missing paranthesis count, negative if misses left paranthesis
* @param calcuationValid is calculation valid
* @param inputSize the current input size (used to where display missing paranthesis)
*/
void writeResultLine(
const std::string string,
const int paranthesis = 0,
const bool calcuationValid = true,
const unsigned int inputSize = 20
) {
auto savedCursorPosition = cursor::getPosition().x;
if (paranthesis != 0) {
cursor::setX(inputSize+5);
std::cout << "\033[31m";
if (paranthesis > 4) {
std::cout << ") * " << paranthesis;
}
else if (paranthesis > 0) {
for(unsigned short i = 0; i < paranthesis; i++) std::cout << ")";
}
else if (paranthesis < 4) {
std::cout << "( * " << -paranthesis;
}
else { //paranthesis is one of -1,-2,-3,-4
for(unsigned short i = 0; i < -paranthesis; i++) std::cout << ")";
}
std::cout << "\033[0m";
}
std::cout << "\n\033[0K\033[90m";
// FIXME: line overflow handling
if (calcuationValid) std::cout << string;
else std::cout << "-";
cursor::up();
cursor::setX(savedCursorPosition);
std::cout << "\033[0m";
}
/**
* @brief Writes result line with the result while in typing mode
*
* @param result the calculation result
* @param inputSize the current input size (used to where display missing paranthesis)
*/
void writeResultLine(Calculator::parseAndCalculateResult result, const unsigned int inputSize) {
std::stringstream result_string;
result_string << result.value;
writeResultLine(result_string.str(), result.unmatchedParanthesis, result.valid, inputSize);
}
int main() {
#ifdef _WIN32
// https://stackoverflow.com/a/47158348
{ // enable virtual terminal for legacy Windows terminals
HANDLE console = GetStdHandle(STD_OUTPUT_HANDLE);
DWORD consoleMode;
GetConsoleMode(console, &consoleMode);
consoleMode |= 0x0004;
SetConsoleMode(console, consoleMode);
}
#endif
std::setlocale(LC_ALL, "");
std::cout << "type :q to exit, type :h for help\n";
short debugOffset = 0; //debug
bool exit = false; //should we exit?
terminal::state keys;
while (!exit) {
writeInput(keys);
// writeResultLine("");
// background calculation
// new std::thread([keys]{
writeResultLine(Calculator::parseAndCaluclate(keys.keys), keys.keys.size());
// });
auto input = cursor::getchar();
// if speciality char
if (input.special != 0) {
switch (input.code)
{
case 75: // ARROW_LEFT
terminal::key_left(keys);
break;
case 77: // ARROW_RIGHT
terminal::key_right(keys);
break;
default:
debugKey(input, debugOffset);
break;
}
continue;
}
else switch (input.code) {
case 8: // BACKSPACE
terminal::key_backspace(keys);
break;
case 13: // ENTER
std::cout << "\n\033[2K" << Calculator::parseAndCaluclate(keys.keys, true).value << "\n";
keys.cursorPos = 0;
keys.keys.clear();
break;
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
case '.':
case '+':
case '-':
case '*':
case '/':
case '(':
case ')':
terminal::key_insert(keys, input.code);
break;
case ':':
exit |= commands::loop();
break;
default:
debugKey(input, debugOffset);
break;
};
}
std::cout << "\033[0m";
return 0;
}

39
src/stack.cpp Normal file
View file

@ -0,0 +1,39 @@
#include <vector>
/**
* @brief Set of tools to manipulate vector stacks.
*/
namespace Stack
{
template <class type>
bool isEmpty(const std::vector<type>& stack) {
return stack.empty();
};
// template <class type>
// bool isFull(std::vector<type>& stack) {
// return !stack->empty();
// };
template <class type>
void put(std::vector<type>& stack, const type& item) {
stack.push_back(item);
}
template <class type>
type take(std::vector<type>& stack) {
if (stack.size() == 0) throw "a";
type toReturn = stack.back();
stack.pop_back();
return toReturn;
}
namespace oper {
template <class type>
void add(std::vector<type>& stack) {
take(stack);
// put(take(stack)+)
}
}
}; // namespace stack

66
src/terminal.cpp Normal file
View file

@ -0,0 +1,66 @@
#ifndef __TERM_CPP
#define __TERM_CPP
#include <string>
namespace terminal {
/**
* @brief represents state of input form
*/
struct state {
std::string keys;
unsigned int cursorPos = 0;
};
/**
* @brief removes current character from state.
*
* @param state
* @return true if character was removed
*/
bool key_backspace(state& state) {
if (state.cursorPos <= 0) return false;
state.keys.erase(state.cursorPos - 1, 1);
state.cursorPos--;
return true;
}
/**
* @brief moves cursor one characer to the left
*
* @param state
* @return true if moved
*/
bool key_left(state& state) {
if (state.cursorPos <= 0) return false;
state.cursorPos--;
return true;
}
/**
* @brief moves cursor one character to the right
*
* @param state
* @return true if moved
*/
bool key_right(state& state) {
if (state.cursorPos >= state.keys.length()) return false;
state.cursorPos++;
return true;
}
/**
* @brief inserts character to current state
*
* @param state form state
* @param key character to insert
*/
void key_insert(state& state, unsigned char key) {
state.keys.insert(state.cursorPos, std::string(1, key));
state.cursorPos++;
}
} //namespace terminal
#endif

36
zadanie.txt Normal file
View file

@ -0,0 +1,36 @@
Aplikacja rozwiązująca równania matematyczne
Zastosowany algorytm: ONP
Zastosowana struktura: STOS/LIFO
Na wejściu: równanie matematyczne
Na wyjściu: wynik obliczeń, liczba
Akceptowane znaki: wszystkie matematyczne: +, -, * ,/
Akceptowalne inne znaki: TAK
Inne znaki akceptowalne: ( )[].
Język programowania: C/C++
Możliwość zastosowania gotowych bibliotek: Brak możliwości stosowania wbudowanych funkcji, tylko IOSTREAM
Sposób programowania: STRUKTURALNY
Wymagane komentarze: TAK
Wymagane formatowanie: TAK - prawidłowe formatowanie kodu
Wymagany opis funkcji: TAK
Hermetyczność funkcji: TAK
Zasięg zmiennych: LOKALNY
Obiekty wskaźnikowe: DOZWOLONE
Nadmiarowość zakresu zmiennych: NIEDOZWOLONA
Nadmiarowość kodu: NIEDOZWOLONA
Interfejs użytkownika: TAK
Forma interfejsu: TEKSTOWA
Możliwość wyboru opcji: TAK
Konieczność zatwierdzania enterem w UI: NIE
Wbudowane okno informacji o autorze i wersji programu: TAK
Dokumentacja: TAK
Forma dokumentacji: PEŁNA
Poziom dokumentacji: 3 stopniowa (wymagania, instrukcja z zrzutami ekranu, kod programu)
Format instrukcji: A4
Format zapisu instrukcji: PDF
Format pliku do upload'u: ZIP (bez hasła)
Czcionka kodu programu: KONSOLOWA
Wymagana wersja źródłowa kodu: TAK
Wymagana wersja wykonywalna kodu: TAK
Karta projektu: TAK
Format karty: A4
Format zapisu karty: PDF