// OpenSTA, Static Timing Analyzer
// Copyright (c) 2026, Parallax Software, Inc.
// 
// 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 of the License, 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, see <https://www.gnu.org/licenses/>.
// 
// The origin of this software must not be misrepresented; you must not
// claim that you wrote the original software.
// 
// Altered source versions must be plainly marked as such, and must not be
// misrepresented as being the original software.
// 
// This notice may not be removed or altered from any source distribution.

%{
#include <string>
#include <utility>

#include "Report.hh"
#include "StringUtil.hh"
#include "parasitics/SpefReaderPvt.hh"
#include "parasitics/SpefScanner.hh"

#undef yylex
#define yylex scanner->lex

// warning: variable 'yynerrs_' set but not used
#pragma GCC diagnostic ignored "-Wunused-but-set-variable"

void
sta::SpefParse::error(const location_type &loc,
                     const std::string &msg)
{
  reader->report()->fileError(1670, reader->filename(), loc.begin.line, "{}", msg);
}
%}

%code requires {
#include <string>
#include "StringUtil.hh"

namespace sta {
// Bison's C++ variant skeleton cannot use void as a semantic type.
struct SpefParseVoid {};
class SpefReader;
class SpefScanner;
class Net;
class Pin;
class PortDirection;
class SpefRspfPi;
class SpefTriple;
}
}

%require  "3.2"
%skeleton "lalr1.cc"
%debug
%define api.namespace {sta}
%locations
%define api.location.file "SpefLocation.hh"
%define parse.assert
%parse-param { SpefScanner *scanner }
%parse-param { SpefReader *reader }
%define api.parser.class {SpefParse}
%define api.value.type variant

%token <int> INTEGER
%token <float> FLOAT
%token <std::string> QSTRING INDEX IDENT NAME

%token SPEF DESIGN DATE VENDOR PROGRAM DESIGN_FLOW
%token PVERSION DIVIDER DELIMITER
%token BUS_DELIMITER T_UNIT C_UNIT R_UNIT L_UNIT NAME_MAP
%token POWER_NETS GROUND_NETS KW_C KW_L KW_S KW_D KW_V
%token PORTS PHYSICAL_PORTS DEFINE PDEFINE
%token D_NET D_PNET R_NET R_PNET END
%token CONN CAP RES INDUC KW_P KW_I KW_N DRIVER CELL C2_R1_C1 LOADS
%token RC KW_Q KW_K

%type <int> conf cap_id res_id induc_id cap_elem cap_elems
%type <int> res_elem res_elems induc_elem induc_elems
%type <int> pos_integer

%type <float> number pos_number threshold
%type <float> real_component imaginary_component pole poles
%type <float> residue residues complex_par_value cnumber

%nterm <sta::SpefParseVoid> name_map_entries name_map_entry net_names
%nterm <sta::SpefParseVoid> net_name port_entries port_entry pport_entries pport_entry
%nterm <sta::SpefParseVoid> pport_name pexternal_connection

%type <std::string> name_or_index inst_name
%type <std::string> mapped_item
%type <std::string> physical_inst port_name pport
%type <std::string> entity external_connection
%type <std::string> cell_type
%type <std::string> driver_cell pnet_ref
%type <std::string> internal_pdspf_node
%type <std::string> parasitic_node internal_parasitic_node

%type <char> hchar suffix_bus_delim prefix_bus_delim

%type <sta::StringSeq *> qstrings
%type <sta::PortDirection *> direction

%type <sta::SpefTriple *> par_value total_cap

%type <sta::SpefRspfPi *> pi_model

%type <sta::Pin *> pin_name driver_pair internal_connection

%type <sta::Net *> net

%start file

%%

file:
	header_def
	name_map
	power_def
	external_def
	define_def
	internal_def
;

/****************************************************************/

prefix_bus_delim:
	'['
	{ $$ = '['; }
|	'{'
	{ $$ = '}'; }
|	'('
	{ $$ = ')'; }
|	'<'
	{ $$ = '<'; }
;

suffix_bus_delim:
	']'
	{ $$ = ']'; }
|	'}'
	{ $$ = '}'; }
|	')'
	{ $$ = ')'; }
|	'>'
	{ $$ = '>'; }
;

hchar:
	'.'
	{ $$ = '.'; }
|	'/'
	{ $$ = '/'; }
|	'|'
	{ $$ = '|'; }
|	':'
	{ $$ = ':'; }
;

/****************************************************************/

header_def:
	spef_version
	design_name
	date
	vendor
	program_name
	program_version
	design_flow
	hierarchy_div_def
	pin_delim_def
	bus_delim_def
	unit_def
;

spef_version:
	SPEF QSTRING
;

design_name:
	DESIGN QSTRING
;

date:
	DATE QSTRING
;

program_name:
	PROGRAM QSTRING
;

program_version:
	PVERSION QSTRING
;

vendor:
	VENDOR QSTRING
;

design_flow:
	DESIGN_FLOW qstrings
	{ reader->setDesignFlow($2); }
;

qstrings:
	QSTRING
	{ $$ = new sta::StringSeq;
	  $$->push_back(std::move($1));
	}
|	qstrings QSTRING
	{ $$ = $1;
	  $$->push_back(std::move($2));
	}
;

hierarchy_div_def:
	DIVIDER hchar
	{ reader->setDivider($2); }
;

pin_delim_def:
	DELIMITER hchar
	{ reader->setDelimiter($2); }
;

bus_delim_def:
	BUS_DELIMITER prefix_bus_delim
	{ reader->setBusBrackets($2, '\0'); }
|	BUS_DELIMITER prefix_bus_delim suffix_bus_delim
	{ reader->setBusBrackets($2, $3); }
;

/****************************************************************/

unit_def:
	time_scale
	cap_scale
	res_scale
	induc_scale
;

time_scale:
	T_UNIT pos_number IDENT
	{ reader->setTimeScale($2, $3); }
;

cap_scale:
	C_UNIT pos_number IDENT
	{ reader->setCapScale($2, $3); }
;

res_scale:
	R_UNIT pos_number IDENT
	{ reader->setResScale($2, $3); }
;

induc_scale:
	L_UNIT pos_number IDENT
	{ reader->setInductScale($2, $3); }
;

/****************************************************************/

name_map:
	/* empty */
|	NAME_MAP name_map_entries
;

name_map_entries:
	name_map_entry
|	name_map_entries name_map_entry
;

name_map_entry:
	INDEX mapped_item
	{ reader->makeNameMapEntry($1, $2); }
;

mapped_item:
	IDENT
|	NAME
|	QSTRING
;

/****************************************************************/

power_def:
	/* empty */
|	power_net_def
|	ground_net_def
|	power_net_def ground_net_def
;

power_net_def:
	POWER_NETS net_names
;

ground_net_def:
	GROUND_NETS net_names
;

net_names:
	net_name
|	net_names net_name
;

net_name:
	name_or_index
	{ $$ = sta::SpefParseVoid{}; }
;

/****************************************************************/

external_def:
	/* empty */
|	port_def
|	physical_port_def
|	port_def physical_port_def
;

port_def:
	PORTS port_entries
;

port_entries:
	port_entry
|	port_entries port_entry
;

port_entry:
	port_name direction conn_attrs
	{ $$ = sta::SpefParseVoid{}; }
;

direction:
	IDENT
	{ $$ = reader->portDirection($1); }
;

port_name:
	name_or_index
;

inst_name:
	name_or_index
;

physical_port_def:
	PHYSICAL_PORTS pport_entries
;

pport_entries:
	pport_entry
|	pport_entries pport_entry
;

pport_entry:
	pport_name IDENT conn_attrs
	{ $$ = sta::SpefParseVoid{}; }
;

pport_name:
	name_or_index
	{ $$ = sta::SpefParseVoid{}; }
|	physical_inst ':' pport
	{ $$ = sta::SpefParseVoid{}; }
;

pport:
	name_or_index
;

physical_inst:
	name_or_index
;

/****************************************************************/

conn_attrs:
	/* empty */
|	conn_attrs conn_attr
;

conn_attr:
	coordinates
|	cap_load
|	slews
|	driving_cell
;

coordinates:
	KW_C number number
;

cap_load:
	KW_L par_value
	{ delete $2; }
;

par_value:
	number
	{ $$ = new sta::SpefTriple($1); }
|	number ':' number ':' number
	{ $$ = new sta::SpefTriple($1, $3, $5); }
;

slews:
	KW_S par_value par_value
	{ delete $2;
	  delete $3;
	}
|	KW_S par_value par_value threshold threshold
	{ delete $2;
	  delete $3;
	}
;

threshold:
	pos_number
|	pos_number ':' pos_number ':' pos_number
;

driving_cell:
	KW_D cell_type
;

cell_type:
	IDENT
|	INDEX
;

/****************************************************************/

define_def:
	/* empty */
|	define_def define_entry
;

define_entry:
	DEFINE inst_name entity
|	DEFINE inst_name inst_name entity
|	PDEFINE physical_inst entity
;

entity:
	QSTRING
;

/****************************************************************/

internal_def:
	nets
|	internal_def nets
;

nets:
	d_net
|	r_net
|	d_pnet
|	r_pnet
;

/****************************************************************/

d_net:
	D_NET net total_cap
	{ reader->dspfBegin($2, $3); }
	routing_conf conn_sec cap_sec res_sec induc_sec END
	{ reader->dspfFinish(); }
;

net:
	name_or_index
	{ $$ = reader->findNet($1); }
;

total_cap:
	par_value
;

routing_conf:
	/* empty */
|	KW_V conf
;

conf:
	pos_integer
;

/****************************************************************/

conn_sec:
	/* empty */
|	CONN conn_defs internal_node_coords
;

conn_defs:
	conn_def
|	conn_defs conn_def
;

conn_def:
	KW_P external_connection direction conn_attrs
|	KW_I internal_connection direction conn_attrs
;

external_connection:
	name_or_index
	{ $$ = std::string(); }
|	physical_inst ':' pport
	{ $$ = std::string(); }
;

internal_connection:
	pin_name
;

pin_name:
	name_or_index
	{ $$ = reader->findPin($1); }
;

internal_node_coords:
	/* empty */
|	internal_node_coords internal_node_coord
;

internal_node_coord:
	KW_N internal_parasitic_node coordinates
;

internal_parasitic_node:
	name_or_index
	{ $$ = std::string(); }
;

/****************************************************************/

cap_sec:
	/* empty */
|	CAP cap_elems
;

cap_elems:
	/* empty */
	{ $$ = 0; }
|	cap_elems cap_elem
	{ $$ = $1; }
;

cap_elem:
	cap_id parasitic_node par_value
	{ reader->makeCapacitor($1, $2, $3); }
|	cap_id parasitic_node parasitic_node par_value
	{ reader->makeCapacitor($1, $2, $3, $4); }
;

cap_id:
	pos_integer
;

parasitic_node:
	name_or_index
;

/****************************************************************/

res_sec:
	/* empty */
|	RES res_elems
;

res_elems:
	/* empty */
	{ $$ = 0; }
|	res_elems res_elem
	{ $$ = $1; }
;

res_elem:
	res_id parasitic_node parasitic_node par_value
	{ reader->makeResistor($1, $2, $3, $4); }
;

res_id:
	pos_integer
;

/****************************************************************/

induc_sec:
	/* empty */
|	INDUC induc_elems
;

induc_elems:
	/* empty */
	{ $$ = 0; }
|	induc_elems induc_elem
	{ $$ = $1; }
;

induc_elem:
	induc_id parasitic_node parasitic_node par_value
	{ delete $4; }
;

induc_id:
	pos_integer
;

/****************************************************************/

r_net:
	R_NET net total_cap
	{ reader->rspfBegin($2, $3); }
	routing_conf driver_reducs END
	{ reader->rspfFinish(); }
;

driver_reducs:
	/* empty */
|	driver_reducs driver_reduc
;

driver_reduc:
	driver_pair driver_cell pi_model
	{ reader->rspfDrvrBegin($1, $3); }
	load_desc
	{ reader->rspfDrvrFinish(); }
;

driver_pair:
	DRIVER pin_name
	{ $$ = $2; }
;

driver_cell:
	CELL cell_type
	{ $$ = $2; }
;

pi_model:
	C2_R1_C1 par_value par_value par_value
	{ $$ = new sta::SpefRspfPi($2, $3, $4); }
;

/****************************************************************/

load_desc:
	LOADS rc_descs
;

rc_descs:
	rc_desc
|	rc_descs rc_desc
;

rc_desc:
	RC pin_name par_value
	{ reader->rspfLoad($2, $3); }
|	RC pin_name par_value pole_residue_desc
	{ reader->rspfLoad($2, $3); }
;

pole_residue_desc:
	pole_desc residue_desc
;

pole_desc:
	KW_Q pos_integer poles
;

poles:
	pole
|	poles pole
	{ $$ = $2; }
;

pole:
	complex_par_value
;

complex_par_value:
	cnumber
|	number
|	cnumber ':' cnumber ':' cnumber
|	number ':' number ':' number
;

cnumber:
	'(' real_component imaginary_component ')'
	{ $$ = $2; }
;

real_component:
	number
;

imaginary_component:
	number
;

residue_desc:
	KW_K pos_integer residues
;

residues:
	residue
|	residues residue
;

residue:
	complex_par_value
;

/****************************************************************/

d_pnet:
	D_PNET pnet_ref total_cap routing_conf pconn_sec cap_sec res_sec
               induc_sec END
;

pnet_ref:
	name_or_index
;

pconn_sec:
	CONN pconn_defs internal_pnode_coords
;

pconn_defs:
	pconn_def
|	pconn_defs pconn_def
;

pconn_def:
	KW_P pexternal_connection direction conn_attr
|	KW_I internal_connection direction conn_attr
;

pexternal_connection:
	pport_name
;

internal_pnode_coords:
	/* empty */
|	internal_pnode_coords internal_pnode_coord
;

internal_pnode_coord:
	KW_N internal_pdspf_node coordinates
;

internal_pdspf_node:
	name_or_index
	{ $$ = std::string(); }
;

name_or_index:
	IDENT
|	NAME
|	INDEX
;

/****************************************************************/

r_pnet:
	R_PNET pnet_ref total_cap routing_conf END
|	R_PNET pnet_ref total_cap routing_conf pdriver_reduc END
;

pdriver_reduc:
	pdriver_pair driver_cell pi_model load_desc
;

pdriver_pair:
	DRIVER internal_connection
;

/****************************************************************/

number:
	INTEGER
	{ $$ = static_cast<float>($1); }
|	FLOAT
	{ $$ = $1; }
;

pos_integer:
	INTEGER
	{ int value = $1;
	  if (value < 0)
	    reader->warn(1525, "{} is not positive.", value);
	  $$ = value;
	}
;

pos_number:
	INTEGER
	{ float value = static_cast<float>($1);
	  if (value < 0)
	    reader->warn(1526, "{:.4f} is not positive.", value);
	  $$ = value;
	}
|	FLOAT
	{ float value = static_cast<float>($1);
	  if (value < 0)
	    reader->warn(1527, "{:.4f} is not positive.", value);
	  $$ = value;
	}
;

%%
