view parse.yy @ 1:08df60a01bc1

debug flag, handle input with signal
author John W. Eaton <jwe@octave.org>
date Mon, 20 May 2019 13:45:58 -0400
parents dff751fb985c
children
line wrap: on
line source

// Infix notation calculator.

%{

#define YYSTYPE double

#include <iostream>

#include <cctype>
#include <cmath>
#include <cstdio>
#include <unistd.h>

#include "main.h"
#include "gui-main.h"
#include "tty-main.h"
#include "parse.h"

namespace interpreter
{
  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 *);

%}

%define api.push-pull push

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

%%

input   : // empty
          { }
        | input line
        | error
          {
            interpreter::debug_trace ("ABORT");
            YYABORT;
          }
        ;

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

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

%%

namespace interpreter
{
  // 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
  parser_fini (void)
  {
    if (ps)
      yypstate_delete (ps);

    ps = 0;
  }

  void parser_init (void)
  {
    parser_fini ();

    ps = yypstate_new ();
  }

  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 emit_result (double value)
  {
    // Simulate a delay in calculation.
    sleep (1);

    if (calc::tty_mode)
      tty::emit_result (value);
    else
      gui::emit_result (value);
  }

  void emit_error (const char *msg)
  {
    if (calc::tty_mode)
      tty::emit_error (msg);
    else
      gui::emit_error (msg);
  }

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

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