%top{ // Include stdint.h at the start of the generated file. Typically // MSVC will include this header later, after the definitions of // the integral type macros. MSVC then complains that about the // redefinition of the types. Including stdint.h early avoids this. #include } %{ #include "pac_action.h" #include "pac_array.h" #include "pac_attr.h" #include "pac_case.h" #include "pac_common.h" #include "pac_conn.h" #include "pac_dataptr.h" #include "pac_dataunit.h" #include "pac_dbg.h" #include "pac_decl.h" #include "pac_exception.h" #include "pac_expr.h" #include "pac_flow.h" #include "pac_id.h" #include "pac_nullptr.h" #include "pac_number.h" #include "pac_output.h" #include "pac_param.h" #include "pac_parse.h" #include "pac_record.h" #include "pac_type.h" #include "pac_utils.h" #include #include #ifdef _MSC_VER #include #else #include #include #endif int line_number = 1; int begin_pac_primitive(int tok); int end_pac_primitive(); int string_token(int tok) { yylval.str = copy_string(yytext); return tok; } int char_token(int tok) { yylval.val = yytext[0]; return tok; } void include_file(const char* filename); std::string do_dirname(std::string_view s) { #ifdef _MSC_VER return std::filesystem::path(s).parent_path().string(); #else std::unique_ptr tmp{new char[s.size() + 1]}; strncpy(tmp.get(), s.data(), s.size()); tmp[s.size()] = '\0'; char* dn = dirname(tmp.get()); if ( ! dn ) return ""; std::string res{dn}; return res; #endif } %} /* EC -- embedded code state */ /* PP -- PAC primitive state */ /* INCL -- @include line */ %s EC INCL PP RE WS [ \t]+ ID [A-Za-z_][A-Za-z_0-9]* D [0-9]+ HEX [0-9a-fA-F]+ FILE [^ \t\n]+ ESCSEQ (\\([^\n]|[0-7]{3}|x[[:xdigit:]]{2})) %option nounput %% "%include" { BEGIN(INCL); } {WS} /* skip whitespace */ {FILE} { BEGIN(INITIAL); include_file(yytext); } "%extern{" { BEGIN(EC); return TOK_LPB_EXTERN; } "%header{" { BEGIN(EC); return TOK_LPB_HEADER; } "%code{" { BEGIN(EC); return TOK_LPB_CODE; } "%init{" { BEGIN(EC); return TOK_LPB_INIT; } "%cleanup{" { BEGIN(EC); return TOK_LPB_CLEANUP; } "%member{" { BEGIN(EC); return TOK_LPB_MEMBER; } "%eof{" { BEGIN(EC); return TOK_LPB_EOF; } "%{" { BEGIN(EC); return TOK_LPB; } "%}" { BEGIN(INITIAL); return TOK_RPB; } "${" return begin_pac_primitive(TOK_PAC_VAL); "$set{" return begin_pac_primitive(TOK_PAC_SET); "$type{" return begin_pac_primitive(TOK_PAC_TYPE); "$typeof{" return begin_pac_primitive(TOK_PAC_TYPEOF); "$const_def{" return begin_pac_primitive(TOK_PAC_CONST_DEF); "//".* return string_token(TOK_EMBEDDED_STRING); . return char_token(TOK_EMBEDDED_ATOM); \n { ++line_number; return char_token(TOK_EMBEDDED_ATOM); } "}" return end_pac_primitive(); \n ++line_number; #.* /* eat comments */ {WS} /* eat whitespace */ "RE/" { BEGIN(RE); return TOK_BEGIN_RE; } ([^/\\\n]|{ESCSEQ})+ return string_token(TOK_REGEX); "/" { BEGIN(INITIAL); return TOK_END_RE; } [\\\n] return yytext[0]; analyzer return TOK_ANALYZER; enum return TOK_ENUM; extern return TOK_EXTERN; flow return TOK_FLOW; function return TOK_FUNCTION; let return TOK_LET; refine return TOK_REFINE; type return TOK_TYPE; align return TOK_ALIGN; case return TOK_CASE; casefunc return TOK_CASEFUNC; casetype return TOK_CASETYPE; connection return TOK_CONNECTION; datagram { yylval.val = AnalyzerDataUnit::DATAGRAM; return TOK_DATAUNIT; } default return TOK_DEFAULT; downflow { yylval.val = AnalyzerFlow::DOWN; return TOK_FLOWDIR; } flowunit { yylval.val = AnalyzerDataUnit::FLOWUNIT; return TOK_DATAUNIT; } nullptr { yylval.nullp = new Nullptr(); return TOK_NULLPTR; } of return TOK_OF; offsetof return TOK_OFFSETOF; padding return TOK_PADDING; record return TOK_RECORD; sizeof return TOK_SIZEOF; to return TOK_TO; typeattr return TOK_TYPEATTR; upflow { yylval.val = AnalyzerFlow::UP; return TOK_FLOWDIR; } withcontext return TOK_WITHCONTEXT; withinput return TOK_WITHINPUT; &also return TOK_ATTR_ALSO; &byteorder return TOK_ATTR_BYTEORDER; &check { fprintf(stderr, "warning in %s:%d: &check is a deprecated no-op, use &enforce\n", input_filename.c_str(), line_number); return TOK_ATTR_CHECK; } &chunked return TOK_ATTR_CHUNKED; &enforce return TOK_ATTR_ENFORCE; &exportsourcedata return TOK_ATTR_EXPORTSOURCEDATA; &if return TOK_ATTR_IF; &length return TOK_ATTR_LENGTH; &let return TOK_ATTR_LET; &linebreaker return TOK_ATTR_LINEBREAKER; &oneline return TOK_ATTR_ONELINE; &refcount return TOK_ATTR_REFCOUNT; &requires return TOK_ATTR_REQUIRES; &restofdata return TOK_ATTR_RESTOFDATA; &restofflow return TOK_ATTR_RESTOFFLOW; &transient return TOK_ATTR_TRANSIENT; &until return TOK_ATTR_UNTIL; "0x"{HEX} { int n; sscanf(yytext + 2, "%x", &n); yylval.num = new Number(yytext, n); return TOK_NUMBER; } {D} { int n; sscanf(yytext, "%d", &n); yylval.num = new Number(yytext, n); return TOK_NUMBER; } {ID}(::{ID})* { yylval.id = new ID(yytext); return TOK_ID; } "$"{ID} { yylval.id = new ID(yytext); return TOK_ID; } \"([^\\\n\"]|{ESCSEQ})*\" return string_token(TOK_STRING); "==" return TOK_EQUAL; "!=" return TOK_NEQ; ">=" return TOK_GE; "<=" return TOK_LE; "<<" return TOK_LSHIFT; ">>" return TOK_RSHIFT; "&&" return TOK_AND; "||" return TOK_OR; "+=" return TOK_PLUSEQ; "->" return TOK_RIGHTARROW; [\.!%*/+\-&|\^,:;<=>?()\[\]{}~] return yytext[0]; %% void begin_RE() { BEGIN(RE); } void end_RE() { BEGIN(INITIAL); } // The DECL state is deprecated void begin_decl() { // BEGIN(DECL); } void end_decl() { // BEGIN(INITIAL); } int begin_pac_primitive(int tok) { BEGIN(PP); return tok; } int end_pac_primitive() { BEGIN(EC); return TOK_END_PAC; } constexpr int MAX_INCLUDE_DEPTH = 100; struct IncludeState { YY_BUFFER_STATE yystate; string input_filename; int line_number; }; IncludeState include_stack[MAX_INCLUDE_DEPTH]; int include_stack_ptr = 0; void switch_to_file(FILE* fp) { yy_switch_to_buffer(yy_create_buffer(fp, YY_BUF_SIZE)); } void switch_to_file(const char* filename) { if ( include_stack_ptr >= MAX_INCLUDE_DEPTH ) { fprintf(stderr, "Includes nested too deeply"); exit(1); } IncludeState state = {YY_CURRENT_BUFFER, input_filename, line_number}; include_stack[include_stack_ptr++] = state; FILE* fp = fopen(filename, "r"); if ( ! fp ) { fprintf(stderr, "%s:%d: error: cannot include file \"%s\"\n", input_filename.c_str(), line_number, filename); exit(1); } yyin = fp; input_filename = string(filename); line_number = 1; switch_to_file(yyin); if ( ! FLAGS_quiet ) fprintf(stderr, "switching to file %s\n", input_filename.c_str()); } void include_file(const char* filename) { ASSERT(filename); string full_filename; if ( filename[0] == '/' ) full_filename = filename; else if ( filename[0] == '.' ) { string dir = do_dirname(input_filename); if ( ! dir.empty() ) full_filename = dir + "/" + filename; else { fprintf(stderr, "%s:%d error: cannot include file \"%s\": %s\n", input_filename.c_str(), line_number, filename, strerror(errno)); exit(1); } } else { int i; for ( i = 0; i < (int)FLAGS_include_directories.size(); ++i ) { full_filename = FLAGS_include_directories[i] + filename; DEBUG_MSG("Try include file: \"%s\"\n", full_filename.c_str()); if ( access(full_filename.c_str(), R_OK) == 0 ) break; } if ( i >= (int)FLAGS_include_directories.size() ) full_filename = filename; } switch_to_file(full_filename.c_str()); } int yywrap() { yy_delete_buffer(YY_CURRENT_BUFFER); --include_stack_ptr; if ( include_stack_ptr < 0 ) return 1; IncludeState state = include_stack[include_stack_ptr]; yy_switch_to_buffer(state.yystate); input_filename = state.input_filename; line_number = state.line_number; return 0; }