Compare commits
3 Commits
d3d487159a
...
dd632b6bcc
| Author | SHA1 | Date | |
|---|---|---|---|
| dd632b6bcc | |||
| 00689d3637 | |||
| 1103efe6fe |
15
src/ast.cpp
15
src/ast.cpp
@ -90,6 +90,21 @@ namespace AST {
|
|||||||
return out.str();
|
return out.str();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::string UnaryExpression::formatted() {
|
||||||
|
std::stringstream out{ "" };
|
||||||
|
switch (this->m_unary) {
|
||||||
|
case types::Unary::AddPostfix:
|
||||||
|
out << this->m_expr->formatted() << "++";
|
||||||
|
break;
|
||||||
|
case types::Unary::AddPrefix:
|
||||||
|
out << "++" << this->m_expr->formatted();
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return out.str();
|
||||||
|
}
|
||||||
|
|
||||||
std::string ExpressionStatement::formatted() {
|
std::string ExpressionStatement::formatted() {
|
||||||
std::stringstream out{ "" };
|
std::stringstream out{ "" };
|
||||||
out << this->m_expr->formatted();
|
out << this->m_expr->formatted();
|
||||||
|
|||||||
27
src/ast.h
27
src/ast.h
@ -322,6 +322,33 @@ namespace AST {
|
|||||||
) override;
|
) override;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/// @brief Same as [prefix]value[postfix], e.g. value++ or ++value
|
||||||
|
class UnaryExpression : public Expression {
|
||||||
|
private:
|
||||||
|
std::unique_ptr<Expression> m_expr;
|
||||||
|
types::Unary m_unary;
|
||||||
|
public:
|
||||||
|
UnaryExpression(
|
||||||
|
token::Metadata meta,
|
||||||
|
std::unique_ptr<Expression> expr,
|
||||||
|
types::Unary unary)
|
||||||
|
: Expression{ meta }
|
||||||
|
, m_expr{ std::move(expr) }
|
||||||
|
, m_unary{ unary } {
|
||||||
|
}
|
||||||
|
virtual ~UnaryExpression() override = default;
|
||||||
|
virtual std::string formatted() override;
|
||||||
|
virtual codegen::StackValue codegen(codegen::Builder& builder, codegen::Scope& scope, codegen::StackAllocator& allocator) override;
|
||||||
|
virtual void codegen_alloca(codegen::StackAllocator& allocator) override;
|
||||||
|
virtual std::shared_ptr<types::Type> get_codegen_type(codegen::Scope& scope) override;
|
||||||
|
virtual void typecheck_preprocess(typecheck::Scope& scope) override;
|
||||||
|
virtual typecheck::ExpressionType typecheck(
|
||||||
|
typecheck::State& state,
|
||||||
|
typecheck::Scope& scope,
|
||||||
|
std::optional<std::shared_ptr<types::Type>> expected_ty
|
||||||
|
) override;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
class ReturnStatement : public Statement {
|
class ReturnStatement : public Statement {
|
||||||
private:
|
private:
|
||||||
|
|||||||
@ -101,4 +101,61 @@ namespace types {
|
|||||||
}
|
}
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::vector<UnopDefinition> create_unops() {
|
||||||
|
std::vector<UnopDefinition> definitions{};
|
||||||
|
|
||||||
|
auto int_ty = std::shared_ptr<types::Type>{
|
||||||
|
new types::FundamentalType{ false, types::FundamentalTypeKind::Int } };
|
||||||
|
auto char_ty = std::shared_ptr<types::Type>{
|
||||||
|
new types::FundamentalType{ false, types::FundamentalTypeKind::Char } };
|
||||||
|
|
||||||
|
// Integer arithmetic binops
|
||||||
|
for (auto& ty : { int_ty, char_ty }) {
|
||||||
|
definitions.push_back(UnopDefinition{
|
||||||
|
ty, types::Unary::AddPostfix, ty,
|
||||||
|
[](codegen::Builder& builder, std::shared_ptr<Type> ty, llvm::Value* ptr) {
|
||||||
|
codegen::TypeMap structs {};
|
||||||
|
auto llvm_ty = ty->codegen(builder, structs);
|
||||||
|
auto loaded = builder.builder->CreateLoad(llvm_ty, ptr);
|
||||||
|
auto const_1 = llvm::ConstantInt::get(llvm_ty, 1);
|
||||||
|
auto result = builder.builder->CreateAdd(loaded, const_1, "add");
|
||||||
|
builder.builder->CreateStore(result, ptr);
|
||||||
|
return reinterpret_cast<llvm::Value*>(loaded);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
definitions.push_back(UnopDefinition{
|
||||||
|
ty, types::Unary::AddPrefix, ty,
|
||||||
|
[](codegen::Builder& builder, std::shared_ptr<Type> ty, llvm::Value* ptr) {
|
||||||
|
codegen::TypeMap structs {};
|
||||||
|
auto llvm_ty = ty->codegen(builder, structs);
|
||||||
|
auto loaded = builder.builder->CreateLoad(llvm_ty, ptr);
|
||||||
|
auto const_1 = llvm::ConstantInt::get(llvm_ty, 1);
|
||||||
|
auto result = builder.builder->CreateAdd(loaded, const_1, "add");
|
||||||
|
builder.builder->CreateStore(result, ptr);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return definitions;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::optional<UnopDefinition> find_unop(
|
||||||
|
std::vector<UnopDefinition>& unops,
|
||||||
|
std::shared_ptr<types::Type> value,
|
||||||
|
Unary op) {
|
||||||
|
for (auto& unop : unops) {
|
||||||
|
if (unop.op != op) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (!types_equal(value, unop.value)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
return unop;
|
||||||
|
}
|
||||||
|
return {};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
19
src/binops.h
19
src/binops.h
@ -15,6 +15,11 @@ namespace types {
|
|||||||
GreaterThan,
|
GreaterThan,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
enum class Unary {
|
||||||
|
AddPostfix,
|
||||||
|
AddPrefix,
|
||||||
|
};
|
||||||
|
|
||||||
int operator_precedence(BinOp& op);
|
int operator_precedence(BinOp& op);
|
||||||
std::string format_operator(BinOp& op);
|
std::string format_operator(BinOp& op);
|
||||||
|
|
||||||
@ -34,6 +39,20 @@ namespace types {
|
|||||||
BinOp op,
|
BinOp op,
|
||||||
std::shared_ptr<types::Type> rhs);
|
std::shared_ptr<types::Type> rhs);
|
||||||
|
|
||||||
|
struct UnopDefinition {
|
||||||
|
std::shared_ptr<Type> value;
|
||||||
|
Unary op;
|
||||||
|
std::shared_ptr<Type> res;
|
||||||
|
llvm::Value* (*codegen)(codegen::Builder& builder, std::shared_ptr<Type> ty, llvm::Value* value);
|
||||||
|
};
|
||||||
|
|
||||||
|
std::vector<UnopDefinition> create_unops();
|
||||||
|
|
||||||
|
std::optional<UnopDefinition> find_unop(
|
||||||
|
std::vector<UnopDefinition>& unops,
|
||||||
|
std::shared_ptr<types::Type> value,
|
||||||
|
Unary op);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
@ -11,7 +11,7 @@
|
|||||||
|
|
||||||
namespace codegen {
|
namespace codegen {
|
||||||
Scope Scope::with_lvalue() {
|
Scope Scope::with_lvalue() {
|
||||||
return Scope{ this->binops, this->casts, this->structs, this->values, true };
|
return Scope{ this->binops, this->unops, this->casts, this->structs, this->values, true };
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -267,8 +267,6 @@ namespace AST {
|
|||||||
value = this->m_expr->codegen(builder, scope, allocator);
|
value = this->m_expr->codegen(builder, scope, allocator);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::cout << intended_ty->formatted() << std::endl;
|
|
||||||
|
|
||||||
std::vector<llvm::Value*> idx_list{ };
|
std::vector<llvm::Value*> idx_list{ };
|
||||||
idx_list.push_back(llvm::ConstantInt::get(builder.builder->getInt32Ty(), this->m_num));
|
idx_list.push_back(llvm::ConstantInt::get(builder.builder->getInt32Ty(), this->m_num));
|
||||||
|
|
||||||
@ -449,6 +447,20 @@ namespace AST {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::shared_ptr<types::Type> UnaryExpression::get_codegen_type(codegen::Scope& scope) {
|
||||||
|
auto unary = types::find_unop(scope.unops, this->m_expr->get_codegen_type(scope), this->m_unary);
|
||||||
|
return unary->res;
|
||||||
|
}
|
||||||
|
|
||||||
|
codegen::StackValue UnaryExpression::codegen(codegen::Builder& builder, codegen::Scope& scope, codegen::StackAllocator& allocator) {
|
||||||
|
auto unary = types::find_unop(scope.unops, this->m_expr->get_codegen_type(scope), this->m_unary);
|
||||||
|
auto lvalue = scope.with_lvalue();
|
||||||
|
auto expr_ptr = this->m_expr->codegen(builder, lvalue, allocator);
|
||||||
|
return {
|
||||||
|
unary->codegen(builder, this->m_expr->get_codegen_type(scope), expr_ptr.value),
|
||||||
|
unary->res
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
void ReturnStatement::codegen(codegen::Builder& builder, codegen::Scope& scope, codegen::StackAllocator& allocator) {
|
void ReturnStatement::codegen(codegen::Builder& builder, codegen::Scope& scope, codegen::StackAllocator& allocator) {
|
||||||
if (!builder.block)
|
if (!builder.block)
|
||||||
|
|||||||
@ -20,6 +20,7 @@ namespace codegen {
|
|||||||
|
|
||||||
struct Scope {
|
struct Scope {
|
||||||
std::vector<types::BinopDefinition>& binops;
|
std::vector<types::BinopDefinition>& binops;
|
||||||
|
std::vector<types::UnopDefinition> unops;
|
||||||
std::vector<types::CastDefinition>& casts;
|
std::vector<types::CastDefinition>& casts;
|
||||||
|
|
||||||
TypeMap structs;
|
TypeMap structs;
|
||||||
|
|||||||
@ -110,6 +110,7 @@ std::optional<CompileOutput> compile(std::string_view in_filename) {
|
|||||||
// Actual typechecking
|
// Actual typechecking
|
||||||
typecheck::State typecheck_state{};
|
typecheck::State typecheck_state{};
|
||||||
typecheck_state.binops = types::create_binops();
|
typecheck_state.binops = types::create_binops();
|
||||||
|
typecheck_state.unops = types::create_unops();
|
||||||
typecheck_state.casts = types::create_casts();
|
typecheck_state.casts = types::create_casts();
|
||||||
|
|
||||||
typecheck::Scope typecheck_scope{};
|
typecheck::Scope typecheck_scope{};
|
||||||
@ -134,6 +135,7 @@ std::optional<CompileOutput> compile(std::string_view in_filename) {
|
|||||||
|
|
||||||
codegen::Scope cg_scope{
|
codegen::Scope cg_scope{
|
||||||
.binops = typecheck_state.binops,
|
.binops = typecheck_state.binops,
|
||||||
|
.unops = typecheck_state.unops,
|
||||||
.casts = typecheck_state.casts,
|
.casts = typecheck_state.casts,
|
||||||
.structs = {},
|
.structs = {},
|
||||||
.values = {},
|
.values = {},
|
||||||
|
|||||||
@ -315,6 +315,15 @@ namespace parsing {
|
|||||||
new AST::DerefExpression(before_meta + inner.metadata(), std::move(expr))
|
new AST::DerefExpression(before_meta + inner.metadata(), std::move(expr))
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
else if (inner.peek().content == "+" && inner.peek(1).content == "+") {
|
||||||
|
inner.next();
|
||||||
|
inner.next();
|
||||||
|
auto expr = parse_primary_expression(inner, scope).unwrap();
|
||||||
|
stream.m_position = inner.m_position;
|
||||||
|
return std::unique_ptr<AST::Expression> {
|
||||||
|
new AST::UnaryExpression(before_meta + inner.metadata(), std::move(expr), types::Unary::AddPrefix)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
auto plain_expr = parse_plain_expression(inner, scope);
|
auto plain_expr = parse_plain_expression(inner, scope);
|
||||||
while (inner.peek().content == "(" || inner.peek().content == "[" || inner.peek().content == ".") {
|
while (inner.peek().content == "(" || inner.peek().content == "[" || inner.peek().content == ".") {
|
||||||
@ -350,6 +359,14 @@ namespace parsing {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (inner.peek().content == "+" && inner.peek(1).content == "+") {
|
||||||
|
inner.next();
|
||||||
|
inner.next();
|
||||||
|
plain_expr = std::unique_ptr<AST::Expression>{
|
||||||
|
new AST::UnaryExpression{before_meta + inner.metadata(), plain_expr.unwrap(), types::Unary::AddPostfix}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
stream.m_position = inner.m_position;
|
stream.m_position = inner.m_position;
|
||||||
return plain_expr;
|
return plain_expr;
|
||||||
|
|||||||
@ -67,6 +67,10 @@ namespace AST {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void UnaryExpression::codegen_alloca(codegen::StackAllocator& allocator) {
|
||||||
|
this->m_expr->codegen_alloca(allocator);
|
||||||
|
}
|
||||||
|
|
||||||
void ReturnStatement::codegen_alloca(codegen::StackAllocator& allocator) {
|
void ReturnStatement::codegen_alloca(codegen::StackAllocator& allocator) {
|
||||||
this->m_expr->codegen_alloca(allocator);
|
this->m_expr->codegen_alloca(allocator);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -537,6 +537,38 @@ namespace AST {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void UnaryExpression::typecheck_preprocess(typecheck::Scope& scope) {
|
||||||
|
this->m_expr->typecheck_preprocess(scope);
|
||||||
|
}
|
||||||
|
|
||||||
|
typecheck::ExpressionType UnaryExpression::typecheck(
|
||||||
|
typecheck::State& state,
|
||||||
|
typecheck::Scope& scope,
|
||||||
|
std::optional<std::shared_ptr<types::Type>> expected_ty
|
||||||
|
) {
|
||||||
|
auto expr_res = this->m_expr->typecheck(state, scope, expected_ty);
|
||||||
|
if (this->m_unary == types::Unary::AddPostfix || this->m_unary == types::Unary::AddPrefix) {
|
||||||
|
if (!expr_res.lvalue) {
|
||||||
|
state.errors.push_back(CompileError("Value must be a modifyable l-value", this->m_meta));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
auto unop = types::find_unop(state.unops, expr_res.type, this->m_unary);
|
||||||
|
if (unop) {
|
||||||
|
auto check_res = check_type(state, expr_res.type, unop->res);
|
||||||
|
this->m_expr = handle_res(std::move(this->m_expr), check_res, state);
|
||||||
|
return {
|
||||||
|
unop->res,
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
state.errors.push_back(CompileError("No suitable unary operator found", this->m_meta));
|
||||||
|
return { expr_res.type, false, false };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void ReturnStatement::typecheck_preprocess(typecheck::Scope& scope) {
|
void ReturnStatement::typecheck_preprocess(typecheck::Scope& scope) {
|
||||||
this->m_expr->typecheck_preprocess(scope);
|
this->m_expr->typecheck_preprocess(scope);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -17,6 +17,7 @@ namespace typecheck {
|
|||||||
|
|
||||||
struct State {
|
struct State {
|
||||||
std::vector<types::BinopDefinition> binops;
|
std::vector<types::BinopDefinition> binops;
|
||||||
|
std::vector<types::UnopDefinition> unops;
|
||||||
std::vector<types::CastDefinition> casts;
|
std::vector<types::CastDefinition> casts;
|
||||||
std::vector<CompileError> errors;
|
std::vector<CompileError> errors;
|
||||||
};
|
};
|
||||||
|
|||||||
5
test.c
5
test.c
@ -51,5 +51,10 @@ int main() {
|
|||||||
twod_array[0][0] = 50;
|
twod_array[0][0] = 50;
|
||||||
printf("2d array: %d!\n", twod_array[0][0]);
|
printf("2d array: %d!\n", twod_array[0][0]);
|
||||||
|
|
||||||
|
int counter = 0;
|
||||||
|
printf("counter: %d\n", counter++);
|
||||||
|
printf("counter: %d\n", counter++);
|
||||||
|
printf("counter: %d\n", counter++);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
Loading…
Reference in New Issue
Block a user