about summary refs log blame commit diff
path: root/third_party/nix/src/libexpr/parser.cc
blob: 50b7cc3ff8e3da24a133abf5590cab50fb216a16 (plain) (tree)

























































































































































































                                                                                
#include "libexpr/parser.hh"

namespace nix {

void addAttr(ExprAttrs* attrs, AttrPath& attrPath, Expr* e, const Pos& pos) {
  AttrPath::iterator i;
  // All attrpaths have at least one attr
  assert(!attrPath.empty());
  // Checking attrPath validity.
  // ===========================
  for (i = attrPath.begin(); i + 1 < attrPath.end(); i++) {
    if (const auto* sym = std::get_if<Symbol>(&(*i)); sym && sym->set()) {
      ExprAttrs::AttrDefs::iterator j = attrs->attrs.find(*sym);
      if (j != attrs->attrs.end()) {
        if (!j->second.inherited) {
          ExprAttrs* attrs2 = dynamic_cast<ExprAttrs*>(j->second.e);
          if (!attrs2) {
            dupAttr(attrPath, pos, j->second.pos);
          }
          attrs = attrs2;
        } else {
          dupAttr(attrPath, pos, j->second.pos);
        }
      } else {
        ExprAttrs* nested = new ExprAttrs;
        attrs->attrs[*sym] = ExprAttrs::AttrDef(nested, pos);
        attrs = nested;
      }
    } else {
      // Yes, this code does not handle all conditions
      // exhaustively. We use std::get to throw if the condition
      // that isn't covered happens, which is potentially a
      // behaviour change from the previous default constructed
      // Symbol. It should alert us about anything untoward going
      // on here.
      auto* expr = std::get<Expr*>(*i);

      ExprAttrs* nested = new ExprAttrs;
      attrs->dynamicAttrs.push_back(
          ExprAttrs::DynamicAttrDef(expr, nested, pos));
      attrs = nested;
    }
  }
  // Expr insertion.
  // ==========================
  if (auto* sym = std::get_if<Symbol>(&(*i)); sym && sym->set()) {
    ExprAttrs::AttrDefs::iterator j = attrs->attrs.find(*sym);
    if (j != attrs->attrs.end()) {
      // This attr path is already defined. However, if both
      // e and the expr pointed by the attr path are two attribute sets,
      // we want to merge them.
      // Otherwise, throw an error.
      auto ae = dynamic_cast<ExprAttrs*>(e);
      auto jAttrs = dynamic_cast<ExprAttrs*>(j->second.e);
      if (jAttrs && ae) {
        for (auto& ad : ae->attrs) {
          auto j2 = jAttrs->attrs.find(ad.first);
          if (j2 !=
              jAttrs->attrs.end()) {  // Attr already defined in iAttrs, error.
            dupAttr(ad.first, j2->second.pos, ad.second.pos);
          }
          jAttrs->attrs[ad.first] = ad.second;
        }
      } else {
        dupAttr(attrPath, pos, j->second.pos);
      }
    } else {
      // This attr path is not defined. Let's create it.
      attrs->attrs[*sym] = ExprAttrs::AttrDef(e, pos);
    }
  } else {
    // Same caveat as the identical line above.
    auto* expr = std::get<Expr*>(*i);
    attrs->dynamicAttrs.push_back(ExprAttrs::DynamicAttrDef(expr, e, pos));
  }
}

void addFormal(const Pos& pos, Formals* formals, const Formal& formal) {
  if (formals->argNames.find(formal.name) != formals->argNames.end())
    throw ParseError(format("duplicate formal function argument '%1%' at %2%") %
                     formal.name % pos);
  formals->formals.push_front(formal);
  formals->argNames.insert(formal.name);
}

Expr* stripIndentation(const Pos& pos, SymbolTable& symbols,
                       std::vector<Expr*>& es) {
  if (es.empty()) {
    return new ExprString(symbols.Create(""));
  }

  /* Figure out the minimum indentation.  Note that by design
     whitespace-only final lines are not taken into account.  (So
     the " " in "\n ''" is ignored, but the " " in "\n foo''" is.) */
  bool atStartOfLine = true; /* = seen only whitespace in the current line */
  size_t minIndent = 1000000;
  size_t curIndent = 0;
  for (auto& i : es) {
    ExprIndStr* e = dynamic_cast<ExprIndStr*>(i);
    if (!e) {
      /* Anti-quotations end the current start-of-line whitespace. */
      if (atStartOfLine) {
        atStartOfLine = false;
        if (curIndent < minIndent) {
          minIndent = curIndent;
        }
      }
      continue;
    }
    for (size_t j = 0; j < e->s.size(); ++j) {
      if (atStartOfLine) {
        if (e->s[j] == ' ')
          curIndent++;
        else if (e->s[j] == '\n') {
          /* Empty line, doesn't influence minimum
             indentation. */
          curIndent = 0;
        } else {
          atStartOfLine = false;
          if (curIndent < minIndent) {
            minIndent = curIndent;
          }
        }
      } else if (e->s[j] == '\n') {
        atStartOfLine = true;
        curIndent = 0;
      }
    }
  }

  /* Strip spaces from each line. */
  std::vector<Expr*>* es2 = new std::vector<Expr*>;
  atStartOfLine = true;
  size_t curDropped = 0;
  size_t n = es.size();
  for (std::vector<Expr*>::iterator i = es.begin(); i != es.end(); ++i, --n) {
    ExprIndStr* e = dynamic_cast<ExprIndStr*>(*i);
    if (!e) {
      atStartOfLine = false;
      curDropped = 0;
      es2->push_back(*i);
      continue;
    }

    std::string s2;
    for (size_t j = 0; j < e->s.size(); ++j) {
      if (atStartOfLine) {
        if (e->s[j] == ' ') {
          if (curDropped++ >= minIndent) s2 += e->s[j];
        } else if (e->s[j] == '\n') {
          curDropped = 0;
          s2 += e->s[j];
        } else {
          atStartOfLine = false;
          curDropped = 0;
          s2 += e->s[j];
        }
      } else {
        s2 += e->s[j];
        if (e->s[j] == '\n') {
          atStartOfLine = true;
        }
      }
    }

    /* Remove the last line if it is empty and consists only of
       spaces. */
    if (n == 1) {
      std::string::size_type p = s2.find_last_of('\n');
      if (p != std::string::npos &&
          s2.find_first_not_of(' ', p + 1) == std::string::npos) {
        s2 = std::string(s2, 0, p + 1);
      }
    }

    es2->push_back(new ExprString(symbols.Create(s2)));
  }

  /* If this is a single string, then don't do a concatenation. */
  return es2->size() == 1 && dynamic_cast<ExprString*>((*es2)[0])
             ? (*es2)[0]
             : new ExprConcatStrings(pos, true, es2);
}


}  // namespace nix