initial code upload
parent
a650e3b1c5
commit
8dc1db8fb9
@ -0,0 +1,2 @@
|
|||||||
|
*.o
|
||||||
|
main
|
@ -0,0 +1,24 @@
|
|||||||
|
CXX := g++
|
||||||
|
CXXFLAGS := -std=c++20 -O0 -ggdb -g3
|
||||||
|
LIBS := -lLLVM-14
|
||||||
|
|
||||||
|
SRC := ast.cpp \
|
||||||
|
parser.cpp \
|
||||||
|
main.cpp
|
||||||
|
|
||||||
|
OBJ := $(subst .cpp,.o,$(SRC))
|
||||||
|
BIN := main
|
||||||
|
|
||||||
|
all: $(BIN)
|
||||||
|
|
||||||
|
clean:
|
||||||
|
@echo " CLEAN"
|
||||||
|
@rm -f $(BIN) $(OBJ)
|
||||||
|
|
||||||
|
$(BIN): $(OBJ)
|
||||||
|
@echo " LD " $@
|
||||||
|
@$(CXX) $(CXXFLAGS) -o $@ $^ $(LIBS)
|
||||||
|
|
||||||
|
.cpp.o:
|
||||||
|
@echo " CXX " $<
|
||||||
|
@$(CXX) $(CXXFLAGS) -c $<
|
@ -0,0 +1,228 @@
|
|||||||
|
/**
|
||||||
|
* 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
@ -0,0 +1,183 @@
|
|||||||
|
/**
|
||||||
|
* 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.hpp
|
||||||
|
* @brief Abstract Syntax Tree (AST) implementation.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef AST_HPP
|
||||||
|
#define AST_HPP
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
#include <variant>
|
||||||
|
|
||||||
|
struct CompilerState;
|
||||||
|
|
||||||
|
namespace llvm {
|
||||||
|
struct Value;
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace AST {
|
||||||
|
|
||||||
|
using Value = llvm::Value *;
|
||||||
|
|
||||||
|
struct Node
|
||||||
|
{
|
||||||
|
virtual ~Node() {};
|
||||||
|
|
||||||
|
virtual Value codegen(CompilerState&) = 0;
|
||||||
|
virtual std::string desc() {
|
||||||
|
return "Node";
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Identifier : public Node
|
||||||
|
{
|
||||||
|
std::string name;
|
||||||
|
|
||||||
|
virtual Value codegen(CompilerState&) final;
|
||||||
|
|
||||||
|
virtual std::string desc() final {
|
||||||
|
return std::string("Identifier: ") + name;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Literal : public Node
|
||||||
|
{
|
||||||
|
enum Type {
|
||||||
|
Unknown,
|
||||||
|
Number,
|
||||||
|
} type = Unknown;
|
||||||
|
std::variant<int, double> value;
|
||||||
|
|
||||||
|
virtual Value codegen(CompilerState&) final;
|
||||||
|
|
||||||
|
virtual std::string desc() final {
|
||||||
|
std::string str ("Literal: ");
|
||||||
|
|
||||||
|
switch (type) {
|
||||||
|
default:
|
||||||
|
case Unknown:
|
||||||
|
str += "unknown";
|
||||||
|
break;
|
||||||
|
case Number:
|
||||||
|
str += std::visit([](auto&& v) { return std::to_string(v); }, value);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return str;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ProcedureCall : public Node
|
||||||
|
{
|
||||||
|
Node *callee;
|
||||||
|
std::vector<Node *> operands;
|
||||||
|
|
||||||
|
virtual Value codegen(CompilerState&) final;
|
||||||
|
|
||||||
|
virtual std::string desc() final {
|
||||||
|
std::string str ("ProcedureCall: ");
|
||||||
|
|
||||||
|
str = str + "<" + (callee ? callee->desc() : "unknown") + ">";
|
||||||
|
str += '(';
|
||||||
|
if (!operands.empty()) {
|
||||||
|
for (const auto& op : operands) {
|
||||||
|
str = str + "<" + (op ? op->desc() : "unknown") + ">";
|
||||||
|
str += ", ";
|
||||||
|
}
|
||||||
|
|
||||||
|
str.pop_back();
|
||||||
|
str.pop_back();
|
||||||
|
}
|
||||||
|
str += ')';
|
||||||
|
|
||||||
|
return str;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct LambdaExpression : public Node
|
||||||
|
{
|
||||||
|
std::vector<Identifier *> operands;
|
||||||
|
std::vector<Node *> body;
|
||||||
|
|
||||||
|
virtual Value codegen(CompilerState&) final;
|
||||||
|
|
||||||
|
virtual std::string desc() final {
|
||||||
|
return "LambdaExpression";
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Conditional : public Node
|
||||||
|
{
|
||||||
|
Node *cond;
|
||||||
|
Node *iftrue;
|
||||||
|
Node *iffalse;
|
||||||
|
|
||||||
|
virtual Value codegen(CompilerState&) final;
|
||||||
|
|
||||||
|
virtual std::string desc() final {
|
||||||
|
return "Conditional";
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Definition : public Node
|
||||||
|
{
|
||||||
|
Identifier *ident;
|
||||||
|
Node *value;
|
||||||
|
|
||||||
|
virtual Value codegen(CompilerState&) final;
|
||||||
|
|
||||||
|
virtual std::string desc() final {
|
||||||
|
return "Definition";
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Assignment : public Node
|
||||||
|
{
|
||||||
|
Identifier *ident;
|
||||||
|
Node *value;
|
||||||
|
|
||||||
|
virtual Value codegen(CompilerState&) final;
|
||||||
|
|
||||||
|
virtual std::string desc() final {
|
||||||
|
return "Assignment";
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct DerivedExpression : public Node
|
||||||
|
{
|
||||||
|
std::string name;
|
||||||
|
|
||||||
|
virtual Value codegen(CompilerState&) final {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual std::string desc() final {
|
||||||
|
return "DerivedExpression";
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// MacroUse
|
||||||
|
// MacroBlock
|
||||||
|
// Includer
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // AST_HPP
|
||||||
|
|
@ -0,0 +1,128 @@
|
|||||||
|
/**
|
||||||
|
* 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 main.cpp
|
||||||
|
* @brief Program entry point.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
|
||||||
|
Current support:
|
||||||
|
- Literals:
|
||||||
|
- Integer
|
||||||
|
- Double (when decimal point is found)
|
||||||
|
- (define name value)
|
||||||
|
- (set! defined-name new-value)
|
||||||
|
- (lambda (...) ...)
|
||||||
|
- (if cond then else)
|
||||||
|
- Calling procedures:
|
||||||
|
- Defined lambdas
|
||||||
|
- Built-in things
|
||||||
|
- Undefined procedures are prototyped
|
||||||
|
|
||||||
|
TODO:
|
||||||
|
- Error reporting!
|
||||||
|
- Documentation!
|
||||||
|
- Arithmetic
|
||||||
|
- String literals?
|
||||||
|
- Typed definitions...
|
||||||
|
- See R7RS-small for more...
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "llvm/ADT/STLExtras.h"
|
||||||
|
#include "llvm/IR/BasicBlock.h"
|
||||||
|
#include "llvm/IR/Function.h"
|
||||||
|
#include "llvm/IR/Instructions.h"
|
||||||
|
#include "llvm/IR/LegacyPassManager.h"
|
||||||
|
#include "llvm/IR/Type.h"
|
||||||
|
#include "llvm/IR/Verifier.h"
|
||||||
|
#include "llvm/MC/TargetRegistry.h"
|
||||||
|
#include "llvm/Support/FileSystem.h"
|
||||||
|
#include "llvm/Support/Host.h"
|
||||||
|
#include "llvm/Support/TargetSelect.h"
|
||||||
|
#include "llvm/Support/raw_ostream.h"
|
||||||
|
#include "llvm/Target/TargetMachine.h"
|
||||||
|
#include "llvm/Target/TargetOptions.h"
|
||||||
|
|
||||||
|
#include <cstdio>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
#include "ast.hpp"
|
||||||
|
#include "parser.hpp"
|
||||||
|
#include "state.hpp"
|
||||||
|
|
||||||
|
void compileToObjectFile(CompilerState&);
|
||||||
|
int main(int argc, const char *argv[])
|
||||||
|
{
|
||||||
|
CompilerState state;
|
||||||
|
Parser parser;
|
||||||
|
|
||||||
|
for (int i = 1; i < argc; ++i)
|
||||||
|
parser.addString(argv[i]);
|
||||||
|
|
||||||
|
auto functype = llvm::FunctionType::get(llvm::Type::getInt32Ty(state.context), {}, false);
|
||||||
|
auto func = llvm::Function::Create(functype, llvm::Function::ExternalLinkage, "main", &state.module);
|
||||||
|
auto block = llvm::BasicBlock::Create(state.context, "entry", func);
|
||||||
|
state.builder.SetInsertPoint(block);
|
||||||
|
|
||||||
|
parser.parse(state);
|
||||||
|
|
||||||
|
state.builder.CreateRet(state.builder.getInt32(0));
|
||||||
|
|
||||||
|
puts("");
|
||||||
|
state.module.print(llvm::errs(), nullptr); // prints everything
|
||||||
|
|
||||||
|
compileToObjectFile(state);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void compileToObjectFile(CompilerState& state)
|
||||||
|
{
|
||||||
|
llvm::InitializeAllTargetInfos();
|
||||||
|
llvm::InitializeAllTargets();
|
||||||
|
llvm::InitializeAllTargetMCs();
|
||||||
|
llvm::InitializeAllAsmParsers();
|
||||||
|
llvm::InitializeAllAsmPrinters();
|
||||||
|
|
||||||
|
auto TargetTriple = "x86_64-pc-linux-gnu";
|
||||||
|
|
||||||
|
std::string error;
|
||||||
|
auto Target = llvm::TargetRegistry::lookupTarget(TargetTriple, error);
|
||||||
|
if (!error.empty())
|
||||||
|
puts(error.c_str());
|
||||||
|
|
||||||
|
auto CPU = "generic";
|
||||||
|
auto Features = "";
|
||||||
|
llvm::TargetOptions opt;
|
||||||
|
auto RM = llvm::Optional<llvm::Reloc::Model>();
|
||||||
|
auto TargetMachine = Target->createTargetMachine(TargetTriple, CPU, Features, opt, RM);
|
||||||
|
|
||||||
|
std::error_code EC;
|
||||||
|
llvm::raw_fd_ostream dest ("out.o", EC, llvm::sys::fs::OF_None);
|
||||||
|
|
||||||
|
llvm::legacy::PassManager pass;
|
||||||
|
|
||||||
|
if (TargetMachine->addPassesToEmitFile(pass, dest, nullptr, llvm::CGFT_ObjectFile)) {
|
||||||
|
llvm::errs() << "TargetMachine can't emit a file of this type";
|
||||||
|
} else {
|
||||||
|
pass.run(state.module);
|
||||||
|
dest.flush();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,67 @@
|
|||||||
|
/**
|
||||||
|
* 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.hpp
|
||||||
|
* @brief Source code parser to produce AST.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef PARSER_HPP
|
||||||
|
#define PARSER_HPP
|
||||||
|
|
||||||
|
#include "ast.hpp"
|
||||||
|
#include "state.hpp"
|
||||||
|
|
||||||
|
#include <deque>
|
||||||
|
#include <optional>
|
||||||
|
#include <string>
|
||||||
|
#include <variant>
|
||||||
|
|
||||||
|
class Parser {
|
||||||
|
public:
|
||||||
|
enum Error {
|
||||||
|
None,
|
||||||
|
ExpectedProcedure,
|
||||||
|
ExpectedIdentifier,
|
||||||
|
ExpectedProcedureCallClose,
|
||||||
|
ExpectedArgumentList,
|
||||||
|
UnknownIdentifier,
|
||||||
|
InvalidOperator,
|
||||||
|
InvalidOperand,
|
||||||
|
};
|
||||||
|
|
||||||
|
void addString(const std::string& str);
|
||||||
|
|
||||||
|
void consumeWhitespace();
|
||||||
|
std::optional<std::string> consumeIdentifier();
|
||||||
|
std::optional<std::variant<int, double>> consumeLiteralNumber();
|
||||||
|
|
||||||
|
AST::Node *parse(CompilerState&);
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::deque<char> text;
|
||||||
|
Error lastError = None;
|
||||||
|
|
||||||
|
AST::Node *parseExpression();
|
||||||
|
AST::Node *parseProcedureCall();
|
||||||
|
AST::Node *parseConditional();
|
||||||
|
AST::Node *parseDefinition();
|
||||||
|
AST::Node *parseAssignment();
|
||||||
|
AST::Node *parseLambdaExpression();
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // PARSER_HPP
|
||||||
|
|
@ -0,0 +1,50 @@
|
|||||||
|
/**
|
||||||
|
* 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 state.hpp
|
||||||
|
* @brief Object to maintain compiler state during code compilation.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef STATE_HPP
|
||||||
|
#define STATE_HPP
|
||||||
|
|
||||||
|
#include "ast.hpp"
|
||||||
|
|
||||||
|
#include "llvm/IR/IRBuilder.h"
|
||||||
|
#include "llvm/IR/LLVMContext.h"
|
||||||
|
#include "llvm/IR/Module.h"
|
||||||
|
|
||||||
|
#include <map>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
struct CompilerState
|
||||||
|
{
|
||||||
|
llvm::LLVMContext context;
|
||||||
|
llvm::Module module;
|
||||||
|
llvm::IRBuilder<> builder;
|
||||||
|
std::map<std::string, AST::Value> namedValues;
|
||||||
|
int scope;
|
||||||
|
|
||||||
|
CompilerState():
|
||||||
|
context(),
|
||||||
|
module("main", context),
|
||||||
|
builder(context),
|
||||||
|
scope(0) {}
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // STATE_HPP
|
||||||
|
|
Loading…
Reference in New Issue