Skip to content

Commit

Permalink
refactor: changed codegen structure (WASM still broken)
Browse files Browse the repository at this point in the history
  • Loading branch information
ZakFarmer committed Nov 3, 2023
1 parent 1bdb58f commit 40899f6
Show file tree
Hide file tree
Showing 26 changed files with 434 additions and 465 deletions.
15 changes: 7 additions & 8 deletions .github/workflows/rust.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,21 +2,20 @@ name: Rust

on:
push:
branches: [ "main", "develop" ]
branches: ["main", "develop", "feature/**"]
pull_request:
branches: [ "main" ]
branches: ["main"]

env:
CARGO_TERM_COLOR: always

jobs:
build:

runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v3
- name: Build
run: cargo build --verbose
- name: Run tests
run: cargo test --verbose
- uses: actions/checkout@v3
- name: Build
run: cargo build --verbose
- name: Run tests
run: cargo test --verbose
24 changes: 22 additions & 2 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 3 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
[workspace]
members = [
"compiler",
"codegen",
"interpreter",
"lexer",
"llvm",
"object",
"parser",
"source",
"token"
]
3 changes: 2 additions & 1 deletion compiler/Cargo.toml → codegen/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
[package]
name = "compiler"
name = "codegen"
version = "0.1.0"
edition = "2021"

Expand All @@ -11,6 +11,7 @@ env_logger = "0.10.0"
inkwell = { version = "0.2.0", features = ["llvm16-0"] }
lazy_static = "1.4.0"
lexer = { path = "../lexer" }
llvm = { path = "../llvm" }
log = "0.4.20"
object = { path = "../object" }
opcode = { path = "../opcode" }
Expand Down
29 changes: 15 additions & 14 deletions compiler/src/codegen/builder.rs → codegen/src/builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,41 +5,42 @@ use inkwell::{
types::IntType,
values::{BasicValueEnum, PointerValue},
};
use llvm::Llvm;
use parser::ast::{Node, Program};

pub struct RecursiveBuilder<'a> {
pub builder: &'a Builder<'a>,
use super::expression::ExpressionBuilder;

pub bool_type: IntType<'a>,
pub i32_type: IntType<'a>,
pub struct RecursiveBuilder<'a, 'b> {
pub llvm: &'b Llvm<'a>,

pub builtins: RefCell<HashMap<String, PointerValue<'a>>>,
pub variables: RefCell<HashMap<String, PointerValue<'a>>>,
}

impl<'a> RecursiveBuilder<'a> {
pub fn new(bool_type: IntType<'a>, i32_type: IntType<'a>, builder: &'a Builder<'_>) -> Self {
Self {
builder,
bool_type,
i32_type,
impl<'ink, 'b> RecursiveBuilder<'ink, 'b> {
pub fn new(llvm: &'b Llvm<'ink>) -> RecursiveBuilder<'ink, 'b> {
RecursiveBuilder {
llvm,
builtins: RefCell::new(HashMap::new()),
variables: RefCell::new(HashMap::new()),
}
}

/// Build a node
pub fn build(&self, ast: &Node) -> BasicValueEnum<'_> {
pub fn build(&self, ast: &Node) -> BasicValueEnum<'ink> {
match ast {
Node::Program(n) => self.build_program(n),
Node::Statement(n) => self.build_statement(n),
Node::Expression(n) => self.build_expression(n),
Node::Expression(n) => {
let expression_builder = ExpressionBuilder::new(self.llvm);
expression_builder.build_expression(n)
},
_ => panic!("Unknown node"),
}
}

/// Build a program
fn build_program(&self, program: &Program) -> BasicValueEnum<'_> {
fn build_program(&self, program: &Program) -> BasicValueEnum<'ink> {
let mut results: Vec<BasicValueEnum<'_>> = vec![];

for statement in &program.statements {
Expand All @@ -49,7 +50,7 @@ impl<'a> RecursiveBuilder<'a> {
let last = results
.last()
.cloned()
.unwrap_or_else(|| BasicValueEnum::IntValue(self.i32_type.const_int(0, false)));
.unwrap_or_else(|| BasicValueEnum::IntValue(self.llvm.i32_type().const_int(0, false)));

last
}
Expand Down
176 changes: 176 additions & 0 deletions codegen/src/expression.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,176 @@
use anyhow::Error;
use inkwell::values::{BasicValueEnum, PointerValue};
use llvm::Llvm;
use parser::ast::{Expression, Literal};
use token::TokenType;

use super::builder::RecursiveBuilder;

pub enum ExpressionValue<'ev> {
LValue(PointerValue<'ev>),
RValue(BasicValueEnum<'ev>),
}

impl<'ev> ExpressionValue<'ev> {
pub fn as_basic_value_enum(&self) -> BasicValueEnum<'ev> {
match self {
ExpressionValue::LValue(pointer) => BasicValueEnum::PointerValue(*pointer),
ExpressionValue::RValue(value) => *value,
}
}

pub fn as_r_value(&self, llvm: &Llvm<'ev>, name: Option<&str>) -> BasicValueEnum<'ev> {
match self {
ExpressionValue::LValue(pointer) => llvm.load_pointer(pointer, name.as_deref().unwrap_or("")),
ExpressionValue::RValue(value) => *value,
}
}
}

pub struct ExpressionBuilder<'a, 'b> {
pub llvm: &'b Llvm<'a>,
}

impl<'ink, 'b> ExpressionBuilder<'ink, 'b> {
pub fn new(llvm: &'b Llvm<'ink>) -> ExpressionBuilder<'ink, 'b> {
ExpressionBuilder { llvm }
}

pub fn build_expression(&self, expression: &Expression) -> BasicValueEnum<'ink> {
match expression {
Expression::Call(_call_expression) => todo!(),
Expression::Function(_function_literal) => todo!(),
Expression::Identifier(ref _identifier) => todo!(),
Expression::If(_if_expression) => todo!(),
Expression::Index(_index_expression) => todo!(),
Expression::Infix(infix_expression) => self.build_infix_expression(infix_expression),
Expression::Literal(literal) => self.build_literal(literal),
Expression::Prefix(prefix_expression) => {
self.build_prefix_expression(prefix_expression)
}
_ => panic!("Unknown expression"),
}
}

/// Build a literal
pub fn build_literal(&self, literal: &Literal) -> BasicValueEnum<'ink> {
match literal {
Literal::Array(_array) => todo!(),
Literal::Boolean(value) => self.build_boolean_literal(value),
Literal::Float(_value) => todo!(),
Literal::Integer(value) => self.build_integer_literal(value),
Literal::String(value) => self.build_string_literal(value),
_ => panic!("Unknown literal"),
}
}

/// Build a boolean literal
pub fn build_boolean_literal(&self, value: &bool) -> BasicValueEnum<'ink> {
BasicValueEnum::IntValue(self.llvm.bool_type().const_int(*value as u64, false))
}

/// Build an integer literal
pub fn build_integer_literal(&self, value: &i64) -> BasicValueEnum<'ink> {
BasicValueEnum::IntValue(self.llvm.i32_type().const_int(*value as u64, false))
}

/// Build a string literal
pub fn build_string_literal(&self, value: &String) -> BasicValueEnum<'ink> {
let string_value = value.as_str();

let string = self
.llvm
.builder
.build_global_string_ptr(string_value, value.as_str())
.as_pointer_value();

BasicValueEnum::PointerValue(string)
}

/// Build a prefix expression
pub fn build_prefix_expression(
&self,
prefix_expression: &parser::ast::PrefixExpression,
) -> BasicValueEnum<'ink> {
let right = self.build_expression(&prefix_expression.right);

let built = match prefix_expression.operator.token_type {
TokenType::Bang => self.llvm.builder.build_int_compare(
inkwell::IntPredicate::EQ,
right.into_int_value(),
self.llvm.i32_type().const_int(0, false),
"not",
),
TokenType::Minus => self.llvm.builder.build_int_neg(right.into_int_value(), "neg"),
_ => panic!("Unknown operator"),
};

BasicValueEnum::IntValue(built)
}

/// Build an infix expression
pub fn build_infix_expression<'a>(
&self,
infix_expression: &parser::ast::InfixExpression,
) -> BasicValueEnum<'ink> {
let left_value = self.build_expression(&infix_expression.left);
let right_value = self.build_expression(&infix_expression.right);

let left = match left_value {
BasicValueEnum::IntValue(val) => val,
_ => panic!("Expected IntValue for left operand"),
};

let right = match right_value {
BasicValueEnum::IntValue(val) => val,
_ => panic!("Expected IntValue for right operand"),
};

let result = match infix_expression.operator.token_type {
TokenType::Plus => self.llvm.builder.build_int_add(left, right, "add"),
TokenType::Minus => self.llvm.builder.build_int_sub(left, right, "sub"),
TokenType::Asterisk => self.llvm.builder.build_int_mul(left, right, "mul"),
TokenType::Slash => self.llvm.builder.build_int_unsigned_div(left, right, "div"),
TokenType::Lt => {
self.llvm.builder
.build_int_compare(inkwell::IntPredicate::SLT, left, right, "less_than")
}
TokenType::Gt => self.llvm.builder.build_int_compare(
inkwell::IntPredicate::SGT,
left,
right,
"greater_than",
),
TokenType::Eq => {
self.llvm.builder
.build_int_compare(inkwell::IntPredicate::EQ, left, right, "equal_to")
}
TokenType::NotEq => self.llvm.builder.build_int_compare(
inkwell::IntPredicate::NE,
left,
right,
"not_equal_to",
),
_ => panic!("Unknown operator"),
};

BasicValueEnum::IntValue(result)
}

/// Build an infix expression
fn build_add_infix_expression<'ctx>(
&'ctx self,
left: BasicValueEnum<'ctx>,
right: BasicValueEnum<'ctx>,
) -> BasicValueEnum<'ctx> {
match left {
BasicValueEnum::IntValue(left) => match right {
BasicValueEnum::IntValue(right) => {
BasicValueEnum::IntValue(self.llvm.builder.build_int_add(left, right, "add"))
}
_ => panic!("Expected IntValue for right operand"),
},
_ => panic!("Expected IntValue for left operand"),
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,14 @@ use parser::ast::Identifier;

use super::builder::RecursiveBuilder;

impl<'a> RecursiveBuilder<'a> {
impl<'ink, 'b> RecursiveBuilder<'ink, 'b> {
/// Build an identifier
pub fn build_identifier(&self, identifier: &Identifier) -> BasicValueEnum<'_> {
let built = match self.variables.borrow().get(&identifier.token.value) {
Some(value) => self
.llvm
.builder
.build_load(self.i32_type, *value, identifier.token.value.as_str())
.build_load(self.llvm.i32_type(), *value, identifier.token.value.as_str())
.into_int_value(),
None => panic!("Unknown identifier"),
};
Expand Down
11 changes: 11 additions & 0 deletions codegen/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
#![deny(elided_lifetimes_in_paths)]

use anyhow::Error;
use inkwell::{context::Context, OptimizationLevel, types::IntType};
use llvm::Llvm;

pub mod builder;
mod expression;
mod identifier;
mod statement;
pub mod symbol_table;
Loading

0 comments on commit 40899f6

Please sign in to comment.