From 404826e78a56e15e20d3938aed80945295921745 Mon Sep 17 00:00:00 2001 From: HombreLaser Date: Sat, 22 Jun 2024 16:45:04 -0600 Subject: Add parser --- src/CMakeLists.txt | 11 ++++++++++- src/calculator.cpp | 14 ++++++++++---- src/include/calculator.hpp | 6 ++++++ src/include/operand.hpp | 10 ++++++++++ src/include/parser.hpp | 24 ++++++++++++++++++++++++ src/include/product.hpp | 9 +++++++++ src/include/substraction.hpp | 8 ++++++++ src/include/sum.hpp | 8 ++++++++ src/include/syntax_tree.hpp | 12 ++++++++++++ src/include/tokenizer.hpp | 17 +++++++++-------- src/operand.cpp | 13 +++++++++++++ src/product.cpp | 11 +++++++++++ src/substraction.cpp | 10 ++++++++++ src/sum.cpp | 9 +++++++++ src/syntax_tree.cpp | 13 +++++++++++++ src/tokenizer.cpp | 28 +++++++++++++++++----------- 16 files changed, 179 insertions(+), 24 deletions(-) create mode 100644 src/include/operand.hpp create mode 100644 src/include/parser.hpp create mode 100644 src/include/product.hpp create mode 100644 src/include/substraction.hpp create mode 100644 src/include/sum.hpp create mode 100644 src/include/syntax_tree.hpp create mode 100644 src/operand.cpp create mode 100644 src/product.cpp create mode 100644 src/substraction.cpp create mode 100644 src/sum.cpp create mode 100644 src/syntax_tree.cpp diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 89b60e6..9265ad8 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -11,7 +11,16 @@ target_include_directories(Pico-I2C-LCD INTERFACE ../libs/Pico-I2C-LCD/) target_include_directories(Pico-I2C-LCD PUBLIC ../libs/Pico-I2C-LCD/) # Main executable -add_executable(pico-calc pico-calc.cpp calculator.cpp tokenizer.cpp) +add_executable(pico-calc pico-calc.cpp + calculator.cpp + tokenizer.cpp + operand.cpp + syntax_tree.cpp + sum.cpp + substraction.cpp + product.cpp +) target_link_libraries(pico-calc hardware_i2c Pico-I2C-LCD pico-keypad hardware_timer) target_include_directories(pico-calc PUBLIC include exceptions) +pico_add_extra_outputs(pico-calc) diff --git a/src/calculator.cpp b/src/calculator.cpp index 5de706b..4dd8dcd 100644 --- a/src/calculator.cpp +++ b/src/calculator.cpp @@ -27,14 +27,20 @@ Calculator::~Calculator() { void Calculator::run() { while(true) { display->Clear(); - std::string input; - short str_len = 0; char pressed_key = default_keypad->getKey(); if(pressed_key == '\0') continue; - while(str_len <= 15) { + processInputs(); +} + +void Calculator::processInputs() { + char pressed_key; + short str_len = 0; + std::string input; + + while(str_len <= 15) { pressed_key = default_keypad->getKey(); if(pressed_key != '\0' && pressed_key != '=') { @@ -44,7 +50,7 @@ void Calculator::run() { } if(pressed_key == '=') { - std::array tokens = tokenizer.tokenize(input); + tokens = tokenizer.tokenize(input); break; } diff --git a/src/include/calculator.hpp b/src/include/calculator.hpp index 8c5e97a..2362258 100644 --- a/src/include/calculator.hpp +++ b/src/include/calculator.hpp @@ -8,9 +8,11 @@ class Calculator { private: + // Private attributes. LCD_I2C *display; Keypad *default_keypad; Tokenizer tokenizer = Tokenizer(); + Token **tokens; char default_keypad_chars[4][4] = { {'1', '2', '3', '+'}, {'4', '5', '6', '-'}, @@ -25,6 +27,10 @@ private: }; uint keypad_col_pins[4] = {6, 7, 8, 9}; uint keypad_row_pins[4] = {16, 17, 18, 19}; + // End of private attributes. + // Private methods. + void processInputs(); + // End of private methods. public: Calculator(); ~Calculator(); diff --git a/src/include/operand.hpp b/src/include/operand.hpp new file mode 100644 index 0000000..cc638de --- /dev/null +++ b/src/include/operand.hpp @@ -0,0 +1,10 @@ +#pragma once +#include "syntax_tree.hpp" + + +class Operand: SyntaxTree { + int value; +public: + Operand(int value); + float eval() const; +}; diff --git a/src/include/parser.hpp b/src/include/parser.hpp new file mode 100644 index 0000000..7bc7997 --- /dev/null +++ b/src/include/parser.hpp @@ -0,0 +1,24 @@ +/* Grammar + * Expression --> Term PrimeExpression + * PrimeExpression --> (+|-)Expression | Null + * Term --> Factor PrimeTerm + * PrimeTerm --> (*|/)Term | Null + * Factor --> -Expression | (Expression) | operand */ +#pragma once +#include "tokenizer.hpp" +#include "syntax_tree.hpp" + + +class Parser { +private: + // Private methods. + void parseExpression(); + void parsePrimeExpression(); + void parseTerm(); + void parsePrimeTerm(); + void parseFactor(); + // End of private methods. +public: + Parser(); + const SyntaxTree *parse(); +}; diff --git a/src/include/product.hpp b/src/include/product.hpp new file mode 100644 index 0000000..1a87f80 --- /dev/null +++ b/src/include/product.hpp @@ -0,0 +1,9 @@ +#pragma once +#include "syntax_tree.hpp" + + +class Product: SyntaxTree { +public: + Product(SyntaxTree *left, SyntaxTree *right); + float eval() const; +}; diff --git a/src/include/substraction.hpp b/src/include/substraction.hpp new file mode 100644 index 0000000..4a5a8c7 --- /dev/null +++ b/src/include/substraction.hpp @@ -0,0 +1,8 @@ +#pragma once +#include "syntax_tree.hpp" + +class Substraction: SyntaxTree { +public: + Substraction(SyntaxTree *left, SyntaxTree *right); + float eval() const; +}; diff --git a/src/include/sum.hpp b/src/include/sum.hpp new file mode 100644 index 0000000..35d6736 --- /dev/null +++ b/src/include/sum.hpp @@ -0,0 +1,8 @@ +#pragma once +#include "syntax_tree.hpp" + +class Sum: SyntaxTree { +public: + Sum(SyntaxTree *left, SyntaxTree *right); + float eval() const; +}; diff --git a/src/include/syntax_tree.hpp b/src/include/syntax_tree.hpp new file mode 100644 index 0000000..9edf23c --- /dev/null +++ b/src/include/syntax_tree.hpp @@ -0,0 +1,12 @@ +#pragma once +#include "tokenizer.hpp" + +class SyntaxTree { +protected: + const SyntaxTree *left {}; + const SyntaxTree *right {}; +public: + SyntaxTree(SyntaxTree *left = nullptr, SyntaxTree *right = nullptr); + virtual ~SyntaxTree(); + virtual float eval() const = 0; +}; diff --git a/src/include/tokenizer.hpp b/src/include/tokenizer.hpp index a3f9998..7d00055 100644 --- a/src/include/tokenizer.hpp +++ b/src/include/tokenizer.hpp @@ -1,7 +1,7 @@ #pragma once #include #include -#include +#define TOKENS_ARRAY_LEN 16 enum Type { operand, @@ -11,7 +11,8 @@ enum Type { division, left_parens, right_parens, - equals + equals, + nil }; struct Token { @@ -22,14 +23,14 @@ struct Token { class Tokenizer { private: // Private attributes - std::array tokens; + Token *tokens[17]; const std::string *to_tokenize; size_t current_char_index; short tokens_head; // Regexes - std::regex operand_regex = std::regex("^0{1}|\\d+"); - std::regex operation_regex = std::regex("\\+|-|/|\\*"); - std::regex parens_regex = std::regex("(|)"); + std::regex operand_regex = std::regex("^0{1}|^\\d+"); + std::regex operation_regex = std::regex("^\\+|^-|/|^\\*"); + std::regex parens_regex = std::regex("^\\(|^\\)"); // End of private attributes // Private methods void clearTokens(); @@ -42,6 +43,6 @@ private: public: Tokenizer(); ~Tokenizer(); - const std::array &tokenize(const std::string &operation); - const std::array &getTokens(); + Token **tokenize(const std::string &operation); + Token **getTokens(); }; diff --git a/src/operand.cpp b/src/operand.cpp new file mode 100644 index 0000000..67654f0 --- /dev/null +++ b/src/operand.cpp @@ -0,0 +1,13 @@ +#include "include/operand.hpp" +#include "include/syntax_tree.hpp" +#include "include/tokenizer.hpp" + + +Operand::Operand(int value) + : SyntaxTree{nullptr, nullptr} + ,value{value} +{} + +float Operand::eval() const { + return value; +} diff --git a/src/product.cpp b/src/product.cpp new file mode 100644 index 0000000..093aff1 --- /dev/null +++ b/src/product.cpp @@ -0,0 +1,11 @@ +#include "include/product.hpp" +#include "include/syntax_tree.hpp" + + +Product::Product(SyntaxTree *left, SyntaxTree *right) + : SyntaxTree{left, right} +{} + +float Product::eval() const { + return left->eval() * right->eval(); +} diff --git a/src/substraction.cpp b/src/substraction.cpp new file mode 100644 index 0000000..ba995b5 --- /dev/null +++ b/src/substraction.cpp @@ -0,0 +1,10 @@ +#include "include/substraction.hpp" +#include "include/syntax_tree.hpp" + + +Substraction::Substraction(SyntaxTree *left, SyntaxTree *right) + : SyntaxTree{left, right} {} + +float Substraction::eval() const { + return left->eval() - right->eval(); +} diff --git a/src/sum.cpp b/src/sum.cpp new file mode 100644 index 0000000..aa77d4e --- /dev/null +++ b/src/sum.cpp @@ -0,0 +1,9 @@ +#include "include/sum.hpp" +#include "include/syntax_tree.hpp" + + +Sum::Sum(SyntaxTree *left, SyntaxTree *right) : SyntaxTree{left, right} {} + +float Sum::eval() const { + return left->eval() + left->eval(); +} diff --git a/src/syntax_tree.cpp b/src/syntax_tree.cpp new file mode 100644 index 0000000..83b5d60 --- /dev/null +++ b/src/syntax_tree.cpp @@ -0,0 +1,13 @@ +#include "include/syntax_tree.hpp" + +SyntaxTree::SyntaxTree(SyntaxTree *left, + SyntaxTree *right) + : left{left}, right{right} {} + +SyntaxTree::~SyntaxTree() { + if(left != nullptr) + delete left; + + if(right != nullptr) + delete right; +} diff --git a/src/tokenizer.cpp b/src/tokenizer.cpp index a156000..783803e 100644 --- a/src/tokenizer.cpp +++ b/src/tokenizer.cpp @@ -1,11 +1,11 @@ #include -#include #include "include/tokenizer.hpp" #include "exceptions/tokenizer_exception.hpp" Tokenizer::Tokenizer() { - tokens.fill(nullptr); tokens_head = 0; + for(int i = 0; i < TOKENS_ARRAY_LEN; ++i) + tokens[i] = nullptr; } Tokenizer::~Tokenizer() { @@ -13,10 +13,14 @@ Tokenizer::~Tokenizer() { } void Tokenizer::clearTokens() { - for(auto token{ tokens.begin() }; *token != nullptr; ++token) - delete token; - - tokens.fill(nullptr); + tokens_head = 0; + + for(int i = 0; i < TOKENS_ARRAY_LEN; ++i) { + if(tokens[i] != nullptr) { + delete tokens[i]; + tokens[i] = nullptr; + } + } } void Tokenizer::insertToken(Type type, int value = 0) { @@ -27,8 +31,8 @@ void Tokenizer::insertToken(Type type, int value = 0) { } -const std::array -&Tokenizer::tokenize(const std::string &operation) { +Token **Tokenizer::tokenize(const std::string &operation) { + clearTokens(); to_tokenize = &operation; current_char_index = 0; @@ -44,6 +48,8 @@ const std::array throw TokenizerException("Invalid character detected."); } + // "Nil" terminated array. Intended to be the epsilon terminal in the grammar. + tokens[tokens_head] = new Token { .type = nil, .value = 0 }; return getTokens(); } @@ -72,7 +78,7 @@ void Tokenizer::matchParens(const std::string &operation) { std::string remaining(operation.substr(current_char_index)); bool result = std::regex_search(remaining, match, - operand_regex); + parens_regex); if(!result) return; @@ -97,7 +103,7 @@ void Tokenizer::matchOperation(const std::string &operation) { std::string remaining(operation.substr(current_char_index)); bool result = std::regex_search(remaining, match, - operand_regex); + operation_regex); if(!result) return; @@ -120,6 +126,6 @@ void Tokenizer::matchOperation(const std::string &operation) { current_char_index += 1; } -const std::array &Tokenizer::getTokens() { +Token **Tokenizer::getTokens() { return tokens; } -- cgit v1.2.3