diff --git a/README.md b/README.md index c78d55a..d7558f6 100644 --- a/README.md +++ b/README.md @@ -12,15 +12,18 @@ Also note that I use WSL for all OCaml programming, and this is my recommendatio This library includes the following custom modules: +- Array (for operations on mutable arrays) - Bool (for basic operations on booleans) - Char (for basic operations on characters) - Fatal (for throwing exceptions, and making assertions) +- File (for file IO) - Float (for basic operations on floating point numbers) - Int (for basic operations on integers) - List (functional list data structure) - Map (functional map implemented as a red-black tree) - Option (functions for working with the option monad) - Queue (functional queue implemented as two lists) +- Random (for pseudorandom generation) - Set (functional set implemented as a red-black tree) - Stack (functional stack data structure) - String (for basic operations on strings) @@ -29,7 +32,7 @@ This library includes the following custom modules: ## Exposure of Functions from Standard Library -Some functions from the OCaml standard library are exposed through this library. They are handled by the `FromStdlib` module which SHOULD NOT be accessed directly in projects using this library. All functions and types from this module that are exposed to be used in projects are redefined in another module and should be accessed there. The reason for this module is to allow the rest of the modules to be compile with the `-nopervasives` flag yet still access a few of the official OCaml standard module functions. +Some functions from the OCaml standard library are exposed through this library, for random number generation, file and terminal IO, and arrays. They are handled by direct reference to the `Stdlib` module and therefore all modules are compiled with the `-nopervasives` flag. ## Type Declarations and General Functions @@ -89,7 +92,7 @@ $(STANDARD_COMPILE) newModule.mli newModule.ml Then the final step within `build` also needs to be altered to include the corresponding `.cmx` file. ``` -ocamlopt -a fromStdlib.cmx exposed.cmx int.cmx float.cmx option.cmx stack.cmx list.cmx map.cmx queue.cmx set.cmx tree.cmx string.cmx newModule.cmx -o $(LIB_NAME).cmxa +ocamlopt -a exposed.cmx int.cmx float.cmx option.cmx stack.cmx list.cmx map.cmx queue.cmx set.cmx tree.cmx string.cmx newModule.cmx -o $(LIB_NAME).cmxa ``` Just as with the project in the top level, file order should be consistent across compile lines and this final line. @@ -98,18 +101,8 @@ Just as with the project in the top level, file order should be consistent acros One of the unfortunate consequences of the way OCaml's compilation works, is that there is a library called the core library (not to be confused with Jane Street's Core), documented [here](https://ocaml.org/manual/core.html), which contains some definitions for types and exceptions, yet does not include the code from the stdlib that uses them. When compiling with the `-nopervasives` flag, this is still included but without the standard library. While this makes sense from the perspective of having some fundamental exceptions always available, having types like `list` included makes it very annoying when implemented a custom standard library (with the benefit of list literals still functioning properly). This quirk is why my library has no type definition for `list`, `bool`, `option`, etc. but still uses these types. -## Planned Changes +## Planned Change; -The following modules are some that I plan on introducing in future iterations of this project: - -- Array: for operations with mutable arrays. -- Random: for random number generation. -- File: for file IO. - -I also intend on modifying the following modules: - -- Terminal: this module still needs input functions. - -## Tests +At some point I would like to remove all dependencies on the actual OCaml standard library by way of custom implementations for all the functions that currently reference the `Stdlib` module, and all external functions using code in the standard library. Unit tests are also planned for all collection modules, but have not been written yet. \ No newline at end of file diff --git a/lib/array.ml b/lib/array.ml new file mode 100644 index 0000000..871ff7a --- /dev/null +++ b/lib/array.ml @@ -0,0 +1,50 @@ +open General + + +let of_list = Stdlib.Array.of_list + +let length = Stdlib.Array.length + +let concat = Stdlib.Array.concat + +let append = Stdlib.Array.append + +let get = Stdlib.Array.get + +let set = Stdlib.Array.set + +let map = Stdlib.Array.map + +let create = Stdlib.Array.init + +let heap_sort = Stdlib.Array.sort + +let merge_sort = Stdlib.Array.stable_sort + +let copy = Stdlib.Array.copy + +let linear_search (arr : 'a array) (value : 'a) = + let i = ref 0 in + + while (!i < length arr) & (get arr !i <> value) do + i := Int.(!i + 1) + done; + + get arr !i + +let rec binary_search_helper (arr : 'a array) (value : 'a) (first : int) (last : int) = + let midpoint = Int.((first + last) / 2) in + + if midpoint < first or midpoint > last then + None + else if value < (get arr midpoint) then + binary_search_helper arr value first Int.(midpoint - 1) + else if (get arr midpoint) < value then + binary_search_helper arr value Int.(midpoint + 1) last + else if get arr midpoint = value then + Some (get arr midpoint) + else + None + +let binary_search (arr : 'a array) (value : 'a) = + binary_search_helper arr value 0 Int.(length arr - 1) \ No newline at end of file diff --git a/lib/array.mli b/lib/array.mli new file mode 100644 index 0000000..ebe8388 --- /dev/null +++ b/lib/array.mli @@ -0,0 +1,28 @@ +open General + + +val of_list : 'a list -> 'a array + +val length : 'a array -> int + +val concat : 'a array list -> 'a array + +val append : 'a array -> 'a array -> 'a array + +val get : 'a array -> int -> 'a + +val set : 'a array -> int -> 'a -> unit + +val map : ('a -> 'b) -> 'a array -> 'b array + +val create : int -> (int -> 'a) -> 'a array + +val heap_sort : ('a -> 'a -> int) -> 'a array -> unit + +val merge_sort : ('a -> 'a -> int) -> 'a array -> unit + +val copy : 'a array -> 'a array + +val linear_search : 'a array -> 'a -> 'a + +val binary_search : 'a array -> 'a -> 'a option diff --git a/lib/bool.ml b/lib/bool.ml index 786a257..c2dd5bd 100644 --- a/lib/bool.ml +++ b/lib/bool.ml @@ -1,3 +1,7 @@ open General -let of_string = FromStdlib.bool_of_string_opt + +let of_string = function + | "true" -> Some true + | "false" -> Some false + | _ -> None diff --git a/lib/bool.mli b/lib/bool.mli index 47f8ad0..c7836b9 100644 --- a/lib/bool.mli +++ b/lib/bool.mli @@ -1,4 +1,5 @@ open General + (** Converts the string to a bool, returning option type to account for invalid strings. *) val of_string : string -> bool option \ No newline at end of file diff --git a/lib/char.ml b/lib/char.ml index 058597e..06e04fb 100644 --- a/lib/char.ml +++ b/lib/char.ml @@ -1,3 +1,11 @@ open General -let of_int = FromStdlib.char_of_int \ No newline at end of file + +external unsafe_char_of_int : int -> char = "%identity" + + +let of_int n = + if n < 0 or n > 255 then + None + else + Some (unsafe_char_of_int n) diff --git a/lib/char.mli b/lib/char.mli index 31be230..d95c51f 100644 --- a/lib/char.mli +++ b/lib/char.mli @@ -1,4 +1,5 @@ open General + (** Converts an int to a char. *) -val of_int : int -> char \ No newline at end of file +val of_int : int -> char option \ No newline at end of file diff --git a/lib/fatal.ml b/lib/fatal.ml index 7638172..a12f126 100644 --- a/lib/fatal.ml +++ b/lib/fatal.ml @@ -1,6 +1,10 @@ open General -let failwith = FromStdlib.failwith + +external raise : exn -> 'a = "%raise" + + +let failwith m = raise (Failure m) let guarantee b = if not b then @@ -8,4 +12,7 @@ let guarantee b = let guarantee_equal a b = if a <> b then - failwith "guarantee_equal failed." \ No newline at end of file + failwith "guarantee_equal failed." + + + \ No newline at end of file diff --git a/lib/fatal.mli b/lib/fatal.mli index 61f1c44..7cbd3db 100644 --- a/lib/fatal.mli +++ b/lib/fatal.mli @@ -1,5 +1,6 @@ open General + (** Throws an exception with a message. *) val failwith : string -> 'a diff --git a/lib/file.ml b/lib/file.ml new file mode 100644 index 0000000..355b8d2 --- /dev/null +++ b/lib/file.ml @@ -0,0 +1,29 @@ +open General + + +let open_out = Stdlib.open_out + +let close_out = Stdlib.close_out + +let printf = Stdlib.Printf.fprintf + +let write_all_text filepath text = + let channel = open_out filepath in + let _ = printf channel "%s" text in + let _ = close_out channel in + () + + +let open_in = Stdlib.open_in + +let close_in = Stdlib.close_in + +let read_n_chars = Stdlib.really_input_string + +let in_channel_length = Stdlib.in_channel_length + +let read_all_text filepath = + let channel = open_in filepath in + let contents = read_n_chars channel (in_channel_length channel) in + let _ = close_in channel in + contents \ No newline at end of file diff --git a/lib/file.mli b/lib/file.mli new file mode 100644 index 0000000..2c1d997 --- /dev/null +++ b/lib/file.mli @@ -0,0 +1,26 @@ +open General + + +(** Opens and returns an output channel at the specified path. *) +val open_out : string -> Stdlib.out_channel + +(** Closes the provided output channel. *) +val close_out : Stdlib.out_channel -> unit + +(** Formatted printing to a provided output channel. *) +val printf : Stdlib.out_channel -> ('a, Stdlib.out_channel, unit) Stdlib.format -> 'a + +(** Writes the given string to a file at the specified filepath. *) +val write_all_text : string -> string -> unit + +(** Opens and returns an input channel at the specified path. *) +val open_in : string -> Stdlib.in_channel + +(** Closes the provided input channel. *) +val close_in : Stdlib.in_channel -> unit + +(** Reads the specified number of characters from the provided input channel as a string. *) +val read_n_chars : Stdlib.in_channel -> int -> string + +(** Reads from the specified file as a string. *) +val read_all_text : string -> string \ No newline at end of file diff --git a/lib/float.ml b/lib/float.ml index 3daa26b..88a747f 100644 --- a/lib/float.ml +++ b/lib/float.ml @@ -1,11 +1,22 @@ open General -let ( + ) a b = FromStdlib.plus_float a b -let ( - ) a b = FromStdlib.minus_float a b +external ( + ) : float -> float -> float = "%addfloat" -let ( * ) a b = FromStdlib.multiply_float a b +external ( - ) : float -> float -> float = "%subfloat" -let ( / ) a b = FromStdlib.divide_float a b +external ( * ) : float -> float -> float = "%mulfloat" -let of_string = FromStdlib.float_of_string_opt +external ( / ) : float -> float -> float = "%divfloat" + + +external float_of_string : string -> float = "caml_float_of_string" + + +let of_string s = + try + Some (float_of_string s) + with + Failure _ -> None + +external of_int : int -> float = "%floatofint" \ No newline at end of file diff --git a/lib/float.mli b/lib/float.mli index 4262976..6325a57 100644 --- a/lib/float.mli +++ b/lib/float.mli @@ -1,12 +1,16 @@ open General -val ( + ) : float -> float -> float -val ( - ) : float -> float -> float +external ( + ) : float -> float -> float = "%addfloat" -val ( * ) : float -> float -> float +external ( - ) : float -> float -> float = "%subfloat" -val ( / ) : float -> float -> float +external ( * ) : float -> float -> float = "%mulfloat" + +external ( / ) : float -> float -> float = "%divfloat" (** Converts the string to a float, returning option type to account for invalid strings. *) -val of_string : string -> float option \ No newline at end of file +val of_string : string -> float option + +(** Converts the int to a float. *) +val of_int : int -> float \ No newline at end of file diff --git a/lib/fromStdlib.ml b/lib/fromStdlib.ml deleted file mode 100644 index 32c945b..0000000 --- a/lib/fromStdlib.ml +++ /dev/null @@ -1,79 +0,0 @@ -(* https://github.com/ocaml/ocaml/blob/cce52acc7c7903e92078e9fe40745e11a1b944f0/stdlib/pervasives.ml *) - -(* For fatal.ml *) -let failwith = Stdlib.failwith - - -(* For terminal.ml *) -let printf = Printf.printf -type 'a printf_format = ('a, out_channel, unit) format - - -(* For int.ml *) -external int_of_char : char -> int = "%identity" -let int_of_string_opt = int_of_string_opt - -external plus_int : int -> int -> int = "%addint" -external minus_int : int -> int -> int = "%subint" -external multiply_int : int -> int -> int = "%mulint" -external divide_int : int -> int -> int = "%divint" -external mod_int : int -> int -> int = "%modint" - - -(* For float.ml *) -let float_of_string_opt = float_of_string_opt - -external plus_float : float -> float -> float = "%addfloat" -external minus_float : float -> float -> float = "%subfloat" -external multiply_float : float -> float -> float = "%mulfloat" -external divide_float : float -> float -> float = "%divfloat" - - -(* For char.ml *) -let char_of_int = char_of_int - - -(* For string.ml *) -let string_of_bool = string_of_bool -let string_of_int = string_of_int -let string_of_float = string_of_float - -external string_length : string -> int = "%string_length" - - (* This block are not exposed *) -external bytes_create : int -> bytes = "caml_create_bytes" -external string_blit : string -> int -> bytes -> int -> int -> unit - = "caml_blit_string" [@@noalloc] -external bytes_blit : bytes -> int -> bytes -> int -> int -> unit - = "caml_blit_bytes" [@@noalloc] -external bytes_unsafe_to_string : bytes -> string = "%bytes_to_string" - -let string_concat s1 s2 = - let l1 = string_length s1 and l2 = string_length s2 in - let s = bytes_create (l1 + l2) in - string_blit s1 0 s 0 l1; - string_blit s2 0 s l1 l2; - bytes_unsafe_to_string s - - -(* For bool.ml *) -let bool_of_string_opt = bool_of_string_opt - - -(* For general.ml *) -external equal : 'a -> 'a -> bool = "%equal" -external not_equal : 'a -> 'a -> bool = "%notequal" - -external less_than : 'a -> 'a -> bool = "%lessthan" -external greater_than : 'a -> 'a -> bool = "%greaterthan" -external less_than_or_equal : 'a -> 'a -> bool = "%lessequal" -external greater_than_or_equal : 'a -> 'a -> bool = "%greaterequal" - -external not : bool -> bool = "%boolnot" -external or_ : bool -> bool -> bool = "%sequor" -external and_ : bool -> bool -> bool = "%sequand" - -external pipeline : 'a -> ('a -> 'b) -> 'b = "%revapply" -external ignore : 'a -> unit = "%ignore" - - diff --git a/lib/fromStdlib.mli b/lib/fromStdlib.mli deleted file mode 100644 index 3330076..0000000 --- a/lib/fromStdlib.mli +++ /dev/null @@ -1,61 +0,0 @@ -(* For fatal.ml *) -val failwith : string -> 'a - - -(* For terminal.ml *) -val printf : ('a, out_channel, unit) format -> 'a -type 'a printf_format = ('a, out_channel, unit) format - - -(* For int.ml *) -external int_of_char : char -> int = "%identity" -val int_of_string_opt : string -> int option - -external plus_int : int -> int -> int = "%addint" -external minus_int : int -> int -> int = "%subint" -external multiply_int : int -> int -> int = "%mulint" -external divide_int : int -> int -> int = "%divint" -external mod_int : int -> int -> int = "%modint" - - -(* For float.ml *) -val float_of_string_opt : string -> float option - -external plus_float : float -> float -> float = "%addfloat" -external minus_float : float -> float -> float = "%subfloat" -external multiply_float : float -> float -> float = "%mulfloat" -external divide_float : float -> float -> float = "%divfloat" - - -(* For char.ml *) -val char_of_int : int -> char - - -(* For string.ml *) -val string_of_bool : bool -> string -val string_of_int : int -> string -val string_of_float : float -> string - -external string_length : string -> int = "%string_length" -val string_concat : string -> string -> string - - -(* For bool.ml *) -val bool_of_string_opt : string -> bool option - - -(* For general.ml *) -external equal : 'a -> 'a -> bool = "%equal" -external not_equal : 'a -> 'a -> bool = "%notequal" - -external less_than : 'a -> 'a -> bool = "%lessthan" -external greater_than : 'a -> 'a -> bool = "%greaterthan" -external less_than_or_equal : 'a -> 'a -> bool = "%lessequal" -external greater_than_or_equal : 'a -> 'a -> bool = "%greaterequal" - -external not : bool -> bool = "%boolnot" -external or_ : bool -> bool -> bool = "%sequor" -external and_ : bool -> bool -> bool = "%sequand" - -external pipeline : 'a -> ('a -> 'b) -> 'b = "%revapply" -external ignore : 'a -> unit = "%ignore" \ No newline at end of file diff --git a/lib/general.ml b/lib/general.ml index 0eab9b9..884dd5f 100644 --- a/lib/general.ml +++ b/lib/general.ml @@ -12,7 +12,7 @@ type 'a tree = | Leaf | Branch of 'a * 'a tree list -let ignore = FromStdlib.ignore +let ignore (a : 'a) = () let id (x : 'a) = x @@ -20,23 +20,38 @@ let ( >> ) f g x = g (f x) let ( << ) g f x = g (f x) -let ( |> ) = FromStdlib.pipeline +let ( |> ) f g = g f -let ( = ) = FromStdlib.equal +external ( = ) : 'a -> 'a -> bool = "%equal" -let ( <> ) = FromStdlib.not_equal +external ( <> ) : 'a -> 'a -> bool = "%notequal" -let ( < ) = FromStdlib.less_than +external ( < ) : 'a -> 'a -> bool = "%lessthan" -let ( > ) = FromStdlib.greater_than +external ( > ) : 'a -> 'a -> bool = "%greaterthan" -let ( <= ) = FromStdlib.less_than_or_equal +external ( <= ) : 'a -> 'a -> bool = "%lessequal" -let ( >= ) = FromStdlib.greater_than_or_equal +external ( >= ) : 'a -> 'a -> bool = "%greaterequal" -let not = FromStdlib.not +let not = function + | true -> false + | false -> true -let ( or ) = FromStdlib.or_ +external ( or ) : bool -> bool -> bool = "%sequor" + +external ( & ) : bool -> bool -> bool = "%sequand" + +type 'a ref = + { mutable contents : 'a } + +let ref a = + { contents = a } + +let ( := ) (cell : 'a ref) (contents : 'a) = + cell.contents <- contents + +let ( ! ) (cell : 'a ref) = + cell.contents -let ( & ) = FromStdlib.and_ diff --git a/lib/general.mli b/lib/general.mli index e8d3bb1..e21dd64 100644 --- a/lib/general.mli +++ b/lib/general.mli @@ -1,6 +1,7 @@ (** Purely functional queue, implemented as a pair of lists. *) type 'a queue = { front : 'a list; back : 'a list } +(** Result type for error handling. *) type 'a result = | Error of string | Success of 'a @@ -10,7 +11,7 @@ type 'a stack = | Empty | Stacked of 'a * 'a stack -(* A purely functional tree with arbitrarily many branches at each node. *) +(**A purely functional tree with arbitrarily many branches at each node. *) type 'a tree = | Leaf | Branch of 'a * 'a tree list @@ -31,28 +32,40 @@ val ( << ) : ('a -> 'b) -> ('c -> 'a) -> 'c -> 'b val ( |> ) : 'a -> ('a -> 'b) -> 'b (** Checks for structural equality. *) -val ( = ) : 'a -> 'a -> bool +external ( = ) : 'a -> 'a -> bool = "%equal" (** Checks for structural inequality. *) -val ( <> ) : 'a -> 'a -> bool +external ( <> ) : 'a -> 'a -> bool = "%notequal" (** Polymorphic less than. *) -val ( < ) : 'a -> 'a -> bool +external ( < ) : 'a -> 'a -> bool = "%lessthan" (** Polymorphic greater than. *) -val ( > ) : 'a -> 'a -> bool +external ( > ) : 'a -> 'a -> bool = "%greaterthan" (** Polymorphic less than or equal to. *) -val ( <= ) : 'a -> 'a -> bool +external ( <= ) : 'a -> 'a -> bool = "%lessequal" (** Polymorphic greater than or equal to. *) -val ( >= ) : 'a -> 'a -> bool +external ( >= ) : 'a -> 'a -> bool = "%greaterequal" (** Logical negation. *) val not : bool -> bool (** Logical or (infix). *) -val ( or ) : bool -> bool -> bool +external ( or ) : bool -> bool -> bool = "%sequor" (** Logical and. *) -val ( & ) : bool -> bool -> bool \ No newline at end of file +external ( & ) : bool -> bool -> bool = "%sequand" + +(** Reference cells for mutable variables. *) +type 'a ref = { mutable contents : 'a; } + +(** Creates a reference cell. *) +val ref : 'a -> 'a ref + +(** Updates a reference cell. *) +val ( := ) : 'a ref -> 'a -> unit + +(** Returns the contents of a reference cell. *) +val ( ! ) : 'a ref -> 'a \ No newline at end of file diff --git a/lib/int.ml b/lib/int.ml index aaafa99..336f0ff 100644 --- a/lib/int.ml +++ b/lib/int.ml @@ -1,16 +1,26 @@ open General -let ( + ) a b = FromStdlib.plus_int a b -let ( - ) a b = FromStdlib.minus_int a b +external ( + ) : int -> int -> int = "%addint" -let ( * ) a b = FromStdlib.multiply_int a b +external ( - ) : int -> int -> int = "%subint" -let ( / ) a b = FromStdlib.divide_int a b +external ( * ) : int -> int -> int = "%mulint" -let ( mod ) a b = FromStdlib.mod_int a b +external ( / ) : int -> int -> int = "%divint" + +external ( mod ) : int -> int -> int = "%modint" + +external of_char : char -> int = "%identity" + + +external int_of_string : string -> int = "caml_int_of_string" + +let of_string s = + try + Some (int_of_string s) + with + | Failure _ -> None -let of_char = FromStdlib.int_of_char -let of_string = FromStdlib.int_of_string_opt diff --git a/lib/int.mli b/lib/int.mli index 049d886..d7391fa 100644 --- a/lib/int.mli +++ b/lib/int.mli @@ -1,17 +1,18 @@ open General -val ( + ) : int -> int -> int -val ( - ) : int -> int -> int +external ( + ) : int -> int -> int = "%addint" -val ( * ) : int -> int -> int +external ( - ) : int -> int -> int = "%subint" -val ( / ) : int -> int -> int +external ( * ) : int -> int -> int = "%mulint" -val ( mod ) : int -> int -> int +external ( / ) : int -> int -> int = "%divint" + +external ( mod ) : int -> int -> int = "%modint" (** Converts the char to an int. *) -val of_char : char -> int +external of_char : char -> int = "%identity" (** Converts the string to an int, returning option type to account for invalid strings. *) val of_string : string -> int option \ No newline at end of file diff --git a/lib/list.ml b/lib/list.ml index c1bfefe..d9f9e90 100644 --- a/lib/list.ml +++ b/lib/list.ml @@ -1,7 +1,15 @@ open General + let empty : 'a list = [] +let rec length_helper (list : 'a list) (length : int) = + match list with + | [] -> length + | _ :: xs -> length_helper xs Int.(length + 1) + +let length (list : 'a list) = length_helper list 0 + let head (ls : 'a list) : 'a option = match ls with | x :: xs -> Some x @@ -142,4 +150,7 @@ let rec initialize_helper (f : int -> 'a) (length : int) (index : int) (acc : 'a initialize_helper f length Int.(index + 1) ((f index) :: acc) let initialize (f : int -> 'a) (length : int) : 'a list = - initialize_helper f length 0 [] \ No newline at end of file + initialize_helper f length 0 [] + +let of_array = + Stdlib.Array.to_list \ No newline at end of file diff --git a/lib/list.mli b/lib/list.mli index 9305893..da58c1c 100644 --- a/lib/list.mli +++ b/lib/list.mli @@ -1,8 +1,12 @@ open General + (** The empty list *) val empty : 'a list +(** Calculates the length of the given list. Runs in O(n) and should be stored rather then recalculated if many calls are made on an unchanging list. *) +val length : 'a list -> int + (** Returns Some [x] if [x] is the head of the list, or None for the empty list. Runs in O(1). *) val head : 'a list -> 'a option @@ -55,4 +59,7 @@ val filter_rev_tr : ('a -> bool) -> 'a list -> 'a list val find : ('a -> bool) -> 'a list -> ('a * int) option (** Initializes a list using a length and a function from the index to the desired list value. Runs in O(n). *) -val initialize : (int -> 'a) -> int -> 'a list \ No newline at end of file +val initialize : (int -> 'a) -> int -> 'a list + +(* Creates a list from the provided array. *) +val of_array : 'a array -> 'a list \ No newline at end of file diff --git a/lib/makefile b/lib/makefile index 02ff73b..27ac957 100644 --- a/lib/makefile +++ b/lib/makefile @@ -3,13 +3,10 @@ STANDARD_COMPILE = ocamlopt $(STANDARD_FLAGS) -nopervasives -c LIB_NAME = library build: - # fromStdlib manages things that need to be exposed from the standard library - ocamlopt $(STANDARD_FLAGS) -c fromStdlib.mli fromStdlib.ml + make clean - # types and functions that should be opened module wide ocamlopt $(STANDARD_FLAGS) -nopervasives -c general.mli general.ml - # the following files make up the core custom standard library code $(STANDARD_COMPILE) fatal.mli fatal.ml $(STANDARD_COMPILE) int.mli int.ml $(STANDARD_COMPILE) float.mli float.ml @@ -24,9 +21,11 @@ build: $(STANDARD_COMPILE) char.mli char.ml $(STANDARD_COMPILE) bool.mli bool.ml $(STANDARD_COMPILE) terminal.mli terminal.ml + $(STANDARD_COMPILE) file.mli file.ml + $(STANDARD_COMPILE) array.mli array.ml + $(STANDARD_COMPILE) random.mli random.ml - ocamlopt -a fromStdlib.cmx general.cmx fatal.cmx int.cmx float.cmx option.cmx stack.cmx list.cmx map.cmx queue.cmx set.cmx tree.cmx string.cmx char.cmx bool.cmx terminal.cmx -o $(LIB_NAME).cmxa + ocamlopt -a general.cmx fatal.cmx int.cmx float.cmx option.cmx stack.cmx list.cmx map.cmx queue.cmx set.cmx tree.cmx string.cmx char.cmx bool.cmx terminal.cmx file.cmx array.cmx random.cmx -o $(LIB_NAME).cmxa clean: rm -f *.o *.a *.s *.cmi *.cmx *.cmxa *.cmo *.cma - \ No newline at end of file diff --git a/lib/map.ml b/lib/map.ml index b05e688..1af95db 100644 --- a/lib/map.ml +++ b/lib/map.ml @@ -1,5 +1,6 @@ open General + module type Map = sig type key diff --git a/lib/map.mli b/lib/map.mli index be0c228..883919a 100644 --- a/lib/map.mli +++ b/lib/map.mli @@ -1,5 +1,6 @@ open General + module type Map = sig (** The type of keys in the map. *) type key diff --git a/lib/option.ml b/lib/option.ml index 67c7185..4dd0243 100644 --- a/lib/option.ml +++ b/lib/option.ml @@ -1,5 +1,6 @@ open General + let return (x : 'a) : 'a option = Some x let ( ~= ) = return diff --git a/lib/option.mli b/lib/option.mli index 07180ef..61e7d65 100644 --- a/lib/option.mli +++ b/lib/option.mli @@ -1,5 +1,6 @@ open General + (* Monadic return for option type. Trivially boxes the variable of type 'a into a 'a option by applying the Some constructor. *) val return : 'a -> 'a option diff --git a/lib/queue.ml b/lib/queue.ml index 6e29156..0ca2f20 100644 --- a/lib/queue.ml +++ b/lib/queue.ml @@ -1,6 +1,8 @@ open General + open List + let enqueue (a : 'a) (qu : 'a queue) : 'a queue = { qu with front = a :: qu.back } diff --git a/lib/queue.mli b/lib/queue.mli index 700e66f..9ad9de3 100644 --- a/lib/queue.mli +++ b/lib/queue.mli @@ -1,5 +1,6 @@ open General + (** Adds an element to the back of the queue, returning the new queue. Runs in O(1). *) val enqueue : 'a -> 'a queue -> 'a queue diff --git a/lib/random.ml b/lib/random.ml new file mode 100644 index 0000000..099119b --- /dev/null +++ b/lib/random.ml @@ -0,0 +1,17 @@ +open General + + +let init = Stdlib.Random.self_init + + +let integer low high = + init (); + Int.((Stdlib.Random.int (high - low)) + low) + +let float low high = + init (); + Float.((Stdlib.Random.float (high - low)) + low) + +let boolean = + init (); + Stdlib.Random.bool diff --git a/lib/random.mli b/lib/random.mli new file mode 100644 index 0000000..56a8168 --- /dev/null +++ b/lib/random.mli @@ -0,0 +1,11 @@ +open General + + +(** Generates a random integer between low (inclusive) and high (exclusive). *) +val integer : int -> int -> int + +(** Generates a random float between low (inclusive) and high (exclusive). *) +val float : float -> float -> float + +(** Generates a random boolean at even odds. *) +val boolean : unit -> bool \ No newline at end of file diff --git a/lib/set.ml b/lib/set.ml index 94fbf06..debe8cc 100644 --- a/lib/set.ml +++ b/lib/set.ml @@ -1,5 +1,6 @@ open General + module type RBTreeSet = sig type member diff --git a/lib/set.mli b/lib/set.mli index 8b38696..b8a775c 100644 --- a/lib/set.mli +++ b/lib/set.mli @@ -1,5 +1,6 @@ open General + module type RBTreeSet = sig (** Type of members in the set. *) type member diff --git a/lib/stack.ml b/lib/stack.ml index 65e9d28..5a0c000 100644 --- a/lib/stack.ml +++ b/lib/stack.ml @@ -1,5 +1,6 @@ open General + let pop (st : 'a stack) : 'a option * 'a stack = match st with | Empty -> (None, Empty) diff --git a/lib/stack.mli b/lib/stack.mli index a41bbe3..6a5bb0b 100644 --- a/lib/stack.mli +++ b/lib/stack.mli @@ -1,5 +1,6 @@ open General + (** Removes the top element from the stack, returning a tuple of the new stack, and None if the stack was empty, or Some [x] if [x] was on top of the stack. Popping an empty stack will result in the returned stack also being empty. Runs in O(1). *) val pop : 'a stack -> 'a option * 'a stack diff --git a/lib/string.ml b/lib/string.ml index 6358b69..0d8fea5 100644 --- a/lib/string.ml +++ b/lib/string.ml @@ -1,11 +1,49 @@ open General -let ( + ) = FromStdlib.string_concat -let length = FromStdlib.string_length +external bytes_create : int -> bytes = "caml_create_bytes" +external string_blit : string -> int -> bytes -> int -> int -> unit = "caml_blit_string" [@@noalloc] +external bytes_blit : bytes -> int -> bytes -> int -> int -> unit = "caml_blit_bytes" [@@noalloc] +external bytes_unsafe_to_string : bytes -> string = "%bytes_to_string" +external format_int : string -> int -> string = "caml_format_int" +external format_float : string -> float -> string = "caml_format_float" -let of_int = FromStdlib.string_of_int -let of_float = FromStdlib.string_of_float +external length : string -> int = "%string_length" -let of_bool = FromStdlib.string_of_bool \ No newline at end of file +let of_int n = + format_int "%d" n + +let of_bool bool = + if bool then + "true" + else + "false" + +let ( + ) s1 s2 = + let l1 = length s1 in + let l2 = length s2 in + + let s = bytes_create Int.(l1 + l2) in + + string_blit s1 0 s 0 l1; + string_blit s2 0 s l1 l2; + + bytes_unsafe_to_string s + + +external string_get : string -> int -> char = "%string_safe_get" +let valid_float_lexem s = + let l = length s in + let rec loop i = + if i >= l then s + "." else + match string_get s i with + | '0' .. '9' | '-' -> loop Int.(i + 1) + | _ -> s + in + + loop 0 + + +let of_float f = + valid_float_lexem (format_float "%.12g" f) diff --git a/lib/string.mli b/lib/string.mli index b67f710..e202629 100644 --- a/lib/string.mli +++ b/lib/string.mli @@ -1,10 +1,8 @@ open General -(** Concatenates two strings together in the provided order. *) -val ( + ) : string -> string -> string (** Calculates the length of the provided string. *) -val length : string -> int +external length : string -> int = "%string_length" (** Converts an int to a string. *) val of_int : int -> string @@ -14,3 +12,6 @@ val of_float : float -> string (** Converts a boolean to a string. *) val of_bool : bool -> string + +(** Concatenates two strings together in the provided order. *) +val ( + ) : string -> string -> string \ No newline at end of file diff --git a/lib/terminal.ml b/lib/terminal.ml index 170d98d..c15ec87 100644 --- a/lib/terminal.ml +++ b/lib/terminal.ml @@ -1,6 +1,8 @@ open General -let printf = FromStdlib.printf +let printf = Stdlib.Printf.printf -let print s = printf "%s" s \ No newline at end of file +let print s = printf "%s" s + +let read_line = Stdlib.read_line \ No newline at end of file diff --git a/lib/terminal.mli b/lib/terminal.mli index 6c0ef17..64753ab 100644 --- a/lib/terminal.mli +++ b/lib/terminal.mli @@ -1,7 +1,11 @@ open General + (** Formatted print to terminal. *) -val printf : 'a FromStdlib.printf_format -> 'a +val printf : ('a, Stdlib.out_channel, unit) Stdlib.format -> 'a (** Prints the provided string to the terminal. *) -val print : string -> unit \ No newline at end of file +val print : string -> unit + +(** Reads an input line from the terminal. *) +val read_line : unit -> string \ No newline at end of file diff --git a/lib/tree.ml b/lib/tree.ml index 11b248c..0ae01ef 100644 --- a/lib/tree.ml +++ b/lib/tree.ml @@ -1,5 +1,6 @@ open General + let combine (tr1 : 'a tree) (tr2 : 'a tree) (topBranch : 'a) : 'a tree = Branch (topBranch, tr1 :: tr2 :: []) diff --git a/lib/tree.mli b/lib/tree.mli index 514f15e..f6d688a 100644 --- a/lib/tree.mli +++ b/lib/tree.mli @@ -1,5 +1,6 @@ open General + (* Combines two trees of the same type, with the specified value at the new top node. Runs in O(1). *) val combine : 'a tree -> 'a tree -> 'a -> 'a tree diff --git a/makefile b/makefile index 88f2414..4d14f7d 100644 --- a/makefile +++ b/makefile @@ -5,10 +5,10 @@ build: ocamlopt $(CUSTOM_LIBRARY_LOCATION)/library.cmxa -o program mostlyclean: - rm -f *.o *.a *.s *.cmi *.cmx *.cmxa *.cmo *.cma main.mli + rm -f *.o *.a *.s *.cmi *.cmx *.cmxa *.cmo *.cma clean: - rm -f *.o *.a *.s *.cmi *.cmx *.cmxa *.cmo *.cma main.mli program + rm -f *.o *.a *.s *.cmi *.cmx *.cmxa *.cmo *.cma program make install: make build