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.

186 lines
5.5 KiB
C++

/**
* forspll - LLVM-based Forsp compiler
* Copyright (C) 2024 Clyne Sullivan <clyne@bitgloo.com>
*
* 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/>.
*/
#include <algorithm>
#include <cctype>
#include <iostream>
#include <list>
#include <map>
#include <stack>
#include <string>
#include <string_view>
#include <tuple>
#include "ast.hpp"
#include "llvm.hpp"
#include "parser.hpp"
#include "var.hpp"
llvm::Function *curFunc;
llvm::Value *curEnv;
static LLVMState llvmState;
static std::list<ThunkAST> scope;
static std::stack<std::reference_wrapper<ThunkAST>> callstack;
static bool parseString(std::string_view sv);
static std::list<ThunkAST>::iterator buildThunk(std::list<ThunkAST>::iterator it);
int main()
{
// 1. Parse code into ThunkASTs, i.e. functions with AST lists for bodies.
std::string line;
for (unsigned lineno = 1; std::cin.good(); ++lineno) {
std::getline(std::cin, line);
if (!parseString(line)) {
std::cerr << " at line " << lineno << std::endl;
return -1;
}
}
// 2. Traverse through thunks in the order they are given in the file and build IR.
Var::pushScope();
for (auto it = scope.begin(); it != scope.end();) {
it = buildThunk(it);
}
// 3. Create main() which initiate global vars and calls the top-level thunk.
auto envtype = llvm::ArrayType::get(llvmState.inttype, llvmState.envidx);
auto zerovec = llvm::ConstantVector::get(llvm::ArrayRef(llvmState.zero));
auto env = new llvm::GlobalVariable(llvmState.modul, envtype, false,
llvm::GlobalValue::InternalLinkage, zerovec, "env");
auto func = llvmState.createFunction("main");
auto entry = llvmState.createEntry(func);
llvmState.builder.SetInsertPoint(entry);
auto [t0, _] = Var::lookup("__t0");
llvmState.builder.CreateCall(llvmState.ftype, t0, llvm::ArrayRef<llvm::Value *> {env});
llvmState.builder.CreateRetVoid();
// 4. Output the IR to stdout.
llvmState.output();
std::cerr << "envidx: " << llvmState.envidx << std::endl;
return 0;
}
bool parseString(std::string_view sv)
{
do {
const auto [nsv, tok] = nextToken(sv);
if (tok == Token::none && !nsv.empty()) {
std::cerr << "error: unknown term: " << nsv << std::endl;
return false;
} else {
std::unique_ptr<BaseAST> expr;
switch (tok) {
case Token::ThunkOpen:
callstack.push(scope.emplace_back());
break;
case Token::ThunkClose:
expr.reset(new PushAST {callstack.top().get().name});
callstack.pop();
break;
case Token::Quote:
std::cerr << "error: quoting is not supported!" << std::endl;
return false;
case Token::PopVar:
expr.reset(new PopAST {name});
break;
case Token::PushVar:
expr.reset(new PushAST {name});
break;
case Token::Var:
expr.reset(new CallAST {name});
break;
case Token::Number:
expr.reset(new NumberAST {name});
break;
case Token::none:
break;
}
if (expr) {
if (!callstack.empty()) {
callstack.top().get().ast.emplace_back().swap(expr);
} else if (tok != Token::ThunkClose) {
std::cerr << "error: non-thunk at top level!" << std::endl;
return false;
}
}
}
sv = nsv;
} while (!sv.empty());
return true;
}
std::list<ThunkAST>::iterator buildThunk(std::list<ThunkAST>::iterator it)
{
auto next = it;
++next;
it->beginGen(llvmState);
Var::pushScope();
curFunc = it->func;
curEnv = it->env;
llvmState.lastSp = it->lastSp;
for (auto ait = it->ast.begin(); ait != it->ast.end(); ++ait) {
auto& a = *ait;
if (a->name.starts_with("__t")) {
it->lastSp = llvmState.lastSp;
next = buildThunk(next);
curFunc = it->func;
curEnv = it->env;
llvmState.lastSp = it->lastSp;
}
auto next = ait;
++next;
if (next != it->ast.end() && dynamic_cast<PushAST *>(ait->get()) && dynamic_cast<PopAST *>(next->get())) {
// Value (e.g. thunk) is immediately pushed then popped into a variable.
// Shortcut and simply rename the value:
Var::rename(a->name, (*next)->name);
it->ast.erase(next);
} else {
// Otherwise, just do the regular codegen.
if (a->codegen(llvmState) == nullptr)
return scope.end();
}
}
it->endGen(llvmState);
auto gen = it->codegen(llvmState);
if (!gen)
return scope.end();
Var::popScope();
Var::addLocal(it->name, Var {gen, true});
scope.erase(it);
return next;
}