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;
|
||||
}
|
||||
|
||||
Value * ExprLiteral::maybeThunk(EvalState & state, Env & env)
|
||||
{
|
||||
state.nrAvoided++;
|
||||
return &v;
|
||||
}
|
||||
|
||||
Value * ExprInt::maybeThunk(EvalState & state, Env & env)
|
||||
{
|
||||
state.nrAvoided++;
|
||||
|
@ -1024,6 +1030,10 @@ void Expr::eval(EvalState & state, Env & env, Value & v)
|
|||
abort();
|
||||
}
|
||||
|
||||
void ExprLiteral::eval(EvalState & state, Env & env, Value & v)
|
||||
{
|
||||
v = this->v;
|
||||
}
|
||||
|
||||
void ExprInt::eval(EvalState & state, Env & env, Value & v)
|
||||
{
|
||||
|
|
|
@ -752,6 +752,7 @@ private:
|
|||
friend struct ExprOpConcatLists;
|
||||
friend struct ExprVar;
|
||||
friend struct ExprString;
|
||||
friend struct ExprLiteral;
|
||||
friend struct ExprInt;
|
||||
friend struct ExprFloat;
|
||||
friend struct ExprPath;
|
||||
|
|
|
@ -34,6 +34,19 @@ void Expr::show(const SymbolTable & symbols, std::ostream & str) const
|
|||
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
|
||||
{
|
||||
str << n;
|
||||
|
@ -300,6 +313,12 @@ void Expr::bindVars(EvalState & es, const std::shared_ptr<const StaticEnv> & env
|
|||
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)
|
||||
{
|
||||
if (es.debugRepl)
|
||||
|
|
|
@ -71,6 +71,17 @@ public:
|
|||
void eval(EvalState & state, Env & env, Value & v) 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
|
||||
{
|
||||
NixInt n;
|
||||
|
|
|
@ -76,6 +76,9 @@ struct kw_in : _keyword<TAO_PEGTL_STRING("in")> {};
|
|||
struct kw_rec : _keyword<TAO_PEGTL_STRING("rec")> {};
|
||||
struct kw_inherit : _keyword<TAO_PEGTL_STRING("inherit")> {};
|
||||
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
|
||||
// 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("if"),
|
||||
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 null : semantic, t::kw_null {};
|
||||
struct _true : semantic, t::kw_true {};
|
||||
struct _false : semantic, t::kw_false {};
|
||||
struct id : semantic, t::identifier {};
|
||||
struct int_ : semantic, t::integer {};
|
||||
struct float_ : semantic, t::floating {};
|
||||
|
@ -459,6 +468,9 @@ struct _expr {
|
|||
> {};
|
||||
|
||||
struct _simple : sor<
|
||||
null,
|
||||
_true,
|
||||
_false,
|
||||
id,
|
||||
int_,
|
||||
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> {
|
||||
static void apply(const auto & in, ExprState & s, State & ps) {
|
||||
if (in.string_view() == "__curPos")
|
||||
|
|
|
@ -195,6 +195,7 @@ constexpr std::array<ExperimentalFeatureDetails, numXpFeatures> xpFeatureDetails
|
|||
- Number/path token boundaries have been cleaned up.
|
||||
- `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.
|
||||
- `null`, `true` and `false` are now proper keywords which cannot simply be shadowed by variables.
|
||||
- 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.
|
||||
- This includes such names in `inherit` within let bindings
|
||||
|
|
|
@ -278,4 +278,16 @@ namespace nix {
|
|||
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 */
|
||||
|
|
Loading…
Reference in a new issue