Last active
August 29, 2015 14:24
-
-
Save DataKinds/fc3c93a251f32570d86f to your computer and use it in GitHub Desktop.
C Math Parser
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <stdio.h> | |
#include <ctype.h> | |
#include <string.h> | |
#include <stdlib.h> | |
#include <gmp.h> | |
#define VERBOSE_ONLY if(isVerbose) | |
int isVerbose = 0; | |
int catorigizeChar(char chr) | |
{ | |
if ( isdigit( chr ) || chr == '.' ) //part of a number | |
{ | |
return 1; | |
} | |
else if ( chr == '+' || chr == '*' || chr == '/' ) //operator (- isn't included as it can be unary) | |
{ | |
return 2; | |
} | |
return 0; | |
} | |
int shouldBreak(char thisChar, char lastChar) | |
{ | |
int thisCharCat = catorigizeChar( thisChar ); | |
int lastCharCat = catorigizeChar( lastChar ); | |
if ( ( thisCharCat != lastCharCat ) || ( !thisCharCat || !lastCharCat ) ) //if they're unequal or both 0 split | |
{ | |
return 1; | |
} | |
return 0; | |
} | |
char* stripWhitespace(char* expression) | |
{ | |
int currentCharPointer; | |
int stringLength = 0; | |
char* outputString = malloc(1024); if ( !outputString ) abort(); | |
outputString[0] = '\0'; | |
for ( currentCharPointer = 0; expression[currentCharPointer] != '\0'; currentCharPointer++ ) | |
{ | |
if ( !isspace( expression[currentCharPointer] ) ) | |
{ | |
outputString[stringLength++] = expression[currentCharPointer]; | |
outputString[stringLength] = '\0'; | |
} | |
} | |
return outputString; | |
} | |
char* getSubExp(char* expression, int wantedPointer) | |
{ | |
int currentCharPointer = -1; | |
int currentPointer = -1; | |
char lastChar = '\0'; | |
char* outputString = malloc(1024); if ( !outputString ) abort(); | |
outputString[0] = '\0'; | |
while ( currentPointer != wantedPointer ) //seek until found | |
{ | |
lastChar = ( currentCharPointer == -1 ? '\0' : expression[currentCharPointer] ); | |
currentCharPointer++; | |
if ( shouldBreak( expression[currentCharPointer], lastChar ) ) | |
{ //if the last char is a digit and this one isn't and vice versa, or the last char is \0 | |
currentPointer++; | |
} | |
if ( currentPointer >= strlen( expression ) ) | |
{ | |
return "\0"; | |
} | |
} | |
while ( currentCharPointer < strlen( expression ) ) | |
{ //now that it is found | |
lastChar = expression[currentCharPointer]; | |
currentCharPointer++; | |
size_t stringLength = strlen( outputString ); | |
outputString[stringLength++] = lastChar; | |
outputString[stringLength] = '\0'; | |
if ( shouldBreak( expression[currentCharPointer], lastChar ) ) | |
{ | |
break; | |
} | |
} | |
return outputString; | |
} | |
int expressionLength(char* expression) | |
{ | |
int i = 0; | |
for (;;) | |
{ | |
if ( strcmp( getSubExp( expression, ++i ), "\0" ) == 0 ) | |
{ | |
break; | |
} | |
} | |
return i; | |
} | |
char* parseNumOpNum(char* one, int oneNeg, char* op, char* two, int twoNeg) | |
{ | |
printf("num: %c%s, op: %c, num: %c%s\n", ( oneNeg ? '+' : '-' ), one, op[0], ( twoNeg ? '+' : '-' ), two); | |
if ( oneNeg == 0 ) oneNeg = -1; | |
if ( twoNeg == 0 ) twoNeg = -1; | |
double numOne = oneNeg * atof( one ); | |
double numTwo = twoNeg * atof( two ); | |
int opNums; | |
char* finalNum = malloc( 64 ); | |
switch ( op[0] ) | |
{ | |
case '+':; opNums = numOne + numTwo; | |
break; | |
case '-':; opNums = numOne - numTwo; | |
break; | |
case '*':; opNums = numOne * numTwo; | |
break; | |
case '/':; opNums = numOne / numTwo; | |
break; | |
default: printf("unrecognized operator %c\n", op[0]); | |
abort(); | |
} | |
sprintf( finalNum, "%d", opNums ); | |
return finalNum; | |
} | |
char* parseOneIteration(char* expression) | |
{ | |
int substringIndex; | |
char* currentSubstring = malloc( 1024 ); if ( !currentSubstring ) abort(); | |
char* peek = malloc( 1024 ); if ( !peek ) abort(); | |
char* peekPeek = malloc( 1024 ); if ( !peekPeek ) abort(); | |
char* peekPeekPeek = malloc( 1024 ); if ( !peekPeekPeek ) abort(); | |
char* peekPeekPeekPeek = malloc( 1024 ); if ( !peekPeekPeekPeek ) abort(); | |
int appliedIteration = 0; //if the iteration was done | |
char* outputString = malloc( 1024 ); if ( !outputString ) abort(); | |
outputString[0] = '\0'; | |
for ( substringIndex = 0; substringIndex < expressionLength( expression ); substringIndex++ ) | |
{ | |
currentSubstring = getSubExp( expression, substringIndex ); | |
peek = getSubExp( expression, substringIndex + 1 ); | |
peekPeek = getSubExp( expression, substringIndex + 2 ); | |
peekPeekPeek = getSubExp( expression, substringIndex + 3 ); | |
peekPeekPeekPeek = getSubExp( expression, substringIndex + 4 ); | |
if ( ( strcmp( currentSubstring, "\0" ) == 0 ) ) { break; } | |
VERBOSE_ONLY printf("substring index: %i\n", substringIndex); | |
if ( !appliedIteration ) | |
{ | |
VERBOSE_ONLY printf("seeking: %s %s %s %s %s\n", currentSubstring, peek, peekPeek, peekPeekPeek, peekPeekPeekPeek); | |
if ( | |
( currentSubstring[0] == '(' ) && | |
( peek[0] == '-' ) && | |
isdigit( peekPeek[0] ) && | |
( peekPeekPeek[0] == ')' ) | |
) //check if it's a negative number in parens, this sends it into an infinite loop without this for some reason | |
{ | |
VERBOSE_ONLY printf("parsing paren neg num paren\n"); | |
appliedIteration = 1; | |
strcat( outputString, peek ); | |
strcat( outputString, peekPeek ); | |
substringIndex += 3; //consume the two parens, neg, and num | |
} | |
else if ( | |
( currentSubstring[0] == '(') && | |
isdigit( peek[0] ) && | |
( peekPeek[0] == ')') | |
) //symplify parens (paren, num, paren) | |
{ | |
VERBOSE_ONLY printf("parsing paren num paren\n"); | |
appliedIteration = 1; | |
strcat( outputString, peek ); | |
substringIndex += 2; //to account for the parens not being consumed | |
} | |
//PARSING OPS | |
else if ( | |
isdigit( currentSubstring[0] ) && | |
!isdigit( peek[0] ) && | |
isdigit( peekPeek[0] ) | |
) //simplify operations (num, symbol, num) | |
{ | |
VERBOSE_ONLY printf("parsing num op num\n"); | |
appliedIteration = 1; | |
char* finalNum = parseNumOpNum( currentSubstring, 1, peek, peekPeek, 1 ); | |
strcat( outputString, finalNum ); | |
substringIndex += 2; //account for + and second operand not being consumed | |
} | |
else if ( | |
currentSubstring[0] == '-' && | |
isdigit( peek[0] ) && | |
!isdigit( peekPeek[0] ) && | |
isdigit( peekPeekPeek[0] ) | |
) //simplify operations (neg, num, symbol, num) | |
{ | |
VERBOSE_ONLY printf("parsing neg num op num\n"); | |
appliedIteration = 1; | |
char* finalNum = parseNumOpNum( currentSubstring, 0, peek, peekPeek, 1 ); | |
strcat( outputString, finalNum ); | |
substringIndex += 3; //account for + and second operand not being consumed | |
} | |
else if ( | |
isdigit( currentSubstring[0] ) && | |
!isdigit( peek[0] ) && | |
( peekPeek[0] == '-' ) && | |
isdigit( peekPeekPeek[0] ) | |
) //simplify operations (neg, num, symbol, num) | |
{ | |
VERBOSE_ONLY printf("parsing neg num op num\n"); | |
appliedIteration = 1; | |
char* finalNum = parseNumOpNum( currentSubstring, 0, peek, peekPeekPeek, 1 ); | |
strcat( outputString, finalNum ); | |
substringIndex += 3; //account for + and second operand not being consumed | |
} | |
else if ( | |
( currentSubstring[0] == '-') && | |
isdigit( peek[0] ) && | |
!isdigit( peekPeek[0] ) && | |
( peekPeekPeek[0] == '-' ) && | |
isdigit( peekPeekPeekPeek[0] ) | |
) //simplify operations (neg, num, symbol, neg, num) | |
{ | |
VERBOSE_ONLY printf("parsing neg num op neg num\n"); | |
appliedIteration = 1; | |
char* finalNum = parseNumOpNum( peek, 0, peekPeek, peekPeekPeekPeek, 0 ); | |
strcat( outputString, finalNum ); | |
substringIndex += 4; //account for + and second operand not being consumed | |
} | |
//END PARSING OPS | |
else | |
{ | |
VERBOSE_ONLY printf("nothing to do, appending %s\n", currentSubstring); | |
strcat( outputString, currentSubstring ); | |
} | |
} | |
else | |
{ | |
VERBOSE_ONLY printf("done parsing this iteration, appending %s\n", currentSubstring); | |
strcat( outputString, currentSubstring ); | |
} | |
} | |
free( currentSubstring ); | |
//free( peek ); | |
//free( peekPeek ); | |
return outputString; | |
} | |
char* parseFully(char* expression) | |
{ | |
for ( ; expressionLength( expression ) > 2; ) //if it's two it must be a negative number | |
{ | |
VERBOSE_ONLY printf("parsing one iteration of: %s\n", expression); | |
expression = parseOneIteration( expression ); | |
} | |
return expression; | |
} | |
int passCount = 0; | |
int failCount = 0; | |
void test(char* expression, char* expectedResult) | |
{ | |
printf("testing expression: %s\n", expression); | |
char* parsedExp = parseFully( stripWhitespace( expression ) ); | |
printf("expected result: %s\n", expectedResult); | |
printf("actual result: %s\n", parsedExp); | |
if ( strcmp( expectedResult, parsedExp ) == 0 ) | |
{ | |
printf("PASSED"); passCount++; | |
} | |
else | |
{ | |
printf("FAILED"); failCount++; | |
} | |
printf("\n\n"); | |
} | |
void testStats() | |
{ | |
printf("%d tests passed, %d test failed", passCount, failCount); | |
} | |
int main(int argc, char* argv[]) | |
{ | |
int currentArg; | |
for ( currentArg = 0; currentArg < argc; currentArg++ ) | |
{ | |
if ( strcmp( argv[currentArg], "-v" ) == 0 ) isVerbose = 1; | |
} | |
int mode = 2; //1 = REPL, 2 = test | |
if ( mode == 1 ) | |
{ | |
char expressionString[1024]; | |
fgets( expressionString, 1024, stdin ); | |
VERBOSE_ONLY printf( "parses to: " ); | |
int i; | |
for ( i = 0; i < expressionLength( stripWhitespace( expressionString ) ); i++ ) | |
{ | |
VERBOSE_ONLY printf( "%s, ", getSubExp( stripWhitespace( expressionString ), i ) ); | |
} | |
VERBOSE_ONLY printf("\n\nbegin parse output:\n"); | |
VERBOSE_ONLY printf( "answer: %s\n\n", parseFully( stripWhitespace( expressionString ) ) ); | |
if ( !isVerbose ) printf( "%s\n", parseFully( stripWhitespace( expressionString ) ) ); | |
} | |
else if ( mode == 2 ) | |
{ | |
test("2+2", "4"); | |
test("(((((((1))))+1)))", "2"); | |
test("2+(3*2)", "8"); | |
test("(1 - (4* 2))", "-7"); | |
test("(((-4)))", "-4"); | |
test("(-6)*(-3)", "18"); | |
testStats(); | |
} | |
return 0; | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
testing expression: 2+2 | |
parsing one iteration of: 2+2 | |
substring index: 0 | |
seeking: 2 + 2 | |
parsing num op num | |
num: +2, op: +, num: +2 | |
expected result: 4 | |
actual result: 4 | |
PASSED | |
testing expression: (((((((1))))+1))) | |
parsing one iteration of: (((((((1))))+1))) | |
substring index: 0 | |
seeking: ( ( ( ( ( | |
nothing to do, appending ( | |
substring index: 1 | |
seeking: ( ( ( ( ( | |
nothing to do, appending ( | |
substring index: 2 | |
seeking: ( ( ( ( ( | |
nothing to do, appending ( | |
substring index: 3 | |
seeking: ( ( ( ( 1 | |
nothing to do, appending ( | |
substring index: 4 | |
seeking: ( ( ( 1 ) | |
nothing to do, appending ( | |
substring index: 5 | |
seeking: ( ( 1 ) ) | |
nothing to do, appending ( | |
substring index: 6 | |
seeking: ( 1 ) ) ) | |
parsing paren num paren | |
substring index: 9 | |
done parsing this iteration, appending ) | |
substring index: 10 | |
done parsing this iteration, appending ) | |
substring index: 11 | |
done parsing this iteration, appending ) | |
substring index: 12 | |
done parsing this iteration, appending + | |
substring index: 13 | |
done parsing this iteration, appending 1 | |
substring index: 14 | |
done parsing this iteration, appending ) | |
substring index: 15 | |
done parsing this iteration, appending ) | |
substring index: 16 | |
done parsing this iteration, appending ) | |
parsing one iteration of: ((((((1)))+1))) | |
substring index: 0 | |
seeking: ( ( ( ( ( | |
nothing to do, appending ( | |
substring index: 1 | |
seeking: ( ( ( ( ( | |
nothing to do, appending ( | |
substring index: 2 | |
seeking: ( ( ( ( 1 | |
nothing to do, appending ( | |
substring index: 3 | |
seeking: ( ( ( 1 ) | |
nothing to do, appending ( | |
substring index: 4 | |
seeking: ( ( 1 ) ) | |
nothing to do, appending ( | |
substring index: 5 | |
seeking: ( 1 ) ) ) | |
parsing paren num paren | |
substring index: 8 | |
done parsing this iteration, appending ) | |
substring index: 9 | |
done parsing this iteration, appending ) | |
substring index: 10 | |
done parsing this iteration, appending + | |
substring index: 11 | |
done parsing this iteration, appending 1 | |
substring index: 12 | |
done parsing this iteration, appending ) | |
substring index: 13 | |
done parsing this iteration, appending ) | |
substring index: 14 | |
done parsing this iteration, appending ) | |
parsing one iteration of: (((((1))+1))) | |
substring index: 0 | |
seeking: ( ( ( ( ( | |
nothing to do, appending ( | |
substring index: 1 | |
seeking: ( ( ( ( 1 | |
nothing to do, appending ( | |
substring index: 2 | |
seeking: ( ( ( 1 ) | |
nothing to do, appending ( | |
substring index: 3 | |
seeking: ( ( 1 ) ) | |
nothing to do, appending ( | |
substring index: 4 | |
seeking: ( 1 ) ) + | |
parsing paren num paren | |
substring index: 7 | |
done parsing this iteration, appending ) | |
substring index: 8 | |
done parsing this iteration, appending + | |
substring index: 9 | |
done parsing this iteration, appending 1 | |
substring index: 10 | |
done parsing this iteration, appending ) | |
substring index: 11 | |
done parsing this iteration, appending ) | |
substring index: 12 | |
done parsing this iteration, appending ) | |
parsing one iteration of: ((((1)+1))) | |
substring index: 0 | |
seeking: ( ( ( ( 1 | |
nothing to do, appending ( | |
substring index: 1 | |
seeking: ( ( ( 1 ) | |
nothing to do, appending ( | |
substring index: 2 | |
seeking: ( ( 1 ) + | |
nothing to do, appending ( | |
substring index: 3 | |
seeking: ( 1 ) + 1 | |
parsing paren num paren | |
substring index: 6 | |
done parsing this iteration, appending + | |
substring index: 7 | |
done parsing this iteration, appending 1 | |
substring index: 8 | |
done parsing this iteration, appending ) | |
substring index: 9 | |
done parsing this iteration, appending ) | |
substring index: 10 | |
done parsing this iteration, appending ) | |
parsing one iteration of: (((1+1))) | |
substring index: 0 | |
seeking: ( ( ( 1 + | |
nothing to do, appending ( | |
substring index: 1 | |
seeking: ( ( 1 + 1 | |
nothing to do, appending ( | |
substring index: 2 | |
seeking: ( 1 + 1 ) | |
nothing to do, appending ( | |
substring index: 3 | |
seeking: 1 + 1 ) ) | |
parsing num op num | |
num: +1, op: +, num: +1 | |
substring index: 6 | |
done parsing this iteration, appending ) | |
substring index: 7 | |
done parsing this iteration, appending ) | |
substring index: 8 | |
done parsing this iteration, appending ) | |
parsing one iteration of: (((2))) | |
substring index: 0 | |
seeking: ( ( ( 2 ) | |
nothing to do, appending ( | |
substring index: 1 | |
seeking: ( ( 2 ) ) | |
nothing to do, appending ( | |
substring index: 2 | |
seeking: ( 2 ) ) ) | |
parsing paren num paren | |
substring index: 5 | |
done parsing this iteration, appending ) | |
substring index: 6 | |
done parsing this iteration, appending ) | |
parsing one iteration of: ((2)) | |
substring index: 0 | |
seeking: ( ( 2 ) ) | |
nothing to do, appending ( | |
substring index: 1 | |
seeking: ( 2 ) ) | |
parsing paren num paren | |
substring index: 4 | |
done parsing this iteration, appending ) | |
parsing one iteration of: (2) | |
substring index: 0 | |
seeking: ( 2 ) | |
parsing paren num paren | |
expected result: 2 | |
actual result: 2 | |
PASSED | |
testing expression: 2+(3*2) | |
parsing one iteration of: 2+(3*2) | |
substring index: 0 | |
seeking: 2 + ( 3 * | |
nothing to do, appending 2 | |
substring index: 1 | |
seeking: + ( 3 * 2 | |
nothing to do, appending + | |
substring index: 2 | |
seeking: ( 3 * 2 ) | |
nothing to do, appending ( | |
substring index: 3 | |
seeking: 3 * 2 ) | |
parsing num op num | |
num: +3, op: *, num: +2 | |
substring index: 6 | |
done parsing this iteration, appending ) | |
parsing one iteration of: 2+(6) | |
substring index: 0 | |
seeking: 2 + ( 6 ) | |
nothing to do, appending 2 | |
substring index: 1 | |
seeking: + ( 6 ) | |
nothing to do, appending + | |
substring index: 2 | |
seeking: ( 6 ) | |
parsing paren num paren | |
parsing one iteration of: 2+6 | |
substring index: 0 | |
seeking: 2 + 6 | |
parsing num op num | |
num: +2, op: +, num: +6 | |
expected result: 8 | |
actual result: 8 | |
PASSED | |
testing expression: (1 - (4* 2)) | |
parsing one iteration of: (1-(4*2)) | |
substring index: 0 | |
seeking: ( 1 - ( 4 | |
nothing to do, appending ( | |
substring index: 1 | |
seeking: 1 - ( 4 * | |
nothing to do, appending 1 | |
substring index: 2 | |
seeking: - ( 4 * 2 | |
nothing to do, appending - | |
substring index: 3 | |
seeking: ( 4 * 2 ) | |
nothing to do, appending ( | |
substring index: 4 | |
seeking: 4 * 2 ) ) | |
parsing num op num | |
num: +4, op: *, num: +2 | |
substring index: 7 | |
done parsing this iteration, appending ) | |
substring index: 8 | |
done parsing this iteration, appending ) | |
parsing one iteration of: (1-(8)) | |
substring index: 0 | |
seeking: ( 1 - ( 8 | |
nothing to do, appending ( | |
substring index: 1 | |
seeking: 1 - ( 8 ) | |
nothing to do, appending 1 | |
substring index: 2 | |
seeking: - ( 8 ) ) | |
nothing to do, appending - | |
substring index: 3 | |
seeking: ( 8 ) ) | |
parsing paren num paren | |
substring index: 6 | |
done parsing this iteration, appending ) | |
parsing one iteration of: (1-8) | |
substring index: 0 | |
seeking: ( 1 - 8 ) | |
nothing to do, appending ( | |
substring index: 1 | |
seeking: 1 - 8 ) | |
parsing num op num | |
num: +1, op: -, num: +8 | |
substring index: 4 | |
done parsing this iteration, appending ) | |
parsing one iteration of: (-7) | |
substring index: 0 | |
seeking: ( - 7 ) | |
parsing paren neg num paren | |
expected result: -7 | |
actual result: -7 | |
PASSED | |
testing expression: (((-4))) | |
parsing one iteration of: (((-4))) | |
substring index: 0 | |
seeking: ( ( ( - 4 | |
nothing to do, appending ( | |
substring index: 1 | |
seeking: ( ( - 4 ) | |
nothing to do, appending ( | |
substring index: 2 | |
seeking: ( - 4 ) ) | |
parsing paren neg num paren | |
substring index: 6 | |
done parsing this iteration, appending ) | |
substring index: 7 | |
done parsing this iteration, appending ) | |
parsing one iteration of: ((-4)) | |
substring index: 0 | |
seeking: ( ( - 4 ) | |
nothing to do, appending ( | |
substring index: 1 | |
seeking: ( - 4 ) ) | |
parsing paren neg num paren | |
substring index: 5 | |
done parsing this iteration, appending ) | |
parsing one iteration of: (-4) | |
substring index: 0 | |
seeking: ( - 4 ) | |
parsing paren neg num paren | |
expected result: -4 | |
actual result: -4 | |
PASSED | |
testing expression: (-6)*(-3) | |
parsing one iteration of: (-6)*(-3) | |
substring index: 0 | |
seeking: ( - 6 ) * | |
parsing paren neg num paren | |
substring index: 4 | |
done parsing this iteration, appending * | |
substring index: 5 | |
done parsing this iteration, appending ( | |
substring index: 6 | |
done parsing this iteration, appending - | |
substring index: 7 | |
done parsing this iteration, appending 3 | |
substring index: 8 | |
done parsing this iteration, appending ) | |
parsing one iteration of: -6*(-3) | |
substring index: 0 | |
seeking: - 6 * ( - | |
nothing to do, appending - | |
substring index: 1 | |
seeking: 6 * ( - 3 | |
nothing to do, appending 6 | |
substring index: 2 | |
seeking: * ( - 3 ) | |
nothing to do, appending * | |
substring index: 3 | |
seeking: ( - 3 ) | |
parsing paren neg num paren | |
parsing one iteration of: -6*-3 | |
substring index: 0 | |
seeking: - 6 * - 3 | |
parsing neg num op neg num | |
num: -6, op: *, num: -3 | |
expected result: 18 | |
actual result: 18 | |
PASSED | |
6 tests passed, 0 test failed |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment