#include "calc.h" /* Tesses Calculator a PEMDAS calculator (with mod) Copyright (C) 2023 Mike Nolan This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ typedef struct node node_t; typedef enum { ADD, SUB, TIMES, DIVIDE, MOD, NEG, POW, NUMBER, PAREN } node_type_t; struct node { node_type_t type; union { struct { node_t* left; node_t* right; } two; node_t* node; double number; } data; }; void node_free(node_t* n) { if(n == NULL) { return; } switch(n->type) { case ADD: case SUB: case TIMES: case DIVIDE: case MOD: case POW: node_free(n->data.two.left); node_free(n->data.two.right); break; case PAREN: case NEG: node_free(n->data.node); break; default: break; } free(n); } typedef struct lextokenlist lextokenlist_t; typedef struct string string_t; struct string { int cap; int len; char* text; }; struct lextokenlist { string_t** tokens; int cap; int len; int i; }; string_t* string_create() { string_t* str = (string_t*)malloc(sizeof(string_t)); str->cap = 16; str->len = 0; str->text = (char*)calloc(str->cap + 1,sizeof(char)); return str; } void string_appendc(string_t* str,char c) { if(str->len >= str->cap) { str->cap = str->len+16; str->text = (char*)realloc(str,str->cap + 1); } str->text[str->len++] = c; str->text[str->len] = 0; } void string_free(string_t* str) { free(str->text); free(str); } lextokenlist_t* lextokenlist_create() { lextokenlist_t* lt = (lextokenlist_t*)malloc(sizeof(lextokenlist_t)); lt->cap = 16; lt->len = 0; lt->i=0; lt->tokens = (string_t**)calloc(lt->cap,sizeof(string_t*)); return lt; } void lextokenlist_flush(lextokenlist_t* ls,string_t** str) { string_t* str2 = *str; if(str2->len > 0) { if(ls->len >= ls->cap) { ls->cap = ls->len+16; ls->tokens = (string_t**)realloc(ls->tokens,ls->cap * sizeof(string_t*)); } ls->tokens[ls->len++] = str2; *str = string_create(); } } lextokenlist_t* calc_lex(const char* data) { string_t* str = string_create(); lextokenlist_t* ls=lextokenlist_create(); while(*data != '\0') { switch(*data) { case '+': case '-': case '*': case '/': case '%': case '^': case '(': case ')': lextokenlist_flush(ls,&str); string_appendc(str,*data); lextokenlist_flush(ls,&str); break; case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': case '.': string_appendc(str,*data); break; default: lextokenlist_flush(ls,&str); break; } data++; } lextokenlist_flush(ls,&str); return ls; } node_t* calc_parse_expression(lextokenlist_t* tokens); node_t* calc_parse_value(lextokenlist_t* tokens) { if(tokens->i>=tokens->len) { return NULL; } if(strncmp(tokens->tokens[tokens->i]->text,"-",(size_t)tokens->tokens[tokens->i]->text)==0) { tokens->i++; if(tokens->i>=tokens->len) { return NULL; } node_t* n = (node_t*)calloc(1,sizeof(node_t)); n->data.node = calc_parse_expression(tokens); n->type = NEG; return n; } else if(strncmp(tokens->tokens[tokens->i]->text,"(",(size_t)tokens->tokens[tokens->i]->text)==0) { tokens->i++; if(tokens->i>=tokens->len) { return NULL; } node_t* n = (node_t*)calloc(1,sizeof(node_t)); n->data.node = calc_parse_expression(tokens); n->type = PAREN; if(tokens->i>=tokens->len) { node_free(n); return NULL; } if(strncmp(tokens->tokens[tokens->i]->text,")",(size_t)tokens->tokens[tokens->i]->text)!=0) { node_free(n); return NULL; } tokens->i++; return n; } node_t* n = (node_t*)calloc(1,sizeof(node_t)); n->type = NUMBER; n->data.number = strtod(tokens->tokens[tokens->i]->text,NULL); tokens->i++; return n; } node_t* calc_parse_pow(lextokenlist_t* tokens) { node_t* node = calc_parse_value(tokens); if(node == NULL) return NULL; while(tokens->ilen && (strncmp(tokens->tokens[tokens->i]->text,"^",(size_t)tokens->tokens[tokens->i]->text)==0)) { if(tokens->ilen && strncmp(tokens->tokens[tokens->i]->text,"^",(size_t)tokens->tokens[tokens->i]->text)==0) { tokens->i++; node_t* left = node; node = (node_t*)calloc(1,sizeof(node_t)); node->type = POW; node->data.two.left = left; node->data.two.right = calc_parse_value(tokens); if(node->data.two.right == NULL) { free(node->data.two.left); free(node); return NULL; } } } return node; } node_t* calc_parse_factor(lextokenlist_t* tokens) { node_t* node = calc_parse_pow(tokens); if(node == NULL) return NULL; while(tokens->ilen && (strncmp(tokens->tokens[tokens->i]->text,"*",(size_t)tokens->tokens[tokens->i]->text)==0 || strncmp(tokens->tokens[tokens->i]->text,"/",(size_t)tokens->tokens[tokens->i]->text) == 0 || strncmp(tokens->tokens[tokens->i]->text,"%",(size_t)tokens->tokens[tokens->i]->text) == 0)) { if(tokens->ilen && strncmp(tokens->tokens[tokens->i]->text,"*",(size_t)tokens->tokens[tokens->i]->text)==0) { tokens->i++; node_t* left = node; node = (node_t*)calloc(1,sizeof(node_t)); node->type = TIMES; node->data.two.left = left; node->data.two.right = calc_parse_pow(tokens); if(node->data.two.right == NULL) { free(node->data.two.left); free(node); return NULL; } } if(tokens->ilen && strncmp(tokens->tokens[tokens->i]->text,"/",(size_t)tokens->tokens[tokens->i]->text)==0) { tokens->i++; node_t* left = node; node = (node_t*)calloc(1,sizeof(node_t)); node->type = DIVIDE; node->data.two.left = left; node->data.two.right = calc_parse_pow(tokens); if(node->data.two.right == NULL) { free(node->data.two.left); free(node); return NULL; } } if(tokens->ilen && strncmp(tokens->tokens[tokens->i]->text,"%",(size_t)tokens->tokens[tokens->i]->text)==0) { tokens->i++; node_t* left = node; node = (node_t*)calloc(1,sizeof(node_t)); node->type = MOD; node->data.two.left = left; node->data.two.right = calc_parse_pow(tokens); if(node->data.two.right == NULL) { free(node->data.two.left); free(node); return NULL; } } } return node; } node_t* calc_parse_expression(lextokenlist_t* tokens) { node_t* node = calc_parse_factor(tokens); if(node == NULL) return NULL; while(tokens->ilen && (strncmp(tokens->tokens[tokens->i]->text,"+",(size_t)tokens->tokens[tokens->i]->text)==0 || strncmp(tokens->tokens[tokens->i]->text,"-",(size_t)tokens->tokens[tokens->i]->text) == 0)) { if(tokens->ilen && strncmp(tokens->tokens[tokens->i]->text,"+",(size_t)tokens->tokens[tokens->i]->text)==0) { tokens->i++; node_t* left = node; node = (node_t*)calloc(1,sizeof(node_t)); node->type = ADD; node->data.two.left = left; node->data.two.right = calc_parse_factor(tokens); if(node->data.two.right == NULL) { node_free(node); return NULL; } } if(tokens->ilen && strncmp(tokens->tokens[tokens->i]->text,"-",(size_t)tokens->tokens[tokens->i]->text)==0) { tokens->i++; node_t* left = node; node = (node_t*)calloc(1,sizeof(node_t)); node->type = SUB; node->data.two.left = left; node->data.two.right = calc_parse_factor(tokens); if(node->data.two.right == NULL) { node_free(node); return NULL; } } } return node; } double calc_execute_expression(node_t* n,bool* error); double calc_execute_expression(node_t* n,bool* error) { if(n == NULL) return 0; if(error != NULL && *error) return 0; switch(n->type) { case ADD: return calc_execute_expression(n->data.two.left,error) + calc_execute_expression(n->data.two.right,error); case SUB: return calc_execute_expression(n->data.two.left,error) - calc_execute_expression(n->data.two.right,error); case TIMES: return calc_execute_expression(n->data.two.left,error) * calc_execute_expression(n->data.two.right,error); case DIVIDE: { double left=calc_execute_expression(n->data.two.left,error); double right = calc_execute_expression(n->data.two.right,error); if(right == 0){ if(error != NULL) *error = true; return 0;} return left / right; } // if(n->data.two.right) { if(error != NULL) *error = true; return 0;} case MOD: return fmod(calc_execute_expression(n->data.two.left,error),calc_execute_expression(n->data.two.right,error)); case POW: return pow(calc_execute_expression(n->data.two.left,error),calc_execute_expression(n->data.two.right,error)); case PAREN: return calc_execute_expression(n->data.node,error); case NEG: return -calc_execute_expression(n->data.node,error); case NUMBER: return n->data.number; } } double calc(const char* data,bool* error) { lextokenlist_t* tokens = calc_lex(data); node_t* expr= calc_parse_expression(tokens); double exprVal = calc_execute_expression(expr,error); if(expr == NULL) { if(error != NULL) *error=true; }else { node_free(expr); } if(tokens != NULL) { int i; for(i=0;ilen;i++) { string_free(tokens->tokens[i]); } free(tokens->tokens); free(tokens); } return exprVal; }