diff options
-rw-r--r-- | cli/generator.cxx | 31 | ||||
-rw-r--r-- | cli/header.cxx | 28 | ||||
-rw-r--r-- | cli/lexer.cxx | 27 | ||||
-rw-r--r-- | cli/parser.cxx | 161 | ||||
-rw-r--r-- | cli/parser.hxx | 7 | ||||
-rw-r--r-- | cli/semantics/unit.hxx | 29 | ||||
-rw-r--r-- | cli/token.hxx | 3 | ||||
-rw-r--r-- | doc/language.txt | 11 | ||||
-rw-r--r-- | tests/lexer/driver.cxx | 9 | ||||
-rw-r--r-- | tests/lexer/test-004.cli | 3 | ||||
-rw-r--r-- | tests/lexer/test-004.std | 12 | ||||
-rw-r--r-- | tests/parser/base.cli | 0 | ||||
-rw-r--r-- | tests/parser/common.cli | 1 | ||||
-rw-r--r-- | tests/parser/test-001.cli | 2 |
14 files changed, 268 insertions, 56 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, diff --git a/doc/language.txt b/doc/language.txt index bb00976..0a5ee5f 100644 --- a/doc/language.txt +++ b/doc/language.txt @@ -1,8 +1,9 @@ Token types: keyword identifier - punctuation (";" "{" "}" "(" ")" "," "|" "=" ":") - path-literal + punctuation (";" "{" "}" "(" ")" "," "|" "=" ":") + cxx-path-literal ("c++:path", <c++:path>, "path", <path>) + cli-path-literal ("cli:path", <cli:path>, "path.cli", <path.cli>) char-literal string-literal bool-literal @@ -20,7 +21,11 @@ include-decl-seq: include-decl-seq include-decl include-decl: - "include" path-literal ";" + "include" include-path ";" + +include-path: + cxx-path-literal + cli-path-literal decl-seq: decl diff --git a/tests/lexer/driver.cxx b/tests/lexer/driver.cxx index c16d92b..20dbf9d 100644 --- a/tests/lexer/driver.cxx +++ b/tests/lexer/driver.cxx @@ -71,9 +71,14 @@ main (int argc, char* argv[]) cout << punctuation[t.punctuation ()] << endl; break; } - case token::t_path_lit: + case token::t_cxx_path_lit: { - cout << "path: " << t.literal () << endl; + cout << "c++ path: " << t.literal () << endl; + break; + } + case token::t_cli_path_lit: + { + cout << "cli path: " << t.literal () << endl; break; } case token::t_string_lit: diff --git a/tests/lexer/test-004.cli b/tests/lexer/test-004.cli index bb6ccea..db95a81 100644 --- a/tests/lexer/test-004.cli +++ b/tests/lexer/test-004.cli @@ -1,2 +1,5 @@ include "foo/abc.hxx"; include <vector>; +include "c++:map"; +include <cli:map>; +include "map.cli" diff --git a/tests/lexer/test-004.std b/tests/lexer/test-004.std index e031891..4216cd1 100644 --- a/tests/lexer/test-004.std +++ b/tests/lexer/test-004.std @@ -1,7 +1,15 @@ keyword: include -path: "foo/abc.hxx" +c++ path: "foo/abc.hxx" ; keyword: include -path: <vector> +c++ path: <vector> ; +keyword: include +c++ path: "map" +; +keyword: include +cli path: <map> +; +keyword: include +cli path: "map.cli" <EOS> diff --git a/tests/parser/base.cli b/tests/parser/base.cli new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/tests/parser/base.cli diff --git a/tests/parser/common.cli b/tests/parser/common.cli new file mode 100644 index 0000000..c0c7262 --- /dev/null +++ b/tests/parser/common.cli @@ -0,0 +1 @@ +include "base.cli"; diff --git a/tests/parser/test-001.cli b/tests/parser/test-001.cli index 80233ff..473a238 100644 --- a/tests/parser/test-001.cli +++ b/tests/parser/test-001.cli @@ -2,3 +2,5 @@ // include <string>; include "types.hxx"; +include "common.cli"; +include "../parser/base.cli"; |