commit
b3e8d72770
|
@ -32,7 +32,7 @@ available as <function>builtins.derivation</function>.</para>
|
|||
<varlistentry><term><function>builtins.add</function>
|
||||
<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>e2</replaceable>.</para></listitem>
|
||||
|
||||
|
@ -204,7 +204,7 @@ if builtins ? getEnv then builtins.getEnv "PATH" else ""</programlisting>
|
|||
<varlistentry><term><function>builtins.div</function>
|
||||
<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>e2</replaceable>.</para></listitem>
|
||||
|
||||
|
@ -620,12 +620,12 @@ x: x + 456</programlisting>
|
|||
<varlistentry><term><function>builtins.lessThan</function>
|
||||
<replaceable>e1</replaceable> <replaceable>e2</replaceable></term>
|
||||
|
||||
<listitem><para>Return <literal>true</literal> if the integer
|
||||
<replaceable>e1</replaceable> is less than the integer
|
||||
<listitem><para>Return <literal>true</literal> if the number
|
||||
<replaceable>e1</replaceable> is less than the number
|
||||
<replaceable>e2</replaceable>, and <literal>false</literal>
|
||||
otherwise. Evaluation aborts if either
|
||||
<replaceable>e1</replaceable> or <replaceable>e2</replaceable>
|
||||
does not evaluate to an integer.</para></listitem>
|
||||
does not evaluate to a number.</para></listitem>
|
||||
|
||||
</varlistentry>
|
||||
|
||||
|
@ -676,7 +676,7 @@ map (x: "foo" + x) [ "bar" "bla" "abc" ]</programlisting>
|
|||
<varlistentry><term><function>builtins.mul</function>
|
||||
<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>e2</replaceable>.</para></listitem>
|
||||
|
||||
|
@ -833,7 +833,7 @@ builtins.sort builtins.lessThan [ 483 249 526 147 42 77 ]
|
|||
<varlistentry><term><function>builtins.sub</function>
|
||||
<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>e2</replaceable>.</para></listitem>
|
||||
|
||||
|
@ -960,7 +960,7 @@ in foo</programlisting>
|
|||
<varlistentry><term><function>builtins.toJSON</function> <replaceable>e</replaceable></term>
|
||||
|
||||
<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
|
||||
(except derivations) are represented as objects. Derivations are
|
||||
translated to a JSON string containing the derivation’s output
|
||||
|
|
|
@ -43,7 +43,7 @@ of which specify the inputs of the build.</para>
|
|||
|
||||
<itemizedlist>
|
||||
|
||||
<listitem><para>Strings and integers are just passed
|
||||
<listitem><para>Strings and numbers are just passed
|
||||
verbatim.</para></listitem>
|
||||
|
||||
<listitem><para>A <emphasis>path</emphasis> (e.g.,
|
||||
|
|
|
@ -140,8 +140,13 @@ stdenv.mkDerivation {
|
|||
|
||||
</listitem>
|
||||
|
||||
<listitem><para><emphasis>Integers</emphasis>, e.g.,
|
||||
<literal>123</literal>.</para></listitem>
|
||||
<listitem><para>Numbers, which can be <emphasis>integers</emphasis> (like
|
||||
<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.,
|
||||
<filename>/bin/sh</filename> or <filename>./builder.sh</filename>.
|
||||
|
|
|
@ -121,6 +121,13 @@ $ diffoscope /nix/store/11a27shh6n2i…-zlib-1.2.8 /nix/store/11a27shh6n2i…-zl
|
|||
also improves performance.</para>
|
||||
</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>
|
||||
<para>All "chroot"-containing strings got renamed to "sandbox".
|
||||
In particular, some Nix options got renamed, but the old names
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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; }
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
|
|
|
@ -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");
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -66,6 +66,7 @@ template<class N> N getIntArg(const string & opt,
|
|||
return n * multiplier;
|
||||
}
|
||||
|
||||
|
||||
/* Show the manual page for the specified program. */
|
||||
void showManPage(const string & name);
|
||||
|
||||
|
|
|
@ -366,6 +366,14 @@ template<class N> bool string2Int(const string & s, N & n)
|
|||
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'. */
|
||||
bool hasSuffix(const string & s, const string & suffix);
|
||||
|
|
|
@ -1127,6 +1127,10 @@ static void opQuery(Globals & globals, Strings opFlags, Strings opArgs)
|
|||
attrs2["type"] = "int";
|
||||
attrs2["value"] = (format("%1%") % v->integer).str();
|
||||
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) {
|
||||
attrs2["type"] = "bool";
|
||||
attrs2["value"] = v->boolean ? "true" : "false";
|
||||
|
|
|
@ -12,7 +12,9 @@ builtins.fromJSON
|
|||
"Width": 100
|
||||
},
|
||||
"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;
|
||||
IDs = [ 116 943 234 38793 true false null (0-100) ];
|
||||
Latitude = 37.7668;
|
||||
Longitude = -122.3959;
|
||||
};
|
||||
}
|
||||
|
|
|
@ -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}"
|
||||
|
|
|
@ -8,4 +8,5 @@ builtins.toJSON
|
|||
g = [ 1 2 3 ];
|
||||
h = [ "a" [ "b" { "foo\nbar" = {}; } ] ];
|
||||
i = 1 + 2;
|
||||
j = 1.44;
|
||||
}
|
||||
|
|
|
@ -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" ]
|
||||
|
|
|
@ -8,6 +8,16 @@ with builtins;
|
|||
(isString [ "x" ])
|
||||
(isInt (1 + 2))
|
||||
(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 null)
|
||||
(isAttrs { x = 123; })
|
||||
|
|
|
@ -45,5 +45,8 @@
|
|||
<attr name="x">
|
||||
<int value="123" />
|
||||
</attr>
|
||||
<attr name="y">
|
||||
<float value="567.89" />
|
||||
</attr>
|
||||
</attrs>
|
||||
</expr>
|
||||
|
|
|
@ -2,6 +2,8 @@ rec {
|
|||
|
||||
x = 123;
|
||||
|
||||
y = 567.890;
|
||||
|
||||
a = "foo";
|
||||
|
||||
b = "bar";
|
||||
|
|
Loading…
Reference in a new issue