Ensure when adding new operators we add them right

Operators not only are needed to be defined by their implementation in
Interpreter constructor, but also are needed to be included in
precedense resolution.

To prevent partial definition we predefine number of builtin operators
as numeric constant and test against it in all relevant places.
This commit is contained in:
Robert Bendun 2022-08-18 21:32:19 +02:00
parent e090778db9
commit a3d0a942e4
3 changed files with 59 additions and 38 deletions

View File

@ -711,27 +711,31 @@ error:
global.force_define("program_change", pgmchange);
}
// Operators definition table
static constexpr auto Operators = std::array {
std::tuple { "+", plus_minus_operator<std::plus<>> },
std::tuple { "-", plus_minus_operator<std::minus<>> },
std::tuple { "*", binary_operator<std::multiplies<>, '*'> },
std::tuple { "/", binary_operator<std::divides<>, '/'> },
operators["+"] = plus_minus_operator<std::plus<>>;
operators["-"] = plus_minus_operator<std::minus<>>;
operators["*"] = binary_operator<std::multiplies<>, '*'>;
operators["/"] = binary_operator<std::divides<>, '/'>;
std::tuple { "<", comparison_operator<std::less<>> },
std::tuple { ">", comparison_operator<std::greater<>> },
std::tuple { "<=", comparison_operator<std::less_equal<>> },
std::tuple { ">=", comparison_operator<std::greater_equal<>> },
operators["<"] = comparison_operator<std::less<>>;
operators[">"] = comparison_operator<std::greater<>>;
operators["<="] = comparison_operator<std::less_equal<>>;
operators[">="] = comparison_operator<std::greater_equal<>>;
std::tuple { "==", equality_operator<std::equal_to<>> },
std::tuple { "!=", equality_operator<std::not_equal_to<>> },
operators["=="] = equality_operator<std::equal_to<>>;
operators["!="] = equality_operator<std::not_equal_to<>>;
operators["."] = +[](Interpreter &i, std::vector<Value> args) -> Result<Value> {
std::tuple { ".",
+[](Interpreter &i, std::vector<Value> args) -> Result<Value> {
assert(args.size() == 2, "Operator . requires two arguments"); // TODO(assert)
assert(args.back().type == Value::Type::Number, "Only numbers can be used for indexing"); // TODO(assert)
return std::move(args.front()).index(i, std::move(args.back()).n.as_int());
};
}
},
operators["&"] = +[](Interpreter &i, std::vector<Value> args) -> Result<Value> {
std::tuple { "&",
+[](Interpreter&, std::vector<Value> args) -> Result<Value> {
using Chord_Chord = Shape<Value::Type::Music, Value::Type::Music>;
if (Chord_Chord::typecheck(args)) {
@ -756,7 +760,17 @@ error:
},
.location = {}
};
}
},
};
// All operators should be defined here except 'and' and 'or' which handle evaluation differently
// and are need unevaluated expressions for their proper evaluation. Exclusion of them is marked
// as subtraction of total excluded operators from expected constant
static_assert(Operators.size() == Operators_Count - 2, "All operators handlers are defined here");
// Set all predefined operators into operators array
for (auto &[name, fptr] : Operators) { operators[name] = fptr; }
}
}

View File

@ -404,7 +404,8 @@ struct Token
Location location;
};
static constexpr auto Keywords_Count = 6;
static constexpr usize Keywords_Count = 6;
static constexpr usize Operators_Count = 14;
std::string_view type_name(Token::Type type);

View File

@ -498,6 +498,12 @@ constexpr bool one_of(std::string_view id, auto const& ...args)
static usize precedense(std::string_view op)
{
// Operators that are not included below are
// '.' since it have own precedense rules and is not binary expression but its own kind of expression
//
// Exclusion of them is marked by subtracting total number of excluded operators.
static_assert(Operators_Count - 1 == 13, "Ensure that all operators have defined precedense below");
if (one_of(op, "or")) return 100;
if (one_of(op, "and")) return 150;
if (one_of(op, "<", ">", "<=", ">=", "==", "!=")) return 200;