coan  6.0.1
A C/C++ Configuration Analyzer
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros
directive.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  * Copyright (C) 2007-2013 Mike Kinghan, imk@burroingroingjoing.com *
3  * All rights reserved. *
4  * *
5  * Contributed originally by Mike Kinghan, imk@burroingroingjoing.com *
6  * *
7  * Redistribution and use in source and binary forms, with or without *
8  * modification, are permitted provided that the following conditions *
9  * are met: *
10  * *
11  * Redistributions of source code must retain the above copyright *
12  * notice, this list of conditions and the following disclaimer. *
13  * *
14  * Redistributions in binary form must reproduce the above copyright *
15  * notice, this list of conditions and the following disclaimer in the *
16  * documentation and/or other materials provided with the distribution. *
17  * *
18  * Neither the name of Mike Kinghan nor the names of its contributors *
19  * may be used to endorse or promote products derived from this software *
20  * without specific prior written permission. *
21  * *
22  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS *
23  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT *
24  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS *
25  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE *
26  * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, *
27  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, *
28  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS *
29  * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED *
30  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,*
31  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF *
32  * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH *
33  * DAMAGE. *
34  * *
35  **************************************************************************/
36 #include "directive.h"
37 #include "expression_parser.h"
38 #include "integer_constant.h"
39 #include "diagnostic.h"
40 #include "lexicon.h"
41 #include "io.h"
42 #include "hash_include.h"
43 #include "if_control.h"
44 #include "contradiction.h"
45 #include "canonical.h"
46 #include "symbol.h"
47 #include <iostream>
48 
53 using namespace std;
54 
56 
57 template<> std::string const directive<HASH_IF>::_keyword_ = TOK_IF;
58 template<> std::string const directive<HASH_IFDEF>::_keyword_ = TOK_IFDEF;
59 template<> std::string const directive<HASH_IFNDEF>::_keyword_ = TOK_IFNDEF;
60 template<> std::string const directive<HASH_ELSE>::_keyword_ = TOK_ELSE;
61 template<> std::string const directive<HASH_ELIF>::_keyword_ = TOK_ELIF;
62 template<> std::string const directive<HASH_ENDIF>::_keyword_ = TOK_ENDIF;
63 template<> std::string const directive<HASH_DEFINE>::_keyword_ = TOK_DEFINE;
64 template<> std::string const directive<HASH_UNDEF>::_keyword_ = TOK_UNDEF;
65 template<> std::string const directive<HASH_INCLUDE>::_keyword_ = TOK_INCLUDE;
66 template<> std::string const directive<HASH_PRAGMA>::_keyword_ = TOK_PRAGMA;
67 template<> std::string const directive<HASH_ERROR>::_keyword_ = TOK_ERROR;
68 template<> std::string const directive<HASH_LINE>::_keyword_ = TOK_LINE;
69 
70 template<>
71 map<string,bool> directive<HASH_IF>::_directives_tab_ = map<string,bool>();
72 template<>
73 map<string,bool> directive<HASH_IFDEF>::_directives_tab_ = map<string,bool>();
74 template<>
75 map<string,bool> directive<HASH_IFNDEF>::_directives_tab_ = map<string,bool>();
76 template<>
77 map<string,bool> directive<HASH_ELSE>::_directives_tab_ = map<string,bool>();
78 template<>
79 map<string,bool> directive<HASH_ELIF>::_directives_tab_ = map<string,bool>();
80 template<>
81 map<string,bool> directive<HASH_ENDIF>::_directives_tab_ = map<string,bool>();
82 template<>
83 map<string,bool> directive<HASH_DEFINE>::_directives_tab_ = map<string,bool>();
84 template<>
85 map<string,bool> directive<HASH_UNDEF>::_directives_tab_ = map<string,bool>();
86 template<>
87 map<string,bool> directive<HASH_INCLUDE>::_directives_tab_ = map<string,bool>();
88 template<>
89 map<string,bool> directive<HASH_PRAGMA>::_directives_tab_ = map<string,bool>();
90 template<>
91 map<string,bool> directive<HASH_ERROR>::_directives_tab_ = map<string,bool>();
92 template<>
93 map<string,bool> directive<HASH_LINE>::_directives_tab_ = map<string,bool>();
94 
95 vector<directive_base::evaluator> directive_base::_evaluator_tab_ = {
109 };
110 map<string, directive_type>
112  { string(TOK_IF), HASH_IF },
113  { string(TOK_IFDEF), HASH_IFDEF },
114  { string(TOK_IFNDEF), HASH_IFNDEF},
115  { string(TOK_ELSE), HASH_ELSE },
116  { string(TOK_ELIF), HASH_ELIF },
117  { string(TOK_ENDIF), HASH_ENDIF },
118  { string(TOK_DEFINE), HASH_DEFINE },
119  { string(TOK_UNDEF), HASH_UNDEF },
120  { string(TOK_INCLUDE), HASH_INCLUDE },
121  { string(TOK_PRAGMA), HASH_PRAGMA },
122  { string(TOK_ERROR), HASH_ERROR },
123  { string(TOK_LINE), HASH_LINE }
124 };
125 
126 directive_type directive_base::get_type(std::string const & keyword)
127 {
128  auto where = _keyword_to_type_map_.find(keyword);
129  if (where == _keyword_to_type_map_.end()) {
130  warning_unknown_directive() << "Unknown directive #"
131  << keyword << emit();
132  return HASH_UNKNOWN;
133  }
134  return where->second;
135 }
136 
137 void directive_base::report(std::string const & keyword,
138  std::string const & arg)
139 {
140  if (!line_despatch::cur_line().reportable() ||
142  return;
143  }
144  std::cout << '#' << keyword << ' ' << arg;
145  if (options::list_location()) {
146  std::cout << ": " << io::in_file_name()
147  << '(' << line_despatch::cur_line().num() << ')';
148  }
149  std::cout << '\n';
150 }
151 
152 void directive_base::report(bool seen,
153  std::string const & keyword,
154  std::string const & arg)
155 {
156  if (!seen) {
157  report(keyword,arg);
158  } else if (!options::list_only_once()) {
159  report(keyword,arg);
160  }
161 }
162 
163 line_type
165  directive_type type,
166  chewer<parse_buffer> & chew)
167 {
168  chew(greyspace);
169  symbol::locator sloc(chew);
170  chew(greyspace);
171  line_type retval = LT_IF;
172  if (sloc->configured()) {
173  if (sloc->defined()) {
174  /* symbol is -Ded*/
175  retval = type == HASH_IFDEF ?
176  LT_TRUE : LT_FALSE;
177  } else {
178  /* symbol is -Ued*/
179  retval = type == HASH_IFNDEF ?
180  LT_TRUE : LT_FALSE;
181  }
182  } else {
183  if (options::implicit()) {
184  retval = type == HASH_IFDEF ?
185  LT_FALSE : LT_TRUE;
186  }
187  }
188  sloc->report();
189  return retval;
190 }
191 
193 {
194  chew(greyspace);
196  line_type retval = _evaluator_tab_[type](chew);
197  chew(greyspace);
198  if (chew) {
199  if (retval != LT_PLAIN &&
200  type != HASH_IF && type != HASH_ELIF) {
201  string pretty = line_despatch::pretty();
202  string good = pretty.substr(0,size_t(chew));
203  string bad = pretty.substr(size_t(chew));
205  "Superfluous \"" << bad << "\" after directive \""
206  << good << '\"' << emit();
207  }
208  }
209  if (line_despatch::cur_line().dropping()) {
211  } else if (type != HASH_UNDEF) {
213  }
214  return retval;
215 }
216 
218  auto command = options::get_command();
219  if (command == CMD_INCLUDES || command == CMD_DIRECTIVES) {
221  }
222  if (command == CMD_PRAGMAS || command == CMD_DIRECTIVES) {
224  }
225  if (command == CMD_DEFS || command == CMD_DIRECTIVES) {
228  }
229  if (command == CMD_ERRORS || command == CMD_DIRECTIVES) {
231  }
232  if (command == CMD_LINES || command == CMD_DIRECTIVES) {
234  }
235 }
236 
237 template<>
239 {
240  return LT_PLAIN;
241 }
242 
243 
244 template<>
246 {
247  line_type lineval = LT_IF;
248  chew(greyspace);
249  if (!chew) {
250  error_if_without_cond() << "#if/elif has no argument." << emit();
251  }
252  if (!options::eval_wip()) {
253  size_t mark = size_t(chew);
255  if (i.type() == INT_INT &&
256  size_t(chew) == mark + 1 && (i.raw() == 1 || i.raw() == 0)) {
257  chew(greyspace);
258  if (!chew) {
259  return lineval;
260  }
261  }
262  chew = mark;
263  }
265  evaluation ev = ep.result();
266  if (ev.resolved()) {
267  lineval = ev.is_true() ? LT_TRUE : LT_FALSE;
268  }
269  if (ep.is_simplified() && lineval == LT_IF) {
270  line_despatch::cur_line().replace(ep.simplified());
271  line_despatch::cur_line().set_simplified(ep.is_simplified());
272  }
273  return lineval;
274 }
275 
276 template<>
278 {
279  return eval_ifdef_or_ifndef(_type_,chew);
280 }
281 
282 template<>
284 {
285  return eval_ifdef_or_ifndef(_type_,chew);
286 }
287 
288 template<>
290 {
291  return LT_ELSE;
292 }
293 
294 template<>
296 {
297  return
298  static_cast<line_type>(directive_base::eval(HASH_IF,chew)
299  - LT_IF + LT_ELIF);
300 }
301 
302 template<>
304 {
305  return LT_ENDIF;
306 }
307 
308 template<>
310 {
313  chew(greyspace);
314  symbol::locator sloc(chew);
315  string definition;
316  formal_parameter_list macro_params;
317  chew(continuation);
318  if (chew) {
319  if (*chew == '(') {
320  macro_params.read(chew);
321  if (!macro_params.well_formed()) {
323  << "Malformed macro parameter for symbol \""
324  << sloc.id() << "\"" << emit();
325  return LT_DIRECTIVE_KEEP;
326  }
327  }
328  definition = canonical<string>(chew);
329  }
331  string arg = sloc.id();
332  if (definition.length()) {
333  arg += ' ';
334  arg += definition;
335  }
336  directive<HASH_DEFINE>(arg).report();
337  }
338  if (!line_despatch::cur_line().dropping()) {
339  retval = sloc->digest_transient_define(macro_params,definition);
340  }
341  sloc->report();
343  return retval;
344 }
345 
346 template<>
348 {
351 
352  chew(greyspace);
353  symbol::locator sloc(chew);
354  if (!line_despatch::cur_line().dropping()) {
355  retval = sloc->digest_transient_undef();
356  }
357  directive<HASH_UNDEF>(sloc.id()).report();
358  sloc->report();
359  chew(greyspace);
360  return retval;
361 }
362 
363 template<>
365 {
368  chew(greyspace);
369  if (!chew) {
370  warning_no_argument() << "#include has no argument" << emit();
371  } else {
372  hash_include hi(chew);
373  chew(greyspace);
374  if (retval == LT_DIRECTIVE_KEEP && !hi.valid()) {
376  << "#include expects <FILENAME> or \"FILENAME\""
377  << emit();
378  }
379  shared_ptr<reference> ref = hi.symbolic_argument();
380  if (ref) {
381  ref->report();
382  }
383  hi.report();
384  }
385  return retval;
386 }
387 
388 template<>
390 {
391  if (line_despatch::cur_line().reportable()) {
392  string str = canonical<string>(chew);
393  directive<HASH_PRAGMA>(str).report();
394  }
395  return LT_PLAIN;
396 }
397 
398 template<>
400 {
401  if (line_despatch::cur_line().reportable()) {
402  string str = canonical<string>(chew);
403  directive<HASH_ERROR>(str).report();
404  }
405  if (!line_despatch::cur_line().dropping()) {
409  "An operative #error directive was input"
410  << emit();
411  } else {
413  "An operative #error directive was output" << emit();
414  }
415  }
416  }
417  return LT_PLAIN;
418 }
419 
420 template<>
422 {
423  chew(greyspace);
424  size_t off = size_t(chew);
425  if (!chew) {
426  warning_no_argument() << "#line has no argument." << emit();
427  } else if (line_despatch::cur_line().reportable()) {
428  string str = canonical<string>(chew);
429  directive<HASH_LINE>(str).report();
430  }
431  expression_parser<parse_buffer> ep(chew = off);
432  evaluation ev = ep.result();
433  if (!ev.resolved() || int(ev.value().raw()) < 1) {
435  "#line expects a line-number" << emit();
436  }
437  return LT_PLAIN;
438 }
439 
441 
442 
443 // EOF
static parsed_line & cur_line()
Get a reference to the current output line.
Definition: line_despatch.h:68
bool well_formed() const
Say whether the parameter_list_base is well-formed.
The directives command.
Definition: options.h:82
static bool implicit()
Do we implicitly --undef all unconfigured symbols?
Definition: options.h:195
warning_msg< 32 > warning_no_argument
Report a directive that lacks a required argument.
Definition: diagnostic.h:715
static bool list_only_once()
Do we report only the first occurrence of listed items?
Definition: options.h:117
static void forget()
Forget about an apparent contradiction.
unsigned long long raw() const
Get the bits comprising the integer as an unsigned long long
Definition: integer.h:93
void set_directive_type(directive_type dtype)
Set the directive type of the line.
Definition: parsed_line.h:132
static integer read_numeral(chewer< CharSeq > &chew)
Read a numeral from a chewer<CharSeq>
A directive line of no more specific type that is to be kept.
Definition: line_type.h:76
static bool eval_wip()
Do we evaluate constants in truth-functional contexts or treat them as unknowns.
Definition: options.h:167
warning_msg< 17 > warning_invalid_include
Report a problematic argument to an #include directive.
Definition: diagnostic.h:683
error_msg< 17 > error_malformed_macro
Report a malformed macro parameter list.
Definition: diagnostic.h:758
static void report(std::string const &keyword, std::string const &arg)
Report a directive.
#define TOK_IFNDEF
Constant denoting the #ifndef directive.
Definition: lexicon.h:50
The symbols command.
Definition: options.h:76
static std::map< std::string, directive_type > _keyword_to_type_map_
Map from keywords to directive types.
Definition: directive.h:119
integer const & value() const
Get the integral value of the expression.
Definition: evaluation.h:98
struct hash_include encapsulates an #include directive.
Definition: hash_include.h:50
The line command.
Definition: options.h:81
struct symbol::locator encapsulates a symbol table entry.
Definition: symbol.h:79
bool dropping() const
Are we dropping the line?
Definition: parsed_line.h:146
An #if directive.
warning_msg< 16 > warning_unknown_directive
Report an unknown preprocessor directive.
Definition: diagnostic.h:681
Encapsulates a directive of a given type.
Definition: directive.h:141
static bool was_unconditional_line()
Is the current line outside any #if scope?
Definition: if_control.h:103
static command_code get_command()
Get the operative coan command code.
Definition: options.cpp:247
static bool is_unconditional_line()
Is the current line outside any #if scope or in the scope of a satisfied #if?
Definition: if_control.cpp:326
Encapsulates parsing of preprocessor expressions.
static line_type eval_ifdef_or_ifndef(directive_type type, chewer< parse_buffer > &chew)
Evaluate an #ifdef or #ifndef directive.
#define TOK_INCLUDE
Constant denoting the #include directive.
Definition: lexicon.h:62
The defs command.
Definition: options.h:78
A #line directive.
An #ifndef directive.
warning_msg< 10 > warning_garbage_after_directive
Report garbage text was input following a directive.
Definition: diagnostic.h:667
#define TOK_ELSE
Constant denoting the #else directive.
Definition: lexicon.h:52
`struct warning_unconditional_error_input' encapsulates a diagnostic for an error directive input unc...
Definition: diagnostic.h:634
error_msg< 15 > error_if_without_cond
Report #if or #elif has no argument.
Definition: diagnostic.h:754
A #pragma directive.
#define TOK_PRAGMA
Constant denoting the #pragma directive.
Definition: lexicon.h:64
A false #if
Definition: line_type.h:58
void replace(std::string const &replacement)
Replace the line with another string.
Definition: parsed_line.h:91
static line_type eval(std::string keyword, chewer< parse_buffer > &chew)
Evaluate a directive.
Definition: directive.h:65
#define TOK_ELIF
Constant denoting the #elif directive.
Definition: lexicon.h:54
Class integer encapsulates an integer of some type.
Definition: integer.h:65
static std::string in_file_name()
Get the name of the current source file.
Definition: io.h:105
struct warning_unconditional_error_output encapsulates a diagnostic for an error directive output unc...
Definition: diagnostic.h:658
static line_type eval(chewer< parse_buffer > &chew)
Evaluate a directive of this type.
unsigned num() const
Get the greatest source line number spanned by this line.
Definition: parsed_line.h:70
#define TOK_IFDEF
Constant denoting the #ifdef directive.
Definition: lexicon.h:48
A true #if
Definition: line_type.h:56
An #endif directive.
Type int
Definition: integer.h:51
void read(chewer< CharSeq > &chew)
Read the formal_parameter_list from a chewer<CharSeq>
The errors command.
Definition: options.h:80
directive_type
Symbolic constants denoting types of directives.
static directive_type get_type(std::string const &keyword)
Get the directive_type of a directive given a keyword.
#define TOK_ERROR
Constant denoting the #error directive.
Definition: lexicon.h:68
A directive line of no more specific type than that is to be dropped.
Definition: line_type.h:74
chew_mode::continuation const continuation
An exemplar chew_mode::continuation
Definition: chew.h:213
The pragmas command.
Definition: options.h:79
The tag class is inserted in a diagnostic_base to tell it to emit itself.
Definition: diagnostic.h:77
An #else that we can't resolve.
Definition: line_type.h:66
An #undef directive.
#define TOK_DEFINE
Constant denoting the #define directive.
Definition: lexicon.h:58
chew_mode::greyspace const greyspace
An exemplar chew_mode::greyspace
Definition: chew.h:211
#define TOK_ENDIF
Constant denoting the #endif directive.
Definition: lexicon.h:56
bool resolved() const
Say whether the expression has been resolved.
Definition: evaluation.h:65
An #else directive.
An unknown directive.
static bool list_location()
Do we report file and line numbers for listed items?
Definition: options.h:113
static void flush()
Discharge any pending contradiction.
An #include directive.
integer_type type() const
Get the type of the integer
Definition: integer.h:77
bool reportable()
Is the line reportable for the operative command.
Definition: parsed_line.h:127
static std::vector< evaluator > _evaluator_tab_
Table of evaluation functions for directive types, indexed by directive_type.
Definition: directive.h:123
An error diagnostic.
warning_msg< 33 > warning_not_a_line_number
Report a #line directive with argument not a positive integer.
Definition: diagnostic.h:717
The includes command.
Definition: options.h:77
`template struct chewer<CharSeq> is a cursor-like type that is associated with a character-sequence t...
Definition: chew.h:248
#define TOK_LINE
Constant denoting the line directive.
Definition: lexicon.h:66
template class canonical<What> encapsulates the canonical representation of values of type What...
Definition: canonical.h:82
void set_simplified(bool value=true)
Classifiy the line as simplified or not.
Definition: parsed_line.h:117
An #error directive.
static size_t discard(unsigned reason)
Discard any queued diagnostics that match a reason-code, returning the number discarded.
Definition: diagnostic.h:217
An #endif
Definition: line_type.h:68
line_type
Enumeration of types of input lines.
Definition: line_type.h:52
An #ifdef directive.
static void erase_all()
Forget all directives table used by the operative command.
A non-directive line.
Definition: line_type.h:70
An #elif that we can't resolve.
Definition: line_type.h:60
static std::string pretty()
Get a pretty printable version of the current input line.
bool is_true() const
Say whether the expression is true.
Definition: evaluation.h:70
An #elif directive.
#define TOK_IF
Constant denoting the if directive.
Definition: lexicon.h:46
An #define directive.
struct evaluation represents the result of evaluating a putative expression.
Definition: evaluation.h:48
struct formal_parameter_list encapsulates a list of formal macro parameters.
An #if that we can't resolve.
Definition: line_type.h:54
#define TOK_UNDEF
Constant denoting the #undef directive.
Definition: lexicon.h:60