Alien-SmokeQt

 view release on metacpan or  search on metacpan

generator/parser/rpp/pp-engine.cpp  view on Meta::CPAN

/*
  Copyright 2005 Roberto Raggi <roberto@kdevelop.org>
  Copyright 2006 Hamish Rodda <rodda@kde.org>

  Permission to use, copy, modify, distribute, and sell this software and its
  documentation for any purpose is hereby granted without fee, provided that
  the above copyright notice appear in all copies and that both that
  copyright notice and this permission notice appear in supporting
  documentation.

  The above copyright notice and this permission notice shall be included in
  all copies or substantial portions of the Software.

  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
  KDEVELOP TEAM BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
  AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
  CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/

#include "pp-engine.h"

#include <QFile>
#include <QTextStream>
#include <QtDebug>

#include "pp-internal.h"
#include "preprocessor.h"
#include "pp-environment.h"
#include "pp-location.h"
#include "chartools.h"
// #include "macrorepository.h"

using namespace rpp;

#define RETURN_ON_FAIL(x) if(!(x)) { ++input; qDebug() << "Preprocessor: Condition not satisfied"; return; }

pp::pp(Preprocessor* preprocessor)
  : m_environment(new Environment(this))
  , expand(this, 0, true)
  , m_preprocessor(preprocessor)
  , nextToken(0)
  , haveNextToken(false)
  , hideNext(false)
  , hadGuardCandidate(false)
  , checkGuardEnd(false)
{
  iflevel = 0;
  _M_skipping[iflevel] = 0;
  _M_true_test[iflevel] = 0;
}

pp::~pp()
{
  delete m_environment;
}

Preprocessor* pp::preprocessor() {
  return m_preprocessor;
}

PreprocessedContents pp::processFile(const QString& fileName)
{
    QFile file(fileName);
    if (!file.open(QIODevice::ReadOnly))
    {
        qWarning() << "file '" << fileName << "' not found!" ;
        return PreprocessedContents();
    }

    PreprocessedContents result;
    processFileInternal(fileName, file.readAll(), result);
    return result;
}

PreprocessedContents pp::processFile(const QString& fileName, const QByteArray& data)
{
    PreprocessedContents result;
    processFileInternal(fileName, data, result);
    return result;
}

void pp::processFileInternal(const QString& fileName, const QByteArray& fileContents, PreprocessedContents& result)
{
    m_files.push(IndexedString(fileName));
    // Guestimate as to how much expansion will occur
    result.reserve(int(fileContents.length() * 1.2));
    PreprocessedContents contents = convertFromByteArray(fileContents);
    {
      Stream is(&contents);
      Stream rs(&result, m_environment->locationTable());
      operator () (is, rs);
    }
    result.squeeze();
}

uint ifDirective = IndexedString("if").index();
uint elseDirective = IndexedString("else").index();
uint elifDirective = IndexedString("elif").index();
uint ifdefDirective = IndexedString("ifdef").index();
uint undefDirective = IndexedString("undef").index();
uint endifDirective = IndexedString("endif").index();
uint ifndefDirective = IndexedString("ifndef").index();
uint defineDirective = IndexedString("define").index();
uint includeDirective = IndexedString("include").index();
uint includeNextDirective = IndexedString("include_next").index();

void pp::handle_directive(uint directive, Stream& input, Stream& output)
{
  skip_blanks (input, output);
  if(!(directive == ifndefDirective)) {
    hadGuardCandidate = true; //Too late, the guard must be the first directive
  }
  if(checkGuardEnd) {
    guardCandidate = IndexedString();
    checkGuardEnd = false;
  }
  
  if(directive == defineDirective)
      if (! skipping ())
        return handle_define(input);

  if(directive == includeDirective || directive == includeNextDirective)
      if (! skipping ())
        return handle_include (directive == includeNextDirective, input, output);

  if(directive == undefDirective)
      if (! skipping ())
        return handle_undef(input);

  if(directive == elifDirective)
      return handle_elif(input);

  if(directive == elseDirective)
      return handle_else(input.inputPosition().line);

  if(directive == endifDirective)
      return handle_endif(input, output);

  if(directive == ifDirective)
      return handle_if(input);

  if(directive == ifdefDirective)
      return handle_ifdef(false, input);

  if(directive == ifndefDirective)
      return handle_ifdef(true, input);
}

void pp::handle_include(bool skip_current_path, Stream& input, Stream& output)
{
  if (isLetter(input.current()) || input == '_') {
    pp_macro_expander expand_include(this);

    Anchor inputPosition = input.inputPosition();
    SimpleCursor originalInputPosition = input.originalInputPosition();
    PreprocessedContents includeString;
    {
      Stream cs(&includeString);
      expand_include(input, cs);
    }

    skip_blanks(input, devnull());
    RETURN_ON_FAIL(!includeString.isEmpty() && (includeString.first() == indexFromCharacter('<') || includeString.first() == indexFromCharacter('"')));

    Stream newInput(&includeString, inputPosition);
    newInput.setOriginalInputPosition(originalInputPosition);
    handle_include(skip_current_path, newInput, output);
    return;
  }

  RETURN_ON_FAIL(input == '<' || input == '"');
  char quote((input == '"') ? '"' : '>');
  ++input;

  PreprocessedContents includeNameB;

  while (!input.atEnd() && input != quote) {
    RETURN_ON_FAIL(input != '\n');

    includeNameB.append(input);
    ++input;
  }

  QString includeName(QString::fromUtf8(stringFromContents(includeNameB)));

  Stream* include = m_preprocessor->sourceNeeded(includeName, quote == '"' ? Preprocessor::IncludeLocal : Preprocessor::IncludeGlobal, input.inputPosition().line, skip_current_path);

  if (include && !include->atEnd()) {
//     m_files.push(IndexedString(includeName));

//     output.mark(Anchor(0, 0));

    operator()(*include, output);

    // restore the file name and sync the buffer
//     output.mark(input.inputPosition());
  }

  delete include;
}

void pp::operator () (Stream& input, Stream& output)
{
  int previousIfLevel = iflevel;

  forever
  {
    haveNextToken = false;

    if (skipping()) {
      skip_blanks(input, devnull());

    } else {
      skip_blanks(input, output);
    }

    if (input.atEnd()) {
      break;

    } else if (input == '#') {
      skip_blanks(++input, devnull());

      uint directive = skip_identifier(input);

      skip_blanks(input, devnull());

      Anchor inputPosition = input.inputPosition();
      SimpleCursor originalInputPosition = input.originalInputPosition();

      PreprocessedContents skipped;
      {
        Stream ss(&skipped);
        skip (input, ss);
      }

      Stream ss(&skipped, inputPosition);
      ss.setOriginalInputPosition(originalInputPosition);
      handle_directive(directive, ss, output);

    } else if (input == '\n') {
      output << input;
      ++input;

    } else if (skipping ()) {
      skip (input, devnull());

    } else {
      output.mark(input.inputPosition());
      if(checkGuardEnd) {
        expand.startSignificantContentSearch();
      }
      
      expand (input, output);
      if(checkGuardEnd) {
        if(expand.foundSignificantContent() || !input.atEnd()) {
          guardCandidate = IndexedString();
        }
        checkGuardEnd = false;
      }
    }
  }
  
  if(!guardCandidate.isEmpty())
    preprocessor()->foundHeaderGuard(input, guardCandidate);

  if (iflevel != previousIfLevel && !input.skippedToEnd())
    createProblem(input, "Unterminated #if statement");
}

void pp::createProblem(Stream& input, const QString& description) {
    Problem* problem = new Problem;
    problem->file = currentFileNameString();
    problem->position = input.originalInputPosition();
    problem->description = description;
    problemEncountered(problem);
}

void pp::handle_define (Stream& input)
{
  pp_macro* macro = new pp_macro;
  macro->file = currentFileName();
  macro->sourceLine = input.originalInputPosition().line;

  skip_blanks (input, devnull());
  macro->name = IndexedString::fromIndex(skip_identifier(input)); //@todo make macros utf8 too

  if (!input.atEnd() && input == '(')
  {
    macro->function_like = true;

    skip_blanks (++input, devnull()); // skip '('
    uint formal = skip_identifier(input);
    if (formal)
      macro->formals.append( IndexedString::fromIndex(formal) );

    skip_blanks(input, devnull());

    if (input == '.') {
      macro->variadics = true;

      do {
        ++input;

      } while (input == '.');
    }

    while (!input.atEnd() && input == ',')
    {
      skip_blanks(++input, devnull());

      uint formal = skip_identifier(input);
      if (formal)
        macro->formals.append( IndexedString::fromIndex(formal) );

      skip_blanks (input, devnull());

generator/parser/rpp/pp-engine.cpp  view on Meta::CPAN

  Value result = eval_logical_and(input);

  int token = next_token(input);

  while (token == TOKEN_OR_OR) {
    accept_token();
    Value value = eval_logical_and(input);
    result = result || value;
    token = next_token(input);
  }

  return result;
}


Value pp::eval_constant_expression(Stream& input)
{
  Value result = eval_logical_or(input);

  int token = next_token(input);

  if (token == '?')
  {
    accept_token();
    Value left_value = eval_constant_expression(input);
    skip_blanks(input, devnull());

    token = next_token_accept(input);
    if (token == ':')
    {
      Value right_value = eval_constant_expression(input);

      result = !result.is_zero() ? left_value : right_value;
    }
    else
    {
      Problem* problem = new Problem;
      problem->file = currentFileNameString();
      problem->position = input.originalInputPosition();
      problem->description = QString("expected ``:'' = %1").arg(int(token));
      problemEncountered(problem);
      result = left_value;
    }
  }

  return result;
}


Value pp::eval_expression(Stream& input)
{
  skip_blanks(input, devnull());
  return eval_constant_expression(input);
}


void pp::handle_if (Stream& input)
{
  if (test_if_level())
  {
    pp_macro_expander expand_condition(this);
    skip_blanks(input, devnull());

    Anchor inputPosition = input.inputPosition();
    SimpleCursor originalInputPosition = input.originalInputPosition();
    PreprocessedContents condition;
    {
      Stream cs(&condition);
      expand_condition(input, cs);
    }

    environment()->enterBlock(input.inputPosition().line, condition);

    Stream cs(&condition, inputPosition);
    cs.setOriginalInputPosition(originalInputPosition);
    Value result = eval_expression(cs);

    _M_true_test[iflevel] = !result.is_zero();
    _M_skipping[iflevel] = result.is_zero();

  } else {
    // Capture info for precompiled macros
    pp_macro_expander expand_condition(this);
    skip_blanks(input, devnull());
    PreprocessedContents condition;
    {
      Stream cs(&condition);
      expand_condition(input, cs);
    }

    environment()->enterBlock(input.inputPosition().line, condition);

    _M_true_test[iflevel] = true;
    _M_skipping[iflevel] = true;
  }
}


void pp::handle_else(int sourceLine)
{
  if(iflevel == 1)
    guardCandidate = IndexedString();

  if (iflevel == 0 && !skipping ())
  {
    Problem* problem = new Problem;
    problem->file = currentFileNameString();
    problem->position = SimpleCursor(sourceLine, 0);
    problem->description = "#else without #if";
    problemEncountered(problem);
  }
  else if (iflevel > 0 && _M_skipping[iflevel - 1])
  {
    _M_skipping[iflevel] = true;
    environment()->elseBlock(sourceLine);
  }
  else
  {
    _M_skipping[iflevel] = _M_true_test[iflevel];
    environment()->elseBlock(sourceLine);
  }
}


void pp::handle_elif(Stream& input)
{
  if(iflevel == 1)
    guardCandidate = IndexedString();
  
  RETURN_ON_FAIL(iflevel > 0);

  if (iflevel == 0 && !skipping())
  {
    Problem* problem = new Problem;
    problem->file = currentFileNameString();
    problem->position = input.originalInputPosition();
    problem->description = "#else without #if";
    problemEncountered(problem);
  }
  else
  {
    pp_macro_expander expand_condition(this);
    skip_blanks(input, devnull());

    Anchor inputPosition = input.inputPosition();
    SimpleCursor originalInputPosition = input.originalInputPosition();
    PreprocessedContents condition;
    {
      Stream cs(&condition);
      cs.setOriginalInputPosition(originalInputPosition);
      expand_condition(input, cs);
    }

    environment()->elseBlock(input.inputPosition().line, condition);

    if (!_M_true_test[iflevel] && !_M_skipping[iflevel - 1])
    {
      Stream cs(&condition, inputPosition);
      Value result = eval_expression(cs);
      _M_true_test[iflevel] = !result.is_zero();
      _M_skipping[iflevel] = result.is_zero();
    }
    else
    {
      _M_skipping[iflevel] = true;
    }
  }
}


void pp::handle_endif(Stream& input, Stream& output)
{
  if (iflevel == 0 && !skipping())
  {
    Problem* problem = new Problem;
    problem->file = currentFileNameString();
    problem->position = input.originalInputPosition();
    problem->description = QString("#endif without #if at output line %1")
                                   .arg(m_environment->locationTable()->anchorForOffset(output.offset()).anchor.line);
    problemEncountered(problem);
  }
  else
  {
    environment()->leaveBlock();

    _M_skipping[iflevel] = 0;
    _M_true_test[iflevel] = 0;

    --iflevel;
    if(iflevel == 0) {
      if(!guardCandidate.isEmpty()) {
        checkGuardEnd = true;
      }
    }
  }
}

uint pp::branchingHash() const
{
  uint hash = 0;
  for( int a = 0; a <= iflevel; a++ ) {
    hash *= 19;
    if( _M_skipping[a] )
      hash += 3;
    if( _M_true_test[a] )
      hash += 7;
  }
  return hash;
}

void pp::handle_ifdef (bool check_undefined, Stream& input)
{
  IndexedString macro_name = IndexedString::fromIndex(skip_identifier(input));
///@todo eventually fix the block description
  if(check_undefined && expand.in_header_section() && guardCandidate.isEmpty() && !hadGuardCandidate && iflevel == 0) {
    //It's the first #ifndef and the header-section hasn't ended yet, assume it to be the header-guard
    guardCandidate = macro_name;
  }

  hadGuardCandidate = true;
  
  environment()->enterBlock(input.inputPosition().line);//, QString("%1defined(%2)").arg(check_undefined ? "!" : "").arg(macro_name).toUtf8());

  if (test_if_level())
  {
    pp_macro* macro = m_environment->retrieveMacro(macro_name, true);
    bool value = false;
    if( macro && macro->defined )
      value = true;

    if (check_undefined)
      value = !value;

    _M_true_test[iflevel] = value;
    _M_skipping[iflevel] = !value;
  }
}


void pp::handle_undef(Stream& input)
{
  skip_blanks (input, devnull());

  IndexedString macro_name = IndexedString::fromIndex(skip_identifier(input));
  RETURN_ON_FAIL(!macro_name.isEmpty());

  pp_macro* macro = new pp_macro;
  macro->file = currentFileName();
  macro->name = macro_name;
  macro->sourceLine = input.originalInputPosition().line;

  macro->defined = false;

  m_environment->setMacro(macro);
}

IndexedString definedText("defined");

int pp::next_token (Stream& input)
{
  if (haveNextToken)
    return nextToken;

  skip_blanks(input, devnull());

  if (input.atEnd())
  {
    return 0;
  }

  char ch = 0;
  if(isCharacter(input.current()))
    ch = characterFromIndex(input.current());
  char ch2 = input.peekNextCharacter();



( run in 0.597 second using v1.01-cache-2.11-cpan-5b529ec07f3 )