// file : cli/parser.cxx // author : Boris Kolpackov // copyright : Copyright (c) 2009-2011 Code Synthesis Tools CC // license : MIT; see accompanying LICENSE file #include #include "token.hxx" #include "lexer.hxx" #include "parser.hxx" #include "semantics.hxx" using namespace std; using namespace semantics; const char* keywords[] = { "include", "namespace", "class", "signed", "unsigned", "bool", "char", "wchar_t", "short", "int", "long", "float", "double" }; const char* punctuation[] = {";", ",", "::", "{", "}", /*"(", ")",*/ "=", "|"}; // Output the token type and value in a format suitable for diagnostics. // std::ostream& operator<< (std::ostream& os, token const& t) { switch (t.type ()) { case token::t_eos: { os << "end-of-stream"; break; } case token::t_keyword: { os << "keyword '" << keywords[t.keyword ()] << "'"; break; } case token::t_identifier: { os << "identifier '" << t.identifier () << "'"; break; } case token::t_punctuation: { os << "'" << punctuation[t.punctuation ()] << "'"; break; } case token::t_path_lit: { os << "path literal"; break; } case token::t_string_lit: { os << "string literal"; break; } case token::t_char_lit: { os << "char literal"; break; } case token::t_bool_lit: { os << "bool literal"; break; } case token::t_int_lit: { os << "integer literal"; break; } case token::t_float_lit: { os << "floating point literal"; break; } case token::t_call_expr: { os << "call expression"; break; } case token::t_template_expr: { os << "template expression"; break; } } return os; } void parser:: recover (token& t) { // Recover by skipping past next ';'. // for (;; t = lexer_->next ()) { if (t.type () == token::t_eos) break; if (t.punctuation () == token::p_semi) { t = lexer_->next (); break; } } } auto_ptr parser:: parse (std::istream& is, path const& p) { auto_ptr unit (new cli_unit (p)); unit_ = unit.get (); lexer l (is, p.string ()); lexer_ = &l; path_ = &p; valid_ = true; def_unit (); if (!valid_ || !l.valid ()) throw invalid_input (); return unit; } void parser:: def_unit () { token t (lexer_->next ()); // include-decl-seq // while (t.keyword () == token::k_include) { try { include_decl (); t = lexer_->next (); } catch (error const&) { valid_ = false; recover (t); } } scope* old (scope_); scope_ = unit_; // decl-seq // while (t.type () != token::t_eos) { try { if (decl (t)) { t = lexer_->next (); continue; } cerr << *path_ << ':' << t.line () << ':' << t.column () << ": error: " << "expected namespace or class declaration instead of " << t << endl; throw error (); } catch (error const&) { valid_ = false; break; // Non-recoverable error. } } scope_ = old; } void parser:: include_decl () { token t (lexer_->next ()); if (t.type () != token::t_path_lit) { cerr << *path_ << ':' << t.line () << ':' << t.column () << ": error: " << "expected path literal instead of " << t << endl; throw error (); } if (valid_) { cxx_unit& n (unit_->new_node (*path_, t.line (), t.column ())); unit_->new_edge (*unit_, n, t.literal ()); } t = lexer_->next (); if (t.punctuation () != token::p_semi) { cerr << *path_ << ':' << t.line () << ':' << t.column () << ": error: " << "expected ';' instead of " << t << endl; throw error (); } } bool parser:: decl (token& t) { if (t.type () == token::t_keyword) { switch (t.keyword ()) { case token::k_namespace: { namespace_def (); return true; } case token::k_class: { class_def (); return true; } default: break; } } return false; } void parser:: namespace_def () { token t (lexer_->next ()); if (t.type () != token::t_identifier) { cerr << *path_ << ':' << t.line () << ':' << t.column () << ": error: " << "expected identifier instead of " << t << endl; throw error (); } scope* old (scope_); if (valid_) { namespace_& n ( unit_->new_node (*path_, t.line (), t.column ())); unit_->new_edge (*scope_, n, t.identifier ()); scope_ = &n; } t = lexer_->next (); if (t.punctuation () != token::p_lcbrace) { cerr << *path_ << ':' << t.line () << ':' << t.column () << ": error: " << "expected '{' instead of " << t << endl; throw error (); } // decl-seq // t = lexer_->next (); while (decl (t)) t = lexer_->next (); scope_ = old; if (t.punctuation () != token::p_rcbrace) { cerr << *path_ << ':' << t.line () << ':' << t.column () << ": error: " << "expected namespace declaration, class declaration, or '}' " << "instead of " << t << endl; throw error (); } } void parser:: class_def () { token t (lexer_->next ()); if (t.type () != token::t_identifier) { cerr << *path_ << ':' << t.line () << ':' << t.column () << ": error: " << "expected identifier instead of " << t << endl; throw error (); } scope* old (scope_); if (valid_) { class_& n (unit_->new_node (*path_, t.line (), t.column ())); unit_->new_edge (*scope_, n, t.identifier ()); scope_ = &n; } t = lexer_->next (); if (t.punctuation () != token::p_lcbrace) { cerr << *path_ << ':' << t.line () << ':' << t.column () << ": error: " << "expected '{' instead of " << t << endl; throw error (); } // decl-seq // t = lexer_->next (); while (true) { try { if (!option_def (t)) break; t = lexer_->next (); } catch (error const&) { valid_ = false; recover (t); } } scope_ = old; if (t.punctuation () != token::p_rcbrace) { cerr << *path_ << ':' << t.line () << ':' << t.column () << ": error: " << "expected option declaration or '}' instead of " << t << endl; throw error (); } t = lexer_->next (); if (t.punctuation () != token::p_semi) { cerr << *path_ << ':' << t.line () << ':' << t.column () << ": error: " << "expected ';' instead of " << t << endl; throw error (); } } bool parser:: option_def (token& t) { size_t l (t.line ()), c (t.column ()); // type-spec // // These two functions set t to the next token if they return // true. // string type_name; if (!qualified_name (t, type_name) && !fundamental_type (t, type_name)) return false; option* o (0); if (valid_) { o = &unit_->new_node