// Lab Problems - file "machinebase.h" // SE307 CCT // T Naughton, CS NUIM, tomn@cs.may.ie // Last updated 1 November 2000 #include #include #include #ifndef MACHINEBASE_H #define MACHINEBASE_H /* This abstract class describes a machine that analyses the ** code of another machine passed to it as input. ** The machine assumes that inputs are syntactically correct ** and follow the restricted format defined in the lab sheets. ** ** The particular analysis performed is determined by the ** ProcessingDeclaration() and ProcessStatement() methods. ** This class cannot instantiate objects. To do so, create a ** derived class with implementations for the aforementioned ** two methods. ** ** As of October 2000 the code resides at ** http://www.cs.may.ie/~tnaughton/ ** at the link for course SE307 Computational Complexity Theory. */ class MachineBase { /* This abstract class defines a machine that reads a ** description of another machine (a program following the ** format described in the lab sheets) and performs some ** analysis on it. The result of the analysis is a simple ** true/false answer. */ /* ** Data members: public, protected, and private */ protected: char b; /* input buffer - file will be read one ** character at a time. */ ifstream machin; /* stream object to read from file. ** The overloaded `>>' operator ignores ** spaces and newlines. */ bool analysis; // the result of the analysis /* ** Member functions: public, protected, and private */ public: MachineBase(string fname); // constructor virtual ~MachineBase(); // destructor bool Analyse(); // analyse the input machine in some way protected: string Spaces(int num) const; /* create an array of blank ** characters. */ string ReadUntil(char c); /* reads characters from a ** file until character c is ** encountered. */ void JumpEndBlock(); /* reposition file pointer ** at the end of the current ** block. */ private: bool ValidHeader(); /* check that the file exists ** and read in the header ** information. */ virtual void ProcessDeclaration() = 0; /* process a declaration. The ** student should change this ** method to suit their own ** purposes. */ virtual void ProcessStatement(int level) = 0; /* process a statement. The ** student should change this ** method to suit their own ** purposes. */ }; MachineBase::MachineBase(string fname) { /* Constructor. Initialise data members. */ machin.open(fname.c_str(), ios::in); // open file for reading if (!machin) { cout << "\nError: Could not open " << fname << " for reading." << endl; } b = '}'; /* if we initialise b to any symbol other ** than `v' (for void) then we can test ** easily if the input file contains no ** program (we have already assumed that ** if there is a program it will be ** syntactically correct). */ analysis = true; // default } MachineBase::~MachineBase() { /* Destructor. Tidy up. ** This method is also virtual. This means that a base ** class pointer pointing to a derived class will call the ** derived class' destructor on any explicit "delete" ** calls. */ machin.close(); // file stream object } bool MachineBase::Analyse() { /* This method analyses the input function in some way, as ** determined by the ProcessDeclaration() and ** ProcessStatement() methods. Analyse() returns the value of ** protected variable `analysis'. On empty input Analyse() ** returns false. */ if (ValidHeader()) { analysis = true; // assume a positive outcome in advance // read first symbol of first statement machin >> b; // all declarations begin with 'int' while (b == 'i') { ProcessDeclaration(); machin >> b; // read first symbol of next statement } /* each top-level statement begins with something other ** than a `}' */ while (b != '}') { ProcessStatement(1); machin >> b; // read first symbol of next statement } /* if a top-level statement begins with `}' then we have ** reached end of input machine. */ cout << endl << b << flush; return analysis; } else { return false; } } bool MachineBase::ValidHeader() { /* Check that the file exists and read & print out the ** header information. On empty input return false. */ if (!machin) { cout << "\nError: An invalid file was specified when " << "creating this machine." << endl; return false; } else { // file was opened successfully // reposition file pointer at beginning of file machin.seekg(0, ios::beg); // read in first symbol machin >> b; /* test in case input file contains no machine (remember ** b was intitialised in the constructor). */ if (b != 'v') { // On empty input, let's return `false' return false; } else { // read in machine (function) header cout << "\n\n" << b; cout << ReadUntil('{'); cout << b << flush; return true; } } } string MachineBase::Spaces(int num) const { /* Return a string of blanks to aid intentation. */ const int grad = 2; // number of blanks per indentation string temp(abs(num)*grad, (num >= 0 ? ' ' : '?')); return temp; } string MachineBase::ReadUntil(char c) { /* Read from `machin' into `strin' until character c is ** encountered. Return all chars up to c as a string. On exit, ** b should equal c. */ string temp; // temporary string temp.erase(); machin >> b; while (b != c) { temp = temp + b; machin >> b; } return temp; } void MachineBase::JumpEndBlock() { /* Reposition file pointer at the end of the current block. ** On entry, b will contain `{' and the file pointer will ** point immediately after the `{' of block in question. On ** exit, the `}' symbol from the end of that block will have ** been read into b. */ machin >> b; while (b != '}') { if (b == '{') { /* a new block. Will return from this recursive call ** when the corresponding `}' has been read. */ JumpEndBlock(); } machin >> b; } } #endif