diff options
author | Boris Kolpackov <boris@codesynthesis.com> | 2016-01-26 15:41:53 +0200 |
---|---|---|
committer | Boris Kolpackov <boris@codesynthesis.com> | 2016-01-26 15:41:53 +0200 |
commit | 6d52ac69e940a6be6c15c5c6f84a183bf56899c8 (patch) | |
tree | 057fc64f259d171d400b2ce3afc6450ff2b200aa /cli/parser.cxx | |
parent | 8e07c5e7dec299df8a0fbdb2d2c6ae5fbf4d9db4 (diff) |
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.
Diffstat (limited to 'cli/parser.cxx')
-rw-r--r-- | cli/parser.cxx | 136 |
1 files changed, 134 insertions, 2 deletions
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<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; + } + } + } + + 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 ()); |