Skip to content

Instantly share code, notes, and snippets.

@DataKinds
Last active August 29, 2015 14:24
Show Gist options
  • Save DataKinds/fc3c93a251f32570d86f to your computer and use it in GitHub Desktop.
Save DataKinds/fc3c93a251f32570d86f to your computer and use it in GitHub Desktop.
C Math Parser
#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;
}
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