aboutsummaryrefslogtreecommitdiff
path: root/src/libexpr
diff options
context:
space:
mode:
authorEelco Dolstra <eelco.dolstra@logicblox.com>2016-02-12 12:49:59 +0100
committerEelco Dolstra <eelco.dolstra@logicblox.com>2016-02-12 12:49:59 +0100
commitb3e8d72770b4100843c60b35633e529e6e69d543 (patch)
treef50070c3c31dd862b837bb514a9e0b10e68216a1 /src/libexpr
parentae4a3cfa030438ca05ad3bf61fa301dee6c1dbb5 (diff)
parent5cdcaf5e8edd6679f667502eec421ac4e725d4ef (diff)
Merge pull request #762 from ctheune/ctheune-floats
Implement floats
Diffstat (limited to 'src/libexpr')
-rw-r--r--src/libexpr/eval.cc57
-rw-r--r--src/libexpr/eval.hh1
-rw-r--r--src/libexpr/get-drvs.cc21
-rw-r--r--src/libexpr/get-drvs.hh3
-rw-r--r--src/libexpr/json-to-value.cc25
-rw-r--r--src/libexpr/lexer.l7
-rw-r--r--src/libexpr/nixexpr.cc9
-rw-r--r--src/libexpr/nixexpr.hh9
-rw-r--r--src/libexpr/parser.y3
-rw-r--r--src/libexpr/primops.cc41
-rw-r--r--src/libexpr/value-to-json.cc4
-rw-r--r--src/libexpr/value-to-xml.cc4
-rw-r--r--src/libexpr/value.hh11
13 files changed, 172 insertions, 23 deletions
diff --git a/src/libexpr/eval.cc b/src/libexpr/eval.cc
index de9454422..8ce2f3dfa 100644
--- a/src/libexpr/eval.cc
+++ b/src/libexpr/eval.cc
@@ -128,6 +128,9 @@ static void printValue(std::ostream & str, std::set<const Value *> & active, con
case tExternal:
str << *v.external;
break;
+ case tFloat:
+ str << v.fpoint;
+ break;
default:
throw Error("invalid value");
}
@@ -161,6 +164,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();
}
@@ -579,6 +583,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++;
@@ -666,6 +676,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;
@@ -1211,6 +1226,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;
@@ -1229,15 +1245,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);
@@ -1295,6 +1326,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);
@@ -1413,6 +1455,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()) {
@@ -1474,6 +1517,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) {
@@ -1531,6 +1581,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));
}
diff --git a/src/libexpr/eval.hh b/src/libexpr/eval.hh
index cf2d57864..40e05712b 100644
--- a/src/libexpr/eval.hh
+++ b/src/libexpr/eval.hh
@@ -147,6 +147,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);
diff --git a/src/libexpr/get-drvs.cc b/src/libexpr/get-drvs.cc
index 1002ee628..996c2c5f4 100644
--- a/src/libexpr/get-drvs.cc
+++ b/src/libexpr/get-drvs.cc
@@ -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;
}
@@ -127,7 +128,7 @@ 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;
@@ -135,12 +136,26 @@ int DrvInfo::queryMetaInt(const string & name, int def)
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)
{
diff --git a/src/libexpr/get-drvs.hh b/src/libexpr/get-drvs.hh
index 98f762494..365c66c8d 100644
--- a/src/libexpr/get-drvs.hh
+++ b/src/libexpr/get-drvs.hh
@@ -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);
diff --git a/src/libexpr/json-to-value.cc b/src/libexpr/json-to-value.cc
index 0898b5609..7ef2b2c56 100644
--- a/src/libexpr/json-to-value.cc
+++ b/src/libexpr/json-to-value.cc
@@ -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) {
diff --git a/src/libexpr/lexer.l b/src/libexpr/lexer.l
index 478f41641..701c01aff 100644
--- a/src/libexpr/lexer.l
+++ b/src/libexpr/lexer.l
@@ -86,6 +86,7 @@ static Expr * unescapeStr(SymbolTable & symbols, const char * s)
ID [a-zA-Z\_][a-zA-Z0-9\_\'\-]*
INT [0-9]+
+FLOAT (([1-9][0-9]*\.[0-9]*)|(0?\.[0-9]+))([Ee][+-]?[0-9]+)?
PATH [a-zA-Z0-9\.\_\-\+]*(\/[a-zA-Z0-9\.\_\-\+]+)+
HPATH \~(\/[a-zA-Z0-9\.\_\-\+]+)+
SPATH \<[a-zA-Z0-9\.\_\-\+]+(\/[a-zA-Z0-9\.\_\-\+]+)*\>
@@ -126,6 +127,12 @@ or { return OR_KW; }
throw ParseError(format("invalid integer ‘%1%’") % yytext);
return INT;
}
+{FLOAT} { errno = 0;
+ yylval->nf = strtod(yytext, 0);
+ if (errno != 0)
+ throw ParseError(format("invalid float ‘%1%’") % yytext);
+ return FLOAT;
+ }
\$\{ { PUSH_STATE(INSIDE_DOLLAR_CURLY); return DOLLAR_CURLY; }
}
diff --git a/src/libexpr/nixexpr.cc b/src/libexpr/nixexpr.cc
index 5bc8e4202..b2c9f0528 100644
--- a/src/libexpr/nixexpr.cc
+++ b/src/libexpr/nixexpr.cc
@@ -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);
@@ -226,6 +231,10 @@ void ExprInt::bindVars(const StaticEnv & env)
{
}
+void ExprFloat::bindVars(const StaticEnv & env)
+{
+}
+
void ExprString::bindVars(const StaticEnv & env)
{
}
diff --git a/src/libexpr/nixexpr.hh b/src/libexpr/nixexpr.hh
index ef07d4557..5e7bc40c8 100644
--- a/src/libexpr/nixexpr.hh
+++ b/src/libexpr/nixexpr.hh
@@ -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;
diff --git a/src/libexpr/parser.y b/src/libexpr/parser.y
index 3e6c6d917..f87aa2619 100644
--- a/src/libexpr/parser.y
+++ b/src/libexpr/parser.y
@@ -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;
@@ -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
@@ -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);
diff --git a/src/libexpr/primops.cc b/src/libexpr/primops.cc
index 2371ae174..3c899d769 100644
--- a/src/libexpr/primops.cc
+++ b/src/libexpr/primops.cc
@@ -195,6 +195,7 @@ static void prim_typeOf(EvalState & state, const Pos & pos, Value * * args, Valu
case tExternal:
t = args[0]->external->typeOf();
break;
+ case tFloat: t = "float"; break;
default: abort();
}
mkString(v, state.symbols.create(t));
@@ -224,6 +225,12 @@ static void prim_isInt(EvalState & state, const Pos & pos, Value * * args, Value
mkBool(v, args[0]->type == tInt);
}
+/* Determine whether the argument is a float. */
+static void prim_isFloat(EvalState & state, const Pos & pos, Value * * args, Value & v)
+{
+ state.forceValue(*args[0]);
+ mkBool(v, args[0]->type == tFloat);
+}
/* Determine whether the argument is a string. */
static void prim_isString(EvalState & state, const Pos & pos, Value * * args, Value & v)
@@ -245,11 +252,17 @@ struct CompareValues
{
bool operator () (const Value * v1, const Value * v2) const
{
+ if (v1->type == tFloat && v2->type == tInt)
+ return v1->fpoint < v2->integer;
+ if (v1->type == tInt && v2->type == tFloat)
+ return v1->integer < v2->fpoint;
if (v1->type != v2->type)
throw EvalError(format("cannot compare %1% with %2%") % showType(*v1) % showType(*v2));
switch (v1->type) {
case tInt:
return v1->integer < v2->integer;
+ case tFloat:
+ return v1->fpoint < v2->fpoint;
case tString:
return strcmp(v1->string.s, v2->string.s) < 0;
case tPath:
@@ -1423,27 +1436,40 @@ static void prim_sort(EvalState & state, const Pos & pos, Value * * args, Value
static void prim_add(EvalState & state, const Pos & pos, Value * * args, Value & v)
{
- mkInt(v, state.forceInt(*args[0], pos) + state.forceInt(*args[1], pos));
+ if (args[0]->type == tFloat || args[1]->type == tFloat)
+ mkFloat(v, state.forceFloat(*args[0], pos) + state.forceFloat(*args[1], pos));
+ else
+ mkInt(v, state.forceInt(*args[0], pos) + state.forceInt(*args[1], pos));
}
static void prim_sub(EvalState & state, const Pos & pos, Value * * args, Value & v)
{
- mkInt(v, state.forceInt(*args[0], pos) - state.forceInt(*args[1], pos));
+ if (args[0]->type == tFloat || args[1]->type == tFloat)
+ mkFloat(v, state.forceFloat(*args[0], pos) - state.forceFloat(*args[1], pos));
+ else
+ mkInt(v, state.forceInt(*args[0], pos) - state.forceInt(*args[1], pos));
}
static void prim_mul(EvalState & state, const Pos & pos, Value * * args, Value & v)
{
- mkInt(v, state.forceInt(*args[0], pos) * state.forceInt(*args[1], pos));
+ if (args[0]->type == tFloat || args[1]->type == tFloat)
+ mkFloat(v, state.forceFloat(*args[0], pos) * state.forceFloat(*args[1], pos));
+ else
+ mkInt(v, state.forceInt(*args[0], pos) * state.forceInt(*args[1], pos));
}
static void prim_div(EvalState & state, const Pos & pos, Value * * args, Value & v)
{
- NixInt i2 = state.forceInt(*args[1], pos);
- if (i2 == 0) throw EvalError(format("division by zero, at %1%") % pos);
- mkInt(v, state.forceInt(*args[0], pos) / i2);
+ NixFloat f2 = state.forceFloat(*args[1], pos);
+ if (f2 == 0) throw EvalError(format("division by zero, at %1%") % pos);
+
+ if (args[0]->type == tFloat || args[1]->type == tFloat)
+ mkFloat(v, state.forceFloat(*args[0], pos) / state.forceFloat(*args[1], pos));
+ else
+ mkInt(v, state.forceInt(*args[0], pos) / state.forceInt(*args[1], pos));
}
@@ -1735,7 +1761,7 @@ void EvalState::createBaseEnv()
language feature gets added. It's not necessary to increase it
when primops get added, because you can just use `builtins ?
primOp' to check. */
- mkInt(v, 3);
+ mkInt(v, 4);
addConstant("__langVersion", v);
// Miscellaneous
@@ -1752,6 +1778,7 @@ void EvalState::createBaseEnv()
addPrimOp("__isFunction", 1, prim_isFunction);
addPrimOp("__isString", 1, prim_isString);
addPrimOp("__isInt", 1, prim_isInt);
+ addPrimOp("__isFloat", 1, prim_isFloat);
addPrimOp("__isBool", 1, prim_isBool);
addPrimOp("__genericClosure", 1, prim_genericClosure);
addPrimOp("abort", 1, prim_abort);
diff --git a/src/libexpr/value-to-json.cc b/src/libexpr/value-to-json.cc
index b0cf85e21..47ee324a6 100644
--- a/src/libexpr/value-to-json.cc
+++ b/src/libexpr/value-to-json.cc
@@ -84,6 +84,10 @@ void printValueAsJSON(EvalState & state, bool strict,
v.external->printValueAsJSON(state, strict, str, context);
break;
+ case tFloat:
+ str << v.fpoint;
+ break;
+
default:
throw TypeError(format("cannot convert %1% to JSON") % showType(v));
}
diff --git a/src/libexpr/value-to-xml.cc b/src/libexpr/value-to-xml.cc
index cb52ce1e6..00b1918a8 100644
--- a/src/libexpr/value-to-xml.cc
+++ b/src/libexpr/value-to-xml.cc
@@ -148,6 +148,10 @@ static void printValueAsXML(EvalState & state, bool strict, bool location,
v.external->printValueAsXML(state, strict, location, doc, context, drvsSeen);
break;
+ case tFloat:
+ doc.writeEmptyElement("float", singletonAttrs("value", (format("%1%") % v.fpoint).str()));
+ break;
+
default:
doc.writeEmptyElement("unevaluated");
}
diff --git a/src/libexpr/value.hh b/src/libexpr/value.hh
index e6d1502cb..62bdd9281 100644
--- a/src/libexpr/value.hh
+++ b/src/libexpr/value.hh
@@ -22,6 +22,7 @@ typedef enum {
tPrimOp,
tPrimOpApp,
tExternal,
+ tFloat
} ValueType;
@@ -38,6 +39,7 @@ class XMLWriter;
typedef long NixInt;
+typedef float NixFloat;
/* External values must descend from ExternalValueBase, so that
* type-agnostic nix functions (e.g. showType) can be implemented
@@ -141,6 +143,7 @@ struct Value
Value * left, * right;
} primOpApp;
ExternalValueBase * external;
+ NixFloat fpoint;
};
bool isList() const
@@ -181,6 +184,14 @@ static inline void mkInt(Value & v, NixInt n)
}
+static inline void mkFloat(Value & v, NixFloat n)
+{
+ clearValue(v);
+ v.type = tFloat;
+ v.fpoint = n;
+}
+
+
static inline void mkBool(Value & v, bool b)
{
clearValue(v);