forked from lix-project/lix
move ParseData to own header, rename to ParserState
ParserState better describes what this struct really is. the parser
really does modify its state (most notably position and symbol tables),
so calling it that rather than obliquely "data" (which implies being
input only) makes sense.
(cherry picked from commit 007605616477f4f0d8a0064c375b1d3cf6188ac5)
Change-Id: I92feaec796530e1d4d0f7d4fba924229591cea95
This commit is contained in:
parent
60e3a05175
commit
e1cd0077f3
|
@ -35,12 +35,7 @@ using namespace nix;
|
||||||
|
|
||||||
namespace nix {
|
namespace nix {
|
||||||
|
|
||||||
static inline PosIdx makeCurPos(const YYLTYPE & loc, ParseData * data)
|
#define CUR_POS state->makeCurPos(*yylloc)
|
||||||
{
|
|
||||||
return data->state.positions.add(data->origin, loc.first_line, loc.first_column);
|
|
||||||
}
|
|
||||||
|
|
||||||
#define CUR_POS makeCurPos(*yylloc, data)
|
|
||||||
|
|
||||||
static void initLoc(YYLTYPE * loc)
|
static void initLoc(YYLTYPE * loc)
|
||||||
{
|
{
|
||||||
|
@ -159,7 +154,7 @@ or { return OR_KW; }
|
||||||
} catch (const boost::bad_lexical_cast &) {
|
} catch (const boost::bad_lexical_cast &) {
|
||||||
throw ParseError({
|
throw ParseError({
|
||||||
.msg = hintfmt("invalid integer '%1%'", yytext),
|
.msg = hintfmt("invalid integer '%1%'", yytext),
|
||||||
.errPos = data->state.positions[CUR_POS],
|
.errPos = state->state.positions[CUR_POS],
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
return INT;
|
return INT;
|
||||||
|
@ -169,7 +164,7 @@ or { return OR_KW; }
|
||||||
if (errno != 0)
|
if (errno != 0)
|
||||||
throw ParseError({
|
throw ParseError({
|
||||||
.msg = hintfmt("invalid float '%1%'", yytext),
|
.msg = hintfmt("invalid float '%1%'", yytext),
|
||||||
.errPos = data->state.positions[CUR_POS],
|
.errPos = state->state.positions[CUR_POS],
|
||||||
});
|
});
|
||||||
return FLOAT;
|
return FLOAT;
|
||||||
}
|
}
|
||||||
|
@ -192,7 +187,7 @@ or { return OR_KW; }
|
||||||
/* It is impossible to match strings ending with '$' with one
|
/* It is impossible to match strings ending with '$' with one
|
||||||
regex because trailing contexts are only valid at the end
|
regex because trailing contexts are only valid at the end
|
||||||
of a rule. (A sane but undocumented limitation.) */
|
of a rule. (A sane but undocumented limitation.) */
|
||||||
yylval->str = unescapeStr(data->symbols, yytext, yyleng);
|
yylval->str = unescapeStr(state->symbols, yytext, yyleng);
|
||||||
return STR;
|
return STR;
|
||||||
}
|
}
|
||||||
<STRING>\$\{ { PUSH_STATE(DEFAULT); return DOLLAR_CURLY; }
|
<STRING>\$\{ { PUSH_STATE(DEFAULT); return DOLLAR_CURLY; }
|
||||||
|
@ -220,7 +215,7 @@ or { return OR_KW; }
|
||||||
return IND_STR;
|
return IND_STR;
|
||||||
}
|
}
|
||||||
<IND_STRING>\'\'\\{ANY} {
|
<IND_STRING>\'\'\\{ANY} {
|
||||||
yylval->str = unescapeStr(data->symbols, yytext + 2, yyleng - 2);
|
yylval->str = unescapeStr(state->symbols, yytext + 2, yyleng - 2);
|
||||||
return IND_STR;
|
return IND_STR;
|
||||||
}
|
}
|
||||||
<IND_STRING>\$\{ { PUSH_STATE(DEFAULT); return DOLLAR_CURLY; }
|
<IND_STRING>\$\{ { PUSH_STATE(DEFAULT); return DOLLAR_CURLY; }
|
||||||
|
@ -298,7 +293,7 @@ or { return OR_KW; }
|
||||||
<INPATH_SLASH><<EOF>> {
|
<INPATH_SLASH><<EOF>> {
|
||||||
throw ParseError({
|
throw ParseError({
|
||||||
.msg = hintfmt("path has a trailing slash"),
|
.msg = hintfmt("path has a trailing slash"),
|
||||||
.errPos = data->state.positions[CUR_POS],
|
.errPos = state->state.positions[CUR_POS],
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
262
src/libexpr/parser-state.hh
Normal file
262
src/libexpr/parser-state.hh
Normal file
|
@ -0,0 +1,262 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "eval.hh"
|
||||||
|
|
||||||
|
namespace nix {
|
||||||
|
|
||||||
|
// using C a struct allows us to avoid having to define the special
|
||||||
|
// members that using string_view here would implicitly delete.
|
||||||
|
struct StringToken {
|
||||||
|
const char * p;
|
||||||
|
size_t l;
|
||||||
|
bool hasIndentation;
|
||||||
|
operator std::string_view() const { return {p, l}; }
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ParserLocation {
|
||||||
|
int first_line, first_column;
|
||||||
|
int last_line, last_column;
|
||||||
|
|
||||||
|
// backup to recover from yyless(0)
|
||||||
|
int stashed_first_line, stashed_first_column;
|
||||||
|
int stashed_last_line, stashed_last_column;
|
||||||
|
|
||||||
|
void stash() {
|
||||||
|
stashed_first_line = first_line;
|
||||||
|
stashed_first_column = first_column;
|
||||||
|
stashed_last_line = last_line;
|
||||||
|
stashed_last_column = last_column;
|
||||||
|
}
|
||||||
|
|
||||||
|
void unstash() {
|
||||||
|
first_line = stashed_first_line;
|
||||||
|
first_column = stashed_first_column;
|
||||||
|
last_line = stashed_last_line;
|
||||||
|
last_column = stashed_last_column;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ParserState {
|
||||||
|
EvalState & state;
|
||||||
|
SymbolTable & symbols;
|
||||||
|
Expr * result;
|
||||||
|
SourcePath basePath;
|
||||||
|
PosTable::Origin origin;
|
||||||
|
|
||||||
|
void dupAttr(const AttrPath & attrPath, const PosIdx pos, const PosIdx prevPos);
|
||||||
|
void dupAttr(Symbol attr, const PosIdx pos, const PosIdx prevPos);
|
||||||
|
void addAttr(ExprAttrs * attrs, AttrPath && attrPath, Expr * e, const PosIdx pos);
|
||||||
|
Formals * validateFormals(Formals * formals, PosIdx pos = noPos, Symbol arg = {});
|
||||||
|
Expr * stripIndentation(const PosIdx pos,
|
||||||
|
std::vector<std::pair<PosIdx, std::variant<Expr *, StringToken>>> && es);
|
||||||
|
PosIdx makeCurPos(const ParserLocation & loc);
|
||||||
|
};
|
||||||
|
|
||||||
|
inline void ParserState::dupAttr(const AttrPath & attrPath, const PosIdx pos, const PosIdx prevPos)
|
||||||
|
{
|
||||||
|
throw ParseError({
|
||||||
|
.msg = hintfmt("attribute '%1%' already defined at %2%",
|
||||||
|
showAttrPath(state.symbols, attrPath), state.positions[prevPos]),
|
||||||
|
.errPos = state.positions[pos]
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void ParserState::dupAttr(Symbol attr, const PosIdx pos, const PosIdx prevPos)
|
||||||
|
{
|
||||||
|
throw ParseError({
|
||||||
|
.msg = hintfmt("attribute '%1%' already defined at %2%", state.symbols[attr], state.positions[prevPos]),
|
||||||
|
.errPos = state.positions[pos]
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void ParserState::addAttr(ExprAttrs * attrs, AttrPath && attrPath, Expr * e, const PosIdx 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 (i->symbol) {
|
||||||
|
ExprAttrs::AttrDefs::iterator j = attrs->attrs.find(i->symbol);
|
||||||
|
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[i->symbol] = ExprAttrs::AttrDef(nested, pos);
|
||||||
|
attrs = nested;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
ExprAttrs *nested = new ExprAttrs;
|
||||||
|
attrs->dynamicAttrs.push_back(ExprAttrs::DynamicAttrDef(i->expr, nested, pos));
|
||||||
|
attrs = nested;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Expr insertion.
|
||||||
|
// ==========================
|
||||||
|
if (i->symbol) {
|
||||||
|
ExprAttrs::AttrDefs::iterator j = attrs->attrs.find(i->symbol);
|
||||||
|
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.emplace(ad.first, ad.second);
|
||||||
|
}
|
||||||
|
jAttrs->dynamicAttrs.insert(jAttrs->dynamicAttrs.end(), ae->dynamicAttrs.begin(), ae->dynamicAttrs.end());
|
||||||
|
} else {
|
||||||
|
dupAttr(attrPath, pos, j->second.pos);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// This attr path is not defined. Let's create it.
|
||||||
|
attrs->attrs.emplace(i->symbol, ExprAttrs::AttrDef(e, pos));
|
||||||
|
e->setName(i->symbol);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
attrs->dynamicAttrs.push_back(ExprAttrs::DynamicAttrDef(i->expr, e, pos));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
inline Formals * ParserState::validateFormals(Formals * formals, PosIdx pos, Symbol arg)
|
||||||
|
{
|
||||||
|
std::sort(formals->formals.begin(), formals->formals.end(),
|
||||||
|
[] (const auto & a, const auto & b) {
|
||||||
|
return std::tie(a.name, a.pos) < std::tie(b.name, b.pos);
|
||||||
|
});
|
||||||
|
|
||||||
|
std::optional<std::pair<Symbol, PosIdx>> duplicate;
|
||||||
|
for (size_t i = 0; i + 1 < formals->formals.size(); i++) {
|
||||||
|
if (formals->formals[i].name != formals->formals[i + 1].name)
|
||||||
|
continue;
|
||||||
|
std::pair thisDup{formals->formals[i].name, formals->formals[i + 1].pos};
|
||||||
|
duplicate = std::min(thisDup, duplicate.value_or(thisDup));
|
||||||
|
}
|
||||||
|
if (duplicate)
|
||||||
|
throw ParseError({
|
||||||
|
.msg = hintfmt("duplicate formal function argument '%1%'", symbols[duplicate->first]),
|
||||||
|
.errPos = state.positions[duplicate->second]
|
||||||
|
});
|
||||||
|
|
||||||
|
if (arg && formals->has(arg))
|
||||||
|
throw ParseError({
|
||||||
|
.msg = hintfmt("duplicate formal function argument '%1%'", symbols[arg]),
|
||||||
|
.errPos = state.positions[pos]
|
||||||
|
});
|
||||||
|
|
||||||
|
return formals;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline Expr * ParserState::stripIndentation(const PosIdx pos,
|
||||||
|
std::vector<std::pair<PosIdx, std::variant<Expr *, StringToken>>> && es)
|
||||||
|
{
|
||||||
|
if (es.empty()) return new ExprString("");
|
||||||
|
|
||||||
|
/* 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_pos, i] : es) {
|
||||||
|
auto * str = std::get_if<StringToken>(&i);
|
||||||
|
if (!str || !str->hasIndentation) {
|
||||||
|
/* Anti-quotations and escaped characters end the current start-of-line whitespace. */
|
||||||
|
if (atStartOfLine) {
|
||||||
|
atStartOfLine = false;
|
||||||
|
if (curIndent < minIndent) minIndent = curIndent;
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
for (size_t j = 0; j < str->l; ++j) {
|
||||||
|
if (atStartOfLine) {
|
||||||
|
if (str->p[j] == ' ')
|
||||||
|
curIndent++;
|
||||||
|
else if (str->p[j] == '\n') {
|
||||||
|
/* Empty line, doesn't influence minimum
|
||||||
|
indentation. */
|
||||||
|
curIndent = 0;
|
||||||
|
} else {
|
||||||
|
atStartOfLine = false;
|
||||||
|
if (curIndent < minIndent) minIndent = curIndent;
|
||||||
|
}
|
||||||
|
} else if (str->p[j] == '\n') {
|
||||||
|
atStartOfLine = true;
|
||||||
|
curIndent = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Strip spaces from each line. */
|
||||||
|
auto * es2 = new std::vector<std::pair<PosIdx, Expr *>>;
|
||||||
|
atStartOfLine = true;
|
||||||
|
size_t curDropped = 0;
|
||||||
|
size_t n = es.size();
|
||||||
|
auto i = es.begin();
|
||||||
|
const auto trimExpr = [&] (Expr * e) {
|
||||||
|
atStartOfLine = false;
|
||||||
|
curDropped = 0;
|
||||||
|
es2->emplace_back(i->first, e);
|
||||||
|
};
|
||||||
|
const auto trimString = [&] (const StringToken & t) {
|
||||||
|
std::string s2;
|
||||||
|
for (size_t j = 0; j < t.l; ++j) {
|
||||||
|
if (atStartOfLine) {
|
||||||
|
if (t.p[j] == ' ') {
|
||||||
|
if (curDropped++ >= minIndent)
|
||||||
|
s2 += t.p[j];
|
||||||
|
}
|
||||||
|
else if (t.p[j] == '\n') {
|
||||||
|
curDropped = 0;
|
||||||
|
s2 += t.p[j];
|
||||||
|
} else {
|
||||||
|
atStartOfLine = false;
|
||||||
|
curDropped = 0;
|
||||||
|
s2 += t.p[j];
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
s2 += t.p[j];
|
||||||
|
if (t.p[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->emplace_back(i->first, new ExprString(std::move(s2)));
|
||||||
|
};
|
||||||
|
for (; i != es.end(); ++i, --n) {
|
||||||
|
std::visit(overloaded { trimExpr, trimString }, i->second);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* If this is a single string, then don't do a concatenation. */
|
||||||
|
if (es2->size() == 1 && dynamic_cast<ExprString *>((*es2)[0].second)) {
|
||||||
|
auto *const result = (*es2)[0].second;
|
||||||
|
delete es2;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
return new ExprConcatStrings(pos, true, es2);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline PosIdx ParserState::makeCurPos(const ParserLocation & loc)
|
||||||
|
{
|
||||||
|
return state.positions.add(origin, loc.first_line, loc.first_column);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -5,9 +5,9 @@
|
||||||
%defines
|
%defines
|
||||||
/* %no-lines */
|
/* %no-lines */
|
||||||
%parse-param { void * scanner }
|
%parse-param { void * scanner }
|
||||||
%parse-param { nix::ParseData * data }
|
%parse-param { nix::ParserState * state }
|
||||||
%lex-param { void * scanner }
|
%lex-param { void * scanner }
|
||||||
%lex-param { nix::ParseData * data }
|
%lex-param { nix::ParserState * state }
|
||||||
%expect 1
|
%expect 1
|
||||||
%expect-rr 1
|
%expect-rr 1
|
||||||
|
|
||||||
|
@ -25,64 +25,11 @@
|
||||||
#include "eval.hh"
|
#include "eval.hh"
|
||||||
#include "eval-settings.hh"
|
#include "eval-settings.hh"
|
||||||
#include "globals.hh"
|
#include "globals.hh"
|
||||||
|
#include "parser-state.hh"
|
||||||
// using C a struct allows us to avoid having to define the special
|
|
||||||
// members that using string_view here would implicitly delete.
|
|
||||||
struct StringToken {
|
|
||||||
const char * p;
|
|
||||||
size_t l;
|
|
||||||
bool hasIndentation;
|
|
||||||
operator std::string_view() const { return {p, l}; }
|
|
||||||
};
|
|
||||||
|
|
||||||
namespace nix {
|
|
||||||
|
|
||||||
#define YYLTYPE ::nix::ParserLocation
|
#define YYLTYPE ::nix::ParserLocation
|
||||||
struct ParserLocation
|
|
||||||
{
|
|
||||||
int first_line, first_column;
|
|
||||||
int last_line, last_column;
|
|
||||||
|
|
||||||
// backup to recover from yyless(0)
|
|
||||||
int stashed_first_line, stashed_first_column;
|
|
||||||
int stashed_last_line, stashed_last_column;
|
|
||||||
|
|
||||||
void stash() {
|
|
||||||
stashed_first_line = first_line;
|
|
||||||
stashed_first_column = first_column;
|
|
||||||
stashed_last_line = last_line;
|
|
||||||
stashed_last_column = last_column;
|
|
||||||
}
|
|
||||||
|
|
||||||
void unstash() {
|
|
||||||
first_line = stashed_first_line;
|
|
||||||
first_column = stashed_first_column;
|
|
||||||
last_line = stashed_last_line;
|
|
||||||
last_column = stashed_last_column;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
struct ParseData
|
|
||||||
{
|
|
||||||
EvalState & state;
|
|
||||||
SymbolTable & symbols;
|
|
||||||
Expr * result;
|
|
||||||
SourcePath basePath;
|
|
||||||
PosTable::Origin origin;
|
|
||||||
|
|
||||||
void dupAttr(const AttrPath & attrPath, const PosIdx pos, const PosIdx prevPos);
|
|
||||||
void dupAttr(Symbol attr, const PosIdx pos, const PosIdx prevPos);
|
|
||||||
void addAttr(ExprAttrs * attrs, AttrPath && attrPath, Expr * e, const PosIdx pos);
|
|
||||||
Formals * validateFormals(Formals * formals, PosIdx pos = noPos, Symbol arg = {});
|
|
||||||
Expr * stripIndentation(const PosIdx pos,
|
|
||||||
std::vector<std::pair<PosIdx, std::variant<Expr *, StringToken>>> && es);
|
|
||||||
PosIdx makeCurPos(const ParserLocation & loc);
|
|
||||||
};
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
#define YY_DECL int yylex \
|
#define YY_DECL int yylex \
|
||||||
(YYSTYPE * yylval_param, YYLTYPE * yylloc_param, yyscan_t yyscanner, nix::ParseData * data)
|
(YYSTYPE * yylval_param, YYLTYPE * yylloc_param, yyscan_t yyscanner, nix::ParserState * state)
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
@ -97,232 +44,14 @@ YY_DECL;
|
||||||
|
|
||||||
using namespace nix;
|
using namespace nix;
|
||||||
|
|
||||||
|
#define CUR_POS state->makeCurPos(*yylocp)
|
||||||
namespace nix {
|
|
||||||
|
|
||||||
|
|
||||||
void ParseData::dupAttr(const AttrPath & attrPath, const PosIdx pos, const PosIdx prevPos)
|
void yyerror(YYLTYPE * loc, yyscan_t scanner, ParserState * state, const char * error)
|
||||||
{
|
|
||||||
throw ParseError({
|
|
||||||
.msg = hintfmt("attribute '%1%' already defined at %2%",
|
|
||||||
showAttrPath(state.symbols, attrPath), state.positions[prevPos]),
|
|
||||||
.errPos = state.positions[pos]
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
void ParseData::dupAttr(Symbol attr, const PosIdx pos, const PosIdx prevPos)
|
|
||||||
{
|
|
||||||
throw ParseError({
|
|
||||||
.msg = hintfmt("attribute '%1%' already defined at %2%", state.symbols[attr], state.positions[prevPos]),
|
|
||||||
.errPos = state.positions[pos]
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void ParseData::addAttr(ExprAttrs * attrs, AttrPath && attrPath, Expr * e, const PosIdx 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 (i->symbol) {
|
|
||||||
ExprAttrs::AttrDefs::iterator j = attrs->attrs.find(i->symbol);
|
|
||||||
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[i->symbol] = ExprAttrs::AttrDef(nested, pos);
|
|
||||||
attrs = nested;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
ExprAttrs *nested = new ExprAttrs;
|
|
||||||
attrs->dynamicAttrs.push_back(ExprAttrs::DynamicAttrDef(i->expr, nested, pos));
|
|
||||||
attrs = nested;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Expr insertion.
|
|
||||||
// ==========================
|
|
||||||
if (i->symbol) {
|
|
||||||
ExprAttrs::AttrDefs::iterator j = attrs->attrs.find(i->symbol);
|
|
||||||
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.emplace(ad.first, ad.second);
|
|
||||||
}
|
|
||||||
jAttrs->dynamicAttrs.insert(jAttrs->dynamicAttrs.end(), ae->dynamicAttrs.begin(), ae->dynamicAttrs.end());
|
|
||||||
} else {
|
|
||||||
dupAttr(attrPath, pos, j->second.pos);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// This attr path is not defined. Let's create it.
|
|
||||||
attrs->attrs.emplace(i->symbol, ExprAttrs::AttrDef(e, pos));
|
|
||||||
e->setName(i->symbol);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
attrs->dynamicAttrs.push_back(ExprAttrs::DynamicAttrDef(i->expr, e, pos));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
Formals * ParseData::validateFormals(Formals * formals, PosIdx pos, Symbol arg)
|
|
||||||
{
|
|
||||||
std::sort(formals->formals.begin(), formals->formals.end(),
|
|
||||||
[] (const auto & a, const auto & b) {
|
|
||||||
return std::tie(a.name, a.pos) < std::tie(b.name, b.pos);
|
|
||||||
});
|
|
||||||
|
|
||||||
std::optional<std::pair<Symbol, PosIdx>> duplicate;
|
|
||||||
for (size_t i = 0; i + 1 < formals->formals.size(); i++) {
|
|
||||||
if (formals->formals[i].name != formals->formals[i + 1].name)
|
|
||||||
continue;
|
|
||||||
std::pair thisDup{formals->formals[i].name, formals->formals[i + 1].pos};
|
|
||||||
duplicate = std::min(thisDup, duplicate.value_or(thisDup));
|
|
||||||
}
|
|
||||||
if (duplicate)
|
|
||||||
throw ParseError({
|
|
||||||
.msg = hintfmt("duplicate formal function argument '%1%'", symbols[duplicate->first]),
|
|
||||||
.errPos = state.positions[duplicate->second]
|
|
||||||
});
|
|
||||||
|
|
||||||
if (arg && formals->has(arg))
|
|
||||||
throw ParseError({
|
|
||||||
.msg = hintfmt("duplicate formal function argument '%1%'", symbols[arg]),
|
|
||||||
.errPos = state.positions[pos]
|
|
||||||
});
|
|
||||||
|
|
||||||
return formals;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
Expr * ParseData::stripIndentation(const PosIdx pos,
|
|
||||||
std::vector<std::pair<PosIdx, std::variant<Expr *, StringToken>>> && es)
|
|
||||||
{
|
|
||||||
if (es.empty()) return new ExprString("");
|
|
||||||
|
|
||||||
/* 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_pos, i] : es) {
|
|
||||||
auto * str = std::get_if<StringToken>(&i);
|
|
||||||
if (!str || !str->hasIndentation) {
|
|
||||||
/* Anti-quotations and escaped characters end the current start-of-line whitespace. */
|
|
||||||
if (atStartOfLine) {
|
|
||||||
atStartOfLine = false;
|
|
||||||
if (curIndent < minIndent) minIndent = curIndent;
|
|
||||||
}
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
for (size_t j = 0; j < str->l; ++j) {
|
|
||||||
if (atStartOfLine) {
|
|
||||||
if (str->p[j] == ' ')
|
|
||||||
curIndent++;
|
|
||||||
else if (str->p[j] == '\n') {
|
|
||||||
/* Empty line, doesn't influence minimum
|
|
||||||
indentation. */
|
|
||||||
curIndent = 0;
|
|
||||||
} else {
|
|
||||||
atStartOfLine = false;
|
|
||||||
if (curIndent < minIndent) minIndent = curIndent;
|
|
||||||
}
|
|
||||||
} else if (str->p[j] == '\n') {
|
|
||||||
atStartOfLine = true;
|
|
||||||
curIndent = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Strip spaces from each line. */
|
|
||||||
auto * es2 = new std::vector<std::pair<PosIdx, Expr *>>;
|
|
||||||
atStartOfLine = true;
|
|
||||||
size_t curDropped = 0;
|
|
||||||
size_t n = es.size();
|
|
||||||
auto i = es.begin();
|
|
||||||
const auto trimExpr = [&] (Expr * e) {
|
|
||||||
atStartOfLine = false;
|
|
||||||
curDropped = 0;
|
|
||||||
es2->emplace_back(i->first, e);
|
|
||||||
};
|
|
||||||
const auto trimString = [&] (const StringToken & t) {
|
|
||||||
std::string s2;
|
|
||||||
for (size_t j = 0; j < t.l; ++j) {
|
|
||||||
if (atStartOfLine) {
|
|
||||||
if (t.p[j] == ' ') {
|
|
||||||
if (curDropped++ >= minIndent)
|
|
||||||
s2 += t.p[j];
|
|
||||||
}
|
|
||||||
else if (t.p[j] == '\n') {
|
|
||||||
curDropped = 0;
|
|
||||||
s2 += t.p[j];
|
|
||||||
} else {
|
|
||||||
atStartOfLine = false;
|
|
||||||
curDropped = 0;
|
|
||||||
s2 += t.p[j];
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
s2 += t.p[j];
|
|
||||||
if (t.p[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->emplace_back(i->first, new ExprString(std::move(s2)));
|
|
||||||
};
|
|
||||||
for (; i != es.end(); ++i, --n) {
|
|
||||||
std::visit(overloaded { trimExpr, trimString }, i->second);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* If this is a single string, then don't do a concatenation. */
|
|
||||||
if (es2->size() == 1 && dynamic_cast<ExprString *>((*es2)[0].second)) {
|
|
||||||
auto *const result = (*es2)[0].second;
|
|
||||||
delete es2;
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
return new ExprConcatStrings(pos, true, es2);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
PosIdx ParseData::makeCurPos(const ParserLocation & loc)
|
|
||||||
{
|
|
||||||
return state.positions.add(origin, loc.first_line, loc.first_column);
|
|
||||||
}
|
|
||||||
|
|
||||||
#define CUR_POS data->makeCurPos(*yylocp)
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void yyerror(YYLTYPE * loc, yyscan_t scanner, ParseData * data, const char * error)
|
|
||||||
{
|
{
|
||||||
throw ParseError({
|
throw ParseError({
|
||||||
.msg = hintfmt(error),
|
.msg = hintfmt(error),
|
||||||
.errPos = data->state.positions[data->makeCurPos(*loc)]
|
.errPos = state->state.positions[state->makeCurPos(*loc)]
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -338,13 +67,13 @@ void yyerror(YYLTYPE * loc, yyscan_t scanner, ParseData * data, const char * err
|
||||||
nix::Formal * formal;
|
nix::Formal * formal;
|
||||||
nix::NixInt n;
|
nix::NixInt n;
|
||||||
nix::NixFloat nf;
|
nix::NixFloat nf;
|
||||||
StringToken id; // !!! -> Symbol
|
nix::StringToken id; // !!! -> Symbol
|
||||||
StringToken path;
|
nix::StringToken path;
|
||||||
StringToken uri;
|
nix::StringToken uri;
|
||||||
StringToken str;
|
nix::StringToken str;
|
||||||
std::vector<nix::AttrName> * attrNames;
|
std::vector<nix::AttrName> * attrNames;
|
||||||
std::vector<std::pair<nix::PosIdx, nix::Expr *>> * string_parts;
|
std::vector<std::pair<nix::PosIdx, nix::Expr *>> * string_parts;
|
||||||
std::vector<std::pair<nix::PosIdx, std::variant<nix::Expr *, StringToken>>> * ind_string_parts;
|
std::vector<std::pair<nix::PosIdx, std::variant<nix::Expr *, nix::StringToken>>> * ind_string_parts;
|
||||||
}
|
}
|
||||||
|
|
||||||
%type <e> start expr expr_function expr_if expr_op
|
%type <e> start expr expr_function expr_if expr_op
|
||||||
|
@ -384,24 +113,24 @@ void yyerror(YYLTYPE * loc, yyscan_t scanner, ParseData * data, const char * err
|
||||||
|
|
||||||
%%
|
%%
|
||||||
|
|
||||||
start: expr { data->result = $1; };
|
start: expr { state->result = $1; };
|
||||||
|
|
||||||
expr: expr_function;
|
expr: expr_function;
|
||||||
|
|
||||||
expr_function
|
expr_function
|
||||||
: ID ':' expr_function
|
: ID ':' expr_function
|
||||||
{ $$ = new ExprLambda(CUR_POS, data->symbols.create($1), 0, $3); }
|
{ $$ = new ExprLambda(CUR_POS, state->symbols.create($1), 0, $3); }
|
||||||
| '{' formals '}' ':' expr_function
|
| '{' formals '}' ':' expr_function
|
||||||
{ $$ = new ExprLambda(CUR_POS, data->validateFormals($2), $5); }
|
{ $$ = new ExprLambda(CUR_POS, state->validateFormals($2), $5); }
|
||||||
| '{' formals '}' '@' ID ':' expr_function
|
| '{' formals '}' '@' ID ':' expr_function
|
||||||
{
|
{
|
||||||
auto arg = data->symbols.create($5);
|
auto arg = state->symbols.create($5);
|
||||||
$$ = new ExprLambda(CUR_POS, arg, data->validateFormals($2, CUR_POS, arg), $7);
|
$$ = new ExprLambda(CUR_POS, arg, state->validateFormals($2, CUR_POS, arg), $7);
|
||||||
}
|
}
|
||||||
| ID '@' '{' formals '}' ':' expr_function
|
| ID '@' '{' formals '}' ':' expr_function
|
||||||
{
|
{
|
||||||
auto arg = data->symbols.create($1);
|
auto arg = state->symbols.create($1);
|
||||||
$$ = new ExprLambda(CUR_POS, arg, data->validateFormals($4, CUR_POS, arg), $7);
|
$$ = new ExprLambda(CUR_POS, arg, state->validateFormals($4, CUR_POS, arg), $7);
|
||||||
}
|
}
|
||||||
| ASSERT expr ';' expr_function
|
| ASSERT expr ';' expr_function
|
||||||
{ $$ = new ExprAssert(CUR_POS, $2, $4); }
|
{ $$ = new ExprAssert(CUR_POS, $2, $4); }
|
||||||
|
@ -411,7 +140,7 @@ expr_function
|
||||||
{ if (!$2->dynamicAttrs.empty())
|
{ if (!$2->dynamicAttrs.empty())
|
||||||
throw ParseError({
|
throw ParseError({
|
||||||
.msg = hintfmt("dynamic attributes not allowed in let"),
|
.msg = hintfmt("dynamic attributes not allowed in let"),
|
||||||
.errPos = data->state.positions[CUR_POS]
|
.errPos = state->state.positions[CUR_POS]
|
||||||
});
|
});
|
||||||
$$ = new ExprLet($2, $4);
|
$$ = new ExprLet($2, $4);
|
||||||
}
|
}
|
||||||
|
@ -425,24 +154,24 @@ expr_if
|
||||||
|
|
||||||
expr_op
|
expr_op
|
||||||
: '!' expr_op %prec NOT { $$ = new ExprOpNot($2); }
|
: '!' expr_op %prec NOT { $$ = new ExprOpNot($2); }
|
||||||
| '-' expr_op %prec NEGATE { $$ = new ExprCall(CUR_POS, new ExprVar(data->symbols.create("__sub")), {new ExprInt(0), $2}); }
|
| '-' expr_op %prec NEGATE { $$ = new ExprCall(CUR_POS, new ExprVar(state->symbols.create("__sub")), {new ExprInt(0), $2}); }
|
||||||
| expr_op EQ expr_op { $$ = new ExprOpEq($1, $3); }
|
| expr_op EQ expr_op { $$ = new ExprOpEq($1, $3); }
|
||||||
| expr_op NEQ expr_op { $$ = new ExprOpNEq($1, $3); }
|
| expr_op NEQ expr_op { $$ = new ExprOpNEq($1, $3); }
|
||||||
| expr_op '<' expr_op { $$ = new ExprCall(data->makeCurPos(@2), new ExprVar(data->symbols.create("__lessThan")), {$1, $3}); }
|
| expr_op '<' expr_op { $$ = new ExprCall(state->makeCurPos(@2), new ExprVar(state->symbols.create("__lessThan")), {$1, $3}); }
|
||||||
| expr_op LEQ expr_op { $$ = new ExprOpNot(new ExprCall(data->makeCurPos(@2), new ExprVar(data->symbols.create("__lessThan")), {$3, $1})); }
|
| expr_op LEQ expr_op { $$ = new ExprOpNot(new ExprCall(state->makeCurPos(@2), new ExprVar(state->symbols.create("__lessThan")), {$3, $1})); }
|
||||||
| expr_op '>' expr_op { $$ = new ExprCall(data->makeCurPos(@2), new ExprVar(data->symbols.create("__lessThan")), {$3, $1}); }
|
| expr_op '>' expr_op { $$ = new ExprCall(state->makeCurPos(@2), new ExprVar(state->symbols.create("__lessThan")), {$3, $1}); }
|
||||||
| expr_op GEQ expr_op { $$ = new ExprOpNot(new ExprCall(data->makeCurPos(@2), new ExprVar(data->symbols.create("__lessThan")), {$1, $3})); }
|
| expr_op GEQ expr_op { $$ = new ExprOpNot(new ExprCall(state->makeCurPos(@2), new ExprVar(state->symbols.create("__lessThan")), {$1, $3})); }
|
||||||
| expr_op AND expr_op { $$ = new ExprOpAnd(data->makeCurPos(@2), $1, $3); }
|
| expr_op AND expr_op { $$ = new ExprOpAnd(state->makeCurPos(@2), $1, $3); }
|
||||||
| expr_op OR expr_op { $$ = new ExprOpOr(data->makeCurPos(@2), $1, $3); }
|
| expr_op OR expr_op { $$ = new ExprOpOr(state->makeCurPos(@2), $1, $3); }
|
||||||
| expr_op IMPL expr_op { $$ = new ExprOpImpl(data->makeCurPos(@2), $1, $3); }
|
| expr_op IMPL expr_op { $$ = new ExprOpImpl(state->makeCurPos(@2), $1, $3); }
|
||||||
| expr_op UPDATE expr_op { $$ = new ExprOpUpdate(data->makeCurPos(@2), $1, $3); }
|
| expr_op UPDATE expr_op { $$ = new ExprOpUpdate(state->makeCurPos(@2), $1, $3); }
|
||||||
| expr_op '?' attrpath { $$ = new ExprOpHasAttr($1, std::move(*$3)); delete $3; }
|
| expr_op '?' attrpath { $$ = new ExprOpHasAttr($1, std::move(*$3)); delete $3; }
|
||||||
| expr_op '+' expr_op
|
| expr_op '+' expr_op
|
||||||
{ $$ = new ExprConcatStrings(data->makeCurPos(@2), false, new std::vector<std::pair<PosIdx, Expr *> >({{data->makeCurPos(@1), $1}, {data->makeCurPos(@3), $3}})); }
|
{ $$ = new ExprConcatStrings(state->makeCurPos(@2), false, new std::vector<std::pair<PosIdx, Expr *> >({{state->makeCurPos(@1), $1}, {state->makeCurPos(@3), $3}})); }
|
||||||
| expr_op '-' expr_op { $$ = new ExprCall(data->makeCurPos(@2), new ExprVar(data->symbols.create("__sub")), {$1, $3}); }
|
| expr_op '-' expr_op { $$ = new ExprCall(state->makeCurPos(@2), new ExprVar(state->symbols.create("__sub")), {$1, $3}); }
|
||||||
| expr_op '*' expr_op { $$ = new ExprCall(data->makeCurPos(@2), new ExprVar(data->symbols.create("__mul")), {$1, $3}); }
|
| expr_op '*' expr_op { $$ = new ExprCall(state->makeCurPos(@2), new ExprVar(state->symbols.create("__mul")), {$1, $3}); }
|
||||||
| expr_op '/' expr_op { $$ = new ExprCall(data->makeCurPos(@2), new ExprVar(data->symbols.create("__div")), {$1, $3}); }
|
| expr_op '/' expr_op { $$ = new ExprCall(state->makeCurPos(@2), new ExprVar(state->symbols.create("__div")), {$1, $3}); }
|
||||||
| expr_op CONCAT expr_op { $$ = new ExprOpConcatLists(data->makeCurPos(@2), $1, $3); }
|
| expr_op CONCAT expr_op { $$ = new ExprOpConcatLists(state->makeCurPos(@2), $1, $3); }
|
||||||
| expr_app
|
| expr_app
|
||||||
;
|
;
|
||||||
|
|
||||||
|
@ -465,7 +194,7 @@ expr_select
|
||||||
| /* Backwards compatibility: because Nixpkgs has a rarely used
|
| /* Backwards compatibility: because Nixpkgs has a rarely used
|
||||||
function named ‘or’, allow stuff like ‘map or [...]’. */
|
function named ‘or’, allow stuff like ‘map or [...]’. */
|
||||||
expr_simple OR_KW
|
expr_simple OR_KW
|
||||||
{ $$ = new ExprCall(CUR_POS, $1, {new ExprVar(CUR_POS, data->symbols.create("or"))}); }
|
{ $$ = new ExprCall(CUR_POS, $1, {new ExprVar(CUR_POS, state->symbols.create("or"))}); }
|
||||||
| expr_simple
|
| expr_simple
|
||||||
;
|
;
|
||||||
|
|
||||||
|
@ -475,25 +204,25 @@ expr_simple
|
||||||
if ($1.l == s.size() && strncmp($1.p, s.data(), s.size()) == 0)
|
if ($1.l == s.size() && strncmp($1.p, s.data(), s.size()) == 0)
|
||||||
$$ = new ExprPos(CUR_POS);
|
$$ = new ExprPos(CUR_POS);
|
||||||
else
|
else
|
||||||
$$ = new ExprVar(CUR_POS, data->symbols.create($1));
|
$$ = new ExprVar(CUR_POS, state->symbols.create($1));
|
||||||
}
|
}
|
||||||
| INT { $$ = new ExprInt($1); }
|
| INT { $$ = new ExprInt($1); }
|
||||||
| FLOAT { $$ = new ExprFloat($1); }
|
| FLOAT { $$ = new ExprFloat($1); }
|
||||||
| '"' string_parts '"' { $$ = $2; }
|
| '"' string_parts '"' { $$ = $2; }
|
||||||
| IND_STRING_OPEN ind_string_parts IND_STRING_CLOSE {
|
| IND_STRING_OPEN ind_string_parts IND_STRING_CLOSE {
|
||||||
$$ = data->stripIndentation(CUR_POS, std::move(*$2));
|
$$ = state->stripIndentation(CUR_POS, std::move(*$2));
|
||||||
delete $2;
|
delete $2;
|
||||||
}
|
}
|
||||||
| path_start PATH_END
|
| path_start PATH_END
|
||||||
| path_start string_parts_interpolated PATH_END {
|
| path_start string_parts_interpolated PATH_END {
|
||||||
$2->insert($2->begin(), {data->makeCurPos(@1), $1});
|
$2->insert($2->begin(), {state->makeCurPos(@1), $1});
|
||||||
$$ = new ExprConcatStrings(CUR_POS, false, $2);
|
$$ = new ExprConcatStrings(CUR_POS, false, $2);
|
||||||
}
|
}
|
||||||
| SPATH {
|
| SPATH {
|
||||||
std::string path($1.p + 1, $1.l - 2);
|
std::string path($1.p + 1, $1.l - 2);
|
||||||
$$ = new ExprCall(CUR_POS,
|
$$ = new ExprCall(CUR_POS,
|
||||||
new ExprVar(data->symbols.create("__findFile")),
|
new ExprVar(state->symbols.create("__findFile")),
|
||||||
{new ExprVar(data->symbols.create("__nixPath")),
|
{new ExprVar(state->symbols.create("__nixPath")),
|
||||||
new ExprString(std::move(path))});
|
new ExprString(std::move(path))});
|
||||||
}
|
}
|
||||||
| URI {
|
| URI {
|
||||||
|
@ -501,7 +230,7 @@ expr_simple
|
||||||
if (noURLLiterals)
|
if (noURLLiterals)
|
||||||
throw ParseError({
|
throw ParseError({
|
||||||
.msg = hintfmt("URL literals are disabled"),
|
.msg = hintfmt("URL literals are disabled"),
|
||||||
.errPos = data->state.positions[CUR_POS]
|
.errPos = state->state.positions[CUR_POS]
|
||||||
});
|
});
|
||||||
$$ = new ExprString(std::string($1));
|
$$ = new ExprString(std::string($1));
|
||||||
}
|
}
|
||||||
|
@ -509,7 +238,7 @@ expr_simple
|
||||||
/* Let expressions `let {..., body = ...}' are just desugared
|
/* Let expressions `let {..., body = ...}' are just desugared
|
||||||
into `(rec {..., body = ...}).body'. */
|
into `(rec {..., body = ...}).body'. */
|
||||||
| LET '{' binds '}'
|
| LET '{' binds '}'
|
||||||
{ $3->recursive = true; $$ = new ExprSelect(noPos, $3, data->symbols.create("body")); }
|
{ $3->recursive = true; $$ = new ExprSelect(noPos, $3, state->symbols.create("body")); }
|
||||||
| REC '{' binds '}'
|
| REC '{' binds '}'
|
||||||
{ $3->recursive = true; $$ = $3; }
|
{ $3->recursive = true; $$ = $3; }
|
||||||
| '{' binds '}'
|
| '{' binds '}'
|
||||||
|
@ -525,19 +254,19 @@ string_parts
|
||||||
|
|
||||||
string_parts_interpolated
|
string_parts_interpolated
|
||||||
: string_parts_interpolated STR
|
: string_parts_interpolated STR
|
||||||
{ $$ = $1; $1->emplace_back(data->makeCurPos(@2), new ExprString(std::string($2))); }
|
{ $$ = $1; $1->emplace_back(state->makeCurPos(@2), new ExprString(std::string($2))); }
|
||||||
| string_parts_interpolated DOLLAR_CURLY expr '}' { $$ = $1; $1->emplace_back(data->makeCurPos(@2), $3); }
|
| string_parts_interpolated DOLLAR_CURLY expr '}' { $$ = $1; $1->emplace_back(state->makeCurPos(@2), $3); }
|
||||||
| DOLLAR_CURLY expr '}' { $$ = new std::vector<std::pair<PosIdx, Expr *>>; $$->emplace_back(data->makeCurPos(@1), $2); }
|
| DOLLAR_CURLY expr '}' { $$ = new std::vector<std::pair<PosIdx, Expr *>>; $$->emplace_back(state->makeCurPos(@1), $2); }
|
||||||
| STR DOLLAR_CURLY expr '}' {
|
| STR DOLLAR_CURLY expr '}' {
|
||||||
$$ = new std::vector<std::pair<PosIdx, Expr *>>;
|
$$ = new std::vector<std::pair<PosIdx, Expr *>>;
|
||||||
$$->emplace_back(data->makeCurPos(@1), new ExprString(std::string($1)));
|
$$->emplace_back(state->makeCurPos(@1), new ExprString(std::string($1)));
|
||||||
$$->emplace_back(data->makeCurPos(@2), $3);
|
$$->emplace_back(state->makeCurPos(@2), $3);
|
||||||
}
|
}
|
||||||
;
|
;
|
||||||
|
|
||||||
path_start
|
path_start
|
||||||
: PATH {
|
: PATH {
|
||||||
Path path(absPath({$1.p, $1.l}, data->basePath.path.abs()));
|
Path path(absPath({$1.p, $1.l}, state->basePath.path.abs()));
|
||||||
/* add back in the trailing '/' to the first segment */
|
/* add back in the trailing '/' to the first segment */
|
||||||
if ($1.p[$1.l-1] == '/' && $1.l > 1)
|
if ($1.p[$1.l-1] == '/' && $1.l > 1)
|
||||||
path += "/";
|
path += "/";
|
||||||
|
@ -556,19 +285,19 @@ path_start
|
||||||
;
|
;
|
||||||
|
|
||||||
ind_string_parts
|
ind_string_parts
|
||||||
: ind_string_parts IND_STR { $$ = $1; $1->emplace_back(data->makeCurPos(@2), $2); }
|
: ind_string_parts IND_STR { $$ = $1; $1->emplace_back(state->makeCurPos(@2), $2); }
|
||||||
| ind_string_parts DOLLAR_CURLY expr '}' { $$ = $1; $1->emplace_back(data->makeCurPos(@2), $3); }
|
| ind_string_parts DOLLAR_CURLY expr '}' { $$ = $1; $1->emplace_back(state->makeCurPos(@2), $3); }
|
||||||
| { $$ = new std::vector<std::pair<PosIdx, std::variant<Expr *, StringToken>>>; }
|
| { $$ = new std::vector<std::pair<PosIdx, std::variant<Expr *, StringToken>>>; }
|
||||||
;
|
;
|
||||||
|
|
||||||
binds
|
binds
|
||||||
: binds attrpath '=' expr ';' { $$ = $1; data->addAttr($$, std::move(*$2), $4, data->makeCurPos(@2)); delete $2; }
|
: binds attrpath '=' expr ';' { $$ = $1; state->addAttr($$, std::move(*$2), $4, state->makeCurPos(@2)); delete $2; }
|
||||||
| binds INHERIT attrs ';'
|
| binds INHERIT attrs ';'
|
||||||
{ $$ = $1;
|
{ $$ = $1;
|
||||||
for (auto & i : *$3) {
|
for (auto & i : *$3) {
|
||||||
if ($$->attrs.find(i.symbol) != $$->attrs.end())
|
if ($$->attrs.find(i.symbol) != $$->attrs.end())
|
||||||
data->dupAttr(i.symbol, data->makeCurPos(@3), $$->attrs[i.symbol].pos);
|
state->dupAttr(i.symbol, state->makeCurPos(@3), $$->attrs[i.symbol].pos);
|
||||||
auto pos = data->makeCurPos(@3);
|
auto pos = state->makeCurPos(@3);
|
||||||
$$->attrs.emplace(i.symbol, ExprAttrs::AttrDef(new ExprVar(CUR_POS, i.symbol), pos, true));
|
$$->attrs.emplace(i.symbol, ExprAttrs::AttrDef(new ExprVar(CUR_POS, i.symbol), pos, true));
|
||||||
}
|
}
|
||||||
delete $3;
|
delete $3;
|
||||||
|
@ -578,48 +307,48 @@ binds
|
||||||
/* !!! Should ensure sharing of the expression in $4. */
|
/* !!! Should ensure sharing of the expression in $4. */
|
||||||
for (auto & i : *$6) {
|
for (auto & i : *$6) {
|
||||||
if ($$->attrs.find(i.symbol) != $$->attrs.end())
|
if ($$->attrs.find(i.symbol) != $$->attrs.end())
|
||||||
data->dupAttr(i.symbol, data->makeCurPos(@6), $$->attrs[i.symbol].pos);
|
state->dupAttr(i.symbol, state->makeCurPos(@6), $$->attrs[i.symbol].pos);
|
||||||
$$->attrs.emplace(i.symbol, ExprAttrs::AttrDef(new ExprSelect(CUR_POS, $4, i.symbol), data->makeCurPos(@6)));
|
$$->attrs.emplace(i.symbol, ExprAttrs::AttrDef(new ExprSelect(CUR_POS, $4, i.symbol), state->makeCurPos(@6)));
|
||||||
}
|
}
|
||||||
delete $6;
|
delete $6;
|
||||||
}
|
}
|
||||||
| { $$ = new ExprAttrs(data->makeCurPos(@0)); }
|
| { $$ = new ExprAttrs(state->makeCurPos(@0)); }
|
||||||
;
|
;
|
||||||
|
|
||||||
attrs
|
attrs
|
||||||
: attrs attr { $$ = $1; $1->push_back(AttrName(data->symbols.create($2))); }
|
: attrs attr { $$ = $1; $1->push_back(AttrName(state->symbols.create($2))); }
|
||||||
| attrs string_attr
|
| attrs string_attr
|
||||||
{ $$ = $1;
|
{ $$ = $1;
|
||||||
ExprString * str = dynamic_cast<ExprString *>($2);
|
ExprString * str = dynamic_cast<ExprString *>($2);
|
||||||
if (str) {
|
if (str) {
|
||||||
$$->push_back(AttrName(data->symbols.create(str->s)));
|
$$->push_back(AttrName(state->symbols.create(str->s)));
|
||||||
delete str;
|
delete str;
|
||||||
} else
|
} else
|
||||||
throw ParseError({
|
throw ParseError({
|
||||||
.msg = hintfmt("dynamic attributes not allowed in inherit"),
|
.msg = hintfmt("dynamic attributes not allowed in inherit"),
|
||||||
.errPos = data->state.positions[data->makeCurPos(@2)]
|
.errPos = state->state.positions[state->makeCurPos(@2)]
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
| { $$ = new AttrPath; }
|
| { $$ = new AttrPath; }
|
||||||
;
|
;
|
||||||
|
|
||||||
attrpath
|
attrpath
|
||||||
: attrpath '.' attr { $$ = $1; $1->push_back(AttrName(data->symbols.create($3))); }
|
: attrpath '.' attr { $$ = $1; $1->push_back(AttrName(state->symbols.create($3))); }
|
||||||
| attrpath '.' string_attr
|
| attrpath '.' string_attr
|
||||||
{ $$ = $1;
|
{ $$ = $1;
|
||||||
ExprString * str = dynamic_cast<ExprString *>($3);
|
ExprString * str = dynamic_cast<ExprString *>($3);
|
||||||
if (str) {
|
if (str) {
|
||||||
$$->push_back(AttrName(data->symbols.create(str->s)));
|
$$->push_back(AttrName(state->symbols.create(str->s)));
|
||||||
delete str;
|
delete str;
|
||||||
} else
|
} else
|
||||||
$$->push_back(AttrName($3));
|
$$->push_back(AttrName($3));
|
||||||
}
|
}
|
||||||
| attr { $$ = new std::vector<AttrName>; $$->push_back(AttrName(data->symbols.create($1))); }
|
| attr { $$ = new std::vector<AttrName>; $$->push_back(AttrName(state->symbols.create($1))); }
|
||||||
| string_attr
|
| string_attr
|
||||||
{ $$ = new std::vector<AttrName>;
|
{ $$ = new std::vector<AttrName>;
|
||||||
ExprString *str = dynamic_cast<ExprString *>($1);
|
ExprString *str = dynamic_cast<ExprString *>($1);
|
||||||
if (str) {
|
if (str) {
|
||||||
$$->push_back(AttrName(data->symbols.create(str->s)));
|
$$->push_back(AttrName(state->symbols.create(str->s)));
|
||||||
delete str;
|
delete str;
|
||||||
} else
|
} else
|
||||||
$$->push_back(AttrName($1));
|
$$->push_back(AttrName($1));
|
||||||
|
@ -653,8 +382,8 @@ formals
|
||||||
;
|
;
|
||||||
|
|
||||||
formal
|
formal
|
||||||
: ID { $$ = new Formal{CUR_POS, data->symbols.create($1), 0}; }
|
: ID { $$ = new Formal{CUR_POS, state->symbols.create($1), 0}; }
|
||||||
| ID '?' expr { $$ = new Formal{CUR_POS, data->symbols.create($1), $3}; }
|
| ID '?' expr { $$ = new Formal{CUR_POS, state->symbols.create($1), $3}; }
|
||||||
;
|
;
|
||||||
|
|
||||||
%%
|
%%
|
||||||
|
@ -683,7 +412,7 @@ Expr * EvalState::parse(
|
||||||
std::shared_ptr<StaticEnv> & staticEnv)
|
std::shared_ptr<StaticEnv> & staticEnv)
|
||||||
{
|
{
|
||||||
yyscan_t scanner;
|
yyscan_t scanner;
|
||||||
ParseData data {
|
ParserState state {
|
||||||
.state = *this,
|
.state = *this,
|
||||||
.symbols = symbols,
|
.symbols = symbols,
|
||||||
.basePath = basePath,
|
.basePath = basePath,
|
||||||
|
@ -694,11 +423,11 @@ Expr * EvalState::parse(
|
||||||
Finally _destroy([&] { yylex_destroy(scanner); });
|
Finally _destroy([&] { yylex_destroy(scanner); });
|
||||||
|
|
||||||
yy_scan_buffer(text, length, scanner);
|
yy_scan_buffer(text, length, scanner);
|
||||||
yyparse(scanner, &data);
|
yyparse(scanner, &state);
|
||||||
|
|
||||||
data.result->bindVars(*this, staticEnv);
|
state.result->bindVars(*this, staticEnv);
|
||||||
|
|
||||||
return data.result;
|
return state.result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue