From 5ae2a2b847f94e39932c7abd06662d990773649a Mon Sep 17 00:00:00 2001 From: Vincent Ambo Date: Thu, 16 Jul 2020 20:28:11 +0100 Subject: refactor(3p/nix/libexpr): Extract static helpers out of parser.y Moves several of the static helper functions into a new parser.cc file. Once the rest of the code is usefully extracted, these will be moved to a private namespace. Change-Id: I0d7b53dcefe31bb5c6bad3ad7f5fcb48276bf799 Reviewed-on: https://cl.tvl.fyi/c/depot/+/1218 Tested-by: BuildkiteCI Reviewed-by: isomer --- third_party/nix/src/libexpr/CMakeLists.txt | 1 + third_party/nix/src/libexpr/parser.cc | 186 ++++++++++++++++++++++++++++ third_party/nix/src/libexpr/parser.hh | 10 ++ third_party/nix/src/libexpr/parser.y | 190 +---------------------------- 4 files changed, 202 insertions(+), 185 deletions(-) create mode 100644 third_party/nix/src/libexpr/parser.cc (limited to 'third_party') diff --git a/third_party/nix/src/libexpr/CMakeLists.txt b/third_party/nix/src/libexpr/CMakeLists.txt index d2e991657b..693b618c66 100644 --- a/third_party/nix/src/libexpr/CMakeLists.txt +++ b/third_party/nix/src/libexpr/CMakeLists.txt @@ -59,6 +59,7 @@ target_sources(nixexpr json-to-value.cc names.cc nixexpr.cc + parser.cc primops.cc symbol-table.cc value-to-json.cc diff --git a/third_party/nix/src/libexpr/parser.cc b/third_party/nix/src/libexpr/parser.cc new file mode 100644 index 0000000000..50b7cc3ff8 --- /dev/null +++ b/third_party/nix/src/libexpr/parser.cc @@ -0,0 +1,186 @@ +#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(&(*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(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(*i); + + ExprAttrs* nested = new ExprAttrs; + attrs->dynamicAttrs.push_back( + ExprAttrs::DynamicAttrDef(expr, nested, pos)); + attrs = nested; + } + } + // Expr insertion. + // ========================== + if (auto* sym = std::get_if(&(*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(e); + auto jAttrs = dynamic_cast(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(*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& 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(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* es2 = new std::vector; + atStartOfLine = true; + size_t curDropped = 0; + size_t n = es.size(); + for (std::vector::iterator i = es.begin(); i != es.end(); ++i, --n) { + ExprIndStr* e = dynamic_cast(*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((*es2)[0]) + ? (*es2)[0] + : new ExprConcatStrings(pos, true, es2); +} + + +} // namespace nix diff --git a/third_party/nix/src/libexpr/parser.hh b/third_party/nix/src/libexpr/parser.hh index ae0e932147..3de8280bf5 100644 --- a/third_party/nix/src/libexpr/parser.hh +++ b/third_party/nix/src/libexpr/parser.hh @@ -15,6 +15,8 @@ int yylex(YYSTYPE* yylval_param, YYLTYPE* yylloc_param, yyscan_t yyscanner, \ nix::ParseData* data) +#define CUR_POS makeCurPos(*yylocp, data) + namespace nix { struct ParseData { @@ -44,4 +46,12 @@ static void dupAttr(Symbol attr, const Pos& pos, const Pos& prevPos) { attr % pos % prevPos); } +void addAttr(ExprAttrs* attrs, AttrPath& attrPath, Expr* e, const Pos& pos); + +void addFormal(const Pos& pos, Formals* formals, const Formal& formal); + +Expr* stripIndentation(const Pos& pos, SymbolTable& symbols, + std::vector& es); + + } // namespace nix diff --git a/third_party/nix/src/libexpr/parser.y b/third_party/nix/src/libexpr/parser.y index 9df9b3081a..2ec1cf6c55 100644 --- a/third_party/nix/src/libexpr/parser.y +++ b/third_party/nix/src/libexpr/parser.y @@ -24,197 +24,17 @@ using namespace nix; namespace nix { -static 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(&(*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(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(*i); - - ExprAttrs *nested = new ExprAttrs; - attrs->dynamicAttrs.push_back(ExprAttrs::DynamicAttrDef(expr, nested, pos)); - attrs = nested; - } - } - // Expr insertion. - // ========================== - if (auto* sym = std::get_if(&(*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(e); - auto jAttrs = dynamic_cast(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(*i); - attrs->dynamicAttrs.push_back(ExprAttrs::DynamicAttrDef(expr, e, pos)); - } +static inline Pos makeCurPos(const YYLTYPE& loc, ParseData* data) { + return Pos(data->path, loc.first_line, loc.first_column); } - -static 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); +void yyerror(YYLTYPE* loc, yyscan_t scanner, ParseData* data, + const char* error) { + data->error = (format("%1%, at %2%") % error % makeCurPos(*loc, data)).str(); } - -static Expr * stripIndentation(const Pos & pos, SymbolTable & symbols, std::vector & 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(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 * es2 = new std::vector; - atStartOfLine = true; - size_t curDropped = 0; - size_t n = es.size(); - for (std::vector::iterator i = es.begin(); i != es.end(); ++i, --n) { - ExprIndStr * e = dynamic_cast(*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((*es2)[0]) ? (*es2)[0] : new ExprConcatStrings(pos, true, es2); -} - - -static inline Pos makeCurPos(const YYLTYPE & loc, ParseData * data) -{ - return Pos(data->path, loc.first_line, loc.first_column); } -#define CUR_POS makeCurPos(*yylocp, data) - - -} - - -void yyerror(YYLTYPE * loc, yyscan_t scanner, ParseData * data, const char * error) -{ - data->error = (format("%1%, at %2%") - % error % makeCurPos(*loc, data)).str(); -} - - %} %union { -- cgit 1.4.1