view parser.yy @ 12:894be158b32d

define parser as a class and eliminate some global variables
author John W. Eaton <jwe@octave.org>
date Thu, 23 May 2019 17:57:20 -0400
parents b652a5528fb1
children d179b0bb85e4
line wrap: on
line source

// Infix notation calculator.

%{

#define YYSTYPE double

#include <iostream>
#include <string>

#include <cctype>
#include <cmath>
#include <cstdio>

#include "interpreter.h"
#include "parser.h"

namespace calc
{
  static void *create_parser_state (void);
  static void delete_parser_state (void *);

  parser::parser (interpreter& interp)
    : m_interpreter (interp),
      m_parser_state (create_parser_state ()),
      m_beg_of_stmt (true)
  { }

  parser::~parser (void)
  {
    delete_parser_state (m_parser_state);
  }

  void parser::emit_error (const char *msg) const
  {
    m_interpreter.emit_error (msg);
  }

  void parser::emit_result (double value) const
  {
    m_interpreter.emit_result (value);
  }

  // For communication between the lexer and parser.
  size_t bufptr = 0;
  size_t chunk_size = 0;
  const char *buf;

  static int yylex (YYSTYPE& token_value);
}

static void yyerror (calc::parser&, char const *);

%}

%define api.pure full
%define api.push-pull push

%parse-param {calc::parser& parser}

// Bison declarations.
%token NUM
%left '-' '+'
%left '*' '/'
%left NEG     // negation--unary minus
%right '^'    // exponentiation

%%

input   : // empty
          { }
        | input line
        | error
          {
            YYABORT;
          }
        ;

line    :     ';'
          { }
        | exp ';'
          {
            parser.emit_result ($1);
            parser.beg_of_stmt (true);
          }
        ;

exp     : NUM
          {
            $$ = $1;
            parser.beg_of_stmt (false);
          }
        | exp '+' exp
          {
            $$ = $1 + $3;
            parser.beg_of_stmt (false);
          }
        | exp '-' exp
          {
            $$ = $1 - $3;
            parser.beg_of_stmt (false);
          }
        | exp '*' exp
          {
            $$ = $1 * $3;
            parser.beg_of_stmt (false);
          }
        | exp '/' exp
          {
            $$ = $1 / $3;
            parser.beg_of_stmt (false);
          }
        | '-' exp  %prec NEG
          {
            $$ = -$2;
            parser.beg_of_stmt (false);
          }
        | exp '^' exp
          {
            $$ = std::pow ($1, $3);
            parser.beg_of_stmt (false);
          }
        | '(' exp ')'
          {
            $$ = $2;
            parser.beg_of_stmt (false);
          }
        ;

%%

namespace calc
{
  // The lexical analyzer returns a double floating point number on the
  // stack and the token NUM, or the numeric code of the character read
  // if not a number.  It skips all blanks and tabs, and returns -1 for
  // end-of-input.

  static int yylex (YYSTYPE& token_value)
  {
    int c;

    if (bufptr >= chunk_size)
      return -1;

    // Skip white space.
    while ((c = buf[bufptr++]) == ' ' || c == '\t' || c == '\n')
      ;

    // Process numbers.
    if (c == '.' || isdigit (c))
      {
        int chars_read = 0;
        bufptr--;
        sscanf (&buf[bufptr], "%lf%n", &token_value, &chars_read);
        bufptr += chars_read;
        return NUM;
      }

    // Return a single char.
    return c;
  }

  int parser::parse_and_execute (const std::string& line)
  {
    bufptr = 0;
    chunk_size = line.length ();
    buf = line.c_str ();

    int status;

    do
      {
        YYSTYPE token_value;
        int input_char = yylex (token_value);

        if (input_char < 0)
          return -1;

        status = yypush_parse (static_cast<yypstate *> (m_parser_state),
                               input_char, &token_value, *this);
      }
    while (status == YYPUSH_MORE);

    return -2;
  }

  static void *create_parser_state (void)
  {
    return yypstate_new ();
  }

  static void delete_parser_state (void *parser_state)
  {
    return yypstate_delete (static_cast<yypstate *> (parser_state));
  }
}

static void yyerror (calc::parser& parser, char const *msg)
{
  parser.emit_error (msg);
}