diff options
Diffstat (limited to 'build/parser.cxx')
-rw-r--r-- | build/parser.cxx | 167 |
1 files changed, 167 insertions, 0 deletions
diff --git a/build/parser.cxx b/build/parser.cxx new file mode 100644 index 0000000..669ac8b --- /dev/null +++ b/build/parser.cxx @@ -0,0 +1,167 @@ +// file : build/parser.cxx -*- C++ -*- +// copyright : Copyright (c) 2014-2015 Code Synthesis Tools CC +// license : MIT; see accompanying LICENSE file + +#include <build/parser> + +#include <iostream> + +#include <build/token> +#include <build/lexer> + +using namespace std; + +namespace build +{ + // Output the token type and value in a format suitable for diagnostics. + // + ostream& + operator<< (ostream&, const token&); + + typedef token_type type; + typedef token_punctuation punc; + + void parser:: + parse (istream& is, const path& p) + { + lexer l (is, p.string (), diag_); + lexer_ = &l; + path_ = &p; + + token t (0, 0); // eos + type tt; + + for (next (t, tt); tt != type::eos; ) + { + // We always start with one or more names. + // + names (t, tt); + + if (t.is (punc::colon)) + { + next (t, tt); + + if (tt == type::name || t.is (punc::lcbrace)) + names (t, tt); + + if (t.is (punc::newline)) + next (t, tt); + else if (tt != type::eos) + { + error (t) << "expected newline insetad of " << t << endl; + throw parser_error (); + } + + continue; + } + + error (t) << "unexpected " << t << endl; + throw parser_error (); + } + } + + void parser:: + names (token& t, type& tt) + { + for (bool first (true);; first = false) + { + // Untyped name group, e.g., '{foo bar}'. + // + if (t.is (punc::lcbrace)) + { + next (t, tt); + names (t, tt); + + if (!t.is (punc::rcbrace)) + { + error (t) << "expected '}' instead of " << t << endl; + throw parser_error (); + } + + next (t, tt); + continue; + } + + // Name. + // + if (tt == type::name) + { + string name (t.name ()); + + // See if this is a type name, that is, it is followed by '{'. + // + next (t, tt); + + if (t.is (punc::lcbrace)) + { + //cout << "type: " << name << endl; + + //@@ TODO: + // + // - detect nested typed name groups, e.g., 'cxx{hxx{foo}}'. + // + next (t, tt); + names (t, tt); + + if (!t.is (punc::rcbrace)) + { + error (t) << "expected '}' instead of " << t << endl; + throw parser_error (); + } + + next (t, tt); + continue; + } + + // This is a target, directory, or variable name. + //cout << "name: " << name << endl; + continue; + } + + if (!first) + break; + + error (t) << "expected name instead of " << t << endl; + throw parser_error (); + } + } + + void parser:: + next (token& t, token_type& tt) + { + t = lexer_->next (); + tt = t.type (); + } + + ostream& parser:: + error (const token& t) + { + return diag_ << path_->string () << ':' << t.line () << ':' << + t.column () << ": error: "; + } + + // Output the token type and value in a format suitable for diagnostics. + // + ostream& + operator<< (ostream& os, const token& t) + { + switch (t.type ()) + { + case token_type::eos: os << "<end-of-stream>"; break; + case token_type::punctuation: + { + switch (t.punctuation ()) + { + case token_punctuation::newline: os << "<newline>"; break; + case token_punctuation::colon: os << "':'"; break; + case token_punctuation::lcbrace: os << "'{'"; break; + case token_punctuation::rcbrace: os << "'}'"; break; + } + break; + } + case token_type::name: os << '\'' << t.name () << '\''; break; + } + + return os; + } +} |