Merge pull request #762 from ctheune/ctheune-floats

Implement floats
This commit is contained in:
Eelco Dolstra 2016-02-12 12:49:59 +01:00
commit b3e8d72770
27 changed files with 231 additions and 37 deletions

View file

@ -32,7 +32,7 @@ available as <function>builtins.derivation</function>.</para>
<varlistentry><term><function>builtins.add</function> <varlistentry><term><function>builtins.add</function>
<replaceable>e1</replaceable> <replaceable>e2</replaceable></term> <replaceable>e1</replaceable> <replaceable>e2</replaceable></term>
<listitem><para>Return the sum of the integers <listitem><para>Return the sum of the numbers
<replaceable>e1</replaceable> and <replaceable>e1</replaceable> and
<replaceable>e2</replaceable>.</para></listitem> <replaceable>e2</replaceable>.</para></listitem>
@ -204,7 +204,7 @@ if builtins ? getEnv then builtins.getEnv "PATH" else ""</programlisting>
<varlistentry><term><function>builtins.div</function> <varlistentry><term><function>builtins.div</function>
<replaceable>e1</replaceable> <replaceable>e2</replaceable></term> <replaceable>e1</replaceable> <replaceable>e2</replaceable></term>
<listitem><para>Return the quotient of the integers <listitem><para>Return the quotient of the numbers
<replaceable>e1</replaceable> and <replaceable>e1</replaceable> and
<replaceable>e2</replaceable>.</para></listitem> <replaceable>e2</replaceable>.</para></listitem>
@ -620,12 +620,12 @@ x: x + 456</programlisting>
<varlistentry><term><function>builtins.lessThan</function> <varlistentry><term><function>builtins.lessThan</function>
<replaceable>e1</replaceable> <replaceable>e2</replaceable></term> <replaceable>e1</replaceable> <replaceable>e2</replaceable></term>
<listitem><para>Return <literal>true</literal> if the integer <listitem><para>Return <literal>true</literal> if the number
<replaceable>e1</replaceable> is less than the integer <replaceable>e1</replaceable> is less than the number
<replaceable>e2</replaceable>, and <literal>false</literal> <replaceable>e2</replaceable>, and <literal>false</literal>
otherwise. Evaluation aborts if either otherwise. Evaluation aborts if either
<replaceable>e1</replaceable> or <replaceable>e2</replaceable> <replaceable>e1</replaceable> or <replaceable>e2</replaceable>
does not evaluate to an integer.</para></listitem> does not evaluate to a number.</para></listitem>
</varlistentry> </varlistentry>
@ -676,7 +676,7 @@ map (x: "foo" + x) [ "bar" "bla" "abc" ]</programlisting>
<varlistentry><term><function>builtins.mul</function> <varlistentry><term><function>builtins.mul</function>
<replaceable>e1</replaceable> <replaceable>e2</replaceable></term> <replaceable>e1</replaceable> <replaceable>e2</replaceable></term>
<listitem><para>Return the product of the integers <listitem><para>Return the product of the numbers
<replaceable>e1</replaceable> and <replaceable>e1</replaceable> and
<replaceable>e2</replaceable>.</para></listitem> <replaceable>e2</replaceable>.</para></listitem>
@ -833,7 +833,7 @@ builtins.sort builtins.lessThan [ 483 249 526 147 42 77 ]
<varlistentry><term><function>builtins.sub</function> <varlistentry><term><function>builtins.sub</function>
<replaceable>e1</replaceable> <replaceable>e2</replaceable></term> <replaceable>e1</replaceable> <replaceable>e2</replaceable></term>
<listitem><para>Return the difference between the integers <listitem><para>Return the difference between the numbers
<replaceable>e1</replaceable> and <replaceable>e1</replaceable> and
<replaceable>e2</replaceable>.</para></listitem> <replaceable>e2</replaceable>.</para></listitem>
@ -960,7 +960,7 @@ in foo</programlisting>
<varlistentry><term><function>builtins.toJSON</function> <replaceable>e</replaceable></term> <varlistentry><term><function>builtins.toJSON</function> <replaceable>e</replaceable></term>
<listitem><para>Return a string containing a JSON representation <listitem><para>Return a string containing a JSON representation
of <replaceable>e</replaceable>. Strings, integers, booleans, of <replaceable>e</replaceable>. Strings, integers, floats, booleans,
nulls and lists are mapped to their JSON equivalents. Sets nulls and lists are mapped to their JSON equivalents. Sets
(except derivations) are represented as objects. Derivations are (except derivations) are represented as objects. Derivations are
translated to a JSON string containing the derivations output translated to a JSON string containing the derivations output

View file

@ -43,7 +43,7 @@ of which specify the inputs of the build.</para>
<itemizedlist> <itemizedlist>
<listitem><para>Strings and integers are just passed <listitem><para>Strings and numbers are just passed
verbatim.</para></listitem> verbatim.</para></listitem>
<listitem><para>A <emphasis>path</emphasis> (e.g., <listitem><para>A <emphasis>path</emphasis> (e.g.,

View file

@ -140,8 +140,13 @@ stdenv.mkDerivation {
</listitem> </listitem>
<listitem><para><emphasis>Integers</emphasis>, e.g., <listitem><para>Numbers, which can be <emphasis>integers</emphasis> (like
<literal>123</literal>.</para></listitem> <literal>123</literal>) or <emphasis>floating point</emphasis> (like
<literal>123.43</literal> or <literal>.27e13</literal>).</para>
<para>Numbers are type-compatible: pure integer operations will always
return integers, whereas any operation involving at least one floating point
number will have a floating point number as a result.</para></listitem>
<listitem><para><emphasis>Paths</emphasis>, e.g., <listitem><para><emphasis>Paths</emphasis>, e.g.,
<filename>/bin/sh</filename> or <filename>./builder.sh</filename>. <filename>/bin/sh</filename> or <filename>./builder.sh</filename>.

View file

@ -121,6 +121,13 @@ $ diffoscope /nix/store/11a27shh6n2i…-zlib-1.2.8 /nix/store/11a27shh6n2i…-zl
also improves performance.</para> also improves performance.</para>
</listitem> </listitem>
<listitem>
<para>The Nix language now supports floating point numbers. They are
based on regular C++ <literal>float</literal> and compatible with
existing integers and number-related operations. Export and import to and
from JSON and XML works, too.
</para>
</listitem>
<listitem> <listitem>
<para>All "chroot"-containing strings got renamed to "sandbox". <para>All "chroot"-containing strings got renamed to "sandbox".
In particular, some Nix options got renamed, but the old names In particular, some Nix options got renamed, but the old names

View file

@ -128,6 +128,9 @@ static void printValue(std::ostream & str, std::set<const Value *> & active, con
case tExternal: case tExternal:
str << *v.external; str << *v.external;
break; break;
case tFloat:
str << v.fpoint;
break;
default: default:
throw Error("invalid value"); throw Error("invalid value");
} }
@ -161,6 +164,7 @@ string showType(const Value & v)
case tPrimOp: return "a built-in function"; case tPrimOp: return "a built-in function";
case tPrimOpApp: return "a partially applied built-in function"; case tPrimOpApp: return "a partially applied built-in function";
case tExternal: return v.external->showType(); case tExternal: return v.external->showType();
case tFloat: return "a float";
} }
abort(); abort();
} }
@ -579,6 +583,12 @@ Value * ExprInt::maybeThunk(EvalState & state, Env & env)
return &v; return &v;
} }
Value * ExprFloat::maybeThunk(EvalState & state, Env & env)
{
nrAvoided++;
return &v;
}
Value * ExprPath::maybeThunk(EvalState & state, Env & env) Value * ExprPath::maybeThunk(EvalState & state, Env & env)
{ {
nrAvoided++; 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) void ExprString::eval(EvalState & state, Env & env, Value & v)
{ {
v = this->v; v = this->v;
@ -1211,6 +1226,7 @@ void ExprConcatStrings::eval(EvalState & state, Env & env, Value & v)
PathSet context; PathSet context;
std::ostringstream s; std::ostringstream s;
NixInt n = 0; NixInt n = 0;
NixFloat nf = 0;
bool first = !forceString; bool first = !forceString;
ValueType firstType = tString; ValueType firstType = tString;
@ -1229,15 +1245,30 @@ void ExprConcatStrings::eval(EvalState & state, Env & env, Value & v)
} }
if (firstType == tInt) { 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); 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 } else
s << state.coerceToString(pos, vTmp, context, false, firstType == tString); s << state.coerceToString(pos, vTmp, context, false, firstType == tString);
} }
if (firstType == tInt) if (firstType == tInt)
mkInt(v, n); mkInt(v, n);
else if (firstType == tFloat)
mkFloat(v, nf);
else if (firstType == tPath) { else if (firstType == tPath) {
if (!context.empty()) if (!context.empty())
throwEvalError("a string that refers to a store path cannot be appended to a path, at %1%", pos); 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) bool EvalState::forceBool(Value & v)
{ {
forceValue(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 "1";
if (v.type == tBool && !v.boolean) return ""; if (v.type == tBool && !v.boolean) return "";
if (v.type == tInt) return std::to_string(v.integer); 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.type == tNull) return "";
if (v.isList()) { if (v.isList()) {
@ -1474,6 +1517,13 @@ bool EvalState::eqValues(Value & v1, Value & v2)
uniqList on a list of sets.) Will remove this eventually. */ uniqList on a list of sets.) Will remove this eventually. */
if (&v1 == &v2) return true; 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; if (v1.type != v2.type) return false;
switch (v1.type) { switch (v1.type) {
@ -1531,6 +1581,9 @@ bool EvalState::eqValues(Value & v1, Value & v2)
case tExternal: case tExternal:
return *v1.external == *v2.external; return *v1.external == *v2.external;
case tFloat:
return v1.fpoint == v2.fpoint;
default: default:
throwEvalError("cannot compare %1% with %2%", showType(v1), showType(v2)); throwEvalError("cannot compare %1% with %2%", showType(v1), showType(v2));
} }

View file

@ -147,6 +147,7 @@ public:
/* Force `v', and then verify that it has the expected type. */ /* Force `v', and then verify that it has the expected type. */
NixInt forceInt(Value & v, const Pos & pos); NixInt forceInt(Value & v, const Pos & pos);
NixFloat forceFloat(Value & v, const Pos & pos);
bool forceBool(Value & v); bool forceBool(Value & v);
inline void forceAttrs(Value & v); inline void forceAttrs(Value & v);
inline void forceAttrs(Value & v, const Pos & pos); inline void forceAttrs(Value & v, const Pos & pos);

View file

@ -106,7 +106,8 @@ bool DrvInfo::checkMeta(Value & v)
if (!checkMeta(*i.value)) return false; if (!checkMeta(*i.value)) return false;
return true; 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); Value * v = queryMeta(name);
if (!v) return def; if (!v) return def;
@ -135,12 +136,26 @@ int DrvInfo::queryMetaInt(const string & name, int def)
if (v->type == tString) { if (v->type == tString) {
/* Backwards compatibility with before we had support for /* Backwards compatibility with before we had support for
integer meta fields. */ integer meta fields. */
int n; NixInt n;
if (string2Int(v->string.s, n)) return n; if (string2Int(v->string.s, n)) return n;
} }
return def; 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) bool DrvInfo::queryMetaBool(const string & name, bool def)
{ {

View file

@ -47,7 +47,8 @@ public:
StringSet queryMetaNames(); StringSet queryMetaNames();
Value * queryMeta(const string & name); Value * queryMeta(const string & name);
string queryMetaString(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); bool queryMetaBool(const string & name, bool def);
void setMeta(const string & name, Value * v); void setMeta(const string & name, Value * v);

View file

@ -105,17 +105,22 @@ static void parseJSON(EvalState & state, const char * & s, Value & v)
mkString(v, parseJSONString(s)); mkString(v, parseJSONString(s));
} }
else if (isdigit(*s) || *s == '-') { else if (isdigit(*s) || *s == '-' || *s == '.' ) {
bool neg = false; // Buffer into a string first, then use built-in C++ conversions
if (*s == '-') { std::string tmp_number;
neg = true; ValueType number_type = tInt;
if (!*++s) throw JSONParseError("unexpected end of JSON number");
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) { else if (strncmp(s, "true", 4) == 0) {

View file

@ -86,6 +86,7 @@ static Expr * unescapeStr(SymbolTable & symbols, const char * s)
ID [a-zA-Z\_][a-zA-Z0-9\_\'\-]* ID [a-zA-Z\_][a-zA-Z0-9\_\'\-]*
INT [0-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\.\_\-\+]+)+ PATH [a-zA-Z0-9\.\_\-\+]*(\/[a-zA-Z0-9\.\_\-\+]+)+
HPATH \~(\/[a-zA-Z0-9\.\_\-\+]+)+ HPATH \~(\/[a-zA-Z0-9\.\_\-\+]+)+
SPATH \<[a-zA-Z0-9\.\_\-\+]+(\/[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); throw ParseError(format("invalid integer %1%") % yytext);
return INT; 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; } \$\{ { PUSH_STATE(INSIDE_DOLLAR_CURLY); return DOLLAR_CURLY; }
} }

View file

@ -68,6 +68,11 @@ void ExprInt::show(std::ostream & str)
str << n; str << n;
} }
void ExprFloat::show(std::ostream & str)
{
str << nf;
}
void ExprString::show(std::ostream & str) void ExprString::show(std::ostream & str)
{ {
showString(str, s); 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) void ExprString::bindVars(const StaticEnv & env)
{ {
} }

View file

@ -98,6 +98,15 @@ struct ExprInt : Expr
Value * maybeThunk(EvalState & state, Env & env); 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 struct ExprString : Expr
{ {
Symbol s; Symbol s;

View file

@ -244,6 +244,7 @@ void yyerror(YYLTYPE * loc, yyscan_t scanner, ParseData * data, const char * err
nix::Formals * formals; nix::Formals * formals;
nix::Formal * formal; nix::Formal * formal;
nix::NixInt n; nix::NixInt n;
nix::NixFloat nf;
const char * id; // !!! -> Symbol const char * id; // !!! -> Symbol
char * path; char * path;
char * uri; char * uri;
@ -264,6 +265,7 @@ void yyerror(YYLTYPE * loc, yyscan_t scanner, ParseData * data, const char * err
%token <id> ID ATTRPATH %token <id> ID ATTRPATH
%token <e> STR IND_STR %token <e> STR IND_STR
%token <n> INT %token <n> INT
%token <nf> FLOAT
%token <path> PATH HPATH SPATH %token <path> PATH HPATH SPATH
%token <uri> URI %token <uri> URI
%token IF THEN ELSE ASSERT WITH LET IN REC INHERIT EQ NEQ AND OR IMPL OR_KW %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)); $$ = new ExprVar(CUR_POS, data->symbols.create($1));
} }
| INT { $$ = new ExprInt($1); } | INT { $$ = new ExprInt($1); }
| FLOAT { $$ = new ExprFloat($1); }
| '"' string_parts '"' { $$ = $2; } | '"' string_parts '"' { $$ = $2; }
| IND_STRING_OPEN ind_string_parts IND_STRING_CLOSE { | IND_STRING_OPEN ind_string_parts IND_STRING_CLOSE {
$$ = stripIndentation(CUR_POS, data->symbols, *$2); $$ = stripIndentation(CUR_POS, data->symbols, *$2);

View file

@ -195,6 +195,7 @@ static void prim_typeOf(EvalState & state, const Pos & pos, Value * * args, Valu
case tExternal: case tExternal:
t = args[0]->external->typeOf(); t = args[0]->external->typeOf();
break; break;
case tFloat: t = "float"; break;
default: abort(); default: abort();
} }
mkString(v, state.symbols.create(t)); 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); 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. */ /* Determine whether the argument is a string. */
static void prim_isString(EvalState & state, const Pos & pos, Value * * args, Value & v) 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 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) if (v1->type != v2->type)
throw EvalError(format("cannot compare %1% with %2%") % showType(*v1) % showType(*v2)); throw EvalError(format("cannot compare %1% with %2%") % showType(*v1) % showType(*v2));
switch (v1->type) { switch (v1->type) {
case tInt: case tInt:
return v1->integer < v2->integer; return v1->integer < v2->integer;
case tFloat:
return v1->fpoint < v2->fpoint;
case tString: case tString:
return strcmp(v1->string.s, v2->string.s) < 0; return strcmp(v1->string.s, v2->string.s) < 0;
case tPath: 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) 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) 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) 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) static void prim_div(EvalState & state, const Pos & pos, Value * * args, Value & v)
{ {
NixInt i2 = state.forceInt(*args[1], pos); NixFloat f2 = state.forceFloat(*args[1], pos);
if (i2 == 0) throw EvalError(format("division by zero, at %1%") % pos); if (f2 == 0) throw EvalError(format("division by zero, at %1%") % pos);
mkInt(v, state.forceInt(*args[0], pos) / i2);
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 language feature gets added. It's not necessary to increase it
when primops get added, because you can just use `builtins ? when primops get added, because you can just use `builtins ?
primOp' to check. */ primOp' to check. */
mkInt(v, 3); mkInt(v, 4);
addConstant("__langVersion", v); addConstant("__langVersion", v);
// Miscellaneous // Miscellaneous
@ -1752,6 +1778,7 @@ void EvalState::createBaseEnv()
addPrimOp("__isFunction", 1, prim_isFunction); addPrimOp("__isFunction", 1, prim_isFunction);
addPrimOp("__isString", 1, prim_isString); addPrimOp("__isString", 1, prim_isString);
addPrimOp("__isInt", 1, prim_isInt); addPrimOp("__isInt", 1, prim_isInt);
addPrimOp("__isFloat", 1, prim_isFloat);
addPrimOp("__isBool", 1, prim_isBool); addPrimOp("__isBool", 1, prim_isBool);
addPrimOp("__genericClosure", 1, prim_genericClosure); addPrimOp("__genericClosure", 1, prim_genericClosure);
addPrimOp("abort", 1, prim_abort); addPrimOp("abort", 1, prim_abort);

View file

@ -84,6 +84,10 @@ void printValueAsJSON(EvalState & state, bool strict,
v.external->printValueAsJSON(state, strict, str, context); v.external->printValueAsJSON(state, strict, str, context);
break; break;
case tFloat:
str << v.fpoint;
break;
default: default:
throw TypeError(format("cannot convert %1% to JSON") % showType(v)); throw TypeError(format("cannot convert %1% to JSON") % showType(v));
} }

View file

@ -148,6 +148,10 @@ static void printValueAsXML(EvalState & state, bool strict, bool location,
v.external->printValueAsXML(state, strict, location, doc, context, drvsSeen); v.external->printValueAsXML(state, strict, location, doc, context, drvsSeen);
break; break;
case tFloat:
doc.writeEmptyElement("float", singletonAttrs("value", (format("%1%") % v.fpoint).str()));
break;
default: default:
doc.writeEmptyElement("unevaluated"); doc.writeEmptyElement("unevaluated");
} }

View file

@ -22,6 +22,7 @@ typedef enum {
tPrimOp, tPrimOp,
tPrimOpApp, tPrimOpApp,
tExternal, tExternal,
tFloat
} ValueType; } ValueType;
@ -38,6 +39,7 @@ class XMLWriter;
typedef long NixInt; typedef long NixInt;
typedef float NixFloat;
/* External values must descend from ExternalValueBase, so that /* External values must descend from ExternalValueBase, so that
* type-agnostic nix functions (e.g. showType) can be implemented * type-agnostic nix functions (e.g. showType) can be implemented
@ -141,6 +143,7 @@ struct Value
Value * left, * right; Value * left, * right;
} primOpApp; } primOpApp;
ExternalValueBase * external; ExternalValueBase * external;
NixFloat fpoint;
}; };
bool isList() const 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) static inline void mkBool(Value & v, bool b)
{ {
clearValue(v); clearValue(v);

View file

@ -66,6 +66,7 @@ template<class N> N getIntArg(const string & opt,
return n * multiplier; return n * multiplier;
} }
/* Show the manual page for the specified program. */ /* Show the manual page for the specified program. */
void showManPage(const string & name); void showManPage(const string & name);

View file

@ -366,6 +366,14 @@ template<class N> bool string2Int(const string & s, N & n)
return str && str.get() == EOF; return str && str.get() == EOF;
} }
/* Parse a string into a float. */
template<class N> bool string2Float(const string & s, N & n)
{
std::istringstream str(s);
str >> n;
return str && str.get() == EOF;
}
/* Return true iff `s' ends in `suffix'. */ /* Return true iff `s' ends in `suffix'. */
bool hasSuffix(const string & s, const string & suffix); bool hasSuffix(const string & s, const string & suffix);

View file

@ -1127,6 +1127,10 @@ static void opQuery(Globals & globals, Strings opFlags, Strings opArgs)
attrs2["type"] = "int"; attrs2["type"] = "int";
attrs2["value"] = (format("%1%") % v->integer).str(); attrs2["value"] = (format("%1%") % v->integer).str();
xml.writeEmptyElement("meta", attrs2); xml.writeEmptyElement("meta", attrs2);
} else if (v->type == tFloat) {
attrs2["type"] = "float";
attrs2["value"] = (format("%1%") % v->fpoint).str();
xml.writeEmptyElement("meta", attrs2);
} else if (v->type == tBool) { } else if (v->type == tBool) {
attrs2["type"] = "bool"; attrs2["type"] = "bool";
attrs2["value"] = v->boolean ? "true" : "false"; attrs2["value"] = v->boolean ? "true" : "false";

View file

@ -12,7 +12,9 @@ builtins.fromJSON
"Width": 100 "Width": 100
}, },
"Animated" : false, "Animated" : false,
"IDs": [116, 943, 234, 38793, true ,false,null, -100] "IDs": [116, 943, 234, 38793, true ,false,null, -100],
"Latitude": 37.7668,
"Longitude": -122.3959,
} }
} }
'' ''
@ -28,5 +30,7 @@ builtins.fromJSON
}; };
Animated = false; Animated = false;
IDs = [ 116 943 234 38793 true false null (0-100) ]; IDs = [ 116 943 234 38793 true false null (0-100) ];
Latitude = 37.7668;
Longitude = -122.3959;
}; };
} }

View file

@ -1 +1 @@
"{\"a\":123,\"b\":-456,\"c\":\"foo\",\"d\":\"foo\\n\\\"bar\\\"\",\"e\":true,\"f\":false,\"g\":[1,2,3],\"h\":[\"a\",[\"b\",{\"foo\\nbar\":{}}]],\"i\":3}" "{\"a\":123,\"b\":-456,\"c\":\"foo\",\"d\":\"foo\\n\\\"bar\\\"\",\"e\":true,\"f\":false,\"g\":[1,2,3],\"h\":[\"a\",[\"b\",{\"foo\\nbar\":{}}]],\"i\":3,\"j\":1.44}"

View file

@ -8,4 +8,5 @@ builtins.toJSON
g = [ 1 2 3 ]; g = [ 1 2 3 ];
h = [ "a" [ "b" { "foo\nbar" = {}; } ] ]; h = [ "a" [ "b" { "foo\nbar" = {}; } ] ];
i = 1 + 2; i = 1 + 2;
j = 1.44;
} }

View file

@ -1 +1 @@
[ true false true false true false true false true false true false "int" "bool" "string" "null" "set" "list" "lambda" "lambda" "lambda" "lambda" ] [ true false true false true false true false true true true true true true true true true true true false true false "int" "bool" "string" "null" "set" "list" "lambda" "lambda" "lambda" "lambda" ]

View file

@ -8,6 +8,16 @@ with builtins;
(isString [ "x" ]) (isString [ "x" ])
(isInt (1 + 2)) (isInt (1 + 2))
(isInt { x = 123; }) (isInt { x = 123; })
(isInt (1 / 2))
(isInt (1 + 1))
(isInt (1 / 2))
(isInt (1 * 2))
(isInt (1 - 2))
(isFloat (1.2))
(isFloat (1 + 1.0))
(isFloat (1 / 2.0))
(isFloat (1 * 2.0))
(isFloat (1 - 2.0))
(isBool (true && false)) (isBool (true && false))
(isBool null) (isBool null)
(isAttrs { x = 123; }) (isAttrs { x = 123; })

View file

@ -45,5 +45,8 @@
<attr name="x"> <attr name="x">
<int value="123" /> <int value="123" />
</attr> </attr>
<attr name="y">
<float value="567.89" />
</attr>
</attrs> </attrs>
</expr> </expr>

View file

@ -2,6 +2,8 @@ rec {
x = 123; x = 123;
y = 567.890;
a = "foo"; a = "foo";
b = "bar"; b = "bar";