Cloned SEACAS for EXODUS library with extra build files for internal package management.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1169 lines
33 KiB

2 years ago
/* -*- Mode: c++ -*- */
/*
* Copyright(C) 1999-2022 National Technology & Engineering Solutions
* of Sandia, LLC (NTESS). Under the terms of Contract DE-NA0003525 with
* NTESS, the U.S. Government retains certain rights in this software.
*
* See packages/seacas/LICENSE for details
*/
%{
#include <fstream>
#include <iostream>
#include <sstream>
#include <stack>
#include <stdio.h>
#include <string.h>
#include <fcntl.h>
#include <unistd.h>
#include "apr_scanner.h"
#include "aprepro.h"
#include "apr_util.h"
#include "apr_getline_int.h"
#define YY_NO_UNISTD_H
/* import the parser's token type into a local typedef */
typedef SEAMS::Parser::token token;
typedef SEAMS::Parser::token_type token_type;
/* By default yylex returns int, we use token_type. Unfortunately yyterminate
* by default returns 0, which is not of token_type. */
#define yyterminate() return token::END
#define show(x) *(aprepro->infoStream) << "<" << x << ">" << std::flush;
namespace SEAMS {
extern bool echo;
void yyerror(const char *s);
}
namespace {
bool string_is_ascii(const char *line, size_t len)
{
for (size_t i = 0; i < len; i++) {
if (!(std::isspace(line[i]) || std::isprint(line[i]))) {
return false;
}
}
return true;
}
} // namespace
int file_must_exist = 0; /* Global used by include/conditional include */
/* Global variables used by the looping mechanism */
SEAMS::file_rec *outer_file = nullptr;
int loop_lvl = 0;
std::fstream *tmp_file;
const char *temp_f;
#if defined __NVCC__
#pragma diag_suppress code_is_unreachable
#endif
#define MAX_IF_NESTING 1024
int if_state[MAX_IF_NESTING] = {0}; // INITIAL
int if_case_run[MAX_IF_NESTING] = {false}; /* Has any if or elseif condition executed */
int if_lvl = 0;
int if_skip_level = 0;
bool suppress_nl = false;
bool switch_active = false; // Are we in a switch
bool switch_case_run = false; // has there been a case which matched condition run?
bool switch_skip_to_endcase = false;
double switch_condition = 0.0; // Value specified in "switch(condition)"
// For substitution history
size_t curr_index = 0;
std::string history_string;
size_t hist_start = 0;
#define YY_USER_ACTION curr_index += yyleng;
%}
/*** Flex Declarations and Options ***/
/* enable c++ scanner class generation */
%option c++
/* change the name of the scanner class. results in "SEAMSFlexLexer" */
%option prefix="SEAMS"
/* enable scanner to generate debug output. disable this for release
* versions. */
%option debug
/* enables the use of start condition stacks */
%option stack
qstring \"[^\"\n]*[\"\n]
mlstring \'[^\']*[\']
D [0-9]
E [Ee][+-]?{D}+
L [A-Za-z_]
id {L}({L}|{D}|:)*
WS [ \t\f]*
NL "\n"
number {D}*\.({D}+)?({E})?
integer {D}+({E})?
%START PARSING GET_FILENAME IF_SKIP GET_VAR VERBATIM IF_WHILE_SKIP GET_LOOP_VAR LOOP LOOP_SKIP END_CASE_SKIP
%%
<VERBATIM>{
"{VERBATIM(OFF)}" { BEGIN(INITIAL); }
[A-Za-z0-9_ ]* |
. { if (echo) ECHO; }
"\n" { if (echo) ECHO; aprepro.ap_file_list.top().lineno++; }
}
<INITIAL>{
"{VERBATIM(ON)}" { BEGIN(VERBATIM); }
{WS}"{ECHO}" |
{WS}"{ECHO(ON)}" { echo = true; }
{WS}"{NOECHO}" |
{WS}"{ECHO(OFF)}" { echo = false; }
{WS}"{IMMUTABLE(ON)}" { aprepro.stateImmutable = true; }
{WS}"{IMMUTABLE(OFF)}" { aprepro.stateImmutable = aprepro.ap_options.immutable; }
{WS}"{"[Ll]"oop"{WS}"(" {
BEGIN(GET_LOOP_VAR);
if (aprepro.ap_options.debugging)
std::cerr << "DEBUG LOOP - Found loop begin test " << yytext << " in file "
<< aprepro.ap_file_list.top().name << " at line " << aprepro.ap_file_list.top().lineno << "\n";
}
}
<GET_LOOP_VAR>{
{number}")".*"\n" |
{integer}")}".*"\n" {
aprepro.ap_file_list.top().lineno++;
/* Loop control defined by integer */
char *pt = strchr(yytext, ')');
*pt = '\0';
sscanf (yytext, "%lf", &yylval->val);
if (yylval->val <= 0) {
BEGIN(LOOP_SKIP);
}
else {/* Value defined and != 0. */
temp_f = get_temp_filename();
SEAMS::file_rec new_file(temp_f, 0, true, (int)yylval->val);
if (aprepro.ap_options.debugging)
std::cerr << "DEBUG LOOP VAR = " << aprepro.ap_file_list.top().loop_count
<< " in file " << aprepro.ap_file_list.top().name
<< " at line " << aprepro.ap_file_list.top().lineno-1 << "\n";
outer_file = &aprepro.ap_file_list.top();
aprepro.ap_file_list.push(new_file);
tmp_file = new std::fstream(temp_f, std::ios::out);
loop_lvl++;
BEGIN(LOOP);
}
aprepro.isCollectingLoop = true;
}
.+")}".*"\n" {
aprepro.ap_file_list.top().lineno++;
/* Loop control defined by variable */
symrec *s;
char *pt = strchr(yytext, ')');
*pt = '\0';
if (!check_valid_var(yytext)) {
aprepro.warning("Invalid variable name syntax '" + std::string(yytext) + "'");
BEGIN(LOOP_SKIP);
} else {
s = aprepro.getsym(yytext);
if (s == nullptr || (s->type != token::SVAR && s->type != token::IMMSVAR && s->value.var == 0.)) {
BEGIN(LOOP_SKIP);
}
else { /* Value defined and != 0. */
if (aprepro.ap_options.debugging)
std::cerr << "DEBUG LOOP VAR = " << aprepro.ap_file_list.top().loop_count
<< " in file " << aprepro.ap_file_list.top().name
<< " at line " << aprepro.ap_file_list.top().lineno-1 << "\n";
temp_f = get_temp_filename();
SEAMS::file_rec new_file(temp_f, 0, true, (int)s->value.var);
outer_file = &aprepro.ap_file_list.top();
aprepro.ap_file_list.push(new_file);
tmp_file = new std::fstream(temp_f, std::ios::out);
loop_lvl++;
BEGIN(LOOP);
}
}
aprepro.isCollectingLoop = true;
}
}
<LOOP>{
{WS}"{"[Ee]"nd"[Ll]"oop".*"\n" {
outer_file->lineno++;
if(loop_lvl > 0)
--loop_lvl;
if (loop_lvl == 0) {
BEGIN(INITIAL);
tmp_file->close();
delete tmp_file;
if(!aprepro.doLoopSubstitution)
yy_push_state(VERBATIM);
aprepro.isCollectingLoop = false;
yyin = aprepro.open_file(aprepro.ap_file_list.top().name, "r");
yyFlexLexer::yypush_buffer_state (yyFlexLexer::yy_create_buffer( yyin, YY_BUF_SIZE));
curr_index = 0;
}
else {
(*tmp_file) << yytext;
}
}
{WS}"{"[Ll]"oop"{WS}"(".*"\n" {
loop_lvl++; /* Nested Loop */
(*tmp_file) << yytext;
outer_file->lineno++;
}
{WS}"{"[Aa]"bort"[Ll]"oop".*"\n" {
if(aprepro.ap_options.interactive ||
aprepro.string_interactive())
{
aprepro.warning("Aborting loop(s).", false);
// Leave the looping state and remove the loop file
BEGIN(INITIAL);
tmp_file->close();
delete tmp_file;
if(aprepro.ap_file_list.top().tmp_file) {
remove(aprepro.ap_file_list.top().name.c_str());
aprepro.ap_file_list.pop();
}
loop_lvl = 0;
aprepro.isCollectingLoop = false;
}
}
.*"\n" {
(*tmp_file) << yytext;
outer_file->lineno++;
}
}
<LOOP_SKIP>{
{WS}"{"[Ee]"nd"[Ll]"oop".*"\n" {
aprepro.ap_file_list.top().lineno++;
if(loop_lvl > 0)
--loop_lvl;
if (loop_lvl == 0) {
BEGIN(INITIAL);
aprepro.isCollectingLoop = false;
}
}
{WS}"{"[Ll]"oop"{WS}"(".*"\n" {
loop_lvl++; /* Nested Loop */
aprepro.ap_file_list.top().lineno++;
}
{WS}"{"[Aa]"bort"[Ll]"oop".*"\n" {
if(aprepro.ap_options.interactive ||
aprepro.string_interactive())
{
aprepro.warning("Aborting loops(s).", false);
// Leave the looping state
BEGIN(INITIAL);
loop_lvl = 0;
aprepro.isCollectingLoop = false;
}
}
.*"\n" { /* Do not increment line count */
;
}
}
<END_CASE_SKIP>{WS}"{"{WS}"case".*"\n" {
yyless(0);
curr_index = 0;
BEGIN(INITIAL);
switch_skip_to_endcase = false;
}
<INITIAL,END_CASE_SKIP>{WS}"{"{WS}"default"{WS}"}".*"\n" {
if (!switch_active) {
yyerror("default statement found outside switch statement.");
}
if (!switch_case_run) {
switch_case_run = true;
BEGIN(INITIAL);
switch_skip_to_endcase = false;
if (aprepro.ap_options.debugging)
fprintf (stderr, "DEBUG SWITCH: 'default' code executing at line %d\n",
aprepro.ap_file_list.top().lineno);
}
else {
if (aprepro.ap_options.debugging)
fprintf (stderr, "DEBUG SWITCH: 'default' not executing since a previous case already ran at line %d\n",
aprepro.ap_file_list.top().lineno);
/* Need to skip all code until end of case */
BEGIN(END_CASE_SKIP);
}
}
<END_CASE_SKIP>{WS}"{"{WS}"endswitch"{WS}"}".*"\n" {
aprepro.ap_file_list.top().lineno++;
BEGIN(INITIAL);
switch_active = false;
switch_skip_to_endcase = false;
suppress_nl = false;
if (aprepro.ap_options.debugging)
fprintf (stderr, "DEBUG SWITCH: 'endswitch' at line %d\n",
aprepro.ap_file_list.top().lineno);
}
<END_CASE_SKIP>.*"\n" { aprepro.ap_file_list.top().lineno++; }
<INITIAL>{WS}"{"{WS}"endswitch"{WS}"}".*"\n" {
aprepro.ap_file_list.top().lineno++;
if (!switch_active) {
yyerror("endswitch statement found without matching switch.");
}
switch_active = false;
switch_skip_to_endcase = false;
}
<INITIAL>{
/* This restores the old behavior of ifdef and ifndef
* where they would eat up any leading whitespace on
* a line.
*/
{WS}"{"[Ii]"fdef"{WS}"(" {
// Used to avoid undefined variable warnings in old ifdef/ifndef construct
aprepro.inIfdefGetvar = true;
unput('(');
unput('f');
unput('e');
unput('d');
unput('f');
unput('i');
unput('_');
unput('{');
}
{WS}"{"[Ii]"fndef"{WS}"(" {
// Used to avoid undefined variable warnings in old ifdef/ifndef construct
aprepro.inIfdefGetvar = true;
unput('(');
unput('f');
unput('e');
unput('d');
unput('n');
unput('f');
unput('i');
unput('_');
unput('{');
}
}
<IF_WHILE_SKIP>{
/* If an if was found while skipping, then eat
* that entire if block until endif
* found since there is no way that
* any of the code in that if block could be executed.
* Make sure to handle multiple levels of skipped ifs...
*
* NOTE: if_lvl was not incremented, so don't need to decrement when
* endif found.
*/
{WS}"{"[Ee]"nd"[Ii]"f}".*"\n" {
aprepro.ap_file_list.top().lineno++;
if (--if_skip_level == 0)
BEGIN(IF_SKIP);
}
{WS}"{"[Ii]"fdef"{WS}"(".*"\n" {
aprepro.ap_file_list.top().lineno++;
if_skip_level++;
}
{WS}"{"[Ii]"f"{WS}"(".*"\n" {
aprepro.ap_file_list.top().lineno++;
if_skip_level++;
}
{WS}"{"[Ii]"fndef"{WS}"(".*"\n" {
aprepro.ap_file_list.top().lineno++;
if_skip_level++;
}
.*"\n" {
aprepro.ap_file_list.top().lineno++;
}
}
<IF_SKIP>{
/* IF an if, ifdef, or ifndef found while skipping, then
* skip the entire block up and including the endif.
* The (IF_WHILE_SKIP) start condition handles this skipping.
*/
{WS}"{"[Ii]"fdef"{WS}"(" {
if (aprepro.ap_options.debugging)
fprintf (stderr, "DEBUG IF: 'ifdef' found while skipping at line %d\n",
aprepro.ap_file_list.top().lineno);
if_skip_level = 1;
BEGIN(IF_WHILE_SKIP);
}
{WS}"{"[Ii]"f"{WS}"(" {
if (aprepro.ap_options.debugging)
fprintf (stderr, "DEBUG IF: 'ifdef' found while skipping at line %d\n",
aprepro.ap_file_list.top().lineno);
if_skip_level = 1;
BEGIN(IF_WHILE_SKIP);
}
{WS}"{"[Ii]"fndef"{WS}"(" {
if (aprepro.ap_options.debugging)
fprintf (stderr, "DEBUG IF: 'ifndef' found while skipping at line %d\n",
aprepro.ap_file_list.top().lineno);
if_skip_level = 1;
BEGIN(IF_WHILE_SKIP);
}
}
{WS}"{"[Ee]"lse}".*"\n" {
aprepro.ap_file_list.top().lineno++;
if (aprepro.ap_options.debugging)
fprintf (stderr, "DEBUG IF: 'else' at level = %d at line %d\n",
if_lvl, aprepro.ap_file_list.top().lineno);
if(YY_START == VERBATIM) {
if(echo) ECHO;
}
else if (if_state[if_lvl] == IF_SKIP) {
if (!if_case_run[if_lvl]) {
BEGIN(INITIAL);
if_state[if_lvl] = INITIAL;
if_case_run[if_lvl] = true;
} else {
BEGIN(IF_SKIP);
if_state[if_lvl] = IF_SKIP;
}
}
else if (if_state[if_lvl] == INITIAL) {
BEGIN(IF_SKIP);
if_state[if_lvl] = IF_SKIP;
}
/* If neither is true, this is a nested
if that should be skipped */
}
<IF_SKIP>{
{WS}"{"{WS}[Ee]"lse"[Ii]"f".*"\n" {
/* If any previous 'block' of this if has executed, then
* just skip this block; otherwise see if condition is
* true and execute this block
*/
if (aprepro.ap_options.debugging)
fprintf (stderr, "DEBUG IF: 'elseif' at level = %d at line %d\n",
if_lvl, aprepro.ap_file_list.top().lineno);
if (if_case_run[if_lvl]) { /* A previous else/elseif has run */
aprepro.ap_file_list.top().lineno++;
/* Already in IF_SKIP, so don't need to change state */
} else {
/* Need to check the elseif condition; push back and parse */
yyless(0);
curr_index = 0;
BEGIN(INITIAL);
if_state[if_lvl] = INITIAL;
}
}
[A-Za-z0-9_ ]* |
\\\{ |
\\\} |
. { ; }
"\n" {
aprepro.ap_file_list.top().lineno++;
}
}
{WS}"{"[Ee]"nd"[Ii]"f}".*"\n" {
if(YY_START == VERBATIM) {
if(echo) ECHO;
}
else {
if (if_state[if_lvl] == IF_SKIP ||
if_state[if_lvl] == INITIAL) {
BEGIN(INITIAL);
suppress_nl = false;
}
/* If neither is true, this is a nested
if that should be skipped */
if (aprepro.ap_options.debugging)
printf ("DEBUG IF: 'endif' at level = %d at line %d\n",
if_lvl, aprepro.ap_file_list.top().lineno);
if (--if_lvl < 0) {
if_lvl = 0;
yyerror("Improperly Nested ifdef/ifndef statements");
}
/* Ignore endif if not skipping */
}
aprepro.ap_file_list.top().lineno++;
}
<INITIAL>{WS}"{"[Ii]"nclude"{WS}"(" { BEGIN(GET_FILENAME);
file_must_exist = true; }
<INITIAL>{WS}"{"[Cc]"include"{WS}"(" { BEGIN(GET_FILENAME);
file_must_exist = false; }
<GET_FILENAME>.+")"{WS}"}"{NL}* {
aprepro.ap_file_list.top().lineno++;
BEGIN(INITIAL);
{
symrec *s;
int quoted = 0;
char *pt = strchr(yytext, ')');
*pt = '\0';
/* Check to see if surrounded by double quote */
if ((pt = strchr(yytext, '"')) != nullptr) {
yytext++;
quoted = 1;
}
if ((pt = strrchr(yytext, '"')) != nullptr) {
*pt = '\0';
quoted = 1;
}
if (quoted == 0) {
/* See if this is an aprepro variable referring to a name */
s = aprepro.getsym(yytext);
if (s == nullptr || (s->type != token::SVAR && s->type != token::IMMSVAR)) {
pt = yytext;
} else {
pt = (char*)s->value.svar.c_str();
}
} else {
pt = yytext;
}
add_include_file(pt, file_must_exist);
if(!aprepro.doIncludeSubstitution)
yy_push_state(VERBATIM);
aprepro.ap_file_list.top().lineno++;
}
}
<PARSING>{integer} |
<PARSING>{number} { sscanf (yytext, "%lf", &yylval->val);
return(token::NUM); }
<PARSING>{WS} ; // Empty rule
<PARSING>{id} {
symrec *s;
s = aprepro.getsym(yytext);
if (s == nullptr)
s = aprepro.putsym (yytext, SEAMS::Aprepro::SYMBOL_TYPE::UNDEFINED_VARIABLE, false);
yylval->tptr = s;
return((token::yytokentype)s->type);
}
<PARSING>"=" return(token::EQUAL);
<PARSING>"+=" return(token::EQ_PLUS);
<PARSING>"-=" return(token::EQ_MINUS);
<PARSING>"*=" return(token::EQ_TIME);
<PARSING>"/=" return(token::EQ_DIV);
<PARSING>"^=" return(token::EQ_POW);
<PARSING>"**=" return(token::EQ_POW);
<PARSING>"++" return(token::INC);
<PARSING>"--" return(token::DEC);
<PARSING>"+" return(token::PLU);
<PARSING>"-" return(token::SUB);
<PARSING>"*" return(token::TIM);
<PARSING>"~" return(token::TIM); /* ~ is same as multiply */
<PARSING>"//" return(token::CONCAT); /* String concatenation */
<PARSING>"/" return(token::DIV);
<PARSING>"%" return(token::MOD);
<PARSING>"^" return(token::POW);
<PARSING>"**" return(token::POW);
<PARSING>"\n" aprepro.ap_file_list.top().lineno++;
<PARSING>"(" return(token::LPAR);
<PARSING>")" return(token::RPAR);
<PARSING>"," return(token::COMMA);
<PARSING>";" return(token::SEMI);
<PARSING>":" return(token::COLON);
<PARSING>"?" return(token::QUEST);
<PARSING>"<" return(token::LT);
<PARSING>">" return(token::GT);
<PARSING>"<=" return(token::LE);
<PARSING>">=" return(token::GE);
<PARSING>"==" return(token::EQ);
<PARSING>"!=" return(token::NE);
<PARSING>"&&" return(token::LAND);
<PARSING>"||" return(token::LOR);
<PARSING>"!" return(token::NOT);
<PARSING>"[" return(token::LBRACK);
<PARSING>"]" return(token::RBRACK);
<PARSING>{qstring} {
char *pt = strrchr(yytext, '"');
*pt = '\0';
new_string(yytext+1, &yylval->string);
return token::QSTRING; }
<PARSING>{mlstring} {
char *pt = strrchr(yytext, '\'');
*pt = '\0';
new_string(yytext+1, &yylval->string);
return token::QSTRING; }
<PARSING>"}" {
// Add to the history string
save_history_string();
if (switch_skip_to_endcase)
BEGIN(END_CASE_SKIP);
else
BEGIN(if_state[if_lvl]);
return(token::RBRACE);
}
\\\{ { if (echo) LexerOutput("{", 1); }
\\\} { if (echo) LexerOutput("}", 1); }
"{" {
// Check if we need to save the substitution history first.
if(aprepro.ap_options.keep_history &&
(aprepro.ap_file_list.top().name != "_string_"))
{
if (curr_index > (size_t)yyleng)
hist_start = curr_index - yyleng;
else
hist_start = 0;
}
BEGIN(PARSING);
return(token::LBRACE);
}
[Ee][Xx][Ii][Tt] |
[Qq][Uu][Ii][Tt] {
if (aprepro.ap_options.end_on_exit) {
if (echo) ECHO;
return((token::yytokentype)-1);
}
else
if (echo) ECHO;
}
\$ { if (echo) ECHO; }
{id} |
. { if (echo && if_state[if_lvl] != IF_SKIP) ECHO; }
"\n" { if (echo && !suppress_nl) ECHO; suppress_nl = false;
aprepro.ap_file_list.top().lineno++;}
%%
/* When the scanner receives an end-of-file indication from YY_INPUT, it then
* checks the yywrap() function. If yywrap() returns false (zero), then it is
* assumed that the function has gone ahead and set up `yyin' to point to
* another input file, and scanning continues. If it returns true (non-zero),
* then the scanner terminates, returning 0 to its caller. */
namespace SEAMS
{
Scanner::Scanner(Aprepro & aprepro_yyarg, std::istream * in, std::ostream * out)
: SEAMSFlexLexer(in, out), aprepro(aprepro_yyarg)
{
aprepro.outputStream.push(out);
}
Scanner::~Scanner() {
while (aprepro.ap_file_list.size() > 1) {
auto kk = aprepro.ap_file_list.top();
if (kk.name != "STDIN") {
yyFlexLexer::yy_load_buffer_state();
delete yyin;
yyin = nullptr;
}
aprepro.ap_file_list.pop();
yyFlexLexer::yypop_buffer_state();
};
}
bool Scanner::add_include_file(const std::string &filename, bool must_exist)
{
std::fstream *yytmp = nullptr;
if (must_exist)
yytmp = aprepro.open_file(filename, "r");
else
yytmp = aprepro.check_open_file(filename, "r");
if (yytmp) {
if (yyin && !yy_init) {
yyFlexLexer::yypush_buffer_state(yyFlexLexer::yy_create_buffer(yyin, YY_BUF_SIZE));
}
yyin = yytmp;
aprepro.info("Included File: '" + filename + "'", true);
SEAMS::file_rec new_file(filename.c_str(), 0, false, 0);
aprepro.ap_file_list.push(new_file);
yyFlexLexer::yypush_buffer_state(yyFlexLexer::yy_create_buffer(yytmp, YY_BUF_SIZE));
curr_index = 0;
}
return yytmp != nullptr;
}
void Scanner::LexerOutput(const char *buf, int size)
{
// Do this before writing so that we have the correct index in the
// output stream.
if (aprepro.ap_options.keep_history) {
aprepro.add_history(history_string, buf);
history_string.clear();
hist_start = 0;
}
aprepro.outputStream.top()->write(buf, size);
if (aprepro.ap_options.interactive && aprepro.outputStream.size() == 1) {
// In interactive mode, output to stdout in addition to the
// output stream, unless user has redirected output...
std::cout << buf;
}
}
int Scanner::LexerInput(char *buf, int max_size)
{
if (yyin->eof() || yyin->fail()) {
return 0;
}
if (aprepro.ap_options.interactive && yyin == &std::cin && isatty(0) != 0 && isatty(1) != 0) {
char *line = ap_getline_int(nullptr);
if (strlen(line) == 0) {
return 0;
}
if (!string_is_ascii(line, strlen(line))) {
yyerror("input line contains non-ASCII (probably UTF-8) characters which will most likely "
"be parsed incorrectly.");
}
ap_gl_histadd(line);
if (strlen(line) > (size_t)max_size - 2) {
yyerror("input line is too long");
return 0;
}
strcpy(buf, line);
strcat(buf, "\n");
return strlen(buf);
}
else {
(void)yyin->read(buf, max_size);
if (yyin->bad()) {
return -1;
}
else {
if (!string_is_ascii(buf, yyin->gcount())) {
yyerror("input file contains non-ASCII (probably UTF-8) characters which will most likely "
"be parsed incorrectly.");
}
return yyin->gcount();
}
}
}
int Scanner::yywrap()
{
// Clear the history string.
history_string.clear();
hist_start = 0;
curr_index = 0;
// If we are using the string interactive method, we want to return to
// our original state if parsing was cutoff prematurely.
if (aprepro.string_interactive() && YY_START == PARSING) {
if (switch_skip_to_endcase) {
BEGIN(END_CASE_SKIP);
}
else {
BEGIN(if_state[if_lvl]);
}
}
if (aprepro.ap_file_list.size() <= 1) { /* End of main file, not in nested include */
return (1);
}
else if (aprepro.string_interactive() && loop_lvl) {
return (1);
}
else if (aprepro.isCollectingLoop) {
yyerror("End-of-file detected inside loop. Check loop syntax. {endloop} must be on line by "
"itself.");
return (1);
}
else {
/* We are in an included or looping file */
if (aprepro.ap_file_list.top().tmp_file) {
if (aprepro.ap_options.debugging) {
std::cerr << "DEBUG LOOP: Loop count = " << aprepro.ap_file_list.top().loop_count << "\n";
}
if (--aprepro.ap_file_list.top().loop_count <= 0) {
// On Windows, you can't remove the temp file until all the references to the
// file object have been released, so we will delete it here.
delete yyin;
yyin = nullptr;
if (aprepro.ap_file_list.top().name != "_string_") {
if (!aprepro.ap_options.debugging) {
remove(aprepro.ap_file_list.top().name.c_str()); /* Delete file if temporary */
}
if (!aprepro.doLoopSubstitution) {
yy_pop_state();
}
}
aprepro.ap_file_list.pop();
yyFlexLexer::yypop_buffer_state();
}
else {
// Do not pop ap_file_list; we are rereading that file...
delete yyin;
yyin = nullptr;
yyFlexLexer::yypop_buffer_state();
yyin = aprepro.open_file(aprepro.ap_file_list.top().name, "r");
yyFlexLexer::yypush_buffer_state(yyFlexLexer::yy_create_buffer(yyin, YY_BUF_SIZE));
aprepro.ap_file_list.top().lineno = 0;
}
}
else {
delete yyin;
yyin = nullptr;
aprepro.ap_file_list.pop();
yyFlexLexer::yypop_buffer_state();
if (aprepro.ap_file_list.top().name == "standard input") {
yyin = &std::cin;
}
/* Turn echoing back on at end of included files. */
echo = true;
// If we are not doing aprepro substitutions for the included file, but
// just collecting lines, pop the state from VERBATIM back to what it
// was previously.
if (!aprepro.doIncludeSubstitution) {
yy_pop_state();
}
/* Set immutable mode back to global immutable
* state at end of included file*/
aprepro.stateImmutable = aprepro.ap_options.immutable;
}
// Reset the current character index.
curr_index = 0;
if (yyin != nullptr) {
curr_index = yyin->tellg();
}
return (0);
}
}
/* Print error message to standard error and return. Note: internally
* 'lineno' starts at zero. To avoid confusion, we add 1 to value
* when it is output.
*/
void Scanner::yyerror(const char *s) { aprepro.error(s); }
char *Scanner::execute(char string[])
{
/* Push the contents of 'string' onto the stack to be reread.
* 'string' will be surrounded by {} so it must be a valid expression.
*/
/*
* NOTE: The closing } has not yet been scanned in the call to execute();
* therefore, we read it ourselves using input(), then we push:
* '}{' + our string + '}'
*/
int i;
while ((i = yyFlexLexer::yyinput()) != '}' && i != EOF)
curr_index++; /* eat up values */
// Increment curr_index to account for the '}' and save history
curr_index++;
save_history_string();
/* Allocate space for string + '}' + '{' + end_of_string */
std::string new_string;
new_string += "}{";
new_string += string;
new_string += "}";
aprepro.ap_file_list.push(SEAMS::file_rec("_string_", 0, true, -1));
auto ins = new std::istringstream(new_string); // Declare an input string stream.
yyFlexLexer::yypush_buffer_state(yyFlexLexer::yy_create_buffer(ins, new_string.size()));
return (nullptr);
}
/* Push the contents of 'string' onto the stack to be reread.
* 'string' will not be surrounded by {}.
*/
char *Scanner::rescan(char *string)
{
int i;
/*
* NOTE: The closing } has not yet been scanned in the call to rescan();
* therefore, we read it ourselves using input(), then we push our
* string and then put the closing } back on the stack last
* (to be read first),
*/
while ((i = yyFlexLexer::yyinput()) != '}' && i != EOF)
curr_index++; /* eat up values */
// Increment curr_index to account for the '}' and save history
curr_index++;
save_history_string();
{
aprepro.ap_file_list.push(SEAMS::file_rec("_string_", 0, true, -1));
std::string new_string("}");
new_string += string;
auto ins = new std::istringstream(new_string); // Declare an input string stream.
yyFlexLexer::yypush_buffer_state(yyFlexLexer::yy_create_buffer(ins, new_string.size()));
}
return (nullptr);
}
char *Scanner::import_handler(char *string)
{
/*
* NOTE: The closing } has not yet been scanned in the call to rescan();
* therefore, we read it ourselves using input().
*/
int i = 0;
while ((i = yyFlexLexer::yyinput()) != '}' && i != EOF)
curr_index++; /* eat up values */
add_include_file(string, true);
std::string info_string = std::string("Imported File: '") + string + "'";
aprepro.info(info_string, true);
if (!aprepro.doIncludeSubstitution) {
yy_push_state(VERBATIM);
}
/*
* Now we need to push back the closing } so it is the first thing read.
* We no longer have the initial file stream (is is pushed down on stack)
* so we need to add a new file stream consisting of just a single character.
* Wasteful, but best I can come up with at this time.
*/
aprepro.ap_file_list.push(SEAMS::file_rec("_string_", 0, true, -1));
std::string new_string("}");
auto ins = new std::istringstream(new_string); // Declare an input string stream.
yyFlexLexer::yypush_buffer_state(yyFlexLexer::yy_create_buffer(ins, new_string.size()));
if (aprepro.ap_options.debugging) {
std::cerr << "DEBUG IMPORT: " << string << "\n";
}
return (nullptr);
}
char *Scanner::if_handler(double x)
{
if_lvl++;
if (if_lvl >= MAX_IF_NESTING) {
yyerror("Too many nested if statements");
}
else {
if (x == 0) {
if_state[if_lvl] = IF_SKIP;
if_case_run[if_lvl] = false;
}
else {
suppress_nl = true;
if_state[if_lvl] = INITIAL;
if_case_run[if_lvl] = true;
}
if (aprepro.ap_options.debugging) {
std::cerr << "DEBUG IF: If level " << if_lvl << " " << if_state[if_lvl] << "\n";
}
}
return (nullptr);
}
char *Scanner::elseif_handler(double x)
{
if (x == 0 || if_case_run[if_lvl]) {
if_state[if_lvl] = IF_SKIP;
}
else {
suppress_nl = 1;
if_state[if_lvl] = INITIAL;
if_case_run[if_lvl] = true;
}
if (aprepro.ap_options.debugging) {
std::cerr << "DEBUG IF: elseif at level " << if_lvl << " " << if_state[if_lvl] << "\n";
}
return (nullptr);
}
char *Scanner::switch_handler(double x)
{
// save that we are in a switch statement
// save the value of 'x' for use in deciding which case to execute
if (switch_active) {
yyerror("switch statement found while switch already active. Nested switch not supported.");
}
switch_active = true;
switch_case_run = false;
switch_condition = x;
switch_skip_to_endcase = true; /* Skip everything until first case */
suppress_nl = true;
if (aprepro.ap_options.debugging) {
std::cerr << "DEBUG SWITCH: 'switch' with condition = " << switch_condition << " at line "
<< aprepro.ap_file_list.top().lineno << "\n";
}
return (nullptr);
}
char *Scanner::case_handler(double x)
{
// make sure we are in a switch statement
// if 'x' matches the value saved in the switch statement
// and no other case has been executed, then
// execute the code in the case and set a flag indicating
// the switch has run;
// if 'x' does not match the value saved, then skip to endcase
suppress_nl = true;
if (!switch_active) {
yyerror("case statement found outside switch statement.");
}
if (!switch_case_run && x == switch_condition) {
switch_case_run = true;
if (aprepro.ap_options.debugging) {
fprintf(stderr,
"DEBUG SWITCH: 'case' condition = %g matches switch condition = %g at line %d\n", x,
switch_condition, aprepro.ap_file_list.top().lineno);
}
}
else {
if (aprepro.ap_options.debugging) {
fprintf(stderr, "DEBUG SWITCH: 'case' condition = %g does not match switch condition = %g "
"(or case already matched) at line %d\n",
x, switch_condition, aprepro.ap_file_list.top().lineno);
}
// Need to skip all code until end of case
switch_skip_to_endcase = true;
}
return (nullptr);
}
void Scanner::save_history_string()
{
if (!aprepro.ap_options.keep_history) {
return;
}
// Don't do it if the file is the one used by execute and rescan.
if (aprepro.ap_file_list.top().name == "_string_" ||
aprepro.ap_file_list.top().name == "standard input") {
return;
}
size_t hist_end = curr_index;
size_t len = hist_end - hist_start;
if (len <= 0)
return;
// Go back in the stream to where we started keeping history.
yyin->seekg(hist_start);
if (!yyin->good()) {
yyerror("Stream state bad in `save_history_string` seekg");
return;
}
// Read everything up to this point again and save it.
auto tmp = new char[len + 1];
yyin->read(tmp, len);
if (!yyin->good()) {
yyerror("Stream state bad in `save_history_string` read");
return;
}
tmp[len] = '\0';
history_string = tmp;
delete[] tmp;
hist_start = 0;
}
}
/* This implementation of SEAMSFlexLexer::yylex() is required to fill the
* vtable of the class SEAMSFlexLexer. We define the scanner's main yylex
* function via YY_DECL to reside in the Scanner class instead. */
#ifdef yylex
#undef yylex
#endif
int SEAMSFlexLexer::yylex()
{
std::cerr << "in SEAMSFlexLexer::yylex() !" << '\n';
return 0;
}
/* When the scanner receives an end-of-file indication from YY_INPUT, it then
* checks the yywrap() function. If yywrap() returns false (zero), then it is
* assumed that the function has gone ahead and set up `yyin' to point to
* another input file, and scanning continues. If it returns true (non-zero),
* then the scanner terminates, returning 0 to its caller. */
int SEAMSFlexLexer::yywrap() { return 1; }