We are required to parse out a given program code. Example of code:
procedure example1 {
x = 0;
z = y + x;
a =1;
while a{
x = z + x;
while x {
c = a + b;
}
}
}
What I have tried:
The example code is in a text file, so i open it and then i pass the info to a vector, after which, i get the tokens from the vector one by one and analyse it looking for the keyword. Currently, my code keeps displaying the error message in the Error method, and i can't see to understand why. This is a school assignment. My code is given below. Any and all help is appreciated.
vector<string> tokens;
SimpleParser::SimpleParser()
{
cout << "Please enter a file name: ";
cin >> userInput;
cout << "fILENAME: " + userInput;
openFile(userInput);
}
SimpleParser::~SimpleParser()
{
}
void SimpleParser::openFile(string fileName) {
ifstream myfile(fileName);
if (myfile.is_open())
{
while (getline(myfile, currLine))
{
size_t comments = currLine.find("//");
if (comments != string::npos)
{
currLine = currLine.erase(comments);
allLines += " " + currLine;
}
else {
allLines += " " + currLine;
}
}
myfile.close();
fillVector(allLines);
}
else
{
cout << "Unable to open file";
}
}
//check if line is proc, while,assign
void SimpleParser::fillVector(string line) {
istringstream iss(line);
copy(istream_iterator<string>(iss),
istream_iterator<string>(),
back_inserter(tokens));
next_token = getToken();
procedure();
}
void SimpleParser::procedure() {
Match("procedure");
//string proc_name = next_token;
//Match(proc_name);
Match("{");
stmtLst();
Match("}");
}
void SimpleParser::stmtLst() {
cout << "All lines : "+ allLines;
}
void SimpleParser::Match(string token) {
if (next_token.compare(token) == 0) {
next_token = getToken();
}
else {
Error();
}
}
string SimpleParser::getToken() {
string t = "";
if (countOfVecs < tokens.size()) {
t = tokens[countOfVecs];
}
countOfVecs++;
return t;
}
void SimpleParser::Error() {
cout << "Error parsing!";
//exit(0);
}
void SimpleParser::Stmt() {
string var_name = next_token;
Match(var_name);
Match("=");
Match(next_token);
}
As I can see, the problem is related either to your get or your:
void SimpleParser::Match(string token) {
// compare token with next_token
if (next_token.compare(token) == 0){
// if they match assign next_token to the next token
next_token = getToken();
}else{
// if they don't compare equal throw an error
Error();
}
}
What exactly is the purpose of the above function?
In general there is no need to waste so much memory and read all the file, you can parse it word by word till you get the needed key word. Thus, here is a slightly different implementation of class that will actually parse without copying all the file contents.
class Token{
public:
// data members for the three types of tokens:
// algebraic operator, number, and user defined variable(name, value)
char kind;
double value;
string name;
// constructors for each of the three tokens
Token(char ch): kind(ch), value(0) { }
Token(char ch, double val) :kind(ch), value(val) { }
Token(char ch, string n) :kind(ch), name(n) { }
};
// class used as an input stream for tokens
class Token_stream {
public:
// constructor
Token_stream() :full(false), buffer(0) { }
// member functions
Token get();
private:
// data members defining the Token_stream buffer
bool full;
Token buffer;
};
const string firstKeyword = "key1";
// get function implementation
Token Token_stream::get(){
if (full) {
full=false;
return buffer;
}
char ch;
// to read from file change cin to the relevant input stream object
cin.get(ch);
switch (ch){
// if the token some of the above symbols: return it
case '(': case ')': case '+': case '-': case ',': case '!':
case '*': case '/': case '%': case 'Q': case '=':
return Token(ch);
// if the token a number int of float: return it as "number token"
case '.':
case '0': case '1': case '2': case '3': case '4':
case '5': case '6': case '7': case '8': case '9':
{
cin.putback(ch);
double val;
cin >> val;
return Token(number,val);
}
case '\n': case '\r':
return Token(print);
default:
{
if (isspace(ch)) // skips whitespaces; taking up this funciton from the replaced 'cin'
{
while(isspace(ch)) cin.get(ch);
}
if (isalpha(ch) || ch == '_' || ch == '#') {
string s;
s += ch;
while(cin.get(ch) && (isalpha(ch) || isdigit(ch) || ch == '_' || ch == '#')) s += ch;
cin.putback(ch);
// if the token is some of the predefined "Keywords": return it as the respective token
if (s == firstKeyword) return Token(keyToken);
if (s == secondKeyword) return Token(sekondKeyToken);
if (s == thirdKeyword) return Token(thirdKeyToken);
return Token(name,s);
}
error("bad input token!", ch);
}
}
}
Related
#include "std_lib_facilities.h"
/*
Simple calculator
Revision history:
Facilities added by Bjarne Stroustrup April 2009
(underscores, assignment, and constants)
Revised by Bjarne Stroustrup May 2007
Revised by Bjarne Stroustrup August 2006
Revised by Bjarne Stroustrup August 2004
Originally written by Bjarne Stroustrup
(bs#cs.tamu.edu) Spring 2004.
This program implements a basic expression calculator.
Input from cin; output to cout.
The grammar for input is:
Calculation:
Statement
Print
Quit
Calculation Statement
Statement:
Declaration
Expression
Declaration:
"let" Name "=" Expression
"const" Name "=" Expression
Print:
;
Quit:
q
Expression:
Term
Expression + Term
Expression - Term
Term:
Primary
Term * Primary
Term / Primary
Term % Primary
Primary:
Number
Name
Name = Expression
( Expression )
- Primary
+ Primary
Number:
floating-point-literal
Name:
[a-zA-Z][a-zA-Z_0-9]* // a letter followed by zero or more letters, underscores, and digits
// note that I decided not to start a namewith an underscore
// just because I consider it ugly)
Input comes from cin through the Token_stream called ts.
*/
// Note that I updated the grammar; keeping comments up-to-data is part of modifying code
//------------------------------------------------------------------------------
const char number = '8'; // t.kind==number means that t is a number Token
const char quit = 'q'; // t.kind==quit means that t is a quit Token
const char print = ';'; // t.kind==print means that t is a print Token
const char name = 'a'; // name token
const char let = 'L'; // declaration token
const char con = 'C'; // const declaration token
const string declkey = "let"; // declaration keyword
const string constkey = "const"; // const keyword
const string prompt = "> ";
const string result = "= "; // used to indicate that what follows is a result
//------------------------------------------------------------------------------
class Token {
public:
char kind; // what kind of token
double value; // for numbers: a value
string name; // for names: name itself
Token(char ch) : kind(ch), value(0) {}
Token(char ch, double val) : kind(ch), value(val) {}
Token(char ch, string n) : kind(ch), name(n) {}
};
//------------------------------------------------------------------------------
class Token_stream {
public:
Token_stream(); // make a Token_stream that reads from cin
Token get(); // get a Token (get() is defined elsewhere)
void putback(Token t); // put a Token back
void ignore(char c); // discard tokens up to an including a c
private:
bool full; // is there a Token in the buffer?
Token buffer; // here is where we keep a Token put back using putback()
};
//------------------------------------------------------------------------------
// The constructor just sets full to indicate that the buffer is empty:
Token_stream::Token_stream()
:full(false), buffer(0) // no Token in buffer
{
}
//------------------------------------------------------------------------------
// The putback() member function puts its argument back into the Token_stream's buffer:
void Token_stream::putback(Token t)
{
if (full) error("putback() into a full buffer");
buffer = t; // copy t to buffer
full = true; // buffer is now full
}
//------------------------------------------------------------------------------
Token Token_stream::get() // read characters from cin and compose a Token
{
if (full) { // check if we already have a Token ready
full=false;
return buffer;
}
char ch;
cin >> ch; // note that >> skips whitespace (space, newline, tab, etc.)
switch (ch) {
case quit:
case print:
case '(':
case ')':
case '+':
case '-':
case '*':
case '/':
case '%':
case '=':
return Token(ch); // let each character represent itself
case '.': // a floating-point literal can start with a dot
case '0': case '1': case '2': case '3': case '4':
case '5': case '6': case '7': case '8': case '9': // numeric literal
{
cin.putback(ch);// put digit back into the input stream
double val;
cin >> val; // read a floating-point number
return Token(number,val);
}
default:
if (isalpha(ch)) { // start with a letter
string s;
s += ch;
while (cin.get(ch) && (isalpha(ch) || isdigit(ch) || ch=='_')) s+=ch; // letters digits and underscores
cin.putback(ch);
if (s == declkey) return Token(let); // keyword "let"
if (s == constkey) return Token(con); // keyword "const"
return Token(name,s);
}
error("Bad token");
}
}
//------------------------------------------------------------------------------
void Token_stream::ignore(char c)
// c represents the kind of a Token
{
// first look in buffer:
if (full && c==buffer.kind) {
full = false;
return;
}
full = false;
// now search input:
char ch = 0;
while (cin>>ch)
if (ch==c) return;
}
//------------------------------------------------------------------------------
Token_stream ts; // provides get() and putback()
//------------------------------------------------------------------------------
class Variable {
public:
string name;
double value;
bool var; // variable (true) or constant (false)
Variable (string n, double v, bool va = true) :name(n), value(v), var(va) { }
};
//------------------------------------------------------------------------------
vector<Variable> var_table;
//------------------------------------------------------------------------------
double get_value(string s)
// return the value of the Variable names s
{
for (int i = 0; i<var_table.size(); ++i)
if (var_table[i].name == s) return var_table[i].value;
error("get: undefined variable ", s);
}
//------------------------------------------------------------------------------
void set_value(string s, double d)
// set the Variable named s to d
{
for (int i = 0; i<var_table.size(); ++i)
if (var_table[i].name == s) {
if (var_table[i].var==false) error(s," is a constant");
var_table[i].value = d;
return;
}
error("set: undefined variable ", s);
}
//------------------------------------------------------------------------------
bool is_declared(string var)
// is var already in var_table?
{
for (int i = 0; i<var_table.size(); ++i)
if (var_table[i].name == var) return true;
return false;
}
//------------------------------------------------------------------------------
double define_name(string s, double val, bool var=true)
// add (s,val,var) to var_table
{
if (is_declared(s)) error(s," declared twice");
var_table.push_back(Variable(s,val,var));
return val;
}
//------------------------------------------------------------------------------
double expression(); // declaration so that primary() can call expression()
//------------------------------------------------------------------------------
// deal with numbers and parentheses
double primary()
{
Token t = ts.get();
switch (t.kind) {
case '(': // handle '(' expression ')'
{
double d = expression();
t = ts.get();
if (t.kind != ')') error("')' expected");
return d;
}
case number:
return t.value; // return the number's value
case name:
{
Token next = ts.get();
if (next.kind == '=') { // handle name = expression
double d = expression();
set_value(t.name,d);
return d;
}
else {
ts.putback(next); // not an assignment: return the value
return get_value(t.name); // return the variable's value
}
}
case '-':
return - primary();
case '+':
return primary();
default:
error("primary expected");
}
}
//------------------------------------------------------------------------------
// deal with *, /, and %
double term()
{
double left = primary();
Token t = ts.get(); // get the next token from token stream
while(true) {
switch (t.kind) {
case '*':
left *= primary();
t = ts.get();
break;
case '/':
{
double d = primary();
if (d == 0) error("divide by zero");
left /= d;
t = ts.get();
break;
}
case '%':
{
int i1 = narrow_cast<int>(left);
int i2 = narrow_cast<int>(term());
if (i2 == 0) error("%: divide by zero");
left = i1%i2;
t = ts.get();
break;
}
default:
ts.putback(t); // put t back into the token stream
return left;
}
}
}
//------------------------------------------------------------------------------
// deal with + and -
double expression()
{
double left = term(); // read and evaluate a Term
Token t = ts.get(); // get the next token from token stream
while(true) {
switch(t.kind) {
case '+':
left += term(); // evaluate Term and add
t = ts.get();
break;
case '-':
left -= term(); // evaluate Term and subtract
t = ts.get();
break;
default:
ts.putback(t); // put t back into the token stream
return left; // finally: no more + or -: return the answer
}
}
}
//------------------------------------------------------------------------------
double declaration(Token k)
// handle: name = expression
// declare a variable called "name" with the initial value "expression"
// k will be "let" or "con"(stant)
{
Token t = ts.get();
if (t.kind != name) error ("name expected in declaration");
string var_name = t.name;
Token t2 = ts.get();
if (t2.kind != '=') error("= missing in declaration of ", var_name);
double d = expression();
define_name(var_name,d,k.kind==let);
return d;
}
//------------------------------------------------------------------------------
double statement()
{
Token t = ts.get();
switch (t.kind) {
case let:
case con:
return declaration(t.kind);
default:
ts.putback(t);
return expression();
}
}
//------------------------------------------------------------------------------
void clean_up_mess()
{
ts.ignore(print);
}
//------------------------------------------------------------------------------
void calculate()
{
while (cin)
try {
cout << prompt;
Token t = ts.get();
while (t.kind == print) t=ts.get(); // first discard all "prints"
if (t.kind == quit) return; // quit
ts.putback(t);
cout << result << statement() << endl;
}
catch (exception& e) {
cerr << e.what() << endl; // write error message
clean_up_mess();
}
}
//------------------------------------------------------------------------------
int main()
try {
// predefine names:
define_name("pi",3.1415926535,false); // these pre-defiend names are constants
define_name("e",2.7182818284,false);
calculate();
keep_window_open(); // cope with Windows console mode
return 0;
}
catch (exception& e) {
cerr << e.what() << endl;
keep_window_open("~~");
return 1;
}
catch (...) {
cerr << "exception \n";
keep_window_open("~~");
return 2;
}
//------------------------------------------------------------------------------
Iam specifically interested in calculate() function and particularly in ' while (t.kind == print) t=ts.get(); // first discard all "prints" ' and ' cout << result << statement() << endl;' lines?
When I run the program and provide an input as'2+2;' it produces an output as:
//output screen
>2+2;
=4
but if i provide an input as '2+2' only then it produces an output as
//output screen 2
>2+2
=
and in the "output screen 2" if i press enter and semicolon after '=' which is produced as output then it shows the desired output i.e. 4.
// like this
>2+2
=;
4
>
The point where Iam confused is why is the output of value returned by statement() being controlled by the print character ';'?
So I have been solving problems from this book called
Programming: Principles and Practice Using C++ by Bjarne Stroustrup to learn C++ and have been enjoying it a lot. I recently got stuck on this question in Chapter 10 (Input and Output
Streams).
The question is given below:
Add a command from x to the calculator from Chapter 7 that makes it
take input from a file x. Add a command to y to the calculator that
makes it write its output (both standard output and error output) to
file y.
The source code for the above mentioned calculator is given below:
/*
Simple Calculator
This program implements a basic expression calculator.
Input from cin; output to cout.
The grammar for input is:
Calculation:
Statement
Print
Quit
Statement:
Declaration
Expression
Declaration:
"let" Name "=" Expression
Print:
;
Quit:
q
Expression:
Term
Term '+' Expression //addition
Term '-' Expression //subtraction
Term:
Primary
Term '*' Primary //multiplication
Term '/' Primary //division
Term '%' Primary //mod division
Primary:
Number
'(' Expression ')'
'-' Primary
'+' Primary
Number:
floating-point-literal
Input comes from cin through the Token_stream called ts.
*/
#include "../../std_lib_facilities.h"
class Token //a very simple user-defined type
{
public:
char kind = ' '; //what kind of token
double value = 0; //for numbers: a value
string name = " "; //for strings: a name
Token(char ch) :kind{ ch } { } //initialize kind with ch
Token(char ch, double val) :kind{ ch }, value{ val } {} //initializes kind and value
Token(char ch, string n) :kind{ ch }, name{ n } { } //initializes kind and name
};
class Token_stream
{
public:
//user interface
Token get(); //get a Token
void putback(Token t); //put a Token back
void ignore(char c); //discard characters up to and including a c
private:
//implementation details
//(not directly accessible to users of Token_stream)
bool full{ false }; //is there a Token in the buffer?
Token buffer = 0; //here is where we keep a Token put back with putback()
};
class Variable
{
public:
string name = " ";
string const_name = " ";
double value = 0;
};
vector<Variable>var_table;
double get_value(string s)
//return the value of the Variable named s
{
for (const Variable& v : var_table) {
if (v.name == s) return v.value;
if (v.const_name == s) return v.value;
}
error("get: undefined variable ", s);
return 1;
}
void set_value(string s, double d)
//set the Variable named s to d
{
for (Variable& v : var_table)
{
if (v.name == s)
{
v.value = d;
return;
}
if (v.const_name == s)
{
error("set: cannot assign a new value to an existing constant");
return;
}
}
error("set: undefined variable", s);
}
const char number = '8'; //t.kind == number means that t is a number Token
const char quit = 'q'; //t.kind == quit means that t is a quit Token
const char print = ';'; //t.kind == print means that t is a print token
const char name = 'a'; //name token
const char constant = 'c'; //constant token
const string constdeclkey = "const"; //constant keyword
const char let = 'L'; //declaration token
const string declkey = "let"; //declaration keyword
const string prompt = "> ";
const string result = "= "; //used to indicate that what follows is a result
void Token_stream::putback(Token t)
{
if (full) error("putback() into a full buffer");
buffer = t; //copy t to buffer
full = true; //buffer is now full
}
void Token_stream::ignore(char c)
//c represents the kind of Token
{
//first look in buffer:
if (full && c == buffer.kind)
{
full = false;
return;
}
full = false;
//now search input:
char ch = 0;
while (cin >> ch)
if (ch == c) return;
}
Token Token_stream::get()
{
if (full)
{
full = false; //do we already have a Token ready?
return buffer; //remove Token from buffer
}
char ch;
cin >> ch; //note that >> skips whitespace(space, newline, tab, etc)
switch (ch)
{
case ';': //for "print"
case 'q': //for "quit"
case '(':
case ')':
case '+':
case '-':
case '*':
case '/':
case '%':
case '=':
return Token{ ch }; //let each character represent itself
case '.':
case '0': case '1': case '2': case '3': case '4':
case '5': case '6': case '7': case '8': case '9':
{
cin.putback(ch); //put digit back into the input stream
double val;
cin >> val; //read a floating-point number
return Token{ number, val }; //let '8' represent a "a number"
}
default:
if (isalpha(ch))
{
string s;
s += ch;
while (cin.get(ch) && (isalpha(ch) || isdigit(ch) || ch == '_')) s += ch;
cin.putback(ch);
if (s == declkey) return Token(let); //declaration keyword
else if (s == constdeclkey) return Token(constant);
return Token{ name, s };
}
error("Bad Token");
return 1;
}
}
bool is_declared(string var)
//is var already in var_table?
{
for (const Variable& v : var_table)
{
if (v.name == var) return true;
}
for (const Variable& v : var_table)
{
if (v.const_name == var) return true;
}
return false;
}
double define_name(string var, double val)
//add (var, val) to var_table
{
if (is_declared(var)) set_value(var, val);
Variable v;
v.name = var;
v.value = val;
var_table.push_back(v);
return val;
}
double define_const_name(const string constant, double val)
//add (constant, val) to var_table
{
if (is_declared(constant)) error(constant, " constant declared twice");
Variable v;
v.const_name = constant;
v.value = val;
var_table.push_back(v);
return val;
}
Token_stream ts; //makes a Token_stream called ts that reads from cin
double expression(); //declaration so that primary() can call it
double declaration()
//assume we have seen "let"
//handle: name = expression
//declare a variable called "name" with the initial value "expression"
{
Token t = ts.get();
if (t.kind != name) error("name expected in declaration");
string var_name = t.name;
Token t2 = ts.get();
if (t2.kind != '=') error("= missing in declaration of ", var_name);
double d = expression();
define_name(var_name, d);
return d;
}
double const_declaration()
//assume we have seen "const"
//handle: const_name = expression
//declare a constant called "const" with the initial value "expression"
{
Token t = ts.get();
if (t.kind != name) error("constant_name expected in declaration");
const string const_name = t.name;
Token t2 = ts.get();
if (t2.kind != '=') error("= missing in declaration of ", const_name);
double d = expression();
define_const_name(const_name, d);
return d;
}
double primary()
//deals with numbers and parantheses
//calls expression() and get_token()
{
Token t = ts.get();
switch (t.kind)
{
case '(': //handle '(' expression ')'
{
double d = expression();
t = ts.get();
if (t.kind != ')') error("')' expected");
return d;
}
case number: //we use '8' to represent a number
return t.value; //return the number's value
case '-':
return -primary();
case '+':
return primary();
case name:
return get_value(t.name);
default:
error("primary expected");
return 1;
}
}
double term()
//deals with * and -
//calls primary and get_token()
{
double left = primary(); //read and evaluate a Primary
Token t = ts.get(); //get the next token
while (true)
{
switch (t.kind) //see which kind of token it is
{
case '*':
left *= primary(); //evaluate Primary and multiply
t = ts.get();
break;
case '/':
{
double d = primary(); //evaluate Primary and divide
if (d == 0) error("divide by zero"); //check if primary isnt zero
left /= d;
t = ts.get();
break;
}
case '%':
{
double d = primary();
if (d == 0) error("divide by zero");
left = fmod(left, d);
t = ts.get();
break;
}
default:
ts.putback(t); //put t back into Token stream
return left; //return result to Expression
}
}
}
double expression()
//deals with + and -
//calls term() and get_token()
{
double left = term(); //read and evaluate an Term
Token t = ts.get(); //get the next token from the Token stream
while (true)
{
switch (t.kind) //see which kind of token it is
{
case '+':
left += term(); //evaluate Term and add
t = ts.get();
break;
case '-':
left -= term(); //evaluate Term and subtract
t = ts.get();
break;
default:
ts.putback(t); //put t back into the token stream
return left; //return the answer
}
}
}
void clean_up_mess() //B)
{
//skip until we find a print
ts.ignore(print);
}
double statement()
{
Token t = ts.get();
switch (t.kind)
{
case let:
return declaration();
case constant:
return const_declaration();
default:
ts.putback(t);
return expression();
}
}
void calculate() //expression evaluation loop
{
double val = 0;
while (cin)
try
{
cout << prompt; //print prompt
Token t = ts.get();
while (t.kind == print) //';' for "print now"
t = ts.get(); //eat ';'
if (t.kind == quit) //'q' for "quit"
{
return;
}
ts.putback(t);
cout << result << statement() << '\n';
}
catch (exception& e)
{
cerr << e.what() << '\n'; //write error message
clean_up_mess();
}
}
int main() //main loop and deals with errors
{
try
{
//predefined names:
define_const_name("pi", 3.1415926535);
define_const_name("e", 2.7182818284);
calculate();
keep_window_open();
return 0;
}
catch (exception& e)
{
cerr << e.what() << '\n';
keep_window_open("~~");
return 1;
}
catch (...)
{
cerr << "exception \n";
keep_window_open();
return 2;
}
}
The calculator kind of works like this:
> (2+3)-4*(5/6); //Input
= 1.66667 //Output
> //Next Input
It can also declare variables and constants and then use them back into for calculations:
> let x = 2; //Variable 1
= 2 //assigned value for Var 1
> const y = 3; //Constant 1
= 3 //assigned value Const 1
> x + y; //added var 1 to const 1
= 5 //result
> //next input
Also ; acts like a print character. The expression gets evaluated and displayed as soon as it reads ; and then it clears the token stream and gets ready to read the next input.
The custom header file std.lib.facilities.h is a collection of most standard C++ libraries and functions some of which are written by Bjarne Stroustrup and might have been used in the code given above.
I have also left the code for that header file just in case if someone is curious:
/*
std_lib_facilities.h
*/
/*
simple "Programming: Principles and Practice using C++ (second edition)" course header to
be used for the first few weeks.
It provides the most common standard headers (in the global namespace)
and minimal exception/error support.
Students: please don't try to understand the details of headers just yet.
All will be explained. This header is primarily used so that you don't have
to understand every concept all at once.
By Chapter 10, you don't need this file and after Chapter 21, you'll understand it
Revised April 25, 2010: simple_error() added
Revised November 25 2013: remove support for pre-C++11 compilers, use C++11: <chrono>
Revised November 28 2013: add a few container algorithms
Revised June 8 2014: added #ifndef to workaround Microsoft C++11 weakness
Revised Febrary 2 2015: randint() can now be seeded (see exercise 5.13).
Revised August 3, 2020: a cleanup removing support for ancient compilers
*/
#ifndef H112
#define H112 080315L
#include<iostream>
#include<iomanip>
#include<fstream>
#include<sstream>
#include<cmath>
#include<cstdlib>
#include<string>
#include<list>
#include <forward_list>
#include<vector>
#include<unordered_map>
#include<algorithm>
#include <array>
#include <regex>
#include<random>
#include<stdexcept>
//------------------------------------------------------------------------------
typedef long Unicode;
//------------------------------------------------------------------------------
using namespace std;
template<class T> string to_string(const T& t)
{
ostringstream os;
os << t;
return os.str();
}
struct Range_error : out_of_range { // enhanced vector range error reporting
int index;
Range_error(int i) :out_of_range("Range error: " + to_string(i)), index(i) { }
};
// trivially range-checked vector (no iterator checking):
template< class T> struct Vector : public std::vector<T> {
using size_type = typename std::vector<T>::size_type;
/* #ifdef _MSC_VER
// microsoft doesn't yet support C++11 inheriting constructors
Vector() { }
explicit Vector(size_type n) :std::vector<T>(n) {}
Vector(size_type n, const T& v) :std::vector<T>(n, v) {}
template <class I>
Vector(I first, I last) : std::vector<T>(first, last) {}
Vector(initializer_list<T> list) : std::vector<T>(list) {}
*/
using std::vector<T>::vector; // inheriting constructor
T& operator[](unsigned int i) // rather than return at(i);
{
if (i<0 || this->size() <= i) throw Range_error(i);
return std::vector<T>::operator[](i);
}
const T& operator[](unsigned int i) const
{
if (i<0 || this->size() <= i) throw Range_error(i);
return std::vector<T>::operator[](i);
}
};
// disgusting macro hack to get a range checked vector:
#define vector Vector
// trivially range-checked string (no iterator checking):
struct String : std::string {
using size_type = std::string::size_type;
// using string::string;
char& operator[](unsigned int i) // rather than return at(i);
{
if (i<0 || size() <= i) throw Range_error(i);
return std::string::operator[](i);
}
const char& operator[](unsigned int i) const
{
if (i<0 || size() <= i) throw Range_error(i);
return std::string::operator[](i);
}
};
namespace std {
template<> struct hash<String>
{
size_t operator()(const String& s) const
{
return hash<std::string>()(s);
}
};
} // of namespace std
struct Exit : runtime_error {
Exit() : runtime_error("Exit") {}
};
// error() simply disguises throws:
inline void error(const string& s)
{
throw runtime_error(s);
}
inline void error(const string& s, const string& s2)
{
error(s + s2);
}
inline void error(const string& s, int i)
{
ostringstream os;
os << s << ": " << i;
error(os.str());
}
template<class T> char* as_bytes(T& i) // needed for binary I/O
{
void* addr = &i; // get the address of the first byte
// of memory used to store the object
return static_cast<char*>(addr); // treat that memory as bytes
}
inline void keep_window_open()
{
cin.clear();
cout << "Please enter a character to exit\n";
char ch;
cin >> ch;
return;
}
inline void keep_window_open(string s)
{
if (s == "") return;
cin.clear();
cin.ignore(120, '\n');
for (;;) {
cout << "Please enter " << s << " to exit\n";
string ss;
while (cin >> ss && ss != s)
cout << "Please enter " << s << " to exit\n";
return;
}
}
// error function to be used (only) until error() is introduced in Chapter 5:
inline void simple_error(string s) // write ``error: s and exit program
{
cerr << "error: " << s << '\n';
keep_window_open(); // for some Windows environments
exit(1);
}
// make std::min() and std::max() accessible on systems with antisocial macros:
#undef min
#undef max
// run-time checked narrowing cast (type conversion). See ???.
template<class R, class A> R narrow_cast(const A& a)
{
R r = R(a);
if (A(r) != a) error(string("info loss"));
return r;
}
// random number generators. See 24.7.
inline default_random_engine& get_rand()
{
static default_random_engine ran; // note: not thread_local
return ran;
};
inline void seed_randint(int s) { get_rand().seed(s); }
inline int randint(int min, int max) { return uniform_int_distribution<>{min, max}(get_rand()); }
inline int randint(int max) { return randint(0, max); }
//inline double sqrt(int x) { return sqrt(double(x)); } // to match C++0x
// container algorithms. See 21.9. // C++ has better versions of this:
template<typename C>
using Value_type = typename C::value_type;
template<typename C>
using Iterator = typename C::iterator;
template<typename C>
// requires Container<C>()
void sort(C& c)
{
std::sort(c.begin(), c.end());
}
template<typename C, typename Pred>
// requires Container<C>() && Binary_Predicate<Value_type<C>>()
void sort(C& c, Pred p)
{
std::sort(c.begin(), c.end(), p);
}
template<typename C, typename Val>
// requires Container<C>() && Equality_comparable<C,Val>()
Iterator<C> find(C& c, Val v)
{
return std::find(c.begin(), c.end(), v);
}
template<typename C, typename Pred>
// requires Container<C>() && Predicate<Pred,Value_type<C>>()
Iterator<C> find_if(C& c, Pred p)
{
return std::find_if(c.begin(), c.end(), p);
}
#endif //H112
So to start with this problem I added the constant character from and constant string from_key just under all of the other constants in the calculator code.
const char from = 'f'; // file input read token
const string from_key = "from"; // file input read keyword
Then I added a new case for from token in the statement()'s function definiton
double statement()
{
Token t = ts.get();
switch (t.kind)
{
/.../
case from:
return input();
/.../
}
}
Ignore the function input() being returned from this case. That's kind of my main problem with this question. I will come back to it after the next part.
I followed it by adding a Token return for from's case in Token Token_stream::get() function's definition
Token Token_stream::get()
{
if (full)
{
/.../
}
char ch;
cin >> ch; //note that >> skips whitespace(space, newline, tab, etc)
switch (ch)
{
/.../
default:
if (isalpha(ch))
{
string s;
s += ch;
while (cin.get(ch) && (isalpha(ch) || isdigit(ch) || ch == '_')) s += ch;
cin.putback(ch);
if (s == declkey) return Token(let); //declaration keyword
else if (s == constdeclkey) return Token(constant);
else if (s == from_key) return Token(from);
return Token{ name, s };
}
error("Bad Token");
return 1;
}
}
After that I started writing the input() function which I mentioned seconds ago. It looks like this for now:
double input()
{
Token t = ts.get();
if (t.kind != name) error("read-file name expected in declaration");
string read_file = t.name;
ifstream ifs{ read_file + ".txt" };
if (!ifs) error("cannot open file " + read_file + ".txt");
while (ifs)
{
try
{
//idk what to do here
}
catch (exception& e)
{
cerr << e.what() << '\n';
clean_up_mess();
}
return 0;
}
}
So what I want to do is pretty simple.
I just want Token Token_stream::get() function to read input from ifs rather than cin, kind of like redirect the istream to ifs while its reading data from the file then switch it back to cin when its done. I'm thinking about calling the statement() function back here to print the evaluation on screen for now and then I might redirect it again to write the output to a file y.txt as it's mentioned in the question.
My issue is that I really don't know how to do this. I tried doing an if-else statement to switch istream from cin to ifs but getting ifs out from input()'s scope to Token Token_stream::get()'s scope is a big hurdle for me.
I am currently doing the exercices in Bjarne Stroustup "Programming Principles and Practice Using C++" and I finished the 8th chapter, where he talks about headers and namespaces. Using this knowledge and other online resources, I tired restructuring the Simple Calculator code into multiple header and source files in order to use the calculator function "outside" that file. I am getting 230+ errors and I really don't know why. It's quite a long file, I would highly appreciate anyone that uses their time to look at this. I will provide bellow all code snippets ( quite long )
The project's structure
NOTE: std_lib_facilites.h is provided by the book and contains only simple declarations and functions that eases the understanding of concepts.
calculator.h
#pragma once
void calculate(Token_stream& ts, Symbol_table& st);
double statement(Token_stream& ts, Symbol_table& st);
double declaration(Token_stream& ts, Symbol_table& st);
double square_root(Token_stream& ts, Symbol_table& sts);
double powerf(Token_stream& ts, Symbol_table& st);
double expression(Token_stream& ts, Symbol_table& st);
double term(Token_stream& ts, Symbol_table& st);
double factorial(Token_stream& ts, Symbol_table& st);
double primary(Token_stream& ts, Symbol_table& st);
double variable(Token_stream& ts, Symbol_table& st);
void intro_message();
void cleanup(Token_stream&);
constants.h:
#pragma once
namespace Constants
{
//Constant declarations and initializations-------------------------------------
const char number = '8'; //representation of a number type for a Token
const char sroot = 'S';
const char let = 'L'; //represents the "let" term in declaration()
const char name = 'a'; //name token
const char power = 'P';
const char vconst = 'C';
const string decl_key = "let"; //declaration keyword
const string sroot_key = "sqrt"; //keyword for calling sqare_root() function
const string power_key = "pow"; //keyword for calling power() function
const string constant_key = "const";
const char quit_key = '#';
const char print_key = ';';
const char help_key = '$';
const char show_vars = '&';
const string prompt = ">>";
const string result = "="; //used to indicate that what follows is a result
const char recover = '~'; //used as an argument for the keep_window_open() functions in catch statements
}
token.h:
#pragma once
class Token {
public:
char type;
double value;
string name; // used for a Token of type name
Token(char ch) :type{ ch }, value{ 0 } {};
Token(char ch, double val) :type{ ch }, value{ val } {};
Token(char ch, string n) :type{ ch }, name{ n } {};
};
class Token_stream {
public:
Token_stream();
Token get();
void putback(Token t);
void ignore(char c);
private:
bool isFull = false;
Token buffer;
};
variable.h:
#pragma once
class Variable
{
public:
string name;
double value;
bool isConst;
Variable(string st, double v, bool b) : name{ st }, value{ v }, isConst{ b } {}
Variable(string st, double v) : name{ st }, value{ v }, isConst{ false } {}
};
class Symbol_table
{
public:
double get_value(string s);
void set_value(string s, double n);
bool is_declared(string var);
double define_variable(string var, double val, bool isConst);
void show_variables();
private:
vector <Variable> var_table;
};
calculator.cpp:
#include "calculator.h"
#include "token.h"
#include "variable.h"
#include "constants.h"
#include "std_lib_facilities.h"
//Grammar implementation---------------------------------------------------------
using namespace Constants;
void calculate(Token_stream& ts, Symbol_table& st)
{
//double val;
while (cin)
try {
cout << prompt;
Token t = ts.get();
while (t.type == print_key) t = ts.get(); // "eat" print_key characters
if (t.type == quit_key) return; //NOTE : In void functions, you can do an empty return.
if (t.type == help_key) intro_message();
if (t.type == show_vars) st.show_variables();
else {
ts.putback(t);
cout << result << statement(ts,st) << "\n\n";
}
//ts.putback(t);
//cout << result << statement() << "\n\n";
//val = statement();
}
catch (exception& e)
{
cout.clear();
cerr << "error: " << e.what() << "\n";
cerr << "Enter " << recover << " to continue.\n";
cleanup(ts);
}
catch (...)
{
cerr << "Unknown error.\n";
cerr << "Enter " << recover << " to continue.\n";
}
}
double statement(Token_stream& ts, Symbol_table& st)
{
Token t = ts.get();
switch (t.type)
{
case let:
return declaration(ts, st);
default:
ts.putback(t);
return expression(ts,st);
}
}
double declaration(Token_stream& ts, Symbol_table& st)
{
// assume we already saw "let" (in statement())
// handle: name = expression
// declare a variable called "name" with initial value "expression"
Token t = ts.get();
bool isConst = false;
if (t.type == vconst)
{
t = ts.get();
isConst = true;
if (t.type != name) error("name expected in declaration");
string var_name = t.name;
}
else if (t.type != name) error("name expected in declaration");
string var_name = t.name;
Token t2 = ts.get();
if (t2.type != '=') error("= missing in declaration of ", var_name);
double d = expression(ts,st);
st.define_variable(var_name, d, isConst);
return d;
}
double square_root(Token_stream& ts, Symbol_table& st)
{
// get a token, assuming that we've already used the string "sqrt" in get()
Token t = ts.get();
if (t.type != '(') error("sqrt: '(' expected");
double e = expression(ts,st);
if (e < 0) error("sqrt: cannot calculate square root of negative number");
t = ts.get();
if (t.type != ')') error("sqrt: ')' expected");
return sqrt(e);
}
double powerf(Token_stream& ts, Symbol_table& st)
{
Token t = ts.get();
if (t.type != '(') error("power: '(' expected");
double t1 = expression(ts,st);
t = ts.get();
if (t.type != ',') error("power: arguments must be separated by a ','");
double t2 = expression(ts, st);
if (t2 < 0) error("power: negative exponent");
t = ts.get();
if (t.type != ')') error("power: ')' expected");
return pow(t1, t2);
}
double expression(Token_stream& ts, Symbol_table& st)
{
double left = term(ts, st);
Token t = ts.get();
while (true)
{
switch (t.type)
{
case '+':
left += term(ts, st);
t = ts.get();
break;
case '-':
left -= term(ts, st);
t = ts.get();
break;
default:
ts.putback(t);
return left; // if there's no more + and -, return the result
}
}
return left;
}
double term(Token_stream& ts, Symbol_table& st)
{
double left = factorial(ts, st);
Token t = ts.get();
while (true)
{
switch (t.type)
{
//ou de paste :)
case '*':
left *= factorial(ts, st);
t = ts.get();
break;
case '/':
{
double d = factorial(ts, st);
if (d == 0) error("term: division: cannot divide by 0");
left /= d;
t = ts.get();
break;
}
case '%': //Only works for integers
{
int i1 = narrow_cast<int>(left);
int i2 = narrow_cast<int>(factorial(ts, st));
if (i2 == 0) error("term: modulo: cannot divide by 0");
left = i1 % i2;
t = ts.get();
break;
}
default:
ts.putback(t);
return left;
}
}
return left;
}
double factorial(Token_stream& ts, Symbol_table& st)
{
double left = primary(ts, st);
Token t = ts.get();
switch (t.type)
{
case '!':
{
int lcopy = narrow_cast<int>(left);
if (left == 0) return 1; // 0! = 1
if (left < 0) error("factorial: cannot calculate factorial of a negative number");
while (lcopy > 1)
{
--lcopy;
left *= lcopy;
}
t = ts.get();
if (t.type == '!') error("factorial: unexpected '!' operator");
}
default:
ts.putback(t);
return left;
}
return left;
}
double primary(Token_stream& ts, Symbol_table& st)
{
Token t = ts.get();
switch (t.type)
{
case '(':
{
double e = expression(ts, st);
t = ts.get();
if (t.type != ')') error("primary: ')' expected");
return e;
}
case '{':
{
double e = expression(ts, st);
Token b = ts.get();
if (b.type != '}') error("primary: '}' expected");
return e;
}
case '-':
return -primary(ts, st);
case '+':
return primary(ts, st);
case number:
return t.value;
case name:
ts.putback(t);
return variable(ts, st);
case power:
return powerf(ts, st);
case sroot:
return square_root(ts, st);
default:
error("primary expexted");
}
}
double variable(Token_stream& ts, Symbol_table& st) {
Token t = ts.get();
switch (t.type)
{
case name:
{
Token t2 = t;
t = ts.get();
// check to see if it's an assignment or just a usage of the variable
if (t.type == '=')
{
double e = expression(ts, st);
st.set_value(t2.name, e);
return e;
}
else
{
ts.putback(t);
return st.get_value(t2.name);
}
}
}
}
//-------------------------------------------------------------------------------
//Additional functions-----------------------------------------------------------
void intro_message() //print a custom "banner"
{
cout << "---------------------------------------\n"
<< "|Simple calculator - V1.0 |\n"
<< "| by BIBAN|\n"
<< "---------------------------------------\n\n"
<< "Supported operators : +, -, *, /, % (for ints only), (), !-factorial\n"
<< "Supported functions :\n"
<< " - sqrt(expression) - calculates square root of any expression\n"
<< " - pow(base, exponent) - calculate a base at the power of exponent\n"
<< " --> base and exponent are expressions\n\n"
<< "Variables can be defined and used as expressions:\n"
<< " - let variable_name = value - define a variable\n"
<< " - let const constant_name = value - define a constant\n"
<< " - variable_name = new_value - assign a new value to a non-constant variable\n"
<< " - " << show_vars << " - display all variables\n\n"
<< "Use " << quit_key << " to quit the program, " << print_key << " to end an ecuation and " << help_key << " to display this message.\n"
<< "If an error occurs, type in " << recover << " to continue.\n\n";
}
void cleanup(Token_stream& ts)
{ //recover from an error
ts.ignore(recover);
}
//-------------------------------------------------------------------------------
token.cpp:
#include "token.h"
#include "constants.h"
#include "std_lib_facilities.h"
using namespace Constants;
Token_stream() :isFull(false), buffer(0) {}
Token Token_stream::get()
{
if (isFull)
{
isFull = false;
return buffer;
}
else
{
char ch;
cin >> ch;
switch (ch)
{
case '+':
case '-':
case '!':
case '*':
case '/':
case '%':
case '{':
case '}':
case '(':
case ')':
case '=': //for Variable declaration and assignment
case ',': //used for separating arguments in functions
case quit_key:
case print_key:
case help_key:
case show_vars:
return Token(ch);
case '0': case '1': case '2': case '3': case '4':
case '5': case '6': case '7': case '8': case '9':
cin.putback(ch);
double d;
cin >> d;
return Token(number, d);
default:
//test if the next token is a string and return a Token if it's valid
if (isalpha(ch)) // is ch a letter ?
{
string s;
s += ch;
while (cin.get(ch) && (isalpha(ch) || isdigit(ch) || ch == '_')) s += ch;
cin.putback(ch);
if (s == decl_key) return Token{ let };
if (s == sroot_key) return Token{ sroot };
if (s == power_key) return Token{ power };
if (s == constant_key) return Token{ vconst };
return Token{ name,s }; //Token of type name (for Variable) and value s(the name for the Variable)
}
runtime_error("Bad token.");
}
}
}
void Token_stream::putback(Token t)
{
if (isFull) runtime_error("buffer already full");
isFull = true;
buffer = t;
}
//Used to recover from errors by ignoring all characters, except char c
void Token_stream::ignore(char c) // c represents the type for the Token
{
// look in buffer
if (isFull && c == buffer.type)
{
isFull = false;
return;
}
isFull = false;
//search input
char ch = 0;
while (cin >> ch) if (ch == c) return;
}
variable.cpp:
#include "std_lib_facilities.h"
#include "variable.h"
double Symbol_table::get_value(string s)
{
// return the value of the Variable named s
for (const Variable& v : var_table)
if (v.name == s) return v.value;
error("get: undefined variable ", s);
}
void Symbol_table::set_value(string s, double n)
{
// set the variable named s to n
for (Variable& v : var_table)
{
if (v.name == s && v.isConst == false)
{
v.value = n;
return;
}
else if (v.name == s && v.isConst) error("set_value: cannot change value of a constant variable");
}
error("set: undefined variable ", s);
}
bool Symbol_table::is_declared(string var)
{
//is var in var_table already?
for (const Variable& v : var_table)
if (v.name == var) return true;
return false;
}
double Symbol_table::define_variable(string var, double val, bool isConst)
{
// add {var,val,isConst} to var_table
if (is_declared(var)) error(var, " declared twice.");
var_table.push_back(Variable{ var,val,isConst });
return val;
}
void Symbol_table::show_variables()
{
for (int i = 0; i < var_table.size(); ++i)
cout << var_table[i].name << " = " << var_table[i].value << "\n";
}
and the main.cpp file, has only a main() function:
#include "calculator.h"
#include "token.h"
#include "variable.h"
Token_stream ts;
Symbol_table st;
int main()
{
calculate(ts, st);
return 0;
}
The problem is that at the point where calculator.h is included the compiler doesn't yet know what Token_stream and Symbol_table are. This is an error, the compiler does not look forward in the code to find out what those symbols are, it just emits an error.
Three possible solutions in order from worst to best (IMHO)
Just make sure that you #include "calculator.h" after token.h and variable.h. That way when the compiler gets to calculator.h it knows what Token_stream and Symbol_table are so no error.
Add #include "token.h" and #include "variable.h" to the beginning of calculator.h. That way the compiler is forced to read token.h and variable.h before it reads the rest of calculator.h
Add forward declarations to calculator.h.
Add the code
// forward declarations
class Token_stream;
class Symbol_table;
to the beginning of calculator.h. This tells the compiler that those symbols are the names of classes. And (in this case) that's enough for the compilation to proceed.
Also, to anyone that has linker errors, mostly in Linux, MAKE TRIPLE SURE THAT YOU INCLUDE ALL THE RELATED .cpp FILES IN THE G++ COMMAND
This question already has answers here:
What is an undefined reference/unresolved external symbol error and how do I fix it?
(39 answers)
Closed 5 years ago.
Hey I am a noob at programming but I am using the Programming Principles and Practice using C++ by Bjarne Stroustrup. I got to a section where we where creating a simple calculator application and in order to do this I made a Token stream class to deal with the calculator input.
class Token_stream {
public:
Token_stream();
Token get();
void putback(Token t);
private:
bool full{ false };
Token buffer;
};
The problem with this is that I get a linker error (LNK2019) from Token_stream() that says it is a unresolved external symbol. As far as I can tell Token_stream() is the default constructor for that class but I do not know how to resolve this error. The code is exactly the same as in the book also so I am stuck. Do you have any idea of how I could fix this?
Edit here's the entire program:
#include <std_fac.h>
void error(string s1, string s2) {
throw runtime_error(s1 + s2);
}
class Token {
public:
char kind; //What kind of token
double value; //For numbers: a value
Token(char ch) // make a Token from a char
:kind(ch), value(0) { }
Token(char ch, double val) // make a Token from a char and a double
:kind(ch), value(val) { }
};
class Token_stream {
public:
Token_stream();
Token get();
void putback(Token t);
private:
bool full{ false };
Token buffer;
};
void Token_stream::putback(Token t) {
if (full) error("putback() into a full buffer");
buffer = t;
full = true;
}
Token Token_stream::get() {
if (full) {
full = false;
return buffer;
}
char ch;
cin >> ch;
switch (ch) {
case ';': // for print
case 'q': // for quit
case '{': case '}': case '(': case ')': case '+': case'-': case '*':
case'/':
return Token{ ch };
case'.':
case '0': case '1': case '2': case '3': case '4':
case '5': case '6': case '7': case '8': case '9':
{
cin.putback(ch);
double val;
cin >> val;
return Token('8', val);
}
default:
error("Bad Token");
}
}
double primary();
double term();
double expression(){
double left = term();
Token_stream ts;
Token t = ts.get();
while(true){
switch (t.kind) {
case '+':
left += term();
t = ts.get();
break;
case '-':
left -= term();
t = ts.get();
break;
default:
ts.putback(t);
return left;
}
}
return left;
}
double term() { // % functionality needs to be implimented
double left = primary();
Token_stream ts;
Token t = ts.get();
while (true) {
switch (t.kind) {
case '*':
left *= primary();
t = ts.get();
break;
case '/':
{ double d = primary();
if (d == 0) error("Dividing by zero");
left /= primary();
t = ts.get();
break;
}
default:
ts.putback(t);
return left;
}
}
}
double primary() {
Token_stream ts;
Token t = ts.get();
switch(t.kind) {
case '{':
{ double d = expression();
t = ts.get();
if (t.kind != '}') error("'}' expected");
return d;
}
case '(':
{ double d = expression();
t = ts.get();
if (t.kind != ')' ) error("')' expected");
return d;
}
case '8':
return t.value;
default:
error("Primary expected");
}
}
int main(){
try {
Token_stream ts;
Token t = ts.get();
while (cin) {
cout << ">";
Token t = ts.get();
if (t.kind == 'q')
{
cout << '\n' << "Goodbye!" << '\n';
keep_window_open();
return 1;
}
if (t.kind == ';') {
cout << "=" << t.value;
}
}
}
catch (exception& e) {
cerr << "error: " << e.what()<< '\n';
keep_window_open();
return 1;
}
catch (...) {
cerr << "Exception \n";
return 2;
}
return 0;
}
In my copy of the book there is a definition
Token_stream::Token_stream()
: full(false), buffer(0)
{ }
at the very end of the page before put_back. You must have missed those lines.
I am very new to C++ and am attempting to create a simple calculator which follows mathematical conventions (BODMAS or PEMDAS). I made a function calculator, but then decided to go further and accept variables, at which point I kept receiving C2280 error.
The error is:
token_stream::token_stream(void): attempting to reference a deleted function
I simply dont understand what this means and have no idea what to do to fix it.
Here is the token_stream class:
class token_stream { //creates a stream of tokens
public:
Token get(); //call to get token function
void putback(Token t); //call to token putback function
void ignore(char a); // call to ignore bad tokens function
private:
bool full{ false }; //is there a token in the buffer?
Token buffer; //this is where putback token is stored
};
Here is the entire calculator code:
#include "../../std_lib_facilities.h"
//---------------------------------------------------------------------------------------------------
class Token //user defined type with two member functions
{
public:
char kind;
double value;
string name;
Token(char ch) : kind{ ch } {} //initialize kind with ch
Token(char ch, double val) : kind{ ch }, value{ val } {} //initialize kind and value
Token(char ch, string name) : kind{ ch }, name{ name } {} //initialize kind and name
};
class token_stream { //creates a stream of tokens
public:
Token get(); //call to get token function
void putback(Token t); //call to token putback function
void ignore(char a); // call to ignore bad tokens function
private:
bool full{ false }; //is there a token in the buffer?
Token buffer; //this is where putback token is stored
};
class Variable {
public:
string name;
double val;
};
//---------------------------------------------------------------------------------------------------
const char number = '8'; //t.kind==number means that t is a number token
const char quit = 'q'; //t.kind==quit means that t is a quit token
const char print = ';'; //t.kind==print means that t is a print token
const char name = 'a';
const char let = 'L';
const string declr_key = "let";
vector<Variable> var_table;
token_stream ts;
//---------------------------------------------------------------------------------------------------
void token_stream::putback(Token t) {
if (full) error("putback() is full"); //tests precondition to see if buffer is full
buffer = t;
full = true;
}
void token_stream::ignore(char c) {
if (full && c == buffer.kind) {
full = false;
return;
}
char ch{ 0 };
while (cin >> ch) {
if (c == ch)
return;
}
}
Token token_stream::get() { //creates a token from inputs
if (full) { //checks if we have a token ready
full = false;
return buffer;
}
char ch;
cin >> ch;
switch (ch) {
case print:
case quit:
case '(':
case ')':
case '+':
case '-':
case '*':
case '/':
case '%':
return Token{ ch }; break; //let each character represent itself
case '.': // floatng point literal can start with a dot
case '0': case '1': case '2': case '3': case '4':
case '5': case '6': case '7': case '8': case '9':
{
cin.putback(ch); //putback the digit into input stream
double val;
cin >> val; //read a floating point number
Token t{ number,val };
return t;
break;
}
default:
if (isalpha(ch)) {
string s;
s += ch;
while (cin.get(ch) && (isalpha(ch) || isdigit(ch))) s += ch;
cin.putback(ch);
if (s == declr_key) return Token{ let };
return Token{ name, s };
}
error("Error in Input: Token Invalid"); break;
}
}
double get_value(string s) { //gets the value for a given variable
for (int i = 0; i < var_table.size(); i++)
if (s == var_table[i].name) return var_table[i].val;
error("Undefined variable: ", s);
}
void set_value(string s,double d) { // sets the value for a pre-defined variable
for (int i = 0; i < var_table.size(); i++)
if (s == var_table[i].name) {
var_table[i].val = d;
return;
}
error("Undefined variable: ", s);
}
//---------------------------------------------------------------------------------------------------
double primary(); //declaration so that term can call primary()
double term() { //does multiplication, division, and modulo
double left = primary();
Token t = ts.get();
while (true)
{
switch (t.kind) {
case '*':
{
left *= primary();
t = ts.get();
break;
}
case '/': {
double d = primary();
if (d == 0) error("Divide by 0!"); //non-zero denom check
else {
left /= d;
t = ts.get();
}
break;
}
case '%': {
double d = primary();
if (d == 0)error("Divide by 0!"); //non-zero denom check
else {
left = fmod(left, d);
t = ts.get();
}
break;
}
default: ts.putback(t); //put back in token strem if none of the other options
return left; break;
}
}
}
double expression() { //handles +, -
double left = term();
Token t = ts.get();
while (true) {
switch (t.kind) {
case '+':
{
left += term();
t = ts.get();
break; }
case '-': {
left -= term();
t = ts.get();
break; }
default: ts.putback(t); //put back into token stream if none of the options above
return left;
break;
}
}
}
double primary() {
Token t = ts.get();
switch (t.kind) {
case '(':
{
double d = expression();
t = ts.get();
if (t.kind != ')') error("Expected a ')'");
return d;
break;
}
case number:
return t.value;
break;
case '-': //handles negative numbers
return - primary();
break;
case '+': //handles positive numbers which are preceded by '+'
return primary();
break;
default: error("primary expected: no value after operator given. check input");
}
}
void check_var(string name) {
for (int i = 0; i < var_table.size(); i++)
if (name == var_table[i].name)
error("Variable name already exists");
}
double define_name(string name, double num) {
check_var(name);
var_table.push_back(Variable{ name, num });
return num;
}
double declaration() {
// assume we have seen a 'let'
// handles: name=expression
// declare a variable called 'name' with value 'val'
Token t = ts.get();
if (t.kind != name) error("Expected a variable name"); //throws an error if no variable name given
string var_name = t.name;
t = ts.get();
if (t.kind != '=')error("No '=' sign present in declaration of ", var_name);
double d = expression();
define_name(var_name, d);
return d;
}
double statement() {
Token t = ts.get();
switch (t.kind) {
case let:
return declaration();
default:
ts.putback(t);
return expression();
}
}
void clean_up_mess() {
ts.ignore(print);
}
void calculate() { //expression evaluation loop
while (cin)
{
try {
Token t = ts.get();
if (t.kind == print) t = ts.get(); //first discard all 'prints'
if (t.kind == quit) {
return;
}
ts.putback(t);
cout << "Answer: " << statement() << "\n";
}
catch (exception&e) {
cout << "Error: " << e.what() << "\n";
clean_up_mess();
}
}
}
//---------------------------------------------------------------------------------------------------
int main()
{
try {
cout << "Enter an expression to solve: \n"; //user prompt
calculate();
keep_window_open();
}
catch(...) {
cout << "Error of Unknown origin.\n";
}
}
Any help is really appreciated!
As Token doesn't have a default constructor (a constructor which can be called without any parameters), and token_stream contains Token, the compiler cannot generate default constructor for token_stream.
Two possible solutions:
Add a default constructor to Token
Add a default constructor to token_stream, which initializes buffer to some value