diff --git a/.ocamlformat b/.ocamlformat new file mode 100644 index 0000000..1d7191d --- /dev/null +++ b/.ocamlformat @@ -0,0 +1,2 @@ +profile = janestreet +version = 0.27.0 \ No newline at end of file diff --git a/src/driver_util.ml b/src/driver_util.ml index 031eee9..daf262f 100644 --- a/src/driver_util.ml +++ b/src/driver_util.ml @@ -246,6 +246,8 @@ let riscv_gen (core : Mcore.t) = core |> Riscv_generate.ssa_of_mcore |> Riscv_opt_gather.opt + |> Riscv_virtasm_generate.virtasm_of_ssa + |> Riscv_reg_alloc.reg_alloc |> Riscv.generate let link_core ~(shrink_wasm : bool) ~(elim_unused_let : bool) diff --git a/src/riscv_generate.ml b/src/riscv_generate.ml index 4caf370..cfa3281 100644 --- a/src/riscv_generate.ml +++ b/src/riscv_generate.ml @@ -38,6 +38,9 @@ let variants = Hashtbl.create 64 (** The enum type that each variant belongs to. *) let belong = Hashtbl.create 64 +(** Init function exprs**) +let init_exprs = Basic_vec.empty () + (** Get offset of the `pos`-th field in the record type called `name`. *) (** Note that there are n+1 fields for offsets in a n-field variant; the last one is the total size. *) let offsetof ty pos = Hashtbl.find offset_table (ty, pos) @@ -506,7 +509,7 @@ let update_types ({ defs; _ }: Mtype.defs) = let sizes = List.map (fun x -> sizeof x) types in let offsets = 0 :: Basic_lst.cumsum sizes in - (* Record the correspondence between name and index *) + (* Record the correspondence between nameand index *) let variant_name = Printf.sprintf "%s.%s" name tag.name_ in Hashtbl.add belong variant_name name; Basic_vec.push tag_offsets offsets @@ -1313,6 +1316,11 @@ let generate_vtables () = Converts given `expr` into a list of SSA instructions, along with the variable in which the result of this expression is stored. *) +let convert_expr_no_ret (expr: Mcore.expr) = + let ssa = Basic_vec.empty () in + let _ = do_convert ssa expr in + Basic_vec.map_into_list ssa (fun x -> x) + let convert_expr (expr: Mcore.expr) = let ssa = Basic_vec.empty () in let return = do_convert ssa expr in @@ -1448,7 +1456,6 @@ let convert_lambda (expr: Mcore.expr) = | w -> w - (** Store captured variables for each closure in `captured` *) let process_closure ((fn: Mcore.fn), (name: Ident.t)) = let free_ident = Mcore_util.free_vars ~exclude:(Ident.Set.singleton name) fn in @@ -1484,6 +1491,11 @@ let convert_toplevel _start (top: Mcore.top_item) = in match top with + (* Init function *) + | Ctop_expr { expr; _ } -> + let expr = convert_expr_no_ret expr in + Basic_vec.append init_exprs @@ Basic_vec.of_list expr; + [] | Ctop_fn { binder; func; export_info_; _ } -> let fn = Ident.to_string binder in let args = List.map var_of_param func.params in @@ -1525,8 +1537,6 @@ let convert_toplevel _start (top: Mcore.top_item) = | Inline_code_text _ | Inline_code_sexp _ -> failwith "RISC-V target does not support inline WASM" | Import _ -> failwith "riscv_ssa.ml: import should have been eliminated in link stage") - - | _ -> failwith "TODO: riscv_ssa.ml: don't know this toplevel" let find_functions (top: Mcore.top_item) = match top with @@ -1589,6 +1599,7 @@ let ssa_of_mcore (core: Mcore.t) = (* Add _start *) let unused = new_temp Mtype.T_unit in + Basic_vec.append _start init_exprs; Basic_vec.push _start (Call { rd = unused; fn = "main"; args = [] }); Basic_vec.push _start (Return unused); diff --git a/src/riscv_reg.ml b/src/riscv_reg.ml new file mode 100644 index 0000000..441eebc --- /dev/null +++ b/src/riscv_reg.ml @@ -0,0 +1,199 @@ +(** Registers for RV64GC **) + +(* Define a label type*) +type label_t = string + +(** +Defines an immediate value type. + +Immediate values can either be an integer (`IntImm`) or a floating-point number (`FloatImm`). +*) +type imm_t = + | IntImm of int (* Integer immediate value *) + | FloatImm of float (* Floating-point immediate value *) + +(** +Defines a slot type for both virtual and physical registers. + +This type encapsulates different kinds of registers, including general-purpose registers, floating-point registers, +and specific slots representing values like `Unit` (no return value). +*) +type slot_t = + | Unit (* Represents no return value, often used in function calls or returns *) + | Slot of int + | FSlot of int + | Reg of reg_t + | FReg of freg_t + +and ret_type = + | IntRet + | FloatRet + | UnitRet + +and reg_t = + | Zero (* zero register *) + | Ra (* caller return address *) + | Sp (* caller (S0) stack pointer *) + | Gp (* global pointer *) + | Tp (* thread pointer *) + | T0 (* caller temporary register *) + | T1 + | T2 + | Fp (* callee stack bottom register *) + | S1 (* callee saved register *) + | A0 (* caller argument register *) + | A1 + | A2 + | A3 + | A4 + | A5 + | A6 + | A7 + | S2 (* callee saved register *) + | S3 + | S4 + | S5 + | S6 + | S7 + | S8 + | S9 + | S10 + | S11 + | T3 (* caller temporary register *) + | T4 + | T5 + | T6 (* caller swap register *) + +and freg_t = + | Ft0 (* caller floating-point temporary register *) + | Ft1 + | Ft2 + | Ft3 + | Ft4 + | Ft5 + | Ft6 + | Ft7 + | Fs0 (* callee floating-point saved register *) + | Fs1 + | Fa0 (* caller floating-point argument register *) + | Fa1 + | Fa2 + | Fa3 + | Fa4 + | Fa5 + | Fa6 + | Fa7 + | Fs2 (* callee floating-point saved register *) + | Fs3 + | Fs4 + | Fs5 + | Fs6 + | Fs7 + | Fs8 + | Fs9 + | Fs10 + | Fs11 + | Ft8 + | Ft9 + | Ft10 + | Ft11 (* caller swap floating-point register *) + +(* Convert reg_t to string representation *) +let reg_to_string r = + match r with + | Zero -> "zero" + | Ra -> "ra" + | Sp -> "sp" + | Gp -> "gp" + | Tp -> "tp" + | T0 -> "t0" + | T1 -> "t1" + | T2 -> "t2" + | Fp -> "s0" + | S1 -> "s1" + | A0 -> "a0" + | A1 -> "a1" + | A2 -> "a2" + | A3 -> "a3" + | A4 -> "a4" + | A5 -> "a5" + | A6 -> "a6" + | A7 -> "a7" + | S2 -> "s2" + | S3 -> "s3" + | S4 -> "s4" + | S5 -> "s5" + | S6 -> "s6" + | S7 -> "s7" + | S8 -> "s8" + | S9 -> "s9" + | S10 -> "s10" + | S11 -> "s11" + | T3 -> "t3" + | T4 -> "t4" + | T5 -> "t5" + | T6 -> "t6" +;; + +(* Convert freg_t to string representation *) +let freg_to_string fr = + match fr with + | Ft0 -> "ft0" + | Ft1 -> "ft1" + | Ft2 -> "ft2" + | Ft3 -> "ft3" + | Ft4 -> "ft4" + | Ft5 -> "ft5" + | Ft6 -> "ft6" + | Ft7 -> "ft7" + | Fs0 -> "fs0" + | Fs1 -> "fs1" + | Fa0 -> "fa0" + | Fa1 -> "fa1" + | Fa2 -> "fa2" + | Fa3 -> "fa3" + | Fa4 -> "fa4" + | Fa5 -> "fa5" + | Fa6 -> "fa6" + | Fa7 -> "fa7" + | Fs2 -> "fs2" + | Fs3 -> "fs3" + | Fs4 -> "fs4" + | Fs5 -> "fs5" + | Fs6 -> "fs6" + | Fs7 -> "fs7" + | Fs8 -> "fs8" + | Fs9 -> "fs9" + | Fs10 -> "fs10" + | Fs11 -> "fs11" + | Ft8 -> "ft8" + | Ft9 -> "ft9" + | Ft10 -> "ft10" + | Ft11 -> "ft11" +;; + +let to_string (s : slot_t) : string = + match s with + | Slot i -> Printf.sprintf "%%%d" i + | FSlot i -> Printf.sprintf "%%f%d" i + | Reg r -> reg_to_string r + | FReg fr -> freg_to_string fr + | Unit -> "_" +;; + +(** Counter of temporaries. *) +let slot_cnt = ref 0 + +let fslot_cnt = ref 0 + +let new_slot () = + let i = !slot_cnt in + slot_cnt := i + 1; + Slot i +;; + +let new_fslot () = + let i = !fslot_cnt in + fslot_cnt := i + 1; + FSlot i +;; diff --git a/src/riscv_reg_alloc.ml b/src/riscv_reg_alloc.ml new file mode 100644 index 0000000..69a704a --- /dev/null +++ b/src/riscv_reg_alloc.ml @@ -0,0 +1,12 @@ +open Riscv_virtasm + +let reg_alloc (vprog : vprog_t) = + let vprog : vprog_t = + { blocks = VBlockMap.empty + ; funcs = VFuncMap.empty + ; consts = VSymbolMap.empty + ; loop_vars = VBlockMap.empty + } + in + vprog +;; diff --git a/src/riscv_ssa.ml b/src/riscv_ssa.ml index 5954e49..21cfbb7 100644 --- a/src/riscv_ssa.ml +++ b/src/riscv_ssa.ml @@ -230,25 +230,31 @@ and t = let pointer_size = 8 (* This is the size of their representations, not the actual size. *) -let sizeof ty = match ty with -| Mtype.T_bool -> 1 -| Mtype.T_byte -> 1 -| Mtype.T_bytes -> pointer_size -| Mtype.T_char -> 2 -| Mtype.T_double -> 8 -| Mtype.T_float -> 4 -| Mtype.T_func _ -> pointer_size -| Mtype.T_int -> 4 -| Mtype.T_int64 -> 8 -| Mtype.T_string -> pointer_size -| Mtype.T_uint -> 4 -| Mtype.T_uint64 -> 8 -| Mtype.T_unit -> 0 -| Mtype.T_tuple _ -> pointer_size -| Mtype.T_constr id -> pointer_size -| Mtype.T_fixedarray _ -> pointer_size -| _ -> failwith "riscv_ssa.ml: cannot calculate size" - +let rec sizeof ty = + match ty with + | Mtype.T_bool -> 1 + | Mtype.T_byte -> 1 + | Mtype.T_bytes -> pointer_size + | Mtype.T_char -> 2 + | Mtype.T_double -> 8 + | Mtype.T_float -> 4 + | Mtype.T_func _ -> pointer_size + | Mtype.T_int -> 4 + | Mtype.T_int64 -> 8 + | Mtype.T_string -> pointer_size + | Mtype.T_uint -> 4 + | Mtype.T_uint64 -> 8 + | Mtype.T_unit -> 0 (* Unit type has no size *) + | Mtype.T_tuple _ -> pointer_size + | Mtype.T_constr id -> pointer_size + | Mtype.T_fixedarray _ -> pointer_size + | Mtype.T_trait _ -> pointer_size + (* | Mtype.T_optimized_option { elem } -> pointer_size *) + (* | Mtype.T_any { name } -> pointer_size *) + (* | Mtype.T_maybe_uninit x -> sizeof x *)(*Same size as the contained type *) + (* | Mtype.T_error_value_result { ok; err; id } -> sizeof ok + sizeof err + pointer_size *) + | _ -> failwith ("riscv_ssa.ml: cannot calculate size for type: "^ Mtype.to_string ty) +;; (** Emits SSA form. We choose a less human-readable form to facilitate verifier. *) let to_string t = diff --git a/src/riscv_virtasm.ml b/src/riscv_virtasm.ml new file mode 100644 index 0000000..8c991b7 --- /dev/null +++ b/src/riscv_virtasm.ml @@ -0,0 +1,320 @@ +open Riscv_reg + +let outfile = Printf.sprintf "%s.deb" !Driver_config.Linkcore_Opt.output_file + +let debshow (x : string) : unit = + (* Basic_io.write outfile x *) + print_endline @@ "[DEBUG]" ^ x +;; + +let debsexp (x : S.t) : unit = + (* Basic_io.write_s outfile x *) + print_endline @@ "[DEBUG]" ^ S.to_string x +;; + +let deblist (listn : string) (f : 'a -> string) (lst : 'a list) : unit = + let list_str = + lst + |> List.map f (* Apply the function to each element *) + |> String.concat "; " (* Concatenate the results with "; " *) + in + print_endline @@ "[DEBUG] " ^ listn ^ ": ["; + print_endline @@ " " ^ list_str ^ ";"; + print_endline @@ "]" +;; + +(** Similar to R-type instructions in RISC-V. *) +type r_slot = + { rd : slot_t + ; rs1 : slot_t + ; rs2 : slot_t + } + +(** R-type instructions for floating-point registers. *) +type r_fslot = + { frd : slot_t + ; frs1 : slot_t + ; frs2 : slot_t + } + +(** I-type, with one destination register, one source and one immediate. *) +type i_slot = + { rd : slot_t + ; rs1 : slot_t + ; imm : imm_t + } + +(** Defines a single floating-point register assignment with a destination register `frd`. *) +type single_fslot = { frd : slot_t } + +(** Defines a direct assignment between general-purpose and floating-point register.*) +type assign_direct = + { frd : slot_t + ; rs : slot_t + } + +type assign_slot = + { rd : slot_t + ; rs : slot_t + } + +type assign_fslot = + { frd : slot_t + ; frs : slot_t + } + +(** For special floating-point operation*) +type triple_fslot = + { frd : slot_t + ; frs1 : slot_t + ; frs2 : slot_t + ; frs3 : slot_t + } + +(** Immediate value `imm` to the destination register `rd`. *) +type assign_int64 = + { rd : slot_t + ; imm : imm_t + } + +(** Defines an assignment of a label (address or function) to a register `rd`. *) +type assign_label = + { rd : slot_t + ; label : label_t + } + +(** +Defines a conversion between floating-point and integer registers. + +Converts the value in the source floating-point register `frs` to the destination integer register `rd`. +*) +type convert_slot = + { rd : slot_t + ; frs : slot_t + } + +(** +Defines a conversion from an integer register to a floating-point register. + +Converts the integer value from `rs` to the destination floating-point register `frd`. +*) +type convert_fslot = + { frd : slot_t + ; rs : slot_t + } + +(** +Defines a comparison between two floating-point registers `frs1` and `frs2`, + with the result stored in the destination register `rd`. +*) +type compare_fslot = + { rd : slot_t + ; frs1 : slot_t + ; frs2 : slot_t + } + +(** Calls function named `fn` with arguments `args`, and store the result in `rd`. *) +type call_data = + { rd : slot_t + ; fn : label_t + ; args : slot_t list + ; fargs : slot_t list + } + +(** Call function pointer with address `rs` and arguments `args`, and returns in `rd` *) +type call_indirect = + { rd : slot_t + ; fn : slot_t + ; args : slot_t list + ; fargs : slot_t list + } + +(** +Similar to `ld` and `st` in RISC-V. + +`rd` and `rs` have different meanings in loads and stores: +We load `byte` bytes from `rs` into `rd`, +and store `byte` bytes from `rd` into `rs`. +*) +type mem_slot = + { rd : slot_t + ; base : slot_t + ; offset : imm_t + } + +type mem_fslot = + { frd : slot_t + ; base : slot_t + ; offset : imm_t + } + +(** +Defines a stack slot used for register spilling and reloading. +The `target` is the register being spilled/reloaded, + and `origin` is the original register it corresponds to. +Used in the final assembly generation to manage stack offsets. +*) + +type stack_slot = + { target : slot_t + ; origin : slot_t + } + +type stack_fslot = + { target : slot_t + ; origin : slot_t + } + +(** Virtual RISC-V Instructions *) +type t = + (* Integer Arithmetic Instructions *) + | Add of r_slot + | Sub of r_slot + | Addi of i_slot + (* Logical and Shift Instructions *) + | And of r_slot + | Or of r_slot + | Xor of r_slot + | Sll of r_slot (* shift left logical *) + | Srl of r_slot (* shift right logical *) + | Sra of r_slot (* shift right arithmetic *) + | Slli of i_slot (* shift left logical immediate *) + | Srli of i_slot (* shift right logical immediate *) + | Srai of i_slot (* shift right arithmetic immediate *) + (* Multiplication and Division Instructions *) + | Mul of r_slot + | Div of r_slot (* signed divide *) + | Divu of r_slot (* unsigned divide *) + | Rem of r_slot (* signed remainder *) + | Remu of r_slot (* unsigned remainder *) + (* Memory Access Instructions *) + | Lw of mem_slot (* load word 32-bit *) + | Ld of mem_slot (* load doubleword 64-bit *) + | Sw of mem_slot (* store word 32-bit *) + | Sd of mem_slot (* store doubleword 64-bit *) + (* Floating-Point Arithmetic Instructions *) + | FaddD of r_fslot + | FsubD of r_fslot + | FmulD of r_fslot + | FdivD of r_fslot + | FmaddD of triple_fslot (* fmadd.d => f[rd] = f[rs1]×f[rs2]+f[rs3] *) + | FmsubD of triple_fslot (* fmsub.d => f[rd] = f[rs1]×f[rs2]-f[rs3] *) + | FnmaddD of triple_fslot (* fnmadd.d => f[rd] = -f[rs1]×f[rs2]+f[rs3] *) + | FnmsubD of triple_fslot (* fnmsub.d => f[rd] = -f[rs1]×f[rs2]-f[rs3] *) + (* Floating-Point Compare Instructions *) + | FeqD of compare_fslot (* == *) + | FltD of compare_fslot (* < *) + | FleD of compare_fslot (* <= *) + (* Floating-Point Conversion *) + | FcvtDW of convert_fslot (* convert int32 to float *) + | FcvtDL of convert_fslot (* convert int64 to float *) + | FcvtLD of convert_slot (* convert float to int64 *) + | FcvtWDRtz of convert_slot (* convert float to int, round towards zero *) + (* Floating-Point Misc Instructions *) + | FsqrtD of assign_fslot (* square root *) + | FabsD of assign_fslot (* absolute value *) + (* Floating-Point Memory Instructions *) + | Fld of mem_fslot (* load doubleword 64-bit *) + | Fsd of mem_fslot (* store doubleword 64-bit *) + (* Movement Instructions *) + | La of assign_label (* load address *) + | Li of assign_int64 (* load immediate *) + | Neg of assign_slot + | Mv of assign_slot + | FnegD of assign_fslot + | FmvD of assign_fslot + | FmvDX of assign_direct (* move integer slot -> float slot (bitwise) *) + | FmvDXZero of single_fslot (* move x0 -> float slot (bitwise), i.e. 0.0 *) + (* Call / Function Invocation Instructions *) + | Call of call_data + | CallIndirect of call_indirect + (* Register Allocation Directives *) + | Spill of stack_slot + | Reload of stack_slot + | FSpill of stack_fslot + | FReload of stack_fslot + +(** Branching is done based on the comparison of registers `rs1`, `rs2` or a single register `rs`. *) +type branch_slot = + { rs1 : slot_t + ; rs2 : slot_t + ; ifso : label_t + ; ifnot : label_t + } + +(** rd stores return address, label is the jump target *) +type jal_label = + { rd : slot_t + ; label : label_t + } + +(** jump address is calculated by rs1 + offset, rd stores return address *) +type jalr_label = + { rd : slot_t + ; rs1 : slot_t + ; offset : imm_t + } + +(** These include conditional branches, unconditional jumps, function returns, and tail calls. *) +type term_t = + | Beq of branch_slot (* Branch if equal *) + | Bne of branch_slot (* Branch if not equal *) + | Blt of branch_slot (* Branch if less than *) + | Bge of branch_slot (* Branch if greater than or equal *) + | Bltu of branch_slot (* Branch if less than unsigned *) + | Bgeu of branch_slot (* Branch if greater than or equal unsigned *) + | Jal of label_t (* jump and link (store return address) *) + | Jalr of jalr_label (* jump and link register (store return address) *) + | TailCall of call_data + | TailCallIndirect of call_indirect + | Ret of slot_t (* Unit for no return*) + +(* Note: *) +(* Riscv Virtual ASM still retains the structure of control flow (CFG), *) +(* while its VirtualASM instructions are closer to real assembly. *) +(* It also includes pseudo-instructions for convenient register allocation and defines the slot_t type, *) +(* which aims to allow virtual registers of Slots to coexist with real registers of Regs. *) + +(* Key int*) +module IntMap = Basic_map_int +module VBlockMap = Basic_map_int +module VSymbolMap = Basic_map_int + +(* Key string*) +module StringMap = Basic_map_string +module VFuncMap = Basic_map_string + +(* Vector alias*) +module Vec = Basic_vec + +type vblock_label = int +type vfunc_label = string +type vsymbol_label = int + +(* Count for VirtSymbol*) +let vsymbol_cnt = ref 0 + +(** VirtRvBlock*) +type vblock_t = + { body : t Vec.t + ; term : term_t (* Single Terminator*) + ; preds : vblock_label Vec.t (* Predecessors*) + } + +(** VirtRvFunc*) +type vfunc_t = + { result : ret_type option + ; args : slot_t list + ; fargs : slot_t list + ; entry : vblock_label + } + +(** VirtRvProg*) +type vprog_t = + { blocks : vblock_t VBlockMap.t + ; funcs : vfunc_t VFuncMap.t + ; consts : imm_t VSymbolMap.t + ; loop_vars : slot_t VBlockMap.t + (* Loop internal variables - + used for register allocation special identification*) + } diff --git a/src/riscv_virtasm_generate.ml b/src/riscv_virtasm_generate.ml new file mode 100644 index 0000000..2fe4f67 --- /dev/null +++ b/src/riscv_virtasm_generate.ml @@ -0,0 +1,13 @@ +open Riscv_virtasm +module Ssa = Riscv_ssa + +let virtasm_of_ssa (ssa : Ssa.t list) = + let vprog : vprog_t = + { blocks = VBlockMap.empty + ; funcs = VFuncMap.empty + ; consts = VSymbolMap.empty + ; loop_vars = VBlockMap.empty + } + in + vprog +;; diff --git a/test/interpreter.cpp b/test/interpreter.cpp index f456fb2..e7cb211 100644 --- a/test/interpreter.cpp +++ b/test/interpreter.cpp @@ -8,6 +8,7 @@ #include #include #include +#include const int unit = unit; diff --git a/test/src/array01/array01.ans b/test/src/array01/array01.ans new file mode 100644 index 0000000..e006bb0 --- /dev/null +++ b/test/src/array01/array01.ans @@ -0,0 +1,8 @@ +5 +2 +push and change element: +[1, 10, 3, 4, 5, 6] +concat two arrays: +[1, 10, 3, 4, 5, 6, 6, 7, 8, 9, 10] +spread arrays: +[1, 10, 3, 4, 5, 6, 1000, 2000, 6, 7, 8, 9, 10, 3000, 4000] diff --git a/test/src/array01/array01.mbt b/test/src/array01/array01.mbt new file mode 100644 index 0000000..f0d56c8 --- /dev/null +++ b/test/src/array01/array01.mbt @@ -0,0 +1,32 @@ +fn main { + // Define two array of Int. + let arr1 : Array[Int] = [1, 2, 3, 4, 5] + + // Let compiler infer the type. + let arr2 = [6, 7, 8, 9, 10] + + // Get the length of the array. + println(arr1.length()) + + // Access the element of the array. + println(arr1[1]) + + // We change the elements inside the array. + // The binding of arr1 is immutable, it still points to the same array. + // The internal mutability of the array is defined by the standard library and + // is not affected by the binding. + arr1.push(6) + arr1[1] = 10 + println("push and change element:") + println(arr1) + + // Concat two arrays. + let arr = arr1 + arr2 + println("concat two arrays:") + println(arr) + + // We can also use the spread operator to concatenate arrays. + let arr3 = [..arr1, 1000, 2000, ..arr2, 3000, 4000] + println("spread arrays:") + println(arr3) +} \ No newline at end of file diff --git a/test/src/map01/map01.ans b/test/src/map01/map01.ans new file mode 100644 index 0000000..ab565f6 --- /dev/null +++ b/test/src/map01/map01.ans @@ -0,0 +1,4 @@ +{"key1": 1, "key2": 2, "key3": 3} +Some(1) +true +{"key1": 10, "key2": 2, "key3": 3} diff --git a/test/src/map01/map01.mbt b/test/src/map01/map01.mbt new file mode 100644 index 0000000..3e21144 --- /dev/null +++ b/test/src/map01/map01.mbt @@ -0,0 +1,16 @@ +fn main { + // Create a map by map literal + let map1 = { "key1": 1, "key2": 2, "key3": 3 } + println(map1) + + // Access a value by key + println(map1["key1"]) + + // You can also create a map by Map::of, from a list of key-value pairs + let map2 = Map::of([("key1", 1), ("key2", 2), ("key3", 3)]) + println(map1 == map2) + + // Update a value by key + map1["key1"] = 10 + println(map1) +} diff --git a/test/src/match01/match01.ans b/test/src/match01/match01.ans new file mode 100644 index 0000000..8564ff7 --- /dev/null +++ b/test/src/match01/match01.ans @@ -0,0 +1,10 @@ +other +one or two +one or two +three +four +other +other +other +other +other \ No newline at end of file diff --git a/test/src/match01/match01.mbt b/test/src/match01/match01.mbt new file mode 100644 index 0000000..d9f1b9f --- /dev/null +++ b/test/src/match01/match01.mbt @@ -0,0 +1,18 @@ +fn main { + for i =0 ; i< 6; i=i+1{ + match i { + 1 | 2 => { + println("one or two"); + } + 3 => { + println("three"); + } + 4 => { + println("four"); + } + _ => { + println("other"); + } + } + } +} \ No newline at end of file diff --git a/test/src/match02/match02.ans b/test/src/match02/match02.ans new file mode 100644 index 0000000..07cd637 --- /dev/null +++ b/test/src/match02/match02.ans @@ -0,0 +1,11 @@ +other +one or two +one or two +one or two +one or two +three +three +four +four +other +other diff --git a/test/src/match02/match02.mbt b/test/src/match02/match02.mbt new file mode 100644 index 0000000..ad9d948 --- /dev/null +++ b/test/src/match02/match02.mbt @@ -0,0 +1,23 @@ +fn main { + for i =0 ; i< 6; i=i+1{ + let a = match i { + 1 | 2 => { + println("one or two"); + "one or two" + } + 3 => { + println("three"); + "three" + } + 4 => { + println("four"); + "four" + } + _ => { + println("other"); + "other" + } + } + println(a) + } +} \ No newline at end of file diff --git a/test/src/record/record.ans b/test/src/record/record.ans new file mode 100644 index 0000000..0b1e618 --- /dev/null +++ b/test/src/record/record.ans @@ -0,0 +1,3 @@ +0 +John Doe +john@doe.name diff --git a/test/src/record/record.mbt b/test/src/record/record.mbt new file mode 100644 index 0000000..8d5c79d --- /dev/null +++ b/test/src/record/record.mbt @@ -0,0 +1,14 @@ +struct User { + mut id: Int + name: String + mut email: String +} + +fn main { + let u = { id: 233, name: "John Doe", email: "john@doe.com" } + u.email = "john@doe.name" + u.id = 0 + println(u.id) + println(u.name) + println(u.email) +} \ No newline at end of file diff --git a/test/src/topexpr/topexpr.ans b/test/src/topexpr/topexpr.ans new file mode 100644 index 0000000..3f9cdde --- /dev/null +++ b/test/src/topexpr/topexpr.ans @@ -0,0 +1,2 @@ +Init: Hello world +Main: Hello, World! diff --git a/test/src/topexpr/topexpr.mbt b/test/src/topexpr/topexpr.mbt new file mode 100644 index 0000000..911ef0b --- /dev/null +++ b/test/src/topexpr/topexpr.mbt @@ -0,0 +1,8 @@ + +fn init { + println("Init: Hello world") +} + +fn main { + println("Main: Hello, World!") +}