Skip to content

Instantly share code, notes, and snippets.

@FormerlyZeroCool
Last active February 21, 2023 06:28
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save FormerlyZeroCool/e858cbaad131d4718ba45ec4e8a1c3cf to your computer and use it in GitHub Desktop.
Save FormerlyZeroCool/e858cbaad131d4718ba45ec4e8a1c3cf to your computer and use it in GitHub Desktop.
#include <array>
#include <iostream>
#include <string_view>
#include <exception>
bool isOperator(char c) noexcept
{
return (c == '*' | c == '/' | c == '+' | c == '-');
}
bool is_digit(unsigned char c) noexcept
{
return (unsigned char)(c - 48) < 10;
}
double operate(char op, const double first, const double second)
{
switch(op)
{
case('*'):
return first * second;
case('/'):
return first / second;
case('-'):
return first - second;
default:
return first + second;
}
}
class Lexer {
const char* const data;
size_t offset = 0;
void seek()
{
while(this->current() == ' ')
{
this->offset++;
}
}
public:
Lexer(const char* data): data(data), offset(0)
{}
size_t get_offset() const noexcept
{
return offset;
}
struct Token {
class InvalidTokenException : public std::exception {
std::string message;
public:
InvalidTokenException(std::string what): message(what) {}
std::string& what()
{
return this->message;
}
};
static const int NUMBER = 0, OPERATOR = 1, END = 2, INVALID_TOKEN = -1;
const std::string_view tok;
const int type = 0;
double number;
Token(int type, std::string_view tok):tok(tok), type(type), number(-1)
{
if(type == Token::NUMBER)
{
char* ptr = const_cast<char*>(this->tok.data() + this->tok.length());
this->number = std::strtod(this->tok.data(), &ptr);
}
}
};
int last_token_type = Lexer::Token::OPERATOR;
Token next() noexcept
{
seek();
if(this->current() == '\0')
{
return Token(Token::END, std::string_view(&this->current(), 1));
}
else if(is_digit(this->data[this->offset]) ||
(
this->current() == '-' && is_digit(this->data[offset + 1])
)
)
{
int start = this->offset;
int len = 0;
//do while to skip - for negatives
do {
this->offset++;
len++;
} while(is_digit(this->current()));
this->last_token_type = Lexer::Token::NUMBER;
Token tok(this->last_token_type, std::string_view(this->data + start, len));
return tok;
}
else if(isOperator(this->current()))
{
this->last_token_type = Lexer::Token::OPERATOR;
Token tok(this->last_token_type, std::string_view(&this->current(), 1));
do {
this->offset++;
} while(isOperator(this->current()));
return tok;
}
return Token(Token::INVALID_TOKEN, std::string_view(&this->current(), 1));
}
char& current()
{
return const_cast<char*>(this->data)[this->offset];
}
};
class InvalidSyntaxException : public std::exception {
std::string message;
public:
InvalidSyntaxException(std::string what): message(what) {}
std::string& what()
{
return this->message;
}
};
class StackOverFlow : public std::exception {
std::string message;
public:
StackOverFlow(std::string what): message(what) {message += " Stack Overflow."; }
std::string& what()
{
return this->message;
}
};
template <typename T, size_t SIZE>
class Stack {
std::array<T, SIZE> data;
size_t len;
public:
Stack():len(0) {}
void clear() noexcept { len = 0; }
bool push(const T &data) noexcept
{
if(len < SIZE)
{
this->data[len++] = data;
return true;
}
return false;
}
bool push(const T &&data) noexcept
{
return push(data);
}
T& pop() noexcept
{
this->len -= this->len > 0;
return this->data[len];
}
const T& top() const noexcept
{
return this->data[len - 1];
}
T& top_unconst() noexcept
{
return this->data[len - 1];
}
size_t size() const noexcept
{
return this->len;
}
};
template <typename STACK>
double evaluate(Lexer& lex, STACK &dataStack)
{ int type = 0;
do
{
const Lexer::Token tok = lex.next();
type = tok.type;
if(type == Lexer::Token::NUMBER)
{
if(!dataStack.push(tok.number))
throw StackOverFlow("Stack size exceeded " + std::to_string(dataStack.size()));
}
else if(type == Lexer::Token::OPERATOR)
{
if(dataStack.size() < 2)
throw InvalidSyntaxException("Invalid syntax missing operands to operate on");
const double& first = dataStack.pop();
dataStack.top_unconst() = operate(tok.tok[0], dataStack.top(), first);
}
else if(type == Lexer::Token::INVALID_TOKEN)
throw Lexer::Token::InvalidTokenException(std::string(tok.tok) + " is an Invalid Token, at index: " + std::to_string(lex.get_offset()));
} while(type != Lexer::Token::END && type != Lexer::Token::INVALID_TOKEN);
if(dataStack.size() > 1)
throw InvalidSyntaxException("Invalid syntax extra operands");
return dataStack.top();
}
int main(int argc, char **argv)
{
if(argc > 1)
{
try {
Stack<double, 128> dataStack;
Lexer lex(argv[1]);
std::cout<<"the answer to "<<argv[1]<<" is: "<< evaluate(lex, dataStack) << std::endl;
dataStack.clear();
}catch(Lexer::Token::InvalidTokenException &e)
{
std::cerr<<e.what()<<"\n";
}catch(InvalidSyntaxException &e)
{
std::cerr<<e.what()<<"\n";
}catch(StackOverFlow &e)
{
std::cerr<<e.what()<<"\n";
}
}
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment