/** * 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 . * * @file parser.cpp * @brief Source code parser to produce AST. */ #include "parser.hpp" #include #include #include #include #include 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 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> 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(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 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 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 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; } }