diff --git a/lib/pinc_bytecode/instruction.ml b/lib/pinc_bytecode/instruction.ml index 5562687..4e7780c 100644 --- a/lib/pinc_bytecode/instruction.ml +++ b/lib/pinc_bytecode/instruction.ml @@ -25,6 +25,7 @@ type t = | I_Set_Global of Int32.t | I_Get_Global of Int32.t | I_Concat + | I_Dynamic_Array | I_Array of Int32.t | I_Record of Int32.t | I_Index @@ -35,6 +36,7 @@ type t = | I_Return | I_Set_Local of Int32.t | I_Get_Local of Int32.t + | I_Length | I_Debug_Print_Stack let byte = function @@ -74,6 +76,8 @@ let byte = function | I_Return -> 0x21 | I_Set_Local _ -> 0x22 | I_Get_Local _ -> 0x23 + | I_Length -> 0x24 + | I_Dynamic_Array -> 0x25 | I_Debug_Print_Stack -> 0xFF ;; @@ -114,6 +118,8 @@ let operands_length = function | I_Range | I_Range_Inclusive | I_Return + | I_Length + | I_Dynamic_Array | I_Debug_Print_Stack -> 0 ;; @@ -177,6 +183,8 @@ let decode bytes offset = | 0x23 -> let offset, addr = Int32.read_bytes bytes offset in (offset, I_Get_Local addr) + | 0x24 -> (offset, I_Length) + | 0x25 -> (offset, I_Dynamic_Array) | 0xFF -> (offset, I_Debug_Print_Stack) | _ -> raise_notrace @@ -220,7 +228,9 @@ let pp fmt = function | I_Return -> Format.fprintf fmt "I_Return" | I_Get_Local addr -> Format.fprintf fmt "I_Get_Local %a" Int32.pp addr | I_Set_Local addr -> Format.fprintf fmt "I_Set_Local %a" Int32.pp addr - | I_Debug_Print_Stack -> Format.fprintf fmt "I_Debug_Print_Stack (!nocommit)" + | I_Length -> Format.fprintf fmt "I_Length" + | I_Dynamic_Array -> Format.fprintf fmt "I_Dynamic_Array" + | I_Debug_Print_Stack -> Format.fprintf fmt "I_Debug_Print_Stack" ;; let to_bytes t = @@ -271,6 +281,8 @@ let to_bytes t = | I_Range | I_Range_Inclusive | I_Return + | I_Length + | I_Dynamic_Array | I_Debug_Print_Stack -> () in diff --git a/lib/pinc_compiler/compiler.ml b/lib/pinc_compiler/compiler.ml index a9d0739..eb43857 100644 --- a/lib/pinc_compiler/compiler.ml +++ b/lib/pinc_compiler/compiler.ml @@ -258,7 +258,8 @@ let rec compile_expr t (expr : Pinc_Types.Ast.expression) = let t = emit t @@ Pinc_Bytecode.Instruction.I_Call num_arguments in t | TagExpression _ -> raise_notrace TODO - | ForInExpression _ -> raise_notrace TODO + | ForInExpression { index; iterator; reverse; iterable; body } -> + compile_loop_expression t ~index ~iterator ~reverse ~iterable ~body | TemplateExpression node -> compile_template_node t node | BlockExpression stmts -> List.fold_left compile_stmt t stmts | ConditionalExpression { condition; consequent; alternate } -> @@ -405,6 +406,56 @@ and compile_conditional_expression t ~condition ~consequent ~alternate = in t +and compile_loop_expression t ~index ~iterator ~reverse:_ ~iterable ~body = + let (Lowercase_Id (iterator, _)) = iterator in + (* TODO: Add scope *) + let t, iterator_symbol = add_symbol t iterator in + let t, length_symbol = add_symbol t ".length" in + (* Index *) + let index_identifier = + match index with + | Some (Lowercase_Id (name, _)) -> name + | None -> ".index" + in + let t = emit_constant t @@ Pinc_Bytecode.Value.Int 0 in + let t, index_symbol = add_symbol t index_identifier in + let t = emit_set_symbol t index_symbol in + (* Iterable *) + let t = compile_expr t iterable in + let t, iterable_symbol = add_symbol t ".iterable" in + let t = emit_set_symbol t iterable_symbol in + let t = emit_get_symbol t iterable_symbol in + let t = emit t @@ Pinc_Bytecode.Instruction.I_Length in + let t = emit_set_symbol t length_symbol in + (* Set Iterator *) + let jump_address = Int32.of_int (Buffer.length @@ current_instructions t) in + let t = emit_get_symbol t iterable_symbol in + let t = emit_get_symbol t index_symbol in + let t = emit t @@ Pinc_Bytecode.Instruction.I_Index in + let t = emit_set_symbol t iterator_symbol in + (* Run body *) + let t = compile_expr t body in + let t = + if match_last_instruction t I_Pop then + remove_last_instruction t + else + t + in + (* Increment and set new index *) + let t = emit_constant t @@ Pinc_Bytecode.Value.Int 1 in + let t = emit_get_symbol t index_symbol in + let t = emit t @@ Pinc_Bytecode.Instruction.I_Add in + let t = emit_set_symbol t index_symbol in + (* Check array length and jump to loop start *) + let t = emit_get_symbol t index_symbol in + let t = emit_get_symbol t length_symbol in + let t = emit t @@ Pinc_Bytecode.Instruction.I_Greater_Equal in + let t = emit t @@ Pinc_Bytecode.Instruction.I_Jump_If_False jump_address in + (* Create array with values left on stack *) + let t = emit_get_symbol t length_symbol in + let t = emit t @@ Pinc_Bytecode.Instruction.I_Dynamic_Array in + t + and compile_stmt t (stmt : Pinc_Types.Ast.statement) = match stmt.statement_desc with | BreakStatement _ -> raise_notrace TODO diff --git a/lib/pinc_vm/vm.ml b/lib/pinc_vm/vm.ml index f5132c5..8556df4 100644 --- a/lib/pinc_vm/vm.ml +++ b/lib/pinc_vm/vm.ml @@ -389,6 +389,16 @@ let run t = | Instruction.I_Get_Global addr -> let value = Int32.Map.find addr t.globals in Stack.push t.stack value + | Instruction.I_Dynamic_Array -> + let value = Stack.pop t.stack in + let length = + match value with + | Value.Int i -> i + | _ -> assert false + in + let elements = Array.of_list @@ Stack.pop_n t.stack length in + let value = Value.Array elements in + Stack.push t.stack value | Instruction.I_Array length -> let elements = Array.of_list @@ Stack.pop_n t.stack (Int32.to_int length) in let value = Value.Array elements in @@ -423,6 +433,19 @@ let run t = let address = frame.base_pointer + Int32.to_int addr in let value = Stack.get t.stack address in Stack.push t.stack value + | Instruction.I_Length -> + let value = Stack.pop t.stack in + let len = + match value with + | Value.Array a -> Array.length a + | Value.String s -> String.length s + | Record r -> StringMap.cardinal r + | _ -> + raise_notrace + @@ Invalid_argument + "Trying to call length a non array, string or record value" + in + Stack.push t.stack (Value.Int len) in () done; diff --git a/test/vm/loop.pi b/test/vm/loop.pi new file mode 100644 index 0000000..5224003 --- /dev/null +++ b/test/vm/loop.pi @@ -0,0 +1,6 @@ +component Loop { + let array = [1, 2, 3, 4, 5]; + for (index, item in array) { + item * index + } +} diff --git a/test/vm/loop.t b/test/vm/loop.t new file mode 100644 index 0000000..f46b5d6 --- /dev/null +++ b/test/vm/loop.t @@ -0,0 +1,2 @@ + $ NO_COLOR="1" print_vm . Loop + 0 2 6 12 20 diff --git a/test/vm/loop_instructions.t b/test/vm/loop_instructions.t new file mode 100644 index 0000000..2a9c6ad --- /dev/null +++ b/test/vm/loop_instructions.t @@ -0,0 +1,45 @@ + $ NO_COLOR="1" print_instructions . Loop + [CONSTANTS] + 0x00000000 (00000000) : 1 + 0x00000001 (00000001) : 2 + 0x00000002 (00000002) : 3 + 0x00000003 (00000003) : 4 + 0x00000004 (00000004) : 5 + 0x00000005 (00000005) : 0 + 0x00000006 (00000006) : 1 + + [INSTRUCTIONS] + 0000 I_Constant 0x00000000 (00000000) + 0005 I_Constant 0x00000001 (00000001) + 0010 I_Constant 0x00000002 (00000002) + 0015 I_Constant 0x00000003 (00000003) + 0020 I_Constant 0x00000004 (00000004) + 0025 I_Array 5 + 0030 I_Set_Global 0x00000000 (00000000) + 0035 I_Null + 0036 I_Pop + 0037 I_Constant 0x00000005 (00000005) + 0042 I_Set_Global 0x00000003 (00000003) + 0047 I_Get_Global 0x00000000 (00000000) + 0052 I_Set_Global 0x00000004 (00000004) + 0057 I_Get_Global 0x00000004 (00000004) + 0062 I_Length + 0063 I_Set_Global 0x00000002 (00000002) + 0068 I_Get_Global 0x00000004 (00000004) + 0073 I_Get_Global 0x00000003 (00000003) + 0078 I_Index + 0079 I_Set_Global 0x00000001 (00000001) + 0084 I_Get_Global 0x00000001 (00000001) + 0089 I_Get_Global 0x00000003 (00000003) + 0094 I_Mul + 0095 I_Constant 0x00000006 (00000006) + 0100 I_Get_Global 0x00000003 (00000003) + 0105 I_Add + 0106 I_Set_Global 0x00000003 (00000003) + 0111 I_Get_Global 0x00000003 (00000003) + 0116 I_Get_Global 0x00000002 (00000002) + 0121 I_Greater_Equal + 0122 I_Jump_If_False 0x00000044 (00000068) + 0127 I_Get_Global 0x00000002 (00000002) + 0132 I_Dynamic_Array + 0133 I_Pop