/* C++ parsers require Bison 3.3 */
%require "3.3"
%language "C++"

/* Write-out tokens header file */
%defines

/* Use Bison's 'variant' to store values. 
 * This allows us to use non POD types (e.g.
 * with constructors/destrictors), which is
 * not possible with the default mode which
 * uses unions.
 */
%define api.value.type variant

/* 
 * Use the 'complete' symbol type (i.e. variant)
 * in the lexer
 */
%define api.token.constructor

/*
 * Add a prefix the make_* functions used to
 * create the symbols
 */
%define api.token.prefix {TOKEN_}

/*
 * Use a re-entrant (no global vars) parser
 */
/*%define api.pure full*/

/* Wrap everything in our namespace */
%define api.namespace {blifparse}

/* Name the parser class */
%define api.parser.class {Parser}

/* Match the flex prefix */
%define api.prefix {blifparse_}

/* Extra checks for correct usage */
%define parse.assert

/* Enable debugging info */
%define parse.trace

/* Better error reporting */
%define parse.error verbose

/* 
 * Fixes inaccuracy in verbose error reporting.
 * May be slow for some grammars.
 */
/*%define parse.lac full*/

/* Track locations */
/*%locations*/

/* Generate a table of token names */
%token-table

%lex-param {Lexer& lexer}
%parse-param {Lexer& lexer}
%parse-param {Callback& callback}


%code requires {
    #include <memory>
    #include "blifparse.hpp"
    #include "blif_lexer_fwd.hpp"
}

%code top {
    #include "blif_lexer.hpp"
    //Bison calls blifparse_lex() to get the next token.
    //We use the Lexer class as the interface to the lexer, so we
    //re-defined the function to tell Bison how to get the next token.
    static blifparse::Parser::symbol_type blifparse_lex(blifparse::Lexer& lexer) {
        return lexer.next_token();
    }
}

%{

#include <stdio.h>
#include "assert.h"

#include "blifparse.hpp"
#include "blif_common.hpp"
#include "blif_error.hpp"

using namespace blifparse;

%}

/* Declare constant */
%token DOT_NAMES ".names"
%token DOT_LATCH ".latch"
%token DOT_MODEL ".model"
%token DOT_SUBCKT ".subckt"
%token DOT_INPUTS ".inputs"
%token DOT_OUTPUTS ".outputs"
%token DOT_CLOCK ".clock"
%token DOT_END ".end"
%token DOT_BLACKBOX ".blackbox"
%token LATCH_FE "fe"
%token LATCH_RE "re"
%token LATCH_AH "ah"
%token LATCH_AL "al"
%token LATCH_AS "as"
%token NIL "NIL"
%token LATCH_INIT_2 "2"
%token LATCH_INIT_3 "3"
%token LOGIC_FALSE "0"
%token LOGIC_TRUE "1"
%token LOGIC_DONT_CARE "-"
%token EQ "="
%token EOL "end-of-line"
%token EOF 0 "end-of-file"

/*BLIF extensions */
%token DOT_CONN ".conn"
%token DOT_ATTR ".attr"
%token DOT_PARAM ".param"
%token DOT_CNAME ".cname"

/* declare variable tokens */
%token <std::string> STRING

/* declare types */
%type <SubCkt> subckt
%type <Names> names
%type <std::vector<std::string>> string_list
%type <std::vector<LogicValue>> so_cover_row
%type <LogicValue> logic_value
%type <LogicValue> latch_init
%type <std::string> latch_control
%type <LatchType> latch_type

/* BLIF Extensions */
%type <Conn> conn
%type <Cname> cname
%type <Attr> attr
%type <Param> param

/* Top level rule */
%start blif_data

%%

blif_data: /*empty*/ { }
    | blif_data DOT_MODEL STRING EOL        { callback.lineno(lexer.lineno()-1); callback.begin_model($3); }
    | blif_data DOT_INPUTS string_list EOL  { callback.lineno(lexer.lineno()-1); callback.inputs($3); }
    | blif_data DOT_OUTPUTS string_list EOL { callback.lineno(lexer.lineno()-1); callback.outputs($3); }
    | blif_data names                       { callback.lineno(lexer.lineno()-1); callback.names($2.nets, $2.so_cover); }
    | blif_data subckt EOL                  { 
                                              if($2.ports.size() != $2.nets.size()) {
                                                  blif_error_wrap(callback ,lexer.lineno()-1, lexer.text(), 
                                                    "Mismatched subckt port and net connection(s) size do not match"
                                                    " (%zu ports, %zu nets)", $2.ports.size(), $2.nets.size());
                                              }
                                              callback.lineno(lexer.lineno()-1); 
                                              callback.subckt($2.model, $2.ports, $2.nets);
                                            }
    | blif_data latch EOL                   { /*callback already called */ }
    | blif_data DOT_BLACKBOX EOL            { callback.lineno(lexer.lineno()-1); callback.blackbox(); }
    | blif_data DOT_END EOL                 { callback.lineno(lexer.lineno()-1); callback.end_model(); }
    | blif_data conn EOL                    { callback.lineno(lexer.lineno()-1); callback.conn($2.src, $2.dst); }
    | blif_data cname EOL                   { callback.lineno(lexer.lineno()-1); callback.cname($2.name); }
    | blif_data attr EOL                    { callback.lineno(lexer.lineno()-1); callback.attr($2.name, $2.value); }
    | blif_data param EOL                   { callback.lineno(lexer.lineno()-1); callback.param($2.name, $2.value); }
    | blif_data EOL                         { /* eat end-of-lines */}
    ;

names: DOT_NAMES string_list EOL { $$ = Names(); $$.nets = $2; }
    | names so_cover_row EOL { 
                                $$ = std::move($1); 
                                if($$.nets.size() != $2.size()) {
                                    blif_error_wrap(callback, lexer.lineno()-1, lexer.text(),
                                        "Mismatched .names single-output cover row."
                                        " names connected to %zu net(s), but cover row has %zu element(s)",
                                        $$.nets.size(), $2.size());
                                }
                                $$.so_cover.push_back($2); 
                             }
    ;

subckt: DOT_SUBCKT STRING       { $$ = SubCkt(); $$.model = $2; }
    | subckt STRING EQ STRING   { $$ = std::move($1); $$.ports.push_back($2); $$.nets.push_back($4); }
    ;

latch: DOT_LATCH STRING STRING {
                                    //Input and output only
                                    callback.lineno(lexer.lineno()); 
                                    callback.latch($2, $3, LatchType::UNSPECIFIED, "", LogicValue::UNKNOWN);
                               }
    | DOT_LATCH STRING STRING latch_type latch_control {
                                    //Input, output, type and control
                                    callback.lineno(lexer.lineno()); 
                                    callback.latch($2, $3, $4, $5, LogicValue::UNKNOWN);
                               }
    | DOT_LATCH STRING STRING latch_type latch_control latch_init {
                                    //Input, output, type, control and init-value
                                    callback.lineno(lexer.lineno()); 
                                    callback.latch($2, $3, $4, $5, $6);
                               }
    | DOT_LATCH STRING STRING latch_init {
                                    //Input, output, and init-value
                                    callback.lineno(lexer.lineno());
                                    callback.latch($2, $3, LatchType::UNSPECIFIED, "", $4);
                               }
    ;

latch_init: LOGIC_TRUE { $$ = LogicValue::TRUE; }
    | LOGIC_FALSE { $$ = LogicValue::FALSE; }
    | LATCH_INIT_2 { $$ = LogicValue::DONT_CARE; }
    | LATCH_INIT_3 { $$ = LogicValue::UNKNOWN; }
    ;

latch_control: STRING { $$ = $1;}
    | NIL { $$ = ""; }
    ;

latch_type: LATCH_FE { $$ = LatchType::FALLING_EDGE; }
    | LATCH_RE { $$ = LatchType::RISING_EDGE; }
    | LATCH_AH { $$ = LatchType::ACTIVE_HIGH; }
    | LATCH_AL { $$ = LatchType::ACTIVE_LOW; }
    | LATCH_AS { $$ = LatchType::ASYNCHRONOUS; }
    ;

so_cover_row: logic_value { $$ = std::vector<LogicValue>(); $$.push_back($1); }
    | so_cover_row logic_value { $$ = std::move($1); $$.push_back($2); }
    ;

logic_value: LOGIC_TRUE { $$ = LogicValue::TRUE; }
    | LOGIC_FALSE { $$ = LogicValue::FALSE; }
    | LOGIC_DONT_CARE { $$ = LogicValue::DONT_CARE; }
    ;

string_list: /*empty*/ { $$ = std::vector<std::string>(); }
    | string_list STRING { $$ = std::move($1); $$.push_back($2); }
    ;

/*
 * BLIF Extensions
 */
conn: DOT_CONN STRING STRING { $$ = Conn(); $$.src = $2; $$.dst = $3; }
cname: DOT_CNAME STRING { $$ = Cname(); $$.name = $2; }
attr: DOT_ATTR STRING STRING { $$ = Attr(); $$.name = $2; $$.value = $3; }
    | DOT_ATTR STRING { $$ = Attr(); $$.name = $2; $$.value = ""; }
param: DOT_PARAM STRING STRING { $$ = Param(); $$.name = $2; $$.value = $3; }
    | DOT_PARAM STRING { $$ = Param(); $$.name = $2; $$.value = ""; }

%%

void blifparse::Parser::error(const std::string& msg) {
    blif_error_wrap(callback, lexer.lineno(), lexer.text(), msg.c_str());
}
