diff options
Diffstat (limited to 'parser.cpp')
-rw-r--r-- | parser.cpp | 170 |
1 files changed, 130 insertions, 40 deletions
@@ -24,24 +24,25 @@ #include <algorithm> #include <cctype> #include <cstdlib> -#include <iostream> #include <vector> void Parser::addString(const std::string& str) { - std::copy(str.cbegin(), str.cend(), std::back_inserter(text)); + if (!str.empty()) + std::copy(str.cbegin(), str.cend(), std::back_inserter(text)); } -void Parser::consumeWhitespace() +void Parser::consumeWhitespace() noexcept { while (isspace(text.front())) text.pop_front(); } -std::optional<std::string> Parser::consumeIdentifier() +std::optional<std::string> Parser::consumeIdentifier() noexcept { std::string ret; + // TODO Accept all valid identifiers according to R7RS-small. if (isalpha(text.front())) { do { ret += text.front(); @@ -66,32 +67,35 @@ std::optional<std::variant<int, double>> Parser::consumeLiteralNumber() if (ret.empty()) return {}; - else if (ret.find('.') == std::string::npos) - return (int)strtol(ret.c_str(), nullptr, 0); - else - return strtod(ret.c_str(), nullptr); + + if (ret.find('.') == std::string::npos) { + int r = strtol(ret.c_str(), nullptr, 0); + // TODO Error check + return r; + } else { + auto r = strtod(ret.c_str(), nullptr); + // TODO Error check + return r; + } } -AST::Node *Parser::parse(CompilerState& state) +std::deque<AST::Node *> Parser::parse() { - AST::Node *ret = nullptr; + std::deque<AST::Node *> ret; + errors.clear(); while (!text.empty()) { consumeWhitespace(); // At the top-level, there will only be procedure calls. - ret = parseProcedureCall(); - if (lastError != None) - return nullptr; - - - std::cout << (ret ? ret->desc() : "nullptr") << std::endl; - - if (ret) - ret->codegen(state); + auto node = parseProcedureCall(); + if (errors.empty() && node) { + ret.push_back(node); + } else { + return {}; + } } - lastError = None; return ret; } @@ -110,6 +114,7 @@ AST::Node *Parser::parseExpression() return lit; } + errors.push_back(InvalidExpression); return nullptr; } @@ -117,7 +122,7 @@ AST::Node *Parser::parseProcedureCall() { // Consume the opening parenthesis. if (text.front() != '(') { - lastError = ExpectedProcedure; + errors.push_back(ExpectedProcedureCallOpen); return nullptr; } else { text.pop_front(); @@ -125,11 +130,10 @@ AST::Node *Parser::parseProcedureCall() // Consume the identifier string. auto ident = parseExpression(); - if (ident == nullptr) { - lastError = InvalidOperator; + if (ident == nullptr) return nullptr; - } + // Check for special procedure calls. if (auto id = dynamic_cast<AST::Identifier *>(ident); id) { if (id->name == "lambda") return parseLambdaExpression(); @@ -141,13 +145,16 @@ AST::Node *Parser::parseProcedureCall() return parseAssignment(); } + // This is a regular procedure call. + // Build the argument list: + std::vector<AST::Node *> args; consumeWhitespace(); while (text.front() != ')') { auto node = parseExpression(); if (node == nullptr) { - lastError = InvalidOperand; + errors.push_back(InvalidOperand); return nullptr; } @@ -164,7 +171,7 @@ AST::Node *Parser::parseProcedureCall() return pc; } else { - lastError = ExpectedProcedureCallClose; + errors.push_back(ExpectedProcedureCallClose); return nullptr; } } @@ -173,18 +180,24 @@ AST::Node *Parser::parseConditional() { consumeWhitespace(); auto cond = parseExpression(); - if (cond == nullptr) + if (cond == nullptr) { + errors.push_back(InvalidCondition); return nullptr; + } consumeWhitespace(); auto ift = parseExpression(); - if (ift == nullptr) + if (ift == nullptr) { + errors.push_back(InvalidThenBranch); return nullptr; + } consumeWhitespace(); auto iff = parseExpression(); - if (iff == nullptr) + if (iff == nullptr) { + errors.push_back(InvalidThenBranch); return nullptr; + } consumeWhitespace(); if (text.front() == ')') { @@ -195,9 +208,10 @@ AST::Node *Parser::parseConditional() node->iftrue = ift; node->iffalse = iff; return node; - } - - return nullptr; + } else { + errors.push_back(ExpectedProcedureCallClose); + return nullptr; + } } AST::Node *Parser::parseDefinition() @@ -221,8 +235,14 @@ AST::Node *Parser::parseDefinition() def->ident = id; def->value = val; return def; + } else { + errors.push_back(ExpectedProcedureCallClose); } + } else { + errors.push_back(InvalidInitializer); } + } else { + errors.push_back(ExpectedIdentifier); } return nullptr; @@ -249,8 +269,14 @@ AST::Node *Parser::parseAssignment() def->ident = id; def->value = val; return def; + } else { + errors.push_back(ExpectedProcedureCallClose); } + } else { + errors.push_back(InvalidAssignValue); } + } else { + errors.push_back(ExpectedIdentifier); } return nullptr; @@ -258,15 +284,17 @@ AST::Node *Parser::parseAssignment() AST::Node *Parser::parseLambdaExpression() { - // First get argument list + // Consume beginning of argument list. consumeWhitespace(); if (text.front() != '(') { - lastError = ExpectedArgumentList; + errors.push_back(ExpectedArgumentList); return nullptr; } else { text.pop_front(); } + // Consume argument list: + std::vector<AST::Identifier *> args; while (text.front() != ')') { @@ -276,22 +304,28 @@ AST::Node *Parser::parseLambdaExpression() ident->name = *arg; args.push_back(ident); } else { - lastError = InvalidOperand; + errors.push_back(InvalidArgumentName); return nullptr; } consumeWhitespace(); } - text.pop_front(); // consume arg list ')' + + // Consume arg list closing ')' that must be there. + text.pop_front(); + + // Consume lambda body: std::vector<AST::Node *> body; - // Next, consume function body. consumeWhitespace(); while (text.front() != ')') { auto exp = parseExpression(); - //if (exp == nullptr) // TODO commands/definitions are okay - // return nullptr; + + if (!errors.empty()) { + errors.push_back(InvalidLambdaBody); + return nullptr; + } body.push_back(exp); consumeWhitespace(); @@ -306,8 +340,64 @@ AST::Node *Parser::parseLambdaExpression() return le; } else { - lastError = ExpectedProcedureCallClose; + errors.push_back(ExpectedProcedureCallClose); return nullptr; } } +std::string Parser::describeErrors() noexcept +{ + std::string ret; + + for (auto& err : errors) { + switch (err) { + case ExpectedProcedureCallOpen: + ret += "Expected opening \'(\' for procedure call.\n"; + break; + case ExpectedIdentifier: + ret += "Expected a valid identifier.\n"; + break; + case ExpectedProcedureCallClose: + ret += "Expected closing \')\' for procedure call.\n"; + break; + case ExpectedArgumentList: + ret += "Expected beginning of argument list.\n"; + break; + case UnknownIdentifier: + ret += "Given identifier is not valid.\n"; + break; + case InvalidExpression: + ret += "Expected a valid expression.\n"; + break; + case InvalidOperand: + ret += "Given invalid argument or operand.\n"; + break; + case InvalidCondition: + ret += "Given invalid condition for conditional statement.\n"; + break; + case InvalidThenBranch: + ret += "Given invalid \"then\" branch for conditional statement.\n"; + break; + case InvalidElseBranch: + ret += "Given invalid \"else\" branch for conditional statement.\n"; + break; + case InvalidInitializer: + ret += "Given invalid initializer for a declaration.\n"; + break; + case InvalidAssignValue: + ret += "Given invalid value for an assignment.\n"; + break; + case InvalidArgumentName: + ret += "Given invalid name for an argument.\n"; + break; + case InvalidLambdaBody: + ret += "Lambda body is invalid.\n"; + break; + default: + break; + } + } + + return ret; +} + |