forked from lix-project/lix
commit
b3e8d72770
|
@ -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 derivation’s output
|
translated to a JSON string containing the derivation’s output
|
||||||
|
|
|
@ -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.,
|
||||||
|
|
|
@ -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>.
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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) {
|
||||||
throwEvalError("cannot add %1% to an integer, at %2%", showType(vTmp), pos);
|
|
||||||
n += vTmp.integer;
|
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);
|
||||||
|
} 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));
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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)
|
||||||
{
|
{
|
||||||
|
|
|
@ -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);
|
||||||
|
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
|
@ -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; }
|
||||||
}
|
}
|
||||||
|
|
|
@ -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)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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)
|
||||||
{
|
{
|
||||||
|
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));
|
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)
|
||||||
{
|
{
|
||||||
|
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));
|
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)
|
||||||
{
|
{
|
||||||
|
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));
|
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);
|
||||||
|
|
|
@ -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));
|
||||||
}
|
}
|
||||||
|
|
|
@ -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");
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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);
|
||||||
|
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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";
|
||||||
|
|
|
@ -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;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -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 ];
|
g = [ 1 2 3 ];
|
||||||
h = [ "a" [ "b" { "foo\nbar" = {}; } ] ];
|
h = [ "a" [ "b" { "foo\nbar" = {}; } ] ];
|
||||||
i = 1 + 2;
|
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" ])
|
(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; })
|
||||||
|
|
|
@ -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>
|
||||||
|
|
|
@ -2,6 +2,8 @@ rec {
|
||||||
|
|
||||||
x = 123;
|
x = 123;
|
||||||
|
|
||||||
|
y = 567.890;
|
||||||
|
|
||||||
a = "foo";
|
a = "foo";
|
||||||
|
|
||||||
b = "bar";
|
b = "bar";
|
||||||
|
|
Loading…
Reference in a new issue