view parser.yy @ 11:b652a5528fb1

handle EOF on input; more misc refactoring
author John W. Eaton <jwe@octave.org>
date Thu, 23 May 2019 13:42:57 -0400
parents 822a2fe5bb51
children 894be158b32d
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 parser
{
  // For communication between the lexer and parser.
  size_t bufptr = 0;
  size_t chunk_size = 0;
  const char *buf;

  // Used to tell readline whether to display the prompt indicating
  // a continuation line.  Is there any way we could do this job
  // without a global variable?
  bool beg_of_stmt = true;

  static int yylex (void);
}

static void yyerror (char const *);

static void emit_error (const char *msg);
static void emit_result (double value);

%}

%define api.push-pull push

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

%%

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

line    :     ';'
          { }
        | exp ';'
          {
            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 parser
{
  // 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 (void)
  {
    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", &yylval, &chars_read);
        bufptr += chars_read;
        return NUM;
      }

    // Return a single char.
    return c;
  }

  // If we defined a parser object, the parser state could be a
  // member variable of that object.

  static yypstate *ps = 0;

  void init (void)
  {
    fini ();

    ps = yypstate_new ();
  }

  void fini (void)
  {
    if (ps)
      yypstate_delete (ps);

    ps = 0;
  }

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

    int status;

    do
      {
        ::yychar = yylex ();

        if (::yychar < 0)
          return -1;

        status = yypush_parse (ps);
      }
    while (status == YYPUSH_MORE);

    return -2;
  }
}

static void yyerror (char const *msg)
{
  emit_error (msg);
}

static void emit_error (const char *msg)
{
  calc::interpreter::the_interpreter->emit_error (msg);
}

static void emit_result (double value)
{
  calc::interpreter::the_interpreter->emit_result (value);
}