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 \r */ EOL_PORTABLE \r?\n EOL {EOL_PORTABLE} %option warn %option yylineno %option noyywrap %option caseless %option reentrant %option bison-bridge %option stack %option extra-type="void *" %{ #include #include // for conversions of numbers from str #include // for strcpy #include #include #include "parse_tree.h" #include "parser_state.h" #include "parser.tab.h" using std::string; using std::vector; using std::deque; typedef struct { enum type { NETLIST_FILE, STRING } type; string description; YY_BUFFER_STATE buf; FILE *fp = nullptr; } netlist_content_t; deque netlist_content_fifo; #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 ); string yyget_filename ( yyscan_t scanner ); string yyget_current_line ( yyscan_t scanner ); string yyget_filename ( yyscan_t scanner ) { const ParserState *yy_extra = (ParserState *)yyget_extra(scanner); return yy_extra->current_filename; } void yyset_filename (string fname, yyscan_t scanner) { ParserState *yy_extra = (ParserState *)yyget_extra(scanner); yy_extra->current_filename = fname; } string yyget_current_line ( yyscan_t scanner ) { const ParserState *yy_extra = (ParserState *)yyget_extra(scanner); return yy_extra->current_line; } void yyset_current_line (string line_content, yyscan_t scanner) { ParserState *yy_extra = (ParserState *)yyget_extra(scanner); yy_extra->current_line = line_content; } int yy_load_next_buf(yyscan_t scanner) { static FILE *last_file = nullptr; if ( last_file ) { fclose(last_file); } if (netlist_content_fifo.empty()) { yyset_filename("-"s, scanner); return 1; } else { auto netlist_content = netlist_content_fifo.front(); netlist_content_fifo.pop_front(); string filename = netlist_content.description; if (netlist_content.type == netlist_content_t::STRING) filename = "[autogenerated - " + filename + "]"; yyset_filename(filename, scanner); yy_switch_to_buffer(netlist_content.buf, scanner); last_file = netlist_content.fp; return 0; } } void yy_add_content_from_string(const string &str, const string &desc, yyscan_t &scanner) { // Allocate memory for the string (c-style) // necessary as flex will delete the buffer char* cstr = new char[str.size() + 1]; // Copy the contents of the std::string to the new memory std::strcpy(cstr, str.c_str()); netlist_content_t netlist_content; netlist_content.type = netlist_content_t::STRING; netlist_content.description = desc; netlist_content.buf = yy_scan_string(cstr, scanner); netlist_content.fp = nullptr; netlist_content_fifo.push_back(netlist_content); } void yy_add_content_from_file(const string &filename, yyscan_t &scanner) { // Check whether the file exists FILE *f; if (filename == "-"s) { cerr << "Error: stdin not supported." << endl; exit(1); } else if (!(f = fopen(filename.c_str(), "r"))) { cerr << "Error: File not found \"" << filename << "\"" << endl; exit(1); } netlist_content_t netlist_content; { stringstream ss; ss << endl << "*** START of " << filename << " ***" << endl; yy_add_content_from_string(ss.str(), "header for " + filename, scanner); } netlist_content.type = netlist_content_t::NETLIST_FILE; netlist_content.description = filename; netlist_content.buf = yy_create_buffer(f, YY_BUF_SIZE, scanner); netlist_content.fp = f; netlist_content_fifo.push_back(netlist_content); { stringstream ss; ss << endl << "*** END of " << filename << " ***" << endl; yy_add_content_from_string(ss.str(), "footer for " + filename, scanner); } } int yylerror(yyscan_t scanner, const char *p) { cerr << "Parsing error (" << yyget_filename(scanner) << ":" << yyget_lineno(scanner) << "): " << p << endl; string str = yyget_current_line(scanner); if (!str.empty()) cerr << "-- while parsing line: \"" << str << "\"" << endl; return 3; } ParseTree *cur_pt = nullptr; static int internal_subckt_count = 0; static size_t delete_me_npos = 0; template static T from_string(const string &str) { std::stringstream ss(str); T ret; ss >> ret; return ret; } string convert_special_char(char c) { stringstream ss; 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; } return ss.str(); } %} /*** 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); } {WS}* /* eat the whitespace */ [^ ;\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); } \" { yy_pop_state(yyscanner); // cout << "leaving dquotes mode" << endl; return '"'; } {EOL} { exit(yylerror(yyscanner, "Unmatched double quote")); } [^\"\r\n]* { // cout << "found quoted string: " << yytext << endl; yylval_param->s_ptr = new string(yytext); return T_STR; } {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 { // 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; } ^\.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; } } . { /* save characters to buffer as-is */ *yylval_param->s_ptr += yytext; } {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(); } <> { 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}+) {D}+ { /* signed integer number */ yylval_param->i_val = from_string(yytext); return T_INT; } (?# [+-]?{DOUBLE}+) {DOUBLE} { /* signed floating point number */ yylval_param->d_val = from_string(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})+ { /* Multi-wavelength probe instance */ yylval_param->s_ptr = new string(yytext); return T_ELEM_MLPROBE; } ^PDET({ALPHA_PLUS_NUM})+ { /* Photodetector instance */ yylval_param->s_ptr = new string(yytext); return T_ELEM_PDET; } ^PCMCELL({ALPHA_PLUS_NUM})+ { /* PCM cell 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; } ^\.ENDS { cerr << "unexpected '.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} { yyset_current_line(yytext, yyscanner); if (false) cout << yytext << endl; 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'; } <> { yypop_buffer_state(yyscanner); if ( !YY_CURRENT_BUFFER ) { if ( ! yy_load_next_buf(yyscanner) ) return '\n'; else yyterminate(); } return '\n'; } <> { exit(yylerror(yyscanner, "unexpected end of file.")); } <*>. { stringstream msg; char c = yytext[0]; msg << "unexpected character: \""; msg << convert_special_char(c); 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; } */