/* Copyright (C) 2021 Free Software Foundation, Inc.
   Contributed by Oracle.

   This file is part of GNU Binutils.

   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation; either version 3, or (at your option)
   any later version.

   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.

   You should have received a copy of the GNU General Public License
   along with this program; if not, write to the Free Software
   Foundation, 51 Franklin Street - Fifth Floor, Boston,
   MA 02110-1301, USA.  */

// To rebuild QLParser.tab.cc and QLParser.tab.hh, use bison 3.6 or newer:
// cd gprofng/src && bison QLParser.yy

// For "api.parser.class"
%require "3.0"
%language "C++"

%code top {
#include <stdio.h>
#include <string.h>
#include <string>
}
%code requires {
#include "QLParser.h"
#include "DbeSession.h"
#include "Expression.h"
#include "Table.h"
#include "i18n.h"
}

%code
{
namespace QL
{
  static QL::Parser::symbol_type yylex (QL::Result &result);

  static Expression *
  processName (std::string str)
  {
    const char *name = str.c_str();
    int propID = dbeSession->getPropIdByName (name);
    if (propID != PROP_NONE)
      return new Expression (Expression::OP_NAME,
		      new Expression (Expression::OP_NUM, (uint64_t) propID));

    // If a name is not statically known try user defined objects
    Expression *expr = dbeSession->findObjDefByName (name);
    if (expr != NULL)
      return expr->copy();

    throw Parser::syntax_error ("Name not found");
  }
}
}

%defines
%define api.namespace {QL}
// in Bison 3.3, use %define api.parser.class {Parser} instead parser_class_name
%define parser_class_name {Parser}
%define api.token.constructor
%define api.value.type variant
// Later: api.value.automove
%define api.token.prefix {L_}
%define parse.assert
%param {QL::Result &result}

%start S

%token LPAR "("
  RPAR ")"
  HASPROP
  FILEIOVFD

%token YYEOF 0
%token <uint64_t> NUM FNAME JGROUP JPARENT QSTR
%token <std::string> NAME

%nonassoc IN SOME ORDR
%left  COMMA ","
%right QWE "?"
       COLON ":"
%left  AND "&&"
       OR "|"
       EQV NEQV
       BITAND BITOR
       BITXOR "^"
%nonassoc EQ "="
	  NE "!="
	  LT "<"
	  GT ">"
	  LE "<="
	  GE ">="
%left LS "<<"
      RS ">>"
      ADD "+"
      MINUS "-"
      MUL "*"
      DIV "/"
      REM "%"
%right DEG
       NOT "!"
       BITNOT "~"

%type <Expression *>  exp term

// %destructor { delete $$; } <Expression *>;

%%

S:	/* empty */		{ result.out = new Expression (Expression::OP_NUM, (uint64_t) 1); }
|	exp			{ result.out = $1; }

exp:	  exp DEG exp		{ $$ = new Expression (Expression::OP_DEG, $1, $3); } /* dead? */
	| exp MUL exp		{ $$ = new Expression (Expression::OP_MUL, $1, $3); }
	| exp DIV exp		{ $$ = new Expression (Expression::OP_DIV, $1, $3); }
	| exp REM exp		{ $$ = new Expression (Expression::OP_REM, $1, $3); }
	| exp ADD exp		{ $$ = new Expression (Expression::OP_ADD, $1, $3); }
	| exp MINUS exp		{ $$ = new Expression (Expression::OP_MINUS, $1, $3); }
	| exp LS  exp		{ $$ = new Expression (Expression::OP_LS, $1, $3); }
	| exp RS  exp		{ $$ = new Expression (Expression::OP_RS, $1, $3); }
	| exp LT  exp		{ $$ = new Expression (Expression::OP_LT, $1, $3); }
	| exp LE  exp		{ $$ = new Expression (Expression::OP_LE, $1, $3); }
	| exp GT  exp		{ $$ = new Expression (Expression::OP_GT, $1, $3); }
	| exp GE  exp		{ $$ = new Expression (Expression::OP_GE, $1, $3); }
	| exp EQ  exp		{ $$ = new Expression (Expression::OP_EQ, $1, $3); }
	| exp NE  exp		{ $$ = new Expression (Expression::OP_NE, $1, $3); }
	| exp BITAND exp	{ $$ = new Expression (Expression::OP_BITAND, $1, $3); }
	| exp BITXOR exp	{ $$ = new Expression (Expression::OP_BITXOR, $1, $3); }
	| exp BITOR  exp	{ $$ = new Expression (Expression::OP_BITOR, $1, $3); }
	| exp AND exp		{ $$ = new Expression (Expression::OP_AND, $1, $3); }
	| exp OR  exp		{ $$ = new Expression (Expression::OP_OR, $1, $3); }
	| exp NEQV exp		{ $$ = new Expression (Expression::OP_NEQV, $1, $3); } /* dead? */
	| exp EQV exp		{ $$ = new Expression (Expression::OP_EQV, $1, $3); } /* dead? */
	| exp QWE exp COLON exp
	  {
	     $$ = new Expression (Expression::OP_QWE, $1,
				  new Expression (Expression::OP_COLON, $3, $5));
	  }
	| exp COMMA  exp	{ $$ = new Expression (Expression::OP_COMMA, $1, $3); }
	| exp IN exp		{ $$ = new Expression (Expression::OP_IN, $1, $3); }
	| exp SOME IN exp	{ $$ = new Expression (Expression::OP_SOMEIN, $1, $4); }
	| exp ORDR IN exp	{ $$ = new Expression (Expression::OP_ORDRIN, $1, $4); }
	| term                  { $$ = $1; }

term:	  MINUS term
	  {
	     $$ = new Expression (Expression::OP_MINUS,
				  new Expression (Expression::OP_NUM, (uint64_t) 0), $2);
	  }
	| NOT    term		{ $$ = new Expression (Expression::OP_NOT, $2); }
	| BITNOT term		{ $$ = new Expression (Expression::OP_BITNOT, $2); }
	| LPAR exp RPAR		{ $$ = $2; }
	| FNAME LPAR QSTR RPAR
	  {
	    $$ = new Expression (Expression::OP_FUNC,
				 new Expression (Expression::OP_NUM, $1),
				 new Expression (Expression::OP_NUM, $3));
	  }
	| HASPROP LPAR NAME RPAR
	  {
	    $$ = new Expression (Expression::OP_HASPROP,
				 new Expression (Expression::OP_NUM, processName($3)));
	  }
	| JGROUP LPAR QSTR RPAR
	  {
	    $$ = new Expression (Expression::OP_JAVA,
				 new Expression (Expression::OP_NUM, $1),
				 new Expression (Expression::OP_NUM, $3));
	  }
	| JPARENT LPAR QSTR RPAR
	  {
	     $$ = new Expression (Expression::OP_JAVA,
				  new Expression (Expression::OP_NUM, $1),
				  new Expression (Expression::OP_NUM, $3));
	  }
	| FILEIOVFD LPAR QSTR RPAR
	  {
	    $$ = new Expression (Expression::OP_FILE,
				 new Expression (Expression::OP_NUM, (uint64_t) 0),
				 new Expression (Expression::OP_NUM, $3));
	  }
	| NUM			{ $$ = new Expression (Expression::OP_NUM, $1); }
	| NAME			{ $$ = processName($1); }

%%

namespace QL
{
  static Parser::symbol_type
  unget_ret (std::istream &in, char c, Parser::symbol_type tok)
  {
    in.putback (c);
    return tok;
  }

  static Parser::symbol_type
  yylex (QL::Result &result)
  {
    int base = 0;
    int c;

    do
      c = result.in.get ();
    while (result.in && (c == ' ' || c == '\t'));
    if (!result.in)
      return Parser::make_YYEOF ();

    switch (c)
      {
      case '\0':
      case '\n': return Parser::make_YYEOF ();
      case '(': return Parser::make_LPAR () ;
      case ')': return Parser::make_RPAR ();
      case ',': return Parser::make_COMMA ();
      case '%': return Parser::make_REM ();
      case '/': return Parser::make_DIV ();
      case '*': return Parser::make_MUL ();
      case '-': return Parser::make_MINUS ();
      case '+': return Parser::make_ADD ();
      case '~': return Parser::make_BITNOT ();
      case '^': return Parser::make_BITXOR ();
      case '?': return Parser::make_QWE ();
      case ':': return Parser::make_COLON ();
      case '|':
	c = result.in.get ();
	if (c == '|')
	  return Parser::make_OR ();
	else
	  return unget_ret (result.in, c, Parser::make_BITOR ());
      case '&':
	c = result.in.get ();
	if (c == '&')
	  return Parser::make_AND ();
	else
	  return unget_ret (result.in, c, Parser::make_BITAND ());
      case '!':
	c = result.in.get ();
	if (c == '=')
	  return Parser::make_NE ();
	else
	  return unget_ret (result.in, c, Parser::make_NOT ());
      case '=':
	c = result.in.get ();
	if (c == '=')
	  return Parser::make_EQ ();
	else
	  throw Parser::syntax_error ("Syntax error after =");
      case '<':
	c = result.in.get ();
	if (c == '=')
	  return Parser::make_LE ();
	else if (c == '<')
	  return Parser::make_LS ();
	else
	  return unget_ret (result.in, c, Parser::make_LT ());
      case '>':
	c = result.in.get ();
	if (c == '=')
	  return Parser::make_GE ();
	else if (c == '>')
	  return Parser::make_RS ();
	else
	  return unget_ret (result.in, c, Parser::make_GT ());
      case '"':
	{
	  int  maxsz = 16;
	  char *str = (char *) malloc (maxsz);
	  char *ptr = str;

	  for (;;)
	    {
	      c = result.in.get ();
	      if (!result.in)
		{
		  free (str);
		  throw Parser::syntax_error ("Unclosed \"");
		}

	      switch (c)
		{
		case '"':
		  *ptr = (char)0;
		  // XXX omazur: need new string type
		  return Parser::make_QSTR ((uint64_t) str);
		case 0:
		case '\n':
		  free (str);
		  throw Parser::syntax_error ("Multiline strings are not supported");
		default:
		  if (ptr - str >= maxsz)
		    {
		      size_t len = ptr - str;
		      maxsz = maxsz > 8192 ? maxsz + 8192 : maxsz * 2;
		      char *new_s = (char *) realloc (str, maxsz);
		      str = new_s;
		      ptr = str + len;
		    }
		  *ptr++ = c;
		}
	    }
	}
      default:
	if (c == '0')
	  {
	    base = 8;
	    c = result.in.get ();
	    if ( c == 'x' )
	      {
		base = 16;
		c = result.in.get ();
	      }
	  }
	else if (c >= '1' && c <='9')
	  base = 10;

	if (base)
	  {
	    uint64_t lval = 0;
	    for (;;)
	      {
		int digit = -1;
		switch (c)
		  {
		  case '0': case '1': case '2': case '3':
		  case '4': case '5': case '6': case '7':
		    digit = c - '0';
		    break;
		  case '8': case '9':
		    if (base > 8)
		      digit = c - '0';
		    break;
		  case 'a': case 'b': case 'c':
		  case 'd': case 'e': case 'f':
		    if (base == 16)
		      digit = c - 'a' + 10;
		    break;
		  case 'A': case 'B': case 'C':
		  case 'D': case 'E': case 'F':
		    if (base == 16)
		      digit = c - 'A' + 10;
		    break;
		  }
		if  (digit == -1)
		  {
		    result.in.putback (c);
		    break;
		  }
		lval = lval * base + digit;
		c = result.in.get ();
	      }
	    return Parser::make_NUM (lval);
	  }

	if ((c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z'))
	  {
	    char name[32];	// omazur XXX: accept any length
	    name[0] = (char)c;
	    for (size_t i = 1; i < sizeof (name); i++)
	      {
		c = result.in.get ();
		if ((c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') ||
		    (c >= '0' && c <= '9') || (c == '_'))
		  name[i] = c;
		else
		  {
		    name[i] = (char)0;
		    result.in.putback (c);
		    break;
		  }
	      }

	    if (strcasecmp (name, NTXT ("IN")) == 0)
	      return Parser::make_IN ();
	    else if (strcasecmp (name, NTXT ("SOME")) == 0)
	      return Parser::make_SOME ();
	    else if (strcasecmp (name, NTXT ("ORDERED")) == 0)
	      return Parser::make_ORDR ();
	    else if (strcasecmp (name, NTXT ("TRUE")) == 0)
	      return Parser::make_NUM ((uint64_t) 1);
	    else if (strcasecmp (name, NTXT ("FALSE")) == 0)
	      return Parser::make_NUM ((uint64_t) 0);
	    else if (strcasecmp (name, NTXT ("FNAME")) == 0)
	      return Parser::make_FNAME (Expression::FUNC_FNAME);
	    else if (strcasecmp (name, NTXT ("HAS_PROP")) == 0)
	      return Parser::make_HASPROP ();
	    else if (strcasecmp (name, NTXT ("JGROUP")) == 0)
	      return Parser::make_JGROUP (Expression::JAVA_JGROUP);
	    else if (strcasecmp (name, NTXT ("JPARENT")) == 0 )
	      return Parser::make_JPARENT (Expression::JAVA_JPARENT);
	    else if (strcasecmp (name, NTXT ("DNAME")) == 0)
	      return Parser::make_FNAME (Expression::FUNC_DNAME);
	    else if (strcasecmp (name, NTXT ("FILEIOVFD")) == 0 )
	      return Parser::make_FILEIOVFD ();

	    std::string nm = std::string (name);
	    return Parser::make_NAME (nm);
	  }

	throw Parser::syntax_error ("Syntax error");
      }
  }
  void
  Parser::error (const std::string &)
  {
    // do nothing for now
  }
}

