aboutsummaryrefslogtreecommitdiffstats
path: root/parser.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'parser.cpp')
-rw-r--r--parser.cpp313
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;
+ }
+}
+