2026-10-02 20:31:24 +03:00
|
|
|
#include <iostream>
|
|
|
|
|
#include <cctype>
|
|
|
|
|
#include <cstring>
|
|
|
|
|
#include <memory>
|
|
|
|
|
#include <vector>
|
|
|
|
|
#include "expression.hpp"
|
|
|
|
|
#include "print.h"
|
|
|
|
|
|
|
|
|
|
namespace Parser {
|
|
|
|
|
|
|
|
|
|
class Parser
|
|
|
|
|
{
|
|
|
|
|
enum State {
|
|
|
|
|
I, // idle
|
|
|
|
|
RC, // read constant
|
|
|
|
|
RV, // read variable
|
|
|
|
|
OP, // read operator
|
|
|
|
|
} s;
|
|
|
|
|
|
|
|
|
|
const char *state_str(enum State e)
|
|
|
|
|
{
|
|
|
|
|
switch (e)
|
|
|
|
|
{
|
|
|
|
|
case I:
|
|
|
|
|
return "I";
|
|
|
|
|
case RC:
|
|
|
|
|
return "RC";
|
|
|
|
|
case RV:
|
|
|
|
|
return "RV";
|
|
|
|
|
case OP:
|
|
|
|
|
return "OP";
|
|
|
|
|
default:
|
|
|
|
|
return "Not a valid state!";
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2026-10-02 22:14:03 +03:00
|
|
|
int switch_state(enum State e)
|
2026-10-02 20:31:24 +03:00
|
|
|
{
|
|
|
|
|
pr_debug("Leaving " << state_str(s) << ", entering " << state_str(e));
|
2026-10-02 22:14:03 +03:00
|
|
|
|
|
|
|
|
int cumulative_backtrack = 0;
|
|
|
|
|
|
2026-10-02 20:31:24 +03:00
|
|
|
switch (s)
|
|
|
|
|
{
|
|
|
|
|
case I:
|
2026-10-02 22:14:03 +03:00
|
|
|
cumulative_backtrack += leave_i();
|
2026-10-02 20:31:24 +03:00
|
|
|
break;
|
|
|
|
|
case RC:
|
2026-10-02 22:14:03 +03:00
|
|
|
cumulative_backtrack += leave_rc();
|
2026-10-02 20:31:24 +03:00
|
|
|
break;
|
|
|
|
|
case RV:
|
2026-10-02 22:14:03 +03:00
|
|
|
cumulative_backtrack += leave_rv();
|
2026-10-02 20:31:24 +03:00
|
|
|
break;
|
|
|
|
|
case OP:
|
2026-10-02 22:14:03 +03:00
|
|
|
cumulative_backtrack += leave_op();
|
2026-10-02 20:31:24 +03:00
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
pr_warn("Warning: broken state machine on leave");
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
switch (e)
|
|
|
|
|
{
|
|
|
|
|
case I:
|
|
|
|
|
enter_i();
|
|
|
|
|
break;
|
|
|
|
|
case RC:
|
|
|
|
|
enter_rc();
|
|
|
|
|
break;
|
|
|
|
|
case RV:
|
|
|
|
|
enter_rv();
|
|
|
|
|
break;
|
|
|
|
|
case OP:
|
|
|
|
|
enter_op();
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
pr_warn("Warning: broken state machine on enter");
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
s = e;
|
2026-10-02 22:14:03 +03:00
|
|
|
|
|
|
|
|
pr_debug("Cumulative backtrack is " << cumulative_backtrack);
|
|
|
|
|
|
|
|
|
|
return cumulative_backtrack;
|
2026-10-02 20:31:24 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public:
|
|
|
|
|
char buffer[256];
|
|
|
|
|
std::shared_ptr<std::vector<Expression::Token>>& pv;
|
|
|
|
|
|
|
|
|
|
Parser(std::shared_ptr<std::vector<Expression::Token>>& pv)
|
|
|
|
|
: s(I), pv(pv)
|
|
|
|
|
{
|
|
|
|
|
buf_clear();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void buf_clear(void)
|
|
|
|
|
{
|
2026-10-04 14:13:14 +03:00
|
|
|
memset(buffer, 0, MAX_TOKEN_STRING_LEN);
|
2026-10-02 20:31:24 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void buf_push(char c)
|
|
|
|
|
{
|
2026-10-04 14:13:14 +03:00
|
|
|
if (int i = strlen(buffer); i < MAX_TOKEN_STRING_LEN - 1) {
|
|
|
|
|
buffer[i] = c;
|
|
|
|
|
}
|
2026-10-02 20:31:24 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int dispatch_to_current(char c)
|
|
|
|
|
{
|
|
|
|
|
switch (s)
|
|
|
|
|
{
|
|
|
|
|
case I:
|
|
|
|
|
return parse_i(c);
|
|
|
|
|
case RC:
|
|
|
|
|
return parse_rc(c);
|
|
|
|
|
case RV:
|
|
|
|
|
return parse_rv(c);
|
|
|
|
|
case OP:
|
|
|
|
|
return parse_op(c);
|
|
|
|
|
default:
|
|
|
|
|
pr_warn("State machine is broken, unable to dispatch character " << c << ", returning 0");
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void enter_i(void) {}
|
|
|
|
|
int parse_i(char c)
|
|
|
|
|
{
|
|
|
|
|
if (isdigit(c) || c == '.')
|
|
|
|
|
{
|
2026-10-02 22:14:03 +03:00
|
|
|
return switch_state(RC) + 1;
|
2026-10-02 20:31:24 +03:00
|
|
|
}
|
|
|
|
|
else if (isalpha(c))
|
|
|
|
|
{
|
2026-10-02 22:14:03 +03:00
|
|
|
return switch_state(RV) + 1;
|
2026-10-02 20:31:24 +03:00
|
|
|
}
|
|
|
|
|
else if (c == '+' || c == '-' || c == '*' || c == '/' || c == '(' || c == ')')
|
|
|
|
|
{
|
2026-10-02 22:14:03 +03:00
|
|
|
return switch_state(OP) + 1;
|
2026-10-02 20:31:24 +03:00
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
2026-10-04 14:13:14 +03:00
|
|
|
// as per requirements (report all wrong symbols)
|
|
|
|
|
if (c != ' ' && c != '\0')
|
|
|
|
|
{
|
|
|
|
|
pr_warn("Unrecognized character: " << c);
|
|
|
|
|
}
|
|
|
|
|
|
2026-10-02 20:31:24 +03:00
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
}
|
2026-10-02 22:14:03 +03:00
|
|
|
int leave_i(void)
|
|
|
|
|
{
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
2026-10-02 20:31:24 +03:00
|
|
|
|
|
|
|
|
void enter_rc(void)
|
|
|
|
|
{
|
|
|
|
|
buf_clear();
|
|
|
|
|
}
|
|
|
|
|
int parse_rc(char c)
|
|
|
|
|
{
|
|
|
|
|
if (!(isdigit(c) || c == '.'))
|
|
|
|
|
{
|
2026-10-02 22:14:03 +03:00
|
|
|
return switch_state(I) + 1;
|
2026-10-02 20:31:24 +03:00
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
buf_push(c);
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
}
|
2026-10-02 22:14:03 +03:00
|
|
|
int leave_rc(void)
|
2026-10-02 20:31:24 +03:00
|
|
|
{
|
2026-10-04 14:13:14 +03:00
|
|
|
// additional check as per requirements
|
|
|
|
|
{
|
|
|
|
|
int i, c;
|
|
|
|
|
#if (MAX_TOKEN_STRING_LEN % 8) == 0
|
|
|
|
|
for (i = 0, c = 0; i < MAX_TOKEN_STRING_LEN; i += 8)
|
|
|
|
|
{
|
|
|
|
|
c += (buffer[i+0] == '.')
|
|
|
|
|
+ (buffer[i+1] == '.')
|
|
|
|
|
+ (buffer[i+2] == '.')
|
|
|
|
|
+ (buffer[i+3] == '.')
|
|
|
|
|
+ (buffer[i+4] == '.')
|
|
|
|
|
+ (buffer[i+5] == '.')
|
|
|
|
|
+ (buffer[i+6] == '.')
|
|
|
|
|
+ (buffer[i+7] == '.');
|
|
|
|
|
}
|
|
|
|
|
#else
|
|
|
|
|
for (int i = 0, c = 0; i < MAX_TOKEN_STRING_LEN; i++)
|
|
|
|
|
{
|
|
|
|
|
c += buffer[i] == '.';
|
|
|
|
|
}
|
|
|
|
|
#endif
|
|
|
|
|
if (c > 1)
|
|
|
|
|
{
|
|
|
|
|
pr_warn("Too many dots in CONST: " << buffer);
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2026-10-02 20:31:24 +03:00
|
|
|
double value;
|
|
|
|
|
|
|
|
|
|
if (!sscanf(buffer, "%lf", &value))
|
|
|
|
|
{
|
|
|
|
|
pr_warn("Invalid CONST: " << buffer);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
pv->push_back(Expression::Token(value));
|
|
|
|
|
}
|
2026-10-02 22:14:03 +03:00
|
|
|
|
|
|
|
|
return 0;
|
2026-10-02 20:31:24 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void enter_rv(void)
|
|
|
|
|
{
|
|
|
|
|
buf_clear();
|
|
|
|
|
}
|
|
|
|
|
int parse_rv(char c)
|
|
|
|
|
{
|
|
|
|
|
if (!isalpha(c))
|
|
|
|
|
{
|
2026-10-02 22:14:03 +03:00
|
|
|
return switch_state(I) + 1;
|
2026-10-02 20:31:24 +03:00
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
buf_push(c);
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
}
|
2026-10-02 22:14:03 +03:00
|
|
|
int leave_rv(void)
|
2026-10-02 20:31:24 +03:00
|
|
|
{
|
|
|
|
|
pv->push_back(Expression::Token(buffer));
|
2026-10-02 22:14:03 +03:00
|
|
|
return 0;
|
2026-10-02 20:31:24 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void enter_op(void)
|
|
|
|
|
{
|
|
|
|
|
buf_clear();
|
|
|
|
|
}
|
|
|
|
|
int parse_op(char c)
|
|
|
|
|
{
|
|
|
|
|
if (!(c == '+' || c == '-' || c == '*' || c == '/' || c == '(' || c == ')'))
|
|
|
|
|
{
|
2026-10-02 22:14:03 +03:00
|
|
|
return switch_state(I) + 1;
|
2026-10-02 20:31:24 +03:00
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
buf_push(c);
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
}
|
2026-10-02 22:14:03 +03:00
|
|
|
int leave_op(void)
|
2026-10-02 20:31:24 +03:00
|
|
|
{
|
2026-10-02 22:14:03 +03:00
|
|
|
int i, downsize;
|
|
|
|
|
for (i = strlen(buffer), downsize = 0; i > 0; buffer[--i] = '\0', downsize++)
|
2026-10-02 20:31:24 +03:00
|
|
|
{
|
2026-10-02 22:14:03 +03:00
|
|
|
if (!strcmp(buffer, "("))
|
|
|
|
|
{
|
|
|
|
|
pv->push_back(Expression::Token(Expression::Token::OPA));
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
else if (!strcmp(buffer, ")"))
|
|
|
|
|
{
|
|
|
|
|
pv->push_back(Expression::Token(Expression::Token::CPA));
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
else if (!strcmp(buffer, "+"))
|
|
|
|
|
{
|
|
|
|
|
pv->push_back(Expression::Token(Expression::Token::ADD));
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
else if (!strcmp(buffer, "-"))
|
|
|
|
|
{
|
|
|
|
|
pv->push_back(Expression::Token(Expression::Token::SUB));
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
else if (!strcmp(buffer, "*"))
|
|
|
|
|
{
|
|
|
|
|
pv->push_back(Expression::Token(Expression::Token::MUL));
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
else if (!strcmp(buffer, "/"))
|
|
|
|
|
{
|
|
|
|
|
pv->push_back(Expression::Token(Expression::Token::DIV));
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
else if (!strcmp(buffer, "**"))
|
|
|
|
|
{
|
|
|
|
|
pv->push_back(Expression::Token(Expression::Token::POW));
|
|
|
|
|
break;
|
|
|
|
|
}
|
2026-10-02 20:31:24 +03:00
|
|
|
}
|
2026-10-02 22:14:03 +03:00
|
|
|
|
|
|
|
|
if (i)
|
2026-10-02 20:31:24 +03:00
|
|
|
{
|
2026-10-02 22:14:03 +03:00
|
|
|
return downsize;
|
2026-10-02 20:31:24 +03:00
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
pr_warn("Skip unknown operator: " << buffer);
|
2026-10-02 22:14:03 +03:00
|
|
|
return 0;
|
2026-10-02 20:31:24 +03:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
2026-10-02 22:14:03 +03:00
|
|
|
std::shared_ptr<std::vector<Expression::Token>> tokenize_input(const char * const input_string)
|
2026-10-02 20:31:24 +03:00
|
|
|
{
|
|
|
|
|
std::shared_ptr<std::vector<Expression::Token>> pv = std::make_shared<std::vector<Expression::Token>>();
|
|
|
|
|
|
|
|
|
|
Parser p(pv);
|
2026-10-02 22:14:03 +03:00
|
|
|
for (int i = 0, l = strlen(input_string) + 1; i < l; i++)
|
2026-10-02 20:31:24 +03:00
|
|
|
{
|
|
|
|
|
i -= p.dispatch_to_current(input_string[i]);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return pv;
|
|
|
|
|
}
|
|
|
|
|
|
2026-10-02 22:14:03 +03:00
|
|
|
int assert_same(const std::vector<Expression::Token>& a, const std::vector<Expression::Token>& b)
|
2026-10-02 20:31:24 +03:00
|
|
|
{
|
|
|
|
|
if (a.size() != b.size())
|
|
|
|
|
{
|
|
|
|
|
std::cout << "Vectors are of different lengths: " << a.size() << " != " << b.size() << "\n";
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for (int i = 0, l = a.size(); i < l; i++)
|
|
|
|
|
{
|
|
|
|
|
if (a[i].t != b[i].t)
|
|
|
|
|
{
|
|
|
|
|
std::cout << "Token " << i << " types differ: " << a[i].type_str() << " != " << b[i].type_str() << "\n";
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
switch (a[i].t)
|
|
|
|
|
{
|
|
|
|
|
case Expression::Token::VAR:
|
|
|
|
|
if (strcmp((*(a[i].v.s)).c_str(),
|
|
|
|
|
(*(b[i].v.s)).c_str()))
|
|
|
|
|
{
|
|
|
|
|
std::cout << "Variable names at " << i << " differ: " << (*(a[i].v.s)).c_str() << " != " << (*(b[i].v.s)).c_str() << "\n";
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
case Expression::Token::CONST:
|
|
|
|
|
if (a[i].v.d != b[i].v.d)
|
|
|
|
|
{
|
|
|
|
|
std::cout << "Constants at " << i << " differ: " << a[i].v.d << " != " << b[i].v.d << "\n";
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
case Expression::Token::OPERATOR:
|
|
|
|
|
if (a[i].v.o != b[i].v.o)
|
|
|
|
|
{
|
|
|
|
|
std::cout << "Operators at " << i << " differ: " << a[i].op_str() << " != " << b[i].op_str() << "\n";
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2026-10-02 22:14:03 +03:00
|
|
|
void print_token_vector(const std::vector<Expression::Token>& tv)
|
|
|
|
|
{
|
|
|
|
|
int i, l;
|
|
|
|
|
for (i = 0, l = tv.size() - 1; i < l; i++)
|
|
|
|
|
{
|
|
|
|
|
std::cout << "(" << *tv[i].to_str() << ") ";
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
std::cout << "(" << *tv[i].to_str() << ")\n";
|
|
|
|
|
}
|
|
|
|
|
|
2026-10-04 14:13:14 +03:00
|
|
|
void print_token_vector(const std::vector<Expression::Token>& tv, const std::vector<unsigned char>& colorcoding)
|
|
|
|
|
{
|
|
|
|
|
int i, l;
|
|
|
|
|
for (i = 0, l = tv.size() - 1; i < l; i++)
|
|
|
|
|
{
|
|
|
|
|
std::cout << "(" << *tv[i].to_str() << ") ";
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
std::cout << "(" << *tv[i].to_str() << ")\n";
|
|
|
|
|
}
|
|
|
|
|
|
2026-10-02 20:31:24 +03:00
|
|
|
}
|