aboutsummaryrefslogblamecommitdiffstats
path: root/lib/kernel/Source.ml
blob: f81499cfd8fe40dd4a06475b105aa4a57464f9d0 (plain) (tree)
1
2
3
4
5
6
7
8
9







                                                                             
                   









                                                                           
 








                                                                                              











                                                                                               


                                                                         

   
                                   






                                                                                         
                                     
                                 
                                                      





                                                                  

       
                                                          
 
                                         
                              





                                                                      
    
                                             

                                                        
 
                                                                                             
 
                                              
                              
                                                         
                                                                               

                                                                               
let blob text = Concepts.Blob.blob_of_bytes (Bytes.of_string text)

let stored_text ~language text =
  EphemeralRelation.Stored {evaluator= language; code= blob text; name= None}

let program ~language text = Prototype.mixture [stored_text ~language text]

let view ~language ~heading text =
  Prototype.mixture
    [ stored_text ~language text;
      Protocols.Schematics.make
        object
          method describe () = Ok (Protocols.Schematics.Relation heading)
        end ]

let stored handle =
  EphemeralRelation.stored handle
  |> Option.map (fun (evaluator, code, name) ->
      evaluator, Bytes.to_string (Concepts.Blob.bytes_of_blob code), name )

let text handle = Option.map (fun (evaluator, text, _) -> evaluator, text) (stored handle)

let reference evaluator =
  match String.rindex_opt evaluator ':' with
  | None -> "system", evaluator
  | Some i ->
      String.sub evaluator 0 i, String.sub evaluator (i + 1) (String.length evaluator - i - 1)

let language_of evaluator = snd (reference evaluator)

module Error = struct
  open Concepts.Condition

  let not_a_program language =
    condition "not-a-program" "An evaluator was given something that carries no source"
      ("language" |=| Concepts.Value.String language)

  let foreign language given =
    condition "foreign-program" "An evaluator was given source in another language"
      ("language" |=| Concepts.Value.String language & "given" |=| Concepts.Value.String given)

  let no_evaluator evaluator =
    condition "no-evaluator" "No evaluator is registered under that name"
      ("evaluator" |=| Concepts.Value.String evaluator)
end

let evaluator ~language ?view run =
  Protocols.Handle.make
    object (self)
      inherit Lifecycle.null
      inherit Identity.of_id
      method to_string = language ^ "-evaluator"
      method protocols : Protocols.Handle.protocol list = [Protocols.Evaluator.make self]

      method invoke program ~within =
        match stored program with
        | None -> Error (Error.not_a_program language)
        | Some (given, _, _) when language_of given <> language ->
            Error (Error.foreign language given)
        | Some (_, source, name) -> (
          match name, view with
          | Some name, Some view -> view ~within ~name source
          | _ -> run ~within source )
    end

let evaluators scope = Path.(scope @/ "evaluator" @/ this)

let bind root ~within evaluator program =
  let open Utilities.Result in
  let scope, language = reference evaluator in
  let* found =
    Result.bind
      (Path.lookup root (evaluators scope))
      (fun directory -> Path.lookup directory Path.(language @/ this))
    |> Result.map_error (fun _ -> Error.no_evaluator evaluator)
  in
  Protocols.Handle.releasing found (fun () ->
      let* found = Protocols.Evaluator.require found in
      Protocols.Evaluator.invoke found program ~within )

let run root ~language ~within source = bind root ~within language (program ~language source)

let register root ~scope ~language evaluator =
  let open Utilities.Result in
  let* directory = Path.lookup root (evaluators scope) in
  let* registry = Protocols.Handle.require Protocols.Registry.from directory in
  let* _ = Protocols.Registry.update registry language None (Some evaluator) in
  Ok ()