coan  6.0.1
A C/C++ Configuration Analyzer
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros
parameter_substitution.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 
37 #include "parameter_substitution.h"
38 #include "diagnostic.h"
39 #include "symbol.h"
40 
45 
47 using namespace std;
48 
49 namespace parameter_substitution {
50 
51 string specifier::legible() const
52 {
53  string s = "%{";
54  s += to_string(get_param_index());
55  s += ':';
56  switch(get_handling()) {
57  case handling::substitute_arg:
58  s += "as-is";
59  break;
60  case handling::substitute_expanded_arg:
61  s += "expanded";
62  break;
63  case handling::substitute_quoted_arg:
64  s += "quoted";
65  break;
66  default:;
67  }
68  s += "}%";
69  return s;
70 }
71 
72 ptrdiff_t format::adjust_for_stringify_op(size_t pos, size_t nparams)
73 {
74  size_t mark = pos;
75  if (++pos >= _fmt.length() ||
76  (pos += bool(isspace(_fmt[pos]))) >= _fmt.length()) {
77  return 0;
78  }
79  size_t i_off;
80  size_t h_off;
81  auto found = specifier::get_at(_fmt,pos,i_off,h_off);
82  if (found != string::npos) {
83  _fmt[h_off] = char(handling::substitute_quoted_arg);
84  _fmt.erase(mark,pos - mark);
85  return mark - pos;
86  } else if (nparams) {
88  }
89  return 0;
90 }
91 
92 ptrdiff_t format::adjust_for_token_paste_op(size_t pos)
93 {
94  size_t mark = pos;
95  ptrdiff_t p(pos);
96  while (--p >= 0 && isspace(_fmt[p])){}
97  if (p < 0) {
99  }
100  pos = p;
101  size_t i_off;
102  size_t h_off;
103  if (specifier::get_ending_at(_fmt,pos,i_off,h_off) != string::npos) {
104  _fmt[h_off] = char(handling::substitute_arg);
105  } else if (!identifier::is_valid_char(_fmt[pos]) && _fmt[pos] != '#') {
106  throw error_bad_token_paste();
107  }
108  size_t start_cut = pos + 1;
109  pos = mark + 2;
110  for ( ;pos < _fmt.length() && isspace(_fmt[pos]); ++pos){}
111  if (pos >= _fmt.length()) {
113  }
114  if (specifier::get_at(_fmt,pos,i_off,h_off) != string::npos) {
115  _fmt[h_off] = char(handling::substitute_arg);
116  } else if (!identifier::is_valid_char(_fmt[pos]) && _fmt[pos] != '#') {
117  throw error_bad_token_paste();
118  }
119  size_t cut_len = pos - start_cut;
120  _fmt.erase(start_cut,cut_len);
121  return -cut_len;
122 }
123 
124 void format::do_stringify_adjustments(size_t nparams)
125 {
126  ptrdiff_t netdiff = 0;
127  for (size_t pos: _stringify_offs) {
128  netdiff += adjust_for_stringify_op(pos + netdiff,nparams);
129  }
130 }
131 
132 void format::do_token_paste_adjustments()
133 {
134  ptrdiff_t netdiff = 0;
135  for (size_t pos: _token_paste_offs) {
136  netdiff += adjust_for_token_paste_op(pos + netdiff);
137  }
138 }
139 
140 void format::build_format(symbol & sym)
141 {
142  string & s = const_cast<string &>(*sym.defn());
143  formal_parameter_list const & params = sym.parameters();
144  chewer<string> chew(chew_mode::plaintext,s);
145  while (chew) {
146  auto mark = size_t(chew);
147  size_t diff = mark - _fmt.length();
148  chew(literal_space);
149  if (size_t(chew) > mark) {
150  _fmt += s.substr(mark,size_t(chew) - mark);
151  if (!chew) {
152  break;
153  }
154  continue;
155  }
156  chew(whitespace);
157  if (size_t(chew) > mark) {
158  if (!_fmt.size() || _fmt.back() != ' ') {
159  _fmt += ' ';
160  }
161  if (!chew) {
162  break;
163  }
164  continue;
165  }
166  if (*chew == '#') {
167  _fmt += '#';
168  if (++chew && *chew == '#') {
169  _fmt += '#';
170  _token_paste_offs.push_back(mark - diff);
171  ++chew;
172  continue;
173  }
174  _stringify_offs.push_back(mark - diff);
175  continue;
176  }
177  chew(name);
178  if (size_t(chew) == mark) {
179  _fmt += *chew;
180  ++chew;
181  continue;
182  }
183  string id = s.substr(mark,size_t(chew) - mark);
184  size_t param_i = params.which(id);
185  if (param_i != string::npos) {
186  specifier spec(param_i,handling::substitute_expanded_arg);
187  spec.append_to(_fmt);
188  } else {
189  _fmt += id;
190 
191  }
192  }
193  do_stringify_adjustments(params.size());
194  do_token_paste_adjustments();
195 }
196 
197 format::format(symbol & sym)
198 {
199  if (!sym.defn()) {
200  return;
201  }
202  try {
203  build_format(sym);
204  }
205  catch(error_stringify_non_param & gripe) {
206  gripe << "#-operator must precede a macro parameter in definition >>"
207  << *sym.defn() << "<< of \"" << sym.signature() << '\"' << emit();
208  }
209  catch(error_misplaced_token_paste & gripe) {
210  gripe << "##-operator cannot begin or end the definition >>"
211  << *sym.defn() << "<< of \"" << sym.signature() << '\"' << emit();
212  }
213  catch(error_bad_token_paste & gripe) {
214  gripe << "##-operator does not compose a token in definition >>"
215  << *sym.defn() << "<< of \"" << sym.signature() << '\"' << emit();
216  }
217 }
218 
219 std::string format::legible(string const & str)
220 {
221  string s;
222  chewer<string> chew(chew_mode::plaintext,const_cast<string &>(str));
223  for ( ;chew; chew(literal_space)) {
224  size_t mark = size_t(chew);
225  chew(literal_space);
226  if (size_t(chew) > mark) {
227  s += str.substr(mark,size_t(chew) - mark);
228  }
229  specifier spec = specifier::read(chew);
230  if (spec) {
231  s += spec.legible();
232  continue;
233  }
234  s += *chew;
235  ++chew;
236  }
237  return s;
238 }
239 
240 std::string format::legible() const
241 {
243 }
244 
245 } // namespace parameter_substitution.
246 
248 
249 // EOF
std::string signature() const
Get the symbol's reference signature as string.
Definition: symbol.h:306
template struct error_msg<Id> generically encapsulates an error diagnostic.
Definition: diagnostic.h:495
error_msg< 24 > error_misplaced_token_paste
Report a ## operator at start or end of definition.
Definition: diagnostic.h:774
std::string legible() const
Get a string legibly representing the format.
std::shared_ptr< std::string const > defn() const
Get a pointer to the symbol's definition; null if undefined.
Definition: symbol.h:194
size_t size() const
Get the number of parameters in the parameter_list_base
chew_mode::whitespace const whitespace
An exemplar chew_mode::whitespace
Definition: chew.h:209
formal_parameter_list const & parameters() const
Get the symbol's formal parameter list.
Definition: symbol.h:311
std::string read(chewer< CharSeq > &chew)
Read an identifier from an chewer<CharSeq>
chew_mode::name const name
An exemplar chew_mode::name
Definition: chew.h:229
The tag class is inserted in a diagnostic_base to tell it to emit itself.
Definition: diagnostic.h:77
error_msg< 27 > error_bad_token_paste
Report that token-pasting does not yield a token.
Definition: diagnostic.h:780
bool is_valid_char(char ch)
Say whether a character can occur in an identifier.
Definition: identifier.h:57
`template struct chewer<CharSeq> is a cursor-like type that is associated with a character-sequence t...
Definition: chew.h:248
struct symbol encapsulates a preprocessor symbol's state
Definition: symbol.h:58
size_t which(std::string const &str) const
Get the index of the parameter that matches a string, if any, else -1.
struct formal_parameter_list encapsulates a list of formal macro parameters.
error_msg< 25 > error_stringify_non_param
Report a # operator not preceding a parameter for function-like macro.
Definition: diagnostic.h:776
chew_mode::literal_space const literal_space
An exemplar chew_mode::literal_space
Definition: chew.h:231