From 6d52ac69e940a6be6c15c5c6f84a183bf56899c8 Mon Sep 17 00:00:00 2001 From: Boris Kolpackov Date: Tue, 26 Jan 2016 15:41:53 +0200 Subject: Implement support for sourcing .cli files The idea is that the file is "read in" as if its content was copy-n-pasted. For example: "\h|Installation|" source "INSTALL.cli" // Also used to generate plain text INSTALL. Unlike include, source can appear anywhere in the file. --- cli/lexer.cxx | 3 +- cli/lexer.hxx | 2 +- cli/parser.cxx | 136 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++- cli/parser.hxx | 3 ++ cli/token.hxx | 1 + 5 files changed, 141 insertions(+), 4 deletions(-) (limited to 'cli') diff --git a/cli/lexer.cxx b/cli/lexer.cxx index bf16ce5..02be745 100644 --- a/cli/lexer.cxx +++ b/cli/lexer.cxx @@ -22,6 +22,7 @@ lexer (istream& is, string const& id) buf_ (0, 0, 0), unget_ (false) { + keyword_map_["source"] = token::k_source; keyword_map_["include"] = token::k_include; keyword_map_["namespace"] = token::k_namespace; keyword_map_["class"] = token::k_class; @@ -340,7 +341,7 @@ identifier (xchar c) if (i != keyword_map_.end ()) { - if (i->second == token::k_include) + if (i->second == token::k_include || i->second == token::k_source) include_ = true; return token (i->second, ln, cl); diff --git a/cli/lexer.hxx b/cli/lexer.hxx index 710090d..830860c 100644 --- a/cli/lexer.hxx +++ b/cli/lexer.hxx @@ -131,7 +131,7 @@ private: keyword_map keyword_map_; bool eos_; - bool include_; + bool include_; // Literal in include or source. bool valid_; xchar buf_; diff --git a/cli/parser.cxx b/cli/parser.cxx index b10cb0e..ba20ab1 100644 --- a/cli/parser.cxx +++ b/cli/parser.cxx @@ -197,11 +197,17 @@ def_unit () // include-decl-seq // - while (t.keyword () == token::k_include) + for (token::keyword_type k (t.keyword ()); + k == token::k_include || k == token::k_source; + k = t.keyword ()) { try { - include_decl (); + if (k == token::k_include) + include_decl (); + else + source_decl (); + t = lexer_->next (); } catch (error const&) @@ -219,6 +225,22 @@ def_unit () { try { + if (t.keyword () == token::k_source) + { + try + { + source_decl (); + t = lexer_->next (); + } + catch (error const&) + { + valid_ = false; + recover (t); + } + + continue; + } + if (decl (t)) { t = lexer_->next (); @@ -239,6 +261,116 @@ def_unit () } void parser:: +source_decl () +{ + token t (lexer_->next ()); + + if (t.type () != token::t_cli_path_lit) + { + cerr << *path_ << ':' << t.line () << ':' << t.column () << ": error: " + << "expected cli path literal instead of " << t << endl; + throw error (); + } + + string const& l (t.literal ()); + bool q (l[0] == '"'); // Quote or braket include? + + 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_) + { + path p; + + // If this is a quote include, then include relative to the current + // file. + // + if (q) + { + p = path_->directory () / f; + p.normalize (); + } + // Otherwise search the include directories (-I). + // + else + { + struct stat s; + for (paths::const_iterator i (include_paths_.begin ()); + i != include_paths_.end (); ++i) + { + p = *i / f; + p.normalize (); + + // Check that the file exist without checking for permissions, etc. + // + if (stat (p.string ().c_str (), &s) == 0 && S_ISREG (s.st_mode)) + break; + + p.clear (); + } + + if (p.empty ()) + { + cerr << *path_ << ':' << t.line () << ':' << t.column () << ": " + << "error: file '" << f << "' not found in any of the " + << "include search directories (-I)" << endl; + valid_ = false; + } + } + + if (valid_) + { + 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; + } + } + } + + t = lexer_->next (); + + if (t.punctuation () != token::p_semi) + { + cerr << *path_ << ':' << t.line () << ':' << t.column () << ": error: " + << "expected ';' instead of " << t << endl; + throw error (); + } +} + +void parser:: include_decl () { token t (lexer_->next ()); diff --git a/cli/parser.hxx b/cli/parser.hxx index 733921a..767b8da 100644 --- a/cli/parser.hxx +++ b/cli/parser.hxx @@ -38,6 +38,9 @@ private: def_unit (); void + source_decl (); + + void include_decl (); bool diff --git a/cli/token.hxx b/cli/token.hxx index 22679d3..d04ee7a 100644 --- a/cli/token.hxx +++ b/cli/token.hxx @@ -43,6 +43,7 @@ public: public: enum keyword_type { + k_source, k_include, k_namespace, k_class, -- cgit v1.1