diff options
Diffstat (limited to 'parser.cpp')
-rw-r--r-- | parser.cpp | 313 |
1 files changed, 313 insertions, 0 deletions
diff --git a/parser.cpp b/parser.cpp new file mode 100644 index 0000000..6d5b992 --- /dev/null +++ b/parser.cpp @@ -0,0 +1,313 @@ +/** + * lisp-compiler: Compiles LISP using LLVM. + * Copyright (C) 2022 Clyne Sullivan + * + * This program is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free Software + * Foundation, either version 3 of the License, or (at your option) any later + * version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see <http://www.gnu.org/licenses/>. + * + * @file parser.cpp + * @brief Source code parser to produce AST. + */ + +#include "parser.hpp" + +#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)); +} + +void Parser::consumeWhitespace() +{ + while (isspace(text.front())) + text.pop_front(); +} + +std::optional<std::string> Parser::consumeIdentifier() +{ + std::string ret; + + if (isalpha(text.front())) { + do { + ret += text.front(); + text.pop_front(); + } while (isalnum(text.front()) || text.front() == '!'); + } + + if (ret.empty()) + return {}; + else + return ret; +} + +std::optional<std::variant<int, double>> Parser::consumeLiteralNumber() +{ + std::string ret; + + while (isdigit(text.front()) || text.front() == '.') { + ret += text.front(); + text.pop_front(); + } + + 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); +} + +AST::Node *Parser::parse(CompilerState& state) +{ + AST::Node *ret = nullptr; + + 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); + } + + lastError = None; + return ret; +} + +AST::Node *Parser::parseExpression() +{ + if (text.front() == '(') { + return parseProcedureCall(); + } else if (auto id = consumeIdentifier(); id) { + auto ident = new AST::Identifier; + ident->name = *id; + return ident; + } else if (auto d = consumeLiteralNumber(); d) { + auto lit = new AST::Literal; + lit->type = AST::Literal::Number; + lit->value = *d; + return lit; + } + + return nullptr; +} + +AST::Node *Parser::parseProcedureCall() +{ + // Consume the opening parenthesis. + if (text.front() != '(') { + lastError = ExpectedProcedure; + return nullptr; + } else { + text.pop_front(); + } + + // Consume the identifier string. + auto ident = parseExpression(); + if (ident == nullptr) { + lastError = InvalidOperator; + return nullptr; + } + + if (auto id = dynamic_cast<AST::Identifier *>(ident); id) { + if (id->name == "lambda") + return parseLambdaExpression(); + else if (id->name == "define") + return parseDefinition(); + else if (id->name == "if") + return parseConditional(); + else if (id->name == "set!") + return parseAssignment(); + } + + std::vector<AST::Node *> args; + + consumeWhitespace(); + while (text.front() != ')') { + auto node = parseExpression(); + if (node == nullptr) { + lastError = InvalidOperand; + return nullptr; + } + + args.push_back(node); + consumeWhitespace(); + } + + if (text.front() == ')') { + text.pop_front(); + + auto pc = new AST::ProcedureCall; + pc->callee = ident; + pc->operands = args; + + return pc; + } else { + lastError = ExpectedProcedureCallClose; + return nullptr; + } +} + +AST::Node *Parser::parseConditional() +{ + consumeWhitespace(); + auto cond = parseExpression(); + if (cond == nullptr) + return nullptr; + + consumeWhitespace(); + auto ift = parseExpression(); + if (ift == nullptr) + return nullptr; + + consumeWhitespace(); + auto iff = parseExpression(); + if (iff == nullptr) + return nullptr; + + consumeWhitespace(); + if (text.front() == ')') { + text.pop_front(); + + auto node = new AST::Conditional; + node->cond = cond; + node->iftrue = ift; + node->iffalse = iff; + return node; + } + + return nullptr; +} + +AST::Node *Parser::parseDefinition() +{ + consumeWhitespace(); + auto ident = consumeIdentifier(); + + if (ident) { + consumeWhitespace(); + + auto val = parseExpression(); + if (val) { + consumeWhitespace(); + if (text.front() == ')') { + text.pop_front(); + + auto id = new AST::Identifier; + id->name = *ident; + + auto def = new AST::Definition; + def->ident = id; + def->value = val; + return def; + } + } + } + + return nullptr; +} + +AST::Node *Parser::parseAssignment() +{ + consumeWhitespace(); + auto ident = consumeIdentifier(); + + if (ident) { + consumeWhitespace(); + + auto val = parseExpression(); + if (val) { + consumeWhitespace(); + if (text.front() == ')') { + text.pop_front(); + + auto id = new AST::Identifier; + id->name = *ident; + + auto def = new AST::Assignment; + def->ident = id; + def->value = val; + return def; + } + } + } + + return nullptr; +} + +AST::Node *Parser::parseLambdaExpression() +{ + // First get argument list + consumeWhitespace(); + if (text.front() != '(') { + lastError = ExpectedArgumentList; + return nullptr; + } else { + text.pop_front(); + } + + std::vector<AST::Identifier *> args; + + while (text.front() != ')') { + auto arg = consumeIdentifier(); + if (arg) { + auto ident = new AST::Identifier; + ident->name = *arg; + args.push_back(ident); + } else { + lastError = InvalidOperand; + return nullptr; + } + + consumeWhitespace(); + } + text.pop_front(); // consume arg list ')' + + 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; + + body.push_back(exp); + consumeWhitespace(); + } + + if (text.front() == ')') { + text.pop_front(); + + auto le = new AST::LambdaExpression; + le->operands = args; + le->body = body; + + return le; + } else { + lastError = ExpectedProcedureCallClose; + return nullptr; + } +} + |