diff options
author | Boris Kolpackov <boris@codesynthesis.com> | 2012-05-10 11:09:13 +0200 |
---|---|---|
committer | Boris Kolpackov <boris@codesynthesis.com> | 2012-05-10 11:09:13 +0200 |
commit | 4f9022f24c4591391637121c7274d9855b37bd93 (patch) | |
tree | 55d15cf4c092e28c588c31daf118cac7b4b5910a /cli/parser.cxx | |
parent | 963eaef32dc1b35bd065de8b62d4c73a932208b2 (diff) |
Add support for options file inclusion
New include-path prefixes, c++: and cli:, are now recognized (e.g.,
include <cli:foo>;). Without a prefix, the include declarations is
considered to be c++-include unless the path ends with the .cli
extension.
The cli-included files are loaded and parsed. Currently, only inclusion
relative to the current file is supported. Duplicate inclusions are
detected and ignored based on the absolute filesystem path.
If a file cli-includes another file, then the runtime code is assumed
to come from the included file and is not generated.
Diffstat (limited to 'cli/parser.cxx')
-rw-r--r-- | cli/parser.cxx | 161 |
1 files changed, 132 insertions, 29 deletions
diff --git a/cli/parser.cxx b/cli/parser.cxx index 6480946..9cefdbc 100644 --- a/cli/parser.cxx +++ b/cli/parser.cxx @@ -3,6 +3,7 @@ // copyright : Copyright (c) 2009-2011 Code Synthesis Tools CC // license : MIT; see accompanying LICENSE file +#include <fstream> #include <iostream> #include "token.hxx" @@ -60,9 +61,14 @@ operator<< (std::ostream& os, token const& t) os << "'" << punctuation[t.punctuation ()] << "'"; break; } - case token::t_path_lit: + case token::t_cxx_path_lit: { - os << "path literal"; + os << "c++ path literal"; + break; + } + case token::t_cli_path_lit: + { + os << "cli path literal"; break; } case token::t_string_lit: @@ -105,6 +111,29 @@ operator<< (std::ostream& os, token const& t) return os; } +// RAII-style set new value on construction, restore old one on destruction. +// +template <typename T> +struct auto_restore +{ + auto_restore (T*& var, T* new_val = 0) + : var_ (var), old_val_ (var_) + { + if (new_val != 0) + var_ = new_val; + } + + void + set (T* new_val) {var_ = new_val;} + + ~auto_restore () {var_ = old_val_;} + +private: + T*& var_; + T* old_val_; +}; + + void parser:: recover (token& t) { @@ -126,8 +155,16 @@ recover (token& t) auto_ptr<cli_unit> parser:: parse (std::istream& is, path const& p) { - auto_ptr<cli_unit> unit (new cli_unit (p)); - unit_ = unit.get (); + auto_ptr<cli_unit> unit (new cli_unit (p, 1, 1)); + + { + path ap (p); + ap.absolute (); + ap.normalize (); + include_map_[ap] = unit.get (); + } + + root_ = cur_ = unit.get (); lexer l (is, p.string ()); lexer_ = &l; @@ -164,8 +201,7 @@ def_unit () } } - scope* old (scope_); - scope_ = unit_; + auto_restore<scope> new_scope (scope_, cur_); // decl-seq // @@ -190,26 +226,97 @@ def_unit () break; // Non-recoverable error. } } - - scope_ = old; } void parser:: include_decl () { token t (lexer_->next ()); + token::token_type tt (t.type ()); - if (t.type () != token::t_path_lit) + if (tt != token::t_cxx_path_lit && tt != token::t_cli_path_lit) { cerr << *path_ << ':' << t.line () << ':' << t.column () << ": error: " << "expected path literal instead of " << t << endl; throw error (); } + string const& l (t.literal ()); + includes::kind_type ik (l[0] == '<' ? includes::bracket : includes::quote); + + path f; + try + { + f = path (string (l, 1, l.size () - 2)); + } + catch (const invalid_path& e) + { + cerr << *path_ << ':' << t.line () << ':' << t.column () << ": error: " + << "'" << e.path () << "' is not a valid filesystem path" << endl; + valid_ = false; + } + if (valid_) { - cxx_unit& n (unit_->new_node<cxx_unit> (*path_, t.line (), t.column ())); - unit_->new_edge<cxx_includes> (*unit_, n, t.literal ()); + if (tt == token::t_cxx_path_lit) + { + cxx_unit& n ( + root_->new_node<cxx_unit> (*path_, t.line (), t.column ())); + root_->new_edge<cxx_includes> (*cur_, n, ik, f); + } + else + { + // For now we only support inclusion relative to the current file. + // + path p (path_->directory () / f); + p.normalize (); + + // Detect and ignore multiple inclusions. + // + path ap (p); + ap.absolute (); + ap.normalize (); + + include_map::iterator it (include_map_.find (ap)); + if (it == include_map_.end ()) + { + cli_unit& n (root_->new_node<cli_unit> (p, 1, 1)); + root_->new_edge<cli_includes> (*cur_, n, ik, f); + include_map_[ap] = &n; + + auto_restore<cli_unit> new_cur (cur_, &n); + auto_restore<path const> new_path (path_, &p); + + ifstream ifs (p.string ().c_str ()); + if (ifs.is_open ()) + { + ifs.exceptions (ifstream::failbit | ifstream::badbit); + + try + { + lexer l (ifs, p.string ()); + auto_restore<lexer> new_lexer (lexer_, &l); + + def_unit (); + + if (!l.valid ()) + valid_ = false; + } + catch (std::ios_base::failure const&) + { + cerr << p << ": error: read failure" << endl; + valid_ = false; + } + } + else + { + cerr << p << ": error: unable to open in read mode" << endl; + valid_ = false; + } + } + else + root_->new_edge<cli_includes> (*cur_, *it->second, ik, f); + } } t = lexer_->next (); @@ -258,14 +365,14 @@ namespace_def () throw error (); } - scope* old (scope_); + auto_restore<scope> new_scope (scope_); if (valid_) { namespace_& n ( - unit_->new_node<namespace_> (*path_, t.line (), t.column ())); - unit_->new_edge<names> (*scope_, n, t.identifier ()); - scope_ = &n; + root_->new_node<namespace_> (*path_, t.line (), t.column ())); + root_->new_edge<names> (*scope_, n, t.identifier ()); + new_scope.set (&n); } t = lexer_->next (); @@ -284,8 +391,6 @@ namespace_def () while (decl (t)) t = lexer_->next (); - scope_ = old; - if (t.punctuation () != token::p_rcbrace) { cerr << *path_ << ':' << t.line () << ':' << t.column () << ": error: " @@ -307,13 +412,13 @@ class_def () throw error (); } - scope* old (scope_); + auto_restore<scope> new_scope (scope_); if (valid_) { - class_& n (unit_->new_node<class_> (*path_, t.line (), t.column ())); - unit_->new_edge<names> (*scope_, n, t.identifier ()); - scope_ = &n; + class_& n (root_->new_node<class_> (*path_, t.line (), t.column ())); + root_->new_edge<names> (*scope_, n, t.identifier ()); + new_scope.set (&n); } t = lexer_->next (); @@ -345,8 +450,6 @@ class_def () } } - scope_ = old; - if (t.punctuation () != token::p_rcbrace) { cerr << *path_ << ':' << t.line () << ':' << t.column () << ": error: " @@ -383,9 +486,9 @@ option_def (token& t) if (valid_) { - o = &unit_->new_node<option> (*path_, l, c); - type& t (unit_->new_type (*path_, l, c, type_name)); - unit_->new_edge<belongs> (*o, t); + o = &root_->new_node<option> (*path_, l, c); + type& t (root_->new_type (*path_, l, c, type_name)); + root_->new_edge<belongs> (*o, t); } // option-name-seq @@ -450,7 +553,7 @@ option_def (token& t) } if (valid_) - unit_->new_edge<names> (*scope_, *o, nl); + root_->new_edge<names> (*scope_, *o, nl); // initializer // @@ -539,8 +642,8 @@ option_def (token& t) if (valid_ && !ev.empty ()) { - expression& e (unit_->new_node<expression> (*path_, l, c, et, ev)); - unit_->new_edge<initialized> (*o, e); + expression& e (root_->new_node<expression> (*path_, l, c, et, ev)); + root_->new_edge<initialized> (*o, e); } if (t.punctuation () == token::p_lcbrace) |