catala/runtimes/jsoo/runtime.ml
Louis Gesbert 9d07015864 Unify runtime error handling
- Clearly distinguish Exceptions from Errors. The only catchable exception
  available in our AST is `EmptyError`, so the corresponding nodes are made less
  generic, and a node `FatalError` is added

- Runtime errors are defined as a specific type in the OCaml runtime, with a
  carrier exception and printing functions. These are used throughout, and
  consistently by the interpreter. They always carry a position, that can be
  converted to be printed with the fancy compiler location printer, or in a
  simpler way from the backends.

- All operators that might be subject to an error take a position as argument,
  in order to print an informative message without relying on backtraces from
  the backend
2024-04-26 18:31:26 +02:00

167 lines
5.3 KiB
OCaml

(* This file is part of the Catala compiler, a specification language for tax
and social benefits computation rules. Copyright (C) 2020 Inria, contributor:
Emile Rolley <emile.rolley@tuta.io>.
Licensed under the Apache License, Version 2.0 (the "License"); you may not
use this file except in compliance with the License. You may obtain a copy of
the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
License for the specific language governing permissions and limitations under
the License. *)
open Js_of_ocaml
module R_ocaml = Runtime_ocaml.Runtime
class type source_position = object
method fileName : Js.js_string Js.t Js.prop
method startLine : int Js.prop
method endLine : int Js.prop
method startColumn : int Js.prop
method endColumn : int Js.prop
method lawHeadings : Js.js_string Js.t Js.js_array Js.t Js.prop
end
class type raw_event = object
method eventType : Js.js_string Js.t Js.prop
method information : Js.js_string Js.t Js.js_array Js.t Js.prop
method sourcePosition : source_position Js.t Js.optdef Js.prop
method loggedIOJson : Js.js_string Js.t Js.prop
method loggedValueJson : Js.js_string Js.t Js.prop
end
class type event = object
method data : Js.js_string Js.t Js.prop
end
class type duration = object
method years : int Js.readonly_prop
method months : int Js.readonly_prop
method days : int Js.readonly_prop
end
let duration_of_js d = R_ocaml.duration_of_numbers d##.years d##.months d##.days
let duration_to_js d =
let years, months, days = R_ocaml.duration_to_years_months_days d in
object%js
val years = years
val months = months
val days = days
end
let date_of_js d =
let d = Js.to_string d in
let d =
if String.contains d 'T' then d |> String.split_on_char 'T' |> List.hd
else d
in
match String.split_on_char '-' d with
| [year; month; day] ->
R_ocaml.date_of_numbers (int_of_string year) (int_of_string month)
(int_of_string day)
| _ -> failwith "date_of_js: invalid date"
let date_to_js d = Js.string @@ R_ocaml.date_to_string d
class type event_manager = object
method resetLog : unit Js.meth
method retrieveEvents : event Js.t Js.js_array Js.t Js.meth
method retrieveRawEvents : raw_event Js.t Js.js_array Js.t Js.meth
end
let event_manager : event_manager Js.t =
object%js (_self)
method resetLog = R_ocaml.reset_log ()
method retrieveEvents =
R_ocaml.retrieve_log ()
|> R_ocaml.EventParser.parse_raw_events
|> List.map (fun event ->
object%js
val mutable data = event |> R_ocaml.Json.event |> Js.string
end)
|> Array.of_list
|> Js.array
method retrieveRawEvents =
let evt_to_js evt =
(* FIXME: ideally this could be just a Json.parse (R_ocaml.Json.event
foo) ? *)
object%js
val mutable eventType =
(match evt with
| R_ocaml.BeginCall _ -> "Begin call"
| EndCall _ -> "End call"
| VariableDefinition _ -> "Variable definition"
| DecisionTaken _ -> "Decision taken")
|> Js.string
val mutable information =
(match evt with
| BeginCall info | EndCall info | VariableDefinition (info, _, _) ->
List.map Js.string info
| DecisionTaken _ -> [])
|> Array.of_list
|> Js.array
val mutable loggedIOJson =
match evt with
| VariableDefinition (_, io, _) ->
io |> R_ocaml.Json.io_log |> Js.string
| EndCall _ | BeginCall _ | DecisionTaken _ ->
"unavailable" |> Js.string
val mutable loggedValueJson =
(match evt with
| VariableDefinition (_, _, v) -> v
| EndCall _ | BeginCall _ | DecisionTaken _ ->
R_ocaml.unembeddable ())
|> R_ocaml.Json.runtime_value
|> Js.string
val mutable sourcePosition =
match evt with
| DecisionTaken pos ->
Js.def
(object%js
val mutable fileName = Js.string pos.filename
val mutable startLine = pos.start_line
val mutable endLine = pos.end_line
val mutable startColumn = pos.start_column
val mutable endColumn = pos.end_column
val mutable lawHeadings =
List.map Js.string pos.law_headings
|> Array.of_list
|> Js.array
end)
| _ -> Js.undefined
end
in
R_ocaml.retrieve_log () |> List.map evt_to_js |> Array.of_list |> Js.array
end
let execute_or_throw_error f =
try f ()
with R_ocaml.Error _ as exc ->
let msg = Js.string (Printexc.to_string exc) in
Js.Js_error.raise_
(Js.Js_error.of_error
(object%js
val mutable name = Js.string "CatalaError"
val mutable message = msg
val mutable stack = Js.Optdef.empty
method toString = msg
end))
let () =
Js.export_all
(object%js
val eventsManager = event_manager
end)