From 3f322bc5792dbadd56435a5ebd0008be6c7ddae2 Mon Sep 17 00:00:00 2001 From: Deffo0 Date: Sun, 24 Dec 2023 14:19:31 +0200 Subject: [PATCH 01/41] start working on parsing table --- {SyntaxPhase => ParserPhase}/CMakeLists.txt | 0 ParserPhase/Common.h | 8 ++++++++ .../GrammarParser/CMakeLists.txt | 0 .../GrammarParser/GrammarConverter.cpp | 0 .../GrammarParser/GrammarConverter.h | 0 ParserPhase/Parser.cpp | 5 +++++ ParserPhase/Parser.h | 14 ++++++++++++++ ParserPhase/ParsingTree.h | 8 ++++++++ ParserPhase/Production.h | 8 ++++++++ ParserPhase/Symbol.h | 8 ++++++++ ParserPhase/Token.h | 8 ++++++++ 11 files changed, 59 insertions(+) rename {SyntaxPhase => ParserPhase}/CMakeLists.txt (100%) create mode 100644 ParserPhase/Common.h rename {SyntaxPhase => ParserPhase}/GrammarParser/CMakeLists.txt (100%) rename {SyntaxPhase => ParserPhase}/GrammarParser/GrammarConverter.cpp (100%) rename {SyntaxPhase => ParserPhase}/GrammarParser/GrammarConverter.h (100%) create mode 100644 ParserPhase/Parser.cpp create mode 100644 ParserPhase/Parser.h create mode 100644 ParserPhase/ParsingTree.h create mode 100644 ParserPhase/Production.h create mode 100644 ParserPhase/Symbol.h create mode 100644 ParserPhase/Token.h diff --git a/SyntaxPhase/CMakeLists.txt b/ParserPhase/CMakeLists.txt similarity index 100% rename from SyntaxPhase/CMakeLists.txt rename to ParserPhase/CMakeLists.txt diff --git a/ParserPhase/Common.h b/ParserPhase/Common.h new file mode 100644 index 0000000..36fb4c1 --- /dev/null +++ b/ParserPhase/Common.h @@ -0,0 +1,8 @@ +// +// Created by deffo on 24/12/23. +// + +#ifndef COMPILER_COMMON_H +#define COMPILER_COMMON_H + +#endif //COMPILER_COMMON_H diff --git a/SyntaxPhase/GrammarParser/CMakeLists.txt b/ParserPhase/GrammarParser/CMakeLists.txt similarity index 100% rename from SyntaxPhase/GrammarParser/CMakeLists.txt rename to ParserPhase/GrammarParser/CMakeLists.txt diff --git a/SyntaxPhase/GrammarParser/GrammarConverter.cpp b/ParserPhase/GrammarParser/GrammarConverter.cpp similarity index 100% rename from SyntaxPhase/GrammarParser/GrammarConverter.cpp rename to ParserPhase/GrammarParser/GrammarConverter.cpp diff --git a/SyntaxPhase/GrammarParser/GrammarConverter.h b/ParserPhase/GrammarParser/GrammarConverter.h similarity index 100% rename from SyntaxPhase/GrammarParser/GrammarConverter.h rename to ParserPhase/GrammarParser/GrammarConverter.h diff --git a/ParserPhase/Parser.cpp b/ParserPhase/Parser.cpp new file mode 100644 index 0000000..9ca3211 --- /dev/null +++ b/ParserPhase/Parser.cpp @@ -0,0 +1,5 @@ +// +// Created by deffo on 24/12/23. +// + +#include "Parser.h" diff --git a/ParserPhase/Parser.h b/ParserPhase/Parser.h new file mode 100644 index 0000000..2956001 --- /dev/null +++ b/ParserPhase/Parser.h @@ -0,0 +1,14 @@ +// +// Created by deffo on 24/12/23. +// + +#ifndef COMPILER_PARSER_H +#define COMPILER_PARSER_H + + +class Parser { + +}; + + +#endif //COMPILER_PARSER_H diff --git a/ParserPhase/ParsingTree.h b/ParserPhase/ParsingTree.h new file mode 100644 index 0000000..45158d5 --- /dev/null +++ b/ParserPhase/ParsingTree.h @@ -0,0 +1,8 @@ +// +// Created by deffo on 24/12/23. +// + +#ifndef COMPILER_PARSINGTREE_H +#define COMPILER_PARSINGTREE_H + +#endif //COMPILER_PARSINGTREE_H diff --git a/ParserPhase/Production.h b/ParserPhase/Production.h new file mode 100644 index 0000000..2731bff --- /dev/null +++ b/ParserPhase/Production.h @@ -0,0 +1,8 @@ +// +// Created by deffo on 24/12/23. +// + +#ifndef COMPILER_PRODUCTION_H +#define COMPILER_PRODUCTION_H + +#endif //COMPILER_PRODUCTION_H diff --git a/ParserPhase/Symbol.h b/ParserPhase/Symbol.h new file mode 100644 index 0000000..14ac1a7 --- /dev/null +++ b/ParserPhase/Symbol.h @@ -0,0 +1,8 @@ +// +// Created by deffo on 24/12/23. +// + +#ifndef COMPILER_SYMBOL_H +#define COMPILER_SYMBOL_H + +#endif //COMPILER_SYMBOL_H diff --git a/ParserPhase/Token.h b/ParserPhase/Token.h new file mode 100644 index 0000000..3f5ec60 --- /dev/null +++ b/ParserPhase/Token.h @@ -0,0 +1,8 @@ +// +// Created by deffo on 24/12/23. +// + +#ifndef COMPILER_TOKEN_H +#define COMPILER_TOKEN_H + +#endif //COMPILER_TOKEN_H From c0d5d5e55a4c7d0ee140cd880e7061ce7a309319 Mon Sep 17 00:00:00 2001 From: Deffo0 Date: Sun, 24 Dec 2023 14:21:02 +0200 Subject: [PATCH 02/41] merge --- CMakeLists.txt | 11 ++++-- ParserPhase/CMakeLists.txt | 2 +- ParserPhase/Common.h | 9 +++++ ParserPhase/GrammarParser/CMakeLists.txt | 3 +- ParserPhase/Parser.cpp | 45 ++++++++++++++++++++++++ ParserPhase/Parser.h | 23 +++++++++++- ParserPhase/ParsingTree.h | 26 ++++++++++++++ ParserPhase/Production.h | 11 ++++++ ParserPhase/Symbol.h | 20 +++++++++++ ParserPhase/Token.h | 11 ++++++ main.cpp | 40 ++++++++++----------- 11 files changed, 176 insertions(+), 25 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index a201085..f515b82 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -3,7 +3,14 @@ project(Compiler) set(CMAKE_CXX_STANDARD 20) -add_executable(Compiler main.cpp) +add_executable(Compiler main.cpp + ParserPhase/Parser.cpp + ParserPhase/Parser.h + ParserPhase/Common.h + ParserPhase/Symbol.h + ParserPhase/ParsingTree.h + ParserPhase/Token.h + ParserPhase/Production.h) target_link_libraries(Compiler Util_lib) target_link_libraries(Compiler RulesParser_lib) @@ -15,6 +22,6 @@ target_link_libraries(Compiler GrammarParser_lib) add_subdirectory(Util) add_subdirectory(LexicalPhase) -add_subdirectory(SyntaxPhase) +add_subdirectory(ParserPhase) add_subdirectory(Google_tests) diff --git a/ParserPhase/CMakeLists.txt b/ParserPhase/CMakeLists.txt index 98f954a..4df86d0 100644 --- a/ParserPhase/CMakeLists.txt +++ b/ParserPhase/CMakeLists.txt @@ -1,3 +1,3 @@ -project(SyntaxPhase) +project(ParserPhase) add_subdirectory(GrammarParser) \ No newline at end of file diff --git a/ParserPhase/Common.h b/ParserPhase/Common.h index 36fb4c1..58804c5 100644 --- a/ParserPhase/Common.h +++ b/ParserPhase/Common.h @@ -5,4 +5,13 @@ #ifndef COMPILER_COMMON_H #define COMPILER_COMMON_H + +#include +#include +#include +#include +#include + +#define EPSILON '\0' + #endif //COMPILER_COMMON_H diff --git a/ParserPhase/GrammarParser/CMakeLists.txt b/ParserPhase/GrammarParser/CMakeLists.txt index 89ceefa..587dfa7 100644 --- a/ParserPhase/GrammarParser/CMakeLists.txt +++ b/ParserPhase/GrammarParser/CMakeLists.txt @@ -4,7 +4,8 @@ set(HEADER_FILES GrammarConverter.h) set(SOURCE_FILES - GrammarConverter.cpp) + GrammarConverter.cpp +) add_library(GrammarParser_lib STATIC ${SOURCE_FILES} ${HEADER_FILES}) diff --git a/ParserPhase/Parser.cpp b/ParserPhase/Parser.cpp index 9ca3211..982e241 100644 --- a/ParserPhase/Parser.cpp +++ b/ParserPhase/Parser.cpp @@ -3,3 +3,48 @@ // #include "Parser.h" + + +Parser::Parser(CFGrammar& cfg): grammar(cfg) { + computeFirstSet(); + computeFollowSet(); + constructParseTable(); +} + +Token& nextToken(std::vector& input, int& index) { + return input[index++]; +}; + +ParsingTree Parser::parse(std::vector& input) { + // TODO: protect against empty input vector + // TODO: create syntax tree (create from left to right - leaf to root) + + int lookaheadIndex = 0; + Token& lookahead = nextToken(input, lookaheadIndex); + + std::stack stack; + stack.push(this->grammar.startSymbol); + + while (!stack.empty()) { + Symbol* currentSymbol = stack.top(); stack.pop(); + + if (currentSymbol->isTerminal()) { + bool match = currentSymbol->name == lookahead.terminal.name; + if (!match) { + // TODO: handle error + } + + lookahead = nextToken(input, lookaheadIndex); + } else { + Production* production = parseTable[{currentSymbol, lookahead.terminal}]; + + for (auto& symbol: production->rhs) + stack.push(symbol); + } + } +} + + +void Parser::computeFirstSet() {} +void Parser::computeFollowSet() {} +void Parser::constructParseTable() {} \ No newline at end of file diff --git a/ParserPhase/Parser.h b/ParserPhase/Parser.h index 2956001..e615e49 100644 --- a/ParserPhase/Parser.h +++ b/ParserPhase/Parser.h @@ -6,9 +6,30 @@ #define COMPILER_PARSER_H + +#include "Common.h" +#include "CFGrammar.h" +#include "ParsingTree.h" +#include "Token.h" +#include "Production.h" + class Parser { +private: + CFGrammar grammar; + // TODO: might need hash and compare functions for this map (not sure) + std::unordered_map,Production*> parseTable; + std::unordered_set> firstSet; + std::unordered_set> followSet; -}; + std::unordered_set first(std::vector symbols); + std::unordered_set follow(std::vector symbols); + void computeFirstSet(); + void computeFollowSet(); + void constructParseTable(); +public: + Parser(CFGrammar& grammar); + SyntaxTree parse(std::vector& input); +}; #endif //COMPILER_PARSER_H diff --git a/ParserPhase/ParsingTree.h b/ParserPhase/ParsingTree.h index 45158d5..1070715 100644 --- a/ParserPhase/ParsingTree.h +++ b/ParserPhase/ParsingTree.h @@ -5,4 +5,30 @@ #ifndef COMPILER_PARSINGTREE_H #define COMPILER_PARSINGTREE_H +#include "Common.h" +#include "Symbol.h" + +class ParsingTreeNode +{ +private: + Symbol *symbol; + std::vector children; +}; + +class ParsingTreeLeafNode : ParsingTreeNode +{ +private: + std::string lexeme; +}; + +class ParsingTree +{ +private: + ParsingTreeNode *root; + +public: + ParsingTree(ParsingTreeNode *r) : root(r){}; + void print(); +}; + #endif //COMPILER_PARSINGTREE_H diff --git a/ParserPhase/Production.h b/ParserPhase/Production.h index 2731bff..0453a6d 100644 --- a/ParserPhase/Production.h +++ b/ParserPhase/Production.h @@ -5,4 +5,15 @@ #ifndef COMPILER_PRODUCTION_H #define COMPILER_PRODUCTION_H + +#include "Common.h" +#include "Symbol.h" + +class Production { +public: + NonTerminal* lhs; + std::vector rhs; + Production(NonTerminal* l, std::vector r): lhs(l), rhs(r) {}; +}; + #endif //COMPILER_PRODUCTION_H diff --git a/ParserPhase/Symbol.h b/ParserPhase/Symbol.h index 14ac1a7..5af4082 100644 --- a/ParserPhase/Symbol.h +++ b/ParserPhase/Symbol.h @@ -5,4 +5,24 @@ #ifndef COMPILER_SYMBOL_H #define COMPILER_SYMBOL_H +#include "Common.h" + +class Symbol { +protected: + Symbol(std::string n, bool t): name(n), terminal(t) {}; +private: + bool terminal; +public: + std::string name; + bool isTerminal() { return this->terminal; }; +}; + +class Terminal : public Symbol { + Terminal(std::string n) : Symbol(n, true) {} +}; + +class NonTerminal : public Symbol { + NonTerminal(std::string n) : Symbol(n, false) {} +}; + #endif //COMPILER_SYMBOL_H diff --git a/ParserPhase/Token.h b/ParserPhase/Token.h index 3f5ec60..393edcf 100644 --- a/ParserPhase/Token.h +++ b/ParserPhase/Token.h @@ -5,4 +5,15 @@ #ifndef COMPILER_TOKEN_H #define COMPILER_TOKEN_H + +#include "Common.h" +#include "Symbol.h" + +class Token +{ +public: + Terminal terminal; + std::string lexeme; +}; + #endif //COMPILER_TOKEN_H diff --git a/main.cpp b/main.cpp index 2710582..a098666 100644 --- a/main.cpp +++ b/main.cpp @@ -56,26 +56,26 @@ int main(int argc, char *argv[]) { } std::cout << "num of DFA states: " << states << '\n'; -// State* minimzedDFAStartState = dfa.minimize(); -// -// int minimizedStates = 0; -// std::stack minimizedFrontier; -// std::unordered_map minimizedVisited; -// minimizedFrontier.push(minimzedDFAStartState); -// while(not minimizedFrontier.empty()){ -// State* miniCurrentState = minimizedFrontier.top(); -// minimizedFrontier.pop(); -// if(minimizedVisited.find(miniCurrentState) != minimizedVisited.end()){ -// continue; -// } -// minimizedStates += 1; -// minimizedVisited[miniCurrentState] = 1; -// for(Transition transition: miniCurrentState->transitions){ -// minimizedFrontier.push(transition.getNextState()); -// std::cout << miniCurrentState << " with input: " << transition.getInput() << " ,to state: " << transition.getNextState() << '\n'; -// } -// } -// std::cout << "num of minimized DFA states: " << minimizedStates << '\n'; + State* minimzedDFAStartState = dfa.minimize(); + + int minimizedStates = 0; + std::stack minimizedFrontier; + std::unordered_map minimizedVisited; + minimizedFrontier.push(minimzedDFAStartState); + while(not minimizedFrontier.empty()){ + State* miniCurrentState = minimizedFrontier.top(); + minimizedFrontier.pop(); + if(minimizedVisited.find(miniCurrentState) != minimizedVisited.end()){ + continue; + } + minimizedStates += 1; + minimizedVisited[miniCurrentState] = 1; + for(Transition transition: miniCurrentState->transitions){ + minimizedFrontier.push(transition.getNextState()); + std::cout << miniCurrentState << " with input: " << transition.getInput() << " ,to state: " << transition.getNextState() << '\n'; + } + } + std::cout << "num of minimized DFA states: " << minimizedStates << '\n'; STGenerator stg(dfa); std::cout << "Run all files in dir --> a\nRun specific file --> s\n> "; From a600c2aeffa2c07fcb57c8b97e54f746081fd994 Mon Sep 17 00:00:00 2001 From: Ahmed Adel <73228199+Deffo0@users.noreply.github.com> Date: Wed, 27 Dec 2023 10:14:33 +0200 Subject: [PATCH 03/41] Comment the parsing tree generation --- ParserPhase/PredictiveParser/Parser.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ParserPhase/PredictiveParser/Parser.h b/ParserPhase/PredictiveParser/Parser.h index 17f95d3..b1abcda 100644 --- a/ParserPhase/PredictiveParser/Parser.h +++ b/ParserPhase/PredictiveParser/Parser.h @@ -39,7 +39,7 @@ class Parser { void constructParseTable(); public: Parser(Grammar& grammar); - ParsingTree parse(std::vector& input); + // ParsingTree parse(std::vector& input); }; From f7e273878da755d9f01ecc65608f8be3f6f9c392 Mon Sep 17 00:00:00 2001 From: Ahmed Adel <73228199+Deffo0@users.noreply.github.com> Date: Wed, 27 Dec 2023 10:15:21 +0200 Subject: [PATCH 04/41] Comment the parsing tree generation --- ParserPhase/PredictiveParser/Parser.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/ParserPhase/PredictiveParser/Parser.cpp b/ParserPhase/PredictiveParser/Parser.cpp index a796b1a..531483c 100644 --- a/ParserPhase/PredictiveParser/Parser.cpp +++ b/ParserPhase/PredictiveParser/Parser.cpp @@ -16,7 +16,7 @@ Token& nextToken(std::vector& input, int& index) { return input[index++]; }; -ParsingTree Parser::parse(std::vector& input) { +// ParsingTree Parser::parse(std::vector& input) { // // TODO: protect against empty input vector // // TODO: create syntax tree (create from left to right - leaf to root) // @@ -43,7 +43,7 @@ ParsingTree Parser::parse(std::vector& input) { // stack.push(symbol); // } // } -} +//} void Parser::computeNTsWithFirstSet() { FirstSetsGenerator firstSG(NTs); @@ -78,4 +78,4 @@ void Parser::constructParseTable() { } } -} \ No newline at end of file +} From 532d9869d5d9dcb1294fbf8dcd6d8a9b0d1474ae Mon Sep 17 00:00:00 2001 From: Meniem Hany Date: Tue, 26 Dec 2023 01:25:06 +0200 Subject: [PATCH 05/41] Minor Changes --- GrammarTest.txt | 8 +------- ParserPhase/GrammarParser/GrammarConverter.cpp | 7 +++---- 2 files changed, 4 insertions(+), 11 deletions(-) diff --git a/GrammarTest.txt b/GrammarTest.txt index d7cc4c2..3a0e34a 100644 --- a/GrammarTest.txt +++ b/GrammarTest.txt @@ -1,7 +1 @@ -# E ::= T E` -# E` ::= '+' E | '\\L' -# T ::= F T` -# T` ::= T | '\\L' -# F ::= P F` -# F` ::= '*' F | '\\L' -# P ::= '(' E ')' | 'a' | 'b' | 'Em' \ No newline at end of file +# E ::= E 'b' | 'a' diff --git a/ParserPhase/GrammarParser/GrammarConverter.cpp b/ParserPhase/GrammarParser/GrammarConverter.cpp index 43cd45d..ba04337 100644 --- a/ParserPhase/GrammarParser/GrammarConverter.cpp +++ b/ParserPhase/GrammarParser/GrammarConverter.cpp @@ -51,15 +51,15 @@ int GrammarConverter::parseFile(const std::string& filePath) { std::vector definitionSides = splitWithStringDelimiter(nonTerminalDefinition, "::="); std::string nonTerminalName = definitionSides[0]; std::string nonTerminalProductions = definitionSides[1]; + trimBlanksFromEnds(nonTerminalName); + trimBlanksFromEnds(nonTerminalProductions); removeConsecutiveSpaces(nonTerminalName); + removeConsecutiveSpaces(nonTerminalProductions); int status = findTerminals(nonTerminalProductions); if (status == -1) return -1; - removeConsecutiveSpaces(nonTerminalProductions); - trimBlanksFromEnds(nonTerminalProductions); - status = parseProductions(nonTerminalName, nonTerminalProductions); if (status == -1) return -1; @@ -123,7 +123,6 @@ const std::vector &GrammarConverter::getNonTerminals() const int GrammarConverter::findTerminals(std::string& productions) { // accumulates terminal symbols if they exist std::string accumulator; - trimBlanksFromEnds(productions); for(int i = 0; i < productions.size(); i++){ char c = productions[i]; // check if single quote is found with no escape character before it. From 985f8f74e3bf04f343d9e9a5477c3a45dd6f17f0 Mon Sep 17 00:00:00 2001 From: Zyad Samy Date: Wed, 27 Dec 2023 11:02:16 +0200 Subject: [PATCH 06/41] parser module --- .DS_Store | Bin 0 -> 6148 bytes Google_tests/CMakeLists.txt | 1 + Google_tests/ParserTest.cpp | 51 ++++++++ ParserPhase/CMakeLists.txt | 2 +- ParserPhase/Common/Symbol.h | 2 + ParserPhase/PredictiveParser/CMakeLists.txt | 7 +- ParserPhase/PredictiveParser/Parser.cpp | 76 +++++++----- ParserPhase/PredictiveParser/Parser.h | 3 +- ParserPhase/PredictiveParser/ParsingTree.cpp | 44 +++++++ ParserPhase/PredictiveParser/ParsingTree.h | 33 +++--- ParserPhase/PredictiveParser/Token.h | 7 +- main.cpp | 116 +++++++++++++++++-- 12 files changed, 279 insertions(+), 63 deletions(-) create mode 100644 .DS_Store create mode 100644 Google_tests/ParserTest.cpp create mode 100644 ParserPhase/PredictiveParser/ParsingTree.cpp diff --git a/.DS_Store b/.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..9c5fca97958e73537e97c843bb97ec8271859202 GIT binary patch literal 6148 zcmeHKK~BRk5FA4eg_P1G7o@zP50Z<1}p$i7vOiE0ii?7&*FR2oZZKIDf3^B!wuBHm8 z0)JfrnY%R{;T#=2B0GN@oW%>wPLpx3n~b}xCyT@(z42*{IiQLYjCl=^kbO%|-_8F5 z@4dha$$MtZ5w8w&rP0iOfwfy;UHNF`?AHRj%GIgQF%8jU%mf#BAWqTG_tkt9coo~n zA)eUF9wwON>u(GS^+UESV`9rp*F>?bw~UfoxWwG5I4xHLqG6U1qKy~!YQ|`bb#dg4 znOY`JW%-WERTIs#Q370(F+`FdwGnl*^>%Q>ez)0|5jB#p;rs1O3rx{B%jB)vFDSr0 zTdWc~)KnEv1yq5h0d-D7OzaVW7_itH^YWV@oXBJBF?Gl%G~=j5M>YP4 zVH}<1k*^nfOdUEpj6ZxBAKCa5im}nzeq^h|#11u81yq5&0-Nr(F8lxS`tyIDq<5-- zD)6rqFhQ%`YH~<^Z(SLj?6nd7jxHwkQiqnp#BawoBU|wv-5Q@ok|D+(Q->^}>5qVw LK@(NrM-})2$r8AZ literal 0 HcmV?d00001 diff --git a/Google_tests/CMakeLists.txt b/Google_tests/CMakeLists.txt index 03e8322..54b7f48 100644 --- a/Google_tests/CMakeLists.txt +++ b/Google_tests/CMakeLists.txt @@ -10,6 +10,7 @@ add_executable(Google_Tests_run UtilsTest.cpp DFATest.cpp STGeneratorTest.cpp GrammarConverterTest.cpp + ParserTest.cpp ) target_link_libraries(Google_Tests_run gtest gtest_main) diff --git a/Google_tests/ParserTest.cpp b/Google_tests/ParserTest.cpp new file mode 100644 index 0000000..a892a2a --- /dev/null +++ b/Google_tests/ParserTest.cpp @@ -0,0 +1,51 @@ +#include +#include "../ParserPhase/PredictiveParser/Parser.h" + +#define MULTILINE(...) #__VA_ARGS__ + +// Define a fixture for the parser tests +// class ParserTest : public ::testing::Test { +// protected: +// // Declare variables needed for the tests +// Parser parser; +// }; + +// Test case for parsing valid input +TEST(ParserTest, ValidInputParsing) { + // Define your tokens and grammar for valid input + std::vector tokens = { + new Token("ident","x"), + new Token("+","+"), + new Token("num","5"), + new Token("*","*"), + new Token("ident","y"), + }; + + std::string grammarString = + "Expr -> Term + Expr | Term - Expr | Term \ + Term -> Factor * Term | Factor / Term | Factor \ + Factor -> (Expr) | Number \ + Number -> [0-9]+"; + + + // Grammar grammar = /* define your grammar */; + + // Parse the input + // ParsingTree tree = parser.parse(tokens, grammar); + + // Assert that the parsing tree is constructed correctly + // ASSERT_TRUE(/* perform assertions on the parsing tree */); +} + +// // Test case for parsing invalid input +// TEST_F(ParserTest, InvalidInputParsing) { +// // Define your tokens and grammar for invalid input +// std::vector tokens = /* define invalid tokens */; +// Grammar grammar = /* define your grammar */; + +// // Parse the invalid input +// ParsingTree tree = parser.parse(tokens, grammar); + +// // Assert that the parsing tree is not constructed or is invalid +// ASSERT_FALSE(/* perform assertions on the parsing tree */); +// } diff --git a/ParserPhase/CMakeLists.txt b/ParserPhase/CMakeLists.txt index 884e52b..e092aa5 100644 --- a/ParserPhase/CMakeLists.txt +++ b/ParserPhase/CMakeLists.txt @@ -3,4 +3,4 @@ project(SyntaxPhase) add_subdirectory(GrammarParser) add_subdirectory(Common) add_subdirectory(FirstAndFollowGenerator) -add_subdirectory(PredictiveParser) \ No newline at end of file +add_subdirectory(PredictiveParser) diff --git a/ParserPhase/Common/Symbol.h b/ParserPhase/Common/Symbol.h index 209d9b5..92fe3b7 100644 --- a/ParserPhase/Common/Symbol.h +++ b/ParserPhase/Common/Symbol.h @@ -6,6 +6,8 @@ #define COMPILER_SYMBOL_H #include +#include + class Symbol { diff --git a/ParserPhase/PredictiveParser/CMakeLists.txt b/ParserPhase/PredictiveParser/CMakeLists.txt index 2d18d16..5d42278 100644 --- a/ParserPhase/PredictiveParser/CMakeLists.txt +++ b/ParserPhase/PredictiveParser/CMakeLists.txt @@ -6,11 +6,12 @@ set(HEADER_FILES ParsingTree.h Token.h ParsingTableEntry.h) - - - + + + set(SOURCE_FILES Parser.cpp + ParsingTree.cpp ParsingTableEntry.cpp) diff --git a/ParserPhase/PredictiveParser/Parser.cpp b/ParserPhase/PredictiveParser/Parser.cpp index 531483c..ae9d354 100644 --- a/ParserPhase/PredictiveParser/Parser.cpp +++ b/ParserPhase/PredictiveParser/Parser.cpp @@ -16,34 +16,53 @@ Token& nextToken(std::vector& input, int& index) { return input[index++]; }; -// ParsingTree Parser::parse(std::vector& input) { -// // TODO: protect against empty input vector -// // TODO: create syntax tree (create from left to right - leaf to root) -// -// int lookaheadIndex = 0; -// Token& lookahead = nextToken(input, lookaheadIndex); -// -// std::stack stack; -// stack.push(this->grammar.startSymbol); -// -// while (!stack.empty()) { -// Symbol* currentSymbol = stack.top(); stack.pop(); -// -// if (currentSymbol->isTerminal()) { -// bool match = currentSymbol->name == lookahead.terminal.name; -// if (!match) { -// // TODO: handle error -// } -// -// lookahead = nextToken(input, lookaheadIndex); -// } else { -// Production* production = parseTable[{currentSymbol, lookahead.terminal}]; -// -// for (auto& symbol: production->rhs) -// stack.push(symbol); -// } -// } -//} +ParsingTree Parser::parse(std::vector& input) { + if (input.size() == 0) + return ParsingTree(); + + Symbol* startSymbol = grammar.getStartSymbol(); + ParsingTreeNode* rootNode = new ParsingTreeNode(startSymbol); + + if (startSymbol->isTerminal()) + throw std::invalid_argument("startSymbol cannot be a terminal"); + + int lookaheadIndex = 0; + Token& lookahead = nextToken(input, lookaheadIndex); + + std::stack stack; + std::stack nodes; + stack.push(startSymbol); + nodes.push(rootNode); + + while (!stack.empty()) { + Symbol* currentSymbol = stack.top(); stack.pop(); + ParsingTreeNode* currentNode = nodes.top(); nodes.pop(); + + if (currentSymbol->isTerminal()) { + bool match = currentSymbol->getName() == lookahead.terminal; + if (!match) { + // TODO: handle error + } + + lookahead = nextToken(input, lookaheadIndex); + } else { + NonTerminal* current = dynamic_cast(currentSymbol); + ParsingTableEntry entry = parsingTable[{current, lookahead.terminal}]; + + for (auto& production: entry.getProductions()) { + for (auto& symbol: production) { + stack.push(symbol); + + ParsingTreeNode* n = new ParsingTreeNode(symbol); + currentNode->addChild(n); + nodes.push(n); + } + } + } + } + + return ParsingTree(rootNode); +} void Parser::computeNTsWithFirstSet() { FirstSetsGenerator firstSG(NTs); @@ -79,3 +98,4 @@ void Parser::constructParseTable() { } } + diff --git a/ParserPhase/PredictiveParser/Parser.h b/ParserPhase/PredictiveParser/Parser.h index b1abcda..6a1571a 100644 --- a/ParserPhase/PredictiveParser/Parser.h +++ b/ParserPhase/PredictiveParser/Parser.h @@ -39,8 +39,7 @@ class Parser { void constructParseTable(); public: Parser(Grammar& grammar); - // ParsingTree parse(std::vector& input); - + ParsingTree parse(std::vector& input); }; #endif //COMPILER_PARSER_H diff --git a/ParserPhase/PredictiveParser/ParsingTree.cpp b/ParserPhase/PredictiveParser/ParsingTree.cpp new file mode 100644 index 0000000..fa43fcc --- /dev/null +++ b/ParserPhase/PredictiveParser/ParsingTree.cpp @@ -0,0 +1,44 @@ +#include "ParsingTree.h" + +ParsingTree::ParsingTree() = default; + +ParsingTree::ParsingTree(ParsingTreeNode *r) : root(r){} + +void ParsingTree::print() { + if (root == nullptr) { + std::cout << "Empty tree"; + } else { + printNode("", root, true); + } +} + +void ParsingTree::printNode(const std::string& prefix, ParsingTreeNode* node, bool isLast) +{ + if (node == nullptr) + throw std::invalid_argument("null node is found in the tree"); + + std::cout << prefix; + std::cout << (isLast ? "└──" : "├──"); + std::cout << node << std::endl; + + auto children = node->getChildren(); + for (int i=0; i < children.size(); i++) { + bool isLastChild = (i == children.size() - 1); + printNode(prefix + (isLast ? " " : "│ "), children[i], isLastChild); + } +} + + +ParsingTreeNode::ParsingTreeNode(Symbol* s) : symbol(s) {} + +void ParsingTreeNode::addChild(ParsingTreeNode* child) { + this->children.push_back(child); +} + +bool ParsingTreeNode::isLeaf() { + return this->children.empty(); +} + +std::vector ParsingTreeNode::getChildren() { + return this->children; +} \ No newline at end of file diff --git a/ParserPhase/PredictiveParser/ParsingTree.h b/ParserPhase/PredictiveParser/ParsingTree.h index 4077777..363a8e1 100644 --- a/ParserPhase/PredictiveParser/ParsingTree.h +++ b/ParserPhase/PredictiveParser/ParsingTree.h @@ -8,24 +8,25 @@ #include "Common.h" #include "../Common/NonTerminal.h" -class ParsingTreeNode{ - private: - NonTerminal *symbol; - std::vector children; +class ParsingTreeNode { +private: + Symbol* symbol; + std::vector children; +public: + ParsingTreeNode(Symbol* symbol); + void addChild(ParsingTreeNode* child); + std::vector getChildren(); + bool isLeaf(); }; -class ParsingTreeLeafNode : ParsingTreeNode{ - private: - std::string lexeme; -}; - -class ParsingTree{ - private: - ParsingTreeNode *root; - - public: - ParsingTree(ParsingTreeNode *r) : root(r){}; - void print(); +class ParsingTree { +private: + ParsingTreeNode* root; + void printNode(const std::string& prefix, ParsingTreeNode* node, bool isLast); +public: + ParsingTree(); + ParsingTree(ParsingTreeNode *r); + void print(); }; #endif //COMPILER_PARSINGTREE_H diff --git a/ParserPhase/PredictiveParser/Token.h b/ParserPhase/PredictiveParser/Token.h index 1ff7f44..95106ea 100644 --- a/ParserPhase/PredictiveParser/Token.h +++ b/ParserPhase/PredictiveParser/Token.h @@ -9,9 +9,10 @@ #include "Common.h" class Token{ - public: - std::string terminal; - std::string lexeme; +public: + Token(std::string t, std::string l): terminal(t), lexeme(l) {}; + std::string terminal; + std::string lexeme; }; #endif //COMPILER_TOKEN_H diff --git a/main.cpp b/main.cpp index 33b0beb..ae1024f 100644 --- a/main.cpp +++ b/main.cpp @@ -10,11 +10,10 @@ #include "ParserPhase/FirstAndFollowGenerator/FirstSetsGenerator.h" #include "ParserPhase/FirstAndFollowGenerator/FollowSetsGenerator.h" - -int main(int argc, char *argv[]) { - - if (argc != 2){ - std::cerr << "Wrong number of parameter (One argument required: Rules File Path)" << "\n"; + if (argc != 2) + { + std::cerr << "Wrong number of parameter (One argument required: Rules File Path)" + << "\n"; return -1; } @@ -24,16 +23,21 @@ int main(int argc, char *argv[]) { return -1; bool leftFactored = grammarConverter.leftFactor(); - if (leftFactored){ - std::cerr<< "Grammar was not left factored but left factoring was eliminated successfully." << "\n"; + if (leftFactored) + { + std::cerr << "Grammar was not left factored but left factoring was eliminated successfully." + << "\n"; } bool leftRecursion = grammarConverter.eliminateLeftRecursion(); - if (leftRecursion){ - std::cerr<< "Grammar had left recursion but was eliminated successfully." << "\n"; + if (leftRecursion) + { + std::cerr << "Grammar had left recursion but was eliminated successfully." + << "\n"; } - for (const NonTerminalSymbol& nonTerminal : grammarConverter.getNonTerminals()) { + for (const NonTerminalSymbol &nonTerminal : grammarConverter.getNonTerminals()) + { std::cout << nonTerminal.toString() << "\n"; } @@ -152,6 +156,98 @@ int main(int argc, char *argv[]) { // std::cout << "Invalid input. Program Terminated." << std::endl; // } + // RulesConverter rulesConverter(argv[1]); + // int statusCode = rulesConverter.parseFile(); + // if (statusCode == -1){ + // std::cerr << "Badly Formatted Rules File" << "\n"; + // return -1; + // } + // + // std::vector regularExpressions = rulesConverter.getRegularExpressions(); + // + // for (const RegularExpression& regExp : regularExpressions) { + // std::cout << regExp.toString() << "\n"; + // } + // + // // Regular expressions conversion to NFA + // NFACombiner nfaCombiner(regularExpressions); + // std::unordered_map, std::vector, PairHash, PairEqual> table = nfaCombiner.extractTableRepresentation(); + // State* nfaComplete = nfaCombiner.getCompleteNfa(); + // + // std::cout << "degree of the start state in NFA: " << nfaComplete->transitions.size() << '\n'; + // + // DFA dfa(nfaComplete); + // State* dfaStartState = dfa.getStartState(); + // + // int states = 0; + // std::stack frontier; + // std::unordered_map visited; + // frontier.push(dfaStartState); + // while(not frontier.empty()){ + // State* currentState = frontier.top(); + // frontier.pop(); + // if(visited.find(currentState) != visited.end()){ + // continue; + // } + // states += 1; + // visited[currentState] = 1; + // for(Transition transition: currentState->transitions){ + // frontier.push(transition.getNextState()); + // std::cout << currentState << " with input: " << transition.getInput() << " ,to state: " << transition.getNextState() << '\n'; + // } + // } + // std::cout << "num of DFA states: " << states << '\n'; + // + // State* minimzedDFAStartState = dfa.minimize(); + // + // int minimizedStates = 0; + // std::stack minimizedFrontier; + // std::unordered_map minimizedVisited; + // minimizedFrontier.push(minimzedDFAStartState); + // while(not minimizedFrontier.empty()){ + // State* miniCurrentState = minimizedFrontier.top(); + // minimizedFrontier.pop(); + // if(minimizedVisited.find(miniCurrentState) != minimizedVisited.end()){ + // continue; + // } + // minimizedStates += 1; + // minimizedVisited[miniCurrentState] = 1; + // for(Transition transition: miniCurrentState->transitions){ + // minimizedFrontier.push(transition.getNextState()); + // std::cout << miniCurrentState << " with input: " << transition.getInput() << " ,to state: " << transition.getNextState() << '\n'; + // } + // } + // std::cout << "num of minimized DFA states: " << minimizedStates << '\n'; + // + // STGenerator stg(dfa); + // std::cout << "Run all files in dir --> a\nRun specific file --> s\n> "; + // std::string input; + // std::getline(std::cin, input); + // std::string dirPath; + // if (input == "a") { + // std::cout << "Enter the directory path: "; + // std::getline(std::cin, dirPath); + // for (const auto &entry: std::filesystem::directory_iterator(dirPath)) { + // if (entry.is_regular_file()) { + // std::string scriptFilePath = entry.path().string(); + // std::cout << "Running: " << scriptFilePath << std::endl; + // stg.execute(scriptFilePath); + // std::cout << "\n#############W####" << std::endl; + // } + // } + // } else if (input == "s") { + // while (true) { + // std::cout << "Enter the file path: "; + // std::string scriptFilePath; + // std::getline(std::cin, scriptFilePath); + // if (scriptFilePath == "$") + // break; + // stg.execute(scriptFilePath); + // std::cout << "\n#############W####" << std::endl; + // } + // }else{ + // std::cout << "Invalid input. Program Terminated." << std::endl; + // } return 0; } \ No newline at end of file From 837a3ed547f2cd75efc02d1587ead2397e025aee Mon Sep 17 00:00:00 2001 From: Zyad Samy Date: Wed, 27 Dec 2023 11:27:14 +0200 Subject: [PATCH 07/41] rename `productionsVector` --- ParserPhase/PredictiveParser/Common.h | 2 +- ParserPhase/PredictiveParser/ParsingTableEntry.cpp | 4 ++-- ParserPhase/PredictiveParser/ParsingTableEntry.h | 7 ++++--- 3 files changed, 7 insertions(+), 6 deletions(-) diff --git a/ParserPhase/PredictiveParser/Common.h b/ParserPhase/PredictiveParser/Common.h index cefbc84..c6f3bf9 100644 --- a/ParserPhase/PredictiveParser/Common.h +++ b/ParserPhase/PredictiveParser/Common.h @@ -15,7 +15,7 @@ #include "Token.h" #include "../Common/Symbol.h" -typedef std::vector>> productionsVector; +typedef std::vector>> ProductionsVector; #define EPSILON '\0' #define END "$" diff --git a/ParserPhase/PredictiveParser/ParsingTableEntry.cpp b/ParserPhase/PredictiveParser/ParsingTableEntry.cpp index becf833..92ba0af 100644 --- a/ParserPhase/PredictiveParser/ParsingTableEntry.cpp +++ b/ParserPhase/PredictiveParser/ParsingTableEntry.cpp @@ -14,7 +14,7 @@ ParsingTableEntry::ParsingTableEntry(std::string type) { } } -ParsingTableEntry::ParsingTableEntry(productionsVector productions) { +ParsingTableEntry::ParsingTableEntry(ProductionsVector productions) { this->productions = std::move(productions); } @@ -26,7 +26,7 @@ bool ParsingTableEntry::isEpsilon() const { return this->epsilon; } -productionsVector ParsingTableEntry::getProductions() { +ProductionsVector ParsingTableEntry::getProductions() { return this->productions; } diff --git a/ParserPhase/PredictiveParser/ParsingTableEntry.h b/ParserPhase/PredictiveParser/ParsingTableEntry.h index e15bb73..ccb8610 100644 --- a/ParserPhase/PredictiveParser/ParsingTableEntry.h +++ b/ParserPhase/PredictiveParser/ParsingTableEntry.h @@ -6,19 +6,20 @@ #define COMPILER_PARSINGTABLEENTRY_H #include "Common.h" + class ParsingTableEntry { private: bool epsilon = false; bool sync = false; - productionsVector productions; + ProductionsVector productions; public: ParsingTableEntry(); explicit ParsingTableEntry(std::string type); - explicit ParsingTableEntry(productionsVector productions); + explicit ParsingTableEntry(ProductionsVector productions); [[nodiscard]] bool isSync() const; [[nodiscard]] bool isEpsilon() const; - productionsVector getProductions(); + ProductionsVector getProductions(); }; From 9f253e28ac4ca7ca40c70bd370738250ce6b648f Mon Sep 17 00:00:00 2001 From: Zyad Samy Date: Thu, 28 Dec 2023 13:12:37 +0200 Subject: [PATCH 08/41] Add error handling and traces to parsing --- Google_tests/CMakeLists.txt | 1 + Google_tests/GrammarExamples/1 | 4 + Google_tests/ParserTest.cpp | 75 ++++--- ParserPhase/GrammarParser/Grammar.h | 4 +- ParserPhase/PredictiveParser/CMakeLists.txt | 4 + ParserPhase/PredictiveParser/Parser.cpp | 189 ++++++++++++------ ParserPhase/PredictiveParser/Parser.h | 8 +- .../PredictiveParser/ParsingResult.cpp | 13 ++ ParserPhase/PredictiveParser/ParsingResult.h | 16 ++ .../PredictiveParser/ParsingTableEntry.cpp | 8 +- .../PredictiveParser/ParsingTableEntry.h | 8 +- ParserPhase/PredictiveParser/ParsingTrace.cpp | 44 ++++ ParserPhase/PredictiveParser/ParsingTrace.h | 24 +++ ParserPhase/PredictiveParser/ParsingTree.cpp | 9 +- ParserPhase/PredictiveParser/ParsingTree.h | 1 + main.cpp | 6 +- 16 files changed, 304 insertions(+), 110 deletions(-) create mode 100644 Google_tests/GrammarExamples/1 create mode 100644 ParserPhase/PredictiveParser/ParsingResult.cpp create mode 100644 ParserPhase/PredictiveParser/ParsingResult.h create mode 100644 ParserPhase/PredictiveParser/ParsingTrace.cpp create mode 100644 ParserPhase/PredictiveParser/ParsingTrace.h diff --git a/Google_tests/CMakeLists.txt b/Google_tests/CMakeLists.txt index 54b7f48..305f9e3 100644 --- a/Google_tests/CMakeLists.txt +++ b/Google_tests/CMakeLists.txt @@ -20,3 +20,4 @@ target_link_libraries(Google_Tests_run NFAConverter_lib) target_link_libraries(Google_Tests_run DFAConverter_lib) target_link_libraries(Google_Tests_run SymbolTableGenerator_lib) target_link_libraries(Google_Tests_run GrammarParser_lib) +target_link_libraries(Google_Tests_run PredictiveParser_lib) diff --git a/Google_tests/GrammarExamples/1 b/Google_tests/GrammarExamples/1 new file mode 100644 index 0000000..8e48fb5 --- /dev/null +++ b/Google_tests/GrammarExamples/1 @@ -0,0 +1,4 @@ +# Expr ::= Term ExprPrime +# ExprPrime ::= '+' Term ExprPrime | '-' Term ExprPrime | \L +# Term ::= Factor '*' Term | Factor '/' Term | Factor +# Factor ::= '(' Expr ')' | 'num' | 'iden' \ No newline at end of file diff --git a/Google_tests/ParserTest.cpp b/Google_tests/ParserTest.cpp index a892a2a..cb3d22a 100644 --- a/Google_tests/ParserTest.cpp +++ b/Google_tests/ParserTest.cpp @@ -1,51 +1,44 @@ #include +// #include "../ParserPhase/PredictiveParser/ParsingTree.h" +#include "../ParserPhase/Common/NonTerminal.h" +#include "../ParserPhase/Common/Terminal.h" +#include "../ParserPhase/Common/Symbol.h" +#include "../ParserPhase/GrammarParser/GrammarConverter.h" +#include "../ParserPhase/GrammarParser/Grammar.h" +#include "../ParserPhase/PredictiveParser/Token.h" #include "../ParserPhase/PredictiveParser/Parser.h" +#include "../ParserPhase/PredictiveParser/ParsingTree.h" +#include "../ParserPhase/PredictiveParser/ParsingResult.h" -#define MULTILINE(...) #__VA_ARGS__ +std::string g1Path = "../../GrammarTest.txt"; -// Define a fixture for the parser tests -// class ParserTest : public ::testing::Test { -// protected: -// // Declare variables needed for the tests -// Parser parser; -// }; - -// Test case for parsing valid input TEST(ParserTest, ValidInputParsing) { - // Define your tokens and grammar for valid input - std::vector tokens = { - new Token("ident","x"), - new Token("+","+"), - new Token("num","5"), - new Token("*","*"), - new Token("ident","y"), + std::vector input = { + Token("a","a"), + Token("b","b"), + Token("b","b"), + Token("a","a"), }; - std::string grammarString = - "Expr -> Term + Expr | Term - Expr | Term \ - Term -> Factor * Term | Factor / Term | Factor \ - Factor -> (Expr) | Number \ - Number -> [0-9]+"; + // S -> aBa + // B -> bB | eps + NonTerminal* S = new NonTerminal("S"); + NonTerminal* B = new NonTerminal("B"); + Terminal* a = new Terminal("a"); + Terminal* b = new Terminal("b"); + std::vector S_production_1 = {a,B,a}; + std::vector B_production_1 = {b,B}; - // Grammar grammar = /* define your grammar */; - - // Parse the input - // ParsingTree tree = parser.parse(tokens, grammar); - - // Assert that the parsing tree is constructed correctly - // ASSERT_TRUE(/* perform assertions on the parsing tree */); + std::unordered_map, ParsingTableEntry, PairHash, PairEqual> parsingTable; + parsingTable[{S,a->getName()}] = ParsingTableEntry({a,B,a}); + parsingTable[{B,a->getName()}] = ParsingTableEntry("epsilon"); + parsingTable[{B,b->getName()}] = ParsingTableEntry({b,B}); + + Parser parser(S, parsingTable); + ParsingResult result = parser.parse(input); + result.tree.print(); + result.printTrace(); + + ASSERT_EQ(1,1); } - -// // Test case for parsing invalid input -// TEST_F(ParserTest, InvalidInputParsing) { -// // Define your tokens and grammar for invalid input -// std::vector tokens = /* define invalid tokens */; -// Grammar grammar = /* define your grammar */; - -// // Parse the invalid input -// ParsingTree tree = parser.parse(tokens, grammar); - -// // Assert that the parsing tree is not constructed or is invalid -// ASSERT_FALSE(/* perform assertions on the parsing tree */); -// } diff --git a/ParserPhase/GrammarParser/Grammar.h b/ParserPhase/GrammarParser/Grammar.h index 490d642..5bba26b 100644 --- a/ParserPhase/GrammarParser/Grammar.h +++ b/ParserPhase/GrammarParser/Grammar.h @@ -29,8 +29,10 @@ private : const std::vector &getStandardizedNonTerminals() const; void standardizeNonTerminals(); explicit Grammar(GrammarConverter modifiedGrammar); - static Terminal* epsilon; + + // for debugging purposes + explicit Grammar(){}; }; diff --git a/ParserPhase/PredictiveParser/CMakeLists.txt b/ParserPhase/PredictiveParser/CMakeLists.txt index 5d42278..f076cd4 100644 --- a/ParserPhase/PredictiveParser/CMakeLists.txt +++ b/ParserPhase/PredictiveParser/CMakeLists.txt @@ -4,6 +4,8 @@ set(HEADER_FILES Common.h Parser.h ParsingTree.h + ParsingTrace.h + ParsingResult.h Token.h ParsingTableEntry.h) @@ -12,6 +14,8 @@ set(HEADER_FILES set(SOURCE_FILES Parser.cpp ParsingTree.cpp + ParsingTrace.cpp + ParsingResult.cpp ParsingTableEntry.cpp) diff --git a/ParserPhase/PredictiveParser/Parser.cpp b/ParserPhase/PredictiveParser/Parser.cpp index ae9d354..d42c721 100644 --- a/ParserPhase/PredictiveParser/Parser.cpp +++ b/ParserPhase/PredictiveParser/Parser.cpp @@ -3,99 +3,176 @@ // #include "Parser.h" +#include "ParsingTrace.h" +#include "ParsingResult.h" #include "../FirstAndFollowGenerator/FollowSetsGenerator.h" #include "../FirstAndFollowGenerator/FirstSetsGenerator.h" -Parser::Parser(Grammar& cfg): grammar(cfg), NTs(cfg.getStandardizedNonTerminals()){ +Parser::Parser(Grammar &cfg) : grammar(cfg), NTs(cfg.getStandardizedNonTerminals()) +{ computeNTsWithFirstSet(); computeNTsWithFollowSet(); constructParseTable(); + startingSymbol = cfg.getStartSymbol(); } -Token& nextToken(std::vector& input, int& index) { +Parser::Parser(Symbol *symbol, std::unordered_map, ParsingTableEntry, PairHash, PairEqual> &pt) + : parsingTable(pt), startingSymbol(symbol) {} + +Token &nextToken(std::vector &input, int &index) +{ + // TODO: return epsilon if out of bound return input[index++]; -}; +} -ParsingTree Parser::parse(std::vector& input) { - if (input.size() == 0) - return ParsingTree(); - - Symbol* startSymbol = grammar.getStartSymbol(); - ParsingTreeNode* rootNode = new ParsingTreeNode(startSymbol); - - if (startSymbol->isTerminal()) +std::string productionString(Production &production, NonTerminal* nonTerminal); + +// TODO: handle case $S | $ | S => eps +ParsingResult Parser::parse(std::vector &input) +{ + if (input.size() == 0) + return ParsingResult(); + + Symbol *startSymbol = this->startingSymbol; + ParsingTreeNode *rootNode = new ParsingTreeNode(startSymbol); + + if (startSymbol->isTerminal()) throw std::invalid_argument("startSymbol cannot be a terminal"); - + int lookaheadIndex = 0; - Token& lookahead = nextToken(input, lookaheadIndex); + Token &lookahead = nextToken(input, lookaheadIndex); - std::stack stack; - std::stack nodes; + std::stack stack; + std::stack nodes; + std::vector traces; stack.push(startSymbol); nodes.push(rootNode); - - while (!stack.empty()) { - Symbol* currentSymbol = stack.top(); stack.pop(); - ParsingTreeNode* currentNode = nodes.top(); nodes.pop(); - if (currentSymbol->isTerminal()) { + while (!stack.empty()) + { + auto trace = ParsingTrace(stack, input, lookaheadIndex - 1); + + Symbol *currentSymbol = stack.top(); + stack.pop(); + ParsingTreeNode *currentNode = nodes.top(); + nodes.pop(); + + if (currentSymbol->isTerminal()) + { bool match = currentSymbol->getName() == lookahead.terminal; - if (!match) { - // TODO: handle error + if (!match) + { + auto err = "Error: missing " + currentSymbol->getName() + ", inserted"; + trace.setError(err); + traces.push_back(trace); + continue; } + traces.push_back(trace); lookahead = nextToken(input, lookaheadIndex); - } else { - NonTerminal* current = dynamic_cast(currentSymbol); - ParsingTableEntry entry = parsingTable[{current, lookahead.terminal}]; - - for (auto& production: entry.getProductions()) { - for (auto& symbol: production) { - stack.push(symbol); - - ParsingTreeNode* n = new ParsingTreeNode(symbol); - currentNode->addChild(n); - nodes.push(n); - } + } + else + { + NonTerminal *currentNonTerminal = dynamic_cast(currentSymbol); + + bool entryExists = parsingTable.contains({currentNonTerminal, lookahead.terminal}); + if (!entryExists) + { + auto err = "Error: (illegal " + currentSymbol->getName() + ") - discarded " + lookahead.terminal; + trace.setError(err); + traces.push_back(trace); + + lookahead = nextToken(input, lookaheadIndex); + stack.push(currentNonTerminal); + continue; + } + + ParsingTableEntry entry = parsingTable[{currentNonTerminal, lookahead.terminal}]; + if (entry.isSync()) + { + auto err = "Error: expected " + currentSymbol->getName(); + trace.setError(err); + traces.push_back(trace); + continue; + } + + Production production = entry.getProduction(); + trace.setResult(productionString(production, currentNonTerminal)); + traces.push_back(trace); + + if (entry.isEpsilon()) + continue; + + for (auto it = production.rbegin(); it != production.rend(); ++it) + { + Symbol *symbol = *it; + stack.push(symbol); + + ParsingTreeNode *n = new ParsingTreeNode(symbol); + currentNode->addChild(n); + nodes.push(n); } } } - - return ParsingTree(rootNode); + + auto tree = ParsingTree(rootNode); + return ParsingResult(tree, traces); } -void Parser::computeNTsWithFirstSet() { +void Parser::computeNTsWithFirstSet() +{ FirstSetsGenerator firstSG(NTs); NTs = firstSG.getNTsWithFirstSets(); } -void Parser::computeNTsWithFollowSet() { +void Parser::computeNTsWithFollowSet() +{ FollowSetsGenerator followSG(NTs, grammar.getStartSymbol()); NTs = followSG.getNTsWithFollowSets(); } -void Parser::constructParseTable() { - for (NonTerminal* nonTerminal: NTs){ - std::shared_ptr firstSet = nonTerminal->getFirstSet(); - std::shared_ptr followSet = nonTerminal->getFollowSet(); +void Parser::constructParseTable() +{ + // for (NonTerminal* nonTerminal: NTs){ + // std::shared_ptr firstSet = nonTerminal->getFirstSet(); + // std::shared_ptr followSet = nonTerminal->getFollowSet(); - for (Terminal* first: firstSet->getSet()){ - if (!first->isEpsilon()){ - ParsingTableEntry entry = ParsingTableEntry(nonTerminal->getProductions()); - parsingTable[std::pair(nonTerminal,first->getName())] = ParsingTableEntry(nonTerminal->getProductions()); - } + // for (Terminal* first: firstSet->getSet()){ + // if (!first->isEpsilon()){ + // ParsingTableEntry entry = ParsingTableEntry(nonTerminal->getProductions()); + // parsingTable[std::pair(nonTerminal,first->getName())] = entry; + // } - } + // } - for (Terminal* follow: followSet->getSet()){ - if (firstSet->hasNoEpsilon()) { - parsingTable[std::pair(nonTerminal,follow->getName())] = ParsingTableEntry("sync"); - } else { - parsingTable[std::pair(nonTerminal,follow->getName())] = ParsingTableEntry("epsilon"); + // for (Terminal* follow: followSet->getSet()){ + // if (firstSet->hasNoEpsilon()) { + // parsingTable[std::pair(nonTerminal,follow->getName())] = ParsingTableEntry("sync"); + // } else { + // parsingTable[std::pair(nonTerminal,follow->getName())] = ParsingTableEntry("epsilon"); - } - } + // } + // } - } + // } } + +std::string productionString(Production &production, NonTerminal* nonTerminal) +{ + std::stringstream ss; + ss << nonTerminal->getName(); + ss << " -> "; + + bool isEpsilonProduction = production.size() == 0; + + if (isEpsilonProduction) { + ss << "\u03B5"; + } else { + for (auto symbol : production) + { + ss << symbol->getName(); + } + } + return ss.str(); +} \ No newline at end of file diff --git a/ParserPhase/PredictiveParser/Parser.h b/ParserPhase/PredictiveParser/Parser.h index 6a1571a..8b42df3 100644 --- a/ParserPhase/PredictiveParser/Parser.h +++ b/ParserPhase/PredictiveParser/Parser.h @@ -9,10 +9,13 @@ #include "Common.h" #include "ParsingTree.h" +#include "ParsingResult.h" #include "ParsingTableEntry.h" #include "../Common/NonTerminal.h" #include "../GrammarParser/Grammar.h" +#include + struct PairHash { template std::size_t operator()(const std::pair& p) const { @@ -31,6 +34,7 @@ struct PairEqual { class Parser { private: + Symbol* startingSymbol; Grammar grammar; std::vector NTs; std::unordered_map, ParsingTableEntry, PairHash, PairEqual> parsingTable; @@ -39,7 +43,9 @@ class Parser { void constructParseTable(); public: Parser(Grammar& grammar); - ParsingTree parse(std::vector& input); + ParsingResult parse(std::vector& input); + // for debugging purposes + Parser(Symbol* symbol, std::unordered_map, ParsingTableEntry, PairHash, PairEqual>& parsingTable); }; #endif //COMPILER_PARSER_H diff --git a/ParserPhase/PredictiveParser/ParsingResult.cpp b/ParserPhase/PredictiveParser/ParsingResult.cpp new file mode 100644 index 0000000..93b9f90 --- /dev/null +++ b/ParserPhase/PredictiveParser/ParsingResult.cpp @@ -0,0 +1,13 @@ +#include "ParsingResult.h" + +ParsingResult::ParsingResult(ParsingTree& tr, std::vector& trc) + : tree(tr), traces(trc) {}; + +ParsingResult::ParsingResult() {}; + +void ParsingResult::printTrace() { + for (auto& trace: traces) { + std::cout << trace; + } + std::cout << std::endl; +} \ No newline at end of file diff --git a/ParserPhase/PredictiveParser/ParsingResult.h b/ParserPhase/PredictiveParser/ParsingResult.h new file mode 100644 index 0000000..83fbfdf --- /dev/null +++ b/ParserPhase/PredictiveParser/ParsingResult.h @@ -0,0 +1,16 @@ +#ifndef COMPILER_PARSING_RESULT_H +#define COMPILER_PARSING_RESULT_H + +#include "ParsingTree.h" +#include "ParsingTrace.h" + +class ParsingResult { +public: + ParsingResult(ParsingTree& tree, std::vector& traces); + ParsingResult(); + ParsingTree tree; + std::vector traces; + void printTrace(); +}; + +#endif \ No newline at end of file diff --git a/ParserPhase/PredictiveParser/ParsingTableEntry.cpp b/ParserPhase/PredictiveParser/ParsingTableEntry.cpp index 92ba0af..e519450 100644 --- a/ParserPhase/PredictiveParser/ParsingTableEntry.cpp +++ b/ParserPhase/PredictiveParser/ParsingTableEntry.cpp @@ -14,8 +14,8 @@ ParsingTableEntry::ParsingTableEntry(std::string type) { } } -ParsingTableEntry::ParsingTableEntry(ProductionsVector productions) { - this->productions = std::move(productions); +ParsingTableEntry::ParsingTableEntry(Production production) { + this->production = std::move(production); } bool ParsingTableEntry::isSync() const { @@ -26,7 +26,7 @@ bool ParsingTableEntry::isEpsilon() const { return this->epsilon; } -ProductionsVector ParsingTableEntry::getProductions() { - return this->productions; +Production ParsingTableEntry::getProduction() { + return this->production; } diff --git a/ParserPhase/PredictiveParser/ParsingTableEntry.h b/ParserPhase/PredictiveParser/ParsingTableEntry.h index ccb8610..6273b73 100644 --- a/ParserPhase/PredictiveParser/ParsingTableEntry.h +++ b/ParserPhase/PredictiveParser/ParsingTableEntry.h @@ -7,19 +7,21 @@ #include "Common.h" +typedef std::vector Production; + class ParsingTableEntry { private: bool epsilon = false; bool sync = false; - ProductionsVector productions; + Production production; public: ParsingTableEntry(); explicit ParsingTableEntry(std::string type); - explicit ParsingTableEntry(ProductionsVector productions); + explicit ParsingTableEntry(Production production); [[nodiscard]] bool isSync() const; [[nodiscard]] bool isEpsilon() const; - ProductionsVector getProductions(); + Production getProduction(); }; diff --git a/ParserPhase/PredictiveParser/ParsingTrace.cpp b/ParserPhase/PredictiveParser/ParsingTrace.cpp new file mode 100644 index 0000000..b9e4b8d --- /dev/null +++ b/ParserPhase/PredictiveParser/ParsingTrace.cpp @@ -0,0 +1,44 @@ +#include "ParsingTrace.h" + +ParsingTrace::ParsingTrace(std::stack s, std::vector& in, int l) + :stack(s), input(in), lookahead(l) {}; + +void ParsingTrace::setError(std::string err) { + this->err = err; +} + +void ParsingTrace::setResult(std::string result) { + this->result = result; +} + +void printStackTrace(std::ostream &os, std::stack stack) { + int n = stack.size(); + std::vector symbols(n); + + for (int i=n-1; i >= 0; i--) { + auto top = stack.top(); stack.pop(); + symbols[i] = top; + } + + for (int i=0; i < n; i++) { + os << symbols[i]->getName(); + } + + os << "\t\t"; +} + +void printInputTrace(std::ostream &os, std::vector input, int l) { + for (int i=l; i < input.size(); i++) { + os << input[i].lexeme; + } + os << "\t\t"; +} + +std::ostream &operator<<(std::ostream &os, const ParsingTrace& trace) { + printStackTrace(os, trace.stack); + printInputTrace(os, trace.input, trace.lookahead); + os << trace.err; + os << trace.result; + os << std::endl; + return os; +} \ No newline at end of file diff --git a/ParserPhase/PredictiveParser/ParsingTrace.h b/ParserPhase/PredictiveParser/ParsingTrace.h new file mode 100644 index 0000000..bf37f7d --- /dev/null +++ b/ParserPhase/PredictiveParser/ParsingTrace.h @@ -0,0 +1,24 @@ +#ifndef COMPILER_PARSING_TRACE_H +#define COMPILER_PARSING_TRACE_H + +#include "Common.h" +#include "Token.h" +#include "../Common/Symbol.h" + +class ParsingTrace +{ +private: + std::stack stack; + std::vector input; + int lookahead; + std::string err; + std::string result; + +public: + ParsingTrace(std::stack stack, std::vector &input, int lookahead); + void setError(std::string err); + void setResult(std::string result); + friend std::ostream &operator<<(std::ostream &os, const ParsingTrace &trace); +}; + +#endif \ No newline at end of file diff --git a/ParserPhase/PredictiveParser/ParsingTree.cpp b/ParserPhase/PredictiveParser/ParsingTree.cpp index fa43fcc..fb6482f 100644 --- a/ParserPhase/PredictiveParser/ParsingTree.cpp +++ b/ParserPhase/PredictiveParser/ParsingTree.cpp @@ -19,7 +19,7 @@ void ParsingTree::printNode(const std::string& prefix, ParsingTreeNode* node, bo std::cout << prefix; std::cout << (isLast ? "└──" : "├──"); - std::cout << node << std::endl; + std::cout << node->getSymbol()->getName() << std::endl; auto children = node->getChildren(); for (int i=0; i < children.size(); i++) { @@ -32,13 +32,18 @@ void ParsingTree::printNode(const std::string& prefix, ParsingTreeNode* node, bo ParsingTreeNode::ParsingTreeNode(Symbol* s) : symbol(s) {} void ParsingTreeNode::addChild(ParsingTreeNode* child) { - this->children.push_back(child); + // TODO: find a more efficient way to do this + this->children.insert(children.begin(), child); } bool ParsingTreeNode::isLeaf() { return this->children.empty(); } +Symbol* ParsingTreeNode::getSymbol() { + return this->symbol; +} + std::vector ParsingTreeNode::getChildren() { return this->children; } \ No newline at end of file diff --git a/ParserPhase/PredictiveParser/ParsingTree.h b/ParserPhase/PredictiveParser/ParsingTree.h index 363a8e1..8a763a7 100644 --- a/ParserPhase/PredictiveParser/ParsingTree.h +++ b/ParserPhase/PredictiveParser/ParsingTree.h @@ -17,6 +17,7 @@ class ParsingTreeNode { void addChild(ParsingTreeNode* child); std::vector getChildren(); bool isLeaf(); + Symbol* getSymbol(); }; class ParsingTree { diff --git a/main.cpp b/main.cpp index ae1024f..8a1c0f9 100644 --- a/main.cpp +++ b/main.cpp @@ -10,6 +10,8 @@ #include "ParserPhase/FirstAndFollowGenerator/FirstSetsGenerator.h" #include "ParserPhase/FirstAndFollowGenerator/FollowSetsGenerator.h" + +int main(int argc, char *argv[]) { if (argc != 2) { std::cerr << "Wrong number of parameter (One argument required: Rules File Path)" @@ -47,9 +49,9 @@ std::cout << "\n\n\n"; std::cout << grammar; - +// FirstSetsGenerator firstSG(grammar.getStandardizedNonTerminals()); - FollowSetsGenerator followSG(firstSG.getNTsWithFirstSets(),grammar.getStartSymbol()); +// FollowSetsGenerator followSG(firstSG.getNTsWithFirstSets(),grammar.getStartSymbol()); // std::vector standardizedGrammar = grammar.getStandardizedNonTerminals(); // NonTerminal declaration = standardizedGrammar[3]; From 1d661d6aaf792aa1f0151637577b36e4c8b59c43 Mon Sep 17 00:00:00 2001 From: Zyad Samy Date: Thu, 28 Dec 2023 13:44:01 +0200 Subject: [PATCH 09/41] handle when the input is empty but the stack isn't while parsing --- ParserPhase/PredictiveParser/Parser.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/ParserPhase/PredictiveParser/Parser.cpp b/ParserPhase/PredictiveParser/Parser.cpp index d42c721..5692fd5 100644 --- a/ParserPhase/PredictiveParser/Parser.cpp +++ b/ParserPhase/PredictiveParser/Parser.cpp @@ -21,7 +21,10 @@ Parser::Parser(Symbol *symbol, std::unordered_map &input, int &index) { - // TODO: return epsilon if out of bound + if (index >= input.size()) { + auto endToken = new Token("$",""); + return *endToken; + } return input[index++]; } From 2e7b3c7449b007064c9f47e68aa0b761baabe486 Mon Sep 17 00:00:00 2001 From: Zyad Samy Date: Thu, 28 Dec 2023 13:49:46 +0200 Subject: [PATCH 10/41] handle when not all inputs are consumed while parsing --- ParserPhase/PredictiveParser/Parser.cpp | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/ParserPhase/PredictiveParser/Parser.cpp b/ParserPhase/PredictiveParser/Parser.cpp index 5692fd5..8640977 100644 --- a/ParserPhase/PredictiveParser/Parser.cpp +++ b/ParserPhase/PredictiveParser/Parser.cpp @@ -81,7 +81,7 @@ ParsingResult Parser::parse(std::vector &input) bool entryExists = parsingTable.contains({currentNonTerminal, lookahead.terminal}); if (!entryExists) { - auto err = "Error: (illegal " + currentSymbol->getName() + ") - discarded " + lookahead.terminal; + auto err = "Error: (illegal " + currentSymbol->getName() + ") - discarded " + lookahead.lexeme; trace.setError(err); traces.push_back(trace); @@ -117,6 +117,13 @@ ParsingResult Parser::parse(std::vector &input) } } } + + if (lookaheadIndex < input.size()) { + auto trace = ParsingTrace(stack, input, lookaheadIndex); + auto err = "Finished parsing but found unexpected tokens after " + input[lookaheadIndex-1].lexeme; + trace.setError(err); + traces.push_back(trace); + } auto tree = ParsingTree(rootNode); return ParsingResult(tree, traces); From c422b4493599fb8d10bb0ae87b4b95b4325ab2a4 Mon Sep 17 00:00:00 2001 From: Zyad Samy Date: Thu, 28 Dec 2023 14:59:49 +0200 Subject: [PATCH 11/41] parsing bug fixes --- ParserPhase/PredictiveParser/Parser.cpp | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/ParserPhase/PredictiveParser/Parser.cpp b/ParserPhase/PredictiveParser/Parser.cpp index 8640977..dbfa014 100644 --- a/ParserPhase/PredictiveParser/Parser.cpp +++ b/ParserPhase/PredictiveParser/Parser.cpp @@ -23,6 +23,7 @@ Token &nextToken(std::vector &input, int &index) { if (index >= input.size()) { auto endToken = new Token("$",""); + index++; return *endToken; } return input[index++]; @@ -30,7 +31,7 @@ Token &nextToken(std::vector &input, int &index) std::string productionString(Production &production, NonTerminal* nonTerminal); -// TODO: handle case $S | $ | S => eps + ParsingResult Parser::parse(std::vector &input) { if (input.size() == 0) @@ -79,14 +80,21 @@ ParsingResult Parser::parse(std::vector &input) NonTerminal *currentNonTerminal = dynamic_cast(currentSymbol); bool entryExists = parsingTable.contains({currentNonTerminal, lookahead.terminal}); - if (!entryExists) + if (!entryExists && lookahead.terminal == END) { + auto err = "Error: missing " + currentSymbol->getName(); + trace.setError(err); + traces.push_back(trace); + continue; + } + else if (!entryExists) { - auto err = "Error: (illegal " + currentSymbol->getName() + ") - discarded " + lookahead.lexeme; + auto err = "Error: (illegal " + currentSymbol->getName() + ") - discarded " + (!lookahead.lexeme.empty() ? lookahead.lexeme : lookahead.terminal); trace.setError(err); traces.push_back(trace); lookahead = nextToken(input, lookaheadIndex); stack.push(currentNonTerminal); + nodes.push(currentNode); continue; } @@ -117,13 +125,6 @@ ParsingResult Parser::parse(std::vector &input) } } } - - if (lookaheadIndex < input.size()) { - auto trace = ParsingTrace(stack, input, lookaheadIndex); - auto err = "Finished parsing but found unexpected tokens after " + input[lookaheadIndex-1].lexeme; - trace.setError(err); - traces.push_back(trace); - } auto tree = ParsingTree(rootNode); return ParsingResult(tree, traces); From 6a204a007b0db337b0f2e8cae8a4aa9e5b4630f2 Mon Sep 17 00:00:00 2001 From: Zyad Samy Date: Thu, 28 Dec 2023 15:02:05 +0200 Subject: [PATCH 12/41] add test coverage for parsing --- Google_tests/ParserTest.cpp | 179 +++++++++++++++++++++++++++++++++++- 1 file changed, 177 insertions(+), 2 deletions(-) diff --git a/Google_tests/ParserTest.cpp b/Google_tests/ParserTest.cpp index cb3d22a..6bd1d7e 100644 --- a/Google_tests/ParserTest.cpp +++ b/Google_tests/ParserTest.cpp @@ -12,7 +12,45 @@ std::string g1Path = "../../GrammarTest.txt"; -TEST(ParserTest, ValidInputParsing) { +std::pair, ParsingTableEntry, PairHash, PairEqual>> MathExprGrammar() { + // Lecture-Parsing-2 Example-2 p-26 + NonTerminal* E = new NonTerminal("E"); + NonTerminal* E_ = new NonTerminal("E'"); + NonTerminal* T = new NonTerminal("T"); + NonTerminal* T_ = new NonTerminal("T'"); + NonTerminal* F = new NonTerminal("F"); + Terminal* id = new Terminal("id"); + Terminal* plus = new Terminal("+"); + Terminal* mult = new Terminal("*"); + Terminal* openB = new Terminal("("); + Terminal* closeB = new Terminal(")"); + + std::vector Eprod = {T,E_}; + std::vector E_prod = {plus,T,E_}; + std::vector Tprod = {F,T_}; + std::vector T_prod = {mult,F,T_}; + std::vector Fprod1 = {openB,E,closeB}; + std::vector Fprod2 = {id}; + + std::unordered_map, ParsingTableEntry, PairHash, PairEqual> parsingTable; + parsingTable[{E,id->getName()}] = ParsingTableEntry(Eprod); + parsingTable[{T,id->getName()}] = ParsingTableEntry(Tprod); + parsingTable[{F,id->getName()}] = ParsingTableEntry(Fprod2); + parsingTable[{E_,plus->getName()}] = ParsingTableEntry(E_prod); + parsingTable[{T_,plus->getName()}] = ParsingTableEntry("epsilon"); + parsingTable[{T_,mult->getName()}] = ParsingTableEntry(T_prod); + parsingTable[{E,openB->getName()}] = ParsingTableEntry(Eprod); + parsingTable[{T,openB->getName()}] = ParsingTableEntry(Tprod); + parsingTable[{F,openB->getName()}] = ParsingTableEntry(Fprod1); + parsingTable[{E_,closeB->getName()}] = ParsingTableEntry("epsilon"); + parsingTable[{T_,closeB->getName()}] = ParsingTableEntry("epsilon"); + parsingTable[{E_,"$"}] = ParsingTableEntry("epsilon"); + parsingTable[{T_,"$"}] = ParsingTableEntry("epsilon"); + + return {E, parsingTable}; +} + +TEST(ParserManualTest, ValidInput) { std::vector input = { Token("a","a"), Token("b","b"), @@ -40,5 +78,142 @@ TEST(ParserTest, ValidInputParsing) { result.tree.print(); result.printTrace(); - ASSERT_EQ(1,1); + +} + +TEST(ParserManualTest, ValidInput2) { + std::vector input = { + Token("id","x"), + Token("+",""), + Token("id","y"), + }; + + auto g = MathExprGrammar(); + auto startSymbol = g.first; + auto parsingTable = g.second; + + Parser parser(startSymbol, parsingTable); + ParsingResult result = parser.parse(input); + result.tree.print(); + result.printTrace(); + + +} + +TEST(ParserManualTest, InvalidInputExcessToken) { + std::vector input = { + Token("(","("), + Token("id","x"), + Token("+",""), + Token("id","y"), + Token(")",")"), + Token("*","*"), + Token("id","z"), + Token("id","w"), + Token("id","w"), + }; + + auto g = MathExprGrammar(); + auto startSymbol = g.first; + auto parsingTable = g.second; + + Parser parser(startSymbol, parsingTable); + ParsingResult result = parser.parse(input); + result.tree.print(); + result.printTrace(); +} + +TEST(ParserManualTest, InvalidInputMissingToken) { + // P-46 left example + NonTerminal* S = new NonTerminal("S"); + NonTerminal* A = new NonTerminal("A"); + Terminal* a = new Terminal("a"); + Terminal* b = new Terminal("b"); + Terminal* c = new Terminal("c"); + Terminal* d = new Terminal("d"); + Terminal* e = new Terminal("e"); + + std::vector S_production_1 = {A,b,S}; + std::vector S_production_2 = {e}; + std::vector A_production_1 = {a}; + std::vector A_production_2 = {c,A,d}; + + std::unordered_map, ParsingTableEntry, PairHash, PairEqual> parsingTable; + parsingTable[{S,a->getName()}] = ParsingTableEntry(S_production_1); + parsingTable[{A,a->getName()}] = ParsingTableEntry(A_production_1); + parsingTable[{A,b->getName()}] = ParsingTableEntry("sync"); + parsingTable[{S,c->getName()}] = ParsingTableEntry(S_production_1); + parsingTable[{A,c->getName()}] = ParsingTableEntry(A_production_2); + parsingTable[{A,d->getName()}] = ParsingTableEntry("sync"); + parsingTable[{S,e->getName()}] = ParsingTableEntry(S_production_2); + parsingTable[{S,"$"}] = ParsingTableEntry("epsilon"); + + + std::vector input = { + Token("a","a"), + Token("a","a"), + Token("b","b") + }; + + Parser parser(S, parsingTable); + ParsingResult result = parser.parse(input); + result.tree.print(); + result.printTrace(); +} + +TEST(ParserManualTest, InvalidInputExcessToken2) { + // P-46 left example + NonTerminal* S = new NonTerminal("S"); + NonTerminal* A = new NonTerminal("A"); + Terminal* a = new Terminal("a"); + Terminal* b = new Terminal("b"); + Terminal* c = new Terminal("c"); + Terminal* d = new Terminal("d"); + Terminal* e = new Terminal("e"); + + std::vector S_production_1 = {A,b,S}; + std::vector S_production_2 = {e}; + std::vector A_production_1 = {a}; + std::vector A_production_2 = {c,A,d}; + + std::unordered_map, ParsingTableEntry, PairHash, PairEqual> parsingTable; + parsingTable[{S,a->getName()}] = ParsingTableEntry(S_production_1); + parsingTable[{A,a->getName()}] = ParsingTableEntry(A_production_1); + parsingTable[{A,b->getName()}] = ParsingTableEntry("sync"); + parsingTable[{S,c->getName()}] = ParsingTableEntry(S_production_1); + parsingTable[{A,c->getName()}] = ParsingTableEntry(A_production_2); + parsingTable[{A,d->getName()}] = ParsingTableEntry("sync"); + parsingTable[{S,e->getName()}] = ParsingTableEntry(S_production_2); + parsingTable[{S,"$"}] = ParsingTableEntry("epsilon"); + + + std::vector input = { + Token("c","c"), + Token("e","e"), + Token("a","a"), + Token("d","d"), + Token("b","b"), + }; + + Parser parser(S, parsingTable); + ParsingResult result = parser.parse(input); + result.tree.print(); + result.printTrace(); +} + +TEST(ParserManualTest, InvalidInputEmptyWhileStackNot) { + std::vector input = { + Token("id", "x"), + Token("+", ""), + // Token("id","y"), + }; + + auto g = MathExprGrammar(); + auto startSymbol = g.first; + auto parsingTable = g.second; + + Parser parser(startSymbol, parsingTable); + ParsingResult result = parser.parse(input); + result.tree.print(); + result.printTrace(); } From 988c0b90fac9b2764b65269e85f61aeb611c3346 Mon Sep 17 00:00:00 2001 From: Zyad Samy Date: Thu, 28 Dec 2023 15:38:16 +0200 Subject: [PATCH 13/41] add printing stack trace only --- ParserPhase/PredictiveParser/ParsingResult.cpp | 7 +++++++ ParserPhase/PredictiveParser/ParsingResult.h | 1 + ParserPhase/PredictiveParser/ParsingTrace.cpp | 9 ++++++--- ParserPhase/PredictiveParser/ParsingTrace.h | 1 + 4 files changed, 15 insertions(+), 3 deletions(-) diff --git a/ParserPhase/PredictiveParser/ParsingResult.cpp b/ParserPhase/PredictiveParser/ParsingResult.cpp index 93b9f90..c683d35 100644 --- a/ParserPhase/PredictiveParser/ParsingResult.cpp +++ b/ParserPhase/PredictiveParser/ParsingResult.cpp @@ -10,4 +10,11 @@ void ParsingResult::printTrace() { std::cout << trace; } std::cout << std::endl; +} + +void ParsingResult::printStackTrace() { + for (auto& trace: traces) { + trace.printStack(); + std::cout << std::endl; + } } \ No newline at end of file diff --git a/ParserPhase/PredictiveParser/ParsingResult.h b/ParserPhase/PredictiveParser/ParsingResult.h index 83fbfdf..c15cf46 100644 --- a/ParserPhase/PredictiveParser/ParsingResult.h +++ b/ParserPhase/PredictiveParser/ParsingResult.h @@ -11,6 +11,7 @@ class ParsingResult { ParsingTree tree; std::vector traces; void printTrace(); + void printStackTrace(); }; #endif \ No newline at end of file diff --git a/ParserPhase/PredictiveParser/ParsingTrace.cpp b/ParserPhase/PredictiveParser/ParsingTrace.cpp index b9e4b8d..2b4101b 100644 --- a/ParserPhase/PredictiveParser/ParsingTrace.cpp +++ b/ParserPhase/PredictiveParser/ParsingTrace.cpp @@ -23,20 +23,23 @@ void printStackTrace(std::ostream &os, std::stack stack) { for (int i=0; i < n; i++) { os << symbols[i]->getName(); } - - os << "\t\t"; } void printInputTrace(std::ostream &os, std::vector input, int l) { for (int i=l; i < input.size(); i++) { os << input[i].lexeme; } - os << "\t\t"; +} + +void ParsingTrace::printStack() { + printStackTrace(std::cout, this->stack); } std::ostream &operator<<(std::ostream &os, const ParsingTrace& trace) { printStackTrace(os, trace.stack); + os << "\t\t"; printInputTrace(os, trace.input, trace.lookahead); + os << "\t\t"; os << trace.err; os << trace.result; os << std::endl; diff --git a/ParserPhase/PredictiveParser/ParsingTrace.h b/ParserPhase/PredictiveParser/ParsingTrace.h index bf37f7d..3f936b9 100644 --- a/ParserPhase/PredictiveParser/ParsingTrace.h +++ b/ParserPhase/PredictiveParser/ParsingTrace.h @@ -18,6 +18,7 @@ class ParsingTrace ParsingTrace(std::stack stack, std::vector &input, int lookahead); void setError(std::string err); void setResult(std::string result); + void printStack(); friend std::ostream &operator<<(std::ostream &os, const ParsingTrace &trace); }; From bee1f5175049f39ffe5fe5332161694956c26d4a Mon Sep 17 00:00:00 2001 From: Zyad Samy Date: Thu, 28 Dec 2023 16:08:51 +0200 Subject: [PATCH 14/41] rename `ParserPhase` back to `SyntaxPhase` --- CMakeLists.txt | 2 +- Google_tests/GrammarConverterTest.cpp | 2 +- Google_tests/ParserTest.cpp | 20 +++++++++---------- {ParserPhase => SyntaxPhase}/CMakeLists.txt | 0 .../Common/CMakeLists.txt | 0 .../Common/NonTerminal.cpp | 0 .../Common/NonTerminal.h | 0 .../Common/Symbol.cpp | 0 {ParserPhase => SyntaxPhase}/Common/Symbol.h | 0 .../Common/Terminal.cpp | 0 .../Common/Terminal.h | 0 .../FirstAndFollowGenerator/CMakeLists.txt | 0 .../FirstAndFollowGenerator/FirstSet.cpp | 0 .../FirstAndFollowGenerator/FirstSet.h | 0 .../FirstSetsGenerator.cpp | 0 .../FirstSetsGenerator.h | 0 .../FirstAndFollowGenerator/FollowSet.cpp | 0 .../FirstAndFollowGenerator/FollowSet.h | 0 .../FollowSetsGenerator.cpp | 0 .../FollowSetsGenerator.h | 0 .../FirstAndFollowGenerator/ParserSet.cpp | 0 .../FirstAndFollowGenerator/ParserSet.h | 0 .../GrammarParser/CMakeLists.txt | 0 .../GrammarParser/Grammar.cpp | 0 .../GrammarParser/Grammar.h | 0 .../GrammarParser/GrammarConverter.cpp | 0 .../GrammarParser/GrammarConverter.h | 0 .../GrammarParser/NonTerminalSymbol.cpp | 0 .../GrammarParser/NonTerminalSymbol.h | 0 .../PredictiveParser/CMakeLists.txt | 0 .../PredictiveParser/Common.h | 0 .../PredictiveParser/Parser.cpp | 0 .../PredictiveParser/Parser.h | 0 .../PredictiveParser/ParsingResult.cpp | 0 .../PredictiveParser/ParsingResult.h | 0 .../PredictiveParser/ParsingTableEntry.cpp | 0 .../PredictiveParser/ParsingTableEntry.h | 0 .../PredictiveParser/ParsingTrace.cpp | 0 .../PredictiveParser/ParsingTrace.h | 0 .../PredictiveParser/ParsingTree.cpp | 0 .../PredictiveParser/ParsingTree.h | 0 .../PredictiveParser/Token.h | 0 main.cpp | 8 ++++---- 43 files changed, 16 insertions(+), 16 deletions(-) rename {ParserPhase => SyntaxPhase}/CMakeLists.txt (100%) rename {ParserPhase => SyntaxPhase}/Common/CMakeLists.txt (100%) rename {ParserPhase => SyntaxPhase}/Common/NonTerminal.cpp (100%) rename {ParserPhase => SyntaxPhase}/Common/NonTerminal.h (100%) rename {ParserPhase => SyntaxPhase}/Common/Symbol.cpp (100%) rename {ParserPhase => SyntaxPhase}/Common/Symbol.h (100%) rename {ParserPhase => SyntaxPhase}/Common/Terminal.cpp (100%) rename {ParserPhase => SyntaxPhase}/Common/Terminal.h (100%) rename {ParserPhase => SyntaxPhase}/FirstAndFollowGenerator/CMakeLists.txt (100%) rename {ParserPhase => SyntaxPhase}/FirstAndFollowGenerator/FirstSet.cpp (100%) rename {ParserPhase => SyntaxPhase}/FirstAndFollowGenerator/FirstSet.h (100%) rename {ParserPhase => SyntaxPhase}/FirstAndFollowGenerator/FirstSetsGenerator.cpp (100%) rename {ParserPhase => SyntaxPhase}/FirstAndFollowGenerator/FirstSetsGenerator.h (100%) rename {ParserPhase => SyntaxPhase}/FirstAndFollowGenerator/FollowSet.cpp (100%) rename {ParserPhase => SyntaxPhase}/FirstAndFollowGenerator/FollowSet.h (100%) rename {ParserPhase => SyntaxPhase}/FirstAndFollowGenerator/FollowSetsGenerator.cpp (100%) rename {ParserPhase => SyntaxPhase}/FirstAndFollowGenerator/FollowSetsGenerator.h (100%) rename {ParserPhase => SyntaxPhase}/FirstAndFollowGenerator/ParserSet.cpp (100%) rename {ParserPhase => SyntaxPhase}/FirstAndFollowGenerator/ParserSet.h (100%) rename {ParserPhase => SyntaxPhase}/GrammarParser/CMakeLists.txt (100%) rename {ParserPhase => SyntaxPhase}/GrammarParser/Grammar.cpp (100%) rename {ParserPhase => SyntaxPhase}/GrammarParser/Grammar.h (100%) rename {ParserPhase => SyntaxPhase}/GrammarParser/GrammarConverter.cpp (100%) rename {ParserPhase => SyntaxPhase}/GrammarParser/GrammarConverter.h (100%) rename {ParserPhase => SyntaxPhase}/GrammarParser/NonTerminalSymbol.cpp (100%) rename {ParserPhase => SyntaxPhase}/GrammarParser/NonTerminalSymbol.h (100%) rename {ParserPhase => SyntaxPhase}/PredictiveParser/CMakeLists.txt (100%) rename {ParserPhase => SyntaxPhase}/PredictiveParser/Common.h (100%) rename {ParserPhase => SyntaxPhase}/PredictiveParser/Parser.cpp (100%) rename {ParserPhase => SyntaxPhase}/PredictiveParser/Parser.h (100%) rename {ParserPhase => SyntaxPhase}/PredictiveParser/ParsingResult.cpp (100%) rename {ParserPhase => SyntaxPhase}/PredictiveParser/ParsingResult.h (100%) rename {ParserPhase => SyntaxPhase}/PredictiveParser/ParsingTableEntry.cpp (100%) rename {ParserPhase => SyntaxPhase}/PredictiveParser/ParsingTableEntry.h (100%) rename {ParserPhase => SyntaxPhase}/PredictiveParser/ParsingTrace.cpp (100%) rename {ParserPhase => SyntaxPhase}/PredictiveParser/ParsingTrace.h (100%) rename {ParserPhase => SyntaxPhase}/PredictiveParser/ParsingTree.cpp (100%) rename {ParserPhase => SyntaxPhase}/PredictiveParser/ParsingTree.h (100%) rename {ParserPhase => SyntaxPhase}/PredictiveParser/Token.h (100%) diff --git a/CMakeLists.txt b/CMakeLists.txt index 5aa248d..bd309c4 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -17,6 +17,6 @@ target_link_libraries(Compiler PredictiveParser_lib) add_subdirectory(Util) add_subdirectory(LexicalPhase) -add_subdirectory(ParserPhase) +add_subdirectory(SyntaxPhase) add_subdirectory(Google_tests) diff --git a/Google_tests/GrammarConverterTest.cpp b/Google_tests/GrammarConverterTest.cpp index 7373995..954b8ed 100644 --- a/Google_tests/GrammarConverterTest.cpp +++ b/Google_tests/GrammarConverterTest.cpp @@ -2,7 +2,7 @@ // Created by Meniem on 19-Dec-23. // #include "gtest/gtest.h" -#include "../ParserPhase/GrammarParser/GrammarConverter.h" +#include "../SyntaxPhase/GrammarParser/GrammarConverter.h" TEST(ValidateGrammar, HandlesNoSeperator){ std::string str = "METHOD_BODY STATEMENT_LIST"; diff --git a/Google_tests/ParserTest.cpp b/Google_tests/ParserTest.cpp index 6bd1d7e..6570e3e 100644 --- a/Google_tests/ParserTest.cpp +++ b/Google_tests/ParserTest.cpp @@ -1,14 +1,14 @@ #include -// #include "../ParserPhase/PredictiveParser/ParsingTree.h" -#include "../ParserPhase/Common/NonTerminal.h" -#include "../ParserPhase/Common/Terminal.h" -#include "../ParserPhase/Common/Symbol.h" -#include "../ParserPhase/GrammarParser/GrammarConverter.h" -#include "../ParserPhase/GrammarParser/Grammar.h" -#include "../ParserPhase/PredictiveParser/Token.h" -#include "../ParserPhase/PredictiveParser/Parser.h" -#include "../ParserPhase/PredictiveParser/ParsingTree.h" -#include "../ParserPhase/PredictiveParser/ParsingResult.h" +// #include "../SyntaxPhase/PredictiveParser/ParsingTree.h" +#include "../SyntaxPhase/Common/NonTerminal.h" +#include "../SyntaxPhase/Common/Terminal.h" +#include "../SyntaxPhase/Common/Symbol.h" +#include "../SyntaxPhase/GrammarParser/GrammarConverter.h" +#include "../SyntaxPhase/GrammarParser/Grammar.h" +#include "../SyntaxPhase/PredictiveParser/Token.h" +#include "../SyntaxPhase/PredictiveParser/Parser.h" +#include "../SyntaxPhase/PredictiveParser/ParsingTree.h" +#include "../SyntaxPhase/PredictiveParser/ParsingResult.h" std::string g1Path = "../../GrammarTest.txt"; diff --git a/ParserPhase/CMakeLists.txt b/SyntaxPhase/CMakeLists.txt similarity index 100% rename from ParserPhase/CMakeLists.txt rename to SyntaxPhase/CMakeLists.txt diff --git a/ParserPhase/Common/CMakeLists.txt b/SyntaxPhase/Common/CMakeLists.txt similarity index 100% rename from ParserPhase/Common/CMakeLists.txt rename to SyntaxPhase/Common/CMakeLists.txt diff --git a/ParserPhase/Common/NonTerminal.cpp b/SyntaxPhase/Common/NonTerminal.cpp similarity index 100% rename from ParserPhase/Common/NonTerminal.cpp rename to SyntaxPhase/Common/NonTerminal.cpp diff --git a/ParserPhase/Common/NonTerminal.h b/SyntaxPhase/Common/NonTerminal.h similarity index 100% rename from ParserPhase/Common/NonTerminal.h rename to SyntaxPhase/Common/NonTerminal.h diff --git a/ParserPhase/Common/Symbol.cpp b/SyntaxPhase/Common/Symbol.cpp similarity index 100% rename from ParserPhase/Common/Symbol.cpp rename to SyntaxPhase/Common/Symbol.cpp diff --git a/ParserPhase/Common/Symbol.h b/SyntaxPhase/Common/Symbol.h similarity index 100% rename from ParserPhase/Common/Symbol.h rename to SyntaxPhase/Common/Symbol.h diff --git a/ParserPhase/Common/Terminal.cpp b/SyntaxPhase/Common/Terminal.cpp similarity index 100% rename from ParserPhase/Common/Terminal.cpp rename to SyntaxPhase/Common/Terminal.cpp diff --git a/ParserPhase/Common/Terminal.h b/SyntaxPhase/Common/Terminal.h similarity index 100% rename from ParserPhase/Common/Terminal.h rename to SyntaxPhase/Common/Terminal.h diff --git a/ParserPhase/FirstAndFollowGenerator/CMakeLists.txt b/SyntaxPhase/FirstAndFollowGenerator/CMakeLists.txt similarity index 100% rename from ParserPhase/FirstAndFollowGenerator/CMakeLists.txt rename to SyntaxPhase/FirstAndFollowGenerator/CMakeLists.txt diff --git a/ParserPhase/FirstAndFollowGenerator/FirstSet.cpp b/SyntaxPhase/FirstAndFollowGenerator/FirstSet.cpp similarity index 100% rename from ParserPhase/FirstAndFollowGenerator/FirstSet.cpp rename to SyntaxPhase/FirstAndFollowGenerator/FirstSet.cpp diff --git a/ParserPhase/FirstAndFollowGenerator/FirstSet.h b/SyntaxPhase/FirstAndFollowGenerator/FirstSet.h similarity index 100% rename from ParserPhase/FirstAndFollowGenerator/FirstSet.h rename to SyntaxPhase/FirstAndFollowGenerator/FirstSet.h diff --git a/ParserPhase/FirstAndFollowGenerator/FirstSetsGenerator.cpp b/SyntaxPhase/FirstAndFollowGenerator/FirstSetsGenerator.cpp similarity index 100% rename from ParserPhase/FirstAndFollowGenerator/FirstSetsGenerator.cpp rename to SyntaxPhase/FirstAndFollowGenerator/FirstSetsGenerator.cpp diff --git a/ParserPhase/FirstAndFollowGenerator/FirstSetsGenerator.h b/SyntaxPhase/FirstAndFollowGenerator/FirstSetsGenerator.h similarity index 100% rename from ParserPhase/FirstAndFollowGenerator/FirstSetsGenerator.h rename to SyntaxPhase/FirstAndFollowGenerator/FirstSetsGenerator.h diff --git a/ParserPhase/FirstAndFollowGenerator/FollowSet.cpp b/SyntaxPhase/FirstAndFollowGenerator/FollowSet.cpp similarity index 100% rename from ParserPhase/FirstAndFollowGenerator/FollowSet.cpp rename to SyntaxPhase/FirstAndFollowGenerator/FollowSet.cpp diff --git a/ParserPhase/FirstAndFollowGenerator/FollowSet.h b/SyntaxPhase/FirstAndFollowGenerator/FollowSet.h similarity index 100% rename from ParserPhase/FirstAndFollowGenerator/FollowSet.h rename to SyntaxPhase/FirstAndFollowGenerator/FollowSet.h diff --git a/ParserPhase/FirstAndFollowGenerator/FollowSetsGenerator.cpp b/SyntaxPhase/FirstAndFollowGenerator/FollowSetsGenerator.cpp similarity index 100% rename from ParserPhase/FirstAndFollowGenerator/FollowSetsGenerator.cpp rename to SyntaxPhase/FirstAndFollowGenerator/FollowSetsGenerator.cpp diff --git a/ParserPhase/FirstAndFollowGenerator/FollowSetsGenerator.h b/SyntaxPhase/FirstAndFollowGenerator/FollowSetsGenerator.h similarity index 100% rename from ParserPhase/FirstAndFollowGenerator/FollowSetsGenerator.h rename to SyntaxPhase/FirstAndFollowGenerator/FollowSetsGenerator.h diff --git a/ParserPhase/FirstAndFollowGenerator/ParserSet.cpp b/SyntaxPhase/FirstAndFollowGenerator/ParserSet.cpp similarity index 100% rename from ParserPhase/FirstAndFollowGenerator/ParserSet.cpp rename to SyntaxPhase/FirstAndFollowGenerator/ParserSet.cpp diff --git a/ParserPhase/FirstAndFollowGenerator/ParserSet.h b/SyntaxPhase/FirstAndFollowGenerator/ParserSet.h similarity index 100% rename from ParserPhase/FirstAndFollowGenerator/ParserSet.h rename to SyntaxPhase/FirstAndFollowGenerator/ParserSet.h diff --git a/ParserPhase/GrammarParser/CMakeLists.txt b/SyntaxPhase/GrammarParser/CMakeLists.txt similarity index 100% rename from ParserPhase/GrammarParser/CMakeLists.txt rename to SyntaxPhase/GrammarParser/CMakeLists.txt diff --git a/ParserPhase/GrammarParser/Grammar.cpp b/SyntaxPhase/GrammarParser/Grammar.cpp similarity index 100% rename from ParserPhase/GrammarParser/Grammar.cpp rename to SyntaxPhase/GrammarParser/Grammar.cpp diff --git a/ParserPhase/GrammarParser/Grammar.h b/SyntaxPhase/GrammarParser/Grammar.h similarity index 100% rename from ParserPhase/GrammarParser/Grammar.h rename to SyntaxPhase/GrammarParser/Grammar.h diff --git a/ParserPhase/GrammarParser/GrammarConverter.cpp b/SyntaxPhase/GrammarParser/GrammarConverter.cpp similarity index 100% rename from ParserPhase/GrammarParser/GrammarConverter.cpp rename to SyntaxPhase/GrammarParser/GrammarConverter.cpp diff --git a/ParserPhase/GrammarParser/GrammarConverter.h b/SyntaxPhase/GrammarParser/GrammarConverter.h similarity index 100% rename from ParserPhase/GrammarParser/GrammarConverter.h rename to SyntaxPhase/GrammarParser/GrammarConverter.h diff --git a/ParserPhase/GrammarParser/NonTerminalSymbol.cpp b/SyntaxPhase/GrammarParser/NonTerminalSymbol.cpp similarity index 100% rename from ParserPhase/GrammarParser/NonTerminalSymbol.cpp rename to SyntaxPhase/GrammarParser/NonTerminalSymbol.cpp diff --git a/ParserPhase/GrammarParser/NonTerminalSymbol.h b/SyntaxPhase/GrammarParser/NonTerminalSymbol.h similarity index 100% rename from ParserPhase/GrammarParser/NonTerminalSymbol.h rename to SyntaxPhase/GrammarParser/NonTerminalSymbol.h diff --git a/ParserPhase/PredictiveParser/CMakeLists.txt b/SyntaxPhase/PredictiveParser/CMakeLists.txt similarity index 100% rename from ParserPhase/PredictiveParser/CMakeLists.txt rename to SyntaxPhase/PredictiveParser/CMakeLists.txt diff --git a/ParserPhase/PredictiveParser/Common.h b/SyntaxPhase/PredictiveParser/Common.h similarity index 100% rename from ParserPhase/PredictiveParser/Common.h rename to SyntaxPhase/PredictiveParser/Common.h diff --git a/ParserPhase/PredictiveParser/Parser.cpp b/SyntaxPhase/PredictiveParser/Parser.cpp similarity index 100% rename from ParserPhase/PredictiveParser/Parser.cpp rename to SyntaxPhase/PredictiveParser/Parser.cpp diff --git a/ParserPhase/PredictiveParser/Parser.h b/SyntaxPhase/PredictiveParser/Parser.h similarity index 100% rename from ParserPhase/PredictiveParser/Parser.h rename to SyntaxPhase/PredictiveParser/Parser.h diff --git a/ParserPhase/PredictiveParser/ParsingResult.cpp b/SyntaxPhase/PredictiveParser/ParsingResult.cpp similarity index 100% rename from ParserPhase/PredictiveParser/ParsingResult.cpp rename to SyntaxPhase/PredictiveParser/ParsingResult.cpp diff --git a/ParserPhase/PredictiveParser/ParsingResult.h b/SyntaxPhase/PredictiveParser/ParsingResult.h similarity index 100% rename from ParserPhase/PredictiveParser/ParsingResult.h rename to SyntaxPhase/PredictiveParser/ParsingResult.h diff --git a/ParserPhase/PredictiveParser/ParsingTableEntry.cpp b/SyntaxPhase/PredictiveParser/ParsingTableEntry.cpp similarity index 100% rename from ParserPhase/PredictiveParser/ParsingTableEntry.cpp rename to SyntaxPhase/PredictiveParser/ParsingTableEntry.cpp diff --git a/ParserPhase/PredictiveParser/ParsingTableEntry.h b/SyntaxPhase/PredictiveParser/ParsingTableEntry.h similarity index 100% rename from ParserPhase/PredictiveParser/ParsingTableEntry.h rename to SyntaxPhase/PredictiveParser/ParsingTableEntry.h diff --git a/ParserPhase/PredictiveParser/ParsingTrace.cpp b/SyntaxPhase/PredictiveParser/ParsingTrace.cpp similarity index 100% rename from ParserPhase/PredictiveParser/ParsingTrace.cpp rename to SyntaxPhase/PredictiveParser/ParsingTrace.cpp diff --git a/ParserPhase/PredictiveParser/ParsingTrace.h b/SyntaxPhase/PredictiveParser/ParsingTrace.h similarity index 100% rename from ParserPhase/PredictiveParser/ParsingTrace.h rename to SyntaxPhase/PredictiveParser/ParsingTrace.h diff --git a/ParserPhase/PredictiveParser/ParsingTree.cpp b/SyntaxPhase/PredictiveParser/ParsingTree.cpp similarity index 100% rename from ParserPhase/PredictiveParser/ParsingTree.cpp rename to SyntaxPhase/PredictiveParser/ParsingTree.cpp diff --git a/ParserPhase/PredictiveParser/ParsingTree.h b/SyntaxPhase/PredictiveParser/ParsingTree.h similarity index 100% rename from ParserPhase/PredictiveParser/ParsingTree.h rename to SyntaxPhase/PredictiveParser/ParsingTree.h diff --git a/ParserPhase/PredictiveParser/Token.h b/SyntaxPhase/PredictiveParser/Token.h similarity index 100% rename from ParserPhase/PredictiveParser/Token.h rename to SyntaxPhase/PredictiveParser/Token.h diff --git a/main.cpp b/main.cpp index 8a1c0f9..6a359f9 100644 --- a/main.cpp +++ b/main.cpp @@ -5,10 +5,10 @@ #include "LexicalPhase/NFAConverter/NFACombiner.h" #include "LexicalPhase/DFAConverter/DFA.h" #include "LexicalPhase/SymbolTableGenerator/STGenerator.h" -#include "ParserPhase/GrammarParser/GrammarConverter.h" -#include "ParserPhase/GrammarParser/Grammar.h" -#include "ParserPhase/FirstAndFollowGenerator/FirstSetsGenerator.h" -#include "ParserPhase/FirstAndFollowGenerator/FollowSetsGenerator.h" +#include "SyntaxPhase/GrammarParser/GrammarConverter.h" +#include "SyntaxPhase/GrammarParser/Grammar.h" +#include "SyntaxPhase/FirstAndFollowGenerator/FirstSetsGenerator.h" +#include "SyntaxPhase/FirstAndFollowGenerator/FollowSetsGenerator.h" int main(int argc, char *argv[]) { From bb05a280d6ba3875011df0172d56b809c724508b Mon Sep 17 00:00:00 2001 From: Zyad Samy Date: Thu, 28 Dec 2023 17:32:11 +0200 Subject: [PATCH 15/41] cleanup --- Google_tests/CMakeLists.txt | 2 +- .../{ParserTest.cpp => SP_ParsingTest.cpp} | 46 ++++++++----------- SyntaxPhase/PredictiveParser/Parser.cpp | 12 ++--- .../PredictiveParser/ParsingTableEntry.cpp | 8 ++++ .../PredictiveParser/ParsingTableEntry.h | 1 + 5 files changed, 35 insertions(+), 34 deletions(-) rename Google_tests/{ParserTest.cpp => SP_ParsingTest.cpp} (87%) diff --git a/Google_tests/CMakeLists.txt b/Google_tests/CMakeLists.txt index 54d5e71..9a409e0 100644 --- a/Google_tests/CMakeLists.txt +++ b/Google_tests/CMakeLists.txt @@ -12,7 +12,7 @@ add_executable(Google_Tests_run UtilsTest.cpp SP_GrammarConverterTest.cpp SP_FollowSetGeneratorTest.cpp SP_FirstSetGeneratorTest.cpp - SP_FollowSetGeneratorTest.cpp) + SP_ParsingTest.cpp) target_link_libraries(Google_Tests_run gtest gtest_main) target_link_libraries(Google_Tests_run Util_lib) diff --git a/Google_tests/ParserTest.cpp b/Google_tests/SP_ParsingTest.cpp similarity index 87% rename from Google_tests/ParserTest.cpp rename to Google_tests/SP_ParsingTest.cpp index 6570e3e..8c19980 100644 --- a/Google_tests/ParserTest.cpp +++ b/Google_tests/SP_ParsingTest.cpp @@ -1,14 +1,9 @@ #include -// #include "../SyntaxPhase/PredictiveParser/ParsingTree.h" #include "../SyntaxPhase/Common/NonTerminal.h" -#include "../SyntaxPhase/Common/Terminal.h" -#include "../SyntaxPhase/Common/Symbol.h" #include "../SyntaxPhase/GrammarParser/GrammarConverter.h" #include "../SyntaxPhase/GrammarParser/Grammar.h" #include "../SyntaxPhase/PredictiveParser/Token.h" #include "../SyntaxPhase/PredictiveParser/Parser.h" -#include "../SyntaxPhase/PredictiveParser/ParsingTree.h" -#include "../SyntaxPhase/PredictiveParser/ParsingResult.h" std::string g1Path = "../../GrammarTest.txt"; @@ -52,33 +47,32 @@ std::pair, Parsi TEST(ParserManualTest, ValidInput) { std::vector input = { - Token("a","a"), - Token("b","b"), - Token("b","b"), - Token("a","a"), + Token("a", "a"), + Token("b", "b"), + Token("b", "b"), + Token("a", "a"), }; - + // S -> aBa // B -> bB | eps - NonTerminal* S = new NonTerminal("S"); - NonTerminal* B = new NonTerminal("B"); - Terminal* a = new Terminal("a"); - Terminal* b = new Terminal("b"); - - std::vector S_production_1 = {a,B,a}; - std::vector B_production_1 = {b,B}; - - std::unordered_map, ParsingTableEntry, PairHash, PairEqual> parsingTable; - parsingTable[{S,a->getName()}] = ParsingTableEntry({a,B,a}); - parsingTable[{B,a->getName()}] = ParsingTableEntry("epsilon"); - parsingTable[{B,b->getName()}] = ParsingTableEntry({b,B}); - + NonTerminal *S = new NonTerminal("S"); + NonTerminal *B = new NonTerminal("B"); + Terminal *a = new Terminal("a"); + Terminal *b = new Terminal("b"); + + std::vector S_production_1 = {a, B, a}; + std::vector B_production_1 = {b, B}; + + std::unordered_map, ParsingTableEntry, PairHash, PairEqual> parsingTable; + parsingTable[{S, a->getName()}] = ParsingTableEntry({a, B, a}); + parsingTable[{B, a->getName()}] = ParsingTableEntry("epsilon"); + parsingTable[{B, b->getName()}] = ParsingTableEntry({b, B}); + Parser parser(S, parsingTable); ParsingResult result = parser.parse(input); result.tree.print(); result.printTrace(); - - + result.printStackTrace(); } TEST(ParserManualTest, ValidInput2) { @@ -96,8 +90,6 @@ TEST(ParserManualTest, ValidInput2) { ParsingResult result = parser.parse(input); result.tree.print(); result.printTrace(); - - } TEST(ParserManualTest, InvalidInputExcessToken) { diff --git a/SyntaxPhase/PredictiveParser/Parser.cpp b/SyntaxPhase/PredictiveParser/Parser.cpp index f6b0c0d..ef60292 100644 --- a/SyntaxPhase/PredictiveParser/Parser.cpp +++ b/SyntaxPhase/PredictiveParser/Parser.cpp @@ -37,11 +37,11 @@ std::string productionString(Production &production, NonTerminal* nonTerminal); ParsingResult Parser::parse(std::vector &input) { - if (input.size() == 0) + if (input.empty()) return ParsingResult(); Symbol *startSymbol = this->startingSymbol; - ParsingTreeNode *rootNode = new ParsingTreeNode(startSymbol); + auto *rootNode = new ParsingTreeNode(startSymbol); if (startSymbol->isTerminal()) throw std::invalid_argument("startSymbol cannot be a terminal"); @@ -80,7 +80,7 @@ ParsingResult Parser::parse(std::vector &input) } else { - NonTerminal *currentNonTerminal = dynamic_cast(currentSymbol); + auto *currentNonTerminal = dynamic_cast(currentSymbol); bool entryExists = parsingTable.contains({currentNonTerminal, lookahead.terminal}); if (!entryExists && lookahead.terminal == END) { @@ -122,7 +122,7 @@ ParsingResult Parser::parse(std::vector &input) Symbol* symbol = (*it).get(); stack.push(symbol); - ParsingTreeNode *n = new ParsingTreeNode(symbol); + auto n = new ParsingTreeNode(symbol); currentNode->addChild(n); nodes.push(n); } @@ -139,12 +139,12 @@ std::string productionString(Production &production, NonTerminal* nonTerminal) ss << nonTerminal->getName(); ss << " -> "; - bool isEpsilonProduction = production.size() == 0; + bool isEpsilonProduction = production.empty(); if (isEpsilonProduction) { ss << "\u03B5"; } else { - for (auto symbol : production) + for (const auto& symbol : production) { ss << symbol->getName(); } diff --git a/SyntaxPhase/PredictiveParser/ParsingTableEntry.cpp b/SyntaxPhase/PredictiveParser/ParsingTableEntry.cpp index fad07be..33bddec 100644 --- a/SyntaxPhase/PredictiveParser/ParsingTableEntry.cpp +++ b/SyntaxPhase/PredictiveParser/ParsingTableEntry.cpp @@ -20,6 +20,14 @@ ParsingTableEntry::ParsingTableEntry(Production production) { this->production = std::move(production); } +ParsingTableEntry::ParsingTableEntry(std::vector p) { + Production production; + for (auto s:p) { + production.push_back(std::shared_ptr (s)); + } + this->production = std::move(production); +} + bool ParsingTableEntry::isSync() const { return this->sync; } diff --git a/SyntaxPhase/PredictiveParser/ParsingTableEntry.h b/SyntaxPhase/PredictiveParser/ParsingTableEntry.h index fed00cf..3c242ac 100644 --- a/SyntaxPhase/PredictiveParser/ParsingTableEntry.h +++ b/SyntaxPhase/PredictiveParser/ParsingTableEntry.h @@ -17,6 +17,7 @@ class ParsingTableEntry { ParsingTableEntry(); explicit ParsingTableEntry(std::string type); explicit ParsingTableEntry(Production production); + explicit ParsingTableEntry(std::vector production); [[nodiscard]] bool isSync() const; [[nodiscard]] bool isEpsilon() const; Production getProduction(); From 63a08e367381a6cbc819f5d1c3e262f0acea86d6 Mon Sep 17 00:00:00 2001 From: Zyad Samy Date: Thu, 28 Dec 2023 18:52:32 +0200 Subject: [PATCH 16/41] integration --- SyntaxPhase/PredictiveParser/Parser.cpp | 10 +- SyntaxPhase/PredictiveParser/ParsingTrace.cpp | 21 +- input.txt | 6 + main.cpp | 273 +++++------------- test.txt | 5 - 5 files changed, 92 insertions(+), 223 deletions(-) create mode 100644 input.txt delete mode 100644 test.txt diff --git a/SyntaxPhase/PredictiveParser/Parser.cpp b/SyntaxPhase/PredictiveParser/Parser.cpp index ef60292..c6c6616 100644 --- a/SyntaxPhase/PredictiveParser/Parser.cpp +++ b/SyntaxPhase/PredictiveParser/Parser.cpp @@ -189,10 +189,12 @@ void Parser::constructParseTable() { } for (Terminal* follow: followSet->getSet()){ - if (firstSet->hasNoEpsilon()) { - parsingTable[std::pair(nonTerminal.get(),follow->getName())] = ParsingTableEntry("sync"); - } else { - parsingTable[std::pair(nonTerminal.get(),follow->getName())] = ParsingTableEntry("epsilon"); + if (!parsingTable.contains({nonTerminal.get(), follow->getName()})){ + if (firstSet->hasNoEpsilon()) { + parsingTable[std::pair(nonTerminal.get(),follow->getName())] = ParsingTableEntry("sync"); + } else { + parsingTable[std::pair(nonTerminal.get(),follow->getName())] = ParsingTableEntry("epsilon"); + } } } } diff --git a/SyntaxPhase/PredictiveParser/ParsingTrace.cpp b/SyntaxPhase/PredictiveParser/ParsingTrace.cpp index 2b4101b..5ffdfc3 100644 --- a/SyntaxPhase/PredictiveParser/ParsingTrace.cpp +++ b/SyntaxPhase/PredictiveParser/ParsingTrace.cpp @@ -11,17 +11,22 @@ void ParsingTrace::setResult(std::string result) { this->result = result; } -void printStackTrace(std::ostream &os, std::stack stack) { +void printStackTrace(std::ostream &os, std::stack stack, bool topFirst = false) { int n = stack.size(); std::vector symbols(n); - - for (int i=n-1; i >= 0; i--) { - auto top = stack.top(); stack.pop(); - symbols[i] = top; + + for (int i=0; i < n; i++) { + auto top = stack.top(); + stack.pop(); + if (topFirst) { + symbols[i] = top; + } else { + symbols[n - i - 1] = top; + } } - + for (int i=0; i < n; i++) { - os << symbols[i]->getName(); + os << symbols[i]->getName() << " "; } } @@ -32,7 +37,7 @@ void printInputTrace(std::ostream &os, std::vector input, int l) { } void ParsingTrace::printStack() { - printStackTrace(std::cout, this->stack); + printStackTrace(std::cout, this->stack, true); } std::ostream &operator<<(std::ostream &os, const ParsingTrace& trace) { diff --git a/input.txt b/input.txt new file mode 100644 index 0000000..9238dfd --- /dev/null +++ b/input.txt @@ -0,0 +1,6 @@ +int x; +x = 5; +if (x > 2) +{ + x = 0; +} \ No newline at end of file diff --git a/main.cpp b/main.cpp index c647ff7..521015a 100644 --- a/main.cpp +++ b/main.cpp @@ -14,17 +14,47 @@ #include "unordered_set" #include "string" +std::vector getTokens(LAOutput output); int main(int argc, char *argv[]) { - if (argc != 2) + int statusCode; + + if (argc != 3) { - std::cerr << "Wrong number of parameter (One argument required: Rules File Path)" + std::cerr << "Wrong number of parameter (2 arguments required: Rules File Path, Grammar File Path)" << "\n"; return -1; } + + auto rulesFilePath = argv[1]; + auto grammarFilePath = argv[2]; + + // Parse rules file + RulesConverter rulesConverter(rulesFilePath); + statusCode = rulesConverter.parseFile(); + if (statusCode == -1){ + std::cerr << "Badly Formatted Rules File" << "\n"; + return -1; + } + + // Rules to Regular Expressions + std::vector regularExpressions = rulesConverter.getRegularExpressions(); + for (const RegularExpression& regExp : regularExpressions) { + std::cout << regExp.toString() << "\n"; + } + // Regular expressions conversion to NFA + NFACombiner nfaCombiner(regularExpressions); + auto table = nfaCombiner.extractTableRepresentation(); + State* nfaComplete = nfaCombiner.getCompleteNfa(); + + // NFA to DFA + DFA dfa(nfaComplete); + STGenerator stg(dfa); + + // parse Grammar file GrammarConverter grammarConverter; - int statusCode = grammarConverter.parseFile(argv[1]); + statusCode = grammarConverter.parseFile(grammarFilePath); if (statusCode == -1) return -1; @@ -37,215 +67,46 @@ int main(int argc, char *argv[]) { if (leftRecursion){ std::cout<< "Grammar had left recursion but was eliminated successfully." << "\n"; } - + + // Generate grammar Grammar grammar(grammarConverter); grammar.standardizeNonTerminals(); - - std::cout << "\n\n\n"; - + std::cout << "\n\n"; + + // Create first & follow sets and the parsing Table Parser parser(grammar); parser.printParsingTable(); parser.writeParsingTableToCSV(); - std::cout << "success"; - - -// std::vector standardizedGrammar = grammar.getStandardizedNonTerminals(); -// NonTerminal declaration = standardizedGrammar[3]; -// std::shared_ptr statementList = declaration.getProductions()[0][0]; -// auto statementListCast = dynamic_pointer_cast(statementList); -// if (statementListCast){ -// auto productions = statementListCast->getProductions(); -// } -// else{ -// std::cout << "Terminal"; -// } - -// RulesConverter rulesConverter(argv[1]); -// int statusCode = rulesConverter.parseFile(); -// if (statusCode == -1){ -// std::cerr << "Badly Formatted Rules File" << "\n"; -// return -1; -// } -// -// std::vector regularExpressions = rulesConverter.getRegularExpressions(); -// -// for (const RegularExpression& regExp : regularExpressions) { -// std::cout << regExp.toString() << "\n"; -// } -// -// // Regular expressions conversion to NFA -// NFACombiner nfaCombiner(regularExpressions); -// std::unordered_map, std::vector, PairHash, PairEqual> table = nfaCombiner.extractTableRepresentation(); -// State* nfaComplete = nfaCombiner.getCompleteNfa(); -// -// std::cout << "degree of the start state in NFA: " << nfaComplete->transitions.size() << '\n'; -// -// DFA dfa(nfaComplete); -// State* dfaStartState = dfa.getStartState(); -// -// int states = 0; -// std::stack frontier; -// std::unordered_map visited; -// frontier.push(dfaStartState); -// while(not frontier.empty()){ -// State* currentState = frontier.top(); -// frontier.pop(); -// if(visited.find(currentState) != visited.end()){ -// continue; -// } -// states += 1; -// visited[currentState] = 1; -// for(Transition transition: currentState->transitions){ -// frontier.push(transition.getNextState()); -// std::cout << currentState << " with input: " << transition.getInput() << " ,to state: " << transition.getNextState() << '\n'; -// } -// } -// std::cout << "num of DFA states: " << states << '\n'; -// -// State* minimzedDFAStartState = dfa.minimize(); -// -// int minimizedStates = 0; -// std::stack minimizedFrontier; -// std::unordered_map minimizedVisited; -// minimizedFrontier.push(minimzedDFAStartState); -// while(not minimizedFrontier.empty()){ -// State* miniCurrentState = minimizedFrontier.top(); -// minimizedFrontier.pop(); -// if(minimizedVisited.find(miniCurrentState) != minimizedVisited.end()){ -// continue; -// } -// minimizedStates += 1; -// minimizedVisited[miniCurrentState] = 1; -// for(Transition transition: miniCurrentState->transitions){ -// minimizedFrontier.push(transition.getNextState()); -// std::cout << miniCurrentState << " with input: " << transition.getInput() << " ,to state: " << transition.getNextState() << '\n'; -// } -// } -// std::cout << "num of minimized DFA states: " << minimizedStates << '\n'; -// -// STGenerator stg(dfa); -// std::cout << "Run all files in dir --> a\nRun specific file --> s\n> "; -// std::string input; -// std::getline(std::cin, input); -// std::string dirPath; -// if (input == "a") { -// std::cout << "Enter the directory path: "; -// std::getline(std::cin, dirPath); -// for (const auto &entry: std::filesystem::directory_iterator(dirPath)) { -// if (entry.is_regular_file()) { -// std::string scriptFilePath = entry.path().string(); -// std::cout << "Running: " << scriptFilePath << std::endl; -// stg.execute(scriptFilePath); -// std::cout << "\n#############W####" << std::endl; -// } -// } -// } else if (input == "s") { -// while (true) { -// std::cout << "Enter the file path: "; -// std::string scriptFilePath; -// std::getline(std::cin, scriptFilePath); -// if (scriptFilePath == "$") -// break; -// stg.execute(scriptFilePath); -// std::cout << "\n#############W####" << std::endl; -// } -// }else{ -// std::cout << "Invalid input. Program Terminated." << std::endl; -// } - - // RulesConverter rulesConverter(argv[1]); - // int statusCode = rulesConverter.parseFile(); - // if (statusCode == -1){ - // std::cerr << "Badly Formatted Rules File" << "\n"; - // return -1; - // } - // - // std::vector regularExpressions = rulesConverter.getRegularExpressions(); - // - // for (const RegularExpression& regExp : regularExpressions) { - // std::cout << regExp.toString() << "\n"; - // } - // - // // Regular expressions conversion to NFA - // NFACombiner nfaCombiner(regularExpressions); - // std::unordered_map, std::vector, PairHash, PairEqual> table = nfaCombiner.extractTableRepresentation(); - // State* nfaComplete = nfaCombiner.getCompleteNfa(); - // - // std::cout << "degree of the start state in NFA: " << nfaComplete->transitions.size() << '\n'; - // - // DFA dfa(nfaComplete); - // State* dfaStartState = dfa.getStartState(); - // - // int states = 0; - // std::stack frontier; - // std::unordered_map visited; - // frontier.push(dfaStartState); - // while(not frontier.empty()){ - // State* currentState = frontier.top(); - // frontier.pop(); - // if(visited.find(currentState) != visited.end()){ - // continue; - // } - // states += 1; - // visited[currentState] = 1; - // for(Transition transition: currentState->transitions){ - // frontier.push(transition.getNextState()); - // std::cout << currentState << " with input: " << transition.getInput() << " ,to state: " << transition.getNextState() << '\n'; - // } - // } - // std::cout << "num of DFA states: " << states << '\n'; - // - // State* minimzedDFAStartState = dfa.minimize(); - // - // int minimizedStates = 0; - // std::stack minimizedFrontier; - // std::unordered_map minimizedVisited; - // minimizedFrontier.push(minimzedDFAStartState); - // while(not minimizedFrontier.empty()){ - // State* miniCurrentState = minimizedFrontier.top(); - // minimizedFrontier.pop(); - // if(minimizedVisited.find(miniCurrentState) != minimizedVisited.end()){ - // continue; - // } - // minimizedStates += 1; - // minimizedVisited[miniCurrentState] = 1; - // for(Transition transition: miniCurrentState->transitions){ - // minimizedFrontier.push(transition.getNextState()); - // std::cout << miniCurrentState << " with input: " << transition.getInput() << " ,to state: " << transition.getNextState() << '\n'; - // } - // } - // std::cout << "num of minimized DFA states: " << minimizedStates << '\n'; - // - // STGenerator stg(dfa); - // std::cout << "Run all files in dir --> a\nRun specific file --> s\n> "; - // std::string input; - // std::getline(std::cin, input); - // std::string dirPath; - // if (input == "a") { - // std::cout << "Enter the directory path: "; - // std::getline(std::cin, dirPath); - // for (const auto &entry: std::filesystem::directory_iterator(dirPath)) { - // if (entry.is_regular_file()) { - // std::string scriptFilePath = entry.path().string(); - // std::cout << "Running: " << scriptFilePath << std::endl; - // stg.execute(scriptFilePath); - // std::cout << "\n#############W####" << std::endl; - // } - // } - // } else if (input == "s") { - // while (true) { - // std::cout << "Enter the file path: "; - // std::string scriptFilePath; - // std::getline(std::cin, scriptFilePath); - // if (scriptFilePath == "$") - // break; - // stg.execute(scriptFilePath); - // std::cout << "\n#############W####" << std::endl; - // } - // }else{ - // std::cout << "Invalid input. Program Terminated." << std::endl; - // } + // Read input file + while (true) { + // Get input path + std::cout << "\n\nEnter the file path: "; + std::string inputFilePath; + std::getline(std::cin, inputFilePath); + if (inputFilePath == "$") + break; + + // Read file into tokens + LAOutput LAout = stg.execute(inputFilePath); + std::vector tokens = getTokens(LAout); + std::cout << "\n\n"; + // Parse tokens + auto result = parser.parse(tokens); + std::cout << "<>\n"; + result.tree.print(); + std::cout << "\ni<>\n"; + result.printStackTrace(); + } return 0; +} + +std::vector getTokens(LAOutput output) { + std::vector tokens; + auto symbolTable = output.getSymbolTable(); + for (auto& row: symbolTable) { + tokens.push_back(Token(row.getTokenType(), row.getToken())); + } + return tokens; } \ No newline at end of file diff --git a/test.txt b/test.txt deleted file mode 100644 index c3463c4..0000000 --- a/test.txt +++ /dev/null @@ -1,5 +0,0 @@ -int sum , count , pass , mnt; while (pass != -10) -{ -pass = pass + 1 ; -} \ No newline at end of file From 211d2016640d204404cfc9609a52c13f26c58c30 Mon Sep 17 00:00:00 2001 From: Zyad Samy Date: Thu, 28 Dec 2023 20:37:22 +0200 Subject: [PATCH 17/41] make printTrace output more readable --- SyntaxPhase/PredictiveParser/Parser.cpp | 9 ++-- .../PredictiveParser/ParsingResult.cpp | 30 +++++++++---- SyntaxPhase/PredictiveParser/ParsingResult.h | 1 + SyntaxPhase/PredictiveParser/ParsingTrace.cpp | 44 +++++++++++-------- SyntaxPhase/PredictiveParser/ParsingTrace.h | 7 ++- main.cpp | 4 +- 6 files changed, 60 insertions(+), 35 deletions(-) diff --git a/SyntaxPhase/PredictiveParser/Parser.cpp b/SyntaxPhase/PredictiveParser/Parser.cpp index c6c6616..a40caa2 100644 --- a/SyntaxPhase/PredictiveParser/Parser.cpp +++ b/SyntaxPhase/PredictiveParser/Parser.cpp @@ -69,7 +69,7 @@ ParsingResult Parser::parse(std::vector &input) bool match = currentSymbol->getName() == lookahead.terminal; if (!match) { - auto err = "Error: missing " + currentSymbol->getName() + ", inserted"; + auto err = "Error: missing '" + currentSymbol->getName() + "', inserted"; trace.setError(err); traces.push_back(trace); continue; @@ -84,14 +84,15 @@ ParsingResult Parser::parse(std::vector &input) bool entryExists = parsingTable.contains({currentNonTerminal, lookahead.terminal}); if (!entryExists && lookahead.terminal == END) { - auto err = "Error: missing " + currentSymbol->getName(); + auto err = "Error: missing '" + currentSymbol->getName() + "'"; trace.setError(err); traces.push_back(trace); continue; } else if (!entryExists) { - auto err = "Error: (illegal " + currentSymbol->getName() + ") - discarded " + (!lookahead.lexeme.empty() ? lookahead.lexeme : lookahead.terminal); + auto err = "Error: illegal {" + currentSymbol->getName() + "} - discarded " + + (!lookahead.lexeme.empty() ? "'" + lookahead.lexeme + "'" : lookahead.terminal); trace.setError(err); traces.push_back(trace); @@ -104,7 +105,7 @@ ParsingResult Parser::parse(std::vector &input) ParsingTableEntry entry = parsingTable[{currentNonTerminal, lookahead.terminal}]; if (entry.isSync()) { - auto err = "Error: expected " + currentSymbol->getName(); + auto err = "Error: expected '" + currentSymbol->getName() + "'"; trace.setError(err); traces.push_back(trace); continue; diff --git a/SyntaxPhase/PredictiveParser/ParsingResult.cpp b/SyntaxPhase/PredictiveParser/ParsingResult.cpp index c683d35..85582ca 100644 --- a/SyntaxPhase/PredictiveParser/ParsingResult.cpp +++ b/SyntaxPhase/PredictiveParser/ParsingResult.cpp @@ -6,15 +6,29 @@ ParsingResult::ParsingResult(ParsingTree& tr, std::vector& trc) ParsingResult::ParsingResult() {}; void ParsingResult::printTrace() { + std::vector stackStrings; + std::vector inputStrings; + int maxStackLength = 0; + int maxInputLength = 0; + for (auto& trace: traces) { - std::cout << trace; + auto stackString = trace.stackString(); + auto inputString = trace.inputString(); + stackStrings.push_back(stackString); + inputStrings.push_back(inputString); + + maxStackLength = std::max(maxStackLength, (int) stackString.length()); + maxInputLength = std::max(maxInputLength, (int) inputString.length()); + } + + std::cout << std::setw(maxStackLength) << "Stack" << "\t" + << std::setw(maxInputLength) << "Input" << "\n"; + + for (int i=0; i < traces.size(); i++) { + std::cout << std::setw(maxStackLength) << stackStrings[i] << "\t"; + std::cout << std::setw(maxInputLength) << inputStrings[i] << "\t"; + std::cout << traces[i].extrasString() << std::endl; } + std::cout << std::endl; } - -void ParsingResult::printStackTrace() { - for (auto& trace: traces) { - trace.printStack(); - std::cout << std::endl; - } -} \ No newline at end of file diff --git a/SyntaxPhase/PredictiveParser/ParsingResult.h b/SyntaxPhase/PredictiveParser/ParsingResult.h index c15cf46..ca15fe5 100644 --- a/SyntaxPhase/PredictiveParser/ParsingResult.h +++ b/SyntaxPhase/PredictiveParser/ParsingResult.h @@ -3,6 +3,7 @@ #include "ParsingTree.h" #include "ParsingTrace.h" +#include class ParsingResult { public: diff --git a/SyntaxPhase/PredictiveParser/ParsingTrace.cpp b/SyntaxPhase/PredictiveParser/ParsingTrace.cpp index 5ffdfc3..82304e5 100644 --- a/SyntaxPhase/PredictiveParser/ParsingTrace.cpp +++ b/SyntaxPhase/PredictiveParser/ParsingTrace.cpp @@ -11,7 +11,7 @@ void ParsingTrace::setResult(std::string result) { this->result = result; } -void printStackTrace(std::ostream &os, std::stack stack, bool topFirst = false) { +std::string ParsingTrace::stackString(bool topFirst) { int n = stack.size(); std::vector symbols(n); @@ -24,29 +24,35 @@ void printStackTrace(std::ostream &os, std::stack stack, bool topFirst symbols[n - i - 1] = top; } } - + for (int i=0; i < n; i++) { - os << symbols[i]->getName() << " "; + if (topFirst) { + stack.push(symbols[i]); + } else { + stack.push(symbols[n-i-1]); + } } + + std::stringstream ss; + for (int i=0; i < n; i++) { + ss << symbols[i]->getName() << " "; + } + return ss.str(); } -void printInputTrace(std::ostream &os, std::vector input, int l) { - for (int i=l; i < input.size(); i++) { - os << input[i].lexeme; +std::string ParsingTrace::inputString() { + std::stringstream ss; + for (int i=lookahead; i < input.size(); i++) { + ss << input[i].lexeme << " "; } + return ss.str(); } -void ParsingTrace::printStack() { - printStackTrace(std::cout, this->stack, true); +std::string ParsingTrace::extrasString() { + std::stringstream ss; + ss << "\033[31m"; // change color to red + ss << err; + ss << "\033[0m"; // reset color + ss << result; + return ss.str(); } - -std::ostream &operator<<(std::ostream &os, const ParsingTrace& trace) { - printStackTrace(os, trace.stack); - os << "\t\t"; - printInputTrace(os, trace.input, trace.lookahead); - os << "\t\t"; - os << trace.err; - os << trace.result; - os << std::endl; - return os; -} \ No newline at end of file diff --git a/SyntaxPhase/PredictiveParser/ParsingTrace.h b/SyntaxPhase/PredictiveParser/ParsingTrace.h index 3f936b9..2be6b70 100644 --- a/SyntaxPhase/PredictiveParser/ParsingTrace.h +++ b/SyntaxPhase/PredictiveParser/ParsingTrace.h @@ -5,6 +5,8 @@ #include "Token.h" #include "../Common/Symbol.h" +#include + class ParsingTrace { private: @@ -18,8 +20,9 @@ class ParsingTrace ParsingTrace(std::stack stack, std::vector &input, int lookahead); void setError(std::string err); void setResult(std::string result); - void printStack(); - friend std::ostream &operator<<(std::ostream &os, const ParsingTrace &trace); + std::string stackString(bool topFirst = true); + std::string inputString(); + std::string extrasString(); }; #endif \ No newline at end of file diff --git a/main.cpp b/main.cpp index 521015a..9876cde 100644 --- a/main.cpp +++ b/main.cpp @@ -95,8 +95,8 @@ int main(int argc, char *argv[]) { auto result = parser.parse(tokens); std::cout << "<>\n"; result.tree.print(); - std::cout << "\ni<>\n"; - result.printStackTrace(); + std::cout << "\n<>\n"; + result.printTrace(); } return 0; From cb01888566a16de289b23675a600de64ee99c796 Mon Sep 17 00:00:00 2001 From: Zyad Samy Date: Thu, 28 Dec 2023 20:47:48 +0200 Subject: [PATCH 18/41] add epsilon derivations to parsing tree print --- SyntaxPhase/PredictiveParser/ParsingTree.cpp | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/SyntaxPhase/PredictiveParser/ParsingTree.cpp b/SyntaxPhase/PredictiveParser/ParsingTree.cpp index fb6482f..bce1cf8 100644 --- a/SyntaxPhase/PredictiveParser/ParsingTree.cpp +++ b/SyntaxPhase/PredictiveParser/ParsingTree.cpp @@ -18,14 +18,20 @@ void ParsingTree::printNode(const std::string& prefix, ParsingTreeNode* node, bo throw std::invalid_argument("null node is found in the tree"); std::cout << prefix; - std::cout << (isLast ? "└──" : "├──"); + std::cout << (isLast ? "└── " : "├── "); std::cout << node->getSymbol()->getName() << std::endl; - auto children = node->getChildren(); - for (int i=0; i < children.size(); i++) { - bool isLastChild = (i == children.size() - 1); - printNode(prefix + (isLast ? " " : "│ "), children[i], isLastChild); - } + auto children = node->getChildren(); + if (node->isLeaf()) { + if (!node->getSymbol()->isTerminal()){ + std::cout << prefix << (isLast ? " " : "│ ") << "└──" << " \u03B5\n"; + } + } else { + for (int i=0; i < children.size(); i++) { + bool isLastChild = (i == children.size() - 1); + printNode(prefix + (isLast ? " " : "│ "), children[i], isLastChild); + } + } } From 1946a4d0ad0cc8f41b44304dcd9f2e527ff6e26f Mon Sep 17 00:00:00 2001 From: Zyad Samy Date: Thu, 28 Dec 2023 22:02:19 +0200 Subject: [PATCH 19/41] fix backslashes terminals bug --- LexicalPhase/RulesParser/RegularDefinition.cpp | 6 +++++- Rules.txt | 3 +-- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/LexicalPhase/RulesParser/RegularDefinition.cpp b/LexicalPhase/RulesParser/RegularDefinition.cpp index f5d20ba..2b00239 100644 --- a/LexicalPhase/RulesParser/RegularDefinition.cpp +++ b/LexicalPhase/RulesParser/RegularDefinition.cpp @@ -17,7 +17,11 @@ const std::string &RegularDefinition::getRegex() const { return regex; } -RegularDefinition::RegularDefinition(std::string name, std::string regex) : name(std::move(name)), regex(std::move(regex)) {} +RegularDefinition::RegularDefinition(std::string name, std::string regex) : regex(std::move(regex)) { + std::string s = name; + replaceAll(s, "\\", ""); + this->name = s; +} int RegularDefinition::standardizeRegex(std::vector regularDefinitions) { removeConsecutiveSpaces(regex); diff --git a/Rules.txt b/Rules.txt index 0f056f8..c029125 100644 --- a/Rules.txt +++ b/Rules.txt @@ -5,8 +5,7 @@ digits = digit+ {boolean int float} num: digit+ | digit+ . digits ( \L | E digits) relop: \=\= | !\= | > | >\= | < | <\= -assign:\= { if else while } -[; , \( \) { }] +[; , \( \) { } =] addop: \+ | \- mulop: \* | / \ No newline at end of file From 0785e8c41363f16355aab45270162eb5eee19287 Mon Sep 17 00:00:00 2001 From: Zyad Samy Date: Fri, 29 Dec 2023 14:33:37 +0200 Subject: [PATCH 20/41] small output adjustment --- SyntaxPhase/PredictiveParser/Parser.cpp | 2 +- SyntaxPhase/PredictiveParser/ParsingResult.cpp | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/SyntaxPhase/PredictiveParser/Parser.cpp b/SyntaxPhase/PredictiveParser/Parser.cpp index a40caa2..a1c25c2 100644 --- a/SyntaxPhase/PredictiveParser/Parser.cpp +++ b/SyntaxPhase/PredictiveParser/Parser.cpp @@ -147,7 +147,7 @@ std::string productionString(Production &production, NonTerminal* nonTerminal) } else { for (const auto& symbol : production) { - ss << symbol->getName(); + ss << symbol->getName() << " "; } } return ss.str(); diff --git a/SyntaxPhase/PredictiveParser/ParsingResult.cpp b/SyntaxPhase/PredictiveParser/ParsingResult.cpp index 85582ca..b1d8f78 100644 --- a/SyntaxPhase/PredictiveParser/ParsingResult.cpp +++ b/SyntaxPhase/PredictiveParser/ParsingResult.cpp @@ -8,8 +8,8 @@ ParsingResult::ParsingResult() {}; void ParsingResult::printTrace() { std::vector stackStrings; std::vector inputStrings; - int maxStackLength = 0; - int maxInputLength = 0; + int maxStackLength = 10; + int maxInputLength = 10; for (auto& trace: traces) { auto stackString = trace.stackString(); From a08d3c7656ad96d715b8e62d9073cf8c77c4a92c Mon Sep 17 00:00:00 2001 From: Deffo0 Date: Fri, 29 Dec 2023 15:48:41 +0200 Subject: [PATCH 21/41] merge final version --- CMakeLists.txt | 1 + Grammar.txt | 4 +- GrammarTest.txt | 1 + SyntaxPhase/CMakeLists.txt | 2 +- SyntaxPhase/Common/NonTerminal.cpp | 8 ++-- SyntaxPhase/PredictiveParser/Parser.cpp | 53 ++++++++++++++++++------- main.cpp | 8 ++-- 7 files changed, 52 insertions(+), 25 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index bd309c4..66af090 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -15,6 +15,7 @@ target_link_libraries(Compiler FirstAndFollowGenerator_lib) target_link_libraries(Compiler PredictiveParser_lib) + add_subdirectory(Util) add_subdirectory(LexicalPhase) add_subdirectory(SyntaxPhase) diff --git a/Grammar.txt b/Grammar.txt index a618707..0879925 100644 --- a/Grammar.txt +++ b/Grammar.txt @@ -6,12 +6,12 @@ | ASSIGNMENT # DECLARATION ::= PRIMITIVE_TYPE 'id' ';' # PRIMITIVE_TYPE ::= 'int' | 'float' -# IF ::= 'if' '(' EXPRESSION ')' '{' STATEMENT '}' 'else' '{' STATEMENT '}' +# IF ::= 'if' '(' EXPRESSION ')' '{' STATEMENT '}' 'else' '{' STATEMENT '}' # WHILE ::= 'while' '(' EXPRESSION ')' '{' STATEMENT '}' # ASSIGNMENT ::= 'id' '=' EXPRESSION ';' # EXPRESSION ::= SIMPLE_EXPRESSION | SIMPLE_EXPRESSION 'relop' SIMPLE_EXPRESSION -# SIMPLE_EXPRESSION ::= TERM | SIGN TERM | SIMPLE_EXPRESSION 'addop' TERM +# SIMPLE_EXPRESSION ::= TERM | SIGN TERM | SIMPLE_EXPRESSION 'addop' TERM # TERM ::= FACTOR | TERM 'mulop' FACTOR # FACTOR ::= 'id' | 'num' | '(' EXPRESSION ')' # SIGN ::= '+' | '-' \ No newline at end of file diff --git a/GrammarTest.txt b/GrammarTest.txt index f2a2a4f..ee54ebb 100644 --- a/GrammarTest.txt +++ b/GrammarTest.txt @@ -6,3 +6,4 @@ # F` ::= '*' F | '\\L' # P ::= '(' E ')' | 'a' | 'b' | 'Em' +pukk \ No newline at end of file diff --git a/SyntaxPhase/CMakeLists.txt b/SyntaxPhase/CMakeLists.txt index e092aa5..884e52b 100644 --- a/SyntaxPhase/CMakeLists.txt +++ b/SyntaxPhase/CMakeLists.txt @@ -3,4 +3,4 @@ project(SyntaxPhase) add_subdirectory(GrammarParser) add_subdirectory(Common) add_subdirectory(FirstAndFollowGenerator) -add_subdirectory(PredictiveParser) +add_subdirectory(PredictiveParser) \ No newline at end of file diff --git a/SyntaxPhase/Common/NonTerminal.cpp b/SyntaxPhase/Common/NonTerminal.cpp index d39ca5b..3ac2683 100644 --- a/SyntaxPhase/Common/NonTerminal.cpp +++ b/SyntaxPhase/Common/NonTerminal.cpp @@ -10,10 +10,6 @@ NonTerminal::NonTerminal(const std::string &name) : Symbol(name, false) { followComputed = false; } -std::vector>>NonTerminal::getProductions() const { - return productions; -} - void NonTerminal::setProductions(const std::vector>> &productionsVector) { NonTerminal::productions = productionsVector; } @@ -67,3 +63,7 @@ void NonTerminal::setFollowComputed() { bool NonTerminal::isFollowComputed() const { return followComputed; } + +std::vector>> NonTerminal::getProductions() const { + return productions; +} diff --git a/SyntaxPhase/PredictiveParser/Parser.cpp b/SyntaxPhase/PredictiveParser/Parser.cpp index a1c25c2..2fdb014 100644 --- a/SyntaxPhase/PredictiveParser/Parser.cpp +++ b/SyntaxPhase/PredictiveParser/Parser.cpp @@ -14,6 +14,7 @@ Parser::Parser(Grammar& cfg): grammar(cfg), NTs(cfg.getStandardizedNonTerminals()) { computeNTsWithFirstSet(); + std::cout << "\n\n\n"; computeNTsWithFollowSet(); constructParseTable(); startingSymbol = cfg.getStartSymbol(); @@ -175,7 +176,6 @@ Production Parser::getInputMatchedProduction(const std::vector nonTerminalsSet; + std::unordered_set terminalsSet; + + for (const auto& entry : parsingTable) { + nonTerminalsSet.insert(entry.first.first); + terminalsSet.insert(entry.first.second); + } + + // Convert sets to vectors for ordered iteration + std::vector nonTerminals(nonTerminalsSet.begin(), nonTerminalsSet.end()); + std::vector terminals(terminalsSet.begin(), terminalsSet.end()); + std::sort(nonTerminals.begin(), nonTerminals.end()); + std::sort(terminals.begin(), terminals.end()); + std::ofstream csvFile(filename); if (!csvFile.is_open()) { @@ -234,21 +248,30 @@ void Parser::writeParsingTableToCSV() { return; } - csvFile << "NonTerminal,Terminal,Entry" << std::endl; - - for (const auto& entry : parsingTable) { - auto key = entry.first; - auto value = entry.second; - - if (key.first && !key.second.empty()) { - csvFile << key.first->getName() - << "," << key.second - << "," << (value.isEpsilon() ? "epsilon" : (value.isSync() ? "sync" : "production")) /* Entry */ - << std::endl; - } else { - std::cerr << "Error: Invalid key in parsingTable." << std::endl; + csvFile << ","; + for (const std::string& terminal : terminals) { + csvFile << terminal << ","; + } + csvFile << std::endl; + + for (NonTerminal* nonTerminal : nonTerminals) { + csvFile << nonTerminal->getName() << ","; + for (const std::string& terminal : terminals) { + auto entry = parsingTable.find({nonTerminal, terminal}); + if (entry != parsingTable.end()) { + auto& value = entry->second; + std::string visibleProduction = "--> "; + for (const std::shared_ptr& symbol : entry->second.getProduction()){ + visibleProduction += symbol->getName() + " "; + } + csvFile << (value.isEpsilon() ? "epsilon" : (value.isSync() ? "sync" : visibleProduction)) << ","; + } else { + // No entry for the combination of non-terminal and terminal + csvFile << "N/A,"; + } } + csvFile << std::endl; } csvFile.close(); -} \ No newline at end of file +} diff --git a/main.cpp b/main.cpp index 9876cde..b55ce7b 100644 --- a/main.cpp +++ b/main.cpp @@ -71,12 +71,14 @@ int main(int argc, char *argv[]) { // Generate grammar Grammar grammar(grammarConverter); grammar.standardizeNonTerminals(); - std::cout << "\n\n"; - - // Create first & follow sets and the parsing Table + + std::cout << grammar; + std::cout << "\n\n\n"; + Parser parser(grammar); parser.printParsingTable(); parser.writeParsingTableToCSV(); + std::cout << "success"; // Read input file while (true) { From cd2c02a68651410578f8282d82706950ea7d135a Mon Sep 17 00:00:00 2001 From: Deffo0 Date: Fri, 29 Dec 2023 16:05:50 +0200 Subject: [PATCH 22/41] print grammar --- SyntaxPhase/GrammarParser/Grammar.cpp | 2 +- main.cpp | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/SyntaxPhase/GrammarParser/Grammar.cpp b/SyntaxPhase/GrammarParser/Grammar.cpp index 2143c9c..093ddfe 100644 --- a/SyntaxPhase/GrammarParser/Grammar.cpp +++ b/SyntaxPhase/GrammarParser/Grammar.cpp @@ -74,7 +74,7 @@ Grammar::Grammar(GrammarConverter modifiedGrammar) : modifiedGrammar(std::move(m std::ostream &operator<<(std::ostream &os, const Grammar &g) { for (const auto& r : g.standardizedNonTerminals) - os << r; + os << r.get(); os << "\n\n\n"; return os; } diff --git a/main.cpp b/main.cpp index b55ce7b..5b88e03 100644 --- a/main.cpp +++ b/main.cpp @@ -70,9 +70,8 @@ int main(int argc, char *argv[]) { // Generate grammar Grammar grammar(grammarConverter); - grammar.standardizeNonTerminals(); - std::cout << grammar; + grammar.standardizeNonTerminals(); std::cout << "\n\n\n"; Parser parser(grammar); From df1d087fdc186557b7960aae365de45a1137ed30 Mon Sep 17 00:00:00 2001 From: Deffo0 Date: Fri, 29 Dec 2023 16:07:17 +0200 Subject: [PATCH 23/41] merge --- SyntaxPhase/CMakeLists.txt | 3 +- main.cpp | 107 +++++++++++++++++++++++++++++++++++++ 2 files changed, 109 insertions(+), 1 deletion(-) diff --git a/SyntaxPhase/CMakeLists.txt b/SyntaxPhase/CMakeLists.txt index 884e52b..22272be 100644 --- a/SyntaxPhase/CMakeLists.txt +++ b/SyntaxPhase/CMakeLists.txt @@ -3,4 +3,5 @@ project(SyntaxPhase) add_subdirectory(GrammarParser) add_subdirectory(Common) add_subdirectory(FirstAndFollowGenerator) -add_subdirectory(PredictiveParser) \ No newline at end of file +add_subdirectory(PredictiveParser) + diff --git a/main.cpp b/main.cpp index 5b88e03..1498e6a 100644 --- a/main.cpp +++ b/main.cpp @@ -99,6 +99,113 @@ int main(int argc, char *argv[]) { std::cout << "\n<>\n"; result.printTrace(); } +======= +// std::vector standardizedGrammar = grammar.getStandardizedNonTerminals(); +// NonTerminal declaration = standardizedGrammar[3]; +// std::shared_ptr statementList = declaration.getProductions()[0][0]; +// auto statementListCast = dynamic_pointer_cast(statementList); +// if (statementListCast){ +// auto productions = statementListCast->getProductions(); +// } +// else{ +// std::cout << "Terminal"; +// } + + +// RulesConverter rulesConverter(argv[1]); +// int statusCode = rulesConverter.parseFile(); +// if (statusCode == -1){ +// std::cerr << "Badly Formatted Rules File" << "\n"; +// return -1; +// } +// +// std::vector regularExpressions = rulesConverter.getRegularExpressions(); +// +// for (const RegularExpression& regExp : regularExpressions) { +// std::cout << regExp.toString() << "\n"; +// } +// +// // Regular expressions conversion to NFA +// NFACombiner nfaCombiner(regularExpressions); +// std::unordered_map, std::vector, PairHash, PairEqual> table = nfaCombiner.extractTableRepresentation(); +// State* nfaComplete = nfaCombiner.getCompleteNfa(); +// +// std::cout << "degree of the start state in NFA: " << nfaComplete->transitions.size() << '\n'; +// +// DFA dfa(nfaComplete); +// State* dfaStartState = dfa.getStartState(); +// +// int states = 0; +// std::stack frontier; +// std::unordered_map visited; +// frontier.push(dfaStartState); +// while(not frontier.empty()){ +// State* currentState = frontier.top(); +// frontier.pop(); +// if(visited.find(currentState) != visited.end()){ +// continue; +// } +// states += 1; +// visited[currentState] = 1; +// for(Transition transition: currentState->transitions){ +// frontier.push(transition.getNextState()); +// std::cout << currentState << " with input: " << transition.getInput() << " ,to state: " << transition.getNextState() << '\n'; +// } +// } +// std::cout << "num of DFA states: " << states << '\n'; +// +// State* minimzedDFAStartState = dfa.minimize(); +// +// int minimizedStates = 0; +// std::stack minimizedFrontier; +// std::unordered_map minimizedVisited; +// minimizedFrontier.push(minimzedDFAStartState); +// while(not minimizedFrontier.empty()){ +// State* miniCurrentState = minimizedFrontier.top(); +// minimizedFrontier.pop(); +// if(minimizedVisited.find(miniCurrentState) != minimizedVisited.end()){ +// continue; +// } +// minimizedStates += 1; +// minimizedVisited[miniCurrentState] = 1; +// for(Transition transition: miniCurrentState->transitions){ +// minimizedFrontier.push(transition.getNextState()); +// std::cout << miniCurrentState << " with input: " << transition.getInput() << " ,to state: " << transition.getNextState() << '\n'; +// } +// } +// std::cout << "num of minimized DFA states: " << minimizedStates << '\n'; +// +// STGenerator stg(dfa); +// std::cout << "Run all files in dir --> a\nRun specific file --> s\n> "; +// std::string input; +// std::getline(std::cin, input); +// std::string dirPath; +// if (input == "a") { +// std::cout << "Enter the directory path: "; +// std::getline(std::cin, dirPath); +// for (const auto &entry: std::filesystem::directory_iterator(dirPath)) { +// if (entry.is_regular_file()) { +// std::string scriptFilePath = entry.path().string(); +// std::cout << "Running: " << scriptFilePath << std::endl; +// stg.execute(scriptFilePath); +// std::cout << "\n#############W####" << std::endl; +// } +// } +// } else if (input == "s") { +// while (true) { +// std::cout << "Enter the file path: "; +// std::string scriptFilePath; +// std::getline(std::cin, scriptFilePath); +// if (scriptFilePath == "$") +// break; +// stg.execute(scriptFilePath); +// std::cout << "\n#############W####" << std::endl; +// } +// }else{ +// std::cout << "Invalid input. Program Terminated." << std::endl; +// } + +>>>>>>> .merge_file_D0zfMT return 0; } From 05242933cfdc59699ba9d4e68f4b726b4ad77586 Mon Sep 17 00:00:00 2001 From: Deffo0 Date: Fri, 29 Dec 2023 16:30:32 +0200 Subject: [PATCH 24/41] comment some unit tests --- Google_tests/SP_ParsingTest.cpp | 421 ++++++++++++++++---------------- 1 file changed, 210 insertions(+), 211 deletions(-) diff --git a/Google_tests/SP_ParsingTest.cpp b/Google_tests/SP_ParsingTest.cpp index 8c19980..ba60f5f 100644 --- a/Google_tests/SP_ParsingTest.cpp +++ b/Google_tests/SP_ParsingTest.cpp @@ -1,211 +1,210 @@ -#include -#include "../SyntaxPhase/Common/NonTerminal.h" -#include "../SyntaxPhase/GrammarParser/GrammarConverter.h" -#include "../SyntaxPhase/GrammarParser/Grammar.h" -#include "../SyntaxPhase/PredictiveParser/Token.h" -#include "../SyntaxPhase/PredictiveParser/Parser.h" - -std::string g1Path = "../../GrammarTest.txt"; - -std::pair, ParsingTableEntry, PairHash, PairEqual>> MathExprGrammar() { - // Lecture-Parsing-2 Example-2 p-26 - NonTerminal* E = new NonTerminal("E"); - NonTerminal* E_ = new NonTerminal("E'"); - NonTerminal* T = new NonTerminal("T"); - NonTerminal* T_ = new NonTerminal("T'"); - NonTerminal* F = new NonTerminal("F"); - Terminal* id = new Terminal("id"); - Terminal* plus = new Terminal("+"); - Terminal* mult = new Terminal("*"); - Terminal* openB = new Terminal("("); - Terminal* closeB = new Terminal(")"); - - std::vector Eprod = {T,E_}; - std::vector E_prod = {plus,T,E_}; - std::vector Tprod = {F,T_}; - std::vector T_prod = {mult,F,T_}; - std::vector Fprod1 = {openB,E,closeB}; - std::vector Fprod2 = {id}; - - std::unordered_map, ParsingTableEntry, PairHash, PairEqual> parsingTable; - parsingTable[{E,id->getName()}] = ParsingTableEntry(Eprod); - parsingTable[{T,id->getName()}] = ParsingTableEntry(Tprod); - parsingTable[{F,id->getName()}] = ParsingTableEntry(Fprod2); - parsingTable[{E_,plus->getName()}] = ParsingTableEntry(E_prod); - parsingTable[{T_,plus->getName()}] = ParsingTableEntry("epsilon"); - parsingTable[{T_,mult->getName()}] = ParsingTableEntry(T_prod); - parsingTable[{E,openB->getName()}] = ParsingTableEntry(Eprod); - parsingTable[{T,openB->getName()}] = ParsingTableEntry(Tprod); - parsingTable[{F,openB->getName()}] = ParsingTableEntry(Fprod1); - parsingTable[{E_,closeB->getName()}] = ParsingTableEntry("epsilon"); - parsingTable[{T_,closeB->getName()}] = ParsingTableEntry("epsilon"); - parsingTable[{E_,"$"}] = ParsingTableEntry("epsilon"); - parsingTable[{T_,"$"}] = ParsingTableEntry("epsilon"); - - return {E, parsingTable}; -} - -TEST(ParserManualTest, ValidInput) { - std::vector input = { - Token("a", "a"), - Token("b", "b"), - Token("b", "b"), - Token("a", "a"), - }; - - // S -> aBa - // B -> bB | eps - NonTerminal *S = new NonTerminal("S"); - NonTerminal *B = new NonTerminal("B"); - Terminal *a = new Terminal("a"); - Terminal *b = new Terminal("b"); - - std::vector S_production_1 = {a, B, a}; - std::vector B_production_1 = {b, B}; - - std::unordered_map, ParsingTableEntry, PairHash, PairEqual> parsingTable; - parsingTable[{S, a->getName()}] = ParsingTableEntry({a, B, a}); - parsingTable[{B, a->getName()}] = ParsingTableEntry("epsilon"); - parsingTable[{B, b->getName()}] = ParsingTableEntry({b, B}); - - Parser parser(S, parsingTable); - ParsingResult result = parser.parse(input); - result.tree.print(); - result.printTrace(); - result.printStackTrace(); -} - -TEST(ParserManualTest, ValidInput2) { - std::vector input = { - Token("id","x"), - Token("+",""), - Token("id","y"), - }; - - auto g = MathExprGrammar(); - auto startSymbol = g.first; - auto parsingTable = g.second; - - Parser parser(startSymbol, parsingTable); - ParsingResult result = parser.parse(input); - result.tree.print(); - result.printTrace(); -} - -TEST(ParserManualTest, InvalidInputExcessToken) { - std::vector input = { - Token("(","("), - Token("id","x"), - Token("+",""), - Token("id","y"), - Token(")",")"), - Token("*","*"), - Token("id","z"), - Token("id","w"), - Token("id","w"), - }; - - auto g = MathExprGrammar(); - auto startSymbol = g.first; - auto parsingTable = g.second; - - Parser parser(startSymbol, parsingTable); - ParsingResult result = parser.parse(input); - result.tree.print(); - result.printTrace(); -} - -TEST(ParserManualTest, InvalidInputMissingToken) { - // P-46 left example - NonTerminal* S = new NonTerminal("S"); - NonTerminal* A = new NonTerminal("A"); - Terminal* a = new Terminal("a"); - Terminal* b = new Terminal("b"); - Terminal* c = new Terminal("c"); - Terminal* d = new Terminal("d"); - Terminal* e = new Terminal("e"); - - std::vector S_production_1 = {A,b,S}; - std::vector S_production_2 = {e}; - std::vector A_production_1 = {a}; - std::vector A_production_2 = {c,A,d}; - - std::unordered_map, ParsingTableEntry, PairHash, PairEqual> parsingTable; - parsingTable[{S,a->getName()}] = ParsingTableEntry(S_production_1); - parsingTable[{A,a->getName()}] = ParsingTableEntry(A_production_1); - parsingTable[{A,b->getName()}] = ParsingTableEntry("sync"); - parsingTable[{S,c->getName()}] = ParsingTableEntry(S_production_1); - parsingTable[{A,c->getName()}] = ParsingTableEntry(A_production_2); - parsingTable[{A,d->getName()}] = ParsingTableEntry("sync"); - parsingTable[{S,e->getName()}] = ParsingTableEntry(S_production_2); - parsingTable[{S,"$"}] = ParsingTableEntry("epsilon"); - - - std::vector input = { - Token("a","a"), - Token("a","a"), - Token("b","b") - }; - - Parser parser(S, parsingTable); - ParsingResult result = parser.parse(input); - result.tree.print(); - result.printTrace(); -} - -TEST(ParserManualTest, InvalidInputExcessToken2) { - // P-46 left example - NonTerminal* S = new NonTerminal("S"); - NonTerminal* A = new NonTerminal("A"); - Terminal* a = new Terminal("a"); - Terminal* b = new Terminal("b"); - Terminal* c = new Terminal("c"); - Terminal* d = new Terminal("d"); - Terminal* e = new Terminal("e"); - - std::vector S_production_1 = {A,b,S}; - std::vector S_production_2 = {e}; - std::vector A_production_1 = {a}; - std::vector A_production_2 = {c,A,d}; - - std::unordered_map, ParsingTableEntry, PairHash, PairEqual> parsingTable; - parsingTable[{S,a->getName()}] = ParsingTableEntry(S_production_1); - parsingTable[{A,a->getName()}] = ParsingTableEntry(A_production_1); - parsingTable[{A,b->getName()}] = ParsingTableEntry("sync"); - parsingTable[{S,c->getName()}] = ParsingTableEntry(S_production_1); - parsingTable[{A,c->getName()}] = ParsingTableEntry(A_production_2); - parsingTable[{A,d->getName()}] = ParsingTableEntry("sync"); - parsingTable[{S,e->getName()}] = ParsingTableEntry(S_production_2); - parsingTable[{S,"$"}] = ParsingTableEntry("epsilon"); - - - std::vector input = { - Token("c","c"), - Token("e","e"), - Token("a","a"), - Token("d","d"), - Token("b","b"), - }; - - Parser parser(S, parsingTable); - ParsingResult result = parser.parse(input); - result.tree.print(); - result.printTrace(); -} - -TEST(ParserManualTest, InvalidInputEmptyWhileStackNot) { - std::vector input = { - Token("id", "x"), - Token("+", ""), - // Token("id","y"), - }; - - auto g = MathExprGrammar(); - auto startSymbol = g.first; - auto parsingTable = g.second; - - Parser parser(startSymbol, parsingTable); - ParsingResult result = parser.parse(input); - result.tree.print(); - result.printTrace(); -} +//#include +//#include "../SyntaxPhase/Common/NonTerminal.h" +//#include "../SyntaxPhase/GrammarParser/GrammarConverter.h" +//#include "../SyntaxPhase/GrammarParser/Grammar.h" +//#include "../SyntaxPhase/PredictiveParser/Token.h" +//#include "../SyntaxPhase/PredictiveParser/Parser.h" +// +//std::string g1Path = "../../GrammarTest.txt"; +// +//std::pair, ParsingTableEntry, PairHash, PairEqual>> MathExprGrammar() { +// // Lecture-Parsing-2 Example-2 p-26 +// NonTerminal* E = new NonTerminal("E"); +// NonTerminal* E_ = new NonTerminal("E'"); +// NonTerminal* T = new NonTerminal("T"); +// NonTerminal* T_ = new NonTerminal("T'"); +// NonTerminal* F = new NonTerminal("F"); +// Terminal* id = new Terminal("id"); +// Terminal* plus = new Terminal("+"); +// Terminal* mult = new Terminal("*"); +// Terminal* openB = new Terminal("("); +// Terminal* closeB = new Terminal(")"); +// +// std::vector Eprod = {T,E_}; +// std::vector E_prod = {plus,T,E_}; +// std::vector Tprod = {F,T_}; +// std::vector T_prod = {mult,F,T_}; +// std::vector Fprod1 = {openB,E,closeB}; +// std::vector Fprod2 = {id}; +// +// std::unordered_map, ParsingTableEntry, PairHash, PairEqual> parsingTable; +// parsingTable[{E,id->getName()}] = ParsingTableEntry(Eprod); +// parsingTable[{T,id->getName()}] = ParsingTableEntry(Tprod); +// parsingTable[{F,id->getName()}] = ParsingTableEntry(Fprod2); +// parsingTable[{E_,plus->getName()}] = ParsingTableEntry(E_prod); +// parsingTable[{T_,plus->getName()}] = ParsingTableEntry("epsilon"); +// parsingTable[{T_,mult->getName()}] = ParsingTableEntry(T_prod); +// parsingTable[{E,openB->getName()}] = ParsingTableEntry(Eprod); +// parsingTable[{T,openB->getName()}] = ParsingTableEntry(Tprod); +// parsingTable[{F,openB->getName()}] = ParsingTableEntry(Fprod1); +// parsingTable[{E_,closeB->getName()}] = ParsingTableEntry("epsilon"); +// parsingTable[{T_,closeB->getName()}] = ParsingTableEntry("epsilon"); +// parsingTable[{E_,"$"}] = ParsingTableEntry("epsilon"); +// parsingTable[{T_,"$"}] = ParsingTableEntry("epsilon"); +// +// return {E, parsingTable}; +//} +// +//TEST(ParserManualTest, ValidInput) { +// std::vector input = { +// Token("a", "a"), +// Token("b", "b"), +// Token("b", "b"), +// Token("a", "a"), +// }; +// +// // S -> aBa +// // B -> bB | eps +// NonTerminal *S = new NonTerminal("S"); +// NonTerminal *B = new NonTerminal("B"); +// Terminal *a = new Terminal("a"); +// Terminal *b = new Terminal("b"); +// +// std::vector S_production_1 = {a, B, a}; +// std::vector B_production_1 = {b, B}; +// +// std::unordered_map, ParsingTableEntry, PairHash, PairEqual> parsingTable; +// parsingTable[{S, a->getName()}] = ParsingTableEntry({a, B, a}); +// parsingTable[{B, a->getName()}] = ParsingTableEntry("epsilon"); +// parsingTable[{B, b->getName()}] = ParsingTableEntry({b, B}); +// +// Parser parser(S, parsingTable); +// ParsingResult result = parser.parse(input); +// result.tree.print(); +// result.printTrace(); +//} +// +//TEST(ParserManualTest, ValidInput2) { +// std::vector input = { +// Token("id","x"), +// Token("+",""), +// Token("id","y"), +// }; +// +// auto g = MathExprGrammar(); +// auto startSymbol = g.first; +// auto parsingTable = g.second; +// +// Parser parser(startSymbol, parsingTable); +// ParsingResult result = parser.parse(input); +// result.tree.print(); +// result.printTrace(); +//} +// +//TEST(ParserManualTest, InvalidInputExcessToken) { +// std::vector input = { +// Token("(","("), +// Token("id","x"), +// Token("+",""), +// Token("id","y"), +// Token(")",")"), +// Token("*","*"), +// Token("id","z"), +// Token("id","w"), +// Token("id","w"), +// }; +// +// auto g = MathExprGrammar(); +// auto startSymbol = g.first; +// auto parsingTable = g.second; +// +// Parser parser(startSymbol, parsingTable); +// ParsingResult result = parser.parse(input); +// result.tree.print(); +// result.printTrace(); +//} +// +//TEST(ParserManualTest, InvalidInputMissingToken) { +// // P-46 left example +// NonTerminal* S = new NonTerminal("S"); +// NonTerminal* A = new NonTerminal("A"); +// Terminal* a = new Terminal("a"); +// Terminal* b = new Terminal("b"); +// Terminal* c = new Terminal("c"); +// Terminal* d = new Terminal("d"); +// Terminal* e = new Terminal("e"); +// +// std::vector S_production_1 = {A,b,S}; +// std::vector S_production_2 = {e}; +// std::vector A_production_1 = {a}; +// std::vector A_production_2 = {c,A,d}; +// +// std::unordered_map, ParsingTableEntry, PairHash, PairEqual> parsingTable; +// parsingTable[{S,a->getName()}] = ParsingTableEntry(S_production_1); +// parsingTable[{A,a->getName()}] = ParsingTableEntry(A_production_1); +// parsingTable[{A,b->getName()}] = ParsingTableEntry("sync"); +// parsingTable[{S,c->getName()}] = ParsingTableEntry(S_production_1); +// parsingTable[{A,c->getName()}] = ParsingTableEntry(A_production_2); +// parsingTable[{A,d->getName()}] = ParsingTableEntry("sync"); +// parsingTable[{S,e->getName()}] = ParsingTableEntry(S_production_2); +// parsingTable[{S,"$"}] = ParsingTableEntry("epsilon"); +// +// +// std::vector input = { +// Token("a","a"), +// Token("a","a"), +// Token("b","b") +// }; +// +// Parser parser(S, parsingTable); +// ParsingResult result = parser.parse(input); +// result.tree.print(); +// result.printTrace(); +//} +// +//TEST(ParserManualTest, InvalidInputExcessToken2) { +// // P-46 left example +// NonTerminal* S = new NonTerminal("S"); +// NonTerminal* A = new NonTerminal("A"); +// Terminal* a = new Terminal("a"); +// Terminal* b = new Terminal("b"); +// Terminal* c = new Terminal("c"); +// Terminal* d = new Terminal("d"); +// Terminal* e = new Terminal("e"); +// +// std::vector S_production_1 = {A,b,S}; +// std::vector S_production_2 = {e}; +// std::vector A_production_1 = {a}; +// std::vector A_production_2 = {c,A,d}; +// +// std::unordered_map, ParsingTableEntry, PairHash, PairEqual> parsingTable; +// parsingTable[{S,a->getName()}] = ParsingTableEntry(S_production_1); +// parsingTable[{A,a->getName()}] = ParsingTableEntry(A_production_1); +// parsingTable[{A,b->getName()}] = ParsingTableEntry("sync"); +// parsingTable[{S,c->getName()}] = ParsingTableEntry(S_production_1); +// parsingTable[{A,c->getName()}] = ParsingTableEntry(A_production_2); +// parsingTable[{A,d->getName()}] = ParsingTableEntry("sync"); +// parsingTable[{S,e->getName()}] = ParsingTableEntry(S_production_2); +// parsingTable[{S,"$"}] = ParsingTableEntry("epsilon"); +// +// +// std::vector input = { +// Token("c","c"), +// Token("e","e"), +// Token("a","a"), +// Token("d","d"), +// Token("b","b"), +// }; +// +// Parser parser(S, parsingTable); +// ParsingResult result = parser.parse(input); +// result.tree.print(); +// result.printTrace(); +//} +// +//TEST(ParserManualTest, InvalidInputEmptyWhileStackNot) { +// std::vector input = { +// Token("id", "x"), +// Token("+", ""), +// // Token("id","y"), +// }; +// +// auto g = MathExprGrammar(); +// auto startSymbol = g.first; +// auto parsingTable = g.second; +// +// Parser parser(startSymbol, parsingTable); +// ParsingResult result = parser.parse(input); +// result.tree.print(); +// result.printTrace(); +//} From f01c157f9bdd1e0d1251d329df72135b7ce2c28b Mon Sep 17 00:00:00 2001 From: Deffo0 Date: Tue, 23 Jan 2024 18:29:31 +0200 Subject: [PATCH 25/41] some refactoring for inputs and outputs --- CMakeLists.txt | 2 +- main.cpp => Driver.cpp | 0 Grammar.txt => Inputs/Grammar.txt | 0 GrammarTest.txt => Inputs/GrammarTest.txt | 4 +--- Rules.txt => Inputs/Rules.txt | 0 {Z.SampleTests => Inputs/Z.SampleTests}/1 | 0 {Z.SampleTests => Inputs/Z.SampleTests}/2 | 0 {Z.SampleTests => Inputs/Z.SampleTests}/3 | 0 input.txt => Inputs/input.txt | 0 Outputs/parsingTable.csv | 18 ++++++++++++++++++ README.md | 0 SyntaxPhase/PredictiveParser/Parser.cpp | 2 +- 12 files changed, 21 insertions(+), 5 deletions(-) rename main.cpp => Driver.cpp (100%) rename Grammar.txt => Inputs/Grammar.txt (100%) rename GrammarTest.txt => Inputs/GrammarTest.txt (71%) rename Rules.txt => Inputs/Rules.txt (100%) rename {Z.SampleTests => Inputs/Z.SampleTests}/1 (100%) rename {Z.SampleTests => Inputs/Z.SampleTests}/2 (100%) rename {Z.SampleTests => Inputs/Z.SampleTests}/3 (100%) rename input.txt => Inputs/input.txt (100%) create mode 100644 Outputs/parsingTable.csv create mode 100644 README.md diff --git a/CMakeLists.txt b/CMakeLists.txt index 66af090..b553814 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -3,7 +3,7 @@ project(Compiler) set(CMAKE_CXX_STANDARD 20) -add_executable(Compiler main.cpp) +add_executable(Compiler Driver.cpp) target_link_libraries(Compiler Util_lib) target_link_libraries(Compiler RulesParser_lib) diff --git a/main.cpp b/Driver.cpp similarity index 100% rename from main.cpp rename to Driver.cpp diff --git a/Grammar.txt b/Inputs/Grammar.txt similarity index 100% rename from Grammar.txt rename to Inputs/Grammar.txt diff --git a/GrammarTest.txt b/Inputs/GrammarTest.txt similarity index 71% rename from GrammarTest.txt rename to Inputs/GrammarTest.txt index ee54ebb..d7cc4c2 100644 --- a/GrammarTest.txt +++ b/Inputs/GrammarTest.txt @@ -4,6 +4,4 @@ # T` ::= T | '\\L' # F ::= P F` # F` ::= '*' F | '\\L' -# P ::= '(' E ')' | 'a' | 'b' | 'Em' - -pukk \ No newline at end of file +# P ::= '(' E ')' | 'a' | 'b' | 'Em' \ No newline at end of file diff --git a/Rules.txt b/Inputs/Rules.txt similarity index 100% rename from Rules.txt rename to Inputs/Rules.txt diff --git a/Z.SampleTests/1 b/Inputs/Z.SampleTests/1 similarity index 100% rename from Z.SampleTests/1 rename to Inputs/Z.SampleTests/1 diff --git a/Z.SampleTests/2 b/Inputs/Z.SampleTests/2 similarity index 100% rename from Z.SampleTests/2 rename to Inputs/Z.SampleTests/2 diff --git a/Z.SampleTests/3 b/Inputs/Z.SampleTests/3 similarity index 100% rename from Z.SampleTests/3 rename to Inputs/Z.SampleTests/3 diff --git a/input.txt b/Inputs/input.txt similarity index 100% rename from input.txt rename to Inputs/input.txt diff --git a/Outputs/parsingTable.csv b/Outputs/parsingTable.csv new file mode 100644 index 0000000..fef9ff0 --- /dev/null +++ b/Outputs/parsingTable.csv @@ -0,0 +1,18 @@ +,$,(,),+,-,;,addop,float,id,if,int,mulop,num,relop,while,}, +STATEMENT_LIST,sync,N/A,N/A,N/A,N/A,N/A,N/A,--> STATEMENT STATEMENT_LIST` ,--> STATEMENT STATEMENT_LIST` ,--> STATEMENT STATEMENT_LIST` ,--> STATEMENT STATEMENT_LIST` ,N/A,N/A,N/A,--> STATEMENT STATEMENT_LIST` ,N/A, +STATEMENT_LIST`,epsilon,N/A,N/A,N/A,N/A,N/A,N/A,--> STATEMENT STATEMENT_LIST` ,--> STATEMENT STATEMENT_LIST` ,--> STATEMENT STATEMENT_LIST` ,--> STATEMENT STATEMENT_LIST` ,N/A,N/A,N/A,--> STATEMENT STATEMENT_LIST` ,N/A, +IF,sync,N/A,N/A,N/A,N/A,N/A,N/A,sync,sync,--> if ( EXPRESSION ) { STATEMENT } else { STATEMENT } ,sync,N/A,N/A,N/A,sync,sync, +DECLARATION,sync,N/A,N/A,N/A,N/A,N/A,N/A,--> PRIMITIVE_TYPE id ; ,sync,sync,--> PRIMITIVE_TYPE id ; ,N/A,N/A,N/A,sync,sync, +FACTOR,N/A,--> ( EXPRESSION ) ,sync,N/A,N/A,sync,sync,N/A,--> id ,N/A,N/A,sync,--> num ,sync,N/A,N/A, +EXPRESSION,N/A,--> SIMPLE_EXPRESSION EXPRESSION1 ,sync,--> SIMPLE_EXPRESSION EXPRESSION1 ,--> SIMPLE_EXPRESSION EXPRESSION1 ,sync,N/A,N/A,--> SIMPLE_EXPRESSION EXPRESSION1 ,N/A,N/A,N/A,--> SIMPLE_EXPRESSION EXPRESSION1 ,N/A,N/A,N/A, +PRIMITIVE_TYPE,N/A,N/A,N/A,N/A,N/A,N/A,N/A,--> float ,sync,N/A,--> int ,N/A,N/A,N/A,N/A,N/A, +STATEMENT,sync,N/A,N/A,N/A,N/A,N/A,N/A,--> DECLARATION ,--> ASSIGNMENT ,--> IF ,--> DECLARATION ,N/A,N/A,N/A,--> WHILE ,sync, +ASSIGNMENT,sync,N/A,N/A,N/A,N/A,N/A,N/A,sync,--> id = EXPRESSION ; ,sync,sync,N/A,N/A,N/A,sync,sync, +SIMPLE_EXPRESSION,N/A,--> TERM SIMPLE_EXPRESSION` ,sync,--> SIGN TERM SIMPLE_EXPRESSION` ,--> SIGN TERM SIMPLE_EXPRESSION` ,sync,N/A,N/A,--> TERM SIMPLE_EXPRESSION` ,N/A,N/A,N/A,--> TERM SIMPLE_EXPRESSION` ,sync,N/A,N/A, +TERM`,N/A,N/A,epsilon,N/A,N/A,epsilon,epsilon,N/A,N/A,N/A,N/A,--> mulop FACTOR TERM` ,N/A,epsilon,N/A,N/A, +METHOD_BODY,sync,N/A,N/A,N/A,N/A,N/A,N/A,--> STATEMENT_LIST ,--> STATEMENT_LIST ,--> STATEMENT_LIST ,--> STATEMENT_LIST ,N/A,N/A,N/A,--> STATEMENT_LIST ,N/A, +SIMPLE_EXPRESSION`,N/A,N/A,epsilon,N/A,N/A,epsilon,--> addop TERM SIMPLE_EXPRESSION` ,N/A,N/A,N/A,N/A,N/A,N/A,epsilon,N/A,N/A, +EXPRESSION1,N/A,N/A,epsilon,N/A,N/A,epsilon,N/A,N/A,N/A,N/A,N/A,N/A,N/A,--> relop SIMPLE_EXPRESSION ,N/A,N/A, +WHILE,sync,N/A,N/A,N/A,N/A,N/A,N/A,sync,sync,sync,sync,N/A,N/A,N/A,--> while ( EXPRESSION ) { STATEMENT } ,sync, +SIGN,N/A,sync,N/A,--> + ,--> - ,N/A,N/A,N/A,sync,N/A,N/A,N/A,sync,N/A,N/A,N/A, +TERM,N/A,--> FACTOR TERM` ,sync,N/A,N/A,sync,sync,N/A,--> FACTOR TERM` ,N/A,N/A,N/A,--> FACTOR TERM` ,sync,N/A,N/A, diff --git a/README.md b/README.md new file mode 100644 index 0000000..e69de29 diff --git a/SyntaxPhase/PredictiveParser/Parser.cpp b/SyntaxPhase/PredictiveParser/Parser.cpp index 2fdb014..3691c51 100644 --- a/SyntaxPhase/PredictiveParser/Parser.cpp +++ b/SyntaxPhase/PredictiveParser/Parser.cpp @@ -226,7 +226,7 @@ void Parser::printParsingTable() { void Parser::writeParsingTableToCSV() { - std::string filename = "./parsingTable.csv"; + std::string filename = "../Outputs/parsingTable.csv"; std::unordered_set nonTerminalsSet; std::unordered_set terminalsSet; From b1c5cc0d7d732d8b758ced368ba903434c22dee5 Mon Sep 17 00:00:00 2001 From: Deffo0 Date: Tue, 23 Jan 2024 19:11:13 +0200 Subject: [PATCH 26/41] Documentation --- README.md | 40 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/README.md b/README.md index e69de29..e3e3e25 100644 --- a/README.md +++ b/README.md @@ -0,0 +1,40 @@ +# Compiler Generator +## Overview +> This is a plug and play compiler which can accommodate with any language once you provide it with its lexical rules and grammar. + +## Authors ++ [Ahmed Adel Abudef](https://github.com/Deffo0) ++ [Abdelmeniem Hany](https://github.com/Ghost8345) ++ [Youssef Saeed](https://github.com/usefSaeed) ++ [Zyad Samy](https://github.com/ZyadSamy) + +## Setup +1. Clone the repo. +``` +git clone https://github.com/Ghost8345/Compiler-Generator.git +``` +2. Load CMakeLists.txt to your project. +3. Add the `Lexical Rules` and `Grammar` as program arguments for `Driver.cpp`. +4. Compile `Driver.cpp`. +5. Run the compiled version. + +## System Flow Chart + +## Inputs ++ Lexical Rules ++ Grammar ++ Language Code (Program) + +## Outputs ++ Stored Data in Files + + Parsing Table ++ Console Output + + Symbol Table + + Parsing Tree + + Parsing Stack Trace + + Production Output + + Errors Reporting + +## What Next 🤔 ++ Implement the Syntax Directed Translation Scheme and Type checkers. ++ Implement the Intermediate Code Generation Phase. \ No newline at end of file From 0f5eabeb14c23a9d06b02b57185683da3d541938 Mon Sep 17 00:00:00 2001 From: Deffo0 Date: Tue, 23 Jan 2024 19:20:42 +0200 Subject: [PATCH 27/41] Documentation --- docs/Images/GeneralizedCompilerFlowchart.jpg | Bin 0 -> 74178 bytes docs/Images/Logo.png | Bin 0 -> 64494 bytes 2 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 docs/Images/GeneralizedCompilerFlowchart.jpg create mode 100644 docs/Images/Logo.png diff --git a/docs/Images/GeneralizedCompilerFlowchart.jpg b/docs/Images/GeneralizedCompilerFlowchart.jpg new file mode 100644 index 0000000000000000000000000000000000000000..66e00c1db89d820ec3268fe039c8f5a8aedc1866 GIT binary patch literal 74178 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%0D%q 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 literal 0 HcmV?d00001 diff --git a/docs/Images/Logo.png b/docs/Images/Logo.png new file mode 100644 index 0000000000000000000000000000000000000000..1acb250dcc797c8959c12e404a0f399ef56adc5f GIT binary patch literal 64494 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

II)*g$*{pfrB0Qg3FQ&9jPoyR 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�zNnNhg_-@$8cZh_F@{BfQeba5e}#Pl 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 Date: Tue, 23 Jan 2024 19:33:36 +0200 Subject: [PATCH 28/41] Add files via upload --- docs/Images/CompilerGenerator.png | Bin 0 -> 13237 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 docs/Images/CompilerGenerator.png diff --git a/docs/Images/CompilerGenerator.png b/docs/Images/CompilerGenerator.png new file mode 100644 index 0000000000000000000000000000000000000000..307f4bf15f2ad74857e5b842e3893a1c4868e97b GIT binary patch literal 13237 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 Date: Tue, 23 Jan 2024 19:49:32 +0200 Subject: [PATCH 29/41] Update README.md --- README.md | 21 ++++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index e3e3e25..422197c 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,8 @@ # Compiler Generator +

+ Logo +

+ ## Overview > This is a plug and play compiler which can accommodate with any language once you provide it with its lexical rules and grammar. @@ -13,12 +17,19 @@ ``` git clone https://github.com/Ghost8345/Compiler-Generator.git ``` -2. Load CMakeLists.txt to your project. -3. Add the `Lexical Rules` and `Grammar` as program arguments for `Driver.cpp`. -4. Compile `Driver.cpp`. -5. Run the compiled version. +1. Change the directory to the cloned repo. +``` +cd Compiler-Generator +``` +1. Load CMakeLists.txt to your project. +1. Add the `Lexical Rules` and `Grammar` paths as program arguments for `Driver.cpp`. +1. Compile `Driver.cpp`. +1. Run the compiled version. ## System Flow Chart +

+ Logo +

## Inputs + Lexical Rules @@ -37,4 +48,4 @@ git clone https://github.com/Ghost8345/Compiler-Generator.git ## What Next 🤔 + Implement the Syntax Directed Translation Scheme and Type checkers. -+ Implement the Intermediate Code Generation Phase. \ No newline at end of file ++ Implement the Intermediate Code Generation Phase. From 89f7cd38286eeef371de1f9c415003ad5a4c8dac Mon Sep 17 00:00:00 2001 From: Ahmed Adel <73228199+Deffo0@users.noreply.github.com> Date: Tue, 23 Jan 2024 20:15:24 +0200 Subject: [PATCH 30/41] Create Lexical Phase Documentation.md --- docs/Lexical Phase Documentation.md | 76 +++++++++++++++++++++++++++++ 1 file changed, 76 insertions(+) create mode 100644 docs/Lexical Phase Documentation.md diff --git a/docs/Lexical Phase Documentation.md b/docs/Lexical Phase Documentation.md new file mode 100644 index 0000000..ba32460 --- /dev/null +++ b/docs/Lexical Phase Documentation.md @@ -0,0 +1,76 @@ + +# Lexical Phase Documentation +## Table of Contents ++ [Rules File to RE](Rules-File-to-RE) ++ [RE to NFA](RE-to-NFA) ++ [NFA to DFA](NFA-to-DFA) ++ [Symbol Table Generator](Symbol-Table-Generator) + +## Rules File to RE +#### Description +The first part handles parsing the rules file and transforming it into a normalized standardized regular expression that can be easily turned into an NFA. + +#### Steps +1. Read the rules file line by line. +2. Detect if it's a keyword, punctuation, regular definition, or regular expression, and handle the cases appropriately. Exit gracefully if there is an error or an unstructured line. +3. Handle keywords and punctuation by assigning each one its own regular expression with the same name and token type. +4. Standardize regular definitions and expressions. +5. Return a list of standardized regular expressions. + +#### Data Structures +- `Vector allKeywords`: Holds all keywords while parsing the rules file. +- `Vector allPunctuation`: Holds all punctuation while parsing the rules file. +- `Vector regularDefinitions`: Holds all regular definitions. +- `Vector regularExpressions`: Holds the final regular expressions. + +#### Assumptions +- Keywords and punctuation must be separated by a space. +- '=' and ':' are added to the reserved symbols that must be escaped by ''. +- Space doesn't matter in disjunction, closure, or range. +- Keywords and punctuation can come anywhere in the rules file, and they will have the highest priority. +- Other regular expressions' priorities will be based on their precedence in the rules file. +- Regular Definitions must be defined before being used. + +## RE to NFA +#### Description +The second part converts the regular expression with its operations to a non-finite automaton (NFA). + +#### Steps +1. Construct fine-grain classes for NFA graph. +2. Define NFA arms and brain, considering parentheses. +3. Define Thompson algorithm transformations. +4. Walk through the regExp and convert it to NFA represented by the start state. +5. Combine NFAs into one. +6. Convert the graph to a map for easier reading and conversions (using DFS). + +#### Data Structures +- `Vector transitions`: Holds all transitions in each state. +- `stack> nfaStack`: Holds the start and end state of each NFA during conversion. +- `stack> disjunctionStack`: Holds disjunction operations in each parentheses level. +- `unordered_map, vector> transitionTable`: Represents the adjacency matrix between states. + +#### Assumptions +- All disjunction operands must be surrounded by brackets (). + +## NFA to DFA +#### Description +The third part turns the NFA into a DFA with minimized states. + +#### Steps +1. Use the epsilon closure of the NFA start state as a seed to the unmarked stack. +2. While unmarked states are not empty, repeat for each state T and each symbol a: + - Find the epsilon closure of states resulting from moving state T using symbol a. + - If the set of states doesn't have a DFA state mapping, create a new state with the token of the highest priority. + +#### Data Structures +- `unordered_map, State*>`: Mapping from a set of NFA states to a DFA state. +- `stack>`: Holds unmarked states. + +#### Assumptions + +## Symbol Table Generator +#### Description + SimplyTakes the DFA as input and the input code file satisfying the grammar rules to generate the symbol table. +Takes the DFA as input and the input code file satisfying the grammar rules to generate the symbol table. + it takes the DFA as input to initiate the constructor, so it has all the main states and all their transitions. Then it takes the input code file that supposedly satisfies the grammar rules that generated the DFA. The output of this part is the symbol table, where it has all the strings that represent a token and their corresponding token names, the syntax errors representing the indices and the characters that didn’t satisfy the DFA, and the trace which tells us how exactly the symbol table was generated and what happened in each state transition. + ``` From 1d496986f910c47a9982c6aeb8e1917a3c0a153d Mon Sep 17 00:00:00 2001 From: Ahmed Adel <73228199+Deffo0@users.noreply.github.com> Date: Tue, 23 Jan 2024 20:16:38 +0200 Subject: [PATCH 31/41] Update Lexical Phase Documentation.md --- docs/Lexical Phase Documentation.md | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/docs/Lexical Phase Documentation.md b/docs/Lexical Phase Documentation.md index ba32460..676469a 100644 --- a/docs/Lexical Phase Documentation.md +++ b/docs/Lexical Phase Documentation.md @@ -1,10 +1,10 @@ # Lexical Phase Documentation ## Table of Contents -+ [Rules File to RE](Rules-File-to-RE) -+ [RE to NFA](RE-to-NFA) -+ [NFA to DFA](NFA-to-DFA) -+ [Symbol Table Generator](Symbol-Table-Generator) ++ [Rules File to RE](#Rules-File-to-RE) ++ [RE to NFA](#RE-to-NFA) ++ [NFA to DFA](#NFA-to-DFA) ++ [Symbol Table Generator](#Symbol-Table-Generator) ## Rules File to RE #### Description @@ -73,4 +73,3 @@ The third part turns the NFA into a DFA with minimized states. SimplyTakes the DFA as input and the input code file satisfying the grammar rules to generate the symbol table. Takes the DFA as input and the input code file satisfying the grammar rules to generate the symbol table. it takes the DFA as input to initiate the constructor, so it has all the main states and all their transitions. Then it takes the input code file that supposedly satisfies the grammar rules that generated the DFA. The output of this part is the symbol table, where it has all the strings that represent a token and their corresponding token names, the syntax errors representing the indices and the characters that didn’t satisfy the DFA, and the trace which tells us how exactly the symbol table was generated and what happened in each state transition. - ``` From 2c4a7f29c8cd03756b40ede90d23516b437d0de2 Mon Sep 17 00:00:00 2001 From: Ahmed Adel <73228199+Deffo0@users.noreply.github.com> Date: Tue, 23 Jan 2024 20:27:37 +0200 Subject: [PATCH 32/41] Create Syntax Phase Documentation.md --- docs/Syntax Phase Documentation.md | 83 ++++++++++++++++++++++++++++++ 1 file changed, 83 insertions(+) create mode 100644 docs/Syntax Phase Documentation.md diff --git a/docs/Syntax Phase Documentation.md b/docs/Syntax Phase Documentation.md new file mode 100644 index 0000000..ef484ec --- /dev/null +++ b/docs/Syntax Phase Documentation.md @@ -0,0 +1,83 @@ + +# Syntax Phase Documentation + +## Table of Contents ++ [Grammar Reader and Converter](#Grammar-Reader-and-Converter) ++ [First and Follow Generator](#First-and-Follow-Generator) ++ [Predictive Parser: Parsing Table](#Parsing-Table) ++ [Predictive Parser: Parsing](#Parsing) + +## Grammar Reader and Converter +#### Description +The first part handles parsing the Grammar rules file and turning it into a standardized list of Non-Terminals and Start Symbol. + +#### Steps +1. (1st Pass) Read the Grammar rules file line by line, validate correctness, and detect undefined or doubly defined Non-Terminals. +2. (2nd Pass) Read the Grammar rules file line by line, find Terminals in Productions, handle escaped reserved characters, populate Non-Terminal productions. +3. Apply Left Factoring (bonus) and Eliminate Left Recursion (bonus). +4. Return a list of standardized Non-Terminals and the Non-Terminal Start Symbol. + +#### Data Structures +- `std::unordered_set terminals`: Holds all Terminal names in the grammar. +- `std::unordered_set nonTerminalNames`: Holds all Non-Terminal names in the grammar. +- `std::vector nonTerminals`: Holds all Non-Terminals with their productions. + +#### Assumptions +- Reserved characters need to be escaped to be used as a Terminal. +- The Start Symbol is the first Non-Terminal in the grammar file. +- No escaped characters are present in the Non-Terminal Name. + +## First and Follow Generator +#### Description +This part generates the First and Follow sets for each nonterminal. + +#### Steps +1. Compute First sets for nonterminals. +2. Compute Follow sets for nonterminals. +3. Handle epsilon and locked nonterminals to avoid infinite recursion. + +#### Data Structures +- `std::vector> NTs`: Holds all non-terminals of the grammar. +- `std::unordered_set firstSet`: First set attribute in each non-terminal. +- `std::unordered_set followSet`: Follow set attribute in each non-terminal. +- `std::unordered_set lockedNTs`: Contains non-terminals being computed to eliminate infinite recursion. + +#### Assumptions +- Input grammar should be properly formatted with all productions. +- Input grammar should be left-refactored and free of any left recursion. +- Nonterminal vector size equals the number of nonterminals in the grammar. + +## Parsing Table +#### Description +This part constructs the parsing table for predictive parsing. + +#### Steps +1. Compute NTs with First Set and Follow Set. +2. Search for matched production in the First set. +3. Construct the parsing table based on First and Follow sets. +4. Print and export the parsing table. + +#### Data Structures +- `std::vector> NTs`: Holds all non-terminals of the grammar. +- `std::unordered_map, ParsingTableEntry> parsingTable`: Holds the parsing table. + +#### Assumptions +- Sync and Epsilon represented by boolean vars inside the ParsingTableEntry. + +## Parsing +#### Description +This part handles the actual parsing, generating the parsing tree, and error recovery. + +#### Steps +1. Seed the stack with the Start Symbol and repeat until the stack is empty. +2. Match or handle errors based on the parsing table entry and input token. +3. Implement panic-mode error recovery. + +#### Data Structures +- `std::stack stack`: Stack for parsing. +- `std::stack nodes`: Stack for parsing tree nodes. +- `std::vector traces`: Keeps track of traces while parsing. +- `ParsingTreeNode`: Used for creating the parsing tree. +- `ParsingTree`: Wrapper for the root node. +- `ParsingTrace`: Used for keeping track of parsing traces. +- `ParsingResult`: Wrapper that contains traces and the final parsing tree. From df0069de24e7ce2178f11b07db9beb7c79d1cc69 Mon Sep 17 00:00:00 2001 From: Ahmed Adel <73228199+Deffo0@users.noreply.github.com> Date: Tue, 23 Jan 2024 20:29:59 +0200 Subject: [PATCH 33/41] Update README.md --- README.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/README.md b/README.md index 422197c..10bbe94 100644 --- a/README.md +++ b/README.md @@ -31,6 +31,9 @@ cd Compiler-Generator Logo

+## Documentation ++ [Lexical Phase Documentation](https://github.com/Ghost8345/Compiler-Generator/blob/docs/docs/Lexical%20Phase%20Documentation.md) ++ [Syntax Phase Documentation](https://github.com/Ghost8345/Compiler-Generator/blob/docs/docs/Syntax%20Phase%20Documentation.md) ## Inputs + Lexical Rules + Grammar From 6909e3a5b58f19cdf49f0e2e783d42f70c06e54d Mon Sep 17 00:00:00 2001 From: Ahmed Adel <73228199+Deffo0@users.noreply.github.com> Date: Tue, 23 Jan 2024 20:33:11 +0200 Subject: [PATCH 34/41] Update README.md --- README.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 10bbe94..bcec126 100644 --- a/README.md +++ b/README.md @@ -17,14 +17,14 @@ ``` git clone https://github.com/Ghost8345/Compiler-Generator.git ``` -1. Change the directory to the cloned repo. +2. Change the directory to the cloned repo. ``` cd Compiler-Generator ``` -1. Load CMakeLists.txt to your project. -1. Add the `Lexical Rules` and `Grammar` paths as program arguments for `Driver.cpp`. -1. Compile `Driver.cpp`. -1. Run the compiled version. +3. Load CMakeLists.txt to your project. +4. Add the `Lexical Rules` and `Grammar` paths as program arguments for `Driver.cpp`. +5. Compile `Driver.cpp`. +6. Run the compiled version. ## System Flow Chart

From 4cd557bcfbc61321d4d795f631d670aca98ae0df Mon Sep 17 00:00:00 2001 From: Ahmed Adel <73228199+Deffo0@users.noreply.github.com> Date: Tue, 23 Jan 2024 20:34:32 +0200 Subject: [PATCH 35/41] Delete .DS_Store --- .DS_Store | Bin 6148 -> 0 bytes 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 .DS_Store diff --git a/.DS_Store b/.DS_Store deleted file mode 100644 index 9c5fca97958e73537e97c843bb97ec8271859202..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 6148 zcmeHKK~BRk5FA4eg_P1G7o@zP50Z<1}p$i7vOiE0ii?7&*FR2oZZKIDf3^B!wuBHm8 z0)JfrnY%R{;T#=2B0GN@oW%>wPLpx3n~b}xCyT@(z42*{IiQLYjCl=^kbO%|-_8F5 z@4dha$$MtZ5w8w&rP0iOfwfy;UHNF`?AHRj%GIgQF%8jU%mf#BAWqTG_tkt9coo~n zA)eUF9wwON>u(GS^+UESV`9rp*F>?bw~UfoxWwG5I4xHLqG6U1qKy~!YQ|`bb#dg4 znOY`JW%-WERTIs#Q370(F+`FdwGnl*^>%Q>ez)0|5jB#p;rs1O3rx{B%jB)vFDSr0 zTdWc~)KnEv1yq5h0d-D7OzaVW7_itH^YWV@oXBJBF?Gl%G~=j5M>YP4 zVH}<1k*^nfOdUEpj6ZxBAKCa5im}nzeq^h|#11u81yq5&0-Nr(F8lxS`tyIDq<5-- zD)6rqFhQ%`YH~<^Z(SLj?6nd7jxHwkQiqnp#BawoBU|wv-5Q@ok|D+(Q->^}>5qVw LK@(NrM-})2$r8AZ From f59b2524276852aeca947b2d25a0b0503bfe3a2c Mon Sep 17 00:00:00 2001 From: Ahmed Adel <73228199+Deffo0@users.noreply.github.com> Date: Tue, 23 Jan 2024 20:39:31 +0200 Subject: [PATCH 36/41] Update README.md --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index bcec126..a304093 100644 --- a/README.md +++ b/README.md @@ -3,6 +3,7 @@ Logo

+ ## Overview > This is a plug and play compiler which can accommodate with any language once you provide it with its lexical rules and grammar. From 9ca6136916efdba64c06b30c5cca0c0253f42e3a Mon Sep 17 00:00:00 2001 From: Deffo0 Date: Tue, 23 Jan 2024 20:43:43 +0200 Subject: [PATCH 37/41] merge --- Outputs/parsingTable.csv | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/Outputs/parsingTable.csv b/Outputs/parsingTable.csv index fef9ff0..c7c5c51 100644 --- a/Outputs/parsingTable.csv +++ b/Outputs/parsingTable.csv @@ -1,18 +1,18 @@ ,$,(,),+,-,;,addop,float,id,if,int,mulop,num,relop,while,}, STATEMENT_LIST,sync,N/A,N/A,N/A,N/A,N/A,N/A,--> STATEMENT STATEMENT_LIST` ,--> STATEMENT STATEMENT_LIST` ,--> STATEMENT STATEMENT_LIST` ,--> STATEMENT STATEMENT_LIST` ,N/A,N/A,N/A,--> STATEMENT STATEMENT_LIST` ,N/A, -STATEMENT_LIST`,epsilon,N/A,N/A,N/A,N/A,N/A,N/A,--> STATEMENT STATEMENT_LIST` ,--> STATEMENT STATEMENT_LIST` ,--> STATEMENT STATEMENT_LIST` ,--> STATEMENT STATEMENT_LIST` ,N/A,N/A,N/A,--> STATEMENT STATEMENT_LIST` ,N/A, -IF,sync,N/A,N/A,N/A,N/A,N/A,N/A,sync,sync,--> if ( EXPRESSION ) { STATEMENT } else { STATEMENT } ,sync,N/A,N/A,N/A,sync,sync, DECLARATION,sync,N/A,N/A,N/A,N/A,N/A,N/A,--> PRIMITIVE_TYPE id ; ,sync,sync,--> PRIMITIVE_TYPE id ; ,N/A,N/A,N/A,sync,sync, -FACTOR,N/A,--> ( EXPRESSION ) ,sync,N/A,N/A,sync,sync,N/A,--> id ,N/A,N/A,sync,--> num ,sync,N/A,N/A, -EXPRESSION,N/A,--> SIMPLE_EXPRESSION EXPRESSION1 ,sync,--> SIMPLE_EXPRESSION EXPRESSION1 ,--> SIMPLE_EXPRESSION EXPRESSION1 ,sync,N/A,N/A,--> SIMPLE_EXPRESSION EXPRESSION1 ,N/A,N/A,N/A,--> SIMPLE_EXPRESSION EXPRESSION1 ,N/A,N/A,N/A, -PRIMITIVE_TYPE,N/A,N/A,N/A,N/A,N/A,N/A,N/A,--> float ,sync,N/A,--> int ,N/A,N/A,N/A,N/A,N/A, -STATEMENT,sync,N/A,N/A,N/A,N/A,N/A,N/A,--> DECLARATION ,--> ASSIGNMENT ,--> IF ,--> DECLARATION ,N/A,N/A,N/A,--> WHILE ,sync, +IF,sync,N/A,N/A,N/A,N/A,N/A,N/A,sync,sync,--> if ( EXPRESSION ) { STATEMENT } else { STATEMENT } ,sync,N/A,N/A,N/A,sync,sync, ASSIGNMENT,sync,N/A,N/A,N/A,N/A,N/A,N/A,sync,--> id = EXPRESSION ; ,sync,sync,N/A,N/A,N/A,sync,sync, +EXPRESSION1,N/A,N/A,epsilon,N/A,N/A,epsilon,N/A,N/A,N/A,N/A,N/A,N/A,N/A,--> relop SIMPLE_EXPRESSION ,N/A,N/A, +TERM,N/A,--> FACTOR TERM` ,sync,N/A,N/A,sync,sync,N/A,--> FACTOR TERM` ,N/A,N/A,N/A,--> FACTOR TERM` ,sync,N/A,N/A, +STATEMENT_LIST`,epsilon,N/A,N/A,N/A,N/A,N/A,N/A,--> STATEMENT STATEMENT_LIST` ,--> STATEMENT STATEMENT_LIST` ,--> STATEMENT STATEMENT_LIST` ,--> STATEMENT STATEMENT_LIST` ,N/A,N/A,N/A,--> STATEMENT STATEMENT_LIST` ,N/A, SIMPLE_EXPRESSION,N/A,--> TERM SIMPLE_EXPRESSION` ,sync,--> SIGN TERM SIMPLE_EXPRESSION` ,--> SIGN TERM SIMPLE_EXPRESSION` ,sync,N/A,N/A,--> TERM SIMPLE_EXPRESSION` ,N/A,N/A,N/A,--> TERM SIMPLE_EXPRESSION` ,sync,N/A,N/A, +FACTOR,N/A,--> ( EXPRESSION ) ,sync,N/A,N/A,sync,sync,N/A,--> id ,N/A,N/A,sync,--> num ,sync,N/A,N/A, +EXPRESSION,N/A,--> SIMPLE_EXPRESSION EXPRESSION1 ,sync,--> SIMPLE_EXPRESSION EXPRESSION1 ,--> SIMPLE_EXPRESSION EXPRESSION1 ,sync,N/A,N/A,--> SIMPLE_EXPRESSION EXPRESSION1 ,N/A,N/A,N/A,--> SIMPLE_EXPRESSION EXPRESSION1 ,N/A,N/A,N/A, TERM`,N/A,N/A,epsilon,N/A,N/A,epsilon,epsilon,N/A,N/A,N/A,N/A,--> mulop FACTOR TERM` ,N/A,epsilon,N/A,N/A, METHOD_BODY,sync,N/A,N/A,N/A,N/A,N/A,N/A,--> STATEMENT_LIST ,--> STATEMENT_LIST ,--> STATEMENT_LIST ,--> STATEMENT_LIST ,N/A,N/A,N/A,--> STATEMENT_LIST ,N/A, +STATEMENT,sync,N/A,N/A,N/A,N/A,N/A,N/A,--> DECLARATION ,--> ASSIGNMENT ,--> IF ,--> DECLARATION ,N/A,N/A,N/A,--> WHILE ,sync, SIMPLE_EXPRESSION`,N/A,N/A,epsilon,N/A,N/A,epsilon,--> addop TERM SIMPLE_EXPRESSION` ,N/A,N/A,N/A,N/A,N/A,N/A,epsilon,N/A,N/A, -EXPRESSION1,N/A,N/A,epsilon,N/A,N/A,epsilon,N/A,N/A,N/A,N/A,N/A,N/A,N/A,--> relop SIMPLE_EXPRESSION ,N/A,N/A, -WHILE,sync,N/A,N/A,N/A,N/A,N/A,N/A,sync,sync,sync,sync,N/A,N/A,N/A,--> while ( EXPRESSION ) { STATEMENT } ,sync, +PRIMITIVE_TYPE,N/A,N/A,N/A,N/A,N/A,N/A,N/A,--> float ,sync,N/A,--> int ,N/A,N/A,N/A,N/A,N/A, SIGN,N/A,sync,N/A,--> + ,--> - ,N/A,N/A,N/A,sync,N/A,N/A,N/A,sync,N/A,N/A,N/A, -TERM,N/A,--> FACTOR TERM` ,sync,N/A,N/A,sync,sync,N/A,--> FACTOR TERM` ,N/A,N/A,N/A,--> FACTOR TERM` ,sync,N/A,N/A, +WHILE,sync,N/A,N/A,N/A,N/A,N/A,N/A,sync,sync,sync,sync,N/A,N/A,N/A,--> while ( EXPRESSION ) { STATEMENT } ,sync, From f5692e0f8883d598e2519f8f078d868440160183 Mon Sep 17 00:00:00 2001 From: Deffo0 Date: Tue, 23 Jan 2024 20:43:43 +0200 Subject: [PATCH 38/41] merge --- Outputs/parsingTable.csv | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/Outputs/parsingTable.csv b/Outputs/parsingTable.csv index fef9ff0..c7c5c51 100644 --- a/Outputs/parsingTable.csv +++ b/Outputs/parsingTable.csv @@ -1,18 +1,18 @@ ,$,(,),+,-,;,addop,float,id,if,int,mulop,num,relop,while,}, STATEMENT_LIST,sync,N/A,N/A,N/A,N/A,N/A,N/A,--> STATEMENT STATEMENT_LIST` ,--> STATEMENT STATEMENT_LIST` ,--> STATEMENT STATEMENT_LIST` ,--> STATEMENT STATEMENT_LIST` ,N/A,N/A,N/A,--> STATEMENT STATEMENT_LIST` ,N/A, -STATEMENT_LIST`,epsilon,N/A,N/A,N/A,N/A,N/A,N/A,--> STATEMENT STATEMENT_LIST` ,--> STATEMENT STATEMENT_LIST` ,--> STATEMENT STATEMENT_LIST` ,--> STATEMENT STATEMENT_LIST` ,N/A,N/A,N/A,--> STATEMENT STATEMENT_LIST` ,N/A, -IF,sync,N/A,N/A,N/A,N/A,N/A,N/A,sync,sync,--> if ( EXPRESSION ) { STATEMENT } else { STATEMENT } ,sync,N/A,N/A,N/A,sync,sync, DECLARATION,sync,N/A,N/A,N/A,N/A,N/A,N/A,--> PRIMITIVE_TYPE id ; ,sync,sync,--> PRIMITIVE_TYPE id ; ,N/A,N/A,N/A,sync,sync, -FACTOR,N/A,--> ( EXPRESSION ) ,sync,N/A,N/A,sync,sync,N/A,--> id ,N/A,N/A,sync,--> num ,sync,N/A,N/A, -EXPRESSION,N/A,--> SIMPLE_EXPRESSION EXPRESSION1 ,sync,--> SIMPLE_EXPRESSION EXPRESSION1 ,--> SIMPLE_EXPRESSION EXPRESSION1 ,sync,N/A,N/A,--> SIMPLE_EXPRESSION EXPRESSION1 ,N/A,N/A,N/A,--> SIMPLE_EXPRESSION EXPRESSION1 ,N/A,N/A,N/A, -PRIMITIVE_TYPE,N/A,N/A,N/A,N/A,N/A,N/A,N/A,--> float ,sync,N/A,--> int ,N/A,N/A,N/A,N/A,N/A, -STATEMENT,sync,N/A,N/A,N/A,N/A,N/A,N/A,--> DECLARATION ,--> ASSIGNMENT ,--> IF ,--> DECLARATION ,N/A,N/A,N/A,--> WHILE ,sync, +IF,sync,N/A,N/A,N/A,N/A,N/A,N/A,sync,sync,--> if ( EXPRESSION ) { STATEMENT } else { STATEMENT } ,sync,N/A,N/A,N/A,sync,sync, ASSIGNMENT,sync,N/A,N/A,N/A,N/A,N/A,N/A,sync,--> id = EXPRESSION ; ,sync,sync,N/A,N/A,N/A,sync,sync, +EXPRESSION1,N/A,N/A,epsilon,N/A,N/A,epsilon,N/A,N/A,N/A,N/A,N/A,N/A,N/A,--> relop SIMPLE_EXPRESSION ,N/A,N/A, +TERM,N/A,--> FACTOR TERM` ,sync,N/A,N/A,sync,sync,N/A,--> FACTOR TERM` ,N/A,N/A,N/A,--> FACTOR TERM` ,sync,N/A,N/A, +STATEMENT_LIST`,epsilon,N/A,N/A,N/A,N/A,N/A,N/A,--> STATEMENT STATEMENT_LIST` ,--> STATEMENT STATEMENT_LIST` ,--> STATEMENT STATEMENT_LIST` ,--> STATEMENT STATEMENT_LIST` ,N/A,N/A,N/A,--> STATEMENT STATEMENT_LIST` ,N/A, SIMPLE_EXPRESSION,N/A,--> TERM SIMPLE_EXPRESSION` ,sync,--> SIGN TERM SIMPLE_EXPRESSION` ,--> SIGN TERM SIMPLE_EXPRESSION` ,sync,N/A,N/A,--> TERM SIMPLE_EXPRESSION` ,N/A,N/A,N/A,--> TERM SIMPLE_EXPRESSION` ,sync,N/A,N/A, +FACTOR,N/A,--> ( EXPRESSION ) ,sync,N/A,N/A,sync,sync,N/A,--> id ,N/A,N/A,sync,--> num ,sync,N/A,N/A, +EXPRESSION,N/A,--> SIMPLE_EXPRESSION EXPRESSION1 ,sync,--> SIMPLE_EXPRESSION EXPRESSION1 ,--> SIMPLE_EXPRESSION EXPRESSION1 ,sync,N/A,N/A,--> SIMPLE_EXPRESSION EXPRESSION1 ,N/A,N/A,N/A,--> SIMPLE_EXPRESSION EXPRESSION1 ,N/A,N/A,N/A, TERM`,N/A,N/A,epsilon,N/A,N/A,epsilon,epsilon,N/A,N/A,N/A,N/A,--> mulop FACTOR TERM` ,N/A,epsilon,N/A,N/A, METHOD_BODY,sync,N/A,N/A,N/A,N/A,N/A,N/A,--> STATEMENT_LIST ,--> STATEMENT_LIST ,--> STATEMENT_LIST ,--> STATEMENT_LIST ,N/A,N/A,N/A,--> STATEMENT_LIST ,N/A, +STATEMENT,sync,N/A,N/A,N/A,N/A,N/A,N/A,--> DECLARATION ,--> ASSIGNMENT ,--> IF ,--> DECLARATION ,N/A,N/A,N/A,--> WHILE ,sync, SIMPLE_EXPRESSION`,N/A,N/A,epsilon,N/A,N/A,epsilon,--> addop TERM SIMPLE_EXPRESSION` ,N/A,N/A,N/A,N/A,N/A,N/A,epsilon,N/A,N/A, -EXPRESSION1,N/A,N/A,epsilon,N/A,N/A,epsilon,N/A,N/A,N/A,N/A,N/A,N/A,N/A,--> relop SIMPLE_EXPRESSION ,N/A,N/A, -WHILE,sync,N/A,N/A,N/A,N/A,N/A,N/A,sync,sync,sync,sync,N/A,N/A,N/A,--> while ( EXPRESSION ) { STATEMENT } ,sync, +PRIMITIVE_TYPE,N/A,N/A,N/A,N/A,N/A,N/A,N/A,--> float ,sync,N/A,--> int ,N/A,N/A,N/A,N/A,N/A, SIGN,N/A,sync,N/A,--> + ,--> - ,N/A,N/A,N/A,sync,N/A,N/A,N/A,sync,N/A,N/A,N/A, -TERM,N/A,--> FACTOR TERM` ,sync,N/A,N/A,sync,sync,N/A,--> FACTOR TERM` ,N/A,N/A,N/A,--> FACTOR TERM` ,sync,N/A,N/A, +WHILE,sync,N/A,N/A,N/A,N/A,N/A,N/A,sync,sync,sync,sync,N/A,N/A,N/A,--> while ( EXPRESSION ) { STATEMENT } ,sync, From c272cb70a7ba52bb8d6f7909d3a6526601f959c9 Mon Sep 17 00:00:00 2001 From: Deffo0 Date: Tue, 23 Jan 2024 21:03:58 +0200 Subject: [PATCH 39/41] merge --- .DS_Store | Bin 0 -> 6148 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 .DS_Store diff --git a/.DS_Store b/.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..9c5fca97958e73537e97c843bb97ec8271859202 GIT binary patch literal 6148 zcmeHKK~BRk5FA4eg_P1G7o@zP50Z<1}p$i7vOiE0ii?7&*FR2oZZKIDf3^B!wuBHm8 z0)JfrnY%R{;T#=2B0GN@oW%>wPLpx3n~b}xCyT@(z42*{IiQLYjCl=^kbO%|-_8F5 z@4dha$$MtZ5w8w&rP0iOfwfy;UHNF`?AHRj%GIgQF%8jU%mf#BAWqTG_tkt9coo~n zA)eUF9wwON>u(GS^+UESV`9rp*F>?bw~UfoxWwG5I4xHLqG6U1qKy~!YQ|`bb#dg4 znOY`JW%-WERTIs#Q370(F+`FdwGnl*^>%Q>ez)0|5jB#p;rs1O3rx{B%jB)vFDSr0 zTdWc~)KnEv1yq5h0d-D7OzaVW7_itH^YWV@oXBJBF?Gl%G~=j5M>YP4 zVH}<1k*^nfOdUEpj6ZxBAKCa5im}nzeq^h|#11u81yq5&0-Nr(F8lxS`tyIDq<5-- zD)6rqFhQ%`YH~<^Z(SLj?6nd7jxHwkQiqnp#BawoBU|wv-5Q@ok|D+(Q->^}>5qVw LK@(NrM-})2$r8AZ literal 0 HcmV?d00001 From 9e05e4c6a238c61b6e5f20e879165b5084a138f0 Mon Sep 17 00:00:00 2001 From: Deffo0 Date: Tue, 23 Jan 2024 21:24:40 +0200 Subject: [PATCH 40/41] merge --- CMakeLists.txt | 12 +- SyntaxPhase/CMakeLists.txt | 6 + SyntaxPhase/GrammarParser/CMakeLists.txt | 17 + SyntaxPhase/GrammarParser/Grammar.cpp | 92 +++++ SyntaxPhase/GrammarParser/Grammar.h | 40 ++ .../GrammarParser/GrammarConverter.cpp | 376 ++++++++++++++++++ SyntaxPhase/GrammarParser/GrammarConverter.h | 35 ++ .../GrammarParser/NonTerminalSymbol.cpp | 39 ++ SyntaxPhase/GrammarParser/NonTerminalSymbol.h | 27 ++ 9 files changed, 635 insertions(+), 9 deletions(-) create mode 100644 SyntaxPhase/GrammarParser/CMakeLists.txt create mode 100644 SyntaxPhase/GrammarParser/Grammar.cpp create mode 100644 SyntaxPhase/GrammarParser/Grammar.h create mode 100644 SyntaxPhase/GrammarParser/GrammarConverter.cpp create mode 100644 SyntaxPhase/GrammarParser/GrammarConverter.h create mode 100644 SyntaxPhase/GrammarParser/NonTerminalSymbol.cpp create mode 100644 SyntaxPhase/GrammarParser/NonTerminalSymbol.h diff --git a/CMakeLists.txt b/CMakeLists.txt index f515b82..51ea383 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -3,14 +3,8 @@ project(Compiler) set(CMAKE_CXX_STANDARD 20) -add_executable(Compiler main.cpp - ParserPhase/Parser.cpp - ParserPhase/Parser.h - ParserPhase/Common.h - ParserPhase/Symbol.h - ParserPhase/ParsingTree.h - ParserPhase/Token.h - ParserPhase/Production.h) +add_executable(Compiler Driver.cpp + ) target_link_libraries(Compiler Util_lib) target_link_libraries(Compiler RulesParser_lib) @@ -22,6 +16,6 @@ target_link_libraries(Compiler GrammarParser_lib) add_subdirectory(Util) add_subdirectory(LexicalPhase) -add_subdirectory(ParserPhase) +add_subdirectory(SyntaxPhase) add_subdirectory(Google_tests) diff --git a/SyntaxPhase/CMakeLists.txt b/SyntaxPhase/CMakeLists.txt index e69de29..e521425 100644 --- a/SyntaxPhase/CMakeLists.txt +++ b/SyntaxPhase/CMakeLists.txt @@ -0,0 +1,6 @@ +project(SyntaxPhase) + +add_subdirectory(Common) +add_subdirectory(FirstAndFollowGenerator) +add_subdirectory(GrammarParser) +add_subdirectory(PredictiveParser) \ No newline at end of file diff --git a/SyntaxPhase/GrammarParser/CMakeLists.txt b/SyntaxPhase/GrammarParser/CMakeLists.txt new file mode 100644 index 0000000..43c1b18 --- /dev/null +++ b/SyntaxPhase/GrammarParser/CMakeLists.txt @@ -0,0 +1,17 @@ +project(GrammarParser) + +set(HEADER_FILES + GrammarConverter.h + NonTerminalSymbol.h + Grammar.h) + +set(SOURCE_FILES + GrammarConverter.cpp + NonTerminalSymbol.cpp + Grammar.cpp) + + +add_library(GrammarParser_lib STATIC ${SOURCE_FILES} ${HEADER_FILES}) + +target_link_libraries(GrammarParser_lib PUBLIC Util_lib) +target_link_libraries(GrammarParser_lib PUBLIC Common_lib) \ No newline at end of file diff --git a/SyntaxPhase/GrammarParser/Grammar.cpp b/SyntaxPhase/GrammarParser/Grammar.cpp new file mode 100644 index 0000000..093ddfe --- /dev/null +++ b/SyntaxPhase/GrammarParser/Grammar.cpp @@ -0,0 +1,92 @@ +// +// Created by Meniem on 24-Dec-23. +// + +#include "Grammar.h" + + +Terminal* Grammar::epsilon = nullptr; + +void Grammar::standardizeNonTerminals() { + auto terminalsSet = modifiedGrammar.getTerminals(); + auto oldNonTerminals = modifiedGrammar.getNonTerminals(); + std::unordered_map> nonTerminalMap; + std::unordered_map> terminalMap; + + for (const auto& nonTerminal : oldNonTerminals){ + auto productions = nonTerminal.getProductions(); + std::vector>> newProductions; + for (const auto& production : productions){ + std::vector> newProduction; + for (const auto& symbolName : production){ + std::shared_ptr symbolPtr; + // Terminal + if (terminalsSet.contains(symbolName)){ + // not defined before + if (!terminalMap.contains(symbolName)){ + symbolPtr = std::make_shared(symbolName); + terminalMap.insert({symbolName, std::static_pointer_cast(symbolPtr)}); + if (symbolName=="\\L"){ + epsilon = dynamic_cast(symbolPtr.get()); + } + } + // defined before + else { + symbolPtr = terminalMap.find(symbolName)->second; + } + } + // Non-Terminal + else { + // not defined before + if (!nonTerminalMap.contains(symbolName)){ + symbolPtr = std::make_shared(symbolName); + nonTerminalMap.insert({symbolName, std::static_pointer_cast(symbolPtr)}); + } + // defined before + else { + symbolPtr = nonTerminalMap.find(symbolName)->second; + } + } + newProduction.push_back(symbolPtr); + } + newProductions.push_back(newProduction); + } + + std::shared_ptr outerNonTerminal; + // not defined before + if (!nonTerminalMap.contains(nonTerminal.getName())){ + outerNonTerminal = std::make_shared(nonTerminal.getName()); + outerNonTerminal->setProductions(newProductions); + nonTerminalMap.insert({nonTerminal.getName(), outerNonTerminal}); + } + // defined before + else { + outerNonTerminal = nonTerminalMap.find(nonTerminal.getName())->second; + outerNonTerminal->setProductions(newProductions); + } + standardizedNonTerminals.push_back(outerNonTerminal); + } + startSymbol = standardizedNonTerminals[0].get(); +} + +Grammar::Grammar(GrammarConverter modifiedGrammar) : modifiedGrammar(std::move(modifiedGrammar)) {} + + +std::ostream &operator<<(std::ostream &os, const Grammar &g) { + for (const auto& r : g.standardizedNonTerminals) + os << r.get(); + os << "\n\n\n"; + return os; +} + +const std::vector> &Grammar::getStandardizedNonTerminals() const { + return standardizedNonTerminals; +} + +NonTerminal *Grammar::getStartSymbol() const { + return startSymbol; +} + + + + diff --git a/SyntaxPhase/GrammarParser/Grammar.h b/SyntaxPhase/GrammarParser/Grammar.h new file mode 100644 index 0000000..3fae901 --- /dev/null +++ b/SyntaxPhase/GrammarParser/Grammar.h @@ -0,0 +1,40 @@ +// +// Created by Meniem on 24-Dec-23. +// + +#ifndef COMPILER_GRAMMAR_H +#define COMPILER_GRAMMAR_H + + +#include "../Common/NonTerminal.h" +#include "../Common/Terminal.h" +#include "GrammarConverter.h" + +#include +#include +#include +#include +#include + +class Grammar { +private : + + GrammarConverter modifiedGrammar; + std::vector> standardizedNonTerminals; + NonTerminal* startSymbol = nullptr; +public: + NonTerminal *getStartSymbol() const; + +public: + const std::vector> &getStandardizedNonTerminals() const; + friend std::ostream& operator<<(std::ostream& os, const Grammar& g); + void standardizeNonTerminals(); + explicit Grammar(GrammarConverter modifiedGrammar); + static Terminal* epsilon; + + // for debugging purposes + explicit Grammar(){}; +}; + + +#endif //COMPILER_GRAMMAR_H diff --git a/SyntaxPhase/GrammarParser/GrammarConverter.cpp b/SyntaxPhase/GrammarParser/GrammarConverter.cpp new file mode 100644 index 0000000..ba04337 --- /dev/null +++ b/SyntaxPhase/GrammarParser/GrammarConverter.cpp @@ -0,0 +1,376 @@ +// +// Created by Meniem on 19-Dec-23. +// + +#include "GrammarConverter.h" +#include +#include +#include +#include "../../Util/Util.h" + +int GrammarConverter::definitionCounter; + +GrammarConverter::GrammarConverter() = default; + +int GrammarConverter::parseFile(const std::string& filePath) { + // Need to Run Validation as one pass to get Non-Terminal Definitions, so we can later detect + // when Non-Terminal are used without definition and catch those errors. + std::ifstream file(filePath); + if (!file.is_open()){ + std::cerr << "Unable to open file." << "\n"; + return -1; + } + + std::string nonTerminalDefinition; + while(getline(file, nonTerminalDefinition, '#')) { + trimBlanksFromEnds(nonTerminalDefinition); + if (nonTerminalDefinition.empty()) + continue; // Ignores empty Lines + definitionCounter++; + + int status = validateGrammar(nonTerminalDefinition); + if (status == -1) return -1; + + } + file.close(); + + definitionCounter = 0; + + file.open(filePath); + if (!file.is_open()){ + std::cerr << "Unable to open file." << "\n"; + return -1; + } + + while(getline(file, nonTerminalDefinition, '#')) { + trimBlanksFromEnds(nonTerminalDefinition); + if (nonTerminalDefinition.empty()) + continue; // Ignores empty Lines + definitionCounter++; + + std::vector definitionSides = splitWithStringDelimiter(nonTerminalDefinition, "::="); + std::string nonTerminalName = definitionSides[0]; + std::string nonTerminalProductions = definitionSides[1]; + + trimBlanksFromEnds(nonTerminalName); + trimBlanksFromEnds(nonTerminalProductions); + removeConsecutiveSpaces(nonTerminalName); + removeConsecutiveSpaces(nonTerminalProductions); + + int status = findTerminals(nonTerminalProductions); + if (status == -1) return -1; + + status = parseProductions(nonTerminalName, nonTerminalProductions); + if (status == -1) return -1; + + } + file.close(); + + return 0; +} + + +const std::unordered_set &GrammarConverter::getTerminals() const { + return terminals; +} + +const std::vector &GrammarConverter::getNonTerminals() const { + return nonTerminals; +} + + int GrammarConverter::validateGrammar(const std::string& definition) { + + + // Handles definitions where there is a wrong number of separator either none or more than one. + std::vector definitionSides = splitWithStringDelimiter(definition, "::="); + if (definitionSides.size() < 2){ + std::cerr << "Definition Number: " << definitionCounter << " does not have correct definition separator." << "\n"; + return -1; + } + + if (definitionSides.size() > 2){ + std::cerr << "Definition Number: " << definitionCounter << " has multiple definition separators." << "\n"; + return -1; + } + + std::string nonTerminalName = definitionSides[0]; + std::string nonTerminalProductions = definitionSides[1]; + trimBlanksFromEnds(nonTerminalName); + trimBlanksFromEnds(nonTerminalProductions); + + // Handles definitions where either side is empty or blank. + if (nonTerminalName.empty()){ + std::cerr << "Definition Number: " << definitionCounter << " Definition Name is blank." << "\n"; + return -1; + } + + if (nonTerminalProductions.empty()){ + std::cerr << "Definition Number: " << definitionCounter << " Definition Production is blank." << "\n"; + return -1; + } + + // Checks if Non-Terminal was defined Before. + if (nonTerminalNames.contains(nonTerminalName)){ + std::cerr << "Definition: " << nonTerminalName << " is defined twice." << "\n"; + return -1; + } + + nonTerminalNames.insert(nonTerminalName); + + return 0; +} + +int GrammarConverter::findTerminals(std::string& productions) { + // accumulates terminal symbols if they exist + std::string accumulator; + for(int i = 0; i < productions.size(); i++){ + char c = productions[i]; + // check if single quote is found with no escape character before it. + if (c == '\'' && productions[i-1] != '\\'){ + // Delete the quotes. + productions.replace(i, 1, " "); + i++; + // loop until another quote symbol is found that doesn't have an escape character before. + while ( (productions[i] != '\'' || productions[i-1] == '\\')){ + // handle a single quote that is not closed. + if (i == productions.size()){ + std::cerr << "Definition Number: " << definitionCounter << " Quote is not closed." << "\n"; + return -1; + } + // Skip the escape character, so it can be put into the terminals set with its appropriate name + if (productions[i] == '\\'){ + if (productions[i+1] != 'L'){ + i++; + continue; + } + } + accumulator += productions[i]; + i++; + } + // Delete the quotes. + productions.replace(i, 1, " "); + } + if (!accumulator.empty()){ + terminals.insert(accumulator); + accumulator.clear(); + } + } + return 0; +} + +int GrammarConverter::parseProductions(const std::string& nonTerminalName, std::string &productions) { + productionsVector result; + // Replace any escaped conjunction, so we can split using conjunction without errors. + replaceAll(productions, "\\|", "\\$"); + std::vector delimitedProductions = split(productions, '|'); + for (std::string& production : delimitedProductions){ + // return the string to original by replacing the replacement string that was used above by the original escaped conjunction + replaceAll(production, "\\$", "\\|"); + trimBlanksFromEnds(production); + removeConsecutiveSpaces(production); + // split by spaces to get each symbol in each production. + std::vector splitProduction = split(production, ' '); + for (std::string& symbol : splitProduction){ + if (symbol != "\\L"){ + // if symbol is escaped then remove the escape character to correctly compare with the terminals set + if (symbol[0] == '\\') + symbol.erase(symbol.begin()); + // if it is a Non-Terminal that was not defined before then throw an error. + // This part is what enforced a one time pass for validation, so we can get all the defined Non-Terminals + // As you can define Non-Terminals in any order, so you can't move sequentially. + if (!terminals.contains(symbol) && !nonTerminalNames.contains(symbol)){ + std::cerr << "Definition Number: " << definitionCounter << " Non-Terminal (" << symbol << ") is not defined."<< "\n"; + return -1; + } + } + } + result.push_back(splitProduction); + } + // Create our new Non-Terminal and place it in our Non-Terminals Vector. + nonTerminals.emplace_back(nonTerminalName, result); + return 0; +} + +bool GrammarConverter::leftFactor() { + bool appliedLeftFactoring = false; // Flag to check if there is a left factoring to eliminate in our grammar or not. + std::vector temporaryNonTerminals; + int originalNonTerminalIndex = 0; + temporaryNonTerminals.push_back(nonTerminals[0]); + for (int i = 0; i < temporaryNonTerminals.size(); i++){ + auto productions = temporaryNonTerminals[i].getProductions(); + std::map> factoringMap; // create a factoring map where each beginning symbol of the productions gets an entry with the places it occurred as values. + // Loop over all productions and get the starting symbol of each production and check if it was repeated. + for (int index = 0; index < productions.size(); index++){ + // If it's the first time we encounter this symbol then add it to the factoring map with just one index + if (!factoringMap.contains(productions[index][0])){ + std::vector factorIndices = {index}; + factoringMap.insert({productions[index][0], factorIndices}); + } + // If it was found in the factoring map then add the other index where it was found, and now we know left factoring is present in our grammar. + else { + auto it = factoringMap.find(productions[index][0]); + (it->second).push_back(index); + appliedLeftFactoring = true; + } + } + + bool nonTerminalsFactored = false; // flag to check if we factor this Non-Terminal + int factorCounter = 1; // number to distinguish names of factored expressions + productionsVector factoredProductions; + productionsVector editedFactoredProductions; + std::string newNonTerminalName; + // Loop over the factoringMap to check the ones who are repeated. + for(auto factor : factoringMap){ + // If 2 productions have a common starting symbol (factor). + if (factor.second.size() > 1){ + nonTerminalsFactored = true; + // Edit the current production : A -> aA` + newNonTerminalName = temporaryNonTerminals[i].getName()+ std::to_string(factorCounter); + editedFactoredProductions.push_back(std::vector{factor.first, newNonTerminalName}); + // Loop over all the indices where the factor was common to create the new Non-Terminal. + for (int j : factor.second){ + std::vector temporaryProduction; + // Loop over each symbol after the first symbol and add it to our temporary production. + for (int k = 1; k < productions[j].size(); k++){ + temporaryProduction.push_back(productions[j][k]); + } + // Handle cases where factor has nothing after it, we add an epsilon in the new Non-Terminal. + if (temporaryProduction.empty()) + temporaryProduction.emplace_back("\\L"); + + factoredProductions.push_back(temporaryProduction); + } + // Add the new NonTerminal to the productions. + auto newNonTerminal = NonTerminalSymbol(newNonTerminalName, factoredProductions); + temporaryNonTerminals.push_back(newNonTerminal); + nonTerminalNames.insert(newNonTerminalName); + factoredProductions.clear(); + + factorCounter++; + } + // If no two productions have the same factor just add the same original productions. + else{ + std::vector temporaryProduction; + int originalIndex = factor.second[0]; + for (const auto& element : productions[originalIndex]) + temporaryProduction.push_back(element); + editedFactoredProductions.push_back(temporaryProduction); + } + } + // If the Non-Terminal was factored then we edit the original production the new one. + if (nonTerminalsFactored) + temporaryNonTerminals[i].setProductions(editedFactoredProductions); + + // Check if it was not factored, and we have not processed each element in our original Non-Terminal Vector and add a new one to our temporary vector. + if (!nonTerminalsFactored && originalNonTerminalIndex < nonTerminals.size() - 1){ + originalNonTerminalIndex++; + temporaryNonTerminals.push_back(nonTerminals[originalNonTerminalIndex]); + } + } + // Update our Non-Terminals vector with the new one. + nonTerminals = temporaryNonTerminals; + + return appliedLeftFactoring; +} + +bool GrammarConverter::eliminateLeftRecursion() { + bool hasLeftRecursion = false; + std::vector newNonTerminals; + // loop over each Non-Terminal + for (int i = 0; i < nonTerminals.size(); i++){ + // We need this because we will constantly edit its productions. + NonTerminalSymbol currentNonTerminal = nonTerminals[i]; + // loop over all Non-Terminals above it. + for (int j = 0; j < i; j++) + // Recursively substitute each Non-Terminal combination (if it exists) with our current Non-Terminal. + currentNonTerminal = substitute(currentNonTerminal, nonTerminals[j]); + // If after substituting immediate left recursion is found. + if (hasImmediateLeftRecursion(currentNonTerminal)){ + hasLeftRecursion = true; + terminals.insert("\\L"); + std::vector modifiedNonTerminals = eliminateImmediateLeftRecursion(currentNonTerminal); + // Push the modified Non-Terminals to our new Non-Terminals vector. + for (const auto& nonTerminal : modifiedNonTerminals){ + newNonTerminals.push_back(nonTerminal); + nonTerminalNames.insert(nonTerminal.getName()); + } + + } + // If no Immediate left recursion is found then just add the original Non-Terminal. + else { + newNonTerminals.push_back(nonTerminals[i]); + } + + } + // Update our Non-Terminals vector with the new one. + nonTerminals = newNonTerminals; + return hasLeftRecursion; +} + + +bool GrammarConverter::hasImmediateLeftRecursion(const NonTerminalSymbol& nonTerminal) { + // loop over each production in the Non-Terminal + auto productions = nonTerminal.getProductions(); + for (auto production : productions){ + // Check if the first symbol of a production is the same name of the Non-Terminal + if (production[0] == nonTerminal.getName()) + return true; + } + return false; +} + +NonTerminalSymbol GrammarConverter::substitute(const NonTerminalSymbol& currentNonTerminal, const NonTerminalSymbol& potentiallySubstitutedNonTerminal) { + productionsVector result; + productionsVector currentProductions = currentNonTerminal.getProductions(); + productionsVector potentiallySubstitutedProductions = potentiallySubstitutedNonTerminal.getProductions(); + + for (auto currentProduction : currentProductions){ + // if current production's first symbol doesn't match our potential symbol then just do nothing (add the same production to the result vector) + if (currentProduction[0] != potentiallySubstitutedNonTerminal.getName()) + result.push_back(currentProduction); + else { + // else if it matches then substitute for every production and push into the result vector. + for (const auto& potentiallySubstitutedProduction : potentiallySubstitutedProductions){ + std::vector tempProduction(potentiallySubstitutedProduction); + for (int i = 1; i < currentProduction.size(); i++) + tempProduction.push_back(currentProduction[i]); + + result.push_back(tempProduction); + } + } + } + NonTerminalSymbol newNonTerminal(currentNonTerminal.getName(), result); + return newNonTerminal; +} + +std::vector GrammarConverter::eliminateImmediateLeftRecursion(const NonTerminalSymbol& nonTerminal) { + productionsVector alphas; + productionsVector betas; + productionsVector productions = nonTerminal.getProductions(); + std::string modifiedName = nonTerminal.getName() + "`"; + // loop over all productions to get each local alpha or beta + for (auto production : productions){ + std::vector localAlpha; + std::vector localBeta; + // check if first symbol of production equals the Non-Terminal name. + if (production[0] == nonTerminal.getName()){ + // If yes then it's alpha then we add all symbols after the first one then the modified name. + for (int i = 1; i < production.size(); i++) + localAlpha.push_back(production[i]); + localAlpha.push_back(modifiedName); + alphas.push_back(localAlpha); + } + else { + // If no then it's beta then we add all the symbols and after them the modified name. + for (const auto & symbol : production) + localBeta.push_back(symbol); + localBeta.push_back(modifiedName); + betas.push_back(localBeta); + } + } + // Add epsilon transition to alphas (modified Non-Terminal) + alphas.push_back(std::vector{"\\L"}); + // Construct our pair of resulting Non-Terminals + std::vector result = {NonTerminalSymbol(nonTerminal.getName(), betas), NonTerminalSymbol(modifiedName, alphas)}; + return result; +} \ No newline at end of file diff --git a/SyntaxPhase/GrammarParser/GrammarConverter.h b/SyntaxPhase/GrammarParser/GrammarConverter.h new file mode 100644 index 0000000..1243877 --- /dev/null +++ b/SyntaxPhase/GrammarParser/GrammarConverter.h @@ -0,0 +1,35 @@ +// +// Created by Meniem on 19-Dec-23. +// + +#ifndef COMPILER_GRAMMARCONVERTER_H +#define COMPILER_GRAMMARCONVERTER_H + +#include +#include +#include "NonTerminalSymbol.h" + +class GrammarConverter { + private: + static int definitionCounter; + std::vector nonTerminals; + std::unordered_set nonTerminalNames; + std::unordered_set terminals; + [[nodiscard]] static bool hasImmediateLeftRecursion(const NonTerminalSymbol& nonTerminal); + [[nodiscard]] static NonTerminalSymbol substitute(const NonTerminalSymbol& currentNonTerminal, const NonTerminalSymbol& potentiallySubstitutedNonTerminal); + [[nodiscard]] static std::vector eliminateImmediateLeftRecursion(const NonTerminalSymbol& nonTerminal); + + public: + GrammarConverter(); + [[nodiscard]] int parseFile(const std::string& filePath); + [[nodiscard]] const std::vector &getNonTerminals() const; + [[nodiscard]] const std::unordered_set &getTerminals() const; + [[nodiscard]] int validateGrammar(const std::string& definition); + [[nodiscard]] int findTerminals(std::string& productions); + [[nodiscard]] int parseProductions(const std::string& nonTerminalName, std::string& productions); + [[nodiscard]] bool leftFactor(); + [[nodiscard]] bool eliminateLeftRecursion(); +}; + + +#endif //COMPILER_GRAMMARCONVERTER_H diff --git a/SyntaxPhase/GrammarParser/NonTerminalSymbol.cpp b/SyntaxPhase/GrammarParser/NonTerminalSymbol.cpp new file mode 100644 index 0000000..8a951d5 --- /dev/null +++ b/SyntaxPhase/GrammarParser/NonTerminalSymbol.cpp @@ -0,0 +1,39 @@ +// +// Created by Meniem on 19-Dec-23. +// + +#include "NonTerminalSymbol.h" +#include + +NonTerminalSymbol::NonTerminalSymbol(std::string name, productionsVector productions) : name(std::move( + name)), productions(std::move(productions)) {} + +const std::string &NonTerminalSymbol::getName() const { + return name; +} + + +const productionsVector &NonTerminalSymbol::getProductions() const { + return productions; +} + +std::string NonTerminalSymbol::toString() const { + std::string result = "Name: " + name + " Productions: "; + + for (const auto& production : productions) { + for (const auto& symbol : production) { + result += symbol + " "; + } + result.pop_back(); // Remove the extra space + result += " | "; + } + result.erase(result.length()-2); + + return result; +} + +void NonTerminalSymbol::setProductions(const productionsVector &productionsVector) { + NonTerminalSymbol::productions = productionsVector; +} + + diff --git a/SyntaxPhase/GrammarParser/NonTerminalSymbol.h b/SyntaxPhase/GrammarParser/NonTerminalSymbol.h new file mode 100644 index 0000000..22c22ba --- /dev/null +++ b/SyntaxPhase/GrammarParser/NonTerminalSymbol.h @@ -0,0 +1,27 @@ +// +// Created by Meniem on 19-Dec-23. +// + +#ifndef COMPILER_NONTERMINALSYMBOL_H +#define COMPILER_NONTERMINALSYMBOL_H + +#include +#include + +typedef std::vector> productionsVector; + +class NonTerminalSymbol { + private: + std::string name; + productionsVector productions; + public: + NonTerminalSymbol(std::string name, productionsVector productions); + [[nodiscard]] std::string toString() const; + [[nodiscard]] const std::string &getName() const; + [[nodiscard]] const productionsVector &getProductions() const; + + void setProductions(const productionsVector &productionsVector); +}; + + +#endif //COMPILER_NONTERMINALSYMBOL_H From 47e0c64e5912e3bdf2036de08120c498c6581df1 Mon Sep 17 00:00:00 2001 From: Deffo0 Date: Tue, 23 Jan 2024 21:40:34 +0200 Subject: [PATCH 41/41] merge --- CMakeLists.txt | 2 +- Outputs/parsingTable.csv | 12 ++++++------ Driver.cpp => driver.cpp | 0 3 files changed, 7 insertions(+), 7 deletions(-) rename Driver.cpp => driver.cpp (100%) diff --git a/CMakeLists.txt b/CMakeLists.txt index b553814..1273347 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -3,7 +3,7 @@ project(Compiler) set(CMAKE_CXX_STANDARD 20) -add_executable(Compiler Driver.cpp) +add_executable(Compiler driver.cpp) target_link_libraries(Compiler Util_lib) target_link_libraries(Compiler RulesParser_lib) diff --git a/Outputs/parsingTable.csv b/Outputs/parsingTable.csv index c7c5c51..4a312ee 100644 --- a/Outputs/parsingTable.csv +++ b/Outputs/parsingTable.csv @@ -1,18 +1,18 @@ ,$,(,),+,-,;,addop,float,id,if,int,mulop,num,relop,while,}, STATEMENT_LIST,sync,N/A,N/A,N/A,N/A,N/A,N/A,--> STATEMENT STATEMENT_LIST` ,--> STATEMENT STATEMENT_LIST` ,--> STATEMENT STATEMENT_LIST` ,--> STATEMENT STATEMENT_LIST` ,N/A,N/A,N/A,--> STATEMENT STATEMENT_LIST` ,N/A, -DECLARATION,sync,N/A,N/A,N/A,N/A,N/A,N/A,--> PRIMITIVE_TYPE id ; ,sync,sync,--> PRIMITIVE_TYPE id ; ,N/A,N/A,N/A,sync,sync, -IF,sync,N/A,N/A,N/A,N/A,N/A,N/A,sync,sync,--> if ( EXPRESSION ) { STATEMENT } else { STATEMENT } ,sync,N/A,N/A,N/A,sync,sync, -ASSIGNMENT,sync,N/A,N/A,N/A,N/A,N/A,N/A,sync,--> id = EXPRESSION ; ,sync,sync,N/A,N/A,N/A,sync,sync, +WHILE,sync,N/A,N/A,N/A,N/A,N/A,N/A,sync,sync,sync,sync,N/A,N/A,N/A,--> while ( EXPRESSION ) { STATEMENT } ,sync, EXPRESSION1,N/A,N/A,epsilon,N/A,N/A,epsilon,N/A,N/A,N/A,N/A,N/A,N/A,N/A,--> relop SIMPLE_EXPRESSION ,N/A,N/A, TERM,N/A,--> FACTOR TERM` ,sync,N/A,N/A,sync,sync,N/A,--> FACTOR TERM` ,N/A,N/A,N/A,--> FACTOR TERM` ,sync,N/A,N/A, +DECLARATION,sync,N/A,N/A,N/A,N/A,N/A,N/A,--> PRIMITIVE_TYPE id ; ,sync,sync,--> PRIMITIVE_TYPE id ; ,N/A,N/A,N/A,sync,sync, +IF,sync,N/A,N/A,N/A,N/A,N/A,N/A,sync,sync,--> if ( EXPRESSION ) { STATEMENT } else { STATEMENT } ,sync,N/A,N/A,N/A,sync,sync, STATEMENT_LIST`,epsilon,N/A,N/A,N/A,N/A,N/A,N/A,--> STATEMENT STATEMENT_LIST` ,--> STATEMENT STATEMENT_LIST` ,--> STATEMENT STATEMENT_LIST` ,--> STATEMENT STATEMENT_LIST` ,N/A,N/A,N/A,--> STATEMENT STATEMENT_LIST` ,N/A, SIMPLE_EXPRESSION,N/A,--> TERM SIMPLE_EXPRESSION` ,sync,--> SIGN TERM SIMPLE_EXPRESSION` ,--> SIGN TERM SIMPLE_EXPRESSION` ,sync,N/A,N/A,--> TERM SIMPLE_EXPRESSION` ,N/A,N/A,N/A,--> TERM SIMPLE_EXPRESSION` ,sync,N/A,N/A, -FACTOR,N/A,--> ( EXPRESSION ) ,sync,N/A,N/A,sync,sync,N/A,--> id ,N/A,N/A,sync,--> num ,sync,N/A,N/A, -EXPRESSION,N/A,--> SIMPLE_EXPRESSION EXPRESSION1 ,sync,--> SIMPLE_EXPRESSION EXPRESSION1 ,--> SIMPLE_EXPRESSION EXPRESSION1 ,sync,N/A,N/A,--> SIMPLE_EXPRESSION EXPRESSION1 ,N/A,N/A,N/A,--> SIMPLE_EXPRESSION EXPRESSION1 ,N/A,N/A,N/A, TERM`,N/A,N/A,epsilon,N/A,N/A,epsilon,epsilon,N/A,N/A,N/A,N/A,--> mulop FACTOR TERM` ,N/A,epsilon,N/A,N/A, +EXPRESSION,N/A,--> SIMPLE_EXPRESSION EXPRESSION1 ,sync,--> SIMPLE_EXPRESSION EXPRESSION1 ,--> SIMPLE_EXPRESSION EXPRESSION1 ,sync,N/A,N/A,--> SIMPLE_EXPRESSION EXPRESSION1 ,N/A,N/A,N/A,--> SIMPLE_EXPRESSION EXPRESSION1 ,N/A,N/A,N/A, +FACTOR,N/A,--> ( EXPRESSION ) ,sync,N/A,N/A,sync,sync,N/A,--> id ,N/A,N/A,sync,--> num ,sync,N/A,N/A, METHOD_BODY,sync,N/A,N/A,N/A,N/A,N/A,N/A,--> STATEMENT_LIST ,--> STATEMENT_LIST ,--> STATEMENT_LIST ,--> STATEMENT_LIST ,N/A,N/A,N/A,--> STATEMENT_LIST ,N/A, STATEMENT,sync,N/A,N/A,N/A,N/A,N/A,N/A,--> DECLARATION ,--> ASSIGNMENT ,--> IF ,--> DECLARATION ,N/A,N/A,N/A,--> WHILE ,sync, SIMPLE_EXPRESSION`,N/A,N/A,epsilon,N/A,N/A,epsilon,--> addop TERM SIMPLE_EXPRESSION` ,N/A,N/A,N/A,N/A,N/A,N/A,epsilon,N/A,N/A, +ASSIGNMENT,sync,N/A,N/A,N/A,N/A,N/A,N/A,sync,--> id = EXPRESSION ; ,sync,sync,N/A,N/A,N/A,sync,sync, PRIMITIVE_TYPE,N/A,N/A,N/A,N/A,N/A,N/A,N/A,--> float ,sync,N/A,--> int ,N/A,N/A,N/A,N/A,N/A, SIGN,N/A,sync,N/A,--> + ,--> - ,N/A,N/A,N/A,sync,N/A,N/A,N/A,sync,N/A,N/A,N/A, -WHILE,sync,N/A,N/A,N/A,N/A,N/A,N/A,sync,sync,sync,sync,N/A,N/A,N/A,--> while ( EXPRESSION ) { STATEMENT } ,sync, diff --git a/Driver.cpp b/driver.cpp similarity index 100% rename from Driver.cpp rename to driver.cpp