Last active
February 21, 2023 06:28
-
-
Save FormerlyZeroCool/e858cbaad131d4718ba45ec4e8a1c3cf to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#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