From f4c5531d928caadee75d66bba40994f55f839be7 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Mon, 24 Jul 2006 16:35:34 +0000 Subject: [PATCH] * New language feature: domain checks, which check whether a function argument has a valid value, i.e., is in a certain domain. E.g., { foo : [true false] , bar : ["a" "b" "c"] }: ... This previously could be done using assertions, but domain checks will allow the buildfarm to automatically extract the configuration space from functions. --- src/libexpr/eval.cc | 35 +++++++++++++++++++++------ src/libexpr/nixexpr.cc | 9 ++++--- src/libexpr/parser.y | 2 +- tests/lang/parse-fail-undef-var-2.nix | 7 ++++++ 4 files changed, 40 insertions(+), 13 deletions(-) create mode 100644 tests/lang/parse-fail-undef-var-2.nix diff --git a/src/libexpr/eval.cc b/src/libexpr/eval.cc index e45de729..06bf671e 100644 --- a/src/libexpr/eval.cc +++ b/src/libexpr/eval.cc @@ -22,7 +22,8 @@ void EvalState::addPrimOp(const string & name, /* Substitute an argument set into the body of a function. */ -static Expr substArgs(Expr body, ATermList formals, Expr arg) +static Expr substArgs(EvalState & state, + Expr body, ATermList formals, Expr arg) { unsigned int nrFormals = ATgetLength(formals); ATermMap subs(nrFormals); @@ -37,17 +38,35 @@ static Expr substArgs(Expr body, ATermList formals, Expr arg) ATermVector defsUsed; ATermList recAttrs = ATempty; for (ATermIterator i(formals); i; ++i) { - Expr name, def; DefaultValue def2; ATerm dummy; - if (!matchFormal(*i, name, dummy, def2)) abort(); /* can't happen */ - if (!matchDefaultValue(def2, def)) def = 0; - if (subs[name] == 0) { + Expr name, def; + ValidValues valids2; + DefaultValue def2; + if (!matchFormal(*i, name, valids2, def2)) abort(); /* can't happen */ + + Expr value = subs[name]; + + if (value == 0) { + if (!matchDefaultValue(def2, def)) def = 0; if (def == 0) throw TypeError(format("the argument named `%1%' required by the function is missing") % aterm2String(name)); + value = def; defsUsed.push_back(name); recAttrs = ATinsert(recAttrs, makeBind(name, def, makeNoPos())); } - /* !!! check that the argument are in the valid values list, - if present */ + + ATermList valids; + if (matchValidValues(valids2, valids)) { + bool found = false; + for (ATermIterator j(valids); j; ++j) { + Expr v = evalExpr(state, *j); + if (value == v) { + found = true; + break; + } + } + if (!found) throw TypeError(format("the argument named `%1%' has an illegal value") + % aterm2String(name)); + } } /* Make a recursive attribute set out of the (argument-name, @@ -340,7 +359,7 @@ Expr evalExpr2(EvalState & state, Expr e) else if (matchFunction(e1, formals, e4, pos)) { e2 = evalExpr(state, e2); try { - return evalExpr(state, substArgs(e4, formals, e2)); + return evalExpr(state, substArgs(state, e4, formals, e2)); } catch (Error & e) { e.addPrefix(format("while evaluating the function at %1%:\n") % showPos(pos)); diff --git a/src/libexpr/nixexpr.cc b/src/libexpr/nixexpr.cc index cbcaec58..56b23cc6 100644 --- a/src/libexpr/nixexpr.cc +++ b/src/libexpr/nixexpr.cc @@ -220,15 +220,16 @@ static void checkVarDefs2(set & done, const ATermMap & defs, Expr e) else if (matchFunction(e, formals, body, pos)) { ATermMap defs2(defs); for (ATermIterator i(formals); i; ++i) { - Expr d1, d2; + ATerm d1, d2; if (!matchFormal(*i, name, d1, d2)) abort(); defs2.set(name, (ATerm) ATempty); } for (ATermIterator i(formals); i; ++i) { - Expr dummy, deflt; + ATerm valids, deflt; set done2; - if (matchFormal(*i, name, dummy, deflt)) /* !!! check dummy */ - checkVarDefs2(done2, defs2, deflt); + matchFormal(*i, name, valids, deflt); + checkVarDefs2(done, defs, valids); + checkVarDefs2(done2, defs2, deflt); } set done2; checkVarDefs2(done2, defs2, body); diff --git a/src/libexpr/parser.y b/src/libexpr/parser.y index b28aa276..c1e3b48a 100644 --- a/src/libexpr/parser.y +++ b/src/libexpr/parser.y @@ -206,7 +206,7 @@ formals formal : ID { $$ = makeFormal($1, makeUnrestrictedValues(), makeNoDefaultValue()); } -// | ID ':' '[' expr_list ']' { $$ = makeDefFormal($1, $3); } + | ID ':' '[' expr_list ']' { $$ = makeFormal($1, makeValidValues($4), makeNoDefaultValue()); } | ID '?' expr { $$ = makeFormal($1, makeUnrestrictedValues(), makeDefaultValue($3)); } ; diff --git a/tests/lang/parse-fail-undef-var-2.nix b/tests/lang/parse-fail-undef-var-2.nix new file mode 100644 index 00000000..c10a52b1 --- /dev/null +++ b/tests/lang/parse-fail-undef-var-2.nix @@ -0,0 +1,7 @@ +let { + + f = {x, y : ["baz" "bar" z "bat"]}: x + y; + + body = f {x = "foo"; y = "bar";}; + +}