-
Notifications
You must be signed in to change notification settings - Fork 2
Example: Calculator with Complex Numbers
Ronald Franco edited this page Dec 21, 2018
·
1 revision
This example exemplifies the ability to use flow variables that are not of a primitive type, in this case complex numbers. The example employs the FlexTokenizer class to tokenize input. To enable flow variables to be complex numbers, a ComplexNum class was created. The example overrides the insertion operator of the TokenStream class to extract and store the semantic value of a given token, representing a complex number, in an out-flow variable properly.
The ComplexNum class overrides the arithmetic operators and they are used on ComplexNum flow variables in the semantic actions of the AFG. The result is a clean looking AFG that isn’t cluttered with complicated semantic actions.
Flex Specification:
%{
#define NUM 2
%}
num [0-9]*([0-9]|\.[0-9]|[0-9]\.)[0-9]*
%option noyywrap
%%
{num}j? { return NUM; }
[-+*/=()\n] { return *yytext; }
. /* do nothing */
%%
Driver Program:
#include <iostream>
#include <cstring>
#include <cmath>
#include <sstream>
#include "parser.h"
#include "flextokenizer.h"
#include "tokenstream.h"
#define NUM_TOK_CODE 2
class ComplexNum {
friend std::ostream& operator<<(std::ostream & out, ComplexNum a)
{
out << a.real;
if (a.imag < 0)
out << " - ";
else if (a.imag > 0)
out << " + ";
if (a.imag != 0)
out << std::abs(a.imag) << "j";
return out;
}
public:
/* constructors */
ComplexNum()
:
real(0),
imag(0)
{ }
ComplexNum(float r, float i = 0)
:
real(r),
imag(i)
{ }
/* arithmetic operator overloads */
ComplexNum operator*(ComplexNum & arg)
{
ComplexNum result;
result.real = (real * arg.real) + (-1 * imag * arg.imag);
result.imag = (real * arg.imag) + (imag * arg.real);
return result;
}
ComplexNum operator*=(ComplexNum & arg)
{
*this = *this * arg;
return *this;
}
ComplexNum operator/(ComplexNum & arg)
{
ComplexNum result;
result.real = (real * arg.real + imag * arg.imag)
/ (arg.real * arg.real + arg.imag * arg.imag * -1);
result.imag = (imag * arg.real - real * arg.imag)
/ (arg.real * arg.real + arg.imag * arg.imag * -1);
return result;
}
ComplexNum operator/=(ComplexNum & arg)
{
*this = *this / arg;
return *this;
}
ComplexNum operator+(ComplexNum & arg)
{
ComplexNum result;
result.real = real + arg.real;
result.imag = imag + arg.imag;
return result;
}
ComplexNum operator+=(ComplexNum & arg)
{
*this = *this + arg;
return *this;
}
ComplexNum operator-(ComplexNum & arg)
{
ComplexNum result;
result.real = real - arg.real;
result.imag = imag - arg.imag;
return result;
}
ComplexNum operator-=(ComplexNum & arg)
{
*this = *this - arg;
return *this;
}
/* member data */
double real;
double imag;
};
/* overload extraction operator of TokenStream class to allow the semantic
* values of tokens to be properly stored into ComplexNum flow variables */
TokenStream<ComplexNum> & operator>>(TokenStream<ComplexNum> & is, ComplexNum & out)
{
std::string tok = is.get_text();
if (tok.back() == 'j')
{
/* token is imaginary */
tok.pop_back();
std::stringstream tok_stream(tok);
out.real = 0;
tok_stream >> out.imag;
} else {
/* token is real */
std::stringstream tok_stream(tok);
tok_stream >> out.real;
out.imag = 0;
}
return is;
}
int main()
{
/* complex number calculator example */
/* create nonterminals line, expr, fact, and term. create terminal num */
Parser<ComplexNum> line, expr, fact, term, num(NUM_TOK_CODE);
/* create ComplexNum flow variables */
ComplexNum a, b;
/* AFG */
line>>a = expr>>a & '\n';
expr>>a = term>>a & *( '+' & term>>b & [&]{ a += b; }
| '-' & term>>b & [&]{ a -= b; });
term>>a = fact>>a & *( '*' & fact>>b & [&]{ a *= b; }
| '/' & fact>>b & [&]{ a /= b; } );
fact>>a = '(' & expr>>a & ')' | num>>a;
/* FlexTokenizer will use stdin */
FlexTokenizer tokens;
/* maintain current position */
size_t pos = 0;
while (true)
{
/* user prompt */
std::cout << "============================================================";
std::cout << "\nA calculator that supports:";
std::cout << "\n\t- integers, doubles, and complex numbers";
std::cout << "\n\t- +, -, *, /";
std::cout << "\n\t- parenthesis";
std::cout << "\nFormat: <expression>";
std::cout << "\nExamples:\n\t1) 4 + 3j * 6\n\t2) (5 + 2j) * (2 - 3j)\n";
std::cout << "============================================================";
std::cout << "\nGive me a mathematical expression.";
std::cout << "\nCtrl + C to quit: ";
/* begin parsing */
if (line.parse(&tokens,&pos))
{
std::cout << "Expression computed succesfully!\nResult: " << a << "\n" << std::endl;
a = ComplexNum();
}
else
{
std::cout << "Expression computation failed\n" << std::endl;
}
/* clear input */
tokens.clear();
pos = 0;
}
return 0;
}