initial commit
This commit is contained in:
@@ -0,0 +1,37 @@
|
||||
cmake_minimum_required(VERSION 3.10)
|
||||
|
||||
SET(CMAKE_CXX_FLAGS_DEBUG "-g")
|
||||
|
||||
project(pzks)
|
||||
|
||||
set (GLOBAL_INC PRIVATE
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/inc
|
||||
${CMAKE_CURRENT_SOURCE_DIR}
|
||||
)
|
||||
|
||||
|
||||
add_library(parser STATIC)
|
||||
|
||||
target_sources(parser PRIVATE
|
||||
src/parser.cpp
|
||||
)
|
||||
|
||||
target_include_directories(parser PRIVATE
|
||||
${GLOBAL_INC}
|
||||
)
|
||||
|
||||
|
||||
add_executable(tester)
|
||||
|
||||
target_sources(tester PRIVATE
|
||||
src/test.cpp
|
||||
)
|
||||
|
||||
target_include_directories(tester PRIVATE
|
||||
${GLOBAL_INC}
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src
|
||||
)
|
||||
|
||||
target_link_libraries(tester PRIVATE
|
||||
parser
|
||||
)
|
||||
@@ -0,0 +1,157 @@
|
||||
#pragma once
|
||||
|
||||
#include <iostream>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
namespace Expression {
|
||||
|
||||
#if 0
|
||||
namespace TokenType {
|
||||
|
||||
enum TokenType {
|
||||
VAR,
|
||||
CONST,
|
||||
OPERATOR
|
||||
};
|
||||
|
||||
}
|
||||
#endif
|
||||
|
||||
class Token {
|
||||
public:
|
||||
enum TokenType {
|
||||
VAR,
|
||||
CONST,
|
||||
OPERATOR
|
||||
} t;
|
||||
|
||||
enum Operator {
|
||||
OPA, // opening parentheses
|
||||
CPA, // closing parentheses
|
||||
ADD,
|
||||
SUB,
|
||||
MUL,
|
||||
DIV,
|
||||
POW
|
||||
};
|
||||
|
||||
union TokenValue {
|
||||
std::shared_ptr<std::string> s;
|
||||
double d;
|
||||
Operator o;
|
||||
|
||||
explicit TokenValue(std::string& s)
|
||||
: s(std::make_shared<std::string>(s))
|
||||
{
|
||||
}
|
||||
|
||||
explicit TokenValue(double d)
|
||||
: d(d)
|
||||
{
|
||||
}
|
||||
|
||||
explicit TokenValue(enum Operator o)
|
||||
: o(o)
|
||||
{
|
||||
}
|
||||
|
||||
explicit TokenValue(void)
|
||||
{
|
||||
}
|
||||
|
||||
~TokenValue(void)
|
||||
{
|
||||
}
|
||||
} v;
|
||||
|
||||
Token(std::string s)
|
||||
: v(s)
|
||||
{
|
||||
t = VAR;
|
||||
}
|
||||
|
||||
Token(double d)
|
||||
: v(d)
|
||||
{
|
||||
t = CONST;
|
||||
}
|
||||
|
||||
Token(enum Operator o)
|
||||
: v(o)
|
||||
{
|
||||
t = OPERATOR;
|
||||
}
|
||||
|
||||
Token(Expression::Token&& other)
|
||||
{
|
||||
t = other.t;
|
||||
|
||||
switch (t)
|
||||
{
|
||||
case VAR:
|
||||
v.s = other.v.s;
|
||||
break;
|
||||
case CONST:
|
||||
v.d = other.v.d;
|
||||
break;
|
||||
case OPERATOR:
|
||||
v.o = other.v.o;
|
||||
break;
|
||||
default:
|
||||
std::cout << "Warning: token type is invalid\n";
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
const char *type_str(void)
|
||||
{
|
||||
switch (t)
|
||||
{
|
||||
case VAR:
|
||||
return "VAR";
|
||||
case CONST:
|
||||
return "CONST";
|
||||
case OPERATOR:
|
||||
return "OPERATOR";
|
||||
default:
|
||||
return "Not a valid type";
|
||||
}
|
||||
}
|
||||
|
||||
const char *op_str(void)
|
||||
{
|
||||
if (t != OPERATOR)
|
||||
{
|
||||
return "Token is not an operator!";
|
||||
}
|
||||
|
||||
switch (v.o)
|
||||
{
|
||||
case OPA:
|
||||
return "OPA";
|
||||
case CPA:
|
||||
return "CPA";
|
||||
case ADD:
|
||||
return "ADD";
|
||||
case SUB:
|
||||
return "SUB";
|
||||
case MUL:
|
||||
return "MUL";
|
||||
case DIV:
|
||||
return "DIV";
|
||||
case POW:
|
||||
return "POW";
|
||||
default:
|
||||
return "Invalid operator";
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
class TreeNode {
|
||||
std::shared_ptr<Token> l; // left hand
|
||||
std::shared_ptr<Token> o; // operation
|
||||
std::shared_ptr<Token> r; // right hand
|
||||
};
|
||||
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
#pragma once
|
||||
|
||||
#include <vector>
|
||||
#include "expression.hpp"
|
||||
|
||||
namespace Parser {
|
||||
|
||||
std::shared_ptr<std::vector<Expression::Token>> tokenize_input(char *input_string);
|
||||
int assert_same(std::vector<Expression::Token>& a, std::vector<Expression::Token>& b);
|
||||
|
||||
}
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
#pragma once
|
||||
|
||||
#include <iostream>
|
||||
#include "config.h"
|
||||
|
||||
#if DEBUG == 1
|
||||
#define pr_debug(...) std::cout << "[" << __func__ << ":" << __LINE__ << "] debug: " << __VA_ARGS__ << "\n"
|
||||
#else
|
||||
#define pr_debug(...) do {} while (0)
|
||||
#endif
|
||||
|
||||
#define pr_warn(...) std::cout << "[" << __func__ << ":" << __LINE__ << "] warning: " << __VA_ARGS__ << "\n"
|
||||
@@ -0,0 +1,19 @@
|
||||
#pragma once
|
||||
|
||||
#include <iostream>
|
||||
#include <vector>
|
||||
|
||||
typedef int (*TestFunc)(void);
|
||||
|
||||
extern std::vector<TestFunc> test_funcs;
|
||||
|
||||
#define CHECK(a) do { \
|
||||
if (a) { \
|
||||
std::cout << "Failed check in " << __func__ << ":" << __LINE__ << "\n" \
|
||||
<< #a << " does not hold\n"; \
|
||||
return 1; \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#define TEST(name) static int name(void)
|
||||
#define REGISTER(name) test_funcs.push_back(name)
|
||||
+312
@@ -0,0 +1,312 @@
|
||||
#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!";
|
||||
}
|
||||
}
|
||||
|
||||
void switch_state(enum State e)
|
||||
{
|
||||
pr_debug("Leaving " << state_str(s) << ", entering " << state_str(e));
|
||||
switch (s)
|
||||
{
|
||||
case I:
|
||||
leave_i();
|
||||
break;
|
||||
case RC:
|
||||
leave_rc();
|
||||
break;
|
||||
case RV:
|
||||
leave_rv();
|
||||
break;
|
||||
case OP:
|
||||
leave_op();
|
||||
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;
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
memset(buffer, 0, 256);
|
||||
}
|
||||
|
||||
void buf_push(char c)
|
||||
{
|
||||
buffer[strlen(buffer)] = c;
|
||||
}
|
||||
|
||||
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 == '.')
|
||||
{
|
||||
switch_state(RC);
|
||||
return 1;
|
||||
}
|
||||
else if (isalpha(c))
|
||||
{
|
||||
switch_state(RV);
|
||||
return 1;
|
||||
}
|
||||
else if (c == '+' || c == '-' || c == '*' || c == '/' || c == '(' || c == ')')
|
||||
{
|
||||
switch_state(OP);
|
||||
return 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
pr_warn("Unknown character skipped (" << c << "), still in idle");
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
void leave_i(void) {}
|
||||
|
||||
void enter_rc(void)
|
||||
{
|
||||
buf_clear();
|
||||
}
|
||||
int parse_rc(char c)
|
||||
{
|
||||
if (!(isdigit(c) || c == '.'))
|
||||
{
|
||||
switch_state(I);
|
||||
return 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
buf_push(c);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
void leave_rc(void)
|
||||
{
|
||||
double value;
|
||||
|
||||
if (!sscanf(buffer, "%lf", &value))
|
||||
{
|
||||
pr_warn("Invalid CONST: " << buffer);
|
||||
}
|
||||
else
|
||||
{
|
||||
pv->push_back(Expression::Token(value));
|
||||
}
|
||||
}
|
||||
|
||||
void enter_rv(void)
|
||||
{
|
||||
buf_clear();
|
||||
}
|
||||
int parse_rv(char c)
|
||||
{
|
||||
if (!isalpha(c))
|
||||
{
|
||||
switch_state(I);
|
||||
return 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
buf_push(c);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
void leave_rv(void)
|
||||
{
|
||||
pv->push_back(Expression::Token(buffer));
|
||||
}
|
||||
|
||||
void enter_op(void)
|
||||
{
|
||||
buf_clear();
|
||||
}
|
||||
int parse_op(char c)
|
||||
{
|
||||
if (!(c == '+' || c == '-' || c == '*' || c == '/' || c == '(' || c == ')'))
|
||||
{
|
||||
switch_state(I);
|
||||
return 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
buf_push(c);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
void leave_op(void)
|
||||
{
|
||||
if (!strcmp(buffer, "("))
|
||||
{
|
||||
pv->push_back(Expression::Token(Expression::Token::OPA));
|
||||
}
|
||||
else if (!strcmp(buffer, ")"))
|
||||
{
|
||||
pv->push_back(Expression::Token(Expression::Token::CPA));
|
||||
}
|
||||
else if (!strcmp(buffer, "+"))
|
||||
{
|
||||
pv->push_back(Expression::Token(Expression::Token::ADD));
|
||||
}
|
||||
else if (!strcmp(buffer, "-"))
|
||||
{
|
||||
pv->push_back(Expression::Token(Expression::Token::SUB));
|
||||
}
|
||||
else if (!strcmp(buffer, "*"))
|
||||
{
|
||||
pv->push_back(Expression::Token(Expression::Token::MUL));
|
||||
}
|
||||
else if (!strcmp(buffer, "/"))
|
||||
{
|
||||
pv->push_back(Expression::Token(Expression::Token::DIV));
|
||||
}
|
||||
else if (!strcmp(buffer, "**"))
|
||||
{
|
||||
pv->push_back(Expression::Token(Expression::Token::POW));
|
||||
}
|
||||
else
|
||||
{
|
||||
pr_warn("Skip unknown operator: " << buffer);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
std::shared_ptr<std::vector<Expression::Token>> tokenize_input(char *input_string)
|
||||
{
|
||||
std::shared_ptr<std::vector<Expression::Token>> pv = std::make_shared<std::vector<Expression::Token>>();
|
||||
|
||||
Parser p(pv);
|
||||
for (int i = 0, l = strlen(input_string); i < l; i++)
|
||||
{
|
||||
i -= p.dispatch_to_current(input_string[i]);
|
||||
}
|
||||
|
||||
(void) p.dispatch_to_current('\0');
|
||||
|
||||
return pv;
|
||||
}
|
||||
|
||||
int assert_same(std::vector<Expression::Token>& a, std::vector<Expression::Token>& b)
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
#include "test-tools.hpp"
|
||||
#include "parser.hpp"
|
||||
#include "expression.hpp"
|
||||
|
||||
TEST(parser_proper_test_1)
|
||||
{
|
||||
auto o = Parser::tokenize_input((char *) "(1+2-c)");
|
||||
std::vector<Expression::Token> r;
|
||||
|
||||
r.push_back(Expression::Token(Expression::Token::OPA));
|
||||
r.push_back(Expression::Token(1));
|
||||
r.push_back(Expression::Token(Expression::Token::ADD));
|
||||
r.push_back(Expression::Token(2));
|
||||
r.push_back(Expression::Token(Expression::Token::SUB));
|
||||
r.push_back(Expression::Token("c"));
|
||||
r.push_back(Expression::Token(Expression::Token::CPA));
|
||||
|
||||
CHECK(Parser::assert_same(*o, r));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
TEST(parser_proper_test_2)
|
||||
{
|
||||
auto o = Parser::tokenize_input((char *) "(1**5,-snake+marble*34*(35 / 63.2))");
|
||||
std::vector<Expression::Token> r;
|
||||
|
||||
r.push_back(Expression::Token(Expression::Token::OPA));
|
||||
r.push_back(Expression::Token(1));
|
||||
r.push_back(Expression::Token(Expression::Token::POW));
|
||||
r.push_back(Expression::Token(5));
|
||||
r.push_back(Expression::Token(Expression::Token::SUB));
|
||||
r.push_back(Expression::Token("snake"));
|
||||
r.push_back(Expression::Token(Expression::Token::ADD));
|
||||
r.push_back(Expression::Token("marble"));
|
||||
r.push_back(Expression::Token(Expression::Token::MUL));
|
||||
r.push_back(Expression::Token(34));
|
||||
r.push_back(Expression::Token(Expression::Token::MUL));
|
||||
r.push_back(Expression::Token(Expression::Token::OPA));
|
||||
r.push_back(Expression::Token(35));
|
||||
r.push_back(Expression::Token(Expression::Token::DIV));
|
||||
r.push_back(Expression::Token(63.2));
|
||||
r.push_back(Expression::Token(Expression::Token::CPA));
|
||||
r.push_back(Expression::Token(Expression::Token::CPA));
|
||||
|
||||
CHECK(Parser::assert_same(*o, r));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void register_tests(void)
|
||||
{
|
||||
REGISTER(parser_proper_test_1);
|
||||
REGISTER(parser_proper_test_2);
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
#include <iostream>
|
||||
#include <vector>
|
||||
|
||||
#include "test-cases.cpp"
|
||||
|
||||
typedef int (*TestFunc)(void);
|
||||
|
||||
void register_tests(void);
|
||||
|
||||
std::vector<TestFunc> test_funcs;
|
||||
|
||||
int main(void)
|
||||
{
|
||||
static unsigned long fails = 0,
|
||||
successes = 0;
|
||||
|
||||
register_tests();
|
||||
|
||||
for (int i = 0, l = test_funcs.size(); i < l; i++) {
|
||||
std::cout << "--- Running test " << i + 1 << "/" << l << "\n";
|
||||
if (test_funcs[i]())
|
||||
{
|
||||
std::cout << "Test failed.\n";
|
||||
fails++;
|
||||
}
|
||||
else
|
||||
{
|
||||
successes++;
|
||||
}
|
||||
}
|
||||
|
||||
std::cout << "--- Finished testing: " << successes << " passed, " << fails << " failed\n";
|
||||
|
||||
return !!fails;
|
||||
}
|
||||
Reference in New Issue
Block a user