Skip to content

Example: Advanced Calculator

Ronald Franco edited this page Dec 21, 2018 · 1 revision

This example builds upon the original calculator example by allowing the use of exponents, logarithms, and the basic trigonometric functions sin, cos, and tan. This example also makes use of a symbol table to store symbols and their corresponding values for reference in mathematical expressions. Note, the Flex specification was altered to tokenize identifiers for the symbol table and keywords associated with the newly supported math functions. The AFG used is similar to that of the original calculator example but has been altered to adjust for the new features.

Flex Specification:

%{
#define ID 1
#define NUM 2
#define LOG 3
#define SIN 4
#define COS 5
#define TAN 6
%}

num    [0-9]*([0-9]|\.[0-9]|[0-9]\.)[0-9]*
id     [a-zA-Z]+

%option noyywrap

%%
[-+*/=()^;\n]   { return *yytext; }
{num}           { return NUM; }
log             { return LOG; }
sin             { return SIN; }
cos             { return COS; }
tan             { return TAN; }
{id}            { return ID; }
.               /* do nothing  */   
%%

Driver Program:

#include <iostream>
#include <map>
#include <string>
#include <cmath>

#include "parser.h"
#include "flextokenizer.h"
#include "tokenstream.h"

typedef std::map<std::string,float> SymbolTable;

TokenStream<std::map<std::string,float>>& operator>>(TokenStream<std::map<std::string,float>> & in, float& out)
{
  /* check if symbol */
  if (in.get_code() == 1)
  {
    /* check if symbol exists in symbol table */
    if (in.get_in()->find(in.get_text()) != in.get_in()->end())
      out = (*in.get_in())[in.get_text()];
    else
      throw parsing_error("Failed to find variable in symbol table");
  }
  else
    throw std::logic_error("Error in lexer specification");
   
  return in;
}

int main()
{
  /* advanced calculator example */
  
  /* declare symbol table */
  SymbolTable sym;

  /* declare (non)terminals */
  Parser<float> line, input, expr, fact, term,
    num(2), log(3), sin(4), cos(5), tan(6);

  Parser<std::string,std::string> new_id(1);
  Parser<std::map<std::string,float>,float> id(1);

  /* declare flow variables */
  float a(0), b(0);
  std::string c;
  
  /* AFG */
  line>>a = input>>a & '\n';

  input>>a = *(new_id>>c & '=' & expr>>a & ';' & [&]{ sym[c] = a; })
      & expr>>a & ';';

  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 & ')'
      | log & '(' & expr>>a & ')' & [&]{ a = std::log(a); }
      | sin & '(' & expr>>a & ')' & [&]{ a = std::sin(a); }
      | cos & '(' & expr>>a & ')' & [&]{ a = std::cos(a); }
      | tan & '(' & expr>>a & ')' & [&]{ a = std::tan(a); }
      | id(sym)>>a
      | num>>a
    ) & -( '^' & fact>>b & [&]{ a = std::pow(a,b); });

  /* 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 symbols\n\t- +, -, *, /";
    std::cout << "\n\t- parenthesis\n\t- natural log\n\t- exponents";
    std::cout << "\n\t- sin, cos, tan";
    std::cout << "\nFormat: <optional_declarations> <final_expression>";
    std::cout << "\nExamples:";
    std::cout << "\n\t1) 5 + 6;\n\t2) x = 5 + 3; y = 6; x * y - 2;\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 successfully!\nResult: " << a << "\n" << std::endl;
    }
    else
    {
      std::cout << "Expression computation failed\n" << std::endl;
    }

    /* clear input */
    tokens.clear();
    pos = 0;
  }

  return 0; 
}