X-Git-Url: https://git.libre-soc.org/?p=sv2nmigen.git;a=blobdiff_plain;f=lexor.py;h=dbf7c880dc2fe93c23a0ce3a4aaa967e83eeb484;hp=754fcf134459009e8553edaefa3400107a608035;hb=2e123971712d469265dc4c47a69a517cc43cc6d8;hpb=7e6264a3c76af3bd5349a2e52cbff329d0400ea5 diff --git a/lexor.py b/lexor.py index 754fcf1..dbf7c88 100644 --- a/lexor.py +++ b/lexor.py @@ -20,58 +20,11 @@ */ """ -from ply import lex - -""" -%x CCOMMENT -%x PCOMMENT -%x LCOMMENT -%x CSTRING -%s UDPTABLE -%x PPTIMESCALE -%x PPUCDRIVE -%x PPDEFAULT_NETTYPE -%x PPBEGIN_KEYWORDS -%s EDGES -%x REAL_SCALE - -# for timescales (regex subst patterns) -W = r'[ \t\b\f\r]+' -S = r'[afpnumkKMGT]' -TU r'[munpf]' - -%% - - /* Recognize the various line directives. */ -^"#line"[ \t]+.+ { line_directive(); } -^[ \t]?"`line"[ \t]+.+ { line_directive2(); } - -[ \t\b\f\r] { ; } -\n { yylloc.first_line += 1; } - - /* C++ style comments start with / / and run to the end of the - current line. These are very easy to handle. The meta-comments - format is a little more tricky to handle, but do what we can. */ - - /* The lexor detects "// synthesis translate_on/off" meta-comments, - we handle them here by turning on/off a flag. The pform uses - that flag to attach implicit attributes to "initial" and - "always" statements. */ +lex_debug = 0 -"//"{W}*"synthesis"{W}+"translate_on"{W}*\n { pform_mc_translate_on(true); } -"//"{W}*"synthesis"{W}+"translate_off"{W}*\n { pform_mc_translate_on(false); } -"//" { comment_enter = YY_START; BEGIN(LCOMMENT); } -. { yymore(); } -\n { yylloc.first_line += 1; BEGIN(comment_enter); } - - - /* The contents of C-style comments are ignored, like white space. */ +from ply import lex -"/*" { comment_enter = YY_START; BEGIN(CCOMMENT); } -. { ; } -\n { yylloc.first_line += 1; } -"*/" { BEGIN(comment_enter); } -""" +#DOCSTRING REMOVED states = (#('module', 'exclusive'), ('timescale', 'exclusive'),) @@ -503,69 +456,6 @@ literals = [ '[', '}', '{', ';', ':', '[', ']', ',', '(', ')', '#', '=', '.', '@', '&', '!', '?', '<', '>', '%', '|', '^', '~', '+', '*', '/', '-'] -""" - /* Watch out for the tricky case of (*). Cannot parse this as "(*" - and ")", but since I know that this is really ( * ), replace it - with "*" and return that. */ -"("{W}*"*"{W}*")" { return '*'; } - -"]" { BEGIN(0); return yytext[0]; } -[}{;:\[\],()#=.@&!?<>%|^~+*/-] { return yytext[0]; } - -\" { BEGIN(CSTRING); } -\\\\ { yymore(); /* Catch \\, which is a \ escaping itself */ } -\\\" { yymore(); /* Catch \", which is an escaped quote */ } -\n { BEGIN(0); - yylval.text = strdupnew(yytext); - VLerror(yylloc, "Missing close quote of string."); - yylloc.first_line += 1; - return STRING; } -\" { BEGIN(0); - yylval.text = strdupnew(yytext); - yylval.text[strlen(yytext)-1] = 0; - return STRING; } -. { yymore(); } - - /* The UDP Table is a unique lexical environment. These are most - tokens that we can expect in a table. */ -\(\?0\) { return '_'; } -\(\?1\) { return '+'; } -\(\?[xX]\) { return '%'; } -\(\?\?\) { return '*'; } -\(01\) { return 'r'; } -\(0[xX]\) { return 'Q'; } -\(b[xX]\) { return 'q'; } -\(b0\) { return 'f'; /* b0 is 10|00, but only 10 is meaningful */} -\(b1\) { return 'r'; /* b1 is 11|01, but only 01 is meaningful */} -\(0\?\) { return 'P'; } -\(10\) { return 'f'; } -\(1[xX]\) { return 'M'; } -\(1\?\) { return 'N'; } -\([xX]0\) { return 'F'; } -\([xX]1\) { return 'R'; } -\([xX]\?\) { return 'B'; } -[bB] { return 'b'; } -[lL] { return 'l'; /* IVL extension */ } -[hH] { return 'h'; /* IVL extension */ } -[fF] { return 'f'; } -[rR] { return 'r'; } -[xX] { return 'x'; } -[nN] { return 'n'; } -[pP] { return 'p'; } -[01\?\*\-:;] { return yytext[0]; } - -"01" { return K_edge_descriptor; } -"0x" { return K_edge_descriptor; } -"0z" { return K_edge_descriptor; } -"10" { return K_edge_descriptor; } -"1x" { return K_edge_descriptor; } -"1z" { return K_edge_descriptor; } -"x0" { return K_edge_descriptor; } -"x1" { return K_edge_descriptor; } -"z0" { return K_edge_descriptor; } -"z1" { return K_edge_descriptor; } -""" - """ def t_module_end(t): r'endmodule' @@ -579,10 +469,10 @@ t_module_ignore = ' \t' """ def t_LITERAL(t): - r'[a-zA-Z_][a-zA-Z0-9$_]*' + r'[a-zA-Z_$][a-zA-Z0-9$_]*' word = t.value - print ("literal", word) keyword = lexor_keyword_code.get(t.value, 'IDENTIFIER') + if(lex_debug): print ("literal", word,keyword) #if keyword in ['K_module', 'K_macromodule']: # t.lexer.modulestart = t.lexpos+len(t.value) # t.lexer.begin('module') @@ -593,150 +483,6 @@ def t_LITERAL(t): t.type = keyword return t -""" - switch (rc) { - case IDENTIFIER: - yylval.text = strdupnew(yytext); - if (strncmp(yylval.text,"PATHPULSE$", 10) == 0) - rc = PATHPULSE_IDENTIFIER; - break; - - case K_edge: - BEGIN(EDGES); - break; - - case K_primitive: - in_UDP = true; - break; - - case K_endprimitive: - in_UDP = false; - break; - - case K_table: - BEGIN(UDPTABLE); - break; - - default: - yylval.text = 0; - break; - } - - /* Special case: If this is part of a scoped name, then check - the package for identifier details. For example, if the - source file is foo::bar, the parse.y will note the - PACKAGE_IDENTIFIER and "::" token and mark the - "in_package_scope" variable. Then this lexor will see the - identifier here and interpret it in the package scope. */ - if (in_package_scope) { - if (rc == IDENTIFIER) { - if (data_type_t*type = pform_test_type_identifier(in_package_scope, yylval.text)) { - yylval.type_identifier.text = yylval.text; - yylval.type_identifier.type = type; - rc = TYPE_IDENTIFIER; - } - } - in_package_scope = 0; - return rc; - } - - /* If this identifier names a discipline, then return this as - a DISCIPLINE_IDENTIFIER and return the discipline as the - value instead. */ - if (rc == IDENTIFIER && gn_verilog_ams_flag) { - perm_string tmp = lex_strings.make(yylval.text); - map::iterator cur = disciplines.find(tmp); - if (cur != disciplines.end()) { - delete[]yylval.text; - yylval.discipline = (*cur).second; - rc = DISCIPLINE_IDENTIFIER; - } - } - - /* If this identifier names a previously declared package, then - return this as a PACKAGE_IDENTIFIER instead. */ - if (rc == IDENTIFIER && gn_system_verilog()) { - if (PPackage*pkg = pform_test_package_identifier(yylval.text)) { - delete[]yylval.text; - yylval.package = pkg; - rc = PACKAGE_IDENTIFIER; - } - } - - /* If this identifier names a previously declared type, then - return this as a TYPE_IDENTIFIER instead. */ - if (rc == IDENTIFIER && gn_system_verilog()) { - if (data_type_t*type = pform_test_type_identifier(yylval.text)) { - yylval.type_identifier.text = yylval.text; - yylval.type_identifier.type = type; - rc = TYPE_IDENTIFIER; - } - } - - return rc; - } -""" - -""" -\\[^ \t\b\f\r\n]+ { - yylval.text = strdupnew(yytext+1); - if (gn_system_verilog()) { - if (PPackage*pkg = pform_test_package_identifier(yylval.text)) { - delete[]yylval.text; - yylval.package = pkg; - return PACKAGE_IDENTIFIER; - } - } - if (gn_system_verilog()) { - if (data_type_t*type = pform_test_type_identifier(yylval.text)) { - yylval.type_identifier.text = yylval.text; - yylval.type_identifier.type = type; - return TYPE_IDENTIFIER; - } - } - return IDENTIFIER; - } - -\$([a-zA-Z0-9$_]+) { - /* The 1364-1995 timing checks. */ - if (strcmp(yytext,"$hold") == 0) - return K_Shold; - if (strcmp(yytext,"$nochange") == 0) - return K_Snochange; - if (strcmp(yytext,"$period") == 0) - return K_Speriod; - if (strcmp(yytext,"$recovery") == 0) - return K_Srecovery; - if (strcmp(yytext,"$setup") == 0) - return K_Ssetup; - if (strcmp(yytext,"$setuphold") == 0) - return K_Ssetuphold; - if (strcmp(yytext,"$skew") == 0) - return K_Sskew; - if (strcmp(yytext,"$width") == 0) - return K_Swidth; - /* The new 1364-2001 timing checks. */ - if (strcmp(yytext,"$fullskew") == 0) - return K_Sfullskew; - if (strcmp(yytext,"$recrem") == 0) - return K_Srecrem; - if (strcmp(yytext,"$removal") == 0) - return K_Sremoval; - if (strcmp(yytext,"$timeskew") == 0) - return K_Stimeskew; - - if (strcmp(yytext,"$attribute") == 0) - return KK_attribute; - - if (gn_system_verilog() && strcmp(yytext,"$unit") == 0) { - yylval.package = pform_units.back(); - return PACKAGE_IDENTIFIER; - } - - yylval.text = strdupnew(yytext); - return SYSTEM_IDENTIFIER; } -""" - def t_dec_number(t): r'\'[sS]?[dD][ \t]*[0-9][0-9_]*' t.type = 'BASED_NUMBER' @@ -778,11 +524,6 @@ def t_unbased_make_unsized_binary(t): mode, where there are no decimal numbers. Reject the match if we are in the UDPTABLE state. */ """ -""" - if (YY_START==UDPTABLE) { - REJECT; - } else { -""" def t_make_unsized_dec(t): r'[0-9][0-9_]*' t.type = 'DEC_NUMBER' @@ -814,1168 +555,6 @@ def t_timescale_end(t): print ("match", code) return t -""" -.* { process_timescale(yytext); } -\n { - if (in_module) { - cerr << yylloc.text << ":" << yylloc.first_line << ": error: " - "`timescale directive can not be inside a module " - "definition." << endl; - error_count += 1; - } - yylloc.first_line += 1; - BEGIN(0); } -""" - -""" - - /* This rule handles scaled time values for SystemVerilog. */ -[0-9][0-9_]*(\.[0-9][0-9_]*)?{TU}?s { - if (gn_system_verilog()) { - yylval.text = strdupnew(yytext); - return TIME_LITERAL; - } else REJECT; } - - /* These rules handle the scaled real literals from Verilog-AMS. The - value is a number with a single letter scale factor. If - verilog-ams is not enabled, then reject this rule. If it is - enabled, then collect the scale and use it to scale the value. */ -[0-9][0-9_]*\.[0-9][0-9_]*/{S} { - if (!gn_verilog_ams_flag) REJECT; - BEGIN(REAL_SCALE); - yymore(); } - -[0-9][0-9_]*/{S} { - if (!gn_verilog_ams_flag) REJECT; - BEGIN(REAL_SCALE); - yymore(); } - -{S} { - size_t token_len = strlen(yytext); - char*tmp = new char[token_len + 5]; - int scale = 0; - strcpy(tmp, yytext); - switch (tmp[token_len-1]) { - case 'a': scale = -18; break; /* atto- */ - case 'f': scale = -15; break; /* femto- */ - case 'p': scale = -12; break; /* pico- */ - case 'n': scale = -9; break; /* nano- */ - case 'u': scale = -6; break; /* micro- */ - case 'm': scale = -3; break; /* milli- */ - case 'k': scale = 3; break; /* kilo- */ - case 'K': scale = 3; break; /* kilo- */ - case 'M': scale = 6; break; /* mega- */ - case 'G': scale = 9; break; /* giga- */ - case 'T': scale = 12; break; /* tera- */ - default: assert(0); break; - } - snprintf(tmp+token_len-1, 5, "e%d", scale); - yylval.realtime = new verireal(tmp); - delete[]tmp; - - BEGIN(0); - return REALTIME; } - -[0-9][0-9_]*\.[0-9][0-9_]*([Ee][+-]?[0-9][0-9_]*)? { - yylval.realtime = new verireal(yytext); - return REALTIME; } - -[0-9][0-9_]*[Ee][+-]?[0-9][0-9_]* { - yylval.realtime = new verireal(yytext); - return REALTIME; } - - - /* Notice and handle the `celldefine and `endcelldefine directives. */ - -^{W}?`celldefine{W}? { in_celldefine = true; } -^{W}?`endcelldefine{W}? { in_celldefine = false; } - - /* Notice and handle the resetall directive. */ - -^{W}?`resetall{W}? { - if (in_module) { - cerr << yylloc.text << ":" << yylloc.first_line << ": error: " - "`resetall directive can not be inside a module " - "definition." << endl; - error_count += 1; - } else if (in_UDP) { - cerr << yylloc.text << ":" << yylloc.first_line << ": error: " - "`resetall directive can not be inside a UDP " - "definition." << endl; - error_count += 1; - } else { - reset_all(); - } } - - /* Notice and handle the `unconnected_drive directive. */ -^{W}?`unconnected_drive { BEGIN(PPUCDRIVE); } -.* { process_ucdrive(yytext); } -\n { - if (in_module) { - cerr << yylloc.text << ":" << yylloc.first_line << ": error: " - "`unconnected_drive directive can not be inside a " - "module definition." << endl; - error_count += 1; - } - yylloc.first_line += 1; - BEGIN(0); } - -^{W}?`nounconnected_drive{W}? { - if (in_module) { - cerr << yylloc.text << ":" << yylloc.first_line << ": error: " - "`nounconnected_drive directive can not be inside a " - "module definition." << endl; - error_count += 1; - } - uc_drive = UCD_NONE; } - - /* These are directives that I do not yet support. I think that IVL - should handle these, not an external preprocessor. */ - /* From 1364-2005 Chapter 19. */ -^{W}?`pragme{W}?.* { } - - /* From 1364-2005 Annex D. */ -^{W}?`default_decay_time{W}?.* { } -^{W}?`default_trireg_strength{W}?.* { } -^{W}?`delay_mode_distributed{W}?.* { } -^{W}?`delay_mode_path{W}?.* { } -^{W}?`delay_mode_unit{W}?.* { } -^{W}?`delay_mode_zero{W}?.* { } - - /* From other places. */ -^{W}?`disable_portfaults{W}?.* { } -^{W}?`enable_portfaults{W}?.* { } -`endprotect { } -^{W}?`nosuppress_faults{W}?.* { } -`protect { } -^{W}?`suppress_faults{W}?.* { } -^{W}?`uselib{W}?.* { } - -^{W}?`begin_keywords{W}? { BEGIN(PPBEGIN_KEYWORDS); } - -\"[a-zA-Z0-9 -\.]*\".* { - keyword_mask_stack.push_front(lexor_keyword_mask); - - char*word = yytext+1; - char*tail = strchr(word, '"'); - tail[0] = 0; - if (strcmp(word,"1364-1995") == 0) { - lexor_keyword_mask = GN_KEYWORDS_1364_1995; - } else if (strcmp(word,"1364-2001") == 0) { - lexor_keyword_mask = GN_KEYWORDS_1364_1995 - |GN_KEYWORDS_1364_2001 - |GN_KEYWORDS_1364_2001_CONFIG; - } else if (strcmp(word,"1364-2001-noconfig") == 0) { - lexor_keyword_mask = GN_KEYWORDS_1364_1995 - |GN_KEYWORDS_1364_2001; - } else if (strcmp(word,"1364-2005") == 0) { - lexor_keyword_mask = GN_KEYWORDS_1364_1995 - |GN_KEYWORDS_1364_2001 - |GN_KEYWORDS_1364_2001_CONFIG - |GN_KEYWORDS_1364_2005; - } else if (strcmp(word,"1800-2005") == 0) { - lexor_keyword_mask = GN_KEYWORDS_1364_1995 - |GN_KEYWORDS_1364_2001 - |GN_KEYWORDS_1364_2001_CONFIG - |GN_KEYWORDS_1364_2005 - |GN_KEYWORDS_1800_2005; - } else if (strcmp(word,"1800-2009") == 0) { - lexor_keyword_mask = GN_KEYWORDS_1364_1995 - |GN_KEYWORDS_1364_2001 - |GN_KEYWORDS_1364_2001_CONFIG - |GN_KEYWORDS_1364_2005 - |GN_KEYWORDS_1800_2005 - |GN_KEYWORDS_1800_2009; - } else if (strcmp(word,"1800-2012") == 0) { - lexor_keyword_mask = GN_KEYWORDS_1364_1995 - |GN_KEYWORDS_1364_2001 - |GN_KEYWORDS_1364_2001_CONFIG - |GN_KEYWORDS_1364_2005 - |GN_KEYWORDS_1800_2005 - |GN_KEYWORDS_1800_2009 - |GN_KEYWORDS_1800_2012; - } else if (strcmp(word,"VAMS-2.3") == 0) { - lexor_keyword_mask = GN_KEYWORDS_1364_1995 - |GN_KEYWORDS_1364_2001 - |GN_KEYWORDS_1364_2001_CONFIG - |GN_KEYWORDS_1364_2005 - |GN_KEYWORDS_VAMS_2_3; - } else { - fprintf(stderr, "%s:%d: Ignoring unknown keywords string: %s\n", - yylloc.text, yylloc.first_line, word); - } - BEGIN(0); - } - -.* { - fprintf(stderr, "%s:%d: Malformed keywords specification: %s\n", - yylloc.text, yylloc.first_line, yytext); - BEGIN(0); - } - -^{W}?`end_keywords{W}?.* { - if (!keyword_mask_stack.empty()) { - lexor_keyword_mask = keyword_mask_stack.front(); - keyword_mask_stack.pop_front(); - } else { - fprintf(stderr, "%s:%d: Mismatched end_keywords directive\n", - yylloc.text, yylloc.first_line); - } - } - - /* Notice and handle the default_nettype directive. The lexor - detects the default_nettype keyword, and the second part of the - rule collects the rest of the line and processes it. We only need - to look for the first work, and interpret it. */ - -`default_nettype{W}? { BEGIN(PPDEFAULT_NETTYPE); } -.* { - NetNet::Type net_type; - size_t wordlen = strcspn(yytext, " \t\f\r\n"); - yytext[wordlen] = 0; - /* Add support for other wire types and better error detection. */ - if (strcmp(yytext,"wire") == 0) { - net_type = NetNet::WIRE; - - } else if (strcmp(yytext,"tri") == 0) { - net_type = NetNet::TRI; - - } else if (strcmp(yytext,"tri0") == 0) { - net_type = NetNet::TRI0; - - } else if (strcmp(yytext,"tri1") == 0) { - net_type = NetNet::TRI1; - - } else if (strcmp(yytext,"wand") == 0) { - net_type = NetNet::WAND; - - } else if (strcmp(yytext,"triand") == 0) { - net_type = NetNet::TRIAND; - - } else if (strcmp(yytext,"wor") == 0) { - net_type = NetNet::WOR; - - } else if (strcmp(yytext,"trior") == 0) { - net_type = NetNet::TRIOR; - - } else if (strcmp(yytext,"none") == 0) { - net_type = NetNet::NONE; - - } else { - cerr << yylloc.text << ":" << yylloc.first_line - << ": error: Net type " << yytext - << " is not a valid (or supported)" - << " default net type." << endl; - net_type = NetNet::WIRE; - error_count += 1; - } - pform_set_default_nettype(net_type, yylloc.text, yylloc.first_line); - } -\n { - yylloc.first_line += 1; - BEGIN(0); } - - - /* These are directives that are not supported by me and should have - been handled by an external preprocessor such as ivlpp. */ - -^{W}?`define{W}?.* { - cerr << yylloc.text << ":" << yylloc.first_line << - ": warning: `define not supported. Use an external preprocessor." - << endl; - } - -^{W}?`else{W}?.* { - cerr << yylloc.text << ":" << yylloc.first_line << - ": warning: `else not supported. Use an external preprocessor." - << endl; - } - -^{W}?`elsif{W}?.* { - cerr << yylloc.text << ":" << yylloc.first_line << - ": warning: `elsif not supported. Use an external preprocessor." - << endl; - } - -^{W}?`endif{W}?.* { - cerr << yylloc.text << ":" << yylloc.first_line << - ": warning: `endif not supported. Use an external preprocessor." - << endl; - } - -^{W}?`ifdef{W}?.* { - cerr << yylloc.text << ":" << yylloc.first_line << - ": warning: `ifdef not supported. Use an external preprocessor." - << endl; - } - -^{W}?`ifndef{W}?.* { - cerr << yylloc.text << ":" << yylloc.first_line << - ": warning: `ifndef not supported. Use an external preprocessor." - << endl; - } - -^`include{W}?.* { - cerr << yylloc.text << ":" << yylloc.first_line << - ": warning: `include not supported. Use an external preprocessor." - << endl; - } - -^`undef{W}?.* { - cerr << yylloc.text << ":" << yylloc.first_line << - ": warning: `undef not supported. Use an external preprocessor." - << endl; - } - - -`{W} { cerr << yylloc.text << ":" << yylloc.first_line << ": error: " - << "Stray tic (`) here. Perhaps you put white space" << endl; - cerr << yylloc.text << ":" << yylloc.first_line << ": : " - << "between the tic and preprocessor directive?" - << endl; - error_count += 1; } - -. { return yytext[0]; } - - /* Final catchall. something got lost or mishandled. */ - /* XXX Should we tell the user something about the lexical state? */ - -<*>.|\n { cerr << yylloc.text << ":" << yylloc.first_line - << ": error: unmatched character ("; - if (isprint(yytext[0])) - cerr << yytext[0]; - else - cerr << "hex " << hex << ((unsigned char) yytext[0]); - - cerr << ")" << endl; - error_count += 1; } - -%% - -/* - * The UDP state table needs some slightly different treatment by the - * lexor. The level characters are normally accepted as other things, - * so the parser needs to switch my mode when it believes in needs to. - */ -void lex_end_table() -{ - BEGIN(INITIAL); -} - -static unsigned truncate_to_integer_width(verinum::V*bits, unsigned size) -{ - if (size <= integer_width) return size; - - verinum::V pad = bits[size-1]; - if (pad == verinum::V1) pad = verinum::V0; - - for (unsigned idx = integer_width; idx < size; idx += 1) { - if (bits[idx] != pad) { - yywarn(yylloc, "Unsized numeric constant truncated to integer width."); - break; - } - } - return integer_width; -} - -verinum*make_unsized_binary(const char*txt) -{ - bool sign_flag = false; - bool single_flag = false; - const char*ptr = txt; - assert(*ptr == '\''); - ptr += 1; - - if (tolower(*ptr) == 's') { - sign_flag = true; - ptr += 1; - } - - assert((tolower(*ptr) == 'b') || gn_system_verilog()); - if (tolower(*ptr) == 'b') { - ptr += 1; - } else { - assert(sign_flag == false); - single_flag = true; - } - - while (*ptr && ((*ptr == ' ') || (*ptr == '\t'))) - ptr += 1; - - unsigned size = 0; - for (const char*idx = ptr ; *idx ; idx += 1) - if (*idx != '_') size += 1; - - if (size == 0) { - VLerror(yylloc, "Numeric literal has no digits in it."); - verinum*out = new verinum(); - out->has_sign(sign_flag); - out->is_single(single_flag); - return out; - } - - if ((based_size > 0) && (size > based_size)) yywarn(yylloc, - "extra digits given for sized binary constant."); - - verinum::V*bits = new verinum::V[size]; - - unsigned idx = size; - while (*ptr) { - switch (ptr[0]) { - case '0': - bits[--idx] = verinum::V0; - break; - case '1': - bits[--idx] = verinum::V1; - break; - case 'z': case 'Z': case '?': - bits[--idx] = verinum::Vz; - break; - case 'x': case 'X': - bits[--idx] = verinum::Vx; - break; - case '_': - break; - default: - fprintf(stderr, "%c\n", ptr[0]); - assert(0); - } - ptr += 1; - } - - if (gn_strict_expr_width_flag && (based_size == 0)) - size = truncate_to_integer_width(bits, size); - - verinum*out = new verinum(bits, size, false); - out->has_sign(sign_flag); - out->is_single(single_flag); - delete[]bits; - return out; -} - - -verinum*make_unsized_octal(const char*txt) -{ - bool sign_flag = false; - const char*ptr = txt; - assert(*ptr == '\''); - ptr += 1; - - if (tolower(*ptr) == 's') { - sign_flag = true; - ptr += 1; - } - - assert(tolower(*ptr) == 'o'); - ptr += 1; - - while (*ptr && ((*ptr == ' ') || (*ptr == '\t'))) - ptr += 1; - - unsigned size = 0; - for (const char*idx = ptr ; *idx ; idx += 1) - if (*idx != '_') size += 3; - - if (based_size > 0) { - int rem = based_size % 3; - if (rem != 0) based_size += 3 - rem; - if (size > based_size) yywarn(yylloc, - "extra digits given for sized octal constant."); - } - - verinum::V*bits = new verinum::V[size]; - - unsigned idx = size; - while (*ptr) { - unsigned val; - switch (ptr[0]) { - case '0': case '1': case '2': case '3': - case '4': case '5': case '6': case '7': - val = *ptr - '0'; - bits[--idx] = (val&4) ? verinum::V1 : verinum::V0; - bits[--idx] = (val&2) ? verinum::V1 : verinum::V0; - bits[--idx] = (val&1) ? verinum::V1 : verinum::V0; - break; - case 'x': case 'X': - bits[--idx] = verinum::Vx; - bits[--idx] = verinum::Vx; - bits[--idx] = verinum::Vx; - break; - case 'z': case 'Z': case '?': - bits[--idx] = verinum::Vz; - bits[--idx] = verinum::Vz; - bits[--idx] = verinum::Vz; - break; - case '_': - break; - default: - assert(0); - } - ptr += 1; - } - - if (gn_strict_expr_width_flag && (based_size == 0)) - size = truncate_to_integer_width(bits, size); - - verinum*out = new verinum(bits, size, false); - out->has_sign(sign_flag); - delete[]bits; - return out; -} - - -verinum*make_unsized_hex(const char*txt) -{ - bool sign_flag = false; - const char*ptr = txt; - assert(*ptr == '\''); - ptr += 1; - - if (tolower(*ptr) == 's') { - sign_flag = true; - ptr += 1; - } - assert(tolower(*ptr) == 'h'); - - ptr += 1; - while (*ptr && ((*ptr == ' ') || (*ptr == '\t'))) - ptr += 1; - - unsigned size = 0; - for (const char*idx = ptr ; *idx ; idx += 1) - if (*idx != '_') size += 4; - - if (based_size > 0) { - int rem = based_size % 4; - if (rem != 0) based_size += 4 - rem; - if (size > based_size) yywarn(yylloc, - "extra digits given for sized hex constant."); - } - - verinum::V*bits = new verinum::V[size]; - - unsigned idx = size; - while (*ptr) { - unsigned val; - switch (ptr[0]) { - case '0': case '1': case '2': case '3': case '4': - case '5': case '6': case '7': case '8': case '9': - val = *ptr - '0'; - bits[--idx] = (val&8) ? verinum::V1 : verinum::V0; - bits[--idx] = (val&4) ? verinum::V1 : verinum::V0; - bits[--idx] = (val&2) ? verinum::V1 : verinum::V0; - bits[--idx] = (val&1) ? verinum::V1 : verinum::V0; - break; - case 'a': case 'b': case 'c': case 'd': case 'e': case 'f': - case 'A': case 'B': case 'C': case 'D': case 'E': case 'F': - val = tolower(*ptr) - 'a' + 10; - bits[--idx] = (val&8) ? verinum::V1 : verinum::V0; - bits[--idx] = (val&4) ? verinum::V1 : verinum::V0; - bits[--idx] = (val&2) ? verinum::V1 : verinum::V0; - bits[--idx] = (val&1) ? verinum::V1 : verinum::V0; - break; - case 'x': case 'X': - bits[--idx] = verinum::Vx; - bits[--idx] = verinum::Vx; - bits[--idx] = verinum::Vx; - bits[--idx] = verinum::Vx; - break; - case 'z': case 'Z': case '?': - bits[--idx] = verinum::Vz; - bits[--idx] = verinum::Vz; - bits[--idx] = verinum::Vz; - bits[--idx] = verinum::Vz; - break; - case '_': - break; - default: - assert(0); - } - ptr += 1; - } - - if (gn_strict_expr_width_flag && (based_size == 0)) - size = truncate_to_integer_width(bits, size); - - verinum*out = new verinum(bits, size, false); - out->has_sign(sign_flag); - delete[]bits; - return out; -} - - -/* Divide the integer given by the string by 2. Return the remainder bit. */ -static int dec_buf_div2(char *buf) -{ - int partial; - int len = strlen(buf); - char *dst_ptr; - int pos; - - partial = 0; - pos = 0; - - /* dst_ptr overwrites buf, but all characters that are overwritten - were already used by the reader. */ - dst_ptr = buf; - - while(buf[pos] == '0') - ++pos; - - for(; pos= 2){ - *dst_ptr = partial/2 + '0'; - partial = partial & 1; - - ++dst_ptr; - } - else{ - *dst_ptr = '0'; - ++dst_ptr; - } - } - - // If result of division was zero string, it should remain that way. - // Don't eat the last zero... - if (dst_ptr == buf){ - *dst_ptr = '0'; - ++dst_ptr; - } - *dst_ptr = 0; - - return partial; -} - -/* Support a single x, z or ? as a decimal constant (from 1364-2005). */ -verinum* make_undef_highz_dec(const char* ptr) -{ - bool signed_flag = false; - - assert(*ptr == '\''); - /* The number may have decorations of the form 'sd, - possibly with space between the d and the . - Also, the 's' is optional, and marks the number as signed. */ - ptr += 1; - - if (tolower(*ptr) == 's') { - signed_flag = true; - ptr += 1; - } - - assert(tolower(*ptr) == 'd'); - ptr += 1; - - while (*ptr && ((*ptr == ' ') || (*ptr == '\t'))) - ptr += 1; - - /* Process the code. */ - verinum::V* bits = new verinum::V[1]; - switch (*ptr) { - case 'x': - case 'X': - bits[0] = verinum::Vx; - break; - case 'z': - case 'Z': - case '?': - bits[0] = verinum::Vz; - break; - default: - assert(0); - } - ptr += 1; - while (*ptr == '_') ptr += 1; - assert(*ptr == 0); - - verinum*out = new verinum(bits, 1, false); - out->has_sign(signed_flag); - delete[]bits; - return out; -} - -/* - * Making a decimal number is much easier than the other base numbers - * because there are no z or x values to worry about. It is much - * harder than other base numbers because the width needed in bits is - * hard to calculate. - */ - -verinum*make_unsized_dec(const char*ptr) -{ - char buf[4096]; - bool signed_flag = false; - unsigned idx; - - if (ptr[0] == '\'') { - /* The number has decorations of the form 'sd, - possibly with space between the d and the . - Also, the 's' is optional, and marks the number as - signed. */ - ptr += 1; - - if (tolower(*ptr) == 's') { - signed_flag = true; - ptr += 1; - } - - assert(tolower(*ptr) == 'd'); - ptr += 1; - - while (*ptr && ((*ptr == ' ') || (*ptr == '\t'))) - ptr += 1; - - } else { - /* ... or an undecorated decimal number is passed - it. These numbers are treated as signed decimal. */ - assert(isdigit(*ptr)); - signed_flag = true; - } - - - /* Copy the digits into a buffer that I can use to do in-place - decimal divides. */ - idx = 0; - while ((idx < sizeof buf) && (*ptr != 0)) { - if (*ptr == '_') { - ptr += 1; - continue; - } - - buf[idx++] = *ptr++; - } - - if (idx == sizeof buf) { - fprintf(stderr, "Ridiculously long" - " decimal constant will be truncated!\n"); - idx -= 1; - } - - buf[idx] = 0; - unsigned tmp_size = idx * 4 + 1; - verinum::V *bits = new verinum::V[tmp_size]; - - idx = 0; - while (idx < tmp_size) { - int rem = dec_buf_div2(buf); - bits[idx++] = (rem == 1) ? verinum::V1 : verinum::V0; - } - - assert(strcmp(buf, "0") == 0); - - /* Now calculate the minimum number of bits needed to - represent this unsigned number. */ - unsigned size = tmp_size; - while ((size > 1) && (bits[size-1] == verinum::V0)) - size -= 1; - - /* Now account for the signedness. Don't leave a 1 in the high - bit if this is a signed number. */ - if (signed_flag && (bits[size-1] == verinum::V1)) { - size += 1; - assert(size <= tmp_size); - } - - /* Since we never have the real number of bits that a decimal - number represents we do not check for extra bits. */ -// if (based_size > 0) { } - - if (gn_strict_expr_width_flag && (based_size == 0)) - size = truncate_to_integer_width(bits, size); - - verinum*res = new verinum(bits, size, false); - res->has_sign(signed_flag); - - delete[]bits; - return res; -} - -/* - * Convert the string to a time unit or precision. - * Returns true on failure. - */ -static bool get_timescale_const(const char *&cp, int &res, bool is_unit) -{ - /* Check for the 1 digit. */ - if (*cp != '1') { - if (is_unit) { - VLerror(yylloc, "Invalid `timescale unit constant " - "(1st digit)"); - } else { - VLerror(yylloc, "Invalid `timescale precision constant " - "(1st digit)"); - } - return true; - } - cp += 1; - - /* Check the number of zeros after the 1. */ - res = strspn(cp, "0"); - if (res > 2) { - if (is_unit) { - VLerror(yylloc, "Invalid `timescale unit constant " - "(number of zeros)"); - } else { - VLerror(yylloc, "Invalid `timescale precision constant " - "(number of zeros)"); - } - return true; - } - cp += res; - - /* Skip any space between the digits and the scaling string. */ - cp += strspn(cp, " \t"); - - /* Now process the scaling string. */ - if (strncmp("s", cp, 1) == 0) { - res -= 0; - cp += 1; - return false; - - } else if (strncmp("ms", cp, 2) == 0) { - res -= 3; - cp += 2; - return false; - - } else if (strncmp("us", cp, 2) == 0) { - res -= 6; - cp += 2; - return false; - - } else if (strncmp("ns", cp, 2) == 0) { - res -= 9; - cp += 2; - return false; - - } else if (strncmp("ps", cp, 2) == 0) { - res -= 12; - cp += 2; - return false; - - } else if (strncmp("fs", cp, 2) == 0) { - res -= 15; - cp += 2; - return false; - - } - - if (is_unit) { - VLerror(yylloc, "Invalid `timescale unit scale"); - } else { - VLerror(yylloc, "Invalid `timescale precision scale"); - } - return true; -} - - -/* - * process either a pull0 or a pull1. - */ -static void process_ucdrive(const char*txt) -{ - UCDriveType ucd = UCD_NONE; - const char*cp = txt + strspn(txt, " \t"); - - /* Skip the space after the `unconnected_drive directive. */ - if (cp == txt) { - VLerror(yylloc, "Space required after `unconnected_drive " - "directive."); - return; - } - - /* Check for the pull keyword. */ - if (strncmp("pull", cp, 4) != 0) { - VLerror(yylloc, "pull required for `unconnected_drive " - "directive."); - return; - } - cp += 4; - if (*cp == '0') ucd = UCD_PULL0; - else if (*cp == '1') ucd = UCD_PULL1; - else { - cerr << yylloc.text << ":" << yylloc.first_line << ": error: " - "`unconnected_drive does not support 'pull" << *cp - << "'." << endl; - error_count += 1; - return; - } - cp += 1; - - /* Verify that only space and/or a single line comment is left. */ - cp += strspn(cp, " \t"); - if (strncmp(cp, "//", 2) != 0 && - (size_t)(cp-yytext) != strlen(yytext)) { - VLerror(yylloc, "Invalid `unconnected_drive directive (extra " - "garbage after precision)."); - return; - } - - uc_drive = ucd; -} - -/* - * The timescale parameter has the form: - * " xs / xs" - */ -static void process_timescale(const char*txt) -{ - const char*cp = txt + strspn(txt, " \t"); - - /* Skip the space after the `timescale directive. */ - if (cp == txt) { - VLerror(yylloc, "Space required after `timescale directive."); - return; - } - - int unit = 0; - int prec = 0; - - /* Get the time units. */ - if (get_timescale_const(cp, unit, true)) return; - - /* Skip any space after the time units, the '/' and any - * space after the '/'. */ - cp += strspn(cp, " \t"); - if (*cp != '/') { - VLerror(yylloc, "`timescale separator '/' appears to be missing."); - return; - } - cp += 1; - cp += strspn(cp, " \t"); - - /* Get the time precision. */ - if (get_timescale_const(cp, prec, false)) return; - - /* Verify that only space and/or a single line comment is left. */ - cp += strspn(cp, " \t"); - if (strncmp(cp, "//", 2) != 0 && - (size_t)(cp-yytext) != strlen(yytext)) { - VLerror(yylloc, "Invalid `timescale directive (extra garbage " - "after precision)."); - return; - } - - /* The time unit must be greater than or equal to the precision. */ - if (unit < prec) { - VLerror(yylloc, "error: `timescale unit must not be less than " - "the precision."); - return; - } - - pform_set_timescale(unit, prec, yylloc.text, yylloc.first_line); -} - -int yywrap() -{ - return 1; -} - -/* - * The line directive matches lines of the form #line "foo" N and - * calls this function. Here I parse out the file name and line - * number, and change the yylloc to suite. - */ -static void line_directive() -{ - char *cpr; - /* Skip any leading space. */ - char *cp = strchr(yytext, '#'); - /* Skip the #line directive. */ - assert(strncmp(cp, "#line", 5) == 0); - cp += 5; - /* Skip the space after the #line directive. */ - cp += strspn(cp, " \t"); - - /* Find the starting " and skip it. */ - char*fn_start = strchr(cp, '"'); - if (cp != fn_start) { - VLerror(yylloc, "Invalid #line directive (file name start)."); - return; - } - fn_start += 1; - - /* Find the last ". */ - char*fn_end = strrchr(fn_start, '"'); - if (!fn_end) { - VLerror(yylloc, "Invalid #line directive (file name end)."); - return; - } - - /* Copy the file name and assign it to yylloc. */ - char*buf = new char[fn_end-fn_start+1]; - strncpy(buf, fn_start, fn_end-fn_start); - buf[fn_end-fn_start] = 0; - - /* Skip the space after the file name. */ - cp = fn_end; - cp += 1; - cpr = cp; - cpr += strspn(cp, " \t"); - if (cp == cpr) { - VLerror(yylloc, "Invalid #line directive (missing space after " - "file name)."); - delete[] buf; - return; - } - cp = cpr; - - /* Get the line number and verify that it is correct. */ - unsigned long lineno = strtoul(cp, &cpr, 10); - if (cp == cpr) { - VLerror(yylloc, "Invalid line number for #line directive."); - delete[] buf; - return; - } - cp = cpr; - - /* Verify that only space is left. */ - cpr += strspn(cp, " \t"); - if ((size_t)(cpr-yytext) != strlen(yytext)) { - VLerror(yylloc, "Invalid #line directive (extra garbage after " - "line number)."); - delete[] buf; - return; - } - - /* Now we can assign the new values to yyloc. */ - yylloc.text = set_file_name(buf); - yylloc.first_line = lineno; -} - -/* - * The line directive matches lines of the form `line N "foo" M and - * calls this function. Here I parse out the file name and line - * number, and change the yylloc to suite. M is ignored. - */ -static void line_directive2() -{ - char *cpr; - /* Skip any leading space. */ - char *cp = strchr(yytext, '`'); - /* Skip the `line directive. */ - assert(strncmp(cp, "`line", 5) == 0); - cp += 5; - - /* strtoul skips leading space. */ - unsigned long lineno = strtoul(cp, &cpr, 10); - if (cp == cpr) { - VLerror(yylloc, "Invalid line number for `line directive."); - return; - } - lineno -= 1; - cp = cpr; - - /* Skip the space between the line number and the file name. */ - cpr += strspn(cp, " \t"); - if (cp == cpr) { - VLerror(yylloc, "Invalid `line directive (missing space after " - "line number)."); - return; - } - cp = cpr; - - /* Find the starting " and skip it. */ - char*fn_start = strchr(cp, '"'); - if (cp != fn_start) { - VLerror(yylloc, "Invalid `line directive (file name start)."); - return; - } - fn_start += 1; - - /* Find the last ". */ - char*fn_end = strrchr(fn_start, '"'); - if (!fn_end) { - VLerror(yylloc, "Invalid `line directive (file name end)."); - return; - } - - /* Skip the space after the file name. */ - cp = fn_end + 1; - cpr = cp; - cpr += strspn(cp, " \t"); - if (cp == cpr) { - VLerror(yylloc, "Invalid `line directive (missing space after " - "file name)."); - return; - } - cp = cpr; - - /* Check that the level is correct, we do not need the level. */ - if (strspn(cp, "012") != 1) { - VLerror(yylloc, "Invalid level for `line directive."); - return; - } - cp += 1; - - /* Verify that only space and/or a single line comment is left. */ - cp += strspn(cp, " \t"); - if (strncmp(cp, "//", 2) != 0 && - (size_t)(cp-yytext) != strlen(yytext)) { - VLerror(yylloc, "Invalid `line directive (extra garbage after " - "level)."); - return; - } - - /* Copy the file name and assign it and the line number to yylloc. */ - char*buf = new char[fn_end-fn_start+1]; - strncpy(buf, fn_start, fn_end-fn_start); - buf[fn_end-fn_start] = 0; - - yylloc.text = set_file_name(buf); - yylloc.first_line = lineno; -} - -/* - * Reset all compiler directives. This will be called when a `resetall - * directive is encountered or when a new compilation unit is started. - */ -static void reset_all() -{ - pform_set_default_nettype(NetNet::WIRE, yylloc.text, yylloc.first_line); - in_celldefine = false; - uc_drive = UCD_NONE; - pform_set_timescale(def_ts_units, def_ts_prec, 0, 0); -} - -extern FILE*vl_input; -void reset_lexor() -{ - yyrestart(vl_input); - yylloc.first_line = 1; - - /* Announce the first file name. */ - yylloc.text = set_file_name(strdupnew(vl_file.c_str())); - - if (separate_compilation) { - reset_all(); - if (!keyword_mask_stack.empty()) { - lexor_keyword_mask = keyword_mask_stack.back(); - keyword_mask_stack.clear(); - } - } -} - -/* - * Modern version of flex (>=2.5.9) can clean up the scanner data. - */ -void destroy_lexor() -{ -# ifdef FLEX_SCANNER -# if YY_FLEX_MAJOR_VERSION >= 2 && YY_FLEX_MINOR_VERSION >= 5 -# if YY_FLEX_MINOR_VERSION > 5 || defined(YY_FLEX_SUBMINOR_VERSION) && YY_FLEX_SUBMINOR_VERSION >= 9 - yylex_destroy(); -# endif -# endif -# endif -} -""" - def t_timescale_error(t): print("%d: Timescale error '%s'" % (t.lexer.lineno, t.value[0])) print(t.value)