From 4f9022f24c4591391637121c7274d9855b37bd93 Mon Sep 17 00:00:00 2001 From: Boris Kolpackov Date: Thu, 10 May 2012 11:09:13 +0200 Subject: Add support for options file inclusion New include-path prefixes, c++: and cli:, are now recognized (e.g., include ;). 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. --- cli/parser.cxx | 161 ++++++++++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 132 insertions(+), 29 deletions(-) (limited to 'cli/parser.cxx') 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 #include #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 +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 parser:: parse (std::istream& is, path const& p) { - auto_ptr unit (new cli_unit (p)); - unit_ = unit.get (); + auto_ptr 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 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 (*path_, t.line (), t.column ())); - unit_->new_edge (*unit_, n, t.literal ()); + if (tt == token::t_cxx_path_lit) + { + cxx_unit& n ( + root_->new_node (*path_, t.line (), t.column ())); + root_->new_edge (*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 (p, 1, 1)); + root_->new_edge (*cur_, n, ik, f); + include_map_[ap] = &n; + + auto_restore new_cur (cur_, &n); + auto_restore 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 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 (*cur_, *it->second, ik, f); + } } t = lexer_->next (); @@ -258,14 +365,14 @@ namespace_def () throw error (); } - scope* old (scope_); + auto_restore new_scope (scope_); if (valid_) { namespace_& n ( - unit_->new_node (*path_, t.line (), t.column ())); - unit_->new_edge (*scope_, n, t.identifier ()); - scope_ = &n; + root_->new_node (*path_, t.line (), t.column ())); + root_->new_edge (*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 new_scope (scope_); if (valid_) { - class_& n (unit_->new_node (*path_, t.line (), t.column ())); - unit_->new_edge (*scope_, n, t.identifier ()); - scope_ = &n; + class_& n (root_->new_node (*path_, t.line (), t.column ())); + root_->new_edge (*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