break code into separate files
parent
47d7c964d0
commit
0ad40ced64
@ -0,0 +1,5 @@
|
|||||||
|
a.out
|
||||||
|
main
|
||||||
|
*.ir
|
||||||
|
*.o
|
||||||
|
*.sw*
|
@ -1,6 +1,23 @@
|
|||||||
# forspll
|
# forspll
|
||||||
|
|
||||||
forspll is an implementation of the [Forsp](https://github.com/xorvoid/forsp) language as an LLVM-based compiler.
|
forspll is an implementation of [Forsp](https://xorvoid.com/forsp.html) as an LLVM-based compiler. Forsp is a tiny yet very versatile programming language that mixes features of Forth and Lisp. Through LLVM, Forsp can be compiled into efficient machine code for a wide array of platforms.
|
||||||
|
|
||||||
TODO
|
forspll features:
|
||||||
|
|
||||||
|
* Lisp-style S-expression syntax
|
||||||
|
* Forth-style data stack for parameters/values
|
||||||
|
* Linking with C functions (see `support.c`)
|
||||||
|
|
||||||
|
Missing features:
|
||||||
|
|
||||||
|
* Quote operator: `quote`/`'`
|
||||||
|
* Lists and atoms or any dynamic allocations
|
||||||
|
|
||||||
|
## Building
|
||||||
|
|
||||||
|
Requires Clang and LLVM development files.
|
||||||
|
|
||||||
|
Run `make` to build the compiler.
|
||||||
|
|
||||||
|
Run `make prog` to compile `test.fp` and `support.c`.
|
||||||
|
|
||||||
|
@ -0,0 +1,78 @@
|
|||||||
|
#include "ast.hpp"
|
||||||
|
|
||||||
|
int ThunkAST::tcount = 0;
|
||||||
|
|
||||||
|
NumberAST::NumberAST(const std::string& n): BaseAST(n) {}
|
||||||
|
|
||||||
|
llvm::Value *NumberAST::codegen(LLVMState& llvmState) const
|
||||||
|
{
|
||||||
|
auto val = llvmState.createInt(std::stoi(name));
|
||||||
|
return llvmState.builder.CreateStore(val, llvmState.createPush());
|
||||||
|
}
|
||||||
|
|
||||||
|
PushAST::PushAST(const std::string& n): BaseAST(n) {}
|
||||||
|
|
||||||
|
llvm::Value *PushAST::codegen(LLVMState& llvmState) const
|
||||||
|
{
|
||||||
|
if (auto [var, thunk] = Var::lookup(name); var) {
|
||||||
|
auto dsget = llvmState.createPush();
|
||||||
|
if (!thunk)
|
||||||
|
var = llvmState.builder.CreateLoad(llvmState.inttype, var);
|
||||||
|
|
||||||
|
return llvmState.builder.CreateStore(var, dsget);
|
||||||
|
} else {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
PopAST::PopAST(const std::string& n): BaseAST(n) {}
|
||||||
|
|
||||||
|
llvm::Value *PopAST::codegen(LLVMState& llvmState) const
|
||||||
|
{
|
||||||
|
auto gep = llvmState.createPop();
|
||||||
|
auto var = llvmState.createVariable(name);
|
||||||
|
auto load = llvmState.builder.CreateLoad(llvmState.inttype, gep);
|
||||||
|
llvmState.builder.CreateStore(load, var, false);
|
||||||
|
|
||||||
|
return Var::addLocal(name, var).value;
|
||||||
|
}
|
||||||
|
|
||||||
|
CallAST::CallAST(const std::string& n): BaseAST(n) {}
|
||||||
|
|
||||||
|
llvm::Value *CallAST::codegen(LLVMState& llvmState) const
|
||||||
|
{
|
||||||
|
if (auto [var, call] = Var::lookup(name); var) {
|
||||||
|
if (call) {
|
||||||
|
return llvmState.builder.CreateCall(llvmState.ftype, var);
|
||||||
|
} else {
|
||||||
|
auto val = llvmState.builder.CreateLoad(llvmState.inttype, var);
|
||||||
|
auto cast = llvmState.builder.CreateIntToPtr(val,
|
||||||
|
llvmState.inttype->getPointerTo());
|
||||||
|
return llvmState.builder.CreateCall(llvmState.ftype, cast);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
auto func = llvmState.createFunction(name);
|
||||||
|
Var::addGlobal(name, Var {func, true});
|
||||||
|
return llvmState.builder.CreateCall(llvmState.ftype, func);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ThunkAST::ThunkAST(LLVMState& llvmState):
|
||||||
|
ThunkAST(llvmState, std::string("__t") + std::to_string(tcount++)) {}
|
||||||
|
|
||||||
|
ThunkAST::ThunkAST(LLVMState& llvmState, std::string n): BaseAST(n)
|
||||||
|
{
|
||||||
|
parent = llvmState.builder.saveIP();
|
||||||
|
func = llvmState.createFunction(name);
|
||||||
|
auto BB = llvmState.createEntry(func);
|
||||||
|
llvmState.builder.SetInsertPoint(BB);
|
||||||
|
}
|
||||||
|
|
||||||
|
llvm::Value *ThunkAST::codegen(LLVMState& llvmState) const
|
||||||
|
{
|
||||||
|
llvmState.builder.CreateRetVoid();
|
||||||
|
llvmState.builder.restoreIP(parent);
|
||||||
|
|
||||||
|
return func;
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,63 @@
|
|||||||
|
#ifndef FORSPLL_AST_HPP
|
||||||
|
#define FORSPLL_AST_HPP
|
||||||
|
|
||||||
|
#include "llvm.hpp"
|
||||||
|
#include "var.hpp"
|
||||||
|
|
||||||
|
#include <list>
|
||||||
|
#include <memory>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
struct BaseAST
|
||||||
|
{
|
||||||
|
std::string name;
|
||||||
|
|
||||||
|
BaseAST(const std::string& n): name(n) {}
|
||||||
|
|
||||||
|
virtual ~BaseAST() = default;
|
||||||
|
virtual llvm::Value *codegen(LLVMState&) const = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct NumberAST : public BaseAST
|
||||||
|
{
|
||||||
|
// push number onto stack
|
||||||
|
explicit NumberAST(const std::string& n);
|
||||||
|
llvm::Value *codegen(LLVMState& llvmState) const override;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct PushAST : public BaseAST
|
||||||
|
{
|
||||||
|
// push named value to stack
|
||||||
|
explicit PushAST(const std::string& n);
|
||||||
|
llvm::Value *codegen(LLVMState& llvmState) const override;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct PopAST : public BaseAST
|
||||||
|
{
|
||||||
|
// pop value on stack to named var
|
||||||
|
explicit PopAST(const std::string& n);
|
||||||
|
llvm::Value *codegen(LLVMState& llvmState) const override;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct CallAST : public BaseAST
|
||||||
|
{
|
||||||
|
// invoke named invocable
|
||||||
|
explicit CallAST(const std::string& n);
|
||||||
|
llvm::Value *codegen(LLVMState& llvmState) const override;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ThunkAST : public BaseAST
|
||||||
|
{
|
||||||
|
static int tcount;
|
||||||
|
|
||||||
|
std::list<std::unique_ptr<BaseAST>> body;
|
||||||
|
llvm::IRBuilderBase::InsertPoint parent;
|
||||||
|
llvm::Function *func;
|
||||||
|
|
||||||
|
explicit ThunkAST(LLVMState& llvmState);
|
||||||
|
explicit ThunkAST(LLVMState& llvmState, std::string n);
|
||||||
|
llvm::Value *codegen(LLVMState& llvmState) const override;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // FORSPLL_AST_HPP
|
||||||
|
|
@ -0,0 +1,64 @@
|
|||||||
|
#include "llvm.hpp"
|
||||||
|
|
||||||
|
LLVMState::LLVMState():
|
||||||
|
ctx(),
|
||||||
|
modul("forsp", ctx),
|
||||||
|
builder(ctx),
|
||||||
|
inttype(llvm::Type::getInt32Ty(ctx)),
|
||||||
|
stacktype(llvm::VectorType::get(inttype, 12, false)),
|
||||||
|
ftype(llvm::FunctionType::get(llvm::Type::getVoidTy(ctx), {}, false)),
|
||||||
|
one(llvm::ConstantInt::get(inttype, 1)),
|
||||||
|
zero(llvm::ConstantInt::get(inttype, 0))
|
||||||
|
{
|
||||||
|
auto zerovec = llvm::ConstantVector::get(llvm::ArrayRef(zero));
|
||||||
|
llvmSp = new llvm::GlobalVariable(modul, inttype, false,
|
||||||
|
llvm::GlobalValue::ExternalLinkage, zero, "sp");
|
||||||
|
llvmStack = new llvm::GlobalVariable(modul, stacktype, false,
|
||||||
|
llvm::GlobalValue::ExternalLinkage, zerovec, "stack");
|
||||||
|
}
|
||||||
|
|
||||||
|
llvm::Value *LLVMState::createPush()
|
||||||
|
{
|
||||||
|
auto dspval = builder.CreateLoad(inttype, llvmSp);
|
||||||
|
auto inc = builder.CreateAdd(dspval, one);
|
||||||
|
builder.CreateStore(inc, llvmSp, false);
|
||||||
|
|
||||||
|
return builder.CreateGEP(stacktype, llvmStack, {zero, dspval});
|
||||||
|
}
|
||||||
|
|
||||||
|
llvm::Value *LLVMState::createPop()
|
||||||
|
{
|
||||||
|
auto dspval = builder.CreateLoad(inttype, llvmSp);
|
||||||
|
auto dec = builder.CreateSub(dspval, one);
|
||||||
|
builder.CreateStore(dec, llvmSp, false);
|
||||||
|
|
||||||
|
return builder.CreateGEP(stacktype, llvmStack, {zero, dec});
|
||||||
|
}
|
||||||
|
|
||||||
|
llvm::Function *LLVMState::createFunction(const std::string& name)
|
||||||
|
{
|
||||||
|
return llvm::Function::Create(ftype, llvm::Function::ExternalLinkage,
|
||||||
|
name.c_str(), modul);
|
||||||
|
}
|
||||||
|
|
||||||
|
llvm::BasicBlock *LLVMState::createEntry(llvm::Function *func)
|
||||||
|
{
|
||||||
|
return llvm::BasicBlock::Create(ctx, "entry", func);
|
||||||
|
}
|
||||||
|
|
||||||
|
llvm::Value *LLVMState::createVariable(const std::string& name)
|
||||||
|
{
|
||||||
|
return new llvm::GlobalVariable(modul, inttype, false,
|
||||||
|
llvm::GlobalValue::InternalLinkage, zero, name);
|
||||||
|
}
|
||||||
|
|
||||||
|
llvm::Constant *LLVMState::createInt(int n)
|
||||||
|
{
|
||||||
|
return llvm::ConstantInt::get(ctx, llvm::APInt(32, n, true));
|
||||||
|
}
|
||||||
|
|
||||||
|
void LLVMState::output()
|
||||||
|
{
|
||||||
|
//std::cout << "LLVM:" << std::endl;
|
||||||
|
modul.print(llvm::errs(), nullptr);
|
||||||
|
}
|
@ -0,0 +1,41 @@
|
|||||||
|
#ifndef FORSPLL_LLVM_HPP
|
||||||
|
#define FORSPLL_LLVM_HPP
|
||||||
|
|
||||||
|
#include <llvm/ADT/APInt.h>
|
||||||
|
#include <llvm/IR/Constants.h>
|
||||||
|
#include <llvm/IR/IRBuilder.h>
|
||||||
|
#include <llvm/IR/LLVMContext.h>
|
||||||
|
#include <llvm/IR/Module.h>
|
||||||
|
#include <llvm/IR/Type.h>
|
||||||
|
#include <llvm/IR/DerivedTypes.h>
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
struct LLVMState
|
||||||
|
{
|
||||||
|
llvm::LLVMContext ctx;
|
||||||
|
llvm::Module modul;
|
||||||
|
llvm::IRBuilder<> builder;
|
||||||
|
|
||||||
|
llvm::Type *inttype;
|
||||||
|
llvm::Type *stacktype;
|
||||||
|
llvm::FunctionType *ftype;
|
||||||
|
llvm::Constant *one;
|
||||||
|
llvm::Constant *zero;
|
||||||
|
llvm::Constant *llvmSp;
|
||||||
|
llvm::Constant *llvmStack;
|
||||||
|
|
||||||
|
LLVMState();
|
||||||
|
|
||||||
|
llvm::Value *createPush();
|
||||||
|
llvm::Value *createPop();
|
||||||
|
llvm::Function *createFunction(const std::string& name);
|
||||||
|
llvm::BasicBlock *createEntry(llvm::Function *func);
|
||||||
|
llvm::Value *createVariable(const std::string& name);
|
||||||
|
llvm::Constant *createInt(int n);
|
||||||
|
|
||||||
|
void output();
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // FORSPLL_LLVM_HPP
|
||||||
|
|
@ -0,0 +1,92 @@
|
|||||||
|
#include "parser.hpp"
|
||||||
|
|
||||||
|
#include <cctype>
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
|
std::string name;
|
||||||
|
|
||||||
|
static bool isname(char ch) {
|
||||||
|
return !isspace(ch) && ch != ')';
|
||||||
|
}
|
||||||
|
|
||||||
|
static std::string_view extractName(std::string_view sv)
|
||||||
|
{
|
||||||
|
name.clear();
|
||||||
|
|
||||||
|
while (!sv.empty()) {
|
||||||
|
const auto ch = sv.front();
|
||||||
|
|
||||||
|
if (isname(ch)) {
|
||||||
|
name += ch;
|
||||||
|
sv.remove_prefix(1);
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return sv;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::pair<std::string_view, Token> nextToken(std::string_view sv)
|
||||||
|
{
|
||||||
|
if (sv.empty())
|
||||||
|
return {sv, Token::none};
|
||||||
|
|
||||||
|
while (std::isspace(sv.front()))
|
||||||
|
sv.remove_prefix(1);
|
||||||
|
|
||||||
|
if (sv.empty())
|
||||||
|
return {sv, Token::none};
|
||||||
|
|
||||||
|
const auto ch = sv.front();
|
||||||
|
if (ch == ';') {
|
||||||
|
return {{}, Token::none};
|
||||||
|
} else if (ch == '(') {
|
||||||
|
return {sv.substr(1), Token::ThunkOpen};
|
||||||
|
} else if (ch == ')') {
|
||||||
|
return {sv.substr(1), Token::ThunkClose};
|
||||||
|
} else if (ch == '\'') {
|
||||||
|
return {extractName(sv.substr(1)), Token::Quote};
|
||||||
|
} else if (ch == '$') {
|
||||||
|
return {extractName(sv.substr(1)), Token::PopVar};
|
||||||
|
} else if (ch == '^') {
|
||||||
|
return {extractName(sv.substr(1)), Token::PushVar};
|
||||||
|
} else if (isdigit(ch) || (ch == '-' && sv.size() > 1 && isdigit(sv[1]))) {
|
||||||
|
return {extractName(sv), Token::Number};
|
||||||
|
} else if (isname(ch)) {
|
||||||
|
return {extractName(sv), Token::Var};
|
||||||
|
}
|
||||||
|
|
||||||
|
return {sv, Token::none};
|
||||||
|
}
|
||||||
|
|
||||||
|
void printToken(Token tok)
|
||||||
|
{
|
||||||
|
switch (tok) {
|
||||||
|
case Token::ThunkOpen:
|
||||||
|
std::cout << "ThunkOpen ";
|
||||||
|
break;
|
||||||
|
case Token::ThunkClose:
|
||||||
|
std::cout << "ThunkClose ";
|
||||||
|
break;
|
||||||
|
case Token::Quote:
|
||||||
|
std::cout << "Quote ";
|
||||||
|
break;
|
||||||
|
case Token::PopVar:
|
||||||
|
std::cout << "PopVar ";
|
||||||
|
break;
|
||||||
|
case Token::PushVar:
|
||||||
|
std::cout << "PushVar ";
|
||||||
|
break;
|
||||||
|
case Token::Var:
|
||||||
|
std::cout << "Var ";
|
||||||
|
break;
|
||||||
|
case Token::Number:
|
||||||
|
std::cout << "Number ";
|
||||||
|
break;
|
||||||
|
case Token::none:
|
||||||
|
//std::cout << "none ";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,25 @@
|
|||||||
|
#ifndef FORSPLL_PARSER_HPP
|
||||||
|
#define FORSPLL_PARSER_HPP
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <string_view>
|
||||||
|
#include <tuple>
|
||||||
|
|
||||||
|
enum class Token {
|
||||||
|
none,
|
||||||
|
ThunkOpen,
|
||||||
|
ThunkClose,
|
||||||
|
Quote,
|
||||||
|
PopVar,
|
||||||
|
PushVar,
|
||||||
|
Var,
|
||||||
|
Number
|
||||||
|
};
|
||||||
|
|
||||||
|
extern std::string name;
|
||||||
|
|
||||||
|
std::pair<std::string_view, Token> nextToken(std::string_view sv);
|
||||||
|
void printToken(Token tok);
|
||||||
|
|
||||||
|
#endif // FORSPLL_PARSER_HPP
|
||||||
|
|
@ -0,0 +1,42 @@
|
|||||||
|
#include "var.hpp"
|
||||||
|
|
||||||
|
#include <list>
|
||||||
|
#include <map>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
static std::list<std::map<std::string, Var>> llvmVars;
|
||||||
|
|
||||||
|
Var Var::lookup(const std::string& name, int skip)
|
||||||
|
{
|
||||||
|
for (auto sc = llvmVars.rbegin(); sc != llvmVars.rend(); ++sc) {
|
||||||
|
if (skip > 0) {
|
||||||
|
--skip;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (sc->contains(name))
|
||||||
|
return (*sc)[name];
|
||||||
|
}
|
||||||
|
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
void Var::pushScope()
|
||||||
|
{
|
||||||
|
llvmVars.emplace_back();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Var::popScope()
|
||||||
|
{
|
||||||
|
llvmVars.pop_back();
|
||||||
|
}
|
||||||
|
|
||||||
|
Var& Var::addGlobal(const std::string& name, Var var)
|
||||||
|
{
|
||||||
|
return llvmVars.front().emplace(name, var).first->second;
|
||||||
|
}
|
||||||
|
|
||||||
|
Var& Var::addLocal(const std::string& name, Var var)
|
||||||
|
{
|
||||||
|
return llvmVars.back().emplace(name, var).first->second;
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,22 @@
|
|||||||
|
#ifndef FORSPLL_VAR_HPP
|
||||||
|
#define FORSPLL_VAR_HPP
|
||||||
|
|
||||||
|
#include <llvm/IR/Type.h>
|
||||||
|
#include <llvm/IR/DerivedTypes.h>
|
||||||
|
|
||||||
|
struct Var {
|
||||||
|
llvm::Value *value;
|
||||||
|
bool callable;
|
||||||
|
|
||||||
|
Var(llvm::Value *v = nullptr, bool c = false):
|
||||||
|
value(v), callable(c) {}
|
||||||
|
|
||||||
|
static Var lookup(const std::string& name, int skip = 0);
|
||||||
|
static void pushScope();
|
||||||
|
static void popScope();
|
||||||
|
static Var& addGlobal(const std::string& name, Var var);
|
||||||
|
static Var& addLocal(const std::string& name, Var var);
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // FORSPLL_VAR_HPP
|
||||||
|
|
Loading…
Reference in New Issue