forked from lix-project/lix
* @-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
|
@ -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,
|
static void patternMatch(EvalState & state,
|
||||||
Pattern pat, Expr arg, ATermMap & subs)
|
Pattern pat, Expr arg, ATermMap & subs, ATermMap & subsRecursive)
|
||||||
{
|
{
|
||||||
ATerm name;
|
ATerm name;
|
||||||
ATermList formals;
|
ATermList formals;
|
||||||
|
Pattern pat1, pat2;
|
||||||
|
|
||||||
if (matchVarPat(pat, name))
|
if (matchVarPat(pat, name))
|
||||||
subs.set(name, arg);
|
subs.set(name, arg);
|
||||||
|
@ -87,60 +92,45 @@ static void patternMatch(EvalState & state,
|
||||||
|
|
||||||
arg = evalExpr(state, arg);
|
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.
|
/* For each formal argument, get the actual argument. If
|
||||||
!!! shouldn't do this once we add `...'.*/
|
there is no matching actual argument but the formal
|
||||||
ATermMap args;
|
argument has a default, use the default. */
|
||||||
queryAllAttrs(arg, args);
|
unsigned int attrsUsed = 0;
|
||||||
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 (ATermIterator i(formals); i; ++i) {
|
for (ATermIterator i(formals); i; ++i) {
|
||||||
Expr name, def;
|
Expr name, def;
|
||||||
DefaultValue def2;
|
DefaultValue def2;
|
||||||
if (!matchFormal(*i, name, def2)) abort(); /* can't happen */
|
if (!matchFormal(*i, name, def2)) abort(); /* can't happen */
|
||||||
|
|
||||||
Expr value = subs[name];
|
Expr value = attrs[name];
|
||||||
|
|
||||||
if (value == 0) {
|
if (value == 0) {
|
||||||
if (!matchDefaultValue(def2, def)) def = 0;
|
if (!matchDefaultValue(def2, def)) def = 0;
|
||||||
if (def == 0) throw TypeError(format("the argument named `%1%' required by the function is missing")
|
if (def == 0) throw TypeError(format("the argument named `%1%' required by the function is missing")
|
||||||
% aterm2String(name));
|
% aterm2String(name));
|
||||||
value = def;
|
subsRecursive.set(name, def);
|
||||||
defsUsed.push_back(name);
|
} else {
|
||||||
recAttrs = ATinsert(recAttrs, makeBind(name, def, makeNoPos()));
|
attrsUsed++;
|
||||||
|
attrs.remove(name);
|
||||||
|
subs.set(name, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Make a recursive attribute set out of the (argument-name,
|
/* Check that each actual argument is listed as a formal
|
||||||
value) tuples. This is so that we can support default
|
argument. */
|
||||||
parameters that refer to each other, e.g. ({x, y ? x + x}:
|
if (attrsUsed != nrAttrs)
|
||||||
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);
|
|
||||||
}
|
|
||||||
throw TypeError(format("the function does not expect an argument named `%1%'")
|
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();
|
else abort();
|
||||||
|
@ -151,9 +141,24 @@ static void patternMatch(EvalState & state,
|
||||||
static Expr substArgs(EvalState & state,
|
static Expr substArgs(EvalState & state,
|
||||||
Expr body, Pattern pat, Expr arg)
|
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);
|
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;
|
ATerm name;
|
||||||
ATermList formals;
|
ATermList formals;
|
||||||
|
Pattern pat1, pat2;
|
||||||
if (matchVarPat(pat, name))
|
if (matchVarPat(pat, name))
|
||||||
doc.writeEmptyElement("varpat", singletonAttrs("name", aterm2String(name)));
|
doc.writeEmptyElement("varpat", singletonAttrs("name", aterm2String(name)));
|
||||||
else if (matchAttrsPat(pat, formals)) {
|
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)));
|
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)) {
|
else if (matchFunction(e, pat, body, pos)) {
|
||||||
XMLOpenElement _(doc, "function");
|
XMLOpenElement _(doc, "function");
|
||||||
printPatternAsXML(pat, doc, context);
|
printPatternAsXML(pat, doc);
|
||||||
}
|
}
|
||||||
|
|
||||||
else
|
else
|
||||||
|
|
|
@ -77,6 +77,7 @@ Scope | | Expr |
|
||||||
|
|
||||||
VarPat | string | Pattern |
|
VarPat | string | Pattern |
|
||||||
AttrsPat | ATermList | Pattern |
|
AttrsPat | ATermList | Pattern |
|
||||||
|
AtPat | Pattern Pattern | Pattern |
|
||||||
|
|
||||||
Formal | string DefaultValue | ATerm |
|
Formal | string DefaultValue | ATerm |
|
||||||
|
|
||||||
|
|
|
@ -114,6 +114,7 @@ static void varsBoundByPattern(ATermMap & map, Pattern pat)
|
||||||
{
|
{
|
||||||
ATerm name;
|
ATerm name;
|
||||||
ATermList formals;
|
ATermList formals;
|
||||||
|
Pattern pat1, pat2;
|
||||||
/* Use makeRemoved() so that it can be used directly in
|
/* Use makeRemoved() so that it can be used directly in
|
||||||
substitute(). */
|
substitute(). */
|
||||||
if (matchVarPat(pat, name))
|
if (matchVarPat(pat, name))
|
||||||
|
@ -125,6 +126,10 @@ static void varsBoundByPattern(ATermMap & map, Pattern pat)
|
||||||
map.set(name, makeRemoved());
|
map.set(name, makeRemoved());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else if (matchAtPat(pat, pat1, pat2)) {
|
||||||
|
varsBoundByPattern(map, pat1);
|
||||||
|
varsBoundByPattern(map, pat2);
|
||||||
|
}
|
||||||
else abort();
|
else abort();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -354,7 +359,7 @@ Expr makeStr(const string & s, const PathSet & context)
|
||||||
|
|
||||||
string showType(Expr e)
|
string showType(Expr e)
|
||||||
{
|
{
|
||||||
ATerm t1, t2, t3;
|
ATerm t1, t2;
|
||||||
ATermList l1;
|
ATermList l1;
|
||||||
ATermBlob b1;
|
ATermBlob b1;
|
||||||
int i1;
|
int i1;
|
||||||
|
|
|
@ -211,7 +211,8 @@ static void freeAndUnprotect(void * p)
|
||||||
}
|
}
|
||||||
|
|
||||||
%type <t> start expr expr_function expr_if expr_op
|
%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
|
%type <ts> binds ids expr_list formals string_parts ind_string_parts
|
||||||
%token <t> ID INT STR IND_STR PATH URI
|
%token <t> ID INT STR IND_STR PATH URI
|
||||||
%token IF THEN ELSE ASSERT WITH LET IN REC INHERIT EQ NEQ AND OR IMPL
|
%token IF THEN ELSE ASSERT WITH LET IN REC INHERIT EQ NEQ AND OR IMPL
|
||||||
|
@ -319,6 +320,11 @@ ind_string_parts
|
||||||
;
|
;
|
||||||
|
|
||||||
pattern
|
pattern
|
||||||
|
: pattern2 '@' pattern { $$ = makeAtPat($1, $3); }
|
||||||
|
| pattern2
|
||||||
|
;
|
||||||
|
|
||||||
|
pattern2
|
||||||
: ID { $$ = makeVarPat($1); }
|
: ID { $$ = makeVarPat($1); }
|
||||||
| '{' formals '}' { $$ = makeAttrsPat($2); }
|
| '{' formals '}' { $$ = makeAttrsPat($2); }
|
||||||
;
|
;
|
||||||
|
@ -394,6 +400,7 @@ static void checkPatternVars(ATerm pos, ATermMap & map, Pattern pat)
|
||||||
{
|
{
|
||||||
ATerm name;
|
ATerm name;
|
||||||
ATermList formals;
|
ATermList formals;
|
||||||
|
Pattern pat1, pat2;
|
||||||
if (matchVarPat(pat, name)) {
|
if (matchVarPat(pat, name)) {
|
||||||
if (map.get(name))
|
if (map.get(name))
|
||||||
throw EvalError(format("duplicate formal function argument `%1%' at %2%")
|
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);
|
map.set(name, name);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else if (matchAtPat(pat, pat1, pat2)) {
|
||||||
|
checkPatternVars(pos, map, pat1);
|
||||||
|
checkPatternVars(pos, map, pat2);
|
||||||
|
}
|
||||||
else abort();
|
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">
|
<attr name="a">
|
||||||
<string value="foo" />
|
<string value="foo" />
|
||||||
</attr>
|
</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">
|
<attr name="b">
|
||||||
<string value="bar" />
|
<string value="bar" />
|
||||||
</attr>
|
</attr>
|
||||||
|
|
|
@ -12,4 +12,6 @@ rec {
|
||||||
|
|
||||||
id = x: x;
|
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