Compare commits
66 Commits
typechecke
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
| 226118bcbb | |||
| 668e18e8ba | |||
| c3e12b087a | |||
| c4837ca9b0 | |||
| 63584525f2 | |||
| 28be145a70 | |||
| 53872373bf | |||
| efb4ce85ac | |||
| 8e6980715e | |||
| f9208a525d | |||
| 6dad36e34a | |||
| d79151380f | |||
| 238577e9ca | |||
| f0607a2310 | |||
| 21d17bb02d | |||
| a12cf52c48 | |||
| 9ed753a238 | |||
| 4b4d3ce14b | |||
| b664ca8f9d | |||
| 1265040f29 | |||
| 8e7facc593 | |||
| 94d1c15897 | |||
| f283149090 | |||
| 29fd757517 | |||
| 66653553a5 | |||
| bca18b4f1b | |||
| 5fc58ea1c5 | |||
| 55388bc6e3 | |||
| aa698c7ed6 | |||
| eacbac9205 | |||
| 5175878407 | |||
| de881d73d2 | |||
| e76540182f | |||
| b97aa3f212 | |||
| 6efcb23d6b | |||
| 45df4fdf5f | |||
| d3f0a730fd | |||
| 26779414a7 | |||
| c0a2c41c33 | |||
| 0400aa1d99 | |||
| d70f6fffa0 | |||
| 3370e76c48 | |||
| e797cab50f | |||
| fa8c4b0e74 | |||
| 0cdf1abf82 | |||
| be2d809986 | |||
| d3a4964f10 | |||
| 01d4999e14 | |||
| acd1e5d39b | |||
| d4016e3ab7 | |||
| 78c5ab4c25 | |||
| 48a3eb0e85 | |||
| 57dc022241 | |||
| 51c54e375a | |||
| 28483812ea | |||
| e0f2a1620e | |||
| ee0e30934a | |||
| 1555c12bbd | |||
| c8d4b653da | |||
| 1814ce2cc9 | |||
| fa01b22b81 | |||
| dab4d47b8e | |||
| e6a16ab667 | |||
| 810dd3595e | |||
| 2695a83ac8 | |||
| b541cf6baf |
@ -24,6 +24,7 @@ add_executable(${PROJECT_NAME}
|
||||
src/types.cpp
|
||||
src/typechecker.cpp
|
||||
src/binops.cpp
|
||||
src/casting.cpp
|
||||
)
|
||||
target_compile_features(${PROJECT_NAME} PRIVATE cxx_std_20)
|
||||
target_compile_options(${PROJECT_NAME} PRIVATE -Wall -Weffc++ -Wextra -Wpedantic -Werror)
|
||||
|
||||
@ -29,7 +29,11 @@ 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. **TODO:** Typechecking phase hasn't yet been developed, but it will go here.
|
||||
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).
|
||||
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
|
||||
|
||||
52
src/ast.cpp
52
src/ast.cpp
@ -42,6 +42,54 @@ 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 ExpressionStatement::formatted() {
|
||||
std::stringstream out{ "" };
|
||||
out << this->m_expr->formatted();
|
||||
@ -108,4 +156,8 @@ namespace AST {
|
||||
}
|
||||
return out.str();
|
||||
}
|
||||
|
||||
std::string TopLevelTypedef::formatted() {
|
||||
return this->m_ty->formatted();
|
||||
}
|
||||
}
|
||||
212
src/ast.h
212
src/ast.h
@ -25,7 +25,9 @@ namespace AST {
|
||||
public:
|
||||
Expression(token::Metadata meta) : Node{ meta } {}
|
||||
virtual codegen::StackValue codegen(codegen::Builder& builder, codegen::Scope& scope) = 0;
|
||||
virtual std::shared_ptr<types::Type> typecheck(
|
||||
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(
|
||||
typecheck::State& state,
|
||||
typecheck::Scope& scope,
|
||||
std::optional<std::shared_ptr<types::Type>> expected_ty
|
||||
@ -36,24 +38,36 @@ namespace AST {
|
||||
public:
|
||||
Statement(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;
|
||||
};
|
||||
|
||||
/// @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 } {}
|
||||
IntLiteralExpression(token::Metadata meta, int value)
|
||||
: Expression{ meta }
|
||||
, m_value{ value }
|
||||
, m_ty{ { std::shared_ptr<types::Type>{
|
||||
new types::FundamentalType{true, types::FundamentalTypeKind::Int}
|
||||
} } } {
|
||||
}
|
||||
virtual ~IntLiteralExpression() override = default;
|
||||
virtual std::string formatted() override;
|
||||
virtual codegen::StackValue codegen(codegen::Builder& builder, codegen::Scope& scope) override;
|
||||
virtual std::shared_ptr<types::Type> typecheck(
|
||||
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 i.e. "contents"
|
||||
class StringLiteralExpression : public Expression {
|
||||
private:
|
||||
std::string m_value;
|
||||
@ -62,13 +76,16 @@ namespace AST {
|
||||
virtual ~StringLiteralExpression() override = default;
|
||||
virtual std::string formatted() override;
|
||||
virtual codegen::StackValue codegen(codegen::Builder& builder, codegen::Scope& scope) override;
|
||||
virtual std::shared_ptr<types::Type> typecheck(
|
||||
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 Anything that references by value
|
||||
class ValueReferenceExpression : public Expression {
|
||||
private:
|
||||
std::string m_name;
|
||||
@ -77,13 +94,16 @@ namespace AST {
|
||||
virtual ~ValueReferenceExpression() override = default;
|
||||
virtual std::string formatted() override;
|
||||
virtual codegen::StackValue codegen(codegen::Builder& builder, codegen::Scope& scope) override;
|
||||
virtual std::shared_ptr<types::Type> typecheck(
|
||||
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 Any binary operation (i.e. lhs + rhs)
|
||||
class BinaryOperationExpression : public Expression {
|
||||
private:
|
||||
std::unique_ptr<Expression> m_lhs;
|
||||
@ -103,13 +123,16 @@ namespace AST {
|
||||
virtual ~BinaryOperationExpression() override = default;
|
||||
virtual std::string formatted() override;
|
||||
virtual codegen::StackValue codegen(codegen::Builder& builder, codegen::Scope& scope) override;
|
||||
virtual std::shared_ptr<types::Type> typecheck(
|
||||
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 fn()/// @brief
|
||||
class FunctionCallExpression : public Expression {
|
||||
private:
|
||||
std::unique_ptr<Expression> m_fn_expr;
|
||||
@ -126,7 +149,159 @@ namespace AST {
|
||||
virtual ~FunctionCallExpression() override = default;
|
||||
virtual std::string formatted() override;
|
||||
virtual codegen::StackValue codegen(codegen::Builder& builder, codegen::Scope& scope) override;
|
||||
virtual std::shared_ptr<types::Type> typecheck(
|
||||
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) 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) 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) 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) 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) 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) 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
|
||||
@ -144,6 +319,7 @@ namespace AST {
|
||||
virtual ~ReturnStatement() 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;
|
||||
};
|
||||
|
||||
@ -166,6 +342,7 @@ namespace AST {
|
||||
virtual ~InitializationStatement() 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;
|
||||
};
|
||||
|
||||
@ -179,6 +356,7 @@ namespace AST {
|
||||
virtual ~ExpressionStatement() 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;
|
||||
};
|
||||
|
||||
@ -200,6 +378,7 @@ namespace AST {
|
||||
virtual ~IfStatement() 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;
|
||||
};
|
||||
|
||||
@ -207,6 +386,7 @@ 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;
|
||||
};
|
||||
|
||||
@ -235,6 +415,24 @@ 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;
|
||||
};
|
||||
}
|
||||
|
||||
@ -6,38 +6,79 @@ namespace types {
|
||||
std::vector<BinopDefinition> definitions{};
|
||||
|
||||
auto int_ty = std::shared_ptr<types::Type>{
|
||||
new types::FundamentalType{ types::FundamentalTypeKind::Int } };
|
||||
new types::FundamentalType{ false, types::FundamentalTypeKind::Int } };
|
||||
auto char_ty = std::shared_ptr<types::Type>{
|
||||
new types::FundamentalType{ types::FundamentalTypeKind::Char } };
|
||||
new types::FundamentalType{ false, types::FundamentalTypeKind::Char } };
|
||||
auto bool_ty = std::shared_ptr<types::Type>{
|
||||
new types::FundamentalType{ types::FundamentalTypeKind::Bool } };
|
||||
new types::FundamentalType{ false, types::FundamentalTypeKind::Bool } };
|
||||
|
||||
// Integer arithmetic binops
|
||||
for (auto& ty : { int_ty, char_ty, bool_ty }) {
|
||||
// Arithmetic binops
|
||||
definitions.push_back(BinopDefinition{
|
||||
ty, types::BinOp::Add, ty,
|
||||
ty, [](codegen::Builder& builder, llvm::Value* lhs, llvm::Value* rhs) {
|
||||
[](codegen::Builder& builder, llvm::Value* lhs, llvm::Value* rhs) {
|
||||
return builder.builder->CreateAdd(lhs, rhs, "add");
|
||||
} });
|
||||
},
|
||||
[](BinopDefinition& def, std::shared_ptr<types::Type>, std::shared_ptr<types::Type>) {return def.lhs;}
|
||||
});
|
||||
|
||||
definitions.push_back(BinopDefinition{
|
||||
ty, types::BinOp::Sub, ty,
|
||||
ty, [](codegen::Builder& builder, llvm::Value* lhs, llvm::Value* rhs) {
|
||||
[](codegen::Builder& builder, llvm::Value* lhs, llvm::Value* rhs) {
|
||||
return builder.builder->CreateSub(lhs, rhs, "sub");
|
||||
} });
|
||||
},
|
||||
[](BinopDefinition& def, std::shared_ptr<types::Type>, std::shared_ptr<types::Type>) {return def.lhs;}
|
||||
});
|
||||
}
|
||||
|
||||
// Comparisons
|
||||
// Signed comparisons
|
||||
for (auto& ty : { int_ty }) {
|
||||
definitions.push_back(BinopDefinition{
|
||||
ty, types::BinOp::LessThan, ty,
|
||||
bool_ty, [](codegen::Builder& builder, llvm::Value* lhs, llvm::Value* rhs) {
|
||||
return builder.builder->CreateICmpSLE(lhs, rhs, "icmpsle");
|
||||
} });
|
||||
[](codegen::Builder& builder, llvm::Value* lhs, llvm::Value* rhs) {
|
||||
return builder.builder->CreateICmpSLT(lhs, rhs, "icmpslt");
|
||||
},
|
||||
[](BinopDefinition&, std::shared_ptr<types::Type>, std::shared_ptr<types::Type>) {
|
||||
return std::shared_ptr<types::Type>{
|
||||
new types::FundamentalType{ false, types::FundamentalTypeKind::Bool } };
|
||||
}
|
||||
});
|
||||
|
||||
definitions.push_back(BinopDefinition{
|
||||
ty, types::BinOp::GreaterThan, ty,
|
||||
bool_ty, [](codegen::Builder& builder, llvm::Value* lhs, llvm::Value* rhs) {
|
||||
[](codegen::Builder& builder, llvm::Value* lhs, llvm::Value* rhs) {
|
||||
return builder.builder->CreateICmpSGT(lhs, rhs, "icmpsgt");
|
||||
} });
|
||||
},
|
||||
[](BinopDefinition&, std::shared_ptr<types::Type>, std::shared_ptr<types::Type>) {
|
||||
return std::shared_ptr<types::Type>{
|
||||
new types::FundamentalType{ false, types::FundamentalTypeKind::Bool } };
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Unsigned comparisons
|
||||
for (auto& ty : { bool_ty, char_ty }) {
|
||||
definitions.push_back(BinopDefinition{
|
||||
ty, types::BinOp::LessThan, ty,
|
||||
[](codegen::Builder& builder, llvm::Value* lhs, llvm::Value* rhs) {
|
||||
return builder.builder->CreateICmpULT(lhs, rhs, "icmpslt");
|
||||
},
|
||||
[](BinopDefinition&, std::shared_ptr<types::Type>, std::shared_ptr<types::Type>) {
|
||||
return std::shared_ptr<types::Type>{
|
||||
new types::FundamentalType{ false, types::FundamentalTypeKind::Bool } };
|
||||
}
|
||||
});
|
||||
|
||||
definitions.push_back(BinopDefinition{
|
||||
ty, types::BinOp::GreaterThan, ty,
|
||||
[](codegen::Builder& builder, llvm::Value* lhs, llvm::Value* rhs) {
|
||||
return builder.builder->CreateICmpUGT(lhs, rhs, "icmpsgt");
|
||||
},
|
||||
[](BinopDefinition&, std::shared_ptr<types::Type>, std::shared_ptr<types::Type>) {
|
||||
return std::shared_ptr<types::Type>{
|
||||
new types::FundamentalType{ false, types::FundamentalTypeKind::Bool } };
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return definitions;
|
||||
|
||||
@ -22,8 +22,8 @@ namespace types {
|
||||
std::shared_ptr<Type> lhs;
|
||||
BinOp op;
|
||||
std::shared_ptr<Type> rhs;
|
||||
std::shared_ptr<Type> result;
|
||||
llvm::Value* (*codegen)(codegen::Builder& builder, llvm::Value* lhs, llvm::Value* rhs);
|
||||
std::shared_ptr<Type>(*result)(BinopDefinition& def, std::shared_ptr<Type> lhs, std::shared_ptr<Type> rhs);
|
||||
};
|
||||
|
||||
std::vector<BinopDefinition> create_binops();
|
||||
|
||||
@ -1,6 +1,8 @@
|
||||
#ifndef BUILDER_H
|
||||
#define BUILDER_H
|
||||
|
||||
#include <map>
|
||||
|
||||
#include <llvm/IR/LLVMContext.h>
|
||||
#include <llvm/IR/IRBuilder.h>
|
||||
|
||||
@ -11,6 +13,8 @@ namespace codegen {
|
||||
std::unique_ptr<llvm::IRBuilder<>> builder;
|
||||
llvm::BasicBlock* block;
|
||||
};
|
||||
|
||||
typedef std::map<std::string, llvm::Type*> TypeMap;
|
||||
}
|
||||
|
||||
#endif
|
||||
77
src/casting.cpp
Normal file
77
src/casting.cpp
Normal file
@ -0,0 +1,77 @@
|
||||
|
||||
#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 char_ty = std::shared_ptr<Type>{
|
||||
new FundamentalType{ false, FundamentalTypeKind::Char } };
|
||||
auto bool_ty = std::shared_ptr<Type>{
|
||||
new FundamentalType{ false, FundamentalTypeKind::Bool } };
|
||||
|
||||
auto numerical_types = { int_ty, char_ty, bool_ty };
|
||||
|
||||
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 {};
|
||||
|
||||
}
|
||||
}
|
||||
23
src/casting.h
Normal file
23
src/casting.h
Normal file
@ -0,0 +1,23 @@
|
||||
#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
|
||||
487
src/codegen.cpp
487
src/codegen.cpp
@ -11,32 +11,71 @@
|
||||
|
||||
namespace codegen {
|
||||
Scope Scope::with_lvalue() {
|
||||
return Scope{ this->binops, this->values, true };
|
||||
return Scope{ this->binops, this->casts, this->structs, this->values, true };
|
||||
}
|
||||
}
|
||||
|
||||
namespace AST {
|
||||
codegen::StackValue IntLiteralExpression::codegen(codegen::Builder& builder, codegen::Scope&) {
|
||||
auto ty = builder.builder->getInt32Ty();
|
||||
std::shared_ptr<types::Type> IntLiteralExpression::get_codegen_type(codegen::Scope&) {
|
||||
return this->m_ty;
|
||||
}
|
||||
|
||||
auto stack_type = new types::FundamentalType{ types::FundamentalTypeKind::Int };
|
||||
codegen::StackValue IntLiteralExpression::codegen(codegen::Builder& builder, codegen::Scope& scope) {
|
||||
auto ty = this->m_ty->codegen(builder, scope.structs);
|
||||
|
||||
return codegen::StackValue{
|
||||
llvm::ConstantInt::get(ty, this->m_value),
|
||||
std::unique_ptr<types::Type>{stack_type}
|
||||
this->m_ty,
|
||||
};
|
||||
}
|
||||
|
||||
codegen::StackValue StringLiteralExpression::codegen(codegen::Builder& builder, codegen::Scope&) {
|
||||
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};
|
||||
}
|
||||
|
||||
auto stack_type = new types::PointerType{ std::make_unique<types::FundamentalType>(types::FundamentalTypeKind::Char) };
|
||||
codegen::StackValue StringLiteralExpression::codegen(codegen::Builder& builder, codegen::Scope& 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
|
||||
};
|
||||
|
||||
auto str = llvm::StringRef{ this->m_value.c_str() };
|
||||
auto global_str = builder.builder->CreateGlobalString(str);
|
||||
|
||||
return codegen::StackValue{
|
||||
builder.builder->CreateGlobalString(str),
|
||||
std::unique_ptr<types::Type>{stack_type},
|
||||
};
|
||||
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},
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
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) {
|
||||
@ -46,7 +85,7 @@ namespace AST {
|
||||
return value->second;
|
||||
}
|
||||
else {
|
||||
auto loaded = value->second.ty->load(builder, value->second.value);
|
||||
auto loaded = value->second.ty->load(builder, value->second.value, scope.structs);
|
||||
return codegen::StackValue{
|
||||
loaded.first,
|
||||
loaded.second
|
||||
@ -58,6 +97,30 @@ 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);
|
||||
try {
|
||||
switch (this->m_binop) {
|
||||
case types::BinOp::Assignment:
|
||||
return rhs;
|
||||
default:
|
||||
auto binop = types::find_binop(
|
||||
scope.binops,
|
||||
lhs,
|
||||
this->m_binop,
|
||||
rhs);
|
||||
if (binop) {
|
||||
return binop->result(*binop, lhs, rhs);
|
||||
}
|
||||
throw CompileError("invalid binop", this->m_meta);
|
||||
}
|
||||
}
|
||||
catch (std::runtime_error& error) {
|
||||
throw CompileError(error.what(), this->m_meta);
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
@ -76,7 +139,7 @@ namespace AST {
|
||||
if (binop) {
|
||||
return codegen::StackValue{
|
||||
binop->codegen(builder, lhs.value, rhs.value),
|
||||
binop->result
|
||||
binop->result(*binop, lhs.ty, rhs.ty)
|
||||
};
|
||||
}
|
||||
throw CompileError("invalid binop", this->m_meta);
|
||||
@ -87,6 +150,11 @@ namespace AST {
|
||||
}
|
||||
}
|
||||
|
||||
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) {
|
||||
std::vector<llvm::Value*> args{};
|
||||
for (auto& arg : this->m_args) {
|
||||
@ -94,13 +162,294 @@ namespace AST {
|
||||
}
|
||||
auto function = this->m_fn_expr->codegen(builder, scope);
|
||||
|
||||
auto value = builder.builder->CreateCall(llvm::dyn_cast<llvm::FunctionType>(function.ty->codegen(builder)), function.value, args, "call");
|
||||
auto value = builder.builder->CreateCall(llvm::dyn_cast<llvm::FunctionType>(function.ty->codegen(builder, scope.structs)), 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) {
|
||||
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);
|
||||
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);
|
||||
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) {
|
||||
auto with_lvalue = scope.with_lvalue();
|
||||
return this->m_expr->codegen(builder, with_lvalue);
|
||||
}
|
||||
|
||||
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) {
|
||||
auto value = this->m_expr->codegen(builder, scope);
|
||||
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) {
|
||||
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);
|
||||
}
|
||||
else {
|
||||
value = this->m_expr->codegen(builder, scope);
|
||||
}
|
||||
|
||||
std::cout << intended_ty->formatted() << std::endl;
|
||||
|
||||
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) {
|
||||
auto lvalued = scope.with_lvalue();
|
||||
auto struct_ptr = this->m_expr->codegen(builder, lvalued);
|
||||
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);
|
||||
|
||||
|
||||
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) {
|
||||
auto value_ptr = builder.builder->CreateAlloca(this->m_ty->codegen(builder, scope.structs));
|
||||
|
||||
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).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).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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void ReturnStatement::codegen(codegen::Builder& builder, codegen::Scope& scope) {
|
||||
if (!builder.block)
|
||||
return;
|
||||
@ -125,14 +474,30 @@ namespace AST {
|
||||
|
||||
builder.builder->SetInsertPoint(builder.block);
|
||||
|
||||
auto ty = this->m_type->codegen(builder);
|
||||
if (this->m_type->m_kind == types::TypeKind::Array) {
|
||||
auto raw_llvm_ty = this->m_type->codegen(builder, scope.structs);
|
||||
auto ptr = builder.builder->CreateAlloca(raw_llvm_ty);
|
||||
if (this->m_expr.has_value()) {
|
||||
auto value = this->m_expr->get()->codegen(builder, scope);
|
||||
builder.builder->CreateStore(value.value, ptr, false);
|
||||
}
|
||||
|
||||
scope.values[this->m_name] = codegen::StackValue{ ptr, this->m_type };
|
||||
return;
|
||||
}
|
||||
|
||||
auto ty = this->m_type->codegen(builder, scope.structs);
|
||||
auto ptr = builder.builder->CreateAlloca(ty);
|
||||
if (this->m_expr.has_value()) {
|
||||
auto value = this->m_expr->get()->codegen(builder, scope);
|
||||
builder.builder->CreateStore(value.value, ptr, false);
|
||||
}
|
||||
|
||||
scope.values[this->m_name] = codegen::StackValue{ ptr, this->m_type };
|
||||
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 };
|
||||
}
|
||||
|
||||
void IfStatement::codegen(codegen::Builder& builder, codegen::Scope& scope) {
|
||||
@ -179,9 +544,11 @@ namespace AST {
|
||||
param_ty_ptrs.push_back(param.second);
|
||||
}
|
||||
|
||||
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_ptr = std::shared_ptr<types::Type>{
|
||||
new types::FunctionType{ true, ret_ty_ptr, param_ty_ptrs, this->m_is_vararg }
|
||||
};
|
||||
|
||||
auto fn_ty = fn_ty_ptr->codegen(builder);
|
||||
auto fn_ty = fn_ty_ptr->codegen(builder, scope.structs);
|
||||
auto function = llvm::Function::Create(
|
||||
llvm::dyn_cast<llvm::FunctionType>(fn_ty),
|
||||
llvm::GlobalValue::LinkageTypes::ExternalLinkage,
|
||||
@ -199,12 +566,32 @@ namespace AST {
|
||||
|
||||
int counter = 0;
|
||||
for (auto& param : this->m_params) {
|
||||
auto param_ty_ptr = param_ty_ptrs[counter];
|
||||
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 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,
|
||||
arg_ptr,
|
||||
param_ty_ptr,
|
||||
};
|
||||
}
|
||||
@ -215,14 +602,26 @@ namespace AST {
|
||||
}
|
||||
}
|
||||
|
||||
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) {
|
||||
llvm::Type* FundamentalType::codegen(codegen::Builder& builder, codegen::TypeMap&) {
|
||||
switch (this->m_ty) {
|
||||
case FundamentalTypeKind::Int:
|
||||
return builder.builder->getInt32Ty();
|
||||
@ -237,19 +636,57 @@ namespace types {
|
||||
}
|
||||
}
|
||||
|
||||
llvm::Type* FunctionType::codegen(codegen::Builder& builder) {
|
||||
llvm::Type* FunctionType::codegen(codegen::Builder& builder, codegen::TypeMap& structs) {
|
||||
std::vector<llvm::Type*> params{};
|
||||
|
||||
for (auto& param : this->m_param_tys) {
|
||||
params.push_back(param->codegen(builder));
|
||||
params.push_back(param->codegen(builder, structs));
|
||||
}
|
||||
|
||||
auto ret_ty = this->m_ret_ty->codegen(builder);
|
||||
auto ret_ty = this->m_ret_ty->codegen(builder, structs);
|
||||
|
||||
return llvm::FunctionType::get(ret_ty, params, this->m_vararg);
|
||||
}
|
||||
|
||||
llvm::Type* PointerType::codegen(codegen::Builder& builder) {
|
||||
llvm::Type* PointerType::codegen(codegen::Builder& builder, codegen::TypeMap&) {
|
||||
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,6 +9,7 @@
|
||||
#include "builder.h"
|
||||
#include "types.h"
|
||||
#include "binops.h"
|
||||
#include "casting.h"
|
||||
#include "tokens.h"
|
||||
|
||||
namespace codegen {
|
||||
@ -19,8 +20,11 @@ namespace codegen {
|
||||
|
||||
struct Scope {
|
||||
std::vector<types::BinopDefinition>& binops;
|
||||
std::vector<types::CastDefinition>& casts;
|
||||
|
||||
TypeMap structs;
|
||||
std::map<std::string, StackValue> values;
|
||||
|
||||
bool is_lvalue;
|
||||
|
||||
Scope with_lvalue();
|
||||
|
||||
25
src/main.cpp
25
src/main.cpp
@ -70,12 +70,14 @@ 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);
|
||||
auto statement = parsing::parse_top_level_statement(stream, parse_scope);
|
||||
while (statement.ok()) {
|
||||
statements.push_back(statement.unwrap());
|
||||
statement = parsing::parse_top_level_statement(stream);
|
||||
statement = parsing::parse_top_level_statement(stream, parse_scope);
|
||||
}
|
||||
if (stream.peek().type != token::Type::Eof) {
|
||||
std::cerr << statement.unwrap_err() << std::endl;
|
||||
@ -97,15 +99,27 @@ 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.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) {
|
||||
@ -116,13 +130,16 @@ std::optional<CompileOutput> compile(std::string_view in_filename) {
|
||||
return {};
|
||||
}
|
||||
|
||||
// Compile parsed output
|
||||
|
||||
codegen::Scope cg_scope{
|
||||
.binops = typecheck_state.binops,
|
||||
.casts = typecheck_state.casts,
|
||||
.structs = {},
|
||||
.values = {},
|
||||
.is_lvalue = false,
|
||||
};
|
||||
|
||||
// Compile parsed output
|
||||
try {
|
||||
for (auto& tls : statements) {
|
||||
std::cout << tls->formatted() << std::endl;
|
||||
@ -159,6 +176,8 @@ 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;
|
||||
|
||||
443
src/parsing.cpp
443
src/parsing.cpp
@ -5,37 +5,154 @@
|
||||
|
||||
namespace parsing {
|
||||
namespace {
|
||||
Result<std::unique_ptr<AST::Expression>, std::string> parse_expression(token::TokenStream& stream);
|
||||
static uint32_t struct_id_counter = 0;
|
||||
|
||||
Result<std::shared_ptr<types::Type>, std::string> parse_type(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) {
|
||||
token::TokenStream inner{ stream };
|
||||
try {
|
||||
auto token = inner.expect(token::Type::Ident);
|
||||
bool is_const = false;
|
||||
if (inner.peek().type == token::Type::Ident && inner.peek().content == "const") {
|
||||
is_const = true;
|
||||
inner.next();
|
||||
}
|
||||
|
||||
// TODO eventually make this be potentially more than one word
|
||||
std::string type_name = token.content;
|
||||
auto token = inner.expect(token::Type::Ident);
|
||||
|
||||
std::shared_ptr<types::Type> returned{};
|
||||
|
||||
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 };
|
||||
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 };
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
throw std::runtime_error("Expected type name, got " + type_name);
|
||||
// TODO eventually make this be potentially more than one word
|
||||
std::string type_name = token.content;
|
||||
|
||||
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 == "char") {
|
||||
auto ty = new types::FundamentalType{ is_const, types::FundamentalTypeKind::Char };
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
while (inner.peek().type == token::Type::Symbol && inner.peek().content == "*") {
|
||||
inner.next();
|
||||
auto ty = new types::PointerType{ std::move(returned) };
|
||||
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) };
|
||||
returned = std::shared_ptr<types::Type>{ ty };
|
||||
}
|
||||
|
||||
@ -49,9 +166,71 @@ namespace parsing {
|
||||
}
|
||||
}
|
||||
|
||||
Result<std::unique_ptr<AST::Expression>, std::string> parse_plain_expression(token::TokenStream& stream) {
|
||||
Result<std::optional<uint32_t>, std::string> parse_array_postfix(token::TokenStream& stream, bool allow_empty, Scope&) {
|
||||
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;
|
||||
@ -80,27 +259,95 @@ namespace parsing {
|
||||
}
|
||||
}
|
||||
|
||||
Result<std::unique_ptr<AST::Expression>, std::string> parse_primary_expression(token::TokenStream& stream) {
|
||||
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) {
|
||||
token::TokenStream inner{ stream };
|
||||
try {
|
||||
auto before_meta = inner.metadata();
|
||||
auto plain_expr = parse_plain_expression(inner);
|
||||
while (inner.peek().content == "(") {
|
||||
|
||||
|
||||
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 == "(") {
|
||||
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).unwrap());
|
||||
}
|
||||
|
||||
auto expr = parse_expression(inner, scope).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))
|
||||
};
|
||||
}
|
||||
|
||||
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 };
|
||||
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}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -112,7 +359,7 @@ namespace parsing {
|
||||
}
|
||||
}
|
||||
|
||||
Result<types::BinOp, std::string> parse_binop(token::TokenStream& stream) {
|
||||
Result<types::BinOp, std::string> parse_binop(token::TokenStream& stream, Scope&) {
|
||||
token::TokenStream inner{ stream };
|
||||
try {
|
||||
auto token = inner.next();
|
||||
@ -148,32 +395,32 @@ namespace parsing {
|
||||
}
|
||||
|
||||
std::unique_ptr<AST::Expression> parse_rhs(
|
||||
token::TokenStream& stream, std::unique_ptr<AST::Expression> lhs, int prev_precedence) {
|
||||
token::TokenStream& stream, std::unique_ptr<AST::Expression> lhs, int prev_precedence, Scope& scope) {
|
||||
|
||||
auto before = stream.metadata();
|
||||
|
||||
auto binop_res = parse_binop(stream);
|
||||
auto binop_res = parse_binop(stream, scope);
|
||||
while (binop_res.ok()) {
|
||||
auto binop = binop_res.unwrap();
|
||||
auto rhs = parse_primary_expression(stream).unwrap();
|
||||
auto rhs = parse_primary_expression(stream, scope).unwrap();
|
||||
|
||||
if (types::operator_precedence(binop) > prev_precedence) {
|
||||
rhs = parse_rhs(stream, std::move(rhs), types::operator_precedence(binop));
|
||||
rhs = parse_rhs(stream, std::move(rhs), types::operator_precedence(binop), scope);
|
||||
}
|
||||
|
||||
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);
|
||||
binop_res = parse_binop(stream, scope);
|
||||
}
|
||||
|
||||
return lhs;
|
||||
}
|
||||
|
||||
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) {
|
||||
try {
|
||||
auto lhs = parse_primary_expression(stream).unwrap();
|
||||
return std::unique_ptr{ parse_rhs(stream, std::move(lhs), 0) };
|
||||
auto lhs = parse_primary_expression(stream, scope).unwrap();
|
||||
return std::unique_ptr{ parse_rhs(stream, std::move(lhs), 0, scope) };
|
||||
}
|
||||
catch (std::runtime_error& error) {
|
||||
return std::string{ error.what() };
|
||||
@ -182,19 +429,28 @@ namespace parsing {
|
||||
|
||||
|
||||
|
||||
Result<std::unique_ptr<AST::InitializationStatement>, std::string> parse_init_statement(token::TokenStream& stream) {
|
||||
Result<std::unique_ptr<AST::InitializationStatement>, std::string> parse_init_statement(token::TokenStream& stream, Scope& scope) {
|
||||
token::TokenStream inner{ stream };
|
||||
|
||||
auto before_meta = inner.metadata();
|
||||
|
||||
try {
|
||||
auto ty = parse_type(inner).unwrap();
|
||||
auto ty = parse_type(inner, scope).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).unwrap();
|
||||
expr = parse_expression(inner, scope).unwrap();
|
||||
}
|
||||
|
||||
inner.expect(token::Type::Symbol, ";");
|
||||
@ -208,13 +464,13 @@ namespace parsing {
|
||||
}
|
||||
}
|
||||
|
||||
Result<std::unique_ptr<AST::Statement>, std::string> parse_statement(token::TokenStream& stream) {
|
||||
Result<std::unique_ptr<AST::Statement>, std::string> parse_statement(token::TokenStream& stream, Scope& scope) {
|
||||
token::TokenStream inner{ stream };
|
||||
auto before_meta = inner.metadata();
|
||||
try {
|
||||
if (inner.peek().type == token::Type::ReturnKeyword) {
|
||||
inner.next();
|
||||
auto expression = parse_expression(inner).unwrap();
|
||||
auto expression = parse_expression(inner, scope).unwrap();
|
||||
inner.expect(token::Type::Symbol, ";");
|
||||
|
||||
stream.m_position = inner.m_position;
|
||||
@ -225,14 +481,14 @@ namespace parsing {
|
||||
else if (inner.peek().type == token::Type::IfKeyword) {
|
||||
inner.next();
|
||||
inner.expect(token::Type::Symbol, "(");
|
||||
auto expression = parse_expression(inner).unwrap();
|
||||
auto expression = parse_expression(inner, scope).unwrap();
|
||||
inner.expect(token::Type::Symbol, ")");
|
||||
|
||||
auto then_statement = parse_statement(inner).unwrap();
|
||||
auto then_statement = parse_statement(inner, scope).unwrap();
|
||||
std::optional<std::unique_ptr<AST::Statement>> else_statement{};
|
||||
if (inner.peek().type == token::Type::ElseKeyword) {
|
||||
inner.next();
|
||||
else_statement = parse_statement(inner).unwrap();
|
||||
else_statement = parse_statement(inner, scope).unwrap();
|
||||
}
|
||||
|
||||
stream.m_position = inner.m_position;
|
||||
@ -245,11 +501,11 @@ namespace parsing {
|
||||
};
|
||||
return std::unique_ptr<AST::Statement>{ statement };
|
||||
}
|
||||
else if (auto init = parse_init_statement(inner); init.ok()) {
|
||||
else if (auto init = parse_init_statement(inner, scope); init.ok()) {
|
||||
stream.m_position = inner.m_position;
|
||||
return std::unique_ptr<AST::Statement>{ init.unwrap() };
|
||||
}
|
||||
else if (auto expr = parse_expression(inner); expr.ok()) {
|
||||
else if (auto expr = parse_expression(inner, scope); 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() };
|
||||
@ -266,11 +522,11 @@ namespace parsing {
|
||||
}
|
||||
}
|
||||
|
||||
Result<std::unique_ptr<AST::TopLevelStatement>, std::string> parse_top_level_statement(token::TokenStream& stream) {
|
||||
Result<std::unique_ptr<AST::TopLevelStatement>, std::string> parse_function(token::TokenStream& stream, Scope& scope) {
|
||||
token::TokenStream inner{ stream };
|
||||
auto before_meta = inner.metadata();
|
||||
try {
|
||||
auto type = parse_type(inner).unwrap();
|
||||
auto type = parse_type(inner, scope).unwrap();
|
||||
auto name_token = inner.expect(token::Type::Ident);
|
||||
inner.expect(token::Type::Symbol, "(");
|
||||
|
||||
@ -289,26 +545,45 @@ namespace parsing {
|
||||
break;
|
||||
}
|
||||
|
||||
auto param_ty = parse_type(inner).unwrap();
|
||||
auto param_ty = parse_type(inner, scope).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);
|
||||
auto statement = parse_statement(inner, inner_scope);
|
||||
while (statement.ok()) {
|
||||
statement_list.push_back(statement.unwrap());
|
||||
statement = parse_statement(inner);
|
||||
statement = parse_statement(inner, inner_scope);
|
||||
}
|
||||
|
||||
statements = std::optional{ std::move(statement_list) };
|
||||
@ -334,4 +609,56 @@ 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,12 +1,19 @@
|
||||
#ifndef PARSING_H
|
||||
#define PARSING_H
|
||||
|
||||
#include <map>
|
||||
|
||||
#include "ast.h"
|
||||
#include "result.h"
|
||||
#include "tokens.h"
|
||||
|
||||
namespace parsing {
|
||||
Result<std::unique_ptr<AST::TopLevelStatement>, std::string> parse_top_level_statement(token::TokenStream& stream);
|
||||
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);
|
||||
}
|
||||
|
||||
#endif
|
||||
@ -5,7 +5,7 @@
|
||||
#include <vector>
|
||||
#include <iostream>
|
||||
#include <sstream>
|
||||
|
||||
#include <optional>
|
||||
|
||||
static bool iswhitespace(char& character) {
|
||||
return character == ' '
|
||||
@ -14,6 +14,33 @@ 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) {
|
||||
@ -152,20 +179,38 @@ namespace token {
|
||||
std::string content{};
|
||||
c = text[++i]; // Skip initial "
|
||||
do {
|
||||
content += c;
|
||||
if ((i + 1) >= text_length) break;
|
||||
c = text[++i];
|
||||
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];
|
||||
}
|
||||
} while (c != '\"');
|
||||
i++; // Skip second "
|
||||
tokens.push_back(token::Token{ token::Type::LiteralStr, content, meta + (content.size() + 2) });
|
||||
}
|
||||
else if (std::isalpha(c)) {
|
||||
else if (std::isalpha(c) || c == '_') {
|
||||
std::string content{};
|
||||
do {
|
||||
content += c;
|
||||
if ((i + 1) >= text_length) break;
|
||||
c = text[++i];
|
||||
} while (std::isalnum(c));
|
||||
} while (std::isalnum(c) || c == '_');
|
||||
|
||||
token::Type type = token::Type::Ident;
|
||||
if (content == "return") {
|
||||
|
||||
@ -5,14 +5,31 @@
|
||||
#include "result.h"
|
||||
|
||||
namespace {
|
||||
enum class TypecheckRes {
|
||||
enum class TypecheckResKind {
|
||||
Ok,
|
||||
Castable,
|
||||
};
|
||||
|
||||
Result<TypecheckRes, std::string> check_type(std::shared_ptr<types::Type> checked, std::shared_ptr<types::Type> target) {
|
||||
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);
|
||||
|
||||
|
||||
if (types::types_equal(checked, target)) {
|
||||
return TypecheckRes::Ok;
|
||||
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 std::string{ "Types " + checked->formatted() + " and " + target->formatted() + " incompatible" };
|
||||
@ -24,12 +41,13 @@ namespace {
|
||||
typecheck::State& state) {
|
||||
if (res.ok()) {
|
||||
auto result = res.unwrap();
|
||||
if (result == TypecheckRes::Ok) {
|
||||
if (result.kind == TypecheckResKind::Ok) {
|
||||
return expr;
|
||||
}
|
||||
else {
|
||||
state.errors.push_back(CompileError("Casting not yet implemented", expr->m_meta));
|
||||
return expr;
|
||||
return std::unique_ptr<AST::Expression> {
|
||||
new AST::CastExpression{ expr->m_meta, result.result, std::move(expr) }
|
||||
};
|
||||
}
|
||||
}
|
||||
else {
|
||||
@ -37,58 +55,140 @@ 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 {
|
||||
std::shared_ptr<types::Type> IntLiteralExpression::typecheck(
|
||||
typecheck::State&,
|
||||
typecheck::Scope&,
|
||||
std::optional<std::shared_ptr<types::Type>>
|
||||
) {
|
||||
return std::shared_ptr<types::Type>{
|
||||
new types::FundamentalType{ types::FundamentalTypeKind::Int }
|
||||
};
|
||||
|
||||
void IntLiteralExpression::typecheck_preprocess(typecheck::Scope& scope) {
|
||||
this->m_ty = refresh_type(scope, this->m_ty);
|
||||
}
|
||||
|
||||
std::shared_ptr<types::Type> StringLiteralExpression::typecheck(
|
||||
typecheck::ExpressionType IntLiteralExpression::typecheck(
|
||||
typecheck::State&,
|
||||
typecheck::Scope&,
|
||||
std::optional<std::shared_ptr<types::Type>> expected_ty
|
||||
) {
|
||||
// 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::Int
|
||||
) {
|
||||
this->m_ty = *expected_ty;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return { this->m_ty, false, false };
|
||||
}
|
||||
|
||||
void StringLiteralExpression::typecheck_preprocess(typecheck::Scope&) {}
|
||||
|
||||
typecheck::ExpressionType StringLiteralExpression::typecheck(
|
||||
typecheck::State&,
|
||||
typecheck::Scope&,
|
||||
std::optional<std::shared_ptr<types::Type>>
|
||||
) {
|
||||
auto char_ty = std::shared_ptr<types::Type>{
|
||||
new types::FundamentalType{ types::FundamentalTypeKind::Char }
|
||||
new types::FundamentalType{ true, types::FundamentalTypeKind::Char }
|
||||
};
|
||||
auto ptr_ty = new types::PointerType{ char_ty };
|
||||
return std::shared_ptr<types::Type>{ptr_ty};
|
||||
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 };
|
||||
}
|
||||
|
||||
std::shared_ptr<types::Type> ValueReferenceExpression::typecheck(
|
||||
void ValueReferenceExpression::typecheck_preprocess(typecheck::Scope&) {}
|
||||
|
||||
typecheck::ExpressionType 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];
|
||||
return { scope.symbols[this->m_name], false, true };
|
||||
}
|
||||
|
||||
state.errors.push_back(CompileError("Value " + this->m_name + " not defined", this->m_meta));
|
||||
return std::shared_ptr<types::Type>{
|
||||
new types::FundamentalType{ types::FundamentalTypeKind::Void }
|
||||
};
|
||||
return { std::shared_ptr<types::Type>{
|
||||
new types::FundamentalType{ false, types::FundamentalTypeKind::Void }
|
||||
}, false, true };
|
||||
}
|
||||
|
||||
std::shared_ptr<types::Type> BinaryOperationExpression::typecheck(
|
||||
void BinaryOperationExpression::typecheck_preprocess(typecheck::Scope& scope) {
|
||||
this->m_lhs->typecheck_preprocess(scope);
|
||||
this->m_rhs->typecheck_preprocess(scope);
|
||||
}
|
||||
|
||||
typecheck::ExpressionType BinaryOperationExpression::typecheck(
|
||||
typecheck::State& state,
|
||||
typecheck::Scope& scope,
|
||||
std::optional<std::shared_ptr<types::Type>>
|
||||
std::optional<std::shared_ptr<types::Type>> expected_ty
|
||||
) {
|
||||
auto lhs_ty = this->m_lhs->typecheck(state, scope, {});
|
||||
auto rhs_ty = this->m_rhs->typecheck(state, scope, {});
|
||||
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;
|
||||
|
||||
if (this->m_binop == types::BinOp::Assignment) {
|
||||
return lhs_ty;
|
||||
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));
|
||||
}
|
||||
|
||||
// Re-typecheck rhs to actually match lhs
|
||||
auto rhs_ty = this->m_rhs->typecheck(state, scope, lhs_ty).type;
|
||||
auto rhs_ty_res = check_type(state, rhs_ty, lhs_ty);
|
||||
this->m_rhs = handle_res(std::move(this->m_rhs), rhs_ty_res, state);
|
||||
return { lhs_ty, false, false };
|
||||
}
|
||||
|
||||
// Try to find a binop that matches exactly
|
||||
auto binop = types::find_binop(
|
||||
state.binops,
|
||||
lhs_ty,
|
||||
@ -97,11 +197,56 @@ namespace AST {
|
||||
);
|
||||
|
||||
if (binop) {
|
||||
return binop->result;
|
||||
return { binop->result(*binop, lhs_ty, rhs_ty), false, false };
|
||||
}
|
||||
|
||||
// TODO check for binops that may be implicitly castable
|
||||
// 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 (expected_ty) {
|
||||
// Skip any binops that would not be immediately assignable to
|
||||
// the expected type
|
||||
if (!types::types_equal(binop.result(binop, lhs_ty, rhs_ty), *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;
|
||||
this->m_rhs = handle_res(std::move(this->m_rhs), rhs_res, state);
|
||||
return { binop.result(binop, lhs_ty, rhs_ty), false, false };
|
||||
}
|
||||
else if (types::types_equal(binop.rhs, rhs_ty)) {
|
||||
auto lhs_res = check_type(state, lhs_ty, binop.lhs);
|
||||
if (!lhs_res.ok())
|
||||
// Skip if not implicitly castable to rhs
|
||||
continue;
|
||||
this->m_lhs = handle_res(std::move(this->m_lhs), lhs_res, state);
|
||||
return { binop.result(binop, lhs_ty, rhs_ty), false, false };
|
||||
}
|
||||
}
|
||||
|
||||
// Finally check for any binop that allows the result to be implicitly
|
||||
// casted to the result
|
||||
for (auto& binop : state.binops) {
|
||||
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(binop, lhs_ty, rhs_ty), *expected_ty);
|
||||
if (!result_res.ok())
|
||||
continue;
|
||||
}
|
||||
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(binop, lhs_ty, rhs_ty), false, false };
|
||||
}
|
||||
|
||||
// No suitable binops found :(
|
||||
state.errors.push_back(CompileError(
|
||||
"No suitable binop between "
|
||||
+ lhs_ty->formatted() + " "
|
||||
@ -109,22 +254,29 @@ namespace AST {
|
||||
+ rhs_ty->formatted(),
|
||||
this->m_meta));
|
||||
|
||||
return std::shared_ptr<types::Type>{
|
||||
new types::FundamentalType{ types::FundamentalTypeKind::Void } };
|
||||
return { std::shared_ptr<types::Type>{
|
||||
new types::FundamentalType{ false, types::FundamentalTypeKind::Void } }, false, false };
|
||||
}
|
||||
|
||||
std::shared_ptr<types::Type> FunctionCallExpression::typecheck(
|
||||
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(
|
||||
typecheck::State& state,
|
||||
typecheck::Scope& scope,
|
||||
std::optional<std::shared_ptr<types::Type>>
|
||||
) {
|
||||
auto expr_ty = this->m_fn_expr->typecheck(state, scope, {});
|
||||
auto expr_ty = this->m_fn_expr->typecheck(state, scope, {}).type;
|
||||
|
||||
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{ types::FundamentalTypeKind::Void }
|
||||
};
|
||||
return { std::shared_ptr<types::Type> {
|
||||
new types::FundamentalType{ false, types::FundamentalTypeKind::Void }
|
||||
}, false, false };
|
||||
}
|
||||
|
||||
auto fn_ty = dynamic_cast<types::FunctionType*>(expr_ty.get());
|
||||
@ -139,9 +291,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);
|
||||
auto param_ty = this->m_args[i]->typecheck(state, scope, expected_param_ty).type;
|
||||
|
||||
auto check_res = check_type(param_ty, expected_param_ty);
|
||||
auto check_res = check_type(state, param_ty, expected_param_ty);
|
||||
this->m_args[i] = handle_res(std::move(this->m_args[i]), check_res, state);
|
||||
}
|
||||
else {
|
||||
@ -150,34 +302,294 @@ namespace AST {
|
||||
}
|
||||
}
|
||||
|
||||
return fn_ty->m_ret_ty;
|
||||
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 ReturnStatement::typecheck_preprocess(typecheck::Scope& scope) {
|
||||
this->m_expr->typecheck_preprocess(scope);
|
||||
}
|
||||
|
||||
void ReturnStatement::typecheck(typecheck::State& state, typecheck::Scope& scope) {
|
||||
auto res_ty = this->m_expr->typecheck(state, scope, scope.return_ty);
|
||||
auto res_ty = this->m_expr->typecheck(state, scope, scope.return_ty).type;
|
||||
if (scope.return_ty) {
|
||||
auto check_res = check_type(res_ty, *scope.return_ty);
|
||||
auto check_res = check_type(state, 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) {
|
||||
(*this->m_expr)->typecheck(state, scope, this->m_type);
|
||||
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);
|
||||
}
|
||||
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{ types::FundamentalTypeKind::Bool } };
|
||||
auto expr_ty = this->m_condition->typecheck(state, scope, bool_ty);
|
||||
new types::FundamentalType{ false, types::FundamentalTypeKind::Bool } };
|
||||
auto expr_ty = this->m_condition->typecheck(state, scope, bool_ty).type;
|
||||
|
||||
auto check_res = check_type(expr_ty, bool_ty);
|
||||
auto check_res = check_type(state, expr_ty, bool_ty);
|
||||
this->m_condition = handle_res(std::move(this->m_condition), check_res, state);
|
||||
|
||||
this->m_then->typecheck(state, scope);
|
||||
@ -186,6 +598,19 @@ namespace AST {
|
||||
}
|
||||
}
|
||||
|
||||
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{};
|
||||
@ -193,7 +618,7 @@ namespace AST {
|
||||
param_tys.push_back(param.second);
|
||||
}
|
||||
|
||||
auto function_ty = new types::FunctionType{ return_ty, param_tys, this->m_is_vararg };
|
||||
auto function_ty = new types::FunctionType{ true, return_ty, param_tys, this->m_is_vararg };
|
||||
scope.symbols[this->m_name] = std::shared_ptr<types::Type>{ function_ty };
|
||||
|
||||
typecheck::Scope inner{ scope };
|
||||
@ -211,4 +636,28 @@ 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,18 +5,27 @@
|
||||
|
||||
#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;
|
||||
};
|
||||
|
||||
struct State {
|
||||
std::vector<types::BinopDefinition> binops;
|
||||
std::vector<types::CastDefinition> casts;
|
||||
std::vector<CompileError> errors;
|
||||
};
|
||||
|
||||
struct ExpressionType {
|
||||
std::shared_ptr<types::Type> type;
|
||||
bool array_initializer;
|
||||
bool lvalue;
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
||||
190
src/types.cpp
190
src/types.cpp
@ -1,8 +1,10 @@
|
||||
|
||||
#include <sstream>
|
||||
#include <iostream>
|
||||
|
||||
#include "types.h"
|
||||
#include "binops.h"
|
||||
#include "builder.h"
|
||||
|
||||
namespace types {
|
||||
int operator_precedence(BinOp& op) {
|
||||
@ -40,85 +42,62 @@ namespace types {
|
||||
}
|
||||
|
||||
std::string FundamentalType::formatted() {
|
||||
std::stringstream out{ "" };
|
||||
if (this->m_const)
|
||||
out << "const ";
|
||||
switch (this->m_ty) {
|
||||
case FundamentalTypeKind::Int:
|
||||
return "Int";
|
||||
out << "Int";
|
||||
break;
|
||||
case FundamentalTypeKind::Bool:
|
||||
return "Bool";
|
||||
out << "Bool";
|
||||
break;
|
||||
case FundamentalTypeKind::Char:
|
||||
return "Char";
|
||||
out << "Char";
|
||||
break;
|
||||
case FundamentalTypeKind::Void:
|
||||
return "Void";
|
||||
out << "Void";
|
||||
break;
|
||||
default:
|
||||
return "Unknown";
|
||||
out << "Unknown";
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
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");
|
||||
return out.str();
|
||||
}
|
||||
|
||||
std::optional<std::shared_ptr<Type>> Type::return_type() {
|
||||
return {};
|
||||
}
|
||||
|
||||
std::pair<llvm::Value*, std::shared_ptr<Type>> FundamentalType::load(codegen::Builder&, llvm::Value* ptr) {
|
||||
bool Type::is_signed() {
|
||||
return false;
|
||||
}
|
||||
|
||||
std::pair<llvm::Value*, std::shared_ptr<Type>> FundamentalType::load(codegen::Builder&, llvm::Value* ptr, codegen::TypeMap&) {
|
||||
auto self = std::make_shared<FundamentalType>(*this);
|
||||
return std::pair(ptr, self);
|
||||
}
|
||||
|
||||
llvm::Value* FundamentalType::add(codegen::Builder& builder, llvm::Value* lhs, llvm::Value* rhs) {
|
||||
bool FundamentalType::is_signed() {
|
||||
switch (this->m_ty) {
|
||||
case FundamentalTypeKind::Int:
|
||||
return true;
|
||||
case FundamentalTypeKind::Bool:
|
||||
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");
|
||||
return false;
|
||||
default:
|
||||
throw std::runtime_error("Invalid type");
|
||||
}
|
||||
}
|
||||
|
||||
llvm::Value* FundamentalType::lt(codegen::Builder& builder, llvm::Value* lhs, llvm::Value* rhs) {
|
||||
uint32_t FundamentalType::size() {
|
||||
switch (this->m_ty) {
|
||||
case FundamentalTypeKind::Int:
|
||||
return 32;
|
||||
case FundamentalTypeKind::Bool:
|
||||
return 1;
|
||||
case FundamentalTypeKind::Char:
|
||||
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);
|
||||
return 8;
|
||||
default:
|
||||
throw std::runtime_error("Invalid type");
|
||||
}
|
||||
@ -126,6 +105,9 @@ namespace types {
|
||||
|
||||
std::string FunctionType::formatted() {
|
||||
std::stringstream out{ "" };
|
||||
if (this->m_const)
|
||||
out << "const ";
|
||||
|
||||
out << "(";
|
||||
|
||||
int counter = 0;
|
||||
@ -149,24 +131,96 @@ namespace types {
|
||||
return this->m_ret_ty;
|
||||
}
|
||||
|
||||
std::pair<llvm::Value*, std::shared_ptr<Type>> FunctionType::load(codegen::Builder&, llvm::Value* ptr) {
|
||||
std::pair<llvm::Value*, std::shared_ptr<Type>> FunctionType::load(codegen::Builder&, llvm::Value* ptr, codegen::TypeMap&) {
|
||||
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) {
|
||||
std::pair<llvm::Value*, std::shared_ptr<Type>> PointerType::load(codegen::Builder& builder, llvm::Value* ptr, codegen::TypeMap& structs) {
|
||||
return std::pair(
|
||||
builder.builder->CreateLoad(this->m_inner->codegen(builder), ptr),
|
||||
builder.builder->CreateLoad(this->m_inner->codegen(builder, structs), 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;
|
||||
@ -202,6 +256,38 @@ 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;
|
||||
}
|
||||
|
||||
87
src/types.h
87
src/types.h
@ -11,6 +11,8 @@ namespace types {
|
||||
Fundamental,
|
||||
Function,
|
||||
Pointer,
|
||||
Array,
|
||||
Struct,
|
||||
};
|
||||
|
||||
enum FundamentalTypeKind {
|
||||
@ -18,35 +20,35 @@ namespace types {
|
||||
Bool,
|
||||
Char,
|
||||
Void,
|
||||
/// @brief Mainly used for binop resolution
|
||||
Any,
|
||||
};
|
||||
|
||||
class Type {
|
||||
public:
|
||||
TypeKind m_kind;
|
||||
Type(TypeKind kind) : m_kind{ kind } {}
|
||||
bool m_const;
|
||||
|
||||
Type(TypeKind kind, bool is_const) : m_kind{ kind }, m_const{ is_const } {}
|
||||
virtual ~Type() = default;
|
||||
virtual std::string formatted() = 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 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 std::optional<std::shared_ptr<Type>> return_type();
|
||||
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);
|
||||
virtual bool is_signed();
|
||||
virtual uint32_t size() = 0;
|
||||
};
|
||||
|
||||
class FundamentalType : public Type {
|
||||
public:
|
||||
FundamentalTypeKind m_ty;
|
||||
FundamentalType(FundamentalTypeKind kind) : Type(TypeKind::Fundamental), m_ty{ kind } {}
|
||||
FundamentalType(bool is_const, FundamentalTypeKind kind) : Type(TypeKind::Fundamental, is_const), m_ty{ kind } {}
|
||||
virtual ~FundamentalType() override = default;
|
||||
virtual std::string formatted() 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;
|
||||
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;
|
||||
};
|
||||
|
||||
|
||||
@ -55,31 +57,68 @@ namespace types {
|
||||
std::shared_ptr<Type> m_ret_ty;
|
||||
std::vector<std::shared_ptr<Type>> m_param_tys;
|
||||
bool m_vararg;
|
||||
FunctionType(std::shared_ptr<Type> ret_ty, std::vector<std::shared_ptr<Type>> param_tys, bool vararg)
|
||||
: Type(TypeKind::Function)
|
||||
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)
|
||||
, 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) override;
|
||||
virtual std::pair<llvm::Value*, std::shared_ptr<Type>> load(codegen::Builder& builder, llvm::Value* ptr) 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 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(std::shared_ptr<Type> inner)
|
||||
: Type(TypeKind::Pointer), m_inner{ std::move(inner) } {
|
||||
|
||||
PointerType(bool is_const, std::shared_ptr<Type> inner)
|
||||
: Type(TypeKind::Pointer, is_const), m_inner{ std::move(inner) } {
|
||||
}
|
||||
virtual ~PointerType() override = default;
|
||||
virtual std::string formatted() 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::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;
|
||||
};
|
||||
|
||||
bool types_equal(std::shared_ptr<types::Type> type1, std::shared_ptr<types::Type> type2);
|
||||
|
||||
45
test.c
45
test.c
@ -6,7 +6,50 @@ 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;
|
||||
}
|
||||
|
||||
int main() {
|
||||
printf("10th fibonacci number is %d!", fibonacci(10));
|
||||
char text[30] = "10th fibonacci number is %d!\n";
|
||||
printf(text, fibonacci(10));
|
||||
|
||||
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]);
|
||||
|
||||
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]);
|
||||
|
||||
char hello = 10;
|
||||
update_ptr(&hello);
|
||||
printf("hello: %d!\n", hello);
|
||||
|
||||
char twod_array[5][5];
|
||||
twod_array[0][0] = 50;
|
||||
printf("2d array: %d!\n", twod_array[0][0]);
|
||||
|
||||
return 0;
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user