Skip to content

Example: Calculator Flow Variables

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

Let us modify the previous example to use more flow variables. More specifically, we’ll use adjust the previous example’s AFG to require inherited attributes, or in this case in-flow variables. The example employs a parser that parses the following grammar:

<E>	::= <T> <TT>	{ TT.st := T.val; E.val := TT.val }
<TT1>	::= + <T> <TT2>	{ TT2.st := TT1.st + T.val; TT1.val := TT2.val }
<TT1>	::= - <T> <TT2>	{ TT2.st := TT1.st - T.val; TT1.val := TT2.val }
<TT>	::= ε		{ TT.val := TT.st }
<T>	::= <F> <FT>	{ FT.st := F.val; T.val := FT.val }
<FT1>	::= * <F> <FT2>	{ FT2.st := FT1.st * F.val; FT1.val := FT2.val }
<FT1>	::= / <F> <FT2>	{ FT2.st := FT1.st / F.val; FT1.val := FT2.val }
<FT>	::= ε		{ FT.val := FT.st }
<F1>	::= - <F2>	{ F1.val := -F2.val }
<F>	::= { <E> )	{ F.val := E.val }
<F>	::= unsigned_int{ F.val := unsigned_int.val }

Where st is an inherited attribute and val is a synthesized attribute. The Flex specification is the same as the previous example's. Notice the use of flow variables in the AFG implement some of the above semantic rules, like “TT.st := T.val” and “TT.val := TT.st”, automatically.

Driver Program:

#include <iostream>

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

int main()
{
  /* calculator w/ in-flow variables example */
  
  /* define tokens */
  Parser<> plus('+'), minus('-'), times('*'), divides('/'), num(2);
  
  /* define nonterminals */
  Parser<int> line, expr, fact, fact_tail, term, term_tail;

  /* define flow variables */
  int a(0), b(0), c(0), d(0);

  /* AFG */
  line>>a = expr>>a & '\n';

  expr>>a = term>>a & term_tail(a)>>a;
  
  term_tail(b)>>b = -('+' & term>>c & [&]{ d = b + c; } & term_tail(d)>>b 
      | '-' & term>>c & [&]{ d = b - c; } & term_tail(d)>>b);
  
  term>>a = fact>>a & fact_tail(a)>>a;
  
  fact_tail(b)>>b = -('*' & fact>>c & [&]{ d = b * c; } & fact_tail(d)>>b 
      | '/' & fact>>c & [&]{ d = b / c; } & fact_tail(d)>>b);
  
  fact>>a = num>>a | '(' & expr>>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 and doubles";
    std::cout << "\n\t- +, -, *, /";
    std::cout << "\n\t- parenthesis";
    std::cout << "\nFormat: <arithmetic expression>";
    std::cout << "\nExamples:\n\t1) 4 + 3 * 2\n\t2) (7 - 2) / 5\n";
    std::cout << "============================================================";

    std::cout << "\nGive me a mathematical expression.";
    std::cout << "\nCtrl + C to quit: ";

    /* if we can parse sucessfully starting at position pos,
     * print result, i.e. the output flow variable of the starting
     * nonterminal */
    if (line.parse(&tokens,&pos))
    {
      std::cout << "Expression computed succesfully!\nResult: " << a << "\n" << std::endl;
    }
    else
    {
      std::cout << "Expression computation failed\n" << std::endl;
    }
    tokens.clear();
    pos = 0;
  }

  return 0;
}
Clone this wiki locally