1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 | // Code derived from Stroustrup's PPP2 book
// § 6.4.2 Writing a grammar
// -and beginning on p 195
//------------------------------------------------------------------------------
/*
List:
"{" Sequence "}"
Sequence
Element
Element " , " Sequence
Element:
"A"
"B"
*/
//------------------------------------------------------------------------------
#include <iostream>
#include <stdexcept>
#include <string>
using std::cerr;
using std::cin;
using std::cout;
using std::exception;
using std::runtime_error;
using std::string;
void error(std::string const& s) { throw runtime_error(s); }
//------------------------------------------------------------------------------
// a simple user-defined type
class Token {
public:
Token(char ch) : kind{ch} {}
char kind = '0';
};
//------------------------------------------------------------------------------
class Token_stream {
public:
Token_stream(); // make a Token_stream that reads from cin
Token get(); // get a Token
void putback(Token t); // put a Token back
private:
bool full; // is there a Token in the buffer?
Token buffer; // here is where we keep a Token put back using putback()
};
Token_stream::Token_stream() : full{false}, buffer{0} // no Token in buffer
{
}
Token Token_stream::get()
{
if (full) { // do we already have a Token ready?
// remove token from buffer:
full = false;
return buffer;
}
char ch;
cin >> ch;
switch (ch) {
case '{':
case '}':
case ',':
case 'A':
case 'B': return Token{ch}; // let each character represent itself
default:
cout << " === in Token_stream::get() ch = '" << ch << "'\n";
return Token{'K'}; // invalid
}
}
// The putback() member function puts its argument back into the Token_stream's
// buffer:
void Token_stream::putback(Token t)
{
buffer = t; // copy t to buffer
full = true; // buffer is now full
}
//------------------------------------------------------------------------------
bool listing();
std::string sequence();
std::string element();
Token_stream ts; // provides get() and putback()
//------------------------------------------------------------------------------
// Note: this is an example to demonstrate parsing the expression of these
// eleven tokens:
// { A , A , A , A , B }
//
// -the idea is to use the functions together to analyze if the input string is
// a valid List, according to the grammar given above.
int main()
try {
cout << "enter: {A, A, A, A, B}\n";
bool valid = listing();
if (valid)
cout << " : this is a valid list\n";
} catch (exception& e) {
cerr << "error: " << e.what() << '\n';
return 1;
} catch (...) {
cerr << "Oops: unknown exception!\n";
return 2;
}
bool listing()
{
string left = ""; // begin with empty slate
Token t = ts.get(); // get the first Token from the Token stream
//
switch (t.kind) { // see which kind of token it is
case '{': // handle "{" Sequence "}"
left += "{ ";
left += sequence();
t = ts.get();
if (t.kind != '}') {
cout << left << '\n';
error(" '}' expected");
return false; // shouldn't reach here
} else {
left += " }";
cout << left;
return true;
}
break;
default:
error("unexpected type in listing : '" + string{t.kind} + "'");
return false; // shouldn't reach here
}
}
std::string sequence()
{
string left = element();
Token t = ts.get();
while (true) {
switch (t.kind) { // see which kind of token it is
case ',': // handle Element " , " Sequence
left += ", ";
left += sequence();
return left;
break;
case '}':
ts.putback(t);
return left;
break;
default:
error("unexpected next token in sequence : '" + string{t.kind} + "'");
return "foo"; // shouldn't reach here
}
}
}
std::string element()
{
Token t = ts.get();
switch (t.kind) { // see which kind of token it is
case 'A':
case 'B': return string{t.kind}; break;
default:
error("unexpected type in element : '" + string{t.kind} + "'");
return "foo"; // shouldn't reach here
}
}
|
build & run:
g++ -std=c++20 -O2 -Wall -pedantic ./ch_06/main_p195.cpp && ./a.out