* More missing constructs.

This commit is contained in:
Eelco Dolstra 2010-04-12 21:21:24 +00:00
parent 4d6ad5be17
commit a60317f20f
6 changed files with 161 additions and 138 deletions

View file

@ -45,6 +45,7 @@ void run(Strings args)
doTest(state, "({x, y, ...}@args: args.z) { x = 1; y = 2; z = 3; }"); doTest(state, "({x, y, ...}@args: args.z) { x = 1; y = 2; z = 3; }");
//doTest(state, "({x ? y, y ? x}: y) { }"); //doTest(state, "({x ? y, y ? x}: y) { }");
doTest(state, "let x = 1; in x"); doTest(state, "let x = 1; in x");
doTest(state, "let { x = 1; body = x; }");
doTest(state, "with { x = 1; }; x"); doTest(state, "with { x = 1; }; x");
doTest(state, "let x = 2; in with { x = 1; }; x"); // => 2 doTest(state, "let x = 2; in with { x = 1; }; x"); // => 2
doTest(state, "with { x = 1; }; with { x = 2; }; x"); // => 1 doTest(state, "with { x = 1; }; with { x = 2; }; x"); // => 1
@ -65,18 +66,19 @@ void run(Strings args)
doTest(state, "__head [ 1 2 3 ]"); doTest(state, "__head [ 1 2 3 ]");
doTest(state, "__add 1 2"); doTest(state, "__add 1 2");
doTest(state, "null"); doTest(state, "null");
//doTest(state, "\"foo\""); doTest(state, "\"foo\"");
//doTest(state, "let s = \"bar\"; in \"foo${s}\""); doTest(state, "let s = \"bar\"; in \"foo${s}\"");
doTest(state, "if true then 1 else 2"); doTest(state, "if true then 1 else 2");
doTest(state, "if false then 1 else 2"); doTest(state, "if false then 1 else 2");
doTest(state, "if false || true then 1 else 2"); doTest(state, "if false || true then 1 else 2");
doTest(state, "!(true || false)");
doTest(state, "let x = x; in if true || x then 1 else 2"); doTest(state, "let x = x; in if true || x then 1 else 2");
doTest(state, "http://nixos.org/"); doTest(state, "http://nixos.org/");
doTest(state, "/etc/passwd"); doTest(state, "/etc/passwd");
//doTest(state, "import ./foo.nix"); //doTest(state, "import ./foo.nix");
doTest(state, "map (x: __add 1 x) [ 1 2 3 ]"); doTest(state, "map (x: __add 1 x) [ 1 2 3 ]");
doTest(state, "map (builtins.add 1) [ 1 2 3 ]"); doTest(state, "map (builtins.add 1) [ 1 2 3 ]");
//doTest(state, "builtins.hasAttr \"x\" { x = 1; }"); doTest(state, "builtins.hasAttr \"x\" { x = 1; }");
doTest(state, "let x = 1; as = rec { inherit x; y = as.x; }; in as.y"); doTest(state, "let x = 1; as = rec { inherit x; y = as.x; }; in as.y");
doTest(state, "let as = { x = 1; }; bs = rec { inherit (as) x; y = x; }; in bs.y"); doTest(state, "let as = { x = 1; }; bs = rec { inherit (as) x; y = x; }; in bs.y");
doTest(state, "let as = rec { inherit (y) x; y = { x = 1; }; }; in as.x"); doTest(state, "let as = rec { inherit (y) x; y = { x = 1; }; }; in as.x");

View file

@ -160,9 +160,9 @@ LocalNoInlineNoReturn(void throwTypeError(const char * s, const string & s2))
throw TypeError(format(s) % s2); throw TypeError(format(s) % s2);
} }
LocalNoInlineNoReturn(void throwAssertionError(const char * s, const string & s2)) LocalNoInlineNoReturn(void throwAssertionError(const char * s, const Pos & pos))
{ {
throw AssertionError(format(s) % s2); throw AssertionError(format(s) % pos);
} }
LocalNoInline(void addErrorPrefix(Error & e, const char * s)) LocalNoInline(void addErrorPrefix(Error & e, const char * s))
@ -341,73 +341,13 @@ void EvalState::eval(Env & env, Expr * e, Value & v)
char x; char x;
if (&x < deepestStack) deepestStack = &x; if (&x < deepestStack) deepestStack = &x;
//debug(format("eval: %1%") % e); //debug(format("eval: %1%") % *e);
checkInterrupt(); checkInterrupt();
nrEvaluated++; nrEvaluated++;
e->eval(*this, env, v); e->eval(*this, env, v);
#if 0
Sym name;
int n;
ATerm s; ATermList context, es;
ATermList rbnds, nrbnds;
Expr e1, e2, e3, fun, arg, attrs;
Pattern pat; Expr body; Pos pos;
else if (matchConcatStrings(e, es)) {
PathSet context;
std::ostringstream s;
bool first = true, isPath = false;
Value vStr;
for (ATermIterator i(es); i; ++i) {
eval(env, *i, vStr);
/* If the first element is a path, then the result will
also be a path, we don't copy anything (yet - that's
done later, since paths are copied when they are used
in a derivation), and none of the strings are allowed
to have contexts. */
if (first) {
isPath = vStr.type == tPath;
first = false;
}
s << coerceToString(vStr, context, false, !isPath);
}
if (isPath && !context.empty())
throwEvalError("a string that refers to a store path cannot be appended to a path, in `%1%'", s.str());
if (isPath)
mkPath(v, s.str().c_str());
else
mkString(v, s.str(), context);
}
/* Assertions. */
else if (matchAssert(e, e1, e2, pos)) {
if (!evalBool(env, e1))
throwAssertionError("assertion failed at %1%", showPos(pos));
eval(env, e2, v);
}
/* Negation. */
else if (matchOpNot(e, e1))
mkBool(v, !evalBool(env, e1));
/* Attribute existence test (?). */
else if (matchOpHasAttr(e, e1, name)) {
Value vAttrs;
eval(env, e1, vAttrs);
forceAttrs(vAttrs);
mkBool(v, vAttrs.attrs->find(name) != vAttrs.attrs->end());
}
#endif
} }
@ -516,6 +456,15 @@ void ExprSelect::eval(EvalState & state, Env & env, Value & v)
} }
void ExprOpHasAttr::eval(EvalState & state, Env & env, Value & v)
{
Value vAttrs;
state.eval(env, e, vAttrs);
state.forceAttrs(vAttrs);
mkBool(v, vAttrs.attrs->find(name) != vAttrs.attrs->end());
}
void ExprLambda::eval(EvalState & state, Env & env, Value & v) void ExprLambda::eval(EvalState & state, Env & env, Value & v)
{ {
v.type = tLambda; v.type = tLambda;
@ -663,6 +612,20 @@ void ExprIf::eval(EvalState & state, Env & env, Value & v)
} }
void ExprAssert::eval(EvalState & state, Env & env, Value & v)
{
if (!state.evalBool(env, cond))
throwAssertionError("assertion failed at %1%", pos);
state.eval(env, body, v);
}
void ExprOpNot::eval(EvalState & state, Env & env, Value & v)
{
mkBool(v, !state.evalBool(env, e));
}
void ExprOpEq::eval(EvalState & state, Env & env, Value & v) void ExprOpEq::eval(EvalState & state, Env & env, Value & v)
{ {
Value v1; state.eval(env, e1, v1); Value v1; state.eval(env, e1, v1);
@ -713,12 +676,6 @@ void ExprOpUpdate::eval(EvalState & state, Env & env, Value & v)
} }
void ExprOpConcatStrings::eval(EvalState & state, Env & env, Value & v)
{
abort();
}
void ExprOpConcatLists::eval(EvalState & state, Env & env, Value & v) void ExprOpConcatLists::eval(EvalState & state, Env & env, Value & v)
{ {
Value v1; state.eval(env, e1, v1); Value v1; state.eval(env, e1, v1);
@ -735,6 +692,39 @@ void ExprOpConcatLists::eval(EvalState & state, Env & env, Value & v)
} }
void ExprConcatStrings::eval(EvalState & state, Env & env, Value & v)
{
PathSet context;
std::ostringstream s;
bool first = true, isPath = false;
Value vStr;
foreach (vector<Expr *>::iterator, i, *es) {
state.eval(env, *i, vStr);
/* If the first element is a path, then the result will also
be a path, we don't copy anything (yet - that's done later,
since paths are copied when they are used in a derivation),
and none of the strings are allowed to have contexts. */
if (first) {
isPath = vStr.type == tPath;
first = false;
}
s << state.coerceToString(vStr, context, false, !isPath);
}
if (isPath && !context.empty())
throwEvalError("a string that refers to a store path cannot be appended to a path, in `%1%'", s.str());
if (isPath)
mkPath(v, s.str().c_str());
else
mkString(v, s.str(), context);
}
void EvalState::forceValue(Value & v) void EvalState::forceValue(Value & v)
{ {
if (v.type == tThunk) { if (v.type == tThunk) {

View file

@ -45,7 +45,6 @@ static void adjustLoc(YYLTYPE * loc, const char * s, size_t len)
static Expr * unescapeStr(const char * s) static Expr * unescapeStr(const char * s)
{ {
#if 0
string t; string t;
char c; char c;
while ((c = *s++)) { while ((c = *s++)) {
@ -64,8 +63,7 @@ static Expr * unescapeStr(const char * s)
} }
else t += c; else t += c;
} }
return makeStr(toATerm(t), ATempty); return new ExprString(t);
#endif
} }

View file

@ -40,6 +40,11 @@ void ExprSelect::show(std::ostream & str)
str << "(" << *e << ")." << name; str << "(" << *e << ")." << name;
} }
void ExprOpHasAttr::show(std::ostream & str)
{
str << "(" << *e << ") ? " << name;
}
void ExprAttrs::show(std::ostream & str) void ExprAttrs::show(std::ostream & str)
{ {
if (recursive) str << "rec "; if (recursive) str << "rec ";
@ -87,19 +92,37 @@ void ExprIf::show(std::ostream & str)
str << "if " << *cond << " then " << *then << " else " << *else_; str << "if " << *cond << " then " << *then << " else " << *else_;
} }
void ExprAssert::show(std::ostream & str)
#if 0
string showPos(ATerm pos)
{ {
ATerm path; str << "assert " << *cond << "; " << *body;
int line, column; }
if (matchNoPos(pos)) return "undefined position";
if (!matchPos(pos, path, line, column)) void ExprOpNot::show(std::ostream & str)
throw badTerm("position expected", pos); {
return (format("`%1%:%2%:%3%'") % aterm2String(path) % line % column).str(); str << "! " << *e;
}
void ExprConcatStrings::show(std::ostream & str)
{
bool first = true;
foreach (vector<Expr *>::iterator, i, *es) {
if (first) first = false; else str << " + ";
str << **i;
}
}
std::ostream & operator << (std::ostream & str, const Pos & pos)
{
if (!pos.line)
str << "undefined position";
else
str << (format("`%1%:%2%:%3%'") % pos.file % pos.line % pos.column).str();
return str;
} }
#if 0
ATerm bottomupRewrite(TermFun & f, ATerm e) ATerm bottomupRewrite(TermFun & f, ATerm e)
{ {
checkInterrupt(); checkInterrupt();

View file

@ -24,6 +24,9 @@ struct Pos
}; };
std::ostream & operator << (std::ostream & str, const Pos & pos);
/* Abstract syntax of Nix expressions. */ /* Abstract syntax of Nix expressions. */
struct Env; struct Env;
@ -81,6 +84,14 @@ struct ExprSelect : Expr
COMMON_METHODS COMMON_METHODS
}; };
struct ExprOpHasAttr : Expr
{
Expr * e;
string name;
ExprOpHasAttr(Expr * e, const string & name) : e(e), name(name) { };
COMMON_METHODS
};
struct ExprAttrs : Expr struct ExprAttrs : Expr
{ {
bool recursive; bool recursive;
@ -139,6 +150,21 @@ struct ExprIf : Expr
COMMON_METHODS COMMON_METHODS
}; };
struct ExprAssert : Expr
{
Pos pos;
Expr * cond, * body;
ExprAssert(const Pos & pos, Expr * cond, Expr * body) : pos(pos), cond(cond), body(body) { };
COMMON_METHODS
};
struct ExprOpNot : Expr
{
Expr * e;
ExprOpNot(Expr * e) : e(e) { };
COMMON_METHODS
};
#define MakeBinOp(name, s) \ #define MakeBinOp(name, s) \
struct Expr##name : Expr \ struct Expr##name : Expr \
{ \ { \
@ -158,15 +184,17 @@ MakeBinOp(OpAnd, "&&")
MakeBinOp(OpOr, "||") MakeBinOp(OpOr, "||")
MakeBinOp(OpImpl, "->") MakeBinOp(OpImpl, "->")
MakeBinOp(OpUpdate, "//") MakeBinOp(OpUpdate, "//")
MakeBinOp(OpConcatStrings, "+")
MakeBinOp(OpConcatLists, "++") MakeBinOp(OpConcatLists, "++")
struct ExprConcatStrings : Expr
{
vector<Expr *> * es;
ExprConcatStrings(vector<Expr *> * es) : es(es) { };
COMMON_METHODS
};
#if 0 #if 0
/* Show a position. */
string showPos(ATerm pos);
/* Generic bottomup traversal over ATerms. The traversal first /* Generic bottomup traversal over ATerms. The traversal first
recursively descends into subterms, and then applies the given term recursively descends into subterms, and then applies the given term
function to the resulting term. */ function to the resulting term. */

View file

@ -20,7 +20,6 @@
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#include "aterm.hh"
#include "util.hh" #include "util.hh"
#include "nixexpr.hh" #include "nixexpr.hh"
@ -116,13 +115,13 @@ static void fixAttrs(ExprAttrs & attrs)
for (ATermIterator j(attrPath); j; ) { for (ATermIterator j(attrPath); j; ) {
name = *j; ++j; name = *j; ++j;
if (t->leaf) throw ParseError(format("attribute set containing `%1%' at %2% already defined at %3%") if (t->leaf) throw ParseError(format("attribute set containing `%1%' at %2% already defined at %3%")
% showAttrPath(attrPath) % showPos(pos) % showPos (t->pos)); % showAttrPath(attrPath) % showPos(pos) % showPos(t->pos));
t = &(t->children[name]); t = &(t->children[name]);
} }
if (t->leaf) if (t->leaf)
throw ParseError(format("duplicate definition of attribute `%1%' at %2% and %3%") throw ParseError(format("duplicate definition of attribute `%1%' at %2% and %3%")
% showAttrPath(attrPath) % showPos(pos) % showPos (t->pos)); % showAttrPath(attrPath) % showPos(pos) % showPos(t->pos));
if (!t->children.empty()) if (!t->children.empty())
throw ParseError(format("duplicate definition of attribute `%1%' at %2%") throw ParseError(format("duplicate definition of attribute `%1%' at %2%")
% showAttrPath(attrPath) % showPos(pos)); % showAttrPath(attrPath) % showPos(pos));
@ -289,30 +288,11 @@ static Pos makeCurPos(YYLTYPE * loc, ParseData * data)
void yyerror(YYLTYPE * loc, yyscan_t scanner, ParseData * data, const char * error) void yyerror(YYLTYPE * loc, yyscan_t scanner, ParseData * data, const char * error)
{ {
data->error = (format("%1%, at `%2%':%3%:%4%") data->error = (format("%1%, at %2%")
% error % data->path % loc->first_line % loc->first_column).str(); % error % makeCurPos(loc, data)).str();
} }
/* Make sure that the parse stack is scanned by the ATerm garbage
collector. */
static void * mallocAndProtect(size_t size)
{
void * p = malloc(size);
if (p) ATprotectMemory(p, size);
return p;
}
static void freeAndUnprotect(void * p)
{
ATunprotectMemory(p);
free(p);
}
#define YYMALLOC mallocAndProtect
#define YYFREE freeAndUnprotect
#endif #endif
@ -329,18 +309,20 @@ static void freeAndUnprotect(void * p)
char * path; char * path;
char * uri; char * uri;
std::list<std::string> * ids; std::list<std::string> * ids;
std::vector<nix::Expr *> * string_parts;
} }
%type <e> start expr expr_function expr_if expr_op %type <e> start expr expr_function expr_if expr_op
%type <e> expr_app expr_select expr_simple %type <e> expr_app expr_select expr_simple
%type <list> expr_list %type <list> expr_list
%type <attrs> binds %type <attrs> binds
%type <ts> attrpath string_parts ind_string_parts %type <ts> attrpath ind_string_parts
%type <formals> formals %type <formals> formals
%type <formal> formal %type <formal> formal
%type <ids> ids %type <ids> ids
%type <string_parts> string_parts
%token <id> ID ATTRPATH %token <id> ID ATTRPATH
%token <t> STR IND_STR %token <e> STR IND_STR
%token <n> INT %token <n> INT
%token <path> PATH %token <path> PATH
%token <uri> URI %token <uri> URI
@ -375,9 +357,8 @@ expr_function
{ $$ = new ExprLambda(CUR_POS, $5, true, $2, $7); } { $$ = new ExprLambda(CUR_POS, $5, true, $2, $7); }
| ID '@' '{' formals '}' ':' expr_function | ID '@' '{' formals '}' ':' expr_function
{ $$ = new ExprLambda(CUR_POS, $1, true, $4, $7); } { $$ = new ExprLambda(CUR_POS, $1, true, $4, $7); }
/* | ASSERT expr ';' expr_function | ASSERT expr ';' expr_function
{ $$ = makeAssert($2, $4, CUR_POS); } { $$ = new ExprAssert(CUR_POS, $2, $4); }
*/
| WITH expr ';' expr_function | WITH expr ';' expr_function
{ $$ = new ExprWith(CUR_POS, $2, $4); } { $$ = new ExprWith(CUR_POS, $2, $4); }
| LET binds IN expr_function | LET binds IN expr_function
@ -391,18 +372,20 @@ expr_if
; ;
expr_op expr_op
: /* '!' expr_op %prec NEG { $$ = makeOpNot($2); } : '!' expr_op %prec NEG { $$ = new ExprOpNot($2); }
| */ | expr_op EQ expr_op { $$ = new ExprOpEq($1, $3); }
expr_op EQ expr_op { $$ = new ExprOpEq($1, $3); }
| expr_op NEQ expr_op { $$ = new ExprOpNEq($1, $3); } | expr_op NEQ expr_op { $$ = new ExprOpNEq($1, $3); }
| expr_op AND expr_op { $$ = new ExprOpAnd($1, $3); } | expr_op AND expr_op { $$ = new ExprOpAnd($1, $3); }
| expr_op OR expr_op { $$ = new ExprOpOr($1, $3); } | expr_op OR expr_op { $$ = new ExprOpOr($1, $3); }
| expr_op IMPL expr_op { $$ = new ExprOpImpl($1, $3); } | expr_op IMPL expr_op { $$ = new ExprOpImpl($1, $3); }
| expr_op UPDATE expr_op { $$ = new ExprOpUpdate($1, $3); } | expr_op UPDATE expr_op { $$ = new ExprOpUpdate($1, $3); }
/* | expr_op '?' ID { $$ = new ExprOpHasAttr($1, $3); }
| expr_op '?' ID { $$ = makeOpHasAttr($1, $3); } | expr_op '+' expr_op
*/ { vector<Expr *> * l = new vector<Expr *>;
| expr_op '+' expr_op { $$ = new ExprOpConcatStrings($1, $3); } l->push_back($1);
l->push_back($3);
$$ = new ExprConcatStrings(l);
}
| expr_op CONCAT expr_op { $$ = new ExprOpConcatLists($1, $3); } | expr_op CONCAT expr_op { $$ = new ExprOpConcatLists($1, $3); }
| expr_app | expr_app
; ;
@ -421,26 +404,25 @@ expr_select
expr_simple expr_simple
: ID { $$ = new ExprVar($1); } : ID { $$ = new ExprVar($1); }
| INT { $$ = new ExprInt($1); } /* | INT { $$ = new ExprInt($1); }
| '"' string_parts '"' { | '"' string_parts '"' {
/* For efficiency, and to simplify parse trees a bit. * / /* For efficiency, and to simplify parse trees a bit. */
if ($2 == ATempty) $$ = makeStr(toATerm(""), ATempty); if ($2->empty()) $$ = new ExprString("");
else if (ATgetNext($2) == ATempty) $$ = ATgetFirst($2); else if ($2->size() == 1) $$ = $2->front();
else $$ = makeConcatStrings(ATreverse($2)); else $$ = new ExprConcatStrings($2);
} }
/*
| IND_STRING_OPEN ind_string_parts IND_STRING_CLOSE { | IND_STRING_OPEN ind_string_parts IND_STRING_CLOSE {
$$ = stripIndentation(ATreverse($2)); $$ = stripIndentation(ATreverse($2));
} }
*/ */
| PATH { $$ = new ExprPath(absPath($1, data->basePath)); } | PATH { $$ = new ExprPath(absPath($1, data->basePath)); }
| URI { $$ = new ExprString($1); } | URI { $$ = new ExprString($1); }
| '(' expr ')' { $$ = $2; } | '(' expr ')' { $$ = $2; }
/*
/* Let expressions `let {..., body = ...}' are just desugared /* Let expressions `let {..., body = ...}' are just desugared
into `(rec {..., body = ...}).body'. * / into `(rec {..., body = ...}).body'. */
| LET '{' binds '}' | LET '{' binds '}'
{ $$ = makeSelect(fixAttrs(true, $3), toATerm("body")); } { fixAttrs(*$3); $3->recursive = true; $$ = new ExprSelect($3, "body"); }
*/
| REC '{' binds '}' | REC '{' binds '}'
{ fixAttrs(*$3); $3->recursive = true; $$ = $3; } { fixAttrs(*$3); $3->recursive = true; $$ = $3; }
| '{' binds '}' | '{' binds '}'
@ -449,9 +431,9 @@ expr_simple
; ;
string_parts string_parts
: string_parts STR { $$ = ATinsert($1, $2); } : string_parts STR { $$ = $1; $1->push_back($2); }
| string_parts DOLLAR_CURLY expr '}' { backToString(scanner); $$ = ATinsert($1, $3); } | string_parts DOLLAR_CURLY expr '}' { backToString(scanner); $$ = $1; $1->push_back($3); }
| { $$ = ATempty; } | { $$ = new vector<Expr *>; }
; ;
ind_string_parts ind_string_parts