This commit is contained in:
Eelco Dolstra 2010-03-29 09:43:39 +00:00
parent 392811eb8f
commit 807a67bc74

View file

@ -29,6 +29,8 @@ typedef enum {
tInt = 1, tInt = 1,
tBool, tBool,
tString, tString,
tPath,
tNull,
tAttrs, tAttrs,
tList, tList,
tThunk, tThunk,
@ -157,6 +159,23 @@ std::ostream & operator << (std::ostream & str, Value & v)
static void eval(Env & env, Expr e, Value & v); static void eval(Env & env, Expr e, Value & v);
string showType(Value & v)
{
switch (v.type) {
case tString: return "a string";
case tPath: return "a path";
case tNull: return "null";
case tInt: return "an integer";
case tBool: return "a boolean";
case tLambda: return "a function";
case tAttrs: return "an attribute set";
case tList: return "a list";
case tPrimOpApp: return "a partially applied built-in function";
default: throw Error("unknown type");
}
}
static void forceValue(Value & v) static void forceValue(Value & v)
{ {
if (v.type == tThunk) { if (v.type == tThunk) {
@ -172,6 +191,30 @@ static void forceValue(Value & v)
} }
static void forceInt(Value & v)
{
forceValue(v);
if (v.type != tInt)
throw TypeError(format("value is %1% while an integer was expected") % showType(v));
}
static void forceAttrs(Value & v)
{
forceValue(v);
if (v.type != tAttrs)
throw TypeError(format("value is %1% while an attribute set was expected") % showType(v));
}
static void forceList(Value & v)
{
forceValue(v);
if (v.type != tList)
throw TypeError(format("value is %1% while a list was expected") % showType(v));
}
static Value * lookupWith(Env * env, Sym name) static Value * lookupWith(Env * env, Sym name)
{ {
if (!env) return 0; if (!env) return 0;
@ -247,7 +290,7 @@ static bool eqValues(Value & v1, Value & v2)
} }
unsigned long nrValues = 0, nrEnvs = 0; unsigned long nrValues = 0, nrEnvs = 0, nrEvaluated = 0;
static Value * allocValues(unsigned int count) static Value * allocValues(unsigned int count)
{ {
@ -272,6 +315,8 @@ static void eval(Env & env, Expr e, Value & v)
printMsg(lvlError, format("eval: %1%") % e); printMsg(lvlError, format("eval: %1%") % e);
nrEvaluated++;
Sym name; Sym name;
if (matchVar(e, name)) { if (matchVar(e, name)) {
Value * v2 = lookupVar(&env, name); Value * v2 = lookupVar(&env, name);
@ -328,7 +373,7 @@ static void eval(Env & env, Expr e, Value & v)
Expr e1, e2; Expr e1, e2;
if (matchSelect(e, e2, name)) { if (matchSelect(e, e2, name)) {
eval(env, e2, v); eval(env, e2, v);
if (v.type != tAttrs) throw TypeError("expected attribute set"); forceAttrs(v); // !!! eval followed by force is slightly inefficient
Bindings::iterator i = v.attrs->find(name); Bindings::iterator i = v.attrs->find(name);
if (i == v.attrs->end()) throw TypeError("attribute not found"); if (i == v.attrs->end()) throw TypeError("attribute not found");
forceValue(i->second); forceValue(i->second);
@ -409,7 +454,7 @@ static void eval(Env & env, Expr e, Value & v)
} }
eval(env, arg, *vArg); eval(env, arg, *vArg);
if (vArg->type != tAttrs) throw TypeError("expected attribute set"); forceAttrs(*vArg);
/* For each formal argument, get the actual argument. If /* For each formal argument, get the actual argument. If
there is no matching actual argument but the formal there is no matching actual argument but the formal
@ -459,7 +504,7 @@ static void eval(Env & env, Expr e, Value & v)
Value & vAttrs = env2.bindings[sWith]; Value & vAttrs = env2.bindings[sWith];
nrValues++; nrValues++;
eval(env, attrs, vAttrs); eval(env, attrs, vAttrs);
if (vAttrs.type != tAttrs) throw TypeError("`with' should evaluate to an attribute set"); forceAttrs(vAttrs);
eval(env2, body, v); eval(env2, body, v);
return; return;
@ -490,9 +535,9 @@ static void eval(Env & env, Expr e, Value & v)
if (matchOpConcat(e, e1, e2)) { if (matchOpConcat(e, e1, e2)) {
Value v1; eval(env, e1, v1); Value v1; eval(env, e1, v1);
if (v1.type != tList) throw TypeError("list expected"); forceList(v1);
Value v2; eval(env, e2, v2); Value v2; eval(env, e2, v2);
if (v2.type != tList) throw TypeError("list expected"); forceList(v2);
v.type = tList; v.type = tList;
v.list.length = v1.list.length + v2.list.length; v.list.length = v1.list.length + v2.list.length;
v.list.elems = allocValues(v.list.length); v.list.elems = allocValues(v.list.length);
@ -546,8 +591,7 @@ static void strictEval(Env & env, Expr e, Value & v)
static void prim_head(Value * * args, Value & v) static void prim_head(Value * * args, Value & v)
{ {
forceValue(*args[0]); forceList(*args[0]);
if (args[0]->type != tList) throw TypeError("list expected");
if (args[0]->list.length == 0) if (args[0]->list.length == 0)
throw Error("`head' called on an empty list"); throw Error("`head' called on an empty list");
forceValue(args[0]->list.elems[0]); forceValue(args[0]->list.elems[0]);
@ -557,10 +601,8 @@ static void prim_head(Value * * args, Value & v)
static void prim_add(Value * * args, Value & v) static void prim_add(Value * * args, Value & v)
{ {
forceValue(*args[0]); forceInt(*args[0]);
if (args[0]->type != tInt) throw TypeError("integer expected"); forceInt(*args[1]);
forceValue(*args[1]);
if (args[1]->type != tInt) throw TypeError("integer expected");
mkInt(v, args[0]->integer + args[1]->integer); mkInt(v, args[0]->integer + args[1]->integer);
} }
@ -649,6 +691,7 @@ void run(Strings args)
printMsg(lvlError, format("alloced %1% values") % nrValues); printMsg(lvlError, format("alloced %1% values") % nrValues);
printMsg(lvlError, format("alloced %1% environments") % nrEnvs); printMsg(lvlError, format("alloced %1% environments") % nrEnvs);
printMsg(lvlError, format("evaluated %1% expressions") % nrEvaluated);
printMsg(lvlError, format("each eval() uses %1% bytes of stack space") % (p1 - p2)); printMsg(lvlError, format("each eval() uses %1% bytes of stack space") % (p1 - p2));
} }