From 6280556badbd5f06694a93ea0f427464e368564f Mon Sep 17 00:00:00 2001 From: Torben Ewert Date: Fri, 19 Jun 2026 15:50:34 +0200 Subject: [PATCH] feat: add binary arithmetic operations --- lib/pinc_bytecode/instruction.ml | 30 ++++++++++ lib/pinc_bytecode/value.ml | 4 ++ lib/pinc_compiler/compiler.ml | 7 ++- lib/pinc_vm/vm.ml | 99 +++++++++++++++++++++++++++++--- test/vm/add.pi | 3 - test/vm/binop.pi | 27 +++++++++ test/vm/run.t | 64 +++++++++++++++++++++ 7 files changed, 221 insertions(+), 13 deletions(-) delete mode 100644 test/vm/add.pi create mode 100644 test/vm/binop.pi diff --git a/lib/pinc_bytecode/instruction.ml b/lib/pinc_bytecode/instruction.ml index be05ba6..583db62 100644 --- a/lib/pinc_bytecode/instruction.ml +++ b/lib/pinc_bytecode/instruction.ml @@ -2,17 +2,32 @@ type t = | I_Pop | I_Constant of UInt16.t | I_Add + | I_Sub + | I_Div + | I_Mul + | I_Mod + | I_Pow let byte = function | I_Pop -> 0x00 | I_Constant _ -> 0x01 | I_Add -> 0x02 + | I_Sub -> 0x03 + | I_Div -> 0x04 + | I_Mul -> 0x05 + | I_Mod -> 0x06 + | I_Pow -> 0x07 ;; let operands_length = function | I_Pop -> 0 | I_Constant addr -> UInt16.width addr | I_Add -> 0 + | I_Sub -> 0 + | I_Div -> 0 + | I_Mul -> 0 + | I_Mod -> 0 + | I_Pow -> 0 ;; let decode bytes offset = @@ -24,6 +39,11 @@ let decode bytes offset = let offset, addr = UInt16.read bytes offset in (offset, I_Constant addr) | 0x02 -> (offset, I_Add) + | 0x03 -> (offset, I_Sub) + | 0x04 -> (offset, I_Div) + | 0x05 -> (offset, I_Mul) + | 0x06 -> (offset, I_Mod) + | 0x07 -> (offset, I_Pow) | _ -> raise_notrace (Invalid_argument (Printf.sprintf "unknown instruction: 0x%.2X" instruction)) @@ -33,6 +53,11 @@ let pp fmt = function | I_Pop -> Format.fprintf fmt "I_Pop" | I_Constant addr -> Format.fprintf fmt "I_Constant %a" UInt16.pp addr | I_Add -> Format.fprintf fmt "I_Add" + | I_Sub -> Format.fprintf fmt "I_Sub" + | I_Div -> Format.fprintf fmt "I_Div" + | I_Mul -> Format.fprintf fmt "I_Mul" + | I_Mod -> Format.fprintf fmt "I_Mod" + | I_Pow -> Format.fprintf fmt "I_Pow" ;; let to_bytes t = @@ -50,6 +75,11 @@ let to_bytes t = | I_Pop -> () | I_Constant addr -> offset := UInt16.write bytes !offset addr | I_Add -> () + | I_Sub -> () + | I_Div -> () + | I_Mul -> () + | I_Mod -> () + | I_Pow -> () in bytes diff --git a/lib/pinc_bytecode/value.ml b/lib/pinc_bytecode/value.ml index 8ba9dfb..427bad7 100644 --- a/lib/pinc_bytecode/value.ml +++ b/lib/pinc_bytecode/value.ml @@ -1,13 +1,17 @@ type t = | Null | Int of int + | Float of float let pp fmt = function | Null -> Format.fprintf fmt "\n%!" | Int i -> Format.fprintf fmt "%i\n%!" i + | Float f -> Format.fprintf fmt "%f\n%!" f ;; let to_string = function | Null -> "" | Int i -> string_of_int i + | Float f when Float.is_integer f -> string_of_int (int_of_float f) + | Float f -> string_of_float f ;; diff --git a/lib/pinc_compiler/compiler.ml b/lib/pinc_compiler/compiler.ml index b2e8c65..6662361 100644 --- a/lib/pinc_compiler/compiler.ml +++ b/lib/pinc_compiler/compiler.ml @@ -57,7 +57,12 @@ let rec compile_expr t (expr : Pinc_Types.Ast.expression) = let t = match op with | Pinc_Types.Operators.Binary.PLUS -> emit t Pinc_Bytecode.Instruction.I_Add - | _ -> assert false (* TODO *) + | Pinc_Types.Operators.Binary.MINUS -> emit t Pinc_Bytecode.Instruction.I_Sub + | Pinc_Types.Operators.Binary.DIV -> emit t Pinc_Bytecode.Instruction.I_Div + | Pinc_Types.Operators.Binary.TIMES -> emit t Pinc_Bytecode.Instruction.I_Mul + | Pinc_Types.Operators.Binary.MODULO -> emit t Pinc_Bytecode.Instruction.I_Mod + | Pinc_Types.Operators.Binary.POW -> emit t Pinc_Bytecode.Instruction.I_Pow + | _ -> assert false in t diff --git a/lib/pinc_vm/vm.ml b/lib/pinc_vm/vm.ml index f18c5ec..27fab5c 100644 --- a/lib/pinc_vm/vm.ml +++ b/lib/pinc_vm/vm.ml @@ -1,3 +1,4 @@ +open Pinc_Types open Pinc_Bytecode module Stack = Vm_stack @@ -12,6 +13,89 @@ let make (bytecode : Bytecode.t) = { bytecode; stack = Stack.make ~size:stack_size ~default_value:Value.Null } ;; +let rec execute_binary_operation t op = + let r = Stack.pop t.stack in + let l = Stack.pop t.stack in + let result = + match op with + | Operators.Binary.PLUS -> execute_binary_add l r + | Operators.Binary.MINUS -> execute_binary_sub l r + | Operators.Binary.TIMES -> execute_binary_times l r + | Operators.Binary.DIV -> execute_binary_div l r + | Operators.Binary.POW -> execute_binary_pow l r + | Operators.Binary.MODULO -> execute_binary_mod l r + | _ -> assert false + in + Stack.push t.stack result + +and execute_binary_add l r = + match (l, r) with + | Value.Int x, Value.Int y -> Value.Int (x + y) + | Value.Float x, Value.Int y -> Value.Float (x +. float_of_int y) + | Value.Int x, Value.Float y -> Value.Float (float_of_int x +. y) + | Value.Float x, Value.Float y -> Value.Float (x +. y) + | (Value.Int _ | Value.Float _), _ | _, (Value.Int _ | Value.Float _) | _ -> + raise_notrace (Invalid_argument "Trying to add non numeric values.") + +and execute_binary_sub l r = + match (l, r) with + | Value.Int x, Value.Int y -> Value.Int (x - y) + | Value.Float x, Value.Int y -> Value.Float (x -. float_of_int y) + | Value.Int x, Value.Float y -> Value.Float (float_of_int x -. y) + | Value.Float x, Value.Float y -> Value.Float (x -. y) + | (Value.Int _ | Value.Float _), _ | _, (Value.Int _ | Value.Float _) | _ -> + raise_notrace (Invalid_argument "Trying to subtract non numeric values.") + +and execute_binary_times l r = + match (l, r) with + | Value.Int x, Value.Int y -> Value.Int (x * y) + | Value.Float x, Value.Int y -> Value.Float (x *. float_of_int y) + | Value.Int x, Value.Float y -> Value.Float (float_of_int x *. y) + | Value.Float x, Value.Float y -> Value.Float (x *. y) + | (Value.Int _ | Value.Float _), _ | _, (Value.Int _ | Value.Float _) | _ -> + raise_notrace (Invalid_argument "Trying to multiply non numeric values.") + +and execute_binary_div l r = + match (l, r) with + | Value.Int _, Value.Int 0 + | Value.Int _, Value.Float 0. + | Value.Float _, Value.Float 0. + | Value.Float _, Value.Int 0 -> raise_notrace (Invalid_argument "Division by 0.") + | Value.Int x, Value.Int y -> Value.Float (float_of_int x /. float_of_int y) + | Value.Float x, Value.Int y -> Value.Float (x /. float_of_int y) + | Value.Int x, Value.Float y -> Value.Float (float_of_int x /. y) + | Value.Float x, Value.Float y -> Value.Float (x /. y) + | (Value.Int _ | Value.Float _), _ | _, (Value.Int _ | Value.Float _) | _ -> + raise_notrace (Invalid_argument "Trying to multiply non numeric values.") + +and execute_binary_mod l r = + match (l, r) with + | Value.Int _, Value.Int 0 + | Value.Int _, Value.Float 0. + | Value.Float _, Value.Float 0. + | Value.Float _, Value.Int 0 -> Value.Int 0 + | Value.Int a, Value.Int b -> Value.Int (a mod b) + | Value.Float a, Value.Float b -> Value.Float (a -. (a /. b *. b)) + | Value.Float a, Value.Int 1 -> Value.Float (fst (Float.modf a)) + | Value.Float a, Value.Int b -> + let b = float_of_int b in + Value.Float (a -. (a /. b *. b)) + | Int a, Float b -> + let a = float_of_int a in + Value.Float (a -. (a /. b *. b)) + | (Value.Int _ | Value.Float _), _ | _, (Value.Int _ | Value.Float _) | _ -> + raise_notrace (Invalid_argument "Trying to modulo non numeric values.") + +and execute_binary_pow l r = + match (l, r) with + | Value.Int l, Value.Int r -> Value.Float (float_of_int l ** float_of_int r) + | Value.Float l, Value.Float r -> Value.Float (l ** r) + | Value.Float l, Value.Int r -> Value.Float (l ** float_of_int r) + | Value.Int l, Value.Float r -> Value.Float (float_of_int l ** r) + | (Value.Int _ | Value.Float _), _ | _, (Value.Int _ | Value.Float _) | _ -> + raise_notrace (Invalid_argument "Trying to raise non numeric values.") +;; + let run t = let ip = ref 0 in let instruction_length = Bytes.length t.bytecode.instructions in @@ -23,15 +107,12 @@ let run t = | Instruction.I_Constant addr -> let constant = UInt16.Map.find addr t.bytecode.constants in Stack.push t.stack constant - | Instruction.I_Add -> - let r = Stack.pop t.stack in - let l = Stack.pop t.stack in - let result = - match (l, r) with - | Value.Int l, Value.Int r -> Value.Int (l + r) - | _ -> assert false (* TODO *) - in - Stack.push t.stack result + | Instruction.I_Add -> execute_binary_operation t Operators.Binary.PLUS + | Instruction.I_Sub -> execute_binary_operation t Operators.Binary.MINUS + | Instruction.I_Div -> execute_binary_operation t Operators.Binary.DIV + | Instruction.I_Mul -> execute_binary_operation t Operators.Binary.TIMES + | Instruction.I_Mod -> execute_binary_operation t Operators.Binary.MODULO + | Instruction.I_Pow -> execute_binary_operation t Operators.Binary.POW in ip := new_ip done; diff --git a/test/vm/add.pi b/test/vm/add.pi deleted file mode 100644 index 3f0b665..0000000 --- a/test/vm/add.pi +++ /dev/null @@ -1,3 +0,0 @@ -component Add { - 7 + 5 -} diff --git a/test/vm/binop.pi b/test/vm/binop.pi new file mode 100644 index 0000000..455c2cd --- /dev/null +++ b/test/vm/binop.pi @@ -0,0 +1,27 @@ +component Add { + 7 + 5 +} + +component Sub { + 7 - 5 +} + +component Div { + 7 / 5 +} + +component Mul { + 7 * 5 +} + +component Mod { + 7 % 5 +} + +component Pow { + 7 ** 5 +} + +component Math { + 5 * 3 + 2 / (6 + 4 * 8) ** 10 +} diff --git a/test/vm/run.t b/test/vm/run.t index c63b7cb..a999705 100644 --- a/test/vm/run.t +++ b/test/vm/run.t @@ -6,3 +6,67 @@ 0003 I_Constant 0x0002 0006 I_Add 0007 I_Pop + + $ NO_COLOR="1" print_vm . Sub + 2 + + $ NO_COLOR="1" print_instructions . Sub + 0000 I_Constant 0x0001 + 0003 I_Constant 0x0002 + 0006 I_Sub + 0007 I_Pop + + $ NO_COLOR="1" print_vm . Div + 1.4 + + $ NO_COLOR="1" print_instructions . Div + 0000 I_Constant 0x0001 + 0003 I_Constant 0x0002 + 0006 I_Div + 0007 I_Pop + + $ NO_COLOR="1" print_vm . Mul + 35 + + $ NO_COLOR="1" print_instructions . Mul + 0000 I_Constant 0x0001 + 0003 I_Constant 0x0002 + 0006 I_Mul + 0007 I_Pop + + $ NO_COLOR="1" print_vm . Mod + 2 + + $ NO_COLOR="1" print_instructions . Mod + 0000 I_Constant 0x0001 + 0003 I_Constant 0x0002 + 0006 I_Mod + 0007 I_Pop + + $ NO_COLOR="1" print_vm . Pow + 16807 + + $ NO_COLOR="1" print_instructions . Pow + 0000 I_Constant 0x0001 + 0003 I_Constant 0x0002 + 0006 I_Pow + 0007 I_Pop + + $ NO_COLOR="1" print_vm . Math + 15 + + $ NO_COLOR="1" print_instructions . Math + 0000 I_Constant 0x0001 + 0003 I_Constant 0x0002 + 0006 I_Mul + 0007 I_Constant 0x0003 + 0010 I_Constant 0x0004 + 0013 I_Constant 0x0005 + 0016 I_Constant 0x0006 + 0019 I_Mul + 0020 I_Add + 0021 I_Constant 0x0007 + 0024 I_Pow + 0025 I_Div + 0026 I_Add + 0027 I_Pop -- 2.54.0