view parser.yy @ 6:1b575145197e

interpreter is now a class instead of a namespace with functions
author John W. Eaton <jwe@octave.org>
date Thu, 23 May 2019 07:41:18 -0400
parents 0e154787183d
children 822a2fe5bb51
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 "main.h"
#include "gui-main.h"
#include "tty-main.h"
#include "parser.h"

namespace parser
{
  FILE *outfile = 0;
  size_t bufptr = 0;
  size_t chunk_size = 0;

  const char *buf;
  bool beg_of_stmt = true;

  static int yylex (void);

  static void debug_trace (const char *);
}

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
          {
            parser::debug_trace ("ABORT");
            YYABORT;
          }
        ;

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

exp     : NUM
          {
            parser::debug_trace ("NUM");
            $$ = $1;
            parser::beg_of_stmt = false;
          }
        | exp '+' exp
          {
            parser::debug_trace ("ADD");
            $$ = $1 + $3;
            parser::beg_of_stmt = false;
          }
        | exp '-' exp
          {
            parser::debug_trace ("SUB");
            $$ = $1 - $3;
            parser::beg_of_stmt = false;
          }
        | exp '*' exp
          {
            parser::debug_trace ("MUL");
            $$ = $1 * $3;
            parser::beg_of_stmt = false;
          }
        | exp '/' exp
          {
            parser::debug_trace ("DIV");
            $$ = $1 / $3;
            parser::beg_of_stmt = false;
          }
        | '-' exp  %prec NEG
          {
            parser::debug_trace ("NEG");
            $$ = -$2;
            parser::beg_of_stmt = false;
          }
        | exp '^' exp
          {
            parser::debug_trace ("EXP");
            $$ = std::pow ($1, $3);
            parser::beg_of_stmt = false;
          }
        | '(' exp ')'
          {
            parser::debug_trace ("PAREN");
            $$ = $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;
  }

  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;
  }

  void debug_trace (const char *msg)
  {
    if (calc::debug_mode)
      std::cerr << msg << std::endl;
  }
}

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);
}