aboutsummaryrefslogtreecommitdiff
path: root/src/parser
diff options
context:
space:
mode:
Diffstat (limited to 'src/parser')
-rw-r--r--src/parser/parse_analysis.cpp109
-rw-r--r--src/parser/parse_analysis.h94
-rw-r--r--src/parser/parse_directive.cpp132
-rw-r--r--src/parser/parse_directive.h68
-rw-r--r--src/parser/parse_element.cpp1266
-rw-r--r--src/parser/parse_element.h104
-rw-r--r--src/parser/parse_tree.cpp972
-rw-r--r--src/parser/parse_tree.h890
-rw-r--r--src/parser/parser.l438
-rw-r--r--src/parser/parser.y961
10 files changed, 5034 insertions, 0 deletions
diff --git a/src/parser/parse_analysis.cpp b/src/parser/parse_analysis.cpp
new file mode 100644
index 0000000..3168858
--- /dev/null
+++ b/src/parser/parse_analysis.cpp
@@ -0,0 +1,109 @@
+#include "parse_analysis.h"
+#include "cw_source.h"
+#include "devices/alldevices.h"
+#include "directional_coupler.h"
+#include "optical_output_port.h"
+#include "parse_tree.h"
+#include "specs.h"
+#include "strutils.h"
+#include "sysc_utils.h"
+#include "general_utils.h"
+#include <functional>
+
+using spx::oa_value_type;
+using spx::ea_value_type;
+using spx::ed_value_type;
+
+using spx::ed_bus_type;
+using spx::oa_signal_type;
+using spx::ea_signal_type;
+using spx::ed_signal_type;
+using spx::ed_bus_type;
+
+void OPAnalysis::create() const
+{
+ specsGlobalConfig.analysis_type = SPECSConfig::OP;
+ specsGlobalConfig.simulation_mode = OpticalOutputPortMode::FREQUENCY_DOMAIN;
+}
+
+void DCAnalysis::create() const
+{
+ specsGlobalConfig.analysis_type = SPECSConfig::DC;
+ specsGlobalConfig.simulation_mode = OpticalOutputPortMode::FREQUENCY_DOMAIN;
+
+ assert(sweep_orders.size() > 0);
+
+ set<CWSource *> all_cws = sc_get_all_object_by_type<CWSource>();
+ vector<CWSource *> all_cws_vec(all_cws.begin(), all_cws.end());
+
+ int i = 0;
+ for (const auto &sweep_order: sweep_orders)
+ {
+ string element_name = parent->name_prefix() + sweep_order.first.first;
+ string attribute_name = sweep_order.first.second;
+
+ auto is_desired_element = [&element_name](const auto &x){ return x->name() == element_name; };
+ auto it = std::find_if(all_cws_vec.begin(), all_cws_vec.end(), is_desired_element);
+ if (it == all_cws_vec.end())
+ {
+ cerr << "Element not found: " << element_name << endl;
+ exit(1);
+ }
+ CWSource *elem = *it;
+ // cout << elem << endl;
+ // cout << &CWSource::setWavelength << endl;
+
+ function<void(double)> f;
+ if (attribute_name == "WL" || attribute_name == "WAVELENGTH" || attribute_name == "LAMBDA")
+ {
+ f = std::bind(&CWSource::setWavelength, elem, placeholders::_1);
+ }
+ else if (attribute_name == "P" || attribute_name == "POW" || attribute_name == "POWER")
+ {
+ f = std::bind(static_cast<void (CWSource::*)(const double&)>(&CWSource::setPower), elem, placeholders::_1);
+ }
+ else if (attribute_name == "F" || attribute_name == "FREQ" || attribute_name == "FREQUENCY")
+ {
+ f = std::bind((&CWSource::setFrequency), elem, placeholders::_1);
+ }
+ else
+ {
+ cerr << "Unknown attribute for " << sweep_order.first.first << ": " << attribute_name << endl;
+ exit(1);
+ }
+ auto values = range(sweep_order.second[0], sweep_order.second[1], sweep_order.second[2]);
+ auto order = pair<function<void(double)>, vector<double>>(f, values);
+ auto order_name = attribute_name + "(" + element_name + ")";
+ specsGlobalConfig.cw_sweep_orders.emplace(order_name, order);
+ ++i;
+ }
+}
+
+void TRANAnalysis::create() const
+{
+ specsGlobalConfig.analysis_type = SPECSConfig::TRAN;
+ specsGlobalConfig.simulation_mode = OpticalOutputPortMode::TIME_DOMAIN;
+
+ assert(args.size() <= 1);
+
+ if (args.size() >= 1)
+ {
+ double val = args[0].as_double();
+ // cout << val << endl;
+ if (val > 0)
+ specsGlobalConfig.tran_duration = args[0].as_double();
+ else
+ {
+ cerr << "Negative simulation duration is not accepted" << endl;
+ exit(1);
+ }
+ }
+
+ for (const auto &p : kwargs)
+ {
+ string kw = p.first;
+ strutils::toupper(kw);
+ cerr << "Unknown keyword " << kw << endl;
+ exit(1);
+ }
+}
diff --git a/src/parser/parse_analysis.h b/src/parser/parse_analysis.h
new file mode 100644
index 0000000..9e3f44c
--- /dev/null
+++ b/src/parser/parse_analysis.h
@@ -0,0 +1,94 @@
+#pragma once
+
+#include "parse_tree.h"
+#include "specs.h"
+#include "strutils.h"
+
+#include <initializer_list>
+#include <utility>
+#include <memory>
+#include <string>
+#include <vector>
+#include <map>
+#include <iostream>
+
+using std::pair;
+using std::make_pair;
+using std::string;
+using std::vector;
+using std::map;
+using std::cout;
+using std::cerr;
+using std::endl;
+using std::pair;
+using std::unique_ptr;
+
+/* OP: operating point analysis */
+struct OPAnalysis : public ParseAnalysis {
+ /* Import constructor from ParseAnalysis */
+ using ParseAnalysis::ParseAnalysis;
+
+ virtual ParseAnalysis* clone() const
+ { return new OPAnalysis(*this); }
+ virtual void create() const;
+ virtual string kind() const
+ { return "OP"; }
+};
+
+/* DC: stepped-source operating point analysis */
+struct DCAnalysis : public ParseAnalysis {
+ typedef pair<string, string> sweep_param_type;
+ typedef vector<double> sweep_range_type;
+ typedef pair<sweep_param_type, sweep_range_type> sweep_order_type;
+
+ /* Import constructor from ParseAnalysis */
+ using ParseAnalysis::ParseAnalysis;
+
+ map<sweep_param_type, sweep_range_type> sweep_orders;
+
+ void register_sweep_order(sweep_param_type param, sweep_range_type range)
+ {
+ strutils::toupper(param.first);
+ strutils::toupper(param.second);
+ if (sweep_orders.count(param) != 0)
+ {
+ cerr << "A sweep order for " << param.second << "(" << param.first << ")";
+ cerr << " was already recoreded." << endl;
+ exit(1);
+ }
+ auto n = (range[1] - range[0]) / range[2];
+ n = max(0.0, n);
+ n = floor(n);
+ if (n <= 1.5)
+ {
+ cerr << "Sweep order must contain at least 2 point." << endl;
+ exit(1);
+ }
+ cout << "Recorded sweep order for " << param.second << "(" << param.first << ")";
+ cout << " (" << n << "points)" << endl;
+ sweep_orders.emplace(param, range);
+ }
+
+ void register_sweep_order(sweep_order_type order)
+ {
+ register_sweep_order(order.first, order.second);
+ }
+
+ virtual ParseAnalysis* clone() const
+ { return new DCAnalysis(*this); }
+ virtual void create() const;
+ virtual string kind() const
+ { return "DC"; }
+};
+
+/* TRAN: transient analysis */
+struct TRANAnalysis : public ParseAnalysis {
+ /* Import constructor from ParseElement */
+ using ParseAnalysis::ParseAnalysis;
+
+ virtual ParseAnalysis* clone() const
+ { return new TRANAnalysis(*this); }
+ virtual void create() const;
+ virtual string kind() const
+ { return "TRAN"; }
+}; \ No newline at end of file
diff --git a/src/parser/parse_directive.cpp b/src/parser/parse_directive.cpp
new file mode 100644
index 0000000..e66e8cf
--- /dev/null
+++ b/src/parser/parse_directive.cpp
@@ -0,0 +1,132 @@
+#include "parse_directive.h"
+#include "devices/alldevices.h"
+#include "directional_coupler.h"
+#include "optical_signal.h"
+#include "parse_tree.h"
+#include "specs.h"
+
+using spx::oa_value_type;
+using spx::ea_value_type;
+using spx::ed_value_type;
+
+using spx::ed_bus_type;
+using spx::oa_signal_type;
+using spx::ea_signal_type;
+using spx::ed_signal_type;
+using spx::ed_bus_type;
+
+void OPTIONSDirective::create() const
+{
+ // Verify number of nodes
+ assert(args.size() == 0);
+
+ // Parse keyword arguments
+ for (auto &p: kwargs)
+ {
+ string kw = p.first;
+ strutils::toupper(kw);
+ if (kw == "TRACEALL")
+ specsGlobalConfig.trace_all_optical_nets = p.second.as_boolean();
+ else if (kw == "ABSTOL")
+ specsGlobalConfig.default_abstol = p.second.as_double();
+ else if (kw == "RELTOL")
+ specsGlobalConfig.default_reltol = p.second.as_double();
+ else if (kw == "TS" || kw == "TIMESCALE")
+ specsGlobalConfig.engine_timescale = (SPECSConfig::EngineTimescale)p.second.as_integer();
+ else if (kw == "RESOLUTION")
+ specsGlobalConfig.default_resolution_multiplier = p.second.as_double();
+ else if (kw == "TRACEALL")
+ specsGlobalConfig.trace_all_optical_nets = p.second.as_double();
+ else if (kw == "TEST_VARIABLE")
+ cout << kw << "(" << p.second.kind() << "): " << p.second.get_str() << endl;
+ else {
+ cerr << "Unknown keyword: " << p.first;
+ cerr << " (value: " << p.second.get_str() << " (" << p.second.kind() << "))" <<endl;
+ exit(1);
+ }
+ }
+}
+
+void NODESETDirective::print() const
+{
+ ParseDirective::print();
+ cout << "{";
+ for (const auto &x : net_assignments)
+ {
+ for (const auto &ass : x.second)
+ cout << ass.first << "(" << x.first << ") = "
+ << ass.second.get_str() << " (" << ass.second.kind() << ") , ";
+ }
+ if ( !net_assignments.empty() )
+ cout << "\b\b";
+ cout << "}" << endl;
+}
+
+void NODESETDirective::create() const
+{
+ cerr << "NODESET is disabled pending bugfixes" << endl;
+ exit(1);
+#if 0
+ map<string, pair<spx::oa_signal_type *, oa_value_type>> ic_map;
+ auto &signals = parent->circuit_nets;
+ for (const auto &x : net_assignments)
+ {
+ const auto &key = x.first;
+ if (signals.count(key) == 0)
+ {
+ cerr << "Net not found " << key << endl;
+ exit(1);
+ }
+ if (ic_map.count(key) == 0)
+ {
+ //FIXME:
+ auto sig = dynamic_cast<oa_signal_type *>(signals[key].get());
+ auto initial_val = sig->read();
+ auto sig_address = dynamic_cast<oa_signal_type *>(signals[key].get());
+ pair<oa_signal_type *, oa_value_type> assignment{sig_address, initial_val};
+ ic_map.emplace(key, assignment);
+ }
+ oa_value_type signal_val = ic_map[key].second;
+ for (const auto &assignment: x.second)
+ {
+ string attribute_name = assignment.first;
+ strutils::toupper(attribute_name);
+ const Variable &value = assignment.second;
+
+ // cout << attribute_name << endl;
+ if (attribute_name == "P" || attribute_name == "POW" || attribute_name == "POWER")
+ {
+ signal_val.m_field = polar(sqrt(value.as_double()), arg(signal_val.m_field));
+ // cout << "################" << endl;
+ // cout << signal_val << endl;
+ }
+ else if (attribute_name == "PHASE" || attribute_name == "PHI")
+ {
+ signal_val.m_field = polar(abs(signal_val.m_field), value.as_double());
+ // cout << "################" << endl;
+ // cout << signal_val << endl;
+ }
+ else if (attribute_name == "WL" || attribute_name == "LAMBDA" || attribute_name == "WAVELENGTH")
+ {
+ signal_val.setWavelength(value.as_double());
+ // cout << "################" << endl;
+ // cout << signal_val << endl;
+ }
+ else
+ {
+ cerr << "Unknown attribute" << endl;
+ exit(1);
+ }
+ }
+ specsGlobalConfig.nodeset_orders[key] = pair<oa_signal_type *, oa_value_type>(
+ dynamic_cast<oa_signal_type *>(signals[key].get())
+ , signal_val);
+ }
+#endif
+}
+
+void ICDirective::create() const
+{
+ cerr << "IC directive is not implemented" << endl;
+ exit(1);
+} \ No newline at end of file
diff --git a/src/parser/parse_directive.h b/src/parser/parse_directive.h
new file mode 100644
index 0000000..212fd4a
--- /dev/null
+++ b/src/parser/parse_directive.h
@@ -0,0 +1,68 @@
+#pragma once
+
+#include "parse_tree.h"
+
+#include <initializer_list>
+#include <utility>
+#include <memory>
+#include <string>
+#include <vector>
+#include <map>
+#include <iostream>
+
+using std::pair;
+using std::make_pair;
+using std::string;
+using std::vector;
+using std::map;
+using std::cout;
+using std::cerr;
+using std::endl;
+using std::pair;
+using std::unique_ptr;
+
+/* OPTIONS: specify simulator options */
+struct OPTIONSDirective : public ParseDirective {
+ /* Import constructor from ParseDirective */
+ using ParseDirective::ParseDirective;
+
+ virtual ParseDirective* clone() const
+ { return new OPTIONSDirective(*this); }
+ virtual void create() const;
+ virtual string kind() const
+ { return "OPTIONS"; }
+};
+
+/* NODESET: specify initial guesses */
+struct NODESETDirective : public ParseDirective {
+ typedef string net_name;
+ typedef string property_name;
+ typedef Variable property_val;
+ typedef pair<property_name, property_val> assignment;
+
+ map<net_name, vector<assignment>> net_assignments;
+
+ /* Import constructor from ParseDirective */
+ using ParseDirective::ParseDirective;
+
+ virtual void print() const;
+
+ virtual ParseDirective* clone() const
+ { return new NODESETDirective(*this); }
+ virtual void create() const;
+ virtual string kind() const
+ { return "NODESET"; }
+};
+
+/* IC: specify initial conditions options */
+// Inherit from NODESETDirective as options are the same.
+struct ICDirective : public NODESETDirective {
+ /* Import constructor from ParseDirective */
+ using NODESETDirective::NODESETDirective;
+
+ virtual ParseDirective* clone() const
+ { return new ICDirective(*this); }
+ virtual void create() const;
+ virtual string kind() const
+ { return "IC"; }
+}; \ No newline at end of file
diff --git a/src/parser/parse_element.cpp b/src/parser/parse_element.cpp
new file mode 100644
index 0000000..14de6e9
--- /dev/null
+++ b/src/parser/parse_element.cpp
@@ -0,0 +1,1266 @@
+#include "parse_element.h"
+#include "alldevices.h"
+#include "subcircuit_instance.h"
+#include "specs.h"
+#include "strutils.h"
+
+#include <string>
+
+/** ******************************************* **/
+/** Helper macros and defines **/
+/** ******************************************* **/
+#define AS_READER (false)
+#define AS_WRITER (!AS_READER)
+
+#define INSTANTIATE_AND_CONNECT_UNI(ELEM_NAME, pt_helper) \
+ELEM_NAME::element_type_base *ELEM_NAME::instantiate_and_connect_uni(ParseTreeCreationHelper &pt_helper) const
+
+#define INSTANTIATE_AND_CONNECT_BI(ELEM_NAME, pt_helper) \
+ELEM_NAME::element_type_base *ELEM_NAME::instantiate_and_connect_bi(ParseTreeCreationHelper &pt_helper) const
+
+#define INSTANTIATE(pt_helper, bidirectional) \
+\
+if (bidirectional)\
+ obj = (element_type_base *)instantiate_and_connect_bi(pt_helper);\
+else\
+ obj = (element_type_base *)instantiate_and_connect_uni(pt_helper);
+
+/** ******************************************* **/
+/** Waveguide **/
+/** ******************************************* **/
+INSTANTIATE_AND_CONNECT_UNI(WGElement, pt_helper)
+{
+ // create object with default params
+ element_type_uni *obj = new element_type_uni(name.c_str());
+
+ // connect ports
+ pt_helper.connect_uni<spx::oa_signal_type>(obj->p_in, nets[0], AS_READER);
+ pt_helper.connect_uni<spx::oa_signal_type>(obj->p_out, nets[1], AS_WRITER);
+
+ // return the module
+ return obj;
+}
+
+INSTANTIATE_AND_CONNECT_BI(WGElement, pt_helper)
+{
+ // create object with default params
+ element_type_bi *obj = new element_type_bi(name.c_str());
+
+ // Update unidirectional nets connected to the waveguide
+ if (!pt_helper.nets->at(nets[0]).bidirectional())
+ (*pt_helper.nets)[nets[0]].m_writers_count++;
+ if (!pt_helper.nets->at(nets[1]).bidirectional())
+ (*pt_helper.nets)[nets[1]].m_readers_count++;
+
+ // Bidirectional waveguide needs all nets as bidir
+ for (const auto &net : nets)
+ pt_helper.upgrade_signal(net);
+
+ // connect ports
+ pt_helper.connect_bi<spx::oa_signal_type>(obj->p0_in, obj->p0_out, nets[0]);
+ pt_helper.connect_bi<spx::oa_signal_type>(obj->p1_in, obj->p1_out, nets[1]);
+
+ return obj;
+}
+
+sc_module *WGElement::create(ParseTreeCreationHelper &pt_helper) const
+{
+ element_type_base *obj = nullptr;
+
+ // Verify number of nets
+ assert(nets.size() == n_nets);
+
+ // Prevent connecting a waveguide to itself, it will hang the execution!!!
+ if (nets[0] == nets[1])
+ {
+ cerr << "Error: A waveguide cannot be looped back upon himself" << endl;
+ exit(1);
+ }
+
+ // Create signals if they don't exist
+ pt_helper.create_signals(this);
+
+ // Check if device has to be bidirectional
+ bool bidirectional = false;
+ for (const auto &net : nets)
+ {
+ // for waveguide, if any net is bidir, module has to be bidir
+ bidirectional |= pt_helper.nets->at(net).bidirectional();
+ }
+
+ // Create the object and connect ports to signals
+ INSTANTIATE(pt_helper, bidirectional);
+
+ // Parse positional arguments
+ if(args.size() > 0)
+ obj->m_length_cm = args[0].as_double() * 100;
+ if(args.size() > 1)
+ obj->m_neff = args[1].as_double();
+ if(args.size() > 2)
+ obj->m_ng = args[2].as_double();
+ if(args.size() > 3)
+ obj->m_attenuation_dB_cm = args[3].as_double();
+ if(args.size() > 4)
+ obj->m_D = args[4].as_double();
+
+ // Parse keyword arguments
+ for (auto &p: kwargs)
+ {
+ string kw = p.first;
+ strutils::toupper(kw);
+ if (kw == "NEFF")
+ obj->m_neff = p.second.as_double();
+ else if (kw == "NG")
+ obj->m_ng = p.second.as_double();
+ else if (kw == "L" || kw == "LENGTH")
+ obj->m_length_cm = p.second.as_double() * 100;
+ else if (kw == "ATT")
+ obj->m_attenuation_dB_cm = p.second.as_double();
+ else if (kw == "D")
+ obj->m_D = p.second.as_double();
+ else {
+ cerr << "Unknown keyword: " << p.first << endl;
+ exit(1);
+ }
+ }
+
+ return obj;
+}
+
+/** ******************************************* **/
+/** Merger **/
+/** ******************************************* **/
+INSTANTIATE_AND_CONNECT_UNI(MergerElement, pt_helper)
+{
+ // create object with default params
+ element_type_uni *obj = new element_type_uni(name.c_str());
+
+ // connect ports
+ pt_helper.connect_uni<spx::oa_signal_type>(obj->p_in1, nets[0], AS_READER);
+ pt_helper.connect_uni<spx::oa_signal_type>(obj->p_in2, nets[1], AS_READER);
+ pt_helper.connect_uni<spx::oa_signal_type>(obj->p_out, nets[2], AS_WRITER);
+
+ // return the module
+ return obj;
+}
+
+sc_module *MergerElement::create(ParseTreeCreationHelper &pt_helper) const
+{
+ element_type_base *obj = nullptr;
+
+ // Verify number of nets
+ assert(nets.size() == n_nets);
+
+ // Create signals if they don't exist
+ pt_helper.create_signals(this);
+
+ // Create the object and connect ports to signals
+ INSTANTIATE(pt_helper, false);
+
+ // Parse positional arguments
+ if(args.size() > 0)
+ obj->m_attenuation_dB = args[0].as_double();
+
+ // Parse keyword arguments
+ for (auto &p: kwargs)
+ {
+ string kw = p.first;
+ strutils::toupper(kw);
+ if (kw == "IL" || kw == "INSERTION_LOSS")
+ obj->m_attenuation_dB = p.second.as_double();
+ else {
+ cerr << "Unknown keyword: " << p.first << endl;
+ exit(1);
+ }
+ }
+ return obj;
+}
+
+/** ******************************************* **/
+/** Splitter **/
+/** ******************************************* **/
+INSTANTIATE_AND_CONNECT_UNI(SplitterElement, pt_helper)
+{
+ // create object with default params
+ element_type_uni *obj = new element_type_base(name.c_str());
+
+ // connect ports
+ pt_helper.connect_uni<spx::oa_signal_type>(obj->p_in, nets[0], AS_READER);
+ pt_helper.connect_uni<spx::oa_signal_type>(obj->p_out1, nets[1], AS_WRITER);
+ pt_helper.connect_uni<spx::oa_signal_type>(obj->p_out2, nets[2], AS_WRITER);
+
+ // return the module
+ return obj;
+}
+
+sc_module *SplitterElement::create(ParseTreeCreationHelper &pt_helper) const
+{
+ element_type_base *obj = nullptr;
+
+ // Verify number of nets
+ assert(nets.size() == n_nets);
+
+ // Create signals if they don't exist
+ pt_helper.create_signals(this);
+
+ // Create the object and connect ports to signals
+ INSTANTIATE(pt_helper, false);
+
+ // Parse positional arguments
+ if(args.size() > 0)
+ obj->m_split_ratio = args[0].as_double();
+ if(args.size() > 1)
+ obj->m_attenuation_dB = args[1].as_double();
+
+ // Parse keyword arguments
+ for (auto &p: kwargs)
+ {
+ string kw = p.first;
+ strutils::toupper(kw);
+ if (kw == "IL" || kw == "INSERTION_LOSS")
+ obj->m_attenuation_dB = p.second.as_double();
+ else if (kw == "SPLITTING_RATIO" || kw == "RATIO")
+ obj->m_split_ratio = p.second.as_double();
+ else {
+ cerr << "Unknown keyword: " << p.first << endl;
+ exit(1);
+ }
+ }
+ return obj;
+}
+
+/** ******************************************* **/
+/** Directional coupler **/
+/** ******************************************* **/
+INSTANTIATE_AND_CONNECT_UNI(DCElement, pt_helper)
+{
+ // create object with default params
+ element_type_uni *obj = new element_type_uni(name.c_str());
+
+ // connect p_in1
+ pt_helper.connect_uni<spx::oa_signal_type>(obj->p_in1, nets[0], AS_READER);
+ pt_helper.connect_uni<spx::oa_signal_type>(obj->p_in2, nets[1], AS_READER);
+ pt_helper.connect_uni<spx::oa_signal_type>(obj->p_out1, nets[2], AS_WRITER);
+ pt_helper.connect_uni<spx::oa_signal_type>(obj->p_out2, nets[3], AS_WRITER);
+
+ // return the module
+ return obj;
+}
+
+INSTANTIATE_AND_CONNECT_BI(DCElement, pt_helper)
+{
+ // create object with default params
+ element_type_bi *obj = new element_type_bi(name.c_str());
+
+ // Update unidirectional nets connected to the waveguide
+ if (!pt_helper.nets->at(nets[0]).bidirectional())
+ (*pt_helper.nets)[nets[0]].m_writers_count++;
+ if (!pt_helper.nets->at(nets[1]).bidirectional())
+ (*pt_helper.nets)[nets[1]].m_writers_count++;
+ if (!pt_helper.nets->at(nets[2]).bidirectional())
+ (*pt_helper.nets)[nets[2]].m_readers_count++;
+ if (!pt_helper.nets->at(nets[3]).bidirectional())
+ (*pt_helper.nets)[nets[3]].m_readers_count++;
+
+ // Bidirectional DC needs all nets as bidir
+ for (const auto &net : nets)
+ pt_helper.upgrade_signal(net);
+
+ // connect ports
+ pt_helper.connect_bi<spx::oa_signal_type>(obj->p0_in, obj->p0_out, nets[0]);
+ pt_helper.connect_bi<spx::oa_signal_type>(obj->p1_in, obj->p1_out, nets[1]);
+ pt_helper.connect_bi<spx::oa_signal_type>(obj->p2_in, obj->p2_out, nets[2]);
+ pt_helper.connect_bi<spx::oa_signal_type>(obj->p3_in, obj->p3_out, nets[3]);
+
+ // return the module
+ return obj;
+}
+
+sc_module *DCElement::create(ParseTreeCreationHelper &pt_helper) const
+{
+ // TODO: change from field to power?
+ cout << "Warning: coupling power for DC currently set to field." << endl;
+
+ element_type_base *obj = nullptr;
+
+ // Verify number of nets
+ assert(nets.size() == n_nets);
+
+ // Create signals if they don't exist
+ pt_helper.create_signals(this);
+
+ // Check if device has to be bidirectional
+ bool bidirectional = false;
+ for (const auto &net : nets)
+ {
+ // for waveguide, if any net is bidir, module has to be bidir
+ bidirectional |= pt_helper.nets->at(net).bidirectional();
+ }
+
+ // Create the object and connect ports to signals
+ INSTANTIATE(pt_helper, bidirectional);
+
+ // Parse positional arguments
+ if(args.size() > 0)
+ // convert from cross-coupling field coef
+ obj->m_dc_through_coupling_power = 1 - pow(args[0].as_double(), 2);
+ if(args.size() > 1)
+ obj->m_dc_loss = args[1].as_double();
+
+ // Parse keyword arguments
+ for (auto &p: kwargs)
+ {
+ string kw = p.first;
+ strutils::toupper(kw);
+ if (kw == "K" || kw == "KF" || kw == "KFIELD")
+ // convert from cross-coupling field coef
+ obj->m_dc_through_coupling_power = 1 - pow(p.second.as_double(), 2);
+ else if (kw == "KP" || kw == "KPOW" || kw == "KPOWER")
+ // convert from cross-coupling field coef
+ obj->m_dc_through_coupling_power = 1 - p.second.as_double();
+ else if (kw == "T")
+ // convert from cross-coupling field coef
+ obj->m_dc_through_coupling_power = pow(p.second.as_double(), 2);
+ else if (kw == "LOSS")
+ obj->m_dc_loss = p.second.as_double();
+ else {
+ cerr << "Unknown keyword: " << p.first << endl;
+ exit(1);
+ }
+ }
+ return obj;
+}
+
+/** ******************************************* **/
+/** Phase-shifter **/
+/** ******************************************* **/
+INSTANTIATE_AND_CONNECT_UNI(PhaseShifterElement, pt_helper)
+{
+ // create object with default params
+ element_type_uni *obj = new element_type_uni(name.c_str());
+
+ // connect ports
+ pt_helper.connect_uni<spx::oa_signal_type>(obj->p_in, nets[0], AS_READER);
+ pt_helper.connect_uni<spx::oa_signal_type>(obj->p_out, nets[1], AS_WRITER);
+ pt_helper.connect_uni<spx::ea_signal_type>(obj->p_vin, nets[2], AS_READER);
+
+ // return the module
+ return obj;
+}
+
+INSTANTIATE_AND_CONNECT_BI(PhaseShifterElement, pt_helper)
+{
+ // create object with default params
+ element_type_bi *obj = new element_type_bi(name.c_str());
+
+ // Update unidirectional nets connected to the waveguide
+ if (!pt_helper.nets->at(nets[0]).bidirectional())
+ (*pt_helper.nets)[nets[0]].m_writers_count++;
+ if (!pt_helper.nets->at(nets[1]).bidirectional())
+ (*pt_helper.nets)[nets[1]].m_readers_count++;
+
+ // Bidirectional phase-shifter needs only optical nets as bidir
+ pt_helper.upgrade_signal(nets[0]);
+ pt_helper.upgrade_signal(nets[1]);
+
+ // connect ports
+ pt_helper.connect_bi<spx::oa_signal_type>(obj->p0_in, obj->p0_out, nets[0]);
+ pt_helper.connect_bi<spx::oa_signal_type>(obj->p1_in, obj->p1_out, nets[1]);
+ pt_helper.connect_uni<spx::ea_signal_type>(obj->p_vin, nets[2], AS_READER);
+
+ // return the module
+ return obj;
+}
+
+sc_module *PhaseShifterElement::create(ParseTreeCreationHelper &pt_helper) const
+{
+ element_type_base *obj = nullptr;
+
+ cout << "Warning (" << name << "): default coupling power for DC currently set to field." << endl;
+
+ // Verify number of nodes
+ assert(nets.size() == n_nets);
+
+ // Create signals if they don't exist
+ pt_helper.create_signals(this);
+
+ // Check if device has to be bidirectional
+ // for PS, if any optical net is bidir, module has to be bidir
+ bool bidirectional = false;
+ bidirectional |= pt_helper.nets->at(nets[0]).bidirectional();
+ bidirectional |= pt_helper.nets->at(nets[1]).bidirectional();
+
+ // Create the object and connect ports to signals
+ INSTANTIATE(pt_helper, bidirectional);
+
+ // Verify number of args
+ assert(args.size() <= 2);
+
+ // Parse positional arguments
+ if(args.size() > 0)
+ obj->m_sensitivity = args[0].as_double();
+ if(args.size() > 1)
+ obj->m_attenuation_dB = args[1].as_double();
+
+ // Parse keyword arguments
+ for (auto &p: kwargs)
+ {
+ string kw = p.first;
+ strutils::toupper(kw);
+
+ if (kw == "ATTENUATION" || kw == "ATT")
+ obj->m_attenuation_dB = p.second.as_double();
+ else if (kw == "SENSITIVITY" || kw == "GAIN" || kw == "G")
+ obj->m_sensitivity = p.second.as_double();
+ else {
+ cerr << "Unknown keyword: " << p.first << endl;
+ exit(1);
+ }
+ }
+ return obj;
+}
+
+/** ******************************************* **/
+/** MZI **/
+/** ******************************************* **/
+INSTANTIATE_AND_CONNECT_UNI(MZIElement, pt_helper)
+{
+ // create object with default params
+ element_type_uni *obj = new element_type_uni(name.c_str());
+
+ // connect ports
+ pt_helper.connect_uni<spx::oa_signal_type>(obj->p_in1, nets[0], AS_READER);
+ pt_helper.connect_uni<spx::oa_signal_type>(obj->p_in2, nets[1], AS_READER);
+ pt_helper.connect_uni<spx::oa_signal_type>(obj->p_out1, nets[2], AS_WRITER);
+ pt_helper.connect_uni<spx::oa_signal_type>(obj->p_out2, nets[3], AS_WRITER);
+ pt_helper.connect_uni<spx::ea_signal_type>(obj->p_vin, nets[4], AS_READER);
+
+ return obj;
+}
+
+INSTANTIATE_AND_CONNECT_BI(MZIElement, pt_helper)
+{
+ // create object with default params
+ element_type_bi *obj = new element_type_bi(name.c_str());
+
+ // Update unidirectional nets connected to the waveguide
+ if (!pt_helper.nets->at(nets[0]).bidirectional())
+ (*pt_helper.nets)[nets[0]].m_writers_count++;
+ if (!pt_helper.nets->at(nets[1]).bidirectional())
+ (*pt_helper.nets)[nets[1]].m_writers_count++;
+ if (!pt_helper.nets->at(nets[2]).bidirectional())
+ (*pt_helper.nets)[nets[2]].m_readers_count++;
+ if (!pt_helper.nets->at(nets[3]).bidirectional())
+ (*pt_helper.nets)[nets[3]].m_readers_count++;
+
+ // Bidirectional phase-shifter needs only optical nets as bidir
+ pt_helper.upgrade_signal(nets[0]);
+ pt_helper.upgrade_signal(nets[1]);
+ pt_helper.upgrade_signal(nets[2]);
+ pt_helper.upgrade_signal(nets[3]);
+
+ // connect ports
+ pt_helper.connect_bi<spx::oa_signal_type>(obj->p0_in, obj->p0_out, nets[0]);
+ pt_helper.connect_bi<spx::oa_signal_type>(obj->p1_in, obj->p1_out, nets[1]);
+ pt_helper.connect_bi<spx::oa_signal_type>(obj->p2_in, obj->p2_out, nets[2]);
+ pt_helper.connect_bi<spx::oa_signal_type>(obj->p3_in, obj->p3_out, nets[3]);
+ pt_helper.connect_uni<spx::ea_signal_type>(obj->p_vin, nets[4], AS_READER);
+
+ return obj;
+}
+
+sc_module *MZIElement::create(ParseTreeCreationHelper &pt_helper) const
+{
+ element_type_base *obj = nullptr;
+
+ cout << "Warning (" << name << "): default coupling power for DC currently set to field." << endl;
+
+ // Verify number of nodes
+ assert(nets.size() == n_nets);
+
+ // Create signals if they don't exist
+ pt_helper.create_signals(this);
+
+ // Check if device has to be bidirectional
+ // for PS, if any optical net is bidir, module has to be bidir
+ bool bidirectional = false;
+ bidirectional |= pt_helper.nets->at(nets[0]).bidirectional();
+ bidirectional |= pt_helper.nets->at(nets[1]).bidirectional();
+
+ // Create the object and connect ports to signals
+ INSTANTIATE(pt_helper, bidirectional);
+
+ assert(args.size() <= 1);
+ // Parse positional arguments
+ if(args.size() > 0)
+ // convert from cross-coupling field coef
+ obj->m_ps_sens_rad_v = args[0].as_double();
+ // TODO: other positional args
+
+ // Parse keyword arguments
+ for (auto &p: kwargs)
+ {
+ string kw = p.first;
+ strutils::toupper(kw);
+ if (kw == "SENSITIVITY")
+ // convert from cross-coupling field coef
+ obj->m_ps_sens_rad_v = p.second.as_double();
+ else if (kw == "LENGTH")
+ obj->m_length_cm = 100 * p.second.as_double();
+ else if (kw == "LENGTH_REF")
+ obj->m_length_ref_cm = 100 * p.second.as_double();
+ else if (kw == "NEFF")
+ obj->m_neff = p.second.as_double();
+ else if (kw == "NG")
+ obj->m_ng = p.second.as_double();
+ else if (kw == "ATT")
+ obj->m_attenuation_dB_cm = p.second.as_double();
+ else if (kw == "IL_DC")
+ obj->m_dc_loss_dB = p.second.as_double();
+ else if (kw == "IL_PHASESHIFTER")
+ obj->m_ps_loss_dB = p.second.as_double();
+ else {
+ cerr << "Unknown keyword: " << p.first << endl;
+ exit(1);
+ }
+ }
+ return obj;
+}
+
+/** ******************************************* **/
+/** Crossing **/
+/** ******************************************* **/
+INSTANTIATE_AND_CONNECT_UNI(CrossingElement, pt_helper)
+{
+ element_type_uni *obj = new element_type_uni(name.c_str());
+
+ // connect ports
+ pt_helper.connect_uni<spx::oa_signal_type>(obj->p_in1, nets[0], AS_READER);
+ pt_helper.connect_uni<spx::oa_signal_type>(obj->p_in2, nets[1], AS_READER);
+ pt_helper.connect_uni<spx::oa_signal_type>(obj->p_out1, nets[2], AS_READER);
+ pt_helper.connect_uni<spx::oa_signal_type>(obj->p_out2, nets[3], AS_READER);
+
+ // return the module
+ return obj;
+}
+
+INSTANTIATE_AND_CONNECT_BI(CrossingElement, pt_helper)
+{
+ element_type_bi *obj = new element_type_bi(name.c_str());
+
+ // Update unidirectional nets connected to the waveguide
+ if (!pt_helper.nets->at(nets[0]).bidirectional())
+ (*pt_helper.nets)[nets[0]].m_writers_count++;
+ if (!pt_helper.nets->at(nets[1]).bidirectional())
+ (*pt_helper.nets)[nets[1]].m_writers_count++;
+ if (!pt_helper.nets->at(nets[2]).bidirectional())
+ (*pt_helper.nets)[nets[2]].m_readers_count++;
+ if (!pt_helper.nets->at(nets[3]).bidirectional())
+ (*pt_helper.nets)[nets[3]].m_readers_count++;
+
+ // Bidirectional crossing needs all optical nets as bidir
+ pt_helper.upgrade_signal(nets[0]);
+ pt_helper.upgrade_signal(nets[1]);
+ pt_helper.upgrade_signal(nets[2]);
+ pt_helper.upgrade_signal(nets[3]);
+
+ // connect ports
+ pt_helper.connect_bi<spx::oa_signal_type>(obj->p0_in, obj->p0_out, nets[0]);
+ pt_helper.connect_bi<spx::oa_signal_type>(obj->p1_in, obj->p1_out, nets[1]);
+ pt_helper.connect_bi<spx::oa_signal_type>(obj->p2_in, obj->p2_out, nets[2]);
+ pt_helper.connect_bi<spx::oa_signal_type>(obj->p3_in, obj->p3_out, nets[3]);
+
+ // return the module
+ return obj;
+}
+
+sc_module *CrossingElement::create(ParseTreeCreationHelper &pt_helper) const
+{
+ element_type_base *obj = nullptr;
+
+ // Verify number of nets
+ assert(nets.size() == n_nets);
+
+ // Verify number of positional args
+ assert(args.size() <= 2);
+
+ // Create signals if they don't exist
+ pt_helper.create_signals(this);
+
+ // Create the object and connect ports to signals
+ INSTANTIATE(pt_helper, false);
+
+ // Parse positional arguments
+ if(args.size() > 0)
+ obj->m_attenuation_power_dB = args[0].as_double();
+ if(args.size() > 1)
+ obj->m_crosstalk_power_dB = args[1].as_double();
+
+ // Parse keyword arguments
+ for (auto &p: kwargs)
+ {
+ string kw = p.first;
+ strutils::toupper(kw);
+ if (kw == "ATTENUATION" || kw == "ATT")
+ obj->m_attenuation_power_dB = p.second.as_double();
+ else if (kw == "CROSSTALK" || kw == "XTALK")
+ obj->m_crosstalk_power_dB = p.second.as_double();
+ else {
+ cerr << "Unknown keyword: " << p.first << endl;
+ exit(1);
+ }
+ }
+ return obj;
+}
+
+/** ******************************************* **/
+/** CW Source **/
+/** ******************************************* **/
+INSTANTIATE_AND_CONNECT_UNI(CWSourceElement, pt_helper)
+{
+ element_type_uni *obj = new element_type_uni(name.c_str());
+
+ // connect ports
+ pt_helper.connect_uni<spx::oa_signal_type>(obj->p_out, nets[0], AS_WRITER);
+
+ // return the module
+ return obj;
+}
+
+sc_module *CWSourceElement::create(ParseTreeCreationHelper &pt_helper) const
+{
+ CWSource *obj = nullptr;
+
+ // Verify number of nets
+ assert(nets.size() == 1);
+
+ // Verify number of positional args
+ assert(args.size() <= 3);
+
+ // Create signals if they don't exist
+ pt_helper.create_signals(this);
+
+ // Create the object and connect ports to signals
+ INSTANTIATE(pt_helper, false);
+
+ // Parse positional arguments
+ if(args.size() > 1)
+ // convert from cross-coupling field coef
+ obj->setWavelength(args[1].as_double());
+ if(args.size() > 2)
+ obj->setPower(args[2].as_double());
+ if(args.size() > 3)
+ obj->setPhase(args[3].as_double());
+
+ // Parse keyword arguments
+ for (auto &p: kwargs)
+ {
+ string kw = p.first;
+ strutils::toupper(kw);
+ if (kw == "WL" || kw == "WAVELENGTH")
+ // convert from cross-coupling field coef
+ obj->setWavelength(p.second.as_double());
+ else if (kw == "POWER")
+ // convert from cross-coupling field coef
+ obj->setPower(p.second.as_double());
+ else if (kw == "PHI")
+ continue; // handle it after all power commands are executed
+ else {
+ cerr << "Unknown keyword: " << p.first << endl;
+ exit(1);
+ }
+ }
+ // TODO: make this cleaner (priority between setting kwargs...)
+ for (auto &p: kwargs)
+ {
+ string kw = p.first;
+ strutils::toupper(kw);
+ if (kw == "PHI")
+ obj->setPhase(p.second.as_double());
+ }
+ return obj;
+}
+
+/** ******************************************* **/
+/** VL Source **/
+/** ******************************************* **/
+INSTANTIATE_AND_CONNECT_UNI(VLSourceElement, pt_helper)
+{
+ element_type_uni *obj = new element_type_uni(name.c_str());
+
+ // connect ports
+ pt_helper.connect_uni<spx::oa_signal_type>(obj->p_out, nets[0], AS_WRITER);
+
+ // return the module
+ return obj;
+}
+
+sc_module *VLSourceElement::create(ParseTreeCreationHelper &pt_helper) const
+{
+ VLSource *obj = nullptr;
+
+ // Verify number of nets
+ assert(nets.size() == n_nets);
+
+ // Verify number of positional args
+ assert(args.size() <= 1);
+
+ // Create signals if they don't exist
+ pt_helper.create_signals(this);
+
+ // Create the object and connect ports to signals
+ INSTANTIATE(pt_helper, false);
+
+ // Parse positional arguments
+ if(args.size() > 1)
+ obj->setValues(args[0].as_string());
+
+ // Parse keyword arguments
+ for (auto &p: kwargs)
+ {
+ string kw = p.first;
+ strutils::toupper(kw);
+ if (kw == "FILE")
+ {
+ cerr << "not implemented" << endl;
+ exit(1);
+ }
+ else if (kw == "SCALE")
+ {
+ cerr << "not implemented" << endl;
+ exit(1);
+ }
+ else if (kw == "VALUES")
+ {
+ // cout << "here" << endl;
+ vector<VLSource::time_value_pair_type> values;
+ bool is_string = p.second.type == Variable::STRING;
+ bool is_list = p.second.type == Variable::LIST;
+ if (!is_list && !is_string)
+ {
+ // cout << "A" << endl;
+ cerr << "Variable passed to keyword 'values' should be either:"<< endl;
+ cerr << " - an Nx3 list [[t0, p0, wl0], ...] with t0, p0 and wl0 convertible to double"
+ << " (or None if no change from previous entry)" << endl;
+ cerr << " - a filename containing the list" << endl;
+ exit(1);
+ }
+
+ if (is_list)
+ {
+ for (const Variable &var : p.second.vec)
+ {
+ if (var.type != Variable::LIST || var.vec.size() != 3)
+ {
+ // cout << "B" << endl;
+ cerr << "Variable passed to keyword 'values' should be a an Nx3 list [[t0, p0, wl0], ...]" << endl;
+ cerr << "with t0, p0 and wl0 convertible to double (or None if no change from previous entry)" << endl;
+ exit(1);
+ }
+ for (const Variable &ivar : var.vec)
+ {
+ // cout << "C" << ivar.get_str() << endl;
+
+ if (ivar.is_number() || ivar.is_none())
+ continue;
+
+ cerr << "Variable passed to keyword 'values' should be a an Nx3 list [[t0, p0, wl0], ...]" << endl;
+ cerr << "with t0, p0 and wl0 convertible to double (or None if no change from previous entry)" << endl;
+ exit(1);
+ }
+ double t, p, wl;
+ int none_cnt = 0;
+ if (var.vec[0].is_number())
+ t = var.vec[0].as_double();
+ else if (var.vec[0].is_none() && ! values.empty())
+ {
+ t = values.back().first;
+ ++none_cnt;
+ }
+ else
+ {
+ cerr << "First values list entry should be fully specified" << endl;
+ exit(1);
+ }
+
+ if (var.vec[1].is_number())
+ p = var.vec[1].as_double();
+ else if (var.vec[1].is_none() && ! values.empty())
+ {
+ p = values.back().second.power();
+ ++none_cnt;
+ }
+ else
+ {
+ cerr << "First values list entry should be fully specified" << endl;
+ exit(1);
+ }
+
+ if (var.vec[2].is_number())
+ wl = var.vec[2].as_double();
+ else if (var.vec[2].is_none() && ! values.empty())
+ {
+ wl = values.back().second.getWavelength();
+ ++none_cnt;
+ }
+ else
+ {
+ cerr << "First values list entry should be fully specified" << endl;
+ exit(1);
+ }
+
+ if (none_cnt < 3)
+ // OpticalSignal takes amplitude as value in the constructor
+ values.emplace_back(t, spx::oa_value_type(sqrt(p), wl));
+ else
+ {
+ cerr << "Values list entry cannot be all none" << endl;
+ }
+ }
+ obj->setValues(values);
+ }
+
+ if (is_string)
+ {
+ obj->setValues(p.second.as_string());
+ }
+ }
+ else {
+ cerr << "Unknown keyword: " << p.first << endl;
+ exit(1);
+ }
+
+ }
+ return obj;
+}
+
+/** ******************************************* **/
+/** EVL Source **/
+/** ******************************************* **/
+INSTANTIATE_AND_CONNECT_UNI(EVLSourceElement, pt_helper)
+{
+ element_type_uni *obj = new element_type_uni(name.c_str());
+
+ // connect ports
+ pt_helper.connect_uni<spx::ea_signal_type>(obj->p_out, nets[0], AS_WRITER);
+
+ // return the module
+ return obj;
+}
+
+sc_module *EVLSourceElement::create(ParseTreeCreationHelper &pt_helper) const
+{
+ EVLSource *obj = nullptr;
+
+ // Verify number of nets
+ assert(nets.size() == n_nets);
+
+ // Verify number of positional args
+ assert(args.size() <= 1);
+
+ // Create signals if they don't exist
+ pt_helper.create_signals(this);
+
+ // Create the object and connect ports to signals
+ INSTANTIATE(pt_helper, false);
+
+ // Parse positional arguments
+ if(args.size() > 1)
+ obj->setValues(args[0].as_string());
+
+ // Parse keyword arguments
+ for (auto &p: kwargs)
+ {
+ string kw = p.first;
+ strutils::toupper(kw);
+ if (kw == "FILE")
+ {
+ cerr << "not implemented" << endl;
+ exit(1);
+ }
+ else if (kw == "SCALE")
+ {
+ cerr << "not implemented" << endl;
+ exit(1);
+ }
+ else if (kw == "VALUES")
+ {
+ // cout << "here" << endl;
+ vector<EVLSource::time_value_pair_type> values;
+ bool is_string = p.second.type == Variable::STRING;
+ bool is_list = p.second.type == Variable::LIST;
+ if (!is_list && !is_string)
+ {
+ cerr << "Variable passed to keyword 'values' should be either:"<< endl;
+ cerr << " - an Nx2 list [[t0, V0], ...] with t0 and V0 convertible to double"
+ << " (or None if no change from previous entry)" << endl;
+ cerr << " - a filename containing the list" << endl;
+ exit(1);
+ }
+
+ if (is_list)
+ {
+ for (const Variable &var : p.second.vec)
+ {
+ if (var.type != Variable::LIST || var.vec.size() != 2)
+ {
+ cerr << "Variable passed to keyword 'values' should be either:"<< endl;
+ cerr << " - an Nx2 list [[t0, V0], ...] with t0 and V0 convertible to double"
+ << " (or None if no change from previous entry)" << endl;
+ cerr << " - a filename containing the list" << endl;
+ exit(1);
+ }
+ for (const Variable &ivar : var.vec)
+ {
+ if (ivar.is_number() || ivar.is_none())
+ continue;
+
+ cerr << "Variable passed to keyword 'values' should be either:"<< endl;
+ cerr << " - an Nx2 list [[t0, V0], ...] with t0 and V0 convertible to double"
+ << " (or None if no change from previous entry)" << endl;
+ cerr << " - a filename containing the list" << endl;
+ exit(1);
+ }
+ double t, V;
+ int none_cnt = 0;
+ if (var.vec[0].is_number())
+ t = var.vec[0].as_double();
+ else if (var.vec[0].is_none() && ! values.empty())
+ {
+ t = values.back().first;
+ ++none_cnt;
+ }
+ else
+ {
+ cerr << "First values list entry should be fully specified" << endl;
+ exit(1);
+ }
+
+ if (var.vec[1].is_number())
+ V = var.vec[1].as_double();
+ else if (var.vec[1].is_none() && ! values.empty())
+ {
+ V = values.back().second;
+ ++none_cnt;
+ }
+ else
+ {
+ cerr << "First values list entry should be fully specified" << endl;
+ exit(1);
+ }
+
+ if (none_cnt < 2)
+ values.emplace_back(t, spx::ea_value_type(V));
+ else
+ {
+ cerr << "Values list entry cannot be all none" << endl;
+ }
+ }
+ obj->setValues(values);
+ }
+
+ if (is_string)
+ {
+ obj->setValues(p.second.as_string());
+ }
+ }
+ else {
+ cerr << "Unknown keyword: " << p.first << endl;
+ exit(1);
+ }
+
+ }
+ return obj;
+}
+
+/** ******************************************* **/
+/** Probe **/
+/** ******************************************* **/
+INSTANTIATE_AND_CONNECT_UNI(ProbeElement, pt_helper)
+{
+ element_type_uni *obj = new element_type_uni(name.c_str());
+
+ // connect p_in
+ pt_helper.connect_uni<spx::oa_signal_type>(
+ obj->p_in,
+ nets[0],
+ AS_READER
+ );
+
+ // return the module
+ return obj;
+}
+
+sc_module *ProbeElement::create(ParseTreeCreationHelper &pt_helper) const
+{
+ Probe *obj = nullptr;
+
+ // Verify number of nets
+ assert(nets.size() == n_nets);
+
+ // Verify number of positional args
+ assert(args.size() <= 4);
+
+ // Create signals if they don't exist
+ pt_helper.create_signals(this);
+
+ // Create the object and connect ports to signals
+ INSTANTIATE(pt_helper, false);
+
+ if (args.empty() && kwargs.empty())
+ {
+ // cout << "empty args" << endl;
+ obj->m_trace_power = true;
+ obj->m_trace_modulus = true;
+ obj->m_trace_phase = true;
+ obj->m_trace_wavelength = true;
+ }
+
+ // Parse positional args
+ for (size_t i = 0; i < args.size(); ++i)
+ {
+ switch (i) {
+ case 0:
+ obj->m_trace_power = args[i].as_boolean();
+ break;
+ case 1:
+ obj->m_trace_modulus = args[i].as_boolean();
+ break;
+ case 2:
+ obj->m_trace_phase = args[i].as_boolean();
+ break;
+ case 3:
+ obj->m_trace_wavelength = args[i].as_boolean();
+ break;
+ default:
+ cerr << "Too many input arguments for " << name << endl;
+ exit(1);
+ }
+ }
+
+ // Parse keyword arguments
+ for (auto &p: kwargs)
+ {
+ string kw = p.first;
+ strutils::toupper(kw);
+ if (kw == "POWER" || kw == "POW" || kw == "P")
+ obj->m_trace_power = p.second.as_boolean();
+ else if (kw == "MAGNITUDE" || kw == "MAG" || kw == "MODULUS" || kw == "MOD" || kw == "E")
+ obj->m_trace_modulus = p.second.as_boolean();
+ else if (kw == "PHASE" || kw == "PHI")
+ obj->m_trace_phase = p.second.as_boolean();
+ else if (kw == "WAVELENGTH" || kw == "WL")
+ obj->m_trace_wavelength = p.second.as_boolean();
+ else {
+ cerr << name << ": unknown keyword: " << p.first << endl;
+ exit(1);
+ }
+ }
+ cout<< "created " << obj->name() << endl;
+ return obj;
+}
+
+/** ******************************************* **/
+/** Multilambda Probe **/
+/** ******************************************* **/
+INSTANTIATE_AND_CONNECT_UNI(MLProbeElement, pt_helper)
+{
+ element_type_uni *obj = new element_type_uni(name.c_str());
+
+ // connect p_in
+ pt_helper.connect_uni<spx::oa_signal_type>(obj->p_in, nets[0], AS_READER);
+
+ // return the module
+ return obj;
+}
+
+sc_module *MLProbeElement::create(ParseTreeCreationHelper &pt_helper) const
+{
+ element_type_base *obj = nullptr;
+
+ // Verify number of nets
+ assert(nets.size() == n_nets);
+
+ // Verify number of positional args
+ assert(args.size() <= 1);
+
+ // Create signals if they don't exist
+ pt_helper.create_signals(this);
+
+ // Create the object and connect ports to signals
+ INSTANTIATE(pt_helper, false);
+
+ // Parse positional args
+ if (args.size() > 0)
+ {
+ if (args[0].is_list())
+ {
+ for(auto v: args[0].vec)
+ {
+ obj->m_lambdas.insert(v.as_double());
+ }
+ }
+ else {
+ cerr << "Positional arg to MLPROBE should be a list of wavelengths (e.g. [1.55e-6, 1.54e-6,...])" << endl;
+ exit(1);
+ }
+ }
+
+ // Parse keyword arguments
+ for (auto &p: kwargs)
+ {
+ string kw = p.first;
+ strutils::toupper(kw);
+ if (kw == "WAVELENGTHS" || kw == "WL") {
+ if (p.second.is_list())
+ {
+ for(auto v: p.second.vec)
+ {
+ obj->m_lambdas.insert(v.as_double());
+ //obj->m_lambdas.push_back(v.as_double());
+ }
+ }
+ else {
+ cerr << "value of kwarg \"" << kw << "\" to MLPROBE should be a list of wavelengths (e.g. [1.55e-6, 1.54e-6,...])" << endl;
+ exit(1);
+ }
+ } else {
+ cerr << name << ": unknown keyword: " << p.first << endl;
+ exit(1);
+ }
+ }
+
+ return obj;
+}
+
+/** ******************************************* **/
+/** Photodetector **/
+/** ******************************************* **/
+INSTANTIATE_AND_CONNECT_UNI(PCMCellElement, pt_helper)
+{
+ element_type_uni *obj = new element_type_uni(name.c_str());
+
+ // connect ports
+ pt_helper.connect_uni<spx::oa_signal_type>(obj->p_in, nets[0], AS_READER);
+ pt_helper.connect_uni<spx::oa_signal_type>(obj->p_out, nets[1], AS_WRITER);
+
+ // return the module
+ return obj;
+}
+
+sc_module *PCMCellElement::create(ParseTreeCreationHelper &pt_helper) const
+{
+ element_type_base *obj = nullptr;
+
+ // Verify number of nets
+ assert(nets.size() == n_nets);
+
+ // Verify number of positional args
+ assert(args.size() <= 2);
+
+ // Create signals if they don't exist
+ pt_helper.create_signals(this);
+
+ // Create the object and connect ports to signals
+ INSTANTIATE(pt_helper, false);
+
+ // Parse positional arguments
+ if(args.size() > 0)
+ obj->m_meltEnergy = args[0].as_double();
+ if(args.size() > 1)
+ obj->m_nStates = args[1].as_integer();
+
+ // Parse keyword arguments
+ for (auto &p: kwargs)
+ {
+ string kw = p.first;
+ strutils::toupper(kw);
+ if (kw == "MELT_ENERGY" || kw == "EMELT")
+ obj->m_meltEnergy = p.second.as_double();
+ else if (kw == "N" || kw == "NSTATES")
+ obj->m_nStates = p.second.as_integer();
+ else {
+ cerr << "Unknown keyword: " << p.first << endl;
+ exit(1);
+ }
+ }
+ if (obj->m_meltEnergy == 0 || obj->m_nStates == 0)
+ {
+ cerr << "PCM Cell needs values for both Emelt and Nstates" << endl;
+ exit(1);
+ }
+ cout << "-----" << endl;
+ return obj;
+}
+
+
+/** ******************************************* **/
+/** Photodetector **/
+/** ******************************************* **/
+INSTANTIATE_AND_CONNECT_UNI(PhotodetectorElement, pt_helper)
+{
+ element_type_uni *obj = new element_type_uni(name.c_str());
+
+ // connect ports
+ pt_helper.connect_uni<spx::oa_signal_type>(obj->p_in, nets[0], AS_READER);
+ pt_helper.connect_uni<spx::ea_signal_type>(obj->p_readout, nets[1], AS_WRITER);
+
+ // return the module
+ return obj;
+}
+
+sc_module *PhotodetectorElement::create(ParseTreeCreationHelper &pt_helper) const
+{
+ element_type_base *obj = nullptr;
+
+ // Verify number of nets
+ assert(nets.size() == n_nets);
+
+ // Verify number of positional args
+ assert(args.size() <= 2);
+
+ // Create signals if they don't exist
+ pt_helper.create_signals(this);
+
+ // Create the object and connect ports to signals
+ INSTANTIATE(pt_helper, false);
+
+ // Parse positional arguments
+ if(args.size() > 0)
+ obj->m_sampling_time = args[0].as_double();
+ if(args.size() > 1)
+ obj->m_responsivity_A_W = args[1].as_double();
+
+ // Parse keyword arguments
+ for (auto &p: kwargs)
+ {
+ string kw = p.first;
+ strutils::toupper(kw);
+ if (kw == "SAMPLING_TIME" || kw == "TS")
+ obj->m_sampling_time = p.second.as_double();
+ else if (kw == "R" || kw == "RESPONSIVITY" || kw == "GAIN")
+ obj->m_responsivity_A_W = p.second.as_double();
+ else {
+ cerr << "Unknown keyword: " << p.first << endl;
+ exit(1);
+ }
+ }
+ return obj;
+}
+
+/** ******************************************* **/
+/** Subcircuit instance **/
+/** ******************************************* **/
+INSTANTIATE_AND_CONNECT_UNI(XElement, pt_helper)
+{
+ (void)pt_helper;
+ return nullptr;
+}
+
+sc_module *XElement::create(ParseTreeCreationHelper &pt_helper) const
+{
+ (void)pt_helper;
+ cerr << "X element should never be called directly" << endl;
+ exit(1);
+ return nullptr;
+}
diff --git a/src/parser/parse_element.h b/src/parser/parse_element.h
new file mode 100644
index 0000000..58ad694
--- /dev/null
+++ b/src/parser/parse_element.h
@@ -0,0 +1,104 @@
+#pragma once
+
+#include "alldevices.h"
+#include "subcircuit_instance.h"
+#include "parse_tree.h"
+#include "specs.h"
+
+#include <initializer_list>
+#include <utility>
+#include <memory>
+#include <string>
+#include <vector>
+#include <map>
+#include <iostream>
+
+using std::pair;
+using std::make_pair;
+using std::string;
+using std::vector;
+using std::map;
+using std::cout;
+using std::cerr;
+using std::endl;
+using std::pair;
+using std::unique_ptr;
+
+/** ******************************************* **/
+/** Helper macros for declaring devices **/
+/** ******************************************* **/
+#define DECLARE_UNIDIR_ELEMENT(ELEM_NAME, ELEM_NAME_LONG, MOD_CLASS_PREFIX, N_NETS) \
+ struct ELEM_NAME : public ParseElement { \
+ typedef MOD_CLASS_PREFIX element_type_base; \
+ typedef MOD_CLASS_PREFIX element_type_uni; \
+ \
+ const size_t n_nets = N_NETS; \
+ \
+ /* Import constructor from ParseElement */ \
+ using ParseElement::ParseElement; \
+ \
+ virtual ParseElement *clone() const \
+ { return new ELEM_NAME(*this); } \
+ /* Implement virtual function create(...) to construct element */ \
+ virtual sc_module *create(ParseTreeCreationHelper &pt_helper) const; \
+ virtual element_type_base * \
+ instantiate_and_connect_uni(ParseTreeCreationHelper &pt_helper) const; \
+ \
+ virtual string kind() const { return ELEM_NAME_LONG; } \
+ };
+
+/*************************************************/
+
+#define DECLARE_BIDIR_ELEMENT(ELEM_NAME, ELEM_NAME_LONG, MOD_CLASS_PREFIX, N_NETS) \
+ \
+ struct ELEM_NAME : public ParseElement { \
+ typedef MOD_CLASS_PREFIX##Base element_type_base; \
+ typedef MOD_CLASS_PREFIX##Uni element_type_uni; \
+ typedef MOD_CLASS_PREFIX##Bi element_type_bi; \
+ \
+ const size_t n_nets = N_NETS; \
+ \
+ /* Import constructor from ParseElement */ \
+ using ParseElement::ParseElement; \
+ \
+ virtual ParseElement *clone() const \
+ { return new ELEM_NAME(*this); } \
+ /* Implement virtual function create(...) to construct element */ \
+ virtual sc_module *create(ParseTreeCreationHelper &pt_helper) const; \
+ virtual element_type_base * \
+ instantiate_and_connect_uni(ParseTreeCreationHelper &pt_helper) const; \
+ virtual element_type_base * \
+ instantiate_and_connect_bi(ParseTreeCreationHelper &pt_helper) const; \
+ \
+ virtual string kind() const { return string(ELEM_NAME_LONG) + " (bidir)"; } \
+ };
+
+/** ******************************************* **/
+/** Devices declarations **/
+/** ******************************************* **/
+DECLARE_BIDIR_ELEMENT(WGElement, "WAVEGUIDE", Waveguide, 2);
+DECLARE_BIDIR_ELEMENT(DCElement, "DIRECTIONAL COUPLER", DirectionalCoupler, 4);
+DECLARE_UNIDIR_ELEMENT(MergerElement, "MERGER", Merger, 3);
+DECLARE_UNIDIR_ELEMENT(SplitterElement, "SPLITTER", Splitter, 3);
+DECLARE_BIDIR_ELEMENT(PhaseShifterElement, "PHASE SHIFTER", PhaseShifter, 3);
+DECLARE_BIDIR_ELEMENT(MZIElement, "MZI MODULATOR", MZIActive, 5);
+//DECLARE_UNIDIR_ELEMENT(MZIElement, "MZI MODULATOR (2 PHASE-SHIFTER)", MZI, 6);
+DECLARE_BIDIR_ELEMENT(CrossingElement, "CROSSING", Crossing, 4);
+DECLARE_UNIDIR_ELEMENT(CWSourceElement, "CW SOURCE", CWSource, 1);
+DECLARE_UNIDIR_ELEMENT(VLSourceElement, "VALUE LIST SOURCE (OPTICAL)", VLSource, 1);
+DECLARE_UNIDIR_ELEMENT(EVLSourceElement, "VALUE LIST SOURCE (ELECTRICAL)", EVLSource, 1);
+
+// TODO: make the following bidirectional
+DECLARE_UNIDIR_ELEMENT(PCMCellElement, "PCM CELL", PCMElement, 2);
+DECLARE_UNIDIR_ELEMENT(PhotodetectorElement, "PHOTODETECTOR", Detector, 2);
+DECLARE_UNIDIR_ELEMENT(ProbeElement, "PROBE", Probe, 1);
+DECLARE_UNIDIR_ELEMENT(MLProbeElement, "MULTIWAVELENGTH PROBE", MLambdaProbe, 1);
+
+// TODO: take care of subcircuit instance...
+DECLARE_UNIDIR_ELEMENT(XElement, "SUBCIRCUIT", SubcircuitInstance, 1);
+
+/** ******************************************* **/
+/** Undefine macros **/
+/** ******************************************* **/
+#undef DECLARE_UNIDIR_ELEMENT
+#undef DECLARE_BIDIR_ELEMENT \ No newline at end of file
diff --git a/src/parser/parse_tree.cpp b/src/parser/parse_tree.cpp
new file mode 100644
index 0000000..76c67b0
--- /dev/null
+++ b/src/parser/parse_tree.cpp
@@ -0,0 +1,972 @@
+#include "parse_tree.h"
+#include "parse_directive.h"
+#include "parse_element.h"
+#include "specs.h"
+
+#include <sstream>
+#include <iomanip>
+
+using std::ostringstream;
+
+using spx::oa_value_type;
+using spx::ea_value_type;
+using spx::ed_value_type;
+
+using spx::ed_bus_type;
+using spx::oa_signal_type;
+using spx::ea_signal_type;
+using spx::ed_signal_type;
+using spx::ed_bus_type;
+
+#include "../build/parser/parser.tab.h"
+#include "../build/parser/parser.yy.h"
+
+vector<shared_ptr<sc_object>> ParseNet::create(const string &name, bool force_bidir) const
+{
+ if ( !bidirectional() && !force_bidir )
+ return { create_uni(name) };
+ else
+ {
+ assert(type() == OANALOG && "Only optical nets can be bidirectional for now");
+ return { create_uni((name + "_0").c_str()), create_uni((name + "_1").c_str()) };
+ }
+}
+
+shared_ptr<sc_object> ParseNet::create_uni(const string &name) const
+{
+ switch (m_type)
+ {
+ case OANALOG:
+ return { make_shared<spx::oa_signal_type>(name.c_str()) };
+ case EANALOG:
+ return { make_shared<spx::ea_signal_type>(name.c_str()) };
+ case EDIGITAL:
+ if (m_size == 1)
+ return { make_shared<spx::ed_signal_type>(name.c_str()) };
+ else
+ return { make_shared<spx::ed_bus_type>(name.c_str(), sc_lv_base(m_size)) };
+ default:
+ cerr << "Unknown net type: " << name << endl;
+ exit(1);
+ }
+ return { nullptr };
+}
+
+string ParseNet::name_from_id(int id)
+{
+ assert(id >= 0);
+ ostringstream ss;
+ ss << "N" << std::setfill('0') << std::setw(5) << id;
+ return ss.str();
+}
+
+// return next bidir net which doesn't have a corresponding signal in circuit_signals
+map<string, ParseNet>::iterator ParseTreeCreationHelper::next_fresh_bidir_net()
+{
+ for (auto it = pt->nets.begin(); it != pt->nets.end(); ++it)
+ {
+ const auto &net = *it;
+
+ // check if net is bidirectional
+ if (!net.second.bidirectional())
+ continue;
+
+ // check if net has a corresponding signal instanciated
+ auto it_sig = find_if(circuit_signals.cbegin(), circuit_signals.cend(), [&it](const auto &p)
+ { return p.first == it->first; }
+ );
+ if (it_sig != circuit_signals.cend())
+ continue;
+
+ // check if net is unbound
+ auto it_elem = next_fresh_element_bound_to(it->first);
+ if (it_elem == pt->elements.cend())
+ continue;
+
+ return it;
+ }
+ return pt->nets.end();
+}
+
+// return next bidir net which doesn't have a corresponding signal in circuit_signals
+map<string, ParseNet>::iterator ParseTreeCreationHelper::next_fresh_net()
+{
+ for (auto it = pt->nets.begin(); it != pt->nets.end(); ++it)
+ {
+ // check if net has a corresponding signal
+ auto it_sig = find_if(circuit_signals.cbegin(), circuit_signals.cend(), [&it](const auto &p)
+ { return p.first == it->first; }
+ );
+ if (it_sig != circuit_signals.cend())
+ continue;
+
+ // check if net is unbound
+ auto it_elem = next_fresh_element_bound_to(it->first);
+ if (it_elem == pt->elements.cend())
+ continue;
+
+ return it;
+ }
+ return pt->nets.end();
+}
+
+// return next element which is connected to "net_name" and doesnt have a corresponding
+// module in circuit_modules and isn't already in the backlog
+vector<ParseElement *>::iterator ParseTreeCreationHelper::next_fresh_element_bound_to(const string &net_name, const set<const ParseElement *> &excludes)
+{
+ // Loop over elements
+ for (auto it = pt->elements.begin(); it != pt->elements.end(); ++it)
+ {
+ const auto &elem = *it;
+
+ // Check if element is part of exlude set
+ if (excludes.find(elem) != excludes.cend())
+ continue;
+
+ // Check if element is part of backlog set
+ if (elements_backlog.find(elem) != elements_backlog.cend())
+ continue;
+
+ // Check if net_name is in element nets
+ if (find(elem->nets.cbegin(), elem->nets.cend(), net_name) == elem->nets.cend())
+ continue;
+
+ // Check if element has a corresponding module instantiated
+ auto it_mod = find_if(circuit_modules.cbegin(), circuit_modules.cend(), [&it](const auto &p)
+ { return p.first == (*it)->name; }
+ );
+
+ // if not, return current iterator `it`
+ if (it_mod == circuit_modules.cend())
+ return it;
+ }
+ return pt->elements.end();
+}
+
+void ParseTreeCreationHelper::create_signals(const ParseElement *elem)
+{
+ // element should be in the parsetree
+ assert(find(pt->elements.begin(), pt->elements.end(), elem) != pt->elements.end());
+
+ // for all nets connected to elements
+ for (const auto &net : elem->nets)
+ {
+ // find whether it has already been instantiated
+ if (circuit_signals.find(net) == circuit_signals.end())
+ {
+ // if not, do it
+ circuit_signals.emplace(net, pt->nets.at(net).create(net));
+
+ // verify the net had no connection (it would be a bug otherwise,
+ // since it hadn't been instantiated)
+ assert(pt->nets.at(net).m_connect_count == 0);
+
+ // find all elements connected to net and add them to the elements backlog
+ // FIXME: potential bug here if an element can have both bidir and unidir nets
+ auto it = next_fresh_element_bound_to(net, {elem});
+ while (it != pt->elements.end())
+ {
+ elements_backlog.insert(*it);
+ it = next_fresh_element_bound_to(net, {elem});
+ }
+ }
+ }
+}
+
+void ParseTreeCreationHelper::upgrade_signal(const string &net_name)
+{
+ // Upgrade even if: net.bidirectional() is false
+
+ // The signal should exist because it has been created by pt_helper.create_signals()
+ // but it doesn't hurt to check once more time
+ assert(circuit_signals.find(net_name) != circuit_signals.end());
+
+ // Set the bidirectional flag to true
+ pt->nets[net_name].m_bidirectional = true;
+
+ // Check if the net was created as unidirectional (only one signal)
+ if (circuit_signals.at(net_name).size() == 1)
+ {
+ // if the existing signal is unidirectional
+
+ // check if a port is already connected to the signal
+ if (pt->nets[net_name].m_connect_count)
+ {
+ // if yes, that means a port is already connected to the signal
+ // we cannot easily re-instantiate it as it may be bound to a port;
+ // instead, we add a second signal to the vector to make it bidirectional
+ circuit_signals[net_name].push_back(pt->nets[net_name].create_uni(net_name + "_1"));
+
+ // For bidirectional signals, first port to connect binds to signal_0
+ // for writing and signal_1 for reading
+ // Therefore we have to "re-create" this scenario, as if the first device
+ // connected to a bidirectional net. But for this we need to know which
+ // if the first device was a writer or a reader.
+
+ // first determine if the connected port was writing or reading the signal
+ bool has_writer = pt->nets[net_name].m_connect_writer_count;
+ bool has_reader = pt->nets[net_name].m_connect_reader_count;
+
+ // only one should be true since the net was unidirectional
+ assert(has_writer ^ has_reader);
+
+ // if a writer, then it's as expected
+ if (has_writer)
+ {
+ // if a writer, then it's as expected:
+ // the next port will connect to signal_1 for writing
+ // we just need to update the number of "readers" connected to the net
+ pt->nets[net_name].m_readers_count++;
+ }
+ else
+ {
+ // if a writer, then the next port needs to reverse its order.
+ // for this we swap signal_0 and signal_1
+ swap(circuit_signals[net_name][0], circuit_signals[net_name][1]);
+ // then we need to update the number of "writers" connected to the net
+ pt->nets[net_name].m_writers_count++;
+ }
+ }
+ else
+ {
+ // otherwise, we can recreate the first signal as well
+ circuit_signals[net_name].clear(); // will delete the net through the constructor
+ circuit_signals[net_name] = pt->nets.at(net_name).create(net_name);
+ }
+ }
+}
+
+string ParseElement::to_json() const
+{
+ // name
+ // kind()
+ // nets
+ // args
+ // kwargs
+
+ stringstream ss;
+ ss << "{";
+ ss << "\"name\":" << '"' << name << '"';
+ ss << ',';
+ ss << "\"type\":" << '"' << kind() << '"';
+ ss << ',';
+ ss << "\"nets\":";
+ {
+ ss << '[';
+ for (size_t i = 0; i < nets.size(); ++i)
+ {
+ ss << '"' << nets[i] << '"';
+ if (i != nets.size() - 1)
+ ss << ", ";
+ }
+ ss << ']';
+ }
+ ss << ',';
+ ss << "\"args\":";
+ {
+ ss << '[';
+ for (size_t i = 0; i < args.size(); ++i)
+ {
+ ss << args[i].to_json();
+ if (i != args.size() - 1)
+ ss << ", ";
+ }
+ ss << ']';
+ }
+ ss << ',';
+ ss << "\"kwargs\":";
+ {
+ ss << '{';
+ for (auto it = kwargs.begin(); it != kwargs.end(); ++it)
+ {
+ if (it != kwargs.begin())
+ ss << ',';
+ ss << '"' << it->first << '"' << ':';
+ ss << it->second.to_json();
+ }
+ ss << '}';
+ }
+ ss << '}';
+ return ss.str();
+}
+
+ParseTree::ParseTree(const string &name, const ParseSubcircuit &subcircuit, const map<string, Variable> &kwargs)
+: ParseTree(name)
+{
+ parent = subcircuit.parent;
+ is_subcircuit = true;
+
+ // take kwargs as local assignments
+ local_assignments = subcircuit.kwargs;
+
+ for (const auto &p : kwargs)
+ {
+ if (local_assignments.count(p.first) == 0)
+ {
+ cerr << name << ": Unknown subcircuit parameter " << p.first << endl;
+ exit(1);
+ }
+ local_assignments[p.first] = p.second;
+ }
+
+ yyscan_t scanner;
+ YY_BUFFER_STATE buf;
+
+ yylex_init(&scanner);
+ buf = yy_scan_string(subcircuit.netlist.c_str(), scanner);
+ yy_switch_to_buffer(buf, scanner);
+
+ int parsing_result = yyparse(scanner, this);
+
+ //yy_delete_buffer(buf, scanner);
+ yylex_destroy(scanner);
+
+ // Return if unsuccessful
+ if (parsing_result != 0) {
+ cerr << name << ": failed parsing subcircuit netlist" << endl;
+ exit(1);
+ }
+
+ if (directives.size() > 0)
+ {
+ cerr << name << ": found a directive in subcircuit, ignoring." << endl;
+ directives.clear();
+ }
+
+ if (analyses.size() > 0)
+ {
+ cerr << name << ": found an analysis in subcircuit, ignoring." << endl;
+ analyses.clear();
+ }
+ // print();
+}
+
+int ParseTree::register_directive(ParseDirective *directive)
+{
+ directive->parent = this;
+ directives.push_back(directive);
+ // cout << "Created directive " << directive->kind() << endl;
+ // cout << "current number of directives: " << directives.size() << endl;
+ return directives.size() - 1;
+
+}
+
+int ParseTree::register_element(ParseElement *element)
+{
+ element->name = name_prefix() + element->name;
+ strutils::toupper(element->name);
+ element->parent = this;
+ elements.push_back(element);
+ // cout << "Created element " << element->name << endl;
+ // cout << "current number of elements: " << elements.size() << endl;
+ return elements.size() - 1;
+}
+
+int ParseTree::register_analysis(ParseAnalysis *analysis)
+{
+ if ( !analyses.empty() )
+ {
+ cerr << "Attempted to register a new analysis (" << analysis->kind() << ")" << endl;
+ cerr << "But another one was already specified (" << analyses[0]->kind() << ")" << endl;
+ exit(1);
+ }
+ analysis->parent = this;
+ analyses.push_back(analysis);
+ // cout << "Created analysis " << analysis->kind() << endl;
+ // cout << "current number of analysis: " << analyses.size() << endl;
+ return analyses.size() - 1;
+}
+
+int ParseTree::register_subcircuit(string name)
+{
+ strutils::toupper(name);
+ //name = name_prefix() + name;
+
+ auto it = find_if(subcircuits.cbegin(), subcircuits.cend(),
+ [&name](const ParseSubcircuit *p){ return p->name == name;});
+ if (it != subcircuits.cend())
+ {
+ cerr << "Attempted to register a new subcircuit with name " << name << endl;
+ cerr << "But another subcircuit with this name already exists" << endl;
+ exit(1);
+ }
+
+ subcircuits.push_back(new ParseSubcircuit(name, this));
+ int i = subcircuits.size() - 1;
+
+ // TODO: Copy variables definitions ?
+
+ // cout << "Created subcircuit " << subcircuits[i]->name << endl;
+ // cout << "(current number of subcircuits: " << subcircuits.size() << ")" << endl;
+
+ return i;
+}
+
+const ParseSubcircuit *ParseTree::find_subcircuit(const string &name) const
+{
+ auto pred = [&name](const ParseSubcircuit *p) {
+ return p->name == name;
+ };
+ auto it = find_if(subcircuits.begin(), subcircuits.end(), pred);
+ if (it != subcircuits.end())
+ return *it;
+
+ cerr << "Subcircuit definition not found: " << name << endl;
+ exit(1);
+}
+
+void ParseTree::print() const
+{
+ cout << "-----------" << endl;
+ cout << global_assignments.size() << " global assignments" << endl;
+ for (const auto &x : global_assignments)
+ cout << " - " << x.first << " (" << x.second.kind()
+ << ") = " << x.second.get_str() << endl;
+
+ cout << "-----------" << endl;
+ cout << local_assignments.size() << " local assignments" << endl;
+ for (const auto &x : local_assignments)
+ cout << " - " << x.first << " (" << x.second.kind()
+ << ") = " << x.second.get_str() << endl;
+
+ cout << "-----------" << endl;
+ cout << elements.size() << " elements" << endl;
+ for (const auto &x : elements)
+ {
+ cout << " - ";
+ x->print();
+ }
+
+ cout << "-----------" << endl;
+ cout << nets.size() << " named nets" << endl;
+ for (const auto &x : nets)
+ cout << " - " << x.first << ": " << x.second.type_str()
+ << "<" << x.second.size() << ">"
+ << (x.second.bidirectional() ? "b" : "u")
+ << " (" << x.second.m_writers_count << "|" << x.second.m_readers_count << ")"
+ << endl;
+
+ cout << "-----------" << endl;
+ cout << directives.size() << " directives" << endl;
+ for (const auto &x : directives)
+ {
+ cout << " - ";
+ x->print();
+ }
+
+ cout << "-----------" << endl;
+ cout << subcircuits.size() << " subcircuits" << endl;
+ for (const auto &x : subcircuits)
+ {
+ cout << " - ";
+ x->print();
+ }
+
+ cout << "-----------" << endl;
+ cout << analyses.size() << " analyses" << endl;
+ for (const auto &x : analyses)
+ {
+ cout << " - ";
+ x->print();
+ }
+}
+
+string ParseDirective::to_json() const
+{
+ stringstream ss;
+ ss << "{";
+ ss << "\"type\":" << '"' << kind() << '"';
+ ss << ',';
+ ss << "\"args\":";
+ {
+ ss << '[';
+ for (size_t i = 0; i < args.size(); ++i)
+ {
+ ss << args[i].to_json();
+ if (i != args.size() - 1)
+ ss << ", ";
+ }
+ ss << ']';
+ }
+ ss << ',';
+ ss << "\"kwargs\":";
+ {
+ ss << '{';
+ for (auto it = kwargs.begin(); it != kwargs.end(); ++it)
+ {
+ if (it != kwargs.begin())
+ ss << ',';
+ ss << '"' << it->first << '"' << ':';
+ ss << it->second.to_json();
+ }
+ ss << '}';
+ }
+ ss << '}';
+ return ss.str();
+}
+
+string ParseAnalysis::to_json() const
+{
+ stringstream ss;
+ ss << "{";
+ ss << "\"type\":" << '"' << kind() << '"';
+ ss << ',';
+ ss << "\"args\":";
+ {
+ ss << '[';
+ for (size_t i = 0; i < args.size(); ++i)
+ {
+ ss << args[i].to_json();
+ if (i != args.size() - 1)
+ ss << ", ";
+ }
+ ss << ']';
+ }
+ ss << ',';
+ ss << "\"kwargs\":";
+ {
+ ss << '{';
+ for (auto it = kwargs.begin(); it != kwargs.end(); ++it)
+ {
+ if (it != kwargs.begin())
+ ss << ',';
+ ss << '"' << it->first << '"' << ':';
+ ss << it->second.to_json();
+ }
+ ss << '}';
+ }
+ ss << '}';
+ return ss.str();
+}
+
+string ParseSubcircuit::to_json() const
+{
+ stringstream ss;
+ ss << "TODO";
+ return ss.str();
+}
+
+string ParseNet::to_json() const
+{
+ stringstream ss;
+ ss << "{";
+ ss << "\"type\":" << '"' << type_str() << '"';
+ ss << ',';
+ ss << "\"size\":" << m_size;
+ ss << ',';
+ ss << "\"bidirectional\":" << bidirectional();
+ ss << ',';
+ ss << "\"readers\":" << m_readers_count;
+ ss << ',';
+ ss << "\"writers\":" << m_readers_count;
+ ss << '}';
+ return ss.str();
+}
+
+string ParseTree::to_json() const
+{
+ stringstream ss;
+ ss << "{";
+ ss << "\"name\":" << '"' << name << '"';
+ ss << ',';
+ ss << "\"parent\":";
+ if (parent)
+ ss << '"' << parent->name << '"';
+ else
+ ss << "null";
+ ss << ',';
+ ss << "\"is_subcircuit\":" << (is_subcircuit ? "true" : "false");
+ ss << ',';
+ ss << "\"unnamed_nets\":" << unnamed_net_count;
+ ss << ',';
+ ss << "\"nets\":";
+ {
+ ss << '{';
+ for (auto it = nets.begin(); it != nets.end(); ++it)
+ {
+ if (it != nets.begin())
+ ss << ',';
+ ss << '"' << it->first << '"' << ':';
+ ss << it->second.to_json();
+ }
+ ss << '}';
+ }
+ ss << ',';
+ ss << "\"elements\":";
+ {
+ ss << '[';
+ for (size_t i = 0; i < elements.size(); ++i)
+ {
+ ss << elements[i]->to_json();
+ if (i != elements.size() - 1)
+ ss << ", ";
+ }
+ ss << ']';
+ }
+ ss << ',';
+ ss << "\"directives\":";
+ {
+ ss << '[';
+ for (size_t i = 0; i < directives.size(); ++i)
+ {
+ ss << directives[i]->to_json();
+ if (i != directives.size() - 1)
+ ss << ", ";
+ }
+ ss << ']';
+ }
+ ss << ',';
+ ss << "\"analyses\":";
+ {
+ ss << '[';
+ for (size_t i = 0; i < analyses.size(); ++i)
+ {
+ ss << analyses[i]->to_json();
+ if (i != analyses.size() - 1)
+ ss << ", ";
+ }
+ ss << ']';
+ }
+ ss << ',';
+ ss << "\"subcircuits\":";
+ {
+ ss << "\"TODO\"";
+ if (false)
+ {
+ ss << '[';
+ for (size_t i = 0; i < subcircuits.size(); ++i)
+ {
+ ss << subcircuits[i]->to_json();
+ if (i != subcircuits.size() - 1)
+ ss << ", ";
+ }
+ ss << ']';
+ }
+ }
+ ss << ',';
+ ss << "\"local_variables\":";
+ {
+ ss << '{';
+ for (auto it = local_assignments.begin(); it != local_assignments.end(); ++it)
+ {
+ if (it != local_assignments.begin())
+ ss << ',';
+ ss << '"' << it->first << '"' << ':';
+ ss << it->second.to_json();
+ }
+ ss << '}';
+ }
+ ss << ',';
+ ss << "\"global_variables\":";
+ {
+ ss << '{';
+ for (auto it = global_assignments.begin(); it != global_assignments.end(); ++it)
+ {
+ if (it != global_assignments.begin())
+ ss << ',';
+ ss << '"' << it->first << '"' << ':';
+ ss << it->second.to_json();
+ }
+ ss << '}';
+ }
+ ss << '}';
+ return ss.str();
+}
+
+void ParseTree::build_circuit()
+{
+ cout << "Flattening..." << endl;
+ // flatten current parse tree by expanding subcircuits
+ flatten();
+ cout << "Done (flattening)" << endl;
+
+ auto pt_helper = ParseTreeCreationHelper(this);
+
+ auto &elements_backlog = pt_helper.elements_backlog;
+ auto &circuit_signals = pt_helper.circuit_signals;
+ auto &circuit_modules = pt_helper.circuit_modules;
+
+ // Find first bidirectional net in nets that is not in circuit_nets
+ auto next_net = pt_helper.next_fresh_bidir_net();
+ while(next_net != nets.end())
+ {
+ cout << "Elaborating network of " << next_net->first << "..." << endl;
+
+ // Find elements which are connected
+ auto elem = pt_helper.next_fresh_element_bound_to(next_net->first);
+ while (elem != elements.end())
+ {
+ cout << "Creating " << (*elem)->name << " (reason: connection to bidir net)" << endl;
+ auto mod = (*elem)->create(pt_helper);
+ cout << "Done creating " << (*elem)->name << endl;
+ if (mod->name() != (*elem)->name)
+ {
+ cerr << "Error: a module with name '" << (*elem)->name << "' already exists or";
+ cerr << " it was renamed due to a bad naming." << endl;
+ cerr << "Make sure there is no naming conflict in the netlist" << endl;
+ exit(1);
+ }
+ circuit_modules.emplace(mod->name(), mod);
+
+ // Process backlog elements (elements that were connected to the nets)
+ while ( !elements_backlog.empty() )
+ {
+ auto it = elements_backlog.begin();
+ auto &backlog_elem = *it;
+ cout << "Creating " << backlog_elem->name << " (reason: backlog)" << endl;
+ auto mod = backlog_elem->create(pt_helper);
+ cout << "Done creating " << backlog_elem->name << endl;
+
+ if (mod->name() != (*it)->name)
+ {
+ cerr << "Error: a module with name '" << (*it)->name << "' already exists or";
+ cerr << " it was renamed due to a bad naming." << endl;
+ cerr << "Make sure there is no naming conflict in the netlist" << endl;
+ exit(1);
+ }
+
+ circuit_modules.emplace(mod->name(), mod);
+ elements_backlog.erase(*it);
+ }
+ elem = pt_helper.next_fresh_element_bound_to(next_net->first);
+ }
+
+ cout << "Done (elaborating network of " << next_net->first << ")" << endl;
+
+ // At this point all elements connected to the bidirectional net have been created
+ // move on to the next one
+ next_net = pt_helper.next_fresh_bidir_net();
+ }
+ cout << "Done with bidirectional nets" << endl;
+ // At this point all bidirectional nets have been created, and all devices
+ // connected to them as well we can proceed with unidirectional nets
+ next_net = pt_helper.next_fresh_net();
+ while(next_net != nets.end())
+ {
+ cout << "Elaborating network of " << next_net->first << "..." << endl;
+
+ // Find elements which are connected
+ auto elem = pt_helper.next_fresh_element_bound_to(next_net->first);
+ while (elem != elements.end())
+ {
+ cout << "Creating " << (*elem)->name << " (reason: connection to unidir net)" << endl;
+ auto mod = (*elem)->create(pt_helper);
+ cout << "Done creating " << (*elem)->name << endl;
+ if (mod->name() != (*elem)->name)
+ {
+ cerr << "Error: a module with name '" << (*elem)->name << "' already exists or";
+ cerr << " it was renamed due to a bad naming." << endl;
+ cerr << "Make sure there is no naming conflict in the netlist" << endl;
+ exit(1);
+ }
+ circuit_modules.emplace(mod->name(), mod);
+
+ // Process backlog elements (elements that were connected to the nets)
+ while ( !elements_backlog.empty() )
+ {
+ auto it = elements_backlog.begin();
+ auto &backlog_elem = *it;
+ cout << "Creating " << backlog_elem->name << " (reason: backlog)" << endl;
+ auto mod = backlog_elem->create(pt_helper);
+ cout << "Done creating " << backlog_elem->name << endl;
+
+ if (mod->name() != (*it)->name)
+ {
+ cerr << "Error: a module with name '" << (*it)->name << "' already exists or";
+ cerr << " it was renamed due to a bad naming." << endl;
+ cerr << "Make sure there is no naming conflict in the netlist" << endl;
+ exit(1);
+ }
+
+ circuit_modules.emplace(mod->name(), mod);
+ elements_backlog.erase(*it);
+ }
+ elem = pt_helper.next_fresh_element_bound_to(next_net->first);
+ }
+
+ cout << "Done (elaborating network of " << next_net->first << ")" << endl;
+
+ // At this point all elements connected to the bidirectional net have been created
+ // move on to the next one
+ next_net = pt_helper.next_fresh_net();
+ }
+ for (const auto &x : directives)
+ x->create();
+
+ if (! analyses.empty())
+ analyses.at(0)->create();
+
+ // Add all nets and elements to SPECSGlobalConfig
+ for (const auto &x: circuit_modules)
+ specsGlobalConfig.register_object(x.second);
+ for (const auto &x: circuit_signals)
+ for (const auto &sig: x.second)
+ specsGlobalConfig.register_object(sig);
+}
+
+void ParseTree::flatten()
+{
+ cout << "Flattening " << name << endl;
+
+ vector<ParseElement *> elements_flat;
+ map<string, ParseNet> nets_flat = nets;
+
+ // First, find all subcircuit instances (X elements) and ingest their parse-tree
+ // (flattening the global parse-tree)
+ for (auto &elem : elements)
+ {
+ // try to cast as XElement
+ XElement *xelem = dynamic_cast<XElement *>(elem);
+
+ // if unsuccessful, it means the device doesnt need to be flattened
+ if (!xelem)
+ {
+ elements_flat.push_back(elem);
+ continue;
+ }
+
+ // get subcircuit name
+ string subcircuit_name = xelem->args.back().as_string();
+ strutils::toupper(subcircuit_name);
+
+ // get subcircuit definition in element's parent
+ // TODO: figure out if we want global subcircuits
+ auto subcircuit = xelem->parent->find_subcircuit(subcircuit_name);
+
+ // parse the subcircuit netlist into a new parse tree
+ ParseTree sub_pt(xelem->name, *subcircuit, xelem->kwargs);
+
+ // flatten it
+ sub_pt.flatten();
+
+ // Check the number of nets (in args)
+ if(xelem->args.size() - 1 != subcircuit->ports.size())
+ {
+ cerr << "Wrong number of nets for subcircuit instance: ";
+ cerr << xelem->name << endl;
+ exit(1);
+ }
+
+ // Compute net_name equivalence between internal and external nets
+ map<string, string> net_names_translations;
+ for (size_t i = 0; i < subcircuit->ports.size(); ++i)
+ {
+ Variable v = xelem->args[i]; // naming from Xnn ... line
+ string internal_net_name = sub_pt.name_prefix() + subcircuit->ports[i]; // naming from .SUBCKT line
+ string external_net_name = "";
+
+ // build full exposed net_name
+ string net_base;
+
+ // extract from Variable (either string or integer)
+ // normally parser should always transform it to a string variable
+ // for us
+ if (v.type == Variable::STRING)
+ net_base = v.as_string();
+ else if (v.type == Variable::INTEGER)
+ net_base = ParseNet::name_from_id(v.as_integer());
+ else
+ {
+ cerr << "Incompatible net name for " << xelem->name << endl;
+ exit(1);
+ }
+
+ // if a single underscore was given, use the unnamed net count to
+ // define a unique net
+ // FIXME: this way is not correct; it should really increment the
+ // parent's unnamed net count; I think it would be acceptable to
+ // discard const for this, as the user specifically doesn't care about
+ // the actual name of unnamed nets.
+ if (net_base == "_")
+ net_base = "_" + to_string(this->unnamed_net_count++);
+
+ // add current prefix
+ external_net_name = name_prefix() + net_base;
+
+ // see if the net was already defined (was used by another device)
+ if (nets.find(external_net_name) != nets.end())
+ {
+ // if so, combined the previously defined net with the internal one
+ bool ok = sub_pt.nets[internal_net_name].combine_with(nets.at(external_net_name));
+ if (!ok)
+ {
+ cerr << "Could not reconcile nets: " << internal_net_name;
+ cerr << " and " << external_net_name << endl;
+ cerr << "During flattening of " << xelem->name << endl;
+ exit(1);
+ }
+ }
+ // Add the (potentially combined) net to pt_helper nets
+ nets_flat[external_net_name] = sub_pt.nets.at(internal_net_name);
+
+ // Update the net name translations list
+ net_names_translations[internal_net_name] = external_net_name;
+
+ // Erase the internal net_name (replaced by the global net name)
+ sub_pt.nets.erase(internal_net_name);
+ } // for (size_t i = 0; i < subcircuit->ports.size(); ++i)
+
+ // show result of translation
+ if (false)
+ for (const auto &p : net_names_translations)
+ cout << p.first << " <==> " << p.second << endl;
+
+ // translate internal net names by modifying the elements within the
+ // subcircuit which are bound to them
+ for (auto subelem : sub_pt.elements)
+ {
+ // loop over translation, and update if necessary
+ for (const auto &p : net_names_translations)
+ {
+ auto it = find(subelem->nets.begin(), subelem->nets.end(), p.first);
+ if (it == subelem->nets.end())
+ continue;
+ *it = p.second;
+ }
+ } // for (auto subelem : sub_pt.elements)
+
+ // insert the (globally named) internal-only nets into flattened netlist
+ nets_flat.insert(sub_pt.nets.cbegin(), sub_pt.nets.cend());
+ // insert the (potentially updated) elements to flattened netlist
+ elements_flat.insert(elements_flat.end(), sub_pt.elements.cbegin(), sub_pt.elements.cend());
+
+ // finally, free the memory of the elem object
+ // note that the pointer itself is still in the elements vector!
+ // we cannot remove it due to the for loop
+ delete elem;
+ elem = nullptr;
+ } // for (auto elem : elements)
+
+ // Print new list of elements and nets
+ if (false)
+ {
+ cout << "-----------" << endl;
+ cout << elements_flat.size() << " elements" << endl;
+ for (const auto &x : elements_flat)
+ {
+ cout << " - ";
+ x->print();
+ }
+
+ cout << "-----------" << endl;
+ cout << nets_flat.size() << " named nets" << endl;
+ for (const auto &x : nets_flat)
+ cout << " - " << x.first << ": " << x.second.type_str()
+ << "<" << x.second.size() << ">"
+ << (x.second.bidirectional() ? "b" : "u")
+ << " (" << x.second.m_writers_count << "|" << x.second.m_readers_count << ")"
+ << endl;
+ }
+
+ // move to flattened nets and elements
+ nets = nets_flat;
+ elements = elements_flat;
+
+ return;
+}
+
+map<string, Variable> ParseTree::global_assignments =
+{
+ { "pi", Variable(M_PI) },
+ { "c", Variable(299792458.0) },
+}; \ No newline at end of file
diff --git a/src/parser/parse_tree.h b/src/parser/parse_tree.h
new file mode 100644
index 0000000..fbcefbc
--- /dev/null
+++ b/src/parser/parse_tree.h
@@ -0,0 +1,890 @@
+#pragma once
+
+#include "specs.h"
+#include "strutils.h"
+
+#include <initializer_list>
+#include <utility>
+#include <memory>
+#include <string>
+#include <vector>
+#include <map>
+#include <iostream>
+
+using std::pair;
+using std::make_pair;
+using std::string;
+using std::vector;
+using std::map;
+using std::cout;
+using std::cerr;
+using std::endl;
+using std::pair;
+using std::unique_ptr;
+
+struct ParseTree;
+struct SUBCKTDirective;
+
+/* Struct to hold a typed variable */
+struct Variable {
+ enum Type {
+ TYPE_MIN = -1,
+ DOUBLE = 0,
+ COMPLEX_DOUBLE,
+ INTEGER,
+ BOOLEAN,
+ STRING,
+ LIST,
+ NONE,
+ TYPE_MAX,
+ };
+ Type type;
+ double num;
+ complex<double> cnum;
+ int inum;
+ bool bnum;
+ string str;
+ vector<Variable> vec;
+
+ Variable(const Variable &v) { *this = v; }
+ Variable(Type type): type(type) {}
+ Variable(double num = 0): type(DOUBLE), num(num) {}
+ explicit Variable(const complex<double> &cnum): type(COMPLEX_DOUBLE), cnum(cnum) {}
+ explicit Variable(int inum): type(INTEGER), inum(inum) {}
+ explicit Variable(bool bnum): type(BOOLEAN), bnum(bnum) {}
+ explicit Variable(const string &str): type(STRING), str(str) {}
+ explicit Variable(const vector<Variable> &vec): type(LIST), vec(vec) {}
+ explicit Variable(const vector<double> &dvec): type(LIST)
+ {
+ vec.resize(dvec.size());
+ for (size_t i = 0; i < dvec.size(); ++i)
+ {
+ vec[i] = Variable(dvec[i]);
+ }
+ }
+
+ bool type_is_valid() const {
+ return TYPE_MIN < type && type < TYPE_MAX;
+ }
+
+ bool is_none() const {
+ return type == NONE;
+ }
+
+ bool is_list() const {
+ return type == LIST;
+ }
+
+ bool is_number() const {
+ return type == DOUBLE || type == INTEGER || type == BOOLEAN;
+ }
+
+ string get_str() const
+ {
+ stringstream ss;
+ switch (type)
+ {
+ case STRING:
+ return str;
+ case DOUBLE:
+ ss << num;
+ return ss.str();
+ case COMPLEX_DOUBLE:
+ ss << cnum.real() << " + " << cnum.imag() << "i";
+ return ss.str();
+ case INTEGER:
+ ss << inum;
+ return ss.str();
+ case BOOLEAN:
+ ss << bnum;
+ return ss.str();
+ case LIST:
+ ss << "[";
+ for (size_t i = 0; i < vec.size(); ++i)
+ {
+ ss << vec[i].to_json();
+ if (i != vec.size() - 1)
+ ss << ", ";
+ }
+ ss << "]";
+ return ss.str();
+ case NONE:
+ return "None";
+ default:
+ cerr << "error: cannot convert variable to string" << endl;
+ exit(1);
+ }
+ }
+
+ string to_json() const
+ {
+ stringstream ss;
+ ss << '{';
+ ss << "\"type\":" << '"' << kind() << '"' << ',';
+ ss << "\"value\":";
+ {
+ stringstream tss;
+ switch (type) {
+ case STRING:
+ tss << '"' << str << '"';
+ break;
+ case DOUBLE:
+ tss << scientific << num;
+ break;
+ case COMPLEX_DOUBLE:
+ tss << "{\"real\":";
+ tss << scientific << cnum.real();
+ tss << ",\"imag\":" << cnum.imag();
+ tss << '}';
+ break;
+ case INTEGER:
+ tss << inum;
+ break;
+ case BOOLEAN:
+ tss << (bnum ? "true" : "false");
+ break;
+ case LIST:
+ tss << "[";
+ for (size_t i = 0; i < vec.size(); ++i) {
+ tss << vec[i].to_json();
+ if (i != vec.size() - 1)
+ tss << ", ";
+ }
+ tss << "]";
+ break;
+ case NONE:
+ tss << "null";
+ break;
+ default:
+ cerr << "error: cannot convert variable to json representation" << endl;
+ exit(1);
+ }
+ ss << tss.str();
+ }
+ ss << '}';
+ return ss.str();
+ }
+
+ double as_double() const
+ {
+ if (type == DOUBLE)
+ return num;
+ if (type == INTEGER)
+ return inum;
+ if (type == BOOLEAN)
+ return bnum;
+ cerr << "Variable cannot be converted to a double value: " << get_str() << endl;
+ exit(1);
+ }
+
+ int as_integer() const
+ {
+ if (type == DOUBLE)
+ return num;
+ if (type == INTEGER)
+ return inum;
+ if (type == BOOLEAN)
+ return bnum;
+ cerr << "Variable cannot be converted to an integer value: " << get_str() << endl;
+ exit(1);
+ }
+
+ bool as_boolean() const
+ {
+ if (type == DOUBLE)
+ return num;
+ if (type == INTEGER)
+ return inum;
+ if (type == BOOLEAN)
+ return bnum;
+ cerr << "Variable cannot be converted to a boolean value: " << get_str() << endl;
+ exit(1);
+ }
+
+ complex<double> as_complex() const
+ {
+ if (type == DOUBLE)
+ return num;
+ if (type == COMPLEX_DOUBLE)
+ return cnum;
+ if (type == INTEGER)
+ return inum;
+ if (type == BOOLEAN)
+ return bnum;
+ cerr << "Variable cannot be converted to a complex value: " << get_str() << endl;
+ exit(1);
+ }
+
+ string as_string() const
+ {
+ if (type == STRING)
+ return str;
+ cerr << "Variable is not a string: " << get_str() << endl;
+ exit(1);
+ return 0;
+ }
+
+ string kind() const
+ {
+ if (type == DOUBLE)
+ return "DOUBLE";
+ if (type == COMPLEX_DOUBLE)
+ return "COMPLEX_DOUBLE";
+ if (type == INTEGER)
+ return "INTEGER";
+ if (type == BOOLEAN)
+ return "BOOLEAN";
+ if (type == STRING)
+ return "STRING";
+ if (type == LIST)
+ return "LIST";
+ if (type == NONE)
+ return "NONE";
+
+ return "UNDEFINED";
+ }
+
+ Variable &operator+=(const Variable &rhs)
+ {
+ num = as_double() + rhs.as_double();
+ type = DOUBLE;
+ return *this;
+ }
+ Variable &operator-=(const Variable &rhs)
+ {
+ num = as_double() - rhs.as_double();
+ type = DOUBLE;
+ return *this;
+ }
+ Variable &operator*=(const Variable &rhs)
+ {
+ num = as_double() * rhs.as_double();
+ type = DOUBLE;
+ return *this;
+ }
+ Variable &operator/=(const Variable &rhs)
+ {
+ num = as_double() / rhs.as_double();
+ type = DOUBLE;
+ return *this;
+ }
+ Variable &operator^=(const Variable &rhs)
+ {
+ num = pow(as_double(), rhs.as_double());
+ type = DOUBLE;
+ return *this;
+ }
+ Variable &operator=(const Variable &rhs) = default;
+ operator string() const { return get_str(); }
+};
+
+/* Dummy circuit directive struct */
+struct ParseDirective {
+ ParseTree *parent;
+ vector<Variable> args; // list of non-keyword arguments found on netlist
+ map<string, Variable> kwargs; // list of kw arguments found on netlist
+
+ ParseDirective()
+ {}
+
+ ParseDirective(const ParseDirective& other) = default;
+
+ virtual void print() const
+ {
+ cout << "DIRECTIVE[" << kind() << "] ";
+ cout << "{";
+ for (const auto &x : args)
+ cout << x.get_str() << " (" << x.kind() << "), ";
+ if ( !args.empty() )
+ cout << "\b\b";
+ cout << "} ";
+ cout << "{";
+ for (const auto &x : kwargs)
+ cout << x.first << " (" << x.second.kind() << ") = " << x.second.get_str() << ", ";
+ if ( !kwargs.empty() )
+ cout << "\b\b";
+ cout << "}" << endl;
+ }
+ virtual string to_json() const;
+
+ virtual ParseDirective *clone() const = 0;
+ virtual void create() const = 0;
+ virtual string kind() const { return "UNDEFINED"; }
+ virtual ~ParseDirective() {}
+};
+
+/* Dummy circuit analysis struct */
+struct ParseAnalysis {
+ const ParseTree *parent;
+ vector<Variable> args; // list of non-keyword arguments found on netlist
+ map<string, Variable> kwargs; // list of kw arguments found on netlist
+
+ ParseAnalysis()
+ {}
+
+ ParseAnalysis(const ParseAnalysis& other) = default;
+
+ void print() const
+ {
+ cout << "ANALYSIS[" << kind() << "] ";
+ cout << "{";
+ for (const auto &x : args)
+ cout << x.get_str() << " (" << x.kind() << "), ";
+ if ( !args.empty() )
+ cout << "\b\b";
+ cout << "} ";
+ cout << "{";
+ for (const auto &x : kwargs)
+ cout << x.first << " (" << x.second.kind() << ") = " << x.second.get_str() << ", ";
+ if ( !kwargs.empty() )
+ cout << "\b\b";
+ cout << "}" << endl;
+ }
+ virtual string to_json() const;
+
+ virtual ParseAnalysis *clone() const = 0;
+ virtual void create() const = 0;
+ virtual string kind() const { return "UNDEFINED"; }
+ virtual ~ParseAnalysis() {}
+};
+
+/* SUBCKT: subcircuit definition */
+struct ParseSubcircuit {
+ string name; // name of the subcircuit
+ string netlist; // subcircuit netlist (extracted from parent netlist)
+ vector<string> ports; // name of ports
+ map<string, Variable> kwargs; // list of kw arguments found on netlist
+ const ParseTree *parent;
+
+ ParseSubcircuit(const string &name, ParseTree *parent = nullptr)
+ : name(name)
+ , parent(parent)
+ {}
+
+ ParseSubcircuit(const ParseSubcircuit& other) = default;
+
+ void register_port(const string &port_name)
+ {
+ auto it = find(ports.cbegin(), ports.cend(), port_name);
+ if (it == ports.cend())
+ {
+ ports.push_back(port_name);
+ }
+ else
+ {
+ cerr << "Subcircuit cannot have duplicate port names";
+ cerr << "(" << port_name << " was already defined)" << endl;
+ exit(1);
+ }
+ }
+ //ParseTree *instantiate(const string &instance_name, const map<string,Variable> &kwargs) const;
+ void print() const
+ {
+ cout << name << " (" << kind() << ") ";
+ for (const auto &x : ports)
+ cout << x << " ";
+ cout << "{";
+ for (const auto &x : kwargs)
+ cout << x.first << " (" << x.second.kind() << ") = " << x.second.get_str() << ", ";
+ if ( !kwargs.empty() )
+ cout << "\b\b";
+ cout << "}" << endl;
+ if (false)
+ {
+ cout << "###########################" << endl;
+ cout << "## Start netlist of " << name << endl;
+ cout << "###########################" << endl;
+ cout << netlist;
+ cout << "###########################" << endl;
+ cout << "## End netlist of " << name << endl;
+ cout << "###########################" << endl;
+ }
+ }
+ string to_json() const;
+ string kind() const
+ { return "SUBCIRCUIT DEFINITION"; }
+ ParseSubcircuit *clone() const
+ { return new ParseSubcircuit(*this); }
+};
+
+/* Dummy circuit net struct (although close to what it should look like) */
+struct ParseNet {
+ /* Possible net types */
+ enum Type {
+ NONE,
+ OANALOG,
+ EANALOG,
+ EDIGITAL,
+ };
+
+ /* Member variables */
+ Type m_type;
+ unsigned int m_size; // number of "wires (more than 1 for buses)
+ bool m_bidirectional = false; // whether the signal should be bidirectional (for oanalog and eanalog representing forward/backward waves)
+ unsigned int m_writers_count = 0; // number of writers
+ unsigned int m_readers_count = 0; // number of readers
+ unsigned int m_ports_count = 0; // number of individual ports connected (in total)
+ unsigned int m_connect_count = 0; // number of ports actually connected (updated during creation phase only)
+ unsigned int m_connect_writer_count = 0; // number of writer ports actually connected (updated during creation phase only)
+ unsigned int m_connect_reader_count = 0; // number of reader ports actually connected (updated during creation phase only)
+
+ /* Constructor */
+ ParseNet(Type type = NONE, unsigned int size = 1): m_type(type), m_size(size) {}
+ ParseNet(const ParseNet &n) = default;
+ inline ParseNet& operator=(const ParseNet &n) = default;
+ bool combine_with(const ParseNet &n)
+ {
+ if (m_type != n.m_type)
+ return false;
+ if (m_size != n.m_size)
+ return false;
+ if (m_connect_count || n.m_connect_count)
+ return false;
+
+ m_bidirectional |= n.m_bidirectional;
+ m_writers_count += n.m_writers_count;
+ m_readers_count += n.m_readers_count;
+ m_ports_count += n.m_ports_count;
+
+ return true;
+ }
+
+ /* const Getters */
+ unsigned int size() const { return m_size; }
+ Type type() const { return m_type; }
+ bool bidirectional() const
+ {
+ return m_bidirectional || (m_writers_count == 2);
+ //return m_bidirectional || (m_writers_count == 2) || (m_readers_count == 2);
+ }
+ bool validate() const
+ {
+ return m_writers_count <= 2 && m_ports_count <= 2;
+ }
+
+ /* setters */
+ inline bool setType(Type type, bool force = false) {
+ //cout << type_str() << " - " << ParseNet(type).type_str() << endl;
+ if (m_type == type)
+ // no need to do anything
+ return true;
+ if (m_type == NONE || force)
+ {
+ m_type = type;
+ return true;
+ }
+ // cerr << "Incompatible net type assignment" << endl;
+ return false;
+ }
+
+ string type_str() const
+ {
+ switch (m_type) {
+ case Type::NONE:
+ return "NONE";
+ case Type::OANALOG:
+ return "OANALOG";
+ case Type::EANALOG:
+ return "EANALOG";
+ case Type::EDIGITAL:
+ return "EDIGITAL";
+ default:
+ cerr << "Unexpected type" << endl;
+ exit(1);
+ }
+ }
+
+ string to_json() const;
+
+ // create the net
+ vector<shared_ptr<sc_object>> create(const string &name, bool force_bidir = false) const;
+ shared_ptr<sc_object> create_uni(const string &name) const;
+
+ /* Get a generic net name from net number (different from id) */
+ static string name_from_id(int net_number);
+};
+
+struct ParseTreeCreationHelper;
+
+/* Dummy circuit element struct (although close to )*/
+struct ParseElement {
+ typedef sc_module element_type_base;
+
+ string name;
+ const ParseTree *parent;
+ vector<string> nets; // name of parent nets connected to element
+ vector<Variable> args; // list of non-keyword arguments found on netlist
+ map<string, Variable> kwargs; // list of kw arguments found on netlist
+
+ const int n_nets = 0;
+ const bool bidirectionalable = false;
+
+ ParseElement(const string &name)
+ : name(name)
+ {}
+
+ ParseElement(const string &name, const vector<string> &nets)
+ : name(name)
+ , nets(nets)
+ {}
+
+ ParseElement(const ParseElement& other) = default;
+
+ void print() const
+ {
+ cout << name << " (" << kind() << ") ";
+ for (const auto &x : nets)
+ cout << x << " ";
+ cout << "{";
+ for (const auto &x : args)
+ cout << x.get_str() << " (" << x.kind() << "), ";
+ if ( !args.empty() )
+ cout << "\b\b";
+ cout << "} ";
+ cout << "{";
+ for (const auto &x : kwargs)
+ cout << x.first << " (" << x.second.kind() << ") = " << x.second.get_str() << ", ";
+ if ( !kwargs.empty() )
+ cout << "\b\b";
+ cout << "}" << endl;
+ }
+
+ virtual ParseElement *clone() const = 0;
+ virtual sc_module *create(ParseTreeCreationHelper &pt_helper) const = 0;
+ virtual element_type_base *instantiate_and_connect_uni(ParseTreeCreationHelper &pt_helper) const = 0;
+ virtual element_type_base *instantiate_and_connect_bi(ParseTreeCreationHelper &pt_helper) const
+ {
+ (void) pt_helper;
+ cerr << "Elements of type " << kind() << " cannot be bidirectional" << endl;
+ exit(1);
+ }
+
+ virtual string kind() const { return "UNDEFINED"; }
+ virtual ~ParseElement() {}
+
+ virtual string to_json() const;
+};
+
+struct ParseTree {
+ string name;
+ const ParseTree *parent = nullptr;
+ bool is_subcircuit = false;
+ size_t unnamed_net_count = 0;
+
+ map<string, ParseNet> nets;
+ vector<string> ports; // only for subcircuits
+ vector<ParseElement *> elements;
+ vector<ParseAnalysis *> analyses;
+ vector<ParseDirective *> directives;
+ vector<ParseSubcircuit *> subcircuits;
+ map<string, Variable> local_assignments;
+ static map<string, Variable> global_assignments;
+
+ ParseTree(const string &name = "ROOT")
+ : name(name)
+ {}
+
+ ParseTree(const string &name, const ParseSubcircuit &subcircuit, const map<string, Variable> &kwargs);
+
+ int register_directive(ParseDirective *directive);
+ int register_element(ParseElement *element);
+ int register_analysis(ParseAnalysis *analysis);
+ int register_subcircuit(string name);
+ const ParseSubcircuit *find_subcircuit(const string &name) const;
+
+ string get_or_create_net(string net_name, unsigned int size = 1,
+ ParseNet::Type type = ParseNet::NONE)
+ {
+ if (net_name == "_")
+ {
+ net_name = "_" + to_string(unnamed_net_count++);
+ }
+
+ string full_net_name = name_prefix() + net_name;
+ if (nets.count(full_net_name) == 0)
+ {
+ nets.emplace(full_net_name, ParseNet(type, size));
+ return full_net_name;
+ }
+ if (nets[full_net_name].size() != size)
+ {
+ cerr << "Incompatible size: " << net_name << "<" << size << ">"
+ << " (previously declared size was: " << nets[full_net_name].size()
+ << ")" << endl;
+ exit(1);
+ }
+ if (nets[full_net_name].type() == ParseNet::NONE && type != ParseNet::NONE)
+ nets[full_net_name].m_type = type;
+ if (type == ParseNet::NONE && nets[full_net_name].type() != ParseNet::NONE)
+ type = nets[full_net_name].m_type;
+ if (nets[full_net_name].type() != type)
+ {
+ cerr << "Incompatible type: " << net_name << "[" << ParseNet(type).type_str() << "]"
+ << " (previously declared type was: " << nets[full_net_name].type_str()
+ << ")" << endl;
+ exit(1);
+ }
+ return full_net_name;
+ }
+
+ string get_or_create_net(int i, unsigned int size = 1,
+ ParseNet::Type type = ParseNet::NONE)
+ {
+ return get_or_create_net(ParseNet::name_from_id(i), size, type);
+ }
+ string get_or_create_net(Variable v, unsigned int size = 1,
+ ParseNet::Type type = ParseNet::NONE)
+ {
+ if (v.type == Variable::STRING)
+ return get_or_create_net(v.as_string(), size, type);
+ else if (v.type == Variable::INTEGER)
+ return get_or_create_net(v.as_integer(), size, type);
+ else
+ {
+ cerr << "Incompatible variable type for creating a net" << endl;
+ exit(1);
+ }
+ }
+
+ string name_prefix() const
+ {
+ string ret = "";
+ if ( !is_subcircuit && parent )
+ {
+ ret = parent->name_prefix();
+ if ( name.empty() )
+ {
+ cerr << "Name of child parse tree should not be empty" << endl;
+ exit(1);
+ }
+ }
+ ret += name + "/";
+ strutils::toupper(ret);
+ return ret;
+ }
+
+ template <typename T>
+ void set_local_variable(string &name, T val)
+ {
+ // will throw a compile error if type T is not supported by Variable
+ local_assignments[name] = val;
+ }
+
+ template <typename T>
+ void set_global_variable(string &name, T val)
+ {
+ // will throw a compile error if type T is not supported by Variable
+ global_assignments[name] = val;
+ }
+
+ Variable get_variable(string &name)
+ {
+ // try to find variable in locals table first
+ auto it = local_assignments.find(name);
+ if (it != local_assignments.end())
+ return it->second;
+
+ // try to find variable in globals table
+ it = global_assignments.find(name);
+ if (it != global_assignments.end())
+ return it->second;
+
+ // error
+ cerr << "Variable not found: " << name << endl;
+ exit(1);
+ }
+
+ void print() const;
+ string to_json() const;
+
+ ParseTree *clone() const
+ {
+ ParseTree *clone = new ParseTree(name);
+
+ clone->parent = nullptr;
+ clone->ports = ports;
+ clone->nets = nets;
+ clone->local_assignments = local_assignments;
+ clone->unnamed_net_count = unnamed_net_count;
+
+ for (const auto &x : elements)
+ clone->elements.push_back(x->clone());
+ for (const auto &x : analyses)
+ clone->analyses.push_back(x->clone());
+ for (const auto &x : directives)
+ clone->directives.push_back(x->clone());
+ for (const auto &x : subcircuits)
+ clone->subcircuits.push_back(x->clone());
+
+ return clone;
+ }
+ void build_circuit();
+ void flatten();
+
+ ~ParseTree()
+ {
+ if (!parent)
+ {
+ for (const auto &x : elements)
+ if(x) delete x;
+ for (const auto &x : analyses)
+ if(x) delete x;
+ for (const auto &x : directives)
+ if(x) delete x;
+ for (const auto &x : subcircuits)
+ if(x) delete x;
+ }
+ }
+};
+
+struct ParseTreeCreationHelper {
+ ParseTree *pt;
+
+ // backlog: elements to be created in priority
+ map<string, ParseNet> *nets;
+
+ // backlog: elements to be created in priority
+ set<const ParseElement *> elements_backlog;
+
+ // Circuit elements generated by build
+ map<string, vector<shared_ptr<sc_object>>> circuit_signals;
+ map<string, shared_ptr<sc_module>> circuit_modules;
+
+ ParseTreeCreationHelper()
+ : pt(nullptr)
+ {}
+
+ ParseTreeCreationHelper(ParseTree *parse_tree)
+ {
+ resetParseTree(parse_tree);
+ }
+
+ void resetParseTree(ParseTree *parse_tree)
+ {
+ pt = parse_tree;
+ nets = &parse_tree->nets;
+ elements_backlog.clear();
+ circuit_signals.clear();
+ circuit_modules.clear();
+ }
+
+ // return next bidir net which doesn't have a corresponding signal in circuit_signals
+ map<string, ParseNet>::iterator next_fresh_bidir_net();
+
+ // return next net which doesn't have a corresponding signal in circuit_signals
+ map<string, ParseNet>::iterator next_fresh_net();
+
+ // return next element which is connected to "net_name" and doesnt have a corresponding module in circuit_modules
+ vector<ParseElement *>::iterator next_fresh_element_bound_to(const string &net_name, const set<const ParseElement *> &excludes = {});
+
+ // upgrade a signal to bidirectional
+ void upgrade_signal(const string &name);
+
+ // Create signals connected to a given element
+ void create_signals(const ParseElement *elem);
+
+ // Connect a port to a signal using the net name
+ template <typename signal_type, typename port_type>
+ void connect_uni(port_type &port, const string& net_name, bool as_writer);
+
+ // Connect a bidirectional port to a signal pair using the net name
+ template <typename signal_type, typename port_in_type, typename port_out_type>
+ void connect_bi(port_in_type &p_in, port_out_type &p_out, const string& net_name);
+
+ ~ParseTreeCreationHelper()
+ {}
+};
+
+/** ******************************************* **/
+/** Non-specialized template implementations **/
+/** ******************************************* **/
+
+template <typename signal_type, typename port_type>
+void ParseTreeCreationHelper::connect_uni(port_type &port, const string& net_name, bool as_writer)
+{
+ assert(pt->nets.find(net_name) != pt->nets.end());
+ const auto &net = pt->nets.at(net_name);
+
+ // Get the signal to be written or read
+ signal_type *sig = nullptr;
+ if ( !net.bidirectional())
+ {
+ auto sig_raw = circuit_signals.at(net_name)[0].get();
+ sig = dynamic_cast<signal_type *>(sig_raw);
+ }
+ else
+ {
+ int i_write = pt->nets.at(net_name).m_connect_count;
+ int i_read = (i_write + 1) % 2;
+ int i = as_writer ? i_write : i_read;
+
+ auto sig_raw = circuit_signals.at(net_name)[i].get();
+ sig = dynamic_cast<signal_type *>(sig_raw);
+ }
+
+ // Check it could correctly be cast to requested type
+ if (!sig)
+ {
+ cerr << "Wrong signal type for " << sig << " connected to "
+ << net_name << endl;
+ exit(1);
+ }
+
+ // Connect the port to the signal
+ port.bind(*sig);
+
+ // Update connection count of the net
+ pt->nets[net_name].m_connect_count++;
+ if (as_writer)
+ pt->nets[net_name].m_connect_writer_count++;
+ else
+ pt->nets[net_name].m_connect_reader_count++;
+}
+
+template <typename signal_type, typename port_in_type, typename port_out_type>
+void ParseTreeCreationHelper::connect_bi(port_in_type &p_in, port_out_type &p_out, const string& net_name)
+{
+ assert(pt->nets.find(net_name) != pt->nets.end());
+ const auto &net = pt->nets.at(net_name);
+
+ if (net.bidirectional())
+ {
+
+ // First get the signal to be written or read (in the right order)
+ int i_write = pt->nets.at(net_name).m_connect_count;
+ int i_read = (i_write + 1) % 2;
+
+ auto sig_writeable_raw = circuit_signals.at(net_name)[i_write].get();
+ auto sig_readable_raw = circuit_signals.at(net_name)[i_read].get();
+ auto sig_writeable = dynamic_cast<signal_type *>(sig_writeable_raw);
+ auto sig_readable = dynamic_cast<signal_type *>(sig_readable_raw);
+
+ // Check they could correctly be cast to requested type
+ if (!sig_writeable)
+ {
+ cerr << "Wrong signal type for " << sig_writeable << " connected to "
+ << net_name << endl;
+ exit(1);
+ }
+ if (!sig_readable)
+ {
+ cerr << "Wrong signal type for " << sig_readable << " connected to "
+ << net_name << endl;
+ exit(1);
+ }
+
+ // Connect ports to both signals
+ p_out.bind(*sig_writeable);
+ p_in.bind(*sig_readable);
+
+ // Update connection count of the net
+ pt->nets[net_name].m_connect_count++;
+ pt->nets[net_name].m_connect_writer_count++;
+ pt->nets[net_name].m_connect_reader_count++;
+ }
+ else
+ {
+ cerr << "Expected a bidirectional net for " << net_name << endl;
+ exit(1);
+ }
+} \ No newline at end of file
diff --git a/src/parser/parser.l b/src/parser/parser.l
new file mode 100644
index 0000000..ca689a3
--- /dev/null
+++ b/src/parser/parser.l
@@ -0,0 +1,438 @@
+ALPHA [[:alpha:]]
+DIGIT [0-9]
+ALPHA_NUM {ALPHA}|{DIGIT}
+ALPHA_PLUS {ALPHA}|[_]
+ALPHA_PLUS_NUM {ALPHA}|[_]|{DIGIT}
+D {DIGIT}
+
+DOUBLE (({D}+)|({D}*\.{D}+))([eE][-+]?[0-9]+)?
+
+BIDIR_SPECIFIER [&]
+BIN_OPERATOR [,=\*/\+\-\^]
+PAR_OPERATOR [\{\}\(\)\[\]<>]
+OPERATOR {BIN_OPERATOR}|{PAR_OPERATOR}|{BIDIR_SPECIFIER}
+
+
+WS [  \t]
+WS_BREAK (?:[^{WS}])
+
+/* EOL_DOS \r\n */
+/* EOL_UNIX \n */
+/* EOL_OLD_MAC \n */
+EOL_PORTABLE \r?\n
+EOL {EOL_PORTABLE}
+
+%option warn
+%option yylineno
+%option noyywrap
+%option caseless
+%option reentrant
+%option bison-bridge
+%option stack
+
+%{
+#include <string>
+#include <sstream> // for conversions of numbers from str
+#include <cstring> // for strcpy
+#include <vector>
+#include "parse_tree.h"
+
+#include "parser.tab.h"
+
+using std::string;
+using std::vector;
+
+#define YY_NO_INPUT
+#define YY_NO_UNPUT
+#define YY_EXTRA_TYPE void*
+
+YY_BUFFER_STATE yy_create_buffer ( FILE *file, int size, yyscan_t scanner);
+void yypush_buffer_state ( YY_BUFFER_STATE buffer, yyscan_t scanner);
+void yypop_buffer_state ( yyscan_t scanner );
+int yyget_lineno ( yyscan_t scanner );
+int yyget_column ( yyscan_t scanner );
+void yyset_extra ( YY_EXTRA_TYPE user_defined, yyscan_t scanner );
+YY_EXTRA_TYPE yyget_extra ( yyscan_t scanner );
+
+int yylerror(yyscan_t scanner, const char *p)
+{
+ cerr << "error (line " << yyget_lineno(scanner) << "): " << p << endl;
+ string * str = (string *)yyget_extra(scanner);
+ if (str)
+ cerr << "\twhile parsing line: " << *str << endl;
+ return 3;
+}
+
+ParseTree *cur_pt = nullptr;
+
+static int internal_subckt_count = 0;
+static size_t delete_me_npos = 0;
+
+template<typename T>
+static T from_string(const string &str)
+{
+ std::stringstream ss(str);
+ T ret;
+ ss >> ret;
+ return ret;
+}
+
+%}
+
+/*** States ***/
+/* %x IN_DQUOTES */
+%x INCLUDE
+%s SUBCKT_DECLARATION
+%x SUBCKT_DEFINITION
+/*Quoted string (returns char[]) (capture until next '"' and throw error on newline)*/
+%x IN_DQUOTES
+
+
+
+/* To ignore case: (?i:xxx) */
+
+%% /*** Rules section ***/
+
+^\.include {yy_push_state(INCLUDE, yyscanner);}
+<INCLUDE>{WS}* /* eat the whitespace */
+<INCLUDE>[^ ;\t\n\r]+ { /* got the include file name */
+ yyin = fopen( yytext, "r" );
+
+ if ( ! yyin )
+ {
+ cerr << "Error: included file not found \"" << yytext << "\"" << endl;
+ exit(1);
+ }
+ cout << "Including file: " << yytext << endl;
+ yypush_buffer_state(yy_create_buffer( yyin, YY_BUF_SIZE, yyscanner), yyscanner);
+
+ //yy_push_state(INITIAL, yyscanner);
+ BEGIN(INITIAL);
+ }
+
+<IN_DQUOTES>\" {
+ yy_pop_state(yyscanner);
+ // cout << "leaving dquotes mode" << endl;
+ return '"';
+ }
+<IN_DQUOTES>{EOL} { exit(yylerror(yyscanner, "Unmatched double quote")); }
+<IN_DQUOTES>[^\"\r\n]* {
+ // cout << "found quoted string: " << yytext << endl;
+ yylval_param->s_ptr = new string(yytext);
+ return T_STR;
+ }
+
+<SUBCKT_DECLARATION>{EOL} {
+ // end of subcircuit declaration line (.subckt name node1 node2...)
+ // go into subckt_definition state, which copies all text to a string
+ // until the corresponding .ends is reached
+ yylval_param->s_ptr = new string;
+ internal_subckt_count = 0;
+ delete_me_npos = 0;
+ // cout << endl << "going to subckt definition mode" << endl;
+ BEGIN(SUBCKT_DEFINITION); //needs BEGIN and not yy_push_state
+}
+
+<SUBCKT_DEFINITION>^\.SUBCKT {
+ // cout << "hello2" << endl;
+ // increase internal count for keeping track of included subcircuits
+ internal_subckt_count++;
+ // but reject the rule and go to the next matching pattern
+ // (i.e. save the line to the string)
+ REJECT;
+}
+
+<SUBCKT_DEFINITION>^\.ENDS {
+ if (internal_subckt_count)
+ {
+ // found .ends of included subcircuit
+ // decrease internal count
+ --internal_subckt_count;
+ // but reject the rule and go to the next matching pattern
+ // (i.e. save the line to the string)
+ REJECT;
+ }
+ else
+ {
+ // found .ends of current subcircuit
+ // go back to normal parsing
+ //BEGIN(INITIAL);
+ yy_pop_state(yyscanner);
+ // return .ends token so that the subcircuit string is recorded
+ return T_DIRECTIVE_ENDS;
+ }
+}
+
+<SUBCKT_DEFINITION>. {
+ /* save characters to buffer as-is */
+ *yylval_param->s_ptr += yytext;
+}
+
+<SUBCKT_DEFINITION>{EOL} {
+ /* save characters to buffer */
+ *yylval_param->s_ptr += yytext;
+
+ // cout << "found subckt line: " << yylval_param->s_ptr->substr(delete_me_npos);
+ // delete_me_npos = yylval_param->s_ptr->size();
+}
+
+<SUBCKT_DEFINITION><<EOF>> {
+ cerr << "Reached end of file before end of subcircuit definition" << endl;
+ exit(1);
+}
+
+\" {
+ yy_push_state(IN_DQUOTES, yyscanner);
+ // cout << "entering dquotes mode" << endl;
+ return '"';
+ }
+
+[+-]?{D}+ {
+ /* signed integer number */
+ yylval_param->i_val = from_string<int>(yytext);
+ return T_INT;
+}
+
+[+-]?{DOUBLE} {
+ /* signed floating point number */
+ yylval_param->d_val = from_string<double>(yytext);
+ return T_NUM;
+}
+
+(;[^\n]*) {
+ /* inline comment starts with ; */
+ continue;
+}
+
+^(\*[^\n]*) {
+ /* full line comment starts with **/
+ continue;
+}
+
+^X({ALPHA_PLUS_NUM})+ {
+ /* subcircuit instance name */
+ yylval_param->s_ptr = new string(yytext);
+ return T_ELEM_X;
+}
+
+^WG({ALPHA_PLUS_NUM})+ {
+ /* waveguide instance name */
+ yylval_param->s_ptr = new string(yytext);
+ return T_ELEM_WG;
+}
+
+^MERGER({ALPHA_PLUS_NUM})+ {
+ /* merger instance name */
+ yylval_param->s_ptr = new string(yytext);
+ return T_ELEM_MERGER;
+}
+
+^SPLITTER({ALPHA_PLUS_NUM})+ {
+ /* merger instance name */
+ yylval_param->s_ptr = new string(yytext);
+ return T_ELEM_SPLITTER;
+}
+
+^COUPLER({ALPHA_PLUS_NUM})+ {
+ /* DC instance name */
+ yylval_param->s_ptr = new string(yytext);
+ return T_ELEM_COUPLER;
+}
+
+^PSHIFT({ALPHA_PLUS_NUM})+ {
+ /* PHASESHIFTER instance name */
+ yylval_param->s_ptr = new string(yytext);
+ return T_ELEM_PSHIFT;
+}
+
+^MZI({ALPHA_PLUS_NUM})+ {
+ /* MZI instance name */
+ yylval_param->s_ptr = new string(yytext);
+ return T_ELEM_MZI;
+}
+
+^CROSSING({ALPHA_PLUS_NUM})+ {
+ /* CROSSING instance name */
+ yylval_param->s_ptr = new string(yytext);
+ return T_ELEM_CROSSING;
+}
+
+^CWSRC({ALPHA_PLUS_NUM})+ {
+ /* Continuous-wave source instance name */
+ yylval_param->s_ptr = new string(yytext);
+ return T_ELEM_CWSRC;
+}
+
+^VLSRC({ALPHA_PLUS_NUM})+ {
+ /* Value-list source instance name */
+ yylval_param->s_ptr = new string(yytext);
+ return T_ELEM_VLSRC;
+}
+
+^EVLSRC({ALPHA_PLUS_NUM})+ {
+ /* Electric Value-list source instance name */
+ yylval_param->s_ptr = new string(yytext);
+ return T_ELEM_EVLSRC;
+}
+
+^PROBE({ALPHA_PLUS_NUM})+ {
+ /* Probe instance */
+ yylval_param->s_ptr = new string(yytext);
+ return T_ELEM_PROBE;
+}
+
+^MLPROBE({ALPHA_PLUS_NUM})+ {
+ /* Probe instance */
+ yylval_param->s_ptr = new string(yytext);
+ return T_ELEM_MLPROBE;
+}
+
+^PDET({ALPHA_PLUS_NUM})+ {
+ /* Probe instance */
+ yylval_param->s_ptr = new string(yytext);
+ return T_ELEM_PDET;
+}
+
+^PCMCELL({ALPHA_PLUS_NUM})+ {
+ /* Probe instance */
+ yylval_param->s_ptr = new string(yytext);
+ return T_ELEM_PCMCELL;
+}
+
+^\.ASSIGN { return T_LOCAL_ASSIGNMENT; }
+^\.PARAM { return T_LOCAL_ASSIGNMENT; }
+^\.SAVE { return T_DIRECTIVE_SAVE; }
+
+^\.OP { return T_ANALYSIS_OP; }
+^\.DC { return T_ANALYSIS_DC; }
+^\.TRAN { return T_ANALYSIS_TRAN; }
+
+^\.OPTIONS { return T_DIRECTIVE_OPTIONS; }
+^\.NODESET { return T_DIRECTIVE_NODESET; }
+^\.IC { exit(1); }
+^\.SUBCKT {
+ /* Subcircuit declaration */
+ // enter new parser state
+ yy_push_state(SUBCKT_DECLARATION, yyscanner);
+ // return token so bison can correctly parse the declaration
+ return T_DIRECTIVE_SUBCKT;
+}
+
+<INITIAL>^\.ENDS {
+ cerr << "Unexpeced '.ENDS' directive" << endl;
+ exit(1);
+}
+
+{ALPHA_PLUS}{ALPHA_PLUS_NUM}*/{WS}*= {
+ yylval_param->s_ptr = new string(yytext);
+
+ return T_ASSIGN_ID;
+}
+
+{ALPHA_PLUS}{ALPHA_PLUS_NUM}* {
+ yylval_param->s_ptr = new string(yytext);
+
+ /*
+ if (yylval_param->s_ptr == "null")
+ return T_NONE;
+ if (yylval_param->s_ptr == "true")
+ {
+ yylval_param->b_val = true;
+ return T_BOOL;
+ }
+ if (yylval_param->s_ptr == "false")
+ {
+ yylval_param->b_val = false;
+ return T_BOOL;
+ }
+ */
+ return T_STR;
+}
+
+{OPERATOR} {
+ yylval_param->c_val = yytext[0];
+ return yytext[0];
+}
+
+{WS} ;
+
+^.*/{EOL} {
+ string * str = (string *)yyget_extra(yyscanner);
+ if (!str)
+ str = new string;
+ *str = yytext;
+ yyset_extra((void *)str, yyscanner);
+ REJECT;
+}
+
+(?# the following lines are for compatibility with KiCad generated netlists)
+(?# they were added fast with relatively no checking for potential problems if)
+(?# the keywords are found in subcircuit files or included files)
+^\.TITLE({WS}.*)?/{EOL} {
+ // cout << "Found '.title' directive, ignoring." << endl;
+}
+^\.END {
+ // cout << "Found '.end' directive, ignoring." << endl;
+}
+
+(?# Ignore EOL followed by '+' -- line continuation)
+{EOL}\+ ;
+
+{EOL} { return '\n'; }
+
+<INITIAL,INCLUDE><<EOF>> {
+ yypop_buffer_state(yyscanner);
+
+ if ( !YY_CURRENT_BUFFER )
+ {
+ yyterminate();
+ }
+ return '\n';
+ }
+
+<SUBCKT_DECLARATION,IN_DQUOTES><<EOF>> {
+ exit(yylerror(yyscanner, "Unexpected end of file."));
+}
+
+<*>. {
+ stringstream msg;
+ msg << "Unexpected character: \"";
+ switch (yytext[0])
+ {
+ case '\r':
+ msg << "\\r";
+ break;
+ case '\n':
+ msg << "\\n";
+ break;
+ case '\t':
+ msg << "\\t";
+ break;
+ case '\b':
+ msg << "\\b";
+ break;
+ default:
+ msg << yytext[0];
+ break;
+ }
+ msg << "\"";
+ exit(yylerror(yyscanner, msg.str().c_str()));
+ }
+%%
+
+/* END */
+
+/* ^.OP(?:{WS}+) { return T_DIRECTIVE_OP; } */
+/* ^.DC(?:{WS}+) { return T_DIRECTIVE_DC; } */
+
+/* Quoted string with 2 strings on the same line (returns char [])
+\"[^\"\\]*\" {
+ int n = strlen(yytext) - 2;
+ yylval_param->sv = new char[n+1];
+ strncpy(yylval_param->sv, yytext + 1, n);
+ yylval_param->sv[n] = '\0';
+ return T_QUOTED_STR;
+}
+*/
+
+
diff --git a/src/parser/parser.y b/src/parser/parser.y
new file mode 100644
index 0000000..36be00f
--- /dev/null
+++ b/src/parser/parser.y
@@ -0,0 +1,961 @@
+%define api.pure full
+%param { yyscan_t scanner }
+
+
+%code requires{
+ typedef void * yyscan_t;
+ // char *yyget_text ( yyscan_t yyscanner );
+ // int yyget_lineno ( yyscan_t yyscanner );
+ // int yylex(YYSTYPE * yylval, yyscan_t scanner);
+
+ //extern int yylineno;
+ //extern char *yytext;
+}
+
+%{
+#include <iostream>
+#include <string>
+#include <vector>
+#include <cstring>
+#include <cmath>
+#include <memory>
+#include "parse_tree.h"
+#include "parse_element.h"
+#include "parse_analysis.h"
+#include "parse_directive.h"
+
+#include "../build/parser/parser.tab.h"
+#include "../build/parser/parser.yy.h"
+
+using std::string;
+using std::vector;
+using std::pow;
+
+extern ParseTree *cur_pt;
+
+/*int yyerror(yyscan_t scanner, ParseTree *pt, const char *err)
+{
+ (void)pt;
+ (void)p;
+ printf("Error in the netlist (line %u:\"%s\")\n", yylineno, yytext);
+ printf("-- Error at line %u:\n\t%s\n", yylineno, err);
+ return 0;
+}
+*/
+int yyerror(yyscan_t scanner, ParseTree *pt, const char *err);
+%}
+
+%union {
+ double d_val;
+ int i_val;
+ char c_val;
+ string *s_ptr;
+ char *c_ptr;
+ vector<double> *vec_d_ptr;
+ pair<string, Variable> *assig_ptr;
+ Variable *variable_ptr;
+ vector<Variable> *vec_var_ptr;
+ pair<string, string> *elemattr_ptr;
+ pair<string, pair<string, Variable>> *netval_assign_ptr;
+ pair<pair<string, string>, vector<double>> *cw_sweep_order_ptr;
+}
+
+// %define api.value.type {struct YYSTYPE}
+
+%token <d_val> T_NUM
+%token <i_val> T_INT
+%token <i_val> T_NONE
+%token <s_ptr> T_STR
+%token <s_ptr> T_ASSIGN_ID
+%token <s_ptr> T_ELEM_CWSRC T_ELEM_VLSRC T_ELEM_EVLSRC
+%token <s_ptr> T_ELEM_WG T_ELEM_COUPLER T_ELEM_MERGER T_ELEM_SPLITTER
+%token <s_ptr> T_ELEM_PSHIFT T_ELEM_MZI T_ELEM_CROSSING T_ELEM_PCMCELL
+%token <s_ptr> T_ELEM_PROBE T_ELEM_MLPROBE T_ELEM_PDET
+%token <s_ptr> T_ELEM_X
+%token <i_val> T_ANALYSIS_OP T_ANALYSIS_DC T_ANALYSIS_TRAN
+%token <i_val> T_DIRECTIVE_OPTIONS T_DIRECTIVE_NODESET
+%token <i_val> T_DIRECTIVE_SUBCKT
+%token <s_ptr> T_DIRECTIVE_ENDS
+%token <s_ptr> T_DIRECTIVE_SAVE
+%token <i_val> T_LOCAL_ASSIGNMENT
+%type <c_val> '='
+
+
+// Available circuit elements
+%type <i_val> element.wg
+%type <i_val> element.merger
+%type <i_val> element.splitter
+%type <i_val> element.coupler
+%type <i_val> element.pshift
+%type <i_val> element.mzi
+%type <i_val> element.crossing
+%type <i_val> element.pcm_cell
+%type <i_val> element.cwsrc
+%type <i_val> element.vlsrc
+%type <i_val> element.evlsrc
+%type <i_val> element.probe
+%type <i_val> element.mlprobe
+%type <i_val> element.pdet
+%type <i_val> element.x
+
+// Available analysis
+%type <i_val> analysis.op
+%type <i_val> analysis.dc
+%type <i_val> analysis.tran
+%type <cw_sweep_order_ptr> cw_sweep_order
+
+// Available directives
+%type <i_val> directive.options
+%type <i_val> directive.nodeset
+
+// Rules for circuit nets (value is index in PT elements)
+%type <s_ptr> net_name.base.str
+%type <s_ptr> net_name_base
+%type <s_ptr> net_name
+%type <s_ptr> net.oanalog net.eanalog net.edigital
+%type <s_ptr> net.oa.explicit_bidir
+%type <s_ptr> net.oa.bidir
+%type <s_ptr> net.oa.in
+%type <s_ptr> net.oa.out
+
+//%type <s_ptr> bus_name bus bus.oanalog bus.eanalog bus.edigital
+
+// Rules related to subcircuits
+%type <i_val> subcircuit
+%type <i_val> subcircuit.with_ports
+%type <i_val> subcircuit.with_assignments
+%type <s_ptr> port_name
+
+// Rules for circuit elements (value is index in PT elements arrays)
+%type <i_val> atomelement element
+%type <i_val> element.with_args element.with_kwargs
+%type <i_val> element.x.with_args element.x.with_kwargs
+
+// Rules for circuit analysis (value is ptr to ParseAnalysis)
+%type <i_val> atomanalysis analysis
+%type <i_val> analysis.with_args analysis.with_kwargs
+
+// Rules for simulator directives (value is ptr to ParseDirective)
+%type <i_val> atomdirective directive
+%type <i_val> directive.with_args directive.with_kwargs
+
+// For argument parsing
+%type <assig_ptr> assignment
+%type <variable_ptr> variable variable_base variable_or_empty
+%type <variable_ptr> variable.free_expr variable.delimited_expr
+//%type <variable_ptr> variable.str_without_quotes
+%type <netval_assign_ptr> netval_assignment
+%type <elemattr_ptr> element_attribute
+
+// For expression parsing
+%type <variable_ptr> expr term fact pow constant
+%type <variable_ptr> listvar.tail listvar.unbound listvar.bound //TODO: check
+
+// Add parse tree parameter to yyparse signature
+%parse-param {ParseTree *pt}
+//%verbose
+//%define parse.error detailed
+//%define parse.trace
+
+// On start, set current parse tree to the one passed in call to yyparse
+%initial-action {
+ cur_pt = pt;
+ if (!pt)
+ yyerror(scanner, pt, "Invalid parse tree pointer");
+}
+
+
+%%
+
+input: %empty
+ | input line '\n'
+;
+
+line: %empty
+ | element { /* cout << "Inserted element: " << cur_pt->elements[$1]->name << endl; */ }
+ | analysis { /* cout << "Registered analysis [...]" << endl; */ }
+ | directive { /* cout << "Registered directive [...]" << endl; */ }
+ | subcircuit { /* cout << "Parsed subcircuit [...]" << endl; */ }
+ | local_assignment { /* cout << "Processed local assignment [...]" << endl; */ }
+;
+
+local_assignment: T_LOCAL_ASSIGNMENT assignment
+ {
+ cur_pt->set_local_variable($2->first, $2->second);
+ }
+ | local_assignment assignment
+ {
+ cur_pt->set_local_variable($2->first, $2->second);
+ }
+;
+
+/* --------- Directives definitions parsing ----------- */
+
+directive.options: T_DIRECTIVE_OPTIONS { $$ = cur_pt->register_directive(new OPTIONSDirective); }
+;
+
+directive.nodeset: T_DIRECTIVE_NODESET { $$ = cur_pt->register_directive(new NODESETDirective); }
+ | directive.nodeset netval_assignment
+ {
+ static_cast<NODESETDirective *>(cur_pt->directives[$1])->net_assignments[$2->first].push_back($2->second);
+ $$ = $1;
+ }
+;
+subcircuit.with_ports: T_DIRECTIVE_SUBCKT T_STR
+ {
+ // Register a new subcircuit in current parse tree
+ $$ = cur_pt->register_subcircuit(*$2);
+ delete $2;
+ }
+ | subcircuit.with_ports port_name
+ {
+ if (!cur_pt->subcircuits[$1]->kwargs.empty())
+ {
+ cerr << "Expected keyword assignment in subcircuit declaration but got: " << *$2 << endl;
+ exit(1);
+ }
+ // cout << *$2 << endl;
+ cur_pt->subcircuits[$1]->register_port(*$2);
+ $$ = $1;
+ delete $2;
+ }
+;
+subcircuit.with_assignments:
+ subcircuit.with_assignments assignment
+ {
+ cur_pt->subcircuits[$1]->kwargs[$2->first] = $2->second;
+
+ $$ = $1;
+ delete $2;
+ }
+| subcircuit.with_ports
+;
+
+subcircuit: subcircuit.with_assignments T_DIRECTIVE_ENDS
+ {
+ // cout << "Parsed subcircuit " << cur_pt->subcircuits[$1]->name << endl;
+ cur_pt->subcircuits[$1]->netlist = *$2;
+ delete $2;
+ }
+;
+
+/* --------- Elements definitions parsing ----------- */
+
+element.wg: T_ELEM_WG net.oa.in net.oa.out
+ {
+ $$ = cur_pt->register_element(new WGElement(*$1, {*$2, *$3}));
+ delete $1;
+ delete $2;
+ delete $3;
+ }
+;
+
+element.merger: T_ELEM_MERGER net.oa.in net.oa.in net.oa.out
+ {
+ $$ = cur_pt->register_element(new MergerElement(*$1, {*$2, *$3, *$4}));
+ delete $1;
+ delete $2;
+ delete $3;
+ delete $4;
+ }
+;
+
+element.splitter: T_ELEM_SPLITTER net.oa.in net.oa.out net.oa.out
+ {
+ $$ = cur_pt->register_element(new SplitterElement(*$1, {*$2, *$3, *$4}));
+ delete $1;
+ delete $2;
+ delete $3;
+ delete $4;
+ }
+;
+
+element.coupler: T_ELEM_COUPLER net.oa.in net.oa.in net.oa.out net.oa.out
+ {
+ $$ = cur_pt->register_element(new DCElement(*$1, {*$2, *$3, *$4, *$5}));
+ delete $1;
+ delete $2;
+ delete $3;
+ delete $4;
+ delete $5;
+ }
+;
+
+element.pshift: T_ELEM_PSHIFT net.oa.in net.oa.out net.eanalog
+ {
+ $$ = cur_pt->register_element(new PhaseShifterElement(*$1, {*$2, *$3, *$4}));
+ delete $1;
+ delete $2;
+ delete $3;
+ delete $4;
+ }
+;
+
+element.mzi: T_ELEM_MZI net.oa.in net.oa.in net.oa.out net.oa.out net.eanalog
+ {
+ $$ = cur_pt->register_element(new MZIElement(*$1, {*$2, *$3, *$4, *$5, *$6}));
+ delete $1;
+ delete $2;
+ delete $3;
+ delete $4;
+ delete $5;
+ delete $6;
+ }
+;
+
+element.crossing: T_ELEM_CROSSING net.oa.in net.oa.in net.oa.out net.oa.out
+ {
+ $$ = cur_pt->register_element(new CrossingElement(*$1, {*$2, *$3, *$4, *$5}));
+ delete $1;
+ delete $2;
+ delete $3;
+ delete $4;
+ delete $5;
+ }
+;
+
+element.pcm_cell: T_ELEM_PCMCELL net.oa.in net.oa.out
+ {
+ $$ = cur_pt->register_element(new PCMCellElement(*$1, {*$2, *$3}));
+ delete $1;
+ delete $2;
+ delete $3;
+ }
+;
+
+element.cwsrc: T_ELEM_CWSRC net.oa.out
+ {
+ $$ = cur_pt->register_element(new CWSourceElement(*$1, {*$2}));
+ delete $1;
+ delete $2;
+ }
+;
+
+element.vlsrc: T_ELEM_VLSRC net.oa.out
+ {
+ $$ = cur_pt->register_element(new VLSourceElement(*$1, {*$2}));
+ delete $1;
+ delete $2;
+ }
+;
+
+element.evlsrc: T_ELEM_EVLSRC net.eanalog
+ {
+ $$ = cur_pt->register_element(new EVLSourceElement(*$1, {*$2}));
+ delete $1;
+ delete $2;
+ }
+;
+
+element.probe: T_ELEM_PROBE net.oa.in
+ {
+ $$ = cur_pt->register_element(new ProbeElement(*$1, {*$2}));
+ delete $1;
+ delete $2;
+ }
+;
+
+element.mlprobe: T_ELEM_MLPROBE net.oa.in
+ {
+ $$ = cur_pt->register_element(new MLProbeElement(*$1, {*$2}));
+ delete $1;
+ delete $2;
+ }
+;
+
+element.pdet: T_ELEM_PDET net.oa.in net.eanalog
+ {
+ $$ = cur_pt->register_element(new PhotodetectorElement(*$1, {*$2, *$3}));
+ delete $1;
+ delete $2;
+ delete $3;
+ }
+;
+
+element.x: T_ELEM_X
+ {
+ $$ = cur_pt->register_element(new XElement(*$1));
+ delete $1;
+ }
+;
+
+/* --------- Analyses definitions parsing ----------- */
+
+analysis.op: T_ANALYSIS_OP
+ { $$ = cur_pt->register_analysis(new OPAnalysis()); }
+;
+
+analysis.dc: T_ANALYSIS_DC
+ { $$ = cur_pt->register_analysis(new DCAnalysis()); }
+ | analysis.dc cw_sweep_order
+ {
+ auto ana = dynamic_cast<DCAnalysis *>(cur_pt->analyses[$1]);
+ if (ana->sweep_orders.size())
+ {
+ cerr << "Registering more than one DC sweep is not supported yet." << endl;
+ exit(1);
+ }
+ ana->register_sweep_order(*$2);
+ delete $2;
+ }
+;
+
+analysis.tran: T_ANALYSIS_TRAN
+ { $$ = cur_pt->register_analysis(new TRANAnalysis()); }
+;
+
+
+/* ---------- Directives arguments parsing ----------- */
+
+atomdirective: directive.options { $$ = $1; }
+ | directive.nodeset { $$ = $1; }
+;
+
+directive.with_args:
+ directive.with_args variable
+ {
+ // cout << "found positional arg: " << $2->get_str() << endl;
+ if (!cur_pt->directives[$1]->kwargs.empty())
+ yyerror(scanner, pt, "positional arguments cannot follow keyword arguments");
+ cur_pt->directives[$1]->args.emplace_back(*$2);
+ $$ = $1;
+ delete $2;
+ }
+| atomdirective { $$ = $1; }
+;
+
+directive.with_kwargs:
+ directive.with_kwargs assignment
+ {
+ // cout << "found keyword arg: " << $2->second.get_str() << endl;
+ cur_pt->directives[$1]->kwargs[$2->first] = $2->second;
+ $$ = $1;
+ delete $2;
+ }
+| directive.with_args
+;
+
+directive: directive.with_kwargs
+;
+
+/* --------- Elements arguments parsing ----------- */
+
+atomelement: element.wg { $$ = $1; }
+ | element.merger { $$ = $1; }
+ | element.splitter { $$ = $1; }
+ | element.coupler { $$ = $1; }
+ | element.pshift { $$ = $1; }
+ | element.mzi { $$ = $1; }
+ | element.crossing { $$ = $1; }
+ | element.pcm_cell { $$ = $1; }
+ | element.cwsrc { $$ = $1; }
+ | element.vlsrc { $$ = $1; }
+ | element.evlsrc { $$ = $1; }
+ | element.probe { $$ = $1; }
+ | element.mlprobe { $$ = $1; }
+ | element.pdet { $$ = $1; }
+;
+
+element.with_args:
+ element.with_args variable
+ {
+ // cout << "found positional arg: " << $2->get_str() << endl;
+ if (!cur_pt->elements[$1]->kwargs.empty())
+ yyerror(scanner, pt, "positional arguments cannot follow keyword arguments");
+ cur_pt->elements[$1]->args.emplace_back(*$2);
+ $$ = $1;
+
+ // TODO: for X element only, nets are stored as args
+ // Xnnnn n1 n2 n3 ... sub_name **sub_kwargs
+ // because the number of nets is not known when reading n1 n2 n3...
+ // [n1 n2 n3 ... subname] are also args
+ // when the time an arg is found, the previous one (if it exists), can be considered a net
+ // and therefore added to the element's nets array and discarded from args
+ // this will ensure that args will contain only [sub_name]
+ // for this to work, the X element cannot have args beyond sub_name (only kwargs)
+ if (cur_pt->elements[$1]->args.size() == 2)
+ {
+ // check if the args[0] is a valid net name
+ // create net with name args[0] (by default with type NONE)
+ // add args[0] to nets
+ // delete args[0] from args
+ }
+
+ delete $2;
+ }
+| atomelement { $$ = $1; }
+;
+
+element.with_kwargs:
+ element.with_kwargs assignment
+ {
+ // cout << "found keyword arg: " << $2->second.get_str() << endl;
+ cur_pt->elements[$1]->kwargs[$2->first] = $2->second;
+ $$ = $1;
+ delete $2;
+ }
+| element.with_args
+;
+
+element.x.with_args:
+ element.x.with_args net_name_base
+ {
+ // cout << "found positional arg: " << $2->get_str() << endl;
+ if (!cur_pt->elements[$1]->kwargs.empty())
+ yyerror(scanner, pt, "positional arguments cannot follow keyword arguments");
+ cur_pt->elements[$1]->args.emplace_back(Variable(*$2));
+ $$ = $1;
+
+ // TODO: for X element only, nets are stored as args
+ // Xnnnn n1 n2 n3 ... sub_name **sub_kwargs
+ // because the number of nets is not known when reading n1 n2 n3...
+ // [n1 n2 n3 ... subname] are also args
+ // when the time an arg is found, the previous one (if it exists), can be considered a net
+ // and therefore added to the element's nets array and discarded from args
+ // this will ensure that args will contain only [sub_name]
+ // for this to work, the X element cannot have args beyond sub_name (only kwargs)
+ if (cur_pt->elements[$1]->args.size() == 2)
+ {
+ // check if the args[0] is a valid net name
+ // create net with name args[0] (by default with type NONE)
+ // add args[0] to nets
+ // delete args[0] from args
+ }
+
+ delete $2;
+ }
+| element.x { $$ = $1; }
+;
+
+element.x.with_kwargs:
+ element.x.with_kwargs assignment
+ {
+ // cout << "found keyword arg: " << $2->second.get_str() << endl;
+ cur_pt->elements[$1]->kwargs[$2->first] = $2->second;
+ $$ = $1;
+ delete $2;
+ }
+| element.x.with_args
+;
+
+element:
+ element.with_kwargs
+| element.x.with_kwargs
+;
+
+/* ---------- Analysis arguments parsing ----------- */
+
+atomanalysis: analysis.op { $$ = $1; }
+ | analysis.dc { $$ = $1; }
+ | analysis.tran { $$ = $1; }
+;
+
+analysis.with_args:
+ analysis.with_args variable
+ {
+ // cout << "found positional arg: " << $2->get_str() << endl;
+ if (!cur_pt->analyses[$1]->kwargs.empty())
+ yyerror(scanner, pt, "positional arguments cannot follow keyword arguments");
+ cur_pt->analyses[$1]->args.emplace_back(*$2);
+ $$ = $1;
+ delete $2;
+ }
+| atomanalysis { $$ = $1; }
+;
+
+analysis.with_kwargs:
+ analysis.with_kwargs assignment
+ {
+ // cout << "found keyword arg: " << $2->second.get_str() << endl;
+ cur_pt->analyses[$1]->kwargs[$2->first] = $2->second;
+ $$ = $1;
+ delete $2;
+ }
+| analysis.with_args
+;
+
+analysis: analysis.with_kwargs
+
+/* --------- NET type parsing ----------- */
+
+net.oanalog: net_name
+ {
+ bool ok = cur_pt->nets[*$1].setType(ParseNet::OANALOG);
+ if (!ok)
+ {
+ stringstream ret;
+ ret << "Error creating net '" + *$1 + "': net already exists with a different type" << endl;
+ ret << "\t (new type: " << "OANALOG" << ", old type: " << cur_pt->nets[*$1].type_str() << ")" << endl;
+ yyerror(scanner, pt, ret.str().c_str());
+ }
+ cur_pt->nets[*$1].m_ports_count++;
+ $$ = $1;
+ }
+;
+
+net.oa.explicit_bidir:
+ net.oanalog '&'
+ {
+ // this net will be both written to and read from by the new device
+ // so increment both reader and writer count
+ // and set the explicit "bidirectional" flag to true
+ cur_pt->nets[*$1].m_bidirectional = true;
+ cur_pt->nets[*$1].m_readers_count++;
+ cur_pt->nets[*$1].m_writers_count++;
+ $$ = $1;
+ }
+;
+
+net.oa.bidir:
+ net.oa.explicit_bidir
+| net.oanalog
+ {
+ // this net will be both written to and read from by the new device
+ // so increment both reader and writer count
+ cur_pt->nets[*$1].m_readers_count++;
+ cur_pt->nets[*$1].m_writers_count++;
+ $$ = $1;
+ }
+;
+
+net.oa.in:
+ net.oanalog
+ {
+ // this net will only be read from by the new device
+ // so increment only reader count
+ cur_pt->nets[*$1].m_readers_count++;
+ $$ = $1;
+ }
+| net.oa.explicit_bidir
+ {
+ // bidir already got a writer and reader increment, but
+ // this net will only be read from by the new device
+ // so decrement writer count
+ cur_pt->nets[*$1].m_writers_count--;
+ $$ = $1;
+ }
+;
+
+net.oa.out:
+ net.oanalog
+ {
+ // this net will only be written to by the new device
+ // so increment only writer count
+ cur_pt->nets[*$1].m_writers_count++;
+ $$ = $1;
+ }
+| net.oa.explicit_bidir
+ {
+ // bidir already got a writer and reader increment, but
+ // this net will only be written to by the new device
+ // so decrement reader count
+ cur_pt->nets[*$1].m_readers_count--;
+ $$ = $1;
+ }
+;
+
+net.eanalog: net_name
+ {
+ bool ok = cur_pt->nets[*$1].setType(ParseNet::EANALOG);
+ if (!ok)
+ {
+ stringstream ret;
+ ret << "Error creating net '" + *$1 + "': net already exists with a different type" << endl;
+ ret << "\t (new type: " << "EANALOG" << ", old type: " << cur_pt->nets[*$1].type_str() << ")" << endl;
+ yyerror(scanner, pt, ret.str().c_str());
+ }
+ $$ = $1;
+ }
+
+net.edigital: net_name
+ {
+ bool ok = cur_pt->nets[*$1].setType(ParseNet::EDIGITAL);
+ if (!ok)
+ {
+ stringstream ret;
+ ret << "Error creating net '" + *$1 + "': net already exists with a different type" << endl;
+ ret << "\t (new type: " << "EDIGITAL" << ", old type: " << cur_pt->nets[*$1].type_str() << ")";
+ yyerror(scanner, pt, ret.str().c_str());
+ }
+ $$ = $1;
+ }
+;
+
+/* --------- BUS type parsing ----------- */
+
+/*
+bus.oanalog: bus_name
+ {
+ cur_pt->nets[*$1].m_type = ParseNet::OANALOG;
+ $$ = $1;
+ }
+;
+
+bus.eanalog: bus_name
+ {
+ cur_pt->nets[*$1].m_type = ParseNet::EANALOG;
+ $$ = $1;
+ }
+;
+
+bus.edigital: bus_name
+ {
+ cur_pt->nets[*$1].m_type = ParseNet::EDIGITAL;
+ $$ = $1;
+ }
+;
+bus: bus.oanalog { $$ = $1; }
+ | bus.eanalog { $$ = $1; }
+ | bus.edigital { $$ = $1; }
+;
+*/
+/* --------- NET and BUS name parsing ----------- */
+
+net_name.base.str:
+ T_STR { $$ = $1; }
+ | '/' T_STR
+ {
+ $$ = $2;
+ *$2 = "/" + *$2;
+ }
+ | '/' T_INT
+ {
+ stringstream ss;
+ ss << "/" << $2;
+ $$ = new string(ss.str());
+ }
+ | net_name.base.str '-' T_STR
+ {
+ $$ = $1;
+ *$1 += "-" + *$3;
+ delete $3;
+ }
+;
+
+net_name_base: T_INT { $$ = new string(ParseNet::name_from_id($1)); }
+ | net_name.base.str { $$ = $1; }
+;
+
+net_name: net_name_base
+ {
+ $$ = new string(cur_pt->get_or_create_net(*$1));
+ delete $1;
+ }
+;
+
+/*
+bus_name: net_name { $$ = $1; }
+ | net_name_base '<' T_INT '>'
+ {
+ $$ = new string(cur_pt->get_or_create_net(*$1, $3));
+ delete $1;
+ }
+;
+*/
+port_name: net_name_base { $$ = $1; }
+;
+
+/* --------- Assignment and variable ----------- */
+
+netval_assignment: '/' net_name '/' T_STR '=' variable.delimited_expr
+ {
+ $$ = new pair<string, pair<string, Variable>>(
+ *$2,
+ pair<string, Variable>(*$4, *$6)
+ );
+ delete $2;
+ delete $4;
+ delete $6;
+ }
+;
+
+cw_sweep_order: element_attribute variable_base variable_base variable_base
+ {
+ // cout << $1->first << "->" << $1->second;
+ // cout << " = [" << $2->as_double();
+ // cout << ":" << $4->as_double();
+ // cout << ":" << $3->as_double() << "]" << endl;
+ $$ = new pair<pair<string, string>, vector<double>>(*$1, {$2->as_double(), $3->as_double(), $4->as_double()});
+ delete $1;
+ delete $2;
+ delete $3;
+ delete $4;
+ }
+;
+
+element_attribute: '/' T_STR '/' T_STR
+ {
+ // cout << *$2 << "->" << *$4 << endl;
+ $$ = new pair<string, string>(*$2,*$4);
+ delete $2;
+ delete $4;
+ }
+;
+
+assignment: T_ASSIGN_ID '=' variable
+ {
+ $$ = new pair<string, Variable>(*$1, *$3);
+ delete $1;
+ delete $3;
+ }
+;
+
+variable_base:
+ T_INT
+ {
+ $$ = new Variable($1);
+ }
+ | T_NUM
+ {
+ $$ = new Variable($1);
+ }
+ | T_NONE
+ {
+ $$ = new Variable(Variable::NONE);
+ }
+ | '{' T_STR '}'
+ {
+ $$ = new Variable(cur_pt->get_variable(*$2));
+ delete $2;
+ }
+ | '"' T_STR '"'
+ {
+ $$ = new Variable(*$2);
+ delete $2;
+ }
+ | listvar.bound
+ {
+ $$ = $1;
+ }
+;
+
+variable.delimited_expr:
+ '{' expr '}'
+ {
+ $$ = $2;
+ }
+| variable_base;
+;
+
+variable.free_expr:
+ expr
+ {
+ $$ = $1;
+ }
+;
+
+variable:
+ variable.free_expr
+;
+
+/*
+variable.str_without_quotes:
+ T_STR
+ {
+ if (*$1 == "None")
+ $$ = new Variable(Variable::NONE);
+ else
+ $$ = new Variable(*$1);
+ delete $1;
+ }
+| variable;
+;
+*/
+
+variable_or_empty:
+ variable
+ | %empty
+ {
+ $$ = new Variable(Variable::NONE);
+ }
+;
+
+/* ------------ Expression parsing ------------- */
+
+constant: variable_base { $$ = $1; }
+;
+
+expr:
+ expr '+' term { $$ = $1; *$$ += *$3; delete $3; }
+| expr '-' term { $$ = $1; *$$ -= *$3; delete $3; }
+| term { $$ = $1; }
+;
+
+term:
+ term '*' fact { $$ = $1; *$$ *= *$3; delete $3; }
+| term '/' fact { $$ = $1; *$$ /= *$3; delete $3; }
+| fact { $$ = $1; }
+;
+
+fact:
+ fact '^' pow { $$ = $1; *$$ ^= *$3; delete $3; }
+| pow { $$ = $1; }
+;
+
+pow:
+ constant
+| '(' expr ')' { $$ = $2; }
+;
+
+// --------------- variables list (elements can have different types)
+listvar.tail: variable_or_empty
+ {
+ $$ = new Variable(Variable::LIST);
+ $$->vec.insert($$->vec.begin(), *$1);
+ delete $1;
+ }
+;
+
+listvar.unbound: variable_or_empty ',' listvar.tail
+ {
+ $3->vec.insert($3->vec.begin(), *$1);
+ $$ = $3;
+ delete $1;
+ }
+ | variable_or_empty ',' listvar.unbound
+ {
+ $3->vec.insert($3->vec.begin(), *$1);
+ $$ = $3;
+ delete $1;
+ }
+;
+
+listvar.bound: '[' listvar.tail ']' { $$ = $2; }
+ | '[' listvar.unbound ']' { $$ = $2; }
+;
+
+%%
+
+int yyerror(yyscan_t scanner, ParseTree *pt, const char *err)
+{
+ (void)pt;
+ (void)scanner;
+ string token = yyget_text(scanner);
+ stringstream ss;
+ for (char c : token)
+ {
+ switch (c)
+ {
+ case '\r':
+ ss << "\\r";
+ break;
+ case '\n':
+ ss << "\\n";
+ break;
+ case '\t':
+ ss << "\\t";
+ break;
+ case '\b':
+ ss << "\\b";
+ break;
+ default:
+ ss << c;
+ break;
+ }
+ }
+ string token_escaped = ss.str();
+ printf("-- Error in the netlist (line %u): %s\n", yyget_lineno(scanner), err);
+ printf("-- \"%s\"\n", token_escaped.c_str());
+ //cerr << err << endl;
+ exit(1);
+}