aboutsummaryrefslogtreecommitdiffstatshomepage
diff options
context:
space:
mode:
authorEgor Tensin <Egor.Tensin@gmail.com>2019-12-31 20:51:34 +0300
committerEgor Tensin <Egor.Tensin@gmail.com>2019-12-31 20:51:34 +0300
commit6d43c1316cf2e7f438c6ca73a43b63d063d7029f (patch)
treedf08df2bc681acc0e6c2906c3c675088fddb5789
parentparser: refactoring (diff)
downloadmath-server-6d43c1316cf2e7f438c6ca73a43b63d063d7029f.tar.gz
math-server-6d43c1316cf2e7f438c6ca73a43b63d063d7029f.zip
parser: support unary +
-rw-r--r--server/parser/parser.hpp24
-rw-r--r--test/unit_tests/parser.cpp23
2 files changed, 36 insertions, 11 deletions
diff --git a/server/parser/parser.hpp b/server/parser/parser.hpp
index 7d6f31d..fb2a5ff 100644
--- a/server/parser/parser.hpp
+++ b/server/parser/parser.hpp
@@ -36,7 +36,7 @@ public:
private:
double exec_expr() {
- return exec_expr(exec_primary(), parser::BinaryOp::min_precedence());
+ return exec_expr(exec_factor(), parser::BinaryOp::min_precedence());
}
double exec_expr(double lhs, unsigned min_prec) {
@@ -45,7 +45,7 @@ private:
const auto prev_prec = prev.get_precedence();
m_lexer.drop_token();
- auto rhs = exec_primary();
+ auto rhs = exec_factor();
for (op = peek_operator(); op.has_value(); op = peek_operator()) {
const auto next = *op;
@@ -77,16 +77,28 @@ private:
return parser::BinaryOp::from_token(*token);
}
- double exec_primary() {
+ double exec_factor() {
if (!m_lexer.has_token()) {
- throw ParserError{"expected '-', '(' or a number"};
+ throw ParserError{"expected '-', '+', '(' or a number"};
}
using Type = lexer::Token::Type;
if (m_lexer.drop_token_of_type(Type::MINUS).has_value()) {
- return -exec_primary();
+ return -exec_factor();
}
+ if (m_lexer.drop_token_of_type(Type::PLUS).has_value()) {
+ return exec_factor();
+ }
+ return exec_atom();
+ }
+
+ double exec_atom() {
+ if (!m_lexer.has_token()) {
+ throw ParserError{"expected '-', '+', '(' or a number"};
+ }
+
+ using Type = lexer::Token::Type;
if (m_lexer.drop_token_of_type(Type::LEFT_PAREN).has_value()) {
const auto inner = exec_expr();
@@ -100,7 +112,7 @@ private:
return token.value().as_number();
}
- throw ParserError{"expected '-', '(' or a number"};
+ throw ParserError{"expected '-', '+', '(' or a number"};
}
Lexer m_lexer;
diff --git a/test/unit_tests/parser.cpp b/test/unit_tests/parser.cpp
index 37d9e0a..594e162 100644
--- a/test/unit_tests/parser.cpp
+++ b/test/unit_tests/parser.cpp
@@ -24,20 +24,31 @@ namespace {
namespace exec::valid {
const std::vector<std::string_view> input{
+ // Constants and unary operators:
"1",
+ "-1",
+ "--1",
+ "+--+-2",
+ // Basic binary operators:
" 1 + 2 ",
" 2 * 1 + 3 ",
" 2 * (1 + 3) ",
" 2 * (1 + 3 * (1 - -3)) ",
- " -2 * ---- (3 + -100e-1) ", // Looks weird, but also works in e.g. Python
+ // Looks weird, but also works in e.g. Python:
+ " -2 * -+--- (3 + -100e-1) ",
+ // Power operator is right-associative:
"2 ^ 3 ^ 3",
- "(2 ^ 3) ^ 3", // Power operator is right-associative
+ "(2 ^ 3) ^ 3",
+ // Power operator has higher precedence than the unary minus:
"(.5 ^ -1) ^ 4",
- ".5 ^ -1 ^ 4", // Power operator has higher precedence than the unary minus
+ ".5 ^ -1 ^ 4",
};
const std::vector<double> expected{
1,
+ -1,
+ 1,
+ -2,
3,
5,
8,
@@ -55,14 +66,16 @@ namespace exec::invalid {
const std::vector<std::string_view> input{
"",
+ // Missing operand:
" 1 + ",
+ // Unmatched parentheses:
" 2 * (1 + 3 ",
" 2 * (1 + 3) )",
};
const std::vector<std::string> error_msg{
- "server error: parser error: expected '-', '(' or a number",
- "server error: parser error: expected '-', '(' or a number",
+ "server error: parser error: expected '-', '+', '(' or a number",
+ "server error: parser error: expected '-', '+', '(' or a number",
"server error: parser error: missing closing ')'",
"server error: parser error: expected a binary operator",
};