package tiny_languages
sectionYPositions = computeSectionYPositions($el), 10)"
x-init="setTimeout(() => sectionYPositions = computeSectionYPositions($el), 10)"
>
Small languages from scratch: Scheme, Lisp, Smalltalk-80, Pascal, BASIC, JavaScript, HTML, CSS and more
Install
dune-project
Dependency
Authors
Maintainers
Sources
0.3.6.tar.gz
md5=7c636383d146d30ac6f2fa234a6253c8
sha512=c79f3823c5f8f57e5038eb640d487c61168b84aa07c61999d6622ef9fd0c890e2b03b4c6a7cdbbe9352a49e25dda00ac7bb14693cee8e3d7beeed251351a2af0
doc/src/tiny_languages.javascript/Js_value.ml.html
Source file Js_value.ml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212(* Claude Code * * Copyright (C) 2026 Yoann Padioleau * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public License * (LGPL) as published by the Free Software Foundation; either version * 2 of the License, or (at your option) any later version. *) (* See Js_value.mli *) type value = Undefined | Null | Bool of bool | Number of float | String of string | Object of obj and obj = { id : int; mutable props : (string * value ref) list; kind : kind; mutable proto : obj option } and kind = | Plain | Array of items | Closure of closure | Host_function of string * (this:value -> value list -> value) | Host_object of host | Regexp of Js_regexp.t and host = { class_name : string; get : string -> value; set : string -> value -> unit; show : unit -> string } and items = { mutable elements : value array; mutable length : int } and closure = { func : Js_ast.func; scope : scope; this : value option } and scope = { vars : (string, binding) Hashtbl.t; parent : scope option } and binding = { mutable value : value; constant : bool } exception Throw of value (*****************************************************************************) (* Objects *) (*****************************************************************************) let counter = ref 0 let make (kind : kind) : obj = incr counter; { id = !counter; props = []; kind; proto = None } let new_object () : obj = make Plain let new_array (vs : value list) : obj = let elements = Array.of_list vs in make (Array { elements; length = Array.length elements }) let host_function (name : string) (f : this:value -> value list -> value) : value = Object (make (Host_function (name, f))) let host_object (h : host) : value = Object (make (Host_object h)) let get_own (o : obj) (k : string) : value option = Option.map ( ! ) (List.assoc_opt k o.props) let set_own (o : obj) (k : string) (v : value) : unit = match List.assoc_opt k o.props with Some r -> r := v | None -> o.props <- (k, ref v) :: o.props let keys (o : obj) : string list = List.rev_map fst o.props let array_items (o : obj) : value list = match o.kind with Array a -> Array.to_list (Array.sub a.elements 0 a.length) | _ -> [] let error (name : string) (message : string) : value = let o = new_object () in set_own o "name" (String name); set_own o "message" (String message); Object o let throw (name : string) (message : string) : 'a = raise (Throw (error name message)) (*****************************************************************************) (* Conversions *) (*****************************************************************************) let typeof (v : value) : string = match v with | Undefined -> "undefined" (* the mistake of 1995, kept since: pages relied on it *) | Null -> "object" | Bool _ -> "boolean" | Number _ -> "number" | String _ -> "string" | Object { kind = Closure _ | Host_function _; _ } -> "function" | Object _ -> "object" let truthy (v : value) : bool = match v with | Undefined | Null -> false | Bool b -> b | Number f -> not (f = 0. || Float.is_nan f) | String s -> s <> "" | Object _ -> true (* -0 is printed 0, as JavaScript does *) let number_to_string (f : float) : string = if f = 0. then "0" else Js_ast.number_to_string f let rec to_string (v : value) : string = match v with | Undefined -> "undefined" | Null -> "null" | Bool b -> string_of_bool b | Number f -> number_to_string f | String s -> s | Object _ -> to_string (to_primitive v) (* an object as a primitive: an array its items joined with commas * (undefined and null as ""), a function its source's stand-in, an * object "[object Object]" *) and to_primitive (v : value) : value = match v with | Object ({ kind = Array _; _ } as o) -> String (String.concat "," (List.map (fun v -> match v with Undefined | Null -> "" | v -> to_string v) (array_items o))) | Object { kind = Closure { func = { name; _ }; _ }; _ } -> String (Printf.sprintf "function %s() { ... }" (Option.value name ~default:"")) | Object { kind = Host_function (name, _); _ } -> String (Printf.sprintf "function %s() { [native code] }" name) | Object { kind = Host_object h; _ } -> String (Printf.sprintf "[object %s]" h.class_name) | Object { kind = Regexp re; _ } -> String (Printf.sprintf "/%s/%s" (Js_regexp.source re) (Js_regexp.flags re)) (* an error, as Error.prototype.toString says it: "TypeError: ..." * (with no prototypes, told by its name and message) *) | Object ({ kind = Plain; _ } as o) -> ( match (get_own o "name", get_own o "message") with | Some (String name), Some (String message) -> String (name ^ ": " ^ message) | _ -> String "[object Object]") | v -> v let to_number (v : value) : float = match to_primitive v with | Undefined -> Float.nan | Null -> 0. | Bool b -> if b then 1. else 0. | Number f -> f | String s -> ( let s = String.trim s in if s = "" then 0. else match s with | "Infinity" | "+Infinity" -> Float.infinity | "-Infinity" -> Float.neg_infinity | _ -> (* decimal digits, a point, an exponent, or 0x: nothing else * (OCaml's float_of_string also reads "nan", "1_000", "0b1") *) let ok = String.for_all (fun c -> (c >= '0' && c <= '9') || String.contains ".eE+-xXabcdefABCDEF" c) s in let hex = String.length s > 2 && (String.sub s 0 2 = "0x" || String.sub s 0 2 = "0X") in let decimal = not (String.exists (fun c -> String.contains "xXabcdfABCDF" c) s) in if ok && (hex || decimal) then Option.value (float_of_string_opt s) ~default:Float.nan else Float.nan) | Object _ -> Float.nan let strict_equal (a : value) (b : value) : bool = match (a, b) with | Undefined, Undefined | Null, Null -> true | Bool x, Bool y -> x = y | Number x, Number y -> x = y (* NaN is not equal to itself; +0 is -0 *) | String x, String y -> String.equal x y | Object x, Object y -> x == y | _ -> false (*****************************************************************************) (* Showing values *) (*****************************************************************************) let display (v : value) : string = let rec go ~top (seen : obj list) (v : value) = match v with | String s -> if top then s else Printf.sprintf "%S" s | Object o when List.memq o seen -> "[Circular]" | Object ({ kind = Array _; _ } as o) -> "[" ^ String.concat ", " (List.map (go ~top:false (o :: seen)) (array_items o)) ^ "]" | Object ({ kind = Plain; _ } as o) -> "{" ^ String.concat ", " (List.map (fun k -> k ^ ": " ^ go ~top:false (o :: seen) (Option.get (get_own o k))) (keys o)) ^ "}" | Object { kind = Closure { func = { name; _ }; _ }; _ } -> "function " ^ Option.value name ~default:"(anonymous)" | Object { kind = Host_function (name, _); _ } -> "function " ^ name | Object { kind = Host_object h; _ } -> h.show () | Object { kind = Regexp _; _ } -> to_string v | v -> to_string v in go ~top:true [] v let to_json (v : value) : string option = let quote s = let b = Buffer.create (String.length s + 2) in Buffer.add_char b '"'; String.iter (fun c -> match c with | '"' -> Buffer.add_string b "\\\"" | '\\' -> Buffer.add_string b "\\\\" | '\n' -> Buffer.add_string b "\\n" | '\t' -> Buffer.add_string b "\\t" | '\r' -> Buffer.add_string b "\\r" | c when Char.code c < 0x20 -> Buffer.add_string b (Printf.sprintf "\\u%04x" (Char.code c)) | c -> Buffer.add_char b c) s; Buffer.add_char b '"'; Buffer.contents b in let exception Cycle in (* None: left out (undefined, a function) *) let rec go (seen : obj list) (v : value) : string option = match v with | Undefined -> None | Null -> Some "null" | Bool b -> Some (string_of_bool b) (* JSON has no NaN and no Infinity *) | Number f -> Some (if Float.is_finite f then number_to_string f else "null") | String s -> Some (quote s) | Object o when List.memq o seen -> raise Cycle | Object ({ kind = Array _; _ } as o) -> Some ("[" ^ String.concat "," (List.map (fun v -> Option.value (go (o :: seen) v) ~default:"null") (array_items o)) ^ "]") | Object ({ kind = Plain; _ } as o) -> Some ("{" ^ String.concat "," (List.filter_map (fun k -> Option.map (fun s -> quote k ^ ":" ^ s) (go (o :: seen) (Option.get (get_own o k)))) (keys o)) ^ "}") | Object _ -> None in match go [] v with s -> s | exception Cycle -> None
sectionYPositions = computeSectionYPositions($el), 10)"
x-init="setTimeout(() => sectionYPositions = computeSectionYPositions($el), 10)"
>