* @-patterns as in Haskell. For instance, in a function definition
f = args @ {x, y, z}: ...; `args' refers to the argument as a whole, which is further pattern-matched against the attribute set pattern {x, y, z}.
This commit is contained in:
parent
e818838412
commit
1b962fc720
10 changed files with 106 additions and 46 deletions
|
@ -74,11 +74,16 @@ LocalNoInline(void addErrorPrefix(Error & e, const char * s, const string & s2,
|
|||
}
|
||||
|
||||
|
||||
/* Pattern-match `pat' against `arg'. The result is a set of
|
||||
substitutions (`subs') and a set of recursive substitutions
|
||||
(`subsRecursive'). The latter can refer to the variables bound by
|
||||
both `subs' and `subsRecursive'. */
|
||||
static void patternMatch(EvalState & state,
|
||||
Pattern pat, Expr arg, ATermMap & subs)
|
||||
Pattern pat, Expr arg, ATermMap & subs, ATermMap & subsRecursive)
|
||||
{
|
||||
ATerm name;
|
||||
ATermList formals;
|
||||
Pattern pat1, pat2;
|
||||
|
||||
if (matchVarPat(pat, name))
|
||||
subs.set(name, arg);
|
||||
|
@ -87,60 +92,45 @@ static void patternMatch(EvalState & state,
|
|||
|
||||
arg = evalExpr(state, arg);
|
||||
|
||||
unsigned int nrFormals = ATgetLength(formals);
|
||||
/* Get the actual arguments. */
|
||||
ATermMap attrs;
|
||||
queryAllAttrs(arg, attrs);
|
||||
unsigned int nrAttrs = attrs.size();
|
||||
|
||||
/* Get the actual arguments and put them in the substitution.
|
||||
!!! shouldn't do this once we add `...'.*/
|
||||
ATermMap args;
|
||||
queryAllAttrs(arg, args);
|
||||
for (ATermMap::const_iterator i = args.begin(); i != args.end(); ++i)
|
||||
subs.set(i->key, i->value);
|
||||
|
||||
/* Get the formal arguments. */
|
||||
ATermVector defsUsed;
|
||||
ATermList recAttrs = ATempty;
|
||||
/* For each formal argument, get the actual argument. If
|
||||
there is no matching actual argument but the formal
|
||||
argument has a default, use the default. */
|
||||
unsigned int attrsUsed = 0;
|
||||
for (ATermIterator i(formals); i; ++i) {
|
||||
Expr name, def;
|
||||
DefaultValue def2;
|
||||
if (!matchFormal(*i, name, def2)) abort(); /* can't happen */
|
||||
|
||||
Expr value = subs[name];
|
||||
|
||||
Expr value = attrs[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()));
|
||||
subsRecursive.set(name, def);
|
||||
} else {
|
||||
attrsUsed++;
|
||||
attrs.remove(name);
|
||||
subs.set(name, value);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/* Make a recursive attribute set out of the (argument-name,
|
||||
value) tuples. This is so that we can support default
|
||||
parameters that refer to each other, e.g. ({x, y ? x + x}:
|
||||
y) {x = "foo";} evaluates to "foofoo". */
|
||||
if (defsUsed.size() != 0) {
|
||||
for (ATermMap::const_iterator i = args.begin(); i != args.end(); ++i)
|
||||
recAttrs = ATinsert(recAttrs, makeBind(i->key, i->value, makeNoPos()));
|
||||
Expr rec = makeRec(recAttrs, ATempty);
|
||||
for (ATermVector::iterator i = defsUsed.begin(); i != defsUsed.end(); ++i)
|
||||
subs.set(*i, makeSelect(rec, *i));
|
||||
}
|
||||
|
||||
if (subs.size() != nrFormals) {
|
||||
/* One or more actual arguments were not declared as
|
||||
formal arguments. Find out which. */
|
||||
for (ATermIterator i(formals); i; ++i) {
|
||||
Expr name; ATerm d1;
|
||||
if (!matchFormal(*i, name, d1)) abort();
|
||||
subs.remove(name);
|
||||
}
|
||||
|
||||
/* Check that each actual argument is listed as a formal
|
||||
argument. */
|
||||
if (attrsUsed != nrAttrs)
|
||||
throw TypeError(format("the function does not expect an argument named `%1%'")
|
||||
% aterm2String(subs.begin()->key));
|
||||
}
|
||||
% aterm2String(attrs.begin()->key));
|
||||
}
|
||||
|
||||
else if (matchAtPat(pat, pat1, pat2)) {
|
||||
patternMatch(state, pat1, arg, subs, subsRecursive);
|
||||
patternMatch(state, pat2, arg, subs, subsRecursive);
|
||||
}
|
||||
|
||||
else abort();
|
||||
|
@ -151,9 +141,24 @@ static void patternMatch(EvalState & state,
|
|||
static Expr substArgs(EvalState & state,
|
||||
Expr body, Pattern pat, Expr arg)
|
||||
{
|
||||
ATermMap subs(16);
|
||||
ATermMap subs(16), subsRecursive(16);
|
||||
|
||||
patternMatch(state, pat, arg, subs);
|
||||
patternMatch(state, pat, arg, subs, subsRecursive);
|
||||
|
||||
/* If we used any default values, make a recursive attribute set
|
||||
out of the (argument-name, value) tuples. This is so that we
|
||||
can support default values that refer to each other, e.g. ({x,
|
||||
y ? x + x}: y) {x = "foo";} evaluates to "foofoo". */
|
||||
if (subsRecursive.size() != 0) {
|
||||
ATermList recAttrs = ATempty;
|
||||
foreach (ATermMap::const_iterator, i, subs)
|
||||
recAttrs = ATinsert(recAttrs, makeBind(i->key, i->value, makeNoPos()));
|
||||
foreach (ATermMap::const_iterator, i, subsRecursive)
|
||||
recAttrs = ATinsert(recAttrs, makeBind(i->key, i->value, makeNoPos()));
|
||||
Expr rec = makeRec(recAttrs, ATempty);
|
||||
foreach (ATermMap::const_iterator, i, subsRecursive)
|
||||
subs.set(i->key, makeSelect(rec, i->key));
|
||||
}
|
||||
|
||||
return substitute(Substitution(0, &subs), body);
|
||||
}
|
||||
|
|
|
@ -40,10 +40,11 @@ static void showAttrs(const ATermMap & attrs, XMLWriter & doc,
|
|||
}
|
||||
|
||||
|
||||
static void printPatternAsXML(Pattern pat, XMLWriter & doc, PathSet & context)
|
||||
static void printPatternAsXML(Pattern pat, XMLWriter & doc)
|
||||
{
|
||||
ATerm name;
|
||||
ATermList formals;
|
||||
Pattern pat1, pat2;
|
||||
if (matchVarPat(pat, name))
|
||||
doc.writeEmptyElement("varpat", singletonAttrs("name", aterm2String(name)));
|
||||
else if (matchAttrsPat(pat, formals)) {
|
||||
|
@ -54,6 +55,11 @@ static void printPatternAsXML(Pattern pat, XMLWriter & doc, PathSet & context)
|
|||
doc.writeEmptyElement("attr", singletonAttrs("name", aterm2String(name)));
|
||||
}
|
||||
}
|
||||
else if (matchAtPat(pat, pat1, pat2)) {
|
||||
XMLOpenElement _(doc, "at");
|
||||
printPatternAsXML(pat1, doc);
|
||||
printPatternAsXML(pat2, doc);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
@ -128,7 +134,7 @@ static void printTermAsXML(Expr e, XMLWriter & doc, PathSet & context,
|
|||
|
||||
else if (matchFunction(e, pat, body, pos)) {
|
||||
XMLOpenElement _(doc, "function");
|
||||
printPatternAsXML(pat, doc, context);
|
||||
printPatternAsXML(pat, doc);
|
||||
}
|
||||
|
||||
else
|
||||
|
|
|
@ -77,6 +77,7 @@ Scope | | Expr |
|
|||
|
||||
VarPat | string | Pattern |
|
||||
AttrsPat | ATermList | Pattern |
|
||||
AtPat | Pattern Pattern | Pattern |
|
||||
|
||||
Formal | string DefaultValue | ATerm |
|
||||
|
||||
|
|
|
@ -114,6 +114,7 @@ static void varsBoundByPattern(ATermMap & map, Pattern pat)
|
|||
{
|
||||
ATerm name;
|
||||
ATermList formals;
|
||||
Pattern pat1, pat2;
|
||||
/* Use makeRemoved() so that it can be used directly in
|
||||
substitute(). */
|
||||
if (matchVarPat(pat, name))
|
||||
|
@ -125,6 +126,10 @@ static void varsBoundByPattern(ATermMap & map, Pattern pat)
|
|||
map.set(name, makeRemoved());
|
||||
}
|
||||
}
|
||||
else if (matchAtPat(pat, pat1, pat2)) {
|
||||
varsBoundByPattern(map, pat1);
|
||||
varsBoundByPattern(map, pat2);
|
||||
}
|
||||
else abort();
|
||||
}
|
||||
|
||||
|
@ -354,7 +359,7 @@ Expr makeStr(const string & s, const PathSet & context)
|
|||
|
||||
string showType(Expr e)
|
||||
{
|
||||
ATerm t1, t2, t3;
|
||||
ATerm t1, t2;
|
||||
ATermList l1;
|
||||
ATermBlob b1;
|
||||
int i1;
|
||||
|
|
|
@ -211,7 +211,8 @@ static void freeAndUnprotect(void * p)
|
|||
}
|
||||
|
||||
%type <t> start expr expr_function expr_if expr_op
|
||||
%type <t> expr_app expr_select expr_simple bind inheritsrc formal pattern
|
||||
%type <t> expr_app expr_select expr_simple bind inheritsrc formal
|
||||
%type <t> pattern pattern2
|
||||
%type <ts> binds ids expr_list formals string_parts ind_string_parts
|
||||
%token <t> ID INT STR IND_STR PATH URI
|
||||
%token IF THEN ELSE ASSERT WITH LET IN REC INHERIT EQ NEQ AND OR IMPL
|
||||
|
@ -319,6 +320,11 @@ ind_string_parts
|
|||
;
|
||||
|
||||
pattern
|
||||
: pattern2 '@' pattern { $$ = makeAtPat($1, $3); }
|
||||
| pattern2
|
||||
;
|
||||
|
||||
pattern2
|
||||
: ID { $$ = makeVarPat($1); }
|
||||
| '{' formals '}' { $$ = makeAttrsPat($2); }
|
||||
;
|
||||
|
@ -394,6 +400,7 @@ static void checkPatternVars(ATerm pos, ATermMap & map, Pattern pat)
|
|||
{
|
||||
ATerm name;
|
||||
ATermList formals;
|
||||
Pattern pat1, pat2;
|
||||
if (matchVarPat(pat, name)) {
|
||||
if (map.get(name))
|
||||
throw EvalError(format("duplicate formal function argument `%1%' at %2%")
|
||||
|
@ -410,6 +417,10 @@ static void checkPatternVars(ATerm pos, ATermMap & map, Pattern pat)
|
|||
map.set(name, name);
|
||||
}
|
||||
}
|
||||
else if (matchAtPat(pat, pat1, pat2)) {
|
||||
checkPatternVars(pos, map, pat1);
|
||||
checkPatternVars(pos, map, pat2);
|
||||
}
|
||||
else abort();
|
||||
}
|
||||
|
||||
|
|
1
tests/lang/eval-okay-patterns.exp
Normal file
1
tests/lang/eval-okay-patterns.exp
Normal file
|
@ -0,0 +1 @@
|
|||
Str("abcxyzDDDDEFgh",[])
|
16
tests/lang/eval-okay-patterns.nix
Normal file
16
tests/lang/eval-okay-patterns.nix
Normal file
|
@ -0,0 +1,16 @@
|
|||
let
|
||||
|
||||
f = args@{x, y, z}: x + args.y + z;
|
||||
|
||||
g = {x, y, z}@args: f args;
|
||||
|
||||
h = {x ? "d", y ? x, z ? args.x}@args: x + y + z;
|
||||
|
||||
i = args@args2: args.x + args2.y;
|
||||
|
||||
in
|
||||
f {x = "a"; y = "b"; z = "c";} +
|
||||
g {x = "x"; y = "y"; z = "z";} +
|
||||
h {x = "D";} +
|
||||
h {x = "D"; y = "E"; z = "F";} +
|
||||
i {x = "g"; y = "h";}
|
|
@ -4,6 +4,18 @@
|
|||
<attr name="a">
|
||||
<string value="foo" />
|
||||
</attr>
|
||||
<attr name="at">
|
||||
<function>
|
||||
<at>
|
||||
<varpat name="args" />
|
||||
<attrspat>
|
||||
<attr name="x" />
|
||||
<attr name="y" />
|
||||
<attr name="z" />
|
||||
</attrspat>
|
||||
</at>
|
||||
</function>
|
||||
</attr>
|
||||
<attr name="b">
|
||||
<string value="bar" />
|
||||
</attr>
|
||||
|
|
|
@ -12,4 +12,6 @@ rec {
|
|||
|
||||
id = x: x;
|
||||
|
||||
at = args@{x, y, z}: x;
|
||||
|
||||
}
|
||||
|
|
1
tests/lang/parse-fail-patterns-1.nix
Normal file
1
tests/lang/parse-fail-patterns-1.nix
Normal file
|
@ -0,0 +1 @@
|
|||
args@{args, x, y, z}: x
|
Loading…
Reference in a new issue