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/parser.cxx | 136 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 134 insertions(+), 2 deletions(-) (limited to 'cli/parser.cxx') 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 ()); -- cgit v1.1