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.
229 lines
6.9 KiB
C++
229 lines
6.9 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 ast.cpp
|
|
* @brief Abstract Syntax Tree (AST) implementation.
|
|
*/
|
|
|
|
#include "ast.hpp"
|
|
#include "state.hpp"
|
|
|
|
#include "llvm/ADT/APFloat.h"
|
|
#include "llvm/ADT/Optional.h"
|
|
#include "llvm/ADT/STLExtras.h"
|
|
#include "llvm/IR/BasicBlock.h"
|
|
#include "llvm/IR/Constants.h"
|
|
#include "llvm/IR/DerivedTypes.h"
|
|
#include "llvm/IR/Function.h"
|
|
#include "llvm/IR/Instructions.h"
|
|
#include "llvm/IR/Type.h"
|
|
|
|
#include <algorithm>
|
|
#include <any>
|
|
#include <cctype>
|
|
#include <cstdio>
|
|
#include <deque>
|
|
#include <iostream>
|
|
#include <map>
|
|
#include <optional>
|
|
#include <string>
|
|
#include <typeinfo>
|
|
#include <variant>
|
|
#include <vector>
|
|
|
|
AST::Value AST::Identifier::codegen(CompilerState& state)
|
|
{
|
|
if (state.namedValues.contains(name))
|
|
return state.namedValues[name];
|
|
else
|
|
return nullptr;
|
|
}
|
|
|
|
AST::Value AST::Literal::codegen(CompilerState& state)
|
|
{
|
|
if (type == AST::Literal::Type::Number) {
|
|
if (std::holds_alternative<int>(value))
|
|
return llvm::ConstantInt::get(state.context, llvm::APInt(sizeof(int) * 8, std::get<int>(value)));
|
|
else
|
|
return llvm::ConstantFP::get(state.context, llvm::APFloat(std::get<double>(value)));
|
|
} else {
|
|
return nullptr;
|
|
}
|
|
}
|
|
|
|
AST::Value AST::ProcedureCall::codegen(CompilerState& state)
|
|
{
|
|
if (auto id = dynamic_cast<AST::Identifier *>(callee); id) {
|
|
if (state.namedValues.contains(id->name)) {
|
|
auto ptrToFuncPtr = state.namedValues[id->name];
|
|
auto funcPtr = state.builder.CreateLoad(
|
|
ptrToFuncPtr->getType()->getContainedType(0),
|
|
ptrToFuncPtr);
|
|
auto funcType = (llvm::FunctionType *)funcPtr->getType()->getContainedType(0);
|
|
return state.builder.CreateCall(funcType, funcPtr, llvm::None, "calltmp");
|
|
} else {
|
|
std::vector<llvm::Value *> args;
|
|
std::vector<llvm::Type *> argtypes;
|
|
|
|
for (auto& a : operands) {
|
|
args.push_back(a->codegen(state));
|
|
argtypes.push_back(args.back()->getType());
|
|
}
|
|
|
|
|
|
auto func = state.module.getOrInsertFunction(id->name,
|
|
llvm::FunctionType::get(
|
|
llvm::Type::getVoidTy(state.context),
|
|
argtypes,
|
|
false));
|
|
return state.builder.CreateCall(func, args);
|
|
}
|
|
|
|
// work off of id's name
|
|
// builtin?
|
|
// named value? (i.e. is defined)
|
|
return nullptr;
|
|
} else if (auto v = callee->codegen(state); v) {
|
|
// v needs to be a callable procedure
|
|
//std::vector<Node *> operands;
|
|
return nullptr;
|
|
} else {
|
|
return nullptr;
|
|
}
|
|
}
|
|
|
|
AST::Value AST::LambdaExpression::codegen(CompilerState& state)
|
|
{
|
|
std::vector<llvm::Type *> args;
|
|
std::vector<std::string> argnames;
|
|
|
|
for (auto& op : operands) {
|
|
args.push_back(llvm::Type::getDoubleTy(state.context));
|
|
argnames.push_back(op->name);
|
|
}
|
|
|
|
auto ftype = llvm::FunctionType::get(
|
|
llvm::Type::getDoubleTy(state.context), args, false);
|
|
auto func = llvm::Function::Create(
|
|
ftype, llvm::Function::ExternalLinkage, "lambda", &state.module);
|
|
|
|
auto n = argnames.cbegin();
|
|
for (auto& a : func->args()) {
|
|
a.setName(*n);
|
|
state.namedValues[*n] = &a;
|
|
++n;
|
|
}
|
|
|
|
auto block = llvm::BasicBlock::Create(state.context, "entry", func);
|
|
|
|
auto ip = state.builder.saveIP();
|
|
state.builder.SetInsertPoint(block);
|
|
++state.scope;
|
|
|
|
llvm::Value *ret;
|
|
for (auto& b : body)
|
|
ret = b->codegen(state);
|
|
|
|
if (ret)
|
|
state.builder.CreateRet(ret);
|
|
else
|
|
state.builder.CreateRetVoid();
|
|
|
|
--state.scope;
|
|
state.builder.restoreIP(ip);
|
|
|
|
for (auto& a : argnames)
|
|
state.namedValues.erase(a);
|
|
|
|
return func;
|
|
}
|
|
|
|
AST::Value AST::Conditional::codegen(CompilerState& state)
|
|
{
|
|
auto cval = state.builder.CreateFCmpONE(
|
|
cond->codegen(state),
|
|
llvm::ConstantFP::get(state.context, llvm::APFloat(0.0)),
|
|
"ifcond");
|
|
|
|
auto func = state.builder.GetInsertBlock()->getParent();
|
|
|
|
auto bthen = llvm::BasicBlock::Create(state.context, "then", func);
|
|
auto belse = llvm::BasicBlock::Create(state.context, "else", func);
|
|
auto bcont = llvm::BasicBlock::Create(state.context, "cont", func);
|
|
state.builder.CreateCondBr(cval, bthen, belse);
|
|
|
|
state.builder.SetInsertPoint(bthen);
|
|
auto vthen = iftrue->codegen(state);
|
|
if (!vthen || vthen->getType() != llvm::Type::getDoubleTy(state.context))
|
|
vthen = llvm::ConstantFP::get(state.context, llvm::APFloat(0.0));
|
|
state.builder.CreateBr(bcont);
|
|
|
|
state.builder.SetInsertPoint(belse);
|
|
auto velse = iffalse->codegen(state);
|
|
if (!velse || velse->getType() != llvm::Type::getDoubleTy(state.context))
|
|
velse = llvm::ConstantFP::get(state.context, llvm::APFloat(0.0));
|
|
state.builder.CreateBr(bcont);
|
|
|
|
state.builder.SetInsertPoint(bcont);
|
|
auto PN = state.builder.CreatePHI(
|
|
llvm::Type::getDoubleTy(state.context), 2, "iftmp");
|
|
|
|
PN->addIncoming(vthen, bthen);
|
|
PN->addIncoming(velse, belse);
|
|
return PN;
|
|
}
|
|
|
|
AST::Value AST::Definition::codegen(CompilerState& state)
|
|
{
|
|
if (!state.namedValues.contains(ident->name)) {
|
|
if (state.scope == 0) {
|
|
auto val = (llvm::Constant *)value->codegen(state);
|
|
|
|
state.module.getOrInsertGlobal(ident->name, val->getType());
|
|
|
|
auto var = state.module.getNamedGlobal(ident->name);
|
|
var->setLinkage(llvm::Function::ExternalLinkage);
|
|
var->setInitializer(val);
|
|
state.namedValues[ident->name] = var;
|
|
return var;
|
|
} else {
|
|
auto alloc = state.builder.CreateAlloca(
|
|
llvm::Type::getDoubleTy(state.context),
|
|
nullptr,
|
|
ident->name);
|
|
state.builder.CreateStore(value->codegen(state), alloc);
|
|
state.namedValues[ident->name] = alloc;
|
|
return alloc;
|
|
}
|
|
} else {
|
|
return nullptr;
|
|
}
|
|
}
|
|
|
|
AST::Value AST::Assignment::codegen(CompilerState& state)
|
|
{
|
|
if (state.scope > 0) {
|
|
if (state.namedValues.contains(ident->name)) {
|
|
return state.builder.CreateStore(value->codegen(state), state.namedValues[ident->name]);
|
|
}
|
|
}
|
|
|
|
return nullptr;
|
|
}
|
|
|
|
|