📄 from_l2.cpp
字号:
/***
*** See the file "mba/disclaimers-and-notices-L2.txt" for
*** information on usage and redistribution of this file,
*** and for a DISCLAIMER OF ALL WARRANTIES.
***/
/* $Id: from_l2.cpp,v 1.1.1.1 2006/10/09 06:58:18 shao Exp $ */
#include <errno.h> // errno, strerror
#include <readers/from_l2.h>
#include <readers/transition.h>
#define tostream(expr) expr
// The do-while(0) is the only portable way to block.
#ifdef ENABLE_L2_VERBOSE
# define verbose(expr) do { if (isVerbose()) { tostream(expr); } } while(0)
#else
# define verbose(expr)
#endif
/***************************************************************************
Store a dictionary. For now, it's very small and could probably
just be an if/else if/... ; but this way I argue it's easier to
read the code, update, and make sure the abbreviations don't clash
***************************************************************************/
/// The sections of a model file in L2 format.
enum Section {
NO_SECTION,
CLAUSES,
COMMANDS,
ENUMS,
MODES,
OBSERVED,
PROPOSITIONS,
TRANSITIONS,
VARIABLES,
INITIAL,
BACKGROUND
};
/// A (section name string, enum) pair used for mapping.
struct SectionName {
const char *name;
enum Section sect;
};
/// A map from section name strings to Section enum values.
SectionName section_names[] = {
// abbreviated
{ "b", BACKGROUND },
{ "cl", CLAUSES },
{ "cm", COMMANDS },
{ "e", ENUMS },
{ "m", MODES },
{ "o", OBSERVED },
{ "p", PROPOSITIONS },
{ "t", TRANSITIONS },
{ "v", VARIABLES },
{ "i", INITIAL },
#ifdef ENABLE_L2_DEBUG_SECTIONS
// full name
{ "background", BACKGROUND },
{ "clauses", CLAUSES },
{ "commands", COMMANDS },
{ "enums", ENUMS },
{ "modes", MODES },
{ "observed", OBSERVED },
{ "propositions", PROPOSITIONS },
{ "transitions", TRANSITIONS },
{ "variables", VARIABLES },
{ "initial", INITIAL }
#endif
};
/// The number of sections in a model file in L2 format.
const size_t num_sections = sizeof(section_names)/sizeof(SectionName);
/**
* Map a section name string onto its Section enum.
* \internal this is not a member function.
* \param the section name string
* \return the corresponding Section enum
*/
Section find_section(const char *name) {
for(size_t i=0; i<num_sections; i++) {
if(!strcmp(name, section_names[i].name))
return section_names[i].sect;
}
return NO_SECTION;
}
/***************************************************************************
Destructor.
***************************************************************************/
from_l2::~from_l2() {
if(the_file) fclose(the_file);
}
/***************************************************************************
Error handling and reporting.
***************************************************************************/
#ifdef ENABLE_L2_VERBOSE
void from_l2::print_parse_error() {
_STD_ cerr <<"Parse error at line " << line_number
<< ", index " << index << _STD_ endl
<< line << _STD_ endl;
for(size_t i=0; i<index; i++) { _STD_ cerr << ' '; }
_STD_ cerr << "^\n";
}
#else
# define print_parse_error()
#endif
/// Check that a section was parsed.
/// Argument 1 is typically a call to was_X_parsed().
/// Argument 2 is what we expect (true for prereqs, false
/// for the section we're now parsing).
/// Argument 3 is the name of the section we're checking.
bool from_l2::check_parsed(bool condition, bool expected, const char *msg) {
if(condition == expected) {
return true;
} else {
print_parse_error();
if (condition) {
tostream(_STD_ cerr << "Section " << msg
<< " has already been parsed.\n");
} else {
tostream(_STD_ cerr<< "Prerequisite " << msg
<< " not yet parsed.\n");
}
return false;
}
}
/***************************************************************************
Lexing functions
***************************************************************************/
/**
* Is string[index] a word separator/terminator?
* White space and the string terminator are word boundaries.
* \internal This is not a member function.
* \param string the string that may contain words
* \param index the character within string that is being tested
* \return whether string[index] is a word separator or terminator.
*/
static bool str_at_word_boundary(const char *string, size_t index) {
switch(string[index]) {
case ' ':
case '\n':
case '\t':
case '\0':
return true;
default:
return false;
}
}
/**
* Copy the next word from string to into and update the index to point to the
* next boundary
* \internal this is not a member function.
* \param into the next word from string is copied into this out parameter
* \param the current character within string; this out parameter is advanced
* \param the string that may contain words
*/
static void str_get_next_word(char *into, size_t& index, const char *string) {
size_t i=0;
while(!str_at_word_boundary(string, index+i)) {
into[i] = string[index+i];
i++;
}
into[i] = 0;
index += i;
}
/// Read a line from the file; leave the results in 'line'
bool from_l2::readline() {
++line_number;
index = 0;
char *ret = fgets(line, L2_MODEL_FILE_MAX_LINE_SIZE, the_file);
// check for EOF
if(!ret) return false;
// check for overflow; also, get rid of the newline: it's annoying
size_t len = strlen(line);
// empty line? I don't think this should ever actually happen.
if(len == 0) return true;
if(line[len-1] != '\n') {
print_parse_error();
tostream(_STD_ cerr
<< "Max line length ("
<< (L2_MODEL_FILE_MAX_LINE_SIZE - 1)
<< ") exceeded."
<< _STD_ endl
<< "Re-compile with a larger value for "
<< "L2_MODEL_FILE_MAX_LINE_SIZE"
<< _STD_ endl);
return false;
}
// kill the \n, thus reducing line length by one
line[len-1] = 0;
return true;
}
/// Intuitively: read the next integer.
/// More formally:
/// Read the substring of the line that starts at index
/// and ends at the next whitespace, newline, or the end
/// of the string. Interpret that substring as a positive
/// integer. Return the integer and set the index to point
/// to the next whitespace etc.
/// Currently we use base 10; the code is easy to modify to
/// other bases.
/// Argument: the name of the thing we're parsing, used for
/// debugging.
/// Errors: return a negative value (other than L2rTransition::ANY_MODE).
/// This occurs if:
/// the integer overflows
/// there are non-digits in the substring
/// If `acceptStar' is on, return L2rTransiton::ANY_MODE if we read a '*'
int from_l2::read_integer(const char *msg, bool acceptStar) {
size_t retval = 0;
unsigned index0 = index;
// loop ends at line[index] in { ' ', '\n', '\t', '\0' },
// or a non-digit line[index]; i's easier to write that as a switch
// statement.
// The line is guaranteed to end, so the loop is too.
if(acceptStar && line[index] == '*') {
index++;
return L2rTransition::ANY_MODE;
}
for(;;++index) {
if(line[index] >= '0' && line[index] <= '9') {
retval *= 10;
retval += line[index]-'0';
continue;
}
// for base > 10, use line[index] >= 'a' && line[index] <= 'f'
// or whatever; and change the *= above.
if(str_at_word_boundary(line, index)) {
if(index0==index) {
print_parse_error();
tostream(_STD_ cerr<< "No integer found while reading "
<< msg << _STD_ endl);
return NOT_AN_INTEGER;
}
return retval;
} else {
// printf("Non-digit character `%c' at index %d in:\n"
// "%s", ...);
print_parse_error();
tostream(_STD_ cerr<< "While reading " << msg << _STD_ endl);
return NOT_AN_INTEGER;
}
// can't get here; we do a continue or return
assert(0);
}
// can't get here; the loop runs until a return
assert(0);
}
/// Skip the word boundary that index is currently in.
/// This advances index to the next non-(word boundary) character,
/// or returns 'false' if we're at the end of the line.
/// If print_error is true, call print_parse_error if we return
/// false.
bool from_l2::skip_word_boundary(bool print_error) {
while(str_at_word_boundary(line, index)) {
if(line[index] == '\0') {
if(print_error)
print_parse_error();
return false;
}
index++;
}
return true;
}
/***************************************************************************
The top-level function
***************************************************************************/
/// The top-level method.
bool from_l2::read() {
// make sure read() hasn't already been called
assert(!the_file);
// open the file
the_file = fopen(filename_.c_str(), "r");
if(!the_file) {
#ifndef _MSC_VER
tostream(_STD_ cerr<< "from_l2: error opening `" << filename_ << "'\n"
<< strerror(errno) << _STD_ endl);
#else
// Under Visual C++ we have not quite figured out the linking
// to get errno to work.
tostream(_STD_ cerr<< "from_l2: error opening `" << filename_ << "'\n"
<< _STD_ endl);
#endif
return false;
}
// first line in the file is a magic cookie to make sure it works.
readline();
if(0 != strcmp(line, L2_READER_MAGIC)) {
tostream(_STD_ cerr<< "from_l2: error opening `" << filename_ << "':\n"
<< "Magic cookie `" << line << "' doesn't match expected " <<
L2_READER_MAGIC << _STD_ endl);
return false;
}
// the main loop; read a line, parse the section name,
// pass it to the appropriate function
while(readline()) {
bool has_debug_info = false;
char *linep = line;
// skip empty lines
if(line[0]=='\0') continue;
#ifdef ENABLE_L2_DEBUG_SECTIONS
// it's a section with debug info; note the fact,
// and search for a section that doesn't start in dbg_
if(!strncmp(linep, "dbg_", 4)) {
has_debug_info = true;
linep+=4;
}
#endif
switch(find_section(linep)) {
case ENUMS:
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -