* @-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:
Eelco Dolstra 2008-08-14 12:53:29 +00:00
parent e818838412
commit 1b962fc720
10 changed files with 106 additions and 46 deletions

View file

@ -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);
} }

View file

@ -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

View file

@ -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 |

View file

@ -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;

View file

@ -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();
} }

View file

@ -0,0 +1 @@
Str("abcxyzDDDDEFgh",[])

View 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";}

View file

@ -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>

View file

@ -12,4 +12,6 @@ rec {
id = x: x; id = x: x;
at = args@{x, y, z}: x;
} }

View file

@ -0,0 +1 @@
args@{args, x, y, z}: x