<commit_before>// Copyright 2015 Google Inc. All rights reserved
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

// +build ignore

#include "eval.h"

#include <errno.h>
#include <string.h>

#include "expr.h"
#include "file.h"
#include "file_cache.h"
#include "fileutil.h"
#include "parser.h"
#include "rule.h"
#include "stmt.h"
#include "strutil.h"
#include "symtab.h"
#include "var.h"

EvalResult::~EvalResult() {
  for (auto p : rule_vars)
    delete p.second;
  delete vars;
}

Evaluator::Evaluator(const Vars* vars)
    : in_vars_(vars),
      vars_(new Vars()),
      last_rule_(NULL),
      current_scope_(NULL),
      avoid_io_(false) {
}

Evaluator::~Evaluator() {
  // delete vars_;
  // for (auto p : rule_vars) {
  //   delete p.second;
  // }
}

Var* Evaluator::EvalRHS(Symbol lhs, Value* rhs_v, StringPiece orig_rhs,
                        AssignOp op, bool is_override) {
  VarOrigin origin = (
      (is_bootstrap_ ? VarOrigin::DEFAULT :
       is_override ? VarOrigin::OVERRIDE : VarOrigin::FILE));

  Var* rhs = NULL;
  bool needs_assign = true;
  switch (op) {
    case AssignOp::COLON_EQ:
      rhs = new SimpleVar(rhs_v->Eval(this), origin);
      break;
    case AssignOp::EQ:
      rhs = new RecursiveVar(rhs_v, origin, orig_rhs);
      break;
    case AssignOp::PLUS_EQ: {
      Var* prev = LookupVarInCurrentScope(lhs);
      if (!prev->IsDefined()) {
        rhs = new RecursiveVar(rhs_v, origin, orig_rhs);
      } else {
        prev->AppendVar(this, rhs_v);
        rhs = prev;
        needs_assign = false;
      }
      break;
    }
    case AssignOp::QUESTION_EQ: {
      Var* prev = LookupVarInCurrentScope(lhs);
      if (!prev->IsDefined()) {
        rhs = new RecursiveVar(rhs_v, origin, orig_rhs);
      } else {
        rhs = prev;
        needs_assign = false;
      }
      break;
    }
  }

  LOG("Assign: %s=%s", lhs.c_str(), rhs->DebugString().c_str());
  if (needs_assign) {
    return rhs;
  }
  return NULL;
}

void Evaluator::EvalAssign(const AssignStmt* stmt) {
  loc_ = stmt->loc();
  last_rule_ = NULL;
  Symbol lhs = Intern(stmt->lhs->Eval(this));
  if (lhs.empty())
    Error("*** empty variable name.");
  Var* rhs = EvalRHS(lhs, stmt->rhs, stmt->orig_rhs, stmt->op,
                     stmt->directive == AssignDirective::OVERRIDE);
  if (rhs)
    vars_->Assign(lhs, rhs);
}

void Evaluator::EvalRule(const RuleStmt* stmt) {
  loc_ = stmt->loc();
  last_rule_ = NULL;

  const string&& expr = stmt->expr->Eval(this);
  // See semicolon.mk.
  if (expr.find_first_not_of(" \t\n;") == string::npos)
    return;

  Rule* rule;
  RuleVarAssignment rule_var;
  ParseRule(loc_, expr, stmt->term, &rule, &rule_var);

  if (rule) {
    if (stmt->term == ';') {
      rule->cmds.push_back(stmt->after_term);
    }

    LOG("Rule: %s", rule->DebugString().c_str());
    rules_.push_back(shared_ptr<Rule>(rule));
    last_rule_ = rule;
    return;
  }

  for (Symbol output : rule_var.outputs) {
    auto p = rule_vars_.emplace(output, nullptr);
    if (p.second) {
      p.first->second = new Vars;
    }

    Value* rhs = stmt->after_term;
    if (!rule_var.rhs.empty()) {
      Value* lit = NewLiteral(rule_var.rhs);
      if (rhs) {
        // TODO: We always insert two whitespaces around the
        // terminator. Preserve whitespaces properly.
        if (stmt->term == ';') {
          rhs = NewExpr3(lit, NewLiteral(StringPiece(" ; ")), rhs);
        } else {
          rhs = NewExpr3(lit, NewLiteral(StringPiece(" = ")), rhs);
        }
      } else {
        rhs = lit;
      }
    }

    current_scope_ = p.first->second;
    Symbol lhs = Intern(rule_var.lhs);
    Var* rhs_var = EvalRHS(lhs, rhs, StringPiece("*TODO*"), rule_var.op);
    if (rhs_var)
      current_scope_->Assign(lhs, new RuleVar(rhs_var, rule_var.op));
    current_scope_ = NULL;
  }
}

void Evaluator::EvalCommand(const CommandStmt* stmt) {
  loc_ = stmt->loc();

  if (!last_rule_) {
    vector<Stmt*> stmts;
    ParseNotAfterRule(stmt->orig, stmt->loc(), &stmts);
    for (Stmt* a : stmts)
      a->Eval(this);
    return;
  }

  last_rule_->cmds.push_back(stmt->expr);
  if (last_rule_->cmd_lineno == 0)
    last_rule_->cmd_lineno = stmt->loc().lineno;
  LOG("Command: %s", stmt->expr->DebugString().c_str());
}

void Evaluator::EvalIf(const IfStmt* stmt) {
  loc_ = stmt->loc();

  bool is_true;
  switch (stmt->op) {
    case CondOp::IFDEF:
    case CondOp::IFNDEF: {
      string var_name;
      stmt->lhs->Eval(this, &var_name);
      if (var_name.find_first_of(" \t") != string::npos)
        Error("*** invalid syntax in conditional.");
      Symbol lhs = Intern(var_name);
      Var* v = LookupVarInCurrentScope(lhs);
      const string&& s = v->Eval(this);<commit_msg>[C++] Let ifdef allow trailing spaces in a variable name<commit_after>// Copyright 2015 Google Inc. All rights reserved
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

// +build ignore

#include "eval.h"

#include <errno.h>
#include <string.h>

#include "expr.h"
#include "file.h"
#include "file_cache.h"
#include "fileutil.h"
#include "parser.h"
#include "rule.h"
#include "stmt.h"
#include "strutil.h"
#include "symtab.h"
#include "var.h"

EvalResult::~EvalResult() {
  for (auto p : rule_vars)
    delete p.second;
  delete vars;
}

Evaluator::Evaluator(const Vars* vars)
    : in_vars_(vars),
      vars_(new Vars()),
      last_rule_(NULL),
      current_scope_(NULL),
      avoid_io_(false) {
}

Evaluator::~Evaluator() {
  // delete vars_;
  // for (auto p : rule_vars) {
  //   delete p.second;
  // }
}

Var* Evaluator::EvalRHS(Symbol lhs, Value* rhs_v, StringPiece orig_rhs,
                        AssignOp op, bool is_override) {
  VarOrigin origin = (
      (is_bootstrap_ ? VarOrigin::DEFAULT :
       is_override ? VarOrigin::OVERRIDE : VarOrigin::FILE));

  Var* rhs = NULL;
  bool needs_assign = true;
  switch (op) {
    case AssignOp::COLON_EQ:
      rhs = new SimpleVar(rhs_v->Eval(this), origin);
      break;
    case AssignOp::EQ:
      rhs = new RecursiveVar(rhs_v, origin, orig_rhs);
      break;
    case AssignOp::PLUS_EQ: {
      Var* prev = LookupVarInCurrentScope(lhs);
      if (!prev->IsDefined()) {
        rhs = new RecursiveVar(rhs_v, origin, orig_rhs);
      } else {
        prev->AppendVar(this, rhs_v);
        rhs = prev;
        needs_assign = false;
      }
      break;
    }
    case AssignOp::QUESTION_EQ: {
      Var* prev = LookupVarInCurrentScope(lhs);
      if (!prev->IsDefined()) {
        rhs = new RecursiveVar(rhs_v, origin, orig_rhs);
      } else {
        rhs = prev;
        needs_assign = false;
      }
      break;
    }
  }

  LOG("Assign: %s=%s", lhs.c_str(), rhs->DebugString().c_str());
  if (needs_assign) {
    return rhs;
  }
  return NULL;
}

void Evaluator::EvalAssign(const AssignStmt* stmt) {
  loc_ = stmt->loc();
  last_rule_ = NULL;
  Symbol lhs = Intern(stmt->lhs->Eval(this));
  if (lhs.empty())
    Error("*** empty variable name.");
  Var* rhs = EvalRHS(lhs, stmt->rhs, stmt->orig_rhs, stmt->op,
                     stmt->directive == AssignDirective::OVERRIDE);
  if (rhs)
    vars_->Assign(lhs, rhs);
}

void Evaluator::EvalRule(const RuleStmt* stmt) {
  loc_ = stmt->loc();
  last_rule_ = NULL;

  const string&& expr = stmt->expr->Eval(this);
  // See semicolon.mk.
  if (expr.find_first_not_of(" \t\n;") == string::npos)
    return;

  Rule* rule;
  RuleVarAssignment rule_var;
  ParseRule(loc_, expr, stmt->term, &rule, &rule_var);

  if (rule) {
    if (stmt->term == ';') {
      rule->cmds.push_back(stmt->after_term);
    }

    LOG("Rule: %s", rule->DebugString().c_str());
    rules_.push_back(shared_ptr<Rule>(rule));
    last_rule_ = rule;
    return;
  }

  for (Symbol output : rule_var.outputs) {
    auto p = rule_vars_.emplace(output, nullptr);
    if (p.second) {
      p.first->second = new Vars;
    }

    Value* rhs = stmt->after_term;
    if (!rule_var.rhs.empty()) {
      Value* lit = NewLiteral(rule_var.rhs);
      if (rhs) {
        // TODO: We always insert two whitespaces around the
        // terminator. Preserve whitespaces properly.
        if (stmt->term == ';') {
          rhs = NewExpr3(lit, NewLiteral(StringPiece(" ; ")), rhs);
        } else {
          rhs = NewExpr3(lit, NewLiteral(StringPiece(" = ")), rhs);
        }
      } else {
        rhs = lit;
      }
    }

    current_scope_ = p.first->second;
    Symbol lhs = Intern(rule_var.lhs);
    Var* rhs_var = EvalRHS(lhs, rhs, StringPiece("*TODO*"), rule_var.op);
    if (rhs_var)
      current_scope_->Assign(lhs, new RuleVar(rhs_var, rule_var.op));
    current_scope_ = NULL;
  }
}

void Evaluator::EvalCommand(const CommandStmt* stmt) {
  loc_ = stmt->loc();

  if (!last_rule_) {
    vector<Stmt*> stmts;
    ParseNotAfterRule(stmt->orig, stmt->loc(), &stmts);
    for (Stmt* a : stmts)
      a->Eval(this);
    return;
  }

  last_rule_->cmds.push_back(stmt->expr);
  if (last_rule_->cmd_lineno == 0)
    last_rule_->cmd_lineno = stmt->loc().lineno;
  LOG("Command: %s", stmt->expr->DebugString().c_str());
}

void Evaluator::EvalIf(const IfStmt* stmt) {
  loc_ = stmt->loc();

  bool is_true;
  switch (stmt->op) {
    case CondOp::IFDEF:
    case CondOp::IFNDEF: {
      string var_name;
      stmt->lhs->Eval(this, &var_name);
      Symbol lhs = Intern(TrimRightSpace(var_name));
      if (lhs.str().find_first_of(" \t") != string::npos)
        Error("*** invalid syntax in conditional.");
      Var* v = LookupVarInCurrentScope(lhs);
      const string&& s = v->Eval(this);
{'fraction_non_alphanumeric': 0.1519916142557652, 'fraction_numerical': 0.0020964360587002098, 'mean_word_length': 2.8712652157875325, 'pattern_counts': {'":': 0, '<': 11, '<?xml version=': 0, '>': 95, 'https://': 0, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '6750527', 'n_tokens_mistral': 3719, 'n_tokens_neox': 3726, 'n_words': 872}