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 <isomer@tvl.fyi>
This commit is contained in:
parent
d579ceb783
commit
5ae2a2b847
4 changed files with 202 additions and 185 deletions
1
third_party/nix/src/libexpr/CMakeLists.txt
vendored
1
third_party/nix/src/libexpr/CMakeLists.txt
vendored
|
@ -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
|
||||
|
|
186
third_party/nix/src/libexpr/parser.cc
vendored
Normal file
186
third_party/nix/src/libexpr/parser.cc
vendored
Normal file
|
@ -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<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
|
10
third_party/nix/src/libexpr/parser.hh
vendored
10
third_party/nix/src/libexpr/parser.hh
vendored
|
@ -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<Expr*>& es);
|
||||
|
||||
|
||||
} // namespace nix
|
||||
|
|
190
third_party/nix/src/libexpr/parser.y
vendored
190
third_party/nix/src/libexpr/parser.y
vendored
|
@ -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<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));
|
||||
}
|
||||
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<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);
|
||||
}
|
||||
|
||||
|
||||
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 {
|
||||
|
|
Loading…
Reference in a new issue