1370 lines
35 KiB
C++
1370 lines
35 KiB
C++
/****************************************************************************
|
|
**
|
|
** Copyright (C) 2008-2009 Nokia Corporation and/or its subsidiary(-ies).
|
|
** All rights reserved.
|
|
** Contact: Nokia Corporation (qt-info@nokia.com)
|
|
**
|
|
** This file is part of the Qt Script Generator project on Qt Labs.
|
|
**
|
|
** $QT_BEGIN_LICENSE:LGPL$
|
|
** No Commercial Usage
|
|
** This file contains pre-release code and may not be distributed.
|
|
** You may use this file in accordance with the terms and conditions
|
|
** contained in the Technology Preview License Agreement accompanying
|
|
** this package.
|
|
**
|
|
** GNU Lesser General Public License Usage
|
|
** Alternatively, this file may be used under the terms of the GNU Lesser
|
|
** General Public License version 2.1 as published by the Free Software
|
|
** Foundation and appearing in the file LICENSE.LGPL included in the
|
|
** packaging of this file. Please review the following information to
|
|
** ensure the GNU Lesser General Public License version 2.1 requirements
|
|
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
|
|
**
|
|
** In addition, as a special exception, Nokia gives you certain additional
|
|
** rights. These rights are described in the Nokia Qt LGPL Exception
|
|
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
|
|
**
|
|
** If you have questions regarding the use of this file, please contact
|
|
** Nokia at qt-info@nokia.com.
|
|
**
|
|
**
|
|
**
|
|
**
|
|
**
|
|
**
|
|
**
|
|
**
|
|
** $QT_END_LICENSE$
|
|
**
|
|
****************************************************************************/
|
|
|
|
#ifndef PP_ENGINE_BITS_H
|
|
#define PP_ENGINE_BITS_H
|
|
|
|
#include <stdio.h>
|
|
|
|
namespace rpp {
|
|
|
|
inline std::string pp::fix_file_path(std::string const &filename) const
|
|
{
|
|
#if defined (PP_OS_WIN)
|
|
std::string s = filename;
|
|
for (std::string::iterator it = s.begin(); it != s.end(); ++it)
|
|
{
|
|
if (*it == '/')
|
|
*it = '\\';
|
|
}
|
|
return s;
|
|
#else
|
|
return filename;
|
|
#endif
|
|
}
|
|
|
|
inline bool pp::is_absolute(std::string const &filename) const
|
|
{
|
|
#if defined(PP_OS_WIN)
|
|
return filename.length() >= 3
|
|
&& filename.at(1) == ':'
|
|
&& (filename.at(2) == '\\' || filename.at(2) == '/');
|
|
#else
|
|
return filename.length() >= 1
|
|
&& filename.at(0) == '/';
|
|
#endif
|
|
}
|
|
|
|
template <typename _OutputIterator>
|
|
void pp::file (std::string const &filename, _OutputIterator __result)
|
|
{
|
|
FILE *fp = fopen (filename.c_str(), "rb");
|
|
if (fp != 0)
|
|
{
|
|
std::string was = env.current_file;
|
|
env.current_file = filename;
|
|
file (fp, __result);
|
|
env.current_file = was;
|
|
}
|
|
//else
|
|
//std::cerr << "** WARNING file ``" << filename << " not found!" << std::endl;
|
|
}
|
|
|
|
template <typename _OutputIterator>
|
|
void pp::file (FILE *fp, _OutputIterator __result)
|
|
{
|
|
assert (fp != 0);
|
|
|
|
#if defined (HAVE_MMAP)
|
|
struct stat st;
|
|
fstat(FILENO (fp), &st);
|
|
std::size_t size = st.st_size;
|
|
char *buffer = 0;
|
|
buffer = (char *) ::mmap(0, size, PROT_READ, MAP_SHARED, FILENO (fp), 0);
|
|
fclose (fp);
|
|
if (!buffer || buffer == (char*) -1)
|
|
return;
|
|
this->operator () (buffer, buffer + size, __result);
|
|
::munmap(buffer, size);
|
|
#else
|
|
std::string buffer;
|
|
while (!feof(fp)) {
|
|
char tmp[1024];
|
|
int read = (int) fread (tmp, sizeof(char), 1023, fp);
|
|
tmp[read] = '\0';
|
|
buffer += tmp;
|
|
}
|
|
fclose (fp);
|
|
this->operator () (buffer.c_str(), buffer.c_str() + buffer.size(), __result);
|
|
#endif
|
|
}
|
|
|
|
template <typename _InputIterator>
|
|
bool pp::find_header_protection (_InputIterator __first, _InputIterator __last, std::string *__prot)
|
|
{
|
|
int was = env.current_line;
|
|
|
|
while (__first != __last)
|
|
{
|
|
if (pp_isspace (*__first))
|
|
{
|
|
if (*__first == '\n')
|
|
++env.current_line;
|
|
|
|
++__first;
|
|
}
|
|
else if (_PP_internal::comment_p (__first, __last))
|
|
{
|
|
__first = skip_comment_or_divop (__first, __last);
|
|
env.current_line += skip_comment_or_divop.lines;
|
|
}
|
|
else if (*__first == '#')
|
|
{
|
|
__first = skip_blanks (++__first, __last);
|
|
env.current_line += skip_blanks.lines;
|
|
|
|
if (__first != __last && *__first == 'i')
|
|
{
|
|
_InputIterator __begin = __first;
|
|
__first = skip_identifier (__begin, __last);
|
|
env.current_line += skip_identifier.lines;
|
|
|
|
std::string __directive (__begin, __first);
|
|
|
|
if (__directive == "ifndef")
|
|
{
|
|
__first = skip_blanks (__first, __last);
|
|
env.current_line += skip_blanks.lines;
|
|
|
|
__begin = __first;
|
|
__first = skip_identifier (__first, __last);
|
|
env.current_line += skip_identifier.lines;
|
|
|
|
if (__begin != __first && __first != __last)
|
|
{
|
|
__prot->assign (__begin, __first);
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
else
|
|
break;
|
|
}
|
|
|
|
env.current_line = was;
|
|
return false;
|
|
}
|
|
|
|
inline pp::PP_DIRECTIVE_TYPE pp::find_directive (char const *__directive, std::size_t __size) const
|
|
{
|
|
switch (__size)
|
|
{
|
|
case 2:
|
|
if (__directive[0] == 'i'
|
|
&& __directive[1] == 'f')
|
|
return PP_IF;
|
|
break;
|
|
|
|
case 4:
|
|
if (__directive[0] == 'e' && !strcmp (__directive, "elif"))
|
|
return PP_ELIF;
|
|
else if (__directive[0] == 'e' && !strcmp (__directive, "else"))
|
|
return PP_ELSE;
|
|
break;
|
|
|
|
case 5:
|
|
if (__directive[0] == 'i' && !strcmp (__directive, "ifdef"))
|
|
return PP_IFDEF;
|
|
else if (__directive[0] == 'u' && !strcmp (__directive, "undef"))
|
|
return PP_UNDEF;
|
|
else if (__directive[0] == 'e') {
|
|
if (!strcmp (__directive, "endif"))
|
|
return PP_ENDIF;
|
|
else if (!strcmp (__directive, "error"))
|
|
return PP_ERROR;
|
|
}
|
|
break;
|
|
|
|
case 6:
|
|
if (__directive[0] == 'i' && !strcmp (__directive, "ifndef"))
|
|
return PP_IFNDEF;
|
|
else if (__directive[0] == 'd' && !strcmp (__directive, "define"))
|
|
return PP_DEFINE;
|
|
else if (__directive[0] == 'p' && !strcmp (__directive, "pragma"))
|
|
return PP_PRAGMA;
|
|
break;
|
|
|
|
case 7:
|
|
if (__directive[0] == 'i' && !strcmp (__directive, "include"))
|
|
return PP_INCLUDE;
|
|
else if (__directive[0] == 'w' && !strcmp(__directive, "warning"))
|
|
return PP_WARNING;
|
|
break;
|
|
|
|
case 12:
|
|
if (__directive[0] == 'i' && !strcmp (__directive, "include_next"))
|
|
return PP_INCLUDE_NEXT;
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
std::cerr << "** WARNING unknown directive '#" << __directive << "' at " << env.current_file << ":" << env.current_line << std::endl;
|
|
return PP_UNKNOWN_DIRECTIVE;
|
|
}
|
|
|
|
inline bool pp::file_isdir (std::string const &__filename) const
|
|
{
|
|
struct stat __st;
|
|
#if defined(PP_OS_WIN)
|
|
if (stat(__filename.c_str (), &__st) == 0)
|
|
return (__st.st_mode & _S_IFDIR) == _S_IFDIR;
|
|
else
|
|
return false;
|
|
#else
|
|
if (lstat (__filename.c_str (), &__st) == 0)
|
|
return (__st.st_mode & S_IFDIR) == S_IFDIR;
|
|
else
|
|
return false;
|
|
#endif
|
|
}
|
|
|
|
inline bool pp::file_exists (std::string const &__filename) const
|
|
{
|
|
struct stat __st;
|
|
#if defined(PP_OS_WIN)
|
|
return stat(__filename.c_str (), &__st) == 0;
|
|
#else
|
|
return lstat (__filename.c_str (), &__st) == 0;
|
|
#endif
|
|
}
|
|
|
|
inline FILE *pp::find_include_file(std::string const &__input_filename, std::string *__filepath,
|
|
INCLUDE_POLICY __include_policy, bool __skip_current_path) const
|
|
{
|
|
assert (__filepath != 0);
|
|
assert (! __input_filename.empty());
|
|
|
|
__filepath->assign (__input_filename);
|
|
|
|
if (is_absolute (*__filepath))
|
|
return fopen (__filepath->c_str(), "r");
|
|
|
|
if (! env.current_file.empty ())
|
|
_PP_internal::extract_file_path (env.current_file, __filepath);
|
|
|
|
if (__include_policy == INCLUDE_LOCAL && ! __skip_current_path)
|
|
{
|
|
std::string __tmp (*__filepath);
|
|
__tmp += __input_filename;
|
|
|
|
if (file_exists (__tmp) && !file_isdir(__tmp))
|
|
{
|
|
__filepath->append (__input_filename);
|
|
return fopen (__filepath->c_str (), "r");
|
|
}
|
|
}
|
|
|
|
std::vector<std::string>::const_iterator it = include_paths.begin ();
|
|
|
|
if (__skip_current_path)
|
|
{
|
|
it = std::find (include_paths.begin (), include_paths.end (), *__filepath);
|
|
|
|
if (it != include_paths.end ())
|
|
++it;
|
|
|
|
else
|
|
it = include_paths.begin ();
|
|
}
|
|
|
|
for (; it != include_paths.end (); ++it)
|
|
{
|
|
if (__skip_current_path && it == include_paths.begin())
|
|
continue;
|
|
|
|
__filepath->assign (*it);
|
|
__filepath->append (__input_filename);
|
|
|
|
if (file_exists (*__filepath) && !file_isdir(*__filepath))
|
|
return fopen (__filepath->c_str(), "r");
|
|
|
|
#ifdef Q_OS_MAC
|
|
// try in Framework path on Mac, if there is a path in front
|
|
// ### what about escaped slashes?
|
|
size_t slashPos = __input_filename.find('/');
|
|
if (slashPos != std::string::npos) {
|
|
__filepath->assign (*it);
|
|
__filepath->append (__input_filename.substr(0, slashPos));
|
|
__filepath->append (".framework/Headers/");
|
|
__filepath->append (__input_filename.substr(slashPos+1, std::string::npos));
|
|
std::cerr << *__filepath << "\n";
|
|
|
|
if (file_exists (*__filepath) && !file_isdir(*__filepath))
|
|
return fopen (__filepath->c_str(), "r");
|
|
}
|
|
#endif // Q_OS_MAC
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
template <typename _InputIterator, typename _OutputIterator>
|
|
_InputIterator pp::handle_directive(char const *__directive, std::size_t __size,
|
|
_InputIterator __first, _InputIterator __last, _OutputIterator __result)
|
|
{
|
|
__first = skip_blanks (__first, __last);
|
|
|
|
PP_DIRECTIVE_TYPE d = find_directive (__directive, __size);
|
|
switch (d)
|
|
{
|
|
case PP_DEFINE:
|
|
if (! skipping ())
|
|
return handle_define (__first, __last);
|
|
break;
|
|
|
|
case PP_INCLUDE:
|
|
case PP_INCLUDE_NEXT:
|
|
if (! skipping ())
|
|
return handle_include (d == PP_INCLUDE_NEXT, __first, __last, __result);
|
|
break;
|
|
|
|
case PP_UNDEF:
|
|
if (! skipping ())
|
|
return handle_undef(__first, __last);
|
|
break;
|
|
|
|
case PP_ELIF:
|
|
return handle_elif (__first, __last);
|
|
|
|
case PP_ELSE:
|
|
return handle_else (__first, __last);
|
|
|
|
case PP_ENDIF:
|
|
return handle_endif (__first, __last);
|
|
|
|
case PP_IF:
|
|
return handle_if (__first, __last);
|
|
|
|
case PP_IFDEF:
|
|
return handle_ifdef (false, __first, __last);
|
|
|
|
case PP_IFNDEF:
|
|
return handle_ifdef (true, __first, __last);
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return __first;
|
|
}
|
|
|
|
template <typename _InputIterator, typename _OutputIterator>
|
|
_InputIterator pp::handle_include (bool __skip_current_path, _InputIterator __first, _InputIterator __last,
|
|
_OutputIterator __result)
|
|
{
|
|
std::cout << env.current_file << std::endl;
|
|
if (pp_isalpha (*__first) || *__first == '_')
|
|
{
|
|
pp_macro_expander expand_include (env);
|
|
std::string name;
|
|
name.reserve (255);
|
|
expand_include (__first, __last, std::back_inserter (name));
|
|
std::string::iterator it = skip_blanks (name.begin (), name.end ());
|
|
printf("%s", name.c_str());
|
|
assert((it != name.end () && (*it == '<' || *it == '"')));
|
|
handle_include (__skip_current_path, it, name.end (), __result);
|
|
return __first;
|
|
}
|
|
|
|
assert (*__first == '<' || *__first == '"');
|
|
int quote = (*__first == '"') ? '"' : '>';
|
|
++__first;
|
|
|
|
_InputIterator end_name = __first;
|
|
for (; end_name != __last; ++end_name)
|
|
{
|
|
assert (*end_name != '\n');
|
|
|
|
if (*end_name == quote)
|
|
break;
|
|
}
|
|
|
|
std::string filename (__first, end_name);
|
|
|
|
#ifdef PP_OS_WIN
|
|
std::replace(filename.begin(), filename.end(), '/', '\\');
|
|
#endif
|
|
|
|
std::string filepath;
|
|
FILE *fp = find_include_file (filename, &filepath, quote == '>' ? INCLUDE_GLOBAL : INCLUDE_LOCAL, __skip_current_path);
|
|
|
|
#if defined (PP_HOOK_ON_FILE_INCLUDED)
|
|
PP_HOOK_ON_FILE_INCLUDED (env.current_file, fp ? filepath : filename, fp);
|
|
#endif
|
|
|
|
if (fp != 0)
|
|
{
|
|
std::string old_file = env.current_file;
|
|
env.current_file = filepath;
|
|
int __saved_lines = env.current_line;
|
|
|
|
env.current_line = 1;
|
|
//output_line (env.current_file, 1, __result);
|
|
|
|
file (fp, __result);
|
|
|
|
// restore the file name and the line position
|
|
env.current_file = old_file;
|
|
env.current_line = __saved_lines;
|
|
|
|
// sync the buffer
|
|
_PP_internal::output_line (env.current_file, env.current_line, __result);
|
|
}
|
|
#ifndef RPP_JAMBI
|
|
// else
|
|
// std::cerr << "*** WARNING " << filename << ": No such file or directory" << std::endl;
|
|
#endif
|
|
|
|
return __first;
|
|
}
|
|
|
|
template <typename _InputIterator, typename _OutputIterator>
|
|
void pp::operator () (_InputIterator __first, _InputIterator __last, _OutputIterator __result)
|
|
{
|
|
#ifndef PP_NO_SMART_HEADER_PROTECTION
|
|
std::string __prot;
|
|
__prot.reserve (255);
|
|
pp_fast_string __tmp (__prot.c_str (), __prot.size ());
|
|
|
|
if (find_header_protection (__first, __last, &__prot)
|
|
&& env.resolve (&__tmp) != 0)
|
|
{
|
|
// std::cerr << "** DEBUG found header protection:" << __prot << std::endl;
|
|
return;
|
|
}
|
|
#endif
|
|
|
|
env.current_line = 1;
|
|
char __buffer[512];
|
|
|
|
while (true)
|
|
{
|
|
__first = skip_blanks (__first, __last);
|
|
env.current_line += skip_blanks.lines;
|
|
|
|
if (__first == __last)
|
|
break;
|
|
else if (*__first == '#')
|
|
{
|
|
assert (*__first == '#');
|
|
__first = skip_blanks (++__first, __last);
|
|
env.current_line += skip_blanks.lines;
|
|
|
|
_InputIterator end_id = skip_identifier (__first, __last);
|
|
env.current_line += skip_identifier.lines;
|
|
std::size_t __size = end_id - __first;
|
|
|
|
assert (__size < 512);
|
|
char *__cp = __buffer;
|
|
std::copy (__first, end_id, __cp);
|
|
__cp[__size] = '\0';
|
|
|
|
end_id = skip_blanks (end_id, __last);
|
|
__first = skip (end_id, __last);
|
|
|
|
int was = env.current_line;
|
|
(void) handle_directive (__buffer, __size, end_id, __first, __result);
|
|
|
|
if (env.current_line != was)
|
|
{
|
|
env.current_line = was;
|
|
_PP_internal::output_line (env.current_file, env.current_line, __result);
|
|
}
|
|
}
|
|
else if (*__first == '\n')
|
|
{
|
|
// ### compress the line
|
|
*__result++ = *__first++;
|
|
++env.current_line;
|
|
}
|
|
else if (skipping ())
|
|
__first = skip (__first, __last);
|
|
else
|
|
{
|
|
_PP_internal::output_line (env.current_file, env.current_line, __result);
|
|
__first = expand (__first, __last, __result);
|
|
env.current_line += expand.lines;
|
|
|
|
if (expand.generated_lines)
|
|
_PP_internal::output_line (env.current_file, env.current_line, __result);
|
|
}
|
|
}
|
|
}
|
|
|
|
inline pp::pp (pp_environment &__env):
|
|
env (__env), expand (env)
|
|
{
|
|
iflevel = 0;
|
|
_M_skipping[iflevel] = 0;
|
|
_M_true_test[iflevel] = 0;
|
|
}
|
|
|
|
inline std::back_insert_iterator<std::vector<std::string> > pp::include_paths_inserter ()
|
|
{ return std::back_inserter (include_paths); }
|
|
|
|
inline std::vector<std::string>::iterator pp::include_paths_begin ()
|
|
{ return include_paths.begin (); }
|
|
|
|
inline std::vector<std::string>::iterator pp::include_paths_end ()
|
|
{ return include_paths.end (); }
|
|
|
|
inline std::vector<std::string>::const_iterator pp::include_paths_begin () const
|
|
{ return include_paths.begin (); }
|
|
|
|
inline std::vector<std::string>::const_iterator pp::include_paths_end () const
|
|
{ return include_paths.end (); }
|
|
|
|
inline void pp::push_include_path (std::string const &__path)
|
|
{
|
|
if (__path.empty () || __path [__path.size () - 1] != PATH_SEPARATOR)
|
|
{
|
|
std::string __tmp (__path);
|
|
__tmp += PATH_SEPARATOR;
|
|
include_paths.push_back (__tmp);
|
|
}
|
|
|
|
else
|
|
include_paths.push_back (__path);
|
|
}
|
|
|
|
template <typename _InputIterator>
|
|
_InputIterator pp::handle_define (_InputIterator __first, _InputIterator __last)
|
|
{
|
|
pp_macro macro;
|
|
#if defined (PP_WITH_MACRO_POSITION)
|
|
macro.file = pp_symbol::get (env.current_file);
|
|
#endif
|
|
std::string definition;
|
|
|
|
__first = skip_blanks (__first, __last);
|
|
_InputIterator end_macro_name = skip_identifier (__first, __last);
|
|
pp_fast_string const *macro_name = pp_symbol::get (__first, end_macro_name);
|
|
__first = end_macro_name;
|
|
|
|
if (__first != __last && *__first == '(')
|
|
{
|
|
macro.function_like = true;
|
|
macro.formals.reserve (5);
|
|
|
|
__first = skip_blanks (++__first, __last); // skip '('
|
|
_InputIterator arg_end = skip_identifier (__first, __last);
|
|
if (__first != arg_end)
|
|
macro.formals.push_back (pp_symbol::get (__first, arg_end));
|
|
|
|
__first = skip_blanks (arg_end, __last);
|
|
|
|
if (*__first == '.')
|
|
{
|
|
macro.variadics = true;
|
|
while (*__first == '.')
|
|
++__first;
|
|
}
|
|
|
|
while (__first != __last && *__first == ',')
|
|
{
|
|
__first = skip_blanks (++__first, __last);
|
|
|
|
arg_end = skip_identifier (__first, __last);
|
|
if (__first != arg_end)
|
|
macro.formals.push_back (pp_symbol::get (__first, arg_end));
|
|
|
|
__first = skip_blanks (arg_end, __last);
|
|
|
|
if (*__first == '.')
|
|
{
|
|
macro.variadics = true;
|
|
while (*__first == '.')
|
|
++__first;
|
|
}
|
|
}
|
|
|
|
assert (*__first == ')');
|
|
++__first;
|
|
}
|
|
|
|
__first = skip_blanks (__first, __last);
|
|
|
|
while (__first != __last && *__first != '\n')
|
|
{
|
|
if (*__first == '/') {
|
|
__first = skip_comment_or_divop(__first, __last);
|
|
env.current_line += skip_comment_or_divop.lines;
|
|
}
|
|
|
|
if (*__first == '\\')
|
|
{
|
|
_InputIterator __begin = __first;
|
|
__begin = skip_blanks (++__begin, __last);
|
|
|
|
if (__begin != __last && *__begin == '\n')
|
|
{
|
|
++macro.lines;
|
|
__first = skip_blanks (++__begin, __last);
|
|
definition += ' ';
|
|
continue;
|
|
}
|
|
}
|
|
|
|
definition += *__first++;
|
|
}
|
|
|
|
macro.definition = pp_symbol::get (definition);
|
|
env.bind (macro_name, macro);
|
|
|
|
return __first;
|
|
}
|
|
|
|
template <typename _InputIterator>
|
|
_InputIterator pp::skip (_InputIterator __first, _InputIterator __last)
|
|
{
|
|
pp_skip_string_literal skip_string_literal;
|
|
pp_skip_char_literal skip_char_literal;
|
|
|
|
while (__first != __last && *__first != '\n')
|
|
{
|
|
if (*__first == '/')
|
|
{
|
|
__first = skip_comment_or_divop (__first, __last);
|
|
env.current_line += skip_comment_or_divop.lines;
|
|
}
|
|
else if (*__first == '"')
|
|
{
|
|
__first = skip_string_literal (__first, __last);
|
|
env.current_line += skip_string_literal.lines;
|
|
}
|
|
else if (*__first == '\'')
|
|
{
|
|
__first = skip_char_literal (__first, __last);
|
|
env.current_line += skip_char_literal.lines;
|
|
}
|
|
else if (*__first == '\\')
|
|
{
|
|
__first = skip_blanks (++__first, __last);
|
|
env.current_line += skip_blanks.lines;
|
|
|
|
if (__first != __last && *__first == '\n')
|
|
{
|
|
++__first;
|
|
++env.current_line;
|
|
}
|
|
}
|
|
else
|
|
++__first;
|
|
}
|
|
|
|
return __first;
|
|
}
|
|
|
|
inline bool pp::test_if_level()
|
|
{
|
|
bool result = !_M_skipping[iflevel++];
|
|
_M_skipping[iflevel] = _M_skipping[iflevel - 1];
|
|
_M_true_test[iflevel] = false;
|
|
return result;
|
|
}
|
|
|
|
inline int pp::skipping() const
|
|
{ return _M_skipping[iflevel]; }
|
|
|
|
template <typename _InputIterator>
|
|
_InputIterator pp::eval_primary(_InputIterator __first, _InputIterator __last, Value *result)
|
|
{
|
|
bool expect_paren = false;
|
|
int token;
|
|
__first = next_token (__first, __last, &token);
|
|
|
|
switch (token)
|
|
{
|
|
case TOKEN_NUMBER:
|
|
result->set_long (token_value);
|
|
break;
|
|
|
|
case TOKEN_UNUMBER:
|
|
result->set_ulong (token_uvalue);
|
|
break;
|
|
|
|
case TOKEN_DEFINED:
|
|
__first = next_token (__first, __last, &token);
|
|
|
|
if (token == '(')
|
|
{
|
|
expect_paren = true;
|
|
__first = next_token (__first, __last, &token);
|
|
}
|
|
|
|
if (token != TOKEN_IDENTIFIER)
|
|
{
|
|
std::cerr << "** WARNING expected ``identifier'' found:" << char(token) << std::endl;
|
|
result->set_long (0);
|
|
break;
|
|
}
|
|
|
|
result->set_long (env.resolve (token_text->c_str (), token_text->size ()) != 0);
|
|
|
|
next_token (__first, __last, &token); // skip '('
|
|
|
|
if (expect_paren)
|
|
{
|
|
_InputIterator next = next_token (__first, __last, &token);
|
|
if (token != ')')
|
|
std::cerr << "** WARNING expected ``)''" << std::endl;
|
|
else
|
|
__first = next;
|
|
}
|
|
break;
|
|
|
|
case TOKEN_IDENTIFIER:
|
|
result->set_long (0);
|
|
break;
|
|
|
|
case '-':
|
|
__first = eval_primary (__first, __last, result);
|
|
result->set_long (- result->l);
|
|
return __first;
|
|
|
|
case '+':
|
|
__first = eval_primary (__first, __last, result);
|
|
return __first;
|
|
|
|
case '!':
|
|
__first = eval_primary (__first, __last, result);
|
|
result->set_long (result->is_zero ());
|
|
return __first;
|
|
|
|
case '(':
|
|
__first = eval_constant_expression(__first, __last, result);
|
|
next_token (__first, __last, &token);
|
|
|
|
if (token != ')')
|
|
std::cerr << "** WARNING expected ``)'' = " << token << std::endl;
|
|
else
|
|
__first = next_token(__first, __last, &token);
|
|
break;
|
|
|
|
default:
|
|
result->set_long (0);
|
|
}
|
|
|
|
return __first;
|
|
}
|
|
|
|
template <typename _InputIterator>
|
|
_InputIterator pp::eval_multiplicative(_InputIterator __first, _InputIterator __last, Value *result)
|
|
{
|
|
__first = eval_primary(__first, __last, result);
|
|
|
|
int token;
|
|
_InputIterator next = next_token (__first, __last, &token);
|
|
|
|
while (token == '*' || token == '/' || token == '%')
|
|
{
|
|
Value value;
|
|
__first = eval_primary(next, __last, &value);
|
|
|
|
if (token == '*')
|
|
result->op_mult (value);
|
|
else if (token == '/')
|
|
{
|
|
if (value.is_zero ())
|
|
{
|
|
std::cerr << "** WARNING division by zero" << std::endl;
|
|
result->set_long (0);
|
|
}
|
|
else
|
|
result->op_div (value);
|
|
}
|
|
else
|
|
{
|
|
if (value.is_zero ())
|
|
{
|
|
std::cerr << "** WARNING division by zero" << std::endl;
|
|
result->set_long (0);
|
|
}
|
|
else
|
|
result->op_mod (value);
|
|
}
|
|
next = next_token (__first, __last, &token);
|
|
}
|
|
|
|
return __first;
|
|
}
|
|
|
|
template <typename _InputIterator>
|
|
_InputIterator pp::eval_additive(_InputIterator __first, _InputIterator __last, Value *result)
|
|
{
|
|
__first = eval_multiplicative(__first, __last, result);
|
|
|
|
int token;
|
|
_InputIterator next = next_token (__first, __last, &token);
|
|
|
|
while (token == '+' || token == '-')
|
|
{
|
|
Value value;
|
|
__first = eval_multiplicative(next, __last, &value);
|
|
|
|
if (token == '+')
|
|
result->op_add (value);
|
|
else
|
|
result->op_sub (value);
|
|
next = next_token (__first, __last, &token);
|
|
}
|
|
|
|
return __first;
|
|
}
|
|
|
|
template <typename _InputIterator>
|
|
_InputIterator pp::eval_shift(_InputIterator __first, _InputIterator __last, Value *result)
|
|
{
|
|
__first = eval_additive(__first, __last, result);
|
|
|
|
int token;
|
|
_InputIterator next = next_token (__first, __last, &token);
|
|
|
|
while (token == TOKEN_LT_LT || token == TOKEN_GT_GT)
|
|
{
|
|
Value value;
|
|
__first = eval_additive (next, __last, &value);
|
|
|
|
if (token == TOKEN_LT_LT)
|
|
result->op_lhs (value);
|
|
else
|
|
result->op_rhs (value);
|
|
next = next_token (__first, __last, &token);
|
|
}
|
|
|
|
return __first;
|
|
}
|
|
|
|
template <typename _InputIterator>
|
|
_InputIterator pp::eval_relational(_InputIterator __first, _InputIterator __last, Value *result)
|
|
{
|
|
__first = eval_shift(__first, __last, result);
|
|
|
|
int token;
|
|
_InputIterator next = next_token (__first, __last, &token);
|
|
|
|
while (token == '<'
|
|
|| token == '>'
|
|
|| token == TOKEN_LT_EQ
|
|
|| token == TOKEN_GT_EQ)
|
|
{
|
|
Value value;
|
|
__first = eval_shift(next, __last, &value);
|
|
|
|
switch (token)
|
|
{
|
|
default:
|
|
assert (0);
|
|
break;
|
|
|
|
case '<':
|
|
result->op_lt (value);
|
|
break;
|
|
|
|
case '>':
|
|
result->op_gt (value);
|
|
break;
|
|
|
|
case TOKEN_LT_EQ:
|
|
result->op_le (value);
|
|
break;
|
|
|
|
case TOKEN_GT_EQ:
|
|
result->op_ge (value);
|
|
break;
|
|
}
|
|
next = next_token (__first, __last, &token);
|
|
}
|
|
|
|
return __first;
|
|
}
|
|
|
|
template <typename _InputIterator>
|
|
_InputIterator pp::eval_equality(_InputIterator __first, _InputIterator __last, Value *result)
|
|
{
|
|
__first = eval_relational(__first, __last, result);
|
|
|
|
int token;
|
|
_InputIterator next = next_token (__first, __last, &token);
|
|
|
|
while (token == TOKEN_EQ_EQ || token == TOKEN_NOT_EQ)
|
|
{
|
|
Value value;
|
|
__first = eval_relational(next, __last, &value);
|
|
|
|
if (token == TOKEN_EQ_EQ)
|
|
result->op_eq (value);
|
|
else
|
|
result->op_ne (value);
|
|
next = next_token (__first, __last, &token);
|
|
}
|
|
|
|
return __first;
|
|
}
|
|
|
|
template <typename _InputIterator>
|
|
_InputIterator pp::eval_and(_InputIterator __first, _InputIterator __last, Value *result)
|
|
{
|
|
__first = eval_equality(__first, __last, result);
|
|
|
|
int token;
|
|
_InputIterator next = next_token (__first, __last, &token);
|
|
|
|
while (token == '&')
|
|
{
|
|
Value value;
|
|
__first = eval_equality(next, __last, &value);
|
|
result->op_bit_and (value);
|
|
next = next_token (__first, __last, &token);
|
|
}
|
|
|
|
return __first;
|
|
}
|
|
|
|
template <typename _InputIterator>
|
|
_InputIterator pp::eval_xor(_InputIterator __first, _InputIterator __last, Value *result)
|
|
{
|
|
__first = eval_and(__first, __last, result);
|
|
|
|
int token;
|
|
_InputIterator next = next_token (__first, __last, &token);
|
|
|
|
while (token == '^')
|
|
{
|
|
Value value;
|
|
__first = eval_and(next, __last, &value);
|
|
result->op_bit_xor (value);
|
|
next = next_token (__first, __last, &token);
|
|
}
|
|
|
|
return __first;
|
|
}
|
|
|
|
template <typename _InputIterator>
|
|
_InputIterator pp::eval_or(_InputIterator __first, _InputIterator __last, Value *result)
|
|
{
|
|
__first = eval_xor(__first, __last, result);
|
|
|
|
int token;
|
|
_InputIterator next = next_token (__first, __last, &token);
|
|
|
|
while (token == '|')
|
|
{
|
|
Value value;
|
|
__first = eval_xor(next, __last, &value);
|
|
result->op_bit_or (value);
|
|
next = next_token (__first, __last, &token);
|
|
}
|
|
|
|
return __first;
|
|
}
|
|
|
|
template <typename _InputIterator>
|
|
_InputIterator pp::eval_logical_and(_InputIterator __first, _InputIterator __last, Value *result)
|
|
{
|
|
__first = eval_or(__first, __last, result);
|
|
|
|
int token;
|
|
_InputIterator next = next_token (__first, __last, &token);
|
|
|
|
while (token == TOKEN_AND_AND)
|
|
{
|
|
Value value;
|
|
__first = eval_or(next, __last, &value);
|
|
result->op_and (value);
|
|
next = next_token (__first, __last, &token);
|
|
}
|
|
|
|
return __first;
|
|
}
|
|
|
|
template <typename _InputIterator>
|
|
_InputIterator pp::eval_logical_or(_InputIterator __first, _InputIterator __last, Value *result)
|
|
{
|
|
__first = eval_logical_and (__first, __last, result);
|
|
|
|
int token;
|
|
_InputIterator next = next_token (__first, __last, &token);
|
|
|
|
while (token == TOKEN_OR_OR)
|
|
{
|
|
Value value;
|
|
__first = eval_logical_and(next, __last, &value);
|
|
result->op_or (value);
|
|
next = next_token (__first, __last, &token);
|
|
}
|
|
|
|
return __first;
|
|
}
|
|
|
|
template <typename _InputIterator>
|
|
_InputIterator pp::eval_constant_expression(_InputIterator __first, _InputIterator __last, Value *result)
|
|
{
|
|
__first = eval_logical_or(__first, __last, result);
|
|
|
|
int token;
|
|
_InputIterator next = next_token (__first, __last, &token);
|
|
|
|
if (token == '?')
|
|
{
|
|
Value left_value;
|
|
__first = eval_constant_expression(next, __last, &left_value);
|
|
__first = skip_blanks (__first, __last);
|
|
|
|
__first = next_token(__first, __last, &token);
|
|
if (token == ':')
|
|
{
|
|
Value right_value;
|
|
__first = eval_constant_expression(__first, __last, &right_value);
|
|
|
|
*result = !result->is_zero () ? left_value : right_value;
|
|
}
|
|
else
|
|
{
|
|
std::cerr << "** WARNING expected ``:'' = " << int (token) << std::endl;
|
|
*result = left_value;
|
|
}
|
|
}
|
|
|
|
return __first;
|
|
}
|
|
|
|
template <typename _InputIterator>
|
|
_InputIterator pp::eval_expression (_InputIterator __first, _InputIterator __last, Value *result)
|
|
{
|
|
return __first = eval_constant_expression (skip_blanks (__first, __last), __last, result);
|
|
}
|
|
|
|
template <typename _InputIterator>
|
|
_InputIterator pp::handle_if (_InputIterator __first, _InputIterator __last)
|
|
{
|
|
if (test_if_level())
|
|
{
|
|
pp_macro_expander expand_condition (env);
|
|
std::string condition;
|
|
condition.reserve (255);
|
|
expand_condition (skip_blanks (__first, __last), __last, std::back_inserter (condition));
|
|
|
|
Value result;
|
|
result.set_long (0);
|
|
eval_expression(condition.c_str (), condition.c_str () + condition.size (), &result);
|
|
|
|
_M_true_test[iflevel] = !result.is_zero ();
|
|
_M_skipping[iflevel] = result.is_zero ();
|
|
}
|
|
|
|
return __first;
|
|
}
|
|
|
|
template <typename _InputIterator>
|
|
_InputIterator pp::handle_else (_InputIterator __first, _InputIterator /*__last*/)
|
|
{
|
|
if (iflevel == 0 && !skipping ())
|
|
{
|
|
std::cerr << "** WARNING #else without #if" << std::endl;
|
|
}
|
|
else if (iflevel > 0 && _M_skipping[iflevel - 1])
|
|
{
|
|
_M_skipping[iflevel] = true;
|
|
}
|
|
else
|
|
{
|
|
_M_skipping[iflevel] = _M_true_test[iflevel];
|
|
}
|
|
|
|
return __first;
|
|
}
|
|
|
|
template <typename _InputIterator>
|
|
_InputIterator pp::handle_elif (_InputIterator __first, _InputIterator __last)
|
|
{
|
|
assert(iflevel > 0);
|
|
|
|
if (iflevel == 0 && !skipping())
|
|
{
|
|
std::cerr << "** WARNING #else without #if" << std::endl;
|
|
}
|
|
else if (!_M_true_test[iflevel] && !_M_skipping[iflevel - 1])
|
|
{
|
|
Value result;
|
|
__first = eval_expression(__first, __last, &result);
|
|
_M_true_test[iflevel] = !result.is_zero ();
|
|
_M_skipping[iflevel] = result.is_zero ();
|
|
}
|
|
else
|
|
{
|
|
_M_skipping[iflevel] = true;
|
|
}
|
|
|
|
return __first;
|
|
}
|
|
|
|
template <typename _InputIterator>
|
|
_InputIterator pp::handle_endif (_InputIterator __first, _InputIterator /*__last*/)
|
|
{
|
|
if (iflevel == 0 && !skipping())
|
|
{
|
|
std::cerr << "** WARNING #endif without #if" << std::endl;
|
|
}
|
|
else
|
|
{
|
|
_M_skipping[iflevel] = 0;
|
|
_M_true_test[iflevel] = 0;
|
|
|
|
--iflevel;
|
|
}
|
|
|
|
return __first;
|
|
}
|
|
|
|
template <typename _InputIterator>
|
|
_InputIterator pp::handle_ifdef (bool check_undefined, _InputIterator __first, _InputIterator __last)
|
|
{
|
|
if (test_if_level())
|
|
{
|
|
_InputIterator end_macro_name = skip_identifier (__first, __last);
|
|
|
|
std::size_t __size;
|
|
#if defined(__SUNPRO_CC)
|
|
std::distance (__first, end_macro_name, __size);
|
|
#else
|
|
__size = std::distance (__first, end_macro_name);
|
|
#endif
|
|
assert (__size < 256);
|
|
|
|
char __buffer [256];
|
|
std::copy (__first, end_macro_name, __buffer);
|
|
|
|
bool value = env.resolve (__buffer, __size) != 0;
|
|
|
|
__first = end_macro_name;
|
|
|
|
if (check_undefined)
|
|
value = !value;
|
|
|
|
_M_true_test[iflevel] = value;
|
|
_M_skipping[iflevel] = !value;
|
|
}
|
|
|
|
return __first;
|
|
}
|
|
|
|
template <typename _InputIterator>
|
|
_InputIterator pp::handle_undef(_InputIterator __first, _InputIterator __last)
|
|
{
|
|
__first = skip_blanks (__first, __last);
|
|
_InputIterator end_macro_name = skip_identifier (__first, __last);
|
|
assert (end_macro_name != __first);
|
|
|
|
std::size_t __size;
|
|
#if defined(__SUNPRO_CC)
|
|
std::distance (__first, end_macro_name, __size);
|
|
#else
|
|
__size = std::distance (__first, end_macro_name);
|
|
#endif
|
|
|
|
assert (__size < 256);
|
|
|
|
char __buffer [256];
|
|
std::copy (__first, end_macro_name, __buffer);
|
|
|
|
pp_fast_string const __tmp (__buffer, __size);
|
|
env.unbind (&__tmp);
|
|
|
|
__first = end_macro_name;
|
|
|
|
return __first;
|
|
}
|
|
|
|
template <typename _InputIterator>
|
|
char pp::peek_char (_InputIterator __first, _InputIterator __last)
|
|
{
|
|
if (__first == __last)
|
|
return 0;
|
|
|
|
return *++__first;
|
|
}
|
|
|
|
template <typename _InputIterator>
|
|
_InputIterator pp::next_token (_InputIterator __first, _InputIterator __last, int *kind)
|
|
{
|
|
__first = skip_blanks (__first, __last);
|
|
|
|
if (__first == __last)
|
|
{
|
|
*kind = 0;
|
|
return __first;
|
|
}
|
|
|
|
char ch = *__first;
|
|
char ch2 = peek_char (__first, __last);
|
|
|
|
switch (ch)
|
|
{
|
|
case '/':
|
|
if (ch2 == '/' || ch2 == '*')
|
|
{
|
|
__first = skip_comment_or_divop (__first, __last);
|
|
return next_token (__first, __last, kind);
|
|
}
|
|
++__first;
|
|
*kind = '/';
|
|
break;
|
|
|
|
case '<':
|
|
++__first;
|
|
if (ch2 == '<')
|
|
{
|
|
++__first;
|
|
*kind = TOKEN_LT_LT;
|
|
}
|
|
else if (ch2 == '=')
|
|
{
|
|
++__first;
|
|
*kind = TOKEN_LT_EQ;
|
|
}
|
|
else
|
|
*kind = '<';
|
|
|
|
return __first;
|
|
|
|
case '>':
|
|
++__first;
|
|
if (ch2 == '>')
|
|
{
|
|
++__first;
|
|
*kind = TOKEN_GT_GT;
|
|
}
|
|
else if (ch2 == '=')
|
|
{
|
|
++__first;
|
|
*kind = TOKEN_GT_EQ;
|
|
}
|
|
else
|
|
*kind = '>';
|
|
|
|
return __first;
|
|
|
|
case '!':
|
|
++__first;
|
|
if (ch2 == '=')
|
|
{
|
|
++__first;
|
|
*kind = TOKEN_NOT_EQ;
|
|
}
|
|
else
|
|
*kind = '!';
|
|
|
|
return __first;
|
|
|
|
case '=':
|
|
++__first;
|
|
if (ch2 == '=')
|
|
{
|
|
++__first;
|
|
*kind = TOKEN_EQ_EQ;
|
|
}
|
|
else
|
|
*kind = '=';
|
|
|
|
return __first;
|
|
|
|
case '|':
|
|
++__first;
|
|
if (ch2 == '|')
|
|
{
|
|
++__first;
|
|
*kind = TOKEN_OR_OR;
|
|
}
|
|
else
|
|
*kind = '|';
|
|
|
|
return __first;
|
|
|
|
case '&':
|
|
++__first;
|
|
if (ch2 == '&')
|
|
{
|
|
++__first;
|
|
*kind = TOKEN_AND_AND;
|
|
}
|
|
else
|
|
*kind = '&';
|
|
|
|
return __first;
|
|
|
|
default:
|
|
if (pp_isalpha (ch) || ch == '_')
|
|
{
|
|
_InputIterator end = skip_identifier (__first, __last);
|
|
_M_current_text.assign (__first, end);
|
|
|
|
token_text = &_M_current_text;
|
|
__first = end;
|
|
|
|
if (*token_text == "defined")
|
|
*kind = TOKEN_DEFINED;
|
|
else
|
|
*kind = TOKEN_IDENTIFIER;
|
|
}
|
|
else if (pp_isdigit (ch))
|
|
{
|
|
_InputIterator end = skip_number (__first, __last);
|
|
std::string __str (__first, __last);
|
|
char ch = __str [__str.size () - 1];
|
|
if (ch == 'u' || ch == 'U')
|
|
{
|
|
token_uvalue = strtoul (__str.c_str (), 0, 0);
|
|
*kind = TOKEN_UNUMBER;
|
|
}
|
|
else
|
|
{
|
|
token_value = strtol (__str.c_str (), 0, 0);
|
|
*kind = TOKEN_NUMBER;
|
|
}
|
|
__first = end;
|
|
}
|
|
else
|
|
*kind = *__first++;
|
|
}
|
|
|
|
return __first;
|
|
}
|
|
|
|
} // namespace rpp
|
|
|
|
#endif // PP_ENGINE_BITS_H
|
|
|
|
// kate: space-indent on; indent-width 2; replace-tabs on;
|