forked from lix-project/lix
libexpr/nix2: Make null, true and false proper keywords
They cannot be used as identifiers anymore, and thus cannot be
overridden either.
Change-Id: I5f0e8252b4516127bda5f42403015fc837902b57
This commit is contained in:
parent
791c0e4468
commit
b5bb2b9034
|
@ -886,6 +886,12 @@ Value * ExprString::maybeThunk(EvalState & state, Env & env)
|
||||||
return &v;
|
return &v;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Value * ExprLiteral::maybeThunk(EvalState & state, Env & env)
|
||||||
|
{
|
||||||
|
state.nrAvoided++;
|
||||||
|
return &v;
|
||||||
|
}
|
||||||
|
|
||||||
Value * ExprInt::maybeThunk(EvalState & state, Env & env)
|
Value * ExprInt::maybeThunk(EvalState & state, Env & env)
|
||||||
{
|
{
|
||||||
state.nrAvoided++;
|
state.nrAvoided++;
|
||||||
|
@ -1024,6 +1030,10 @@ void Expr::eval(EvalState & state, Env & env, Value & v)
|
||||||
abort();
|
abort();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ExprLiteral::eval(EvalState & state, Env & env, Value & v)
|
||||||
|
{
|
||||||
|
v = this->v;
|
||||||
|
}
|
||||||
|
|
||||||
void ExprInt::eval(EvalState & state, Env & env, Value & v)
|
void ExprInt::eval(EvalState & state, Env & env, Value & v)
|
||||||
{
|
{
|
||||||
|
|
|
@ -752,6 +752,7 @@ private:
|
||||||
friend struct ExprOpConcatLists;
|
friend struct ExprOpConcatLists;
|
||||||
friend struct ExprVar;
|
friend struct ExprVar;
|
||||||
friend struct ExprString;
|
friend struct ExprString;
|
||||||
|
friend struct ExprLiteral;
|
||||||
friend struct ExprInt;
|
friend struct ExprInt;
|
||||||
friend struct ExprFloat;
|
friend struct ExprFloat;
|
||||||
friend struct ExprPath;
|
friend struct ExprPath;
|
||||||
|
|
|
@ -34,6 +34,19 @@ void Expr::show(const SymbolTable & symbols, std::ostream & str) const
|
||||||
abort();
|
abort();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ExprLiteral::show(const SymbolTable & symbols, std::ostream & str) const
|
||||||
|
{
|
||||||
|
// The value printer is useless because it may force values and thus needs state and stuff.
|
||||||
|
// These are the only literals we currently need it for
|
||||||
|
if (v.type() == nBool) {
|
||||||
|
str << (v.boolean ? "true" : "false");
|
||||||
|
} else if (v.type() == nNull) {
|
||||||
|
str << "null";
|
||||||
|
} else {
|
||||||
|
abort();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void ExprInt::show(const SymbolTable & symbols, std::ostream & str) const
|
void ExprInt::show(const SymbolTable & symbols, std::ostream & str) const
|
||||||
{
|
{
|
||||||
str << n;
|
str << n;
|
||||||
|
@ -300,6 +313,12 @@ void Expr::bindVars(EvalState & es, const std::shared_ptr<const StaticEnv> & env
|
||||||
abort();
|
abort();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ExprLiteral::bindVars(EvalState & es, const std::shared_ptr<const StaticEnv> & env)
|
||||||
|
{
|
||||||
|
if (es.debugRepl)
|
||||||
|
es.exprEnvs.insert(std::make_pair(this, env));
|
||||||
|
}
|
||||||
|
|
||||||
void ExprInt::bindVars(EvalState & es, const std::shared_ptr<const StaticEnv> & env)
|
void ExprInt::bindVars(EvalState & es, const std::shared_ptr<const StaticEnv> & env)
|
||||||
{
|
{
|
||||||
if (es.debugRepl)
|
if (es.debugRepl)
|
||||||
|
|
|
@ -71,6 +71,17 @@ public:
|
||||||
void eval(EvalState & state, Env & env, Value & v) override; \
|
void eval(EvalState & state, Env & env, Value & v) override; \
|
||||||
void bindVars(EvalState & es, const std::shared_ptr<const StaticEnv> & env) override;
|
void bindVars(EvalState & es, const std::shared_ptr<const StaticEnv> & env) override;
|
||||||
|
|
||||||
|
// Only used in nix-lang v2.
|
||||||
|
// in v1 null, true and false are just built-in constants
|
||||||
|
struct ExprLiteral : Expr
|
||||||
|
{
|
||||||
|
Value v;
|
||||||
|
ExprLiteral() { v.mkNull(); }
|
||||||
|
ExprLiteral(bool b) { v.mkBool(b); };
|
||||||
|
Value * maybeThunk(EvalState & state, Env & env) override;
|
||||||
|
COMMON_METHODS
|
||||||
|
};
|
||||||
|
|
||||||
struct ExprInt : Expr
|
struct ExprInt : Expr
|
||||||
{
|
{
|
||||||
NixInt n;
|
NixInt n;
|
||||||
|
|
|
@ -76,6 +76,9 @@ struct kw_in : _keyword<TAO_PEGTL_STRING("in")> {};
|
||||||
struct kw_rec : _keyword<TAO_PEGTL_STRING("rec")> {};
|
struct kw_rec : _keyword<TAO_PEGTL_STRING("rec")> {};
|
||||||
struct kw_inherit : _keyword<TAO_PEGTL_STRING("inherit")> {};
|
struct kw_inherit : _keyword<TAO_PEGTL_STRING("inherit")> {};
|
||||||
struct kw_or : _keyword<TAO_PEGTL_STRING("or")> {};
|
struct kw_or : _keyword<TAO_PEGTL_STRING("or")> {};
|
||||||
|
struct kw_null : _keyword<TAO_PEGTL_STRING("null")> {};
|
||||||
|
struct kw_true : _keyword<TAO_PEGTL_STRING("true")> {};
|
||||||
|
struct kw_false : _keyword<TAO_PEGTL_STRING("false")> {};
|
||||||
|
|
||||||
// `-` can be a unary prefix op, a binary infix op, or the first character
|
// `-` can be a unary prefix op, a binary infix op, or the first character
|
||||||
// of a path or -> (ex 1->1--1)
|
// of a path or -> (ex 1->1--1)
|
||||||
|
@ -98,7 +101,10 @@ struct _not_at_any_keyword : minus<
|
||||||
TAO_PEGTL_STRING("rec"),
|
TAO_PEGTL_STRING("rec"),
|
||||||
TAO_PEGTL_STRING("if"),
|
TAO_PEGTL_STRING("if"),
|
||||||
TAO_PEGTL_STRING("in"),
|
TAO_PEGTL_STRING("in"),
|
||||||
TAO_PEGTL_STRING("or")
|
TAO_PEGTL_STRING("or"),
|
||||||
|
TAO_PEGTL_STRING("null"),
|
||||||
|
TAO_PEGTL_STRING("true"),
|
||||||
|
TAO_PEGTL_STRING("false")
|
||||||
>
|
>
|
||||||
> {};
|
> {};
|
||||||
|
|
||||||
|
@ -440,6 +446,9 @@ struct _expr {
|
||||||
|
|
||||||
struct select;
|
struct select;
|
||||||
|
|
||||||
|
struct null : semantic, t::kw_null {};
|
||||||
|
struct _true : semantic, t::kw_true {};
|
||||||
|
struct _false : semantic, t::kw_false {};
|
||||||
struct id : semantic, t::identifier {};
|
struct id : semantic, t::identifier {};
|
||||||
struct int_ : semantic, t::integer {};
|
struct int_ : semantic, t::integer {};
|
||||||
struct float_ : semantic, t::floating {};
|
struct float_ : semantic, t::floating {};
|
||||||
|
@ -459,6 +468,9 @@ struct _expr {
|
||||||
> {};
|
> {};
|
||||||
|
|
||||||
struct _simple : sor<
|
struct _simple : sor<
|
||||||
|
null,
|
||||||
|
_true,
|
||||||
|
_false,
|
||||||
id,
|
id,
|
||||||
int_,
|
int_,
|
||||||
float_,
|
float_,
|
||||||
|
|
|
@ -388,6 +388,24 @@ template<> struct BuildAST<grammar::v2::binding> {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
template<> struct BuildAST<grammar::v2::expr::null> {
|
||||||
|
static void apply(const auto & in, ExprState & s, State & ps) {
|
||||||
|
s.pushExpr<ExprLiteral>(ps.at(in));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template<> struct BuildAST<grammar::v2::expr::_true> {
|
||||||
|
static void apply(const auto & in, ExprState & s, State & ps) {
|
||||||
|
s.pushExpr<ExprLiteral>(ps.at(in), true);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template<> struct BuildAST<grammar::v2::expr::_false> {
|
||||||
|
static void apply(const auto & in, ExprState & s, State & ps) {
|
||||||
|
s.pushExpr<ExprLiteral>(ps.at(in), false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
template<> struct BuildAST<grammar::v2::expr::id> {
|
template<> struct BuildAST<grammar::v2::expr::id> {
|
||||||
static void apply(const auto & in, ExprState & s, State & ps) {
|
static void apply(const auto & in, ExprState & s, State & ps) {
|
||||||
if (in.string_view() == "__curPos")
|
if (in.string_view() == "__curPos")
|
||||||
|
|
|
@ -195,6 +195,7 @@ constexpr std::array<ExperimentalFeatureDetails, numXpFeatures> xpFeatureDetails
|
||||||
- Number/path token boundaries have been cleaned up.
|
- Number/path token boundaries have been cleaned up.
|
||||||
- `3a` is a parse error instead of a function application.
|
- `3a` is a parse error instead of a function application.
|
||||||
- Ambiguous path/division syntax like `1.0/3` now throws a parse error instead of parsing as path.
|
- Ambiguous path/division syntax like `1.0/3` now throws a parse error instead of parsing as path.
|
||||||
|
- `null`, `true` and `false` are now proper keywords which cannot simply be shadowed by variables.
|
||||||
- Identifiers have been cleaned up.
|
- Identifiers have been cleaned up.
|
||||||
- String identifiers (`"foo bar"`) are now disallowed in let bindings, as there is no way to reference to these variables anyways.
|
- String identifiers (`"foo bar"`) are now disallowed in let bindings, as there is no way to reference to these variables anyways.
|
||||||
- This includes such names in `inherit` within let bindings
|
- This includes such names in `inherit` within let bindings
|
||||||
|
|
|
@ -278,4 +278,16 @@ namespace nix {
|
||||||
ASSERT_THAT(v, IsStringEq("ac"));
|
ASSERT_THAT(v, IsStringEq("ac"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_F(TrivialExpressionTest, literalsOverride) {
|
||||||
|
auto v = eval("let true = \"true\"; false = \"false\"; null = \"null\"; in true + false + null");
|
||||||
|
ASSERT_THAT(v, IsStringEq("truefalsenull"));
|
||||||
|
|
||||||
|
FeatureSettings mockFeatureSettings;
|
||||||
|
mockFeatureSettings.set("experimental-features", "nix-lang2");
|
||||||
|
// Need separate tests for each because parser fails at first error
|
||||||
|
ASSERT_THROW(eval("let true = \"true\"; in true", true, mockFeatureSettings), Error);
|
||||||
|
ASSERT_THROW(eval("let false = \"false\"; in false", true, mockFeatureSettings), Error);
|
||||||
|
ASSERT_THROW(eval("let null = \"null\"; in null", true, mockFeatureSettings), Error);
|
||||||
|
}
|
||||||
|
|
||||||
} /* namespace nix */
|
} /* namespace nix */
|
||||||
|
|
Loading…
Reference in a new issue