Compare commits

..

No commits in common. "main" and "typechecker" have entirely different histories.

23 changed files with 298 additions and 3744 deletions

View File

@ -24,8 +24,6 @@ add_executable(${PROJECT_NAME}
src/types.cpp
src/typechecker.cpp
src/binops.cpp
src/casting.cpp
src/stack_allocator.cpp
)
target_compile_features(${PROJECT_NAME} PRIVATE cxx_std_20)
target_compile_options(${PROJECT_NAME} PRIVATE -Wall -Weffc++ -Wextra -Wpedantic -Werror)

View File

@ -29,11 +29,7 @@ Currently the stages are as follows:
a format that is easier for the computer to process. The AST itself lives in
[`src/ast.h`](src/ast.h), and the code for the parsing phase lives in
[`src/parsing.cpp`](src/parsing.cpp).
4. In the typechecking stage we do static analysis on the generated AST to make
sure expected types match true types, and do other checks (such as checking
that the correct amount of parameters is provided in function calls). The
source code for this stage lives in
[`src/typechecker.cpp`](src/typechecker.cpp).
4. **TODO:** Typechecking phase hasn't yet been developed, but it will go here.
5. Finally the program is **compiled**, or in other words **code-generated**,
hence why this is the **codegen** stage. This is where the AST from the
previous stages is taken and LLVM Intermediate Representation is produced

View File

@ -6,7 +6,6 @@ namespace AST {
std::string IntLiteralExpression::formatted() {
std::stringstream out{ "" };
out << this->m_value;
out << this->m_ty->formatted();
return out.str();
}
@ -43,84 +42,6 @@ namespace AST {
return out.str();
}
std::string CastExpression::formatted() {
std::stringstream out{ "" };
out << "(" << this->m_ty->formatted() << ")";
out << this->m_expr->formatted();
return out.str();
}
std::string RefExpression::formatted() {
std::stringstream out{ "" };
out << "&" << this->m_expr->formatted();
return out.str();
}
std::string DerefExpression::formatted() {
std::stringstream out{ "" };
out << "*" << this->m_expr->formatted();
return out.str();
}
std::string IndexAccessExpression::formatted() {
std::stringstream out{ "" };
out << this->m_expr->formatted();
out << "[" << this->m_num << "]";
return out.str();
}
std::string FieldAccessExpression::formatted() {
std::stringstream out{ "" };
out << this->m_expr->formatted();
out << "." << this->m_field;
return out.str();
}
std::string ListInitializerExpression::formatted() {
std::stringstream out{ "" };
out << "{ ";
int counter = 0;
for (auto& expr : this->m_expressions) {
if (counter++ > 0)
out << ", ";
out << expr->formatted();
}
out << " }";
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;
case types::Unary::SubPostfix:
out << this->m_expr->formatted() << "--";
break;
case types::Unary::SubPrefix:
out << "--" << this->m_expr->formatted();
break;
case types::Unary::Not:
out << "!" << this->m_expr->formatted();
break;
case types::Unary::Negation:
out << "-" << this->m_expr->formatted();
break;
case types::Unary::Plus:
out << "+" << this->m_expr->formatted();
break;
default:
break;
}
return out.str();
}
std::string ExpressionStatement::formatted() {
std::stringstream out{ "" };
out << this->m_expr->formatted();
@ -138,34 +59,6 @@ namespace AST {
return out.str();
}
std::string ForStatement::formatted() {
std::stringstream out{ "" };
out << "for (";
if (this->m_init)
out << (*this->m_init)->formatted();
out << "; ";
if (this->m_cond)
out << (*this->m_cond)->formatted();
out << "; ";
if (this->m_after)
out << (*this->m_after)->formatted();
out << ")";
out << "\n then " << this->m_loop->formatted();
return out.str();
}
std::string WhileStatement::formatted() {
std::stringstream out{ "" };
out << "while (";
if (this->m_cond)
out << (*this->m_cond)->formatted();
out << ")";
out << "\n then " << this->m_loop->formatted();
return out.str();
}
std::string ReturnStatement::formatted() {
std::stringstream out{ "" };
out << "return ";
@ -174,24 +67,6 @@ namespace AST {
return out.str();
}
std::string CompoundStatement::formatted() {
std::stringstream out{ "" };
out << "{\n";
for (auto& statement : this->m_statements) {
out << " " << statement->formatted() << "\n";
}
out << "}";
return out.str();
}
std::string BreakStatement::formatted() {
return "break;";
}
std::string ContinueStatement::formatted() {
return "continue;";
}
std::string InitializationStatement::formatted() {
std::stringstream out{ "" };
out << this->m_type->formatted() << " " << this->m_name;
@ -233,8 +108,4 @@ namespace AST {
}
return out.str();
}
std::string TopLevelTypedef::formatted() {
return this->m_ty->formatted();
}
}

371
src/ast.h
View File

@ -10,7 +10,6 @@
#include "binops.h"
#include "tokens.h"
#include "typechecker.h"
#include "stack_allocator.h"
namespace AST {
class Node {
@ -25,11 +24,8 @@ namespace AST {
class Expression : public Node {
public:
Expression(token::Metadata meta) : Node{ meta } {}
virtual codegen::StackValue codegen(codegen::Builder& builder, codegen::Scope& scope, codegen::StackAllocator& allocator) = 0;
virtual void codegen_alloca(codegen::StackAllocator& allocator) = 0;
virtual std::shared_ptr<types::Type> get_codegen_type(codegen::Scope& scope) = 0;
virtual void typecheck_preprocess(typecheck::Scope& scope) = 0;
virtual typecheck::ExpressionType typecheck(
virtual codegen::StackValue codegen(codegen::Builder& builder, codegen::Scope& scope) = 0;
virtual std::shared_ptr<types::Type> typecheck(
typecheck::State& state,
typecheck::Scope& scope,
std::optional<std::shared_ptr<types::Type>> expected_ty
@ -39,39 +35,25 @@ namespace AST {
class Statement : public Node {
public:
Statement(token::Metadata meta) : Node{ meta } {}
virtual void codegen(codegen::Builder& builder, codegen::Scope& scope, codegen::StackAllocator& allocator) = 0;
virtual void codegen_alloca(codegen::StackAllocator& allocator) = 0;
virtual void typecheck_preprocess(typecheck::Scope& scope) = 0;
virtual void codegen(codegen::Builder& builder, codegen::Scope& scope) = 0;
virtual void typecheck(typecheck::State& state, typecheck::Scope& scope) = 0;
};
/// @brief Any integer literal
class IntLiteralExpression : public Expression {
private:
int m_value;
std::shared_ptr<types::Type> m_ty;
public:
IntLiteralExpression(token::Metadata meta, int value)
: Expression{ meta }
, m_value{ value }
, m_ty{ { std::shared_ptr<types::Type>{
new types::FundamentalType{true, types::FundamentalTypeKind::AnyInt}
} } } {
}
IntLiteralExpression(token::Metadata meta, int value) : Expression{ meta }, m_value{ value } {}
virtual ~IntLiteralExpression() 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(
virtual codegen::StackValue codegen(codegen::Builder& builder, codegen::Scope& scope) override;
virtual std::shared_ptr<types::Type> typecheck(
typecheck::State& state,
typecheck::Scope& scope,
std::optional<std::shared_ptr<types::Type>> expected_ty
) override;
};
/// @brief i.e. "contents"
class StringLiteralExpression : public Expression {
private:
std::string m_value;
@ -79,18 +61,14 @@ namespace AST {
StringLiteralExpression(token::Metadata meta, std::string value) : Expression{ meta }, m_value{ value } {}
virtual ~StringLiteralExpression() 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(
virtual codegen::StackValue codegen(codegen::Builder& builder, codegen::Scope& scope) override;
virtual std::shared_ptr<types::Type> typecheck(
typecheck::State& state,
typecheck::Scope& scope,
std::optional<std::shared_ptr<types::Type>> expected_ty
) override;
};
/// @brief Anything that references by value
class ValueReferenceExpression : public Expression {
private:
std::string m_name;
@ -98,18 +76,14 @@ namespace AST {
ValueReferenceExpression(token::Metadata meta, std::string name) : Expression{ meta }, m_name{ name } {}
virtual ~ValueReferenceExpression() 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(
virtual codegen::StackValue codegen(codegen::Builder& builder, codegen::Scope& scope) override;
virtual std::shared_ptr<types::Type> typecheck(
typecheck::State& state,
typecheck::Scope& scope,
std::optional<std::shared_ptr<types::Type>> expected_ty
) override;
};
/// @brief Any binary operation (i.e. lhs + rhs)
class BinaryOperationExpression : public Expression {
private:
std::unique_ptr<Expression> m_lhs;
@ -128,18 +102,14 @@ namespace AST {
}
virtual ~BinaryOperationExpression() 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(
virtual codegen::StackValue codegen(codegen::Builder& builder, codegen::Scope& scope) override;
virtual std::shared_ptr<types::Type> typecheck(
typecheck::State& state,
typecheck::Scope& scope,
std::optional<std::shared_ptr<types::Type>> expected_ty
) override;
};
/// @brief Same as fn()/// @brief
class FunctionCallExpression : public Expression {
private:
std::unique_ptr<Expression> m_fn_expr;
@ -155,194 +125,8 @@ namespace AST {
}
virtual ~FunctionCallExpression() 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;
};
/// @brief Same as (type) value
class CastExpression : public Expression {
private:
std::shared_ptr<types::Type> m_ty;
std::unique_ptr<Expression> m_expr;
public:
CastExpression(
token::Metadata meta,
std::shared_ptr<types::Type> type,
std::unique_ptr<Expression> expr)
: Expression{ meta }
, m_ty{ type }
, m_expr{ std::move(expr) } {
}
virtual ~CastExpression() 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;
};
/// @brief Same as &value
class RefExpression : public Expression {
private:
std::unique_ptr<Expression> m_expr;
public:
RefExpression(
token::Metadata meta,
std::unique_ptr<Expression> expr)
: Expression{ meta }
, m_expr{ std::move(expr) } {
}
virtual ~RefExpression() 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;
};
/// @brief Same as *value
class DerefExpression : public Expression {
private:
std::unique_ptr<Expression> m_expr;
public:
DerefExpression(
token::Metadata meta,
std::unique_ptr<Expression> expr)
: Expression{ meta }
, m_expr{ std::move(expr) } {
}
virtual ~DerefExpression() 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;
};
/// @brief Same as value[num]
class IndexAccessExpression : public Expression {
private:
std::unique_ptr<Expression> m_expr;
uint32_t m_num;
public:
IndexAccessExpression(
token::Metadata meta,
std::unique_ptr<Expression> expr,
uint32_t num)
: Expression{ meta }
, m_expr{ std::move(expr) }
, m_num{ num } {
}
virtual ~IndexAccessExpression() 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;
};
/// @brief Same as value.field
class FieldAccessExpression : public Expression {
private:
std::unique_ptr<Expression> m_expr;
std::string m_field;
public:
FieldAccessExpression(
token::Metadata meta,
std::unique_ptr<Expression> expr,
std::string field)
: Expression{ meta }
, m_expr{ std::move(expr) }
, m_field{ field } {
}
virtual ~FieldAccessExpression() 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;
};
/// @brief Same as {value1, value2}
class ListInitializerExpression : public Expression {
private:
std::vector<std::unique_ptr<Expression>> m_expressions;
std::shared_ptr<types::Type> m_ty;
public:
ListInitializerExpression(
token::Metadata meta,
std::vector<std::unique_ptr<Expression>> expressions,
std::shared_ptr<types::Type> type)
: Expression{ meta }
, m_expressions{ std::move(expressions) }
, m_ty{ type } {
}
virtual ~ListInitializerExpression() 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;
};
/// @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(
virtual codegen::StackValue codegen(codegen::Builder& builder, codegen::Scope& scope) override;
virtual std::shared_ptr<types::Type> typecheck(
typecheck::State& state,
typecheck::Scope& scope,
std::optional<std::shared_ptr<types::Type>> expected_ty
@ -359,9 +143,7 @@ namespace AST {
}
virtual ~ReturnStatement() override = default;
virtual std::string formatted() override;
virtual void codegen(codegen::Builder& builder, codegen::Scope& scope, codegen::StackAllocator& allocator) override;
virtual void codegen_alloca(codegen::StackAllocator& allocator) override;
virtual void typecheck_preprocess(typecheck::Scope& scope) override;
virtual void codegen(codegen::Builder& builder, codegen::Scope& scope) override;
virtual void typecheck(typecheck::State& state, typecheck::Scope& scope) override;
};
@ -383,9 +165,7 @@ namespace AST {
}
virtual ~InitializationStatement() override = default;
virtual std::string formatted() override;
virtual void codegen(codegen::Builder& builder, codegen::Scope& scope, codegen::StackAllocator& allocator) override;
virtual void codegen_alloca(codegen::StackAllocator& allocator) override;
virtual void typecheck_preprocess(typecheck::Scope& scope) override;
virtual void codegen(codegen::Builder& builder, codegen::Scope& scope) override;
virtual void typecheck(typecheck::State& state, typecheck::Scope& scope) override;
};
@ -398,9 +178,7 @@ namespace AST {
}
virtual ~ExpressionStatement() override = default;
virtual std::string formatted() override;
virtual void codegen(codegen::Builder& builder, codegen::Scope& scope, codegen::StackAllocator& allocator) override;
virtual void codegen_alloca(codegen::StackAllocator& allocator) override;
virtual void typecheck_preprocess(typecheck::Scope& scope) override;
virtual void codegen(codegen::Builder& builder, codegen::Scope& scope) override;
virtual void typecheck(typecheck::State& state, typecheck::Scope& scope) override;
};
@ -421,101 +199,7 @@ namespace AST {
}
virtual ~IfStatement() override = default;
virtual std::string formatted() override;
virtual void codegen(codegen::Builder& builder, codegen::Scope& scope, codegen::StackAllocator& allocator) override;
virtual void codegen_alloca(codegen::StackAllocator& allocator) override;
virtual void typecheck_preprocess(typecheck::Scope& scope) override;
virtual void typecheck(typecheck::State& state, typecheck::Scope& scope) override;
};
class ForStatement : public Statement {
private:
std::optional<std::unique_ptr<Statement>> m_init;
std::optional<std::unique_ptr<Expression>> m_cond;
std::optional<std::unique_ptr<Expression>> m_after;
std::unique_ptr<Statement> m_loop;
public:
ForStatement(token::Metadata meta,
std::optional<std::unique_ptr<Statement>> init,
std::optional<std::unique_ptr<Expression>> cond,
std::optional<std::unique_ptr<Expression>> after,
std::unique_ptr<Statement> loop)
: Statement{ meta }
, m_init{ std::move(init) }
, m_cond{ std::move(cond) }
, m_after{ std::move(after) }
, m_loop{ std::move(loop) } {
}
virtual ~ForStatement() override = default;
virtual std::string formatted() override;
virtual void codegen(codegen::Builder& builder, codegen::Scope& scope, codegen::StackAllocator& allocator) override;
virtual void codegen_alloca(codegen::StackAllocator& allocator) override;
virtual void typecheck_preprocess(typecheck::Scope& scope) override;
virtual void typecheck(typecheck::State& state, typecheck::Scope& scope) override;
};
class WhileStatement : public Statement {
private:
std::optional<std::unique_ptr<Expression>> m_cond;
std::unique_ptr<Statement> m_loop;
public:
WhileStatement(token::Metadata meta,
std::optional<std::unique_ptr<Expression>> cond,
std::unique_ptr<Statement> loop)
: Statement{ meta }
, m_cond{ std::move(cond) }
, m_loop{ std::move(loop) } {
}
virtual ~WhileStatement() override = default;
virtual std::string formatted() override;
virtual void codegen(codegen::Builder& builder, codegen::Scope& scope, codegen::StackAllocator& allocator) override;
virtual void codegen_alloca(codegen::StackAllocator& allocator) override;
virtual void typecheck_preprocess(typecheck::Scope& scope) override;
virtual void typecheck(typecheck::State& state, typecheck::Scope& scope) override;
};
/** @brief a block e.g. {statement; statement;} */
class CompoundStatement : public Statement {
private:
std::vector<std::unique_ptr<Statement>> m_statements;
public:
CompoundStatement(token::Metadata meta,
std::vector<std::unique_ptr<Statement>> statements)
: Statement{ meta }
, m_statements{ std::move(statements) } {
}
virtual ~CompoundStatement() override = default;
virtual std::string formatted() override;
virtual void codegen(codegen::Builder& builder, codegen::Scope& scope, codegen::StackAllocator& allocator) override;
virtual void codegen_alloca(codegen::StackAllocator& allocator) override;
virtual void typecheck_preprocess(typecheck::Scope& scope) override;
virtual void typecheck(typecheck::State& state, typecheck::Scope& scope) override;
};
/** @brief break-keyword */
class BreakStatement : public Statement {
public:
BreakStatement(token::Metadata meta)
: Statement{ meta } {
}
virtual ~BreakStatement() override = default;
virtual std::string formatted() override;
virtual void codegen(codegen::Builder& builder, codegen::Scope& scope, codegen::StackAllocator& allocator) override;
virtual void codegen_alloca(codegen::StackAllocator& allocator) override;
virtual void typecheck_preprocess(typecheck::Scope& scope) override;
virtual void typecheck(typecheck::State& state, typecheck::Scope& scope) override;
};
/** @brief continue-keyword */
class ContinueStatement : public Statement {
public:
ContinueStatement(token::Metadata meta)
: Statement{ meta } {
}
virtual ~ContinueStatement() override = default;
virtual std::string formatted() override;
virtual void codegen(codegen::Builder& builder, codegen::Scope& scope, codegen::StackAllocator& allocator) override;
virtual void codegen_alloca(codegen::StackAllocator& allocator) override;
virtual void typecheck_preprocess(typecheck::Scope& scope) override;
virtual void codegen(codegen::Builder& builder, codegen::Scope& scope) override;
virtual void typecheck(typecheck::State& state, typecheck::Scope& scope) override;
};
@ -523,7 +207,6 @@ namespace AST {
public:
TopLevelStatement(token::Metadata meta) : Node{ meta } {}
virtual void codegen(codegen::Builder& builder, codegen::Scope& scope) = 0;
virtual void typecheck_preprocess(typecheck::Scope& scope) = 0;
virtual void typecheck(typecheck::State& state, typecheck::Scope& scope) = 0;
};
@ -552,24 +235,6 @@ namespace AST {
virtual ~Function() override = default;
virtual std::string formatted() override;
virtual void codegen(codegen::Builder& builder, codegen::Scope& scope) override;
virtual void typecheck_preprocess(typecheck::Scope& scope) override;
virtual void typecheck(typecheck::State& state, typecheck::Scope& scope) override;
};
class TopLevelTypedef : public TopLevelStatement {
private:
std::shared_ptr<types::Type> m_ty;
public:
TopLevelTypedef(
token::Metadata meta,
std::shared_ptr<types::Type> type)
: TopLevelStatement{ meta }
, m_ty{ type } {
}
virtual ~TopLevelTypedef() override = default;
virtual std::string formatted() override;
virtual void codegen(codegen::Builder& builder, codegen::Scope& scope) override;
virtual void typecheck_preprocess(typecheck::Scope& scope) override;
virtual void typecheck(typecheck::State& state, typecheck::Scope& scope) override;
};
}

View File

@ -6,244 +6,40 @@ namespace types {
std::vector<BinopDefinition> definitions{};
auto int_ty = std::shared_ptr<types::Type>{
new types::FundamentalType{ false, types::FundamentalTypeKind::Int } };
auto uint_ty = std::shared_ptr<types::Type>{
new types::FundamentalType{ false, types::FundamentalTypeKind::UInt } };
new types::FundamentalType{ types::FundamentalTypeKind::Int } };
auto char_ty = std::shared_ptr<types::Type>{
new types::FundamentalType{ false, types::FundamentalTypeKind::Char } };
auto uchar_ty = std::shared_ptr<types::Type>{
new types::FundamentalType{ false, types::FundamentalTypeKind::UChar } };
auto short_int_ty = std::shared_ptr<types::Type>{
new types::FundamentalType{ false, types::FundamentalTypeKind::ShortInt } };
auto ushort_int_ty = std::shared_ptr<types::Type>{
new types::FundamentalType{ false, types::FundamentalTypeKind::UShortInt } };
auto long_int_ty = std::shared_ptr<types::Type>{
new types::FundamentalType{ false, types::FundamentalTypeKind::LongInt } };
auto ulong_int_ty = std::shared_ptr<types::Type>{
new types::FundamentalType{ false, types::FundamentalTypeKind::ULongInt } };
auto long_long_int_ty = std::shared_ptr<types::Type>{
new types::FundamentalType{ false, types::FundamentalTypeKind::LongLongInt } };
auto ulong_long_int_ty = std::shared_ptr<types::Type>{
new types::FundamentalType{ false, types::FundamentalTypeKind::ULongLongInt } };
new types::FundamentalType{ types::FundamentalTypeKind::Char } };
auto bool_ty = std::shared_ptr<types::Type>{
new types::FundamentalType{ false, types::FundamentalTypeKind::Bool } };
new types::FundamentalType{ types::FundamentalTypeKind::Bool } };
// Integer arithmetic binops
for (auto& ty : {
short_int_ty, int_ty, long_int_ty, long_long_int_ty, char_ty,
ushort_int_ty, uint_ty, ulong_int_ty, ulong_long_int_ty, uchar_ty,
bool_ty
}) {
for (auto& ty : { int_ty, char_ty, bool_ty }) {
// Arithmetic binops
definitions.push_back(BinopDefinition{
ty, types::BinOp::Add, ty, ty, false,
[](codegen::Builder& builder, llvm::Value* lhs, llvm::Value* rhs) {
ty, types::BinOp::Add, ty,
ty, [](codegen::Builder& builder, llvm::Value* lhs, llvm::Value* rhs) {
return builder.builder->CreateAdd(lhs, rhs, "add");
},
});
} });
definitions.push_back(BinopDefinition{
ty, types::BinOp::Sub, ty, ty, false,
[](codegen::Builder& builder, llvm::Value* lhs, llvm::Value* rhs) {
ty, types::BinOp::Sub, ty,
ty, [](codegen::Builder& builder, llvm::Value* lhs, llvm::Value* rhs) {
return builder.builder->CreateSub(lhs, rhs, "sub");
},
});
} });
// Comparisons
definitions.push_back(BinopDefinition{
ty, types::BinOp::Mul, ty, ty, false,
[](codegen::Builder& builder, llvm::Value* lhs, llvm::Value* rhs) {
return builder.builder->CreateMul(lhs, rhs, "mul");
},
});
definitions.push_back(BinopDefinition{
ty, types::BinOp::BitAnd, ty, ty, false,
[](codegen::Builder& builder, llvm::Value* lhs, llvm::Value* rhs) {
return builder.builder->CreateAnd(lhs, rhs, "and");
},
});
definitions.push_back(BinopDefinition{
ty, types::BinOp::BitOr, ty, ty, false,
[](codegen::Builder& builder, llvm::Value* lhs, llvm::Value* rhs) {
return builder.builder->CreateOr(lhs, rhs, "or");
},
});
definitions.push_back(BinopDefinition{
ty, types::BinOp::BitXor, ty, ty, false,
[](codegen::Builder& builder, llvm::Value* lhs, llvm::Value* rhs) {
return builder.builder->CreateXor(lhs, rhs, "xor");
},
});
definitions.push_back(BinopDefinition{
ty, types::BinOp::BitShiftLeft, ty, ty, false,
[](codegen::Builder& builder, llvm::Value* lhs, llvm::Value* rhs) {
return builder.builder->CreateShl(lhs, rhs, "shl");
},
});
definitions.push_back(BinopDefinition{
ty, types::BinOp::BitShiftRight, ty, ty, false,
[](codegen::Builder& builder, llvm::Value* lhs, llvm::Value* rhs) {
return builder.builder->CreateAShr(lhs, rhs, "shr");
},
});
}
// Signed Integer arithmetic binops
for (auto& ty : {
short_int_ty, int_ty, long_int_ty, long_long_int_ty, char_ty,
}) {
definitions.push_back(BinopDefinition{
ty, types::BinOp::Div, ty, ty, false,
[](codegen::Builder& builder, llvm::Value* lhs, llvm::Value* rhs) {
return builder.builder->CreateSDiv(lhs, rhs, "sdiv");
},
});
definitions.push_back(BinopDefinition{
ty, types::BinOp::Modulo, ty, ty, false,
[](codegen::Builder& builder, llvm::Value* lhs, llvm::Value* rhs) {
auto div_trunc = builder.builder->CreateSDiv(lhs, rhs, "mod.sdiv");
auto div_mul = builder.builder->CreateMul(div_trunc, rhs, "mod.mul");
return builder.builder->CreateSub(lhs, div_mul, "mod.value");
},
});
}
// Unsigned Integer arithmetic binops
for (auto& ty : {
ushort_int_ty, uint_ty, ulong_int_ty, ulong_long_int_ty, uchar_ty,
bool_ty
}) {
definitions.push_back(BinopDefinition{
ty, types::BinOp::Div, ty, ty, false,
[](codegen::Builder& builder, llvm::Value* lhs, llvm::Value* rhs) {
return builder.builder->CreateUDiv(lhs, rhs, "udiv");
},
});
definitions.push_back(BinopDefinition{
ty, types::BinOp::Modulo, ty, ty, false,
[](codegen::Builder& builder, llvm::Value* lhs, llvm::Value* rhs) {
auto div_trunc = builder.builder->CreateUDiv(lhs, rhs, "mod.udiv");
auto div_mul = builder.builder->CreateMul(div_trunc, rhs, "mod.mul");
return builder.builder->CreateSub(lhs, div_mul, "mod.value");
},
});
}
// Signed comparisons
for (auto& ty : {
short_int_ty, int_ty, long_int_ty, long_long_int_ty, char_ty,
}) {
definitions.push_back(BinopDefinition{
ty, types::BinOp::LessThan, ty, bool_ty, false,
[](codegen::Builder& builder, llvm::Value* lhs, llvm::Value* rhs) {
return builder.builder->CreateICmpSLT(lhs, rhs, "icmpslt");
},
});
definitions.push_back(BinopDefinition{
ty, types::BinOp::GreaterThan, ty, bool_ty, false,
[](codegen::Builder& builder, llvm::Value* lhs, llvm::Value* rhs) {
return builder.builder->CreateICmpSGT(lhs, rhs, "icmpsgt");
},
});
definitions.push_back(BinopDefinition{
ty, types::BinOp::LessThanEquals, ty, bool_ty, false,
[](codegen::Builder& builder, llvm::Value* lhs, llvm::Value* rhs) {
ty, types::BinOp::LessThan, ty,
bool_ty, [](codegen::Builder& builder, llvm::Value* lhs, llvm::Value* rhs) {
return builder.builder->CreateICmpSLE(lhs, rhs, "icmpsle");
},
});
} });
definitions.push_back(BinopDefinition{
ty, types::BinOp::GreaterThanEquals, ty, bool_ty, false,
[](codegen::Builder& builder, llvm::Value* lhs, llvm::Value* rhs) {
return builder.builder->CreateICmpSGE(lhs, rhs, "icmpsge");
},
});
definitions.push_back(BinopDefinition{
ty, types::BinOp::Equals, ty, bool_ty, false,
[](codegen::Builder& builder, llvm::Value* lhs, llvm::Value* rhs) {
return builder.builder->CreateICmpEQ(lhs, rhs, "icmpeq");
},
});
definitions.push_back(BinopDefinition{
ty, types::BinOp::NotEquals, ty, bool_ty, false,
[](codegen::Builder& builder, llvm::Value* lhs, llvm::Value* rhs) {
return builder.builder->CreateICmpNE(lhs, rhs, "icmpne");
},
});
ty, types::BinOp::GreaterThan, ty,
bool_ty, [](codegen::Builder& builder, llvm::Value* lhs, llvm::Value* rhs) {
return builder.builder->CreateICmpSGT(lhs, rhs, "icmpsgt");
} });
}
// Unsigned comparisons
for (auto& ty : {
ushort_int_ty, uint_ty, ulong_int_ty, ulong_long_int_ty, uchar_ty,
bool_ty
}) {
definitions.push_back(BinopDefinition{
ty, types::BinOp::LessThan, ty, bool_ty, false,
[](codegen::Builder& builder, llvm::Value* lhs, llvm::Value* rhs) {
return builder.builder->CreateICmpULT(lhs, rhs, "icmpult");
},
});
definitions.push_back(BinopDefinition{
ty, types::BinOp::GreaterThan, ty, bool_ty, false,
[](codegen::Builder& builder, llvm::Value* lhs, llvm::Value* rhs) {
return builder.builder->CreateICmpUGT(lhs, rhs, "icmpugt");
},
});
definitions.push_back(BinopDefinition{
ty, types::BinOp::LessThanEquals, ty, bool_ty, false,
[](codegen::Builder& builder, llvm::Value* lhs, llvm::Value* rhs) {
return builder.builder->CreateICmpULE(lhs, rhs, "icmpule");
},
});
definitions.push_back(BinopDefinition{
ty, types::BinOp::GreaterThanEquals, ty, bool_ty, false,
[](codegen::Builder& builder, llvm::Value* lhs, llvm::Value* rhs) {
return builder.builder->CreateICmpUGE(lhs, rhs, "icmpuge");
},
});
definitions.push_back(BinopDefinition{
ty, types::BinOp::Equals, ty, bool_ty, false,
[](codegen::Builder& builder, llvm::Value* lhs, llvm::Value* rhs) {
return builder.builder->CreateICmpEQ(lhs, rhs, "icmpeq");
},
});
definitions.push_back(BinopDefinition{
ty, types::BinOp::NotEquals, ty, bool_ty, false,
[](codegen::Builder& builder, llvm::Value* lhs, llvm::Value* rhs) {
return builder.builder->CreateICmpNE(lhs, rhs, "icmpne");
},
});
}
definitions.push_back(BinopDefinition{
bool_ty, types::BinOp::And, bool_ty, bool_ty, false,
[](codegen::Builder& builder, llvm::Value* lhs, llvm::Value* rhs) {
return builder.builder->CreateAnd(lhs, rhs, "and");
},
});
definitions.push_back(BinopDefinition{
bool_ty, types::BinOp::Or, bool_ty, bool_ty, false,
[](codegen::Builder& builder, llvm::Value* lhs, llvm::Value* rhs) {
return builder.builder->CreateOr(lhs, rhs, "or");
},
});
return definitions;
}
@ -262,156 +58,6 @@ namespace types {
return binop;
}
if (op == BinOp::Assignment) {
auto void_ty = std::shared_ptr<types::Type>{
new types::FundamentalType{ false, types::FundamentalTypeKind::Void } };;
return BinopDefinition{
lhs, types::BinOp::Assignment, lhs, void_ty, true,
[](codegen::Builder& builder, llvm::Value* lhs, llvm::Value* rhs) {
builder.builder->CreateStore(rhs, lhs);
return llvm::dyn_cast<llvm::Value>(llvm::ConstantInt::get(llvm::IntegerType::get(*builder.context, 1), 0));
},
};
}
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 uint_ty = std::shared_ptr<types::Type>{
new types::FundamentalType{ false, types::FundamentalTypeKind::UInt } };
auto char_ty = std::shared_ptr<types::Type>{
new types::FundamentalType{ false, types::FundamentalTypeKind::Char } };
auto uchar_ty = std::shared_ptr<types::Type>{
new types::FundamentalType{ false, types::FundamentalTypeKind::UChar } };
auto short_int_ty = std::shared_ptr<types::Type>{
new types::FundamentalType{ false, types::FundamentalTypeKind::ShortInt } };
auto ushort_int_ty = std::shared_ptr<types::Type>{
new types::FundamentalType{ false, types::FundamentalTypeKind::UShortInt } };
auto long_int_ty = std::shared_ptr<types::Type>{
new types::FundamentalType{ false, types::FundamentalTypeKind::LongInt } };
auto ulong_int_ty = std::shared_ptr<types::Type>{
new types::FundamentalType{ false, types::FundamentalTypeKind::ULongInt } };
auto long_long_int_ty = std::shared_ptr<types::Type>{
new types::FundamentalType{ false, types::FundamentalTypeKind::LongLongInt } };
auto ulong_long_int_ty = std::shared_ptr<types::Type>{
new types::FundamentalType{ false, types::FundamentalTypeKind::ULongLongInt } };
auto bool_ty = std::shared_ptr<types::Type>{
new types::FundamentalType{ false, types::FundamentalTypeKind::Bool } };
// Integer Increment/Decrement unaries
for (auto& ty : {
short_int_ty, int_ty, long_int_ty, long_long_int_ty, char_ty,
ushort_int_ty, uint_ty, ulong_int_ty, ulong_long_int_ty, uchar_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, "load");
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, "load");
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;
}
});
definitions.push_back(UnopDefinition{
ty, types::Unary::SubPostfix, 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, "load");
auto const_1 = llvm::ConstantInt::get(llvm_ty, 1);
auto result = builder.builder->CreateSub(loaded, const_1, "sub");
builder.builder->CreateStore(result, ptr);
return reinterpret_cast<llvm::Value*>(loaded);
}
});
definitions.push_back(UnopDefinition{
ty, types::Unary::SubPrefix, 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, "load");
auto const_1 = llvm::ConstantInt::get(llvm_ty, 1);
auto result = builder.builder->CreateSub(loaded, const_1, "sub");
builder.builder->CreateStore(result, ptr);
return result;
}
});
}
// Not & Negation
for (auto& ty : {
short_int_ty, int_ty, long_int_ty, long_long_int_ty, char_ty,
ushort_int_ty, uint_ty, ulong_int_ty, ulong_long_int_ty, uchar_ty,
bool_ty
}) {
definitions.push_back(UnopDefinition{
ty, types::Unary::Not, ty,
[](codegen::Builder& builder, std::shared_ptr<Type> ty, llvm::Value* value) {
codegen::TypeMap structs {};
auto llvm_ty = ty->codegen(builder, structs);
auto const_1 = llvm::ConstantInt::get(llvm_ty, 1);
auto const_0 = llvm::ConstantInt::get(llvm_ty, 0);
auto cmp = builder.builder->CreateICmp(llvm::CmpInst::Predicate::ICMP_EQ, value, const_0, "not_cmp");
return builder.builder->CreateSelect(cmp, const_1, const_0, "not_select");
}
});
definitions.push_back(UnopDefinition{
ty, types::Unary::Negation, ty,
[](codegen::Builder& builder, std::shared_ptr<Type>, llvm::Value* value) {
return builder.builder->CreateNeg(value, "neg");
}
});
definitions.push_back(UnopDefinition{
ty, types::Unary::Plus, ty,
[](codegen::Builder&, std::shared_ptr<Type>, llvm::Value* value) {
return value;
}
});
}
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 {};
}
}

View File

@ -9,38 +9,10 @@
namespace types {
enum class BinOp {
Assignment,
Add,
Sub,
Mul,
Div,
Modulo,
BitAnd,
BitOr,
BitXor,
BitShiftLeft,
BitShiftRight,
And,
Or,
LessThan,
LessThanEquals,
GreaterThan,
GreaterThanEquals,
Equals,
NotEquals,
};
enum class Unary {
AddPostfix,
AddPrefix,
SubPostfix,
SubPrefix,
Not,
Negation,
Plus,
};
int operator_precedence(BinOp& op);
@ -51,7 +23,6 @@ namespace types {
BinOp op;
std::shared_ptr<Type> rhs;
std::shared_ptr<Type> result;
bool lhs_lvalue;
llvm::Value* (*codegen)(codegen::Builder& builder, llvm::Value* lhs, llvm::Value* rhs);
};
@ -63,20 +34,6 @@ namespace types {
BinOp op,
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

View File

@ -1,8 +1,6 @@
#ifndef BUILDER_H
#define BUILDER_H
#include <map>
#include <llvm/IR/LLVMContext.h>
#include <llvm/IR/IRBuilder.h>
@ -13,8 +11,6 @@ namespace codegen {
std::unique_ptr<llvm::IRBuilder<>> builder;
llvm::BasicBlock* block;
};
typedef std::map<std::string, llvm::Type*> TypeMap;
}
#endif

View File

@ -1,106 +0,0 @@
#include "casting.h"
#include <iostream>
namespace types {
std::vector<CastDefinition> create_casts() {
std::vector<CastDefinition> casts{};
auto int_ty = std::shared_ptr<Type>{
new FundamentalType{ false, FundamentalTypeKind::Int } };
auto uint_ty = std::shared_ptr<Type>{
new FundamentalType{ false, FundamentalTypeKind::UInt } };
auto char_ty = std::shared_ptr<Type>{
new FundamentalType{ false, FundamentalTypeKind::Char } };
auto uchar_ty = std::shared_ptr<Type>{
new FundamentalType{ false, FundamentalTypeKind::UChar } };
auto short_int_ty = std::shared_ptr<Type>{
new FundamentalType{ false, FundamentalTypeKind::ShortInt } };
auto ushort_int_ty = std::shared_ptr<Type>{
new FundamentalType{ false, FundamentalTypeKind::UShortInt } };
auto long_int_ty = std::shared_ptr<Type>{
new FundamentalType{ false, FundamentalTypeKind::LongInt } };
auto ulong_int_ty = std::shared_ptr<Type>{
new FundamentalType{ false, FundamentalTypeKind::ULongInt } };
auto long_long_int_ty = std::shared_ptr<Type>{
new FundamentalType{ false, FundamentalTypeKind::LongLongInt } };
auto ulong_long_int_ty = std::shared_ptr<Type>{
new FundamentalType{ false, FundamentalTypeKind::ULongLongInt } };
auto bool_ty = std::shared_ptr<Type>{
new FundamentalType{ false, FundamentalTypeKind::Bool } };
auto any_int_ty = std::shared_ptr<Type>{
new FundamentalType{ false, FundamentalTypeKind::AnyInt } };
auto numerical_types = {
short_int_ty, int_ty, long_int_ty, long_long_int_ty, char_ty,
ushort_int_ty, uint_ty, ulong_int_ty, ulong_long_int_ty, uchar_ty,
bool_ty
};
for (auto& target_ty : numerical_types) {
casts.push_back(CastDefinition{ any_int_ty, target_ty, true,
[](codegen::Builder&, std::shared_ptr<Type>, llvm::Value* value) {
return value;
} });
}
for (auto& source_ty : numerical_types) {
for (auto& target_ty : numerical_types) {
if (types::types_equal(source_ty, target_ty)) {
casts.push_back(CastDefinition{ source_ty, target_ty, true,
[](codegen::Builder&, std::shared_ptr<Type>, llvm::Value* value) {
return value;
} });
continue;
}
bool allow_implicit = false;
if (target_ty->size() >= source_ty->size())
allow_implicit = true;
if (target_ty->is_signed()) {
casts.push_back(CastDefinition{ source_ty, target_ty, allow_implicit,
[](codegen::Builder& builder, std::shared_ptr<Type> target, llvm::Value* value) {
codegen::TypeMap empty {};
return builder.builder->CreateSExtOrTrunc(value, target->codegen(builder, empty), "cast");
} });
}
else {
casts.push_back(CastDefinition{ source_ty, target_ty, allow_implicit,
[](codegen::Builder& builder, std::shared_ptr<Type> target, llvm::Value* value) {
codegen::TypeMap empty {};
return builder.builder->CreateZExtOrTrunc(value, target->codegen(builder, empty), "cast");
} });
}
}
}
return casts;
}
std::optional<CastDefinition> find_cast(
std::vector<CastDefinition>& casts,
std::shared_ptr<Type> casted_ty,
std::shared_ptr<Type> target_ty) {
if (casted_ty->m_kind == types::TypeKind::Array && target_ty->m_kind == types::TypeKind::Pointer) {
auto array_ty = dynamic_cast<types::ArrayType*>(casted_ty.get());
auto ptr_ty = dynamic_cast<types::PointerType*>(target_ty.get());
if (!types_equal(array_ty->m_inner, ptr_ty->m_inner))
return {};
return CastDefinition{ casted_ty, target_ty, true,
[](codegen::Builder&, std::shared_ptr<Type>, llvm::Value* value) {
return value;
} };
}
for (auto& cast : casts) {
if (types_equal(cast.casted_ty, casted_ty) && types_equal(cast.target_ty, target_ty)) {
return cast;
}
}
return {};
}
}

View File

@ -1,23 +0,0 @@
#ifndef CASTING_H
#define CASTING_H
#include "types.h"
namespace types {
struct CastDefinition {
std::shared_ptr<Type> casted_ty;
std::shared_ptr<Type> target_ty;
bool allow_implicit;
llvm::Value* (*codegen)(codegen::Builder& builder, std::shared_ptr<types::Type> target, llvm::Value* value);
};
std::vector<CastDefinition> create_casts();
std::optional<CastDefinition> find_cast(
std::vector<CastDefinition>& casts,
std::shared_ptr<Type> casted_ty,
std::shared_ptr<Type> target_ty);
}
#endif

View File

@ -11,90 +11,42 @@
namespace codegen {
Scope Scope::with_lvalue() {
return Scope{
.binops = this->binops,
.unops = this->unops,
.casts = this->casts,
.structs = this->structs,
.values = this->values,
.is_lvalue = true,
.continue_bb = this->continue_bb,
.break_bb = this->break_bb,
};
return Scope{ this->binops, this->values, true };
}
}
namespace AST {
std::shared_ptr<types::Type> IntLiteralExpression::get_codegen_type(codegen::Scope&) {
return this->m_ty;
}
codegen::StackValue IntLiteralExpression::codegen(codegen::Builder& builder, codegen::Scope&) {
auto ty = builder.builder->getInt32Ty();
codegen::StackValue IntLiteralExpression::codegen(codegen::Builder& builder, codegen::Scope& scope, codegen::StackAllocator&) {
auto ty = this->m_ty->codegen(builder, scope.structs);
auto stack_type = new types::FundamentalType{ types::FundamentalTypeKind::Int };
return codegen::StackValue{
llvm::ConstantInt::get(ty, this->m_value),
this->m_ty,
std::unique_ptr<types::Type>{stack_type}
};
}
std::shared_ptr<types::Type> StringLiteralExpression::get_codegen_type(codegen::Scope&) {
auto stack_type = new types::ArrayType{
std::make_shared<types::FundamentalType>(true, types::FundamentalTypeKind::Char),
static_cast<uint32_t>(this->m_value.size()) + 1,
true
};
return std::shared_ptr<types::Type> {stack_type};
}
codegen::StackValue StringLiteralExpression::codegen(codegen::Builder& builder, codegen::Scope&) {
codegen::StackValue StringLiteralExpression::codegen(codegen::Builder& builder, codegen::Scope& scope, codegen::StackAllocator&) {
auto stack_type = new types::ArrayType{
std::make_shared<types::FundamentalType>(true, types::FundamentalTypeKind::Char),
static_cast<uint32_t>(this->m_value.size()) + 1,
true
};
auto stack_type = new types::PointerType{ std::make_unique<types::FundamentalType>(types::FundamentalTypeKind::Char) };
auto str = llvm::StringRef{ this->m_value.c_str() };
auto global_str = builder.builder->CreateGlobalString(str);
if (scope.is_lvalue) {
return codegen::StackValue{
global_str,
std::unique_ptr<types::Type>{
new types::PointerType { false, std::shared_ptr<types::Type> {stack_type } }
},
};
}
else {
return codegen::StackValue{
builder.builder->CreateLoad(stack_type->codegen(builder, scope.structs), global_str, "literal"),
std::unique_ptr<types::Type>{stack_type},
};
}
return codegen::StackValue{
builder.builder->CreateGlobalString(str),
std::unique_ptr<types::Type>{stack_type},
};
}
std::shared_ptr<types::Type> ValueReferenceExpression::get_codegen_type(codegen::Scope& scope) {
auto value = scope.values.find(this->m_name);
if (value != scope.values.end()) {
if (value->second.ty->m_kind == types::TypeKind::Pointer) {
auto ptr_ty = dynamic_cast<types::PointerType*>(value->second.ty.get());
return ptr_ty->m_inner;
}
return value->second.ty;
}
else {
throw CompileError("Value " + this->m_name + " not found", this->m_meta);
}
}
codegen::StackValue ValueReferenceExpression::codegen(codegen::Builder& builder, codegen::Scope& scope, codegen::StackAllocator&) {
codegen::StackValue ValueReferenceExpression::codegen(codegen::Builder& builder, codegen::Scope& scope) {
auto value = scope.values.find(this->m_name);
if (value != scope.values.end()) {
if (scope.is_lvalue) {
return value->second;
}
else {
auto loaded = value->second.ty->load(builder, value->second.value, scope.structs);
auto loaded = value->second.ty->load(builder, value->second.value);
return codegen::StackValue{
loaded.first,
loaded.second
@ -106,21 +58,26 @@ namespace AST {
}
}
std::shared_ptr<types::Type> BinaryOperationExpression::get_codegen_type(codegen::Scope& scope) {
auto lhs = this->m_lhs->get_codegen_type(scope);
auto rhs = this->m_rhs->get_codegen_type(scope);
codegen::StackValue BinaryOperationExpression::codegen(codegen::Builder& builder, codegen::Scope& scope) {
auto lvalued = scope.with_lvalue();
auto lhs = this->m_lhs->codegen(builder, this->m_binop == types::BinOp::Assignment ? lvalued : scope);
auto rhs = this->m_rhs->codegen(builder, scope);
try {
switch (this->m_binop) {
case types::BinOp::Assignment:
builder.builder->CreateStore(rhs.value, lhs.value, false);
return rhs;
default:
auto binop = types::find_binop(
scope.binops,
lhs,
lhs.ty,
this->m_binop,
rhs);
rhs.ty);
if (binop) {
return binop->result;
return codegen::StackValue{
binop->codegen(builder, lhs.value, rhs.value),
binop->result
};
}
throw CompileError("invalid binop", this->m_meta);
}
@ -130,407 +87,61 @@ namespace AST {
}
}
codegen::StackValue BinaryOperationExpression::codegen(codegen::Builder& builder, codegen::Scope& scope, codegen::StackAllocator& allocator) {
auto lvalued = scope.with_lvalue();
auto binop = types::find_binop(
scope.binops,
this->m_lhs->get_codegen_type(scope),
this->m_binop,
this->m_rhs->get_codegen_type(scope));
auto lhs = this->m_lhs->codegen(builder, binop->lhs_lvalue ? lvalued : scope, allocator);
auto rhs = this->m_rhs->codegen(builder, scope, allocator);
if (binop) {
return codegen::StackValue{
binop->codegen(builder, lhs.value, rhs.value),
binop->result
};
}
else {
throw CompileError("invalid binop", this->m_meta);
}
}
std::shared_ptr<types::Type> FunctionCallExpression::get_codegen_type(codegen::Scope& scope) {
auto fn_ty = this->m_fn_expr->get_codegen_type(scope);
return *fn_ty->return_type();
}
codegen::StackValue FunctionCallExpression::codegen(codegen::Builder& builder, codegen::Scope& scope, codegen::StackAllocator& allocator) {
codegen::StackValue FunctionCallExpression::codegen(codegen::Builder& builder, codegen::Scope& scope) {
std::vector<llvm::Value*> args{};
for (auto& arg : this->m_args) {
args.push_back(arg->codegen(builder, scope, allocator).value);
args.push_back(arg->codegen(builder, scope).value);
}
auto function = this->m_fn_expr->codegen(builder, scope, allocator);
auto function = this->m_fn_expr->codegen(builder, scope);
auto value = builder.builder->CreateCall(llvm::dyn_cast<llvm::FunctionType>(function.ty->codegen(builder, scope.structs)), function.value, args, "call");
auto value = builder.builder->CreateCall(llvm::dyn_cast<llvm::FunctionType>(function.ty->codegen(builder)), function.value, args, "call");
return codegen::StackValue{
value,
*function.ty->return_type(),
};
}
std::shared_ptr<types::Type> CastExpression::get_codegen_type(codegen::Scope&) {
return this->m_ty;
}
codegen::StackValue CastExpression::codegen(codegen::Builder& builder, codegen::Scope& scope, codegen::StackAllocator& allocator) {
auto expr_ty = this->m_expr->get_codegen_type(scope);
if (expr_ty->m_kind == types::TypeKind::Array
&& this->m_ty->m_kind == types::TypeKind::Pointer) {
auto lvalued = scope.with_lvalue();
auto expr = this->m_expr->codegen(builder, lvalued, allocator);
auto cast = types::find_cast(scope.casts, expr.ty, this->m_ty);
if (cast) {
return codegen::StackValue{
cast->codegen(builder, cast->target_ty, expr.value),
cast->target_ty
};
}
else {
return expr;
}
}
else {
auto expr = this->m_expr->codegen(builder, scope, allocator);
auto cast = types::find_cast(scope.casts, expr.ty, this->m_ty);
if (cast) {
return codegen::StackValue{
cast->codegen(builder, cast->target_ty, expr.value),
cast->target_ty
};
}
return expr;
}
}
std::shared_ptr<types::Type> RefExpression::get_codegen_type(codegen::Scope& scope) {
return std::shared_ptr<types::Type> {
new types::PointerType{ false, this->m_expr->get_codegen_type(scope) }
};
}
codegen::StackValue RefExpression::codegen(codegen::Builder& builder, codegen::Scope& scope, codegen::StackAllocator& allocator) {
auto with_lvalue = scope.with_lvalue();
return this->m_expr->codegen(builder, with_lvalue, allocator);
}
std::shared_ptr<types::Type> DerefExpression::get_codegen_type(codegen::Scope& scope) {
auto ty = this->m_expr->get_codegen_type(scope);
if (ty->m_kind == types::TypeKind::Pointer) {
auto ptr_ty = dynamic_cast<types::PointerType*>(ty.get());
return ptr_ty->m_inner;
}
else {
throw new CompileError("Tried to deref a non-pointer!", this->m_meta);
}
}
codegen::StackValue DerefExpression::codegen(codegen::Builder& builder, codegen::Scope& scope, codegen::StackAllocator& allocator) {
auto value = this->m_expr->codegen(builder, scope, allocator);
if (value.ty->m_kind == types::TypeKind::Pointer) {
auto loaded = value.ty->load(builder, value.value, scope.structs);
return codegen::StackValue{
loaded.first,
loaded.second
};
}
else {
throw new CompileError("Tried to deref a non-pointer!", this->m_meta);
}
}
std::shared_ptr<types::Type> IndexAccessExpression::get_codegen_type(codegen::Scope& scope) {
auto ty = this->m_expr->get_codegen_type(scope);
if (ty->m_kind == types::TypeKind::Array) {
auto ptr_ty = dynamic_cast<types::ArrayType*>(ty.get());
return ptr_ty->m_inner;
}
else if (ty->m_kind == types::TypeKind::Pointer) {
auto ptr_ty = dynamic_cast<types::PointerType*>(ty.get());
return ptr_ty->m_inner;
}
else {
throw CompileError("Tried indexing a non-pointer", this->m_meta);
}
}
codegen::StackValue IndexAccessExpression::codegen(codegen::Builder& builder, codegen::Scope& scope, codegen::StackAllocator& allocator) {
auto intended_ty = this->m_expr->get_codegen_type(scope);
codegen::StackValue value;
if (intended_ty->m_kind != types::TypeKind::Pointer) {
auto lvalued = scope.with_lvalue();
value = this->m_expr->codegen(builder, lvalued, allocator);
}
else {
value = this->m_expr->codegen(builder, scope, allocator);
}
std::vector<llvm::Value*> idx_list{ };
idx_list.push_back(llvm::ConstantInt::get(builder.builder->getInt32Ty(), this->m_num));
if (value.ty->m_kind == types::TypeKind::Pointer) {
auto ptr_ty = dynamic_cast<types::PointerType*>(value.ty.get());
auto gep_value = builder.builder->CreateGEP(ptr_ty->m_inner->codegen(builder, scope.structs), value.value, idx_list, "ptr_gep");
if (scope.is_lvalue) {
return codegen::StackValue{
gep_value,
value.ty,
};
}
else {
auto loaded = ptr_ty->load(builder, gep_value, scope.structs);
return codegen::StackValue{
loaded.first,
loaded.second
};
}
}
else if (value.ty->m_kind == types::TypeKind::Array) {
auto array_ty = dynamic_cast<types::ArrayType*>(value.ty.get());
// if (array_ty->m_raw) {
// throw CompileError("Tried indexing a raw array", this->m_meta);
// }
auto gep_value = builder.builder->CreateGEP(array_ty->m_inner->codegen(builder, scope.structs), value.value, idx_list, "array_gep");
if (scope.is_lvalue) {
return codegen::StackValue{
gep_value,
array_ty->m_inner,
};
}
else {
auto ptr_ty = std::shared_ptr<types::Type>{
new types::PointerType { array_ty->m_inner->m_const, array_ty->m_inner}
};
auto loaded = ptr_ty->load(builder, gep_value, scope.structs);
return codegen::StackValue{
loaded.first,
loaded.second
};
}
}
throw CompileError("Tried indexing a non-pointer", this->m_meta);
}
std::shared_ptr<types::Type> FieldAccessExpression::get_codegen_type(codegen::Scope& scope) {
auto ty = this->m_expr->get_codegen_type(scope);
if (ty->m_kind == types::TypeKind::Struct) {
auto struct_ty = dynamic_cast<types::StructType*>(ty.get());
if (struct_ty->m_fields) {
for (auto& field : *struct_ty->m_fields) {
if (field.first == this->m_field)
return field.second;
}
throw CompileError("Unknown field", this->m_meta);
}
throw CompileError("Cannot access a field of opaque struct", this->m_meta);
}
else {
throw CompileError("Tried accessing a non-struct", this->m_meta);
}
}
codegen::StackValue FieldAccessExpression::codegen(codegen::Builder& builder, codegen::Scope& scope, codegen::StackAllocator& allocator) {
auto lvalued = scope.with_lvalue();
auto struct_ptr = this->m_expr->codegen(builder, lvalued, allocator);
if (struct_ptr.ty->m_kind == types::TypeKind::Pointer) {
auto ptr_ty = dynamic_cast<types::PointerType*>(struct_ptr.ty.get());
if (ptr_ty->m_inner->m_kind == types::TypeKind::Struct) {
auto struct_ty = dynamic_cast<types::StructType*>(ptr_ty->m_inner.get());
int idx = -1;
auto field_ty = std::shared_ptr<types::Type>{};
for (int i = 0; i < static_cast<int>(struct_ty->m_fields->size()); i++) {
auto field = (*struct_ty->m_fields)[i];
if (field.first == this->m_field) {
idx = i;
field_ty = field.second;
break;
}
}
auto gep = builder.builder->CreateStructGEP(
ptr_ty->m_inner->codegen(builder, scope.structs), struct_ptr.value, idx, "struct_gep");
if (scope.is_lvalue) {
return codegen::StackValue{
gep,
field_ty
};
}
else {
auto ptr_ty = std::shared_ptr<types::Type>{
new types::PointerType { field_ty->m_const, field_ty }
};
auto loaded = ptr_ty->load(builder, gep, scope.structs);
return codegen::StackValue{ loaded.first, loaded.second };
}
}
else {
throw CompileError("Tried field-accessing a non-struct-pointer", this->m_meta);
}
}
else {
throw CompileError("Tried field-accessing a non-pointer", this->m_meta);
}
}
std::shared_ptr<types::Type> ListInitializerExpression::get_codegen_type(codegen::Scope&) {
return this->m_ty;
}
codegen::StackValue ListInitializerExpression::codegen(codegen::Builder& builder, codegen::Scope& scope, codegen::StackAllocator& allocator) {
auto value_ptr = allocator.pop_alloca(this->m_ty);
if (this->m_ty->m_kind == types::TypeKind::Array) {
auto array_ty = dynamic_cast<types::ArrayType*>(this->m_ty.get());
int counter = 0;
for (auto& expr : this->m_expressions) {
std::vector<llvm::Value*> indices{};
indices.push_back(llvm::ConstantInt::get(builder.builder->getInt32Ty(), counter++));
auto gep = builder.builder->CreateGEP(
array_ty->m_inner->codegen(builder, scope.structs), value_ptr, indices, "GEP");
builder.builder->CreateStore(expr->codegen(builder, scope, allocator).value, gep);
}
auto ptr_ty = std::shared_ptr<types::Type>{
new types::PointerType{this->m_ty->m_const, this->m_ty}
};
if (scope.is_lvalue) {
return codegen::StackValue{ value_ptr, ptr_ty };
}
else {
auto loaded = ptr_ty->load(builder, value_ptr, scope.structs);
return codegen::StackValue{ loaded.first, loaded.second };
}
}
else if (this->m_ty->m_kind == types::TypeKind::Struct) {
auto struct_ty = dynamic_cast<types::StructType*>(this->m_ty.get());
for (int i = 0; i < static_cast<int>(this->m_expressions.size()); i++) {
std::vector<llvm::Value*> indices{};
auto gep = builder.builder->CreateStructGEP(
struct_ty->codegen(builder, scope.structs), value_ptr, i, "struct_gep"
);
builder.builder->CreateStore(
this->m_expressions[i]->codegen(builder, scope, allocator).value, gep);
}
auto ptr_ty = std::shared_ptr<types::Type>{
new types::PointerType{this->m_ty->m_const, this->m_ty}
};
if (scope.is_lvalue) {
return codegen::StackValue{ value_ptr, ptr_ty };
}
else {
auto loaded = ptr_ty->load(builder, value_ptr, scope.structs);
return codegen::StackValue{ loaded.first, loaded.second };
}
}
else {
throw CompileError("Tried to list-initialize a non-array!", this->m_meta);
}
}
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);
if (this->m_unary == types::Unary::AddPostfix || this->m_unary == types::Unary::AddPrefix
|| this->m_unary == types::Unary::SubPostfix || this->m_unary == types::Unary::SubPrefix) {
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
};
}
else {
auto expr_ptr = this->m_expr->codegen(builder, scope, 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) {
if (!builder.block)
return;
builder.builder->SetInsertPoint(builder.block);
auto value = this->m_expr->codegen(builder, scope, allocator);
auto value = this->m_expr->codegen(builder, scope);
builder.builder->CreateRet(value.value);
}
void ExpressionStatement::codegen(codegen::Builder& builder, codegen::Scope& scope, codegen::StackAllocator& allocator) {
void ExpressionStatement::codegen(codegen::Builder& builder, codegen::Scope& scope) {
if (!builder.block)
return;
builder.builder->SetInsertPoint(builder.block);
this->m_expr->codegen(builder, scope, allocator);
this->m_expr->codegen(builder, scope);
}
void InitializationStatement::codegen(codegen::Builder& builder, codegen::Scope& scope, codegen::StackAllocator& allocator) {
void InitializationStatement::codegen(codegen::Builder& builder, codegen::Scope& scope) {
if (!builder.block)
return;
builder.builder->SetInsertPoint(builder.block);
if (this->m_type->m_kind == types::TypeKind::Array) {
auto ptr = allocator.pop_alloca(this->m_type);
if (this->m_expr.has_value()) {
auto value = this->m_expr->get()->codegen(builder, scope, allocator);
builder.builder->CreateStore(value.value, ptr, false);
}
scope.values[this->m_name] = codegen::StackValue{ ptr, this->m_type };
return;
}
auto ptr = allocator.pop_alloca(this->m_type);
auto ty = this->m_type->codegen(builder);
auto ptr = builder.builder->CreateAlloca(ty);
if (this->m_expr.has_value()) {
auto value = this->m_expr->get()->codegen(builder, scope, allocator);
auto value = this->m_expr->get()->codegen(builder, scope);
builder.builder->CreateStore(value.value, ptr, false);
}
auto ptr_ty = std::shared_ptr<types::Type>{
new types::PointerType{ this->m_type->m_const, this->m_type }
};
scope.values[this->m_name] = codegen::StackValue{ ptr, ptr_ty };
scope.values[this->m_name] = codegen::StackValue{ ptr, this->m_type };
}
void IfStatement::codegen(codegen::Builder& builder, codegen::Scope& scope, codegen::StackAllocator& allocator) {
void IfStatement::codegen(codegen::Builder& builder, codegen::Scope& scope) {
if (!builder.block)
return;
builder.builder->SetInsertPoint(builder.block);
auto condition = this->m_condition->codegen(builder, scope, allocator);
auto condition = this->m_condition->codegen(builder, scope);
auto function = builder.block->getParent();
auto then_block = llvm::BasicBlock::Create(*builder.context, "then", function);
@ -545,141 +156,20 @@ namespace AST {
builder.block = then_block;
builder.builder->SetInsertPoint(then_block);
this->m_then->codegen(builder, scope, allocator);
if (builder.block->getTerminator() == nullptr)
builder.builder->CreateBr(after_block);
this->m_then->codegen(builder, scope);
builder.builder->CreateBr(after_block);
if (else_block.has_value()) {
builder.block = *else_block;
builder.builder->SetInsertPoint(*else_block);
this->m_else->get()->codegen(builder, scope, allocator);
if (builder.block->getTerminator() == nullptr)
builder.builder->CreateBr(after_block);
this->m_else->get()->codegen(builder, scope);
builder.builder->CreateBr(after_block);
}
builder.block = after_block;
builder.builder->SetInsertPoint(after_block);
}
void ForStatement::codegen(codegen::Builder& builder, codegen::Scope& scope, codegen::StackAllocator& allocator) {
if (!builder.block)
return;
builder.builder->SetInsertPoint(builder.block);
if (this->m_init)
(*this->m_init)->codegen(builder, scope, allocator);
auto function = builder.block->getParent();
auto cond_bb = llvm::BasicBlock::Create(*builder.context, "for-cond", function);
auto inner_bb = llvm::BasicBlock::Create(*builder.context, "for-inner", function);
auto after_bb = llvm::BasicBlock::Create(*builder.context, "for-after", function);
auto done_bb = llvm::BasicBlock::Create(*builder.context, "for-done", function);
builder.builder->CreateBr(cond_bb);
codegen::Scope inner_scope{ scope };
inner_scope.continue_bb = after_bb;
inner_scope.break_bb = done_bb;
// Loop conditional
builder.builder->SetInsertPoint(cond_bb);
if (this->m_cond) {
auto cond_res = (*this->m_cond)->codegen(builder, inner_scope, allocator);
builder.builder->CreateCondBr(cond_res.value, inner_bb, done_bb);
}
else {
builder.builder->CreateBr(inner_bb);
}
// Loop inner block
builder.block = inner_bb;
builder.builder->SetInsertPoint(inner_bb);
this->m_loop->codegen(builder, inner_scope, allocator);
if (builder.block->getTerminator() == nullptr)
builder.builder->CreateBr(after_bb);
// Loop after-part
builder.block = after_bb;
builder.builder->SetInsertPoint(after_bb);
if (this->m_after)
(*this->m_after)->codegen(builder, inner_scope, allocator);
builder.builder->CreateBr(cond_bb);
// Loop after
builder.block = done_bb;
builder.builder->SetInsertPoint(done_bb);
}
void WhileStatement::codegen(codegen::Builder& builder, codegen::Scope& scope, codegen::StackAllocator& allocator) {
if (!builder.block)
return;
builder.builder->SetInsertPoint(builder.block);
auto function = builder.block->getParent();
auto cond_bb = llvm::BasicBlock::Create(*builder.context, "while-cond", function);
auto inner_bb = llvm::BasicBlock::Create(*builder.context, "while-inner", function);
auto after_bb = llvm::BasicBlock::Create(*builder.context, "while-after", function);
builder.builder->CreateBr(cond_bb);
codegen::Scope inner_scope{ scope };
inner_scope.continue_bb = cond_bb;
inner_scope.break_bb = after_bb;
// While condition
builder.builder->SetInsertPoint(cond_bb);
if (this->m_cond) {
auto cond_value = (*this->m_cond)->codegen(builder, inner_scope, allocator);
builder.builder->CreateCondBr(cond_value.value, inner_bb, after_bb);
}
else {
builder.builder->CreateBr(inner_bb);
}
// While loop
builder.builder->SetInsertPoint(inner_bb);
builder.block = inner_bb;
this->m_loop->codegen(builder, inner_scope, allocator);
if (builder.block->getTerminator() == nullptr)
builder.builder->CreateBr(cond_bb);
// After
builder.builder->SetInsertPoint(after_bb);
builder.block = after_bb;
}
void CompoundStatement::codegen(codegen::Builder& builder, codegen::Scope& scope, codegen::StackAllocator& allocator) {
if (!builder.block)
return;
builder.builder->SetInsertPoint(builder.block);
codegen::Scope inner{ scope };
for (auto& statement : this->m_statements)
statement->codegen(builder, inner, allocator);
}
void BreakStatement::codegen(codegen::Builder& builder, codegen::Scope& scope, codegen::StackAllocator&) {
if (!builder.block)
return;
builder.builder->SetInsertPoint(builder.block);
if (scope.break_bb == nullptr)
throw CompileError("No break_bb!", this->m_meta);
builder.builder->CreateBr(scope.break_bb);
}
void ContinueStatement::codegen(codegen::Builder& builder, codegen::Scope& scope, codegen::StackAllocator&) {
if (!builder.block)
return;
builder.builder->SetInsertPoint(builder.block);
if (scope.continue_bb == nullptr)
throw CompileError("No continue_bb!", this->m_meta);
builder.builder->CreateBr(scope.continue_bb);
}
void Function::codegen(codegen::Builder& builder, codegen::Scope& scope) {
std::shared_ptr<types::Type> ret_ty_ptr{ this->m_return_ty };
@ -689,11 +179,9 @@ namespace AST {
param_ty_ptrs.push_back(param.second);
}
auto fn_ty_ptr = std::shared_ptr<types::Type>{
new types::FunctionType{ true, ret_ty_ptr, param_ty_ptrs, this->m_is_vararg }
};
auto fn_ty_ptr = std::shared_ptr<types::Type>{ new types::FunctionType{ ret_ty_ptr, param_ty_ptrs, this->m_is_vararg } };
auto fn_ty = fn_ty_ptr->codegen(builder, scope.structs);
auto fn_ty = fn_ty_ptr->codegen(builder);
auto function = llvm::Function::Create(
llvm::dyn_cast<llvm::FunctionType>(fn_ty),
llvm::GlobalValue::LinkageTypes::ExternalLinkage,
@ -711,88 +199,37 @@ namespace AST {
int counter = 0;
for (auto& param : this->m_params) {
if (param.second->m_kind == types::TypeKind::Array) {
auto array_ty = dynamic_cast<types::ArrayType*>(param.second.get());
if (!array_ty->m_raw) {
auto ty = param_ty_ptrs[counter];
auto arg = function->getArg(counter++);
if (param.first) {
arg->setName(*param.first);
inner_scope.values[*param.first] = codegen::StackValue{
arg,
ty,
};
}
continue;
}
}
builder.builder->SetInsertPoint(BB);
auto arg_ptr = builder.builder->CreateAlloca(param_ty_ptrs[counter]->codegen(builder, scope.structs));
auto param_ty_ptr = std::shared_ptr<types::Type>{
new types::PointerType { true, param_ty_ptrs[counter]}
};
auto param_ty_ptr = param_ty_ptrs[counter];
auto arg = function->getArg(counter++);
builder.builder->CreateStore(arg, arg_ptr);
if (param.first) {
arg->setName(*param.first);
inner_scope.values[*param.first] = codegen::StackValue{
arg_ptr,
arg,
param_ty_ptr,
};
}
}
codegen::StackAllocator allocator{ builder, scope.structs };
builder.builder->SetInsertPoint(BB);
for (auto& statement : *this->m_statements) {
statement->codegen_alloca(allocator);
}
for (auto& statement : *this->m_statements) {
statement->codegen(builder, inner_scope, allocator);
statement->codegen(builder, inner_scope);
}
}
builder.builder->CreateRetVoid();
llvm::verifyFunction(*function);
builder.block = nullptr;
}
void TopLevelTypedef::codegen(codegen::Builder& builder, codegen::Scope& scope) {
auto ty = this->m_ty->codegen(builder, scope.structs);
if (this->m_ty->m_kind == types::TypeKind::Struct) {
auto struct_ty = dynamic_cast<types::StructType*>(this->m_ty.get());
if (struct_ty->m_name) {
scope.structs[*struct_ty->m_name] = ty;
}
}
}
}
namespace types {
llvm::Type* FundamentalType::codegen(codegen::Builder& builder, codegen::TypeMap&) {
llvm::Type* FundamentalType::codegen(codegen::Builder& builder) {
switch (this->m_ty) {
case FundamentalTypeKind::ShortInt:
case FundamentalTypeKind::UShortInt:
return builder.builder->getInt16Ty();
case FundamentalTypeKind::Int:
case FundamentalTypeKind::UInt:
return builder.builder->getInt32Ty();
case FundamentalTypeKind::LongInt:
case FundamentalTypeKind::ULongInt:
return builder.builder->getInt64Ty();
case FundamentalTypeKind::LongLongInt:
case FundamentalTypeKind::ULongLongInt:
return builder.builder->getInt128Ty();
case FundamentalTypeKind::Bool:
return builder.builder->getInt1Ty();
case FundamentalTypeKind::Char:
return builder.builder->getInt8Ty();
case FundamentalTypeKind::UChar:
return builder.builder->getInt8Ty();
case FundamentalTypeKind::Void:
return builder.builder->getVoidTy();
default:
@ -800,57 +237,19 @@ namespace types {
}
}
llvm::Type* FunctionType::codegen(codegen::Builder& builder, codegen::TypeMap& structs) {
llvm::Type* FunctionType::codegen(codegen::Builder& builder) {
std::vector<llvm::Type*> params{};
for (auto& param : this->m_param_tys) {
params.push_back(param->codegen(builder, structs));
params.push_back(param->codegen(builder));
}
auto ret_ty = this->m_ret_ty->codegen(builder, structs);
auto ret_ty = this->m_ret_ty->codegen(builder);
return llvm::FunctionType::get(ret_ty, params, this->m_vararg);
}
llvm::Type* PointerType::codegen(codegen::Builder& builder, codegen::TypeMap&) {
llvm::Type* PointerType::codegen(codegen::Builder& builder) {
return llvm::PointerType::get(*builder.context, 0);
}
llvm::Type* ArrayType::codegen(codegen::Builder& builder, codegen::TypeMap& structs) {
if (this->m_raw)
return llvm::ArrayType::get(this->m_inner->codegen(builder, structs), this->m_size);
return llvm::PointerType::get(*builder.context, 0);
}
llvm::Type* StructType::codegen(codegen::Builder& builder, codegen::TypeMap& structs) {
if (this->m_is_ref) {
if (this->m_name) {
return structs[*this->m_name];
}
else {
throw CompileError("reference to nonexistant struct", {});
}
}
if (this->m_fields) {
std::vector<llvm::Type*> fields{};
for (auto& field : *this->m_fields) {
fields.push_back(field.second->codegen(builder, structs));
}
auto ty = llvm::StructType::create(*builder.context, fields);
if (this->m_name)
ty->setName(*this->m_name);
return ty;
}
else {
auto ty = llvm::StructType::create(*builder.context);
if (this->m_name)
ty->setName(*this->m_name);
return ty;
}
}
// llvm::Type* StructRef::codegen(codegen::Builder&, codegen::TypeMap& structs) {
// return structs[this->m_name];
// }
}

View File

@ -9,7 +9,6 @@
#include "builder.h"
#include "types.h"
#include "binops.h"
#include "casting.h"
#include "tokens.h"
namespace codegen {
@ -20,17 +19,10 @@ namespace codegen {
struct Scope {
std::vector<types::BinopDefinition>& binops;
std::vector<types::UnopDefinition> unops;
std::vector<types::CastDefinition>& casts;
TypeMap structs;
std::map<std::string, StackValue> values;
bool is_lvalue;
llvm::BasicBlock* continue_bb;
llvm::BasicBlock* break_bb;
Scope with_lvalue();
};
}

View File

@ -70,14 +70,12 @@ std::optional<CompileOutput> compile(std::string_view in_filename) {
}
// Parse tokens
parsing::Scope parse_scope{};
auto stream = token::TokenStream{ tokens };
std::vector<std::unique_ptr<AST::TopLevelStatement>> statements;
auto statement = parsing::parse_top_level_statement(stream, parse_scope);
auto statement = parsing::parse_top_level_statement(stream);
while (statement.ok()) {
statements.push_back(statement.unwrap());
statement = parsing::parse_top_level_statement(stream, parse_scope);
statement = parsing::parse_top_level_statement(stream);
}
if (stream.peek().type != token::Type::Eof) {
std::cerr << statement.unwrap_err() << std::endl;
@ -99,28 +97,15 @@ std::optional<CompileOutput> compile(std::string_view in_filename) {
// Perform static analysis
typecheck::Scope preprocess_scope{};
// Preprocess
for (auto& tls : statements) {
std::cout << tls->formatted() << std::endl;
tls->typecheck_preprocess(preprocess_scope);
}
// Actual typechecking
typecheck::State typecheck_state{};
typecheck_state.binops = types::create_binops();
typecheck_state.unops = types::create_unops();
typecheck_state.casts = types::create_casts();
typecheck::Scope typecheck_scope{};
for (auto& tls : statements) {
std::cout << tls->formatted() << std::endl;
tls->typecheck(typecheck_state, typecheck_scope);
}
// Error checking
if (typecheck_state.errors.size() > 0) {
std::cerr << "Errors while typechecking:" << std::endl;
for (auto& error : typecheck_state.errors) {
@ -131,19 +116,13 @@ std::optional<CompileOutput> compile(std::string_view in_filename) {
return {};
}
// Compile parsed output
codegen::Scope cg_scope{
.binops = typecheck_state.binops,
.unops = typecheck_state.unops,
.casts = typecheck_state.casts,
.structs = {},
.values = {},
.is_lvalue = false,
.continue_bb = nullptr,
.break_bb = nullptr,
};
// Compile parsed output
try {
for (auto& tls : statements) {
std::cout << tls->formatted() << std::endl;
@ -180,8 +159,6 @@ std::optional<CompileOutput> compile(std::string_view in_filename) {
builder.mod->print(llvm_ir_dest, nullptr);
llvm_ir_dest.flush();
std::cout << llvm_ir_string << std::endl;
// Print output to obj-file
std::error_code EC;
std::string obj_string;

View File

@ -2,206 +2,40 @@
#include "parsing.h"
#include "tokens.h"
#include <set>
namespace parsing {
namespace {
static uint32_t struct_id_counter = 0;
Result<std::unique_ptr<AST::Expression>, std::string> parse_expression(token::TokenStream& stream);
Result<std::unique_ptr<AST::Expression>, std::string> parse_expression(token::TokenStream& stream, Scope& scope);
Result<std::optional<uint32_t>, std::string> parse_array_postfix(token::TokenStream& stream, bool allow_empty, Scope&);
Result<std::shared_ptr<types::Type>, std::string> parse_type(token::TokenStream& stream, Scope& scope) {
Result<std::shared_ptr<types::Type>, std::string> parse_type(token::TokenStream& stream) {
token::TokenStream inner{ stream };
try {
bool is_const = false;
if (inner.peek().type == token::Type::Ident && inner.peek().content == "const") {
is_const = true;
inner.next();
}
auto token = inner.expect(token::Type::Ident);
// TODO eventually make this be potentially more than one word
std::string type_name = token.content;
std::shared_ptr<types::Type> returned{};
if (token.content == "struct") {
std::optional<std::string> struct_name{};
if (inner.peek().type == token::Type::Ident) {
struct_name = inner.expect(token::Type::Ident).content;
}
std::optional<std::vector<types::StructField>> maybe_fields{};
if (inner.peek().content == "{") {
std::vector<types::StructField> fields{};
inner.expect(token::Type::Symbol, "{");
int counter = 0;
while (inner.peek().content != "}") {
if (counter++ > 0)
inner.expect(token::Type::Symbol, ";");
auto ty_res = parse_type(inner, scope);
if (!ty_res.ok())
break;
auto ty = ty_res.unwrap();
auto field_name = inner.expect(token::Type::Ident);
auto array_postfix = parse_array_postfix(inner, false, scope);
while (array_postfix.ok()) {
auto postfix = array_postfix.unwrap();
ty = std::shared_ptr<types::Type>{
new types::ArrayType(ty, *postfix, true)
};
array_postfix = parse_array_postfix(inner, false, scope);
}
fields.push_back(types::StructField{ field_name.content, ty });
}
inner.expect(token::Type::Symbol, "}");
maybe_fields = fields;
}
if (!struct_name && !maybe_fields) {
throw std::runtime_error("Struct must have a name or fields!");
}
if (struct_name && !maybe_fields && scope.structs.find(*struct_name) != scope.structs.end()) {
auto original_ty = scope.structs[*struct_name];
auto original_struct_ty = dynamic_cast<types::StructType*>(original_ty.get());
auto ty = new types::StructType{
is_const,
struct_name,
original_struct_ty->m_fields,
true,
false,
original_struct_ty->m_id
};
returned = std::shared_ptr<types::Type>{ ty };
}
else {
if (scope.structs.find(*struct_name) != scope.structs.end()) {
auto original_ty = scope.structs[*struct_name];
auto original_struct_ty = dynamic_cast<types::StructType*>(original_ty.get());
if (!original_struct_ty->m_fields.has_value()) {
auto ty = new types::StructType{
is_const,
struct_name,
maybe_fields,
false,
true,
original_struct_ty->m_id
};
returned = std::shared_ptr<types::Type>{ ty };
}
else {
auto ty = new types::StructType{
is_const,
struct_name,
maybe_fields,
false,
false,
struct_id_counter++
};
returned = std::shared_ptr<types::Type>{ ty };
}
}
else {
auto ty = new types::StructType{
is_const,
struct_name,
maybe_fields,
false,
false,
struct_id_counter++
};
returned = std::shared_ptr<types::Type>{ ty };
}
}
if (type_name == "int") {
auto ty = new types::FundamentalType{ types::FundamentalTypeKind::Int };
returned = std::shared_ptr<types::Type>{ ty };
}
else if (type_name == "char") {
auto ty = new types::FundamentalType{ types::FundamentalTypeKind::Char };
returned = std::shared_ptr<types::Type>{ ty };
}
else if (type_name == "void") {
auto ty = new types::FundamentalType{ types::FundamentalTypeKind::Void };
returned = std::shared_ptr<types::Type>{ ty };
}
else {
// TODO eventually make this be potentially more than one word
std::string type_name = {};
std::set<std::string> type_parts = { "unsigned", "short", "long", "int", "char", "void" };
int counter = 0;
while (token.type == token::Type::Ident && type_parts.contains(token.content)) {
if (counter > 0) {
type_name += " ";
inner.next();
}
type_name += token.content;
counter++;
token = inner.peek();
}
std::cout << type_name << std::endl;
if (type_name == "short int") {
auto ty = new types::FundamentalType{ is_const, types::FundamentalTypeKind::ShortInt };
returned = std::shared_ptr<types::Type>{ ty };
}
else if (type_name == "unsigned short int") {
auto ty = new types::FundamentalType{ is_const, types::FundamentalTypeKind::UShortInt };
returned = std::shared_ptr<types::Type>{ ty };
}
else if (type_name == "int") {
auto ty = new types::FundamentalType{ is_const, types::FundamentalTypeKind::Int };
returned = std::shared_ptr<types::Type>{ ty };
}
else if (type_name == "unsigned int") {
auto ty = new types::FundamentalType{ is_const, types::FundamentalTypeKind::UInt };
returned = std::shared_ptr<types::Type>{ ty };
}
else if (type_name == "long int") {
auto ty = new types::FundamentalType{ is_const, types::FundamentalTypeKind::LongInt };
returned = std::shared_ptr<types::Type>{ ty };
}
else if (type_name == "unsigned long int") {
auto ty = new types::FundamentalType{ is_const, types::FundamentalTypeKind::ULongInt };
returned = std::shared_ptr<types::Type>{ ty };
}
else if (type_name == "long long int") {
auto ty = new types::FundamentalType{ is_const, types::FundamentalTypeKind::LongLongInt };
returned = std::shared_ptr<types::Type>{ ty };
}
else if (type_name == "unsigned long long int") {
auto ty = new types::FundamentalType{ is_const, types::FundamentalTypeKind::ULongLongInt };
returned = std::shared_ptr<types::Type>{ ty };
}
else if (type_name == "char") {
auto ty = new types::FundamentalType{ is_const, types::FundamentalTypeKind::Char };
returned = std::shared_ptr<types::Type>{ ty };
}
else if (type_name == "unsigned char") {
auto ty = new types::FundamentalType{ is_const, types::FundamentalTypeKind::UChar };
returned = std::shared_ptr<types::Type>{ ty };
}
else if (type_name == "void") {
auto ty = new types::FundamentalType{ is_const, types::FundamentalTypeKind::Void };
returned = std::shared_ptr<types::Type>{ ty };
}
else {
throw std::runtime_error("Expected type name, got " + type_name);
}
throw std::runtime_error("Expected type name, got " + type_name);
}
while (inner.peek().type == token::Type::Symbol && inner.peek().content == "*") {
inner.next();
auto ptr_const = false;
if (inner.peek().type == token::Type::Ident && inner.peek().content == "const") {
inner.next();
ptr_const = true;
}
auto ty = new types::PointerType{ ptr_const, std::move(returned) };
auto ty = new types::PointerType{ std::move(returned) };
returned = std::shared_ptr<types::Type>{ ty };
}
@ -215,71 +49,9 @@ namespace parsing {
}
}
Result<std::optional<uint32_t>, std::string> parse_array_postfix(token::TokenStream& stream, bool allow_empty, Scope&) {
Result<std::unique_ptr<AST::Expression>, std::string> parse_plain_expression(token::TokenStream& stream) {
token::TokenStream inner{ stream };
try {
std::optional<uint32_t> returned{};
inner.expect(token::Type::Symbol, "[");
if (inner.peek().type == token::Type::LiteralInt) {
returned = std::stoi(inner.next().content);
}
if (!allow_empty && !returned.has_value()) {
throw std::runtime_error("Expected array size");
}
inner.expect(token::Type::Symbol, "]");
stream.m_position = inner.m_position;
return returned;
}
catch (std::runtime_error& error) {
return std::string{ error.what() };
}
}
Result<std::unique_ptr<AST::Expression>, std::string> parse_list_initializer(token::TokenStream& stream, Scope& scope) {
token::TokenStream inner{ stream };
try {
auto before_meta = inner.metadata();
inner.expect(token::Type::Symbol, "{");
std::vector<std::unique_ptr<AST::Expression>> expressions{};
int counter = 0;
while (inner.peek().content != "}") {
if (counter++ > 0) {
inner.expect(token::Type::Symbol, ",");
}
expressions.push_back(parse_expression(inner, scope).unwrap());
}
inner.expect(token::Type::Symbol, "}");
stream.m_position = inner.m_position;
return std::unique_ptr<AST::Expression> {
new AST::ListInitializerExpression(
before_meta + inner.metadata(),
std::move(expressions),
std::shared_ptr<types::Type> {
new types::FundamentalType{ true, types::FundamentalTypeKind::Any }
})
};
}
catch (std::runtime_error& error) {
return std::string{ error.what() };
}
}
Result<std::unique_ptr<AST::Expression>, std::string> parse_plain_expression(token::TokenStream& stream, Scope& scope) {
token::TokenStream inner{ stream };
try {
if (auto list_init = parse_list_initializer(inner, scope); list_init.ok()) {
stream.m_position = inner.m_position;
return std::unique_ptr<AST::Expression> { list_init.unwrap() };
}
auto token = inner.next();
if (token.type == token::Type::LiteralInt) {
stream.m_position = inner.m_position;
@ -308,152 +80,27 @@ namespace parsing {
}
}
Result<std::shared_ptr<types::Type>, std::string> parse_cast(token::TokenStream& stream, Scope& scope) {
token::TokenStream inner{ stream };
try {
inner.expect(token::Type::Symbol, "(");
auto ty = parse_type(inner, scope).unwrap();
inner.expect(token::Type::Symbol, ")");
stream.m_position = inner.m_position;
return ty;
}
catch (std::runtime_error& error) {
return std::string{ error.what() };
}
}
Result<std::unique_ptr<AST::Expression>, std::string> parse_primary_expression(token::TokenStream& stream, Scope& scope) {
Result<std::unique_ptr<AST::Expression>, std::string> parse_primary_expression(token::TokenStream& stream) {
token::TokenStream inner{ stream };
try {
auto before_meta = inner.metadata();
if (auto cast = parse_cast(inner, scope); cast.ok()) {
auto expr = parse_primary_expression(inner, scope).unwrap();
stream.m_position = inner.m_position;
return std::unique_ptr<AST::Expression>{
new AST::CastExpression{
before_meta + inner.metadata(),
cast.unwrap(),
std::move(expr)
}
};
}
else if (inner.peek().content == "(") {
auto plain_expr = parse_plain_expression(inner);
while (inner.peek().content == "(") {
inner.next();
auto expr = parse_expression(inner, scope).unwrap();
std::vector<std::unique_ptr<AST::Expression>> args{};
int counter = 0;
while (inner.peek().content != ")") {
if (counter++ > 0)
inner.expect(token::Type::Symbol, ",");
args.push_back(parse_expression(inner).unwrap());
}
inner.expect(token::Type::Symbol, ")");
stream.m_position = inner.m_position;
return expr;
}
else if (inner.peek().content == "&") {
inner.next();
auto expr = parse_primary_expression(inner, scope).unwrap();
stream.m_position = inner.m_position;
return std::unique_ptr<AST::Expression> {
new AST::RefExpression(before_meta + inner.metadata(), std::move(expr))
};
}
else if (inner.peek().content == "*") {
inner.next();
auto expr = parse_primary_expression(inner, scope).unwrap();
stream.m_position = inner.m_position;
return std::unique_ptr<AST::Expression> {
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)
};
}
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::SubPrefix)
};
}
else if (inner.peek().content == "!") {
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::Not)
};
}
else if (inner.peek().content == "-") {
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::Negation)
};
}
else if (inner.peek().content == "+") {
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::Plus)
};
}
auto plain_expr = parse_plain_expression(inner, scope);
while (inner.peek().content == "(" || inner.peek().content == "[" || inner.peek().content == ".") {
if (inner.peek().content == "(") {
inner.next();
std::vector<std::unique_ptr<AST::Expression>> args{};
int counter = 0;
while (inner.peek().content != ")") {
if (counter++ > 0)
inner.expect(token::Type::Symbol, ",");
args.push_back(parse_expression(inner, scope).unwrap());
}
inner.expect(token::Type::Symbol, ")");
auto fn_call = new AST::FunctionCallExpression{ before_meta + inner.metadata(), plain_expr.unwrap(), std::move(args) };
plain_expr = std::unique_ptr<AST::Expression>{ fn_call };
}
else if (auto postfix = parse_array_postfix(inner, false, scope); postfix.ok()) {
auto idx_expr = new AST::IndexAccessExpression{
before_meta + inner.metadata(), plain_expr.unwrap(), *postfix.unwrap() };
plain_expr = std::unique_ptr<AST::Expression>{ idx_expr };
}
else if (inner.peek().content == ".") {
inner.next();
std::string field_name = inner.expect(token::Type::Ident).content;
plain_expr = std::unique_ptr<AST::Expression>{
new AST::FieldAccessExpression{before_meta + inner.metadata(), plain_expr.unwrap(), field_name}
};
}
}
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}
};
}
else 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::SubPostfix}
};
auto fn_call = new AST::FunctionCallExpression{ before_meta + inner.metadata(), plain_expr.unwrap(), std::move(args) };
plain_expr = std::unique_ptr<AST::Expression>{ fn_call };
}
@ -465,51 +112,17 @@ namespace parsing {
}
}
Result<types::BinOp, std::string> parse_binop(token::TokenStream& stream, Scope&) {
Result<types::BinOp, std::string> parse_binop(token::TokenStream& stream) {
token::TokenStream inner{ stream };
try {
auto token = inner.next();
auto peeked = inner.peek();
if (token.type != token::Type::Symbol) {
throw std::runtime_error("Expected binop");
}
else if (token.content == "<" && peeked.content == "<") {
inner.next();
stream.m_position = inner.m_position;
return types::BinOp{ types::BinOp::BitShiftLeft };
}
else if (token.content == ">" && peeked.content == ">") {
inner.next();
stream.m_position = inner.m_position;
return types::BinOp{ types::BinOp::BitShiftRight };
}
else if (token.content == "<" && peeked.content == "=") {
inner.next();
stream.m_position = inner.m_position;
return types::BinOp{ types::BinOp::LessThanEquals };
}
else if (token.content == ">" && peeked.content == "=") {
inner.next();
stream.m_position = inner.m_position;
return types::BinOp{ types::BinOp::GreaterThanEquals };
}
else if (token.content == "=" && peeked.content == "=") {
inner.next();
stream.m_position = inner.m_position;
return types::BinOp{ types::BinOp::Equals };
}
else if (token.content == "!" && peeked.content == "=") {
inner.next();
stream.m_position = inner.m_position;
return types::BinOp{ types::BinOp::NotEquals };
}
else if (token.content == "=") {
stream.m_position = inner.m_position;
return types::BinOp{ types::BinOp::Assignment };
}
else if (token.content == "+") {
stream.m_position = inner.m_position;
return types::BinOp{ types::BinOp::Add };
@ -518,32 +131,6 @@ namespace parsing {
stream.m_position = inner.m_position;
return types::BinOp{ types::BinOp::Sub };
}
else if (token.content == "*") {
stream.m_position = inner.m_position;
return types::BinOp{ types::BinOp::Mul };
}
else if (token.content == "/") {
stream.m_position = inner.m_position;
return types::BinOp{ types::BinOp::Div };
}
else if (token.content == "%") {
stream.m_position = inner.m_position;
return types::BinOp{ types::BinOp::Modulo };
}
else if (token.content == "&") {
stream.m_position = inner.m_position;
return types::BinOp{ types::BinOp::BitAnd };
}
else if (token.content == "|") {
stream.m_position = inner.m_position;
return types::BinOp{ types::BinOp::BitOr };
}
else if (token.content == "^") {
stream.m_position = inner.m_position;
return types::BinOp{ types::BinOp::BitXor };
}
else if (token.content == "<") {
stream.m_position = inner.m_position;
return types::BinOp{ types::BinOp::LessThan };
@ -561,32 +148,32 @@ namespace parsing {
}
std::unique_ptr<AST::Expression> parse_rhs(
token::TokenStream& stream, std::unique_ptr<AST::Expression> lhs, int prev_precedence, Scope& scope) {
token::TokenStream& stream, std::unique_ptr<AST::Expression> lhs, int prev_precedence) {
auto before = stream.metadata();
auto binop_res = parse_binop(stream, scope);
auto binop_res = parse_binop(stream);
while (binop_res.ok()) {
auto binop = binop_res.unwrap();
auto rhs = parse_primary_expression(stream, scope).unwrap();
auto rhs = parse_primary_expression(stream).unwrap();
if (types::operator_precedence(binop) > prev_precedence) {
rhs = parse_rhs(stream, std::move(rhs), types::operator_precedence(binop), scope);
rhs = parse_rhs(stream, std::move(rhs), types::operator_precedence(binop));
}
auto binop_expr = new AST::BinaryOperationExpression{ before + stream.metadata(), std::move(lhs), binop, std::move(rhs) };
lhs = std::unique_ptr<AST::Expression>{ binop_expr };
binop_res = parse_binop(stream, scope);
binop_res = parse_binop(stream);
}
return lhs;
}
Result<std::unique_ptr<AST::Expression>, std::string> parse_expression(token::TokenStream& stream, Scope& scope) {
Result<std::unique_ptr<AST::Expression>, std::string> parse_expression(token::TokenStream& stream) {
try {
auto lhs = parse_primary_expression(stream, scope).unwrap();
return std::unique_ptr{ parse_rhs(stream, std::move(lhs), 0, scope) };
auto lhs = parse_primary_expression(stream).unwrap();
return std::unique_ptr{ parse_rhs(stream, std::move(lhs), 0) };
}
catch (std::runtime_error& error) {
return std::string{ error.what() };
@ -595,30 +182,23 @@ namespace parsing {
Result<std::unique_ptr<AST::InitializationStatement>, std::string> parse_init_statement(token::TokenStream& stream, Scope& scope) {
Result<std::unique_ptr<AST::InitializationStatement>, std::string> parse_init_statement(token::TokenStream& stream) {
token::TokenStream inner{ stream };
auto before_meta = inner.metadata();
try {
auto ty = parse_type(inner, scope).unwrap();
auto ty = parse_type(inner).unwrap();
auto name = inner.expect(token::Type::Ident);
auto array_postfix = parse_array_postfix(inner, false, scope);
while (array_postfix.ok()) {
auto postfix = array_postfix.unwrap();
ty = std::shared_ptr<types::Type>{
new types::ArrayType(ty, *postfix, true)
};
array_postfix = parse_array_postfix(inner, false, scope);
}
std::optional<std::unique_ptr<AST::Expression>> expr = {};
if (inner.peek().type == token::Type::Symbol && inner.peek().content == "=") {
inner.expect(token::Type::Symbol, "=");
expr = parse_expression(inner, scope).unwrap();
expr = parse_expression(inner).unwrap();
}
inner.expect(token::Type::Symbol, ";");
stream.m_position = inner.m_position;
auto init = new AST::InitializationStatement{ before_meta + inner.metadata(), std::move(ty), name.content, std::move(expr) };
return std::unique_ptr<AST::InitializationStatement>{ init };
@ -628,66 +208,31 @@ namespace parsing {
}
}
Result<std::pair<std::unique_ptr<AST::Statement>, bool>, std::string> parse_statement(token::TokenStream& stream, Scope& scope) {
Result<std::unique_ptr<AST::Statement>, std::string> parse_statement(token::TokenStream& stream) {
token::TokenStream inner{ stream };
auto before_meta = inner.metadata();
try {
if (inner.peek().type == token::Type::BreakKeyword) {
inner.next();
stream.m_position = inner.m_position;
auto ret = new AST::BreakStatement{ before_meta + stream.metadata() };
return std::pair{ std::unique_ptr<AST::Statement>{ret}, true };
}
if (inner.peek().type == token::Type::ContinueKeyword) {
inner.next();
stream.m_position = inner.m_position;
auto ret = new AST::ContinueStatement{ before_meta + stream.metadata() };
return std::pair{ std::unique_ptr<AST::Statement>{ret}, true };
}
if (inner.peek().type == token::Type::Symbol && inner.peek().content == "{") {
inner.expect(token::Type::Symbol, "{");
std::vector<std::unique_ptr<AST::Statement>> statements{};
Scope inner_scope{ scope };
while (inner.peek().content != "}") {
auto res = parse_statement(inner, inner_scope).unwrap();
if (res.second)
inner.expect(token::Type::Symbol, ";");
statements.push_back(std::move(res.first));
}
inner.expect(token::Type::Symbol, "}");
stream.m_position = inner.m_position;
auto ret = new AST::CompoundStatement{ before_meta + stream.metadata(),std::move(statements) };
return std::pair{ std::unique_ptr<AST::Statement>{ret}, false };
}
if (inner.peek().type == token::Type::ReturnKeyword) {
inner.next();
auto expression = parse_expression(inner, scope).unwrap();
auto expression = parse_expression(inner).unwrap();
inner.expect(token::Type::Symbol, ";");
stream.m_position = inner.m_position;
auto ret = new AST::ReturnStatement{ before_meta + stream.metadata(),std::move(expression) };
return std::pair{ std::unique_ptr<AST::Statement>{ ret }, true };
return std::unique_ptr<AST::Statement>{ ret };
}
else if (inner.peek().type == token::Type::IfKeyword) {
inner.next();
inner.expect(token::Type::Symbol, "(");
auto expression = parse_expression(inner, scope).unwrap();
auto expression = parse_expression(inner).unwrap();
inner.expect(token::Type::Symbol, ")");
auto then_res = parse_statement(inner, scope).unwrap();
if (then_res.second)
inner.expect(token::Type::Symbol, ";");
auto then_statement = std::move(then_res.first);
auto then_statement = parse_statement(inner).unwrap();
std::optional<std::unique_ptr<AST::Statement>> else_statement{};
if (inner.peek().type == token::Type::ElseKeyword) {
inner.next();
auto else_res = parse_statement(inner, scope).unwrap();
if (else_res.second)
inner.expect(token::Type::Symbol, ";");
else_statement = std::move(else_res.first);
else_statement = parse_statement(inner).unwrap();
}
stream.m_position = inner.m_position;
@ -698,76 +243,17 @@ namespace parsing {
std::move(then_statement),
std::move(else_statement)
};
return std::pair{ std::unique_ptr<AST::Statement>{ statement }, false };
return std::unique_ptr<AST::Statement>{ statement };
}
else if (inner.peek().type == token::Type::ForKeyword) {
inner.next();
inner.expect(token::Type::Symbol, "(");
std::optional<std::unique_ptr<AST::Statement>> init;
std::optional<std::unique_ptr<AST::Expression>> cond;
std::optional<std::unique_ptr<AST::Expression>> after;
if (inner.peek().content != ";")
init = parse_statement(inner, scope).unwrap().first;
inner.expect(token::Type::Symbol, ";");
if (inner.peek().content != ";")
cond = parse_expression(inner, scope).unwrap();
inner.expect(token::Type::Symbol, ";");
if (inner.peek().content != ")")
after = parse_expression(inner, scope).unwrap();
inner.expect(token::Type::Symbol, ")");
auto loop_res = parse_statement(inner, scope).unwrap();
if (loop_res.second)
inner.expect(token::Type::Symbol, ";");
auto loop = std::move(loop_res.first);
else if (auto init = parse_init_statement(inner); init.ok()) {
stream.m_position = inner.m_position;
auto statement = new AST::ForStatement{
before_meta + stream.metadata(),
std::move(init),
std::move(cond),
std::move(after),
std::move(loop),
};
return std::pair{ std::unique_ptr<AST::Statement>{ statement }, false };
return std::unique_ptr<AST::Statement>{ init.unwrap() };
}
else if (inner.peek().type == token::Type::WhileKeyword) {
inner.next();
inner.expect(token::Type::Symbol, "(");
std::optional<std::unique_ptr<AST::Expression>> cond;
if (inner.peek().content != ")")
cond = parse_expression(inner, scope).unwrap();
inner.expect(token::Type::Symbol, ")");
auto loop_res = parse_statement(inner, scope).unwrap();
if (loop_res.second)
inner.expect(token::Type::Symbol, ";");
auto loop = std::move(loop_res.first);
stream.m_position = inner.m_position;
auto statement = new AST::WhileStatement{
before_meta + stream.metadata(),
std::move(cond),
std::move(loop),
};
return std::pair{ std::unique_ptr<AST::Statement>{ statement }, false };
}
else if (auto init = parse_init_statement(inner, scope); init.ok()) {
stream.m_position = inner.m_position;
return std::pair{ std::unique_ptr<AST::Statement>{ init.unwrap() }, true };
}
else if (auto expr = parse_expression(inner, scope); expr.ok()) {
else if (auto expr = parse_expression(inner); expr.ok()) {
stream.m_position = inner.m_position;
stream.expect(token::Type::Symbol, ";");
auto expr_statement = new AST::ExpressionStatement{ before_meta + stream.metadata(), expr.unwrap() };
return std::pair{ std::unique_ptr<AST::Statement>{ expr_statement }, true };
return std::unique_ptr<AST::Statement>{ expr_statement };
}
else {
throw std::runtime_error("Expected return-keyword");
@ -780,11 +266,11 @@ namespace parsing {
}
}
Result<std::unique_ptr<AST::TopLevelStatement>, std::string> parse_function(token::TokenStream& stream, Scope& scope) {
Result<std::unique_ptr<AST::TopLevelStatement>, std::string> parse_top_level_statement(token::TokenStream& stream) {
token::TokenStream inner{ stream };
auto before_meta = inner.metadata();
try {
auto type = parse_type(inner, scope).unwrap();
auto type = parse_type(inner).unwrap();
auto name_token = inner.expect(token::Type::Ident);
inner.expect(token::Type::Symbol, "(");
@ -803,48 +289,26 @@ namespace parsing {
break;
}
auto param_ty = parse_type(inner, scope).unwrap();
auto param_ty = parse_type(inner).unwrap();
std::optional<std::string> param_name{};
if (inner.peek().type == token::Type::Ident) {
param_name = inner.expect(token::Type::Ident).content;
auto postfix = parse_array_postfix(inner, true, scope);
while (postfix.ok()) {
auto array_postfix = postfix.unwrap();
if (array_postfix) {
param_ty = std::shared_ptr<types::Type>{
new types::ArrayType(param_ty, *array_postfix, false)
};
}
else {
param_ty = std::shared_ptr<types::Type>{
new types::PointerType(true, param_ty)
};
}
postfix = parse_array_postfix(inner, true, scope);
}
}
params.push_back(std::pair(param_name, std::move(param_ty)));
}
inner.expect(token::Type::Symbol, ")");
auto inner_scope = parsing::Scope{ scope };
std::optional<std::vector<std::unique_ptr<AST::Statement>>> statements{};
if (inner.peek().content == "{") {
inner.expect(token::Type::Symbol, "{");
std::vector<std::unique_ptr<AST::Statement>> statement_list{};
auto statement = parse_statement(inner, inner_scope);
auto statement = parse_statement(inner);
while (statement.ok()) {
auto statement_res = statement.unwrap();
if (statement_res.second)
inner.expect(token::Type::Symbol, ";");
statement_list.push_back(std::move(statement_res.first));
statement = parse_statement(inner, inner_scope);
statement_list.push_back(statement.unwrap());
statement = parse_statement(inner);
}
statements = std::optional{ std::move(statement_list) };
@ -870,56 +334,4 @@ namespace parsing {
return std::string(error.what());
}
}
Result<std::unique_ptr<AST::TopLevelStatement>, std::string> parse_tl_typedef(token::TokenStream& stream, Scope& scope) {
token::TokenStream inner{ stream };
auto before_meta = inner.metadata();
try {
auto ty = parse_type(inner, scope).unwrap();
inner.expect(token::Type::Symbol, ";");
if (ty->m_kind == types::TypeKind::Struct) {
auto struct_ty = dynamic_cast<types::StructType*>(ty.get());
if (!struct_ty->m_is_ref && struct_ty->m_name) {
if (scope.structs.find(*struct_ty->m_name) != scope.structs.end() && struct_ty->m_is_def) {
auto true_ty = dynamic_cast<types::StructType*>(scope.structs[*struct_ty->m_name].get());
true_ty->m_fields = struct_ty->m_fields;
}
else {
scope.structs[*struct_ty->m_name] = ty;
}
}
}
stream.m_position = inner.m_position;
auto tl_typedef = new AST::TopLevelTypedef{
before_meta + stream.metadata(), ty
};
return std::unique_ptr<AST::TopLevelStatement>{tl_typedef};
}
catch (std::runtime_error& error) {
return std::string(error.what());
}
}
Result<std::unique_ptr<AST::TopLevelStatement>, std::string> parse_top_level_statement(token::TokenStream& stream, Scope& scope) {
token::TokenStream inner{ stream };
auto before_meta = inner.metadata();
try {
if (auto func = parse_function(inner, scope); func.ok()) {
stream.m_position = inner.m_position;
return func.unwrap();
}
else if (auto tl_typedef = parse_tl_typedef(inner, scope); tl_typedef.ok()) {
stream.m_position = inner.m_position;
return tl_typedef.unwrap();
}
else {
throw std::runtime_error("Expected top-level statement, got " + inner.peek().formatted());
}
}
catch (std::runtime_error& error) {
return std::string(error.what());
}
}
}

View File

@ -1,19 +1,12 @@
#ifndef PARSING_H
#define PARSING_H
#include <map>
#include "ast.h"
#include "result.h"
#include "tokens.h"
namespace parsing {
struct Scope {
std::map<std::string, std::shared_ptr<types::Type>> structs;
};
Result<std::unique_ptr<AST::TopLevelStatement>, std::string> parse_top_level_statement(
token::TokenStream& stream, Scope& scope);
Result<std::unique_ptr<AST::TopLevelStatement>, std::string> parse_top_level_statement(token::TokenStream& stream);
}
#endif

View File

@ -1,121 +0,0 @@
#include "stack_allocator.h"
#include "ast.h"
namespace codegen {
void StackAllocator::push_alloca(std::shared_ptr<types::Type> ty, std::string name) {
auto type = ty->codegen(this->m_builder, this->m_structs);
auto value = this->m_builder.builder->CreateAlloca(type, nullptr, name);
this->m_stack_ptrs.push_back(std::pair<llvm::Value*, std::shared_ptr<types::Type>>{ value, ty });
}
llvm::Value* StackAllocator::pop_alloca(std::shared_ptr<types::Type> ty) {
assert(this->m_stack_ptrs.size() > 0);
auto first = this->m_stack_ptrs[0];
assert(types::types_equal(first.second, ty));
this->m_stack_ptrs.erase(this->m_stack_ptrs.begin());
return first.first;
}
}
namespace AST {
void IntLiteralExpression::codegen_alloca(codegen::StackAllocator&) {
}
void StringLiteralExpression::codegen_alloca(codegen::StackAllocator&) {
}
void ValueReferenceExpression::codegen_alloca(codegen::StackAllocator&) {
}
void BinaryOperationExpression::codegen_alloca(codegen::StackAllocator& allocator) {
this->m_lhs->codegen_alloca(allocator);
this->m_rhs->codegen_alloca(allocator);
}
void FunctionCallExpression::codegen_alloca(codegen::StackAllocator& allocator) {
this->m_fn_expr->codegen_alloca(allocator);
for (auto& arg : this->m_args) {
arg->codegen_alloca(allocator);
}
}
void CastExpression::codegen_alloca(codegen::StackAllocator& allocator) {
this->m_expr->codegen_alloca(allocator);
}
void RefExpression::codegen_alloca(codegen::StackAllocator& allocator) {
this->m_expr->codegen_alloca(allocator);
}
void DerefExpression::codegen_alloca(codegen::StackAllocator& allocator) {
this->m_expr->codegen_alloca(allocator);
}
void IndexAccessExpression::codegen_alloca(codegen::StackAllocator& allocator) {
this->m_expr->codegen_alloca(allocator);
}
void FieldAccessExpression::codegen_alloca(codegen::StackAllocator& allocator) {
this->m_expr->codegen_alloca(allocator);
}
void ListInitializerExpression::codegen_alloca(codegen::StackAllocator& allocator) {
allocator.push_alloca(this->m_ty, "list");
for (auto& expr : this->m_expressions) {
expr->codegen_alloca(allocator);
}
}
void UnaryExpression::codegen_alloca(codegen::StackAllocator& allocator) {
this->m_expr->codegen_alloca(allocator);
}
void ReturnStatement::codegen_alloca(codegen::StackAllocator& allocator) {
this->m_expr->codegen_alloca(allocator);
}
void InitializationStatement::codegen_alloca(codegen::StackAllocator& allocator) {
allocator.push_alloca(this->m_type, this->m_name);
if (this->m_expr) {
(*this->m_expr)->codegen_alloca(allocator);
}
}
void ExpressionStatement::codegen_alloca(codegen::StackAllocator& allocator) {
this->m_expr->codegen_alloca(allocator);
}
void IfStatement::codegen_alloca(codegen::StackAllocator& allocator) {
this->m_condition->codegen_alloca(allocator);
this->m_then->codegen_alloca(allocator);
if (this->m_else) {
(*this->m_else)->codegen_alloca(allocator);
}
}
void ForStatement::codegen_alloca(codegen::StackAllocator& allocator) {
if (this->m_init)
(*this->m_init)->codegen_alloca(allocator);
if (this->m_cond)
(*this->m_cond)->codegen_alloca(allocator);
if (this->m_after)
(*this->m_after)->codegen_alloca(allocator);
this->m_loop->codegen_alloca(allocator);
}
void WhileStatement::codegen_alloca(codegen::StackAllocator& allocator) {
if (this->m_cond)
(*this->m_cond)->codegen_alloca(allocator);
this->m_loop->codegen_alloca(allocator);
}
void CompoundStatement::codegen_alloca(codegen::StackAllocator& allocator) {
for (auto& statement : this->m_statements)
statement->codegen_alloca(allocator);
}
void BreakStatement::codegen_alloca(codegen::StackAllocator&) {}
void ContinueStatement::codegen_alloca(codegen::StackAllocator&) {}
}

View File

@ -1,28 +0,0 @@
#ifndef ALLOCATOR_H
#define ALLOCATOR_H
#include <vector>
#include <llvm/IR/IRBuilder.h>
#include "types.h"
#include "builder.h"
namespace codegen {
class StackAllocator {
private:
std::vector<std::pair<llvm::Value*, std::shared_ptr<types::Type>>> m_stack_ptrs;
codegen::Builder& m_builder;
codegen::TypeMap& m_structs;
public:
StackAllocator(codegen::Builder& builder, codegen::TypeMap& structs)
: m_stack_ptrs{}
, m_builder{ builder }
, m_structs{ structs } {
}
void push_alloca(std::shared_ptr<types::Type> ty, std::string name);
llvm::Value* pop_alloca(std::shared_ptr<types::Type> ty);
};
}
#endif

View File

@ -5,7 +5,7 @@
#include <vector>
#include <iostream>
#include <sstream>
#include <optional>
static bool iswhitespace(char& character) {
return character == ' '
@ -14,33 +14,6 @@ static bool iswhitespace(char& character) {
|| character == '\r';
}
static std::optional<char> get_escaped(std::string_view inspected) {
if (inspected == "n")
return '\n';
if (inspected == "r")
return '\r';
if (inspected == "t")
return '\t';
if (inspected == "\\")
return '\\';
if (inspected.size() <= 3) {
for (char c : inspected) {
std::cout << c << std::endl;
if (!std::isdigit(c))
return {};
if (std::stoi(std::string{ c }) > 8)
return {};
}
unsigned int x;
std::stringstream ss;
ss << std::oct << inspected;
ss >> x;
return static_cast<char>(x);
}
return {};
}
namespace token {
std::string type_name(Type& type) {
switch (type) {
@ -59,19 +32,9 @@ namespace token {
return "If";
case token::Type::ElseKeyword:
return "Else";
case token::Type::ForKeyword:
return "For";
case token::Type::WhileKeyword:
return "While";
case token::Type::BreakKeyword:
return "Break";
case token::Type::ContinueKeyword:
return "Continue";
case token::Type::Whitespace:
return "Whitespace";
case token::Type::Comment:
return "Comment";
case token::Type::Eof:
return "EOF";
@ -136,8 +99,7 @@ namespace token {
Token TokenStream::next() {
token::Token got = this->peek(0);
m_position++;
while (m_position < static_cast<int>(m_tokens.size())
&& (this->peek().type == Type::Whitespace || this->peek().type == Type::Comment)) {
while (m_position < static_cast<int>(m_tokens.size()) && this->peek().type == Type::Whitespace) {
m_position++;
}
return got;
@ -186,64 +148,24 @@ namespace token {
} while (std::isdigit(c));
tokens.push_back(token::Token{ token::Type::LiteralInt, content, meta + content.size() });
}
// Single-line comments
else if (c == '/' && text_length > (i + 2) && text[i + 1] == '/') {
std::string content{ };
i += 2;
while (text_length > i && text[i] != '\n') {
content += text[i++];
}
tokens.push_back(token::Token{ token::Type::Comment, content, meta + content.size() });
}
// Multi-line comments
else if (c == '/' && text_length > (i + 2) && text[i + 1] == '*') {
std::string content{ };
i += 2;
while (text_length > i) {
if (text[i] == '*' && text_length > (i + 1) && text[i + 1] == '/') {
i += 2;
break;
}
content += text[i++];
}
tokens.push_back(token::Token{ token::Type::Comment, content, meta + content.size() });
}
else if (c == '\"') {
std::string content{};
c = text[++i]; // Skip initial "
do {
if (c == '\\') {
std::string escaped_content{};
if ((i + 1) >= text_length) break;
auto potential = get_escaped(escaped_content + text[++i]);
while (potential.has_value() && (i + 1) < text_length) {
escaped_content += text[i];
potential = get_escaped(escaped_content + text[++i]);
}
if (escaped_content.size() > 0) {
auto escaped = get_escaped(escaped_content);
if (escaped.has_value())
content += *escaped;
}
c = text[i];
}
else {
content += c;
if ((i + 1) >= text_length) break;
c = text[++i];
}
content += c;
if ((i + 1) >= text_length) break;
c = text[++i];
} while (c != '\"');
i++; // Skip second "
tokens.push_back(token::Token{ token::Type::LiteralStr, content, meta + (content.size() + 2) });
}
else if (std::isalpha(c) || c == '_') {
else if (std::isalpha(c)) {
std::string content{};
do {
content += c;
if ((i + 1) >= text_length) break;
c = text[++i];
} while (std::isalnum(c) || c == '_');
} while (std::isalnum(c));
token::Type type = token::Type::Ident;
if (content == "return") {
@ -255,18 +177,6 @@ namespace token {
else if (content == "else") {
type = token::Type::ElseKeyword;
}
else if (content == "for") {
type = token::Type::ForKeyword;
}
else if (content == "while") {
type = token::Type::WhileKeyword;
}
else if (content == "break") {
type = token::Type::BreakKeyword;
}
else if (content == "continue") {
type = token::Type::ContinueKeyword;
}
tokens.push_back(token::Token{ type, content, meta + content.size() });
}
else if (iswhitespace(c)) {

View File

@ -16,13 +16,8 @@ namespace token {
ReturnKeyword,
IfKeyword,
ElseKeyword,
ForKeyword,
WhileKeyword,
BreakKeyword,
ContinueKeyword,
Whitespace,
Comment,
Eof,
};

View File

@ -5,31 +5,14 @@
#include "result.h"
namespace {
enum class TypecheckResKind {
enum class TypecheckRes {
Ok,
Castable,
};
struct TypecheckRes {
TypecheckResKind kind;
std::shared_ptr<types::Type> result;
};
Result<TypecheckRes, std::string> check_type(
typecheck::State& state,
std::shared_ptr<types::Type> checked,
std::shared_ptr<types::Type> target) {
auto potential_cast = types::find_cast(state.casts, checked, target);
Result<TypecheckRes, std::string> check_type(std::shared_ptr<types::Type> checked, std::shared_ptr<types::Type> target) {
if (types::types_equal(checked, target)) {
return TypecheckRes{ TypecheckResKind::Ok, target };
}
else if (potential_cast.has_value()) {
if (potential_cast->allow_implicit)
return TypecheckRes{ TypecheckResKind::Castable, target };
return std::string{ "Type " + checked->formatted() + " not implicitly castable to " + target->formatted() };
return TypecheckRes::Ok;
}
return std::string{ "Types " + checked->formatted() + " and " + target->formatted() + " incompatible" };
@ -41,13 +24,12 @@ namespace {
typecheck::State& state) {
if (res.ok()) {
auto result = res.unwrap();
if (result.kind == TypecheckResKind::Ok) {
if (result == TypecheckRes::Ok) {
return expr;
}
else {
return std::unique_ptr<AST::Expression> {
new AST::CastExpression{ expr->m_meta, result.result, std::move(expr) }
};
state.errors.push_back(CompileError("Casting not yet implemented", expr->m_meta));
return expr;
}
}
else {
@ -55,133 +37,58 @@ namespace {
return expr;
}
}
std::shared_ptr<types::Type> refresh_type(typecheck::Scope& scope, std::shared_ptr<types::Type> ty) {
if (ty->m_kind == types::TypeKind::Fundamental) {
return ty;
}
else if (ty->m_kind == types::TypeKind::Array) {
auto array_ty = dynamic_cast<types::ArrayType*>(ty.get());
array_ty->m_inner = refresh_type(scope, array_ty->m_inner);
return ty;
}
else if (ty->m_kind == types::TypeKind::Function) {
auto function_ty = dynamic_cast<types::FunctionType*>(ty.get());
function_ty->m_ret_ty = refresh_type(scope, function_ty->m_ret_ty);
for (int i = 0; i < static_cast<int>(function_ty->m_param_tys.size()); i++) {
function_ty->m_param_tys[i] = refresh_type(scope, function_ty->m_param_tys[i]);
}
return ty;
}
else if (ty->m_kind == types::TypeKind::Pointer) {
auto ptr_ty = dynamic_cast<types::PointerType*>(ty.get());
ptr_ty->m_inner = refresh_type(scope, ptr_ty->m_inner);
return ty;
}
else if (ty->m_kind == types::TypeKind::Struct) {
auto struct_ty = dynamic_cast<types::StructType*>(ty.get());
if (struct_ty->m_is_ref || struct_ty->m_is_def) {
if (scope.structs.find(*struct_ty->m_name) != scope.structs.end()) {
auto pre_existing = dynamic_cast<types::StructType*>(scope.structs[*struct_ty->m_name].get());
struct_ty->m_fields = pre_existing->m_fields;
}
}
if (struct_ty->m_fields) {
for (int i = 0; i < static_cast<int>((*struct_ty->m_fields).size()); i++) {
(*struct_ty->m_fields)[i].second = refresh_type(scope, (*struct_ty->m_fields)[i].second);
}
}
return ty;
}
else {
return ty;
}
}
}
namespace AST {
void IntLiteralExpression::typecheck_preprocess(typecheck::Scope& scope) {
this->m_ty = refresh_type(scope, this->m_ty);
}
typecheck::ExpressionType IntLiteralExpression::typecheck(
std::shared_ptr<types::Type> IntLiteralExpression::typecheck(
typecheck::State&,
typecheck::Scope&,
std::optional<std::shared_ptr<types::Type>> expected_ty
std::optional<std::shared_ptr<types::Type>>
) {
// Allow implicitly converting IntLiteralExpression to other types
// representable by integers.
if (expected_ty) {
if ((*expected_ty)->m_kind == types::TypeKind::Fundamental) {
auto ty = dynamic_cast<types::FundamentalType*>((*expected_ty).get());
if (
ty->m_ty == types::FundamentalTypeKind::Bool
|| ty->m_ty == types::FundamentalTypeKind::Char
|| ty->m_ty == types::FundamentalTypeKind::UChar
|| ty->m_ty == types::FundamentalTypeKind::ShortInt
|| ty->m_ty == types::FundamentalTypeKind::UShortInt
|| ty->m_ty == types::FundamentalTypeKind::Int
|| ty->m_ty == types::FundamentalTypeKind::UInt
|| ty->m_ty == types::FundamentalTypeKind::LongInt
|| ty->m_ty == types::FundamentalTypeKind::ULongInt
|| ty->m_ty == types::FundamentalTypeKind::LongLongInt
|| ty->m_ty == types::FundamentalTypeKind::ULongLongInt
) {
this->m_ty = *expected_ty;
}
}
}
return { this->m_ty, false, false };
return std::shared_ptr<types::Type>{
new types::FundamentalType{ types::FundamentalTypeKind::Int }
};
}
void StringLiteralExpression::typecheck_preprocess(typecheck::Scope&) {}
typecheck::ExpressionType StringLiteralExpression::typecheck(
std::shared_ptr<types::Type> StringLiteralExpression::typecheck(
typecheck::State&,
typecheck::Scope&,
std::optional<std::shared_ptr<types::Type>>
) {
auto char_ty = std::shared_ptr<types::Type>{
new types::FundamentalType{ true, types::FundamentalTypeKind::Char }
new types::FundamentalType{ types::FundamentalTypeKind::Char }
};
auto ptr_ty = new types::ArrayType{ char_ty, static_cast<uint32_t>(this->m_value.size()) + 1, true };
return { std::shared_ptr<types::Type>{ptr_ty}, true, false };
auto ptr_ty = new types::PointerType{ char_ty };
return std::shared_ptr<types::Type>{ptr_ty};
}
void ValueReferenceExpression::typecheck_preprocess(typecheck::Scope&) {}
typecheck::ExpressionType ValueReferenceExpression::typecheck(
std::shared_ptr<types::Type> ValueReferenceExpression::typecheck(
typecheck::State& state,
typecheck::Scope& scope,
std::optional<std::shared_ptr<types::Type>>
) {
if (scope.symbols.find(this->m_name) != scope.symbols.end()) {
return { scope.symbols[this->m_name], false, true };
return scope.symbols[this->m_name];
}
state.errors.push_back(CompileError("Value " + this->m_name + " not defined", this->m_meta));
return { std::shared_ptr<types::Type>{
new types::FundamentalType{ false, types::FundamentalTypeKind::Void }
}, false, true };
return std::shared_ptr<types::Type>{
new types::FundamentalType{ types::FundamentalTypeKind::Void }
};
}
void BinaryOperationExpression::typecheck_preprocess(typecheck::Scope& scope) {
this->m_lhs->typecheck_preprocess(scope);
this->m_rhs->typecheck_preprocess(scope);
}
typecheck::ExpressionType BinaryOperationExpression::typecheck(
std::shared_ptr<types::Type> BinaryOperationExpression::typecheck(
typecheck::State& state,
typecheck::Scope& scope,
std::optional<std::shared_ptr<types::Type>> expected_ty
std::optional<std::shared_ptr<types::Type>>
) {
auto lhs_res = this->m_lhs->typecheck(state, scope, {});
auto lhs_ty = lhs_res.type;
auto rhs_ty = this->m_rhs->typecheck(state, scope, {}).type;
auto lhs_ty = this->m_lhs->typecheck(state, scope, {});
auto rhs_ty = this->m_rhs->typecheck(state, scope, {});
if (this->m_binop == types::BinOp::Assignment) {
return lhs_ty;
}
// Try to find a binop that matches exactly
auto binop = types::find_binop(
state.binops,
lhs_ty,
@ -190,154 +97,11 @@ namespace AST {
);
if (binop) {
if (binop->lhs_lvalue) {
if (!lhs_res.lvalue) {
state.errors.push_back(CompileError("Value must be a modifiable l-value", this->m_lhs->m_meta));
}
else if (lhs_ty->m_const) {
state.errors.push_back(CompileError("Value must be a modifiable l-value", this->m_lhs->m_meta));
}
}
auto lhs_res = this->m_lhs->typecheck(state, scope, binop->lhs);
auto lhs_ty = lhs_res.type;
auto rhs_ty = this->m_rhs->typecheck(state, scope, binop->rhs).type;
auto lhs_ty_res = check_type(state, lhs_ty, binop->lhs);
auto rhs_ty_res = check_type(state, rhs_ty, binop->rhs);
this->m_lhs = handle_res(std::move(this->m_lhs), lhs_ty_res, state);
this->m_rhs = handle_res(std::move(this->m_rhs), rhs_ty_res, state);
return { binop->result, false, false };
return binop->result;
}
// If that fails, try to find binop that matches on one side perfectly
// and is castable on the other side, and would also be perfectly
// assignable to the expected value.
for (auto& binop : state.binops) {
if (binop.op != this->m_binop)
continue;
// TODO check for binops that may be implicitly castable
if (expected_ty) {
// Skip any binops that would not be immediately assignable to
// the expected type
if (!types::types_equal(binop.result, *expected_ty)) {
continue;
}
}
if (types::types_equal(binop.lhs, lhs_ty)) {
auto rhs_res = check_type(state, rhs_ty, binop.rhs);
if (!rhs_res.ok())
// Skip if not implicitly castable to lhs
continue;
if (binop.lhs_lvalue) {
if (!lhs_res.lvalue) {
state.errors.push_back(CompileError("Value must be a modifiable l-value", this->m_lhs->m_meta));
}
else if (lhs_ty->m_const) {
state.errors.push_back(CompileError("Value must be a modifiable l-value", this->m_lhs->m_meta));
}
}
rhs_ty = this->m_rhs->typecheck(state, scope, binop.rhs).type;
rhs_res = check_type(state, rhs_ty, binop.rhs);
this->m_rhs = handle_res(std::move(this->m_rhs), rhs_res, state);
return { binop.result, false, false };
}
else if (types::types_equal(binop.rhs, rhs_ty)) {
auto lhs_ty_res = check_type(state, lhs_ty, binop.lhs);
if (!lhs_ty_res.ok())
// Skip if not implicitly castable to rhs
continue;
if (binop.lhs_lvalue) {
if (!lhs_res.lvalue) {
state.errors.push_back(CompileError("Value must be a modifiable l-value", this->m_lhs->m_meta));
}
else if (lhs_ty->m_const) {
state.errors.push_back(CompileError("Value must be a modifiable l-value", this->m_lhs->m_meta));
}
}
lhs_ty = this->m_lhs->typecheck(state, scope, binop.lhs).type;
lhs_ty_res = check_type(state, lhs_ty, binop.lhs);
this->m_lhs = handle_res(std::move(this->m_lhs), lhs_ty_res, state);
return { binop.result, false, false };
}
}
// if that fails, accept binops that match the result type perfectly and
// is able to cast both types successfully
for (auto& binop : state.binops) {
if (binop.op != this->m_binop)
continue;
if (expected_ty) {
// Skip any binops that would not be immediately assignable to
// the expected type
if (!types::types_equal(binop.result, *expected_ty)) {
continue;
}
}
auto rhs_ty_res = check_type(state, rhs_ty, binop.rhs);
auto lhs_ty_res = check_type(state, lhs_ty, binop.lhs);
if (!rhs_ty_res.ok() || !lhs_ty_res.ok())
continue;
if (binop.lhs_lvalue) {
if (!lhs_res.lvalue) {
state.errors.push_back(CompileError("Value must be a modifiable l-value", this->m_lhs->m_meta));
}
else if (lhs_ty->m_const) {
state.errors.push_back(CompileError("Value must be a modifiable l-value", this->m_lhs->m_meta));
}
}
lhs_ty = this->m_lhs->typecheck(state, scope, binop.lhs).type;
rhs_ty = this->m_rhs->typecheck(state, scope, binop.rhs).type;
lhs_ty_res = check_type(state, lhs_ty, binop.lhs);
rhs_ty_res = check_type(state, rhs_ty, binop.rhs);
this->m_lhs = handle_res(std::move(this->m_lhs), lhs_ty_res, state);
this->m_rhs = handle_res(std::move(this->m_rhs), rhs_ty_res, state);
return { binop.result, false, false };
}
// Finally check for any binop that allows the result to be implicitly
// casted to the result
for (auto& binop : state.binops) {
if (binop.op != this->m_binop)
continue;
if (expected_ty) {
// Skip any binops that would not even be implicitly castable to
// the expected result
auto result_res = check_type(state, binop.result, *expected_ty);
if (!result_res.ok())
continue;
}
if (binop.lhs_lvalue) {
if (!lhs_res.lvalue) {
state.errors.push_back(CompileError("Value must be a modifiable l-value", this->m_lhs->m_meta));
}
else if (lhs_ty->m_const) {
state.errors.push_back(CompileError("Value must be a modifiable l-value", this->m_lhs->m_meta));
}
}
lhs_ty = this->m_lhs->typecheck(state, scope, binop.lhs).type;
rhs_ty = this->m_rhs->typecheck(state, scope, binop.rhs).type;
auto lhs_result = check_type(state, lhs_ty, binop.lhs);
auto rhs_result = check_type(state, rhs_ty, binop.rhs);
this->m_lhs = handle_res(std::move(this->m_lhs), lhs_result, state);
this->m_rhs = handle_res(std::move(this->m_rhs), lhs_result, state);
return { binop.result, false, false };
}
// No suitable binops found :(
state.errors.push_back(CompileError(
"No suitable binop between "
+ lhs_ty->formatted() + " "
@ -345,29 +109,22 @@ namespace AST {
+ rhs_ty->formatted(),
this->m_meta));
return { std::shared_ptr<types::Type>{
new types::FundamentalType{ false, types::FundamentalTypeKind::Void } }, false, false };
return std::shared_ptr<types::Type>{
new types::FundamentalType{ types::FundamentalTypeKind::Void } };
}
void FunctionCallExpression::typecheck_preprocess(typecheck::Scope& scope) {
this->m_fn_expr->typecheck_preprocess(scope);
for (auto& expr : this->m_args) {
expr->typecheck_preprocess(scope);
}
}
typecheck::ExpressionType FunctionCallExpression::typecheck(
std::shared_ptr<types::Type> FunctionCallExpression::typecheck(
typecheck::State& state,
typecheck::Scope& scope,
std::optional<std::shared_ptr<types::Type>>
) {
auto expr_ty = this->m_fn_expr->typecheck(state, scope, {}).type;
auto expr_ty = this->m_fn_expr->typecheck(state, scope, {});
if (expr_ty->m_kind != types::TypeKind::Function) {
state.errors.push_back(CompileError("Tried calling a non-function", this->m_meta));
return { std::shared_ptr<types::Type> {
new types::FundamentalType{ false, types::FundamentalTypeKind::Void }
}, false, false };
return std::shared_ptr<types::Type> {
new types::FundamentalType{ types::FundamentalTypeKind::Void }
};
}
auto fn_ty = dynamic_cast<types::FunctionType*>(expr_ty.get());
@ -382,9 +139,9 @@ namespace AST {
for (int i = 0; i < static_cast<int>(this->m_args.size()); i++) {
if (i < static_cast<int>(fn_ty->m_param_tys.size())) {
auto expected_param_ty = fn_ty->m_param_tys[i];
auto param_ty = this->m_args[i]->typecheck(state, scope, expected_param_ty).type;
auto param_ty = this->m_args[i]->typecheck(state, scope, expected_param_ty);
auto check_res = check_type(state, param_ty, expected_param_ty);
auto check_res = check_type(param_ty, expected_param_ty);
this->m_args[i] = handle_res(std::move(this->m_args[i]), check_res, state);
}
else {
@ -393,327 +150,34 @@ namespace AST {
}
}
return { fn_ty->m_ret_ty, false, false };
}
void CastExpression::typecheck_preprocess(typecheck::Scope& scope) {
this->m_ty = refresh_type(scope, this->m_ty);
this->m_expr->typecheck_preprocess(scope);
}
typecheck::ExpressionType CastExpression::typecheck(
typecheck::State& state,
typecheck::Scope& scope,
std::optional<std::shared_ptr<types::Type>>
) {
auto expr_ty = this->m_expr->typecheck(state, scope, {}).type;
auto cast = types::find_cast(state.casts, expr_ty, this->m_ty);
if (cast) {
return { cast->target_ty, false, false };
}
state.errors.push_back(CompileError("Cast from type "
+ expr_ty->formatted() + "to type " + this->m_ty->formatted()
+ " is not permitted", this->m_meta));
return { std::shared_ptr<types::Type> { new types::FundamentalType{
false,
types::FundamentalTypeKind::Void
} }, false, false };
}
void RefExpression::typecheck_preprocess(typecheck::Scope& scope) {
this->m_expr->typecheck_preprocess(scope);
}
typecheck::ExpressionType RefExpression::typecheck(
typecheck::State& state,
typecheck::Scope& scope,
std::optional<std::shared_ptr<types::Type>>
) {
auto expr_ty = this->m_expr->typecheck(state, scope, {}).type;
return { std::shared_ptr<types::Type> {
new types::PointerType{ false, expr_ty }
}, false, false };
}
void DerefExpression::typecheck_preprocess(typecheck::Scope& scope) {
this->m_expr->typecheck_preprocess(scope);
}
typecheck::ExpressionType DerefExpression::typecheck(
typecheck::State& state,
typecheck::Scope& scope,
std::optional<std::shared_ptr<types::Type>>
) {
auto expr_ty = this->m_expr->typecheck(state, scope, {});
if (expr_ty.type->m_kind != types::TypeKind::Pointer) {
state.errors.push_back(
CompileError("Tried to deref " + expr_ty.type->formatted(), this->m_meta));
return { std::shared_ptr<types::Type> {
new types::FundamentalType{ false, types::FundamentalTypeKind::Void }
}, false, expr_ty.lvalue };
}
auto ptr_ty = dynamic_cast<types::PointerType*>(expr_ty.type.get());
return { ptr_ty->m_inner, false, expr_ty.lvalue };
}
void IndexAccessExpression::typecheck_preprocess(typecheck::Scope& scope) {
this->m_expr->typecheck_preprocess(scope);
}
typecheck::ExpressionType IndexAccessExpression::typecheck(
typecheck::State& state,
typecheck::Scope& scope,
std::optional<std::shared_ptr<types::Type>>
) {
auto expr_ty = this->m_expr->typecheck(state, scope, {});
if (expr_ty.type->m_kind != types::TypeKind::Pointer && expr_ty.type->m_kind != types::TypeKind::Array) {
state.errors.push_back(
CompileError("Tried to index " + expr_ty.type->formatted(), this->m_meta));
return { std::shared_ptr<types::Type> {
new types::FundamentalType{ false, types::FundamentalTypeKind::Void }
}, false, expr_ty.lvalue };
}
if (expr_ty.type->m_kind == types::TypeKind::Pointer) {
auto ptr_ty = dynamic_cast<types::PointerType*>(expr_ty.type.get());
return { ptr_ty->m_inner, false, expr_ty.lvalue };
}
else if (expr_ty.type->m_kind == types::TypeKind::Array) {
auto ptr_ty = dynamic_cast<types::ArrayType*>(expr_ty.type.get());
return { ptr_ty->m_inner, false, expr_ty.lvalue };
}
// Default return type
return { std::shared_ptr<types::Type> {
new types::FundamentalType{ false, types::FundamentalTypeKind::Void }
}, false, expr_ty.lvalue };
}
void FieldAccessExpression::typecheck_preprocess(typecheck::Scope& scope) {
this->m_expr->typecheck_preprocess(scope);
}
typecheck::ExpressionType FieldAccessExpression::typecheck(
typecheck::State& state,
typecheck::Scope& scope,
std::optional<std::shared_ptr<types::Type>>
) {
auto expr_ty = this->m_expr->typecheck(state, scope, {});
if (expr_ty.type->m_kind != types::TypeKind::Struct) {
state.errors.push_back(
CompileError("Tried to access " + expr_ty.type->formatted() + "." + this->m_field, this->m_meta));
return { std::shared_ptr<types::Type> {
new types::FundamentalType{ false, types::FundamentalTypeKind::Void }
}, false, expr_ty.lvalue };
}
auto struct_ty = dynamic_cast<types::StructType*>(expr_ty.type.get());
if (struct_ty->m_fields) {
for (auto& field : *struct_ty->m_fields) {
if (field.first == this->m_field) {
return { field.second, false, expr_ty.lvalue };
}
}
state.errors.push_back(CompileError("No such field", this->m_meta));
return { std::shared_ptr<types::Type> {
new types::FundamentalType{ false, types::FundamentalTypeKind::Void }
}, false, expr_ty.lvalue };
}
state.errors.push_back(CompileError("Cannot access fields of opaque struct", this->m_meta));
return { std::shared_ptr<types::Type> {
new types::FundamentalType{ false, types::FundamentalTypeKind::Void }
}, false, expr_ty.lvalue };
}
void ListInitializerExpression::typecheck_preprocess(typecheck::Scope& scope) {
this->m_ty = refresh_type(scope, this->m_ty);
for (auto& expr : this->m_expressions) {
expr->typecheck_preprocess(scope);
}
}
typecheck::ExpressionType ListInitializerExpression::typecheck(
typecheck::State& state,
typecheck::Scope& scope,
std::optional<std::shared_ptr<types::Type>> expected_ty
) {
if (expected_ty) {
if ((*expected_ty)->m_kind == types::TypeKind::Array) {
auto array_ty = dynamic_cast<types::ArrayType*>(expected_ty->get());
for (auto& expr : this->m_expressions) {
auto expr_ty = expr->typecheck(state, scope, array_ty->m_inner).type;
auto expr_res = check_type(state, expr_ty, array_ty->m_inner);
expr = handle_res(std::move(expr), expr_res, state);
}
this->m_ty = std::shared_ptr<types::Type>{
new types::ArrayType{
array_ty->m_inner,
static_cast<uint32_t>(this->m_expressions.size()),
true
}
};
}
else if ((*expected_ty)->m_kind == types::TypeKind::Struct) {
auto struct_ty = dynamic_cast<types::StructType*>(expected_ty->get());
if (struct_ty->m_fields) {
if (this->m_expressions.size() > struct_ty->m_fields->size()) {
state.errors.push_back(CompileError(
"Too many initializer values for " + struct_ty->formatted(),
this->m_meta));
return { *expected_ty, true, false };
}
for (int i = 0; i < static_cast<int>(this->m_expressions.size()); i++) {
auto expected_field = (*struct_ty->m_fields)[i];
auto expr_ty = this->m_expressions[i]->typecheck(state, scope, expected_field.second).type;
auto res = check_type(state, expr_ty, expected_field.second);
this->m_expressions[i] = handle_res(std::move(this->m_expressions[i]), res, state);
}
this->m_ty = *expected_ty;
return { this->m_ty, true, false };
}
else {
if (this->m_expressions.size() > 0) {
state.errors.push_back(CompileError(
"Too many initializer values for " + struct_ty->formatted(),
this->m_meta));
return { *expected_ty, true, false };
}
else {
this->m_ty = *expected_ty;
return { this->m_ty, true, false };
}
}
}
else {
return { std::shared_ptr<types::Type> {
new types::FundamentalType{ false, types::FundamentalTypeKind::Void }
}, true, false };
}
return { this->m_ty, true, false };
}
// No expected ty, try to infer array type from elements
if (this->m_expressions.size() == 0) {
this->m_ty = std::shared_ptr<types::Type>{
new types::ArrayType{
std::make_shared<types::FundamentalType>(true, types::FundamentalTypeKind::Void),
0,
true
}
};
return { this->m_ty, true, false };
}
else {
auto first_expr_ty = this->m_expressions[0]->typecheck(state, scope, {}).type;
for (int i = 1; i < static_cast<int>(this->m_expressions.size()); i++) {
auto expr_ty = this->m_expressions[i]->typecheck(state, scope, first_expr_ty).type;
auto expr_res = check_type(state, expr_ty, first_expr_ty);
this->m_expressions[i] = handle_res(std::move(this->m_expressions[i]), expr_res, state);
}
this->m_ty = std::shared_ptr<types::Type>{
new types::ArrayType{
first_expr_ty,
static_cast<uint32_t>(this->m_expressions.size()),
true
}
};
return { this->m_ty, true, false };
}
}
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
|| this->m_unary == types::Unary::SubPostfix || this->m_unary == types::Unary::SubPrefix) {
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 for " + this->formatted(), this->m_meta));
return { expr_res.type, false, false };
}
}
void ReturnStatement::typecheck_preprocess(typecheck::Scope& scope) {
this->m_expr->typecheck_preprocess(scope);
return fn_ty->m_ret_ty;
}
void ReturnStatement::typecheck(typecheck::State& state, typecheck::Scope& scope) {
auto res_ty = this->m_expr->typecheck(state, scope, scope.return_ty).type;
auto res_ty = this->m_expr->typecheck(state, scope, scope.return_ty);
if (scope.return_ty) {
auto check_res = check_type(state, res_ty, *scope.return_ty);
auto check_res = check_type(res_ty, *scope.return_ty);
this->m_expr = handle_res(std::move(this->m_expr), check_res, state);
}
}
void InitializationStatement::typecheck_preprocess(typecheck::Scope& scope) {
this->m_type = refresh_type(scope, this->m_type);
if (this->m_expr)
(*this->m_expr)->typecheck_preprocess(scope);
}
void InitializationStatement::typecheck(typecheck::State& state, typecheck::Scope& scope) {
if (this->m_expr) {
auto expr_ty = (*this->m_expr)->typecheck(state, scope, this->m_type);
if (this->m_type->m_kind == types::TypeKind::Array && !expr_ty.array_initializer) {
state.errors.push_back(
CompileError("Arrays can only be initialized with list-initializers or strings", this->m_meta)
);
}
auto check_res = check_type(state, expr_ty.type, this->m_type);
this->m_expr = handle_res(std::move(*this->m_expr), check_res, state);
(*this->m_expr)->typecheck(state, scope, this->m_type);
}
scope.symbols[this->m_name] = this->m_type;
}
void ExpressionStatement::typecheck_preprocess(typecheck::Scope& scope) {
this->m_expr->typecheck_preprocess(scope);
}
void ExpressionStatement::typecheck(typecheck::State& state, typecheck::Scope& scope) {
this->m_expr->typecheck(state, scope, {});
}
void IfStatement::typecheck_preprocess(typecheck::Scope& scope) {
this->m_condition->typecheck_preprocess(scope);
this->m_then->typecheck_preprocess(scope);
if (this->m_else)
(*this->m_else)->typecheck_preprocess(scope);
}
void IfStatement::typecheck(typecheck::State& state, typecheck::Scope& scope) {
auto bool_ty = std::shared_ptr<types::Type>{
new types::FundamentalType{ false, types::FundamentalTypeKind::Bool } };
auto expr_ty = this->m_condition->typecheck(state, scope, bool_ty).type;
new types::FundamentalType{ types::FundamentalTypeKind::Bool } };
auto expr_ty = this->m_condition->typecheck(state, scope, bool_ty);
auto check_res = check_type(state, expr_ty, bool_ty);
auto check_res = check_type(expr_ty, bool_ty);
this->m_condition = handle_res(std::move(this->m_condition), check_res, state);
this->m_then->typecheck(state, scope);
@ -722,103 +186,6 @@ namespace AST {
}
}
void ForStatement::typecheck_preprocess(typecheck::Scope& scope) {
if (this->m_init)
(*this->m_init)->typecheck_preprocess(scope);
if (this->m_cond)
(*this->m_cond)->typecheck_preprocess(scope);
if (this->m_after)
(*this->m_after)->typecheck_preprocess(scope);
this->m_loop->typecheck_preprocess(scope);
}
void ForStatement::typecheck(typecheck::State& state, typecheck::Scope& scope) {
auto bool_ty = std::shared_ptr<types::Type>{
new types::FundamentalType{ false, types::FundamentalTypeKind::Bool } };
typecheck::Scope inner_scope{ scope };
inner_scope.loop_depth += 1;
if (this->m_init)
(*this->m_init)->typecheck(state, inner_scope);
if (this->m_cond) {
auto cond_ty = (*this->m_cond)->typecheck(state, inner_scope, bool_ty).type;
auto check_res = check_type(state, cond_ty, bool_ty);
this->m_cond = handle_res(std::move(*this->m_cond), check_res, state);
}
if (this->m_after)
(*this->m_after)->typecheck(state, inner_scope, {});
this->m_loop->typecheck(state, inner_scope);
}
void WhileStatement::typecheck_preprocess(typecheck::Scope& scope) {
if (this->m_cond)
(*this->m_cond)->typecheck_preprocess(scope);
this->m_loop->typecheck_preprocess(scope);
}
void WhileStatement::typecheck(typecheck::State& state, typecheck::Scope& scope) {
auto bool_ty = std::shared_ptr<types::Type>{
new types::FundamentalType{ false, types::FundamentalTypeKind::Bool } };
typecheck::Scope inner_scope{ scope };
inner_scope.loop_depth += 1;
if (this->m_cond) {
auto cond_ty = (*this->m_cond)->typecheck(state, inner_scope, bool_ty).type;
auto check_res = check_type(state, cond_ty, bool_ty);
this->m_cond = handle_res(std::move(*this->m_cond), check_res, state);
}
this->m_loop->typecheck(state, inner_scope);
}
void CompoundStatement::typecheck_preprocess(typecheck::Scope& scope) {
typecheck::Scope inner{ scope };
for (auto& statement : this->m_statements) {
statement->typecheck_preprocess(inner);
}
}
void CompoundStatement::typecheck(typecheck::State& state, typecheck::Scope& scope) {
typecheck::Scope inner{ scope };
for (auto& statement : this->m_statements) {
statement->typecheck(state, inner);
}
}
void BreakStatement::typecheck_preprocess(typecheck::Scope&) {}
void BreakStatement::typecheck(typecheck::State& state, typecheck::Scope& scope) {
if (scope.loop_depth <= 0) {
state.errors.push_back(CompileError("Can not use break outside of a loop", this->m_meta));
}
}
void ContinueStatement::typecheck_preprocess(typecheck::Scope&) {}
void ContinueStatement::typecheck(typecheck::State& state, typecheck::Scope& scope) {
if (scope.loop_depth <= 0) {
state.errors.push_back(CompileError("Can not use continue outside of a loop", this->m_meta));
}
}
void Function::typecheck_preprocess(typecheck::Scope& scope) {
this->m_return_ty = refresh_type(scope, this->m_return_ty);
for (auto& param : this->m_params) {
param.second = refresh_type(scope, param.second);
}
if (this->m_statements) {
for (auto& statement : *this->m_statements) {
statement->typecheck_preprocess(scope);
}
}
}
void Function::typecheck(typecheck::State& state, typecheck::Scope& scope) {
auto return_ty = this->m_return_ty;
std::vector<std::shared_ptr<types::Type>> param_tys{};
@ -826,7 +193,7 @@ namespace AST {
param_tys.push_back(param.second);
}
auto function_ty = new types::FunctionType{ true, return_ty, param_tys, this->m_is_vararg };
auto function_ty = new types::FunctionType{ return_ty, param_tys, this->m_is_vararg };
scope.symbols[this->m_name] = std::shared_ptr<types::Type>{ function_ty };
typecheck::Scope inner{ scope };
@ -844,28 +211,4 @@ namespace AST {
}
}
}
void TopLevelTypedef::typecheck_preprocess(typecheck::Scope& scope) {
if (this->m_ty->m_kind == types::TypeKind::Struct) {
auto struct_ty = dynamic_cast<types::StructType*>(this->m_ty.get());
if (struct_ty->m_is_ref || struct_ty->m_is_def) {
return;
}
if (struct_ty->m_name) {
scope.structs[*struct_ty->m_name] = this->m_ty;
}
}
}
void TopLevelTypedef::typecheck(typecheck::State&, typecheck::Scope& scope) {
if (this->m_ty->m_kind == types::TypeKind::Struct) {
auto struct_ty = dynamic_cast<types::StructType*>(this->m_ty.get());
if (struct_ty->m_is_ref || struct_ty->m_is_def) {
return;
}
if (struct_ty->m_name) {
scope.structs[*struct_ty->m_name] = this->m_ty;
}
}
}
}

View File

@ -5,29 +5,18 @@
#include "types.h"
#include "binops.h"
#include "casting.h"
#include "errors.h"
namespace typecheck {
struct Scope {
std::map<std::string, std::shared_ptr<types::Type>> symbols;
std::map<std::string, std::shared_ptr<types::Type>> structs;
std::optional<std::shared_ptr<types::Type>> return_ty;
int loop_depth;
};
struct State {
std::vector<types::BinopDefinition> binops;
std::vector<types::UnopDefinition> unops;
std::vector<types::CastDefinition> casts;
std::vector<CompileError> errors;
};
struct ExpressionType {
std::shared_ptr<types::Type> type;
bool array_initializer;
bool lvalue;
};
}
#endif

View File

@ -1,49 +1,19 @@
#include <sstream>
#include <iostream>
#include "types.h"
#include "binops.h"
#include "builder.h"
namespace types {
/// @brief https://en.cppreference.com/c/language/operator_precedence
int operator_precedence(BinOp& op) {
switch (op) {
case BinOp::Mul:
case BinOp::Div:
case BinOp::Modulo:
return 3;
case BinOp::Add:
case BinOp::Sub:
return 4;
case BinOp::BitShiftLeft:
case BinOp::BitShiftRight:
return 5;
return 10;
case BinOp::LessThan:
case BinOp::GreaterThan:
case BinOp::LessThanEquals:
case BinOp::GreaterThanEquals:
return 6;
case BinOp::Equals:
case BinOp::NotEquals:
return 7;
case BinOp::BitAnd:
return 8;
case BinOp::BitXor:
return 9;
case BinOp::BitOr:
return 10;
case BinOp::And:
return 11;
case BinOp::Or:
return 12;
return 20;
case BinOp::Assignment:
return 1000;
@ -56,152 +26,99 @@ namespace types {
switch (op) {
case BinOp::Assignment:
return "=";
case BinOp::Add:
return "+";
case BinOp::Sub:
return "-";
case BinOp::Mul:
return "*";
case BinOp::Div:
return "/";
case BinOp::Modulo:
return "%";
case BinOp::BitAnd:
return "&";
case BinOp::BitOr:
return "|";
case BinOp::BitXor:
return "^";
case BinOp::BitShiftLeft:
return "<<";
case BinOp::BitShiftRight:
return ">>";
case BinOp::LessThan:
return "<";
case BinOp::GreaterThan:
return ">";
case BinOp::LessThanEquals:
return "<=";
case BinOp::GreaterThanEquals:
return ">=";
case BinOp::Equals:
return "==";
case BinOp::NotEquals:
return "!=";
default:
return "??";
}
}
std::string FundamentalType::formatted() {
std::stringstream out{ "" };
if (this->m_const)
out << "const ";
switch (this->m_ty) {
case FundamentalTypeKind::ShortInt:
out << "ShortInt";
break;
case FundamentalTypeKind::Int:
out << "Int";
break;
case FundamentalTypeKind::LongInt:
out << "LongInt";
break;
case FundamentalTypeKind::LongLongInt:
out << "LongLongInt";
break;
case FundamentalTypeKind::UShortInt:
out << "UShortInt";
break;
case FundamentalTypeKind::UInt:
out << "UInt";
break;
case FundamentalTypeKind::ULongInt:
out << "ULongInt";
break;
case FundamentalTypeKind::ULongLongInt:
out << "ULongLongInt";
break;
case FundamentalTypeKind::AnyInt:
out << "AnyInt";
break;
return "Int";
case FundamentalTypeKind::Bool:
out << "Bool";
break;
case FundamentalTypeKind::UChar:
out << "UChar";
break;
return "Bool";
case FundamentalTypeKind::Char:
out << "Char";
break;
return "Char";
case FundamentalTypeKind::Void:
out << "Void";
break;
return "Void";
default:
out << "Unknown";
break;
return "Unknown";
}
return out.str();
}
llvm::Value* Type::add(codegen::Builder&, llvm::Value*, llvm::Value*) {
throw std::runtime_error("Invalid operation for this type");
}
llvm::Value* Type::sub(codegen::Builder&, llvm::Value*, llvm::Value*) {
throw std::runtime_error("Invalid operation for this type");
}
llvm::Value* Type::lt(codegen::Builder&, llvm::Value*, llvm::Value*) {
throw std::runtime_error("Invalid operation for this type");
}
llvm::Value* Type::gt(codegen::Builder&, llvm::Value*, llvm::Value*) {
throw std::runtime_error("Invalid operation for this type");
}
std::optional<std::shared_ptr<Type>> Type::return_type() {
return {};
}
bool Type::is_signed() {
return false;
}
std::pair<llvm::Value*, std::shared_ptr<Type>> FundamentalType::load(codegen::Builder&, llvm::Value* ptr, codegen::TypeMap&) {
std::pair<llvm::Value*, std::shared_ptr<Type>> FundamentalType::load(codegen::Builder&, llvm::Value* ptr) {
auto self = std::make_shared<FundamentalType>(*this);
return std::pair(ptr, self);
}
bool FundamentalType::is_signed() {
llvm::Value* FundamentalType::add(codegen::Builder& builder, llvm::Value* lhs, llvm::Value* rhs) {
switch (this->m_ty) {
case FundamentalTypeKind::ShortInt:
case FundamentalTypeKind::Int:
case FundamentalTypeKind::LongInt:
case FundamentalTypeKind::LongLongInt:
return true;
case FundamentalTypeKind::UShortInt:
case FundamentalTypeKind::UInt:
case FundamentalTypeKind::ULongInt:
case FundamentalTypeKind::ULongLongInt:
return false;
case FundamentalTypeKind::Char:
return true;
case FundamentalTypeKind::UChar:
return false;
case FundamentalTypeKind::Bool:
return false;
case FundamentalTypeKind::Char:
return builder.builder->CreateAdd(lhs, rhs, "add");
default:
throw std::runtime_error("Invalid type for add");
}
}
llvm::Value* FundamentalType::sub(codegen::Builder& builder, llvm::Value* lhs, llvm::Value* rhs) {
switch (this->m_ty) {
case FundamentalTypeKind::Int:
case FundamentalTypeKind::Bool:
case FundamentalTypeKind::Char:
return builder.builder->CreateSub(lhs, rhs, "sub");
default:
throw std::runtime_error("Invalid type");
}
}
uint32_t FundamentalType::size() {
llvm::Value* FundamentalType::lt(codegen::Builder& builder, llvm::Value* lhs, llvm::Value* rhs) {
switch (this->m_ty) {
case FundamentalTypeKind::ShortInt:
case FundamentalTypeKind::UShortInt:
return 16;
case FundamentalTypeKind::Int:
case FundamentalTypeKind::UInt:
return 32;
case FundamentalTypeKind::LongInt:
case FundamentalTypeKind::ULongInt:
return 64;
case FundamentalTypeKind::LongLongInt:
case FundamentalTypeKind::ULongLongInt:
return 128;
case FundamentalTypeKind::Bool:
return 1;
case FundamentalTypeKind::Char:
case FundamentalTypeKind::UChar:
return 8;
return builder.builder->CreateCmp(llvm::CmpInst::Predicate::ICMP_SLT, lhs, rhs, "cmp");
default:
throw std::runtime_error("Invalid type");
}
}
llvm::Value* FundamentalType::gt(codegen::Builder& builder, llvm::Value* lhs, llvm::Value* rhs) {
switch (this->m_ty) {
case FundamentalTypeKind::Int:
case FundamentalTypeKind::Bool:
case FundamentalTypeKind::Char:
return builder.builder->CreateCmp(llvm::CmpInst::Predicate::ICMP_SGT, lhs, rhs);
default:
throw std::runtime_error("Invalid type");
}
@ -209,9 +126,6 @@ namespace types {
std::string FunctionType::formatted() {
std::stringstream out{ "" };
if (this->m_const)
out << "const ";
out << "(";
int counter = 0;
@ -235,96 +149,24 @@ namespace types {
return this->m_ret_ty;
}
std::pair<llvm::Value*, std::shared_ptr<Type>> FunctionType::load(codegen::Builder&, llvm::Value* ptr, codegen::TypeMap&) {
std::pair<llvm::Value*, std::shared_ptr<Type>> FunctionType::load(codegen::Builder&, llvm::Value* ptr) {
auto self = std::make_shared<FunctionType>(*this);
return std::pair(ptr, self);
}
uint32_t FunctionType::size() {
return 64;
}
std::string PointerType::formatted() {
std::stringstream out{ "" };
out << this->m_inner->formatted() << "*";
if (this->m_const)
out << " const";
return out.str();
}
std::pair<llvm::Value*, std::shared_ptr<Type>> PointerType::load(codegen::Builder& builder, llvm::Value* ptr, codegen::TypeMap& structs) {
std::pair<llvm::Value*, std::shared_ptr<Type>> PointerType::load(codegen::Builder& builder, llvm::Value* ptr) {
return std::pair(
builder.builder->CreateLoad(this->m_inner->codegen(builder, structs), ptr, "load"),
builder.builder->CreateLoad(this->m_inner->codegen(builder), ptr),
this->m_inner
);
}
uint32_t PointerType::size() {
return 64;
}
std::string ArrayType::formatted() {
std::stringstream out{ "" };
// Arrays are always constant, no reason to format it.
out << this->m_inner->formatted();
out << "[" << this->m_size << "]";
return out.str();
}
std::pair<llvm::Value*, std::shared_ptr<Type>> ArrayType::load(codegen::Builder&, llvm::Value* ptr, codegen::TypeMap&) {
auto self = std::make_shared<ArrayType>(*this);
return std::pair(ptr, self);
}
uint32_t ArrayType::size() {
return this->m_size * this->m_inner->size();
}
std::string StructType::formatted() {
std::stringstream out{ "" };
if (this->m_const)
out << "const ";
out << "struct(" << this->m_id << ")";
if (this->m_is_ref)
out << "(ref)";
out << " ";
if (this->m_name) {
out << *this->m_name << " ";
}
if (this->m_fields) {
out << "{ ";
int counter = 0;
for (auto& field : *this->m_fields) {
if (counter++ > 0)
out << ", ";
out << field.first << ": " << field.second->formatted() << " ";
}
out << "}";
}
return out.str();
}
std::pair<llvm::Value*, std::shared_ptr<Type>> StructType::load(codegen::Builder&, llvm::Value* ptr, codegen::TypeMap&) {
auto self = std::make_shared<StructType>(*this);
return std::pair(ptr, self);
}
uint32_t StructType::size() {
uint32_t size{ 0 };
if (this->m_fields) {
for (auto& field : *this->m_fields) {
size += field.second->size();
}
}
return size;
}
bool types_equal(std::shared_ptr<types::Type> type1, std::shared_ptr<types::Type> type2) {
if (type1->m_kind != type2->m_kind)
return false;
@ -360,38 +202,6 @@ namespace types {
return types_equal(ty1->m_inner, ty2->m_inner);
}
else if (type1->m_kind == TypeKind::Array) {
auto ty1 = dynamic_cast<ArrayType*>(type1.get());
auto ty2 = dynamic_cast<ArrayType*>(type2.get());
return types_equal(ty1->m_inner, ty2->m_inner) && ty1->m_size == ty2->m_size;
}
else if (type1->m_kind == TypeKind::Struct) {
auto ty1 = dynamic_cast<StructType*>(type1.get());
auto ty2 = dynamic_cast<StructType*>(type2.get());
if (ty1->m_is_ref || ty2->m_is_ref)
return ty1->m_id == ty2->m_id;
if (ty1->m_fields.has_value() != ty2->m_fields.has_value())
return false;
if (ty1->m_fields) {
if (ty1->m_fields->size() != ty2->m_fields->size())
return false;
for (int i = 0; i < static_cast<int>(ty1->m_fields->size()); i++) {
auto field1 = (*ty1->m_fields)[i];
auto field2 = (*ty2->m_fields)[i];
if (!types_equal(field1.second, field2.second))
return false;
}
}
return true;
}
else {
return false;
}

View File

@ -11,57 +11,42 @@ namespace types {
Fundamental,
Function,
Pointer,
Array,
Struct,
};
enum FundamentalTypeKind {
Int,
ShortInt,
LongInt,
LongLongInt,
UInt,
UShortInt,
ULongInt,
ULongLongInt,
/// @brief stand-in type for integer literals
AnyInt,
Bool,
Char,
UChar,
Void,
/// @brief Mainly used for binop resolution
Any,
};
class Type {
public:
TypeKind m_kind;
bool m_const;
Type(TypeKind kind, bool is_const) : m_kind{ kind }, m_const{ is_const } {}
Type(TypeKind kind) : m_kind{ kind } {}
virtual ~Type() = default;
virtual std::string formatted() = 0;
virtual llvm::Type* codegen(codegen::Builder& builder, codegen::TypeMap& structs) = 0;
virtual std::pair<llvm::Value*, std::shared_ptr<Type>> load(codegen::Builder& builder, llvm::Value* ptr, codegen::TypeMap& structs) = 0;
virtual llvm::Type* codegen(codegen::Builder& builder) = 0;
virtual std::pair<llvm::Value*, std::shared_ptr<Type>> load(codegen::Builder& builder, llvm::Value* ptr) = 0;
virtual std::optional<std::shared_ptr<Type>> return_type();
virtual bool is_signed();
virtual uint32_t size() = 0;
virtual llvm::Value* add(codegen::Builder& builder, llvm::Value* lhs, llvm::Value* rhs);
virtual llvm::Value* sub(codegen::Builder& builder, llvm::Value* lhs, llvm::Value* rhs);
virtual llvm::Value* lt(codegen::Builder& builder, llvm::Value* lhs, llvm::Value* rhs);
virtual llvm::Value* gt(codegen::Builder& builder, llvm::Value* lhs, llvm::Value* rhs);
};
class FundamentalType : public Type {
public:
FundamentalTypeKind m_ty;
FundamentalType(bool is_const, FundamentalTypeKind kind) : Type(TypeKind::Fundamental, is_const), m_ty{ kind } {}
FundamentalType(FundamentalTypeKind kind) : Type(TypeKind::Fundamental), m_ty{ kind } {}
virtual ~FundamentalType() override = default;
virtual std::string formatted() override;
virtual llvm::Type* codegen(codegen::Builder& builder, codegen::TypeMap& structs) override;
virtual std::pair<llvm::Value*, std::shared_ptr<Type>> load(codegen::Builder& builder, llvm::Value* ptr, codegen::TypeMap& structs) override;
virtual bool is_signed() override;
virtual uint32_t size() override;
virtual llvm::Type* codegen(codegen::Builder& builder) override;
virtual std::pair<llvm::Value*, std::shared_ptr<Type>> load(codegen::Builder& builder, llvm::Value* ptr) override;
virtual llvm::Value* add(codegen::Builder& builder, llvm::Value* lhs, llvm::Value* rhs) override;
virtual llvm::Value* sub(codegen::Builder& builder, llvm::Value* lhs, llvm::Value* rhs) override;
virtual llvm::Value* lt(codegen::Builder& builder, llvm::Value* lhs, llvm::Value* rhs) override;
virtual llvm::Value* gt(codegen::Builder& builder, llvm::Value* lhs, llvm::Value* rhs) override;
};
@ -70,68 +55,31 @@ namespace types {
std::shared_ptr<Type> m_ret_ty;
std::vector<std::shared_ptr<Type>> m_param_tys;
bool m_vararg;
FunctionType(bool is_const, std::shared_ptr<Type> ret_ty, std::vector<std::shared_ptr<Type>> param_tys, bool vararg)
: Type(TypeKind::Function, is_const)
FunctionType(std::shared_ptr<Type> ret_ty, std::vector<std::shared_ptr<Type>> param_tys, bool vararg)
: Type(TypeKind::Function)
, m_ret_ty{ std::move(ret_ty) }
, m_param_tys{ std::move(param_tys) }
, m_vararg{ vararg } {
}
virtual ~FunctionType() override = default;
virtual std::string formatted() override;
virtual llvm::Type* codegen(codegen::Builder& builder, codegen::TypeMap& structs) override;
virtual std::pair<llvm::Value*, std::shared_ptr<Type>> load(codegen::Builder& builder, llvm::Value* ptr, codegen::TypeMap& structs) override;
virtual llvm::Type* codegen(codegen::Builder& builder) override;
virtual std::pair<llvm::Value*, std::shared_ptr<Type>> load(codegen::Builder& builder, llvm::Value* ptr) override;
virtual std::optional<std::shared_ptr<Type>> return_type() override;
virtual uint32_t size() override;
};
class PointerType : public Type {
public:
std::shared_ptr<Type> m_inner;
PointerType(bool is_const, std::shared_ptr<Type> inner)
: Type(TypeKind::Pointer, is_const), m_inner{ std::move(inner) } {
PointerType(std::shared_ptr<Type> inner)
: Type(TypeKind::Pointer), m_inner{ std::move(inner) } {
}
virtual ~PointerType() override = default;
virtual std::string formatted() override;
virtual llvm::Type* codegen(codegen::Builder& builder, codegen::TypeMap& structs) override;
virtual std::pair<llvm::Value*, std::shared_ptr<Type>> load(codegen::Builder& builder, llvm::Value* ptr, codegen::TypeMap& structs) override;
virtual uint32_t size() override;
};
class ArrayType : public Type {
public:
std::shared_ptr<Type> m_inner;
uint32_t m_size;
bool m_raw;
ArrayType(std::shared_ptr<Type> inner, uint32_t size, bool raw)
: Type(TypeKind::Array, true), m_inner{ std::move(inner) }, m_size{ size }, m_raw{ raw } {
}
virtual ~ArrayType() override = default;
virtual std::string formatted() override;
virtual llvm::Type* codegen(codegen::Builder& builder, codegen::TypeMap& structs) override;
virtual std::pair<llvm::Value*, std::shared_ptr<Type>> load(codegen::Builder& builder, llvm::Value* ptr, codegen::TypeMap& structs) override;
virtual uint32_t size() override;
};
typedef std::pair<std::string, std::shared_ptr<types::Type>> StructField;
class StructType : public Type {
public:
std::optional<std::string> m_name;
std::optional<std::vector<StructField>> m_fields;
bool m_is_ref;
bool m_is_def;
uint32_t m_id;
StructType(bool is_const, std::optional<std::string> name, std::optional<std::vector<StructField>> fields, bool is_ref, bool is_def, uint32_t id)
: Type(TypeKind::Struct, is_const), m_name{ name }, m_fields{ fields }, m_is_ref{ is_ref }, m_is_def{ is_def }, m_id{ id } {
}
virtual ~StructType() override = default;
virtual std::string formatted() override;
virtual llvm::Type* codegen(codegen::Builder& builder, codegen::TypeMap& structs) override;
virtual std::pair<llvm::Value*, std::shared_ptr<Type>> load(codegen::Builder& builder, llvm::Value* ptr, codegen::TypeMap& structs) override;
virtual uint32_t size() override;
virtual llvm::Type* codegen(codegen::Builder& builder) override;
virtual std::pair<llvm::Value*, std::shared_ptr<Type>> load(codegen::Builder& builder, llvm::Value* ptr) override;
};
bool types_equal(std::shared_ptr<types::Type> type1, std::shared_ptr<types::Type> type2);

71
test.c
View File

@ -6,72 +6,7 @@ int fibonacci(int n) {
return fibonacci(n - 1) + fibonacci(n - 2);
}
void change_first(char otus[5]) {
otus[0] = 115;
}
struct Otus;
struct Otus {
int field;
int second[2];
};
void update(struct Otus potus) {
potus.field = 20;
}
void update_ptr(char* ptr) {
*ptr = 50;
}
long long int main() {
// Test fibonacci sequence
char text[30] = "10th fibonacci number is %d!\n";
printf(text, fibonacci(10));
// Test arrays
char somelist[5] = { 1, 2, 3, 4, 5 };
char* somelist_ptr = somelist;
change_first(somelist);
printf("first element: %d!\n", somelist[0]);
printf("first element via ptr: %d!\n", somelist_ptr[0]);
// Test structs and nested fields
struct Otus otus = { 5, { 7, 3 } };
update(otus);
printf("first field: %d!\n", otus.field);
printf("second field's second element: %d!\n", otus.second[1]);
// Test pointers
char hello = 10;
update_ptr(&hello);
printf("hello: %d!\n", hello);
/** Test nested arrays */
char twod_array[5][5];
twod_array[0][0] = 50;
printf("2d array: %d!\n", twod_array[0][0]);
// Test for-loops
for (int counter = 0; counter < 10; counter++) {
if (counter < 5)
continue;
printf("for-counter: %d\n", counter);
}
// Test while-loops
int counter = 0;
while (1) {
if (counter > 10)
break;
printf("while-counter: %d\n", counter++);
}
short int sh = 123 + 5;
return (sh % 10) == 8;
int main() {
printf("10th fibonacci number is %d!", fibonacci(10));
return 0;
}