forked from lix-project/lix
First hit at providing support for floats in the language.
This commit is contained in:
parent
b8258a4475
commit
14ebde5289
|
@ -128,6 +128,8 @@ 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;
|
||||||
default:
|
default:
|
||||||
throw Error("invalid value");
|
throw Error("invalid value");
|
||||||
}
|
}
|
||||||
|
@ -161,6 +163,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();
|
||||||
}
|
}
|
||||||
|
@ -577,6 +580,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++;
|
||||||
|
@ -664,6 +673,11 @@ void ExprInt::eval(EvalState & state, Env & env, Value & v)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void ExprFloat::eval(EvalState & state, Env & env, Value & v)
|
||||||
|
{
|
||||||
|
v = this->v;
|
||||||
|
}
|
||||||
|
|
||||||
void ExprString::eval(EvalState & state, Env & env, Value & v)
|
void ExprString::eval(EvalState & state, Env & env, Value & v)
|
||||||
{
|
{
|
||||||
v = this->v;
|
v = this->v;
|
||||||
|
@ -1209,6 +1223,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;
|
||||||
|
@ -1227,15 +1242,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);
|
||||||
|
@ -1293,6 +1323,17 @@ NixInt EvalState::forceInt(Value & v, const Pos & pos)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
NixFloat EvalState::forceFloat(Value & v, const Pos & pos)
|
||||||
|
{
|
||||||
|
forceValue(v, pos);
|
||||||
|
if (v.type == tInt)
|
||||||
|
return v.integer;
|
||||||
|
else if (v.type != tFloat)
|
||||||
|
throwTypeError("value is %1% while a float was expected, at %2%", v, pos);
|
||||||
|
return v.fpoint;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
bool EvalState::forceBool(Value & v)
|
bool EvalState::forceBool(Value & v)
|
||||||
{
|
{
|
||||||
forceValue(v);
|
forceValue(v);
|
||||||
|
@ -1404,6 +1445,7 @@ string EvalState::coerceToString(const Pos & pos, Value & v, PathSet & context,
|
||||||
if (v.type == tBool && v.boolean) return "1";
|
if (v.type == tBool && v.boolean) return "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()) {
|
||||||
|
@ -1465,6 +1507,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) {
|
||||||
|
@ -1522,6 +1571,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));
|
||||||
}
|
}
|
||||||
|
|
|
@ -144,6 +144,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) {
|
||||||
|
|
|
@ -85,6 +85,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 {INT}[0-9]+\.[0-9]+[eE]-?{INT}
|
||||||
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\.\_\-\+]+)*\>
|
||||||
|
@ -123,6 +124,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->n = strtod(yytext, 0);
|
||||||
|
if (errno != 0)
|
||||||
|
throw ParseError(format("invalid float ‘%1%’") % yytext);
|
||||||
|
return FLOAT;
|
||||||
|
}
|
||||||
|
|
||||||
\$\{ { PUSH_STATE(INITIAL); return DOLLAR_CURLY; }
|
\$\{ { PUSH_STATE(INITIAL); return DOLLAR_CURLY; }
|
||||||
\{ { PUSH_STATE(INITIAL); return '{'; }
|
\{ { PUSH_STATE(INITIAL); return '{'; }
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -197,6 +197,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));
|
||||||
|
@ -226,6 +227,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)
|
||||||
|
@ -247,11 +254,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("cannot compare values of different types");
|
throw EvalError("cannot compare values of different types");
|
||||||
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:
|
||||||
|
@ -1424,27 +1437,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));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -1736,7 +1762,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
|
||||||
|
@ -1753,6 +1779,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 double 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,30 @@ template<class N> N getIntArg(const string & opt,
|
||||||
return n * multiplier;
|
return n * multiplier;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template<class N> N getFloatArg(const string & opt,
|
||||||
|
Strings::iterator & i, const Strings::iterator & end, bool allowUnit)
|
||||||
|
{
|
||||||
|
++i;
|
||||||
|
if (i == end) throw UsageError(format("‘%1%’ requires an argument") % opt);
|
||||||
|
string s = *i;
|
||||||
|
N multiplier = 1;
|
||||||
|
if (allowUnit && !s.empty()) {
|
||||||
|
char u = std::toupper(*s.rbegin());
|
||||||
|
if (std::isalpha(u)) {
|
||||||
|
if (u == 'K') multiplier = 1ULL << 10;
|
||||||
|
else if (u == 'M') multiplier = 1ULL << 20;
|
||||||
|
else if (u == 'G') multiplier = 1ULL << 30;
|
||||||
|
else if (u == 'T') multiplier = 1ULL << 40;
|
||||||
|
else throw UsageError(format("invalid unit specifier ‘%1%’") % u);
|
||||||
|
s.resize(s.size() - 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
N n;
|
||||||
|
if (!string2Float(s, n))
|
||||||
|
throw UsageError(format("‘%1%’ requires a float argument") % opt);
|
||||||
|
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);
|
||||||
|
|
||||||
|
|
|
@ -358,6 +358,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";
|
||||||
|
|
Loading…
Reference in a new issue