* Implemented `map'.
This commit is contained in:
parent
d78a05ab40
commit
5b72d8a749
4 changed files with 139 additions and 108 deletions
|
@ -68,6 +68,8 @@ void run(Strings args)
|
||||||
doTest("let x = x; in if true || x then 1 else 2");
|
doTest("let x = x; in if true || x then 1 else 2");
|
||||||
doTest("/etc/passwd");
|
doTest("/etc/passwd");
|
||||||
doTest("import ./foo.nix");
|
doTest("import ./foo.nix");
|
||||||
|
doTest("map (x: __add 1 x) [ 1 2 3 ]");
|
||||||
|
doTest("map (__add 1) [ 1 2 3 ]");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -328,106 +328,9 @@ void EvalState::eval(Env & env, Expr e, Value & v)
|
||||||
Expr fun, arg;
|
Expr fun, arg;
|
||||||
if (matchCall(e, fun, arg)) {
|
if (matchCall(e, fun, arg)) {
|
||||||
eval(env, fun, v);
|
eval(env, fun, v);
|
||||||
|
Value vArg;
|
||||||
if (v.type == tPrimOp || v.type == tPrimOpApp) {
|
mkThunk(vArg, env, arg); // !!! should this be on the heap?
|
||||||
unsigned int argsLeft =
|
callFunction(v, vArg, v);
|
||||||
v.type == tPrimOp ? v.primOp.arity : v.primOpApp.argsLeft;
|
|
||||||
if (argsLeft == 1) {
|
|
||||||
/* We have all the arguments, so call the primop.
|
|
||||||
First find the primop. */
|
|
||||||
Value * primOp = &v;
|
|
||||||
while (primOp->type == tPrimOpApp) primOp = primOp->primOpApp.left;
|
|
||||||
assert(primOp->type == tPrimOp);
|
|
||||||
unsigned int arity = primOp->primOp.arity;
|
|
||||||
|
|
||||||
Value vLastArg;
|
|
||||||
mkThunk(vLastArg, env, arg);
|
|
||||||
|
|
||||||
/* Put all the arguments in an array. */
|
|
||||||
Value * vArgs[arity];
|
|
||||||
unsigned int n = arity - 1;
|
|
||||||
vArgs[n--] = &vLastArg;
|
|
||||||
for (Value * arg = &v; arg->type == tPrimOpApp; arg = arg->primOpApp.left)
|
|
||||||
vArgs[n--] = arg->primOpApp.right;
|
|
||||||
|
|
||||||
/* And call the primop. */
|
|
||||||
primOp->primOp.fun(*this, vArgs, v);
|
|
||||||
} else {
|
|
||||||
Value * v2 = allocValues(2);
|
|
||||||
v2[0] = v;
|
|
||||||
mkThunk(v2[1], env, arg);
|
|
||||||
v.type = tPrimOpApp;
|
|
||||||
v.primOpApp.left = &v2[0];
|
|
||||||
v.primOpApp.right = &v2[1];
|
|
||||||
v.primOpApp.argsLeft = argsLeft - 1;
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (v.type != tLambda) throw TypeError("expected function");
|
|
||||||
|
|
||||||
Env & env2(allocEnv());
|
|
||||||
env2.up = &env;
|
|
||||||
|
|
||||||
ATermList formals; ATerm ellipsis;
|
|
||||||
|
|
||||||
if (matchVarPat(v.lambda.pat, name)) {
|
|
||||||
Value & vArg = env2.bindings[name];
|
|
||||||
nrValues++;
|
|
||||||
mkThunk(vArg, env, arg);
|
|
||||||
}
|
|
||||||
|
|
||||||
else if (matchAttrsPat(v.lambda.pat, formals, ellipsis, name)) {
|
|
||||||
Value * vArg;
|
|
||||||
Value vArg_;
|
|
||||||
|
|
||||||
if (name == sNoAlias)
|
|
||||||
vArg = &vArg_;
|
|
||||||
else {
|
|
||||||
vArg = &env2.bindings[name];
|
|
||||||
nrValues++;
|
|
||||||
}
|
|
||||||
|
|
||||||
eval(env, arg, *vArg);
|
|
||||||
forceAttrs(*vArg);
|
|
||||||
|
|
||||||
/* 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 def; Sym name;
|
|
||||||
DefaultValue def2;
|
|
||||||
if (!matchFormal(*i, name, def2)) abort(); /* can't happen */
|
|
||||||
|
|
||||||
Bindings::iterator j = vArg->attrs->find(name);
|
|
||||||
|
|
||||||
Value & v = env2.bindings[name];
|
|
||||||
nrValues++;
|
|
||||||
|
|
||||||
if (j == vArg->attrs->end()) {
|
|
||||||
if (!matchDefaultValue(def2, def)) def = 0;
|
|
||||||
if (def == 0) throw TypeError(format("the argument named `%1%' required by the function is missing")
|
|
||||||
% aterm2String(name));
|
|
||||||
mkThunk(v, env2, def);
|
|
||||||
} else {
|
|
||||||
attrsUsed++;
|
|
||||||
v.type = tCopy;
|
|
||||||
v.val = &j->second;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Check that each actual argument is listed as a formal
|
|
||||||
argument (unless the attribute match specifies a
|
|
||||||
`...'). TODO: show the names of the
|
|
||||||
expected/unexpected arguments. */
|
|
||||||
if (ellipsis == eFalse && attrsUsed != vArg->attrs->size())
|
|
||||||
throw TypeError("function called with unexpected argument");
|
|
||||||
}
|
|
||||||
|
|
||||||
else abort();
|
|
||||||
|
|
||||||
eval(env2, v.lambda.body, v);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -519,6 +422,103 @@ void EvalState::eval(Env & env, Expr e, Value & v)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void EvalState::callFunction(Value & fun, Value & arg, Value & v)
|
||||||
|
{
|
||||||
|
if (fun.type == tPrimOp || fun.type == tPrimOpApp) {
|
||||||
|
unsigned int argsLeft =
|
||||||
|
fun.type == tPrimOp ? fun.primOp.arity : fun.primOpApp.argsLeft;
|
||||||
|
if (argsLeft == 1) {
|
||||||
|
/* We have all the arguments, so call the primop. First
|
||||||
|
find the primop. */
|
||||||
|
Value * primOp = &fun;
|
||||||
|
while (primOp->type == tPrimOpApp) primOp = primOp->primOpApp.left;
|
||||||
|
assert(primOp->type == tPrimOp);
|
||||||
|
unsigned int arity = primOp->primOp.arity;
|
||||||
|
|
||||||
|
/* Put all the arguments in an array. */
|
||||||
|
Value * vArgs[arity];
|
||||||
|
unsigned int n = arity - 1;
|
||||||
|
vArgs[n--] = &arg;
|
||||||
|
for (Value * arg = &fun; arg->type == tPrimOpApp; arg = arg->primOpApp.left)
|
||||||
|
vArgs[n--] = arg->primOpApp.right;
|
||||||
|
|
||||||
|
/* And call the primop. */
|
||||||
|
primOp->primOp.fun(*this, vArgs, v);
|
||||||
|
} else {
|
||||||
|
Value * v2 = allocValues(2);
|
||||||
|
v2[0] = fun;
|
||||||
|
v2[1] = arg;
|
||||||
|
v.type = tPrimOpApp;
|
||||||
|
v.primOpApp.left = &v2[0];
|
||||||
|
v.primOpApp.right = &v2[1];
|
||||||
|
v.primOpApp.argsLeft = argsLeft - 1;
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fun.type != tLambda)
|
||||||
|
throwTypeError("attempt to call something which is neither a function nor a primop (built-in operation) but %1%",
|
||||||
|
showType(fun));
|
||||||
|
|
||||||
|
Env & env2(allocEnv());
|
||||||
|
env2.up = fun.lambda.env;
|
||||||
|
|
||||||
|
ATermList formals; ATerm ellipsis, name;
|
||||||
|
|
||||||
|
if (matchVarPat(fun.lambda.pat, name)) {
|
||||||
|
Value & vArg = env2.bindings[name];
|
||||||
|
nrValues++;
|
||||||
|
vArg = arg;
|
||||||
|
}
|
||||||
|
|
||||||
|
else if (matchAttrsPat(fun.lambda.pat, formals, ellipsis, name)) {
|
||||||
|
forceAttrs(arg);
|
||||||
|
|
||||||
|
if (name != sNoAlias) {
|
||||||
|
env2.bindings[name] = arg;
|
||||||
|
nrValues++;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 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 def; Sym name;
|
||||||
|
DefaultValue def2;
|
||||||
|
if (!matchFormal(*i, name, def2)) abort(); /* can't happen */
|
||||||
|
|
||||||
|
Bindings::iterator j = arg.attrs->find(name);
|
||||||
|
|
||||||
|
Value & v = env2.bindings[name];
|
||||||
|
nrValues++;
|
||||||
|
|
||||||
|
if (j == arg.attrs->end()) {
|
||||||
|
if (!matchDefaultValue(def2, def)) def = 0;
|
||||||
|
if (def == 0) throw TypeError(format("the argument named `%1%' required by the function is missing")
|
||||||
|
% aterm2String(name));
|
||||||
|
mkThunk(v, env2, def);
|
||||||
|
} else {
|
||||||
|
attrsUsed++;
|
||||||
|
v.type = tCopy;
|
||||||
|
v.val = &j->second;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Check that each actual argument is listed as a formal
|
||||||
|
argument (unless the attribute match specifies a `...').
|
||||||
|
TODO: show the names of the expected/unexpected
|
||||||
|
arguments. */
|
||||||
|
if (ellipsis == eFalse && attrsUsed != arg.attrs->size())
|
||||||
|
throw TypeError("function called with unexpected argument");
|
||||||
|
}
|
||||||
|
|
||||||
|
else abort();
|
||||||
|
|
||||||
|
eval(env2, fun.lambda.body, v);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
void EvalState::eval(Expr e, Value & v)
|
void EvalState::eval(Expr e, Value & v)
|
||||||
{
|
{
|
||||||
eval(baseEnv, e, v);
|
eval(baseEnv, e, v);
|
||||||
|
@ -567,6 +567,8 @@ void EvalState::forceValue(Value & v)
|
||||||
forceValue(*v.val);
|
forceValue(*v.val);
|
||||||
v = *v.val;
|
v = *v.val;
|
||||||
}
|
}
|
||||||
|
else if (v.type == tApp)
|
||||||
|
callFunction(*v.app.left, *v.app.right, v);
|
||||||
else if (v.type == tBlackhole)
|
else if (v.type == tBlackhole)
|
||||||
throw EvalError("infinite recursion encountered");
|
throw EvalError("infinite recursion encountered");
|
||||||
}
|
}
|
||||||
|
@ -597,6 +599,14 @@ void EvalState::forceList(Value & v)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void EvalState::forceFunction(Value & v)
|
||||||
|
{
|
||||||
|
forceValue(v);
|
||||||
|
if (v.type != tLambda && v.type != tPrimOp && v.type != tPrimOpApp)
|
||||||
|
throw TypeError(format("value is %1% while a function was expected") % showType(v));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
string EvalState::coerceToString(Value & v, PathSet & context,
|
string EvalState::coerceToString(Value & v, PathSet & context,
|
||||||
bool coerceMore, bool copyToStore)
|
bool coerceMore, bool copyToStore)
|
||||||
{
|
{
|
||||||
|
|
|
@ -36,6 +36,7 @@ typedef enum {
|
||||||
tAttrs,
|
tAttrs,
|
||||||
tList,
|
tList,
|
||||||
tThunk,
|
tThunk,
|
||||||
|
tApp,
|
||||||
tLambda,
|
tLambda,
|
||||||
tCopy,
|
tCopy,
|
||||||
tBlackhole,
|
tBlackhole,
|
||||||
|
@ -68,6 +69,9 @@ struct Value
|
||||||
Env * env;
|
Env * env;
|
||||||
Expr expr;
|
Expr expr;
|
||||||
} thunk;
|
} thunk;
|
||||||
|
struct {
|
||||||
|
Value * left, * right;
|
||||||
|
} app;
|
||||||
struct {
|
struct {
|
||||||
Env * env;
|
Env * env;
|
||||||
Pattern pat;
|
Pattern pat;
|
||||||
|
@ -161,13 +165,16 @@ struct EvalState
|
||||||
void strictEval(Env & env, Expr e, Value & v);
|
void strictEval(Env & env, Expr e, Value & v);
|
||||||
|
|
||||||
/* If `v' is a thunk, enter it and overwrite `v' with the result
|
/* If `v' is a thunk, enter it and overwrite `v' with the result
|
||||||
of the evaluation of the thunk. Otherwise, this is a no-op. */
|
of the evaluation of the thunk. If `v' is a delayed function
|
||||||
|
application, call the function and overwrite `v' with the
|
||||||
|
result. Otherwise, this is a no-op. */
|
||||||
void forceValue(Value & v);
|
void forceValue(Value & v);
|
||||||
|
|
||||||
/* Force `v', and then verify that it has the expected type. */
|
/* Force `v', and then verify that it has the expected type. */
|
||||||
int forceInt(Value & v);
|
int forceInt(Value & v);
|
||||||
void forceAttrs(Value & v);
|
void forceAttrs(Value & v);
|
||||||
void forceList(Value & v);
|
void forceList(Value & v);
|
||||||
|
void forceFunction(Value & v); // either lambda or primop
|
||||||
|
|
||||||
/* String coercion. Converts strings, paths and derivations to a
|
/* String coercion. Converts strings, paths and derivations to a
|
||||||
string. If `coerceMore' is set, also converts nulls, integers,
|
string. If `coerceMore' is set, also converts nulls, integers,
|
||||||
|
@ -196,6 +203,10 @@ private:
|
||||||
elements and attributes are compared recursively. */
|
elements and attributes are compared recursively. */
|
||||||
bool eqValues(Value & v1, Value & v2);
|
bool eqValues(Value & v1, Value & v2);
|
||||||
|
|
||||||
|
void callFunction(Value & fun, Value & arg, Value & v);
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
/* Allocation primitives. */
|
/* Allocation primitives. */
|
||||||
Value * allocValues(unsigned int count);
|
Value * allocValues(unsigned int count);
|
||||||
Env & allocEnv();
|
Env & allocEnv();
|
||||||
|
|
|
@ -913,22 +913,28 @@ static Expr prim_tail(EvalState & state, const ATermVector & args)
|
||||||
throw Error("`tail' called on an empty list");
|
throw Error("`tail' called on an empty list");
|
||||||
return makeList(ATgetNext(list));
|
return makeList(ATgetNext(list));
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
/* Apply a function to every element of a list. */
|
/* Apply a function to every element of a list. */
|
||||||
static Expr prim_map(EvalState & state, const ATermVector & args)
|
static void prim_map(EvalState & state, Value * * args, Value & v)
|
||||||
{
|
{
|
||||||
Expr fun = evalExpr(state, args[0]);
|
state.forceFunction(*args[0]);
|
||||||
ATermList list = evalList(state, args[1]);
|
state.forceList(*args[1]);
|
||||||
|
|
||||||
ATermList res = ATempty;
|
v.type = tList;
|
||||||
for (ATermIterator i(list); i; ++i)
|
v.list.length = args[1]->list.length;
|
||||||
res = ATinsert(res, makeCall(fun, *i));
|
v.list.elems = state.allocValues(v.list.length);
|
||||||
|
|
||||||
return makeList(ATreverse(res));
|
for (unsigned int n = 0; n < v.list.length; ++n) {
|
||||||
|
v.list.elems[n].type = tApp;
|
||||||
|
v.list.elems[n].app.left = args[0];
|
||||||
|
v.list.elems[n].app.right = &args[1]->list.elems[n];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#if 0
|
||||||
/* Return the length of a list. This is an O(1) time operation. */
|
/* Return the length of a list. This is an O(1) time operation. */
|
||||||
static Expr prim_length(EvalState & state, const ATermVector & args)
|
static Expr prim_length(EvalState & state, const ATermVector & args)
|
||||||
{
|
{
|
||||||
|
@ -1189,7 +1195,9 @@ void EvalState::createBaseEnv()
|
||||||
addPrimOp("__head", 1, prim_head);
|
addPrimOp("__head", 1, prim_head);
|
||||||
#if 0
|
#if 0
|
||||||
addPrimOp("__tail", 1, prim_tail);
|
addPrimOp("__tail", 1, prim_tail);
|
||||||
|
#endif
|
||||||
addPrimOp("map", 2, prim_map);
|
addPrimOp("map", 2, prim_map);
|
||||||
|
#if 0
|
||||||
addPrimOp("__length", 1, prim_length);
|
addPrimOp("__length", 1, prim_length);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue