package cmon
Install
Dune Dependency
Authors
Maintainers
Sources
sha256=1791fd1a44ceec7f2fd3e31d7af966ff64b0ac5a9e7c4b072df11777d0c2bfde
sha512=4c892b3202d1c97279a60414ca1f0db77906fd6a05303232e2063af95fb44cdb0bb73bc36b7756a65dd9cc48761d99357dc536d5cff33b992b344ac0ddba63d4
Description
Published: 18 Jan 2022
README
CMON : CaMl Object Notation
Cmon
provides a few tools for printing values. This "object notation" mimics the syntax of OCaml literal values: tuples, named constructors and records.
No parser is provided (yet?). The library is intended to print values, for logging and debugging, that can be copy-pasted to an OCaml toplevel.
The unusual feature is that the printer makes sharing explicit: let-binding are introduced at the optimal place by computing dominating nodes. This is particularly convenient for printing the internal structures of symbolic manipulation tools (e.g. typecheckers) that can internally make use of sharing a lot.
Example
A few samples, from the toplevel:
# #require "cmon";;
# #install_printer Cmon.format;;
# Cmon.unit
- : Cmon.t = ()
# Cmon.record ["int", Cmon.int 42; "bool", Cmon.bool false];;
- : Cmon.t = { int = 42; bool = false; }
# let ty1 = Cmon.construct "Ty_arrow" [
Cmon.construct "Ty_int" [];
Cmon.construct "Ty_int" []
];;
val ty1 : Cmon.t = Ty_arrow (Ty_int, Ty_int)
# (* Printing the type derivation for a fictitious, simple,
programming language to illustrate sharing of sub values *)
Cmon.crecord "Let_binding" [
"name", Cmon.string "id_int";
"typ", ty1;
"body", Cmon.crecord "Exp_fun" [
"var", Cmon.string "x";
"typ", ty1;
"body", Cmon.construct "Exp_ident" [Cmon.string "x"]
]
];;
- : Cmon.t =
let v0 = Ty_arrow (Ty_int, Ty_int) in
Let_binding {
name = "id_int";
typ = v0;
body = Exp_fun { var = "x"; typ = v0; body = Exp_ident "x"; };
}
Installation and usage
The library is distributed on opam:
$ opam install cmon
Add cmon
in the libraries
of a dune
file to use it.
Documentation
Simple values are built using the functions unit
, bool
, char
, int
, float
, string
, tuple
, record
, nil
, cons
, list
, constant
, construct
and crecord
.
# #require "cmon";;
# #install_printer Cmon.format;;
# open Cmon;;
# unit;;
- : t = ()
# bool false;;
- : t = false
# int 10;;
- : t = 10
# char 'a';;
- : t = 'a'
# float 10.0;;
- : t = 10.
# string "foo";;
- : t = "foo"
# tuple [int 42; bool true];;
- : t = (42, true)
# record ["field1", int 1; "field2", char 'b'];;
- : t = { field1 = 1; field2 = 'b'; }
# nil;;
- : t = []
# cons (int 1) nil;;
- : t = [ 1 ]
# cons (int 1) (constant "x");;
- : t = 1 :: x
# cons (int 1) (constant "xs");;
- : t = 1 :: xs
# list [int 1; int 2; int 3];;
- : t = [ 1; 2; 3 ]
# crecord "Inline_record" ["field1", int 1; "field2", char 'b'];;
- : t = Inline_record { field1 = 1; field2 = 'b'; }
Sharing is enabled by default for all compound values and strings.
# let twice x = Cmon.tuple [x;x];;
# twice (string "foo-bar-baz");;
- : t = let v0 = "foo-bar-baz" in
(v0, v0)
# twice (construct "None" []);;
- : t = (None, None)
# twice (construct "Some" [bool true]);;
- : t = let v0 = Some true in
(v0, v0)
To opt out, use the unshared_*
variants of the main functions to opt out: unshared_construct
, unshared_crecord
, unshared_list
, unshared_record
, unshared_string
, unshared_tuple
.
# twice (unshared_string "foo-bar-baz");;
- : t = ("foo-bar-baz", "foo-bar-baz")
# twice (unshared_construct "Some" [bool true]);;
- : t = (Some true, Some true)
Sharing is based on the physical identities of Cmon.t
values, not their structure:
# let s = string "foo-bar-baz" in
Cmon.tuple [s; s];;
- : t = let v0 = "foo-bar-baz" in
(v0, v0)
# Cmon.tuple [string "foo-bar-baz"; string "foo-bar-baz"];;
- : t = ("foo-bar-baz", "foo-bar-baz")
Once composed, a Cmon.t
value can be converted to a PPrint.document
or directly sent to a Format.formatter
:
val Cmon.print : t -> PPrint.document
val Cmon.format : Format.formatter -> t -> unit
Inspecting Cmon.t
values
Cmon.t
is defined as an algebraic type:
type t =
| Unit (* () *)
| Nil (* [] *)
| Bool of bool (* true, false *)
| Char of char (* 'x' *)
| Int of int (* 0, 1, ... *)
| Int32 of int32 (* 0l, 1l, ... *)
| Int64 of int64 (* 0L, 1L, ... *)
| Nativeint of nativeint (* 0n, 1n, ... *)
| Float of float (* 0.0, 1.0, ... *)
| Constant of string (* constant constructor, e.g None *)
| Cons of {id: id; car: t; cdr: t} (* x :: xs *)
| String of {id: id; data: string} (* "Foo" *)
| Tuple of {id: id; data: t list} (* (a, b, c) ... *)
| Record of {id: id; data: (string * t) list} (* {a: va; b: vb} *)
| Constructor of {id: id; tag: string; data: t} (* Some foo *)
| Array of {id: id; data: t array}
| Lazy of {id: id; data: t lazy_t}
| Var of id (* x *)
| Let of {id: id; recursive: bool; bindings: (var * t) list; body: t}
It is possible to match on Cmon.t
values, inspect their structure, and create new values. This is useful for debugging and understanding how Cmon works but not recommended in normal use (one could easily create "unbound" variables by messing with the structure). Stick to the public functions.
The Cmon.explicit_sharing
function can reveal the sharing that would be displayed by the print
functions:
# let c = Cmon.tuple [s; s];;
val c : t =
Tuple
{Cmon.id = 6;
data =
[String {Cmon.id = 4; data = "foo-bar-baz"};
String {Cmon.id = 4; data = "foo-bar-baz"}]}
# Cmon.explicit_sharing c;;
- : t =
Let
{Cmon.id = 5; bindings = [(0, String {Cmon.id = 1; data = "foo-bar-baz"})];
body = Tuple {Cmon.id = 0; data = [Var 0; Var 0]}}
To print without introducing more sharing, the _as_is
variants are provided:
val Cmon.print : t -> PPrint.document
val Cmon.format : Format.formatter -> t -> unit
Mixing explicit_sharing
and print_as_is
can be useful to print composite documents while "controlling" sharing boundaries.