From d4362d62a6350d3468cf46bc1fcb146af9993548 Mon Sep 17 00:00:00 2001 From: Willyboar Date: Wed, 31 May 2023 14:03:37 +0300 Subject: [PATCH] Implement functions and Tests --- README.md | 3 +- src/glove.gleam | 73 +++++++++----- test/glove_test.gleam | 216 ++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 268 insertions(+), 24 deletions(-) diff --git a/README.md b/README.md index b31f692..85cbf64 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,6 @@ -## Don't Use it yet (WIP) [![Package Version](https://img.shields.io/hexpm/v/glove)](https://hex.pm/packages/glove) [![Hex Docs](https://img.shields.io/badge/hex-docs-ffaff3)](https://hexdocs.pm/glove/) @@ -21,7 +20,7 @@ QBE Backend can be found [here](https://c9x.me/compile/) ## Quick start -TODO: Look to examples +TODO ## Run the tests ```sh diff --git a/src/glove.gleam b/src/glove.gleam index b2e3298..6e6a56e 100644 --- a/src/glove.gleam +++ b/src/glove.gleam @@ -127,7 +127,6 @@ pub fn display_inst(inst: Inst) -> String { |> list.index_map(fn(_, arg) { case arg { #(ty, val) -> display_type(ty) <> " " <> display_value(val) - _ -> "..." } }) |> string.join(", ") @@ -225,7 +224,17 @@ pub fn into_base(self) -> Type { /// Returns byte size for values of the type pub fn size(self) -> Int { - todo + case self { + Byte -> 1 + Halfword -> 2 + Word | Single -> 4 + Long | Double -> 8 + // This not working + Aggregate(td) -> + case td.items { + [] -> 0 + } + } } /// QBE data definition @@ -348,18 +357,37 @@ pub fn display_block(block: Block) -> String { } /// Adds a new instruction to the block -pub fn add_inst() -> Nil { - todo +pub fn add_inst(block: Block, inst: Inst) -> Block { + Block( + label: block.label, + statements: list.append(block.statements, [Volatile(inst)]), + ) } /// Adds a new instruction assigned to a temporary -pub fn assign_inst() -> Nil { - todo +pub fn assign_inst(block: Block, val: Value, typ: Type, inst: Inst) -> Block { + Block( + label: block.label, + statements: list.append(block.statements, [Assign(val, typ, inst)]), + ) } /// Returns true if the block's last instruction is a jump -pub fn jumps() -> Bool { - todo +pub fn jumps(block: Block) -> Bool { + case list.last(block.statements) { + Ok(statement) -> + case statement { + Volatile(instr) -> + case instr { + Ret(_) -> True + Jmp(_) -> True + Jnz(_, _, _) -> True + _ -> False + } + _ -> False + } + Error(_) -> False + } } /// QBE Function @@ -387,6 +415,7 @@ pub fn display_function(func: Function) -> String { linkage_str <> "function" <> return_str <> " " <> "$" <> name_str <> "(" <> args_str <> ")" <> " {\n" <> blocks_str <> "}" } +/// Display functions Arguments pub fn display_arguments(arguments: List(#(Type, Value))) -> String { case arguments { [] -> "" @@ -401,6 +430,7 @@ pub fn display_arguments(arguments: List(#(Type, Value))) -> String { } } +/// Display blocks pub fn display_blocks(blocks: List(Block)) -> String { blocks |> list.map(fn(block) { display_block(block) }) @@ -420,23 +450,16 @@ pub fn new_function() -> Function { /// Adds a new empty block with a specified label and returns /// a reference to it -pub fn add_block() -> Block { - todo +pub fn add_block(label: String) -> Block { + Block(label: label, statements: []) } /// Returns a reference to the last block -pub fn last_block() -> Nil { - todo -} - -/// Adds a new instruction to the last block -pub fn add_instr() -> Nil { - todo -} - -/// Adds a new instruction assigned to a temporary -pub fn assign_instr() -> Nil { - todo +pub fn last_block(blocks: List(Block)) -> Option(Block) { + case list.last(blocks) { + Ok(block) -> Some(block) + Error(_) -> None + } } /// Linkage of a function or data defintion (e.g. section and @@ -445,6 +468,7 @@ pub type Linkage { Linkage(exported: Bool, section: Option(String), secflags: Option(String)) } +/// Display function for Linkage pub fn display_linkage(linkage: Linkage) -> String { let exported_str = case linkage.exported { True -> "export " @@ -471,10 +495,12 @@ pub fn private_with_section(section: String) -> Linkage { Linkage(exported: False, section: Some(section), secflags: None) } +/// Returns the default configuration for public linkage pub fn public() -> Linkage { Linkage(exported: True, section: None, secflags: None) } +/// Returns the configuration for public linkage with a provided section pub fn public_with_section(section: String) -> Linkage { Linkage(exported: True, section: Some(section), secflags: None) } @@ -509,6 +535,7 @@ pub fn display_module(module: Module) -> String { functions_str <> "\n\n" <> types_str <> "\n\n" <> data_str } +/// Add function to module pub fn add_function(module: Module, function: Function) -> Module { Module( functions: list.append(module.functions, [function]), @@ -517,6 +544,7 @@ pub fn add_function(module: Module, function: Function) -> Module { ) } +/// Add type to module pub fn add_type(module: Module, type_def: TypeDef) -> Module { Module( functions: module.functions, @@ -525,6 +553,7 @@ pub fn add_type(module: Module, type_def: TypeDef) -> Module { ) } +/// Add Data to module pub fn add_data(module: Module, data_def: DataDef) -> Module { Module( functions: module.functions, diff --git a/test/glove_test.gleam b/test/glove_test.gleam index 98d361f..d639ce6 100644 --- a/test/glove_test.gleam +++ b/test/glove_test.gleam @@ -422,12 +422,14 @@ pub fn display_data_def_test() { } // Tests for Linkage functions +// Test for Linkage private without section pub fn private_test() { let expected = glove.Linkage(exported: False, section: None, secflags: None) let result = glove.private() should.equal(result, expected) } +// Test for Linkage private with section pub fn private_with_section_test() { let section = "mysection" let expected = @@ -436,12 +438,14 @@ pub fn private_with_section_test() { should.equal(result, expected) } +// Test for Linkage public without section pub fn public_test() { let expected = glove.Linkage(exported: True, section: None, secflags: None) let result = glove.public() should.equal(result, expected) } +// Test for Linkage public with section pub fn public_with_section_test() { let section = "mysection" let expected = @@ -450,6 +454,7 @@ pub fn public_with_section_test() { should.equal(result, expected) } +// Tests for display QBE.Datadef pub fn display_data_test() { let data_def = glove.DataDef( @@ -467,6 +472,7 @@ pub fn display_data_test() { should.equal(result, expected) } +// Tests for display QBE.functions pub fn display_function_test() { let function = glove.Function( @@ -495,6 +501,7 @@ pub fn display_function_test() { should.equal(result, expected) } +// Tests for display QBE.Blocks pub fn display_blocks_test() { let blocks = [ glove.Block( @@ -515,6 +522,7 @@ pub fn display_blocks_test() { should.equal(result, expected) } +// TEst for display arguments pub fn display_arguments_test() { let arguments = [ #(glove.Word, glove.Global("arg1")), @@ -528,6 +536,7 @@ pub fn display_arguments_test() { should.equal(result, expected) } +// Tests for display QBE.Modules pub fn display_module_test() { // Test case with empty module let empty_module = glove.Module(functions: [], types: [], data: []) @@ -614,3 +623,210 @@ pub fn display_module_test() { "function w $add(w %a, w %b) {\n" <> "@start\n" <> "%c =w add %a, %b\n" <> "ret %c\n}\n\n" <> "export function w $main() {\n" <> "@start\n" <> "%r =w call $add(w 1, w 1)\n" <> "call $printf(l $fmt, w %r)\n" <> "ret 0\n}\n\n\n\n" <> "data $fmt = " <> "{ b \"One and one make %d!\n\", b 0 }", ) } + +// Test for new_datadef function +pub fn new_datadef_test() { + let datadef = glove.new_datadef() + + // Assert the default values + should.equal(datadef.linkage, glove.private()) + should.equal(datadef.name, "") + should.equal(datadef.align, None) + should.equal(datadef.items, []) +} + +pub fn new_module_test() { + let module = glove.new_module() + + // Assert empty lists for functions, types, and data + should.equal(module.functions, []) + should.equal(module.types, []) + should.equal(module.data, []) +} + +pub fn add_function_test() { + let module = glove.new_module() + let function = glove.new_function() + let updated_module = glove.add_function(module, function) + + // Assert the function is added to the module + should.equal(updated_module.functions, [function]) + should.equal(updated_module.types, []) + should.equal(updated_module.data, []) +} + +pub fn add_data_test() { + let module = glove.new_module() + let data_def = glove.new_datadef() + let updated_module = glove.add_data(module, data_def) + + // Assert the data definition is added to the module + should.equal(updated_module.functions, []) + should.equal(updated_module.types, []) + should.equal(updated_module.data, [data_def]) +} + +pub fn add_type_test() { + let module = glove.new_module() + let type_def = + glove.TypeDef("my_type", None, [#(glove.Word, 2), #(glove.Word, 3)]) + let updated_module = glove.add_type(module, type_def) + + // Assert the type definition is added to the module + should.equal(updated_module.functions, []) + should.equal( + updated_module.types, + [glove.TypeDef("my_type", None, [#(glove.Word, 2), #(glove.Word, 3)])], + ) + should.equal(updated_module.data, []) +} + +// Test for add instruction function +pub fn add_inst_test() { + let block = glove.Block(label: "my_block", statements: []) + let inst = glove.Call(glove.Global("foo"), []) + let new_block = glove.add_inst(block, inst) + should.equal(new_block.statements, [glove.Volatile(inst)]) +} + +// Test for assign instruction function +pub fn assign_inst_test() { + let block = glove.Block(label: "my_block", statements: []) + let val = glove.Temporary("tmp") + let typ = glove.Word + let inst = glove.Call(glove.Global("bar"), []) + let new_block = glove.assign_inst(block, val, typ, inst) + should.equal(new_block.statements, [glove.Assign(val, typ, inst)]) +} + +pub fn jumps_test() { + let block_with_jump = + glove.Block( + label: "my_block", + statements: [ + glove.Assign( + glove.Temporary("r"), + glove.Word, + glove.Call( + glove.Global("add"), + [#(glove.Word, glove.Const(1)), #(glove.Word, glove.Const(1))], + ), + ), + glove.Volatile(glove.Ret(Some(glove.Const(0)))), + // Non-jump instruction + glove.Assign( + glove.Temporary("r"), + glove.Word, + glove.Call( + glove.Global("add"), + [#(glove.Word, glove.Const(1)), #(glove.Word, glove.Const(1))], + ), + ), + glove.Volatile(glove.Jmp("label")), + ], + ) + + should.equal(glove.jumps(block_with_jump), True) + + let block_without_jump = + glove.Block( + label: "my_block", + statements: [ + glove.Assign( + glove.Temporary("r"), + glove.Word, + glove.Call( + glove.Global("add"), + [#(glove.Word, glove.Const(1)), #(glove.Word, glove.Const(1))], + ), + ), + glove.Volatile(glove.Ret(Some(glove.Const(0)))), + // Non-jump instruction + glove.Assign( + glove.Temporary("r"), + glove.Word, + glove.Call( + glove.Global("add"), + [#(glove.Word, glove.Const(1)), #(glove.Word, glove.Const(1))], + ), + ), + glove.Volatile(glove.Add(glove.Const(1), glove.Const(2))), + ], + ) + + should.equal(glove.jumps(block_without_jump), False) +} + +// Test add_block function +pub fn add_block_test() { + let block = glove.add_block("my_block") + should.equal(block.label, "my_block") + should.equal(block.statements, []) +} + +// Test last_block function +pub fn last_block_test() { + let blocks = [ + glove.Block(label: "block1", statements: []), + glove.Block(label: "block2", statements: []), + glove.Block(label: "block3", statements: []), + ] + + should.equal( + glove.last_block(blocks), + Some(glove.Block(label: "block3", statements: [])), + ) + + let empty_blocks: List(glove.Block) = [] + should.equal(glove.last_block(empty_blocks), None) +} + +// Test for new function +pub fn new_function_test() { + let function = glove.new_function() + should.equal(function.linkage, glove.private()) + should.equal(function.name, "") + should.equal(function.arguments, []) + should.equal(function.return_ty, None) + should.equal(function.blocks, []) +} + +pub fn into_abi_test() { + should.equal(glove.into_abi(glove.Byte), glove.Word) + should.equal(glove.into_abi(glove.Halfword), glove.Word) + should.equal(glove.into_abi(glove.Word), glove.Word) + should.equal(glove.into_abi(glove.Single), glove.Single) + should.equal(glove.into_abi(glove.Long), glove.Long) + should.equal(glove.into_abi(glove.Double), glove.Double) + should.equal( + glove.into_abi(glove.Aggregate(glove.TypeDef( + "struct", + Some(4), + [#(glove.Word, 2), #(glove.Word, 1), #(glove.Word, 3)], + ))), + glove.Aggregate(glove.TypeDef( + "struct", + Some(4), + [#(glove.Word, 2), #(glove.Word, 1), #(glove.Word, 3)], + )), + ) + // Assuming td is a valid `TypeDef` +} + +pub fn into_base_test() { + should.equal(glove.into_base(glove.Byte), glove.Word) + should.equal(glove.into_base(glove.Halfword), glove.Word) + should.equal(glove.into_base(glove.Word), glove.Word) + should.equal(glove.into_base(glove.Single), glove.Single) + should.equal(glove.into_base(glove.Long), glove.Long) + should.equal(glove.into_base(glove.Double), glove.Double) + should.equal( + glove.into_base(glove.Aggregate(glove.TypeDef( + "struct", + Some(4), + [#(glove.Word, 2), #(glove.Word, 1), #(glove.Word, 3)], + ))), + glove.Long, + ) + // Assuming td is a valid `TypeDef` +}