Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement variable references with a simple hashmap #2

Merged
merged 2 commits into from
Sep 15, 2020
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -11,6 +11,10 @@ It should include:
- The ability to declare and define variables of type `i32`
- The ability to declare functions (with and without body brackets)
- The ability to call functions
- No concept of scope
- This is not a permanent feature and scope should [eventually be implemented](https://github.com/fnune/vampa/issues/1)
- A single hash map for all variables
- Variable references

The following program should compile and run:

7 changes: 7 additions & 0 deletions src/libvamc_lexer/src/test.rs
Original file line number Diff line number Diff line change
@@ -21,6 +21,13 @@ mod test {
])
}

#[test]
fn works_with_a_single_identifier() {
let tokens: Vec<Token> = Cursor::new("first").collect();

assert_eq!(tokens, vec![Token::new(TokenKind::Identifier, "first")])
}

#[test]
fn works_with_an_inline_comment() {
let tokens: Vec<Token> = Cursor::new("2 # The number 20").collect();
2 changes: 1 addition & 1 deletion src/libvamc_llvm/src/block.rs
Original file line number Diff line number Diff line change
@@ -4,7 +4,7 @@ use inkwell::values::BasicValueEnum;
use vamc_parser::definitions::ast::Block;

impl<'a, 'ctx> Compiler<'a, 'ctx> {
pub fn compile_block(&self, block: Block) -> CompilerResult<BasicValueEnum> {
pub fn compile_block(&mut self, block: Block) -> CompilerResult<BasicValueEnum<'ctx>> {
let target_block = self.context.append_basic_block(
*self
.function_value
2 changes: 1 addition & 1 deletion src/libvamc_llvm/src/definitions/mod.rs
Original file line number Diff line number Diff line change
@@ -10,7 +10,7 @@ use vamc_errors::Diagnostic;

pub type CompilerResult<T> = Result<T, Diagnostic>;

pub struct Compiler<'a, 'ctx> {
pub struct Compiler<'a, 'ctx: 'a> {
pub context: &'ctx Context,
pub builder: &'a Builder<'ctx>,
pub module: &'a Module<'ctx>,
9 changes: 8 additions & 1 deletion src/libvamc_llvm/src/expression.rs
Original file line number Diff line number Diff line change
@@ -4,13 +4,20 @@ use inkwell::values::BasicValueEnum;
use vamc_parser::definitions::ast::{Expression, ExpressionKind};

impl<'a, 'ctx> Compiler<'a, 'ctx> {
pub fn compile_expression(&self, expression: Expression) -> CompilerResult<BasicValueEnum> {
pub fn compile_expression(
&mut self,
expression: Expression,
) -> CompilerResult<BasicValueEnum<'ctx>>
{
match expression.kind {
ExpressionKind::Literal(literal) => self.compile_literal(literal),
ExpressionKind::FunctionCall(function_name, function_parameters) => {
self.compile_function_call(function_name.as_str(), function_parameters)
},
ExpressionKind::Block(block) => self.compile_block(*block),
ExpressionKind::VariableReference(variable_name) => {
self.compile_variable_reference(variable_name.as_str())
},
_ => unimplemented!(),
}
}
4 changes: 2 additions & 2 deletions src/libvamc_llvm/src/function_call.rs
Original file line number Diff line number Diff line change
@@ -6,10 +6,10 @@ use vamc_parser::definitions::ast::Expression;

impl<'a, 'ctx> Compiler<'a, 'ctx> {
pub fn compile_function_call(
&self,
&mut self,
function_name: &str,
parameters: Vec<Box<Expression>>,
) -> CompilerResult<BasicValueEnum>
) -> CompilerResult<BasicValueEnum<'ctx>>
{
self.builder
.position_at_end(self.function_value.unwrap().get_last_basic_block().unwrap());
10 changes: 5 additions & 5 deletions src/libvamc_llvm/src/function_declaration.rs
Original file line number Diff line number Diff line change
@@ -9,12 +9,16 @@ use vamc_parser::definitions::ast::{FunctionDeclaration, IntType, Parameters, Ty

impl<'a, 'ctx> Compiler<'a, 'ctx> {
pub fn compile_function_declaration(
&self,
&mut self,
function_declaration: FunctionDeclaration,
) -> CompilerResult<FunctionValue>
{
let function_name = function_declaration.name.as_str();

let function_body_expression = self
.compile_expression(*function_declaration.body)
.expect("Failed to compile function body expression.");

let function = self
.compile_function_signature(
function_declaration.parameters,
@@ -27,10 +31,6 @@ impl<'a, 'ctx> Compiler<'a, 'ctx> {

let function_body_block = self.context.append_basic_block(function, function_name);

let function_body_expression = self
.compile_expression(*function_declaration.body)
.expect("Failed to compile function body expression.");

let function_body_expression =
match function_return_type.expect("Functions must all return something.".into()) {
BasicTypeEnum::IntType(_) => function_body_expression.into_int_value(),
2 changes: 1 addition & 1 deletion src/libvamc_llvm/src/literal.rs
Original file line number Diff line number Diff line change
@@ -4,7 +4,7 @@ use inkwell::values::BasicValueEnum;
use vamc_parser::definitions::ast::{Literal, LiteralIntType, LiteralKind};

impl<'a, 'ctx> Compiler<'a, 'ctx> {
pub fn compile_literal(&self, literal: Literal) -> CompilerResult<BasicValueEnum> {
pub fn compile_literal(&self, literal: Literal) -> CompilerResult<BasicValueEnum<'ctx>> {
match literal.kind {
LiteralKind::Int(value, typ) => match typ {
LiteralIntType::Unsuffixed => Ok(BasicValueEnum::IntValue(
15 changes: 6 additions & 9 deletions src/libvamc_llvm/src/source_file.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
use crate::definitions::{Compiler, CompilerResult};
use crate::definitions::Compiler;

use inkwell::values::BasicValueEnum;
use vamc_parser::definitions::ast::SourceFile;

impl<'a, 'ctx> Compiler<'a, 'ctx> {
pub fn compile_source_file(&self, source_file: SourceFile) -> CompilerResult<BasicValueEnum> {
pub fn compile_source_file(&mut self, source_file: SourceFile) {
let target_block = self.context.append_basic_block(
*self
.function_value
@@ -14,11 +13,9 @@ impl<'a, 'ctx> Compiler<'a, 'ctx> {

self.builder.position_at_end(target_block);

let compiled_statements = source_file
.statements
.into_iter()
.map(|statement| self.compile_statement(target_block, *statement));

Ok(compiled_statements.last().unwrap().unwrap())
source_file.statements.into_iter().for_each(|statement| {
self.compile_statement(target_block, *statement)
.expect("Failed to compile top-level statement.");
});
}
}
7 changes: 3 additions & 4 deletions src/libvamc_llvm/src/statement.rs
Original file line number Diff line number Diff line change
@@ -8,15 +8,14 @@ use vamc_parser::definitions::ast::{Statement, StatementKind};

impl<'a, 'ctx> Compiler<'a, 'ctx> {
pub fn compile_statement(
&self,
&mut self,
target_block: BasicBlock,
statement: Statement,
) -> CompilerResult<BasicValueEnum>
) -> CompilerResult<BasicValueEnum<'ctx>>
{
match statement.kind {
StatementKind::VariableDeclaration(variable_declaration) => {
self.compile_variable_declaration(target_block, variable_declaration)
.unwrap();
self.compile_variable_declaration(target_block, variable_declaration);
Ok(self.context.i8_type().const_zero().as_basic_value_enum())
},
StatementKind::FunctionDeclaration(function_declaration) => {
2 changes: 1 addition & 1 deletion src/libvamc_llvm/src/tests/blocks/blocks.rs
Original file line number Diff line number Diff line change
@@ -30,7 +30,7 @@ fn basic_block() {
],
};

with_compiler(|compiler, _| {
with_compiler(|mut compiler, _| {
compiler.compile_block(block).unwrap();

assert_snapshot!(compiler.module.print_to_string().to_string_lossy())
2 changes: 1 addition & 1 deletion src/libvamc_llvm/src/tests/functions/function_calls.rs
Original file line number Diff line number Diff line change
@@ -23,7 +23,7 @@ fn typed_function_declaration() {
}),
};

with_compiler(|compiler, _| {
with_compiler(|mut compiler, _| {
compiler
.compile_function_declaration(function_declaration)
.unwrap();
4 changes: 2 additions & 2 deletions src/libvamc_llvm/src/tests/functions/function_declarations.rs
Original file line number Diff line number Diff line change
@@ -23,7 +23,7 @@ fn typed_function_declaration() {
}),
};

with_compiler(|compiler, _| {
with_compiler(|mut compiler, _| {
compiler
.compile_function_declaration(function_declaration)
.unwrap();
@@ -66,7 +66,7 @@ fn typed_ternary_function_declaration() {
}),
};

with_compiler(|compiler, _| {
with_compiler(|mut compiler, _| {
compiler
.compile_function_declaration(function_declaration)
.unwrap();
6 changes: 2 additions & 4 deletions src/libvamc_llvm/src/tests/variables/variable_declarations.rs
Original file line number Diff line number Diff line change
@@ -17,10 +17,8 @@ fn simple_variable_declaration() {
}),
};

with_compiler(|compiler, target_block| {
compiler
.compile_variable_declaration(target_block, variable_declaration)
.unwrap();
with_compiler(|mut compiler, target_block| {
compiler.compile_variable_declaration(target_block, variable_declaration);

assert_snapshot!(compiler.module.print_to_string().to_string_lossy())
});
19 changes: 7 additions & 12 deletions src/libvamc_llvm/src/variable_declaration.rs
Original file line number Diff line number Diff line change
@@ -1,18 +1,16 @@
use crate::definitions::{Compiler, CompilerResult};
use crate::definitions::Compiler;

use inkwell::{basic_block::BasicBlock, values::BasicValueEnum};

use vamc_errors::Diagnostic;
use inkwell::basic_block::BasicBlock;
use vamc_parser::definitions::ast::{IntType, TypKind, VariableDeclaration};

impl<'a, 'ctx> Compiler<'a, 'ctx> {
/// We return the assigned value, but the important part is the side effect
/// of allocating the newly-declared variable into the target block.
pub fn compile_variable_declaration(
&self,
&mut self,
target_block: BasicBlock,
variable_declaration: VariableDeclaration,
) -> CompilerResult<BasicValueEnum>
)
{
let name = variable_declaration.name;

@@ -31,15 +29,12 @@ impl<'a, 'ctx> Compiler<'a, 'ctx> {

self.builder.build_store(allocation, value.into_int_value());

Ok(value)
// TODO: nested scopes https://github.com/fnune/vampa/issues/1
self.variables.insert(*name, allocation);
},
},
TypKind::Infer => Err(Diagnostic::error(
"Type inference isn't supported yet.".into(),
)),
TypKind::Infer => (),
}
} else {
Err(Diagnostic::error("Failed to compile expression.".into()))
}
}
}
14 changes: 12 additions & 2 deletions src/libvamc_llvm/src/variable_reference.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,17 @@
use crate::definitions::{Compiler, CompilerResult};

use inkwell::values::BasicValueEnum;
use vamc_errors::Diagnostic;

impl<'a, 'ctx> Compiler<'a, 'ctx> {
pub fn compile_variable_reference(&mut self) -> CompilerResult<()> {
unimplemented!();
pub fn compile_variable_reference(
&mut self,
variable_name: &str,
) -> CompilerResult<BasicValueEnum<'ctx>>
{
match self.variables.get(variable_name) {
Some(pointer_value) => Ok(self.builder.build_load(*pointer_value, variable_name)),
None => Err(Diagnostic::error("Reference to undefined variable.".into())),
}
}
}
2 changes: 0 additions & 2 deletions src/libvamc_parser/src/statement.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
use vamc_errors::Diagnostic;

use crate::{
definitions::{
ast::{Statement, StatementKind},
19 changes: 19 additions & 0 deletions src/libvamc_parser/src/tests/source_files/simple.rs
Original file line number Diff line number Diff line change
@@ -37,3 +37,22 @@ fn simple_module() {
})
);
}

#[test]
fn simple_module_with_variable_reference() {
let tokens: Vec<Token> = Cursor::new("first").collect();
let mut parser = Parser::new(tokens);
let result = parser.parse_source_file("program.vam");

assert_eq!(
result,
Ok(SourceFile {
file_name: "program.vam".into(),
statements: vec![Box::new(Statement {
kind: StatementKind::Return(Box::new(Expression {
kind: ExpressionKind::VariableReference(Box::new("first".to_string())),
})),
}),]
})
);
}
9 changes: 7 additions & 2 deletions src/libvamc_parser/src/variable_reference.rs
Original file line number Diff line number Diff line change
@@ -13,14 +13,19 @@ impl Parser {
let token = self.token();
let token_is_keyword = is_keyword(token);

match token {
let result = match token {
_ if token_is_keyword => Err(Diagnostic::error(format!(
"Failed to parse variable reference: `{}` is a reserved keyword.",
token.value
))),
token => Ok(Expression {
kind: ExpressionKind::VariableReference(Box::new(token.value.clone())),
}),
}
};

// Eat the identifier token.
self.bump_until_next();

result
}
}
2 changes: 1 addition & 1 deletion src/vamc/src/main.rs
Original file line number Diff line number Diff line change
@@ -41,7 +41,7 @@ fn main() {

let output_path = Path::new(output_path_buf.to_str().unwrap());

compiler.compile_source_file(source_file_ast).unwrap();
compiler.compile_source_file(source_file_ast);
compiler.module.write_bitcode_to_path(&output_path);

fs::File::open(output_path)