Skip to content

Commit

Permalink
First hit at providing support for floats in the language.
Browse files Browse the repository at this point in the history
  • Loading branch information
ctheune committed Jan 4, 2016
1 parent b8258a4 commit 14ebde5
Show file tree
Hide file tree
Showing 16 changed files with 207 additions and 23 deletions.
56 changes: 54 additions & 2 deletions src/libexpr/eval.cc
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,8 @@ static void printValue(std::ostream & str, std::set<const Value *> & active, con
case tExternal:
str << *v.external;
break;
case tFloat:
str << v.fpoint;
default:
throw Error("invalid value");
}
Expand Down Expand Up @@ -161,6 +163,7 @@ string showType(const Value & v)
case tPrimOp: return "a built-in function";
case tPrimOpApp: return "a partially applied built-in function";
case tExternal: return v.external->showType();
case tFloat: return "a float";
}
abort();
}
Expand Down Expand Up @@ -577,6 +580,12 @@ Value * ExprInt::maybeThunk(EvalState & state, Env & env)
return &v;
}

Value * ExprFloat::maybeThunk(EvalState & state, Env & env)
{
nrAvoided++;
return &v;
}

Value * ExprPath::maybeThunk(EvalState & state, Env & env)
{
nrAvoided++;
Expand Down Expand Up @@ -664,6 +673,11 @@ void ExprInt::eval(EvalState & state, Env & env, Value & v)
}


void ExprFloat::eval(EvalState & state, Env & env, Value & v)
{
v = this->v;
}

void ExprString::eval(EvalState & state, Env & env, Value & v)
{
v = this->v;
Expand Down Expand Up @@ -1209,6 +1223,7 @@ void ExprConcatStrings::eval(EvalState & state, Env & env, Value & v)
PathSet context;
std::ostringstream s;
NixInt n = 0;
NixFloat nf = 0;

bool first = !forceString;
ValueType firstType = tString;
Expand All @@ -1227,15 +1242,30 @@ void ExprConcatStrings::eval(EvalState & state, Env & env, Value & v)
}

if (firstType == tInt) {
if (vTmp.type != tInt)
if (vTmp.type == tInt) {
n += vTmp.integer;
} else if (vTmp.type == tFloat) {
// Upgrade the type from int to float;
firstType = tFloat;
nf = n;
nf += vTmp.fpoint;
} else
throwEvalError("cannot add %1% to an integer, at %2%", showType(vTmp), pos);
n += vTmp.integer;
} else if (firstType == tFloat) {
if (vTmp.type == tInt) {
nf += vTmp.integer;
} else if (vTmp.type == tFloat) {
nf += vTmp.fpoint;
} else
throwEvalError("cannot add %1% to a float, at %2%", showType(vTmp), pos);
} else
s << state.coerceToString(pos, vTmp, context, false, firstType == tString);
}

if (firstType == tInt)
mkInt(v, n);
else if (firstType == tFloat)
mkFloat(v, nf);
else if (firstType == tPath) {
if (!context.empty())
throwEvalError("a string that refers to a store path cannot be appended to a path, at %1%", pos);
Expand Down Expand Up @@ -1293,6 +1323,17 @@ NixInt EvalState::forceInt(Value & v, const Pos & pos)
}


NixFloat EvalState::forceFloat(Value & v, const Pos & pos)
{
forceValue(v, pos);
if (v.type == tInt)
return v.integer;
else if (v.type != tFloat)
throwTypeError("value is %1% while a float was expected, at %2%", v, pos);
return v.fpoint;
}


bool EvalState::forceBool(Value & v)
{
forceValue(v);
Expand Down Expand Up @@ -1404,6 +1445,7 @@ string EvalState::coerceToString(const Pos & pos, Value & v, PathSet & context,
if (v.type == tBool && v.boolean) return "1";
if (v.type == tBool && !v.boolean) return "";
if (v.type == tInt) return std::to_string(v.integer);
if (v.type == tFloat) return std::to_string(v.fpoint);
if (v.type == tNull) return "";

if (v.isList()) {
Expand Down Expand Up @@ -1465,6 +1507,13 @@ bool EvalState::eqValues(Value & v1, Value & v2)
uniqList on a list of sets.) Will remove this eventually. */
if (&v1 == &v2) return true;

// Special case type-compatibility between float and int
if (v1.type == tInt && v2.type == tFloat)
return v1.integer == v2.fpoint;
if (v1.type == tFloat && v2.type == tInt)
return v1.fpoint == v2.integer;

// All other types are not compatible with each other.
if (v1.type != v2.type) return false;

switch (v1.type) {
Expand Down Expand Up @@ -1522,6 +1571,9 @@ bool EvalState::eqValues(Value & v1, Value & v2)
case tExternal:
return *v1.external == *v2.external;

case tFloat:
return v1.fpoint == v2.fpoint;

default:
throwEvalError("cannot compare %1% with %2%", showType(v1), showType(v2));
}
Expand Down
1 change: 1 addition & 0 deletions src/libexpr/eval.hh
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,7 @@ public:

/* Force `v', and then verify that it has the expected type. */
NixInt forceInt(Value & v, const Pos & pos);
NixFloat forceFloat(Value & v, const Pos & pos);
bool forceBool(Value & v);
inline void forceAttrs(Value & v);
inline void forceAttrs(Value & v, const Pos & pos);
Expand Down
21 changes: 18 additions & 3 deletions src/libexpr/get-drvs.cc
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,8 @@ bool DrvInfo::checkMeta(Value & v)
if (!checkMeta(*i.value)) return false;
return true;
}
else return v.type == tInt || v.type == tBool || v.type == tString;
else return v.type == tInt || v.type == tBool || v.type == tString ||
v.type == tFloat;
}


Expand All @@ -127,20 +128,34 @@ string DrvInfo::queryMetaString(const string & name)
}


int DrvInfo::queryMetaInt(const string & name, int def)
NixInt DrvInfo::queryMetaInt(const string & name, NixInt def)
{
Value * v = queryMeta(name);
if (!v) return def;
if (v->type == tInt) return v->integer;
if (v->type == tString) {
/* Backwards compatibility with before we had support for
integer meta fields. */
int n;
NixInt n;
if (string2Int(v->string.s, n)) return n;
}
return def;
}

NixFloat DrvInfo::queryMetaFloat(const string & name, NixFloat def)
{
Value * v = queryMeta(name);
if (!v) return def;
if (v->type == tFloat) return v->fpoint;
if (v->type == tString) {
/* Backwards compatibility with before we had support for
float meta fields. */
NixFloat n;
if (string2Float(v->string.s, n)) return n;
}
return def;
}


bool DrvInfo::queryMetaBool(const string & name, bool def)
{
Expand Down
3 changes: 2 additions & 1 deletion src/libexpr/get-drvs.hh
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,8 @@ public:
StringSet queryMetaNames();
Value * queryMeta(const string & name);
string queryMetaString(const string & name);
int queryMetaInt(const string & name, int def);
NixInt queryMetaInt(const string & name, NixInt def);
NixFloat queryMetaFloat(const string & name, NixFloat def);
bool queryMetaBool(const string & name, bool def);
void setMeta(const string & name, Value * v);

Expand Down
25 changes: 15 additions & 10 deletions src/libexpr/json-to-value.cc
Original file line number Diff line number Diff line change
Expand Up @@ -105,17 +105,22 @@ static void parseJSON(EvalState & state, const char * & s, Value & v)
mkString(v, parseJSONString(s));
}

else if (isdigit(*s) || *s == '-') {
bool neg = false;
if (*s == '-') {
neg = true;
if (!*++s) throw JSONParseError("unexpected end of JSON number");
else if (isdigit(*s) || *s == '-' || *s == '.' ) {
// Buffer into a string first, then use built-in C++ conversions
std::string tmp_number;
ValueType number_type = tInt;

while (isdigit(*s) || *s == '-' || *s == '.' || *s == 'e' || *s == 'E') {
if (*s == '.' || *s == 'e' || *s == 'E')
number_type = tFloat;
tmp_number.append(*s++, 1);
}

if (number_type == tFloat) {
mkFloat(v, stod(tmp_number));
} else {
mkInt(v, stoi(tmp_number));
}
NixInt n = 0;
// FIXME: detect overflow
while (isdigit(*s)) n = n * 10 + (*s++ - '0');
if (*s == '.' || *s == 'e') throw JSONParseError("floating point JSON numbers are not supported");
mkInt(v, neg ? -n : n);
}

else if (strncmp(s, "true", 4) == 0) {
Expand Down
7 changes: 7 additions & 0 deletions src/libexpr/lexer.l
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,7 @@ static Expr * unescapeStr(SymbolTable & symbols, const char * s)

ID [a-zA-Z\_][a-zA-Z0-9\_\'\-]*
INT [0-9]+
FLOAT {INT}[0-9]+\.[0-9]+[eE]-?{INT}
PATH [a-zA-Z0-9\.\_\-\+]*(\/[a-zA-Z0-9\.\_\-\+]+)+
HPATH \~(\/[a-zA-Z0-9\.\_\-\+]+)+
SPATH \<[a-zA-Z0-9\.\_\-\+]+(\/[a-zA-Z0-9\.\_\-\+]+)*\>
Expand Down Expand Up @@ -123,6 +124,12 @@ or { return OR_KW; }
throw ParseError(format("invalid integer ‘%1%’") % yytext);
return INT;
}
{FLOAT} { errno = 0;
yylval->n = strtod(yytext, 0);
if (errno != 0)
throw ParseError(format("invalid float ‘%1%’") % yytext);
return FLOAT;
}

\$\{ { PUSH_STATE(INITIAL); return DOLLAR_CURLY; }
\{ { PUSH_STATE(INITIAL); return '{'; }
Expand Down
9 changes: 9 additions & 0 deletions src/libexpr/nixexpr.cc
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,11 @@ void ExprInt::show(std::ostream & str)
str << n;
}

void ExprFloat::show(std::ostream & str)
{
str << nf;
}

void ExprString::show(std::ostream & str)
{
showString(str, s);
Expand Down Expand Up @@ -226,6 +231,10 @@ void ExprInt::bindVars(const StaticEnv & env)
{
}

void ExprFloat::bindVars(const StaticEnv & env)
{
}

void ExprString::bindVars(const StaticEnv & env)
{
}
Expand Down
9 changes: 9 additions & 0 deletions src/libexpr/nixexpr.hh
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,15 @@ struct ExprInt : Expr
Value * maybeThunk(EvalState & state, Env & env);
};

struct ExprFloat : Expr
{
NixFloat nf;
Value v;
ExprFloat(NixFloat nf) : nf(nf) { mkFloat(v, nf); };
COMMON_METHODS
Value * maybeThunk(EvalState & state, Env & env);
};

struct ExprString : Expr
{
Symbol s;
Expand Down
3 changes: 3 additions & 0 deletions src/libexpr/parser.y
Original file line number Diff line number Diff line change
Expand Up @@ -244,6 +244,7 @@ void yyerror(YYLTYPE * loc, yyscan_t scanner, ParseData * data, const char * err
nix::Formals * formals;
nix::Formal * formal;
nix::NixInt n;
nix::NixFloat nf;
const char * id; // !!! -> Symbol
char * path;
char * uri;
Expand All @@ -264,6 +265,7 @@ void yyerror(YYLTYPE * loc, yyscan_t scanner, ParseData * data, const char * err
%token <id> ID ATTRPATH
%token <e> STR IND_STR
%token <n> INT
%token <nf> FLOAT
%token <path> PATH HPATH SPATH
%token <uri> URI
%token IF THEN ELSE ASSERT WITH LET IN REC INHERIT EQ NEQ AND OR IMPL OR_KW
Expand Down Expand Up @@ -366,6 +368,7 @@ expr_simple
$$ = new ExprVar(CUR_POS, data->symbols.create($1));
}
| INT { $$ = new ExprInt($1); }
| FLOAT { $$ = new ExprFloat($1); }
| '"' string_parts '"' { $$ = $2; }
| IND_STRING_OPEN ind_string_parts IND_STRING_CLOSE {
$$ = stripIndentation(CUR_POS, data->symbols, *$2);
Expand Down

0 comments on commit 14ebde5

Please sign in to comment.