Compare commits
No commits in common. "main" and "typechecker" have entirely different histories.
main
...
typechecke
@ -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)
|
||||
|
||||
@ -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
|
||||
|
||||
129
src/ast.cpp
129
src/ast.cpp
@ -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
371
src/ast.h
@ -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;
|
||||
};
|
||||
}
|
||||
|
||||
392
src/binops.cpp
392
src/binops.cpp
@ -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 {};
|
||||
}
|
||||
}
|
||||
43
src/binops.h
43
src/binops.h
@ -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
|
||||
@ -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
|
||||
106
src/casting.cpp
106
src/casting.cpp
@ -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 {};
|
||||
|
||||
}
|
||||
}
|
||||
@ -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
|
||||
707
src/codegen.cpp
707
src/codegen.cpp
@ -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];
|
||||
// }
|
||||
}
|
||||
@ -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();
|
||||
};
|
||||
}
|
||||
|
||||
29
src/main.cpp
29
src/main.cpp
@ -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;
|
||||
|
||||
722
src/parsing.cpp
722
src/parsing.cpp
@ -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());
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -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
|
||||
@ -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&) {}
|
||||
}
|
||||
@ -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
|
||||
104
src/tokens.cpp
104
src/tokens.cpp
@ -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)) {
|
||||
|
||||
@ -16,13 +16,8 @@ namespace token {
|
||||
ReturnKeyword,
|
||||
IfKeyword,
|
||||
ElseKeyword,
|
||||
ForKeyword,
|
||||
WhileKeyword,
|
||||
BreakKeyword,
|
||||
ContinueKeyword,
|
||||
|
||||
Whitespace,
|
||||
Comment,
|
||||
|
||||
Eof,
|
||||
};
|
||||
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -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
|
||||
300
src/types.cpp
300
src/types.cpp
@ -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;
|
||||
}
|
||||
|
||||
100
src/types.h
100
src/types.h
@ -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
71
test.c
@ -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;
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user