You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
314 lines
7.1 KiB
C++
314 lines
7.1 KiB
C++
/**
|
|
* 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;
|
|
}
|
|
}
|
|
|