First hit at providing support for floats in the language.
This commit is contained in:
		
							parent
							
								
									b8258a4475
								
							
						
					
					
						commit
						14ebde5289
					
				
					 16 changed files with 207 additions and 23 deletions
				
			
		|  | @ -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) { | ||||||
|  |                 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); | ||||||
|  | @ -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) | ||||||
| { | { | ||||||
|     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)); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|  | @ -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…
	
	Add table
		Add a link
		
	
		Reference in a new issue