Git Product home page Git Product logo

simplecalculator's Introduction

Simple Interpreting Calculator Project

Requirements

  • g++ compiler

Building and running the project:

make run

Demo

example03

Algorithm

GeeksForGeeks
Wikipedia

  1. Get the expression as a string.
    • If string contains assignment operation then split it to variable and expression to evaluate. ('x=1+2' -> variable to assign a value to: 'x' and value to be assigned '1+2' (will be evaluated in next steps))
  2. Convert string to array of special symbols (operators, operands, brackets, etc).
    1. Handle sequences of numbers to get numbers with multiple digits and number with floating point.
    2. Handle sequences of letters to get variables with multiple digits including numbers (like 'var1').
    3. Handle unary operators by converting them to binary operators (replace '-x' with '(-1)*x').
  3. Convert array of special symbols to BET (binary expression tree)
    First example:
    EXPRESSION: (3*(x-4)-8)*(6-2*y)
    BINARY EXPRESSION TREE:
    ---{*}
       |--{-}
       |   |--{*}
       |   |   |--{3}
       |   |   ---{-}
       |   |       |--{x}
       |   |       ---{4}
       |   ---{8}
       ---{-}
          |--{6}
          ---{*}
                |--{2}
                ---{y}
    
    
    Second example (different output of a tree):
    EXPRESSION: (34 * (-var - 4.5) + 68.95) * (67 - (5^2)/per)
    BINARY EXPRESSION TREE:
                   {per}
                {/}
                      {2.00}
                   {^}
                      {5.00}
             {-}
                {67.00}
          {*}
                {68.95}
             {+}
                      {4.50}
                   {-}
                         {var}
                      {*}
                         {-1.00}
                {*}
                   {34.00}
    
    • If current expression is length of 1, then add it to root.
    • Else find the last operator with the minimum priority and add it to root. Then add to root the result of recursive call of the same function on the expression on the left and right side of this operator.
  4. Evaluate binary BET from bottom to top.
    • If the node is number or variable, then return it's value.
    • Else (the node is operator) return the result of recursive call of the same function on the right and left child.

Main features

  • Functions
  • Types: floating point, integer, nan
  • Basic binary operations: - + * /
  • Binary shifts < and >
  • Unary + and -
  • Multiple digits numbers. ex: 123
  • Multiple digits variables (including numbers on non-first position). ex: length1
  • Floating point number. 3.14 or .15
  • Positive integer powers: 2^3 -> 8
  • Space ignoring in expressions for better user experience.
  • Syntax errors handling
  • External Libraries

Other features

Use ! sign to print something

  • In a console mode it'll just print message after '!'
  • In a file mode it'll print message after '!' and result of evaluating of next line of file.
File mode:
   File:
   !radius = 
   radius = 10

   Output:
   [6]: radius=
   [7]: 10

Console mode:
   >>> !hello
   hello

See the result of previous evaluation with reserved underscore '_' variable

>>> 3*4 + 2^5
44
>>> _
44
>>> 

Display current variables with their values with 'vars' command

>>> x=1
1
>>> y=2
2
>>> z=x*y^2
4
>>> vars
VARIABLES:
   _ = int   4
   x = int   1
   y = int   2
   z = int   4
END

External libraries Create .splc file with variable and function declarations.
On runtime use 'import' keyword.
Inside a library file you can only declare functions and assign values to variables. NOTHING MORE. Any other commands will be ignored.
Only one level of depth:
Library -> none
InputFile -> Library -> none

>>> import math
importing: 'math'
import finished

There is a predefined math.splc with definition of pi, e, area(radius) - calculate area of a circle with a given radius.

Functions Define a function with keyword:

def:

Syntaxis of defining a function:

def: name([arg1, arg2, ...]) = expression

Example:

>>> def: mult_add_square(a,b,c)=a*b+c^2
new function 'mult_add_square' added

Maximum number of function args see in constants.h: FUNCTION_MAX_ARGS_N.
Args can be:

  • Number
  • Variable
  • Call of other function
    Args can't be expression (like '3+1' or 'x*10'). Example of calling a function defined in previous example with variable and number arg:
>>> x = 2
2
>>> mult_add_square(x,3,4) // 2*3 + 4^2 = 6 + 16 = 22
22

Example of calling a function with argument as another function call:

>>> def: f(x) = x^2
new function 'f' added
>>> def: g(x) = x+1
new function 'g' added
>>> g(f(2))
5

Functions can use other functions in their expression:

>>> def: f(x) = x^2
new function 'f' added
>>> def: g(y) = f(y)*2
new function 'g' added
>>> g(2)
8

To define a function with no args use null keyword

>>> def: f(null) = 10*0.5
new function 'f' added
>>> funcs
FUNCTIONS:
   f() = 10*0.5
END

Save current variables in text or binary file. Load previously saved variables if binary file with them exists. Use keywords 'save', 'saveB', 'load'.
Save and load example:

>>> x=1
1
>>> y=2
2
>>> saveB
successfully saved 3 variables
>>> x=0
0
>>> y=0
0
>>> load
successfully loaded 3 variables
>>> x
1
>>> y
2

Close the application with 'exit'

>>> exit
bye

simplecalculator's People

Contributors

plasmaa0 avatar

Stargazers

 avatar  avatar  avatar  avatar

Watchers

 avatar

simplecalculator's Issues

memory leaks

i'm sure there are many memory leaks in the entire program

optimization

char result[EXPR_MAX_LEN + 1];
int resultLength = 0;
for (unsigned int i = 0; i < strlen(expr); i++)
{
if (expr[i] != ' ')
{
result[resultLength] = expr[i];
resultLength++;
}
}
result[resultLength] = '\0';
strncpy(expr, result, EXPR_MAX_LEN + 1);
}
void smartLineNumberPrint(char *expr, int lineNumber)
{
switch (recognizeExpressionType(expr))

if the length of result == length of input then do not strncpy??

logger

Is your feature request related to a problem? Please describe.
It's mess in the code with #ifdef DEBUG printing debug messages

Describe the solution you'd like
Create logger module that can print messages with different levels of message (info, log, warning, error, fatal)

Describe alternatives you've considered
create some macros but it's still will look messy

memory usage

void addFunction(char *funcName, Function func, FunctionDictionary *dict)
{
bool alreadyExist = false;
for (unsigned int i = 0; i < dict->freeIndex; i++)
{
if (strncmp(dict->names[i], funcName, MAX_VARIABLE_NAME_LEN) == 0)
{
// printf("reset %s from %f to %f\n", variableName, dict->values[i], value);
dict->functions[i] = func;
alreadyExist = true;
printf("function '%s' redefinded\n", funcName);
break;
}
}
if (dict->freeIndex < dict->size and (not alreadyExist))
{
// if (not alreadyExist)
// {
// printf("call strncpy func\n");
strncpy(dict->names[dict->freeIndex], funcName, MAX_NUMBER_LENGTH);
dict->functions[dict->freeIndex] = func;
dict->freeIndex++;
printf("new function '%s' added\n", funcName);
// }
}
else if (not alreadyExist)
{
printf("dictionary overflow, can't add function %s\n", funcName);
}
}

void addFunction(char *funcName, **Function func**, FunctionDictionary *dict)

why not pointer to Function func?

magic number

unsigned int prioritizedOperatorIndex(Expression *expr)
{
setPriorities(expr);
int minPriority = 10000000;
int priorOpIndex = PRIORITIZED_OPERATOR_NOT_FOUND;
unsigned int nestLevel = 0;
for (int i = 0; i < expr->length; i++)
{
if (expr->symbols[i].type == ESymbolType::OPENING_BRACKET)
{
nestLevel++;
}
else if (expr->symbols[i].type == ESymbolType::CLOSING_BRACKET)
{
nestLevel--;
}
if (expr->symbols[i].type == ESymbolType::OPERATOR && expr->symbols[i].priority <= minPriority && nestLevel == 0)
{
minPriority = expr->symbols[i].priority;
priorOpIndex = i;
}
}
return priorOpIndex;
}

setVariable optimization

void setVariable(char *variableName, Number number, VariableDictionary *dict)
{
auto val = (number.type == EnumberType::INTEGER ? number.value.integer : number.value.decimal);
bool alreadyExist = false;
for (unsigned int i = 0; i < dict->freeIndex; i++)
{
if (strncmp(dict->keys[i], variableName, MAX_VARIABLE_NAME_LEN) == 0)
{
// printf("\t\t\t\t\tRESET %s\n", variableName);
dict->values[i] = number;
alreadyExist = true;
break;
}
}
if (dict->freeIndex < dict->size and (not alreadyExist))
{
// if (not alreadyExist)
// {
// printf("\t\t\t\tdict[FREE] = %s\n", dict->keys[dict->freeIndex]);
// printf("strncpy call\n");
strncpy(dict->keys[dict->freeIndex], variableName, MAX_NUMBER_LENGTH);
dict->values[dict->freeIndex] = number;
dict->freeIndex++;
// }
}
else if (not alreadyExist)
{
printf("dictionary overflow, can't add variable %s = %f\n", variableName, val);
}
}

mb check if it is correct variable name? (except '_')

unsafe actionsmb use strNcmp instead of strcmp?

{
return EExpressionType::DO_NOTHING;
}
if (strcmp(expr, "exit") == 0)
{
return EExpressionType::EXIT;
}
if (strcmp(expr, "help") == 0)
{
return EExpressionType::HELP;
}
if (strcmp(expr, "vars") == 0)
{
return EExpressionType::SHOW_VARIABLES;
}
if (strcmp(expr, "funcs") == 0)
{
return EExpressionType::SHOW_FUNCTIONS;
}
if (strcmp(expr, "saveB") == 0)
{
return EExpressionType::SAVE_VARIABLES_BIN;
}
if (strcmp(expr, "save") == 0)
{
return EExpressionType::SAVE_VARIABLES_TXT;
}
if (strcmp(expr, "load") == 0)
{
return EExpressionType::LOAD_VARIABLES;
}
if (strchr(expr, '='))
{
if (strncmp(expr, "def:", 4) == 0)
{
// printf("f create\n");
return EExpressionType::CREATE_FUNCTION;
}
else
{
// printf("not f\n");
return EExpressionType::EVALUATE_AND_ASSIGN;
}
}
else
{
return EExpressionType::EVALUATE;
}
printf("unrecognized expression\n");
return EExpressionType::DO_NOTHING;
}
int equalsSignIndex(char *expr)
{

memory leak

bool evaluateFunction(Number *args, unsigned int argsN, Function *func, FunctionDictionary *fdict, Number &result)
{
if (func->argsNumber != argsN)
{
printf("incompatible argument number\n");
return false;
}
VariableDictionary *localVariables = createVariableDictionary(argsN + 1);
for (unsigned int i = 0; i < argsN; i++)
{
#ifdef DEBUF
printf(">>>>>>>>>>>>>>>>>>>>>>>>>>> %d >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>\n", i);
// printf(" BEFORE PRINT THE VARIABLE\n");
// printf("local vdict and fdict:\n");
// print(localVariables);
// print(fdict);
printf("%s on [%p] var set %s = ", func->asString, func, func->argsNames[i]);
print(args[i]);
printf("\n");
// printf(" AFTER PRINT THE VARIABLE\n");
// printf("local vdict and fdict:\n");
// print(localVariables);
// print(fdict);
// printf("call set\n");
#endif
setVariable(func->argsNames[i], args[i], localVariables);
// printf(" AFTER SETTING THE VARIABLE\n");
// printf("local vdict and fdict:\n");
// print(localVariables);
// print(fdict);
}
#ifdef DEBUF
printf(" EVALUATE FUNCTION --->>> EVAL\n");
#endif
Expression *functionAsExpression = strToExpr(func->asString);
if (functionAsExpression == nullptr)
{
#ifdef DEBUF
printf("evaluateFunction error in strToExpr(func->asString)\n");
#endif
return false;
}
else
{
#ifdef DEBUF
printf("evaluateFunction SUCCESS in strToExpr(func->asString)\n");
#endif
}
BETNode *functionAsBET = exprToAET(functionAsExpression);
if (functionAsBET == nullptr)
{
#ifdef DEBUF
printf("evaluateFunction error in exprToAET(functionAsExpression)\n");
#endif
return false;
}
else
{
#ifdef DEBUF
printf("evaluateFunction SUCCESS in exprToAET(functionAsExpression)\n");
#endif
}
bool success = eval(functionAsBET, localVariables, fdict, result);
if (not success)
{
#ifdef DEBUF
printf("failure at eval(functionAsBET, localVariables, fdict, result)\n");
#endif
}
else
{
#ifdef DEBUF
printf("SUCCESS at eval(functionAsBET, localVariables, fdict, result)\n");
#endif
}
/**
*
* CLEAR MEMORY FROM:
* - functionAsExpression
* - functionAsBET
* - localVariables
*
*/
return success;
}

after evaluating the function

  • delete local variables
  • delete function as expression
  • delete function as BET

memory leakexpression not deleted after converting to BET. BET not deleted after evaluating.

Expression *e = strToExpr(str);
// printf("strToExpr success: ");
// print(e);
// printf("\n");
BETNode *root = exprToAET(e);
// printf("exprToAET success\n");
// prettyPrint(root);
// print(root);
bool evalSuccess = eval(root, dict, fdict, result);
// if (evalSuccess)
// printf("OK OK OK OK OK OK OK OK OK OK OK OK OK OK OK OK OK OK OK OK OK \n");
// else
// printf("FAILURE FAILURE FAILURE FAILURE FAILURE FAILURE FAILURE FAILURE\n");
// printf("|%d|\n", (evalSuccess ? 111111 : 999999));
// printf("eval success\n");
// printf("pre-order: ");
// pre_order(root);
// printf("\n");
// printf("in-order: ");
// in_order(root);
// printf("\n");
// printf("post-order: ");
// post_order(root);
// printf("\n");
return evalSuccess;
}
EExpressionType recognizeExpressionType(char *expr)
{

memory leak

lastResult[1] = '\0';
char *expr = new char[EXPR_MAX_LEN + 1];
bool running = true;
int lineNumber = 0;
while (running)
{

dynamicly allocated objects not get deleted in the end of function

handle number sequences

Symbol newSymbol;
if (currentNumberLength + decimalPartLength > 0)
{
//number finished construction
// print(digitsBuffer, currentNumberLength);
Number number = numberFromDigits(digitsBuffer, currentNumberLength, decimalPartBuffer, decimalPartLength);
// printf("%d", number);
//create symbol
newSymbol.type = ESymbolType::NUMBER;
newSymbol.entity.number = number;
//reset cycle
memset(digitsBuffer, 0, MAX_NUMBER_LENGTH * sizeof(int));
memset(decimalPartBuffer, 0, MAX_NUMBER_LENGTH * sizeof(int));
decimalPartLength = 0;
currentNumberLength = 0;
currentIsNumber = false;
decimalPart = false;
//add to array
oversizedSymbolArray[resultLength] = newSymbol;
resultLength++;
}
newSymbol = expr->symbols[symbolIndex];
oversizedSymbolArray[resultLength] = newSymbol;
resultLength++;
// print(newSymbol);
}
}
if (currentNumberLength + decimalPartLength > 0)
{
Symbol newSymbol;
//number finished construction
// print(digitsBuffer, currentNumberLength);
Number number = numberFromDigits(digitsBuffer, currentNumberLength, decimalPartBuffer, decimalPartLength);
// printf("%d", number);
//create symbol
newSymbol.type = ESymbolType::NUMBER;
newSymbol.entity.number = number;
//add to array
oversizedSymbolArray[resultLength] = newSymbol;
resultLength++;
}
Expression *result = new Expression;

mb extract adding new element to an array as inline function because it repeats twice - inside cycle and outside of cycle.

//reset cycle
memset(digitsBuffer, 0, MAX_NUMBER_LENGTH * sizeof(int));
memset(decimalPartBuffer, 0, MAX_NUMBER_LENGTH * sizeof(int));
decimalPartLength = 0;
currentNumberLength = 0;
currentIsNumber = false;
decimalPart = false;

memset isn't neccessarry

Expression *result = new Expression;
result->length = resultLength;
result->symbols = new Symbol[resultLength];
// memcpy(result->symbols, oversizedSymbolArray, resultLength * sizeof(Symbol));
// printf("over:\n");
for (int i = 0; i < resultLength; i++)
{
// print(oversizedSymbolArray[i]);
result->symbols[i] = oversizedSymbolArray[i];
}
// printf("\nexpr:\n");
// print(result);
// printf("\n");
return result;

oversizedSymbolArray is left unused but not deleted

magic number

void setPriorities(Expression *expr)
{
unsigned int nestLevel = 0;
for (int i = 0; i < expr->length; i++)
{
if (expr->symbols[i].type == ESymbolType::OPENING_BRACKET)
{
nestLevel += 10;
expr->symbols[i].priority = -nestLevel;
}
else if (expr->symbols[i].type == ESymbolType::CLOSING_BRACKET)
{
nestLevel -= 10;
expr->symbols[i].priority = -nestLevel;
}
else if (expr->symbols[i].type == ESymbolType::OPERATOR)
{
expr->symbols[i].priority = nestLevel + priority(expr->symbols[i].entity.operator_);
}
else
{
expr->symbols[i].priority = nestLevel;
}
}
}

memory leak probably :

char *functionName = strtok(functionDeclaration, "(");
char *variablesList = strtok(NULL, ")=");
char *functionBody = strtok(NULL, "=");
// printf("b:");
// print(strToExpr(functionBody));
// printf("\n");
// printf("name: |%s|\nargs: |%s|\nbody: |%s|\n", functionName, variablesList, functionBody);
Function *func = createFunction(variablesList, functionBody);
// printf("func entity created\n");
addFunction(functionName, *func, functions);
break;
}
case EExpressionType::SHOW_FUNCTIONS:
{
print(functions);

bunch of pointers are not freed at the end of case

optimization

char result[EXPR_MAX_LEN + 1];
int resultLength = 0;
for (unsigned int i = 0; i < strlen(expr); i++)
{
if (expr[i] != ' ')
{
result[resultLength] = expr[i];
resultLength++;
}
}
result[resultLength] = '\0';
strncpy(expr, result, EXPR_MAX_LEN + 1);
}
void smartLineNumberPrint(char *expr, int lineNumber)
{
switch (recognizeExpressionType(expr))

memory leak

Expression *strip(Expression *expr)
{
bool stripLeft = false;
if (expr->symbols[0].type == ESymbolType::OPENING_BRACKET || expr->symbols[0].type == ESymbolType::CLOSING_BRACKET)
{
stripLeft = true;
}
bool stripRight = false;
if (expr->symbols[expr->length - 1].type == ESymbolType::OPENING_BRACKET || expr->symbols[expr->length - 1].type == ESymbolType::CLOSING_BRACKET)
{
stripRight = true;
}
if (stripLeft && stripRight)
{
expr = slice(expr, 1, expr->length - 1);
}
else if (stripLeft)
{
expr = slice(expr, 1, expr->length);
}
else if (stripRight)
{
expr = slice(expr, 0, expr->length - 1);
}
return expr;
}

if stripLeft || stripRight then delete input expr after stripping

optimization

case ESymbolType::NUMBER:
symb.entity.number.type = EnumberType::INTEGER;
symb.entity.number.value.integer = atoi(&ch);
break;

mb (int)('0' + ch) instead of atoi?

memory leak

char op;
if (isCompound)
{
op = getCompoundOperator(expr);
// printf("op: %c\n", op);
char opDivider[2];
opDivider[0] = op;
opDivider[1] = ' ';
strncpy(var, strtok(expr, opDivider), MAX_VARIABLE_NAME_LEN);
// if (strchr(var, ' ') != nullptr) //strip trailing whitespace if exist
// var[var - strchr(var, ' ') - 1] = '\0';
// strtok(NULL, "="); //skip the '='
}
else
{
strncpy(var, strtok(expr, " ="), MAX_VARIABLE_NAME_LEN);
}
if (not isCorrectVariableName(var))
{
printf("invalid variable name\n");
break;
}
strncpy(expr, strtok(NULL, "="), EXPR_MAX_LEN);
// printf("var: |%s|\nexpr: %s\n", var, expr);
Number evaluationResult;
bool evalSuccess = eval(expr, dict, functions, evaluationResult);
if (evalSuccess)
{
if (isCompound)
{
Number oldVariableValue;
Number newVariableValue;
getVariable(var, dict, oldVariableValue);
solve(oldVariableValue, evaluationResult, op, newVariableValue);
setVariable(var, newVariableValue, dict);
}
else
{
setVariable(lastResult, evaluationResult, dict);
print(evaluationResult);
printf("\n");
setVariable(var, evaluationResult, dict);
}
}
break;
}
case EExpressionType::CREATE_FUNCTION:
{
char *functionDeclaration = expr + 4; //strip function declaration keyword

char *var that is dynamically allocated is not deleted at the end of case

is it neccessarry to memset?

FunctionDictionary *createFunctionDictionary(unsigned int size)
{
FunctionDictionary *dict = new FunctionDictionary;
dict->size = size;
dict->freeIndex = 0;
dict->functions = new Function[size];
dict->names = new char *[size];
for (unsigned int i = 0; i < size; i++)
{
dict->names[i] = new char[MAX_VARIABLE_NAME_LEN];
memset(dict->names[i], 0, MAX_VARIABLE_NAME_LEN);
}
return dict;
}

memory leak

Expression *strToExpr(char *str)
{
// malloc();
Expression *expr = createExpr();
// Expression *expr = new Expression;
expr->length = strlen(str);
// printf("len: %d\n", expr->length);
if (expr->length > EXPR_MAX_LEN)
{
printf("expression is longer than it can be\n");
}
int spacesCounter = 0;
for (int i = 0; i < expr->length; i++)
{
if (str[i] == ' ')
{
spacesCounter++;
}
}
// printf("spcs: %d\n", spacesCounter);
expr->symbols = new Symbol[expr->length - spacesCounter];
// printf("creating..\n");
int writeIndex = 0;
for (int symbolIndex = 0; symbolIndex < expr->length; symbolIndex++)
{
if (str[symbolIndex] != ' ')
{
expr->symbols[writeIndex] = charToSymbol(str[symbolIndex]);
// if (expr->symbols[writeIndex].type == ESymbolType::NOT_A_SYMBOL)
// {
// printf("%c is unknown symbol. it may cause problems.\n", str[symbolIndex]);
// }
writeIndex++;
}
}
// printf("spaces: %d\n", spacesCounter);
expr->length -= spacesCounter;
// printf("raw\n");
// print(expr);
// printf("\n");
expr = handleLetterSequences(expr);
// printf("letters\n");
// print(expr);
// printf("\n");
expr = handleNumberSequences(expr);
// printf("numbers\n");
// print(expr);
// printf("\n");
expr = handleUnaryMinus(expr);
expr = handleFunctions(expr);
return expr;
}

in lines 479-489 need to delete previous versions of expressions after passing it to some handlers

optimization

Expression *slice(Expression *expr, int a, int b)
{
Expression *result = new Expression;
result->length = b - a;
result->symbols = new Symbol[result->length];
int k = 0;
for (int i = a; i < b; i++)
{
result->symbols[k] = expr->symbols[i];
k++;
}
return result;
}

mb if result->length == expr->length then return expr?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.