diff --git a/src/libexpr/meson.build b/src/libexpr/meson.build index 67743f0f5..e30b0a1c9 100644 --- a/src/libexpr/meson.build +++ b/src/libexpr/meson.build @@ -86,6 +86,7 @@ libexpr_sources = files( 'flake/flake.cc', 'flake/flakeref.cc', 'flake/lockfile.cc', + 'primops/arithmetic.cc', 'primops/attrset.cc', 'primops/context.cc', 'primops/control.cc', diff --git a/src/libexpr/primops.cc b/src/libexpr/primops.cc index d8351087c..b6c7447d4 100644 --- a/src/libexpr/primops.cc +++ b/src/libexpr/primops.cc @@ -652,7 +652,6 @@ static RegisterPrimOp primop_genericClosure(PrimOp { .fun = prim_genericClosure, }); - static void prim_addErrorContext(EvalState & state, const PosIdx pos, Value * * args, Value & v) { try { @@ -674,45 +673,6 @@ static RegisterPrimOp primop_addErrorContext(PrimOp { .fun = prim_addErrorContext, }); -static void prim_ceil(EvalState & state, const PosIdx pos, Value * * args, Value & v) -{ - auto value = state.forceFloat(*args[0], args[0]->determinePos(pos), - "while evaluating the first argument passed to builtins.ceil"); - v.mkInt(ceil(value)); -} - -static RegisterPrimOp primop_ceil({ - .name = "__ceil", - .args = {"double"}, - .doc = R"( - Converts an IEEE-754 double-precision floating-point number (*double*) to - the next higher integer. - - If the datatype is neither an integer nor a "float", an evaluation error will be - thrown. - )", - .fun = prim_ceil, -}); - -static void prim_floor(EvalState & state, const PosIdx pos, Value * * args, Value & v) -{ - auto value = state.forceFloat(*args[0], args[0]->determinePos(pos), "while evaluating the first argument passed to builtins.floor"); - v.mkInt(floor(value)); -} - -static RegisterPrimOp primop_floor({ - .name = "__floor", - .args = {"double"}, - .doc = R"( - Converts an IEEE-754 double-precision floating-point number (*double*) to - the next lower integer. - - If the datatype is neither an integer nor a "float", an evaluation error will be - thrown. - )", - .fun = prim_floor, -}); - /* Try evaluating the argument. Success => {success=true; value=something;}, * else => {success=false; value=false;} */ @@ -2314,164 +2274,7 @@ static RegisterPrimOp primop_isList({ *************************************************************/ -static void prim_add(EvalState & state, const PosIdx pos, Value * * args, Value & v) -{ - state.forceValue(*args[0], pos); - state.forceValue(*args[1], pos); - if (args[0]->type() == nFloat || args[1]->type() == nFloat) - v.mkFloat(state.forceFloat(*args[0], pos, "while evaluating the first argument of the addition") - + state.forceFloat(*args[1], pos, "while evaluating the second argument of the addition")); - else - v.mkInt( state.forceInt(*args[0], pos, "while evaluating the first argument of the addition") - + state.forceInt(*args[1], pos, "while evaluating the second argument of the addition")); -} -static RegisterPrimOp primop_add({ - .name = "__add", - .args = {"e1", "e2"}, - .doc = R"( - Return the sum of the numbers *e1* and *e2*. - )", - .fun = prim_add, -}); - -static void prim_sub(EvalState & state, const PosIdx pos, Value * * args, Value & v) -{ - state.forceValue(*args[0], pos); - state.forceValue(*args[1], pos); - if (args[0]->type() == nFloat || args[1]->type() == nFloat) - v.mkFloat(state.forceFloat(*args[0], pos, "while evaluating the first argument of the subtraction") - - state.forceFloat(*args[1], pos, "while evaluating the second argument of the subtraction")); - else - v.mkInt( state.forceInt(*args[0], pos, "while evaluating the first argument of the subtraction") - - state.forceInt(*args[1], pos, "while evaluating the second argument of the subtraction")); -} - -static RegisterPrimOp primop_sub({ - .name = "__sub", - .args = {"e1", "e2"}, - .doc = R"( - Return the difference between the numbers *e1* and *e2*. - )", - .fun = prim_sub, -}); - -static void prim_mul(EvalState & state, const PosIdx pos, Value * * args, Value & v) -{ - state.forceValue(*args[0], pos); - state.forceValue(*args[1], pos); - if (args[0]->type() == nFloat || args[1]->type() == nFloat) - v.mkFloat(state.forceFloat(*args[0], pos, "while evaluating the first of the multiplication") - * state.forceFloat(*args[1], pos, "while evaluating the second argument of the multiplication")); - else - v.mkInt( state.forceInt(*args[0], pos, "while evaluating the first argument of the multiplication") - * state.forceInt(*args[1], pos, "while evaluating the second argument of the multiplication")); -} - -static RegisterPrimOp primop_mul({ - .name = "__mul", - .args = {"e1", "e2"}, - .doc = R"( - Return the product of the numbers *e1* and *e2*. - )", - .fun = prim_mul, -}); - -static void prim_div(EvalState & state, const PosIdx pos, Value * * args, Value & v) -{ - state.forceValue(*args[0], pos); - state.forceValue(*args[1], pos); - - NixFloat f2 = state.forceFloat(*args[1], pos, "while evaluating the second operand of the division"); - if (f2 == 0) - state.error("division by zero").atPos(pos).debugThrow(); - - if (args[0]->type() == nFloat || args[1]->type() == nFloat) { - v.mkFloat(state.forceFloat(*args[0], pos, "while evaluating the first operand of the division") / f2); - } else { - NixInt i1 = state.forceInt(*args[0], pos, "while evaluating the first operand of the division"); - NixInt i2 = state.forceInt(*args[1], pos, "while evaluating the second operand of the division"); - /* Avoid division overflow as it might raise SIGFPE. */ - if (i1 == std::numeric_limits::min() && i2 == -1) - state.error("overflow in integer division").atPos(pos).debugThrow(); - - v.mkInt(i1 / i2); - } -} - -static RegisterPrimOp primop_div({ - .name = "__div", - .args = {"e1", "e2"}, - .doc = R"( - Return the quotient of the numbers *e1* and *e2*. - )", - .fun = prim_div, -}); - -static void prim_bitAnd(EvalState & state, const PosIdx pos, Value * * args, Value & v) -{ - v.mkInt(state.forceInt(*args[0], pos, "while evaluating the first argument passed to builtins.bitAnd") - & state.forceInt(*args[1], pos, "while evaluating the second argument passed to builtins.bitAnd")); -} - -static RegisterPrimOp primop_bitAnd({ - .name = "__bitAnd", - .args = {"e1", "e2"}, - .doc = R"( - Return the bitwise AND of the integers *e1* and *e2*. - )", - .fun = prim_bitAnd, -}); - -static void prim_bitOr(EvalState & state, const PosIdx pos, Value * * args, Value & v) -{ - v.mkInt(state.forceInt(*args[0], pos, "while evaluating the first argument passed to builtins.bitOr") - | state.forceInt(*args[1], pos, "while evaluating the second argument passed to builtins.bitOr")); -} - -static RegisterPrimOp primop_bitOr({ - .name = "__bitOr", - .args = {"e1", "e2"}, - .doc = R"( - Return the bitwise OR of the integers *e1* and *e2*. - )", - .fun = prim_bitOr, -}); - -static void prim_bitXor(EvalState & state, const PosIdx pos, Value * * args, Value & v) -{ - v.mkInt(state.forceInt(*args[0], pos, "while evaluating the first argument passed to builtins.bitXor") - ^ state.forceInt(*args[1], pos, "while evaluating the second argument passed to builtins.bitXor")); -} - -static RegisterPrimOp primop_bitXor({ - .name = "__bitXor", - .args = {"e1", "e2"}, - .doc = R"( - Return the bitwise XOR of the integers *e1* and *e2*. - )", - .fun = prim_bitXor, -}); - -void prim_lessThan(EvalState & state, const PosIdx pos, Value * * args, Value & v) -{ - state.forceValue(*args[0], pos); - state.forceValue(*args[1], pos); - // pos is exact here, no need for a message. - CompareValues comp(state, noPos, ""); - v.mkBool(comp(args[0], args[1])); -} - -static RegisterPrimOp primop_lessThan({ - .name = "__lessThan", - .args = {"e1", "e2"}, - .doc = R"( - Return `true` if the number *e1* is less than the number *e2*, and - `false` otherwise. Evaluation aborts if either *e1* or *e2* does not - evaluate to a number. - )", - .fun = prim_lessThan, -}); /************************************************************* diff --git a/src/libexpr/primops/arithmetic.cc b/src/libexpr/primops/arithmetic.cc new file mode 100644 index 000000000..4b3857728 --- /dev/null +++ b/src/libexpr/primops/arithmetic.cc @@ -0,0 +1,306 @@ +#include "primops.hh" + +namespace nix { + +/** + * builtins.add + */ + +static void prim_add(EvalState & state, const PosIdx pos, Value ** args, Value & v) +{ + state.forceValue(*args[0], pos); + state.forceValue(*args[1], pos); + if (args[0]->type() == nFloat || args[1]->type() == nFloat) { + v.mkFloat( + state.forceFloat(*args[0], pos, "while evaluating the first argument of the addition") + + state.forceFloat( + *args[1], pos, "while evaluating the second argument of the addition" + ) + ); + } else { + v.mkInt( + state.forceInt(*args[0], pos, "while evaluating the first argument of the addition") + + state.forceInt(*args[1], pos, "while evaluating the second argument of the addition") + ); + } +} + +static RegisterPrimOp primop_add({ + .name = "__add", + .args = {"e1", "e2"}, + .doc = R"( + Return the sum of the numbers *e1* and *e2*. + Return a float if either *e1* or *e2* is a float, otherwise + return an integer. + )", + .fun = prim_add, +}); + +/** + * builtins.bitAnd + */ + +static void prim_bitAnd(EvalState & state, const PosIdx pos, Value ** args, Value & v) +{ + v.mkInt( + state.forceInt( + *args[0], pos, "while evaluating the first argument passed to builtins.bitAnd" + ) + & state.forceInt( + *args[1], pos, "while evaluating the second argument passed to builtins.bitAnd" + ) + ); +} + +static RegisterPrimOp primop_bitAnd({ + .name = "__bitAnd", + .args = {"e1", "e2"}, + .doc = R"( + Return the bitwise AND of the integers *e1* and *e2*. + )", + .fun = prim_bitAnd, +}); + +/** + * builtins.bitOr + */ + +static void prim_bitOr(EvalState & state, const PosIdx pos, Value ** args, Value & v) +{ + v.mkInt( + state.forceInt( + *args[0], pos, "while evaluating the first argument passed to builtins.bitOr" + ) + | state.forceInt( + *args[1], pos, "while evaluating the second argument passed to builtins.bitOr" + ) + ); +} + +static RegisterPrimOp primop_bitOr({ + .name = "__bitOr", + .args = {"e1", "e2"}, + .doc = R"( + Return the bitwise OR of the integers *e1* and *e2*. + )", + .fun = prim_bitOr, +}); + +/** + * builtins.bitXor + */ + +static void prim_bitXor(EvalState & state, const PosIdx pos, Value ** args, Value & v) +{ + v.mkInt( + state.forceInt( + *args[0], pos, "while evaluating the first argument passed to builtins.bitXor" + ) + ^ state.forceInt( + *args[1], pos, "while evaluating the second argument passed to builtins.bitXor" + ) + ); +} + +static RegisterPrimOp primop_bitXor({ + .name = "__bitXor", + .args = {"e1", "e2"}, + .doc = R"( + Return the bitwise XOR of the integers *e1* and *e2*. + )", + .fun = prim_bitXor, +}); + +/** + * builtins.ceil + */ + +static void prim_ceil(EvalState & state, const PosIdx pos, Value ** args, Value & v) +{ + auto value = state.forceFloat( + *args[0], + args[0]->determinePos(pos), + "while evaluating the first argument passed to builtins.ceil" + ); + v.mkInt(ceil(value)); +} + +static RegisterPrimOp primop_ceil({ + .name = "__ceil", + .args = {"double"}, + .doc = R"( + Converts an IEEE-754 double-precision floating-point number (*double*) to + the next higher integer. + + If the datatype is neither an integer nor a "float", an evaluation error will be + thrown. + )", + .fun = prim_ceil, +}); + +/** + * builtins.div + */ + +static void prim_div(EvalState & state, const PosIdx pos, Value ** args, Value & v) +{ + state.forceValue(*args[0], pos); + state.forceValue(*args[1], pos); + + NixFloat f2 = + state.forceFloat(*args[1], pos, "while evaluating the second operand of the division"); + if (f2 == 0) { + state.error("division by zero").atPos(pos).debugThrow(); + } + + if (args[0]->type() == nFloat || args[1]->type() == nFloat) { + v.mkFloat( + state.forceFloat(*args[0], pos, "while evaluating the first operand of the division") + / f2 + ); + } else { + NixInt i1 = + state.forceInt(*args[0], pos, "while evaluating the first operand of the division"); + NixInt i2 = + state.forceInt(*args[1], pos, "while evaluating the second operand of the division"); + /* Avoid division overflow as it might raise SIGFPE. */ + if (i1 == std::numeric_limits::min() && i2 == -1) { + state.error("overflow in integer division").atPos(pos).debugThrow(); + } + + v.mkInt(i1 / i2); + } +} + +static RegisterPrimOp primop_div({ + .name = "__div", + .args = {"e1", "e2"}, + .doc = R"( + Return the quotient of the numbers *e1* and *e2*. + )", + .fun = prim_div, +}); + +/** + * builtins.floor + */ + +static void prim_floor(EvalState & state, const PosIdx pos, Value ** args, Value & v) +{ + auto value = state.forceFloat( + *args[0], + args[0]->determinePos(pos), + "while evaluating the first argument passed to builtins.floor" + ); + v.mkInt(floor(value)); +} + +static RegisterPrimOp primop_floor({ + .name = "__floor", + .args = {"double"}, + .doc = R"( + Converts an IEEE-754 double-precision floating-point number (*double*) to + the next lower integer. + + If the datatype is neither an integer nor a "float", an evaluation error will be + thrown. + )", + .fun = prim_floor, +}); + +/** + * builtins.lessThan + */ + +void prim_lessThan(EvalState & state, const PosIdx pos, Value ** args, Value & v) +{ + state.forceValue(*args[0], pos); + state.forceValue(*args[1], pos); + // pos is exact here, no need for a message. + CompareValues comp(state, noPos, ""); + v.mkBool(comp(args[0], args[1])); +} + +static RegisterPrimOp primop_lessThan({ + .name = "__lessThan", + .args = {"e1", "e2"}, + .doc = R"( + Return `true` if the number *e1* is less than the number *e2*, and + `false` otherwise. Evaluation aborts if either *e1* or *e2* does not + evaluate to a number. + )", + .fun = prim_lessThan, +}); + +/** + * builtins.mul + */ + +static void prim_mul(EvalState & state, const PosIdx pos, Value ** args, Value & v) +{ + state.forceValue(*args[0], pos); + state.forceValue(*args[1], pos); + if (args[0]->type() == nFloat || args[1]->type() == nFloat) { + v.mkFloat( + state.forceFloat(*args[0], pos, "while evaluating the first of the multiplication") + * state.forceFloat( + *args[1], pos, "while evaluating the second argument of the multiplication" + ) + ); + } else { + v.mkInt( + state.forceInt( + *args[0], pos, "while evaluating the first argument of the multiplication" + ) + * state.forceInt( + *args[1], pos, "while evaluating the second argument of the multiplication" + ) + ); + } +} + +static RegisterPrimOp primop_mul({ + .name = "__mul", + .args = {"e1", "e2"}, + .doc = R"( + Return the product of the numbers *e1* and *e2*. + )", + .fun = prim_mul, +}); + +/** + * builtins.sub + */ + +static void prim_sub(EvalState & state, const PosIdx pos, Value ** args, Value & v) +{ + state.forceValue(*args[0], pos); + state.forceValue(*args[1], pos); + if (args[0]->type() == nFloat || args[1]->type() == nFloat) { + v.mkFloat( + state.forceFloat( + *args[0], pos, "while evaluating the first argument of the subtraction" + ) + - state.forceFloat( + *args[1], pos, "while evaluating the second argument of the subtraction" + ) + ); + } else { + v.mkInt( + state.forceInt(*args[0], pos, "while evaluating the first argument of the subtraction") + - state.forceInt( + *args[1], pos, "while evaluating the second argument of the subtraction" + ) + ); + } +} + +static RegisterPrimOp primop_sub({ + .name = "__sub", + .args = {"e1", "e2"}, + .doc = R"( + Return the difference between the numbers *e1* and *e2*. + )", + .fun = prim_sub, +}); +}