summaryrefslogtreecommitdiff
path: root/cli
diff options
context:
space:
mode:
authorBoris Kolpackov <boris@codesynthesis.com>2012-05-10 11:09:13 +0200
committerBoris Kolpackov <boris@codesynthesis.com>2012-05-10 11:09:13 +0200
commit4f9022f24c4591391637121c7274d9855b37bd93 (patch)
tree55d15cf4c092e28c588c31daf118cac7b4b5910a /cli
parent963eaef32dc1b35bd065de8b62d4c73a932208b2 (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')
-rw-r--r--cli/generator.cxx31
-rw-r--r--cli/header.cxx28
-rw-r--r--cli/lexer.cxx27
-rw-r--r--cli/parser.cxx161
-rw-r--r--cli/parser.hxx7
-rw-r--r--cli/semantics/unit.hxx29
-rw-r--r--cli/token.hxx3
7 files changed, 237 insertions, 49 deletions
diff --git a/cli/generator.cxx b/cli/generator.cxx
index 842fc54..8b4b244 100644
--- a/cli/generator.cxx
+++ b/cli/generator.cxx
@@ -158,6 +158,19 @@ generate (options const& ops, semantics::cli_unit& unit, path const& p)
process_names (ctx);
}
+ // Check if we need to generate the runtime code. If we include
+ // another options file, then we assume the runtime is generated
+ // there.
+ //
+ bool runtime (true);
+ for (semantics::cli_unit::includes_iterator i (unit.includes_begin ());
+ runtime && i != unit.includes_end ();
+ ++i)
+ {
+ if (i->is_a<semantics::cli_includes> ())
+ runtime = false;
+ }
+
//
//
ofstream hxx (hxx_path.string ().c_str ());
@@ -237,7 +250,9 @@ generate (options const& ops, semantics::cli_unit& unit, path const& p)
<< "#define " << guard << endl
<< endl;
- generate_runtime_header (ctx);
+ if (runtime)
+ generate_runtime_header (ctx);
+
generate_header (ctx);
if (inl)
@@ -256,7 +271,10 @@ generate (options const& ops, semantics::cli_unit& unit, path const& p)
{
cxx_filter filt (ixx);
context ctx (ixx, unit, ops);
- generate_runtime_inline (ctx);
+
+ if (runtime)
+ generate_runtime_inline (ctx);
+
generate_inline (ctx);
}
@@ -270,10 +288,13 @@ generate (options const& ops, semantics::cli_unit& unit, path const& p)
(br ? '>' : '"') << endl
<< endl;
- if (!inl)
- generate_runtime_inline (ctx);
+ if (runtime)
+ {
+ if (!inl)
+ generate_runtime_inline (ctx);
- generate_runtime_source (ctx);
+ generate_runtime_source (ctx);
+ }
if (!inl)
generate_inline (ctx);
diff --git a/cli/header.cxx b/cli/header.cxx
index 3a47c4d..fb714ed 100644
--- a/cli/header.cxx
+++ b/cli/header.cxx
@@ -185,14 +185,38 @@ namespace
//
//
- struct includes: traversal::cxx_includes, context
+ struct includes: traversal::cxx_includes,
+ traversal::cli_includes,
+ context
{
includes (context& c) : context (c) {}
virtual void
traverse (semantics::cxx_includes& i)
{
- os << "#include " << i.file () << endl
+ generate (i.kind (), i.file ().string ());
+ }
+
+ virtual void
+ traverse (semantics::cli_includes& i)
+ {
+ generate (i.kind (),
+ i.file ().base ().string () + options.hxx_suffix ());
+ }
+
+ void
+ generate (semantics::includes::kind_type k, string const& f)
+ {
+ char b, e;
+ if (k == semantics::includes::quote)
+ b = e = '"';
+ else
+ {
+ b = '<';
+ e = '>';
+ }
+
+ os << "#include " << b << f << e << endl
<< endl;
}
};
diff --git a/cli/lexer.cxx b/cli/lexer.cxx
index 3edfc13..baa8423 100644
--- a/cli/lexer.cxx
+++ b/cli/lexer.cxx
@@ -495,7 +495,32 @@ path_literal (xchar c)
break;
}
- return token (token::t_path_lit, lexeme, ln, cl);
+ token::token_type tt;
+
+ if (lexeme.compare (1, 4, "c++:") == 0)
+ {
+ tt = token::t_cxx_path_lit;
+ lexeme = lexeme[0] + string (lexeme, 5, string::npos);
+ }
+ else if (lexeme.compare (1, 4, "cli:") == 0)
+ {
+ tt = token::t_cli_path_lit;
+ lexeme = lexeme[0] + string (lexeme, 5, string::npos);
+ }
+ else
+ {
+ // See if the path ends with .cli. If not, then we assume this is
+ // a C++ inclusion.
+ //
+ size_t n (lexeme.size ());
+
+ if (n > 5 && lexeme.compare (n - 5, 4, ".cli") == 0)
+ tt = token::t_cli_path_lit;
+ else
+ tt = token::t_cxx_path_lit;
+ }
+
+ return token (tt, lexeme, ln, cl);
}
token lexer::
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)
diff --git a/cli/parser.hxx b/cli/parser.hxx
index 4e6c18f..c93b9a0 100644
--- a/cli/parser.hxx
+++ b/cli/parser.hxx
@@ -6,6 +6,7 @@
#ifndef CLI_PARSER_HXX
#define CLI_PARSER_HXX
+#include <map>
#include <string>
#include <memory> // std::auto_ptr
#include <istream>
@@ -61,8 +62,12 @@ private:
lexer* lexer_;
- semantics::cli_unit* unit_;
+ semantics::cli_unit* root_;
+ semantics::cli_unit* cur_;
semantics::scope* scope_;
+
+ typedef std::map<semantics::path, semantics::cli_unit*> include_map;
+ include_map include_map_;
};
#endif // CLI_PARSER_HXX
diff --git a/cli/semantics/unit.hxx b/cli/semantics/unit.hxx
index 9aac2c3..f20ba82 100644
--- a/cli/semantics/unit.hxx
+++ b/cli/semantics/unit.hxx
@@ -24,21 +24,29 @@ namespace semantics
class includes: public edge
{
public:
+ enum kind_type {quote, bracket};
+
cli_unit&
includer () const
{
return *includer_;
}
- string const&
+ kind_type
+ kind () const
+ {
+ return kind_;
+ }
+
+ path const&
file () const
{
return file_;
}
public:
- includes (string const& file)
- : file_ (file)
+ includes (kind_type kind, path const& file)
+ : kind_ (kind), file_ (file)
{
}
@@ -49,7 +57,8 @@ namespace semantics
}
protected:
- string file_;
+ kind_type kind_;
+ path file_;
cli_unit* includer_;
};
@@ -65,8 +74,8 @@ namespace semantics
}
public:
- cli_includes (string const& file)
- : includes (file)
+ cli_includes (kind_type kind, path const& file)
+ : includes (kind, file)
{
}
@@ -92,8 +101,8 @@ namespace semantics
}
public:
- cxx_includes (string const& file)
- : includes (file)
+ cxx_includes (kind_type kind, path const& file)
+ : includes (kind, file)
{
}
@@ -147,8 +156,8 @@ namespace semantics
}
public:
- cli_unit (path const& file)
- : node (file, 1, 1), graph_ (*this)
+ cli_unit (path const& file, size_t line, size_t column)
+ : node (file, line, column), graph_ (*this)
{
// Use a special edge to get this->name() return the global
// namespace name ("").
diff --git a/cli/token.hxx b/cli/token.hxx
index 6389164..cfa3afd 100644
--- a/cli/token.hxx
+++ b/cli/token.hxx
@@ -18,7 +18,8 @@ public:
t_keyword,
t_identifier,
t_punctuation,
- t_path_lit,
+ t_cxx_path_lit,
+ t_cli_path_lit,
t_string_lit,
t_char_lit,
t_bool_lit,