Compare commits

..

66 Commits

Author SHA1 Message Date
226118bcbb Fix some indexing problems 2026-04-16 23:47:42 +03:00
668e18e8ba Fix somelist_pointer 2026-04-16 21:55:43 +03:00
c3e12b087a Add escaped backslash 2026-04-16 20:45:38 +03:00
c4837ca9b0 Improve escaped character parsing 2026-04-16 20:45:11 +03:00
63584525f2 Add escaping of characters 2026-04-16 20:25:00 +03:00
28be145a70 Remove const array formatting 2026-04-16 20:01:30 +03:00
53872373bf Check for const in assignment binop 2026-04-16 19:47:56 +03:00
efb4ce85ac Format const in type 2026-04-16 19:46:47 +03:00
8e6980715e Add const-ability to types 2026-04-16 19:43:36 +03:00
f9208a525d Fix how pointer indexing works 2026-04-16 19:23:41 +03:00
6dad36e34a Add l-value checking in typechecker 2026-04-16 19:08:34 +03:00
d79151380f Prevent initialization of arrays except with list-inits and strings 2026-04-16 18:51:45 +03:00
238577e9ca Fix how arrays are managed 2026-04-16 18:32:31 +03:00
f0607a2310 Allow forward-declaration of structs 2026-04-16 01:39:07 +03:00
21d17bb02d Parse field accesses 2026-04-15 20:04:07 +03:00
a12cf52c48 Add FieldAccessExpression 2026-04-15 20:01:20 +03:00
9ed753a238 Implement struct initialization 2026-04-15 19:40:00 +03:00
4b4d3ce14b Implement types_equal for Structs 2026-04-15 19:34:09 +03:00
b664ca8f9d Typecheck list initializer for structs 2026-04-15 19:28:09 +03:00
1265040f29 Fix parsing struct types 2026-04-15 19:09:38 +03:00
8e7facc593 Format ref-structs correctly 2026-04-15 19:07:49 +03:00
94d1c15897 Parse struct type 2026-04-15 19:02:01 +03:00
f283149090 Parse tl-typedef 2026-04-15 18:45:29 +03:00
29fd757517 Add Struct types 2026-04-15 18:40:26 +03:00
66653553a5 Update test.c 2026-04-15 18:22:23 +03:00
bca18b4f1b Add Struct and StructRef-types 2026-04-15 18:21:22 +03:00
5fc58ea1c5 Add parsing::Scope 2026-04-15 17:45:14 +03:00
55388bc6e3 Parse listinitializer 2026-04-15 16:07:07 +03:00
aa698c7ed6 Fix string type lvalue return type 2026-04-15 15:59:12 +03:00
eacbac9205 Implement ListInitializer 2026-04-15 15:57:54 +03:00
5175878407 Fix wonky casting rules 2026-04-14 20:02:06 +03:00
de881d73d2 Fix bug with string literals 2026-04-14 19:26:21 +03:00
e76540182f Possibly fix passing of arrays 2026-04-14 19:18:10 +03:00
b97aa3f212 Fix parsing of array type parameters 2026-04-14 18:55:15 +03:00
6efcb23d6b Add get_codegen_type 2026-04-14 18:48:17 +03:00
45df4fdf5f Fix array to ptr cast 2026-04-14 18:26:33 +03:00
d3f0a730fd Make strings be char[n] 2026-04-14 17:01:08 +03:00
26779414a7 Implement arrays 2026-04-14 15:55:13 +03:00
c0a2c41c33 Fix parsing of empty arrays 2026-04-14 15:24:49 +03:00
0400aa1d99 Make empty arrays into just pointers 2026-04-14 15:20:14 +03:00
d70f6fffa0 Parse array type 2026-04-14 15:05:44 +03:00
3370e76c48 Add ArrayType 2026-04-14 14:57:25 +03:00
e797cab50f Remove some unused code 2026-04-14 14:49:07 +03:00
fa8c4b0e74 Allow underscores in idents 2026-04-14 14:19:08 +03:00
0cdf1abf82 Tweak parsing 2026-04-14 14:17:17 +03:00
be2d809986 Make ref and deref only parse primary expression 2026-04-14 00:28:12 +03:00
d3a4964f10 Add ret void to every function, to make sure one exists 2026-04-14 00:26:37 +03:00
01d4999e14 Store params in stack properly as well 2026-04-14 00:25:54 +03:00
acd1e5d39b Fix bug in deref 2026-04-14 00:09:36 +03:00
d4016e3ab7 Parse ref/deref/parenthesis 2026-04-14 00:05:30 +03:00
78c5ab4c25 Add ref and deref 2026-04-14 00:01:33 +03:00
48a3eb0e85 Improve binop result calculation 2026-04-13 23:50:07 +03:00
57dc022241 Improve binop typechecking 2026-04-13 23:16:39 +03:00
51c54e375a Add better error raporting 2026-04-13 21:47:18 +03:00
28483812ea Allow implicit cast from char to int 2026-04-13 21:45:58 +03:00
e0f2a1620e Implicitly add cast if implicit cast is found 2026-04-13 21:36:44 +03:00
ee0e30934a Allow typechecker to perform implicit casts 2026-04-13 21:32:34 +03:00
1555c12bbd Improve casts 2026-04-13 21:28:50 +03:00
c8d4b653da Fix bug with binop assignment typechecking 2026-04-13 21:24:34 +03:00
1814ce2cc9 Fix bug with loading values, parse casts 2026-04-13 21:21:46 +03:00
fa01b22b81 Add compilation of casts 2026-04-13 21:08:57 +03:00
dab4d47b8e Separate signed and unsigned comparisons correctly 2026-04-13 20:46:43 +03:00
e6a16ab667 Fix bug in codegen 2026-04-13 20:44:29 +03:00
810dd3595e Allow implicitly converting IntLiteralExpr to other int types 2026-04-13 20:15:48 +03:00
2695a83ac8 Fix LT 2026-04-13 20:06:43 +03:00
b541cf6baf Update README.md 2026-04-13 18:28:17 +03:00
20 changed files with 2105 additions and 240 deletions

View File

@ -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)

View File

@ -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

View File

@ -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
View File

@ -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;
};
}

View File

@ -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;

View File

@ -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();

View File

@ -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
View 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
View 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

View File

@ -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];
// }
}

View File

@ -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();

View File

@ -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;

View File

@ -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());
}
}
}

View File

@ -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

View File

@ -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") {

View File

@ -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;
}
}
}
}

View File

@ -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

View File

@ -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;
}

View File

@ -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
View File

@ -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;
}