""" %{ /* * Copyright (c) 1998-2017 Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form under the terms of the GNU * General Public License as published by the Free Software * Foundation; either version 2 of the License, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ """ 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. */ "//"{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. */ "/*" { comment_enter = YY_START; BEGIN(CCOMMENT); } . { ; } \n { yylloc.first_line += 1; } "*/" { BEGIN(comment_enter); } """ states = (#('module', 'exclusive'), ('timescale', 'exclusive'),) from parse_tokens import tokens tokens += ['timescale', 'LITERAL', 'IDENTIFIER', 'DEC_NUMBER', 'BASED_NUMBER', 'UNBASED_NUMBER'] def t_ccomment(t): r'/\*(.|\n)*?\*/' t.lexer.lineno += t.value.count('\n') t_ignore_cppcomment = r'//.*' t_ignore = ' \t\n' t_K_PSTAR = r"\(\*" t_K_STARP = r"\*\)" t_K_DOTSTAR = r"\.\*" t_K_LS = r"(<<|<<<)" t_K_RS = r">>" t_K_RSS = r">>>" t_K_POW = r"\*\*" t_K_LE = r"<=" t_K_GE = r">=" t_K_EG = r"=>" """ "+=>"|"-=>" { /* * Resolve the ambiguity between the += assignment * operator and +=> polarity edge path operator * * +=> should be treated as two separate tokens '+' and * '=>' (K_EG), therefore we only consume the first * character of the matched pattern i.e. either + or - * and push back the rest of the matches text (=>) in * the input stream. */ yyless(1); return yytext[0]; } """ t_K_SG = r"\*>" t_K_EQ = r"==" t_K_NE = r"!=" t_K_CEQ = r"===" t_K_CNE = r"!==" t_K_WEQ = r"==\?" t_K_WNE = r"!=\?" t_K_LOR = r"\|\|" t_K_LAND = r"\&\&" t_K_TAND = r"\&\&\&" t_K_NOR = r"\~\|" t_K_NXOR = r"(\~\^|\^\~)" t_K_NAND = r"\~\&" t_K_TRIGGER = r"\->" t_K_PO_POS = r"\+:" t_K_PO_NEG = r"\-:" t_K_CONTRIBUTE = r"<\+" t_K_PLUS_EQ = r"\+=" t_K_MINUS_EQ = r"\-=" t_K_MUL_EQ = r"\*=" t_K_DIV_EQ = r"\/=" t_K_MOD_EQ = r"\%=" t_K_AND_EQ = r"\&=" t_K_OR_EQ = r"\|=" t_K_XOR_EQ = r"\^=" t_K_LS_EQ = r"(<<=|<<<=)" t_K_RS_EQ = r">>=" t_K_RSS_EQ = r">>>=" t_K_INCR = r"\+\+" t_K_DECR = r"\\--" t_K_LP = r"\'\{" t_K_SCOPE_RES = r"::" tokens += [ 'K_PSTAR', 'K_STARP', 'K_DOTSTAR', 'K_LS', 'K_RS', 'K_RSS', 'K_POW', 'K_LE', 'K_GE', 'K_EG', 'K_SG', 'K_EQ', 'K_NE', 'K_CEQ', 'K_CNE', 'K_WEQ', 'K_WNE', 'K_LOR', 'K_LAND', 'K_TAND', 'K_NOR', 'K_NXOR', 'K_NAND', 'K_TRIGGER', 'K_PO_POS', 'K_PO_NEG', 'K_CONTRIBUTE', 'K_PLUS_EQ', 'K_MINUS_EQ', 'K_MUL_EQ', 'K_DIV_EQ', 'K_MOD_EQ', 'K_AND_EQ', 'K_OR_EQ', 'K_XOR_EQ', 'K_LS_EQ', 'K_RS_EQ', 'K_RSS_EQ', 'K_INCR', 'K_DECR', 'K_LP', 'K_SCOPE_RES' ] lexor_keyword_code = { "above" : 'K_above', "abs" : 'K_abs', "absdelay" : 'K_absdelay', "abstol" : 'K_abstol', "accept_on" : 'K_accept_on', "access" : 'K_access', "acos" : 'K_acos', "acosh" : 'K_acosh', "ac_stim" : 'K_ac_stim', "alias" : 'K_alias', "aliasparam" : 'K_aliasparam', "always" : 'K_always', "always_comb" : 'K_always_comb', "always_ff" : 'K_always_ff', "always_latch" : 'K_always_latch', "analog" : 'K_analog', "analysis" : 'K_analysis', "and" : 'K_and', "asin" : 'K_asin', "asinh" : 'K_asinh', "assert" : 'K_assert', "assign" : 'K_assign', "assume" : 'K_assume', "atan" : 'K_atan', "atan2" : 'K_atan2', "atanh" : 'K_atanh', "automatic" : 'K_automatic', "before" : 'K_before', "begin" : 'K_begin', "bind" : 'K_bind', "bins" : 'K_bins', "binsof" : 'K_binsof', "bit" : 'K_bit', "branch" : 'K_branch', "break" : 'K_break', "bool" : 'K_bool', "buf" : 'K_buf', "bufif0" : 'K_bufif0', "bufif1" : 'K_bufif1', "byte" : 'K_byte', "case" : 'K_case', "casex" : 'K_casex', "casez" : 'K_casez', "ceil" : 'K_ceil', "cell" : 'K_cell', "chandle" : 'K_chandle', "checker" : 'K_checker', "class" : 'K_class', "clocking" : 'K_clocking', "cmos" : 'K_cmos', "config" : 'K_config', "connect" : 'K_connect', "connectmodule" : 'K_connectmodule', "connectrules" : 'K_connectrules', "const" : 'K_const', "constraint" : 'K_constraint', "context" : 'K_context', "continue" : 'K_continue', "continuous" : 'K_continuous', "cos" : 'K_cos', "cosh" : 'K_cosh', "cover" : 'K_cover', "covergroup" : 'K_covergroup', "coverpoint" : 'K_coverpoint', "cross" : 'K_cross', "ddt" : 'K_ddt', "ddt_nature" : 'K_ddt_nature', "ddx" : 'K_ddx', "deassign" : 'K_deassign', "default" : 'K_default', "defparam" : 'K_defparam', "design" : 'K_design', "disable" : 'K_disable', "discipline" : 'K_discipline', "discrete" : 'K_discrete', "dist" : 'K_dist', "do" : 'K_do', "domain" : 'K_domain', "driver_update" : 'K_driver_update', "edge" : 'K_edge', "else" : 'K_else', "end" : 'K_end', "endcase" : 'K_endcase', "endchecker" : 'K_endchecker', "endconfig" : 'K_endconfig', "endclass" : 'K_endclass', "endclocking" : 'K_endclocking', "endconnectrules" : 'K_endconnectrules', "enddiscipline" : 'K_enddiscipline', "endfunction" : 'K_endfunction', "endgenerate" : 'K_endgenerate', "endgroup" : 'K_endgroup', "endinterface" : 'K_endinterface', "endmodule" : 'K_endmodule', "endnature" : 'K_endnature', "endpackage" : 'K_endpackage', "endparamset" : 'K_endparamset', "endprimitive" : 'K_endprimitive', "endprogram" : 'K_endprogram', "endproperty" : 'K_endproperty', "endspecify" : 'K_endspecify', "endsequence" : 'K_endsequence', "endtable" : 'K_endtable', "endtask" : 'K_endtask', "enum" : 'K_enum', "event" : 'K_event', "eventually" : 'K_eventually', "exclude" : 'K_exclude', "exp" : 'K_exp', "expect" : 'K_expect', "export" : 'K_export', "extends" : 'K_extends', "extern" : 'K_extern', "final" : 'K_final', "final_step" : 'K_final_step', "first_match" : 'K_first_match', "flicker_noise" : 'K_flicker_noise', "floor" : 'K_floor', "flow" : 'K_flow', "for" : 'K_for', "foreach" : 'K_foreach', "force" : 'K_force', "forever" : 'K_forever', "fork" : 'K_fork', "forkjoin" : 'K_forkjoin', "from" : 'K_from', "function" : 'K_function', "generate" : 'K_generate', "genvar" : 'K_genvar', "global" : 'K_global', "ground" : 'K_ground', "highz0" : 'K_highz0', "highz1" : 'K_highz1', "hypot" : 'K_hypot', "idt" : 'K_idt', "idtmod" : 'K_idtmod', "idt_nature" : 'K_idt_nature', "if" : 'K_if', "iff" : 'K_iff', "ifnone" : 'K_ifnone', "ignore_bins" : 'K_ignore_bins', "illegal_bins" : 'K_illegal_bins', "implies" : 'K_implies', "implements" : 'K_implements', "import" : 'K_import', "incdir" : 'K_incdir', "include" : 'K_include', "inf" : 'K_inf', "initial" : 'K_initial', "initial_step" : 'K_initial_step', "inout" : 'K_inout', "input" : 'K_input', "inside" : 'K_inside', "instance" : 'K_instance', "int" : 'K_int', "integer" : 'K_integer', "interconnect" : 'K_interconnect', "interface" : 'K_interface', "intersect" : 'K_intersect', "join" : 'K_join', "join_any" : 'K_join_any', "join_none" : 'K_join_none', "laplace_nd" : 'K_laplace_nd', "laplace_np" : 'K_laplace_np', "laplace_zd" : 'K_laplace_zd', "laplace_zp" : 'K_laplace_zp', "large" : 'K_large', "last_crossing" : 'K_last_crossing', "let" : 'K_let', "liblist" : 'K_liblist', "library" : 'K_library', "limexp" : 'K_limexp', "ln" : 'K_ln', "local" : 'K_local', "localparam" : 'K_localparam', "log" : 'K_log', # This is defined by SystemVerilog 1800-2005 and as an Icarus extension.' "logic" : 'K_logic', "longint" : 'K_longint', "macromodule" : 'K_macromodule', "matches" : 'K_matches', "max" : 'K_max', "medium" : 'K_medium', "merged" : 'K_merged', "min" : 'K_min', "modport" : 'K_modport', "module" : 'K_module', "nand" : 'K_nand', "nature" : 'K_nature', "negedge" : 'K_negedge', "net_resolution" : 'K_net_resolution', "nettype" : 'K_nettype', "new" : 'K_new', "nexttime" : 'K_nexttime', "nmos" : 'K_nmos', "noise_table" : 'K_noise_table', "nor" : 'K_nor', "noshowcancelled" : 'K_noshowcancelled', "not" : 'K_not', "notif0" : 'K_notif0', "notif1" : 'K_notif1', "null" : 'K_null', "or" : 'K_or', "output" : 'K_output', "package" : 'K_package', "packed" : 'K_packed', "parameter" : 'K_parameter', "paramset" : 'K_paramset', "pmos" : 'K_pmos', "posedge" : 'K_posedge', "potential" : 'K_potential', "pow" : 'K_pow', "primitive" : 'K_primitive', "priority" : 'K_priority', "program" : 'K_program', "property" : 'K_property', "protected" : 'K_protected', "pull0" : 'K_pull0', "pull1" : 'K_pull1', "pulldown" : 'K_pulldown', "pullup" : 'K_pullup', "pulsestyle_onevent" : 'K_pulsestyle_onevent', "pulsestyle_ondetect" : 'K_pulsestyle_ondetect', "pure" : 'K_pure', "rand" : 'K_rand', "randc" : 'K_randc', "randcase" : 'K_randcase', "randsequence" : 'K_randsequence', "rcmos" : 'K_rcmos', "real" : 'K_real', "realtime" : 'K_realtime', "ref" : 'K_ref', "reg" : 'K_reg', "reject_on" : 'K_reject_on', "release" : 'K_release', "repeat" : 'K_repeat', "resolveto" : 'K_resolveto', "restrict" : 'K_restrict', "return" : 'K_return', "rnmos" : 'K_rnmos', "rpmos" : 'K_rpmos', "rtran" : 'K_rtran', "rtranif0" : 'K_rtranif0', "rtranif1" : 'K_rtranif1', "s_always" : 'K_s_always', "s_eventually" : 'K_s_eventually', "s_nexttime" : 'K_s_nexttime', "s_until" : 'K_s_until', "s_until_with" : 'K_s_until_with', "scalared" : 'K_scalared', "sequence" : 'K_sequence', "shortint" : 'K_shortint', "shortreal" : 'K_shortreal', "showcancelled" : 'K_showcancelled', "signed" : 'K_signed', "sin" : 'K_sin', "sinh" : 'K_sinh', "slew" : 'K_slew', "small" : 'K_small', "soft" : 'K_soft', "solve" : 'K_solve', "specify" : 'K_specify', "specparam" : 'K_specparam', "split" : 'K_split', "sqrt" : 'K_sqrt', "static" : 'K_static', # This is defined by both SystemVerilog 1800-2005 and Verilog-AMS 2.3', "string" : 'K_string', "strong" : 'K_strong', "strong0" : 'K_strong0', "strong1" : 'K_strong1', "struct" : 'K_struct', "super" : 'K_super', "supply0" : 'K_supply0', "supply1" : 'K_supply1', "sync_accept_on" : 'K_sync_accept_on', "sync_reject_on" : 'K_sync_reject_on', "table" : 'K_table', "tagged" : 'K_tagged', "tan" : 'K_tan', "tanh" : 'K_tanh', "task" : 'K_task', "this" : 'K_this', "throughout" : 'K_throughout', "time" : 'K_time', "timeprecision" : 'K_timeprecision', "timer" : 'K_timer', "timeunit" : 'K_timeunit', "tran" : 'K_tran', "tranif0" : 'K_tranif0', "tranif1" : 'K_tranif1', "transition" : 'K_transition', "tri" : 'K_tri', "tri0" : 'K_tri0', "tri1" : 'K_tri1', "triand" : 'K_triand', "trior" : 'K_trior', "trireg" : 'K_trireg', "type" : 'K_type', "typedef" : 'K_typedef', "union" : 'K_union', "unique" : 'K_unique', "unique0" : 'K_unique', "units" : 'K_units', # Reserved for future use!', "unsigned" : 'K_unsigned', "until" : 'K_until', "until_with" : 'K_until_with', "untyped" : 'K_untyped', "use" : 'K_use', "uwire" : 'K_uwire', "var" : 'K_var', "vectored" : 'K_vectored', "virtual" : 'K_virtual', "void" : 'K_void', "wait" : 'K_wait', "wait_order" : 'K_wait_order', "wand" : 'K_wand', "weak" : 'K_weak', "weak0" : 'K_weak0', "weak1" : 'K_weak1', "while" : 'K_while', "white_noise" : 'K_white_noise', "wildcard" : 'K_wildcard', "wire" : 'K_wire', "with" : 'K_with', "within" : 'K_within', # This is the name originally proposed for uwire and is deprecated!', "wone" : 'K_wone', "wor" : 'K_wor', # This is defined by Verilog-AMS 2.3 and as an Icarus extension.', "wreal" : 'K_wreal', "xnor" : 'K_xnor', "xor" : 'K_xor', "zi_nd" : 'K_zi_nd', "zi_np" : 'K_zi_np', "zi_zd" : 'K_zi_zd', "zi_zp" : 'K_zi_zp', } 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' code = t.lexer.lexdata[t.modulestart:t.lexpos] t.type = 'INITIAL' t.value = code t.lexer.lineno += t.value.count('\n') return t t_module_ignore = ' \t' """ def t_LITERAL(t): r'[a-zA-Z_][a-zA-Z0-9$_]*' word = t.value print ("literal", word) keyword = lexor_keyword_code.get(t.value, 'IDENTIFIER') #if keyword in ['K_module', 'K_macromodule']: # t.lexer.modulestart = t.lexpos+len(t.value) # t.lexer.begin('module') if keyword == 'IDENTIFIER': t.type = 'IDENTIFIER' t.value = word return 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' #t.value = word # make_unsized_dec(yytext); return t def t_undef_highz_dec(t): r'\'[sS]?[dD][ \t]*[xzXZ?]_*' t.type = 'BASED_NUMBER' #t.value = word # make_undef_highz_dec(yytext); return t def t_based_make_unsized_binary(t): r'\'[sS]?[bB][ \t]*[0-1xzXZ?][0-1xzXZ?_]*' t.type = 'BASED_NUMBER' #t.value = word # make_unsized_binary(yytext); return t def t_make_unsized_octal(t): r'\'[sS]?[oO][ \t]*[0-7xzXZ?][0-7xzXZ?_]*' t.type = 'BASED_NUMBER' #t.value = word # make_unsized_octal(yytext); return t def t_make_unsized_hex(t): r'\'[sS]?[hH][ \t]*[0-9a-fA-FxzXZ?][0-9a-fA-FxzXZ?_]*' t.type = 'BASED_NUMBER' #t.value = word # make_unsized_hex(yytext); return t def t_unbased_make_unsized_binary(t): r'\'[01xzXZ]' t.type = 'UNBASED_NUMBER' #t.value = word # make_unsized_binary(yytext); return t """ /* Decimal numbers are the usual. But watch out for the UDPTABLE 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' #t.value = word # make_unsized_dec(yytext); #based_size = yylval.number->as_ulong(); return t """ /* Notice and handle the `timescale directive. */ """ def t_timescale(t): #r'^{W}?`timescale' r'`timescale' t.lexer.timestart = t.lexpos+len(t.value) t.lexer.push_state('timescale') #t_timescale_ignore_toeol = r'.+\n' t_timescale_ignore = ' \t' #t_timescale_ignore_whitespace = r'\s+' #t_code_ignore = "" def t_timescale_end(t): r'.+\n' code = t.lexer.lexdata[t.lexer.timestart:t.lexpos] t.type = 'timescale' t.value = code t.lexer.pop_state() 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) raise RuntimeError """ def t_module_error(t): print("%d: Module error '%s'" % (t.lexer.lineno, t.value[0])) print(t.value) raise RuntimeError """ def t_error(t): print("%d: Illegal character '%s'" % (t.lexer.lineno, t.value[0])) print(t.value) t.lexer.skip(1) tokens = list(set(tokens)) lex.lex() if __name__ == '__main__': lex.runmain()