Listing 1: A declaration parser that also recognizes non-empty parameter lists (with possibly unnamed parameters) in function declarations
// // 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