diff --git a/Makefile b/Makefile index 6c4cb8a..105551d 100644 --- a/Makefile +++ b/Makefile @@ -1,17 +1,16 @@ -aqaml: main.ml +SRC=hashmap.ml hashtbl.ml main.ml + +aqaml: $(SRC) ocamlopt $^ -o $@ -test: aqaml test.sh utility.o _test.ml +test: aqaml test.sh utility.o ./test.sh utility.o: utility.c gcc -Wall -std=c11 -c -o $@ $^ -_test.ml: stdlib.ml test.ml - cat stdlib.ml test.ml > $@ - -_self_aqaml: stdlib.ml main.ml utility.o aqaml - cat stdlib.ml main.ml | ./aqaml > _self_aqaml.s +_self_aqaml: stdlib.ml $(SRC) utility.o aqaml + ./aqaml stdlib.ml $(SRC) > _self_aqaml.s gcc _self_aqaml.s utility.o -o $@ strip $@ @@ -19,11 +18,11 @@ _self_test.sh: test.sh cat test.sh | sed "s#./aqaml#./_self_aqaml#g" > _self_test.sh chmod +x _self_test.sh -self_test: _self_aqaml _test.ml _self_test.sh utility.o +self_test: _self_aqaml _self_test.sh utility.o ./_self_test.sh -_selfself_aqaml: stdlib.ml main.ml utility.o _self_aqaml - cat stdlib.ml main.ml | ./_self_aqaml > _selfself_aqaml.s +_selfself_aqaml: stdlib.ml $(SRC) utility.o _self_aqaml + ./_self_aqaml stdlib.ml $(SRC) > _selfself_aqaml.s gcc _selfself_aqaml.s utility.o -o $@ strip $@ @@ -31,11 +30,12 @@ _selfself_test.sh: test.sh cat test.sh | sed "s#./aqaml#./_selfself_aqaml#g" > _selfself_test.sh chmod +x _selfself_test.sh -selfself_test: _selfself_aqaml _test.ml _selfself_test.sh utility.o +selfself_test: _selfself_aqaml _selfself_test.sh utility.o ./_selfself_test.sh cmp _self_aqaml _selfself_aqaml clean: - rm -f _test.ml _self_test.sh _self_aqaml _self_aqaml.s _test.o _test.s aqaml utility.o _selfself_aqaml _selfself_aqaml.s _selfself_test.sh + rm -f _self_test.sh _self_aqaml _self_aqaml.s _test.o _test.s aqaml utility.o _selfself_aqaml _selfself_aqaml.s _selfself_test.sh + rm -f $(SRC:.ml=.cmi) $(SRC:.ml=.cmx) $(SRC:.ml=.o) $(SRC:.ml=.cmo) .PHONY: test self_test clean selfself_test diff --git a/hashmap.ml b/hashmap.ml new file mode 100644 index 0000000..462387a --- /dev/null +++ b/hashmap.ml @@ -0,0 +1,48 @@ +type ('a, 'b) t = ('a * 'b) list + +let empty = [] + +let add k v m = (k, v) :: m + +let rec find k = function + | (k', v') :: xs -> if k = k' then v' else find k xs + | [] -> raise Not_found + +let mem k m = + try + ignore (find k m) ; + true + with Not_found -> false + +let merge f m1 m2 = + let src = ref empty in + let rec iter_m1 = function + | (k, v) :: xs -> + ( try src := add k (Some v, Some (find k m2)) !src with Not_found -> + src := add k (Some v, None) !src ) ; + iter_m1 xs + | [] -> () + in + let rec iter_m2 = function + | (k, v) :: xs -> + if not (mem k m1) then src := add k (None, Some v) !src ; + iter_m2 xs + | [] -> () + in + iter_m1 m1 ; + iter_m2 m2 ; + List.fold_left + (fun m (k, (l, r)) -> match f k l r with None -> m | Some v -> add k v m) + empty !src + +let union f m1 m2 = + merge + (fun k l r -> + match (l, r) with + | None, None -> None + | Some v, None -> l + | None, Some v -> r + | Some v1, Some v2 -> f k v1 v2 ) + m1 m2 + +let cardinal m = List.length m diff --git a/hashtbl.ml b/hashtbl.ml new file mode 100644 index 0000000..147c192 --- /dev/null +++ b/hashtbl.ml @@ -0,0 +1,11 @@ +type ('a, 'b) t = ('a, 'b) Hashmap.t ref + +let create size_hint = ref Hashmap.empty + +let add tbl k v = tbl := Hashmap.add k v !tbl + +let mem tbl k = Hashmap.mem k !tbl + +let find tbl k = Hashmap.find k !tbl + +let length tbl = Hashmap.cardinal !tbl diff --git a/main.ml b/main.ml index 42d5c0d..4814014 100644 --- a/main.ml +++ b/main.ml @@ -1,71 +1,5 @@ open Printf -module HashMap = struct - type ('a, 'b) t = ('a * 'b) list - - let empty = [] - - let add k v m = (k, v) :: m - - let rec find k = function - | (k', v') :: xs -> if k = k' then v' else find k xs - | [] -> raise Not_found - - let mem k m = - try - ignore (find k m) ; - true - with Not_found -> false - - let merge f m1 m2 = - let src = ref empty in - let rec iter_m1 = function - | (k, v) :: xs -> - ( try src := add k (Some v, Some (find k m2)) !src - with Not_found -> src := add k (Some v, None) !src ) ; - iter_m1 xs - | [] -> () - in - let rec iter_m2 = function - | (k, v) :: xs -> - if not (mem k m1) then src := add k (None, Some v) !src ; - iter_m2 xs - | [] -> () - in - iter_m1 m1 ; - iter_m2 m2 ; - List.fold_left - (fun m (k, (l, r)) -> - match f k l r with None -> m | Some v -> add k v m ) - empty !src - - let union f m1 m2 = - merge - (fun k l r -> - match (l, r) with - | None, None -> None - | Some v, None -> l - | None, Some v -> r - | Some v1, Some v2 -> f k v1 v2 ) - m1 m2 - - let cardinal m = List.length m -end - -module Hashtbl = struct - type ('a, 'b) t = ('a, 'b) HashMap.t ref - - let create size_hint = ref HashMap.empty - - let add tbl k v = tbl := HashMap.add k v !tbl - - let mem tbl k = HashMap.mem k !tbl - - let find tbl k = HashMap.find k !tbl - - let length tbl = HashMap.cardinal !tbl -end - let filter_after_map f lst = List.map (function Some x -> x | None -> failwith "invalid op") @@ List.filter (function Some x -> true | None -> false) @@ -96,20 +30,11 @@ let is_ident_char = function let string_of_list src = "[" ^ String.concat "; " src ^ "]" let hashmap_of_list src = - let hashmap = ref HashMap.empty in - List.iter (fun (k, v) -> hashmap := HashMap.add k v !hashmap) src ; + let hashmap = ref Hashmap.empty in + List.iter (fun (k, v) -> hashmap := Hashmap.add k v !hashmap) src ; !hashmap -let integrate od nw = HashMap.union (fun _ _ r -> Some r) od nw - -let read_lines () = - let rec aux lines = - try - let line = read_line () in - aux (line :: lines) - with End_of_file -> lines - in - String.concat "\n" (List.rev (aux [])) +let integrate od nw = Hashmap.union (fun _ _ r -> Some r) od nw let appfmt buf fmt = ksprintf (fun str -> Buffer.add_string buf (str ^ "\n")) fmt @@ -1344,7 +1269,7 @@ let parse tokens = parse_expressions_and_definitions tokens type environment = - { symbols: (string, ast) HashMap.t + { symbols: (string, ast) Hashmap.t ; parent: environment option ; freevars: (string * string) list ref } @@ -1397,8 +1322,8 @@ let analyze asts = ; exps= Hashtbl.create 16 ; records= Hashtbl.create 16 ; records_fields= Hashtbl.create 16 - ; modulename= ["Pervasives"] - ; opened_modulename= ["Pervasives."] + ; modulename= [] + ; opened_modulename= ["Stdlib."] ; modules= Hashtbl.create 16 } in let with_modulename name = @@ -1439,7 +1364,7 @@ let analyze asts = find_with_modulename (fun x -> Hashtbl.find hashtbl x) name in let hashmap_find_with_modulename name hashmap = - let _, res = find_with_modulename (fun x -> HashMap.find x hashmap) name in + let _, res = find_with_modulename (fun x -> Hashmap.find x hashmap) name in res in let find_symbol env name = @@ -1530,7 +1455,7 @@ let analyze asts = ( None , List.map (fun fieldname -> - try (fieldname, HashMap.find fieldname fields) + try (fieldname, Hashmap.find fieldname fields) with Not_found -> ( fieldname , RecordDotAccess (None, new_base, fieldname) ) ) @@ -1710,7 +1635,7 @@ let analyze asts = let gen_indexname = make_id indexname in let env' = { env with - symbols= HashMap.add indexname (Var gen_indexname) env.symbols } + symbols= Hashmap.add indexname (Var gen_indexname) env.symbols } in let expr1 = aux env expr1 in let expr2 = aux env expr2 in @@ -1820,7 +1745,7 @@ let analyze asts = | LetFunc (recursive, funcname, args, func, _) -> let gen_funcname = Hashtbl.find funcnames2gen funcname in let env_in = - { symbols= add_symbols_in_patterns HashMap.empty args + { symbols= add_symbols_in_patterns Hashmap.empty args ; parent= Some env ; freevars= ref [] } in @@ -1881,7 +1806,7 @@ let analyze asts = let env_out = { env' with symbols= - HashMap.add funcname + Hashmap.add funcname (FuncVar (gen_funcname, List.length args)) env'.symbols } in @@ -1900,7 +1825,7 @@ let analyze asts = let funcvar = Var gen_funcname in let env_out = { env' with - symbols= HashMap.add funcname funcvar env'.symbols } + symbols= Hashmap.add funcname funcvar env'.symbols } in let ast = LetFunc @@ -1970,7 +1895,7 @@ let analyze asts = toplevel_env := { !toplevel_env with symbols= - HashMap.add id (FuncVar (decl, nargs)) !toplevel_env.symbols } ; + Hashmap.add id (FuncVar (decl, nargs)) !toplevel_env.symbols } ; aux' exprs asts | ast :: asts -> ( try aux' (aux !toplevel_env ast :: exprs) asts @@ -1983,13 +1908,13 @@ let analyze asts = let ast = aux' [] exprs in (!toplevel_env, ast) in - let env = {symbols= HashMap.empty; parent= None; freevars= ref []} in + let env = {symbols= Hashmap.empty; parent= None; freevars= ref []} in let _, ast = analyze_module env asts in let ast = LetFunc (false, "aqaml_main", [UnitValue], ast, []) in toplevel.letfuncs <- ast :: toplevel.letfuncs ; (toplevel.letfuncs, toplevel.strings, toplevel.typedefs, toplevel.exps_list) -type gen_environment = {offset: int; varoffset: (string, int) HashMap.t} +type gen_environment = {offset: int; varoffset: (string, int) Hashmap.t} type ctype = CTyInt | CTyUnit | CTyPtr @@ -2059,7 +1984,7 @@ let rec generate (letfuncs, strings, typedefs, exps) = Buffer.contents buf | Var varname -> let buf = Buffer.create 128 in - let offset = HashMap.find varname env.varoffset in + let offset = Hashmap.find varname env.varoffset in appstr buf "pop rax" ; appfmt buf "mov [rbp + %d], rax" offset ; Buffer.contents buf @@ -2108,7 +2033,7 @@ let rec generate (letfuncs, strings, typedefs, exps) = Buffer.contents buf | PtnAlias (ptn, Var varname) -> let buf = Buffer.create 128 in - let offset = HashMap.find varname env.varoffset in + let offset = Hashmap.find varname env.varoffset in appstr buf "pop rax" ; appfmt buf "mov [rbp + %d], rax" offset ; appstr buf "push rax" ; @@ -2199,7 +2124,7 @@ let rec generate (letfuncs, strings, typedefs, exps) = (let rec aux i varoffset = function | varname :: varnames -> aux (i + 1) - (HashMap.add varname (env.offset - (i * 8)) varoffset) + (Hashmap.add varname (env.offset - (i * 8)) varoffset) varnames | [] -> varoffset in @@ -2579,7 +2504,7 @@ let rec generate (letfuncs, strings, typedefs, exps) = exprs) | _, Var varname -> ( try - let offset = HashMap.find varname env.varoffset in + let offset = Hashmap.find varname env.varoffset in let buf = Buffer.create 128 in appfmt buf "mov rax, [rbp + %d]" offset ; appstr buf "push rax" ; @@ -2655,7 +2580,7 @@ let rec generate (letfuncs, strings, typedefs, exps) = let exit_label = make_label () in let offset = new_offset env 1 in let env' = - {offset; varoffset= HashMap.add indexname offset env.varoffset} + {offset; varoffset= Hashmap.add indexname offset env.varoffset} in let buf = Buffer.create 128 in appstr buf @@ aux env (NonTail, expr2) ; @@ -2704,7 +2629,7 @@ let rec generate (letfuncs, strings, typedefs, exps) = | LetFunc (_, funcname, _, _, _) -> let offset = new_offset env' 1 in let env' = - {offset; varoffset= HashMap.add funcname offset env'.varoffset} + {offset; varoffset= Hashmap.add funcname offset env'.varoffset} in env' | _ -> raise Unexpected_ast @@ -2720,7 +2645,7 @@ let rec generate (letfuncs, strings, typedefs, exps) = appfmt buf "mov QWORD PTR [rax + 8], %d" @@ nargs ; List.iteri (fun i var -> - let offset = HashMap.find var env.varoffset in + let offset = Hashmap.find var env.varoffset in appfmt buf "mov rdi, [rbp + %d]" offset ; appfmt buf "mov [rax + %d], rdi" ((i + 2) * 8) ) freevars ; @@ -2803,7 +2728,7 @@ let rec generate (letfuncs, strings, typedefs, exps) = let env = { offset= List.length varnames * -8 ; varoffset= - integrate HashMap.empty @@ hashmap_of_list + integrate Hashmap.empty @@ hashmap_of_list @@ List.mapi (fun i arg -> (arg, -8 * (i + 1))) varnames } in @@ -2847,7 +2772,7 @@ let rec generate (letfuncs, strings, typedefs, exps) = (fun i var -> appfmt buf "mov rdi, [rax + %d]" (i * 8) ; appfmt buf "mov [rbp + %d], rdi" - @@ HashMap.find var env.varoffset ) + @@ Hashmap.find var env.varoffset ) freevars ) ; appstr buf code ; appstr buf "pop rax" ; @@ -2895,6 +2820,7 @@ let rec generate (letfuncs, strings, typedefs, exps) = gen_c_func "aqaml_string_get" [CTyPtr; CTyInt] CTyInt ; gen_c_func "aqaml_string_set" [CTyPtr; CTyInt; CTyInt] CTyInt ; gen_c_func "aqaml_array_get" [CTyPtr; CTyInt] CTyPtr ; + gen_c_func "aqaml_array_length" [CTyPtr] CTyInt ; gen_c_func "aqaml_string_create" [CTyInt] CTyPtr ; gen_c_func "aqaml_string_blit" [CTyPtr; CTyInt; CTyPtr; CTyInt; CTyInt] @@ -2951,6 +2877,7 @@ let rec generate (letfuncs, strings, typedefs, exps) = appstr buf "ret" ; appstr buf "" ; appstr buf "aqaml_char_code:" ; + appstr buf "aqaml_char_chr:" ; appstr buf "ret" ; appstr buf "" ; appstr buf "aqaml_exit:" ; @@ -3081,8 +3008,49 @@ let rec generate (letfuncs, strings, typedefs, exps) = in main_code ^ letfuncs_code ^ strings_code +let input_lines inch = + let rec aux lines = + try + let line = input_line inch in + aux (line :: lines) + with End_of_file -> lines + in + String.concat "\n" (List.rev (aux [])) + +let read_file path = + let inch = open_in path in + let str = input_lines inch in + close_in inch ; str + +let get_modulename_from_path path = + let rec aux i fi ti = + if i < 0 then + String.init + (ti - fi + 1) + (fun i -> + let idx = i + fi in + if i = 0 && path.[idx] >= 'a' then + Char.chr @@ (Char.code path.[idx] - 32) + else path.[idx] ) + else + match path.[i] with + | '.' -> aux (i - 1) (i - 1) (i - 1) + | '/' -> aux (-1) fi ti + | _ -> aux (i - 1) i ti + in + let i = String.length path - 1 in + aux i i i + ;; try - read_lines () |> tokenize |> parse |> analyze |> generate + let buf = Buffer.create 128 in + Array.iteri + (fun i path -> + if i >= 1 then + appfmt buf ";;module %s = struct %s end;;" + (get_modulename_from_path path) + (read_file path) ) + Sys.argv ; + Buffer.contents buf |> tokenize |> parse |> analyze |> generate |> printf ".intel_syntax noprefix\n%s" with Failure str -> eprintf "[AQaml Error] %s\n" @@ str diff --git a/stdlib.ml b/stdlib.ml index 5279df3..71041b6 100644 --- a/stdlib.ml +++ b/stdlib.ml @@ -32,7 +32,7 @@ external input_char : in_channel -> char = "aqaml_input_char" external open_in : string -> in_channel = "aqaml_open_in" -external close_in : string -> in_channel = "aqaml_close_in" +external close_in : in_channel -> unit = "aqaml_close_in" type 'a option = Some of 'a | None @@ -44,6 +44,8 @@ let max a b = if a < b then b else a module Char = struct external code : char -> int = "aqaml_char_code" + + external chr : int -> char = "aqaml_char_chr" end type bytes = string @@ -69,9 +71,9 @@ module Bytes = struct string -> int -> bytes -> int -> int -> unit = "aqaml_string_blit" - let of_string str = str + let unsafe_of_string str = str - let to_string bytes = bytes + let unsafe_to_string bytes = bytes end module List = struct @@ -146,6 +148,13 @@ module String = struct string -> int -> bytes -> int -> int -> unit = "aqaml_string_blit" + let init n f = + let buf = Bytes.create n in + for i = 0 to n - 1 do + Bytes.set buf i (f i) + done ; + Bytes.unsafe_to_string buf + let concat sep = function | [] -> "" | lst -> @@ -163,7 +172,7 @@ module String = struct Bytes.blit_string sep 0 buf (pos + hdlen) seplen ; aux (pos + hdlen + seplen) tl in - aux 0 lst ; Bytes.to_string buf + aux 0 lst ; Bytes.unsafe_to_string buf end module Buffer = struct @@ -208,12 +217,19 @@ end module Array = struct external get : 'a array -> int -> 'a = "aqaml_array_get" + + external length : 'a array -> int = "aqaml_array_length" + + let iteri f ary = + for i = 0 to Array.length ary - 1 do + f i ary.(i) + done end -let read_line () = +let input_line inch = let buf = Buffer.create 65 in let rec aux () = - let ch = try Some (input_char stdin) with End_of_file -> None in + let ch = try Some (input_char inch) with End_of_file -> None in match ch with | Some '\n' -> () | None -> if Buffer.length buf = 0 then raise End_of_file else () @@ -221,6 +237,8 @@ let read_line () = in aux () ; Buffer.contents buf +let read_line () = input_line stdin + let not x = if x then false else true module Sys = struct diff --git a/test.ml b/test.ml index 910862e..49b2fba 100644 --- a/test.ml +++ b/test.ml @@ -886,8 +886,6 @@ let isVeggieDish = function in test (isVeggieDish (Rice Katsuo)) false -type 'a option = Some of 'a | None - ;; let div x y = if y = 0 then None else Some (x / y) in test (div 12 3) (Some 4) ; @@ -1419,17 +1417,17 @@ test (try digit '\n' with Failure _ -> -1) (-1) type test_for_func_type = Value of int | Func of (int -> int) ;; -let str = Bytes.of_string "debug" in -test str @@ Bytes.of_string "debug" ; +let str = Bytes.unsafe_of_string "debug" in +test str @@ Bytes.unsafe_of_string "debug" ; str.[2] <- 'a' ; -test str @@ Bytes.of_string "deaug" +test str @@ Bytes.unsafe_of_string "deaug" ;; let src = "abcd" in -let dst = Bytes.of_string "def " in +let dst = Bytes.unsafe_of_string "def " in String.blit src 1 dst 2 3 ; test src "abcd" ; -test dst @@ Bytes.of_string "debcd" +test dst @@ Bytes.unsafe_of_string "debcd" ;; let src = "abcd" in @@ -1437,7 +1435,7 @@ let dst = Bytes.create 5 in dst.[0] <- 'd' ; dst.[1] <- 'e' ; String.blit src 1 dst 2 3 ; -test dst @@ Bytes.of_string "debcd" +test dst @@ Bytes.unsafe_of_string "debcd" type testrecord2 = {mutable testrecord2_int: int; mutable testrecord2_str: string} diff --git a/test.sh b/test.sh index abd90c5..711fd8d 100755 --- a/test.sh +++ b/test.sh @@ -1,5 +1,5 @@ #!/bin/bash -cat _test.ml | ./aqaml > _test.s +./aqaml stdlib.ml test.ml > _test.s gcc utility.o _test.s -o _test.o ./_test.o test_command_line_argument1 test_command_line_argument2 diff --git a/utility.c b/utility.c index 24d7dca..40c343c 100644 --- a/utility.c +++ b/utility.c @@ -550,3 +550,10 @@ uint64_t aqaml_array_get_detail(uint64_t ary_src, uint64_t idx) assert(idx < (ary.array->header >> 10)); return ary.array->data[idx]; } + +uint64_t aqaml_array_length_detail(uint64_t ary_src) +{ + AQamlValue ary = get_value(ary_src); + assert(ary.kind == AQAML_ARRAY); + return ary.array->header >> 10; +}