//
// decl.cpp - translate C++ declarations into English
// with syntactic error detection and recovery
// using exception handling
//
// Copyright (C) 1996 by Dan Saks.
// May be copied for private, non-commercial use,
// provided this copyright notice remains intact.
// All other rights reserved.
//
#include <iostream>
#include "scanner.h"
class parser
{
public:
parser(istream &, ostream &);
private:
enum declarator_category { ABSTRACT, CONCRETE, EITHER };
struct recoverable_error { };
scanner input;
ostream &output;
string indent;
void error(const string &);
void must_be(token::category);
string array_suffix();
string cv_qualifier_seq();
string declarator(declarator_category);
string decl_specifier_seq();
string direct_declarator(declarator_category);
string function_suffix();
string parameter_declaration();
string parameter_list();
string ptr_operator();
string simple_declaration();
parser(const parser &);
parser &operator=(const parser &);
};
void parser::error(const string &why)
{
output << "error: " << why << '\n';
input.reset();
indent = "";
throw recoverable_error();
}
void parser::must_be(token::category tc)
{
if (input.current().kind() == tc)
input.get();
else
error(string("\'") + image(tc) + "' expected");
}
//
// array-suffix =
// "[" [ constant-name | integer-literal ] "]" .
//
string parser::array_suffix()
{
must_be(token::LEFT_BRACKET);
string as = "array with ";
token::category tc = input.current().kind();
if (tc == token::NAME || tc == token::INT_LITERAL)
{
as += input.current().text() + ' ';
input.get();
}
else
as += "unspecified number of ";
as += "elements of type...\n";
must_be(token::RIGHT_BRACKET);
return indent + as;
}
//
// cv-qualifier-seq =
// { "const" | "volatile" } .
//
string parser::cv_qualifier_seq()
{
bool cq = false;
bool vq = false;
token::category tc;
for (;;)
{
tc = input.current().kind();
if (tc == token::CONST)
{
if (cq)
error("redundant 'const' qualifier");
else
cq = true;
}
else if (tc == token::VOLATILE)
{
if (vq)
error("redundant 'volatile' qualifier");
else
vq = true;
}
else
break;
input.get();
}
string t;
if (cq)
t += "const ";
if (vq)
t += "volatile ";
return t;
}
//
// declarator =
// direct-declarator | ptr-operator declarator .
//
string parser::declarator(declarator_category dc)
{
token::category tc = input.current().kind();
if (tc == token::AMPERSAND || tc == token::STAR
|| tc == token::NAME)
{
string p = ptr_operator();
return declarator(dc) + p;
}
else
return direct_declarator(dc);
}
//
// decl-specifier-seq =
// {
// "const" | "volatile" | type-keyword | type-name
// } .
//
string parser::decl_specifier_seq()
{
bool cq = false;
bool vq = false;
string tn;
token::category tc;
for (;;)
{
tc = input.current().kind();
if (tc == token::NAME)
{
input.mark();
tc = input.get().kind();
input.backup();
if (tc == token::SCOPE)
break;
tc = input.current().kind();
}
if (tc == token::CONST)
{
if (!cq)
cq = true;
else
error("redundant 'const' qualifier");
}
else if (tc == token::VOLATILE)
{
if (!vq)
vq = true;
else
error("redundant 'volatile' qualifier");
}
else if (tc == token::TYPE_KEYWORD
|| tc == token::NAME)
{
if (tn == "")
tn = input.current().text();
else
break;
}
else
break;
input.get();
}
if (tn == "")
{
if (!(cq | vq))
error("no type specifier");
tn = "int";
}
string t;
if (cq)
t += "const ";
if (vq)
t += "volatile ";
return t + tn;
}
//
// direct-declarator =
// [ declarator-id | "(" declarator ")" ]
// { array-suffix | function-suffix } .
//
string parser::direct_declarator(declarator_category dc)
{
string dd;
token::category tc = input.current().kind();
if (tc == token::IDENTIFIER)
{
if (dc == ABSTRACT)
error("declarator-id not allowed in abstract-declarator");
dd = indent + input.current().text() + " is ...\n";
input.get();
}
else if (tc == token::LEFT_PAREN)
{
bool its_grouping = false;
input.mark();
tc = input.get().kind();
if (tc == token::IDENTIFIER
|| tc == token::AMPERSAND || tc == token::STAR
|| tc == token::LEFT_BRACKET || tc == token::LEFT_PAREN
|| (tc == token::NAME && input.get().kind() == token::SCOPE))
its_grouping = true;
input.backup();
if (its_grouping)
{
input.get();
dd = declarator(dc);
must_be(token::RIGHT_PAREN);
}
}
if (dd == "")
{
if (dc == CONCRETE)
error("declarator-id missing or obscured"
" by something before it");
dd = indent + "<unnamed> is ...\n";
}
for (;;)
{
tc = input.current().kind();
if (tc == token::LEFT_BRACKET)
dd += array_suffix();
else if (tc == token::LEFT_PAREN)
dd += function_suffix();
else
break;
}
return dd;
}
//
// function-suffix =
// "(" [ parameter-list ] ")" cv-qualifier-seq .
//
string parser::function_suffix()
{
must_be(token::LEFT_PAREN);
string fs = "function with ";
token::category tc = input.current().kind();
if (tc == token::NAME || tc == token::TYPE_KEYWORD
|| tc == token::CONST || tc == token::VOLATILE)
fs += "parameter(s) ...\n" + parameter_list();
else
fs += "no parameters ...\n";
must_be(token::RIGHT_PAREN);
fs += indent + "returning ...\n";
string cvs = cv_qualifier_seq();
if (cvs != "")
fs = cvs + "member " + fs;
return indent + fs;
}
//
// parameter-declaration =
// decl-specifier-seq declarator .
//
string parser::parameter_declaration()
{
string dss = indent + decl_specifier_seq();
string pd = declarator(EITHER) + dss + '\n';
return pd;
}
//
// parameter-list =
// parameter-declaration { "," parameter-declaration } .
//
string parser::parameter_list()
{
const string tab_stop = " ";
indent += tab_stop;
string pl = parameter_declaration();
while (input.current().kind() == token::COMMA)
{
input.get();
pl += parameter_declaration();
}
indent = string(indent, 0, indent.length() - tab_stop.length());
return pl;
}
//
// ptr-operator =
// "&" | [ type-name "::" ] "*" cv-qualifier-seq .
//
string parser::ptr_operator()
{
token t = input.current();
if (t.kind() == token::AMPERSAND)
{
input.get();
return indent + "reference to ...\n";
}
else
{
string p = "pointer to ";
if (t.kind() == token::NAME)
{
p += "member of " + t.text()
+ " with type ";
input.get();
must_be(token::SCOPE);
}
must_be(token::STAR);
return indent + cv_qualifier_seq() + p + "...\n";
}
}
//
// simple-declaration =
// decl-specifier-seq declarator { "," declarator } .
//
string parser::simple_declaration()
{
string d = decl_specifier_seq();
string sd = declarator(CONCRETE) + d + '\n';
while (input.current().kind() == token::COMMA)
{
input.get();
sd += declarator(CONCRETE) + d + '\n';
}
return sd;
}
//
// parser =
// { simple-declaration ";" } .
//
parser::parser(istream &is, ostream &os)
: input(is), output(os)
{
for (;;)
try
{
while (input.get().kind() != token::NO_MORE)
{
string s = simple_declaration();
if (input.current().kind() != token::SEMICOLON)
error("';' expected");
else
output << s;
}
break;
}
catch (const recoverable_error &)
{
}
}
int main()
{
parser declarations(cin, cout);
return 0;
}
//End of File