#ifndef YERBACON_PARSER_HPP #define YERBACON_PARSER_HPP #include #include "ParseComponents.hpp" #include "../Yerbacon.hpp" #include #include namespace Parser { typedef Yerbacon::Exception ParsingException; void error(const tok& token, const string& text, unsigned long line, const bool& quoteTokenText = false) { throw ParsingException(quoteTokenText ? "\"" + token.toktext + "\"" + text : text, line); } inline void parsingError( const tok& token, const string& text, const bool& quoteTokenText = false ) { error(token, text, token.line, quoteTokenText); } IS(ParseTree) T parse(const span&& lexed) { T parseTree; using namespace StandardComponents; using enum tok::type; unsigned int i = 0; const auto nextAre = [&i, &lexed] Y>(const initializer_list& nextValues) -> bool { unsigned int j = 1; for (const Y& nextValue: nextValues) { if (!cmp_greater(lexed.size() - i, nextValues.size()) || lexed[i + j].toktype != nextValue) { return false; } ++j; } return true; }; for (;i < lexed.size(); ++i) { const bool hasNext = (i + 1) < lexed.size(); const tok& current = lexed[i], next = hasNext ? lexed[i + 1] : tok(UNEXPECTED, current.line); switch (current.toktype) { case STRING: parseTree << types::String(current.toktext.data()); break; case IDENTIFIER: { if (current.toktext == "class" || current.toktext == "structure") { if (next.toktype == IDENTIFIER) { parseTree << Class(next.toktext); ++i; } else { parsingError(next, hasNext ? " is not a valid class identifier" : "A class identifier is required", hasNext); } } else { bool isFinalDefine = nextAre({TAG, DEFINE}); if (isFinalDefine || next.toktype == DEFINE) { const optional previousDefinition = parseTree.template findReferenceByName(current.toktext); if (previousDefinition.has_value()) { if (previousDefinition.value().get().final || isFinalDefine) { parsingError(current, previousDefinition->get().final ? " cannot be redefined as it is final" : " cannot be made final after it has been declared", true); } } parseTree << Define(isFinalDefine, current.toktext); i += 1 + isFinalDefine; } else { parseTree << Reference(current.toktext); } } break; } case LPAR: case LBRACE: case LBRACKET: { const auto inverseCharacter = tok::inverseLCharacter(current.toktype); const auto closingCharacter = find_if(lexed.begin() + i, lexed.end(), [&inverseCharacter](const tok& it){ return it.toktype == inverseCharacter; }); if (closingCharacter != lexed.end()) { vector subTokens(lexed.begin() + i + 1, closingCharacter); if (current.toktype == LPAR || current.toktype == LBRACKET) { if (subTokens.size() >= 2 && subTokens[1].toktype != RPAR) { for (auto iterator = subTokens.cbegin(); iterator < (subTokens.cend() - 1); ++iterator) { const auto nextIterator = iterator + 1; if (nextIterator->toktype == COMMA) { subTokens.erase(nextIterator); } else throw ParsingException("Missing comma after \"" + iterator->toktext + '"'); } } } switch (current.toktype) { case LPAR: parseTree << parse(subTokens); break; case LBRACE: // TODO Add structures for class/function bodies case LBRACKET: default: parseTree << parse(subTokens); break; } i = distance(lexed.begin(), closingCharacter); } else parsingError(current, string(" is missing a closing \"").append(1, inverseCharacter) + '"', true); break; } case RPAR: case RBRACE: case RBRACKET: parsingError(current, " \u27F5 Unexpected character", true); default: break; } } return parseTree; } } #endif //YERBACON_PARSER_HPP